[[package]]
name = "measureme"
-version = "0.5.0"
+version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c420bbc064623934620b5ab2dc0cf96451b34163329e82f95e7fa1b7b99a6ac8"
+checksum = "fef709d3257013bba7cff14fc504e07e80631d3fe0f6d38ce63b8f6510ccb932"
dependencies = [
"byteorder",
"memmap",
"jobserver",
"log",
"measureme",
- "num_cpus",
"parking_lot",
"polonius-engine",
"rustc-rayon",
"rustc_error_codes",
"rustc_errors",
"rustc_feature",
- "rustc_fs_util",
"rustc_hir",
"rustc_index",
"rustc_macros",
"jemalloc-sys",
"rustc_codegen_ssa",
"rustc_driver",
- "rustc_target",
]
[[package]]
"smallvec 1.0.0",
]
-[[package]]
-name = "rustc_asan"
-version = "0.0.0"
-dependencies = [
- "alloc",
- "build_helper",
- "cmake",
- "compiler_builtins",
- "core",
-]
-
[[package]]
name = "rustc_ast_lowering"
version = "0.0.0"
"syntax",
]
+[[package]]
+name = "rustc_ast_passes"
+version = "0.0.0"
+dependencies = [
+ "log",
+ "rustc_data_structures",
+ "rustc_error_codes",
+ "rustc_errors",
+ "rustc_feature",
+ "rustc_parse",
+ "rustc_session",
+ "rustc_span",
+ "syntax",
+]
+
[[package]]
name = "rustc_builtin_macros"
version = "0.0.0"
"rustc_expand",
"rustc_feature",
"rustc_parse",
+ "rustc_session",
"rustc_span",
"rustc_target",
"smallvec 1.0.0",
"rustc_codegen_utils",
"rustc_data_structures",
"rustc_errors",
- "rustc_expand",
"rustc_feature",
"rustc_fs_util",
"rustc_hir",
version = "0.0.0"
dependencies = [
"env_logger 0.7.1",
- "graphviz",
"lazy_static 1.3.0",
"log",
"rustc",
"rustc_mir",
"rustc_parse",
"rustc_plugin_impl",
- "rustc_resolve",
"rustc_save_analysis",
"rustc_span",
"rustc_target",
version = "0.0.0"
dependencies = [
"log",
+ "rustc_ast_passes",
"rustc_data_structures",
"rustc_errors",
"rustc_feature",
"rustc_lexer",
"rustc_parse",
+ "rustc_session",
"rustc_span",
"serialize",
"smallvec 1.0.0",
"rustc",
"rustc-rayon",
"rustc_ast_lowering",
+ "rustc_ast_passes",
"rustc_builtin_macros",
"rustc_codegen_llvm",
"rustc_codegen_ssa",
"rustc_lint",
"rustc_metadata",
"rustc_mir",
+ "rustc_mir_build",
"rustc_parse",
"rustc_passes",
"rustc_plugin_impl",
"rustc_privacy",
"rustc_resolve",
+ "rustc_session",
"rustc_span",
"rustc_target",
"rustc_traits",
"rustc",
"rustc_data_structures",
"rustc_error_codes",
+ "rustc_errors",
"rustc_feature",
"rustc_hir",
"rustc_index",
"libc",
]
-[[package]]
-name = "rustc_lsan"
-version = "0.0.0"
-dependencies = [
- "alloc",
- "build_helper",
- "cmake",
- "compiler_builtins",
- "core",
-]
-
[[package]]
name = "rustc_macros"
version = "0.1.0"
name = "rustc_mir"
version = "0.0.0"
dependencies = [
- "arena",
"either",
"graphviz",
"itertools 0.8.0",
]
[[package]]
-name = "rustc_msan"
+name = "rustc_mir_build"
version = "0.0.0"
dependencies = [
- "alloc",
- "build_helper",
- "cmake",
- "compiler_builtins",
- "core",
+ "arena",
+ "itertools 0.8.0",
+ "log",
+ "rustc",
+ "rustc_apfloat",
+ "rustc_data_structures",
+ "rustc_error_codes",
+ "rustc_errors",
+ "rustc_hir",
+ "rustc_index",
+ "rustc_macros",
+ "rustc_span",
+ "rustc_target",
+ "serialize",
+ "smallvec 1.0.0",
+ "syntax",
]
[[package]]
"rustc_errors",
"rustc_feature",
"rustc_lexer",
+ "rustc_session",
"rustc_span",
"smallvec 1.0.0",
"syntax",
"rustc_feature",
"rustc_hir",
"rustc_index",
- "rustc_parse",
+ "rustc_session",
"rustc_span",
"rustc_target",
"syntax",
dependencies = [
"rustc",
"rustc_error_codes",
+ "rustc_errors",
"rustc_hir",
+ "rustc_lint",
"rustc_metadata",
"rustc_span",
"syntax",
"rustc",
"rustc_data_structures",
"rustc_error_codes",
+ "rustc_errors",
"rustc_hir",
"rustc_span",
"rustc_typeck",
"log",
"num_cpus",
"rustc_data_structures",
+ "rustc_error_codes",
"rustc_errors",
"rustc_feature",
"rustc_fs_util",
"syntax",
]
-[[package]]
-name = "rustc_tsan"
-version = "0.0.0"
-dependencies = [
- "alloc",
- "build_helper",
- "cmake",
- "compiler_builtins",
- "core",
-]
-
[[package]]
name = "rustc_typeck"
version = "0.0.0"
"panic_unwind",
"profiler_builtins",
"rand 0.7.0",
- "rustc_asan",
- "rustc_lsan",
- "rustc_msan",
- "rustc_tsan",
"unwind",
"wasi 0.9.0+wasi-snapshot-preview1",
]
name = "syntax"
version = "0.0.0"
dependencies = [
- "bitflags",
- "lazy_static 1.3.0",
"log",
"rustc_data_structures",
"rustc_error_codes",
tool::Rustdoc,
tool::Clippy,
native::Llvm,
+ native::Sanitizers,
tool::Rustfmt,
tool::Miri,
native::Lld
self.clear_if_dirty(&my_out, &rustdoc);
}
- cargo.env("CARGO_TARGET_DIR", out_dir).arg(cmd).arg("-Zconfig-profile");
+ cargo.env("CARGO_TARGET_DIR", &out_dir).arg(cmd).arg("-Zconfig-profile");
let profile_var = |name: &str| {
let profile = if self.config.rust_optimize { "RELEASE" } else { "DEV" };
let sysroot = if use_snapshot { self.rustc_snapshot_sysroot() } else { &maybe_sysroot };
let libdir = self.rustc_libdir(compiler);
+ // Clear the output directory if the real rustc we're using has changed;
+ // Cargo cannot detect this as it thinks rustc is bootstrap/debug/rustc.
+ //
+ // Avoid doing this during dry run as that usually means the relevant
+ // compiler is not yet linked/copied properly.
+ //
+ // Only clear out the directory if we're compiling std; otherwise, we
+ // should let Cargo take care of things for us (via depdep info)
+ if !self.config.dry_run && mode == Mode::ToolStd && cmd == "build" {
+ self.clear_if_dirty(&out_dir, &self.rustc(compiler));
+ }
+
// Customize the compiler we're running. Specify the compiler to cargo
// as our shim and then pass it some various options used to configure
// how the actual compiler itself is called.
let compiler = builder.compiler(0, builder.config.build);
let mut cargo = builder.cargo(compiler, Mode::Std, target, cargo_subcommand(builder.kind));
- std_cargo(builder, &compiler, target, &mut cargo);
+ std_cargo(builder, target, &mut cargo);
builder.info(&format!("Checking std artifacts ({} -> {})", &compiler.host, target));
run_cargo(
target_deps.extend(copy_third_party_objects(builder, &compiler, target).into_iter());
let mut cargo = builder.cargo(compiler, Mode::Std, target, "build");
- std_cargo(builder, &compiler, target, &mut cargo);
+ std_cargo(builder, target, &mut cargo);
builder.info(&format!(
"Building stage{} std artifacts ({} -> {})",
copy_and_stamp(Path::new(&src), "libunwind.a");
}
+ if builder.config.sanitizers && compiler.stage != 0 {
+ // The sanitizers are only copied in stage1 or above,
+ // to avoid creating dependency on LLVM.
+ target_deps.extend(copy_sanitizers(builder, &compiler, target));
+ }
+
target_deps
}
/// Configure cargo to compile the standard library, adding appropriate env vars
/// and such.
-pub fn std_cargo(
- builder: &Builder<'_>,
- compiler: &Compiler,
- target: Interned<String>,
- cargo: &mut Cargo,
-) {
+pub fn std_cargo(builder: &Builder<'_>, target: Interned<String>, cargo: &mut Cargo) {
if let Some(target) = env::var_os("MACOSX_STD_DEPLOYMENT_TARGET") {
cargo.env("MACOSX_DEPLOYMENT_TARGET", target);
}
let mut features = builder.std_features();
features.push_str(&compiler_builtins_c_feature);
- if compiler.stage != 0 && builder.config.sanitizers {
- // This variable is used by the sanitizer runtime crates, e.g.
- // rustc_lsan, to build the sanitizer runtime from C code
- // When this variable is missing, those crates won't compile the C code,
- // so we don't set this variable during stage0 where llvm-config is
- // missing
- // We also only build the runtimes when --enable-sanitizers (or its
- // config.toml equivalent) is used
- let llvm_config = builder.ensure(native::Llvm { target: builder.config.build });
- cargo.env("LLVM_CONFIG", llvm_config);
- cargo.env("RUSTC_BUILD_SANITIZERS", "1");
- }
-
cargo
.arg("--features")
.arg(features)
let libdir = builder.sysroot_libdir(target_compiler, target);
let hostdir = builder.sysroot_libdir(target_compiler, compiler.host);
add_to_sysroot(builder, &libdir, &hostdir, &libstd_stamp(builder, compiler, target));
-
- if builder.config.sanitizers && compiler.stage != 0 && target == "x86_64-apple-darwin" {
- // The sanitizers are only built in stage1 or above, so the dylibs will
- // be missing in stage0 and causes panic. See the `std()` function above
- // for reason why the sanitizers are not built in stage0.
- copy_apple_sanitizer_dylibs(builder, &builder.native_dir(target), "osx", &libdir);
- }
}
}
-fn copy_apple_sanitizer_dylibs(
+/// Copies sanitizer runtime libraries into target libdir.
+fn copy_sanitizers(
builder: &Builder<'_>,
- native_dir: &Path,
- platform: &str,
- into: &Path,
-) {
- for &sanitizer in &["asan", "tsan"] {
- let filename = format!("lib__rustc__clang_rt.{}_{}_dynamic.dylib", sanitizer, platform);
- let mut src_path = native_dir.join(sanitizer);
- src_path.push("build");
- src_path.push("lib");
- src_path.push("darwin");
- src_path.push(&filename);
- builder.copy(&src_path, &into.join(filename));
+ compiler: &Compiler,
+ target: Interned<String>,
+) -> Vec<PathBuf> {
+ let runtimes: Vec<native::SanitizerRuntime> = builder.ensure(native::Sanitizers { target });
+
+ if builder.config.dry_run {
+ return Vec::new();
+ }
+
+ let mut target_deps = Vec::new();
+ let libdir = builder.sysroot_libdir(*compiler, target);
+
+ for runtime in &runtimes {
+ let dst = libdir.join(&runtime.name);
+ builder.copy(&runtime.path, &dst);
+
+ if target == "x86_64-apple-darwin" {
+ // Update the library install name reflect the fact it has been renamed.
+ let status = Command::new("install_name_tool")
+ .arg("-id")
+ .arg(format!("@rpath/{}", runtime.name))
+ .arg(&dst)
+ .status()
+ .expect("failed to execute `install_name_tool`");
+ assert!(status.success());
+ }
+
+ target_deps.push(dst);
}
+
+ target_deps
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
let bindir = sysroot.join("bin");
t!(fs::create_dir_all(&bindir));
let compiler = builder.rustc(target_compiler);
- let _ = fs::remove_file(&compiler);
builder.copy(&rustc, &compiler);
target_compiler
config.mandir = install.mandir.clone().map(PathBuf::from);
}
+ // We want the llvm-skip-rebuild flag to take precedence over the
+ // skip-rebuild config.toml option so we store it separately
+ // so that we can infer the right value
+ let mut llvm_skip_rebuild = flags.llvm_skip_rebuild;
+
// Store off these values as options because if they're not provided
// we'll infer default values for them later
- let mut llvm_skip_rebuild = None;
let mut llvm_assertions = None;
let mut debug = None;
let mut debug_assertions = None;
}
set(&mut config.ninja, llvm.ninja);
llvm_assertions = llvm.assertions;
- llvm_skip_rebuild = llvm.skip_rebuild;
+ llvm_skip_rebuild = llvm_skip_rebuild.or(llvm.skip_rebuild);
set(&mut config.llvm_optimize, llvm.optimize);
set(&mut config.llvm_thin_lto, llvm.thin_lto);
set(&mut config.llvm_release_debuginfo, llvm.release_debuginfo);
"src/libcore",
"src/libpanic_abort",
"src/libpanic_unwind",
- "src/librustc_asan",
- "src/librustc_lsan",
- "src/librustc_msan",
- "src/librustc_tsan",
"src/libstd",
"src/libunwind",
"src/libtest",
builder.ensure(RustbookSrc {
target: self.target,
name: INTERNER.intern_str($book_name),
- src: doc_src(builder),
+ src: INTERNER.intern_path(builder.src.join($path)),
})
}
}
// NOTE: When adding a book here, make sure to ALSO build the book by
// adding a build step in `src/bootstrap/builder.rs`!
book!(
+ CargoBook, "src/tools/cargo/src/doc", "cargo";
EditionGuide, "src/doc/edition-guide", "edition-guide";
EmbeddedBook, "src/doc/embedded-book", "embedded-book";
Nomicon, "src/doc/nomicon", "nomicon";
RustdocBook, "src/doc/rustdoc", "rustdoc";
);
-fn doc_src(builder: &Builder<'_>) -> Interned<PathBuf> {
- INTERNER.intern_path(builder.src.join("src/doc"))
-}
-
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct UnstableBook {
target: Interned<String>,
builder.ensure(RustbookSrc {
target: self.target,
name: INTERNER.intern_str("unstable-book"),
- src: builder.md_doc_out(self.target),
+ src: INTERNER.intern_path(builder.md_doc_out(self.target).join("unstable-book")),
})
}
}
-#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
-pub struct CargoBook {
- target: Interned<String>,
- name: Interned<String>,
-}
-
-impl Step for CargoBook {
- type Output = ();
- const DEFAULT: bool = true;
-
- fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
- let builder = run.builder;
- run.path("src/tools/cargo/src/doc/book").default_condition(builder.config.docs)
- }
-
- fn make_run(run: RunConfig<'_>) {
- run.builder.ensure(CargoBook { target: run.target, name: INTERNER.intern_str("cargo") });
- }
-
- fn run(self, builder: &Builder<'_>) {
- let target = self.target;
- let name = self.name;
- let src = builder.src.join("src/tools/cargo/src/doc");
-
- let out = builder.doc_out(target);
- t!(fs::create_dir_all(&out));
-
- let out = out.join(name);
-
- builder.info(&format!("Cargo Book ({}) - {}", target, name));
-
- let _ = fs::remove_dir_all(&out);
-
- builder.run(builder.tool_cmd(Tool::Rustbook).arg("build").arg(&src).arg("-d").arg(out));
- }
-}
-
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
struct RustbookSrc {
target: Interned<String>,
t!(fs::create_dir_all(&out));
let out = out.join(name);
- let src = src.join(name);
let index = out.join("index.html");
let rustbook = builder.tool_exe(Tool::Rustbook);
let mut rustbook_cmd = builder.tool_cmd(Tool::Rustbook);
pub struct TheBook {
compiler: Compiler,
target: Interned<String>,
- name: &'static str,
}
impl Step for TheBook {
run.builder.ensure(TheBook {
compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
target: run.target,
- name: "book",
});
}
///
/// We need to build:
///
- /// * Book (first edition)
- /// * Book (second edition)
+ /// * Book
+ /// * Older edition redirects
/// * Version info and CSS
/// * Index page
/// * Redirect pages
fn run(self, builder: &Builder<'_>) {
let compiler = self.compiler;
let target = self.target;
- let name = self.name;
// build book
builder.ensure(RustbookSrc {
target,
- name: INTERNER.intern_string(name.to_string()),
- src: doc_src(builder),
+ name: INTERNER.intern_str("book"),
+ src: INTERNER.intern_path(builder.src.join("src/doc/book")),
});
// building older edition redirects
-
- let source_name = format!("{}/first-edition", name);
- builder.ensure(RustbookSrc {
- target,
- name: INTERNER.intern_string(source_name),
- src: doc_src(builder),
- });
-
- let source_name = format!("{}/second-edition", name);
- builder.ensure(RustbookSrc {
- target,
- name: INTERNER.intern_string(source_name),
- src: doc_src(builder),
- });
-
- let source_name = format!("{}/2018-edition", name);
- builder.ensure(RustbookSrc {
- target,
- name: INTERNER.intern_string(source_name),
- src: doc_src(builder),
- });
+ for edition in &["first-edition", "second-edition", "2018-edition"] {
+ builder.ensure(RustbookSrc {
+ target,
+ name: INTERNER.intern_string(format!("book/{}", edition)),
+ src: INTERNER.intern_path(builder.src.join("src/doc/book").join(edition)),
+ });
+ }
// build the version info page and CSS
builder.ensure(Standalone { compiler, target });
let run_cargo_rustdoc_for = |package: &str| {
let mut cargo = builder.cargo(compiler, Mode::Std, target, "rustdoc");
- compile::std_cargo(builder, &compiler, target, &mut cargo);
+ compile::std_cargo(builder, target, &mut cargo);
// Keep a whitelist so we do not build internal stdlib crates, these will be
// build by the rustc step later if enabled.
// Build cargo command.
let mut cargo = builder.cargo(compiler, Mode::Rustc, target, "doc");
- cargo.env("RUSTDOCFLAGS", "--document-private-items --passes strip-hidden");
+ cargo.env("RUSTDOCFLAGS", "--document-private-items");
compile::rustc_cargo(builder, &mut cargo, target);
// Only include compiler crates, no dependencies of those, such as `libc`.
//
// true => deny, false => warn
pub deny_warnings: Option<bool>,
+
+ pub llvm_skip_rebuild: Option<bool>,
}
pub enum Subcommand {
"VALUE",
);
opts.optopt("", "error-format", "rustc error format", "FORMAT");
+ opts.optopt(
+ "",
+ "llvm-skip-rebuild",
+ "whether rebuilding llvm should be skipped \
+ a VALUE of TRUE indicates that llvm will not be rebuilt \
+ VALUE overrides the skip-rebuild option in config.toml.",
+ "VALUE",
+ );
// fn usage()
let usage =
.map(|p| p.into())
.collect::<Vec<_>>(),
deny_warnings: parse_deny_warnings(&matches),
+ llvm_skip_rebuild: matches.opt_str("llvm-skip-rebuild").map(|s| s.to_lowercase()).map(
+ |s| s.parse::<bool>().expect("`llvm-skip-rebuild` should be either true or false"),
+ ),
}
}
}
.compile("rust_test_helpers");
}
}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub struct Sanitizers {
+ pub target: Interned<String>,
+}
+
+impl Step for Sanitizers {
+ type Output = Vec<SanitizerRuntime>;
+
+ fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
+ run.path("src/llvm-project/compiler-rt").path("src/sanitizers")
+ }
+
+ fn make_run(run: RunConfig<'_>) {
+ run.builder.ensure(Sanitizers { target: run.target });
+ }
+
+ /// Builds sanitizer runtime libraries.
+ fn run(self, builder: &Builder<'_>) -> Self::Output {
+ let compiler_rt_dir = builder.src.join("src/llvm-project/compiler-rt");
+ if !compiler_rt_dir.exists() {
+ return Vec::new();
+ }
+
+ let out_dir = builder.native_dir(self.target).join("sanitizers");
+ let runtimes = supported_sanitizers(&out_dir, self.target);
+ if runtimes.is_empty() {
+ return runtimes;
+ }
+
+ let llvm_config = builder.ensure(Llvm { target: builder.config.build });
+ if builder.config.dry_run {
+ return runtimes;
+ }
+
+ let done_stamp = out_dir.join("sanitizers-finished-building");
+ if done_stamp.exists() {
+ builder.info(&format!(
+ "Assuming that sanitizers rebuild is not necessary. \
+ To force a rebuild, remove the file `{}`",
+ done_stamp.display()
+ ));
+ return runtimes;
+ }
+
+ builder.info(&format!("Building sanitizers for {}", self.target));
+ let _time = util::timeit(&builder);
+
+ let mut cfg = cmake::Config::new(&compiler_rt_dir);
+ cfg.target(&self.target);
+ cfg.host(&builder.config.build);
+ cfg.profile("Release");
+
+ cfg.define("CMAKE_C_COMPILER_TARGET", self.target);
+ cfg.define("COMPILER_RT_BUILD_BUILTINS", "OFF");
+ cfg.define("COMPILER_RT_BUILD_CRT", "OFF");
+ cfg.define("COMPILER_RT_BUILD_LIBFUZZER", "OFF");
+ cfg.define("COMPILER_RT_BUILD_PROFILE", "OFF");
+ cfg.define("COMPILER_RT_BUILD_SANITIZERS", "ON");
+ cfg.define("COMPILER_RT_BUILD_XRAY", "OFF");
+ cfg.define("COMPILER_RT_DEFAULT_TARGET_ONLY", "ON");
+ cfg.define("COMPILER_RT_USE_LIBCXX", "OFF");
+ cfg.define("LLVM_CONFIG_PATH", &llvm_config);
+
+ t!(fs::create_dir_all(&out_dir));
+ cfg.out_dir(out_dir);
+
+ for runtime in &runtimes {
+ cfg.build_target(&runtime.cmake_target);
+ cfg.build();
+ }
+
+ t!(fs::write(&done_stamp, b""));
+
+ runtimes
+ }
+}
+
+#[derive(Clone, Debug)]
+pub struct SanitizerRuntime {
+ /// CMake target used to build the runtime.
+ pub cmake_target: String,
+ /// Path to the built runtime library.
+ pub path: PathBuf,
+ /// Library filename that will be used rustc.
+ pub name: String,
+}
+
+/// Returns sanitizers available on a given target.
+fn supported_sanitizers(out_dir: &Path, target: Interned<String>) -> Vec<SanitizerRuntime> {
+ let mut result = Vec::new();
+ match &*target {
+ "x86_64-apple-darwin" => {
+ for s in &["asan", "lsan", "tsan"] {
+ result.push(SanitizerRuntime {
+ cmake_target: format!("clang_rt.{}_osx_dynamic", s),
+ path: out_dir
+ .join(&format!("build/lib/darwin/libclang_rt.{}_osx_dynamic.dylib", s)),
+ name: format!("librustc_rt.{}.dylib", s),
+ });
+ }
+ }
+ "x86_64-unknown-linux-gnu" => {
+ for s in &["asan", "lsan", "msan", "tsan"] {
+ result.push(SanitizerRuntime {
+ cmake_target: format!("clang_rt.{}-x86_64", s),
+ path: out_dir.join(&format!("build/lib/linux/libclang_rt.{}-x86_64.a", s)),
+ name: format!("librustc_rt.{}.a", s),
+ });
+ }
+ }
+ _ => {}
+ }
+ result
+}
let mut cargo = builder.cargo(compiler, mode, target, test_kind.subcommand());
match mode {
Mode::Std => {
- compile::std_cargo(builder, &compiler, target, &mut cargo);
+ compile::std_cargo(builder, target, &mut cargo);
}
Mode::Rustc => {
builder.ensure(compile::Rustc { compiler, target });
macro_rules! bootstrap_tool {
($(
$name:ident, $path:expr, $tool_name:expr
- $(,llvm_tools = $llvm:expr)*
$(,is_external_tool = $external:expr)*
+ $(,is_unstable_tool = $unstable:expr)*
$(,features = $features:expr)*
;
)+) => {
)+
}
- impl Tool {
- /// Whether this tool requires LLVM to run
- pub fn uses_llvm_tools(&self) -> bool {
- match self {
- $(Tool::$name => false $(|| $llvm)*,)+
- }
- }
- }
-
impl<'a> Builder<'a> {
pub fn tool_exe(&self, tool: Tool) -> PathBuf {
match tool {
compiler: self.compiler,
target: self.target,
tool: $tool_name,
- mode: Mode::ToolBootstrap,
+ mode: if false $(|| $unstable)* {
+ // use in-tree libraries for unstable features
+ Mode::ToolStd
+ } else {
+ Mode::ToolBootstrap
+ },
path: $path,
is_optional_tool: false,
source_type: if false $(|| $external)* {
Tidy, "src/tools/tidy", "tidy";
Linkchecker, "src/tools/linkchecker", "linkchecker";
CargoTest, "src/tools/cargotest", "cargotest";
- Compiletest, "src/tools/compiletest", "compiletest", llvm_tools = true;
+ Compiletest, "src/tools/compiletest", "compiletest", is_unstable_tool = true;
BuildManifest, "src/tools/build-manifest", "build-manifest";
RemoteTestClient, "src/tools/remote-test-client", "remote-test-client";
RustInstaller, "src/tools/rust-installer", "fabricate", is_external_tool = true;
-use std::fs::File;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
-use std::thread;
use std::time::{SystemTime, UNIX_EPOCH};
use std::{env, fs};
}
}
-#[must_use]
-pub struct NativeLibBoilerplate {
- pub src_dir: PathBuf,
- pub out_dir: PathBuf,
-}
-
-impl NativeLibBoilerplate {
- /// On macOS we don't want to ship the exact filename that compiler-rt builds.
- /// This conflicts with the system and ours is likely a wildly different
- /// version, so they can't be substituted.
- ///
- /// As a result, we rename it here but we need to also use
- /// `install_name_tool` on macOS to rename the commands listed inside of it to
- /// ensure it's linked against correctly.
- pub fn fixup_sanitizer_lib_name(&self, sanitizer_name: &str) {
- if env::var("TARGET").unwrap() != "x86_64-apple-darwin" {
- return;
- }
-
- let dir = self.out_dir.join("build/lib/darwin");
- let name = format!("clang_rt.{}_osx_dynamic", sanitizer_name);
- let src = dir.join(&format!("lib{}.dylib", name));
- let new_name = format!("lib__rustc__{}.dylib", name);
- let dst = dir.join(&new_name);
-
- println!("{} => {}", src.display(), dst.display());
- fs::rename(&src, &dst).unwrap();
- let status = Command::new("install_name_tool")
- .arg("-id")
- .arg(format!("@rpath/{}", new_name))
- .arg(&dst)
- .status()
- .expect("failed to execute `install_name_tool`");
- assert!(status.success());
- }
-}
-
-impl Drop for NativeLibBoilerplate {
- fn drop(&mut self) {
- if !thread::panicking() {
- t!(File::create(self.out_dir.join("rustbuild.timestamp")));
- }
- }
-}
-
-// Perform standard preparations for native libraries that are build only once for all stages.
-// Emit rerun-if-changed and linking attributes for Cargo, check if any source files are
-// updated, calculate paths used later in actual build with CMake/make or C/C++ compiler.
-// If Err is returned, then everything is up-to-date and further build actions can be skipped.
-// Timestamps are created automatically when the result of `native_lib_boilerplate` goes out
-// of scope, so all the build actions should be completed until then.
-pub fn native_lib_boilerplate(
- src_dir: &Path,
- out_name: &str,
- link_name: &str,
- search_subdir: &str,
-) -> Result<NativeLibBoilerplate, ()> {
- rerun_if_changed_anything_in_dir(src_dir);
-
- let out_dir =
- env::var_os("RUSTBUILD_NATIVE_DIR").unwrap_or_else(|| env::var_os("OUT_DIR").unwrap());
- let out_dir = PathBuf::from(out_dir).join(out_name);
- t!(fs::create_dir_all(&out_dir));
- if link_name.contains('=') {
- println!("cargo:rustc-link-lib={}", link_name);
- } else {
- println!("cargo:rustc-link-lib=static={}", link_name);
- }
- println!("cargo:rustc-link-search=native={}", out_dir.join(search_subdir).display());
-
- let timestamp = out_dir.join("rustbuild.timestamp");
- if !up_to_date(Path::new("build.rs"), ×tamp) || !up_to_date(src_dir, ×tamp) {
- Ok(NativeLibBoilerplate { src_dir: src_dir.to_path_buf(), out_dir })
- } else {
- Err(())
- }
-}
-
-pub fn sanitizer_lib_boilerplate(
- sanitizer_name: &str,
-) -> Result<(NativeLibBoilerplate, String), ()> {
- let (link_name, search_path, apple) = match &*env::var("TARGET").unwrap() {
- "x86_64-unknown-linux-gnu" => {
- (format!("clang_rt.{}-x86_64", sanitizer_name), "build/lib/linux", false)
- }
- "x86_64-apple-darwin" => {
- (format!("clang_rt.{}_osx_dynamic", sanitizer_name), "build/lib/darwin", true)
- }
- _ => return Err(()),
- };
- let to_link = if apple {
- format!("dylib=__rustc__{}", link_name)
- } else {
- format!("static={}", link_name)
- };
- // This env var is provided by rustbuild to tell us where `compiler-rt`
- // lives.
- let dir = env::var_os("RUST_COMPILER_RT_ROOT").unwrap();
- let lib = native_lib_boilerplate(dir.as_ref(), sanitizer_name, &to_link, search_path)?;
- Ok((lib, link_name))
-}
-
fn dir_up_to_date(src: &Path, threshold: SystemTime) -> bool {
t!(fs::read_dir(src)).map(|e| t!(e)).all(|e| {
let meta = t!(e.metadata());
-FROM ubuntu:19.04
+FROM ubuntu:19.10
RUN apt-get update && apt-get install -y --no-install-recommends \
g++ \
-FROM ubuntu:19.04
+FROM ubuntu:19.10
RUN apt-get update && apt-get install -y --no-install-recommends \
g++ \
-#!/bin/sh
+#!/bin/bash
-set -eu
+set -euo pipefail
+IFS=$'\n\t'
+
+source "$(cd "$(dirname "$0")" && pwd)/shared.sh"
# The following lines are also found in src/bootstrap/toolstate.rs,
# so if updating here, please also update that file.
FAILURE=1
for RETRY_COUNT in 1 2 3 4 5; do
# The purpose is to publish the new "current" toolstate in the toolstate repo.
- "$BUILD_SOURCESDIRECTORY/src/tools/publish_toolstate.py" "$GIT_COMMIT" \
+ "$(ciCheckoutPath)/src/tools/publish_toolstate.py" "$GIT_COMMIT" \
"$GIT_COMMIT_MSG" \
"$MESSAGE_FILE" \
"$TOOLSTATE_REPO_ACCESS_TOKEN"
ciCommandSetEnv CC "$(pwd)/clang+llvm-9.0.0-x86_64-darwin-apple/bin/clang"
ciCommandSetEnv CXX "$(pwd)/clang+llvm-9.0.0-x86_64-darwin-apple/bin/clang++"
+ # macOS 10.15 onwards doesn't have libraries in /usr/include anymore: those
+ # are now located deep into the filesystem, under Xcode's own files. The
+ # native clang is configured to use the correct path, but our custom one
+ # doesn't. This sets the SDKROOT environment variable to the SDK so that
+ # our own clang can figure out the correct include path on its own.
+ if ! [[ -d "/usr/include" ]]; then
+ ciCommandSetEnv SDKROOT "$(xcrun --sdk macosx --show-sdk-path)"
+ fi
+
# Configure `AR` specifically so rustbuild doesn't try to infer it as
# `clang-ar` by accident.
ciCommandSetEnv AR "ar"
source "$(cd "$(dirname "$0")" && pwd)/../shared.sh"
if isWindows; then
- pacman -S --noconfirm --needed base-devel ca-certificates make diffutils tar
+ pacman -S --noconfirm --needed base-devel ca-certificates make diffutils tar \
+ binutils
# Make sure we use the native python interpreter instead of some msys equivalent
# one way or another. The msys interpreters seem to have weird path conversions
function ciCheckoutPath {
if isAzurePipelines; then
- echo "${SYSTEM_WORKFOLDER}"
+ echo "${BUILD_SOURCESDIRECTORY}"
elif isGitHubActions; then
echo "${GITHUB_WORKSPACE}"
else
if isAzurePipelines; then
echo "##vso[task.prependpath]${path}"
elif isGitHubActions; then
- echo "::add-path::${value}"
+ echo "::add-path::${path}"
else
echo "ciCommandAddPath only works inside CI!"
exit 1
--- /dev/null
+# `sanitizer`
+
+The tracking issue for this feature is: [#39699](https://github.com/rust-lang/rust/issues/39699).
+
+------------------------
+
+This feature allows for use of one of following sanitizers:
+
+* [AddressSanitizer][clang-asan] a faster memory error detector. Can
+ detect out-of-bounds access to heap, stack, and globals, use after free, use
+ after return, double free, invalid free, memory leaks.
+* [LeakSanitizer][clang-lsan] a run-time memory leak detector.
+* [MemorySanitizer][clang-msan] a detector of uninitialized reads.
+* [ThreadSanitizer][clang-tsan] a fast data race detector.
+
+To enable a sanitizer compile with `-Zsanitizer=...` option, where value is one
+of `address`, `leak`, `memory` or `thread`.
+
+# Examples
+
+This sections show various issues that can be detected with sanitizers. For
+simplicity, the examples are prepared under assumption that optimization level
+used is zero.
+
+## AddressSanitizer
+
+Stack buffer overflow:
+
+```shell
+$ cat a.rs
+fn main() {
+ let xs = [0, 1, 2, 3];
+ let _y = unsafe { *xs.as_ptr().offset(4) };
+}
+$ rustc -Zsanitizer=address a.rs
+$ ./a
+=================================================================
+==10029==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffcc15f43d0 at pc 0x55f77dc015c5 bp 0x7ffcc15f4390 sp 0x7ffcc15f4388
+READ of size 4 at 0x7ffcc15f43d0 thread T0
+ #0 0x55f77dc015c4 in a::main::hab3bd2a745c2d0ac (/tmp/a+0xa5c4)
+ #1 0x55f77dc01cdb in std::rt::lang_start::_$u7b$$u7b$closure$u7d$$u7d$::haa8c76d1faa7b7ca (/tmp/a+0xacdb)
+ #2 0x55f77dc90f02 in std::rt::lang_start_internal::_$u7b$$u7b$closure$u7d$$u7d$::hfeb9a1aef9ac820d /rustc/c27f7568bc74c418996892028a629eed5a7f5f00/src/libstd/rt.rs:48:12
+ #3 0x55f77dc90f02 in std::panicking::try::do_call::h12f0919717b8e0a6 /rustc/c27f7568bc74c418996892028a629eed5a7f5f00/src/libstd/panicking.rs:288:39
+ #4 0x55f77dc926c9 in __rust_maybe_catch_panic /rustc/c27f7568bc74c418996892028a629eed5a7f5f00/src/libpanic_unwind/lib.rs:80:7
+ #5 0x55f77dc9197c in std::panicking::try::h413b21cdcd6cfd86 /rustc/c27f7568bc74c418996892028a629eed5a7f5f00/src/libstd/panicking.rs:267:12
+ #6 0x55f77dc9197c in std::panic::catch_unwind::hc5cc8ef2fd73424d /rustc/c27f7568bc74c418996892028a629eed5a7f5f00/src/libstd/panic.rs:396:8
+ #7 0x55f77dc9197c in std::rt::lang_start_internal::h2039f418ab92218f /rustc/c27f7568bc74c418996892028a629eed5a7f5f00/src/libstd/rt.rs:47:24
+ #8 0x55f77dc01c61 in std::rt::lang_start::ha905d28f6b61d691 (/tmp/a+0xac61)
+ #9 0x55f77dc0163a in main (/tmp/a+0xa63a)
+ #10 0x7f9b3cf5bbba in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x26bba)
+ #11 0x55f77dc01289 in _start (/tmp/a+0xa289)
+
+Address 0x7ffcc15f43d0 is located in stack of thread T0 at offset 48 in frame
+ #0 0x55f77dc0135f in a::main::hab3bd2a745c2d0ac (/tmp/a+0xa35f)
+
+ This frame has 1 object(s):
+ [32, 48) 'xs' <== Memory access at offset 48 overflows this variable
+HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
+ (longjmp and C++ exceptions *are* supported)
+SUMMARY: AddressSanitizer: stack-buffer-overflow (/tmp/a+0xa5c4) in a::main::hab3bd2a745c2d0ac
+Shadow bytes around the buggy address:
+ 0x1000182b6820: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 0x1000182b6830: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 0x1000182b6840: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 0x1000182b6850: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 0x1000182b6860: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+=>0x1000182b6870: 00 00 00 00 f1 f1 f1 f1 00 00[f3]f3 00 00 00 00
+ 0x1000182b6880: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 0x1000182b6890: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 0x1000182b68a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 0x1000182b68b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 0x1000182b68c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+Shadow byte legend (one shadow byte represents 8 application bytes):
+ Addressable: 00
+ Partially addressable: 01 02 03 04 05 06 07
+ Heap left redzone: fa
+ Freed heap region: fd
+ Stack left redzone: f1
+ Stack mid redzone: f2
+ Stack right redzone: f3
+ Stack after return: f5
+ Stack use after scope: f8
+ Global redzone: f9
+ Global init order: f6
+ Poisoned by user: f7
+ Container overflow: fc
+ Array cookie: ac
+ Intra object redzone: bb
+ ASan internal: fe
+ Left alloca redzone: ca
+ Right alloca redzone: cb
+ Shadow gap: cc
+==10029==ABORTING
+```
+
+## MemorySanitizer
+
+Use of uninitialized memory. Note that we are using `-Zbuild-std` to instrument
+standard library, and passing `-msan-track-origins=2` to the LLVM to track
+origins of uninitialized memory:
+
+```shell
+$ cat src/main.rs
+use std::mem::MaybeUninit;
+
+fn main() {
+ unsafe {
+ let a = MaybeUninit::<[usize; 4]>::uninit();
+ let a = a.assume_init();
+ println!("{}", a[2]);
+ }
+}
+
+$ env RUSTFLAGS="-Zsanitizer=memory -Cllvm-args=-msan-track-origins=2" cargo -Zbuild-std run --target x86_64-unknown-linux-gnu
+==9416==WARNING: MemorySanitizer: use-of-uninitialized-value
+ #0 0x560c04f7488a in core::fmt::num::imp::fmt_u64::haa293b0b098501ca $RUST/build/x86_64-unknown-linux-gnu/stage1/lib/rustlib/src/rust/src/libcore/fmt/num.rs:202:16
+...
+ Uninitialized value was stored to memory at
+ #0 0x560c04ae898a in __msan_memcpy.part.0 $RUST/src/llvm-project/compiler-rt/lib/msan/msan_interceptors.cc:1558:3
+ #1 0x560c04b2bf88 in memory::main::hd2333c1899d997f5 $CWD/src/main.rs:6:16
+
+ Uninitialized value was created by an allocation of 'a' in the stack frame of function '_ZN6memory4main17hd2333c1899d997f5E'
+ #0 0x560c04b2bc50 in memory::main::hd2333c1899d997f5 $CWD/src/main.rs:3
+```
+
+
+# Instrumentation of external dependencies and std
+
+The sanitizers to varying degrees work correctly with partially instrumented
+code. On the one extreme is LeakSanitizer that doesn't use any compile time
+instrumentation, on the other is MemorySanitizer that requires that all program
+code to be instrumented (failing to achieve that will inevitably result in
+false positives).
+
+It is strongly recommended to combine sanitizers with recompiled and
+instrumented standard library, for example using [cargo `-Zbuild-std`
+functionality][build-std].
+
+[build-std]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#build-std
+
+# Build scripts and procedural macros
+
+Use of sanitizers together with build scripts and procedural macros is
+technically possible, but in almost all cases it would be best avoided. This
+is especially true for procedural macros which would require an instrumented
+version of rustc.
+
+In more practical terms when using cargo always remember to pass `--target`
+flag, so that rustflags will not be applied to build scripts and procedural
+macros.
+
+# Additional Information
+
+* [Sanitizers project page](https://github.com/google/sanitizers/wiki/)
+* [AddressSanitizer in Clang][clang-asan]
+* [LeakSanitizer in Clang][clang-lsan]
+* [MemorySanitizer in Clang][clang-msan]
+* [ThreadSanitizer in Clang][clang-tsan]
+
+[clang-asan]: https://clang.llvm.org/docs/AddressSanitizer.html
+[clang-lsan]: https://clang.llvm.org/docs/LeakSanitizer.html
+[clang-msan]: https://clang.llvm.org/docs/MemorySanitizer.html
+[clang-tsan]: https://clang.llvm.org/docs/ThreadSanitizer.html
+++ /dev/null
-# `sanitizer_runtime_lib`
-
-This feature is internal to the Rust compiler and is not intended for general use.
-
-------------------------
#[stable(feature = "box_slice_clone", since = "1.3.0")]
impl<T: Clone> Clone for Box<[T]> {
fn clone(&self) -> Self {
- let mut new = BoxBuilder { data: RawVec::with_capacity(self.len()), len: 0 };
-
- let mut target = new.data.ptr();
-
- for item in self.iter() {
- unsafe {
- ptr::write(target, item.clone());
- target = target.offset(1);
- };
-
- new.len += 1;
- }
-
- return unsafe { new.into_box() };
-
- // Helper type for responding to panics correctly.
- struct BoxBuilder<T> {
- data: RawVec<T>,
- len: usize,
- }
-
- impl<T> BoxBuilder<T> {
- unsafe fn into_box(self) -> Box<[T]> {
- let raw = ptr::read(&self.data);
- mem::forget(self);
- raw.into_box()
- }
- }
-
- impl<T> Drop for BoxBuilder<T> {
- fn drop(&mut self) {
- let mut data = self.data.ptr();
- let max = unsafe { data.add(self.len) };
-
- while data != max {
- unsafe {
- ptr::read(data);
- data = data.offset(1);
- }
- }
- }
- }
+ self.to_vec().into_boxed_slice()
}
}
/// Borrows a view into the values stored in the node.
/// The caller must ensure that the node is not the shared root.
+ /// This function is not public, so doesn't have to support shared roots like `keys` does.
fn vals(&self) -> &[V] {
self.reborrow().into_val_slice()
}
}
/// The caller must ensure that the node is not the shared root.
+ /// This function is not public, so doesn't have to support shared roots like `keys` does.
fn keys_mut(&mut self) -> &mut [K] {
unsafe { self.reborrow_mut().into_key_slice_mut() }
}
unsafe { &mut *(self.root as *mut Root<K, V>) }
}
+ /// The caller must ensure that the node is not the shared root.
fn into_key_slice_mut(mut self) -> &'a mut [K] {
- // Same as for `into_key_slice` above, we try to avoid a run-time check.
- if (mem::align_of::<NodeHeader<K, V, K>>() > mem::align_of::<NodeHeader<K, V>>()
- || mem::size_of::<NodeHeader<K, V, K>>() != mem::size_of::<NodeHeader<K, V>>())
- && self.is_shared_root()
- {
- &mut []
- } else {
- unsafe {
- slice::from_raw_parts_mut(
- MaybeUninit::first_ptr_mut(&mut (*self.as_leaf_mut()).keys),
- self.len(),
- )
- }
+ debug_assert!(!self.is_shared_root());
+ // We cannot be the shared root, so `as_leaf_mut` is okay.
+ unsafe {
+ slice::from_raw_parts_mut(
+ MaybeUninit::first_ptr_mut(&mut (*self.as_leaf_mut()).keys),
+ self.len(),
+ )
}
}
}
}
+/// Returns the index in the node at which the key (or an equivalent) exists
+/// or could exist, and whether it exists in the node itself. If it doesn't
+/// exist in the node itself, it may exist in the subtree with that index
+/// (if the node has subtrees). If the key doesn't exist in node or subtree,
+/// the returned index is the position or subtree to insert at.
pub fn search_linear<BorrowType, K, V, Type, Q: ?Sized>(
node: &NodeRef<BorrowType, K, V, Type>,
key: &Q,
Q: Ord,
K: Borrow<Q>,
{
+ // This function is defined over all borrow types (immutable, mutable, owned),
+ // and may be called on the shared root in each case.
+ // Crucially, we use `keys()` here, i.e., we work with immutable data.
+ // `keys_mut()` does not support the shared root, so we cannot use it.
+ // Using `keys()` is fine here even if BorrowType is mutable, as all we return
+ // is an index -- not a reference.
for (i, k) in node.keys().iter().enumerate() {
match key.cmp(k.borrow()) {
Ordering::Greater => {}
#![feature(associated_type_bounds)]
#![feature(binary_heap_into_iter_sorted)]
#![feature(binary_heap_drain_sorted)]
+#![feature(vec_remove_item)]
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
/// # Examples
///
/// ```
+ /// # #![feature(vec_remove_item)]
/// let mut vec = vec![1, 2, 3, 1];
///
/// vec.remove_item(&1);
///
/// assert_eq!(vec, vec![2, 3, 1]);
/// ```
- #[stable(feature = "vec_remove_item", since = "1.42.0")]
+ #[unstable(feature = "vec_remove_item", reason = "recently added", issue = "40062")]
pub fn remove_item<V>(&mut self, item: &V) -> Option<T>
where
T: PartialEq<V>,
#[derive(Debug)]
pub struct Excess(pub NonNull<u8>, pub usize);
-fn size_align<T>() -> (usize, usize) {
+const fn size_align<T>() -> (usize, usize) {
(mem::size_of::<T>(), mem::align_of::<T>())
}
/// must not overflow (i.e., the rounded value must be less than
/// `usize::MAX`).
#[stable(feature = "alloc_layout", since = "1.28.0")]
+ #[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")]
#[inline]
- pub fn from_size_align(size: usize, align: usize) -> Result<Self, LayoutErr> {
+ pub const fn from_size_align(size: usize, align: usize) -> Result<Self, LayoutErr> {
if !align.is_power_of_two() {
return Err(LayoutErr { private: () });
}
/// The minimum size in bytes for a memory block of this layout.
#[stable(feature = "alloc_layout", since = "1.28.0")]
+ #[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")]
#[inline]
- pub fn size(&self) -> usize {
+ pub const fn size(&self) -> usize {
self.size_
}
/// The minimum byte alignment for a memory block of this layout.
#[stable(feature = "alloc_layout", since = "1.28.0")]
+ #[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")]
#[inline]
- pub fn align(&self) -> usize {
+ pub const fn align(&self) -> usize {
self.align_.get()
}
/// Constructs a `Layout` suitable for holding a value of type `T`.
#[stable(feature = "alloc_layout", since = "1.28.0")]
+ #[rustc_const_stable(feature = "alloc_layout_const_new", since = "1.42.0")]
#[inline]
- pub fn new<T>() -> Self {
+ pub const fn new<T>() -> Self {
let (size, align) = size_align::<T>();
// Note that the align is guaranteed by rustc to be a power of two and
// the size+align combo is guaranteed to fit in our address space. As a
// result use the unchecked constructor here to avoid inserting code
// that panics if it isn't optimized well enough.
- debug_assert!(Layout::from_size_align(size, align).is_ok());
unsafe { Layout::from_size_align_unchecked(size, align) }
}
/// address for the whole allocated block of memory. One way to
/// satisfy this constraint is to ensure `align <= self.align()`.
#[unstable(feature = "alloc_layout_extra", issue = "55724")]
+ #[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")]
#[inline]
- pub fn padding_needed_for(&self, align: usize) -> usize {
+ pub const fn padding_needed_for(&self, align: usize) -> usize {
let len = self.size();
// Rounded up value is:
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
fn lt(&self, other: &Rhs) -> bool {
- match self.partial_cmp(other) {
- Some(Less) => true,
- _ => false,
- }
+ matches!(self.partial_cmp(other), Some(Less))
}
/// This method tests less than or equal to (for `self` and `other`) and is used by the `<=`
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
fn le(&self, other: &Rhs) -> bool {
- match self.partial_cmp(other) {
- Some(Less) | Some(Equal) => true,
- _ => false,
- }
+ matches!(self.partial_cmp(other), Some(Less) | Some(Equal))
}
/// This method tests greater than (for `self` and `other`) and is used by the `>` operator.
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
fn gt(&self, other: &Rhs) -> bool {
- match self.partial_cmp(other) {
- Some(Greater) => true,
- _ => false,
- }
+ matches!(self.partial_cmp(other), Some(Greater))
}
/// This method tests greater than or equal to (for `self` and `other`) and is used by the `>=`
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
fn ge(&self, other: &Rhs) -> bool {
- match self.partial_cmp(other) {
- Some(Greater) | Some(Equal) => true,
- _ => false,
- }
+ matches!(self.partial_cmp(other), Some(Greater) | Some(Equal))
}
}
Self::Item: PartialOrd<I::Item>,
Self: Sized,
{
- match self.partial_cmp(other) {
- Some(Ordering::Less) | Some(Ordering::Equal) => true,
- _ => false,
- }
+ matches!(self.partial_cmp(other), Some(Ordering::Less) | Some(Ordering::Equal))
}
/// Determines if the elements of this `Iterator` are lexicographically
Self::Item: PartialOrd<I::Item>,
Self: Sized,
{
- match self.partial_cmp(other) {
- Some(Ordering::Greater) | Some(Ordering::Equal) => true,
- _ => false,
- }
+ matches!(self.partial_cmp(other), Some(Ordering::Greater) | Some(Ordering::Equal))
}
/// Checks if the elements of this iterator are sorted.
#![feature(bound_cloned)]
#![feature(cfg_target_has_atomic)]
#![feature(concat_idents)]
-#![feature(const_fn)]
+#![feature(const_alloc_layout)]
#![feature(const_if_match)]
#![feature(const_panic)]
#![feature(const_fn_union)]
```"),
#[stable(feature = "no_panic_abs", since = "1.13.0")]
#[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
+ #[allow(unused_attributes)]
#[allow_internal_unstable(const_if_match)]
#[inline]
pub const fn wrapping_abs(self) -> Self {
#[inline]
#[stable(feature = "wrapping", since = "1.7.0")]
#[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
+ #[allow(unused_attributes)]
#[allow_internal_unstable(const_if_match)]
pub const fn overflowing_neg(self) -> (Self, bool) {
if self == Self::min_value() {
```"),
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
+ #[allow(unused_attributes)]
#[allow_internal_unstable(const_if_match)]
#[inline]
#[rustc_inherit_overflow_checks]
#[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")]
#[inline]
pub fn is_ascii_alphabetic(&self) -> bool {
- match *self {
- b'A'..=b'Z' | b'a'..=b'z' => true,
- _ => false,
- }
+ matches!(*self, b'A'..=b'Z' | b'a'..=b'z')
}
/// Checks if the value is an ASCII uppercase character:
#[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")]
#[inline]
pub fn is_ascii_uppercase(&self) -> bool {
- match *self {
- b'A'..=b'Z' => true,
- _ => false,
- }
+ matches!(*self, b'A'..=b'Z')
}
/// Checks if the value is an ASCII lowercase character:
#[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")]
#[inline]
pub fn is_ascii_lowercase(&self) -> bool {
- match *self {
- b'a'..=b'z' => true,
- _ => false,
- }
+ matches!(*self, b'a'..=b'z')
}
/// Checks if the value is an ASCII alphanumeric character:
#[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")]
#[inline]
pub fn is_ascii_alphanumeric(&self) -> bool {
- match *self {
- b'0'..=b'9' | b'A'..=b'Z' | b'a'..=b'z' => true,
- _ => false,
- }
+ matches!(*self, b'0'..=b'9' | b'A'..=b'Z' | b'a'..=b'z')
}
/// Checks if the value is an ASCII decimal digit:
#[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")]
#[inline]
pub fn is_ascii_digit(&self) -> bool {
- match *self {
- b'0'..=b'9' => true,
- _ => false,
- }
+ matches!(*self, b'0'..=b'9')
}
/// Checks if the value is an ASCII hexadecimal digit:
#[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")]
#[inline]
pub fn is_ascii_hexdigit(&self) -> bool {
- match *self {
- b'0'..=b'9' | b'A'..=b'F' | b'a'..=b'f' => true,
- _ => false,
- }
+ matches!(*self, b'0'..=b'9' | b'A'..=b'F' | b'a'..=b'f')
}
/// Checks if the value is an ASCII punctuation character:
#[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")]
#[inline]
pub fn is_ascii_punctuation(&self) -> bool {
- match *self {
- b'!'..=b'/' | b':'..=b'@' | b'['..=b'`' | b'{'..=b'~' => true,
- _ => false,
- }
+ matches!(*self, b'!'..=b'/' | b':'..=b'@' | b'['..=b'`' | b'{'..=b'~')
}
/// Checks if the value is an ASCII graphic character:
#[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")]
#[inline]
pub fn is_ascii_graphic(&self) -> bool {
- match *self {
- b'!'..=b'~' => true,
- _ => false,
- }
+ matches!(*self, b'!'..=b'~')
}
/// Checks if the value is an ASCII whitespace character:
#[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")]
#[inline]
pub fn is_ascii_whitespace(&self) -> bool {
- match *self {
- b'\t' | b'\n' | b'\x0C' | b'\r' | b' ' => true,
- _ => false,
- }
+ matches!(*self, b'\t' | b'\n' | b'\x0C' | b'\r' | b' ')
}
/// Checks if the value is an ASCII control character:
#[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")]
#[inline]
pub fn is_ascii_control(&self) -> bool {
- match *self {
- b'\0'..=b'\x1F' | b'\x7F' => true,
- _ => false,
- }
+ matches!(*self, b'\0'..=b'\x1F' | b'\x7F')
}
}
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn is_some(&self) -> bool {
- match *self {
- Some(_) => true,
- None => false,
- }
+ matches!(*self, Some(_))
}
/// Returns `true` if the option is a [`None`] value.
/// x.expect("the world is ending"); // panics with `the world is ending`
/// ```
#[inline]
+ #[track_caller]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn expect(self, msg: &str) -> T {
match self {
/// assert_eq!(x.unwrap(), "air"); // fails
/// ```
#[inline]
+ #[track_caller]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn unwrap(self) -> T {
match self {
/// }
/// ```
#[inline]
+ #[track_caller]
#[unstable(feature = "option_expect_none", reason = "newly added", issue = "62633")]
pub fn expect_none(self, msg: &str) {
if let Some(val) = self {
/// }
/// ```
#[inline]
+ #[track_caller]
#[unstable(feature = "option_unwrap_none", reason = "newly added", issue = "62633")]
pub fn unwrap_none(self) {
if let Some(val) = self {
// This is a separate function to reduce the code size of .expect() itself.
#[inline(never)]
#[cold]
+#[track_caller]
fn expect_failed(msg: &str) -> ! {
panic!("{}", msg)
}
// This is a separate function to reduce the code size of .expect_none() itself.
#[inline(never)]
#[cold]
+#[track_caller]
fn expect_none_failed(msg: &str, value: &dyn fmt::Debug) -> ! {
panic!("{}: {:?}", msg, value)
}
#[stable(feature = "pin", since = "1.33.0")]
pub unsafe fn map_unchecked<U, F>(self, func: F) -> Pin<&'a U>
where
+ U: ?Sized,
F: FnOnce(&T) -> &U,
{
let pointer = &*self.pointer;
#[stable(feature = "pin", since = "1.33.0")]
pub unsafe fn map_unchecked_mut<U, F>(self, func: F) -> Pin<&'a mut U>
where
+ U: ?Sized,
F: FnOnce(&mut T) -> &mut U,
{
let pointer = Pin::get_unchecked_mut(self);
/// *first_value = 4;
/// println!("{:?}", s); // It'll print: "[4, 2, 3]".
/// ```
+ ///
+ /// # Null-unchecked version
+ ///
+ /// If you are sure the pointer can never be null and are looking for some kind of
+ /// `as_mut_unchecked` that returns the `&mut T` instead of `Option<&mut T>`, know that
+ /// you can dereference the pointer directly.
+ ///
+ /// ```
+ /// let mut s = [1, 2, 3];
+ /// let ptr: *mut u32 = s.as_mut_ptr();
+ /// let first_value = unsafe { &mut *ptr };
+ /// *first_value = 4;
+ /// println!("{:?}", s); // It'll print: "[4, 2, 3]".
+ /// ```
#[stable(feature = "ptr_as_ref", since = "1.9.0")]
#[inline]
pub unsafe fn as_mut<'a>(self) -> Option<&'a mut T> {
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
pub const fn is_ok(&self) -> bool {
- match *self {
- Ok(_) => true,
- Err(_) => false,
- }
+ matches!(*self, Ok(_))
}
/// Returns `true` if the result is [`Err`].
/// x.unwrap(); // panics with `emergency failure`
/// ```
#[inline]
+ #[track_caller]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn unwrap(self) -> T {
match self {
/// x.expect("Testing expect"); // panics with `Testing expect: emergency failure`
/// ```
#[inline]
+ #[track_caller]
#[stable(feature = "result_expect", since = "1.4.0")]
pub fn expect(self, msg: &str) -> T {
match self {
/// assert_eq!(x.unwrap_err(), "emergency failure");
/// ```
#[inline]
+ #[track_caller]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn unwrap_err(self) -> E {
match self {
/// x.expect_err("Testing expect_err"); // panics with `Testing expect_err: 10`
/// ```
#[inline]
+ #[track_caller]
#[stable(feature = "result_expect_err", since = "1.17.0")]
pub fn expect_err(self, msg: &str) -> E {
match self {
}
}
+#[unstable(feature = "unwrap_infallible", reason = "newly added", issue = "61695")]
+impl<T, E: Into<!>> Result<T, E> {
+ /// Unwraps a result that can never be an [`Err`], yielding the content of the [`Ok`].
+ ///
+ /// Unlike [`unwrap`], this method is known to never panic on the
+ /// result types it is implemented for. Therefore, it can be used
+ /// instead of `unwrap` as a maintainability safeguard that will fail
+ /// to compile if the error type of the `Result` is later changed
+ /// to an error that can actually occur.
+ ///
+ /// [`Ok`]: enum.Result.html#variant.Ok
+ /// [`Err`]: enum.Result.html#variant.Err
+ /// [`unwrap`]: enum.Result.html#method.unwrap
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// # #![feature(never_type)]
+ /// # #![feature(unwrap_infallible)]
+ ///
+ /// fn only_good_news() -> Result<String, !> {
+ /// Ok("this is fine".into())
+ /// }
+ ///
+ /// let s: String = only_good_news().into_ok();
+ /// println!("{}", s);
+ /// ```
+ #[inline]
+ pub fn into_ok(self) -> T {
+ match self {
+ Ok(x) => x,
+ Err(e) => e.into(),
+ }
+ }
+}
+
#[unstable(feature = "inner_deref", reason = "newly added", issue = "50264")]
impl<T: Deref, E> Result<T, E> {
/// Converts from `Result<T, E>` (or `&Result<T, E>`) to `Result<&T::Target, &E>`.
///
/// Leaves the original `Result` in-place, creating a new one containing a reference to the
/// `Ok` type's `Deref::Target` type.
- pub fn as_deref_ok(&self) -> Result<&T::Target, &E> {
+ pub fn as_deref(&self) -> Result<&T::Target, &E> {
self.as_ref().map(|t| t.deref())
}
}
}
}
-#[unstable(feature = "inner_deref", reason = "newly added", issue = "50264")]
-impl<T: Deref, E: Deref> Result<T, E> {
- /// Converts from `Result<T, E>` (or `&Result<T, E>`) to `Result<&T::Target, &E::Target>`.
- ///
- /// Leaves the original `Result` in-place, creating a new one containing a reference to both
- /// the `Ok` and `Err` types' `Deref::Target` types.
- pub fn as_deref(&self) -> Result<&T::Target, &E::Target> {
- self.as_ref().map(|t| t.deref()).map_err(|e| e.deref())
- }
-}
-
#[unstable(feature = "inner_deref", reason = "newly added", issue = "50264")]
impl<T: DerefMut, E> Result<T, E> {
/// Converts from `Result<T, E>` (or `&mut Result<T, E>`) to `Result<&mut T::Target, &mut E>`.
///
/// Leaves the original `Result` in-place, creating a new one containing a mutable reference to
/// the `Ok` type's `Deref::Target` type.
- pub fn as_deref_mut_ok(&mut self) -> Result<&mut T::Target, &mut E> {
+ pub fn as_deref_mut(&mut self) -> Result<&mut T::Target, &mut E> {
self.as_mut().map(|t| t.deref_mut())
}
}
}
}
-#[unstable(feature = "inner_deref", reason = "newly added", issue = "50264")]
-impl<T: DerefMut, E: DerefMut> Result<T, E> {
- /// Converts from `Result<T, E>` (or `&mut Result<T, E>`) to
- /// `Result<&mut T::Target, &mut E::Target>`.
- ///
- /// Leaves the original `Result` in-place, creating a new one containing a mutable reference to
- /// both the `Ok` and `Err` types' `Deref::Target` types.
- pub fn as_deref_mut(&mut self) -> Result<&mut T::Target, &mut E::Target> {
- self.as_mut().map(|t| t.deref_mut()).map_err(|e| e.deref_mut())
- }
-}
-
impl<T, E> Result<Option<T>, E> {
/// Transposes a `Result` of an `Option` into an `Option` of a `Result`.
///
// This is a separate function to reduce the code size of the methods
#[inline(never)]
#[cold]
+#[track_caller]
fn unwrap_failed(msg: &str, error: &dyn fmt::Debug) -> ! {
panic!("{}: {:?}", msg, error)
}
/// Checks whether the pattern matches at the front of the haystack
#[inline]
fn is_prefix_of(self, haystack: &'a str) -> bool {
- match self.into_searcher(haystack).next() {
- SearchStep::Match(0, _) => true,
- _ => false,
- }
+ matches!(self.into_searcher(haystack).next(), SearchStep::Match(0, _))
}
/// Checks whether the pattern matches at the back of the haystack
where
Self::Searcher: ReverseSearcher<'a>,
{
- match self.into_searcher(haystack).next_back() {
- SearchStep::Match(_, j) if haystack.len() == j => true,
- _ => false,
- }
+ matches!(self.into_searcher(haystack).next_back(), SearchStep::Match(_, j) if haystack.len() == j)
}
}
/// This function is different from [`std::thread::yield_now`] which directly yields to the
/// system's scheduler, whereas `spin_loop_hint` does not interact with the operating system.
///
-/// Spin locks can be very efficient for short lock durations because they do not involve context
-/// switches or interaction with the operating system. For long lock durations they become wasteful
-/// however because they use CPU cycles for the entire lock duration, and using a
-/// [`std::sync::Mutex`] is likely the better approach. If actively spinning for a long time is
-/// required, e.g. because code polls a non-blocking API, calling [`std::thread::yield_now`]
-/// or [`std::thread::sleep`] may be the best option.
-///
-/// **Note**: Spin locks are based on the underlying assumption that another thread will release
-/// the lock 'soon'. In order for this to work, that other thread must run on a different CPU or
-/// core (at least potentially). Spin locks do not work efficiently on single CPU / core platforms.
+/// A common use case for `spin_loop_hint` is implementing bounded optimistic spinning in a CAS
+/// loop in synchronization primitives. To avoid problems like priority inversion, it is strongly
+/// recommended that the spin loop is terminated after a finite amount of iterations and an
+/// appropriate blocking syscall is made.
///
/// **Note**: On platforms that do not support receiving spin-loop hints this function does not
/// do anything at all.
#[inline]
#[stable(feature = "futures_api", since = "1.36.0")]
pub fn is_ready(&self) -> bool {
- match *self {
- Poll::Ready(_) => true,
- Poll::Pending => false,
- }
+ matches!(*self, Poll::Ready(_))
}
/// Returns `true` if this is `Poll::Pending`
#![feature(slice_internals)]
#![feature(slice_partition_dedup)]
#![feature(int_error_matching)]
-#![feature(const_fn)]
#![feature(array_value_iter)]
#![feature(iter_partition_in_place)]
#![feature(iter_is_partitioned)]
#![feature(slice_from_raw_parts)]
#![feature(const_slice_from_raw_parts)]
#![feature(const_raw_ptr_deref)]
+#![feature(never_type)]
+#![feature(unwrap_infallible)]
extern crate test;
assert_eq!(op2().unwrap_or_default(), 0);
}
+#[test]
+pub fn test_into_ok() {
+ fn infallible_op() -> Result<isize, !> {
+ Ok(666)
+ }
+
+ assert_eq!(infallible_op().into_ok(), 666);
+
+ enum MyNeverToken {}
+ impl From<MyNeverToken> for ! {
+ fn from(never: MyNeverToken) -> ! {
+ match never {}
+ }
+ }
+
+ fn infallible_op2() -> Result<isize, MyNeverToken> {
+ Ok(667)
+ }
+
+ assert_eq!(infallible_op2().into_ok(), 667);
+}
+
#[test]
fn test_try() {
fn try_result_some() -> Option<u8> {
#[test]
fn test_result_as_deref() {
- // &Result<T: Deref, E>::Ok(T).as_deref_ok() ->
+ // &Result<T: Deref, E>::Ok(T).as_deref() ->
// Result<&T::Deref::Target, &E>::Ok(&*T)
let ref_ok = &Result::Ok::<&i32, u8>(&42);
let expected_result = Result::Ok::<&i32, &u8>(&42);
- assert_eq!(ref_ok.as_deref_ok(), expected_result);
-
- let ref_ok = &Result::Ok::<String, u32>(String::from("a result"));
- let expected_result = Result::Ok::<&str, &u32>("a result");
- assert_eq!(ref_ok.as_deref_ok(), expected_result);
-
- let ref_ok = &Result::Ok::<Vec<i32>, u32>(vec![1, 2, 3, 4, 5]);
- let expected_result = Result::Ok::<&[i32], &u32>([1, 2, 3, 4, 5].as_slice());
- assert_eq!(ref_ok.as_deref_ok(), expected_result);
-
- // &Result<T: Deref, E: Deref>::Ok(T).as_deref() ->
- // Result<&T::Deref::Target, &E::Deref::Target>::Ok(&*T)
- let ref_ok = &Result::Ok::<&i32, &u8>(&42);
- let expected_result = Result::Ok::<&i32, &u8>(&42);
assert_eq!(ref_ok.as_deref(), expected_result);
- let ref_ok = &Result::Ok::<String, &u32>(String::from("a result"));
+ let ref_ok = &Result::Ok::<String, u32>(String::from("a result"));
let expected_result = Result::Ok::<&str, &u32>("a result");
assert_eq!(ref_ok.as_deref(), expected_result);
- let ref_ok = &Result::Ok::<Vec<i32>, &u32>(vec![1, 2, 3, 4, 5]);
+ let ref_ok = &Result::Ok::<Vec<i32>, u32>(vec![1, 2, 3, 4, 5]);
let expected_result = Result::Ok::<&[i32], &u32>([1, 2, 3, 4, 5].as_slice());
assert_eq!(ref_ok.as_deref(), expected_result);
// &Result<T: Deref, E: Deref>::Err(T).as_deref_err() ->
// Result<&T, &E::Deref::Target>::Err(&*E)
let ref_err = &Result::Err::<&u8, &i32>(&41);
- let expected_result = Result::Err::<&u8, &i32>(&41);
+ let expected_result = Result::Err::<&u8, &&i32>(&&41);
assert_eq!(ref_err.as_deref(), expected_result);
- let ref_err = &Result::Err::<&u32, String>(String::from("an error"));
- let expected_result = Result::Err::<&u32, &str>("an error");
+ let s = String::from("an error");
+ let ref_err = &Result::Err::<&u32, String>(s.clone());
+ let expected_result = Result::Err::<&u32, &String>(&s);
assert_eq!(ref_err.as_deref(), expected_result);
- let ref_err = &Result::Err::<&u32, Vec<i32>>(vec![5, 4, 3, 2, 1]);
- let expected_result = Result::Err::<&u32, &[i32]>([5, 4, 3, 2, 1].as_slice());
+ let v = vec![5, 4, 3, 2, 1];
+ let ref_err = &Result::Err::<&u32, Vec<i32>>(v.clone());
+ let expected_result = Result::Err::<&u32, &Vec<i32>>(&v);
assert_eq!(ref_err.as_deref(), expected_result);
// The following cases test calling `as_deref_*` with the wrong variant (i.e.
- // `as_deref_ok()` with a `Result::Err()`, or `as_deref_err()` with a `Result::Ok()`.
+ // `as_deref()` with a `Result::Err()`, or `as_deref_err()` with a `Result::Ok()`.
// While uncommon, these cases are supported to ensure that an `as_deref_*`
// call can still be made even when one of the Result types does not implement
// `Deref` (for example, std::io::Error).
let expected_result = Result::Ok::<&[i32; 5], &u32>(&[1, 2, 3, 4, 5]);
assert_eq!(ref_ok.as_deref_err(), expected_result);
- // &Result<T: Deref, E>::Err(E).as_deref_ok() ->
+ // &Result<T: Deref, E>::Err(E).as_deref() ->
// Result<&T::Deref::Target, &E>::Err(&E)
let ref_err = &Result::Err::<&u8, i32>(41);
let expected_result = Result::Err::<&u8, &i32>(&41);
- assert_eq!(ref_err.as_deref_ok(), expected_result);
+ assert_eq!(ref_err.as_deref(), expected_result);
let ref_err = &Result::Err::<&u32, &str>("an error");
let expected_result = Result::Err::<&u32, &&str>(&"an error");
- assert_eq!(ref_err.as_deref_ok(), expected_result);
+ assert_eq!(ref_err.as_deref(), expected_result);
let ref_err = &Result::Err::<&u32, [i32; 5]>([5, 4, 3, 2, 1]);
let expected_result = Result::Err::<&u32, &[i32; 5]>(&[5, 4, 3, 2, 1]);
- assert_eq!(ref_err.as_deref_ok(), expected_result);
+ assert_eq!(ref_err.as_deref(), expected_result);
}
#[test]
fn test_result_as_deref_mut() {
- // &mut Result<T: Deref, E>::Ok(T).as_deref_mut_ok() ->
+ // &mut Result<T: Deref, E>::Ok(T).as_deref_mut() ->
// Result<&mut T::Deref::Target, &mut E>::Ok(&mut *T)
let mut val = 42;
let mut expected_val = 42;
let mut_ok = &mut Result::Ok::<&mut i32, u8>(&mut val);
let expected_result = Result::Ok::<&mut i32, &mut u8>(&mut expected_val);
- assert_eq!(mut_ok.as_deref_mut_ok(), expected_result);
-
- let mut expected_string = String::from("a result");
- let mut_ok = &mut Result::Ok::<String, u32>(expected_string.clone());
- let expected_result = Result::Ok::<&mut str, &mut u32>(expected_string.deref_mut());
- assert_eq!(mut_ok.as_deref_mut_ok(), expected_result);
-
- let mut expected_vec = vec![1, 2, 3, 4, 5];
- let mut_ok = &mut Result::Ok::<Vec<i32>, u32>(expected_vec.clone());
- let expected_result = Result::Ok::<&mut [i32], &mut u32>(expected_vec.as_mut_slice());
- assert_eq!(mut_ok.as_deref_mut_ok(), expected_result);
-
- // &mut Result<T: Deref, E: Deref>::Ok(T).as_deref_mut() ->
- // Result<&mut T::Deref::Target, &mut E::Deref::Target>::Ok(&mut *T)
- let mut val = 42;
- let mut expected_val = 42;
- let mut_ok = &mut Result::Ok::<&mut i32, &mut u8>(&mut val);
- let expected_result = Result::Ok::<&mut i32, &mut u8>(&mut expected_val);
assert_eq!(mut_ok.as_deref_mut(), expected_result);
let mut expected_string = String::from("a result");
- let mut_ok = &mut Result::Ok::<String, &mut u32>(expected_string.clone());
+ let mut_ok = &mut Result::Ok::<String, u32>(expected_string.clone());
let expected_result = Result::Ok::<&mut str, &mut u32>(expected_string.deref_mut());
assert_eq!(mut_ok.as_deref_mut(), expected_result);
let mut expected_vec = vec![1, 2, 3, 4, 5];
- let mut_ok = &mut Result::Ok::<Vec<i32>, &mut u32>(expected_vec.clone());
+ let mut_ok = &mut Result::Ok::<Vec<i32>, u32>(expected_vec.clone());
let expected_result = Result::Ok::<&mut [i32], &mut u32>(expected_vec.as_mut_slice());
assert_eq!(mut_ok.as_deref_mut(), expected_result);
// &mut Result<T: Deref, E: Deref>::Err(T).as_deref_mut_err() ->
// Result<&mut T, &mut E::Deref::Target>::Err(&mut *E)
let mut val = 41;
- let mut expected_val = 41;
- let mut_err = &mut Result::Err::<&mut u8, &mut i32>(&mut val);
- let expected_result = Result::Err::<&mut u8, &mut i32>(&mut expected_val);
+ let mut_err = &mut Result::Err::<&mut u8, i32>(val);
+ let expected_result = Result::Err::<&mut u8, &mut i32>(&mut val);
assert_eq!(mut_err.as_deref_mut(), expected_result);
let mut expected_string = String::from("an error");
let mut_err = &mut Result::Err::<&mut u32, String>(expected_string.clone());
- let expected_result = Result::Err::<&mut u32, &mut str>(expected_string.as_mut_str());
+ let expected_result = Result::Err::<&mut u32, &mut String>(&mut expected_string);
assert_eq!(mut_err.as_deref_mut(), expected_result);
let mut expected_vec = vec![5, 4, 3, 2, 1];
let mut_err = &mut Result::Err::<&mut u32, Vec<i32>>(expected_vec.clone());
- let expected_result = Result::Err::<&mut u32, &mut [i32]>(expected_vec.as_mut_slice());
+ let expected_result = Result::Err::<&mut u32, &mut Vec<i32>>(&mut expected_vec);
assert_eq!(mut_err.as_deref_mut(), expected_result);
// The following cases test calling `as_deref_mut_*` with the wrong variant (i.e.
- // `as_deref_mut_ok()` with a `Result::Err()`, or `as_deref_mut_err()` with a `Result::Ok()`.
+ // `as_deref_mut()` with a `Result::Err()`, or `as_deref_mut_err()` with a `Result::Ok()`.
// While uncommon, these cases are supported to ensure that an `as_deref_mut_*`
// call can still be made even when one of the Result types does not implement
// `Deref` (for example, std::io::Error).
let expected_result = Result::Ok::<&mut [i32; 5], &mut u32>(&mut expected_arr);
assert_eq!(mut_ok.as_deref_mut_err(), expected_result);
- // &mut Result<T: Deref, E>::Err(E).as_deref_mut_ok() ->
+ // &mut Result<T: Deref, E>::Err(E).as_deref_mut() ->
// Result<&mut T::Deref::Target, &mut E>::Err(&mut E)
let mut expected_val = 41;
let mut_err = &mut Result::Err::<&mut u8, i32>(expected_val.clone());
let expected_result = Result::Err::<&mut u8, &mut i32>(&mut expected_val);
- assert_eq!(mut_err.as_deref_mut_ok(), expected_result);
+ assert_eq!(mut_err.as_deref_mut(), expected_result);
let string = String::from("an error");
let expected_string = string.clone();
let mut ref_str = expected_string.as_ref();
let mut_err = &mut Result::Err::<&mut u32, &str>(string.as_str());
let expected_result = Result::Err::<&mut u32, &mut &str>(&mut ref_str);
- assert_eq!(mut_err.as_deref_mut_ok(), expected_result);
+ assert_eq!(mut_err.as_deref_mut(), expected_result);
let mut expected_arr = [5, 4, 3, 2, 1];
let mut_err = &mut Result::Err::<&mut u32, [i32; 5]>(expected_arr.clone());
let expected_result = Result::Err::<&mut u32, &mut [i32; 5]>(&mut expected_arr);
- assert_eq!(mut_err.as_deref_mut_ok(), expected_result);
+ assert_eq!(mut_err.as_deref_mut(), expected_result);
}
pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
let sz = mem::size_of_val(&data);
let exception = __cxa_allocate_exception(sz);
- if exception == ptr::null_mut() {
+ if exception.is_null() {
return uw::_URC_FATAL_PHASE1_ERROR as u32;
}
ptr::write(exception as *mut _, data);
#![feature(nll)]
#![feature(staged_api)]
#![feature(allow_internal_unstable)]
-#![feature(const_fn)]
#![feature(decl_macro)]
#![feature(extern_types)]
#![feature(in_band_lifetimes)]
"InstrProfilingPlatformLinux.c",
"InstrProfilingPlatformOther.c",
"InstrProfilingPlatformWindows.c",
- "InstrProfilingRuntime.cc",
"InstrProfilingUtil.c",
"InstrProfilingValue.c",
"InstrProfilingWriter.c",
let root = env::var_os("RUST_COMPILER_RT_ROOT").unwrap();
let root = Path::new(&root);
+ let src_root = root.join("lib").join("profile");
for src in profile_sources {
- cfg.file(root.join("lib").join("profile").join(src));
+ cfg.file(src_root.join(src));
}
+ // The file was renamed in LLVM 10.
+ let old_runtime_path = src_root.join("InstrProfilingRuntime.cc");
+ let new_runtime_path = src_root.join("InstrProfilingRuntime.cpp");
+ cfg.file(if old_runtime_path.exists() { old_runtime_path } else { new_runtime_path });
+
+ cfg.include(root.join("include"));
cfg.warnings(false);
cfg.compile("profiler-rt");
}
fmt_macros = { path = "../libfmt_macros" }
graphviz = { path = "../libgraphviz" }
jobserver = "0.1"
-num_cpus = "1.0"
scoped-tls = "1.0"
log = { version = "0.4", features = ["release_max_level_info", "std"] }
rustc-rayon = "0.3.0"
rustc_target = { path = "../librustc_target" }
rustc_macros = { path = "../librustc_macros" }
rustc_data_structures = { path = "../librustc_data_structures" }
+rustc_errors = { path = "../librustc_errors" }
rustc_index = { path = "../librustc_index" }
-errors = { path = "../librustc_errors", package = "rustc_errors" }
rustc_serialize = { path = "../libserialize", package = "serialize" }
syntax = { path = "../libsyntax" }
rustc_span = { path = "../librustc_span" }
parking_lot = "0.9"
byteorder = { version = "1.3" }
chalk-engine = { version = "0.9.0", default-features=false }
-rustc_fs_util = { path = "../librustc_fs_util" }
smallvec = { version = "1.0", features = ["union", "may_dangle"] }
-measureme = "0.5"
+measureme = "0.7.1"
rustc_error_codes = { path = "../librustc_error_codes" }
rustc_session = { path = "../librustc_session" }
use crate::ty::{self, TyCtxt};
-use errors::Diagnostic;
use parking_lot::{Condvar, Mutex};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::profiling::QueryInvocationId;
use rustc_data_structures::sharded::{self, Sharded};
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_data_structures::sync::{AtomicU32, AtomicU64, Lock, Lrc, Ordering};
+use rustc_errors::Diagnostic;
use rustc_index::vec::{Idx, IndexVec};
use smallvec::SmallVec;
use std::collections::hash_map::Entry;
use std::env;
use std::hash::Hash;
use std::mem;
-use std::sync::atomic::Ordering::SeqCst;
+use std::sync::atomic::Ordering::Relaxed;
use crate::ich::{Fingerprint, StableHashingContext, StableHashingContextProvider};
#[derive(Clone)]
pub struct DepGraph {
data: Option<Lrc<DepGraphData>>,
+
+ /// This field is used for assigning DepNodeIndices when running in
+ /// non-incremental mode. Even in non-incremental mode we make sure that
+ /// each task has a `DepNodeIndex` that uniquely identifies it. This unique
+ /// ID is used for self-profiling.
+ virtual_dep_node_index: Lrc<AtomicU32>,
}
rustc_index::newtype_index! {
pub const INVALID: DepNodeIndex = DepNodeIndex::MAX;
}
+impl std::convert::From<DepNodeIndex> for QueryInvocationId {
+ #[inline]
+ fn from(dep_node_index: DepNodeIndex) -> Self {
+ QueryInvocationId(dep_node_index.as_u32())
+ }
+}
+
#[derive(PartialEq)]
pub enum DepNodeColor {
Red,
previous: prev_graph,
colors: DepNodeColorMap::new(prev_graph_node_count),
})),
+ virtual_dep_node_index: Lrc::new(AtomicU32::new(0)),
}
}
pub fn new_disabled() -> DepGraph {
- DepGraph { data: None }
+ DepGraph { data: None, virtual_dep_node_index: Lrc::new(AtomicU32::new(0)) }
}
/// Returns `true` if we are actually building the full dep-graph, and `false` otherwise.
(result, dep_node_index)
} else {
- (task(cx, arg), DepNodeIndex::INVALID)
+ (task(cx, arg), self.next_virtual_depnode_index())
}
}
let dep_node_index = data.current.complete_anon_task(dep_kind, task_deps);
(result, dep_node_index)
} else {
- (op(), DepNodeIndex::INVALID)
+ (op(), self.next_virtual_depnode_index())
}
}
let current_dep_graph = &self.data.as_ref().unwrap().current;
Some((
- current_dep_graph.total_read_count.load(SeqCst),
- current_dep_graph.total_duplicate_read_count.load(SeqCst),
+ current_dep_graph.total_read_count.load(Relaxed),
+ current_dep_graph.total_duplicate_read_count.load(Relaxed),
))
} else {
None
}
}
}
+
+ fn next_virtual_depnode_index(&self) -> DepNodeIndex {
+ let index = self.virtual_dep_node_index.fetch_add(1, Relaxed);
+ DepNodeIndex::from_u32(index)
+ }
}
/// A "work product" is an intermediate result that we save into the
if let Some(task_deps) = icx.task_deps {
let mut task_deps = task_deps.lock();
if cfg!(debug_assertions) {
- self.current.total_read_count.fetch_add(1, SeqCst);
+ self.current.total_read_count.fetch_add(1, Relaxed);
}
if task_deps.read_set.insert(source) {
task_deps.reads.push(source);
}
}
} else if cfg!(debug_assertions) {
- self.current.total_duplicate_read_count.fetch_add(1, SeqCst);
+ self.current.total_duplicate_read_count.fetch_add(1, Relaxed);
}
}
})
//! conflicts between multiple such attributes attached to the same
//! item.
-use crate::hir::intravisit::{self, NestedVisitorMap, Visitor};
-use crate::lint::builtin::UNUSED_ATTRIBUTES;
+use crate::hir::map::Map;
use crate::ty::query::Providers;
use crate::ty::TyCtxt;
+
use rustc_error_codes::*;
+use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_hir::DUMMY_HIR_ID;
use rustc_hir::{self, HirId, Item, ItemKind, TraitItem, TraitItemKind};
+use rustc_session::lint::builtin::UNUSED_ATTRIBUTES;
use rustc_span::symbol::sym;
use rustc_span::Span;
use syntax::ast::Attribute;
// Error on repr(transparent, <anything else>).
if is_transparent && hints.len() > 1 {
let hint_spans: Vec<_> = hint_spans.clone().collect();
- span_err!(
+ struct_span_err!(
self.tcx.sess,
hint_spans,
E0692,
"transparent {} cannot have other repr hints",
target
- );
+ )
+ .emit();
}
// Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)
if (int_reprs > 1)
|| (is_simd && is_c)
|| (int_reprs == 1 && is_c && item.map_or(false, |item| is_c_like_enum(item)))
{
- let hint_spans: Vec<_> = hint_spans.collect();
- span_warn!(self.tcx.sess, hint_spans, E0566, "conflicting representation hints");
+ struct_span_err!(
+ self.tcx.sess,
+ hint_spans.collect::<Vec<Span>>(),
+ E0566,
+ "conflicting representation hints",
+ )
+ .emit();
}
}
}
impl Visitor<'tcx> for CheckAttrVisitor<'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, Self::Map> {
NestedVisitorMap::OnlyBodies(&self.tcx.hir())
}
+++ /dev/null
-//! HIR walker for walking the contents of nodes.
-//!
-//! **For an overview of the visitor strategy, see the docs on the
-//! `super::itemlikevisit::ItemLikeVisitor` trait.**
-//!
-//! If you have decided to use this visitor, here are some general
-//! notes on how to do so:
-//!
-//! Each overridden visit method has full control over what
-//! happens with its node, it can do its own traversal of the node's children,
-//! call `intravisit::walk_*` to apply the default traversal algorithm, or prevent
-//! deeper traversal by doing nothing.
-//!
-//! When visiting the HIR, the contents of nested items are NOT visited
-//! by default. This is different from the AST visitor, which does a deep walk.
-//! Hence this module is called `intravisit`; see the method `visit_nested_item`
-//! for more details.
-//!
-//! Note: it is an important invariant that the default visitor walks
-//! the body of a function in "execution order" - more concretely, if
-//! we consider the reverse post-order (RPO) of the CFG implied by the HIR,
-//! then a pre-order traversal of the HIR is consistent with the CFG RPO
-//! on the *initial CFG point* of each HIR node, while a post-order traversal
-//! of the HIR is consistent with the CFG RPO on each *final CFG point* of
-//! each CFG node.
-//!
-//! One thing that follows is that if HIR node A always starts/ends executing
-//! before HIR node B, then A appears in traversal pre/postorder before B,
-//! respectively. (This follows from RPO respecting CFG domination).
-//!
-//! This order consistency is required in a few places in rustc, for
-//! example generator inference, and possibly also HIR borrowck.
-
-use crate::hir::map::Map;
-
-use rustc_hir::itemlikevisit::{ItemLikeVisitor, ParItemLikeVisitor};
-use rustc_hir::*;
-use rustc_span::Span;
-use syntax::ast::{Attribute, Ident, Label, Name};
-
-pub struct DeepVisitor<'v, V> {
- visitor: &'v mut V,
-}
-
-impl<'v, 'hir, V> DeepVisitor<'v, V>
-where
- V: Visitor<'hir> + 'v,
-{
- pub fn new(base: &'v mut V) -> Self {
- DeepVisitor { visitor: base }
- }
-}
-
-impl<'v, 'hir, V> ItemLikeVisitor<'hir> for DeepVisitor<'v, V>
-where
- V: Visitor<'hir>,
-{
- fn visit_item(&mut self, item: &'hir Item<'hir>) {
- self.visitor.visit_item(item);
- }
-
- fn visit_trait_item(&mut self, trait_item: &'hir TraitItem<'hir>) {
- self.visitor.visit_trait_item(trait_item);
- }
-
- fn visit_impl_item(&mut self, impl_item: &'hir ImplItem<'hir>) {
- self.visitor.visit_impl_item(impl_item);
- }
-}
-
-pub trait IntoVisitor<'hir> {
- type Visitor: Visitor<'hir>;
- fn into_visitor(&self) -> Self::Visitor;
-}
-
-pub struct ParDeepVisitor<V>(pub V);
-
-impl<'hir, V> ParItemLikeVisitor<'hir> for ParDeepVisitor<V>
-where
- V: IntoVisitor<'hir>,
-{
- fn visit_item(&self, item: &'hir Item<'hir>) {
- self.0.into_visitor().visit_item(item);
- }
-
- fn visit_trait_item(&self, trait_item: &'hir TraitItem<'hir>) {
- self.0.into_visitor().visit_trait_item(trait_item);
- }
-
- fn visit_impl_item(&self, impl_item: &'hir ImplItem<'hir>) {
- self.0.into_visitor().visit_impl_item(impl_item);
- }
-}
-
-#[derive(Copy, Clone)]
-pub enum FnKind<'a> {
- /// `#[xxx] pub async/const/extern "Abi" fn foo()`
- ItemFn(Ident, &'a Generics<'a>, FnHeader, &'a Visibility<'a>, &'a [Attribute]),
-
- /// `fn foo(&self)`
- Method(Ident, &'a FnSig<'a>, Option<&'a Visibility<'a>>, &'a [Attribute]),
-
- /// `|x, y| {}`
- Closure(&'a [Attribute]),
-}
-
-impl<'a> FnKind<'a> {
- pub fn attrs(&self) -> &'a [Attribute] {
- match *self {
- FnKind::ItemFn(.., attrs) => attrs,
- FnKind::Method(.., attrs) => attrs,
- FnKind::Closure(attrs) => attrs,
- }
- }
-
- pub fn header(&self) -> Option<&FnHeader> {
- match *self {
- FnKind::ItemFn(_, _, ref header, _, _) => Some(header),
- FnKind::Method(_, ref sig, _, _) => Some(&sig.header),
- FnKind::Closure(_) => None,
- }
- }
-}
-
-/// Specifies what nested things a visitor wants to visit. The most
-/// common choice is `OnlyBodies`, which will cause the visitor to
-/// visit fn bodies for fns that it encounters, but skip over nested
-/// item-like things.
-///
-/// See the comments on `ItemLikeVisitor` for more details on the overall
-/// visit strategy.
-pub enum NestedVisitorMap<'this, 'tcx> {
- /// Do not visit any nested things. When you add a new
- /// "non-nested" thing, you will want to audit such uses to see if
- /// they remain valid.
- ///
- /// Use this if you are only walking some particular kind of tree
- /// (i.e., a type, or fn signature) and you don't want to thread a
- /// HIR map around.
- None,
-
- /// Do not visit nested item-like things, but visit nested things
- /// that are inside of an item-like.
- ///
- /// **This is the most common choice.** A very common pattern is
- /// to use `visit_all_item_likes()` as an outer loop,
- /// and to have the visitor that visits the contents of each item
- /// using this setting.
- OnlyBodies(&'this Map<'tcx>),
-
- /// Visits all nested things, including item-likes.
- ///
- /// **This is an unusual choice.** It is used when you want to
- /// process everything within their lexical context. Typically you
- /// kick off the visit by doing `walk_krate()`.
- All(&'this Map<'tcx>),
-}
-
-impl<'this, 'tcx> NestedVisitorMap<'this, 'tcx> {
- /// Returns the map to use for an "intra item-like" thing (if any).
- /// E.g., function body.
- pub fn intra(self) -> Option<&'this Map<'tcx>> {
- match self {
- NestedVisitorMap::None => None,
- NestedVisitorMap::OnlyBodies(map) => Some(map),
- NestedVisitorMap::All(map) => Some(map),
- }
- }
-
- /// Returns the map to use for an "item-like" thing (if any).
- /// E.g., item, impl-item.
- pub fn inter(self) -> Option<&'this Map<'tcx>> {
- match self {
- NestedVisitorMap::None => None,
- NestedVisitorMap::OnlyBodies(_) => None,
- NestedVisitorMap::All(map) => Some(map),
- }
- }
-}
-
-/// Each method of the Visitor trait is a hook to be potentially
-/// overridden. Each method's default implementation recursively visits
-/// the substructure of the input via the corresponding `walk` method;
-/// e.g., the `visit_mod` method by default calls `intravisit::walk_mod`.
-///
-/// Note that this visitor does NOT visit nested items by default
-/// (this is why the module is called `intravisit`, to distinguish it
-/// from the AST's `visit` module, which acts differently). If you
-/// simply want to visit all items in the crate in some order, you
-/// should call `Crate::visit_all_items`. Otherwise, see the comment
-/// on `visit_nested_item` for details on how to visit nested items.
-///
-/// If you want to ensure that your code handles every variant
-/// explicitly, you need to override each method. (And you also need
-/// to monitor future changes to `Visitor` in case a new method with a
-/// new default implementation gets introduced.)
-pub trait Visitor<'v>: Sized {
- ///////////////////////////////////////////////////////////////////////////
- // Nested items.
-
- /// The default versions of the `visit_nested_XXX` routines invoke
- /// this method to get a map to use. By selecting an enum variant,
- /// you control which kinds of nested HIR are visited; see
- /// `NestedVisitorMap` for details. By "nested HIR", we are
- /// referring to bits of HIR that are not directly embedded within
- /// one another but rather indirectly, through a table in the
- /// crate. This is done to control dependencies during incremental
- /// compilation: the non-inline bits of HIR can be tracked and
- /// hashed separately.
- ///
- /// **If for some reason you want the nested behavior, but don't
- /// have a `Map` at your disposal:** then you should override the
- /// `visit_nested_XXX` methods, and override this method to
- /// `panic!()`. This way, if a new `visit_nested_XXX` variant is
- /// added in the future, we will see the panic in your code and
- /// fix it appropriately.
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'v>;
-
- /// Invoked when a nested item is encountered. By default does
- /// nothing unless you override `nested_visit_map` to return other than
- /// `None`, in which case it will walk the item. **You probably
- /// don't want to override this method** -- instead, override
- /// `nested_visit_map` or use the "shallow" or "deep" visit
- /// patterns described on `itemlikevisit::ItemLikeVisitor`. The only
- /// reason to override this method is if you want a nested pattern
- /// but cannot supply a `Map`; see `nested_visit_map` for advice.
- #[allow(unused_variables)]
- fn visit_nested_item(&mut self, id: ItemId) {
- let opt_item = self.nested_visit_map().inter().map(|map| map.expect_item(id.id));
- if let Some(item) = opt_item {
- self.visit_item(item);
- }
- }
-
- /// Like `visit_nested_item()`, but for trait items. See
- /// `visit_nested_item()` for advice on when to override this
- /// method.
- #[allow(unused_variables)]
- fn visit_nested_trait_item(&mut self, id: TraitItemId) {
- let opt_item = self.nested_visit_map().inter().map(|map| map.trait_item(id));
- if let Some(item) = opt_item {
- self.visit_trait_item(item);
- }
- }
-
- /// Like `visit_nested_item()`, but for impl items. See
- /// `visit_nested_item()` for advice on when to override this
- /// method.
- #[allow(unused_variables)]
- fn visit_nested_impl_item(&mut self, id: ImplItemId) {
- let opt_item = self.nested_visit_map().inter().map(|map| map.impl_item(id));
- if let Some(item) = opt_item {
- self.visit_impl_item(item);
- }
- }
-
- /// Invoked to visit the body of a function, method or closure. Like
- /// visit_nested_item, does nothing by default unless you override
- /// `nested_visit_map` to return other than `None`, in which case it will walk
- /// the body.
- fn visit_nested_body(&mut self, id: BodyId) {
- let opt_body = self.nested_visit_map().intra().map(|map| map.body(id));
- if let Some(body) = opt_body {
- self.visit_body(body);
- }
- }
-
- fn visit_param(&mut self, param: &'v Param<'v>) {
- walk_param(self, param)
- }
-
- /// Visits the top-level item and (optionally) nested items / impl items. See
- /// `visit_nested_item` for details.
- fn visit_item(&mut self, i: &'v Item<'v>) {
- walk_item(self, i)
- }
-
- fn visit_body(&mut self, b: &'v Body<'v>) {
- walk_body(self, b);
- }
-
- /// When invoking `visit_all_item_likes()`, you need to supply an
- /// item-like visitor. This method converts a "intra-visit"
- /// visitor into an item-like visitor that walks the entire tree.
- /// If you use this, you probably don't want to process the
- /// contents of nested item-like things, since the outer loop will
- /// visit them as well.
- fn as_deep_visitor<'s>(&'s mut self) -> DeepVisitor<'s, Self> {
- DeepVisitor::new(self)
- }
-
- ///////////////////////////////////////////////////////////////////////////
-
- fn visit_id(&mut self, _hir_id: HirId) {
- // Nothing to do.
- }
- fn visit_name(&mut self, _span: Span, _name: Name) {
- // Nothing to do.
- }
- fn visit_ident(&mut self, ident: Ident) {
- walk_ident(self, ident)
- }
- fn visit_mod(&mut self, m: &'v Mod<'v>, _s: Span, n: HirId) {
- walk_mod(self, m, n)
- }
- fn visit_foreign_item(&mut self, i: &'v ForeignItem<'v>) {
- walk_foreign_item(self, i)
- }
- fn visit_local(&mut self, l: &'v Local<'v>) {
- walk_local(self, l)
- }
- fn visit_block(&mut self, b: &'v Block<'v>) {
- walk_block(self, b)
- }
- fn visit_stmt(&mut self, s: &'v Stmt<'v>) {
- walk_stmt(self, s)
- }
- fn visit_arm(&mut self, a: &'v Arm<'v>) {
- walk_arm(self, a)
- }
- fn visit_pat(&mut self, p: &'v Pat<'v>) {
- walk_pat(self, p)
- }
- fn visit_anon_const(&mut self, c: &'v AnonConst) {
- walk_anon_const(self, c)
- }
- fn visit_expr(&mut self, ex: &'v Expr<'v>) {
- walk_expr(self, ex)
- }
- fn visit_ty(&mut self, t: &'v Ty<'v>) {
- walk_ty(self, t)
- }
- fn visit_generic_param(&mut self, p: &'v GenericParam<'v>) {
- walk_generic_param(self, p)
- }
- fn visit_generics(&mut self, g: &'v Generics<'v>) {
- walk_generics(self, g)
- }
- fn visit_where_predicate(&mut self, predicate: &'v WherePredicate<'v>) {
- walk_where_predicate(self, predicate)
- }
- fn visit_fn_decl(&mut self, fd: &'v FnDecl<'v>) {
- walk_fn_decl(self, fd)
- }
- fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl<'v>, b: BodyId, s: Span, id: HirId) {
- walk_fn(self, fk, fd, b, s, id)
- }
- fn visit_use(&mut self, path: &'v Path<'v>, hir_id: HirId) {
- walk_use(self, path, hir_id)
- }
- fn visit_trait_item(&mut self, ti: &'v TraitItem<'v>) {
- walk_trait_item(self, ti)
- }
- fn visit_trait_item_ref(&mut self, ii: &'v TraitItemRef) {
- walk_trait_item_ref(self, ii)
- }
- fn visit_impl_item(&mut self, ii: &'v ImplItem<'v>) {
- walk_impl_item(self, ii)
- }
- fn visit_impl_item_ref(&mut self, ii: &'v ImplItemRef<'v>) {
- walk_impl_item_ref(self, ii)
- }
- fn visit_trait_ref(&mut self, t: &'v TraitRef<'v>) {
- walk_trait_ref(self, t)
- }
- fn visit_param_bound(&mut self, bounds: &'v GenericBound<'v>) {
- walk_param_bound(self, bounds)
- }
- fn visit_poly_trait_ref(&mut self, t: &'v PolyTraitRef<'v>, m: TraitBoundModifier) {
- walk_poly_trait_ref(self, t, m)
- }
- fn visit_variant_data(
- &mut self,
- s: &'v VariantData<'v>,
- _: Name,
- _: &'v Generics<'v>,
- _parent_id: HirId,
- _: Span,
- ) {
- walk_struct_def(self, s)
- }
- fn visit_struct_field(&mut self, s: &'v StructField<'v>) {
- walk_struct_field(self, s)
- }
- fn visit_enum_def(
- &mut self,
- enum_definition: &'v EnumDef<'v>,
- generics: &'v Generics<'v>,
- item_id: HirId,
- _: Span,
- ) {
- walk_enum_def(self, enum_definition, generics, item_id)
- }
- fn visit_variant(&mut self, v: &'v Variant<'v>, g: &'v Generics<'v>, item_id: HirId) {
- walk_variant(self, v, g, item_id)
- }
- fn visit_label(&mut self, label: &'v Label) {
- walk_label(self, label)
- }
- fn visit_generic_arg(&mut self, generic_arg: &'v GenericArg<'v>) {
- 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),
- }
- }
- fn visit_lifetime(&mut self, lifetime: &'v Lifetime) {
- walk_lifetime(self, lifetime)
- }
- fn visit_qpath(&mut self, qpath: &'v QPath<'v>, id: HirId, span: Span) {
- walk_qpath(self, qpath, id, span)
- }
- fn visit_path(&mut self, path: &'v Path<'v>, _id: HirId) {
- walk_path(self, path)
- }
- fn visit_path_segment(&mut self, path_span: Span, path_segment: &'v PathSegment<'v>) {
- walk_path_segment(self, path_span, path_segment)
- }
- fn visit_generic_args(&mut self, path_span: Span, generic_args: &'v GenericArgs<'v>) {
- walk_generic_args(self, path_span, generic_args)
- }
- fn visit_assoc_type_binding(&mut self, type_binding: &'v TypeBinding<'v>) {
- walk_assoc_type_binding(self, type_binding)
- }
- fn visit_attribute(&mut self, _attr: &'v Attribute) {}
- fn visit_macro_def(&mut self, macro_def: &'v MacroDef<'v>) {
- walk_macro_def(self, macro_def)
- }
- fn visit_vis(&mut self, vis: &'v Visibility<'v>) {
- walk_vis(self, vis)
- }
- fn visit_associated_item_kind(&mut self, kind: &'v AssocItemKind) {
- walk_associated_item_kind(self, kind);
- }
- fn visit_defaultness(&mut self, defaultness: &'v Defaultness) {
- walk_defaultness(self, defaultness);
- }
-}
-
-/// Walks the contents of a crate. See also `Crate::visit_all_items`.
-pub fn walk_crate<'v, V: Visitor<'v>>(visitor: &mut V, krate: &'v Crate<'v>) {
- visitor.visit_mod(&krate.module, krate.span, CRATE_HIR_ID);
- walk_list!(visitor, visit_attribute, krate.attrs);
- walk_list!(visitor, visit_macro_def, krate.exported_macros);
-}
-
-pub fn walk_macro_def<'v, V: Visitor<'v>>(visitor: &mut V, macro_def: &'v MacroDef<'v>) {
- visitor.visit_id(macro_def.hir_id);
- visitor.visit_name(macro_def.span, macro_def.name);
- walk_list!(visitor, visit_attribute, macro_def.attrs);
-}
-
-pub fn walk_mod<'v, V: Visitor<'v>>(visitor: &mut V, module: &'v Mod<'v>, mod_hir_id: HirId) {
- visitor.visit_id(mod_hir_id);
- for &item_id in module.item_ids {
- visitor.visit_nested_item(item_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);
-}
-
-pub fn walk_local<'v, V: Visitor<'v>>(visitor: &mut V, local: &'v Local<'v>) {
- // Intentionally visiting the expr first - the initialization expr
- // dominates the local's definition.
- walk_list!(visitor, visit_expr, &local.init);
- walk_list!(visitor, visit_attribute, local.attrs.iter());
- visitor.visit_id(local.hir_id);
- visitor.visit_pat(&local.pat);
- walk_list!(visitor, visit_ty, &local.ty);
-}
-
-pub fn walk_ident<'v, V: Visitor<'v>>(visitor: &mut V, ident: Ident) {
- visitor.visit_name(ident.span, ident.name);
-}
-
-pub fn walk_label<'v, V: Visitor<'v>>(visitor: &mut V, label: &'v Label) {
- visitor.visit_ident(label.ident);
-}
-
-pub fn walk_lifetime<'v, V: Visitor<'v>>(visitor: &mut V, lifetime: &'v Lifetime) {
- visitor.visit_id(lifetime.hir_id);
- match lifetime.name {
- LifetimeName::Param(ParamName::Plain(ident)) => {
- visitor.visit_ident(ident);
- }
- LifetimeName::Param(ParamName::Fresh(_))
- | LifetimeName::Param(ParamName::Error)
- | LifetimeName::Static
- | LifetimeName::Error
- | LifetimeName::Implicit
- | LifetimeName::ImplicitObjectLifetimeDefault
- | LifetimeName::Underscore => {}
- }
-}
-
-pub fn walk_poly_trait_ref<'v, V>(
- visitor: &mut V,
- trait_ref: &'v PolyTraitRef<'v>,
- _modifier: TraitBoundModifier,
-) where
- V: Visitor<'v>,
-{
- walk_list!(visitor, visit_generic_param, trait_ref.bound_generic_params);
- visitor.visit_trait_ref(&trait_ref.trait_ref);
-}
-
-pub fn walk_trait_ref<'v, V>(visitor: &mut V, trait_ref: &'v TraitRef<'v>)
-where
- V: Visitor<'v>,
-{
- visitor.visit_id(trait_ref.hir_ref_id);
- visitor.visit_path(&trait_ref.path, trait_ref.hir_ref_id)
-}
-
-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);
- walk_list!(visitor, visit_attribute, param.attrs);
-}
-
-pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) {
- visitor.visit_vis(&item.vis);
- visitor.visit_ident(item.ident);
- match item.kind {
- ItemKind::ExternCrate(orig_name) => {
- visitor.visit_id(item.hir_id);
- if let Some(orig_name) = orig_name {
- visitor.visit_name(item.span, orig_name);
- }
- }
- ItemKind::Use(ref path, _) => {
- visitor.visit_use(path, item.hir_id);
- }
- ItemKind::Static(ref typ, _, body) | ItemKind::Const(ref typ, body) => {
- visitor.visit_id(item.hir_id);
- visitor.visit_ty(typ);
- visitor.visit_nested_body(body);
- }
- ItemKind::Fn(ref sig, ref generics, body_id) => visitor.visit_fn(
- FnKind::ItemFn(item.ident, generics, sig.header, &item.vis, &item.attrs),
- &sig.decl,
- body_id,
- item.span,
- item.hir_id,
- ),
- ItemKind::Mod(ref module) => {
- // `visit_mod()` takes care of visiting the `Item`'s `HirId`.
- visitor.visit_mod(module, item.span, item.hir_id)
- }
- ItemKind::ForeignMod(ref foreign_module) => {
- visitor.visit_id(item.hir_id);
- walk_list!(visitor, visit_foreign_item, foreign_module.items);
- }
- ItemKind::GlobalAsm(_) => {
- visitor.visit_id(item.hir_id);
- }
- ItemKind::TyAlias(ref ty, ref generics) => {
- visitor.visit_id(item.hir_id);
- visitor.visit_ty(ty);
- visitor.visit_generics(generics)
- }
- ItemKind::OpaqueTy(OpaqueTy { ref generics, bounds, .. }) => {
- visitor.visit_id(item.hir_id);
- walk_generics(visitor, generics);
- walk_list!(visitor, visit_param_bound, bounds);
- }
- ItemKind::Enum(ref enum_definition, ref generics) => {
- visitor.visit_generics(generics);
- // `visit_enum_def()` takes care of visiting the `Item`'s `HirId`.
- visitor.visit_enum_def(enum_definition, generics, item.hir_id, item.span)
- }
- ItemKind::Impl(.., ref generics, ref opt_trait_reference, ref typ, impl_item_refs) => {
- visitor.visit_id(item.hir_id);
- visitor.visit_generics(generics);
- walk_list!(visitor, visit_trait_ref, opt_trait_reference);
- visitor.visit_ty(typ);
- walk_list!(visitor, visit_impl_item_ref, impl_item_refs);
- }
- ItemKind::Struct(ref struct_definition, ref generics)
- | ItemKind::Union(ref struct_definition, ref generics) => {
- visitor.visit_generics(generics);
- visitor.visit_id(item.hir_id);
- visitor.visit_variant_data(
- struct_definition,
- item.ident.name,
- generics,
- item.hir_id,
- item.span,
- );
- }
- ItemKind::Trait(.., ref generics, bounds, trait_item_refs) => {
- visitor.visit_id(item.hir_id);
- visitor.visit_generics(generics);
- walk_list!(visitor, visit_param_bound, bounds);
- walk_list!(visitor, visit_trait_item_ref, trait_item_refs);
- }
- ItemKind::TraitAlias(ref generics, bounds) => {
- visitor.visit_id(item.hir_id);
- visitor.visit_generics(generics);
- walk_list!(visitor, visit_param_bound, bounds);
- }
- }
- walk_list!(visitor, visit_attribute, item.attrs);
-}
-
-pub fn walk_use<'v, V: Visitor<'v>>(visitor: &mut V, path: &'v Path<'v>, hir_id: HirId) {
- visitor.visit_id(hir_id);
- visitor.visit_path(path, hir_id);
-}
-
-pub fn walk_enum_def<'v, V: Visitor<'v>>(
- visitor: &mut V,
- enum_definition: &'v EnumDef<'v>,
- generics: &'v Generics<'v>,
- item_id: HirId,
-) {
- visitor.visit_id(item_id);
- walk_list!(visitor, visit_variant, enum_definition.variants, generics, item_id);
-}
-
-pub fn walk_variant<'v, V: Visitor<'v>>(
- visitor: &mut V,
- variant: &'v Variant<'v>,
- generics: &'v Generics<'v>,
- parent_item_id: HirId,
-) {
- visitor.visit_ident(variant.ident);
- visitor.visit_id(variant.id);
- visitor.visit_variant_data(
- &variant.data,
- variant.ident.name,
- generics,
- parent_item_id,
- variant.span,
- );
- walk_list!(visitor, visit_anon_const, &variant.disr_expr);
- walk_list!(visitor, visit_attribute, variant.attrs);
-}
-
-pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) {
- visitor.visit_id(typ.hir_id);
-
- match typ.kind {
- TyKind::Slice(ref ty) => visitor.visit_ty(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)
- }
- TyKind::Never => {}
- TyKind::Tup(tuple_element_types) => {
- walk_list!(visitor, visit_ty, 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);
- }
- TyKind::Path(ref qpath) => {
- visitor.visit_qpath(qpath, typ.hir_id, typ.span);
- }
- TyKind::Def(item_id, lifetimes) => {
- visitor.visit_nested_item(item_id);
- walk_list!(visitor, visit_generic_arg, lifetimes);
- }
- TyKind::Array(ref ty, ref length) => {
- visitor.visit_ty(ty);
- visitor.visit_anon_const(length)
- }
- TyKind::TraitObject(bounds, ref lifetime) => {
- for bound in bounds {
- visitor.visit_poly_trait_ref(bound, TraitBoundModifier::None);
- }
- visitor.visit_lifetime(lifetime);
- }
- TyKind::Typeof(ref expression) => visitor.visit_anon_const(expression),
- TyKind::Infer | TyKind::Err => {}
- }
-}
-
-pub fn walk_qpath<'v, V: Visitor<'v>>(
- visitor: &mut V,
- qpath: &'v QPath<'v>,
- id: HirId,
- span: Span,
-) {
- match *qpath {
- QPath::Resolved(ref maybe_qself, ref path) => {
- if let Some(ref qself) = *maybe_qself {
- visitor.visit_ty(qself);
- }
- visitor.visit_path(path, id)
- }
- QPath::TypeRelative(ref qself, ref segment) => {
- visitor.visit_ty(qself);
- visitor.visit_path_segment(span, segment);
- }
- }
-}
-
-pub fn walk_path<'v, V: Visitor<'v>>(visitor: &mut V, path: &'v Path<'v>) {
- for segment in path.segments {
- visitor.visit_path_segment(path.span, segment);
- }
-}
-
-pub fn walk_path_segment<'v, V: Visitor<'v>>(
- visitor: &mut V,
- path_span: Span,
- segment: &'v PathSegment<'v>,
-) {
- visitor.visit_ident(segment.ident);
- if let Some(id) = segment.hir_id {
- visitor.visit_id(id);
- }
- if let Some(ref args) = segment.args {
- visitor.visit_generic_args(path_span, args);
- }
-}
-
-pub fn walk_generic_args<'v, V: Visitor<'v>>(
- visitor: &mut V,
- _path_span: Span,
- generic_args: &'v GenericArgs<'v>,
-) {
- walk_list!(visitor, visit_generic_arg, generic_args.args);
- walk_list!(visitor, visit_assoc_type_binding, generic_args.bindings);
-}
-
-pub fn walk_assoc_type_binding<'v, V: Visitor<'v>>(
- visitor: &mut V,
- type_binding: &'v TypeBinding<'v>,
-) {
- visitor.visit_id(type_binding.hir_id);
- visitor.visit_ident(type_binding.ident);
- match type_binding.kind {
- TypeBindingKind::Equality { ref ty } => {
- visitor.visit_ty(ty);
- }
- TypeBindingKind::Constraint { bounds } => {
- walk_list!(visitor, visit_param_bound, bounds);
- }
- }
-}
-
-pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat<'v>) {
- visitor.visit_id(pattern.hir_id);
- match pattern.kind {
- PatKind::TupleStruct(ref qpath, children, _) => {
- visitor.visit_qpath(qpath, pattern.hir_id, pattern.span);
- walk_list!(visitor, visit_pat, children);
- }
- PatKind::Path(ref qpath) => {
- visitor.visit_qpath(qpath, pattern.hir_id, pattern.span);
- }
- PatKind::Struct(ref qpath, fields, _) => {
- visitor.visit_qpath(qpath, pattern.hir_id, pattern.span);
- for field in fields {
- visitor.visit_id(field.hir_id);
- visitor.visit_ident(field.ident);
- visitor.visit_pat(&field.pat)
- }
- }
- PatKind::Or(pats) => walk_list!(visitor, visit_pat, pats),
- PatKind::Tuple(tuple_elements, _) => {
- walk_list!(visitor, visit_pat, tuple_elements);
- }
- PatKind::Box(ref subpattern) | PatKind::Ref(ref subpattern, _) => {
- visitor.visit_pat(subpattern)
- }
- PatKind::Binding(_, _hir_id, ident, ref optional_subpattern) => {
- visitor.visit_ident(ident);
- walk_list!(visitor, visit_pat, optional_subpattern);
- }
- PatKind::Lit(ref expression) => visitor.visit_expr(expression),
- PatKind::Range(ref lower_bound, ref upper_bound, _) => {
- visitor.visit_expr(lower_bound);
- visitor.visit_expr(upper_bound)
- }
- PatKind::Wild => (),
- PatKind::Slice(prepatterns, ref slice_pattern, postpatterns) => {
- walk_list!(visitor, visit_pat, prepatterns);
- walk_list!(visitor, visit_pat, slice_pattern);
- walk_list!(visitor, visit_pat, postpatterns);
- }
- }
-}
-
-pub fn walk_foreign_item<'v, V: Visitor<'v>>(visitor: &mut V, foreign_item: &'v ForeignItem<'v>) {
- visitor.visit_id(foreign_item.hir_id);
- visitor.visit_vis(&foreign_item.vis);
- visitor.visit_ident(foreign_item.ident);
-
- match foreign_item.kind {
- ForeignItemKind::Fn(ref function_declaration, param_names, ref generics) => {
- visitor.visit_generics(generics);
- visitor.visit_fn_decl(function_declaration);
- for ¶m_name in param_names {
- visitor.visit_ident(param_name);
- }
- }
- ForeignItemKind::Static(ref typ, _) => visitor.visit_ty(typ),
- ForeignItemKind::Type => (),
- }
-
- walk_list!(visitor, visit_attribute, foreign_item.attrs);
-}
-
-pub fn walk_param_bound<'v, V: Visitor<'v>>(visitor: &mut V, bound: &'v GenericBound<'v>) {
- match *bound {
- GenericBound::Trait(ref typ, modifier) => {
- visitor.visit_poly_trait_ref(typ, modifier);
- }
- GenericBound::Outlives(ref lifetime) => visitor.visit_lifetime(lifetime),
- }
-}
-
-pub fn walk_generic_param<'v, V: Visitor<'v>>(visitor: &mut V, param: &'v GenericParam<'v>) {
- visitor.visit_id(param.hir_id);
- walk_list!(visitor, visit_attribute, param.attrs);
- match param.name {
- ParamName::Plain(ident) => visitor.visit_ident(ident),
- ParamName::Error | ParamName::Fresh(_) => {}
- }
- match param.kind {
- GenericParamKind::Lifetime { .. } => {}
- GenericParamKind::Type { ref default, .. } => walk_list!(visitor, visit_ty, default),
- GenericParamKind::Const { ref ty } => visitor.visit_ty(ty),
- }
- walk_list!(visitor, visit_param_bound, param.bounds);
-}
-
-pub fn walk_generics<'v, V: Visitor<'v>>(visitor: &mut V, generics: &'v Generics<'v>) {
- walk_list!(visitor, visit_generic_param, generics.params);
- walk_list!(visitor, visit_where_predicate, generics.where_clause.predicates);
-}
-
-pub fn walk_where_predicate<'v, V: Visitor<'v>>(
- visitor: &mut V,
- predicate: &'v WherePredicate<'v>,
-) {
- match predicate {
- &WherePredicate::BoundPredicate(WhereBoundPredicate {
- ref bounded_ty,
- bounds,
- bound_generic_params,
- ..
- }) => {
- visitor.visit_ty(bounded_ty);
- walk_list!(visitor, visit_param_bound, bounds);
- walk_list!(visitor, visit_generic_param, bound_generic_params);
- }
- &WherePredicate::RegionPredicate(WhereRegionPredicate { ref lifetime, bounds, .. }) => {
- visitor.visit_lifetime(lifetime);
- walk_list!(visitor, visit_param_bound, bounds);
- }
- &WherePredicate::EqPredicate(WhereEqPredicate {
- hir_id, ref lhs_ty, ref rhs_ty, ..
- }) => {
- visitor.visit_id(hir_id);
- visitor.visit_ty(lhs_ty);
- visitor.visit_ty(rhs_ty);
- }
- }
-}
-
-pub fn walk_fn_ret_ty<'v, V: Visitor<'v>>(visitor: &mut V, ret_ty: &'v FunctionRetTy<'v>) {
- if let FunctionRetTy::Return(ref output_ty) = *ret_ty {
- visitor.visit_ty(output_ty)
- }
-}
-
-pub fn walk_fn_decl<'v, V: Visitor<'v>>(visitor: &mut V, function_declaration: &'v FnDecl<'v>) {
- for ty in function_declaration.inputs {
- visitor.visit_ty(ty)
- }
- walk_fn_ret_ty(visitor, &function_declaration.output)
-}
-
-pub fn walk_fn_kind<'v, V: Visitor<'v>>(visitor: &mut V, function_kind: FnKind<'v>) {
- match function_kind {
- FnKind::ItemFn(_, generics, ..) => {
- visitor.visit_generics(generics);
- }
- FnKind::Method(..) | FnKind::Closure(_) => {}
- }
-}
-
-pub fn walk_fn<'v, V: Visitor<'v>>(
- visitor: &mut V,
- function_kind: FnKind<'v>,
- function_declaration: &'v FnDecl<'v>,
- body_id: BodyId,
- _span: Span,
- id: HirId,
-) {
- visitor.visit_id(id);
- visitor.visit_fn_decl(function_declaration);
- walk_fn_kind(visitor, function_kind);
- visitor.visit_nested_body(body_id)
-}
-
-pub fn walk_trait_item<'v, V: Visitor<'v>>(visitor: &mut V, trait_item: &'v TraitItem<'v>) {
- visitor.visit_ident(trait_item.ident);
- walk_list!(visitor, visit_attribute, trait_item.attrs);
- visitor.visit_generics(&trait_item.generics);
- match trait_item.kind {
- TraitItemKind::Const(ref ty, default) => {
- visitor.visit_id(trait_item.hir_id);
- visitor.visit_ty(ty);
- walk_list!(visitor, visit_nested_body, default);
- }
- TraitItemKind::Method(ref sig, TraitMethod::Required(param_names)) => {
- visitor.visit_id(trait_item.hir_id);
- visitor.visit_fn_decl(&sig.decl);
- for ¶m_name in param_names {
- visitor.visit_ident(param_name);
- }
- }
- TraitItemKind::Method(ref sig, TraitMethod::Provided(body_id)) => {
- visitor.visit_fn(
- FnKind::Method(trait_item.ident, sig, None, &trait_item.attrs),
- &sig.decl,
- body_id,
- trait_item.span,
- trait_item.hir_id,
- );
- }
- TraitItemKind::Type(bounds, ref default) => {
- visitor.visit_id(trait_item.hir_id);
- walk_list!(visitor, visit_param_bound, bounds);
- walk_list!(visitor, visit_ty, default);
- }
- }
-}
-
-pub fn walk_trait_item_ref<'v, V: Visitor<'v>>(visitor: &mut V, trait_item_ref: &'v TraitItemRef) {
- // N.B., deliberately force a compilation error if/when new fields are added.
- let TraitItemRef { id, ident, ref kind, span: _, ref defaultness } = *trait_item_ref;
- visitor.visit_nested_trait_item(id);
- visitor.visit_ident(ident);
- visitor.visit_associated_item_kind(kind);
- visitor.visit_defaultness(defaultness);
-}
-
-pub fn walk_impl_item<'v, V: Visitor<'v>>(visitor: &mut V, impl_item: &'v ImplItem<'v>) {
- // N.B., deliberately force a compilation error if/when new fields are added.
- let ImplItem {
- hir_id: _,
- ident,
- ref vis,
- ref defaultness,
- attrs,
- ref generics,
- ref kind,
- span: _,
- } = *impl_item;
-
- visitor.visit_ident(ident);
- visitor.visit_vis(vis);
- visitor.visit_defaultness(defaultness);
- walk_list!(visitor, visit_attribute, attrs);
- visitor.visit_generics(generics);
- match *kind {
- ImplItemKind::Const(ref ty, body) => {
- visitor.visit_id(impl_item.hir_id);
- visitor.visit_ty(ty);
- visitor.visit_nested_body(body);
- }
- ImplItemKind::Method(ref sig, body_id) => {
- visitor.visit_fn(
- FnKind::Method(impl_item.ident, sig, Some(&impl_item.vis), &impl_item.attrs),
- &sig.decl,
- body_id,
- impl_item.span,
- impl_item.hir_id,
- );
- }
- ImplItemKind::TyAlias(ref ty) => {
- visitor.visit_id(impl_item.hir_id);
- visitor.visit_ty(ty);
- }
- ImplItemKind::OpaqueTy(bounds) => {
- visitor.visit_id(impl_item.hir_id);
- walk_list!(visitor, visit_param_bound, bounds);
- }
- }
-}
-
-pub fn walk_impl_item_ref<'v, V: Visitor<'v>>(visitor: &mut V, impl_item_ref: &'v ImplItemRef<'v>) {
- // N.B., deliberately force a compilation error if/when new fields are added.
- let ImplItemRef { id, ident, ref kind, span: _, ref vis, ref defaultness } = *impl_item_ref;
- visitor.visit_nested_impl_item(id);
- visitor.visit_ident(ident);
- visitor.visit_associated_item_kind(kind);
- visitor.visit_vis(vis);
- visitor.visit_defaultness(defaultness);
-}
-
-pub fn walk_struct_def<'v, V: Visitor<'v>>(
- visitor: &mut V,
- struct_definition: &'v VariantData<'v>,
-) {
- if let Some(ctor_hir_id) = struct_definition.ctor_hir_id() {
- visitor.visit_id(ctor_hir_id);
- }
- walk_list!(visitor, visit_struct_field, struct_definition.fields());
-}
-
-pub fn walk_struct_field<'v, V: Visitor<'v>>(visitor: &mut V, struct_field: &'v StructField<'v>) {
- visitor.visit_id(struct_field.hir_id);
- visitor.visit_vis(&struct_field.vis);
- visitor.visit_ident(struct_field.ident);
- visitor.visit_ty(&struct_field.ty);
- walk_list!(visitor, visit_attribute, struct_field.attrs);
-}
-
-pub fn walk_block<'v, V: Visitor<'v>>(visitor: &mut V, block: &'v Block<'v>) {
- visitor.visit_id(block.hir_id);
- walk_list!(visitor, visit_stmt, block.stmts);
- walk_list!(visitor, visit_expr, &block.expr);
-}
-
-pub fn walk_stmt<'v, V: Visitor<'v>>(visitor: &mut V, statement: &'v Stmt<'v>) {
- visitor.visit_id(statement.hir_id);
- match statement.kind {
- StmtKind::Local(ref local) => visitor.visit_local(local),
- StmtKind::Item(item) => visitor.visit_nested_item(item),
- StmtKind::Expr(ref expression) | StmtKind::Semi(ref expression) => {
- visitor.visit_expr(expression)
- }
- }
-}
-
-pub fn walk_anon_const<'v, V: Visitor<'v>>(visitor: &mut V, constant: &'v AnonConst) {
- visitor.visit_id(constant.hir_id);
- visitor.visit_nested_body(constant.body);
-}
-
-pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) {
- visitor.visit_id(expression.hir_id);
- walk_list!(visitor, visit_attribute, expression.attrs.iter());
- match expression.kind {
- ExprKind::Box(ref subexpression) => visitor.visit_expr(subexpression),
- ExprKind::Array(subexpressions) => {
- walk_list!(visitor, visit_expr, subexpressions);
- }
- ExprKind::Repeat(ref element, ref count) => {
- visitor.visit_expr(element);
- visitor.visit_anon_const(count)
- }
- ExprKind::Struct(ref qpath, fields, ref optional_base) => {
- visitor.visit_qpath(qpath, expression.hir_id, expression.span);
- for field in fields {
- visitor.visit_id(field.hir_id);
- visitor.visit_ident(field.ident);
- visitor.visit_expr(&field.expr)
- }
- walk_list!(visitor, visit_expr, optional_base);
- }
- ExprKind::Tup(subexpressions) => {
- walk_list!(visitor, visit_expr, subexpressions);
- }
- ExprKind::Call(ref callee_expression, arguments) => {
- visitor.visit_expr(callee_expression);
- walk_list!(visitor, visit_expr, arguments);
- }
- ExprKind::MethodCall(ref segment, _, arguments) => {
- visitor.visit_path_segment(expression.span, segment);
- walk_list!(visitor, visit_expr, arguments);
- }
- ExprKind::Binary(_, ref left_expression, ref right_expression) => {
- visitor.visit_expr(left_expression);
- visitor.visit_expr(right_expression)
- }
- ExprKind::AddrOf(_, _, ref subexpression) | ExprKind::Unary(_, ref subexpression) => {
- visitor.visit_expr(subexpression)
- }
- ExprKind::Cast(ref subexpression, ref typ) | ExprKind::Type(ref subexpression, ref typ) => {
- visitor.visit_expr(subexpression);
- visitor.visit_ty(typ)
- }
- ExprKind::DropTemps(ref subexpression) => {
- visitor.visit_expr(subexpression);
- }
- ExprKind::Loop(ref block, ref opt_label, _) => {
- walk_list!(visitor, visit_label, opt_label);
- visitor.visit_block(block);
- }
- ExprKind::Match(ref subexpression, arms, _) => {
- visitor.visit_expr(subexpression);
- walk_list!(visitor, visit_arm, arms);
- }
- ExprKind::Closure(_, ref function_declaration, body, _fn_decl_span, _gen) => visitor
- .visit_fn(
- FnKind::Closure(&expression.attrs),
- function_declaration,
- body,
- expression.span,
- expression.hir_id,
- ),
- ExprKind::Block(ref block, ref opt_label) => {
- walk_list!(visitor, visit_label, opt_label);
- visitor.visit_block(block);
- }
- ExprKind::Assign(ref lhs, ref rhs, _) => {
- visitor.visit_expr(rhs);
- visitor.visit_expr(lhs)
- }
- ExprKind::AssignOp(_, ref left_expression, ref right_expression) => {
- visitor.visit_expr(right_expression);
- visitor.visit_expr(left_expression);
- }
- ExprKind::Field(ref subexpression, ident) => {
- visitor.visit_expr(subexpression);
- visitor.visit_ident(ident);
- }
- ExprKind::Index(ref main_expression, ref index_expression) => {
- visitor.visit_expr(main_expression);
- visitor.visit_expr(index_expression)
- }
- ExprKind::Path(ref qpath) => {
- visitor.visit_qpath(qpath, expression.hir_id, expression.span);
- }
- ExprKind::Break(ref destination, ref opt_expr) => {
- if let Some(ref label) = destination.label {
- visitor.visit_label(label);
- }
- walk_list!(visitor, visit_expr, opt_expr);
- }
- ExprKind::Continue(ref destination) => {
- if let Some(ref label) = destination.label {
- visitor.visit_label(label);
- }
- }
- ExprKind::Ret(ref optional_expression) => {
- walk_list!(visitor, visit_expr, optional_expression);
- }
- ExprKind::InlineAsm(ref asm) => {
- walk_list!(visitor, visit_expr, asm.outputs_exprs);
- walk_list!(visitor, visit_expr, asm.inputs_exprs);
- }
- ExprKind::Yield(ref subexpression, _) => {
- visitor.visit_expr(subexpression);
- }
- ExprKind::Lit(_) | ExprKind::Err => {}
- }
-}
-
-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);
- if let Some(ref g) = arm.guard {
- match g {
- Guard::If(ref e) => visitor.visit_expr(e),
- }
- }
- visitor.visit_expr(&arm.body);
- walk_list!(visitor, visit_attribute, arm.attrs);
-}
-
-pub fn walk_vis<'v, V: Visitor<'v>>(visitor: &mut V, vis: &'v Visibility<'v>) {
- if let VisibilityKind::Restricted { ref path, hir_id } = vis.node {
- visitor.visit_id(hir_id);
- visitor.visit_path(path, hir_id)
- }
-}
-
-pub fn walk_associated_item_kind<'v, V: Visitor<'v>>(_: &mut V, _: &'v AssocItemKind) {
- // No visitable content here: this fn exists so you can call it if
- // the right thing to do, should content be added in the future,
- // would be to walk it.
-}
-
-pub fn walk_defaultness<'v, V: Visitor<'v>>(_: &mut V, _: &'v Defaultness) {
- // No visitable content here: this fn exists so you can call it if
- // the right thing to do, should content be added in the future,
- // would be to walk it.
-}
//! nested within a uniquely determined `FnLike`), and users can ask
//! for the `Code` associated with a particular NodeId.
-use crate::hir::intravisit::FnKind;
-use crate::hir::map;
-use rustc_hir as ast;
+use crate::hir::map::Map;
+use rustc_hir as hir;
+use rustc_hir::intravisit::FnKind;
use rustc_hir::{Expr, FnDecl, Node};
use rustc_span::Span;
use syntax::ast::{Attribute, Ident};
fn is_fn_like(&self) -> bool;
}
-impl MaybeFnLike for ast::Item<'_> {
+impl MaybeFnLike for hir::Item<'_> {
fn is_fn_like(&self) -> bool {
match self.kind {
- ast::ItemKind::Fn(..) => true,
+ hir::ItemKind::Fn(..) => true,
_ => false,
}
}
}
-impl MaybeFnLike for ast::ImplItem<'_> {
+impl MaybeFnLike for hir::ImplItem<'_> {
fn is_fn_like(&self) -> bool {
match self.kind {
- ast::ImplItemKind::Method(..) => true,
+ hir::ImplItemKind::Method(..) => true,
_ => false,
}
}
}
-impl MaybeFnLike for ast::TraitItem<'_> {
+impl MaybeFnLike for hir::TraitItem<'_> {
fn is_fn_like(&self) -> bool {
match self.kind {
- ast::TraitItemKind::Method(_, ast::TraitMethod::Provided(_)) => true,
+ hir::TraitItemKind::Method(_, hir::TraitMethod::Provided(_)) => true,
_ => false,
}
}
}
-impl MaybeFnLike for ast::Expr<'_> {
+impl MaybeFnLike for hir::Expr<'_> {
fn is_fn_like(&self) -> bool {
match self.kind {
- ast::ExprKind::Closure(..) => true,
+ hir::ExprKind::Closure(..) => true,
_ => false,
}
}
}
impl<'a> Code<'a> {
- pub fn id(&self) -> ast::HirId {
+ pub fn id(&self) -> hir::HirId {
match *self {
Code::FnLike(node) => node.id(),
Code::Expr(block) => block.hir_id,
}
/// Attempts to construct a Code from presumed FnLike or Expr node input.
- pub fn from_node(map: &map::Map<'a>, id: ast::HirId) -> Option<Code<'a>> {
+ pub fn from_node(map: &Map<'a>, id: hir::HirId) -> Option<Code<'a>> {
match map.get(id) {
- map::Node::Block(_) => {
+ Node::Block(_) => {
// Use the parent, hopefully an expression node.
Code::from_node(map, map.get_parent_node(id))
}
- map::Node::Expr(expr) => Some(Code::Expr(expr)),
+ Node::Expr(expr) => Some(Code::Expr(expr)),
node => FnLikeNode::from_node(node).map(Code::FnLike),
}
}
/// use when implementing FnLikeNode operations.
struct ItemFnParts<'a> {
ident: Ident,
- decl: &'a ast::FnDecl<'a>,
- header: ast::FnHeader,
- vis: &'a ast::Visibility<'a>,
- generics: &'a ast::Generics<'a>,
- body: ast::BodyId,
- id: ast::HirId,
+ decl: &'a hir::FnDecl<'a>,
+ header: hir::FnHeader,
+ vis: &'a hir::Visibility<'a>,
+ generics: &'a hir::Generics<'a>,
+ body: hir::BodyId,
+ id: hir::HirId,
span: Span,
attrs: &'a [Attribute],
}
/// for use when implementing FnLikeNode operations.
struct ClosureParts<'a> {
decl: &'a FnDecl<'a>,
- body: ast::BodyId,
- id: ast::HirId,
+ body: hir::BodyId,
+ id: hir::HirId,
span: Span,
attrs: &'a [Attribute],
}
impl<'a> ClosureParts<'a> {
fn new(
d: &'a FnDecl<'a>,
- b: ast::BodyId,
- id: ast::HirId,
+ b: hir::BodyId,
+ id: hir::HirId,
s: Span,
attrs: &'a [Attribute],
) -> Self {
/// Attempts to construct a FnLikeNode from presumed FnLike node input.
pub fn from_node(node: Node<'_>) -> Option<FnLikeNode<'_>> {
let fn_like = match node {
- map::Node::Item(item) => item.is_fn_like(),
- map::Node::TraitItem(tm) => tm.is_fn_like(),
- map::Node::ImplItem(it) => it.is_fn_like(),
- map::Node::Expr(e) => e.is_fn_like(),
+ Node::Item(item) => item.is_fn_like(),
+ Node::TraitItem(tm) => tm.is_fn_like(),
+ Node::ImplItem(it) => it.is_fn_like(),
+ Node::Expr(e) => e.is_fn_like(),
_ => false,
};
fn_like.then_some(FnLikeNode { node })
}
- pub fn body(self) -> ast::BodyId {
+ pub fn body(self) -> hir::BodyId {
self.handle(
|i: ItemFnParts<'a>| i.body,
- |_, _, _: &'a ast::FnSig<'a>, _, body: ast::BodyId, _, _| body,
+ |_, _, _: &'a hir::FnSig<'a>, _, body: hir::BodyId, _, _| body,
|c: ClosureParts<'a>| c.body,
)
}
pub fn decl(self) -> &'a FnDecl<'a> {
self.handle(
|i: ItemFnParts<'a>| &*i.decl,
- |_, _, sig: &'a ast::FnSig<'a>, _, _, _, _| &sig.decl,
+ |_, _, sig: &'a hir::FnSig<'a>, _, _, _, _| &sig.decl,
|c: ClosureParts<'a>| c.decl,
)
}
pub fn span(self) -> Span {
self.handle(
|i: ItemFnParts<'_>| i.span,
- |_, _, _: &'a ast::FnSig<'a>, _, _, span, _| span,
+ |_, _, _: &'a hir::FnSig<'a>, _, _, span, _| span,
|c: ClosureParts<'_>| c.span,
)
}
- pub fn id(self) -> ast::HirId {
+ pub fn id(self) -> hir::HirId {
self.handle(
|i: ItemFnParts<'_>| i.id,
- |id, _, _: &'a ast::FnSig<'a>, _, _, _, _| id,
+ |id, _, _: &'a hir::FnSig<'a>, _, _, _, _| id,
|c: ClosureParts<'_>| c.id,
)
}
- pub fn constness(self) -> ast::Constness {
- self.kind().header().map_or(ast::Constness::NotConst, |header| header.constness)
+ pub fn constness(self) -> hir::Constness {
+ self.kind().header().map_or(hir::Constness::NotConst, |header| header.constness)
}
- pub fn asyncness(self) -> ast::IsAsync {
- self.kind().header().map_or(ast::IsAsync::NotAsync, |header| header.asyncness)
+ pub fn asyncness(self) -> hir::IsAsync {
+ self.kind().header().map_or(hir::IsAsync::NotAsync, |header| header.asyncness)
}
- pub fn unsafety(self) -> ast::Unsafety {
- self.kind().header().map_or(ast::Unsafety::Normal, |header| header.unsafety)
+ pub fn unsafety(self) -> hir::Unsafety {
+ self.kind().header().map_or(hir::Unsafety::Normal, |header| header.unsafety)
}
pub fn kind(self) -> FnKind<'a> {
FnKind::ItemFn(p.ident, p.generics, p.header, p.vis, p.attrs)
};
let closure = |c: ClosureParts<'a>| FnKind::Closure(c.attrs);
- let method = |_, ident: Ident, sig: &'a ast::FnSig<'a>, vis, _, _, attrs| {
+ let method = |_, ident: Ident, sig: &'a hir::FnSig<'a>, vis, _, _, attrs| {
FnKind::Method(ident, sig, vis, attrs)
};
self.handle(item, method, closure)
where
I: FnOnce(ItemFnParts<'a>) -> A,
M: FnOnce(
- ast::HirId,
+ hir::HirId,
Ident,
- &'a ast::FnSig<'a>,
- Option<&'a ast::Visibility<'a>>,
- ast::BodyId,
+ &'a hir::FnSig<'a>,
+ Option<&'a hir::Visibility<'a>>,
+ hir::BodyId,
Span,
&'a [Attribute],
) -> A,
C: FnOnce(ClosureParts<'a>) -> A,
{
match self.node {
- map::Node::Item(i) => match i.kind {
- ast::ItemKind::Fn(ref sig, ref generics, block) => item_fn(ItemFnParts {
+ Node::Item(i) => match i.kind {
+ hir::ItemKind::Fn(ref sig, ref generics, block) => item_fn(ItemFnParts {
id: i.hir_id,
ident: i.ident,
decl: &sig.decl,
}),
_ => bug!("item FnLikeNode that is not fn-like"),
},
- map::Node::TraitItem(ti) => match ti.kind {
- ast::TraitItemKind::Method(ref sig, ast::TraitMethod::Provided(body)) => {
+ Node::TraitItem(ti) => match ti.kind {
+ hir::TraitItemKind::Method(ref sig, hir::TraitMethod::Provided(body)) => {
method(ti.hir_id, ti.ident, sig, None, body, ti.span, &ti.attrs)
}
_ => bug!("trait method FnLikeNode that is not fn-like"),
},
- map::Node::ImplItem(ii) => match ii.kind {
- ast::ImplItemKind::Method(ref sig, body) => {
+ Node::ImplItem(ii) => match ii.kind {
+ hir::ImplItemKind::Method(ref sig, body) => {
method(ii.hir_id, ii.ident, sig, Some(&ii.vis), body, ii.span, &ii.attrs)
}
_ => bug!("impl method FnLikeNode that is not fn-like"),
},
- map::Node::Expr(e) => match e.kind {
- ast::ExprKind::Closure(_, ref decl, block, _fn_decl_span, _gen) => {
+ Node::Expr(e) => match e.kind {
+ hir::ExprKind::Closure(_, ref decl, block, _fn_decl_span, _gen) => {
closure(ClosureParts::new(&decl, block, e.hir_id, e.span, &e.attrs))
}
_ => bug!("expr FnLikeNode that is not fn-like"),
-use super::*;
-use crate::dep_graph::{DepGraph, DepKind, DepNodeIndex};
-use crate::hir::intravisit::{NestedVisitorMap, Visitor};
-use crate::hir::map::HirEntryMap;
-use crate::ich::Fingerprint;
+use crate::dep_graph::{DepGraph, DepKind, DepNode, DepNodeIndex};
+use crate::hir::map::definitions::{self, DefPathHash};
+use crate::hir::map::{Entry, HirEntryMap, Map};
+use crate::ich::StableHashingContext;
use crate::middle::cstore::CrateStore;
+use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_data_structures::svh::Svh;
use rustc_hir as hir;
+use rustc_hir::def_id::CRATE_DEF_INDEX;
use rustc_hir::def_id::{CrateNum, DefIndex, LOCAL_CRATE};
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
+use rustc_hir::*;
use rustc_index::vec::IndexVec;
use rustc_session::{CrateDisambiguator, Session};
use rustc_span::source_map::SourceMap;
-use rustc_span::Span;
-use std::iter::repeat;
+use rustc_span::{Span, Symbol, DUMMY_SP};
use syntax::ast::NodeId;
-use crate::ich::StableHashingContext;
-use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
+use std::iter::repeat;
/// A visitor that walks over the HIR and collects `Node`s into a HIR map.
pub(super) struct NodeCollector<'a, 'hir> {
hir_body_nodes: Vec<(DefPathHash, Fingerprint)>,
}
-fn input_dep_node_and_hash<I>(
+fn input_dep_node_and_hash(
dep_graph: &DepGraph,
hcx: &mut StableHashingContext<'_>,
dep_node: DepNode,
- input: I,
-) -> (DepNodeIndex, Fingerprint)
-where
- I: for<'a> HashStable<StableHashingContext<'a>>,
-{
+ input: impl for<'a> HashStable<StableHashingContext<'a>>,
+) -> (DepNodeIndex, Fingerprint) {
let dep_node_index = dep_graph.input_task(dep_node, &mut *hcx, &input).1;
let hash = if dep_graph.is_fully_enabled() {
(dep_node_index, hash)
}
-fn alloc_hir_dep_nodes<I>(
+fn alloc_hir_dep_nodes(
dep_graph: &DepGraph,
hcx: &mut StableHashingContext<'_>,
def_path_hash: DefPathHash,
- item_like: I,
+ item_like: impl for<'a> HashStable<StableHashingContext<'a>>,
hir_body_nodes: &mut Vec<(DefPathHash, Fingerprint)>,
-) -> (DepNodeIndex, DepNodeIndex)
-where
- I: for<'a> HashStable<StableHashingContext<'a>>,
-{
+) -> (DepNodeIndex, DepNodeIndex) {
let sig = dep_graph
.input_task(
def_path_hash.to_dep_node(DepKind::Hir),
(sig, full)
}
+fn upstream_crates(cstore: &dyn CrateStore) -> Vec<(Symbol, Fingerprint, Svh)> {
+ let mut upstream_crates: Vec<_> = cstore
+ .crates_untracked()
+ .iter()
+ .map(|&cnum| {
+ let name = cstore.crate_name_untracked(cnum);
+ let disambiguator = cstore.crate_disambiguator_untracked(cnum).to_fingerprint();
+ let hash = cstore.crate_hash_untracked(cnum);
+ (name, disambiguator, hash)
+ })
+ .collect();
+ upstream_crates.sort_unstable_by_key(|&(name, dis, _)| (name.as_str(), dis));
+ upstream_crates
+}
+
impl<'a, 'hir> NodeCollector<'a, 'hir> {
pub(super) fn root(
sess: &'a Session,
},
);
- let mut upstream_crates: Vec<_> = cstore
- .crates_untracked()
- .iter()
- .map(|&cnum| {
- let name = cstore.crate_name_untracked(cnum);
- let disambiguator = cstore.crate_disambiguator_untracked(cnum).to_fingerprint();
- let hash = cstore.crate_hash_untracked(cnum);
- (name, disambiguator, hash)
- })
- .collect();
-
- upstream_crates.sort_unstable_by_key(|&(name, dis, _)| (name.as_str(), dis));
+ let upstream_crates = upstream_crates(cstore);
// We hash the final, remapped names of all local source files so we
// don't have to include the path prefix remapping commandline args.
}
impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
+ type Map = Map<'hir>;
+
/// Because we want to track parent items and so forth, enable
/// deep walking so that we walk nested items in the context of
/// their outer items.
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'hir> {
+ fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, Self::Map> {
panic!("`visit_nested_xxx` must be manually implemented in this visitor");
}
//! There are also some rather random cases (like const initializer
//! expressions) that are mostly just leftovers.
-use crate::ich::Fingerprint;
+use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stable_hasher::StableHasher;
use rustc_hir as hir;
use rustc_span::hygiene::ExpnId;
use rustc_span::symbol::{sym, Symbol};
use rustc_span::Span;
+use syntax::ast;
+
use std::borrow::Borrow;
use std::fmt::Write;
use std::hash::Hash;
-use syntax::ast;
/// The `DefPathTable` maps `DefIndex`es to `DefKey`s and vice versa.
/// Internally the `DefPathTable` holds a tree of `DefKey`s, where each `DefKey`
-use crate::hir::intravisit;
use crate::hir::map::Map;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::sync::{par_iter, Lock, ParallelIterator};
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, DefIndex, CRATE_DEF_INDEX};
+use rustc_hir::intravisit;
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_hir::{HirId, ItemLocalId};
}
impl<'a, 'hir> intravisit::Visitor<'hir> for HirIdValidator<'a, 'hir> {
- fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'hir> {
+ type Map = Map<'hir>;
+
+ fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, Self::Map> {
intravisit::NestedVisitorMap::OnlyBodies(self.hir_map)
}
};
use crate::dep_graph::{DepGraph, DepKind, DepNode, DepNodeIndex};
-use crate::hir::intravisit;
use crate::middle::cstore::CrateStoreDyn;
use crate::ty::query::Providers;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::svh::Svh;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, DefIndex, LocalDefId, CRATE_DEF_INDEX};
+use rustc_hir::intravisit;
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_hir::print::Nested;
use rustc_hir::*;
use rustc_span::hygiene::MacroKind;
use rustc_span::source_map::Spanned;
use rustc_span::symbol::kw;
-use rustc_span::{Span, DUMMY_SP};
+use rustc_span::Span;
use rustc_target::spec::abi::Abi;
use syntax::ast::{self, Name, NodeId};
}
impl<'map, 'hir> ParentHirIterator<'map, 'hir> {
- fn new(current_id: HirId, map: &'map Map<'hir>) -> ParentHirIterator<'map, 'hir> {
- ParentHirIterator { current_id, map }
+ fn new(current_id: HirId, map: &'map Map<'hir>) -> Self {
+ Self { current_id, map }
}
}
-impl<'map, 'hir> Iterator for ParentHirIterator<'map, 'hir> {
+impl<'hir> Iterator for ParentHirIterator<'_, 'hir> {
type Item = (HirId, Node<'hir>);
fn next(&mut self) -> Option<Self::Item> {
self.forest.krate()
}
+ pub fn item(&self, id: HirId) -> &'hir Item<'hir> {
+ self.read(id);
+
+ // N.B., intentionally bypass `self.forest.krate()` so that we
+ // do not trigger a read of the whole krate here
+ self.forest.krate.item(id)
+ }
+
pub fn trait_item(&self, id: TraitItemId) -> &'hir TraitItem<'hir> {
self.read(id.hir_id);
}
}
+impl<'hir> intravisit::Map<'hir> for Map<'hir> {
+ fn body(&self, id: BodyId) -> &'hir Body<'hir> {
+ self.body(id)
+ }
+
+ fn item(&self, id: HirId) -> &'hir Item<'hir> {
+ self.item(id)
+ }
+
+ fn trait_item(&self, id: TraitItemId) -> &'hir TraitItem<'hir> {
+ self.trait_item(id)
+ }
+
+ fn impl_item(&self, id: ImplItemId) -> &'hir ImplItem<'hir> {
+ self.impl_item(id)
+ }
+}
+
pub struct NodesMatchingSuffix<'a> {
map: &'a Map<'a>,
item_name: &'a String,
definitions,
};
- sess.time("validate HIR map", || {
+ sess.time("validate_HIR_map", || {
hir_id_validator::check_crate(&map);
});
pub mod check_attr;
pub mod exports;
-pub mod intravisit;
pub mod map;
pub mod upvars;
//! Upvar (closure capture) collection from cross-body HIR uses of `Res::Local`s.
-use crate::hir::intravisit::{self, NestedVisitorMap, Visitor};
+use crate::hir::map::Map;
use crate::ty::query::Providers;
use crate::ty::TyCtxt;
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
use rustc_hir as hir;
use rustc_hir::def::Res;
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_hir::{self, HirId};
use rustc_span::Span;
}
impl Visitor<'tcx> for LocalCollector {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, Self::Map> {
NestedVisitorMap::None
}
}
impl Visitor<'tcx> for CaptureCollector<'a, 'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, Self::Map> {
NestedVisitorMap::None
}
use crate::ty::fold::TypeFoldable;
use crate::ty::subst::{GenericArg, GenericArgKind};
use crate::ty::{self, BoundVar, Ty, TyCtxt};
-use crate::util::captures::Captures;
+use rustc_data_structures::captures::Captures;
use rustc_index::vec::Idx;
use rustc_index::vec::IndexVec;
use rustc_span::DUMMY_SP;
use crate::infer::opaque_types;
use crate::infer::{self, SuppressRegionErrors};
use crate::middle::region;
+use crate::traits::error_reporting::report_object_safety_error;
+use crate::traits::object_safety_violations;
use crate::traits::{
IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
};
subst::{Subst, SubstsRef},
Region, Ty, TyCtxt, TypeFoldable,
};
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_error_codes::*;
+use rustc_errors::{pluralize, struct_span_err};
+use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::Node;
-
-use errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString};
-use rustc_error_codes::*;
-use rustc_span::{Pos, Span};
+use rustc_span::{DesugaringKind, Pos, Span};
use rustc_target::spec::abi;
use std::{cmp, fmt};
pub mod nice_region_error;
-impl<'tcx> TyCtxt<'tcx> {
- pub fn note_and_explain_region(
- self,
- region_scope_tree: ®ion::ScopeTree,
- err: &mut DiagnosticBuilder<'_>,
- prefix: &str,
- region: ty::Region<'tcx>,
- suffix: &str,
- ) {
- let (description, span) = match *region {
- ty::ReScope(scope) => {
- let new_string;
- let unknown_scope = || {
- format!("{}unknown scope: {:?}{}. Please report a bug.", prefix, scope, suffix)
- };
- let span = scope.span(self, region_scope_tree);
- let tag = match self.hir().find(scope.hir_id(region_scope_tree)) {
- Some(Node::Block(_)) => "block",
- Some(Node::Expr(expr)) => match expr.kind {
- hir::ExprKind::Call(..) => "call",
- hir::ExprKind::MethodCall(..) => "method call",
- hir::ExprKind::Match(.., hir::MatchSource::IfLetDesugar { .. }) => "if let",
- hir::ExprKind::Match(.., hir::MatchSource::WhileLetDesugar) => "while let",
- hir::ExprKind::Match(.., hir::MatchSource::ForLoopDesugar) => "for",
- hir::ExprKind::Match(..) => "match",
- _ => "expression",
- },
- Some(Node::Stmt(_)) => "statement",
- Some(Node::Item(it)) => Self::item_scope_tag(&it),
- Some(Node::TraitItem(it)) => Self::trait_item_scope_tag(&it),
- Some(Node::ImplItem(it)) => Self::impl_item_scope_tag(&it),
- Some(_) | None => {
- err.span_note(span, &unknown_scope());
- return;
- }
- };
- let scope_decorated_tag = match scope.data {
- region::ScopeData::Node => tag,
- region::ScopeData::CallSite => "scope of call-site for function",
- region::ScopeData::Arguments => "scope of function body",
- region::ScopeData::Destruction => {
- new_string = format!("destruction scope surrounding {}", tag);
- &new_string[..]
- }
- region::ScopeData::Remainder(first_statement_index) => {
- new_string = format!(
- "block suffix following statement {}",
- first_statement_index.index()
- );
- &new_string[..]
- }
- };
- self.explain_span(scope_decorated_tag, span)
- }
+pub(super) fn note_and_explain_region(
+ tcx: TyCtxt<'tcx>,
+ region_scope_tree: ®ion::ScopeTree,
+ err: &mut DiagnosticBuilder<'_>,
+ prefix: &str,
+ region: ty::Region<'tcx>,
+ suffix: &str,
+) {
+ let (description, span) = match *region {
+ ty::ReScope(scope) => {
+ let new_string;
+ let unknown_scope =
+ || format!("{}unknown scope: {:?}{}. Please report a bug.", prefix, scope, suffix);
+ let span = scope.span(tcx, region_scope_tree);
+ let tag = match tcx.hir().find(scope.hir_id(region_scope_tree)) {
+ Some(Node::Block(_)) => "block",
+ Some(Node::Expr(expr)) => match expr.kind {
+ hir::ExprKind::Call(..) => "call",
+ hir::ExprKind::MethodCall(..) => "method call",
+ hir::ExprKind::Match(.., hir::MatchSource::IfLetDesugar { .. }) => "if let",
+ hir::ExprKind::Match(.., hir::MatchSource::WhileLetDesugar) => "while let",
+ hir::ExprKind::Match(.., hir::MatchSource::ForLoopDesugar) => "for",
+ hir::ExprKind::Match(..) => "match",
+ _ => "expression",
+ },
+ Some(Node::Stmt(_)) => "statement",
+ Some(Node::Item(it)) => item_scope_tag(&it),
+ Some(Node::TraitItem(it)) => trait_item_scope_tag(&it),
+ Some(Node::ImplItem(it)) => impl_item_scope_tag(&it),
+ Some(_) | None => {
+ err.span_note(span, &unknown_scope());
+ return;
+ }
+ };
+ let scope_decorated_tag = match scope.data {
+ region::ScopeData::Node => tag,
+ region::ScopeData::CallSite => "scope of call-site for function",
+ region::ScopeData::Arguments => "scope of function body",
+ region::ScopeData::Destruction => {
+ new_string = format!("destruction scope surrounding {}", tag);
+ &new_string[..]
+ }
+ region::ScopeData::Remainder(first_statement_index) => {
+ new_string = format!(
+ "block suffix following statement {}",
+ first_statement_index.index()
+ );
+ &new_string[..]
+ }
+ };
+ explain_span(tcx, scope_decorated_tag, span)
+ }
- ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic => {
- self.msg_span_from_free_region(region)
- }
+ ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic => {
+ msg_span_from_free_region(tcx, region)
+ }
- ty::ReEmpty => ("the empty lifetime".to_owned(), None),
+ ty::ReEmpty => ("the empty lifetime".to_owned(), None),
- ty::RePlaceholder(_) => (format!("any other region"), None),
+ ty::RePlaceholder(_) => (format!("any other region"), None),
- // FIXME(#13998) RePlaceholder should probably print like
- // ReFree rather than dumping Debug output on the user.
- //
- // We shouldn't really be having unification failures with ReVar
- // and ReLateBound though.
- ty::ReVar(_) | ty::ReLateBound(..) | ty::ReErased => {
- (format!("lifetime {:?}", region), None)
- }
+ // FIXME(#13998) RePlaceholder should probably print like
+ // ReFree rather than dumping Debug output on the user.
+ //
+ // We shouldn't really be having unification failures with ReVar
+ // and ReLateBound though.
+ ty::ReVar(_) | ty::ReLateBound(..) | ty::ReErased => {
+ (format!("lifetime {:?}", region), None)
+ }
- // We shouldn't encounter an error message with ReClosureBound.
- ty::ReClosureBound(..) => {
- bug!("encountered unexpected ReClosureBound: {:?}", region,);
- }
- };
+ // We shouldn't encounter an error message with ReClosureBound.
+ ty::ReClosureBound(..) => {
+ bug!("encountered unexpected ReClosureBound: {:?}", region,);
+ }
+ };
- TyCtxt::emit_msg_span(err, prefix, description, span, suffix);
- }
+ emit_msg_span(err, prefix, description, span, suffix);
+}
- pub fn note_and_explain_free_region(
- self,
- err: &mut DiagnosticBuilder<'_>,
- prefix: &str,
- region: ty::Region<'tcx>,
- suffix: &str,
- ) {
- let (description, span) = self.msg_span_from_free_region(region);
+pub(super) fn note_and_explain_free_region(
+ tcx: TyCtxt<'tcx>,
+ err: &mut DiagnosticBuilder<'_>,
+ prefix: &str,
+ region: ty::Region<'tcx>,
+ suffix: &str,
+) {
+ let (description, span) = msg_span_from_free_region(tcx, region);
- TyCtxt::emit_msg_span(err, prefix, description, span, suffix);
- }
+ emit_msg_span(err, prefix, description, span, suffix);
+}
- fn msg_span_from_free_region(self, region: ty::Region<'tcx>) -> (String, Option<Span>) {
- match *region {
- ty::ReEarlyBound(_) | ty::ReFree(_) => {
- self.msg_span_from_early_bound_and_free_regions(region)
- }
- ty::ReStatic => ("the static lifetime".to_owned(), None),
- ty::ReEmpty => ("an empty lifetime".to_owned(), None),
- _ => bug!("{:?}", region),
+fn msg_span_from_free_region(
+ tcx: TyCtxt<'tcx>,
+ region: ty::Region<'tcx>,
+) -> (String, Option<Span>) {
+ match *region {
+ ty::ReEarlyBound(_) | ty::ReFree(_) => {
+ msg_span_from_early_bound_and_free_regions(tcx, region)
}
+ ty::ReStatic => ("the static lifetime".to_owned(), None),
+ ty::ReEmpty => ("an empty lifetime".to_owned(), None),
+ _ => bug!("{:?}", region),
}
+}
- fn msg_span_from_early_bound_and_free_regions(
- self,
- region: ty::Region<'tcx>,
- ) -> (String, Option<Span>) {
- let cm = self.sess.source_map();
-
- let scope = region.free_region_binding_scope(self);
- let node = self.hir().as_local_hir_id(scope).unwrap_or(hir::DUMMY_HIR_ID);
- let tag = match self.hir().find(node) {
- Some(Node::Block(_)) | Some(Node::Expr(_)) => "body",
- Some(Node::Item(it)) => Self::item_scope_tag(&it),
- Some(Node::TraitItem(it)) => Self::trait_item_scope_tag(&it),
- Some(Node::ImplItem(it)) => Self::impl_item_scope_tag(&it),
- _ => unreachable!(),
- };
- let (prefix, span) = match *region {
- ty::ReEarlyBound(ref br) => {
- let mut sp = cm.def_span(self.hir().span(node));
- if let Some(param) =
- self.hir().get_generics(scope).and_then(|generics| generics.get_named(br.name))
- {
- sp = param.span;
- }
- (format!("the lifetime `{}` as defined on", br.name), sp)
- }
- ty::ReFree(ty::FreeRegion {
- bound_region: ty::BoundRegion::BrNamed(_, name), ..
- }) => {
- let mut sp = cm.def_span(self.hir().span(node));
- if let Some(param) =
- self.hir().get_generics(scope).and_then(|generics| generics.get_named(name))
- {
- sp = param.span;
- }
- (format!("the lifetime `{}` as defined on", name), sp)
- }
- ty::ReFree(ref fr) => match fr.bound_region {
- ty::BrAnon(idx) => (
- format!("the anonymous lifetime #{} defined on", idx + 1),
- self.hir().span(node),
- ),
- _ => (
- format!("the lifetime `{}` as defined on", region),
- cm.def_span(self.hir().span(node)),
- ),
- },
- _ => bug!(),
- };
- let (msg, opt_span) = self.explain_span(tag, span);
- (format!("{} {}", prefix, msg), opt_span)
- }
-
- fn emit_msg_span(
- err: &mut DiagnosticBuilder<'_>,
- prefix: &str,
- description: String,
- span: Option<Span>,
- suffix: &str,
- ) {
- let message = format!("{}{}{}", prefix, description, suffix);
-
- if let Some(span) = span {
- err.span_note(span, &message);
- } else {
- err.note(&message);
+fn msg_span_from_early_bound_and_free_regions(
+ tcx: TyCtxt<'tcx>,
+ region: ty::Region<'tcx>,
+) -> (String, Option<Span>) {
+ let cm = tcx.sess.source_map();
+
+ let scope = region.free_region_binding_scope(tcx);
+ let node = tcx.hir().as_local_hir_id(scope).unwrap_or(hir::DUMMY_HIR_ID);
+ let tag = match tcx.hir().find(node) {
+ Some(Node::Block(_)) | Some(Node::Expr(_)) => "body",
+ Some(Node::Item(it)) => item_scope_tag(&it),
+ Some(Node::TraitItem(it)) => trait_item_scope_tag(&it),
+ Some(Node::ImplItem(it)) => impl_item_scope_tag(&it),
+ _ => unreachable!(),
+ };
+ let (prefix, span) = match *region {
+ ty::ReEarlyBound(ref br) => {
+ let mut sp = cm.def_span(tcx.hir().span(node));
+ if let Some(param) =
+ tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(br.name))
+ {
+ sp = param.span;
+ }
+ (format!("the lifetime `{}` as defined on", br.name), sp)
}
- }
-
- fn item_scope_tag(item: &hir::Item<'_>) -> &'static str {
- match item.kind {
- hir::ItemKind::Impl(..) => "impl",
- hir::ItemKind::Struct(..) => "struct",
- hir::ItemKind::Union(..) => "union",
- hir::ItemKind::Enum(..) => "enum",
- hir::ItemKind::Trait(..) => "trait",
- hir::ItemKind::Fn(..) => "function body",
- _ => "item",
+ ty::ReFree(ty::FreeRegion { bound_region: ty::BoundRegion::BrNamed(_, name), .. }) => {
+ let mut sp = cm.def_span(tcx.hir().span(node));
+ if let Some(param) =
+ tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(name))
+ {
+ sp = param.span;
+ }
+ (format!("the lifetime `{}` as defined on", name), sp)
}
+ ty::ReFree(ref fr) => match fr.bound_region {
+ ty::BrAnon(idx) => {
+ (format!("the anonymous lifetime #{} defined on", idx + 1), tcx.hir().span(node))
+ }
+ _ => (
+ format!("the lifetime `{}` as defined on", region),
+ cm.def_span(tcx.hir().span(node)),
+ ),
+ },
+ _ => bug!(),
+ };
+ let (msg, opt_span) = explain_span(tcx, tag, span);
+ (format!("{} {}", prefix, msg), opt_span)
+}
+
+fn emit_msg_span(
+ err: &mut DiagnosticBuilder<'_>,
+ prefix: &str,
+ description: String,
+ span: Option<Span>,
+ suffix: &str,
+) {
+ let message = format!("{}{}{}", prefix, description, suffix);
+
+ if let Some(span) = span {
+ err.span_note(span, &message);
+ } else {
+ err.note(&message);
}
+}
- fn trait_item_scope_tag(item: &hir::TraitItem<'_>) -> &'static str {
- match item.kind {
- hir::TraitItemKind::Method(..) => "method body",
- hir::TraitItemKind::Const(..) | hir::TraitItemKind::Type(..) => "associated item",
- }
+fn item_scope_tag(item: &hir::Item<'_>) -> &'static str {
+ match item.kind {
+ hir::ItemKind::Impl(..) => "impl",
+ hir::ItemKind::Struct(..) => "struct",
+ hir::ItemKind::Union(..) => "union",
+ hir::ItemKind::Enum(..) => "enum",
+ hir::ItemKind::Trait(..) => "trait",
+ hir::ItemKind::Fn(..) => "function body",
+ _ => "item",
}
+}
- fn impl_item_scope_tag(item: &hir::ImplItem<'_>) -> &'static str {
- match item.kind {
- hir::ImplItemKind::Method(..) => "method body",
- hir::ImplItemKind::Const(..)
- | hir::ImplItemKind::OpaqueTy(..)
- | hir::ImplItemKind::TyAlias(..) => "associated item",
- }
+fn trait_item_scope_tag(item: &hir::TraitItem<'_>) -> &'static str {
+ match item.kind {
+ hir::TraitItemKind::Method(..) => "method body",
+ hir::TraitItemKind::Const(..) | hir::TraitItemKind::Type(..) => "associated item",
}
+}
- fn explain_span(self, heading: &str, span: Span) -> (String, Option<Span>) {
- let lo = self.sess.source_map().lookup_char_pos(span.lo());
- (format!("the {} at {}:{}", heading, lo.line, lo.col.to_usize() + 1), Some(span))
+fn impl_item_scope_tag(item: &hir::ImplItem<'_>) -> &'static str {
+ match item.kind {
+ hir::ImplItemKind::Method(..) => "method body",
+ hir::ImplItemKind::Const(..)
+ | hir::ImplItemKind::OpaqueTy(..)
+ | hir::ImplItemKind::TyAlias(..) => "associated item",
}
}
+fn explain_span(tcx: TyCtxt<'tcx>, heading: &str, span: Span) -> (String, Option<Span>) {
+ let lo = tcx.sess.source_map().lookup_char_pos(span.lo());
+ (format!("the {} at {}:{}", heading, lo.line, lo.col.to_usize() + 1), Some(span))
+}
+
impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
pub fn report_region_errors(
&self,
mut values: Option<ValuePairs<'tcx>>,
terr: &TypeError<'tcx>,
) {
+ let span = cause.span(self.tcx);
+
// For some types of errors, expected-found does not make
// sense, so just ignore the values we were given.
match terr {
_ => {}
}
+ struct OpaqueTypesVisitor<'tcx> {
+ types: FxHashMap<TyCategory, FxHashSet<Span>>,
+ expected: FxHashMap<TyCategory, FxHashSet<Span>>,
+ found: FxHashMap<TyCategory, FxHashSet<Span>>,
+ ignore_span: Span,
+ tcx: TyCtxt<'tcx>,
+ }
+
+ impl<'tcx> OpaqueTypesVisitor<'tcx> {
+ fn visit_expected_found(
+ tcx: TyCtxt<'tcx>,
+ expected: Ty<'tcx>,
+ found: Ty<'tcx>,
+ ignore_span: Span,
+ ) -> Self {
+ let mut types_visitor = OpaqueTypesVisitor {
+ types: Default::default(),
+ expected: Default::default(),
+ found: Default::default(),
+ ignore_span,
+ tcx,
+ };
+ // The visitor puts all the relevant encountered types in `self.types`, but in
+ // here we want to visit two separate types with no relation to each other, so we
+ // move the results from `types` to `expected` or `found` as appropriate.
+ expected.visit_with(&mut types_visitor);
+ std::mem::swap(&mut types_visitor.expected, &mut types_visitor.types);
+ found.visit_with(&mut types_visitor);
+ std::mem::swap(&mut types_visitor.found, &mut types_visitor.types);
+ types_visitor
+ }
+
+ fn report(&self, err: &mut DiagnosticBuilder<'_>) {
+ self.add_labels_for_types(err, "expected", &self.expected);
+ self.add_labels_for_types(err, "found", &self.found);
+ }
+
+ fn add_labels_for_types(
+ &self,
+ err: &mut DiagnosticBuilder<'_>,
+ target: &str,
+ types: &FxHashMap<TyCategory, FxHashSet<Span>>,
+ ) {
+ for (key, values) in types.iter() {
+ let count = values.len();
+ let kind = key.descr();
+ for sp in values {
+ err.span_label(
+ *sp,
+ format!(
+ "{}{}{} {}{}",
+ if sp.is_desugaring(DesugaringKind::Async) {
+ "the `Output` of this `async fn`'s "
+ } else if count == 1 {
+ "the "
+ } else {
+ ""
+ },
+ if count > 1 { "one of the " } else { "" },
+ target,
+ kind,
+ pluralize!(count),
+ ),
+ );
+ }
+ }
+ }
+ }
+
+ impl<'tcx> ty::fold::TypeVisitor<'tcx> for OpaqueTypesVisitor<'tcx> {
+ fn visit_ty(&mut self, t: Ty<'tcx>) -> bool {
+ if let Some((kind, def_id)) = TyCategory::from_ty(t) {
+ let span = self.tcx.def_span(def_id);
+ // Avoid cluttering the output when the "found" and error span overlap:
+ //
+ // error[E0308]: mismatched types
+ // --> $DIR/issue-20862.rs:2:5
+ // |
+ // LL | |y| x + y
+ // | ^^^^^^^^^
+ // | |
+ // | the found closure
+ // | expected `()`, found closure
+ // |
+ // = note: expected unit type `()`
+ // found closure `[closure@$DIR/issue-20862.rs:2:5: 2:14 x:_]`
+ if !self.ignore_span.overlaps(span) {
+ self.types.entry(kind).or_default().insert(span);
+ }
+ }
+ t.super_visit_with(self)
+ }
+ }
+
debug!("note_type_err(diag={:?})", diag);
let (expected_found, exp_found, is_simple_error) = match values {
None => (None, None, false),
ValuePairs::Types(exp_found) => {
let is_simple_err =
exp_found.expected.is_simple_text() && exp_found.found.is_simple_text();
+ OpaqueTypesVisitor::visit_expected_found(
+ self.tcx,
+ exp_found.expected,
+ exp_found.found,
+ span,
+ )
+ .report(diag);
(is_simple_err, Some(exp_found))
}
}
};
- let span = cause.span(self.tcx);
-
// Ignore msg for object safe coercion
// since E0038 message will be printed
match terr {
}
}
};
-
if let Some((expected, found)) = expected_found {
let expected_label = exp_found.map_or("type".into(), |ef| ef.expected.prefix_string());
let found_label = exp_found.map_or("type".into(), |ef| ef.found.prefix_string());
let failure_code = trace.cause.as_failure_code(terr);
let mut diag = match failure_code {
FailureCode::Error0038(did) => {
- let violations = self.tcx.object_safety_violations(did);
- self.tcx.report_object_safety_error(span, did, violations)
+ let violations = object_safety_violations(self.tcx, did);
+ report_object_safety_error(self.tcx, span, did, violations)
}
FailureCode::Error0317(failure_str) => {
struct_span_err!(self.tcx.sess, span, E0317, "{}", failure_str)
"consider adding an explicit lifetime bound for `{}`",
bound_kind
));
- self.tcx.note_and_explain_region(
+ note_and_explain_region(
+ self.tcx,
region_scope_tree,
&mut err,
&format!("{} must be valid for ", labeled_user_string),
) {
let mut err = self.report_inference_failure(var_origin);
- self.tcx.note_and_explain_region(
+ note_and_explain_region(
+ self.tcx,
region_scope_tree,
&mut err,
"first, the lifetime cannot outlive ",
(self.values_str(&sup_trace.values), self.values_str(&sub_trace.values))
{
if sub_expected == sup_expected && sub_found == sup_found {
- self.tcx.note_and_explain_region(
+ note_and_explain_region(
+ self.tcx,
region_scope_tree,
&mut err,
"...but the lifetime must also be valid for ",
self.note_region_origin(&mut err, &sup_origin);
- self.tcx.note_and_explain_region(
+ note_and_explain_region(
+ self.tcx,
region_scope_tree,
&mut err,
"but, the lifetime must be valid for ",
}
}
}
+
+/// This is a bare signal of what kind of type we're dealing with. `ty::TyKind` tracks
+/// extra information about each type, but we only care about the category.
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
+crate enum TyCategory {
+ Closure,
+ Opaque,
+ Generator,
+ Foreign,
+}
+
+impl TyCategory {
+ fn descr(&self) -> &'static str {
+ match self {
+ Self::Closure => "closure",
+ Self::Opaque => "opaque type",
+ Self::Generator => "generator",
+ Self::Foreign => "foreign type",
+ }
+ }
+
+ pub fn from_ty(ty: Ty<'_>) -> Option<(Self, DefId)> {
+ match ty.kind {
+ ty::Closure(def_id, _) => Some((Self::Closure, def_id)),
+ ty::Opaque(def_id, _) => Some((Self::Opaque, def_id)),
+ ty::Generator(def_id, ..) => Some((Self::Generator, def_id)),
+ ty::Foreign(def_id) => Some((Self::Foreign, def_id)),
+ _ => None,
+ }
+ }
+}
-use crate::hir::intravisit::{self, NestedVisitorMap, Visitor};
use crate::hir::map::Map;
use crate::infer::type_variable::TypeVariableOriginKind;
use crate::infer::InferCtxt;
use crate::ty::print::Print;
use crate::ty::{self, DefIdTree, Infer, Ty, TyVar};
-use errors::{Applicability, DiagnosticBuilder};
+use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Namespace};
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_hir::{Body, Expr, ExprKind, FunctionRetTy, HirId, Local, Pat};
use rustc_span::source_map::DesugaringKind;
use rustc_span::symbol::kw;
}
impl<'a, 'tcx> Visitor<'tcx> for FindLocalByTypeVisitor<'a, 'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, Self::Map> {
NestedVisitorMap::OnlyBodies(&self.hir_map)
}
E0284,
}
-impl Into<errors::DiagnosticId> for TypeAnnotationNeeded {
- fn into(self) -> errors::DiagnosticId {
- syntax::diagnostic_used!(E0282);
- syntax::diagnostic_used!(E0283);
- syntax::diagnostic_used!(E0284);
- errors::DiagnosticId::Error(match self {
- Self::E0282 => "E0282".to_string(),
- Self::E0283 => "E0283".to_string(),
- Self::E0284 => "E0284".to_string(),
- })
+impl Into<rustc_errors::DiagnosticId> for TypeAnnotationNeeded {
+ fn into(self) -> rustc_errors::DiagnosticId {
+ match self {
+ Self::E0282 => rustc_errors::error_code!(E0282),
+ Self::E0283 => rustc_errors::error_code!(E0283),
+ Self::E0284 => rustc_errors::error_code!(E0284),
+ }
}
}
use crate::util::common::ErrorReported;
use rustc_error_codes::*;
+use rustc_errors::struct_span_err;
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
/// Print the error message for lifetime errors when both the concerned regions are anonymous.
-use crate::hir::intravisit::{self, NestedVisitorMap, Visitor};
+use crate::hir::map::Map;
use crate::infer::error_reporting::nice_region_error::NiceRegionError;
use crate::middle::resolve_lifetime as rl;
use crate::ty::{self, Region, TyCtxt};
use rustc_hir as hir;
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_hir::Node;
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
}
impl Visitor<'tcx> for FindNestedTypeVisitor<'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, Self::Map> {
NestedVisitorMap::OnlyBodies(&self.tcx.hir())
}
}
impl Visitor<'tcx> for TyPathVisitor<'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, Map<'tcx>> {
NestedVisitorMap::OnlyBodies(&self.tcx.hir())
}
use crate::infer::InferCtxt;
use crate::ty::{self, TyCtxt};
use crate::util::common::ErrorReported;
-use errors::DiagnosticBuilder;
+use rustc_errors::DiagnosticBuilder;
use rustc_span::source_map::Span;
mod different_lifetimes;
//! where one region is named and the other is anonymous.
use crate::infer::error_reporting::nice_region_error::NiceRegionError;
use crate::ty;
-use errors::{Applicability, DiagnosticBuilder};
+use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
use rustc_hir::{FunctionRetTy, TyKind};
use rustc_error_codes::*;
use crate::ty::print::{FmtPrinter, Print, RegionHighlightMode};
use crate::ty::subst::SubstsRef;
use crate::ty::{self, TyCtxt};
-use errors::DiagnosticBuilder;
+use rustc_errors::DiagnosticBuilder;
use rustc_hir::def::Namespace;
use rustc_hir::def_id::DefId;
//! Error Reporting for static impl Traits.
+use crate::infer::error_reporting::msg_span_from_free_region;
use crate::infer::error_reporting::nice_region_error::NiceRegionError;
use crate::infer::lexical_region_resolve::RegionResolutionError;
use crate::ty::{BoundRegion, FreeRegion, RegionKind};
use crate::util::common::ErrorReported;
-use errors::Applicability;
+use rustc_errors::Applicability;
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
/// Print the error message for lifetime errors when the return type is a static impl Trait.
);
err.span_label(sup_origin.span(), "...but this borrow...");
- let (lifetime, lt_sp_opt) = self.tcx().msg_span_from_free_region(sup_r);
+ let (lifetime, lt_sp_opt) = msg_span_from_free_region(self.tcx(), sup_r);
if let Some(lifetime_sp) = lt_sp_opt {
err.span_note(lifetime_sp, &format!("...can't outlive {}", lifetime));
}
+use crate::infer::error_reporting::note_and_explain_region;
use crate::infer::{self, InferCtxt, SubregionOrigin};
use crate::middle::region;
use crate::ty::error::TypeError;
use crate::ty::{self, Region};
-use errors::DiagnosticBuilder;
+use rustc_errors::{struct_span_err, DiagnosticBuilder};
use rustc_error_codes::*;
infer::Subtype(box trace) => {
let terr = TypeError::RegionsDoesNotOutlive(sup, sub);
let mut err = self.report_and_explain_type_error(trace, &terr);
- self.tcx.note_and_explain_region(region_scope_tree, &mut err, "", sup, "...");
- self.tcx.note_and_explain_region(
+ note_and_explain_region(self.tcx, region_scope_tree, &mut err, "", sup, "...");
+ note_and_explain_region(
+ self.tcx,
region_scope_tree,
&mut err,
"...does not necessarily outlive ",
"lifetime of reference outlives lifetime of \
borrowed content..."
);
- self.tcx.note_and_explain_region(
+ note_and_explain_region(
+ self.tcx,
region_scope_tree,
&mut err,
"...the reference is valid for ",
sub,
"...",
);
- self.tcx.note_and_explain_region(
+ note_and_explain_region(
+ self.tcx,
region_scope_tree,
&mut err,
"...but the borrowed content is only valid for ",
of captured variable `{}`...",
var_name
);
- self.tcx.note_and_explain_region(
+ note_and_explain_region(
+ self.tcx,
region_scope_tree,
&mut err,
"...the borrowed pointer is valid for ",
sub,
"...",
);
- self.tcx.note_and_explain_region(
+ note_and_explain_region(
+ self.tcx,
region_scope_tree,
&mut err,
&format!("...but `{}` is only valid for ", var_name),
infer::InfStackClosure(span) => {
let mut err =
struct_span_err!(self.tcx.sess, span, E0314, "closure outlives stack frame");
- self.tcx.note_and_explain_region(
+ note_and_explain_region(
+ self.tcx,
region_scope_tree,
&mut err,
"...the closure must be valid for ",
sub,
"...",
);
- self.tcx.note_and_explain_region(
+ note_and_explain_region(
+ self.tcx,
region_scope_tree,
&mut err,
"...but the closure's stack frame is only valid \
E0315,
"cannot invoke closure outside of its lifetime"
);
- self.tcx.note_and_explain_region(
+ note_and_explain_region(
+ self.tcx,
region_scope_tree,
&mut err,
"the closure is only valid for ",
E0473,
"dereference of reference outside its lifetime"
);
- self.tcx.note_and_explain_region(
+ note_and_explain_region(
+ self.tcx,
region_scope_tree,
&mut err,
"the reference is only valid for ",
enclosing closure",
self.tcx.hir().name(id)
);
- self.tcx.note_and_explain_region(
+ note_and_explain_region(
+ self.tcx,
region_scope_tree,
&mut err,
"captured variable is valid for ",
sup,
"",
);
- self.tcx.note_and_explain_region(
+ note_and_explain_region(
+ self.tcx,
region_scope_tree,
&mut err,
"closure is valid for ",
E0475,
"index of slice outside its lifetime"
);
- self.tcx.note_and_explain_region(
+ note_and_explain_region(
+ self.tcx,
region_scope_tree,
&mut err,
"the slice is only valid for ",
"lifetime of the source pointer does not outlive \
lifetime bound of the object type"
);
- self.tcx.note_and_explain_region(
+ note_and_explain_region(
+ self.tcx,
region_scope_tree,
&mut err,
"object type is valid for ",
sub,
"",
);
- self.tcx.note_and_explain_region(
+ note_and_explain_region(
+ self.tcx,
region_scope_tree,
&mut err,
"source pointer is only valid for ",
self.ty_to_string(ty)
);
match *sub {
- ty::ReStatic => self.tcx.note_and_explain_region(
+ ty::ReStatic => note_and_explain_region(
+ self.tcx,
region_scope_tree,
&mut err,
"type must satisfy ",
sub,
"",
),
- _ => self.tcx.note_and_explain_region(
+ _ => note_and_explain_region(
+ self.tcx,
region_scope_tree,
&mut err,
"type must outlive ",
infer::RelateRegionParamBound(span) => {
let mut err =
struct_span_err!(self.tcx.sess, span, E0478, "lifetime bound not satisfied");
- self.tcx.note_and_explain_region(
+ note_and_explain_region(
+ self.tcx,
region_scope_tree,
&mut err,
"lifetime parameter instantiated with ",
sup,
"",
);
- self.tcx.note_and_explain_region(
+ note_and_explain_region(
+ self.tcx,
region_scope_tree,
&mut err,
"but lifetime parameter must outlive ",
parameter) is not valid at this point",
self.ty_to_string(ty)
);
- self.tcx.note_and_explain_region(
+ note_and_explain_region(
+ self.tcx,
region_scope_tree,
&mut err,
"type must outlive ",
"lifetime of method receiver does not outlive the \
method call"
);
- self.tcx.note_and_explain_region(
+ note_and_explain_region(
+ self.tcx,
region_scope_tree,
&mut err,
"the receiver is only valid for ",
"lifetime of function argument does not outlive \
the function call"
);
- self.tcx.note_and_explain_region(
+ note_and_explain_region(
+ self.tcx,
region_scope_tree,
&mut err,
"the function argument is only valid for ",
"lifetime of return value does not outlive the \
function call"
);
- self.tcx.note_and_explain_region(
+ note_and_explain_region(
+ self.tcx,
region_scope_tree,
&mut err,
"the return value is only valid for ",
"lifetime of operand does not outlive the \
operation"
);
- self.tcx.note_and_explain_region(
+ note_and_explain_region(
+ self.tcx,
region_scope_tree,
&mut err,
"the operand is only valid for ",
E0484,
"reference is not valid at the time of borrow"
);
- self.tcx.note_and_explain_region(
+ note_and_explain_region(
+ self.tcx,
region_scope_tree,
&mut err,
"the borrow is only valid for ",
"automatically reference is not valid at the time \
of borrow"
);
- self.tcx.note_and_explain_region(
+ note_and_explain_region(
+ self.tcx,
region_scope_tree,
&mut err,
"the automatic borrow is only valid for ",
not valid during the expression: `{}`",
self.ty_to_string(t)
);
- self.tcx.note_and_explain_region(
+ note_and_explain_region(
+ self.tcx,
region_scope_tree,
&mut err,
"type is only valid for ",
called while references are dead"
);
// FIXME (22171): terms "super/subregion" are suboptimal
- self.tcx.note_and_explain_region(
+ note_and_explain_region(
+ self.tcx,
region_scope_tree,
&mut err,
"superregion: ",
sup,
"",
);
- self.tcx.note_and_explain_region(
+ note_and_explain_region(
+ self.tcx,
region_scope_tree,
&mut err,
"subregion: ",
"lifetime of variable does not enclose its \
declaration"
);
- self.tcx.note_and_explain_region(
+ note_and_explain_region(
+ self.tcx,
region_scope_tree,
&mut err,
"the variable is only valid for ",
E0489,
"type/lifetime parameter not in scope here"
);
- self.tcx.note_and_explain_region(
+ note_and_explain_region(
+ self.tcx,
region_scope_tree,
&mut err,
"the parameter is only valid for ",
"a value of type `{}` is borrowed for too long",
self.ty_to_string(ty)
);
- self.tcx.note_and_explain_region(
+ note_and_explain_region(
+ self.tcx,
region_scope_tree,
&mut err,
"the type is valid for ",
sub,
"",
);
- self.tcx.note_and_explain_region(
+ note_and_explain_region(
+ self.tcx,
region_scope_tree,
&mut err,
"but the borrow lasts for ",
than the data it references",
self.ty_to_string(ty)
);
- self.tcx.note_and_explain_region(
+ note_and_explain_region(
+ self.tcx,
region_scope_tree,
&mut err,
"the pointer is valid for ",
sub,
"",
);
- self.tcx.note_and_explain_region(
+ note_and_explain_region(
+ self.tcx,
region_scope_tree,
&mut err,
"but the referenced data is only valid for ",
// want to stop at the first constraint that makes a change.
let mut any_changed = false;
for member_constraint in &self.data.member_constraints {
- if self.enforce_member_constraint(graph, member_constraint, var_values) {
- any_changed = true;
- }
+ any_changed |= self.enforce_member_constraint(graph, member_constraint, var_values);
}
any_changed
}
for index in live_indices.iter() {
let constraint = constraints[index];
let (edge_changed, retain) = process_constraint(constraint);
- if edge_changed {
- changed = true;
- }
+ changed |= edge_changed;
if !retain {
let changed = killed_indices.insert(index);
debug_assert!(changed);
use crate::ty::{self, GenericParamDefKind, InferConst, Ty, TyCtxt};
use crate::ty::{ConstVid, FloatVid, IntVid, TyVid};
-use errors::DiagnosticBuilder;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::sync::Lrc;
use rustc_data_structures::unify as ut;
+use rustc_errors::DiagnosticBuilder;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_span::symbol::Symbol;
-use crate::infer::outlives::free_region_map::FreeRegionRelations;
+use crate::infer::error_reporting::{note_and_explain_free_region, note_and_explain_region};
use crate::infer::{self, InferCtxt, InferOk, TypeVariableOrigin, TypeVariableOriginKind};
use crate::middle::region;
use crate::traits::{self, PredicateObligation};
use crate::ty::fold::{BottomUpFolder, TypeFoldable, TypeFolder, TypeVisitor};
+use crate::ty::free_region_map::FreeRegionRelations;
use crate::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, SubstsRef};
use crate::ty::{self, GenericParamDefKind, Ty, TyCtxt};
-use errors::DiagnosticBuilder;
use rustc::session::config::nightly_options;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sync::Lrc;
+use rustc_errors::{struct_span_err, DiagnosticBuilder};
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, DefIdMap};
use rustc_hir::Node;
debug!("constrain_opaque_type: bounds={:#?}", bounds);
let opaque_type = tcx.mk_opaque(def_id, opaque_defn.substs);
- let required_region_bounds = tcx.required_region_bounds(opaque_type, bounds.predicates);
+ let required_region_bounds =
+ required_region_bounds(tcx, opaque_type, bounds.predicates);
debug_assert!(!required_region_bounds.is_empty());
for required_region in required_region_bounds {
err.span_label(span, label);
if nightly_options::is_nightly_build() {
- help!(
- err,
- "add #![feature(member_constraints)] to the crate attributes \
- to enable"
- );
+ err.help("add #![feature(member_constraints)] to the crate attributes to enable");
}
err.emit();
//
// (*) if not, the `tainted_by_errors` flag would be set to
// true in any case, so we wouldn't be here at all.
- tcx.note_and_explain_free_region(
+ note_and_explain_free_region(
+ tcx,
&mut err,
&format!("hidden type `{}` captures ", hidden_ty),
hidden_region,
// If the `region_scope_tree` is available, this is being
// invoked from the "region inferencer error". We can at
// least report a really cryptic error for now.
- tcx.note_and_explain_region(
+ note_and_explain_region(
+ tcx,
region_scope_tree,
&mut err,
&format!("hidden type `{}` captures ", hidden_ty),
debug!("instantiate_opaque_types: bounds={:?}", bounds);
- let required_region_bounds = tcx.required_region_bounds(ty, bounds.predicates.clone());
+ let required_region_bounds = required_region_bounds(tcx, ty, bounds.predicates.clone());
debug!("instantiate_opaque_types: required_region_bounds={:?}", required_region_bounds);
// Make sure that we are in fact defining the *entire* type
);
res
}
+
+/// Given a set of predicates that apply to an object type, returns
+/// the region bounds that the (erased) `Self` type must
+/// outlive. Precisely *because* the `Self` type is erased, the
+/// parameter `erased_self_ty` must be supplied to indicate what type
+/// has been used to represent `Self` in the predicates
+/// themselves. This should really be a unique type; `FreshTy(0)` is a
+/// popular choice.
+///
+/// N.B., in some cases, particularly around higher-ranked bounds,
+/// this function returns a kind of conservative approximation.
+/// That is, all regions returned by this function are definitely
+/// required, but there may be other region bounds that are not
+/// returned, as well as requirements like `for<'a> T: 'a`.
+///
+/// Requires that trait definitions have been processed so that we can
+/// elaborate predicates and walk supertraits.
+//
+// FIXME: callers may only have a `&[Predicate]`, not a `Vec`, so that's
+// what this code should accept.
+crate fn required_region_bounds(
+ tcx: TyCtxt<'tcx>,
+ erased_self_ty: Ty<'tcx>,
+ predicates: Vec<ty::Predicate<'tcx>>,
+) -> Vec<ty::Region<'tcx>> {
+ debug!(
+ "required_region_bounds(erased_self_ty={:?}, predicates={:?})",
+ erased_self_ty, predicates
+ );
+
+ assert!(!erased_self_ty.has_escaping_bound_vars());
+
+ traits::elaborate_predicates(tcx, predicates)
+ .filter_map(|predicate| {
+ match predicate {
+ ty::Predicate::Projection(..)
+ | ty::Predicate::Trait(..)
+ | ty::Predicate::Subtype(..)
+ | ty::Predicate::WellFormed(..)
+ | ty::Predicate::ObjectSafe(..)
+ | ty::Predicate::ClosureKind(..)
+ | ty::Predicate::RegionOutlives(..)
+ | ty::Predicate::ConstEvaluatable(..) => None,
+ ty::Predicate::TypeOutlives(predicate) => {
+ // Search for a bound of the form `erased_self_ty
+ // : 'a`, but be wary of something like `for<'a>
+ // erased_self_ty : 'a` (we interpret a
+ // higher-ranked bound like that as 'static,
+ // though at present the code in `fulfill.rs`
+ // considers such bounds to be unsatisfiable, so
+ // it's kind of a moot point since you could never
+ // construct such an object, but this seems
+ // correct even if that code changes).
+ let ty::OutlivesPredicate(ref t, ref r) = predicate.skip_binder();
+ if t == &erased_self_ty && !r.has_escaping_bound_vars() {
+ Some(*r)
+ } else {
+ None
+ }
+ }
+ }
+ })
+ .collect()
+}
-use crate::infer::outlives::free_region_map::FreeRegionMap;
use crate::infer::{GenericKind, InferCtxt};
use crate::traits::query::outlives_bounds::{self, OutlivesBound};
+use crate::ty::free_region_map::FreeRegionMap;
use crate::ty::{self, Ty};
use rustc_data_structures::fx::FxHashMap;
use rustc_hir as hir;
+++ /dev/null
-use crate::ty::{self, Lift, Region, TyCtxt};
-use rustc_data_structures::transitive_relation::TransitiveRelation;
-
-#[derive(Clone, RustcEncodable, RustcDecodable, Debug, Default, HashStable)]
-pub struct FreeRegionMap<'tcx> {
- // Stores the relation `a < b`, where `a` and `b` are regions.
- //
- // Invariant: only free regions like `'x` or `'static` are stored
- // in this relation, not scopes.
- relation: TransitiveRelation<Region<'tcx>>,
-}
-
-impl<'tcx> FreeRegionMap<'tcx> {
- pub fn elements(&self) -> impl Iterator<Item = &Region<'tcx>> {
- self.relation.elements()
- }
-
- pub fn is_empty(&self) -> bool {
- self.relation.is_empty()
- }
-
- // Record that `'sup:'sub`. Or, put another way, `'sub <= 'sup`.
- // (with the exception that `'static: 'x` is not notable)
- pub fn relate_regions(&mut self, sub: Region<'tcx>, sup: Region<'tcx>) {
- debug!("relate_regions(sub={:?}, sup={:?})", sub, sup);
- if is_free_or_static(sub) && is_free(sup) {
- self.relation.add(sub, sup)
- }
- }
-
- /// Computes the least-upper-bound of two free regions. In some
- /// cases, this is more conservative than necessary, in order to
- /// avoid making arbitrary choices. See
- /// `TransitiveRelation::postdom_upper_bound` for more details.
- pub fn lub_free_regions(
- &self,
- tcx: TyCtxt<'tcx>,
- r_a: Region<'tcx>,
- r_b: Region<'tcx>,
- ) -> Region<'tcx> {
- debug!("lub_free_regions(r_a={:?}, r_b={:?})", r_a, r_b);
- assert!(is_free(r_a));
- assert!(is_free(r_b));
- let result = if r_a == r_b {
- r_a
- } else {
- match self.relation.postdom_upper_bound(&r_a, &r_b) {
- None => tcx.mk_region(ty::ReStatic),
- Some(r) => *r,
- }
- };
- debug!("lub_free_regions(r_a={:?}, r_b={:?}) = {:?}", r_a, r_b, result);
- result
- }
-}
-
-/// The NLL region handling code represents free region relations in a
-/// slightly different way; this trait allows functions to be abstract
-/// over which version is in use.
-pub trait FreeRegionRelations<'tcx> {
- /// Tests whether `r_a <= r_b`. Both must be free regions or
- /// `'static`.
- fn sub_free_regions(&self, shorter: ty::Region<'tcx>, longer: ty::Region<'tcx>) -> bool;
-}
-
-impl<'tcx> FreeRegionRelations<'tcx> for FreeRegionMap<'tcx> {
- fn sub_free_regions(&self, r_a: Region<'tcx>, r_b: Region<'tcx>) -> bool {
- assert!(is_free_or_static(r_a) && is_free_or_static(r_b));
- if let ty::ReStatic = r_b {
- true // `'a <= 'static` is just always true, and not stored in the relation explicitly
- } else {
- r_a == r_b || self.relation.contains(&r_a, &r_b)
- }
- }
-}
-
-fn is_free(r: Region<'_>) -> bool {
- match *r {
- ty::ReEarlyBound(_) | ty::ReFree(_) => true,
- _ => false,
- }
-}
-
-fn is_free_or_static(r: Region<'_>) -> bool {
- match *r {
- ty::ReStatic => true,
- _ => is_free(r),
- }
-}
-
-impl<'a, 'tcx> Lift<'tcx> for FreeRegionMap<'a> {
- type Lifted = FreeRegionMap<'tcx>;
- fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option<FreeRegionMap<'tcx>> {
- self.relation.maybe_map(|&fr| tcx.lift(&fr)).map(|relation| FreeRegionMap { relation })
- }
-}
//! Various code related to computing outlives relations.
pub mod env;
-pub mod free_region_map;
pub mod obligations;
pub mod verify;
use crate::traits;
use crate::ty::subst::{InternalSubsts, Subst};
use crate::ty::{self, Ty, TyCtxt};
-use crate::util::captures::Captures;
+use rustc_data_structures::captures::Captures;
use rustc_hir::def_id::DefId;
/// The `TypeOutlives` struct has the job of "lowering" a `T: 'a`
#![feature(bool_to_option)]
#![feature(box_patterns)]
#![feature(box_syntax)]
-#![feature(const_fn)]
#![feature(const_transmute)]
#![feature(core_intrinsics)]
#![feature(drain_filter)]
#![feature(thread_local)]
#![feature(trace_macros)]
#![feature(trusted_len)]
+#![feature(vec_remove_item)]
#![feature(stmt_expr_attributes)]
#![feature(integer_atomics)]
#![feature(test)]
#[macro_use]
extern crate log;
#[macro_use]
-extern crate syntax;
-#[macro_use]
extern crate smallvec;
#[cfg(test)]
pub mod util {
pub mod bug;
- pub mod captures;
pub mod common;
}
--- /dev/null
+use std::cmp;
+
+use crate::ich::StableHashingContext;
+use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
+use rustc_errors::{pluralize, Applicability, DiagnosticBuilder, DiagnosticId};
+use rustc_hir::HirId;
+pub use rustc_session::lint::{builtin, Level, Lint, LintId, LintPass};
+use rustc_session::{DiagnosticMessageId, Session};
+use rustc_span::hygiene::MacroKind;
+use rustc_span::source_map::{DesugaringKind, ExpnKind, MultiSpan};
+use rustc_span::{Span, Symbol};
+
+/// How a lint level was set.
+#[derive(Clone, Copy, PartialEq, Eq, HashStable)]
+pub enum LintSource {
+ /// Lint is at the default level as declared
+ /// in rustc or a plugin.
+ Default,
+
+ /// Lint level was set by an attribute.
+ Node(Symbol, Span, Option<Symbol> /* RFC 2383 reason */),
+
+ /// Lint level was set by a command-line flag.
+ CommandLine(Symbol),
+}
+
+pub type LevelSource = (Level, LintSource);
+
+pub struct LintLevelSets {
+ pub list: Vec<LintSet>,
+ pub lint_cap: Level,
+}
+
+pub enum LintSet {
+ CommandLine {
+ // -A,-W,-D flags, a `Symbol` for the flag itself and `Level` for which
+ // flag.
+ specs: FxHashMap<LintId, LevelSource>,
+ },
+
+ Node {
+ specs: FxHashMap<LintId, LevelSource>,
+ parent: u32,
+ },
+}
+
+impl LintLevelSets {
+ pub fn new() -> Self {
+ LintLevelSets { list: Vec::new(), lint_cap: Level::Forbid }
+ }
+
+ pub fn get_lint_level(
+ &self,
+ lint: &'static Lint,
+ idx: u32,
+ aux: Option<&FxHashMap<LintId, LevelSource>>,
+ sess: &Session,
+ ) -> LevelSource {
+ let (level, mut src) = self.get_lint_id_level(LintId::of(lint), idx, aux);
+
+ // If `level` is none then we actually assume the default level for this
+ // lint.
+ let mut level = level.unwrap_or_else(|| lint.default_level(sess.edition()));
+
+ // If we're about to issue a warning, check at the last minute for any
+ // directives against the warnings "lint". If, for example, there's an
+ // `allow(warnings)` in scope then we want to respect that instead.
+ if level == Level::Warn {
+ let (warnings_level, warnings_src) =
+ self.get_lint_id_level(LintId::of(builtin::WARNINGS), idx, aux);
+ if let Some(configured_warning_level) = warnings_level {
+ if configured_warning_level != Level::Warn {
+ level = configured_warning_level;
+ src = warnings_src;
+ }
+ }
+ }
+
+ // Ensure that we never exceed the `--cap-lints` argument.
+ level = cmp::min(level, self.lint_cap);
+
+ if let Some(driver_level) = sess.driver_lint_caps.get(&LintId::of(lint)) {
+ // Ensure that we never exceed driver level.
+ level = cmp::min(*driver_level, level);
+ }
+
+ return (level, src);
+ }
+
+ pub fn get_lint_id_level(
+ &self,
+ id: LintId,
+ mut idx: u32,
+ aux: Option<&FxHashMap<LintId, LevelSource>>,
+ ) -> (Option<Level>, LintSource) {
+ if let Some(specs) = aux {
+ if let Some(&(level, src)) = specs.get(&id) {
+ return (Some(level), src);
+ }
+ }
+ loop {
+ match self.list[idx as usize] {
+ LintSet::CommandLine { ref specs } => {
+ if let Some(&(level, src)) = specs.get(&id) {
+ return (Some(level), src);
+ }
+ return (None, LintSource::Default);
+ }
+ LintSet::Node { ref specs, parent } => {
+ if let Some(&(level, src)) = specs.get(&id) {
+ return (Some(level), src);
+ }
+ idx = parent;
+ }
+ }
+ }
+ }
+}
+
+pub struct LintLevelMap {
+ pub sets: LintLevelSets,
+ pub id_to_set: FxHashMap<HirId, u32>,
+}
+
+impl LintLevelMap {
+ /// If the `id` was previously registered with `register_id` when building
+ /// this `LintLevelMap` this returns the corresponding lint level and source
+ /// of the lint level for the lint provided.
+ ///
+ /// If the `id` was not previously registered, returns `None`. If `None` is
+ /// returned then the parent of `id` should be acquired and this function
+ /// should be called again.
+ pub fn level_and_source(
+ &self,
+ lint: &'static Lint,
+ id: HirId,
+ session: &Session,
+ ) -> Option<LevelSource> {
+ self.id_to_set.get(&id).map(|idx| self.sets.get_lint_level(lint, *idx, None, session))
+ }
+}
+
+impl<'a> HashStable<StableHashingContext<'a>> for LintLevelMap {
+ #[inline]
+ fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
+ let LintLevelMap { ref sets, ref id_to_set } = *self;
+
+ id_to_set.hash_stable(hcx, hasher);
+
+ let LintLevelSets { ref list, lint_cap } = *sets;
+
+ lint_cap.hash_stable(hcx, hasher);
+
+ hcx.while_hashing_spans(true, |hcx| {
+ list.len().hash_stable(hcx, hasher);
+
+ // We are working under the assumption here that the list of
+ // lint-sets is built in a deterministic order.
+ for lint_set in list {
+ ::std::mem::discriminant(lint_set).hash_stable(hcx, hasher);
+
+ match *lint_set {
+ LintSet::CommandLine { ref specs } => {
+ specs.hash_stable(hcx, hasher);
+ }
+ LintSet::Node { ref specs, parent } => {
+ specs.hash_stable(hcx, hasher);
+ parent.hash_stable(hcx, hasher);
+ }
+ }
+ }
+ })
+ }
+}
+
+pub fn struct_lint_level<'a>(
+ sess: &'a Session,
+ lint: &'static Lint,
+ level: Level,
+ src: LintSource,
+ span: Option<MultiSpan>,
+ msg: &str,
+) -> DiagnosticBuilder<'a> {
+ let mut err = match (level, span) {
+ (Level::Allow, _) => return sess.diagnostic().struct_dummy(),
+ (Level::Warn, Some(span)) => sess.struct_span_warn(span, msg),
+ (Level::Warn, None) => sess.struct_warn(msg),
+ (Level::Deny, Some(span)) | (Level::Forbid, Some(span)) => sess.struct_span_err(span, msg),
+ (Level::Deny, None) | (Level::Forbid, None) => sess.struct_err(msg),
+ };
+
+ // Check for future incompatibility lints and issue a stronger warning.
+ let lint_id = LintId::of(lint);
+ let future_incompatible = lint.future_incompatible;
+
+ // If this code originates in a foreign macro, aka something that this crate
+ // did not itself author, then it's likely that there's nothing this crate
+ // can do about it. We probably want to skip the lint entirely.
+ if err.span.primary_spans().iter().any(|s| in_external_macro(sess, *s)) {
+ // Any suggestions made here are likely to be incorrect, so anything we
+ // emit shouldn't be automatically fixed by rustfix.
+ err.allow_suggestions(false);
+
+ // If this is a future incompatible lint it'll become a hard error, so
+ // we have to emit *something*. Also allow lints to whitelist themselves
+ // on a case-by-case basis for emission in a foreign macro.
+ if future_incompatible.is_none() && !lint.report_in_external_macro {
+ err.cancel();
+ // Don't continue further, since we don't want to have
+ // `diag_span_note_once` called for a diagnostic that isn't emitted.
+ return err;
+ }
+ }
+
+ let name = lint.name_lower();
+ match src {
+ LintSource::Default => {
+ sess.diag_note_once(
+ &mut err,
+ DiagnosticMessageId::from(lint),
+ &format!("`#[{}({})]` on by default", level.as_str(), name),
+ );
+ }
+ LintSource::CommandLine(lint_flag_val) => {
+ let flag = match level {
+ Level::Warn => "-W",
+ Level::Deny => "-D",
+ Level::Forbid => "-F",
+ Level::Allow => panic!(),
+ };
+ let hyphen_case_lint_name = name.replace("_", "-");
+ if lint_flag_val.as_str() == name {
+ sess.diag_note_once(
+ &mut err,
+ DiagnosticMessageId::from(lint),
+ &format!(
+ "requested on the command line with `{} {}`",
+ flag, hyphen_case_lint_name
+ ),
+ );
+ } else {
+ let hyphen_case_flag_val = lint_flag_val.as_str().replace("_", "-");
+ sess.diag_note_once(
+ &mut err,
+ DiagnosticMessageId::from(lint),
+ &format!(
+ "`{} {}` implied by `{} {}`",
+ flag, hyphen_case_lint_name, flag, hyphen_case_flag_val
+ ),
+ );
+ }
+ }
+ LintSource::Node(lint_attr_name, src, reason) => {
+ if let Some(rationale) = reason {
+ err.note(&rationale.as_str());
+ }
+ sess.diag_span_note_once(
+ &mut err,
+ DiagnosticMessageId::from(lint),
+ src,
+ "lint level defined here",
+ );
+ if lint_attr_name.as_str() != name {
+ let level_str = level.as_str();
+ sess.diag_note_once(
+ &mut err,
+ DiagnosticMessageId::from(lint),
+ &format!(
+ "`#[{}({})]` implied by `#[{}({})]`",
+ level_str, name, level_str, lint_attr_name
+ ),
+ );
+ }
+ }
+ }
+
+ err.code(DiagnosticId::Lint(name));
+
+ if let Some(future_incompatible) = future_incompatible {
+ const STANDARD_MESSAGE: &str = "this was previously accepted by the compiler but is being phased out; \
+ it will become a hard error";
+
+ let explanation = if lint_id == LintId::of(builtin::UNSTABLE_NAME_COLLISIONS) {
+ "once this method is added to the standard library, \
+ the ambiguity may cause an error or change in behavior!"
+ .to_owned()
+ } else if lint_id == LintId::of(builtin::MUTABLE_BORROW_RESERVATION_CONFLICT) {
+ "this borrowing pattern was not meant to be accepted, \
+ and may become a hard error in the future"
+ .to_owned()
+ } else if let Some(edition) = future_incompatible.edition {
+ format!("{} in the {} edition!", STANDARD_MESSAGE, edition)
+ } else {
+ format!("{} in a future release!", STANDARD_MESSAGE)
+ };
+ let citation = format!("for more information, see {}", future_incompatible.reference);
+ err.warn(&explanation);
+ err.note(&citation);
+ }
+
+ return err;
+}
+
+/// Returns whether `span` originates in a foreign crate's external macro.
+///
+/// This is used to test whether a lint should not even begin to figure out whether it should
+/// be reported on the current node.
+pub fn in_external_macro(sess: &Session, span: Span) -> bool {
+ let expn_data = span.ctxt().outer_expn_data();
+ match expn_data.kind {
+ ExpnKind::Root | ExpnKind::Desugaring(DesugaringKind::ForLoop) => false,
+ ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) => true, // well, it's "external"
+ ExpnKind::Macro(MacroKind::Bang, _) => {
+ if expn_data.def_site.is_dummy() {
+ // Dummy span for the `def_site` means it's an external macro.
+ return true;
+ }
+ match sess.source_map().span_to_snippet(expn_data.def_site) {
+ Ok(code) => !code.starts_with("macro_rules"),
+ // No snippet means external macro or compiler-builtin expansion.
+ Err(_) => true,
+ }
+ }
+ ExpnKind::Macro(..) => true, // definitely a plugin
+ }
+}
+
+pub fn add_elided_lifetime_in_path_suggestion(
+ sess: &Session,
+ db: &mut DiagnosticBuilder<'_>,
+ n: usize,
+ path_span: Span,
+ incl_angl_brckt: bool,
+ insertion_span: Span,
+ anon_lts: String,
+) {
+ let (replace_span, suggestion) = if incl_angl_brckt {
+ (insertion_span, anon_lts)
+ } else {
+ // When possible, prefer a suggestion that replaces the whole
+ // `Path<T>` expression with `Path<'_, T>`, rather than inserting `'_, `
+ // at a point (which makes for an ugly/confusing label)
+ if let Ok(snippet) = sess.source_map().span_to_snippet(path_span) {
+ // But our spans can get out of whack due to macros; if the place we think
+ // we want to insert `'_` isn't even within the path expression's span, we
+ // should bail out of making any suggestion rather than panicking on a
+ // subtract-with-overflow or string-slice-out-out-bounds (!)
+ // FIXME: can we do better?
+ if insertion_span.lo().0 < path_span.lo().0 {
+ return;
+ }
+ let insertion_index = (insertion_span.lo().0 - path_span.lo().0) as usize;
+ if insertion_index > snippet.len() {
+ return;
+ }
+ let (before, after) = snippet.split_at(insertion_index);
+ (path_span, format!("{}{}{}", before, anon_lts, after))
+ } else {
+ (insertion_span, anon_lts)
+ }
+ };
+ db.span_suggestion(
+ replace_span,
+ &format!("indicate the anonymous lifetime{}", pluralize!(n)),
+ suggestion,
+ Applicability::MachineApplicable,
+ );
+}
+++ /dev/null
-//! Some lints that are built in to the compiler.
-//!
-//! These are the built-in lints that are emitted direct in the main
-//! compiler code, rather than using their own custom pass. Those
-//! lints are all available in `rustc_lint::builtin`.
-
-use crate::lint::{FutureIncompatibleInfo, LateLintPass, LintArray, LintPass};
-use crate::middle::stability;
-use crate::session::Session;
-use errors::{pluralize, Applicability, DiagnosticBuilder};
-use rustc_session::declare_lint;
-use rustc_span::edition::Edition;
-use rustc_span::source_map::Span;
-use rustc_span::symbol::Symbol;
-use syntax::ast;
-use syntax::early_buffered_lints::{ILL_FORMED_ATTRIBUTE_INPUT, META_VARIABLE_MISUSE};
-
-declare_lint! {
- pub EXCEEDING_BITSHIFTS,
- Deny,
- "shift exceeds the type's number of bits"
-}
-
-declare_lint! {
- pub CONST_ERR,
- Deny,
- "constant evaluation detected erroneous expression",
- report_in_external_macro
-}
-
-declare_lint! {
- pub UNUSED_IMPORTS,
- Warn,
- "imports that are never used"
-}
-
-declare_lint! {
- pub UNUSED_EXTERN_CRATES,
- Allow,
- "extern crates that are never used"
-}
-
-declare_lint! {
- pub UNUSED_QUALIFICATIONS,
- Allow,
- "detects unnecessarily qualified names"
-}
-
-declare_lint! {
- pub UNKNOWN_LINTS,
- Warn,
- "unrecognized lint attribute"
-}
-
-declare_lint! {
- pub UNUSED_VARIABLES,
- Warn,
- "detect variables which are not used in any way"
-}
-
-declare_lint! {
- pub UNUSED_ASSIGNMENTS,
- Warn,
- "detect assignments that will never be read"
-}
-
-declare_lint! {
- pub DEAD_CODE,
- Warn,
- "detect unused, unexported items"
-}
-
-declare_lint! {
- pub UNUSED_ATTRIBUTES,
- Warn,
- "detects attributes that were not used by the compiler"
-}
-
-declare_lint! {
- pub UNREACHABLE_CODE,
- Warn,
- "detects unreachable code paths",
- report_in_external_macro
-}
-
-declare_lint! {
- pub UNREACHABLE_PATTERNS,
- Warn,
- "detects unreachable patterns"
-}
-
-declare_lint! {
- pub OVERLAPPING_PATTERNS,
- Warn,
- "detects overlapping patterns"
-}
-
-declare_lint! {
- pub UNUSED_MACROS,
- Warn,
- "detects macros that were not used"
-}
-
-declare_lint! {
- pub WARNINGS,
- Warn,
- "mass-change the level for lints which produce warnings"
-}
-
-declare_lint! {
- pub UNUSED_FEATURES,
- Warn,
- "unused features found in crate-level `#[feature]` directives"
-}
-
-declare_lint! {
- pub STABLE_FEATURES,
- Warn,
- "stable features found in `#[feature]` directive"
-}
-
-declare_lint! {
- pub UNKNOWN_CRATE_TYPES,
- Deny,
- "unknown crate type found in `#[crate_type]` directive"
-}
-
-declare_lint! {
- pub TRIVIAL_CASTS,
- Allow,
- "detects trivial casts which could be removed"
-}
-
-declare_lint! {
- pub TRIVIAL_NUMERIC_CASTS,
- Allow,
- "detects trivial casts of numeric types which could be removed"
-}
-
-declare_lint! {
- pub PRIVATE_IN_PUBLIC,
- Warn,
- "detect private items in public interfaces not caught by the old implementation",
- @future_incompatible = FutureIncompatibleInfo {
- reference: "issue #34537 <https://github.com/rust-lang/rust/issues/34537>",
- edition: None,
- };
-}
-
-declare_lint! {
- pub EXPORTED_PRIVATE_DEPENDENCIES,
- Warn,
- "public interface leaks type from a private dependency"
-}
-
-declare_lint! {
- pub PUB_USE_OF_PRIVATE_EXTERN_CRATE,
- Deny,
- "detect public re-exports of private extern crates",
- @future_incompatible = FutureIncompatibleInfo {
- reference: "issue #34537 <https://github.com/rust-lang/rust/issues/34537>",
- edition: None,
- };
-}
-
-declare_lint! {
- pub INVALID_TYPE_PARAM_DEFAULT,
- Deny,
- "type parameter default erroneously allowed in invalid location",
- @future_incompatible = FutureIncompatibleInfo {
- reference: "issue #36887 <https://github.com/rust-lang/rust/issues/36887>",
- edition: None,
- };
-}
-
-declare_lint! {
- pub RENAMED_AND_REMOVED_LINTS,
- Warn,
- "lints that have been renamed or removed"
-}
-
-declare_lint! {
- pub SAFE_PACKED_BORROWS,
- Warn,
- "safe borrows of fields of packed structs were was erroneously allowed",
- @future_incompatible = FutureIncompatibleInfo {
- reference: "issue #46043 <https://github.com/rust-lang/rust/issues/46043>",
- edition: None,
- };
-}
-
-declare_lint! {
- pub PATTERNS_IN_FNS_WITHOUT_BODY,
- Deny,
- "patterns in functions without body were erroneously allowed",
- @future_incompatible = FutureIncompatibleInfo {
- reference: "issue #35203 <https://github.com/rust-lang/rust/issues/35203>",
- edition: None,
- };
-}
-
-declare_lint! {
- pub MISSING_FRAGMENT_SPECIFIER,
- Deny,
- "detects missing fragment specifiers in unused `macro_rules!` patterns",
- @future_incompatible = FutureIncompatibleInfo {
- reference: "issue #40107 <https://github.com/rust-lang/rust/issues/40107>",
- edition: None,
- };
-}
-
-declare_lint! {
- pub LATE_BOUND_LIFETIME_ARGUMENTS,
- Warn,
- "detects generic lifetime arguments in path segments with late bound lifetime parameters",
- @future_incompatible = FutureIncompatibleInfo {
- reference: "issue #42868 <https://github.com/rust-lang/rust/issues/42868>",
- edition: None,
- };
-}
-
-declare_lint! {
- pub ORDER_DEPENDENT_TRAIT_OBJECTS,
- Deny,
- "trait-object types were treated as different depending on marker-trait order",
- @future_incompatible = FutureIncompatibleInfo {
- reference: "issue #56484 <https://github.com/rust-lang/rust/issues/56484>",
- edition: None,
- };
-}
-
-declare_lint! {
- pub DEPRECATED,
- Warn,
- "detects use of deprecated items",
- report_in_external_macro
-}
-
-declare_lint! {
- pub UNUSED_UNSAFE,
- Warn,
- "unnecessary use of an `unsafe` block"
-}
-
-declare_lint! {
- pub UNUSED_MUT,
- Warn,
- "detect mut variables which don't need to be mutable"
-}
-
-declare_lint! {
- pub UNCONDITIONAL_RECURSION,
- Warn,
- "functions that cannot return without calling themselves"
-}
-
-declare_lint! {
- pub SINGLE_USE_LIFETIMES,
- Allow,
- "detects lifetime parameters that are only used once"
-}
-
-declare_lint! {
- pub UNUSED_LIFETIMES,
- Allow,
- "detects lifetime parameters that are never used"
-}
-
-declare_lint! {
- pub TYVAR_BEHIND_RAW_POINTER,
- Warn,
- "raw pointer to an inference variable",
- @future_incompatible = FutureIncompatibleInfo {
- reference: "issue #46906 <https://github.com/rust-lang/rust/issues/46906>",
- edition: Some(Edition::Edition2018),
- };
-}
-
-declare_lint! {
- pub ELIDED_LIFETIMES_IN_PATHS,
- Allow,
- "hidden lifetime parameters in types are deprecated"
-}
-
-declare_lint! {
- pub BARE_TRAIT_OBJECTS,
- Warn,
- "suggest using `dyn Trait` for trait objects"
-}
-
-declare_lint! {
- pub ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE,
- Allow,
- "fully qualified paths that start with a module name \
- instead of `crate`, `self`, or an extern crate name",
- @future_incompatible = FutureIncompatibleInfo {
- reference: "issue #53130 <https://github.com/rust-lang/rust/issues/53130>",
- edition: Some(Edition::Edition2018),
- };
-}
-
-declare_lint! {
- pub ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
- Warn,
- "floating-point literals cannot be used in patterns",
- @future_incompatible = FutureIncompatibleInfo {
- reference: "issue #41620 <https://github.com/rust-lang/rust/issues/41620>",
- edition: None,
- };
-}
-
-declare_lint! {
- pub UNSTABLE_NAME_COLLISIONS,
- Warn,
- "detects name collision with an existing but unstable method",
- @future_incompatible = FutureIncompatibleInfo {
- reference: "issue #48919 <https://github.com/rust-lang/rust/issues/48919>",
- edition: None,
- // Note: this item represents future incompatibility of all unstable functions in the
- // standard library, and thus should never be removed or changed to an error.
- };
-}
-
-declare_lint! {
- pub IRREFUTABLE_LET_PATTERNS,
- Warn,
- "detects irrefutable patterns in if-let and while-let statements"
-}
-
-declare_lint! {
- pub UNUSED_LABELS,
- Warn,
- "detects labels that are never used"
-}
-
-declare_lint! {
- pub INTRA_DOC_LINK_RESOLUTION_FAILURE,
- Warn,
- "failures in resolving intra-doc link targets"
-}
-
-declare_lint! {
- pub MISSING_DOC_CODE_EXAMPLES,
- Allow,
- "detects publicly-exported items without code samples in their documentation"
-}
-
-declare_lint! {
- pub PRIVATE_DOC_TESTS,
- Allow,
- "detects code samples in docs of private items not documented by rustdoc"
-}
-
-declare_lint! {
- pub WHERE_CLAUSES_OBJECT_SAFETY,
- Warn,
- "checks the object safety of where clauses",
- @future_incompatible = FutureIncompatibleInfo {
- reference: "issue #51443 <https://github.com/rust-lang/rust/issues/51443>",
- edition: None,
- };
-}
-
-declare_lint! {
- pub PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
- Warn,
- "detects proc macro derives using inaccessible names from parent modules",
- @future_incompatible = FutureIncompatibleInfo {
- reference: "issue #50504 <https://github.com/rust-lang/rust/issues/50504>",
- edition: None,
- };
-}
-
-declare_lint! {
- pub MACRO_USE_EXTERN_CRATE,
- Allow,
- "the `#[macro_use]` attribute is now deprecated in favor of using macros \
- via the module system"
-}
-
-declare_lint! {
- pub MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS,
- Deny,
- "macro-expanded `macro_export` macros from the current crate \
- cannot be referred to by absolute paths",
- @future_incompatible = FutureIncompatibleInfo {
- reference: "issue #52234 <https://github.com/rust-lang/rust/issues/52234>",
- edition: None,
- };
-}
-
-declare_lint! {
- pub EXPLICIT_OUTLIVES_REQUIREMENTS,
- Allow,
- "outlives requirements can be inferred"
-}
-
-declare_lint! {
- pub INDIRECT_STRUCTURAL_MATCH,
- // defaulting to allow until rust-lang/rust#62614 is fixed.
- Allow,
- "pattern with const indirectly referencing non-`#[structural_match]` type",
- @future_incompatible = FutureIncompatibleInfo {
- reference: "issue #62411 <https://github.com/rust-lang/rust/issues/62411>",
- edition: None,
- };
-}
-
-declare_lint! {
- pub DEPRECATED_IN_FUTURE,
- Allow,
- "detects use of items that will be deprecated in a future version",
- report_in_external_macro
-}
-
-declare_lint! {
- pub AMBIGUOUS_ASSOCIATED_ITEMS,
- Deny,
- "ambiguous associated items",
- @future_incompatible = FutureIncompatibleInfo {
- reference: "issue #57644 <https://github.com/rust-lang/rust/issues/57644>",
- edition: None,
- };
-}
-
-declare_lint! {
- pub MUTABLE_BORROW_RESERVATION_CONFLICT,
- Warn,
- "reservation of a two-phased borrow conflicts with other shared borrows",
- @future_incompatible = FutureIncompatibleInfo {
- reference: "issue #59159 <https://github.com/rust-lang/rust/issues/59159>",
- edition: None,
- };
-}
-
-declare_lint! {
- pub SOFT_UNSTABLE,
- Deny,
- "a feature gate that doesn't break dependent crates",
- @future_incompatible = FutureIncompatibleInfo {
- reference: "issue #64266 <https://github.com/rust-lang/rust/issues/64266>",
- edition: None,
- };
-}
-
-declare_lint_pass! {
- /// Does nothing as a lint pass, but registers some `Lint`s
- /// that are used by other parts of the compiler.
- HardwiredLints => [
- ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
- EXCEEDING_BITSHIFTS,
- UNUSED_IMPORTS,
- UNUSED_EXTERN_CRATES,
- UNUSED_QUALIFICATIONS,
- UNKNOWN_LINTS,
- UNUSED_VARIABLES,
- UNUSED_ASSIGNMENTS,
- DEAD_CODE,
- UNREACHABLE_CODE,
- UNREACHABLE_PATTERNS,
- OVERLAPPING_PATTERNS,
- UNUSED_MACROS,
- WARNINGS,
- UNUSED_FEATURES,
- STABLE_FEATURES,
- UNKNOWN_CRATE_TYPES,
- TRIVIAL_CASTS,
- TRIVIAL_NUMERIC_CASTS,
- PRIVATE_IN_PUBLIC,
- EXPORTED_PRIVATE_DEPENDENCIES,
- PUB_USE_OF_PRIVATE_EXTERN_CRATE,
- INVALID_TYPE_PARAM_DEFAULT,
- CONST_ERR,
- RENAMED_AND_REMOVED_LINTS,
- SAFE_PACKED_BORROWS,
- PATTERNS_IN_FNS_WITHOUT_BODY,
- MISSING_FRAGMENT_SPECIFIER,
- LATE_BOUND_LIFETIME_ARGUMENTS,
- ORDER_DEPENDENT_TRAIT_OBJECTS,
- DEPRECATED,
- UNUSED_UNSAFE,
- UNUSED_MUT,
- UNCONDITIONAL_RECURSION,
- SINGLE_USE_LIFETIMES,
- UNUSED_LIFETIMES,
- UNUSED_LABELS,
- TYVAR_BEHIND_RAW_POINTER,
- ELIDED_LIFETIMES_IN_PATHS,
- BARE_TRAIT_OBJECTS,
- ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE,
- UNSTABLE_NAME_COLLISIONS,
- IRREFUTABLE_LET_PATTERNS,
- INTRA_DOC_LINK_RESOLUTION_FAILURE,
- MISSING_DOC_CODE_EXAMPLES,
- PRIVATE_DOC_TESTS,
- WHERE_CLAUSES_OBJECT_SAFETY,
- PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
- MACRO_USE_EXTERN_CRATE,
- MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS,
- ILL_FORMED_ATTRIBUTE_INPUT,
- META_VARIABLE_MISUSE,
- DEPRECATED_IN_FUTURE,
- AMBIGUOUS_ASSOCIATED_ITEMS,
- MUTABLE_BORROW_RESERVATION_CONFLICT,
- INDIRECT_STRUCTURAL_MATCH,
- SOFT_UNSTABLE,
- ]
-}
-
-// this could be a closure, but then implementing derive traits
-// becomes hacky (and it gets allocated)
-#[derive(PartialEq, RustcEncodable, RustcDecodable, Debug)]
-pub enum BuiltinLintDiagnostics {
- Normal,
- BareTraitObject(Span, /* is_global */ bool),
- AbsPathWithModule(Span),
- ProcMacroDeriveResolutionFallback(Span),
- MacroExpandedMacroExportsAccessedByAbsolutePaths(Span),
- ElidedLifetimesInPaths(usize, Span, bool, Span, String),
- UnknownCrateTypes(Span, String, String),
- UnusedImports(String, Vec<(Span, String)>),
- RedundantImport(Vec<(Span, bool)>, ast::Ident),
- DeprecatedMacro(Option<Symbol>, Span),
-}
-
-pub fn add_elided_lifetime_in_path_suggestion(
- sess: &Session,
- db: &mut DiagnosticBuilder<'_>,
- n: usize,
- path_span: Span,
- incl_angl_brckt: bool,
- insertion_span: Span,
- anon_lts: String,
-) {
- let (replace_span, suggestion) = if incl_angl_brckt {
- (insertion_span, anon_lts)
- } else {
- // When possible, prefer a suggestion that replaces the whole
- // `Path<T>` expression with `Path<'_, T>`, rather than inserting `'_, `
- // at a point (which makes for an ugly/confusing label)
- if let Ok(snippet) = sess.source_map().span_to_snippet(path_span) {
- // But our spans can get out of whack due to macros; if the place we think
- // we want to insert `'_` isn't even within the path expression's span, we
- // should bail out of making any suggestion rather than panicking on a
- // subtract-with-overflow or string-slice-out-out-bounds (!)
- // FIXME: can we do better?
- if insertion_span.lo().0 < path_span.lo().0 {
- return;
- }
- let insertion_index = (insertion_span.lo().0 - path_span.lo().0) as usize;
- if insertion_index > snippet.len() {
- return;
- }
- let (before, after) = snippet.split_at(insertion_index);
- (path_span, format!("{}{}{}", before, anon_lts, after))
- } else {
- (insertion_span, anon_lts)
- }
- };
- db.span_suggestion(
- replace_span,
- &format!("indicate the anonymous lifetime{}", pluralize!(n)),
- suggestion,
- Applicability::MachineApplicable,
- );
-}
-
-impl BuiltinLintDiagnostics {
- pub fn run(self, sess: &Session, db: &mut DiagnosticBuilder<'_>) {
- match self {
- BuiltinLintDiagnostics::Normal => (),
- BuiltinLintDiagnostics::BareTraitObject(span, is_global) => {
- let (sugg, app) = match sess.source_map().span_to_snippet(span) {
- Ok(ref s) if is_global => {
- (format!("dyn ({})", s), Applicability::MachineApplicable)
- }
- Ok(s) => (format!("dyn {}", s), Applicability::MachineApplicable),
- Err(_) => ("dyn <type>".to_string(), Applicability::HasPlaceholders),
- };
- db.span_suggestion(span, "use `dyn`", sugg, app);
- }
- BuiltinLintDiagnostics::AbsPathWithModule(span) => {
- let (sugg, app) = match sess.source_map().span_to_snippet(span) {
- Ok(ref s) => {
- // FIXME(Manishearth) ideally the emitting code
- // can tell us whether or not this is global
- let opt_colon = if s.trim_start().starts_with("::") { "" } else { "::" };
-
- (format!("crate{}{}", opt_colon, s), Applicability::MachineApplicable)
- }
- Err(_) => ("crate::<path>".to_string(), Applicability::HasPlaceholders),
- };
- db.span_suggestion(span, "use `crate`", sugg, app);
- }
- BuiltinLintDiagnostics::ProcMacroDeriveResolutionFallback(span) => {
- db.span_label(
- span,
- "names from parent modules are not \
- accessible without an explicit import",
- );
- }
- BuiltinLintDiagnostics::MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def) => {
- db.span_note(span_def, "the macro is defined here");
- }
- BuiltinLintDiagnostics::ElidedLifetimesInPaths(
- n,
- path_span,
- incl_angl_brckt,
- insertion_span,
- anon_lts,
- ) => {
- add_elided_lifetime_in_path_suggestion(
- sess,
- db,
- n,
- path_span,
- incl_angl_brckt,
- insertion_span,
- anon_lts,
- );
- }
- BuiltinLintDiagnostics::UnknownCrateTypes(span, note, sugg) => {
- db.span_suggestion(span, ¬e, sugg, Applicability::MaybeIncorrect);
- }
- BuiltinLintDiagnostics::UnusedImports(message, replaces) => {
- if !replaces.is_empty() {
- db.tool_only_multipart_suggestion(
- &message,
- replaces,
- Applicability::MachineApplicable,
- );
- }
- }
- BuiltinLintDiagnostics::RedundantImport(spans, ident) => {
- for (span, is_imported) in spans {
- let introduced = if is_imported { "imported" } else { "defined" };
- db.span_label(
- span,
- format!("the item `{}` is already {} here", ident, introduced),
- );
- }
- }
- BuiltinLintDiagnostics::DeprecatedMacro(suggestion, span) => {
- stability::deprecation_suggestion(db, suggestion, span)
- }
- }
- }
-}
-
-impl<'a, 'tcx> LateLintPass<'a, 'tcx> for HardwiredLints {}
+++ /dev/null
-//! Implementation of lint checking.
-//!
-//! The lint checking is mostly consolidated into one pass which runs
-//! after all other analyses. Throughout compilation, lint warnings
-//! can be added via the `add_lint` method on the Session structure. This
-//! requires a span and an ID of the node that the lint is being added to. The
-//! lint isn't actually emitted at that time because it is unknown what the
-//! actual lint level at that location is.
-//!
-//! To actually emit lint warnings/errors, a separate pass is used.
-//! A context keeps track of the current state of all lint levels.
-//! Upon entering a node of the ast which can modify the lint settings, the
-//! previous lint state is pushed onto a stack and the ast is then recursed
-//! upon. As the ast is traversed, this keeps track of the current lint level
-//! for all lint attributes.
-
-use self::TargetLint::*;
-
-use crate::hir::map::{definitions::DisambiguatedDefPathData, DefPathData};
-use crate::lint::builtin::BuiltinLintDiagnostics;
-use crate::lint::levels::{LintLevelSets, LintLevelsBuilder};
-use crate::lint::{EarlyLintPassObject, LateLintPassObject};
-use crate::lint::{FutureIncompatibleInfo, Level, Lint, LintBuffer, LintId};
-use crate::middle::privacy::AccessLevels;
-use crate::session::Session;
-use crate::ty::layout::{LayoutError, LayoutOf, TyLayout};
-use crate::ty::{self, print::Printer, subst::GenericArg, Ty, TyCtxt};
-use errors::DiagnosticBuilder;
-use rustc_data_structures::fx::FxHashMap;
-use rustc_data_structures::sync;
-use rustc_hir as hir;
-use rustc_hir::def_id::{CrateNum, DefId};
-use rustc_span::{symbol::Symbol, MultiSpan, Span};
-use std::slice;
-use syntax::ast;
-use syntax::util::lev_distance::find_best_match_for_name;
-
-use rustc_error_codes::*;
-
-/// Information about the registered lints.
-///
-/// This is basically the subset of `Context` that we can
-/// build early in the compile pipeline.
-pub struct LintStore {
- /// Registered lints.
- lints: Vec<&'static Lint>,
-
- /// Constructor functions for each variety of lint pass.
- ///
- /// These should only be called once, but since we want to avoid locks or
- /// interior mutability, we don't enforce this (and lints should, in theory,
- /// be compatible with being constructed more than once, though not
- /// necessarily in a sane manner. This is safe though.)
- pub pre_expansion_passes: Vec<Box<dyn Fn() -> EarlyLintPassObject + sync::Send + sync::Sync>>,
- pub early_passes: Vec<Box<dyn Fn() -> EarlyLintPassObject + sync::Send + sync::Sync>>,
- pub late_passes: Vec<Box<dyn Fn() -> LateLintPassObject + sync::Send + sync::Sync>>,
- /// This is unique in that we construct them per-module, so not once.
- pub late_module_passes: Vec<Box<dyn Fn() -> LateLintPassObject + sync::Send + sync::Sync>>,
-
- /// Lints indexed by name.
- by_name: FxHashMap<String, TargetLint>,
-
- /// Map of registered lint groups to what lints they expand to.
- lint_groups: FxHashMap<&'static str, LintGroup>,
-}
-
-/// Lints that are buffered up early on in the `Session` before the
-/// `LintLevels` is calculated
-#[derive(PartialEq, Debug)]
-pub struct BufferedEarlyLint {
- pub lint_id: LintId,
- pub ast_id: ast::NodeId,
- pub span: MultiSpan,
- pub msg: String,
- pub diagnostic: BuiltinLintDiagnostics,
-}
-
-/// The target of the `by_name` map, which accounts for renaming/deprecation.
-enum TargetLint {
- /// A direct lint target
- Id(LintId),
-
- /// Temporary renaming, used for easing migration pain; see #16545
- Renamed(String, LintId),
-
- /// Lint with this name existed previously, but has been removed/deprecated.
- /// The string argument is the reason for removal.
- Removed(String),
-}
-
-pub enum FindLintError {
- NotFound,
- Removed,
-}
-
-struct LintAlias {
- name: &'static str,
- /// Whether deprecation warnings should be suppressed for this alias.
- silent: bool,
-}
-
-struct LintGroup {
- lint_ids: Vec<LintId>,
- from_plugin: bool,
- depr: Option<LintAlias>,
-}
-
-pub enum CheckLintNameResult<'a> {
- Ok(&'a [LintId]),
- /// Lint doesn't exist. Potentially contains a suggestion for a correct lint name.
- NoLint(Option<Symbol>),
- /// The lint is either renamed or removed. This is the warning
- /// message, and an optional new name (`None` if removed).
- Warning(String, Option<String>),
- /// The lint is from a tool. If the Option is None, then either
- /// the lint does not exist in the tool or the code was not
- /// compiled with the tool and therefore the lint was never
- /// added to the `LintStore`. Otherwise the `LintId` will be
- /// returned as if it where a rustc lint.
- Tool(Result<&'a [LintId], (Option<&'a [LintId]>, String)>),
-}
-
-impl LintStore {
- pub fn new() -> LintStore {
- LintStore {
- lints: vec![],
- pre_expansion_passes: vec![],
- early_passes: vec![],
- late_passes: vec![],
- late_module_passes: vec![],
- by_name: Default::default(),
- lint_groups: Default::default(),
- }
- }
-
- pub fn get_lints<'t>(&'t self) -> &'t [&'static Lint] {
- &self.lints
- }
-
- pub fn get_lint_groups<'t>(&'t self) -> Vec<(&'static str, Vec<LintId>, bool)> {
- self.lint_groups
- .iter()
- .filter(|(_, LintGroup { depr, .. })| {
- // Don't display deprecated lint groups.
- depr.is_none()
- })
- .map(|(k, LintGroup { lint_ids, from_plugin, .. })| {
- (*k, lint_ids.clone(), *from_plugin)
- })
- .collect()
- }
-
- pub fn register_early_pass(
- &mut self,
- pass: impl Fn() -> EarlyLintPassObject + 'static + sync::Send + sync::Sync,
- ) {
- self.early_passes.push(Box::new(pass));
- }
-
- pub fn register_pre_expansion_pass(
- &mut self,
- pass: impl Fn() -> EarlyLintPassObject + 'static + sync::Send + sync::Sync,
- ) {
- self.pre_expansion_passes.push(Box::new(pass));
- }
-
- pub fn register_late_pass(
- &mut self,
- pass: impl Fn() -> LateLintPassObject + 'static + sync::Send + sync::Sync,
- ) {
- self.late_passes.push(Box::new(pass));
- }
-
- pub fn register_late_mod_pass(
- &mut self,
- pass: impl Fn() -> LateLintPassObject + 'static + sync::Send + sync::Sync,
- ) {
- self.late_module_passes.push(Box::new(pass));
- }
-
- // Helper method for register_early/late_pass
- pub fn register_lints(&mut self, lints: &[&'static Lint]) {
- for lint in lints {
- self.lints.push(lint);
-
- let id = LintId::of(lint);
- if self.by_name.insert(lint.name_lower(), Id(id)).is_some() {
- bug!("duplicate specification of lint {}", lint.name_lower())
- }
-
- if let Some(FutureIncompatibleInfo { edition, .. }) = lint.future_incompatible {
- if let Some(edition) = edition {
- self.lint_groups
- .entry(edition.lint_name())
- .or_insert(LintGroup {
- lint_ids: vec![],
- from_plugin: lint.is_plugin,
- depr: None,
- })
- .lint_ids
- .push(id);
- }
-
- self.lint_groups
- .entry("future_incompatible")
- .or_insert(LintGroup {
- lint_ids: vec![],
- from_plugin: lint.is_plugin,
- depr: None,
- })
- .lint_ids
- .push(id);
- }
- }
- }
-
- pub fn register_group_alias(&mut self, lint_name: &'static str, alias: &'static str) {
- self.lint_groups.insert(
- alias,
- LintGroup {
- lint_ids: vec![],
- from_plugin: false,
- depr: Some(LintAlias { name: lint_name, silent: true }),
- },
- );
- }
-
- pub fn register_group(
- &mut self,
- from_plugin: bool,
- name: &'static str,
- deprecated_name: Option<&'static str>,
- to: Vec<LintId>,
- ) {
- let new = self
- .lint_groups
- .insert(name, LintGroup { lint_ids: to, from_plugin, depr: None })
- .is_none();
- if let Some(deprecated) = deprecated_name {
- self.lint_groups.insert(
- deprecated,
- LintGroup {
- lint_ids: vec![],
- from_plugin,
- depr: Some(LintAlias { name, silent: false }),
- },
- );
- }
-
- if !new {
- bug!("duplicate specification of lint group {}", name);
- }
- }
-
- pub fn register_renamed(&mut self, old_name: &str, new_name: &str) {
- let target = match self.by_name.get(new_name) {
- Some(&Id(lint_id)) => lint_id.clone(),
- _ => bug!("invalid lint renaming of {} to {}", old_name, new_name),
- };
- self.by_name.insert(old_name.to_string(), Renamed(new_name.to_string(), target));
- }
-
- pub fn register_removed(&mut self, name: &str, reason: &str) {
- self.by_name.insert(name.into(), Removed(reason.into()));
- }
-
- pub fn find_lints(&self, mut lint_name: &str) -> Result<Vec<LintId>, FindLintError> {
- match self.by_name.get(lint_name) {
- Some(&Id(lint_id)) => Ok(vec![lint_id]),
- Some(&Renamed(_, lint_id)) => Ok(vec![lint_id]),
- Some(&Removed(_)) => Err(FindLintError::Removed),
- None => loop {
- return match self.lint_groups.get(lint_name) {
- Some(LintGroup { lint_ids, depr, .. }) => {
- if let Some(LintAlias { name, .. }) = depr {
- lint_name = name;
- continue;
- }
- Ok(lint_ids.clone())
- }
- None => Err(FindLintError::Removed),
- };
- },
- }
- }
-
- /// Checks the validity of lint names derived from the command line
- pub fn check_lint_name_cmdline(&self, sess: &Session, lint_name: &str, level: Level) {
- let db = match self.check_lint_name(lint_name, None) {
- CheckLintNameResult::Ok(_) => None,
- CheckLintNameResult::Warning(ref msg, _) => Some(sess.struct_warn(msg)),
- CheckLintNameResult::NoLint(suggestion) => {
- let mut err = struct_err!(sess, E0602, "unknown lint: `{}`", lint_name);
-
- if let Some(suggestion) = suggestion {
- err.help(&format!("did you mean: `{}`", suggestion));
- }
-
- Some(err)
- }
- CheckLintNameResult::Tool(result) => match result {
- Err((Some(_), new_name)) => Some(sess.struct_warn(&format!(
- "lint name `{}` is deprecated \
- and does not have an effect anymore. \
- Use: {}",
- lint_name, new_name
- ))),
- _ => None,
- },
- };
-
- if let Some(mut db) = db {
- let msg = format!(
- "requested on the command line with `{} {}`",
- match level {
- Level::Allow => "-A",
- Level::Warn => "-W",
- Level::Deny => "-D",
- Level::Forbid => "-F",
- },
- lint_name
- );
- db.note(&msg);
- db.emit();
- }
- }
-
- /// Checks the name of a lint for its existence, and whether it was
- /// renamed or removed. Generates a DiagnosticBuilder containing a
- /// warning for renamed and removed lints. This is over both lint
- /// names from attributes and those passed on the command line. Since
- /// it emits non-fatal warnings and there are *two* lint passes that
- /// inspect attributes, this is only run from the late pass to avoid
- /// printing duplicate warnings.
- pub fn check_lint_name(
- &self,
- lint_name: &str,
- tool_name: Option<Symbol>,
- ) -> CheckLintNameResult<'_> {
- let complete_name = if let Some(tool_name) = tool_name {
- format!("{}::{}", tool_name, lint_name)
- } else {
- lint_name.to_string()
- };
- // If the lint was scoped with `tool::` check if the tool lint exists
- if let Some(_) = tool_name {
- match self.by_name.get(&complete_name) {
- None => match self.lint_groups.get(&*complete_name) {
- None => return CheckLintNameResult::Tool(Err((None, String::new()))),
- Some(LintGroup { lint_ids, .. }) => {
- return CheckLintNameResult::Tool(Ok(&lint_ids));
- }
- },
- Some(&Id(ref id)) => return CheckLintNameResult::Tool(Ok(slice::from_ref(id))),
- // If the lint was registered as removed or renamed by the lint tool, we don't need
- // to treat tool_lints and rustc lints different and can use the code below.
- _ => {}
- }
- }
- match self.by_name.get(&complete_name) {
- Some(&Renamed(ref new_name, _)) => CheckLintNameResult::Warning(
- format!("lint `{}` has been renamed to `{}`", complete_name, new_name),
- Some(new_name.to_owned()),
- ),
- Some(&Removed(ref reason)) => CheckLintNameResult::Warning(
- format!("lint `{}` has been removed: `{}`", complete_name, reason),
- None,
- ),
- None => match self.lint_groups.get(&*complete_name) {
- // If neither the lint, nor the lint group exists check if there is a `clippy::`
- // variant of this lint
- None => self.check_tool_name_for_backwards_compat(&complete_name, "clippy"),
- Some(LintGroup { lint_ids, depr, .. }) => {
- // Check if the lint group name is deprecated
- if let Some(LintAlias { name, silent }) = depr {
- let LintGroup { lint_ids, .. } = self.lint_groups.get(name).unwrap();
- return if *silent {
- CheckLintNameResult::Ok(&lint_ids)
- } else {
- CheckLintNameResult::Tool(Err((Some(&lint_ids), name.to_string())))
- };
- }
- CheckLintNameResult::Ok(&lint_ids)
- }
- },
- Some(&Id(ref id)) => CheckLintNameResult::Ok(slice::from_ref(id)),
- }
- }
-
- fn check_tool_name_for_backwards_compat(
- &self,
- lint_name: &str,
- tool_name: &str,
- ) -> CheckLintNameResult<'_> {
- let complete_name = format!("{}::{}", tool_name, lint_name);
- match self.by_name.get(&complete_name) {
- None => match self.lint_groups.get(&*complete_name) {
- // Now we are sure, that this lint exists nowhere
- None => {
- let symbols =
- self.by_name.keys().map(|name| Symbol::intern(&name)).collect::<Vec<_>>();
-
- let suggestion =
- find_best_match_for_name(symbols.iter(), &lint_name.to_lowercase(), None);
-
- CheckLintNameResult::NoLint(suggestion)
- }
- Some(LintGroup { lint_ids, depr, .. }) => {
- // Reaching this would be weird, but let's cover this case anyway
- if let Some(LintAlias { name, silent }) = depr {
- let LintGroup { lint_ids, .. } = self.lint_groups.get(name).unwrap();
- return if *silent {
- CheckLintNameResult::Tool(Err((Some(&lint_ids), complete_name)))
- } else {
- CheckLintNameResult::Tool(Err((Some(&lint_ids), name.to_string())))
- };
- }
- CheckLintNameResult::Tool(Err((Some(&lint_ids), complete_name)))
- }
- },
- Some(&Id(ref id)) => {
- CheckLintNameResult::Tool(Err((Some(slice::from_ref(id)), complete_name)))
- }
- _ => CheckLintNameResult::NoLint(None),
- }
- }
-}
-
-/// Context for lint checking after type checking.
-pub struct LateContext<'a, 'tcx> {
- /// Type context we're checking in.
- pub tcx: TyCtxt<'tcx>,
-
- /// Side-tables for the body we are in.
- // FIXME: Make this lazy to avoid running the TypeckTables query?
- pub tables: &'a ty::TypeckTables<'tcx>,
-
- /// Parameter environment for the item we are in.
- pub param_env: ty::ParamEnv<'tcx>,
-
- /// Items accessible from the crate being checked.
- pub access_levels: &'a AccessLevels,
-
- /// The store of registered lints and the lint levels.
- pub lint_store: &'tcx LintStore,
-
- pub last_node_with_lint_attrs: hir::HirId,
-
- /// Generic type parameters in scope for the item we are in.
- pub generics: Option<&'tcx hir::Generics<'tcx>>,
-
- /// We are only looking at one module
- pub only_module: bool,
-}
-
-/// Context for lint checking of the AST, after expansion, before lowering to
-/// HIR.
-pub struct EarlyContext<'a> {
- /// Type context we're checking in.
- pub sess: &'a Session,
-
- /// The crate being checked.
- pub krate: &'a ast::Crate,
-
- pub builder: LintLevelsBuilder<'a>,
-
- /// The store of registered lints and the lint levels.
- pub lint_store: &'a LintStore,
-
- pub buffered: LintBuffer,
-}
-
-pub trait LintPassObject: Sized {}
-
-impl LintPassObject for EarlyLintPassObject {}
-
-impl LintPassObject for LateLintPassObject {}
-
-pub trait LintContext: Sized {
- type PassObject: LintPassObject;
-
- fn sess(&self) -> &Session;
- fn lints(&self) -> &LintStore;
-
- fn lookup_and_emit<S: Into<MultiSpan>>(&self, lint: &'static Lint, span: Option<S>, msg: &str) {
- self.lookup(lint, span, msg).emit();
- }
-
- fn lookup_and_emit_with_diagnostics<S: Into<MultiSpan>>(
- &self,
- lint: &'static Lint,
- span: Option<S>,
- msg: &str,
- diagnostic: BuiltinLintDiagnostics,
- ) {
- let mut db = self.lookup(lint, span, msg);
- diagnostic.run(self.sess(), &mut db);
- db.emit();
- }
-
- fn lookup<S: Into<MultiSpan>>(
- &self,
- lint: &'static Lint,
- span: Option<S>,
- msg: &str,
- ) -> DiagnosticBuilder<'_>;
-
- /// Emit a lint at the appropriate level, for a particular span.
- fn span_lint<S: Into<MultiSpan>>(&self, lint: &'static Lint, span: S, msg: &str) {
- self.lookup_and_emit(lint, Some(span), msg);
- }
-
- fn struct_span_lint<S: Into<MultiSpan>>(
- &self,
- lint: &'static Lint,
- span: S,
- msg: &str,
- ) -> DiagnosticBuilder<'_> {
- self.lookup(lint, Some(span), msg)
- }
-
- /// Emit a lint and note at the appropriate level, for a particular span.
- fn span_lint_note(
- &self,
- lint: &'static Lint,
- span: Span,
- msg: &str,
- note_span: Span,
- note: &str,
- ) {
- let mut err = self.lookup(lint, Some(span), msg);
- if note_span == span {
- err.note(note);
- } else {
- err.span_note(note_span, note);
- }
- err.emit();
- }
-
- /// Emit a lint and help at the appropriate level, for a particular span.
- fn span_lint_help(&self, lint: &'static Lint, span: Span, msg: &str, help: &str) {
- let mut err = self.lookup(lint, Some(span), msg);
- self.span_lint(lint, span, msg);
- err.span_help(span, help);
- err.emit();
- }
-
- /// Emit a lint at the appropriate level, with no associated span.
- fn lint(&self, lint: &'static Lint, msg: &str) {
- self.lookup_and_emit(lint, None as Option<Span>, msg);
- }
-}
-
-impl<'a> EarlyContext<'a> {
- pub fn new(
- sess: &'a Session,
- lint_store: &'a LintStore,
- krate: &'a ast::Crate,
- buffered: LintBuffer,
- warn_about_weird_lints: bool,
- ) -> EarlyContext<'a> {
- EarlyContext {
- sess,
- krate,
- lint_store,
- builder: LintLevelSets::builder(sess, warn_about_weird_lints, lint_store),
- buffered,
- }
- }
-}
-
-impl LintContext for LateContext<'_, '_> {
- type PassObject = LateLintPassObject;
-
- /// Gets the overall compiler `Session` object.
- fn sess(&self) -> &Session {
- &self.tcx.sess
- }
-
- fn lints(&self) -> &LintStore {
- &*self.lint_store
- }
-
- fn lookup<S: Into<MultiSpan>>(
- &self,
- lint: &'static Lint,
- span: Option<S>,
- msg: &str,
- ) -> DiagnosticBuilder<'_> {
- let hir_id = self.last_node_with_lint_attrs;
-
- match span {
- Some(s) => self.tcx.struct_span_lint_hir(lint, hir_id, s, msg),
- None => self.tcx.struct_lint_node(lint, hir_id, msg),
- }
- }
-}
-
-impl LintContext for EarlyContext<'_> {
- type PassObject = EarlyLintPassObject;
-
- /// Gets the overall compiler `Session` object.
- fn sess(&self) -> &Session {
- &self.sess
- }
-
- fn lints(&self) -> &LintStore {
- &*self.lint_store
- }
-
- fn lookup<S: Into<MultiSpan>>(
- &self,
- lint: &'static Lint,
- span: Option<S>,
- msg: &str,
- ) -> DiagnosticBuilder<'_> {
- self.builder.struct_lint(lint, span.map(|s| s.into()), msg)
- }
-}
-
-impl<'a, 'tcx> LateContext<'a, 'tcx> {
- pub fn current_lint_root(&self) -> hir::HirId {
- self.last_node_with_lint_attrs
- }
-
- /// Check if a `DefId`'s path matches the given absolute type path usage.
- ///
- /// Anonymous scopes such as `extern` imports are matched with `kw::Invalid`;
- /// inherent `impl` blocks are matched with the name of the type.
- ///
- /// # Examples
- ///
- /// ```rust,ignore (no context or def id available)
- /// if cx.match_def_path(def_id, &[sym::core, sym::option, sym::Option]) {
- /// // The given `def_id` is that of an `Option` type
- /// }
- /// ```
- pub fn match_def_path(&self, def_id: DefId, path: &[Symbol]) -> bool {
- let names = self.get_def_path(def_id);
-
- names.len() == path.len() && names.into_iter().zip(path.iter()).all(|(a, &b)| a == b)
- }
-
- /// Gets the absolute path of `def_id` as a vector of `Symbol`.
- ///
- /// # Examples
- ///
- /// ```rust,ignore (no context or def id available)
- /// let def_path = cx.get_def_path(def_id);
- /// if let &[sym::core, sym::option, sym::Option] = &def_path[..] {
- /// // The given `def_id` is that of an `Option` type
- /// }
- /// ```
- pub fn get_def_path(&self, def_id: DefId) -> Vec<Symbol> {
- pub struct AbsolutePathPrinter<'tcx> {
- pub tcx: TyCtxt<'tcx>,
- }
-
- impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> {
- type Error = !;
-
- type Path = Vec<Symbol>;
- type Region = ();
- type Type = ();
- type DynExistential = ();
- type Const = ();
-
- fn tcx(&self) -> TyCtxt<'tcx> {
- self.tcx
- }
-
- fn print_region(self, _region: ty::Region<'_>) -> Result<Self::Region, Self::Error> {
- Ok(())
- }
-
- fn print_type(self, _ty: Ty<'tcx>) -> Result<Self::Type, Self::Error> {
- Ok(())
- }
-
- fn print_dyn_existential(
- self,
- _predicates: &'tcx ty::List<ty::ExistentialPredicate<'tcx>>,
- ) -> Result<Self::DynExistential, Self::Error> {
- Ok(())
- }
-
- fn print_const(self, _ct: &'tcx ty::Const<'tcx>) -> Result<Self::Const, Self::Error> {
- Ok(())
- }
-
- fn path_crate(self, cnum: CrateNum) -> Result<Self::Path, Self::Error> {
- Ok(vec![self.tcx.original_crate_name(cnum)])
- }
-
- fn path_qualified(
- self,
- self_ty: Ty<'tcx>,
- trait_ref: Option<ty::TraitRef<'tcx>>,
- ) -> Result<Self::Path, Self::Error> {
- if trait_ref.is_none() {
- if let ty::Adt(def, substs) = self_ty.kind {
- return self.print_def_path(def.did, substs);
- }
- }
-
- // This shouldn't ever be needed, but just in case:
- Ok(vec![match trait_ref {
- Some(trait_ref) => Symbol::intern(&format!("{:?}", trait_ref)),
- None => Symbol::intern(&format!("<{}>", self_ty)),
- }])
- }
-
- fn path_append_impl(
- self,
- print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
- _disambiguated_data: &DisambiguatedDefPathData,
- self_ty: Ty<'tcx>,
- trait_ref: Option<ty::TraitRef<'tcx>>,
- ) -> Result<Self::Path, Self::Error> {
- let mut path = print_prefix(self)?;
-
- // This shouldn't ever be needed, but just in case:
- path.push(match trait_ref {
- Some(trait_ref) => Symbol::intern(&format!(
- "<impl {} for {}>",
- trait_ref.print_only_trait_path(),
- self_ty
- )),
- None => Symbol::intern(&format!("<impl {}>", self_ty)),
- });
-
- Ok(path)
- }
-
- fn path_append(
- self,
- print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
- disambiguated_data: &DisambiguatedDefPathData,
- ) -> Result<Self::Path, Self::Error> {
- let mut path = print_prefix(self)?;
-
- // Skip `::{{constructor}}` on tuple/unit structs.
- match disambiguated_data.data {
- DefPathData::Ctor => return Ok(path),
- _ => {}
- }
-
- path.push(disambiguated_data.data.as_symbol());
- Ok(path)
- }
-
- fn path_generic_args(
- self,
- print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
- _args: &[GenericArg<'tcx>],
- ) -> Result<Self::Path, Self::Error> {
- print_prefix(self)
- }
- }
-
- AbsolutePathPrinter { tcx: self.tcx }.print_def_path(def_id, &[]).unwrap()
- }
-}
-
-impl<'a, 'tcx> LayoutOf for LateContext<'a, 'tcx> {
- type Ty = Ty<'tcx>;
- type TyLayout = Result<TyLayout<'tcx>, LayoutError<'tcx>>;
-
- fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyLayout {
- self.tcx.layout_of(self.param_env.and(ty))
- }
-}
+++ /dev/null
-//! Some lints that are only useful in the compiler or crates that use compiler internals, such as
-//! Clippy.
-
-use crate::lint::{
- EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintArray, LintContext, LintPass,
-};
-use errors::Applicability;
-use rustc_data_structures::fx::FxHashMap;
-use rustc_hir::{GenericArg, HirId, MutTy, Mutability, Path, PathSegment, QPath, Ty, TyKind};
-use rustc_session::declare_tool_lint;
-use rustc_span::symbol::{sym, Symbol};
-use syntax::ast::{Ident, Item, ItemKind};
-
-declare_tool_lint! {
- pub rustc::DEFAULT_HASH_TYPES,
- Allow,
- "forbid HashMap and HashSet and suggest the FxHash* variants"
-}
-
-pub struct DefaultHashTypes {
- map: FxHashMap<Symbol, Symbol>,
-}
-
-impl DefaultHashTypes {
- // we are allowed to use `HashMap` and `HashSet` as identifiers for implementing the lint itself
- #[allow(rustc::default_hash_types)]
- pub fn new() -> Self {
- let mut map = FxHashMap::default();
- map.insert(sym::HashMap, sym::FxHashMap);
- map.insert(sym::HashSet, sym::FxHashSet);
- Self { map }
- }
-}
-
-impl_lint_pass!(DefaultHashTypes => [DEFAULT_HASH_TYPES]);
-
-impl EarlyLintPass for DefaultHashTypes {
- fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: Ident) {
- if let Some(replace) = self.map.get(&ident.name) {
- let msg = format!("Prefer {} over {}, it has better performance", replace, ident);
- let mut db = cx.struct_span_lint(DEFAULT_HASH_TYPES, ident.span, &msg);
- db.span_suggestion(
- ident.span,
- "use",
- replace.to_string(),
- Applicability::MaybeIncorrect, // FxHashMap, ... needs another import
- );
- db.note(&format!("a `use rustc_data_structures::fx::{}` may be necessary", replace))
- .emit();
- }
- }
-}
-
-declare_tool_lint! {
- pub rustc::USAGE_OF_TY_TYKIND,
- Allow,
- "usage of `ty::TyKind` outside of the `ty::sty` module"
-}
-
-declare_tool_lint! {
- pub rustc::TY_PASS_BY_REFERENCE,
- Allow,
- "passing `Ty` or `TyCtxt` by reference"
-}
-
-declare_tool_lint! {
- pub rustc::USAGE_OF_QUALIFIED_TY,
- Allow,
- "using `ty::{Ty,TyCtxt}` instead of importing it"
-}
-
-declare_lint_pass!(TyTyKind => [
- USAGE_OF_TY_TYKIND,
- TY_PASS_BY_REFERENCE,
- USAGE_OF_QUALIFIED_TY,
-]);
-
-impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TyTyKind {
- fn check_path(&mut self, cx: &LateContext<'_, '_>, path: &'tcx Path<'tcx>, _: HirId) {
- let segments = path.segments.iter().rev().skip(1).rev();
-
- if let Some(last) = segments.last() {
- let span = path.span.with_hi(last.ident.span.hi());
- if lint_ty_kind_usage(cx, last) {
- cx.struct_span_lint(USAGE_OF_TY_TYKIND, span, "usage of `ty::TyKind::<kind>`")
- .span_suggestion(
- span,
- "try using ty::<kind> directly",
- "ty".to_string(),
- Applicability::MaybeIncorrect, // ty maybe needs an import
- )
- .emit();
- }
- }
- }
-
- fn check_ty(&mut self, cx: &LateContext<'_, '_>, ty: &'tcx Ty<'tcx>) {
- match &ty.kind {
- TyKind::Path(qpath) => {
- if let QPath::Resolved(_, path) = qpath {
- if let Some(last) = path.segments.iter().last() {
- if lint_ty_kind_usage(cx, last) {
- cx.struct_span_lint(
- USAGE_OF_TY_TYKIND,
- path.span,
- "usage of `ty::TyKind`",
- )
- .help("try using `Ty` instead")
- .emit();
- } else {
- if ty.span.from_expansion() {
- return;
- }
- if let Some(t) = is_ty_or_ty_ctxt(cx, ty) {
- if path.segments.len() > 1 {
- cx.struct_span_lint(
- USAGE_OF_QUALIFIED_TY,
- path.span,
- &format!("usage of qualified `ty::{}`", t),
- )
- .span_suggestion(
- path.span,
- "try using it unqualified",
- t,
- // The import probably needs to be changed
- Applicability::MaybeIncorrect,
- )
- .emit();
- }
- }
- }
- }
- }
- }
- TyKind::Rptr(_, MutTy { ty: inner_ty, mutbl: Mutability::Not }) => {
- if let Some(impl_did) = cx.tcx.impl_of_method(ty.hir_id.owner_def_id()) {
- if cx.tcx.impl_trait_ref(impl_did).is_some() {
- return;
- }
- }
- if let Some(t) = is_ty_or_ty_ctxt(cx, &inner_ty) {
- cx.struct_span_lint(
- TY_PASS_BY_REFERENCE,
- ty.span,
- &format!("passing `{}` by reference", t),
- )
- .span_suggestion(
- ty.span,
- "try passing by value",
- t,
- // Changing type of function argument
- Applicability::MaybeIncorrect,
- )
- .emit();
- }
- }
- _ => {}
- }
- }
-}
-
-fn lint_ty_kind_usage(cx: &LateContext<'_, '_>, segment: &PathSegment<'_>) -> bool {
- if let Some(res) = segment.res {
- if let Some(did) = res.opt_def_id() {
- return cx.tcx.is_diagnostic_item(sym::TyKind, did);
- }
- }
-
- false
-}
-
-fn is_ty_or_ty_ctxt(cx: &LateContext<'_, '_>, ty: &Ty<'_>) -> Option<String> {
- match &ty.kind {
- TyKind::Path(qpath) => {
- if let QPath::Resolved(_, path) = qpath {
- let did = path.res.opt_def_id()?;
- if cx.tcx.is_diagnostic_item(sym::Ty, did) {
- return Some(format!("Ty{}", gen_args(path.segments.last().unwrap())));
- } else if cx.tcx.is_diagnostic_item(sym::TyCtxt, did) {
- return Some(format!("TyCtxt{}", gen_args(path.segments.last().unwrap())));
- }
- }
- }
- _ => {}
- }
-
- None
-}
-
-fn gen_args(segment: &PathSegment<'_>) -> String {
- if let Some(args) = &segment.args {
- let lifetimes = args
- .args
- .iter()
- .filter_map(|arg| {
- if let GenericArg::Lifetime(lt) = arg {
- Some(lt.name.ident().to_string())
- } else {
- None
- }
- })
- .collect::<Vec<_>>();
-
- if !lifetimes.is_empty() {
- return format!("<{}>", lifetimes.join(", "));
- }
- }
-
- String::new()
-}
-
-declare_tool_lint! {
- pub rustc::LINT_PASS_IMPL_WITHOUT_MACRO,
- Allow,
- "`impl LintPass` without the `declare_lint_pass!` or `impl_lint_pass!` macros"
-}
-
-declare_lint_pass!(LintPassImpl => [LINT_PASS_IMPL_WITHOUT_MACRO]);
-
-impl EarlyLintPass for LintPassImpl {
- fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
- if let ItemKind::Impl(_, _, _, _, Some(lint_pass), _, _) = &item.kind {
- if let Some(last) = lint_pass.path.segments.last() {
- if last.ident.name == sym::LintPass {
- let expn_data = lint_pass.path.span.ctxt().outer_expn_data();
- let call_site = expn_data.call_site;
- if expn_data.kind.descr() != sym::impl_lint_pass
- && call_site.ctxt().outer_expn_data().kind.descr() != sym::declare_lint_pass
- {
- cx.struct_span_lint(
- LINT_PASS_IMPL_WITHOUT_MACRO,
- lint_pass.path.span,
- "implementing `LintPass` by hand",
- )
- .help("try using `declare_lint_pass!` or `impl_lint_pass!` instead")
- .emit();
- }
- }
- }
- }
- }
-}
+++ /dev/null
-use std::cmp;
-
-use crate::ich::StableHashingContext;
-use crate::lint::builtin;
-use crate::lint::context::{CheckLintNameResult, LintStore};
-use crate::lint::{self, Level, Lint, LintId, LintSource};
-use crate::session::Session;
-use errors::{Applicability, DiagnosticBuilder};
-use rustc_data_structures::fx::FxHashMap;
-use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
-use rustc_hir::HirId;
-use rustc_span::source_map::MultiSpan;
-use rustc_span::symbol::{sym, Symbol};
-use syntax::ast;
-use syntax::attr;
-use syntax::feature_gate;
-use syntax::print::pprust;
-
-use rustc_error_codes::*;
-
-pub struct LintLevelSets {
- list: Vec<LintSet>,
- lint_cap: Level,
-}
-
-enum LintSet {
- CommandLine {
- // -A,-W,-D flags, a `Symbol` for the flag itself and `Level` for which
- // flag.
- specs: FxHashMap<LintId, (Level, LintSource)>,
- },
-
- Node {
- specs: FxHashMap<LintId, (Level, LintSource)>,
- parent: u32,
- },
-}
-
-impl LintLevelSets {
- pub fn new(sess: &Session, lint_store: &LintStore) -> LintLevelSets {
- let mut me = LintLevelSets { list: Vec::new(), lint_cap: Level::Forbid };
- me.process_command_line(sess, lint_store);
- return me;
- }
-
- pub fn builder<'a>(
- sess: &'a Session,
- warn_about_weird_lints: bool,
- store: &LintStore,
- ) -> LintLevelsBuilder<'a> {
- LintLevelsBuilder::new(sess, warn_about_weird_lints, LintLevelSets::new(sess, store))
- }
-
- fn process_command_line(&mut self, sess: &Session, store: &LintStore) {
- let mut specs = FxHashMap::default();
- self.lint_cap = sess.opts.lint_cap.unwrap_or(Level::Forbid);
-
- for &(ref lint_name, level) in &sess.opts.lint_opts {
- store.check_lint_name_cmdline(sess, &lint_name, level);
-
- // If the cap is less than this specified level, e.g., if we've got
- // `--cap-lints allow` but we've also got `-D foo` then we ignore
- // this specification as the lint cap will set it to allow anyway.
- let level = cmp::min(level, self.lint_cap);
-
- let lint_flag_val = Symbol::intern(lint_name);
- let ids = match store.find_lints(&lint_name) {
- Ok(ids) => ids,
- Err(_) => continue, // errors handled in check_lint_name_cmdline above
- };
- for id in ids {
- let src = LintSource::CommandLine(lint_flag_val);
- specs.insert(id, (level, src));
- }
- }
-
- self.list.push(LintSet::CommandLine { specs: specs });
- }
-
- fn get_lint_level(
- &self,
- lint: &'static Lint,
- idx: u32,
- aux: Option<&FxHashMap<LintId, (Level, LintSource)>>,
- sess: &Session,
- ) -> (Level, LintSource) {
- let (level, mut src) = self.get_lint_id_level(LintId::of(lint), idx, aux);
-
- // If `level` is none then we actually assume the default level for this
- // lint.
- let mut level = level.unwrap_or_else(|| lint.default_level(sess.edition()));
-
- // If we're about to issue a warning, check at the last minute for any
- // directives against the warnings "lint". If, for example, there's an
- // `allow(warnings)` in scope then we want to respect that instead.
- if level == Level::Warn {
- let (warnings_level, warnings_src) =
- self.get_lint_id_level(LintId::of(lint::builtin::WARNINGS), idx, aux);
- if let Some(configured_warning_level) = warnings_level {
- if configured_warning_level != Level::Warn {
- level = configured_warning_level;
- src = warnings_src;
- }
- }
- }
-
- // Ensure that we never exceed the `--cap-lints` argument.
- level = cmp::min(level, self.lint_cap);
-
- if let Some(driver_level) = sess.driver_lint_caps.get(&LintId::of(lint)) {
- // Ensure that we never exceed driver level.
- level = cmp::min(*driver_level, level);
- }
-
- return (level, src);
- }
-
- fn get_lint_id_level(
- &self,
- id: LintId,
- mut idx: u32,
- aux: Option<&FxHashMap<LintId, (Level, LintSource)>>,
- ) -> (Option<Level>, LintSource) {
- if let Some(specs) = aux {
- if let Some(&(level, src)) = specs.get(&id) {
- return (Some(level), src);
- }
- }
- loop {
- match self.list[idx as usize] {
- LintSet::CommandLine { ref specs } => {
- if let Some(&(level, src)) = specs.get(&id) {
- return (Some(level), src);
- }
- return (None, LintSource::Default);
- }
- LintSet::Node { ref specs, parent } => {
- if let Some(&(level, src)) = specs.get(&id) {
- return (Some(level), src);
- }
- idx = parent;
- }
- }
- }
- }
-}
-
-pub struct LintLevelsBuilder<'a> {
- sess: &'a Session,
- sets: LintLevelSets,
- id_to_set: FxHashMap<HirId, u32>,
- cur: u32,
- warn_about_weird_lints: bool,
-}
-
-pub struct BuilderPush {
- prev: u32,
- pub changed: bool,
-}
-
-impl<'a> LintLevelsBuilder<'a> {
- pub fn new(
- sess: &'a Session,
- warn_about_weird_lints: bool,
- sets: LintLevelSets,
- ) -> LintLevelsBuilder<'a> {
- assert_eq!(sets.list.len(), 1);
- LintLevelsBuilder {
- sess,
- sets,
- cur: 0,
- id_to_set: Default::default(),
- warn_about_weird_lints,
- }
- }
-
- /// Pushes a list of AST lint attributes onto this context.
- ///
- /// This function will return a `BuilderPush` object which should be passed
- /// to `pop` when this scope for the attributes provided is exited.
- ///
- /// This function will perform a number of tasks:
- ///
- /// * It'll validate all lint-related attributes in `attrs`
- /// * It'll mark all lint-related attributes as used
- /// * Lint levels will be updated based on the attributes provided
- /// * Lint attributes are validated, e.g., a #[forbid] can't be switched to
- /// #[allow]
- ///
- /// Don't forget to call `pop`!
- pub fn push(&mut self, attrs: &[ast::Attribute], store: &LintStore) -> BuilderPush {
- let mut specs = FxHashMap::default();
- let sess = self.sess;
- let bad_attr = |span| struct_span_err!(sess, span, E0452, "malformed lint attribute input");
- for attr in attrs {
- let level = match Level::from_symbol(attr.name_or_empty()) {
- None => continue,
- Some(lvl) => lvl,
- };
-
- let meta = unwrap_or!(attr.meta(), continue);
- attr::mark_used(attr);
-
- let mut metas = unwrap_or!(meta.meta_item_list(), continue);
-
- if metas.is_empty() {
- // FIXME (#55112): issue unused-attributes lint for `#[level()]`
- continue;
- }
-
- // Before processing the lint names, look for a reason (RFC 2383)
- // at the end.
- let mut reason = None;
- let tail_li = &metas[metas.len() - 1];
- if let Some(item) = tail_li.meta_item() {
- match item.kind {
- ast::MetaItemKind::Word => {} // actual lint names handled later
- ast::MetaItemKind::NameValue(ref name_value) => {
- if item.path == sym::reason {
- // found reason, reslice meta list to exclude it
- metas = &metas[0..metas.len() - 1];
- // FIXME (#55112): issue unused-attributes lint if we thereby
- // don't have any lint names (`#[level(reason = "foo")]`)
- if let ast::LitKind::Str(rationale, _) = name_value.kind {
- if !self.sess.features_untracked().lint_reasons {
- feature_gate::feature_err(
- &self.sess.parse_sess,
- sym::lint_reasons,
- item.span,
- "lint reasons are experimental",
- )
- .emit();
- }
- reason = Some(rationale);
- } else {
- bad_attr(name_value.span)
- .span_label(name_value.span, "reason must be a string literal")
- .emit();
- }
- } else {
- bad_attr(item.span)
- .span_label(item.span, "bad attribute argument")
- .emit();
- }
- }
- ast::MetaItemKind::List(_) => {
- bad_attr(item.span).span_label(item.span, "bad attribute argument").emit();
- }
- }
- }
-
- for li in metas {
- let meta_item = match li.meta_item() {
- Some(meta_item) if meta_item.is_word() => meta_item,
- _ => {
- let sp = li.span();
- let mut err = bad_attr(sp);
- let mut add_label = true;
- if let Some(item) = li.meta_item() {
- if let ast::MetaItemKind::NameValue(_) = item.kind {
- if item.path == sym::reason {
- err.span_label(sp, "reason in lint attribute must come last");
- add_label = false;
- }
- }
- }
- if add_label {
- err.span_label(sp, "bad attribute argument");
- }
- err.emit();
- continue;
- }
- };
- let tool_name = if meta_item.path.segments.len() > 1 {
- let tool_ident = meta_item.path.segments[0].ident;
- if !attr::is_known_lint_tool(tool_ident) {
- span_err!(
- sess,
- tool_ident.span,
- E0710,
- "an unknown tool name found in scoped lint: `{}`",
- pprust::path_to_string(&meta_item.path),
- );
- continue;
- }
-
- Some(tool_ident.name)
- } else {
- None
- };
- let name = meta_item.path.segments.last().expect("empty lint name").ident.name;
- match store.check_lint_name(&name.as_str(), tool_name) {
- CheckLintNameResult::Ok(ids) => {
- let src = LintSource::Node(name, li.span(), reason);
- for id in ids {
- specs.insert(*id, (level, src));
- }
- }
-
- CheckLintNameResult::Tool(result) => {
- match result {
- Ok(ids) => {
- let complete_name = &format!("{}::{}", tool_name.unwrap(), name);
- let src = LintSource::Node(
- Symbol::intern(complete_name),
- li.span(),
- reason,
- );
- for id in ids {
- specs.insert(*id, (level, src));
- }
- }
- Err((Some(ids), new_lint_name)) => {
- let lint = builtin::RENAMED_AND_REMOVED_LINTS;
- let (lvl, src) =
- self.sets.get_lint_level(lint, self.cur, Some(&specs), &sess);
- let msg = format!(
- "lint name `{}` is deprecated \
- and may not have an effect in the future. \
- Also `cfg_attr(cargo-clippy)` won't be necessary anymore",
- name
- );
- lint::struct_lint_level(
- self.sess,
- lint,
- lvl,
- src,
- Some(li.span().into()),
- &msg,
- )
- .span_suggestion(
- li.span(),
- "change it to",
- new_lint_name.to_string(),
- Applicability::MachineApplicable,
- )
- .emit();
-
- let src = LintSource::Node(
- Symbol::intern(&new_lint_name),
- li.span(),
- reason,
- );
- for id in ids {
- specs.insert(*id, (level, src));
- }
- }
- Err((None, _)) => {
- // If Tool(Err(None, _)) is returned, then either the lint does not
- // exist in the tool or the code was not compiled with the tool and
- // therefore the lint was never added to the `LintStore`. To detect
- // this is the responsibility of the lint tool.
- }
- }
- }
-
- _ if !self.warn_about_weird_lints => {}
-
- CheckLintNameResult::Warning(msg, renamed) => {
- let lint = builtin::RENAMED_AND_REMOVED_LINTS;
- let (level, src) =
- self.sets.get_lint_level(lint, self.cur, Some(&specs), &sess);
- let mut err = lint::struct_lint_level(
- self.sess,
- lint,
- level,
- src,
- Some(li.span().into()),
- &msg,
- );
- if let Some(new_name) = renamed {
- err.span_suggestion(
- li.span(),
- "use the new name",
- new_name,
- Applicability::MachineApplicable,
- );
- }
- err.emit();
- }
- CheckLintNameResult::NoLint(suggestion) => {
- let lint = builtin::UNKNOWN_LINTS;
- let (level, src) =
- self.sets.get_lint_level(lint, self.cur, Some(&specs), self.sess);
- let msg = format!("unknown lint: `{}`", name);
- let mut db = lint::struct_lint_level(
- self.sess,
- lint,
- level,
- src,
- Some(li.span().into()),
- &msg,
- );
-
- if let Some(suggestion) = suggestion {
- db.span_suggestion(
- li.span(),
- "did you mean",
- suggestion.to_string(),
- Applicability::MachineApplicable,
- );
- }
-
- db.emit();
- }
- }
- }
- }
-
- for (id, &(level, ref src)) in specs.iter() {
- if level == Level::Forbid {
- continue;
- }
- let forbid_src = match self.sets.get_lint_id_level(*id, self.cur, None) {
- (Some(Level::Forbid), src) => src,
- _ => continue,
- };
- let forbidden_lint_name = match forbid_src {
- LintSource::Default => id.to_string(),
- LintSource::Node(name, _, _) => name.to_string(),
- LintSource::CommandLine(name) => name.to_string(),
- };
- let (lint_attr_name, lint_attr_span) = match *src {
- LintSource::Node(name, span, _) => (name, span),
- _ => continue,
- };
- let mut diag_builder = struct_span_err!(
- self.sess,
- lint_attr_span,
- E0453,
- "{}({}) overruled by outer forbid({})",
- level.as_str(),
- lint_attr_name,
- forbidden_lint_name
- );
- diag_builder.span_label(lint_attr_span, "overruled by previous forbid");
- match forbid_src {
- LintSource::Default => {}
- LintSource::Node(_, forbid_source_span, reason) => {
- diag_builder.span_label(forbid_source_span, "`forbid` level set here");
- if let Some(rationale) = reason {
- diag_builder.note(&rationale.as_str());
- }
- }
- LintSource::CommandLine(_) => {
- diag_builder.note("`forbid` lint level was set on command line");
- }
- }
- diag_builder.emit();
- // don't set a separate error for every lint in the group
- break;
- }
-
- let prev = self.cur;
- if specs.len() > 0 {
- self.cur = self.sets.list.len() as u32;
- self.sets.list.push(LintSet::Node { specs: specs, parent: prev });
- }
-
- BuilderPush { prev: prev, changed: prev != self.cur }
- }
-
- /// Called after `push` when the scope of a set of attributes are exited.
- pub fn pop(&mut self, push: BuilderPush) {
- self.cur = push.prev;
- }
-
- /// Used to emit a lint-related diagnostic based on the current state of
- /// this lint context.
- pub fn struct_lint(
- &self,
- lint: &'static Lint,
- span: Option<MultiSpan>,
- msg: &str,
- ) -> DiagnosticBuilder<'a> {
- let (level, src) = self.sets.get_lint_level(lint, self.cur, None, self.sess);
- lint::struct_lint_level(self.sess, lint, level, src, span, msg)
- }
-
- /// Registers the ID provided with the current set of lints stored in
- /// this context.
- pub fn register_id(&mut self, id: HirId) {
- self.id_to_set.insert(id, self.cur);
- }
-
- pub fn build(self) -> LintLevelSets {
- self.sets
- }
-
- pub fn build_map(self) -> LintLevelMap {
- LintLevelMap { sets: self.sets, id_to_set: self.id_to_set }
- }
-}
-
-pub struct LintLevelMap {
- sets: LintLevelSets,
- id_to_set: FxHashMap<HirId, u32>,
-}
-
-impl LintLevelMap {
- /// If the `id` was previously registered with `register_id` when building
- /// this `LintLevelMap` this returns the corresponding lint level and source
- /// of the lint level for the lint provided.
- ///
- /// If the `id` was not previously registered, returns `None`. If `None` is
- /// returned then the parent of `id` should be acquired and this function
- /// should be called again.
- pub fn level_and_source(
- &self,
- lint: &'static Lint,
- id: HirId,
- session: &Session,
- ) -> Option<(Level, LintSource)> {
- self.id_to_set.get(&id).map(|idx| self.sets.get_lint_level(lint, *idx, None, session))
- }
-}
-
-impl<'a> HashStable<StableHashingContext<'a>> for LintLevelMap {
- #[inline]
- fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
- let LintLevelMap { ref sets, ref id_to_set } = *self;
-
- id_to_set.hash_stable(hcx, hasher);
-
- let LintLevelSets { ref list, lint_cap } = *sets;
-
- lint_cap.hash_stable(hcx, hasher);
-
- hcx.while_hashing_spans(true, |hcx| {
- list.len().hash_stable(hcx, hasher);
-
- // We are working under the assumption here that the list of
- // lint-sets is built in a deterministic order.
- for lint_set in list {
- ::std::mem::discriminant(lint_set).hash_stable(hcx, hasher);
-
- match *lint_set {
- LintSet::CommandLine { ref specs } => {
- specs.hash_stable(hcx, hasher);
- }
- LintSet::Node { ref specs, parent } => {
- specs.hash_stable(hcx, hasher);
- parent.hash_stable(hcx, hasher);
- }
- }
- }
- })
- }
-}
+++ /dev/null
-//! Lints, aka compiler warnings.
-//!
-//! A 'lint' check is a kind of miscellaneous constraint that a user _might_
-//! want to enforce, but might reasonably want to permit as well, on a
-//! module-by-module basis. They contrast with static constraints enforced by
-//! other phases of the compiler, which are generally required to hold in order
-//! to compile the program at all.
-//!
-//! Most lints can be written as `LintPass` instances. These run after
-//! all other analyses. The `LintPass`es built into rustc are defined
-//! within `builtin.rs`, which has further comments on how to add such a lint.
-//! rustc can also load user-defined lint plugins via the plugin mechanism.
-//!
-//! Some of rustc's lints are defined elsewhere in the compiler and work by
-//! calling `add_lint()` on the overall `Session` object. This works when
-//! it happens before the main lint pass, which emits the lints stored by
-//! `add_lint()`. To emit lints after the main lint pass (from codegen, for
-//! example) requires more effort. See `emit_lint` and `GatherNodeLevels`
-//! in `context.rs`.
-
-pub use self::Level::*;
-pub use self::LintSource::*;
-
-use rustc_data_structures::sync;
-
-use crate::lint::builtin::BuiltinLintDiagnostics;
-use crate::ty::TyCtxt;
-use errors::{DiagnosticBuilder, DiagnosticId};
-use rustc_hir as hir;
-use rustc_session::node_id::NodeMap;
-use rustc_session::{DiagnosticMessageId, Session};
-use rustc_span::hygiene::MacroKind;
-use rustc_span::source_map::{DesugaringKind, ExpnKind, MultiSpan};
-use rustc_span::symbol::Symbol;
-use rustc_span::Span;
-use syntax::ast;
-
-pub use crate::lint::context::{
- BufferedEarlyLint, CheckLintNameResult, EarlyContext, LateContext, LintContext, LintStore,
-};
-
-pub use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintId};
-
-/// Declares a static `LintArray` and return it as an expression.
-#[macro_export]
-macro_rules! lint_array {
- ($( $lint:expr ),* ,) => { lint_array!( $($lint),* ) };
- ($( $lint:expr ),*) => {{
- vec![$($lint),*]
- }}
-}
-
-pub type LintArray = Vec<&'static Lint>;
-
-pub trait LintPass {
- fn name(&self) -> &'static str;
-}
-
-/// Implements `LintPass for $name` with the given list of `Lint` statics.
-#[macro_export]
-macro_rules! impl_lint_pass {
- ($name:ident => [$($lint:expr),* $(,)?]) => {
- impl LintPass for $name {
- fn name(&self) -> &'static str { stringify!($name) }
- }
- impl $name {
- pub fn get_lints() -> LintArray { $crate::lint_array!($($lint),*) }
- }
- };
-}
-
-/// Declares a type named `$name` which implements `LintPass`.
-/// To the right of `=>` a comma separated list of `Lint` statics is given.
-#[macro_export]
-macro_rules! declare_lint_pass {
- ($(#[$m:meta])* $name:ident => [$($lint:expr),* $(,)?]) => {
- $(#[$m])* #[derive(Copy, Clone)] pub struct $name;
- $crate::impl_lint_pass!($name => [$($lint),*]);
- };
-}
-
-#[macro_export]
-macro_rules! late_lint_methods {
- ($macro:path, $args:tt, [$hir:tt]) => (
- $macro!($args, [$hir], [
- fn check_param(a: &$hir hir::Param<$hir>);
- fn check_body(a: &$hir hir::Body<$hir>);
- fn check_body_post(a: &$hir hir::Body<$hir>);
- fn check_name(a: Span, b: ast::Name);
- fn check_crate(a: &$hir hir::Crate<$hir>);
- fn check_crate_post(a: &$hir hir::Crate<$hir>);
- fn check_mod(a: &$hir hir::Mod<$hir>, b: Span, c: hir::HirId);
- fn check_mod_post(a: &$hir hir::Mod<$hir>, b: Span, c: hir::HirId);
- fn check_foreign_item(a: &$hir hir::ForeignItem<$hir>);
- fn check_foreign_item_post(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_where_predicate(a: &$hir hir::WherePredicate<$hir>);
- fn check_poly_trait_ref(a: &$hir hir::PolyTraitRef<$hir>, b: hir::TraitBoundModifier);
- fn check_fn(
- a: $crate::hir::intravisit::FnKind<$hir>,
- b: &$hir hir::FnDecl<$hir>,
- c: &$hir hir::Body<$hir>,
- d: Span,
- e: hir::HirId);
- fn check_fn_post(
- a: $crate::hir::intravisit::FnKind<$hir>,
- b: &$hir hir::FnDecl<$hir>,
- c: &$hir hir::Body<$hir>,
- d: Span,
- e: hir::HirId
- );
- fn check_trait_item(a: &$hir hir::TraitItem<$hir>);
- fn check_trait_item_post(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_struct_def_post(a: &$hir hir::VariantData<$hir>);
- fn check_struct_field(a: &$hir hir::StructField<$hir>);
- fn check_variant(a: &$hir hir::Variant<$hir>);
- fn check_variant_post(a: &$hir hir::Variant<$hir>);
- fn check_lifetime(a: &$hir hir::Lifetime);
- fn check_path(a: &$hir hir::Path<$hir>, b: hir::HirId);
- fn check_attribute(a: &$hir 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]);
-
- /// Counterpart to `enter_lint_attrs`.
- fn exit_lint_attrs(a: &$hir [ast::Attribute]);
- ]);
- )
-}
-
-/// Trait for types providing lint checks.
-///
-/// Each `check` method checks a single syntax node, and should not
-/// invoke methods recursively (unlike `Visitor`). By default they
-/// do nothing.
-//
-// 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<'a, $hir>: LintPass {
- expand_lint_pass_methods!(&LateContext<'a, $hir>, [$($methods)*]);
- }
- )
-}
-
-late_lint_methods!(declare_late_lint_pass, [], ['tcx]);
-
-#[macro_export]
-macro_rules! expand_combined_late_lint_pass_method {
- ([$($passes:ident),*], $self: ident, $name: ident, $params:tt) => ({
- $($self.$passes.$name $params;)*
- })
-}
-
-#[macro_export]
-macro_rules! expand_combined_late_lint_pass_methods {
- ($passes:tt, [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => (
- $(fn $name(&mut self, context: &LateContext<'a, 'tcx>, $($param: $arg),*) {
- expand_combined_late_lint_pass_method!($passes, self, $name, (context, $($param),*));
- })*
- )
-}
-
-#[macro_export]
-macro_rules! declare_combined_late_lint_pass {
- ([$v:vis $name:ident, [$($passes:ident: $constructor:expr,)*]], [$hir:tt], $methods:tt) => (
- #[allow(non_snake_case)]
- $v struct $name {
- $($passes: $passes,)*
- }
-
- impl $name {
- $v fn new() -> Self {
- Self {
- $($passes: $constructor,)*
- }
- }
-
- $v fn get_lints() -> LintArray {
- let mut lints = Vec::new();
- $(lints.extend_from_slice(&$passes::get_lints());)*
- lints
- }
- }
-
- impl<'a, 'tcx> LateLintPass<'a, 'tcx> for $name {
- expand_combined_late_lint_pass_methods!([$($passes),*], $methods);
- }
-
- impl LintPass for $name {
- fn name(&self) -> &'static str {
- panic!()
- }
- }
- )
-}
-
-#[macro_export]
-macro_rules! early_lint_methods {
- ($macro:path, $args:tt) => (
- $macro!($args, [
- fn check_param(a: &ast::Param);
- fn check_ident(a: ast::Ident);
- fn check_crate(a: &ast::Crate);
- fn check_crate_post(a: &ast::Crate);
- fn check_mod(a: &ast::Mod, b: Span, c: ast::NodeId);
- fn check_mod_post(a: &ast::Mod, b: Span, c: ast::NodeId);
- fn check_foreign_item(a: &ast::ForeignItem);
- fn check_foreign_item_post(a: &ast::ForeignItem);
- fn check_item(a: &ast::Item);
- fn check_item_post(a: &ast::Item);
- fn check_local(a: &ast::Local);
- fn check_block(a: &ast::Block);
- fn check_block_post(a: &ast::Block);
- fn check_stmt(a: &ast::Stmt);
- fn check_arm(a: &ast::Arm);
- fn check_pat(a: &ast::Pat);
- fn check_pat_post(a: &ast::Pat);
- fn check_expr(a: &ast::Expr);
- fn check_expr_post(a: &ast::Expr);
- fn check_ty(a: &ast::Ty);
- fn check_generic_param(a: &ast::GenericParam);
- fn check_generics(a: &ast::Generics);
- fn check_where_predicate(a: &ast::WherePredicate);
- fn check_poly_trait_ref(a: &ast::PolyTraitRef,
- b: &ast::TraitBoundModifier);
- fn check_fn(a: syntax::visit::FnKind<'_>, b: &ast::FnDecl, c: Span, d_: ast::NodeId);
- fn check_fn_post(
- a: syntax::visit::FnKind<'_>,
- b: &ast::FnDecl,
- c: Span,
- d: ast::NodeId
- );
- fn check_trait_item(a: &ast::AssocItem);
- fn check_trait_item_post(a: &ast::AssocItem);
- fn check_impl_item(a: &ast::AssocItem);
- fn check_impl_item_post(a: &ast::AssocItem);
- fn check_struct_def(a: &ast::VariantData);
- fn check_struct_def_post(a: &ast::VariantData);
- fn check_struct_field(a: &ast::StructField);
- fn check_variant(a: &ast::Variant);
- fn check_variant_post(a: &ast::Variant);
- fn check_lifetime(a: &ast::Lifetime);
- fn check_path(a: &ast::Path, b: ast::NodeId);
- fn check_attribute(a: &ast::Attribute);
- fn check_mac_def(a: &ast::MacroDef, b: ast::NodeId);
- fn check_mac(a: &ast::Mac);
-
- /// 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: &[ast::Attribute]);
-
- /// Counterpart to `enter_lint_attrs`.
- fn exit_lint_attrs(a: &[ast::Attribute]);
- ]);
- )
-}
-
-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)*]) => (
- pub trait EarlyLintPass: LintPass {
- expand_early_lint_pass_methods!(&EarlyContext<'_>, [$($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;)*
- })
-}
-
-#[macro_export]
-macro_rules! expand_combined_early_lint_pass_methods {
- ($passes:tt, [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => (
- $(fn $name(&mut self, context: &EarlyContext<'_>, $($param: $arg),*) {
- expand_combined_early_lint_pass_method!($passes, self, $name, (context, $($param),*));
- })*
- )
-}
-
-#[macro_export]
-macro_rules! declare_combined_early_lint_pass {
- ([$v:vis $name:ident, [$($passes:ident: $constructor:expr,)*]], $methods:tt) => (
- #[allow(non_snake_case)]
- $v struct $name {
- $($passes: $passes,)*
- }
-
- impl $name {
- $v fn new() -> Self {
- Self {
- $($passes: $constructor,)*
- }
- }
-
- $v fn get_lints() -> LintArray {
- let mut lints = Vec::new();
- $(lints.extend_from_slice(&$passes::get_lints());)*
- lints
- }
- }
-
- impl EarlyLintPass for $name {
- expand_combined_early_lint_pass_methods!([$($passes),*], $methods);
- }
-
- impl LintPass for $name {
- fn name(&self) -> &'static str {
- panic!()
- }
- }
- )
-}
-
-/// A lint pass boxed up as a trait object.
-pub type EarlyLintPassObject = Box<dyn EarlyLintPass + sync::Send + sync::Sync + 'static>;
-pub type LateLintPassObject =
- Box<dyn for<'a, 'tcx> LateLintPass<'a, 'tcx> + sync::Send + sync::Sync + 'static>;
-
-/// How a lint level was set.
-#[derive(Clone, Copy, PartialEq, Eq, HashStable)]
-pub enum LintSource {
- /// Lint is at the default level as declared
- /// in rustc or a plugin.
- Default,
-
- /// Lint level was set by an attribute.
- Node(ast::Name, Span, Option<Symbol> /* RFC 2383 reason */),
-
- /// Lint level was set by a command-line flag.
- CommandLine(Symbol),
-}
-
-pub type LevelSource = (Level, LintSource);
-
-pub mod builtin;
-mod context;
-pub mod internal;
-mod levels;
-
-pub use self::levels::{LintLevelMap, LintLevelSets, LintLevelsBuilder};
-
-#[derive(Default)]
-pub struct LintBuffer {
- pub map: NodeMap<Vec<BufferedEarlyLint>>,
-}
-
-impl LintBuffer {
- pub fn add_lint(
- &mut self,
- lint: &'static Lint,
- id: ast::NodeId,
- sp: MultiSpan,
- msg: &str,
- diagnostic: BuiltinLintDiagnostics,
- ) {
- let early_lint = BufferedEarlyLint {
- lint_id: LintId::of(lint),
- ast_id: id,
- span: sp,
- msg: msg.to_string(),
- diagnostic,
- };
- let arr = self.map.entry(id).or_default();
- if !arr.contains(&early_lint) {
- arr.push(early_lint);
- }
- }
-
- pub fn take(&mut self, id: ast::NodeId) -> Vec<BufferedEarlyLint> {
- self.map.remove(&id).unwrap_or_default()
- }
-
- pub fn buffer_lint<S: Into<MultiSpan>>(
- &mut self,
- lint: &'static Lint,
- id: ast::NodeId,
- sp: S,
- msg: &str,
- ) {
- self.add_lint(lint, id, sp.into(), msg, BuiltinLintDiagnostics::Normal)
- }
-
- pub fn buffer_lint_with_diagnostic<S: Into<MultiSpan>>(
- &mut self,
- lint: &'static Lint,
- id: ast::NodeId,
- sp: S,
- msg: &str,
- diagnostic: BuiltinLintDiagnostics,
- ) {
- self.add_lint(lint, id, sp.into(), msg, diagnostic)
- }
-}
-
-pub fn struct_lint_level<'a>(
- sess: &'a Session,
- lint: &'static Lint,
- level: Level,
- src: LintSource,
- span: Option<MultiSpan>,
- msg: &str,
-) -> DiagnosticBuilder<'a> {
- let mut err = match (level, span) {
- (Level::Allow, _) => return sess.diagnostic().struct_dummy(),
- (Level::Warn, Some(span)) => sess.struct_span_warn(span, msg),
- (Level::Warn, None) => sess.struct_warn(msg),
- (Level::Deny, Some(span)) | (Level::Forbid, Some(span)) => sess.struct_span_err(span, msg),
- (Level::Deny, None) | (Level::Forbid, None) => sess.struct_err(msg),
- };
-
- // Check for future incompatibility lints and issue a stronger warning.
- let lint_id = LintId::of(lint);
- let future_incompatible = lint.future_incompatible;
-
- // If this code originates in a foreign macro, aka something that this crate
- // did not itself author, then it's likely that there's nothing this crate
- // can do about it. We probably want to skip the lint entirely.
- if err.span.primary_spans().iter().any(|s| in_external_macro(sess, *s)) {
- // Any suggestions made here are likely to be incorrect, so anything we
- // emit shouldn't be automatically fixed by rustfix.
- err.allow_suggestions(false);
-
- // If this is a future incompatible lint it'll become a hard error, so
- // we have to emit *something*. Also allow lints to whitelist themselves
- // on a case-by-case basis for emission in a foreign macro.
- if future_incompatible.is_none() && !lint.report_in_external_macro {
- err.cancel();
- // Don't continue further, since we don't want to have
- // `diag_span_note_once` called for a diagnostic that isn't emitted.
- return err;
- }
- }
-
- let name = lint.name_lower();
- match src {
- LintSource::Default => {
- sess.diag_note_once(
- &mut err,
- DiagnosticMessageId::from(lint),
- &format!("`#[{}({})]` on by default", level.as_str(), name),
- );
- }
- LintSource::CommandLine(lint_flag_val) => {
- let flag = match level {
- Level::Warn => "-W",
- Level::Deny => "-D",
- Level::Forbid => "-F",
- Level::Allow => panic!(),
- };
- let hyphen_case_lint_name = name.replace("_", "-");
- if lint_flag_val.as_str() == name {
- sess.diag_note_once(
- &mut err,
- DiagnosticMessageId::from(lint),
- &format!(
- "requested on the command line with `{} {}`",
- flag, hyphen_case_lint_name
- ),
- );
- } else {
- let hyphen_case_flag_val = lint_flag_val.as_str().replace("_", "-");
- sess.diag_note_once(
- &mut err,
- DiagnosticMessageId::from(lint),
- &format!(
- "`{} {}` implied by `{} {}`",
- flag, hyphen_case_lint_name, flag, hyphen_case_flag_val
- ),
- );
- }
- }
- LintSource::Node(lint_attr_name, src, reason) => {
- if let Some(rationale) = reason {
- err.note(&rationale.as_str());
- }
- sess.diag_span_note_once(
- &mut err,
- DiagnosticMessageId::from(lint),
- src,
- "lint level defined here",
- );
- if lint_attr_name.as_str() != name {
- let level_str = level.as_str();
- sess.diag_note_once(
- &mut err,
- DiagnosticMessageId::from(lint),
- &format!(
- "`#[{}({})]` implied by `#[{}({})]`",
- level_str, name, level_str, lint_attr_name
- ),
- );
- }
- }
- }
-
- err.code(DiagnosticId::Lint(name));
-
- if let Some(future_incompatible) = future_incompatible {
- const STANDARD_MESSAGE: &str = "this was previously accepted by the compiler but is being phased out; \
- it will become a hard error";
-
- let explanation = if lint_id == LintId::of(builtin::UNSTABLE_NAME_COLLISIONS) {
- "once this method is added to the standard library, \
- the ambiguity may cause an error or change in behavior!"
- .to_owned()
- } else if lint_id == LintId::of(builtin::MUTABLE_BORROW_RESERVATION_CONFLICT) {
- "this borrowing pattern was not meant to be accepted, \
- and may become a hard error in the future"
- .to_owned()
- } else if let Some(edition) = future_incompatible.edition {
- format!("{} in the {} edition!", STANDARD_MESSAGE, edition)
- } else {
- format!("{} in a future release!", STANDARD_MESSAGE)
- };
- let citation = format!("for more information, see {}", future_incompatible.reference);
- err.warn(&explanation);
- err.note(&citation);
- }
-
- return err;
-}
-
-pub fn maybe_lint_level_root(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
- let attrs = tcx.hir().attrs(id);
- attrs.iter().any(|attr| Level::from_symbol(attr.name_or_empty()).is_some())
-}
-
-/// Returns whether `span` originates in a foreign crate's external macro.
-///
-/// This is used to test whether a lint should not even begin to figure out whether it should
-/// be reported on the current node.
-pub fn in_external_macro(sess: &Session, span: Span) -> bool {
- let expn_data = span.ctxt().outer_expn_data();
- match expn_data.kind {
- ExpnKind::Root | ExpnKind::Desugaring(DesugaringKind::ForLoop) => false,
- ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) => true, // well, it's "external"
- ExpnKind::Macro(MacroKind::Bang, _) => {
- if expn_data.def_site.is_dummy() {
- // Dummy span for the `def_site` means it's an external macro.
- return true;
- }
- match sess.source_map().span_to_snippet(expn_data.def_site) {
- Ok(code) => !code.starts_with("macro_rules"),
- // No snippet means external macro or compiler-builtin expansion.
- Err(_) => true,
- }
- }
- ExpnKind::Macro(..) => true, // definitely a plugin
- }
-}
-
-/// Returns `true` if `span` originates in a derive-macro's expansion.
-pub fn in_derive_expansion(span: Span) -> bool {
- if let ExpnKind::Macro(MacroKind::Derive, _) = span.ctxt().outer_expn_data().kind {
- return true;
- }
- false
-}
use crate::hir::map as hir_map;
use crate::hir::map::definitions::{DefKey, DefPathTable};
use crate::session::search_paths::PathKind;
-use crate::session::{CrateDisambiguator, Session};
-use crate::ty::{self, TyCtxt};
+use crate::session::CrateDisambiguator;
+use crate::ty::TyCtxt;
use rustc_data_structures::svh::Svh;
use rustc_data_structures::sync::{self, MetadataRef};
fn crate_is_private_dep_untracked(&self, cnum: CrateNum) -> bool;
fn crate_disambiguator_untracked(&self, cnum: CrateNum) -> CrateDisambiguator;
fn crate_hash_untracked(&self, cnum: CrateNum) -> Svh;
- fn item_generics_cloned_untracked(&self, def: DefId, sess: &Session) -> ty::Generics;
// This is basically a 1-based range of ints, which is a little
// silly - I may fix that.
//! or explicit bounds. In that case, we track the bounds using the `TransitiveRelation` type,
//! and use that to decide when one free region outlives another, and so forth.
-use crate::infer::outlives::free_region_map::{FreeRegionMap, FreeRegionRelations};
use crate::middle::region;
+use crate::ty::free_region_map::{FreeRegionMap, FreeRegionRelations};
use crate::ty::{self, Region, TyCtxt};
use rustc_hir::def_id::DefId;
use crate::ty::{self, TyCtxt};
use rustc_data_structures::fx::FxHashMap;
+use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::itemlikevisit::ItemLikeVisitor;
span,
E0152,
"duplicate lang item found: `{}`.",
- name),
+ name
+ ),
None => {
match self.tcx.extern_crate(item_def_id) {
Some(ExternCrate {dependency_of, ..}) => {
},
};
if let Some(span) = self.tcx.hir().span_if_local(original_def_id) {
- span_note!(&mut err, span, "first defined here.");
+ err.span_note(span, "first defined here.");
} else {
match self.tcx.extern_crate(original_def_id) {
Some(ExternCrate {dependency_of, ..}) => {
pub use self::StabilityLevel::*;
-use crate::lint::builtin::BuiltinLintDiagnostics;
-use crate::lint::{self, in_derive_expansion, Lint};
use crate::session::{DiagnosticMessageId, Session};
use crate::ty::{self, TyCtxt};
-use errors::DiagnosticBuilder;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_feature::GateIssue;
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX};
use rustc_hir::{self, HirId};
+use rustc_session::lint::{self, BuiltinLintDiagnostics, Lint, LintBuffer};
use rustc_span::symbol::{sym, Symbol};
use rustc_span::{MultiSpan, Span};
use syntax::ast::CRATE_NODE_ID;
use syntax::attr::{self, ConstStability, Deprecation, RustcDeprecation, Stability};
-use syntax::errors::Applicability;
-use syntax::feature_gate::feature_err_issue;
+use syntax::sess::feature_err_issue;
use std::num::NonZeroU32;
}
pub fn early_report_deprecation(
- lint_buffer: &'a mut lint::LintBuffer,
+ lint_buffer: &'a mut LintBuffer,
message: &str,
suggestion: Option<Symbol>,
lint: &'static Lint,
span: Span,
) {
- if in_derive_expansion(span) {
+ if span.in_derive_expansion() {
return;
}
def_id: DefId,
hir_id: HirId,
) {
- if in_derive_expansion(span) {
+ if span.in_derive_expansion() {
return;
}
Unmarked,
}
-impl<'tcx> TyCtxt<'tcx> {
- // See issue #38412.
- fn skip_stability_check_due_to_privacy(self, mut def_id: DefId) -> bool {
- // Check if `def_id` is a trait method.
- match self.def_kind(def_id) {
- Some(DefKind::Method) | Some(DefKind::AssocTy) | Some(DefKind::AssocConst) => {
- if let ty::TraitContainer(trait_def_id) = self.associated_item(def_id).container {
- // Trait methods do not declare visibility (even
- // for visibility info in cstore). Use containing
- // trait instead, so methods of `pub` traits are
- // themselves considered `pub`.
- def_id = trait_def_id;
- }
+// See issue #38412.
+fn skip_stability_check_due_to_privacy(tcx: TyCtxt<'_>, mut def_id: DefId) -> bool {
+ // Check if `def_id` is a trait method.
+ match tcx.def_kind(def_id) {
+ Some(DefKind::Method) | Some(DefKind::AssocTy) | Some(DefKind::AssocConst) => {
+ if let ty::TraitContainer(trait_def_id) = tcx.associated_item(def_id).container {
+ // Trait methods do not declare visibility (even
+ // for visibility info in cstore). Use containing
+ // trait instead, so methods of `pub` traits are
+ // themselves considered `pub`.
+ def_id = trait_def_id;
}
- _ => {}
}
+ _ => {}
+ }
- let visibility = self.visibility(def_id);
+ let visibility = tcx.visibility(def_id);
- match visibility {
- // Must check stability for `pub` items.
- ty::Visibility::Public => false,
+ match visibility {
+ // Must check stability for `pub` items.
+ ty::Visibility::Public => false,
- // These are not visible outside crate; therefore
- // stability markers are irrelevant, if even present.
- ty::Visibility::Restricted(..) | ty::Visibility::Invisible => true,
- }
+ // These are not visible outside crate; therefore
+ // stability markers are irrelevant, if even present.
+ ty::Visibility::Restricted(..) | ty::Visibility::Invisible => true,
}
+}
+impl<'tcx> TyCtxt<'tcx> {
/// Evaluates the stability of an item.
///
/// Returns `EvalResult::Allow` if the item is stable, or unstable but the corresponding
}
// Issue #38412: private items lack stability markers.
- if self.skip_stability_check_due_to_privacy(def_id) {
+ if skip_stability_check_due_to_privacy(self, def_id) {
return EvalResult::Allow;
}
}
}
}
-}
-impl<'tcx> TyCtxt<'tcx> {
pub fn lookup_deprecation(self, id: DefId) -> Option<Deprecation> {
self.lookup_deprecation_entry(id).map(|depr| depr.attr)
}
use crate::middle::lang_items;
use crate::session::config;
-use crate::hir::intravisit;
-use crate::hir::intravisit::{NestedVisitorMap, Visitor};
+use crate::hir::map::Map;
use crate::ty::TyCtxt;
use rustc_data_structures::fx::FxHashSet;
+use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_span::symbol::{sym, Symbol};
use rustc_span::Span;
use rustc_target::spec::PanicStrategy;
self.items.missing.push(lang_items::$item);
}
} else)* {
- span_err!(self.tcx.sess, span, E0264,
- "unknown external lang item: `{}`",
- name);
+ struct_span_err!(
+ self.tcx.sess, span, E0264,
+ "unknown external lang item: `{}`",
+ name
+ )
+ .emit();
}
}
}
impl<'a, 'tcx, 'v> Visitor<'v> for Context<'a, 'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'v> {
+ type Map = Map<'v>;
+
+ fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, Map<'v>> {
NestedVisitorMap::None
}
use crate::ty::{self, layout, Ty};
use backtrace::Backtrace;
-use errors::DiagnosticBuilder;
use hir::GeneratorKind;
+use rustc_errors::{struct_span_err, DiagnosticBuilder};
use rustc_hir as hir;
use rustc_macros::HashStable;
use rustc_span::symbol::Symbol;
.next()
.unwrap_or(lint_root);
tcx.struct_span_lint_hir(
- crate::rustc::lint::builtin::CONST_ERR,
+ rustc_session::lint::builtin::CONST_ERR,
hir_id,
tcx.span,
message,
param_env: ty::ParamEnv<'tcx>,
def_id: DefId,
substs: SubstsRef<'tcx>,
+ promoted: Option<mir::Promoted>,
span: Option<Span>,
) -> ConstEvalResult<'tcx> {
let instance = ty::Instance::resolve(self, param_env, def_id, substs);
if let Some(instance) = instance {
- self.const_eval_instance(param_env, instance, span)
+ if let Some(promoted) = promoted {
+ self.const_eval_promoted(param_env, instance, promoted)
+ } else {
+ self.const_eval_instance(param_env, instance, span)
+ }
} else {
Err(ErrorHandled::TooGeneric)
}
/// Evaluate a promoted constant.
pub fn const_eval_promoted(
self,
+ param_env: ty::ParamEnv<'tcx>,
instance: ty::Instance<'tcx>,
promoted: mir::Promoted,
) -> ConstEvalResult<'tcx> {
let cid = GlobalId { instance, promoted: Some(promoted) };
- let param_env = ty::ParamEnv::reveal_all();
self.const_eval_validated(param_env.and(cid))
}
}
-// ignore-tidy-filelength
-
//! MIR datatypes and passes. See the [rustc guide] for more info.
//!
//! [rustc guide]: https://rust-lang.github.io/rustc-guide/mir/index.html
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::graph::dominators::Dominators;
use rustc_data_structures::graph::{self, GraphSuccessors};
-use rustc_data_structures::sync::Lrc;
use rustc_index::bit_set::BitMatrix;
use rustc_index::vec::{Idx, IndexVec};
use rustc_macros::HashStable;
use rustc_serialize::{Decodable, Encodable};
use rustc_span::symbol::Symbol;
use rustc_span::{Span, DUMMY_SP};
-use smallvec::SmallVec;
use std::borrow::Cow;
use std::fmt::{self, Debug, Display, Formatter, Write};
use std::ops::Index;
pub use syntax::ast::Mutability;
use syntax::ast::Name;
-pub use crate::mir::cache::{BodyAndCache, ReadOnlyBodyAndCache};
-pub use crate::mir::interpret::AssertMessage;
+pub use self::cache::{BodyAndCache, ReadOnlyBodyAndCache};
+pub use self::interpret::AssertMessage;
+pub use self::query::*;
pub use crate::read_only;
mod cache;
pub mod interpret;
pub mod mono;
+mod query;
pub mod tcx;
pub mod traversal;
pub mod visit;
/// A span representing this MIR, for error reporting.
pub span: Span,
+
+ /// The user may be writing e.g. &[(SOME_CELL, 42)][i].1 and this would get promoted, because
+ /// we'd statically know that no thing with interior mutability will ever be available to the
+ /// user without some serious unsafe code. Now this means that our promoted is actually
+ /// &[(SOME_CELL, 42)] and the MIR using it will do the &promoted[i].1 projection because the
+ /// index may be a runtime value. Such a promoted value is illegal because it has reachable
+ /// interior mutability. This flag just makes this situation very obvious where the previous
+ /// implementation without the flag hid this situation silently.
+ /// FIXME(oli-obk): rewrite the promoted during promotion to eliminate the cell components.
+ pub ignore_interior_mut_in_const_validation: bool,
}
impl<'tcx> Body<'tcx> {
spread_arg: None,
var_debug_info,
span,
+ ignore_interior_mut_in_const_validation: false,
control_flow_destroyed,
}
}
/// A path to a value; something that can be evaluated without
/// changing or disturbing program state.
-#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, HashStable)]
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, HashStable)]
pub struct Place<'tcx> {
- pub base: PlaceBase<'tcx>,
+ pub local: Local,
/// projection out of a place (access a field, deref a pointer, etc)
pub projection: &'tcx List<PlaceElem<'tcx>>,
impl<'tcx> rustc_serialize::UseSpecializedDecodable for Place<'tcx> {}
-#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable, HashStable)]
-pub enum PlaceBase<'tcx> {
- /// local variable
- Local(Local),
-
- /// static or static mut variable
- Static(Box<Static<'tcx>>),
-}
-
-/// We store the normalized type to avoid requiring normalization when reading MIR
-#[derive(
- Clone,
- Debug,
- PartialEq,
- Eq,
- PartialOrd,
- Ord,
- Hash,
- RustcEncodable,
- RustcDecodable,
- HashStable
-)]
-pub struct Static<'tcx> {
- pub ty: Ty<'tcx>,
- pub kind: StaticKind<'tcx>,
- /// The `DefId` of the item this static was declared in. For promoted values, usually, this is
- /// the same as the `DefId` of the `mir::Body` containing the `Place` this promoted appears in.
- /// However, after inlining, that might no longer be the case as inlined `Place`s are copied
- /// into the calling frame.
- pub def_id: DefId,
-}
-
-#[derive(
- Clone,
- Debug,
- PartialEq,
- Eq,
- PartialOrd,
- Ord,
- Hash,
- HashStable,
- RustcEncodable,
- RustcDecodable
-)]
-pub enum StaticKind<'tcx> {
- /// Promoted references consist of an id (`Promoted`) and the substs necessary to monomorphize
- /// it. Usually, these substs are just the identity substs for the item. However, the inliner
- /// will adjust these substs when it inlines a function based on the substs at the callsite.
- Promoted(Promoted, SubstsRef<'tcx>),
- Static,
-}
-
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(RustcEncodable, RustcDecodable, HashStable)]
pub enum ProjectionElem<V, T> {
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PlaceRef<'a, 'tcx> {
- pub base: &'a PlaceBase<'tcx>,
+ pub local: &'a Local,
pub projection: &'a [PlaceElem<'tcx>],
}
impl<'tcx> Place<'tcx> {
// FIXME change this to a const fn by also making List::empty a const fn.
pub fn return_place() -> Place<'tcx> {
- Place { base: PlaceBase::Local(RETURN_PLACE), projection: List::empty() }
+ Place { local: RETURN_PLACE, projection: List::empty() }
}
/// Returns `true` if this `Place` contains a `Deref` projection.
// FIXME: can we safely swap the semantics of `fn base_local` below in here instead?
pub fn local_or_deref_local(&self) -> Option<Local> {
match self.as_ref() {
- PlaceRef { base: &PlaceBase::Local(local), projection: &[] }
- | PlaceRef { base: &PlaceBase::Local(local), projection: &[ProjectionElem::Deref] } => {
- Some(local)
- }
+ PlaceRef { local, projection: &[] }
+ | PlaceRef { local, projection: &[ProjectionElem::Deref] } => Some(*local),
_ => None,
}
}
}
pub fn as_ref(&self) -> PlaceRef<'_, 'tcx> {
- PlaceRef { base: &self.base, projection: &self.projection }
+ PlaceRef { local: &self.local, projection: &self.projection }
}
}
impl From<Local> for Place<'_> {
fn from(local: Local) -> Self {
- Place { base: local.into(), projection: List::empty() }
- }
-}
-
-impl From<Local> for PlaceBase<'_> {
- fn from(local: Local) -> Self {
- PlaceBase::Local(local)
+ Place { local, projection: List::empty() }
}
}
// FIXME: can we safely swap the semantics of `fn base_local` below in here instead?
pub fn local_or_deref_local(&self) -> Option<Local> {
match self {
- PlaceRef { base: PlaceBase::Local(local), projection: [] }
- | PlaceRef { base: PlaceBase::Local(local), projection: [ProjectionElem::Deref] } => {
- Some(*local)
- }
+ PlaceRef { local, projection: [] }
+ | PlaceRef { local, projection: [ProjectionElem::Deref] } => Some(**local),
_ => None,
}
}
/// projections, return `Some(_X)`.
pub fn as_local(&self) -> Option<Local> {
match self {
- PlaceRef { base: PlaceBase::Local(l), projection: [] } => Some(*l),
+ PlaceRef { local, projection: [] } => Some(**local),
_ => None,
}
}
}
}
- write!(fmt, "{:?}", self.base)?;
+ write!(fmt, "{:?}", self.local)?;
for elem in self.projection.iter() {
match elem {
}
}
-impl Debug for PlaceBase<'_> {
- fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
- match *self {
- PlaceBase::Local(id) => write!(fmt, "{:?}", id),
- PlaceBase::Static(box self::Static { ty, kind: StaticKind::Static, def_id }) => {
- write!(fmt, "({}: {:?})", ty::tls::with(|tcx| tcx.def_path_str(def_id)), ty)
- }
- PlaceBase::Static(box self::Static {
- ty,
- kind: StaticKind::Promoted(promoted, _),
- def_id: _,
- }) => write!(fmt, "({:?}: {:?})", promoted, ty),
- }
- }
-}
-
///////////////////////////////////////////////////////////////////////////
// Scopes
}
}
-#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, HashStable)]
-pub enum UnsafetyViolationKind {
- General,
- /// Permitted both in `const fn`s and regular `fn`s.
- GeneralAndConstFn,
- BorrowPacked(hir::HirId),
-}
-
-#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, HashStable)]
-pub struct UnsafetyViolation {
- pub source_info: SourceInfo,
- pub description: Symbol,
- pub details: Symbol,
- pub kind: UnsafetyViolationKind,
-}
-
-#[derive(Clone, RustcEncodable, RustcDecodable, HashStable)]
-pub struct UnsafetyCheckResult {
- /// Violations that are propagated *upwards* from this function.
- pub violations: Lrc<[UnsafetyViolation]>,
- /// `unsafe` blocks in this function, along with whether they are used. This is
- /// used for the "unused_unsafe" lint.
- pub unsafe_blocks: Lrc<[(hir::HirId, bool)]>,
-}
-
-rustc_index::newtype_index! {
- pub struct GeneratorSavedLocal {
- derive [HashStable]
- DEBUG_FORMAT = "_{}",
- }
-}
-
-/// The layout of generator state.
-#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)]
-pub struct GeneratorLayout<'tcx> {
- /// The type of every local stored inside the generator.
- pub field_tys: IndexVec<GeneratorSavedLocal, Ty<'tcx>>,
-
- /// Which of the above fields are in each variant. Note that one field may
- /// be stored in multiple variants.
- pub variant_fields: IndexVec<VariantIdx, IndexVec<Field, GeneratorSavedLocal>>,
-
- /// Which saved locals are storage-live at the same time. Locals that do not
- /// have conflicts with each other are allowed to overlap in the computed
- /// layout.
- pub storage_conflicts: BitMatrix<GeneratorSavedLocal, GeneratorSavedLocal>,
-}
-
-#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)]
-pub struct BorrowCheckResult<'tcx> {
- pub closure_requirements: Option<ClosureRegionRequirements<'tcx>>,
- pub used_mut_upvars: SmallVec<[Field; 8]>,
-}
-
-/// The result of the `mir_const_qualif` query.
-///
-/// Each field corresponds to an implementer of the `Qualif` trait in
-/// `librustc_mir/transform/check_consts/qualifs.rs`. See that file for more information on each
-/// `Qualif`.
-#[derive(Clone, Copy, Debug, Default, RustcEncodable, RustcDecodable, HashStable)]
-pub struct ConstQualifs {
- pub has_mut_interior: bool,
- pub needs_drop: bool,
-}
-
-/// After we borrow check a closure, we are left with various
-/// requirements that we have inferred between the free regions that
-/// appear in the closure's signature or on its field types. These
-/// requirements are then verified and proved by the closure's
-/// creating function. This struct encodes those requirements.
-///
-/// The requirements are listed as being between various
-/// `RegionVid`. The 0th region refers to `'static`; subsequent region
-/// vids refer to the free regions that appear in the closure (or
-/// generator's) type, in order of appearance. (This numbering is
-/// actually defined by the `UniversalRegions` struct in the NLL
-/// region checker. See for example
-/// `UniversalRegions::closure_mapping`.) Note that we treat the free
-/// regions in the closure's type "as if" they were erased, so their
-/// precise identity is not important, only their position.
-///
-/// Example: If type check produces a closure with the closure substs:
-///
-/// ```text
-/// ClosureSubsts = [
-/// i8, // the "closure kind"
-/// for<'x> fn(&'a &'x u32) -> &'x u32, // the "closure signature"
-/// &'a String, // some upvar
-/// ]
-/// ```
-///
-/// here, there is one unique free region (`'a`) but it appears
-/// twice. We would "renumber" each occurrence to a unique vid, as follows:
-///
-/// ```text
-/// ClosureSubsts = [
-/// i8, // the "closure kind"
-/// for<'x> fn(&'1 &'x u32) -> &'x u32, // the "closure signature"
-/// &'2 String, // some upvar
-/// ]
-/// ```
-///
-/// Now the code might impose a requirement like `'1: '2`. When an
-/// instance of the closure is created, the corresponding free regions
-/// can be extracted from its type and constrained to have the given
-/// outlives relationship.
-///
-/// In some cases, we have to record outlives requirements between
-/// types and regions as well. In that case, if those types include
-/// any regions, those regions are recorded as `ReClosureBound`
-/// instances assigned one of these same indices. Those regions will
-/// be substituted away by the creator. We use `ReClosureBound` in
-/// that case because the regions must be allocated in the global
-/// `TyCtxt`, and hence we cannot use `ReVar` (which is what we use
-/// internally within the rest of the NLL code).
-#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)]
-pub struct ClosureRegionRequirements<'tcx> {
- /// The number of external regions defined on the closure. In our
- /// example above, it would be 3 -- one for `'static`, then `'1`
- /// and `'2`. This is just used for a sanity check later on, to
- /// make sure that the number of regions we see at the callsite
- /// matches.
- pub num_external_vids: usize,
-
- /// Requirements between the various free regions defined in
- /// indices.
- pub outlives_requirements: Vec<ClosureOutlivesRequirement<'tcx>>,
-}
-
-/// Indicates an outlives-constraint between a type or between two
-/// free regions declared on the closure.
-#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable, HashStable)]
-pub struct ClosureOutlivesRequirement<'tcx> {
- // This region or type ...
- pub subject: ClosureOutlivesSubject<'tcx>,
-
- // ... must outlive this one.
- pub outlived_free_region: ty::RegionVid,
-
- // If not, report an error here ...
- pub blame_span: Span,
-
- // ... due to this reason.
- pub category: ConstraintCategory,
-}
-
-/// Outlives-constraints can be categorized to determine whether and why they
-/// are interesting (for error reporting). Order of variants indicates sort
-/// order of the category, thereby influencing diagnostic output.
-///
-/// See also [rustc_mir::borrow_check::nll::constraints].
-#[derive(
- Copy,
- Clone,
- Debug,
- Eq,
- PartialEq,
- PartialOrd,
- Ord,
- Hash,
- RustcEncodable,
- RustcDecodable,
- HashStable
-)]
-pub enum ConstraintCategory {
- Return,
- Yield,
- UseAsConst,
- UseAsStatic,
- TypeAnnotation,
- Cast,
-
- /// A constraint that came from checking the body of a closure.
- ///
- /// We try to get the category that the closure used when reporting this.
- ClosureBounds,
- CallArgument,
- CopyBound,
- SizedBound,
- Assignment,
- OpaqueType,
-
- /// A "boring" constraint (caused by the given location) is one that
- /// the user probably doesn't want to see described in diagnostics,
- /// because it is kind of an artifact of the type system setup.
- /// Example: `x = Foo { field: y }` technically creates
- /// intermediate regions representing the "type of `Foo { field: y
- /// }`", and data flows from `y` into those variables, but they
- /// are not very interesting. The assignment into `x` on the other
- /// hand might be.
- Boring,
- // Boring and applicable everywhere.
- BoringNoLocation,
-
- /// A constraint that doesn't correspond to anything the user sees.
- Internal,
-}
-
-/// The subject of a `ClosureOutlivesRequirement` -- that is, the thing
-/// that must outlive some region.
-#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable, HashStable)]
-pub enum ClosureOutlivesSubject<'tcx> {
- /// Subject is a type, typically a type parameter, but could also
- /// be a projection. Indicates a requirement like `T: 'a` being
- /// passed to the caller, where the type here is `T`.
- ///
- /// The type here is guaranteed not to contain any free regions at
- /// present.
- Ty(Ty<'tcx>),
-
- /// Subject is a free region from the closure. Indicates a requirement
- /// like `'a: 'b` being passed to the caller; the region here is `'a`.
- Region(ty::RegionVid),
-}
-
/*
* `TypeFoldable` implementations for MIR types
*/
impl<'tcx> TypeFoldable<'tcx> for Place<'tcx> {
fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
- Place { base: self.base.fold_with(folder), projection: self.projection.fold_with(folder) }
- }
-
- fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
- self.base.visit_with(visitor) || self.projection.visit_with(visitor)
- }
-}
-
-impl<'tcx> TypeFoldable<'tcx> for PlaceBase<'tcx> {
- fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
- match self {
- PlaceBase::Local(local) => PlaceBase::Local(local.fold_with(folder)),
- PlaceBase::Static(static_) => PlaceBase::Static(static_.fold_with(folder)),
- }
+ Place { local: self.local.fold_with(folder), projection: self.projection.fold_with(folder) }
}
fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
- match self {
- PlaceBase::Local(local) => local.visit_with(visitor),
- PlaceBase::Static(static_) => (**static_).visit_with(visitor),
- }
+ self.local.visit_with(visitor) || self.projection.visit_with(visitor)
}
}
}
}
-impl<'tcx> TypeFoldable<'tcx> for Static<'tcx> {
- fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
- Static {
- ty: self.ty.fold_with(folder),
- kind: self.kind.fold_with(folder),
- def_id: self.def_id,
- }
- }
-
- fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
- let Static { ty, kind, def_id: _ } = self;
-
- ty.visit_with(visitor) || kind.visit_with(visitor)
- }
-}
-
-impl<'tcx> TypeFoldable<'tcx> for StaticKind<'tcx> {
- fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
- match self {
- StaticKind::Promoted(promoted, substs) => {
- StaticKind::Promoted(promoted.fold_with(folder), substs.fold_with(folder))
- }
- StaticKind::Static => StaticKind::Static,
- }
- }
-
- fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
- match self {
- StaticKind::Promoted(promoted, substs) => {
- promoted.visit_with(visitor) || substs.visit_with(visitor)
- }
- StaticKind::Static => false,
- }
- }
-}
-
impl<'tcx> TypeFoldable<'tcx> for Rvalue<'tcx> {
fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
use crate::mir::Rvalue::*;
--- /dev/null
+//! Values computed by queries that use MIR.
+
+use crate::ty::{self, Ty};
+use rustc_data_structures::sync::Lrc;
+use rustc_hir as hir;
+use rustc_index::bit_set::BitMatrix;
+use rustc_index::vec::IndexVec;
+use rustc_span::{Span, Symbol};
+use rustc_target::abi::VariantIdx;
+use smallvec::SmallVec;
+
+use super::{Field, SourceInfo};
+
+#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, HashStable)]
+pub enum UnsafetyViolationKind {
+ General,
+ /// Permitted both in `const fn`s and regular `fn`s.
+ GeneralAndConstFn,
+ BorrowPacked(hir::HirId),
+}
+
+#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, HashStable)]
+pub struct UnsafetyViolation {
+ pub source_info: SourceInfo,
+ pub description: Symbol,
+ pub details: Symbol,
+ pub kind: UnsafetyViolationKind,
+}
+
+#[derive(Clone, RustcEncodable, RustcDecodable, HashStable)]
+pub struct UnsafetyCheckResult {
+ /// Violations that are propagated *upwards* from this function.
+ pub violations: Lrc<[UnsafetyViolation]>,
+ /// `unsafe` blocks in this function, along with whether they are used. This is
+ /// used for the "unused_unsafe" lint.
+ pub unsafe_blocks: Lrc<[(hir::HirId, bool)]>,
+}
+
+rustc_index::newtype_index! {
+ pub struct GeneratorSavedLocal {
+ derive [HashStable]
+ DEBUG_FORMAT = "_{}",
+ }
+}
+
+/// The layout of generator state.
+#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)]
+pub struct GeneratorLayout<'tcx> {
+ /// The type of every local stored inside the generator.
+ pub field_tys: IndexVec<GeneratorSavedLocal, Ty<'tcx>>,
+
+ /// Which of the above fields are in each variant. Note that one field may
+ /// be stored in multiple variants.
+ pub variant_fields: IndexVec<VariantIdx, IndexVec<Field, GeneratorSavedLocal>>,
+
+ /// Which saved locals are storage-live at the same time. Locals that do not
+ /// have conflicts with each other are allowed to overlap in the computed
+ /// layout.
+ pub storage_conflicts: BitMatrix<GeneratorSavedLocal, GeneratorSavedLocal>,
+}
+
+#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)]
+pub struct BorrowCheckResult<'tcx> {
+ pub closure_requirements: Option<ClosureRegionRequirements<'tcx>>,
+ pub used_mut_upvars: SmallVec<[Field; 8]>,
+}
+
+/// The result of the `mir_const_qualif` query.
+///
+/// Each field corresponds to an implementer of the `Qualif` trait in
+/// `librustc_mir/transform/check_consts/qualifs.rs`. See that file for more information on each
+/// `Qualif`.
+#[derive(Clone, Copy, Debug, Default, RustcEncodable, RustcDecodable, HashStable)]
+pub struct ConstQualifs {
+ pub has_mut_interior: bool,
+ pub needs_drop: bool,
+}
+
+/// After we borrow check a closure, we are left with various
+/// requirements that we have inferred between the free regions that
+/// appear in the closure's signature or on its field types. These
+/// requirements are then verified and proved by the closure's
+/// creating function. This struct encodes those requirements.
+///
+/// The requirements are listed as being between various
+/// `RegionVid`. The 0th region refers to `'static`; subsequent region
+/// vids refer to the free regions that appear in the closure (or
+/// generator's) type, in order of appearance. (This numbering is
+/// actually defined by the `UniversalRegions` struct in the NLL
+/// region checker. See for example
+/// `UniversalRegions::closure_mapping`.) Note that we treat the free
+/// regions in the closure's type "as if" they were erased, so their
+/// precise identity is not important, only their position.
+///
+/// Example: If type check produces a closure with the closure substs:
+///
+/// ```text
+/// ClosureSubsts = [
+/// i8, // the "closure kind"
+/// for<'x> fn(&'a &'x u32) -> &'x u32, // the "closure signature"
+/// &'a String, // some upvar
+/// ]
+/// ```
+///
+/// here, there is one unique free region (`'a`) but it appears
+/// twice. We would "renumber" each occurrence to a unique vid, as follows:
+///
+/// ```text
+/// ClosureSubsts = [
+/// i8, // the "closure kind"
+/// for<'x> fn(&'1 &'x u32) -> &'x u32, // the "closure signature"
+/// &'2 String, // some upvar
+/// ]
+/// ```
+///
+/// Now the code might impose a requirement like `'1: '2`. When an
+/// instance of the closure is created, the corresponding free regions
+/// can be extracted from its type and constrained to have the given
+/// outlives relationship.
+///
+/// In some cases, we have to record outlives requirements between
+/// types and regions as well. In that case, if those types include
+/// any regions, those regions are recorded as `ReClosureBound`
+/// instances assigned one of these same indices. Those regions will
+/// be substituted away by the creator. We use `ReClosureBound` in
+/// that case because the regions must be allocated in the global
+/// `TyCtxt`, and hence we cannot use `ReVar` (which is what we use
+/// internally within the rest of the NLL code).
+#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)]
+pub struct ClosureRegionRequirements<'tcx> {
+ /// The number of external regions defined on the closure. In our
+ /// example above, it would be 3 -- one for `'static`, then `'1`
+ /// and `'2`. This is just used for a sanity check later on, to
+ /// make sure that the number of regions we see at the callsite
+ /// matches.
+ pub num_external_vids: usize,
+
+ /// Requirements between the various free regions defined in
+ /// indices.
+ pub outlives_requirements: Vec<ClosureOutlivesRequirement<'tcx>>,
+}
+
+/// Indicates an outlives-constraint between a type or between two
+/// free regions declared on the closure.
+#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable, HashStable)]
+pub struct ClosureOutlivesRequirement<'tcx> {
+ // This region or type ...
+ pub subject: ClosureOutlivesSubject<'tcx>,
+
+ // ... must outlive this one.
+ pub outlived_free_region: ty::RegionVid,
+
+ // If not, report an error here ...
+ pub blame_span: Span,
+
+ // ... due to this reason.
+ pub category: ConstraintCategory,
+}
+
+/// Outlives-constraints can be categorized to determine whether and why they
+/// are interesting (for error reporting). Order of variants indicates sort
+/// order of the category, thereby influencing diagnostic output.
+///
+/// See also [rustc_mir::borrow_check::nll::constraints].
+#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
+#[derive(RustcEncodable, RustcDecodable, HashStable)]
+pub enum ConstraintCategory {
+ Return,
+ Yield,
+ UseAsConst,
+ UseAsStatic,
+ TypeAnnotation,
+ Cast,
+
+ /// A constraint that came from checking the body of a closure.
+ ///
+ /// We try to get the category that the closure used when reporting this.
+ ClosureBounds,
+ CallArgument,
+ CopyBound,
+ SizedBound,
+ Assignment,
+ OpaqueType,
+
+ /// A "boring" constraint (caused by the given location) is one that
+ /// the user probably doesn't want to see described in diagnostics,
+ /// because it is kind of an artifact of the type system setup.
+ /// Example: `x = Foo { field: y }` technically creates
+ /// intermediate regions representing the "type of `Foo { field: y
+ /// }`", and data flows from `y` into those variables, but they
+ /// are not very interesting. The assignment into `x` on the other
+ /// hand might be.
+ Boring,
+ // Boring and applicable everywhere.
+ BoringNoLocation,
+
+ /// A constraint that doesn't correspond to anything the user sees.
+ Internal,
+}
+
+/// The subject of a `ClosureOutlivesRequirement` -- that is, the thing
+/// that must outlive some region.
+#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable, HashStable)]
+pub enum ClosureOutlivesSubject<'tcx> {
+ /// Subject is a type, typically a type parameter, but could also
+ /// be a projection. Indicates a requirement like `T: 'a` being
+ /// passed to the caller, where the type here is `T`.
+ ///
+ /// The type here is guaranteed not to contain any free regions at
+ /// present.
+ Ty(Ty<'tcx>),
+
+ /// Subject is a free region from the closure. Indicates a requirement
+ /// like `'a: 'b` being passed to the caller; the region here is `'a`.
+ Region(ty::RegionVid),
+}
+
+/// The constituent parts of an ADT or array.
+#[derive(Copy, Clone, Debug, HashStable)]
+pub struct DestructuredConst<'tcx> {
+ pub variant: VariantIdx,
+ pub fields: &'tcx [&'tcx ty::Const<'tcx>],
+}
impl<'tcx> Place<'tcx> {
pub fn ty_from<D>(
- base: &PlaceBase<'tcx>,
+ local: &Local,
projection: &[PlaceElem<'tcx>],
local_decls: &D,
tcx: TyCtxt<'tcx>,
{
projection
.iter()
- .fold(base.ty(local_decls), |place_ty, elem| place_ty.projection_ty(tcx, elem))
+ .fold(PlaceTy::from_ty(local_decls.local_decls()[*local].ty), |place_ty, elem| {
+ place_ty.projection_ty(tcx, elem)
+ })
}
pub fn ty<D>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> PlaceTy<'tcx>
where
D: HasLocalDecls<'tcx>,
{
- Place::ty_from(&self.base, &self.projection, local_decls, tcx)
- }
-}
-
-impl<'tcx> PlaceBase<'tcx> {
- pub fn ty<D>(&self, local_decls: &D) -> PlaceTy<'tcx>
- where
- D: HasLocalDecls<'tcx>,
- {
- match self {
- PlaceBase::Local(index) => PlaceTy::from_ty(local_decls.local_decls()[*index].ty),
- PlaceBase::Static(data) => PlaceTy::from_ty(data.ty),
- }
+ Place::ty_from(&self.local, &self.projection, local_decls, tcx)
}
}
}
fn visit_place_base(&mut self,
- base: & $($mutability)? PlaceBase<'tcx>,
+ local: & $($mutability)? Local,
context: PlaceContext,
location: Location) {
- self.super_place_base(base, context, location);
+ self.super_place_base(local, context, location);
}
visit_place_fns!($($mutability)?);
}
fn super_place_base(&mut self,
- place_base: & $($mutability)? PlaceBase<'tcx>,
+ local: & $($mutability)? Local,
context: PlaceContext,
location: Location) {
- match place_base {
- PlaceBase::Local(local) => {
- self.visit_local(local, context, location);
- }
- PlaceBase::Static(box Static { kind: _, ty, def_id: _ }) => {
- self.visit_ty(& $($mutability)? *ty, TyContext::Location(location));
- }
- }
+ self.visit_local(local, context, location);
}
fn super_local_decl(&mut self,
context: PlaceContext,
location: Location,
) {
- self.visit_place_base(&mut place.base, context, location);
+ self.visit_place_base(&mut place.local, context, location);
if let Some(new_projection) = self.process_projection(&place.projection) {
place.projection = self.tcx().intern_place_elems(&new_projection);
() => (
fn visit_projection(
&mut self,
- base: &PlaceBase<'tcx>,
+ local: &Local,
projection: &[PlaceElem<'tcx>],
context: PlaceContext,
location: Location,
) {
- self.super_projection(base, projection, context, location);
+ self.super_projection(local, projection, context, location);
}
fn visit_projection_elem(
&mut self,
- base: &PlaceBase<'tcx>,
+ local: &Local,
proj_base: &[PlaceElem<'tcx>],
elem: &PlaceElem<'tcx>,
context: PlaceContext,
location: Location,
) {
- self.super_projection_elem(base, proj_base, elem, context, location);
+ self.super_projection_elem(local, proj_base, elem, context, location);
}
fn super_place(
};
}
- self.visit_place_base(&place.base, context, location);
+ self.visit_place_base(&place.local, context, location);
- self.visit_projection(&place.base,
+ self.visit_projection(&place.local,
&place.projection,
context,
location);
fn super_projection(
&mut self,
- base: &PlaceBase<'tcx>,
+ local: &Local,
projection: &[PlaceElem<'tcx>],
context: PlaceContext,
location: Location,
let mut cursor = projection;
while let [proj_base @ .., elem] = cursor {
cursor = proj_base;
- self.visit_projection_elem(base, cursor, elem, context, location);
+ self.visit_projection_elem(local, cursor, elem, context, location);
}
}
fn super_projection_elem(
&mut self,
- _base: &PlaceBase<'tcx>,
+ _local: &Local,
_proj_base: &[PlaceElem<'tcx>],
elem: &PlaceElem<'tcx>,
_context: PlaceContext,
desc { "looking up the native libraries of a linked crate" }
}
- query lint_levels(_: CrateNum) -> &'tcx lint::LintLevelMap {
+ query lint_levels(_: CrateNum) -> &'tcx LintLevelMap {
eval_always
desc { "computing the lint levels for items in this crate" }
}
desc { "extract field of const" }
}
+ /// Destructure a constant ADT or array into its variant indent and its
+ /// field values.
+ query destructure_const(
+ key: ty::ParamEnvAnd<'tcx, &'tcx ty::Const<'tcx>>
+ ) -> mir::DestructuredConst<'tcx> {
+ no_force
+ desc { "destructure constant" }
+ }
+
query const_caller_location(key: (rustc_span::Symbol, u32, u32)) -> &'tcx ty::Const<'tcx> {
no_force
desc { "get a &core::panic::Location referring to a span" }
fatal_cycle
desc { "checking if the crate has_panic_handler" }
}
- query is_sanitizer_runtime(_: CrateNum) -> bool {
- fatal_cycle
- desc { "query a crate is `#![sanitizer_runtime]`" }
- }
query is_profiler_runtime(_: CrateNum) -> bool {
fatal_cycle
desc { "query a crate is `#![profiler_runtime]`" }
FulfillmentContext, Obligation, ObligationCause, SelectionContext, TraitEngine, Vtable,
};
use crate::ty::fold::TypeFoldable;
-use crate::ty::subst::{Subst, SubstsRef};
use crate::ty::{self, TyCtxt};
/// Attempts to resolve an obligation to a vtable. The result is
})
}
-impl<'tcx> TyCtxt<'tcx> {
- /// Monomorphizes a type from the AST by first applying the
- /// in-scope substitutions and then normalizing any associated
- /// types.
- pub fn subst_and_normalize_erasing_regions<T>(
- self,
- param_substs: SubstsRef<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- value: &T,
- ) -> T
- where
- T: TypeFoldable<'tcx>,
- {
- debug!(
- "subst_and_normalize_erasing_regions(\
- param_substs={:?}, \
- value={:?}, \
- param_env={:?})",
- param_substs, value, param_env,
- );
- let substituted = value.subst(self, param_substs);
- self.normalize_erasing_regions(param_env, substituted)
- }
-}
-
// # Global Cache
impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
pub involves_placeholder: bool,
}
-pub fn add_placeholder_note(err: &mut errors::DiagnosticBuilder<'_>) {
+pub fn add_placeholder_note(err: &mut rustc_errors::DiagnosticBuilder<'_>) {
err.note(&format!(
"this behavior recently changed as a result of a bug fix; \
see rust-lang/rust#56105 for details"
TraitNotObjectSafe,
};
-use crate::infer::error_reporting::TypeAnnotationNeeded as ErrorCode;
+use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode};
use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use crate::infer::{self, InferCtxt};
use crate::mir::interpret::ErrorHandled;
use crate::session::DiagnosticMessageId;
+use crate::traits::object_safety_violations;
use crate::ty::error::ExpectedFound;
use crate::ty::fast_reject;
use crate::ty::fold::TypeFolder;
use crate::ty::SubtypePredicate;
use crate::ty::TypeckTables;
use crate::ty::{self, AdtKind, DefIdTree, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable};
-use errors::{pluralize, Applicability, DiagnosticBuilder, Style};
+
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, Style};
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
use rustc_hir::Node;
flags.push((sym::from_method, Some(method.to_string())));
}
}
- if let Some(t) = self.get_parent_trait_ref(&obligation.cause.code) {
+ if let Some((t, _)) = self.get_parent_trait_ref(&obligation.cause.code) {
flags.push((sym::parent_trait, Some(t)));
}
}
/// Gets the parent trait chain start
- fn get_parent_trait_ref(&self, code: &ObligationCauseCode<'tcx>) -> Option<String> {
+ fn get_parent_trait_ref(
+ &self,
+ code: &ObligationCauseCode<'tcx>,
+ ) -> Option<(String, Option<Span>)> {
match code {
&ObligationCauseCode::BuiltinDerivedObligation(ref data) => {
let parent_trait_ref = self.resolve_vars_if_possible(&data.parent_trait_ref);
match self.get_parent_trait_ref(&data.parent_code) {
Some(t) => Some(t),
- None => Some(parent_trait_ref.skip_binder().self_ty().to_string()),
+ None => {
+ let ty = parent_trait_ref.skip_binder().self_ty();
+ let span =
+ TyCategory::from_ty(ty).map(|(_, def_id)| self.tcx.def_span(def_id));
+ Some((ty.to_string(), span))
+ }
}
}
_ => None,
return;
}
let trait_ref = trait_predicate.to_poly_trait_ref();
- let (post_message, pre_message) = self
+ let (post_message, pre_message, type_def) = self
.get_parent_trait_ref(&obligation.cause.code)
- .map(|t| (format!(" in `{}`", t), format!("within `{}`, ", t)))
+ .map(|(t, s)| {
+ (
+ format!(" in `{}`", t),
+ format!("within `{}`, ", t),
+ s.map(|s| (format!("within this `{}`", t), s)),
+ )
+ })
.unwrap_or_default();
let OnUnimplementedNote { message, label, note, enclosing_scope } =
} else {
err.span_label(span, explanation);
}
+ if let Some((msg, span)) = type_def {
+ err.span_label(span, &msg);
+ }
if let Some(ref s) = note {
// If it has a custom `#[rustc_on_unimplemented]` note, let's display it
err.note(s.as_str());
}
ty::Predicate::ObjectSafe(trait_def_id) => {
- let violations = self.tcx.object_safety_violations(trait_def_id);
- self.tcx.report_object_safety_error(span, trait_def_id, violations)
+ let violations = object_safety_violations(self.tcx, trait_def_id);
+ report_object_safety_error(self.tcx, span, trait_def_id, violations)
}
ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) => {
}
TraitNotObjectSafe(did) => {
- let violations = self.tcx.object_safety_violations(did);
- self.tcx.report_object_safety_error(span, did, violations)
+ let violations = object_safety_violations(self.tcx, did);
+ report_object_safety_error(self.tcx, span, did, violations)
}
// already reported in the query
}
}
-impl<'tcx> TyCtxt<'tcx> {
- pub fn recursive_type_with_infinite_size_error(
- self,
- type_def_id: DefId,
- ) -> DiagnosticBuilder<'tcx> {
- assert!(type_def_id.is_local());
- let span = self.hir().span_if_local(type_def_id).unwrap();
- let span = self.sess.source_map().def_span(span);
- let mut err = struct_span_err!(
- self.sess,
- span,
- E0072,
- "recursive type `{}` has infinite size",
- self.def_path_str(type_def_id)
- );
- err.span_label(span, "recursive type has infinite size");
- err.help(&format!(
- "insert indirection (e.g., a `Box`, `Rc`, or `&`) \
+pub fn recursive_type_with_infinite_size_error(
+ tcx: TyCtxt<'tcx>,
+ type_def_id: DefId,
+) -> DiagnosticBuilder<'tcx> {
+ assert!(type_def_id.is_local());
+ let span = tcx.hir().span_if_local(type_def_id).unwrap();
+ let span = tcx.sess.source_map().def_span(span);
+ let mut err = struct_span_err!(
+ tcx.sess,
+ span,
+ E0072,
+ "recursive type `{}` has infinite size",
+ tcx.def_path_str(type_def_id)
+ );
+ err.span_label(span, "recursive type has infinite size");
+ err.help(&format!(
+ "insert indirection (e.g., a `Box`, `Rc`, or `&`) \
at some point to make `{}` representable",
- self.def_path_str(type_def_id)
- ));
- err
- }
-
- pub fn report_object_safety_error(
- self,
- span: Span,
- trait_def_id: DefId,
- violations: Vec<ObjectSafetyViolation>,
- ) -> DiagnosticBuilder<'tcx> {
- let trait_str = self.def_path_str(trait_def_id);
- let span = self.sess.source_map().def_span(span);
- let mut err = struct_span_err!(
- self.sess,
- span,
- E0038,
- "the trait `{}` cannot be made into an object",
- trait_str
- );
- err.span_label(span, format!("the trait `{}` cannot be made into an object", trait_str));
-
- let mut reported_violations = FxHashSet::default();
- for violation in violations {
- if reported_violations.insert(violation.clone()) {
- match violation.span() {
- Some(span) => err.span_label(span, violation.error_msg()),
- None => err.note(&violation.error_msg()),
- };
- }
- }
+ tcx.def_path_str(type_def_id)
+ ));
+ err
+}
- if self.sess.trait_methods_not_found.borrow().contains(&span) {
- // Avoid emitting error caused by non-existing method (#58734)
- err.cancel();
+pub fn report_object_safety_error(
+ tcx: TyCtxt<'tcx>,
+ span: Span,
+ trait_def_id: DefId,
+ violations: Vec<ObjectSafetyViolation>,
+) -> DiagnosticBuilder<'tcx> {
+ let trait_str = tcx.def_path_str(trait_def_id);
+ let span = tcx.sess.source_map().def_span(span);
+ let mut err = struct_span_err!(
+ tcx.sess,
+ span,
+ E0038,
+ "the trait `{}` cannot be made into an object",
+ trait_str
+ );
+ err.span_label(span, format!("the trait `{}` cannot be made into an object", trait_str));
+
+ let mut reported_violations = FxHashSet::default();
+ for violation in violations {
+ if reported_violations.insert(violation.clone()) {
+ match violation.span() {
+ Some(span) => err.span_label(span, violation.error_msg()),
+ None => err.note(&violation.error_msg()),
+ };
}
+ }
- err
+ if tcx.sess.trait_methods_not_found.borrow().contains(&span) {
+ // Avoid emitting error caused by non-existing method (#58734)
+ err.cancel();
}
+
+ err
}
impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
}
let mut err = self.need_type_info_err(body_id, span, self_ty, ErrorCode::E0283);
err.note(&format!("cannot resolve `{}`", predicate));
- if let (Ok(ref snippet), ObligationCauseCode::BindingObligation(ref def_id, _)) =
+ if let ObligationCauseCode::ItemObligation(def_id) = obligation.cause.code {
+ self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id());
+ } else if let (
+ Ok(ref snippet),
+ ObligationCauseCode::BindingObligation(ref def_id, _),
+ ) =
(self.tcx.sess.source_map().span_to_snippet(span), &obligation.cause.code)
{
let generics = self.tcx.generics_of(*def_id);
err.emit();
}
+ fn suggest_fully_qualified_path(
+ &self,
+ err: &mut DiagnosticBuilder<'_>,
+ def_id: DefId,
+ span: Span,
+ trait_ref: DefId,
+ ) {
+ if let Some(assoc_item) = self.tcx.opt_associated_item(def_id) {
+ if let ty::AssocKind::Const | ty::AssocKind::Type = assoc_item.kind {
+ err.note(&format!(
+ "{}s cannot be accessed directly on a `trait`, they can only be \
+ accessed through a specific `impl`",
+ assoc_item.kind.suggestion_descr(),
+ ));
+ err.span_suggestion(
+ span,
+ "use the fully qualified path to an implementation",
+ format!("<Type as {}>::{}", self.tcx.def_path_str(trait_ref), assoc_item.ident),
+ Applicability::HasPlaceholders,
+ );
+ }
+ }
+ }
+
/// Returns `true` if the trait predicate may apply for *some* assignment
/// to the type parameters.
fn predicate_can_apply(
use super::engine::{TraitEngine, TraitEngineExt};
use super::project;
use super::select::SelectionContext;
+use super::wf;
use super::CodeAmbiguity;
use super::CodeProjectionError;
use super::CodeSelectionError;
}
ty::Predicate::WellFormed(ty) => {
- match ty::wf::obligations(
+ match wf::obligations(
self.selcx.infcx(),
obligation.param_env,
obligation.cause.body_id,
obligation.param_env,
def_id,
substs,
+ None,
Some(obligation.cause.span),
) {
Ok(_) => ProcessResult::Changed(vec![]),
--- /dev/null
+//! Miscellaneous type-system utilities that are too small to deserve their own modules.
+
+use crate::middle::lang_items;
+use crate::traits::{self, ObligationCause};
+use crate::ty::util::NeedsDrop;
+use crate::ty::{self, Ty, TyCtxt, TypeFoldable};
+
+use rustc_hir as hir;
+use rustc_span::DUMMY_SP;
+
+#[derive(Clone)]
+pub enum CopyImplementationError<'tcx> {
+ InfrigingFields(Vec<&'tcx ty::FieldDef>),
+ NotAnAdt,
+ HasDestructor,
+}
+
+pub fn can_type_implement_copy(
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ self_type: Ty<'tcx>,
+) -> Result<(), CopyImplementationError<'tcx>> {
+ // FIXME: (@jroesch) float this code up
+ tcx.infer_ctxt().enter(|infcx| {
+ let (adt, substs) = match self_type.kind {
+ // These types used to have a builtin impl.
+ // Now libcore provides that impl.
+ ty::Uint(_)
+ | ty::Int(_)
+ | ty::Bool
+ | ty::Float(_)
+ | ty::Char
+ | ty::RawPtr(..)
+ | ty::Never
+ | ty::Ref(_, _, hir::Mutability::Not) => return Ok(()),
+
+ ty::Adt(adt, substs) => (adt, substs),
+
+ _ => return Err(CopyImplementationError::NotAnAdt),
+ };
+
+ let mut infringing = Vec::new();
+ for variant in &adt.variants {
+ for field in &variant.fields {
+ let ty = field.ty(tcx, substs);
+ if ty.references_error() {
+ continue;
+ }
+ let span = tcx.def_span(field.did);
+ let cause = ObligationCause { span, ..ObligationCause::dummy() };
+ let ctx = traits::FulfillmentContext::new();
+ match traits::fully_normalize(&infcx, ctx, cause, param_env, &ty) {
+ Ok(ty) => {
+ if !infcx.type_is_copy_modulo_regions(param_env, ty, span) {
+ infringing.push(field);
+ }
+ }
+ Err(errors) => {
+ infcx.report_fulfillment_errors(&errors, None, false);
+ }
+ };
+ }
+ }
+ if !infringing.is_empty() {
+ return Err(CopyImplementationError::InfrigingFields(infringing));
+ }
+ if adt.has_dtor(tcx) {
+ return Err(CopyImplementationError::HasDestructor);
+ }
+
+ Ok(())
+ })
+}
+
+fn is_copy_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
+ is_item_raw(tcx, query, lang_items::CopyTraitLangItem)
+}
+
+fn is_sized_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
+ is_item_raw(tcx, query, lang_items::SizedTraitLangItem)
+}
+
+fn is_freeze_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
+ is_item_raw(tcx, query, lang_items::FreezeTraitLangItem)
+}
+
+fn is_item_raw<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
+ item: lang_items::LangItem,
+) -> bool {
+ let (param_env, ty) = query.into_parts();
+ let trait_def_id = tcx.require_lang_item(item, None);
+ tcx.infer_ctxt().enter(|infcx| {
+ traits::type_known_to_meet_bound_modulo_regions(
+ &infcx,
+ param_env,
+ ty,
+ trait_def_id,
+ DUMMY_SP,
+ )
+ })
+}
+
+fn needs_drop_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> NeedsDrop {
+ let (param_env, ty) = query.into_parts();
+
+ let needs_drop = |ty: Ty<'tcx>| -> bool { tcx.needs_drop_raw(param_env.and(ty)).0 };
+
+ assert!(!ty.needs_infer());
+
+ NeedsDrop(match ty.kind {
+ // Fast-path for primitive types
+ ty::Infer(ty::FreshIntTy(_))
+ | ty::Infer(ty::FreshFloatTy(_))
+ | ty::Bool
+ | ty::Int(_)
+ | ty::Uint(_)
+ | ty::Float(_)
+ | ty::Never
+ | ty::FnDef(..)
+ | ty::FnPtr(_)
+ | ty::Char
+ | ty::GeneratorWitness(..)
+ | ty::RawPtr(_)
+ | ty::Ref(..)
+ | ty::Str => false,
+
+ // Foreign types can never have destructors
+ ty::Foreign(..) => false,
+
+ // `ManuallyDrop` doesn't have a destructor regardless of field types.
+ ty::Adt(def, _) if Some(def.did) == tcx.lang_items().manually_drop() => false,
+
+ // Issue #22536: We first query `is_copy_modulo_regions`. It sees a
+ // normalized version of the type, and therefore will definitely
+ // know whether the type implements Copy (and thus needs no
+ // cleanup/drop/zeroing) ...
+ _ if ty.is_copy_modulo_regions(tcx, param_env, DUMMY_SP) => false,
+
+ // ... (issue #22536 continued) but as an optimization, still use
+ // prior logic of asking for the structural "may drop".
+
+ // FIXME(#22815): Note that this is a conservative heuristic;
+ // it may report that the type "may drop" when actual type does
+ // not actually have a destructor associated with it. But since
+ // the type absolutely did not have the `Copy` bound attached
+ // (see above), it is sound to treat it as having a destructor.
+
+ // User destructors are the only way to have concrete drop types.
+ ty::Adt(def, _) if def.has_dtor(tcx) => true,
+
+ // Can refer to a type which may drop.
+ // FIXME(eddyb) check this against a ParamEnv.
+ ty::Dynamic(..)
+ | ty::Projection(..)
+ | ty::Param(_)
+ | ty::Bound(..)
+ | ty::Placeholder(..)
+ | ty::Opaque(..)
+ | ty::Infer(_)
+ | ty::Error => true,
+
+ ty::UnnormalizedProjection(..) => bug!("only used with chalk-engine"),
+
+ // Zero-length arrays never contain anything to drop.
+ ty::Array(_, len) if len.try_eval_usize(tcx, param_env) == Some(0) => false,
+
+ // Structural recursion.
+ ty::Array(ty, _) | ty::Slice(ty) => needs_drop(ty),
+
+ ty::Closure(def_id, ref substs) => {
+ substs.as_closure().upvar_tys(def_id, tcx).any(needs_drop)
+ }
+
+ // Pessimistically assume that all generators will require destructors
+ // as we don't know if a destructor is a noop or not until after the MIR
+ // state transformation pass
+ ty::Generator(..) => true,
+
+ ty::Tuple(..) => ty.tuple_fields().any(needs_drop),
+
+ // unions don't have destructors because of the child types,
+ // only if they manually implement `Drop` (handled above).
+ ty::Adt(def, _) if def.is_union() => false,
+
+ ty::Adt(def, substs) => def
+ .variants
+ .iter()
+ .any(|variant| variant.fields.iter().any(|field| needs_drop(field.ty(tcx, substs)))),
+ })
+}
+
+pub fn provide(providers: &mut ty::query::Providers<'_>) {
+ *providers = ty::query::Providers {
+ is_copy_raw,
+ is_sized_raw,
+ is_freeze_raw,
+ needs_drop_raw,
+ ..*providers
+ };
+}
mod engine;
pub mod error_reporting;
mod fulfill;
+pub mod misc;
mod object_safety;
mod on_unimplemented;
mod project;
mod select;
mod specialize;
mod structural_impls;
+mod structural_match;
mod util;
+pub mod wf;
use crate::infer::outlives::env::OutlivesEnvironment;
use crate::infer::{InferCtxt, SuppressRegionErrors};
pub use self::coherence::{OrphanCheckErr, OverlapResult};
pub use self::engine::{TraitEngine, TraitEngineExt};
pub use self::fulfill::{FulfillmentContext, PendingPredicateObligation};
+pub use self::object_safety::astconv_object_safety_violations;
+pub use self::object_safety::is_vtable_safe_method;
+pub use self::object_safety::object_safety_violations;
pub use self::object_safety::MethodViolationCode;
pub use self::object_safety::ObjectSafetyViolation;
pub use self::on_unimplemented::{OnUnimplementedDirective, OnUnimplementedNote};
pub use self::specialize::specialization_graph::FutureCompatOverlapError;
pub use self::specialize::specialization_graph::FutureCompatOverlapErrorKind;
pub use self::specialize::{specialization_graph, translate_substs, OverlapError};
+pub use self::structural_match::search_for_structural_match_violation;
+pub use self::structural_match::type_marked_structural;
+pub use self::structural_match::NonStructuralMatchTy;
pub use self::util::{elaborate_predicates, elaborate_trait_ref, elaborate_trait_refs};
pub use self::util::{expand_trait_aliases, TraitAliasExpander};
+pub use self::util::{
+ get_vtable_index_of_object_method, impl_is_default, impl_item_is_final,
+ predicate_for_trait_def, upcast_choices,
+};
pub use self::util::{
supertrait_def_ids, supertraits, transitive_bounds, SupertraitDefIds, Supertraits,
};
let def_id = trait_method.def_id;
// Some methods cannot be called on an object; skip those.
- if !tcx.is_vtable_safe_method(trait_ref.def_id(), &trait_method) {
+ if !is_vtable_safe_method(tcx, trait_ref.def_id(), &trait_method) {
debug!("vtable_methods: not vtable safe");
return None;
}
}
pub fn provide(providers: &mut ty::query::Providers<'_>) {
+ misc::provide(providers);
*providers = ty::query::Providers {
is_object_safe: object_safety::is_object_safe_provider,
specialization_graph_of: specialize::specialization_graph_provider,
use super::elaborate_predicates;
-use crate::lint;
use crate::traits::{self, Obligation, ObligationCause};
use crate::ty::subst::{InternalSubsts, Subst};
use crate::ty::{self, Predicate, ToPredicate, Ty, TyCtxt, TypeFoldable};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
+use rustc_session::lint::builtin::WHERE_CLAUSES_OBJECT_SAFETY;
use rustc_span::symbol::Symbol;
use rustc_span::{Span, DUMMY_SP};
+use syntax::ast;
+
use std::borrow::Cow;
use std::iter::{self};
-use syntax::ast::{self};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum ObjectSafetyViolation {
UndispatchableReceiver,
}
-impl<'tcx> TyCtxt<'tcx> {
- /// Returns the object safety violations that affect
- /// astconv -- currently, `Self` in supertraits. This is needed
- /// because `object_safety_violations` can't be used during
- /// type collection.
- pub fn astconv_object_safety_violations(
- self,
- trait_def_id: DefId,
- ) -> Vec<ObjectSafetyViolation> {
- debug_assert!(self.generics_of(trait_def_id).has_self);
- let violations = traits::supertrait_def_ids(self, trait_def_id)
- .filter(|&def_id| self.predicates_reference_self(def_id, true))
- .map(|_| ObjectSafetyViolation::SupertraitSelf)
- .collect();
-
- debug!(
- "astconv_object_safety_violations(trait_def_id={:?}) = {:?}",
- trait_def_id, violations
- );
+/// Returns the object safety violations that affect
+/// astconv -- currently, `Self` in supertraits. This is needed
+/// because `object_safety_violations` can't be used during
+/// type collection.
+pub fn astconv_object_safety_violations(
+ tcx: TyCtxt<'_>,
+ trait_def_id: DefId,
+) -> Vec<ObjectSafetyViolation> {
+ debug_assert!(tcx.generics_of(trait_def_id).has_self);
+ let violations = traits::supertrait_def_ids(tcx, trait_def_id)
+ .filter(|&def_id| predicates_reference_self(tcx, def_id, true))
+ .map(|_| ObjectSafetyViolation::SupertraitSelf)
+ .collect();
+
+ debug!("astconv_object_safety_violations(trait_def_id={:?}) = {:?}", trait_def_id, violations);
+
+ violations
+}
- violations
- }
+pub fn object_safety_violations(
+ tcx: TyCtxt<'_>,
+ trait_def_id: DefId,
+) -> Vec<ObjectSafetyViolation> {
+ debug_assert!(tcx.generics_of(trait_def_id).has_self);
+ debug!("object_safety_violations: {:?}", trait_def_id);
- pub fn object_safety_violations(self, trait_def_id: DefId) -> Vec<ObjectSafetyViolation> {
- debug_assert!(self.generics_of(trait_def_id).has_self);
- debug!("object_safety_violations: {:?}", trait_def_id);
+ traits::supertrait_def_ids(tcx, trait_def_id)
+ .flat_map(|def_id| object_safety_violations_for_trait(tcx, def_id))
+ .collect()
+}
- traits::supertrait_def_ids(self, trait_def_id)
- .flat_map(|def_id| self.object_safety_violations_for_trait(def_id))
- .collect()
+/// We say a method is *vtable safe* if it can be invoked on a trait
+/// object. Note that object-safe traits can have some
+/// non-vtable-safe methods, so long as they require `Self: Sized` or
+/// otherwise ensure that they cannot be used when `Self = Trait`.
+pub fn is_vtable_safe_method(tcx: TyCtxt<'_>, trait_def_id: DefId, method: &ty::AssocItem) -> bool {
+ debug_assert!(tcx.generics_of(trait_def_id).has_self);
+ debug!("is_vtable_safe_method({:?}, {:?})", trait_def_id, method);
+ // Any method that has a `Self: Sized` bound cannot be called.
+ if generics_require_sized_self(tcx, method.def_id) {
+ return false;
}
- /// We say a method is *vtable safe* if it can be invoked on a trait
- /// object. Note that object-safe traits can have some
- /// non-vtable-safe methods, so long as they require `Self: Sized` or
- /// otherwise ensure that they cannot be used when `Self = Trait`.
- pub fn is_vtable_safe_method(self, trait_def_id: DefId, method: &ty::AssocItem) -> bool {
- debug_assert!(self.generics_of(trait_def_id).has_self);
- debug!("is_vtable_safe_method({:?}, {:?})", trait_def_id, method);
- // Any method that has a `Self: Sized` bound cannot be called.
- if self.generics_require_sized_self(method.def_id) {
- return false;
- }
-
- match self.virtual_call_violation_for_method(trait_def_id, method) {
- None | Some(MethodViolationCode::WhereClauseReferencesSelf) => true,
- Some(_) => false,
- }
+ match virtual_call_violation_for_method(tcx, trait_def_id, method) {
+ None | Some(MethodViolationCode::WhereClauseReferencesSelf) => true,
+ Some(_) => false,
}
+}
- fn object_safety_violations_for_trait(self, trait_def_id: DefId) -> Vec<ObjectSafetyViolation> {
- // Check methods for violations.
- let mut violations: Vec<_> = self
- .associated_items(trait_def_id)
- .filter(|item| item.kind == ty::AssocKind::Method)
- .filter_map(|item| {
- self.object_safety_violation_for_method(trait_def_id, &item).map(|code| {
- ObjectSafetyViolation::Method(item.ident.name, code, item.ident.span)
- })
- })
- .filter(|violation| {
- if let ObjectSafetyViolation::Method(
- _,
- MethodViolationCode::WhereClauseReferencesSelf,
- span,
- ) = violation
- {
- // Using `CRATE_NODE_ID` is wrong, but it's hard to get a more precise id.
- // It's also hard to get a use site span, so we use the method definition span.
- self.lint_node_note(
- lint::builtin::WHERE_CLAUSES_OBJECT_SAFETY,
- hir::CRATE_HIR_ID,
- *span,
- &format!(
- "the trait `{}` cannot be made into an object",
- self.def_path_str(trait_def_id)
- ),
- &violation.error_msg(),
- );
- false
- } else {
- true
- }
- })
- .collect();
-
- // Check the trait itself.
- if self.trait_has_sized_self(trait_def_id) {
- violations.push(ObjectSafetyViolation::SizedSelf);
- }
- if self.predicates_reference_self(trait_def_id, false) {
- violations.push(ObjectSafetyViolation::SupertraitSelf);
- }
-
- violations.extend(
- self.associated_items(trait_def_id)
- .filter(|item| item.kind == ty::AssocKind::Const)
- .map(|item| ObjectSafetyViolation::AssocConst(item.ident.name, item.ident.span)),
- );
-
- debug!(
- "object_safety_violations_for_trait(trait_def_id={:?}) = {:?}",
- trait_def_id, violations
- );
+fn object_safety_violations_for_trait(
+ tcx: TyCtxt<'_>,
+ trait_def_id: DefId,
+) -> Vec<ObjectSafetyViolation> {
+ // Check methods for violations.
+ let mut violations: Vec<_> = tcx
+ .associated_items(trait_def_id)
+ .filter(|item| item.kind == ty::AssocKind::Method)
+ .filter_map(|item| {
+ object_safety_violation_for_method(tcx, trait_def_id, &item)
+ .map(|code| ObjectSafetyViolation::Method(item.ident.name, code, item.ident.span))
+ })
+ .filter(|violation| {
+ if let ObjectSafetyViolation::Method(
+ _,
+ MethodViolationCode::WhereClauseReferencesSelf,
+ span,
+ ) = violation
+ {
+ // Using `CRATE_NODE_ID` is wrong, but it's hard to get a more precise id.
+ // It's also hard to get a use site span, so we use the method definition span.
+ tcx.struct_span_lint_hir(
+ WHERE_CLAUSES_OBJECT_SAFETY,
+ hir::CRATE_HIR_ID,
+ *span,
+ &format!(
+ "the trait `{}` cannot be made into an object",
+ tcx.def_path_str(trait_def_id)
+ ),
+ )
+ .note(&violation.error_msg())
+ .emit();
+ false
+ } else {
+ true
+ }
+ })
+ .collect();
- violations
+ // Check the trait itself.
+ if trait_has_sized_self(tcx, trait_def_id) {
+ violations.push(ObjectSafetyViolation::SizedSelf);
}
-
- fn predicates_reference_self(self, trait_def_id: DefId, supertraits_only: bool) -> bool {
- let trait_ref = ty::Binder::dummy(ty::TraitRef::identity(self, trait_def_id));
- let predicates = if supertraits_only {
- self.super_predicates_of(trait_def_id)
- } else {
- self.predicates_of(trait_def_id)
- };
- let self_ty = self.types.self_param;
- let has_self_ty = |t: Ty<'tcx>| t.walk().any(|t| t == self_ty);
- predicates
- .predicates
- .iter()
- .map(|(predicate, _)| predicate.subst_supertrait(self, &trait_ref))
- .any(|predicate| {
- match predicate {
- ty::Predicate::Trait(ref data) => {
- // In the case of a trait predicate, we can skip the "self" type.
- data.skip_binder().input_types().skip(1).any(has_self_ty)
- }
- ty::Predicate::Projection(ref data) => {
- // And similarly for projections. This should be redundant with
- // the previous check because any projection should have a
- // matching `Trait` predicate with the same inputs, but we do
- // the check to be safe.
- //
- // Note that we *do* allow projection *outputs* to contain
- // `self` (i.e., `trait Foo: Bar<Output=Self::Result> { type Result; }`),
- // we just require the user to specify *both* outputs
- // in the object type (i.e., `dyn Foo<Output=(), Result=()>`).
- //
- // This is ALT2 in issue #56288, see that for discussion of the
- // possible alternatives.
- data.skip_binder()
- .projection_ty
- .trait_ref(self)
- .input_types()
- .skip(1)
- .any(has_self_ty)
- }
- ty::Predicate::WellFormed(..)
- | ty::Predicate::ObjectSafe(..)
- | ty::Predicate::TypeOutlives(..)
- | ty::Predicate::RegionOutlives(..)
- | ty::Predicate::ClosureKind(..)
- | ty::Predicate::Subtype(..)
- | ty::Predicate::ConstEvaluatable(..) => false,
- }
- })
+ if predicates_reference_self(tcx, trait_def_id, false) {
+ violations.push(ObjectSafetyViolation::SupertraitSelf);
}
- fn trait_has_sized_self(self, trait_def_id: DefId) -> bool {
- self.generics_require_sized_self(trait_def_id)
- }
+ violations.extend(
+ tcx.associated_items(trait_def_id)
+ .filter(|item| item.kind == ty::AssocKind::Const)
+ .map(|item| ObjectSafetyViolation::AssocConst(item.ident.name, item.ident.span)),
+ );
- fn generics_require_sized_self(self, def_id: DefId) -> bool {
- let sized_def_id = match self.lang_items().sized_trait() {
- Some(def_id) => def_id,
- None => {
- return false; /* No Sized trait, can't require it! */
- }
- };
+ debug!(
+ "object_safety_violations_for_trait(trait_def_id={:?}) = {:?}",
+ trait_def_id, violations
+ );
- // Search for a predicate like `Self : Sized` amongst the trait bounds.
- let predicates = self.predicates_of(def_id);
- let predicates = predicates.instantiate_identity(self).predicates;
- elaborate_predicates(self, predicates).any(|predicate| match predicate {
- ty::Predicate::Trait(ref trait_pred) => {
- trait_pred.def_id() == sized_def_id
- && trait_pred.skip_binder().self_ty().is_param(0)
+ violations
+}
+
+fn predicates_reference_self(tcx: TyCtxt<'_>, trait_def_id: DefId, supertraits_only: bool) -> bool {
+ let trait_ref = ty::Binder::dummy(ty::TraitRef::identity(tcx, trait_def_id));
+ let predicates = if supertraits_only {
+ tcx.super_predicates_of(trait_def_id)
+ } else {
+ tcx.predicates_of(trait_def_id)
+ };
+ let self_ty = tcx.types.self_param;
+ let has_self_ty = |t: Ty<'_>| t.walk().any(|t| t == self_ty);
+ predicates
+ .predicates
+ .iter()
+ .map(|(predicate, _)| predicate.subst_supertrait(tcx, &trait_ref))
+ .any(|predicate| {
+ match predicate {
+ ty::Predicate::Trait(ref data) => {
+ // In the case of a trait predicate, we can skip the "self" type.
+ data.skip_binder().input_types().skip(1).any(has_self_ty)
+ }
+ ty::Predicate::Projection(ref data) => {
+ // And similarly for projections. This should be redundant with
+ // the previous check because any projection should have a
+ // matching `Trait` predicate with the same inputs, but we do
+ // the check to be safe.
+ //
+ // Note that we *do* allow projection *outputs* to contain
+ // `self` (i.e., `trait Foo: Bar<Output=Self::Result> { type Result; }`),
+ // we just require the user to specify *both* outputs
+ // in the object type (i.e., `dyn Foo<Output=(), Result=()>`).
+ //
+ // This is ALT2 in issue #56288, see that for discussion of the
+ // possible alternatives.
+ data.skip_binder()
+ .projection_ty
+ .trait_ref(tcx)
+ .input_types()
+ .skip(1)
+ .any(has_self_ty)
+ }
+ ty::Predicate::WellFormed(..)
+ | ty::Predicate::ObjectSafe(..)
+ | ty::Predicate::TypeOutlives(..)
+ | ty::Predicate::RegionOutlives(..)
+ | ty::Predicate::ClosureKind(..)
+ | ty::Predicate::Subtype(..)
+ | ty::Predicate::ConstEvaluatable(..) => false,
}
- ty::Predicate::Projection(..)
- | ty::Predicate::Subtype(..)
- | ty::Predicate::RegionOutlives(..)
- | ty::Predicate::WellFormed(..)
- | ty::Predicate::ObjectSafe(..)
- | ty::Predicate::ClosureKind(..)
- | ty::Predicate::TypeOutlives(..)
- | ty::Predicate::ConstEvaluatable(..) => false,
})
- }
+}
+
+fn trait_has_sized_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool {
+ generics_require_sized_self(tcx, trait_def_id)
+}
- /// Returns `Some(_)` if this method makes the containing trait not object safe.
- fn object_safety_violation_for_method(
- self,
- trait_def_id: DefId,
- method: &ty::AssocItem,
- ) -> Option<MethodViolationCode> {
- debug!("object_safety_violation_for_method({:?}, {:?})", trait_def_id, method);
- // Any method that has a `Self : Sized` requisite is otherwise
- // exempt from the regulations.
- if self.generics_require_sized_self(method.def_id) {
- return None;
+fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
+ let sized_def_id = match tcx.lang_items().sized_trait() {
+ Some(def_id) => def_id,
+ None => {
+ return false; /* No Sized trait, can't require it! */
+ }
+ };
+
+ // Search for a predicate like `Self : Sized` amongst the trait bounds.
+ let predicates = tcx.predicates_of(def_id);
+ let predicates = predicates.instantiate_identity(tcx).predicates;
+ elaborate_predicates(tcx, predicates).any(|predicate| match predicate {
+ ty::Predicate::Trait(ref trait_pred) => {
+ trait_pred.def_id() == sized_def_id && trait_pred.skip_binder().self_ty().is_param(0)
}
+ ty::Predicate::Projection(..)
+ | ty::Predicate::Subtype(..)
+ | ty::Predicate::RegionOutlives(..)
+ | ty::Predicate::WellFormed(..)
+ | ty::Predicate::ObjectSafe(..)
+ | ty::Predicate::ClosureKind(..)
+ | ty::Predicate::TypeOutlives(..)
+ | ty::Predicate::ConstEvaluatable(..) => false,
+ })
+}
- self.virtual_call_violation_for_method(trait_def_id, method)
+/// Returns `Some(_)` if this method makes the containing trait not object safe.
+fn object_safety_violation_for_method(
+ tcx: TyCtxt<'_>,
+ trait_def_id: DefId,
+ method: &ty::AssocItem,
+) -> Option<MethodViolationCode> {
+ debug!("object_safety_violation_for_method({:?}, {:?})", trait_def_id, method);
+ // Any method that has a `Self : Sized` requisite is otherwise
+ // exempt from the regulations.
+ if generics_require_sized_self(tcx, method.def_id) {
+ return None;
}
- /// Returns `Some(_)` if this method cannot be called on a trait
- /// object; this does not necessarily imply that the enclosing trait
- /// is not object safe, because the method might have a where clause
- /// `Self:Sized`.
- fn virtual_call_violation_for_method(
- self,
- trait_def_id: DefId,
- method: &ty::AssocItem,
- ) -> Option<MethodViolationCode> {
- // The method's first parameter must be named `self`
- if !method.method_has_self_argument {
- return Some(MethodViolationCode::StaticMethod);
- }
+ virtual_call_violation_for_method(tcx, trait_def_id, method)
+}
- let sig = self.fn_sig(method.def_id);
+/// Returns `Some(_)` if this method cannot be called on a trait
+/// object; this does not necessarily imply that the enclosing trait
+/// is not object safe, because the method might have a where clause
+/// `Self:Sized`.
+fn virtual_call_violation_for_method<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ trait_def_id: DefId,
+ method: &ty::AssocItem,
+) -> Option<MethodViolationCode> {
+ // The method's first parameter must be named `self`
+ if !method.method_has_self_argument {
+ return Some(MethodViolationCode::StaticMethod);
+ }
- for input_ty in &sig.skip_binder().inputs()[1..] {
- if self.contains_illegal_self_type_reference(trait_def_id, input_ty) {
- return Some(MethodViolationCode::ReferencesSelf);
- }
- }
- if self.contains_illegal_self_type_reference(trait_def_id, sig.output().skip_binder()) {
+ let sig = tcx.fn_sig(method.def_id);
+
+ for input_ty in &sig.skip_binder().inputs()[1..] {
+ if contains_illegal_self_type_reference(tcx, trait_def_id, input_ty) {
return Some(MethodViolationCode::ReferencesSelf);
}
+ }
+ if contains_illegal_self_type_reference(tcx, trait_def_id, sig.output().skip_binder()) {
+ return Some(MethodViolationCode::ReferencesSelf);
+ }
- // We can't monomorphize things like `fn foo<A>(...)`.
- let own_counts = self.generics_of(method.def_id).own_counts();
- if own_counts.types + own_counts.consts != 0 {
- return Some(MethodViolationCode::Generic);
- }
+ // We can't monomorphize things like `fn foo<A>(...)`.
+ let own_counts = tcx.generics_of(method.def_id).own_counts();
+ if own_counts.types + own_counts.consts != 0 {
+ return Some(MethodViolationCode::Generic);
+ }
- if self
- .predicates_of(method.def_id)
- .predicates
- .iter()
- // A trait object can't claim to live more than the concrete type,
- // so outlives predicates will always hold.
- .cloned()
- .filter(|(p, _)| p.to_opt_type_outlives().is_none())
- .collect::<Vec<_>>()
- // Do a shallow visit so that `contains_illegal_self_type_reference`
- // may apply it's custom visiting.
- .visit_tys_shallow(|t| self.contains_illegal_self_type_reference(trait_def_id, t))
- {
- return Some(MethodViolationCode::WhereClauseReferencesSelf);
- }
+ if tcx
+ .predicates_of(method.def_id)
+ .predicates
+ .iter()
+ // A trait object can't claim to live more than the concrete type,
+ // so outlives predicates will always hold.
+ .cloned()
+ .filter(|(p, _)| p.to_opt_type_outlives().is_none())
+ .collect::<Vec<_>>()
+ // Do a shallow visit so that `contains_illegal_self_type_reference`
+ // may apply it's custom visiting.
+ .visit_tys_shallow(|t| contains_illegal_self_type_reference(tcx, trait_def_id, t))
+ {
+ return Some(MethodViolationCode::WhereClauseReferencesSelf);
+ }
- let receiver_ty =
- self.liberate_late_bound_regions(method.def_id, &sig.map_bound(|sig| sig.inputs()[0]));
+ let receiver_ty =
+ tcx.liberate_late_bound_regions(method.def_id, &sig.map_bound(|sig| sig.inputs()[0]));
- // Until `unsized_locals` is fully implemented, `self: Self` can't be dispatched on.
- // However, this is already considered object-safe. We allow it as a special case here.
- // FIXME(mikeyhew) get rid of this `if` statement once `receiver_is_dispatchable` allows
- // `Receiver: Unsize<Receiver[Self => dyn Trait]>`.
- if receiver_ty != self.types.self_param {
- if !self.receiver_is_dispatchable(method, receiver_ty) {
- return Some(MethodViolationCode::UndispatchableReceiver);
- } else {
- // Do sanity check to make sure the receiver actually has the layout of a pointer.
+ // Until `unsized_locals` is fully implemented, `self: Self` can't be dispatched on.
+ // However, this is already considered object-safe. We allow it as a special case here.
+ // FIXME(mikeyhew) get rid of this `if` statement once `receiver_is_dispatchable` allows
+ // `Receiver: Unsize<Receiver[Self => dyn Trait]>`.
+ if receiver_ty != tcx.types.self_param {
+ if !receiver_is_dispatchable(tcx, method, receiver_ty) {
+ return Some(MethodViolationCode::UndispatchableReceiver);
+ } else {
+ // Do sanity check to make sure the receiver actually has the layout of a pointer.
- use crate::ty::layout::Abi;
+ use crate::ty::layout::Abi;
- let param_env = self.param_env(method.def_id);
+ let param_env = tcx.param_env(method.def_id);
- let abi_of_ty = |ty: Ty<'tcx>| -> &Abi {
- match self.layout_of(param_env.and(ty)) {
- Ok(layout) => &layout.abi,
- Err(err) => {
- bug!("error: {}\n while computing layout for type {:?}", err, ty)
- }
- }
- };
-
- // e.g., `Rc<()>`
- let unit_receiver_ty =
- self.receiver_for_self_ty(receiver_ty, self.mk_unit(), method.def_id);
-
- match abi_of_ty(unit_receiver_ty) {
- &Abi::Scalar(..) => (),
- abi => {
- self.sess.delay_span_bug(
- self.def_span(method.def_id),
- &format!(
- "receiver when `Self = ()` should have a Scalar ABI; found {:?}",
- abi
- ),
- );
- }
+ let abi_of_ty = |ty: Ty<'tcx>| -> &Abi {
+ match tcx.layout_of(param_env.and(ty)) {
+ Ok(layout) => &layout.abi,
+ Err(err) => bug!("error: {}\n while computing layout for type {:?}", err, ty),
}
+ };
- let trait_object_ty =
- self.object_ty_for_trait(trait_def_id, self.mk_region(ty::ReStatic));
+ // e.g., `Rc<()>`
+ let unit_receiver_ty =
+ receiver_for_self_ty(tcx, receiver_ty, tcx.mk_unit(), method.def_id);
- // e.g., `Rc<dyn Trait>`
- let trait_object_receiver =
- self.receiver_for_self_ty(receiver_ty, trait_object_ty, method.def_id);
+ match abi_of_ty(unit_receiver_ty) {
+ &Abi::Scalar(..) => (),
+ abi => {
+ tcx.sess.delay_span_bug(
+ tcx.def_span(method.def_id),
+ &format!(
+ "receiver when `Self = ()` should have a Scalar ABI; found {:?}",
+ abi
+ ),
+ );
+ }
+ }
- match abi_of_ty(trait_object_receiver) {
- &Abi::ScalarPair(..) => (),
- abi => {
- self.sess.delay_span_bug(
- self.def_span(method.def_id),
- &format!(
- "receiver when `Self = {}` should have a ScalarPair ABI; \
+ let trait_object_ty =
+ object_ty_for_trait(tcx, trait_def_id, tcx.mk_region(ty::ReStatic));
+
+ // e.g., `Rc<dyn Trait>`
+ let trait_object_receiver =
+ receiver_for_self_ty(tcx, receiver_ty, trait_object_ty, method.def_id);
+
+ match abi_of_ty(trait_object_receiver) {
+ &Abi::ScalarPair(..) => (),
+ abi => {
+ tcx.sess.delay_span_bug(
+ tcx.def_span(method.def_id),
+ &format!(
+ "receiver when `Self = {}` should have a ScalarPair ABI; \
found {:?}",
- trait_object_ty, abi
- ),
- );
- }
+ trait_object_ty, abi
+ ),
+ );
}
}
}
-
- None
}
- /// Performs a type substitution to produce the version of `receiver_ty` when `Self = self_ty`.
- /// For example, for `receiver_ty = Rc<Self>` and `self_ty = Foo`, returns `Rc<Foo>`.
- fn receiver_for_self_ty(
- self,
- receiver_ty: Ty<'tcx>,
- self_ty: Ty<'tcx>,
- method_def_id: DefId,
- ) -> Ty<'tcx> {
- debug!("receiver_for_self_ty({:?}, {:?}, {:?})", receiver_ty, self_ty, method_def_id);
- let substs = InternalSubsts::for_item(self, method_def_id, |param, _| {
- if param.index == 0 { self_ty.into() } else { self.mk_param_from_def(param) }
- });
-
- let result = receiver_ty.subst(self, substs);
- debug!(
- "receiver_for_self_ty({:?}, {:?}, {:?}) = {:?}",
- receiver_ty, self_ty, method_def_id, result
- );
- result
- }
+ None
+}
- /// Creates the object type for the current trait. For example,
- /// if the current trait is `Deref`, then this will be
- /// `dyn Deref<Target = Self::Target> + 'static`.
- fn object_ty_for_trait(self, trait_def_id: DefId, lifetime: ty::Region<'tcx>) -> Ty<'tcx> {
- debug!("object_ty_for_trait: trait_def_id={:?}", trait_def_id);
-
- let trait_ref = ty::TraitRef::identity(self, trait_def_id);
-
- let trait_predicate = ty::ExistentialPredicate::Trait(
- ty::ExistentialTraitRef::erase_self_ty(self, trait_ref),
- );
-
- let mut associated_types = traits::supertraits(self, ty::Binder::dummy(trait_ref))
- .flat_map(|super_trait_ref| {
- self.associated_items(super_trait_ref.def_id())
- .map(move |item| (super_trait_ref, item))
- })
- .filter(|(_, item)| item.kind == ty::AssocKind::Type)
- .collect::<Vec<_>>();
-
- // existential predicates need to be in a specific order
- associated_types.sort_by_cached_key(|(_, item)| self.def_path_hash(item.def_id));
-
- let projection_predicates = associated_types.into_iter().map(|(super_trait_ref, item)| {
- // We *can* get bound lifetimes here in cases like
- // `trait MyTrait: for<'s> OtherTrait<&'s T, Output=bool>`.
- //
- // binder moved to (*)...
- let super_trait_ref = super_trait_ref.skip_binder();
- ty::ExistentialPredicate::Projection(ty::ExistentialProjection {
- ty: self.mk_projection(item.def_id, super_trait_ref.substs),
- item_def_id: item.def_id,
- substs: super_trait_ref.substs,
- })
- });
-
- let existential_predicates = self
- .mk_existential_predicates(iter::once(trait_predicate).chain(projection_predicates));
-
- let object_ty = self.mk_dynamic(
- // (*) ... binder re-introduced here
- ty::Binder::bind(existential_predicates),
- lifetime,
- );
-
- debug!("object_ty_for_trait: object_ty=`{}`", object_ty);
-
- object_ty
- }
+/// Performs a type substitution to produce the version of `receiver_ty` when `Self = self_ty`.
+/// For example, for `receiver_ty = Rc<Self>` and `self_ty = Foo`, returns `Rc<Foo>`.
+fn receiver_for_self_ty<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ receiver_ty: Ty<'tcx>,
+ self_ty: Ty<'tcx>,
+ method_def_id: DefId,
+) -> Ty<'tcx> {
+ debug!("receiver_for_self_ty({:?}, {:?}, {:?})", receiver_ty, self_ty, method_def_id);
+ let substs = InternalSubsts::for_item(tcx, method_def_id, |param, _| {
+ if param.index == 0 { self_ty.into() } else { tcx.mk_param_from_def(param) }
+ });
+
+ let result = receiver_ty.subst(tcx, substs);
+ debug!(
+ "receiver_for_self_ty({:?}, {:?}, {:?}) = {:?}",
+ receiver_ty, self_ty, method_def_id, result
+ );
+ result
+}
- /// Checks the method's receiver (the `self` argument) can be dispatched on when `Self` is a
- /// trait object. We require that `DispatchableFromDyn` be implemented for the receiver type
- /// in the following way:
- /// - let `Receiver` be the type of the `self` argument, i.e `Self`, `&Self`, `Rc<Self>`,
- /// - require the following bound:
- ///
- /// ```
- /// Receiver[Self => T]: DispatchFromDyn<Receiver[Self => dyn Trait]>
- /// ```
- ///
- /// where `Foo[X => Y]` means "the same type as `Foo`, but with `X` replaced with `Y`"
- /// (substitution notation).
- ///
- /// Some examples of receiver types and their required obligation:
- /// - `&'a mut self` requires `&'a mut Self: DispatchFromDyn<&'a mut dyn Trait>`,
- /// - `self: Rc<Self>` requires `Rc<Self>: DispatchFromDyn<Rc<dyn Trait>>`,
- /// - `self: Pin<Box<Self>>` requires `Pin<Box<Self>>: DispatchFromDyn<Pin<Box<dyn Trait>>>`.
- ///
- /// The only case where the receiver is not dispatchable, but is still a valid receiver
- /// type (just not object-safe), is when there is more than one level of pointer indirection.
- /// E.g., `self: &&Self`, `self: &Rc<Self>`, `self: Box<Box<Self>>`. In these cases, there
- /// is no way, or at least no inexpensive way, to coerce the receiver from the version where
- /// `Self = dyn Trait` to the version where `Self = T`, where `T` is the unknown erased type
- /// contained by the trait object, because the object that needs to be coerced is behind
- /// a pointer.
- ///
- /// In practice, we cannot use `dyn Trait` explicitly in the obligation because it would result
- /// in a new check that `Trait` is object safe, creating a cycle (until object_safe_for_dispatch
- /// is stabilized, see tracking issue https://github.com/rust-lang/rust/issues/43561).
- /// Instead, we fudge a little by introducing a new type parameter `U` such that
- /// `Self: Unsize<U>` and `U: Trait + ?Sized`, and use `U` in place of `dyn Trait`.
- /// Written as a chalk-style query:
- ///
- /// forall (U: Trait + ?Sized) {
- /// if (Self: Unsize<U>) {
- /// Receiver: DispatchFromDyn<Receiver[Self => U]>
- /// }
- /// }
- ///
- /// for `self: &'a mut Self`, this means `&'a mut Self: DispatchFromDyn<&'a mut U>`
- /// for `self: Rc<Self>`, this means `Rc<Self>: DispatchFromDyn<Rc<U>>`
- /// for `self: Pin<Box<Self>>`, this means `Pin<Box<Self>>: DispatchFromDyn<Pin<Box<U>>>`
- //
- // FIXME(mikeyhew) when unsized receivers are implemented as part of unsized rvalues, add this
- // fallback query: `Receiver: Unsize<Receiver[Self => U]>` to support receivers like
- // `self: Wrapper<Self>`.
- #[allow(dead_code)]
- fn receiver_is_dispatchable(self, method: &ty::AssocItem, receiver_ty: Ty<'tcx>) -> bool {
- debug!("receiver_is_dispatchable: method = {:?}, receiver_ty = {:?}", method, receiver_ty);
-
- let traits =
- (self.lang_items().unsize_trait(), self.lang_items().dispatch_from_dyn_trait());
- let (unsize_did, dispatch_from_dyn_did) = if let (Some(u), Some(cu)) = traits {
- (u, cu)
- } else {
- debug!("receiver_is_dispatchable: Missing Unsize or DispatchFromDyn traits");
- return false;
- };
+/// Creates the object type for the current trait. For example,
+/// if the current trait is `Deref`, then this will be
+/// `dyn Deref<Target = Self::Target> + 'static`.
+fn object_ty_for_trait<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ trait_def_id: DefId,
+ lifetime: ty::Region<'tcx>,
+) -> Ty<'tcx> {
+ debug!("object_ty_for_trait: trait_def_id={:?}", trait_def_id);
- // the type `U` in the query
- // use a bogus type parameter to mimick a forall(U) query using u32::MAX for now.
- // FIXME(mikeyhew) this is a total hack. Once object_safe_for_dispatch is stabilized, we can
- // replace this with `dyn Trait`
- let unsized_self_ty: Ty<'tcx> =
- self.mk_ty_param(::std::u32::MAX, Symbol::intern("RustaceansAreAwesome"));
-
- // `Receiver[Self => U]`
- let unsized_receiver_ty =
- self.receiver_for_self_ty(receiver_ty, unsized_self_ty, method.def_id);
-
- // create a modified param env, with `Self: Unsize<U>` and `U: Trait` added to caller bounds
- // `U: ?Sized` is already implied here
- let param_env = {
- let mut param_env = self.param_env(method.def_id);
-
- // Self: Unsize<U>
- let unsize_predicate = ty::TraitRef {
- def_id: unsize_did,
- substs: self.mk_substs_trait(self.types.self_param, &[unsized_self_ty.into()]),
- }
- .to_predicate();
-
- // U: Trait<Arg1, ..., ArgN>
- let trait_predicate = {
- let substs =
- InternalSubsts::for_item(self, method.container.assert_trait(), |param, _| {
- if param.index == 0 {
- unsized_self_ty.into()
- } else {
- self.mk_param_from_def(param)
- }
- });
-
- ty::TraitRef { def_id: unsize_did, substs }.to_predicate()
- };
+ let trait_ref = ty::TraitRef::identity(tcx, trait_def_id);
- let caller_bounds: Vec<Predicate<'tcx>> = param_env
- .caller_bounds
- .iter()
- .cloned()
- .chain(iter::once(unsize_predicate))
- .chain(iter::once(trait_predicate))
- .collect();
+ let trait_predicate =
+ ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref));
- param_env.caller_bounds = self.intern_predicates(&caller_bounds);
+ let mut associated_types = traits::supertraits(tcx, ty::Binder::dummy(trait_ref))
+ .flat_map(|super_trait_ref| {
+ tcx.associated_items(super_trait_ref.def_id()).map(move |item| (super_trait_ref, item))
+ })
+ .filter(|(_, item)| item.kind == ty::AssocKind::Type)
+ .collect::<Vec<_>>();
- param_env
- };
+ // existential predicates need to be in a specific order
+ associated_types.sort_by_cached_key(|(_, item)| tcx.def_path_hash(item.def_id));
- // Receiver: DispatchFromDyn<Receiver[Self => U]>
- let obligation = {
- let predicate = ty::TraitRef {
- def_id: dispatch_from_dyn_did,
- substs: self.mk_substs_trait(receiver_ty, &[unsized_receiver_ty.into()]),
- }
- .to_predicate();
+ let projection_predicates = associated_types.into_iter().map(|(super_trait_ref, item)| {
+ // We *can* get bound lifetimes here in cases like
+ // `trait MyTrait: for<'s> OtherTrait<&'s T, Output=bool>`.
+ //
+ // binder moved to (*)...
+ let super_trait_ref = super_trait_ref.skip_binder();
+ ty::ExistentialPredicate::Projection(ty::ExistentialProjection {
+ ty: tcx.mk_projection(item.def_id, super_trait_ref.substs),
+ item_def_id: item.def_id,
+ substs: super_trait_ref.substs,
+ })
+ });
- Obligation::new(ObligationCause::dummy(), param_env, predicate)
- };
+ let existential_predicates =
+ tcx.mk_existential_predicates(iter::once(trait_predicate).chain(projection_predicates));
- self.infer_ctxt().enter(|ref infcx| {
- // the receiver is dispatchable iff the obligation holds
- infcx.predicate_must_hold_modulo_regions(&obligation)
- })
- }
+ let object_ty = tcx.mk_dynamic(
+ // (*) ... binder re-introduced here
+ ty::Binder::bind(existential_predicates),
+ lifetime,
+ );
- fn contains_illegal_self_type_reference(self, trait_def_id: DefId, ty: Ty<'tcx>) -> bool {
- // This is somewhat subtle. In general, we want to forbid
- // references to `Self` in the argument and return types,
- // since the value of `Self` is erased. However, there is one
- // exception: it is ok to reference `Self` in order to access
- // an associated type of the current trait, since we retain
- // the value of those associated types in the object type
- // itself.
- //
- // ```rust
- // trait SuperTrait {
- // type X;
- // }
- //
- // trait Trait : SuperTrait {
- // type Y;
- // fn foo(&self, x: Self) // bad
- // fn foo(&self) -> Self // bad
- // fn foo(&self) -> Option<Self> // bad
- // fn foo(&self) -> Self::Y // OK, desugars to next example
- // fn foo(&self) -> <Self as Trait>::Y // OK
- // fn foo(&self) -> Self::X // OK, desugars to next example
- // fn foo(&self) -> <Self as SuperTrait>::X // OK
- // }
- // ```
- //
- // However, it is not as simple as allowing `Self` in a projected
- // type, because there are illegal ways to use `Self` as well:
- //
- // ```rust
- // trait Trait : SuperTrait {
- // ...
- // fn foo(&self) -> <Self as SomeOtherTrait>::X;
- // }
- // ```
- //
- // Here we will not have the type of `X` recorded in the
- // object type, and we cannot resolve `Self as SomeOtherTrait`
- // without knowing what `Self` is.
-
- let mut supertraits: Option<Vec<ty::PolyTraitRef<'tcx>>> = None;
- let mut error = false;
- let self_ty = self.types.self_param;
- ty.maybe_walk(|ty| {
- match ty.kind {
- ty::Param(_) => {
- if ty == self_ty {
- error = true;
+ debug!("object_ty_for_trait: object_ty=`{}`", object_ty);
+
+ object_ty
+}
+
+/// Checks the method's receiver (the `self` argument) can be dispatched on when `Self` is a
+/// trait object. We require that `DispatchableFromDyn` be implemented for the receiver type
+/// in the following way:
+/// - let `Receiver` be the type of the `self` argument, i.e `Self`, `&Self`, `Rc<Self>`,
+/// - require the following bound:
+///
+/// ```
+/// Receiver[Self => T]: DispatchFromDyn<Receiver[Self => dyn Trait]>
+/// ```
+///
+/// where `Foo[X => Y]` means "the same type as `Foo`, but with `X` replaced with `Y`"
+/// (substitution notation).
+///
+/// Some examples of receiver types and their required obligation:
+/// - `&'a mut self` requires `&'a mut Self: DispatchFromDyn<&'a mut dyn Trait>`,
+/// - `self: Rc<Self>` requires `Rc<Self>: DispatchFromDyn<Rc<dyn Trait>>`,
+/// - `self: Pin<Box<Self>>` requires `Pin<Box<Self>>: DispatchFromDyn<Pin<Box<dyn Trait>>>`.
+///
+/// The only case where the receiver is not dispatchable, but is still a valid receiver
+/// type (just not object-safe), is when there is more than one level of pointer indirection.
+/// E.g., `self: &&Self`, `self: &Rc<Self>`, `self: Box<Box<Self>>`. In these cases, there
+/// is no way, or at least no inexpensive way, to coerce the receiver from the version where
+/// `Self = dyn Trait` to the version where `Self = T`, where `T` is the unknown erased type
+/// contained by the trait object, because the object that needs to be coerced is behind
+/// a pointer.
+///
+/// In practice, we cannot use `dyn Trait` explicitly in the obligation because it would result
+/// in a new check that `Trait` is object safe, creating a cycle (until object_safe_for_dispatch
+/// is stabilized, see tracking issue https://github.com/rust-lang/rust/issues/43561).
+/// Instead, we fudge a little by introducing a new type parameter `U` such that
+/// `Self: Unsize<U>` and `U: Trait + ?Sized`, and use `U` in place of `dyn Trait`.
+/// Written as a chalk-style query:
+///
+/// forall (U: Trait + ?Sized) {
+/// if (Self: Unsize<U>) {
+/// Receiver: DispatchFromDyn<Receiver[Self => U]>
+/// }
+/// }
+///
+/// for `self: &'a mut Self`, this means `&'a mut Self: DispatchFromDyn<&'a mut U>`
+/// for `self: Rc<Self>`, this means `Rc<Self>: DispatchFromDyn<Rc<U>>`
+/// for `self: Pin<Box<Self>>`, this means `Pin<Box<Self>>: DispatchFromDyn<Pin<Box<U>>>`
+//
+// FIXME(mikeyhew) when unsized receivers are implemented as part of unsized rvalues, add this
+// fallback query: `Receiver: Unsize<Receiver[Self => U]>` to support receivers like
+// `self: Wrapper<Self>`.
+#[allow(dead_code)]
+fn receiver_is_dispatchable<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ method: &ty::AssocItem,
+ receiver_ty: Ty<'tcx>,
+) -> bool {
+ debug!("receiver_is_dispatchable: method = {:?}, receiver_ty = {:?}", method, receiver_ty);
+
+ let traits = (tcx.lang_items().unsize_trait(), tcx.lang_items().dispatch_from_dyn_trait());
+ let (unsize_did, dispatch_from_dyn_did) = if let (Some(u), Some(cu)) = traits {
+ (u, cu)
+ } else {
+ debug!("receiver_is_dispatchable: Missing Unsize or DispatchFromDyn traits");
+ return false;
+ };
+
+ // the type `U` in the query
+ // use a bogus type parameter to mimick a forall(U) query using u32::MAX for now.
+ // FIXME(mikeyhew) this is a total hack. Once object_safe_for_dispatch is stabilized, we can
+ // replace this with `dyn Trait`
+ let unsized_self_ty: Ty<'tcx> =
+ tcx.mk_ty_param(::std::u32::MAX, Symbol::intern("RustaceansAreAwesome"));
+
+ // `Receiver[Self => U]`
+ let unsized_receiver_ty =
+ receiver_for_self_ty(tcx, receiver_ty, unsized_self_ty, method.def_id);
+
+ // create a modified param env, with `Self: Unsize<U>` and `U: Trait` added to caller bounds
+ // `U: ?Sized` is already implied here
+ let param_env = {
+ let mut param_env = tcx.param_env(method.def_id);
+
+ // Self: Unsize<U>
+ let unsize_predicate = ty::TraitRef {
+ def_id: unsize_did,
+ substs: tcx.mk_substs_trait(tcx.types.self_param, &[unsized_self_ty.into()]),
+ }
+ .to_predicate();
+
+ // U: Trait<Arg1, ..., ArgN>
+ let trait_predicate = {
+ let substs =
+ InternalSubsts::for_item(tcx, method.container.assert_trait(), |param, _| {
+ if param.index == 0 {
+ unsized_self_ty.into()
+ } else {
+ tcx.mk_param_from_def(param)
}
+ });
+
+ ty::TraitRef { def_id: unsize_did, substs }.to_predicate()
+ };
+
+ let caller_bounds: Vec<Predicate<'tcx>> = param_env
+ .caller_bounds
+ .iter()
+ .cloned()
+ .chain(iter::once(unsize_predicate))
+ .chain(iter::once(trait_predicate))
+ .collect();
+
+ param_env.caller_bounds = tcx.intern_predicates(&caller_bounds);
+
+ param_env
+ };
+
+ // Receiver: DispatchFromDyn<Receiver[Self => U]>
+ let obligation = {
+ let predicate = ty::TraitRef {
+ def_id: dispatch_from_dyn_did,
+ substs: tcx.mk_substs_trait(receiver_ty, &[unsized_receiver_ty.into()]),
+ }
+ .to_predicate();
+
+ Obligation::new(ObligationCause::dummy(), param_env, predicate)
+ };
- false // no contained types to walk
+ tcx.infer_ctxt().enter(|ref infcx| {
+ // the receiver is dispatchable iff the obligation holds
+ infcx.predicate_must_hold_modulo_regions(&obligation)
+ })
+}
+
+fn contains_illegal_self_type_reference<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ trait_def_id: DefId,
+ ty: Ty<'tcx>,
+) -> bool {
+ // This is somewhat subtle. In general, we want to forbid
+ // references to `Self` in the argument and return types,
+ // since the value of `Self` is erased. However, there is one
+ // exception: it is ok to reference `Self` in order to access
+ // an associated type of the current trait, since we retain
+ // the value of those associated types in the object type
+ // itself.
+ //
+ // ```rust
+ // trait SuperTrait {
+ // type X;
+ // }
+ //
+ // trait Trait : SuperTrait {
+ // type Y;
+ // fn foo(&self, x: Self) // bad
+ // fn foo(&self) -> Self // bad
+ // fn foo(&self) -> Option<Self> // bad
+ // fn foo(&self) -> Self::Y // OK, desugars to next example
+ // fn foo(&self) -> <Self as Trait>::Y // OK
+ // fn foo(&self) -> Self::X // OK, desugars to next example
+ // fn foo(&self) -> <Self as SuperTrait>::X // OK
+ // }
+ // ```
+ //
+ // However, it is not as simple as allowing `Self` in a projected
+ // type, because there are illegal ways to use `Self` as well:
+ //
+ // ```rust
+ // trait Trait : SuperTrait {
+ // ...
+ // fn foo(&self) -> <Self as SomeOtherTrait>::X;
+ // }
+ // ```
+ //
+ // Here we will not have the type of `X` recorded in the
+ // object type, and we cannot resolve `Self as SomeOtherTrait`
+ // without knowing what `Self` is.
+
+ let mut supertraits: Option<Vec<ty::PolyTraitRef<'tcx>>> = None;
+ let mut error = false;
+ let self_ty = tcx.types.self_param;
+ ty.maybe_walk(|ty| {
+ match ty.kind {
+ ty::Param(_) => {
+ if ty == self_ty {
+ error = true;
}
- ty::Projection(ref data) => {
- // This is a projected type `<Foo as SomeTrait>::X`.
+ false // no contained types to walk
+ }
- // Compute supertraits of current trait lazily.
- if supertraits.is_none() {
- let trait_ref =
- ty::Binder::bind(ty::TraitRef::identity(self, trait_def_id));
- supertraits = Some(traits::supertraits(self, trait_ref).collect());
- }
+ ty::Projection(ref data) => {
+ // This is a projected type `<Foo as SomeTrait>::X`.
- // Determine whether the trait reference `Foo as
- // SomeTrait` is in fact a supertrait of the
- // current trait. In that case, this type is
- // legal, because the type `X` will be specified
- // in the object type. Note that we can just use
- // direct equality here because all of these types
- // are part of the formal parameter listing, and
- // hence there should be no inference variables.
- let projection_trait_ref = ty::Binder::bind(data.trait_ref(self));
- let is_supertrait_of_current_trait =
- supertraits.as_ref().unwrap().contains(&projection_trait_ref);
-
- if is_supertrait_of_current_trait {
- false // do not walk contained types, do not report error, do collect $200
- } else {
- true // DO walk contained types, POSSIBLY reporting an error
- }
+ // Compute supertraits of current trait lazily.
+ if supertraits.is_none() {
+ let trait_ref = ty::Binder::bind(ty::TraitRef::identity(tcx, trait_def_id));
+ supertraits = Some(traits::supertraits(tcx, trait_ref).collect());
}
- _ => true, // walk contained types, if any
+ // Determine whether the trait reference `Foo as
+ // SomeTrait` is in fact a supertrait of the
+ // current trait. In that case, this type is
+ // legal, because the type `X` will be specified
+ // in the object type. Note that we can just use
+ // direct equality here because all of these types
+ // are part of the formal parameter listing, and
+ // hence there should be no inference variables.
+ let projection_trait_ref = ty::Binder::bind(data.trait_ref(tcx));
+ let is_supertrait_of_current_trait =
+ supertraits.as_ref().unwrap().contains(&projection_trait_ref);
+
+ if is_supertrait_of_current_trait {
+ false // do not walk contained types, do not report error, do collect $200
+ } else {
+ true // DO walk contained types, POSSIBLY reporting an error
+ }
}
- });
- error
- }
+ _ => true, // walk contained types, if any
+ }
+ });
+
+ error
}
pub(super) fn is_object_safe_provider(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool {
- tcx.object_safety_violations(trait_def_id).is_empty()
+ object_safety_violations(tcx, trait_def_id).is_empty()
}
use crate::ty::{self, GenericParamDefKind, TyCtxt};
use crate::util::common::ErrorReported;
+
use rustc_data_structures::fx::FxHashMap;
+use rustc_errors::struct_span_err;
use rustc_hir::def_id::DefId;
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_span::Span;
match generics.params.iter().find(|param| param.name == s) {
Some(_) => (),
None => {
- span_err!(
+ struct_span_err!(
tcx.sess,
span,
E0230,
"there is no parameter `{}` on trait `{}`",
s,
name
- );
+ )
+ .emit();
result = Err(ErrorReported);
}
}
}
// `{:1}` and `{}` are not to be used
Position::ArgumentIs(_) | Position::ArgumentImplicitlyIs(_) => {
- span_err!(
+ struct_span_err!(
tcx.sess,
span,
E0231,
"only named substitution parameters are allowed"
- );
+ )
+ .emit();
result = Err(ErrorReported);
}
},
use crate::ty::fold::{TypeFoldable, TypeFolder};
use crate::ty::subst::{InternalSubsts, Subst};
use crate::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt};
-use crate::util::common::FN_OUTPUT_NAME;
use rustc_data_structures::snapshot_map::{Snapshot, SnapshotMap};
use rustc_hir::def_id::DefId;
use rustc_macros::HashStable;
node_item.item.defaultness.has_value()
} else {
node_item.item.defaultness.is_default()
- || selcx.tcx().impl_is_default(node_item.node.def_id())
+ || super::util::impl_is_default(selcx.tcx(), node_item.node.def_id())
};
// Only reveal a specializable default if we're past type-checking
let gen_def_id = tcx.lang_items().gen_trait().unwrap();
- let predicate = tcx
- .generator_trait_ref_and_outputs(gen_def_id, obligation.predicate.self_ty(), gen_sig)
- .map_bound(|(trait_ref, yield_ty, return_ty)| {
- let name = tcx.associated_item(obligation.predicate.item_def_id).ident.name;
- let ty = if name == sym::Return {
- return_ty
- } else if name == sym::Yield {
- yield_ty
- } else {
- bug!()
- };
+ let predicate = super::util::generator_trait_ref_and_outputs(
+ tcx,
+ gen_def_id,
+ obligation.predicate.self_ty(),
+ gen_sig,
+ )
+ .map_bound(|(trait_ref, yield_ty, return_ty)| {
+ let name = tcx.associated_item(obligation.predicate.item_def_id).ident.name;
+ let ty = if name == sym::Return {
+ return_ty
+ } else if name == sym::Yield {
+ yield_ty
+ } else {
+ bug!()
+ };
- ty::ProjectionPredicate {
- projection_ty: ty::ProjectionTy {
- substs: trait_ref.substs,
- item_def_id: obligation.predicate.item_def_id,
- },
- ty: ty,
- }
- });
+ ty::ProjectionPredicate {
+ projection_ty: ty::ProjectionTy {
+ substs: trait_ref.substs,
+ item_def_id: obligation.predicate.item_def_id,
+ },
+ ty: ty,
+ }
+ });
confirm_param_env_candidate(selcx, obligation, predicate)
.with_addl_obligations(vtable.nested)
// the `Output` associated type is declared on `FnOnce`
let fn_once_def_id = tcx.lang_items().fn_once_trait().unwrap();
- let predicate = tcx
- .closure_trait_ref_and_return_type(
- fn_once_def_id,
- obligation.predicate.self_ty(),
- fn_sig,
- flag,
- )
- .map_bound(|(trait_ref, ret_type)| ty::ProjectionPredicate {
- projection_ty: ty::ProjectionTy::from_ref_and_name(
- tcx,
- trait_ref,
- Ident::with_dummy_span(FN_OUTPUT_NAME),
- ),
- ty: ret_type,
- });
+ let predicate = super::util::closure_trait_ref_and_return_type(
+ tcx,
+ fn_once_def_id,
+ obligation.predicate.self_ty(),
+ fn_sig,
+ flag,
+ )
+ .map_bound(|(trait_ref, ret_type)| ty::ProjectionPredicate {
+ projection_ty: ty::ProjectionTy::from_ref_and_name(
+ tcx,
+ trait_ref,
+ Ident::with_dummy_span(rustc_hir::FN_OUTPUT_NAME),
+ ),
+ ty: ret_type,
+ });
confirm_param_env_candidate(selcx, obligation, predicate)
}
// cycle error if the specialization graph is currently being built.
let impl_node = specialization_graph::Node::Impl(impl_def_id);
for item in impl_node.items(tcx) {
- if item.kind == ty::AssocKind::Type
+ if matches!(item.kind, ty::AssocKind::Type | ty::AssocKind::OpaqueTy)
&& tcx.hygienic_eq(item.ident, assoc_ty_name, trait_def_id)
{
return specialization_graph::NodeItem {
impl<'tcx> DropckOutlivesResult<'tcx> {
pub fn report_overflows(&self, tcx: TyCtxt<'tcx>, span: Span, ty: Ty<'tcx>) {
if let Some(overflow_ty) = self.overflows.iter().next() {
- let mut err = struct_span_err!(
+ rustc_errors::struct_span_err!(
tcx.sess,
span,
E0320,
"overflow while adding drop-check rules for {}",
ty,
- );
- err.note(&format!("overflowed on {}", overflow_ty));
- err.emit();
+ )
+ .note(&format!("overflowed on {}", overflow_ty))
+ .emit();
}
}
pub mod evaluate_obligation;
pub mod method_autoderef;
pub mod normalize;
-pub mod normalize_erasing_regions;
pub mod outlives_bounds;
pub mod type_op;
+++ /dev/null
-//! Methods for normalizing when you don't care about regions (and
-//! aren't doing type inference). If either of those things don't
-//! apply to you, use `infcx.normalize(...)`.
-//!
-//! The methods in this file use a `TypeFolder` to recursively process
-//! contents, invoking the underlying
-//! `normalize_ty_after_erasing_regions` query for each type found
-//! within. (This underlying query is what is cached.)
-
-use crate::ty::fold::{TypeFoldable, TypeFolder};
-use crate::ty::{self, Ty, TyCtxt};
-
-impl<'tcx> TyCtxt<'tcx> {
- /// Erase the regions in `value` and then fully normalize all the
- /// types found within. The result will also have regions erased.
- ///
- /// This is appropriate to use only after type-check: it assumes
- /// that normalization will succeed, for example.
- pub fn normalize_erasing_regions<T>(self, param_env: ty::ParamEnv<'tcx>, value: T) -> T
- where
- T: TypeFoldable<'tcx>,
- {
- debug!(
- "normalize_erasing_regions::<{}>(value={:?}, param_env={:?})",
- ::std::any::type_name::<T>(),
- value,
- param_env,
- );
-
- // Erase first before we do the real query -- this keeps the
- // cache from being too polluted.
- let value = self.erase_regions(&value);
- if !value.has_projections() {
- value
- } else {
- value.fold_with(&mut NormalizeAfterErasingRegionsFolder {
- tcx: self,
- param_env: param_env,
- })
- }
- }
-
- /// If you have a `Binder<T>`, you can do this to strip out the
- /// late-bound regions and then normalize the result, yielding up
- /// a `T` (with regions erased). This is appropriate when the
- /// binder is being instantiated at the call site.
- ///
- /// N.B., currently, higher-ranked type bounds inhibit
- /// normalization. Therefore, each time we erase them in
- /// codegen, we need to normalize the contents.
- pub fn normalize_erasing_late_bound_regions<T>(
- self,
- param_env: ty::ParamEnv<'tcx>,
- value: &ty::Binder<T>,
- ) -> T
- where
- T: TypeFoldable<'tcx>,
- {
- assert!(!value.needs_subst());
- let value = self.erase_late_bound_regions(value);
- self.normalize_erasing_regions(param_env, value)
- }
-}
-
-struct NormalizeAfterErasingRegionsFolder<'tcx> {
- tcx: TyCtxt<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
-}
-
-impl TypeFolder<'tcx> for NormalizeAfterErasingRegionsFolder<'tcx> {
- fn tcx(&self) -> TyCtxt<'tcx> {
- self.tcx
- }
-
- fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
- self.tcx.normalize_ty_after_erasing_regions(self.param_env.and(ty))
- }
-}
use super::project;
use super::project::{normalize_with_depth, Normalized, ProjectionCacheKey};
use super::util;
+use super::util::{closure_trait_ref_and_return_type, predicate_for_trait_def};
+use super::wf;
use super::DerivedObligationCause;
use super::Selection;
use super::SelectionResult;
impl IntercrateAmbiguityCause {
/// Emits notes when the overlap is caused by complex intercrate ambiguities.
/// See #23980 for details.
- pub fn add_intercrate_ambiguity_hint(&self, err: &mut errors::DiagnosticBuilder<'_>) {
+ pub fn add_intercrate_ambiguity_hint(&self, err: &mut rustc_errors::DiagnosticBuilder<'_>) {
err.note(&self.intercrate_ambiguity_hint());
}
}
}
- ty::Predicate::WellFormed(ty) => match ty::wf::obligations(
+ ty::Predicate::WellFormed(ty) => match wf::obligations(
self.infcx,
obligation.param_env,
obligation.cause.body_id,
ty::Predicate::ConstEvaluatable(def_id, substs) => {
if !(obligation.param_env, substs).has_local_value() {
- match self.tcx().const_eval_resolve(obligation.param_env, def_id, substs, None)
- {
+ match self.tcx().const_eval_resolve(
+ obligation.param_env,
+ def_id,
+ substs,
+ None,
+ None,
+ ) {
Ok(_) => Ok(EvaluatedToOk),
Err(_) => Ok(EvaluatedToErr),
}
/// to have a *lower* recursion_depth than the obligation used to create it.
/// Projection sub-obligations may be returned from the projection cache,
/// which results in obligations with an 'old' `recursion_depth`.
- /// Additionally, methods like `ty::wf::obligations` and
+ /// Additionally, methods like `wf::obligations` and
/// `InferCtxt.subtype_predicate` produce subobligations without
/// taking in a 'parent' depth, causing the generated subobligations
/// to have a `recursion_depth` of `0`.
recursion_depth,
&skol_ty,
);
- let skol_obligation = self.tcx().predicate_for_trait_def(
+ let skol_obligation = predicate_for_trait_def(
+ self.tcx(),
param_env,
cause.clone(),
trait_def_id,
// we pass over, we sum up the set of number of vtable
// entries, so that we can compute the offset for the selected
// trait.
- vtable_base = nonmatching.map(|t| tcx.count_own_vtable_entries(t)).sum();
+ vtable_base = nonmatching.map(|t| super::util::count_own_vtable_entries(tcx, t)).sum();
}
VtableObjectData { upcast_trait_ref: upcast_trait_ref.unwrap(), vtable_base, nested }
// Okay to skip binder; it is reintroduced below.
let self_ty = self.infcx.shallow_resolve(*obligation.self_ty().skip_binder());
let sig = self_ty.fn_sig(self.tcx());
- let trait_ref = self
- .tcx()
- .closure_trait_ref_and_return_type(
- obligation.predicate.def_id(),
- self_ty,
- sig,
- util::TupleArgumentsFlag::Yes,
- )
- .map_bound(|(trait_ref, _)| trait_ref);
+ let trait_ref = closure_trait_ref_and_return_type(
+ self.tcx(),
+ obligation.predicate.def_id(),
+ self_ty,
+ sig,
+ util::TupleArgumentsFlag::Yes,
+ )
+ .map_bound(|(trait_ref, _)| trait_ref);
let Normalized { value: trait_ref, obligations } = project::normalize_with_depth(
self,
nested.extend(obligations);
// Construct the nested `Field<T>: Unsize<Field<U>>` predicate.
- nested.push(tcx.predicate_for_trait_def(
+ nested.push(predicate_for_trait_def(
+ tcx,
obligation.param_env,
obligation.cause.clone(),
obligation.predicate.def_id(),
nested.extend(obligations);
// Construct the nested `T: Unsize<U>` predicate.
- nested.push(tcx.predicate_for_trait_def(
+ nested.push(predicate_for_trait_def(
+ tcx,
obligation.param_env,
obligation.cause.clone(),
obligation.predicate.def_id(),
// in fact unparameterized (or at least does not reference any
// regions bound in the obligation). Still probably some
// refactoring could make this nicer.
- self.tcx()
- .closure_trait_ref_and_return_type(
- obligation.predicate.def_id(),
- obligation.predicate.skip_binder().self_ty(), // (1)
- closure_type,
- util::TupleArgumentsFlag::No,
- )
- .map_bound(|(trait_ref, _)| trait_ref)
+ closure_trait_ref_and_return_type(
+ self.tcx(),
+ obligation.predicate.def_id(),
+ obligation.predicate.skip_binder().self_ty(), // (1)
+ closure_type,
+ util::TupleArgumentsFlag::No,
+ )
+ .map_bound(|(trait_ref, _)| trait_ref)
}
fn generator_trait_ref_unnormalized(
// regions bound in the obligation). Still probably some
// refactoring could make this nicer.
- self.tcx()
- .generator_trait_ref_and_outputs(
- obligation.predicate.def_id(),
- obligation.predicate.skip_binder().self_ty(), // (1)
- gen_sig,
- )
- .map_bound(|(trait_ref, ..)| trait_ref)
+ super::util::generator_trait_ref_and_outputs(
+ self.tcx(),
+ obligation.predicate.def_id(),
+ obligation.predicate.skip_binder().self_ty(), // (1)
+ gen_sig,
+ )
+ .map_bound(|(trait_ref, ..)| trait_ref)
}
/// Returns the obligations that are implied by instantiating an
pub mod specialization_graph;
use crate::infer::{InferCtxt, InferOk};
-use crate::lint;
use crate::traits::select::IntercrateAmbiguityCause;
use crate::traits::{self, coherence, FutureCompatOverlapErrorKind, ObligationCause, TraitEngine};
use crate::ty::subst::{InternalSubsts, Subst, SubstsRef};
use crate::ty::{self, TyCtxt, TypeFoldable};
use rustc_data_structures::fx::FxHashSet;
+use rustc_errors::struct_span_err;
use rustc_hir::def_id::DefId;
+use rustc_session::lint::builtin::ORDER_DEPENDENT_TRAIT_OBJECTS;
use rustc_span::DUMMY_SP;
use super::util::impl_trait_ref_and_oblig;
unreachable!("converted to hard error above")
}
FutureCompatOverlapErrorKind::Issue33140 => {
- lint::builtin::ORDER_DEPENDENT_TRAIT_OBJECTS
+ ORDER_DEPENDENT_TRAIT_OBJECTS
}
};
tcx.struct_span_lint_hir(
--- /dev/null
+use crate::ty::fold::{TypeFoldable, TypeVisitor};
+use crate::ty::{self, AdtDef, Ty, TyCtxt};
+
+use rustc::infer::InferCtxt;
+use rustc::traits::ObligationCause;
+use rustc::traits::{self, ConstPatternStructural, TraitEngine};
+use rustc_data_structures::fx::FxHashSet;
+use rustc_hir as hir;
+use rustc_span::Span;
+
+#[derive(Debug)]
+pub enum NonStructuralMatchTy<'tcx> {
+ Adt(&'tcx AdtDef),
+ Param,
+}
+
+/// This method traverses the structure of `ty`, trying to find an
+/// instance of an ADT (i.e. struct or enum) that was declared without
+/// the `#[structural_match]` attribute, or a generic type parameter
+/// (which cannot be determined to be `structural_match`).
+///
+/// The "structure of a type" includes all components that would be
+/// considered when doing a pattern match on a constant of that
+/// type.
+///
+/// * This means this method descends into fields of structs/enums,
+/// and also descends into the inner type `T` of `&T` and `&mut T`
+///
+/// * The traversal doesn't dereference unsafe pointers (`*const T`,
+/// `*mut T`), and it does not visit the type arguments of an
+/// instantiated generic like `PhantomData<T>`.
+///
+/// The reason we do this search is Rust currently require all ADTs
+/// reachable from a constant's type to be annotated with
+/// `#[structural_match]`, an attribute which essentially says that
+/// the implementation of `PartialEq::eq` behaves *equivalently* to a
+/// comparison against the unfolded structure.
+///
+/// For more background on why Rust has this requirement, and issues
+/// that arose when the requirement was not enforced completely, see
+/// Rust RFC 1445, rust-lang/rust#61188, and rust-lang/rust#62307.
+pub fn search_for_structural_match_violation<'tcx>(
+ id: hir::HirId,
+ span: Span,
+ tcx: TyCtxt<'tcx>,
+ ty: Ty<'tcx>,
+) -> Option<NonStructuralMatchTy<'tcx>> {
+ // FIXME: we should instead pass in an `infcx` from the outside.
+ tcx.infer_ctxt().enter(|infcx| {
+ let mut search = Search { id, span, infcx, found: None, seen: FxHashSet::default() };
+ ty.visit_with(&mut search);
+ search.found
+ })
+}
+
+/// 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.
+pub fn type_marked_structural(
+ id: hir::HirId,
+ span: Span,
+ infcx: &InferCtxt<'_, 'tcx>,
+ adt_ty: Ty<'tcx>,
+) -> bool {
+ let mut fulfillment_cx = traits::FulfillmentContext::new();
+ let cause = ObligationCause::new(span, id, ConstPatternStructural);
+ // require `#[derive(PartialEq)]`
+ let structural_peq_def_id = infcx.tcx.lang_items().structural_peq_trait().unwrap();
+ fulfillment_cx.register_bound(
+ infcx,
+ ty::ParamEnv::empty(),
+ adt_ty,
+ structural_peq_def_id,
+ cause,
+ );
+ // 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 cause = ObligationCause::new(span, id, ConstPatternStructural);
+ let structural_teq_def_id = infcx.tcx.lang_items().structural_teq_trait().unwrap();
+ fulfillment_cx.register_bound(
+ infcx,
+ ty::ParamEnv::empty(),
+ adt_ty,
+ structural_teq_def_id,
+ cause,
+ );
+
+ // 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.
+ fulfillment_cx.select_all_or_error(infcx).is_ok()
+}
+
+/// This implements the traversal over the structure of a given type to try to
+/// find instances of ADTs (specifically structs or enums) that do not implement
+/// the structural-match traits (`StructuralPartialEq` and `StructuralEq`).
+struct Search<'a, 'tcx> {
+ id: hir::HirId,
+ span: Span,
+
+ infcx: InferCtxt<'a, 'tcx>,
+
+ /// Records first ADT that does not implement a structural-match trait.
+ found: Option<NonStructuralMatchTy<'tcx>>,
+
+ /// Tracks ADTs previously encountered during search, so that
+ /// we will not recur on them again.
+ seen: FxHashSet<hir::def_id::DefId>,
+}
+
+impl Search<'a, 'tcx> {
+ fn tcx(&self) -> TyCtxt<'tcx> {
+ self.infcx.tcx
+ }
+
+ fn type_marked_structural(&self, adt_ty: Ty<'tcx>) -> bool {
+ type_marked_structural(self.id, self.span, &self.infcx, adt_ty)
+ }
+}
+
+impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> {
+ fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool {
+ debug!("Search visiting ty: {:?}", ty);
+
+ let (adt_def, substs) = match ty.kind {
+ ty::Adt(adt_def, substs) => (adt_def, substs),
+ ty::Param(_) => {
+ self.found = Some(NonStructuralMatchTy::Param);
+ return true; // Stop visiting.
+ }
+ ty::RawPtr(..) => {
+ // structural-match ignores substructure of
+ // `*const _`/`*mut _`, so skip `super_visit_with`.
+ //
+ // For example, if you have:
+ // ```
+ // struct NonStructural;
+ // #[derive(PartialEq, Eq)]
+ // struct T(*const NonStructural);
+ // const C: T = T(std::ptr::null());
+ // ```
+ //
+ // Even though `NonStructural` does not implement `PartialEq`,
+ // structural equality on `T` does not recur into the raw
+ // pointer. Therefore, one can still use `C` in a pattern.
+
+ // (But still tell caller to continue search.)
+ return false;
+ }
+ ty::FnDef(..) | ty::FnPtr(..) => {
+ // types of formals and return in `fn(_) -> _` are also irrelevant;
+ // so we do not recur into them via `super_visit_with`
+ //
+ // (But still tell caller to continue search.)
+ return false;
+ }
+ ty::Array(_, n)
+ if { n.try_eval_usize(self.tcx(), ty::ParamEnv::reveal_all()) == Some(0) } =>
+ {
+ // rust-lang/rust#62336: ignore type of contents
+ // for empty array.
+ return false;
+ }
+ _ => {
+ ty.super_visit_with(self);
+ return false;
+ }
+ };
+
+ if !self.seen.insert(adt_def.did) {
+ debug!("Search already seen adt_def: {:?}", adt_def);
+ // let caller continue its search
+ return false;
+ }
+
+ if !self.type_marked_structural(ty) {
+ debug!("Search found ty: {:?}", ty);
+ self.found = Some(NonStructuralMatchTy::Adt(&adt_def));
+ return true; // Halt visiting!
+ }
+
+ // structural-match does not care about the
+ // instantiation of the generics in an ADT (it
+ // instead looks directly at its fields outside
+ // this match), so we skip super_visit_with.
+ //
+ // (Must not recur on substs for `PhantomData<T>` cf
+ // rust-lang/rust#55028 and rust-lang/rust#55837; but also
+ // want to skip substs when only uses of generic are
+ // behind unsafe pointers `*const T`/`*mut T`.)
+
+ // even though we skip super_visit_with, we must recur on
+ // fields of ADT.
+ let tcx = self.tcx();
+ for field_ty in adt_def.all_fields().map(|field| field.ty(tcx, substs)) {
+ if field_ty.visit_with(self) {
+ // found an ADT without structural-match; halt visiting!
+ assert!(self.found.is_some());
+ return true;
+ }
+ }
+
+ // Even though we do not want to recur on substs, we do
+ // want our caller to continue its own search.
+ false
+ }
+}
-use errors::DiagnosticBuilder;
+use rustc_errors::DiagnosticBuilder;
use rustc_span::Span;
use smallvec::SmallVec;
Obligation { cause, param_env, recursion_depth, predicate: trait_ref.to_predicate() }
}
-impl<'tcx> TyCtxt<'tcx> {
- pub fn predicate_for_trait_def(
- self,
- param_env: ty::ParamEnv<'tcx>,
- cause: ObligationCause<'tcx>,
- trait_def_id: DefId,
- recursion_depth: usize,
- self_ty: Ty<'tcx>,
- params: &[GenericArg<'tcx>],
- ) -> PredicateObligation<'tcx> {
- let trait_ref =
- ty::TraitRef { def_id: trait_def_id, substs: self.mk_substs_trait(self_ty, params) };
- predicate_for_trait_ref(cause, param_env, trait_ref, recursion_depth)
- }
-
- /// Casts a trait reference into a reference to one of its super
- /// traits; returns `None` if `target_trait_def_id` is not a
- /// supertrait.
- pub fn upcast_choices(
- self,
- source_trait_ref: ty::PolyTraitRef<'tcx>,
- target_trait_def_id: DefId,
- ) -> Vec<ty::PolyTraitRef<'tcx>> {
- if source_trait_ref.def_id() == target_trait_def_id {
- return vec![source_trait_ref]; // Shortcut the most common case.
- }
+pub fn predicate_for_trait_def(
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ cause: ObligationCause<'tcx>,
+ trait_def_id: DefId,
+ recursion_depth: usize,
+ self_ty: Ty<'tcx>,
+ params: &[GenericArg<'tcx>],
+) -> PredicateObligation<'tcx> {
+ let trait_ref =
+ ty::TraitRef { def_id: trait_def_id, substs: tcx.mk_substs_trait(self_ty, params) };
+ predicate_for_trait_ref(cause, param_env, trait_ref, recursion_depth)
+}
- supertraits(self, source_trait_ref).filter(|r| r.def_id() == target_trait_def_id).collect()
+/// Casts a trait reference into a reference to one of its super
+/// traits; returns `None` if `target_trait_def_id` is not a
+/// supertrait.
+pub fn upcast_choices(
+ tcx: TyCtxt<'tcx>,
+ source_trait_ref: ty::PolyTraitRef<'tcx>,
+ target_trait_def_id: DefId,
+) -> Vec<ty::PolyTraitRef<'tcx>> {
+ if source_trait_ref.def_id() == target_trait_def_id {
+ return vec![source_trait_ref]; // Shortcut the most common case.
}
- /// 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(self, trait_ref: ty::PolyTraitRef<'tcx>) -> usize {
- let mut entries = 0;
- // Count number of methods and add them to the total offset.
- // Skip over associated types and constants.
- for trait_item in self.associated_items(trait_ref.def_id()) {
- if trait_item.kind == ty::AssocKind::Method {
- entries += 1;
- }
+ 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: TyCtxt<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>) -> usize {
+ let mut entries = 0;
+ // Count number of methods and add them to the total offset.
+ // Skip over associated types and constants.
+ for trait_item in tcx.associated_items(trait_ref.def_id()) {
+ if trait_item.kind == ty::AssocKind::Method {
+ entries += 1;
}
- entries
}
+ entries
+}
- /// 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`.
- pub fn get_vtable_index_of_object_method<N>(
- self,
- object: &super::VtableObjectData<'tcx, N>,
- method_def_id: DefId,
- ) -> usize {
- // Count number of methods preceding the one we are selecting and
- // add them to the total offset.
- // Skip over associated types and constants.
- let mut entries = object.vtable_base;
- for trait_item in self.associated_items(object.upcast_trait_ref.def_id()) {
- if trait_item.def_id == method_def_id {
- // The item with the ID we were given really ought to be a method.
- assert_eq!(trait_item.kind, ty::AssocKind::Method);
- return entries;
- }
- if trait_item.kind == ty::AssocKind::Method {
- entries += 1;
- }
+/// 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`.
+pub fn get_vtable_index_of_object_method<N>(
+ tcx: TyCtxt<'tcx>,
+ object: &super::VtableObjectData<'tcx, N>,
+ method_def_id: DefId,
+) -> usize {
+ // Count number of methods preceding the one we are selecting and
+ // add them to the total offset.
+ // Skip over associated types and constants.
+ let mut entries = object.vtable_base;
+ for trait_item in tcx.associated_items(object.upcast_trait_ref.def_id()) {
+ if trait_item.def_id == method_def_id {
+ // The item with the ID we were given really ought to be a method.
+ assert_eq!(trait_item.kind, ty::AssocKind::Method);
+ return entries;
+ }
+ if trait_item.kind == ty::AssocKind::Method {
+ entries += 1;
}
-
- bug!("get_vtable_index_of_object_method: {:?} was not found", method_def_id);
}
- pub fn closure_trait_ref_and_return_type(
- self,
- fn_trait_def_id: DefId,
- self_ty: Ty<'tcx>,
- sig: ty::PolyFnSig<'tcx>,
- tuple_arguments: TupleArgumentsFlag,
- ) -> ty::Binder<(ty::TraitRef<'tcx>, Ty<'tcx>)> {
- let arguments_tuple = match tuple_arguments {
- TupleArgumentsFlag::No => sig.skip_binder().inputs()[0],
- TupleArgumentsFlag::Yes => self.intern_tup(sig.skip_binder().inputs()),
- };
- let trait_ref = ty::TraitRef {
- def_id: fn_trait_def_id,
- substs: self.mk_substs_trait(self_ty, &[arguments_tuple.into()]),
- };
- ty::Binder::bind((trait_ref, sig.skip_binder().output()))
- }
+ bug!("get_vtable_index_of_object_method: {:?} was not found", method_def_id);
+}
- pub fn generator_trait_ref_and_outputs(
- self,
- fn_trait_def_id: DefId,
- self_ty: Ty<'tcx>,
- sig: ty::PolyGenSig<'tcx>,
- ) -> ty::Binder<(ty::TraitRef<'tcx>, Ty<'tcx>, Ty<'tcx>)> {
- let trait_ref =
- ty::TraitRef { def_id: fn_trait_def_id, substs: self.mk_substs_trait(self_ty, &[]) };
- ty::Binder::bind((trait_ref, sig.skip_binder().yield_ty, sig.skip_binder().return_ty))
- }
+pub fn closure_trait_ref_and_return_type(
+ tcx: TyCtxt<'tcx>,
+ fn_trait_def_id: DefId,
+ self_ty: Ty<'tcx>,
+ sig: ty::PolyFnSig<'tcx>,
+ tuple_arguments: TupleArgumentsFlag,
+) -> ty::Binder<(ty::TraitRef<'tcx>, Ty<'tcx>)> {
+ let arguments_tuple = match tuple_arguments {
+ TupleArgumentsFlag::No => sig.skip_binder().inputs()[0],
+ TupleArgumentsFlag::Yes => tcx.intern_tup(sig.skip_binder().inputs()),
+ };
+ let trait_ref = ty::TraitRef {
+ def_id: fn_trait_def_id,
+ substs: tcx.mk_substs_trait(self_ty, &[arguments_tuple.into()]),
+ };
+ ty::Binder::bind((trait_ref, sig.skip_binder().output()))
+}
- pub fn impl_is_default(self, node_item_def_id: DefId) -> bool {
- match self.hir().as_local_hir_id(node_item_def_id) {
- Some(hir_id) => {
- let item = self.hir().expect_item(hir_id);
- if let hir::ItemKind::Impl(_, _, defaultness, ..) = item.kind {
- defaultness.is_default()
- } else {
- false
- }
+pub fn generator_trait_ref_and_outputs(
+ tcx: TyCtxt<'tcx>,
+ fn_trait_def_id: DefId,
+ self_ty: Ty<'tcx>,
+ sig: ty::PolyGenSig<'tcx>,
+) -> ty::Binder<(ty::TraitRef<'tcx>, Ty<'tcx>, Ty<'tcx>)> {
+ let trait_ref =
+ ty::TraitRef { def_id: fn_trait_def_id, substs: tcx.mk_substs_trait(self_ty, &[]) };
+ ty::Binder::bind((trait_ref, sig.skip_binder().yield_ty, sig.skip_binder().return_ty))
+}
+
+pub fn impl_is_default(tcx: TyCtxt<'_>, node_item_def_id: DefId) -> bool {
+ match tcx.hir().as_local_hir_id(node_item_def_id) {
+ Some(hir_id) => {
+ let item = tcx.hir().expect_item(hir_id);
+ if let hir::ItemKind::Impl(_, _, defaultness, ..) = item.kind {
+ defaultness.is_default()
+ } else {
+ false
}
- None => self.impl_defaultness(node_item_def_id).is_default(),
}
+ None => tcx.impl_defaultness(node_item_def_id).is_default(),
}
+}
- pub fn impl_item_is_final(self, assoc_item: &ty::AssocItem) -> bool {
- assoc_item.defaultness.is_final() && !self.impl_is_default(assoc_item.container.id())
- }
+pub fn impl_item_is_final(tcx: TyCtxt<'_>, assoc_item: &ty::AssocItem) -> bool {
+ assoc_item.defaultness.is_final() && !impl_is_default(tcx, assoc_item.container.id())
}
pub enum TupleArgumentsFlag {
--- /dev/null
+use crate::infer::opaque_types::required_region_bounds;
+use crate::infer::InferCtxt;
+use crate::middle::lang_items;
+use crate::traits::{self, AssocTypeBoundData};
+use crate::ty::subst::SubstsRef;
+use crate::ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable};
+use rustc_hir as hir;
+use rustc_hir::def_id::DefId;
+use rustc_span::symbol::{kw, Ident};
+use rustc_span::Span;
+use std::iter::once;
+
+/// Returns the set of obligations needed to make `ty` well-formed.
+/// If `ty` contains unresolved inference variables, this may include
+/// further WF obligations. However, if `ty` IS an unresolved
+/// inference variable, returns `None`, because we are not able to
+/// make any progress at all. This is to prevent "livelock" where we
+/// say "$0 is WF if $0 is WF".
+pub fn obligations<'a, 'tcx>(
+ infcx: &InferCtxt<'a, 'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ body_id: hir::HirId,
+ ty: Ty<'tcx>,
+ span: Span,
+) -> Option<Vec<traits::PredicateObligation<'tcx>>> {
+ let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item: None };
+ if wf.compute(ty) {
+ debug!("wf::obligations({:?}, body_id={:?}) = {:?}", ty, body_id, wf.out);
+ let result = wf.normalize();
+ debug!("wf::obligations({:?}, body_id={:?}) ~~> {:?}", ty, body_id, result);
+ Some(result)
+ } else {
+ None // no progress made, return None
+ }
+}
+
+/// Returns the obligations that make this trait reference
+/// well-formed. For example, if there is a trait `Set` defined like
+/// `trait Set<K:Eq>`, then the trait reference `Foo: Set<Bar>` is WF
+/// if `Bar: Eq`.
+pub fn trait_obligations<'a, 'tcx>(
+ infcx: &InferCtxt<'a, 'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ body_id: hir::HirId,
+ trait_ref: &ty::TraitRef<'tcx>,
+ span: Span,
+ item: Option<&'tcx hir::Item<'tcx>>,
+) -> Vec<traits::PredicateObligation<'tcx>> {
+ let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item };
+ wf.compute_trait_ref(trait_ref, Elaborate::All);
+ wf.normalize()
+}
+
+pub fn predicate_obligations<'a, 'tcx>(
+ infcx: &InferCtxt<'a, 'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ body_id: hir::HirId,
+ predicate: &ty::Predicate<'tcx>,
+ span: Span,
+) -> Vec<traits::PredicateObligation<'tcx>> {
+ let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item: None };
+
+ // (*) ok to skip binders, because wf code is prepared for it
+ match *predicate {
+ ty::Predicate::Trait(ref t) => {
+ wf.compute_trait_ref(&t.skip_binder().trait_ref, Elaborate::None); // (*)
+ }
+ ty::Predicate::RegionOutlives(..) => {}
+ ty::Predicate::TypeOutlives(ref t) => {
+ wf.compute(t.skip_binder().0);
+ }
+ ty::Predicate::Projection(ref t) => {
+ let t = t.skip_binder(); // (*)
+ wf.compute_projection(t.projection_ty);
+ wf.compute(t.ty);
+ }
+ ty::Predicate::WellFormed(t) => {
+ wf.compute(t);
+ }
+ ty::Predicate::ObjectSafe(_) => {}
+ ty::Predicate::ClosureKind(..) => {}
+ ty::Predicate::Subtype(ref data) => {
+ wf.compute(data.skip_binder().a); // (*)
+ wf.compute(data.skip_binder().b); // (*)
+ }
+ ty::Predicate::ConstEvaluatable(def_id, substs) => {
+ let obligations = wf.nominal_obligations(def_id, substs);
+ wf.out.extend(obligations);
+
+ for ty in substs.types() {
+ wf.compute(ty);
+ }
+ }
+ }
+
+ wf.normalize()
+}
+
+struct WfPredicates<'a, 'tcx> {
+ infcx: &'a InferCtxt<'a, 'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ body_id: hir::HirId,
+ span: Span,
+ out: Vec<traits::PredicateObligation<'tcx>>,
+ item: Option<&'tcx hir::Item<'tcx>>,
+}
+
+/// Controls whether we "elaborate" supertraits and so forth on the WF
+/// predicates. This is a kind of hack to address #43784. The
+/// underlying problem in that issue was a trait structure like:
+///
+/// ```
+/// trait Foo: Copy { }
+/// trait Bar: Foo { }
+/// impl<T: Bar> Foo for T { }
+/// impl<T> Bar for T { }
+/// ```
+///
+/// Here, in the `Foo` impl, we will check that `T: Copy` holds -- but
+/// we decide that this is true because `T: Bar` is in the
+/// where-clauses (and we can elaborate that to include `T:
+/// Copy`). This wouldn't be a problem, except that when we check the
+/// `Bar` impl, we decide that `T: Foo` must hold because of the `Foo`
+/// impl. And so nowhere did we check that `T: Copy` holds!
+///
+/// To resolve this, we elaborate the WF requirements that must be
+/// proven when checking impls. This means that (e.g.) the `impl Bar
+/// for T` will be forced to prove not only that `T: Foo` but also `T:
+/// Copy` (which it won't be able to do, because there is no `Copy`
+/// impl for `T`).
+#[derive(Debug, PartialEq, Eq, Copy, Clone)]
+enum Elaborate {
+ All,
+ None,
+}
+
+impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
+ fn cause(&mut self, code: traits::ObligationCauseCode<'tcx>) -> traits::ObligationCause<'tcx> {
+ traits::ObligationCause::new(self.span, self.body_id, code)
+ }
+
+ fn normalize(&mut self) -> Vec<traits::PredicateObligation<'tcx>> {
+ let cause = self.cause(traits::MiscObligation);
+ let infcx = &mut self.infcx;
+ let param_env = self.param_env;
+ self.out
+ .iter()
+ .inspect(|pred| assert!(!pred.has_escaping_bound_vars()))
+ .flat_map(|pred| {
+ let mut selcx = traits::SelectionContext::new(infcx);
+ let pred = traits::normalize(&mut selcx, param_env, cause.clone(), pred);
+ once(pred.value).chain(pred.obligations)
+ })
+ .collect()
+ }
+
+ /// Pushes the obligations required for `trait_ref` to be WF into `self.out`.
+ fn compute_trait_ref(&mut self, trait_ref: &ty::TraitRef<'tcx>, elaborate: Elaborate) {
+ let tcx = self.infcx.tcx;
+ let obligations = self.nominal_obligations(trait_ref.def_id, trait_ref.substs);
+
+ let cause = self.cause(traits::MiscObligation);
+ let param_env = self.param_env;
+
+ let item = &self.item;
+ let extend_cause_with_original_assoc_item_obligation =
+ |cause: &mut traits::ObligationCause<'_>,
+ pred: &ty::Predicate<'_>,
+ trait_assoc_items: ty::AssocItemsIterator<'_>| {
+ let trait_item = tcx
+ .hir()
+ .as_local_hir_id(trait_ref.def_id)
+ .and_then(|trait_id| tcx.hir().find(trait_id));
+ let (trait_name, trait_generics) = match trait_item {
+ Some(hir::Node::Item(hir::Item {
+ ident,
+ kind: hir::ItemKind::Trait(.., generics, _, _),
+ ..
+ }))
+ | Some(hir::Node::Item(hir::Item {
+ ident,
+ kind: hir::ItemKind::TraitAlias(generics, _),
+ ..
+ })) => (Some(ident), Some(generics)),
+ _ => (None, None),
+ };
+
+ let item_span = item.map(|i| tcx.sess.source_map().def_span(i.span));
+ match pred {
+ ty::Predicate::Projection(proj) => {
+ // The obligation comes not from the current `impl` nor the `trait` being
+ // implemented, but rather from a "second order" obligation, like in
+ // `src/test/ui/associated-types/point-at-type-on-obligation-failure.rs`:
+ //
+ // error[E0271]: type mismatch resolving `<Foo2 as Bar2>::Ok == ()`
+ // --> $DIR/point-at-type-on-obligation-failure.rs:13:5
+ // |
+ // LL | type Ok;
+ // | -- associated type defined here
+ // ...
+ // LL | impl Bar for Foo {
+ // | ---------------- in this `impl` item
+ // LL | type Ok = ();
+ // | ^^^^^^^^^^^^^ expected `u32`, found `()`
+ // |
+ // = note: expected type `u32`
+ // found type `()`
+ //
+ // FIXME: we would want to point a span to all places that contributed to this
+ // obligation. In the case above, it should be closer to:
+ //
+ // error[E0271]: type mismatch resolving `<Foo2 as Bar2>::Ok == ()`
+ // --> $DIR/point-at-type-on-obligation-failure.rs:13:5
+ // |
+ // LL | type Ok;
+ // | -- associated type defined here
+ // LL | type Sibling: Bar2<Ok=Self::Ok>;
+ // | -------------------------------- obligation set here
+ // ...
+ // LL | impl Bar for Foo {
+ // | ---------------- in this `impl` item
+ // LL | type Ok = ();
+ // | ^^^^^^^^^^^^^ expected `u32`, found `()`
+ // ...
+ // LL | impl Bar2 for Foo2 {
+ // | ---------------- in this `impl` item
+ // LL | type Ok = u32;
+ // | -------------- obligation set here
+ // |
+ // = note: expected type `u32`
+ // found type `()`
+ if let Some(hir::ItemKind::Impl(.., impl_items)) = item.map(|i| &i.kind) {
+ let trait_assoc_item = tcx.associated_item(proj.projection_def_id());
+ if let Some(impl_item) = impl_items
+ .iter()
+ .filter(|item| item.ident == trait_assoc_item.ident)
+ .next()
+ {
+ cause.span = impl_item.span;
+ cause.code = traits::AssocTypeBound(Box::new(AssocTypeBoundData {
+ impl_span: item_span,
+ original: trait_assoc_item.ident.span,
+ bounds: vec![],
+ }));
+ }
+ }
+ }
+ ty::Predicate::Trait(proj) => {
+ // An associated item obligation born out of the `trait` failed to be met.
+ // Point at the `impl` that failed the obligation, the associated item that
+ // needed to meet the obligation, and the definition of that associated item,
+ // which should hold the obligation in most cases. An example can be seen in
+ // `src/test/ui/associated-types/point-at-type-on-obligation-failure-2.rs`:
+ //
+ // error[E0277]: the trait bound `bool: Bar` is not satisfied
+ // --> $DIR/point-at-type-on-obligation-failure-2.rs:8:5
+ // |
+ // LL | type Assoc: Bar;
+ // | ----- associated type defined here
+ // ...
+ // LL | impl Foo for () {
+ // | --------------- in this `impl` item
+ // LL | type Assoc = bool;
+ // | ^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `bool`
+ //
+ // If the obligation comes from the where clause in the `trait`, we point at it:
+ //
+ // error[E0277]: the trait bound `bool: Bar` is not satisfied
+ // --> $DIR/point-at-type-on-obligation-failure-2.rs:8:5
+ // |
+ // | trait Foo where <Self as Foo>>::Assoc: Bar {
+ // | -------------------------- restricted in this bound
+ // LL | type Assoc;
+ // | ----- associated type defined here
+ // ...
+ // LL | impl Foo for () {
+ // | --------------- in this `impl` item
+ // LL | type Assoc = bool;
+ // | ^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `bool`
+ if let (
+ ty::Projection(ty::ProjectionTy { item_def_id, .. }),
+ Some(hir::ItemKind::Impl(.., impl_items)),
+ ) = (&proj.skip_binder().self_ty().kind, item.map(|i| &i.kind))
+ {
+ if let Some((impl_item, trait_assoc_item)) = trait_assoc_items
+ .filter(|i| i.def_id == *item_def_id)
+ .next()
+ .and_then(|trait_assoc_item| {
+ impl_items
+ .iter()
+ .filter(|i| i.ident == trait_assoc_item.ident)
+ .next()
+ .map(|impl_item| (impl_item, trait_assoc_item))
+ })
+ {
+ let bounds = trait_generics
+ .map(|generics| {
+ get_generic_bound_spans(
+ &generics,
+ trait_name,
+ trait_assoc_item.ident,
+ )
+ })
+ .unwrap_or_else(Vec::new);
+ cause.span = impl_item.span;
+ cause.code = traits::AssocTypeBound(Box::new(AssocTypeBoundData {
+ impl_span: item_span,
+ original: trait_assoc_item.ident.span,
+ bounds,
+ }));
+ }
+ }
+ }
+ _ => {}
+ }
+ };
+
+ if let Elaborate::All = elaborate {
+ let trait_assoc_items = tcx.associated_items(trait_ref.def_id);
+
+ let predicates =
+ obligations.iter().map(|obligation| obligation.predicate.clone()).collect();
+ let implied_obligations = traits::elaborate_predicates(tcx, predicates);
+ let implied_obligations = implied_obligations.map(|pred| {
+ let mut cause = cause.clone();
+ extend_cause_with_original_assoc_item_obligation(
+ &mut cause,
+ &pred,
+ trait_assoc_items.clone(),
+ );
+ traits::Obligation::new(cause, param_env, pred)
+ });
+ self.out.extend(implied_obligations);
+ }
+
+ self.out.extend(obligations);
+
+ self.out.extend(trait_ref.substs.types().filter(|ty| !ty.has_escaping_bound_vars()).map(
+ |ty| traits::Obligation::new(cause.clone(), param_env, ty::Predicate::WellFormed(ty)),
+ ));
+ }
+
+ /// Pushes the obligations required for `trait_ref::Item` to be WF
+ /// into `self.out`.
+ fn compute_projection(&mut self, data: ty::ProjectionTy<'tcx>) {
+ // A projection is well-formed if (a) the trait ref itself is
+ // WF and (b) the trait-ref holds. (It may also be
+ // normalizable and be WF that way.)
+ let trait_ref = data.trait_ref(self.infcx.tcx);
+ self.compute_trait_ref(&trait_ref, Elaborate::None);
+
+ if !data.has_escaping_bound_vars() {
+ let predicate = trait_ref.to_predicate();
+ let cause = self.cause(traits::ProjectionWf(data));
+ self.out.push(traits::Obligation::new(cause, self.param_env, predicate));
+ }
+ }
+
+ /// Pushes the obligations required for an array length to be WF
+ /// into `self.out`.
+ fn compute_array_len(&mut self, constant: ty::Const<'tcx>) {
+ if let ty::ConstKind::Unevaluated(def_id, substs, promoted) = constant.val {
+ assert!(promoted.is_none());
+
+ let obligations = self.nominal_obligations(def_id, substs);
+ self.out.extend(obligations);
+
+ let predicate = ty::Predicate::ConstEvaluatable(def_id, substs);
+ let cause = self.cause(traits::MiscObligation);
+ self.out.push(traits::Obligation::new(cause, self.param_env, predicate));
+ }
+ }
+
+ fn require_sized(&mut self, subty: Ty<'tcx>, cause: traits::ObligationCauseCode<'tcx>) {
+ if !subty.has_escaping_bound_vars() {
+ let cause = self.cause(cause);
+ let trait_ref = ty::TraitRef {
+ def_id: self.infcx.tcx.require_lang_item(lang_items::SizedTraitLangItem, None),
+ substs: self.infcx.tcx.mk_substs_trait(subty, &[]),
+ };
+ self.out.push(traits::Obligation::new(cause, self.param_env, trait_ref.to_predicate()));
+ }
+ }
+
+ /// Pushes new obligations into `out`. Returns `true` if it was able
+ /// to generate all the predicates needed to validate that `ty0`
+ /// is WF. Returns false if `ty0` is an unresolved type variable,
+ /// in which case we are not able to simplify at all.
+ fn compute(&mut self, ty0: Ty<'tcx>) -> bool {
+ let mut subtys = ty0.walk();
+ let param_env = self.param_env;
+ while let Some(ty) = subtys.next() {
+ match ty.kind {
+ ty::Bool
+ | ty::Char
+ | ty::Int(..)
+ | ty::Uint(..)
+ | ty::Float(..)
+ | ty::Error
+ | ty::Str
+ | ty::GeneratorWitness(..)
+ | ty::Never
+ | ty::Param(_)
+ | ty::Bound(..)
+ | ty::Placeholder(..)
+ | ty::Foreign(..) => {
+ // WfScalar, WfParameter, etc
+ }
+
+ ty::Slice(subty) => {
+ self.require_sized(subty, traits::SliceOrArrayElem);
+ }
+
+ ty::Array(subty, len) => {
+ self.require_sized(subty, traits::SliceOrArrayElem);
+ self.compute_array_len(*len);
+ }
+
+ ty::Tuple(ref tys) => {
+ if let Some((_last, rest)) = tys.split_last() {
+ for elem in rest {
+ self.require_sized(elem.expect_ty(), traits::TupleElem);
+ }
+ }
+ }
+
+ ty::RawPtr(_) => {
+ // simple cases that are WF if their type args are WF
+ }
+
+ ty::Projection(data) => {
+ subtys.skip_current_subtree(); // subtree handled by compute_projection
+ self.compute_projection(data);
+ }
+
+ ty::UnnormalizedProjection(..) => bug!("only used with chalk-engine"),
+
+ ty::Adt(def, substs) => {
+ // WfNominalType
+ let obligations = self.nominal_obligations(def.did, substs);
+ self.out.extend(obligations);
+ }
+
+ ty::FnDef(did, substs) => {
+ let obligations = self.nominal_obligations(did, substs);
+ self.out.extend(obligations);
+ }
+
+ ty::Ref(r, rty, _) => {
+ // WfReference
+ if !r.has_escaping_bound_vars() && !rty.has_escaping_bound_vars() {
+ let cause = self.cause(traits::ReferenceOutlivesReferent(ty));
+ self.out.push(traits::Obligation::new(
+ cause,
+ param_env,
+ ty::Predicate::TypeOutlives(ty::Binder::dummy(ty::OutlivesPredicate(
+ rty, r,
+ ))),
+ ));
+ }
+ }
+
+ ty::Generator(..) => {
+ // Walk ALL the types in the generator: this will
+ // include the upvar types as well as the yield
+ // type. Note that this is mildly distinct from
+ // the closure case, where we have to be careful
+ // about the signature of the closure. We don't
+ // have the problem of implied bounds here since
+ // generators don't take arguments.
+ }
+
+ ty::Closure(def_id, substs) => {
+ // Only check the upvar types for WF, not the rest
+ // of the types within. This is needed because we
+ // capture the signature and it may not be WF
+ // without the implied bounds. Consider a closure
+ // like `|x: &'a T|` -- it may be that `T: 'a` is
+ // not known to hold in the creator's context (and
+ // indeed the closure may not be invoked by its
+ // creator, but rather turned to someone who *can*
+ // verify that).
+ //
+ // The special treatment of closures here really
+ // ought not to be necessary either; the problem
+ // is related to #25860 -- there is no way for us
+ // to express a fn type complete with the implied
+ // bounds that it is assuming. I think in reality
+ // the WF rules around fn are a bit messed up, and
+ // that is the rot problem: `fn(&'a T)` should
+ // probably always be WF, because it should be
+ // shorthand for something like `where(T: 'a) {
+ // fn(&'a T) }`, as discussed in #25860.
+ //
+ // Note that we are also skipping the generic
+ // types. This is consistent with the `outlives`
+ // code, but anyway doesn't matter: within the fn
+ // body where they are created, the generics will
+ // always be WF, and outside of that fn body we
+ // are not directly inspecting closure types
+ // anyway, except via auto trait matching (which
+ // only inspects the upvar types).
+ subtys.skip_current_subtree(); // subtree handled by compute_projection
+ for upvar_ty in substs.as_closure().upvar_tys(def_id, self.infcx.tcx) {
+ self.compute(upvar_ty);
+ }
+ }
+
+ ty::FnPtr(_) => {
+ // let the loop iterate into the argument/return
+ // types appearing in the fn signature
+ }
+
+ ty::Opaque(did, substs) => {
+ // all of the requirements on type parameters
+ // should've been checked by the instantiation
+ // of whatever returned this exact `impl Trait`.
+
+ // for named opaque `impl Trait` types we still need to check them
+ if ty::is_impl_trait_defn(self.infcx.tcx, did).is_none() {
+ let obligations = self.nominal_obligations(did, substs);
+ self.out.extend(obligations);
+ }
+ }
+
+ ty::Dynamic(data, r) => {
+ // WfObject
+ //
+ // Here, we defer WF checking due to higher-ranked
+ // regions. This is perhaps not ideal.
+ self.from_object_ty(ty, data, r);
+
+ // FIXME(#27579) RFC also considers adding trait
+ // obligations that don't refer to Self and
+ // checking those
+
+ let defer_to_coercion = self.infcx.tcx.features().object_safe_for_dispatch;
+
+ if !defer_to_coercion {
+ let cause = self.cause(traits::MiscObligation);
+ let component_traits = data.auto_traits().chain(data.principal_def_id());
+ self.out.extend(component_traits.map(|did| {
+ traits::Obligation::new(
+ cause.clone(),
+ param_env,
+ ty::Predicate::ObjectSafe(did),
+ )
+ }));
+ }
+ }
+
+ // Inference variables are the complicated case, since we don't
+ // know what type they are. We do two things:
+ //
+ // 1. Check if they have been resolved, and if so proceed with
+ // THAT type.
+ // 2. If not, check whether this is the type that we
+ // started with (ty0). In that case, we've made no
+ // progress at all, so return false. Otherwise,
+ // we've at least simplified things (i.e., we went
+ // from `Vec<$0>: WF` to `$0: WF`, so we can
+ // register a pending obligation and keep
+ // moving. (Goal is that an "inductive hypothesis"
+ // is satisfied to ensure termination.)
+ ty::Infer(_) => {
+ let ty = self.infcx.shallow_resolve(ty);
+ if let ty::Infer(_) = ty.kind {
+ // not yet resolved...
+ if ty == ty0 {
+ // ...this is the type we started from! no progress.
+ return false;
+ }
+
+ let cause = self.cause(traits::MiscObligation);
+ self.out.push(
+ // ...not the type we started from, so we made progress.
+ traits::Obligation::new(
+ cause,
+ self.param_env,
+ ty::Predicate::WellFormed(ty),
+ ),
+ );
+ } else {
+ // Yes, resolved, proceed with the
+ // result. Should never return false because
+ // `ty` is not a Infer.
+ assert!(self.compute(ty));
+ }
+ }
+ }
+ }
+
+ // if we made it through that loop above, we made progress!
+ return true;
+ }
+
+ fn nominal_obligations(
+ &mut self,
+ def_id: DefId,
+ substs: SubstsRef<'tcx>,
+ ) -> Vec<traits::PredicateObligation<'tcx>> {
+ let predicates = self.infcx.tcx.predicates_of(def_id).instantiate(self.infcx.tcx, substs);
+ let cause = self.cause(traits::ItemObligation(def_id));
+ predicates
+ .predicates
+ .into_iter()
+ .map(|pred| traits::Obligation::new(cause.clone(), self.param_env, pred))
+ .filter(|pred| !pred.has_escaping_bound_vars())
+ .collect()
+ }
+
+ fn from_object_ty(
+ &mut self,
+ ty: Ty<'tcx>,
+ data: ty::Binder<&'tcx ty::List<ty::ExistentialPredicate<'tcx>>>,
+ region: ty::Region<'tcx>,
+ ) {
+ // Imagine a type like this:
+ //
+ // trait Foo { }
+ // trait Bar<'c> : 'c { }
+ //
+ // &'b (Foo+'c+Bar<'d>)
+ // ^
+ //
+ // In this case, the following relationships must hold:
+ //
+ // 'b <= 'c
+ // 'd <= 'c
+ //
+ // The first conditions is due to the normal region pointer
+ // rules, which say that a reference cannot outlive its
+ // referent.
+ //
+ // The final condition may be a bit surprising. In particular,
+ // you may expect that it would have been `'c <= 'd`, since
+ // usually lifetimes of outer things are conservative
+ // approximations for inner things. However, it works somewhat
+ // differently with trait objects: here the idea is that if the
+ // user specifies a region bound (`'c`, in this case) it is the
+ // "master bound" that *implies* that bounds from other traits are
+ // all met. (Remember that *all bounds* in a type like
+ // `Foo+Bar+Zed` must be met, not just one, hence if we write
+ // `Foo<'x>+Bar<'y>`, we know that the type outlives *both* 'x and
+ // 'y.)
+ //
+ // Note: in fact we only permit builtin traits, not `Bar<'d>`, I
+ // am looking forward to the future here.
+ if !data.has_escaping_bound_vars() && !region.has_escaping_bound_vars() {
+ let implicit_bounds = object_region_bounds(self.infcx.tcx, data);
+
+ let explicit_bound = region;
+
+ self.out.reserve(implicit_bounds.len());
+ for implicit_bound in implicit_bounds {
+ let cause = self.cause(traits::ObjectTypeBound(ty, explicit_bound));
+ let outlives =
+ ty::Binder::dummy(ty::OutlivesPredicate(explicit_bound, implicit_bound));
+ self.out.push(traits::Obligation::new(
+ cause,
+ self.param_env,
+ outlives.to_predicate(),
+ ));
+ }
+ }
+ }
+}
+
+/// Given an object type like `SomeTrait + Send`, computes the lifetime
+/// bounds that must hold on the elided self type. These are derived
+/// from the declarations of `SomeTrait`, `Send`, and friends -- if
+/// they declare `trait SomeTrait : 'static`, for example, then
+/// `'static` would appear in the list. The hard work is done by
+/// `infer::required_region_bounds`, see that for more information.
+pub fn object_region_bounds<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ existential_predicates: ty::Binder<&'tcx ty::List<ty::ExistentialPredicate<'tcx>>>,
+) -> Vec<ty::Region<'tcx>> {
+ // Since we don't actually *know* the self type for an object,
+ // this "open(err)" serves as a kind of dummy standin -- basically
+ // a placeholder type.
+ let open_ty = tcx.mk_ty_infer(ty::FreshTy(0));
+
+ let predicates = existential_predicates
+ .iter()
+ .filter_map(|predicate| {
+ if let ty::ExistentialPredicate::Projection(_) = *predicate.skip_binder() {
+ None
+ } else {
+ Some(predicate.with_self_ty(tcx, open_ty))
+ }
+ })
+ .collect();
+
+ required_region_bounds(tcx, open_ty, predicates)
+}
+
+/// Find the span of a generic bound affecting an associated type.
+fn get_generic_bound_spans(
+ generics: &hir::Generics<'_>,
+ trait_name: Option<&Ident>,
+ assoc_item_name: Ident,
+) -> Vec<Span> {
+ let mut bounds = vec![];
+ for clause in generics.where_clause.predicates.iter() {
+ if let hir::WherePredicate::BoundPredicate(pred) = clause {
+ match &pred.bounded_ty.kind {
+ hir::TyKind::Path(hir::QPath::Resolved(Some(ty), path)) => {
+ let mut s = path.segments.iter();
+ if let (a, Some(b), None) = (s.next(), s.next(), s.next()) {
+ if a.map(|s| &s.ident) == trait_name
+ && b.ident == assoc_item_name
+ && is_self_path(&ty.kind)
+ {
+ // `<Self as Foo>::Bar`
+ bounds.push(pred.span);
+ }
+ }
+ }
+ hir::TyKind::Path(hir::QPath::TypeRelative(ty, segment)) => {
+ if segment.ident == assoc_item_name {
+ if is_self_path(&ty.kind) {
+ // `Self::Bar`
+ bounds.push(pred.span);
+ }
+ }
+ }
+ _ => {}
+ }
+ }
+ }
+ bounds
+}
+
+fn is_self_path(kind: &hir::TyKind<'_>) -> bool {
+ match kind {
+ hir::TyKind::Path(hir::QPath::Resolved(None, path)) => {
+ let mut s = path.segments.iter();
+ if let (Some(segment), None) = (s.next(), s.next()) {
+ if segment.ident.name == kw::SelfUpper {
+ // `type(Self)`
+ return true;
+ }
+ }
+ }
+ _ => {}
+ }
+ false
+}
where
D: TyDecoder<'tcx>,
{
- let base: mir::PlaceBase<'tcx> = Decodable::decode(decoder)?;
+ let local: mir::Local = Decodable::decode(decoder)?;
let len = decoder.read_usize()?;
let projection: &'tcx List<mir::PlaceElem<'tcx>> =
decoder.tcx().mk_place_elems((0..len).map(|_| Decodable::decode(decoder)))?;
- Ok(mir::Place { base, projection })
+ Ok(mir::Place { local, projection })
}
#[inline]
+++ /dev/null
-use crate::hir::map::blocks::FnLikeNode;
-use crate::ty::query::Providers;
-use crate::ty::TyCtxt;
-use rustc_hir as hir;
-use rustc_hir::def_id::DefId;
-use rustc_span::symbol::Symbol;
-use rustc_target::spec::abi::Abi;
-use syntax::attr;
-
-impl<'tcx> TyCtxt<'tcx> {
- /// Whether the `def_id` counts as const fn in your current crate, considering all active
- /// feature gates
- pub fn is_const_fn(self, def_id: DefId) -> bool {
- self.is_const_fn_raw(def_id)
- && match self.is_unstable_const_fn(def_id) {
- Some(feature_name) => {
- // has a `rustc_const_unstable` attribute, check whether the user enabled the
- // corresponding feature gate.
- self.features()
- .declared_lib_features
- .iter()
- .any(|&(sym, _)| sym == feature_name)
- }
- // functions without const stability are either stable user written
- // const fn or the user is using feature gates and we thus don't
- // care what they do
- None => true,
- }
- }
-
- /// Whether the `def_id` is an unstable const fn and what feature gate is necessary to enable it
- pub fn is_unstable_const_fn(self, def_id: DefId) -> Option<Symbol> {
- if self.is_const_fn_raw(def_id) {
- let const_stab = self.lookup_const_stability(def_id)?;
- if const_stab.level.is_unstable() { Some(const_stab.feature) } else { None }
- } else {
- None
- }
- }
-
- /// Returns `true` if this function must conform to `min_const_fn`
- pub fn is_min_const_fn(self, def_id: DefId) -> bool {
- // Bail out if the signature doesn't contain `const`
- if !self.is_const_fn_raw(def_id) {
- return false;
- }
-
- if self.features().staged_api {
- // In order for a libstd function to be considered min_const_fn
- // it needs to be stable and have no `rustc_const_unstable` attribute.
- match self.lookup_const_stability(def_id) {
- // `rustc_const_unstable` functions don't need to conform.
- Some(&attr::ConstStability { ref level, .. }) if level.is_unstable() => false,
- None => {
- if let Some(stab) = self.lookup_stability(def_id) {
- if stab.level.is_stable() {
- self.sess.span_err(
- self.def_span(def_id),
- "stable const functions must have either `rustc_const_stable` or \
- `rustc_const_unstable` attribute",
- );
- // While we errored above, because we don't know if we need to conform, we
- // err on the "safe" side and require min_const_fn.
- true
- } else {
- // Unstable functions need not conform to min_const_fn.
- false
- }
- } else {
- // Internal functions are forced to conform to min_const_fn.
- // Annotate the internal function with a const stability attribute if
- // you need to use unstable features.
- // Note: this is an arbitrary choice that does not affect stability or const
- // safety or anything, it just changes whether we need to annotate some
- // internal functions with `rustc_const_stable` or with `rustc_const_unstable`
- true
- }
- }
- // Everything else needs to conform, because it would be callable from
- // other `min_const_fn` functions.
- _ => true,
- }
- } else {
- // users enabling the `const_fn` feature gate can do what they want
- !self.features().const_fn
- }
- }
-}
-
-pub fn provide(providers: &mut Providers<'_>) {
- /// Const evaluability whitelist is here to check evaluability at the
- /// top level beforehand.
- fn is_const_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> Option<bool> {
- match tcx.fn_sig(def_id).abi() {
- Abi::RustIntrinsic | Abi::PlatformIntrinsic => {
- Some(tcx.lookup_const_stability(def_id).is_some())
- }
- _ => None,
- }
- }
-
- /// Checks whether the function has a `const` modifier or, in case it is an intrinsic, whether
- /// said intrinsic is on the whitelist for being const callable.
- fn is_const_fn_raw(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
- let hir_id = tcx
- .hir()
- .as_local_hir_id(def_id)
- .expect("Non-local call to local provider is_const_fn");
-
- let node = tcx.hir().get(hir_id);
-
- if let Some(whitelisted) = is_const_intrinsic(tcx, def_id) {
- whitelisted
- } else if let Some(fn_like) = FnLikeNode::from_node(node) {
- fn_like.constness() == hir::Constness::Const
- } else if let hir::Node::Ctor(_) = node {
- true
- } else {
- false
- }
- }
-
- fn is_promotable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
- tcx.is_const_fn(def_id)
- && match tcx.lookup_const_stability(def_id) {
- Some(stab) => {
- if cfg!(debug_assertions) && stab.promotable {
- let sig = tcx.fn_sig(def_id);
- assert_eq!(
- sig.unsafety(),
- hir::Unsafety::Normal,
- "don't mark const unsafe fns as promotable",
- // https://github.com/rust-lang/rust/pull/53851#issuecomment-418760682
- );
- }
- stab.promotable
- }
- None => false,
- }
- }
-
- fn const_fn_is_allowed_fn_ptr(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
- tcx.is_const_fn(def_id)
- && tcx
- .lookup_const_stability(def_id)
- .map(|stab| stab.allow_const_fn_ptr)
- .unwrap_or(false)
- }
-
- *providers = Providers {
- is_const_fn_raw,
- is_promotable_const_fn,
- const_fn_is_allowed_fn_ptr,
- ..*providers
- };
-}
use crate::hir::map::DefPathHash;
use crate::ich::{NodeIdHashingMode, StableHashingContext};
use crate::infer::canonical::{Canonical, CanonicalVarInfo, CanonicalVarInfos};
-use crate::infer::outlives::free_region_map::FreeRegionMap;
-use crate::lint::{self, Lint};
+use crate::lint::{struct_lint_level, LintSource};
use crate::middle;
use crate::middle::cstore::CrateStoreDyn;
use crate::middle::cstore::EncodedMetadata;
use crate::mir::{
interpret, BodyAndCache, Field, Local, Place, PlaceElem, ProjectionKind, Promoted,
};
-use crate::session::config::CrateType;
-use crate::session::config::{BorrowckMode, OutputFilenames};
-use crate::session::Session;
use crate::traits;
use crate::traits::{Clause, Clauses, Goal, GoalKind, Goals};
+use crate::ty::free_region_map::FreeRegionMap;
use crate::ty::layout::{LayoutDetails, TargetDataLayout, VariantIdx};
use crate::ty::query;
use crate::ty::steal::Steal;
use crate::ty::{InferConst, ParamConst};
use crate::ty::{List, TyKind, TyS};
use crate::util::common::ErrorReported;
+use rustc_data_structures::sync;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet, DefIndex, LOCAL_CRATE};
use rustc_hir::{HirId, Node, TraitCandidate};
use rustc_hir::{ItemKind, ItemLocalId, ItemLocalMap, ItemLocalSet};
+use rustc_session::config::CrateType;
+use rustc_session::config::{BorrowckMode, OutputFilenames};
+use rustc_session::Session;
use arena::SyncDroplessArena;
-use errors::DiagnosticBuilder;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::profiling::SelfProfilerRef;
use rustc_data_structures::sharded::ShardedHashMap;
hash_stable_hashmap, HashStable, StableHasher, StableVec,
};
use rustc_data_structures::sync::{Lock, Lrc, WorkerLocal};
+use rustc_errors::DiagnosticBuilder;
use rustc_index::vec::{Idx, IndexVec};
use rustc_macros::HashStable;
+use rustc_session::lint::{Level, Lint};
use rustc_session::node_id::NodeMap;
use rustc_span::source_map::MultiSpan;
use rustc_span::symbol::{kw, sym, Symbol};
pub sess: &'tcx Session,
- pub lint_store: Lrc<lint::LintStore>,
+ /// This only ever stores a `LintStore` but we don't want a dependency on that type here.
+ ///
+ /// FIXME(Centril): consider `dyn LintStoreMarker` once
+ /// we can upcast to `Any` for some additional type safety.
+ pub lint_store: Lrc<dyn Any + sync::Sync + sync::Send>,
pub dep_graph: DepGraph,
/// reference to the context, to allow formatting values that need it.
pub fn create_global_ctxt(
s: &'tcx Session,
- lint_store: Lrc<lint::LintStore>,
+ lint_store: Lrc<dyn Any + sync::Send + sync::Sync>,
local_providers: ty::query::Providers<'tcx>,
extern_providers: ty::query::Providers<'tcx>,
arenas: &'tcx AllArenas,
use crate::dep_graph::TaskDeps;
use crate::ty::query;
- use errors::Diagnostic;
use rustc_data_structures::sync::{self, Lock, Lrc};
use rustc_data_structures::thin_vec::ThinVec;
use rustc_data_structures::OnDrop;
+ use rustc_errors::Diagnostic;
use std::mem;
#[cfg(not(parallel_compiler))]
let mut projection = place.projection.to_vec();
projection.push(elem);
- Place { base: place.base, projection: self.intern_place_elems(&projection) }
+ Place { local: place.local, projection: self.intern_place_elems(&projection) }
}
pub fn intern_existential_predicates(
iter.intern_with(|xs| self.intern_goals(xs))
}
- pub fn lint_hir<S: Into<MultiSpan>>(
+ pub fn lint_hir(
self,
lint: &'static Lint,
hir_id: HirId,
- span: S,
+ span: impl Into<MultiSpan>,
msg: &str,
) {
self.struct_span_lint_hir(lint, hir_id, span.into(), msg).emit()
}
- pub fn lint_hir_note<S: Into<MultiSpan>>(
- self,
- lint: &'static Lint,
- hir_id: HirId,
- span: S,
- msg: &str,
- note: &str,
- ) {
- let mut err = self.struct_span_lint_hir(lint, hir_id, span.into(), msg);
- err.note(note);
- err.emit()
- }
-
- pub fn lint_node_note<S: Into<MultiSpan>>(
- self,
- lint: &'static Lint,
- id: hir::HirId,
- span: S,
- msg: &str,
- note: &str,
- ) {
- let mut err = self.struct_span_lint_hir(lint, id, span.into(), msg);
- err.note(note);
- err.emit()
- }
-
/// Walks upwards from `id` to find a node which might change lint levels with attributes.
/// It stops at `bound` and just returns it if reached.
- pub fn maybe_lint_level_root_bounded(
- self,
- mut id: hir::HirId,
- bound: hir::HirId,
- ) -> hir::HirId {
+ pub fn maybe_lint_level_root_bounded(self, mut id: HirId, bound: HirId) -> HirId {
+ let hir = self.hir();
loop {
if id == bound {
return bound;
}
- if lint::maybe_lint_level_root(self, id) {
+
+ if hir.attrs(id).iter().any(|attr| Level::from_symbol(attr.name_or_empty()).is_some()) {
return id;
}
- let next = self.hir().get_parent_node(id);
+ let next = hir.get_parent_node(id);
if next == id {
bug!("lint traversal reached the root of the crate");
}
self,
lint: &'static Lint,
mut id: hir::HirId,
- ) -> (lint::Level, lint::LintSource) {
+ ) -> (Level, LintSource) {
let sets = self.lint_levels(LOCAL_CRATE);
loop {
if let Some(pair) = sets.level_and_source(lint, id, self.sess) {
}
}
- pub fn struct_span_lint_hir<S: Into<MultiSpan>>(
+ pub fn struct_span_lint_hir(
self,
lint: &'static Lint,
hir_id: HirId,
- span: S,
+ span: impl Into<MultiSpan>,
msg: &str,
) -> DiagnosticBuilder<'tcx> {
let (level, src) = self.lint_level_at_node(lint, hir_id);
- lint::struct_lint_level(self.sess, lint, level, src, Some(span.into()), msg)
+ struct_lint_level(self.sess, lint, level, src, Some(span.into()), msg)
}
pub fn struct_lint_node(
msg: &str,
) -> DiagnosticBuilder<'tcx> {
let (level, src) = self.lint_level_at_node(lint, id);
- lint::struct_lint_level(self.sess, lint, level, src, None, msg)
+ struct_lint_level(self.sess, lint, level, src, None, msg)
}
pub fn in_scope_traits(self, id: HirId) -> Option<&'tcx StableVec<TraitCandidate>> {
use crate::ty::{self, BoundRegion, Region, Ty, TyCtxt};
+use rustc_errors::{pluralize, Applicability, DiagnosticBuilder};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
-
-use errors::{Applicability, DiagnosticBuilder};
use rustc_span::Span;
use rustc_target::spec::abi;
use syntax::ast;
-use syntax::errors::pluralize;
use std::borrow::Cow;
use std::fmt;
fn add_const(&mut self, c: &ty::Const<'_>) {
self.add_ty(c.ty);
match c.val {
- ty::ConstKind::Unevaluated(_, substs) => {
+ ty::ConstKind::Unevaluated(_, substs, _) => {
self.add_substs(substs);
self.add_flags(TypeFlags::HAS_PROJECTION);
}
--- /dev/null
+use crate::ty::{self, Lift, Region, TyCtxt};
+use rustc_data_structures::transitive_relation::TransitiveRelation;
+
+#[derive(Clone, RustcEncodable, RustcDecodable, Debug, Default, HashStable)]
+pub struct FreeRegionMap<'tcx> {
+ // Stores the relation `a < b`, where `a` and `b` are regions.
+ //
+ // Invariant: only free regions like `'x` or `'static` are stored
+ // in this relation, not scopes.
+ relation: TransitiveRelation<Region<'tcx>>,
+}
+
+impl<'tcx> FreeRegionMap<'tcx> {
+ pub fn elements(&self) -> impl Iterator<Item = &Region<'tcx>> {
+ self.relation.elements()
+ }
+
+ pub fn is_empty(&self) -> bool {
+ self.relation.is_empty()
+ }
+
+ // Record that `'sup:'sub`. Or, put another way, `'sub <= 'sup`.
+ // (with the exception that `'static: 'x` is not notable)
+ pub fn relate_regions(&mut self, sub: Region<'tcx>, sup: Region<'tcx>) {
+ debug!("relate_regions(sub={:?}, sup={:?})", sub, sup);
+ if is_free_or_static(sub) && is_free(sup) {
+ self.relation.add(sub, sup)
+ }
+ }
+
+ /// Computes the least-upper-bound of two free regions. In some
+ /// cases, this is more conservative than necessary, in order to
+ /// avoid making arbitrary choices. See
+ /// `TransitiveRelation::postdom_upper_bound` for more details.
+ pub fn lub_free_regions(
+ &self,
+ tcx: TyCtxt<'tcx>,
+ r_a: Region<'tcx>,
+ r_b: Region<'tcx>,
+ ) -> Region<'tcx> {
+ debug!("lub_free_regions(r_a={:?}, r_b={:?})", r_a, r_b);
+ assert!(is_free(r_a));
+ assert!(is_free(r_b));
+ let result = if r_a == r_b {
+ r_a
+ } else {
+ match self.relation.postdom_upper_bound(&r_a, &r_b) {
+ None => tcx.mk_region(ty::ReStatic),
+ Some(r) => *r,
+ }
+ };
+ debug!("lub_free_regions(r_a={:?}, r_b={:?}) = {:?}", r_a, r_b, result);
+ result
+ }
+}
+
+/// The NLL region handling code represents free region relations in a
+/// slightly different way; this trait allows functions to be abstract
+/// over which version is in use.
+pub trait FreeRegionRelations<'tcx> {
+ /// Tests whether `r_a <= r_b`. Both must be free regions or
+ /// `'static`.
+ fn sub_free_regions(&self, shorter: ty::Region<'tcx>, longer: ty::Region<'tcx>) -> bool;
+}
+
+impl<'tcx> FreeRegionRelations<'tcx> for FreeRegionMap<'tcx> {
+ fn sub_free_regions(&self, r_a: Region<'tcx>, r_b: Region<'tcx>) -> bool {
+ assert!(is_free_or_static(r_a) && is_free_or_static(r_b));
+ if let ty::ReStatic = r_b {
+ true // `'a <= 'static` is just always true, and not stored in the relation explicitly
+ } else {
+ r_a == r_b || self.relation.contains(&r_a, &r_b)
+ }
+ }
+}
+
+fn is_free(r: Region<'_>) -> bool {
+ match *r {
+ ty::ReEarlyBound(_) | ty::ReFree(_) => true,
+ _ => false,
+ }
+}
+
+fn is_free_or_static(r: Region<'_>) -> bool {
+ match *r {
+ ty::ReStatic => true,
+ _ => is_free(r),
+ }
+}
+
+impl<'a, 'tcx> Lift<'tcx> for FreeRegionMap<'a> {
+ type Lifted = FreeRegionMap<'tcx>;
+ fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option<FreeRegionMap<'tcx>> {
+ self.relation.maybe_map(|&fr| tcx.lift(&fr)).map(|relation| FreeRegionMap { relation })
+ }
+}
// ```
// forest.is_empty()
// ```
- self.ty_inhabitedness_forest(ty).contains(self, module)
+ ty.uninhabited_from(self).contains(self, module)
}
pub fn is_ty_uninhabited_from_any_module(self, ty: Ty<'tcx>) -> bool {
- !self.ty_inhabitedness_forest(ty).is_empty()
- }
-
- fn ty_inhabitedness_forest(self, ty: Ty<'tcx>) -> DefIdForest {
- ty.uninhabited_from(self)
+ !ty.uninhabited_from(self).is_empty()
}
}
substs: rcvr_substs,
}),
traits::VtableObject(ref data) => {
- let index = tcx.get_vtable_index_of_object_method(data, def_id);
+ let index = traits::get_vtable_index_of_object_method(tcx, data, def_id);
Some(Instance { def: ty::InstanceDef::Virtual(def_id, index), substs: rcvr_substs })
}
traits::VtableBuiltin(..) => {
let extra_args = if sig.abi == RustCall {
assert!(!sig.c_variadic && extra_args.is_empty());
- match sig.inputs().last().unwrap().kind {
- ty::Tuple(tupled_arguments) => {
+ if let Some(input) = sig.inputs().last() {
+ if let ty::Tuple(tupled_arguments) = input.kind {
inputs = &sig.inputs()[0..sig.inputs().len() - 1];
tupled_arguments.iter().map(|k| k.expect_ty()).collect()
- }
- _ => {
+ } else {
bug!(
"argument to function with \"rust-call\" ABI \
- is not a tuple"
+ is not a tuple"
);
}
+ } else {
+ bug!(
+ "argument to function with \"rust-call\" ABI \
+ is not a tuple"
+ );
}
} else {
assert!(sig.c_variadic || extra_args.is_empty());
use crate::ty::subst::{InternalSubsts, Subst, SubstsRef};
use crate::ty::util::{Discr, IntTypeExt};
use crate::ty::walk::TypeWalker;
-use crate::util::captures::Captures;
use arena::SyncDroplessArena;
+use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
pub use self::instance::{Instance, InstanceDef};
-pub use self::structural_match::search_for_structural_match_violation;
-pub use self::structural_match::type_marked_structural;
-pub use self::structural_match::NonStructuralMatchTy;
-
pub use self::trait_def::TraitDef;
pub use self::query::queries;
#[macro_use]
pub mod codec;
pub mod _match;
-mod constness;
mod erase_regions;
pub mod error;
pub mod fast_reject;
pub mod flags;
pub mod fold;
+pub mod free_region_map;
pub mod inhabitedness;
pub mod layout;
+pub mod normalize_erasing_regions;
pub mod outlives;
pub mod print;
pub mod query;
pub mod trait_def;
pub mod util;
pub mod walk;
-pub mod wf;
mod context;
mod diagnostics;
mod instance;
mod structural_impls;
-mod structural_match;
mod sty;
// Data types
pub type UpvarListMap = FxHashMap<DefId, FxIndexMap<hir::HirId, UpvarId>>;
pub type UpvarCaptureMap<'tcx> = FxHashMap<UpvarId, UpvarCapture<'tcx>>;
-#[derive(Copy, Clone, TypeFoldable)]
-pub struct ClosureUpvar<'tcx> {
- pub res: Res,
- pub span: Span,
- pub ty: Ty<'tcx>,
-}
-
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum IntVarValue {
IntType(ast::IntTy),
context::provide(providers);
erase_regions::provide(providers);
layout::provide(providers);
- util::provide(providers);
- constness::provide(providers);
*providers = ty::query::Providers {
asyncness,
associated_item,
--- /dev/null
+//! Methods for normalizing when you don't care about regions (and
+//! aren't doing type inference). If either of those things don't
+//! apply to you, use `infcx.normalize(...)`.
+//!
+//! The methods in this file use a `TypeFolder` to recursively process
+//! contents, invoking the underlying
+//! `normalize_ty_after_erasing_regions` query for each type found
+//! within. (This underlying query is what is cached.)
+
+use crate::ty::fold::{TypeFoldable, TypeFolder};
+use crate::ty::subst::{Subst, SubstsRef};
+use crate::ty::{self, Ty, TyCtxt};
+
+impl<'tcx> TyCtxt<'tcx> {
+ /// Erase the regions in `value` and then fully normalize all the
+ /// types found within. The result will also have regions erased.
+ ///
+ /// This is appropriate to use only after type-check: it assumes
+ /// that normalization will succeed, for example.
+ pub fn normalize_erasing_regions<T>(self, param_env: ty::ParamEnv<'tcx>, value: T) -> T
+ where
+ T: TypeFoldable<'tcx>,
+ {
+ debug!(
+ "normalize_erasing_regions::<{}>(value={:?}, param_env={:?})",
+ ::std::any::type_name::<T>(),
+ value,
+ param_env,
+ );
+
+ // Erase first before we do the real query -- this keeps the
+ // cache from being too polluted.
+ let value = self.erase_regions(&value);
+ if !value.has_projections() {
+ value
+ } else {
+ value.fold_with(&mut NormalizeAfterErasingRegionsFolder {
+ tcx: self,
+ param_env: param_env,
+ })
+ }
+ }
+
+ /// If you have a `Binder<T>`, you can do this to strip out the
+ /// late-bound regions and then normalize the result, yielding up
+ /// a `T` (with regions erased). This is appropriate when the
+ /// binder is being instantiated at the call site.
+ ///
+ /// N.B., currently, higher-ranked type bounds inhibit
+ /// normalization. Therefore, each time we erase them in
+ /// codegen, we need to normalize the contents.
+ pub fn normalize_erasing_late_bound_regions<T>(
+ self,
+ param_env: ty::ParamEnv<'tcx>,
+ value: &ty::Binder<T>,
+ ) -> T
+ where
+ T: TypeFoldable<'tcx>,
+ {
+ assert!(!value.needs_subst());
+ let value = self.erase_late_bound_regions(value);
+ self.normalize_erasing_regions(param_env, value)
+ }
+
+ /// Monomorphizes a type from the AST by first applying the
+ /// in-scope substitutions and then normalizing any associated
+ /// types.
+ pub fn subst_and_normalize_erasing_regions<T>(
+ self,
+ param_substs: SubstsRef<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ value: &T,
+ ) -> T
+ where
+ T: TypeFoldable<'tcx>,
+ {
+ debug!(
+ "subst_and_normalize_erasing_regions(\
+ param_substs={:?}, \
+ value={:?}, \
+ param_env={:?})",
+ param_substs, value, param_env,
+ );
+ let substituted = value.subst(self, param_substs);
+ self.normalize_erasing_regions(param_env, substituted)
+ }
+}
+
+struct NormalizeAfterErasingRegionsFolder<'tcx> {
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+}
+
+impl TypeFolder<'tcx> for NormalizeAfterErasingRegionsFolder<'tcx> {
+ fn tcx(&self) -> TyCtxt<'tcx> {
+ self.tcx
+ }
+
+ fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
+ self.tcx.normalize_ty_after_erasing_regions(self.param_env.and(ty))
+ }
+}
impl<'tcx> TyCtxt<'tcx> {
/// Push onto `out` all the things that must outlive `'a` for the condition
/// `ty0: 'a` to hold. Note that `ty0` must be a **fully resolved type**.
- pub fn push_outlives_components(
- &self,
- ty0: Ty<'tcx>,
- out: &mut SmallVec<[Component<'tcx>; 4]>,
- ) {
- self.compute_components(ty0, out);
+ pub fn push_outlives_components(self, ty0: Ty<'tcx>, out: &mut SmallVec<[Component<'tcx>; 4]>) {
+ compute_components(self, ty0, out);
debug!("components({:?}) = {:?}", ty0, out);
}
+}
- fn compute_components(&self, ty: Ty<'tcx>, out: &mut SmallVec<[Component<'tcx>; 4]>) {
- // Descend through the types, looking for the various "base"
- // components and collecting them into `out`. This is not written
- // with `collect()` because of the need to sometimes skip subtrees
- // in the `subtys` iterator (e.g., when encountering a
- // projection).
- match ty.kind {
+fn compute_components(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, out: &mut SmallVec<[Component<'tcx>; 4]>) {
+ // Descend through the types, looking for the various "base"
+ // components and collecting them into `out`. This is not written
+ // with `collect()` because of the need to sometimes skip subtrees
+ // in the `subtys` iterator (e.g., when encountering a
+ // projection).
+ match ty.kind {
ty::Closure(def_id, ref substs) => {
- for upvar_ty in substs.as_closure().upvar_tys(def_id, *self) {
- self.compute_components(upvar_ty, out);
+ for upvar_ty in substs.as_closure().upvar_tys(def_id, tcx) {
+ compute_components(tcx, upvar_ty, out);
}
}
ty::Generator(def_id, ref substs, _) => {
// Same as the closure case
- for upvar_ty in substs.as_generator().upvar_tys(def_id, *self) {
- self.compute_components(upvar_ty, out);
+ for upvar_ty in substs.as_generator().upvar_tys(def_id, tcx) {
+ compute_components(tcx, upvar_ty, out);
}
// We ignore regions in the generator interior as we don't
// fallback case: hard code
// OutlivesProjectionComponents. Continue walking
// through and constrain Pi.
- let subcomponents = self.capture_components(ty);
+ let subcomponents = capture_components(tcx, ty);
out.push(Component::EscapingProjection(subcomponents));
}
}
push_region_constraints(ty, out);
for subty in ty.walk_shallow() {
- self.compute_components(subty, out);
+ compute_components(tcx, subty, out);
}
}
}
- }
+}
- fn capture_components(&self, ty: Ty<'tcx>) -> Vec<Component<'tcx>> {
- let mut temp = smallvec![];
- push_region_constraints(ty, &mut temp);
- for subty in ty.walk_shallow() {
- self.compute_components(subty, &mut temp);
- }
- temp.into_iter().collect()
+fn capture_components(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Vec<Component<'tcx>> {
+ let mut temp = smallvec![];
+ push_region_constraints(ty, &mut temp);
+ for subty in ty.walk_shallow() {
+ compute_components(tcx, subty, &mut temp);
}
+ temp.into_iter().collect()
}
fn push_region_constraints<'tcx>(ty: Ty<'tcx>, out: &mut SmallVec<[Component<'tcx>; 4]>) {
match (ct.val, &ct.ty.kind) {
(_, ty::FnDef(did, substs)) => p!(print_value_path(*did, substs)),
- (ty::ConstKind::Unevaluated(did, substs), _) => match self.tcx().def_kind(did) {
- Some(DefKind::Static) | Some(DefKind::Const) | Some(DefKind::AssocConst) => {
- p!(print_value_path(did, substs))
- }
- _ => {
- if did.is_local() {
- let span = self.tcx().def_span(did);
- if let Ok(snip) = self.tcx().sess.source_map().span_to_snippet(span) {
- p!(write("{}", snip))
- } else {
- p!(write("_: "), print(ct.ty))
+ (ty::ConstKind::Unevaluated(did, substs, promoted), _) => {
+ if let Some(promoted) = promoted {
+ p!(print_value_path(did, substs));
+ p!(write("::{:?}", promoted));
+ } else {
+ match self.tcx().def_kind(did) {
+ Some(DefKind::Static)
+ | Some(DefKind::Const)
+ | Some(DefKind::AssocConst) => p!(print_value_path(did, substs)),
+ _ => {
+ if did.is_local() {
+ let span = self.tcx().def_span(did);
+ if let Ok(snip) = self.tcx().sess.source_map().span_to_snippet(span)
+ {
+ p!(write("{}", snip))
+ } else {
+ p!(write("_: "), print(ct.ty))
+ }
+ } else {
+ p!(write("_: "), print(ct.ty))
+ }
}
- } else {
- p!(write("_: "), print(ct.ty))
}
}
- },
+ }
(ty::ConstKind::Infer(..), _) => p!(write("_: "), print(ct.ty)),
(ty::ConstKind::Param(ParamConst { name, .. }), _) => p!(write("{}", name)),
(ty::ConstKind::Value(value), _) => return self.pretty_print_const_value(value, ct.ty),
}
}
-impl TyCtxt<'t> {
- // HACK(eddyb) get rid of `def_path_str` and/or pass `Namespace` explicitly always
- // (but also some things just print a `DefId` generally so maybe we need this?)
- fn guess_def_namespace(self, def_id: DefId) -> Namespace {
- match self.def_key(def_id).disambiguated_data.data {
- DefPathData::TypeNs(..) | DefPathData::CrateRoot | DefPathData::ImplTrait => {
- Namespace::TypeNS
- }
+// HACK(eddyb) get rid of `def_path_str` and/or pass `Namespace` explicitly always
+// (but also some things just print a `DefId` generally so maybe we need this?)
+fn guess_def_namespace(tcx: TyCtxt<'_>, def_id: DefId) -> Namespace {
+ match tcx.def_key(def_id).disambiguated_data.data {
+ DefPathData::TypeNs(..) | DefPathData::CrateRoot | DefPathData::ImplTrait => {
+ Namespace::TypeNS
+ }
- DefPathData::ValueNs(..)
- | DefPathData::AnonConst
- | DefPathData::ClosureExpr
- | DefPathData::Ctor => Namespace::ValueNS,
+ DefPathData::ValueNs(..)
+ | DefPathData::AnonConst
+ | DefPathData::ClosureExpr
+ | DefPathData::Ctor => Namespace::ValueNS,
- DefPathData::MacroNs(..) => Namespace::MacroNS,
+ DefPathData::MacroNs(..) => Namespace::MacroNS,
- _ => Namespace::TypeNS,
- }
+ _ => Namespace::TypeNS,
}
+}
+impl TyCtxt<'t> {
/// Returns a string identifying this `DefId`. This string is
/// suitable for user output.
pub fn def_path_str(self, def_id: DefId) -> String {
}
pub fn def_path_str_with_substs(self, def_id: DefId, substs: &'t [GenericArg<'t>]) -> String {
- let ns = self.guess_def_namespace(def_id);
+ let ns = guess_def_namespace(self, def_id);
debug!("def_path_str: def_id={:?}, ns={:?}", def_id, ns);
let mut s = String::new();
let _ = FmtPrinter::new(self, &mut s, ns).print_def_path(def_id, substs);
use crate::dep_graph::{DepKind, DepNode};
use crate::ty::query::plumbing::CycleError;
use crate::ty::query::queries;
-use crate::ty::query::QueryCache;
-use crate::ty::query::{Query, QueryName};
+use crate::ty::query::{Query, QueryCache};
use crate::ty::TyCtxt;
use rustc_data_structures::profiling::ProfileCategory;
use rustc_hir::def_id::{CrateNum, DefId};
// FIXME(eddyb) false positive, the lifetime parameter is used for `Key`/`Value`.
#[allow(unused_lifetimes)]
pub trait QueryConfig<'tcx> {
- const NAME: QueryName;
+ const NAME: &'static str;
const CATEGORY: ProfileCategory;
type Key: Eq + Hash + Clone + Debug;
}
}
-impl<'tcx> Key for ty::Const<'tcx> {
+impl<'tcx> Key for &'tcx ty::Const<'tcx> {
fn query_crate(&self) -> CrateNum {
LOCAL_CRATE
}
use crate::dep_graph::{self, DepNode};
use crate::hir::exports::Export;
use crate::infer::canonical::{self, Canonical};
-use crate::lint;
+use crate::lint::LintLevelMap;
use crate::middle::codegen_fn_attrs::CodegenFnAttrs;
use crate::middle::cstore::{CrateSource, DepKind, NativeLibraryKind};
use crate::middle::cstore::{ExternCrate, ForeignModule, LinkagePreference, NativeLibrary};
mod on_disk_cache;
pub use self::on_disk_cache::OnDiskCache;
+mod profiling_support;
+pub use self::profiling_support::{IntoSelfProfilingString, QueryKeyStringBuilder};
+
// Each of these queries corresponds to a function pointer field in the
// `Providers` struct for requesting a value of that type, and a method
// on `tcx: TyCtxt` (and `tcx.at(span)`) for doing that request in a way
use crate::ty::codec::{self as ty_codec, TyDecoder, TyEncoder};
use crate::ty::context::TyCtxt;
use crate::ty::{self, Ty};
-use errors::Diagnostic;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sync::{HashMapExt, Lock, Lrc, Once};
use rustc_data_structures::thin_vec::ThinVec;
+use rustc_errors::Diagnostic;
use rustc_hir as hir;
use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, LOCAL_CRATE};
use rustc_index::vec::{Idx, IndexVec};
// Encode query results.
let mut query_result_index = EncodedQueryResultIndex::new();
- tcx.sess.time("encode query results", || {
+ tcx.sess.time("encode_query_results", || {
let enc = &mut encoder;
let qri = &mut query_result_index;
Q: super::config::QueryDescription<'tcx, Value: Encodable>,
E: 'a + TyEncoder,
{
- let desc = &format!("encode_query_results for {}", ::std::any::type_name::<Q>());
- let _timer = tcx.sess.prof.generic_pass(desc);
+ let desc = &format!("encode_query_results_for_{}", ::std::any::type_name::<Q>());
+ let _timer = tcx.sess.prof.extra_verbose_generic_activity(desc);
let shards = Q::query_cache(tcx).lock_shards();
assert!(shards.iter().all(|shard| shard.active.is_empty()));
use crate::ty::tls;
use crate::ty::{self, TyCtxt};
-use errors::Diagnostic;
-use errors::DiagnosticBuilder;
-use errors::FatalError;
-use errors::Handler;
-use errors::Level;
#[cfg(not(parallel_compiler))]
use rustc_data_structures::cold_path;
use rustc_data_structures::fx::{FxHashMap, FxHasher};
+#[cfg(parallel_compiler)]
+use rustc_data_structures::profiling::TimingGuard;
use rustc_data_structures::sharded::Sharded;
use rustc_data_structures::sync::{Lock, Lrc};
use rustc_data_structures::thin_vec::ThinVec;
+use rustc_errors::{struct_span_err, Diagnostic, DiagnosticBuilder, FatalError, Handler, Level};
use rustc_span::source_map::DUMMY_SP;
use rustc_span::Span;
use std::collections::hash_map::Entry;
/// for some compile-time benchmarks.
#[inline(always)]
pub(super) fn try_get(tcx: TyCtxt<'tcx>, span: Span, key: &Q::Key) -> TryGetJob<'a, 'tcx, Q> {
+ // Handling the `query_blocked_prof_timer` is a bit weird because of the
+ // control flow in this function: Blocking is implemented by
+ // awaiting a running job and, once that is done, entering the loop below
+ // again from the top. In that second iteration we will hit the
+ // cache which provides us with the information we need for
+ // finishing the "query-blocked" event.
+ //
+ // We thus allocate `query_blocked_prof_timer` outside the loop,
+ // initialize it during the first iteration and finish it during the
+ // second iteration.
+ #[cfg(parallel_compiler)]
+ let mut query_blocked_prof_timer: Option<TimingGuard<'_>> = None;
+
let cache = Q::query_cache(tcx);
loop {
// We compute the key's hash once and then use it for both the
if let Some((_, value)) =
lock.results.raw_entry().from_key_hashed_nocheck(key_hash, key)
{
- tcx.prof.query_cache_hit(Q::NAME);
+ if unlikely!(tcx.prof.enabled()) {
+ tcx.prof.query_cache_hit(value.index.into());
+
+ #[cfg(parallel_compiler)]
+ {
+ if let Some(prof_timer) = query_blocked_prof_timer.take() {
+ prof_timer.finish_with_query_invocation_id(value.index.into());
+ }
+ }
+ }
+
let result = (value.value.clone(), value.index);
#[cfg(debug_assertions)]
{
return TryGetJob::JobCompleted(result);
}
- #[cfg(parallel_compiler)]
- let query_blocked_prof_timer;
-
let job = match lock.active.entry((*key).clone()) {
Entry::Occupied(entry) => {
match *entry.get() {
// self-profiler.
#[cfg(parallel_compiler)]
{
- query_blocked_prof_timer = tcx.prof.query_blocked(Q::NAME);
+ query_blocked_prof_timer = Some(tcx.prof.query_blocked());
}
job.clone()
{
let result = job.r#await(tcx, span);
- // This `drop()` is not strictly necessary as the binding
- // would go out of scope anyway. But it's good to have an
- // explicit marker of how far the measurement goes.
- drop(query_blocked_prof_timer);
-
if let Err(cycle) = result {
return TryGetJob::Cycle(Q::handle_cycle_error(tcx, cycle));
}
#[inline(never)]
pub(super) fn get_query<Q: QueryDescription<'tcx>>(self, span: Span, key: Q::Key) -> Q::Value {
- debug!("ty::query::get_query<{}>(key={:?}, span={:?})", Q::NAME.as_str(), key, span);
+ debug!("ty::query::get_query<{}>(key={:?}, span={:?})", Q::NAME, key, span);
let job = match JobOwner::try_get(self, span, &key) {
TryGetJob::NotYetStarted(job) => job,
}
if Q::ANON {
- let prof_timer = self.prof.query_provider(Q::NAME);
+ let prof_timer = self.prof.query_provider();
let ((result, dep_node_index), diagnostics) = with_diagnostics(|diagnostics| {
self.start_query(job.job.clone(), diagnostics, |tcx| {
})
});
- drop(prof_timer);
+ prof_timer.finish_with_query_invocation_id(dep_node_index.into());
self.dep_graph.read_index(dep_node_index);
let result = if Q::cache_on_disk(self, key.clone(), None)
&& self.sess.opts.debugging_opts.incremental_queries
{
- let _prof_timer = self.prof.incr_cache_loading(Q::NAME);
+ let prof_timer = self.prof.incr_cache_loading();
let result = Q::try_load_from_disk(self, prev_dep_node_index);
+ prof_timer.finish_with_query_invocation_id(dep_node_index.into());
// We always expect to find a cached result for things that
// can be forced from `DepNode`.
} else {
// We could not load a result from the on-disk cache, so
// recompute.
- let _prof_timer = self.prof.query_provider(Q::NAME);
+ let prof_timer = self.prof.query_provider();
// The dep-graph for this computation is already in-place.
let result = self.dep_graph.with_ignore(|| Q::compute(self, key));
+ prof_timer.finish_with_query_invocation_id(dep_node_index.into());
+
result
};
dep_node
);
- let prof_timer = self.prof.query_provider(Q::NAME);
+ let prof_timer = self.prof.query_provider();
let ((result, dep_node_index), diagnostics) = with_diagnostics(|diagnostics| {
self.start_query(job.job.clone(), diagnostics, |tcx| {
})
});
- drop(prof_timer);
+ prof_timer.finish_with_query_invocation_id(dep_node_index.into());
if unlikely!(!diagnostics.is_empty()) {
if dep_node.kind != crate::dep_graph::DepKind::Null {
let dep_node = Q::to_dep_node(self, &key);
- if self.dep_graph.try_mark_green_and_read(self, &dep_node).is_none() {
- // A None return from `try_mark_green_and_read` means that this is either
- // a new dep node or that the dep node has already been marked red.
- // Either way, we can't call `dep_graph.read()` as we don't have the
- // DepNodeIndex. We must invoke the query itself. The performance cost
- // this introduces should be negligible as we'll immediately hit the
- // in-memory cache, or another query down the line will.
-
- let _ = self.get_query::<Q>(DUMMY_SP, key);
- } else {
- self.prof.query_cache_hit(Q::NAME);
+ match self.dep_graph.try_mark_green_and_read(self, &dep_node) {
+ None => {
+ // A None return from `try_mark_green_and_read` means that this is either
+ // a new dep node or that the dep node has already been marked red.
+ // Either way, we can't call `dep_graph.read()` as we don't have the
+ // DepNodeIndex. We must invoke the query itself. The performance cost
+ // this introduces should be negligible as we'll immediately hit the
+ // in-memory cache, or another query down the line will.
+ let _ = self.get_query::<Q>(DUMMY_SP, key);
+ }
+ Some((_, dep_node_index)) => {
+ self.prof.query_cache_hit(dep_node_index.into());
+ }
}
}
}
}
- #[allow(nonstandard_style)]
- #[derive(Clone, Copy)]
- pub enum QueryName {
- $($name),*
- }
-
- impl rustc_data_structures::profiling::QueryName for QueryName {
- fn discriminant(self) -> std::mem::Discriminant<QueryName> {
- std::mem::discriminant(&self)
- }
-
- fn as_str(self) -> &'static str {
- QueryName::as_str(&self)
- }
- }
-
- impl QueryName {
- pub fn register_with_profiler(
- profiler: &rustc_data_structures::profiling::SelfProfiler,
- ) {
- $(profiler.register_query_name(QueryName::$name);)*
- }
-
- pub fn as_str(&self) -> &'static str {
- match self {
- $(QueryName::$name => stringify!($name),)*
- }
- }
- }
-
#[allow(nonstandard_style)]
#[derive(Clone, Debug)]
pub enum Query<$tcx> {
$(Query::$name(key) => key.default_span(tcx),)*
}
}
-
- pub fn query_name(&self) -> QueryName {
- match self {
- $(Query::$name(_) => QueryName::$name,)*
- }
- }
}
impl<'a, $tcx> HashStable<StableHashingContext<'a>> for Query<$tcx> {
type Key = $K;
type Value = $V;
- const NAME: QueryName = QueryName::$name;
+ const NAME: &'static str = stringify!($name);
const CATEGORY: ProfileCategory = $category;
}
pub fn $name(self, key: $K) -> $V {
self.at(DUMMY_SP).$name(key)
})*
+
+ /// All self-profiling events generated by the query engine use
+ /// virtual `StringId`s for their `event_id`. This method makes all
+ /// those virtual `StringId`s point to actual strings.
+ ///
+ /// If we are recording only summary data, the ids will point to
+ /// just the query names. If we are recording query keys too, we
+ /// allocate the corresponding strings here.
+ pub fn alloc_self_profile_query_strings(self) {
+ use crate::ty::query::profiling_support::{
+ alloc_self_profile_query_strings_for_query_cache,
+ QueryKeyStringCache,
+ };
+
+ if !self.prof.enabled() {
+ return;
+ }
+
+ let mut string_cache = QueryKeyStringCache::new();
+
+ $({
+ alloc_self_profile_query_strings_for_query_cache(
+ self,
+ stringify!($name),
+ &self.queries.$name,
+ &mut string_cache,
+ );
+ })*
+ }
}
impl TyCtxtAt<$tcx> {
--- /dev/null
+use crate::hir::map::definitions::DefPathData;
+use crate::ty::context::TyCtxt;
+use crate::ty::query::config::QueryConfig;
+use crate::ty::query::plumbing::QueryCache;
+use measureme::{StringComponent, StringId};
+use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::profiling::SelfProfiler;
+use rustc_data_structures::sharded::Sharded;
+use rustc_hir::def_id::{CrateNum, DefId, DefIndex, CRATE_DEF_INDEX, LOCAL_CRATE};
+use std::fmt::Debug;
+use std::io::Write;
+
+pub struct QueryKeyStringCache {
+ def_id_cache: FxHashMap<DefId, StringId>,
+}
+
+impl QueryKeyStringCache {
+ pub fn new() -> QueryKeyStringCache {
+ QueryKeyStringCache { def_id_cache: Default::default() }
+ }
+}
+
+pub struct QueryKeyStringBuilder<'p, 'c, 'tcx> {
+ profiler: &'p SelfProfiler,
+ tcx: TyCtxt<'tcx>,
+ string_cache: &'c mut QueryKeyStringCache,
+}
+
+impl<'p, 'c, 'tcx> QueryKeyStringBuilder<'p, 'c, 'tcx> {
+ pub fn new(
+ profiler: &'p SelfProfiler,
+ tcx: TyCtxt<'tcx>,
+ string_cache: &'c mut QueryKeyStringCache,
+ ) -> QueryKeyStringBuilder<'p, 'c, 'tcx> {
+ QueryKeyStringBuilder { profiler, tcx, string_cache }
+ }
+
+ // The current implementation is rather crude. In the future it might be a
+ // good idea to base this on `ty::print` in order to get nicer and more
+ // efficient query keys.
+ fn def_id_to_string_id(&mut self, def_id: DefId) -> StringId {
+ if let Some(&string_id) = self.string_cache.def_id_cache.get(&def_id) {
+ return string_id;
+ }
+
+ let def_key = self.tcx.def_key(def_id);
+
+ let (parent_string_id, start_index) = match def_key.parent {
+ Some(parent_index) => {
+ let parent_def_id = DefId { index: parent_index, krate: def_id.krate };
+
+ (self.def_id_to_string_id(parent_def_id), 0)
+ }
+ None => (StringId::INVALID, 2),
+ };
+
+ let dis_buffer = &mut [0u8; 16];
+ let name;
+ let dis;
+ let end_index;
+
+ match def_key.disambiguated_data.data {
+ DefPathData::CrateRoot => {
+ name = self.tcx.original_crate_name(def_id.krate).as_str();
+ dis = "";
+ end_index = 3;
+ }
+ other => {
+ name = other.as_symbol().as_str();
+ if def_key.disambiguated_data.disambiguator == 0 {
+ dis = "";
+ end_index = 3;
+ } else {
+ write!(&mut dis_buffer[..], "[{}]", def_key.disambiguated_data.disambiguator)
+ .unwrap();
+ let end_of_dis = dis_buffer.iter().position(|&c| c == b']').unwrap();
+ dis = std::str::from_utf8(&dis_buffer[..end_of_dis + 1]).unwrap();
+ end_index = 4;
+ }
+ }
+ }
+
+ let components = [
+ StringComponent::Ref(parent_string_id),
+ StringComponent::Value("::"),
+ StringComponent::Value(&name[..]),
+ StringComponent::Value(dis),
+ ];
+
+ let string_id = self.profiler.alloc_string(&components[start_index..end_index]);
+
+ self.string_cache.def_id_cache.insert(def_id, string_id);
+
+ string_id
+ }
+}
+
+pub trait IntoSelfProfilingString {
+ fn to_self_profile_string(&self, builder: &mut QueryKeyStringBuilder<'_, '_, '_>) -> StringId;
+}
+
+// The default implementation of `IntoSelfProfilingString` just uses `Debug`
+// which is slow and causes lots of duplication of string data.
+// The specialized impls below take care of making the `DefId` case more
+// efficient.
+impl<T: Debug> IntoSelfProfilingString for T {
+ default fn to_self_profile_string(
+ &self,
+ builder: &mut QueryKeyStringBuilder<'_, '_, '_>,
+ ) -> StringId {
+ let s = format!("{:?}", self);
+ builder.profiler.alloc_string(&s[..])
+ }
+}
+
+impl IntoSelfProfilingString for DefId {
+ fn to_self_profile_string(&self, builder: &mut QueryKeyStringBuilder<'_, '_, '_>) -> StringId {
+ builder.def_id_to_string_id(*self)
+ }
+}
+
+impl IntoSelfProfilingString for CrateNum {
+ fn to_self_profile_string(&self, builder: &mut QueryKeyStringBuilder<'_, '_, '_>) -> StringId {
+ builder.def_id_to_string_id(DefId { krate: *self, index: CRATE_DEF_INDEX })
+ }
+}
+
+impl IntoSelfProfilingString for DefIndex {
+ fn to_self_profile_string(&self, builder: &mut QueryKeyStringBuilder<'_, '_, '_>) -> StringId {
+ builder.def_id_to_string_id(DefId { krate: LOCAL_CRATE, index: *self })
+ }
+}
+
+impl<T0, T1> IntoSelfProfilingString for (T0, T1)
+where
+ T0: IntoSelfProfilingString + Debug,
+ T1: IntoSelfProfilingString + Debug,
+{
+ default fn to_self_profile_string(
+ &self,
+ builder: &mut QueryKeyStringBuilder<'_, '_, '_>,
+ ) -> StringId {
+ let val0 = self.0.to_self_profile_string(builder);
+ let val1 = self.1.to_self_profile_string(builder);
+
+ let components = &[
+ StringComponent::Value("("),
+ StringComponent::Ref(val0),
+ StringComponent::Value(","),
+ StringComponent::Ref(val1),
+ StringComponent::Value(")"),
+ ];
+
+ builder.profiler.alloc_string(components)
+ }
+}
+
+/// Allocate the self-profiling query strings for a single query cache. This
+/// method is called from `alloc_self_profile_query_strings` which knows all
+/// the queries via macro magic.
+pub(super) fn alloc_self_profile_query_strings_for_query_cache<'tcx, Q>(
+ tcx: TyCtxt<'tcx>,
+ query_name: &'static str,
+ query_cache: &Sharded<QueryCache<'tcx, Q>>,
+ string_cache: &mut QueryKeyStringCache,
+) where
+ Q: QueryConfig<'tcx>,
+{
+ tcx.prof.with_profiler(|profiler| {
+ let event_id_builder = profiler.event_id_builder();
+
+ // Walk the entire query cache and allocate the appropriate
+ // string representations. Each cache entry is uniquely
+ // identified by its dep_node_index.
+ if profiler.query_key_recording_enabled() {
+ let mut query_string_builder = QueryKeyStringBuilder::new(profiler, tcx, string_cache);
+
+ let query_name = profiler.get_or_alloc_cached_string(query_name);
+
+ // Since building the string representation of query keys might
+ // need to invoke queries itself, we cannot keep the query caches
+ // locked while doing so. Instead we copy out the
+ // `(query_key, dep_node_index)` pairs and release the lock again.
+ let query_keys_and_indices = {
+ let shards = query_cache.lock_shards();
+ let len = shards.iter().map(|shard| shard.results.len()).sum();
+
+ let mut query_keys_and_indices = Vec::with_capacity(len);
+
+ for shard in &shards {
+ query_keys_and_indices.extend(
+ shard.results.iter().map(|(q_key, q_val)| (q_key.clone(), q_val.index)),
+ );
+ }
+
+ query_keys_and_indices
+ };
+
+ // Now actually allocate the strings. If allocating the strings
+ // generates new entries in the query cache, we'll miss them but
+ // we don't actually care.
+ for (query_key, dep_node_index) in query_keys_and_indices {
+ // Translate the DepNodeIndex into a QueryInvocationId
+ let query_invocation_id = dep_node_index.into();
+
+ // Create the string version of the query-key
+ let query_key = query_key.to_self_profile_string(&mut query_string_builder);
+ let event_id = event_id_builder.from_label_and_arg(query_name, query_key);
+
+ // Doing this in bulk might be a good idea:
+ profiler.map_query_invocation_id_to_string(
+ query_invocation_id,
+ event_id.to_string_id(),
+ );
+ }
+ } else {
+ // In this branch we don't allocate query keys
+ let query_name = profiler.get_or_alloc_cached_string(query_name);
+ let event_id = event_id_builder.from_label(query_name).to_string_id();
+
+ let shards = query_cache.lock_shards();
+
+ for shard in shards.iter() {
+ let query_invocation_ids = shard
+ .results
+ .values()
+ .map(|v| v.index)
+ .map(|dep_node_index| dep_node_index.into());
+
+ profiler
+ .bulk_map_query_invocation_id_to_single_string(query_invocation_ids, event_id);
+ }
+ }
+ });
+}
// FIXME(const_generics): this is wrong, as it is a projection
(
- ty::ConstKind::Unevaluated(a_def_id, a_substs),
- ty::ConstKind::Unevaluated(b_def_id, b_substs),
- ) if a_def_id == b_def_id => {
+ ty::ConstKind::Unevaluated(a_def_id, a_substs, a_promoted),
+ ty::ConstKind::Unevaluated(b_def_id, b_substs, b_promoted),
+ ) if a_def_id == b_def_id && a_promoted == b_promoted => {
let substs =
relation.relate_with_variance(ty::Variance::Invariant, &a_substs, &b_substs)?;
- Ok(ty::ConstKind::Unevaluated(a_def_id, &substs))
+ Ok(ty::ConstKind::Unevaluated(a_def_id, &substs, a_promoted))
}
_ => Err(TypeError::ConstMismatch(expected_found(relation, &a, &b))),
};
}
}
-impl fmt::Debug for ty::ClosureUpvar<'tcx> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "ClosureUpvar({:?},{:?})", self.res, self.ty)
- }
-}
-
impl fmt::Debug for ty::UpvarId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = ty::tls::with(|tcx| tcx.hir().name(self.var_path.hir_id));
match *self {
ty::ConstKind::Infer(ic) => ty::ConstKind::Infer(ic.fold_with(folder)),
ty::ConstKind::Param(p) => ty::ConstKind::Param(p.fold_with(folder)),
- ty::ConstKind::Unevaluated(did, substs) => {
- ty::ConstKind::Unevaluated(did, substs.fold_with(folder))
+ ty::ConstKind::Unevaluated(did, substs, promoted) => {
+ ty::ConstKind::Unevaluated(did, substs.fold_with(folder), promoted)
}
ty::ConstKind::Value(_) | ty::ConstKind::Bound(..) | ty::ConstKind::Placeholder(..) => {
*self
match *self {
ty::ConstKind::Infer(ic) => ic.visit_with(visitor),
ty::ConstKind::Param(p) => p.visit_with(visitor),
- ty::ConstKind::Unevaluated(_, substs) => substs.visit_with(visitor),
+ ty::ConstKind::Unevaluated(_, substs, _) => substs.visit_with(visitor),
ty::ConstKind::Value(_) | ty::ConstKind::Bound(..) | ty::ConstKind::Placeholder(_) => {
false
}
+++ /dev/null
-use crate::ty::fold::{TypeFoldable, TypeVisitor};
-use crate::ty::{self, AdtDef, Ty, TyCtxt};
-
-use rustc::infer::InferCtxt;
-use rustc::traits::ObligationCause;
-use rustc::traits::{self, ConstPatternStructural, TraitEngine};
-use rustc_data_structures::fx::FxHashSet;
-use rustc_hir as hir;
-use rustc_span::Span;
-
-#[derive(Debug)]
-pub enum NonStructuralMatchTy<'tcx> {
- Adt(&'tcx AdtDef),
- Param,
-}
-
-/// This method traverses the structure of `ty`, trying to find an
-/// instance of an ADT (i.e. struct or enum) that was declared without
-/// the `#[structural_match]` attribute, or a generic type parameter
-/// (which cannot be determined to be `structural_match`).
-///
-/// The "structure of a type" includes all components that would be
-/// considered when doing a pattern match on a constant of that
-/// type.
-///
-/// * This means this method descends into fields of structs/enums,
-/// and also descends into the inner type `T` of `&T` and `&mut T`
-///
-/// * The traversal doesn't dereference unsafe pointers (`*const T`,
-/// `*mut T`), and it does not visit the type arguments of an
-/// instantiated generic like `PhantomData<T>`.
-///
-/// The reason we do this search is Rust currently require all ADTs
-/// reachable from a constant's type to be annotated with
-/// `#[structural_match]`, an attribute which essentially says that
-/// the implementation of `PartialEq::eq` behaves *equivalently* to a
-/// comparison against the unfolded structure.
-///
-/// For more background on why Rust has this requirement, and issues
-/// that arose when the requirement was not enforced completely, see
-/// Rust RFC 1445, rust-lang/rust#61188, and rust-lang/rust#62307.
-pub fn search_for_structural_match_violation<'tcx>(
- id: hir::HirId,
- span: Span,
- tcx: TyCtxt<'tcx>,
- ty: Ty<'tcx>,
-) -> Option<NonStructuralMatchTy<'tcx>> {
- // FIXME: we should instead pass in an `infcx` from the outside.
- tcx.infer_ctxt().enter(|infcx| {
- let mut search = Search { id, span, infcx, found: None, seen: FxHashSet::default() };
- ty.visit_with(&mut search);
- search.found
- })
-}
-
-/// 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.
-pub fn type_marked_structural(
- id: hir::HirId,
- span: Span,
- infcx: &InferCtxt<'_, 'tcx>,
- adt_ty: Ty<'tcx>,
-) -> bool {
- let mut fulfillment_cx = traits::FulfillmentContext::new();
- let cause = ObligationCause::new(span, id, ConstPatternStructural);
- // require `#[derive(PartialEq)]`
- let structural_peq_def_id = infcx.tcx.lang_items().structural_peq_trait().unwrap();
- fulfillment_cx.register_bound(
- infcx,
- ty::ParamEnv::empty(),
- adt_ty,
- structural_peq_def_id,
- cause,
- );
- // 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 cause = ObligationCause::new(span, id, ConstPatternStructural);
- let structural_teq_def_id = infcx.tcx.lang_items().structural_teq_trait().unwrap();
- fulfillment_cx.register_bound(
- infcx,
- ty::ParamEnv::empty(),
- adt_ty,
- structural_teq_def_id,
- cause,
- );
-
- // 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.
- fulfillment_cx.select_all_or_error(infcx).is_ok()
-}
-
-/// This implements the traversal over the structure of a given type to try to
-/// find instances of ADTs (specifically structs or enums) that do not implement
-/// the structural-match traits (`StructuralPartialEq` and `StructuralEq`).
-struct Search<'a, 'tcx> {
- id: hir::HirId,
- span: Span,
-
- infcx: InferCtxt<'a, 'tcx>,
-
- /// Records first ADT that does not implement a structural-match trait.
- found: Option<NonStructuralMatchTy<'tcx>>,
-
- /// Tracks ADTs previously encountered during search, so that
- /// we will not recur on them again.
- seen: FxHashSet<hir::def_id::DefId>,
-}
-
-impl Search<'a, 'tcx> {
- fn tcx(&self) -> TyCtxt<'tcx> {
- self.infcx.tcx
- }
-
- fn type_marked_structural(&self, adt_ty: Ty<'tcx>) -> bool {
- type_marked_structural(self.id, self.span, &self.infcx, adt_ty)
- }
-}
-
-impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> {
- fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool {
- debug!("Search visiting ty: {:?}", ty);
-
- let (adt_def, substs) = match ty.kind {
- ty::Adt(adt_def, substs) => (adt_def, substs),
- ty::Param(_) => {
- self.found = Some(NonStructuralMatchTy::Param);
- return true; // Stop visiting.
- }
- ty::RawPtr(..) => {
- // structural-match ignores substructure of
- // `*const _`/`*mut _`, so skip `super_visit_with`.
- //
- // For example, if you have:
- // ```
- // struct NonStructural;
- // #[derive(PartialEq, Eq)]
- // struct T(*const NonStructural);
- // const C: T = T(std::ptr::null());
- // ```
- //
- // Even though `NonStructural` does not implement `PartialEq`,
- // structural equality on `T` does not recur into the raw
- // pointer. Therefore, one can still use `C` in a pattern.
-
- // (But still tell caller to continue search.)
- return false;
- }
- ty::FnDef(..) | ty::FnPtr(..) => {
- // types of formals and return in `fn(_) -> _` are also irrelevant;
- // so we do not recur into them via `super_visit_with`
- //
- // (But still tell caller to continue search.)
- return false;
- }
- ty::Array(_, n)
- if { n.try_eval_usize(self.tcx(), ty::ParamEnv::reveal_all()) == Some(0) } =>
- {
- // rust-lang/rust#62336: ignore type of contents
- // for empty array.
- return false;
- }
- _ => {
- ty.super_visit_with(self);
- return false;
- }
- };
-
- if !self.seen.insert(adt_def.did) {
- debug!("Search already seen adt_def: {:?}", adt_def);
- // let caller continue its search
- return false;
- }
-
- if !self.type_marked_structural(ty) {
- debug!("Search found ty: {:?}", ty);
- self.found = Some(NonStructuralMatchTy::Adt(&adt_def));
- return true; // Halt visiting!
- }
-
- // structural-match does not care about the
- // instantiation of the generics in an ADT (it
- // instead looks directly at its fields outside
- // this match), so we skip super_visit_with.
- //
- // (Must not recur on substs for `PhantomData<T>` cf
- // rust-lang/rust#55028 and rust-lang/rust#55837; but also
- // want to skip substs when only uses of generic are
- // behind unsafe pointers `*const T`/`*mut T`.)
-
- // even though we skip super_visit_with, we must recur on
- // fields of ADT.
- let tcx = self.tcx();
- for field_ty in adt_def.all_fields().map(|field| field.ty(tcx, substs)) {
- if field_ty.visit_with(self) {
- // found an ADT without structural-match; halt visiting!
- assert!(self.found.is_some());
- return true;
- }
- }
-
- // Even though we do not want to recur on substs, we do
- // want our caller to continue its own search.
- false
- }
-}
use crate::middle::region;
use crate::mir::interpret::ConstValue;
use crate::mir::interpret::Scalar;
+use crate::mir::Promoted;
use crate::ty::layout::VariantIdx;
use crate::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, Subst, SubstsRef};
use crate::ty::{self, AdtDef, DefIdTree, Discr, Ty, TyCtxt, TypeFlags, TypeFoldable};
use crate::ty::{List, ParamEnv, ParamEnvAnd, TyS};
-use crate::util::captures::Captures;
+use polonius_engine::Atom;
+use rustc_data_structures::captures::Captures;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
-
-use polonius_engine::Atom;
use rustc_index::vec::Idx;
use rustc_macros::HashStable;
use rustc_span::symbol::{kw, Symbol};
#[inline]
pub fn eval(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> &Const<'tcx> {
- let try_const_eval = |did, param_env: ParamEnv<'tcx>, substs| {
+ let try_const_eval = |did, param_env: ParamEnv<'tcx>, substs, promoted| {
let param_env_and_substs = param_env.with_reveal_all().and(substs);
// Avoid querying `tcx.const_eval(...)` with any e.g. inference vars.
// try to resolve e.g. associated constants to their definition on an impl, and then
// evaluate the const.
- tcx.const_eval_resolve(param_env, did, substs, None).ok()
+ tcx.const_eval_resolve(param_env, did, substs, promoted, None).ok()
};
match self.val {
- ConstKind::Unevaluated(did, substs) => {
+ ConstKind::Unevaluated(did, substs, promoted) => {
// HACK(eddyb) when substs contain e.g. inference variables,
// attempt using identity substs instead, that will succeed
// when the expression doesn't depend on any parameters.
let identity_substs = InternalSubsts::identity_for_item(tcx, did);
// The `ParamEnv` needs to match the `identity_substs`.
let identity_param_env = tcx.param_env(did);
- match try_const_eval(did, identity_param_env, identity_substs) {
+ match try_const_eval(did, identity_param_env, identity_substs, promoted) {
Some(ct) => ct.subst(tcx, substs),
None => self,
}
} else {
- try_const_eval(did, param_env, substs).unwrap_or(self)
+ try_const_eval(did, param_env, substs, promoted).unwrap_or(self)
}
}
_ => self,
/// Used in the HIR by using `Unevaluated` everywhere and later normalizing to one of the other
/// variants when the code is monomorphic enough for that.
- Unevaluated(DefId, SubstsRef<'tcx>),
+ Unevaluated(DefId, SubstsRef<'tcx>, Option<Promoted>),
/// Used to hold computed value.
Value(ConstValue<'tcx>),
use crate::hir::map::DefPathData;
use crate::ich::NodeIdHashingMode;
-use crate::middle::lang_items;
use crate::mir::interpret::{sign_extend, truncate};
-use crate::traits::{self, ObligationCause};
-use crate::ty::layout::{Integer, IntegerExt};
+use crate::ty::layout::{Integer, IntegerExt, Size};
use crate::ty::query::TyCtxtAt;
use crate::ty::subst::{GenericArgKind, InternalSubsts, Subst, SubstsRef};
use crate::ty::TyKind::*;
use crate::ty::{self, DefIdTree, GenericParamDefKind, Ty, TyCtxt, TypeFoldable};
use crate::util::common::ErrorReported;
+use rustc_apfloat::Float as _;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId;
-
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_macros::HashStable;
-use rustc_span::{Span, DUMMY_SP};
+use rustc_span::Span;
use std::{cmp, fmt};
use syntax::ast;
use syntax::attr::{self, SignedInt, UnsignedInt};
}
}
+fn signed_min(size: Size) -> i128 {
+ sign_extend(1_u128 << (size.bits() - 1), size) as i128
+}
+
+fn signed_max(size: Size) -> i128 {
+ i128::max_value() >> (128 - size.bits())
+}
+
+fn unsigned_max(size: Size) -> u128 {
+ u128::max_value() >> (128 - size.bits())
+}
+
+fn int_size_and_signed<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> (Size, bool) {
+ let (int, signed) = match ty.kind {
+ Int(ity) => (Integer::from_attr(&tcx, SignedInt(ity)), true),
+ Uint(uty) => (Integer::from_attr(&tcx, UnsignedInt(uty)), false),
+ _ => bug!("non integer discriminant"),
+ };
+ (int.size(), signed)
+}
+
impl<'tcx> Discr<'tcx> {
/// Adds `1` to the value and wraps around if the maximum for the type is reached.
pub fn wrap_incr(self, tcx: TyCtxt<'tcx>) -> Self {
self.checked_add(tcx, 1).0
}
pub fn checked_add(self, tcx: TyCtxt<'tcx>, n: u128) -> (Self, bool) {
- let (int, signed) = match self.ty.kind {
- Int(ity) => (Integer::from_attr(&tcx, SignedInt(ity)), true),
- Uint(uty) => (Integer::from_attr(&tcx, UnsignedInt(uty)), false),
- _ => bug!("non integer discriminant"),
- };
-
- let size = int.size();
- let bit_size = int.size().bits();
- let shift = 128 - bit_size;
- if signed {
- let sext = |u| sign_extend(u, size) as i128;
- let min = sext(1_u128 << (bit_size - 1));
- let max = i128::max_value() >> shift;
- let val = sext(self.val);
+ let (size, signed) = int_size_and_signed(tcx, self.ty);
+ let (val, oflo) = if signed {
+ let min = signed_min(size);
+ let max = signed_max(size);
+ let val = sign_extend(self.val, size) as i128;
assert!(n < (i128::max_value() as u128));
let n = n as i128;
let oflo = val > max - n;
// zero the upper bits
let val = val as u128;
let val = truncate(val, size);
- (Self { val: val as u128, ty: self.ty }, oflo)
+ (val, oflo)
} else {
- let max = u128::max_value() >> shift;
+ let max = unsigned_max(size);
let val = self.val;
let oflo = val > max - n;
let val = if oflo { n - (max - val) - 1 } else { val + n };
- (Self { val: val, ty: self.ty }, oflo)
- }
+ (val, oflo)
+ };
+ (Self { val, ty: self.ty }, oflo)
}
}
}
}
-#[derive(Clone)]
-pub enum CopyImplementationError<'tcx> {
- InfrigingFields(Vec<&'tcx ty::FieldDef>),
- NotAnAdt,
- HasDestructor,
-}
-
/// Describes whether a type is representable. For types that are not
/// representable, 'SelfRecursive' and 'ContainsRecursive' are used to
/// distinguish between types that are recursive with themselves and types that
SelfRecursive(Vec<Span>),
}
-impl<'tcx> ty::ParamEnv<'tcx> {
- pub fn can_type_implement_copy(
- self,
- tcx: TyCtxt<'tcx>,
- self_type: Ty<'tcx>,
- ) -> Result<(), CopyImplementationError<'tcx>> {
- // FIXME: (@jroesch) float this code up
- tcx.infer_ctxt().enter(|infcx| {
- let (adt, substs) = match self_type.kind {
- // These types used to have a builtin impl.
- // Now libcore provides that impl.
- ty::Uint(_)
- | ty::Int(_)
- | ty::Bool
- | ty::Float(_)
- | ty::Char
- | ty::RawPtr(..)
- | ty::Never
- | ty::Ref(_, _, hir::Mutability::Not) => return Ok(()),
-
- ty::Adt(adt, substs) => (adt, substs),
-
- _ => return Err(CopyImplementationError::NotAnAdt),
- };
-
- let mut infringing = Vec::new();
- for variant in &adt.variants {
- for field in &variant.fields {
- let ty = field.ty(tcx, substs);
- if ty.references_error() {
- continue;
- }
- let span = tcx.def_span(field.did);
- let cause = ObligationCause { span, ..ObligationCause::dummy() };
- let ctx = traits::FulfillmentContext::new();
- match traits::fully_normalize(&infcx, ctx, cause, self, &ty) {
- Ok(ty) => {
- if !infcx.type_is_copy_modulo_regions(self, ty, span) {
- infringing.push(field);
- }
- }
- Err(errors) => {
- infcx.report_fulfillment_errors(&errors, None, false);
- }
- };
- }
- }
- if !infringing.is_empty() {
- return Err(CopyImplementationError::InfrigingFields(infringing));
- }
- if adt.has_dtor(tcx) {
- return Err(CopyImplementationError::HasDestructor);
- }
-
- Ok(())
- })
- }
-}
-
impl<'tcx> TyCtxt<'tcx> {
/// Creates a hash of the type `Ty` which will be the same no matter what crate
/// context it's calculated within. This is used by the `type_id` intrinsic.
(a, b)
}
- /// Given a set of predicates that apply to an object type, returns
- /// the region bounds that the (erased) `Self` type must
- /// outlive. Precisely *because* the `Self` type is erased, the
- /// parameter `erased_self_ty` must be supplied to indicate what type
- /// has been used to represent `Self` in the predicates
- /// themselves. This should really be a unique type; `FreshTy(0)` is a
- /// popular choice.
- ///
- /// N.B., in some cases, particularly around higher-ranked bounds,
- /// this function returns a kind of conservative approximation.
- /// That is, all regions returned by this function are definitely
- /// required, but there may be other region bounds that are not
- /// returned, as well as requirements like `for<'a> T: 'a`.
- ///
- /// Requires that trait definitions have been processed so that we can
- /// elaborate predicates and walk supertraits.
- //
- // FIXME: callers may only have a `&[Predicate]`, not a `Vec`, so that's
- // what this code should accept.
- pub fn required_region_bounds(
- self,
- erased_self_ty: Ty<'tcx>,
- predicates: Vec<ty::Predicate<'tcx>>,
- ) -> Vec<ty::Region<'tcx>> {
- debug!(
- "required_region_bounds(erased_self_ty={:?}, predicates={:?})",
- erased_self_ty, predicates
- );
-
- assert!(!erased_self_ty.has_escaping_bound_vars());
-
- traits::elaborate_predicates(self, predicates)
- .filter_map(|predicate| {
- match predicate {
- ty::Predicate::Projection(..)
- | ty::Predicate::Trait(..)
- | ty::Predicate::Subtype(..)
- | ty::Predicate::WellFormed(..)
- | ty::Predicate::ObjectSafe(..)
- | ty::Predicate::ClosureKind(..)
- | ty::Predicate::RegionOutlives(..)
- | ty::Predicate::ConstEvaluatable(..) => None,
- ty::Predicate::TypeOutlives(predicate) => {
- // Search for a bound of the form `erased_self_ty
- // : 'a`, but be wary of something like `for<'a>
- // erased_self_ty : 'a` (we interpret a
- // higher-ranked bound like that as 'static,
- // though at present the code in `fulfill.rs`
- // considers such bounds to be unsatisfiable, so
- // it's kind of a moot point since you could never
- // construct such an object, but this seems
- // correct even if that code changes).
- let ty::OutlivesPredicate(ref t, ref r) = predicate.skip_binder();
- if t == &erased_self_ty && !r.has_escaping_bound_vars() {
- Some(*r)
- } else {
- None
- }
- }
- }
- })
- .collect()
- }
-
/// Calculate the destructor of a given type.
pub fn calculate_dtor(
self,
if self.is_mutable_static(def_id) {
self.mk_mut_ptr(static_ty)
- } else if self.is_foreign_item(def_id) {
- self.mk_imm_ptr(static_ty)
} else {
self.mk_imm_ref(self.lifetimes.re_erased, static_ty)
}
}
impl<'tcx> ty::TyS<'tcx> {
+ /// Returns the maximum value for the given numeric type (including `char`s)
+ /// or returns `None` if the type is not numeric.
+ pub fn numeric_max_val(&'tcx self, tcx: TyCtxt<'tcx>) -> Option<&'tcx ty::Const<'tcx>> {
+ let val = match self.kind {
+ ty::Int(_) | ty::Uint(_) => {
+ let (size, signed) = int_size_and_signed(tcx, self);
+ let val = if signed { signed_max(size) as u128 } else { unsigned_max(size) };
+ Some(val)
+ }
+ ty::Char => Some(std::char::MAX as u128),
+ ty::Float(fty) => Some(match fty {
+ ast::FloatTy::F32 => ::rustc_apfloat::ieee::Single::INFINITY.to_bits(),
+ ast::FloatTy::F64 => ::rustc_apfloat::ieee::Double::INFINITY.to_bits(),
+ }),
+ _ => None,
+ };
+ val.map(|v| ty::Const::from_bits(tcx, v, ty::ParamEnv::empty().and(self)))
+ }
+
+ /// Returns the minimum value for the given numeric type (including `char`s)
+ /// or returns `None` if the type is not numeric.
+ pub fn numeric_min_val(&'tcx self, tcx: TyCtxt<'tcx>) -> Option<&'tcx ty::Const<'tcx>> {
+ let val = match self.kind {
+ ty::Int(_) | ty::Uint(_) => {
+ let (size, signed) = int_size_and_signed(tcx, self);
+ let val = if signed { truncate(signed_min(size) as u128, size) } else { 0 };
+ Some(val)
+ }
+ ty::Char => Some(0),
+ ty::Float(fty) => Some(match fty {
+ ast::FloatTy::F32 => (-::rustc_apfloat::ieee::Single::INFINITY).to_bits(),
+ ast::FloatTy::F64 => (-::rustc_apfloat::ieee::Double::INFINITY).to_bits(),
+ }),
+ _ => None,
+ };
+ val.map(|v| ty::Const::from_bits(tcx, v, ty::ParamEnv::empty().and(self)))
+ }
+
/// Checks whether values of this type `T` are *moved* or *copied*
/// when referenced -- this amounts to a check for whether `T:
/// Copy`, but note that we **don't** consider lifetimes when
}
}
-fn is_copy_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
- is_item_raw(tcx, query, lang_items::CopyTraitLangItem)
-}
-
-fn is_sized_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
- is_item_raw(tcx, query, lang_items::SizedTraitLangItem)
-}
-
-fn is_freeze_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
- is_item_raw(tcx, query, lang_items::FreezeTraitLangItem)
-}
-
-fn is_item_raw<'tcx>(
- tcx: TyCtxt<'tcx>,
- query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
- item: lang_items::LangItem,
-) -> bool {
- let (param_env, ty) = query.into_parts();
- let trait_def_id = tcx.require_lang_item(item, None);
- tcx.infer_ctxt().enter(|infcx| {
- traits::type_known_to_meet_bound_modulo_regions(
- &infcx,
- param_env,
- ty,
- trait_def_id,
- DUMMY_SP,
- )
- })
-}
-
#[derive(Clone, HashStable)]
pub struct NeedsDrop(pub bool);
-fn needs_drop_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> NeedsDrop {
- let (param_env, ty) = query.into_parts();
-
- let needs_drop = |ty: Ty<'tcx>| -> bool { tcx.needs_drop_raw(param_env.and(ty)).0 };
-
- assert!(!ty.needs_infer());
-
- NeedsDrop(match ty.kind {
- // Fast-path for primitive types
- ty::Infer(ty::FreshIntTy(_))
- | ty::Infer(ty::FreshFloatTy(_))
- | ty::Bool
- | ty::Int(_)
- | ty::Uint(_)
- | ty::Float(_)
- | ty::Never
- | ty::FnDef(..)
- | ty::FnPtr(_)
- | ty::Char
- | ty::GeneratorWitness(..)
- | ty::RawPtr(_)
- | ty::Ref(..)
- | ty::Str => false,
-
- // Foreign types can never have destructors
- ty::Foreign(..) => false,
-
- // `ManuallyDrop` doesn't have a destructor regardless of field types.
- ty::Adt(def, _) if Some(def.did) == tcx.lang_items().manually_drop() => false,
-
- // Issue #22536: We first query `is_copy_modulo_regions`. It sees a
- // normalized version of the type, and therefore will definitely
- // know whether the type implements Copy (and thus needs no
- // cleanup/drop/zeroing) ...
- _ if ty.is_copy_modulo_regions(tcx, param_env, DUMMY_SP) => false,
-
- // ... (issue #22536 continued) but as an optimization, still use
- // prior logic of asking for the structural "may drop".
-
- // FIXME(#22815): Note that this is a conservative heuristic;
- // it may report that the type "may drop" when actual type does
- // not actually have a destructor associated with it. But since
- // the type absolutely did not have the `Copy` bound attached
- // (see above), it is sound to treat it as having a destructor.
-
- // User destructors are the only way to have concrete drop types.
- ty::Adt(def, _) if def.has_dtor(tcx) => true,
-
- // Can refer to a type which may drop.
- // FIXME(eddyb) check this against a ParamEnv.
- ty::Dynamic(..)
- | ty::Projection(..)
- | ty::Param(_)
- | ty::Bound(..)
- | ty::Placeholder(..)
- | ty::Opaque(..)
- | ty::Infer(_)
- | ty::Error => true,
-
- ty::UnnormalizedProjection(..) => bug!("only used with chalk-engine"),
-
- // Zero-length arrays never contain anything to drop.
- ty::Array(_, len) if len.try_eval_usize(tcx, param_env) == Some(0) => false,
-
- // Structural recursion.
- ty::Array(ty, _) | ty::Slice(ty) => needs_drop(ty),
-
- ty::Closure(def_id, ref substs) => {
- substs.as_closure().upvar_tys(def_id, tcx).any(needs_drop)
- }
-
- // Pessimistically assume that all generators will require destructors
- // as we don't know if a destructor is a noop or not until after the MIR
- // state transformation pass
- ty::Generator(..) => true,
-
- ty::Tuple(..) => ty.tuple_fields().any(needs_drop),
-
- // unions don't have destructors because of the child types,
- // only if they manually implement `Drop` (handled above).
- ty::Adt(def, _) if def.is_union() => false,
-
- ty::Adt(def, substs) => def
- .variants
- .iter()
- .any(|variant| variant.fields.iter().any(|field| needs_drop(field.ty(tcx, substs)))),
- })
-}
-
pub enum ExplicitSelf<'tcx> {
ByValue,
ByReference(ty::Region<'tcx>, hir::Mutability),
}
}
}
-
-pub fn provide(providers: &mut ty::query::Providers<'_>) {
- *providers = ty::query::Providers {
- is_copy_raw,
- is_sized_raw,
- is_freeze_raw,
- needs_drop_raw,
- ..*providers
- };
-}
| ty::Bound(..)
| ty::Foreign(..) => {}
ty::Array(ty, len) => {
- if let ty::ConstKind::Unevaluated(_, substs) = len.val {
+ if let ty::ConstKind::Unevaluated(_, substs, promoted) = len.val {
+ assert!(promoted.is_none());
stack.extend(substs.types().rev());
}
stack.push(len.ty);
+++ /dev/null
-use crate::infer::InferCtxt;
-use crate::middle::lang_items;
-use crate::traits::{self, AssocTypeBoundData};
-use crate::ty::subst::SubstsRef;
-use crate::ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable};
-use rustc_hir as hir;
-use rustc_hir::def_id::DefId;
-use rustc_span::symbol::{kw, Ident};
-use rustc_span::Span;
-use std::iter::once;
-
-/// Returns the set of obligations needed to make `ty` well-formed.
-/// If `ty` contains unresolved inference variables, this may include
-/// further WF obligations. However, if `ty` IS an unresolved
-/// inference variable, returns `None`, because we are not able to
-/// make any progress at all. This is to prevent "livelock" where we
-/// say "$0 is WF if $0 is WF".
-pub fn obligations<'a, 'tcx>(
- infcx: &InferCtxt<'a, 'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- body_id: hir::HirId,
- ty: Ty<'tcx>,
- span: Span,
-) -> Option<Vec<traits::PredicateObligation<'tcx>>> {
- let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item: None };
- if wf.compute(ty) {
- debug!("wf::obligations({:?}, body_id={:?}) = {:?}", ty, body_id, wf.out);
- let result = wf.normalize();
- debug!("wf::obligations({:?}, body_id={:?}) ~~> {:?}", ty, body_id, result);
- Some(result)
- } else {
- None // no progress made, return None
- }
-}
-
-/// Returns the obligations that make this trait reference
-/// well-formed. For example, if there is a trait `Set` defined like
-/// `trait Set<K:Eq>`, then the trait reference `Foo: Set<Bar>` is WF
-/// if `Bar: Eq`.
-pub fn trait_obligations<'a, 'tcx>(
- infcx: &InferCtxt<'a, 'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- body_id: hir::HirId,
- trait_ref: &ty::TraitRef<'tcx>,
- span: Span,
- item: Option<&'tcx hir::Item<'tcx>>,
-) -> Vec<traits::PredicateObligation<'tcx>> {
- let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item };
- wf.compute_trait_ref(trait_ref, Elaborate::All);
- wf.normalize()
-}
-
-pub fn predicate_obligations<'a, 'tcx>(
- infcx: &InferCtxt<'a, 'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- body_id: hir::HirId,
- predicate: &ty::Predicate<'tcx>,
- span: Span,
-) -> Vec<traits::PredicateObligation<'tcx>> {
- let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item: None };
-
- // (*) ok to skip binders, because wf code is prepared for it
- match *predicate {
- ty::Predicate::Trait(ref t) => {
- wf.compute_trait_ref(&t.skip_binder().trait_ref, Elaborate::None); // (*)
- }
- ty::Predicate::RegionOutlives(..) => {}
- ty::Predicate::TypeOutlives(ref t) => {
- wf.compute(t.skip_binder().0);
- }
- ty::Predicate::Projection(ref t) => {
- let t = t.skip_binder(); // (*)
- wf.compute_projection(t.projection_ty);
- wf.compute(t.ty);
- }
- ty::Predicate::WellFormed(t) => {
- wf.compute(t);
- }
- ty::Predicate::ObjectSafe(_) => {}
- ty::Predicate::ClosureKind(..) => {}
- ty::Predicate::Subtype(ref data) => {
- wf.compute(data.skip_binder().a); // (*)
- wf.compute(data.skip_binder().b); // (*)
- }
- ty::Predicate::ConstEvaluatable(def_id, substs) => {
- let obligations = wf.nominal_obligations(def_id, substs);
- wf.out.extend(obligations);
-
- for ty in substs.types() {
- wf.compute(ty);
- }
- }
- }
-
- wf.normalize()
-}
-
-struct WfPredicates<'a, 'tcx> {
- infcx: &'a InferCtxt<'a, 'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- body_id: hir::HirId,
- span: Span,
- out: Vec<traits::PredicateObligation<'tcx>>,
- item: Option<&'tcx hir::Item<'tcx>>,
-}
-
-/// Controls whether we "elaborate" supertraits and so forth on the WF
-/// predicates. This is a kind of hack to address #43784. The
-/// underlying problem in that issue was a trait structure like:
-///
-/// ```
-/// trait Foo: Copy { }
-/// trait Bar: Foo { }
-/// impl<T: Bar> Foo for T { }
-/// impl<T> Bar for T { }
-/// ```
-///
-/// Here, in the `Foo` impl, we will check that `T: Copy` holds -- but
-/// we decide that this is true because `T: Bar` is in the
-/// where-clauses (and we can elaborate that to include `T:
-/// Copy`). This wouldn't be a problem, except that when we check the
-/// `Bar` impl, we decide that `T: Foo` must hold because of the `Foo`
-/// impl. And so nowhere did we check that `T: Copy` holds!
-///
-/// To resolve this, we elaborate the WF requirements that must be
-/// proven when checking impls. This means that (e.g.) the `impl Bar
-/// for T` will be forced to prove not only that `T: Foo` but also `T:
-/// Copy` (which it won't be able to do, because there is no `Copy`
-/// impl for `T`).
-#[derive(Debug, PartialEq, Eq, Copy, Clone)]
-enum Elaborate {
- All,
- None,
-}
-
-impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
- fn cause(&mut self, code: traits::ObligationCauseCode<'tcx>) -> traits::ObligationCause<'tcx> {
- traits::ObligationCause::new(self.span, self.body_id, code)
- }
-
- fn normalize(&mut self) -> Vec<traits::PredicateObligation<'tcx>> {
- let cause = self.cause(traits::MiscObligation);
- let infcx = &mut self.infcx;
- let param_env = self.param_env;
- self.out
- .iter()
- .inspect(|pred| assert!(!pred.has_escaping_bound_vars()))
- .flat_map(|pred| {
- let mut selcx = traits::SelectionContext::new(infcx);
- let pred = traits::normalize(&mut selcx, param_env, cause.clone(), pred);
- once(pred.value).chain(pred.obligations)
- })
- .collect()
- }
-
- /// Pushes the obligations required for `trait_ref` to be WF into `self.out`.
- fn compute_trait_ref(&mut self, trait_ref: &ty::TraitRef<'tcx>, elaborate: Elaborate) {
- let tcx = self.infcx.tcx;
- let obligations = self.nominal_obligations(trait_ref.def_id, trait_ref.substs);
-
- let cause = self.cause(traits::MiscObligation);
- let param_env = self.param_env;
-
- let item = &self.item;
- let extend_cause_with_original_assoc_item_obligation =
- |cause: &mut traits::ObligationCause<'_>,
- pred: &ty::Predicate<'_>,
- trait_assoc_items: ty::AssocItemsIterator<'_>| {
- let trait_item = tcx
- .hir()
- .as_local_hir_id(trait_ref.def_id)
- .and_then(|trait_id| tcx.hir().find(trait_id));
- let (trait_name, trait_generics) = match trait_item {
- Some(hir::Node::Item(hir::Item {
- ident,
- kind: hir::ItemKind::Trait(.., generics, _, _),
- ..
- }))
- | Some(hir::Node::Item(hir::Item {
- ident,
- kind: hir::ItemKind::TraitAlias(generics, _),
- ..
- })) => (Some(ident), Some(generics)),
- _ => (None, None),
- };
-
- let item_span = item.map(|i| tcx.sess.source_map().def_span(i.span));
- match pred {
- ty::Predicate::Projection(proj) => {
- // The obligation comes not from the current `impl` nor the `trait` being
- // implemented, but rather from a "second order" obligation, like in
- // `src/test/ui/associated-types/point-at-type-on-obligation-failure.rs`:
- //
- // error[E0271]: type mismatch resolving `<Foo2 as Bar2>::Ok == ()`
- // --> $DIR/point-at-type-on-obligation-failure.rs:13:5
- // |
- // LL | type Ok;
- // | -- associated type defined here
- // ...
- // LL | impl Bar for Foo {
- // | ---------------- in this `impl` item
- // LL | type Ok = ();
- // | ^^^^^^^^^^^^^ expected `u32`, found `()`
- // |
- // = note: expected type `u32`
- // found type `()`
- //
- // FIXME: we would want to point a span to all places that contributed to this
- // obligation. In the case above, it should be closer to:
- //
- // error[E0271]: type mismatch resolving `<Foo2 as Bar2>::Ok == ()`
- // --> $DIR/point-at-type-on-obligation-failure.rs:13:5
- // |
- // LL | type Ok;
- // | -- associated type defined here
- // LL | type Sibling: Bar2<Ok=Self::Ok>;
- // | -------------------------------- obligation set here
- // ...
- // LL | impl Bar for Foo {
- // | ---------------- in this `impl` item
- // LL | type Ok = ();
- // | ^^^^^^^^^^^^^ expected `u32`, found `()`
- // ...
- // LL | impl Bar2 for Foo2 {
- // | ---------------- in this `impl` item
- // LL | type Ok = u32;
- // | -------------- obligation set here
- // |
- // = note: expected type `u32`
- // found type `()`
- if let Some(hir::ItemKind::Impl(.., impl_items)) = item.map(|i| &i.kind) {
- let trait_assoc_item = tcx.associated_item(proj.projection_def_id());
- if let Some(impl_item) = impl_items
- .iter()
- .filter(|item| item.ident == trait_assoc_item.ident)
- .next()
- {
- cause.span = impl_item.span;
- cause.code = traits::AssocTypeBound(Box::new(AssocTypeBoundData {
- impl_span: item_span,
- original: trait_assoc_item.ident.span,
- bounds: vec![],
- }));
- }
- }
- }
- ty::Predicate::Trait(proj) => {
- // An associated item obligation born out of the `trait` failed to be met.
- // Point at the `impl` that failed the obligation, the associated item that
- // needed to meet the obligation, and the definition of that associated item,
- // which should hold the obligation in most cases. An example can be seen in
- // `src/test/ui/associated-types/point-at-type-on-obligation-failure-2.rs`:
- //
- // error[E0277]: the trait bound `bool: Bar` is not satisfied
- // --> $DIR/point-at-type-on-obligation-failure-2.rs:8:5
- // |
- // LL | type Assoc: Bar;
- // | ----- associated type defined here
- // ...
- // LL | impl Foo for () {
- // | --------------- in this `impl` item
- // LL | type Assoc = bool;
- // | ^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `bool`
- //
- // If the obligation comes from the where clause in the `trait`, we point at it:
- //
- // error[E0277]: the trait bound `bool: Bar` is not satisfied
- // --> $DIR/point-at-type-on-obligation-failure-2.rs:8:5
- // |
- // | trait Foo where <Self as Foo>>::Assoc: Bar {
- // | -------------------------- restricted in this bound
- // LL | type Assoc;
- // | ----- associated type defined here
- // ...
- // LL | impl Foo for () {
- // | --------------- in this `impl` item
- // LL | type Assoc = bool;
- // | ^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `bool`
- if let (
- ty::Projection(ty::ProjectionTy { item_def_id, .. }),
- Some(hir::ItemKind::Impl(.., impl_items)),
- ) = (&proj.skip_binder().self_ty().kind, item.map(|i| &i.kind))
- {
- if let Some((impl_item, trait_assoc_item)) = trait_assoc_items
- .filter(|i| i.def_id == *item_def_id)
- .next()
- .and_then(|trait_assoc_item| {
- impl_items
- .iter()
- .filter(|i| i.ident == trait_assoc_item.ident)
- .next()
- .map(|impl_item| (impl_item, trait_assoc_item))
- })
- {
- let bounds = trait_generics
- .map(|generics| {
- get_generic_bound_spans(
- &generics,
- trait_name,
- trait_assoc_item.ident,
- )
- })
- .unwrap_or_else(Vec::new);
- cause.span = impl_item.span;
- cause.code = traits::AssocTypeBound(Box::new(AssocTypeBoundData {
- impl_span: item_span,
- original: trait_assoc_item.ident.span,
- bounds,
- }));
- }
- }
- }
- _ => {}
- }
- };
-
- if let Elaborate::All = elaborate {
- let trait_assoc_items = tcx.associated_items(trait_ref.def_id);
-
- let predicates =
- obligations.iter().map(|obligation| obligation.predicate.clone()).collect();
- let implied_obligations = traits::elaborate_predicates(tcx, predicates);
- let implied_obligations = implied_obligations.map(|pred| {
- let mut cause = cause.clone();
- extend_cause_with_original_assoc_item_obligation(
- &mut cause,
- &pred,
- trait_assoc_items.clone(),
- );
- traits::Obligation::new(cause, param_env, pred)
- });
- self.out.extend(implied_obligations);
- }
-
- self.out.extend(obligations);
-
- self.out.extend(trait_ref.substs.types().filter(|ty| !ty.has_escaping_bound_vars()).map(
- |ty| traits::Obligation::new(cause.clone(), param_env, ty::Predicate::WellFormed(ty)),
- ));
- }
-
- /// Pushes the obligations required for `trait_ref::Item` to be WF
- /// into `self.out`.
- fn compute_projection(&mut self, data: ty::ProjectionTy<'tcx>) {
- // A projection is well-formed if (a) the trait ref itself is
- // WF and (b) the trait-ref holds. (It may also be
- // normalizable and be WF that way.)
- let trait_ref = data.trait_ref(self.infcx.tcx);
- self.compute_trait_ref(&trait_ref, Elaborate::None);
-
- if !data.has_escaping_bound_vars() {
- let predicate = trait_ref.to_predicate();
- let cause = self.cause(traits::ProjectionWf(data));
- self.out.push(traits::Obligation::new(cause, self.param_env, predicate));
- }
- }
-
- /// Pushes the obligations required for an array length to be WF
- /// into `self.out`.
- fn compute_array_len(&mut self, constant: ty::Const<'tcx>) {
- if let ty::ConstKind::Unevaluated(def_id, substs) = constant.val {
- let obligations = self.nominal_obligations(def_id, substs);
- self.out.extend(obligations);
-
- let predicate = ty::Predicate::ConstEvaluatable(def_id, substs);
- let cause = self.cause(traits::MiscObligation);
- self.out.push(traits::Obligation::new(cause, self.param_env, predicate));
- }
- }
-
- fn require_sized(&mut self, subty: Ty<'tcx>, cause: traits::ObligationCauseCode<'tcx>) {
- if !subty.has_escaping_bound_vars() {
- let cause = self.cause(cause);
- let trait_ref = ty::TraitRef {
- def_id: self.infcx.tcx.require_lang_item(lang_items::SizedTraitLangItem, None),
- substs: self.infcx.tcx.mk_substs_trait(subty, &[]),
- };
- self.out.push(traits::Obligation::new(cause, self.param_env, trait_ref.to_predicate()));
- }
- }
-
- /// Pushes new obligations into `out`. Returns `true` if it was able
- /// to generate all the predicates needed to validate that `ty0`
- /// is WF. Returns false if `ty0` is an unresolved type variable,
- /// in which case we are not able to simplify at all.
- fn compute(&mut self, ty0: Ty<'tcx>) -> bool {
- let mut subtys = ty0.walk();
- let param_env = self.param_env;
- while let Some(ty) = subtys.next() {
- match ty.kind {
- ty::Bool
- | ty::Char
- | ty::Int(..)
- | ty::Uint(..)
- | ty::Float(..)
- | ty::Error
- | ty::Str
- | ty::GeneratorWitness(..)
- | ty::Never
- | ty::Param(_)
- | ty::Bound(..)
- | ty::Placeholder(..)
- | ty::Foreign(..) => {
- // WfScalar, WfParameter, etc
- }
-
- ty::Slice(subty) => {
- self.require_sized(subty, traits::SliceOrArrayElem);
- }
-
- ty::Array(subty, len) => {
- self.require_sized(subty, traits::SliceOrArrayElem);
- self.compute_array_len(*len);
- }
-
- ty::Tuple(ref tys) => {
- if let Some((_last, rest)) = tys.split_last() {
- for elem in rest {
- self.require_sized(elem.expect_ty(), traits::TupleElem);
- }
- }
- }
-
- ty::RawPtr(_) => {
- // simple cases that are WF if their type args are WF
- }
-
- ty::Projection(data) => {
- subtys.skip_current_subtree(); // subtree handled by compute_projection
- self.compute_projection(data);
- }
-
- ty::UnnormalizedProjection(..) => bug!("only used with chalk-engine"),
-
- ty::Adt(def, substs) => {
- // WfNominalType
- let obligations = self.nominal_obligations(def.did, substs);
- self.out.extend(obligations);
- }
-
- ty::FnDef(did, substs) => {
- let obligations = self.nominal_obligations(did, substs);
- self.out.extend(obligations);
- }
-
- ty::Ref(r, rty, _) => {
- // WfReference
- if !r.has_escaping_bound_vars() && !rty.has_escaping_bound_vars() {
- let cause = self.cause(traits::ReferenceOutlivesReferent(ty));
- self.out.push(traits::Obligation::new(
- cause,
- param_env,
- ty::Predicate::TypeOutlives(ty::Binder::dummy(ty::OutlivesPredicate(
- rty, r,
- ))),
- ));
- }
- }
-
- ty::Generator(..) => {
- // Walk ALL the types in the generator: this will
- // include the upvar types as well as the yield
- // type. Note that this is mildly distinct from
- // the closure case, where we have to be careful
- // about the signature of the closure. We don't
- // have the problem of implied bounds here since
- // generators don't take arguments.
- }
-
- ty::Closure(def_id, substs) => {
- // Only check the upvar types for WF, not the rest
- // of the types within. This is needed because we
- // capture the signature and it may not be WF
- // without the implied bounds. Consider a closure
- // like `|x: &'a T|` -- it may be that `T: 'a` is
- // not known to hold in the creator's context (and
- // indeed the closure may not be invoked by its
- // creator, but rather turned to someone who *can*
- // verify that).
- //
- // The special treatment of closures here really
- // ought not to be necessary either; the problem
- // is related to #25860 -- there is no way for us
- // to express a fn type complete with the implied
- // bounds that it is assuming. I think in reality
- // the WF rules around fn are a bit messed up, and
- // that is the rot problem: `fn(&'a T)` should
- // probably always be WF, because it should be
- // shorthand for something like `where(T: 'a) {
- // fn(&'a T) }`, as discussed in #25860.
- //
- // Note that we are also skipping the generic
- // types. This is consistent with the `outlives`
- // code, but anyway doesn't matter: within the fn
- // body where they are created, the generics will
- // always be WF, and outside of that fn body we
- // are not directly inspecting closure types
- // anyway, except via auto trait matching (which
- // only inspects the upvar types).
- subtys.skip_current_subtree(); // subtree handled by compute_projection
- for upvar_ty in substs.as_closure().upvar_tys(def_id, self.infcx.tcx) {
- self.compute(upvar_ty);
- }
- }
-
- ty::FnPtr(_) => {
- // let the loop iterate into the argument/return
- // types appearing in the fn signature
- }
-
- ty::Opaque(did, substs) => {
- // all of the requirements on type parameters
- // should've been checked by the instantiation
- // of whatever returned this exact `impl Trait`.
-
- // for named opaque `impl Trait` types we still need to check them
- if super::is_impl_trait_defn(self.infcx.tcx, did).is_none() {
- let obligations = self.nominal_obligations(did, substs);
- self.out.extend(obligations);
- }
- }
-
- ty::Dynamic(data, r) => {
- // WfObject
- //
- // Here, we defer WF checking due to higher-ranked
- // regions. This is perhaps not ideal.
- self.from_object_ty(ty, data, r);
-
- // FIXME(#27579) RFC also considers adding trait
- // obligations that don't refer to Self and
- // checking those
-
- let defer_to_coercion = self.infcx.tcx.features().object_safe_for_dispatch;
-
- if !defer_to_coercion {
- let cause = self.cause(traits::MiscObligation);
- let component_traits = data.auto_traits().chain(data.principal_def_id());
- self.out.extend(component_traits.map(|did| {
- traits::Obligation::new(
- cause.clone(),
- param_env,
- ty::Predicate::ObjectSafe(did),
- )
- }));
- }
- }
-
- // Inference variables are the complicated case, since we don't
- // know what type they are. We do two things:
- //
- // 1. Check if they have been resolved, and if so proceed with
- // THAT type.
- // 2. If not, check whether this is the type that we
- // started with (ty0). In that case, we've made no
- // progress at all, so return false. Otherwise,
- // we've at least simplified things (i.e., we went
- // from `Vec<$0>: WF` to `$0: WF`, so we can
- // register a pending obligation and keep
- // moving. (Goal is that an "inductive hypothesis"
- // is satisfied to ensure termination.)
- ty::Infer(_) => {
- let ty = self.infcx.shallow_resolve(ty);
- if let ty::Infer(_) = ty.kind {
- // not yet resolved...
- if ty == ty0 {
- // ...this is the type we started from! no progress.
- return false;
- }
-
- let cause = self.cause(traits::MiscObligation);
- self.out.push(
- // ...not the type we started from, so we made progress.
- traits::Obligation::new(
- cause,
- self.param_env,
- ty::Predicate::WellFormed(ty),
- ),
- );
- } else {
- // Yes, resolved, proceed with the
- // result. Should never return false because
- // `ty` is not a Infer.
- assert!(self.compute(ty));
- }
- }
- }
- }
-
- // if we made it through that loop above, we made progress!
- return true;
- }
-
- fn nominal_obligations(
- &mut self,
- def_id: DefId,
- substs: SubstsRef<'tcx>,
- ) -> Vec<traits::PredicateObligation<'tcx>> {
- let predicates = self.infcx.tcx.predicates_of(def_id).instantiate(self.infcx.tcx, substs);
- let cause = self.cause(traits::ItemObligation(def_id));
- predicates
- .predicates
- .into_iter()
- .map(|pred| traits::Obligation::new(cause.clone(), self.param_env, pred))
- .filter(|pred| !pred.has_escaping_bound_vars())
- .collect()
- }
-
- fn from_object_ty(
- &mut self,
- ty: Ty<'tcx>,
- data: ty::Binder<&'tcx ty::List<ty::ExistentialPredicate<'tcx>>>,
- region: ty::Region<'tcx>,
- ) {
- // Imagine a type like this:
- //
- // trait Foo { }
- // trait Bar<'c> : 'c { }
- //
- // &'b (Foo+'c+Bar<'d>)
- // ^
- //
- // In this case, the following relationships must hold:
- //
- // 'b <= 'c
- // 'd <= 'c
- //
- // The first conditions is due to the normal region pointer
- // rules, which say that a reference cannot outlive its
- // referent.
- //
- // The final condition may be a bit surprising. In particular,
- // you may expect that it would have been `'c <= 'd`, since
- // usually lifetimes of outer things are conservative
- // approximations for inner things. However, it works somewhat
- // differently with trait objects: here the idea is that if the
- // user specifies a region bound (`'c`, in this case) it is the
- // "master bound" that *implies* that bounds from other traits are
- // all met. (Remember that *all bounds* in a type like
- // `Foo+Bar+Zed` must be met, not just one, hence if we write
- // `Foo<'x>+Bar<'y>`, we know that the type outlives *both* 'x and
- // 'y.)
- //
- // Note: in fact we only permit builtin traits, not `Bar<'d>`, I
- // am looking forward to the future here.
- if !data.has_escaping_bound_vars() && !region.has_escaping_bound_vars() {
- let implicit_bounds = object_region_bounds(self.infcx.tcx, data);
-
- let explicit_bound = region;
-
- self.out.reserve(implicit_bounds.len());
- for implicit_bound in implicit_bounds {
- let cause = self.cause(traits::ObjectTypeBound(ty, explicit_bound));
- let outlives =
- ty::Binder::dummy(ty::OutlivesPredicate(explicit_bound, implicit_bound));
- self.out.push(traits::Obligation::new(
- cause,
- self.param_env,
- outlives.to_predicate(),
- ));
- }
- }
- }
-}
-
-/// Given an object type like `SomeTrait + Send`, computes the lifetime
-/// bounds that must hold on the elided self type. These are derived
-/// from the declarations of `SomeTrait`, `Send`, and friends -- if
-/// they declare `trait SomeTrait : 'static`, for example, then
-/// `'static` would appear in the list. The hard work is done by
-/// `ty::required_region_bounds`, see that for more information.
-pub fn object_region_bounds<'tcx>(
- tcx: TyCtxt<'tcx>,
- existential_predicates: ty::Binder<&'tcx ty::List<ty::ExistentialPredicate<'tcx>>>,
-) -> Vec<ty::Region<'tcx>> {
- // Since we don't actually *know* the self type for an object,
- // this "open(err)" serves as a kind of dummy standin -- basically
- // a placeholder type.
- let open_ty = tcx.mk_ty_infer(ty::FreshTy(0));
-
- let predicates = existential_predicates
- .iter()
- .filter_map(|predicate| {
- if let ty::ExistentialPredicate::Projection(_) = *predicate.skip_binder() {
- None
- } else {
- Some(predicate.with_self_ty(tcx, open_ty))
- }
- })
- .collect();
-
- tcx.required_region_bounds(open_ty, predicates)
-}
-
-/// Find the span of a generic bound affecting an associated type.
-fn get_generic_bound_spans(
- generics: &hir::Generics<'_>,
- trait_name: Option<&Ident>,
- assoc_item_name: Ident,
-) -> Vec<Span> {
- let mut bounds = vec![];
- for clause in generics.where_clause.predicates.iter() {
- if let hir::WherePredicate::BoundPredicate(pred) = clause {
- match &pred.bounded_ty.kind {
- hir::TyKind::Path(hir::QPath::Resolved(Some(ty), path)) => {
- let mut s = path.segments.iter();
- if let (a, Some(b), None) = (s.next(), s.next(), s.next()) {
- if a.map(|s| &s.ident) == trait_name
- && b.ident == assoc_item_name
- && is_self_path(&ty.kind)
- {
- // `<Self as Foo>::Bar`
- bounds.push(pred.span);
- }
- }
- }
- hir::TyKind::Path(hir::QPath::TypeRelative(ty, segment)) => {
- if segment.ident == assoc_item_name {
- if is_self_path(&ty.kind) {
- // `Self::Bar`
- bounds.push(pred.span);
- }
- }
- }
- _ => {}
- }
- }
- }
- bounds
-}
-
-fn is_self_path(kind: &hir::TyKind<'_>) -> bool {
- match kind {
- hir::TyKind::Path(hir::QPath::Resolved(None, path)) => {
- let mut s = path.segments.iter();
- if let (Some(segment), None) = (s.next(), s.next()) {
- if segment.ident.name == kw::SelfUpper {
- // `type(Self)`
- return true;
- }
- }
- }
- _ => {}
- }
- false
-}
+++ /dev/null
-/// "Signaling" trait used in impl trait to tag lifetimes that you may
-/// need to capture but don't really need for other reasons.
-/// Basically a workaround; see [this comment] for details.
-///
-/// [this comment]: https://github.com/rust-lang/rust/issues/34511#issuecomment-373423999
-// FIXME(eddyb) false positive, the lifetime parameter is "phantom" but needed.
-#[allow(unused_lifetimes)]
-pub trait Captures<'a> {}
-
-impl<'a, T: ?Sized> Captures<'a> for T {}
use std::fmt::Debug;
use std::time::{Duration, Instant};
-use rustc_span::symbol::{sym, Symbol};
-
#[cfg(test)]
mod tests;
-// The name of the associated type for `Fn` return types.
-pub const FN_OUTPUT_NAME: Symbol = sym::Output;
-
-pub use errors::ErrorReported;
+pub use rustc_errors::ErrorReported;
pub fn to_readable_str(mut val: usize) -> String {
let mut groups = vec![];
+++ /dev/null
-[package]
-authors = ["The Rust Project Developers"]
-build = "build.rs"
-name = "rustc_asan"
-version = "0.0.0"
-edition = "2018"
-
-[lib]
-name = "rustc_asan"
-path = "lib.rs"
-test = false
-
-[build-dependencies]
-build_helper = { path = "../build_helper" }
-cmake = "0.1.38"
-
-[dependencies]
-alloc = { path = "../liballoc" }
-core = { path = "../libcore" }
-compiler_builtins = "0.1.0"
+++ /dev/null
-use build_helper::sanitizer_lib_boilerplate;
-use std::env;
-
-use cmake::Config;
-
-fn main() {
- println!("cargo:rerun-if-env-changed=RUSTC_BUILD_SANITIZERS");
- if env::var("RUSTC_BUILD_SANITIZERS") != Ok("1".to_string()) {
- return;
- }
- if let Some(llvm_config) = env::var_os("LLVM_CONFIG") {
- build_helper::restore_library_path();
-
- let (native, target) = match sanitizer_lib_boilerplate("asan") {
- Ok(native) => native,
- _ => return,
- };
-
- Config::new(&native.src_dir)
- .define("COMPILER_RT_BUILD_SANITIZERS", "ON")
- .define("COMPILER_RT_BUILD_BUILTINS", "OFF")
- .define("COMPILER_RT_BUILD_XRAY", "OFF")
- .define("LLVM_CONFIG_PATH", llvm_config)
- .out_dir(&native.out_dir)
- .build_target(&target)
- .build();
- native.fixup_sanitizer_lib_name("asan");
- }
- println!("cargo:rerun-if-env-changed=LLVM_CONFIG");
-}
+++ /dev/null
-#![sanitizer_runtime]
-#![feature(nll)]
-#![feature(sanitizer_runtime)]
-#![feature(staged_api)]
-#![no_std]
-#![unstable(
- feature = "sanitizer_runtime_lib",
- reason = "internal implementation detail of sanitizers",
- issue = "none"
-)]
use rustc::bug;
use rustc_data_structures::thin_vec::ThinVec;
use rustc_error_codes::*;
+use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def::Res;
use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned};
use syntax::ast::*;
use syntax::attr;
use syntax::ptr::P as AstP;
-use syntax::{span_err, struct_span_err};
impl<'hir> LoweringContext<'_, 'hir> {
fn lower_exprs(&mut self, exprs: &[AstP<Expr>]) -> &'hir [hir::Expr<'hir>] {
ExprKind::Mac(_) => panic!("Shouldn't exist here"),
};
- hir::Expr { hir_id: self.lower_node_id(e.id), kind, span: e.span, attrs: e.attrs.clone() }
+ hir::Expr {
+ hir_id: self.lower_node_id(e.id),
+ kind,
+ span: e.span,
+ attrs: e.attrs.iter().map(|a| self.lower_attr(a)).collect::<Vec<_>>().into(),
+ }
}
fn lower_unop(&mut self, u: UnOp) -> hir::UnOp {
match generator_kind {
Some(hir::GeneratorKind::Gen) => {
if !decl.inputs.is_empty() {
- span_err!(
+ struct_span_err!(
self.sess,
fn_decl_span,
E0628,
"generators cannot have explicit parameters"
- );
+ )
+ .emit();
}
Some(movability)
}
}
None => {
if movability == Movability::Static {
- span_err!(self.sess, fn_decl_span, E0697, "closures cannot be static");
+ struct_span_err!(self.sess, fn_decl_span, E0697, "closures cannot be static")
+ .emit();
}
None
}
match self.generator_kind {
Some(hir::GeneratorKind::Gen) => {}
Some(hir::GeneratorKind::Async(_)) => {
- span_err!(self.sess, span, E0727, "`async` generators are not yet supported",);
+ struct_span_err!(
+ self.sess,
+ span,
+ E0727,
+ "`async` generators are not yet supported"
+ )
+ .emit();
return hir::ExprKind::Err;
}
None => self.generator_kind = Some(hir::GeneratorKind::Gen),
use rustc::arena::Arena;
use rustc::bug;
use rustc_error_codes::*;
+use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_target::spec::abi;
use syntax::ast::*;
use syntax::attr;
-use syntax::struct_span_err;
use syntax::visit::{self, Visitor};
use log::debug;
pub(super) lctx: &'a mut LoweringContext<'lowering, 'hir>,
}
-impl<'a, 'lowering, 'hir> ItemLowerer<'a, 'lowering, 'hir> {
- fn with_trait_impl_ref<F>(&mut self, trait_impl_ref: &Option<TraitRef>, f: F)
- where
- F: FnOnce(&mut Self),
- {
+impl ItemLowerer<'_, '_, '_> {
+ fn with_trait_impl_ref(&mut self, impl_ref: &Option<TraitRef>, f: impl FnOnce(&mut Self)) {
let old = self.lctx.is_in_trait_impl;
- self.lctx.is_in_trait_impl = if let &None = trait_impl_ref { false } else { true };
+ self.lctx.is_in_trait_impl = if let &None = impl_ref { false } else { true };
f(self);
self.lctx.is_in_trait_impl = old;
}
}
-impl<'a, 'lowering, 'hir> Visitor<'a> for ItemLowerer<'a, 'lowering, 'hir> {
+impl<'a> Visitor<'a> for ItemLowerer<'a, '_, '_> {
fn visit_mod(&mut self, m: &'a Mod, _s: Span, _attrs: &[Attribute], n: NodeId) {
let hir_id = self.lctx.lower_node_id(n);
self.lctx.with_parent_item_lifetime_defs(hir_id, |this| {
let this = &mut ItemLowerer { lctx: this };
if let ItemKind::Impl(.., ref opt_trait_ref, _, _) = item.kind {
+ if opt_trait_ref.as_ref().map(|tr| tr.constness.is_some()).unwrap_or(false) {
+ this.lctx
+ .diagnostic()
+ .span_err(item.span, "const trait impls are not yet implemented");
+ }
+
this.with_trait_impl_ref(opt_trait_ref, |this| visit::walk_item(this, item));
} else {
visit::walk_item(this, item);
-// ignore-tidy-filelength
-
//! Lowers the AST to the HIR.
//!
//! Since the AST and HIR are fairly similar, this is mostly a simple procedure,
//! in the HIR, especially for multiple identifiers.
#![feature(array_value_iter)]
+#![feature(crate_visibility_modifier)]
use rustc::arena::Arena;
use rustc::dep_graph::DepGraph;
-use rustc::hir::intravisit;
-use rustc::hir::map::{DefKey, DefPathData, Definitions};
-use rustc::lint;
-use rustc::lint::builtin::{self, ELIDED_LIFETIMES_IN_PATHS};
-use rustc::middle::cstore::CrateStore;
-use rustc::util::captures::Captures;
-use rustc::util::common::FN_OUTPUT_NAME;
+use rustc::hir::map::definitions::{DefKey, DefPathData, Definitions};
+use rustc::hir::map::Map;
use rustc::{bug, span_bug};
+use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::sync::Lrc;
use rustc_error_codes::*;
-use rustc_errors::Applicability;
+use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Namespace, PartialRes, PerNS, Res};
use rustc_hir::def_id::{DefId, DefIdMap, DefIndex, CRATE_DEF_INDEX};
+use rustc_hir::intravisit;
use rustc_hir::{ConstArg, GenericArg, ParamName};
use rustc_index::vec::IndexVec;
use rustc_session::config::nightly_options;
+use rustc_session::lint::{builtin, BuiltinLintDiagnostics, LintBuffer};
use rustc_session::node_id::NodeMap;
use rustc_session::Session;
use rustc_span::hygiene::ExpnId;
-use rustc_span::source_map::{respan, DesugaringKind, ExpnData, ExpnKind, Spanned};
+use rustc_span::source_map::{respan, DesugaringKind, ExpnData, ExpnKind};
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_span::Span;
use syntax::ast;
use syntax::ast::*;
use syntax::attr;
use syntax::print::pprust;
-use syntax::ptr::P as AstP;
use syntax::sess::ParseSess;
use syntax::token::{self, Nonterminal, Token};
use syntax::tokenstream::{TokenStream, TokenTree};
use syntax::visit::{self, Visitor};
-use syntax::{help, struct_span_err, walk_list};
+use syntax::walk_list;
use log::{debug, trace};
use smallvec::{smallvec, SmallVec};
mod expr;
mod item;
+mod pat;
+mod path;
const HIR_ID_COUNTER_LOCKED: u32 = 0xFFFFFFFF;
}
pub trait Resolver {
- fn cstore(&self) -> &dyn CrateStore;
+ fn def_key(&mut self, id: DefId) -> DefKey;
+
+ fn item_generics_num_lifetimes(&self, def: DefId, sess: &Session) -> usize;
/// Obtains resolution for a `NodeId` with a single resolution.
fn get_partial_res(&mut self, id: NodeId) -> Option<PartialRes>;
ns: Namespace,
) -> (ast::Path, Res<NodeId>);
- fn lint_buffer(&mut self) -> &mut lint::LintBuffer;
+ fn lint_buffer(&mut self) -> &mut LintBuffer;
fn next_node_id(&mut self) -> NodeId;
}
// incr. comp. yet.
dep_graph.assert_ignored();
- let _prof_timer = sess.prof.generic_activity("hir_lowering");
+ let _prof_timer = sess.prof.verbose_generic_activity("hir_lowering");
LoweringContext {
crate_root: sess.parse_sess.injected_crate_name.try_get().copied(),
}
}
- fn with_hir_id_owner<F, T>(&mut self, owner: Option<NodeId>, f: F) -> T
- where
- F: FnOnce(&mut Self) -> T,
- {
+ fn with_hir_id_owner<T>(
+ &mut self,
+ owner: Option<NodeId>,
+ f: impl FnOnce(&mut Self) -> T,
+ ) -> T {
let old = mem::replace(&mut self.hir_id_owner, owner);
let r = f(self);
self.hir_id_owner = old;
}
}
- impl<'tcx, 'lowering, 'hir> Visitor<'tcx> for MiscCollector<'tcx, 'lowering, 'hir> {
+ impl<'tcx> Visitor<'tcx> for MiscCollector<'tcx, '_, '_> {
fn visit_pat(&mut self, p: &'tcx Pat) {
if let PatKind::Paren(..) | PatKind::Rest = p.kind {
// Doesn't generate a HIR node
lowered
}
- fn lower_node_id_generic<F>(&mut self, ast_node_id: NodeId, alloc_hir_id: F) -> hir::HirId
- where
- F: FnOnce(&mut Self) -> hir::HirId,
- {
+ fn lower_node_id_generic(
+ &mut self,
+ ast_node_id: NodeId,
+ alloc_hir_id: impl FnOnce(&mut Self) -> hir::HirId,
+ ) -> hir::HirId {
if ast_node_id == DUMMY_NODE_ID {
return hir::DUMMY_HIR_ID;
}
}
}
- fn with_hir_id_owner<F, T>(&mut self, owner: NodeId, f: F) -> T
- where
- F: FnOnce(&mut Self) -> T,
- {
+ fn with_hir_id_owner<T>(&mut self, owner: NodeId, f: impl FnOnce(&mut Self) -> T) -> T {
let counter = self
.item_local_id_counters
.insert(owner, HIR_ID_COUNTER_LOCKED)
/// Presuming that in-band lifetimes are enabled, then
/// `self.anonymous_lifetime_mode` will be updated to match the
/// parameter while `f` is running (and restored afterwards).
- fn collect_in_band_defs<T, F>(
+ fn collect_in_band_defs<T>(
&mut self,
parent_id: DefId,
anonymous_lifetime_mode: AnonymousLifetimeMode,
- f: F,
- ) -> (Vec<hir::GenericParam<'hir>>, T)
- where
- F: FnOnce(&mut Self) -> (Vec<hir::GenericParam<'hir>>, T),
- {
+ f: impl FnOnce(&mut Self) -> (Vec<hir::GenericParam<'hir>>, T),
+ ) -> (Vec<hir::GenericParam<'hir>>, T) {
assert!(!self.is_collecting_in_band_lifetimes);
assert!(self.lifetimes_to_define.is_empty());
let old_anonymous_lifetime_mode = self.anonymous_lifetime_mode;
// This is used to track which lifetimes have already been defined, and
// which are new in-band lifetimes that need to have a definition created
// for them.
- fn with_in_scope_lifetime_defs<T, F>(&mut self, params: &[GenericParam], f: F) -> T
- where
- F: FnOnce(&mut Self) -> T,
- {
+ fn with_in_scope_lifetime_defs<T>(
+ &mut self,
+ params: &[GenericParam],
+ f: impl FnOnce(&mut Self) -> T,
+ ) -> T {
let old_len = self.in_scope_lifetimes.len();
let lt_def_names = params.iter().filter_map(|param| match param.kind {
GenericParamKind::Lifetime { .. } => Some(ParamName::Plain(param.ident.modern())),
/// Presuming that in-band lifetimes are enabled, then
/// `self.anonymous_lifetime_mode` will be updated to match the
/// parameter while `f` is running (and restored afterwards).
- fn add_in_band_defs<F, T>(
+ fn add_in_band_defs<T>(
&mut self,
generics: &Generics,
parent_id: DefId,
anonymous_lifetime_mode: AnonymousLifetimeMode,
- f: F,
- ) -> (hir::Generics<'hir>, T)
- where
- F: FnOnce(&mut Self, &mut Vec<hir::GenericParam<'hir>>) -> T,
- {
+ f: impl FnOnce(&mut Self, &mut Vec<hir::GenericParam<'hir>>) -> T,
+ ) -> (hir::Generics<'hir>, T) {
let (in_band_defs, (mut lowered_generics, res)) =
self.with_in_scope_lifetime_defs(&generics.params, |this| {
this.collect_in_band_defs(parent_id, anonymous_lifetime_mode, |this| {
(lowered_generics, res)
}
- fn with_dyn_type_scope<T, F>(&mut self, in_scope: bool, f: F) -> T
- where
- F: FnOnce(&mut Self) -> T,
- {
+ fn with_dyn_type_scope<T>(&mut self, in_scope: bool, f: impl FnOnce(&mut Self) -> T) -> T {
let was_in_dyn_type = self.is_in_dyn_type;
self.is_in_dyn_type = in_scope;
result
}
- fn with_new_scopes<T, F>(&mut self, f: F) -> T
- where
- F: FnOnce(&mut Self) -> T,
- {
+ fn with_new_scopes<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> T {
let was_in_loop_condition = self.is_in_loop_condition;
self.is_in_loop_condition = false;
ret
}
- fn def_key(&mut self, id: DefId) -> DefKey {
- if id.is_local() {
- self.resolver.definitions().def_key(id.index)
- } else {
- self.resolver.cstore().def_key(id)
- }
- }
-
fn lower_attrs(&mut self, attrs: &[Attribute]) -> &'hir [Attribute] {
self.arena.alloc_from_iter(attrs.iter().map(|a| self.lower_attr(a)))
}
);
if pos == ImplTraitPosition::Binding && nightly_options::is_nightly_build()
{
- help!(
- err,
+ err.help(
"add `#![feature(impl_trait_in_bindings)]` to the crate \
- attributes to enable"
+ attributes to enable",
);
}
err.emit();
}
impl<'r, 'a, 'v, 'hir> intravisit::Visitor<'v> for ImplTraitLifetimeCollector<'r, 'a, 'hir> {
- fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'v> {
+ type Map = Map<'v>;
+
+ fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<'_, Self::Map> {
intravisit::NestedVisitorMap::None
}
)
}
- fn lower_qpath(
- &mut self,
- id: NodeId,
- qself: &Option<QSelf>,
- p: &Path,
- param_mode: ParamMode,
- mut itctx: ImplTraitContext<'_, 'hir>,
- ) -> hir::QPath<'hir> {
- let qself_position = qself.as_ref().map(|q| q.position);
- let qself = qself.as_ref().map(|q| self.lower_ty(&q.ty, itctx.reborrow()));
-
- let partial_res =
- self.resolver.get_partial_res(id).unwrap_or_else(|| PartialRes::new(Res::Err));
-
- let proj_start = p.segments.len() - partial_res.unresolved_segments();
- let path = self.arena.alloc(hir::Path {
- res: self.lower_res(partial_res.base_res()),
- segments: self.arena.alloc_from_iter(p.segments[..proj_start].iter().enumerate().map(
- |(i, segment)| {
- let param_mode = match (qself_position, param_mode) {
- (Some(j), ParamMode::Optional) if i < j => {
- // This segment is part of the trait path in a
- // qualified path - one of `a`, `b` or `Trait`
- // in `<X as a::b::Trait>::T::U::method`.
- ParamMode::Explicit
- }
- _ => param_mode,
- };
-
- // Figure out if this is a type/trait segment,
- // which may need lifetime elision performed.
- let parent_def_id = |this: &mut Self, def_id: DefId| DefId {
- krate: def_id.krate,
- index: this.def_key(def_id).parent.expect("missing parent"),
- };
- let type_def_id = match partial_res.base_res() {
- Res::Def(DefKind::AssocTy, def_id) if i + 2 == proj_start => {
- Some(parent_def_id(self, def_id))
- }
- Res::Def(DefKind::Variant, def_id) if i + 1 == proj_start => {
- Some(parent_def_id(self, def_id))
- }
- Res::Def(DefKind::Struct, def_id)
- | Res::Def(DefKind::Union, def_id)
- | Res::Def(DefKind::Enum, def_id)
- | Res::Def(DefKind::TyAlias, def_id)
- | Res::Def(DefKind::Trait, def_id)
- if i + 1 == proj_start =>
- {
- Some(def_id)
- }
- _ => None,
- };
- let parenthesized_generic_args = match partial_res.base_res() {
- // `a::b::Trait(Args)`
- Res::Def(DefKind::Trait, _) if i + 1 == proj_start => {
- ParenthesizedGenericArgs::Ok
- }
- // `a::b::Trait(Args)::TraitItem`
- Res::Def(DefKind::Method, _)
- | Res::Def(DefKind::AssocConst, _)
- | Res::Def(DefKind::AssocTy, _)
- if i + 2 == proj_start =>
- {
- ParenthesizedGenericArgs::Ok
- }
- // Avoid duplicated errors.
- Res::Err => ParenthesizedGenericArgs::Ok,
- // An error
- _ => ParenthesizedGenericArgs::Err,
- };
-
- let num_lifetimes = type_def_id.map_or(0, |def_id| {
- if let Some(&n) = self.type_def_lifetime_params.get(&def_id) {
- return n;
- }
- assert!(!def_id.is_local());
- let item_generics = self
- .resolver
- .cstore()
- .item_generics_cloned_untracked(def_id, self.sess);
- let n = item_generics.own_counts().lifetimes;
- self.type_def_lifetime_params.insert(def_id, n);
- n
- });
- self.lower_path_segment(
- p.span,
- segment,
- param_mode,
- num_lifetimes,
- parenthesized_generic_args,
- itctx.reborrow(),
- None,
- )
- },
- )),
- span: p.span,
- });
-
- // Simple case, either no projections, or only fully-qualified.
- // E.g., `std::mem::size_of` or `<I as Iterator>::Item`.
- if partial_res.unresolved_segments() == 0 {
- return hir::QPath::Resolved(qself, path);
- }
-
- // Create the innermost type that we're projecting from.
- let mut ty = if path.segments.is_empty() {
- // If the base path is empty that means there exists a
- // syntactical `Self`, e.g., `&i32` in `<&i32>::clone`.
- qself.expect("missing QSelf for <T>::...")
- } else {
- // Otherwise, the base path is an implicit `Self` type path,
- // e.g., `Vec` in `Vec::new` or `<I as Iterator>::Item` in
- // `<I as Iterator>::Item::default`.
- let new_id = self.next_id();
- self.arena.alloc(self.ty_path(new_id, p.span, hir::QPath::Resolved(qself, path)))
- };
-
- // Anything after the base path are associated "extensions",
- // out of which all but the last one are associated types,
- // e.g., for `std::vec::Vec::<T>::IntoIter::Item::clone`:
- // * base path is `std::vec::Vec<T>`
- // * "extensions" are `IntoIter`, `Item` and `clone`
- // * type nodes are:
- // 1. `std::vec::Vec<T>` (created above)
- // 2. `<std::vec::Vec<T>>::IntoIter`
- // 3. `<<std::vec::Vec<T>>::IntoIter>::Item`
- // * final path is `<<<std::vec::Vec<T>>::IntoIter>::Item>::clone`
- for (i, segment) in p.segments.iter().enumerate().skip(proj_start) {
- let segment = self.arena.alloc(self.lower_path_segment(
- p.span,
- segment,
- param_mode,
- 0,
- ParenthesizedGenericArgs::Err,
- itctx.reborrow(),
- None,
- ));
- let qpath = hir::QPath::TypeRelative(ty, segment);
-
- // It's finished, return the extension of the right node type.
- if i == p.segments.len() - 1 {
- return qpath;
- }
-
- // Wrap the associated extension in another type node.
- let new_id = self.next_id();
- ty = self.arena.alloc(self.ty_path(new_id, p.span, qpath));
- }
-
- // We should've returned in the for loop above.
- span_bug!(
- p.span,
- "lower_qpath: no final extension segment in {}..{}",
- proj_start,
- p.segments.len()
- )
- }
-
- fn lower_path_extra(
- &mut self,
- res: Res,
- p: &Path,
- param_mode: ParamMode,
- explicit_owner: Option<NodeId>,
- ) -> &'hir hir::Path<'hir> {
- self.arena.alloc(hir::Path {
- res,
- segments: self.arena.alloc_from_iter(p.segments.iter().map(|segment| {
- self.lower_path_segment(
- p.span,
- segment,
- param_mode,
- 0,
- ParenthesizedGenericArgs::Err,
- ImplTraitContext::disallowed(),
- explicit_owner,
- )
- })),
- span: p.span,
- })
- }
-
- 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, None)
- }
-
- fn lower_path_segment(
- &mut self,
- path_span: Span,
- segment: &PathSegment,
- param_mode: ParamMode,
- expected_lifetimes: usize,
- parenthesized_generic_args: ParenthesizedGenericArgs,
- itctx: ImplTraitContext<'_, 'hir>,
- explicit_owner: Option<NodeId>,
- ) -> hir::PathSegment<'hir> {
- let (mut generic_args, infer_args) = if let Some(ref generic_args) = segment.args {
- let msg = "parenthesized type parameters may only be used with a `Fn` trait";
- match **generic_args {
- GenericArgs::AngleBracketed(ref data) => {
- self.lower_angle_bracketed_parameter_data(data, param_mode, itctx)
- }
- GenericArgs::Parenthesized(ref data) => match parenthesized_generic_args {
- ParenthesizedGenericArgs::Ok => self.lower_parenthesized_parameter_data(data),
- ParenthesizedGenericArgs::Err => {
- let mut err = struct_span_err!(self.sess, data.span, E0214, "{}", msg);
- err.span_label(data.span, "only `Fn` traits may use parentheses");
- if let Ok(snippet) = self.sess.source_map().span_to_snippet(data.span) {
- // Do not suggest going from `Trait()` to `Trait<>`
- if data.inputs.len() > 0 {
- if let Some(split) = snippet.find('(') {
- let trait_name = &snippet[0..split];
- let args = &snippet[split + 1..snippet.len() - 1];
- err.span_suggestion(
- data.span,
- "use angle brackets instead",
- format!("{}<{}>", trait_name, args),
- Applicability::MaybeIncorrect,
- );
- }
- }
- };
- err.emit();
- (
- self.lower_angle_bracketed_parameter_data(
- &data.as_angle_bracketed_args(),
- param_mode,
- itctx,
- )
- .0,
- false,
- )
- }
- },
- }
- } else {
- self.lower_angle_bracketed_parameter_data(&Default::default(), param_mode, itctx)
- };
-
- let has_lifetimes = generic_args.args.iter().any(|arg| match arg {
- GenericArg::Lifetime(_) => true,
- _ => false,
- });
- let first_generic_span = generic_args
- .args
- .iter()
- .map(|a| a.span())
- .chain(generic_args.bindings.iter().map(|b| b.span))
- .next();
- if !generic_args.parenthesized && !has_lifetimes {
- generic_args.args = self
- .elided_path_lifetimes(path_span, expected_lifetimes)
- .map(|lt| GenericArg::Lifetime(lt))
- .chain(generic_args.args.into_iter())
- .collect();
- if expected_lifetimes > 0 && param_mode == ParamMode::Explicit {
- let anon_lt_suggestion = vec!["'_"; expected_lifetimes].join(", ");
- let no_non_lt_args = generic_args.args.len() == expected_lifetimes;
- let no_bindings = generic_args.bindings.is_empty();
- let (incl_angl_brckt, insertion_sp, suggestion) = if no_non_lt_args && no_bindings {
- // If there are no (non-implicit) generic args or associated type
- // bindings, our suggestion includes the angle brackets.
- (true, path_span.shrink_to_hi(), format!("<{}>", anon_lt_suggestion))
- } else {
- // Otherwise (sorry, this is kind of gross) we need to infer the
- // place to splice in the `'_, ` from the generics that do exist.
- let first_generic_span = first_generic_span
- .expect("already checked that non-lifetime args or bindings exist");
- (false, first_generic_span.shrink_to_lo(), format!("{}, ", anon_lt_suggestion))
- };
- match self.anonymous_lifetime_mode {
- // In create-parameter mode we error here because we don't want to support
- // deprecated impl elision in new features like impl elision and `async fn`,
- // both of which work using the `CreateParameter` mode:
- //
- // impl Foo for std::cell::Ref<u32> // note lack of '_
- // async fn foo(_: std::cell::Ref<u32>) { ... }
- AnonymousLifetimeMode::CreateParameter => {
- let mut err = struct_span_err!(
- self.sess,
- path_span,
- E0726,
- "implicit elided lifetime not allowed here"
- );
- crate::lint::builtin::add_elided_lifetime_in_path_suggestion(
- &self.sess,
- &mut err,
- expected_lifetimes,
- path_span,
- incl_angl_brckt,
- insertion_sp,
- suggestion,
- );
- err.emit();
- }
- AnonymousLifetimeMode::PassThrough | AnonymousLifetimeMode::ReportError => {
- self.resolver.lint_buffer().buffer_lint_with_diagnostic(
- ELIDED_LIFETIMES_IN_PATHS,
- CRATE_NODE_ID,
- path_span,
- "hidden lifetime parameters in types are deprecated",
- builtin::BuiltinLintDiagnostics::ElidedLifetimesInPaths(
- expected_lifetimes,
- path_span,
- incl_angl_brckt,
- insertion_sp,
- suggestion,
- ),
- );
- }
- }
- }
- }
-
- let res = self.expect_full_res(segment.id);
- let id = if let Some(owner) = explicit_owner {
- self.lower_node_id_with_owner(segment.id, owner)
- } else {
- self.lower_node_id(segment.id)
- };
- debug!(
- "lower_path_segment: ident={:?} original-id={:?} new-id={:?}",
- segment.ident, segment.id, id,
- );
-
- hir::PathSegment {
- ident: segment.ident,
- hir_id: Some(id),
- res: Some(self.lower_res(res)),
- infer_args,
- args: if generic_args.is_empty() {
- None
- } else {
- Some(self.arena.alloc(generic_args.into_generic_args(self.arena)))
- },
- }
- }
-
- fn lower_angle_bracketed_parameter_data(
- &mut self,
- data: &AngleBracketedArgs,
- param_mode: ParamMode,
- mut itctx: ImplTraitContext<'_, 'hir>,
- ) -> (GenericArgsCtor<'hir>, bool) {
- let &AngleBracketedArgs { ref args, ref constraints, .. } = data;
- let has_non_lt_args = args.iter().any(|arg| match arg {
- ast::GenericArg::Lifetime(_) => false,
- ast::GenericArg::Type(_) => true,
- ast::GenericArg::Const(_) => true,
- });
- (
- GenericArgsCtor {
- args: args.iter().map(|a| self.lower_generic_arg(a, itctx.reborrow())).collect(),
- bindings: self.arena.alloc_from_iter(
- constraints.iter().map(|b| self.lower_assoc_ty_constraint(b, itctx.reborrow())),
- ),
- parenthesized: false,
- },
- !has_non_lt_args && param_mode == ParamMode::Optional,
- )
- }
-
- fn lower_parenthesized_parameter_data(
- &mut self,
- data: &ParenthesizedArgs,
- ) -> (GenericArgsCtor<'hir>, bool) {
- // Switch to `PassThrough` mode for anonymous lifetimes; this
- // means that we permit things like `&Ref<T>`, where `Ref` has
- // a hidden lifetime parameter. This is needed for backwards
- // compatibility, even in contexts like an impl header where
- // we generally don't permit such things (see #51008).
- self.with_anonymous_lifetime_mode(AnonymousLifetimeMode::PassThrough, |this| {
- let &ParenthesizedArgs { ref inputs, ref output, span } = data;
- let inputs = this.arena.alloc_from_iter(
- inputs.iter().map(|ty| this.lower_ty_direct(ty, ImplTraitContext::disallowed())),
- );
- let output_ty = match output {
- FunctionRetTy::Ty(ty) => this.lower_ty(&ty, ImplTraitContext::disallowed()),
- FunctionRetTy::Default(_) => this.arena.alloc(this.ty_tup(span, &[])),
- };
- let args = smallvec![GenericArg::Type(this.ty_tup(span, inputs))];
- let binding = hir::TypeBinding {
- hir_id: this.next_id(),
- ident: Ident::with_dummy_span(FN_OUTPUT_NAME),
- span: output_ty.span,
- kind: hir::TypeBindingKind::Equality { ty: output_ty },
- };
- (
- GenericArgsCtor { args, bindings: arena_vec![this; binding], parenthesized: true },
- false,
- )
- })
- }
-
fn lower_local(&mut self, l: &Local) -> (hir::Local<'hir>, SmallVec<[NodeId; 1]>) {
let mut ids = SmallVec::<[NodeId; 1]>::new();
if self.sess.features_untracked().impl_trait_in_bindings {
// "<Output = T>"
let future_params = self.arena.alloc(hir::GenericArgs {
args: &[],
- bindings: arena_vec![self; hir::TypeBinding {
- ident: Ident::with_dummy_span(FN_OUTPUT_NAME),
- kind: hir::TypeBindingKind::Equality { ty: output_ty },
- hir_id: self.next_id(),
- span,
- }],
+ bindings: arena_vec![self; self.output_ty_binding(span, output_ty)],
parenthesized: false,
});
p: &PolyTraitRef,
mut itctx: ImplTraitContext<'_, 'hir>,
) -> hir::PolyTraitRef<'hir> {
+ if p.trait_ref.constness.is_some() {
+ self.diagnostic().span_err(p.span, "`?const` on trait bounds is not yet implemented");
+ }
+
let bound_generic_params = self.lower_generic_params(
&p.bound_generic_params,
&NodeMap::default(),
self.expr_block(block, AttrVec::new())
}
- fn lower_pat(&mut self, p: &Pat) -> &'hir hir::Pat<'hir> {
- let node = match p.kind {
- PatKind::Wild => hir::PatKind::Wild,
- PatKind::Ident(ref binding_mode, ident, ref sub) => {
- let lower_sub = |this: &mut Self| sub.as_ref().map(|s| this.lower_pat(&*s));
- let node = self.lower_pat_ident(p, binding_mode, ident, lower_sub);
- node
- }
- PatKind::Lit(ref e) => hir::PatKind::Lit(self.lower_expr(e)),
- PatKind::TupleStruct(ref path, ref pats) => {
- let qpath = self.lower_qpath(
- p.id,
- &None,
- path,
- ParamMode::Optional,
- ImplTraitContext::disallowed(),
- );
- let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple struct");
- hir::PatKind::TupleStruct(qpath, pats, ddpos)
- }
- PatKind::Or(ref pats) => {
- hir::PatKind::Or(self.arena.alloc_from_iter(pats.iter().map(|x| self.lower_pat(x))))
- }
- PatKind::Path(ref qself, ref path) => {
- let qpath = self.lower_qpath(
- p.id,
- qself,
- path,
- ParamMode::Optional,
- ImplTraitContext::disallowed(),
- );
- hir::PatKind::Path(qpath)
- }
- PatKind::Struct(ref path, ref fields, etc) => {
- let qpath = self.lower_qpath(
- p.id,
- &None,
- path,
- ParamMode::Optional,
- ImplTraitContext::disallowed(),
- );
-
- let fs = self.arena.alloc_from_iter(fields.iter().map(|f| hir::FieldPat {
- hir_id: self.next_id(),
- ident: f.ident,
- pat: self.lower_pat(&f.pat),
- is_shorthand: f.is_shorthand,
- span: f.span,
- }));
- hir::PatKind::Struct(qpath, fs, etc)
- }
- PatKind::Tuple(ref pats) => {
- let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple");
- hir::PatKind::Tuple(pats, ddpos)
- }
- PatKind::Box(ref inner) => hir::PatKind::Box(self.lower_pat(inner)),
- PatKind::Ref(ref inner, mutbl) => hir::PatKind::Ref(self.lower_pat(inner), mutbl),
- PatKind::Range(ref e1, ref e2, Spanned { node: ref end, .. }) => hir::PatKind::Range(
- self.lower_expr(e1),
- self.lower_expr(e2),
- self.lower_range_end(end),
- ),
- PatKind::Slice(ref pats) => self.lower_pat_slice(pats),
- PatKind::Rest => {
- // If we reach here the `..` pattern is not semantically allowed.
- self.ban_illegal_rest_pat(p.span)
- }
- PatKind::Paren(ref inner) => return self.lower_pat(inner),
- PatKind::Mac(_) => panic!("Shouldn't exist here"),
- };
-
- self.pat_with_node_id_of(p, node)
- }
-
- fn lower_pat_tuple(
- &mut self,
- pats: &[AstP<Pat>],
- ctx: &str,
- ) -> (&'hir [&'hir hir::Pat<'hir>], Option<usize>) {
- let mut elems = Vec::with_capacity(pats.len());
- let mut rest = None;
-
- let mut iter = pats.iter().enumerate();
- for (idx, pat) in iter.by_ref() {
- // Interpret the first `..` pattern as a sub-tuple pattern.
- // Note that unlike for slice patterns,
- // where `xs @ ..` is a legal sub-slice pattern,
- // it is not a legal sub-tuple pattern.
- if pat.is_rest() {
- rest = Some((idx, pat.span));
- break;
- }
- // It was not a sub-tuple pattern so lower it normally.
- elems.push(self.lower_pat(pat));
- }
-
- for (_, pat) in iter {
- // There was a previous sub-tuple pattern; make sure we don't allow more...
- if pat.is_rest() {
- // ...but there was one again, so error.
- self.ban_extra_rest_pat(pat.span, rest.unwrap().1, ctx);
- } else {
- elems.push(self.lower_pat(pat));
- }
- }
-
- (self.arena.alloc_from_iter(elems), rest.map(|(ddpos, _)| ddpos))
- }
-
- /// Lower a slice pattern of form `[pat_0, ..., pat_n]` into
- /// `hir::PatKind::Slice(before, slice, after)`.
- ///
- /// When encountering `($binding_mode $ident @)? ..` (`slice`),
- /// this is interpreted as a sub-slice pattern semantically.
- /// Patterns that follow, which are not like `slice` -- or an error occurs, are in `after`.
- fn lower_pat_slice(&mut self, pats: &[AstP<Pat>]) -> hir::PatKind<'hir> {
- let mut before = Vec::new();
- let mut after = Vec::new();
- let mut slice = None;
- let mut prev_rest_span = None;
-
- let mut iter = pats.iter();
- // Lower all the patterns until the first occurence of a sub-slice pattern.
- for pat in iter.by_ref() {
- match pat.kind {
- // Found a sub-slice pattern `..`. Record, lower it to `_`, and stop here.
- PatKind::Rest => {
- prev_rest_span = Some(pat.span);
- slice = Some(self.pat_wild_with_node_id_of(pat));
- break;
- }
- // Found a sub-slice pattern `$binding_mode $ident @ ..`.
- // Record, lower it to `$binding_mode $ident @ _`, and stop here.
- PatKind::Ident(ref bm, ident, Some(ref sub)) if sub.is_rest() => {
- prev_rest_span = Some(sub.span);
- let lower_sub = |this: &mut Self| Some(this.pat_wild_with_node_id_of(sub));
- let node = self.lower_pat_ident(pat, bm, ident, lower_sub);
- slice = Some(self.pat_with_node_id_of(pat, node));
- break;
- }
- // It was not a subslice pattern so lower it normally.
- _ => before.push(self.lower_pat(pat)),
- }
- }
-
- // Lower all the patterns after the first sub-slice pattern.
- for pat in iter {
- // There was a previous subslice pattern; make sure we don't allow more.
- let rest_span = match pat.kind {
- PatKind::Rest => Some(pat.span),
- PatKind::Ident(.., Some(ref sub)) if sub.is_rest() => {
- // The `HirValidator` is merciless; add a `_` pattern to avoid ICEs.
- after.push(self.pat_wild_with_node_id_of(pat));
- Some(sub.span)
- }
- _ => None,
- };
- if let Some(rest_span) = rest_span {
- // We have e.g., `[a, .., b, ..]`. That's no good, error!
- self.ban_extra_rest_pat(rest_span, prev_rest_span.unwrap(), "slice");
- } else {
- // Lower the pattern normally.
- after.push(self.lower_pat(pat));
- }
- }
-
- hir::PatKind::Slice(
- self.arena.alloc_from_iter(before),
- slice,
- self.arena.alloc_from_iter(after),
- )
- }
-
- fn lower_pat_ident(
- &mut self,
- p: &Pat,
- binding_mode: &BindingMode,
- ident: Ident,
- lower_sub: impl FnOnce(&mut Self) -> Option<&'hir hir::Pat<'hir>>,
- ) -> hir::PatKind<'hir> {
- match self.resolver.get_partial_res(p.id).map(|d| d.base_res()) {
- // `None` can occur in body-less function signatures
- res @ None | res @ Some(Res::Local(_)) => {
- let canonical_id = match res {
- Some(Res::Local(id)) => id,
- _ => p.id,
- };
-
- hir::PatKind::Binding(
- self.lower_binding_mode(binding_mode),
- self.lower_node_id(canonical_id),
- ident,
- lower_sub(self),
- )
- }
- Some(res) => hir::PatKind::Path(hir::QPath::Resolved(
- None,
- self.arena.alloc(hir::Path {
- span: ident.span,
- res: self.lower_res(res),
- segments: arena_vec![self; hir::PathSegment::from_ident(ident)],
- }),
- )),
- }
- }
-
- fn pat_wild_with_node_id_of(&mut self, p: &Pat) -> &'hir hir::Pat<'hir> {
- self.pat_with_node_id_of(p, hir::PatKind::Wild)
- }
-
- /// Construct a `Pat` with the `HirId` of `p.id` lowered.
- fn pat_with_node_id_of(&mut self, p: &Pat, kind: hir::PatKind<'hir>) -> &'hir hir::Pat<'hir> {
- self.arena.alloc(hir::Pat { hir_id: self.lower_node_id(p.id), kind, span: p.span })
- }
-
- /// Emit a friendly error for extra `..` patterns in a tuple/tuple struct/slice pattern.
- fn ban_extra_rest_pat(&self, sp: Span, prev_sp: Span, ctx: &str) {
- self.diagnostic()
- .struct_span_err(sp, &format!("`..` can only be used once per {} pattern", ctx))
- .span_label(sp, &format!("can only be used once per {} pattern", ctx))
- .span_label(prev_sp, "previously used here")
- .emit();
- }
-
- /// Used to ban the `..` pattern in places it shouldn't be semantically.
- fn ban_illegal_rest_pat(&self, sp: Span) -> hir::PatKind<'hir> {
- self.diagnostic()
- .struct_span_err(sp, "`..` patterns are not allowed here")
- .note("only allowed in tuple, tuple struct, and slice patterns")
- .emit();
-
- // We're not in a list context so `..` can be reasonably treated
- // as `_` because it should always be valid and roughly matches the
- // intent of `..` (notice that the rest of a single slot is that slot).
- hir::PatKind::Wild
- }
-
- fn lower_range_end(&mut self, e: &RangeEnd) -> hir::RangeEnd {
- match *e {
- RangeEnd::Included(_) => hir::RangeEnd::Included,
- RangeEnd::Excluded => hir::RangeEnd::Excluded,
- }
- }
-
fn lower_anon_const(&mut self, c: &AnonConst) -> hir::AnonConst {
self.with_new_scopes(|this| hir::AnonConst {
hir_id: this.lower_node_id(c.id),
}
}
- fn lower_binding_mode(&mut self, b: &BindingMode) -> hir::BindingAnnotation {
- match *b {
- BindingMode::ByValue(Mutability::Not) => hir::BindingAnnotation::Unannotated,
- BindingMode::ByRef(Mutability::Not) => hir::BindingAnnotation::Ref,
- BindingMode::ByValue(Mutability::Mut) => hir::BindingAnnotation::Mutable,
- BindingMode::ByRef(Mutability::Mut) => hir::BindingAnnotation::RefMut,
- }
- }
-
fn lower_unsafe_source(&mut self, u: UnsafeSource) -> hir::UnsafeSource {
match u {
CompilerGenerated => hir::UnsafeSource::CompilerGenerated,
id,
span,
"trait objects without an explicit `dyn` are deprecated",
- builtin::BuiltinLintDiagnostics::BareTraitObject(span, is_global),
+ BuiltinLintDiagnostics::BareTraitObject(span, is_global),
)
}
}
--- /dev/null
+use super::{ImplTraitContext, LoweringContext, ParamMode};
+
+use rustc_hir as hir;
+use rustc_hir::def::Res;
+use rustc_span::{source_map::Spanned, Span};
+use syntax::ast::*;
+use syntax::ptr::P;
+
+impl<'a, 'hir> LoweringContext<'a, 'hir> {
+ crate fn lower_pat(&mut self, p: &Pat) -> &'hir hir::Pat<'hir> {
+ let node = match p.kind {
+ PatKind::Wild => hir::PatKind::Wild,
+ PatKind::Ident(ref binding_mode, ident, ref sub) => {
+ let lower_sub = |this: &mut Self| sub.as_ref().map(|s| this.lower_pat(&*s));
+ let node = self.lower_pat_ident(p, binding_mode, ident, lower_sub);
+ node
+ }
+ PatKind::Lit(ref e) => hir::PatKind::Lit(self.lower_expr(e)),
+ PatKind::TupleStruct(ref path, ref pats) => {
+ let qpath = self.lower_qpath(
+ p.id,
+ &None,
+ path,
+ ParamMode::Optional,
+ ImplTraitContext::disallowed(),
+ );
+ let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple struct");
+ hir::PatKind::TupleStruct(qpath, pats, ddpos)
+ }
+ PatKind::Or(ref pats) => {
+ hir::PatKind::Or(self.arena.alloc_from_iter(pats.iter().map(|x| self.lower_pat(x))))
+ }
+ PatKind::Path(ref qself, ref path) => {
+ let qpath = self.lower_qpath(
+ p.id,
+ qself,
+ path,
+ ParamMode::Optional,
+ ImplTraitContext::disallowed(),
+ );
+ hir::PatKind::Path(qpath)
+ }
+ PatKind::Struct(ref path, ref fields, etc) => {
+ let qpath = self.lower_qpath(
+ p.id,
+ &None,
+ path,
+ ParamMode::Optional,
+ ImplTraitContext::disallowed(),
+ );
+
+ let fs = self.arena.alloc_from_iter(fields.iter().map(|f| hir::FieldPat {
+ hir_id: self.next_id(),
+ ident: f.ident,
+ pat: self.lower_pat(&f.pat),
+ is_shorthand: f.is_shorthand,
+ span: f.span,
+ }));
+ hir::PatKind::Struct(qpath, fs, etc)
+ }
+ PatKind::Tuple(ref pats) => {
+ let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple");
+ hir::PatKind::Tuple(pats, ddpos)
+ }
+ PatKind::Box(ref inner) => hir::PatKind::Box(self.lower_pat(inner)),
+ PatKind::Ref(ref inner, mutbl) => hir::PatKind::Ref(self.lower_pat(inner), mutbl),
+ PatKind::Range(ref e1, ref e2, Spanned { node: ref end, .. }) => hir::PatKind::Range(
+ e1.as_deref().map(|e| self.lower_expr(e)),
+ e2.as_deref().map(|e| self.lower_expr(e)),
+ self.lower_range_end(end, e2.is_some()),
+ ),
+ PatKind::Slice(ref pats) => self.lower_pat_slice(pats),
+ PatKind::Rest => {
+ // If we reach here the `..` pattern is not semantically allowed.
+ self.ban_illegal_rest_pat(p.span)
+ }
+ PatKind::Paren(ref inner) => return self.lower_pat(inner),
+ PatKind::Mac(_) => panic!("Shouldn't exist here"),
+ };
+
+ self.pat_with_node_id_of(p, node)
+ }
+
+ fn lower_pat_tuple(
+ &mut self,
+ pats: &[P<Pat>],
+ ctx: &str,
+ ) -> (&'hir [&'hir hir::Pat<'hir>], Option<usize>) {
+ let mut elems = Vec::with_capacity(pats.len());
+ let mut rest = None;
+
+ let mut iter = pats.iter().enumerate();
+ for (idx, pat) in iter.by_ref() {
+ // Interpret the first `..` pattern as a sub-tuple pattern.
+ // Note that unlike for slice patterns,
+ // where `xs @ ..` is a legal sub-slice pattern,
+ // it is not a legal sub-tuple pattern.
+ if pat.is_rest() {
+ rest = Some((idx, pat.span));
+ break;
+ }
+ // It was not a sub-tuple pattern so lower it normally.
+ elems.push(self.lower_pat(pat));
+ }
+
+ for (_, pat) in iter {
+ // There was a previous sub-tuple pattern; make sure we don't allow more...
+ if pat.is_rest() {
+ // ...but there was one again, so error.
+ self.ban_extra_rest_pat(pat.span, rest.unwrap().1, ctx);
+ } else {
+ elems.push(self.lower_pat(pat));
+ }
+ }
+
+ (self.arena.alloc_from_iter(elems), rest.map(|(ddpos, _)| ddpos))
+ }
+
+ /// Lower a slice pattern of form `[pat_0, ..., pat_n]` into
+ /// `hir::PatKind::Slice(before, slice, after)`.
+ ///
+ /// When encountering `($binding_mode $ident @)? ..` (`slice`),
+ /// this is interpreted as a sub-slice pattern semantically.
+ /// Patterns that follow, which are not like `slice` -- or an error occurs, are in `after`.
+ fn lower_pat_slice(&mut self, pats: &[P<Pat>]) -> hir::PatKind<'hir> {
+ let mut before = Vec::new();
+ let mut after = Vec::new();
+ let mut slice = None;
+ let mut prev_rest_span = None;
+
+ let mut iter = pats.iter();
+ // Lower all the patterns until the first occurence of a sub-slice pattern.
+ for pat in iter.by_ref() {
+ match pat.kind {
+ // Found a sub-slice pattern `..`. Record, lower it to `_`, and stop here.
+ PatKind::Rest => {
+ prev_rest_span = Some(pat.span);
+ slice = Some(self.pat_wild_with_node_id_of(pat));
+ break;
+ }
+ // Found a sub-slice pattern `$binding_mode $ident @ ..`.
+ // Record, lower it to `$binding_mode $ident @ _`, and stop here.
+ PatKind::Ident(ref bm, ident, Some(ref sub)) if sub.is_rest() => {
+ prev_rest_span = Some(sub.span);
+ let lower_sub = |this: &mut Self| Some(this.pat_wild_with_node_id_of(sub));
+ let node = self.lower_pat_ident(pat, bm, ident, lower_sub);
+ slice = Some(self.pat_with_node_id_of(pat, node));
+ break;
+ }
+ // It was not a subslice pattern so lower it normally.
+ _ => before.push(self.lower_pat(pat)),
+ }
+ }
+
+ // Lower all the patterns after the first sub-slice pattern.
+ for pat in iter {
+ // There was a previous subslice pattern; make sure we don't allow more.
+ let rest_span = match pat.kind {
+ PatKind::Rest => Some(pat.span),
+ PatKind::Ident(.., Some(ref sub)) if sub.is_rest() => {
+ // The `HirValidator` is merciless; add a `_` pattern to avoid ICEs.
+ after.push(self.pat_wild_with_node_id_of(pat));
+ Some(sub.span)
+ }
+ _ => None,
+ };
+ if let Some(rest_span) = rest_span {
+ // We have e.g., `[a, .., b, ..]`. That's no good, error!
+ self.ban_extra_rest_pat(rest_span, prev_rest_span.unwrap(), "slice");
+ } else {
+ // Lower the pattern normally.
+ after.push(self.lower_pat(pat));
+ }
+ }
+
+ hir::PatKind::Slice(
+ self.arena.alloc_from_iter(before),
+ slice,
+ self.arena.alloc_from_iter(after),
+ )
+ }
+
+ fn lower_pat_ident(
+ &mut self,
+ p: &Pat,
+ binding_mode: &BindingMode,
+ ident: Ident,
+ lower_sub: impl FnOnce(&mut Self) -> Option<&'hir hir::Pat<'hir>>,
+ ) -> hir::PatKind<'hir> {
+ match self.resolver.get_partial_res(p.id).map(|d| d.base_res()) {
+ // `None` can occur in body-less function signatures
+ res @ None | res @ Some(Res::Local(_)) => {
+ let canonical_id = match res {
+ Some(Res::Local(id)) => id,
+ _ => p.id,
+ };
+
+ hir::PatKind::Binding(
+ self.lower_binding_mode(binding_mode),
+ self.lower_node_id(canonical_id),
+ ident,
+ lower_sub(self),
+ )
+ }
+ Some(res) => hir::PatKind::Path(hir::QPath::Resolved(
+ None,
+ self.arena.alloc(hir::Path {
+ span: ident.span,
+ res: self.lower_res(res),
+ segments: arena_vec![self; hir::PathSegment::from_ident(ident)],
+ }),
+ )),
+ }
+ }
+
+ fn lower_binding_mode(&mut self, b: &BindingMode) -> hir::BindingAnnotation {
+ match *b {
+ BindingMode::ByValue(Mutability::Not) => hir::BindingAnnotation::Unannotated,
+ BindingMode::ByRef(Mutability::Not) => hir::BindingAnnotation::Ref,
+ BindingMode::ByValue(Mutability::Mut) => hir::BindingAnnotation::Mutable,
+ BindingMode::ByRef(Mutability::Mut) => hir::BindingAnnotation::RefMut,
+ }
+ }
+
+ fn pat_wild_with_node_id_of(&mut self, p: &Pat) -> &'hir hir::Pat<'hir> {
+ self.pat_with_node_id_of(p, hir::PatKind::Wild)
+ }
+
+ /// Construct a `Pat` with the `HirId` of `p.id` lowered.
+ fn pat_with_node_id_of(&mut self, p: &Pat, kind: hir::PatKind<'hir>) -> &'hir hir::Pat<'hir> {
+ self.arena.alloc(hir::Pat { hir_id: self.lower_node_id(p.id), kind, span: p.span })
+ }
+
+ /// Emit a friendly error for extra `..` patterns in a tuple/tuple struct/slice pattern.
+ fn ban_extra_rest_pat(&self, sp: Span, prev_sp: Span, ctx: &str) {
+ self.diagnostic()
+ .struct_span_err(sp, &format!("`..` can only be used once per {} pattern", ctx))
+ .span_label(sp, &format!("can only be used once per {} pattern", ctx))
+ .span_label(prev_sp, "previously used here")
+ .emit();
+ }
+
+ /// Used to ban the `..` pattern in places it shouldn't be semantically.
+ fn ban_illegal_rest_pat(&self, sp: Span) -> hir::PatKind<'hir> {
+ self.diagnostic()
+ .struct_span_err(sp, "`..` patterns are not allowed here")
+ .note("only allowed in tuple, tuple struct, and slice patterns")
+ .emit();
+
+ // We're not in a list context so `..` can be reasonably treated
+ // as `_` because it should always be valid and roughly matches the
+ // intent of `..` (notice that the rest of a single slot is that slot).
+ hir::PatKind::Wild
+ }
+
+ fn lower_range_end(&mut self, e: &RangeEnd, has_end: bool) -> hir::RangeEnd {
+ match *e {
+ RangeEnd::Excluded if has_end => hir::RangeEnd::Excluded,
+ // No end; so `X..` behaves like `RangeFrom`.
+ RangeEnd::Excluded | RangeEnd::Included(_) => hir::RangeEnd::Included,
+ }
+ }
+}
--- /dev/null
+use super::{AnonymousLifetimeMode, ImplTraitContext, LoweringContext, ParamMode};
+use super::{GenericArgsCtor, ParenthesizedGenericArgs};
+
+use rustc::lint::builtin::ELIDED_LIFETIMES_IN_PATHS;
+use rustc::span_bug;
+use rustc_error_codes::*;
+use rustc_errors::{struct_span_err, Applicability};
+use rustc_hir as hir;
+use rustc_hir::def::{DefKind, PartialRes, Res};
+use rustc_hir::def_id::DefId;
+use rustc_hir::GenericArg;
+use rustc_session::lint::BuiltinLintDiagnostics;
+use rustc_span::Span;
+use syntax::ast::{self, *};
+
+use log::debug;
+use smallvec::smallvec;
+
+impl<'a, 'hir> LoweringContext<'a, 'hir> {
+ crate fn lower_qpath(
+ &mut self,
+ id: NodeId,
+ qself: &Option<QSelf>,
+ p: &Path,
+ param_mode: ParamMode,
+ mut itctx: ImplTraitContext<'_, 'hir>,
+ ) -> hir::QPath<'hir> {
+ let qself_position = qself.as_ref().map(|q| q.position);
+ let qself = qself.as_ref().map(|q| self.lower_ty(&q.ty, itctx.reborrow()));
+
+ let partial_res =
+ self.resolver.get_partial_res(id).unwrap_or_else(|| PartialRes::new(Res::Err));
+
+ let proj_start = p.segments.len() - partial_res.unresolved_segments();
+ let path = self.arena.alloc(hir::Path {
+ res: self.lower_res(partial_res.base_res()),
+ segments: self.arena.alloc_from_iter(p.segments[..proj_start].iter().enumerate().map(
+ |(i, segment)| {
+ let param_mode = match (qself_position, param_mode) {
+ (Some(j), ParamMode::Optional) if i < j => {
+ // This segment is part of the trait path in a
+ // qualified path - one of `a`, `b` or `Trait`
+ // in `<X as a::b::Trait>::T::U::method`.
+ ParamMode::Explicit
+ }
+ _ => param_mode,
+ };
+
+ // Figure out if this is a type/trait segment,
+ // which may need lifetime elision performed.
+ let parent_def_id = |this: &mut Self, def_id: DefId| DefId {
+ krate: def_id.krate,
+ index: this.resolver.def_key(def_id).parent.expect("missing parent"),
+ };
+ let type_def_id = match partial_res.base_res() {
+ Res::Def(DefKind::AssocTy, def_id) if i + 2 == proj_start => {
+ Some(parent_def_id(self, def_id))
+ }
+ Res::Def(DefKind::Variant, def_id) if i + 1 == proj_start => {
+ Some(parent_def_id(self, def_id))
+ }
+ Res::Def(DefKind::Struct, def_id)
+ | Res::Def(DefKind::Union, def_id)
+ | Res::Def(DefKind::Enum, def_id)
+ | Res::Def(DefKind::TyAlias, def_id)
+ | Res::Def(DefKind::Trait, def_id)
+ if i + 1 == proj_start =>
+ {
+ Some(def_id)
+ }
+ _ => None,
+ };
+ let parenthesized_generic_args = match partial_res.base_res() {
+ // `a::b::Trait(Args)`
+ Res::Def(DefKind::Trait, _) if i + 1 == proj_start => {
+ ParenthesizedGenericArgs::Ok
+ }
+ // `a::b::Trait(Args)::TraitItem`
+ Res::Def(DefKind::Method, _)
+ | Res::Def(DefKind::AssocConst, _)
+ | Res::Def(DefKind::AssocTy, _)
+ if i + 2 == proj_start =>
+ {
+ ParenthesizedGenericArgs::Ok
+ }
+ // Avoid duplicated errors.
+ Res::Err => ParenthesizedGenericArgs::Ok,
+ // An error
+ _ => ParenthesizedGenericArgs::Err,
+ };
+
+ let num_lifetimes = type_def_id.map_or(0, |def_id| {
+ if let Some(&n) = self.type_def_lifetime_params.get(&def_id) {
+ return n;
+ }
+ assert!(!def_id.is_local());
+ let n = self.resolver.item_generics_num_lifetimes(def_id, self.sess);
+ self.type_def_lifetime_params.insert(def_id, n);
+ n
+ });
+ self.lower_path_segment(
+ p.span,
+ segment,
+ param_mode,
+ num_lifetimes,
+ parenthesized_generic_args,
+ itctx.reborrow(),
+ None,
+ )
+ },
+ )),
+ span: p.span,
+ });
+
+ // Simple case, either no projections, or only fully-qualified.
+ // E.g., `std::mem::size_of` or `<I as Iterator>::Item`.
+ if partial_res.unresolved_segments() == 0 {
+ return hir::QPath::Resolved(qself, path);
+ }
+
+ // Create the innermost type that we're projecting from.
+ let mut ty = if path.segments.is_empty() {
+ // If the base path is empty that means there exists a
+ // syntactical `Self`, e.g., `&i32` in `<&i32>::clone`.
+ qself.expect("missing QSelf for <T>::...")
+ } else {
+ // Otherwise, the base path is an implicit `Self` type path,
+ // e.g., `Vec` in `Vec::new` or `<I as Iterator>::Item` in
+ // `<I as Iterator>::Item::default`.
+ let new_id = self.next_id();
+ self.arena.alloc(self.ty_path(new_id, p.span, hir::QPath::Resolved(qself, path)))
+ };
+
+ // Anything after the base path are associated "extensions",
+ // out of which all but the last one are associated types,
+ // e.g., for `std::vec::Vec::<T>::IntoIter::Item::clone`:
+ // * base path is `std::vec::Vec<T>`
+ // * "extensions" are `IntoIter`, `Item` and `clone`
+ // * type nodes are:
+ // 1. `std::vec::Vec<T>` (created above)
+ // 2. `<std::vec::Vec<T>>::IntoIter`
+ // 3. `<<std::vec::Vec<T>>::IntoIter>::Item`
+ // * final path is `<<<std::vec::Vec<T>>::IntoIter>::Item>::clone`
+ for (i, segment) in p.segments.iter().enumerate().skip(proj_start) {
+ let segment = self.arena.alloc(self.lower_path_segment(
+ p.span,
+ segment,
+ param_mode,
+ 0,
+ ParenthesizedGenericArgs::Err,
+ itctx.reborrow(),
+ None,
+ ));
+ let qpath = hir::QPath::TypeRelative(ty, segment);
+
+ // It's finished, return the extension of the right node type.
+ if i == p.segments.len() - 1 {
+ return qpath;
+ }
+
+ // Wrap the associated extension in another type node.
+ let new_id = self.next_id();
+ ty = self.arena.alloc(self.ty_path(new_id, p.span, qpath));
+ }
+
+ // We should've returned in the for loop above.
+ span_bug!(
+ p.span,
+ "lower_qpath: no final extension segment in {}..{}",
+ proj_start,
+ p.segments.len()
+ )
+ }
+
+ crate fn lower_path_extra(
+ &mut self,
+ res: Res,
+ p: &Path,
+ param_mode: ParamMode,
+ explicit_owner: Option<NodeId>,
+ ) -> &'hir hir::Path<'hir> {
+ self.arena.alloc(hir::Path {
+ res,
+ segments: self.arena.alloc_from_iter(p.segments.iter().map(|segment| {
+ self.lower_path_segment(
+ p.span,
+ segment,
+ param_mode,
+ 0,
+ ParenthesizedGenericArgs::Err,
+ ImplTraitContext::disallowed(),
+ explicit_owner,
+ )
+ })),
+ span: p.span,
+ })
+ }
+
+ 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, None)
+ }
+
+ crate fn lower_path_segment(
+ &mut self,
+ path_span: Span,
+ segment: &PathSegment,
+ param_mode: ParamMode,
+ expected_lifetimes: usize,
+ parenthesized_generic_args: ParenthesizedGenericArgs,
+ itctx: ImplTraitContext<'_, 'hir>,
+ explicit_owner: Option<NodeId>,
+ ) -> hir::PathSegment<'hir> {
+ let (mut generic_args, infer_args) = if let Some(ref generic_args) = segment.args {
+ let msg = "parenthesized type parameters may only be used with a `Fn` trait";
+ match **generic_args {
+ GenericArgs::AngleBracketed(ref data) => {
+ self.lower_angle_bracketed_parameter_data(data, param_mode, itctx)
+ }
+ GenericArgs::Parenthesized(ref data) => match parenthesized_generic_args {
+ ParenthesizedGenericArgs::Ok => self.lower_parenthesized_parameter_data(data),
+ ParenthesizedGenericArgs::Err => {
+ let mut err = struct_span_err!(self.sess, data.span, E0214, "{}", msg);
+ err.span_label(data.span, "only `Fn` traits may use parentheses");
+ if let Ok(snippet) = self.sess.source_map().span_to_snippet(data.span) {
+ // Do not suggest going from `Trait()` to `Trait<>`
+ if data.inputs.len() > 0 {
+ if let Some(split) = snippet.find('(') {
+ let trait_name = &snippet[0..split];
+ let args = &snippet[split + 1..snippet.len() - 1];
+ err.span_suggestion(
+ data.span,
+ "use angle brackets instead",
+ format!("{}<{}>", trait_name, args),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+ };
+ err.emit();
+ (
+ self.lower_angle_bracketed_parameter_data(
+ &data.as_angle_bracketed_args(),
+ param_mode,
+ itctx,
+ )
+ .0,
+ false,
+ )
+ }
+ },
+ }
+ } else {
+ self.lower_angle_bracketed_parameter_data(&Default::default(), param_mode, itctx)
+ };
+
+ let has_lifetimes = generic_args.args.iter().any(|arg| match arg {
+ GenericArg::Lifetime(_) => true,
+ _ => false,
+ });
+ let first_generic_span = generic_args
+ .args
+ .iter()
+ .map(|a| a.span())
+ .chain(generic_args.bindings.iter().map(|b| b.span))
+ .next();
+ if !generic_args.parenthesized && !has_lifetimes {
+ generic_args.args = self
+ .elided_path_lifetimes(path_span, expected_lifetimes)
+ .map(|lt| GenericArg::Lifetime(lt))
+ .chain(generic_args.args.into_iter())
+ .collect();
+ if expected_lifetimes > 0 && param_mode == ParamMode::Explicit {
+ let anon_lt_suggestion = vec!["'_"; expected_lifetimes].join(", ");
+ let no_non_lt_args = generic_args.args.len() == expected_lifetimes;
+ let no_bindings = generic_args.bindings.is_empty();
+ let (incl_angl_brckt, insertion_sp, suggestion) = if no_non_lt_args && no_bindings {
+ // If there are no (non-implicit) generic args or associated type
+ // bindings, our suggestion includes the angle brackets.
+ (true, path_span.shrink_to_hi(), format!("<{}>", anon_lt_suggestion))
+ } else {
+ // Otherwise (sorry, this is kind of gross) we need to infer the
+ // place to splice in the `'_, ` from the generics that do exist.
+ let first_generic_span = first_generic_span
+ .expect("already checked that non-lifetime args or bindings exist");
+ (false, first_generic_span.shrink_to_lo(), format!("{}, ", anon_lt_suggestion))
+ };
+ match self.anonymous_lifetime_mode {
+ // In create-parameter mode we error here because we don't want to support
+ // deprecated impl elision in new features like impl elision and `async fn`,
+ // both of which work using the `CreateParameter` mode:
+ //
+ // impl Foo for std::cell::Ref<u32> // note lack of '_
+ // async fn foo(_: std::cell::Ref<u32>) { ... }
+ AnonymousLifetimeMode::CreateParameter => {
+ let mut err = struct_span_err!(
+ self.sess,
+ path_span,
+ E0726,
+ "implicit elided lifetime not allowed here"
+ );
+ rustc::lint::add_elided_lifetime_in_path_suggestion(
+ &self.sess,
+ &mut err,
+ expected_lifetimes,
+ path_span,
+ incl_angl_brckt,
+ insertion_sp,
+ suggestion,
+ );
+ err.emit();
+ }
+ AnonymousLifetimeMode::PassThrough | AnonymousLifetimeMode::ReportError => {
+ self.resolver.lint_buffer().buffer_lint_with_diagnostic(
+ ELIDED_LIFETIMES_IN_PATHS,
+ CRATE_NODE_ID,
+ path_span,
+ "hidden lifetime parameters in types are deprecated",
+ BuiltinLintDiagnostics::ElidedLifetimesInPaths(
+ expected_lifetimes,
+ path_span,
+ incl_angl_brckt,
+ insertion_sp,
+ suggestion,
+ ),
+ );
+ }
+ }
+ }
+ }
+
+ let res = self.expect_full_res(segment.id);
+ let id = if let Some(owner) = explicit_owner {
+ self.lower_node_id_with_owner(segment.id, owner)
+ } else {
+ self.lower_node_id(segment.id)
+ };
+ debug!(
+ "lower_path_segment: ident={:?} original-id={:?} new-id={:?}",
+ segment.ident, segment.id, id,
+ );
+
+ hir::PathSegment {
+ ident: segment.ident,
+ hir_id: Some(id),
+ res: Some(self.lower_res(res)),
+ infer_args,
+ args: if generic_args.is_empty() {
+ None
+ } else {
+ Some(self.arena.alloc(generic_args.into_generic_args(self.arena)))
+ },
+ }
+ }
+
+ fn lower_angle_bracketed_parameter_data(
+ &mut self,
+ data: &AngleBracketedArgs,
+ param_mode: ParamMode,
+ mut itctx: ImplTraitContext<'_, 'hir>,
+ ) -> (GenericArgsCtor<'hir>, bool) {
+ let &AngleBracketedArgs { ref args, ref constraints, .. } = data;
+ let has_non_lt_args = args.iter().any(|arg| match arg {
+ ast::GenericArg::Lifetime(_) => false,
+ ast::GenericArg::Type(_) => true,
+ ast::GenericArg::Const(_) => true,
+ });
+ (
+ GenericArgsCtor {
+ args: args.iter().map(|a| self.lower_generic_arg(a, itctx.reborrow())).collect(),
+ bindings: self.arena.alloc_from_iter(
+ constraints.iter().map(|b| self.lower_assoc_ty_constraint(b, itctx.reborrow())),
+ ),
+ parenthesized: false,
+ },
+ !has_non_lt_args && param_mode == ParamMode::Optional,
+ )
+ }
+
+ fn lower_parenthesized_parameter_data(
+ &mut self,
+ data: &ParenthesizedArgs,
+ ) -> (GenericArgsCtor<'hir>, bool) {
+ // Switch to `PassThrough` mode for anonymous lifetimes; this
+ // means that we permit things like `&Ref<T>`, where `Ref` has
+ // a hidden lifetime parameter. This is needed for backwards
+ // compatibility, even in contexts like an impl header where
+ // we generally don't permit such things (see #51008).
+ self.with_anonymous_lifetime_mode(AnonymousLifetimeMode::PassThrough, |this| {
+ let &ParenthesizedArgs { ref inputs, ref output, span } = data;
+ let inputs = this.arena.alloc_from_iter(
+ inputs.iter().map(|ty| this.lower_ty_direct(ty, ImplTraitContext::disallowed())),
+ );
+ let output_ty = match output {
+ FunctionRetTy::Ty(ty) => this.lower_ty(&ty, ImplTraitContext::disallowed()),
+ FunctionRetTy::Default(_) => this.arena.alloc(this.ty_tup(span, &[])),
+ };
+ let args = smallvec![GenericArg::Type(this.ty_tup(span, inputs))];
+ let binding = this.output_ty_binding(output_ty.span, output_ty);
+ (
+ GenericArgsCtor { args, bindings: arena_vec![this; binding], parenthesized: true },
+ false,
+ )
+ })
+ }
+
+ /// An associated type binding `Output = $ty`.
+ crate fn output_ty_binding(
+ &mut self,
+ span: Span,
+ ty: &'hir hir::Ty<'hir>,
+ ) -> hir::TypeBinding<'hir> {
+ let ident = Ident::with_dummy_span(hir::FN_OUTPUT_NAME);
+ let kind = hir::TypeBindingKind::Equality { ty };
+ hir::TypeBinding { hir_id: self.next_id(), span, ident, kind }
+ }
+}
--- /dev/null
+[package]
+authors = ["The Rust Project Developers"]
+name = "rustc_ast_passes"
+version = "0.0.0"
+edition = "2018"
+
+[lib]
+name = "rustc_ast_passes"
+path = "lib.rs"
+
+[dependencies]
+log = "0.4"
+rustc_data_structures = { path = "../librustc_data_structures" }
+rustc_errors = { path = "../librustc_errors" }
+rustc_error_codes = { path = "../librustc_error_codes" }
+rustc_feature = { path = "../librustc_feature" }
+rustc_parse = { path = "../librustc_parse" }
+rustc_session = { path = "../librustc_session" }
+rustc_span = { path = "../librustc_span" }
+syntax = { path = "../libsyntax" }
--- /dev/null
+// Validate AST before lowering it to HIR.
+//
+// This pass is supposed to catch things that fit into AST data structures,
+// but not permitted by the language. It runs after expansion when AST is frozen,
+// so it can check for erroneous constructions produced by syntax extensions.
+// This pass is supposed to perform only simple checks not requiring name resolution
+// or type checking or some other kind of complex analysis.
+
+use rustc_data_structures::fx::FxHashMap;
+use rustc_errors::{struct_span_err, Applicability, FatalError};
+use rustc_parse::validate_attr;
+use rustc_session::lint::builtin::PATTERNS_IN_FNS_WITHOUT_BODY;
+use rustc_session::lint::LintBuffer;
+use rustc_session::Session;
+use rustc_span::source_map::Spanned;
+use rustc_span::symbol::{kw, sym};
+use rustc_span::Span;
+use std::mem;
+use syntax::ast::*;
+use syntax::attr;
+use syntax::expand::is_proc_macro_attr;
+use syntax::print::pprust;
+use syntax::visit::{self, Visitor};
+use syntax::walk_list;
+
+use rustc_error_codes::*;
+
+/// A syntactic context that disallows certain kinds of bounds (e.g., `?Trait` or `?const Trait`).
+#[derive(Clone, Copy)]
+enum BoundContext {
+ ImplTrait,
+ TraitBounds,
+ TraitObject,
+}
+
+impl BoundContext {
+ fn description(&self) -> &'static str {
+ match self {
+ Self::ImplTrait => "`impl Trait`",
+ Self::TraitBounds => "supertraits",
+ Self::TraitObject => "trait objects",
+ }
+ }
+}
+
+struct AstValidator<'a> {
+ session: &'a Session,
+ has_proc_macro_decls: bool,
+
+ /// Used to ban nested `impl Trait`, e.g., `impl Into<impl Debug>`.
+ /// Nested `impl Trait` _is_ allowed in associated type position,
+ /// e.g., `impl Iterator<Item = impl Debug>`.
+ outer_impl_trait: Option<Span>,
+
+ /// Keeps track of the `BoundContext` as we recurse.
+ ///
+ /// This is used to forbid `?const Trait` bounds in, e.g.,
+ /// `impl Iterator<Item = Box<dyn ?const Trait>`.
+ bound_context: Option<BoundContext>,
+
+ /// Used to ban `impl Trait` in path projections like `<impl Iterator>::Item`
+ /// or `Foo::Bar<impl Trait>`
+ is_impl_trait_banned: bool,
+
+ /// Used to ban associated type bounds (i.e., `Type<AssocType: Bounds>`) in
+ /// certain positions.
+ is_assoc_ty_bound_banned: bool,
+
+ lint_buffer: &'a mut LintBuffer,
+}
+
+impl<'a> AstValidator<'a> {
+ fn with_banned_impl_trait(&mut self, f: impl FnOnce(&mut Self)) {
+ let old = mem::replace(&mut self.is_impl_trait_banned, true);
+ f(self);
+ self.is_impl_trait_banned = old;
+ }
+
+ fn with_banned_assoc_ty_bound(&mut self, f: impl FnOnce(&mut Self)) {
+ let old = mem::replace(&mut self.is_assoc_ty_bound_banned, true);
+ f(self);
+ self.is_assoc_ty_bound_banned = old;
+ }
+
+ fn with_impl_trait(&mut self, outer: Option<Span>, f: impl FnOnce(&mut Self)) {
+ let old = mem::replace(&mut self.outer_impl_trait, outer);
+ if outer.is_some() {
+ self.with_bound_context(BoundContext::ImplTrait, |this| f(this));
+ } else {
+ f(self)
+ }
+ self.outer_impl_trait = old;
+ }
+
+ fn with_bound_context(&mut self, ctx: BoundContext, f: impl FnOnce(&mut Self)) {
+ let old = self.bound_context.replace(ctx);
+ f(self);
+ self.bound_context = old;
+ }
+
+ fn visit_assoc_ty_constraint_from_generic_args(&mut self, constraint: &'a AssocTyConstraint) {
+ match constraint.kind {
+ AssocTyConstraintKind::Equality { .. } => {}
+ AssocTyConstraintKind::Bound { .. } => {
+ if self.is_assoc_ty_bound_banned {
+ self.err_handler().span_err(
+ constraint.span,
+ "associated type bounds are not allowed within structs, enums, or unions",
+ );
+ }
+ }
+ }
+ self.visit_assoc_ty_constraint(constraint);
+ }
+
+ // Mirrors `visit::walk_ty`, but tracks relevant state.
+ fn walk_ty(&mut self, t: &'a Ty) {
+ match t.kind {
+ TyKind::ImplTrait(..) => {
+ self.with_impl_trait(Some(t.span), |this| visit::walk_ty(this, t))
+ }
+ TyKind::TraitObject(..) => {
+ self.with_bound_context(BoundContext::TraitObject, |this| visit::walk_ty(this, t));
+ }
+ TyKind::Path(ref qself, ref path) => {
+ // We allow these:
+ // - `Option<impl Trait>`
+ // - `option::Option<impl Trait>`
+ // - `option::Option<T>::Foo<impl Trait>
+ //
+ // But not these:
+ // - `<impl Trait>::Foo`
+ // - `option::Option<impl Trait>::Foo`.
+ //
+ // To implement this, we disallow `impl Trait` from `qself`
+ // (for cases like `<impl Trait>::Foo>`)
+ // but we allow `impl Trait` in `GenericArgs`
+ // iff there are no more PathSegments.
+ if let Some(ref qself) = *qself {
+ // `impl Trait` in `qself` is always illegal
+ self.with_banned_impl_trait(|this| this.visit_ty(&qself.ty));
+ }
+
+ // Note that there should be a call to visit_path here,
+ // so if any logic is added to process `Path`s a call to it should be
+ // added both in visit_path and here. This code mirrors visit::walk_path.
+ for (i, segment) in path.segments.iter().enumerate() {
+ // Allow `impl Trait` iff we're on the final path segment
+ if i == path.segments.len() - 1 {
+ self.visit_path_segment(path.span, segment);
+ } else {
+ self.with_banned_impl_trait(|this| {
+ this.visit_path_segment(path.span, segment)
+ });
+ }
+ }
+ }
+ _ => visit::walk_ty(self, t),
+ }
+ }
+
+ fn err_handler(&self) -> &rustc_errors::Handler {
+ &self.session.diagnostic()
+ }
+
+ fn check_lifetime(&self, ident: Ident) {
+ let valid_names = [kw::UnderscoreLifetime, kw::StaticLifetime, kw::Invalid];
+ if !valid_names.contains(&ident.name) && ident.without_first_quote().is_reserved() {
+ self.err_handler().span_err(ident.span, "lifetimes cannot use keyword names");
+ }
+ }
+
+ fn check_label(&self, ident: Ident) {
+ if ident.without_first_quote().is_reserved() {
+ self.err_handler()
+ .span_err(ident.span, &format!("invalid label name `{}`", ident.name));
+ }
+ }
+
+ fn invalid_visibility(&self, vis: &Visibility, note: Option<&str>) {
+ if let VisibilityKind::Inherited = vis.node {
+ return;
+ }
+
+ let mut err =
+ struct_span_err!(self.session, vis.span, E0449, "unnecessary visibility qualifier");
+ if vis.node.is_pub() {
+ err.span_label(vis.span, "`pub` not permitted here because it's implied");
+ }
+ if let Some(note) = note {
+ err.note(note);
+ }
+ err.emit();
+ }
+
+ fn check_decl_no_pat(decl: &FnDecl, mut report_err: impl FnMut(Span, bool)) {
+ for Param { pat, .. } in &decl.inputs {
+ match pat.kind {
+ PatKind::Ident(BindingMode::ByValue(Mutability::Not), _, None) | PatKind::Wild => {}
+ PatKind::Ident(BindingMode::ByValue(Mutability::Mut), _, None) => {
+ report_err(pat.span, true)
+ }
+ _ => report_err(pat.span, false),
+ }
+ }
+ }
+
+ fn check_trait_fn_not_async(&self, span: Span, asyncness: IsAsync) {
+ if asyncness.is_async() {
+ struct_span_err!(self.session, span, E0706, "trait fns cannot be declared `async`")
+ .note("`async` trait functions are not currently supported")
+ .note(
+ "consider using the `async-trait` crate: \
+ https://crates.io/crates/async-trait",
+ )
+ .emit();
+ }
+ }
+
+ fn check_trait_fn_not_const(&self, constness: Spanned<Constness>) {
+ if constness.node == Constness::Const {
+ struct_span_err!(
+ self.session,
+ constness.span,
+ E0379,
+ "trait fns cannot be declared const"
+ )
+ .span_label(constness.span, "trait fns cannot be const")
+ .emit();
+ }
+ }
+
+ // FIXME(ecstaticmorse): Instead, use `bound_context` to check this in `visit_param_bound`.
+ fn no_questions_in_bounds(&self, bounds: &GenericBounds, where_: &str, is_trait: bool) {
+ for bound in bounds {
+ if let GenericBound::Trait(ref poly, TraitBoundModifier::Maybe) = *bound {
+ let mut err = self.err_handler().struct_span_err(
+ poly.span,
+ &format!("`?Trait` is not permitted in {}", where_),
+ );
+ if is_trait {
+ let path_str = pprust::path_to_string(&poly.trait_ref.path);
+ err.note(&format!("traits are `?{}` by default", path_str));
+ }
+ err.emit();
+ }
+ }
+ }
+
+ /// Matches `'-' lit | lit (cf. parser::Parser::parse_literal_maybe_minus)`,
+ /// or paths for ranges.
+ //
+ // FIXME: do we want to allow `expr -> pattern` conversion to create path expressions?
+ // That means making this work:
+ //
+ // ```rust,ignore (FIXME)
+ // struct S;
+ // macro_rules! m {
+ // ($a:expr) => {
+ // let $a = S;
+ // }
+ // }
+ // m!(S);
+ // ```
+ fn check_expr_within_pat(&self, expr: &Expr, allow_paths: bool) {
+ match expr.kind {
+ ExprKind::Lit(..) | ExprKind::Err => {}
+ ExprKind::Path(..) if allow_paths => {}
+ ExprKind::Unary(UnOp::Neg, ref inner)
+ if match inner.kind {
+ ExprKind::Lit(_) => true,
+ _ => false,
+ } => {}
+ _ => self.err_handler().span_err(
+ expr.span,
+ "arbitrary expressions aren't allowed \
+ in patterns",
+ ),
+ }
+ }
+
+ fn check_late_bound_lifetime_defs(&self, params: &[GenericParam]) {
+ // Check only lifetime parameters are present and that the lifetime
+ // parameters that are present have no bounds.
+ let non_lt_param_spans: Vec<_> = params
+ .iter()
+ .filter_map(|param| match param.kind {
+ GenericParamKind::Lifetime { .. } => {
+ if !param.bounds.is_empty() {
+ let spans: Vec<_> = param.bounds.iter().map(|b| b.span()).collect();
+ self.err_handler()
+ .span_err(spans, "lifetime bounds cannot be used in this context");
+ }
+ None
+ }
+ _ => Some(param.ident.span),
+ })
+ .collect();
+ if !non_lt_param_spans.is_empty() {
+ self.err_handler().span_err(
+ non_lt_param_spans,
+ "only lifetime parameters can be used in this context",
+ );
+ }
+ }
+
+ fn check_fn_decl(&self, fn_decl: &FnDecl) {
+ match &*fn_decl.inputs {
+ [Param { ty, span, .. }] => {
+ if let TyKind::CVarArgs = ty.kind {
+ self.err_handler().span_err(
+ *span,
+ "C-variadic function must be declared with at least one named argument",
+ );
+ }
+ }
+ [ps @ .., _] => {
+ for Param { ty, span, .. } in ps {
+ if let TyKind::CVarArgs = ty.kind {
+ self.err_handler().span_err(
+ *span,
+ "`...` must be the last argument of a C-variadic function",
+ );
+ }
+ }
+ }
+ _ => {}
+ }
+
+ fn_decl
+ .inputs
+ .iter()
+ .flat_map(|i| i.attrs.as_ref())
+ .filter(|attr| {
+ let arr = [sym::allow, sym::cfg, sym::cfg_attr, sym::deny, sym::forbid, sym::warn];
+ !arr.contains(&attr.name_or_empty()) && attr::is_builtin_attr(attr)
+ })
+ .for_each(|attr| {
+ if attr.is_doc_comment() {
+ self.err_handler()
+ .struct_span_err(
+ attr.span,
+ "documentation comments cannot be applied to function parameters",
+ )
+ .span_label(attr.span, "doc comments are not allowed here")
+ .emit();
+ } else {
+ self.err_handler().span_err(
+ attr.span,
+ "allow, cfg, cfg_attr, deny, \
+ forbid, and warn are the only allowed built-in attributes in function parameters",
+ )
+ }
+ });
+ }
+
+ fn check_defaultness(&self, span: Span, defaultness: Defaultness) {
+ if let Defaultness::Default = defaultness {
+ self.err_handler()
+ .struct_span_err(span, "`default` is only allowed on items in `impl` definitions")
+ .emit();
+ }
+ }
+
+ fn check_impl_item_provided<T>(&self, sp: Span, body: &Option<T>, ctx: &str, sugg: &str) {
+ if body.is_some() {
+ return;
+ }
+
+ self.err_handler()
+ .struct_span_err(sp, &format!("associated {} in `impl` without body", ctx))
+ .span_suggestion(
+ self.session.source_map().end_point(sp),
+ &format!("provide a definition for the {}", ctx),
+ sugg.to_string(),
+ Applicability::HasPlaceholders,
+ )
+ .emit();
+ }
+
+ fn check_impl_assoc_type_no_bounds(&self, bounds: &[GenericBound]) {
+ let span = match bounds {
+ [] => return,
+ [b0] => b0.span(),
+ [b0, .., bl] => b0.span().to(bl.span()),
+ };
+ self.err_handler()
+ .struct_span_err(span, "bounds on associated `type`s in `impl`s have no effect")
+ .emit();
+ }
+
+ fn check_c_varadic_type(&self, decl: &FnDecl) {
+ for Param { ty, span, .. } in &decl.inputs {
+ if let TyKind::CVarArgs = ty.kind {
+ self.err_handler()
+ .struct_span_err(
+ *span,
+ "only foreign or `unsafe extern \"C\" functions may be C-variadic",
+ )
+ .emit();
+ }
+ }
+ }
+}
+
+enum GenericPosition {
+ Param,
+ Arg,
+}
+
+fn validate_generics_order<'a>(
+ sess: &Session,
+ handler: &rustc_errors::Handler,
+ generics: impl Iterator<Item = (ParamKindOrd, Option<&'a [GenericBound]>, Span, Option<String>)>,
+ pos: GenericPosition,
+ span: Span,
+) {
+ let mut max_param: Option<ParamKindOrd> = None;
+ let mut out_of_order = FxHashMap::default();
+ let mut param_idents = vec![];
+ let mut found_type = false;
+ let mut found_const = false;
+
+ for (kind, bounds, span, ident) in generics {
+ if let Some(ident) = ident {
+ param_idents.push((kind, bounds, param_idents.len(), ident));
+ }
+ let max_param = &mut max_param;
+ match max_param {
+ Some(max_param) if *max_param > kind => {
+ let entry = out_of_order.entry(kind).or_insert((*max_param, vec![]));
+ entry.1.push(span);
+ }
+ Some(_) | None => *max_param = Some(kind),
+ };
+ match kind {
+ ParamKindOrd::Type => found_type = true,
+ ParamKindOrd::Const => found_const = true,
+ _ => {}
+ }
+ }
+
+ let mut ordered_params = "<".to_string();
+ if !out_of_order.is_empty() {
+ param_idents.sort_by_key(|&(po, _, i, _)| (po, i));
+ let mut first = true;
+ for (_, bounds, _, ident) in param_idents {
+ if !first {
+ ordered_params += ", ";
+ }
+ ordered_params += &ident;
+ if let Some(bounds) = bounds {
+ if !bounds.is_empty() {
+ ordered_params += ": ";
+ ordered_params += &pprust::bounds_to_string(&bounds);
+ }
+ }
+ first = false;
+ }
+ }
+ ordered_params += ">";
+
+ let pos_str = match pos {
+ GenericPosition::Param => "parameter",
+ GenericPosition::Arg => "argument",
+ };
+
+ for (param_ord, (max_param, spans)) in &out_of_order {
+ let mut err = handler.struct_span_err(
+ spans.clone(),
+ &format!(
+ "{} {pos}s must be declared prior to {} {pos}s",
+ param_ord,
+ max_param,
+ pos = pos_str,
+ ),
+ );
+ if let GenericPosition::Param = pos {
+ err.span_suggestion(
+ span,
+ &format!(
+ "reorder the {}s: lifetimes, then types{}",
+ pos_str,
+ if sess.features_untracked().const_generics { ", then consts" } else { "" },
+ ),
+ ordered_params.clone(),
+ Applicability::MachineApplicable,
+ );
+ }
+ err.emit();
+ }
+
+ // FIXME(const_generics): we shouldn't have to abort here at all, but we currently get ICEs
+ // if we don't. Const parameters and type parameters can currently conflict if they
+ // are out-of-order.
+ if !out_of_order.is_empty() && found_type && found_const {
+ FatalError.raise();
+ }
+}
+
+impl<'a> Visitor<'a> for AstValidator<'a> {
+ fn visit_attribute(&mut self, attr: &Attribute) {
+ validate_attr::check_meta(&self.session.parse_sess, attr);
+ }
+
+ fn visit_expr(&mut self, expr: &'a Expr) {
+ match &expr.kind {
+ ExprKind::Closure(_, _, _, fn_decl, _, _) => {
+ self.check_fn_decl(fn_decl);
+ }
+ ExprKind::InlineAsm(..) if !self.session.target.target.options.allow_asm => {
+ struct_span_err!(
+ self.session,
+ expr.span,
+ E0472,
+ "asm! is unsupported on this target"
+ )
+ .emit();
+ }
+ _ => {}
+ }
+
+ visit::walk_expr(self, expr);
+ }
+
+ fn visit_ty(&mut self, ty: &'a Ty) {
+ match ty.kind {
+ TyKind::BareFn(ref bfty) => {
+ self.check_fn_decl(&bfty.decl);
+ Self::check_decl_no_pat(&bfty.decl, |span, _| {
+ struct_span_err!(
+ self.session,
+ span,
+ E0561,
+ "patterns aren't allowed in function pointer types"
+ )
+ .emit();
+ });
+ self.check_late_bound_lifetime_defs(&bfty.generic_params);
+ }
+ TyKind::TraitObject(ref bounds, ..) => {
+ let mut any_lifetime_bounds = false;
+ for bound in bounds {
+ if let GenericBound::Outlives(ref lifetime) = *bound {
+ if any_lifetime_bounds {
+ struct_span_err!(
+ self.session,
+ lifetime.ident.span,
+ E0226,
+ "only a single explicit lifetime bound is permitted"
+ )
+ .emit();
+ break;
+ }
+ any_lifetime_bounds = true;
+ }
+ }
+ self.no_questions_in_bounds(bounds, "trait object types", false);
+ }
+ TyKind::ImplTrait(_, ref bounds) => {
+ if self.is_impl_trait_banned {
+ struct_span_err!(
+ self.session,
+ ty.span,
+ E0667,
+ "`impl Trait` is not allowed in path parameters"
+ )
+ .emit();
+ }
+
+ if let Some(outer_impl_trait_sp) = self.outer_impl_trait {
+ struct_span_err!(
+ self.session,
+ ty.span,
+ E0666,
+ "nested `impl Trait` is not allowed"
+ )
+ .span_label(outer_impl_trait_sp, "outer `impl Trait`")
+ .span_label(ty.span, "nested `impl Trait` here")
+ .emit();
+ }
+
+ if !bounds
+ .iter()
+ .any(|b| if let GenericBound::Trait(..) = *b { true } else { false })
+ {
+ self.err_handler().span_err(ty.span, "at least one trait must be specified");
+ }
+
+ self.walk_ty(ty);
+ return;
+ }
+ _ => {}
+ }
+
+ self.walk_ty(ty)
+ }
+
+ fn visit_label(&mut self, label: &'a Label) {
+ self.check_label(label.ident);
+ visit::walk_label(self, label);
+ }
+
+ fn visit_lifetime(&mut self, lifetime: &'a Lifetime) {
+ self.check_lifetime(lifetime.ident);
+ visit::walk_lifetime(self, lifetime);
+ }
+
+ fn visit_item(&mut self, item: &'a Item) {
+ if item.attrs.iter().any(|attr| is_proc_macro_attr(attr)) {
+ self.has_proc_macro_decls = true;
+ }
+
+ match item.kind {
+ ItemKind::Impl(unsafety, polarity, _, _, Some(..), ref ty, ref impl_items) => {
+ self.invalid_visibility(&item.vis, None);
+ if let TyKind::Err = ty.kind {
+ self.err_handler()
+ .struct_span_err(item.span, "`impl Trait for .. {}` is an obsolete syntax")
+ .help("use `auto trait Trait {}` instead")
+ .emit();
+ }
+ if unsafety == Unsafety::Unsafe && polarity == ImplPolarity::Negative {
+ struct_span_err!(
+ self.session,
+ item.span,
+ E0198,
+ "negative impls cannot be unsafe"
+ )
+ .emit();
+ }
+ for impl_item in impl_items {
+ self.invalid_visibility(&impl_item.vis, None);
+ if let AssocItemKind::Fn(ref sig, _) = impl_item.kind {
+ self.check_trait_fn_not_const(sig.header.constness);
+ self.check_trait_fn_not_async(impl_item.span, sig.header.asyncness.node);
+ }
+ }
+ }
+ ItemKind::Impl(unsafety, polarity, defaultness, _, None, _, _) => {
+ self.invalid_visibility(
+ &item.vis,
+ Some("place qualifiers on individual impl items instead"),
+ );
+ if unsafety == Unsafety::Unsafe {
+ struct_span_err!(
+ self.session,
+ item.span,
+ E0197,
+ "inherent impls cannot be unsafe"
+ )
+ .emit();
+ }
+ if polarity == ImplPolarity::Negative {
+ self.err_handler().span_err(item.span, "inherent impls cannot be negative");
+ }
+ if defaultness == Defaultness::Default {
+ self.err_handler()
+ .struct_span_err(item.span, "inherent impls cannot be default")
+ .note("only trait implementations may be annotated with default")
+ .emit();
+ }
+ }
+ ItemKind::Fn(ref sig, ref generics, _) => {
+ self.visit_fn_header(&sig.header);
+ self.check_fn_decl(&sig.decl);
+ // We currently do not permit const generics in `const fn`, as
+ // this is tantamount to allowing compile-time dependent typing.
+ if sig.header.constness.node == Constness::Const {
+ // Look for const generics and error if we find any.
+ for param in &generics.params {
+ match param.kind {
+ GenericParamKind::Const { .. } => {
+ self.err_handler()
+ .struct_span_err(
+ item.span,
+ "const parameters are not permitted in `const fn`",
+ )
+ .emit();
+ }
+ _ => {}
+ }
+ }
+ }
+ // Reject C-varadic type unless the function is `unsafe extern "C"` semantically.
+ match sig.header.ext {
+ Extern::Explicit(StrLit { symbol_unescaped: sym::C, .. })
+ | Extern::Implicit
+ if sig.header.unsafety == Unsafety::Unsafe => {}
+ _ => self.check_c_varadic_type(&sig.decl),
+ }
+ }
+ ItemKind::ForeignMod(..) => {
+ self.invalid_visibility(
+ &item.vis,
+ Some("place qualifiers on individual foreign items instead"),
+ );
+ }
+ ItemKind::Enum(ref def, _) => {
+ for variant in &def.variants {
+ self.invalid_visibility(&variant.vis, None);
+ for field in variant.data.fields() {
+ self.invalid_visibility(&field.vis, None);
+ }
+ }
+ }
+ ItemKind::Trait(is_auto, _, ref generics, ref bounds, ref trait_items) => {
+ if is_auto == IsAuto::Yes {
+ // Auto traits cannot have generics, super traits nor contain items.
+ if !generics.params.is_empty() {
+ struct_span_err!(
+ self.session,
+ item.span,
+ E0567,
+ "auto traits cannot have generic parameters"
+ )
+ .emit();
+ }
+ if !bounds.is_empty() {
+ struct_span_err!(
+ self.session,
+ item.span,
+ E0568,
+ "auto traits cannot have super traits"
+ )
+ .emit();
+ }
+ if !trait_items.is_empty() {
+ struct_span_err!(
+ self.session,
+ item.span,
+ E0380,
+ "auto traits cannot have methods or associated items"
+ )
+ .emit();
+ }
+ }
+ self.no_questions_in_bounds(bounds, "supertraits", true);
+
+ // Equivalent of `visit::walk_item` for `ItemKind::Trait` that inserts a bound
+ // context for the supertraits.
+ self.visit_vis(&item.vis);
+ self.visit_ident(item.ident);
+ self.visit_generics(generics);
+ self.with_bound_context(BoundContext::TraitBounds, |this| {
+ walk_list!(this, visit_param_bound, bounds);
+ });
+ walk_list!(self, visit_trait_item, trait_items);
+ walk_list!(self, visit_attribute, &item.attrs);
+ return;
+ }
+ ItemKind::Mod(_) => {
+ // Ensure that `path` attributes on modules are recorded as used (cf. issue #35584).
+ attr::first_attr_value_str_by_name(&item.attrs, sym::path);
+ }
+ ItemKind::Union(ref vdata, _) => {
+ if let VariantData::Tuple(..) | VariantData::Unit(..) = vdata {
+ self.err_handler()
+ .span_err(item.span, "tuple and unit unions are not permitted");
+ }
+ if vdata.fields().is_empty() {
+ self.err_handler().span_err(item.span, "unions cannot have zero fields");
+ }
+ }
+ _ => {}
+ }
+
+ visit::walk_item(self, item)
+ }
+
+ fn visit_foreign_item(&mut self, fi: &'a ForeignItem) {
+ match fi.kind {
+ ForeignItemKind::Fn(ref decl, _) => {
+ self.check_fn_decl(decl);
+ Self::check_decl_no_pat(decl, |span, _| {
+ struct_span_err!(
+ self.session,
+ span,
+ E0130,
+ "patterns aren't allowed in foreign function declarations"
+ )
+ .span_label(span, "pattern not allowed in foreign function")
+ .emit();
+ });
+ }
+ ForeignItemKind::Static(..) | ForeignItemKind::Ty | ForeignItemKind::Macro(..) => {}
+ }
+
+ visit::walk_foreign_item(self, fi)
+ }
+
+ // Mirrors `visit::walk_generic_args`, but tracks relevant state.
+ fn visit_generic_args(&mut self, _: Span, generic_args: &'a GenericArgs) {
+ match *generic_args {
+ GenericArgs::AngleBracketed(ref data) => {
+ walk_list!(self, visit_generic_arg, &data.args);
+ validate_generics_order(
+ self.session,
+ self.err_handler(),
+ data.args.iter().map(|arg| {
+ (
+ match arg {
+ GenericArg::Lifetime(..) => ParamKindOrd::Lifetime,
+ GenericArg::Type(..) => ParamKindOrd::Type,
+ GenericArg::Const(..) => ParamKindOrd::Const,
+ },
+ None,
+ arg.span(),
+ None,
+ )
+ }),
+ GenericPosition::Arg,
+ generic_args.span(),
+ );
+
+ // Type bindings such as `Item = impl Debug` in `Iterator<Item = Debug>`
+ // are allowed to contain nested `impl Trait`.
+ self.with_impl_trait(None, |this| {
+ walk_list!(
+ this,
+ visit_assoc_ty_constraint_from_generic_args,
+ &data.constraints
+ );
+ });
+ }
+ GenericArgs::Parenthesized(ref data) => {
+ walk_list!(self, visit_ty, &data.inputs);
+ if let FunctionRetTy::Ty(ty) = &data.output {
+ // `-> Foo` syntax is essentially an associated type binding,
+ // so it is also allowed to contain nested `impl Trait`.
+ self.with_impl_trait(None, |this| this.visit_ty(ty));
+ }
+ }
+ }
+ }
+
+ fn visit_generics(&mut self, generics: &'a Generics) {
+ let mut prev_ty_default = None;
+ for param in &generics.params {
+ if let GenericParamKind::Type { ref default, .. } = param.kind {
+ if default.is_some() {
+ prev_ty_default = Some(param.ident.span);
+ } else if let Some(span) = prev_ty_default {
+ self.err_handler()
+ .span_err(span, "type parameters with a default must be trailing");
+ break;
+ }
+ }
+ }
+
+ validate_generics_order(
+ self.session,
+ self.err_handler(),
+ generics.params.iter().map(|param| {
+ let ident = Some(param.ident.to_string());
+ let (kind, ident) = match ¶m.kind {
+ GenericParamKind::Lifetime { .. } => (ParamKindOrd::Lifetime, ident),
+ GenericParamKind::Type { .. } => (ParamKindOrd::Type, ident),
+ GenericParamKind::Const { ref ty } => {
+ let ty = pprust::ty_to_string(ty);
+ (ParamKindOrd::Const, Some(format!("const {}: {}", param.ident, ty)))
+ }
+ };
+ (kind, Some(&*param.bounds), param.ident.span, ident)
+ }),
+ GenericPosition::Param,
+ generics.span,
+ );
+
+ for predicate in &generics.where_clause.predicates {
+ if let WherePredicate::EqPredicate(ref predicate) = *predicate {
+ self.err_handler()
+ .struct_span_err(
+ predicate.span,
+ "equality constraints are not yet supported in `where` clauses",
+ )
+ .span_label(predicate.span, "not supported")
+ .note(
+ "for more information, see https://github.com/rust-lang/rust/issues/20041",
+ )
+ .emit();
+ }
+ }
+
+ visit::walk_generics(self, generics)
+ }
+
+ fn visit_generic_param(&mut self, param: &'a GenericParam) {
+ if let GenericParamKind::Lifetime { .. } = param.kind {
+ self.check_lifetime(param.ident);
+ }
+ visit::walk_generic_param(self, param);
+ }
+
+ fn visit_param_bound(&mut self, bound: &'a GenericBound) {
+ if let GenericBound::Trait(poly, maybe_bound) = bound {
+ match poly.trait_ref.constness {
+ Some(Constness::NotConst) => {
+ if *maybe_bound == TraitBoundModifier::Maybe {
+ self.err_handler()
+ .span_err(bound.span(), "`?const` and `?` are mutually exclusive");
+ }
+
+ if let Some(ctx) = self.bound_context {
+ let msg = format!("`?const` is not permitted in {}", ctx.description());
+ self.err_handler().span_err(bound.span(), &msg);
+ }
+ }
+
+ Some(Constness::Const) => panic!("Parser should reject bare `const` on bounds"),
+ None => {}
+ }
+ }
+
+ visit::walk_param_bound(self, bound)
+ }
+
+ fn visit_pat(&mut self, pat: &'a Pat) {
+ match pat.kind {
+ PatKind::Lit(ref expr) => {
+ self.check_expr_within_pat(expr, false);
+ }
+ PatKind::Range(ref start, ref end, _) => {
+ if let Some(expr) = start {
+ self.check_expr_within_pat(expr, true);
+ }
+ if let Some(expr) = end {
+ self.check_expr_within_pat(expr, true);
+ }
+ }
+ _ => {}
+ }
+
+ visit::walk_pat(self, pat)
+ }
+
+ fn visit_where_predicate(&mut self, p: &'a WherePredicate) {
+ if let &WherePredicate::BoundPredicate(ref bound_predicate) = p {
+ // A type binding, eg `for<'c> Foo: Send+Clone+'c`
+ self.check_late_bound_lifetime_defs(&bound_predicate.bound_generic_params);
+ }
+ visit::walk_where_predicate(self, p);
+ }
+
+ fn visit_poly_trait_ref(&mut self, t: &'a PolyTraitRef, m: &'a TraitBoundModifier) {
+ self.check_late_bound_lifetime_defs(&t.bound_generic_params);
+ visit::walk_poly_trait_ref(self, t, m);
+ }
+
+ fn visit_variant_data(&mut self, s: &'a VariantData) {
+ self.with_banned_assoc_ty_bound(|this| visit::walk_struct_def(this, s))
+ }
+
+ fn visit_enum_def(
+ &mut self,
+ enum_definition: &'a EnumDef,
+ generics: &'a Generics,
+ item_id: NodeId,
+ _: Span,
+ ) {
+ self.with_banned_assoc_ty_bound(|this| {
+ visit::walk_enum_def(this, enum_definition, generics, item_id)
+ })
+ }
+
+ fn visit_impl_item(&mut self, ii: &'a AssocItem) {
+ match &ii.kind {
+ AssocItemKind::Const(_, body) => {
+ self.check_impl_item_provided(ii.span, body, "constant", " = <expr>;");
+ }
+ AssocItemKind::Fn(sig, body) => {
+ self.check_impl_item_provided(ii.span, body, "function", " { <body> }");
+ self.check_fn_decl(&sig.decl);
+ }
+ AssocItemKind::TyAlias(bounds, body) => {
+ self.check_impl_item_provided(ii.span, body, "type", " = <type>;");
+ self.check_impl_assoc_type_no_bounds(bounds);
+ }
+ _ => {}
+ }
+ visit::walk_impl_item(self, ii);
+ }
+
+ fn visit_trait_item(&mut self, ti: &'a AssocItem) {
+ self.invalid_visibility(&ti.vis, None);
+ self.check_defaultness(ti.span, ti.defaultness);
+
+ if let AssocItemKind::Fn(sig, block) = &ti.kind {
+ self.check_fn_decl(&sig.decl);
+ self.check_trait_fn_not_async(ti.span, sig.header.asyncness.node);
+ self.check_trait_fn_not_const(sig.header.constness);
+ if block.is_none() {
+ Self::check_decl_no_pat(&sig.decl, |span, mut_ident| {
+ if mut_ident {
+ self.lint_buffer.buffer_lint(
+ PATTERNS_IN_FNS_WITHOUT_BODY,
+ ti.id,
+ span,
+ "patterns aren't allowed in methods without bodies",
+ );
+ } else {
+ struct_span_err!(
+ self.session,
+ span,
+ E0642,
+ "patterns aren't allowed in methods without bodies"
+ )
+ .emit();
+ }
+ });
+ }
+ }
+
+ visit::walk_trait_item(self, ti);
+ }
+
+ fn visit_assoc_item(&mut self, item: &'a AssocItem) {
+ if let AssocItemKind::Fn(sig, _) = &item.kind {
+ self.check_c_varadic_type(&sig.decl);
+ }
+ visit::walk_assoc_item(self, item);
+ }
+}
+
+pub fn check_crate(session: &Session, krate: &Crate, lints: &mut LintBuffer) -> bool {
+ let mut validator = AstValidator {
+ session,
+ has_proc_macro_decls: false,
+ outer_impl_trait: None,
+ bound_context: None,
+ is_impl_trait_banned: false,
+ is_assoc_ty_bound_banned: false,
+ lint_buffer: lints,
+ };
+ visit::walk_crate(&mut validator, krate);
+
+ validator.has_proc_macro_decls
+}
--- /dev/null
+use rustc_error_codes::*;
+use rustc_errors::{struct_span_err, Handler};
+use rustc_feature::{AttributeGate, BUILTIN_ATTRIBUTE_MAP};
+use rustc_feature::{Features, GateIssue, UnstableFeatures};
+use rustc_span::source_map::Spanned;
+use rustc_span::symbol::sym;
+use rustc_span::Span;
+use syntax::ast::{self, AssocTyConstraint, AssocTyConstraintKind, NodeId};
+use syntax::ast::{GenericParam, GenericParamKind, PatKind, RangeEnd, VariantData};
+use syntax::attr;
+use syntax::sess::{feature_err, feature_err_issue, ParseSess};
+use syntax::visit::{self, FnKind, Visitor};
+
+use log::debug;
+
+macro_rules! gate_feature_fn {
+ ($cx: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr) => {{
+ let (cx, has_feature, span, name, explain) = (&*$cx, $has_feature, $span, $name, $explain);
+ let has_feature: bool = has_feature(&$cx.features);
+ debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", name, span, has_feature);
+ if !has_feature && !span.allows_unstable($name) {
+ feature_err_issue(cx.parse_sess, name, span, GateIssue::Language, explain).emit();
+ }
+ }};
+}
+
+macro_rules! gate_feature_post {
+ ($cx: expr, $feature: ident, $span: expr, $explain: expr) => {
+ gate_feature_fn!($cx, |x: &Features| x.$feature, $span, sym::$feature, $explain)
+ };
+}
+
+pub fn check_attribute(attr: &ast::Attribute, parse_sess: &ParseSess, features: &Features) {
+ PostExpansionVisitor { parse_sess, features }.visit_attribute(attr)
+}
+
+struct PostExpansionVisitor<'a> {
+ parse_sess: &'a ParseSess,
+ features: &'a Features,
+}
+
+impl<'a> PostExpansionVisitor<'a> {
+ fn check_abi(&self, abi: ast::StrLit) {
+ let ast::StrLit { symbol_unescaped, span, .. } = abi;
+
+ match &*symbol_unescaped.as_str() {
+ // Stable
+ "Rust" | "C" | "cdecl" | "stdcall" | "fastcall" | "aapcs" | "win64" | "sysv64"
+ | "system" => {}
+ "rust-intrinsic" => {
+ gate_feature_post!(&self, intrinsics, span, "intrinsics are subject to change");
+ }
+ "platform-intrinsic" => {
+ gate_feature_post!(
+ &self,
+ platform_intrinsics,
+ span,
+ "platform intrinsics are experimental and possibly buggy"
+ );
+ }
+ "vectorcall" => {
+ gate_feature_post!(
+ &self,
+ abi_vectorcall,
+ span,
+ "vectorcall is experimental and subject to change"
+ );
+ }
+ "thiscall" => {
+ gate_feature_post!(
+ &self,
+ abi_thiscall,
+ span,
+ "thiscall is experimental and subject to change"
+ );
+ }
+ "rust-call" => {
+ gate_feature_post!(
+ &self,
+ unboxed_closures,
+ span,
+ "rust-call ABI is subject to change"
+ );
+ }
+ "ptx-kernel" => {
+ gate_feature_post!(
+ &self,
+ abi_ptx,
+ span,
+ "PTX ABIs are experimental and subject to change"
+ );
+ }
+ "unadjusted" => {
+ gate_feature_post!(
+ &self,
+ abi_unadjusted,
+ span,
+ "unadjusted ABI is an implementation detail and perma-unstable"
+ );
+ }
+ "msp430-interrupt" => {
+ gate_feature_post!(
+ &self,
+ abi_msp430_interrupt,
+ span,
+ "msp430-interrupt ABI is experimental and subject to change"
+ );
+ }
+ "x86-interrupt" => {
+ gate_feature_post!(
+ &self,
+ abi_x86_interrupt,
+ span,
+ "x86-interrupt ABI is experimental and subject to change"
+ );
+ }
+ "amdgpu-kernel" => {
+ gate_feature_post!(
+ &self,
+ abi_amdgpu_kernel,
+ span,
+ "amdgpu-kernel ABI is experimental and subject to change"
+ );
+ }
+ "efiapi" => {
+ gate_feature_post!(
+ &self,
+ abi_efiapi,
+ span,
+ "efiapi ABI is experimental and subject to change"
+ );
+ }
+ abi => self
+ .parse_sess
+ .span_diagnostic
+ .delay_span_bug(span, &format!("unrecognized ABI not caught in lowering: {}", abi)),
+ }
+ }
+
+ fn check_extern(&self, ext: ast::Extern) {
+ if let ast::Extern::Explicit(abi) = ext {
+ self.check_abi(abi);
+ }
+ }
+
+ fn maybe_report_invalid_custom_discriminants(&self, variants: &[ast::Variant]) {
+ let has_fields = variants.iter().any(|variant| match variant.data {
+ VariantData::Tuple(..) | VariantData::Struct(..) => true,
+ VariantData::Unit(..) => false,
+ });
+
+ let discriminant_spans = variants
+ .iter()
+ .filter(|variant| match variant.data {
+ VariantData::Tuple(..) | VariantData::Struct(..) => false,
+ VariantData::Unit(..) => true,
+ })
+ .filter_map(|variant| variant.disr_expr.as_ref().map(|c| c.value.span))
+ .collect::<Vec<_>>();
+
+ if !discriminant_spans.is_empty() && has_fields {
+ let mut err = feature_err(
+ self.parse_sess,
+ sym::arbitrary_enum_discriminant,
+ discriminant_spans.clone(),
+ "custom discriminant values are not allowed in enums with tuple or struct variants",
+ );
+ for sp in discriminant_spans {
+ err.span_label(sp, "disallowed custom discriminant");
+ }
+ for variant in variants.iter() {
+ match &variant.data {
+ VariantData::Struct(..) => {
+ err.span_label(variant.span, "struct variant defined here");
+ }
+ VariantData::Tuple(..) => {
+ err.span_label(variant.span, "tuple variant defined here");
+ }
+ VariantData::Unit(..) => {}
+ }
+ }
+ err.emit();
+ }
+ }
+
+ fn check_gat(&self, generics: &ast::Generics, span: Span) {
+ if !generics.params.is_empty() {
+ gate_feature_post!(
+ &self,
+ generic_associated_types,
+ span,
+ "generic associated types are unstable"
+ );
+ }
+ if !generics.where_clause.predicates.is_empty() {
+ gate_feature_post!(
+ &self,
+ generic_associated_types,
+ span,
+ "where clauses on associated types are unstable"
+ );
+ }
+ }
+
+ /// Feature gate `impl Trait` inside `type Alias = $type_expr;`.
+ fn check_impl_trait(&self, ty: &ast::Ty) {
+ struct ImplTraitVisitor<'a> {
+ vis: &'a PostExpansionVisitor<'a>,
+ }
+ impl Visitor<'_> for ImplTraitVisitor<'_> {
+ fn visit_ty(&mut self, ty: &ast::Ty) {
+ if let ast::TyKind::ImplTrait(..) = ty.kind {
+ gate_feature_post!(
+ &self.vis,
+ type_alias_impl_trait,
+ ty.span,
+ "`impl Trait` in type aliases is unstable"
+ );
+ }
+ visit::walk_ty(self, ty);
+ }
+ }
+ ImplTraitVisitor { vis: self }.visit_ty(ty);
+ }
+}
+
+impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
+ fn visit_attribute(&mut self, attr: &ast::Attribute) {
+ let attr_info =
+ attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)).map(|a| **a);
+ // Check feature gates for built-in attributes.
+ if let Some((.., AttributeGate::Gated(_, name, descr, has_feature))) = attr_info {
+ gate_feature_fn!(self, has_feature, attr.span, name, descr);
+ }
+ // Check unstable flavors of the `#[doc]` attribute.
+ if attr.check_name(sym::doc) {
+ for nested_meta in attr.meta_item_list().unwrap_or_default() {
+ macro_rules! gate_doc { ($($name:ident => $feature:ident)*) => {
+ $(if nested_meta.check_name(sym::$name) {
+ let msg = concat!("`#[doc(", stringify!($name), ")]` is experimental");
+ gate_feature_post!(self, $feature, attr.span, msg);
+ })*
+ }}
+
+ gate_doc!(
+ include => external_doc
+ cfg => doc_cfg
+ masked => doc_masked
+ spotlight => doc_spotlight
+ alias => doc_alias
+ keyword => doc_keyword
+ );
+ }
+ }
+ }
+
+ fn visit_name(&mut self, sp: Span, name: ast::Name) {
+ if !name.as_str().is_ascii() {
+ gate_feature_post!(
+ &self,
+ non_ascii_idents,
+ self.parse_sess.source_map().def_span(sp),
+ "non-ascii idents are not fully supported"
+ );
+ }
+ }
+
+ fn visit_item(&mut self, i: &'a ast::Item) {
+ match i.kind {
+ ast::ItemKind::ForeignMod(ref foreign_module) => {
+ if let Some(abi) = foreign_module.abi {
+ self.check_abi(abi);
+ }
+ }
+
+ ast::ItemKind::Fn(..) => {
+ if attr::contains_name(&i.attrs[..], sym::plugin_registrar) {
+ gate_feature_post!(
+ &self,
+ plugin_registrar,
+ i.span,
+ "compiler plugins are experimental and possibly buggy"
+ );
+ }
+ if attr::contains_name(&i.attrs[..], sym::start) {
+ gate_feature_post!(
+ &self,
+ start,
+ i.span,
+ "`#[start]` functions are experimental \
+ and their signature may change \
+ over time"
+ );
+ }
+ if attr::contains_name(&i.attrs[..], sym::main) {
+ gate_feature_post!(
+ &self,
+ main,
+ i.span,
+ "declaration of a non-standard `#[main]` \
+ function may change over time, for now \
+ a top-level `fn main()` is required"
+ );
+ }
+ }
+
+ ast::ItemKind::Struct(..) => {
+ for attr in attr::filter_by_name(&i.attrs[..], sym::repr) {
+ for item in attr.meta_item_list().unwrap_or_else(Vec::new) {
+ if item.check_name(sym::simd) {
+ gate_feature_post!(
+ &self,
+ repr_simd,
+ attr.span,
+ "SIMD types are experimental and possibly buggy"
+ );
+ }
+ }
+ }
+ }
+
+ ast::ItemKind::Enum(ast::EnumDef { ref variants, .. }, ..) => {
+ for variant in variants {
+ match (&variant.data, &variant.disr_expr) {
+ (ast::VariantData::Unit(..), _) => {}
+ (_, Some(disr_expr)) => gate_feature_post!(
+ &self,
+ arbitrary_enum_discriminant,
+ disr_expr.value.span,
+ "discriminants on non-unit variants are experimental"
+ ),
+ _ => {}
+ }
+ }
+
+ let has_feature = self.features.arbitrary_enum_discriminant;
+ if !has_feature && !i.span.allows_unstable(sym::arbitrary_enum_discriminant) {
+ self.maybe_report_invalid_custom_discriminants(&variants);
+ }
+ }
+
+ ast::ItemKind::Impl(_, polarity, defaultness, ..) => {
+ if polarity == ast::ImplPolarity::Negative {
+ gate_feature_post!(
+ &self,
+ optin_builtin_traits,
+ i.span,
+ "negative trait bounds are not yet fully implemented; \
+ use marker types for now"
+ );
+ }
+
+ if let ast::Defaultness::Default = defaultness {
+ gate_feature_post!(&self, specialization, i.span, "specialization is unstable");
+ }
+ }
+
+ ast::ItemKind::Trait(ast::IsAuto::Yes, ..) => {
+ gate_feature_post!(
+ &self,
+ optin_builtin_traits,
+ i.span,
+ "auto traits are experimental and possibly buggy"
+ );
+ }
+
+ ast::ItemKind::TraitAlias(..) => {
+ gate_feature_post!(&self, trait_alias, i.span, "trait aliases are experimental");
+ }
+
+ ast::ItemKind::MacroDef(ast::MacroDef { legacy: false, .. }) => {
+ let msg = "`macro` is experimental";
+ gate_feature_post!(&self, decl_macro, i.span, msg);
+ }
+
+ ast::ItemKind::TyAlias(ref ty, ..) => self.check_impl_trait(&ty),
+
+ _ => {}
+ }
+
+ visit::walk_item(self, i);
+ }
+
+ fn visit_foreign_item(&mut self, i: &'a ast::ForeignItem) {
+ match i.kind {
+ ast::ForeignItemKind::Fn(..) | ast::ForeignItemKind::Static(..) => {
+ let link_name = attr::first_attr_value_str_by_name(&i.attrs, sym::link_name);
+ let links_to_llvm = match link_name {
+ Some(val) => val.as_str().starts_with("llvm."),
+ _ => false,
+ };
+ if links_to_llvm {
+ gate_feature_post!(
+ &self,
+ link_llvm_intrinsics,
+ i.span,
+ "linking to LLVM intrinsics is experimental"
+ );
+ }
+ }
+ ast::ForeignItemKind::Ty => {
+ gate_feature_post!(&self, extern_types, i.span, "extern types are experimental");
+ }
+ ast::ForeignItemKind::Macro(..) => {}
+ }
+
+ visit::walk_foreign_item(self, i)
+ }
+
+ fn visit_ty(&mut self, ty: &'a ast::Ty) {
+ match ty.kind {
+ ast::TyKind::BareFn(ref bare_fn_ty) => {
+ self.check_extern(bare_fn_ty.ext);
+ }
+ ast::TyKind::Never => {
+ gate_feature_post!(&self, never_type, ty.span, "The `!` type is experimental");
+ }
+ _ => {}
+ }
+ visit::walk_ty(self, ty)
+ }
+
+ fn visit_fn_ret_ty(&mut self, ret_ty: &'a ast::FunctionRetTy) {
+ if let ast::FunctionRetTy::Ty(ref output_ty) = *ret_ty {
+ if let ast::TyKind::Never = output_ty.kind {
+ // Do nothing.
+ } else {
+ self.visit_ty(output_ty)
+ }
+ }
+ }
+
+ fn visit_expr(&mut self, e: &'a ast::Expr) {
+ match e.kind {
+ ast::ExprKind::Box(_) => {
+ gate_feature_post!(
+ &self,
+ box_syntax,
+ e.span,
+ "box expression syntax is experimental; you can call `Box::new` instead"
+ );
+ }
+ ast::ExprKind::Type(..) => {
+ // To avoid noise about type ascription in common syntax errors, only emit if it
+ // is the *only* error.
+ if self.parse_sess.span_diagnostic.err_count() == 0 {
+ gate_feature_post!(
+ &self,
+ type_ascription,
+ e.span,
+ "type ascription is experimental"
+ );
+ }
+ }
+ ast::ExprKind::TryBlock(_) => {
+ gate_feature_post!(&self, try_blocks, e.span, "`try` expression is experimental");
+ }
+ ast::ExprKind::Block(_, opt_label) => {
+ if let Some(label) = opt_label {
+ gate_feature_post!(
+ &self,
+ label_break_value,
+ label.ident.span,
+ "labels on blocks are unstable"
+ );
+ }
+ }
+ _ => {}
+ }
+ visit::walk_expr(self, e)
+ }
+
+ fn visit_arm(&mut self, arm: &'a ast::Arm) {
+ visit::walk_arm(self, arm)
+ }
+
+ fn visit_pat(&mut self, pattern: &'a ast::Pat) {
+ match &pattern.kind {
+ PatKind::Slice(pats) => {
+ for pat in &*pats {
+ let span = pat.span;
+ let inner_pat = match &pat.kind {
+ PatKind::Ident(.., Some(pat)) => pat,
+ _ => pat,
+ };
+ if inner_pat.is_rest() {
+ gate_feature_post!(
+ &self,
+ slice_patterns,
+ span,
+ "subslice patterns are unstable"
+ );
+ }
+ }
+ }
+ PatKind::Box(..) => {
+ gate_feature_post!(
+ &self,
+ box_patterns,
+ pattern.span,
+ "box pattern syntax is experimental"
+ );
+ }
+ PatKind::Range(_, _, Spanned { node: RangeEnd::Excluded, .. }) => {
+ gate_feature_post!(
+ &self,
+ exclusive_range_pattern,
+ pattern.span,
+ "exclusive range pattern syntax is experimental"
+ );
+ }
+ _ => {}
+ }
+ visit::walk_pat(self, pattern)
+ }
+
+ fn visit_fn(
+ &mut self,
+ fn_kind: FnKind<'a>,
+ fn_decl: &'a ast::FnDecl,
+ span: Span,
+ _node_id: NodeId,
+ ) {
+ if let Some(header) = fn_kind.header() {
+ // Stability of const fn methods are covered in
+ // `visit_trait_item` and `visit_impl_item` below; this is
+ // because default methods don't pass through this point.
+ self.check_extern(header.ext);
+ }
+
+ if fn_decl.c_variadic() {
+ gate_feature_post!(&self, c_variadic, span, "C-variadic functions are unstable");
+ }
+
+ visit::walk_fn(self, fn_kind, fn_decl, span)
+ }
+
+ fn visit_generic_param(&mut self, param: &'a GenericParam) {
+ match param.kind {
+ GenericParamKind::Const { .. } => gate_feature_post!(
+ &self,
+ const_generics,
+ param.ident.span,
+ "const generics are unstable"
+ ),
+ _ => {}
+ }
+ visit::walk_generic_param(self, param)
+ }
+
+ fn visit_assoc_ty_constraint(&mut self, constraint: &'a AssocTyConstraint) {
+ match constraint.kind {
+ AssocTyConstraintKind::Bound { .. } => gate_feature_post!(
+ &self,
+ associated_type_bounds,
+ constraint.span,
+ "associated type bounds are unstable"
+ ),
+ _ => {}
+ }
+ visit::walk_assoc_ty_constraint(self, constraint)
+ }
+
+ fn visit_trait_item(&mut self, ti: &'a ast::AssocItem) {
+ match ti.kind {
+ ast::AssocItemKind::Fn(ref sig, ref block) => {
+ if block.is_none() {
+ self.check_extern(sig.header.ext);
+ }
+ if sig.header.constness.node == ast::Constness::Const {
+ gate_feature_post!(&self, const_fn, ti.span, "const fn is unstable");
+ }
+ }
+ ast::AssocItemKind::TyAlias(_, ref default) => {
+ if let Some(_) = default {
+ gate_feature_post!(
+ &self,
+ associated_type_defaults,
+ ti.span,
+ "associated type defaults are unstable"
+ );
+ }
+ }
+ _ => {}
+ }
+ visit::walk_trait_item(self, ti)
+ }
+
+ fn visit_assoc_item(&mut self, ii: &'a ast::AssocItem) {
+ if ii.defaultness == ast::Defaultness::Default {
+ gate_feature_post!(&self, specialization, ii.span, "specialization is unstable");
+ }
+
+ match ii.kind {
+ ast::AssocItemKind::Fn(ref sig, _) => {
+ if sig.decl.c_variadic() {
+ gate_feature_post!(
+ &self,
+ c_variadic,
+ ii.span,
+ "C-variadic functions are unstable"
+ );
+ }
+ }
+ ast::AssocItemKind::TyAlias(_, ref ty) => {
+ if let Some(ty) = ty {
+ self.check_impl_trait(ty);
+ }
+ self.check_gat(&ii.generics, ii.span);
+ }
+ _ => {}
+ }
+ visit::walk_assoc_item(self, ii)
+ }
+
+ fn visit_vis(&mut self, vis: &'a ast::Visibility) {
+ if let ast::VisibilityKind::Crate(ast::CrateSugar::JustCrate) = vis.node {
+ gate_feature_post!(
+ &self,
+ crate_visibility_modifier,
+ vis.span,
+ "`crate` visibility modifier is experimental"
+ );
+ }
+ visit::walk_vis(self, vis)
+ }
+}
+
+pub fn check_crate(
+ krate: &ast::Crate,
+ parse_sess: &ParseSess,
+ features: &Features,
+ unstable: UnstableFeatures,
+) {
+ maybe_stage_features(&parse_sess.span_diagnostic, krate, unstable);
+ let mut visitor = PostExpansionVisitor { parse_sess, features };
+
+ let spans = parse_sess.gated_spans.spans.borrow();
+ macro_rules! gate_all {
+ ($gate:ident, $msg:literal) => {
+ for span in spans.get(&sym::$gate).unwrap_or(&vec![]) {
+ gate_feature_post!(&visitor, $gate, *span, $msg);
+ }
+ };
+ }
+ gate_all!(let_chains, "`let` expressions in this position are experimental");
+ gate_all!(async_closure, "async closures are unstable");
+ gate_all!(generators, "yield syntax is experimental");
+ gate_all!(or_patterns, "or-patterns syntax is experimental");
+ gate_all!(const_extern_fn, "`const extern fn` definitions are unstable");
+ gate_all!(raw_ref_op, "raw address of syntax is experimental");
+ gate_all!(const_trait_bound_opt_out, "`?const` on trait bounds is experimental");
+ gate_all!(const_trait_impl, "const trait impls are experimental");
+ gate_all!(half_open_range_patterns, "half-open range patterns are unstable");
+
+ // All uses of `gate_all!` below this point were added in #65742,
+ // and subsequently disabled (with the non-early gating readded).
+ macro_rules! gate_all {
+ ($gate:ident, $msg:literal) => {
+ // FIXME(eddyb) do something more useful than always
+ // disabling these uses of early feature-gatings.
+ if false {
+ for span in spans.get(&sym::$gate).unwrap_or(&vec![]) {
+ gate_feature_post!(&visitor, $gate, *span, $msg);
+ }
+ }
+ };
+ }
+
+ gate_all!(trait_alias, "trait aliases are experimental");
+ gate_all!(associated_type_bounds, "associated type bounds are unstable");
+ gate_all!(crate_visibility_modifier, "`crate` visibility modifier is experimental");
+ gate_all!(const_generics, "const generics are unstable");
+ gate_all!(decl_macro, "`macro` is experimental");
+ gate_all!(box_patterns, "box pattern syntax is experimental");
+ gate_all!(exclusive_range_pattern, "exclusive range pattern syntax is experimental");
+ gate_all!(try_blocks, "`try` blocks are unstable");
+ gate_all!(label_break_value, "labels on blocks are unstable");
+ gate_all!(box_syntax, "box expression syntax is experimental; you can call `Box::new` instead");
+ // To avoid noise about type ascription in common syntax errors,
+ // only emit if it is the *only* error. (Also check it last.)
+ if parse_sess.span_diagnostic.err_count() == 0 {
+ gate_all!(type_ascription, "type ascription is experimental");
+ }
+
+ visit::walk_crate(&mut visitor, krate);
+}
+
+fn maybe_stage_features(span_handler: &Handler, krate: &ast::Crate, unstable: UnstableFeatures) {
+ if !unstable.is_nightly_build() {
+ for attr in krate.attrs.iter().filter(|attr| attr.check_name(sym::feature)) {
+ struct_span_err!(
+ span_handler,
+ attr.span,
+ E0554,
+ "`#![feature]` may not be used on the {} release channel",
+ option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)")
+ )
+ .emit();
+ }
+ }
+}
--- /dev/null
+//! The `rustc_ast_passes` crate contains passes which validate the AST in `syntax`
+//! parsed by `rustc_parse` and then lowered, after the passes in this crate,
+//! by `rustc_ast_lowering`.
+
+#![feature(slice_patterns)]
+
+pub mod ast_validation;
+pub mod feature_gate;
+pub mod show_span;
--- /dev/null
+//! Span debugger
+//!
+//! This module shows spans for all expressions in the crate
+//! to help with compiler debugging.
+
+use std::str::FromStr;
+
+use syntax::ast;
+use syntax::visit;
+use syntax::visit::Visitor;
+
+enum Mode {
+ Expression,
+ Pattern,
+ Type,
+}
+
+impl FromStr for Mode {
+ type Err = ();
+ fn from_str(s: &str) -> Result<Mode, ()> {
+ let mode = match s {
+ "expr" => Mode::Expression,
+ "pat" => Mode::Pattern,
+ "ty" => Mode::Type,
+ _ => return Err(()),
+ };
+ Ok(mode)
+ }
+}
+
+struct ShowSpanVisitor<'a> {
+ span_diagnostic: &'a rustc_errors::Handler,
+ mode: Mode,
+}
+
+impl<'a> Visitor<'a> for ShowSpanVisitor<'a> {
+ fn visit_expr(&mut self, e: &'a ast::Expr) {
+ if let Mode::Expression = self.mode {
+ self.span_diagnostic.span_warn(e.span, "expression");
+ }
+ visit::walk_expr(self, e);
+ }
+
+ fn visit_pat(&mut self, p: &'a ast::Pat) {
+ if let Mode::Pattern = self.mode {
+ self.span_diagnostic.span_warn(p.span, "pattern");
+ }
+ visit::walk_pat(self, p);
+ }
+
+ fn visit_ty(&mut self, t: &'a ast::Ty) {
+ if let Mode::Type = self.mode {
+ self.span_diagnostic.span_warn(t.span, "type");
+ }
+ visit::walk_ty(self, t);
+ }
+
+ fn visit_mac(&mut self, mac: &'a ast::Mac) {
+ visit::walk_mac(self, mac);
+ }
+}
+
+pub fn run(span_diagnostic: &rustc_errors::Handler, mode: &str, krate: &ast::Crate) {
+ let mode = match mode.parse().ok() {
+ Some(mode) => mode,
+ None => return,
+ };
+ let mut v = ShowSpanVisitor { span_diagnostic, mode };
+ visit::walk_crate(&mut v, krate);
+}
doctest = false
[dependencies]
-errors = { path = "../librustc_errors", package = "rustc_errors" }
fmt_macros = { path = "../libfmt_macros" }
log = "0.4"
rustc_data_structures = { path = "../librustc_data_structures" }
+rustc_errors = { path = "../librustc_errors" }
rustc_feature = { path = "../librustc_feature" }
rustc_parse = { path = "../librustc_parse" }
rustc_target = { path = "../librustc_target" }
+rustc_session = { path = "../librustc_session" }
smallvec = { version = "1.0", features = ["union", "may_dangle"] }
syntax = { path = "../libsyntax" }
rustc_expand = { path = "../librustc_expand" }
//
use State::*;
-use errors::{DiagnosticBuilder, PResult};
+use rustc_errors::{struct_span_err, DiagnosticBuilder, PResult};
use rustc_expand::base::*;
use rustc_parse::parser::Parser;
use rustc_span::symbol::{kw, sym, Symbol};
use syntax::ptr::P;
use syntax::token::{self, Token};
use syntax::tokenstream::{self, TokenStream};
-use syntax::{span_err, struct_span_err};
use rustc_error_codes::*;
Some('=') => None,
Some('+') => Some(Symbol::intern(&format!("={}", ch.as_str()))),
_ => {
- span_err!(
- cx,
+ struct_span_err!(
+ cx.parse_sess.span_diagnostic,
span,
E0661,
"output operand constraint lacks '=' or '+'"
- );
+ )
+ .emit();
None
}
};
let constraint = parse_asm_str(&mut p)?;
if constraint.as_str().starts_with("=") {
- span_err!(cx, p.prev_span, E0662, "input operand constraint contains '='");
+ struct_span_err!(
+ cx.parse_sess.span_diagnostic,
+ p.prev_span,
+ E0662,
+ "input operand constraint contains '='"
+ )
+ .emit();
} else if constraint.as_str().starts_with("+") {
- span_err!(cx, p.prev_span, E0663, "input operand constraint contains '+'");
+ struct_span_err!(
+ cx.parse_sess.span_diagnostic,
+ p.prev_span,
+ E0663,
+ "input operand constraint contains '+'"
+ )
+ .emit();
}
p.expect(&token::OpenDelim(token::Paren))?;
if OPTIONS.iter().any(|&opt| s == opt) {
cx.span_warn(p.prev_span, "expected a clobber, found an option");
} else if s.as_str().starts_with("{") || s.as_str().ends_with("}") {
- span_err!(
- cx,
+ struct_span_err!(
+ cx.parse_sess.span_diagnostic,
p.prev_span,
E0664,
"clobber should not be surrounded by braces"
- );
+ )
+ .emit();
}
clobs.push(s);
-use errors::{Applicability, DiagnosticBuilder};
+use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_expand::base::*;
use rustc_parse::parser::Parser;
let custom_message =
if let token::Literal(token::Lit { kind: token::Str, .. }) = parser.token.kind {
let mut err = cx.struct_span_warn(parser.token.span, "unexpected string literal");
- let comma_span = cx.source_map().next_point(parser.prev_span);
+ let comma_span = parser.prev_span.shrink_to_hi();
err.span_suggestion_short(
comma_span,
"try adding a comma",
-/// The compiler code necessary to support the cfg! extension, which expands to
-/// a literal `true` or `false` based on whether the given cfg matches the
-/// current compilation environment.
-use errors::DiagnosticBuilder;
+//! The compiler code necessary to support the cfg! extension, which expands to
+//! a literal `true` or `false` based on whether the given cfg matches the
+//! current compilation environment.
+use rustc_errors::DiagnosticBuilder;
use rustc_expand::base::{self, *};
use rustc_span::Span;
use syntax::ast;
use crate::deriving::generic::*;
use crate::deriving::path_std;
+use rustc_errors::struct_span_err;
use rustc_expand::base::{Annotatable, DummyResult, ExtCtxt};
use rustc_span::symbol::{kw, sym};
use rustc_span::Span;
use syntax::ast::{Expr, MetaItem};
use syntax::ptr::P;
-use syntax::span_err;
use rustc_error_codes::*;
}
},
StaticEnum(..) => {
- span_err!(cx, trait_span, E0665, "`Default` cannot be derived for enums, only structs");
+ struct_span_err!(
+ cx.parse_sess.span_diagnostic,
+ trait_span,
+ E0665,
+ "`Default` cannot be derived for enums, only structs"
+ )
+ .emit();
// let compilation continue
DummyResult::raw_expr(trait_span, true)
}
use fmt_macros as parse;
-use errors::pluralize;
-use errors::Applicability;
-use errors::DiagnosticBuilder;
-
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::{pluralize, Applicability, DiagnosticBuilder};
use rustc_expand::base::{self, *};
use rustc_span::symbol::{sym, Symbol};
use rustc_span::{MultiSpan, Span};
use syntax::token;
use syntax::tokenstream::TokenStream;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use std::borrow::Cow;
use std::collections::hash_map::Entry;
-/// Module-level assembly support.
-///
-/// The macro defined here allows you to specify "top-level",
-/// "file-scoped", or "module-level" assembly. These synonyms
-/// all correspond to LLVM's module-level inline assembly instruction.
-///
-/// For example, `global_asm!("some assembly here")` codegens to
-/// LLVM's `module asm "some assembly here"`. All of LLVM's caveats
-/// therefore apply.
-use errors::DiagnosticBuilder;
+//! Module-level assembly support.
+//!
+//! The macro defined here allows you to specify "top-level",
+//! "file-scoped", or "module-level" assembly. These synonyms
+//! all correspond to LLVM's module-level inline assembly instruction.
+//!
+//! For example, `global_asm!("some assembly here")` codegens to
+//! LLVM's `module asm "some assembly here"`. All of LLVM's caveats
+//! therefore apply.
+use rustc_errors::DiagnosticBuilder;
use rustc_expand::base::{self, *};
use rustc_span::source_map::respan;
use rustc_span::Span;
struct CollectProcMacros<'a> {
macros: Vec<ProcMacro>,
in_root: bool,
- handler: &'a errors::Handler,
+ handler: &'a rustc_errors::Handler,
is_proc_macro_crate: bool,
is_test_crate: bool,
}
has_proc_macro_decls: bool,
is_test_crate: bool,
num_crate_types: usize,
- handler: &errors::Handler,
+ handler: &rustc_errors::Handler,
) -> ast::Crate {
let ecfg = ExpansionConfig::default("proc_macro".to_string());
let mut cx = ExtCtxt::new(sess, ecfg, resolver);
use rustc_expand::base::{self, *};
use rustc_expand::panictry;
use rustc_parse::{self, new_sub_parser_from_file, parser::Parser, DirectoryOwnership};
+use rustc_session::lint::builtin::INCOMPLETE_INCLUDE;
use rustc_span::symbol::Symbol;
+use rustc_span::{self, Pos, Span};
use syntax::ast;
-use syntax::early_buffered_lints::INCOMPLETE_INCLUDE;
use syntax::print::pprust;
use syntax::ptr::P;
use syntax::token;
use syntax::tokenstream::TokenStream;
-use rustc_span::{self, Pos, Span};
use smallvec::SmallVec;
use rustc_data_structures::sync::Lrc;
resolver: &mut dyn Resolver,
should_test: bool,
krate: &mut ast::Crate,
- span_diagnostic: &errors::Handler,
+ span_diagnostic: &rustc_errors::Handler,
features: &Features,
panic_strategy: PanicStrategy,
platform_panic_strategy: PanicStrategy,
attr::contains_name(&i.attrs, sym::rustc_test_marker)
}
-fn get_test_runner(sd: &errors::Handler, krate: &ast::Crate) -> Option<ast::Path> {
+fn get_test_runner(sd: &rustc_errors::Handler, krate: &ast::Crate) -> Option<ast::Path> {
let test_attr = attr::find_by_name(&krate.attrs, sym::test_runner)?;
test_attr.meta_item_list().map(|meta_list| {
if meta_list.len() != 1 {
rustc_target = { path = "../librustc_target" }
smallvec = { version = "1.0", features = ["union", "may_dangle"] }
syntax = { path = "../libsyntax" }
-rustc_expand = { path = "../librustc_expand" }
rustc_span = { path = "../librustc_span" }
info!("adding bytecode {}", name);
let bc_encoded = data.data();
- let (bc, id) = cgcx.prof.generic_pass(&format!("decode {}", name)).run(|| {
- match DecodedBytecode::new(bc_encoded) {
+ let (bc, id) = cgcx
+ .prof
+ .extra_verbose_generic_activity(&format!("decode {}", name))
+ .run(|| match DecodedBytecode::new(bc_encoded) {
Ok(b) => Ok((b.bytecode(), b.identifier().to_string())),
Err(e) => Err(diag_handler.fatal(&e)),
- }
- })?;
+ })?;
let bc = SerializedModule::FromRlib(bc);
upstream_modules.push((bc, CString::new(id).unwrap()));
}
// save and persist everything with the original module.
let mut linker = Linker::new(llmod);
for (bc_decoded, name) in serialized_modules {
+ let _timer = cgcx.prof.generic_activity("LLVM_fat_lto_link_module");
info!("linking {:?}", name);
- cgcx.prof.generic_pass(&format!("ll link {:?}", name)).run(|| {
+ cgcx.prof.extra_verbose_generic_activity(&format!("ll link {:?}", name)).run(|| {
let data = bc_decoded.data();
linker.add(&data).map_err(|()| {
let msg = format!("failed to load bc of {:?}", name);
}
cgcx.prof
- .generic_pass("LTO passes")
+ .extra_verbose_generic_activity("LTO_passes")
.run(|| llvm::LLVMRunPassManager(pm, module.module_llvm.llmod()));
llvm::LLVMDisposePassManager(pm);
// Finally, run the actual optimization passes
{
+ let _timer = cgcx.prof.generic_activity("LLVM_module_optimize_function_passes");
let desc = &format!("llvm function passes [{}]", module_name.unwrap());
- let _timer = if config.time_module { Some(cgcx.prof.generic_pass(desc)) } else { None };
+ let _timer = if config.time_module {
+ Some(cgcx.prof.extra_verbose_generic_activity(desc))
+ } else {
+ None
+ };
llvm::LLVMRustRunFunctionPassManager(fpm, llmod);
}
{
+ let _timer = cgcx.prof.generic_activity("LLVM_module_optimize_module_passes");
let desc = &format!("llvm module passes [{}]", module_name.unwrap());
- let _timer = if config.time_module { Some(cgcx.prof.generic_pass(desc)) } else { None };
+ let _timer = if config.time_module {
+ Some(cgcx.prof.extra_verbose_generic_activity(desc))
+ } else {
+ None
+ };
llvm::LLVMRunPassManager(mpm, llmod);
}
{
let desc = &format!("codegen passes [{}]", module_name.unwrap());
- let _timer = if config.time_module { Some(cgcx.prof.generic_pass(desc)) } else { None };
+ let _timer = if config.time_module {
+ Some(cgcx.prof.extra_verbose_generic_activity(desc))
+ } else {
+ None
+ };
if config.emit_ir {
let _timer = cgcx.prof.generic_activity("LLVM_module_codegen_emit_ir");
//! but one `llvm::Type` corresponds to many `Ty`s; for instance, `tup(int, int,
//! int)` and `rec(x=int, y=int, z=int)` will have the same `llvm::Type`.
-use super::{LlvmCodegenBackend, ModuleLlvm};
+use super::ModuleLlvm;
use crate::builder::Builder;
use crate::common;
use rustc::mir::mono::{Linkage, Visibility};
use rustc::session::config::DebugInfo;
use rustc::ty::TyCtxt;
-use rustc_codegen_ssa::back::write::submit_codegened_module_to_llvm;
use rustc_codegen_ssa::base::maybe_create_entry_wrapper;
use rustc_codegen_ssa::mono_item::MonoItemExt;
use rustc_codegen_ssa::traits::*;
pub fn compile_codegen_unit(
tcx: TyCtxt<'tcx>,
cgu_name: Symbol,
- tx_to_llvm_workers: &std::sync::mpsc::Sender<Box<dyn std::any::Any + Send>>,
-) {
+) -> (ModuleCodegen<ModuleLlvm>, u64) {
let prof_timer = tcx.prof.generic_activity("codegen_module");
let start_time = Instant::now();
// the time we needed for codegenning it.
let cost = time_to_codegen.as_secs() * 1_000_000_000 + time_to_codegen.subsec_nanos() as u64;
- submit_codegened_module_to_llvm(&LlvmCodegenBackend(()), tx_to_llvm_workers, module, cost);
-
fn module_codegen(tcx: TyCtxt<'_>, cgu_name: Symbol) -> ModuleCodegen<ModuleLlvm> {
let cgu = tcx.codegen_unit(cgu_name);
// Instantiate monomorphizations without filling out definitions yet...
kind: ModuleKind::Regular,
}
}
+
+ (module, cost)
}
pub fn set_link_section(llval: &Value, attrs: &CodegenFnAttrs) {
data_layout.replace("-Fi8-", "-")
}
+fn strip_x86_address_spaces(data_layout: String) -> String {
+ data_layout.replace("-p270:32:32-p271:32:32-p272:64:64-", "-")
+}
+
pub unsafe fn create_module(
tcx: TyCtxt<'_>,
llcx: &'ll llvm::Context,
if llvm_util::get_major_version() < 9 {
target_data_layout = strip_function_ptr_alignment(target_data_layout);
}
+ if llvm_util::get_major_version() < 10 {
+ if sess.target.target.arch == "x86" || sess.target.target.arch == "x86_64" {
+ target_data_layout = strip_x86_address_spaces(target_data_layout);
+ }
+ }
// Ensure the data-layout values hardcoded remain the defaults.
if sess.target.target.options.is_builtin {
let generator_layout = body.generator_layout.as_ref().unwrap();
let mut generator_saved_local_names = IndexVec::from_elem(None, &generator_layout.field_tys);
- let state_arg = mir::PlaceBase::Local(mir::Local::new(1));
+ let state_arg = mir::Local::new(1);
for var in &body.var_debug_info {
- if var.place.base != state_arg {
+ if var.place.local != state_arg {
continue;
}
match var.place.projection[..] {
#![feature(link_args)]
#![feature(static_nobundle)]
#![feature(trusted_len)]
+#![recursion_limit = "256"]
use back::write::{create_informational_target_machine, create_target_machine};
use rustc_span::symbol::Symbol;
&self,
tcx: TyCtxt<'_>,
cgu_name: Symbol,
- tx: &std::sync::mpsc::Sender<Box<dyn Any + Send>>,
- ) {
- base::compile_codegen_unit(tcx, cgu_name, tx);
+ ) -> (ModuleCodegen<ModuleLlvm>, u64) {
+ base::compile_codegen_unit(tcx, cgu_name)
}
fn target_machine_factory(
&self,
rustc_codegen_ssa::back::write::dump_incremental_data(&codegen_results);
}
- sess.time("serialize work products", move || {
+ sess.time("serialize_work_products", move || {
rustc_incremental::save_work_product_index(sess, &dep_graph, work_products)
});
// Run the linker on any artifacts that resulted from the LLVM run.
// This should produce either a finished executable or library.
- sess.time("linking", || {
+ sess.time("link_crate", || {
use crate::back::archive::LlvmArchiveBuilder;
use rustc_codegen_ssa::back::link::link_binary;
}
unsafe fn configure_llvm(sess: &Session) {
- let n_args = sess.opts.cg.llvm_args.len();
+ let n_args = sess.opts.cg.llvm_args.len() + sess.target.target.options.llvm_args.len();
let mut llvm_c_strs = Vec::with_capacity(n_args + 1);
let mut llvm_args = Vec::with_capacity(n_args + 1);
full_arg.trim().split(|c: char| c == '=' || c.is_whitespace()).next().unwrap_or("")
}
- let user_specified_args: FxHashSet<_> = sess
- .opts
- .cg
- .llvm_args
- .iter()
- .map(|s| llvm_arg_to_arg_name(s))
- .filter(|s| s.len() > 0)
- .collect();
+ let cg_opts = sess.opts.cg.llvm_args.iter();
+ let tg_opts = sess.target.target.options.llvm_args.iter();
+
+ let user_specified_args: FxHashSet<_> =
+ cg_opts.chain(tg_opts).map(|s| llvm_arg_to_arg_name(s)).filter(|s| s.len() > 0).collect();
{
// This adds the given argument to LLVM. Unless `force` is true
crate_name: &str,
target_cpu: &str,
) {
+ let _timer = sess.timer("link_binary");
let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata);
for &crate_type in sess.crate_types.borrow().iter() {
// Ignore executable crates if we have -Z no-codegen, as they will error.
);
}
- for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) {
- check_file_is_writeable(obj, sess);
- }
+ sess.time("link_binary_check_files_are_writeable", || {
+ for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) {
+ check_file_is_writeable(obj, sess);
+ }
+ });
let tmpdir = TempFileBuilder::new()
.prefix("rustc")
let out_filename = out_filename(sess, crate_type, outputs, crate_name);
match crate_type {
config::CrateType::Rlib => {
+ let _timer = sess.timer("link_rlib");
link_rlib::<B>(
sess,
codegen_results,
}
// Remove the temporary object file and metadata if we aren't saving temps
- if !sess.opts.cg.save_temps {
- if sess.opts.output_types.should_codegen() && !preserve_objects_for_their_debuginfo(sess) {
- for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) {
- remove(sess, obj);
+ sess.time("link_binary_remove_temps", || {
+ if !sess.opts.cg.save_temps {
+ if sess.opts.output_types.should_codegen()
+ && !preserve_objects_for_their_debuginfo(sess)
+ {
+ for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) {
+ remove(sess, obj);
+ }
}
- }
- for obj in codegen_results.modules.iter().filter_map(|m| m.bytecode_compressed.as_ref()) {
- remove(sess, obj);
- }
- if let Some(ref metadata_module) = codegen_results.metadata_module {
- if let Some(ref obj) = metadata_module.object {
+ for obj in codegen_results.modules.iter().filter_map(|m| m.bytecode_compressed.as_ref())
+ {
remove(sess, obj);
}
- }
- if let Some(ref allocator_module) = codegen_results.allocator_module {
- if let Some(ref obj) = allocator_module.object {
- remove(sess, obj);
+ if let Some(ref metadata_module) = codegen_results.metadata_module {
+ if let Some(ref obj) = metadata_module.object {
+ remove(sess, obj);
+ }
}
- if let Some(ref bc) = allocator_module.bytecode_compressed {
- remove(sess, bc);
+ if let Some(ref allocator_module) = codegen_results.allocator_module {
+ if let Some(ref obj) = allocator_module.object {
+ remove(sess, obj);
+ }
+ if let Some(ref bc) = allocator_module.bytecode_compressed {
+ remove(sess, bc);
+ }
}
}
- }
+ });
}
// The third parameter is for env vars, used on windows to set up the
{
let mut linker = codegen_results.linker_info.to_linker(cmd, &sess, flavor, target_cpu);
+ link_sanitizer_runtime(sess, crate_type, &mut *linker);
link_args::<B>(
&mut *linker,
flavor,
let mut i = 0;
loop {
i += 1;
- prog = sess.time("running linker", || exec_linker(sess, &mut cmd, out_filename, tmpdir));
+ prog = sess.time("run_linker", || exec_linker(sess, &mut cmd, out_filename, tmpdir));
let output = match prog {
Ok(ref output) => output,
Err(_) => break,
}
}
+fn link_sanitizer_runtime(sess: &Session, crate_type: config::CrateType, linker: &mut dyn Linker) {
+ let sanitizer = match &sess.opts.debugging_opts.sanitizer {
+ Some(s) => s,
+ None => return,
+ };
+
+ if crate_type != config::CrateType::Executable {
+ return;
+ }
+
+ let name = match sanitizer {
+ Sanitizer::Address => "asan",
+ Sanitizer::Leak => "lsan",
+ Sanitizer::Memory => "msan",
+ Sanitizer::Thread => "tsan",
+ };
+
+ let default_sysroot = filesearch::get_or_default_sysroot();
+ let default_tlib =
+ filesearch::make_target_lib_path(&default_sysroot, sess.opts.target_triple.triple());
+
+ match sess.opts.target_triple.triple() {
+ "x86_64-apple-darwin" => {
+ // On Apple platforms, the sanitizer is always built as a dylib, and
+ // LLVM will link to `@rpath/*.dylib`, so we need to specify an
+ // rpath to the library as well (the rpath should be absolute, see
+ // PR #41352 for details).
+ let libname = format!("rustc_rt.{}", name);
+ let rpath = default_tlib.to_str().expect("non-utf8 component in path");
+ linker.args(&["-Wl,-rpath".into(), "-Xlinker".into(), rpath.into()]);
+ linker.link_dylib(Symbol::intern(&libname));
+ }
+ "x86_64-unknown-linux-gnu" => {
+ let filename = format!("librustc_rt.{}.a", name);
+ let path = default_tlib.join(&filename);
+ linker.link_whole_rlib(&path);
+ }
+ _ => {}
+ }
+}
+
/// Returns a boolean indicating whether the specified crate should be ignored
/// during LTO.
///
_ if codegen_results.crate_info.profiler_runtime == Some(cnum) => {
add_static_crate::<B>(cmd, sess, codegen_results, tmpdir, crate_type, cnum);
}
- _ if codegen_results.crate_info.sanitizer_runtime == Some(cnum)
- && crate_type == config::CrateType::Executable =>
- {
- // Link the sanitizer runtimes only if we are actually producing an executable
- link_sanitizer_runtime::<B>(cmd, sess, codegen_results, tmpdir, cnum);
- }
// compiler-builtins are always placed last to ensure that they're
// linked correctly.
_ if codegen_results.crate_info.compiler_builtins == Some(cnum) => {
}
}
- // We must link the sanitizer runtime using -Wl,--whole-archive but since
- // it's packed in a .rlib, it contains stuff that are not objects that will
- // make the linker error. So we must remove those bits from the .rlib before
- // linking it.
- fn link_sanitizer_runtime<'a, B: ArchiveBuilder<'a>>(
- cmd: &mut dyn Linker,
- sess: &'a Session,
- codegen_results: &CodegenResults,
- tmpdir: &Path,
- cnum: CrateNum,
- ) {
- let src = &codegen_results.crate_info.used_crate_source[&cnum];
- let cratepath = &src.rlib.as_ref().unwrap().0;
-
- if sess.target.target.options.is_like_osx {
- // On Apple platforms, the sanitizer is always built as a dylib, and
- // LLVM will link to `@rpath/*.dylib`, so we need to specify an
- // rpath to the library as well (the rpath should be absolute, see
- // PR #41352 for details).
- //
- // FIXME: Remove this logic into librustc_*san once Cargo supports it
- let rpath = cratepath.parent().unwrap();
- let rpath = rpath.to_str().expect("non-utf8 component in path");
- cmd.args(&["-Wl,-rpath".into(), "-Xlinker".into(), rpath.into()]);
- }
-
- let dst = tmpdir.join(cratepath.file_name().unwrap());
- let mut archive = <B as ArchiveBuilder>::new(sess, &dst, Some(cratepath));
- archive.update_symbols();
-
- for f in archive.src_files() {
- if f.ends_with(RLIB_BYTECODE_EXTENSION) || f == METADATA_FILENAME {
- archive.remove_file(&f);
- }
- }
-
- archive.build();
-
- cmd.link_whole_rlib(&dst);
- }
-
// Adds the static "rlib" versions of all crates to the command line.
// There's a bit of magic which happens here specifically related to LTO and
// dynamic libraries. Specifically:
let name = cratepath.file_name().unwrap().to_str().unwrap();
let name = &name[3..name.len() - 5]; // chop off lib/.rlib
- sess.prof.generic_pass(&format!("altering {}.rlib", name)).run(|| {
+ sess.prof.extra_verbose_generic_activity(&format!("altering {}.rlib", name)).run(|| {
let mut archive = <B as ArchiveBuilder>::new(sess, &dst, Some(cratepath));
archive.update_symbols();
if is_extern && !std_internal {
let target = &tcx.sess.target.target.llvm_target;
// WebAssembly cannot export data symbols, so reduce their export level
- if target.contains("wasm32") || target.contains("emscripten") {
+ if target.contains("emscripten") {
if let Some(Node::Item(&hir::Item { kind: hir::ItemKind::Static(..), .. })) =
tcx.hir().get_if_local(sym_def_id)
{
return work_products;
}
+ let _timer = sess.timer("incr_comp_copy_cgu_workproducts");
+
for module in compiled_modules.modules.iter().filter(|m| m.kind == ModuleKind::Regular) {
let mut files = vec![];
worker_id: usize,
},
Done {
- result: Result<CompiledModule, ()>,
+ result: Result<CompiledModule, Option<WorkerFatalError>>,
worker_id: usize,
},
CodegenDone {
main_thread_worker_state = MainThreadWorkerState::Idle;
}
// If the thread failed that means it panicked, so we abort immediately.
- Message::Done { result: Err(()), worker_id: _ } => {
+ Message::Done { result: Err(None), worker_id: _ } => {
bug!("worker thread panicked");
}
+ Message::Done { result: Err(Some(WorkerFatalError)), worker_id: _ } => {
+ return Err(());
+ }
Message::CodegenItem => bug!("the coordinator should not receive codegen requests"),
}
}
llvm_start_time: &mut Option<VerboseTimingGuard<'a>>,
) {
if config.time_module && llvm_start_time.is_none() {
- *llvm_start_time = Some(prof.generic_pass("LLVM passes"));
+ *llvm_start_time = Some(prof.extra_verbose_generic_activity("LLVM_passes"));
}
}
}
pub const CODEGEN_WORKER_ID: usize = ::std::usize::MAX;
+/// `FatalError` is explicitly not `Send`.
+#[must_use]
+pub struct WorkerFatalError;
+
fn spawn_work<B: ExtraBackendMethods>(cgcx: CodegenContext<B>, work: WorkItem<B>) {
thread::spawn(move || {
// Set up a destructor which will fire off a message that we're done as
// we exit.
struct Bomb<B: ExtraBackendMethods> {
coordinator_send: Sender<Box<dyn Any + Send>>,
- result: Option<WorkItemResult<B>>,
+ result: Option<Result<WorkItemResult<B>, FatalError>>,
worker_id: usize,
}
impl<B: ExtraBackendMethods> Drop for Bomb<B> {
fn drop(&mut self) {
let worker_id = self.worker_id;
let msg = match self.result.take() {
- Some(WorkItemResult::Compiled(m)) => {
+ Some(Ok(WorkItemResult::Compiled(m))) => {
Message::Done::<B> { result: Ok(m), worker_id }
}
- Some(WorkItemResult::NeedsFatLTO(m)) => {
+ Some(Ok(WorkItemResult::NeedsFatLTO(m))) => {
Message::NeedsFatLTO::<B> { result: m, worker_id }
}
- Some(WorkItemResult::NeedsThinLTO(name, thin_buffer)) => {
+ Some(Ok(WorkItemResult::NeedsThinLTO(name, thin_buffer))) => {
Message::NeedsThinLTO::<B> { name, thin_buffer, worker_id }
}
- None => Message::Done::<B> { result: Err(()), worker_id },
+ Some(Err(FatalError)) => {
+ Message::Done::<B> { result: Err(Some(WorkerFatalError)), worker_id }
+ }
+ None => Message::Done::<B> { result: Err(None), worker_id },
};
drop(self.coordinator_send.send(Box::new(msg)));
}
// surface that there was an error in this worker.
bomb.result = {
let _prof_timer = cgcx.prof.generic_activity(work.profiling_event_id());
- execute_work_item(&cgcx, work).ok()
+ Some(execute_work_item(&cgcx, work))
};
});
}
d.code(code);
}
handler.emit_diagnostic(&d);
- handler.abort_if_errors_and_should_abort();
}
Ok(SharedEmitterMessage::InlineAsmError(cookie, msg)) => {
sess.span_err(ExpnId::from_u32(cookie).expn_data().call_site, &msg)
impl<B: ExtraBackendMethods> OngoingCodegen<B> {
pub fn join(self, sess: &Session) -> (CodegenResults, FxHashMap<WorkProductId, WorkProduct>) {
+ let _timer = sess.timer("finish_ongoing_codegen");
+
self.shared_emitter_main.check(sess, true);
- let compiled_modules = match self.future.join() {
+ let future = self.future;
+ let compiled_modules = sess.time("join_worker_thread", || match future.join() {
Ok(Ok(compiled_modules)) => compiled_modules,
Ok(Err(())) => {
sess.abort_if_errors();
Err(_) => {
bug!("panic during codegen/LLVM phase");
}
- };
+ });
sess.cgu_reuse_tracker.check_expected_reuse(sess.diagnostic());
//! int)` and `rec(x=int, y=int, z=int)` will have the same `llvm::Type`.
use crate::back::write::{
- start_async_codegen, submit_post_lto_module_to_llvm, submit_pre_lto_module_to_llvm,
- OngoingCodegen,
+ start_async_codegen, submit_codegened_module_to_llvm, submit_post_lto_module_to_llvm,
+ submit_pre_lto_module_to_llvm, OngoingCodegen,
};
use crate::common::{IntPredicate, RealPredicate, TypeKind};
use crate::meth;
use rustc_codegen_utils::{check_for_rustc_errors_attr, symbol_names_test};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::profiling::print_time_passes_entry;
+use rustc_data_structures::sync::{par_iter, Lock, ParallelIterator};
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
use rustc_index::vec::Idx;
}
op => bug!(
"comparison_op_to_icmp_predicate: expected comparison operator, \
- found {:?}",
+ found {:?}",
op
),
}
op => {
bug!(
"comparison_op_to_fcmp_predicate: expected comparison operator, \
- found {:?}",
+ found {:?}",
op
);
}
ongoing_codegen.codegen_finished(tcx);
- assert_and_save_dep_graph(tcx);
+ finalize_tcx(tcx);
ongoing_codegen.check_for_errors(tcx.sess);
cgu_name_builder.build_cgu_name(LOCAL_CRATE, &["crate"], Some("allocator")).to_string();
let mut modules = backend.new_metadata(tcx, &llmod_id);
tcx.sess
- .time("write allocator module", || backend.codegen_allocator(tcx, &mut modules, kind));
+ .time("write_allocator_module", || backend.codegen_allocator(tcx, &mut modules, kind));
Some(ModuleCodegen { name: llmod_id, module_llvm: modules, kind: ModuleKind::Allocator })
} else {
let metadata_cgu_name =
cgu_name_builder.build_cgu_name(LOCAL_CRATE, &["crate"], Some("metadata")).to_string();
let mut metadata_llvm_module = backend.new_metadata(tcx, &metadata_cgu_name);
- tcx.sess.time("write compressed metadata", || {
+ tcx.sess.time("write_compressed_metadata", || {
backend.write_compressed_metadata(
tcx,
&ongoing_codegen.metadata,
codegen_units
};
- let mut total_codegen_time = Duration::new(0, 0);
+ let total_codegen_time = Lock::new(Duration::new(0, 0));
- for cgu in codegen_units.into_iter() {
+ // The non-parallel compiler can only translate codegen units to LLVM IR
+ // on a single thread, leading to a staircase effect where the N LLVM
+ // threads have to wait on the single codegen threads to generate work
+ // for them. The parallel compiler does not have this restriction, so
+ // we can pre-load the LLVM queue in parallel before handing off
+ // coordination to the OnGoingCodegen scheduler.
+ //
+ // This likely is a temporary measure. Once we don't have to support the
+ // non-parallel compiler anymore, we can compile CGUs end-to-end in
+ // parallel and get rid of the complicated scheduling logic.
+ let pre_compile_cgus = |cgu_reuse: &[CguReuse]| {
+ if cfg!(parallel_compiler) {
+ tcx.sess.time("compile_first_CGU_batch", || {
+ // Try to find one CGU to compile per thread.
+ let cgus: Vec<_> = cgu_reuse
+ .iter()
+ .enumerate()
+ .filter(|&(_, reuse)| reuse == &CguReuse::No)
+ .take(tcx.sess.threads())
+ .collect();
+
+ // Compile the found CGUs in parallel.
+ par_iter(cgus)
+ .map(|(i, _)| {
+ let start_time = Instant::now();
+ let module = backend.compile_codegen_unit(tcx, codegen_units[i].name());
+ let mut time = total_codegen_time.lock();
+ *time += start_time.elapsed();
+ (i, module)
+ })
+ .collect()
+ })
+ } else {
+ FxHashMap::default()
+ }
+ };
+
+ let mut cgu_reuse = Vec::new();
+ let mut pre_compiled_cgus: Option<FxHashMap<usize, _>> = None;
+
+ for (i, cgu) in codegen_units.iter().enumerate() {
ongoing_codegen.wait_for_signal_to_codegen_item();
ongoing_codegen.check_for_errors(tcx.sess);
- let cgu_reuse = determine_cgu_reuse(tcx, &cgu);
+ // Do some setup work in the first iteration
+ if pre_compiled_cgus.is_none() {
+ // Calculate the CGU reuse
+ cgu_reuse = tcx.sess.time("find_cgu_reuse", || {
+ codegen_units.iter().map(|cgu| determine_cgu_reuse(tcx, &cgu)).collect()
+ });
+ // Pre compile some CGUs
+ pre_compiled_cgus = Some(pre_compile_cgus(&cgu_reuse));
+ }
+
+ let cgu_reuse = cgu_reuse[i];
tcx.sess.cgu_reuse_tracker.set_actual_reuse(&cgu.name().as_str(), cgu_reuse);
match cgu_reuse {
CguReuse::No => {
- let start_time = Instant::now();
- backend.compile_codegen_unit(tcx, cgu.name(), &ongoing_codegen.coordinator_send);
- total_codegen_time += start_time.elapsed();
+ let (module, cost) =
+ if let Some(cgu) = pre_compiled_cgus.as_mut().unwrap().remove(&i) {
+ cgu
+ } else {
+ let start_time = Instant::now();
+ let module = backend.compile_codegen_unit(tcx, cgu.name());
+ let mut time = total_codegen_time.lock();
+ *time += start_time.elapsed();
+ module
+ };
+ submit_codegened_module_to_llvm(
+ &backend,
+ &ongoing_codegen.coordinator_send,
+ module,
+ cost,
+ );
false
}
CguReuse::PreLto => {
// Since the main thread is sometimes blocked during codegen, we keep track
// -Ztime-passes output manually.
- print_time_passes_entry(tcx.sess.time_passes(), "codegen to LLVM IR", total_codegen_time);
+ print_time_passes_entry(
+ tcx.sess.time_passes(),
+ "codegen_to_LLVM_IR",
+ total_codegen_time.into_inner(),
+ );
::rustc_incremental::assert_module_sources::assert_module_sources(tcx);
ongoing_codegen.check_for_errors(tcx.sess);
- assert_and_save_dep_graph(tcx);
+ finalize_tcx(tcx);
+
ongoing_codegen.into_inner()
}
}
}
-fn assert_and_save_dep_graph(tcx: TyCtxt<'_>) {
- tcx.sess.time("assert dep graph", || ::rustc_incremental::assert_dep_graph(tcx));
+fn finalize_tcx(tcx: TyCtxt<'_>) {
+ tcx.sess.time("assert_dep_graph", || ::rustc_incremental::assert_dep_graph(tcx));
+ tcx.sess.time("serialize_dep_graph", || ::rustc_incremental::save_dep_graph(tcx));
- tcx.sess.time("serialize dep graph", || ::rustc_incremental::save_dep_graph(tcx));
+ // We assume that no queries are run past here. If there are new queries
+ // after this point, they'll show up as "<unknown>" in self-profiling data.
+ {
+ let _prof_timer = tcx.prof.generic_activity("self_profile_alloc_query_strings");
+ tcx.alloc_self_profile_query_strings();
+ }
}
impl CrateInfo {
panic_runtime: None,
compiler_builtins: None,
profiler_runtime: None,
- sanitizer_runtime: None,
is_no_builtins: Default::default(),
native_libraries: Default::default(),
used_libraries: tcx.native_libraries(LOCAL_CRATE),
if tcx.is_profiler_runtime(cnum) {
info.profiler_runtime = Some(cnum);
}
- if tcx.is_sanitizer_runtime(cnum) {
- info.sanitizer_runtime = Some(cnum);
- }
if tcx.is_no_builtins(cnum) {
info.is_no_builtins.insert(cnum);
}
use rustc::session::Session;
use rustc::ty::{Ty, TyCtxt};
+use rustc_errors::struct_span_err;
use rustc_span::Span;
use crate::base;
}
pub fn span_invalid_monomorphization_error(a: &Session, b: Span, c: &str) {
- span_err!(a, b, E0511, "{}", c);
+ struct_span_err!(a, b, E0511, "{}", c).emit();
}
extern crate log;
#[macro_use]
extern crate rustc;
-#[macro_use]
-extern crate syntax;
use rustc::dep_graph::WorkProduct;
use rustc::middle::cstore::{CrateSource, LibSource, NativeLibrary};
pub panic_runtime: Option<CrateNum>,
pub compiler_builtins: Option<CrateNum>,
pub profiler_runtime: Option<CrateNum>,
- pub sanitizer_runtime: Option<CrateNum>,
pub is_no_builtins: FxHashSet<CrateNum>,
pub native_libraries: FxHashMap<CrateNum, Lrc<Vec<NativeLibrary>>>,
pub crate_name: FxHashMap<CrateNum, String>,
use rustc_data_structures::graph::dominators::Dominators;
use rustc_index::bit_set::BitSet;
use rustc_index::vec::{Idx, IndexVec};
-use rustc_span::DUMMY_SP;
pub fn non_ssa_locals<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
fx: &FunctionCx<'a, 'tcx, Bx>,
};
if is_consume {
let base_ty =
- mir::Place::ty_from(place_ref.base, proj_base, *self.fx.mir, cx.tcx());
+ mir::Place::ty_from(place_ref.local, proj_base, *self.fx.mir, cx.tcx());
let base_ty = self.fx.monomorphize(&base_ty);
// ZSTs don't require any actual memory access.
let elem_ty = base_ty.projection_ty(cx.tcx(), elem).ty;
let elem_ty = self.fx.monomorphize(&elem_ty);
- let span = if let mir::PlaceBase::Local(index) = place_ref.base {
- self.fx.mir.local_decls[*index].source_info.span
- } else {
- DUMMY_SP
- };
+ let span = self.fx.mir.local_decls[*place_ref.local].source_info.span;
if cx.spanned_layout_of(elem_ty, span).is_zst() {
return;
}
// We use `NonUseContext::VarDebugInfo` for the base,
// which might not force the base local to memory,
// so we have to do it manually.
- if let mir::PlaceBase::Local(local) = place_ref.base {
- self.visit_local(&local, context, location);
- }
+ self.visit_local(place_ref.local, context, location);
}
}
}
self.process_place(
- &mir::PlaceRef { base: place_ref.base, projection: proj_base },
+ &mir::PlaceRef { local: place_ref.local, projection: proj_base },
base_context,
location,
);
};
}
- self.visit_place_base(place_ref.base, context, location);
- self.visit_projection(place_ref.base, place_ref.projection, context, location);
+ self.visit_place_base(place_ref.local, context, location);
+ self.visit_projection(place_ref.local, place_ref.projection, context, location);
}
}
}
use crate::MemFlags;
use rustc::middle::lang_items;
+use rustc::mir;
use rustc::mir::interpret::PanicInfo;
-use rustc::mir::{self, PlaceBase, Static, StaticKind};
use rustc::ty::layout::{self, FnAbiExt, HasTyCtxt, LayoutOf};
use rustc::ty::{self, Instance, Ty, TypeFoldable};
use rustc_index::vec::Idx;
// checked by const-qualification, which also
// promotes any complex rvalues to constants.
if i == 2 && intrinsic.unwrap().starts_with("simd_shuffle") {
- match arg {
- // The shuffle array argument is usually not an explicit constant,
- // but specified directly in the code. This means it gets promoted
- // and we can then extract the value by evaluating the promoted.
- mir::Operand::Copy(place) | mir::Operand::Move(place) => {
- if let mir::PlaceRef {
- base:
- &PlaceBase::Static(box Static {
- kind: StaticKind::Promoted(promoted, substs),
- ty,
- def_id,
- }),
- projection: &[],
- } = place.as_ref()
- {
- let c = bx.tcx().const_eval_promoted(
- Instance::new(def_id, self.monomorphize(&substs)),
- promoted,
- );
- let (llval, ty) = self.simd_shuffle_indices(
- &bx,
- terminator.source_info.span,
- ty,
- c,
- );
- return OperandRef {
- val: Immediate(llval),
- layout: bx.layout_of(ty),
- };
- } else {
- span_bug!(span, "shuffle indices must be constant");
- }
- }
-
- mir::Operand::Constant(constant) => {
- let c = self.eval_mir_constant(constant);
- let (llval, ty) = self.simd_shuffle_indices(
- &bx,
- constant.span,
- constant.literal.ty,
- c,
- );
- return OperandRef {
- val: Immediate(llval),
- layout: bx.layout_of(ty),
- };
- }
+ if let mir::Operand::Constant(constant) = arg {
+ let c = self.eval_mir_constant(constant);
+ let (llval, ty) = self.simd_shuffle_indices(
+ &bx,
+ constant.span,
+ constant.literal.ty,
+ c,
+ );
+ return OperandRef { val: Immediate(llval), layout: bx.layout_of(ty) };
+ } else {
+ span_bug!(span, "shuffle indices must be constant");
}
}
} else {
self.codegen_place(
bx,
- &mir::PlaceRef { base: &dest.base, projection: &dest.projection },
+ &mir::PlaceRef { local: &dest.local, projection: &dest.projection },
)
};
if fn_ret.is_indirect() {
// use `get_static` to get at their id.
// FIXME(oli-obk): can we unify this somehow, maybe by making const eval of statics
// always produce `&STATIC`. This may also simplify how const eval works with statics.
- ty::ConstKind::Unevaluated(def_id, substs) if self.cx.tcx().is_static(def_id) => {
+ ty::ConstKind::Unevaluated(def_id, substs, None) if self.cx.tcx().is_static(def_id) => {
assert!(substs.is_empty(), "we don't support generic statics yet");
let static_ = bx.get_static(def_id);
// we treat operands referring to statics as if they were `&STATIC` instead
constant: &mir::Constant<'tcx>,
) -> Result<&'tcx ty::Const<'tcx>, ErrorHandled> {
match constant.literal.val {
- ty::ConstKind::Unevaluated(def_id, substs) => {
+ ty::ConstKind::Unevaluated(def_id, substs, promoted) => {
let substs = self.monomorphize(&substs);
self.cx
.tcx()
- .const_eval_resolve(ty::ParamEnv::reveal_all(), def_id, substs, None)
+ .const_eval_resolve(ty::ParamEnv::reveal_all(), def_id, substs, promoted, None)
.map_err(|err| {
- self.cx
- .tcx()
- .sess
- .span_err(constant.span, "erroneous constant encountered");
+ if promoted.is_none() {
+ self.cx
+ .tcx()
+ .sess
+ .span_err(constant.span, "erroneous constant encountered");
+ }
err
})
}
if tcx.sess.opts.debuginfo == DebugInfo::Full || !tcx.sess.fewer_names() {
let mut per_local = IndexVec::from_elem(vec![], &body.local_decls);
for var in &body.var_debug_info {
- if let mir::PlaceBase::Local(local) = var.place.base {
- per_local[local].push(var);
- }
+ per_local[var.place.local].push(var);
}
Some(per_local)
} else {
) -> Option<OperandRef<'tcx, Bx::Value>> {
debug!("maybe_codegen_consume_direct(place_ref={:?})", place_ref);
- if let mir::PlaceBase::Local(index) = place_ref.base {
- match self.locals[*index] {
- LocalRef::Operand(Some(mut o)) => {
- // Moves out of scalar and scalar pair fields are trivial.
- for elem in place_ref.projection.iter() {
- match elem {
- mir::ProjectionElem::Field(ref f, _) => {
- o = o.extract_field(bx, f.index());
- }
- mir::ProjectionElem::Index(_)
- | mir::ProjectionElem::ConstantIndex { .. } => {
- // ZSTs don't require any actual memory access.
- // FIXME(eddyb) deduplicate this with the identical
- // checks in `codegen_consume` and `extract_field`.
- let elem = o.layout.field(bx.cx(), 0);
- if elem.is_zst() {
- o = OperandRef::new_zst(bx, elem);
- } else {
- return None;
- }
+ match self.locals[*place_ref.local] {
+ LocalRef::Operand(Some(mut o)) => {
+ // Moves out of scalar and scalar pair fields are trivial.
+ for elem in place_ref.projection.iter() {
+ match elem {
+ mir::ProjectionElem::Field(ref f, _) => {
+ o = o.extract_field(bx, f.index());
+ }
+ mir::ProjectionElem::Index(_)
+ | mir::ProjectionElem::ConstantIndex { .. } => {
+ // ZSTs don't require any actual memory access.
+ // FIXME(eddyb) deduplicate this with the identical
+ // checks in `codegen_consume` and `extract_field`.
+ let elem = o.layout.field(bx.cx(), 0);
+ if elem.is_zst() {
+ o = OperandRef::new_zst(bx, elem);
+ } else {
+ return None;
}
- _ => return None,
}
+ _ => return None,
}
-
- Some(o)
- }
- LocalRef::Operand(None) => {
- bug!("use of {:?} before def", place_ref);
- }
- LocalRef::Place(..) | LocalRef::UnsizedPlace(..) => {
- // watch out for locals that do not have an
- // alloca; they are handled somewhat differently
- None
}
+
+ Some(o)
+ }
+ LocalRef::Operand(None) => {
+ bug!("use of {:?} before def", place_ref);
+ }
+ LocalRef::Place(..) | LocalRef::UnsizedPlace(..) => {
+ // watch out for locals that do not have an
+ // alloca; they are handled somewhat differently
+ None
}
- } else {
- None
}
}
use rustc::mir;
use rustc::mir::tcx::PlaceTy;
use rustc::ty::layout::{self, Align, HasTyCtxt, LayoutOf, TyLayout, VariantIdx};
-use rustc::ty::{self, Instance, Ty};
+use rustc::ty::{self, Ty};
#[derive(Copy, Clone, Debug)]
pub struct PlaceRef<'tcx, V> {
PlaceRef { llval, llextra: None, layout, align }
}
- fn new_thin_place<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
- bx: &mut Bx,
- llval: V,
- layout: TyLayout<'tcx>,
- ) -> PlaceRef<'tcx, V> {
- assert!(!bx.cx().type_has_metadata(layout.ty));
- PlaceRef { llval, llextra: None, layout, align: layout.align.abi }
- }
-
// FIXME(eddyb) pass something else for the name so no work is done
// unless LLVM IR names are turned on (e.g. for `--emit=llvm-ir`).
pub fn alloca<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
let tcx = self.cx.tcx();
let result = match place_ref {
- mir::PlaceRef { base: mir::PlaceBase::Local(index), projection: [] } => {
- match self.locals[*index] {
- LocalRef::Place(place) => {
- return place;
- }
- LocalRef::UnsizedPlace(place) => {
- return bx.load_operand(place).deref(cx);
- }
- LocalRef::Operand(..) => {
- bug!("using operand local {:?} as place", place_ref);
- }
+ mir::PlaceRef { local, projection: [] } => match self.locals[**local] {
+ LocalRef::Place(place) => {
+ return place;
}
- }
- mir::PlaceRef {
- base:
- mir::PlaceBase::Static(box mir::Static {
- ty,
- kind: mir::StaticKind::Promoted(promoted, substs),
- def_id,
- }),
- projection: [],
- } => {
- let instance = Instance::new(*def_id, self.monomorphize(substs));
- let layout = cx.layout_of(self.monomorphize(&ty));
- match bx.tcx().const_eval_promoted(instance, *promoted) {
- Ok(val) => match val.val {
- ty::ConstKind::Value(mir::interpret::ConstValue::ByRef {
- alloc,
- offset,
- }) => bx.cx().from_const_alloc(layout, alloc, offset),
- _ => bug!("promoteds should have an allocation: {:?}", val),
- },
- Err(_) => {
- // This is unreachable as long as runtime
- // and compile-time agree perfectly.
- // With floats that won't always be true,
- // so we generate a (safe) abort.
- bx.abort();
- // We still have to return a place but it doesn't matter,
- // this code is unreachable.
- let llval =
- bx.cx().const_undef(bx.cx().type_ptr_to(bx.cx().backend_type(layout)));
- PlaceRef::new_sized(llval, layout)
- }
+ LocalRef::UnsizedPlace(place) => {
+ return bx.load_operand(place).deref(cx);
}
- }
- mir::PlaceRef {
- base:
- mir::PlaceBase::Static(box mir::Static {
- ty,
- kind: mir::StaticKind::Static,
- def_id,
- }),
- projection: [],
- } => {
- // NB: The layout of a static may be unsized as is the case when working
- // with a static that is an extern_type.
- let layout = cx.layout_of(self.monomorphize(&ty));
- let static_ = bx.get_static(*def_id);
- PlaceRef::new_thin_place(bx, static_, layout)
- }
- mir::PlaceRef { base, projection: [proj_base @ .., mir::ProjectionElem::Deref] } => {
+ LocalRef::Operand(..) => {
+ bug!("using operand local {:?} as place", place_ref);
+ }
+ },
+ mir::PlaceRef { local, projection: [proj_base @ .., mir::ProjectionElem::Deref] } => {
// Load the pointer from its location.
- self.codegen_consume(bx, &mir::PlaceRef { base, projection: proj_base })
+ self.codegen_consume(bx, &mir::PlaceRef { local, projection: proj_base })
.deref(bx.cx())
}
- mir::PlaceRef { base, projection: [proj_base @ .., elem] } => {
+ mir::PlaceRef { local, projection: [proj_base @ .., elem] } => {
// FIXME turn this recursion into iteration
let cg_base =
- self.codegen_place(bx, &mir::PlaceRef { base, projection: proj_base });
+ self.codegen_place(bx, &mir::PlaceRef { local, projection: proj_base });
match elem {
mir::ProjectionElem::Deref => bug!(),
pub fn monomorphized_place_ty(&self, place_ref: &mir::PlaceRef<'_, 'tcx>) -> Ty<'tcx> {
let tcx = self.cx.tcx();
- let place_ty = mir::Place::ty_from(place_ref.base, place_ref.projection, *self.mir, tcx);
+ let place_ty = mir::Place::ty_from(place_ref.local, place_ref.projection, *self.mir, tcx);
self.monomorphize(&place_ty.ty)
}
}
use rustc::mir;
+use rustc_errors::struct_span_err;
use super::FunctionCx;
use super::LocalRef;
if let OperandValue::Immediate(_) = op.val {
acc.push(op.immediate());
} else {
- span_err!(
+ struct_span_err!(
bx.sess(),
span.to_owned(),
E0669,
"invalid value for constraint in inline assembly"
- );
+ )
+ .emit();
}
acc
},
statement.source_info.span,
);
if !res {
- span_err!(
+ struct_span_err!(
bx.sess(),
statement.source_info.span,
E0668,
"malformed inline assembly"
- );
+ )
+ .emit();
}
}
bx
use super::write::WriteBackendMethods;
use super::CodegenObject;
+use crate::ModuleCodegen;
use rustc::middle::cstore::EncodedMetadata;
use rustc::session::{config, Session};
use rustc_span::symbol::Symbol;
use syntax::expand::allocator::AllocatorKind;
-use std::sync::mpsc;
use std::sync::Arc;
pub trait BackendTypes {
{
}
-pub trait ExtraBackendMethods: CodegenBackend + WriteBackendMethods + Sized + Send {
+pub trait ExtraBackendMethods: CodegenBackend + WriteBackendMethods + Sized + Send + Sync {
fn new_metadata(&self, sess: TyCtxt<'_>, mod_name: &str) -> Self::Module;
fn write_compressed_metadata<'tcx>(
&self,
mods: &mut Self::Module,
kind: AllocatorKind,
);
+ /// This generates the codegen unit and returns it along with
+ /// a `u64` giving an estimate of the unit's processing cost.
fn compile_codegen_unit(
&self,
tcx: TyCtxt<'_>,
cgu_name: Symbol,
- tx_to_llvm_workers: &mpsc::Sender<Box<dyn std::any::Any + Send>>,
- );
+ ) -> (ModuleCodegen<Self::Module>, u64);
// If find_features is true this won't access `sess.crate_types` by assuming
// that `is_pie_binary` is false. When we discover LLVM target features
// `sess.crate_types` is uninitialized so we cannot access it.
smallvec = { version = "1.0", features = ["union", "may_dangle"] }
rustc_index = { path = "../librustc_index", package = "rustc_index" }
bitflags = "1.2.1"
-measureme = "0.5"
+measureme = "0.7.1"
[dependencies.parking_lot]
version = "0.9"
Ok(mid) => mid,
Err(_) => return &[],
};
+ let size = data.len();
- // We get back *some* element with the given key -- so
- // search backwards to find the *first* one.
- //
- // (It'd be more efficient to use a "galloping" search
- // here, but it's not really worth it for small-ish
- // amounts of data.)
+ // We get back *some* element with the given key -- so do
+ // a galloping search backwards to find the *first* one.
let mut start = mid;
- while start > 0 {
- if key_fn(&data[start - 1]) == *key {
- start -= 1;
- } else {
+ let mut previous = mid;
+ let mut step = 1;
+ loop {
+ start = start.saturating_sub(step);
+ if start == 0 || key_fn(&data[start]) != *key {
break;
}
+ previous = start;
+ step *= 2;
+ }
+ step = previous - start;
+ while step > 1 {
+ let half = step / 2;
+ let mid = start + half;
+ if key_fn(&data[mid]) != *key {
+ start = mid;
+ }
+ step -= half;
+ }
+ // adjust by one if we have overshot
+ if start < size && key_fn(&data[start]) != *key {
+ start += 1;
}
// Now search forward to find the *last* one.
- //
- // (It'd be more efficient to use a "galloping" search
- // here, but it's not really worth it for small-ish
- // amounts of data.)
- let mut end = mid + 1;
- let max = data.len();
- while end < max {
- if key_fn(&data[end]) == *key {
- end += 1;
- } else {
+ let mut end = mid;
+ let mut previous = mid;
+ let mut step = 1;
+ loop {
+ end = end.saturating_add(step).min(size);
+ if end == size || key_fn(&data[end]) != *key {
break;
}
+ previous = end;
+ step *= 2;
+ }
+ step = end - previous;
+ while step > 1 {
+ let half = step / 2;
+ let mid = end - half;
+ if key_fn(&data[mid]) != *key {
+ end = mid;
+ }
+ step -= half;
}
&data[start..end]
--- /dev/null
+/// "Signaling" trait used in impl trait to tag lifetimes that you may
+/// need to capture but don't really need for other reasons.
+/// Basically a workaround; see [this comment] for details.
+///
+/// [this comment]: https://github.com/rust-lang/rust/issues/34511#issuecomment-373423999
+// FIXME(eddyb) false positive, the lifetime parameter is "phantom" but needed.
+#[allow(unused_lifetimes)]
+pub trait Captures<'a> {}
+
+impl<'a, T: ?Sized> Captures<'a> for T {}
pub mod base_n;
pub mod binary_search_util;
pub mod box_region;
+pub mod captures;
pub mod const_cstr;
pub mod flock;
pub mod fx;
+//! # Rust Compiler Self-Profiling
+//!
+//! This module implements the basic framework for the compiler's self-
+//! profiling support. It provides the `SelfProfiler` type which enables
+//! recording "events". An event is something that starts and ends at a given
+//! point in time and has an ID and a kind attached to it. This allows for
+//! tracing the compiler's activity.
+//!
+//! Internally this module uses the custom tailored [measureme][mm] crate for
+//! efficiently recording events to disk in a compact format that can be
+//! post-processed and analyzed by the suite of tools in the `measureme`
+//! project. The highest priority for the tracing framework is on incurring as
+//! little overhead as possible.
+//!
+//!
+//! ## Event Overview
+//!
+//! Events have a few properties:
+//!
+//! - The `event_kind` designates the broad category of an event (e.g. does it
+//! correspond to the execution of a query provider or to loading something
+//! from the incr. comp. on-disk cache, etc).
+//! - The `event_id` designates the query invocation or function call it
+//! corresponds to, possibly including the query key or function arguments.
+//! - Each event stores the ID of the thread it was recorded on.
+//! - The timestamp stores beginning and end of the event, or the single point
+//! in time it occurred at for "instant" events.
+//!
+//!
+//! ## Event Filtering
+//!
+//! Event generation can be filtered by event kind. Recording all possible
+//! events generates a lot of data, much of which is not needed for most kinds
+//! of analysis. So, in order to keep overhead as low as possible for a given
+//! use case, the `SelfProfiler` will only record the kinds of events that
+//! pass the filter specified as a command line argument to the compiler.
+//!
+//!
+//! ## `event_id` Assignment
+//!
+//! As far as `measureme` is concerned, `event_id`s are just strings. However,
+//! it would incur too much overhead to generate and persist each `event_id`
+//! string at the point where the event is recorded. In order to make this more
+//! efficient `measureme` has two features:
+//!
+//! - Strings can share their content, so that re-occurring parts don't have to
+//! be copied over and over again. One allocates a string in `measureme` and
+//! gets back a `StringId`. This `StringId` is then used to refer to that
+//! string. `measureme` strings are actually DAGs of string components so that
+//! arbitrary sharing of substrings can be done efficiently. This is useful
+//! because `event_id`s contain lots of redundant text like query names or
+//! def-path components.
+//!
+//! - `StringId`s can be "virtual" which means that the client picks a numeric
+//! ID according to some application-specific scheme and can later make that
+//! ID be mapped to an actual string. This is used to cheaply generate
+//! `event_id`s while the events actually occur, causing little timing
+//! distortion, and then later map those `StringId`s, in bulk, to actual
+//! `event_id` strings. This way the largest part of the tracing overhead is
+//! localized to one contiguous chunk of time.
+//!
+//! How are these `event_id`s generated in the compiler? For things that occur
+//! infrequently (e.g. "generic activities"), we just allocate the string the
+//! first time it is used and then keep the `StringId` in a hash table. This
+//! is implemented in `SelfProfiler::get_or_alloc_cached_string()`.
+//!
+//! For queries it gets more interesting: First we need a unique numeric ID for
+//! each query invocation (the `QueryInvocationId`). This ID is used as the
+//! virtual `StringId` we use as `event_id` for a given event. This ID has to
+//! be available both when the query is executed and later, together with the
+//! query key, when we allocate the actual `event_id` strings in bulk.
+//!
+//! We could make the compiler generate and keep track of such an ID for each
+//! query invocation but luckily we already have something that fits all the
+//! the requirements: the query's `DepNodeIndex`. So we use the numeric value
+//! of the `DepNodeIndex` as `event_id` when recording the event and then,
+//! just before the query context is dropped, we walk the entire query cache
+//! (which stores the `DepNodeIndex` along with the query key for each
+//! invocation) and allocate the corresponding strings together with a mapping
+//! for `DepNodeIndex as StringId`.
+//!
+//! [mm]: https://github.com/rust-lang/measureme/
+
+use crate::fx::FxHashMap;
+
use std::error::Error;
use std::fs;
-use std::mem::{self, Discriminant};
use std::path::Path;
use std::process;
use std::sync::Arc;
use std::time::{Duration, Instant};
use std::u32;
-use crate::cold_path;
-
-use measureme::StringId;
+use measureme::{EventId, EventIdBuilder, SerializableString, StringId};
+use parking_lot::RwLock;
/// MmapSerializatioSink is faster on macOS and Linux
/// but FileSerializationSink is faster on Windows
type Profiler = measureme::Profiler<SerializationSink>;
-pub trait QueryName: Sized + Copy {
- fn discriminant(self) -> Discriminant<Self>;
- fn as_str(self) -> &'static str;
-}
-
#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd)]
pub enum ProfileCategory {
Parsing,
const QUERY_CACHE_HITS = 1 << 2;
const QUERY_BLOCKED = 1 << 3;
const INCR_CACHE_LOADS = 1 << 4;
- const SPARSE_PASS = 1 << 5;
- const GENERIC_PASS = 1 << 6;
+
+ const QUERY_KEYS = 1 << 5;
const DEFAULT = Self::GENERIC_ACTIVITIES.bits |
Self::QUERY_PROVIDERS.bits |
Self::QUERY_BLOCKED.bits |
- Self::INCR_CACHE_LOADS.bits |
- Self::SPARSE_PASS.bits |
- Self::GENERIC_PASS.bits;
+ Self::INCR_CACHE_LOADS.bits;
// empty() and none() aren't const-fns unfortunately
const NONE = 0;
const EVENT_FILTERS_BY_NAME: &[(&str, EventFilter)] = &[
("none", EventFilter::NONE),
("all", EventFilter::ALL),
- ("sparse-pass", EventFilter::SPARSE_PASS),
- ("generic-pass", EventFilter::GENERIC_PASS),
("generic-activity", EventFilter::GENERIC_ACTIVITIES),
("query-provider", EventFilter::QUERY_PROVIDERS),
("query-cache-hit", EventFilter::QUERY_CACHE_HITS),
("query-blocked", EventFilter::QUERY_BLOCKED),
("incr-cache-load", EventFilter::INCR_CACHE_LOADS),
+ ("query-keys", EventFilter::QUERY_KEYS),
];
fn thread_id_to_u32(tid: ThreadId) -> u32 {
- unsafe { mem::transmute::<ThreadId, u64>(tid) as u32 }
+ unsafe { std::mem::transmute::<ThreadId, u64>(tid) as u32 }
}
+/// Something that uniquely identifies a query invocation.
+pub struct QueryInvocationId(pub u32);
+
/// A reference to the SelfProfiler. It can be cloned and sent across thread
/// boundaries at will.
#[derive(Clone)]
// actually enabled.
event_filter_mask: EventFilter,
- // Print sparse passes to stdout
- verbose_sparse: bool,
+ // Print verbose generic activities to stdout
+ print_verbose_generic_activities: bool,
- // Print generic passes to stdout
- verbose_generic: bool,
+ // Print extra verbose generic activities to stdout
+ print_extra_verbose_generic_activities: bool,
}
impl SelfProfilerRef {
pub fn new(
profiler: Option<Arc<SelfProfiler>>,
- verbose_sparse: bool,
- verbose_generic: bool,
+ print_verbose_generic_activities: bool,
+ print_extra_verbose_generic_activities: bool,
) -> SelfProfilerRef {
// If there is no SelfProfiler then the filter mask is set to NONE,
// ensuring that nothing ever tries to actually access it.
- let mut event_filter_mask =
+ let event_filter_mask =
profiler.as_ref().map(|p| p.event_filter_mask).unwrap_or(EventFilter::NONE);
- if verbose_sparse {
- event_filter_mask |= EventFilter::SPARSE_PASS;
- }
-
- if verbose_generic {
- event_filter_mask |= EventFilter::GENERIC_PASS;
+ SelfProfilerRef {
+ profiler,
+ event_filter_mask,
+ print_verbose_generic_activities,
+ print_extra_verbose_generic_activities,
}
-
- SelfProfilerRef { profiler, event_filter_mask, verbose_sparse, verbose_generic }
}
+ // This shim makes sure that calls only get executed if the filter mask
+ // lets them pass. It also contains some trickery to make sure that
+ // code is optimized for non-profiling compilation sessions, i.e. anything
+ // past the filter check is never inlined so it doesn't clutter the fast
+ // path.
#[inline(always)]
fn exec<F>(&self, event_filter: EventFilter, f: F) -> TimingGuard<'_>
where
F: for<'a> FnOnce(&'a SelfProfiler) -> TimingGuard<'a>,
{
- self.handle_event(
- event_filter,
- || f(self.profiler.as_ref().unwrap()),
- || TimingGuard::none(),
- )
- }
+ #[inline(never)]
+ fn cold_call<F>(profiler_ref: &SelfProfilerRef, f: F) -> TimingGuard<'_>
+ where
+ F: for<'a> FnOnce(&'a SelfProfiler) -> TimingGuard<'a>,
+ {
+ let profiler = profiler_ref.profiler.as_ref().unwrap();
+ f(&**profiler)
+ }
- // This shim makes sure that cold calls only get executed if the filter mask
- // lets them pass. It also contains some trickery to make sure that
- // code is optimized for non-profiling compilation sessions, i.e. anything
- // past the filter check is never inlined so it doesn't clutter the fast
- // path.
- #[inline(always)]
- fn handle_event<R>(
- &self,
- event_filter: EventFilter,
- cold: impl FnOnce() -> R,
- hot: impl FnOnce() -> R,
- ) -> R {
if unlikely!(self.event_filter_mask.contains(event_filter)) {
- cold_path(|| cold())
+ cold_call(self, f)
} else {
- hot()
+ TimingGuard::none()
}
}
- /// Start profiling a sparse pass. Profiling continues until the
- /// VerboseTimingGuard returned from this call is dropped.
+ /// Start profiling a verbose generic activity. Profiling continues until the
+ /// VerboseTimingGuard returned from this call is dropped. In addition to recording
+ /// a measureme event, "verbose" generic activities also print a timing entry to
+ /// stdout if the compiler is invoked with -Ztime or -Ztime-passes.
#[inline(always)]
- pub fn sparse_pass<'a>(&'a self, event_id: &'a str) -> VerboseTimingGuard<'a> {
- self.handle_event(
- EventFilter::SPARSE_PASS,
- || {
- VerboseTimingGuard::start(
- self.profiler
- .as_ref()
- .map(|profiler| (&**profiler, profiler.sparse_pass_event_kind)),
- event_id,
- self.verbose_sparse,
- )
- },
- || VerboseTimingGuard::none(),
+ pub fn verbose_generic_activity<'a>(
+ &'a self,
+ event_id: &'static str,
+ ) -> VerboseTimingGuard<'a> {
+ VerboseTimingGuard::start(
+ event_id,
+ self.print_verbose_generic_activities,
+ self.generic_activity(event_id),
)
}
- /// Start profiling a generic pass. Profiling continues until the
- /// VerboseTimingGuard returned from this call is dropped.
+ /// Start profiling a extra verbose generic activity. Profiling continues until the
+ /// VerboseTimingGuard returned from this call is dropped. In addition to recording
+ /// a measureme event, "extra verbose" generic activities also print a timing entry to
+ /// stdout if the compiler is invoked with -Ztime-passes.
#[inline(always)]
- pub fn generic_pass<'a>(&'a self, event_id: &'a str) -> VerboseTimingGuard<'a> {
- self.handle_event(
- EventFilter::GENERIC_PASS,
- || {
- VerboseTimingGuard::start(
- self.profiler
- .as_ref()
- .map(|profiler| (&**profiler, profiler.generic_pass_event_kind)),
- event_id,
- self.verbose_generic,
- )
- },
- || VerboseTimingGuard::none(),
+ pub fn extra_verbose_generic_activity<'a>(
+ &'a self,
+ event_id: &'a str,
+ ) -> VerboseTimingGuard<'a> {
+ // FIXME: This does not yet emit a measureme event
+ // because callers encode arguments into `event_id`.
+ VerboseTimingGuard::start(
+ event_id,
+ self.print_extra_verbose_generic_activities,
+ TimingGuard::none(),
)
}
/// Start profiling a generic activity. Profiling continues until the
/// TimingGuard returned from this call is dropped.
#[inline(always)]
- pub fn generic_activity(&self, event_id: &str) -> TimingGuard<'_> {
+ pub fn generic_activity(&self, event_id: &'static str) -> TimingGuard<'_> {
self.exec(EventFilter::GENERIC_ACTIVITIES, |profiler| {
- let event_id = profiler.profiler.alloc_string(event_id);
+ let event_id = profiler.get_or_alloc_cached_string(event_id);
+ let event_id = EventId::from_label(event_id);
TimingGuard::start(profiler, profiler.generic_activity_event_kind, event_id)
})
}
/// Start profiling a query provider. Profiling continues until the
/// TimingGuard returned from this call is dropped.
#[inline(always)]
- pub fn query_provider(&self, query_name: impl QueryName) -> TimingGuard<'_> {
+ pub fn query_provider(&self) -> TimingGuard<'_> {
self.exec(EventFilter::QUERY_PROVIDERS, |profiler| {
- let event_id = SelfProfiler::get_query_name_string_id(query_name);
- TimingGuard::start(profiler, profiler.query_event_kind, event_id)
+ TimingGuard::start(profiler, profiler.query_event_kind, EventId::INVALID)
})
}
/// Record a query in-memory cache hit.
#[inline(always)]
- pub fn query_cache_hit(&self, query_name: impl QueryName) {
+ pub fn query_cache_hit(&self, query_invocation_id: QueryInvocationId) {
self.instant_query_event(
|profiler| profiler.query_cache_hit_event_kind,
- query_name,
+ query_invocation_id,
EventFilter::QUERY_CACHE_HITS,
);
}
/// Profiling continues until the TimingGuard returned from this call is
/// dropped.
#[inline(always)]
- pub fn query_blocked(&self, query_name: impl QueryName) -> TimingGuard<'_> {
+ pub fn query_blocked(&self) -> TimingGuard<'_> {
self.exec(EventFilter::QUERY_BLOCKED, |profiler| {
- let event_id = SelfProfiler::get_query_name_string_id(query_name);
- TimingGuard::start(profiler, profiler.query_blocked_event_kind, event_id)
+ TimingGuard::start(profiler, profiler.query_blocked_event_kind, EventId::INVALID)
})
}
/// incremental compilation on-disk cache. Profiling continues until the
/// TimingGuard returned from this call is dropped.
#[inline(always)]
- pub fn incr_cache_loading(&self, query_name: impl QueryName) -> TimingGuard<'_> {
+ pub fn incr_cache_loading(&self) -> TimingGuard<'_> {
self.exec(EventFilter::INCR_CACHE_LOADS, |profiler| {
- let event_id = SelfProfiler::get_query_name_string_id(query_name);
- TimingGuard::start(profiler, profiler.incremental_load_result_event_kind, event_id)
+ TimingGuard::start(
+ profiler,
+ profiler.incremental_load_result_event_kind,
+ EventId::INVALID,
+ )
})
}
fn instant_query_event(
&self,
event_kind: fn(&SelfProfiler) -> StringId,
- query_name: impl QueryName,
+ query_invocation_id: QueryInvocationId,
event_filter: EventFilter,
) {
drop(self.exec(event_filter, |profiler| {
- let event_id = SelfProfiler::get_query_name_string_id(query_name);
+ let event_id = StringId::new_virtual(query_invocation_id.0);
let thread_id = thread_id_to_u32(std::thread::current().id());
- profiler.profiler.record_instant_event(event_kind(profiler), event_id, thread_id);
+ profiler.profiler.record_instant_event(
+ event_kind(profiler),
+ EventId::from_virtual(event_id),
+ thread_id,
+ );
TimingGuard::none()
}));
}
- pub fn register_queries(&self, f: impl FnOnce(&SelfProfiler)) {
+ pub fn with_profiler(&self, f: impl FnOnce(&SelfProfiler)) {
if let Some(profiler) = &self.profiler {
f(&profiler)
}
}
+
+ #[inline]
+ pub fn enabled(&self) -> bool {
+ self.profiler.is_some()
+ }
}
pub struct SelfProfiler {
profiler: Profiler,
event_filter_mask: EventFilter,
+
+ string_cache: RwLock<FxHashMap<&'static str, StringId>>,
+
query_event_kind: StringId,
- sparse_pass_event_kind: StringId,
- generic_pass_event_kind: StringId,
generic_activity_event_kind: StringId,
incremental_load_result_event_kind: StringId,
query_blocked_event_kind: StringId,
let profiler = Profiler::new(&path)?;
let query_event_kind = profiler.alloc_string("Query");
- let sparse_pass_event_kind = profiler.alloc_string("SparsePass");
- let generic_pass_event_kind = profiler.alloc_string("GenericPass");
let generic_activity_event_kind = profiler.alloc_string("GenericActivity");
let incremental_load_result_event_kind = profiler.alloc_string("IncrementalLoadResult");
let query_blocked_event_kind = profiler.alloc_string("QueryBlocked");
Ok(SelfProfiler {
profiler,
event_filter_mask,
+ string_cache: RwLock::new(FxHashMap::default()),
query_event_kind,
- sparse_pass_event_kind,
- generic_pass_event_kind,
generic_activity_event_kind,
incremental_load_result_event_kind,
query_blocked_event_kind,
})
}
- fn get_query_name_string_id(query_name: impl QueryName) -> StringId {
- let discriminant =
- unsafe { mem::transmute::<Discriminant<_>, u64>(query_name.discriminant()) };
+ /// Allocates a new string in the profiling data. Does not do any caching
+ /// or deduplication.
+ pub fn alloc_string<STR: SerializableString + ?Sized>(&self, s: &STR) -> StringId {
+ self.profiler.alloc_string(s)
+ }
+
+ /// Gets a `StringId` for the given string. This method makes sure that
+ /// any strings going through it will only be allocated once in the
+ /// profiling data.
+ pub fn get_or_alloc_cached_string(&self, s: &'static str) -> StringId {
+ // Only acquire a read-lock first since we assume that the string is
+ // already present in the common case.
+ {
+ let string_cache = self.string_cache.read();
+
+ if let Some(&id) = string_cache.get(s) {
+ return id;
+ }
+ }
+
+ let mut string_cache = self.string_cache.write();
+ // Check if the string has already been added in the small time window
+ // between dropping the read lock and acquiring the write lock.
+ *string_cache.entry(s).or_insert_with(|| self.profiler.alloc_string(s))
+ }
+
+ pub fn map_query_invocation_id_to_string(&self, from: QueryInvocationId, to: StringId) {
+ let from = StringId::new_virtual(from.0);
+ self.profiler.map_virtual_to_concrete_string(from, to);
+ }
+
+ pub fn bulk_map_query_invocation_id_to_single_string<I>(&self, from: I, to: StringId)
+ where
+ I: Iterator<Item = QueryInvocationId> + ExactSizeIterator,
+ {
+ let from = from.map(|qid| StringId::new_virtual(qid.0));
+ self.profiler.bulk_map_virtual_to_single_concrete_string(from, to);
+ }
- StringId::reserved(discriminant as u32)
+ pub fn query_key_recording_enabled(&self) -> bool {
+ self.event_filter_mask.contains(EventFilter::QUERY_KEYS)
}
- pub fn register_query_name(&self, query_name: impl QueryName) {
- let id = SelfProfiler::get_query_name_string_id(query_name);
- self.profiler.alloc_string_with_reserved_id(id, query_name.as_str());
+ pub fn event_id_builder(&self) -> EventIdBuilder<'_, SerializationSink> {
+ EventIdBuilder::new(&self.profiler)
}
}
pub fn start(
profiler: &'a SelfProfiler,
event_kind: StringId,
- event_id: StringId,
+ event_id: EventId,
) -> TimingGuard<'a> {
let thread_id = thread_id_to_u32(std::thread::current().id());
let raw_profiler = &profiler.profiler;
TimingGuard(Some(timing_guard))
}
+ #[inline]
+ pub fn finish_with_query_invocation_id(self, query_invocation_id: QueryInvocationId) {
+ if let Some(guard) = self.0 {
+ let event_id = StringId::new_virtual(query_invocation_id.0);
+ let event_id = EventId::from_virtual(event_id);
+ guard.finish_with_override_event_id(event_id);
+ }
+ }
+
#[inline]
pub fn none() -> TimingGuard<'a> {
TimingGuard(None)
}
+
+ #[inline(always)]
+ pub fn run<R>(self, f: impl FnOnce() -> R) -> R {
+ let _timer = self;
+ f()
+ }
}
#[must_use]
}
impl<'a> VerboseTimingGuard<'a> {
- pub fn start(
- profiler: Option<(&'a SelfProfiler, StringId)>,
- event_id: &'a str,
- verbose: bool,
- ) -> Self {
- let _guard = profiler.map_or(TimingGuard::none(), |(profiler, event_kind)| {
- let event = profiler.profiler.alloc_string(event_id);
- TimingGuard::start(profiler, event_kind, event)
- });
+ pub fn start(event_id: &'a str, verbose: bool, _guard: TimingGuard<'a>) -> Self {
VerboseTimingGuard {
event_id,
_guard,
- start: if verbose { Some(Instant::now()) } else { None },
+ start: if unlikely!(verbose) { Some(Instant::now()) } else { None },
}
}
let _timer = self;
f()
}
-
- fn none() -> Self {
- VerboseTimingGuard { event_id: "", start: None, _guard: TimingGuard::none() }
- }
}
impl Drop for VerboseTimingGuard<'_> {
cb: DWORD,
) -> BOOL;
}
- let mut pmc: PROCESS_MEMORY_COUNTERS = unsafe { mem::zeroed() };
- pmc.cb = mem::size_of_val(&pmc) as DWORD;
+ let mut pmc: PROCESS_MEMORY_COUNTERS = unsafe { std::mem::zeroed() };
+ pmc.cb = std::mem::size_of_val(&pmc) as DWORD;
match unsafe { GetProcessMemoryInfo(GetCurrentProcess(), &mut pmc, pmc.cb) } {
0 => None,
_ => Some(pmc.WorkingSetSize as usize),
crate-type = ["dylib"]
[dependencies]
-graphviz = { path = "../libgraphviz" }
lazy_static = "1.0"
log = "0.4"
env_logger = { version = "0.7", default-features = false }
rustc_target = { path = "../librustc_target" }
rustc_lint = { path = "../librustc_lint" }
rustc_data_structures = { path = "../librustc_data_structures" }
-errors = { path = "../librustc_errors", package = "rustc_errors" }
+rustc_errors = { path = "../librustc_errors" }
rustc_feature = { path = "../librustc_feature" }
rustc_hir = { path = "../librustc_hir" }
rustc_metadata = { path = "../librustc_metadata" }
rustc_error_codes = { path = "../librustc_error_codes" }
rustc_interface = { path = "../librustc_interface" }
rustc_serialize = { path = "../libserialize", package = "serialize" }
-rustc_resolve = { path = "../librustc_resolve" }
syntax = { path = "../libsyntax" }
rustc_span = { path = "../librustc_span" }
pub extern crate rustc_plugin_impl as plugin;
-//use rustc_resolve as resolve;
-use errors::{registry::Registry, PResult};
-use rustc::lint;
-use rustc::lint::Lint;
+use rustc::lint::{Lint, LintId};
use rustc::middle::cstore::MetadataLoader;
use rustc::session::config::nightly_options;
use rustc::session::config::{ErrorOutputType, Input, OutputType, PrintRequest};
use rustc_codegen_utils::codegen_backend::CodegenBackend;
use rustc_data_structures::profiling::print_time_passes_entry;
use rustc_data_structures::sync::SeqCst;
+use rustc_errors::{registry::Registry, PResult};
use rustc_feature::{find_gated_cfg, UnstableFeatures};
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_interface::util::get_builtin_codegen_backend;
use rustc_interface::{interface, Queries};
+use rustc_lint::LintStore;
use rustc_metadata::locator;
use rustc_save_analysis as save;
use rustc_save_analysis::DumpHandler;
queries.global_ctxt()?.peek_mut().enter(|tcx| {
let result = tcx.analysis(LOCAL_CRATE);
- sess.time("save analysis", || {
+ sess.time("save_analysis", || {
save::process_crate(
tcx,
&expanded_crate,
})?;
} else {
// Drop AST after creating GlobalCtxt to free memory
+ let _timer = sess.prof.generic_activity("drop_ast");
mem::drop(queries.expansion()?.take());
}
})?;
if let Some(linker) = linker {
+ let _timer = sess.timer("link");
linker.link()?
}
);
}
-fn describe_lints(sess: &Session, lint_store: &lint::LintStore, loaded_plugins: bool) {
+fn describe_lints(sess: &Session, lint_store: &LintStore, loaded_plugins: bool) {
println!(
"
Available lint options:
}
fn sort_lint_groups(
- lints: Vec<(&'static str, Vec<lint::LintId>, bool)>,
- ) -> Vec<(&'static str, Vec<lint::LintId>)> {
+ lints: Vec<(&'static str, Vec<LintId>, bool)>,
+ ) -> Vec<(&'static str, Vec<LintId>)> {
let mut lints: Vec<_> = lints.into_iter().map(|(x, y, _)| (x, y)).collect();
lints.sort_by_key(|l| l.0);
lints
println!(" {} {}", padded("----"), "---------");
println!(" {} {}", padded("warnings"), "all lints that are set to issue warnings");
- let print_lint_groups = |lints: Vec<(&'static str, Vec<lint::LintId>)>| {
+ let print_lint_groups = |lints: Vec<(&'static str, Vec<LintId>)>| {
for (name, to) in lints {
let name = name.to_lowercase().replace("_", "-");
let desc = to
/// the panic into a `Result` instead.
pub fn catch_fatal_errors<F: FnOnce() -> R, R>(f: F) -> Result<R, ErrorReported> {
catch_unwind(panic::AssertUnwindSafe(f)).map_err(|value| {
- if value.is::<errors::FatalErrorMarker>() {
+ if value.is::<rustc_errors::FatalErrorMarker>() {
ErrorReported
} else {
panic::resume_unwind(value);
// Separate the output with an empty line
eprintln!();
- let emitter = Box::new(errors::emitter::EmitterWriter::stderr(
- errors::ColorConfig::Auto,
+ let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
+ rustc_errors::ColorConfig::Auto,
None,
false,
false,
None,
false,
));
- let handler = errors::Handler::with_emitter(true, None, emitter);
+ let handler = rustc_errors::Handler::with_emitter(true, None, emitter);
// a .span_bug or .bug call has already printed what
// it wants to print.
- if !info.payload().is::<errors::ExplicitBug>() {
- let d = errors::Diagnostic::new(errors::Level::Bug, "unexpected panic");
+ if !info.payload().is::<rustc_errors::ExplicitBug>() {
+ let d = rustc_errors::Diagnostic::new(rustc_errors::Level::Bug, "unexpected panic");
handler.emit_diagnostic(&d);
- handler.abort_if_errors_and_should_abort();
}
let mut xs: Vec<Cow<'static, str>> = vec![
generates does not grow drastically, since the compiler will only generate an
implementation if the function is called with unparametrized substitutions
(i.e., substitutions where none of the substituted types are themselves
-parametrized).
+parameterized).
However, with trait objects we have to make a table containing _every_ object
that implements the trait. Now, if it has type parameters, we need to add
}
```
-or you remove the integer represention of your enum:
+or you remove the integer representation of your enum:
```
enum NightsWatch {}
-Explicitly implementing both Drop and Copy for a type is currently disallowed.
-This feature can make some sense in theory, but the current implementation is
-incorrect and can lead to memory unsafety (see [issue #20126][iss20126]), so
-it has been disabled for now.
+The `Copy` trait was implemented on a type with a `Drop` implementation.
+
+Erroneous code example:
+
+```compile_fail,E0184
+#[derive(Copy)]
+struct Foo; // error!
+
+impl Drop for Foo {
+ fn drop(&mut self) {
+ }
+}
+```
+
+Explicitly implementing both `Drop` and `Copy` trait on a type is currently
+disallowed. This feature can make some sense in theory, but the current
+implementation is incorrect and can lead to memory unsafety (see
+[issue #20126][iss20126]), so it has been disabled for now.
[iss20126]: https://github.com/rust-lang/rust/issues/20126
implementation of the trait declared the same function to be a method (i.e., to
take a `self` parameter).
-Here's an example of this error:
+Erroneous code example:
```compile_fail,E0185
trait Foo {
fn foo(&self) {}
}
```
+
+When a type implements a trait's associated function, it has to use the same
+signature. So in this case, since `Foo::foo` does not take any argument and
+does not return anything, its implementation on `Bar` should be the same:
+
+```
+trait Foo {
+ fn foo();
+}
+
+struct Bar;
+
+impl Foo for Bar {
+ fn foo() {} // ok!
+}
+```
`self` parameter), but an implementation of the trait declared the same function
to be static.
-Here's an example of this error:
+Erroneous code example:
```compile_fail,E0186
trait Foo {
fn foo() {}
}
```
+
+When a type implements a trait's associated function, it has to use the same
+signature. So in this case, since `Foo::foo` takes `self` as argument and
+does not return anything, its implementation on `Bar` should be the same:
+
+```
+trait Foo {
+ fn foo(&self);
+}
+
+struct Bar;
+
+impl Foo for Bar {
+ fn foo(&self) {} // ok!
+}
+```
}
```
-E0307 will be emitted by the compiler when using an invalid reciver type,
+E0307 will be emitted by the compiler when using an invalid receiver type,
like in the following example:
```compile_fail,E0307
may well have disappeared by the time we try to use them. Even if we call
`thr.join()` within foo (which blocks until `thr` has completed, ensuring the
stack frame won't disappear), we will not succeed: the compiler cannot prove
-that this behaviour is safe, and so won't let us do it.
+that this behavior is safe, and so won't let us do it.
The solution to this problem is usually to switch to using a `move` closure.
This approach moves (or copies, where possible) data into the closure, rather
}
```
-Please verify you spelt or declare the label correctly. Example:
+Please verify you spelled or declared the label correctly. Example:
```
'a: loop {
pub trait Bar : Foo {} // error: private trait in public interface
pub struct Bar2<T: Foo>(pub T); // same error
pub fn foo<T: Foo> (t: T) {} // same error
+
+fn main() {}
```
To solve this error, please ensure that the trait is also public. The trait
pub trait Bar : Foo {} // ok!
pub struct Bar2<T: Foo>(pub T); // ok!
pub fn foo<T: Foo> (t: T) {} // ok!
+
+fn main() {}
```
Bar(0)
}
}
+
+fn main() {}
```
To solve this error, please ensure that the type is also public. The type
Bar(0)
}
}
+
+fn main() {}
```
Erroneous code example:
```compile_fail,E0491
-trait SomeTrait<'a> {
- type Output;
+struct Foo<'a> {
+ x: fn(&'a i32),
}
-impl<'a, T> SomeTrait<'a> for T {
- type Output = &'a T; // compile error E0491
+trait Trait<'a, 'b> {
+ type Out;
+}
+
+impl<'a, 'b> Trait<'a, 'b> for usize {
+ type Out = &'a Foo<'b>; // error!
}
```
-Here, the problem is that a reference type like `&'a T` is only valid
-if all the data in T outlives the lifetime `'a`. But this impl as written
-is applicable to any lifetime `'a` and any type `T` -- we have no guarantee
-that `T` outlives `'a`. To fix this, you can add a where clause like
-`where T: 'a`.
+Here, the problem is that the compiler cannot be sure that the `'b` lifetime
+will live longer than `'a`, which should be mandatory in order to be sure that
+`Trait::Out` will always have a reference pointing to an existing type. So in
+this case, we just need to tell the compiler than `'b` must outlive `'a`:
```
-trait SomeTrait<'a> {
- type Output;
+struct Foo<'a> {
+ x: fn(&'a i32),
+}
+
+trait Trait<'a, 'b> {
+ type Out;
}
-impl<'a, T> SomeTrait<'a> for T
-where
- T: 'a,
-{
- type Output = &'a T; // compile error E0491
+impl<'a, 'b: 'a> Trait<'a, 'b> for usize { // we added the lifetime enforcement
+ type Out = &'a Foo<'b>; // it now works!
}
```
Erroneous code example:
-```
-#[repr(u32, u64)] // warning!
+```compile_fail,E0566
+#[repr(u32, u64)]
enum Repr { A }
```
}
}
}
+
+#[macro_export]
+macro_rules! struct_span_err {
+ ($session:expr, $span:expr, $code:ident, $($message:tt)*) => ({
+ $session.struct_span_err_with_code(
+ $span,
+ &format!($($message)*),
+ $crate::error_code!($code),
+ )
+ })
+}
+
+#[macro_export]
+macro_rules! error_code {
+ ($code:ident) => {{
+ let _ = $code;
+ $crate::DiagnosticId::Error(stringify!($code).to_owned())
+ }};
+}
// Find the bounding span.
let lo = substitution.parts.iter().map(|part| part.span.lo()).min().unwrap();
- let hi = substitution.parts.iter().map(|part| part.span.hi()).min().unwrap();
+ let hi = substitution.parts.iter().map(|part| part.span.hi()).max().unwrap();
let bounding_span = Span::with_root_ctxt(lo, hi);
let lines = cm.span_to_lines(bounding_span).unwrap();
assert!(!lines.lines.is_empty());
err_count: usize,
deduplicated_err_count: usize,
emitter: Box<dyn Emitter + sync::Send>,
- continue_after_error: bool,
delayed_span_bugs: Vec<Diagnostic>,
/// This set contains the `DiagnosticId` of all emitted diagnostics to avoid
err_count: 0,
deduplicated_err_count: 0,
emitter,
- continue_after_error: true,
delayed_span_bugs: Vec::new(),
taught_diagnostics: Default::default(),
emitted_diagnostic_codes: Default::default(),
}
}
- pub fn set_continue_after_error(&self, continue_after_error: bool) {
- self.inner.borrow_mut().continue_after_error = continue_after_error;
- }
-
// This is here to not allow mutation of flags;
// as of this writing it's only used in tests in librustc.
pub fn can_emit_warnings(&self) -> bool {
self.inner.borrow_mut().abort_if_errors()
}
- pub fn abort_if_errors_and_should_abort(&self) {
- self.inner.borrow_mut().abort_if_errors_and_should_abort()
- }
-
/// `true` if we haven't taught a diagnostic with this code already.
/// The caller must then teach the user about such a diagnostic.
///
fn emit_diag_at_span(&self, mut diag: Diagnostic, sp: impl Into<MultiSpan>) {
let mut inner = self.inner.borrow_mut();
inner.emit_diagnostic(diag.set_span(sp));
- inner.abort_if_errors_and_should_abort();
}
pub fn emit_artifact_notification(&self, path: &Path, artifact_type: &str) {
self.has_errors() || !self.delayed_span_bugs.is_empty()
}
- fn abort_if_errors_and_should_abort(&mut self) {
- self.emit_stashed_diagnostics();
-
- if self.has_errors() && !self.continue_after_error {
- FatalError.raise();
- }
- }
-
fn abort_if_errors(&mut self) {
self.emit_stashed_diagnostics();
fn emit_diag_at_span(&mut self, mut diag: Diagnostic, sp: impl Into<MultiSpan>) {
self.emit_diagnostic(diag.set_span(sp));
- self.abort_if_errors_and_should_abort();
}
fn delay_span_bug(&mut self, sp: impl Into<MultiSpan>, msg: &str) {
rustc_serialize = { path = "../libserialize", package = "serialize" }
log = "0.4"
rustc_span = { path = "../librustc_span" }
-errors = { path = "../librustc_errors", package = "rustc_errors" }
+rustc_ast_passes = { path = "../librustc_ast_passes" }
rustc_data_structures = { path = "../librustc_data_structures" }
+rustc_errors = { path = "../librustc_errors" }
rustc_feature = { path = "../librustc_feature" }
rustc_lexer = { path = "../librustc_lexer" }
rustc_parse = { path = "../librustc_parse" }
+rustc_session = { path = "../librustc_session" }
smallvec = { version = "1.0", features = ["union", "may_dangle"] }
syntax = { path = "../libsyntax" }
use crate::expand::{self, AstFragment, Invocation};
+use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::sync::{self, Lrc};
+use rustc_errors::{DiagnosticBuilder, DiagnosticId};
use rustc_parse::{self, parser, DirectoryOwnership, MACRO_ARGUMENTS};
use rustc_span::edition::Edition;
+use rustc_span::hygiene::{AstPass, ExpnData, ExpnId, ExpnKind};
use rustc_span::source_map::SourceMap;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
+use rustc_span::{FileName, MultiSpan, Span, DUMMY_SP};
+use smallvec::{smallvec, SmallVec};
use syntax::ast::{self, Attribute, Name, NodeId, PatKind};
use syntax::attr::{self, Deprecation, HasAttrs, Stability};
use syntax::mut_visit::{self, MutVisitor};
use syntax::tokenstream::{self, TokenStream};
use syntax::visit::Visitor;
-use errors::{DiagnosticBuilder, DiagnosticId};
-use rustc_data_structures::fx::FxHashMap;
-use rustc_data_structures::sync::{self, Lrc};
-use rustc_span::hygiene::{AstPass, ExpnData, ExpnId, ExpnKind};
-use rustc_span::{FileName, MultiSpan, Span, DUMMY_SP};
-use smallvec::{smallvec, SmallVec};
-
use std::default::Default;
use std::iter;
use std::path::PathBuf;
}
pub fn trait_ref(&self, path: ast::Path) -> ast::TraitRef {
- ast::TraitRef { path, ref_id: ast::DUMMY_NODE_ID }
+ ast::TraitRef { path, constness: None, ref_id: ast::DUMMY_NODE_ID }
}
pub fn poly_trait_ref(&self, span: Span, path: ast::Path) -> ast::PolyTraitRef {
use crate::placeholders::{placeholder, PlaceholderExpander};
use crate::proc_macro::collect_derives;
+use rustc_data_structures::sync::Lrc;
+use rustc_errors::{Applicability, FatalError, PResult};
use rustc_feature::Features;
use rustc_parse::configure;
use rustc_parse::parser::Parser;
use syntax::ast::{self, AttrItem, Block, Ident, LitKind, NodeId, PatKind, Path};
use syntax::ast::{ItemKind, MacArgs, MacStmtStyle, StmtKind};
use syntax::attr::{self, is_builtin_attr, HasAttrs};
-use syntax::feature_gate::{self, feature_err};
use syntax::mut_visit::*;
use syntax::print::pprust;
use syntax::ptr::P;
-use syntax::sess::ParseSess;
+use syntax::sess::{feature_err, ParseSess};
use syntax::token;
use syntax::tokenstream::{TokenStream, TokenTree};
use syntax::util::map_in_place::MapInPlace;
use syntax::visit::{self, Visitor};
-use errors::{Applicability, FatalError, PResult};
use smallvec::{smallvec, SmallVec};
-
-use rustc_data_structures::sync::Lrc;
use std::io::ErrorKind;
use std::ops::DerefMut;
use std::path::PathBuf;
fn check_attributes(&mut self, attrs: &[ast::Attribute]) {
let features = self.cx.ecfg.features.unwrap();
for attr in attrs.iter() {
- feature_gate::check_attribute(attr, self.cx.parse_sess, features);
+ rustc_ast_passes::feature_gate::check_attribute(attr, self.cx.parse_sess, features);
validate_attr::check_meta(self.cx.parse_sess, attr);
// macros are expanded before any lint passes so this warning has to be hardcoded
#[macro_export]
macro_rules! panictry {
($e:expr) => {{
- use errors::FatalError;
+ use rustc_errors::FatalError;
use std::result::Result::{Err, Ok};
match $e {
Ok(e) => e,
//! bound.
use crate::mbe::{KleeneToken, TokenTree};
+use rustc_data_structures::fx::FxHashMap;
+use rustc_session::lint::builtin::META_VARIABLE_MISUSE;
+use rustc_session::parse::ParseSess;
use rustc_span::symbol::{kw, sym};
+use rustc_span::{symbol::Ident, MultiSpan, Span};
use syntax::ast::NodeId;
-use syntax::early_buffered_lints::META_VARIABLE_MISUSE;
-use syntax::sess::ParseSess;
use syntax::token::{DelimToken, Token, TokenKind};
-use rustc_data_structures::fx::FxHashMap;
-use rustc_span::{symbol::Ident, MultiSpan, Span};
use smallvec::SmallVec;
/// Stack represented as linked list.
use syntax::token::{self, DocComment, Nonterminal, Token};
use syntax::tokenstream::TokenStream;
-use errors::{FatalError, PResult};
+use rustc_errors::{FatalError, PResult};
use rustc_span::Span;
use smallvec::{smallvec, SmallVec};
if parser.token.span.is_dummy() {
parser.token.span
} else {
- sess.source_map().next_point(parser.token.span)
+ parser.token.span.shrink_to_hi()
},
),
"missing tokens in macro arguments",
use crate::mbe::macro_parser::{MatchedNonterminal, MatchedSeq, NamedParseResult};
use crate::mbe::transcribe::transcribe;
+use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::sync::Lrc;
+use rustc_errors::{Applicability, DiagnosticBuilder, FatalError};
use rustc_feature::Features;
use rustc_parse::parser::Parser;
use rustc_parse::Directory;
use syntax::token::{self, NtTT, Token, TokenKind::*};
use syntax::tokenstream::{DelimSpan, TokenStream};
-use errors::{DiagnosticBuilder, FatalError};
use log::debug;
-
-use rustc_data_structures::fx::FxHashMap;
-use rustc_data_structures::sync::Lrc;
use std::borrow::Cow;
use std::collections::hash_map::Entry;
use std::{mem, slice};
-use errors::Applicability;
-
const VALID_FRAGMENT_NAMES_MSG: &str = "valid fragment specifiers are \
`ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, \
`literal`, `path`, `meta`, `tt`, `item` and `vis`";
use crate::mbe;
use crate::mbe::macro_parser::{MatchedNonterminal, MatchedSeq, NamedMatch};
+use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::sync::Lrc;
+use rustc_errors::pluralize;
+use rustc_span::hygiene::{ExpnId, Transparency};
+use rustc_span::Span;
use syntax::ast::{Ident, Mac};
use syntax::mut_visit::{self, MutVisitor};
use syntax::token::{self, NtTT, Token};
use syntax::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndJoint};
use smallvec::{smallvec, SmallVec};
-
-use errors::pluralize;
-use rustc_data_structures::fx::FxHashMap;
-use rustc_data_structures::sync::Lrc;
-use rustc_span::hygiene::{ExpnId, Transparency};
-use rustc_span::Span;
-
use std::mem;
// A Marker adds the given mark to the syntax context.
use rustc_data_structures::sync::Lrc;
+use rustc_errors::{emitter::EmitterWriter, Handler};
use rustc_parse::lexer::StringReader;
use rustc_span::source_map::{FilePathMapping, SourceMap};
use rustc_span::symbol::Symbol;
use syntax::util::comments::is_doc_comment;
use syntax::with_default_globals;
-use errors::{emitter::EmitterWriter, Handler};
use std::io;
use std::path::PathBuf;
use crate::tests::{matches_codepattern, string_to_stream, with_error_checking_parse};
-use errors::PResult;
+use rustc_errors::PResult;
use rustc_parse::new_parser_from_source_str;
use rustc_span::source_map::FilePathMapping;
use rustc_span::symbol::{kw, sym, Symbol};
use crate::base::{self, *};
use crate::proc_macro_server;
+use rustc_data_structures::sync::Lrc;
+use rustc_errors::{Applicability, FatalError};
use rustc_span::symbol::sym;
+use rustc_span::{Span, DUMMY_SP};
use syntax::ast::{self, ItemKind, MetaItemKind, NestedMetaItem};
-use syntax::errors::{Applicability, FatalError};
use syntax::token;
use syntax::tokenstream::{self, TokenStream};
-use rustc_data_structures::sync::Lrc;
-use rustc_span::{Span, DUMMY_SP};
-
const EXEC_STRATEGY: pm::bridge::server::SameThread = pm::bridge::server::SameThread;
pub struct BangProcMacro {
use crate::base::ExtCtxt;
+use rustc_data_structures::sync::Lrc;
+use rustc_errors::Diagnostic;
use rustc_parse::lexer::nfc_normalize;
use rustc_parse::{nt_to_tokenstream, parse_stream_from_source_str};
use rustc_span::symbol::{kw, sym, Symbol};
use syntax::tokenstream::{self, DelimSpan, IsJoint::*, TokenStream, TreeAndJoint};
use syntax::util::comments;
-use errors::Diagnostic;
-use rustc_data_structures::sync::Lrc;
-
use pm::bridge::{server, TokenTree};
use pm::{Delimiter, Level, LineColumn, Spacing};
use std::ops::Bound;
}
}
-impl ToInternal<errors::Level> for Level {
- fn to_internal(self) -> errors::Level {
+impl ToInternal<rustc_errors::Level> for Level {
+ fn to_internal(self) -> rustc_errors::Level {
match self {
- Level::Error => errors::Level::Error,
- Level::Warning => errors::Level::Warning,
- Level::Note => errors::Level::Note,
- Level::Help => errors::Level::Help,
+ Level::Error => rustc_errors::Level::Error,
+ Level::Warning => rustc_errors::Level::Warning,
+ Level::Note => rustc_errors::Level::Note,
+ Level::Help => rustc_errors::Level::Help,
_ => unreachable!("unknown proc_macro::Level variant: {:?}", self),
}
}
use syntax::tokenstream::TokenStream;
use syntax::with_default_globals;
-use errors::emitter::EmitterWriter;
-use errors::{Handler, PResult};
use rustc_data_structures::sync::Lrc;
+use rustc_errors::emitter::EmitterWriter;
+use rustc_errors::{Handler, PResult};
use std::io;
use std::io::prelude::*;
/// Allows using the `unadjusted` ABI; perma-unstable.
(active, abi_unadjusted, "1.16.0", None, None),
- /// Allows identifying crates that contain sanitizer runtimes.
- (active, sanitizer_runtime, "1.17.0", None, None),
-
/// Used to identify crates that contain the profiler runtime.
(active, profiler_runtime, "1.18.0", None, None),
/// Allows the use of `#[cfg(sanitize = "option")]`; set when -Zsanitizer is used.
(active, cfg_sanitize, "1.41.0", Some(39699), None),
+ /// Allows using `..X`, `..=X`, `...X`, and `X..` as a pattern.
+ (active, half_open_range_patterns, "1.41.0", Some(67264), None),
+
/// Allows using `&mut` in constant functions.
(active, const_mut_refs, "1.41.0", Some(57349), None),
/// For example, you can write `x @ Some(y)`.
(active, bindings_after_at, "1.41.0", Some(65490), None),
+ /// Allows `impl const Trait for T` syntax.
+ (active, const_trait_impl, "1.42.0", Some(67792), None),
+
+ /// Allows `T: ?const Trait` syntax in bounds.
+ (active, const_trait_bound_opt_out, "1.42.0", Some(67794), None),
+
// -------------------------------------------------------------------------
// feature-group-end: actual feature gates
// -------------------------------------------------------------------------
sym::or_patterns,
sym::let_chains,
sym::raw_dylib,
+ sym::const_trait_impl,
+ sym::const_trait_bound_opt_out,
];
"the `#[compiler_builtins]` attribute is used to identify the `compiler_builtins` crate \
which contains compiler-rt intrinsics and will never be stable",
),
- gated!(
- sanitizer_runtime, Whitelisted, template!(Word),
- "the `#[sanitizer_runtime]` attribute is used to identify crates that contain the runtime \
- of a sanitizer and will never be stable",
- ),
gated!(
profiler_runtime, Whitelisted, template!(Word),
"the `#[profiler_runtime]` attribute is used to identify the `profiler_builtins` crate \
//! # Feature gates
//!
//! This crate declares the set of past and present unstable features in the compiler.
-//! Feature gate checking itself is done in `libsyntax/feature_gate/check.rs` at the moment.
+//! Feature gate checking itself is done in `librustc_ast_passes/feature_gate.rs`
+//! at the moment.
//!
//! Features are enabled in programs via the crate-level attributes of
//! `#![feature(...)]` with a comma-separated list of features.
(removed, pushpop_unsafe, "1.2.0", None, None, None),
(removed, needs_allocator, "1.4.0", Some(27389), None,
Some("subsumed by `#![feature(allocator_internals)]`")),
+ /// Allows identifying crates that contain sanitizer runtimes.
+ (removed, sanitizer_runtime, "1.17.0", None, None, None),
(removed, proc_macro_mod, "1.27.0", Some(54727), None,
Some("subsumed by `#![feature(proc_macro_hygiene)]`")),
(removed, proc_macro_expr, "1.27.0", Some(54727), None,
Lit(&'hir Expr<'hir>),
/// A range pattern (e.g., `1..=2` or `1..2`).
- Range(&'hir Expr<'hir>, &'hir Expr<'hir>, RangeEnd),
+ Range(Option<&'hir Expr<'hir>>, Option<&'hir Expr<'hir>>, RangeEnd),
/// A slice pattern, `[before_0, ..., before_n, (slice, after_0, ..., after_n)?]`.
///
OpaqueTy(GenericBounds<'hir>),
}
+// The name of the associated type for `Fn` return types.
+pub const FN_OUTPUT_NAME: Symbol = sym::Output;
+
/// Bind a type to an associated type (i.e., `A = Foo`).
///
/// Bindings like `A: Debug` are represented as a special type `A =
--- /dev/null
+//! HIR walker for walking the contents of nodes.
+//!
+//! **For an overview of the visitor strategy, see the docs on the
+//! `super::itemlikevisit::ItemLikeVisitor` trait.**
+//!
+//! If you have decided to use this visitor, here are some general
+//! notes on how to do so:
+//!
+//! Each overridden visit method has full control over what
+//! happens with its node, it can do its own traversal of the node's children,
+//! call `intravisit::walk_*` to apply the default traversal algorithm, or prevent
+//! deeper traversal by doing nothing.
+//!
+//! When visiting the HIR, the contents of nested items are NOT visited
+//! by default. This is different from the AST visitor, which does a deep walk.
+//! Hence this module is called `intravisit`; see the method `visit_nested_item`
+//! for more details.
+//!
+//! Note: it is an important invariant that the default visitor walks
+//! the body of a function in "execution order" - more concretely, if
+//! we consider the reverse post-order (RPO) of the CFG implied by the HIR,
+//! then a pre-order traversal of the HIR is consistent with the CFG RPO
+//! on the *initial CFG point* of each HIR node, while a post-order traversal
+//! of the HIR is consistent with the CFG RPO on each *final CFG point* of
+//! each CFG node.
+//!
+//! One thing that follows is that if HIR node A always starts/ends executing
+//! before HIR node B, then A appears in traversal pre/postorder before B,
+//! respectively. (This follows from RPO respecting CFG domination).
+//!
+//! This order consistency is required in a few places in rustc, for
+//! example generator inference, and possibly also HIR borrowck.
+
+use crate::hir::*;
+use crate::hir_id::CRATE_HIR_ID;
+use crate::itemlikevisit::{ItemLikeVisitor, ParItemLikeVisitor};
+use rustc_span::Span;
+use syntax::ast::{Attribute, Ident, Label, Name};
+use syntax::walk_list;
+
+pub struct DeepVisitor<'v, V> {
+ visitor: &'v mut V,
+}
+
+impl<'v, V> DeepVisitor<'v, V> {
+ pub fn new(base: &'v mut V) -> Self {
+ DeepVisitor { visitor: base }
+ }
+}
+
+impl<'v, 'hir, V> ItemLikeVisitor<'hir> for DeepVisitor<'v, V>
+where
+ V: Visitor<'hir>,
+{
+ fn visit_item(&mut self, item: &'hir Item<'hir>) {
+ self.visitor.visit_item(item);
+ }
+
+ fn visit_trait_item(&mut self, trait_item: &'hir TraitItem<'hir>) {
+ self.visitor.visit_trait_item(trait_item);
+ }
+
+ fn visit_impl_item(&mut self, impl_item: &'hir ImplItem<'hir>) {
+ self.visitor.visit_impl_item(impl_item);
+ }
+}
+
+pub trait IntoVisitor<'hir> {
+ type Visitor: Visitor<'hir>;
+ fn into_visitor(&self) -> Self::Visitor;
+}
+
+pub struct ParDeepVisitor<V>(pub V);
+
+impl<'hir, V> ParItemLikeVisitor<'hir> for ParDeepVisitor<V>
+where
+ V: IntoVisitor<'hir>,
+{
+ fn visit_item(&self, item: &'hir Item<'hir>) {
+ self.0.into_visitor().visit_item(item);
+ }
+
+ fn visit_trait_item(&self, trait_item: &'hir TraitItem<'hir>) {
+ self.0.into_visitor().visit_trait_item(trait_item);
+ }
+
+ fn visit_impl_item(&self, impl_item: &'hir ImplItem<'hir>) {
+ self.0.into_visitor().visit_impl_item(impl_item);
+ }
+}
+
+#[derive(Copy, Clone)]
+pub enum FnKind<'a> {
+ /// `#[xxx] pub async/const/extern "Abi" fn foo()`
+ ItemFn(Ident, &'a Generics<'a>, FnHeader, &'a Visibility<'a>, &'a [Attribute]),
+
+ /// `fn foo(&self)`
+ Method(Ident, &'a FnSig<'a>, Option<&'a Visibility<'a>>, &'a [Attribute]),
+
+ /// `|x, y| {}`
+ Closure(&'a [Attribute]),
+}
+
+impl<'a> FnKind<'a> {
+ pub fn attrs(&self) -> &'a [Attribute] {
+ match *self {
+ FnKind::ItemFn(.., attrs) => attrs,
+ FnKind::Method(.., attrs) => attrs,
+ FnKind::Closure(attrs) => attrs,
+ }
+ }
+
+ pub fn header(&self) -> Option<&FnHeader> {
+ match *self {
+ FnKind::ItemFn(_, _, ref header, _, _) => Some(header),
+ FnKind::Method(_, ref sig, _, _) => Some(&sig.header),
+ FnKind::Closure(_) => None,
+ }
+ }
+}
+
+/// An abstract representation of the HIR `rustc::hir::map::Map`.
+pub trait Map<'hir> {
+ fn body(&self, id: BodyId) -> &'hir Body<'hir>;
+ fn item(&self, id: HirId) -> &'hir Item<'hir>;
+ fn trait_item(&self, id: TraitItemId) -> &'hir TraitItem<'hir>;
+ fn impl_item(&self, id: ImplItemId) -> &'hir ImplItem<'hir>;
+}
+
+/// Specifies what nested things a visitor wants to visit. The most
+/// common choice is `OnlyBodies`, which will cause the visitor to
+/// visit fn bodies for fns that it encounters, but skip over nested
+/// item-like things.
+///
+/// See the comments on `ItemLikeVisitor` for more details on the overall
+/// visit strategy.
+pub enum NestedVisitorMap<'this, M> {
+ /// Do not visit any nested things. When you add a new
+ /// "non-nested" thing, you will want to audit such uses to see if
+ /// they remain valid.
+ ///
+ /// Use this if you are only walking some particular kind of tree
+ /// (i.e., a type, or fn signature) and you don't want to thread a
+ /// HIR map around.
+ None,
+
+ /// Do not visit nested item-like things, but visit nested things
+ /// that are inside of an item-like.
+ ///
+ /// **This is the most common choice.** A very common pattern is
+ /// to use `visit_all_item_likes()` as an outer loop,
+ /// and to have the visitor that visits the contents of each item
+ /// using this setting.
+ OnlyBodies(&'this M),
+
+ /// Visits all nested things, including item-likes.
+ ///
+ /// **This is an unusual choice.** It is used when you want to
+ /// process everything within their lexical context. Typically you
+ /// kick off the visit by doing `walk_krate()`.
+ All(&'this M),
+}
+
+impl<'this, M> NestedVisitorMap<'this, M> {
+ /// Returns the map to use for an "intra item-like" thing (if any).
+ /// E.g., function body.
+ fn intra(self) -> Option<&'this M> {
+ match self {
+ NestedVisitorMap::None => None,
+ NestedVisitorMap::OnlyBodies(map) => Some(map),
+ NestedVisitorMap::All(map) => Some(map),
+ }
+ }
+
+ /// Returns the map to use for an "item-like" thing (if any).
+ /// E.g., item, impl-item.
+ fn inter(self) -> Option<&'this M> {
+ match self {
+ NestedVisitorMap::None => None,
+ NestedVisitorMap::OnlyBodies(_) => None,
+ NestedVisitorMap::All(map) => Some(map),
+ }
+ }
+}
+
+/// Each method of the Visitor trait is a hook to be potentially
+/// overridden. Each method's default implementation recursively visits
+/// the substructure of the input via the corresponding `walk` method;
+/// e.g., the `visit_mod` method by default calls `intravisit::walk_mod`.
+///
+/// Note that this visitor does NOT visit nested items by default
+/// (this is why the module is called `intravisit`, to distinguish it
+/// from the AST's `visit` module, which acts differently). If you
+/// simply want to visit all items in the crate in some order, you
+/// should call `Crate::visit_all_items`. Otherwise, see the comment
+/// on `visit_nested_item` for details on how to visit nested items.
+///
+/// If you want to ensure that your code handles every variant
+/// explicitly, you need to override each method. (And you also need
+/// to monitor future changes to `Visitor` in case a new method with a
+/// new default implementation gets introduced.)
+pub trait Visitor<'v>: Sized {
+ type Map: Map<'v>;
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Nested items.
+
+ /// The default versions of the `visit_nested_XXX` routines invoke
+ /// this method to get a map to use. By selecting an enum variant,
+ /// you control which kinds of nested HIR are visited; see
+ /// `NestedVisitorMap` for details. By "nested HIR", we are
+ /// referring to bits of HIR that are not directly embedded within
+ /// one another but rather indirectly, through a table in the
+ /// crate. This is done to control dependencies during incremental
+ /// compilation: the non-inline bits of HIR can be tracked and
+ /// hashed separately.
+ ///
+ /// **If for some reason you want the nested behavior, but don't
+ /// have a `Map` at your disposal:** then you should override the
+ /// `visit_nested_XXX` methods, and override this method to
+ /// `panic!()`. This way, if a new `visit_nested_XXX` variant is
+ /// added in the future, we will see the panic in your code and
+ /// fix it appropriately.
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map>;
+
+ /// Invoked when a nested item is encountered. By default does
+ /// nothing unless you override `nested_visit_map` to return other than
+ /// `None`, in which case it will walk the item. **You probably
+ /// don't want to override this method** -- instead, override
+ /// `nested_visit_map` or use the "shallow" or "deep" visit
+ /// patterns described on `itemlikevisit::ItemLikeVisitor`. The only
+ /// reason to override this method is if you want a nested pattern
+ /// but cannot supply a `Map`; see `nested_visit_map` for advice.
+ #[allow(unused_variables)]
+ fn visit_nested_item(&mut self, id: ItemId) {
+ let opt_item = self.nested_visit_map().inter().map(|map| map.item(id.id));
+ walk_list!(self, visit_item, opt_item);
+ }
+
+ /// Like `visit_nested_item()`, but for trait items. See
+ /// `visit_nested_item()` for advice on when to override this
+ /// method.
+ #[allow(unused_variables)]
+ fn visit_nested_trait_item(&mut self, id: TraitItemId) {
+ let opt_item = self.nested_visit_map().inter().map(|map| map.trait_item(id));
+ walk_list!(self, visit_trait_item, opt_item);
+ }
+
+ /// Like `visit_nested_item()`, but for impl items. See
+ /// `visit_nested_item()` for advice on when to override this
+ /// method.
+ #[allow(unused_variables)]
+ fn visit_nested_impl_item(&mut self, id: ImplItemId) {
+ let opt_item = self.nested_visit_map().inter().map(|map| map.impl_item(id));
+ walk_list!(self, visit_impl_item, opt_item);
+ }
+
+ /// Invoked to visit the body of a function, method or closure. Like
+ /// visit_nested_item, does nothing by default unless you override
+ /// `nested_visit_map` to return other than `None`, in which case it will walk
+ /// the body.
+ fn visit_nested_body(&mut self, id: BodyId) {
+ let opt_body = self.nested_visit_map().intra().map(|map| map.body(id));
+ walk_list!(self, visit_body, opt_body);
+ }
+
+ fn visit_param(&mut self, param: &'v Param<'v>) {
+ walk_param(self, param)
+ }
+
+ /// Visits the top-level item and (optionally) nested items / impl items. See
+ /// `visit_nested_item` for details.
+ fn visit_item(&mut self, i: &'v Item<'v>) {
+ walk_item(self, i)
+ }
+
+ fn visit_body(&mut self, b: &'v Body<'v>) {
+ walk_body(self, b);
+ }
+
+ /// When invoking `visit_all_item_likes()`, you need to supply an
+ /// item-like visitor. This method converts a "intra-visit"
+ /// visitor into an item-like visitor that walks the entire tree.
+ /// If you use this, you probably don't want to process the
+ /// contents of nested item-like things, since the outer loop will
+ /// visit them as well.
+ fn as_deep_visitor<'s>(&'s mut self) -> DeepVisitor<'s, Self> {
+ DeepVisitor::new(self)
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ fn visit_id(&mut self, _hir_id: HirId) {
+ // Nothing to do.
+ }
+ fn visit_name(&mut self, _span: Span, _name: Name) {
+ // Nothing to do.
+ }
+ fn visit_ident(&mut self, ident: Ident) {
+ walk_ident(self, ident)
+ }
+ fn visit_mod(&mut self, m: &'v Mod<'v>, _s: Span, n: HirId) {
+ walk_mod(self, m, n)
+ }
+ fn visit_foreign_item(&mut self, i: &'v ForeignItem<'v>) {
+ walk_foreign_item(self, i)
+ }
+ fn visit_local(&mut self, l: &'v Local<'v>) {
+ walk_local(self, l)
+ }
+ fn visit_block(&mut self, b: &'v Block<'v>) {
+ walk_block(self, b)
+ }
+ fn visit_stmt(&mut self, s: &'v Stmt<'v>) {
+ walk_stmt(self, s)
+ }
+ fn visit_arm(&mut self, a: &'v Arm<'v>) {
+ walk_arm(self, a)
+ }
+ fn visit_pat(&mut self, p: &'v Pat<'v>) {
+ walk_pat(self, p)
+ }
+ fn visit_anon_const(&mut self, c: &'v AnonConst) {
+ walk_anon_const(self, c)
+ }
+ fn visit_expr(&mut self, ex: &'v Expr<'v>) {
+ walk_expr(self, ex)
+ }
+ fn visit_ty(&mut self, t: &'v Ty<'v>) {
+ walk_ty(self, t)
+ }
+ fn visit_generic_param(&mut self, p: &'v GenericParam<'v>) {
+ walk_generic_param(self, p)
+ }
+ fn visit_generics(&mut self, g: &'v Generics<'v>) {
+ walk_generics(self, g)
+ }
+ fn visit_where_predicate(&mut self, predicate: &'v WherePredicate<'v>) {
+ walk_where_predicate(self, predicate)
+ }
+ fn visit_fn_decl(&mut self, fd: &'v FnDecl<'v>) {
+ walk_fn_decl(self, fd)
+ }
+ fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl<'v>, b: BodyId, s: Span, id: HirId) {
+ walk_fn(self, fk, fd, b, s, id)
+ }
+ fn visit_use(&mut self, path: &'v Path<'v>, hir_id: HirId) {
+ walk_use(self, path, hir_id)
+ }
+ fn visit_trait_item(&mut self, ti: &'v TraitItem<'v>) {
+ walk_trait_item(self, ti)
+ }
+ fn visit_trait_item_ref(&mut self, ii: &'v TraitItemRef) {
+ walk_trait_item_ref(self, ii)
+ }
+ fn visit_impl_item(&mut self, ii: &'v ImplItem<'v>) {
+ walk_impl_item(self, ii)
+ }
+ fn visit_impl_item_ref(&mut self, ii: &'v ImplItemRef<'v>) {
+ walk_impl_item_ref(self, ii)
+ }
+ fn visit_trait_ref(&mut self, t: &'v TraitRef<'v>) {
+ walk_trait_ref(self, t)
+ }
+ fn visit_param_bound(&mut self, bounds: &'v GenericBound<'v>) {
+ walk_param_bound(self, bounds)
+ }
+ fn visit_poly_trait_ref(&mut self, t: &'v PolyTraitRef<'v>, m: TraitBoundModifier) {
+ walk_poly_trait_ref(self, t, m)
+ }
+ fn visit_variant_data(
+ &mut self,
+ s: &'v VariantData<'v>,
+ _: Name,
+ _: &'v Generics<'v>,
+ _parent_id: HirId,
+ _: Span,
+ ) {
+ walk_struct_def(self, s)
+ }
+ fn visit_struct_field(&mut self, s: &'v StructField<'v>) {
+ walk_struct_field(self, s)
+ }
+ fn visit_enum_def(
+ &mut self,
+ enum_definition: &'v EnumDef<'v>,
+ generics: &'v Generics<'v>,
+ item_id: HirId,
+ _: Span,
+ ) {
+ walk_enum_def(self, enum_definition, generics, item_id)
+ }
+ fn visit_variant(&mut self, v: &'v Variant<'v>, g: &'v Generics<'v>, item_id: HirId) {
+ walk_variant(self, v, g, item_id)
+ }
+ fn visit_label(&mut self, label: &'v Label) {
+ walk_label(self, label)
+ }
+ fn visit_generic_arg(&mut self, generic_arg: &'v GenericArg<'v>) {
+ 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),
+ }
+ }
+ fn visit_lifetime(&mut self, lifetime: &'v Lifetime) {
+ walk_lifetime(self, lifetime)
+ }
+ fn visit_qpath(&mut self, qpath: &'v QPath<'v>, id: HirId, span: Span) {
+ walk_qpath(self, qpath, id, span)
+ }
+ fn visit_path(&mut self, path: &'v Path<'v>, _id: HirId) {
+ walk_path(self, path)
+ }
+ fn visit_path_segment(&mut self, path_span: Span, path_segment: &'v PathSegment<'v>) {
+ walk_path_segment(self, path_span, path_segment)
+ }
+ fn visit_generic_args(&mut self, path_span: Span, generic_args: &'v GenericArgs<'v>) {
+ walk_generic_args(self, path_span, generic_args)
+ }
+ fn visit_assoc_type_binding(&mut self, type_binding: &'v TypeBinding<'v>) {
+ walk_assoc_type_binding(self, type_binding)
+ }
+ fn visit_attribute(&mut self, _attr: &'v Attribute) {}
+ fn visit_macro_def(&mut self, macro_def: &'v MacroDef<'v>) {
+ walk_macro_def(self, macro_def)
+ }
+ fn visit_vis(&mut self, vis: &'v Visibility<'v>) {
+ walk_vis(self, vis)
+ }
+ fn visit_associated_item_kind(&mut self, kind: &'v AssocItemKind) {
+ walk_associated_item_kind(self, kind);
+ }
+ fn visit_defaultness(&mut self, defaultness: &'v Defaultness) {
+ walk_defaultness(self, defaultness);
+ }
+}
+
+/// Walks the contents of a crate. See also `Crate::visit_all_items`.
+pub fn walk_crate<'v, V: Visitor<'v>>(visitor: &mut V, krate: &'v Crate<'v>) {
+ visitor.visit_mod(&krate.module, krate.span, CRATE_HIR_ID);
+ walk_list!(visitor, visit_attribute, krate.attrs);
+ walk_list!(visitor, visit_macro_def, krate.exported_macros);
+}
+
+pub fn walk_macro_def<'v, V: Visitor<'v>>(visitor: &mut V, macro_def: &'v MacroDef<'v>) {
+ visitor.visit_id(macro_def.hir_id);
+ visitor.visit_name(macro_def.span, macro_def.name);
+ walk_list!(visitor, visit_attribute, macro_def.attrs);
+}
+
+pub fn walk_mod<'v, V: Visitor<'v>>(visitor: &mut V, module: &'v Mod<'v>, mod_hir_id: HirId) {
+ visitor.visit_id(mod_hir_id);
+ for &item_id in module.item_ids {
+ visitor.visit_nested_item(item_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);
+}
+
+pub fn walk_local<'v, V: Visitor<'v>>(visitor: &mut V, local: &'v Local<'v>) {
+ // Intentionally visiting the expr first - the initialization expr
+ // dominates the local's definition.
+ walk_list!(visitor, visit_expr, &local.init);
+ walk_list!(visitor, visit_attribute, local.attrs.iter());
+ visitor.visit_id(local.hir_id);
+ visitor.visit_pat(&local.pat);
+ walk_list!(visitor, visit_ty, &local.ty);
+}
+
+pub fn walk_ident<'v, V: Visitor<'v>>(visitor: &mut V, ident: Ident) {
+ visitor.visit_name(ident.span, ident.name);
+}
+
+pub fn walk_label<'v, V: Visitor<'v>>(visitor: &mut V, label: &'v Label) {
+ visitor.visit_ident(label.ident);
+}
+
+pub fn walk_lifetime<'v, V: Visitor<'v>>(visitor: &mut V, lifetime: &'v Lifetime) {
+ visitor.visit_id(lifetime.hir_id);
+ match lifetime.name {
+ LifetimeName::Param(ParamName::Plain(ident)) => {
+ visitor.visit_ident(ident);
+ }
+ LifetimeName::Param(ParamName::Fresh(_))
+ | LifetimeName::Param(ParamName::Error)
+ | LifetimeName::Static
+ | LifetimeName::Error
+ | LifetimeName::Implicit
+ | LifetimeName::ImplicitObjectLifetimeDefault
+ | LifetimeName::Underscore => {}
+ }
+}
+
+pub fn walk_poly_trait_ref<'v, V: Visitor<'v>>(
+ visitor: &mut V,
+ trait_ref: &'v PolyTraitRef<'v>,
+ _modifier: TraitBoundModifier,
+) {
+ walk_list!(visitor, visit_generic_param, trait_ref.bound_generic_params);
+ visitor.visit_trait_ref(&trait_ref.trait_ref);
+}
+
+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)
+}
+
+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);
+ walk_list!(visitor, visit_attribute, param.attrs);
+}
+
+pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) {
+ visitor.visit_vis(&item.vis);
+ visitor.visit_ident(item.ident);
+ match item.kind {
+ ItemKind::ExternCrate(orig_name) => {
+ visitor.visit_id(item.hir_id);
+ if let Some(orig_name) = orig_name {
+ visitor.visit_name(item.span, orig_name);
+ }
+ }
+ ItemKind::Use(ref path, _) => {
+ visitor.visit_use(path, item.hir_id);
+ }
+ ItemKind::Static(ref typ, _, body) | ItemKind::Const(ref typ, body) => {
+ visitor.visit_id(item.hir_id);
+ visitor.visit_ty(typ);
+ visitor.visit_nested_body(body);
+ }
+ ItemKind::Fn(ref sig, ref generics, body_id) => visitor.visit_fn(
+ FnKind::ItemFn(item.ident, generics, sig.header, &item.vis, &item.attrs),
+ &sig.decl,
+ body_id,
+ item.span,
+ item.hir_id,
+ ),
+ ItemKind::Mod(ref module) => {
+ // `visit_mod()` takes care of visiting the `Item`'s `HirId`.
+ visitor.visit_mod(module, item.span, item.hir_id)
+ }
+ ItemKind::ForeignMod(ref foreign_module) => {
+ visitor.visit_id(item.hir_id);
+ walk_list!(visitor, visit_foreign_item, foreign_module.items);
+ }
+ ItemKind::GlobalAsm(_) => {
+ visitor.visit_id(item.hir_id);
+ }
+ ItemKind::TyAlias(ref ty, ref generics) => {
+ visitor.visit_id(item.hir_id);
+ visitor.visit_ty(ty);
+ visitor.visit_generics(generics)
+ }
+ ItemKind::OpaqueTy(OpaqueTy { ref generics, bounds, .. }) => {
+ visitor.visit_id(item.hir_id);
+ walk_generics(visitor, generics);
+ walk_list!(visitor, visit_param_bound, bounds);
+ }
+ ItemKind::Enum(ref enum_definition, ref generics) => {
+ visitor.visit_generics(generics);
+ // `visit_enum_def()` takes care of visiting the `Item`'s `HirId`.
+ visitor.visit_enum_def(enum_definition, generics, item.hir_id, item.span)
+ }
+ ItemKind::Impl(.., ref generics, ref opt_trait_reference, ref typ, impl_item_refs) => {
+ visitor.visit_id(item.hir_id);
+ visitor.visit_generics(generics);
+ walk_list!(visitor, visit_trait_ref, opt_trait_reference);
+ visitor.visit_ty(typ);
+ walk_list!(visitor, visit_impl_item_ref, impl_item_refs);
+ }
+ ItemKind::Struct(ref struct_definition, ref generics)
+ | ItemKind::Union(ref struct_definition, ref generics) => {
+ visitor.visit_generics(generics);
+ visitor.visit_id(item.hir_id);
+ visitor.visit_variant_data(
+ struct_definition,
+ item.ident.name,
+ generics,
+ item.hir_id,
+ item.span,
+ );
+ }
+ ItemKind::Trait(.., ref generics, bounds, trait_item_refs) => {
+ visitor.visit_id(item.hir_id);
+ visitor.visit_generics(generics);
+ walk_list!(visitor, visit_param_bound, bounds);
+ walk_list!(visitor, visit_trait_item_ref, trait_item_refs);
+ }
+ ItemKind::TraitAlias(ref generics, bounds) => {
+ visitor.visit_id(item.hir_id);
+ visitor.visit_generics(generics);
+ walk_list!(visitor, visit_param_bound, bounds);
+ }
+ }
+ walk_list!(visitor, visit_attribute, item.attrs);
+}
+
+pub fn walk_use<'v, V: Visitor<'v>>(visitor: &mut V, path: &'v Path<'v>, hir_id: HirId) {
+ visitor.visit_id(hir_id);
+ visitor.visit_path(path, hir_id);
+}
+
+pub fn walk_enum_def<'v, V: Visitor<'v>>(
+ visitor: &mut V,
+ enum_definition: &'v EnumDef<'v>,
+ generics: &'v Generics<'v>,
+ item_id: HirId,
+) {
+ visitor.visit_id(item_id);
+ walk_list!(visitor, visit_variant, enum_definition.variants, generics, item_id);
+}
+
+pub fn walk_variant<'v, V: Visitor<'v>>(
+ visitor: &mut V,
+ variant: &'v Variant<'v>,
+ generics: &'v Generics<'v>,
+ parent_item_id: HirId,
+) {
+ visitor.visit_ident(variant.ident);
+ visitor.visit_id(variant.id);
+ visitor.visit_variant_data(
+ &variant.data,
+ variant.ident.name,
+ generics,
+ parent_item_id,
+ variant.span,
+ );
+ walk_list!(visitor, visit_anon_const, &variant.disr_expr);
+ walk_list!(visitor, visit_attribute, variant.attrs);
+}
+
+pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) {
+ visitor.visit_id(typ.hir_id);
+
+ match typ.kind {
+ TyKind::Slice(ref ty) => visitor.visit_ty(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)
+ }
+ TyKind::Never => {}
+ TyKind::Tup(tuple_element_types) => {
+ walk_list!(visitor, visit_ty, 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);
+ }
+ TyKind::Path(ref qpath) => {
+ visitor.visit_qpath(qpath, typ.hir_id, typ.span);
+ }
+ TyKind::Def(item_id, lifetimes) => {
+ visitor.visit_nested_item(item_id);
+ walk_list!(visitor, visit_generic_arg, lifetimes);
+ }
+ TyKind::Array(ref ty, ref length) => {
+ visitor.visit_ty(ty);
+ visitor.visit_anon_const(length)
+ }
+ TyKind::TraitObject(bounds, ref lifetime) => {
+ for bound in bounds {
+ visitor.visit_poly_trait_ref(bound, TraitBoundModifier::None);
+ }
+ visitor.visit_lifetime(lifetime);
+ }
+ TyKind::Typeof(ref expression) => visitor.visit_anon_const(expression),
+ TyKind::Infer | TyKind::Err => {}
+ }
+}
+
+pub fn walk_qpath<'v, V: Visitor<'v>>(
+ visitor: &mut V,
+ qpath: &'v QPath<'v>,
+ id: HirId,
+ span: Span,
+) {
+ match *qpath {
+ QPath::Resolved(ref maybe_qself, ref path) => {
+ walk_list!(visitor, visit_ty, maybe_qself);
+ visitor.visit_path(path, id)
+ }
+ QPath::TypeRelative(ref qself, ref segment) => {
+ visitor.visit_ty(qself);
+ visitor.visit_path_segment(span, segment);
+ }
+ }
+}
+
+pub fn walk_path<'v, V: Visitor<'v>>(visitor: &mut V, path: &'v Path<'v>) {
+ for segment in path.segments {
+ visitor.visit_path_segment(path.span, segment);
+ }
+}
+
+pub fn walk_path_segment<'v, V: Visitor<'v>>(
+ visitor: &mut V,
+ path_span: Span,
+ segment: &'v PathSegment<'v>,
+) {
+ visitor.visit_ident(segment.ident);
+ walk_list!(visitor, visit_id, segment.hir_id);
+ if let Some(ref args) = segment.args {
+ visitor.visit_generic_args(path_span, args);
+ }
+}
+
+pub fn walk_generic_args<'v, V: Visitor<'v>>(
+ visitor: &mut V,
+ _path_span: Span,
+ generic_args: &'v GenericArgs<'v>,
+) {
+ walk_list!(visitor, visit_generic_arg, generic_args.args);
+ walk_list!(visitor, visit_assoc_type_binding, generic_args.bindings);
+}
+
+pub fn walk_assoc_type_binding<'v, V: Visitor<'v>>(
+ visitor: &mut V,
+ type_binding: &'v TypeBinding<'v>,
+) {
+ visitor.visit_id(type_binding.hir_id);
+ visitor.visit_ident(type_binding.ident);
+ match type_binding.kind {
+ TypeBindingKind::Equality { ref ty } => {
+ visitor.visit_ty(ty);
+ }
+ TypeBindingKind::Constraint { bounds } => {
+ walk_list!(visitor, visit_param_bound, bounds);
+ }
+ }
+}
+
+pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat<'v>) {
+ visitor.visit_id(pattern.hir_id);
+ match pattern.kind {
+ PatKind::TupleStruct(ref qpath, children, _) => {
+ visitor.visit_qpath(qpath, pattern.hir_id, pattern.span);
+ walk_list!(visitor, visit_pat, children);
+ }
+ PatKind::Path(ref qpath) => {
+ visitor.visit_qpath(qpath, pattern.hir_id, pattern.span);
+ }
+ PatKind::Struct(ref qpath, fields, _) => {
+ visitor.visit_qpath(qpath, pattern.hir_id, pattern.span);
+ for field in fields {
+ visitor.visit_id(field.hir_id);
+ visitor.visit_ident(field.ident);
+ visitor.visit_pat(&field.pat)
+ }
+ }
+ PatKind::Or(pats) => walk_list!(visitor, visit_pat, pats),
+ PatKind::Tuple(tuple_elements, _) => {
+ walk_list!(visitor, visit_pat, tuple_elements);
+ }
+ PatKind::Box(ref subpattern) | PatKind::Ref(ref subpattern, _) => {
+ visitor.visit_pat(subpattern)
+ }
+ PatKind::Binding(_, _hir_id, ident, ref optional_subpattern) => {
+ visitor.visit_ident(ident);
+ walk_list!(visitor, visit_pat, optional_subpattern);
+ }
+ PatKind::Lit(ref expression) => visitor.visit_expr(expression),
+ PatKind::Range(ref lower_bound, ref upper_bound, _) => {
+ walk_list!(visitor, visit_expr, lower_bound);
+ walk_list!(visitor, visit_expr, upper_bound);
+ }
+ PatKind::Wild => (),
+ PatKind::Slice(prepatterns, ref slice_pattern, postpatterns) => {
+ walk_list!(visitor, visit_pat, prepatterns);
+ walk_list!(visitor, visit_pat, slice_pattern);
+ walk_list!(visitor, visit_pat, postpatterns);
+ }
+ }
+}
+
+pub fn walk_foreign_item<'v, V: Visitor<'v>>(visitor: &mut V, foreign_item: &'v ForeignItem<'v>) {
+ visitor.visit_id(foreign_item.hir_id);
+ visitor.visit_vis(&foreign_item.vis);
+ visitor.visit_ident(foreign_item.ident);
+
+ match foreign_item.kind {
+ ForeignItemKind::Fn(ref function_declaration, param_names, ref generics) => {
+ visitor.visit_generics(generics);
+ visitor.visit_fn_decl(function_declaration);
+ for ¶m_name in param_names {
+ visitor.visit_ident(param_name);
+ }
+ }
+ ForeignItemKind::Static(ref typ, _) => visitor.visit_ty(typ),
+ ForeignItemKind::Type => (),
+ }
+
+ walk_list!(visitor, visit_attribute, foreign_item.attrs);
+}
+
+pub fn walk_param_bound<'v, V: Visitor<'v>>(visitor: &mut V, bound: &'v GenericBound<'v>) {
+ match *bound {
+ GenericBound::Trait(ref typ, modifier) => {
+ visitor.visit_poly_trait_ref(typ, modifier);
+ }
+ GenericBound::Outlives(ref lifetime) => visitor.visit_lifetime(lifetime),
+ }
+}
+
+pub fn walk_generic_param<'v, V: Visitor<'v>>(visitor: &mut V, param: &'v GenericParam<'v>) {
+ visitor.visit_id(param.hir_id);
+ walk_list!(visitor, visit_attribute, param.attrs);
+ match param.name {
+ ParamName::Plain(ident) => visitor.visit_ident(ident),
+ ParamName::Error | ParamName::Fresh(_) => {}
+ }
+ match param.kind {
+ GenericParamKind::Lifetime { .. } => {}
+ GenericParamKind::Type { ref default, .. } => walk_list!(visitor, visit_ty, default),
+ GenericParamKind::Const { ref ty } => visitor.visit_ty(ty),
+ }
+ walk_list!(visitor, visit_param_bound, param.bounds);
+}
+
+pub fn walk_generics<'v, V: Visitor<'v>>(visitor: &mut V, generics: &'v Generics<'v>) {
+ walk_list!(visitor, visit_generic_param, generics.params);
+ walk_list!(visitor, visit_where_predicate, generics.where_clause.predicates);
+}
+
+pub fn walk_where_predicate<'v, V: Visitor<'v>>(
+ visitor: &mut V,
+ predicate: &'v WherePredicate<'v>,
+) {
+ match predicate {
+ &WherePredicate::BoundPredicate(WhereBoundPredicate {
+ ref bounded_ty,
+ bounds,
+ bound_generic_params,
+ ..
+ }) => {
+ visitor.visit_ty(bounded_ty);
+ walk_list!(visitor, visit_param_bound, bounds);
+ walk_list!(visitor, visit_generic_param, bound_generic_params);
+ }
+ &WherePredicate::RegionPredicate(WhereRegionPredicate { ref lifetime, bounds, .. }) => {
+ visitor.visit_lifetime(lifetime);
+ walk_list!(visitor, visit_param_bound, bounds);
+ }
+ &WherePredicate::EqPredicate(WhereEqPredicate {
+ hir_id, ref lhs_ty, ref rhs_ty, ..
+ }) => {
+ visitor.visit_id(hir_id);
+ visitor.visit_ty(lhs_ty);
+ visitor.visit_ty(rhs_ty);
+ }
+ }
+}
+
+pub fn walk_fn_ret_ty<'v, V: Visitor<'v>>(visitor: &mut V, ret_ty: &'v FunctionRetTy<'v>) {
+ if let FunctionRetTy::Return(ref output_ty) = *ret_ty {
+ visitor.visit_ty(output_ty)
+ }
+}
+
+pub fn walk_fn_decl<'v, V: Visitor<'v>>(visitor: &mut V, function_declaration: &'v FnDecl<'v>) {
+ for ty in function_declaration.inputs {
+ visitor.visit_ty(ty)
+ }
+ walk_fn_ret_ty(visitor, &function_declaration.output)
+}
+
+pub fn walk_fn_kind<'v, V: Visitor<'v>>(visitor: &mut V, function_kind: FnKind<'v>) {
+ match function_kind {
+ FnKind::ItemFn(_, generics, ..) => {
+ visitor.visit_generics(generics);
+ }
+ FnKind::Method(..) | FnKind::Closure(_) => {}
+ }
+}
+
+pub fn walk_fn<'v, V: Visitor<'v>>(
+ visitor: &mut V,
+ function_kind: FnKind<'v>,
+ function_declaration: &'v FnDecl<'v>,
+ body_id: BodyId,
+ _span: Span,
+ id: HirId,
+) {
+ visitor.visit_id(id);
+ visitor.visit_fn_decl(function_declaration);
+ walk_fn_kind(visitor, function_kind);
+ visitor.visit_nested_body(body_id)
+}
+
+pub fn walk_trait_item<'v, V: Visitor<'v>>(visitor: &mut V, trait_item: &'v TraitItem<'v>) {
+ visitor.visit_ident(trait_item.ident);
+ walk_list!(visitor, visit_attribute, trait_item.attrs);
+ visitor.visit_generics(&trait_item.generics);
+ match trait_item.kind {
+ TraitItemKind::Const(ref ty, default) => {
+ visitor.visit_id(trait_item.hir_id);
+ visitor.visit_ty(ty);
+ walk_list!(visitor, visit_nested_body, default);
+ }
+ TraitItemKind::Method(ref sig, TraitMethod::Required(param_names)) => {
+ visitor.visit_id(trait_item.hir_id);
+ visitor.visit_fn_decl(&sig.decl);
+ for ¶m_name in param_names {
+ visitor.visit_ident(param_name);
+ }
+ }
+ TraitItemKind::Method(ref sig, TraitMethod::Provided(body_id)) => {
+ visitor.visit_fn(
+ FnKind::Method(trait_item.ident, sig, None, &trait_item.attrs),
+ &sig.decl,
+ body_id,
+ trait_item.span,
+ trait_item.hir_id,
+ );
+ }
+ TraitItemKind::Type(bounds, ref default) => {
+ visitor.visit_id(trait_item.hir_id);
+ walk_list!(visitor, visit_param_bound, bounds);
+ walk_list!(visitor, visit_ty, default);
+ }
+ }
+}
+
+pub fn walk_trait_item_ref<'v, V: Visitor<'v>>(visitor: &mut V, trait_item_ref: &'v TraitItemRef) {
+ // N.B., deliberately force a compilation error if/when new fields are added.
+ let TraitItemRef { id, ident, ref kind, span: _, ref defaultness } = *trait_item_ref;
+ visitor.visit_nested_trait_item(id);
+ visitor.visit_ident(ident);
+ visitor.visit_associated_item_kind(kind);
+ visitor.visit_defaultness(defaultness);
+}
+
+pub fn walk_impl_item<'v, V: Visitor<'v>>(visitor: &mut V, impl_item: &'v ImplItem<'v>) {
+ // N.B., deliberately force a compilation error if/when new fields are added.
+ let ImplItem {
+ hir_id: _,
+ ident,
+ ref vis,
+ ref defaultness,
+ attrs,
+ ref generics,
+ ref kind,
+ span: _,
+ } = *impl_item;
+
+ visitor.visit_ident(ident);
+ visitor.visit_vis(vis);
+ visitor.visit_defaultness(defaultness);
+ walk_list!(visitor, visit_attribute, attrs);
+ visitor.visit_generics(generics);
+ match *kind {
+ ImplItemKind::Const(ref ty, body) => {
+ visitor.visit_id(impl_item.hir_id);
+ visitor.visit_ty(ty);
+ visitor.visit_nested_body(body);
+ }
+ ImplItemKind::Method(ref sig, body_id) => {
+ visitor.visit_fn(
+ FnKind::Method(impl_item.ident, sig, Some(&impl_item.vis), &impl_item.attrs),
+ &sig.decl,
+ body_id,
+ impl_item.span,
+ impl_item.hir_id,
+ );
+ }
+ ImplItemKind::TyAlias(ref ty) => {
+ visitor.visit_id(impl_item.hir_id);
+ visitor.visit_ty(ty);
+ }
+ ImplItemKind::OpaqueTy(bounds) => {
+ visitor.visit_id(impl_item.hir_id);
+ walk_list!(visitor, visit_param_bound, bounds);
+ }
+ }
+}
+
+pub fn walk_impl_item_ref<'v, V: Visitor<'v>>(visitor: &mut V, impl_item_ref: &'v ImplItemRef<'v>) {
+ // N.B., deliberately force a compilation error if/when new fields are added.
+ let ImplItemRef { id, ident, ref kind, span: _, ref vis, ref defaultness } = *impl_item_ref;
+ visitor.visit_nested_impl_item(id);
+ visitor.visit_ident(ident);
+ visitor.visit_associated_item_kind(kind);
+ visitor.visit_vis(vis);
+ visitor.visit_defaultness(defaultness);
+}
+
+pub fn walk_struct_def<'v, V: Visitor<'v>>(
+ visitor: &mut V,
+ struct_definition: &'v VariantData<'v>,
+) {
+ walk_list!(visitor, visit_id, struct_definition.ctor_hir_id());
+ walk_list!(visitor, visit_struct_field, struct_definition.fields());
+}
+
+pub fn walk_struct_field<'v, V: Visitor<'v>>(visitor: &mut V, struct_field: &'v StructField<'v>) {
+ visitor.visit_id(struct_field.hir_id);
+ visitor.visit_vis(&struct_field.vis);
+ visitor.visit_ident(struct_field.ident);
+ visitor.visit_ty(&struct_field.ty);
+ walk_list!(visitor, visit_attribute, struct_field.attrs);
+}
+
+pub fn walk_block<'v, V: Visitor<'v>>(visitor: &mut V, block: &'v Block<'v>) {
+ visitor.visit_id(block.hir_id);
+ walk_list!(visitor, visit_stmt, block.stmts);
+ walk_list!(visitor, visit_expr, &block.expr);
+}
+
+pub fn walk_stmt<'v, V: Visitor<'v>>(visitor: &mut V, statement: &'v Stmt<'v>) {
+ visitor.visit_id(statement.hir_id);
+ match statement.kind {
+ StmtKind::Local(ref local) => visitor.visit_local(local),
+ StmtKind::Item(item) => visitor.visit_nested_item(item),
+ StmtKind::Expr(ref expression) | StmtKind::Semi(ref expression) => {
+ visitor.visit_expr(expression)
+ }
+ }
+}
+
+pub fn walk_anon_const<'v, V: Visitor<'v>>(visitor: &mut V, constant: &'v AnonConst) {
+ visitor.visit_id(constant.hir_id);
+ visitor.visit_nested_body(constant.body);
+}
+
+pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) {
+ visitor.visit_id(expression.hir_id);
+ walk_list!(visitor, visit_attribute, expression.attrs.iter());
+ match expression.kind {
+ ExprKind::Box(ref subexpression) => visitor.visit_expr(subexpression),
+ ExprKind::Array(subexpressions) => {
+ walk_list!(visitor, visit_expr, subexpressions);
+ }
+ ExprKind::Repeat(ref element, ref count) => {
+ visitor.visit_expr(element);
+ visitor.visit_anon_const(count)
+ }
+ ExprKind::Struct(ref qpath, fields, ref optional_base) => {
+ visitor.visit_qpath(qpath, expression.hir_id, expression.span);
+ for field in fields {
+ visitor.visit_id(field.hir_id);
+ visitor.visit_ident(field.ident);
+ visitor.visit_expr(&field.expr)
+ }
+ walk_list!(visitor, visit_expr, optional_base);
+ }
+ ExprKind::Tup(subexpressions) => {
+ walk_list!(visitor, visit_expr, subexpressions);
+ }
+ ExprKind::Call(ref callee_expression, arguments) => {
+ visitor.visit_expr(callee_expression);
+ walk_list!(visitor, visit_expr, arguments);
+ }
+ ExprKind::MethodCall(ref segment, _, arguments) => {
+ visitor.visit_path_segment(expression.span, segment);
+ walk_list!(visitor, visit_expr, arguments);
+ }
+ ExprKind::Binary(_, ref left_expression, ref right_expression) => {
+ visitor.visit_expr(left_expression);
+ visitor.visit_expr(right_expression)
+ }
+ ExprKind::AddrOf(_, _, ref subexpression) | ExprKind::Unary(_, ref subexpression) => {
+ visitor.visit_expr(subexpression)
+ }
+ ExprKind::Cast(ref subexpression, ref typ) | ExprKind::Type(ref subexpression, ref typ) => {
+ visitor.visit_expr(subexpression);
+ visitor.visit_ty(typ)
+ }
+ ExprKind::DropTemps(ref subexpression) => {
+ visitor.visit_expr(subexpression);
+ }
+ ExprKind::Loop(ref block, ref opt_label, _) => {
+ walk_list!(visitor, visit_label, opt_label);
+ visitor.visit_block(block);
+ }
+ ExprKind::Match(ref subexpression, arms, _) => {
+ visitor.visit_expr(subexpression);
+ walk_list!(visitor, visit_arm, arms);
+ }
+ ExprKind::Closure(_, ref function_declaration, body, _fn_decl_span, _gen) => visitor
+ .visit_fn(
+ FnKind::Closure(&expression.attrs),
+ function_declaration,
+ body,
+ expression.span,
+ expression.hir_id,
+ ),
+ ExprKind::Block(ref block, ref opt_label) => {
+ walk_list!(visitor, visit_label, opt_label);
+ visitor.visit_block(block);
+ }
+ ExprKind::Assign(ref lhs, ref rhs, _) => {
+ visitor.visit_expr(rhs);
+ visitor.visit_expr(lhs)
+ }
+ ExprKind::AssignOp(_, ref left_expression, ref right_expression) => {
+ visitor.visit_expr(right_expression);
+ visitor.visit_expr(left_expression);
+ }
+ ExprKind::Field(ref subexpression, ident) => {
+ visitor.visit_expr(subexpression);
+ visitor.visit_ident(ident);
+ }
+ ExprKind::Index(ref main_expression, ref index_expression) => {
+ visitor.visit_expr(main_expression);
+ visitor.visit_expr(index_expression)
+ }
+ ExprKind::Path(ref qpath) => {
+ visitor.visit_qpath(qpath, expression.hir_id, expression.span);
+ }
+ ExprKind::Break(ref destination, ref opt_expr) => {
+ walk_list!(visitor, visit_label, &destination.label);
+ walk_list!(visitor, visit_expr, opt_expr);
+ }
+ ExprKind::Continue(ref destination) => {
+ walk_list!(visitor, visit_label, &destination.label);
+ }
+ ExprKind::Ret(ref optional_expression) => {
+ walk_list!(visitor, visit_expr, optional_expression);
+ }
+ ExprKind::InlineAsm(ref asm) => {
+ walk_list!(visitor, visit_expr, asm.outputs_exprs);
+ walk_list!(visitor, visit_expr, asm.inputs_exprs);
+ }
+ ExprKind::Yield(ref subexpression, _) => {
+ visitor.visit_expr(subexpression);
+ }
+ ExprKind::Lit(_) | ExprKind::Err => {}
+ }
+}
+
+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);
+ if let Some(ref g) = arm.guard {
+ match g {
+ Guard::If(ref e) => visitor.visit_expr(e),
+ }
+ }
+ visitor.visit_expr(&arm.body);
+ walk_list!(visitor, visit_attribute, arm.attrs);
+}
+
+pub fn walk_vis<'v, V: Visitor<'v>>(visitor: &mut V, vis: &'v Visibility<'v>) {
+ if let VisibilityKind::Restricted { ref path, hir_id } = vis.node {
+ visitor.visit_id(hir_id);
+ visitor.visit_path(path, hir_id)
+ }
+}
+
+pub fn walk_associated_item_kind<'v, V: Visitor<'v>>(_: &mut V, _: &'v AssocItemKind) {
+ // No visitable content here: this fn exists so you can call it if
+ // the right thing to do, should content be added in the future,
+ // would be to walk it.
+}
+
+pub fn walk_defaultness<'v, V: Visitor<'v>>(_: &mut V, _: &'v Defaultness) {
+ // No visitable content here: this fn exists so you can call it if
+ // the right thing to do, should content be added in the future,
+ // would be to walk it.
+}
//! [rustc guide]: https://rust-lang.github.io/rustc-guide/hir.html
#![feature(crate_visibility_modifier)]
-#![feature(const_fn)]
+#![feature(const_fn)] // For the unsizing cast on `&[]`
#![feature(in_band_lifetimes)]
#![feature(specialization)]
#![recursion_limit = "256"]
pub mod def_id;
mod hir;
pub mod hir_id;
+pub mod intravisit;
pub mod itemlikevisit;
pub mod pat_util;
pub mod print;
}
PatKind::Lit(ref e) => self.print_expr(&e),
PatKind::Range(ref begin, ref end, ref end_kind) => {
- self.print_expr(&begin);
- self.s.space();
+ if let Some(expr) = begin {
+ self.print_expr(expr);
+ self.s.space();
+ }
match *end_kind {
RangeEnd::Included => self.s.word("..."),
RangeEnd::Excluded => self.s.word(".."),
}
- self.print_expr(&end);
+ if let Some(expr) = end {
+ self.print_expr(expr);
+ }
}
PatKind::Slice(ref before, ref slice, ref after) => {
self.s.word("[");
use graphviz as dot;
use rustc::dep_graph::debug::{DepNodeFilter, EdgeFilter};
use rustc::dep_graph::{DepGraphQuery, DepKind, DepNode};
-use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor};
+use rustc::hir::map::Map;
use rustc::ty::TyCtxt;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::graph::implementation::{Direction, NodeIndex, INCOMING, OUTGOING};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_span::symbol::sym;
use rustc_span::Span;
use syntax::ast;
}
impl Visitor<'tcx> for IfThisChanged<'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, Self::Map> {
NestedVisitorMap::OnlyBodies(&self.tcx.hir())
}
//! the required condition is not met.
use rustc::dep_graph::{label_strs, DepNode};
-use rustc::hir::intravisit;
+use rustc::hir::map::Map;
use rustc::ty::TyCtxt;
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
+use rustc_hir::intravisit;
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_hir::Node as HirNode;
use rustc_hir::{ImplItemKind, ItemKind as HirItem, TraitItemKind};
}
impl intravisit::Visitor<'tcx> for FindAllAttrs<'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, Self::Map> {
intravisit::NestedVisitorMap::All(&self.tcx.hir())
}
return;
}
+ let _timer = sess.timer("incr_comp_prepare_session_directory");
+
debug!("prepare_session_directory");
// {incr-comp-dir}/{crate-name-and-disambiguator}
return;
}
+ let _timer = sess.timer("incr_comp_finalize_session_directory");
+
let incr_comp_session_dir: PathBuf = sess.incr_comp_session_dir().clone();
if sess.has_errors_or_delayed_span_bugs() {
return MaybeAsync::Sync(LoadResult::Ok { data: Default::default() });
}
+ let _timer = sess.prof.generic_activity("incr_comp_prepare_load_dep_graph");
+
// Calling `sess.incr_comp_session_dir()` will panic if `sess.opts.incremental.is_none()`.
// Fortunately, we just checked that this isn't the case.
let path = dep_graph_path_from(&sess.incr_comp_session_dir());
}
MaybeAsync::Async(std::thread::spawn(move || {
- let _prof_timer = prof.generic_pass("background load prev dep-graph");
+ let _prof_timer = prof.generic_activity("incr_comp_load_dep_graph");
match load_data(report_incremental_info, &path) {
LoadResult::DataOutOfDate => LoadResult::DataOutOfDate,
join(
move || {
if tcx.sess.opts.debugging_opts.incremental_queries {
- sess.time("persist query result cache", || {
+ sess.time("incr_comp_persist_result_cache", || {
save_in(sess, query_cache_path, |e| encode_query_cache(tcx, e));
});
}
},
|| {
- sess.time("persist dep-graph", || {
+ sess.time("incr_comp_persist_dep_graph", || {
save_in(sess, dep_graph_path, |e| {
- sess.time("encode dep-graph", || encode_dep_graph(tcx, e))
+ sess.time("incr_comp_encode_dep_graph", || encode_dep_graph(tcx, e))
});
});
},
tcx.sess.opts.dep_tracking_hash().encode(encoder).unwrap();
// Encode the graph data.
- let serialized_graph = tcx.sess.time("getting serialized graph", || tcx.dep_graph.serialize());
+ let serialized_graph =
+ tcx.sess.time("incr_comp_serialize_dep_graph", || tcx.dep_graph.serialize());
if tcx.sess.opts.debugging_opts.incremental_info {
#[derive(Clone)]
println!("[incremental]");
}
- tcx.sess.time("encoding serialized graph", || {
+ tcx.sess.time("incr_comp_encode_serialized_dep_graph", || {
serialized_graph.encode(encoder).unwrap();
});
}
}
fn encode_query_cache(tcx: TyCtxt<'_>, encoder: &mut Encoder) {
- tcx.sess.time("serialize query result cache", || {
+ tcx.sess.time("incr_comp_serialize_result_cache", || {
tcx.serialize_query_result_cache(encoder).unwrap();
})
}
rustc_builtin_macros = { path = "../librustc_builtin_macros" }
rustc_expand = { path = "../librustc_expand" }
rustc_parse = { path = "../librustc_parse" }
+rustc_session = { path = "../librustc_session" }
rustc_span = { path = "../librustc_span" }
rustc_serialize = { path = "../libserialize", package = "serialize" }
rustc = { path = "../librustc" }
rustc_ast_lowering = { path = "../librustc_ast_lowering" }
+rustc_ast_passes = { path = "../librustc_ast_passes" }
rustc_incremental = { path = "../librustc_incremental" }
rustc_traits = { path = "../librustc_traits" }
rustc_data_structures = { path = "../librustc_data_structures" }
rustc_hir = { path = "../librustc_hir" }
rustc_metadata = { path = "../librustc_metadata" }
rustc_mir = { path = "../librustc_mir" }
+rustc_mir_build = { path = "../librustc_mir_build" }
rustc_passes = { path = "../librustc_passes" }
rustc_typeck = { path = "../librustc_typeck" }
rustc_lint = { path = "../librustc_lint" }
use rustc_data_structures::sync::Lrc;
use rustc_data_structures::OnDrop;
use rustc_errors::registry::Registry;
+use rustc_lint::LintStore;
use rustc_parse::new_parser_from_source_str;
use rustc_span::edition;
use rustc_span::source_map::{FileLoader, FileName, SourceMap};
pub(crate) output_dir: Option<PathBuf>,
pub(crate) output_file: Option<PathBuf>,
pub(crate) crate_name: Option<String>,
- pub(crate) register_lints: Option<Box<dyn Fn(&Session, &mut lint::LintStore) + Send + Sync>>,
+ pub(crate) register_lints: Option<Box<dyn Fn(&Session, &mut LintStore) + Send + Sync>>,
pub(crate) override_queries:
Option<fn(&Session, &mut ty::query::Providers<'_>, &mut ty::query::Providers<'_>)>,
}
///
/// Note that if you find a Some here you probably want to call that function in the new
/// function being registered.
- pub register_lints: Option<Box<dyn Fn(&Session, &mut lint::LintStore) + Send + Sync>>,
+ pub register_lints: Option<Box<dyn Fn(&Session, &mut LintStore) + Send + Sync>>,
/// This is a callback from the driver that is called just after we have populated
/// the list of queries.
override_queries: config.override_queries,
};
- let _sess_abort_error = OnDrop(|| {
- compiler.sess.diagnostic().print_error_count(registry);
- });
+ let r = {
+ let _sess_abort_error = OnDrop(|| {
+ compiler.sess.diagnostic().print_error_count(registry);
+ });
- f(&compiler)
+ f(&compiler)
+ };
+
+ let prof = compiler.sess.prof.clone();
+ prof.generic_activity("drop_compiler").run(move || drop(compiler));
+ r
}
pub fn run_compiler<R: Send>(mut config: Config, f: impl FnOnce(&Compiler) -> R + Send) -> R {
use rustc_expand::base::ExtCtxt;
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
use rustc_incremental;
+use rustc_lint::LintStore;
use rustc_mir as mir;
+use rustc_mir_build as mir_build;
use rustc_parse::{parse_crate_from_file, parse_crate_from_source_str};
-use rustc_passes::{self, ast_validation, hir_stats, layout_test};
+use rustc_passes::{self, hir_stats, layout_test};
use rustc_plugin_impl as plugin;
use rustc_privacy;
use rustc_resolve::{Resolver, ResolverArenas};
use rustc_span::FileName;
use rustc_traits;
use rustc_typeck as typeck;
-use syntax::early_buffered_lints::BufferedEarlyLint;
use syntax::mut_visit::MutVisitor;
use syntax::util::node_count::NodeCounter;
use syntax::{self, ast, visit};
use std::{env, fs, iter, mem};
pub fn parse<'a>(sess: &'a Session, input: &Input) -> PResult<'a, ast::Crate> {
- sess.diagnostic().set_continue_after_error(sess.opts.debugging_opts.continue_parse_after_error);
- let krate = sess.time("parsing", || match input {
+ let krate = sess.time("parse_crate", || match input {
Input::File(file) => parse_crate_from_file(file, &sess.parse_sess),
Input::Str { input, name } => {
parse_crate_from_source_str(name.clone(), input.clone(), &sess.parse_sess)
}
})?;
- sess.diagnostic().set_continue_after_error(true);
-
if sess.opts.debugging_opts.ast_json_noexpand {
println!("{}", json::as_json(&krate));
}
}
if let Some(ref s) = sess.opts.debugging_opts.show_span {
- syntax::show_span::run(sess.diagnostic(), s, &krate);
+ rustc_ast_passes::show_span::run(sess.diagnostic(), s, &krate);
}
if sess.opts.debugging_opts.hir_stats {
/// Returns `None` if we're aborting after handling -W help.
pub fn configure_and_expand(
sess: Lrc<Session>,
- lint_store: Lrc<lint::LintStore>,
+ lint_store: Lrc<LintStore>,
metadata_loader: Box<MetadataLoaderDyn>,
krate: ast::Crate,
crate_name: &str,
pub fn register_plugins<'a>(
sess: &'a Session,
metadata_loader: &'a dyn MetadataLoader,
- register_lints: impl Fn(&Session, &mut lint::LintStore),
+ register_lints: impl Fn(&Session, &mut LintStore),
mut krate: ast::Crate,
crate_name: &str,
-) -> Result<(ast::Crate, Lrc<lint::LintStore>)> {
- krate = sess.time("attributes injection", || {
+) -> Result<(ast::Crate, Lrc<LintStore>)> {
+ krate = sess.time("attributes_injection", || {
rustc_builtin_macros::cmdline_attrs::inject(
krate,
&sess.parse_sess,
rustc_incremental::prepare_session_directory(sess, &crate_name, disambiguator);
if sess.opts.incremental.is_some() {
- sess.time("garbage-collect incremental cache directory", || {
- let _prof_timer =
- sess.prof.generic_activity("incr_comp_garbage_collect_session_directories");
+ sess.time("incr_comp_garbage_collect_session_directories", || {
if let Err(e) = rustc_incremental::garbage_collect_session_directories(sess) {
warn!(
"Error while trying to garbage collect incremental \
});
}
- sess.time("recursion limit", || {
+ sess.time("recursion_limit", || {
middle::recursion_limit::update_limits(sess, &krate);
});
register_lints(&sess, &mut lint_store);
let registrars =
- sess.time("plugin loading", || plugin::load::load_plugins(sess, metadata_loader, &krate));
- sess.time("plugin registration", || {
+ sess.time("plugin_loading", || plugin::load::load_plugins(sess, metadata_loader, &krate));
+ sess.time("plugin_registration", || {
let mut registry = plugin::Registry { lint_store: &mut lint_store };
for registrar in registrars {
registrar(&mut registry);
fn configure_and_expand_inner<'a>(
sess: &'a Session,
- lint_store: &'a lint::LintStore,
+ lint_store: &'a LintStore,
mut krate: ast::Crate,
crate_name: &str,
resolver_arenas: &'a ResolverArenas<'a>,
metadata_loader: &'a MetadataLoaderDyn,
) -> Result<(ast::Crate, Resolver<'a>)> {
- sess.time("pre-AST-expansion lint checks", || {
+ sess.time("pre_AST_expansion_lint_checks", || {
rustc_lint::check_ast_crate(
sess,
lint_store,
let mut resolver = Resolver::new(sess, &krate, crate_name, metadata_loader, &resolver_arenas);
rustc_builtin_macros::register_builtin_macros(&mut resolver, sess.edition());
- krate = sess.time("crate injection", || {
+ krate = sess.time("crate_injection", || {
let alt_std_name = sess.opts.alt_std_name.as_ref().map(|s| Symbol::intern(s));
let (krate, name) = rustc_builtin_macros::standard_library_imports::inject(
krate,
util::check_attr_crate_type(&krate.attrs, &mut resolver.lint_buffer());
// Expand all macros
- krate = sess.time("expansion", || {
+ krate = sess.time("macro_expand_crate", || {
// Windows dlls do not have rpaths, so they don't know how to find their
// dependencies. It's up to us to tell the system where to find all the
// dependent dlls. Note that this uses cfg!(windows) as opposed to
let mut ecx = ExtCtxt::new(&sess.parse_sess, cfg, &mut resolver);
// Expand macros now!
- let krate = sess.time("expand crate", || ecx.monotonic_expander().expand_crate(krate));
+ let krate = sess.time("expand_crate", || ecx.monotonic_expander().expand_crate(krate));
// The rest is error reporting
- sess.time("check unused macros", || {
+ sess.time("check_unused_macros", || {
ecx.check_unused_macros();
});
krate
});
- sess.time("maybe building test harness", || {
+ sess.time("maybe_building_test_harness", || {
rustc_builtin_macros::test_harness::inject(
&sess.parse_sess,
&mut resolver,
util::ReplaceBodyWithLoop::new(&mut resolver).visit_crate(&mut krate);
}
- let has_proc_macro_decls = sess.time("AST validation", || {
- ast_validation::check_crate(sess, &krate, &mut resolver.lint_buffer())
+ let has_proc_macro_decls = sess.time("AST_validation", || {
+ rustc_ast_passes::ast_validation::check_crate(sess, &krate, &mut resolver.lint_buffer())
});
let crate_types = sess.crate_types.borrow();
msg.warn("The generated documentation may be incorrect");
msg.emit()
} else {
- krate = sess.time("maybe creating a macro crate", || {
+ krate = sess.time("maybe_create_a_macro_crate", || {
let num_crate_types = crate_types.len();
let is_test_crate = sess.opts.test;
rustc_builtin_macros::proc_macro_harness::inject(
println!("{}", json::as_json(&krate));
}
- sess.time("name resolution", || {
- resolver.resolve_crate(&krate);
- });
+ resolver.resolve_crate(&krate);
// Needs to go *after* expansion to be able to check the results of macro expansion.
- sess.time("complete gated feature checking", || {
- syntax::feature_gate::check_crate(
+ sess.time("complete_gated_feature_checking", || {
+ rustc_ast_passes::feature_gate::check_crate(
&krate,
&sess.parse_sess,
&sess.features_untracked(),
// Add all buffered lints from the `ParseSess` to the `Session`.
sess.parse_sess.buffered_lints.with_lock(|buffered_lints| {
info!("{} parse sess buffered_lints", buffered_lints.len());
- for BufferedEarlyLint { id, span, msg, lint_id } in buffered_lints.drain(..) {
- resolver.lint_buffer().buffer_lint(lint_id, id, span, &msg);
+ for early_lint in buffered_lints.drain(..) {
+ resolver.lint_buffer().add_early_lint(early_lint);
}
});
pub fn lower_to_hir<'res, 'tcx>(
sess: &'tcx Session,
- lint_store: &lint::LintStore,
+ lint_store: &LintStore,
resolver: &'res mut Resolver<'_>,
dep_graph: &'res DepGraph,
krate: &'res ast::Crate,
arena: &'tcx Arena<'tcx>,
) -> Result<map::Forest<'tcx>> {
// Lower AST to HIR.
- let hir_forest = sess.time("lowering AST -> HIR", || {
- let hir_crate = rustc_ast_lowering::lower_crate(
- sess,
- &dep_graph,
- &krate,
- resolver,
- rustc_parse::nt_to_tokenstream,
- arena,
- );
+ let hir_crate = rustc_ast_lowering::lower_crate(
+ sess,
+ &dep_graph,
+ &krate,
+ resolver,
+ rustc_parse::nt_to_tokenstream,
+ arena,
+ );
- if sess.opts.debugging_opts.hir_stats {
- hir_stats::print_hir_stats(&hir_crate);
- }
+ if sess.opts.debugging_opts.hir_stats {
+ hir_stats::print_hir_stats(&hir_crate);
+ }
- map::Forest::new(hir_crate, &dep_graph)
- });
+ let hir_forest = map::Forest::new(hir_crate, &dep_graph);
- sess.time("early lint checks", || {
+ sess.time("early_lint_checks", || {
rustc_lint::check_ast_crate(
sess,
lint_store,
boxed_resolver: &Steal<Rc<RefCell<BoxedResolver>>>,
crate_name: &str,
) -> Result<OutputFilenames> {
+ let _timer = sess.timer("prepare_outputs");
+
// FIXME: rustdoc passes &[] instead of &krate.attrs here
let outputs = util::build_output_filenames(
&compiler.input,
plugin::build::provide(providers);
rustc::hir::provide(providers);
mir::provide(providers);
+ mir_build::provide(providers);
rustc_privacy::provide(providers);
typeck::provide(providers);
ty::provide(providers);
pub fn create_global_ctxt<'tcx>(
compiler: &'tcx Compiler,
- lint_store: Lrc<lint::LintStore>,
+ lint_store: Lrc<LintStore>,
hir_forest: &'tcx map::Forest<'tcx>,
mut resolver_outputs: ResolverOutputs,
outputs: OutputFilenames,
let defs = mem::take(&mut resolver_outputs.definitions);
// Construct the HIR map.
- let hir_map = sess.time("indexing HIR", || {
- map::map_crate(sess, &*resolver_outputs.cstore, &hir_forest, defs)
- });
+ let hir_map = map::map_crate(sess, &*resolver_outputs.cstore, &hir_forest, defs);
- let query_result_on_disk_cache =
- sess.time("load query result cache", || rustc_incremental::load_query_result_cache(sess));
+ let query_result_on_disk_cache = rustc_incremental::load_query_result_cache(sess);
let codegen_backend = compiler.codegen_backend();
let mut local_providers = ty::query::Providers::default();
callback(sess, &mut local_providers, &mut extern_providers);
}
- let gcx = global_ctxt.init_locking(|| {
- TyCtxt::create_global_ctxt(
- sess,
- lint_store,
- local_providers,
- extern_providers,
- &all_arenas,
- arena,
- resolver_outputs,
- hir_map,
- query_result_on_disk_cache,
- &crate_name,
- &outputs,
- )
+ let gcx = sess.time("setup_global_ctxt", || {
+ global_ctxt.init_locking(|| {
+ TyCtxt::create_global_ctxt(
+ sess,
+ lint_store,
+ local_providers,
+ extern_providers,
+ &all_arenas,
+ arena,
+ resolver_outputs,
+ hir_map,
+ query_result_on_disk_cache,
+ &crate_name,
+ &outputs,
+ )
+ })
});
// Do some initialization of the DepGraph that can only be done with the tcx available.
ty::tls::enter_global(&gcx, |tcx| {
- tcx.sess.time("dep graph tcx init", || rustc_incremental::dep_graph_tcx_init(tcx));
+ tcx.sess.time("dep_graph_tcx_init", || rustc_incremental::dep_graph_tcx_init(tcx));
});
QueryContext(gcx)
let sess = tcx.sess;
let mut entry_point = None;
- sess.time("misc checking 1", || {
+ sess.time("misc_checking_1", || {
parallel!(
{
entry_point = sess
- .time("looking for entry point", || rustc_passes::entry::find_entry_point(tcx));
+ .time("looking_for_entry_point", || rustc_passes::entry::find_entry_point(tcx));
- sess.time("looking for plugin registrar", || {
+ sess.time("looking_for_plugin_registrar", || {
plugin::build::find_plugin_registrar(tcx)
});
- sess.time("looking for derive registrar", || proc_macro_decls::find(tcx));
+ sess.time("looking_for_derive_registrar", || proc_macro_decls::find(tcx));
},
{
par_iter(&tcx.hir().krate().modules).for_each(|(&module, _)| {
// passes are timed inside typeck
typeck::check_crate(tcx)?;
- sess.time("misc checking 2", || {
+ sess.time("misc_checking_2", || {
parallel!(
{
- sess.time("match checking", || {
+ sess.time("match_checking", || {
tcx.par_body_owners(|def_id| {
tcx.ensure().check_match(def_id);
});
});
},
{
- sess.time("liveness checking + intrinsic checking", || {
+ sess.time("liveness_and_intrinsic_checking", || {
par_iter(&tcx.hir().krate().modules).for_each(|(&module, _)| {
// this must run before MIR dump, because
// "not all control paths return a value" is reported here.
);
});
- sess.time("MIR borrow checking", || {
+ sess.time("MIR_borrow_checking", || {
tcx.par_body_owners(|def_id| tcx.ensure().mir_borrowck(def_id));
});
- sess.time("dumping Chalk-like clauses", || {
+ sess.time("dumping_chalk_like_clauses", || {
rustc_traits::lowering::dump_program_clauses(tcx);
});
- sess.time("MIR effect checking", || {
+ sess.time("MIR_effect_checking", || {
for def_id in tcx.body_owners() {
mir::transform::check_unsafety::check_unsafety(tcx, def_id)
}
});
- sess.time("layout testing", || layout_test::test_layout(tcx));
+ sess.time("layout_testing", || layout_test::test_layout(tcx));
// Avoid overwhelming user with errors if borrow checking failed.
// I'm not sure how helpful this is, to be honest, but it avoids a
return Err(ErrorReported);
}
- sess.time("misc checking 3", || {
+ sess.time("misc_checking_3", || {
parallel!(
{
- sess.time("privacy access levels", || {
- tcx.ensure().privacy_access_levels(LOCAL_CRATE);
- });
+ tcx.ensure().privacy_access_levels(LOCAL_CRATE);
+
parallel!(
{
- sess.time("private in public", || {
- tcx.ensure().check_private_in_public(LOCAL_CRATE);
- });
+ tcx.ensure().check_private_in_public(LOCAL_CRATE);
},
{
- sess.time("death checking", || rustc_passes::dead::check_crate(tcx));
+ sess.time("death_checking", || rustc_passes::dead::check_crate(tcx));
},
{
- sess.time("unused lib feature checking", || {
+ sess.time("unused_lib_feature_checking", || {
rustc_passes::stability::check_unused_or_stable_features(tcx)
});
},
{
- sess.time("lint checking", || {
+ sess.time("lint_checking", || {
rustc_lint::check_crate(tcx, || {
rustc_lint::BuiltinCombinedLateLintPass::new()
});
);
},
{
- sess.time("privacy checking modules", || {
+ sess.time("privacy_checking_modules", || {
par_iter(&tcx.hir().krate().modules).for_each(|(&module, _)| {
tcx.ensure().check_mod_privacy(tcx.hir().local_def_id(module));
});
MetadataKind::Uncompressed | MetadataKind::Compressed => tcx.encode_metadata(),
};
+ let _prof_timer = tcx.sess.prof.generic_activity("write_crate_metadata");
+
let need_metadata_file = tcx.sess.opts.output_types.contains_key(&OutputType::Metadata);
if need_metadata_file {
let crate_name = &tcx.crate_name(LOCAL_CRATE).as_str();
tcx.print_debug_stats();
}
- let (metadata, need_metadata_module) =
- tcx.sess.time("metadata encoding and writing", || encode_and_write_metadata(tcx, outputs));
+ let (metadata, need_metadata_module) = encode_and_write_metadata(tcx, outputs);
- let codegen = tcx.sess.time("codegen", move || {
+ let codegen = tcx.sess.time("codegen_crate", move || {
codegen_backend.codegen_crate(tcx, metadata, need_metadata_module)
});
use rustc::arena::Arena;
use rustc::dep_graph::DepGraph;
use rustc::hir::map;
-use rustc::lint;
-use rustc::lint::LintStore;
use rustc::session::config::{OutputFilenames, OutputType};
use rustc::session::Session;
use rustc::ty::steal::Steal;
use rustc_data_structures::sync::{Lrc, Once, WorkerLocal};
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_incremental::DepGraphFuture;
+use rustc_lint::LintStore;
use std::any::Any;
use std::cell::{Ref, RefCell, RefMut};
use std::mem;
let crate_name = self.crate_name()?.peek().clone();
let krate = self.parse()?.take();
- let empty: &(dyn Fn(&Session, &mut lint::LintStore) + Sync + Send) = &|_, _| {};
+ let empty: &(dyn Fn(&Session, &mut LintStore) + Sync + Send) = &|_, _| {};
let result = passes::register_plugins(
self.session(),
&*self.codegen_backend().metadata_loader(),
self.expansion.compute(|| {
let crate_name = self.crate_name()?.peek().clone();
let (krate, lint_store) = self.register_plugins()?.take();
+ let _timer = self.session().timer("configure_and_expand");
passes::configure_and_expand(
self.session().clone(),
lint_store.clone(),
None => DepGraph::new_disabled(),
Some(future) => {
let (prev_graph, prev_work_products) =
- self.session().time("blocked while dep-graph loading finishes", || {
+ self.session().time("blocked_on_dep_graph_loading", || {
future
.open()
.unwrap_or_else(|e| rustc_incremental::LoadResult::Error {
let lint_store = self.expansion()?.peek().2.clone();
let hir = self.lower_to_hir()?.peek();
let (ref hir_forest, ref resolver_outputs) = &*hir;
+ let _timer = self.session().timer("create_global_ctxt");
Ok(passes::create_global_ctxt(
self.compiler,
lint_store,
impl Linker {
pub fn link(self) -> Result<()> {
- self.codegen_backend
+ let r = self
+ .codegen_backend
.join_codegen_and_link(
self.ongoing_codegen,
&self.sess,
&self.dep_graph,
&self.prepare_outputs,
)
- .map_err(|_| ErrorReported)
+ .map_err(|_| ErrorReported);
+ let prof = self.sess.prof.clone();
+ let dep_graph = self.dep_graph;
+ prof.generic_activity("drop_dep_graph").run(move || drop(dep_graph));
+ r
}
}
where
F: for<'tcx> FnOnce(&'tcx Queries<'tcx>) -> T,
{
+ let mut _timer = None;
let queries = Queries::new(&self);
let ret = f(&queries);
}
}
+ _timer = Some(self.session().timer("free_global_ctxt"));
+
ret
}
use crate::interface::parse_cfgspecs;
-use rustc::lint;
+use rustc::lint::Level;
use rustc::middle::cstore;
use rustc::session::config::{build_configuration, build_session_options, to_crate_config};
use rustc::session::config::{rustc_optgroups, ErrorOutputType, ExternLocation, Options, Passes};
let mut v3 = Options::default();
v1.lint_opts = vec![
- (String::from("a"), lint::Allow),
- (String::from("b"), lint::Warn),
- (String::from("c"), lint::Deny),
- (String::from("d"), lint::Forbid),
+ (String::from("a"), Level::Allow),
+ (String::from("b"), Level::Warn),
+ (String::from("c"), Level::Deny),
+ (String::from("d"), Level::Forbid),
];
v2.lint_opts = vec![
- (String::from("a"), lint::Allow),
- (String::from("b"), lint::Warn),
- (String::from("X"), lint::Deny),
- (String::from("d"), lint::Forbid),
+ (String::from("a"), Level::Allow),
+ (String::from("b"), Level::Warn),
+ (String::from("X"), Level::Deny),
+ (String::from("d"), Level::Forbid),
];
v3.lint_opts = vec![
- (String::from("a"), lint::Allow),
- (String::from("b"), lint::Warn),
- (String::from("c"), lint::Forbid),
- (String::from("d"), lint::Deny),
+ (String::from("a"), Level::Allow),
+ (String::from("b"), Level::Warn),
+ (String::from("c"), Level::Forbid),
+ (String::from("d"), Level::Deny),
];
assert!(v1.dep_tracking_hash() != v2.dep_tracking_hash());
let mut v2 = Options::default();
v1.lint_opts = vec![
- (String::from("a"), lint::Allow),
- (String::from("b"), lint::Warn),
- (String::from("c"), lint::Deny),
- (String::from("d"), lint::Forbid),
+ (String::from("a"), Level::Allow),
+ (String::from("b"), Level::Warn),
+ (String::from("c"), Level::Deny),
+ (String::from("d"), Level::Forbid),
];
v2.lint_opts = vec![
- (String::from("a"), lint::Allow),
- (String::from("c"), lint::Deny),
- (String::from("b"), lint::Warn),
- (String::from("d"), lint::Forbid),
+ (String::from("a"), Level::Allow),
+ (String::from("c"), Level::Deny),
+ (String::from("b"), Level::Warn),
+ (String::from("d"), Level::Forbid),
];
assert_eq!(v1.dep_tracking_hash(), v2.dep_tracking_hash());
opts.debugging_opts.report_delayed_bugs = true;
assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
- opts = reference.clone();
- opts.debugging_opts.continue_parse_after_error = true;
- assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
-
opts = reference.clone();
opts.debugging_opts.force_overflow_checks = Some(true);
assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
use log::info;
use rustc::lint;
-use rustc::session::config::{ErrorOutputType, Input, OutputFilenames};
-use rustc::session::CrateDisambiguator;
-use rustc::session::{self, config, early_error, filesearch, DiagnosticOutput, Session};
use rustc::ty;
use rustc_codegen_utils::codegen_backend::CodegenBackend;
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_errors::registry::Registry;
use rustc_metadata::dynamic_lib::DynamicLibrary;
use rustc_resolve::{self, Resolver};
+use rustc_session as session;
+use rustc_session::config::{ErrorOutputType, Input, OutputFilenames};
+use rustc_session::lint::{BuiltinLintDiagnostics, LintBuffer};
+use rustc_session::CrateDisambiguator;
+use rustc_session::{config, early_error, filesearch, DiagnosticOutput, Session};
use rustc_span::edition::Edition;
use rustc_span::source_map::{FileLoader, RealFileLoader, SourceMap};
use rustc_span::symbol::{sym, Symbol};
lint_caps,
);
- sess.prof.register_queries(|profiler| {
- rustc::ty::query::QueryName::register_with_profiler(&profiler);
- });
-
let codegen_backend = get_codegen_backend(&sess);
let mut cfg = config::build_configuration(&sess, config::to_crate_config(cfg));
CrateDisambiguator::from(hasher.finish::<Fingerprint>())
}
-pub(crate) fn check_attr_crate_type(attrs: &[ast::Attribute], lint_buffer: &mut lint::LintBuffer) {
+pub(crate) fn check_attr_crate_type(attrs: &[ast::Attribute], lint_buffer: &mut LintBuffer) {
// Unconditionally collect crate types from attributes to make them used
for a in attrs.iter() {
if a.check_name(sym::crate_type) {
ast::CRATE_NODE_ID,
span,
"invalid `crate_type` value",
- lint::builtin::BuiltinLintDiagnostics::UnknownCrateTypes(
+ BuiltinLintDiagnostics::UnknownCrateTypes(
span,
"did you mean".to_string(),
format!("\"{}\"", candidate),
log = "0.4"
unicode-security = "0.0.2"
rustc = { path = "../librustc" }
+rustc_errors = { path = "../librustc_errors" }
+rustc_error_codes = { path = "../librustc_error_codes" }
rustc_hir = { path = "../librustc_hir" }
rustc_target = { path = "../librustc_target" }
syntax = { path = "../libsyntax" }
rustc_data_structures = { path = "../librustc_data_structures" }
rustc_feature = { path = "../librustc_feature" }
rustc_index = { path = "../librustc_index" }
-rustc_error_codes = { path = "../librustc_error_codes" }
rustc_session = { path = "../librustc_session" }
-use crate::lint::{LateContext, LateLintPass, LintArray, LintContext, LintPass};
-use rustc::lint::FutureIncompatibleInfo;
+use crate::{LateContext, LateLintPass, LintContext};
use rustc::ty;
use rustc::ty::adjustment::{Adjust, Adjustment};
+use rustc_errors::Applicability;
use rustc_hir as hir;
+use rustc_session::lint::FutureIncompatibleInfo;
use rustc_span::symbol::sym;
-use syntax::errors::Applicability;
declare_lint! {
pub ARRAY_INTO_ITER,
//! If you define a new `LateLintPass`, you will also need to add it to the
//! `late_lint_methods!` invocation in `lib.rs`.
-use std::fmt::Write;
-
-use lint::{EarlyContext, EarlyLintPass, LateLintPass, LintPass};
-use lint::{LateContext, LintArray, LintContext};
-use rustc::lint;
-use rustc::lint::FutureIncompatibleInfo;
+use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
+use rustc::hir::map::Map;
+use rustc::traits::misc::can_type_implement_copy;
use rustc::ty::{self, layout::VariantIdx, Ty, TyCtxt};
use rustc_data_structures::fx::FxHashSet;
+use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_feature::Stability;
use rustc_feature::{deprecated_attributes, AttributeGate, AttributeTemplate, AttributeType};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::{GenericParamKind, PatKind};
use rustc_hir::{HirIdSet, Node};
+use rustc_session::lint::FutureIncompatibleInfo;
use rustc_span::edition::Edition;
use rustc_span::source_map::Spanned;
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_span::{BytePos, Span};
use syntax::ast::{self, Expr};
use syntax::attr::{self, HasAttrs};
-use syntax::errors::{Applicability, DiagnosticBuilder};
use syntax::print::pprust::{self, expr_to_string};
-use syntax::ptr::P;
use syntax::tokenstream::{TokenStream, TokenTree};
use syntax::visit::FnKind;
use crate::nonstandard_style::{method_context, MethodLateContext};
use log::debug;
+use std::fmt::Write;
// hardwired lints from librustc
-pub use lint::builtin::*;
+pub use rustc_session::lint::builtin::*;
declare_lint! {
WHILE_TRUE,
if ty.is_copy_modulo_regions(cx.tcx, param_env, item.span) {
return;
}
- if param_env.can_type_implement_copy(cx.tcx, ty).is_ok() {
+ if can_type_implement_copy(cx.tcx, param_env, ty).is_ok() {
cx.span_lint(
MISSING_COPY_IMPLEMENTATIONS,
item.span,
// bound. Let's see if this type does that.
// We use a HIR visitor to walk the type.
- use rustc::hir::intravisit::{self, Visitor};
+ use rustc_hir::intravisit::{self, Visitor};
struct WalkAssocTypes<'a, 'db> {
err: &'a mut DiagnosticBuilder<'db>,
}
impl<'a, 'db, 'v> Visitor<'v> for WalkAssocTypes<'a, 'db> {
- fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'v> {
+ type Map = Map<'v>;
+
+ fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<'_, Self::Map> {
intravisit::NestedVisitorMap::None
}
/// If `pat` is a `...` pattern, return the start and end of the range, as well as the span
/// corresponding to the ellipsis.
- fn matches_ellipsis_pat(pat: &ast::Pat) -> Option<(&P<Expr>, &P<Expr>, Span)> {
+ fn matches_ellipsis_pat(pat: &ast::Pat) -> Option<(Option<&Expr>, &Expr, Span)> {
match &pat.kind {
- PatKind::Range(a, b, Spanned { span, node: RangeEnd::Included(DotDotDot), .. }) => {
- Some((a, b, *span))
- }
+ PatKind::Range(
+ a,
+ Some(b),
+ Spanned { span, node: RangeEnd::Included(DotDotDot) },
+ ) => Some((a.as_deref(), b, *span)),
_ => None,
}
}
let suggestion = "use `..=` for an inclusive range";
if parenthesise {
self.node_id = Some(pat.id);
+ let end = expr_to_string(&end);
+ let replace = match start {
+ Some(start) => format!("&({}..={})", expr_to_string(&start), end),
+ None => format!("&(..={})", end),
+ };
let mut err = cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, pat.span, msg);
err.span_suggestion(
pat.span,
suggestion,
- format!("&({}..={})", expr_to_string(&start), expr_to_string(&end)),
+ replace,
Applicability::MachineApplicable,
);
err.emit();
}
declare_lint_pass!(
- /// Check for used feature gates in `INCOMPLETE_FEATURES` in `feature_gate.rs`.
+ /// Check for used feature gates in `INCOMPLETE_FEATURES` in `librustc_feature/active.rs`.
IncompleteFeatures => [INCOMPLETE_FEATURES]
);
--- /dev/null
+//! Implementation of lint checking.
+//!
+//! The lint checking is mostly consolidated into one pass which runs
+//! after all other analyses. Throughout compilation, lint warnings
+//! can be added via the `add_lint` method on the Session structure. This
+//! requires a span and an ID of the node that the lint is being added to. The
+//! lint isn't actually emitted at that time because it is unknown what the
+//! actual lint level at that location is.
+//!
+//! To actually emit lint warnings/errors, a separate pass is used.
+//! A context keeps track of the current state of all lint levels.
+//! Upon entering a node of the ast which can modify the lint settings, the
+//! previous lint state is pushed onto a stack and the ast is then recursed
+//! upon. As the ast is traversed, this keeps track of the current lint level
+//! for all lint attributes.
+
+use self::TargetLint::*;
+
+use crate::levels::LintLevelsBuilder;
+use crate::passes::{EarlyLintPassObject, LateLintPassObject};
+use rustc::hir::map::definitions::{DefPathData, DisambiguatedDefPathData};
+use rustc::lint::add_elided_lifetime_in_path_suggestion;
+use rustc::middle::privacy::AccessLevels;
+use rustc::middle::stability;
+use rustc::ty::layout::{LayoutError, LayoutOf, TyLayout};
+use rustc::ty::{self, print::Printer, subst::GenericArg, Ty, TyCtxt};
+use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::sync;
+use rustc_error_codes::*;
+use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
+use rustc_hir as hir;
+use rustc_hir::def_id::{CrateNum, DefId};
+use rustc_session::lint::BuiltinLintDiagnostics;
+use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintBuffer, LintId};
+use rustc_session::Session;
+use rustc_span::{symbol::Symbol, MultiSpan, Span, DUMMY_SP};
+use syntax::ast;
+use syntax::util::lev_distance::find_best_match_for_name;
+
+use std::slice;
+
+/// Information about the registered lints.
+///
+/// This is basically the subset of `Context` that we can
+/// build early in the compile pipeline.
+pub struct LintStore {
+ /// Registered lints.
+ lints: Vec<&'static Lint>,
+
+ /// Constructor functions for each variety of lint pass.
+ ///
+ /// These should only be called once, but since we want to avoid locks or
+ /// interior mutability, we don't enforce this (and lints should, in theory,
+ /// be compatible with being constructed more than once, though not
+ /// necessarily in a sane manner. This is safe though.)
+ pub pre_expansion_passes: Vec<Box<dyn Fn() -> EarlyLintPassObject + sync::Send + sync::Sync>>,
+ pub early_passes: Vec<Box<dyn Fn() -> EarlyLintPassObject + sync::Send + sync::Sync>>,
+ pub late_passes: Vec<Box<dyn Fn() -> LateLintPassObject + sync::Send + sync::Sync>>,
+ /// This is unique in that we construct them per-module, so not once.
+ pub late_module_passes: Vec<Box<dyn Fn() -> LateLintPassObject + sync::Send + sync::Sync>>,
+
+ /// Lints indexed by name.
+ by_name: FxHashMap<String, TargetLint>,
+
+ /// Map of registered lint groups to what lints they expand to.
+ lint_groups: FxHashMap<&'static str, LintGroup>,
+}
+
+/// The target of the `by_name` map, which accounts for renaming/deprecation.
+enum TargetLint {
+ /// A direct lint target
+ Id(LintId),
+
+ /// Temporary renaming, used for easing migration pain; see #16545
+ Renamed(String, LintId),
+
+ /// Lint with this name existed previously, but has been removed/deprecated.
+ /// The string argument is the reason for removal.
+ Removed(String),
+}
+
+pub enum FindLintError {
+ NotFound,
+ Removed,
+}
+
+struct LintAlias {
+ name: &'static str,
+ /// Whether deprecation warnings should be suppressed for this alias.
+ silent: bool,
+}
+
+struct LintGroup {
+ lint_ids: Vec<LintId>,
+ from_plugin: bool,
+ depr: Option<LintAlias>,
+}
+
+pub enum CheckLintNameResult<'a> {
+ Ok(&'a [LintId]),
+ /// Lint doesn't exist. Potentially contains a suggestion for a correct lint name.
+ NoLint(Option<Symbol>),
+ /// The lint is either renamed or removed. This is the warning
+ /// message, and an optional new name (`None` if removed).
+ Warning(String, Option<String>),
+ /// The lint is from a tool. If the Option is None, then either
+ /// the lint does not exist in the tool or the code was not
+ /// compiled with the tool and therefore the lint was never
+ /// added to the `LintStore`. Otherwise the `LintId` will be
+ /// returned as if it where a rustc lint.
+ Tool(Result<&'a [LintId], (Option<&'a [LintId]>, String)>),
+}
+
+impl LintStore {
+ pub fn new() -> LintStore {
+ LintStore {
+ lints: vec![],
+ pre_expansion_passes: vec![],
+ early_passes: vec![],
+ late_passes: vec![],
+ late_module_passes: vec![],
+ by_name: Default::default(),
+ lint_groups: Default::default(),
+ }
+ }
+
+ pub fn get_lints<'t>(&'t self) -> &'t [&'static Lint] {
+ &self.lints
+ }
+
+ pub fn get_lint_groups<'t>(&'t self) -> Vec<(&'static str, Vec<LintId>, bool)> {
+ self.lint_groups
+ .iter()
+ .filter(|(_, LintGroup { depr, .. })| {
+ // Don't display deprecated lint groups.
+ depr.is_none()
+ })
+ .map(|(k, LintGroup { lint_ids, from_plugin, .. })| {
+ (*k, lint_ids.clone(), *from_plugin)
+ })
+ .collect()
+ }
+
+ pub fn register_early_pass(
+ &mut self,
+ pass: impl Fn() -> EarlyLintPassObject + 'static + sync::Send + sync::Sync,
+ ) {
+ self.early_passes.push(Box::new(pass));
+ }
+
+ pub fn register_pre_expansion_pass(
+ &mut self,
+ pass: impl Fn() -> EarlyLintPassObject + 'static + sync::Send + sync::Sync,
+ ) {
+ self.pre_expansion_passes.push(Box::new(pass));
+ }
+
+ pub fn register_late_pass(
+ &mut self,
+ pass: impl Fn() -> LateLintPassObject + 'static + sync::Send + sync::Sync,
+ ) {
+ self.late_passes.push(Box::new(pass));
+ }
+
+ pub fn register_late_mod_pass(
+ &mut self,
+ pass: impl Fn() -> LateLintPassObject + 'static + sync::Send + sync::Sync,
+ ) {
+ self.late_module_passes.push(Box::new(pass));
+ }
+
+ // Helper method for register_early/late_pass
+ pub fn register_lints(&mut self, lints: &[&'static Lint]) {
+ for lint in lints {
+ self.lints.push(lint);
+
+ let id = LintId::of(lint);
+ if self.by_name.insert(lint.name_lower(), Id(id)).is_some() {
+ bug!("duplicate specification of lint {}", lint.name_lower())
+ }
+
+ if let Some(FutureIncompatibleInfo { edition, .. }) = lint.future_incompatible {
+ if let Some(edition) = edition {
+ self.lint_groups
+ .entry(edition.lint_name())
+ .or_insert(LintGroup {
+ lint_ids: vec![],
+ from_plugin: lint.is_plugin,
+ depr: None,
+ })
+ .lint_ids
+ .push(id);
+ }
+
+ self.lint_groups
+ .entry("future_incompatible")
+ .or_insert(LintGroup {
+ lint_ids: vec![],
+ from_plugin: lint.is_plugin,
+ depr: None,
+ })
+ .lint_ids
+ .push(id);
+ }
+ }
+ }
+
+ pub fn register_group_alias(&mut self, lint_name: &'static str, alias: &'static str) {
+ self.lint_groups.insert(
+ alias,
+ LintGroup {
+ lint_ids: vec![],
+ from_plugin: false,
+ depr: Some(LintAlias { name: lint_name, silent: true }),
+ },
+ );
+ }
+
+ pub fn register_group(
+ &mut self,
+ from_plugin: bool,
+ name: &'static str,
+ deprecated_name: Option<&'static str>,
+ to: Vec<LintId>,
+ ) {
+ let new = self
+ .lint_groups
+ .insert(name, LintGroup { lint_ids: to, from_plugin, depr: None })
+ .is_none();
+ if let Some(deprecated) = deprecated_name {
+ self.lint_groups.insert(
+ deprecated,
+ LintGroup {
+ lint_ids: vec![],
+ from_plugin,
+ depr: Some(LintAlias { name, silent: false }),
+ },
+ );
+ }
+
+ if !new {
+ bug!("duplicate specification of lint group {}", name);
+ }
+ }
+
+ pub fn register_renamed(&mut self, old_name: &str, new_name: &str) {
+ let target = match self.by_name.get(new_name) {
+ Some(&Id(lint_id)) => lint_id.clone(),
+ _ => bug!("invalid lint renaming of {} to {}", old_name, new_name),
+ };
+ self.by_name.insert(old_name.to_string(), Renamed(new_name.to_string(), target));
+ }
+
+ pub fn register_removed(&mut self, name: &str, reason: &str) {
+ self.by_name.insert(name.into(), Removed(reason.into()));
+ }
+
+ pub fn find_lints(&self, mut lint_name: &str) -> Result<Vec<LintId>, FindLintError> {
+ match self.by_name.get(lint_name) {
+ Some(&Id(lint_id)) => Ok(vec![lint_id]),
+ Some(&Renamed(_, lint_id)) => Ok(vec![lint_id]),
+ Some(&Removed(_)) => Err(FindLintError::Removed),
+ None => loop {
+ return match self.lint_groups.get(lint_name) {
+ Some(LintGroup { lint_ids, depr, .. }) => {
+ if let Some(LintAlias { name, .. }) = depr {
+ lint_name = name;
+ continue;
+ }
+ Ok(lint_ids.clone())
+ }
+ None => Err(FindLintError::Removed),
+ };
+ },
+ }
+ }
+
+ /// Checks the validity of lint names derived from the command line
+ pub fn check_lint_name_cmdline(&self, sess: &Session, lint_name: &str, level: Level) {
+ let db = match self.check_lint_name(lint_name, None) {
+ CheckLintNameResult::Ok(_) => None,
+ CheckLintNameResult::Warning(ref msg, _) => Some(sess.struct_warn(msg)),
+ CheckLintNameResult::NoLint(suggestion) => {
+ let mut err =
+ struct_span_err!(sess, DUMMY_SP, E0602, "unknown lint: `{}`", lint_name);
+
+ if let Some(suggestion) = suggestion {
+ err.help(&format!("did you mean: `{}`", suggestion));
+ }
+
+ Some(err)
+ }
+ CheckLintNameResult::Tool(result) => match result {
+ Err((Some(_), new_name)) => Some(sess.struct_warn(&format!(
+ "lint name `{}` is deprecated \
+ and does not have an effect anymore. \
+ Use: {}",
+ lint_name, new_name
+ ))),
+ _ => None,
+ },
+ };
+
+ if let Some(mut db) = db {
+ let msg = format!(
+ "requested on the command line with `{} {}`",
+ match level {
+ Level::Allow => "-A",
+ Level::Warn => "-W",
+ Level::Deny => "-D",
+ Level::Forbid => "-F",
+ },
+ lint_name
+ );
+ db.note(&msg);
+ db.emit();
+ }
+ }
+
+ /// Checks the name of a lint for its existence, and whether it was
+ /// renamed or removed. Generates a DiagnosticBuilder containing a
+ /// warning for renamed and removed lints. This is over both lint
+ /// names from attributes and those passed on the command line. Since
+ /// it emits non-fatal warnings and there are *two* lint passes that
+ /// inspect attributes, this is only run from the late pass to avoid
+ /// printing duplicate warnings.
+ pub fn check_lint_name(
+ &self,
+ lint_name: &str,
+ tool_name: Option<Symbol>,
+ ) -> CheckLintNameResult<'_> {
+ let complete_name = if let Some(tool_name) = tool_name {
+ format!("{}::{}", tool_name, lint_name)
+ } else {
+ lint_name.to_string()
+ };
+ // If the lint was scoped with `tool::` check if the tool lint exists
+ if let Some(_) = tool_name {
+ match self.by_name.get(&complete_name) {
+ None => match self.lint_groups.get(&*complete_name) {
+ None => return CheckLintNameResult::Tool(Err((None, String::new()))),
+ Some(LintGroup { lint_ids, .. }) => {
+ return CheckLintNameResult::Tool(Ok(&lint_ids));
+ }
+ },
+ Some(&Id(ref id)) => return CheckLintNameResult::Tool(Ok(slice::from_ref(id))),
+ // If the lint was registered as removed or renamed by the lint tool, we don't need
+ // to treat tool_lints and rustc lints different and can use the code below.
+ _ => {}
+ }
+ }
+ match self.by_name.get(&complete_name) {
+ Some(&Renamed(ref new_name, _)) => CheckLintNameResult::Warning(
+ format!("lint `{}` has been renamed to `{}`", complete_name, new_name),
+ Some(new_name.to_owned()),
+ ),
+ Some(&Removed(ref reason)) => CheckLintNameResult::Warning(
+ format!("lint `{}` has been removed: `{}`", complete_name, reason),
+ None,
+ ),
+ None => match self.lint_groups.get(&*complete_name) {
+ // If neither the lint, nor the lint group exists check if there is a `clippy::`
+ // variant of this lint
+ None => self.check_tool_name_for_backwards_compat(&complete_name, "clippy"),
+ Some(LintGroup { lint_ids, depr, .. }) => {
+ // Check if the lint group name is deprecated
+ if let Some(LintAlias { name, silent }) = depr {
+ let LintGroup { lint_ids, .. } = self.lint_groups.get(name).unwrap();
+ return if *silent {
+ CheckLintNameResult::Ok(&lint_ids)
+ } else {
+ CheckLintNameResult::Tool(Err((Some(&lint_ids), name.to_string())))
+ };
+ }
+ CheckLintNameResult::Ok(&lint_ids)
+ }
+ },
+ Some(&Id(ref id)) => CheckLintNameResult::Ok(slice::from_ref(id)),
+ }
+ }
+
+ fn check_tool_name_for_backwards_compat(
+ &self,
+ lint_name: &str,
+ tool_name: &str,
+ ) -> CheckLintNameResult<'_> {
+ let complete_name = format!("{}::{}", tool_name, lint_name);
+ match self.by_name.get(&complete_name) {
+ None => match self.lint_groups.get(&*complete_name) {
+ // Now we are sure, that this lint exists nowhere
+ None => {
+ let symbols =
+ self.by_name.keys().map(|name| Symbol::intern(&name)).collect::<Vec<_>>();
+
+ let suggestion =
+ find_best_match_for_name(symbols.iter(), &lint_name.to_lowercase(), None);
+
+ CheckLintNameResult::NoLint(suggestion)
+ }
+ Some(LintGroup { lint_ids, depr, .. }) => {
+ // Reaching this would be weird, but let's cover this case anyway
+ if let Some(LintAlias { name, silent }) = depr {
+ let LintGroup { lint_ids, .. } = self.lint_groups.get(name).unwrap();
+ return if *silent {
+ CheckLintNameResult::Tool(Err((Some(&lint_ids), complete_name)))
+ } else {
+ CheckLintNameResult::Tool(Err((Some(&lint_ids), name.to_string())))
+ };
+ }
+ CheckLintNameResult::Tool(Err((Some(&lint_ids), complete_name)))
+ }
+ },
+ Some(&Id(ref id)) => {
+ CheckLintNameResult::Tool(Err((Some(slice::from_ref(id)), complete_name)))
+ }
+ _ => CheckLintNameResult::NoLint(None),
+ }
+ }
+}
+
+/// Context for lint checking after type checking.
+pub struct LateContext<'a, 'tcx> {
+ /// Type context we're checking in.
+ pub tcx: TyCtxt<'tcx>,
+
+ /// Side-tables for the body we are in.
+ // FIXME: Make this lazy to avoid running the TypeckTables query?
+ pub tables: &'a ty::TypeckTables<'tcx>,
+
+ /// Parameter environment for the item we are in.
+ pub param_env: ty::ParamEnv<'tcx>,
+
+ /// Items accessible from the crate being checked.
+ pub access_levels: &'a AccessLevels,
+
+ /// The store of registered lints and the lint levels.
+ pub lint_store: &'tcx LintStore,
+
+ pub last_node_with_lint_attrs: hir::HirId,
+
+ /// Generic type parameters in scope for the item we are in.
+ pub generics: Option<&'tcx hir::Generics<'tcx>>,
+
+ /// We are only looking at one module
+ pub only_module: bool,
+}
+
+/// Context for lint checking of the AST, after expansion, before lowering to
+/// HIR.
+pub struct EarlyContext<'a> {
+ /// Type context we're checking in.
+ pub sess: &'a Session,
+
+ /// The crate being checked.
+ pub krate: &'a ast::Crate,
+
+ pub builder: LintLevelsBuilder<'a>,
+
+ /// The store of registered lints and the lint levels.
+ pub lint_store: &'a LintStore,
+
+ pub buffered: LintBuffer,
+}
+
+pub trait LintPassObject: Sized {}
+
+impl LintPassObject for EarlyLintPassObject {}
+
+impl LintPassObject for LateLintPassObject {}
+
+pub trait LintContext: Sized {
+ type PassObject: LintPassObject;
+
+ fn sess(&self) -> &Session;
+ fn lints(&self) -> &LintStore;
+
+ fn lookup_and_emit<S: Into<MultiSpan>>(&self, lint: &'static Lint, span: Option<S>, msg: &str) {
+ self.lookup(lint, span, msg).emit();
+ }
+
+ fn lookup_and_emit_with_diagnostics<S: Into<MultiSpan>>(
+ &self,
+ lint: &'static Lint,
+ span: Option<S>,
+ msg: &str,
+ diagnostic: BuiltinLintDiagnostics,
+ ) {
+ let mut db = self.lookup(lint, span, msg);
+
+ let sess = self.sess();
+ match diagnostic {
+ BuiltinLintDiagnostics::Normal => (),
+ BuiltinLintDiagnostics::BareTraitObject(span, is_global) => {
+ let (sugg, app) = match sess.source_map().span_to_snippet(span) {
+ Ok(s) if is_global => {
+ (format!("dyn ({})", s), Applicability::MachineApplicable)
+ }
+ Ok(s) => (format!("dyn {}", s), Applicability::MachineApplicable),
+ Err(_) => ("dyn <type>".to_string(), Applicability::HasPlaceholders),
+ };
+ db.span_suggestion(span, "use `dyn`", sugg, app);
+ }
+ BuiltinLintDiagnostics::AbsPathWithModule(span) => {
+ let (sugg, app) = match sess.source_map().span_to_snippet(span) {
+ Ok(ref s) => {
+ // FIXME(Manishearth) ideally the emitting code
+ // can tell us whether or not this is global
+ let opt_colon = if s.trim_start().starts_with("::") { "" } else { "::" };
+
+ (format!("crate{}{}", opt_colon, s), Applicability::MachineApplicable)
+ }
+ Err(_) => ("crate::<path>".to_string(), Applicability::HasPlaceholders),
+ };
+ db.span_suggestion(span, "use `crate`", sugg, app);
+ }
+ BuiltinLintDiagnostics::ProcMacroDeriveResolutionFallback(span) => {
+ db.span_label(
+ span,
+ "names from parent modules are not accessible without an explicit import",
+ );
+ }
+ BuiltinLintDiagnostics::MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def) => {
+ db.span_note(span_def, "the macro is defined here");
+ }
+ BuiltinLintDiagnostics::ElidedLifetimesInPaths(
+ n,
+ path_span,
+ incl_angl_brckt,
+ insertion_span,
+ anon_lts,
+ ) => {
+ add_elided_lifetime_in_path_suggestion(
+ sess,
+ &mut db,
+ n,
+ path_span,
+ incl_angl_brckt,
+ insertion_span,
+ anon_lts,
+ );
+ }
+ BuiltinLintDiagnostics::UnknownCrateTypes(span, note, sugg) => {
+ db.span_suggestion(span, ¬e, sugg, Applicability::MaybeIncorrect);
+ }
+ BuiltinLintDiagnostics::UnusedImports(message, replaces) => {
+ if !replaces.is_empty() {
+ db.tool_only_multipart_suggestion(
+ &message,
+ replaces,
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ BuiltinLintDiagnostics::RedundantImport(spans, ident) => {
+ for (span, is_imported) in spans {
+ let introduced = if is_imported { "imported" } else { "defined" };
+ db.span_label(
+ span,
+ format!("the item `{}` is already {} here", ident, introduced),
+ );
+ }
+ }
+ BuiltinLintDiagnostics::DeprecatedMacro(suggestion, span) => {
+ stability::deprecation_suggestion(&mut db, suggestion, span)
+ }
+ }
+
+ db.emit();
+ }
+
+ fn lookup<S: Into<MultiSpan>>(
+ &self,
+ lint: &'static Lint,
+ span: Option<S>,
+ msg: &str,
+ ) -> DiagnosticBuilder<'_>;
+
+ /// Emit a lint at the appropriate level, for a particular span.
+ fn span_lint<S: Into<MultiSpan>>(&self, lint: &'static Lint, span: S, msg: &str) {
+ self.lookup_and_emit(lint, Some(span), msg);
+ }
+
+ fn struct_span_lint<S: Into<MultiSpan>>(
+ &self,
+ lint: &'static Lint,
+ span: S,
+ msg: &str,
+ ) -> DiagnosticBuilder<'_> {
+ self.lookup(lint, Some(span), msg)
+ }
+
+ /// Emit a lint and note at the appropriate level, for a particular span.
+ fn span_lint_note(
+ &self,
+ lint: &'static Lint,
+ span: Span,
+ msg: &str,
+ note_span: Span,
+ note: &str,
+ ) {
+ let mut err = self.lookup(lint, Some(span), msg);
+ if note_span == span {
+ err.note(note);
+ } else {
+ err.span_note(note_span, note);
+ }
+ err.emit();
+ }
+
+ /// Emit a lint and help at the appropriate level, for a particular span.
+ fn span_lint_help(&self, lint: &'static Lint, span: Span, msg: &str, help: &str) {
+ let mut err = self.lookup(lint, Some(span), msg);
+ self.span_lint(lint, span, msg);
+ err.span_help(span, help);
+ err.emit();
+ }
+
+ /// Emit a lint at the appropriate level, with no associated span.
+ fn lint(&self, lint: &'static Lint, msg: &str) {
+ self.lookup_and_emit(lint, None as Option<Span>, msg);
+ }
+}
+
+impl<'a> EarlyContext<'a> {
+ pub fn new(
+ sess: &'a Session,
+ lint_store: &'a LintStore,
+ krate: &'a ast::Crate,
+ buffered: LintBuffer,
+ warn_about_weird_lints: bool,
+ ) -> EarlyContext<'a> {
+ EarlyContext {
+ sess,
+ krate,
+ lint_store,
+ builder: LintLevelsBuilder::new(sess, warn_about_weird_lints, lint_store),
+ buffered,
+ }
+ }
+}
+
+impl LintContext for LateContext<'_, '_> {
+ type PassObject = LateLintPassObject;
+
+ /// Gets the overall compiler `Session` object.
+ fn sess(&self) -> &Session {
+ &self.tcx.sess
+ }
+
+ fn lints(&self) -> &LintStore {
+ &*self.lint_store
+ }
+
+ fn lookup<S: Into<MultiSpan>>(
+ &self,
+ lint: &'static Lint,
+ span: Option<S>,
+ msg: &str,
+ ) -> DiagnosticBuilder<'_> {
+ let hir_id = self.last_node_with_lint_attrs;
+
+ match span {
+ Some(s) => self.tcx.struct_span_lint_hir(lint, hir_id, s, msg),
+ None => self.tcx.struct_lint_node(lint, hir_id, msg),
+ }
+ }
+}
+
+impl LintContext for EarlyContext<'_> {
+ type PassObject = EarlyLintPassObject;
+
+ /// Gets the overall compiler `Session` object.
+ fn sess(&self) -> &Session {
+ &self.sess
+ }
+
+ fn lints(&self) -> &LintStore {
+ &*self.lint_store
+ }
+
+ fn lookup<S: Into<MultiSpan>>(
+ &self,
+ lint: &'static Lint,
+ span: Option<S>,
+ msg: &str,
+ ) -> DiagnosticBuilder<'_> {
+ self.builder.struct_lint(lint, span.map(|s| s.into()), msg)
+ }
+}
+
+impl<'a, 'tcx> LateContext<'a, 'tcx> {
+ pub fn current_lint_root(&self) -> hir::HirId {
+ self.last_node_with_lint_attrs
+ }
+
+ /// Check if a `DefId`'s path matches the given absolute type path usage.
+ ///
+ /// Anonymous scopes such as `extern` imports are matched with `kw::Invalid`;
+ /// inherent `impl` blocks are matched with the name of the type.
+ ///
+ /// # Examples
+ ///
+ /// ```rust,ignore (no context or def id available)
+ /// if cx.match_def_path(def_id, &[sym::core, sym::option, sym::Option]) {
+ /// // The given `def_id` is that of an `Option` type
+ /// }
+ /// ```
+ pub fn match_def_path(&self, def_id: DefId, path: &[Symbol]) -> bool {
+ let names = self.get_def_path(def_id);
+
+ names.len() == path.len() && names.into_iter().zip(path.iter()).all(|(a, &b)| a == b)
+ }
+
+ /// Gets the absolute path of `def_id` as a vector of `Symbol`.
+ ///
+ /// # Examples
+ ///
+ /// ```rust,ignore (no context or def id available)
+ /// let def_path = cx.get_def_path(def_id);
+ /// if let &[sym::core, sym::option, sym::Option] = &def_path[..] {
+ /// // The given `def_id` is that of an `Option` type
+ /// }
+ /// ```
+ pub fn get_def_path(&self, def_id: DefId) -> Vec<Symbol> {
+ pub struct AbsolutePathPrinter<'tcx> {
+ pub tcx: TyCtxt<'tcx>,
+ }
+
+ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> {
+ type Error = !;
+
+ type Path = Vec<Symbol>;
+ type Region = ();
+ type Type = ();
+ type DynExistential = ();
+ type Const = ();
+
+ fn tcx(&self) -> TyCtxt<'tcx> {
+ self.tcx
+ }
+
+ fn print_region(self, _region: ty::Region<'_>) -> Result<Self::Region, Self::Error> {
+ Ok(())
+ }
+
+ fn print_type(self, _ty: Ty<'tcx>) -> Result<Self::Type, Self::Error> {
+ Ok(())
+ }
+
+ fn print_dyn_existential(
+ self,
+ _predicates: &'tcx ty::List<ty::ExistentialPredicate<'tcx>>,
+ ) -> Result<Self::DynExistential, Self::Error> {
+ Ok(())
+ }
+
+ fn print_const(self, _ct: &'tcx ty::Const<'tcx>) -> Result<Self::Const, Self::Error> {
+ Ok(())
+ }
+
+ fn path_crate(self, cnum: CrateNum) -> Result<Self::Path, Self::Error> {
+ Ok(vec![self.tcx.original_crate_name(cnum)])
+ }
+
+ fn path_qualified(
+ self,
+ self_ty: Ty<'tcx>,
+ trait_ref: Option<ty::TraitRef<'tcx>>,
+ ) -> Result<Self::Path, Self::Error> {
+ if trait_ref.is_none() {
+ if let ty::Adt(def, substs) = self_ty.kind {
+ return self.print_def_path(def.did, substs);
+ }
+ }
+
+ // This shouldn't ever be needed, but just in case:
+ Ok(vec![match trait_ref {
+ Some(trait_ref) => Symbol::intern(&format!("{:?}", trait_ref)),
+ None => Symbol::intern(&format!("<{}>", self_ty)),
+ }])
+ }
+
+ fn path_append_impl(
+ self,
+ print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
+ _disambiguated_data: &DisambiguatedDefPathData,
+ self_ty: Ty<'tcx>,
+ trait_ref: Option<ty::TraitRef<'tcx>>,
+ ) -> Result<Self::Path, Self::Error> {
+ let mut path = print_prefix(self)?;
+
+ // This shouldn't ever be needed, but just in case:
+ path.push(match trait_ref {
+ Some(trait_ref) => Symbol::intern(&format!(
+ "<impl {} for {}>",
+ trait_ref.print_only_trait_path(),
+ self_ty
+ )),
+ None => Symbol::intern(&format!("<impl {}>", self_ty)),
+ });
+
+ Ok(path)
+ }
+
+ fn path_append(
+ self,
+ print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
+ disambiguated_data: &DisambiguatedDefPathData,
+ ) -> Result<Self::Path, Self::Error> {
+ let mut path = print_prefix(self)?;
+
+ // Skip `::{{constructor}}` on tuple/unit structs.
+ match disambiguated_data.data {
+ DefPathData::Ctor => return Ok(path),
+ _ => {}
+ }
+
+ path.push(disambiguated_data.data.as_symbol());
+ Ok(path)
+ }
+
+ fn path_generic_args(
+ self,
+ print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
+ _args: &[GenericArg<'tcx>],
+ ) -> Result<Self::Path, Self::Error> {
+ print_prefix(self)
+ }
+ }
+
+ AbsolutePathPrinter { tcx: self.tcx }.print_def_path(def_id, &[]).unwrap()
+ }
+}
+
+impl<'a, 'tcx> LayoutOf for LateContext<'a, 'tcx> {
+ type Ty = Ty<'tcx>;
+ type TyLayout = Result<TyLayout<'tcx>, LayoutError<'tcx>>;
+
+ fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyLayout {
+ self.tcx.layout_of(self.param_env.and(ty))
+ }
+}
//! upon. As the ast is traversed, this keeps track of the current lint level
//! for all lint attributes.
-use rustc::lint::{EarlyContext, LintStore};
-use rustc::lint::{EarlyLintPass, EarlyLintPassObject};
-use rustc::lint::{LintBuffer, LintContext, LintPass};
-use rustc::session::Session;
-
+use crate::context::{EarlyContext, LintContext, LintStore};
+use crate::passes::{EarlyLintPass, EarlyLintPassObject};
+use rustc_session::lint::{LintBuffer, LintPass};
+use rustc_session::Session;
use rustc_span::Span;
-use std::slice;
use syntax::ast;
use syntax::visit as ast_visit;
use log::debug;
+use std::slice;
macro_rules! run_early_pass { ($cx:expr, $f:ident, $($args:expr),*) => ({
$cx.pass.$f(&$cx.context, $($args),*);
)
}
-early_lint_methods!(early_lint_pass_impl, []);
+crate::early_lint_methods!(early_lint_pass_impl, []);
fn early_lint_crate<T: EarlyLintPass>(
sess: &Session,
}
} else {
for pass in &mut passes {
- buffered = sess.time(&format!("running lint: {}", pass.name()), || {
- early_lint_crate(
- sess,
- lint_store,
- krate,
- EarlyLintPassObjects { lints: slice::from_mut(pass) },
- buffered,
- pre_expansion,
- )
- });
+ buffered = sess
+ .prof
+ .extra_verbose_generic_activity(&format!("running lint: {}", pass.name()))
+ .run(|| {
+ early_lint_crate(
+ sess,
+ lint_store,
+ krate,
+ EarlyLintPassObjects { lints: slice::from_mut(pass) },
+ buffered,
+ pre_expansion,
+ )
+ });
}
}
--- /dev/null
+//! Some lints that are only useful in the compiler or crates that use compiler internals, such as
+//! Clippy.
+
+use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
+use rustc_data_structures::fx::FxHashMap;
+use rustc_errors::Applicability;
+use rustc_hir::{GenericArg, HirId, MutTy, Mutability, Path, PathSegment, QPath, Ty, TyKind};
+use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
+use rustc_span::symbol::{sym, Symbol};
+use syntax::ast::{Ident, Item, ItemKind};
+
+declare_tool_lint! {
+ pub rustc::DEFAULT_HASH_TYPES,
+ Allow,
+ "forbid HashMap and HashSet and suggest the FxHash* variants",
+ report_in_external_macro: true
+}
+
+pub struct DefaultHashTypes {
+ map: FxHashMap<Symbol, Symbol>,
+}
+
+impl DefaultHashTypes {
+ // we are allowed to use `HashMap` and `HashSet` as identifiers for implementing the lint itself
+ #[allow(rustc::default_hash_types)]
+ pub fn new() -> Self {
+ let mut map = FxHashMap::default();
+ map.insert(sym::HashMap, sym::FxHashMap);
+ map.insert(sym::HashSet, sym::FxHashSet);
+ Self { map }
+ }
+}
+
+impl_lint_pass!(DefaultHashTypes => [DEFAULT_HASH_TYPES]);
+
+impl EarlyLintPass for DefaultHashTypes {
+ fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: Ident) {
+ if let Some(replace) = self.map.get(&ident.name) {
+ let msg = format!("Prefer {} over {}, it has better performance", replace, ident);
+ let mut db = cx.struct_span_lint(DEFAULT_HASH_TYPES, ident.span, &msg);
+ db.span_suggestion(
+ ident.span,
+ "use",
+ replace.to_string(),
+ Applicability::MaybeIncorrect, // FxHashMap, ... needs another import
+ );
+ db.note(&format!("a `use rustc_data_structures::fx::{}` may be necessary", replace))
+ .emit();
+ }
+ }
+}
+
+declare_tool_lint! {
+ pub rustc::USAGE_OF_TY_TYKIND,
+ Allow,
+ "usage of `ty::TyKind` outside of the `ty::sty` module",
+ report_in_external_macro: true
+}
+
+declare_tool_lint! {
+ pub rustc::TY_PASS_BY_REFERENCE,
+ Allow,
+ "passing `Ty` or `TyCtxt` by reference",
+ report_in_external_macro: true
+}
+
+declare_tool_lint! {
+ pub rustc::USAGE_OF_QUALIFIED_TY,
+ Allow,
+ "using `ty::{Ty,TyCtxt}` instead of importing it",
+ report_in_external_macro: true
+}
+
+declare_lint_pass!(TyTyKind => [
+ USAGE_OF_TY_TYKIND,
+ TY_PASS_BY_REFERENCE,
+ USAGE_OF_QUALIFIED_TY,
+]);
+
+impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TyTyKind {
+ fn check_path(&mut self, cx: &LateContext<'_, '_>, path: &'tcx Path<'tcx>, _: HirId) {
+ let segments = path.segments.iter().rev().skip(1).rev();
+
+ if let Some(last) = segments.last() {
+ let span = path.span.with_hi(last.ident.span.hi());
+ if lint_ty_kind_usage(cx, last) {
+ cx.struct_span_lint(USAGE_OF_TY_TYKIND, span, "usage of `ty::TyKind::<kind>`")
+ .span_suggestion(
+ span,
+ "try using ty::<kind> directly",
+ "ty".to_string(),
+ Applicability::MaybeIncorrect, // ty maybe needs an import
+ )
+ .emit();
+ }
+ }
+ }
+
+ fn check_ty(&mut self, cx: &LateContext<'_, '_>, ty: &'tcx Ty<'tcx>) {
+ match &ty.kind {
+ TyKind::Path(qpath) => {
+ if let QPath::Resolved(_, path) = qpath {
+ if let Some(last) = path.segments.iter().last() {
+ if lint_ty_kind_usage(cx, last) {
+ cx.struct_span_lint(
+ USAGE_OF_TY_TYKIND,
+ path.span,
+ "usage of `ty::TyKind`",
+ )
+ .help("try using `Ty` instead")
+ .emit();
+ } else {
+ if ty.span.from_expansion() {
+ return;
+ }
+ if let Some(t) = is_ty_or_ty_ctxt(cx, ty) {
+ if path.segments.len() > 1 {
+ cx.struct_span_lint(
+ USAGE_OF_QUALIFIED_TY,
+ path.span,
+ &format!("usage of qualified `ty::{}`", t),
+ )
+ .span_suggestion(
+ path.span,
+ "try using it unqualified",
+ t,
+ // The import probably needs to be changed
+ Applicability::MaybeIncorrect,
+ )
+ .emit();
+ }
+ }
+ }
+ }
+ }
+ }
+ TyKind::Rptr(_, MutTy { ty: inner_ty, mutbl: Mutability::Not }) => {
+ if let Some(impl_did) = cx.tcx.impl_of_method(ty.hir_id.owner_def_id()) {
+ if cx.tcx.impl_trait_ref(impl_did).is_some() {
+ return;
+ }
+ }
+ if let Some(t) = is_ty_or_ty_ctxt(cx, &inner_ty) {
+ cx.struct_span_lint(
+ TY_PASS_BY_REFERENCE,
+ ty.span,
+ &format!("passing `{}` by reference", t),
+ )
+ .span_suggestion(
+ ty.span,
+ "try passing by value",
+ t,
+ // Changing type of function argument
+ Applicability::MaybeIncorrect,
+ )
+ .emit();
+ }
+ }
+ _ => {}
+ }
+ }
+}
+
+fn lint_ty_kind_usage(cx: &LateContext<'_, '_>, segment: &PathSegment<'_>) -> bool {
+ if let Some(res) = segment.res {
+ if let Some(did) = res.opt_def_id() {
+ return cx.tcx.is_diagnostic_item(sym::TyKind, did);
+ }
+ }
+
+ false
+}
+
+fn is_ty_or_ty_ctxt(cx: &LateContext<'_, '_>, ty: &Ty<'_>) -> Option<String> {
+ match &ty.kind {
+ TyKind::Path(qpath) => {
+ if let QPath::Resolved(_, path) = qpath {
+ let did = path.res.opt_def_id()?;
+ if cx.tcx.is_diagnostic_item(sym::Ty, did) {
+ return Some(format!("Ty{}", gen_args(path.segments.last().unwrap())));
+ } else if cx.tcx.is_diagnostic_item(sym::TyCtxt, did) {
+ return Some(format!("TyCtxt{}", gen_args(path.segments.last().unwrap())));
+ }
+ }
+ }
+ _ => {}
+ }
+
+ None
+}
+
+fn gen_args(segment: &PathSegment<'_>) -> String {
+ if let Some(args) = &segment.args {
+ let lifetimes = args
+ .args
+ .iter()
+ .filter_map(|arg| {
+ if let GenericArg::Lifetime(lt) = arg {
+ Some(lt.name.ident().to_string())
+ } else {
+ None
+ }
+ })
+ .collect::<Vec<_>>();
+
+ if !lifetimes.is_empty() {
+ return format!("<{}>", lifetimes.join(", "));
+ }
+ }
+
+ String::new()
+}
+
+declare_tool_lint! {
+ pub rustc::LINT_PASS_IMPL_WITHOUT_MACRO,
+ Allow,
+ "`impl LintPass` without the `declare_lint_pass!` or `impl_lint_pass!` macros"
+}
+
+declare_lint_pass!(LintPassImpl => [LINT_PASS_IMPL_WITHOUT_MACRO]);
+
+impl EarlyLintPass for LintPassImpl {
+ fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
+ if let ItemKind::Impl(_, _, _, _, Some(lint_pass), _, _) = &item.kind {
+ if let Some(last) = lint_pass.path.segments.last() {
+ if last.ident.name == sym::LintPass {
+ let expn_data = lint_pass.path.span.ctxt().outer_expn_data();
+ let call_site = expn_data.call_site;
+ if expn_data.kind.descr() != sym::impl_lint_pass
+ && call_site.ctxt().outer_expn_data().kind.descr() != sym::declare_lint_pass
+ {
+ cx.struct_span_lint(
+ LINT_PASS_IMPL_WITHOUT_MACRO,
+ lint_pass.path.span,
+ "implementing `LintPass` by hand",
+ )
+ .help("try using `declare_lint_pass!` or `impl_lint_pass!` instead")
+ .emit();
+ }
+ }
+ }
+ }
+ }
+}
//! upon. As the ast is traversed, this keeps track of the current lint level
//! for all lint attributes.
-use rustc::hir::intravisit as hir_visit;
-use rustc::hir::intravisit::Visitor;
-use rustc::lint::LateContext;
-use rustc::lint::LintPass;
-use rustc::lint::{LateLintPass, LateLintPassObject};
+use crate::{passes::LateLintPassObject, LateContext, LateLintPass, LintStore};
+use rustc::hir::map::Map;
use rustc::ty::{self, TyCtxt};
use rustc_data_structures::sync::{join, par_iter, ParallelIterator};
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
+use rustc_hir::intravisit as hir_visit;
+use rustc_hir::intravisit::Visitor;
+use rustc_session::lint::LintPass;
use rustc_span::Span;
-use std::slice;
use syntax::ast;
+use syntax::walk_list;
use log::debug;
-use syntax::walk_list;
+use std::any::Any;
+use std::slice;
+
+/// Extract the `LintStore` from the query context.
+/// This function exists because we've erased `LintStore` as `dyn Any` in the context.
+crate fn unerased_lint_store<'tcx>(tcx: TyCtxt<'tcx>) -> &'tcx LintStore {
+ let store: &dyn Any = &*tcx.lint_store;
+ store.downcast_ref().unwrap()
+}
macro_rules! lint_callback { ($cx:expr, $f:ident, $($args:expr),*) => ({
$cx.pass.$f(&$cx.context, $($args),*);
impl<'a, 'tcx, T: LateLintPass<'a, 'tcx>> hir_visit::Visitor<'tcx>
for LateContextAndPass<'a, 'tcx, T>
{
+ type Map = Map<'tcx>;
+
/// Because lints are scoped lexically, we want to walk nested
/// items in the context of the outer item, so enable
/// deep-walking.
- fn nested_visit_map<'this>(&'this mut self) -> hir_visit::NestedVisitorMap<'this, 'tcx> {
+ fn nested_visit_map<'this>(&'this mut self) -> hir_visit::NestedVisitorMap<'this, Self::Map> {
hir_visit::NestedVisitorMap::All(&self.context.tcx.hir())
}
)
}
-late_lint_methods!(late_lint_pass_impl, [], ['tcx]);
+crate::late_lint_methods!(late_lint_pass_impl, [], ['tcx]);
fn late_lint_mod_pass<'tcx, T: for<'a> LateLintPass<'a, 'tcx>>(
tcx: TyCtxt<'tcx>,
tables: &ty::TypeckTables::empty(None),
param_env: ty::ParamEnv::empty(),
access_levels,
- lint_store: &tcx.lint_store,
+ lint_store: unerased_lint_store(tcx),
last_node_with_lint_attrs: tcx.hir().as_local_hir_id(module_def_id).unwrap(),
generics: None,
only_module: true,
late_lint_mod_pass(tcx, module_def_id, builtin_lints);
let mut passes: Vec<_> =
- tcx.lint_store.late_module_passes.iter().map(|pass| (pass)()).collect();
+ unerased_lint_store(tcx).late_module_passes.iter().map(|pass| (pass)()).collect();
if !passes.is_empty() {
late_lint_mod_pass(tcx, module_def_id, LateLintPassObjects { lints: &mut passes[..] });
tables: &ty::TypeckTables::empty(None),
param_env: ty::ParamEnv::empty(),
access_levels,
- lint_store: &tcx.lint_store,
+ lint_store: unerased_lint_store(tcx),
last_node_with_lint_attrs: hir::CRATE_HIR_ID,
generics: None,
only_module: false,
}
fn late_lint_crate<'tcx, T: for<'a> LateLintPass<'a, 'tcx>>(tcx: TyCtxt<'tcx>, builtin_lints: T) {
- let mut passes = tcx.lint_store.late_passes.iter().map(|p| (p)()).collect::<Vec<_>>();
+ let mut passes = unerased_lint_store(tcx).late_passes.iter().map(|p| (p)()).collect::<Vec<_>>();
if !tcx.sess.opts.debugging_opts.no_interleave_lints {
if !passes.is_empty() {
late_lint_pass_crate(tcx, builtin_lints);
} else {
for pass in &mut passes {
- tcx.sess.time(&format!("running late lint: {}", pass.name()), || {
- late_lint_pass_crate(tcx, LateLintPassObjects { lints: slice::from_mut(pass) });
- });
+ tcx.sess
+ .prof
+ .extra_verbose_generic_activity(&format!("running late lint: {}", pass.name()))
+ .run(|| {
+ late_lint_pass_crate(tcx, LateLintPassObjects { lints: slice::from_mut(pass) });
+ });
}
let mut passes: Vec<_> =
- tcx.lint_store.late_module_passes.iter().map(|pass| (pass)()).collect();
+ unerased_lint_store(tcx).late_module_passes.iter().map(|pass| (pass)()).collect();
for pass in &mut passes {
- tcx.sess.time(&format!("running late module lint: {}", pass.name()), || {
- late_lint_pass_crate(tcx, LateLintPassObjects { lints: slice::from_mut(pass) });
- });
+ tcx.sess
+ .prof
+ .extra_verbose_generic_activity(&format!(
+ "running late module lint: {}",
+ pass.name()
+ ))
+ .run(|| {
+ late_lint_pass_crate(tcx, LateLintPassObjects { lints: slice::from_mut(pass) });
+ });
}
}
}
) {
join(
|| {
- tcx.sess.time("crate lints", || {
+ tcx.sess.time("crate_lints", || {
// Run whole crate non-incremental lints
late_lint_crate(tcx, builtin_lints());
});
},
|| {
- tcx.sess.time("module lints", || {
+ tcx.sess.time("module_lints", || {
// Run per-module lints
par_iter(&tcx.hir().krate().modules).for_each(|(&module, _)| {
tcx.ensure().lint_mod(tcx.hir().local_def_id(module));
-use rustc::hir::intravisit;
-use rustc::lint::{LintLevelMap, LintLevelSets, LintLevelsBuilder, LintStore};
+use crate::context::{CheckLintNameResult, LintStore};
+use crate::late::unerased_lint_store;
+use rustc::hir::map::Map;
+use rustc::lint::struct_lint_level;
+use rustc::lint::{LintLevelMap, LintLevelSets, LintSet, LintSource};
use rustc::ty::query::Providers;
use rustc::ty::TyCtxt;
+use rustc_data_structures::fx::FxHashMap;
+use rustc_error_codes::*;
+use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
use rustc_hir as hir;
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
+use rustc_hir::hir_id::HirId;
+use rustc_hir::intravisit;
+use rustc_session::lint::{builtin, Level, Lint};
+use rustc_session::Session;
+use rustc_span::{sym, MultiSpan, Symbol};
use syntax::ast;
+use syntax::attr;
+use syntax::print::pprust;
+use syntax::sess::feature_err;
+use syntax::unwrap_or;
-pub use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintId};
+use std::cmp;
fn lint_levels(tcx: TyCtxt<'_>, cnum: CrateNum) -> &LintLevelMap {
assert_eq!(cnum, LOCAL_CRATE);
- let store = &tcx.lint_store;
- let mut builder = LintLevelMapBuilder {
- levels: LintLevelSets::builder(tcx.sess, false, &store),
- tcx: tcx,
- store: store,
- };
+ let store = unerased_lint_store(tcx);
+ let levels = LintLevelsBuilder::new(tcx.sess, false, &store);
+ let mut builder = LintLevelMapBuilder { levels, tcx, store };
let krate = tcx.hir().krate();
let push = builder.levels.push(&krate.attrs, &store);
tcx.arena.alloc(builder.levels.build_map())
}
+pub struct LintLevelsBuilder<'a> {
+ sess: &'a Session,
+ sets: LintLevelSets,
+ id_to_set: FxHashMap<HirId, u32>,
+ cur: u32,
+ warn_about_weird_lints: bool,
+}
+
+pub struct BuilderPush {
+ prev: u32,
+ pub changed: bool,
+}
+
+impl<'a> LintLevelsBuilder<'a> {
+ pub fn new(sess: &'a Session, warn_about_weird_lints: bool, store: &LintStore) -> Self {
+ let mut builder = LintLevelsBuilder {
+ sess,
+ sets: LintLevelSets::new(),
+ cur: 0,
+ id_to_set: Default::default(),
+ warn_about_weird_lints,
+ };
+ builder.process_command_line(sess, store);
+ assert_eq!(builder.sets.list.len(), 1);
+ builder
+ }
+
+ fn process_command_line(&mut self, sess: &Session, store: &LintStore) {
+ let mut specs = FxHashMap::default();
+ self.sets.lint_cap = sess.opts.lint_cap.unwrap_or(Level::Forbid);
+
+ for &(ref lint_name, level) in &sess.opts.lint_opts {
+ store.check_lint_name_cmdline(sess, &lint_name, level);
+
+ // If the cap is less than this specified level, e.g., if we've got
+ // `--cap-lints allow` but we've also got `-D foo` then we ignore
+ // this specification as the lint cap will set it to allow anyway.
+ let level = cmp::min(level, self.sets.lint_cap);
+
+ let lint_flag_val = Symbol::intern(lint_name);
+ let ids = match store.find_lints(&lint_name) {
+ Ok(ids) => ids,
+ Err(_) => continue, // errors handled in check_lint_name_cmdline above
+ };
+ for id in ids {
+ let src = LintSource::CommandLine(lint_flag_val);
+ specs.insert(id, (level, src));
+ }
+ }
+
+ self.sets.list.push(LintSet::CommandLine { specs });
+ }
+
+ /// Pushes a list of AST lint attributes onto this context.
+ ///
+ /// This function will return a `BuilderPush` object which should be passed
+ /// to `pop` when this scope for the attributes provided is exited.
+ ///
+ /// This function will perform a number of tasks:
+ ///
+ /// * It'll validate all lint-related attributes in `attrs`
+ /// * It'll mark all lint-related attributes as used
+ /// * Lint levels will be updated based on the attributes provided
+ /// * Lint attributes are validated, e.g., a #[forbid] can't be switched to
+ /// #[allow]
+ ///
+ /// Don't forget to call `pop`!
+ pub fn push(&mut self, attrs: &[ast::Attribute], store: &LintStore) -> BuilderPush {
+ let mut specs = FxHashMap::default();
+ let sess = self.sess;
+ let bad_attr = |span| struct_span_err!(sess, span, E0452, "malformed lint attribute input");
+ for attr in attrs {
+ let level = match Level::from_symbol(attr.name_or_empty()) {
+ None => continue,
+ Some(lvl) => lvl,
+ };
+
+ let meta = unwrap_or!(attr.meta(), continue);
+ attr::mark_used(attr);
+
+ let mut metas = unwrap_or!(meta.meta_item_list(), continue);
+
+ if metas.is_empty() {
+ // FIXME (#55112): issue unused-attributes lint for `#[level()]`
+ continue;
+ }
+
+ // Before processing the lint names, look for a reason (RFC 2383)
+ // at the end.
+ let mut reason = None;
+ let tail_li = &metas[metas.len() - 1];
+ if let Some(item) = tail_li.meta_item() {
+ match item.kind {
+ ast::MetaItemKind::Word => {} // actual lint names handled later
+ ast::MetaItemKind::NameValue(ref name_value) => {
+ if item.path == sym::reason {
+ // found reason, reslice meta list to exclude it
+ metas = &metas[0..metas.len() - 1];
+ // FIXME (#55112): issue unused-attributes lint if we thereby
+ // don't have any lint names (`#[level(reason = "foo")]`)
+ if let ast::LitKind::Str(rationale, _) = name_value.kind {
+ if !self.sess.features_untracked().lint_reasons {
+ feature_err(
+ &self.sess.parse_sess,
+ sym::lint_reasons,
+ item.span,
+ "lint reasons are experimental",
+ )
+ .emit();
+ }
+ reason = Some(rationale);
+ } else {
+ bad_attr(name_value.span)
+ .span_label(name_value.span, "reason must be a string literal")
+ .emit();
+ }
+ } else {
+ bad_attr(item.span)
+ .span_label(item.span, "bad attribute argument")
+ .emit();
+ }
+ }
+ ast::MetaItemKind::List(_) => {
+ bad_attr(item.span).span_label(item.span, "bad attribute argument").emit();
+ }
+ }
+ }
+
+ for li in metas {
+ let meta_item = match li.meta_item() {
+ Some(meta_item) if meta_item.is_word() => meta_item,
+ _ => {
+ let sp = li.span();
+ let mut err = bad_attr(sp);
+ let mut add_label = true;
+ if let Some(item) = li.meta_item() {
+ if let ast::MetaItemKind::NameValue(_) = item.kind {
+ if item.path == sym::reason {
+ err.span_label(sp, "reason in lint attribute must come last");
+ add_label = false;
+ }
+ }
+ }
+ if add_label {
+ err.span_label(sp, "bad attribute argument");
+ }
+ err.emit();
+ continue;
+ }
+ };
+ let tool_name = if meta_item.path.segments.len() > 1 {
+ let tool_ident = meta_item.path.segments[0].ident;
+ if !attr::is_known_lint_tool(tool_ident) {
+ struct_span_err!(
+ sess,
+ tool_ident.span,
+ E0710,
+ "an unknown tool name found in scoped lint: `{}`",
+ pprust::path_to_string(&meta_item.path),
+ )
+ .emit();
+ continue;
+ }
+
+ Some(tool_ident.name)
+ } else {
+ None
+ };
+ let name = meta_item.path.segments.last().expect("empty lint name").ident.name;
+ match store.check_lint_name(&name.as_str(), tool_name) {
+ CheckLintNameResult::Ok(ids) => {
+ let src = LintSource::Node(name, li.span(), reason);
+ for id in ids {
+ specs.insert(*id, (level, src));
+ }
+ }
+
+ CheckLintNameResult::Tool(result) => {
+ match result {
+ Ok(ids) => {
+ let complete_name = &format!("{}::{}", tool_name.unwrap(), name);
+ let src = LintSource::Node(
+ Symbol::intern(complete_name),
+ li.span(),
+ reason,
+ );
+ for id in ids {
+ specs.insert(*id, (level, src));
+ }
+ }
+ Err((Some(ids), new_lint_name)) => {
+ let lint = builtin::RENAMED_AND_REMOVED_LINTS;
+ let (lvl, src) =
+ self.sets.get_lint_level(lint, self.cur, Some(&specs), &sess);
+ let msg = format!(
+ "lint name `{}` is deprecated \
+ and may not have an effect in the future. \
+ Also `cfg_attr(cargo-clippy)` won't be necessary anymore",
+ name
+ );
+ struct_lint_level(
+ self.sess,
+ lint,
+ lvl,
+ src,
+ Some(li.span().into()),
+ &msg,
+ )
+ .span_suggestion(
+ li.span(),
+ "change it to",
+ new_lint_name.to_string(),
+ Applicability::MachineApplicable,
+ )
+ .emit();
+
+ let src = LintSource::Node(
+ Symbol::intern(&new_lint_name),
+ li.span(),
+ reason,
+ );
+ for id in ids {
+ specs.insert(*id, (level, src));
+ }
+ }
+ Err((None, _)) => {
+ // If Tool(Err(None, _)) is returned, then either the lint does not
+ // exist in the tool or the code was not compiled with the tool and
+ // therefore the lint was never added to the `LintStore`. To detect
+ // this is the responsibility of the lint tool.
+ }
+ }
+ }
+
+ _ if !self.warn_about_weird_lints => {}
+
+ CheckLintNameResult::Warning(msg, renamed) => {
+ let lint = builtin::RENAMED_AND_REMOVED_LINTS;
+ let (level, src) =
+ self.sets.get_lint_level(lint, self.cur, Some(&specs), &sess);
+ let mut err = struct_lint_level(
+ self.sess,
+ lint,
+ level,
+ src,
+ Some(li.span().into()),
+ &msg,
+ );
+ if let Some(new_name) = renamed {
+ err.span_suggestion(
+ li.span(),
+ "use the new name",
+ new_name,
+ Applicability::MachineApplicable,
+ );
+ }
+ err.emit();
+ }
+ CheckLintNameResult::NoLint(suggestion) => {
+ let lint = builtin::UNKNOWN_LINTS;
+ let (level, src) =
+ self.sets.get_lint_level(lint, self.cur, Some(&specs), self.sess);
+ let msg = format!("unknown lint: `{}`", name);
+ let mut db = struct_lint_level(
+ self.sess,
+ lint,
+ level,
+ src,
+ Some(li.span().into()),
+ &msg,
+ );
+
+ if let Some(suggestion) = suggestion {
+ db.span_suggestion(
+ li.span(),
+ "did you mean",
+ suggestion.to_string(),
+ Applicability::MachineApplicable,
+ );
+ }
+
+ db.emit();
+ }
+ }
+ }
+ }
+
+ for (id, &(level, ref src)) in specs.iter() {
+ if level == Level::Forbid {
+ continue;
+ }
+ let forbid_src = match self.sets.get_lint_id_level(*id, self.cur, None) {
+ (Some(Level::Forbid), src) => src,
+ _ => continue,
+ };
+ let forbidden_lint_name = match forbid_src {
+ LintSource::Default => id.to_string(),
+ LintSource::Node(name, _, _) => name.to_string(),
+ LintSource::CommandLine(name) => name.to_string(),
+ };
+ let (lint_attr_name, lint_attr_span) = match *src {
+ LintSource::Node(name, span, _) => (name, span),
+ _ => continue,
+ };
+ let mut diag_builder = struct_span_err!(
+ self.sess,
+ lint_attr_span,
+ E0453,
+ "{}({}) overruled by outer forbid({})",
+ level.as_str(),
+ lint_attr_name,
+ forbidden_lint_name
+ );
+ diag_builder.span_label(lint_attr_span, "overruled by previous forbid");
+ match forbid_src {
+ LintSource::Default => {}
+ LintSource::Node(_, forbid_source_span, reason) => {
+ diag_builder.span_label(forbid_source_span, "`forbid` level set here");
+ if let Some(rationale) = reason {
+ diag_builder.note(&rationale.as_str());
+ }
+ }
+ LintSource::CommandLine(_) => {
+ diag_builder.note("`forbid` lint level was set on command line");
+ }
+ }
+ diag_builder.emit();
+ // don't set a separate error for every lint in the group
+ break;
+ }
+
+ let prev = self.cur;
+ if specs.len() > 0 {
+ self.cur = self.sets.list.len() as u32;
+ self.sets.list.push(LintSet::Node { specs: specs, parent: prev });
+ }
+
+ BuilderPush { prev: prev, changed: prev != self.cur }
+ }
+
+ /// Called after `push` when the scope of a set of attributes are exited.
+ pub fn pop(&mut self, push: BuilderPush) {
+ self.cur = push.prev;
+ }
+
+ /// Used to emit a lint-related diagnostic based on the current state of
+ /// this lint context.
+ pub fn struct_lint(
+ &self,
+ lint: &'static Lint,
+ span: Option<MultiSpan>,
+ msg: &str,
+ ) -> DiagnosticBuilder<'a> {
+ let (level, src) = self.sets.get_lint_level(lint, self.cur, None, self.sess);
+ struct_lint_level(self.sess, lint, level, src, span, msg)
+ }
+
+ /// Registers the ID provided with the current set of lints stored in
+ /// this context.
+ pub fn register_id(&mut self, id: HirId) {
+ self.id_to_set.insert(id, self.cur);
+ }
+
+ pub fn build(self) -> LintLevelSets {
+ self.sets
+ }
+
+ pub fn build_map(self) -> LintLevelMap {
+ LintLevelMap { sets: self.sets, id_to_set: self.id_to_set }
+ }
+}
+
struct LintLevelMapBuilder<'a, 'tcx> {
levels: LintLevelsBuilder<'tcx>,
tcx: TyCtxt<'tcx>,
}
impl<'tcx> intravisit::Visitor<'tcx> for LintLevelMapBuilder<'_, 'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, Self::Map> {
intravisit::NestedVisitorMap::All(&self.tcx.hir())
}
-//! # Lints in the Rust compiler
+//! Lints, aka compiler warnings.
//!
-//! This currently only contains the definitions and implementations
-//! of most of the lints that `rustc` supports directly, it does not
-//! contain the infrastructure for defining/registering lints. That is
-//! available in `rustc::lint` and `rustc_driver::plugin` respectively.
+//! A 'lint' check is a kind of miscellaneous constraint that a user _might_
+//! want to enforce, but might reasonably want to permit as well, on a
+//! module-by-module basis. They contrast with static constraints enforced by
+//! other phases of the compiler, which are generally required to hold in order
+//! to compile the program at all.
+//!
+//! Most lints can be written as `LintPass` instances. These run after
+//! all other analyses. The `LintPass`es built into rustc are defined
+//! within `rustc_session::lint::builtin`,
+//! which has further comments on how to add such a lint.
+//! rustc can also load user-defined lint plugins via the plugin mechanism.
+//!
+//! Some of rustc's lints are defined elsewhere in the compiler and work by
+//! calling `add_lint()` on the overall `Session` object. This works when
+//! it happens before the main lint pass, which emits the lints stored by
+//! `add_lint()`. To emit lints after the main lint pass (from codegen, for
+//! example) requires more effort. See `emit_lint` and `GatherNodeLevels`
+//! in `context.rs`.
+//!
+//! Some code also exists in `rustc_session::lint`, `rustc::lint`.
//!
//! ## Note
//!
#![feature(bool_to_option)]
#![feature(box_patterns)]
#![feature(box_syntax)]
+#![feature(crate_visibility_modifier)]
+#![feature(never_type)]
#![feature(nll)]
#![recursion_limit = "256"]
mod array_into_iter;
pub mod builtin;
+mod context;
mod early;
+mod internal;
mod late;
mod levels;
mod non_ascii_idents;
mod nonstandard_style;
+mod passes;
mod redundant_semicolon;
mod types;
mod unused;
-use rustc::lint;
-use rustc::lint::builtin::{
- BARE_TRAIT_OBJECTS, ELIDED_LIFETIMES_IN_PATHS, EXPLICIT_OUTLIVES_REQUIREMENTS,
- INTRA_DOC_LINK_RESOLUTION_FAILURE, MISSING_DOC_CODE_EXAMPLES, PRIVATE_DOC_TESTS,
-};
-use rustc::lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintArray, LintPass};
use rustc::ty::query::Providers;
use rustc::ty::TyCtxt;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
-
+use rustc_session::lint::builtin::{
+ BARE_TRAIT_OBJECTS, ELIDED_LIFETIMES_IN_PATHS, EXPLICIT_OUTLIVES_REQUIREMENTS,
+ INTRA_DOC_LINK_RESOLUTION_FAILURE, MISSING_DOC_CODE_EXAMPLES, PRIVATE_DOC_TESTS,
+};
use rustc_span::Span;
use syntax::ast;
-use lint::LintId;
-
use array_into_iter::ArrayIntoIter;
use builtin::*;
+use internal::*;
use non_ascii_idents::*;
use nonstandard_style::*;
use redundant_semicolon::*;
-use rustc::lint::internal::*;
use types::*;
use unused::*;
-/// Useful for other parts of the compiler.
+/// Useful for other parts of the compiler / Clippy.
pub use builtin::SoftLints;
+pub use context::{CheckLintNameResult, EarlyContext, LateContext, LintContext, LintStore};
pub use early::check_ast_crate;
pub use late::check_crate;
+pub use passes::{EarlyLintPass, LateLintPass};
+pub use rustc_session::lint::Level::{self, *};
+pub use rustc_session::lint::{BufferedEarlyLint, FutureIncompatibleInfo, Lint, LintId};
+pub use rustc_session::lint::{LintArray, LintPass};
pub fn provide(providers: &mut Providers<'_>) {
levels::provide(providers);
late_lint_mod_passes!(declare_combined_late_pass, [BuiltinCombinedModuleLateLintPass]);
-pub fn new_lint_store(no_interleave_lints: bool, internal_lints: bool) -> lint::LintStore {
- let mut lint_store = lint::LintStore::new();
+pub fn new_lint_store(no_interleave_lints: bool, internal_lints: bool) -> LintStore {
+ let mut lint_store = LintStore::new();
register_builtins(&mut lint_store, no_interleave_lints);
if internal_lints {
/// Tell the `LintStore` about all the built-in lints (the ones
/// defined in this crate and the ones defined in
/// `rustc::lint::builtin`).
-fn register_builtins(store: &mut lint::LintStore, no_interleave_lints: bool) {
+fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) {
macro_rules! add_lint_group {
($name:expr, $($lint:ident),*) => (
store.register_group(false, $name, None, vec![$(LintId::of($lint)),*]);
store.register_removed("plugin_as_library", "plugins have been deprecated and retired");
}
-fn register_internals(store: &mut lint::LintStore) {
+fn register_internals(store: &mut LintStore) {
store.register_lints(&DefaultHashTypes::get_lints());
store.register_early_pass(|| box DefaultHashTypes::new());
store.register_lints(&LintPassImpl::get_lints());
-use crate::lint::{EarlyContext, EarlyLintPass, LintArray, LintContext, LintPass};
+use crate::{EarlyContext, EarlyLintPass, LintContext};
use syntax::ast;
declare_lint! {
-use lint::{EarlyContext, LateContext, LintArray, LintContext};
-use lint::{EarlyLintPass, LateLintPass, LintPass};
-use rustc::hir::intravisit::FnKind;
-use rustc::lint;
+use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
use rustc::ty;
+use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
+use rustc_hir::intravisit::FnKind;
use rustc_hir::{GenericParamKind, PatKind};
use rustc_span::symbol::sym;
use rustc_span::{symbol::Ident, BytePos, Span};
use rustc_target::spec::abi::Abi;
use syntax::ast;
use syntax::attr;
-use syntax::errors::Applicability;
#[derive(PartialEq)]
pub enum MethodLateContext {
--- /dev/null
+use crate::context::{EarlyContext, LateContext};
+
+use rustc_data_structures::sync;
+use rustc_hir as hir;
+use rustc_session::lint::builtin::HardwiredLints;
+use rustc_session::lint::LintPass;
+use rustc_span::Span;
+use syntax::ast;
+
+#[macro_export]
+macro_rules! late_lint_methods {
+ ($macro:path, $args:tt, [$hir:tt]) => (
+ $macro!($args, [$hir], [
+ fn check_param(a: &$hir hir::Param<$hir>);
+ fn check_body(a: &$hir hir::Body<$hir>);
+ fn check_body_post(a: &$hir hir::Body<$hir>);
+ fn check_name(a: Span, b: ast::Name);
+ fn check_crate(a: &$hir hir::Crate<$hir>);
+ fn check_crate_post(a: &$hir hir::Crate<$hir>);
+ fn check_mod(a: &$hir hir::Mod<$hir>, b: Span, c: hir::HirId);
+ fn check_mod_post(a: &$hir hir::Mod<$hir>, b: Span, c: hir::HirId);
+ fn check_foreign_item(a: &$hir hir::ForeignItem<$hir>);
+ fn check_foreign_item_post(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_where_predicate(a: &$hir hir::WherePredicate<$hir>);
+ fn check_poly_trait_ref(a: &$hir hir::PolyTraitRef<$hir>, b: hir::TraitBoundModifier);
+ fn check_fn(
+ a: rustc_hir::intravisit::FnKind<$hir>,
+ b: &$hir hir::FnDecl<$hir>,
+ c: &$hir hir::Body<$hir>,
+ d: Span,
+ e: hir::HirId);
+ fn check_fn_post(
+ a: rustc_hir::intravisit::FnKind<$hir>,
+ b: &$hir hir::FnDecl<$hir>,
+ c: &$hir hir::Body<$hir>,
+ d: Span,
+ e: hir::HirId
+ );
+ fn check_trait_item(a: &$hir hir::TraitItem<$hir>);
+ fn check_trait_item_post(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_struct_def_post(a: &$hir hir::VariantData<$hir>);
+ fn check_struct_field(a: &$hir hir::StructField<$hir>);
+ fn check_variant(a: &$hir hir::Variant<$hir>);
+ fn check_variant_post(a: &$hir hir::Variant<$hir>);
+ fn check_lifetime(a: &$hir hir::Lifetime);
+ fn check_path(a: &$hir hir::Path<$hir>, b: hir::HirId);
+ fn check_attribute(a: &$hir 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]);
+
+ /// Counterpart to `enter_lint_attrs`.
+ fn exit_lint_attrs(a: &$hir [ast::Attribute]);
+ ]);
+ )
+}
+
+/// Trait for types providing lint checks.
+///
+/// Each `check` method checks a single syntax node, and should not
+/// invoke methods recursively (unlike `Visitor`). By default they
+/// do nothing.
+//
+// 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<'a, $hir>: LintPass {
+ expand_lint_pass_methods!(&LateContext<'a, $hir>, [$($methods)*]);
+ }
+ )
+}
+
+late_lint_methods!(declare_late_lint_pass, [], ['tcx]);
+
+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;)*
+ })
+}
+
+#[macro_export]
+macro_rules! expand_combined_late_lint_pass_methods {
+ ($passes:tt, [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => (
+ $(fn $name(&mut self, context: &LateContext<'a, 'tcx>, $($param: $arg),*) {
+ expand_combined_late_lint_pass_method!($passes, self, $name, (context, $($param),*));
+ })*
+ )
+}
+
+#[macro_export]
+macro_rules! declare_combined_late_lint_pass {
+ ([$v:vis $name:ident, [$($passes:ident: $constructor:expr,)*]], [$hir:tt], $methods:tt) => (
+ #[allow(non_snake_case)]
+ $v struct $name {
+ $($passes: $passes,)*
+ }
+
+ impl $name {
+ $v fn new() -> Self {
+ Self {
+ $($passes: $constructor,)*
+ }
+ }
+
+ $v fn get_lints() -> LintArray {
+ let mut lints = Vec::new();
+ $(lints.extend_from_slice(&$passes::get_lints());)*
+ lints
+ }
+ }
+
+ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for $name {
+ expand_combined_late_lint_pass_methods!([$($passes),*], $methods);
+ }
+
+ #[allow(rustc::lint_pass_impl_without_macro)]
+ impl LintPass for $name {
+ fn name(&self) -> &'static str {
+ panic!()
+ }
+ }
+ )
+}
+
+#[macro_export]
+macro_rules! early_lint_methods {
+ ($macro:path, $args:tt) => (
+ $macro!($args, [
+ fn check_param(a: &ast::Param);
+ fn check_ident(a: ast::Ident);
+ fn check_crate(a: &ast::Crate);
+ fn check_crate_post(a: &ast::Crate);
+ fn check_mod(a: &ast::Mod, b: Span, c: ast::NodeId);
+ fn check_mod_post(a: &ast::Mod, b: Span, c: ast::NodeId);
+ fn check_foreign_item(a: &ast::ForeignItem);
+ fn check_foreign_item_post(a: &ast::ForeignItem);
+ fn check_item(a: &ast::Item);
+ fn check_item_post(a: &ast::Item);
+ fn check_local(a: &ast::Local);
+ fn check_block(a: &ast::Block);
+ fn check_block_post(a: &ast::Block);
+ fn check_stmt(a: &ast::Stmt);
+ fn check_arm(a: &ast::Arm);
+ fn check_pat(a: &ast::Pat);
+ fn check_pat_post(a: &ast::Pat);
+ fn check_expr(a: &ast::Expr);
+ fn check_expr_post(a: &ast::Expr);
+ fn check_ty(a: &ast::Ty);
+ fn check_generic_param(a: &ast::GenericParam);
+ fn check_generics(a: &ast::Generics);
+ fn check_where_predicate(a: &ast::WherePredicate);
+ fn check_poly_trait_ref(a: &ast::PolyTraitRef,
+ b: &ast::TraitBoundModifier);
+ fn check_fn(a: syntax::visit::FnKind<'_>, b: &ast::FnDecl, c: Span, d_: ast::NodeId);
+ fn check_fn_post(
+ a: syntax::visit::FnKind<'_>,
+ b: &ast::FnDecl,
+ c: Span,
+ d: ast::NodeId
+ );
+ fn check_trait_item(a: &ast::AssocItem);
+ fn check_trait_item_post(a: &ast::AssocItem);
+ fn check_impl_item(a: &ast::AssocItem);
+ fn check_impl_item_post(a: &ast::AssocItem);
+ fn check_struct_def(a: &ast::VariantData);
+ fn check_struct_def_post(a: &ast::VariantData);
+ fn check_struct_field(a: &ast::StructField);
+ fn check_variant(a: &ast::Variant);
+ fn check_variant_post(a: &ast::Variant);
+ fn check_lifetime(a: &ast::Lifetime);
+ fn check_path(a: &ast::Path, b: ast::NodeId);
+ fn check_attribute(a: &ast::Attribute);
+ fn check_mac_def(a: &ast::MacroDef, b: ast::NodeId);
+ fn check_mac(a: &ast::Mac);
+
+ /// 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: &[ast::Attribute]);
+
+ /// Counterpart to `enter_lint_attrs`.
+ fn exit_lint_attrs(a: &[ast::Attribute]);
+ ]);
+ )
+}
+
+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)*]) => (
+ pub trait EarlyLintPass: LintPass {
+ expand_early_lint_pass_methods!(&EarlyContext<'_>, [$($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;)*
+ })
+}
+
+#[macro_export]
+macro_rules! expand_combined_early_lint_pass_methods {
+ ($passes:tt, [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => (
+ $(fn $name(&mut self, context: &EarlyContext<'_>, $($param: $arg),*) {
+ expand_combined_early_lint_pass_method!($passes, self, $name, (context, $($param),*));
+ })*
+ )
+}
+
+#[macro_export]
+macro_rules! declare_combined_early_lint_pass {
+ ([$v:vis $name:ident, [$($passes:ident: $constructor:expr,)*]], $methods:tt) => (
+ #[allow(non_snake_case)]
+ $v struct $name {
+ $($passes: $passes,)*
+ }
+
+ impl $name {
+ $v fn new() -> Self {
+ Self {
+ $($passes: $constructor,)*
+ }
+ }
+
+ $v fn get_lints() -> LintArray {
+ let mut lints = Vec::new();
+ $(lints.extend_from_slice(&$passes::get_lints());)*
+ lints
+ }
+ }
+
+ impl EarlyLintPass for $name {
+ expand_combined_early_lint_pass_methods!([$($passes),*], $methods);
+ }
+
+ #[allow(rustc::lint_pass_impl_without_macro)]
+ impl LintPass for $name {
+ fn name(&self) -> &'static str {
+ panic!()
+ }
+ }
+ )
+}
+
+/// A lint pass boxed up as a trait object.
+pub type EarlyLintPassObject = Box<dyn EarlyLintPass + sync::Send + sync::Sync + 'static>;
+pub type LateLintPassObject =
+ Box<dyn for<'a, 'tcx> LateLintPass<'a, 'tcx> + sync::Send + sync::Sync + 'static>;
-use crate::lint::{EarlyContext, EarlyLintPass, LintArray, LintContext, LintPass};
+use crate::{EarlyContext, EarlyLintPass, LintContext};
+use rustc_errors::Applicability;
use syntax::ast::{ExprKind, Stmt, StmtKind};
-use syntax::errors::Applicability;
declare_lint! {
pub REDUNDANT_SEMICOLON,
#![allow(non_snake_case)]
-use crate::hir::def_id::DefId;
-use lint::{LateContext, LintArray, LintContext};
-use lint::{LateLintPass, LintPass};
-use rustc::lint;
+use crate::{LateContext, LateLintPass, LintContext};
use rustc::mir::interpret::{sign_extend, truncate};
use rustc::ty::layout::{self, IntegerExt, LayoutOf, SizeSkeleton, VariantIdx};
use rustc::ty::subst::SubstsRef;
use rustc::ty::{self, AdtKind, ParamEnv, Ty, TyCtxt};
use rustc_data_structures::fx::FxHashSet;
+use rustc_errors::Applicability;
use rustc_hir as hir;
+use rustc_hir::def_id::DefId;
use rustc_hir::{is_range_literal, ExprKind, Node};
use rustc_index::vec::Idx;
use rustc_span::source_map;
use rustc_span::symbol::sym;
use rustc_span::Span;
use rustc_target::spec::abi::Abi;
-use syntax::errors::Applicability;
use syntax::{ast, attr};
use log::debug;
-use lint::{EarlyContext, LateContext, LintArray, LintContext};
-use lint::{EarlyLintPass, LateLintPass, LintPass};
-use rustc::lint;
-use rustc::lint::builtin::UNUSED_ATTRIBUTES;
+use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
use rustc::ty::adjustment;
use rustc::ty::{self, Ty};
use rustc_data_structures::fx::FxHashMap;
+use rustc_errors::{pluralize, Applicability};
use rustc_feature::{AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
-
+use rustc_session::lint::builtin::UNUSED_ATTRIBUTES;
use rustc_span::symbol::Symbol;
use rustc_span::symbol::{kw, sym};
use rustc_span::{BytePos, Span};
use syntax::ast;
use syntax::attr;
-use syntax::errors::{pluralize, Applicability};
use syntax::print::pprust;
use syntax::util::parser;
+++ /dev/null
-[package]
-authors = ["The Rust Project Developers"]
-build = "build.rs"
-name = "rustc_lsan"
-version = "0.0.0"
-edition = "2018"
-
-[lib]
-name = "rustc_lsan"
-path = "lib.rs"
-test = false
-
-[build-dependencies]
-build_helper = { path = "../build_helper" }
-cmake = "0.1.38"
-
-[dependencies]
-alloc = { path = "../liballoc" }
-core = { path = "../libcore" }
-compiler_builtins = "0.1.0"
+++ /dev/null
-use build_helper::sanitizer_lib_boilerplate;
-use std::env;
-
-use cmake::Config;
-
-fn main() {
- println!("cargo:rerun-if-env-changed=RUSTC_BUILD_SANITIZERS");
- if env::var("RUSTC_BUILD_SANITIZERS") != Ok("1".to_string()) {
- return;
- }
- if let Some(llvm_config) = env::var_os("LLVM_CONFIG") {
- build_helper::restore_library_path();
-
- let (native, target) = match sanitizer_lib_boilerplate("lsan") {
- Ok(native) => native,
- _ => return,
- };
-
- Config::new(&native.src_dir)
- .define("COMPILER_RT_BUILD_SANITIZERS", "ON")
- .define("COMPILER_RT_BUILD_BUILTINS", "OFF")
- .define("COMPILER_RT_BUILD_XRAY", "OFF")
- .define("LLVM_CONFIG_PATH", llvm_config)
- .out_dir(&native.out_dir)
- .build_target(&target)
- .build();
- }
- println!("cargo:rerun-if-env-changed=LLVM_CONFIG");
-}
+++ /dev/null
-#![sanitizer_runtime]
-#![feature(nll)]
-#![feature(sanitizer_runtime)]
-#![feature(staged_api)]
-#![no_std]
-#![unstable(
- feature = "sanitizer_runtime_lib",
- reason = "internal implementation detail of sanitizers",
- issue = "none"
-)]
smallvec = { version = "1.0", features = ["union", "may_dangle"] }
rustc = { path = "../librustc" }
rustc_data_structures = { path = "../librustc_data_structures" }
+rustc_errors = { path = "../librustc_errors" }
rustc_hir = { path = "../librustc_hir" }
-errors = { path = "../librustc_errors", package = "rustc_errors" }
rustc_target = { path = "../librustc_target" }
rustc_index = { path = "../librustc_index" }
rustc_serialize = { path = "../libserialize", package = "serialize" }
use rustc::hir::map::Definitions;
use rustc::middle::cstore::DepKind;
use rustc::middle::cstore::{CrateSource, ExternCrate, ExternCrateSource, MetadataLoaderDyn};
-use rustc::session::config::{self, Sanitizer};
+use rustc::session::config;
use rustc::session::search_paths::PathKind;
use rustc::session::{CrateDisambiguator, Session};
use rustc::ty::TyCtxt;
use rustc_data_structures::svh::Svh;
use rustc_data_structures::sync::Lrc;
-use rustc_hir::def_id::CrateNum;
-use rustc_hir::def_id::LOCAL_CRATE;
-use rustc_index::vec::IndexVec;
-use rustc_target::spec::{PanicStrategy, TargetTriple};
-
-use std::path::Path;
-use std::{cmp, fs};
-
-use log::{debug, info, log_enabled};
-use proc_macro::bridge::client::ProcMacro;
+use rustc_error_codes::*;
+use rustc_errors::struct_span_err;
use rustc_expand::base::SyntaxExtension;
+use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
+use rustc_index::vec::IndexVec;
use rustc_span::edition::Edition;
use rustc_span::symbol::{sym, Symbol};
use rustc_span::{Span, DUMMY_SP};
+use rustc_target::spec::{PanicStrategy, TargetTriple};
use syntax::ast;
use syntax::attr;
use syntax::expand::allocator::{global_allocator_spans, AllocatorKind};
-use syntax::span_fatal;
-use rustc_error_codes::*;
+use log::{debug, info, log_enabled};
+use proc_macro::bridge::client::ProcMacro;
+use std::path::Path;
+use std::{cmp, fs};
#[derive(Clone)]
pub struct CStore {
if self.local_crate_name == root.name()
&& self.sess.local_crate_disambiguator() == root.disambiguator()
{
- span_fatal!(
+ struct_span_err!(
self.sess,
span,
E0519,
will result in symbol conflicts between the two.",
root.name()
)
+ .emit()
}
// Check for conflicts with any crate loaded so far
other.hash() != root.hash()
{
// but different SVH
- span_fatal!(
+ struct_span_err!(
self.sess,
span,
E0523,
will result in symbol conflicts between the two.",
root.name()
)
+ .emit();
}
});
}
self.inject_dependency_if(cnum, "a panic runtime", &|data| data.needs_panic_runtime());
}
- fn inject_sanitizer_runtime(&mut self) {
- if let Some(ref sanitizer) = self.sess.opts.debugging_opts.sanitizer {
- // Sanitizers can only be used on some tested platforms with
- // executables linked to `std`
- const ASAN_SUPPORTED_TARGETS: &[&str] =
- &["x86_64-unknown-linux-gnu", "x86_64-apple-darwin"];
- const TSAN_SUPPORTED_TARGETS: &[&str] =
- &["x86_64-unknown-linux-gnu", "x86_64-apple-darwin"];
- const LSAN_SUPPORTED_TARGETS: &[&str] = &["x86_64-unknown-linux-gnu"];
- const MSAN_SUPPORTED_TARGETS: &[&str] = &["x86_64-unknown-linux-gnu"];
-
- let supported_targets = match *sanitizer {
- Sanitizer::Address => ASAN_SUPPORTED_TARGETS,
- Sanitizer::Thread => TSAN_SUPPORTED_TARGETS,
- Sanitizer::Leak => LSAN_SUPPORTED_TARGETS,
- Sanitizer::Memory => MSAN_SUPPORTED_TARGETS,
- };
- if !supported_targets.contains(&&*self.sess.opts.target_triple.triple()) {
- self.sess.err(&format!(
- "{:?}Sanitizer only works with the `{}` target",
- sanitizer,
- supported_targets.join("` or `")
- ));
- return;
- }
-
- // firstyear 2017 - during testing I was unable to access an OSX machine
- // to make this work on different crate types. As a result, today I have
- // only been able to test and support linux as a target.
- if self.sess.opts.target_triple.triple() == "x86_64-unknown-linux-gnu" {
- if !self.sess.crate_types.borrow().iter().all(|ct| {
- match *ct {
- // Link the runtime
- config::CrateType::Executable => true,
- // This crate will be compiled with the required
- // instrumentation pass
- config::CrateType::Staticlib
- | config::CrateType::Rlib
- | config::CrateType::Dylib
- | config::CrateType::Cdylib => false,
- _ => {
- self.sess.err(&format!(
- "Only executables, staticlibs, \
- cdylibs, dylibs and rlibs can be compiled with \
- `-Z sanitizer`"
- ));
- false
- }
- }
- }) {
- return;
- }
- } else {
- if !self.sess.crate_types.borrow().iter().all(|ct| {
- match *ct {
- // Link the runtime
- config::CrateType::Executable => true,
- // This crate will be compiled with the required
- // instrumentation pass
- config::CrateType::Rlib => false,
- _ => {
- self.sess.err(&format!(
- "Only executables and rlibs can be \
- compiled with `-Z sanitizer`"
- ));
- false
- }
- }
- }) {
- return;
- }
- }
-
- let mut uses_std = false;
- self.cstore.iter_crate_data(|_, data| {
- if data.name() == sym::std {
- uses_std = true;
- }
- });
-
- if uses_std {
- let name = Symbol::intern(match sanitizer {
- Sanitizer::Address => "rustc_asan",
- Sanitizer::Leak => "rustc_lsan",
- Sanitizer::Memory => "rustc_msan",
- Sanitizer::Thread => "rustc_tsan",
- });
- info!("loading sanitizer: {}", name);
-
- let cnum = self.resolve_crate(name, DUMMY_SP, DepKind::Explicit, None);
- let data = self.cstore.get_crate_data(cnum);
-
- // Sanity check the loaded crate to ensure it is indeed a sanitizer runtime
- if !data.is_sanitizer_runtime() {
- self.sess.err(&format!("the crate `{}` is not a sanitizer runtime", name));
- }
- } else {
- self.sess.err("Must link std to be compiled with `-Z sanitizer`");
- }
- }
- }
-
fn inject_profiler_runtime(&mut self) {
if self.sess.opts.debugging_opts.profile || self.sess.opts.cg.profile_generate.enabled() {
info!("loading profiler");
}
pub fn postprocess(&mut self, krate: &ast::Crate) {
- self.inject_sanitizer_runtime();
self.inject_profiler_runtime();
self.inject_allocator_crate(krate);
self.inject_panic_runtime(krate);
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::svh::Svh;
use rustc_data_structures::sync::MetadataRef;
-
-use errors::DiagnosticBuilder;
+use rustc_errors::{struct_span_err, DiagnosticBuilder};
use rustc_span::symbol::{sym, Symbol};
use rustc_span::Span;
use rustc_target::spec::{Target, TargetTriple};
-use syntax::struct_span_err;
-use syntax::{span_err, span_fatal};
use std::cmp;
use std::fmt;
};
if target_only {
- // Need to abort before syntax expansion.
let message = format!(
- "plugin `{}` is not available for triple `{}` \
- (only found {})",
+ "plugin `{}` is not available for triple `{}` (only found {})",
name,
config::host_triple(),
sess.opts.target_triple
);
- span_fatal!(sess, span, E0456, "{}", &message);
+ struct_span_err!(sess, span, E0456, "{}", &message).emit();
+ return None;
}
match library.source.dylib {
Some(dylib) => Some((dylib.0, library.metadata.get_root().disambiguator())),
None => {
- span_err!(
+ struct_span_err!(
sess,
span,
E0457,
"plugin `{}` only found in rlib format, but must be available \
in dylib format",
name
- );
+ )
+ .emit();
// No need to abort because the loading code will just ignore this
// empty dylib.
None
use rustc::middle::cstore::{self, NativeLibrary};
+use rustc::session::parse::feature_err;
use rustc::session::Session;
use rustc::ty::TyCtxt;
use rustc_data_structures::fx::FxHashSet;
+use rustc_error_codes::*;
+use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_span::source_map::Span;
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_target::spec::abi::Abi;
use syntax::attr;
-use syntax::feature_gate::feature_err;
-use syntax::{span_err, struct_span_err};
-
-use rustc_error_codes::*;
crate fn collect(tcx: TyCtxt<'_>) -> Vec<NativeLibrary> {
let mut collector = Collector { tcx, libs: Vec::new() };
if lib.kind == cstore::NativeFramework && !is_osx {
let msg = "native frameworks are only available on macOS targets";
match span {
- Some(span) => span_err!(self.tcx.sess, span, E0455, "{}", msg),
+ Some(span) => struct_span_err!(self.tcx.sess, span, E0455, "{}", msg).emit(),
None => self.tcx.sess.err(msg),
}
}
use rustc::session::Session;
use rustc::ty::codec::TyDecoder;
use rustc::ty::{self, Ty, TyCtxt};
-use rustc::util::captures::Captures;
use rustc::util::common::record_time;
+use rustc_data_structures::captures::Captures;
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::svh::Svh;
self.root.panic_runtime
}
- crate fn is_sanitizer_runtime(&self) -> bool {
- self.root.sanitizer_runtime
- }
-
crate fn is_profiler_runtime(&self) -> bool {
self.root.profiler_runtime
}
is_compiler_builtins => { cdata.root.compiler_builtins }
has_global_allocator => { cdata.root.has_global_allocator }
has_panic_handler => { cdata.root.has_panic_handler }
- is_sanitizer_runtime => { cdata.root.sanitizer_runtime }
is_profiler_runtime => { cdata.root.profiler_runtime }
panic_strategy => { cdata.root.panic_strategy }
extern_crate => {
pub fn crate_source_untracked(&self, cnum: CrateNum) -> CrateSource {
self.get_crate_data(cnum).source.clone()
}
+
+ pub fn get_span_untracked(&self, def_id: DefId, sess: &Session) -> Span {
+ self.get_crate_data(def_id.krate).get_span(def_id.index, sess)
+ }
+
+ pub fn item_generics_num_lifetimes(&self, def_id: DefId, sess: &Session) -> usize {
+ self.get_crate_data(def_id.krate).get_generics(def_id.index, sess).own_counts().lifetimes
+ }
}
impl CrateStore for CStore {
self
}
- fn item_generics_cloned_untracked(&self, def: DefId, sess: &Session) -> ty::Generics {
- self.get_crate_data(def.krate).get_generics(def.index, sess)
- }
-
fn crate_name_untracked(&self, cnum: CrateNum) -> Symbol {
self.get_crate_data(cnum).root.name
}
use crate::rmeta::*;
use rustc::hir::map::definitions::DefPathTable;
+use rustc::hir::map::Map;
use rustc::middle::cstore::{EncodedMetadata, ForeignModule, LinkagePreference, NativeLibrary};
use rustc::middle::dependency_format::Linkage;
use rustc::middle::exported_symbols::{metadata_symbol_name, ExportedSymbol, SymbolExportLevel};
use syntax::attr;
use syntax::expand::is_proc_macro_attr;
-use rustc::hir::intravisit;
-use rustc::hir::intravisit::{NestedVisitorMap, Visitor};
use rustc_hir as hir;
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_hir::itemlikevisit::ItemLikeVisitor;
struct EncodeContext<'tcx> {
no_builtins: attr::contains_name(&attrs, sym::no_builtins),
panic_runtime: attr::contains_name(&attrs, sym::panic_runtime),
profiler_runtime: attr::contains_name(&attrs, sym::profiler_runtime),
- sanitizer_runtime: attr::contains_name(&attrs, sym::sanitizer_runtime),
symbol_mangling_version: tcx.sess.opts.debugging_opts.symbol_mangling_version,
crate_deps,
// FIXME(eddyb) make metadata encoding walk over all definitions, instead of HIR.
impl Visitor<'tcx> for EncodeContext<'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> {
NestedVisitorMap::OnlyBodies(&self.tcx.hir())
}
fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) {
no_builtins: bool,
panic_runtime: bool,
profiler_runtime: bool,
- sanitizer_runtime: bool,
symbol_mangling_version: SymbolManglingVersion,
}
doctest = false
[dependencies]
-arena = { path = "../libarena" }
either = "1.5.0"
dot = { path = "../libgraphviz", package = "graphviz" }
itertools = "0.8"
self.insert_as_pending_if_two_phase(location, &assigned_place, kind, idx);
- if let mir::PlaceBase::Local(local) = borrowed_place.base {
- self.local_map.entry(local).or_default().insert(idx);
- }
+ self.local_map.entry(borrowed_place.local).or_default().insert(idx);
}
self.super_assign(assigned_place, rvalue, location)
use rustc::mir::visit::TyContext;
use rustc::mir::visit::Visitor;
use rustc::mir::{
- BasicBlock, BasicBlockData, Body, Local, Location, Place, PlaceBase, PlaceRef, ProjectionElem,
- Rvalue, SourceInfo, Statement, StatementKind, Terminator, TerminatorKind, UserTypeProjection,
+ BasicBlock, BasicBlockData, Body, Local, Location, Place, PlaceRef, ProjectionElem, Rvalue,
+ SourceInfo, Statement, StatementKind, Terminator, TerminatorKind, UserTypeProjection,
};
use rustc::ty::fold::TypeFoldable;
use rustc::ty::subst::SubstsRef;
pub(super) fn generate_constraints<'cx, 'tcx>(
infcx: &InferCtxt<'cx, 'tcx>,
- param_env: ty::ParamEnv<'tcx>,
liveness_constraints: &mut LivenessValues<RegionVid>,
all_facts: &mut Option<AllFacts>,
location_table: &LocationTable,
location_table,
all_facts,
body,
- param_env,
};
for (bb, data) in body.basic_blocks().iter_enumerated() {
/// 'cg = the duration of the constraint generation process itself.
struct ConstraintGeneration<'cg, 'cx, 'tcx> {
infcx: &'cg InferCtxt<'cx, 'tcx>,
- param_env: ty::ParamEnv<'tcx>,
all_facts: &'cg mut Option<AllFacts>,
location_table: &'cg LocationTable,
liveness_constraints: &'cg mut LivenessValues<RegionVid>,
// of the borrows are killed: the ones whose `borrowed_place`
// conflicts with the `place`.
match place.as_ref() {
- PlaceRef { base: &PlaceBase::Local(local), projection: &[] }
- | PlaceRef {
- base: &PlaceBase::Local(local),
- projection: &[ProjectionElem::Deref],
- } => {
+ PlaceRef { local, projection: &[] }
+ | PlaceRef { local, projection: &[ProjectionElem::Deref] } => {
debug!(
"Recording `killed` facts for borrows of local={:?} at location={:?}",
local, location
all_facts,
self.borrow_set,
self.location_table,
- &local,
+ local,
location,
);
}
- PlaceRef { base: &PlaceBase::Static(_), .. } => {
- // Ignore kills of static or static mut variables.
- }
-
- PlaceRef { base: &PlaceBase::Local(local), projection: &[.., _] } => {
+ PlaceRef { local, projection: &[.., _] } => {
// Kill conflicting borrows of the innermost local.
debug!(
"Recording `killed` facts for borrows of \
local, location
);
- if let Some(borrow_indices) = self.borrow_set.local_map.get(&local) {
+ if let Some(borrow_indices) = self.borrow_set.local_map.get(local) {
for &borrow_index in borrow_indices {
let places_conflict = places_conflict::places_conflict(
self.infcx.tcx,
- self.param_env,
self.body,
&self.borrow_set.borrows[borrow_index].borrowed_place,
place,
use rustc::mir::{
self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, ConstraintCategory,
- FakeReadCause, Local, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceBase,
- PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, TerminatorKind, VarBindingForm,
+ FakeReadCause, Local, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef,
+ ProjectionElem, Rvalue, Statement, StatementKind, TerminatorKind, VarBindingForm,
};
use rustc::traits::error_reporting::suggest_constraining_type_param;
use rustc::ty::{self, Ty};
}
let ty =
- Place::ty_from(used_place.base, used_place.projection, *self.body, self.infcx.tcx)
+ Place::ty_from(used_place.local, used_place.projection, *self.body, self.infcx.tcx)
.ty;
let needs_note = match ty.kind {
ty::Closure(id, _) => {
// field access to a union. If we find that, then we will keep the place of the
// union being accessed and the field that was being accessed so we can check the
// second borrowed place for the same union and a access to a different field.
- let Place { base, projection } = first_borrowed_place;
+ let Place { local, projection } = first_borrowed_place;
let mut cursor = projection.as_ref();
while let [proj_base @ .., elem] = cursor {
cursor = proj_base;
match elem {
- ProjectionElem::Field(field, _) if union_ty(base, proj_base).is_some() => {
- return Some((PlaceRef { base: base, projection: proj_base }, field));
+ ProjectionElem::Field(field, _) if union_ty(local, proj_base).is_some() => {
+ return Some((PlaceRef { local, projection: proj_base }, field));
}
_ => {}
}
.and_then(|(target_base, target_field)| {
// With the place of a union and a field access into it, we traverse the second
// borrowed place and look for a access to a different field of the same union.
- let Place { base, projection } = second_borrowed_place;
+ let Place { local, projection } = second_borrowed_place;
let mut cursor = projection.as_ref();
while let [proj_base @ .., elem] = cursor {
cursor = proj_base;
if let ProjectionElem::Field(field, _) = elem {
- if let Some(union_ty) = union_ty(base, proj_base) {
+ if let Some(union_ty) = union_ty(local, proj_base) {
if field != target_field
- && base == target_base.base
+ && local == target_base.local
&& proj_base == target_base.projection
{
// FIXME when we avoid clone reuse describe_place closure
let describe_base_place = self
- .describe_place(PlaceRef { base: base, projection: proj_base })
+ .describe_place(PlaceRef { local, projection: proj_base })
.unwrap_or_else(|| "_".to_owned());
return Some((
let borrow_span = borrow_spans.var_or_use();
assert!(root_place.projection.is_empty());
- let proper_span = match root_place.base {
- PlaceBase::Local(local) => self.body.local_decls[*local].source_info.span,
- _ => drop_span,
- };
+ let proper_span = self.body.local_decls[*root_place.local].source_info.span;
let root_place_projection = self.infcx.tcx.intern_place_elems(root_place.projection);
if self.access_place_error_reported.contains(&(
- Place { base: root_place.base.clone(), projection: root_place_projection },
+ Place { local: root_place.local.clone(), projection: root_place_projection },
borrow_span,
)) {
debug!(
}
self.access_place_error_reported.insert((
- Place { base: root_place.base.clone(), projection: root_place_projection },
+ Place { local: root_place.local.clone(), projection: root_place_projection },
borrow_span,
));
- if let PlaceBase::Local(local) = borrow.borrowed_place.base {
- if self.body.local_decls[local].is_ref_to_thread_local() {
- let err = self
- .report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span);
- err.buffer(&mut self.errors_buffer);
- return;
- }
- };
+ let borrowed_local = borrow.borrowed_place.local;
+ if self.body.local_decls[borrowed_local].is_ref_to_thread_local() {
+ let err =
+ self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span);
+ err.buffer(&mut self.errors_buffer);
+ return;
+ }
if let StorageDeadOrDrop::Destructor(dropped_ty) =
self.classify_drop_access_kind(borrow.borrowed_place.as_ref())
} else {
let root_place =
self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
- let local =
- if let PlaceRef { base: PlaceBase::Local(local), projection: [] } = root_place {
- local
- } else {
- bug!("try_report_cannot_return_reference_to_local: not a local")
- };
+ let local = root_place.local;
match self.body.local_kind(*local) {
LocalKind::ReturnPointer | LocalKind::Temp => {
("temporary value".to_string(), "temporary value created here".to_string())
[proj_base @ .., elem] => {
// FIXME(spastorino) make this iterate
let base_access = self.classify_drop_access_kind(PlaceRef {
- base: place.base,
+ local: place.local,
projection: proj_base,
});
match elem {
StorageDeadOrDrop::LocalStorageDead
| StorageDeadOrDrop::BoxedStorageDead => {
assert!(
- Place::ty_from(&place.base, proj_base, *self.body, tcx).ty.is_box(),
+ Place::ty_from(&place.local, proj_base, *self.body, tcx)
+ .ty
+ .is_box(),
"Drop of value behind a reference or raw pointer"
);
StorageDeadOrDrop::BoxedStorageDead
StorageDeadOrDrop::Destructor(_) => base_access,
},
ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => {
- let base_ty = Place::ty_from(&place.base, proj_base, *self.body, tcx).ty;
+ let base_ty = Place::ty_from(&place.local, proj_base, *self.body, tcx).ty;
match base_ty.kind {
ty::Adt(def, _) if def.has_dtor(tcx) => {
// Report the outermost adt with a destructor
use rustc::mir::{
AggregateKind, Constant, Field, Local, LocalInfo, LocalKind, Location, Operand, Place,
- PlaceBase, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Static, StaticKind,
- Terminator, TerminatorKind,
+ PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind,
};
use rustc::ty::layout::VariantIdx;
use rustc::ty::print::Print;
including_downcast: &IncludingDowncast,
) -> Result<(), ()> {
match place {
- PlaceRef { base: PlaceBase::Local(local), projection: [] } => {
+ PlaceRef { local, projection: [] } => {
self.append_local_to_string(*local, buf)?;
}
- PlaceRef {
- base: PlaceBase::Static(box Static { kind: StaticKind::Promoted(..), .. }),
- projection: [],
- } => {
- buf.push_str("promoted");
- }
- PlaceRef {
- base: PlaceBase::Static(box Static { kind: StaticKind::Static, def_id, .. }),
- projection: [],
- } => {
- buf.push_str(&self.infcx.tcx.item_name(*def_id).to_string());
- }
- PlaceRef { base: &PlaceBase::Local(local), projection: [ProjectionElem::Deref] }
- if self.body.local_decls[local].is_ref_for_guard() =>
+ PlaceRef { local, projection: [ProjectionElem::Deref] }
+ if self.body.local_decls[*local].is_ref_for_guard() =>
{
self.append_place_to_string(
- PlaceRef { base: &PlaceBase::Local(local), projection: &[] },
+ PlaceRef { local: local, projection: &[] },
buf,
autoderef,
&including_downcast,
)?;
}
- PlaceRef { base: &PlaceBase::Local(local), projection: [ProjectionElem::Deref] }
- if self.body.local_decls[local].is_ref_to_static() =>
+ PlaceRef { local, projection: [ProjectionElem::Deref] }
+ if self.body.local_decls[*local].is_ref_to_static() =>
{
- let local_info = &self.body.local_decls[local].local_info;
+ let local_info = &self.body.local_decls[*local].local_info;
if let LocalInfo::StaticRef { def_id, .. } = *local_info {
buf.push_str(&self.infcx.tcx.item_name(def_id).as_str());
} else {
unreachable!();
}
}
- PlaceRef { base, projection: [proj_base @ .., elem] } => {
+ PlaceRef { local, projection: [proj_base @ .., elem] } => {
match elem {
ProjectionElem::Deref => {
let upvar_field_projection = self.is_upvar_field_projection(place);
if autoderef {
// FIXME turn this recursion into iteration
self.append_place_to_string(
- PlaceRef { base, projection: proj_base },
+ PlaceRef { local, projection: proj_base },
buf,
autoderef,
&including_downcast,
)?;
} else {
- match (proj_base, base) {
- _ => {
- buf.push_str(&"*");
- self.append_place_to_string(
- PlaceRef { base, projection: proj_base },
- buf,
- autoderef,
- &including_downcast,
- )?;
- }
- }
+ buf.push_str(&"*");
+ self.append_place_to_string(
+ PlaceRef { local, projection: proj_base },
+ buf,
+ autoderef,
+ &including_downcast,
+ )?;
}
}
}
ProjectionElem::Downcast(..) => {
self.append_place_to_string(
- PlaceRef { base, projection: proj_base },
+ PlaceRef { local, projection: proj_base },
buf,
autoderef,
&including_downcast,
buf.push_str(&name);
} else {
let field_name = self
- .describe_field(PlaceRef { base, projection: proj_base }, *field);
+ .describe_field(PlaceRef { local, projection: proj_base }, *field);
self.append_place_to_string(
- PlaceRef { base, projection: proj_base },
+ PlaceRef { local, projection: proj_base },
buf,
autoderef,
&including_downcast,
autoderef = true;
self.append_place_to_string(
- PlaceRef { base, projection: proj_base },
+ PlaceRef { local, projection: proj_base },
buf,
autoderef,
&including_downcast,
// then use another while the borrow is held, don't output indices details
// to avoid confusing the end-user
self.append_place_to_string(
- PlaceRef { base, projection: proj_base },
+ PlaceRef { local, projection: proj_base },
buf,
autoderef,
&including_downcast,
fn describe_field(&self, place: PlaceRef<'cx, 'tcx>, field: Field) -> String {
// FIXME Place2 Make this work iteratively
match place {
- PlaceRef { base: PlaceBase::Local(local), projection: [] } => {
+ PlaceRef { local, projection: [] } => {
let local = &self.body.local_decls[*local];
self.describe_field_from_ty(&local.ty, field, None)
}
- PlaceRef { base: PlaceBase::Static(static_), projection: [] } => {
- self.describe_field_from_ty(&static_.ty, field, None)
- }
- PlaceRef { base, projection: [proj_base @ .., elem] } => match elem {
+ PlaceRef { local, projection: [proj_base @ .., elem] } => match elem {
ProjectionElem::Deref => {
- self.describe_field(PlaceRef { base, projection: proj_base }, field)
+ self.describe_field(PlaceRef { local, projection: proj_base }, field)
}
ProjectionElem::Downcast(_, variant_index) => {
let base_ty =
- Place::ty_from(place.base, place.projection, *self.body, self.infcx.tcx).ty;
+ Place::ty_from(place.local, place.projection, *self.body, self.infcx.tcx)
+ .ty;
self.describe_field_from_ty(&base_ty, field, Some(*variant_index))
}
ProjectionElem::Field(_, field_type) => {
ProjectionElem::Index(..)
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. } => {
- self.describe_field(PlaceRef { base, projection: proj_base }, field)
+ self.describe_field(PlaceRef { local, projection: proj_base }, field)
}
},
}
// If we didn't find an overloaded deref or index, then assume it's a
// built in deref and check the type of the base.
- let base_ty = Place::ty_from(deref_base.base, deref_base.projection, *self.body, tcx).ty;
+ let base_ty = Place::ty_from(deref_base.local, deref_base.projection, *self.body, tcx).ty;
if base_ty.is_unsafe_ptr() {
BorrowedContentSource::DerefRawPointer
} else if base_ty.is_mutable_ptr() {
);
(
match kind {
- IllegalMoveOriginKind::Static => {
- unreachable!();
- }
IllegalMoveOriginKind::BorrowedContent { target_place } => self
.report_cannot_move_from_borrowed_content(
original_path,
let description = if place.projection.len() == 1 {
format!("static item `{}`", self.describe_place(place.as_ref()).unwrap())
} else {
- let base_static = PlaceRef { base: &place.base, projection: &[ProjectionElem::Deref] };
+ let base_static =
+ PlaceRef { local: &place.local, projection: &[ProjectionElem::Deref] };
format!(
"`{:?}` as `{:?}` is a static item",
let deref_base = match deref_target_place.projection.as_ref() {
&[ref proj_base @ .., ProjectionElem::Deref] => {
- PlaceRef { base: &deref_target_place.base, projection: &proj_base }
+ PlaceRef { local: &deref_target_place.local, projection: &proj_base }
}
_ => bug!("deref_target_place is not a deref projection"),
};
- if let PlaceRef { base: PlaceBase::Local(local), projection: [] } = deref_base {
+ if let PlaceRef { local, projection: [] } = deref_base {
let decl = &self.body.local_decls[*local];
if decl.is_ref_for_guard() {
let mut err = self.cannot_move_out_of(
use rustc::mir::{self, ClearCrossCrate, Local, LocalInfo, Location, ReadOnlyBodyAndCache};
-use rustc::mir::{Mutability, Place, PlaceBase, PlaceRef, ProjectionElem};
+use rustc::mir::{Mutability, Place, PlaceRef, ProjectionElem};
use rustc::ty::{self, Ty, TyCtxt};
use rustc_hir as hir;
use rustc_hir::Node;
debug!("report_mutability_error: access_place_desc={:?}", access_place_desc);
match the_place_err {
- PlaceRef { base: PlaceBase::Local(local), projection: [] } => {
+ PlaceRef { local, projection: [] } => {
item_msg = format!("`{}`", access_place_desc.unwrap());
if access_place.as_local().is_some() {
reason = ", as it is not declared as mutable".to_string();
}
PlaceRef {
- base: _,
+ local,
projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)],
} => {
debug_assert!(is_closure_or_generator(
- Place::ty_from(&the_place_err.base, proj_base, *self.body, self.infcx.tcx).ty
+ Place::ty_from(local, proj_base, *self.body, self.infcx.tcx).ty
));
item_msg = format!("`{}`", access_place_desc.unwrap());
}
}
- PlaceRef { base: &PlaceBase::Local(local), projection: [ProjectionElem::Deref] }
- if self.body.local_decls[local].is_ref_for_guard() =>
+ PlaceRef { local, projection: [ProjectionElem::Deref] }
+ if self.body.local_decls[*local].is_ref_for_guard() =>
{
item_msg = format!("`{}`", access_place_desc.unwrap());
reason = ", as it is immutable for the pattern guard".to_string();
}
- PlaceRef { base: &PlaceBase::Local(local), projection: [ProjectionElem::Deref] }
- if self.body.local_decls[local].is_ref_to_static() =>
+ PlaceRef { local, projection: [ProjectionElem::Deref] }
+ if self.body.local_decls[*local].is_ref_to_static() =>
{
if access_place.projection.len() == 1 {
item_msg = format!("immutable static item `{}`", access_place_desc.unwrap());
reason = String::new();
} else {
item_msg = format!("`{}`", access_place_desc.unwrap());
- let local_info = &self.body.local_decls[local].local_info;
+ let local_info = &self.body.local_decls[*local].local_info;
if let LocalInfo::StaticRef { def_id, .. } = *local_info {
let static_name = &self.infcx.tcx.item_name(def_id);
reason = format!(", as `{}` is an immutable static item", static_name);
}
}
}
- PlaceRef { base: _, projection: [proj_base @ .., ProjectionElem::Deref] } => {
- if the_place_err.base == &PlaceBase::Local(Local::new(1))
+ PlaceRef { local: _, projection: [proj_base @ .., ProjectionElem::Deref] } => {
+ if *the_place_err.local == Local::new(1)
&& proj_base.is_empty()
&& !self.upvars.is_empty()
{
debug_assert!(self.body.local_decls[Local::new(1)].ty.is_region_ptr());
debug_assert!(is_closure_or_generator(
Place::ty_from(
- the_place_err.base,
+ the_place_err.local,
the_place_err.projection,
*self.body,
self.infcx.tcx
}
} else {
let source = self.borrowed_content_source(PlaceRef {
- base: the_place_err.base,
+ local: the_place_err.local,
projection: proj_base,
});
let pointer_type = source.describe_for_immutable_place();
}
}
- PlaceRef { base: PlaceBase::Static(_), .. }
- | PlaceRef { base: _, projection: [.., ProjectionElem::Index(_)] }
- | PlaceRef { base: _, projection: [.., ProjectionElem::ConstantIndex { .. }] }
- | PlaceRef { base: _, projection: [.., ProjectionElem::Subslice { .. }] }
- | PlaceRef { base: _, projection: [.., ProjectionElem::Downcast(..)] } => {
+ PlaceRef { local: _, projection: [.., ProjectionElem::Index(_)] }
+ | PlaceRef { local: _, projection: [.., ProjectionElem::ConstantIndex { .. }] }
+ | PlaceRef { local: _, projection: [.., ProjectionElem::Subslice { .. }] }
+ | PlaceRef { local: _, projection: [.., ProjectionElem::Downcast(..)] } => {
bug!("Unexpected immutable place.")
}
}
// struct we've got a field access of (it must be a reference since there's a deref
// after the field access).
PlaceRef {
- base,
+ local,
projection:
[proj_base @ .., ProjectionElem::Deref, ProjectionElem::Field(field, _), ProjectionElem::Deref],
} => {
if let Some((span, message)) = annotate_struct_field(
self.infcx.tcx,
- Place::ty_from(base, proj_base, *self.body, self.infcx.tcx).ty,
+ Place::ty_from(local, proj_base, *self.body, self.infcx.tcx).ty,
field,
) {
err.span_suggestion(
}
// Suggest removing a `&mut` from the use of a mutable reference.
- PlaceRef { base: PlaceBase::Local(local), projection: [] }
+ PlaceRef { local, projection: [] }
if {
self.body
.local_decls
// We want to suggest users use `let mut` for local (user
// variable) mutations...
- PlaceRef { base: PlaceBase::Local(local), projection: [] }
+ PlaceRef { local, projection: [] }
if self.body.local_decls[*local].can_be_made_mutable() =>
{
// ... but it doesn't make sense to suggest it on
// Also suggest adding mut for upvars
PlaceRef {
- base,
+ local,
projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)],
} => {
debug_assert!(is_closure_or_generator(
- Place::ty_from(base, proj_base, *self.body, self.infcx.tcx).ty
+ Place::ty_from(local, proj_base, *self.body, self.infcx.tcx).ty
));
err.span_label(span, format!("cannot {ACT}", ACT = act));
// complete hack to approximate old AST-borrowck
// diagnostic: if the span starts with a mutable borrow of
// a local variable, then just suggest the user remove it.
- PlaceRef { base: PlaceBase::Local(_), projection: [] }
+ PlaceRef { local: _, projection: [] }
if {
if let Ok(snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(span) {
snippet.starts_with("&mut ")
err.span_label(span, "try removing `&mut` here");
}
- PlaceRef { base: PlaceBase::Local(local), projection: [ProjectionElem::Deref] }
+ PlaceRef { local, projection: [ProjectionElem::Deref] }
if self.body.local_decls[*local].is_ref_for_guard() =>
{
err.span_label(span, format!("cannot {ACT}", ACT = act));
//
// FIXME: can this case be generalized to work for an
// arbitrary base for the projection?
- PlaceRef { base: PlaceBase::Local(local), projection: [ProjectionElem::Deref] }
+ PlaceRef { local, projection: [ProjectionElem::Deref] }
if self.body.local_decls[*local].is_user_variable() =>
{
let local_decl = &self.body.local_decls[*local];
}
PlaceRef {
- base,
+ local,
projection: [ProjectionElem::Deref],
// FIXME document what is this 1 magic number about
- } if *base == PlaceBase::Local(Local::new(1)) && !self.upvars.is_empty() => {
+ } if *local == Local::new(1) && !self.upvars.is_empty() => {
err.span_label(span, format!("cannot {ACT}", ACT = act));
err.span_help(
self.body.span,
);
}
- PlaceRef { base: _, projection: [.., ProjectionElem::Deref] } => {
+ PlaceRef { local: _, projection: [.., ProjectionElem::Deref] } => {
err.span_label(span, format!("cannot {ACT}", ACT = act));
match opt_source {
};
use rustc::mir::{Body, ConstraintCategory, Location};
use rustc::ty::{self, RegionVid, Ty};
-use rustc_errors::DiagnosticBuilder;
+use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_hir::def_id::DefId;
use rustc_index::vec::IndexVec;
use rustc_span::symbol::kw;
use rustc_span::Span;
use std::collections::VecDeque;
-use syntax::errors::Applicability;
use crate::util::borrowck_errors;
use rustc::mir::{BasicBlock, Body, Location, Place, ReadOnlyBodyAndCache, Rvalue};
use rustc::mir::{BorrowKind, Mutability, Operand};
use rustc::mir::{Statement, StatementKind};
-use rustc::ty::{self, TyCtxt};
+use rustc::ty::TyCtxt;
use rustc_data_structures::graph::dominators::Dominators;
use crate::dataflow::indexes::BorrowIndex;
pub(super) fn generate_invalidates<'tcx>(
tcx: TyCtxt<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
all_facts: &mut Option<AllFacts>,
location_table: &LocationTable,
body: ReadOnlyBodyAndCache<'_, 'tcx>,
let mut ig = InvalidationGenerator {
all_facts,
borrow_set,
- param_env,
tcx,
location_table,
body: &body,
struct InvalidationGenerator<'cx, 'tcx> {
tcx: TyCtxt<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
all_facts: &'cx mut AllFacts,
location_table: &'cx LocationTable,
body: &'cx Body<'tcx>,
);
let tcx = self.tcx;
let body = self.body;
- let param_env = self.param_env;
let borrow_set = self.borrow_set.clone();
let indices = self.borrow_set.borrows.indices();
each_borrow_involving_path(
self,
tcx,
- param_env,
body,
location,
(sd, place),
use rustc::lint::builtin::UNUSED_MUT;
use rustc::mir::{
read_only, Body, BodyAndCache, ClearCrossCrate, Local, Location, Mutability, Operand, Place,
- PlaceBase, PlaceElem, PlaceRef, ReadOnlyBodyAndCache, Static, StaticKind,
+ PlaceElem, PlaceRef, ReadOnlyBodyAndCache,
};
use rustc::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind};
use rustc::mir::{Field, ProjectionElem, Promoted, Rvalue, Statement, StatementKind};
def_id,
&attributes,
&dead_unwinds,
- Borrows::new(tcx, &body, param_env, regioncx.clone(), &borrow_set),
+ Borrows::new(tcx, &body, regioncx.clone(), &borrow_set),
|rs, i| DebugFormatted::new(&rs.location(i)),
));
let flow_uninits = FlowAtLocation::new(do_dataflow(
infcx,
body,
mir_def_id: def_id,
- param_env,
move_data: &mdpe.move_data,
location_table,
movable_generator,
crate infcx: &'cx InferCtxt<'cx, 'tcx>,
body: ReadOnlyBodyAndCache<'cx, 'tcx>,
mir_def_id: DefId,
- param_env: ty::ParamEnv<'tcx>,
move_data: &'cx MoveData<'tcx>,
/// Map from MIR `Location` to `LocationIndex`; created
}
struct RootPlace<'d, 'tcx> {
- place_base: &'d PlaceBase<'tcx>,
+ place_local: &'d Local,
place_projection: &'d [PlaceElem<'tcx>],
is_local_mutation_allowed: LocalMutationIsAllowed,
}
let tcx = self.infcx.tcx;
let body = self.body;
let body: &Body<'_> = &body;
- let param_env = self.param_env;
let location_table = self.location_table.start_index(location);
let borrow_set = self.borrow_set.clone();
each_borrow_involving_path(
self,
tcx,
- param_env,
body,
location,
(sd, place_span.0),
if let Some(field) = this.is_upvar_field_projection(place.as_ref()) {
this.used_mut_upvars.push(field);
}
- } else if let PlaceBase::Local(local) = place.base {
- this.used_mut.insert(local);
+ } else {
+ this.used_mut.insert(place.local);
}
};
debug!("check_for_invalidation_at_exit({:?})", borrow);
let place = &borrow.borrowed_place;
let deref = [ProjectionElem::Deref];
- let mut root_place = PlaceRef { base: &place.base, projection: &[] };
+ let mut root_place = PlaceRef { local: &place.local, projection: &[] };
// FIXME(nll-rfc#40): do more precise destructor tracking here. For now
// we just know that all locals are dropped at function exit (otherwise
//
// FIXME: allow thread-locals to borrow other thread locals?
- let (might_be_alive, will_be_dropped) = match root_place.base {
- PlaceBase::Static(_) => (true, false),
- PlaceBase::Local(local) => {
- if self.body.local_decls[*local].is_ref_to_thread_local() {
- // Thread-locals might be dropped after the function exits
- // We have to dereference the outer reference because
- // borrows don't conflict behind shared references.
- root_place.projection = &deref;
- (true, true)
- } else {
- (false, self.locals_are_invalidated_at_exit)
- }
- }
- };
+ let (might_be_alive, will_be_dropped) =
+ if self.body.local_decls[*root_place.local].is_ref_to_thread_local() {
+ // Thread-locals might be dropped after the function exits
+ // We have to dereference the outer reference because
+ // borrows don't conflict behind shared references.
+ root_place.projection = &deref;
+ (true, true)
+ } else {
+ (false, self.locals_are_invalidated_at_exit)
+ };
if !will_be_dropped {
debug!("place_is_invalidated_at_exit({:?}) - won't be dropped", place);
if places_conflict::borrow_conflicts_with_place(
self.infcx.tcx,
- self.param_env,
&self.body,
place,
borrow.kind,
// This code covers scenarios 1, 2, and 3.
debug!("check_if_full_path_is_moved place: {:?}", place_span.0);
- match self.move_path_closest_to(place_span.0) {
- Ok((prefix, mpi)) => {
- if maybe_uninits.contains(mpi) {
- self.report_use_of_moved_or_uninitialized(
- location,
- desired_action,
- (prefix, place_span.0, place_span.1),
- mpi,
- );
- }
- }
- Err(NoMovePathFound::ReachedStatic) => {
- // Okay: we do not build MoveData for static variables
- } // Only query longest prefix with a MovePath, not further
- // ancestors; dataflow recurs on children when parents
- // move (to support partial (re)inits).
- //
- // (I.e., querying parents breaks scenario 7; but may want
- // to do such a query based on partial-init feature-gate.)
- }
+ let (prefix, mpi) = self.move_path_closest_to(place_span.0);
+ if maybe_uninits.contains(mpi) {
+ self.report_use_of_moved_or_uninitialized(
+ location,
+ desired_action,
+ (prefix, place_span.0, place_span.1),
+ mpi,
+ );
+ } // Only query longest prefix with a MovePath, not further
+ // ancestors; dataflow recurs on children when parents
+ // move (to support partial (re)inits).
+ //
+ // (I.e., querying parents breaks scenario 7; but may want
+ // to do such a query based on partial-init feature-gate.)
}
/// Subslices correspond to multiple move paths, so we iterate through the
place_span.0.projection
{
let place_ty =
- Place::ty_from(place_span.0.base, base_proj, self.body(), self.infcx.tcx);
+ Place::ty_from(place_span.0.local, base_proj, self.body(), self.infcx.tcx);
if let ty::Array(..) = place_ty.ty.kind {
- let array_place = PlaceRef { base: place_span.0.base, projection: base_proj };
+ let array_place = PlaceRef { local: place_span.0.local, projection: base_proj };
self.check_if_subslice_element_is_moved(
location,
desired_action,
fn move_path_closest_to(
&mut self,
place: PlaceRef<'_, 'tcx>,
- ) -> Result<(PlaceRef<'cx, 'tcx>, MovePathIndex), NoMovePathFound> {
+ ) -> (PlaceRef<'cx, 'tcx>, MovePathIndex) {
match self.move_data.rev_lookup.find(place) {
LookupResult::Parent(Some(mpi)) | LookupResult::Exact(mpi) => {
- Ok((self.move_data.move_paths[mpi].place.as_ref(), mpi))
+ (self.move_data.move_paths[mpi].place.as_ref(), mpi)
}
- LookupResult::Parent(None) => Err(NoMovePathFound::ReachedStatic),
+ LookupResult::Parent(None) => panic!("should have move path for every Local"),
}
}
self.check_if_full_path_is_moved(
location, InitializationRequiringAction::Use,
(PlaceRef {
- base: &place.base,
+ local: &place.local,
projection: proj_base,
}, span), flow_state);
// (base initialized; no need to
// assigning to `P.f` requires `P` itself
// be already initialized
let tcx = self.infcx.tcx;
- let base_ty = Place::ty_from(&place.base, proj_base, self.body(), tcx).ty;
+ let base_ty = Place::ty_from(&place.local, proj_base, self.body(), tcx).ty;
match base_ty.kind {
ty::Adt(def, _) if def.has_dtor(tcx) => {
self.check_if_path_or_subpath_is_moved(
location, InitializationRequiringAction::Assignment,
(PlaceRef {
- base: &place.base,
+ local: &place.local,
projection: proj_base,
}, span), flow_state);
// is allowed, remove this match arm.
ty::Adt(..) | ty::Tuple(..) => {
check_parent_of_field(self, location, PlaceRef {
- base: &place.base,
+ local: &place.local,
projection: proj_base,
}, span, flow_state);
- if let PlaceBase::Local(local) = place.base {
- // rust-lang/rust#21232,
- // #54499, #54986: during
- // period where we reject
- // partial initialization, do
- // not complain about
- // unnecessary `mut` on an
- // attempt to do a partial
- // initialization.
- self.used_mut.insert(local);
- }
+ // rust-lang/rust#21232, #54499, #54986: during period where we reject
+ // partial initialization, do not complain about unnecessary `mut` on
+ // an attempt to do a partial initialization.
+ self.used_mut.insert(place.local);
}
_ => {}
// of the union - we should error in that case.
let tcx = this.infcx.tcx;
if let ty::Adt(def, _) =
- Place::ty_from(base.base, base.projection, this.body(), tcx).ty.kind
+ Place::ty_from(base.local, base.projection, this.body(), tcx).ty.kind
{
if def.is_union() {
if this.move_data.path_map[mpi].iter().any(|moi| {
// partial initialization, do not complain about mutability
// errors except for actual mutation (as opposed to an attempt
// to do a partial initialization).
- let previously_initialized = if let PlaceBase::Local(local) = place.base {
- self.is_local_ever_initialized(local, flow_state).is_some()
- } else {
- true
- };
+ let previously_initialized =
+ self.is_local_ever_initialized(place.local, flow_state).is_some();
// at this point, we have set up the error reporting state.
if previously_initialized {
/// Adds the place into the used mutable variables set
fn add_used_mut<'d>(&mut self, root_place: RootPlace<'d, 'tcx>, flow_state: &Flows<'cx, 'tcx>) {
match root_place {
- RootPlace {
- place_base: PlaceBase::Local(local),
- place_projection: [],
- is_local_mutation_allowed,
- } => {
+ RootPlace { place_local: local, place_projection: [], is_local_mutation_allowed } => {
// If the local may have been initialized, and it is now currently being
// mutated, then it is justified to be annotated with the `mut`
// keyword, since the mutation may be a possible reassignment.
}
}
RootPlace {
- place_base: _,
+ place_local: _,
place_projection: _,
is_local_mutation_allowed: LocalMutationIsAllowed::Yes,
} => {}
RootPlace {
- place_base,
+ place_local,
place_projection: place_projection @ [.., _],
is_local_mutation_allowed: _,
} => {
if let Some(field) = self.is_upvar_field_projection(PlaceRef {
- base: &place_base,
- projection: &place_projection,
+ local: place_local,
+ projection: place_projection,
}) {
self.used_mut_upvars.push(field);
}
}
- RootPlace {
- place_base: PlaceBase::Static(..),
- place_projection: [],
- is_local_mutation_allowed: _,
- } => {}
}
}
is_local_mutation_allowed: LocalMutationIsAllowed,
) -> Result<RootPlace<'d, 'tcx>, PlaceRef<'d, 'tcx>> {
match place {
- PlaceRef { base: PlaceBase::Local(local), projection: [] } => {
+ PlaceRef { local, projection: [] } => {
let local = &self.body.local_decls[*local];
match local.mutability {
Mutability::Not => match is_local_mutation_allowed {
LocalMutationIsAllowed::Yes => Ok(RootPlace {
- place_base: place.base,
+ place_local: place.local,
place_projection: place.projection,
is_local_mutation_allowed: LocalMutationIsAllowed::Yes,
}),
LocalMutationIsAllowed::ExceptUpvars => Ok(RootPlace {
- place_base: place.base,
+ place_local: place.local,
place_projection: place.projection,
is_local_mutation_allowed: LocalMutationIsAllowed::ExceptUpvars,
}),
LocalMutationIsAllowed::No => Err(place),
},
Mutability::Mut => Ok(RootPlace {
- place_base: place.base,
+ place_local: place.local,
place_projection: place.projection,
is_local_mutation_allowed,
}),
}
}
- // The rules for promotion are made by `qualify_consts`, there wouldn't even be a
- // `Place::Promoted` if the promotion weren't 100% legal. So we just forward this
- PlaceRef {
- base: PlaceBase::Static(box Static { kind: StaticKind::Promoted(..), .. }),
- projection: [],
- } => Ok(RootPlace {
- place_base: place.base,
- place_projection: place.projection,
- is_local_mutation_allowed,
- }),
- PlaceRef {
- base: PlaceBase::Static(box Static { kind: StaticKind::Static, def_id, .. }),
- projection: [],
- } => {
- if !self.infcx.tcx.is_mutable_static(*def_id) {
- Err(place)
- } else {
- Ok(RootPlace {
- place_base: place.base,
- place_projection: place.projection,
- is_local_mutation_allowed,
- })
- }
- }
- PlaceRef { base: _, projection: [proj_base @ .., elem] } => {
+ PlaceRef { local: _, projection: [proj_base @ .., elem] } => {
match elem {
ProjectionElem::Deref => {
let base_ty =
- Place::ty_from(place.base, proj_base, self.body(), self.infcx.tcx).ty;
+ Place::ty_from(place.local, proj_base, self.body(), self.infcx.tcx).ty;
// Check the kind of deref to decide
match base_ty.kind {
};
self.is_mutable(
- PlaceRef { base: place.base, projection: proj_base },
+ PlaceRef { local: place.local, projection: proj_base },
mode,
)
}
// `*mut` raw pointers are always mutable, regardless of
// context. The users have to check by themselves.
hir::Mutability::Mut => Ok(RootPlace {
- place_base: place.base,
+ place_local: place.local,
place_projection: place.projection,
is_local_mutation_allowed,
}),
}
// `Box<T>` owns its content, so mutable if its location is mutable
_ if base_ty.is_box() => self.is_mutable(
- PlaceRef { base: place.base, projection: proj_base },
+ PlaceRef { local: place.local, projection: proj_base },
is_local_mutation_allowed,
),
// Deref should only be for reference, pointers or boxes
// }
// ```
let _ = self.is_mutable(
- PlaceRef { base: place.base, projection: proj_base },
+ PlaceRef { local: place.local, projection: proj_base },
is_local_mutation_allowed,
)?;
Ok(RootPlace {
- place_base: place.base,
+ place_local: place.local,
place_projection: place.projection,
is_local_mutation_allowed,
})
}
} else {
self.is_mutable(
- PlaceRef { base: place.base, projection: proj_base },
+ PlaceRef { local: place.local, projection: proj_base },
is_local_mutation_allowed,
)
}
match place_projection {
[base @ .., ProjectionElem::Field(field, _ty)] => {
let tcx = self.infcx.tcx;
- let base_ty = Place::ty_from(place_ref.base, base, self.body(), tcx).ty;
+ let base_ty = Place::ty_from(place_ref.local, base, self.body(), tcx).ty;
if (base_ty.is_closure() || base_ty.is_generator())
&& (!by_ref || self.upvars[field.index()].by_ref)
}
}
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-enum NoMovePathFound {
- ReachedStatic,
-}
-
/// The degree of overlap between 2 places for borrow-checking.
enum Overlap {
/// The places might partially overlap - in this case, we give
constraint_generation::generate_constraints(
infcx,
- param_env,
&mut liveness_constraints,
&mut all_facts,
location_table,
);
// Generate various additional constraints.
- invalidation::generate_invalidates(
- infcx.tcx,
- param_env,
- &mut all_facts,
- location_table,
- body,
- borrow_set,
- );
+ invalidation::generate_invalidates(infcx.tcx, &mut all_facts, location_table, body, borrow_set);
// Dump facts if requested.
let polonius_output = all_facts.and_then(|all_facts| {
use crate::borrow_check::AccessDepth;
use crate::dataflow::indexes::BorrowIndex;
use rustc::mir::BorrowKind;
-use rustc::mir::{BasicBlock, Body, Location, Place, PlaceBase};
-use rustc::ty::{self, TyCtxt};
+use rustc::mir::{BasicBlock, Body, Location, Place};
+use rustc::ty::TyCtxt;
use rustc_data_structures::graph::dominators::Dominators;
/// Returns `true` if the borrow represented by `kind` is
pub(super) fn each_borrow_involving_path<'tcx, F, I, S>(
s: &mut S,
tcx: TyCtxt<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
body: &Body<'tcx>,
_location: Location,
access_place: (AccessDepth, &Place<'tcx>),
if places_conflict::borrow_conflicts_with_place(
tcx,
- param_env,
body,
&borrowed.borrowed_place,
borrowed.kind,
/// Determines if a given borrow is borrowing local data
/// This is called for all Yield expressions on movable generators
pub(super) fn borrow_of_local_data(place: &Place<'_>) -> bool {
- match place.base {
- PlaceBase::Static(_) => false,
-
- // Reborrow of already borrowed data is ignored
- // Any errors will be caught on the initial borrow
- PlaceBase::Local(_) => !place.is_indirect(),
- }
+ // Reborrow of already borrowed data is ignored
+ // Any errors will be caught on the initial borrow
+ !place.is_indirect()
}
use crate::borrow_check::borrow_set::LocalsStateAtExit;
use rustc::mir::ProjectionElem;
-use rustc::mir::{Body, Mutability, Place, PlaceBase};
+use rustc::mir::{Body, Mutability, Place};
use rustc::ty::{self, TyCtxt};
use rustc_hir as hir;
body: &Body<'tcx>,
locals_state_at_exit: &LocalsStateAtExit,
) -> bool {
- let local = match self.base {
- // If a local variable is immutable, then we only need to track borrows to guard
- // against two kinds of errors:
- // * The variable being dropped while still borrowed (e.g., because the fn returns
- // a reference to a local variable)
- // * The variable being moved while still borrowed
- //
- // In particular, the variable cannot be mutated -- the "access checks" will fail --
- // so we don't have to worry about mutation while borrowed.
- PlaceBase::Local(local) => match locals_state_at_exit {
- LocalsStateAtExit::AllAreInvalidated => local,
- LocalsStateAtExit::SomeAreInvalidated { has_storage_dead_or_moved } => {
- let ignore = !has_storage_dead_or_moved.contains(local)
- && body.local_decls[local].mutability == Mutability::Not;
- debug!("ignore_borrow: local {:?} => {:?}", local, ignore);
- if ignore {
- return true;
- } else {
- local
- }
- }
- },
- PlaceBase::Static(_) => return true,
- };
+ // If a local variable is immutable, then we only need to track borrows to guard
+ // against two kinds of errors:
+ // * The variable being dropped while still borrowed (e.g., because the fn returns
+ // a reference to a local variable)
+ // * The variable being moved while still borrowed
+ //
+ // In particular, the variable cannot be mutated -- the "access checks" will fail --
+ // so we don't have to worry about mutation while borrowed.
+ if let LocalsStateAtExit::SomeAreInvalidated { has_storage_dead_or_moved } =
+ locals_state_at_exit
+ {
+ let ignore = !has_storage_dead_or_moved.contains(self.local)
+ && body.local_decls[self.local].mutability == Mutability::Not;
+ debug!("ignore_borrow: local {:?} => {:?}", self.local, ignore);
+ if ignore {
+ return true;
+ }
+ }
for (i, elem) in self.projection.iter().enumerate() {
let proj_base = &self.projection[..i];
if *elem == ProjectionElem::Deref {
- let ty = Place::ty_from(&self.base, proj_base, body, tcx).ty;
+ let ty = Place::ty_from(&self.local, proj_base, body, tcx).ty;
match ty.kind {
ty::Ref(_, _, hir::Mutability::Not) if i == 0 => {
// For references to thread-local statics, we do need
// to track the borrow.
- if body.local_decls[local].is_ref_to_thread_local() {
+ if body.local_decls[self.local].is_ref_to_thread_local() {
continue;
}
return true;
use crate::borrow_check::ArtificialField;
use crate::borrow_check::Overlap;
use crate::borrow_check::{AccessDepth, Deep, Shallow};
-use rustc::mir::{
- Body, BorrowKind, Place, PlaceBase, PlaceElem, PlaceRef, ProjectionElem, StaticKind,
-};
+use rustc::mir::{Body, BorrowKind, Local, Place, PlaceElem, PlaceRef, ProjectionElem};
use rustc::ty::{self, TyCtxt};
use rustc_hir as hir;
use std::cmp::max;
/// dataflow).
crate fn places_conflict<'tcx>(
tcx: TyCtxt<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
body: &Body<'tcx>,
borrow_place: &Place<'tcx>,
access_place: &Place<'tcx>,
) -> bool {
borrow_conflicts_with_place(
tcx,
- param_env,
body,
borrow_place,
BorrowKind::Mut { allow_two_phase_borrow: true },
/// order to make the conservative choice and preserve soundness.
pub(super) fn borrow_conflicts_with_place<'tcx>(
tcx: TyCtxt<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
body: &Body<'tcx>,
borrow_place: &Place<'tcx>,
borrow_kind: BorrowKind,
}
}
- place_components_conflict(
- tcx,
- param_env,
- body,
- borrow_place,
- borrow_kind,
- access_place,
- access,
- bias,
- )
+ place_components_conflict(tcx, body, borrow_place, borrow_kind, access_place, access, bias)
}
fn place_components_conflict<'tcx>(
tcx: TyCtxt<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
body: &Body<'tcx>,
borrow_place: &Place<'tcx>,
borrow_kind: BorrowKind,
// and either equal or disjoint.
// - If we did run out of access, the borrow can access a part of it.
- let borrow_base = &borrow_place.base;
- let access_base = access_place.base;
+ let borrow_local = &borrow_place.local;
+ let access_local = access_place.local;
- match place_base_conflict(tcx, param_env, borrow_base, access_base) {
+ match place_base_conflict(borrow_local, access_local) {
Overlap::Arbitrary => {
bug!("Two base can't return Arbitrary");
}
match place_projection_conflict(
tcx,
body,
- borrow_base,
+ borrow_local,
borrow_proj_base,
borrow_c,
access_c,
// access cares about.
let proj_base = &borrow_place.projection[..access_place.projection.len() + i];
- let base_ty = Place::ty_from(borrow_base, proj_base, body, tcx).ty;
+ let base_ty = Place::ty_from(borrow_local, proj_base, body, tcx).ty;
match (elem, &base_ty.kind, access) {
(_, _, Shallow(Some(ArtificialField::ArrayLength)))
// Given that the bases of `elem1` and `elem2` are always either equal
// or disjoint (and have the same type!), return the overlap situation
// between `elem1` and `elem2`.
-fn place_base_conflict<'tcx>(
- tcx: TyCtxt<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- elem1: &PlaceBase<'tcx>,
- elem2: &PlaceBase<'tcx>,
-) -> Overlap {
- match (elem1, elem2) {
- (PlaceBase::Local(l1), PlaceBase::Local(l2)) => {
- if l1 == l2 {
- // the same local - base case, equal
- debug!("place_element_conflict: DISJOINT-OR-EQ-LOCAL");
- Overlap::EqualOrDisjoint
- } else {
- // different locals - base case, disjoint
- debug!("place_element_conflict: DISJOINT-LOCAL");
- Overlap::Disjoint
- }
- }
- (PlaceBase::Static(s1), PlaceBase::Static(s2)) => {
- match (&s1.kind, &s2.kind) {
- (StaticKind::Static, StaticKind::Static) => {
- if s1.def_id != s2.def_id {
- debug!("place_element_conflict: DISJOINT-STATIC");
- Overlap::Disjoint
- } else if tcx.is_mutable_static(s1.def_id) {
- // We ignore mutable statics - they can only be unsafe code.
- debug!("place_element_conflict: IGNORE-STATIC-MUT");
- Overlap::Disjoint
- } else {
- debug!("place_element_conflict: DISJOINT-OR-EQ-STATIC");
- Overlap::EqualOrDisjoint
- }
- }
- (StaticKind::Promoted(promoted_1, _), StaticKind::Promoted(promoted_2, _)) => {
- if promoted_1 == promoted_2 {
- if let ty::Array(_, len) = s1.ty.kind {
- if let Some(0) = len.try_eval_usize(tcx, param_env) {
- // Ignore conflicts with promoted [T; 0].
- debug!("place_element_conflict: IGNORE-LEN-0-PROMOTED");
- return Overlap::Disjoint;
- }
- }
- // the same promoted - base case, equal
- debug!("place_element_conflict: DISJOINT-OR-EQ-PROMOTED");
- Overlap::EqualOrDisjoint
- } else {
- // different promoteds - base case, disjoint
- debug!("place_element_conflict: DISJOINT-PROMOTED");
- Overlap::Disjoint
- }
- }
- (_, _) => {
- debug!("place_element_conflict: DISJOINT-STATIC-PROMOTED");
- Overlap::Disjoint
- }
- }
- }
- (PlaceBase::Local(_), PlaceBase::Static(_))
- | (PlaceBase::Static(_), PlaceBase::Local(_)) => {
- debug!("place_element_conflict: DISJOINT-STATIC-LOCAL-PROMOTED");
- Overlap::Disjoint
- }
+fn place_base_conflict(l1: &Local, l2: &Local) -> Overlap {
+ if l1 == l2 {
+ // the same local - base case, equal
+ debug!("place_element_conflict: DISJOINT-OR-EQ-LOCAL");
+ Overlap::EqualOrDisjoint
+ } else {
+ // different locals - base case, disjoint
+ debug!("place_element_conflict: DISJOINT-LOCAL");
+ Overlap::Disjoint
}
}
fn place_projection_conflict<'tcx>(
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
- pi1_base: &PlaceBase<'tcx>,
+ pi1_local: &Local,
pi1_proj_base: &[PlaceElem<'tcx>],
pi1_elem: &PlaceElem<'tcx>,
pi2_elem: &PlaceElem<'tcx>,
debug!("place_element_conflict: DISJOINT-OR-EQ-FIELD");
Overlap::EqualOrDisjoint
} else {
- let ty = Place::ty_from(pi1_base, pi1_proj_base, body, tcx).ty;
+ let ty = Place::ty_from(pi1_local, pi1_proj_base, body, tcx).ty;
match ty.kind {
ty::Adt(def, _) if def.is_union() => {
// Different fields of a union, we are basically stuck.
use super::MirBorrowckCtxt;
-use rustc::mir::{Place, PlaceBase, PlaceRef, ProjectionElem, ReadOnlyBodyAndCache};
+use rustc::mir::{Place, PlaceRef, ProjectionElem, ReadOnlyBodyAndCache};
use rustc::ty::{self, TyCtxt};
use rustc_hir as hir;
impl<'cx, 'tcx> IsPrefixOf<'cx, 'tcx> for PlaceRef<'cx, 'tcx> {
fn is_prefix_of(&self, other: PlaceRef<'cx, 'tcx>) -> bool {
- self.base == other.base
+ self.local == other.local
&& self.projection.len() <= other.projection.len()
&& self.projection == &other.projection[..self.projection.len()]
}
'cursor: loop {
match &cursor {
- PlaceRef {
- base: PlaceBase::Local(_),
- projection: [],
- }
- | // search yielded this leaf
- PlaceRef {
- base: PlaceBase::Static(_),
- projection: [],
- } => {
+ PlaceRef { local: _, projection: [] } => {
self.next = None;
return Some(cursor);
}
- PlaceRef {
- base: _,
- projection: [proj_base @ .., elem],
- } => {
+ PlaceRef { local: _, projection: [proj_base @ .., elem] } => {
match elem {
ProjectionElem::Field(_ /*field*/, _ /*ty*/) => {
// FIXME: add union handling
- self.next = Some(PlaceRef {
- base: cursor.base,
- projection: proj_base,
- });
+ self.next =
+ Some(PlaceRef { local: cursor.local, projection: proj_base });
return Some(cursor);
}
- ProjectionElem::Downcast(..) |
- ProjectionElem::Subslice { .. } |
- ProjectionElem::ConstantIndex { .. } |
- ProjectionElem::Index(_) => {
- cursor = PlaceRef {
- base: cursor.base,
- projection: proj_base,
- };
+ ProjectionElem::Downcast(..)
+ | ProjectionElem::Subslice { .. }
+ | ProjectionElem::ConstantIndex { .. }
+ | ProjectionElem::Index(_) => {
+ cursor = PlaceRef { local: cursor.local, projection: proj_base };
continue 'cursor;
}
ProjectionElem::Deref => {
PrefixSet::All => {
// All prefixes: just blindly enqueue the base
// of the projection.
- self.next = Some(PlaceRef {
- base: cursor.base,
- projection: proj_base,
- });
+ self.next =
+ Some(PlaceRef { local: cursor.local, projection: proj_base });
return Some(cursor);
}
PrefixSet::Supporting => {
// derefs, except we stop at the deref of a shared
// reference.
- let ty = Place::ty_from(cursor.base, proj_base, *self.body, self.tcx).ty;
+ let ty = Place::ty_from(cursor.local, proj_base, *self.body, self.tcx).ty;
match ty.kind {
- ty::RawPtr(_) |
- ty::Ref(
- _, /*rgn*/
- _, /*ty*/
- hir::Mutability::Not
- ) => {
+ ty::RawPtr(_) | ty::Ref(_ /*rgn*/, _ /*ty*/, hir::Mutability::Not) => {
// don't continue traversing over derefs of raw pointers or shared
// borrows.
self.next = None;
return Some(cursor);
}
- ty::Ref(
- _, /*rgn*/
- _, /*ty*/
- hir::Mutability::Mut,
- ) => {
- self.next = Some(PlaceRef {
- base: cursor.base,
- projection: proj_base,
- });
+ ty::Ref(_ /*rgn*/, _ /*ty*/, hir::Mutability::Mut) => {
+ self.next =
+ Some(PlaceRef { local: cursor.local, projection: proj_base });
return Some(cursor);
}
ty::Adt(..) if ty.is_box() => {
- self.next = Some(PlaceRef {
- base: cursor.base,
- projection: proj_base,
- });
+ self.next =
+ Some(PlaceRef { local: cursor.local, projection: proj_base });
return Some(cursor);
}
use rustc::infer::canonical::QueryRegionConstraints;
-use rustc::infer::outlives::free_region_map::FreeRegionRelations;
use rustc::infer::region_constraints::GenericKind;
use rustc::infer::InferCtxt;
use rustc::mir::ConstraintCategory;
use rustc::traits::query::outlives_bounds::{self, OutlivesBound};
use rustc::traits::query::type_op::{self, TypeOp};
+use rustc::ty::free_region_map::FreeRegionRelations;
use rustc::ty::{self, RegionVid, Ty};
use rustc_data_structures::transitive_relation::TransitiveRelation;
use rustc_span::DUMMY_SP;
};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_error_codes::*;
+use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_index::vec::{Idx, IndexVec};
);
}
} else {
- if let ty::ConstKind::Unevaluated(def_id, substs) = constant.literal.val {
- if let Err(terr) = self.cx.fully_perform_op(
- location.to_locations(),
- ConstraintCategory::Boring,
- self.cx.param_env.and(type_op::ascribe_user_type::AscribeUserType::new(
- constant.literal.ty,
- def_id,
- UserSubsts { substs, user_self_ty: None },
- )),
- ) {
- span_mirbug!(self, constant, "bad constant type {:?} ({:?})", constant, terr);
+ if let ty::ConstKind::Unevaluated(def_id, substs, promoted) = constant.literal.val {
+ if let Some(promoted) = promoted {
+ let check_err = |verifier: &mut TypeVerifier<'a, 'b, 'tcx>,
+ promoted: &ReadOnlyBodyAndCache<'_, 'tcx>,
+ ty,
+ san_ty| {
+ if let Err(terr) = verifier.cx.eq_types(
+ san_ty,
+ ty,
+ location.to_locations(),
+ ConstraintCategory::Boring,
+ ) {
+ span_mirbug!(
+ verifier,
+ promoted,
+ "bad promoted type ({:?}: {:?}): {:?}",
+ ty,
+ san_ty,
+ terr
+ );
+ };
+ };
+
+ if !self.errors_reported {
+ let promoted_body = self.promoted[promoted];
+ self.sanitize_promoted(promoted_body, location);
+
+ let promoted_ty = promoted_body.return_ty();
+ check_err(self, &promoted_body, ty, promoted_ty);
+ }
+ } else {
+ if let Err(terr) = self.cx.fully_perform_op(
+ location.to_locations(),
+ ConstraintCategory::Boring,
+ self.cx.param_env.and(type_op::ascribe_user_type::AscribeUserType::new(
+ constant.literal.ty,
+ def_id,
+ UserSubsts { substs, user_self_ty: None },
+ )),
+ ) {
+ span_mirbug!(
+ self,
+ constant,
+ "bad constant type {:?} ({:?})",
+ constant,
+ terr
+ );
+ }
}
}
if let ty::FnDef(def_id, substs) = constant.literal.ty.kind {
) -> PlaceTy<'tcx> {
debug!("sanitize_place: {:?}", place);
- let mut place_ty = match &place.base {
- PlaceBase::Local(index) => PlaceTy::from_ty(self.body.local_decls[*index].ty),
- PlaceBase::Static(box Static { kind, ty, def_id }) => {
- let san_ty = self.sanitize_type(place, ty);
- let check_err =
- |verifier: &mut TypeVerifier<'a, 'b, 'tcx>, place: &Place<'tcx>, ty, san_ty| {
- if let Err(terr) = verifier.cx.eq_types(
- san_ty,
- ty,
- location.to_locations(),
- ConstraintCategory::Boring,
- ) {
- span_mirbug!(
- verifier,
- place,
- "bad promoted type ({:?}: {:?}): {:?}",
- ty,
- san_ty,
- terr
- );
- };
- };
- match kind {
- StaticKind::Promoted(promoted, _) => {
- if !self.errors_reported {
- let promoted_body_cache = self.promoted[*promoted];
- self.sanitize_promoted(promoted_body_cache, location);
-
- let promoted_ty = promoted_body_cache.return_ty();
- check_err(self, place, promoted_ty, san_ty);
- }
- }
- StaticKind::Static => {
- let ty = self.tcx().type_of(*def_id);
- let ty = self.cx.normalize(ty, location);
-
- check_err(self, place, ty, san_ty);
- }
- }
- PlaceTy::from_ty(san_ty)
- }
- };
+ let mut place_ty = PlaceTy::from_ty(self.body.local_decls[place.local].ty);
if place.projection.is_empty() {
if let PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) = context {
- let is_promoted = match place.as_ref() {
- PlaceRef {
- base: &PlaceBase::Static(box Static { kind: StaticKind::Promoted(..), .. }),
- projection: &[],
- } => true,
- _ => false,
+ let tcx = self.tcx();
+ let trait_ref = ty::TraitRef {
+ def_id: tcx.lang_items().copy_trait().unwrap(),
+ substs: tcx.mk_substs_trait(place_ty.ty, &[]),
};
- if !is_promoted {
- let tcx = self.tcx();
- let trait_ref = ty::TraitRef {
- def_id: tcx.lang_items().copy_trait().unwrap(),
- substs: tcx.mk_substs_trait(place_ty.ty, &[]),
- };
-
- // To have a `Copy` operand, the type `T` of the
- // value must be `Copy`. Note that we prove that `T: Copy`,
- // rather than using the `is_copy_modulo_regions`
- // test. This is important because
- // `is_copy_modulo_regions` ignores the resulting region
- // obligations and assumes they pass. This can result in
- // bounds from `Copy` impls being unsoundly ignored (e.g.,
- // #29149). Note that we decide to use `Copy` before knowing
- // whether the bounds fully apply: in effect, the rule is
- // that if a value of some type could implement `Copy`, then
- // it must.
- self.cx.prove_trait_ref(
- trait_ref,
- location.to_locations(),
- ConstraintCategory::CopyBound,
- );
- }
+ // To have a `Copy` operand, the type `T` of the
+ // value must be `Copy`. Note that we prove that `T: Copy`,
+ // rather than using the `is_copy_modulo_regions`
+ // test. This is important because
+ // `is_copy_modulo_regions` ignores the resulting region
+ // obligations and assumes they pass. This can result in
+ // bounds from `Copy` impls being unsoundly ignored (e.g.,
+ // #29149). Note that we decide to use `Copy` before knowing
+ // whether the bounds fully apply: in effect, the rule is
+ // that if a value of some type could implement `Copy`, then
+ // it must.
+ self.cx.prove_trait_ref(
+ trait_ref,
+ location.to_locations(),
+ ConstraintCategory::CopyBound,
+ );
}
}
match elem {
ProjectionElem::Deref => {
let tcx = self.infcx.tcx;
- let base_ty = Place::ty_from(&borrowed_place.base, proj_base, body, tcx).ty;
+ let base_ty = Place::ty_from(&borrowed_place.local, proj_base, body, tcx).ty;
debug!("add_reborrow_constraint - base_ty = {:?}", base_ty);
match base_ty.kind {
use rustc::mir::visit::{PlaceContext, Visitor};
-use rustc::mir::{Local, Location, Place, PlaceBase, Statement, StatementKind, TerminatorKind};
+use rustc::mir::{Local, Location, Place, Statement, StatementKind, TerminatorKind};
use rustc_data_structures::fx::FxHashSet;
// be those that were never initialized - we will consider those as being used as
// they will either have been removed by unreachable code optimizations; or linted
// as unused variables.
- if let PlaceBase::Local(local) = into.base {
- let _ = self.never_initialized_mut_locals.remove(&local);
- }
+ self.never_initialized_mut_locals.remove(&into.local);
}
}
fn visit_statement(&mut self, statement: &Statement<'tcx>, _location: Location) {
match &statement.kind {
StatementKind::Assign(box (into, _)) => {
- if let PlaceBase::Local(local) = into.base {
- debug!(
- "visit_statement: statement={:?} local={:?} \
- never_initialized_mut_locals={:?}",
- statement, local, self.never_initialized_mut_locals
- );
- }
+ debug!(
+ "visit_statement: statement={:?} local={:?} \
+ never_initialized_mut_locals={:?}",
+ statement, into.local, self.never_initialized_mut_locals
+ );
self.remove_never_initialized_mut_locals(into);
}
_ => {}
+++ /dev/null
-use crate::build::matches::ArmHasGuard;
-use crate::build::ForGuard::OutsideGuard;
-use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
-use crate::hair::*;
-use rustc::mir::*;
-use rustc_hir as hir;
-use rustc_span::Span;
-
-impl<'a, 'tcx> Builder<'a, 'tcx> {
- pub fn ast_block(
- &mut self,
- destination: &Place<'tcx>,
- block: BasicBlock,
- ast_block: &'tcx hir::Block<'tcx>,
- source_info: SourceInfo,
- ) -> BlockAnd<()> {
- let Block {
- region_scope,
- opt_destruction_scope,
- span,
- stmts,
- expr,
- targeted_by_break,
- safety_mode,
- } = self.hir.mirror(ast_block);
- self.in_opt_scope(opt_destruction_scope.map(|de| (de, source_info)), move |this| {
- this.in_scope((region_scope, source_info), LintLevel::Inherited, move |this| {
- if targeted_by_break {
- // This is a `break`-able block
- let exit_block = this.cfg.start_new_block();
- let block_exit =
- this.in_breakable_scope(None, exit_block, destination.clone(), |this| {
- this.ast_block_stmts(destination, block, span, stmts, expr, safety_mode)
- });
- this.cfg.goto(unpack!(block_exit), source_info, exit_block);
- exit_block.unit()
- } else {
- this.ast_block_stmts(destination, block, span, stmts, expr, safety_mode)
- }
- })
- })
- }
-
- fn ast_block_stmts(
- &mut self,
- destination: &Place<'tcx>,
- mut block: BasicBlock,
- span: Span,
- stmts: Vec<StmtRef<'tcx>>,
- expr: Option<ExprRef<'tcx>>,
- safety_mode: BlockSafety,
- ) -> BlockAnd<()> {
- let this = self;
-
- // This convoluted structure is to avoid using recursion as we walk down a list
- // of statements. Basically, the structure we get back is something like:
- //
- // let x = <init> in {
- // expr1;
- // let y = <init> in {
- // expr2;
- // expr3;
- // ...
- // }
- // }
- //
- // The let bindings are valid till the end of block so all we have to do is to pop all
- // the let-scopes at the end.
- //
- // First we build all the statements in the block.
- let mut let_scope_stack = Vec::with_capacity(8);
- let outer_source_scope = this.source_scope;
- let outer_push_unsafe_count = this.push_unsafe_count;
- let outer_unpushed_unsafe = this.unpushed_unsafe;
- this.update_source_scope_for_safety_mode(span, safety_mode);
-
- let source_info = this.source_info(span);
- for stmt in stmts {
- let Stmt { kind, opt_destruction_scope } = this.hir.mirror(stmt);
- match kind {
- StmtKind::Expr { scope, expr } => {
- this.block_context.push(BlockFrame::Statement { ignores_expr_result: true });
- unpack!(
- block = this.in_opt_scope(
- opt_destruction_scope.map(|de| (de, source_info)),
- |this| {
- let si = (scope, source_info);
- this.in_scope(si, LintLevel::Inherited, |this| {
- let expr = this.hir.mirror(expr);
- this.stmt_expr(block, expr, Some(scope))
- })
- }
- )
- );
- }
- StmtKind::Let { remainder_scope, init_scope, pattern, initializer, lint_level } => {
- let ignores_expr_result =
- if let PatKind::Wild = *pattern.kind { true } else { false };
- this.block_context.push(BlockFrame::Statement { ignores_expr_result });
-
- // Enter the remainder scope, i.e., the bindings' destruction scope.
- this.push_scope((remainder_scope, source_info));
- let_scope_stack.push(remainder_scope);
-
- // Declare the bindings, which may create a source scope.
- let remainder_span =
- remainder_scope.span(this.hir.tcx(), &this.hir.region_scope_tree);
-
- let visibility_scope =
- Some(this.new_source_scope(remainder_span, LintLevel::Inherited, None));
-
- // Evaluate the initializer, if present.
- if let Some(init) = initializer {
- let initializer_span = init.span();
-
- unpack!(
- block = this.in_opt_scope(
- opt_destruction_scope.map(|de| (de, source_info)),
- |this| {
- let scope = (init_scope, source_info);
- this.in_scope(scope, lint_level, |this| {
- this.declare_bindings(
- visibility_scope,
- remainder_span,
- &pattern,
- ArmHasGuard(false),
- Some((None, initializer_span)),
- );
- this.expr_into_pattern(block, pattern, init)
- })
- }
- )
- );
- } else {
- let scope = (init_scope, source_info);
- unpack!(this.in_scope(scope, lint_level, |this| {
- this.declare_bindings(
- visibility_scope,
- remainder_span,
- &pattern,
- ArmHasGuard(false),
- None,
- );
- block.unit()
- }));
-
- debug!("ast_block_stmts: pattern={:?}", pattern);
- this.visit_bindings(
- &pattern,
- UserTypeProjections::none(),
- &mut |this, _, _, _, node, span, _, _| {
- this.storage_live_binding(block, node, span, OutsideGuard);
- this.schedule_drop_for_binding(node, span, OutsideGuard);
- },
- )
- }
-
- // Enter the visibility scope, after evaluating the initializer.
- if let Some(source_scope) = visibility_scope {
- this.source_scope = source_scope;
- }
- }
- }
-
- let popped = this.block_context.pop();
- assert!(popped.map_or(false, |bf| bf.is_statement()));
- }
-
- // Then, the block may have an optional trailing expression which is a “return” value
- // of the block, which is stored into `destination`.
- let tcx = this.hir.tcx();
- let destination_ty = destination.ty(&this.local_decls, tcx).ty;
- if let Some(expr) = expr {
- let tail_result_is_ignored =
- destination_ty.is_unit() || this.block_context.currently_ignores_tail_results();
- this.block_context.push(BlockFrame::TailExpr { tail_result_is_ignored });
-
- unpack!(block = this.into(destination, block, expr));
- let popped = this.block_context.pop();
-
- assert!(popped.map_or(false, |bf| bf.is_tail_expr()));
- } else {
- // If a block has no trailing expression, then it is given an implicit return type.
- // This return type is usually `()`, unless the block is diverging, in which case the
- // return type is `!`. For the unit type, we need to actually return the unit, but in
- // the case of `!`, no return value is required, as the block will never return.
- if destination_ty.is_unit() {
- // We only want to assign an implicit `()` as the return value of the block if the
- // block does not diverge. (Otherwise, we may try to assign a unit to a `!`-type.)
- this.cfg.push_assign_unit(block, source_info, destination);
- }
- }
- // Finally, we pop all the let scopes before exiting out from the scope of block
- // itself.
- for scope in let_scope_stack.into_iter().rev() {
- unpack!(block = this.pop_scope((scope, source_info), block));
- }
- // Restore the original source scope.
- this.source_scope = outer_source_scope;
- this.push_unsafe_count = outer_push_unsafe_count;
- this.unpushed_unsafe = outer_unpushed_unsafe;
- block.unit()
- }
-
- /// If we are changing the safety mode, create a new source scope
- fn update_source_scope_for_safety_mode(&mut self, span: Span, safety_mode: BlockSafety) {
- debug!("update_source_scope_for({:?}, {:?})", span, safety_mode);
- let new_unsafety = match safety_mode {
- BlockSafety::Safe => None,
- BlockSafety::ExplicitUnsafe(hir_id) => {
- assert_eq!(self.push_unsafe_count, 0);
- match self.unpushed_unsafe {
- Safety::Safe => {}
- _ => return,
- }
- self.unpushed_unsafe = Safety::ExplicitUnsafe(hir_id);
- Some(Safety::ExplicitUnsafe(hir_id))
- }
- BlockSafety::PushUnsafe => {
- self.push_unsafe_count += 1;
- Some(Safety::BuiltinUnsafe)
- }
- BlockSafety::PopUnsafe => {
- self.push_unsafe_count = self
- .push_unsafe_count
- .checked_sub(1)
- .unwrap_or_else(|| span_bug!(span, "unsafe count underflow"));
- if self.push_unsafe_count == 0 { Some(self.unpushed_unsafe) } else { None }
- }
- };
-
- if let Some(unsafety) = new_unsafety {
- self.source_scope = self.new_source_scope(span, LintLevel::Inherited, Some(unsafety));
- }
- }
-}
+++ /dev/null
-//! Routines for manipulating the control-flow graph.
-
-use crate::build::CFG;
-use rustc::mir::*;
-
-impl<'tcx> CFG<'tcx> {
- pub fn block_data(&self, blk: BasicBlock) -> &BasicBlockData<'tcx> {
- &self.basic_blocks[blk]
- }
-
- pub fn block_data_mut(&mut self, blk: BasicBlock) -> &mut BasicBlockData<'tcx> {
- &mut self.basic_blocks[blk]
- }
-
- // llvm.org/PR32488 makes this function use an excess of stack space. Mark
- // it as #[inline(never)] to keep rustc's stack use in check.
- #[inline(never)]
- pub fn start_new_block(&mut self) -> BasicBlock {
- self.basic_blocks.push(BasicBlockData::new(None))
- }
-
- pub fn start_new_cleanup_block(&mut self) -> BasicBlock {
- let bb = self.start_new_block();
- self.block_data_mut(bb).is_cleanup = true;
- bb
- }
-
- pub fn push(&mut self, block: BasicBlock, statement: Statement<'tcx>) {
- debug!("push({:?}, {:?})", block, statement);
- self.block_data_mut(block).statements.push(statement);
- }
-
- pub fn push_assign(
- &mut self,
- block: BasicBlock,
- source_info: SourceInfo,
- place: &Place<'tcx>,
- rvalue: Rvalue<'tcx>,
- ) {
- self.push(
- block,
- Statement { source_info, kind: StatementKind::Assign(box (place.clone(), rvalue)) },
- );
- }
-
- pub fn push_assign_constant(
- &mut self,
- block: BasicBlock,
- source_info: SourceInfo,
- temp: &Place<'tcx>,
- constant: Constant<'tcx>,
- ) {
- self.push_assign(block, source_info, temp, Rvalue::Use(Operand::Constant(box constant)));
- }
-
- pub fn push_assign_unit(
- &mut self,
- block: BasicBlock,
- source_info: SourceInfo,
- place: &Place<'tcx>,
- ) {
- self.push_assign(
- block,
- source_info,
- place,
- Rvalue::Aggregate(box AggregateKind::Tuple, vec![]),
- );
- }
-
- pub fn push_fake_read(
- &mut self,
- block: BasicBlock,
- source_info: SourceInfo,
- cause: FakeReadCause,
- place: Place<'tcx>,
- ) {
- let kind = StatementKind::FakeRead(cause, box place);
- let stmt = Statement { source_info, kind };
- self.push(block, stmt);
- }
-
- pub fn terminate(
- &mut self,
- block: BasicBlock,
- source_info: SourceInfo,
- kind: TerminatorKind<'tcx>,
- ) {
- debug!("terminating block {:?} <- {:?}", block, kind);
- debug_assert!(
- self.block_data(block).terminator.is_none(),
- "terminate: block {:?}={:?} already has a terminator set",
- block,
- self.block_data(block)
- );
- self.block_data_mut(block).terminator = Some(Terminator { source_info, kind });
- }
-
- /// In the `origin` block, push a `goto -> target` terminator.
- pub fn goto(&mut self, origin: BasicBlock, source_info: SourceInfo, target: BasicBlock) {
- self.terminate(origin, source_info, TerminatorKind::Goto { target })
- }
-}
+++ /dev/null
-//! See docs in build/expr/mod.rs
-
-use crate::build::Builder;
-use crate::hair::*;
-use rustc::mir::*;
-use rustc::ty::CanonicalUserTypeAnnotation;
-
-impl<'a, 'tcx> Builder<'a, 'tcx> {
- /// Compile `expr`, yielding a compile-time constant. Assumes that
- /// `expr` is a valid compile-time constant!
- pub fn as_constant<M>(&mut self, expr: M) -> Constant<'tcx>
- where
- M: Mirror<'tcx, Output = Expr<'tcx>>,
- {
- let expr = self.hir.mirror(expr);
- self.expr_as_constant(expr)
- }
-
- fn expr_as_constant(&mut self, expr: Expr<'tcx>) -> Constant<'tcx> {
- let this = self;
- let Expr { ty, temp_lifetime: _, span, kind } = expr;
- match kind {
- ExprKind::Scope { region_scope: _, lint_level: _, value } => this.as_constant(value),
- ExprKind::Literal { literal, user_ty } => {
- let user_ty = user_ty.map(|user_ty| {
- this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
- span,
- user_ty,
- inferred_ty: ty,
- })
- });
- assert_eq!(literal.ty, ty);
- Constant { span, user_ty, literal }
- }
- ExprKind::StaticRef { literal, .. } => Constant { span, user_ty: None, literal },
- _ => span_bug!(span, "expression is not a valid constant {:?}", kind),
- }
- }
-}
+++ /dev/null
-//! See docs in build/expr/mod.rs
-
-use crate::build::expr::category::Category;
-use crate::build::{BlockAnd, BlockAndExtension, Builder};
-use crate::hair::*;
-use rustc::middle::region;
-use rustc::mir::*;
-
-impl<'a, 'tcx> Builder<'a, 'tcx> {
- /// Returns an operand suitable for use until the end of the current
- /// scope expression.
- ///
- /// The operand returned from this function will *not be valid* after
- /// an ExprKind::Scope is passed, so please do *not* return it from
- /// functions to avoid bad miscompiles.
- pub fn as_local_operand<M>(&mut self, block: BasicBlock, expr: M) -> BlockAnd<Operand<'tcx>>
- where
- M: Mirror<'tcx, Output = Expr<'tcx>>,
- {
- let local_scope = self.local_scope();
- self.as_operand(block, local_scope, expr)
- }
-
- /// Compile `expr` into a value that can be used as an operand.
- /// If `expr` is a place like `x`, this will introduce a
- /// temporary `tmp = x`, so that we capture the value of `x` at
- /// this time.
- ///
- /// The operand is known to be live until the end of `scope`.
- pub fn as_operand<M>(
- &mut self,
- block: BasicBlock,
- scope: Option<region::Scope>,
- expr: M,
- ) -> BlockAnd<Operand<'tcx>>
- where
- M: Mirror<'tcx, Output = Expr<'tcx>>,
- {
- let expr = self.hir.mirror(expr);
- self.expr_as_operand(block, scope, expr)
- }
-
- fn expr_as_operand(
- &mut self,
- mut block: BasicBlock,
- scope: Option<region::Scope>,
- expr: Expr<'tcx>,
- ) -> BlockAnd<Operand<'tcx>> {
- debug!("expr_as_operand(block={:?}, expr={:?})", block, expr);
- let this = self;
-
- if let ExprKind::Scope { region_scope, lint_level, value } = expr.kind {
- let source_info = this.source_info(expr.span);
- let region_scope = (region_scope, source_info);
- return this
- .in_scope(region_scope, lint_level, |this| this.as_operand(block, scope, value));
- }
-
- let category = Category::of(&expr.kind).unwrap();
- debug!("expr_as_operand: category={:?} for={:?}", category, expr.kind);
- match category {
- Category::Constant => {
- let constant = this.as_constant(expr);
- block.and(Operand::Constant(box constant))
- }
- Category::Place | Category::Rvalue(..) => {
- let operand = unpack!(block = this.as_temp(block, scope, expr, Mutability::Mut));
- block.and(Operand::Move(Place::from(operand)))
- }
- }
- }
-}
+++ /dev/null
-//! See docs in build/expr/mod.rs
-
-use crate::build::expr::category::Category;
-use crate::build::ForGuard::{OutsideGuard, RefWithinGuard};
-use crate::build::{BlockAnd, BlockAndExtension, Builder};
-use crate::hair::*;
-use rustc::middle::region;
-use rustc::mir::interpret::PanicInfo::BoundsCheck;
-use rustc::mir::*;
-use rustc::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt, Variance};
-use rustc_span::Span;
-
-use rustc_index::vec::Idx;
-
-/// `PlaceBuilder` is used to create places during MIR construction. It allows you to "build up" a
-/// place by pushing more and more projections onto the end, and then convert the final set into a
-/// place using the `into_place` method.
-///
-/// This is used internally when building a place for an expression like `a.b.c`. The fields `b`
-/// and `c` can be progressively pushed onto the place builder that is created when converting `a`.
-#[derive(Clone)]
-struct PlaceBuilder<'tcx> {
- base: PlaceBase<'tcx>,
- projection: Vec<PlaceElem<'tcx>>,
-}
-
-impl PlaceBuilder<'tcx> {
- fn into_place(self, tcx: TyCtxt<'tcx>) -> Place<'tcx> {
- Place { base: self.base, projection: tcx.intern_place_elems(&self.projection) }
- }
-
- fn field(self, f: Field, ty: Ty<'tcx>) -> Self {
- self.project(PlaceElem::Field(f, ty))
- }
-
- fn deref(self) -> Self {
- self.project(PlaceElem::Deref)
- }
-
- fn index(self, index: Local) -> Self {
- self.project(PlaceElem::Index(index))
- }
-
- fn project(mut self, elem: PlaceElem<'tcx>) -> Self {
- self.projection.push(elem);
- self
- }
-}
-
-impl From<Local> for PlaceBuilder<'tcx> {
- fn from(local: Local) -> Self {
- Self { base: local.into(), projection: Vec::new() }
- }
-}
-
-impl From<PlaceBase<'tcx>> for PlaceBuilder<'tcx> {
- fn from(base: PlaceBase<'tcx>) -> Self {
- Self { base, projection: Vec::new() }
- }
-}
-
-impl<'a, 'tcx> Builder<'a, 'tcx> {
- /// Compile `expr`, yielding a place that we can move from etc.
- ///
- /// WARNING: Any user code might:
- /// * Invalidate any slice bounds checks performed.
- /// * Change the address that this `Place` refers to.
- /// * Modify the memory that this place refers to.
- /// * Invalidate the memory that this place refers to, this will be caught
- /// by borrow checking.
- ///
- /// Extra care is needed if any user code is allowed to run between calling
- /// this method and using it, as is the case for `match` and index
- /// expressions.
- pub fn as_place<M>(&mut self, mut block: BasicBlock, expr: M) -> BlockAnd<Place<'tcx>>
- where
- M: Mirror<'tcx, Output = Expr<'tcx>>,
- {
- let place_builder = unpack!(block = self.as_place_builder(block, expr));
- block.and(place_builder.into_place(self.hir.tcx()))
- }
-
- /// This is used when constructing a compound `Place`, so that we can avoid creating
- /// intermediate `Place` values until we know the full set of projections.
- fn as_place_builder<M>(&mut self, block: BasicBlock, expr: M) -> BlockAnd<PlaceBuilder<'tcx>>
- where
- M: Mirror<'tcx, Output = Expr<'tcx>>,
- {
- let expr = self.hir.mirror(expr);
- self.expr_as_place(block, expr, Mutability::Mut, None)
- }
-
- /// Compile `expr`, yielding a place that we can move from etc.
- /// Mutability note: The caller of this method promises only to read from the resulting
- /// place. The place itself may or may not be mutable:
- /// * If this expr is a place expr like a.b, then we will return that place.
- /// * Otherwise, a temporary is created: in that event, it will be an immutable temporary.
- pub fn as_read_only_place<M>(&mut self, mut block: BasicBlock, expr: M) -> BlockAnd<Place<'tcx>>
- where
- M: Mirror<'tcx, Output = Expr<'tcx>>,
- {
- let place_builder = unpack!(block = self.as_read_only_place_builder(block, expr));
- block.and(place_builder.into_place(self.hir.tcx()))
- }
-
- /// This is used when constructing a compound `Place`, so that we can avoid creating
- /// intermediate `Place` values until we know the full set of projections.
- /// Mutability note: The caller of this method promises only to read from the resulting
- /// place. The place itself may or may not be mutable:
- /// * If this expr is a place expr like a.b, then we will return that place.
- /// * Otherwise, a temporary is created: in that event, it will be an immutable temporary.
- fn as_read_only_place_builder<M>(
- &mut self,
- block: BasicBlock,
- expr: M,
- ) -> BlockAnd<PlaceBuilder<'tcx>>
- where
- M: Mirror<'tcx, Output = Expr<'tcx>>,
- {
- let expr = self.hir.mirror(expr);
- self.expr_as_place(block, expr, Mutability::Not, None)
- }
-
- fn expr_as_place(
- &mut self,
- mut block: BasicBlock,
- expr: Expr<'tcx>,
- mutability: Mutability,
- fake_borrow_temps: Option<&mut Vec<Local>>,
- ) -> BlockAnd<PlaceBuilder<'tcx>> {
- debug!("expr_as_place(block={:?}, expr={:?}, mutability={:?})", block, expr, mutability);
-
- let this = self;
- let expr_span = expr.span;
- let source_info = this.source_info(expr_span);
- match expr.kind {
- ExprKind::Scope { region_scope, lint_level, value } => {
- this.in_scope((region_scope, source_info), lint_level, |this| {
- let value = this.hir.mirror(value);
- this.expr_as_place(block, value, mutability, fake_borrow_temps)
- })
- }
- ExprKind::Field { lhs, name } => {
- let lhs = this.hir.mirror(lhs);
- let place_builder =
- unpack!(block = this.expr_as_place(block, lhs, mutability, fake_borrow_temps,));
- block.and(place_builder.field(name, expr.ty))
- }
- ExprKind::Deref { arg } => {
- let arg = this.hir.mirror(arg);
- let place_builder =
- unpack!(block = this.expr_as_place(block, arg, mutability, fake_borrow_temps,));
- block.and(place_builder.deref())
- }
- ExprKind::Index { lhs, index } => this.lower_index_expression(
- block,
- lhs,
- index,
- mutability,
- fake_borrow_temps,
- expr.temp_lifetime,
- expr_span,
- source_info,
- ),
- ExprKind::SelfRef => block.and(PlaceBuilder::from(Local::new(1))),
- ExprKind::VarRef { id } => {
- let place_builder = if this.is_bound_var_in_guard(id) {
- let index = this.var_local_id(id, RefWithinGuard);
- PlaceBuilder::from(index).deref()
- } else {
- let index = this.var_local_id(id, OutsideGuard);
- PlaceBuilder::from(index)
- };
- block.and(place_builder)
- }
-
- ExprKind::PlaceTypeAscription { source, user_ty } => {
- let source = this.hir.mirror(source);
- let place_builder = unpack!(
- block = this.expr_as_place(block, source, mutability, fake_borrow_temps,)
- );
- if let Some(user_ty) = user_ty {
- let annotation_index =
- this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
- span: source_info.span,
- user_ty,
- inferred_ty: expr.ty,
- });
-
- let place = place_builder.clone().into_place(this.hir.tcx());
- this.cfg.push(
- block,
- Statement {
- source_info,
- kind: StatementKind::AscribeUserType(
- box (
- place,
- UserTypeProjection { base: annotation_index, projs: vec![] },
- ),
- Variance::Invariant,
- ),
- },
- );
- }
- block.and(place_builder)
- }
- ExprKind::ValueTypeAscription { source, user_ty } => {
- let source = this.hir.mirror(source);
- let temp =
- unpack!(block = this.as_temp(block, source.temp_lifetime, source, mutability));
- if let Some(user_ty) = user_ty {
- let annotation_index =
- this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
- span: source_info.span,
- user_ty,
- inferred_ty: expr.ty,
- });
- this.cfg.push(
- block,
- Statement {
- source_info,
- kind: StatementKind::AscribeUserType(
- box (
- Place::from(temp.clone()),
- UserTypeProjection { base: annotation_index, projs: vec![] },
- ),
- Variance::Invariant,
- ),
- },
- );
- }
- block.and(PlaceBuilder::from(temp))
- }
-
- ExprKind::Array { .. }
- | ExprKind::Tuple { .. }
- | ExprKind::Adt { .. }
- | ExprKind::Closure { .. }
- | ExprKind::Unary { .. }
- | ExprKind::Binary { .. }
- | ExprKind::LogicalOp { .. }
- | ExprKind::Box { .. }
- | ExprKind::Cast { .. }
- | ExprKind::Use { .. }
- | ExprKind::NeverToAny { .. }
- | ExprKind::Pointer { .. }
- | ExprKind::Repeat { .. }
- | ExprKind::Borrow { .. }
- | ExprKind::AddressOf { .. }
- | ExprKind::Match { .. }
- | ExprKind::Loop { .. }
- | ExprKind::Block { .. }
- | ExprKind::Assign { .. }
- | ExprKind::AssignOp { .. }
- | ExprKind::Break { .. }
- | ExprKind::Continue { .. }
- | ExprKind::Return { .. }
- | ExprKind::Literal { .. }
- | ExprKind::StaticRef { .. }
- | ExprKind::InlineAsm { .. }
- | ExprKind::Yield { .. }
- | ExprKind::Call { .. } => {
- // these are not places, so we need to make a temporary.
- debug_assert!(match Category::of(&expr.kind) {
- Some(Category::Place) => false,
- _ => true,
- });
- let temp =
- unpack!(block = this.as_temp(block, expr.temp_lifetime, expr, mutability));
- block.and(PlaceBuilder::from(temp))
- }
- }
- }
-
- /// Lower an index expression
- ///
- /// This has two complications;
- ///
- /// * We need to do a bounds check.
- /// * We need to ensure that the bounds check can't be invalidated using an
- /// expression like `x[1][{x = y; 2}]`. We use fake borrows here to ensure
- /// that this is the case.
- fn lower_index_expression(
- &mut self,
- mut block: BasicBlock,
- base: ExprRef<'tcx>,
- index: ExprRef<'tcx>,
- mutability: Mutability,
- fake_borrow_temps: Option<&mut Vec<Local>>,
- temp_lifetime: Option<region::Scope>,
- expr_span: Span,
- source_info: SourceInfo,
- ) -> BlockAnd<PlaceBuilder<'tcx>> {
- let lhs = self.hir.mirror(base);
-
- let base_fake_borrow_temps = &mut Vec::new();
- let is_outermost_index = fake_borrow_temps.is_none();
- let fake_borrow_temps = fake_borrow_temps.unwrap_or(base_fake_borrow_temps);
-
- let base_place =
- unpack!(block = self.expr_as_place(block, lhs, mutability, Some(fake_borrow_temps),));
-
- // Making this a *fresh* temporary means we do not have to worry about
- // the index changing later: Nothing will ever change this temporary.
- // The "retagging" transformation (for Stacked Borrows) relies on this.
- let idx = unpack!(block = self.as_temp(block, temp_lifetime, index, Mutability::Not,));
-
- block = self.bounds_check(
- block,
- base_place.clone().into_place(self.hir.tcx()),
- idx,
- expr_span,
- source_info,
- );
-
- if is_outermost_index {
- self.read_fake_borrows(block, fake_borrow_temps, source_info)
- } else {
- self.add_fake_borrows_of_base(
- &base_place,
- block,
- fake_borrow_temps,
- expr_span,
- source_info,
- );
- }
-
- block.and(base_place.index(idx))
- }
-
- fn bounds_check(
- &mut self,
- block: BasicBlock,
- slice: Place<'tcx>,
- index: Local,
- expr_span: Span,
- source_info: SourceInfo,
- ) -> BasicBlock {
- let usize_ty = self.hir.usize_ty();
- let bool_ty = self.hir.bool_ty();
- // bounds check:
- let len = self.temp(usize_ty, expr_span);
- let lt = self.temp(bool_ty, expr_span);
-
- // len = len(slice)
- self.cfg.push_assign(block, source_info, &len, Rvalue::Len(slice));
- // lt = idx < len
- self.cfg.push_assign(
- block,
- source_info,
- <,
- Rvalue::BinaryOp(
- BinOp::Lt,
- Operand::Copy(Place::from(index)),
- Operand::Copy(len.clone()),
- ),
- );
- let msg = BoundsCheck { len: Operand::Move(len), index: Operand::Copy(Place::from(index)) };
- // assert!(lt, "...")
- self.assert(block, Operand::Move(lt), true, msg, expr_span)
- }
-
- fn add_fake_borrows_of_base(
- &mut self,
- base_place: &PlaceBuilder<'tcx>,
- block: BasicBlock,
- fake_borrow_temps: &mut Vec<Local>,
- expr_span: Span,
- source_info: SourceInfo,
- ) {
- let tcx = self.hir.tcx();
- let place_ty =
- Place::ty_from(&base_place.base, &base_place.projection, &self.local_decls, tcx);
- if let ty::Slice(_) = place_ty.ty.kind {
- // We need to create fake borrows to ensure that the bounds
- // check that we just did stays valid. Since we can't assign to
- // unsized values, we only need to ensure that none of the
- // pointers in the base place are modified.
- for (idx, elem) in base_place.projection.iter().enumerate().rev() {
- match elem {
- ProjectionElem::Deref => {
- let fake_borrow_deref_ty = Place::ty_from(
- &base_place.base,
- &base_place.projection[..idx],
- &self.local_decls,
- tcx,
- )
- .ty;
- let fake_borrow_ty =
- tcx.mk_imm_ref(tcx.lifetimes.re_erased, fake_borrow_deref_ty);
- let fake_borrow_temp =
- self.local_decls.push(LocalDecl::new_temp(fake_borrow_ty, expr_span));
- let projection = tcx.intern_place_elems(&base_place.projection[..idx]);
- self.cfg.push_assign(
- block,
- source_info,
- &fake_borrow_temp.into(),
- Rvalue::Ref(
- tcx.lifetimes.re_erased,
- BorrowKind::Shallow,
- Place { base: base_place.base.clone(), projection },
- ),
- );
- fake_borrow_temps.push(fake_borrow_temp);
- }
- ProjectionElem::Index(_) => {
- let index_ty = Place::ty_from(
- &base_place.base,
- &base_place.projection[..idx],
- &self.local_decls,
- tcx,
- );
- match index_ty.ty.kind {
- // The previous index expression has already
- // done any index expressions needed here.
- ty::Slice(_) => break,
- ty::Array(..) => (),
- _ => bug!("unexpected index base"),
- }
- }
- ProjectionElem::Field(..)
- | ProjectionElem::Downcast(..)
- | ProjectionElem::ConstantIndex { .. }
- | ProjectionElem::Subslice { .. } => (),
- }
- }
- }
- }
-
- fn read_fake_borrows(
- &mut self,
- bb: BasicBlock,
- fake_borrow_temps: &mut Vec<Local>,
- source_info: SourceInfo,
- ) {
- // All indexes have been evaluated now, read all of the
- // fake borrows so that they are live across those index
- // expressions.
- for temp in fake_borrow_temps {
- self.cfg.push_fake_read(bb, source_info, FakeReadCause::ForIndex, Place::from(*temp));
- }
- }
-}
+++ /dev/null
-//! See docs in `build/expr/mod.rs`.
-
-use rustc_index::vec::Idx;
-
-use crate::build::expr::category::{Category, RvalueFunc};
-use crate::build::{BlockAnd, BlockAndExtension, Builder};
-use crate::hair::*;
-use rustc::middle::region;
-use rustc::mir::interpret::PanicInfo;
-use rustc::mir::*;
-use rustc::ty::{self, Ty, UpvarSubsts};
-use rustc_span::Span;
-
-impl<'a, 'tcx> Builder<'a, 'tcx> {
- /// Returns an rvalue suitable for use until the end of the current
- /// scope expression.
- ///
- /// The operand returned from this function will *not be valid* after
- /// an ExprKind::Scope is passed, so please do *not* return it from
- /// functions to avoid bad miscompiles.
- pub fn as_local_rvalue<M>(&mut self, block: BasicBlock, expr: M) -> BlockAnd<Rvalue<'tcx>>
- where
- M: Mirror<'tcx, Output = Expr<'tcx>>,
- {
- let local_scope = self.local_scope();
- self.as_rvalue(block, local_scope, expr)
- }
-
- /// Compile `expr`, yielding an rvalue.
- fn as_rvalue<M>(
- &mut self,
- block: BasicBlock,
- scope: Option<region::Scope>,
- expr: M,
- ) -> BlockAnd<Rvalue<'tcx>>
- where
- M: Mirror<'tcx, Output = Expr<'tcx>>,
- {
- let expr = self.hir.mirror(expr);
- self.expr_as_rvalue(block, scope, expr)
- }
-
- fn expr_as_rvalue(
- &mut self,
- mut block: BasicBlock,
- scope: Option<region::Scope>,
- expr: Expr<'tcx>,
- ) -> BlockAnd<Rvalue<'tcx>> {
- debug!("expr_as_rvalue(block={:?}, scope={:?}, expr={:?})", block, scope, expr);
-
- let this = self;
- let expr_span = expr.span;
- let source_info = this.source_info(expr_span);
-
- match expr.kind {
- ExprKind::Scope { region_scope, lint_level, value } => {
- let region_scope = (region_scope, source_info);
- this.in_scope(region_scope, lint_level, |this| this.as_rvalue(block, scope, value))
- }
- ExprKind::Repeat { value, count } => {
- let value_operand = unpack!(block = this.as_operand(block, scope, value));
- block.and(Rvalue::Repeat(value_operand, count))
- }
- ExprKind::Binary { op, lhs, rhs } => {
- let lhs = unpack!(block = this.as_operand(block, scope, lhs));
- let rhs = unpack!(block = this.as_operand(block, scope, rhs));
- this.build_binary_op(block, op, expr_span, expr.ty, lhs, rhs)
- }
- ExprKind::Unary { op, arg } => {
- let arg = unpack!(block = this.as_operand(block, scope, arg));
- // Check for -MIN on signed integers
- if this.hir.check_overflow() && op == UnOp::Neg && expr.ty.is_signed() {
- let bool_ty = this.hir.bool_ty();
-
- let minval = this.minval_literal(expr_span, expr.ty);
- let is_min = this.temp(bool_ty, expr_span);
-
- this.cfg.push_assign(
- block,
- source_info,
- &is_min,
- Rvalue::BinaryOp(BinOp::Eq, arg.to_copy(), minval),
- );
-
- block = this.assert(
- block,
- Operand::Move(is_min),
- false,
- PanicInfo::OverflowNeg,
- expr_span,
- );
- }
- block.and(Rvalue::UnaryOp(op, arg))
- }
- ExprKind::Box { value } => {
- let value = this.hir.mirror(value);
- // The `Box<T>` temporary created here is not a part of the HIR,
- // and therefore is not considered during generator OIBIT
- // determination. See the comment about `box` at `yield_in_scope`.
- let result = this.local_decls.push(LocalDecl::new_internal(expr.ty, expr_span));
- this.cfg.push(
- block,
- Statement { source_info, kind: StatementKind::StorageLive(result) },
- );
- if let Some(scope) = scope {
- // schedule a shallow free of that memory, lest we unwind:
- this.schedule_drop_storage_and_value(expr_span, scope, result);
- }
-
- // malloc some memory of suitable type (thus far, uninitialized):
- let box_ = Rvalue::NullaryOp(NullOp::Box, value.ty);
- this.cfg.push_assign(block, source_info, &Place::from(result), box_);
-
- // initialize the box contents:
- unpack!(
- block = this.into(
- &this.hir.tcx().mk_place_deref(Place::from(result)),
- block,
- value
- )
- );
- block.and(Rvalue::Use(Operand::Move(Place::from(result))))
- }
- ExprKind::Cast { source } => {
- let source = unpack!(block = this.as_operand(block, scope, source));
- block.and(Rvalue::Cast(CastKind::Misc, source, expr.ty))
- }
- ExprKind::Pointer { cast, source } => {
- let source = unpack!(block = this.as_operand(block, scope, source));
- block.and(Rvalue::Cast(CastKind::Pointer(cast), source, expr.ty))
- }
- ExprKind::Array { fields } => {
- // (*) We would (maybe) be closer to codegen if we
- // handled this and other aggregate cases via
- // `into()`, not `as_rvalue` -- in that case, instead
- // of generating
- //
- // let tmp1 = ...1;
- // let tmp2 = ...2;
- // dest = Rvalue::Aggregate(Foo, [tmp1, tmp2])
- //
- // we could just generate
- //
- // dest.f = ...1;
- // dest.g = ...2;
- //
- // The problem is that then we would need to:
- //
- // (a) have a more complex mechanism for handling
- // partial cleanup;
- // (b) distinguish the case where the type `Foo` has a
- // destructor, in which case creating an instance
- // as a whole "arms" the destructor, and you can't
- // write individual fields; and,
- // (c) handle the case where the type Foo has no
- // fields. We don't want `let x: ();` to compile
- // to the same MIR as `let x = ();`.
-
- // first process the set of fields
- let el_ty = expr.ty.sequence_element_type(this.hir.tcx());
- let fields: Vec<_> = fields
- .into_iter()
- .map(|f| unpack!(block = this.as_operand(block, scope, f)))
- .collect();
-
- block.and(Rvalue::Aggregate(box AggregateKind::Array(el_ty), fields))
- }
- ExprKind::Tuple { fields } => {
- // see (*) above
- // first process the set of fields
- let fields: Vec<_> = fields
- .into_iter()
- .map(|f| unpack!(block = this.as_operand(block, scope, f)))
- .collect();
-
- block.and(Rvalue::Aggregate(box AggregateKind::Tuple, fields))
- }
- ExprKind::Closure { closure_id, substs, upvars, movability } => {
- // see (*) above
- let operands: Vec<_> = upvars
- .into_iter()
- .map(|upvar| {
- let upvar = this.hir.mirror(upvar);
- match Category::of(&upvar.kind) {
- // Use as_place to avoid creating a temporary when
- // moving a variable into a closure, so that
- // borrowck knows which variables to mark as being
- // used as mut. This is OK here because the upvar
- // expressions have no side effects and act on
- // disjoint places.
- // This occurs when capturing by copy/move, while
- // by reference captures use as_operand
- Some(Category::Place) => {
- let place = unpack!(block = this.as_place(block, upvar));
- this.consume_by_copy_or_move(place)
- }
- _ => {
- // Turn mutable borrow captures into unique
- // borrow captures when capturing an immutable
- // variable. This is sound because the mutation
- // that caused the capture will cause an error.
- match upvar.kind {
- ExprKind::Borrow {
- borrow_kind:
- BorrowKind::Mut { allow_two_phase_borrow: false },
- arg,
- } => unpack!(
- block = this.limit_capture_mutability(
- upvar.span, upvar.ty, scope, block, arg,
- )
- ),
- _ => unpack!(block = this.as_operand(block, scope, upvar)),
- }
- }
- }
- })
- .collect();
- let result = match substs {
- UpvarSubsts::Generator(substs) => {
- // We implicitly set the discriminant to 0. See
- // librustc_mir/transform/deaggregator.rs for details.
- let movability = movability.unwrap();
- box AggregateKind::Generator(closure_id, substs, movability)
- }
- UpvarSubsts::Closure(substs) => box AggregateKind::Closure(closure_id, substs),
- };
- block.and(Rvalue::Aggregate(result, operands))
- }
- ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => {
- block = unpack!(this.stmt_expr(block, expr, None));
- block.and(this.unit_rvalue())
- }
- ExprKind::Yield { value } => {
- let value = unpack!(block = this.as_operand(block, scope, value));
- let resume = this.cfg.start_new_block();
- let cleanup = this.generator_drop_cleanup();
- this.cfg.terminate(
- block,
- source_info,
- TerminatorKind::Yield { value: value, resume: resume, drop: cleanup },
- );
- resume.and(this.unit_rvalue())
- }
- ExprKind::Literal { .. }
- | ExprKind::StaticRef { .. }
- | ExprKind::Block { .. }
- | ExprKind::Match { .. }
- | ExprKind::NeverToAny { .. }
- | ExprKind::Use { .. }
- | ExprKind::Borrow { .. }
- | ExprKind::AddressOf { .. }
- | ExprKind::Adt { .. }
- | ExprKind::Loop { .. }
- | ExprKind::LogicalOp { .. }
- | ExprKind::Call { .. }
- | ExprKind::Field { .. }
- | ExprKind::Deref { .. }
- | ExprKind::Index { .. }
- | ExprKind::VarRef { .. }
- | ExprKind::SelfRef
- | ExprKind::Break { .. }
- | ExprKind::Continue { .. }
- | ExprKind::Return { .. }
- | ExprKind::InlineAsm { .. }
- | ExprKind::PlaceTypeAscription { .. }
- | ExprKind::ValueTypeAscription { .. } => {
- // these do not have corresponding `Rvalue` variants,
- // so make an operand and then return that
- debug_assert!(match Category::of(&expr.kind) {
- Some(Category::Rvalue(RvalueFunc::AsRvalue)) => false,
- _ => true,
- });
- let operand = unpack!(block = this.as_operand(block, scope, expr));
- block.and(Rvalue::Use(operand))
- }
- }
- }
-
- pub fn build_binary_op(
- &mut self,
- mut block: BasicBlock,
- op: BinOp,
- span: Span,
- ty: Ty<'tcx>,
- lhs: Operand<'tcx>,
- rhs: Operand<'tcx>,
- ) -> BlockAnd<Rvalue<'tcx>> {
- let source_info = self.source_info(span);
- let bool_ty = self.hir.bool_ty();
- if self.hir.check_overflow() && op.is_checkable() && ty.is_integral() {
- let result_tup = self.hir.tcx().intern_tup(&[ty, bool_ty]);
- let result_value = self.temp(result_tup, span);
-
- self.cfg.push_assign(
- block,
- source_info,
- &result_value,
- Rvalue::CheckedBinaryOp(op, lhs, rhs),
- );
- let val_fld = Field::new(0);
- let of_fld = Field::new(1);
-
- let tcx = self.hir.tcx();
- let val = tcx.mk_place_field(result_value.clone(), val_fld, ty);
- let of = tcx.mk_place_field(result_value, of_fld, bool_ty);
-
- let err = PanicInfo::Overflow(op);
-
- block = self.assert(block, Operand::Move(of), false, err, span);
-
- block.and(Rvalue::Use(Operand::Move(val)))
- } else {
- if ty.is_integral() && (op == BinOp::Div || op == BinOp::Rem) {
- // Checking division and remainder is more complex, since we 1. always check
- // and 2. there are two possible failure cases, divide-by-zero and overflow.
-
- let zero_err = if op == BinOp::Div {
- PanicInfo::DivisionByZero
- } else {
- PanicInfo::RemainderByZero
- };
- let overflow_err = PanicInfo::Overflow(op);
-
- // Check for / 0
- let is_zero = self.temp(bool_ty, span);
- let zero = self.zero_literal(span, ty);
- self.cfg.push_assign(
- block,
- source_info,
- &is_zero,
- Rvalue::BinaryOp(BinOp::Eq, rhs.to_copy(), zero),
- );
-
- block = self.assert(block, Operand::Move(is_zero), false, zero_err, span);
-
- // We only need to check for the overflow in one case:
- // MIN / -1, and only for signed values.
- if ty.is_signed() {
- let neg_1 = self.neg_1_literal(span, ty);
- let min = self.minval_literal(span, ty);
-
- let is_neg_1 = self.temp(bool_ty, span);
- let is_min = self.temp(bool_ty, span);
- let of = self.temp(bool_ty, span);
-
- // this does (rhs == -1) & (lhs == MIN). It could short-circuit instead
-
- self.cfg.push_assign(
- block,
- source_info,
- &is_neg_1,
- Rvalue::BinaryOp(BinOp::Eq, rhs.to_copy(), neg_1),
- );
- self.cfg.push_assign(
- block,
- source_info,
- &is_min,
- Rvalue::BinaryOp(BinOp::Eq, lhs.to_copy(), min),
- );
-
- let is_neg_1 = Operand::Move(is_neg_1);
- let is_min = Operand::Move(is_min);
- self.cfg.push_assign(
- block,
- source_info,
- &of,
- Rvalue::BinaryOp(BinOp::BitAnd, is_neg_1, is_min),
- );
-
- block = self.assert(block, Operand::Move(of), false, overflow_err, span);
- }
- }
-
- block.and(Rvalue::BinaryOp(op, lhs, rhs))
- }
- }
-
- fn limit_capture_mutability(
- &mut self,
- upvar_span: Span,
- upvar_ty: Ty<'tcx>,
- temp_lifetime: Option<region::Scope>,
- mut block: BasicBlock,
- arg: ExprRef<'tcx>,
- ) -> BlockAnd<Operand<'tcx>> {
- let this = self;
-
- let source_info = this.source_info(upvar_span);
- let temp = this.local_decls.push(LocalDecl::new_temp(upvar_ty, upvar_span));
-
- this.cfg.push(block, Statement { source_info, kind: StatementKind::StorageLive(temp) });
-
- let arg_place = unpack!(block = this.as_place(block, arg));
-
- let mutability = match arg_place.as_ref() {
- PlaceRef { base: &PlaceBase::Local(local), projection: &[] } => {
- this.local_decls[local].mutability
- }
- PlaceRef { base: &PlaceBase::Local(local), projection: &[ProjectionElem::Deref] } => {
- debug_assert!(
- this.local_decls[local].is_ref_for_guard(),
- "Unexpected capture place",
- );
- this.local_decls[local].mutability
- }
- PlaceRef {
- ref base,
- projection: &[ref proj_base @ .., ProjectionElem::Field(upvar_index, _)],
- }
- | PlaceRef {
- ref base,
- projection:
- &[ref proj_base @ .., ProjectionElem::Field(upvar_index, _), ProjectionElem::Deref],
- } => {
- let place = PlaceRef { base, projection: proj_base };
-
- // Not projected from the implicit `self` in a closure.
- debug_assert!(
- match place.local_or_deref_local() {
- Some(local) => local == Local::new(1),
- None => false,
- },
- "Unexpected capture place"
- );
- // Not in a closure
- debug_assert!(
- this.upvar_mutbls.len() > upvar_index.index(),
- "Unexpected capture place"
- );
- this.upvar_mutbls[upvar_index.index()]
- }
- _ => bug!("Unexpected capture place"),
- };
-
- let borrow_kind = match mutability {
- Mutability::Not => BorrowKind::Unique,
- Mutability::Mut => BorrowKind::Mut { allow_two_phase_borrow: false },
- };
-
- this.cfg.push_assign(
- block,
- source_info,
- &Place::from(temp),
- Rvalue::Ref(this.hir.tcx().lifetimes.re_erased, borrow_kind, arg_place),
- );
-
- // In constants, temp_lifetime is None. We should not need to drop
- // anything because no values with a destructor can be created in
- // a constant at this time, even if the type may need dropping.
- if let Some(temp_lifetime) = temp_lifetime {
- this.schedule_drop_storage_and_value(upvar_span, temp_lifetime, temp);
- }
-
- block.and(Operand::Move(Place::from(temp)))
- }
-
- // Helper to get a `-1` value of the appropriate type
- fn neg_1_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> {
- let param_ty = ty::ParamEnv::empty().and(ty);
- let bits = self.hir.tcx().layout_of(param_ty).unwrap().size.bits();
- let n = (!0u128) >> (128 - bits);
- let literal = ty::Const::from_bits(self.hir.tcx(), n, param_ty);
-
- self.literal_operand(span, literal)
- }
-
- // Helper to get the minimum value of the appropriate type
- fn minval_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> {
- assert!(ty.is_signed());
- let param_ty = ty::ParamEnv::empty().and(ty);
- let bits = self.hir.tcx().layout_of(param_ty).unwrap().size.bits();
- let n = 1 << (bits - 1);
- let literal = ty::Const::from_bits(self.hir.tcx(), n, param_ty);
-
- self.literal_operand(span, literal)
- }
-}
+++ /dev/null
-//! See docs in build/expr/mod.rs
-
-use crate::build::scope::DropKind;
-use crate::build::{BlockAnd, BlockAndExtension, Builder};
-use crate::hair::*;
-use rustc::middle::region;
-use rustc::mir::*;
-use rustc_hir as hir;
-use rustc_span::symbol::sym;
-
-impl<'a, 'tcx> Builder<'a, 'tcx> {
- /// Compile `expr` into a fresh temporary. This is used when building
- /// up rvalues so as to freeze the value that will be consumed.
- pub fn as_temp<M>(
- &mut self,
- block: BasicBlock,
- temp_lifetime: Option<region::Scope>,
- expr: M,
- mutability: Mutability,
- ) -> BlockAnd<Local>
- where
- M: Mirror<'tcx, Output = Expr<'tcx>>,
- {
- let expr = self.hir.mirror(expr);
- self.expr_as_temp(block, temp_lifetime, expr, mutability)
- }
-
- fn expr_as_temp(
- &mut self,
- mut block: BasicBlock,
- temp_lifetime: Option<region::Scope>,
- expr: Expr<'tcx>,
- mutability: Mutability,
- ) -> BlockAnd<Local> {
- debug!(
- "expr_as_temp(block={:?}, temp_lifetime={:?}, expr={:?}, mutability={:?})",
- block, temp_lifetime, expr, mutability
- );
- let this = self;
-
- let expr_span = expr.span;
- let source_info = this.source_info(expr_span);
- if let ExprKind::Scope { region_scope, lint_level, value } = expr.kind {
- return this.in_scope((region_scope, source_info), lint_level, |this| {
- this.as_temp(block, temp_lifetime, value, mutability)
- });
- }
-
- let expr_ty = expr.ty;
- let temp = {
- let mut local_decl = LocalDecl::new_temp(expr_ty, expr_span);
- if mutability == Mutability::Not {
- local_decl = local_decl.immutable();
- }
-
- debug!("creating temp {:?} with block_context: {:?}", local_decl, this.block_context);
- // Find out whether this temp is being created within the
- // tail expression of a block whose result is ignored.
- if let Some(tail_info) = this.block_context.currently_in_block_tail() {
- local_decl = local_decl.block_tail(tail_info);
- }
- if let ExprKind::StaticRef { def_id, .. } = expr.kind {
- let is_thread_local = this.hir.tcx().has_attr(def_id, sym::thread_local);
- local_decl.local_info = LocalInfo::StaticRef { def_id, is_thread_local };
- }
- this.local_decls.push(local_decl)
- };
- let temp_place = &Place::from(temp);
-
- match expr.kind {
- // Don't bother with StorageLive and Dead for these temporaries,
- // they are never assigned.
- ExprKind::Break { .. } | ExprKind::Continue { .. } | ExprKind::Return { .. } => (),
- ExprKind::Block { body: hir::Block { expr: None, targeted_by_break: false, .. } }
- if expr_ty.is_never() =>
- {
- ()
- }
- _ => {
- this.cfg
- .push(block, Statement { source_info, kind: StatementKind::StorageLive(temp) });
-
- // In constants, `temp_lifetime` is `None` for temporaries that
- // live for the `'static` lifetime. Thus we do not drop these
- // temporaries and simply leak them.
- // This is equivalent to what `let x = &foo();` does in
- // functions. The temporary is lifted to their surrounding
- // scope. In a function that means the temporary lives until
- // just before the function returns. In constants that means it
- // outlives the constant's initialization value computation.
- // Anything outliving a constant must have the `'static`
- // lifetime and live forever.
- // Anything with a shorter lifetime (e.g the `&foo()` in
- // `bar(&foo())` or anything within a block will keep the
- // regular drops just like runtime code.
- if let Some(temp_lifetime) = temp_lifetime {
- this.schedule_drop(expr_span, temp_lifetime, temp, DropKind::Storage);
- }
- }
- }
-
- unpack!(block = this.into(temp_place, block, expr));
-
- if let Some(temp_lifetime) = temp_lifetime {
- this.schedule_drop(expr_span, temp_lifetime, temp, DropKind::Value);
- }
-
- block.and(temp)
- }
-}
+++ /dev/null
-use crate::hair::*;
-
-#[derive(Debug, PartialEq)]
-pub enum Category {
- // An assignable memory location like `x`, `x.f`, `foo()[3]`, that
- // sort of thing. Something that could appear on the LHS of an `=`
- // sign.
- Place,
-
- // A literal like `23` or `"foo"`. Does not include constant
- // expressions like `3 + 5`.
- Constant,
-
- // Something that generates a new value at runtime, like `x + y`
- // or `foo()`.
- Rvalue(RvalueFunc),
-}
-
-// Rvalues fall into different "styles" that will determine which fn
-// is best suited to generate them.
-#[derive(Debug, PartialEq)]
-pub enum RvalueFunc {
- // Best generated by `into`. This is generally exprs that
- // cause branching, like `match`, but also includes calls.
- Into,
-
- // Best generated by `as_rvalue`. This is usually the case.
- AsRvalue,
-}
-
-/// Determines the category for a given expression. Note that scope
-/// and paren expressions have no category.
-impl Category {
- pub fn of(ek: &ExprKind<'_>) -> Option<Category> {
- match *ek {
- ExprKind::Scope { .. } => None,
-
- ExprKind::Field { .. }
- | ExprKind::Deref { .. }
- | ExprKind::Index { .. }
- | ExprKind::SelfRef
- | ExprKind::VarRef { .. }
- | ExprKind::PlaceTypeAscription { .. }
- | ExprKind::ValueTypeAscription { .. } => Some(Category::Place),
-
- ExprKind::LogicalOp { .. }
- | ExprKind::Match { .. }
- | ExprKind::NeverToAny { .. }
- | ExprKind::Use { .. }
- | ExprKind::Adt { .. }
- | ExprKind::Borrow { .. }
- | ExprKind::AddressOf { .. }
- | ExprKind::Call { .. } => Some(Category::Rvalue(RvalueFunc::Into)),
-
- ExprKind::Array { .. }
- | ExprKind::Tuple { .. }
- | ExprKind::Closure { .. }
- | ExprKind::Unary { .. }
- | ExprKind::Binary { .. }
- | ExprKind::Box { .. }
- | ExprKind::Cast { .. }
- | ExprKind::Pointer { .. }
- | ExprKind::Repeat { .. }
- | ExprKind::Assign { .. }
- | ExprKind::AssignOp { .. }
- | ExprKind::Yield { .. }
- | ExprKind::InlineAsm { .. } => Some(Category::Rvalue(RvalueFunc::AsRvalue)),
-
- ExprKind::Literal { .. } | ExprKind::StaticRef { .. } => Some(Category::Constant),
-
- ExprKind::Loop { .. }
- | ExprKind::Block { .. }
- | ExprKind::Break { .. }
- | ExprKind::Continue { .. }
- | ExprKind::Return { .. } =>
- // FIXME(#27840) these probably want their own
- // category, like "nonterminating"
- {
- Some(Category::Rvalue(RvalueFunc::Into))
- }
- }
- }
-}
+++ /dev/null
-//! See docs in build/expr/mod.rs
-
-use crate::build::expr::category::{Category, RvalueFunc};
-use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
-use crate::hair::*;
-use rustc::mir::*;
-use rustc::ty::{self, CanonicalUserTypeAnnotation};
-use rustc_data_structures::fx::FxHashMap;
-use rustc_hir as hir;
-use rustc_span::symbol::sym;
-
-use rustc_target::spec::abi::Abi;
-
-impl<'a, 'tcx> Builder<'a, 'tcx> {
- /// Compile `expr`, storing the result into `destination`, which
- /// is assumed to be uninitialized.
- pub fn into_expr(
- &mut self,
- destination: &Place<'tcx>,
- mut block: BasicBlock,
- expr: Expr<'tcx>,
- ) -> BlockAnd<()> {
- debug!("into_expr(destination={:?}, block={:?}, expr={:?})", destination, block, expr);
-
- // since we frequently have to reference `self` from within a
- // closure, where `self` would be shadowed, it's easier to
- // just use the name `this` uniformly
- let this = self;
- let expr_span = expr.span;
- let source_info = this.source_info(expr_span);
-
- let expr_is_block_or_scope = match expr.kind {
- ExprKind::Block { .. } => true,
- ExprKind::Scope { .. } => true,
- _ => false,
- };
-
- if !expr_is_block_or_scope {
- this.block_context.push(BlockFrame::SubExpr);
- }
-
- let block_and = match expr.kind {
- ExprKind::Scope { region_scope, lint_level, value } => {
- let region_scope = (region_scope, source_info);
- this.in_scope(region_scope, lint_level, |this| this.into(destination, block, value))
- }
- ExprKind::Block { body: ast_block } => {
- this.ast_block(destination, block, ast_block, source_info)
- }
- ExprKind::Match { scrutinee, arms } => {
- this.match_expr(destination, expr_span, block, scrutinee, arms)
- }
- ExprKind::NeverToAny { source } => {
- let source = this.hir.mirror(source);
- let is_call = match source.kind {
- ExprKind::Call { .. } => true,
- _ => false,
- };
-
- // (#66975) Source could be a const of type `!`, so has to
- // exist in the generated MIR.
- unpack!(block = this.as_temp(block, this.local_scope(), source, Mutability::Mut,));
-
- // This is an optimization. If the expression was a call then we already have an
- // unreachable block. Don't bother to terminate it and create a new one.
- if is_call {
- block.unit()
- } else {
- this.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
- let end_block = this.cfg.start_new_block();
- end_block.unit()
- }
- }
- ExprKind::LogicalOp { op, lhs, rhs } => {
- // And:
- //
- // [block: If(lhs)] -true-> [else_block: If(rhs)] -true-> [true_block]
- // | | (false)
- // +----------false-----------+------------------> [false_block]
- //
- // Or:
- //
- // [block: If(lhs)] -false-> [else_block: If(rhs)] -true-> [true_block]
- // | (true) | (false)
- // [true_block] [false_block]
-
- let (true_block, false_block, mut else_block, join_block) = (
- this.cfg.start_new_block(),
- this.cfg.start_new_block(),
- this.cfg.start_new_block(),
- this.cfg.start_new_block(),
- );
-
- let lhs = unpack!(block = this.as_local_operand(block, lhs));
- let blocks = match op {
- LogicalOp::And => (else_block, false_block),
- LogicalOp::Or => (true_block, else_block),
- };
- let term = TerminatorKind::if_(this.hir.tcx(), lhs, blocks.0, blocks.1);
- this.cfg.terminate(block, source_info, term);
-
- let rhs = unpack!(else_block = this.as_local_operand(else_block, rhs));
- let term = TerminatorKind::if_(this.hir.tcx(), rhs, true_block, false_block);
- this.cfg.terminate(else_block, source_info, term);
-
- this.cfg.push_assign_constant(
- true_block,
- source_info,
- destination,
- Constant { span: expr_span, user_ty: None, literal: this.hir.true_literal() },
- );
-
- this.cfg.push_assign_constant(
- false_block,
- source_info,
- destination,
- Constant { span: expr_span, user_ty: None, literal: this.hir.false_literal() },
- );
-
- // Link up both branches:
- this.cfg.goto(true_block, source_info, join_block);
- this.cfg.goto(false_block, source_info, join_block);
- join_block.unit()
- }
- ExprKind::Loop { body } => {
- // [block]
- // |
- // [loop_block] -> [body_block] -/eval. body/-> [body_block_end]
- // | ^ |
- // false link | |
- // | +-----------------------------------------+
- // +-> [diverge_cleanup]
- // The false link is required to make sure borrowck considers unwinds through the
- // body, even when the exact code in the body cannot unwind
-
- let loop_block = this.cfg.start_new_block();
- let exit_block = this.cfg.start_new_block();
-
- // Start the loop.
- this.cfg.goto(block, source_info, loop_block);
-
- this.in_breakable_scope(
- Some(loop_block),
- exit_block,
- destination.clone(),
- move |this| {
- // conduct the test, if necessary
- let body_block = this.cfg.start_new_block();
- let diverge_cleanup = this.diverge_cleanup();
- this.cfg.terminate(
- loop_block,
- source_info,
- TerminatorKind::FalseUnwind {
- real_target: body_block,
- unwind: Some(diverge_cleanup),
- },
- );
-
- // The “return” value of the loop body must always be an unit. We therefore
- // introduce a unit temporary as the destination for the loop body.
- let tmp = this.get_unit_temp();
- // Execute the body, branching back to the test.
- let body_block_end = unpack!(this.into(&tmp, body_block, body));
- this.cfg.goto(body_block_end, source_info, loop_block);
- },
- );
- exit_block.unit()
- }
- ExprKind::Call { ty, fun, args, from_hir_call } => {
- let intrinsic = match ty.kind {
- ty::FnDef(def_id, _) => {
- let f = ty.fn_sig(this.hir.tcx());
- if f.abi() == Abi::RustIntrinsic || f.abi() == Abi::PlatformIntrinsic {
- Some(this.hir.tcx().item_name(def_id))
- } else {
- None
- }
- }
- _ => None,
- };
- let fun = unpack!(block = this.as_local_operand(block, fun));
- if let Some(sym::move_val_init) = intrinsic {
- // `move_val_init` has "magic" semantics - the second argument is
- // always evaluated "directly" into the first one.
-
- let mut args = args.into_iter();
- let ptr = args.next().expect("0 arguments to `move_val_init`");
- let val = args.next().expect("1 argument to `move_val_init`");
- assert!(args.next().is_none(), ">2 arguments to `move_val_init`");
-
- let ptr = this.hir.mirror(ptr);
- let ptr_ty = ptr.ty;
- // Create an *internal* temp for the pointer, so that unsafety
- // checking won't complain about the raw pointer assignment.
- let ptr_temp = this.local_decls.push(LocalDecl {
- mutability: Mutability::Mut,
- ty: ptr_ty,
- user_ty: UserTypeProjections::none(),
- source_info,
- internal: true,
- local_info: LocalInfo::Other,
- is_block_tail: None,
- });
- let ptr_temp = Place::from(ptr_temp);
- let block = unpack!(this.into(&ptr_temp, block, ptr));
- this.into(&this.hir.tcx().mk_place_deref(ptr_temp), block, val)
- } else {
- let args: Vec<_> = args
- .into_iter()
- .map(|arg| unpack!(block = this.as_local_operand(block, arg)))
- .collect();
-
- let success = this.cfg.start_new_block();
- let cleanup = this.diverge_cleanup();
-
- this.record_operands_moved(&args);
-
- this.cfg.terminate(
- block,
- source_info,
- TerminatorKind::Call {
- func: fun,
- args,
- cleanup: Some(cleanup),
- // FIXME(varkor): replace this with an uninhabitedness-based check.
- // This requires getting access to the current module to call
- // `tcx.is_ty_uninhabited_from`, which is currently tricky to do.
- destination: if expr.ty.is_never() {
- None
- } else {
- Some((destination.clone(), success))
- },
- from_hir_call,
- },
- );
- success.unit()
- }
- }
- ExprKind::Use { source } => this.into(destination, block, source),
- ExprKind::Borrow { arg, borrow_kind } => {
- // We don't do this in `as_rvalue` because we use `as_place`
- // for borrow expressions, so we cannot create an `RValue` that
- // remains valid across user code. `as_rvalue` is usually called
- // by this method anyway, so this shouldn't cause too many
- // unnecessary temporaries.
- let arg_place = match borrow_kind {
- BorrowKind::Shared => unpack!(block = this.as_read_only_place(block, arg)),
- _ => unpack!(block = this.as_place(block, arg)),
- };
- let borrow =
- Rvalue::Ref(this.hir.tcx().lifetimes.re_erased, borrow_kind, arg_place);
- this.cfg.push_assign(block, source_info, destination, borrow);
- block.unit()
- }
- ExprKind::AddressOf { mutability, arg } => {
- let place = match mutability {
- hir::Mutability::Not => this.as_read_only_place(block, arg),
- hir::Mutability::Mut => this.as_place(block, arg),
- };
- let address_of = Rvalue::AddressOf(mutability, unpack!(block = place));
- this.cfg.push_assign(block, source_info, destination, address_of);
- block.unit()
- }
- ExprKind::Adt { adt_def, variant_index, substs, user_ty, fields, base } => {
- // See the notes for `ExprKind::Array` in `as_rvalue` and for
- // `ExprKind::Borrow` above.
- let is_union = adt_def.is_union();
- let active_field_index = if is_union { Some(fields[0].name.index()) } else { None };
-
- let scope = this.local_scope();
-
- // first process the set of fields that were provided
- // (evaluating them in order given by user)
- let fields_map: FxHashMap<_, _> = fields
- .into_iter()
- .map(|f| (f.name, unpack!(block = this.as_operand(block, scope, f.expr))))
- .collect();
-
- let field_names = this.hir.all_fields(adt_def, variant_index);
-
- let fields =
- if let Some(FruInfo { base, field_types }) = base {
- let base = unpack!(block = this.as_place(block, base));
-
- // MIR does not natively support FRU, so for each
- // base-supplied field, generate an operand that
- // reads it from the base.
- field_names
- .into_iter()
- .zip(field_types.into_iter())
- .map(|(n, ty)| match fields_map.get(&n) {
- Some(v) => v.clone(),
- None => this.consume_by_copy_or_move(
- this.hir.tcx().mk_place_field(base.clone(), n, ty),
- ),
- })
- .collect()
- } else {
- field_names.iter().filter_map(|n| fields_map.get(n).cloned()).collect()
- };
-
- let inferred_ty = expr.ty;
- let user_ty = user_ty.map(|ty| {
- this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
- span: source_info.span,
- user_ty: ty,
- inferred_ty,
- })
- });
- let adt = box AggregateKind::Adt(
- adt_def,
- variant_index,
- substs,
- user_ty,
- active_field_index,
- );
- this.cfg.push_assign(
- block,
- source_info,
- destination,
- Rvalue::Aggregate(adt, fields),
- );
- block.unit()
- }
-
- // These cases don't actually need a destination
- ExprKind::Assign { .. }
- | ExprKind::AssignOp { .. }
- | ExprKind::Continue { .. }
- | ExprKind::Break { .. }
- | ExprKind::InlineAsm { .. }
- | ExprKind::Return { .. } => {
- unpack!(block = this.stmt_expr(block, expr, None));
- this.cfg.push_assign_unit(block, source_info, destination);
- block.unit()
- }
-
- // Avoid creating a temporary
- ExprKind::VarRef { .. }
- | ExprKind::SelfRef
- | ExprKind::PlaceTypeAscription { .. }
- | ExprKind::ValueTypeAscription { .. } => {
- debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
-
- let place = unpack!(block = this.as_place(block, expr));
- let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
- this.cfg.push_assign(block, source_info, destination, rvalue);
- block.unit()
- }
- ExprKind::Index { .. } | ExprKind::Deref { .. } | ExprKind::Field { .. } => {
- debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
-
- // Create a "fake" temporary variable so that we check that the
- // value is Sized. Usually, this is caught in type checking, but
- // in the case of box expr there is no such check.
- if !destination.projection.is_empty() {
- this.local_decls.push(LocalDecl::new_temp(expr.ty, expr.span));
- }
-
- debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
-
- let place = unpack!(block = this.as_place(block, expr));
- let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
- this.cfg.push_assign(block, source_info, destination, rvalue);
- block.unit()
- }
-
- // these are the cases that are more naturally handled by some other mode
- ExprKind::Unary { .. }
- | ExprKind::Binary { .. }
- | ExprKind::Box { .. }
- | ExprKind::Cast { .. }
- | ExprKind::Pointer { .. }
- | ExprKind::Repeat { .. }
- | ExprKind::Array { .. }
- | ExprKind::Tuple { .. }
- | ExprKind::Closure { .. }
- | ExprKind::Literal { .. }
- | ExprKind::StaticRef { .. }
- | ExprKind::Yield { .. } => {
- debug_assert!(match Category::of(&expr.kind).unwrap() {
- // should be handled above
- Category::Rvalue(RvalueFunc::Into) => false,
-
- // must be handled above or else we get an
- // infinite loop in the builder; see
- // e.g., `ExprKind::VarRef` above
- Category::Place => false,
-
- _ => true,
- });
-
- let rvalue = unpack!(block = this.as_local_rvalue(block, expr));
- this.cfg.push_assign(block, source_info, destination, rvalue);
- block.unit()
- }
- };
-
- if !expr_is_block_or_scope {
- let popped = this.block_context.pop();
- assert!(popped.is_some());
- }
-
- block_and
- }
-}
+++ /dev/null
-//! Builds MIR from expressions. As a caller into this module, you
-//! have many options, but the first thing you have to decide is
-//! whether you are evaluating this expression for its *value*, its
-//! *location*, or as a *constant*.
-//!
-//! Typically, you want the value: e.g., if you are doing `expr_a +
-//! expr_b`, you want the values of those expressions. In that case,
-//! you want one of the following functions. Note that if the expr has
-//! a type that is not `Copy`, then using any of these functions will
-//! "move" the value out of its current home (if any).
-//!
-//! - `into` -- writes the value into a specific location, which
-//! should be uninitialized
-//! - `as_operand` -- evaluates the value and yields an `Operand`,
-//! suitable for use as an argument to an `Rvalue`
-//! - `as_temp` -- evaluates into a temporary; this is similar to `as_operand`
-//! except it always returns a fresh place, even for constants
-//! - `as_rvalue` -- yields an `Rvalue`, suitable for use in an assignment;
-//! as of this writing, never needed outside of the `expr` module itself
-//!
-//! Sometimes though want the expression's *location*. An example
-//! would be during a match statement, or the operand of the `&`
-//! operator. In that case, you want `as_place`. This will create a
-//! temporary if necessary.
-//!
-//! Finally, if it's a constant you seek, then call
-//! `as_constant`. This creates a `Constant<H>`, but naturally it can
-//! only be used on constant expressions and hence is needed only in
-//! very limited contexts.
-//!
-//! ### Implementation notes
-//!
-//! For any given kind of expression, there is generally one way that
-//! can be lowered most naturally. This is specified by the
-//! `Category::of` function in the `category` module. For example, a
-//! struct expression (or other expression that creates a new value)
-//! is typically easiest to write in terms of `as_rvalue` or `into`,
-//! whereas a reference to a field is easiest to write in terms of
-//! `as_place`. (The exception to this is scope and paren
-//! expressions, which have no category.)
-//!
-//! Therefore, the various functions above make use of one another in
-//! a descending fashion. For any given expression, you should pick
-//! the most suitable spot to implement it, and then just let the
-//! other fns cycle around. The handoff works like this:
-//!
-//! - `into(place)` -> fallback is to create a rvalue with `as_rvalue` and assign it to `place`
-//! - `as_rvalue` -> fallback is to create an Operand with `as_operand` and use `Rvalue::use`
-//! - `as_operand` -> either invokes `as_constant` or `as_temp`
-//! - `as_constant` -> (no fallback)
-//! - `as_temp` -> creates a temporary and either calls `as_place` or `into`
-//! - `as_place` -> for rvalues, falls back to `as_temp` and returns that
-//!
-//! As you can see, there is a cycle where `into` can (in theory) fallback to `as_temp`
-//! which can fallback to `into`. So if one of the `ExprKind` variants is not, in fact,
-//! implemented in the category where it is supposed to be, there will be a problem.
-//!
-//! Of those fallbacks, the most interesting one is `into`, because
-//! it discriminates based on the category of the expression. This is
-//! basically the point where the "by value" operations are bridged
-//! over to the "by reference" mode (`as_place`).
-
-mod as_constant;
-mod as_operand;
-mod as_place;
-mod as_rvalue;
-mod as_temp;
-mod category;
-mod into;
-mod stmt;
+++ /dev/null
-use crate::build::scope::BreakableTarget;
-use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
-use crate::hair::*;
-use rustc::middle::region;
-use rustc::mir::*;
-
-impl<'a, 'tcx> Builder<'a, 'tcx> {
- /// Builds a block of MIR statements to evaluate the HAIR `expr`.
- /// If the original expression was an AST statement,
- /// (e.g., `some().code(&here());`) then `opt_stmt_span` is the
- /// span of that statement (including its semicolon, if any).
- /// The scope is used if a statement temporary must be dropped.
- pub fn stmt_expr(
- &mut self,
- mut block: BasicBlock,
- expr: Expr<'tcx>,
- statement_scope: Option<region::Scope>,
- ) -> BlockAnd<()> {
- let this = self;
- let expr_span = expr.span;
- let source_info = this.source_info(expr.span);
- // Handle a number of expressions that don't need a destination at all. This
- // avoids needing a mountain of temporary `()` variables.
- let expr2 = expr.clone();
- match expr.kind {
- ExprKind::Scope { region_scope, lint_level, value } => {
- let value = this.hir.mirror(value);
- this.in_scope((region_scope, source_info), lint_level, |this| {
- this.stmt_expr(block, value, statement_scope)
- })
- }
- ExprKind::Assign { lhs, rhs } => {
- let lhs = this.hir.mirror(lhs);
- let rhs = this.hir.mirror(rhs);
- let lhs_span = lhs.span;
-
- // Note: we evaluate assignments right-to-left. This
- // is better for borrowck interaction with overloaded
- // operators like x[j] = x[i].
-
- debug!("stmt_expr Assign block_context.push(SubExpr) : {:?}", expr2);
- this.block_context.push(BlockFrame::SubExpr);
-
- // Generate better code for things that don't need to be
- // dropped.
- if this.hir.needs_drop(lhs.ty) {
- let rhs = unpack!(block = this.as_local_operand(block, rhs));
- let lhs = unpack!(block = this.as_place(block, lhs));
- unpack!(block = this.build_drop_and_replace(block, lhs_span, lhs, rhs));
- } else {
- let rhs = unpack!(block = this.as_local_rvalue(block, rhs));
- let lhs = unpack!(block = this.as_place(block, lhs));
- this.cfg.push_assign(block, source_info, &lhs, rhs);
- }
-
- this.block_context.pop();
- block.unit()
- }
- ExprKind::AssignOp { op, lhs, rhs } => {
- // FIXME(#28160) there is an interesting semantics
- // question raised here -- should we "freeze" the
- // value of the lhs here? I'm inclined to think not,
- // since it seems closer to the semantics of the
- // overloaded version, which takes `&mut self`. This
- // only affects weird things like `x += {x += 1; x}`
- // -- is that equal to `x + (x + 1)` or `2*(x+1)`?
-
- let lhs = this.hir.mirror(lhs);
- let lhs_ty = lhs.ty;
-
- debug!("stmt_expr AssignOp block_context.push(SubExpr) : {:?}", expr2);
- this.block_context.push(BlockFrame::SubExpr);
-
- // As above, RTL.
- let rhs = unpack!(block = this.as_local_operand(block, rhs));
- let lhs = unpack!(block = this.as_place(block, lhs));
-
- // we don't have to drop prior contents or anything
- // because AssignOp is only legal for Copy types
- // (overloaded ops should be desugared into a call).
- let result = unpack!(
- block = this.build_binary_op(
- block,
- op,
- expr_span,
- lhs_ty,
- Operand::Copy(lhs.clone()),
- rhs
- )
- );
- this.cfg.push_assign(block, source_info, &lhs, result);
-
- this.block_context.pop();
- block.unit()
- }
- ExprKind::Continue { label } => {
- this.break_scope(block, None, BreakableTarget::Continue(label), source_info)
- }
- ExprKind::Break { label, value } => {
- this.break_scope(block, value, BreakableTarget::Break(label), source_info)
- }
- ExprKind::Return { value } => {
- this.break_scope(block, value, BreakableTarget::Return, source_info)
- }
- ExprKind::InlineAsm { asm, outputs, inputs } => {
- debug!("stmt_expr InlineAsm block_context.push(SubExpr) : {:?}", expr2);
- this.block_context.push(BlockFrame::SubExpr);
- let outputs = outputs
- .into_iter()
- .map(|output| unpack!(block = this.as_place(block, output)))
- .collect::<Vec<_>>()
- .into_boxed_slice();
- let inputs = inputs
- .into_iter()
- .map(|input| {
- (input.span(), unpack!(block = this.as_local_operand(block, input)))
- })
- .collect::<Vec<_>>()
- .into_boxed_slice();
- this.cfg.push(
- block,
- Statement {
- source_info,
- kind: StatementKind::InlineAsm(box InlineAsm {
- asm: asm.clone(),
- outputs,
- inputs,
- }),
- },
- );
- this.block_context.pop();
- block.unit()
- }
- _ => {
- assert!(
- statement_scope.is_some(),
- "Should not be calling `stmt_expr` on a general expression \
- without a statement scope",
- );
-
- // Issue #54382: When creating temp for the value of
- // expression like:
- //
- // `{ side_effects(); { let l = stuff(); the_value } }`
- //
- // it is usually better to focus on `the_value` rather
- // than the entirety of block(s) surrounding it.
- let adjusted_span = (|| {
- if let ExprKind::Block { body } = expr.kind {
- if let Some(tail_expr) = &body.expr {
- let mut expr = tail_expr;
- while let rustc_hir::ExprKind::Block(subblock, _label) = &expr.kind {
- if let Some(subtail_expr) = &subblock.expr {
- expr = subtail_expr
- } else {
- break;
- }
- }
- this.block_context
- .push(BlockFrame::TailExpr { tail_result_is_ignored: true });
- return Some(expr.span);
- }
- }
- None
- })();
-
- let temp =
- unpack!(block = this.as_temp(block, statement_scope, expr, Mutability::Not));
-
- if let Some(span) = adjusted_span {
- this.local_decls[temp].source_info.span = span;
- this.block_context.pop();
- }
-
- block.unit()
- }
- }
- }
-}
+++ /dev/null
-//! In general, there are a number of things for which it's convenient
-//! to just call `builder.into` and have it emit its result into a
-//! given location. This is basically for expressions or things that can be
-//! wrapped up as expressions (e.g., blocks). To make this ergonomic, we use this
-//! latter `EvalInto` trait.
-
-use crate::build::{BlockAnd, Builder};
-use crate::hair::*;
-use rustc::mir::*;
-
-pub(in crate::build) trait EvalInto<'tcx> {
- fn eval_into(
- self,
- builder: &mut Builder<'_, 'tcx>,
- destination: &Place<'tcx>,
- block: BasicBlock,
- ) -> BlockAnd<()>;
-}
-
-impl<'a, 'tcx> Builder<'a, 'tcx> {
- pub fn into<E>(&mut self, destination: &Place<'tcx>, block: BasicBlock, expr: E) -> BlockAnd<()>
- where
- E: EvalInto<'tcx>,
- {
- expr.eval_into(self, destination, block)
- }
-}
-
-impl<'tcx> EvalInto<'tcx> for ExprRef<'tcx> {
- fn eval_into(
- self,
- builder: &mut Builder<'_, 'tcx>,
- destination: &Place<'tcx>,
- block: BasicBlock,
- ) -> BlockAnd<()> {
- let expr = builder.hir.mirror(self);
- builder.into_expr(destination, block, expr)
- }
-}
-
-impl<'tcx> EvalInto<'tcx> for Expr<'tcx> {
- fn eval_into(
- self,
- builder: &mut Builder<'_, 'tcx>,
- destination: &Place<'tcx>,
- block: BasicBlock,
- ) -> BlockAnd<()> {
- builder.into_expr(destination, block, self)
- }
-}
+++ /dev/null
-//! Code related to match expressions. These are sufficiently complex to
-//! warrant their own module and submodules. :) This main module includes the
-//! high-level algorithm, the submodules contain the details.
-//!
-//! This also includes code for pattern bindings in `let` statements and
-//! function parameters.
-
-use crate::build::scope::DropKind;
-use crate::build::ForGuard::{self, OutsideGuard, RefWithinGuard};
-use crate::build::{BlockAnd, BlockAndExtension, Builder};
-use crate::build::{GuardFrame, GuardFrameLocal, LocalsForNode};
-use crate::hair::{self, *};
-use rustc::middle::region;
-use rustc::mir::*;
-use rustc::ty::layout::VariantIdx;
-use rustc::ty::{self, CanonicalUserTypeAnnotation, Ty};
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_hir::HirId;
-use rustc_index::bit_set::BitSet;
-use rustc_span::Span;
-use smallvec::{smallvec, SmallVec};
-use syntax::ast::Name;
-
-// helper functions, broken out by category:
-mod simplify;
-mod test;
-mod util;
-
-use itertools::Itertools;
-use std::convert::TryFrom;
-
-impl<'a, 'tcx> Builder<'a, 'tcx> {
- /// Generates MIR for a `match` expression.
- ///
- /// The MIR that we generate for a match looks like this.
- ///
- /// ```text
- /// [ 0. Pre-match ]
- /// |
- /// [ 1. Evaluate Scrutinee (expression being matched on) ]
- /// [ (fake read of scrutinee) ]
- /// |
- /// [ 2. Decision tree -- check discriminants ] <--------+
- /// | |
- /// | (once a specific arm is chosen) |
- /// | |
- /// [pre_binding_block] [otherwise_block]
- /// | |
- /// [ 3. Create "guard bindings" for arm ] |
- /// [ (create fake borrows) ] |
- /// | |
- /// [ 4. Execute guard code ] |
- /// [ (read fake borrows) ] --(guard is false)-----------+
- /// |
- /// | (guard results in true)
- /// |
- /// [ 5. Create real bindings and execute arm ]
- /// |
- /// [ Exit match ]
- /// ```
- ///
- /// All of the different arms have been stacked on top of each other to
- /// simplify the diagram. For an arm with no guard the blocks marked 3 and
- /// 4 and the fake borrows are omitted.
- ///
- /// We generate MIR in the following steps:
- ///
- /// 1. Evaluate the scrutinee and add the fake read of it ([Builder::lower_scrutinee]).
- /// 2. Create the prebinding and otherwise blocks ([Builder::create_match_candidates]).
- /// 3. Create the decision tree ([Builder::lower_match_tree]).
- /// 4. Determine the fake borrows that are needed from the places that were
- /// matched against and create the required temporaries for them
- /// ([Builder::calculate_fake_borrows]).
- /// 5. Create everything else: the guards and the arms ([Builder::lower_match_arms]).
- ///
- /// ## False edges
- ///
- /// We don't want to have the exact structure of the decision tree be
- /// visible through borrow checking. False edges ensure that the CFG as
- /// seen by borrow checking doesn't encode this. False edges are added:
- ///
- /// * From each prebinding block to the next prebinding block.
- /// * From each otherwise block to the next prebinding block.
- pub fn match_expr(
- &mut self,
- destination: &Place<'tcx>,
- span: Span,
- mut block: BasicBlock,
- scrutinee: ExprRef<'tcx>,
- arms: Vec<Arm<'tcx>>,
- ) -> BlockAnd<()> {
- let scrutinee_span = scrutinee.span();
- let scrutinee_place =
- unpack!(block = self.lower_scrutinee(block, scrutinee, scrutinee_span,));
-
- let mut arm_candidates = self.create_match_candidates(&scrutinee_place, &arms);
-
- let match_has_guard = arms.iter().any(|arm| arm.guard.is_some());
- let candidates =
- arm_candidates.iter_mut().flat_map(|(_, candidates)| candidates).collect::<Vec<_>>();
-
- let fake_borrow_temps =
- self.lower_match_tree(block, scrutinee_span, match_has_guard, candidates);
-
- self.lower_match_arms(
- &destination,
- scrutinee_place,
- scrutinee_span,
- arm_candidates,
- self.source_info(span),
- fake_borrow_temps,
- )
- }
-
- /// Evaluate the scrutinee and add the fake read of it.
- fn lower_scrutinee(
- &mut self,
- mut block: BasicBlock,
- scrutinee: ExprRef<'tcx>,
- scrutinee_span: Span,
- ) -> BlockAnd<Place<'tcx>> {
- let scrutinee_place = unpack!(block = self.as_place(block, scrutinee));
- // Matching on a `scrutinee_place` with an uninhabited type doesn't
- // generate any memory reads by itself, and so if the place "expression"
- // contains unsafe operations like raw pointer dereferences or union
- // field projections, we wouldn't know to require an `unsafe` block
- // around a `match` equivalent to `std::intrinsics::unreachable()`.
- // See issue #47412 for this hole being discovered in the wild.
- //
- // HACK(eddyb) Work around the above issue by adding a dummy inspection
- // of `scrutinee_place`, specifically by applying `ReadForMatch`.
- //
- // NOTE: ReadForMatch also checks that the scrutinee is initialized.
- // This is currently needed to not allow matching on an uninitialized,
- // uninhabited value. If we get never patterns, those will check that
- // the place is initialized, and so this read would only be used to
- // check safety.
- let cause_matched_place = FakeReadCause::ForMatchedPlace;
- let source_info = self.source_info(scrutinee_span);
- self.cfg.push_fake_read(block, source_info, cause_matched_place, scrutinee_place.clone());
-
- block.and(scrutinee_place)
- }
-
- /// Create the initial `Candidate`s for a `match` expression.
- fn create_match_candidates<'pat>(
- &mut self,
- scrutinee: &Place<'tcx>,
- arms: &'pat [Arm<'tcx>],
- ) -> Vec<(&'pat Arm<'tcx>, Vec<Candidate<'pat, 'tcx>>)> {
- let candidate_count = arms.iter().map(|c| c.top_pats_hack().len()).sum::<usize>();
- let pre_binding_blocks: Vec<_> =
- (0..candidate_count).map(|_| self.cfg.start_new_block()).collect();
-
- let mut candidate_pre_binding_blocks = pre_binding_blocks.iter();
- let mut next_candidate_pre_binding_blocks = pre_binding_blocks.iter().skip(1);
-
- // Assemble a list of candidates: there is one candidate per pattern,
- // which means there may be more than one candidate *per arm*.
- arms.iter()
- .map(|arm| {
- let arm_has_guard = arm.guard.is_some();
- let arm_candidates: Vec<_> = arm
- .top_pats_hack()
- .iter()
- .zip(candidate_pre_binding_blocks.by_ref())
- .map(|(pattern, pre_binding_block)| Candidate {
- span: pattern.span,
- match_pairs: smallvec![MatchPair::new(scrutinee.clone(), pattern)],
- bindings: vec![],
- ascriptions: vec![],
- otherwise_block: if arm_has_guard {
- Some(self.cfg.start_new_block())
- } else {
- None
- },
- pre_binding_block: *pre_binding_block,
- next_candidate_pre_binding_block: next_candidate_pre_binding_blocks
- .next()
- .copied(),
- })
- .collect();
- (arm, arm_candidates)
- })
- .collect()
- }
-
- /// Create the decision tree for the match expression, starting from `block`.
- ///
- /// Modifies `candidates` to store the bindings and type ascriptions for
- /// that candidate.
- ///
- /// Returns the places that need fake borrows because we bind or test them.
- fn lower_match_tree<'pat>(
- &mut self,
- block: BasicBlock,
- scrutinee_span: Span,
- match_has_guard: bool,
- mut candidates: Vec<&mut Candidate<'pat, 'tcx>>,
- ) -> Vec<(Place<'tcx>, Local)> {
- // The set of places that we are creating fake borrows of. If there are
- // no match guards then we don't need any fake borrows, so don't track
- // them.
- let mut fake_borrows = if match_has_guard { Some(FxHashSet::default()) } else { None };
-
- // This will generate code to test scrutinee_place and
- // branch to the appropriate arm block
- self.match_candidates(
- scrutinee_span,
- &mut Some(block),
- None,
- &mut candidates,
- &mut fake_borrows,
- );
-
- if let Some(ref borrows) = fake_borrows {
- self.calculate_fake_borrows(borrows, scrutinee_span)
- } else {
- Vec::new()
- }
- }
-
- /// Lower the bindings, guards and arm bodies of a `match` expression.
- ///
- /// The decision tree should have already been created
- /// (by [Builder::lower_match_tree]).
- ///
- /// `outer_source_info` is the SourceInfo for the whole match.
- fn lower_match_arms(
- &mut self,
- destination: &Place<'tcx>,
- scrutinee_place: Place<'tcx>,
- scrutinee_span: Span,
- arm_candidates: Vec<(&'_ Arm<'tcx>, Vec<Candidate<'_, 'tcx>>)>,
- outer_source_info: SourceInfo,
- fake_borrow_temps: Vec<(Place<'tcx>, Local)>,
- ) -> BlockAnd<()> {
- let match_scope = self.scopes.topmost();
-
- let arm_end_blocks: Vec<_> = arm_candidates
- .into_iter()
- .map(|(arm, candidates)| {
- debug!("lowering arm {:?}\ncanidates = {:?}", arm, candidates);
-
- let arm_source_info = self.source_info(arm.span);
- let arm_scope = (arm.scope, arm_source_info);
- self.in_scope(arm_scope, arm.lint_level, |this| {
- let body = this.hir.mirror(arm.body.clone());
- let scope = this.declare_bindings(
- None,
- arm.span,
- &arm.top_pats_hack()[0],
- ArmHasGuard(arm.guard.is_some()),
- Some((Some(&scrutinee_place), scrutinee_span)),
- );
-
- let arm_block = this.bind_pattern(
- outer_source_info,
- candidates,
- arm.guard.as_ref().map(|g| (g, match_scope)),
- &fake_borrow_temps,
- scrutinee_span,
- arm.scope,
- );
-
- if let Some(source_scope) = scope {
- this.source_scope = source_scope;
- }
-
- this.into(destination, arm_block, body)
- })
- })
- .collect();
-
- // all the arm blocks will rejoin here
- let end_block = self.cfg.start_new_block();
-
- for arm_block in arm_end_blocks {
- self.cfg.goto(unpack!(arm_block), outer_source_info, end_block);
- }
-
- self.source_scope = outer_source_info.scope;
-
- end_block.unit()
- }
-
- /// Binds the variables and ascribes types for a given `match` arm.
- ///
- /// Also check if the guard matches, if it's provided.
- fn bind_pattern(
- &mut self,
- outer_source_info: SourceInfo,
- mut candidates: Vec<Candidate<'_, 'tcx>>,
- guard: Option<(&Guard<'tcx>, region::Scope)>,
- fake_borrow_temps: &Vec<(Place<'tcx>, Local)>,
- scrutinee_span: Span,
- arm_scope: region::Scope,
- ) -> BasicBlock {
- if candidates.len() == 1 {
- // Avoid generating another `BasicBlock` when we only have one
- // candidate.
- self.bind_and_guard_matched_candidate(
- candidates.pop().unwrap(),
- guard,
- fake_borrow_temps,
- scrutinee_span,
- )
- } else {
- let arm_block = self.cfg.start_new_block();
- for candidate in candidates {
- // Avoid scheduling drops multiple times.
- self.clear_top_scope(arm_scope);
- let binding_end = self.bind_and_guard_matched_candidate(
- candidate,
- guard,
- fake_borrow_temps,
- scrutinee_span,
- );
- self.cfg.goto(binding_end, outer_source_info, arm_block);
- }
- arm_block
- }
- }
-
- pub(super) fn expr_into_pattern(
- &mut self,
- mut block: BasicBlock,
- irrefutable_pat: Pat<'tcx>,
- initializer: ExprRef<'tcx>,
- ) -> BlockAnd<()> {
- match *irrefutable_pat.kind {
- // Optimize the case of `let x = ...` to write directly into `x`
- PatKind::Binding { mode: BindingMode::ByValue, var, subpattern: None, .. } => {
- let place =
- self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard);
- unpack!(block = self.into(&place, block, initializer));
-
- // Inject a fake read, see comments on `FakeReadCause::ForLet`.
- let source_info = self.source_info(irrefutable_pat.span);
- self.cfg.push_fake_read(block, source_info, FakeReadCause::ForLet, place);
-
- self.schedule_drop_for_binding(var, irrefutable_pat.span, OutsideGuard);
- block.unit()
- }
-
- // Optimize the case of `let x: T = ...` to write directly
- // into `x` and then require that `T == typeof(x)`.
- //
- // Weirdly, this is needed to prevent the
- // `intrinsic-move-val.rs` test case from crashing. That
- // test works with uninitialized values in a rather
- // dubious way, so it may be that the test is kind of
- // broken.
- PatKind::AscribeUserType {
- subpattern:
- Pat {
- kind:
- box PatKind::Binding {
- mode: BindingMode::ByValue,
- var,
- subpattern: None,
- ..
- },
- ..
- },
- ascription:
- hair::pattern::Ascription { user_ty: pat_ascription_ty, variance: _, user_ty_span },
- } => {
- let place =
- self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard);
- unpack!(block = self.into(&place, block, initializer));
-
- // Inject a fake read, see comments on `FakeReadCause::ForLet`.
- let pattern_source_info = self.source_info(irrefutable_pat.span);
- let cause_let = FakeReadCause::ForLet;
- self.cfg.push_fake_read(block, pattern_source_info, cause_let, place.clone());
-
- let ty_source_info = self.source_info(user_ty_span);
- let user_ty = pat_ascription_ty.user_ty(
- &mut self.canonical_user_type_annotations,
- place.ty(&self.local_decls, self.hir.tcx()).ty,
- ty_source_info.span,
- );
- self.cfg.push(
- block,
- Statement {
- source_info: ty_source_info,
- kind: StatementKind::AscribeUserType(
- box (place, user_ty),
- // We always use invariant as the variance here. This is because the
- // variance field from the ascription refers to the variance to use
- // when applying the type to the value being matched, but this
- // ascription applies rather to the type of the binding. e.g., in this
- // example:
- //
- // ```
- // let x: T = <expr>
- // ```
- //
- // We are creating an ascription that defines the type of `x` to be
- // exactly `T` (i.e., with invariance). The variance field, in
- // contrast, is intended to be used to relate `T` to the type of
- // `<expr>`.
- ty::Variance::Invariant,
- ),
- },
- );
-
- self.schedule_drop_for_binding(var, irrefutable_pat.span, OutsideGuard);
- block.unit()
- }
-
- _ => {
- let place = unpack!(block = self.as_place(block, initializer));
- self.place_into_pattern(block, irrefutable_pat, &place, true)
- }
- }
- }
-
- pub fn place_into_pattern(
- &mut self,
- block: BasicBlock,
- irrefutable_pat: Pat<'tcx>,
- initializer: &Place<'tcx>,
- set_match_place: bool,
- ) -> BlockAnd<()> {
- // create a dummy candidate
- let mut candidate = Candidate {
- span: irrefutable_pat.span,
- match_pairs: smallvec![MatchPair::new(initializer.clone(), &irrefutable_pat)],
- bindings: vec![],
- ascriptions: vec![],
-
- // since we don't call `match_candidates`, next fields are unused
- otherwise_block: None,
- pre_binding_block: block,
- next_candidate_pre_binding_block: None,
- };
-
- // Simplify the candidate. Since the pattern is irrefutable, this should
- // always convert all match-pairs into bindings.
- self.simplify_candidate(&mut candidate);
-
- if !candidate.match_pairs.is_empty() {
- // ICE if no other errors have been emitted. This used to be a hard error that wouldn't
- // be reached because `hair::pattern::check_match::check_match` wouldn't have let the
- // compiler continue. In our tests this is only ever hit by
- // `ui/consts/const-match-check.rs` with `--cfg eval1`, and that file already generates
- // a different error before hand.
- self.hir.tcx().sess.delay_span_bug(
- candidate.match_pairs[0].pattern.span,
- &format!(
- "match pairs {:?} remaining after simplifying irrefutable pattern",
- candidate.match_pairs,
- ),
- );
- }
-
- // for matches and function arguments, the place that is being matched
- // can be set when creating the variables. But the place for
- // let PATTERN = ... might not even exist until we do the assignment.
- // so we set it here instead
- if set_match_place {
- for binding in &candidate.bindings {
- let local = self.var_local_id(binding.var_id, OutsideGuard);
-
- if let LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
- opt_match_place: Some((ref mut match_place, _)),
- ..
- }))) = self.local_decls[local].local_info
- {
- *match_place = Some(initializer.clone());
- } else {
- bug!("Let binding to non-user variable.")
- }
- }
- }
-
- self.ascribe_types(block, &candidate.ascriptions);
-
- // now apply the bindings, which will also declare the variables
- self.bind_matched_candidate_for_arm_body(block, &candidate.bindings);
-
- block.unit()
- }
-
- /// Declares the bindings of the given patterns and returns the visibility
- /// scope for the bindings in these patterns, if such a scope had to be
- /// created. NOTE: Declaring the bindings should always be done in their
- /// drop scope.
- pub fn declare_bindings(
- &mut self,
- mut visibility_scope: Option<SourceScope>,
- scope_span: Span,
- pattern: &Pat<'tcx>,
- has_guard: ArmHasGuard,
- opt_match_place: Option<(Option<&Place<'tcx>>, Span)>,
- ) -> Option<SourceScope> {
- debug!("declare_bindings: pattern={:?}", pattern);
- self.visit_bindings(
- &pattern,
- UserTypeProjections::none(),
- &mut |this, mutability, name, mode, var, span, ty, user_ty| {
- if visibility_scope.is_none() {
- visibility_scope =
- Some(this.new_source_scope(scope_span, LintLevel::Inherited, None));
- }
- let source_info = SourceInfo { span, scope: this.source_scope };
- let visibility_scope = visibility_scope.unwrap();
- this.declare_binding(
- source_info,
- visibility_scope,
- mutability,
- name,
- mode,
- var,
- ty,
- user_ty,
- has_guard,
- opt_match_place.map(|(x, y)| (x.cloned(), y)),
- pattern.span,
- );
- },
- );
- visibility_scope
- }
-
- pub fn storage_live_binding(
- &mut self,
- block: BasicBlock,
- var: HirId,
- span: Span,
- for_guard: ForGuard,
- ) -> Place<'tcx> {
- let local_id = self.var_local_id(var, for_guard);
- let source_info = self.source_info(span);
- self.cfg.push(block, Statement { source_info, kind: StatementKind::StorageLive(local_id) });
- let region_scope = self.hir.region_scope_tree.var_scope(var.local_id);
- self.schedule_drop(span, region_scope, local_id, DropKind::Storage);
- Place::from(local_id)
- }
-
- pub fn schedule_drop_for_binding(&mut self, var: HirId, span: Span, for_guard: ForGuard) {
- let local_id = self.var_local_id(var, for_guard);
- let region_scope = self.hir.region_scope_tree.var_scope(var.local_id);
- self.schedule_drop(span, region_scope, local_id, DropKind::Value);
- }
-
- pub(super) fn visit_bindings(
- &mut self,
- pattern: &Pat<'tcx>,
- pattern_user_ty: UserTypeProjections,
- f: &mut impl FnMut(
- &mut Self,
- Mutability,
- Name,
- BindingMode,
- HirId,
- Span,
- Ty<'tcx>,
- UserTypeProjections,
- ),
- ) {
- debug!("visit_bindings: pattern={:?} pattern_user_ty={:?}", pattern, pattern_user_ty);
- match *pattern.kind {
- PatKind::Binding { mutability, name, mode, var, ty, ref subpattern, .. } => {
- f(self, mutability, name, mode, var, pattern.span, ty, pattern_user_ty.clone());
- if let Some(subpattern) = subpattern.as_ref() {
- self.visit_bindings(subpattern, pattern_user_ty, f);
- }
- }
-
- PatKind::Array { ref prefix, ref slice, ref suffix }
- | PatKind::Slice { ref prefix, ref slice, ref suffix } => {
- let from = u32::try_from(prefix.len()).unwrap();
- let to = u32::try_from(suffix.len()).unwrap();
- for subpattern in prefix {
- self.visit_bindings(subpattern, pattern_user_ty.clone().index(), f);
- }
- for subpattern in slice {
- self.visit_bindings(subpattern, pattern_user_ty.clone().subslice(from, to), f);
- }
- for subpattern in suffix {
- self.visit_bindings(subpattern, pattern_user_ty.clone().index(), f);
- }
- }
-
- PatKind::Constant { .. } | PatKind::Range { .. } | PatKind::Wild => {}
-
- PatKind::Deref { ref subpattern } => {
- self.visit_bindings(subpattern, pattern_user_ty.deref(), f);
- }
-
- PatKind::AscribeUserType {
- ref subpattern,
- ascription: hair::pattern::Ascription { ref user_ty, user_ty_span, variance: _ },
- } => {
- // This corresponds to something like
- //
- // ```
- // let A::<'a>(_): A<'static> = ...;
- // ```
- //
- // Note that the variance doesn't apply here, as we are tracking the effect
- // of `user_ty` on any bindings contained with subpattern.
- let annotation = CanonicalUserTypeAnnotation {
- span: user_ty_span,
- user_ty: user_ty.user_ty,
- inferred_ty: subpattern.ty,
- };
- let projection = UserTypeProjection {
- base: self.canonical_user_type_annotations.push(annotation),
- projs: Vec::new(),
- };
- let subpattern_user_ty = pattern_user_ty.push_projection(&projection, user_ty_span);
- self.visit_bindings(subpattern, subpattern_user_ty, f)
- }
-
- PatKind::Leaf { ref subpatterns } => {
- for subpattern in subpatterns {
- let subpattern_user_ty = pattern_user_ty.clone().leaf(subpattern.field);
- debug!("visit_bindings: subpattern_user_ty={:?}", subpattern_user_ty);
- self.visit_bindings(&subpattern.pattern, subpattern_user_ty, f);
- }
- }
-
- PatKind::Variant { adt_def, substs: _, variant_index, ref subpatterns } => {
- for subpattern in subpatterns {
- let subpattern_user_ty =
- pattern_user_ty.clone().variant(adt_def, variant_index, subpattern.field);
- self.visit_bindings(&subpattern.pattern, subpattern_user_ty, f);
- }
- }
- PatKind::Or { ref pats } => {
- for pat in pats {
- self.visit_bindings(&pat, pattern_user_ty.clone(), f);
- }
- }
- }
- }
-}
-
-#[derive(Debug)]
-pub struct Candidate<'pat, 'tcx> {
- // span of the original pattern that gave rise to this candidate
- span: Span,
-
- // all of these must be satisfied...
- match_pairs: SmallVec<[MatchPair<'pat, 'tcx>; 1]>,
-
- // ...these bindings established...
- bindings: Vec<Binding<'tcx>>,
-
- // ...and these types asserted...
- ascriptions: Vec<Ascription<'tcx>>,
-
- // ...and the guard must be evaluated, if false branch to Block...
- otherwise_block: Option<BasicBlock>,
-
- // ...and the blocks for add false edges between candidates
- pre_binding_block: BasicBlock,
- next_candidate_pre_binding_block: Option<BasicBlock>,
-}
-
-#[derive(Clone, Debug)]
-struct Binding<'tcx> {
- span: Span,
- source: Place<'tcx>,
- name: Name,
- var_id: HirId,
- var_ty: Ty<'tcx>,
- mutability: Mutability,
- binding_mode: BindingMode,
-}
-
-/// Indicates that the type of `source` must be a subtype of the
-/// user-given type `user_ty`; this is basically a no-op but can
-/// influence region inference.
-#[derive(Clone, Debug)]
-struct Ascription<'tcx> {
- span: Span,
- source: Place<'tcx>,
- user_ty: PatTyProj<'tcx>,
- variance: ty::Variance,
-}
-
-#[derive(Clone, Debug)]
-pub struct MatchPair<'pat, 'tcx> {
- // this place...
- place: Place<'tcx>,
-
- // ... must match this pattern.
- pattern: &'pat Pat<'tcx>,
-}
-
-#[derive(Clone, Debug, PartialEq)]
-enum TestKind<'tcx> {
- /// Test the branches of enum.
- Switch {
- /// The enum being tested
- adt_def: &'tcx ty::AdtDef,
- /// The set of variants that we should create a branch for. We also
- /// create an additional "otherwise" case.
- variants: BitSet<VariantIdx>,
- },
-
- /// Test what value an `integer`, `bool` or `char` has.
- SwitchInt {
- /// The type of the value that we're testing.
- switch_ty: Ty<'tcx>,
- /// The (ordered) set of values that we test for.
- ///
- /// For integers and `char`s we create a branch to each of the values in
- /// `options`, as well as an "otherwise" branch for all other values, even
- /// in the (rare) case that options is exhaustive.
- ///
- /// For `bool` we always generate two edges, one for `true` and one for
- /// `false`.
- options: Vec<u128>,
- /// Reverse map used to ensure that the values in `options` are unique.
- indices: FxHashMap<&'tcx ty::Const<'tcx>, usize>,
- },
-
- /// Test for equality with value, possibly after an unsizing coercion to
- /// `ty`,
- Eq {
- value: &'tcx ty::Const<'tcx>,
- // Integer types are handled by `SwitchInt`, and constants with ADT
- // types are converted back into patterns, so this can only be `&str`,
- // `&[T]`, `f32` or `f64`.
- ty: Ty<'tcx>,
- },
-
- /// Test whether the value falls within an inclusive or exclusive range
- Range(PatRange<'tcx>),
-
- /// Test length of the slice is equal to len
- Len { len: u64, op: BinOp },
-}
-
-#[derive(Debug)]
-pub struct Test<'tcx> {
- span: Span,
- kind: TestKind<'tcx>,
-}
-
-/// ArmHasGuard is isomorphic to a boolean flag. It indicates whether
-/// a match arm has a guard expression attached to it.
-#[derive(Copy, Clone, Debug)]
-pub(crate) struct ArmHasGuard(pub bool);
-
-///////////////////////////////////////////////////////////////////////////
-// Main matching algorithm
-
-impl<'a, 'tcx> Builder<'a, 'tcx> {
- /// The main match algorithm. It begins with a set of candidates
- /// `candidates` and has the job of generating code to determine
- /// which of these candidates, if any, is the correct one. The
- /// candidates are sorted such that the first item in the list
- /// has the highest priority. When a candidate is found to match
- /// the value, we will generate a branch to the appropriate
- /// prebinding block.
- ///
- /// If we find that *NONE* of the candidates apply, we branch to the
- /// `otherwise_block`. In principle, this means that the input list was not
- /// exhaustive, though at present we sometimes are not smart enough to
- /// recognize all exhaustive inputs.
- ///
- /// It might be surprising that the input can be inexhaustive.
- /// Indeed, initially, it is not, because all matches are
- /// exhaustive in Rust. But during processing we sometimes divide
- /// up the list of candidates and recurse with a non-exhaustive
- /// list. This is important to keep the size of the generated code
- /// under control. See `test_candidates` for more details.
- ///
- /// If `fake_borrows` is Some, then places which need fake borrows
- /// will be added to it.
- fn match_candidates<'pat>(
- &mut self,
- span: Span,
- start_block: &mut Option<BasicBlock>,
- otherwise_block: Option<BasicBlock>,
- candidates: &mut [&mut Candidate<'pat, 'tcx>],
- fake_borrows: &mut Option<FxHashSet<Place<'tcx>>>,
- ) {
- debug!(
- "matched_candidate(span={:?}, candidates={:?}, start_block={:?}, otherwise_block={:?})",
- span, candidates, start_block, otherwise_block,
- );
-
- // Start by simplifying candidates. Once this process is complete, all
- // the match pairs which remain require some form of test, whether it
- // be a switch or pattern comparison.
- for candidate in &mut *candidates {
- self.simplify_candidate(candidate);
- }
-
- // The candidates are sorted by priority. Check to see whether the
- // higher priority candidates (and hence at the front of the slice)
- // have satisfied all their match pairs.
- let fully_matched = candidates.iter().take_while(|c| c.match_pairs.is_empty()).count();
- debug!("match_candidates: {:?} candidates fully matched", fully_matched);
- let (matched_candidates, unmatched_candidates) = candidates.split_at_mut(fully_matched);
-
- let block: BasicBlock = if !matched_candidates.is_empty() {
- let otherwise_block =
- self.select_matched_candidates(matched_candidates, start_block, fake_borrows);
-
- if let Some(last_otherwise_block) = otherwise_block {
- last_otherwise_block
- } else {
- // Any remaining candidates are unreachable.
- if unmatched_candidates.is_empty() {
- return;
- }
- self.cfg.start_new_block()
- }
- } else {
- *start_block.get_or_insert_with(|| self.cfg.start_new_block())
- };
-
- // If there are no candidates that still need testing, we're
- // done. Since all matches are exhaustive, execution should
- // never reach this point.
- if unmatched_candidates.is_empty() {
- let source_info = self.source_info(span);
- match otherwise_block {
- Some(otherwise) => self.cfg.goto(block, source_info, otherwise),
- None => self.cfg.terminate(block, source_info, TerminatorKind::Unreachable),
- }
- return;
- }
-
- // Test for the remaining candidates.
- self.test_candidates(span, unmatched_candidates, block, otherwise_block, fake_borrows);
- }
-
- /// Link up matched candidates. For example, if we have something like
- /// this:
- ///
- /// ...
- /// Some(x) if cond => ...
- /// Some(x) => ...
- /// Some(x) if cond => ...
- /// ...
- ///
- /// We generate real edges from:
- /// * `start_block` to the `prebinding_block` of the first pattern,
- /// * the otherwise block of the first pattern to the second pattern,
- /// * the otherwise block of the third pattern to the a block with an
- /// Unreachable terminator.
- ///
- /// As well as that we add fake edges from the otherwise blocks to the
- /// prebinding block of the next candidate in the original set of
- /// candidates.
- fn select_matched_candidates(
- &mut self,
- matched_candidates: &mut [&mut Candidate<'_, 'tcx>],
- start_block: &mut Option<BasicBlock>,
- fake_borrows: &mut Option<FxHashSet<Place<'tcx>>>,
- ) -> Option<BasicBlock> {
- debug_assert!(
- !matched_candidates.is_empty(),
- "select_matched_candidates called with no candidates",
- );
-
- // Insert a borrows of prefixes of places that are bound and are
- // behind a dereference projection.
- //
- // These borrows are taken to avoid situations like the following:
- //
- // match x[10] {
- // _ if { x = &[0]; false } => (),
- // y => (), // Out of bounds array access!
- // }
- //
- // match *x {
- // // y is bound by reference in the guard and then by copy in the
- // // arm, so y is 2 in the arm!
- // y if { y == 1 && (x = &2) == () } => y,
- // _ => 3,
- // }
- if let Some(fake_borrows) = fake_borrows {
- for Binding { source, .. } in
- matched_candidates.iter().flat_map(|candidate| &candidate.bindings)
- {
- if let Some(i) =
- source.projection.iter().rposition(|elem| *elem == ProjectionElem::Deref)
- {
- let proj_base = &source.projection[..i];
-
- fake_borrows.insert(Place {
- base: source.base.clone(),
- projection: self.hir.tcx().intern_place_elems(proj_base),
- });
- }
- }
- }
-
- let fully_matched_with_guard = matched_candidates
- .iter()
- .position(|c| c.otherwise_block.is_none())
- .unwrap_or(matched_candidates.len() - 1);
-
- let (reachable_candidates, unreachable_candidates) =
- matched_candidates.split_at_mut(fully_matched_with_guard + 1);
-
- let first_candidate = &reachable_candidates[0];
- let first_prebinding_block = first_candidate.pre_binding_block;
-
- // `goto -> first_prebinding_block` from the `start_block` if there is one.
- if let Some(start_block) = *start_block {
- let source_info = self.source_info(first_candidate.span);
- self.cfg.goto(start_block, source_info, first_prebinding_block);
- } else {
- *start_block = Some(first_prebinding_block);
- }
-
- for (first_candidate, second_candidate) in reachable_candidates.iter().tuple_windows() {
- let source_info = self.source_info(first_candidate.span);
- if let Some(otherwise_block) = first_candidate.otherwise_block {
- self.false_edges(
- otherwise_block,
- second_candidate.pre_binding_block,
- first_candidate.next_candidate_pre_binding_block,
- source_info,
- );
- } else {
- bug!("candidate other than the last has no guard");
- }
- }
-
- debug!("match_candidates: add false edges for unreachable {:?}", unreachable_candidates);
- for candidate in unreachable_candidates {
- if let Some(otherwise) = candidate.otherwise_block {
- let source_info = self.source_info(candidate.span);
- let unreachable = self.cfg.start_new_block();
- self.false_edges(
- otherwise,
- unreachable,
- candidate.next_candidate_pre_binding_block,
- source_info,
- );
- self.cfg.terminate(unreachable, source_info, TerminatorKind::Unreachable);
- }
- }
-
- let last_candidate = reachable_candidates.last().unwrap();
- if let Some(otherwise) = last_candidate.otherwise_block {
- let source_info = self.source_info(last_candidate.span);
- let block = self.cfg.start_new_block();
- self.false_edges(
- otherwise,
- block,
- last_candidate.next_candidate_pre_binding_block,
- source_info,
- );
- Some(block)
- } else {
- None
- }
- }
-
- /// This is the most subtle part of the matching algorithm. At
- /// this point, the input candidates have been fully simplified,
- /// and so we know that all remaining match-pairs require some
- /// sort of test. To decide what test to do, we take the highest
- /// priority candidate (last one in the list) and extract the
- /// first match-pair from the list. From this we decide what kind
- /// of test is needed using `test`, defined in the `test` module.
- ///
- /// *Note:* taking the first match pair is somewhat arbitrary, and
- /// we might do better here by choosing more carefully what to
- /// test.
- ///
- /// For example, consider the following possible match-pairs:
- ///
- /// 1. `x @ Some(P)` -- we will do a `Switch` to decide what variant `x` has
- /// 2. `x @ 22` -- we will do a `SwitchInt`
- /// 3. `x @ 3..5` -- we will do a range test
- /// 4. etc.
- ///
- /// Once we know what sort of test we are going to perform, this
- /// Tests may also help us with other candidates. So we walk over
- /// the candidates (from high to low priority) and check. This
- /// gives us, for each outcome of the test, a transformed list of
- /// candidates. For example, if we are testing the current
- /// variant of `x.0`, and we have a candidate `{x.0 @ Some(v), x.1
- /// @ 22}`, then we would have a resulting candidate of `{(x.0 as
- /// Some).0 @ v, x.1 @ 22}`. Note that the first match-pair is now
- /// simpler (and, in fact, irrefutable).
- ///
- /// But there may also be candidates that the test just doesn't
- /// apply to. The classical example involves wildcards:
- ///
- /// ```
- /// # let (x, y, z) = (true, true, true);
- /// match (x, y, z) {
- /// (true, _, true) => true, // (0)
- /// (_, true, _) => true, // (1)
- /// (false, false, _) => false, // (2)
- /// (true, _, false) => false, // (3)
- /// }
- /// ```
- ///
- /// In that case, after we test on `x`, there are 2 overlapping candidate
- /// sets:
- ///
- /// - If the outcome is that `x` is true, candidates 0, 1, and 3
- /// - If the outcome is that `x` is false, candidates 1 and 2
- ///
- /// Here, the traditional "decision tree" method would generate 2
- /// separate code-paths for the 2 separate cases.
- ///
- /// In some cases, this duplication can create an exponential amount of
- /// code. This is most easily seen by noticing that this method terminates
- /// with precisely the reachable arms being reachable - but that problem
- /// is trivially NP-complete:
- ///
- /// ```rust
- /// match (var0, var1, var2, var3, ..) {
- /// (true, _, _, false, true, ...) => false,
- /// (_, true, true, false, _, ...) => false,
- /// (false, _, false, false, _, ...) => false,
- /// ...
- /// _ => true
- /// }
- /// ```
- ///
- /// Here the last arm is reachable only if there is an assignment to
- /// the variables that does not match any of the literals. Therefore,
- /// compilation would take an exponential amount of time in some cases.
- ///
- /// That kind of exponential worst-case might not occur in practice, but
- /// our simplistic treatment of constants and guards would make it occur
- /// in very common situations - for example #29740:
- ///
- /// ```rust
- /// match x {
- /// "foo" if foo_guard => ...,
- /// "bar" if bar_guard => ...,
- /// "baz" if baz_guard => ...,
- /// ...
- /// }
- /// ```
- ///
- /// Here we first test the match-pair `x @ "foo"`, which is an `Eq` test.
- ///
- /// It might seem that we would end up with 2 disjoint candidate
- /// sets, consisting of the first candidate or the other 3, but our
- /// algorithm doesn't reason about "foo" being distinct from the other
- /// constants; it considers the latter arms to potentially match after
- /// both outcomes, which obviously leads to an exponential amount
- /// of tests.
- ///
- /// To avoid these kinds of problems, our algorithm tries to ensure
- /// the amount of generated tests is linear. When we do a k-way test,
- /// we return an additional "unmatched" set alongside the obvious `k`
- /// sets. When we encounter a candidate that would be present in more
- /// than one of the sets, we put it and all candidates below it into the
- /// "unmatched" set. This ensures these `k+1` sets are disjoint.
- ///
- /// After we perform our test, we branch into the appropriate candidate
- /// set and recurse with `match_candidates`. These sub-matches are
- /// obviously inexhaustive - as we discarded our otherwise set - so
- /// we set their continuation to do `match_candidates` on the
- /// "unmatched" set (which is again inexhaustive).
- ///
- /// If you apply this to the above test, you basically wind up
- /// with an if-else-if chain, testing each candidate in turn,
- /// which is precisely what we want.
- ///
- /// In addition to avoiding exponential-time blowups, this algorithm
- /// also has nice property that each guard and arm is only generated
- /// once.
- fn test_candidates<'pat, 'b, 'c>(
- &mut self,
- span: Span,
- mut candidates: &'b mut [&'c mut Candidate<'pat, 'tcx>],
- block: BasicBlock,
- mut otherwise_block: Option<BasicBlock>,
- fake_borrows: &mut Option<FxHashSet<Place<'tcx>>>,
- ) {
- // extract the match-pair from the highest priority candidate
- let match_pair = &candidates.first().unwrap().match_pairs[0];
- let mut test = self.test(match_pair);
- let match_place = match_pair.place.clone();
-
- // most of the time, the test to perform is simply a function
- // of the main candidate; but for a test like SwitchInt, we
- // may want to add cases based on the candidates that are
- // available
- match test.kind {
- TestKind::SwitchInt { switch_ty, ref mut options, ref mut indices } => {
- for candidate in candidates.iter() {
- if !self.add_cases_to_switch(
- &match_place,
- candidate,
- switch_ty,
- options,
- indices,
- ) {
- break;
- }
- }
- }
- TestKind::Switch { adt_def: _, ref mut variants } => {
- for candidate in candidates.iter() {
- if !self.add_variants_to_switch(&match_place, candidate, variants) {
- break;
- }
- }
- }
- _ => {}
- }
-
- // Insert a Shallow borrow of any places that is switched on.
- fake_borrows.as_mut().map(|fb| fb.insert(match_place.clone()));
-
- // perform the test, branching to one of N blocks. For each of
- // those N possible outcomes, create a (initially empty)
- // vector of candidates. Those are the candidates that still
- // apply if the test has that particular outcome.
- debug!("match_candidates: test={:?} match_pair={:?}", test, match_pair);
- let mut target_candidates: Vec<Vec<&mut Candidate<'pat, 'tcx>>> = vec![];
- target_candidates.resize_with(test.targets(), Default::default);
-
- let total_candidate_count = candidates.len();
-
- // Sort the candidates into the appropriate vector in
- // `target_candidates`. Note that at some point we may
- // encounter a candidate where the test is not relevant; at
- // that point, we stop sorting.
- while let Some(candidate) = candidates.first_mut() {
- if let Some(idx) = self.sort_candidate(&match_place, &test, candidate) {
- let (candidate, rest) = candidates.split_first_mut().unwrap();
- target_candidates[idx].push(candidate);
- candidates = rest;
- } else {
- break;
- }
- }
- // at least the first candidate ought to be tested
- assert!(total_candidate_count > candidates.len());
- debug!("tested_candidates: {}", total_candidate_count - candidates.len());
- debug!("untested_candidates: {}", candidates.len());
-
- // HACK(matthewjasper) This is a closure so that we can let the test
- // create its blocks before the rest of the match. This currently
- // improves the speed of llvm when optimizing long string literal
- // matches
- let make_target_blocks = move |this: &mut Self| -> Vec<BasicBlock> {
- // For each outcome of test, process the candidates that still
- // apply. Collect a list of blocks where control flow will
- // branch if one of the `target_candidate` sets is not
- // exhaustive.
- if !candidates.is_empty() {
- let remainder_start = &mut None;
- this.match_candidates(
- span,
- remainder_start,
- otherwise_block,
- candidates,
- fake_borrows,
- );
- otherwise_block = Some(remainder_start.unwrap());
- };
-
- target_candidates
- .into_iter()
- .map(|mut candidates| {
- if candidates.len() != 0 {
- let candidate_start = &mut None;
- this.match_candidates(
- span,
- candidate_start,
- otherwise_block,
- &mut *candidates,
- fake_borrows,
- );
- candidate_start.unwrap()
- } else {
- *otherwise_block.get_or_insert_with(|| {
- let unreachable = this.cfg.start_new_block();
- let source_info = this.source_info(span);
- this.cfg.terminate(
- unreachable,
- source_info,
- TerminatorKind::Unreachable,
- );
- unreachable
- })
- }
- })
- .collect()
- };
-
- self.perform_test(block, &match_place, &test, make_target_blocks);
- }
-
- /// Determine the fake borrows that are needed from a set of places that
- /// have to be stable across match guards.
- ///
- /// Returns a list of places that need a fake borrow and the temporary
- /// that's used to store the fake borrow.
- ///
- /// Match exhaustiveness checking is not able to handle the case where the
- /// place being matched on is mutated in the guards. We add "fake borrows"
- /// to the guards that prevent any mutation of the place being matched.
- /// There are a some subtleties:
- ///
- /// 1. Borrowing `*x` doesn't prevent assigning to `x`. If `x` is a shared
- /// reference, the borrow isn't even tracked. As such we have to add fake
- /// borrows of any prefixes of a place
- /// 2. We don't want `match x { _ => (), }` to conflict with mutable
- /// borrows of `x`, so we only add fake borrows for places which are
- /// bound or tested by the match.
- /// 3. We don't want the fake borrows to conflict with `ref mut` bindings,
- /// so we use a special BorrowKind for them.
- /// 4. The fake borrows may be of places in inactive variants, so it would
- /// be UB to generate code for them. They therefore have to be removed
- /// by a MIR pass run after borrow checking.
- fn calculate_fake_borrows<'b>(
- &mut self,
- fake_borrows: &'b FxHashSet<Place<'tcx>>,
- temp_span: Span,
- ) -> Vec<(Place<'tcx>, Local)> {
- let tcx = self.hir.tcx();
-
- debug!("add_fake_borrows fake_borrows = {:?}", fake_borrows);
-
- let mut all_fake_borrows = Vec::with_capacity(fake_borrows.len());
-
- // Insert a Shallow borrow of the prefixes of any fake borrows.
- for place in fake_borrows {
- let mut cursor = place.projection.as_ref();
- while let [proj_base @ .., elem] = cursor {
- cursor = proj_base;
-
- if let ProjectionElem::Deref = elem {
- // Insert a shallow borrow after a deref. For other
- // projections the borrow of prefix_cursor will
- // conflict with any mutation of base.
- all_fake_borrows.push(PlaceRef { base: &place.base, projection: proj_base });
- }
- }
-
- all_fake_borrows.push(place.as_ref());
- }
-
- // Deduplicate and ensure a deterministic order.
- all_fake_borrows.sort();
- all_fake_borrows.dedup();
-
- debug!("add_fake_borrows all_fake_borrows = {:?}", all_fake_borrows);
-
- all_fake_borrows
- .into_iter()
- .map(|matched_place_ref| {
- let matched_place = Place {
- base: matched_place_ref.base.clone(),
- projection: tcx.intern_place_elems(matched_place_ref.projection),
- };
- let fake_borrow_deref_ty = matched_place.ty(&self.local_decls, tcx).ty;
- let fake_borrow_ty = tcx.mk_imm_ref(tcx.lifetimes.re_erased, fake_borrow_deref_ty);
- let fake_borrow_temp =
- self.local_decls.push(LocalDecl::new_temp(fake_borrow_ty, temp_span));
-
- (matched_place, fake_borrow_temp)
- })
- .collect()
- }
-}
-
-///////////////////////////////////////////////////////////////////////////
-// Pat binding - used for `let` and function parameters as well.
-
-impl<'a, 'tcx> Builder<'a, 'tcx> {
- /// Initializes each of the bindings from the candidate by
- /// moving/copying/ref'ing the source as appropriate. Tests the guard, if
- /// any, and then branches to the arm. Returns the block for the case where
- /// the guard fails.
- ///
- /// Note: we do not check earlier that if there is a guard,
- /// there cannot be move bindings. We avoid a use-after-move by only
- /// moving the binding once the guard has evaluated to true (see below).
- fn bind_and_guard_matched_candidate<'pat>(
- &mut self,
- candidate: Candidate<'pat, 'tcx>,
- guard: Option<(&Guard<'tcx>, region::Scope)>,
- fake_borrows: &Vec<(Place<'tcx>, Local)>,
- scrutinee_span: Span,
- ) -> BasicBlock {
- debug!("bind_and_guard_matched_candidate(candidate={:?})", candidate);
-
- debug_assert!(candidate.match_pairs.is_empty());
-
- let candidate_source_info = self.source_info(candidate.span);
-
- let mut block = candidate.pre_binding_block;
-
- // If we are adding our own statements, then we need a fresh block.
- let create_fresh_block = candidate.next_candidate_pre_binding_block.is_some()
- || !candidate.bindings.is_empty()
- || !candidate.ascriptions.is_empty()
- || guard.is_some();
-
- if create_fresh_block {
- let fresh_block = self.cfg.start_new_block();
- self.false_edges(
- block,
- fresh_block,
- candidate.next_candidate_pre_binding_block,
- candidate_source_info,
- );
- block = fresh_block;
- self.ascribe_types(block, &candidate.ascriptions);
- } else {
- return block;
- }
-
- // rust-lang/rust#27282: The `autoref` business deserves some
- // explanation here.
- //
- // The intent of the `autoref` flag is that when it is true,
- // then any pattern bindings of type T will map to a `&T`
- // within the context of the guard expression, but will
- // continue to map to a `T` in the context of the arm body. To
- // avoid surfacing this distinction in the user source code
- // (which would be a severe change to the language and require
- // far more revision to the compiler), when `autoref` is true,
- // then any occurrence of the identifier in the guard
- // expression will automatically get a deref op applied to it.
- //
- // So an input like:
- //
- // ```
- // let place = Foo::new();
- // match place { foo if inspect(foo)
- // => feed(foo), ... }
- // ```
- //
- // will be treated as if it were really something like:
- //
- // ```
- // let place = Foo::new();
- // match place { Foo { .. } if { let tmp1 = &place; inspect(*tmp1) }
- // => { let tmp2 = place; feed(tmp2) }, ... }
- //
- // And an input like:
- //
- // ```
- // let place = Foo::new();
- // match place { ref mut foo if inspect(foo)
- // => feed(foo), ... }
- // ```
- //
- // will be treated as if it were really something like:
- //
- // ```
- // let place = Foo::new();
- // match place { Foo { .. } if { let tmp1 = & &mut place; inspect(*tmp1) }
- // => { let tmp2 = &mut place; feed(tmp2) }, ... }
- // ```
- //
- // In short, any pattern binding will always look like *some*
- // kind of `&T` within the guard at least in terms of how the
- // MIR-borrowck views it, and this will ensure that guard
- // expressions cannot mutate their the match inputs via such
- // bindings. (It also ensures that guard expressions can at
- // most *copy* values from such bindings; non-Copy things
- // cannot be moved via pattern bindings in guard expressions.)
- //
- // ----
- //
- // Implementation notes (under assumption `autoref` is true).
- //
- // To encode the distinction above, we must inject the
- // temporaries `tmp1` and `tmp2`.
- //
- // There are two cases of interest: binding by-value, and binding by-ref.
- //
- // 1. Binding by-value: Things are simple.
- //
- // * Establishing `tmp1` creates a reference into the
- // matched place. This code is emitted by
- // bind_matched_candidate_for_guard.
- //
- // * `tmp2` is only initialized "lazily", after we have
- // checked the guard. Thus, the code that can trigger
- // moves out of the candidate can only fire after the
- // guard evaluated to true. This initialization code is
- // emitted by bind_matched_candidate_for_arm.
- //
- // 2. Binding by-reference: Things are tricky.
- //
- // * Here, the guard expression wants a `&&` or `&&mut`
- // into the original input. This means we need to borrow
- // the reference that we create for the arm.
- // * So we eagerly create the reference for the arm and then take a
- // reference to that.
- if let Some((guard, region_scope)) = guard {
- let tcx = self.hir.tcx();
-
- self.bind_matched_candidate_for_guard(block, &candidate.bindings);
- let guard_frame = GuardFrame {
- locals: candidate
- .bindings
- .iter()
- .map(|b| GuardFrameLocal::new(b.var_id, b.binding_mode))
- .collect(),
- };
- debug!("entering guard building context: {:?}", guard_frame);
- self.guard_context.push(guard_frame);
-
- let re_erased = tcx.lifetimes.re_erased;
- let scrutinee_source_info = self.source_info(scrutinee_span);
- for (place, temp) in fake_borrows {
- let borrow = Rvalue::Ref(re_erased, BorrowKind::Shallow, place.clone());
- self.cfg.push_assign(block, scrutinee_source_info, &Place::from(*temp), borrow);
- }
-
- // the block to branch to if the guard fails; if there is no
- // guard, this block is simply unreachable
- let guard = match guard {
- Guard::If(e) => self.hir.mirror(e.clone()),
- };
- let source_info = self.source_info(guard.span);
- let guard_end = self.source_info(tcx.sess.source_map().end_point(guard.span));
- let (post_guard_block, otherwise_post_guard_block) =
- self.test_bool(block, guard, source_info);
- let guard_frame = self.guard_context.pop().unwrap();
- debug!("Exiting guard building context with locals: {:?}", guard_frame);
-
- for &(_, temp) in fake_borrows {
- let cause = FakeReadCause::ForMatchGuard;
- self.cfg.push_fake_read(post_guard_block, guard_end, cause, Place::from(temp));
- }
-
- self.exit_scope(
- source_info.span,
- region_scope,
- otherwise_post_guard_block,
- candidate.otherwise_block.unwrap(),
- );
-
- // We want to ensure that the matched candidates are bound
- // after we have confirmed this candidate *and* any
- // associated guard; Binding them on `block` is too soon,
- // because that would be before we've checked the result
- // from the guard.
- //
- // But binding them on the arm is *too late*, because
- // then all of the candidates for a single arm would be
- // bound in the same place, that would cause a case like:
- //
- // ```rust
- // match (30, 2) {
- // (mut x, 1) | (2, mut x) if { true } => { ... }
- // ... // ^^^^^^^ (this is `arm_block`)
- // }
- // ```
- //
- // would yield a `arm_block` something like:
- //
- // ```
- // StorageLive(_4); // _4 is `x`
- // _4 = &mut (_1.0: i32); // this is handling `(mut x, 1)` case
- // _4 = &mut (_1.1: i32); // this is handling `(2, mut x)` case
- // ```
- //
- // and that is clearly not correct.
- let by_value_bindings = candidate.bindings.iter().filter(|binding| {
- if let BindingMode::ByValue = binding.binding_mode { true } else { false }
- });
- // Read all of the by reference bindings to ensure that the
- // place they refer to can't be modified by the guard.
- for binding in by_value_bindings.clone() {
- let local_id = self.var_local_id(binding.var_id, RefWithinGuard);
- let cause = FakeReadCause::ForGuardBinding;
- self.cfg.push_fake_read(post_guard_block, guard_end, cause, Place::from(local_id));
- }
- self.bind_matched_candidate_for_arm_body(post_guard_block, by_value_bindings);
-
- post_guard_block
- } else {
- assert!(candidate.otherwise_block.is_none());
- // (Here, it is not too early to bind the matched
- // candidate on `block`, because there is no guard result
- // that we have to inspect before we bind them.)
- self.bind_matched_candidate_for_arm_body(block, &candidate.bindings);
- block
- }
- }
-
- /// Append `AscribeUserType` statements onto the end of `block`
- /// for each ascription
- fn ascribe_types(&mut self, block: BasicBlock, ascriptions: &[Ascription<'tcx>]) {
- for ascription in ascriptions {
- let source_info = self.source_info(ascription.span);
-
- debug!(
- "adding user ascription at span {:?} of place {:?} and {:?}",
- source_info.span, ascription.source, ascription.user_ty,
- );
-
- let user_ty = ascription.user_ty.clone().user_ty(
- &mut self.canonical_user_type_annotations,
- ascription.source.ty(&self.local_decls, self.hir.tcx()).ty,
- source_info.span,
- );
- self.cfg.push(
- block,
- Statement {
- source_info,
- kind: StatementKind::AscribeUserType(
- box (ascription.source.clone(), user_ty),
- ascription.variance,
- ),
- },
- );
- }
- }
-
- fn bind_matched_candidate_for_guard(&mut self, block: BasicBlock, bindings: &[Binding<'tcx>]) {
- debug!("bind_matched_candidate_for_guard(block={:?}, bindings={:?})", block, bindings);
-
- // Assign each of the bindings. Since we are binding for a
- // guard expression, this will never trigger moves out of the
- // candidate.
- let re_erased = self.hir.tcx().lifetimes.re_erased;
- for binding in bindings {
- let source_info = self.source_info(binding.span);
-
- // For each pattern ident P of type T, `ref_for_guard` is
- // a reference R: &T pointing to the location matched by
- // the pattern, and every occurrence of P within a guard
- // denotes *R.
- let ref_for_guard =
- self.storage_live_binding(block, binding.var_id, binding.span, RefWithinGuard);
- match binding.binding_mode {
- BindingMode::ByValue => {
- let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, binding.source.clone());
- self.cfg.push_assign(block, source_info, &ref_for_guard, rvalue);
- }
- BindingMode::ByRef(borrow_kind) => {
- let value_for_arm = self.storage_live_binding(
- block,
- binding.var_id,
- binding.span,
- OutsideGuard,
- );
-
- let rvalue = Rvalue::Ref(re_erased, borrow_kind, binding.source.clone());
- self.cfg.push_assign(block, source_info, &value_for_arm, rvalue);
- let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, value_for_arm);
- self.cfg.push_assign(block, source_info, &ref_for_guard, rvalue);
- }
- }
- }
- }
-
- fn bind_matched_candidate_for_arm_body<'b>(
- &mut self,
- block: BasicBlock,
- bindings: impl IntoIterator<Item = &'b Binding<'tcx>>,
- ) where
- 'tcx: 'b,
- {
- debug!("bind_matched_candidate_for_arm_body(block={:?})", block);
-
- let re_erased = self.hir.tcx().lifetimes.re_erased;
- // Assign each of the bindings. This may trigger moves out of the candidate.
- for binding in bindings {
- let source_info = self.source_info(binding.span);
- let local =
- self.storage_live_binding(block, binding.var_id, binding.span, OutsideGuard);
- self.schedule_drop_for_binding(binding.var_id, binding.span, OutsideGuard);
- let rvalue = match binding.binding_mode {
- BindingMode::ByValue => {
- Rvalue::Use(self.consume_by_copy_or_move(binding.source.clone()))
- }
- BindingMode::ByRef(borrow_kind) => {
- Rvalue::Ref(re_erased, borrow_kind, binding.source.clone())
- }
- };
- self.cfg.push_assign(block, source_info, &local, rvalue);
- }
- }
-
- /// Each binding (`ref mut var`/`ref var`/`mut var`/`var`, where the bound
- /// `var` has type `T` in the arm body) in a pattern maps to 2 locals. The
- /// first local is a binding for occurrences of `var` in the guard, which
- /// will have type `&T`. The second local is a binding for occurrences of
- /// `var` in the arm body, which will have type `T`.
- fn declare_binding(
- &mut self,
- source_info: SourceInfo,
- visibility_scope: SourceScope,
- mutability: Mutability,
- name: Name,
- mode: BindingMode,
- var_id: HirId,
- var_ty: Ty<'tcx>,
- user_ty: UserTypeProjections,
- has_guard: ArmHasGuard,
- opt_match_place: Option<(Option<Place<'tcx>>, Span)>,
- pat_span: Span,
- ) {
- debug!(
- "declare_binding(var_id={:?}, name={:?}, mode={:?}, var_ty={:?}, \
- visibility_scope={:?}, source_info={:?})",
- var_id, name, mode, var_ty, visibility_scope, source_info
- );
-
- let tcx = self.hir.tcx();
- let debug_source_info = SourceInfo { span: source_info.span, scope: visibility_scope };
- let binding_mode = match mode {
- BindingMode::ByValue => ty::BindingMode::BindByValue(mutability.into()),
- BindingMode::ByRef(_) => ty::BindingMode::BindByReference(mutability.into()),
- };
- debug!("declare_binding: user_ty={:?}", user_ty);
- let local = LocalDecl::<'tcx> {
- mutability,
- ty: var_ty,
- user_ty,
- source_info,
- internal: false,
- is_block_tail: None,
- local_info: LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
- binding_mode,
- // hypothetically, `visit_bindings` could try to unzip
- // an outermost hir::Ty as we descend, matching up
- // idents in pat; but complex w/ unclear UI payoff.
- // Instead, just abandon providing diagnostic info.
- opt_ty_info: None,
- opt_match_place,
- pat_span,
- }))),
- };
- let for_arm_body = self.local_decls.push(local);
- self.var_debug_info.push(VarDebugInfo {
- name,
- source_info: debug_source_info,
- place: for_arm_body.into(),
- });
- let locals = if has_guard.0 {
- let ref_for_guard = self.local_decls.push(LocalDecl::<'tcx> {
- // This variable isn't mutated but has a name, so has to be
- // immutable to avoid the unused mut lint.
- mutability: Mutability::Not,
- ty: tcx.mk_imm_ref(tcx.lifetimes.re_erased, var_ty),
- user_ty: UserTypeProjections::none(),
- source_info,
- internal: false,
- is_block_tail: None,
- local_info: LocalInfo::User(ClearCrossCrate::Set(BindingForm::RefForGuard)),
- });
- self.var_debug_info.push(VarDebugInfo {
- name,
- source_info: debug_source_info,
- place: ref_for_guard.into(),
- });
- LocalsForNode::ForGuard { ref_for_guard, for_arm_body }
- } else {
- LocalsForNode::One(for_arm_body)
- };
- debug!("declare_binding: vars={:?}", locals);
- self.var_indices.insert(var_id, locals);
- }
-}
+++ /dev/null
-//! Simplifying Candidates
-//!
-//! *Simplifying* a match pair `place @ pattern` means breaking it down
-//! into bindings or other, simpler match pairs. For example:
-//!
-//! - `place @ (P1, P2)` can be simplified to `[place.0 @ P1, place.1 @ P2]`
-//! - `place @ x` can be simplified to `[]` by binding `x` to `place`
-//!
-//! The `simplify_candidate` routine just repeatedly applies these
-//! sort of simplifications until there is nothing left to
-//! simplify. Match pairs cannot be simplified if they require some
-//! sort of test: for example, testing which variant an enum is, or
-//! testing a value against a constant.
-
-use crate::build::matches::{Ascription, Binding, Candidate, MatchPair};
-use crate::build::Builder;
-use crate::hair::{self, *};
-use rustc::mir::interpret::truncate;
-use rustc::ty;
-use rustc::ty::layout::{Integer, IntegerExt, Size};
-use rustc_hir::RangeEnd;
-use syntax::attr::{SignedInt, UnsignedInt};
-
-use std::mem;
-
-impl<'a, 'tcx> Builder<'a, 'tcx> {
- pub fn simplify_candidate<'pat>(&mut self, candidate: &mut Candidate<'pat, 'tcx>) {
- // repeatedly simplify match pairs until fixed point is reached
- loop {
- let match_pairs = mem::take(&mut candidate.match_pairs);
- let mut changed = false;
- for match_pair in match_pairs {
- match self.simplify_match_pair(match_pair, candidate) {
- Ok(()) => {
- changed = true;
- }
- Err(match_pair) => {
- candidate.match_pairs.push(match_pair);
- }
- }
- }
- if !changed {
- return; // if we were not able to simplify any, done.
- }
- }
- }
-
- /// Tries to simplify `match_pair`, returning `Ok(())` if
- /// successful. If successful, new match pairs and bindings will
- /// have been pushed into the candidate. If no simplification is
- /// possible, `Err` is returned and no changes are made to
- /// candidate.
- fn simplify_match_pair<'pat>(
- &mut self,
- match_pair: MatchPair<'pat, 'tcx>,
- candidate: &mut Candidate<'pat, 'tcx>,
- ) -> Result<(), MatchPair<'pat, 'tcx>> {
- let tcx = self.hir.tcx();
- match *match_pair.pattern.kind {
- PatKind::AscribeUserType {
- ref subpattern,
- ascription: hair::pattern::Ascription { variance, ref user_ty, user_ty_span },
- } => {
- // Apply the type ascription to the value at `match_pair.place`, which is the
- // value being matched, taking the variance field into account.
- candidate.ascriptions.push(Ascription {
- span: user_ty_span,
- user_ty: user_ty.clone(),
- source: match_pair.place.clone(),
- variance,
- });
-
- candidate.match_pairs.push(MatchPair::new(match_pair.place, subpattern));
-
- Ok(())
- }
-
- PatKind::Wild => {
- // nothing left to do
- Ok(())
- }
-
- PatKind::Binding { name, mutability, mode, var, ty, ref subpattern } => {
- candidate.bindings.push(Binding {
- name,
- mutability,
- span: match_pair.pattern.span,
- source: match_pair.place.clone(),
- var_id: var,
- var_ty: ty,
- binding_mode: mode,
- });
-
- if let Some(subpattern) = subpattern.as_ref() {
- // this is the `x @ P` case; have to keep matching against `P` now
- candidate.match_pairs.push(MatchPair::new(match_pair.place, subpattern));
- }
-
- Ok(())
- }
-
- PatKind::Constant { .. } => {
- // FIXME normalize patterns when possible
- Err(match_pair)
- }
-
- PatKind::Range(PatRange { lo, hi, end }) => {
- let (range, bias) = match lo.ty.kind {
- ty::Char => {
- (Some(('\u{0000}' as u128, '\u{10FFFF}' as u128, Size::from_bits(32))), 0)
- }
- ty::Int(ity) => {
- let size = Integer::from_attr(&tcx, SignedInt(ity)).size();
- let max = truncate(u128::max_value(), size);
- let bias = 1u128 << (size.bits() - 1);
- (Some((0, max, size)), bias)
- }
- ty::Uint(uty) => {
- let size = Integer::from_attr(&tcx, UnsignedInt(uty)).size();
- let max = truncate(u128::max_value(), size);
- (Some((0, max, size)), 0)
- }
- _ => (None, 0),
- };
- if let Some((min, max, sz)) = range {
- if let (Some(lo), Some(hi)) = (lo.val.try_to_bits(sz), hi.val.try_to_bits(sz)) {
- // We want to compare ranges numerically, but the order of the bitwise
- // representation of signed integers does not match their numeric order.
- // Thus, to correct the ordering, we need to shift the range of signed
- // integers to correct the comparison. This is achieved by XORing with a
- // bias (see pattern/_match.rs for another pertinent example of this
- // pattern).
- let (lo, hi) = (lo ^ bias, hi ^ bias);
- if lo <= min && (hi > max || hi == max && end == RangeEnd::Included) {
- // Irrefutable pattern match.
- return Ok(());
- }
- }
- }
- Err(match_pair)
- }
-
- PatKind::Slice { ref prefix, ref slice, ref suffix } => {
- if prefix.is_empty() && slice.is_some() && suffix.is_empty() {
- // irrefutable
- self.prefix_slice_suffix(
- &mut candidate.match_pairs,
- &match_pair.place,
- prefix,
- slice.as_ref(),
- suffix,
- );
- Ok(())
- } else {
- Err(match_pair)
- }
- }
-
- PatKind::Variant { adt_def, substs, variant_index, ref subpatterns } => {
- let irrefutable = adt_def.variants.iter_enumerated().all(|(i, v)| {
- i == variant_index || {
- self.hir.tcx().features().exhaustive_patterns
- && !v
- .uninhabited_from(self.hir.tcx(), substs, adt_def.adt_kind())
- .is_empty()
- }
- }) && (adt_def.did.is_local()
- || !adt_def.is_variant_list_non_exhaustive());
- if irrefutable {
- let place = tcx.mk_place_downcast(match_pair.place, adt_def, variant_index);
- candidate.match_pairs.extend(self.field_match_pairs(place, subpatterns));
- Ok(())
- } else {
- Err(match_pair)
- }
- }
-
- PatKind::Array { ref prefix, ref slice, ref suffix } => {
- self.prefix_slice_suffix(
- &mut candidate.match_pairs,
- &match_pair.place,
- prefix,
- slice.as_ref(),
- suffix,
- );
- Ok(())
- }
-
- PatKind::Leaf { ref subpatterns } => {
- // tuple struct, match subpats (if any)
- candidate.match_pairs.extend(self.field_match_pairs(match_pair.place, subpatterns));
- Ok(())
- }
-
- PatKind::Deref { ref subpattern } => {
- let place = tcx.mk_place_deref(match_pair.place);
- candidate.match_pairs.push(MatchPair::new(place, subpattern));
- Ok(())
- }
-
- PatKind::Or { .. } => Err(match_pair),
- }
- }
-}
+++ /dev/null
-// Testing candidates
-//
-// After candidates have been simplified, the only match pairs that
-// remain are those that require some sort of test. The functions here
-// identify what tests are needed, perform the tests, and then filter
-// the candidates based on the result.
-
-use crate::build::matches::{Candidate, MatchPair, Test, TestKind};
-use crate::build::Builder;
-use crate::hair::pattern::compare_const_vals;
-use crate::hair::*;
-use rustc::mir::*;
-use rustc::ty::layout::VariantIdx;
-use rustc::ty::util::IntTypeExt;
-use rustc::ty::{self, adjustment::PointerCast, Ty};
-use rustc_data_structures::fx::FxHashMap;
-use rustc_hir::RangeEnd;
-use rustc_index::bit_set::BitSet;
-use rustc_span::symbol::sym;
-
-use std::cmp::Ordering;
-
-impl<'a, 'tcx> Builder<'a, 'tcx> {
- /// Identifies what test is needed to decide if `match_pair` is applicable.
- ///
- /// It is a bug to call this with a simplifiable pattern.
- pub fn test<'pat>(&mut self, match_pair: &MatchPair<'pat, 'tcx>) -> Test<'tcx> {
- match *match_pair.pattern.kind {
- PatKind::Variant { ref adt_def, substs: _, variant_index: _, subpatterns: _ } => Test {
- span: match_pair.pattern.span,
- kind: TestKind::Switch {
- adt_def,
- variants: BitSet::new_empty(adt_def.variants.len()),
- },
- },
-
- PatKind::Constant { .. } if is_switch_ty(match_pair.pattern.ty) => {
- // For integers, we use a `SwitchInt` match, which allows
- // us to handle more cases.
- Test {
- span: match_pair.pattern.span,
- kind: TestKind::SwitchInt {
- switch_ty: match_pair.pattern.ty,
-
- // these maps are empty to start; cases are
- // added below in add_cases_to_switch
- options: vec![],
- indices: Default::default(),
- },
- }
- }
-
- PatKind::Constant { value } => Test {
- span: match_pair.pattern.span,
- kind: TestKind::Eq { value, ty: match_pair.pattern.ty.clone() },
- },
-
- PatKind::Range(range) => {
- assert_eq!(range.lo.ty, match_pair.pattern.ty);
- assert_eq!(range.hi.ty, match_pair.pattern.ty);
- Test { span: match_pair.pattern.span, kind: TestKind::Range(range) }
- }
-
- PatKind::Slice { ref prefix, ref slice, ref suffix } => {
- let len = prefix.len() + suffix.len();
- let op = if slice.is_some() { BinOp::Ge } else { BinOp::Eq };
- Test {
- span: match_pair.pattern.span,
- kind: TestKind::Len { len: len as u64, op: op },
- }
- }
-
- PatKind::Or { .. } => self
- .hir
- .tcx()
- .sess
- .span_fatal(match_pair.pattern.span, "or-patterns are not fully implemented yet"),
-
- PatKind::AscribeUserType { .. }
- | PatKind::Array { .. }
- | PatKind::Wild
- | PatKind::Binding { .. }
- | PatKind::Leaf { .. }
- | PatKind::Deref { .. } => self.error_simplifyable(match_pair),
- }
- }
-
- pub fn add_cases_to_switch<'pat>(
- &mut self,
- test_place: &Place<'tcx>,
- candidate: &Candidate<'pat, 'tcx>,
- switch_ty: Ty<'tcx>,
- options: &mut Vec<u128>,
- indices: &mut FxHashMap<&'tcx ty::Const<'tcx>, usize>,
- ) -> bool {
- let match_pair = match candidate.match_pairs.iter().find(|mp| mp.place == *test_place) {
- Some(match_pair) => match_pair,
- _ => {
- return false;
- }
- };
-
- match *match_pair.pattern.kind {
- PatKind::Constant { value } => {
- indices.entry(value).or_insert_with(|| {
- options.push(value.eval_bits(self.hir.tcx(), self.hir.param_env, switch_ty));
- options.len() - 1
- });
- true
- }
- PatKind::Variant { .. } => {
- panic!("you should have called add_variants_to_switch instead!");
- }
- PatKind::Range(range) => {
- // Check that none of the switch values are in the range.
- self.values_not_contained_in_range(range, indices).unwrap_or(false)
- }
- PatKind::Slice { .. }
- | PatKind::Array { .. }
- | PatKind::Wild
- | PatKind::Or { .. }
- | PatKind::Binding { .. }
- | PatKind::AscribeUserType { .. }
- | PatKind::Leaf { .. }
- | PatKind::Deref { .. } => {
- // don't know how to add these patterns to a switch
- false
- }
- }
- }
-
- pub fn add_variants_to_switch<'pat>(
- &mut self,
- test_place: &Place<'tcx>,
- candidate: &Candidate<'pat, 'tcx>,
- variants: &mut BitSet<VariantIdx>,
- ) -> bool {
- let match_pair = match candidate.match_pairs.iter().find(|mp| mp.place == *test_place) {
- Some(match_pair) => match_pair,
- _ => {
- return false;
- }
- };
-
- match *match_pair.pattern.kind {
- PatKind::Variant { adt_def: _, variant_index, .. } => {
- // We have a pattern testing for variant `variant_index`
- // set the corresponding index to true
- variants.insert(variant_index);
- true
- }
- _ => {
- // don't know how to add these patterns to a switch
- false
- }
- }
- }
-
- pub fn perform_test(
- &mut self,
- block: BasicBlock,
- place: &Place<'tcx>,
- test: &Test<'tcx>,
- make_target_blocks: impl FnOnce(&mut Self) -> Vec<BasicBlock>,
- ) {
- debug!(
- "perform_test({:?}, {:?}: {:?}, {:?})",
- block,
- place,
- place.ty(&self.local_decls, self.hir.tcx()),
- test
- );
-
- let source_info = self.source_info(test.span);
- match test.kind {
- TestKind::Switch { adt_def, ref variants } => {
- let target_blocks = make_target_blocks(self);
- // Variants is a BitVec of indexes into adt_def.variants.
- let num_enum_variants = adt_def.variants.len();
- let used_variants = variants.count();
- debug_assert_eq!(target_blocks.len(), num_enum_variants + 1);
- let otherwise_block = *target_blocks.last().unwrap();
- let mut targets = Vec::with_capacity(used_variants + 1);
- let mut values = Vec::with_capacity(used_variants);
- let tcx = self.hir.tcx();
- for (idx, discr) in adt_def.discriminants(tcx) {
- if variants.contains(idx) {
- debug_assert_ne!(
- target_blocks[idx.index()],
- otherwise_block,
- "no canididates for tested discriminant: {:?}",
- discr,
- );
- values.push(discr.val);
- targets.push(target_blocks[idx.index()]);
- } else {
- debug_assert_eq!(
- target_blocks[idx.index()],
- otherwise_block,
- "found canididates for untested discriminant: {:?}",
- discr,
- );
- }
- }
- targets.push(otherwise_block);
- debug!(
- "num_enum_variants: {}, tested variants: {:?}, variants: {:?}",
- num_enum_variants, values, variants
- );
- let discr_ty = adt_def.repr.discr_type().to_ty(tcx);
- let discr = self.temp(discr_ty, test.span);
- self.cfg.push_assign(
- block,
- source_info,
- &discr,
- Rvalue::Discriminant(place.clone()),
- );
- assert_eq!(values.len() + 1, targets.len());
- self.cfg.terminate(
- block,
- source_info,
- TerminatorKind::SwitchInt {
- discr: Operand::Move(discr),
- switch_ty: discr_ty,
- values: From::from(values),
- targets,
- },
- );
- }
-
- TestKind::SwitchInt { switch_ty, ref options, indices: _ } => {
- let target_blocks = make_target_blocks(self);
- let terminator = if switch_ty.kind == ty::Bool {
- assert!(options.len() > 0 && options.len() <= 2);
- if let [first_bb, second_bb] = *target_blocks {
- let (true_bb, false_bb) = match options[0] {
- 1 => (first_bb, second_bb),
- 0 => (second_bb, first_bb),
- v => span_bug!(test.span, "expected boolean value but got {:?}", v),
- };
- TerminatorKind::if_(
- self.hir.tcx(),
- Operand::Copy(place.clone()),
- true_bb,
- false_bb,
- )
- } else {
- bug!("`TestKind::SwitchInt` on `bool` should have two targets")
- }
- } 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.clone()),
- switch_ty,
- values: options.clone().into(),
- targets: target_blocks,
- }
- };
- self.cfg.terminate(block, source_info, terminator);
- }
-
- TestKind::Eq { value, ty } => {
- if !ty.is_scalar() {
- // Use `PartialEq::eq` instead of `BinOp::Eq`
- // (the binop can only handle primitives)
- self.non_scalar_compare(
- block,
- make_target_blocks,
- source_info,
- value,
- place,
- ty,
- );
- } else {
- if let [success, fail] = *make_target_blocks(self) {
- assert_eq!(value.ty, ty);
- let expect = self.literal_operand(test.span, value);
- let val = Operand::Copy(place.clone());
- self.compare(block, success, fail, source_info, BinOp::Eq, expect, val);
- } else {
- bug!("`TestKind::Eq` should have two target blocks");
- }
- }
- }
-
- TestKind::Range(PatRange { ref lo, ref hi, ref end }) => {
- let lower_bound_success = self.cfg.start_new_block();
- let target_blocks = make_target_blocks(self);
-
- // Test `val` by computing `lo <= val && val <= hi`, using primitive comparisons.
- let lo = self.literal_operand(test.span, lo);
- let hi = self.literal_operand(test.span, hi);
- let val = Operand::Copy(place.clone());
-
- if let [success, fail] = *target_blocks {
- self.compare(
- block,
- lower_bound_success,
- fail,
- source_info,
- BinOp::Le,
- lo,
- val.clone(),
- );
- let op = match *end {
- RangeEnd::Included => BinOp::Le,
- RangeEnd::Excluded => BinOp::Lt,
- };
- self.compare(lower_bound_success, success, fail, source_info, op, val, hi);
- } else {
- bug!("`TestKind::Range` should have two target blocks");
- }
- }
-
- TestKind::Len { len, op } => {
- let target_blocks = make_target_blocks(self);
-
- let usize_ty = self.hir.usize_ty();
- let actual = self.temp(usize_ty, test.span);
-
- // actual = len(place)
- self.cfg.push_assign(block, source_info, &actual, Rvalue::Len(place.clone()));
-
- // expected = <N>
- let expected = self.push_usize(block, source_info, len);
-
- if let [true_bb, false_bb] = *target_blocks {
- // result = actual == expected OR result = actual < expected
- // branch based on result
- self.compare(
- block,
- true_bb,
- false_bb,
- source_info,
- op,
- Operand::Move(actual),
- Operand::Move(expected),
- );
- } else {
- bug!("`TestKind::Len` should have two target blocks");
- }
- }
- }
- }
-
- /// Compare using the provided built-in comparison operator
- fn compare(
- &mut self,
- block: BasicBlock,
- success_block: BasicBlock,
- fail_block: BasicBlock,
- source_info: SourceInfo,
- op: BinOp,
- left: Operand<'tcx>,
- right: Operand<'tcx>,
- ) {
- let bool_ty = self.hir.bool_ty();
- let result = self.temp(bool_ty, source_info.span);
-
- // result = op(left, right)
- self.cfg.push_assign(block, source_info, &result, Rvalue::BinaryOp(op, left, right));
-
- // branch based on result
- self.cfg.terminate(
- block,
- source_info,
- TerminatorKind::if_(self.hir.tcx(), Operand::Move(result), success_block, fail_block),
- );
- }
-
- /// Compare two `&T` values using `<T as std::compare::PartialEq>::eq`
- fn non_scalar_compare(
- &mut self,
- block: BasicBlock,
- make_target_blocks: impl FnOnce(&mut Self) -> Vec<BasicBlock>,
- source_info: SourceInfo,
- value: &'tcx ty::Const<'tcx>,
- place: &Place<'tcx>,
- mut ty: Ty<'tcx>,
- ) {
- use rustc::middle::lang_items::EqTraitLangItem;
-
- let mut expect = self.literal_operand(source_info.span, value);
- let mut val = Operand::Copy(place.clone());
-
- // If we're using `b"..."` as a pattern, we need to insert an
- // unsizing coercion, as the byte string has the type `&[u8; N]`.
- //
- // We want to do this even when the scrutinee is a reference to an
- // array, so we can call `<[u8]>::eq` rather than having to find an
- // `<[u8; N]>::eq`.
- let unsize = |ty: Ty<'tcx>| match ty.kind {
- ty::Ref(region, rty, _) => match rty.kind {
- ty::Array(inner_ty, n) => Some((region, inner_ty, n)),
- _ => None,
- },
- _ => None,
- };
- let opt_ref_ty = unsize(ty);
- let opt_ref_test_ty = unsize(value.ty);
- match (opt_ref_ty, opt_ref_test_ty) {
- // nothing to do, neither is an array
- (None, None) => {}
- (Some((region, elem_ty, _)), _) | (None, Some((region, elem_ty, _))) => {
- let tcx = self.hir.tcx();
- // make both a slice
- ty = tcx.mk_imm_ref(region, tcx.mk_slice(elem_ty));
- if opt_ref_ty.is_some() {
- let temp = self.temp(ty, source_info.span);
- self.cfg.push_assign(
- block,
- source_info,
- &temp,
- Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), val, ty),
- );
- val = Operand::Move(temp);
- }
- if opt_ref_test_ty.is_some() {
- let slice = self.temp(ty, source_info.span);
- self.cfg.push_assign(
- block,
- source_info,
- &slice,
- Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), expect, ty),
- );
- expect = Operand::Move(slice);
- }
- }
- }
-
- let deref_ty = match ty.kind {
- ty::Ref(_, deref_ty, _) => deref_ty,
- _ => bug!("non_scalar_compare called on non-reference type: {}", ty),
- };
-
- let eq_def_id = self.hir.tcx().require_lang_item(EqTraitLangItem, None);
- let method = self.hir.trait_method(eq_def_id, sym::eq, deref_ty, &[deref_ty.into()]);
-
- let bool_ty = self.hir.bool_ty();
- let eq_result = self.temp(bool_ty, source_info.span);
- let eq_block = self.cfg.start_new_block();
- let cleanup = self.diverge_cleanup();
- self.cfg.terminate(
- block,
- source_info,
- TerminatorKind::Call {
- func: Operand::Constant(box Constant {
- span: source_info.span,
-
- // FIXME(#54571): This constant comes from user input (a
- // constant in a pattern). Are there forms where users can add
- // type annotations here? For example, an associated constant?
- // Need to experiment.
- user_ty: None,
-
- literal: method,
- }),
- args: vec![val, expect],
- destination: Some((eq_result.clone(), eq_block)),
- cleanup: Some(cleanup),
- from_hir_call: false,
- },
- );
-
- if let [success_block, fail_block] = *make_target_blocks(self) {
- // check the result
- self.cfg.terminate(
- eq_block,
- source_info,
- TerminatorKind::if_(
- self.hir.tcx(),
- Operand::Move(eq_result),
- success_block,
- fail_block,
- ),
- );
- } else {
- bug!("`TestKind::Eq` should have two target blocks")
- }
- }
-
- /// Given that we are performing `test` against `test_place`, this job
- /// sorts out what the status of `candidate` will be after the test. See
- /// `test_candidates` for the usage of this function. The returned index is
- /// the index that this candidate should be placed in the
- /// `target_candidates` vec. The candidate may be modified to update its
- /// `match_pairs`.
- ///
- /// So, for example, if this candidate is `x @ Some(P0)` and the `Test` is
- /// a variant test, then we would modify the candidate to be `(x as
- /// Option).0 @ P0` and return the index corresponding to the variant
- /// `Some`.
- ///
- /// However, in some cases, the test may just not be relevant to candidate.
- /// For example, suppose we are testing whether `foo.x == 22`, but in one
- /// match arm we have `Foo { x: _, ... }`... in that case, the test for
- /// what value `x` has has no particular relevance to this candidate. In
- /// such cases, this function just returns None without doing anything.
- /// This is used by the overall `match_candidates` algorithm to structure
- /// the match as a whole. See `match_candidates` for more details.
- ///
- /// FIXME(#29623). In some cases, we have some tricky choices to make. for
- /// example, if we are testing that `x == 22`, but the candidate is `x @
- /// 13..55`, what should we do? In the event that the test is true, we know
- /// that the candidate applies, but in the event of false, we don't know
- /// that it *doesn't* apply. For now, we return false, indicate that the
- /// test does not apply to this candidate, but it might be we can get
- /// tighter match code if we do something a bit different.
- pub fn sort_candidate<'pat>(
- &mut self,
- test_place: &Place<'tcx>,
- test: &Test<'tcx>,
- candidate: &mut Candidate<'pat, 'tcx>,
- ) -> Option<usize> {
- // Find the match_pair for this place (if any). At present,
- // afaik, there can be at most one. (In the future, if we
- // adopted a more general `@` operator, there might be more
- // than one, but it'd be very unusual to have two sides that
- // both require tests; you'd expect one side to be simplified
- // away.)
- let (match_pair_index, match_pair) =
- candidate.match_pairs.iter().enumerate().find(|&(_, mp)| mp.place == *test_place)?;
-
- match (&test.kind, &*match_pair.pattern.kind) {
- // If we are performing a variant switch, then this
- // informs variant patterns, but nothing else.
- (
- &TestKind::Switch { adt_def: tested_adt_def, .. },
- &PatKind::Variant { adt_def, variant_index, ref subpatterns, .. },
- ) => {
- assert_eq!(adt_def, tested_adt_def);
- self.candidate_after_variant_switch(
- match_pair_index,
- adt_def,
- variant_index,
- subpatterns,
- candidate,
- );
- Some(variant_index.as_usize())
- }
-
- (&TestKind::Switch { .. }, _) => None,
-
- // If we are performing a switch over integers, then this informs integer
- // equality, but nothing else.
- //
- // FIXME(#29623) we could use PatKind::Range to rule
- // things out here, in some cases.
- (
- &TestKind::SwitchInt { switch_ty: _, options: _, ref indices },
- &PatKind::Constant { ref value },
- ) if is_switch_ty(match_pair.pattern.ty) => {
- let index = indices[value];
- self.candidate_without_match_pair(match_pair_index, candidate);
- Some(index)
- }
-
- (
- &TestKind::SwitchInt { switch_ty: _, ref options, ref indices },
- &PatKind::Range(range),
- ) => {
- let not_contained =
- self.values_not_contained_in_range(range, indices).unwrap_or(false);
-
- if not_contained {
- // No switch values are contained in the pattern range,
- // so the pattern can be matched only if this test fails.
- let otherwise = options.len();
- Some(otherwise)
- } else {
- None
- }
- }
-
- (&TestKind::SwitchInt { .. }, _) => None,
-
- (
- &TestKind::Len { len: test_len, op: BinOp::Eq },
- &PatKind::Slice { ref prefix, ref slice, ref suffix },
- ) => {
- let pat_len = (prefix.len() + suffix.len()) as u64;
- match (test_len.cmp(&pat_len), slice) {
- (Ordering::Equal, &None) => {
- // on true, min_len = len = $actual_length,
- // on false, len != $actual_length
- self.candidate_after_slice_test(
- match_pair_index,
- candidate,
- prefix,
- slice.as_ref(),
- suffix,
- );
- Some(0)
- }
- (Ordering::Less, _) => {
- // test_len < pat_len. If $actual_len = test_len,
- // then $actual_len < pat_len and we don't have
- // enough elements.
- Some(1)
- }
- (Ordering::Equal, &Some(_)) | (Ordering::Greater, &Some(_)) => {
- // This can match both if $actual_len = test_len >= pat_len,
- // and if $actual_len > test_len. We can't advance.
- None
- }
- (Ordering::Greater, &None) => {
- // test_len != pat_len, so if $actual_len = test_len, then
- // $actual_len != pat_len.
- Some(1)
- }
- }
- }
-
- (
- &TestKind::Len { len: test_len, op: BinOp::Ge },
- &PatKind::Slice { ref prefix, ref slice, ref suffix },
- ) => {
- // the test is `$actual_len >= test_len`
- let pat_len = (prefix.len() + suffix.len()) as u64;
- match (test_len.cmp(&pat_len), slice) {
- (Ordering::Equal, &Some(_)) => {
- // $actual_len >= test_len = pat_len,
- // so we can match.
- self.candidate_after_slice_test(
- match_pair_index,
- candidate,
- prefix,
- slice.as_ref(),
- suffix,
- );
- Some(0)
- }
- (Ordering::Less, _) | (Ordering::Equal, &None) => {
- // test_len <= pat_len. If $actual_len < test_len,
- // then it is also < pat_len, so the test passing is
- // necessary (but insufficient).
- Some(0)
- }
- (Ordering::Greater, &None) => {
- // test_len > pat_len. If $actual_len >= test_len > pat_len,
- // then we know we won't have a match.
- Some(1)
- }
- (Ordering::Greater, &Some(_)) => {
- // test_len < pat_len, and is therefore less
- // strict. This can still go both ways.
- None
- }
- }
- }
-
- (&TestKind::Range(test), &PatKind::Range(pat)) => {
- if test == pat {
- self.candidate_without_match_pair(match_pair_index, candidate);
- return Some(0);
- }
-
- let no_overlap = (|| {
- use rustc_hir::RangeEnd::*;
- use std::cmp::Ordering::*;
-
- let tcx = self.hir.tcx();
-
- let test_ty = test.lo.ty;
- let lo = compare_const_vals(tcx, test.lo, pat.hi, self.hir.param_env, test_ty)?;
- let hi = compare_const_vals(tcx, test.hi, pat.lo, self.hir.param_env, test_ty)?;
-
- match (test.end, pat.end, lo, hi) {
- // pat < test
- (_, _, Greater, _) |
- (_, Excluded, Equal, _) |
- // pat > test
- (_, _, _, Less) |
- (Excluded, _, _, Equal) => Some(true),
- _ => Some(false),
- }
- })();
-
- if let Some(true) = no_overlap {
- // Testing range does not overlap with pattern range,
- // so the pattern can be matched only if this test fails.
- Some(1)
- } else {
- None
- }
- }
-
- (&TestKind::Range(range), &PatKind::Constant { value }) => {
- if let Some(false) = self.const_range_contains(range, value) {
- // `value` is not contained in the testing range,
- // so `value` can be matched only if this test fails.
- Some(1)
- } else {
- None
- }
- }
-
- (&TestKind::Range { .. }, _) => None,
-
- (&TestKind::Eq { .. }, _) | (&TestKind::Len { .. }, _) => {
- // These are all binary tests.
- //
- // FIXME(#29623) we can be more clever here
- let pattern_test = self.test(&match_pair);
- if pattern_test.kind == test.kind {
- self.candidate_without_match_pair(match_pair_index, candidate);
- Some(0)
- } else {
- None
- }
- }
- }
- }
-
- fn candidate_without_match_pair(
- &mut self,
- match_pair_index: usize,
- candidate: &mut Candidate<'_, 'tcx>,
- ) {
- candidate.match_pairs.remove(match_pair_index);
- }
-
- fn candidate_after_slice_test<'pat>(
- &mut self,
- match_pair_index: usize,
- candidate: &mut Candidate<'pat, 'tcx>,
- prefix: &'pat [Pat<'tcx>],
- opt_slice: Option<&'pat Pat<'tcx>>,
- suffix: &'pat [Pat<'tcx>],
- ) {
- let removed_place = candidate.match_pairs.remove(match_pair_index).place;
- self.prefix_slice_suffix(
- &mut candidate.match_pairs,
- &removed_place,
- prefix,
- opt_slice,
- suffix,
- );
- }
-
- fn candidate_after_variant_switch<'pat>(
- &mut self,
- match_pair_index: usize,
- adt_def: &'tcx ty::AdtDef,
- variant_index: VariantIdx,
- subpatterns: &'pat [FieldPat<'tcx>],
- candidate: &mut Candidate<'pat, 'tcx>,
- ) {
- let match_pair = candidate.match_pairs.remove(match_pair_index);
- let tcx = self.hir.tcx();
-
- // So, if we have a match-pattern like `x @ Enum::Variant(P1, P2)`,
- // we want to create a set of derived match-patterns like
- // `(x as Variant).0 @ P1` and `(x as Variant).1 @ P1`.
- let elem = ProjectionElem::Downcast(
- Some(adt_def.variants[variant_index].ident.name),
- variant_index,
- );
- let downcast_place = tcx.mk_place_elem(match_pair.place, elem); // `(x as Variant)`
- let consequent_match_pairs = subpatterns.iter().map(|subpattern| {
- // e.g., `(x as Variant).0`
- let place =
- tcx.mk_place_field(downcast_place.clone(), subpattern.field, subpattern.pattern.ty);
- // e.g., `(x as Variant).0 @ P1`
- MatchPair::new(place, &subpattern.pattern)
- });
-
- candidate.match_pairs.extend(consequent_match_pairs);
- }
-
- fn error_simplifyable<'pat>(&mut self, match_pair: &MatchPair<'pat, 'tcx>) -> ! {
- span_bug!(match_pair.pattern.span, "simplifyable pattern found: {:?}", match_pair.pattern)
- }
-
- fn const_range_contains(
- &self,
- range: PatRange<'tcx>,
- value: &'tcx ty::Const<'tcx>,
- ) -> Option<bool> {
- use std::cmp::Ordering::*;
-
- let tcx = self.hir.tcx();
-
- let a = compare_const_vals(tcx, range.lo, value, self.hir.param_env, range.lo.ty)?;
- let b = compare_const_vals(tcx, value, range.hi, self.hir.param_env, range.lo.ty)?;
-
- match (b, range.end) {
- (Less, _) | (Equal, RangeEnd::Included) if a != Greater => Some(true),
- _ => Some(false),
- }
- }
-
- fn values_not_contained_in_range(
- &self,
- range: PatRange<'tcx>,
- indices: &FxHashMap<&'tcx ty::Const<'tcx>, usize>,
- ) -> Option<bool> {
- for &val in indices.keys() {
- if self.const_range_contains(range, val)? {
- return Some(false);
- }
- }
-
- Some(true)
- }
-}
-
-impl Test<'_> {
- pub(super) fn targets(&self) -> usize {
- match self.kind {
- TestKind::Eq { .. } | TestKind::Range(_) | TestKind::Len { .. } => 2,
- TestKind::Switch { adt_def, .. } => {
- // While the switch that we generate doesn't test for all
- // variants, we have a target for each variant and the
- // otherwise case, and we make sure that all of the cases not
- // specified have the same block.
- adt_def.variants.len() + 1
- }
- TestKind::SwitchInt { switch_ty, ref options, .. } => {
- if switch_ty.is_bool() {
- // `bool` is special cased in `perform_test` to always
- // branch to two blocks.
- 2
- } else {
- options.len() + 1
- }
- }
- }
- }
-}
-
-fn is_switch_ty(ty: Ty<'_>) -> bool {
- ty.is_integral() || ty.is_char() || ty.is_bool()
-}
+++ /dev/null
-use crate::build::matches::MatchPair;
-use crate::build::Builder;
-use crate::hair::*;
-use rustc::mir::*;
-use rustc::ty;
-use smallvec::SmallVec;
-use std::convert::TryInto;
-use std::u32;
-
-impl<'a, 'tcx> Builder<'a, 'tcx> {
- pub fn field_match_pairs<'pat>(
- &mut self,
- place: Place<'tcx>,
- subpatterns: &'pat [FieldPat<'tcx>],
- ) -> Vec<MatchPair<'pat, 'tcx>> {
- subpatterns
- .iter()
- .map(|fieldpat| {
- let place = self.hir.tcx().mk_place_field(
- place.clone(),
- fieldpat.field,
- fieldpat.pattern.ty,
- );
- MatchPair::new(place, &fieldpat.pattern)
- })
- .collect()
- }
-
- pub fn prefix_slice_suffix<'pat>(
- &mut self,
- match_pairs: &mut SmallVec<[MatchPair<'pat, 'tcx>; 1]>,
- place: &Place<'tcx>,
- prefix: &'pat [Pat<'tcx>],
- opt_slice: Option<&'pat Pat<'tcx>>,
- suffix: &'pat [Pat<'tcx>],
- ) {
- let tcx = self.hir.tcx();
- let (min_length, exact_size) = match place.ty(&self.local_decls, tcx).ty.kind {
- ty::Array(_, length) => {
- (length.eval_usize(tcx, self.hir.param_env).try_into().unwrap(), true)
- }
- _ => ((prefix.len() + suffix.len()).try_into().unwrap(), false),
- };
-
- match_pairs.extend(prefix.iter().enumerate().map(|(idx, subpattern)| {
- let elem =
- ProjectionElem::ConstantIndex { offset: idx as u32, min_length, from_end: false };
- let place = tcx.mk_place_elem(place.clone(), elem);
- MatchPair::new(place, subpattern)
- }));
-
- if let Some(subslice_pat) = opt_slice {
- let suffix_len = suffix.len() as u32;
- let subslice = tcx.mk_place_elem(
- place.clone(),
- ProjectionElem::Subslice {
- from: prefix.len() as u32,
- to: if exact_size { min_length - suffix_len } else { suffix_len },
- from_end: !exact_size,
- },
- );
- match_pairs.push(MatchPair::new(subslice, subslice_pat));
- }
-
- match_pairs.extend(suffix.iter().rev().enumerate().map(|(idx, subpattern)| {
- let end_offset = (idx + 1) as u32;
- let elem = ProjectionElem::ConstantIndex {
- offset: if exact_size { min_length - end_offset } else { end_offset },
- min_length,
- from_end: !exact_size,
- };
- let place = tcx.mk_place_elem(place.clone(), elem);
- MatchPair::new(place, subpattern)
- }));
- }
-
- /// Creates a false edge to `imaginary_target` and a real edge to
- /// real_target. If `imaginary_target` is none, or is the same as the real
- /// target, a Goto is generated instead to simplify the generated MIR.
- pub fn false_edges(
- &mut self,
- from_block: BasicBlock,
- real_target: BasicBlock,
- imaginary_target: Option<BasicBlock>,
- source_info: SourceInfo,
- ) {
- match imaginary_target {
- Some(target) if target != real_target => {
- self.cfg.terminate(
- from_block,
- source_info,
- TerminatorKind::FalseEdges { real_target, imaginary_target: target },
- );
- }
- _ => self.cfg.goto(from_block, source_info, real_target),
- }
- }
-}
-
-impl<'pat, 'tcx> MatchPair<'pat, 'tcx> {
- pub fn new(place: Place<'tcx>, pattern: &'pat Pat<'tcx>) -> MatchPair<'pat, 'tcx> {
- MatchPair { place, pattern }
- }
-}
+++ /dev/null
-//! Miscellaneous builder routines that are not specific to building any particular
-//! kind of thing.
-
-use crate::build::Builder;
-
-use rustc::ty::{self, Ty};
-
-use rustc::mir::*;
-use rustc_span::{Span, DUMMY_SP};
-
-impl<'a, 'tcx> Builder<'a, 'tcx> {
- /// Adds a new temporary value of type `ty` storing the result of
- /// evaluating `expr`.
- ///
- /// N.B., **No cleanup is scheduled for this temporary.** You should
- /// call `schedule_drop` once the temporary is initialized.
- pub fn temp(&mut self, ty: Ty<'tcx>, span: Span) -> Place<'tcx> {
- let temp = self.local_decls.push(LocalDecl::new_temp(ty, span));
- let place = Place::from(temp);
- debug!("temp: created temp {:?} with type {:?}", place, self.local_decls[temp].ty);
- place
- }
-
- /// Convenience function for creating a literal operand, one
- /// without any user type annotation.
- pub fn literal_operand(&mut self, span: Span, literal: &'tcx ty::Const<'tcx>) -> Operand<'tcx> {
- let constant = box Constant { span, user_ty: None, literal };
- Operand::Constant(constant)
- }
-
- pub fn unit_rvalue(&mut self) -> Rvalue<'tcx> {
- Rvalue::Aggregate(box AggregateKind::Tuple, vec![])
- }
-
- // Returns a zero literal operand for the appropriate type, works for
- // bool, char and integers.
- pub fn zero_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> {
- let literal = ty::Const::from_bits(self.hir.tcx(), 0, ty::ParamEnv::empty().and(ty));
-
- self.literal_operand(span, literal)
- }
-
- pub fn push_usize(
- &mut self,
- block: BasicBlock,
- source_info: SourceInfo,
- value: u64,
- ) -> Place<'tcx> {
- let usize_ty = self.hir.usize_ty();
- let temp = self.temp(usize_ty, source_info.span);
- self.cfg.push_assign_constant(
- block,
- source_info,
- &temp,
- Constant {
- span: source_info.span,
- user_ty: None,
- literal: self.hir.usize_literal(value),
- },
- );
- temp
- }
-
- pub fn consume_by_copy_or_move(&self, place: Place<'tcx>) -> Operand<'tcx> {
- let tcx = self.hir.tcx();
- let ty = place.ty(&self.local_decls, tcx).ty;
- if !self.hir.type_is_copy_modulo_regions(ty, DUMMY_SP) {
- Operand::Move(place)
- } else {
- Operand::Copy(place)
- }
- }
-}
+++ /dev/null
-use crate::build;
-use crate::build::scope::DropKind;
-use crate::hair::cx::Cx;
-use crate::hair::{BindingMode, LintLevel, PatKind};
-use crate::transform::MirSource;
-use crate::util as mir_util;
-use rustc::middle::lang_items;
-use rustc::middle::region;
-use rustc::mir::*;
-use rustc::ty::subst::Subst;
-use rustc::ty::{self, Ty, TyCtxt};
-use rustc_hir as hir;
-use rustc_hir::def_id::DefId;
-use rustc_hir::{GeneratorKind, HirIdMap, Node};
-use rustc_index::vec::{Idx, IndexVec};
-use rustc_span::symbol::kw;
-use rustc_span::Span;
-use rustc_target::spec::abi::Abi;
-use rustc_target::spec::PanicStrategy;
-use std::u32;
-use syntax::attr::{self, UnwindAttr};
-
-use super::lints;
-
-/// Construct the MIR for a given `DefId`.
-pub fn mir_build(tcx: TyCtxt<'_>, def_id: DefId) -> BodyAndCache<'_> {
- let id = tcx.hir().as_local_hir_id(def_id).unwrap();
-
- // Figure out what primary body this item has.
- let (body_id, return_ty_span) = match tcx.hir().get(id) {
- Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(_, decl, body_id, _, _), .. }) => {
- (*body_id, decl.output.span())
- }
- Node::Item(hir::Item {
- kind: hir::ItemKind::Fn(hir::FnSig { decl, .. }, _, body_id),
- ..
- })
- | Node::ImplItem(hir::ImplItem {
- kind: hir::ImplItemKind::Method(hir::FnSig { decl, .. }, body_id),
- ..
- })
- | Node::TraitItem(hir::TraitItem {
- kind:
- hir::TraitItemKind::Method(hir::FnSig { decl, .. }, hir::TraitMethod::Provided(body_id)),
- ..
- }) => (*body_id, decl.output.span()),
- Node::Item(hir::Item { kind: hir::ItemKind::Static(ty, _, body_id), .. })
- | Node::Item(hir::Item { kind: hir::ItemKind::Const(ty, body_id), .. })
- | Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(ty, body_id), .. })
- | Node::TraitItem(hir::TraitItem {
- kind: hir::TraitItemKind::Const(ty, Some(body_id)),
- ..
- }) => (*body_id, ty.span),
- Node::AnonConst(hir::AnonConst { body, hir_id, .. }) => (*body, tcx.hir().span(*hir_id)),
-
- _ => span_bug!(tcx.hir().span(id), "can't build MIR for {:?}", def_id),
- };
-
- tcx.infer_ctxt().enter(|infcx| {
- let cx = Cx::new(&infcx, id);
- let body = if cx.tables().tainted_by_errors {
- build::construct_error(cx, body_id)
- } else if cx.body_owner_kind.is_fn_or_closure() {
- // fetch the fully liberated fn signature (that is, all bound
- // types/lifetimes replaced)
- let fn_sig = cx.tables().liberated_fn_sigs()[id].clone();
- let fn_def_id = tcx.hir().local_def_id(id);
-
- let ty = tcx.type_of(fn_def_id);
- let mut abi = fn_sig.abi;
- let implicit_argument = match ty.kind {
- ty::Closure(..) => {
- // HACK(eddyb) Avoid having RustCall on closures,
- // as it adds unnecessary (and wrong) auto-tupling.
- abi = Abi::Rust;
- Some(ArgInfo(liberated_closure_env_ty(tcx, id, body_id), None, None, None))
- }
- ty::Generator(..) => {
- let gen_ty = tcx.body_tables(body_id).node_type(id);
- Some(ArgInfo(gen_ty, None, None, None))
- }
- _ => None,
- };
-
- let safety = match fn_sig.unsafety {
- hir::Unsafety::Normal => Safety::Safe,
- hir::Unsafety::Unsafe => Safety::FnUnsafe,
- };
-
- let body = tcx.hir().body(body_id);
- let explicit_arguments = body.params.iter().enumerate().map(|(index, arg)| {
- let owner_id = tcx.hir().body_owner(body_id);
- let opt_ty_info;
- let self_arg;
- if let Some(ref fn_decl) = tcx.hir().fn_decl_by_hir_id(owner_id) {
- opt_ty_info = fn_decl.inputs.get(index).map(|ty| ty.span);
- self_arg = if index == 0 && fn_decl.implicit_self.has_implicit_self() {
- match fn_decl.implicit_self {
- hir::ImplicitSelfKind::Imm => Some(ImplicitSelfKind::Imm),
- hir::ImplicitSelfKind::Mut => Some(ImplicitSelfKind::Mut),
- hir::ImplicitSelfKind::ImmRef => Some(ImplicitSelfKind::ImmRef),
- hir::ImplicitSelfKind::MutRef => Some(ImplicitSelfKind::MutRef),
- _ => None,
- }
- } else {
- None
- };
- } else {
- opt_ty_info = None;
- self_arg = None;
- }
-
- // C-variadic fns also have a `VaList` input that's not listed in `fn_sig`
- // (as it's created inside the body itself, not passed in from outside).
- let ty = if fn_sig.c_variadic && index == fn_sig.inputs().len() {
- let va_list_did =
- tcx.require_lang_item(lang_items::VaListTypeLangItem, Some(arg.span));
- let region = tcx.mk_region(ty::ReScope(region::Scope {
- id: body.value.hir_id.local_id,
- data: region::ScopeData::CallSite,
- }));
-
- tcx.type_of(va_list_did).subst(tcx, &[region.into()])
- } else {
- fn_sig.inputs()[index]
- };
-
- ArgInfo(ty, opt_ty_info, Some(&arg), self_arg)
- });
-
- let arguments = implicit_argument.into_iter().chain(explicit_arguments);
-
- let (yield_ty, return_ty) = if body.generator_kind.is_some() {
- let gen_sig = match ty.kind {
- ty::Generator(gen_def_id, gen_substs, ..) => {
- gen_substs.as_generator().sig(gen_def_id, tcx)
- }
- _ => span_bug!(tcx.hir().span(id), "generator w/o generator type: {:?}", ty),
- };
- (Some(gen_sig.yield_ty), gen_sig.return_ty)
- } else {
- (None, fn_sig.output())
- };
-
- let mut mir = build::construct_fn(
- cx,
- id,
- arguments,
- safety,
- abi,
- return_ty,
- return_ty_span,
- body,
- );
- mir.yield_ty = yield_ty;
- mir
- } else {
- // Get the revealed type of this const. This is *not* the adjusted
- // type of its body, which may be a subtype of this type. For
- // example:
- //
- // fn foo(_: &()) {}
- // static X: fn(&'static ()) = foo;
- //
- // The adjusted type of the body of X is `for<'a> fn(&'a ())` which
- // is not the same as the type of X. We need the type of the return
- // place to be the type of the constant because NLL typeck will
- // equate them.
-
- let return_ty = cx.tables().node_type(id);
-
- build::construct_const(cx, body_id, return_ty, return_ty_span)
- };
-
- mir_util::dump_mir(tcx, None, "mir_map", &0, MirSource::item(def_id), &body, |_, _| Ok(()));
-
- lints::check(tcx, &body, def_id);
-
- let mut body = BodyAndCache::new(body);
- body.ensure_predecessors();
- body
- })
-}
-
-///////////////////////////////////////////////////////////////////////////
-// BuildMir -- walks a crate, looking for fn items and methods to build MIR from
-
-fn liberated_closure_env_ty(
- tcx: TyCtxt<'_>,
- closure_expr_id: hir::HirId,
- body_id: hir::BodyId,
-) -> Ty<'_> {
- let closure_ty = tcx.body_tables(body_id).node_type(closure_expr_id);
-
- let (closure_def_id, closure_substs) = match closure_ty.kind {
- ty::Closure(closure_def_id, closure_substs) => (closure_def_id, closure_substs),
- _ => bug!("closure expr does not have closure type: {:?}", closure_ty),
- };
-
- let closure_env_ty = tcx.closure_env_ty(closure_def_id, closure_substs).unwrap();
- tcx.liberate_late_bound_regions(closure_def_id, &closure_env_ty)
-}
-
-#[derive(Debug, PartialEq, Eq)]
-pub enum BlockFrame {
- /// Evaluation is currently within a statement.
- ///
- /// Examples include:
- /// 1. `EXPR;`
- /// 2. `let _ = EXPR;`
- /// 3. `let x = EXPR;`
- Statement {
- /// If true, then statement discards result from evaluating
- /// the expression (such as examples 1 and 2 above).
- ignores_expr_result: bool,
- },
-
- /// Evaluation is currently within the tail expression of a block.
- ///
- /// Example: `{ STMT_1; STMT_2; EXPR }`
- TailExpr {
- /// If true, then the surrounding context of the block ignores
- /// the result of evaluating the block's tail expression.
- ///
- /// Example: `let _ = { STMT_1; EXPR };`
- tail_result_is_ignored: bool,
- },
-
- /// Generic mark meaning that the block occurred as a subexpression
- /// where the result might be used.
- ///
- /// Examples: `foo(EXPR)`, `match EXPR { ... }`
- SubExpr,
-}
-
-impl BlockFrame {
- fn is_tail_expr(&self) -> bool {
- match *self {
- BlockFrame::TailExpr { .. } => true,
-
- BlockFrame::Statement { .. } | BlockFrame::SubExpr => false,
- }
- }
- fn is_statement(&self) -> bool {
- match *self {
- BlockFrame::Statement { .. } => true,
-
- BlockFrame::TailExpr { .. } | BlockFrame::SubExpr => false,
- }
- }
-}
-
-#[derive(Debug)]
-struct BlockContext(Vec<BlockFrame>);
-
-struct Builder<'a, 'tcx> {
- hir: Cx<'a, 'tcx>,
- cfg: CFG<'tcx>,
-
- fn_span: Span,
- arg_count: usize,
- generator_kind: Option<GeneratorKind>,
-
- /// The current set of scopes, updated as we traverse;
- /// see the `scope` module for more details.
- scopes: scope::Scopes<'tcx>,
-
- /// The block-context: each time we build the code within an hair::Block,
- /// we push a frame here tracking whether we are building a statement or
- /// if we are pushing the tail expression of the block. This is used to
- /// embed information in generated temps about whether they were created
- /// for a block tail expression or not.
- ///
- /// It would be great if we could fold this into `self.scopes`
- /// somehow, but right now I think that is very tightly tied to
- /// the code generation in ways that we cannot (or should not)
- /// start just throwing new entries onto that vector in order to
- /// distinguish the context of EXPR1 from the context of EXPR2 in
- /// `{ STMTS; EXPR1 } + EXPR2`.
- block_context: BlockContext,
-
- /// The current unsafe block in scope, even if it is hidden by
- /// a `PushUnsafeBlock`.
- unpushed_unsafe: Safety,
-
- /// The number of `push_unsafe_block` levels in scope.
- push_unsafe_count: usize,
-
- /// The vector of all scopes that we have created thus far;
- /// we track this for debuginfo later.
- source_scopes: IndexVec<SourceScope, SourceScopeData>,
- source_scope: SourceScope,
-
- /// The guard-context: each time we build the guard expression for
- /// a match arm, we push onto this stack, and then pop when we
- /// finish building it.
- guard_context: Vec<GuardFrame>,
-
- /// Maps `HirId`s of variable bindings to the `Local`s created for them.
- /// (A match binding can have two locals; the 2nd is for the arm's guard.)
- var_indices: HirIdMap<LocalsForNode>,
- local_decls: IndexVec<Local, LocalDecl<'tcx>>,
- canonical_user_type_annotations: ty::CanonicalUserTypeAnnotations<'tcx>,
- upvar_mutbls: Vec<Mutability>,
- unit_temp: Option<Place<'tcx>>,
-
- var_debug_info: Vec<VarDebugInfo<'tcx>>,
-
- /// Cached block with the `RESUME` terminator; this is created
- /// when first set of cleanups are built.
- cached_resume_block: Option<BasicBlock>,
- /// Cached block with the `RETURN` terminator.
- cached_return_block: Option<BasicBlock>,
- /// Cached block with the `UNREACHABLE` terminator.
- cached_unreachable_block: Option<BasicBlock>,
-}
-
-impl<'a, 'tcx> Builder<'a, 'tcx> {
- fn is_bound_var_in_guard(&self, id: hir::HirId) -> bool {
- self.guard_context.iter().any(|frame| frame.locals.iter().any(|local| local.id == id))
- }
-
- fn var_local_id(&self, id: hir::HirId, for_guard: ForGuard) -> Local {
- self.var_indices[&id].local_id(for_guard)
- }
-}
-
-impl BlockContext {
- fn new() -> Self {
- BlockContext(vec![])
- }
- fn push(&mut self, bf: BlockFrame) {
- self.0.push(bf);
- }
- fn pop(&mut self) -> Option<BlockFrame> {
- self.0.pop()
- }
-
- /// Traverses the frames on the `BlockContext`, searching for either
- /// the first block-tail expression frame with no intervening
- /// statement frame.
- ///
- /// Notably, this skips over `SubExpr` frames; this method is
- /// meant to be used in the context of understanding the
- /// relationship of a temp (created within some complicated
- /// expression) with its containing expression, and whether the
- /// value of that *containing expression* (not the temp!) is
- /// ignored.
- fn currently_in_block_tail(&self) -> Option<BlockTailInfo> {
- for bf in self.0.iter().rev() {
- match bf {
- BlockFrame::SubExpr => continue,
- BlockFrame::Statement { .. } => break,
- &BlockFrame::TailExpr { tail_result_is_ignored } => {
- return Some(BlockTailInfo { tail_result_is_ignored });
- }
- }
- }
-
- return None;
- }
-
- /// Looks at the topmost frame on the BlockContext and reports
- /// whether its one that would discard a block tail result.
- ///
- /// Unlike `currently_within_ignored_tail_expression`, this does
- /// *not* skip over `SubExpr` frames: here, we want to know
- /// whether the block result itself is discarded.
- fn currently_ignores_tail_results(&self) -> bool {
- match self.0.last() {
- // no context: conservatively assume result is read
- None => false,
-
- // sub-expression: block result feeds into some computation
- Some(BlockFrame::SubExpr) => false,
-
- // otherwise: use accumulated is_ignored state.
- Some(BlockFrame::TailExpr { tail_result_is_ignored: ignored })
- | Some(BlockFrame::Statement { ignores_expr_result: ignored }) => *ignored,
- }
- }
-}
-
-#[derive(Debug)]
-enum LocalsForNode {
- /// In the usual case, a `HirId` for an identifier maps to at most
- /// one `Local` declaration.
- One(Local),
-
- /// The exceptional case is identifiers in a match arm's pattern
- /// that are referenced in a guard of that match arm. For these,
- /// we have `2` Locals.
- ///
- /// * `for_arm_body` is the Local used in the arm body (which is
- /// just like the `One` case above),
- ///
- /// * `ref_for_guard` is the Local used in the arm's guard (which
- /// is a reference to a temp that is an alias of
- /// `for_arm_body`).
- ForGuard { ref_for_guard: Local, for_arm_body: Local },
-}
-
-#[derive(Debug)]
-struct GuardFrameLocal {
- id: hir::HirId,
-}
-
-impl GuardFrameLocal {
- fn new(id: hir::HirId, _binding_mode: BindingMode) -> Self {
- GuardFrameLocal { id: id }
- }
-}
-
-#[derive(Debug)]
-struct GuardFrame {
- /// These are the id's of names that are bound by patterns of the
- /// arm of *this* guard.
- ///
- /// (Frames higher up the stack will have the id's bound in arms
- /// further out, such as in a case like:
- ///
- /// match E1 {
- /// P1(id1) if (... (match E2 { P2(id2) if ... => B2 })) => B1,
- /// }
- ///
- /// here, when building for FIXME.
- locals: Vec<GuardFrameLocal>,
-}
-
-/// `ForGuard` indicates whether we are talking about:
-/// 1. The variable for use outside of guard expressions, or
-/// 2. The temp that holds reference to (1.), which is actually what the
-/// guard expressions see.
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-enum ForGuard {
- RefWithinGuard,
- OutsideGuard,
-}
-
-impl LocalsForNode {
- fn local_id(&self, for_guard: ForGuard) -> Local {
- match (self, for_guard) {
- (&LocalsForNode::One(local_id), ForGuard::OutsideGuard)
- | (
- &LocalsForNode::ForGuard { ref_for_guard: local_id, .. },
- ForGuard::RefWithinGuard,
- )
- | (&LocalsForNode::ForGuard { for_arm_body: local_id, .. }, ForGuard::OutsideGuard) => {
- local_id
- }
-
- (&LocalsForNode::One(_), ForGuard::RefWithinGuard) => {
- bug!("anything with one local should never be within a guard.")
- }
- }
- }
-}
-
-struct CFG<'tcx> {
- basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
-}
-
-rustc_index::newtype_index! {
- pub struct ScopeId { .. }
-}
-
-///////////////////////////////////////////////////////////////////////////
-/// The `BlockAnd` "monad" packages up the new basic block along with a
-/// produced value (sometimes just unit, of course). The `unpack!`
-/// macro (and methods below) makes working with `BlockAnd` much more
-/// convenient.
-
-#[must_use = "if you don't use one of these results, you're leaving a dangling edge"]
-struct BlockAnd<T>(BasicBlock, T);
-
-trait BlockAndExtension {
- fn and<T>(self, v: T) -> BlockAnd<T>;
- fn unit(self) -> BlockAnd<()>;
-}
-
-impl BlockAndExtension for BasicBlock {
- fn and<T>(self, v: T) -> BlockAnd<T> {
- BlockAnd(self, v)
- }
-
- fn unit(self) -> BlockAnd<()> {
- BlockAnd(self, ())
- }
-}
-
-/// Update a block pointer and return the value.
-/// Use it like `let x = unpack!(block = self.foo(block, foo))`.
-macro_rules! unpack {
- ($x:ident = $c:expr) => {{
- let BlockAnd(b, v) = $c;
- $x = b;
- v
- }};
-
- ($c:expr) => {{
- let BlockAnd(b, ()) = $c;
- b
- }};
-}
-
-fn should_abort_on_panic(tcx: TyCtxt<'_>, fn_def_id: DefId, _abi: Abi) -> bool {
- // Validate `#[unwind]` syntax regardless of platform-specific panic strategy.
- let attrs = &tcx.get_attrs(fn_def_id);
- let unwind_attr = attr::find_unwind_attr(Some(tcx.sess.diagnostic()), attrs);
-
- // We never unwind, so it's not relevant to stop an unwind.
- if tcx.sess.panic_strategy() != PanicStrategy::Unwind {
- return false;
- }
-
- // We cannot add landing pads, so don't add one.
- if tcx.sess.no_landing_pads() {
- return false;
- }
-
- // This is a special case: some functions have a C abi but are meant to
- // unwind anyway. Don't stop them.
- match unwind_attr {
- None => false, // FIXME(#58794); should be `!(abi == Abi::Rust || abi == Abi::RustCall)`
- Some(UnwindAttr::Allowed) => false,
- Some(UnwindAttr::Aborts) => true,
- }
-}
-
-///////////////////////////////////////////////////////////////////////////
-/// the main entry point for building MIR for a function
-
-struct ArgInfo<'tcx>(
- Ty<'tcx>,
- Option<Span>,
- Option<&'tcx hir::Param<'tcx>>,
- Option<ImplicitSelfKind>,
-);
-
-fn construct_fn<'a, 'tcx, A>(
- hir: Cx<'a, 'tcx>,
- fn_id: hir::HirId,
- arguments: A,
- safety: Safety,
- abi: Abi,
- return_ty: Ty<'tcx>,
- return_ty_span: Span,
- body: &'tcx hir::Body<'tcx>,
-) -> Body<'tcx>
-where
- A: Iterator<Item = ArgInfo<'tcx>>,
-{
- let arguments: Vec<_> = arguments.collect();
-
- let tcx = hir.tcx();
- let tcx_hir = tcx.hir();
- let span = tcx_hir.span(fn_id);
-
- let fn_def_id = tcx_hir.local_def_id(fn_id);
-
- let mut builder = Builder::new(
- hir,
- span,
- arguments.len(),
- safety,
- return_ty,
- return_ty_span,
- body.generator_kind,
- );
-
- let call_site_scope =
- region::Scope { id: body.value.hir_id.local_id, data: region::ScopeData::CallSite };
- let arg_scope =
- region::Scope { id: body.value.hir_id.local_id, data: region::ScopeData::Arguments };
- let mut block = START_BLOCK;
- let source_info = builder.source_info(span);
- let call_site_s = (call_site_scope, source_info);
- unpack!(
- block = builder.in_scope(call_site_s, LintLevel::Inherited, |builder| {
- if should_abort_on_panic(tcx, fn_def_id, abi) {
- builder.schedule_abort();
- }
-
- let arg_scope_s = (arg_scope, source_info);
- // `return_block` is called when we evaluate a `return` expression, so
- // we just use `START_BLOCK` here.
- unpack!(
- block = builder.in_breakable_scope(
- None,
- START_BLOCK,
- Place::return_place(),
- |builder| {
- builder.in_scope(arg_scope_s, LintLevel::Inherited, |builder| {
- builder.args_and_body(
- block,
- fn_def_id,
- &arguments,
- arg_scope,
- &body.value,
- )
- })
- },
- )
- );
- // Attribute epilogue to function's closing brace
- let fn_end = span.shrink_to_hi();
- let source_info = builder.source_info(fn_end);
- let return_block = builder.return_block();
- builder.cfg.goto(block, source_info, return_block);
- builder.cfg.terminate(return_block, source_info, TerminatorKind::Return);
- // Attribute any unreachable codepaths to the function's closing brace
- if let Some(unreachable_block) = builder.cached_unreachable_block {
- builder.cfg.terminate(unreachable_block, source_info, TerminatorKind::Unreachable);
- }
- return_block.unit()
- })
- );
- assert_eq!(block, builder.return_block());
-
- let mut spread_arg = None;
- if abi == Abi::RustCall {
- // RustCall pseudo-ABI untuples the last argument.
- spread_arg = Some(Local::new(arguments.len()));
- }
- debug!("fn_id {:?} has attrs {:?}", fn_def_id, tcx.get_attrs(fn_def_id));
-
- let mut body = builder.finish();
- body.spread_arg = spread_arg;
- body
-}
-
-fn construct_const<'a, 'tcx>(
- hir: Cx<'a, 'tcx>,
- body_id: hir::BodyId,
- const_ty: Ty<'tcx>,
- const_ty_span: Span,
-) -> Body<'tcx> {
- let tcx = hir.tcx();
- let owner_id = tcx.hir().body_owner(body_id);
- let span = tcx.hir().span(owner_id);
- let mut builder = Builder::new(hir, span, 0, Safety::Safe, const_ty, const_ty_span, None);
-
- let mut block = START_BLOCK;
- let ast_expr = &tcx.hir().body(body_id).value;
- let expr = builder.hir.mirror(ast_expr);
- unpack!(block = builder.into_expr(&Place::return_place(), block, expr));
-
- let source_info = builder.source_info(span);
- builder.cfg.terminate(block, source_info, TerminatorKind::Return);
-
- // Constants can't `return` so a return block should not be created.
- assert_eq!(builder.cached_return_block, None);
-
- // Constants may be match expressions in which case an unreachable block may
- // be created, so terminate it properly.
- if let Some(unreachable_block) = builder.cached_unreachable_block {
- builder.cfg.terminate(unreachable_block, source_info, TerminatorKind::Unreachable);
- }
-
- builder.finish()
-}
-
-fn construct_error<'a, 'tcx>(hir: Cx<'a, 'tcx>, body_id: hir::BodyId) -> Body<'tcx> {
- let owner_id = hir.tcx().hir().body_owner(body_id);
- let span = hir.tcx().hir().span(owner_id);
- let ty = hir.tcx().types.err;
- let mut builder = Builder::new(hir, span, 0, Safety::Safe, ty, span, None);
- let source_info = builder.source_info(span);
- builder.cfg.terminate(START_BLOCK, source_info, TerminatorKind::Unreachable);
- builder.finish()
-}
-
-impl<'a, 'tcx> Builder<'a, 'tcx> {
- fn new(
- hir: Cx<'a, 'tcx>,
- span: Span,
- arg_count: usize,
- safety: Safety,
- return_ty: Ty<'tcx>,
- return_span: Span,
- generator_kind: Option<GeneratorKind>,
- ) -> Builder<'a, 'tcx> {
- let lint_level = LintLevel::Explicit(hir.root_lint_level);
- let mut builder = Builder {
- hir,
- cfg: CFG { basic_blocks: IndexVec::new() },
- fn_span: span,
- arg_count,
- generator_kind,
- scopes: Default::default(),
- block_context: BlockContext::new(),
- source_scopes: IndexVec::new(),
- source_scope: OUTERMOST_SOURCE_SCOPE,
- guard_context: vec![],
- push_unsafe_count: 0,
- unpushed_unsafe: safety,
- local_decls: IndexVec::from_elem_n(
- LocalDecl::new_return_place(return_ty, return_span),
- 1,
- ),
- canonical_user_type_annotations: IndexVec::new(),
- upvar_mutbls: vec![],
- var_indices: Default::default(),
- unit_temp: None,
- var_debug_info: vec![],
- cached_resume_block: None,
- cached_return_block: None,
- cached_unreachable_block: None,
- };
-
- assert_eq!(builder.cfg.start_new_block(), START_BLOCK);
- assert_eq!(
- builder.new_source_scope(span, lint_level, Some(safety)),
- OUTERMOST_SOURCE_SCOPE
- );
- builder.source_scopes[OUTERMOST_SOURCE_SCOPE].parent_scope = None;
-
- builder
- }
-
- fn finish(self) -> Body<'tcx> {
- for (index, block) in self.cfg.basic_blocks.iter().enumerate() {
- if block.terminator.is_none() {
- span_bug!(self.fn_span, "no terminator on block {:?}", index);
- }
- }
-
- Body::new(
- self.cfg.basic_blocks,
- self.source_scopes,
- self.local_decls,
- self.canonical_user_type_annotations,
- self.arg_count,
- self.var_debug_info,
- self.fn_span,
- self.hir.control_flow_destroyed(),
- self.generator_kind,
- )
- }
-
- fn args_and_body(
- &mut self,
- mut block: BasicBlock,
- fn_def_id: DefId,
- arguments: &[ArgInfo<'tcx>],
- argument_scope: region::Scope,
- ast_body: &'tcx hir::Expr<'tcx>,
- ) -> BlockAnd<()> {
- // Allocate locals for the function arguments
- for &ArgInfo(ty, _, arg_opt, _) in arguments.iter() {
- let source_info = SourceInfo {
- scope: OUTERMOST_SOURCE_SCOPE,
- span: arg_opt.map_or(self.fn_span, |arg| arg.pat.span),
- };
- let arg_local = self.local_decls.push(LocalDecl {
- mutability: Mutability::Mut,
- ty,
- user_ty: UserTypeProjections::none(),
- source_info,
- internal: false,
- local_info: LocalInfo::Other,
- is_block_tail: None,
- });
-
- // If this is a simple binding pattern, give debuginfo a nice name.
- if let Some(arg) = arg_opt {
- if let Some(ident) = arg.pat.simple_ident() {
- self.var_debug_info.push(VarDebugInfo {
- name: ident.name,
- source_info,
- place: arg_local.into(),
- });
- }
- }
- }
-
- let tcx = self.hir.tcx();
- let tcx_hir = tcx.hir();
- let hir_tables = self.hir.tables();
-
- // In analyze_closure() in upvar.rs we gathered a list of upvars used by a
- // closure and we stored in a map called upvar_list in TypeckTables indexed
- // with the closure's DefId. Here, we run through that vec of UpvarIds for
- // the given closure and use the necessary information to create upvar
- // debuginfo and to fill `self.upvar_mutbls`.
- if let Some(upvars) = hir_tables.upvar_list.get(&fn_def_id) {
- let closure_env_arg = Local::new(1);
- let mut closure_env_projs = vec![];
- let mut closure_ty = self.local_decls[closure_env_arg].ty;
- if let ty::Ref(_, ty, _) = closure_ty.kind {
- closure_env_projs.push(ProjectionElem::Deref);
- closure_ty = ty;
- }
- let (def_id, upvar_substs) = match closure_ty.kind {
- ty::Closure(def_id, substs) => (def_id, ty::UpvarSubsts::Closure(substs)),
- ty::Generator(def_id, substs, _) => (def_id, ty::UpvarSubsts::Generator(substs)),
- _ => span_bug!(self.fn_span, "upvars with non-closure env ty {:?}", closure_ty),
- };
- let upvar_tys = upvar_substs.upvar_tys(def_id, tcx);
- let upvars_with_tys = upvars.iter().zip(upvar_tys);
- self.upvar_mutbls = upvars_with_tys
- .enumerate()
- .map(|(i, ((&var_id, &upvar_id), ty))| {
- let capture = hir_tables.upvar_capture(upvar_id);
-
- let mut mutability = Mutability::Not;
- let mut name = kw::Invalid;
- if let Some(Node::Binding(pat)) = tcx_hir.find(var_id) {
- if let hir::PatKind::Binding(_, _, ident, _) = pat.kind {
- name = ident.name;
- match hir_tables.extract_binding_mode(tcx.sess, pat.hir_id, pat.span) {
- Some(ty::BindByValue(hir::Mutability::Mut)) => {
- mutability = Mutability::Mut;
- }
- Some(_) => mutability = Mutability::Not,
- _ => {}
- }
- }
- }
-
- let mut projs = closure_env_projs.clone();
- projs.push(ProjectionElem::Field(Field::new(i), ty));
- match capture {
- ty::UpvarCapture::ByValue => {}
- ty::UpvarCapture::ByRef(..) => {
- projs.push(ProjectionElem::Deref);
- }
- };
-
- self.var_debug_info.push(VarDebugInfo {
- name,
- source_info: SourceInfo {
- scope: OUTERMOST_SOURCE_SCOPE,
- span: tcx_hir.span(var_id),
- },
- place: Place {
- base: closure_env_arg.into(),
- projection: tcx.intern_place_elems(&projs),
- },
- });
-
- mutability
- })
- .collect();
- }
-
- let mut scope = None;
- // Bind the argument patterns
- for (index, arg_info) in arguments.iter().enumerate() {
- // Function arguments always get the first Local indices after the return place
- let local = Local::new(index + 1);
- let place = Place::from(local);
- let &ArgInfo(_, opt_ty_info, arg_opt, ref self_binding) = arg_info;
-
- // Make sure we drop (parts of) the argument even when not matched on.
- self.schedule_drop(
- arg_opt.as_ref().map_or(ast_body.span, |arg| arg.pat.span),
- argument_scope,
- local,
- DropKind::Value,
- );
-
- if let Some(arg) = arg_opt {
- let pattern = self.hir.pattern_from_hir(&arg.pat);
- let original_source_scope = self.source_scope;
- let span = pattern.span;
- self.set_correct_source_scope_for_arg(arg.hir_id, original_source_scope, span);
- match *pattern.kind {
- // Don't introduce extra copies for simple bindings
- PatKind::Binding {
- mutability,
- var,
- mode: BindingMode::ByValue,
- subpattern: None,
- ..
- } => {
- self.local_decls[local].mutability = mutability;
- self.local_decls[local].source_info.scope = self.source_scope;
- self.local_decls[local].local_info = if let Some(kind) = self_binding {
- LocalInfo::User(ClearCrossCrate::Set(BindingForm::ImplicitSelf(*kind)))
- } else {
- let binding_mode = ty::BindingMode::BindByValue(mutability.into());
- LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(
- VarBindingForm {
- binding_mode,
- opt_ty_info,
- opt_match_place: Some((Some(place.clone()), span)),
- pat_span: span,
- },
- )))
- };
- self.var_indices.insert(var, LocalsForNode::One(local));
- }
- _ => {
- scope = self.declare_bindings(
- scope,
- ast_body.span,
- &pattern,
- matches::ArmHasGuard(false),
- Some((Some(&place), span)),
- );
- unpack!(block = self.place_into_pattern(block, pattern, &place, false));
- }
- }
- self.source_scope = original_source_scope;
- }
- }
-
- // Enter the argument pattern bindings source scope, if it exists.
- if let Some(source_scope) = scope {
- self.source_scope = source_scope;
- }
-
- let body = self.hir.mirror(ast_body);
- self.into(&Place::return_place(), block, body)
- }
-
- fn set_correct_source_scope_for_arg(
- &mut self,
- arg_hir_id: hir::HirId,
- original_source_scope: SourceScope,
- pattern_span: Span,
- ) {
- let tcx = self.hir.tcx();
- let current_root = tcx.maybe_lint_level_root_bounded(arg_hir_id, self.hir.root_lint_level);
- 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.root_lint_level,
- );
- if current_root != parent_root {
- self.source_scope =
- self.new_source_scope(pattern_span, LintLevel::Explicit(current_root), None);
- }
- }
-
- fn get_unit_temp(&mut self) -> Place<'tcx> {
- match self.unit_temp {
- Some(ref tmp) => tmp.clone(),
- None => {
- let ty = self.hir.unit_ty();
- let fn_span = self.fn_span;
- let tmp = self.temp(ty, fn_span);
- self.unit_temp = Some(tmp.clone());
- tmp
- }
- }
- }
-
- fn return_block(&mut self) -> BasicBlock {
- match self.cached_return_block {
- Some(rb) => rb,
- None => {
- let rb = self.cfg.start_new_block();
- self.cached_return_block = Some(rb);
- rb
- }
- }
- }
-}
-
-///////////////////////////////////////////////////////////////////////////
-// Builder methods are broken up into modules, depending on what kind
-// of thing is being lowered. Note that they use the `unpack` macro
-// above extensively.
-
-mod block;
-mod cfg;
-mod expr;
-mod into;
-mod matches;
-mod misc;
-mod scope;
+++ /dev/null
-/*!
-Managing the scope stack. The scopes are tied to lexical scopes, so as
-we descend the HAIR, we push a scope on the stack, build its
-contents, and then pop it off. Every scope is named by a
-`region::Scope`.
-
-### SEME Regions
-
-When pushing a new scope, we record the current point in the graph (a
-basic block); this marks the entry to the scope. We then generate more
-stuff in the control-flow graph. Whenever the scope is exited, either
-via a `break` or `return` or just by fallthrough, that marks an exit
-from the scope. Each lexical scope thus corresponds to a single-entry,
-multiple-exit (SEME) region in the control-flow graph.
-
-For now, we keep a mapping from each `region::Scope` to its
-corresponding SEME region for later reference (see caveat in next
-paragraph). This is because region scopes are tied to
-them. Eventually, when we shift to non-lexical lifetimes, there should
-be no need to remember this mapping.
-
-### Not so SEME Regions
-
-In the course of building matches, it sometimes happens that certain code
-(namely guards) gets executed multiple times. This means that the scope lexical
-scope may in fact correspond to multiple, disjoint SEME regions. So in fact our
-mapping is from one scope to a vector of SEME regions.
-
-Also in matches, the scopes assigned to arms are not even SEME regions! Each
-arm has a single region with one entry for each pattern. We manually
-manipulate the scheduled drops in this scope to avoid dropping things multiple
-times, although drop elaboration would clean this up for value drops.
-
-### Drops
-
-The primary purpose for scopes is to insert drops: while building
-the contents, we also accumulate places that need to be dropped upon
-exit from each scope. This is done by calling `schedule_drop`. Once a
-drop is scheduled, whenever we branch out we will insert drops of all
-those places onto the outgoing edge. Note that we don't know the full
-set of scheduled drops up front, and so whenever we exit from the
-scope we only drop the values scheduled thus far. For example, consider
-the scope S corresponding to this loop:
-
-```
-# let cond = true;
-loop {
- let x = ..;
- if cond { break; }
- let y = ..;
-}
-```
-
-When processing the `let x`, we will add one drop to the scope for
-`x`. The break will then insert a drop for `x`. When we process `let
-y`, we will add another drop (in fact, to a subscope, but let's ignore
-that for now); any later drops would also drop `y`.
-
-### Early exit
-
-There are numerous "normal" ways to early exit a scope: `break`,
-`continue`, `return` (panics are handled separately). Whenever an
-early exit occurs, the method `exit_scope` is called. It is given the
-current point in execution where the early exit occurs, as well as the
-scope you want to branch to (note that all early exits from to some
-other enclosing scope). `exit_scope` will record this exit point and
-also add all drops.
-
-Panics are handled in a similar fashion, except that a panic always
-returns out to the `DIVERGE_BLOCK`. To trigger a panic, simply call
-`panic(p)` with the current point `p`. Or else you can call
-`diverge_cleanup`, which will produce a block that you can branch to
-which does the appropriate cleanup and then diverges. `panic(p)`
-simply calls `diverge_cleanup()` and adds an edge from `p` to the
-result.
-
-### Loop scopes
-
-In addition to the normal scope stack, we track a loop scope stack
-that contains only loops. It tracks where a `break` and `continue`
-should go to.
-
-*/
-
-use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder, CFG};
-use crate::hair::{Expr, ExprRef, LintLevel};
-use rustc::middle::region;
-use rustc::mir::*;
-use rustc_data_structures::fx::FxHashMap;
-use rustc_hir as hir;
-use rustc_hir::GeneratorKind;
-use rustc_span::{Span, DUMMY_SP};
-use std::collections::hash_map::Entry;
-use std::mem;
-
-#[derive(Debug)]
-struct Scope {
- /// The source scope this scope was created in.
- source_scope: SourceScope,
-
- /// the region span of this scope within source code.
- region_scope: region::Scope,
-
- /// the span of that region_scope
- region_scope_span: Span,
-
- /// set of places to drop when exiting this scope. This starts
- /// out empty but grows as variables are declared during the
- /// building process. This is a stack, so we always drop from the
- /// end of the vector (top of the stack) first.
- drops: Vec<DropData>,
-
- moved_locals: Vec<Local>,
-
- /// The cache for drop chain on “normal” exit into a particular BasicBlock.
- cached_exits: FxHashMap<(BasicBlock, region::Scope), BasicBlock>,
-
- /// The cache for drop chain on "generator drop" exit.
- cached_generator_drop: Option<BasicBlock>,
-
- /// The cache for drop chain on "unwind" exit.
- cached_unwind: CachedBlock,
-}
-
-#[derive(Debug, Default)]
-pub struct Scopes<'tcx> {
- scopes: Vec<Scope>,
- /// The current set of breakable scopes. See module comment for more details.
- breakable_scopes: Vec<BreakableScope<'tcx>>,
-}
-
-#[derive(Debug)]
-struct DropData {
- /// span where drop obligation was incurred (typically where place was declared)
- span: Span,
-
- /// local to drop
- local: Local,
-
- /// Whether this is a value Drop or a StorageDead.
- kind: DropKind,
-
- /// The cached blocks for unwinds.
- cached_block: CachedBlock,
-}
-
-#[derive(Debug, Default, Clone, Copy)]
-struct CachedBlock {
- /// The cached block for the cleanups-on-diverge path. This block
- /// contains code to run the current drop and all the preceding
- /// drops (i.e., those having lower index in Drop’s Scope drop
- /// array)
- unwind: Option<BasicBlock>,
-
- /// The cached block for unwinds during cleanups-on-generator-drop path
- ///
- /// This is split from the standard unwind path here to prevent drop
- /// elaboration from creating drop flags that would have to be captured
- /// by the generator. I'm not sure how important this optimization is,
- /// but it is here.
- generator_drop: Option<BasicBlock>,
-}
-
-#[derive(Debug, PartialEq, Eq)]
-pub(crate) enum DropKind {
- Value,
- Storage,
-}
-
-#[derive(Clone, Debug)]
-struct BreakableScope<'tcx> {
- /// Region scope of the loop
- region_scope: region::Scope,
- /// Where the body of the loop begins. `None` if block
- continue_block: Option<BasicBlock>,
- /// Block to branch into when the loop or block terminates (either by being
- /// `break`-en out from, or by having its condition to become false)
- break_block: BasicBlock,
- /// The destination of the loop/block expression itself (i.e., where to put
- /// the result of a `break` expression)
- break_destination: Place<'tcx>,
-}
-
-/// The target of an expression that breaks out of a scope
-#[derive(Clone, Copy, Debug)]
-pub enum BreakableTarget {
- Continue(region::Scope),
- Break(region::Scope),
- Return,
-}
-
-impl CachedBlock {
- fn invalidate(&mut self) {
- *self = CachedBlock::default();
- }
-
- fn get(&self, generator_drop: bool) -> Option<BasicBlock> {
- if generator_drop { self.generator_drop } else { self.unwind }
- }
-
- fn ref_mut(&mut self, generator_drop: bool) -> &mut Option<BasicBlock> {
- if generator_drop { &mut self.generator_drop } else { &mut self.unwind }
- }
-}
-
-impl Scope {
- /// Invalidates all the cached blocks in the scope.
- ///
- /// Should always be run for all inner scopes when a drop is pushed into some scope enclosing a
- /// larger extent of code.
- ///
- /// `storage_only` controls whether to invalidate only drop paths that run `StorageDead`.
- /// `this_scope_only` controls whether to invalidate only drop paths that refer to the current
- /// top-of-scope (as opposed to dependent scopes).
- fn invalidate_cache(
- &mut self,
- storage_only: bool,
- generator_kind: Option<GeneratorKind>,
- this_scope_only: bool,
- ) {
- // FIXME: maybe do shared caching of `cached_exits` etc. to handle functions
- // with lots of `try!`?
-
- // cached exits drop storage and refer to the top-of-scope
- self.cached_exits.clear();
-
- // the current generator drop and unwind refer to top-of-scope
- self.cached_generator_drop = None;
-
- let ignore_unwinds = storage_only && generator_kind.is_none();
- if !ignore_unwinds {
- self.cached_unwind.invalidate();
- }
-
- if !ignore_unwinds && !this_scope_only {
- for drop_data in &mut self.drops {
- drop_data.cached_block.invalidate();
- }
- }
- }
-
- /// Given a span and this scope's source scope, make a SourceInfo.
- fn source_info(&self, span: Span) -> SourceInfo {
- SourceInfo { span, scope: self.source_scope }
- }
-
- /// Whether there's anything to do for the cleanup path, that is,
- /// when unwinding through this scope. This includes destructors,
- /// but not StorageDead statements, which don't get emitted at all
- /// for unwinding, for several reasons:
- /// * clang doesn't emit llvm.lifetime.end for C++ unwinding
- /// * LLVM's memory dependency analysis can't handle it atm
- /// * polluting the cleanup MIR with StorageDead creates
- /// landing pads even though there's no actual destructors
- /// * freeing up stack space has no effect during unwinding
- /// Note that for generators we do emit StorageDeads, for the
- /// use of optimizations in the MIR generator transform.
- fn needs_cleanup(&self) -> bool {
- self.drops.iter().any(|drop| match drop.kind {
- DropKind::Value => true,
- DropKind::Storage => false,
- })
- }
-}
-
-impl<'tcx> Scopes<'tcx> {
- fn len(&self) -> usize {
- self.scopes.len()
- }
-
- fn push_scope(&mut self, region_scope: (region::Scope, SourceInfo), vis_scope: SourceScope) {
- debug!("push_scope({:?})", region_scope);
- self.scopes.push(Scope {
- source_scope: vis_scope,
- region_scope: region_scope.0,
- region_scope_span: region_scope.1.span,
- drops: vec![],
- moved_locals: vec![],
- cached_generator_drop: None,
- cached_exits: Default::default(),
- cached_unwind: CachedBlock::default(),
- });
- }
-
- fn pop_scope(
- &mut self,
- region_scope: (region::Scope, SourceInfo),
- ) -> (Scope, Option<BasicBlock>) {
- let scope = self.scopes.pop().unwrap();
- assert_eq!(scope.region_scope, region_scope.0);
- let unwind_to =
- self.scopes.last().and_then(|next_scope| next_scope.cached_unwind.get(false));
- (scope, unwind_to)
- }
-
- fn may_panic(&self, scope_count: usize) -> bool {
- let len = self.len();
- self.scopes[(len - scope_count)..].iter().any(|s| s.needs_cleanup())
- }
-
- /// Finds the breakable scope for a given label. This is used for
- /// resolving `return`, `break` and `continue`.
- fn find_breakable_scope(
- &self,
- span: Span,
- target: BreakableTarget,
- ) -> (BasicBlock, region::Scope, Option<Place<'tcx>>) {
- let get_scope = |scope: region::Scope| {
- // find the loop-scope by its `region::Scope`.
- self.breakable_scopes
- .iter()
- .rfind(|breakable_scope| breakable_scope.region_scope == scope)
- .unwrap_or_else(|| span_bug!(span, "no enclosing breakable scope found"))
- };
- match target {
- BreakableTarget::Return => {
- let scope = &self.breakable_scopes[0];
- if scope.break_destination != Place::return_place() {
- span_bug!(span, "`return` in item with no return scope");
- }
- (scope.break_block, scope.region_scope, Some(scope.break_destination.clone()))
- }
- BreakableTarget::Break(scope) => {
- let scope = get_scope(scope);
- (scope.break_block, scope.region_scope, Some(scope.break_destination.clone()))
- }
- BreakableTarget::Continue(scope) => {
- let scope = get_scope(scope);
- let continue_block = scope
- .continue_block
- .unwrap_or_else(|| span_bug!(span, "missing `continue` block"));
- (continue_block, scope.region_scope, None)
- }
- }
- }
-
- fn num_scopes_above(&self, region_scope: region::Scope, span: Span) -> usize {
- let scope_count = self
- .scopes
- .iter()
- .rev()
- .position(|scope| scope.region_scope == region_scope)
- .unwrap_or_else(|| span_bug!(span, "region_scope {:?} does not enclose", region_scope));
- let len = self.len();
- assert!(scope_count < len, "should not use `exit_scope` to pop ALL scopes");
- scope_count
- }
-
- fn iter_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut Scope> + '_ {
- self.scopes.iter_mut().rev()
- }
-
- fn top_scopes(&mut self, count: usize) -> impl DoubleEndedIterator<Item = &mut Scope> + '_ {
- let len = self.len();
- self.scopes[len - count..].iter_mut()
- }
-
- /// Returns the topmost active scope, which is known to be alive until
- /// the next scope expression.
- pub(super) fn topmost(&self) -> region::Scope {
- self.scopes.last().expect("topmost_scope: no scopes present").region_scope
- }
-
- fn source_info(&self, index: usize, span: Span) -> SourceInfo {
- self.scopes[self.len() - index].source_info(span)
- }
-}
-
-impl<'a, 'tcx> Builder<'a, 'tcx> {
- // Adding and removing scopes
- // ==========================
- // Start a breakable scope, which tracks where `continue`, `break` and
- // `return` should branch to.
- pub fn in_breakable_scope<F, R>(
- &mut self,
- loop_block: Option<BasicBlock>,
- break_block: BasicBlock,
- break_destination: Place<'tcx>,
- f: F,
- ) -> R
- where
- F: FnOnce(&mut Builder<'a, 'tcx>) -> R,
- {
- let region_scope = self.scopes.topmost();
- let scope = BreakableScope {
- region_scope,
- continue_block: loop_block,
- break_block,
- break_destination,
- };
- self.scopes.breakable_scopes.push(scope);
- let res = f(self);
- let breakable_scope = self.scopes.breakable_scopes.pop().unwrap();
- assert!(breakable_scope.region_scope == region_scope);
- res
- }
-
- pub fn in_opt_scope<F, R>(
- &mut self,
- opt_scope: Option<(region::Scope, SourceInfo)>,
- f: F,
- ) -> BlockAnd<R>
- where
- F: FnOnce(&mut Builder<'a, 'tcx>) -> BlockAnd<R>,
- {
- debug!("in_opt_scope(opt_scope={:?})", opt_scope);
- if let Some(region_scope) = opt_scope {
- self.push_scope(region_scope);
- }
- let mut block;
- let rv = unpack!(block = f(self));
- if let Some(region_scope) = opt_scope {
- unpack!(block = self.pop_scope(region_scope, block));
- }
- debug!("in_scope: exiting opt_scope={:?} block={:?}", opt_scope, block);
- block.and(rv)
- }
-
- /// Convenience wrapper that pushes a scope and then executes `f`
- /// to build its contents, popping the scope afterwards.
- pub fn in_scope<F, R>(
- &mut self,
- region_scope: (region::Scope, SourceInfo),
- lint_level: LintLevel,
- f: F,
- ) -> BlockAnd<R>
- where
- F: FnOnce(&mut Builder<'a, 'tcx>) -> BlockAnd<R>,
- {
- debug!("in_scope(region_scope={:?})", region_scope);
- let source_scope = self.source_scope;
- let tcx = self.hir.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 dependences 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.root_lint_level,
- );
- let current_root =
- tcx.maybe_lint_level_root_bounded(current_hir_id, self.hir.root_lint_level);
-
- if parent_root != current_root {
- self.source_scope = self.new_source_scope(
- region_scope.1.span,
- LintLevel::Explicit(current_root),
- None,
- );
- }
- }
- self.push_scope(region_scope);
- let mut block;
- let rv = unpack!(block = f(self));
- unpack!(block = self.pop_scope(region_scope, block));
- self.source_scope = source_scope;
- debug!("in_scope: exiting region_scope={:?} block={:?}", region_scope, block);
- block.and(rv)
- }
-
- /// Push a scope onto the stack. You can then build code in this
- /// scope and call `pop_scope` afterwards. Note that these two
- /// calls must be paired; using `in_scope` as a convenience
- /// wrapper maybe preferable.
- pub fn push_scope(&mut self, region_scope: (region::Scope, SourceInfo)) {
- self.scopes.push_scope(region_scope, self.source_scope);
- }
-
- /// Pops a scope, which should have region scope `region_scope`,
- /// adding any drops onto the end of `block` that are needed.
- /// This must match 1-to-1 with `push_scope`.
- pub fn pop_scope(
- &mut self,
- region_scope: (region::Scope, SourceInfo),
- mut block: BasicBlock,
- ) -> BlockAnd<()> {
- debug!("pop_scope({:?}, {:?})", region_scope, block);
- // If we are emitting a `drop` statement, we need to have the cached
- // diverge cleanup pads ready in case that drop panics.
- if self.scopes.may_panic(1) {
- self.diverge_cleanup();
- }
- let (scope, unwind_to) = self.scopes.pop_scope(region_scope);
- let unwind_to = unwind_to.unwrap_or_else(|| self.resume_block());
-
- unpack!(
- block = build_scope_drops(
- &mut self.cfg,
- self.generator_kind,
- &scope,
- block,
- unwind_to,
- self.arg_count,
- false, // not generator
- false, // not unwind path
- )
- );
-
- block.unit()
- }
-
- pub fn break_scope(
- &mut self,
- mut block: BasicBlock,
- value: Option<ExprRef<'tcx>>,
- scope: BreakableTarget,
- source_info: SourceInfo,
- ) -> BlockAnd<()> {
- let (mut target_block, region_scope, destination) =
- self.scopes.find_breakable_scope(source_info.span, scope);
- if let BreakableTarget::Return = scope {
- // We call this now, rather than when we start lowering the
- // function so that the return block doesn't precede the entire
- // rest of the CFG. Some passes and LLVM prefer blocks to be in
- // approximately CFG order.
- target_block = self.return_block();
- }
- if let Some(destination) = destination {
- if let Some(value) = value {
- debug!("stmt_expr Break val block_context.push(SubExpr)");
- self.block_context.push(BlockFrame::SubExpr);
- unpack!(block = self.into(&destination, block, value));
- self.block_context.pop();
- } else {
- self.cfg.push_assign_unit(block, source_info, &destination)
- }
- } else {
- assert!(value.is_none(), "`return` and `break` should have a destination");
- }
- self.exit_scope(source_info.span, region_scope, block, target_block);
- self.cfg.start_new_block().unit()
- }
-
- /// Branch out of `block` to `target`, exiting all scopes up to
- /// and including `region_scope`. This will insert whatever drops are
- /// needed. See module comment for details.
- pub fn exit_scope(
- &mut self,
- span: Span,
- region_scope: region::Scope,
- mut block: BasicBlock,
- target: BasicBlock,
- ) {
- debug!(
- "exit_scope(region_scope={:?}, block={:?}, target={:?})",
- region_scope, block, target
- );
- let scope_count = self.scopes.num_scopes_above(region_scope, span);
-
- // If we are emitting a `drop` statement, we need to have the cached
- // diverge cleanup pads ready in case that drop panics.
- let may_panic = self.scopes.may_panic(scope_count);
- if may_panic {
- self.diverge_cleanup();
- }
-
- let mut scopes = self.scopes.top_scopes(scope_count + 1).rev();
- let mut scope = scopes.next().unwrap();
- for next_scope in scopes {
- if scope.drops.is_empty() {
- scope = next_scope;
- continue;
- }
- let source_info = scope.source_info(span);
- block = match scope.cached_exits.entry((target, region_scope)) {
- Entry::Occupied(e) => {
- self.cfg.goto(block, source_info, *e.get());
- return;
- }
- Entry::Vacant(v) => {
- let b = self.cfg.start_new_block();
- self.cfg.goto(block, source_info, b);
- v.insert(b);
- b
- }
- };
-
- let unwind_to = next_scope.cached_unwind.get(false).unwrap_or_else(|| {
- debug_assert!(!may_panic, "cached block not present?");
- START_BLOCK
- });
-
- unpack!(
- block = build_scope_drops(
- &mut self.cfg,
- self.generator_kind,
- scope,
- block,
- unwind_to,
- self.arg_count,
- false, // not generator
- false, // not unwind path
- )
- );
-
- scope = next_scope;
- }
-
- self.cfg.goto(block, self.scopes.source_info(scope_count, span), target);
- }
-
- /// Creates a path that performs all required cleanup for dropping a generator.
- ///
- /// This path terminates in GeneratorDrop. Returns the start of the path.
- /// None indicates there’s no cleanup to do at this point.
- pub fn generator_drop_cleanup(&mut self) -> Option<BasicBlock> {
- // Fill in the cache for unwinds
- self.diverge_cleanup_gen(true);
-
- let src_info = self.scopes.source_info(self.scopes.len(), self.fn_span);
- let resume_block = self.resume_block();
- let mut scopes = self.scopes.iter_mut().peekable();
- let mut block = self.cfg.start_new_block();
- let result = block;
-
- while let Some(scope) = scopes.next() {
- block = if let Some(b) = scope.cached_generator_drop {
- self.cfg.goto(block, src_info, b);
- return Some(result);
- } else {
- let b = self.cfg.start_new_block();
- scope.cached_generator_drop = Some(b);
- self.cfg.goto(block, src_info, b);
- b
- };
-
- let unwind_to = scopes
- .peek()
- .as_ref()
- .map(|scope| {
- scope
- .cached_unwind
- .get(true)
- .unwrap_or_else(|| span_bug!(src_info.span, "cached block not present?"))
- })
- .unwrap_or(resume_block);
-
- unpack!(
- block = build_scope_drops(
- &mut self.cfg,
- self.generator_kind,
- scope,
- block,
- unwind_to,
- self.arg_count,
- true, // is generator
- true, // is cached path
- )
- );
- }
-
- self.cfg.terminate(block, src_info, TerminatorKind::GeneratorDrop);
-
- Some(result)
- }
-
- /// Creates a new source scope, nested in the current one.
- pub fn new_source_scope(
- &mut self,
- span: Span,
- lint_level: LintLevel,
- safety: Option<Safety>,
- ) -> SourceScope {
- let parent = self.source_scope;
- debug!(
- "new_source_scope({:?}, {:?}, {:?}) - parent({:?})={:?}",
- span,
- lint_level,
- safety,
- parent,
- self.source_scopes.get(parent)
- );
- let scope_local_data = SourceScopeLocalData {
- lint_root: if let LintLevel::Explicit(lint_root) = lint_level {
- lint_root
- } else {
- self.source_scopes[parent].local_data.as_ref().assert_crate_local().lint_root
- },
- safety: safety.unwrap_or_else(|| {
- self.source_scopes[parent].local_data.as_ref().assert_crate_local().safety
- }),
- };
- self.source_scopes.push(SourceScopeData {
- span,
- parent_scope: Some(parent),
- local_data: ClearCrossCrate::Set(scope_local_data),
- })
- }
-
- /// Given a span and the current source scope, make a SourceInfo.
- pub fn source_info(&self, span: Span) -> SourceInfo {
- SourceInfo { span, scope: self.source_scope }
- }
-
- // Finding scopes
- // ==============
- /// Returns the scope that we should use as the lifetime of an
- /// operand. Basically, an operand must live until it is consumed.
- /// This is similar to, but not quite the same as, the temporary
- /// scope (which can be larger or smaller).
- ///
- /// Consider:
- ///
- /// let x = foo(bar(X, Y));
- ///
- /// We wish to pop the storage for X and Y after `bar()` is
- /// called, not after the whole `let` is completed.
- ///
- /// As another example, if the second argument diverges:
- ///
- /// foo(Box::new(2), panic!())
- ///
- /// We would allocate the box but then free it on the unwinding
- /// path; we would also emit a free on the 'success' path from
- /// panic, but that will turn out to be removed as dead-code.
- ///
- /// When building statics/constants, returns `None` since
- /// intermediate values do not have to be dropped in that case.
- pub fn local_scope(&self) -> Option<region::Scope> {
- match self.hir.body_owner_kind {
- hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) =>
- // No need to free storage in this context.
- {
- None
- }
- hir::BodyOwnerKind::Closure | hir::BodyOwnerKind::Fn => Some(self.scopes.topmost()),
- }
- }
-
- // Schedule an abort block - this is used for some ABIs that cannot unwind
- pub fn schedule_abort(&mut self) -> BasicBlock {
- let source_info = self.scopes.source_info(self.scopes.len(), self.fn_span);
- let abortblk = self.cfg.start_new_cleanup_block();
- self.cfg.terminate(abortblk, source_info, TerminatorKind::Abort);
- self.cached_resume_block = Some(abortblk);
- abortblk
- }
-
- // Scheduling drops
- // ================
- pub fn schedule_drop_storage_and_value(
- &mut self,
- span: Span,
- region_scope: region::Scope,
- local: Local,
- ) {
- self.schedule_drop(span, region_scope, local, DropKind::Storage);
- self.schedule_drop(span, region_scope, local, DropKind::Value);
- }
-
- /// Indicates that `place` should be dropped on exit from
- /// `region_scope`.
- ///
- /// When called with `DropKind::Storage`, `place` should be a local
- /// with an index higher than the current `self.arg_count`.
- pub fn schedule_drop(
- &mut self,
- span: Span,
- region_scope: region::Scope,
- local: Local,
- drop_kind: DropKind,
- ) {
- let needs_drop = match drop_kind {
- DropKind::Value => {
- if !self.hir.needs_drop(self.local_decls[local].ty) {
- return;
- }
- true
- }
- DropKind::Storage => {
- if local.index() <= self.arg_count {
- span_bug!(
- span,
- "`schedule_drop` called with local {:?} and arg_count {}",
- local,
- self.arg_count,
- )
- }
- false
- }
- };
-
- for scope in self.scopes.iter_mut() {
- let this_scope = scope.region_scope == region_scope;
- // When building drops, we try to cache chains of drops in such a way so these drops
- // could be reused by the drops which would branch into the cached (already built)
- // blocks. This, however, means that whenever we add a drop into a scope which already
- // had some blocks built (and thus, cached) for it, we must invalidate all caches which
- // might branch into the scope which had a drop just added to it. This is necessary,
- // because otherwise some other code might use the cache to branch into already built
- // chain of drops, essentially ignoring the newly added drop.
- //
- // For example consider there’s two scopes with a drop in each. These are built and
- // thus the caches are filled:
- //
- // +--------------------------------------------------------+
- // | +---------------------------------+ |
- // | | +--------+ +-------------+ | +---------------+ |
- // | | | return | <-+ | drop(outer) | <-+ | drop(middle) | |
- // | | +--------+ +-------------+ | +---------------+ |
- // | +------------|outer_scope cache|--+ |
- // +------------------------------|middle_scope cache|------+
- //
- // Now, a new, inner-most scope is added along with a new drop into both inner-most and
- // outer-most scopes:
- //
- // +------------------------------------------------------------+
- // | +----------------------------------+ |
- // | | +--------+ +-------------+ | +---------------+ | +-------------+
- // | | | return | <+ | drop(new) | <-+ | drop(middle) | <--+| drop(inner) |
- // | | +--------+ | | drop(outer) | | +---------------+ | +-------------+
- // | | +-+ +-------------+ | |
- // | +---|invalid outer_scope cache|----+ |
- // +----=----------------|invalid middle_scope cache|-----------+
- //
- // If, when adding `drop(new)` we do not invalidate the cached blocks for both
- // outer_scope and middle_scope, then, when building drops for the inner (right-most)
- // scope, the old, cached blocks, without `drop(new)` will get used, producing the
- // wrong results.
- //
- // The cache and its invalidation for unwind branch is somewhat special. The cache is
- // per-drop, rather than per scope, which has a several different implications. Adding
- // a new drop into a scope will not invalidate cached blocks of the prior drops in the
- // scope. That is true, because none of the already existing drops will have an edge
- // into a block with the newly added drop.
- //
- // Note that this code iterates scopes from the inner-most to the outer-most,
- // invalidating caches of each scope visited. This way bare minimum of the
- // caches gets invalidated. i.e., if a new drop is added into the middle scope, the
- // cache of outer scope stays intact.
- scope.invalidate_cache(!needs_drop, self.generator_kind, this_scope);
- if this_scope {
- let region_scope_span =
- region_scope.span(self.hir.tcx(), &self.hir.region_scope_tree);
- // Attribute scope exit drops to scope's closing brace.
- let scope_end = self.hir.tcx().sess.source_map().end_point(region_scope_span);
-
- scope.drops.push(DropData {
- span: scope_end,
- local,
- kind: drop_kind,
- cached_block: CachedBlock::default(),
- });
- return;
- }
- }
- span_bug!(span, "region scope {:?} not in scope to drop {:?}", region_scope, local);
- }
-
- /// Indicates that the "local operand" stored in `local` is
- /// *moved* at some point during execution (see `local_scope` for
- /// more information about what a "local operand" is -- in short,
- /// it's an intermediate operand created as part of preparing some
- /// MIR instruction). We use this information to suppress
- /// redundant drops on the non-unwind paths. This results in less
- /// MIR, but also avoids spurious borrow check errors
- /// (c.f. #64391).
- ///
- /// Example: when compiling the call to `foo` here:
- ///
- /// ```rust
- /// foo(bar(), ...)
- /// ```
- ///
- /// we would evaluate `bar()` to an operand `_X`. We would also
- /// schedule `_X` to be dropped when the expression scope for
- /// `foo(bar())` is exited. This is relevant, for example, if the
- /// later arguments should unwind (it would ensure that `_X` gets
- /// dropped). However, if no unwind occurs, then `_X` will be
- /// unconditionally consumed by the `call`:
- ///
- /// ```
- /// bb {
- /// ...
- /// _R = CALL(foo, _X, ...)
- /// }
- /// ```
- ///
- /// However, `_X` is still registered to be dropped, and so if we
- /// do nothing else, we would generate a `DROP(_X)` that occurs
- /// after the call. This will later be optimized out by the
- /// drop-elaboation code, but in the meantime it can lead to
- /// spurious borrow-check errors -- the problem, ironically, is
- /// not the `DROP(_X)` itself, but the (spurious) unwind pathways
- /// that it creates. See #64391 for an example.
- pub fn record_operands_moved(&mut self, operands: &[Operand<'tcx>]) {
- let scope = match self.local_scope() {
- None => {
- // if there is no local scope, operands won't be dropped anyway
- return;
- }
-
- Some(local_scope) => self
- .scopes
- .iter_mut()
- .find(|scope| scope.region_scope == local_scope)
- .unwrap_or_else(|| bug!("scope {:?} not found in scope list!", local_scope)),
- };
-
- // look for moves of a local variable, like `MOVE(_X)`
- let locals_moved = operands.iter().flat_map(|operand| match operand {
- Operand::Copy(_) | Operand::Constant(_) => None,
- Operand::Move(place) => place.as_local(),
- });
-
- for local in locals_moved {
- // check if we have a Drop for this operand and -- if so
- // -- add it to the list of moved operands. Note that this
- // local might not have been an operand created for this
- // call, it could come from other places too.
- if scope.drops.iter().any(|drop| drop.local == local && drop.kind == DropKind::Value) {
- scope.moved_locals.push(local);
- }
- }
- }
-
- // Other
- // =====
- /// Branch based on a boolean condition.
- ///
- /// This is a special case because the temporary for the condition needs to
- /// be dropped on both the true and the false arm.
- pub fn test_bool(
- &mut self,
- mut block: BasicBlock,
- condition: Expr<'tcx>,
- source_info: SourceInfo,
- ) -> (BasicBlock, BasicBlock) {
- let cond = unpack!(block = self.as_local_operand(block, condition));
- let true_block = self.cfg.start_new_block();
- let false_block = self.cfg.start_new_block();
- let term = TerminatorKind::if_(self.hir.tcx(), cond.clone(), true_block, false_block);
- self.cfg.terminate(block, source_info, term);
-
- match cond {
- // Don't try to drop a constant
- Operand::Constant(_) => (),
- // If constants and statics, we don't generate StorageLive for this
- // temporary, so don't try to generate StorageDead for it either.
- _ if self.local_scope().is_none() => (),
- Operand::Copy(place) | Operand::Move(place) => {
- if let Some(cond_temp) = place.as_local() {
- // Manually drop the condition on both branches.
- let top_scope = self.scopes.scopes.last_mut().unwrap();
- let top_drop_data = top_scope.drops.pop().unwrap();
-
- match top_drop_data.kind {
- DropKind::Value { .. } => {
- bug!("Drop scheduled on top of condition variable")
- }
- DropKind::Storage => {
- let source_info = top_scope.source_info(top_drop_data.span);
- let local = top_drop_data.local;
- assert_eq!(local, cond_temp, "Drop scheduled on top of condition");
- self.cfg.push(
- true_block,
- Statement { source_info, kind: StatementKind::StorageDead(local) },
- );
- self.cfg.push(
- false_block,
- Statement { source_info, kind: StatementKind::StorageDead(local) },
- );
- }
- }
-
- top_scope.invalidate_cache(true, self.generator_kind, true);
- } else {
- bug!("Expected as_local_operand to produce a temporary");
- }
- }
- }
-
- (true_block, false_block)
- }
-
- /// Creates a path that performs all required cleanup for unwinding.
- ///
- /// This path terminates in Resume. Returns the start of the path.
- /// See module comment for more details.
- pub fn diverge_cleanup(&mut self) -> BasicBlock {
- self.diverge_cleanup_gen(false)
- }
-
- fn resume_block(&mut self) -> BasicBlock {
- if let Some(target) = self.cached_resume_block {
- target
- } else {
- let resumeblk = self.cfg.start_new_cleanup_block();
- self.cfg.terminate(
- resumeblk,
- SourceInfo { scope: OUTERMOST_SOURCE_SCOPE, span: self.fn_span },
- TerminatorKind::Resume,
- );
- self.cached_resume_block = Some(resumeblk);
- resumeblk
- }
- }
-
- fn diverge_cleanup_gen(&mut self, generator_drop: bool) -> BasicBlock {
- // Build up the drops in **reverse** order. The end result will
- // look like:
- //
- // scopes[n] -> scopes[n-1] -> ... -> scopes[0]
- //
- // However, we build this in **reverse order**. That is, we
- // process scopes[0], then scopes[1], etc, pointing each one at
- // the result generates from the one before. Along the way, we
- // store caches. If everything is cached, we'll just walk right
- // to left reading the cached results but never created anything.
-
- // Find the last cached block
- debug!("diverge_cleanup_gen(self.scopes = {:?})", self.scopes);
- let cached_cleanup = self.scopes.iter_mut().enumerate().find_map(|(idx, ref scope)| {
- let cached_block = scope.cached_unwind.get(generator_drop)?;
- Some((cached_block, idx))
- });
- let (mut target, first_uncached) =
- cached_cleanup.unwrap_or_else(|| (self.resume_block(), self.scopes.len()));
-
- for scope in self.scopes.top_scopes(first_uncached) {
- target = build_diverge_scope(
- &mut self.cfg,
- scope.region_scope_span,
- scope,
- target,
- generator_drop,
- self.generator_kind,
- );
- }
-
- target
- }
-
- /// Utility function for *non*-scope code to build their own drops
- pub fn build_drop_and_replace(
- &mut self,
- block: BasicBlock,
- span: Span,
- location: Place<'tcx>,
- value: Operand<'tcx>,
- ) -> BlockAnd<()> {
- let source_info = self.source_info(span);
- let next_target = self.cfg.start_new_block();
- let diverge_target = self.diverge_cleanup();
- self.cfg.terminate(
- block,
- source_info,
- TerminatorKind::DropAndReplace {
- location,
- value,
- target: next_target,
- unwind: Some(diverge_target),
- },
- );
- next_target.unit()
- }
-
- /// Creates an Assert terminator and return the success block.
- /// If the boolean condition operand is not the expected value,
- /// a runtime panic will be caused with the given message.
- pub fn assert(
- &mut self,
- block: BasicBlock,
- cond: Operand<'tcx>,
- expected: bool,
- msg: AssertMessage<'tcx>,
- span: Span,
- ) -> BasicBlock {
- let source_info = self.source_info(span);
-
- let success_block = self.cfg.start_new_block();
- let cleanup = self.diverge_cleanup();
-
- self.cfg.terminate(
- block,
- source_info,
- TerminatorKind::Assert {
- cond,
- expected,
- msg,
- target: success_block,
- cleanup: Some(cleanup),
- },
- );
-
- success_block
- }
-
- // `match` arm scopes
- // ==================
- /// Unschedules any drops in the top scope.
- ///
- /// This is only needed for `match` arm scopes, because they have one
- /// entrance per pattern, but only one exit.
- pub(crate) fn clear_top_scope(&mut self, region_scope: region::Scope) {
- let top_scope = self.scopes.scopes.last_mut().unwrap();
-
- assert_eq!(top_scope.region_scope, region_scope);
-
- top_scope.drops.clear();
- top_scope.invalidate_cache(false, self.generator_kind, true);
- }
-}
-
-/// Builds drops for pop_scope and exit_scope.
-fn build_scope_drops<'tcx>(
- cfg: &mut CFG<'tcx>,
- generator_kind: Option<GeneratorKind>,
- scope: &Scope,
- mut block: BasicBlock,
- last_unwind_to: BasicBlock,
- arg_count: usize,
- generator_drop: bool,
- is_cached_path: bool,
-) -> BlockAnd<()> {
- debug!("build_scope_drops({:?} -> {:?})", block, scope);
-
- // Build up the drops in evaluation order. The end result will
- // look like:
- //
- // [SDs, drops[n]] --..> [SDs, drop[1]] -> [SDs, drop[0]] -> [[SDs]]
- // | | |
- // : | |
- // V V
- // [drop[n]] -...-> [drop[1]] ------> [drop[0]] ------> [last_unwind_to]
- //
- // The horizontal arrows represent the execution path when the drops return
- // successfully. The downwards arrows represent the execution path when the
- // drops panic (panicking while unwinding will abort, so there's no need for
- // another set of arrows).
- //
- // For generators, we unwind from a drop on a local to its StorageDead
- // statement. For other functions we don't worry about StorageDead. The
- // drops for the unwind path should have already been generated by
- // `diverge_cleanup_gen`.
-
- for drop_idx in (0..scope.drops.len()).rev() {
- let drop_data = &scope.drops[drop_idx];
- let source_info = scope.source_info(drop_data.span);
- let local = drop_data.local;
-
- match drop_data.kind {
- DropKind::Value => {
- // If the operand has been moved, and we are not on an unwind
- // path, then don't generate the drop. (We only take this into
- // account for non-unwind paths so as not to disturb the
- // caching mechanism.)
- if !is_cached_path && scope.moved_locals.iter().any(|&o| o == local) {
- continue;
- }
-
- let unwind_to = get_unwind_to(scope, generator_kind, drop_idx, generator_drop)
- .unwrap_or(last_unwind_to);
-
- let next = cfg.start_new_block();
- cfg.terminate(
- block,
- source_info,
- TerminatorKind::Drop {
- location: local.into(),
- target: next,
- unwind: Some(unwind_to),
- },
- );
- block = next;
- }
- DropKind::Storage => {
- // Only temps and vars need their storage dead.
- assert!(local.index() > arg_count);
- cfg.push(block, Statement { source_info, kind: StatementKind::StorageDead(local) });
- }
- }
- }
- block.unit()
-}
-
-fn get_unwind_to(
- scope: &Scope,
- generator_kind: Option<GeneratorKind>,
- unwind_from: usize,
- generator_drop: bool,
-) -> Option<BasicBlock> {
- for drop_idx in (0..unwind_from).rev() {
- let drop_data = &scope.drops[drop_idx];
- match (generator_kind, &drop_data.kind) {
- (Some(_), DropKind::Storage) => {
- return Some(drop_data.cached_block.get(generator_drop).unwrap_or_else(|| {
- span_bug!(drop_data.span, "cached block not present for {:?}", drop_data)
- }));
- }
- (None, DropKind::Value) => {
- return Some(drop_data.cached_block.get(generator_drop).unwrap_or_else(|| {
- span_bug!(drop_data.span, "cached block not present for {:?}", drop_data)
- }));
- }
- _ => (),
- }
- }
- None
-}
-
-fn build_diverge_scope<'tcx>(
- cfg: &mut CFG<'tcx>,
- span: Span,
- scope: &mut Scope,
- mut target: BasicBlock,
- generator_drop: bool,
- generator_kind: Option<GeneratorKind>,
-) -> BasicBlock {
- // Build up the drops in **reverse** order. The end result will
- // look like:
- //
- // [drops[n]] -...-> [drops[0]] -> [target]
- //
- // The code in this function reads from right to left. At each
- // point, we check for cached blocks representing the
- // remainder. If everything is cached, we'll just walk right to
- // left reading the cached results but never create anything.
-
- let source_scope = scope.source_scope;
- let source_info = |span| SourceInfo { span, scope: source_scope };
-
- // We keep track of StorageDead statements to prepend to our current block
- // and store them here, in reverse order.
- let mut storage_deads = vec![];
-
- let mut target_built_by_us = false;
-
- // Build up the drops. Here we iterate the vector in
- // *forward* order, so that we generate drops[0] first (right to
- // left in diagram above).
- debug!("build_diverge_scope({:?})", scope.drops);
- for (j, drop_data) in scope.drops.iter_mut().enumerate() {
- debug!("build_diverge_scope drop_data[{}]: {:?}", j, drop_data);
- // Only full value drops are emitted in the diverging path,
- // not StorageDead, except in the case of generators.
- //
- // Note: This may not actually be what we desire (are we
- // "freeing" stack storage as we unwind, or merely observing a
- // frozen stack)? In particular, the intent may have been to
- // match the behavior of clang, but on inspection eddyb says
- // this is not what clang does.
- match drop_data.kind {
- DropKind::Storage if generator_kind.is_some() => {
- storage_deads.push(Statement {
- source_info: source_info(drop_data.span),
- kind: StatementKind::StorageDead(drop_data.local),
- });
- if !target_built_by_us {
- // We cannot add statements to an existing block, so we create a new
- // block for our StorageDead statements.
- let block = cfg.start_new_cleanup_block();
- let source_info = SourceInfo { span: DUMMY_SP, scope: source_scope };
- cfg.goto(block, source_info, target);
- target = block;
- target_built_by_us = true;
- }
- *drop_data.cached_block.ref_mut(generator_drop) = Some(target);
- }
- DropKind::Storage => {}
- DropKind::Value => {
- let cached_block = drop_data.cached_block.ref_mut(generator_drop);
- target = if let Some(cached_block) = *cached_block {
- storage_deads.clear();
- target_built_by_us = false;
- cached_block
- } else {
- push_storage_deads(cfg, target, &mut storage_deads);
- let block = cfg.start_new_cleanup_block();
- cfg.terminate(
- block,
- source_info(drop_data.span),
- TerminatorKind::Drop {
- location: drop_data.local.into(),
- target,
- unwind: None,
- },
- );
- *cached_block = Some(block);
- target_built_by_us = true;
- block
- };
- }
- };
- }
- push_storage_deads(cfg, target, &mut storage_deads);
- *scope.cached_unwind.ref_mut(generator_drop) = Some(target);
-
- assert!(storage_deads.is_empty());
- debug!("build_diverge_scope({:?}, {:?}) = {:?}", scope, span, target);
-
- target
-}
-
-fn push_storage_deads(
- cfg: &mut CFG<'tcx>,
- target: BasicBlock,
- storage_deads: &mut Vec<Statement<'tcx>>,
-) {
- if storage_deads.is_empty() {
- return;
- }
- let statements = &mut cfg.block_data_mut(target).statements;
- storage_deads.reverse();
- debug!(
- "push_storage_deads({:?}), storage_deads={:?}, statements={:?}",
- target, storage_deads, statements
- );
- storage_deads.append(statements);
- mem::swap(statements, storage_deads);
- assert!(storage_deads.is_empty());
-}
mod error;
mod eval_queries;
+mod fn_queries;
mod machine;
pub use error::*;
pub use eval_queries::*;
+pub use fn_queries::*;
pub use machine::*;
/// Extracts a field of a (variant of a) const.
let loc_ty = tcx.caller_location_ty();
let loc_place = ecx.alloc_caller_location(file, line, col);
- intern_const_alloc_recursive(&mut ecx, None, loc_place).unwrap();
+ intern_const_alloc_recursive(&mut ecx, None, loc_place, false).unwrap();
let loc_const = ty::Const {
ty: loc_ty,
val: ty::ConstKind::Value(ConstValue::Scalar(loc_place.ptr.into())),
tcx.mk_const(loc_const)
}
-// this function uses `unwrap` copiously, because an already validated constant must have valid
-// fields and can thus never fail outside of compiler bugs
-pub(crate) fn const_variant_index<'tcx>(
+// this function uses `unwrap` copiously, because an already validated constant
+// must have valid fields and can thus never fail outside of compiler bugs
+pub(crate) fn destructure_const<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
val: &'tcx ty::Const<'tcx>,
-) -> VariantIdx {
- trace!("const_variant_index: {:?}", val);
+) -> mir::DestructuredConst<'tcx> {
+ trace!("destructure_const: {:?}", val);
let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
let op = ecx.eval_const_to_op(val, None).unwrap();
- ecx.read_discriminant(op).unwrap().1
+
+ let variant = ecx.read_discriminant(op).unwrap().1;
+
+ let field_count = match val.ty.kind {
+ ty::Array(_, len) => len.eval_usize(tcx, param_env),
+ ty::Adt(def, _) => def.variants[variant].fields.len() as u64,
+ ty::Tuple(substs) => substs.len() as u64,
+ _ => bug!("cannot destructure constant {:?}", val),
+ };
+
+ let down = ecx.operand_downcast(op, variant).unwrap();
+ let fields_iter = (0..field_count).map(|i| {
+ let field_op = ecx.operand_field(down, i).unwrap();
+ op_to_const(&ecx, field_op)
+ });
+ let fields = tcx.arena.alloc_from_iter(fields_iter);
+
+ mir::DestructuredConst { variant, fields }
}
ecx.run()?;
// Intern the result
- intern_const_alloc_recursive(ecx, tcx.static_mutability(cid.instance.def_id()), ret)?;
+ intern_const_alloc_recursive(
+ ecx,
+ tcx.static_mutability(cid.instance.def_id()),
+ ret,
+ body.ignore_interior_mut_in_const_validation,
+ )?;
debug!("eval_body_using_ecx done: {:?}", *ret);
Ok(ret)
// by-val is if we are in const_field, i.e., if this is (a field of) something that we
// "tried to make immediate" before. We wouldn't do that for non-slice scalar pairs or
// structs containing such.
- op.try_as_mplace()
+ op.try_as_mplace(ecx)
};
- let val = match immediate {
- Ok(mplace) => {
- let ptr = mplace.ptr.assert_ptr();
+
+ let to_const_value = |mplace: MPlaceTy<'_>| match mplace.ptr {
+ Scalar::Ptr(ptr) => {
let alloc = ecx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id);
ConstValue::ByRef { alloc, offset: ptr.offset }
}
+ Scalar::Raw { data, .. } => {
+ assert!(mplace.layout.is_zst());
+ assert_eq!(
+ data,
+ mplace.layout.align.abi.bytes().into(),
+ "this MPlaceTy must come from `try_as_mplace` being used on a zst, so we know what
+ value this integer address must have",
+ );
+ ConstValue::Scalar(Scalar::zst())
+ }
+ };
+ let val = match immediate {
+ Ok(mplace) => to_const_value(mplace),
// see comment on `let try_as_immediate` above
Err(ImmTy { imm: Immediate::Scalar(x), .. }) => match x {
ScalarMaybeUndef::Scalar(s) => ConstValue::Scalar(s),
- ScalarMaybeUndef::Undef => {
- // When coming out of "normal CTFE", we'll always have an `Indirect` operand as
- // argument and we will not need this. The only way we can already have an
- // `Immediate` is when we are called from `const_field`, and that `Immediate`
- // comes from a constant so it can happen have `Undef`, because the indirect
- // memory that was read had undefined bytes.
- let mplace = op.assert_mem_place();
- let ptr = mplace.ptr.assert_ptr();
- let alloc = ecx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id);
- ConstValue::ByRef { alloc, offset: ptr.offset }
- }
+ ScalarMaybeUndef::Undef => to_const_value(op.assert_mem_place(ecx)),
},
Err(ImmTy { imm: Immediate::ScalarPair(a, b), .. }) => {
let (data, start) = match a.not_undef().unwrap() {
let ecx = mk_eval_cx(tcx, tcx.def_span(key.value.instance.def_id()), key.param_env, is_static);
let val = (|| {
let mplace = ecx.raw_const_to_mplace(constant)?;
- let mut ref_tracking = RefTracking::new(mplace);
- while let Some((mplace, path)) = ref_tracking.todo.pop() {
- ecx.validate_operand(mplace.into(), path, Some(&mut ref_tracking))?;
+
+ // FIXME do not validate promoteds until a decision on
+ // https://github.com/rust-lang/rust/issues/67465 is made
+ if cid.promoted.is_none() {
+ let mut ref_tracking = RefTracking::new(mplace);
+ while let Some((mplace, path)) = ref_tracking.todo.pop() {
+ ecx.validate_operand(mplace.into(), path, Some(&mut ref_tracking))?;
+ }
}
// Now that we validated, turn this into a proper constant.
// Statics/promoteds are always `ByRef`, for the rest `op_to_const` decides
--- /dev/null
+use rustc::hir::map::blocks::FnLikeNode;
+use rustc::ty::query::Providers;
+use rustc::ty::TyCtxt;
+use rustc_hir as hir;
+use rustc_hir::def_id::DefId;
+use rustc_span::symbol::Symbol;
+use rustc_target::spec::abi::Abi;
+use syntax::attr;
+
+/// Whether the `def_id` counts as const fn in your current crate, considering all active
+/// feature gates
+pub fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
+ tcx.is_const_fn_raw(def_id)
+ && match is_unstable_const_fn(tcx, def_id) {
+ Some(feature_name) => {
+ // has a `rustc_const_unstable` attribute, check whether the user enabled the
+ // corresponding feature gate.
+ tcx.features().declared_lib_features.iter().any(|&(sym, _)| sym == feature_name)
+ }
+ // functions without const stability are either stable user written
+ // const fn or the user is using feature gates and we thus don't
+ // care what they do
+ None => true,
+ }
+}
+
+/// Whether the `def_id` is an unstable const fn and what feature gate is necessary to enable it
+pub fn is_unstable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Symbol> {
+ if tcx.is_const_fn_raw(def_id) {
+ let const_stab = tcx.lookup_const_stability(def_id)?;
+ if const_stab.level.is_unstable() { Some(const_stab.feature) } else { None }
+ } else {
+ None
+ }
+}
+
+/// Returns `true` if this function must conform to `min_const_fn`
+pub fn is_min_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
+ // Bail out if the signature doesn't contain `const`
+ if !tcx.is_const_fn_raw(def_id) {
+ return false;
+ }
+
+ if tcx.features().staged_api {
+ // In order for a libstd function to be considered min_const_fn
+ // it needs to be stable and have no `rustc_const_unstable` attribute.
+ match tcx.lookup_const_stability(def_id) {
+ // `rustc_const_unstable` functions don't need to conform.
+ Some(&attr::ConstStability { ref level, .. }) if level.is_unstable() => false,
+ None => {
+ if let Some(stab) = tcx.lookup_stability(def_id) {
+ if stab.level.is_stable() {
+ tcx.sess.span_err(
+ tcx.def_span(def_id),
+ "stable const functions must have either `rustc_const_stable` or \
+ `rustc_const_unstable` attribute",
+ );
+ // While we errored above, because we don't know if we need to conform, we
+ // err on the "safe" side and require min_const_fn.
+ true
+ } else {
+ // Unstable functions need not conform to min_const_fn.
+ false
+ }
+ } else {
+ // Internal functions are forced to conform to min_const_fn.
+ // Annotate the internal function with a const stability attribute if
+ // you need to use unstable features.
+ // Note: this is an arbitrary choice that does not affect stability or const
+ // safety or anything, it just changes whether we need to annotate some
+ // internal functions with `rustc_const_stable` or with `rustc_const_unstable`
+ true
+ }
+ }
+ // Everything else needs to conform, because it would be callable from
+ // other `min_const_fn` functions.
+ _ => true,
+ }
+ } else {
+ // users enabling the `const_fn` feature gate can do what they want
+ !tcx.features().const_fn
+ }
+}
+
+pub fn provide(providers: &mut Providers<'_>) {
+ /// Const evaluability whitelist is here to check evaluability at the
+ /// top level beforehand.
+ fn is_const_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> Option<bool> {
+ match tcx.fn_sig(def_id).abi() {
+ Abi::RustIntrinsic | Abi::PlatformIntrinsic => {
+ Some(tcx.lookup_const_stability(def_id).is_some())
+ }
+ _ => None,
+ }
+ }
+
+ /// Checks whether the function has a `const` modifier or, in case it is an intrinsic, whether
+ /// said intrinsic is on the whitelist for being const callable.
+ fn is_const_fn_raw(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
+ let hir_id = tcx
+ .hir()
+ .as_local_hir_id(def_id)
+ .expect("Non-local call to local provider is_const_fn");
+
+ let node = tcx.hir().get(hir_id);
+
+ if let Some(whitelisted) = is_const_intrinsic(tcx, def_id) {
+ whitelisted
+ } else if let Some(fn_like) = FnLikeNode::from_node(node) {
+ fn_like.constness() == hir::Constness::Const
+ } else if let hir::Node::Ctor(_) = node {
+ true
+ } else {
+ false
+ }
+ }
+
+ fn is_promotable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
+ is_const_fn(tcx, def_id)
+ && match tcx.lookup_const_stability(def_id) {
+ Some(stab) => {
+ if cfg!(debug_assertions) && stab.promotable {
+ let sig = tcx.fn_sig(def_id);
+ assert_eq!(
+ sig.unsafety(),
+ hir::Unsafety::Normal,
+ "don't mark const unsafe fns as promotable",
+ // https://github.com/rust-lang/rust/pull/53851#issuecomment-418760682
+ );
+ }
+ stab.promotable
+ }
+ None => false,
+ }
+ }
+
+ fn const_fn_is_allowed_fn_ptr(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
+ is_const_fn(tcx, def_id)
+ && tcx
+ .lookup_const_stability(def_id)
+ .map(|stab| stab.allow_const_fn_ptr)
+ .unwrap_or(false)
+ }
+
+ *providers = Providers {
+ is_const_fn_raw,
+ is_promotable_const_fn,
+ const_fn_is_allowed_fn_ptr,
+ ..*providers
+ };
+}
}
fn find_local(place: &Place<'_>) -> Option<Local> {
- match place.base {
- PlaceBase::Local(local) if !place.is_indirect() => Some(local),
- _ => None,
- }
+ if !place.is_indirect() { Some(place.local) } else { None }
}
impl<'tcx> Visitor<'tcx> for BorrowedLocalsVisitor<'_> {
-use rustc::mir::{self, Body, Location, Place, PlaceBase};
+use rustc::mir::{self, Body, Location, Place};
use rustc::ty::RegionVid;
-use rustc::ty::{self, TyCtxt};
+use rustc::ty::TyCtxt;
use rustc_data_structures::fx::FxHashMap;
use rustc_index::bit_set::BitSet;
pub struct Borrows<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
body: &'a Body<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
borrow_set: Rc<BorrowSet<'tcx>>,
borrows_out_of_scope_at_location: FxHashMap<Location, Vec<BorrowIndex>>,
crate fn new(
tcx: TyCtxt<'tcx>,
body: &'a Body<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
nonlexical_regioncx: Rc<RegionInferenceContext<'tcx>>,
borrow_set: &Rc<BorrowSet<'tcx>>,
) -> Self {
Borrows {
tcx,
body,
- param_env,
borrow_set: borrow_set.clone(),
borrows_out_of_scope_at_location,
_nonlexical_regioncx: nonlexical_regioncx,
fn kill_borrows_on_place(&self, trans: &mut GenKillSet<BorrowIndex>, place: &Place<'tcx>) {
debug!("kill_borrows_on_place: place={:?}", place);
- if let PlaceBase::Local(local) = place.base {
- let other_borrows_of_local =
- self.borrow_set.local_map.get(&local).into_iter().flat_map(|bs| bs.into_iter());
+ let other_borrows_of_local =
+ self.borrow_set.local_map.get(&place.local).into_iter().flat_map(|bs| bs.into_iter());
- // If the borrowed place is a local with no projections, all other borrows of this
- // local must conflict. This is purely an optimization so we don't have to call
- // `places_conflict` for every borrow.
- if place.projection.is_empty() {
- if !self.body.local_decls[local].is_ref_to_static() {
- trans.kill_all(other_borrows_of_local);
- }
- return;
+ // If the borrowed place is a local with no projections, all other borrows of this
+ // local must conflict. This is purely an optimization so we don't have to call
+ // `places_conflict` for every borrow.
+ if place.projection.is_empty() {
+ if !self.body.local_decls[place.local].is_ref_to_static() {
+ trans.kill_all(other_borrows_of_local);
}
-
- // By passing `PlaceConflictBias::NoOverlap`, we conservatively assume that any given
- // pair of array indices are unequal, so that when `places_conflict` returns true, we
- // will be assured that two places being compared definitely denotes the same sets of
- // locations.
- let definitely_conflicting_borrows = other_borrows_of_local.filter(|&&i| {
- places_conflict(
- self.tcx,
- self.param_env,
- self.body,
- &self.borrow_set.borrows[i].borrowed_place,
- place,
- PlaceConflictBias::NoOverlap,
- )
- });
-
- trans.kill_all(definitely_conflicting_borrows);
+ return;
}
+
+ // By passing `PlaceConflictBias::NoOverlap`, we conservatively assume that any given
+ // pair of array indices are unequal, so that when `places_conflict` returns true, we
+ // will be assured that two places being compared definitely denotes the same sets of
+ // locations.
+ let definitely_conflicting_borrows = other_borrows_of_local.filter(|&&i| {
+ places_conflict(
+ self.tcx,
+ self.body,
+ &self.borrow_set.borrows[i].borrowed_place,
+ place,
+ PlaceConflictBias::NoOverlap,
+ )
+ });
+
+ trans.kill_all(definitely_conflicting_borrows);
}
}
fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) {
if let mir::Rvalue::Ref(_, kind, ref borrowed_place) = *rvalue {
if self.borrow_allows_mutation(kind, borrowed_place) {
- match borrowed_place.base {
- mir::PlaceBase::Local(borrowed_local) if !borrowed_place.is_indirect() => {
- self.trans.gen(borrowed_local)
- }
-
- _ => (),
+ if !borrowed_place.is_indirect() {
+ self.trans.gen(borrowed_place.local);
}
}
}
StatementKind::StorageDead(l) => sets.kill(l),
StatementKind::Assign(box (ref place, _))
| StatementKind::SetDiscriminant { box ref place, .. } => {
- if let PlaceBase::Local(local) = place.base {
- sets.gen(local);
- }
+ sets.gen(place.local);
}
StatementKind::InlineAsm(box InlineAsm { ref outputs, .. }) => {
- for p in &**outputs {
- if let PlaceBase::Local(local) = p.base {
- sets.gen(local);
- }
+ for place in &**outputs {
+ sets.gen(place.local);
}
}
_ => (),
fn before_terminator_effect(&self, sets: &mut GenKillSet<Local>, loc: Location) {
self.check_for_borrow(sets, loc);
- if let TerminatorKind::Call {
- destination: Some((Place { base: PlaceBase::Local(local), .. }, _)),
- ..
- } = self.body[loc.block].terminator().kind
+ if let TerminatorKind::Call { destination: Some((Place { local, .. }, _)), .. } =
+ self.body[loc.block].terminator().kind
{
sets.gen(local);
}
_dest_bb: mir::BasicBlock,
dest_place: &mir::Place<'tcx>,
) {
- if let PlaceBase::Local(local) = dest_place.base {
- in_out.insert(local);
- }
+ in_out.insert(dest_place.local);
}
}
const BOTTOM_VALUE: bool;
/// Merges `in_set` into `inout_set`, returning `true` if `inout_set` changed.
+ ///
+ /// It is almost certainly wrong to override this, since it automatically applies
+ /// * `inout_set & in_set` if `BOTTOM_VALUE == true`
+ /// * `inout_set | in_set` if `BOTTOM_VALUE == false`
+ ///
+ /// This means that if a bit is not `BOTTOM_VALUE`, it is propagated into all target blocks.
+ /// For clarity, the above statement again from a different perspective:
+ /// A bit in the block's entry set is `!BOTTOM_VALUE` if *any* predecessor block's bit value is
+ /// `!BOTTOM_VALUE`.
+ ///
+ /// There are situations where you want the opposite behaviour: propagate only if *all*
+ /// predecessor blocks's value is `!BOTTOM_VALUE`.
+ /// E.g. if you want to know whether a bit is *definitely* set at a specific location. This
+ /// means that all code paths leading to the location must have set the bit, instead of any
+ /// code path leading there.
+ ///
+ /// If you want this kind of "definitely set" analysis, you need to
+ /// 1. Invert `BOTTOM_VALUE`
+ /// 2. Reset the `entry_set` in `start_block_effect` to `!BOTTOM_VALUE`
+ /// 3. Override `join` to do the opposite from what it's doing now.
#[inline]
fn join<T: Idx>(&self, inout_set: &mut BitSet<T>, in_set: &BitSet<T>) -> bool {
if Self::BOTTOM_VALUE == false {
/// for each block individually. The entry set for all other basic blocks is
/// initialized to `Self::BOTTOM_VALUE`. The dataflow analysis then
/// iteratively modifies the various entry sets (but leaves the the transfer
-/// function unchanged).
+/// function unchanged). `BottomValue::join` is used to merge the bitsets from
+/// two blocks (e.g. when two blocks' terminator jumps to a single block, that
+/// target block's state is the merged state of both incoming blocks).
pub trait BitDenotation<'tcx>: BottomValue {
/// Specifies what index type is used to access the bitvector.
type Idx: Idx;
/// Maybe we should have separate "borrowck" and "moveck" modes.
fn move_path_for(&mut self, place: &Place<'tcx>) -> Result<MovePathIndex, MoveError<'tcx>> {
debug!("lookup({:?})", place);
- let mut base = match place.base {
- PlaceBase::Local(local) => self.builder.data.rev_lookup.locals[local],
- PlaceBase::Static(..) => {
- return Err(MoveError::cannot_move_out_of(self.loc, Static));
- }
- };
+ let mut base = self.builder.data.rev_lookup.locals[place.local];
// The move path index of the first union that we find. Once this is
// some we stop creating child move paths, since moves from unions
let proj_base = &place.projection[..i];
let body = self.builder.body;
let tcx = self.builder.tcx;
- let place_ty = Place::ty_from(&place.base, proj_base, body, tcx).ty;
+ let place_ty = Place::ty_from(&place.local, proj_base, body, tcx).ty;
match place_ty.kind {
ty::Ref(..) | ty::RawPtr(..) => {
let proj = &place.projection[..i + 1];
self.loc,
BorrowedContent {
target_place: Place {
- base: place.base.clone(),
+ local: place.local,
projection: tcx.intern_place_elems(proj),
},
},
if union_path.is_none() {
base = self.add_move_path(base, elem, |tcx| Place {
- base: place.base.clone(),
+ local: place.local.clone(),
projection: tcx.intern_place_elems(&place.projection[..i + 1]),
});
}
// `ConstIndex` patterns. This is done to ensure that all move paths
// are disjoint, which is expected by drop elaboration.
let base_place = Place {
- base: place.base.clone(),
+ local: place.local.clone(),
projection: self.builder.tcx.intern_place_elems(base),
};
let base_path = match self.move_path_for(&base_place) {
// of the union so it is marked as initialized again.
if let [proj_base @ .., ProjectionElem::Field(_, _)] = place.projection {
if let ty::Adt(def, _) =
- Place::ty_from(place.base, proj_base, self.builder.body, self.builder.tcx).ty.kind
+ Place::ty_from(place.local, proj_base, self.builder.body, self.builder.tcx).ty.kind
{
if def.is_union() {
- place = PlaceRef { base: place.base, projection: proj_base }
+ place = PlaceRef { local: place.local, projection: proj_base }
}
}
}
// unknown place, but will rather return the nearest available
// parent.
pub fn find(&self, place: PlaceRef<'_, '_>) -> LookupResult {
- let mut result = match place.base {
- PlaceBase::Local(local) => self.locals[*local],
- PlaceBase::Static(..) => return LookupResult::Parent(None),
- };
+ let mut result = self.locals[*place.local];
for elem in place.projection.iter() {
if let Some(&subpath) = self.projections.get(&(result, elem.lift())) {
#[derive(Debug)]
pub(crate) enum IllegalMoveOriginKind<'tcx> {
- /// Illegal move due to attempt to move from `static` variable.
- Static,
-
/// Illegal move due to attempt to move from behind a reference.
BorrowedContent {
/// The place the reference refers to: if erroneous code was trying to
+++ /dev/null
-use rustc::mir::interpret::{ConstValue, Scalar};
-use rustc::ty::{self, layout::Size, ParamEnv, Ty, TyCtxt};
-use rustc_span::symbol::Symbol;
-use syntax::ast;
-
-#[derive(PartialEq)]
-crate enum LitToConstError {
- UnparseableFloat,
- Reported,
-}
-
-crate fn lit_to_const<'tcx>(
- lit: &'tcx ast::LitKind,
- tcx: TyCtxt<'tcx>,
- ty: Ty<'tcx>,
- neg: bool,
-) -> Result<&'tcx ty::Const<'tcx>, LitToConstError> {
- use syntax::ast::*;
-
- let trunc = |n| {
- let param_ty = ParamEnv::reveal_all().and(ty);
- let width = tcx.layout_of(param_ty).map_err(|_| LitToConstError::Reported)?.size;
- trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits());
- let result = truncate(n, width);
- trace!("trunc result: {}", result);
- Ok(ConstValue::Scalar(Scalar::from_uint(result, width)))
- };
-
- use rustc::mir::interpret::*;
- let lit = match *lit {
- LitKind::Str(ref s, _) => {
- let s = s.as_str();
- let allocation = Allocation::from_byte_aligned_bytes(s.as_bytes());
- let allocation = tcx.intern_const_alloc(allocation);
- ConstValue::Slice { data: allocation, start: 0, end: s.len() }
- }
- LitKind::ByteStr(ref data) => {
- let id = tcx.allocate_bytes(data);
- ConstValue::Scalar(Scalar::Ptr(id.into()))
- }
- LitKind::Byte(n) => ConstValue::Scalar(Scalar::from_uint(n, Size::from_bytes(1))),
- LitKind::Int(n, _) if neg => {
- let n = n as i128;
- let n = n.overflowing_neg().0;
- trunc(n as u128)?
- }
- LitKind::Int(n, _) => trunc(n)?,
- LitKind::Float(n, _) => {
- let fty = match ty.kind {
- ty::Float(fty) => fty,
- _ => bug!(),
- };
- parse_float(n, fty, neg).map_err(|_| LitToConstError::UnparseableFloat)?
- }
- LitKind::Bool(b) => ConstValue::Scalar(Scalar::from_bool(b)),
- LitKind::Char(c) => ConstValue::Scalar(Scalar::from_char(c)),
- LitKind::Err(_) => unreachable!(),
- };
- Ok(tcx.mk_const(ty::Const { val: ty::ConstKind::Value(lit), ty }))
-}
-
-fn parse_float<'tcx>(num: Symbol, fty: ast::FloatTy, neg: bool) -> Result<ConstValue<'tcx>, ()> {
- let num = num.as_str();
- use rustc_apfloat::ieee::{Double, Single};
- let scalar = match fty {
- ast::FloatTy::F32 => {
- num.parse::<f32>().map_err(|_| ())?;
- let mut f = num.parse::<Single>().unwrap_or_else(|e| {
- panic!("apfloat::ieee::Single failed to parse `{}`: {:?}", num, e)
- });
- if neg {
- f = -f;
- }
- Scalar::from_f32(f)
- }
- ast::FloatTy::F64 => {
- num.parse::<f64>().map_err(|_| ())?;
- let mut f = num.parse::<Double>().unwrap_or_else(|e| {
- panic!("apfloat::ieee::Double failed to parse `{}`: {:?}", num, e)
- });
- if neg {
- f = -f;
- }
- Scalar::from_f64(f)
- }
- };
-
- Ok(ConstValue::Scalar(scalar))
-}
+++ /dev/null
-use crate::hair::cx::to_ref::ToRef;
-use crate::hair::cx::Cx;
-use crate::hair::{self, *};
-
-use rustc::middle::region;
-use rustc::ty;
-use rustc_hir as hir;
-
-use rustc_index::vec::Idx;
-
-impl<'tcx> Mirror<'tcx> for &'tcx hir::Block<'tcx> {
- type Output = Block<'tcx>;
-
- fn make_mirror(self, cx: &mut Cx<'_, 'tcx>) -> Block<'tcx> {
- // We have to eagerly lower the "spine" of the statements
- // in order to get the lexical scoping correctly.
- let stmts = mirror_stmts(cx, self.hir_id.local_id, &*self.stmts);
- let opt_destruction_scope =
- cx.region_scope_tree.opt_destruction_scope(self.hir_id.local_id);
- Block {
- targeted_by_break: self.targeted_by_break,
- region_scope: region::Scope { id: self.hir_id.local_id, data: region::ScopeData::Node },
- opt_destruction_scope,
- span: self.span,
- stmts,
- expr: self.expr.to_ref(),
- safety_mode: match self.rules {
- hir::BlockCheckMode::DefaultBlock => BlockSafety::Safe,
- hir::BlockCheckMode::UnsafeBlock(..) => BlockSafety::ExplicitUnsafe(self.hir_id),
- hir::BlockCheckMode::PushUnsafeBlock(..) => BlockSafety::PushUnsafe,
- hir::BlockCheckMode::PopUnsafeBlock(..) => BlockSafety::PopUnsafe,
- },
- }
- }
-}
-
-fn mirror_stmts<'a, 'tcx>(
- cx: &mut Cx<'a, 'tcx>,
- block_id: hir::ItemLocalId,
- stmts: &'tcx [hir::Stmt<'tcx>],
-) -> Vec<StmtRef<'tcx>> {
- let mut result = vec![];
- for (index, stmt) in stmts.iter().enumerate() {
- let hir_id = stmt.hir_id;
- let opt_dxn_ext = cx.region_scope_tree.opt_destruction_scope(hir_id.local_id);
- match stmt.kind {
- hir::StmtKind::Expr(ref expr) | hir::StmtKind::Semi(ref expr) => {
- result.push(StmtRef::Mirror(Box::new(Stmt {
- kind: StmtKind::Expr {
- scope: region::Scope { id: hir_id.local_id, data: region::ScopeData::Node },
- expr: expr.to_ref(),
- },
- opt_destruction_scope: opt_dxn_ext,
- })))
- }
- hir::StmtKind::Item(..) => {
- // ignore for purposes of the MIR
- }
- hir::StmtKind::Local(ref local) => {
- let remainder_scope = region::Scope {
- id: block_id,
- data: region::ScopeData::Remainder(region::FirstStatementIndex::new(index)),
- };
-
- let mut pattern = cx.pattern_from_hir(&local.pat);
-
- if let Some(ty) = &local.ty {
- if let Some(&user_ty) = cx.tables.user_provided_types().get(ty.hir_id) {
- debug!("mirror_stmts: user_ty={:?}", user_ty);
- pattern = Pat {
- ty: pattern.ty,
- span: pattern.span,
- kind: Box::new(PatKind::AscribeUserType {
- ascription: hair::pattern::Ascription {
- user_ty: PatTyProj::from_user_type(user_ty),
- user_ty_span: ty.span,
- variance: ty::Variance::Covariant,
- },
- subpattern: pattern,
- }),
- };
- }
- }
-
- result.push(StmtRef::Mirror(Box::new(Stmt {
- kind: StmtKind::Let {
- remainder_scope: remainder_scope,
- init_scope: region::Scope {
- id: hir_id.local_id,
- data: region::ScopeData::Node,
- },
- pattern,
- initializer: local.init.to_ref(),
- lint_level: LintLevel::Explicit(local.hir_id),
- },
- opt_destruction_scope: opt_dxn_ext,
- })));
- }
- }
- }
- return result;
-}
-
-pub fn to_expr_ref<'a, 'tcx>(
- cx: &mut Cx<'a, 'tcx>,
- block: &'tcx hir::Block<'tcx>,
-) -> ExprRef<'tcx> {
- let block_ty = cx.tables().node_type(block.hir_id);
- let temp_lifetime = cx.region_scope_tree.temporary_scope(block.hir_id.local_id);
- let expr = Expr {
- ty: block_ty,
- temp_lifetime,
- span: block.span,
- kind: ExprKind::Block { body: block },
- };
- expr.to_ref()
-}
+++ /dev/null
-use crate::hair::cx::block;
-use crate::hair::cx::to_ref::ToRef;
-use crate::hair::cx::Cx;
-use crate::hair::util::UserAnnotatedTyHelpers;
-use crate::hair::*;
-use rustc::mir::interpret::{ErrorHandled, Scalar};
-use rustc::mir::BorrowKind;
-use rustc::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, PointerCast};
-use rustc::ty::subst::{InternalSubsts, SubstsRef};
-use rustc::ty::{self, AdtKind, Ty};
-use rustc_hir as hir;
-use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
-use rustc_hir::def_id::LocalDefId;
-use rustc_index::vec::Idx;
-use rustc_span::Span;
-
-impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr<'tcx> {
- type Output = Expr<'tcx>;
-
- fn make_mirror(self, cx: &mut Cx<'_, 'tcx>) -> Expr<'tcx> {
- let temp_lifetime = cx.region_scope_tree.temporary_scope(self.hir_id.local_id);
- let expr_scope = region::Scope { id: self.hir_id.local_id, data: region::ScopeData::Node };
-
- debug!("Expr::make_mirror(): id={}, span={:?}", self.hir_id, self.span);
-
- let mut expr = make_mirror_unadjusted(cx, self);
-
- // Now apply adjustments, if any.
- for adjustment in cx.tables().expr_adjustments(self) {
- debug!("make_mirror: expr={:?} applying adjustment={:?}", expr, adjustment);
- expr = apply_adjustment(cx, self, expr, adjustment);
- }
-
- // Next, wrap this up in the expr's scope.
- expr = Expr {
- temp_lifetime,
- ty: expr.ty,
- span: self.span,
- kind: ExprKind::Scope {
- region_scope: expr_scope,
- value: expr.to_ref(),
- lint_level: LintLevel::Explicit(self.hir_id),
- },
- };
-
- // Finally, create a destruction scope, if any.
- if let Some(region_scope) = cx.region_scope_tree.opt_destruction_scope(self.hir_id.local_id)
- {
- expr = Expr {
- temp_lifetime,
- ty: expr.ty,
- span: self.span,
- kind: ExprKind::Scope {
- region_scope,
- value: expr.to_ref(),
- lint_level: LintLevel::Inherited,
- },
- };
- }
-
- // OK, all done!
- expr
- }
-}
-
-fn apply_adjustment<'a, 'tcx>(
- cx: &mut Cx<'a, 'tcx>,
- hir_expr: &'tcx hir::Expr<'tcx>,
- mut expr: Expr<'tcx>,
- adjustment: &Adjustment<'tcx>,
-) -> Expr<'tcx> {
- let Expr { temp_lifetime, mut span, .. } = expr;
-
- // Adjust the span from the block, to the last expression of the
- // block. This is a better span when returning a mutable reference
- // with too short a lifetime. The error message will use the span
- // from the assignment to the return place, which should only point
- // at the returned value, not the entire function body.
- //
- // fn return_short_lived<'a>(x: &'a mut i32) -> &'static mut i32 {
- // x
- // // ^ error message points at this expression.
- // }
- let mut adjust_span = |expr: &mut Expr<'tcx>| {
- if let ExprKind::Block { body } = expr.kind {
- if let Some(ref last_expr) = body.expr {
- span = last_expr.span;
- expr.span = span;
- }
- }
- };
-
- let kind = match adjustment.kind {
- Adjust::Pointer(PointerCast::Unsize) => {
- adjust_span(&mut expr);
- ExprKind::Pointer { cast: PointerCast::Unsize, source: expr.to_ref() }
- }
- Adjust::Pointer(cast) => ExprKind::Pointer { cast, source: expr.to_ref() },
- Adjust::NeverToAny => ExprKind::NeverToAny { source: expr.to_ref() },
- Adjust::Deref(None) => {
- adjust_span(&mut expr);
- ExprKind::Deref { arg: expr.to_ref() }
- }
- Adjust::Deref(Some(deref)) => {
- // We don't need to do call adjust_span here since
- // deref coercions always start with a built-in deref.
- let call = deref.method_call(cx.tcx(), expr.ty);
-
- expr = Expr {
- temp_lifetime,
- ty: cx.tcx.mk_ref(deref.region, ty::TypeAndMut { ty: expr.ty, mutbl: deref.mutbl }),
- span,
- kind: ExprKind::Borrow {
- borrow_kind: deref.mutbl.to_borrow_kind(),
- arg: expr.to_ref(),
- },
- };
-
- overloaded_place(cx, hir_expr, adjustment.target, Some(call), vec![expr.to_ref()])
- }
- Adjust::Borrow(AutoBorrow::Ref(_, m)) => {
- ExprKind::Borrow { borrow_kind: m.to_borrow_kind(), arg: expr.to_ref() }
- }
- Adjust::Borrow(AutoBorrow::RawPtr(mutability)) => {
- ExprKind::AddressOf { mutability, arg: expr.to_ref() }
- }
- };
-
- Expr { temp_lifetime, ty: adjustment.target, span, kind }
-}
-
-fn make_mirror_unadjusted<'a, 'tcx>(
- cx: &mut Cx<'a, 'tcx>,
- expr: &'tcx hir::Expr<'tcx>,
-) -> Expr<'tcx> {
- let expr_ty = cx.tables().expr_ty(expr);
- let temp_lifetime = cx.region_scope_tree.temporary_scope(expr.hir_id.local_id);
-
- let kind = match expr.kind {
- // Here comes the interesting stuff:
- hir::ExprKind::MethodCall(_, method_span, ref args) => {
- // Rewrite a.b(c) into UFCS form like Trait::b(a, c)
- let expr = method_callee(cx, expr, method_span, None);
- let args = args.iter().map(|e| e.to_ref()).collect();
- ExprKind::Call { ty: expr.ty, fun: expr.to_ref(), args, from_hir_call: true }
- }
-
- hir::ExprKind::Call(ref fun, ref args) => {
- if cx.tables().is_method_call(expr) {
- // The callee is something implementing Fn, FnMut, or FnOnce.
- // Find the actual method implementation being called and
- // build the appropriate UFCS call expression with the
- // callee-object as expr parameter.
-
- // rewrite f(u, v) into FnOnce::call_once(f, (u, v))
-
- let method = method_callee(cx, expr, fun.span, None);
-
- let arg_tys = args.iter().map(|e| cx.tables().expr_ty_adjusted(e));
- let tupled_args = Expr {
- ty: cx.tcx.mk_tup(arg_tys),
- temp_lifetime,
- span: expr.span,
- kind: ExprKind::Tuple { fields: args.iter().map(ToRef::to_ref).collect() },
- };
-
- ExprKind::Call {
- ty: method.ty,
- fun: method.to_ref(),
- args: vec![fun.to_ref(), tupled_args.to_ref()],
- from_hir_call: true,
- }
- } else {
- let adt_data =
- if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = fun.kind {
- // Tuple-like ADTs are represented as ExprKind::Call. We convert them here.
- expr_ty.ty_adt_def().and_then(|adt_def| match path.res {
- Res::Def(DefKind::Ctor(_, CtorKind::Fn), ctor_id) => {
- Some((adt_def, adt_def.variant_index_with_ctor_id(ctor_id)))
- }
- Res::SelfCtor(..) => Some((adt_def, VariantIdx::new(0))),
- _ => None,
- })
- } else {
- None
- };
- if let Some((adt_def, index)) = adt_data {
- let substs = cx.tables().node_substs(fun.hir_id);
- let user_provided_types = cx.tables().user_provided_types();
- let user_ty =
- user_provided_types.get(fun.hir_id).map(|u_ty| *u_ty).map(|mut u_ty| {
- if let UserType::TypeOf(ref mut did, _) = &mut u_ty.value {
- *did = adt_def.did;
- }
- u_ty
- });
- debug!("make_mirror_unadjusted: (call) user_ty={:?}", user_ty);
-
- let field_refs = args
- .iter()
- .enumerate()
- .map(|(idx, e)| FieldExprRef { name: Field::new(idx), expr: e.to_ref() })
- .collect();
- ExprKind::Adt {
- adt_def,
- substs,
- variant_index: index,
- fields: field_refs,
- user_ty,
- base: None,
- }
- } else {
- ExprKind::Call {
- ty: cx.tables().node_type(fun.hir_id),
- fun: fun.to_ref(),
- args: args.to_ref(),
- from_hir_call: true,
- }
- }
- }
- }
-
- hir::ExprKind::AddrOf(hir::BorrowKind::Ref, mutbl, ref arg) => {
- ExprKind::Borrow { borrow_kind: mutbl.to_borrow_kind(), arg: arg.to_ref() }
- }
-
- hir::ExprKind::AddrOf(hir::BorrowKind::Raw, mutability, ref arg) => {
- ExprKind::AddressOf { mutability, arg: arg.to_ref() }
- }
-
- hir::ExprKind::Block(ref blk, _) => ExprKind::Block { body: &blk },
-
- hir::ExprKind::Assign(ref lhs, ref rhs, _) => {
- ExprKind::Assign { lhs: lhs.to_ref(), rhs: rhs.to_ref() }
- }
-
- hir::ExprKind::AssignOp(op, ref lhs, ref rhs) => {
- if cx.tables().is_method_call(expr) {
- overloaded_operator(cx, expr, vec![lhs.to_ref(), rhs.to_ref()])
- } else {
- ExprKind::AssignOp { op: bin_op(op.node), lhs: lhs.to_ref(), rhs: rhs.to_ref() }
- }
- }
-
- hir::ExprKind::Lit(ref lit) => ExprKind::Literal {
- literal: cx.const_eval_literal(&lit.node, expr_ty, lit.span, false),
- user_ty: None,
- },
-
- hir::ExprKind::Binary(op, ref lhs, ref rhs) => {
- if cx.tables().is_method_call(expr) {
- overloaded_operator(cx, expr, vec![lhs.to_ref(), rhs.to_ref()])
- } else {
- // FIXME overflow
- match (op.node, cx.constness) {
- // Destroy control flow if `#![feature(const_if_match)]` is not enabled.
- (hir::BinOpKind::And, hir::Constness::Const)
- if !cx.tcx.features().const_if_match =>
- {
- cx.control_flow_destroyed.push((op.span, "`&&` operator".into()));
- ExprKind::Binary { op: BinOp::BitAnd, lhs: lhs.to_ref(), rhs: rhs.to_ref() }
- }
- (hir::BinOpKind::Or, hir::Constness::Const)
- if !cx.tcx.features().const_if_match =>
- {
- cx.control_flow_destroyed.push((op.span, "`||` operator".into()));
- ExprKind::Binary { op: BinOp::BitOr, lhs: lhs.to_ref(), rhs: rhs.to_ref() }
- }
-
- (hir::BinOpKind::And, _) => ExprKind::LogicalOp {
- op: LogicalOp::And,
- lhs: lhs.to_ref(),
- rhs: rhs.to_ref(),
- },
- (hir::BinOpKind::Or, _) => ExprKind::LogicalOp {
- op: LogicalOp::Or,
- lhs: lhs.to_ref(),
- rhs: rhs.to_ref(),
- },
-
- _ => {
- let op = bin_op(op.node);
- ExprKind::Binary { op, lhs: lhs.to_ref(), rhs: rhs.to_ref() }
- }
- }
- }
- }
-
- hir::ExprKind::Index(ref lhs, ref index) => {
- if cx.tables().is_method_call(expr) {
- overloaded_place(cx, expr, expr_ty, None, vec![lhs.to_ref(), index.to_ref()])
- } else {
- ExprKind::Index { lhs: lhs.to_ref(), index: index.to_ref() }
- }
- }
-
- hir::ExprKind::Unary(hir::UnOp::UnDeref, ref arg) => {
- if cx.tables().is_method_call(expr) {
- overloaded_place(cx, expr, expr_ty, None, vec![arg.to_ref()])
- } else {
- ExprKind::Deref { arg: arg.to_ref() }
- }
- }
-
- hir::ExprKind::Unary(hir::UnOp::UnNot, ref arg) => {
- if cx.tables().is_method_call(expr) {
- overloaded_operator(cx, expr, vec![arg.to_ref()])
- } else {
- ExprKind::Unary { op: UnOp::Not, arg: arg.to_ref() }
- }
- }
-
- hir::ExprKind::Unary(hir::UnOp::UnNeg, ref arg) => {
- if cx.tables().is_method_call(expr) {
- overloaded_operator(cx, expr, vec![arg.to_ref()])
- } else {
- if let hir::ExprKind::Lit(ref lit) = arg.kind {
- ExprKind::Literal {
- literal: cx.const_eval_literal(&lit.node, expr_ty, lit.span, true),
- user_ty: None,
- }
- } else {
- ExprKind::Unary { op: UnOp::Neg, arg: arg.to_ref() }
- }
- }
- }
-
- hir::ExprKind::Struct(ref qpath, ref fields, ref base) => match expr_ty.kind {
- ty::Adt(adt, substs) => match adt.adt_kind() {
- AdtKind::Struct | AdtKind::Union => {
- let user_provided_types = cx.tables().user_provided_types();
- let user_ty = user_provided_types.get(expr.hir_id).map(|u_ty| *u_ty);
- debug!("make_mirror_unadjusted: (struct/union) user_ty={:?}", user_ty);
- ExprKind::Adt {
- adt_def: adt,
- variant_index: VariantIdx::new(0),
- substs,
- user_ty,
- fields: field_refs(cx, fields),
- base: base.as_ref().map(|base| FruInfo {
- base: base.to_ref(),
- field_types: cx.tables().fru_field_types()[expr.hir_id].clone(),
- }),
- }
- }
- AdtKind::Enum => {
- let res = cx.tables().qpath_res(qpath, expr.hir_id);
- match res {
- Res::Def(DefKind::Variant, variant_id) => {
- assert!(base.is_none());
-
- let index = adt.variant_index_with_id(variant_id);
- let user_provided_types = cx.tables().user_provided_types();
- let user_ty = user_provided_types.get(expr.hir_id).map(|u_ty| *u_ty);
- debug!("make_mirror_unadjusted: (variant) user_ty={:?}", user_ty);
- ExprKind::Adt {
- adt_def: adt,
- variant_index: index,
- substs,
- user_ty,
- fields: field_refs(cx, fields),
- base: None,
- }
- }
- _ => {
- span_bug!(expr.span, "unexpected res: {:?}", res);
- }
- }
- }
- },
- _ => {
- span_bug!(expr.span, "unexpected type for struct literal: {:?}", expr_ty);
- }
- },
-
- hir::ExprKind::Closure(..) => {
- let closure_ty = cx.tables().expr_ty(expr);
- let (def_id, substs, movability) = match closure_ty.kind {
- ty::Closure(def_id, substs) => (def_id, UpvarSubsts::Closure(substs), None),
- ty::Generator(def_id, substs, movability) => {
- (def_id, UpvarSubsts::Generator(substs), Some(movability))
- }
- _ => {
- span_bug!(expr.span, "closure expr w/o closure type: {:?}", closure_ty);
- }
- };
- let upvars = cx
- .tcx
- .upvars(def_id)
- .iter()
- .flat_map(|upvars| upvars.iter())
- .zip(substs.upvar_tys(def_id, cx.tcx))
- .map(|((&var_hir_id, _), ty)| capture_upvar(cx, expr, var_hir_id, ty))
- .collect();
- ExprKind::Closure { closure_id: def_id, substs, upvars, movability }
- }
-
- hir::ExprKind::Path(ref qpath) => {
- let res = cx.tables().qpath_res(qpath, expr.hir_id);
- convert_path_expr(cx, expr, res)
- }
-
- hir::ExprKind::InlineAsm(ref asm) => ExprKind::InlineAsm {
- asm: &asm.inner,
- outputs: asm.outputs_exprs.to_ref(),
- inputs: asm.inputs_exprs.to_ref(),
- },
-
- // Now comes the rote stuff:
- hir::ExprKind::Repeat(ref v, ref count) => {
- let def_id = cx.tcx.hir().local_def_id(count.hir_id);
- let substs = InternalSubsts::identity_for_item(cx.tcx, def_id);
- let span = cx.tcx.def_span(def_id);
- let count = match cx.tcx.const_eval_resolve(cx.param_env, def_id, substs, Some(span)) {
- Ok(cv) => cv.eval_usize(cx.tcx, cx.param_env),
- Err(ErrorHandled::Reported) => 0,
- Err(ErrorHandled::TooGeneric) => {
- let span = cx.tcx.def_span(def_id);
- cx.tcx.sess.span_err(span, "array lengths can't depend on generic parameters");
- 0
- }
- };
-
- ExprKind::Repeat { value: v.to_ref(), count }
- }
- hir::ExprKind::Ret(ref v) => ExprKind::Return { value: v.to_ref() },
- hir::ExprKind::Break(dest, ref value) => match dest.target_id {
- Ok(target_id) => ExprKind::Break {
- label: region::Scope { id: target_id.local_id, data: region::ScopeData::Node },
- value: value.to_ref(),
- },
- Err(err) => bug!("invalid loop id for break: {}", err),
- },
- hir::ExprKind::Continue(dest) => match dest.target_id {
- Ok(loop_id) => ExprKind::Continue {
- label: region::Scope { id: loop_id.local_id, data: region::ScopeData::Node },
- },
- Err(err) => bug!("invalid loop id for continue: {}", err),
- },
- hir::ExprKind::Match(ref discr, ref arms, _) => ExprKind::Match {
- scrutinee: discr.to_ref(),
- arms: arms.iter().map(|a| convert_arm(cx, a)).collect(),
- },
- hir::ExprKind::Loop(ref body, _, _) => {
- ExprKind::Loop { body: block::to_expr_ref(cx, body) }
- }
- hir::ExprKind::Field(ref source, ..) => ExprKind::Field {
- lhs: source.to_ref(),
- name: Field::new(cx.tcx.field_index(expr.hir_id, cx.tables)),
- },
- hir::ExprKind::Cast(ref source, ref cast_ty) => {
- // Check for a user-given type annotation on this `cast`
- let user_provided_types = cx.tables.user_provided_types();
- let user_ty = user_provided_types.get(cast_ty.hir_id);
-
- debug!(
- "cast({:?}) has ty w/ hir_id {:?} and user provided ty {:?}",
- expr, cast_ty.hir_id, user_ty,
- );
-
- // Check to see if this cast is a "coercion cast", where the cast is actually done
- // using a coercion (or is a no-op).
- let cast = if cx.tables().is_coercion_cast(source.hir_id) {
- // Convert the lexpr to a vexpr.
- ExprKind::Use { source: source.to_ref() }
- } else if cx.tables().expr_ty(source).is_region_ptr() {
- // Special cased so that we can type check that the element
- // type of the source matches the pointed to type of the
- // destination.
- ExprKind::Pointer { source: source.to_ref(), cast: PointerCast::ArrayToPointer }
- } else {
- // check whether this is casting an enum variant discriminant
- // to prevent cycles, we refer to the discriminant initializer
- // which is always an integer and thus doesn't need to know the
- // enum's layout (or its tag type) to compute it during const eval
- // Example:
- // enum Foo {
- // A,
- // B = A as isize + 4,
- // }
- // The correct solution would be to add symbolic computations to miri,
- // so we wouldn't have to compute and store the actual value
- let var = if let hir::ExprKind::Path(ref qpath) = source.kind {
- let res = cx.tables().qpath_res(qpath, source.hir_id);
- cx.tables().node_type(source.hir_id).ty_adt_def().and_then(
- |adt_def| match res {
- Res::Def(
- DefKind::Ctor(CtorOf::Variant, CtorKind::Const),
- variant_ctor_id,
- ) => {
- let idx = adt_def.variant_index_with_ctor_id(variant_ctor_id);
- let (d, o) = adt_def.discriminant_def_for_variant(idx);
- use rustc::ty::util::IntTypeExt;
- let ty = adt_def.repr.discr_type();
- let ty = ty.to_ty(cx.tcx());
- Some((d, o, ty))
- }
- _ => None,
- },
- )
- } else {
- None
- };
-
- let source = if let Some((did, offset, var_ty)) = var {
- let mk_const = |literal| {
- Expr {
- temp_lifetime,
- ty: var_ty,
- span: expr.span,
- kind: ExprKind::Literal { literal, user_ty: None },
- }
- .to_ref()
- };
- let offset = mk_const(ty::Const::from_bits(
- cx.tcx,
- offset as u128,
- cx.param_env.and(var_ty),
- ));
- match did {
- Some(did) => {
- // in case we are offsetting from a computed discriminant
- // and not the beginning of discriminants (which is always `0`)
- let substs = InternalSubsts::identity_for_item(cx.tcx(), did);
- let lhs = mk_const(cx.tcx().mk_const(ty::Const {
- val: ty::ConstKind::Unevaluated(did, substs),
- ty: var_ty,
- }));
- let bin = ExprKind::Binary { op: BinOp::Add, lhs, rhs: offset };
- Expr { temp_lifetime, ty: var_ty, span: expr.span, kind: bin }.to_ref()
- }
- None => offset,
- }
- } else {
- source.to_ref()
- };
-
- ExprKind::Cast { source }
- };
-
- if let Some(user_ty) = user_ty {
- // NOTE: Creating a new Expr and wrapping a Cast inside of it may be
- // inefficient, revisit this when performance becomes an issue.
- let cast_expr = Expr { temp_lifetime, ty: expr_ty, span: expr.span, kind: cast };
- debug!("make_mirror_unadjusted: (cast) user_ty={:?}", user_ty);
-
- ExprKind::ValueTypeAscription {
- source: cast_expr.to_ref(),
- user_ty: Some(*user_ty),
- }
- } else {
- cast
- }
- }
- hir::ExprKind::Type(ref source, ref ty) => {
- let user_provided_types = cx.tables.user_provided_types();
- let user_ty = user_provided_types.get(ty.hir_id).map(|u_ty| *u_ty);
- debug!("make_mirror_unadjusted: (type) user_ty={:?}", user_ty);
- if source.is_syntactic_place_expr() {
- ExprKind::PlaceTypeAscription { source: source.to_ref(), user_ty }
- } else {
- ExprKind::ValueTypeAscription { source: source.to_ref(), user_ty }
- }
- }
- hir::ExprKind::DropTemps(ref source) => ExprKind::Use { source: source.to_ref() },
- hir::ExprKind::Box(ref value) => ExprKind::Box { value: value.to_ref() },
- hir::ExprKind::Array(ref fields) => ExprKind::Array { fields: fields.to_ref() },
- hir::ExprKind::Tup(ref fields) => ExprKind::Tuple { fields: fields.to_ref() },
-
- hir::ExprKind::Yield(ref v, _) => ExprKind::Yield { value: v.to_ref() },
- hir::ExprKind::Err => unreachable!(),
- };
-
- Expr { temp_lifetime, ty: expr_ty, span: expr.span, kind }
-}
-
-fn user_substs_applied_to_res(
- cx: &mut Cx<'a, 'tcx>,
- hir_id: hir::HirId,
- res: Res,
-) -> Option<ty::CanonicalUserType<'tcx>> {
- debug!("user_substs_applied_to_res: res={:?}", res);
- let user_provided_type = match res {
- // A reference to something callable -- e.g., a fn, method, or
- // a tuple-struct or tuple-variant. This has the type of a
- // `Fn` but with the user-given substitutions.
- Res::Def(DefKind::Fn, _)
- | Res::Def(DefKind::Method, _)
- | Res::Def(DefKind::Ctor(_, CtorKind::Fn), _)
- | Res::Def(DefKind::Const, _)
- | Res::Def(DefKind::AssocConst, _) => {
- cx.tables().user_provided_types().get(hir_id).map(|u_ty| *u_ty)
- }
-
- // A unit struct/variant which is used as a value (e.g.,
- // `None`). This has the type of the enum/struct that defines
- // this variant -- but with the substitutions given by the
- // user.
- Res::Def(DefKind::Ctor(_, CtorKind::Const), _) => {
- cx.user_substs_applied_to_ty_of_hir_id(hir_id)
- }
-
- // `Self` is used in expression as a tuple struct constructor or an unit struct constructor
- Res::SelfCtor(_) => cx.user_substs_applied_to_ty_of_hir_id(hir_id),
-
- _ => bug!("user_substs_applied_to_res: unexpected res {:?} at {:?}", res, hir_id),
- };
- debug!("user_substs_applied_to_res: user_provided_type={:?}", user_provided_type);
- user_provided_type
-}
-
-fn method_callee<'a, 'tcx>(
- cx: &mut Cx<'a, 'tcx>,
- expr: &hir::Expr<'_>,
- span: Span,
- overloaded_callee: Option<(DefId, SubstsRef<'tcx>)>,
-) -> Expr<'tcx> {
- let temp_lifetime = cx.region_scope_tree.temporary_scope(expr.hir_id.local_id);
- let (def_id, substs, user_ty) = match overloaded_callee {
- Some((def_id, substs)) => (def_id, substs, None),
- None => {
- let (kind, def_id) = cx
- .tables()
- .type_dependent_def(expr.hir_id)
- .unwrap_or_else(|| span_bug!(expr.span, "no type-dependent def for method callee"));
- let user_ty = user_substs_applied_to_res(cx, expr.hir_id, Res::Def(kind, def_id));
- debug!("method_callee: user_ty={:?}", user_ty);
- (def_id, cx.tables().node_substs(expr.hir_id), user_ty)
- }
- };
- let ty = cx.tcx().mk_fn_def(def_id, substs);
- Expr {
- temp_lifetime,
- ty,
- span,
- kind: ExprKind::Literal { literal: ty::Const::zero_sized(cx.tcx(), ty), user_ty },
- }
-}
-
-trait ToBorrowKind {
- fn to_borrow_kind(&self) -> BorrowKind;
-}
-
-impl ToBorrowKind for AutoBorrowMutability {
- fn to_borrow_kind(&self) -> BorrowKind {
- use rustc::ty::adjustment::AllowTwoPhase;
- match *self {
- AutoBorrowMutability::Mut { allow_two_phase_borrow } => BorrowKind::Mut {
- allow_two_phase_borrow: match allow_two_phase_borrow {
- AllowTwoPhase::Yes => true,
- AllowTwoPhase::No => false,
- },
- },
- AutoBorrowMutability::Not => BorrowKind::Shared,
- }
- }
-}
-
-impl ToBorrowKind for hir::Mutability {
- fn to_borrow_kind(&self) -> BorrowKind {
- match *self {
- hir::Mutability::Mut => BorrowKind::Mut { allow_two_phase_borrow: false },
- hir::Mutability::Not => BorrowKind::Shared,
- }
- }
-}
-
-fn convert_arm<'tcx>(cx: &mut Cx<'_, 'tcx>, arm: &'tcx hir::Arm<'tcx>) -> Arm<'tcx> {
- Arm {
- pattern: cx.pattern_from_hir(&arm.pat),
- guard: match arm.guard {
- Some(hir::Guard::If(ref e)) => Some(Guard::If(e.to_ref())),
- _ => None,
- },
- body: arm.body.to_ref(),
- lint_level: LintLevel::Explicit(arm.hir_id),
- scope: region::Scope { id: arm.hir_id.local_id, data: region::ScopeData::Node },
- span: arm.span,
- }
-}
-
-fn convert_path_expr<'a, 'tcx>(
- cx: &mut Cx<'a, 'tcx>,
- expr: &'tcx hir::Expr<'tcx>,
- res: Res,
-) -> ExprKind<'tcx> {
- let substs = cx.tables().node_substs(expr.hir_id);
- match res {
- // A regular function, constructor function or a constant.
- Res::Def(DefKind::Fn, _)
- | Res::Def(DefKind::Method, _)
- | Res::Def(DefKind::Ctor(_, CtorKind::Fn), _)
- | Res::SelfCtor(..) => {
- let user_ty = user_substs_applied_to_res(cx, expr.hir_id, res);
- debug!("convert_path_expr: user_ty={:?}", user_ty);
- ExprKind::Literal {
- literal: ty::Const::zero_sized(cx.tcx, cx.tables().node_type(expr.hir_id)),
- user_ty,
- }
- }
-
- Res::Def(DefKind::ConstParam, def_id) => {
- let hir_id = cx.tcx.hir().as_local_hir_id(def_id).unwrap();
- let item_id = cx.tcx.hir().get_parent_node(hir_id);
- let item_def_id = cx.tcx.hir().local_def_id(item_id);
- let generics = cx.tcx.generics_of(item_def_id);
- let local_def_id = cx.tcx.hir().local_def_id(hir_id);
- let index = generics.param_def_id_to_index[&local_def_id];
- let name = cx.tcx.hir().name(hir_id);
- let val = ty::ConstKind::Param(ty::ParamConst::new(index, name));
- ExprKind::Literal {
- literal: cx.tcx.mk_const(ty::Const { val, ty: cx.tables().node_type(expr.hir_id) }),
- user_ty: None,
- }
- }
-
- Res::Def(DefKind::Const, def_id) | Res::Def(DefKind::AssocConst, def_id) => {
- let user_ty = user_substs_applied_to_res(cx, expr.hir_id, res);
- debug!("convert_path_expr: (const) user_ty={:?}", user_ty);
- ExprKind::Literal {
- literal: cx.tcx.mk_const(ty::Const {
- val: ty::ConstKind::Unevaluated(def_id, substs),
- ty: cx.tables().node_type(expr.hir_id),
- }),
- user_ty,
- }
- }
-
- Res::Def(DefKind::Ctor(_, CtorKind::Const), def_id) => {
- let user_provided_types = cx.tables.user_provided_types();
- let user_provided_type = user_provided_types.get(expr.hir_id).map(|u_ty| *u_ty);
- debug!("convert_path_expr: user_provided_type={:?}", user_provided_type);
- let ty = cx.tables().node_type(expr.hir_id);
- match ty.kind {
- // A unit struct/variant which is used as a value.
- // We return a completely different ExprKind here to account for this special case.
- ty::Adt(adt_def, substs) => ExprKind::Adt {
- adt_def,
- variant_index: adt_def.variant_index_with_ctor_id(def_id),
- substs,
- user_ty: user_provided_type,
- fields: vec![],
- base: None,
- },
- _ => bug!("unexpected ty: {:?}", ty),
- }
- }
-
- // We encode uses of statics as a `*&STATIC` where the `&STATIC` part is
- // a constant reference (or constant raw pointer for `static mut`) in MIR
- Res::Def(DefKind::Static, id) => {
- let ty = cx.tcx.static_ptr_ty(id);
- let ptr = cx.tcx.alloc_map.lock().create_static_alloc(id);
- let temp_lifetime = cx.region_scope_tree.temporary_scope(expr.hir_id.local_id);
- ExprKind::Deref {
- arg: Expr {
- ty,
- temp_lifetime,
- span: expr.span,
- kind: ExprKind::StaticRef {
- literal: ty::Const::from_scalar(cx.tcx, Scalar::Ptr(ptr.into()), ty),
- def_id: id,
- },
- }
- .to_ref(),
- }
- }
-
- Res::Local(var_hir_id) => convert_var(cx, expr, var_hir_id),
-
- _ => span_bug!(expr.span, "res `{:?}` not yet implemented", res),
- }
-}
-
-fn convert_var(
- cx: &mut Cx<'_, 'tcx>,
- expr: &'tcx hir::Expr<'tcx>,
- var_hir_id: hir::HirId,
-) -> ExprKind<'tcx> {
- let upvar_index = cx
- .tables()
- .upvar_list
- .get(&cx.body_owner)
- .and_then(|upvars| upvars.get_full(&var_hir_id).map(|(i, _, _)| i));
-
- debug!(
- "convert_var({:?}): upvar_index={:?}, body_owner={:?}",
- var_hir_id, upvar_index, cx.body_owner
- );
-
- let temp_lifetime = cx.region_scope_tree.temporary_scope(expr.hir_id.local_id);
-
- match upvar_index {
- None => ExprKind::VarRef { id: var_hir_id },
-
- Some(upvar_index) => {
- let closure_def_id = cx.body_owner;
- let upvar_id = ty::UpvarId {
- var_path: ty::UpvarPath { hir_id: var_hir_id },
- closure_expr_id: LocalDefId::from_def_id(closure_def_id),
- };
- let var_ty = cx.tables().node_type(var_hir_id);
-
- // FIXME free regions in closures are not right
- let closure_ty = cx
- .tables()
- .node_type(cx.tcx.hir().local_def_id_to_hir_id(upvar_id.closure_expr_id));
-
- // FIXME we're just hard-coding the idea that the
- // signature will be &self or &mut self and hence will
- // have a bound region with number 0
- let region = ty::ReFree(ty::FreeRegion {
- scope: closure_def_id,
- bound_region: ty::BoundRegion::BrAnon(0),
- });
- let region = cx.tcx.mk_region(region);
-
- let self_expr = if let ty::Closure(_, closure_substs) = closure_ty.kind {
- match cx.infcx.closure_kind(closure_def_id, closure_substs).unwrap() {
- ty::ClosureKind::Fn => {
- let ref_closure_ty = cx.tcx.mk_ref(
- region,
- ty::TypeAndMut { ty: closure_ty, mutbl: hir::Mutability::Not },
- );
- Expr {
- ty: closure_ty,
- temp_lifetime,
- span: expr.span,
- kind: ExprKind::Deref {
- arg: Expr {
- ty: ref_closure_ty,
- temp_lifetime,
- span: expr.span,
- kind: ExprKind::SelfRef,
- }
- .to_ref(),
- },
- }
- }
- ty::ClosureKind::FnMut => {
- let ref_closure_ty = cx.tcx.mk_ref(
- region,
- ty::TypeAndMut { ty: closure_ty, mutbl: hir::Mutability::Mut },
- );
- Expr {
- ty: closure_ty,
- temp_lifetime,
- span: expr.span,
- kind: ExprKind::Deref {
- arg: Expr {
- ty: ref_closure_ty,
- temp_lifetime,
- span: expr.span,
- kind: ExprKind::SelfRef,
- }
- .to_ref(),
- },
- }
- }
- ty::ClosureKind::FnOnce => Expr {
- ty: closure_ty,
- temp_lifetime,
- span: expr.span,
- kind: ExprKind::SelfRef,
- },
- }
- } else {
- Expr { ty: closure_ty, temp_lifetime, span: expr.span, kind: ExprKind::SelfRef }
- };
-
- // at this point we have `self.n`, which loads up the upvar
- let field_kind =
- ExprKind::Field { lhs: self_expr.to_ref(), name: Field::new(upvar_index) };
-
- // ...but the upvar might be an `&T` or `&mut T` capture, at which
- // point we need an implicit deref
- match cx.tables().upvar_capture(upvar_id) {
- ty::UpvarCapture::ByValue => field_kind,
- ty::UpvarCapture::ByRef(borrow) => ExprKind::Deref {
- arg: Expr {
- temp_lifetime,
- ty: cx.tcx.mk_ref(
- borrow.region,
- ty::TypeAndMut { ty: var_ty, mutbl: borrow.kind.to_mutbl_lossy() },
- ),
- span: expr.span,
- kind: field_kind,
- }
- .to_ref(),
- },
- }
- }
- }
-}
-
-fn bin_op(op: hir::BinOpKind) -> BinOp {
- match op {
- hir::BinOpKind::Add => BinOp::Add,
- hir::BinOpKind::Sub => BinOp::Sub,
- hir::BinOpKind::Mul => BinOp::Mul,
- hir::BinOpKind::Div => BinOp::Div,
- hir::BinOpKind::Rem => BinOp::Rem,
- hir::BinOpKind::BitXor => BinOp::BitXor,
- hir::BinOpKind::BitAnd => BinOp::BitAnd,
- hir::BinOpKind::BitOr => BinOp::BitOr,
- hir::BinOpKind::Shl => BinOp::Shl,
- hir::BinOpKind::Shr => BinOp::Shr,
- hir::BinOpKind::Eq => BinOp::Eq,
- hir::BinOpKind::Lt => BinOp::Lt,
- hir::BinOpKind::Le => BinOp::Le,
- hir::BinOpKind::Ne => BinOp::Ne,
- hir::BinOpKind::Ge => BinOp::Ge,
- hir::BinOpKind::Gt => BinOp::Gt,
- _ => bug!("no equivalent for ast binop {:?}", op),
- }
-}
-
-fn overloaded_operator<'a, 'tcx>(
- cx: &mut Cx<'a, 'tcx>,
- expr: &'tcx hir::Expr<'tcx>,
- args: Vec<ExprRef<'tcx>>,
-) -> ExprKind<'tcx> {
- let fun = method_callee(cx, expr, expr.span, None);
- ExprKind::Call { ty: fun.ty, fun: fun.to_ref(), args, from_hir_call: false }
-}
-
-fn overloaded_place<'a, 'tcx>(
- cx: &mut Cx<'a, 'tcx>,
- expr: &'tcx hir::Expr<'tcx>,
- place_ty: Ty<'tcx>,
- overloaded_callee: Option<(DefId, SubstsRef<'tcx>)>,
- args: Vec<ExprRef<'tcx>>,
-) -> ExprKind<'tcx> {
- // For an overloaded *x or x[y] expression of type T, the method
- // call returns an &T and we must add the deref so that the types
- // line up (this is because `*x` and `x[y]` represent places):
-
- let recv_ty = match args[0] {
- ExprRef::Hair(e) => cx.tables().expr_ty_adjusted(e),
- ExprRef::Mirror(ref e) => e.ty,
- };
-
- // Reconstruct the output assuming it's a reference with the
- // same region and mutability as the receiver. This holds for
- // `Deref(Mut)::Deref(_mut)` and `Index(Mut)::index(_mut)`.
- let (region, mutbl) = match recv_ty.kind {
- ty::Ref(region, _, mutbl) => (region, mutbl),
- _ => span_bug!(expr.span, "overloaded_place: receiver is not a reference"),
- };
- let ref_ty = cx.tcx.mk_ref(region, ty::TypeAndMut { ty: place_ty, mutbl });
-
- // construct the complete expression `foo()` for the overloaded call,
- // which will yield the &T type
- let temp_lifetime = cx.region_scope_tree.temporary_scope(expr.hir_id.local_id);
- let fun = method_callee(cx, expr, expr.span, overloaded_callee);
- let ref_expr = Expr {
- temp_lifetime,
- ty: ref_ty,
- span: expr.span,
- kind: ExprKind::Call { ty: fun.ty, fun: fun.to_ref(), args, from_hir_call: false },
- };
-
- // construct and return a deref wrapper `*foo()`
- ExprKind::Deref { arg: ref_expr.to_ref() }
-}
-
-fn capture_upvar<'tcx>(
- cx: &mut Cx<'_, 'tcx>,
- closure_expr: &'tcx hir::Expr<'tcx>,
- var_hir_id: hir::HirId,
- upvar_ty: Ty<'tcx>,
-) -> ExprRef<'tcx> {
- let upvar_id = ty::UpvarId {
- var_path: ty::UpvarPath { hir_id: var_hir_id },
- closure_expr_id: cx.tcx.hir().local_def_id(closure_expr.hir_id).to_local(),
- };
- let upvar_capture = cx.tables().upvar_capture(upvar_id);
- let temp_lifetime = cx.region_scope_tree.temporary_scope(closure_expr.hir_id.local_id);
- let var_ty = cx.tables().node_type(var_hir_id);
- let captured_var = Expr {
- temp_lifetime,
- ty: var_ty,
- span: closure_expr.span,
- kind: convert_var(cx, closure_expr, var_hir_id),
- };
- match upvar_capture {
- ty::UpvarCapture::ByValue => captured_var.to_ref(),
- ty::UpvarCapture::ByRef(upvar_borrow) => {
- let borrow_kind = match upvar_borrow.kind {
- ty::BorrowKind::ImmBorrow => BorrowKind::Shared,
- ty::BorrowKind::UniqueImmBorrow => BorrowKind::Unique,
- ty::BorrowKind::MutBorrow => BorrowKind::Mut { allow_two_phase_borrow: false },
- };
- Expr {
- temp_lifetime,
- ty: upvar_ty,
- span: closure_expr.span,
- kind: ExprKind::Borrow { borrow_kind, arg: captured_var.to_ref() },
- }
- .to_ref()
- }
- }
-}
-
-/// Converts a list of named fields (i.e., for struct-like struct/enum ADTs) into FieldExprRef.
-fn field_refs<'a, 'tcx>(
- cx: &mut Cx<'a, 'tcx>,
- fields: &'tcx [hir::Field<'tcx>],
-) -> Vec<FieldExprRef<'tcx>> {
- fields
- .iter()
- .map(|field| FieldExprRef {
- name: Field::new(cx.tcx.field_index(field.hir_id, cx.tables)),
- expr: field.expr.to_ref(),
- })
- .collect()
-}
+++ /dev/null
-//! This module contains the fcuntaiontliy to convert from the wacky tcx data
-//! structures into the HAIR. The `builder` is generally ignorant of the tcx,
-//! etc., and instead goes through the `Cx` for most of its work.
-
-use crate::hair::util::UserAnnotatedTyHelpers;
-use crate::hair::*;
-
-use crate::hair::constant::{lit_to_const, LitToConstError};
-use rustc::infer::InferCtxt;
-use rustc::middle::region;
-use rustc::ty::layout::VariantIdx;
-use rustc::ty::subst::Subst;
-use rustc::ty::subst::{GenericArg, InternalSubsts};
-use rustc::ty::{self, Ty, TyCtxt};
-use rustc_hir as hir;
-use rustc_hir::def_id::DefId;
-use rustc_hir::Node;
-use rustc_index::vec::Idx;
-use rustc_span::symbol::{sym, Symbol};
-use syntax::ast;
-use syntax::attr;
-
-#[derive(Clone)]
-pub struct Cx<'a, 'tcx> {
- tcx: TyCtxt<'tcx>,
- infcx: &'a InferCtxt<'a, 'tcx>,
-
- pub root_lint_level: hir::HirId,
- pub param_env: ty::ParamEnv<'tcx>,
-
- /// Identity `InternalSubsts` for use with const-evaluation.
- pub identity_substs: &'tcx InternalSubsts<'tcx>,
-
- pub region_scope_tree: &'tcx region::ScopeTree,
- pub tables: &'a ty::TypeckTables<'tcx>,
-
- /// This is `Constness::Const` if we are compiling a `static`,
- /// `const`, or the body of a `const fn`.
- constness: hir::Constness,
-
- /// The `DefId` of the owner of this body.
- body_owner: DefId,
-
- /// What kind of body is being compiled.
- pub body_owner_kind: hir::BodyOwnerKind,
-
- /// Whether this constant/function needs overflow checks.
- check_overflow: bool,
-
- /// See field with the same name on `mir::Body`.
- control_flow_destroyed: Vec<(Span, String)>,
-}
-
-impl<'a, 'tcx> Cx<'a, 'tcx> {
- pub fn new(infcx: &'a InferCtxt<'a, 'tcx>, src_id: hir::HirId) -> Cx<'a, 'tcx> {
- let tcx = infcx.tcx;
- let src_def_id = tcx.hir().local_def_id(src_id);
- let tables = tcx.typeck_tables_of(src_def_id);
- let body_owner_kind = tcx.hir().body_owner_kind(src_id);
-
- let constness = match body_owner_kind {
- hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => hir::Constness::Const,
- hir::BodyOwnerKind::Closure | hir::BodyOwnerKind::Fn => hir::Constness::NotConst,
- };
-
- let attrs = tcx.hir().attrs(src_id);
-
- // Some functions always have overflow checks enabled,
- // however, they may not get codegen'd, depending on
- // the settings for the crate they are codegened in.
- let mut check_overflow = attr::contains_name(attrs, sym::rustc_inherit_overflow_checks);
-
- // Respect -C overflow-checks.
- check_overflow |= tcx.sess.overflow_checks();
-
- // Constants always need overflow checks.
- check_overflow |= constness == hir::Constness::Const;
-
- Cx {
- tcx,
- infcx,
- root_lint_level: src_id,
- param_env: tcx.param_env(src_def_id),
- identity_substs: InternalSubsts::identity_for_item(tcx, src_def_id),
- region_scope_tree: tcx.region_scope_tree(src_def_id),
- tables,
- constness,
- body_owner: src_def_id,
- body_owner_kind,
- check_overflow,
- control_flow_destroyed: Vec::new(),
- }
- }
-
- pub fn control_flow_destroyed(self) -> Vec<(Span, String)> {
- self.control_flow_destroyed
- }
-}
-
-impl<'a, 'tcx> Cx<'a, 'tcx> {
- /// Normalizes `ast` into the appropriate "mirror" type.
- pub fn mirror<M: Mirror<'tcx>>(&mut self, ast: M) -> M::Output {
- ast.make_mirror(self)
- }
-
- pub fn usize_ty(&mut self) -> Ty<'tcx> {
- self.tcx.types.usize
- }
-
- pub fn usize_literal(&mut self, value: u64) -> &'tcx ty::Const<'tcx> {
- ty::Const::from_usize(self.tcx, value)
- }
-
- pub fn bool_ty(&mut self) -> Ty<'tcx> {
- self.tcx.types.bool
- }
-
- pub fn unit_ty(&mut self) -> Ty<'tcx> {
- self.tcx.mk_unit()
- }
-
- pub fn true_literal(&mut self) -> &'tcx ty::Const<'tcx> {
- ty::Const::from_bool(self.tcx, true)
- }
-
- pub fn false_literal(&mut self) -> &'tcx ty::Const<'tcx> {
- ty::Const::from_bool(self.tcx, false)
- }
-
- pub fn const_eval_literal(
- &mut self,
- lit: &'tcx ast::LitKind,
- ty: Ty<'tcx>,
- sp: Span,
- neg: bool,
- ) -> &'tcx ty::Const<'tcx> {
- trace!("const_eval_literal: {:#?}, {:?}, {:?}, {:?}", lit, ty, sp, neg);
-
- match lit_to_const(lit, self.tcx, ty, neg) {
- Ok(c) => c,
- Err(LitToConstError::UnparseableFloat) => {
- // FIXME(#31407) this is only necessary because float parsing is buggy
- self.tcx.sess.span_err(sp, "could not evaluate float literal (see issue #31407)");
- // create a dummy value and continue compiling
- Const::from_bits(self.tcx, 0, self.param_env.and(ty))
- }
- Err(LitToConstError::Reported) => {
- // create a dummy value and continue compiling
- Const::from_bits(self.tcx, 0, self.param_env.and(ty))
- }
- }
- }
-
- pub fn pattern_from_hir(&mut self, p: &hir::Pat<'_>) -> Pat<'tcx> {
- let p = match self.tcx.hir().get(p.hir_id) {
- Node::Pat(p) | Node::Binding(p) => p,
- node => bug!("pattern became {:?}", node),
- };
- Pat::from_hir(self.tcx, self.param_env.and(self.identity_substs), self.tables(), p)
- }
-
- pub fn trait_method(
- &mut self,
- trait_def_id: DefId,
- method_name: Symbol,
- self_ty: Ty<'tcx>,
- params: &[GenericArg<'tcx>],
- ) -> &'tcx ty::Const<'tcx> {
- let substs = self.tcx.mk_substs_trait(self_ty, params);
- for item in self.tcx.associated_items(trait_def_id) {
- if item.kind == ty::AssocKind::Method && item.ident.name == method_name {
- let method_ty = self.tcx.type_of(item.def_id);
- let method_ty = method_ty.subst(self.tcx, substs);
- return ty::Const::zero_sized(self.tcx, method_ty);
- }
- }
-
- bug!("found no method `{}` in `{:?}`", method_name, trait_def_id);
- }
-
- pub fn all_fields(&mut self, adt_def: &ty::AdtDef, variant_index: VariantIdx) -> Vec<Field> {
- (0..adt_def.variants[variant_index].fields.len()).map(Field::new).collect()
- }
-
- pub fn needs_drop(&mut self, ty: Ty<'tcx>) -> bool {
- ty.needs_drop(self.tcx, self.param_env)
- }
-
- pub fn tcx(&self) -> TyCtxt<'tcx> {
- self.tcx
- }
-
- pub fn tables(&self) -> &'a ty::TypeckTables<'tcx> {
- self.tables
- }
-
- pub fn check_overflow(&self) -> bool {
- self.check_overflow
- }
-
- pub fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>, span: Span) -> bool {
- self.infcx.type_is_copy_modulo_regions(self.param_env, ty, span)
- }
-}
-
-impl UserAnnotatedTyHelpers<'tcx> for Cx<'_, 'tcx> {
- fn tcx(&self) -> TyCtxt<'tcx> {
- self.tcx()
- }
-
- fn tables(&self) -> &ty::TypeckTables<'tcx> {
- self.tables()
- }
-}
-
-mod block;
-mod expr;
-mod to_ref;
+++ /dev/null
-use crate::hair::*;
-
-use rustc_hir as hir;
-
-pub trait ToRef {
- type Output;
- fn to_ref(self) -> Self::Output;
-}
-
-impl<'tcx> ToRef for &'tcx hir::Expr<'tcx> {
- type Output = ExprRef<'tcx>;
-
- fn to_ref(self) -> ExprRef<'tcx> {
- ExprRef::Hair(self)
- }
-}
-
-impl<'tcx> ToRef for &'tcx &'tcx hir::Expr<'tcx> {
- type Output = ExprRef<'tcx>;
-
- fn to_ref(self) -> ExprRef<'tcx> {
- ExprRef::Hair(&**self)
- }
-}
-
-impl<'tcx> ToRef for Expr<'tcx> {
- type Output = ExprRef<'tcx>;
-
- fn to_ref(self) -> ExprRef<'tcx> {
- ExprRef::Mirror(Box::new(self))
- }
-}
-
-impl<'tcx, T, U> ToRef for &'tcx Option<T>
-where
- &'tcx T: ToRef<Output = U>,
-{
- type Output = Option<U>;
-
- fn to_ref(self) -> Option<U> {
- self.as_ref().map(|expr| expr.to_ref())
- }
-}
-
-impl<'tcx, T, U> ToRef for &'tcx Vec<T>
-where
- &'tcx T: ToRef<Output = U>,
-{
- type Output = Vec<U>;
-
- fn to_ref(self) -> Vec<U> {
- self.iter().map(|expr| expr.to_ref()).collect()
- }
-}
-
-impl<'tcx, T, U> ToRef for &'tcx [T]
-where
- &'tcx T: ToRef<Output = U>,
-{
- type Output = Vec<U>;
-
- fn to_ref(self) -> Vec<U> {
- self.iter().map(|expr| expr.to_ref()).collect()
- }
-}
+++ /dev/null
-//! The MIR is built from some high-level abstract IR
-//! (HAIR). This section defines the HAIR along with a trait for
-//! accessing it. The intention is to allow MIR construction to be
-//! unit-tested and separated from the Rust source and compiler data
-//! structures.
-
-use self::cx::Cx;
-use rustc::infer::canonical::Canonical;
-use rustc::middle::region;
-use rustc::mir::{BinOp, BorrowKind, Field, UnOp};
-use rustc::ty::adjustment::PointerCast;
-use rustc::ty::layout::VariantIdx;
-use rustc::ty::subst::SubstsRef;
-use rustc::ty::{AdtDef, Const, Ty, UpvarSubsts, UserType};
-use rustc_hir as hir;
-use rustc_hir::def_id::DefId;
-use rustc_span::Span;
-
-mod constant;
-pub mod cx;
-
-pub mod pattern;
-pub(crate) use self::pattern::PatTyProj;
-pub use self::pattern::{BindingMode, FieldPat, Pat, PatKind, PatRange};
-
-mod util;
-
-#[derive(Copy, Clone, Debug)]
-pub enum LintLevel {
- Inherited,
- Explicit(hir::HirId),
-}
-
-#[derive(Clone, Debug)]
-pub struct Block<'tcx> {
- pub targeted_by_break: bool,
- pub region_scope: region::Scope,
- pub opt_destruction_scope: Option<region::Scope>,
- pub span: Span,
- pub stmts: Vec<StmtRef<'tcx>>,
- pub expr: Option<ExprRef<'tcx>>,
- pub safety_mode: BlockSafety,
-}
-
-#[derive(Copy, Clone, Debug)]
-pub enum BlockSafety {
- Safe,
- ExplicitUnsafe(hir::HirId),
- PushUnsafe,
- PopUnsafe,
-}
-
-#[derive(Clone, Debug)]
-pub enum StmtRef<'tcx> {
- Mirror(Box<Stmt<'tcx>>),
-}
-
-#[derive(Clone, Debug)]
-pub struct Stmt<'tcx> {
- pub kind: StmtKind<'tcx>,
- pub opt_destruction_scope: Option<region::Scope>,
-}
-
-#[derive(Clone, Debug)]
-pub enum StmtKind<'tcx> {
- Expr {
- /// scope for this statement; may be used as lifetime of temporaries
- scope: region::Scope,
-
- /// expression being evaluated in this statement
- expr: ExprRef<'tcx>,
- },
-
- Let {
- /// scope for variables bound in this let; covers this and
- /// remaining statements in block
- remainder_scope: region::Scope,
-
- /// scope for the initialization itself; might be used as
- /// lifetime of temporaries
- init_scope: region::Scope,
-
- /// `let <PAT> = ...`
- ///
- /// if a type is included, it is added as an ascription pattern
- pattern: Pat<'tcx>,
-
- /// let pat: ty = <INIT> ...
- initializer: Option<ExprRef<'tcx>>,
-
- /// the lint level for this let-statement
- lint_level: LintLevel,
- },
-}
-
-// `Expr` is used a lot. Make sure it doesn't unintentionally get bigger.
-#[cfg(target_arch = "x86_64")]
-rustc_data_structures::static_assert_size!(Expr<'_>, 168);
-
-/// The Hair trait implementor lowers their expressions (`&'tcx H::Expr`)
-/// into instances of this `Expr` enum. This lowering can be done
-/// basically as lazily or as eagerly as desired: every recursive
-/// reference to an expression in this enum is an `ExprRef<'tcx>`, which
-/// may in turn be another instance of this enum (boxed), or else an
-/// unlowered `&'tcx H::Expr`. Note that instances of `Expr` are very
-/// short-lived. They are created by `Hair::to_expr`, analyzed and
-/// converted into MIR, and then discarded.
-///
-/// If you compare `Expr` to the full compiler AST, you will see it is
-/// a good bit simpler. In fact, a number of the more straight-forward
-/// MIR simplifications are already done in the impl of `Hair`. For
-/// example, method calls and overloaded operators are absent: they are
-/// expected to be converted into `Expr::Call` instances.
-#[derive(Clone, Debug)]
-pub struct Expr<'tcx> {
- /// type of this expression
- pub ty: Ty<'tcx>,
-
- /// lifetime of this expression if it should be spilled into a
- /// temporary; should be None only if in a constant context
- pub temp_lifetime: Option<region::Scope>,
-
- /// span of the expression in the source
- pub span: Span,
-
- /// kind of expression
- pub kind: ExprKind<'tcx>,
-}
-
-#[derive(Clone, Debug)]
-pub enum ExprKind<'tcx> {
- Scope {
- region_scope: region::Scope,
- lint_level: LintLevel,
- value: ExprRef<'tcx>,
- },
- Box {
- value: ExprRef<'tcx>,
- },
- Call {
- ty: Ty<'tcx>,
- fun: ExprRef<'tcx>,
- args: Vec<ExprRef<'tcx>>,
- // Whether this is from a call in HIR, rather than from an overloaded
- // operator. True for overloaded function call.
- from_hir_call: bool,
- },
- Deref {
- arg: ExprRef<'tcx>,
- }, // NOT overloaded!
- Binary {
- op: BinOp,
- lhs: ExprRef<'tcx>,
- rhs: ExprRef<'tcx>,
- }, // NOT overloaded!
- LogicalOp {
- op: LogicalOp,
- lhs: ExprRef<'tcx>,
- rhs: ExprRef<'tcx>,
- }, // NOT overloaded!
- // LogicalOp is distinct from BinaryOp because of lazy evaluation of the operands.
- Unary {
- op: UnOp,
- arg: ExprRef<'tcx>,
- }, // NOT overloaded!
- Cast {
- source: ExprRef<'tcx>,
- },
- Use {
- source: ExprRef<'tcx>,
- }, // Use a lexpr to get a vexpr.
- NeverToAny {
- source: ExprRef<'tcx>,
- },
- Pointer {
- cast: PointerCast,
- source: ExprRef<'tcx>,
- },
- Loop {
- body: ExprRef<'tcx>,
- },
- Match {
- scrutinee: ExprRef<'tcx>,
- arms: Vec<Arm<'tcx>>,
- },
- Block {
- body: &'tcx hir::Block<'tcx>,
- },
- Assign {
- lhs: ExprRef<'tcx>,
- rhs: ExprRef<'tcx>,
- },
- AssignOp {
- op: BinOp,
- lhs: ExprRef<'tcx>,
- rhs: ExprRef<'tcx>,
- },
- Field {
- lhs: ExprRef<'tcx>,
- name: Field,
- },
- Index {
- lhs: ExprRef<'tcx>,
- index: ExprRef<'tcx>,
- },
- VarRef {
- id: hir::HirId,
- },
- /// first argument, used for self in a closure
- SelfRef,
- Borrow {
- borrow_kind: BorrowKind,
- arg: ExprRef<'tcx>,
- },
- /// A `&raw [const|mut] $place_expr` raw borrow resulting in type `*[const|mut] T`.
- AddressOf {
- mutability: hir::Mutability,
- arg: ExprRef<'tcx>,
- },
- Break {
- label: region::Scope,
- value: Option<ExprRef<'tcx>>,
- },
- Continue {
- label: region::Scope,
- },
- Return {
- value: Option<ExprRef<'tcx>>,
- },
- Repeat {
- value: ExprRef<'tcx>,
- count: u64,
- },
- Array {
- fields: Vec<ExprRef<'tcx>>,
- },
- Tuple {
- fields: Vec<ExprRef<'tcx>>,
- },
- Adt {
- adt_def: &'tcx AdtDef,
- variant_index: VariantIdx,
- substs: SubstsRef<'tcx>,
-
- /// Optional user-given substs: for something like `let x =
- /// Bar::<T> { ... }`.
- user_ty: Option<Canonical<'tcx, UserType<'tcx>>>,
-
- fields: Vec<FieldExprRef<'tcx>>,
- base: Option<FruInfo<'tcx>>,
- },
- PlaceTypeAscription {
- source: ExprRef<'tcx>,
- /// Type that the user gave to this expression
- user_ty: Option<Canonical<'tcx, UserType<'tcx>>>,
- },
- ValueTypeAscription {
- source: ExprRef<'tcx>,
- /// Type that the user gave to this expression
- user_ty: Option<Canonical<'tcx, UserType<'tcx>>>,
- },
- Closure {
- closure_id: DefId,
- substs: UpvarSubsts<'tcx>,
- upvars: Vec<ExprRef<'tcx>>,
- movability: Option<hir::Movability>,
- },
- Literal {
- literal: &'tcx Const<'tcx>,
- user_ty: Option<Canonical<'tcx, UserType<'tcx>>>,
- },
- /// A literal containing the address of a `static`.
- ///
- /// This is only distinguished from `Literal` so that we can register some
- /// info for diagnostics.
- StaticRef {
- literal: &'tcx Const<'tcx>,
- def_id: DefId,
- },
- InlineAsm {
- asm: &'tcx hir::InlineAsmInner,
- outputs: Vec<ExprRef<'tcx>>,
- inputs: Vec<ExprRef<'tcx>>,
- },
- Yield {
- value: ExprRef<'tcx>,
- },
-}
-
-#[derive(Clone, Debug)]
-pub enum ExprRef<'tcx> {
- Hair(&'tcx hir::Expr<'tcx>),
- Mirror(Box<Expr<'tcx>>),
-}
-
-#[derive(Clone, Debug)]
-pub struct FieldExprRef<'tcx> {
- pub name: Field,
- pub expr: ExprRef<'tcx>,
-}
-
-#[derive(Clone, Debug)]
-pub struct FruInfo<'tcx> {
- pub base: ExprRef<'tcx>,
- pub field_types: Vec<Ty<'tcx>>,
-}
-
-#[derive(Clone, Debug)]
-pub struct Arm<'tcx> {
- pub pattern: Pat<'tcx>,
- pub guard: Option<Guard<'tcx>>,
- pub body: ExprRef<'tcx>,
- pub lint_level: LintLevel,
- pub scope: region::Scope,
- pub span: Span,
-}
-
-impl Arm<'tcx> {
- // HACK(or_patterns; Centril | dlrobertson): Remove this and
- // correctly handle each case in which this method is used.
- pub fn top_pats_hack(&self) -> &[Pat<'tcx>] {
- match &*self.pattern.kind {
- PatKind::Or { pats } => pats,
- _ => std::slice::from_ref(&self.pattern),
- }
- }
-}
-
-#[derive(Clone, Debug)]
-pub enum Guard<'tcx> {
- If(ExprRef<'tcx>),
-}
-
-#[derive(Copy, Clone, Debug)]
-pub enum LogicalOp {
- And,
- Or,
-}
-
-impl<'tcx> ExprRef<'tcx> {
- pub fn span(&self) -> Span {
- match self {
- ExprRef::Hair(expr) => expr.span,
- ExprRef::Mirror(expr) => expr.span,
- }
- }
-}
-
-///////////////////////////////////////////////////////////////////////////
-// The Mirror trait
-
-/// "Mirroring" is the process of converting from a HIR type into one
-/// of the HAIR types defined in this file. This is basically a "on
-/// the fly" desugaring step that hides a lot of the messiness in the
-/// tcx. For example, the mirror of a `&'tcx hir::Expr` is an
-/// `Expr<'tcx>`.
-///
-/// Mirroring is gradual: when you mirror an outer expression like `e1
-/// + e2`, the references to the inner expressions `e1` and `e2` are
-/// `ExprRef<'tcx>` instances, and they may or may not be eagerly
-/// mirrored. This allows a single AST node from the compiler to
-/// expand into one or more Hair nodes, which lets the Hair nodes be
-/// simpler.
-pub trait Mirror<'tcx> {
- type Output;
-
- fn make_mirror(self, cx: &mut Cx<'_, 'tcx>) -> Self::Output;
-}
-
-impl<'tcx> Mirror<'tcx> for Expr<'tcx> {
- type Output = Expr<'tcx>;
-
- fn make_mirror(self, _: &mut Cx<'_, 'tcx>) -> Expr<'tcx> {
- self
- }
-}
-
-impl<'tcx> Mirror<'tcx> for ExprRef<'tcx> {
- type Output = Expr<'tcx>;
-
- fn make_mirror(self, hir: &mut Cx<'a, 'tcx>) -> Expr<'tcx> {
- match self {
- ExprRef::Hair(h) => h.make_mirror(hir),
- ExprRef::Mirror(m) => *m,
- }
- }
-}
-
-impl<'tcx> Mirror<'tcx> for Stmt<'tcx> {
- type Output = Stmt<'tcx>;
-
- fn make_mirror(self, _: &mut Cx<'_, 'tcx>) -> Stmt<'tcx> {
- self
- }
-}
-
-impl<'tcx> Mirror<'tcx> for StmtRef<'tcx> {
- type Output = Stmt<'tcx>;
-
- fn make_mirror(self, _: &mut Cx<'_, 'tcx>) -> Stmt<'tcx> {
- match self {
- StmtRef::Mirror(m) => *m,
- }
- }
-}
-
-impl<'tcx> Mirror<'tcx> for Block<'tcx> {
- type Output = Block<'tcx>;
-
- fn make_mirror(self, _: &mut Cx<'_, 'tcx>) -> Block<'tcx> {
- self
- }
-}
+++ /dev/null
-/// Note: most tests relevant to this file can be found (at the time of writing)
-/// in src/tests/ui/pattern/usefulness.
-///
-/// This file includes the logic for exhaustiveness and usefulness checking for
-/// pattern-matching. Specifically, given a list of patterns for a type, we can
-/// tell whether:
-/// (a) the patterns cover every possible constructor for the type [exhaustiveness]
-/// (b) each pattern is necessary [usefulness]
-///
-/// The algorithm implemented here is a modified version of the one described in:
-/// http://moscova.inria.fr/~maranget/papers/warn/index.html
-/// However, to save future implementors from reading the original paper, we
-/// summarise the algorithm here to hopefully save time and be a little clearer
-/// (without being so rigorous).
-///
-/// The core of the algorithm revolves about a "usefulness" check. In particular, we
-/// are trying to compute a predicate `U(P, p)` where `P` is a list of patterns (we refer to this as
-/// a matrix). `U(P, p)` represents whether, given an existing list of patterns
-/// `P_1 ..= P_m`, adding a new pattern `p` will be "useful" (that is, cover previously-
-/// uncovered values of the type).
-///
-/// If we have this predicate, then we can easily compute both exhaustiveness of an
-/// entire set of patterns and the individual usefulness of each one.
-/// (a) the set of patterns is exhaustive iff `U(P, _)` is false (i.e., adding a wildcard
-/// match doesn't increase the number of values we're matching)
-/// (b) a pattern `P_i` is not useful if `U(P[0..=(i-1), P_i)` is false (i.e., adding a
-/// pattern to those that have come before it doesn't increase the number of values
-/// we're matching).
-///
-/// During the course of the algorithm, the rows of the matrix won't just be individual patterns,
-/// but rather partially-deconstructed patterns in the form of a list of patterns. The paper
-/// calls those pattern-vectors, and we will call them pattern-stacks. The same holds for the
-/// new pattern `p`.
-///
-/// For example, say we have the following:
-/// ```
-/// // x: (Option<bool>, Result<()>)
-/// match x {
-/// (Some(true), _) => {}
-/// (None, Err(())) => {}
-/// (None, Err(_)) => {}
-/// }
-/// ```
-/// Here, the matrix `P` starts as:
-/// [
-/// [(Some(true), _)],
-/// [(None, Err(()))],
-/// [(None, Err(_))],
-/// ]
-/// We can tell it's not exhaustive, because `U(P, _)` is true (we're not covering
-/// `[(Some(false), _)]`, for instance). In addition, row 3 is not useful, because
-/// all the values it covers are already covered by row 2.
-///
-/// A list of patterns can be thought of as a stack, because we are mainly interested in the top of
-/// the stack at any given point, and we can pop or apply constructors to get new pattern-stacks.
-/// To match the paper, the top of the stack is at the beginning / on the left.
-///
-/// There are two important operations on pattern-stacks necessary to understand the algorithm:
-/// 1. We can pop a given constructor off the top of a stack. This operation is called
-/// `specialize`, and is denoted `S(c, p)` where `c` is a constructor (like `Some` or
-/// `None`) and `p` a pattern-stack.
-/// If the pattern on top of the stack can cover `c`, this removes the constructor and
-/// pushes its arguments onto the stack. It also expands OR-patterns into distinct patterns.
-/// Otherwise the pattern-stack is discarded.
-/// This essentially filters those pattern-stacks whose top covers the constructor `c` and
-/// discards the others.
-///
-/// For example, the first pattern above initially gives a stack `[(Some(true), _)]`. If we
-/// pop the tuple constructor, we are left with `[Some(true), _]`, and if we then pop the
-/// `Some` constructor we get `[true, _]`. If we had popped `None` instead, we would get
-/// nothing back.
-///
-/// This returns zero or more new pattern-stacks, as follows. We look at the pattern `p_1`
-/// on top of the stack, and we have four cases:
-/// 1.1. `p_1 = c(r_1, .., r_a)`, i.e. the top of the stack has constructor `c`. We
-/// push onto the stack the arguments of this constructor, and return the result:
-/// r_1, .., r_a, p_2, .., p_n
-/// 1.2. `p_1 = c'(r_1, .., r_a')` where `c ≠ c'`. We discard the current stack and
-/// return nothing.
-/// 1.3. `p_1 = _`. We push onto the stack as many wildcards as the constructor `c` has
-/// arguments (its arity), and return the resulting stack:
-/// _, .., _, p_2, .., p_n
-/// 1.4. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting
-/// stack:
-/// S(c, (r_1, p_2, .., p_n))
-/// S(c, (r_2, p_2, .., p_n))
-///
-/// 2. We can pop a wildcard off the top of the stack. This is called `D(p)`, where `p` is
-/// a pattern-stack.
-/// This is used when we know there are missing constructor cases, but there might be
-/// existing wildcard patterns, so to check the usefulness of the matrix, we have to check
-/// all its *other* components.
-///
-/// It is computed as follows. We look at the pattern `p_1` on top of the stack,
-/// and we have three cases:
-/// 1.1. `p_1 = c(r_1, .., r_a)`. We discard the current stack and return nothing.
-/// 1.2. `p_1 = _`. We return the rest of the stack:
-/// p_2, .., p_n
-/// 1.3. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting
-/// stack.
-/// D((r_1, p_2, .., p_n))
-/// D((r_2, p_2, .., p_n))
-///
-/// Note that the OR-patterns are not always used directly in Rust, but are used to derive the
-/// exhaustive integer matching rules, so they're written here for posterity.
-///
-/// Both those operations extend straightforwardly to a list or pattern-stacks, i.e. a matrix, by
-/// working row-by-row. Popping a constructor ends up keeping only the matrix rows that start with
-/// the given constructor, and popping a wildcard keeps those rows that start with a wildcard.
-///
-///
-/// The algorithm for computing `U`
-/// -------------------------------
-/// The algorithm is inductive (on the number of columns: i.e., components of tuple patterns).
-/// That means we're going to check the components from left-to-right, so the algorithm
-/// operates principally on the first component of the matrix and new pattern-stack `p`.
-/// This algorithm is realised in the `is_useful` function.
-///
-/// Base case. (`n = 0`, i.e., an empty tuple pattern)
-/// - If `P` already contains an empty pattern (i.e., if the number of patterns `m > 0`),
-/// then `U(P, p)` is false.
-/// - Otherwise, `P` must be empty, so `U(P, p)` is true.
-///
-/// Inductive step. (`n > 0`, i.e., whether there's at least one column
-/// [which may then be expanded into further columns later])
-/// We're going to match on the top of the new pattern-stack, `p_1`.
-/// - If `p_1 == c(r_1, .., r_a)`, i.e. we have a constructor pattern.
-/// Then, the usefulness of `p_1` can be reduced to whether it is useful when
-/// we ignore all the patterns in the first column of `P` that involve other constructors.
-/// This is where `S(c, P)` comes in:
-/// `U(P, p) := U(S(c, P), S(c, p))`
-/// This special case is handled in `is_useful_specialized`.
-///
-/// For example, if `P` is:
-/// [
-/// [Some(true), _],
-/// [None, 0],
-/// ]
-/// and `p` is [Some(false), 0], then we don't care about row 2 since we know `p` only
-/// matches values that row 2 doesn't. For row 1 however, we need to dig into the
-/// arguments of `Some` to know whether some new value is covered. So we compute
-/// `U([[true, _]], [false, 0])`.
-///
-/// - If `p_1 == _`, then we look at the list of constructors that appear in the first
-/// component of the rows of `P`:
-/// + If there are some constructors that aren't present, then we might think that the
-/// wildcard `_` is useful, since it covers those constructors that weren't covered
-/// before.
-/// That's almost correct, but only works if there were no wildcards in those first
-/// components. So we need to check that `p` is useful with respect to the rows that
-/// start with a wildcard, if there are any. This is where `D` comes in:
-/// `U(P, p) := U(D(P), D(p))`
-///
-/// For example, if `P` is:
-/// [
-/// [_, true, _],
-/// [None, false, 1],
-/// ]
-/// and `p` is [_, false, _], the `Some` constructor doesn't appear in `P`. So if we
-/// only had row 2, we'd know that `p` is useful. However row 1 starts with a
-/// wildcard, so we need to check whether `U([[true, _]], [false, 1])`.
-///
-/// + Otherwise, all possible constructors (for the relevant type) are present. In this
-/// case we must check whether the wildcard pattern covers any unmatched value. For
-/// that, we can think of the `_` pattern as a big OR-pattern that covers all
-/// possible constructors. For `Option`, that would mean `_ = None | Some(_)` for
-/// example. The wildcard pattern is useful in this case if it is useful when
-/// specialized to one of the possible constructors. So we compute:
-/// `U(P, p) := ∃(k ϵ constructors) U(S(k, P), S(k, p))`
-///
-/// For example, if `P` is:
-/// [
-/// [Some(true), _],
-/// [None, false],
-/// ]
-/// and `p` is [_, false], both `None` and `Some` constructors appear in the first
-/// components of `P`. We will therefore try popping both constructors in turn: we
-/// compute U([[true, _]], [_, false]) for the `Some` constructor, and U([[false]],
-/// [false]) for the `None` constructor. The first case returns true, so we know that
-/// `p` is useful for `P`. Indeed, it matches `[Some(false), _]` that wasn't matched
-/// before.
-///
-/// - If `p_1 == r_1 | r_2`, then the usefulness depends on each `r_i` separately:
-/// `U(P, p) := U(P, (r_1, p_2, .., p_n))
-/// || U(P, (r_2, p_2, .., p_n))`
-///
-/// Modifications to the algorithm
-/// ------------------------------
-/// The algorithm in the paper doesn't cover some of the special cases that arise in Rust, for
-/// example uninhabited types and variable-length slice patterns. These are drawn attention to
-/// throughout the code below. I'll make a quick note here about how exhaustive integer matching is
-/// accounted for, though.
-///
-/// Exhaustive integer matching
-/// ---------------------------
-/// An integer type can be thought of as a (huge) sum type: 1 | 2 | 3 | ...
-/// So to support exhaustive integer matching, we can make use of the logic in the paper for
-/// OR-patterns. However, we obviously can't just treat ranges x..=y as individual sums, because
-/// they are likely gigantic. So we instead treat ranges as constructors of the integers. This means
-/// that we have a constructor *of* constructors (the integers themselves). We then need to work
-/// through all the inductive step rules above, deriving how the ranges would be treated as
-/// OR-patterns, and making sure that they're treated in the same way even when they're ranges.
-/// There are really only four special cases here:
-/// - When we match on a constructor that's actually a range, we have to treat it as if we would
-/// an OR-pattern.
-/// + It turns out that we can simply extend the case for single-value patterns in
-/// `specialize` to either be *equal* to a value constructor, or *contained within* a range
-/// constructor.
-/// + When the pattern itself is a range, you just want to tell whether any of the values in
-/// the pattern range coincide with values in the constructor range, which is precisely
-/// intersection.
-/// Since when encountering a range pattern for a value constructor, we also use inclusion, it
-/// means that whenever the constructor is a value/range and the pattern is also a value/range,
-/// we can simply use intersection to test usefulness.
-/// - When we're testing for usefulness of a pattern and the pattern's first component is a
-/// wildcard.
-/// + If all the constructors appear in the matrix, we have a slight complication. By default,
-/// the behaviour (i.e., a disjunction over specialised matrices for each constructor) is
-/// invalid, because we want a disjunction over every *integer* in each range, not just a
-/// disjunction over every range. This is a bit more tricky to deal with: essentially we need
-/// to form equivalence classes of subranges of the constructor range for which the behaviour
-/// of the matrix `P` and new pattern `p` are the same. This is described in more
-/// detail in `split_grouped_constructors`.
-/// + If some constructors are missing from the matrix, it turns out we don't need to do
-/// anything special (because we know none of the integers are actually wildcards: i.e., we
-/// can't span wildcards using ranges).
-use self::Constructor::*;
-use self::SliceKind::*;
-use self::Usefulness::*;
-use self::WitnessPreference::*;
-
-use rustc_data_structures::fx::FxHashMap;
-use rustc_index::vec::Idx;
-
-use super::{compare_const_vals, PatternFoldable, PatternFolder};
-use super::{FieldPat, Pat, PatKind, PatRange};
-
-use rustc::ty::layout::{Integer, IntegerExt, Size, VariantIdx};
-use rustc::ty::{self, Const, Ty, TyCtxt, TypeFoldable, VariantDef};
-use rustc_hir::def_id::DefId;
-use rustc_hir::{HirId, RangeEnd};
-
-use rustc::lint;
-use rustc::mir::interpret::{truncate, AllocId, ConstValue, Pointer, Scalar};
-use rustc::mir::Field;
-use rustc::util::captures::Captures;
-use rustc::util::common::ErrorReported;
-
-use rustc_span::{Span, DUMMY_SP};
-use syntax::attr::{SignedInt, UnsignedInt};
-
-use arena::TypedArena;
-
-use smallvec::{smallvec, SmallVec};
-use std::borrow::Cow;
-use std::cmp::{self, max, min, Ordering};
-use std::convert::TryInto;
-use std::fmt;
-use std::iter::{FromIterator, IntoIterator};
-use std::ops::RangeInclusive;
-use std::u128;
-
-pub fn expand_pattern<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, pat: Pat<'tcx>) -> Pat<'tcx> {
- LiteralExpander { tcx: cx.tcx, param_env: cx.param_env }.fold_pattern(&pat)
-}
-
-struct LiteralExpander<'tcx> {
- tcx: TyCtxt<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
-}
-
-impl LiteralExpander<'tcx> {
- /// Derefs `val` and potentially unsizes the value if `crty` is an array and `rty` a slice.
- ///
- /// `crty` and `rty` can differ because you can use array constants in the presence of slice
- /// patterns. So the pattern may end up being a slice, but the constant is an array. We convert
- /// the array to a slice in that case.
- fn fold_const_value_deref(
- &mut self,
- val: ConstValue<'tcx>,
- // the pattern's pointee type
- rty: Ty<'tcx>,
- // the constant's pointee type
- crty: Ty<'tcx>,
- ) -> ConstValue<'tcx> {
- debug!("fold_const_value_deref {:?} {:?} {:?}", val, rty, crty);
- match (val, &crty.kind, &rty.kind) {
- // the easy case, deref a reference
- (ConstValue::Scalar(p), x, y) if x == y => {
- match p {
- Scalar::Ptr(p) => {
- let alloc = self.tcx.alloc_map.lock().unwrap_memory(p.alloc_id);
- ConstValue::ByRef { alloc, offset: p.offset }
- }
- Scalar::Raw { .. } => {
- let layout = self.tcx.layout_of(self.param_env.and(rty)).unwrap();
- if layout.is_zst() {
- // Deref of a reference to a ZST is a nop.
- ConstValue::Scalar(Scalar::zst())
- } else {
- // FIXME(oli-obk): this is reachable for `const FOO: &&&u32 = &&&42;`
- bug!("cannot deref {:#?}, {} -> {}", val, crty, rty);
- }
- }
- }
- }
- // unsize array to slice if pattern is array but match value or other patterns are slice
- (ConstValue::Scalar(Scalar::Ptr(p)), ty::Array(t, n), ty::Slice(u)) => {
- assert_eq!(t, u);
- ConstValue::Slice {
- data: self.tcx.alloc_map.lock().unwrap_memory(p.alloc_id),
- start: p.offset.bytes().try_into().unwrap(),
- end: n.eval_usize(self.tcx, ty::ParamEnv::empty()).try_into().unwrap(),
- }
- }
- // fat pointers stay the same
- (ConstValue::Slice { .. }, _, _)
- | (_, ty::Slice(_), ty::Slice(_))
- | (_, ty::Str, ty::Str) => val,
- // FIXME(oli-obk): this is reachable for `const FOO: &&&u32 = &&&42;` being used
- _ => bug!("cannot deref {:#?}, {} -> {}", val, crty, rty),
- }
- }
-}
-
-impl PatternFolder<'tcx> for LiteralExpander<'tcx> {
- fn fold_pattern(&mut self, pat: &Pat<'tcx>) -> Pat<'tcx> {
- debug!("fold_pattern {:?} {:?} {:?}", pat, pat.ty.kind, pat.kind);
- match (&pat.ty.kind, &*pat.kind) {
- (
- &ty::Ref(_, rty, _),
- &PatKind::Constant {
- value:
- Const {
- val: ty::ConstKind::Value(val),
- ty: ty::TyS { kind: ty::Ref(_, crty, _), .. },
- },
- },
- ) => Pat {
- ty: pat.ty,
- span: pat.span,
- kind: box PatKind::Deref {
- subpattern: Pat {
- ty: rty,
- span: pat.span,
- kind: box PatKind::Constant {
- value: self.tcx.mk_const(Const {
- val: ty::ConstKind::Value(
- self.fold_const_value_deref(*val, rty, crty),
- ),
- ty: rty,
- }),
- },
- },
- },
- },
-
- (
- &ty::Ref(_, rty, _),
- &PatKind::Constant {
- value: Const { val, ty: ty::TyS { kind: ty::Ref(_, crty, _), .. } },
- },
- ) => bug!("cannot deref {:#?}, {} -> {}", val, crty, rty),
-
- (_, &PatKind::Binding { subpattern: Some(ref s), .. }) => s.fold_with(self),
- (_, &PatKind::AscribeUserType { subpattern: ref s, .. }) => s.fold_with(self),
- _ => pat.super_fold_with(self),
- }
- }
-}
-
-impl<'tcx> Pat<'tcx> {
- pub(super) fn is_wildcard(&self) -> bool {
- match *self.kind {
- PatKind::Binding { subpattern: None, .. } | PatKind::Wild => true,
- _ => false,
- }
- }
-}
-
-/// A row of a matrix. Rows of len 1 are very common, which is why `SmallVec[_; 2]`
-/// works well.
-#[derive(Debug, Clone)]
-pub struct PatStack<'p, 'tcx>(SmallVec<[&'p Pat<'tcx>; 2]>);
-
-impl<'p, 'tcx> PatStack<'p, 'tcx> {
- pub fn from_pattern(pat: &'p Pat<'tcx>) -> Self {
- PatStack(smallvec![pat])
- }
-
- fn from_vec(vec: SmallVec<[&'p Pat<'tcx>; 2]>) -> Self {
- PatStack(vec)
- }
-
- fn from_slice(s: &[&'p Pat<'tcx>]) -> Self {
- PatStack(SmallVec::from_slice(s))
- }
-
- fn is_empty(&self) -> bool {
- self.0.is_empty()
- }
-
- fn len(&self) -> usize {
- self.0.len()
- }
-
- fn head(&self) -> &'p Pat<'tcx> {
- self.0[0]
- }
-
- fn to_tail(&self) -> Self {
- PatStack::from_slice(&self.0[1..])
- }
-
- fn iter(&self) -> impl Iterator<Item = &Pat<'tcx>> {
- self.0.iter().map(|p| *p)
- }
-
- // If the first pattern is an or-pattern, expand this pattern. Otherwise, return `None`.
- fn expand_or_pat(&self) -> Option<Vec<Self>> {
- if self.is_empty() {
- None
- } else if let PatKind::Or { pats } = &*self.head().kind {
- Some(
- pats.iter()
- .map(|pat| {
- let mut new_patstack = PatStack::from_pattern(pat);
- new_patstack.0.extend_from_slice(&self.0[1..]);
- new_patstack
- })
- .collect(),
- )
- } else {
- None
- }
- }
-
- /// This computes `D(self)`. See top of the file for explanations.
- fn specialize_wildcard(&self) -> Option<Self> {
- if self.head().is_wildcard() { Some(self.to_tail()) } else { None }
- }
-
- /// This computes `S(constructor, self)`. See top of the file for explanations.
- fn specialize_constructor(
- &self,
- cx: &mut MatchCheckCtxt<'p, 'tcx>,
- constructor: &Constructor<'tcx>,
- ctor_wild_subpatterns: &'p [Pat<'tcx>],
- ) -> Option<PatStack<'p, 'tcx>> {
- let new_heads = specialize_one_pattern(cx, self.head(), constructor, ctor_wild_subpatterns);
- new_heads.map(|mut new_head| {
- new_head.0.extend_from_slice(&self.0[1..]);
- new_head
- })
- }
-}
-
-impl<'p, 'tcx> Default for PatStack<'p, 'tcx> {
- fn default() -> Self {
- PatStack(smallvec![])
- }
-}
-
-impl<'p, 'tcx> FromIterator<&'p Pat<'tcx>> for PatStack<'p, 'tcx> {
- fn from_iter<T>(iter: T) -> Self
- where
- T: IntoIterator<Item = &'p Pat<'tcx>>,
- {
- PatStack(iter.into_iter().collect())
- }
-}
-
-/// A 2D matrix.
-#[derive(Clone)]
-pub struct Matrix<'p, 'tcx>(Vec<PatStack<'p, 'tcx>>);
-
-impl<'p, 'tcx> Matrix<'p, 'tcx> {
- pub fn empty() -> Self {
- Matrix(vec![])
- }
-
- /// Pushes a new row to the matrix. If the row starts with an or-pattern, this expands it.
- pub fn push(&mut self, row: PatStack<'p, 'tcx>) {
- if let Some(rows) = row.expand_or_pat() {
- self.0.extend(rows);
- } else {
- self.0.push(row);
- }
- }
-
- /// Iterate over the first component of each row
- fn heads<'a>(&'a self) -> impl Iterator<Item = &'a Pat<'tcx>> + Captures<'p> {
- self.0.iter().map(|r| r.head())
- }
-
- /// This computes `D(self)`. See top of the file for explanations.
- fn specialize_wildcard(&self) -> Self {
- self.0.iter().filter_map(|r| r.specialize_wildcard()).collect()
- }
-
- /// This computes `S(constructor, self)`. See top of the file for explanations.
- fn specialize_constructor(
- &self,
- cx: &mut MatchCheckCtxt<'p, 'tcx>,
- constructor: &Constructor<'tcx>,
- ctor_wild_subpatterns: &'p [Pat<'tcx>],
- ) -> Matrix<'p, 'tcx> {
- self.0
- .iter()
- .filter_map(|r| r.specialize_constructor(cx, constructor, ctor_wild_subpatterns))
- .collect()
- }
-}
-
-/// Pretty-printer for matrices of patterns, example:
-/// +++++++++++++++++++++++++++++
-/// + _ + [] +
-/// +++++++++++++++++++++++++++++
-/// + true + [First] +
-/// +++++++++++++++++++++++++++++
-/// + true + [Second(true)] +
-/// +++++++++++++++++++++++++++++
-/// + false + [_] +
-/// +++++++++++++++++++++++++++++
-/// + _ + [_, _, tail @ ..] +
-/// +++++++++++++++++++++++++++++
-impl<'p, 'tcx> fmt::Debug for Matrix<'p, 'tcx> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "\n")?;
-
- let &Matrix(ref m) = self;
- let pretty_printed_matrix: Vec<Vec<String>> =
- m.iter().map(|row| row.iter().map(|pat| format!("{:?}", pat)).collect()).collect();
-
- let column_count = m.iter().map(|row| row.len()).max().unwrap_or(0);
- assert!(m.iter().all(|row| row.len() == column_count));
- let column_widths: Vec<usize> = (0..column_count)
- .map(|col| pretty_printed_matrix.iter().map(|row| row[col].len()).max().unwrap_or(0))
- .collect();
-
- let total_width = column_widths.iter().cloned().sum::<usize>() + column_count * 3 + 1;
- let br = "+".repeat(total_width);
- write!(f, "{}\n", br)?;
- for row in pretty_printed_matrix {
- write!(f, "+")?;
- for (column, pat_str) in row.into_iter().enumerate() {
- write!(f, " ")?;
- write!(f, "{:1$}", pat_str, column_widths[column])?;
- write!(f, " +")?;
- }
- write!(f, "\n")?;
- write!(f, "{}\n", br)?;
- }
- Ok(())
- }
-}
-
-impl<'p, 'tcx> FromIterator<PatStack<'p, 'tcx>> for Matrix<'p, 'tcx> {
- fn from_iter<T>(iter: T) -> Self
- where
- T: IntoIterator<Item = PatStack<'p, 'tcx>>,
- {
- let mut matrix = Matrix::empty();
- for x in iter {
- // Using `push` ensures we correctly expand or-patterns.
- matrix.push(x);
- }
- matrix
- }
-}
-
-pub struct MatchCheckCtxt<'a, 'tcx> {
- pub tcx: TyCtxt<'tcx>,
- /// The module in which the match occurs. This is necessary for
- /// checking inhabited-ness of types because whether a type is (visibly)
- /// inhabited can depend on whether it was defined in the current module or
- /// not. E.g., `struct Foo { _private: ! }` cannot be seen to be empty
- /// outside it's module and should not be matchable with an empty match
- /// statement.
- pub module: DefId,
- param_env: ty::ParamEnv<'tcx>,
- pub pattern_arena: &'a TypedArena<Pat<'tcx>>,
- pub byte_array_map: FxHashMap<*const Pat<'tcx>, Vec<&'a Pat<'tcx>>>,
-}
-
-impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
- pub fn create_and_enter<F, R>(
- tcx: TyCtxt<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- module: DefId,
- f: F,
- ) -> R
- where
- F: for<'b> FnOnce(MatchCheckCtxt<'b, 'tcx>) -> R,
- {
- let pattern_arena = TypedArena::default();
-
- f(MatchCheckCtxt {
- tcx,
- param_env,
- module,
- pattern_arena: &pattern_arena,
- byte_array_map: FxHashMap::default(),
- })
- }
-
- fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool {
- if self.tcx.features().exhaustive_patterns {
- self.tcx.is_ty_uninhabited_from(self.module, ty)
- } else {
- false
- }
- }
-
- // Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`.
- pub fn is_foreign_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool {
- match ty.kind {
- ty::Adt(def, ..) => {
- def.is_enum() && def.is_variant_list_non_exhaustive() && !def.did.is_local()
- }
- _ => false,
- }
- }
-
- // Returns whether the given variant is from another crate and has its fields declared
- // `#[non_exhaustive]`.
- fn is_foreign_non_exhaustive_variant(&self, ty: Ty<'tcx>, variant: &VariantDef) -> bool {
- match ty.kind {
- ty::Adt(def, ..) => variant.is_field_list_non_exhaustive() && !def.did.is_local(),
- _ => false,
- }
- }
-}
-
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-enum SliceKind {
- /// Patterns of length `n` (`[x, y]`).
- FixedLen(u64),
- /// Patterns using the `..` notation (`[x, .., y]`).
- /// Captures any array constructor of `length >= i + j`.
- /// In the case where `array_len` is `Some(_)`,
- /// this indicates that we only care about the first `i` and the last `j` values of the array,
- /// and everything in between is a wildcard `_`.
- VarLen(u64, u64),
-}
-
-impl SliceKind {
- fn arity(self) -> u64 {
- match self {
- FixedLen(length) => length,
- VarLen(prefix, suffix) => prefix + suffix,
- }
- }
-
- /// Whether this pattern includes patterns of length `other_len`.
- fn covers_length(self, other_len: u64) -> bool {
- match self {
- FixedLen(len) => len == other_len,
- VarLen(prefix, suffix) => prefix + suffix <= other_len,
- }
- }
-
- /// Returns a collection of slices that spans the values covered by `self`, subtracted by the
- /// values covered by `other`: i.e., `self \ other` (in set notation).
- fn subtract(self, other: Self) -> SmallVec<[Self; 1]> {
- // Remember, `VarLen(i, j)` covers the union of `FixedLen` from `i + j` to infinity.
- // Naming: we remove the "neg" constructors from the "pos" ones.
- match self {
- FixedLen(pos_len) => {
- if other.covers_length(pos_len) {
- smallvec![]
- } else {
- smallvec![self]
- }
- }
- VarLen(pos_prefix, pos_suffix) => {
- let pos_len = pos_prefix + pos_suffix;
- match other {
- FixedLen(neg_len) => {
- if neg_len < pos_len {
- smallvec![self]
- } else {
- (pos_len..neg_len)
- .map(FixedLen)
- // We know that `neg_len + 1 >= pos_len >= pos_suffix`.
- .chain(Some(VarLen(neg_len + 1 - pos_suffix, pos_suffix)))
- .collect()
- }
- }
- VarLen(neg_prefix, neg_suffix) => {
- let neg_len = neg_prefix + neg_suffix;
- if neg_len <= pos_len {
- smallvec![]
- } else {
- (pos_len..neg_len).map(FixedLen).collect()
- }
- }
- }
- }
- }
- }
-}
-
-/// A constructor for array and slice patterns.
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-struct Slice {
- /// `None` if the matched value is a slice, `Some(n)` if it is an array of size `n`.
- array_len: Option<u64>,
- /// The kind of pattern it is: fixed-length `[x, y]` or variable length `[x, .., y]`.
- kind: SliceKind,
-}
-
-impl Slice {
- /// Returns what patterns this constructor covers: either fixed-length patterns or
- /// variable-length patterns.
- fn pattern_kind(self) -> SliceKind {
- match self {
- Slice { array_len: Some(len), kind: VarLen(prefix, suffix) }
- if prefix + suffix == len =>
- {
- FixedLen(len)
- }
- _ => self.kind,
- }
- }
-
- /// Returns what values this constructor covers: either values of only one given length, or
- /// values of length above a given length.
- /// This is different from `pattern_kind()` because in some cases the pattern only takes into
- /// account a subset of the entries of the array, but still only captures values of a given
- /// length.
- fn value_kind(self) -> SliceKind {
- match self {
- Slice { array_len: Some(len), kind: VarLen(_, _) } => FixedLen(len),
- _ => self.kind,
- }
- }
-
- fn arity(self) -> u64 {
- self.pattern_kind().arity()
- }
-}
-
-#[derive(Clone, Debug, PartialEq)]
-enum Constructor<'tcx> {
- /// The constructor of all patterns that don't vary by constructor,
- /// e.g., struct patterns and fixed-length arrays.
- Single,
- /// Enum variants.
- Variant(DefId),
- /// Literal values.
- ConstantValue(&'tcx ty::Const<'tcx>),
- /// Ranges of integer literal values (`2`, `2..=5` or `2..5`).
- IntRange(IntRange<'tcx>),
- /// Ranges of floating-point literal values (`2.0..=5.2`).
- FloatRange(&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>, RangeEnd),
- /// Array and slice patterns.
- Slice(Slice),
- /// Fake extra constructor for enums that aren't allowed to be matched exhaustively.
- NonExhaustive,
-}
-
-impl<'tcx> Constructor<'tcx> {
- fn is_slice(&self) -> bool {
- match self {
- Slice(_) => true,
- _ => false,
- }
- }
-
- fn variant_index_for_adt<'a>(
- &self,
- cx: &MatchCheckCtxt<'a, 'tcx>,
- adt: &'tcx ty::AdtDef,
- ) -> VariantIdx {
- match self {
- Variant(id) => adt.variant_index_with_id(*id),
- Single => {
- assert!(!adt.is_enum());
- VariantIdx::new(0)
- }
- ConstantValue(c) => crate::const_eval::const_variant_index(cx.tcx, cx.param_env, c),
- _ => bug!("bad constructor {:?} for adt {:?}", self, adt),
- }
- }
-
- // Returns the set of constructors covered by `self` but not by
- // anything in `other_ctors`.
- fn subtract_ctors(&self, other_ctors: &Vec<Constructor<'tcx>>) -> Vec<Constructor<'tcx>> {
- if other_ctors.is_empty() {
- return vec![self.clone()];
- }
-
- match self {
- // Those constructors can only match themselves.
- Single | Variant(_) | ConstantValue(..) | FloatRange(..) => {
- if other_ctors.iter().any(|c| c == self) { vec![] } else { vec![self.clone()] }
- }
- &Slice(slice) => {
- let mut other_slices = other_ctors
- .iter()
- .filter_map(|c: &Constructor<'_>| match c {
- Slice(slice) => Some(*slice),
- // FIXME(oli-obk): implement `deref` for `ConstValue`
- ConstantValue(..) => None,
- _ => bug!("bad slice pattern constructor {:?}", c),
- })
- .map(Slice::value_kind);
-
- match slice.value_kind() {
- FixedLen(self_len) => {
- if other_slices.any(|other_slice| other_slice.covers_length(self_len)) {
- vec![]
- } else {
- vec![Slice(slice)]
- }
- }
- kind @ VarLen(..) => {
- let mut remaining_slices = vec![kind];
-
- // For each used slice, subtract from the current set of slices.
- for other_slice in other_slices {
- remaining_slices = remaining_slices
- .into_iter()
- .flat_map(|remaining_slice| remaining_slice.subtract(other_slice))
- .collect();
-
- // If the constructors that have been considered so far already cover
- // the entire range of `self`, no need to look at more constructors.
- if remaining_slices.is_empty() {
- break;
- }
- }
-
- remaining_slices
- .into_iter()
- .map(|kind| Slice { array_len: slice.array_len, kind })
- .map(Slice)
- .collect()
- }
- }
- }
- IntRange(self_range) => {
- let mut remaining_ranges = vec![self_range.clone()];
- for other_ctor in other_ctors {
- if let IntRange(other_range) = other_ctor {
- if other_range == self_range {
- // If the `self` range appears directly in a `match` arm, we can
- // eliminate it straight away.
- remaining_ranges = vec![];
- } else {
- // Otherwise explicitely compute the remaining ranges.
- remaining_ranges = other_range.subtract_from(remaining_ranges);
- }
-
- // If the ranges that have been considered so far already cover the entire
- // range of values, we can return early.
- if remaining_ranges.is_empty() {
- break;
- }
- }
- }
-
- // Convert the ranges back into constructors.
- remaining_ranges.into_iter().map(IntRange).collect()
- }
- // This constructor is never covered by anything else
- NonExhaustive => vec![NonExhaustive],
- }
- }
-
- /// This returns one wildcard pattern for each argument to this constructor.
- ///
- /// This must be consistent with `apply`, `specialize_one_pattern`, and `arity`.
- fn wildcard_subpatterns<'a>(
- &self,
- cx: &MatchCheckCtxt<'a, 'tcx>,
- ty: Ty<'tcx>,
- ) -> Vec<Pat<'tcx>> {
- debug!("wildcard_subpatterns({:#?}, {:?})", self, ty);
-
- match self {
- Single | Variant(_) => match ty.kind {
- ty::Tuple(ref fs) => {
- fs.into_iter().map(|t| t.expect_ty()).map(Pat::wildcard_from_ty).collect()
- }
- ty::Ref(_, rty, _) => vec![Pat::wildcard_from_ty(rty)],
- ty::Adt(adt, substs) => {
- if adt.is_box() {
- // Use T as the sub pattern type of Box<T>.
- vec![Pat::wildcard_from_ty(substs.type_at(0))]
- } else {
- let variant = &adt.variants[self.variant_index_for_adt(cx, adt)];
- let is_non_exhaustive = cx.is_foreign_non_exhaustive_variant(ty, variant);
- variant
- .fields
- .iter()
- .map(|field| {
- let is_visible = adt.is_enum()
- || field.vis.is_accessible_from(cx.module, cx.tcx);
- let is_uninhabited = cx.is_uninhabited(field.ty(cx.tcx, substs));
- match (is_visible, is_non_exhaustive, is_uninhabited) {
- // Treat all uninhabited types in non-exhaustive variants as
- // `TyErr`.
- (_, true, true) => cx.tcx.types.err,
- // Treat all non-visible fields as `TyErr`. They can't appear
- // in any other pattern from this match (because they are
- // private), so their type does not matter - but we don't want
- // to know they are uninhabited.
- (false, ..) => cx.tcx.types.err,
- (true, ..) => {
- let ty = field.ty(cx.tcx, substs);
- match ty.kind {
- // If the field type returned is an array of an unknown
- // size return an TyErr.
- ty::Array(_, len)
- if len
- .try_eval_usize(cx.tcx, cx.param_env)
- .is_none() =>
- {
- cx.tcx.types.err
- }
- _ => ty,
- }
- }
- }
- })
- .map(Pat::wildcard_from_ty)
- .collect()
- }
- }
- _ => vec![],
- },
- Slice(_) => match ty.kind {
- ty::Slice(ty) | ty::Array(ty, _) => {
- let arity = self.arity(cx, ty);
- (0..arity).map(|_| Pat::wildcard_from_ty(ty)).collect()
- }
- _ => bug!("bad slice pattern {:?} {:?}", self, ty),
- },
- ConstantValue(..) | FloatRange(..) | IntRange(..) | NonExhaustive => vec![],
- }
- }
-
- /// This computes the arity of a constructor. The arity of a constructor
- /// is how many subpattern patterns of that constructor should be expanded to.
- ///
- /// For instance, a tuple pattern `(_, 42, Some([]))` has the arity of 3.
- /// A struct pattern's arity is the number of fields it contains, etc.
- ///
- /// This must be consistent with `wildcard_subpatterns`, `specialize_one_pattern`, and `apply`.
- fn arity<'a>(&self, cx: &MatchCheckCtxt<'a, 'tcx>, ty: Ty<'tcx>) -> u64 {
- debug!("Constructor::arity({:#?}, {:?})", self, ty);
- match self {
- Single | Variant(_) => match ty.kind {
- ty::Tuple(ref fs) => fs.len() as u64,
- ty::Slice(..) | ty::Array(..) => bug!("bad slice pattern {:?} {:?}", self, ty),
- ty::Ref(..) => 1,
- ty::Adt(adt, _) => {
- adt.variants[self.variant_index_for_adt(cx, adt)].fields.len() as u64
- }
- _ => 0,
- },
- Slice(slice) => slice.arity(),
- ConstantValue(..) | FloatRange(..) | IntRange(..) | NonExhaustive => 0,
- }
- }
-
- /// Apply a constructor to a list of patterns, yielding a new pattern. `pats`
- /// must have as many elements as this constructor's arity.
- ///
- /// This must be consistent with `wildcard_subpatterns`, `specialize_one_pattern`, and `arity`.
- ///
- /// Examples:
- /// `self`: `Constructor::Single`
- /// `ty`: `(u32, u32, u32)`
- /// `pats`: `[10, 20, _]`
- /// returns `(10, 20, _)`
- ///
- /// `self`: `Constructor::Variant(Option::Some)`
- /// `ty`: `Option<bool>`
- /// `pats`: `[false]`
- /// returns `Some(false)`
- fn apply<'a>(
- &self,
- cx: &MatchCheckCtxt<'a, 'tcx>,
- ty: Ty<'tcx>,
- pats: impl IntoIterator<Item = Pat<'tcx>>,
- ) -> Pat<'tcx> {
- let mut subpatterns = pats.into_iter();
-
- let pat = match self {
- Single | Variant(_) => match ty.kind {
- ty::Adt(..) | ty::Tuple(..) => {
- let subpatterns = subpatterns
- .enumerate()
- .map(|(i, p)| FieldPat { field: Field::new(i), pattern: p })
- .collect();
-
- if let ty::Adt(adt, substs) = ty.kind {
- if adt.is_enum() {
- PatKind::Variant {
- adt_def: adt,
- substs,
- variant_index: self.variant_index_for_adt(cx, adt),
- subpatterns,
- }
- } else {
- PatKind::Leaf { subpatterns }
- }
- } else {
- PatKind::Leaf { subpatterns }
- }
- }
- ty::Ref(..) => PatKind::Deref { subpattern: subpatterns.nth(0).unwrap() },
- ty::Slice(_) | ty::Array(..) => bug!("bad slice pattern {:?} {:?}", self, ty),
- _ => PatKind::Wild,
- },
- Slice(slice) => match slice.pattern_kind() {
- FixedLen(_) => {
- PatKind::Slice { prefix: subpatterns.collect(), slice: None, suffix: vec![] }
- }
- VarLen(prefix, _) => {
- let mut prefix: Vec<_> = subpatterns.by_ref().take(prefix as usize).collect();
- if slice.array_len.is_some() {
- // Improves diagnostics a bit: if the type is a known-size array, instead
- // of reporting `[x, _, .., _, y]`, we prefer to report `[x, .., y]`.
- // This is incorrect if the size is not known, since `[_, ..]` captures
- // arrays of lengths `>= 1` whereas `[..]` captures any length.
- while !prefix.is_empty() && prefix.last().unwrap().is_wildcard() {
- prefix.pop();
- }
- }
- let suffix: Vec<_> = if slice.array_len.is_some() {
- // Same as above.
- subpatterns.skip_while(Pat::is_wildcard).collect()
- } else {
- subpatterns.collect()
- };
- let wild = Pat::wildcard_from_ty(ty);
- PatKind::Slice { prefix, slice: Some(wild), suffix }
- }
- },
- &ConstantValue(value) => PatKind::Constant { value },
- &FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }),
- IntRange(range) => return range.to_pat(cx.tcx),
- NonExhaustive => PatKind::Wild,
- };
-
- Pat { ty, span: DUMMY_SP, kind: Box::new(pat) }
- }
-
- /// Like `apply`, but where all the subpatterns are wildcards `_`.
- fn apply_wildcards<'a>(&self, cx: &MatchCheckCtxt<'a, 'tcx>, ty: Ty<'tcx>) -> Pat<'tcx> {
- let subpatterns = self.wildcard_subpatterns(cx, ty).into_iter().rev();
- self.apply(cx, ty, subpatterns)
- }
-}
-
-#[derive(Clone, Debug)]
-pub enum Usefulness<'tcx, 'p> {
- /// Carries a list of unreachable subpatterns. Used only in the presence of or-patterns.
- Useful(Vec<&'p Pat<'tcx>>),
- /// Carries a list of witnesses of non-exhaustiveness.
- UsefulWithWitness(Vec<Witness<'tcx>>),
- NotUseful,
-}
-
-impl<'tcx, 'p> Usefulness<'tcx, 'p> {
- fn new_useful(preference: WitnessPreference) -> Self {
- match preference {
- ConstructWitness => UsefulWithWitness(vec![Witness(vec![])]),
- LeaveOutWitness => Useful(vec![]),
- }
- }
-
- fn is_useful(&self) -> bool {
- match *self {
- NotUseful => false,
- _ => true,
- }
- }
-
- fn apply_constructor(
- self,
- cx: &MatchCheckCtxt<'_, 'tcx>,
- ctor: &Constructor<'tcx>,
- ty: Ty<'tcx>,
- ) -> Self {
- match self {
- UsefulWithWitness(witnesses) => UsefulWithWitness(
- witnesses
- .into_iter()
- .map(|witness| witness.apply_constructor(cx, &ctor, ty))
- .collect(),
- ),
- x => x,
- }
- }
-
- fn apply_wildcard(self, ty: Ty<'tcx>) -> Self {
- match self {
- UsefulWithWitness(witnesses) => {
- let wild = Pat::wildcard_from_ty(ty);
- UsefulWithWitness(
- witnesses
- .into_iter()
- .map(|mut witness| {
- witness.0.push(wild.clone());
- witness
- })
- .collect(),
- )
- }
- x => x,
- }
- }
-
- fn apply_missing_ctors(
- self,
- cx: &MatchCheckCtxt<'_, 'tcx>,
- ty: Ty<'tcx>,
- missing_ctors: &MissingConstructors<'tcx>,
- ) -> Self {
- match self {
- UsefulWithWitness(witnesses) => {
- let new_patterns: Vec<_> =
- missing_ctors.iter().map(|ctor| ctor.apply_wildcards(cx, ty)).collect();
- // Add the new patterns to each witness
- UsefulWithWitness(
- witnesses
- .into_iter()
- .flat_map(|witness| {
- new_patterns.iter().map(move |pat| {
- let mut witness = witness.clone();
- witness.0.push(pat.clone());
- witness
- })
- })
- .collect(),
- )
- }
- x => x,
- }
- }
-}
-
-#[derive(Copy, Clone, Debug)]
-pub enum WitnessPreference {
- ConstructWitness,
- LeaveOutWitness,
-}
-
-#[derive(Copy, Clone, Debug)]
-struct PatCtxt<'tcx> {
- ty: Ty<'tcx>,
- span: Span,
-}
-
-/// A witness of non-exhaustiveness for error reporting, represented
-/// as a list of patterns (in reverse order of construction) with
-/// wildcards inside to represent elements that can take any inhabitant
-/// of the type as a value.
-///
-/// A witness against a list of patterns should have the same types
-/// and length as the pattern matched against. Because Rust `match`
-/// is always against a single pattern, at the end the witness will
-/// have length 1, but in the middle of the algorithm, it can contain
-/// multiple patterns.
-///
-/// For example, if we are constructing a witness for the match against
-/// ```
-/// struct Pair(Option<(u32, u32)>, bool);
-///
-/// match (p: Pair) {
-/// Pair(None, _) => {}
-/// Pair(_, false) => {}
-/// }
-/// ```
-///
-/// We'll perform the following steps:
-/// 1. Start with an empty witness
-/// `Witness(vec![])`
-/// 2. Push a witness `Some(_)` against the `None`
-/// `Witness(vec![Some(_)])`
-/// 3. Push a witness `true` against the `false`
-/// `Witness(vec![Some(_), true])`
-/// 4. Apply the `Pair` constructor to the witnesses
-/// `Witness(vec![Pair(Some(_), true)])`
-///
-/// The final `Pair(Some(_), true)` is then the resulting witness.
-#[derive(Clone, Debug)]
-pub struct Witness<'tcx>(Vec<Pat<'tcx>>);
-
-impl<'tcx> Witness<'tcx> {
- pub fn single_pattern(self) -> Pat<'tcx> {
- assert_eq!(self.0.len(), 1);
- self.0.into_iter().next().unwrap()
- }
-
- /// Constructs a partial witness for a pattern given a list of
- /// patterns expanded by the specialization step.
- ///
- /// When a pattern P is discovered to be useful, this function is used bottom-up
- /// to reconstruct a complete witness, e.g., a pattern P' that covers a subset
- /// of values, V, where each value in that set is not covered by any previously
- /// used patterns and is covered by the pattern P'. Examples:
- ///
- /// left_ty: tuple of 3 elements
- /// pats: [10, 20, _] => (10, 20, _)
- ///
- /// left_ty: struct X { a: (bool, &'static str), b: usize}
- /// pats: [(false, "foo"), 42] => X { a: (false, "foo"), b: 42 }
- fn apply_constructor<'a>(
- mut self,
- cx: &MatchCheckCtxt<'a, 'tcx>,
- ctor: &Constructor<'tcx>,
- ty: Ty<'tcx>,
- ) -> Self {
- let arity = ctor.arity(cx, ty);
- let pat = {
- let len = self.0.len() as u64;
- let pats = self.0.drain((len - arity) as usize..).rev();
- ctor.apply(cx, ty, pats)
- };
-
- self.0.push(pat);
-
- self
- }
-}
-
-/// This determines the set of all possible constructors of a pattern matching
-/// values of type `left_ty`. For vectors, this would normally be an infinite set
-/// but is instead bounded by the maximum fixed length of slice patterns in
-/// the column of patterns being analyzed.
-///
-/// We make sure to omit constructors that are statically impossible. E.g., for
-/// `Option<!>`, we do not include `Some(_)` in the returned list of constructors.
-/// Invariant: this returns an empty `Vec` if and only if the type is uninhabited (as determined by
-/// `cx.is_uninhabited()`).
-fn all_constructors<'a, 'tcx>(
- cx: &mut MatchCheckCtxt<'a, 'tcx>,
- pcx: PatCtxt<'tcx>,
-) -> Vec<Constructor<'tcx>> {
- debug!("all_constructors({:?})", pcx.ty);
- let make_range = |start, end| {
- IntRange(
- // `unwrap()` is ok because we know the type is an integer.
- IntRange::from_range(cx.tcx, start, end, pcx.ty, &RangeEnd::Included, pcx.span)
- .unwrap(),
- )
- };
- match pcx.ty.kind {
- ty::Bool => {
- [true, false].iter().map(|&b| ConstantValue(ty::Const::from_bool(cx.tcx, b))).collect()
- }
- ty::Array(ref sub_ty, len) if len.try_eval_usize(cx.tcx, cx.param_env).is_some() => {
- let len = len.eval_usize(cx.tcx, cx.param_env);
- if len != 0 && cx.is_uninhabited(sub_ty) {
- vec![]
- } else {
- vec![Slice(Slice { array_len: Some(len), kind: VarLen(0, 0) })]
- }
- }
- // Treat arrays of a constant but unknown length like slices.
- ty::Array(ref sub_ty, _) | ty::Slice(ref sub_ty) => {
- let kind = if cx.is_uninhabited(sub_ty) { FixedLen(0) } else { VarLen(0, 0) };
- vec![Slice(Slice { array_len: None, kind })]
- }
- ty::Adt(def, substs) if def.is_enum() => {
- let ctors: Vec<_> = if cx.tcx.features().exhaustive_patterns {
- // If `exhaustive_patterns` is enabled, we exclude variants known to be
- // uninhabited.
- def.variants
- .iter()
- .filter(|v| {
- !v.uninhabited_from(cx.tcx, substs, def.adt_kind())
- .contains(cx.tcx, cx.module)
- })
- .map(|v| Variant(v.def_id))
- .collect()
- } else {
- def.variants.iter().map(|v| Variant(v.def_id)).collect()
- };
-
- // If the enum is declared as `#[non_exhaustive]`, we treat it as if it had an
- // additional "unknown" constructor.
- // There is no point in enumerating all possible variants, because the user can't
- // actually match against them all themselves. So we always return only the fictitious
- // constructor.
- // E.g., in an example like:
- // ```
- // let err: io::ErrorKind = ...;
- // match err {
- // io::ErrorKind::NotFound => {},
- // }
- // ```
- // we don't want to show every possible IO error, but instead have only `_` as the
- // witness.
- let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(pcx.ty);
-
- // If `exhaustive_patterns` is disabled and our scrutinee is an empty enum, we treat it
- // as though it had an "unknown" constructor to avoid exposing its emptyness. Note that
- // an empty match will still be considered exhaustive because that case is handled
- // separately in `check_match`.
- let is_secretly_empty =
- def.variants.is_empty() && !cx.tcx.features().exhaustive_patterns;
-
- if is_secretly_empty || is_declared_nonexhaustive { vec![NonExhaustive] } else { ctors }
- }
- ty::Char => {
- vec![
- // The valid Unicode Scalar Value ranges.
- make_range('\u{0000}' as u128, '\u{D7FF}' as u128),
- make_range('\u{E000}' as u128, '\u{10FFFF}' as u128),
- ]
- }
- ty::Int(_) | ty::Uint(_)
- if pcx.ty.is_ptr_sized_integral()
- && !cx.tcx.features().precise_pointer_size_matching =>
- {
- // `usize`/`isize` are not allowed to be matched exhaustively unless the
- // `precise_pointer_size_matching` feature is enabled. So we treat those types like
- // `#[non_exhaustive]` enums by returning a special unmatcheable constructor.
- vec![NonExhaustive]
- }
- ty::Int(ity) => {
- let bits = Integer::from_attr(&cx.tcx, SignedInt(ity)).size().bits() as u128;
- let min = 1u128 << (bits - 1);
- let max = min - 1;
- vec![make_range(min, max)]
- }
- ty::Uint(uty) => {
- let size = Integer::from_attr(&cx.tcx, UnsignedInt(uty)).size();
- let max = truncate(u128::max_value(), size);
- vec![make_range(0, max)]
- }
- _ => {
- if cx.is_uninhabited(pcx.ty) {
- vec![]
- } else {
- vec![Single]
- }
- }
- }
-}
-
-/// An inclusive interval, used for precise integer exhaustiveness checking.
-/// `IntRange`s always store a contiguous range. This means that values are
-/// encoded such that `0` encodes the minimum value for the integer,
-/// regardless of the signedness.
-/// For example, the pattern `-128..=127i8` is encoded as `0..=255`.
-/// This makes comparisons and arithmetic on interval endpoints much more
-/// straightforward. See `signed_bias` for details.
-///
-/// `IntRange` is never used to encode an empty range or a "range" that wraps
-/// around the (offset) space: i.e., `range.lo <= range.hi`.
-#[derive(Clone, Debug)]
-struct IntRange<'tcx> {
- pub range: RangeInclusive<u128>,
- pub ty: Ty<'tcx>,
- pub span: Span,
-}
-
-impl<'tcx> IntRange<'tcx> {
- #[inline]
- fn is_integral(ty: Ty<'_>) -> bool {
- match ty.kind {
- ty::Char | ty::Int(_) | ty::Uint(_) => true,
- _ => false,
- }
- }
-
- fn is_singleton(&self) -> bool {
- self.range.start() == self.range.end()
- }
-
- fn boundaries(&self) -> (u128, u128) {
- (*self.range.start(), *self.range.end())
- }
-
- /// Don't treat `usize`/`isize` exhaustively unless the `precise_pointer_size_matching` feature
- /// is enabled.
- fn treat_exhaustively(&self, tcx: TyCtxt<'tcx>) -> bool {
- !self.ty.is_ptr_sized_integral() || tcx.features().precise_pointer_size_matching
- }
-
- #[inline]
- fn integral_size_and_signed_bias(tcx: TyCtxt<'tcx>, ty: Ty<'_>) -> Option<(Size, u128)> {
- match ty.kind {
- ty::Char => Some((Size::from_bytes(4), 0)),
- ty::Int(ity) => {
- let size = Integer::from_attr(&tcx, SignedInt(ity)).size();
- Some((size, 1u128 << (size.bits() as u128 - 1)))
- }
- ty::Uint(uty) => Some((Integer::from_attr(&tcx, UnsignedInt(uty)).size(), 0)),
- _ => None,
- }
- }
-
- #[inline]
- fn from_const(
- tcx: TyCtxt<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- value: &Const<'tcx>,
- span: Span,
- ) -> Option<IntRange<'tcx>> {
- if let Some((target_size, bias)) = Self::integral_size_and_signed_bias(tcx, value.ty) {
- let ty = value.ty;
- let val = if let ty::ConstKind::Value(ConstValue::Scalar(Scalar::Raw { data, size })) =
- value.val
- {
- // For this specific pattern we can skip a lot of effort and go
- // straight to the result, after doing a bit of checking. (We
- // could remove this branch and just use the next branch, which
- // is more general but much slower.)
- Scalar::<()>::check_raw(data, size, target_size);
- data
- } else if let Some(val) = value.try_eval_bits(tcx, param_env, ty) {
- // This is a more general form of the previous branch.
- val
- } else {
- return None;
- };
- let val = val ^ bias;
- Some(IntRange { range: val..=val, ty, span })
- } else {
- None
- }
- }
-
- #[inline]
- fn from_range(
- tcx: TyCtxt<'tcx>,
- lo: u128,
- hi: u128,
- ty: Ty<'tcx>,
- end: &RangeEnd,
- span: Span,
- ) -> Option<IntRange<'tcx>> {
- if Self::is_integral(ty) {
- // Perform a shift if the underlying types are signed,
- // which makes the interval arithmetic simpler.
- let bias = IntRange::signed_bias(tcx, ty);
- let (lo, hi) = (lo ^ bias, hi ^ bias);
- let offset = (*end == RangeEnd::Excluded) as u128;
- if lo > hi || (lo == hi && *end == RangeEnd::Excluded) {
- // This should have been caught earlier by E0030.
- bug!("malformed range pattern: {}..={}", lo, (hi - offset));
- }
- Some(IntRange { range: lo..=(hi - offset), ty, span })
- } else {
- None
- }
- }
-
- fn from_pat(
- tcx: TyCtxt<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- pat: &Pat<'tcx>,
- ) -> Option<IntRange<'tcx>> {
- match pat_constructor(tcx, param_env, pat)? {
- IntRange(range) => Some(range),
- _ => None,
- }
- }
-
- // The return value of `signed_bias` should be XORed with an endpoint to encode/decode it.
- fn signed_bias(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> u128 {
- match ty.kind {
- ty::Int(ity) => {
- let bits = Integer::from_attr(&tcx, SignedInt(ity)).size().bits() as u128;
- 1u128 << (bits - 1)
- }
- _ => 0,
- }
- }
-
- /// Returns a collection of ranges that spans the values covered by `ranges`, subtracted
- /// by the values covered by `self`: i.e., `ranges \ self` (in set notation).
- fn subtract_from(&self, ranges: Vec<IntRange<'tcx>>) -> Vec<IntRange<'tcx>> {
- let mut remaining_ranges = vec![];
- let ty = self.ty;
- let span = self.span;
- let (lo, hi) = self.boundaries();
- for subrange in ranges {
- let (subrange_lo, subrange_hi) = subrange.range.into_inner();
- if lo > subrange_hi || subrange_lo > hi {
- // The pattern doesn't intersect with the subrange at all,
- // so the subrange remains untouched.
- remaining_ranges.push(IntRange { range: subrange_lo..=subrange_hi, ty, span });
- } else {
- if lo > subrange_lo {
- // The pattern intersects an upper section of the
- // subrange, so a lower section will remain.
- remaining_ranges.push(IntRange { range: subrange_lo..=(lo - 1), ty, span });
- }
- if hi < subrange_hi {
- // The pattern intersects a lower section of the
- // subrange, so an upper section will remain.
- remaining_ranges.push(IntRange { range: (hi + 1)..=subrange_hi, ty, span });
- }
- }
- }
- remaining_ranges
- }
-
- fn is_subrange(&self, other: &Self) -> bool {
- other.range.start() <= self.range.start() && self.range.end() <= other.range.end()
- }
-
- fn intersection(&self, tcx: TyCtxt<'tcx>, other: &Self) -> Option<Self> {
- let ty = self.ty;
- let (lo, hi) = self.boundaries();
- let (other_lo, other_hi) = other.boundaries();
- if self.treat_exhaustively(tcx) {
- if lo <= other_hi && other_lo <= hi {
- let span = other.span;
- Some(IntRange { range: max(lo, other_lo)..=min(hi, other_hi), ty, span })
- } else {
- None
- }
- } else {
- // If the range should not be treated exhaustively, fallback to checking for inclusion.
- if self.is_subrange(other) { Some(self.clone()) } else { None }
- }
- }
-
- fn suspicious_intersection(&self, other: &Self) -> bool {
- // `false` in the following cases:
- // 1 ---- // 1 ---------- // 1 ---- // 1 ----
- // 2 ---------- // 2 ---- // 2 ---- // 2 ----
- //
- // The following are currently `false`, but could be `true` in the future (#64007):
- // 1 --------- // 1 ---------
- // 2 ---------- // 2 ----------
- //
- // `true` in the following cases:
- // 1 ------- // 1 -------
- // 2 -------- // 2 -------
- let (lo, hi) = self.boundaries();
- let (other_lo, other_hi) = other.boundaries();
- (lo == other_hi || hi == other_lo)
- }
-
- fn to_pat(&self, tcx: TyCtxt<'tcx>) -> Pat<'tcx> {
- let (lo, hi) = self.boundaries();
-
- let bias = IntRange::signed_bias(tcx, self.ty);
- let (lo, hi) = (lo ^ bias, hi ^ bias);
-
- let ty = ty::ParamEnv::empty().and(self.ty);
- let lo_const = ty::Const::from_bits(tcx, lo, ty);
- let hi_const = ty::Const::from_bits(tcx, hi, ty);
-
- let kind = if lo == hi {
- PatKind::Constant { value: lo_const }
- } else {
- PatKind::Range(PatRange { lo: lo_const, hi: hi_const, end: RangeEnd::Included })
- };
-
- // This is a brand new pattern, so we don't reuse `self.span`.
- Pat { ty: self.ty, span: DUMMY_SP, kind: Box::new(kind) }
- }
-}
-
-/// Ignore spans when comparing, they don't carry semantic information as they are only for lints.
-impl<'tcx> std::cmp::PartialEq for IntRange<'tcx> {
- fn eq(&self, other: &Self) -> bool {
- self.range == other.range && self.ty == other.ty
- }
-}
-
-// A struct to compute a set of constructors equivalent to `all_ctors \ used_ctors`.
-struct MissingConstructors<'tcx> {
- all_ctors: Vec<Constructor<'tcx>>,
- used_ctors: Vec<Constructor<'tcx>>,
-}
-
-impl<'tcx> MissingConstructors<'tcx> {
- fn new(all_ctors: Vec<Constructor<'tcx>>, used_ctors: Vec<Constructor<'tcx>>) -> Self {
- MissingConstructors { all_ctors, used_ctors }
- }
-
- fn into_inner(self) -> (Vec<Constructor<'tcx>>, Vec<Constructor<'tcx>>) {
- (self.all_ctors, self.used_ctors)
- }
-
- fn is_empty(&self) -> bool {
- self.iter().next().is_none()
- }
- /// Whether this contains all the constructors for the given type or only a
- /// subset.
- fn all_ctors_are_missing(&self) -> bool {
- self.used_ctors.is_empty()
- }
-
- /// Iterate over all_ctors \ used_ctors
- fn iter<'a>(&'a self) -> impl Iterator<Item = Constructor<'tcx>> + Captures<'a> {
- self.all_ctors.iter().flat_map(move |req_ctor| req_ctor.subtract_ctors(&self.used_ctors))
- }
-}
-
-impl<'tcx> fmt::Debug for MissingConstructors<'tcx> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let ctors: Vec<_> = self.iter().collect();
- write!(f, "{:?}", ctors)
- }
-}
-
-/// Algorithm from http://moscova.inria.fr/~maranget/papers/warn/index.html.
-/// The algorithm from the paper has been modified to correctly handle empty
-/// types. The changes are:
-/// (0) We don't exit early if the pattern matrix has zero rows. We just
-/// continue to recurse over columns.
-/// (1) all_constructors will only return constructors that are statically
-/// possible. E.g., it will only return `Ok` for `Result<T, !>`.
-///
-/// This finds whether a (row) vector `v` of patterns is 'useful' in relation
-/// to a set of such vectors `m` - this is defined as there being a set of
-/// inputs that will match `v` but not any of the sets in `m`.
-///
-/// All the patterns at each column of the `matrix ++ v` matrix must
-/// have the same type, except that wildcard (PatKind::Wild) patterns
-/// with type `TyErr` are also allowed, even if the "type of the column"
-/// is not `TyErr`. That is used to represent private fields, as using their
-/// real type would assert that they are inhabited.
-///
-/// This is used both for reachability checking (if a pattern isn't useful in
-/// relation to preceding patterns, it is not reachable) and exhaustiveness
-/// checking (if a wildcard pattern is useful in relation to a matrix, the
-/// matrix isn't exhaustive).
-pub fn is_useful<'p, 'tcx>(
- cx: &mut MatchCheckCtxt<'p, 'tcx>,
- matrix: &Matrix<'p, 'tcx>,
- v: &PatStack<'p, 'tcx>,
- witness_preference: WitnessPreference,
- hir_id: HirId,
- is_top_level: bool,
-) -> Usefulness<'tcx, 'p> {
- let &Matrix(ref rows) = matrix;
- debug!("is_useful({:#?}, {:#?})", matrix, v);
-
- // The base case. We are pattern-matching on () and the return value is
- // based on whether our matrix has a row or not.
- // NOTE: This could potentially be optimized by checking rows.is_empty()
- // first and then, if v is non-empty, the return value is based on whether
- // the type of the tuple we're checking is inhabited or not.
- if v.is_empty() {
- return if rows.is_empty() {
- Usefulness::new_useful(witness_preference)
- } else {
- NotUseful
- };
- };
-
- assert!(rows.iter().all(|r| r.len() == v.len()));
-
- // If the first pattern is an or-pattern, expand it.
- if let Some(vs) = v.expand_or_pat() {
- // We need to push the already-seen patterns into the matrix in order to detect redundant
- // branches like `Some(_) | Some(0)`. We also keep track of the unreachable subpatterns.
- let mut matrix = matrix.clone();
- let mut unreachable_pats = Vec::new();
- let mut any_is_useful = false;
- for v in vs {
- let res = is_useful(cx, &matrix, &v, witness_preference, hir_id, false);
- match res {
- Useful(pats) => {
- any_is_useful = true;
- unreachable_pats.extend(pats);
- }
- NotUseful => unreachable_pats.push(v.head()),
- UsefulWithWitness(_) => {
- bug!("Encountered or-pat in `v` during exhaustiveness checking")
- }
- }
- matrix.push(v);
- }
- return if any_is_useful { Useful(unreachable_pats) } else { NotUseful };
- }
-
- let (ty, span) = matrix
- .heads()
- .map(|r| (r.ty, r.span))
- .find(|(ty, _)| !ty.references_error())
- .unwrap_or((v.head().ty, v.head().span));
- let pcx = PatCtxt {
- // TyErr is used to represent the type of wildcard patterns matching
- // against inaccessible (private) fields of structs, so that we won't
- // be able to observe whether the types of the struct's fields are
- // inhabited.
- //
- // If the field is truly inaccessible, then all the patterns
- // matching against it must be wildcard patterns, so its type
- // does not matter.
- //
- // However, if we are matching against non-wildcard patterns, we
- // need to know the real type of the field so we can specialize
- // against it. This primarily occurs through constants - they
- // can include contents for fields that are inaccessible at the
- // location of the match. In that case, the field's type is
- // inhabited - by the constant - so we can just use it.
- //
- // FIXME: this might lead to "unstable" behavior with macro hygiene
- // introducing uninhabited patterns for inaccessible fields. We
- // need to figure out how to model that.
- ty,
- span,
- };
-
- debug!("is_useful_expand_first_col: pcx={:#?}, expanding {:#?}", pcx, v.head());
-
- if let Some(constructor) = pat_constructor(cx.tcx, cx.param_env, v.head()) {
- debug!("is_useful - expanding constructor: {:#?}", constructor);
- split_grouped_constructors(
- cx.tcx,
- cx.param_env,
- pcx,
- vec![constructor],
- matrix,
- pcx.span,
- Some(hir_id),
- )
- .into_iter()
- .map(|c| is_useful_specialized(cx, matrix, v, c, pcx.ty, witness_preference, hir_id))
- .find(|result| result.is_useful())
- .unwrap_or(NotUseful)
- } else {
- debug!("is_useful - expanding wildcard");
-
- let used_ctors: Vec<Constructor<'_>> =
- matrix.heads().filter_map(|p| pat_constructor(cx.tcx, cx.param_env, p)).collect();
- debug!("used_ctors = {:#?}", used_ctors);
- // `all_ctors` are all the constructors for the given type, which
- // should all be represented (or caught with the wild pattern `_`).
- let all_ctors = all_constructors(cx, pcx);
- debug!("all_ctors = {:#?}", all_ctors);
-
- // `missing_ctors` is the set of constructors from the same type as the
- // first column of `matrix` that are matched only by wildcard patterns
- // from the first column.
- //
- // Therefore, if there is some pattern that is unmatched by `matrix`,
- // it will still be unmatched if the first constructor is replaced by
- // any of the constructors in `missing_ctors`
-
- // Missing constructors are those that are not matched by any non-wildcard patterns in the
- // current column. We only fully construct them on-demand, because they're rarely used and
- // can be big.
- let missing_ctors = MissingConstructors::new(all_ctors, used_ctors);
-
- debug!("missing_ctors.empty()={:#?}", missing_ctors.is_empty(),);
-
- if missing_ctors.is_empty() {
- let (all_ctors, _) = missing_ctors.into_inner();
- split_grouped_constructors(cx.tcx, cx.param_env, pcx, all_ctors, matrix, DUMMY_SP, None)
- .into_iter()
- .map(|c| {
- is_useful_specialized(cx, matrix, v, c, pcx.ty, witness_preference, hir_id)
- })
- .find(|result| result.is_useful())
- .unwrap_or(NotUseful)
- } else {
- let matrix = matrix.specialize_wildcard();
- let v = v.to_tail();
- let usefulness = is_useful(cx, &matrix, &v, witness_preference, hir_id, false);
-
- // In this case, there's at least one "free"
- // constructor that is only matched against by
- // wildcard patterns.
- //
- // There are 2 ways we can report a witness here.
- // Commonly, we can report all the "free"
- // constructors as witnesses, e.g., if we have:
- //
- // ```
- // enum Direction { N, S, E, W }
- // let Direction::N = ...;
- // ```
- //
- // we can report 3 witnesses: `S`, `E`, and `W`.
- //
- // However, there is a case where we don't want
- // to do this and instead report a single `_` witness:
- // if the user didn't actually specify a constructor
- // in this arm, e.g., in
- // ```
- // let x: (Direction, Direction, bool) = ...;
- // let (_, _, false) = x;
- // ```
- // we don't want to show all 16 possible witnesses
- // `(<direction-1>, <direction-2>, true)` - we are
- // satisfied with `(_, _, true)`. In this case,
- // `used_ctors` is empty.
- // The exception is: if we are at the top-level, for example in an empty match, we
- // sometimes prefer reporting the list of constructors instead of just `_`.
- let report_ctors_rather_than_wildcard = is_top_level && !IntRange::is_integral(pcx.ty);
- if missing_ctors.all_ctors_are_missing() && !report_ctors_rather_than_wildcard {
- // All constructors are unused. Add a wild pattern
- // rather than each individual constructor.
- usefulness.apply_wildcard(pcx.ty)
- } else {
- // Construct for each missing constructor a "wild" version of this
- // constructor, that matches everything that can be built with
- // it. For example, if `ctor` is a `Constructor::Variant` for
- // `Option::Some`, we get the pattern `Some(_)`.
- usefulness.apply_missing_ctors(cx, pcx.ty, &missing_ctors)
- }
- }
- }
-}
-
-/// A shorthand for the `U(S(c, P), S(c, q))` operation from the paper. I.e., `is_useful` applied
-/// to the specialised version of both the pattern matrix `P` and the new pattern `q`.
-fn is_useful_specialized<'p, 'tcx>(
- cx: &mut MatchCheckCtxt<'p, 'tcx>,
- matrix: &Matrix<'p, 'tcx>,
- v: &PatStack<'p, 'tcx>,
- ctor: Constructor<'tcx>,
- lty: Ty<'tcx>,
- witness_preference: WitnessPreference,
- hir_id: HirId,
-) -> Usefulness<'tcx, 'p> {
- debug!("is_useful_specialized({:#?}, {:#?}, {:?})", v, ctor, lty);
-
- let ctor_wild_subpatterns =
- cx.pattern_arena.alloc_from_iter(ctor.wildcard_subpatterns(cx, lty));
- let matrix = matrix.specialize_constructor(cx, &ctor, ctor_wild_subpatterns);
- v.specialize_constructor(cx, &ctor, ctor_wild_subpatterns)
- .map(|v| is_useful(cx, &matrix, &v, witness_preference, hir_id, false))
- .map(|u| u.apply_constructor(cx, &ctor, lty))
- .unwrap_or(NotUseful)
-}
-
-/// Determines the constructor that the given pattern can be specialized to.
-/// Returns `None` in case of a catch-all, which can't be specialized.
-fn pat_constructor<'tcx>(
- tcx: TyCtxt<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- pat: &Pat<'tcx>,
-) -> Option<Constructor<'tcx>> {
- match *pat.kind {
- PatKind::AscribeUserType { .. } => bug!(), // Handled by `expand_pattern`
- PatKind::Binding { .. } | PatKind::Wild => None,
- PatKind::Leaf { .. } | PatKind::Deref { .. } => Some(Single),
- PatKind::Variant { adt_def, variant_index, .. } => {
- Some(Variant(adt_def.variants[variant_index].def_id))
- }
- PatKind::Constant { value } => {
- if let Some(int_range) = IntRange::from_const(tcx, param_env, value, pat.span) {
- Some(IntRange(int_range))
- } else {
- match (value.val, &value.ty.kind) {
- (_, ty::Array(_, n)) => {
- let len = n.eval_usize(tcx, param_env);
- Some(Slice(Slice { array_len: Some(len), kind: FixedLen(len) }))
- }
- (ty::ConstKind::Value(ConstValue::Slice { start, end, .. }), ty::Slice(_)) => {
- let len = (end - start) as u64;
- Some(Slice(Slice { array_len: None, kind: FixedLen(len) }))
- }
- // FIXME(oli-obk): implement `deref` for `ConstValue`
- // (ty::ConstKind::Value(ConstValue::ByRef { .. }), ty::Slice(_)) => { ... }
- _ => Some(ConstantValue(value)),
- }
- }
- }
- PatKind::Range(PatRange { lo, hi, end }) => {
- let ty = lo.ty;
- if let Some(int_range) = IntRange::from_range(
- tcx,
- lo.eval_bits(tcx, param_env, lo.ty),
- hi.eval_bits(tcx, param_env, hi.ty),
- ty,
- &end,
- pat.span,
- ) {
- Some(IntRange(int_range))
- } else {
- Some(FloatRange(lo, hi, end))
- }
- }
- PatKind::Array { ref prefix, ref slice, ref suffix }
- | PatKind::Slice { ref prefix, ref slice, ref suffix } => {
- let array_len = match pat.ty.kind {
- ty::Array(_, length) => Some(length.eval_usize(tcx, param_env)),
- ty::Slice(_) => None,
- _ => span_bug!(pat.span, "bad ty {:?} for slice pattern", pat.ty),
- };
- let prefix = prefix.len() as u64;
- let suffix = suffix.len() as u64;
- let kind =
- if slice.is_some() { VarLen(prefix, suffix) } else { FixedLen(prefix + suffix) };
- Some(Slice(Slice { array_len, kind }))
- }
- PatKind::Or { .. } => bug!("Or-pattern should have been expanded earlier on."),
- }
-}
-
-// checks whether a constant is equal to a user-written slice pattern. Only supports byte slices,
-// meaning all other types will compare unequal and thus equal patterns often do not cause the
-// second pattern to lint about unreachable match arms.
-fn slice_pat_covered_by_const<'tcx>(
- tcx: TyCtxt<'tcx>,
- _span: Span,
- const_val: &'tcx ty::Const<'tcx>,
- prefix: &[Pat<'tcx>],
- slice: &Option<Pat<'tcx>>,
- suffix: &[Pat<'tcx>],
- param_env: ty::ParamEnv<'tcx>,
-) -> Result<bool, ErrorReported> {
- let const_val_val = if let ty::ConstKind::Value(val) = const_val.val {
- val
- } else {
- bug!(
- "slice_pat_covered_by_const: {:#?}, {:#?}, {:#?}, {:#?}",
- const_val,
- prefix,
- slice,
- suffix,
- )
- };
-
- let data: &[u8] = match (const_val_val, &const_val.ty.kind) {
- (ConstValue::ByRef { offset, alloc, .. }, ty::Array(t, n)) => {
- assert_eq!(*t, tcx.types.u8);
- let n = n.eval_usize(tcx, param_env);
- let ptr = Pointer::new(AllocId(0), offset);
- alloc.get_bytes(&tcx, ptr, Size::from_bytes(n)).unwrap()
- }
- (ConstValue::Slice { data, start, end }, ty::Slice(t)) => {
- assert_eq!(*t, tcx.types.u8);
- let ptr = Pointer::new(AllocId(0), Size::from_bytes(start as u64));
- data.get_bytes(&tcx, ptr, Size::from_bytes((end - start) as u64)).unwrap()
- }
- // FIXME(oli-obk): create a way to extract fat pointers from ByRef
- (_, ty::Slice(_)) => return Ok(false),
- _ => bug!(
- "slice_pat_covered_by_const: {:#?}, {:#?}, {:#?}, {:#?}",
- const_val,
- prefix,
- slice,
- suffix,
- ),
- };
-
- let pat_len = prefix.len() + suffix.len();
- if data.len() < pat_len || (slice.is_none() && data.len() > pat_len) {
- return Ok(false);
- }
-
- for (ch, pat) in data[..prefix.len()]
- .iter()
- .zip(prefix)
- .chain(data[data.len() - suffix.len()..].iter().zip(suffix))
- {
- match pat.kind {
- box PatKind::Constant { value } => {
- let b = value.eval_bits(tcx, param_env, pat.ty);
- assert_eq!(b as u8 as u128, b);
- if b as u8 != *ch {
- return Ok(false);
- }
- }
- _ => {}
- }
- }
-
- Ok(true)
-}
-
-/// For exhaustive integer matching, some constructors are grouped within other constructors
-/// (namely integer typed values are grouped within ranges). However, when specialising these
-/// constructors, we want to be specialising for the underlying constructors (the integers), not
-/// the groups (the ranges). Thus we need to split the groups up. Splitting them up naïvely would
-/// mean creating a separate constructor for every single value in the range, which is clearly
-/// impractical. However, observe that for some ranges of integers, the specialisation will be
-/// identical across all values in that range (i.e., there are equivalence classes of ranges of
-/// constructors based on their `is_useful_specialized` outcome). These classes are grouped by
-/// the patterns that apply to them (in the matrix `P`). We can split the range whenever the
-/// patterns that apply to that range (specifically: the patterns that *intersect* with that range)
-/// change.
-/// Our solution, therefore, is to split the range constructor into subranges at every single point
-/// the group of intersecting patterns changes (using the method described below).
-/// And voilà! We're testing precisely those ranges that we need to, without any exhaustive matching
-/// on actual integers. The nice thing about this is that the number of subranges is linear in the
-/// number of rows in the matrix (i.e., the number of cases in the `match` statement), so we don't
-/// need to be worried about matching over gargantuan ranges.
-///
-/// Essentially, given the first column of a matrix representing ranges, looking like the following:
-///
-/// |------| |----------| |-------| ||
-/// |-------| |-------| |----| ||
-/// |---------|
-///
-/// We split the ranges up into equivalence classes so the ranges are no longer overlapping:
-///
-/// |--|--|||-||||--||---|||-------| |-|||| ||
-///
-/// The logic for determining how to split the ranges is fairly straightforward: we calculate
-/// boundaries for each interval range, sort them, then create constructors for each new interval
-/// between every pair of boundary points. (This essentially sums up to performing the intuitive
-/// merging operation depicted above.)
-///
-/// `hir_id` is `None` when we're evaluating the wildcard pattern, do not lint for overlapping in
-/// ranges that case.
-///
-/// This also splits variable-length slices into fixed-length slices.
-fn split_grouped_constructors<'p, 'tcx>(
- tcx: TyCtxt<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- pcx: PatCtxt<'tcx>,
- ctors: Vec<Constructor<'tcx>>,
- matrix: &Matrix<'p, 'tcx>,
- span: Span,
- hir_id: Option<HirId>,
-) -> Vec<Constructor<'tcx>> {
- let ty = pcx.ty;
- let mut split_ctors = Vec::with_capacity(ctors.len());
- debug!("split_grouped_constructors({:#?}, {:#?})", matrix, ctors);
-
- for ctor in ctors.into_iter() {
- match ctor {
- IntRange(ctor_range) if ctor_range.treat_exhaustively(tcx) => {
- // Fast-track if the range is trivial. In particular, don't do the overlapping
- // ranges check.
- if ctor_range.is_singleton() {
- split_ctors.push(IntRange(ctor_range));
- continue;
- }
-
- /// Represents a border between 2 integers. Because the intervals spanning borders
- /// must be able to cover every integer, we need to be able to represent
- /// 2^128 + 1 such borders.
- #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
- enum Border {
- JustBefore(u128),
- AfterMax,
- }
-
- // A function for extracting the borders of an integer interval.
- fn range_borders(r: IntRange<'_>) -> impl Iterator<Item = Border> {
- let (lo, hi) = r.range.into_inner();
- let from = Border::JustBefore(lo);
- let to = match hi.checked_add(1) {
- Some(m) => Border::JustBefore(m),
- None => Border::AfterMax,
- };
- vec![from, to].into_iter()
- }
-
- // Collect the span and range of all the intersecting ranges to lint on likely
- // incorrect range patterns. (#63987)
- let mut overlaps = vec![];
- // `borders` is the set of borders between equivalence classes: each equivalence
- // class lies between 2 borders.
- let row_borders = matrix
- .0
- .iter()
- .flat_map(|row| {
- IntRange::from_pat(tcx, param_env, row.head()).map(|r| (r, row.len()))
- })
- .flat_map(|(range, row_len)| {
- let intersection = ctor_range.intersection(tcx, &range);
- let should_lint = ctor_range.suspicious_intersection(&range);
- if let (Some(range), 1, true) = (&intersection, row_len, should_lint) {
- // FIXME: for now, only check for overlapping ranges on simple range
- // patterns. Otherwise with the current logic the following is detected
- // as overlapping:
- // match (10u8, true) {
- // (0 ..= 125, false) => {}
- // (126 ..= 255, false) => {}
- // (0 ..= 255, true) => {}
- // }
- overlaps.push(range.clone());
- }
- intersection
- })
- .flat_map(|range| range_borders(range));
- let ctor_borders = range_borders(ctor_range.clone());
- let mut borders: Vec<_> = row_borders.chain(ctor_borders).collect();
- borders.sort_unstable();
-
- lint_overlapping_patterns(tcx, hir_id, ctor_range, ty, overlaps);
-
- // We're going to iterate through every adjacent pair of borders, making sure that
- // each represents an interval of nonnegative length, and convert each such
- // interval into a constructor.
- split_ctors.extend(
- borders
- .windows(2)
- .filter_map(|window| match (window[0], window[1]) {
- (Border::JustBefore(n), Border::JustBefore(m)) => {
- if n < m {
- Some(IntRange { range: n..=(m - 1), ty, span })
- } else {
- None
- }
- }
- (Border::JustBefore(n), Border::AfterMax) => {
- Some(IntRange { range: n..=u128::MAX, ty, span })
- }
- (Border::AfterMax, _) => None,
- })
- .map(IntRange),
- );
- }
- Slice(Slice { array_len, kind: VarLen(self_prefix, self_suffix) }) => {
- // The exhaustiveness-checking paper does not include any details on
- // checking variable-length slice patterns. However, they are matched
- // by an infinite collection of fixed-length array patterns.
- //
- // Checking the infinite set directly would take an infinite amount
- // of time. However, it turns out that for each finite set of
- // patterns `P`, all sufficiently large array lengths are equivalent:
- //
- // Each slice `s` with a "sufficiently-large" length `l ≥ L` that applies
- // to exactly the subset `Pₜ` of `P` can be transformed to a slice
- // `sₘ` for each sufficiently-large length `m` that applies to exactly
- // the same subset of `P`.
- //
- // Because of that, each witness for reachability-checking from one
- // of the sufficiently-large lengths can be transformed to an
- // equally-valid witness from any other length, so we only have
- // to check slice lengths from the "minimal sufficiently-large length"
- // and below.
- //
- // Note that the fact that there is a *single* `sₘ` for each `m`
- // not depending on the specific pattern in `P` is important: if
- // you look at the pair of patterns
- // `[true, ..]`
- // `[.., false]`
- // Then any slice of length ≥1 that matches one of these two
- // patterns can be trivially turned to a slice of any
- // other length ≥1 that matches them and vice-versa - for
- // but the slice from length 2 `[false, true]` that matches neither
- // of these patterns can't be turned to a slice from length 1 that
- // matches neither of these patterns, so we have to consider
- // slices from length 2 there.
- //
- // Now, to see that that length exists and find it, observe that slice
- // patterns are either "fixed-length" patterns (`[_, _, _]`) or
- // "variable-length" patterns (`[_, .., _]`).
- //
- // For fixed-length patterns, all slices with lengths *longer* than
- // the pattern's length have the same outcome (of not matching), so
- // as long as `L` is greater than the pattern's length we can pick
- // any `sₘ` from that length and get the same result.
- //
- // For variable-length patterns, the situation is more complicated,
- // because as seen above the precise value of `sₘ` matters.
- //
- // However, for each variable-length pattern `p` with a prefix of length
- // `plₚ` and suffix of length `slₚ`, only the first `plₚ` and the last
- // `slₚ` elements are examined.
- //
- // Therefore, as long as `L` is positive (to avoid concerns about empty
- // types), all elements after the maximum prefix length and before
- // the maximum suffix length are not examined by any variable-length
- // pattern, and therefore can be added/removed without affecting
- // them - creating equivalent patterns from any sufficiently-large
- // length.
- //
- // Of course, if fixed-length patterns exist, we must be sure
- // that our length is large enough to miss them all, so
- // we can pick `L = max(max(FIXED_LEN)+1, max(PREFIX_LEN) + max(SUFFIX_LEN))`
- //
- // for example, with the above pair of patterns, all elements
- // but the first and last can be added/removed, so any
- // witness of length ≥2 (say, `[false, false, true]`) can be
- // turned to a witness from any other length ≥2.
-
- let mut max_prefix_len = self_prefix;
- let mut max_suffix_len = self_suffix;
- let mut max_fixed_len = 0;
-
- let head_ctors =
- matrix.heads().filter_map(|pat| pat_constructor(tcx, param_env, pat));
- for ctor in head_ctors {
- match ctor {
- Slice(slice) => match slice.pattern_kind() {
- FixedLen(len) => {
- max_fixed_len = cmp::max(max_fixed_len, len);
- }
- VarLen(prefix, suffix) => {
- max_prefix_len = cmp::max(max_prefix_len, prefix);
- max_suffix_len = cmp::max(max_suffix_len, suffix);
- }
- },
- _ => {}
- }
- }
-
- // For diagnostics, we keep the prefix and suffix lengths separate, so in the case
- // where `max_fixed_len + 1` is the largest, we adapt `max_prefix_len` accordingly,
- // so that `L = max_prefix_len + max_suffix_len`.
- if max_fixed_len + 1 >= max_prefix_len + max_suffix_len {
- // The subtraction can't overflow thanks to the above check.
- // The new `max_prefix_len` is also guaranteed to be larger than its previous
- // value.
- max_prefix_len = max_fixed_len + 1 - max_suffix_len;
- }
-
- match array_len {
- Some(len) => {
- let kind = if max_prefix_len + max_suffix_len < len {
- VarLen(max_prefix_len, max_suffix_len)
- } else {
- FixedLen(len)
- };
- split_ctors.push(Slice(Slice { array_len, kind }));
- }
- None => {
- // `ctor` originally covered the range `(self_prefix +
- // self_suffix..infinity)`. We now split it into two: lengths smaller than
- // `max_prefix_len + max_suffix_len` are treated independently as
- // fixed-lengths slices, and lengths above are captured by a final VarLen
- // constructor.
- split_ctors.extend(
- (self_prefix + self_suffix..max_prefix_len + max_suffix_len)
- .map(|len| Slice(Slice { array_len, kind: FixedLen(len) })),
- );
- split_ctors.push(Slice(Slice {
- array_len,
- kind: VarLen(max_prefix_len, max_suffix_len),
- }));
- }
- }
- }
- // Any other constructor can be used unchanged.
- _ => split_ctors.push(ctor),
- }
- }
-
- debug!("split_grouped_constructors(..)={:#?}", split_ctors);
- split_ctors
-}
-
-fn lint_overlapping_patterns(
- tcx: TyCtxt<'tcx>,
- hir_id: Option<HirId>,
- ctor_range: IntRange<'tcx>,
- ty: Ty<'tcx>,
- overlaps: Vec<IntRange<'tcx>>,
-) {
- if let (true, Some(hir_id)) = (!overlaps.is_empty(), hir_id) {
- let mut err = tcx.struct_span_lint_hir(
- lint::builtin::OVERLAPPING_PATTERNS,
- hir_id,
- ctor_range.span,
- "multiple patterns covering the same range",
- );
- err.span_label(ctor_range.span, "overlapping patterns");
- for int_range in overlaps {
- // Use the real type for user display of the ranges:
- err.span_label(
- int_range.span,
- &format!(
- "this range overlaps on `{}`",
- IntRange { range: int_range.range, ty, span: DUMMY_SP }.to_pat(tcx),
- ),
- );
- }
- err.emit();
- }
-}
-
-fn constructor_covered_by_range<'tcx>(
- tcx: TyCtxt<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- ctor: &Constructor<'tcx>,
- pat: &Pat<'tcx>,
-) -> Option<()> {
- if let Single = ctor {
- return Some(());
- }
-
- let (pat_from, pat_to, pat_end, ty) = match *pat.kind {
- PatKind::Constant { value } => (value, value, RangeEnd::Included, value.ty),
- PatKind::Range(PatRange { lo, hi, end }) => (lo, hi, end, lo.ty),
- _ => bug!("`constructor_covered_by_range` called with {:?}", pat),
- };
- let (ctor_from, ctor_to, ctor_end) = match *ctor {
- ConstantValue(value) => (value, value, RangeEnd::Included),
- FloatRange(from, to, ctor_end) => (from, to, ctor_end),
- _ => bug!("`constructor_covered_by_range` called with {:?}", ctor),
- };
- trace!("constructor_covered_by_range {:#?}, {:#?}, {:#?}, {}", ctor, pat_from, pat_to, ty);
-
- let to = compare_const_vals(tcx, ctor_to, pat_to, param_env, ty)?;
- let from = compare_const_vals(tcx, ctor_from, pat_from, param_env, ty)?;
- let intersects = (from == Ordering::Greater || from == Ordering::Equal)
- && (to == Ordering::Less || (pat_end == ctor_end && to == Ordering::Equal));
- if intersects { Some(()) } else { None }
-}
-
-fn patterns_for_variant<'p, 'tcx>(
- cx: &mut MatchCheckCtxt<'p, 'tcx>,
- subpatterns: &'p [FieldPat<'tcx>],
- ctor_wild_subpatterns: &'p [Pat<'tcx>],
- is_non_exhaustive: bool,
-) -> PatStack<'p, 'tcx> {
- let mut result: SmallVec<_> = ctor_wild_subpatterns.iter().collect();
-
- for subpat in subpatterns {
- if !is_non_exhaustive || !cx.is_uninhabited(subpat.pattern.ty) {
- result[subpat.field.index()] = &subpat.pattern;
- }
- }
-
- debug!(
- "patterns_for_variant({:#?}, {:#?}) = {:#?}",
- subpatterns, ctor_wild_subpatterns, result
- );
- PatStack::from_vec(result)
-}
-
-/// This is the main specialization step. It expands the pattern
-/// into `arity` patterns based on the constructor. For most patterns, the step is trivial,
-/// for instance tuple patterns are flattened and box patterns expand into their inner pattern.
-/// Returns `None` if the pattern does not have the given constructor.
-///
-/// OTOH, slice patterns with a subslice pattern (tail @ ..) can be expanded into multiple
-/// different patterns.
-/// Structure patterns with a partial wild pattern (Foo { a: 42, .. }) have their missing
-/// fields filled with wild patterns.
-fn specialize_one_pattern<'p, 'tcx>(
- cx: &mut MatchCheckCtxt<'p, 'tcx>,
- pat: &'p Pat<'tcx>,
- constructor: &Constructor<'tcx>,
- ctor_wild_subpatterns: &'p [Pat<'tcx>],
-) -> Option<PatStack<'p, 'tcx>> {
- if let NonExhaustive = constructor {
- // Only a wildcard pattern can match the special extra constructor
- return if pat.is_wildcard() { Some(PatStack::default()) } else { None };
- }
-
- let result = match *pat.kind {
- PatKind::AscribeUserType { .. } => bug!(), // Handled by `expand_pattern`
-
- PatKind::Binding { .. } | PatKind::Wild => Some(ctor_wild_subpatterns.iter().collect()),
-
- PatKind::Variant { adt_def, variant_index, ref subpatterns, .. } => {
- let ref variant = adt_def.variants[variant_index];
- let is_non_exhaustive = cx.is_foreign_non_exhaustive_variant(pat.ty, variant);
- Some(Variant(variant.def_id))
- .filter(|variant_constructor| variant_constructor == constructor)
- .map(|_| {
- patterns_for_variant(cx, subpatterns, ctor_wild_subpatterns, is_non_exhaustive)
- })
- }
-
- PatKind::Leaf { ref subpatterns } => {
- Some(patterns_for_variant(cx, subpatterns, ctor_wild_subpatterns, false))
- }
-
- PatKind::Deref { ref subpattern } => Some(PatStack::from_pattern(subpattern)),
-
- PatKind::Constant { value } if constructor.is_slice() => {
- // We extract an `Option` for the pointer because slices of zero
- // elements don't necessarily point to memory, they are usually
- // just integers. The only time they should be pointing to memory
- // is when they are subslices of nonzero slices.
- let (alloc, offset, n, ty) = match value.ty.kind {
- ty::Array(t, n) => {
- let n = n.eval_usize(cx.tcx, cx.param_env);
- // Shortcut for `n == 0` where no matter what `alloc` and `offset` we produce,
- // the result would be exactly what we early return here.
- if n == 0 {
- if ctor_wild_subpatterns.len() as u64 == 0 {
- return Some(PatStack::from_slice(&[]));
- } else {
- return None;
- }
- }
- match value.val {
- ty::ConstKind::Value(ConstValue::ByRef { offset, alloc, .. }) => {
- (Cow::Borrowed(alloc), offset, n, t)
- }
- _ => span_bug!(pat.span, "array pattern is {:?}", value,),
- }
- }
- ty::Slice(t) => {
- match value.val {
- ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => {
- let offset = Size::from_bytes(start as u64);
- let n = (end - start) as u64;
- (Cow::Borrowed(data), offset, n, t)
- }
- ty::ConstKind::Value(ConstValue::ByRef { .. }) => {
- // FIXME(oli-obk): implement `deref` for `ConstValue`
- return None;
- }
- _ => span_bug!(
- pat.span,
- "slice pattern constant must be scalar pair but is {:?}",
- value,
- ),
- }
- }
- _ => span_bug!(
- pat.span,
- "unexpected const-val {:?} with ctor {:?}",
- value,
- constructor,
- ),
- };
- if ctor_wild_subpatterns.len() as u64 == n {
- // convert a constant slice/array pattern to a list of patterns.
- let layout = cx.tcx.layout_of(cx.param_env.and(ty)).ok()?;
- let ptr = Pointer::new(AllocId(0), offset);
- (0..n)
- .map(|i| {
- let ptr = ptr.offset(layout.size * i, &cx.tcx).ok()?;
- let scalar = alloc.read_scalar(&cx.tcx, ptr, layout.size).ok()?;
- let scalar = scalar.not_undef().ok()?;
- let value = ty::Const::from_scalar(cx.tcx, scalar, ty);
- let pattern =
- Pat { ty, span: pat.span, kind: box PatKind::Constant { value } };
- Some(&*cx.pattern_arena.alloc(pattern))
- })
- .collect()
- } else {
- None
- }
- }
-
- PatKind::Constant { .. } | PatKind::Range { .. } => {
- // If the constructor is a:
- // - Single value: add a row if the pattern contains the constructor.
- // - Range: add a row if the constructor intersects the pattern.
- if let IntRange(ctor) = constructor {
- match IntRange::from_pat(cx.tcx, cx.param_env, pat) {
- Some(pat) => ctor.intersection(cx.tcx, &pat).map(|_| {
- // Constructor splitting should ensure that all intersections we encounter
- // are actually inclusions.
- assert!(ctor.is_subrange(&pat));
- PatStack::default()
- }),
- _ => None,
- }
- } else {
- // Fallback for non-ranges and ranges that involve
- // floating-point numbers, which are not conveniently handled
- // by `IntRange`. For these cases, the constructor may not be a
- // range so intersection actually devolves into being covered
- // by the pattern.
- constructor_covered_by_range(cx.tcx, cx.param_env, constructor, pat)
- .map(|()| PatStack::default())
- }
- }
-
- PatKind::Array { ref prefix, ref slice, ref suffix }
- | PatKind::Slice { ref prefix, ref slice, ref suffix } => match *constructor {
- Slice(_) => {
- let pat_len = prefix.len() + suffix.len();
- if let Some(slice_count) = ctor_wild_subpatterns.len().checked_sub(pat_len) {
- if slice_count == 0 || slice.is_some() {
- Some(
- prefix
- .iter()
- .chain(
- ctor_wild_subpatterns
- .iter()
- .skip(prefix.len())
- .take(slice_count)
- .chain(suffix.iter()),
- )
- .collect(),
- )
- } else {
- None
- }
- } else {
- None
- }
- }
- ConstantValue(cv) => {
- match slice_pat_covered_by_const(
- cx.tcx,
- pat.span,
- cv,
- prefix,
- slice,
- suffix,
- cx.param_env,
- ) {
- Ok(true) => Some(PatStack::default()),
- Ok(false) => None,
- Err(ErrorReported) => None,
- }
- }
- _ => span_bug!(pat.span, "unexpected ctor {:?} for slice pat", constructor),
- },
-
- PatKind::Or { .. } => bug!("Or-pattern should have been expanded earlier on."),
- };
- debug!("specialize({:#?}, {:#?}) = {:#?}", pat, ctor_wild_subpatterns, result);
-
- result
-}
+++ /dev/null
-use super::_match::Usefulness::*;
-use super::_match::WitnessPreference::*;
-use super::_match::{expand_pattern, is_useful, MatchCheckCtxt, Matrix, PatStack};
-
-use super::{PatCtxt, PatKind, PatternError};
-
-use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor};
-use rustc::lint;
-use rustc::session::Session;
-use rustc::ty::subst::{InternalSubsts, SubstsRef};
-use rustc::ty::{self, Ty, TyCtxt};
-use rustc_error_codes::*;
-use rustc_errors::{Applicability, DiagnosticBuilder};
-use rustc_hir as hir;
-use rustc_hir::def::*;
-use rustc_hir::def_id::DefId;
-use rustc_hir::{HirId, Pat};
-use rustc_span::symbol::sym;
-use rustc_span::{MultiSpan, Span};
-use syntax::ast::Mutability;
-use syntax::feature_gate::feature_err;
-
-use std::slice;
-
-crate fn check_match(tcx: TyCtxt<'_>, def_id: DefId) {
- let body_id = match tcx.hir().as_local_hir_id(def_id) {
- None => return,
- Some(id) => tcx.hir().body_owned_by(id),
- };
-
- let mut visitor = MatchVisitor {
- tcx,
- tables: tcx.body_tables(body_id),
- param_env: tcx.param_env(def_id),
- identity_substs: InternalSubsts::identity_for_item(tcx, def_id),
- };
- visitor.visit_body(tcx.hir().body(body_id));
-}
-
-fn create_e0004(sess: &Session, sp: Span, error_message: String) -> DiagnosticBuilder<'_> {
- struct_span_err!(sess, sp, E0004, "{}", &error_message)
-}
-
-struct MatchVisitor<'a, 'tcx> {
- tcx: TyCtxt<'tcx>,
- tables: &'a ty::TypeckTables<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- identity_substs: SubstsRef<'tcx>,
-}
-
-impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, 'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
- NestedVisitorMap::None
- }
-
- fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) {
- intravisit::walk_expr(self, ex);
-
- if let hir::ExprKind::Match(ref scrut, ref arms, source) = ex.kind {
- self.check_match(scrut, arms, source);
- }
- }
-
- fn visit_local(&mut self, loc: &'tcx hir::Local<'tcx>) {
- intravisit::walk_local(self, loc);
-
- let (msg, sp) = match loc.source {
- hir::LocalSource::Normal => ("local binding", Some(loc.span)),
- hir::LocalSource::ForLoopDesugar => ("`for` loop binding", None),
- hir::LocalSource::AsyncFn => ("async fn binding", None),
- hir::LocalSource::AwaitDesugar => ("`await` future binding", None),
- };
- self.check_irrefutable(&loc.pat, msg, sp);
-
- // Check legality of move bindings and `@` patterns.
- self.check_patterns(false, &loc.pat);
- }
-
- fn visit_body(&mut self, body: &'tcx hir::Body<'tcx>) {
- intravisit::walk_body(self, body);
-
- for param in body.params {
- self.check_irrefutable(¶m.pat, "function argument", None);
- self.check_patterns(false, ¶m.pat);
- }
- }
-}
-
-impl PatCtxt<'_, '_> {
- fn report_inlining_errors(&self, pat_span: Span) {
- for error in &self.errors {
- match *error {
- PatternError::StaticInPattern(span) => {
- self.span_e0158(span, "statics cannot be referenced in patterns")
- }
- PatternError::AssocConstInPattern(span) => {
- self.span_e0158(span, "associated consts cannot be referenced in patterns")
- }
- PatternError::FloatBug => {
- // FIXME(#31407) this is only necessary because float parsing is buggy
- ::rustc::mir::interpret::struct_error(
- self.tcx.at(pat_span),
- "could not evaluate float literal (see issue #31407)",
- )
- .emit();
- }
- PatternError::NonConstPath(span) => {
- ::rustc::mir::interpret::struct_error(
- self.tcx.at(span),
- "runtime values cannot be referenced in patterns",
- )
- .emit();
- }
- }
- }
- }
-
- fn span_e0158(&self, span: Span, text: &str) {
- span_err!(self.tcx.sess, span, E0158, "{}", text)
- }
-}
-
-impl<'tcx> MatchVisitor<'_, 'tcx> {
- fn check_patterns(&mut self, has_guard: bool, pat: &Pat<'_>) {
- check_legality_of_move_bindings(self, has_guard, pat);
- check_borrow_conflicts_in_at_patterns(self, pat);
- if !self.tcx.features().bindings_after_at {
- check_legality_of_bindings_in_at_patterns(self, pat);
- }
- }
-
- fn check_match(
- &mut self,
- scrut: &hir::Expr<'_>,
- arms: &'tcx [hir::Arm<'tcx>],
- source: hir::MatchSource,
- ) {
- for arm in arms {
- // First, check legality of move bindings.
- self.check_patterns(arm.guard.is_some(), &arm.pat);
-
- // Second, perform some lints.
- check_for_bindings_named_same_as_variants(self, &arm.pat);
- }
-
- let module = self.tcx.hir().get_module_parent(scrut.hir_id);
- MatchCheckCtxt::create_and_enter(self.tcx, self.param_env, module, |ref mut cx| {
- let mut have_errors = false;
-
- let inlined_arms: Vec<_> = arms
- .iter()
- .map(|arm| {
- let mut patcx = PatCtxt::new(
- self.tcx,
- self.param_env.and(self.identity_substs),
- self.tables,
- );
- patcx.include_lint_checks();
- let pattern = patcx.lower_pattern(&arm.pat);
- let pattern: &_ = cx.pattern_arena.alloc(expand_pattern(cx, pattern));
- if !patcx.errors.is_empty() {
- patcx.report_inlining_errors(arm.pat.span);
- have_errors = true;
- }
- (pattern, &*arm.pat, arm.guard.is_some())
- })
- .collect();
-
- // Bail out early if inlining failed.
- if have_errors {
- return;
- }
-
- // Fourth, check for unreachable arms.
- let matrix = check_arms(cx, &inlined_arms, source);
-
- // Fifth, check if the match is exhaustive.
- let scrut_ty = self.tables.node_type(scrut.hir_id);
- // Note: An empty match isn't the same as an empty matrix for diagnostics purposes,
- // since an empty matrix can occur when there are arms, if those arms all have guards.
- let is_empty_match = inlined_arms.is_empty();
- check_exhaustive(cx, scrut_ty, scrut.span, &matrix, scrut.hir_id, is_empty_match);
- })
- }
-
- fn check_irrefutable(&self, pat: &'tcx Pat<'tcx>, origin: &str, sp: Option<Span>) {
- let module = self.tcx.hir().get_module_parent(pat.hir_id);
- MatchCheckCtxt::create_and_enter(self.tcx, self.param_env, module, |ref mut cx| {
- let mut patcx =
- PatCtxt::new(self.tcx, self.param_env.and(self.identity_substs), self.tables);
- patcx.include_lint_checks();
- let pattern = patcx.lower_pattern(pat);
- let pattern_ty = pattern.ty;
- let pattern = cx.pattern_arena.alloc(expand_pattern(cx, pattern));
- let pats: Matrix<'_, '_> = vec![PatStack::from_pattern(pattern)].into_iter().collect();
-
- let witnesses = match check_not_useful(cx, pattern_ty, &pats, pat.hir_id) {
- Ok(_) => return,
- Err(err) => err,
- };
-
- let joined_patterns = joined_uncovered_patterns(&witnesses);
- let mut err = struct_span_err!(
- self.tcx.sess,
- pat.span,
- E0005,
- "refutable pattern in {}: {} not covered",
- origin,
- joined_patterns
- );
- let suggest_if_let = match &pat.kind {
- hir::PatKind::Path(hir::QPath::Resolved(None, path))
- if path.segments.len() == 1 && path.segments[0].args.is_none() =>
- {
- const_not_var(&mut err, cx.tcx, pat, path);
- false
- }
- _ => {
- err.span_label(
- pat.span,
- pattern_not_covered_label(&witnesses, &joined_patterns),
- );
- true
- }
- };
-
- if let (Some(span), true) = (sp, suggest_if_let) {
- err.note(
- "`let` bindings require an \"irrefutable pattern\", like a `struct` or \
- an `enum` with only one variant",
- );
- if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
- err.span_suggestion(
- span,
- "you might want to use `if let` to ignore the variant that isn't matched",
- format!("if {} {{ /* */ }}", &snippet[..snippet.len() - 1]),
- Applicability::HasPlaceholders,
- );
- }
- err.note(
- "for more information, visit \
- https://doc.rust-lang.org/book/ch18-02-refutability.html",
- );
- }
-
- adt_defined_here(cx, &mut err, pattern_ty, &witnesses);
- err.emit();
- });
- }
-}
-
-/// A path pattern was interpreted as a constant, not a new variable.
-/// This caused an irrefutable match failure in e.g. `let`.
-fn const_not_var(
- err: &mut DiagnosticBuilder<'_>,
- tcx: TyCtxt<'_>,
- pat: &Pat<'_>,
- path: &hir::Path<'_>,
-) {
- let descr = path.res.descr();
- err.span_label(
- pat.span,
- format!("interpreted as {} {} pattern, not a new variable", path.res.article(), descr,),
- );
-
- err.span_suggestion(
- pat.span,
- "introduce a variable instead",
- format!("{}_var", path.segments[0].ident).to_lowercase(),
- // Cannot use `MachineApplicable` as it's not really *always* correct
- // because there may be such an identifier in scope or the user maybe
- // really wanted to match against the constant. This is quite unlikely however.
- Applicability::MaybeIncorrect,
- );
-
- if let Some(span) = tcx.hir().res_span(path.res) {
- err.span_label(span, format!("{} defined here", descr));
- }
-}
-
-fn check_for_bindings_named_same_as_variants(cx: &MatchVisitor<'_, '_>, pat: &Pat<'_>) {
- pat.walk_always(|p| {
- if let hir::PatKind::Binding(_, _, ident, None) = p.kind {
- if let Some(ty::BindByValue(hir::Mutability::Not)) =
- cx.tables.extract_binding_mode(cx.tcx.sess, p.hir_id, p.span)
- {
- let pat_ty = cx.tables.pat_ty(p).peel_refs();
- if let ty::Adt(edef, _) = pat_ty.kind {
- if edef.is_enum()
- && edef.variants.iter().any(|variant| {
- variant.ident == ident && variant.ctor_kind == CtorKind::Const
- })
- {
- // FIXME(Centril): Should be a lint?
- let ty_path = cx.tcx.def_path_str(edef.did);
- let mut err = struct_span_warn!(
- cx.tcx.sess,
- p.span,
- E0170,
- "pattern binding `{}` is named the same as one \
- of the variants of the type `{}`",
- ident,
- ty_path
- );
- err.span_suggestion(
- p.span,
- "to match on the variant, qualify the path",
- format!("{}::{}", ty_path, ident),
- Applicability::MachineApplicable,
- );
- err.emit();
- }
- }
- }
- }
- });
-}
-
-/// Checks for common cases of "catchall" patterns that may not be intended as such.
-fn pat_is_catchall(pat: &Pat<'_>) -> bool {
- match pat.kind {
- hir::PatKind::Binding(.., None) => true,
- hir::PatKind::Binding(.., Some(ref s)) => pat_is_catchall(s),
- hir::PatKind::Ref(ref s, _) => pat_is_catchall(s),
- hir::PatKind::Tuple(ref v, _) => v.iter().all(|p| pat_is_catchall(&p)),
- _ => false,
- }
-}
-
-/// Check for unreachable patterns.
-fn check_arms<'p, 'tcx>(
- cx: &mut MatchCheckCtxt<'p, 'tcx>,
- arms: &[(&'p super::Pat<'tcx>, &hir::Pat<'_>, bool)],
- source: hir::MatchSource,
-) -> Matrix<'p, 'tcx> {
- let mut seen = Matrix::empty();
- let mut catchall = None;
- for (arm_index, (pat, hir_pat, has_guard)) in arms.iter().enumerate() {
- let v = PatStack::from_pattern(pat);
-
- match is_useful(cx, &seen, &v, LeaveOutWitness, hir_pat.hir_id, true) {
- NotUseful => {
- match source {
- hir::MatchSource::IfDesugar { .. } | hir::MatchSource::WhileDesugar => bug!(),
-
- hir::MatchSource::IfLetDesugar { .. } | hir::MatchSource::WhileLetDesugar => {
- // check which arm we're on.
- match arm_index {
- // The arm with the user-specified pattern.
- 0 => {
- cx.tcx.lint_hir(
- lint::builtin::UNREACHABLE_PATTERNS,
- hir_pat.hir_id,
- pat.span,
- "unreachable pattern",
- );
- }
- // The arm with the wildcard pattern.
- 1 => {
- let msg = match source {
- hir::MatchSource::IfLetDesugar { .. } => {
- "irrefutable if-let pattern"
- }
- hir::MatchSource::WhileLetDesugar => {
- "irrefutable while-let pattern"
- }
- _ => bug!(),
- };
- cx.tcx.lint_hir(
- lint::builtin::IRREFUTABLE_LET_PATTERNS,
- hir_pat.hir_id,
- pat.span,
- msg,
- );
- }
- _ => bug!(),
- }
- }
-
- hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => {
- let mut err = cx.tcx.struct_span_lint_hir(
- lint::builtin::UNREACHABLE_PATTERNS,
- hir_pat.hir_id,
- pat.span,
- "unreachable pattern",
- );
- // if we had a catchall pattern, hint at that
- if let Some(catchall) = catchall {
- err.span_label(pat.span, "unreachable pattern");
- err.span_label(catchall, "matches any value");
- }
- err.emit();
- }
-
- // Unreachable patterns in try and await expressions occur when one of
- // the arms are an uninhabited type. Which is OK.
- hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar => {}
- }
- }
- Useful(unreachable_subpatterns) => {
- for pat in unreachable_subpatterns {
- cx.tcx.lint_hir(
- lint::builtin::UNREACHABLE_PATTERNS,
- hir_pat.hir_id,
- pat.span,
- "unreachable pattern",
- );
- }
- }
- UsefulWithWitness(_) => bug!(),
- }
- if !has_guard {
- seen.push(v);
- if catchall.is_none() && pat_is_catchall(hir_pat) {
- catchall = Some(pat.span);
- }
- }
- }
- seen
-}
-
-fn check_not_useful<'p, 'tcx>(
- cx: &mut MatchCheckCtxt<'p, 'tcx>,
- ty: Ty<'tcx>,
- matrix: &Matrix<'p, 'tcx>,
- hir_id: HirId,
-) -> Result<(), Vec<super::Pat<'tcx>>> {
- let wild_pattern = cx.pattern_arena.alloc(super::Pat::wildcard_from_ty(ty));
- let v = PatStack::from_pattern(wild_pattern);
- match is_useful(cx, matrix, &v, ConstructWitness, hir_id, true) {
- NotUseful => Ok(()), // This is good, wildcard pattern isn't reachable.
- UsefulWithWitness(pats) => Err(if pats.is_empty() {
- bug!("Exhaustiveness check returned no witnesses")
- } else {
- pats.into_iter().map(|w| w.single_pattern()).collect()
- }),
- Useful(_) => bug!(),
- }
-}
-
-fn check_exhaustive<'p, 'tcx>(
- cx: &mut MatchCheckCtxt<'p, 'tcx>,
- scrut_ty: Ty<'tcx>,
- sp: Span,
- matrix: &Matrix<'p, 'tcx>,
- hir_id: HirId,
- is_empty_match: bool,
-) {
- // In the absence of the `exhaustive_patterns` feature, empty matches are not detected by
- // `is_useful` to exhaustively match uninhabited types, so we manually check here.
- if is_empty_match && !cx.tcx.features().exhaustive_patterns {
- let scrutinee_is_visibly_uninhabited = match scrut_ty.kind {
- ty::Never => true,
- ty::Adt(def, _) => {
- def.is_enum()
- && def.variants.is_empty()
- && !cx.is_foreign_non_exhaustive_enum(scrut_ty)
- }
- _ => false,
- };
- if scrutinee_is_visibly_uninhabited {
- // If the type *is* uninhabited, an empty match is vacuously exhaustive.
- return;
- }
- }
-
- let witnesses = match check_not_useful(cx, scrut_ty, matrix, hir_id) {
- Ok(_) => return,
- Err(err) => err,
- };
-
- let non_empty_enum = match scrut_ty.kind {
- ty::Adt(def, _) => def.is_enum() && !def.variants.is_empty(),
- _ => false,
- };
- // In the case of an empty match, replace the '`_` not covered' diagnostic with something more
- // informative.
- let mut err;
- if is_empty_match && !non_empty_enum {
- err = create_e0004(
- cx.tcx.sess,
- sp,
- format!("non-exhaustive patterns: type `{}` is non-empty", scrut_ty),
- );
- } else {
- let joined_patterns = joined_uncovered_patterns(&witnesses);
- err = create_e0004(
- cx.tcx.sess,
- sp,
- format!("non-exhaustive patterns: {} not covered", joined_patterns),
- );
- err.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns));
- };
-
- adt_defined_here(cx, &mut err, scrut_ty, &witnesses);
- err.help(
- "ensure that all possible cases are being handled, \
- possibly by adding wildcards or more match arms",
- );
- err.emit();
-}
-
-fn joined_uncovered_patterns(witnesses: &[super::Pat<'_>]) -> String {
- const LIMIT: usize = 3;
- match witnesses {
- [] => bug!(),
- [witness] => format!("`{}`", witness),
- [head @ .., tail] if head.len() < LIMIT => {
- let head: Vec<_> = head.iter().map(<_>::to_string).collect();
- format!("`{}` and `{}`", head.join("`, `"), tail)
- }
- _ => {
- let (head, tail) = witnesses.split_at(LIMIT);
- let head: Vec<_> = head.iter().map(<_>::to_string).collect();
- format!("`{}` and {} more", head.join("`, `"), tail.len())
- }
- }
-}
-
-fn pattern_not_covered_label(witnesses: &[super::Pat<'_>], joined_patterns: &str) -> String {
- format!("pattern{} {} not covered", rustc_errors::pluralize!(witnesses.len()), joined_patterns)
-}
-
-/// Point at the definition of non-covered `enum` variants.
-fn adt_defined_here(
- cx: &MatchCheckCtxt<'_, '_>,
- err: &mut DiagnosticBuilder<'_>,
- ty: Ty<'_>,
- witnesses: &[super::Pat<'_>],
-) {
- let ty = ty.peel_refs();
- if let ty::Adt(def, _) = ty.kind {
- if let Some(sp) = cx.tcx.hir().span_if_local(def.did) {
- err.span_label(sp, format!("`{}` defined here", ty));
- }
-
- if witnesses.len() < 4 {
- for sp in maybe_point_at_variant(ty, &witnesses) {
- err.span_label(sp, "not covered");
- }
- }
- }
-}
-
-fn maybe_point_at_variant(ty: Ty<'_>, patterns: &[super::Pat<'_>]) -> Vec<Span> {
- let mut covered = vec![];
- if let ty::Adt(def, _) = ty.kind {
- // Don't point at variants that have already been covered due to other patterns to avoid
- // visual clutter.
- for pattern in patterns {
- use PatKind::{AscribeUserType, Deref, Leaf, Or, Variant};
- match &*pattern.kind {
- AscribeUserType { subpattern, .. } | Deref { subpattern } => {
- covered.extend(maybe_point_at_variant(ty, slice::from_ref(&subpattern)));
- }
- Variant { adt_def, variant_index, subpatterns, .. } if adt_def.did == def.did => {
- let sp = def.variants[*variant_index].ident.span;
- if covered.contains(&sp) {
- continue;
- }
- covered.push(sp);
-
- let pats = subpatterns
- .iter()
- .map(|field_pattern| field_pattern.pattern.clone())
- .collect::<Box<[_]>>();
- covered.extend(maybe_point_at_variant(ty, &pats));
- }
- Leaf { subpatterns } => {
- let pats = subpatterns
- .iter()
- .map(|field_pattern| field_pattern.pattern.clone())
- .collect::<Box<[_]>>();
- covered.extend(maybe_point_at_variant(ty, &pats));
- }
- Or { pats } => {
- let pats = pats.iter().cloned().collect::<Box<[_]>>();
- covered.extend(maybe_point_at_variant(ty, &pats));
- }
- _ => {}
- }
- }
- }
- covered
-}
-
-/// Check the legality of legality of by-move bindings.
-fn check_legality_of_move_bindings(cx: &mut MatchVisitor<'_, '_>, has_guard: bool, pat: &Pat<'_>) {
- let sess = cx.tcx.sess;
- let tables = cx.tables;
-
- // Find all by-ref spans.
- let mut by_ref_spans = Vec::new();
- pat.each_binding(|_, hir_id, span, _| {
- if let Some(ty::BindByReference(_)) = tables.extract_binding_mode(sess, hir_id, span) {
- by_ref_spans.push(span);
- }
- });
-
- // Find bad by-move spans:
- let by_move_spans = &mut Vec::new();
- let mut check_move = |p: &Pat<'_>, sub: Option<&Pat<'_>>| {
- // Check legality of moving out of the enum.
- //
- // `x @ Foo(..)` is legal, but `x @ Foo(y)` isn't.
- if sub.map_or(false, |p| p.contains_bindings()) {
- struct_span_err!(sess, p.span, E0007, "cannot bind by-move with sub-bindings")
- .span_label(p.span, "binds an already bound by-move value by moving it")
- .emit();
- } else if !has_guard && !by_ref_spans.is_empty() {
- by_move_spans.push(p.span);
- }
- };
- pat.walk_always(|p| {
- if let hir::PatKind::Binding(.., sub) = &p.kind {
- if let Some(ty::BindByValue(_)) = tables.extract_binding_mode(sess, p.hir_id, p.span) {
- let pat_ty = tables.node_type(p.hir_id);
- if !pat_ty.is_copy_modulo_regions(cx.tcx, cx.param_env, pat.span) {
- check_move(p, sub.as_deref());
- }
- }
- }
- });
-
- // Found some bad by-move spans, error!
- if !by_move_spans.is_empty() {
- let mut err = struct_span_err!(
- sess,
- MultiSpan::from_spans(by_move_spans.clone()),
- E0009,
- "cannot bind by-move and by-ref in the same pattern",
- );
- for span in by_ref_spans.iter() {
- err.span_label(*span, "by-ref pattern here");
- }
- for span in by_move_spans.iter() {
- err.span_label(*span, "by-move pattern here");
- }
- err.emit();
- }
-}
-
-/// Check that there are no borrow conflicts in `binding @ subpat` patterns.
-///
-/// For example, this would reject:
-/// - `ref x @ Some(ref mut y)`,
-/// - `ref mut x @ Some(ref y)`
-/// - `ref mut x @ Some(ref mut y)`.
-///
-/// This analysis is *not* subsumed by NLL.
-fn check_borrow_conflicts_in_at_patterns(cx: &MatchVisitor<'_, '_>, pat: &Pat<'_>) {
- let tab = cx.tables;
- let sess = cx.tcx.sess;
- // Get the mutability of `p` if it's by-ref.
- let extract_binding_mut = |hir_id, span| match tab.extract_binding_mode(sess, hir_id, span)? {
- ty::BindByValue(_) => None,
- ty::BindByReference(m) => Some(m),
- };
- pat.walk_always(|pat| {
- // Extract `sub` in `binding @ sub`.
- let (name, sub) = match &pat.kind {
- hir::PatKind::Binding(.., name, Some(sub)) => (*name, sub),
- _ => return,
- };
-
- // Extract the mutability.
- let mut_outer = match extract_binding_mut(pat.hir_id, pat.span) {
- None => return,
- Some(m) => m,
- };
-
- // We now have `ref $mut_outer binding @ sub` (semantically).
- // Recurse into each binding in `sub` and find mutability conflicts.
- let mut conflicts_mut_mut = Vec::new();
- let mut conflicts_mut_ref = Vec::new();
- sub.each_binding(|_, hir_id, span, _| {
- if let Some(mut_inner) = extract_binding_mut(hir_id, span) {
- match (mut_outer, mut_inner) {
- (Mutability::Not, Mutability::Not) => {}
- (Mutability::Mut, Mutability::Mut) => conflicts_mut_mut.push(span),
- _ => conflicts_mut_ref.push(span),
- }
- }
- });
-
- // Report errors if any.
- let binding_span = pat.span.with_hi(name.span.hi());
- if !conflicts_mut_mut.is_empty() {
- // Report mutability conflicts for e.g. `ref mut x @ Some(ref mut y)`.
- let msg = &format!("cannot borrow `{}` as mutable more than once at a time", name);
- let mut err = sess.struct_span_err(pat.span, msg);
- err.span_label(binding_span, "first mutable borrow occurs here");
- for sp in conflicts_mut_mut {
- err.span_label(sp, "another mutable borrow occurs here");
- }
- for sp in conflicts_mut_ref {
- err.span_label(sp, "also borrowed as immutable here");
- }
- err.emit();
- } else if !conflicts_mut_ref.is_empty() {
- // Report mutability conflicts for e.g. `ref x @ Some(ref mut y)` or the converse.
- let (primary, also) = match mut_outer {
- Mutability::Mut => ("mutable", "immutable"),
- Mutability::Not => ("immutable", "mutable"),
- };
- let msg = &format!(
- "cannot borrow `{}` as {} because it is also borrowed as {}",
- name, also, primary,
- );
- let mut err = sess.struct_span_err(pat.span, msg);
- err.span_label(binding_span, &format!("{} borrow occurs here", primary));
- for sp in conflicts_mut_ref {
- err.span_label(sp, &format!("{} borrow occurs here", also));
- }
- err.emit();
- }
- });
-}
-
-/// Forbids bindings in `@` patterns. This used to be is necessary for memory safety,
-/// because of the way rvalues were handled in the borrow check. (See issue #14587.)
-fn check_legality_of_bindings_in_at_patterns(cx: &MatchVisitor<'_, '_>, pat: &Pat<'_>) {
- AtBindingPatternVisitor { cx, bindings_allowed: true }.visit_pat(pat);
-
- struct AtBindingPatternVisitor<'a, 'b, 'tcx> {
- cx: &'a MatchVisitor<'b, 'tcx>,
- bindings_allowed: bool,
- }
-
- impl<'v> Visitor<'v> for AtBindingPatternVisitor<'_, '_, '_> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'v> {
- NestedVisitorMap::None
- }
-
- fn visit_pat(&mut self, pat: &Pat<'_>) {
- match pat.kind {
- hir::PatKind::Binding(.., ref subpat) => {
- if !self.bindings_allowed {
- feature_err(
- &self.cx.tcx.sess.parse_sess,
- sym::bindings_after_at,
- pat.span,
- "pattern bindings after an `@` are unstable",
- )
- .emit();
- }
-
- if subpat.is_some() {
- let bindings_were_allowed = self.bindings_allowed;
- self.bindings_allowed = false;
- intravisit::walk_pat(self, pat);
- self.bindings_allowed = bindings_were_allowed;
- }
- }
- _ => intravisit::walk_pat(self, pat),
- }
- }
- }
-}
+++ /dev/null
-use crate::const_eval::const_variant_index;
-
-use rustc::infer::InferCtxt;
-use rustc::lint;
-use rustc::mir::Field;
-use rustc::traits::{ObligationCause, PredicateObligation};
-use rustc::ty::{self, Ty, TyCtxt};
-use rustc_hir as hir;
-
-use rustc_index::vec::Idx;
-
-use rustc_span::Span;
-
-use std::cell::Cell;
-
-use super::{FieldPat, Pat, PatCtxt, PatKind};
-
-impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
- /// Converts an evaluated constant to a pattern (if possible).
- /// This means aggregate values (like structs and enums) are converted
- /// to a pattern that matches the value (as if you'd compared via structural equality).
- pub(super) fn const_to_pat(
- &self,
- cv: &'tcx ty::Const<'tcx>,
- id: hir::HirId,
- span: Span,
- ) -> Pat<'tcx> {
- debug!("const_to_pat: cv={:#?} id={:?}", cv, id);
- debug!("const_to_pat: cv.ty={:?} span={:?}", cv.ty, span);
-
- self.tcx.infer_ctxt().enter(|infcx| {
- let mut convert = ConstToPat::new(self, id, span, infcx);
- convert.to_pat(cv)
- })
- }
-}
-
-struct ConstToPat<'a, 'tcx> {
- id: hir::HirId,
- span: Span,
- param_env: ty::ParamEnv<'tcx>,
-
- // This tracks if we signal some hard error for a given const value, so that
- // we will not subsequently issue an irrelevant lint for the same const
- // value.
- saw_const_match_error: Cell<bool>,
-
- // inference context used for checking `T: Structural` bounds.
- infcx: InferCtxt<'a, 'tcx>,
-
- include_lint_checks: bool,
-}
-
-impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
- fn new(
- pat_ctxt: &PatCtxt<'_, 'tcx>,
- id: hir::HirId,
- span: Span,
- infcx: InferCtxt<'a, 'tcx>,
- ) -> Self {
- ConstToPat {
- id,
- span,
- infcx,
- param_env: pat_ctxt.param_env,
- include_lint_checks: pat_ctxt.include_lint_checks,
- saw_const_match_error: Cell::new(false),
- }
- }
-
- fn tcx(&self) -> TyCtxt<'tcx> {
- self.infcx.tcx
- }
-
- fn search_for_structural_match_violation(
- &self,
- ty: Ty<'tcx>,
- ) -> Option<ty::NonStructuralMatchTy<'tcx>> {
- ty::search_for_structural_match_violation(self.id, self.span, self.tcx(), ty)
- }
-
- fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool {
- ty::type_marked_structural(self.id, self.span, &self.infcx, ty)
- }
-
- fn to_pat(&mut self, cv: &'tcx ty::Const<'tcx>) -> Pat<'tcx> {
- // This method is just a wrapper handling a validity check; the heavy lifting is
- // performed by the recursive `recur` method, which is not meant to be
- // invoked except by this method.
- //
- // once indirect_structural_match is a full fledged error, this
- // level of indirection can be eliminated
-
- let inlined_const_as_pat = self.recur(cv);
-
- if self.include_lint_checks && !self.saw_const_match_error.get() {
- // If we were able to successfully convert the const to some pat,
- // double-check that all types in the const implement `Structural`.
-
- let structural = self.search_for_structural_match_violation(cv.ty);
- debug!(
- "search_for_structural_match_violation cv.ty: {:?} returned: {:?}",
- cv.ty, structural
- );
- if let Some(non_sm_ty) = structural {
- let adt_def = match non_sm_ty {
- ty::NonStructuralMatchTy::Adt(adt_def) => adt_def,
- ty::NonStructuralMatchTy::Param => {
- bug!("use of constant whose type is a parameter inside a pattern")
- }
- };
- let path = self.tcx().def_path_str(adt_def.did);
- let msg = format!(
- "to use a constant of type `{}` in a pattern, \
- `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
- path, path,
- );
-
- // double-check there even *is* a semantic `PartialEq` to dispatch to.
- //
- // (If there isn't, then we can safely issue a hard
- // error, because that's never worked, due to compiler
- // using `PartialEq::eq` in this scenario in the past.)
- //
- // Note: To fix rust-lang/rust#65466, one could lift this check
- // *before* any structural-match checking, and unconditionally error
- // if `PartialEq` is not implemented. However, that breaks stable
- // code at the moment, because types like `for <'a> fn(&'a ())` do
- // not *yet* implement `PartialEq`. So for now we leave this here.
- let ty_is_partial_eq: bool = {
- let partial_eq_trait_id = self.tcx().lang_items().eq_trait().unwrap();
- let obligation: PredicateObligation<'_> = self.tcx().predicate_for_trait_def(
- self.param_env,
- ObligationCause::misc(self.span, self.id),
- partial_eq_trait_id,
- 0,
- cv.ty,
- &[],
- );
- // FIXME: should this call a `predicate_must_hold` variant instead?
- self.infcx.predicate_may_hold(&obligation)
- };
-
- if !ty_is_partial_eq {
- // span_fatal avoids ICE from resolution of non-existent method (rare case).
- self.tcx().sess.span_fatal(self.span, &msg);
- } else {
- self.tcx().lint_hir(
- lint::builtin::INDIRECT_STRUCTURAL_MATCH,
- self.id,
- self.span,
- &msg,
- );
- }
- }
- }
-
- inlined_const_as_pat
- }
-
- // Recursive helper for `to_pat`; invoke that (instead of calling this directly).
- fn recur(&self, cv: &'tcx ty::Const<'tcx>) -> Pat<'tcx> {
- let id = self.id;
- let span = self.span;
- let tcx = self.tcx();
- let param_env = self.param_env;
-
- let adt_subpattern = |i, variant_opt| {
- let field = Field::new(i);
- let val = crate::const_eval::const_field(tcx, param_env, variant_opt, field, cv);
- self.recur(val)
- };
- let adt_subpatterns = |n, variant_opt| {
- (0..n)
- .map(|i| {
- let field = Field::new(i);
- FieldPat { field, pattern: adt_subpattern(i, variant_opt) }
- })
- .collect::<Vec<_>>()
- };
-
- let kind = match cv.ty.kind {
- ty::Float(_) => {
- tcx.lint_hir(
- ::rustc::lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
- id,
- span,
- "floating-point types cannot be used in patterns",
- );
- PatKind::Constant { value: cv }
- }
- ty::Adt(adt_def, _) if adt_def.is_union() => {
- // Matching on union fields is unsafe, we can't hide it in constants
- self.saw_const_match_error.set(true);
- tcx.sess.span_err(span, "cannot use unions in constant patterns");
- PatKind::Wild
- }
- // keep old code until future-compat upgraded to errors.
- ty::Adt(adt_def, _) if !self.type_marked_structural(cv.ty) => {
- debug!("adt_def {:?} has !type_marked_structural for cv.ty: {:?}", adt_def, cv.ty);
- let path = tcx.def_path_str(adt_def.did);
- let msg = format!(
- "to use a constant of type `{}` in a pattern, \
- `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
- path, path,
- );
- self.saw_const_match_error.set(true);
- tcx.sess.span_err(span, &msg);
- PatKind::Wild
- }
- // keep old code until future-compat upgraded to errors.
- ty::Ref(_, adt_ty @ ty::TyS { kind: ty::Adt(_, _), .. }, _)
- if !self.type_marked_structural(adt_ty) =>
- {
- let adt_def =
- if let ty::Adt(adt_def, _) = adt_ty.kind { adt_def } else { unreachable!() };
-
- debug!(
- "adt_def {:?} has !type_marked_structural for adt_ty: {:?}",
- adt_def, adt_ty
- );
-
- // HACK(estebank): Side-step ICE #53708, but anything other than erroring here
- // would be wrong. Returnging `PatKind::Wild` is not technically correct.
- let path = tcx.def_path_str(adt_def.did);
- let msg = format!(
- "to use a constant of type `{}` in a pattern, \
- `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
- path, path,
- );
- self.saw_const_match_error.set(true);
- tcx.sess.span_err(span, &msg);
- PatKind::Wild
- }
- ty::Adt(adt_def, substs) if adt_def.is_enum() => {
- let variant_index = const_variant_index(tcx, self.param_env, cv);
- let subpatterns = adt_subpatterns(
- adt_def.variants[variant_index].fields.len(),
- Some(variant_index),
- );
- PatKind::Variant { adt_def, substs, variant_index, subpatterns }
- }
- ty::Adt(adt_def, _) => {
- let struct_var = adt_def.non_enum_variant();
- PatKind::Leaf { subpatterns: adt_subpatterns(struct_var.fields.len(), None) }
- }
- ty::Tuple(fields) => PatKind::Leaf { subpatterns: adt_subpatterns(fields.len(), None) },
- ty::Array(_, n) => PatKind::Array {
- prefix: (0..n.eval_usize(tcx, self.param_env))
- .map(|i| adt_subpattern(i as usize, None))
- .collect(),
- slice: None,
- suffix: Vec::new(),
- },
- _ => PatKind::Constant { value: cv },
- };
-
- Pat { span, ty: cv.ty, kind: Box::new(kind) }
- }
-}
+++ /dev/null
-//! Validation of patterns/matches.
-
-mod _match;
-mod check_match;
-mod const_to_pat;
-
-pub(crate) use self::check_match::check_match;
-
-use crate::hair::constant::*;
-use crate::hair::util::UserAnnotatedTyHelpers;
-
-use rustc::mir::interpret::{get_slice_bytes, sign_extend, ConstValue, ErrorHandled};
-use rustc::mir::UserTypeProjection;
-use rustc::mir::{BorrowKind, Field, Mutability};
-use rustc::ty::layout::VariantIdx;
-use rustc::ty::subst::{GenericArg, SubstsRef};
-use rustc::ty::{self, AdtDef, DefIdTree, Region, Ty, TyCtxt, UserType};
-use rustc::ty::{CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations};
-use rustc_hir as hir;
-use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
-use rustc_hir::pat_util::EnumerateAndAdjustIterator;
-use rustc_hir::RangeEnd;
-use rustc_index::vec::Idx;
-use rustc_span::{Span, DUMMY_SP};
-use syntax::ast;
-
-use std::cmp::Ordering;
-use std::fmt;
-
-use rustc_error_codes::*;
-
-#[derive(Clone, Debug)]
-pub enum PatternError {
- AssocConstInPattern(Span),
- StaticInPattern(Span),
- FloatBug,
- NonConstPath(Span),
-}
-
-#[derive(Copy, Clone, Debug)]
-pub enum BindingMode {
- ByValue,
- ByRef(BorrowKind),
-}
-
-#[derive(Clone, Debug)]
-pub struct FieldPat<'tcx> {
- pub field: Field,
- pub pattern: Pat<'tcx>,
-}
-
-#[derive(Clone, Debug)]
-pub struct Pat<'tcx> {
- pub ty: Ty<'tcx>,
- pub span: Span,
- pub kind: Box<PatKind<'tcx>>,
-}
-
-impl<'tcx> Pat<'tcx> {
- pub(crate) fn wildcard_from_ty(ty: Ty<'tcx>) -> Self {
- Pat { ty, span: DUMMY_SP, kind: Box::new(PatKind::Wild) }
- }
-}
-
-#[derive(Copy, Clone, Debug, PartialEq)]
-pub struct PatTyProj<'tcx> {
- pub user_ty: CanonicalUserType<'tcx>,
-}
-
-impl<'tcx> PatTyProj<'tcx> {
- pub(crate) fn from_user_type(user_annotation: CanonicalUserType<'tcx>) -> Self {
- Self { user_ty: user_annotation }
- }
-
- pub(crate) fn user_ty(
- self,
- annotations: &mut CanonicalUserTypeAnnotations<'tcx>,
- inferred_ty: Ty<'tcx>,
- span: Span,
- ) -> UserTypeProjection {
- UserTypeProjection {
- base: annotations.push(CanonicalUserTypeAnnotation {
- span,
- user_ty: self.user_ty,
- inferred_ty,
- }),
- projs: Vec::new(),
- }
- }
-}
-
-#[derive(Copy, Clone, Debug, PartialEq)]
-pub struct Ascription<'tcx> {
- pub user_ty: PatTyProj<'tcx>,
- /// Variance to use when relating the type `user_ty` to the **type of the value being
- /// matched**. Typically, this is `Variance::Covariant`, since the value being matched must
- /// have a type that is some subtype of the ascribed type.
- ///
- /// Note that this variance does not apply for any bindings within subpatterns. The type
- /// assigned to those bindings must be exactly equal to the `user_ty` given here.
- ///
- /// The only place where this field is not `Covariant` is when matching constants, where
- /// we currently use `Contravariant` -- this is because the constant type just needs to
- /// be "comparable" to the type of the input value. So, for example:
- ///
- /// ```text
- /// match x { "foo" => .. }
- /// ```
- ///
- /// requires that `&'static str <: T_x`, where `T_x` is the type of `x`. Really, we should
- /// probably be checking for a `PartialEq` impl instead, but this preserves the behavior
- /// of the old type-check for now. See #57280 for details.
- pub variance: ty::Variance,
- pub user_ty_span: Span,
-}
-
-#[derive(Clone, Debug)]
-pub enum PatKind<'tcx> {
- Wild,
-
- AscribeUserType {
- ascription: Ascription<'tcx>,
- subpattern: Pat<'tcx>,
- },
-
- /// `x`, `ref x`, `x @ P`, etc.
- Binding {
- mutability: Mutability,
- name: ast::Name,
- mode: BindingMode,
- var: hir::HirId,
- ty: Ty<'tcx>,
- subpattern: Option<Pat<'tcx>>,
- },
-
- /// `Foo(...)` or `Foo{...}` or `Foo`, where `Foo` is a variant name from an ADT with
- /// multiple variants.
- Variant {
- adt_def: &'tcx AdtDef,
- substs: SubstsRef<'tcx>,
- variant_index: VariantIdx,
- subpatterns: Vec<FieldPat<'tcx>>,
- },
-
- /// `(...)`, `Foo(...)`, `Foo{...}`, or `Foo`, where `Foo` is a variant name from an ADT with
- /// a single variant.
- Leaf {
- subpatterns: Vec<FieldPat<'tcx>>,
- },
-
- /// `box P`, `&P`, `&mut P`, etc.
- Deref {
- subpattern: Pat<'tcx>,
- },
-
- Constant {
- value: &'tcx ty::Const<'tcx>,
- },
-
- Range(PatRange<'tcx>),
-
- /// Matches against a slice, checking the length and extracting elements.
- /// irrefutable when there is a slice pattern and both `prefix` and `suffix` are empty.
- /// e.g., `&[ref xs @ ..]`.
- Slice {
- prefix: Vec<Pat<'tcx>>,
- slice: Option<Pat<'tcx>>,
- suffix: Vec<Pat<'tcx>>,
- },
-
- /// Fixed match against an array; irrefutable.
- Array {
- prefix: Vec<Pat<'tcx>>,
- slice: Option<Pat<'tcx>>,
- suffix: Vec<Pat<'tcx>>,
- },
-
- /// An or-pattern, e.g. `p | q`.
- /// Invariant: `pats.len() >= 2`.
- Or {
- pats: Vec<Pat<'tcx>>,
- },
-}
-
-#[derive(Copy, Clone, Debug, PartialEq)]
-pub struct PatRange<'tcx> {
- pub lo: &'tcx ty::Const<'tcx>,
- pub hi: &'tcx ty::Const<'tcx>,
- pub end: RangeEnd,
-}
-
-impl<'tcx> fmt::Display for Pat<'tcx> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- // Printing lists is a chore.
- let mut first = true;
- let mut start_or_continue = |s| {
- if first {
- first = false;
- ""
- } else {
- s
- }
- };
- let mut start_or_comma = || start_or_continue(", ");
-
- match *self.kind {
- PatKind::Wild => write!(f, "_"),
- PatKind::AscribeUserType { ref subpattern, .. } => write!(f, "{}: _", subpattern),
- PatKind::Binding { mutability, name, mode, ref subpattern, .. } => {
- let is_mut = match mode {
- BindingMode::ByValue => mutability == Mutability::Mut,
- BindingMode::ByRef(bk) => {
- write!(f, "ref ")?;
- match bk {
- BorrowKind::Mut { .. } => true,
- _ => false,
- }
- }
- };
- if is_mut {
- write!(f, "mut ")?;
- }
- write!(f, "{}", name)?;
- if let Some(ref subpattern) = *subpattern {
- write!(f, " @ {}", subpattern)?;
- }
- Ok(())
- }
- PatKind::Variant { ref subpatterns, .. } | PatKind::Leaf { ref subpatterns } => {
- let variant = match *self.kind {
- PatKind::Variant { adt_def, variant_index, .. } => {
- Some(&adt_def.variants[variant_index])
- }
- _ => {
- if let ty::Adt(adt, _) = self.ty.kind {
- if !adt.is_enum() {
- Some(&adt.variants[VariantIdx::new(0)])
- } else {
- None
- }
- } else {
- None
- }
- }
- };
-
- if let Some(variant) = variant {
- write!(f, "{}", variant.ident)?;
-
- // Only for Adt we can have `S {...}`,
- // which we handle separately here.
- if variant.ctor_kind == CtorKind::Fictive {
- write!(f, " {{ ")?;
-
- let mut printed = 0;
- for p in subpatterns {
- if let PatKind::Wild = *p.pattern.kind {
- continue;
- }
- let name = variant.fields[p.field.index()].ident;
- write!(f, "{}{}: {}", start_or_comma(), name, p.pattern)?;
- printed += 1;
- }
-
- if printed < variant.fields.len() {
- write!(f, "{}..", start_or_comma())?;
- }
-
- return write!(f, " }}");
- }
- }
-
- let num_fields = variant.map_or(subpatterns.len(), |v| v.fields.len());
- if num_fields != 0 || variant.is_none() {
- write!(f, "(")?;
- for i in 0..num_fields {
- write!(f, "{}", start_or_comma())?;
-
- // Common case: the field is where we expect it.
- if let Some(p) = subpatterns.get(i) {
- if p.field.index() == i {
- write!(f, "{}", p.pattern)?;
- continue;
- }
- }
-
- // Otherwise, we have to go looking for it.
- if let Some(p) = subpatterns.iter().find(|p| p.field.index() == i) {
- write!(f, "{}", p.pattern)?;
- } else {
- write!(f, "_")?;
- }
- }
- write!(f, ")")?;
- }
-
- Ok(())
- }
- PatKind::Deref { ref subpattern } => {
- match self.ty.kind {
- ty::Adt(def, _) if def.is_box() => write!(f, "box ")?,
- ty::Ref(_, _, mutbl) => {
- write!(f, "&{}", mutbl.prefix_str())?;
- }
- _ => bug!("{} is a bad Deref pattern type", self.ty),
- }
- write!(f, "{}", subpattern)
- }
- PatKind::Constant { value } => write!(f, "{}", value),
- PatKind::Range(PatRange { lo, hi, end }) => {
- write!(f, "{}", lo)?;
- write!(f, "{}", end)?;
- write!(f, "{}", hi)
- }
- PatKind::Slice { ref prefix, ref slice, ref suffix }
- | PatKind::Array { ref prefix, ref slice, ref suffix } => {
- write!(f, "[")?;
- for p in prefix {
- write!(f, "{}{}", start_or_comma(), p)?;
- }
- if let Some(ref slice) = *slice {
- write!(f, "{}", start_or_comma())?;
- match *slice.kind {
- PatKind::Wild => {}
- _ => write!(f, "{}", slice)?,
- }
- write!(f, "..")?;
- }
- for p in suffix {
- write!(f, "{}{}", start_or_comma(), p)?;
- }
- write!(f, "]")
- }
- PatKind::Or { ref pats } => {
- for pat in pats {
- write!(f, "{}{}", start_or_continue(" | "), pat)?;
- }
- Ok(())
- }
- }
- }
-}
-
-pub struct PatCtxt<'a, 'tcx> {
- pub tcx: TyCtxt<'tcx>,
- pub param_env: ty::ParamEnv<'tcx>,
- pub tables: &'a ty::TypeckTables<'tcx>,
- pub substs: SubstsRef<'tcx>,
- pub errors: Vec<PatternError>,
- include_lint_checks: bool,
-}
-
-impl<'a, 'tcx> Pat<'tcx> {
- pub fn from_hir(
- tcx: TyCtxt<'tcx>,
- param_env_and_substs: ty::ParamEnvAnd<'tcx, SubstsRef<'tcx>>,
- tables: &'a ty::TypeckTables<'tcx>,
- pat: &'tcx hir::Pat<'tcx>,
- ) -> Self {
- let mut pcx = PatCtxt::new(tcx, param_env_and_substs, tables);
- let result = pcx.lower_pattern(pat);
- if !pcx.errors.is_empty() {
- let msg = format!("encountered errors lowering pattern: {:?}", pcx.errors);
- tcx.sess.delay_span_bug(pat.span, &msg);
- }
- debug!("Pat::from_hir({:?}) = {:?}", pat, result);
- result
- }
-}
-
-impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
- pub fn new(
- tcx: TyCtxt<'tcx>,
- param_env_and_substs: ty::ParamEnvAnd<'tcx, SubstsRef<'tcx>>,
- tables: &'a ty::TypeckTables<'tcx>,
- ) -> Self {
- PatCtxt {
- tcx,
- param_env: param_env_and_substs.param_env,
- tables,
- substs: param_env_and_substs.value,
- errors: vec![],
- include_lint_checks: false,
- }
- }
-
- pub fn include_lint_checks(&mut self) -> &mut Self {
- self.include_lint_checks = true;
- self
- }
-
- pub fn lower_pattern(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Pat<'tcx> {
- // When implicit dereferences have been inserted in this pattern, the unadjusted lowered
- // pattern has the type that results *after* dereferencing. For example, in this code:
- //
- // ```
- // match &&Some(0i32) {
- // Some(n) => { ... },
- // _ => { ... },
- // }
- // ```
- //
- // the type assigned to `Some(n)` in `unadjusted_pat` would be `Option<i32>` (this is
- // determined in rustc_typeck::check::match). The adjustments would be
- //
- // `vec![&&Option<i32>, &Option<i32>]`.
- //
- // Applying the adjustments, we want to instead output `&&Some(n)` (as a HAIR pattern). So
- // we wrap the unadjusted pattern in `PatKind::Deref` repeatedly, consuming the
- // adjustments in *reverse order* (last-in-first-out, so that the last `Deref` inserted
- // gets the least-dereferenced type).
- let unadjusted_pat = self.lower_pattern_unadjusted(pat);
- self.tables.pat_adjustments().get(pat.hir_id).unwrap_or(&vec![]).iter().rev().fold(
- unadjusted_pat,
- |pat, ref_ty| {
- debug!("{:?}: wrapping pattern with type {:?}", pat, ref_ty);
- Pat {
- span: pat.span,
- ty: ref_ty,
- kind: Box::new(PatKind::Deref { subpattern: pat }),
- }
- },
- )
- }
-
- fn lower_range_expr(
- &mut self,
- expr: &'tcx hir::Expr<'tcx>,
- ) -> (PatKind<'tcx>, Option<Ascription<'tcx>>) {
- match self.lower_lit(expr) {
- PatKind::AscribeUserType {
- ascription: lo_ascription,
- subpattern: Pat { kind: box kind, .. },
- } => (kind, Some(lo_ascription)),
- kind => (kind, None),
- }
- }
-
- fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Pat<'tcx> {
- let mut ty = self.tables.node_type(pat.hir_id);
-
- if let ty::Error = ty.kind {
- // Avoid ICEs (e.g., #50577 and #50585).
- return Pat { span: pat.span, ty, kind: Box::new(PatKind::Wild) };
- }
-
- let kind = match pat.kind {
- hir::PatKind::Wild => PatKind::Wild,
-
- hir::PatKind::Lit(ref value) => self.lower_lit(value),
-
- hir::PatKind::Range(ref lo_expr, ref hi_expr, end) => {
- let (lo, lo_ascription) = self.lower_range_expr(lo_expr);
- let (hi, hi_ascription) = self.lower_range_expr(hi_expr);
-
- let mut kind = match (lo, hi) {
- (PatKind::Constant { value: lo }, PatKind::Constant { value: hi }) => {
- assert_eq!(lo.ty, ty);
- assert_eq!(hi.ty, ty);
- let cmp = compare_const_vals(self.tcx, lo, hi, self.param_env, ty);
- match (end, cmp) {
- (RangeEnd::Excluded, Some(Ordering::Less)) => {
- PatKind::Range(PatRange { lo, hi, end })
- }
- (RangeEnd::Excluded, _) => {
- span_err!(
- self.tcx.sess,
- lo_expr.span,
- E0579,
- "lower range bound must be less than upper",
- );
- PatKind::Wild
- }
- (RangeEnd::Included, Some(Ordering::Equal)) => {
- PatKind::Constant { value: lo }
- }
- (RangeEnd::Included, Some(Ordering::Less)) => {
- PatKind::Range(PatRange { lo, hi, end })
- }
- (RangeEnd::Included, _) => {
- let mut err = struct_span_err!(
- self.tcx.sess,
- lo_expr.span,
- E0030,
- "lower range bound must be less than or equal to upper"
- );
- err.span_label(lo_expr.span, "lower bound larger than upper bound");
- if self.tcx.sess.teach(&err.get_code().unwrap()) {
- err.note(
- "When matching against a range, the compiler \
- verifies that the range is non-empty. Range \
- patterns include both end-points, so this is \
- equivalent to requiring the start of the range \
- to be less than or equal to the end of the range.",
- );
- }
- err.emit();
- PatKind::Wild
- }
- }
- }
- ref pats => {
- self.tcx.sess.delay_span_bug(
- pat.span,
- &format!(
- "found bad range pattern `{:?}` outside of error recovery",
- pats,
- ),
- );
-
- PatKind::Wild
- }
- };
-
- // If we are handling a range with associated constants (e.g.
- // `Foo::<'a>::A..=Foo::B`), we need to put the ascriptions for the associated
- // constants somewhere. Have them on the range pattern.
- for ascription in &[lo_ascription, hi_ascription] {
- if let Some(ascription) = ascription {
- kind = PatKind::AscribeUserType {
- ascription: *ascription,
- subpattern: Pat { span: pat.span, ty, kind: Box::new(kind) },
- };
- }
- }
-
- kind
- }
-
- hir::PatKind::Path(ref qpath) => {
- return self.lower_path(qpath, pat.hir_id, pat.span);
- }
-
- hir::PatKind::Ref(ref subpattern, _) | hir::PatKind::Box(ref subpattern) => {
- PatKind::Deref { subpattern: self.lower_pattern(subpattern) }
- }
-
- hir::PatKind::Slice(ref prefix, ref slice, ref suffix) => {
- self.slice_or_array_pattern(pat.span, ty, prefix, slice, suffix)
- }
-
- hir::PatKind::Tuple(ref pats, ddpos) => {
- let tys = match ty.kind {
- ty::Tuple(ref tys) => tys,
- _ => span_bug!(pat.span, "unexpected type for tuple pattern: {:?}", ty),
- };
- let subpatterns = self.lower_tuple_subpats(pats, tys.len(), ddpos);
- PatKind::Leaf { subpatterns }
- }
-
- hir::PatKind::Binding(_, id, ident, ref sub) => {
- let bm =
- *self.tables.pat_binding_modes().get(pat.hir_id).expect("missing binding mode");
- let (mutability, mode) = match bm {
- ty::BindByValue(mutbl) => (mutbl, BindingMode::ByValue),
- ty::BindByReference(hir::Mutability::Mut) => (
- Mutability::Not,
- BindingMode::ByRef(BorrowKind::Mut { allow_two_phase_borrow: false }),
- ),
- ty::BindByReference(hir::Mutability::Not) => {
- (Mutability::Not, BindingMode::ByRef(BorrowKind::Shared))
- }
- };
-
- // A ref x pattern is the same node used for x, and as such it has
- // x's type, which is &T, where we want T (the type being matched).
- let var_ty = ty;
- if let ty::BindByReference(_) = bm {
- if let ty::Ref(_, rty, _) = ty.kind {
- ty = rty;
- } else {
- bug!("`ref {}` has wrong type {}", ident, ty);
- }
- };
-
- PatKind::Binding {
- mutability,
- mode,
- name: ident.name,
- var: id,
- ty: var_ty,
- subpattern: self.lower_opt_pattern(sub),
- }
- }
-
- hir::PatKind::TupleStruct(ref qpath, ref pats, ddpos) => {
- let res = self.tables.qpath_res(qpath, pat.hir_id);
- let adt_def = match ty.kind {
- ty::Adt(adt_def, _) => adt_def,
- _ => span_bug!(pat.span, "tuple struct pattern not applied to an ADT {:?}", ty),
- };
- let variant_def = adt_def.variant_of_res(res);
- let subpatterns = self.lower_tuple_subpats(pats, variant_def.fields.len(), ddpos);
- self.lower_variant_or_leaf(res, pat.hir_id, pat.span, ty, subpatterns)
- }
-
- hir::PatKind::Struct(ref qpath, ref fields, _) => {
- let res = self.tables.qpath_res(qpath, pat.hir_id);
- let subpatterns = fields
- .iter()
- .map(|field| FieldPat {
- field: Field::new(self.tcx.field_index(field.hir_id, self.tables)),
- pattern: self.lower_pattern(&field.pat),
- })
- .collect();
-
- self.lower_variant_or_leaf(res, pat.hir_id, pat.span, ty, subpatterns)
- }
-
- hir::PatKind::Or(ref pats) => PatKind::Or { pats: self.lower_patterns(pats) },
- };
-
- Pat { span: pat.span, ty, kind: Box::new(kind) }
- }
-
- fn lower_tuple_subpats(
- &mut self,
- pats: &'tcx [&'tcx hir::Pat<'tcx>],
- expected_len: usize,
- gap_pos: Option<usize>,
- ) -> Vec<FieldPat<'tcx>> {
- pats.iter()
- .enumerate_and_adjust(expected_len, gap_pos)
- .map(|(i, subpattern)| FieldPat {
- field: Field::new(i),
- pattern: self.lower_pattern(subpattern),
- })
- .collect()
- }
-
- fn lower_patterns(&mut self, pats: &'tcx [&'tcx hir::Pat<'tcx>]) -> Vec<Pat<'tcx>> {
- pats.iter().map(|p| self.lower_pattern(p)).collect()
- }
-
- fn lower_opt_pattern(&mut self, pat: &'tcx Option<&'tcx hir::Pat<'tcx>>) -> Option<Pat<'tcx>> {
- pat.as_ref().map(|p| self.lower_pattern(p))
- }
-
- fn slice_or_array_pattern(
- &mut self,
- span: Span,
- ty: Ty<'tcx>,
- prefix: &'tcx [&'tcx hir::Pat<'tcx>],
- slice: &'tcx Option<&'tcx hir::Pat<'tcx>>,
- suffix: &'tcx [&'tcx hir::Pat<'tcx>],
- ) -> PatKind<'tcx> {
- let prefix = self.lower_patterns(prefix);
- let slice = self.lower_opt_pattern(slice);
- let suffix = self.lower_patterns(suffix);
- match ty.kind {
- // Matching a slice, `[T]`.
- ty::Slice(..) => PatKind::Slice { prefix, slice, suffix },
- // Fixed-length array, `[T; len]`.
- ty::Array(_, len) => {
- let len = len.eval_usize(self.tcx, self.param_env);
- assert!(len >= prefix.len() as u64 + suffix.len() as u64);
- PatKind::Array { prefix, slice, suffix }
- }
- _ => span_bug!(span, "bad slice pattern type {:?}", ty),
- }
- }
-
- fn lower_variant_or_leaf(
- &mut self,
- res: Res,
- hir_id: hir::HirId,
- span: Span,
- ty: Ty<'tcx>,
- subpatterns: Vec<FieldPat<'tcx>>,
- ) -> PatKind<'tcx> {
- let res = match res {
- Res::Def(DefKind::Ctor(CtorOf::Variant, ..), variant_ctor_id) => {
- let variant_id = self.tcx.parent(variant_ctor_id).unwrap();
- Res::Def(DefKind::Variant, variant_id)
- }
- res => res,
- };
-
- let mut kind = match res {
- Res::Def(DefKind::Variant, variant_id) => {
- let enum_id = self.tcx.parent(variant_id).unwrap();
- let adt_def = self.tcx.adt_def(enum_id);
- if adt_def.is_enum() {
- let substs = match ty.kind {
- ty::Adt(_, substs) | ty::FnDef(_, substs) => substs,
- ty::Error => {
- // Avoid ICE (#50585)
- return PatKind::Wild;
- }
- _ => bug!("inappropriate type for def: {:?}", ty),
- };
- PatKind::Variant {
- adt_def,
- substs,
- variant_index: adt_def.variant_index_with_id(variant_id),
- subpatterns,
- }
- } else {
- PatKind::Leaf { subpatterns }
- }
- }
-
- Res::Def(DefKind::Struct, _)
- | Res::Def(DefKind::Ctor(CtorOf::Struct, ..), _)
- | Res::Def(DefKind::Union, _)
- | Res::Def(DefKind::TyAlias, _)
- | Res::Def(DefKind::AssocTy, _)
- | Res::SelfTy(..)
- | Res::SelfCtor(..) => PatKind::Leaf { subpatterns },
-
- _ => {
- self.errors.push(PatternError::NonConstPath(span));
- PatKind::Wild
- }
- };
-
- if let Some(user_ty) = self.user_substs_applied_to_ty_of_hir_id(hir_id) {
- debug!("lower_variant_or_leaf: kind={:?} user_ty={:?} span={:?}", kind, user_ty, span);
- kind = PatKind::AscribeUserType {
- subpattern: Pat { span, ty, kind: Box::new(kind) },
- ascription: Ascription {
- user_ty: PatTyProj::from_user_type(user_ty),
- user_ty_span: span,
- variance: ty::Variance::Covariant,
- },
- };
- }
-
- kind
- }
-
- /// Takes a HIR Path. If the path is a constant, evaluates it and feeds
- /// it to `const_to_pat`. Any other path (like enum variants without fields)
- /// is converted to the corresponding pattern via `lower_variant_or_leaf`.
- fn lower_path(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, span: Span) -> Pat<'tcx> {
- let ty = self.tables.node_type(id);
- let res = self.tables.qpath_res(qpath, id);
- let is_associated_const = match res {
- Res::Def(DefKind::AssocConst, _) => true,
- _ => false,
- };
- let kind = match res {
- Res::Def(DefKind::Const, def_id) | Res::Def(DefKind::AssocConst, def_id) => {
- let substs = self.tables.node_substs(id);
- // Use `Reveal::All` here because patterns are always monomorphic even if their function isn't.
- match self.tcx.const_eval_resolve(
- self.param_env.with_reveal_all(),
- def_id,
- substs,
- Some(span),
- ) {
- Ok(value) => {
- let pattern = self.const_to_pat(value, id, span);
- if !is_associated_const {
- return pattern;
- }
-
- let user_provided_types = self.tables().user_provided_types();
- return if let Some(u_ty) = user_provided_types.get(id) {
- let user_ty = PatTyProj::from_user_type(*u_ty);
- Pat {
- span,
- kind: Box::new(PatKind::AscribeUserType {
- subpattern: pattern,
- ascription: Ascription {
- /// Note that use `Contravariant` here. See the
- /// `variance` field documentation for details.
- variance: ty::Variance::Contravariant,
- user_ty,
- user_ty_span: span,
- },
- }),
- ty: value.ty,
- }
- } else {
- pattern
- };
- }
- Err(ErrorHandled::TooGeneric) => {
- self.errors.push(if is_associated_const {
- PatternError::AssocConstInPattern(span)
- } else {
- PatternError::StaticInPattern(span)
- });
- PatKind::Wild
- }
- Err(_) => {
- self.tcx.sess.span_err(span, "could not evaluate constant pattern");
- PatKind::Wild
- }
- }
- }
- _ => self.lower_variant_or_leaf(res, id, span, ty, vec![]),
- };
-
- Pat { span, ty, kind: Box::new(kind) }
- }
-
- /// Converts literals, paths and negation of literals to patterns.
- /// The special case for negation exists to allow things like `-128_i8`
- /// which would overflow if we tried to evaluate `128_i8` and then negate
- /// afterwards.
- fn lower_lit(&mut self, expr: &'tcx hir::Expr<'tcx>) -> PatKind<'tcx> {
- match expr.kind {
- hir::ExprKind::Lit(ref lit) => {
- let ty = self.tables.expr_ty(expr);
- match lit_to_const(&lit.node, self.tcx, ty, false) {
- Ok(val) => *self.const_to_pat(val, expr.hir_id, lit.span).kind,
- Err(LitToConstError::UnparseableFloat) => {
- self.errors.push(PatternError::FloatBug);
- PatKind::Wild
- }
- Err(LitToConstError::Reported) => PatKind::Wild,
- }
- }
- hir::ExprKind::Path(ref qpath) => *self.lower_path(qpath, expr.hir_id, expr.span).kind,
- hir::ExprKind::Unary(hir::UnOp::UnNeg, ref expr) => {
- let ty = self.tables.expr_ty(expr);
- let lit = match expr.kind {
- hir::ExprKind::Lit(ref lit) => lit,
- _ => span_bug!(expr.span, "not a literal: {:?}", expr),
- };
- match lit_to_const(&lit.node, self.tcx, ty, true) {
- Ok(val) => *self.const_to_pat(val, expr.hir_id, lit.span).kind,
- Err(LitToConstError::UnparseableFloat) => {
- self.errors.push(PatternError::FloatBug);
- PatKind::Wild
- }
- Err(LitToConstError::Reported) => PatKind::Wild,
- }
- }
- _ => span_bug!(expr.span, "not a literal: {:?}", expr),
- }
- }
-}
-
-impl UserAnnotatedTyHelpers<'tcx> for PatCtxt<'_, 'tcx> {
- fn tcx(&self) -> TyCtxt<'tcx> {
- self.tcx
- }
-
- fn tables(&self) -> &ty::TypeckTables<'tcx> {
- self.tables
- }
-}
-
-pub trait PatternFoldable<'tcx>: Sized {
- fn fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self {
- self.super_fold_with(folder)
- }
-
- fn super_fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self;
-}
-
-pub trait PatternFolder<'tcx>: Sized {
- fn fold_pattern(&mut self, pattern: &Pat<'tcx>) -> Pat<'tcx> {
- pattern.super_fold_with(self)
- }
-
- fn fold_pattern_kind(&mut self, kind: &PatKind<'tcx>) -> PatKind<'tcx> {
- kind.super_fold_with(self)
- }
-}
-
-impl<'tcx, T: PatternFoldable<'tcx>> PatternFoldable<'tcx> for Box<T> {
- fn super_fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self {
- let content: T = (**self).fold_with(folder);
- box content
- }
-}
-
-impl<'tcx, T: PatternFoldable<'tcx>> PatternFoldable<'tcx> for Vec<T> {
- fn super_fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self {
- self.iter().map(|t| t.fold_with(folder)).collect()
- }
-}
-
-impl<'tcx, T: PatternFoldable<'tcx>> PatternFoldable<'tcx> for Option<T> {
- fn super_fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self {
- self.as_ref().map(|t| t.fold_with(folder))
- }
-}
-
-macro_rules! CloneImpls {
- (<$lt_tcx:tt> $($ty:ty),+) => {
- $(
- impl<$lt_tcx> PatternFoldable<$lt_tcx> for $ty {
- fn super_fold_with<F: PatternFolder<$lt_tcx>>(&self, _: &mut F) -> Self {
- Clone::clone(self)
- }
- }
- )+
- }
-}
-
-CloneImpls! { <'tcx>
- Span, Field, Mutability, ast::Name, hir::HirId, usize, ty::Const<'tcx>,
- Region<'tcx>, Ty<'tcx>, BindingMode, &'tcx AdtDef,
- SubstsRef<'tcx>, &'tcx GenericArg<'tcx>, UserType<'tcx>,
- UserTypeProjection, PatTyProj<'tcx>
-}
-
-impl<'tcx> PatternFoldable<'tcx> for FieldPat<'tcx> {
- fn super_fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self {
- FieldPat { field: self.field.fold_with(folder), pattern: self.pattern.fold_with(folder) }
- }
-}
-
-impl<'tcx> PatternFoldable<'tcx> for Pat<'tcx> {
- fn fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self {
- folder.fold_pattern(self)
- }
-
- fn super_fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self {
- Pat {
- ty: self.ty.fold_with(folder),
- span: self.span.fold_with(folder),
- kind: self.kind.fold_with(folder),
- }
- }
-}
-
-impl<'tcx> PatternFoldable<'tcx> for PatKind<'tcx> {
- fn fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self {
- folder.fold_pattern_kind(self)
- }
-
- fn super_fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self {
- match *self {
- PatKind::Wild => PatKind::Wild,
- PatKind::AscribeUserType {
- ref subpattern,
- ascription: Ascription { variance, ref user_ty, user_ty_span },
- } => PatKind::AscribeUserType {
- subpattern: subpattern.fold_with(folder),
- ascription: Ascription {
- user_ty: user_ty.fold_with(folder),
- variance,
- user_ty_span,
- },
- },
- PatKind::Binding { mutability, name, mode, var, ty, ref subpattern } => {
- PatKind::Binding {
- mutability: mutability.fold_with(folder),
- name: name.fold_with(folder),
- mode: mode.fold_with(folder),
- var: var.fold_with(folder),
- ty: ty.fold_with(folder),
- subpattern: subpattern.fold_with(folder),
- }
- }
- PatKind::Variant { adt_def, substs, variant_index, ref subpatterns } => {
- PatKind::Variant {
- adt_def: adt_def.fold_with(folder),
- substs: substs.fold_with(folder),
- variant_index,
- subpatterns: subpatterns.fold_with(folder),
- }
- }
- PatKind::Leaf { ref subpatterns } => {
- PatKind::Leaf { subpatterns: subpatterns.fold_with(folder) }
- }
- PatKind::Deref { ref subpattern } => {
- PatKind::Deref { subpattern: subpattern.fold_with(folder) }
- }
- PatKind::Constant { value } => PatKind::Constant { value },
- PatKind::Range(range) => PatKind::Range(range),
- PatKind::Slice { ref prefix, ref slice, ref suffix } => PatKind::Slice {
- prefix: prefix.fold_with(folder),
- slice: slice.fold_with(folder),
- suffix: suffix.fold_with(folder),
- },
- PatKind::Array { ref prefix, ref slice, ref suffix } => PatKind::Array {
- prefix: prefix.fold_with(folder),
- slice: slice.fold_with(folder),
- suffix: suffix.fold_with(folder),
- },
- PatKind::Or { ref pats } => PatKind::Or { pats: pats.fold_with(folder) },
- }
- }
-}
-
-pub fn compare_const_vals<'tcx>(
- tcx: TyCtxt<'tcx>,
- a: &'tcx ty::Const<'tcx>,
- b: &'tcx ty::Const<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- ty: Ty<'tcx>,
-) -> Option<Ordering> {
- trace!("compare_const_vals: {:?}, {:?}", a, b);
-
- let from_bool = |v: bool| v.then_some(Ordering::Equal);
-
- let fallback = || from_bool(a == b);
-
- // Use the fallback if any type differs
- if a.ty != b.ty || a.ty != ty {
- return fallback();
- }
-
- // Early return for equal constants (so e.g. references to ZSTs can be compared, even if they
- // are just integer addresses).
- if a.val == b.val {
- return from_bool(true);
- }
-
- let a_bits = a.try_eval_bits(tcx, param_env, ty);
- let b_bits = b.try_eval_bits(tcx, param_env, ty);
-
- if let (Some(a), Some(b)) = (a_bits, b_bits) {
- use ::rustc_apfloat::Float;
- return match ty.kind {
- ty::Float(ast::FloatTy::F32) => {
- let l = ::rustc_apfloat::ieee::Single::from_bits(a);
- let r = ::rustc_apfloat::ieee::Single::from_bits(b);
- l.partial_cmp(&r)
- }
- ty::Float(ast::FloatTy::F64) => {
- let l = ::rustc_apfloat::ieee::Double::from_bits(a);
- let r = ::rustc_apfloat::ieee::Double::from_bits(b);
- l.partial_cmp(&r)
- }
- ty::Int(ity) => {
- use rustc::ty::layout::{Integer, IntegerExt};
- use syntax::attr::SignedInt;
- let size = Integer::from_attr(&tcx, SignedInt(ity)).size();
- let a = sign_extend(a, size);
- let b = sign_extend(b, size);
- Some((a as i128).cmp(&(b as i128)))
- }
- _ => Some(a.cmp(&b)),
- };
- }
-
- if let ty::Str = ty.kind {
- match (a.val, b.val) {
- (
- ty::ConstKind::Value(a_val @ ConstValue::Slice { .. }),
- ty::ConstKind::Value(b_val @ ConstValue::Slice { .. }),
- ) => {
- let a_bytes = get_slice_bytes(&tcx, a_val);
- let b_bytes = get_slice_bytes(&tcx, b_val);
- return from_bool(a_bytes == b_bytes);
- }
- _ => (),
- }
- }
-
- fallback()
-}
+++ /dev/null
-use rustc::ty::{self, CanonicalUserType, TyCtxt, UserType};
-use rustc_hir as hir;
-
-crate trait UserAnnotatedTyHelpers<'tcx> {
- fn tcx(&self) -> TyCtxt<'tcx>;
-
- fn tables(&self) -> &ty::TypeckTables<'tcx>;
-
- /// Looks up the type associated with this hir-id and applies the
- /// user-given substitutions; the hir-id must map to a suitable
- /// type.
- fn user_substs_applied_to_ty_of_hir_id(
- &self,
- hir_id: hir::HirId,
- ) -> Option<CanonicalUserType<'tcx>> {
- let user_provided_types = self.tables().user_provided_types();
- let mut user_ty = *user_provided_types.get(hir_id)?;
- debug!("user_subts_applied_to_ty_of_hir_id: user_ty={:?}", user_ty);
- let ty = self.tables().node_type(hir_id);
- match ty.kind {
- ty::Adt(adt_def, ..) => {
- if let UserType::TypeOf(ref mut did, _) = &mut user_ty.value {
- *did = adt_def.did;
- }
- Some(user_ty)
- }
- ty::FnDef(..) => Some(user_ty),
- _ => bug!("ty: {:?} should not have user provided type {:?} recorded ", ty, user_ty),
- }
- }
-}
use rustc_span::source_map::{self, Span, DUMMY_SP};
use super::{
- Immediate, MPlaceTy, Machine, MemPlace, Memory, OpTy, Operand, Place, PlaceTy,
+ Immediate, MPlaceTy, Machine, MemPlace, MemPlaceMeta, Memory, OpTy, Operand, Place, PlaceTy,
ScalarMaybeUndef, StackPopInfo,
};
/// This can fail to provide an answer for extern types.
pub(super) fn size_and_align_of(
&self,
- metadata: Option<Scalar<M::PointerTag>>,
+ metadata: MemPlaceMeta<M::PointerTag>,
layout: TyLayout<'tcx>,
) -> InterpResult<'tcx, Option<(Size, Align)>> {
if !layout.is_unsized() {
Ok(Some((size, align)))
}
ty::Dynamic(..) => {
- let vtable = metadata.expect("dyn trait fat ptr must have vtable");
+ let vtable = metadata.unwrap_meta();
// Read size and align from vtable (already checks size).
Ok(Some(self.read_size_and_align_from_vtable(vtable)?))
}
ty::Slice(_) | ty::Str => {
- let len =
- metadata.expect("slice fat ptr must have length").to_machine_usize(self)?;
+ let len = metadata.unwrap_meta().to_machine_usize(self)?;
let elem = layout.field(self, 0)?;
// Make sure the slice is not too big.
&self,
gid: GlobalId<'tcx>,
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
- let val = if self.tcx.is_static(gid.instance.def_id()) {
- self.tcx.const_eval_poly(gid.instance.def_id())?
- } else if let Some(promoted) = gid.promoted {
- self.tcx.const_eval_promoted(gid.instance, promoted)?
+ // For statics we pick `ParamEnv::reveal_all`, because statics don't have generics
+ // and thus don't care about the parameter environment. While we could just use
+ // `self.param_env`, that would mean we invoke the query to evaluate the static
+ // with different parameter environments, thus causing the static to be evaluated
+ // multiple times.
+ let param_env = if self.tcx.is_static(gid.instance.def_id()) {
+ ty::ParamEnv::reveal_all()
} else {
- self.tcx.const_eval_instance(self.param_env, gid.instance, Some(self.tcx.span))?
+ self.param_env
};
+ let val = if let Some(promoted) = gid.promoted {
+ self.tcx.const_eval_promoted(param_env, gid.instance, promoted)?
+ } else {
+ self.tcx.const_eval_instance(param_env, gid.instance, Some(self.tcx.span))?
+ };
+
// Even though `ecx.const_eval` is called from `eval_const_to_op` we can never have a
// recursion deeper than one level, because the `tcx.const_eval` above is guaranteed to not
// return `ConstValue::Unevaluated`, which is the only way that `eval_const_to_op` will call
" by align({}){} ref:",
mplace.align.bytes(),
match mplace.meta {
- Some(meta) => format!(" meta({:?})", meta),
- None => String::new(),
+ MemPlaceMeta::Meta(meta) => format!(" meta({:?})", meta),
+ MemPlaceMeta::Poison | MemPlaceMeta::None => String::new(),
}
)
.unwrap();
/// despite the nested mutable reference!
/// The field gets updated when an `UnsafeCell` is encountered.
mutability: Mutability,
+
+ /// This flag is to avoid triggering UnsafeCells are not allowed behind references in constants
+ /// for promoteds.
+ /// It's a copy of `mir::Body`'s ignore_interior_mut_in_const_validation field
+ ignore_interior_mut_in_const_validation: bool,
}
#[derive(Copy, Clone, Debug, PartialEq, Hash, Eq)]
// References we encounter inside here are interned as pointing to mutable
// allocations.
let old = std::mem::replace(&mut self.mutability, Mutability::Mut);
- assert_ne!(
- self.mode,
- InternMode::Const,
- "UnsafeCells are not allowed behind references in constants. This should have \
- been prevented statically by const qualification. If this were allowed one \
- would be able to change a constant at one use site and other use sites could \
- observe that mutation.",
- );
+ if !self.ignore_interior_mut_in_const_validation {
+ assert_ne!(
+ self.mode,
+ InternMode::Const,
+ "UnsafeCells are not allowed behind references in constants. This should \
+ have been prevented statically by const qualification. If this were \
+ allowed one would be able to change a constant at one use site and other \
+ use sites could observe that mutation.",
+ );
+ }
let walked = self.walk_aggregate(mplace, fields);
self.mutability = old;
return walked;
{
// Validation has already errored on an invalid vtable pointer so we can safely not
// do anything if this is not a real pointer.
- if let Scalar::Ptr(vtable) = mplace.meta.unwrap() {
+ if let Scalar::Ptr(vtable) = mplace.meta.unwrap_meta() {
// Explicitly choose `Immutable` here, since vtables are immutable, even
// if the reference of the fat pointer is mutable.
self.intern_shallow(vtable.alloc_id, Mutability::Not, None)?;
| (InternMode::Const, hir::Mutability::Mut) => match referenced_ty.kind {
ty::Array(_, n)
if n.eval_usize(self.ecx.tcx.tcx, self.ecx.param_env) == 0 => {}
- ty::Slice(_) if mplace.meta.unwrap().to_machine_usize(self.ecx)? == 0 => {}
+ ty::Slice(_)
+ if mplace.meta.unwrap_meta().to_machine_usize(self.ecx)? == 0 => {}
_ => bug!("const qualif failed to prevent mutable references"),
},
}
// The `mutability` of the place, ignoring the type.
place_mut: Option<hir::Mutability>,
ret: MPlaceTy<'tcx>,
+ ignore_interior_mut_in_const_validation: bool,
) -> InterpResult<'tcx> {
let tcx = ecx.tcx;
let (base_mutability, base_intern_mode) = match place_mut {
mode,
leftover_allocations,
mutability,
+ ignore_interior_mut_in_const_validation,
}
.visit_value(mplace);
if let Err(error) = interned {
frame.locals[local].access()
}
- /// Called before a `StaticKind::Static` value is accessed.
+ /// Called before a `Static` value is accessed.
fn before_access_static(
_memory_extra: &Self::MemoryExtra,
_allocation: &Allocation,
pub use self::eval_context::{Frame, InterpCx, LocalState, LocalValue, StackPopCleanup};
-pub use self::place::{MPlaceTy, MemPlace, Place, PlaceTy};
+pub use self::place::{MPlaceTy, MemPlace, MemPlaceMeta, Place, PlaceTy};
pub use self::memory::{AllocCheck, FnVal, Memory, MemoryKind};
Indirect(MemPlace<Tag, Id>),
}
-impl<Tag> Operand<Tag> {
- #[inline]
- pub fn assert_mem_place(self) -> MemPlace<Tag>
- where
- Tag: ::std::fmt::Debug,
- {
- match self {
- Operand::Indirect(mplace) => mplace,
- _ => bug!("assert_mem_place: expected Operand::Indirect, got {:?}", self),
- }
- }
-
- #[inline]
- pub fn assert_immediate(self) -> Immediate<Tag>
- where
- Tag: ::std::fmt::Debug,
- {
- match self {
- Operand::Immediate(imm) => imm,
- _ => bug!("assert_immediate: expected Operand::Immediate, got {:?}", self),
- }
- }
-}
-
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct OpTy<'tcx, Tag = ()> {
op: Operand<Tag>, // Keep this private; it helps enforce invariants.
&self,
op: OpTy<'tcx, M::PointerTag>,
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
- match op.try_as_mplace() {
+ match op.try_as_mplace(self) {
Ok(mplace) => Ok(self.force_mplace_ptr(mplace)?.into()),
Err(imm) => Ok(imm.into()), // Nothing to cast/force
}
&self,
src: OpTy<'tcx, M::PointerTag>,
) -> InterpResult<'tcx, Result<ImmTy<'tcx, M::PointerTag>, MPlaceTy<'tcx, M::PointerTag>>> {
- Ok(match src.try_as_mplace() {
+ Ok(match src.try_as_mplace(self) {
Ok(mplace) => {
if let Some(val) = self.try_read_immediate_from_mplace(mplace)? {
Ok(val)
op: OpTy<'tcx, M::PointerTag>,
field: u64,
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
- let base = match op.try_as_mplace() {
+ let base = match op.try_as_mplace(self) {
Ok(mplace) => {
// The easy case
let field = self.mplace_field(mplace, field)?;
variant: VariantIdx,
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
// Downcasts only change the layout
- Ok(match op.try_as_mplace() {
+ Ok(match op.try_as_mplace(self) {
Ok(mplace) => self.mplace_downcast(mplace, variant)?.into(),
Err(..) => {
let layout = op.layout.for_variant(self, variant);
Field(field, _) => self.operand_field(base, field.index() as u64)?,
Downcast(_, variant) => self.operand_downcast(base, variant)?,
Deref => self.deref_operand(base)?.into(),
- ConstantIndex { .. } | Index(_) if base.layout.is_zst() => {
- OpTy {
- op: Operand::Immediate(Scalar::zst().into()),
- // the actual index doesn't matter, so we just pick a convenient one like 0
- layout: base.layout.field(self, 0)?,
- }
- }
- Subslice { from, to, from_end } if base.layout.is_zst() => {
- let elem_ty = if let ty::Array(elem_ty, _) = base.layout.ty.kind {
- elem_ty
- } else {
- bug!("slices shouldn't be zero-sized");
- };
- assert!(!from_end, "arrays shouldn't be subsliced from the end");
-
- OpTy {
- op: Operand::Immediate(Scalar::zst().into()),
- layout: self.layout_of(self.tcx.mk_array(elem_ty, (to - from) as u64))?,
- }
- }
Subslice { .. } | ConstantIndex { .. } | Index(_) => {
// The rest should only occur as mplace, we do not use Immediates for types
// allowing such operations. This matches place_projection forcing an allocation.
- let mplace = base.assert_mem_place();
+ let mplace = base.assert_mem_place(self);
self.mplace_projection(mplace, proj_elem)?.into()
}
})
place: &mir::Place<'tcx>,
layout: Option<TyLayout<'tcx>>,
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
- use rustc::mir::PlaceBase;
-
- let base_op = match &place.base {
- PlaceBase::Local(mir::RETURN_PLACE) => throw_unsup!(ReadFromReturnPointer),
- PlaceBase::Local(local) => {
+ let base_op = match place.local {
+ mir::RETURN_PLACE => throw_unsup!(ReadFromReturnPointer),
+ local => {
// Do not use the layout passed in as argument if the base we are looking at
// here is not the entire place.
- // FIXME use place_projection.is_empty() when is available
let layout = if place.projection.is_empty() { layout } else { None };
- self.access_local(self.frame(), *local, layout)?
+ self.access_local(self.frame(), local, layout)?
}
- PlaceBase::Static(place_static) => self.eval_static_to_mplace(&place_static)?.into(),
};
let op = place
// Early-return cases.
let val_val = match val.val {
ty::ConstKind::Param(_) => throw_inval!(TooGeneric),
- ty::ConstKind::Unevaluated(def_id, substs) => {
+ ty::ConstKind::Unevaluated(def_id, substs, promoted) => {
let instance = self.resolve(def_id, substs)?;
// We use `const_eval` here and `const_eval_raw` elsewhere in mir interpretation.
// The reason we use `const_eval_raw` everywhere else is to prevent cycles during
// potentially requiring the current static to be evaluated again. This is not a
// problem here, because we are building an operand which means an actual read is
// happening.
- // FIXME(oli-obk): eliminate all the `const_eval_raw` usages when we get rid of
- // `StaticKind` once and for all.
- return self.const_eval(GlobalId { instance, promoted: None });
+ return Ok(OpTy::from(self.const_eval(GlobalId { instance, promoted })?));
}
ty::ConstKind::Infer(..)
| ty::ConstKind::Bound(..)
use rustc::ty::layout::{
self, Align, HasDataLayout, LayoutOf, PrimitiveExt, Size, TyLayout, VariantIdx,
};
-use rustc::ty::TypeFoldable;
use rustc::ty::{self, Ty};
use rustc_macros::HashStable;
use super::{
- AllocId, AllocMap, Allocation, AllocationExtra, GlobalId, ImmTy, Immediate, InterpCx,
- InterpResult, LocalValue, Machine, MemoryKind, OpTy, Operand, Pointer, PointerArithmetic,
- RawConst, Scalar, ScalarMaybeUndef,
+ AllocId, AllocMap, Allocation, AllocationExtra, ImmTy, Immediate, InterpCx, InterpResult,
+ LocalValue, Machine, MemoryKind, OpTy, Operand, Pointer, PointerArithmetic, RawConst, Scalar,
+ ScalarMaybeUndef,
};
+#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable)]
+/// Information required for the sound usage of a `MemPlace`.
+pub enum MemPlaceMeta<Tag = (), Id = AllocId> {
+ /// The unsized payload (e.g. length for slices or vtable pointer for trait objects).
+ Meta(Scalar<Tag, Id>),
+ /// `Sized` types or unsized `extern type`
+ None,
+ /// The address of this place may not be taken. This protects the `MemPlace` from coming from
+ /// a ZST Operand with a backing allocation and being converted to an integer address. This
+ /// should be impossible, because you can't take the address of an operand, but this is a second
+ /// protection layer ensuring that we don't mess up.
+ Poison,
+}
+
+impl<Tag, Id> MemPlaceMeta<Tag, Id> {
+ pub fn unwrap_meta(self) -> Scalar<Tag, Id> {
+ match self {
+ Self::Meta(s) => s,
+ Self::None | Self::Poison => {
+ bug!("expected wide pointer extra data (e.g. slice length or trait object vtable)")
+ }
+ }
+ }
+ fn has_meta(self) -> bool {
+ match self {
+ Self::Meta(_) => true,
+ Self::None | Self::Poison => false,
+ }
+ }
+}
+
+impl<Tag> MemPlaceMeta<Tag> {
+ pub fn erase_tag(self) -> MemPlaceMeta<()> {
+ match self {
+ Self::Meta(s) => MemPlaceMeta::Meta(s.erase_tag()),
+ Self::None => MemPlaceMeta::None,
+ Self::Poison => MemPlaceMeta::Poison,
+ }
+ }
+}
+
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable)]
pub struct MemPlace<Tag = (), Id = AllocId> {
/// A place may have an integral pointer for ZSTs, and since it might
/// Metadata for unsized places. Interpretation is up to the type.
/// Must not be present for sized types, but can be missing for unsized types
/// (e.g., `extern type`).
- pub meta: Option<Scalar<Tag, Id>>,
+ pub meta: MemPlaceMeta<Tag, Id>,
}
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable)]
#[inline]
pub fn erase_tag(self) -> MemPlace {
- MemPlace {
- ptr: self.ptr.erase_tag(),
- align: self.align,
- meta: self.meta.map(Scalar::erase_tag),
- }
+ MemPlace { ptr: self.ptr.erase_tag(), align: self.align, meta: self.meta.erase_tag() }
}
#[inline(always)]
- pub fn from_scalar_ptr(ptr: Scalar<Tag>, align: Align) -> Self {
- MemPlace { ptr, align, meta: None }
+ fn from_scalar_ptr(ptr: Scalar<Tag>, align: Align) -> Self {
+ MemPlace { ptr, align, meta: MemPlaceMeta::None }
}
/// Produces a Place that will error if attempted to be read from or written to
#[inline(always)]
- pub fn null(cx: &impl HasDataLayout) -> Self {
+ fn null(cx: &impl HasDataLayout) -> Self {
Self::from_scalar_ptr(Scalar::ptr_null(cx), Align::from_bytes(1).unwrap())
}
#[inline(always)]
pub fn to_ref(self) -> Immediate<Tag> {
match self.meta {
- None => Immediate::Scalar(self.ptr.into()),
- Some(meta) => Immediate::ScalarPair(self.ptr.into(), meta.into()),
+ MemPlaceMeta::None => Immediate::Scalar(self.ptr.into()),
+ MemPlaceMeta::Meta(meta) => Immediate::ScalarPair(self.ptr.into(), meta.into()),
+ MemPlaceMeta::Poison => bug!(
+ "MPlaceTy::dangling may never be used to produce a \
+ place that will have the address of its pointee taken"
+ ),
}
}
pub fn offset(
self,
offset: Size,
- meta: Option<Scalar<Tag>>,
+ meta: MemPlaceMeta<Tag>,
cx: &impl HasDataLayout,
) -> InterpResult<'tcx, Self> {
Ok(MemPlace {
/// Produces a MemPlace that works for ZST but nothing else
#[inline]
pub fn dangling(layout: TyLayout<'tcx>, cx: &impl HasDataLayout) -> Self {
- MPlaceTy {
- mplace: MemPlace::from_scalar_ptr(
- Scalar::from_uint(layout.align.abi.bytes(), cx.pointer_size()),
- layout.align.abi,
- ),
- layout,
- }
+ let align = layout.align.abi;
+ let ptr = Scalar::from_uint(align.bytes(), cx.pointer_size());
+ // `Poison` this to make sure that the pointer value `ptr` is never observable by the program.
+ MPlaceTy { mplace: MemPlace { ptr, align, meta: MemPlaceMeta::Poison }, layout }
}
/// Replace ptr tag, maintain vtable tag (if any)
pub fn offset(
self,
offset: Size,
- meta: Option<Scalar<Tag>>,
+ meta: MemPlaceMeta<Tag>,
layout: TyLayout<'tcx>,
cx: &impl HasDataLayout,
) -> InterpResult<'tcx, Self> {
if self.layout.is_unsized() {
// We need to consult `meta` metadata
match self.layout.ty.kind {
- ty::Slice(..) | ty::Str => return self.mplace.meta.unwrap().to_machine_usize(cx),
+ ty::Slice(..) | ty::Str => {
+ return self.mplace.meta.unwrap_meta().to_machine_usize(cx);
+ }
_ => bug!("len not supported on unsized type {:?}", self.layout.ty),
}
} else {
#[inline]
pub(super) fn vtable(self) -> Scalar<Tag> {
match self.layout.ty.kind {
- ty::Dynamic(..) => self.mplace.meta.unwrap(),
+ ty::Dynamic(..) => self.mplace.meta.unwrap_meta(),
_ => bug!("vtable not supported on type {:?}", self.layout.ty),
}
}
// These are defined here because they produce a place.
impl<'tcx, Tag: ::std::fmt::Debug + Copy> OpTy<'tcx, Tag> {
#[inline(always)]
- pub fn try_as_mplace(self) -> Result<MPlaceTy<'tcx, Tag>, ImmTy<'tcx, Tag>> {
+ /// Note: do not call `as_ref` on the resulting place. This function should only be used to
+ /// read from the resulting mplace, not to get its address back.
+ pub fn try_as_mplace(
+ self,
+ cx: &impl HasDataLayout,
+ ) -> Result<MPlaceTy<'tcx, Tag>, ImmTy<'tcx, Tag>> {
match *self {
Operand::Indirect(mplace) => Ok(MPlaceTy { mplace, layout: self.layout }),
+ Operand::Immediate(_) if self.layout.is_zst() => {
+ Ok(MPlaceTy::dangling(self.layout, cx))
+ }
Operand::Immediate(imm) => Err(ImmTy { imm, layout: self.layout }),
}
}
#[inline(always)]
- pub fn assert_mem_place(self) -> MPlaceTy<'tcx, Tag> {
- self.try_as_mplace().unwrap()
+ /// Note: do not call `as_ref` on the resulting place. This function should only be used to
+ /// read from the resulting mplace, not to get its address back.
+ pub fn assert_mem_place(self, cx: &impl HasDataLayout) -> MPlaceTy<'tcx, Tag> {
+ self.try_as_mplace(cx).unwrap()
}
}
impl<Tag: ::std::fmt::Debug> Place<Tag> {
/// Produces a Place that will error if attempted to be read from or written to
#[inline(always)]
- pub fn null(cx: &impl HasDataLayout) -> Self {
+ fn null(cx: &impl HasDataLayout) -> Self {
Place::Ptr(MemPlace::null(cx))
}
- #[inline(always)]
- pub fn from_scalar_ptr(ptr: Scalar<Tag>, align: Align) -> Self {
- Place::Ptr(MemPlace::from_scalar_ptr(ptr, align))
- }
-
- #[inline(always)]
- pub fn from_ptr(ptr: Pointer<Tag>, align: Align) -> Self {
- Place::Ptr(MemPlace::from_ptr(ptr, align))
- }
-
#[inline]
pub fn assert_mem_place(self) -> MemPlace<Tag> {
match self {
val.layout.ty.builtin_deref(true).expect("`ref_to_mplace` called on non-ptr type").ty;
let layout = self.layout_of(pointee_type)?;
let (ptr, meta) = match *val {
- Immediate::Scalar(ptr) => (ptr.not_undef()?, None),
- Immediate::ScalarPair(ptr, meta) => (ptr.not_undef()?, Some(meta.not_undef()?)),
+ Immediate::Scalar(ptr) => (ptr.not_undef()?, MemPlaceMeta::None),
+ Immediate::ScalarPair(ptr, meta) => {
+ (ptr.not_undef()?, MemPlaceMeta::Meta(meta.not_undef()?))
+ }
};
let mplace = MemPlace {
/// On success, returns `None` for zero-sized accesses (where nothing else is
/// left to do) and a `Pointer` to use for the actual access otherwise.
#[inline]
- pub fn check_mplace_access(
+ pub(super) fn check_mplace_access(
&self,
place: MPlaceTy<'tcx, M::PointerTag>,
size: Option<Size>,
) -> InterpResult<'tcx, Option<Pointer<M::PointerTag>>> {
let size = size.unwrap_or_else(|| {
assert!(!place.layout.is_unsized());
- assert!(place.meta.is_none());
+ assert!(!place.meta.has_meta());
place.layout.size
});
self.memory.check_ptr_access(place.ptr, size, place.align)
/// Force `place.ptr` to a `Pointer`.
/// Can be helpful to avoid lots of `force_ptr` calls later, if this place is used a lot.
- pub fn force_mplace_ptr(
+ pub(super) fn force_mplace_ptr(
&self,
mut place: MPlaceTy<'tcx, M::PointerTag>,
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
} else {
// base.meta could be present; we might be accessing a sized field of an unsized
// struct.
- (None, offset)
+ (MemPlaceMeta::None, offset)
};
// We do not look at `base.layout.align` nor `field_layout.align`, unlike
// Iterates over all fields of an array. Much more efficient than doing the
// same by repeatedly calling `mplace_array`.
- pub fn mplace_array_fields(
+ pub(super) fn mplace_array_fields(
&self,
base: MPlaceTy<'tcx, Tag>,
) -> InterpResult<'tcx, impl Iterator<Item = InterpResult<'tcx, MPlaceTy<'tcx, Tag>>> + 'tcx>
};
let layout = base.layout.field(self, 0)?;
let dl = &self.tcx.data_layout;
- Ok((0..len).map(move |i| base.offset(i * stride, None, layout, dl)))
+ Ok((0..len).map(move |i| base.offset(i * stride, MemPlaceMeta::None, layout, dl)))
}
- pub fn mplace_subslice(
+ fn mplace_subslice(
&self,
base: MPlaceTy<'tcx, M::PointerTag>,
from: u64,
let (meta, ty) = match base.layout.ty.kind {
// It is not nice to match on the type, but that seems to be the only way to
// implement this.
- ty::Array(inner, _) => (None, self.tcx.mk_array(inner, inner_len)),
+ ty::Array(inner, _) => (MemPlaceMeta::None, self.tcx.mk_array(inner, inner_len)),
ty::Slice(..) => {
let len = Scalar::from_uint(inner_len, self.pointer_size());
- (Some(len), base.layout.ty)
+ (MemPlaceMeta::Meta(len), base.layout.ty)
}
_ => bug!("cannot subslice non-array type: `{:?}`", base.layout.ty),
};
base.offset(from_offset, meta, layout, self)
}
- pub fn mplace_downcast(
+ pub(super) fn mplace_downcast(
&self,
base: MPlaceTy<'tcx, M::PointerTag>,
variant: VariantIdx,
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
// Downcasts only change the layout
- assert!(base.meta.is_none());
+ assert!(!base.meta.has_meta());
Ok(MPlaceTy { layout: base.layout.for_variant(self, variant), ..base })
}
/// Project into an mplace
- pub fn mplace_projection(
+ pub(super) fn mplace_projection(
&self,
base: MPlaceTy<'tcx, M::PointerTag>,
proj_elem: &mir::PlaceElem<'tcx>,
})
}
- /// Evaluate statics and promoteds to an `MPlace`. Used to share some code between
- /// `eval_place` and `eval_place_to_op`.
- pub(super) fn eval_static_to_mplace(
- &self,
- place_static: &mir::Static<'tcx>,
- ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
- use rustc::mir::StaticKind;
-
- Ok(match place_static.kind {
- StaticKind::Promoted(promoted, promoted_substs) => {
- let substs = self.subst_from_frame_and_normalize_erasing_regions(promoted_substs);
- let instance = ty::Instance::new(place_static.def_id, substs);
-
- // Even after getting `substs` from the frame, this instance may still be
- // polymorphic because `ConstProp` will try to promote polymorphic MIR.
- if instance.needs_subst() {
- throw_inval!(TooGeneric);
- }
-
- self.const_eval_raw(GlobalId { instance, promoted: Some(promoted) })?
- }
-
- StaticKind::Static => {
- let ty = place_static.ty;
- assert!(!ty.needs_subst());
- let layout = self.layout_of(ty)?;
- // Just create a lazy reference, so we can support recursive statics.
- // tcx takes care of assigning every static one and only one unique AllocId.
- // When the data here is ever actually used, memory will notice,
- // and it knows how to deal with alloc_id that are present in the
- // global table but not in its local memory: It calls back into tcx through
- // a query, triggering the CTFE machinery to actually turn this lazy reference
- // into a bunch of bytes. IOW, statics are evaluated with CTFE even when
- // this InterpCx uses another Machine (e.g., in miri). This is what we
- // want! This way, computing statics works consistently between codegen
- // and miri: They use the same query to eventually obtain a `ty::Const`
- // and use that for further computation.
- //
- // Notice that statics have *two* AllocIds: the lazy one, and the resolved
- // one. Here we make sure that the interpreted program never sees the
- // resolved ID. Also see the doc comment of `Memory::get_static_alloc`.
- let alloc_id = self.tcx.alloc_map.lock().create_static_alloc(place_static.def_id);
- let ptr = self.tag_static_base_pointer(Pointer::from(alloc_id));
- MPlaceTy::from_aligned_ptr(ptr, layout)
- }
- })
- }
-
/// Computes a place. You should only use this if you intend to write into this
/// place; for reading, a more efficient alternative is `eval_place_for_read`.
pub fn eval_place(
&mut self,
place: &mir::Place<'tcx>,
) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
- use rustc::mir::PlaceBase;
-
- let mut place_ty = match &place.base {
- PlaceBase::Local(mir::RETURN_PLACE) => {
+ let mut place_ty = match place.local {
+ mir::RETURN_PLACE => {
// `return_place` has the *caller* layout, but we want to use our
// `layout to verify our assumption. The caller will validate
// their layout on return.
))?,
}
}
- PlaceBase::Local(local) => PlaceTy {
+ local => PlaceTy {
// This works even for dead/uninitialized locals; we check further when writing
- place: Place::Local { frame: self.cur_frame(), local: *local },
- layout: self.layout_of_local(self.frame(), *local, None)?,
+ place: Place::Local { frame: self.cur_frame(), local: local },
+ layout: self.layout_of_local(self.frame(), local, None)?,
},
- PlaceBase::Static(place_static) => self.eval_static_to_mplace(&place_static)?.into(),
};
for elem in place.projection.iter() {
pub fn force_allocation_maybe_sized(
&mut self,
place: PlaceTy<'tcx, M::PointerTag>,
- meta: Option<Scalar<M::PointerTag>>,
+ meta: MemPlaceMeta<M::PointerTag>,
) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::PointerTag>, Option<Size>)> {
let (mplace, size) = match place.place {
Place::Local { frame, local } => {
&mut self,
place: PlaceTy<'tcx, M::PointerTag>,
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
- Ok(self.force_allocation_maybe_sized(place, None)?.0)
+ Ok(self.force_allocation_maybe_sized(place, MemPlaceMeta::None)?.0)
}
pub fn allocate(
) -> MPlaceTy<'tcx, M::PointerTag> {
let ptr = self.memory.allocate_static_bytes(str.as_bytes(), kind);
let meta = Scalar::from_uint(str.len() as u128, self.pointer_size());
- let mplace =
- MemPlace { ptr: ptr.into(), align: Align::from_bytes(1).unwrap(), meta: Some(meta) };
+ let mplace = MemPlace {
+ ptr: ptr.into(),
+ align: Align::from_bytes(1).unwrap(),
+ meta: MemPlaceMeta::Meta(meta),
+ };
let layout = self.layout_of(self.tcx.mk_static_str()).unwrap();
MPlaceTy { mplace, layout }
assert_eq!(align, layout.align.abi);
}
- let mplace = MPlaceTy { mplace: MemPlace { meta: None, ..*mplace }, layout };
+ let mplace = MPlaceTy { mplace: MemPlace { meta: MemPlaceMeta::None, ..*mplace }, layout };
Ok((instance, mplace))
}
}
use syntax::ast::Mutability;
use super::eval_context::{LocalState, StackPopCleanup};
-use super::{Frame, Immediate, LocalValue, MemPlace, Memory, Operand, Place, ScalarMaybeUndef};
+use super::{
+ Frame, Immediate, LocalValue, MemPlace, MemPlaceMeta, Memory, Operand, Place, ScalarMaybeUndef,
+};
use crate::const_eval::CompileTimeInterpreter;
#[derive(Default)]
}
);
+impl_snapshot_for!(
+ enum MemPlaceMeta {
+ Meta(s),
+ None,
+ Poison,
+ }
+);
+
impl_snapshot_for!(struct MemPlace {
ptr,
meta,
}
None => {
// Unsized self.
- args[0].assert_mem_place()
+ args[0].assert_mem_place(self)
}
};
// Find and consult vtable
use std::hash::Hash;
use super::{
- CheckInAllocMsg, GlobalAlloc, InterpCx, InterpResult, MPlaceTy, Machine, OpTy, Scalar,
+ CheckInAllocMsg, GlobalAlloc, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy,
ValueVisitor,
};
fn check_wide_ptr_meta(
&mut self,
- meta: Option<Scalar<M::PointerTag>>,
+ meta: MemPlaceMeta<M::PointerTag>,
pointee: TyLayout<'tcx>,
) -> InterpResult<'tcx> {
let tail = self.ecx.tcx.struct_tail_erasing_lifetimes(pointee.ty, self.ecx.param_env);
match tail.kind {
ty::Dynamic(..) => {
- let vtable = meta.unwrap();
+ let vtable = meta.unwrap_meta();
try_validation!(
self.ecx.memory.check_ptr_access(
vtable,
}
ty::Slice(..) | ty::Str => {
let _len = try_validation!(
- meta.unwrap().to_machine_usize(self.ecx),
+ meta.unwrap_meta().to_machine_usize(self.ecx),
"non-integer slice length in wide pointer",
self.path
);
) -> InterpResult<'tcx> {
match op.layout.ty.kind {
ty::Str => {
- let mplace = op.assert_mem_place(); // strings are never immediate
+ let mplace = op.assert_mem_place(self.ecx); // strings are never immediate
try_validation!(
self.ecx.read_str(mplace),
"uninitialized or non-UTF-8 data in str",
{
// Optimized handling for arrays of integer/float type.
- // bailing out for zsts is ok, since the array element type can only be int/float
- if op.layout.is_zst() {
- return Ok(());
- }
- // non-ZST array cannot be immediate, slices are never immediate
- let mplace = op.assert_mem_place();
+ // Arrays cannot be immediate, slices are never immediate.
+ let mplace = op.assert_mem_place(self.ecx);
// This is the length of the array/slice.
let len = mplace.len(self.ecx)?;
- // zero length slices have nothing to be checked
+ // Zero length slices have nothing to be checked.
if len == 0 {
return Ok(());
}
// This is the element type size.
- let ty_size = self.ecx.layout_of(tys)?.size;
+ let layout = self.ecx.layout_of(tys)?;
+ // Empty tuples and fieldless structs (the only ZSTs that allow reaching this code)
+ // have no data to be checked.
+ if layout.is_zst() {
+ return Ok(());
+ }
// This is the size in bytes of the whole array.
- let size = ty_size * len;
+ let size = layout.size * len;
// Size is not 0, get a pointer.
let ptr = self.ecx.force_ptr(mplace.ptr)?;
// Some byte was undefined, determine which
// element that byte belongs to so we can
// provide an index.
- let i = (offset.bytes() / ty_size.bytes()) as usize;
+ let i = (offset.bytes() / layout.size.bytes()) as usize;
self.path.push(PathElem::ArrayElem(i));
throw_validation_failure!("undefined bytes", self.path)
match v.layout().ty.kind {
ty::Dynamic(..) => {
// immediate trait objects are not a thing
- let dest = v.to_op(self.ecx())?.assert_mem_place();
+ let dest = v.to_op(self.ecx())?.assert_mem_place(self.ecx());
let inner = self.ecx().unpack_dyn_trait(dest)?.1;
trace!("walk_value: dyn object layout: {:#?}", inner.layout);
// recurse with the inner type
},
layout::FieldPlacement::Array { .. } => {
// Let's get an mplace first.
- let mplace = if v.layout().is_zst() {
- // it's a ZST, the memory content cannot matter
- MPlaceTy::dangling(v.layout(), self.ecx())
- } else {
- // non-ZST array/slice/str cannot be immediate
- v.to_op(self.ecx())?.assert_mem_place()
- };
+ let mplace = v.to_op(self.ecx())?.assert_mem_place(self.ecx());
// Now we can go over all the fields.
let iter = self.ecx().mplace_array_fields(mplace)?
.map(|f| f.and_then(|f| {
#![feature(box_syntax)]
#![feature(crate_visibility_modifier)]
#![feature(core_intrinsics)]
-#![feature(const_fn)]
#![feature(decl_macro)]
#![feature(drain_filter)]
#![feature(exhaustive_patterns)]
extern crate log;
#[macro_use]
extern crate rustc;
-#[macro_use]
-extern crate syntax;
mod borrow_check;
-mod build;
pub mod const_eval;
pub mod dataflow;
-mod hair;
pub mod interpret;
-mod lints;
pub mod monomorphize;
mod shim;
pub mod transform;
pub fn provide(providers: &mut Providers<'_>) {
borrow_check::provide(providers);
+ const_eval::provide(providers);
shim::provide(providers);
transform::provide(providers);
monomorphize::partitioning::provide(providers);
providers.const_eval_validated = const_eval::const_eval_validated_provider;
providers.const_eval_raw = const_eval::const_eval_raw_provider;
- providers.check_match = hair::pattern::check_match;
providers.const_caller_location = const_eval::const_caller_location;
providers.const_field = |tcx, param_env_and_value| {
let (param_env, (value, field)) = param_env_and_value.into_parts();
const_eval::const_field(tcx, param_env, None, field, value)
};
+ providers.destructure_const = |tcx, param_env_and_value| {
+ let (param_env, value) = param_env_and_value.into_parts();
+ const_eval::destructure_const(tcx, param_env, value)
+ }
}
+++ /dev/null
-use rustc::hir::intravisit::FnKind;
-use rustc::hir::map::blocks::FnLikeNode;
-use rustc::lint::builtin::UNCONDITIONAL_RECURSION;
-use rustc::mir::{self, Body, TerminatorKind};
-use rustc::ty::subst::InternalSubsts;
-use rustc::ty::{self, AssocItem, AssocItemContainer, Instance, TyCtxt};
-use rustc_hir::def_id::DefId;
-use rustc_index::bit_set::BitSet;
-
-pub fn check(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId) {
- let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
-
- if let Some(fn_like_node) = FnLikeNode::from_node(tcx.hir().get(hir_id)) {
- check_fn_for_unconditional_recursion(tcx, fn_like_node.kind(), body, def_id);
- }
-}
-
-fn check_fn_for_unconditional_recursion(
- tcx: TyCtxt<'tcx>,
- fn_kind: FnKind<'_>,
- body: &Body<'tcx>,
- def_id: DefId,
-) {
- if let FnKind::Closure(_) = fn_kind {
- // closures can't recur, so they don't matter.
- return;
- }
-
- //FIXME(#54444) rewrite this lint to use the dataflow framework
-
- // Walk through this function (say `f`) looking to see if
- // every possible path references itself, i.e., the function is
- // called recursively unconditionally. This is done by trying
- // to find a path from the entry node to the exit node that
- // *doesn't* call `f` by traversing from the entry while
- // pretending that calls of `f` are sinks (i.e., ignoring any
- // exit edges from them).
- //
- // NB. this has an edge case with non-returning statements,
- // like `loop {}` or `panic!()`: control flow never reaches
- // the exit node through these, so one can have a function
- // that never actually calls itself but is still picked up by
- // this lint:
- //
- // fn f(cond: bool) {
- // if !cond { panic!() } // could come from `assert!(cond)`
- // f(false)
- // }
- //
- // In general, functions of that form may be able to call
- // itself a finite number of times and then diverge. The lint
- // considers this to be an error for two reasons, (a) it is
- // easier to implement, and (b) it seems rare to actually want
- // to have behaviour like the above, rather than
- // e.g., accidentally recursing after an assert.
-
- let basic_blocks = body.basic_blocks();
- let mut reachable_without_self_call_queue = vec![mir::START_BLOCK];
- let mut reached_exit_without_self_call = false;
- let mut self_call_locations = vec![];
- let mut visited = BitSet::new_empty(basic_blocks.len());
-
- let param_env = tcx.param_env(def_id);
- let trait_substs_count = match tcx.opt_associated_item(def_id) {
- Some(AssocItem { container: AssocItemContainer::TraitContainer(trait_def_id), .. }) => {
- tcx.generics_of(trait_def_id).count()
- }
- _ => 0,
- };
- let caller_substs = &InternalSubsts::identity_for_item(tcx, def_id)[..trait_substs_count];
-
- while let Some(bb) = reachable_without_self_call_queue.pop() {
- if !visited.insert(bb) {
- //already done
- continue;
- }
-
- let block = &basic_blocks[bb];
-
- if let Some(ref terminator) = block.terminator {
- match terminator.kind {
- TerminatorKind::Call { ref func, .. } => {
- let func_ty = func.ty(body, tcx);
-
- if let ty::FnDef(fn_def_id, substs) = func_ty.kind {
- let (call_fn_id, call_substs) = if let Some(instance) =
- Instance::resolve(tcx, param_env, fn_def_id, substs)
- {
- (instance.def_id(), instance.substs)
- } else {
- (fn_def_id, substs)
- };
-
- let is_self_call = call_fn_id == def_id
- && &call_substs[..caller_substs.len()] == caller_substs;
-
- if is_self_call {
- self_call_locations.push(terminator.source_info);
-
- //this is a self call so we shouldn't explore
- //further down this path
- continue;
- }
- }
- }
- TerminatorKind::Abort | TerminatorKind::Return => {
- //found a path!
- reached_exit_without_self_call = true;
- break;
- }
- _ => {}
- }
-
- for successor in terminator.successors() {
- reachable_without_self_call_queue.push(*successor);
- }
- }
- }
-
- // Check the number of self calls because a function that
- // doesn't return (e.g., calls a `-> !` function or `loop { /*
- // no break */ }`) shouldn't be linted unless it actually
- // recurs.
- if !reached_exit_without_self_call && !self_call_locations.is_empty() {
- let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
- let sp = tcx.sess.source_map().def_span(tcx.hir().span(hir_id));
- let mut db = tcx.struct_span_lint_hir(
- UNCONDITIONAL_RECURSION,
- hir_id,
- sp,
- "function cannot return without recursing",
- );
- db.span_label(sp, "cannot return without recursing");
- // offer some help to the programmer.
- for location in &self_call_locations {
- db.span_label(location.span, "recursive call site");
- }
- db.help("a `loop` may express intention better if this is on purpose");
- db.emit();
- }
-}
use rustc::mir::interpret::{ErrorHandled, GlobalAlloc, Scalar};
use rustc::mir::mono::{InstantiationMode, MonoItem};
use rustc::mir::visit::Visitor as MirVisitor;
-use rustc::mir::{self, Location, PlaceBase, Static, StaticKind};
+use rustc::mir::{self, Local, Location};
use rustc::session::config::EntryFnType;
use rustc::ty::adjustment::{CustomCoerceUnsized, PointerCast};
use rustc::ty::print::obsolete::DefPathBasedNames;
-use rustc::ty::subst::{InternalSubsts, Subst, SubstsRef};
+use rustc::ty::subst::{InternalSubsts, SubstsRef};
use rustc::ty::{self, GenericParamDefKind, Instance, Ty, TyCtxt, TypeFoldable};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::sync::{par_iter, MTLock, MTRef, ParallelIterator};
use rustc_hir::def_id::{DefId, DefIdMap, LOCAL_CRATE};
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_index::bit_set::GrowableBitSet;
-
+use smallvec::SmallVec;
use std::iter;
#[derive(PartialEq)]
}
}
- fn record_accesses<I>(&mut self, source: MonoItem<'tcx>, new_targets: I)
- where
- I: Iterator<Item = (MonoItem<'tcx>, bool)> + ExactSizeIterator,
- {
- assert!(!self.index.contains_key(&source));
-
+ fn record_accesses(&mut self, source: MonoItem<'tcx>, new_targets: &[(MonoItem<'tcx>, bool)]) {
let start_index = self.targets.len();
let new_items_count = new_targets.len();
let new_items_count_total = new_items_count + self.targets.len();
self.targets.reserve(new_items_count);
self.inlines.ensure(new_items_count_total);
- for (i, (target, inline)) in new_targets.enumerate() {
- self.targets.push(target);
- if inline {
+ for (i, (target, inline)) in new_targets.iter().enumerate() {
+ self.targets.push(*target);
+ if *inline {
self.inlines.insert(i + start_index);
}
}
let end_index = self.targets.len();
- self.index.insert(source, (start_index, end_index));
+ assert!(self.index.insert(source, (start_index, end_index)).is_none());
}
// Internally iterate over all items referenced by `source` which will be
) -> (FxHashSet<MonoItem<'_>>, InliningMap<'_>) {
let _prof_timer = tcx.prof.generic_activity("monomorphization_collector");
- let roots = tcx.sess.time("collecting roots", || {
- let _prof_timer = tcx.prof.generic_activity("monomorphization_collector_root_collections");
- collect_roots(tcx, mode)
- });
+ let roots =
+ tcx.sess.time("monomorphization_collector_root_collections", || collect_roots(tcx, mode));
debug!("building mono item graph, beginning at roots");
let visited: MTRef<'_, _> = &mut visited;
let inlining_map: MTRef<'_, _> = &mut inlining_map;
- tcx.sess.time("collecting mono items", || {
+ tcx.sess.time("monomorphization_collector_graph_walk", || {
par_iter(roots).for_each(|root| {
let mut recursion_depths = DefIdMap::default();
collect_items_rec(tcx, root, visited, &mut recursion_depths, inlining_map);
mono_item.instantiation_mode(tcx) == InstantiationMode::LocalCopy
};
- let accesses =
- callees.into_iter().map(|mono_item| (*mono_item, is_inlining_candidate(mono_item)));
+ // We collect this into a `SmallVec` to avoid calling `is_inlining_candidate` in the lock.
+ // FIXME: Call `is_inlining_candidate` when pushing to `neighbors` in `collect_items_rec`
+ // instead to avoid creating this `SmallVec`.
+ let accesses: SmallVec<[_; 128]> = callees
+ .into_iter()
+ .map(|mono_item| (*mono_item, is_inlining_candidate(mono_item)))
+ .collect();
- inlining_map.lock_mut().record_accesses(caller, accesses);
+ inlining_map.lock_mut().record_accesses(caller, &accesses);
}
fn check_recursion_limit<'tcx>(
fn visit_place_base(
&mut self,
- place_base: &mir::PlaceBase<'tcx>,
+ _place_local: &Local,
_context: mir::visit::PlaceContext,
- location: Location,
+ _location: Location,
) {
- match place_base {
- PlaceBase::Static(box Static { kind: StaticKind::Static, def_id, .. }) => {
- debug!("visiting static {:?} @ {:?}", def_id, location);
-
- let tcx = self.tcx;
- let instance = Instance::mono(tcx, *def_id);
- if should_monomorphize_locally(tcx, &instance) {
- self.output.push(MonoItem::Static(*def_id));
- }
- }
- PlaceBase::Static(box Static {
- kind: StaticKind::Promoted(promoted, substs),
- def_id,
- ..
- }) => {
- let instance = Instance::new(*def_id, substs.subst(self.tcx, self.param_substs));
- match self.tcx.const_eval_promoted(instance, *promoted) {
- Ok(val) => collect_const(self.tcx, val, substs, self.output),
- Err(ErrorHandled::Reported) => {}
- Err(ErrorHandled::TooGeneric) => {
- let span = self.tcx.promoted_mir(*def_id)[*promoted].span;
- span_bug!(span, "collection encountered polymorphic constant")
- }
- }
- }
- PlaceBase::Local(_) => {
- // Locals have no relevance for collector.
- }
- }
}
}
collect_miri(tcx, id, output);
}
}
- ty::ConstKind::Unevaluated(def_id, substs) => {
- match tcx.const_eval_resolve(param_env, def_id, substs, None) {
+ ty::ConstKind::Unevaluated(def_id, substs, promoted) => {
+ match tcx.const_eval_resolve(param_env, def_id, substs, promoted, None) {
Ok(val) => collect_const(tcx, val, param_substs, output),
Err(ErrorHandled::Reported) => {}
Err(ErrorHandled::TooGeneric) => {
use rustc::ty::query::Providers;
use rustc::ty::{self, DefIdTree, InstanceDef, TyCtxt};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::sync;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{CrateNum, DefId, DefIdSet, CRATE_DEF_INDEX, LOCAL_CRATE};
use rustc_span::symbol::Symbol;
I: Iterator<Item = &'a MonoItem<'tcx>>,
'tcx: 'a,
{
+ let _prof_timer = tcx.prof.generic_activity("assert_symbols_are_distinct");
+
let mut symbols: Vec<_> =
mono_items.map(|mono_item| (mono_item, mono_item.symbol_name(tcx))).collect();
}
};
- let (items, inlining_map) = tcx.sess.time("monomorphization collection", || {
- collector::collect_crate_mono_items(tcx, collection_mode)
- });
+ let (items, inlining_map) = collector::collect_crate_mono_items(tcx, collection_mode);
tcx.sess.abort_if_errors();
- assert_symbols_are_distinct(tcx, items.iter());
-
- let strategy = if tcx.sess.opts.incremental.is_some() {
- PartitioningStrategy::PerModule
- } else {
- PartitioningStrategy::FixedUnitCount(tcx.sess.codegen_units())
- };
-
- let codegen_units = tcx.sess.time("codegen unit partitioning", || {
- partition(tcx, items.iter().cloned(), strategy, &inlining_map)
- .into_iter()
- .map(Arc::new)
- .collect::<Vec<_>>()
+ let (codegen_units, _) = tcx.sess.time("partition_and_assert_distinct_symbols", || {
+ sync::join(
+ || {
+ let strategy = if tcx.sess.opts.incremental.is_some() {
+ PartitioningStrategy::PerModule
+ } else {
+ PartitioningStrategy::FixedUnitCount(tcx.sess.codegen_units())
+ };
+
+ partition(tcx, items.iter().cloned(), strategy, &inlining_map)
+ .into_iter()
+ .map(Arc::new)
+ .collect::<Vec<_>>()
+ },
+ || assert_symbols_are_distinct(tcx, items.iter()),
+ )
});
let mono_items: DefIdSet = items
//! Concrete error types for all operations which may be invalid in a certain const context.
use rustc::session::config::nightly_options;
+use rustc::session::parse::feature_err;
use rustc::ty::TyCtxt;
+use rustc_errors::struct_span_err;
use rustc_hir::def_id::DefId;
use rustc_span::symbol::sym;
use rustc_span::{Span, Symbol};
-use syntax::feature_gate::feature_err;
use super::{ConstKind, Item};
&format!("`{}` is not yet stable as a const fn", item.tcx.def_path_str(def_id)),
);
if nightly_options::is_nightly_build() {
- help!(
- &mut err,
- "add `#![feature({})]` to the \
- crate attributes to enable",
- feature
- );
+ err.help(&format!("add `#![feature({})]` to the crate attributes to enable", feature));
}
err.emit();
}
pub struct CellBorrow;
impl NonConstOp for CellBorrow {
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
- span_err!(
+ struct_span_err!(
item.tcx.sess,
span,
E0492,
"cannot borrow a constant which may contain \
interior mutability, create a static instead"
- );
+ )
+ .emit();
}
}
item.tcx.sess,
span,
E0013,
- "{}s cannot refer to statics, use \
- a constant instead",
+ "{}s cannot refer to statics",
item.const_kind()
);
+ err.help(
+ "consider extracting the value of the `static` to a `const`, and referring to that",
+ );
if item.tcx.sess.teach(&err.get_code().unwrap()) {
err.note(
- "Static and const variables can refer to other const variables. \
- But a const variable cannot refer to a static variable.",
+ "`static` and `const` variables can refer to other `const` variables. \
+ A `const` variable, however, cannot refer to a `static` variable.",
);
- err.help("To fix this, the value can be extracted as a const and then used.");
+ err.help("To fix this, the value can be extracted to a `const` and then used.");
}
err.emit();
}
const IS_SUPPORTED_IN_MIRI: bool = false;
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
- span_err!(
+ struct_span_err!(
item.tcx.sess,
span,
E0625,
"thread-local statics cannot be \
accessed at compile-time"
- );
+ )
+ .emit();
}
}
place: PlaceRef<'_, 'tcx>,
) -> bool {
if let [proj_base @ .., elem] = place.projection {
- let base_qualif =
- Self::in_place(cx, per_local, PlaceRef { base: place.base, projection: proj_base });
+ let base_qualif = Self::in_place(
+ cx,
+ per_local,
+ PlaceRef { local: place.local, projection: proj_base },
+ );
let qualif = base_qualif
&& Self::in_any_value_of_ty(
cx,
- Place::ty_from(place.base, proj_base, *cx.body, cx.tcx)
+ Place::ty_from(place.local, proj_base, *cx.body, cx.tcx)
.projection_ty(cx.tcx, elem)
.ty,
);
place: PlaceRef<'_, 'tcx>,
) -> bool {
match place {
- PlaceRef { base: PlaceBase::Local(local), projection: [] } => per_local(*local),
- PlaceRef { base: PlaceBase::Static(_), projection: [] } => {
- bug!("qualifying already promoted MIR")
- }
- PlaceRef { base: _, projection: [.., _] } => Self::in_projection(cx, per_local, place),
+ PlaceRef { local, projection: [] } => per_local(*local),
+ PlaceRef { local: _, projection: [.., _] } => Self::in_projection(cx, per_local, place),
}
}
// Note: this uses `constant.literal.ty` which is a reference or pointer to the
// type of the actual `static` item.
Self::in_any_value_of_ty(cx, constant.literal.ty)
- } else if let ty::ConstKind::Unevaluated(def_id, _) = constant.literal.val {
+ } else if let ty::ConstKind::Unevaluated(def_id, _, promoted) = constant.literal.val
+ {
+ assert!(promoted.is_none());
// Don't peek inside trait associated constants.
if cx.tcx.trait_of_item(def_id).is_some() {
Self::in_any_value_of_ty(cx, constant.literal.ty)
Rvalue::Ref(_, _, ref place) | Rvalue::AddressOf(_, ref place) => {
// Special-case reborrows to be more like a copy of the reference.
if let [proj_base @ .., ProjectionElem::Deref] = place.projection.as_ref() {
- let base_ty = Place::ty_from(&place.base, proj_base, *cx.body, cx.tcx).ty;
+ let base_ty = Place::ty_from(&place.local, proj_base, *cx.body, cx.tcx).ty;
if let ty::Ref(..) = base_ty.kind {
return Self::in_place(
cx,
per_local,
- PlaceRef { base: &place.base, projection: proj_base },
+ PlaceRef { local: &place.local, projection: proj_base },
);
}
}
debug_assert!(!place.is_indirect());
match (value, place.as_ref()) {
- (true, mir::PlaceRef { base: &mir::PlaceBase::Local(local), .. }) => {
- self.qualifs_per_local.insert(local);
+ (true, mir::PlaceRef { local, .. }) => {
+ self.qualifs_per_local.insert(*local);
}
// For now, we do not clear the qualif if a local is overwritten in full by
// an unqualified rvalue (e.g. `y = 5`). This is to be consistent
// with aggregates where we overwrite all fields with assignments, which would not
// get this feature.
- (false, mir::PlaceRef { base: &mir::PlaceBase::Local(_local), projection: &[] }) => {
+ (false, mir::PlaceRef { local: _, projection: &[] }) => {
// self.qualifs_per_local.remove(*local);
}
use rustc::ty::cast::CastTy;
use rustc::ty::{self, TyCtxt};
use rustc_error_codes::*;
+use rustc_errors::struct_span_err;
use rustc_hir::{def_id::DefId, HirId};
use rustc_index::bit_set::BitSet;
use rustc_span::symbol::sym;
use super::qualifs::{self, HasMutInterior, NeedsDrop};
use super::resolver::FlowSensitiveAnalysis;
use super::{is_lang_panic_fn, ConstKind, Item, Qualif};
+use crate::const_eval::{is_const_fn, is_unstable_const_fn};
use crate::dataflow::{self as old_dataflow, generic as dataflow};
pub type IndirectlyMutableResults<'mir, 'tcx> =
let Item { tcx, body, def_id, const_kind, .. } = *self.item;
let use_min_const_fn_checks = (const_kind == Some(ConstKind::ConstFn)
- && tcx.is_min_const_fn(def_id))
+ && crate::const_eval::is_min_const_fn(tcx, def_id))
&& !tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you;
if use_min_const_fn_checks {
PlaceContext::MutatingUse(MutatingUseContext::Borrow)
}
};
- self.visit_place_base(&place.base, ctx, location);
- self.visit_projection(&place.base, reborrowed_proj, ctx, location);
+ self.visit_place_base(&place.local, ctx, location);
+ self.visit_projection(&place.local, reborrowed_proj, ctx, location);
return;
}
}
}
Mutability::Mut => PlaceContext::MutatingUse(MutatingUseContext::AddressOf),
};
- self.visit_place_base(&place.base, ctx, location);
- self.visit_projection(&place.base, reborrowed_proj, ctx, location);
+ self.visit_place_base(&place.local, ctx, location);
+ self.visit_projection(&place.local, reborrowed_proj, ctx, location);
return;
}
}
Rvalue::AddressOf(Mutability::Mut, _) => self.check_op(ops::MutAddressOf),
- // At the moment, `PlaceBase::Static` is only used for promoted MIR.
- Rvalue::Ref(_, BorrowKind::Shared, ref place)
- | Rvalue::Ref(_, BorrowKind::Shallow, ref place)
- | Rvalue::AddressOf(Mutability::Not, ref place)
- if matches!(place.base, PlaceBase::Static(_)) =>
- {
- bug!("Saw a promoted during const-checking, which must run before promotion")
- }
-
Rvalue::Ref(_, BorrowKind::Shared, ref place)
| Rvalue::Ref(_, BorrowKind::Shallow, ref place) => {
self.check_immutable_borrow_like(location, place)
}
}
- fn visit_place_base(
- &mut self,
- place_base: &PlaceBase<'tcx>,
- context: PlaceContext,
- location: Location,
- ) {
+ fn visit_place_base(&mut self, place_local: &Local, context: PlaceContext, location: Location) {
trace!(
- "visit_place_base: place_base={:?} context={:?} location={:?}",
- place_base,
+ "visit_place_base: place_local={:?} context={:?} location={:?}",
+ place_local,
context,
location,
);
- self.super_place_base(place_base, context, location);
-
- match place_base {
- PlaceBase::Local(_) => {}
- PlaceBase::Static(_) => {
- bug!("Promotion must be run after const validation");
- }
- }
+ self.super_place_base(place_local, context, location);
}
fn visit_operand(&mut self, op: &Operand<'tcx>, location: Location) {
}
fn visit_projection_elem(
&mut self,
- place_base: &PlaceBase<'tcx>,
+ place_local: &Local,
proj_base: &[PlaceElem<'tcx>],
elem: &PlaceElem<'tcx>,
context: PlaceContext,
location: Location,
) {
trace!(
- "visit_projection_elem: place_base={:?} proj_base={:?} elem={:?} \
+ "visit_projection_elem: place_local={:?} proj_base={:?} elem={:?} \
context={:?} location={:?}",
- place_base,
+ place_local,
proj_base,
elem,
context,
location,
);
- self.super_projection_elem(place_base, proj_base, elem, context, location);
+ self.super_projection_elem(place_local, proj_base, elem, context, location);
match elem {
ProjectionElem::Deref => {
- let base_ty = Place::ty_from(place_base, proj_base, *self.body, self.tcx).ty;
+ let base_ty = Place::ty_from(place_local, proj_base, *self.body, self.tcx).ty;
if let ty::RawPtr(_) = base_ty.kind {
if proj_base.is_empty() {
- if let (PlaceBase::Local(local), []) = (place_base, proj_base) {
+ if let (local, []) = (place_local, proj_base) {
let decl = &self.body.local_decls[*local];
if let LocalInfo::StaticRef { def_id, .. } = decl.local_info {
let span = decl.source_info.span;
| ProjectionElem::Subslice { .. }
| ProjectionElem::Field(..)
| ProjectionElem::Index(_) => {
- let base_ty = Place::ty_from(place_base, proj_base, *self.body, self.tcx).ty;
+ let base_ty = Place::ty_from(place_local, proj_base, *self.body, self.tcx).ty;
match base_ty.ty_adt_def() {
Some(def) if def.is_union() => {
self.check_op(ops::UnionAccess);
};
// At this point, we are calling a function whose `DefId` is known...
- if self.tcx.is_const_fn(def_id) {
+ if is_const_fn(self.tcx, def_id) {
return;
}
if is_lang_panic_fn(self.tcx, def_id) {
self.check_op(ops::Panic);
- } else if let Some(feature) = self.tcx.is_unstable_const_fn(def_id) {
+ } else if let Some(feature) = is_unstable_const_fn(self.tcx, def_id) {
// Exempt unstable const fns inside of macros with
// `#[allow_internal_unstable]`.
if !self.span.allows_unstable(feature) {
// A borrow of a `static` also looks like `&(*_1)` in the MIR, but `_1` is a `const`
// that points to the allocation for the static. Don't treat these as reborrows.
- if let PlaceBase::Local(local) = place.base {
- if body.local_decls[local].is_ref_to_static() {
- return None;
- }
+ if body.local_decls[place.local].is_ref_to_static() {
+ return None;
}
// Ensure the type being derefed is a reference and not a raw pointer.
//
// This is sufficient to prevent an access to a `static mut` from being marked as a
// reborrow, even if the check above were to disappear.
- let inner_ty = Place::ty_from(&place.base, inner, body, tcx).ty;
+ let inner_ty = Place::ty_from(&place.local, inner, body, tcx).ty;
match inner_ty.kind {
ty::Ref(..) => Some(inner),
_ => None,
-use rustc::hir::intravisit;
+use rustc::hir::map::Map;
use rustc::lint::builtin::{SAFE_PACKED_BORROWS, UNUSED_UNSAFE};
use rustc::mir::visit::{MutatingUseContext, PlaceContext, Visitor};
use rustc::mir::*;
use rustc::ty::query::Providers;
use rustc::ty::{self, TyCtxt};
use rustc_data_structures::fx::FxHashSet;
+use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
+use rustc_hir::intravisit;
use rustc_hir::Node;
use rustc_span::symbol::{sym, Symbol};
use std::ops::Bound;
+use crate::const_eval::{is_const_fn, is_min_const_fn};
use crate::util;
use rustc_error_codes::*;
}
fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: Location) {
- match place.base {
- PlaceBase::Local(..) => {
- // Locals are safe.
- }
- PlaceBase::Static(box Static { kind: StaticKind::Promoted(_, _), .. }) => {
- bug!("unsafety checking should happen before promotion");
- }
- PlaceBase::Static(box Static { kind: StaticKind::Static, .. }) => {
- bug!("StaticKind::Static should not exist");
- }
- }
-
for (i, elem) in place.projection.iter().enumerate() {
let proj_base = &place.projection[..i];
}
}
let is_borrow_of_interior_mut = context.is_borrow()
- && !Place::ty_from(&place.base, proj_base, self.body, self.tcx).ty.is_freeze(
+ && !Place::ty_from(&place.local, proj_base, self.body, self.tcx).ty.is_freeze(
self.tcx,
self.param_env,
self.source_info.span,
self.check_mut_borrowing_layout_constrained_field(place, context.is_mutating_use());
}
let old_source_info = self.source_info;
- if let (PlaceBase::Local(local), []) = (&place.base, proj_base) {
+ if let (local, []) = (&place.local, proj_base) {
let decl = &self.body.local_decls[*local];
if decl.internal {
// Internal locals are used in the `move_val_init` desugaring.
}
}
}
- let base_ty = Place::ty_from(&place.base, proj_base, self.body, self.tcx).ty;
+ let base_ty = Place::ty_from(&place.local, proj_base, self.body, self.tcx).ty;
match base_ty.kind {
ty::RawPtr(..) => self.require_unsafe(
"dereference of raw pointer",
match elem {
ProjectionElem::Field(..) => {
let ty =
- Place::ty_from(&place.base, proj_base, &self.body.local_decls, self.tcx).ty;
+ Place::ty_from(&place.local, proj_base, &self.body.local_decls, self.tcx)
+ .ty;
match ty.kind {
ty::Adt(def, _) => match self.tcx.layout_scalar_valid_range(def.did) {
(Bound::Unbounded, Bound::Unbounded) => {}
}
impl<'a, 'tcx> intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'a> {
- fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, Self::Map> {
intravisit::NestedVisitorMap::None
}
let id = tcx.hir().as_local_hir_id(def_id).unwrap();
let (const_context, min_const_fn) = match tcx.hir().body_owner_kind(id) {
hir::BodyOwnerKind::Closure => (false, false),
- hir::BodyOwnerKind::Fn => (tcx.is_const_fn(def_id), tcx.is_min_const_fn(def_id)),
+ hir::BodyOwnerKind::Fn => (is_const_fn(tcx, def_id), is_min_const_fn(tcx, def_id)),
hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => (true, false),
};
let mut checker = UnsafetyChecker::new(const_context, min_const_fn, body, tcx, param_env);
if let Some(impl_def_id) = builtin_derive_def_id(tcx, def_id) {
tcx.unsafe_derive_on_repr_packed(impl_def_id);
} else {
- tcx.lint_node_note(
+ tcx.struct_span_lint_hir(
SAFE_PACKED_BORROWS,
lint_hir_id,
source_info.span,
&format!(
- "{} is unsafe and requires unsafe function or block \
- (error E0133)",
+ "{} is unsafe and requires unsafe function or block (error E0133)",
description
),
- &details.as_str(),
- );
+ )
+ .note(&details.as_str())
+ .emit();
}
}
}
};
use rustc::mir::{
read_only, AggregateKind, BasicBlock, BinOp, Body, BodyAndCache, ClearCrossCrate, Constant,
- Local, LocalDecl, LocalKind, Location, Operand, Place, PlaceBase, ReadOnlyBodyAndCache, Rvalue,
+ Local, LocalDecl, LocalKind, Location, Operand, Place, ReadOnlyBodyAndCache, Rvalue,
SourceInfo, SourceScope, SourceScopeData, Statement, StatementKind, Terminator, TerminatorKind,
UnOp, RETURN_PLACE,
};
use rustc::ty::layout::{
HasDataLayout, HasTyCtxt, LayoutError, LayoutOf, Size, TargetDataLayout, TyLayout,
};
-use rustc::ty::subst::InternalSubsts;
-use rustc::ty::{self, Instance, ParamEnv, Ty, TyCtxt, TypeFoldable};
+use rustc::ty::subst::{InternalSubsts, Subst};
+use rustc::ty::{self, ConstKind, Instance, ParamEnv, Ty, TyCtxt, TypeFoldable};
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId;
LocalState, LocalValue, Memory, MemoryKind, OpTy, Operand as InterpOperand, PlaceTy, Pointer,
ScalarMaybeUndef, StackPopCleanup,
};
-use crate::rustc::ty::subst::Subst;
use crate::transform::{MirPass, MirSource};
/// The maximum number of bytes that we'll allocate space for a return value.
source_info: SourceInfo,
) -> Option<Const<'tcx>> {
self.ecx.tcx.span = c.span;
+
+ // FIXME we need to revisit this for #67176
+ if c.needs_subst() {
+ return None;
+ }
+
match self.ecx.eval_const_to_op(c.literal, None) {
Ok(op) => Some(op),
Err(error) => {
let err = error_to_const_error(&self.ecx, error);
- match self.lint_root(source_info) {
- Some(lint_root) if c.literal.needs_subst() => {
+ if let Some(lint_root) = self.lint_root(source_info) {
+ let lint_only = match c.literal.val {
+ // Promoteds must lint and not error as the user didn't ask for them
+ ConstKind::Unevaluated(_, _, Some(_)) => true,
+ // Out of backwards compatibility we cannot report hard errors in unused
+ // generic functions using associated constants of the generic parameters.
+ _ => c.literal.needs_subst(),
+ };
+ if lint_only {
// Out of backwards compatibility we cannot report hard errors in unused
// generic functions using associated constants of the generic parameters.
err.report_as_lint(
lint_root,
Some(c.span),
);
- }
- _ => {
+ } else {
err.report_as_error(self.ecx.tcx, "erroneous constant used");
}
+ } else {
+ err.report_as_error(self.ecx.tcx, "erroneous constant used");
}
None
}
return None;
}
+ // FIXME we need to revisit this for #67176
+ if rvalue.needs_subst() {
+ return None;
+ }
+
let overflow_check = self.tcx.sess.overflow_checks();
// Perform any special handling for specific Rvalue types.
ScalarMaybeUndef::Scalar(r),
)) => l.is_bits() && r.is_bits(),
interpret::Operand::Indirect(_) if mir_opt_level >= 2 => {
- intern_const_alloc_recursive(&mut self.ecx, None, op.assert_mem_place())
+ let mplace = op.assert_mem_place(&self.ecx);
+ intern_const_alloc_recursive(&mut self.ecx, None, mplace, false)
.expect("failed to intern alloc");
true
}
// doesn't use the invalid value
match cond {
Operand::Move(ref place) | Operand::Copy(ref place) => {
- if let PlaceBase::Local(local) = place.base {
- self.remove_const(local);
- }
+ self.remove_const(place.local);
}
Operand::Constant(_) => {}
}
let param_env = tcx.param_env(src.def_id()).with_reveal_all();
let move_data = match MoveData::gather_moves(body, tcx, param_env) {
Ok(move_data) => move_data,
- Err(_) => bug!("No `move_errors` should be allowed in MIR borrowck"),
+ Err((move_data, _)) => {
+ tcx.sess.delay_span_bug(
+ body.span,
+ "No `move_errors` should be allowed in MIR borrowck",
+ );
+ move_data
+ }
};
let elaborate_patch = {
let body = &*body;
}
fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) {
- if place.base == PlaceBase::Local(self_arg()) {
+ if place.local == self_arg() {
replace_base(
place,
Place {
- base: PlaceBase::Local(self_arg()),
+ local: self_arg(),
projection: self.tcx().intern_place_elems(&vec![ProjectionElem::Deref]),
},
self.tcx,
);
} else {
- self.visit_place_base(&mut place.base, context, location);
+ self.visit_place_base(&mut place.local, context, location);
for elem in place.projection.iter() {
if let PlaceElem::Index(local) = elem {
}
fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) {
- if place.base == PlaceBase::Local(self_arg()) {
+ if place.local == self_arg() {
replace_base(
place,
Place {
- base: PlaceBase::Local(self_arg()),
+ local: self_arg(),
projection: self.tcx().intern_place_elems(&vec![ProjectionElem::Field(
Field::new(0),
self.ref_gen_ty,
self.tcx,
);
} else {
- self.visit_place_base(&mut place.base, context, location);
+ self.visit_place_base(&mut place.local, context, location);
for elem in place.projection.iter() {
if let PlaceElem::Index(local) = elem {
}
fn replace_base<'tcx>(place: &mut Place<'tcx>, new_base: Place<'tcx>, tcx: TyCtxt<'tcx>) {
- place.base = new_base.base;
+ place.local = new_base.local;
let mut new_projection = new_base.projection.to_vec();
new_projection.append(&mut place.projection.to_vec());
let mut projection = base.projection.to_vec();
projection.push(ProjectionElem::Field(Field::new(idx), ty));
- Place { base: base.base, projection: self.tcx.intern_place_elems(&projection) }
+ Place { local: base.local, projection: self.tcx.intern_place_elems(&projection) }
}
// Create a statement which changes the discriminant
assert_eq!(self.remap.get(local), None);
}
- fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) {
- if let PlaceBase::Local(l) = place.base {
- // Replace an Local in the remap with a generator struct access
- if let Some(&(ty, variant_index, idx)) = self.remap.get(&l) {
- replace_base(place, self.make_field(variant_index, idx, ty), self.tcx);
- }
- } else {
- self.visit_place_base(&mut place.base, context, location);
-
- for elem in place.projection.iter() {
- if let PlaceElem::Index(local) = elem {
- assert_ne!(*local, self_arg());
- }
- }
+ fn visit_place(
+ &mut self,
+ place: &mut Place<'tcx>,
+ _context: PlaceContext,
+ _location: Location,
+ ) {
+ // Replace an Local in the remap with a generator struct access
+ if let Some(&(ty, variant_index, idx)) = self.remap.get(&place.local) {
+ replace_base(place, self.make_field(variant_index, idx, ty), self.tcx);
}
}
}
}
- match place.base {
- // Static variables need a borrow because the callee
- // might modify the same static.
- PlaceBase::Static(_) => true,
- _ => false,
- }
+ false
}
let dest = if dest_needs_borrow(&destination.0) {
fn make_integrate_local(&self, local: &Local) -> Local {
if *local == RETURN_PLACE {
- match self.destination.base {
- PlaceBase::Local(l) => return l,
- PlaceBase::Static(ref s) => bug!("Return place is {:?}, not local", s),
- }
+ return self.destination.local;
}
let idx = local.index() - 1;
}
fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) {
- match &mut place.base {
- PlaceBase::Static(_) => {}
- PlaceBase::Local(l) => {
- // If this is the `RETURN_PLACE`, we need to rebase any projections onto it.
- let dest_proj_len = self.destination.projection.len();
- if *l == RETURN_PLACE && dest_proj_len > 0 {
- let mut projs = Vec::with_capacity(dest_proj_len + place.projection.len());
- projs.extend(self.destination.projection);
- projs.extend(place.projection);
-
- place.projection = self.tcx.intern_place_elems(&*projs);
- }
- }
+ // If this is the `RETURN_PLACE`, we need to rebase any projections onto it.
+ let dest_proj_len = self.destination.projection.len();
+ if place.local == RETURN_PLACE && dest_proj_len > 0 {
+ let mut projs = Vec::with_capacity(dest_proj_len + place.projection.len());
+ projs.extend(self.destination.projection);
+ projs.extend(place.projection);
+
+ place.projection = self.tcx.intern_place_elems(&*projs);
}
// Handles integrating any locals that occur in the base
// or projections
use crate::transform::{MirPass, MirSource};
use rustc::mir::visit::{MutVisitor, Visitor};
use rustc::mir::{
- read_only, Body, BodyAndCache, Constant, Local, Location, Operand, Place, PlaceBase, PlaceRef,
+ read_only, Body, BodyAndCache, Constant, Local, Location, Operand, Place, PlaceRef,
ProjectionElem, Rvalue,
};
use rustc::ty::{self, TyCtxt};
Place {
// Replace with dummy
- base: mem::replace(&mut place.base, PlaceBase::Local(Local::new(0))),
+ local: mem::replace(&mut place.local, Local::new(0)),
projection: self.tcx().intern_place_elems(proj_l),
}
} else {
impl Visitor<'tcx> for OptimizationFinder<'b, 'tcx> {
fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
if let Rvalue::Ref(_, _, place) = rvalue {
- if let PlaceRef { base, projection: &[ref proj_base @ .., ProjectionElem::Deref] } =
+ if let PlaceRef { local, projection: &[ref proj_base @ .., ProjectionElem::Deref] } =
place.as_ref()
{
- if Place::ty_from(base, proj_base, self.body, self.tcx).ty.is_region_ptr() {
+ if Place::ty_from(local, proj_base, self.body, self.tcx).ty.is_region_ptr() {
self.optimizations.and_stars.insert(location);
}
}
-use crate::{build, shim};
-use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor};
+use crate::{shim, util};
+use rustc::hir::map::Map;
use rustc::mir::{BodyAndCache, ConstQualifs, MirPhase, Promoted};
use rustc::ty::query::Providers;
use rustc::ty::steal::Steal;
use rustc::ty::{InstanceDef, TyCtxt, TypeFoldable};
use rustc_hir as hir;
use rustc_hir::def_id::{CrateNum, DefId, DefIdSet, LOCAL_CRATE};
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_index::vec::IndexVec;
use rustc_span::Span;
use std::borrow::Cow;
self::check_unsafety::provide(providers);
*providers = Providers {
mir_keys,
- mir_built,
mir_const,
mir_const_qualif,
mir_validated,
}
intravisit::walk_struct_def(self, v)
}
- fn nested_visit_map<'b>(&'b mut self) -> NestedVisitorMap<'b, 'tcx> {
+ type Map = Map<'tcx>;
+ fn nested_visit_map<'b>(&'b mut self) -> NestedVisitorMap<'b, Self::Map> {
NestedVisitorMap::None
}
}
tcx.arena.alloc(set)
}
-fn mir_built(tcx: TyCtxt<'_>, def_id: DefId) -> &Steal<BodyAndCache<'_>> {
- let mir = build::mir_build(tcx, def_id);
- tcx.alloc_steal_mir(mir)
-}
-
/// Where a specific `mir::Body` comes from.
#[derive(Debug, Copy, Clone)]
pub struct MirSource<'tcx> {
let _ = tcx.unsafety_check_result(def_id);
let mut body = tcx.mir_built(def_id).steal();
+
+ util::dump_mir(tcx, None, "mir_map", &0, MirSource::item(def_id), &body, |_, _| Ok(()));
+
run_passes(
tcx,
&mut body,
use rustc_target::spec::abi::Abi;
use std::cell::Cell;
-use std::{iter, mem, usize};
+use std::{cmp, iter, mem, usize};
+use crate::const_eval::{is_const_fn, is_unstable_const_fn};
use crate::transform::check_consts::{is_lang_panic_fn, qualifs, ConstKind, Item};
use crate::transform::{MirPass, MirSource};
/// errors when promotion of `#[rustc_args_required_const]` arguments fails.
///
/// After this pass is run, `promoted_fragments` will hold the MIR body corresponding to each
-/// newly created `StaticKind::Promoted`.
+/// newly created `Constant`.
#[derive(Default)]
pub struct PromoteTemps<'tcx> {
pub promoted_fragments: Cell<IndexVec<Promoted, BodyAndCache<'tcx>>>,
// We can only promote interior borrows of promotable temps (non-temps
// don't get promoted anyway).
- let base = match place.base {
- PlaceBase::Local(local) => local,
- _ => return Err(Unpromotable),
- };
- self.validate_local(base)?;
+ self.validate_local(place.local)?;
if place.projection.contains(&ProjectionElem::Deref) {
return Err(Unpromotable);
}
let mut has_mut_interior =
- self.qualif_local::<qualifs::HasMutInterior>(base);
+ self.qualif_local::<qualifs::HasMutInterior>(place.local);
// HACK(eddyb) this should compute the same thing as
// `<HasMutInterior as Qualif>::in_projection` from
// `check_consts::qualifs` but without recursion.
// FIXME(eddyb) this is probably excessive, with
// the exception of `union` member accesses.
let ty =
- Place::ty_from(&place.base, proj_base, *self.body, self.tcx)
+ Place::ty_from(&place.local, proj_base, *self.body, self.tcx)
.projection_ty(self.tcx, elem)
.ty;
if ty.is_freeze(self.tcx, self.param_env, DUMMY_SP) {
if has_mut_interior {
return Err(Unpromotable);
}
- if self.qualif_local::<qualifs::NeedsDrop>(base) {
+ if self.qualif_local::<qualifs::NeedsDrop>(place.local) {
return Err(Unpromotable);
}
fn validate_place(&self, place: PlaceRef<'_, 'tcx>) -> Result<(), Unpromotable> {
match place {
- PlaceRef { base: PlaceBase::Local(local), projection: [] } => {
- self.validate_local(*local)
- }
- PlaceRef { base: PlaceBase::Static(_), projection: [] } => {
- bug!("qualifying already promoted MIR")
- }
- PlaceRef { base: _, projection: [proj_base @ .., elem] } => {
+ PlaceRef { local, projection: [] } => self.validate_local(*local),
+ PlaceRef { local: _, projection: [proj_base @ .., elem] } => {
match *elem {
ProjectionElem::Deref | ProjectionElem::Downcast(..) => {
return Err(Unpromotable);
ProjectionElem::Field(..) => {
if self.const_kind.is_none() {
let base_ty =
- Place::ty_from(place.base, proj_base, *self.body, self.tcx).ty;
+ Place::ty_from(place.local, proj_base, *self.body, self.tcx).ty;
if let Some(def) = base_ty.ty_adt_def() {
// No promotion of union field accesses.
if def.is_union() {
}
}
- self.validate_place(PlaceRef { base: place.base, projection: proj_base })
+ self.validate_place(PlaceRef { local: place.local, projection: proj_base })
}
}
}
// Raw reborrows can come from reference to pointer coercions,
// so are allowed.
if let [proj_base @ .., ProjectionElem::Deref] = place.projection.as_ref() {
- let base_ty = Place::ty_from(&place.base, proj_base, *self.body, self.tcx).ty;
+ let base_ty = Place::ty_from(&place.local, proj_base, *self.body, self.tcx).ty;
if let ty::Ref(..) = base_ty.kind {
- return self
- .validate_place(PlaceRef { base: &place.base, projection: proj_base });
+ return self.validate_place(PlaceRef {
+ local: &place.local,
+ projection: proj_base,
+ });
}
}
Err(Unpromotable)
// Special-case reborrows to be more like a copy of the reference.
let mut place = place.as_ref();
if let [proj_base @ .., ProjectionElem::Deref] = &place.projection {
- let base_ty = Place::ty_from(&place.base, proj_base, *self.body, self.tcx).ty;
+ let base_ty = Place::ty_from(&place.local, proj_base, *self.body, self.tcx).ty;
if let ty::Ref(..) = base_ty.kind {
- place = PlaceRef { base: &place.base, projection: proj_base };
+ place = PlaceRef { local: &place.local, projection: proj_base };
}
}
// HACK(eddyb) this should compute the same thing as
// `<HasMutInterior as Qualif>::in_projection` from
// `check_consts::qualifs` but without recursion.
- let mut has_mut_interior = match place.base {
- PlaceBase::Local(local) => self.qualif_local::<qualifs::HasMutInterior>(*local),
- PlaceBase::Static(_) => false,
- };
+ let mut has_mut_interior =
+ self.qualif_local::<qualifs::HasMutInterior>(*place.local);
if has_mut_interior {
let mut place_projection = place.projection;
// FIXME(eddyb) use a forward loop instead of a reverse one.
while let [proj_base @ .., elem] = place_projection {
// FIXME(eddyb) this is probably excessive, with
// the exception of `union` member accesses.
- let ty = Place::ty_from(place.base, proj_base, *self.body, self.tcx)
+ let ty = Place::ty_from(place.local, proj_base, *self.body, self.tcx)
.projection_ty(self.tcx, elem)
.ty;
if ty.is_freeze(self.tcx, self.param_env, DUMMY_SP) {
let is_const_fn = match fn_ty.kind {
ty::FnDef(def_id, _) => {
- self.tcx.is_const_fn(def_id)
- || self.tcx.is_unstable_const_fn(def_id).is_some()
+ is_const_fn(self.tcx, def_id)
+ || is_unstable_const_fn(self.tcx, def_id).is_some()
|| is_lang_panic_fn(self.tcx, self.def_id)
}
_ => false,
source: &'a mut BodyAndCache<'tcx>,
promoted: BodyAndCache<'tcx>,
temps: &'a mut IndexVec<Local, TempState>,
+ extra_statements: &'a mut Vec<(Location, Statement<'tcx>)>,
/// If true, all nested temps are also kept in the
/// source MIR, not moved to the promoted MIR.
candidate: Candidate,
next_promoted_id: usize,
) -> Option<BodyAndCache<'tcx>> {
- let mut operand = {
+ let mut rvalue = {
let promoted = &mut self.promoted;
let promoted_id = Promoted::new(next_promoted_id);
let tcx = self.tcx;
- let mut promoted_place = |ty, span| {
+ let mut promoted_operand = |ty, span| {
promoted.span = span;
promoted.local_decls[RETURN_PLACE] = LocalDecl::new_return_place(ty, span);
- Place {
- base: PlaceBase::Static(box Static {
- kind: StaticKind::Promoted(
- promoted_id,
+
+ Operand::Constant(Box::new(Constant {
+ span,
+ user_ty: None,
+ literal: tcx.mk_const(ty::Const {
+ ty,
+ val: ty::ConstKind::Unevaluated(
+ def_id,
InternalSubsts::identity_for_item(tcx, def_id),
+ Some(promoted_id),
),
- ty,
- def_id,
}),
- projection: List::empty(),
- }
+ }))
};
let (blocks, local_decls) = self.source.basic_blocks_and_local_decls_mut();
match candidate {
Candidate::Ref(loc) => {
let ref mut statement = blocks[loc.block].statements[loc.statement_index];
match statement.kind {
- StatementKind::Assign(box (_, Rvalue::Ref(_, _, ref mut place))) => {
+ StatementKind::Assign(box (
+ _,
+ Rvalue::Ref(ref mut region, borrow_kind, ref mut place),
+ )) => {
// Use the underlying local for this (necessarily interior) borrow.
- let ty = place.base.ty(local_decls).ty;
+ let ty = local_decls.local_decls()[place.local].ty;
let span = statement.source_info.span;
- Operand::Move(Place {
- base: mem::replace(&mut place.base, promoted_place(ty, span).base),
- projection: List::empty(),
- })
+ let ref_ty = tcx.mk_ref(
+ tcx.lifetimes.re_erased,
+ ty::TypeAndMut { ty, mutbl: borrow_kind.to_mutbl_lossy() },
+ );
+
+ *region = tcx.lifetimes.re_erased;
+
+ let mut projection = vec![PlaceElem::Deref];
+ projection.extend(place.projection);
+ place.projection = tcx.intern_place_elems(&projection);
+
+ // Create a temp to hold the promoted reference.
+ // This is because `*r` requires `r` to be a local,
+ // otherwise we would use the `promoted` directly.
+ let mut promoted_ref = LocalDecl::new_temp(ref_ty, span);
+ promoted_ref.source_info = statement.source_info;
+ let promoted_ref = local_decls.push(promoted_ref);
+ assert_eq!(self.temps.push(TempState::Unpromotable), promoted_ref);
+
+ let promoted_ref_statement = Statement {
+ source_info: statement.source_info,
+ kind: StatementKind::Assign(Box::new((
+ Place::from(promoted_ref),
+ Rvalue::Use(promoted_operand(ref_ty, span)),
+ ))),
+ };
+ self.extra_statements.push((loc, promoted_ref_statement));
+
+ Rvalue::Ref(
+ tcx.lifetimes.re_erased,
+ borrow_kind,
+ Place {
+ local: mem::replace(&mut place.local, promoted_ref),
+ projection: List::empty(),
+ },
+ )
}
_ => bug!(),
}
StatementKind::Assign(box (_, Rvalue::Repeat(ref mut operand, _))) => {
let ty = operand.ty(local_decls, self.tcx);
let span = statement.source_info.span;
- mem::replace(operand, Operand::Copy(promoted_place(ty, span)))
+
+ Rvalue::Use(mem::replace(operand, promoted_operand(ty, span)))
}
_ => bug!(),
}
TerminatorKind::Call { ref mut args, .. } => {
let ty = args[index].ty(local_decls, self.tcx);
let span = terminator.source_info.span;
- let operand = Operand::Copy(promoted_place(ty, span));
- mem::replace(&mut args[index], operand)
+
+ Rvalue::Use(mem::replace(&mut args[index], promoted_operand(ty, span)))
}
// We expected a `TerminatorKind::Call` for which we'd like to promote an
// argument. `qualify_consts` saw a `TerminatorKind::Call` here, but
};
assert_eq!(self.new_block(), START_BLOCK);
- self.visit_operand(
- &mut operand,
+ self.visit_rvalue(
+ &mut rvalue,
Location { block: BasicBlock::new(0), statement_index: usize::MAX },
);
let span = self.promoted.span;
- self.assign(RETURN_PLACE, Rvalue::Use(operand), span);
+ self.assign(RETURN_PLACE, rvalue, span);
Some(self.promoted)
}
}
let mut promotions = IndexVec::new();
+ let mut extra_statements = vec![];
for candidate in candidates.into_iter().rev() {
match candidate {
Candidate::Repeat(Location { block, statement_index })
let initial_locals =
iter::once(LocalDecl::new_return_place(tcx.types.never, body.span)).collect();
+ let mut promoted = Body::new(
+ IndexVec::new(),
+ // FIXME: maybe try to filter this to avoid blowing up
+ // memory usage?
+ body.source_scopes.clone(),
+ initial_locals,
+ IndexVec::new(),
+ 0,
+ vec![],
+ body.span,
+ vec![],
+ body.generator_kind,
+ );
+ promoted.ignore_interior_mut_in_const_validation = true;
+
let promoter = Promoter {
- promoted: BodyAndCache::new(Body::new(
- IndexVec::new(),
- // FIXME: maybe try to filter this to avoid blowing up
- // memory usage?
- body.source_scopes.clone(),
- initial_locals,
- IndexVec::new(),
- 0,
- vec![],
- body.span,
- vec![],
- body.generator_kind,
- )),
+ promoted: BodyAndCache::new(promoted),
tcx,
source: body,
temps: &mut temps,
+ extra_statements: &mut extra_statements,
keep_original: false,
};
}
}
+ // Insert each of `extra_statements` before its indicated location, which
+ // has to be done in reverse location order, to not invalidate the rest.
+ extra_statements.sort_by_key(|&(loc, _)| cmp::Reverse(loc));
+ for (loc, statement) in extra_statements {
+ body[loc.block].statements.insert(loc.statement_index, statement);
+ }
+
// Eliminate assignments to, and drops of promoted temps.
let promoted = |index: Local| temps[index] == TempState::PromotedOut;
for block in body.basic_blocks_mut() {
ProjectionElem::Downcast(_symbol, _variant_index) => {}
ProjectionElem::Field(..) => {
- let base_ty = Place::ty_from(&place.base, &proj_base, body, tcx).ty;
+ let base_ty = Place::ty_from(&place.local, &proj_base, body, tcx).ty;
if let Some(def) = base_ty.ty_adt_def() {
// No union field accesses in `const fn`
if def.is_union() {
Ok(())
}
-/// Returns whether `allow_internal_unstable(..., <feature_gate>, ...)` is present.
+/// Returns `true` if the given feature gate is allowed within the function with the given `DefId`.
fn feature_allowed(tcx: TyCtxt<'tcx>, def_id: DefId, feature_gate: Symbol) -> bool {
+ // All features require that the corresponding gate be enabled,
+ // even if the function has `#[allow_internal_unstable(the_gate)]`.
+ if !tcx.features().enabled(feature_gate) {
+ return false;
+ }
+
+ // If this crate is not using stability attributes, or this function is not claiming to be a
+ // stable `const fn`, that is all that is required.
+ if !tcx.features().staged_api || tcx.has_attr(def_id, sym::rustc_const_unstable) {
+ return true;
+ }
+
+ // However, we cannot allow stable `const fn`s to use unstable features without an explicit
+ // opt-in via `allow_internal_unstable`.
attr::allow_internal_unstable(&tcx.get_attrs(def_id), &tcx.sess.diagnostic())
.map_or(false, |mut features| features.any(|name| name == feature_gate))
}
TerminatorKind::Call { func, args, from_hir_call: _, destination: _, cleanup: _ } => {
let fn_ty = func.ty(body, tcx);
if let ty::FnDef(def_id, _) = fn_ty.kind {
- if !tcx.is_min_const_fn(def_id) {
+ if !crate::const_eval::is_min_const_fn(tcx, def_id) {
return Err((
span,
format!(
// Remove unnecessary StorageLive and StorageDead annotations.
data.statements.retain(|stmt| match &stmt.kind {
StatementKind::StorageLive(l) | StatementKind::StorageDead(l) => self.map[*l].is_some(),
- StatementKind::Assign(box (place, _)) => {
- if let PlaceBase::Local(local) = place.base {
- self.map[local].is_some()
- } else {
- true
- }
- }
+ StatementKind::Assign(box (place, _)) => self.map[place.local].is_some(),
_ => true,
});
self.super_basic_block_data(block, data);
fn match_variant_field_place<'tcx>(place: &Place<'tcx>) -> Option<(Local, VarField<'tcx>)> {
match place.as_ref() {
PlaceRef {
- base: &PlaceBase::Local(local),
+ local,
projection: &[ProjectionElem::Downcast(_, var_idx), ProjectionElem::Field(field, ty)],
- } => Some((local, VarField { field, field_ty: ty, var_idx })),
+ } => Some((*local, VarField { field, field_ty: ty, var_idx })),
_ => None,
}
}
// encountered a Deref, which is ABI-aligned
ProjectionElem::Deref => break,
ProjectionElem::Field(..) => {
- let ty = Place::ty_from(&place.base, proj_base, local_decls, tcx).ty;
+ let ty = Place::ty_from(&place.local, proj_base, local_decls, tcx).ty;
match ty.kind {
ty::Adt(def, _) if def.repr.packed() => return true,
_ => {}
use rustc::ty::{self, Ty, TyCtxt};
-use rustc_errors::{DiagnosticBuilder, DiagnosticId};
-use rustc_span::{MultiSpan, Span};
-
use rustc_error_codes::*;
+use rustc_errors::{struct_span_err, DiagnosticBuilder, DiagnosticId};
+use rustc_span::{MultiSpan, Span};
impl<'cx, 'tcx> crate::borrow_check::MirBorrowckCtxt<'cx, 'tcx> {
crate fn cannot_move_when_borrowed(&self, span: Span, desc: &str) -> DiagnosticBuilder<'cx> {
--- /dev/null
+[package]
+authors = ["The Rust Project Developers"]
+name = "rustc_mir_build"
+version = "0.0.0"
+edition = "2018"
+
+[lib]
+name = "rustc_mir_build"
+path = "lib.rs"
+doctest = false
+
+[dependencies]
+arena = { path = "../libarena" }
+itertools = "0.8"
+log = "0.4"
+rustc = { path = "../librustc" }
+rustc_apfloat = { path = "../librustc_apfloat" }
+rustc_data_structures = { path = "../librustc_data_structures" }
+rustc_index = { path = "../librustc_index" }
+rustc_errors = { path = "../librustc_errors" }
+rustc_hir = { path = "../librustc_hir" }
+rustc_macros = { path = "../librustc_macros" }
+rustc_serialize = { path = "../libserialize", package = "serialize" }
+rustc_span = { path = "../librustc_span" }
+rustc_target = { path = "../librustc_target" }
+syntax = { path = "../libsyntax" }
+smallvec = { version = "1.0", features = ["union", "may_dangle"] }
+rustc_error_codes = { path = "../librustc_error_codes" }
--- /dev/null
+use crate::build::matches::ArmHasGuard;
+use crate::build::ForGuard::OutsideGuard;
+use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
+use crate::hair::*;
+use rustc::mir::*;
+use rustc_hir as hir;
+use rustc_span::Span;
+
+impl<'a, 'tcx> Builder<'a, 'tcx> {
+ crate fn ast_block(
+ &mut self,
+ destination: &Place<'tcx>,
+ block: BasicBlock,
+ ast_block: &'tcx hir::Block<'tcx>,
+ source_info: SourceInfo,
+ ) -> BlockAnd<()> {
+ let Block {
+ region_scope,
+ opt_destruction_scope,
+ span,
+ stmts,
+ expr,
+ targeted_by_break,
+ safety_mode,
+ } = self.hir.mirror(ast_block);
+ self.in_opt_scope(opt_destruction_scope.map(|de| (de, source_info)), move |this| {
+ this.in_scope((region_scope, source_info), LintLevel::Inherited, move |this| {
+ if targeted_by_break {
+ // This is a `break`-able block
+ let exit_block = this.cfg.start_new_block();
+ let block_exit =
+ this.in_breakable_scope(None, exit_block, destination.clone(), |this| {
+ this.ast_block_stmts(destination, block, span, stmts, expr, safety_mode)
+ });
+ this.cfg.goto(unpack!(block_exit), source_info, exit_block);
+ exit_block.unit()
+ } else {
+ this.ast_block_stmts(destination, block, span, stmts, expr, safety_mode)
+ }
+ })
+ })
+ }
+
+ fn ast_block_stmts(
+ &mut self,
+ destination: &Place<'tcx>,
+ mut block: BasicBlock,
+ span: Span,
+ stmts: Vec<StmtRef<'tcx>>,
+ expr: Option<ExprRef<'tcx>>,
+ safety_mode: BlockSafety,
+ ) -> BlockAnd<()> {
+ let this = self;
+
+ // This convoluted structure is to avoid using recursion as we walk down a list
+ // of statements. Basically, the structure we get back is something like:
+ //
+ // let x = <init> in {
+ // expr1;
+ // let y = <init> in {
+ // expr2;
+ // expr3;
+ // ...
+ // }
+ // }
+ //
+ // The let bindings are valid till the end of block so all we have to do is to pop all
+ // the let-scopes at the end.
+ //
+ // First we build all the statements in the block.
+ let mut let_scope_stack = Vec::with_capacity(8);
+ let outer_source_scope = this.source_scope;
+ let outer_push_unsafe_count = this.push_unsafe_count;
+ let outer_unpushed_unsafe = this.unpushed_unsafe;
+ this.update_source_scope_for_safety_mode(span, safety_mode);
+
+ let source_info = this.source_info(span);
+ for stmt in stmts {
+ let Stmt { kind, opt_destruction_scope } = this.hir.mirror(stmt);
+ match kind {
+ StmtKind::Expr { scope, expr } => {
+ this.block_context.push(BlockFrame::Statement { ignores_expr_result: true });
+ unpack!(
+ block = this.in_opt_scope(
+ opt_destruction_scope.map(|de| (de, source_info)),
+ |this| {
+ let si = (scope, source_info);
+ this.in_scope(si, LintLevel::Inherited, |this| {
+ let expr = this.hir.mirror(expr);
+ this.stmt_expr(block, expr, Some(scope))
+ })
+ }
+ )
+ );
+ }
+ StmtKind::Let { remainder_scope, init_scope, pattern, initializer, lint_level } => {
+ let ignores_expr_result =
+ if let PatKind::Wild = *pattern.kind { true } else { false };
+ this.block_context.push(BlockFrame::Statement { ignores_expr_result });
+
+ // Enter the remainder scope, i.e., the bindings' destruction scope.
+ this.push_scope((remainder_scope, source_info));
+ let_scope_stack.push(remainder_scope);
+
+ // Declare the bindings, which may create a source scope.
+ let remainder_span =
+ remainder_scope.span(this.hir.tcx(), &this.hir.region_scope_tree);
+
+ let visibility_scope =
+ Some(this.new_source_scope(remainder_span, LintLevel::Inherited, None));
+
+ // Evaluate the initializer, if present.
+ if let Some(init) = initializer {
+ let initializer_span = init.span();
+
+ unpack!(
+ block = this.in_opt_scope(
+ opt_destruction_scope.map(|de| (de, source_info)),
+ |this| {
+ let scope = (init_scope, source_info);
+ this.in_scope(scope, lint_level, |this| {
+ this.declare_bindings(
+ visibility_scope,
+ remainder_span,
+ &pattern,
+ ArmHasGuard(false),
+ Some((None, initializer_span)),
+ );
+ this.expr_into_pattern(block, pattern, init)
+ })
+ }
+ )
+ );
+ } else {
+ let scope = (init_scope, source_info);
+ unpack!(this.in_scope(scope, lint_level, |this| {
+ this.declare_bindings(
+ visibility_scope,
+ remainder_span,
+ &pattern,
+ ArmHasGuard(false),
+ None,
+ );
+ block.unit()
+ }));
+
+ debug!("ast_block_stmts: pattern={:?}", pattern);
+ this.visit_bindings(
+ &pattern,
+ UserTypeProjections::none(),
+ &mut |this, _, _, _, node, span, _, _| {
+ this.storage_live_binding(block, node, span, OutsideGuard);
+ this.schedule_drop_for_binding(node, span, OutsideGuard);
+ },
+ )
+ }
+
+ // Enter the visibility scope, after evaluating the initializer.
+ if let Some(source_scope) = visibility_scope {
+ this.source_scope = source_scope;
+ }
+ }
+ }
+
+ let popped = this.block_context.pop();
+ assert!(popped.map_or(false, |bf| bf.is_statement()));
+ }
+
+ // Then, the block may have an optional trailing expression which is a “return” value
+ // of the block, which is stored into `destination`.
+ let tcx = this.hir.tcx();
+ let destination_ty = destination.ty(&this.local_decls, tcx).ty;
+ if let Some(expr) = expr {
+ let tail_result_is_ignored =
+ destination_ty.is_unit() || this.block_context.currently_ignores_tail_results();
+ this.block_context.push(BlockFrame::TailExpr { tail_result_is_ignored });
+
+ unpack!(block = this.into(destination, block, expr));
+ let popped = this.block_context.pop();
+
+ assert!(popped.map_or(false, |bf| bf.is_tail_expr()));
+ } else {
+ // If a block has no trailing expression, then it is given an implicit return type.
+ // This return type is usually `()`, unless the block is diverging, in which case the
+ // return type is `!`. For the unit type, we need to actually return the unit, but in
+ // the case of `!`, no return value is required, as the block will never return.
+ if destination_ty.is_unit() {
+ // We only want to assign an implicit `()` as the return value of the block if the
+ // block does not diverge. (Otherwise, we may try to assign a unit to a `!`-type.)
+ this.cfg.push_assign_unit(block, source_info, destination);
+ }
+ }
+ // Finally, we pop all the let scopes before exiting out from the scope of block
+ // itself.
+ for scope in let_scope_stack.into_iter().rev() {
+ unpack!(block = this.pop_scope((scope, source_info), block));
+ }
+ // Restore the original source scope.
+ this.source_scope = outer_source_scope;
+ this.push_unsafe_count = outer_push_unsafe_count;
+ this.unpushed_unsafe = outer_unpushed_unsafe;
+ block.unit()
+ }
+
+ /// If we are changing the safety mode, create a new source scope
+ fn update_source_scope_for_safety_mode(&mut self, span: Span, safety_mode: BlockSafety) {
+ debug!("update_source_scope_for({:?}, {:?})", span, safety_mode);
+ let new_unsafety = match safety_mode {
+ BlockSafety::Safe => None,
+ BlockSafety::ExplicitUnsafe(hir_id) => {
+ assert_eq!(self.push_unsafe_count, 0);
+ match self.unpushed_unsafe {
+ Safety::Safe => {}
+ _ => return,
+ }
+ self.unpushed_unsafe = Safety::ExplicitUnsafe(hir_id);
+ Some(Safety::ExplicitUnsafe(hir_id))
+ }
+ BlockSafety::PushUnsafe => {
+ self.push_unsafe_count += 1;
+ Some(Safety::BuiltinUnsafe)
+ }
+ BlockSafety::PopUnsafe => {
+ self.push_unsafe_count = self
+ .push_unsafe_count
+ .checked_sub(1)
+ .unwrap_or_else(|| span_bug!(span, "unsafe count underflow"));
+ if self.push_unsafe_count == 0 { Some(self.unpushed_unsafe) } else { None }
+ }
+ };
+
+ if let Some(unsafety) = new_unsafety {
+ self.source_scope = self.new_source_scope(span, LintLevel::Inherited, Some(unsafety));
+ }
+ }
+}
--- /dev/null
+//! Routines for manipulating the control-flow graph.
+
+use crate::build::CFG;
+use rustc::mir::*;
+
+impl<'tcx> CFG<'tcx> {
+ crate fn block_data(&self, blk: BasicBlock) -> &BasicBlockData<'tcx> {
+ &self.basic_blocks[blk]
+ }
+
+ crate fn block_data_mut(&mut self, blk: BasicBlock) -> &mut BasicBlockData<'tcx> {
+ &mut self.basic_blocks[blk]
+ }
+
+ // llvm.org/PR32488 makes this function use an excess of stack space. Mark
+ // it as #[inline(never)] to keep rustc's stack use in check.
+ #[inline(never)]
+ crate fn start_new_block(&mut self) -> BasicBlock {
+ self.basic_blocks.push(BasicBlockData::new(None))
+ }
+
+ crate fn start_new_cleanup_block(&mut self) -> BasicBlock {
+ let bb = self.start_new_block();
+ self.block_data_mut(bb).is_cleanup = true;
+ bb
+ }
+
+ crate fn push(&mut self, block: BasicBlock, statement: Statement<'tcx>) {
+ debug!("push({:?}, {:?})", block, statement);
+ self.block_data_mut(block).statements.push(statement);
+ }
+
+ crate fn push_assign(
+ &mut self,
+ block: BasicBlock,
+ source_info: SourceInfo,
+ place: &Place<'tcx>,
+ rvalue: Rvalue<'tcx>,
+ ) {
+ self.push(
+ block,
+ Statement { source_info, kind: StatementKind::Assign(box (place.clone(), rvalue)) },
+ );
+ }
+
+ crate fn push_assign_constant(
+ &mut self,
+ block: BasicBlock,
+ source_info: SourceInfo,
+ temp: &Place<'tcx>,
+ constant: Constant<'tcx>,
+ ) {
+ self.push_assign(block, source_info, temp, Rvalue::Use(Operand::Constant(box constant)));
+ }
+
+ crate fn push_assign_unit(
+ &mut self,
+ block: BasicBlock,
+ source_info: SourceInfo,
+ place: &Place<'tcx>,
+ ) {
+ self.push_assign(
+ block,
+ source_info,
+ place,
+ Rvalue::Aggregate(box AggregateKind::Tuple, vec![]),
+ );
+ }
+
+ crate fn push_fake_read(
+ &mut self,
+ block: BasicBlock,
+ source_info: SourceInfo,
+ cause: FakeReadCause,
+ place: Place<'tcx>,
+ ) {
+ let kind = StatementKind::FakeRead(cause, box place);
+ let stmt = Statement { source_info, kind };
+ self.push(block, stmt);
+ }
+
+ crate fn terminate(
+ &mut self,
+ block: BasicBlock,
+ source_info: SourceInfo,
+ kind: TerminatorKind<'tcx>,
+ ) {
+ debug!("terminating block {:?} <- {:?}", block, kind);
+ debug_assert!(
+ self.block_data(block).terminator.is_none(),
+ "terminate: block {:?}={:?} already has a terminator set",
+ block,
+ self.block_data(block)
+ );
+ self.block_data_mut(block).terminator = Some(Terminator { source_info, kind });
+ }
+
+ /// In the `origin` block, push a `goto -> target` terminator.
+ crate fn goto(&mut self, origin: BasicBlock, source_info: SourceInfo, target: BasicBlock) {
+ self.terminate(origin, source_info, TerminatorKind::Goto { target })
+ }
+}
--- /dev/null
+//! See docs in build/expr/mod.rs
+
+use crate::build::Builder;
+use crate::hair::*;
+use rustc::mir::*;
+use rustc::ty::CanonicalUserTypeAnnotation;
+
+impl<'a, 'tcx> Builder<'a, 'tcx> {
+ /// Compile `expr`, yielding a compile-time constant. Assumes that
+ /// `expr` is a valid compile-time constant!
+ crate fn as_constant<M>(&mut self, expr: M) -> Constant<'tcx>
+ where
+ M: Mirror<'tcx, Output = Expr<'tcx>>,
+ {
+ let expr = self.hir.mirror(expr);
+ self.expr_as_constant(expr)
+ }
+
+ fn expr_as_constant(&mut self, expr: Expr<'tcx>) -> Constant<'tcx> {
+ let this = self;
+ let Expr { ty, temp_lifetime: _, span, kind } = expr;
+ match kind {
+ ExprKind::Scope { region_scope: _, lint_level: _, value } => this.as_constant(value),
+ ExprKind::Literal { literal, user_ty } => {
+ let user_ty = user_ty.map(|user_ty| {
+ this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
+ span,
+ user_ty,
+ inferred_ty: ty,
+ })
+ });
+ assert_eq!(literal.ty, ty);
+ Constant { span, user_ty, literal }
+ }
+ ExprKind::StaticRef { literal, .. } => Constant { span, user_ty: None, literal },
+ _ => span_bug!(span, "expression is not a valid constant {:?}", kind),
+ }
+ }
+}
--- /dev/null
+//! See docs in build/expr/mod.rs
+
+use crate::build::expr::category::Category;
+use crate::build::{BlockAnd, BlockAndExtension, Builder};
+use crate::hair::*;
+use rustc::middle::region;
+use rustc::mir::*;
+
+impl<'a, 'tcx> Builder<'a, 'tcx> {
+ /// Returns an operand suitable for use until the end of the current
+ /// scope expression.
+ ///
+ /// The operand returned from this function will *not be valid* after
+ /// an ExprKind::Scope is passed, so please do *not* return it from
+ /// functions to avoid bad miscompiles.
+ crate fn as_local_operand<M>(&mut self, block: BasicBlock, expr: M) -> BlockAnd<Operand<'tcx>>
+ where
+ M: Mirror<'tcx, Output = Expr<'tcx>>,
+ {
+ let local_scope = self.local_scope();
+ self.as_operand(block, local_scope, expr)
+ }
+
+ /// Compile `expr` into a value that can be used as an operand.
+ /// If `expr` is a place like `x`, this will introduce a
+ /// temporary `tmp = x`, so that we capture the value of `x` at
+ /// this time.
+ ///
+ /// The operand is known to be live until the end of `scope`.
+ crate fn as_operand<M>(
+ &mut self,
+ block: BasicBlock,
+ scope: Option<region::Scope>,
+ expr: M,
+ ) -> BlockAnd<Operand<'tcx>>
+ where
+ M: Mirror<'tcx, Output = Expr<'tcx>>,
+ {
+ let expr = self.hir.mirror(expr);
+ self.expr_as_operand(block, scope, expr)
+ }
+
+ fn expr_as_operand(
+ &mut self,
+ mut block: BasicBlock,
+ scope: Option<region::Scope>,
+ expr: Expr<'tcx>,
+ ) -> BlockAnd<Operand<'tcx>> {
+ debug!("expr_as_operand(block={:?}, expr={:?})", block, expr);
+ let this = self;
+
+ if let ExprKind::Scope { region_scope, lint_level, value } = expr.kind {
+ let source_info = this.source_info(expr.span);
+ let region_scope = (region_scope, source_info);
+ return this
+ .in_scope(region_scope, lint_level, |this| this.as_operand(block, scope, value));
+ }
+
+ let category = Category::of(&expr.kind).unwrap();
+ debug!("expr_as_operand: category={:?} for={:?}", category, expr.kind);
+ match category {
+ Category::Constant => {
+ let constant = this.as_constant(expr);
+ block.and(Operand::Constant(box constant))
+ }
+ Category::Place | Category::Rvalue(..) => {
+ let operand = unpack!(block = this.as_temp(block, scope, expr, Mutability::Mut));
+ block.and(Operand::Move(Place::from(operand)))
+ }
+ }
+ }
+}
--- /dev/null
+//! See docs in build/expr/mod.rs
+
+use crate::build::expr::category::Category;
+use crate::build::ForGuard::{OutsideGuard, RefWithinGuard};
+use crate::build::{BlockAnd, BlockAndExtension, Builder};
+use crate::hair::*;
+use rustc::middle::region;
+use rustc::mir::interpret::PanicInfo::BoundsCheck;
+use rustc::mir::*;
+use rustc::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt, Variance};
+use rustc_span::Span;
+
+use rustc_index::vec::Idx;
+
+/// `PlaceBuilder` is used to create places during MIR construction. It allows you to "build up" a
+/// place by pushing more and more projections onto the end, and then convert the final set into a
+/// place using the `into_place` method.
+///
+/// This is used internally when building a place for an expression like `a.b.c`. The fields `b`
+/// and `c` can be progressively pushed onto the place builder that is created when converting `a`.
+#[derive(Clone)]
+struct PlaceBuilder<'tcx> {
+ local: Local,
+ projection: Vec<PlaceElem<'tcx>>,
+}
+
+impl<'tcx> PlaceBuilder<'tcx> {
+ fn into_place(self, tcx: TyCtxt<'tcx>) -> Place<'tcx> {
+ Place { local: self.local, projection: tcx.intern_place_elems(&self.projection) }
+ }
+
+ fn field(self, f: Field, ty: Ty<'tcx>) -> Self {
+ self.project(PlaceElem::Field(f, ty))
+ }
+
+ fn deref(self) -> Self {
+ self.project(PlaceElem::Deref)
+ }
+
+ fn index(self, index: Local) -> Self {
+ self.project(PlaceElem::Index(index))
+ }
+
+ fn project(mut self, elem: PlaceElem<'tcx>) -> Self {
+ self.projection.push(elem);
+ self
+ }
+}
+
+impl<'tcx> From<Local> for PlaceBuilder<'tcx> {
+ fn from(local: Local) -> Self {
+ Self { local, projection: Vec::new() }
+ }
+}
+
+impl<'a, 'tcx> Builder<'a, 'tcx> {
+ /// Compile `expr`, yielding a place that we can move from etc.
+ ///
+ /// WARNING: Any user code might:
+ /// * Invalidate any slice bounds checks performed.
+ /// * Change the address that this `Place` refers to.
+ /// * Modify the memory that this place refers to.
+ /// * Invalidate the memory that this place refers to, this will be caught
+ /// by borrow checking.
+ ///
+ /// Extra care is needed if any user code is allowed to run between calling
+ /// this method and using it, as is the case for `match` and index
+ /// expressions.
+ crate fn as_place<M>(&mut self, mut block: BasicBlock, expr: M) -> BlockAnd<Place<'tcx>>
+ where
+ M: Mirror<'tcx, Output = Expr<'tcx>>,
+ {
+ let place_builder = unpack!(block = self.as_place_builder(block, expr));
+ block.and(place_builder.into_place(self.hir.tcx()))
+ }
+
+ /// This is used when constructing a compound `Place`, so that we can avoid creating
+ /// intermediate `Place` values until we know the full set of projections.
+ fn as_place_builder<M>(&mut self, block: BasicBlock, expr: M) -> BlockAnd<PlaceBuilder<'tcx>>
+ where
+ M: Mirror<'tcx, Output = Expr<'tcx>>,
+ {
+ let expr = self.hir.mirror(expr);
+ self.expr_as_place(block, expr, Mutability::Mut, None)
+ }
+
+ /// Compile `expr`, yielding a place that we can move from etc.
+ /// Mutability note: The caller of this method promises only to read from the resulting
+ /// place. The place itself may or may not be mutable:
+ /// * If this expr is a place expr like a.b, then we will return that place.
+ /// * Otherwise, a temporary is created: in that event, it will be an immutable temporary.
+ crate fn as_read_only_place<M>(
+ &mut self,
+ mut block: BasicBlock,
+ expr: M,
+ ) -> BlockAnd<Place<'tcx>>
+ where
+ M: Mirror<'tcx, Output = Expr<'tcx>>,
+ {
+ let place_builder = unpack!(block = self.as_read_only_place_builder(block, expr));
+ block.and(place_builder.into_place(self.hir.tcx()))
+ }
+
+ /// This is used when constructing a compound `Place`, so that we can avoid creating
+ /// intermediate `Place` values until we know the full set of projections.
+ /// Mutability note: The caller of this method promises only to read from the resulting
+ /// place. The place itself may or may not be mutable:
+ /// * If this expr is a place expr like a.b, then we will return that place.
+ /// * Otherwise, a temporary is created: in that event, it will be an immutable temporary.
+ fn as_read_only_place_builder<M>(
+ &mut self,
+ block: BasicBlock,
+ expr: M,
+ ) -> BlockAnd<PlaceBuilder<'tcx>>
+ where
+ M: Mirror<'tcx, Output = Expr<'tcx>>,
+ {
+ let expr = self.hir.mirror(expr);
+ self.expr_as_place(block, expr, Mutability::Not, None)
+ }
+
+ fn expr_as_place(
+ &mut self,
+ mut block: BasicBlock,
+ expr: Expr<'tcx>,
+ mutability: Mutability,
+ fake_borrow_temps: Option<&mut Vec<Local>>,
+ ) -> BlockAnd<PlaceBuilder<'tcx>> {
+ debug!("expr_as_place(block={:?}, expr={:?}, mutability={:?})", block, expr, mutability);
+
+ let this = self;
+ let expr_span = expr.span;
+ let source_info = this.source_info(expr_span);
+ match expr.kind {
+ ExprKind::Scope { region_scope, lint_level, value } => {
+ this.in_scope((region_scope, source_info), lint_level, |this| {
+ let value = this.hir.mirror(value);
+ this.expr_as_place(block, value, mutability, fake_borrow_temps)
+ })
+ }
+ ExprKind::Field { lhs, name } => {
+ let lhs = this.hir.mirror(lhs);
+ let place_builder =
+ unpack!(block = this.expr_as_place(block, lhs, mutability, fake_borrow_temps,));
+ block.and(place_builder.field(name, expr.ty))
+ }
+ ExprKind::Deref { arg } => {
+ let arg = this.hir.mirror(arg);
+ let place_builder =
+ unpack!(block = this.expr_as_place(block, arg, mutability, fake_borrow_temps,));
+ block.and(place_builder.deref())
+ }
+ ExprKind::Index { lhs, index } => this.lower_index_expression(
+ block,
+ lhs,
+ index,
+ mutability,
+ fake_borrow_temps,
+ expr.temp_lifetime,
+ expr_span,
+ source_info,
+ ),
+ ExprKind::SelfRef => block.and(PlaceBuilder::from(Local::new(1))),
+ ExprKind::VarRef { id } => {
+ let place_builder = if this.is_bound_var_in_guard(id) {
+ let index = this.var_local_id(id, RefWithinGuard);
+ PlaceBuilder::from(index).deref()
+ } else {
+ let index = this.var_local_id(id, OutsideGuard);
+ PlaceBuilder::from(index)
+ };
+ block.and(place_builder)
+ }
+
+ ExprKind::PlaceTypeAscription { source, user_ty } => {
+ let source = this.hir.mirror(source);
+ let place_builder = unpack!(
+ block = this.expr_as_place(block, source, mutability, fake_borrow_temps,)
+ );
+ if let Some(user_ty) = user_ty {
+ let annotation_index =
+ this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
+ span: source_info.span,
+ user_ty,
+ inferred_ty: expr.ty,
+ });
+
+ let place = place_builder.clone().into_place(this.hir.tcx());
+ this.cfg.push(
+ block,
+ Statement {
+ source_info,
+ kind: StatementKind::AscribeUserType(
+ box (
+ place,
+ UserTypeProjection { base: annotation_index, projs: vec![] },
+ ),
+ Variance::Invariant,
+ ),
+ },
+ );
+ }
+ block.and(place_builder)
+ }
+ ExprKind::ValueTypeAscription { source, user_ty } => {
+ let source = this.hir.mirror(source);
+ let temp =
+ unpack!(block = this.as_temp(block, source.temp_lifetime, source, mutability));
+ if let Some(user_ty) = user_ty {
+ let annotation_index =
+ this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
+ span: source_info.span,
+ user_ty,
+ inferred_ty: expr.ty,
+ });
+ this.cfg.push(
+ block,
+ Statement {
+ source_info,
+ kind: StatementKind::AscribeUserType(
+ box (
+ Place::from(temp.clone()),
+ UserTypeProjection { base: annotation_index, projs: vec![] },
+ ),
+ Variance::Invariant,
+ ),
+ },
+ );
+ }
+ block.and(PlaceBuilder::from(temp))
+ }
+
+ ExprKind::Array { .. }
+ | ExprKind::Tuple { .. }
+ | ExprKind::Adt { .. }
+ | ExprKind::Closure { .. }
+ | ExprKind::Unary { .. }
+ | ExprKind::Binary { .. }
+ | ExprKind::LogicalOp { .. }
+ | ExprKind::Box { .. }
+ | ExprKind::Cast { .. }
+ | ExprKind::Use { .. }
+ | ExprKind::NeverToAny { .. }
+ | ExprKind::Pointer { .. }
+ | ExprKind::Repeat { .. }
+ | ExprKind::Borrow { .. }
+ | ExprKind::AddressOf { .. }
+ | ExprKind::Match { .. }
+ | ExprKind::Loop { .. }
+ | ExprKind::Block { .. }
+ | ExprKind::Assign { .. }
+ | ExprKind::AssignOp { .. }
+ | ExprKind::Break { .. }
+ | ExprKind::Continue { .. }
+ | ExprKind::Return { .. }
+ | ExprKind::Literal { .. }
+ | ExprKind::StaticRef { .. }
+ | ExprKind::InlineAsm { .. }
+ | ExprKind::Yield { .. }
+ | ExprKind::Call { .. } => {
+ // these are not places, so we need to make a temporary.
+ debug_assert!(match Category::of(&expr.kind) {
+ Some(Category::Place) => false,
+ _ => true,
+ });
+ let temp =
+ unpack!(block = this.as_temp(block, expr.temp_lifetime, expr, mutability));
+ block.and(PlaceBuilder::from(temp))
+ }
+ }
+ }
+
+ /// Lower an index expression
+ ///
+ /// This has two complications;
+ ///
+ /// * We need to do a bounds check.
+ /// * We need to ensure that the bounds check can't be invalidated using an
+ /// expression like `x[1][{x = y; 2}]`. We use fake borrows here to ensure
+ /// that this is the case.
+ fn lower_index_expression(
+ &mut self,
+ mut block: BasicBlock,
+ base: ExprRef<'tcx>,
+ index: ExprRef<'tcx>,
+ mutability: Mutability,
+ fake_borrow_temps: Option<&mut Vec<Local>>,
+ temp_lifetime: Option<region::Scope>,
+ expr_span: Span,
+ source_info: SourceInfo,
+ ) -> BlockAnd<PlaceBuilder<'tcx>> {
+ let lhs = self.hir.mirror(base);
+
+ let base_fake_borrow_temps = &mut Vec::new();
+ let is_outermost_index = fake_borrow_temps.is_none();
+ let fake_borrow_temps = fake_borrow_temps.unwrap_or(base_fake_borrow_temps);
+
+ let base_place =
+ unpack!(block = self.expr_as_place(block, lhs, mutability, Some(fake_borrow_temps),));
+
+ // Making this a *fresh* temporary means we do not have to worry about
+ // the index changing later: Nothing will ever change this temporary.
+ // The "retagging" transformation (for Stacked Borrows) relies on this.
+ let idx = unpack!(block = self.as_temp(block, temp_lifetime, index, Mutability::Not,));
+
+ block = self.bounds_check(
+ block,
+ base_place.clone().into_place(self.hir.tcx()),
+ idx,
+ expr_span,
+ source_info,
+ );
+
+ if is_outermost_index {
+ self.read_fake_borrows(block, fake_borrow_temps, source_info)
+ } else {
+ self.add_fake_borrows_of_base(
+ &base_place,
+ block,
+ fake_borrow_temps,
+ expr_span,
+ source_info,
+ );
+ }
+
+ block.and(base_place.index(idx))
+ }
+
+ fn bounds_check(
+ &mut self,
+ block: BasicBlock,
+ slice: Place<'tcx>,
+ index: Local,
+ expr_span: Span,
+ source_info: SourceInfo,
+ ) -> BasicBlock {
+ let usize_ty = self.hir.usize_ty();
+ let bool_ty = self.hir.bool_ty();
+ // bounds check:
+ let len = self.temp(usize_ty, expr_span);
+ let lt = self.temp(bool_ty, expr_span);
+
+ // len = len(slice)
+ self.cfg.push_assign(block, source_info, &len, Rvalue::Len(slice));
+ // lt = idx < len
+ self.cfg.push_assign(
+ block,
+ source_info,
+ <,
+ Rvalue::BinaryOp(
+ BinOp::Lt,
+ Operand::Copy(Place::from(index)),
+ Operand::Copy(len.clone()),
+ ),
+ );
+ let msg = BoundsCheck { len: Operand::Move(len), index: Operand::Copy(Place::from(index)) };
+ // assert!(lt, "...")
+ self.assert(block, Operand::Move(lt), true, msg, expr_span)
+ }
+
+ fn add_fake_borrows_of_base(
+ &mut self,
+ base_place: &PlaceBuilder<'tcx>,
+ block: BasicBlock,
+ fake_borrow_temps: &mut Vec<Local>,
+ expr_span: Span,
+ source_info: SourceInfo,
+ ) {
+ let tcx = self.hir.tcx();
+ let place_ty =
+ Place::ty_from(&base_place.local, &base_place.projection, &self.local_decls, tcx);
+ if let ty::Slice(_) = place_ty.ty.kind {
+ // We need to create fake borrows to ensure that the bounds
+ // check that we just did stays valid. Since we can't assign to
+ // unsized values, we only need to ensure that none of the
+ // pointers in the base place are modified.
+ for (idx, elem) in base_place.projection.iter().enumerate().rev() {
+ match elem {
+ ProjectionElem::Deref => {
+ let fake_borrow_deref_ty = Place::ty_from(
+ &base_place.local,
+ &base_place.projection[..idx],
+ &self.local_decls,
+ tcx,
+ )
+ .ty;
+ let fake_borrow_ty =
+ tcx.mk_imm_ref(tcx.lifetimes.re_erased, fake_borrow_deref_ty);
+ let fake_borrow_temp =
+ self.local_decls.push(LocalDecl::new_temp(fake_borrow_ty, expr_span));
+ let projection = tcx.intern_place_elems(&base_place.projection[..idx]);
+ self.cfg.push_assign(
+ block,
+ source_info,
+ &fake_borrow_temp.into(),
+ Rvalue::Ref(
+ tcx.lifetimes.re_erased,
+ BorrowKind::Shallow,
+ Place { local: base_place.local.clone(), projection },
+ ),
+ );
+ fake_borrow_temps.push(fake_borrow_temp);
+ }
+ ProjectionElem::Index(_) => {
+ let index_ty = Place::ty_from(
+ &base_place.local,
+ &base_place.projection[..idx],
+ &self.local_decls,
+ tcx,
+ );
+ match index_ty.ty.kind {
+ // The previous index expression has already
+ // done any index expressions needed here.
+ ty::Slice(_) => break,
+ ty::Array(..) => (),
+ _ => bug!("unexpected index base"),
+ }
+ }
+ ProjectionElem::Field(..)
+ | ProjectionElem::Downcast(..)
+ | ProjectionElem::ConstantIndex { .. }
+ | ProjectionElem::Subslice { .. } => (),
+ }
+ }
+ }
+ }
+
+ fn read_fake_borrows(
+ &mut self,
+ bb: BasicBlock,
+ fake_borrow_temps: &mut Vec<Local>,
+ source_info: SourceInfo,
+ ) {
+ // All indexes have been evaluated now, read all of the
+ // fake borrows so that they are live across those index
+ // expressions.
+ for temp in fake_borrow_temps {
+ self.cfg.push_fake_read(bb, source_info, FakeReadCause::ForIndex, Place::from(*temp));
+ }
+ }
+}
--- /dev/null
+//! See docs in `build/expr/mod.rs`.
+
+use rustc_index::vec::Idx;
+
+use crate::build::expr::category::{Category, RvalueFunc};
+use crate::build::{BlockAnd, BlockAndExtension, Builder};
+use crate::hair::*;
+use rustc::middle::region;
+use rustc::mir::interpret::PanicInfo;
+use rustc::mir::*;
+use rustc::ty::{self, Ty, UpvarSubsts};
+use rustc_span::Span;
+
+impl<'a, 'tcx> Builder<'a, 'tcx> {
+ /// Returns an rvalue suitable for use until the end of the current
+ /// scope expression.
+ ///
+ /// The operand returned from this function will *not be valid* after
+ /// an ExprKind::Scope is passed, so please do *not* return it from
+ /// functions to avoid bad miscompiles.
+ crate fn as_local_rvalue<M>(&mut self, block: BasicBlock, expr: M) -> BlockAnd<Rvalue<'tcx>>
+ where
+ M: Mirror<'tcx, Output = Expr<'tcx>>,
+ {
+ let local_scope = self.local_scope();
+ self.as_rvalue(block, local_scope, expr)
+ }
+
+ /// Compile `expr`, yielding an rvalue.
+ fn as_rvalue<M>(
+ &mut self,
+ block: BasicBlock,
+ scope: Option<region::Scope>,
+ expr: M,
+ ) -> BlockAnd<Rvalue<'tcx>>
+ where
+ M: Mirror<'tcx, Output = Expr<'tcx>>,
+ {
+ let expr = self.hir.mirror(expr);
+ self.expr_as_rvalue(block, scope, expr)
+ }
+
+ fn expr_as_rvalue(
+ &mut self,
+ mut block: BasicBlock,
+ scope: Option<region::Scope>,
+ expr: Expr<'tcx>,
+ ) -> BlockAnd<Rvalue<'tcx>> {
+ debug!("expr_as_rvalue(block={:?}, scope={:?}, expr={:?})", block, scope, expr);
+
+ let this = self;
+ let expr_span = expr.span;
+ let source_info = this.source_info(expr_span);
+
+ match expr.kind {
+ ExprKind::Scope { region_scope, lint_level, value } => {
+ let region_scope = (region_scope, source_info);
+ this.in_scope(region_scope, lint_level, |this| this.as_rvalue(block, scope, value))
+ }
+ ExprKind::Repeat { value, count } => {
+ let value_operand = unpack!(block = this.as_operand(block, scope, value));
+ block.and(Rvalue::Repeat(value_operand, count))
+ }
+ ExprKind::Binary { op, lhs, rhs } => {
+ let lhs = unpack!(block = this.as_operand(block, scope, lhs));
+ let rhs = unpack!(block = this.as_operand(block, scope, rhs));
+ this.build_binary_op(block, op, expr_span, expr.ty, lhs, rhs)
+ }
+ ExprKind::Unary { op, arg } => {
+ let arg = unpack!(block = this.as_operand(block, scope, arg));
+ // Check for -MIN on signed integers
+ if this.hir.check_overflow() && op == UnOp::Neg && expr.ty.is_signed() {
+ let bool_ty = this.hir.bool_ty();
+
+ let minval = this.minval_literal(expr_span, expr.ty);
+ let is_min = this.temp(bool_ty, expr_span);
+
+ this.cfg.push_assign(
+ block,
+ source_info,
+ &is_min,
+ Rvalue::BinaryOp(BinOp::Eq, arg.to_copy(), minval),
+ );
+
+ block = this.assert(
+ block,
+ Operand::Move(is_min),
+ false,
+ PanicInfo::OverflowNeg,
+ expr_span,
+ );
+ }
+ block.and(Rvalue::UnaryOp(op, arg))
+ }
+ ExprKind::Box { value } => {
+ let value = this.hir.mirror(value);
+ // The `Box<T>` temporary created here is not a part of the HIR,
+ // and therefore is not considered during generator OIBIT
+ // determination. See the comment about `box` at `yield_in_scope`.
+ let result = this.local_decls.push(LocalDecl::new_internal(expr.ty, expr_span));
+ this.cfg.push(
+ block,
+ Statement { source_info, kind: StatementKind::StorageLive(result) },
+ );
+ if let Some(scope) = scope {
+ // schedule a shallow free of that memory, lest we unwind:
+ this.schedule_drop_storage_and_value(expr_span, scope, result);
+ }
+
+ // malloc some memory of suitable type (thus far, uninitialized):
+ let box_ = Rvalue::NullaryOp(NullOp::Box, value.ty);
+ this.cfg.push_assign(block, source_info, &Place::from(result), box_);
+
+ // initialize the box contents:
+ unpack!(
+ block = this.into(
+ &this.hir.tcx().mk_place_deref(Place::from(result)),
+ block,
+ value
+ )
+ );
+ block.and(Rvalue::Use(Operand::Move(Place::from(result))))
+ }
+ ExprKind::Cast { source } => {
+ let source = unpack!(block = this.as_operand(block, scope, source));
+ block.and(Rvalue::Cast(CastKind::Misc, source, expr.ty))
+ }
+ ExprKind::Pointer { cast, source } => {
+ let source = unpack!(block = this.as_operand(block, scope, source));
+ block.and(Rvalue::Cast(CastKind::Pointer(cast), source, expr.ty))
+ }
+ ExprKind::Array { fields } => {
+ // (*) We would (maybe) be closer to codegen if we
+ // handled this and other aggregate cases via
+ // `into()`, not `as_rvalue` -- in that case, instead
+ // of generating
+ //
+ // let tmp1 = ...1;
+ // let tmp2 = ...2;
+ // dest = Rvalue::Aggregate(Foo, [tmp1, tmp2])
+ //
+ // we could just generate
+ //
+ // dest.f = ...1;
+ // dest.g = ...2;
+ //
+ // The problem is that then we would need to:
+ //
+ // (a) have a more complex mechanism for handling
+ // partial cleanup;
+ // (b) distinguish the case where the type `Foo` has a
+ // destructor, in which case creating an instance
+ // as a whole "arms" the destructor, and you can't
+ // write individual fields; and,
+ // (c) handle the case where the type Foo has no
+ // fields. We don't want `let x: ();` to compile
+ // to the same MIR as `let x = ();`.
+
+ // first process the set of fields
+ let el_ty = expr.ty.sequence_element_type(this.hir.tcx());
+ let fields: Vec<_> = fields
+ .into_iter()
+ .map(|f| unpack!(block = this.as_operand(block, scope, f)))
+ .collect();
+
+ block.and(Rvalue::Aggregate(box AggregateKind::Array(el_ty), fields))
+ }
+ ExprKind::Tuple { fields } => {
+ // see (*) above
+ // first process the set of fields
+ let fields: Vec<_> = fields
+ .into_iter()
+ .map(|f| unpack!(block = this.as_operand(block, scope, f)))
+ .collect();
+
+ block.and(Rvalue::Aggregate(box AggregateKind::Tuple, fields))
+ }
+ ExprKind::Closure { closure_id, substs, upvars, movability } => {
+ // see (*) above
+ let operands: Vec<_> = upvars
+ .into_iter()
+ .map(|upvar| {
+ let upvar = this.hir.mirror(upvar);
+ match Category::of(&upvar.kind) {
+ // Use as_place to avoid creating a temporary when
+ // moving a variable into a closure, so that
+ // borrowck knows which variables to mark as being
+ // used as mut. This is OK here because the upvar
+ // expressions have no side effects and act on
+ // disjoint places.
+ // This occurs when capturing by copy/move, while
+ // by reference captures use as_operand
+ Some(Category::Place) => {
+ let place = unpack!(block = this.as_place(block, upvar));
+ this.consume_by_copy_or_move(place)
+ }
+ _ => {
+ // Turn mutable borrow captures into unique
+ // borrow captures when capturing an immutable
+ // variable. This is sound because the mutation
+ // that caused the capture will cause an error.
+ match upvar.kind {
+ ExprKind::Borrow {
+ borrow_kind:
+ BorrowKind::Mut { allow_two_phase_borrow: false },
+ arg,
+ } => unpack!(
+ block = this.limit_capture_mutability(
+ upvar.span, upvar.ty, scope, block, arg,
+ )
+ ),
+ _ => unpack!(block = this.as_operand(block, scope, upvar)),
+ }
+ }
+ }
+ })
+ .collect();
+ let result = match substs {
+ UpvarSubsts::Generator(substs) => {
+ // We implicitly set the discriminant to 0. See
+ // librustc_mir/transform/deaggregator.rs for details.
+ let movability = movability.unwrap();
+ box AggregateKind::Generator(closure_id, substs, movability)
+ }
+ UpvarSubsts::Closure(substs) => box AggregateKind::Closure(closure_id, substs),
+ };
+ block.and(Rvalue::Aggregate(result, operands))
+ }
+ ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => {
+ block = unpack!(this.stmt_expr(block, expr, None));
+ block.and(this.unit_rvalue())
+ }
+ ExprKind::Yield { value } => {
+ let value = unpack!(block = this.as_operand(block, scope, value));
+ let resume = this.cfg.start_new_block();
+ let cleanup = this.generator_drop_cleanup();
+ this.cfg.terminate(
+ block,
+ source_info,
+ TerminatorKind::Yield { value: value, resume: resume, drop: cleanup },
+ );
+ resume.and(this.unit_rvalue())
+ }
+ ExprKind::Literal { .. }
+ | ExprKind::StaticRef { .. }
+ | ExprKind::Block { .. }
+ | ExprKind::Match { .. }
+ | ExprKind::NeverToAny { .. }
+ | ExprKind::Use { .. }
+ | ExprKind::Borrow { .. }
+ | ExprKind::AddressOf { .. }
+ | ExprKind::Adt { .. }
+ | ExprKind::Loop { .. }
+ | ExprKind::LogicalOp { .. }
+ | ExprKind::Call { .. }
+ | ExprKind::Field { .. }
+ | ExprKind::Deref { .. }
+ | ExprKind::Index { .. }
+ | ExprKind::VarRef { .. }
+ | ExprKind::SelfRef
+ | ExprKind::Break { .. }
+ | ExprKind::Continue { .. }
+ | ExprKind::Return { .. }
+ | ExprKind::InlineAsm { .. }
+ | ExprKind::PlaceTypeAscription { .. }
+ | ExprKind::ValueTypeAscription { .. } => {
+ // these do not have corresponding `Rvalue` variants,
+ // so make an operand and then return that
+ debug_assert!(match Category::of(&expr.kind) {
+ Some(Category::Rvalue(RvalueFunc::AsRvalue)) => false,
+ _ => true,
+ });
+ let operand = unpack!(block = this.as_operand(block, scope, expr));
+ block.and(Rvalue::Use(operand))
+ }
+ }
+ }
+
+ crate fn build_binary_op(
+ &mut self,
+ mut block: BasicBlock,
+ op: BinOp,
+ span: Span,
+ ty: Ty<'tcx>,
+ lhs: Operand<'tcx>,
+ rhs: Operand<'tcx>,
+ ) -> BlockAnd<Rvalue<'tcx>> {
+ let source_info = self.source_info(span);
+ let bool_ty = self.hir.bool_ty();
+ if self.hir.check_overflow() && op.is_checkable() && ty.is_integral() {
+ let result_tup = self.hir.tcx().intern_tup(&[ty, bool_ty]);
+ let result_value = self.temp(result_tup, span);
+
+ self.cfg.push_assign(
+ block,
+ source_info,
+ &result_value,
+ Rvalue::CheckedBinaryOp(op, lhs, rhs),
+ );
+ let val_fld = Field::new(0);
+ let of_fld = Field::new(1);
+
+ let tcx = self.hir.tcx();
+ let val = tcx.mk_place_field(result_value.clone(), val_fld, ty);
+ let of = tcx.mk_place_field(result_value, of_fld, bool_ty);
+
+ let err = PanicInfo::Overflow(op);
+
+ block = self.assert(block, Operand::Move(of), false, err, span);
+
+ block.and(Rvalue::Use(Operand::Move(val)))
+ } else {
+ if ty.is_integral() && (op == BinOp::Div || op == BinOp::Rem) {
+ // Checking division and remainder is more complex, since we 1. always check
+ // and 2. there are two possible failure cases, divide-by-zero and overflow.
+
+ let zero_err = if op == BinOp::Div {
+ PanicInfo::DivisionByZero
+ } else {
+ PanicInfo::RemainderByZero
+ };
+ let overflow_err = PanicInfo::Overflow(op);
+
+ // Check for / 0
+ let is_zero = self.temp(bool_ty, span);
+ let zero = self.zero_literal(span, ty);
+ self.cfg.push_assign(
+ block,
+ source_info,
+ &is_zero,
+ Rvalue::BinaryOp(BinOp::Eq, rhs.to_copy(), zero),
+ );
+
+ block = self.assert(block, Operand::Move(is_zero), false, zero_err, span);
+
+ // We only need to check for the overflow in one case:
+ // MIN / -1, and only for signed values.
+ if ty.is_signed() {
+ let neg_1 = self.neg_1_literal(span, ty);
+ let min = self.minval_literal(span, ty);
+
+ let is_neg_1 = self.temp(bool_ty, span);
+ let is_min = self.temp(bool_ty, span);
+ let of = self.temp(bool_ty, span);
+
+ // this does (rhs == -1) & (lhs == MIN). It could short-circuit instead
+
+ self.cfg.push_assign(
+ block,
+ source_info,
+ &is_neg_1,
+ Rvalue::BinaryOp(BinOp::Eq, rhs.to_copy(), neg_1),
+ );
+ self.cfg.push_assign(
+ block,
+ source_info,
+ &is_min,
+ Rvalue::BinaryOp(BinOp::Eq, lhs.to_copy(), min),
+ );
+
+ let is_neg_1 = Operand::Move(is_neg_1);
+ let is_min = Operand::Move(is_min);
+ self.cfg.push_assign(
+ block,
+ source_info,
+ &of,
+ Rvalue::BinaryOp(BinOp::BitAnd, is_neg_1, is_min),
+ );
+
+ block = self.assert(block, Operand::Move(of), false, overflow_err, span);
+ }
+ }
+
+ block.and(Rvalue::BinaryOp(op, lhs, rhs))
+ }
+ }
+
+ fn limit_capture_mutability(
+ &mut self,
+ upvar_span: Span,
+ upvar_ty: Ty<'tcx>,
+ temp_lifetime: Option<region::Scope>,
+ mut block: BasicBlock,
+ arg: ExprRef<'tcx>,
+ ) -> BlockAnd<Operand<'tcx>> {
+ let this = self;
+
+ let source_info = this.source_info(upvar_span);
+ let temp = this.local_decls.push(LocalDecl::new_temp(upvar_ty, upvar_span));
+
+ this.cfg.push(block, Statement { source_info, kind: StatementKind::StorageLive(temp) });
+
+ let arg_place = unpack!(block = this.as_place(block, arg));
+
+ let mutability = match arg_place.as_ref() {
+ PlaceRef { local, projection: &[] } => this.local_decls[*local].mutability,
+ PlaceRef { local, projection: &[ProjectionElem::Deref] } => {
+ debug_assert!(
+ this.local_decls[*local].is_ref_for_guard(),
+ "Unexpected capture place",
+ );
+ this.local_decls[*local].mutability
+ }
+ PlaceRef {
+ ref local,
+ projection: &[ref proj_base @ .., ProjectionElem::Field(upvar_index, _)],
+ }
+ | PlaceRef {
+ ref local,
+ projection:
+ &[ref proj_base @ .., ProjectionElem::Field(upvar_index, _), ProjectionElem::Deref],
+ } => {
+ let place = PlaceRef { local, projection: proj_base };
+
+ // Not projected from the implicit `self` in a closure.
+ debug_assert!(
+ match place.local_or_deref_local() {
+ Some(local) => local == Local::new(1),
+ None => false,
+ },
+ "Unexpected capture place"
+ );
+ // Not in a closure
+ debug_assert!(
+ this.upvar_mutbls.len() > upvar_index.index(),
+ "Unexpected capture place"
+ );
+ this.upvar_mutbls[upvar_index.index()]
+ }
+ _ => bug!("Unexpected capture place"),
+ };
+
+ let borrow_kind = match mutability {
+ Mutability::Not => BorrowKind::Unique,
+ Mutability::Mut => BorrowKind::Mut { allow_two_phase_borrow: false },
+ };
+
+ this.cfg.push_assign(
+ block,
+ source_info,
+ &Place::from(temp),
+ Rvalue::Ref(this.hir.tcx().lifetimes.re_erased, borrow_kind, arg_place),
+ );
+
+ // In constants, temp_lifetime is None. We should not need to drop
+ // anything because no values with a destructor can be created in
+ // a constant at this time, even if the type may need dropping.
+ if let Some(temp_lifetime) = temp_lifetime {
+ this.schedule_drop_storage_and_value(upvar_span, temp_lifetime, temp);
+ }
+
+ block.and(Operand::Move(Place::from(temp)))
+ }
+
+ // Helper to get a `-1` value of the appropriate type
+ fn neg_1_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> {
+ let param_ty = ty::ParamEnv::empty().and(ty);
+ let bits = self.hir.tcx().layout_of(param_ty).unwrap().size.bits();
+ let n = (!0u128) >> (128 - bits);
+ let literal = ty::Const::from_bits(self.hir.tcx(), n, param_ty);
+
+ self.literal_operand(span, literal)
+ }
+
+ // Helper to get the minimum value of the appropriate type
+ fn minval_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> {
+ assert!(ty.is_signed());
+ let param_ty = ty::ParamEnv::empty().and(ty);
+ let bits = self.hir.tcx().layout_of(param_ty).unwrap().size.bits();
+ let n = 1 << (bits - 1);
+ let literal = ty::Const::from_bits(self.hir.tcx(), n, param_ty);
+
+ self.literal_operand(span, literal)
+ }
+}
--- /dev/null
+//! See docs in build/expr/mod.rs
+
+use crate::build::scope::DropKind;
+use crate::build::{BlockAnd, BlockAndExtension, Builder};
+use crate::hair::*;
+use rustc::middle::region;
+use rustc::mir::*;
+use rustc_hir as hir;
+use rustc_span::symbol::sym;
+
+impl<'a, 'tcx> Builder<'a, 'tcx> {
+ /// Compile `expr` into a fresh temporary. This is used when building
+ /// up rvalues so as to freeze the value that will be consumed.
+ crate fn as_temp<M>(
+ &mut self,
+ block: BasicBlock,
+ temp_lifetime: Option<region::Scope>,
+ expr: M,
+ mutability: Mutability,
+ ) -> BlockAnd<Local>
+ where
+ M: Mirror<'tcx, Output = Expr<'tcx>>,
+ {
+ let expr = self.hir.mirror(expr);
+ self.expr_as_temp(block, temp_lifetime, expr, mutability)
+ }
+
+ fn expr_as_temp(
+ &mut self,
+ mut block: BasicBlock,
+ temp_lifetime: Option<region::Scope>,
+ expr: Expr<'tcx>,
+ mutability: Mutability,
+ ) -> BlockAnd<Local> {
+ debug!(
+ "expr_as_temp(block={:?}, temp_lifetime={:?}, expr={:?}, mutability={:?})",
+ block, temp_lifetime, expr, mutability
+ );
+ let this = self;
+
+ let expr_span = expr.span;
+ let source_info = this.source_info(expr_span);
+ if let ExprKind::Scope { region_scope, lint_level, value } = expr.kind {
+ return this.in_scope((region_scope, source_info), lint_level, |this| {
+ this.as_temp(block, temp_lifetime, value, mutability)
+ });
+ }
+
+ let expr_ty = expr.ty;
+ let temp = {
+ let mut local_decl = LocalDecl::new_temp(expr_ty, expr_span);
+ if mutability == Mutability::Not {
+ local_decl = local_decl.immutable();
+ }
+
+ debug!("creating temp {:?} with block_context: {:?}", local_decl, this.block_context);
+ // Find out whether this temp is being created within the
+ // tail expression of a block whose result is ignored.
+ if let Some(tail_info) = this.block_context.currently_in_block_tail() {
+ local_decl = local_decl.block_tail(tail_info);
+ }
+ if let ExprKind::StaticRef { def_id, .. } = expr.kind {
+ let is_thread_local = this.hir.tcx().has_attr(def_id, sym::thread_local);
+ local_decl.local_info = LocalInfo::StaticRef { def_id, is_thread_local };
+ }
+ this.local_decls.push(local_decl)
+ };
+ let temp_place = &Place::from(temp);
+
+ match expr.kind {
+ // Don't bother with StorageLive and Dead for these temporaries,
+ // they are never assigned.
+ ExprKind::Break { .. } | ExprKind::Continue { .. } | ExprKind::Return { .. } => (),
+ ExprKind::Block { body: hir::Block { expr: None, targeted_by_break: false, .. } }
+ if expr_ty.is_never() =>
+ {
+ ()
+ }
+ _ => {
+ this.cfg
+ .push(block, Statement { source_info, kind: StatementKind::StorageLive(temp) });
+
+ // In constants, `temp_lifetime` is `None` for temporaries that
+ // live for the `'static` lifetime. Thus we do not drop these
+ // temporaries and simply leak them.
+ // This is equivalent to what `let x = &foo();` does in
+ // functions. The temporary is lifted to their surrounding
+ // scope. In a function that means the temporary lives until
+ // just before the function returns. In constants that means it
+ // outlives the constant's initialization value computation.
+ // Anything outliving a constant must have the `'static`
+ // lifetime and live forever.
+ // Anything with a shorter lifetime (e.g the `&foo()` in
+ // `bar(&foo())` or anything within a block will keep the
+ // regular drops just like runtime code.
+ if let Some(temp_lifetime) = temp_lifetime {
+ this.schedule_drop(expr_span, temp_lifetime, temp, DropKind::Storage);
+ }
+ }
+ }
+
+ unpack!(block = this.into(temp_place, block, expr));
+
+ if let Some(temp_lifetime) = temp_lifetime {
+ this.schedule_drop(expr_span, temp_lifetime, temp, DropKind::Value);
+ }
+
+ block.and(temp)
+ }
+}
--- /dev/null
+use crate::hair::*;
+
+#[derive(Debug, PartialEq)]
+crate enum Category {
+ // An assignable memory location like `x`, `x.f`, `foo()[3]`, that
+ // sort of thing. Something that could appear on the LHS of an `=`
+ // sign.
+ Place,
+
+ // A literal like `23` or `"foo"`. Does not include constant
+ // expressions like `3 + 5`.
+ Constant,
+
+ // Something that generates a new value at runtime, like `x + y`
+ // or `foo()`.
+ Rvalue(RvalueFunc),
+}
+
+// Rvalues fall into different "styles" that will determine which fn
+// is best suited to generate them.
+#[derive(Debug, PartialEq)]
+crate enum RvalueFunc {
+ // Best generated by `into`. This is generally exprs that
+ // cause branching, like `match`, but also includes calls.
+ Into,
+
+ // Best generated by `as_rvalue`. This is usually the case.
+ AsRvalue,
+}
+
+/// Determines the category for a given expression. Note that scope
+/// and paren expressions have no category.
+impl Category {
+ crate fn of(ek: &ExprKind<'_>) -> Option<Category> {
+ match *ek {
+ ExprKind::Scope { .. } => None,
+
+ ExprKind::Field { .. }
+ | ExprKind::Deref { .. }
+ | ExprKind::Index { .. }
+ | ExprKind::SelfRef
+ | ExprKind::VarRef { .. }
+ | ExprKind::PlaceTypeAscription { .. }
+ | ExprKind::ValueTypeAscription { .. } => Some(Category::Place),
+
+ ExprKind::LogicalOp { .. }
+ | ExprKind::Match { .. }
+ | ExprKind::NeverToAny { .. }
+ | ExprKind::Use { .. }
+ | ExprKind::Adt { .. }
+ | ExprKind::Borrow { .. }
+ | ExprKind::AddressOf { .. }
+ | ExprKind::Call { .. } => Some(Category::Rvalue(RvalueFunc::Into)),
+
+ ExprKind::Array { .. }
+ | ExprKind::Tuple { .. }
+ | ExprKind::Closure { .. }
+ | ExprKind::Unary { .. }
+ | ExprKind::Binary { .. }
+ | ExprKind::Box { .. }
+ | ExprKind::Cast { .. }
+ | ExprKind::Pointer { .. }
+ | ExprKind::Repeat { .. }
+ | ExprKind::Assign { .. }
+ | ExprKind::AssignOp { .. }
+ | ExprKind::Yield { .. }
+ | ExprKind::InlineAsm { .. } => Some(Category::Rvalue(RvalueFunc::AsRvalue)),
+
+ ExprKind::Literal { .. } | ExprKind::StaticRef { .. } => Some(Category::Constant),
+
+ ExprKind::Loop { .. }
+ | ExprKind::Block { .. }
+ | ExprKind::Break { .. }
+ | ExprKind::Continue { .. }
+ | ExprKind::Return { .. } =>
+ // FIXME(#27840) these probably want their own
+ // category, like "nonterminating"
+ {
+ Some(Category::Rvalue(RvalueFunc::Into))
+ }
+ }
+ }
+}
--- /dev/null
+//! See docs in build/expr/mod.rs
+
+use crate::build::expr::category::{Category, RvalueFunc};
+use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
+use crate::hair::*;
+use rustc::mir::*;
+use rustc::ty::{self, CanonicalUserTypeAnnotation};
+use rustc_data_structures::fx::FxHashMap;
+use rustc_hir as hir;
+use rustc_span::symbol::sym;
+
+use rustc_target::spec::abi::Abi;
+
+impl<'a, 'tcx> Builder<'a, 'tcx> {
+ /// Compile `expr`, storing the result into `destination`, which
+ /// is assumed to be uninitialized.
+ crate fn into_expr(
+ &mut self,
+ destination: &Place<'tcx>,
+ mut block: BasicBlock,
+ expr: Expr<'tcx>,
+ ) -> BlockAnd<()> {
+ debug!("into_expr(destination={:?}, block={:?}, expr={:?})", destination, block, expr);
+
+ // since we frequently have to reference `self` from within a
+ // closure, where `self` would be shadowed, it's easier to
+ // just use the name `this` uniformly
+ let this = self;
+ let expr_span = expr.span;
+ let source_info = this.source_info(expr_span);
+
+ let expr_is_block_or_scope = match expr.kind {
+ ExprKind::Block { .. } => true,
+ ExprKind::Scope { .. } => true,
+ _ => false,
+ };
+
+ if !expr_is_block_or_scope {
+ this.block_context.push(BlockFrame::SubExpr);
+ }
+
+ let block_and = match expr.kind {
+ ExprKind::Scope { region_scope, lint_level, value } => {
+ let region_scope = (region_scope, source_info);
+ this.in_scope(region_scope, lint_level, |this| this.into(destination, block, value))
+ }
+ ExprKind::Block { body: ast_block } => {
+ this.ast_block(destination, block, ast_block, source_info)
+ }
+ ExprKind::Match { scrutinee, arms } => {
+ this.match_expr(destination, expr_span, block, scrutinee, arms)
+ }
+ ExprKind::NeverToAny { source } => {
+ let source = this.hir.mirror(source);
+ let is_call = match source.kind {
+ ExprKind::Call { .. } => true,
+ _ => false,
+ };
+
+ // (#66975) Source could be a const of type `!`, so has to
+ // exist in the generated MIR.
+ unpack!(block = this.as_temp(block, this.local_scope(), source, Mutability::Mut,));
+
+ // This is an optimization. If the expression was a call then we already have an
+ // unreachable block. Don't bother to terminate it and create a new one.
+ if is_call {
+ block.unit()
+ } else {
+ this.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
+ let end_block = this.cfg.start_new_block();
+ end_block.unit()
+ }
+ }
+ ExprKind::LogicalOp { op, lhs, rhs } => {
+ // And:
+ //
+ // [block: If(lhs)] -true-> [else_block: If(rhs)] -true-> [true_block]
+ // | | (false)
+ // +----------false-----------+------------------> [false_block]
+ //
+ // Or:
+ //
+ // [block: If(lhs)] -false-> [else_block: If(rhs)] -true-> [true_block]
+ // | (true) | (false)
+ // [true_block] [false_block]
+
+ let (true_block, false_block, mut else_block, join_block) = (
+ this.cfg.start_new_block(),
+ this.cfg.start_new_block(),
+ this.cfg.start_new_block(),
+ this.cfg.start_new_block(),
+ );
+
+ let lhs = unpack!(block = this.as_local_operand(block, lhs));
+ let blocks = match op {
+ LogicalOp::And => (else_block, false_block),
+ LogicalOp::Or => (true_block, else_block),
+ };
+ let term = TerminatorKind::if_(this.hir.tcx(), lhs, blocks.0, blocks.1);
+ this.cfg.terminate(block, source_info, term);
+
+ let rhs = unpack!(else_block = this.as_local_operand(else_block, rhs));
+ let term = TerminatorKind::if_(this.hir.tcx(), rhs, true_block, false_block);
+ this.cfg.terminate(else_block, source_info, term);
+
+ this.cfg.push_assign_constant(
+ true_block,
+ source_info,
+ destination,
+ Constant { span: expr_span, user_ty: None, literal: this.hir.true_literal() },
+ );
+
+ this.cfg.push_assign_constant(
+ false_block,
+ source_info,
+ destination,
+ Constant { span: expr_span, user_ty: None, literal: this.hir.false_literal() },
+ );
+
+ // Link up both branches:
+ this.cfg.goto(true_block, source_info, join_block);
+ this.cfg.goto(false_block, source_info, join_block);
+ join_block.unit()
+ }
+ ExprKind::Loop { body } => {
+ // [block]
+ // |
+ // [loop_block] -> [body_block] -/eval. body/-> [body_block_end]
+ // | ^ |
+ // false link | |
+ // | +-----------------------------------------+
+ // +-> [diverge_cleanup]
+ // The false link is required to make sure borrowck considers unwinds through the
+ // body, even when the exact code in the body cannot unwind
+
+ let loop_block = this.cfg.start_new_block();
+ let exit_block = this.cfg.start_new_block();
+
+ // Start the loop.
+ this.cfg.goto(block, source_info, loop_block);
+
+ this.in_breakable_scope(
+ Some(loop_block),
+ exit_block,
+ destination.clone(),
+ move |this| {
+ // conduct the test, if necessary
+ let body_block = this.cfg.start_new_block();
+ let diverge_cleanup = this.diverge_cleanup();
+ this.cfg.terminate(
+ loop_block,
+ source_info,
+ TerminatorKind::FalseUnwind {
+ real_target: body_block,
+ unwind: Some(diverge_cleanup),
+ },
+ );
+
+ // The “return” value of the loop body must always be an unit. We therefore
+ // introduce a unit temporary as the destination for the loop body.
+ let tmp = this.get_unit_temp();
+ // Execute the body, branching back to the test.
+ let body_block_end = unpack!(this.into(&tmp, body_block, body));
+ this.cfg.goto(body_block_end, source_info, loop_block);
+ },
+ );
+ exit_block.unit()
+ }
+ ExprKind::Call { ty, fun, args, from_hir_call } => {
+ let intrinsic = match ty.kind {
+ ty::FnDef(def_id, _) => {
+ let f = ty.fn_sig(this.hir.tcx());
+ if f.abi() == Abi::RustIntrinsic || f.abi() == Abi::PlatformIntrinsic {
+ Some(this.hir.tcx().item_name(def_id))
+ } else {
+ None
+ }
+ }
+ _ => None,
+ };
+ let fun = unpack!(block = this.as_local_operand(block, fun));
+ if let Some(sym::move_val_init) = intrinsic {
+ // `move_val_init` has "magic" semantics - the second argument is
+ // always evaluated "directly" into the first one.
+
+ let mut args = args.into_iter();
+ let ptr = args.next().expect("0 arguments to `move_val_init`");
+ let val = args.next().expect("1 argument to `move_val_init`");
+ assert!(args.next().is_none(), ">2 arguments to `move_val_init`");
+
+ let ptr = this.hir.mirror(ptr);
+ let ptr_ty = ptr.ty;
+ // Create an *internal* temp for the pointer, so that unsafety
+ // checking won't complain about the raw pointer assignment.
+ let ptr_temp = this.local_decls.push(LocalDecl {
+ mutability: Mutability::Mut,
+ ty: ptr_ty,
+ user_ty: UserTypeProjections::none(),
+ source_info,
+ internal: true,
+ local_info: LocalInfo::Other,
+ is_block_tail: None,
+ });
+ let ptr_temp = Place::from(ptr_temp);
+ let block = unpack!(this.into(&ptr_temp, block, ptr));
+ this.into(&this.hir.tcx().mk_place_deref(ptr_temp), block, val)
+ } else {
+ let args: Vec<_> = args
+ .into_iter()
+ .map(|arg| unpack!(block = this.as_local_operand(block, arg)))
+ .collect();
+
+ let success = this.cfg.start_new_block();
+ let cleanup = this.diverge_cleanup();
+
+ this.record_operands_moved(&args);
+
+ this.cfg.terminate(
+ block,
+ source_info,
+ TerminatorKind::Call {
+ func: fun,
+ args,
+ cleanup: Some(cleanup),
+ // FIXME(varkor): replace this with an uninhabitedness-based check.
+ // This requires getting access to the current module to call
+ // `tcx.is_ty_uninhabited_from`, which is currently tricky to do.
+ destination: if expr.ty.is_never() {
+ None
+ } else {
+ Some((destination.clone(), success))
+ },
+ from_hir_call,
+ },
+ );
+ success.unit()
+ }
+ }
+ ExprKind::Use { source } => this.into(destination, block, source),
+ ExprKind::Borrow { arg, borrow_kind } => {
+ // We don't do this in `as_rvalue` because we use `as_place`
+ // for borrow expressions, so we cannot create an `RValue` that
+ // remains valid across user code. `as_rvalue` is usually called
+ // by this method anyway, so this shouldn't cause too many
+ // unnecessary temporaries.
+ let arg_place = match borrow_kind {
+ BorrowKind::Shared => unpack!(block = this.as_read_only_place(block, arg)),
+ _ => unpack!(block = this.as_place(block, arg)),
+ };
+ let borrow =
+ Rvalue::Ref(this.hir.tcx().lifetimes.re_erased, borrow_kind, arg_place);
+ this.cfg.push_assign(block, source_info, destination, borrow);
+ block.unit()
+ }
+ ExprKind::AddressOf { mutability, arg } => {
+ let place = match mutability {
+ hir::Mutability::Not => this.as_read_only_place(block, arg),
+ hir::Mutability::Mut => this.as_place(block, arg),
+ };
+ let address_of = Rvalue::AddressOf(mutability, unpack!(block = place));
+ this.cfg.push_assign(block, source_info, destination, address_of);
+ block.unit()
+ }
+ ExprKind::Adt { adt_def, variant_index, substs, user_ty, fields, base } => {
+ // See the notes for `ExprKind::Array` in `as_rvalue` and for
+ // `ExprKind::Borrow` above.
+ let is_union = adt_def.is_union();
+ let active_field_index = if is_union { Some(fields[0].name.index()) } else { None };
+
+ let scope = this.local_scope();
+
+ // first process the set of fields that were provided
+ // (evaluating them in order given by user)
+ let fields_map: FxHashMap<_, _> = fields
+ .into_iter()
+ .map(|f| (f.name, unpack!(block = this.as_operand(block, scope, f.expr))))
+ .collect();
+
+ let field_names = this.hir.all_fields(adt_def, variant_index);
+
+ let fields =
+ if let Some(FruInfo { base, field_types }) = base {
+ let base = unpack!(block = this.as_place(block, base));
+
+ // MIR does not natively support FRU, so for each
+ // base-supplied field, generate an operand that
+ // reads it from the base.
+ field_names
+ .into_iter()
+ .zip(field_types.into_iter())
+ .map(|(n, ty)| match fields_map.get(&n) {
+ Some(v) => v.clone(),
+ None => this.consume_by_copy_or_move(
+ this.hir.tcx().mk_place_field(base.clone(), n, ty),
+ ),
+ })
+ .collect()
+ } else {
+ field_names.iter().filter_map(|n| fields_map.get(n).cloned()).collect()
+ };
+
+ let inferred_ty = expr.ty;
+ let user_ty = user_ty.map(|ty| {
+ this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
+ span: source_info.span,
+ user_ty: ty,
+ inferred_ty,
+ })
+ });
+ let adt = box AggregateKind::Adt(
+ adt_def,
+ variant_index,
+ substs,
+ user_ty,
+ active_field_index,
+ );
+ this.cfg.push_assign(
+ block,
+ source_info,
+ destination,
+ Rvalue::Aggregate(adt, fields),
+ );
+ block.unit()
+ }
+
+ // These cases don't actually need a destination
+ ExprKind::Assign { .. }
+ | ExprKind::AssignOp { .. }
+ | ExprKind::Continue { .. }
+ | ExprKind::Break { .. }
+ | ExprKind::InlineAsm { .. }
+ | ExprKind::Return { .. } => {
+ unpack!(block = this.stmt_expr(block, expr, None));
+ this.cfg.push_assign_unit(block, source_info, destination);
+ block.unit()
+ }
+
+ // Avoid creating a temporary
+ ExprKind::VarRef { .. }
+ | ExprKind::SelfRef
+ | ExprKind::PlaceTypeAscription { .. }
+ | ExprKind::ValueTypeAscription { .. } => {
+ debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
+
+ let place = unpack!(block = this.as_place(block, expr));
+ let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
+ this.cfg.push_assign(block, source_info, destination, rvalue);
+ block.unit()
+ }
+ ExprKind::Index { .. } | ExprKind::Deref { .. } | ExprKind::Field { .. } => {
+ debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
+
+ // Create a "fake" temporary variable so that we check that the
+ // value is Sized. Usually, this is caught in type checking, but
+ // in the case of box expr there is no such check.
+ if !destination.projection.is_empty() {
+ this.local_decls.push(LocalDecl::new_temp(expr.ty, expr.span));
+ }
+
+ debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
+
+ let place = unpack!(block = this.as_place(block, expr));
+ let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
+ this.cfg.push_assign(block, source_info, destination, rvalue);
+ block.unit()
+ }
+
+ // these are the cases that are more naturally handled by some other mode
+ ExprKind::Unary { .. }
+ | ExprKind::Binary { .. }
+ | ExprKind::Box { .. }
+ | ExprKind::Cast { .. }
+ | ExprKind::Pointer { .. }
+ | ExprKind::Repeat { .. }
+ | ExprKind::Array { .. }
+ | ExprKind::Tuple { .. }
+ | ExprKind::Closure { .. }
+ | ExprKind::Literal { .. }
+ | ExprKind::StaticRef { .. }
+ | ExprKind::Yield { .. } => {
+ debug_assert!(match Category::of(&expr.kind).unwrap() {
+ // should be handled above
+ Category::Rvalue(RvalueFunc::Into) => false,
+
+ // must be handled above or else we get an
+ // infinite loop in the builder; see
+ // e.g., `ExprKind::VarRef` above
+ Category::Place => false,
+
+ _ => true,
+ });
+
+ let rvalue = unpack!(block = this.as_local_rvalue(block, expr));
+ this.cfg.push_assign(block, source_info, destination, rvalue);
+ block.unit()
+ }
+ };
+
+ if !expr_is_block_or_scope {
+ let popped = this.block_context.pop();
+ assert!(popped.is_some());
+ }
+
+ block_and
+ }
+}
--- /dev/null
+//! Builds MIR from expressions. As a caller into this module, you
+//! have many options, but the first thing you have to decide is
+//! whether you are evaluating this expression for its *value*, its
+//! *location*, or as a *constant*.
+//!
+//! Typically, you want the value: e.g., if you are doing `expr_a +
+//! expr_b`, you want the values of those expressions. In that case,
+//! you want one of the following functions. Note that if the expr has
+//! a type that is not `Copy`, then using any of these functions will
+//! "move" the value out of its current home (if any).
+//!
+//! - `into` -- writes the value into a specific location, which
+//! should be uninitialized
+//! - `as_operand` -- evaluates the value and yields an `Operand`,
+//! suitable for use as an argument to an `Rvalue`
+//! - `as_temp` -- evaluates into a temporary; this is similar to `as_operand`
+//! except it always returns a fresh place, even for constants
+//! - `as_rvalue` -- yields an `Rvalue`, suitable for use in an assignment;
+//! as of this writing, never needed outside of the `expr` module itself
+//!
+//! Sometimes though want the expression's *location*. An example
+//! would be during a match statement, or the operand of the `&`
+//! operator. In that case, you want `as_place`. This will create a
+//! temporary if necessary.
+//!
+//! Finally, if it's a constant you seek, then call
+//! `as_constant`. This creates a `Constant<H>`, but naturally it can
+//! only be used on constant expressions and hence is needed only in
+//! very limited contexts.
+//!
+//! ### Implementation notes
+//!
+//! For any given kind of expression, there is generally one way that
+//! can be lowered most naturally. This is specified by the
+//! `Category::of` function in the `category` module. For example, a
+//! struct expression (or other expression that creates a new value)
+//! is typically easiest to write in terms of `as_rvalue` or `into`,
+//! whereas a reference to a field is easiest to write in terms of
+//! `as_place`. (The exception to this is scope and paren
+//! expressions, which have no category.)
+//!
+//! Therefore, the various functions above make use of one another in
+//! a descending fashion. For any given expression, you should pick
+//! the most suitable spot to implement it, and then just let the
+//! other fns cycle around. The handoff works like this:
+//!
+//! - `into(place)` -> fallback is to create a rvalue with `as_rvalue` and assign it to `place`
+//! - `as_rvalue` -> fallback is to create an Operand with `as_operand` and use `Rvalue::use`
+//! - `as_operand` -> either invokes `as_constant` or `as_temp`
+//! - `as_constant` -> (no fallback)
+//! - `as_temp` -> creates a temporary and either calls `as_place` or `into`
+//! - `as_place` -> for rvalues, falls back to `as_temp` and returns that
+//!
+//! As you can see, there is a cycle where `into` can (in theory) fallback to `as_temp`
+//! which can fallback to `into`. So if one of the `ExprKind` variants is not, in fact,
+//! implemented in the category where it is supposed to be, there will be a problem.
+//!
+//! Of those fallbacks, the most interesting one is `into`, because
+//! it discriminates based on the category of the expression. This is
+//! basically the point where the "by value" operations are bridged
+//! over to the "by reference" mode (`as_place`).
+
+mod as_constant;
+mod as_operand;
+mod as_place;
+mod as_rvalue;
+mod as_temp;
+mod category;
+mod into;
+mod stmt;
--- /dev/null
+use crate::build::scope::BreakableTarget;
+use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
+use crate::hair::*;
+use rustc::middle::region;
+use rustc::mir::*;
+
+impl<'a, 'tcx> Builder<'a, 'tcx> {
+ /// Builds a block of MIR statements to evaluate the HAIR `expr`.
+ /// If the original expression was an AST statement,
+ /// (e.g., `some().code(&here());`) then `opt_stmt_span` is the
+ /// span of that statement (including its semicolon, if any).
+ /// The scope is used if a statement temporary must be dropped.
+ crate fn stmt_expr(
+ &mut self,
+ mut block: BasicBlock,
+ expr: Expr<'tcx>,
+ statement_scope: Option<region::Scope>,
+ ) -> BlockAnd<()> {
+ let this = self;
+ let expr_span = expr.span;
+ let source_info = this.source_info(expr.span);
+ // Handle a number of expressions that don't need a destination at all. This
+ // avoids needing a mountain of temporary `()` variables.
+ let expr2 = expr.clone();
+ match expr.kind {
+ ExprKind::Scope { region_scope, lint_level, value } => {
+ let value = this.hir.mirror(value);
+ this.in_scope((region_scope, source_info), lint_level, |this| {
+ this.stmt_expr(block, value, statement_scope)
+ })
+ }
+ ExprKind::Assign { lhs, rhs } => {
+ let lhs = this.hir.mirror(lhs);
+ let rhs = this.hir.mirror(rhs);
+ let lhs_span = lhs.span;
+
+ // Note: we evaluate assignments right-to-left. This
+ // is better for borrowck interaction with overloaded
+ // operators like x[j] = x[i].
+
+ debug!("stmt_expr Assign block_context.push(SubExpr) : {:?}", expr2);
+ this.block_context.push(BlockFrame::SubExpr);
+
+ // Generate better code for things that don't need to be
+ // dropped.
+ if this.hir.needs_drop(lhs.ty) {
+ let rhs = unpack!(block = this.as_local_operand(block, rhs));
+ let lhs = unpack!(block = this.as_place(block, lhs));
+ unpack!(block = this.build_drop_and_replace(block, lhs_span, lhs, rhs));
+ } else {
+ let rhs = unpack!(block = this.as_local_rvalue(block, rhs));
+ let lhs = unpack!(block = this.as_place(block, lhs));
+ this.cfg.push_assign(block, source_info, &lhs, rhs);
+ }
+
+ this.block_context.pop();
+ block.unit()
+ }
+ ExprKind::AssignOp { op, lhs, rhs } => {
+ // FIXME(#28160) there is an interesting semantics
+ // question raised here -- should we "freeze" the
+ // value of the lhs here? I'm inclined to think not,
+ // since it seems closer to the semantics of the
+ // overloaded version, which takes `&mut self`. This
+ // only affects weird things like `x += {x += 1; x}`
+ // -- is that equal to `x + (x + 1)` or `2*(x+1)`?
+
+ let lhs = this.hir.mirror(lhs);
+ let lhs_ty = lhs.ty;
+
+ debug!("stmt_expr AssignOp block_context.push(SubExpr) : {:?}", expr2);
+ this.block_context.push(BlockFrame::SubExpr);
+
+ // As above, RTL.
+ let rhs = unpack!(block = this.as_local_operand(block, rhs));
+ let lhs = unpack!(block = this.as_place(block, lhs));
+
+ // we don't have to drop prior contents or anything
+ // because AssignOp is only legal for Copy types
+ // (overloaded ops should be desugared into a call).
+ let result = unpack!(
+ block = this.build_binary_op(
+ block,
+ op,
+ expr_span,
+ lhs_ty,
+ Operand::Copy(lhs.clone()),
+ rhs
+ )
+ );
+ this.cfg.push_assign(block, source_info, &lhs, result);
+
+ this.block_context.pop();
+ block.unit()
+ }
+ ExprKind::Continue { label } => {
+ this.break_scope(block, None, BreakableTarget::Continue(label), source_info)
+ }
+ ExprKind::Break { label, value } => {
+ this.break_scope(block, value, BreakableTarget::Break(label), source_info)
+ }
+ ExprKind::Return { value } => {
+ this.break_scope(block, value, BreakableTarget::Return, source_info)
+ }
+ ExprKind::InlineAsm { asm, outputs, inputs } => {
+ debug!("stmt_expr InlineAsm block_context.push(SubExpr) : {:?}", expr2);
+ this.block_context.push(BlockFrame::SubExpr);
+ let outputs = outputs
+ .into_iter()
+ .map(|output| unpack!(block = this.as_place(block, output)))
+ .collect::<Vec<_>>()
+ .into_boxed_slice();
+ let inputs = inputs
+ .into_iter()
+ .map(|input| {
+ (input.span(), unpack!(block = this.as_local_operand(block, input)))
+ })
+ .collect::<Vec<_>>()
+ .into_boxed_slice();
+ this.cfg.push(
+ block,
+ Statement {
+ source_info,
+ kind: StatementKind::InlineAsm(box InlineAsm {
+ asm: asm.clone(),
+ outputs,
+ inputs,
+ }),
+ },
+ );
+ this.block_context.pop();
+ block.unit()
+ }
+ _ => {
+ assert!(
+ statement_scope.is_some(),
+ "Should not be calling `stmt_expr` on a general expression \
+ without a statement scope",
+ );
+
+ // Issue #54382: When creating temp for the value of
+ // expression like:
+ //
+ // `{ side_effects(); { let l = stuff(); the_value } }`
+ //
+ // it is usually better to focus on `the_value` rather
+ // than the entirety of block(s) surrounding it.
+ let adjusted_span = (|| {
+ if let ExprKind::Block { body } = expr.kind {
+ if let Some(tail_expr) = &body.expr {
+ let mut expr = tail_expr;
+ while let rustc_hir::ExprKind::Block(subblock, _label) = &expr.kind {
+ if let Some(subtail_expr) = &subblock.expr {
+ expr = subtail_expr
+ } else {
+ break;
+ }
+ }
+ this.block_context
+ .push(BlockFrame::TailExpr { tail_result_is_ignored: true });
+ return Some(expr.span);
+ }
+ }
+ None
+ })();
+
+ let temp =
+ unpack!(block = this.as_temp(block, statement_scope, expr, Mutability::Not));
+
+ if let Some(span) = adjusted_span {
+ this.local_decls[temp].source_info.span = span;
+ this.block_context.pop();
+ }
+
+ block.unit()
+ }
+ }
+ }
+}
--- /dev/null
+//! In general, there are a number of things for which it's convenient
+//! to just call `builder.into` and have it emit its result into a
+//! given location. This is basically for expressions or things that can be
+//! wrapped up as expressions (e.g., blocks). To make this ergonomic, we use this
+//! latter `EvalInto` trait.
+
+use crate::build::{BlockAnd, Builder};
+use crate::hair::*;
+use rustc::mir::*;
+
+pub(in crate::build) trait EvalInto<'tcx> {
+ fn eval_into(
+ self,
+ builder: &mut Builder<'_, 'tcx>,
+ destination: &Place<'tcx>,
+ block: BasicBlock,
+ ) -> BlockAnd<()>;
+}
+
+impl<'a, 'tcx> Builder<'a, 'tcx> {
+ crate fn into<E>(
+ &mut self,
+ destination: &Place<'tcx>,
+ block: BasicBlock,
+ expr: E,
+ ) -> BlockAnd<()>
+ where
+ E: EvalInto<'tcx>,
+ {
+ expr.eval_into(self, destination, block)
+ }
+}
+
+impl<'tcx> EvalInto<'tcx> for ExprRef<'tcx> {
+ fn eval_into(
+ self,
+ builder: &mut Builder<'_, 'tcx>,
+ destination: &Place<'tcx>,
+ block: BasicBlock,
+ ) -> BlockAnd<()> {
+ let expr = builder.hir.mirror(self);
+ builder.into_expr(destination, block, expr)
+ }
+}
+
+impl<'tcx> EvalInto<'tcx> for Expr<'tcx> {
+ fn eval_into(
+ self,
+ builder: &mut Builder<'_, 'tcx>,
+ destination: &Place<'tcx>,
+ block: BasicBlock,
+ ) -> BlockAnd<()> {
+ builder.into_expr(destination, block, self)
+ }
+}
--- /dev/null
+//! Code related to match expressions. These are sufficiently complex to
+//! warrant their own module and submodules. :) This main module includes the
+//! high-level algorithm, the submodules contain the details.
+//!
+//! This also includes code for pattern bindings in `let` statements and
+//! function parameters.
+
+use crate::build::scope::DropKind;
+use crate::build::ForGuard::{self, OutsideGuard, RefWithinGuard};
+use crate::build::{BlockAnd, BlockAndExtension, Builder};
+use crate::build::{GuardFrame, GuardFrameLocal, LocalsForNode};
+use crate::hair::{self, *};
+use rustc::middle::region;
+use rustc::mir::*;
+use rustc::ty::layout::VariantIdx;
+use rustc::ty::{self, CanonicalUserTypeAnnotation, Ty};
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_hir::HirId;
+use rustc_index::bit_set::BitSet;
+use rustc_span::Span;
+use smallvec::{smallvec, SmallVec};
+use syntax::ast::Name;
+
+// helper functions, broken out by category:
+mod simplify;
+mod test;
+mod util;
+
+use itertools::Itertools;
+use std::convert::TryFrom;
+
+impl<'a, 'tcx> Builder<'a, 'tcx> {
+ /// Generates MIR for a `match` expression.
+ ///
+ /// The MIR that we generate for a match looks like this.
+ ///
+ /// ```text
+ /// [ 0. Pre-match ]
+ /// |
+ /// [ 1. Evaluate Scrutinee (expression being matched on) ]
+ /// [ (fake read of scrutinee) ]
+ /// |
+ /// [ 2. Decision tree -- check discriminants ] <--------+
+ /// | |
+ /// | (once a specific arm is chosen) |
+ /// | |
+ /// [pre_binding_block] [otherwise_block]
+ /// | |
+ /// [ 3. Create "guard bindings" for arm ] |
+ /// [ (create fake borrows) ] |
+ /// | |
+ /// [ 4. Execute guard code ] |
+ /// [ (read fake borrows) ] --(guard is false)-----------+
+ /// |
+ /// | (guard results in true)
+ /// |
+ /// [ 5. Create real bindings and execute arm ]
+ /// |
+ /// [ Exit match ]
+ /// ```
+ ///
+ /// All of the different arms have been stacked on top of each other to
+ /// simplify the diagram. For an arm with no guard the blocks marked 3 and
+ /// 4 and the fake borrows are omitted.
+ ///
+ /// We generate MIR in the following steps:
+ ///
+ /// 1. Evaluate the scrutinee and add the fake read of it ([Builder::lower_scrutinee]).
+ /// 2. Create the prebinding and otherwise blocks ([Builder::create_match_candidates]).
+ /// 3. Create the decision tree ([Builder::lower_match_tree]).
+ /// 4. Determine the fake borrows that are needed from the places that were
+ /// matched against and create the required temporaries for them
+ /// ([Builder::calculate_fake_borrows]).
+ /// 5. Create everything else: the guards and the arms ([Builder::lower_match_arms]).
+ ///
+ /// ## False edges
+ ///
+ /// We don't want to have the exact structure of the decision tree be
+ /// visible through borrow checking. False edges ensure that the CFG as
+ /// seen by borrow checking doesn't encode this. False edges are added:
+ ///
+ /// * From each prebinding block to the next prebinding block.
+ /// * From each otherwise block to the next prebinding block.
+ crate fn match_expr(
+ &mut self,
+ destination: &Place<'tcx>,
+ span: Span,
+ mut block: BasicBlock,
+ scrutinee: ExprRef<'tcx>,
+ arms: Vec<Arm<'tcx>>,
+ ) -> BlockAnd<()> {
+ let scrutinee_span = scrutinee.span();
+ let scrutinee_place =
+ unpack!(block = self.lower_scrutinee(block, scrutinee, scrutinee_span,));
+
+ let mut arm_candidates = self.create_match_candidates(&scrutinee_place, &arms);
+
+ let match_has_guard = arms.iter().any(|arm| arm.guard.is_some());
+ let candidates =
+ arm_candidates.iter_mut().flat_map(|(_, candidates)| candidates).collect::<Vec<_>>();
+
+ let fake_borrow_temps =
+ self.lower_match_tree(block, scrutinee_span, match_has_guard, candidates);
+
+ self.lower_match_arms(
+ &destination,
+ scrutinee_place,
+ scrutinee_span,
+ arm_candidates,
+ self.source_info(span),
+ fake_borrow_temps,
+ )
+ }
+
+ /// Evaluate the scrutinee and add the fake read of it.
+ fn lower_scrutinee(
+ &mut self,
+ mut block: BasicBlock,
+ scrutinee: ExprRef<'tcx>,
+ scrutinee_span: Span,
+ ) -> BlockAnd<Place<'tcx>> {
+ let scrutinee_place = unpack!(block = self.as_place(block, scrutinee));
+ // Matching on a `scrutinee_place` with an uninhabited type doesn't
+ // generate any memory reads by itself, and so if the place "expression"
+ // contains unsafe operations like raw pointer dereferences or union
+ // field projections, we wouldn't know to require an `unsafe` block
+ // around a `match` equivalent to `std::intrinsics::unreachable()`.
+ // See issue #47412 for this hole being discovered in the wild.
+ //
+ // HACK(eddyb) Work around the above issue by adding a dummy inspection
+ // of `scrutinee_place`, specifically by applying `ReadForMatch`.
+ //
+ // NOTE: ReadForMatch also checks that the scrutinee is initialized.
+ // This is currently needed to not allow matching on an uninitialized,
+ // uninhabited value. If we get never patterns, those will check that
+ // the place is initialized, and so this read would only be used to
+ // check safety.
+ let cause_matched_place = FakeReadCause::ForMatchedPlace;
+ let source_info = self.source_info(scrutinee_span);
+ self.cfg.push_fake_read(block, source_info, cause_matched_place, scrutinee_place.clone());
+
+ block.and(scrutinee_place)
+ }
+
+ /// Create the initial `Candidate`s for a `match` expression.
+ fn create_match_candidates<'pat>(
+ &mut self,
+ scrutinee: &Place<'tcx>,
+ arms: &'pat [Arm<'tcx>],
+ ) -> Vec<(&'pat Arm<'tcx>, Vec<Candidate<'pat, 'tcx>>)> {
+ let candidate_count = arms.iter().map(|c| c.top_pats_hack().len()).sum::<usize>();
+ let pre_binding_blocks: Vec<_> =
+ (0..candidate_count).map(|_| self.cfg.start_new_block()).collect();
+
+ let mut candidate_pre_binding_blocks = pre_binding_blocks.iter();
+ let mut next_candidate_pre_binding_blocks = pre_binding_blocks.iter().skip(1);
+
+ // Assemble a list of candidates: there is one candidate per pattern,
+ // which means there may be more than one candidate *per arm*.
+ arms.iter()
+ .map(|arm| {
+ let arm_has_guard = arm.guard.is_some();
+ let arm_candidates: Vec<_> = arm
+ .top_pats_hack()
+ .iter()
+ .zip(candidate_pre_binding_blocks.by_ref())
+ .map(|(pattern, pre_binding_block)| Candidate {
+ span: pattern.span,
+ match_pairs: smallvec![MatchPair::new(scrutinee.clone(), pattern)],
+ bindings: vec![],
+ ascriptions: vec![],
+ otherwise_block: if arm_has_guard {
+ Some(self.cfg.start_new_block())
+ } else {
+ None
+ },
+ pre_binding_block: *pre_binding_block,
+ next_candidate_pre_binding_block: next_candidate_pre_binding_blocks
+ .next()
+ .copied(),
+ })
+ .collect();
+ (arm, arm_candidates)
+ })
+ .collect()
+ }
+
+ /// Create the decision tree for the match expression, starting from `block`.
+ ///
+ /// Modifies `candidates` to store the bindings and type ascriptions for
+ /// that candidate.
+ ///
+ /// Returns the places that need fake borrows because we bind or test them.
+ fn lower_match_tree<'pat>(
+ &mut self,
+ block: BasicBlock,
+ scrutinee_span: Span,
+ match_has_guard: bool,
+ mut candidates: Vec<&mut Candidate<'pat, 'tcx>>,
+ ) -> Vec<(Place<'tcx>, Local)> {
+ // The set of places that we are creating fake borrows of. If there are
+ // no match guards then we don't need any fake borrows, so don't track
+ // them.
+ let mut fake_borrows = if match_has_guard { Some(FxHashSet::default()) } else { None };
+
+ // This will generate code to test scrutinee_place and
+ // branch to the appropriate arm block
+ self.match_candidates(
+ scrutinee_span,
+ &mut Some(block),
+ None,
+ &mut candidates,
+ &mut fake_borrows,
+ );
+
+ if let Some(ref borrows) = fake_borrows {
+ self.calculate_fake_borrows(borrows, scrutinee_span)
+ } else {
+ Vec::new()
+ }
+ }
+
+ /// Lower the bindings, guards and arm bodies of a `match` expression.
+ ///
+ /// The decision tree should have already been created
+ /// (by [Builder::lower_match_tree]).
+ ///
+ /// `outer_source_info` is the SourceInfo for the whole match.
+ fn lower_match_arms(
+ &mut self,
+ destination: &Place<'tcx>,
+ scrutinee_place: Place<'tcx>,
+ scrutinee_span: Span,
+ arm_candidates: Vec<(&'_ Arm<'tcx>, Vec<Candidate<'_, 'tcx>>)>,
+ outer_source_info: SourceInfo,
+ fake_borrow_temps: Vec<(Place<'tcx>, Local)>,
+ ) -> BlockAnd<()> {
+ let match_scope = self.scopes.topmost();
+
+ let arm_end_blocks: Vec<_> = arm_candidates
+ .into_iter()
+ .map(|(arm, candidates)| {
+ debug!("lowering arm {:?}\ncanidates = {:?}", arm, candidates);
+
+ let arm_source_info = self.source_info(arm.span);
+ let arm_scope = (arm.scope, arm_source_info);
+ self.in_scope(arm_scope, arm.lint_level, |this| {
+ let body = this.hir.mirror(arm.body.clone());
+ let scope = this.declare_bindings(
+ None,
+ arm.span,
+ &arm.top_pats_hack()[0],
+ ArmHasGuard(arm.guard.is_some()),
+ Some((Some(&scrutinee_place), scrutinee_span)),
+ );
+
+ let arm_block = this.bind_pattern(
+ outer_source_info,
+ candidates,
+ arm.guard.as_ref().map(|g| (g, match_scope)),
+ &fake_borrow_temps,
+ scrutinee_span,
+ arm.scope,
+ );
+
+ if let Some(source_scope) = scope {
+ this.source_scope = source_scope;
+ }
+
+ this.into(destination, arm_block, body)
+ })
+ })
+ .collect();
+
+ // all the arm blocks will rejoin here
+ let end_block = self.cfg.start_new_block();
+
+ for arm_block in arm_end_blocks {
+ self.cfg.goto(unpack!(arm_block), outer_source_info, end_block);
+ }
+
+ self.source_scope = outer_source_info.scope;
+
+ end_block.unit()
+ }
+
+ /// Binds the variables and ascribes types for a given `match` arm.
+ ///
+ /// Also check if the guard matches, if it's provided.
+ fn bind_pattern(
+ &mut self,
+ outer_source_info: SourceInfo,
+ mut candidates: Vec<Candidate<'_, 'tcx>>,
+ guard: Option<(&Guard<'tcx>, region::Scope)>,
+ fake_borrow_temps: &Vec<(Place<'tcx>, Local)>,
+ scrutinee_span: Span,
+ arm_scope: region::Scope,
+ ) -> BasicBlock {
+ if candidates.len() == 1 {
+ // Avoid generating another `BasicBlock` when we only have one
+ // candidate.
+ self.bind_and_guard_matched_candidate(
+ candidates.pop().unwrap(),
+ guard,
+ fake_borrow_temps,
+ scrutinee_span,
+ )
+ } else {
+ let arm_block = self.cfg.start_new_block();
+ for candidate in candidates {
+ // Avoid scheduling drops multiple times.
+ self.clear_top_scope(arm_scope);
+ let binding_end = self.bind_and_guard_matched_candidate(
+ candidate,
+ guard,
+ fake_borrow_temps,
+ scrutinee_span,
+ );
+ self.cfg.goto(binding_end, outer_source_info, arm_block);
+ }
+ arm_block
+ }
+ }
+
+ pub(super) fn expr_into_pattern(
+ &mut self,
+ mut block: BasicBlock,
+ irrefutable_pat: Pat<'tcx>,
+ initializer: ExprRef<'tcx>,
+ ) -> BlockAnd<()> {
+ match *irrefutable_pat.kind {
+ // Optimize the case of `let x = ...` to write directly into `x`
+ PatKind::Binding { mode: BindingMode::ByValue, var, subpattern: None, .. } => {
+ let place =
+ self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard);
+ unpack!(block = self.into(&place, block, initializer));
+
+ // Inject a fake read, see comments on `FakeReadCause::ForLet`.
+ let source_info = self.source_info(irrefutable_pat.span);
+ self.cfg.push_fake_read(block, source_info, FakeReadCause::ForLet, place);
+
+ self.schedule_drop_for_binding(var, irrefutable_pat.span, OutsideGuard);
+ block.unit()
+ }
+
+ // Optimize the case of `let x: T = ...` to write directly
+ // into `x` and then require that `T == typeof(x)`.
+ //
+ // Weirdly, this is needed to prevent the
+ // `intrinsic-move-val.rs` test case from crashing. That
+ // test works with uninitialized values in a rather
+ // dubious way, so it may be that the test is kind of
+ // broken.
+ PatKind::AscribeUserType {
+ subpattern:
+ Pat {
+ kind:
+ box PatKind::Binding {
+ mode: BindingMode::ByValue,
+ var,
+ subpattern: None,
+ ..
+ },
+ ..
+ },
+ ascription:
+ hair::pattern::Ascription { user_ty: pat_ascription_ty, variance: _, user_ty_span },
+ } => {
+ let place =
+ self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard);
+ unpack!(block = self.into(&place, block, initializer));
+
+ // Inject a fake read, see comments on `FakeReadCause::ForLet`.
+ let pattern_source_info = self.source_info(irrefutable_pat.span);
+ let cause_let = FakeReadCause::ForLet;
+ self.cfg.push_fake_read(block, pattern_source_info, cause_let, place.clone());
+
+ let ty_source_info = self.source_info(user_ty_span);
+ let user_ty = pat_ascription_ty.user_ty(
+ &mut self.canonical_user_type_annotations,
+ place.ty(&self.local_decls, self.hir.tcx()).ty,
+ ty_source_info.span,
+ );
+ self.cfg.push(
+ block,
+ Statement {
+ source_info: ty_source_info,
+ kind: StatementKind::AscribeUserType(
+ box (place, user_ty),
+ // We always use invariant as the variance here. This is because the
+ // variance field from the ascription refers to the variance to use
+ // when applying the type to the value being matched, but this
+ // ascription applies rather to the type of the binding. e.g., in this
+ // example:
+ //
+ // ```
+ // let x: T = <expr>
+ // ```
+ //
+ // We are creating an ascription that defines the type of `x` to be
+ // exactly `T` (i.e., with invariance). The variance field, in
+ // contrast, is intended to be used to relate `T` to the type of
+ // `<expr>`.
+ ty::Variance::Invariant,
+ ),
+ },
+ );
+
+ self.schedule_drop_for_binding(var, irrefutable_pat.span, OutsideGuard);
+ block.unit()
+ }
+
+ _ => {
+ let place = unpack!(block = self.as_place(block, initializer));
+ self.place_into_pattern(block, irrefutable_pat, &place, true)
+ }
+ }
+ }
+
+ crate fn place_into_pattern(
+ &mut self,
+ block: BasicBlock,
+ irrefutable_pat: Pat<'tcx>,
+ initializer: &Place<'tcx>,
+ set_match_place: bool,
+ ) -> BlockAnd<()> {
+ // create a dummy candidate
+ let mut candidate = Candidate {
+ span: irrefutable_pat.span,
+ match_pairs: smallvec![MatchPair::new(initializer.clone(), &irrefutable_pat)],
+ bindings: vec![],
+ ascriptions: vec![],
+
+ // since we don't call `match_candidates`, next fields are unused
+ otherwise_block: None,
+ pre_binding_block: block,
+ next_candidate_pre_binding_block: None,
+ };
+
+ // Simplify the candidate. Since the pattern is irrefutable, this should
+ // always convert all match-pairs into bindings.
+ self.simplify_candidate(&mut candidate);
+
+ if !candidate.match_pairs.is_empty() {
+ // ICE if no other errors have been emitted. This used to be a hard error that wouldn't
+ // be reached because `hair::pattern::check_match::check_match` wouldn't have let the
+ // compiler continue. In our tests this is only ever hit by
+ // `ui/consts/const-match-check.rs` with `--cfg eval1`, and that file already generates
+ // a different error before hand.
+ self.hir.tcx().sess.delay_span_bug(
+ candidate.match_pairs[0].pattern.span,
+ &format!(
+ "match pairs {:?} remaining after simplifying irrefutable pattern",
+ candidate.match_pairs,
+ ),
+ );
+ }
+
+ // for matches and function arguments, the place that is being matched
+ // can be set when creating the variables. But the place for
+ // let PATTERN = ... might not even exist until we do the assignment.
+ // so we set it here instead
+ if set_match_place {
+ for binding in &candidate.bindings {
+ let local = self.var_local_id(binding.var_id, OutsideGuard);
+
+ if let LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
+ opt_match_place: Some((ref mut match_place, _)),
+ ..
+ }))) = self.local_decls[local].local_info
+ {
+ *match_place = Some(initializer.clone());
+ } else {
+ bug!("Let binding to non-user variable.")
+ }
+ }
+ }
+
+ self.ascribe_types(block, &candidate.ascriptions);
+
+ // now apply the bindings, which will also declare the variables
+ self.bind_matched_candidate_for_arm_body(block, &candidate.bindings);
+
+ block.unit()
+ }
+
+ /// Declares the bindings of the given patterns and returns the visibility
+ /// scope for the bindings in these patterns, if such a scope had to be
+ /// created. NOTE: Declaring the bindings should always be done in their
+ /// drop scope.
+ crate fn declare_bindings(
+ &mut self,
+ mut visibility_scope: Option<SourceScope>,
+ scope_span: Span,
+ pattern: &Pat<'tcx>,
+ has_guard: ArmHasGuard,
+ opt_match_place: Option<(Option<&Place<'tcx>>, Span)>,
+ ) -> Option<SourceScope> {
+ debug!("declare_bindings: pattern={:?}", pattern);
+ self.visit_bindings(
+ &pattern,
+ UserTypeProjections::none(),
+ &mut |this, mutability, name, mode, var, span, ty, user_ty| {
+ if visibility_scope.is_none() {
+ visibility_scope =
+ Some(this.new_source_scope(scope_span, LintLevel::Inherited, None));
+ }
+ let source_info = SourceInfo { span, scope: this.source_scope };
+ let visibility_scope = visibility_scope.unwrap();
+ this.declare_binding(
+ source_info,
+ visibility_scope,
+ mutability,
+ name,
+ mode,
+ var,
+ ty,
+ user_ty,
+ has_guard,
+ opt_match_place.map(|(x, y)| (x.cloned(), y)),
+ pattern.span,
+ );
+ },
+ );
+ visibility_scope
+ }
+
+ crate fn storage_live_binding(
+ &mut self,
+ block: BasicBlock,
+ var: HirId,
+ span: Span,
+ for_guard: ForGuard,
+ ) -> Place<'tcx> {
+ let local_id = self.var_local_id(var, for_guard);
+ let source_info = self.source_info(span);
+ self.cfg.push(block, Statement { source_info, kind: StatementKind::StorageLive(local_id) });
+ let region_scope = self.hir.region_scope_tree.var_scope(var.local_id);
+ self.schedule_drop(span, region_scope, local_id, DropKind::Storage);
+ Place::from(local_id)
+ }
+
+ crate fn schedule_drop_for_binding(&mut self, var: HirId, span: Span, for_guard: ForGuard) {
+ let local_id = self.var_local_id(var, for_guard);
+ let region_scope = self.hir.region_scope_tree.var_scope(var.local_id);
+ self.schedule_drop(span, region_scope, local_id, DropKind::Value);
+ }
+
+ pub(super) fn visit_bindings(
+ &mut self,
+ pattern: &Pat<'tcx>,
+ pattern_user_ty: UserTypeProjections,
+ f: &mut impl FnMut(
+ &mut Self,
+ Mutability,
+ Name,
+ BindingMode,
+ HirId,
+ Span,
+ Ty<'tcx>,
+ UserTypeProjections,
+ ),
+ ) {
+ debug!("visit_bindings: pattern={:?} pattern_user_ty={:?}", pattern, pattern_user_ty);
+ match *pattern.kind {
+ PatKind::Binding { mutability, name, mode, var, ty, ref subpattern, .. } => {
+ f(self, mutability, name, mode, var, pattern.span, ty, pattern_user_ty.clone());
+ if let Some(subpattern) = subpattern.as_ref() {
+ self.visit_bindings(subpattern, pattern_user_ty, f);
+ }
+ }
+
+ PatKind::Array { ref prefix, ref slice, ref suffix }
+ | PatKind::Slice { ref prefix, ref slice, ref suffix } => {
+ let from = u32::try_from(prefix.len()).unwrap();
+ let to = u32::try_from(suffix.len()).unwrap();
+ for subpattern in prefix {
+ self.visit_bindings(subpattern, pattern_user_ty.clone().index(), f);
+ }
+ for subpattern in slice {
+ self.visit_bindings(subpattern, pattern_user_ty.clone().subslice(from, to), f);
+ }
+ for subpattern in suffix {
+ self.visit_bindings(subpattern, pattern_user_ty.clone().index(), f);
+ }
+ }
+
+ PatKind::Constant { .. } | PatKind::Range { .. } | PatKind::Wild => {}
+
+ PatKind::Deref { ref subpattern } => {
+ self.visit_bindings(subpattern, pattern_user_ty.deref(), f);
+ }
+
+ PatKind::AscribeUserType {
+ ref subpattern,
+ ascription: hair::pattern::Ascription { ref user_ty, user_ty_span, variance: _ },
+ } => {
+ // This corresponds to something like
+ //
+ // ```
+ // let A::<'a>(_): A<'static> = ...;
+ // ```
+ //
+ // Note that the variance doesn't apply here, as we are tracking the effect
+ // of `user_ty` on any bindings contained with subpattern.
+ let annotation = CanonicalUserTypeAnnotation {
+ span: user_ty_span,
+ user_ty: user_ty.user_ty,
+ inferred_ty: subpattern.ty,
+ };
+ let projection = UserTypeProjection {
+ base: self.canonical_user_type_annotations.push(annotation),
+ projs: Vec::new(),
+ };
+ let subpattern_user_ty = pattern_user_ty.push_projection(&projection, user_ty_span);
+ self.visit_bindings(subpattern, subpattern_user_ty, f)
+ }
+
+ PatKind::Leaf { ref subpatterns } => {
+ for subpattern in subpatterns {
+ let subpattern_user_ty = pattern_user_ty.clone().leaf(subpattern.field);
+ debug!("visit_bindings: subpattern_user_ty={:?}", subpattern_user_ty);
+ self.visit_bindings(&subpattern.pattern, subpattern_user_ty, f);
+ }
+ }
+
+ PatKind::Variant { adt_def, substs: _, variant_index, ref subpatterns } => {
+ for subpattern in subpatterns {
+ let subpattern_user_ty =
+ pattern_user_ty.clone().variant(adt_def, variant_index, subpattern.field);
+ self.visit_bindings(&subpattern.pattern, subpattern_user_ty, f);
+ }
+ }
+ PatKind::Or { ref pats } => {
+ for pat in pats {
+ self.visit_bindings(&pat, pattern_user_ty.clone(), f);
+ }
+ }
+ }
+ }
+}
+
+#[derive(Debug)]
+crate struct Candidate<'pat, 'tcx> {
+ // span of the original pattern that gave rise to this candidate
+ span: Span,
+
+ // all of these must be satisfied...
+ match_pairs: SmallVec<[MatchPair<'pat, 'tcx>; 1]>,
+
+ // ...these bindings established...
+ bindings: Vec<Binding<'tcx>>,
+
+ // ...and these types asserted...
+ ascriptions: Vec<Ascription<'tcx>>,
+
+ // ...and the guard must be evaluated, if false branch to Block...
+ otherwise_block: Option<BasicBlock>,
+
+ // ...and the blocks for add false edges between candidates
+ pre_binding_block: BasicBlock,
+ next_candidate_pre_binding_block: Option<BasicBlock>,
+}
+
+#[derive(Clone, Debug)]
+struct Binding<'tcx> {
+ span: Span,
+ source: Place<'tcx>,
+ name: Name,
+ var_id: HirId,
+ var_ty: Ty<'tcx>,
+ mutability: Mutability,
+ binding_mode: BindingMode,
+}
+
+/// Indicates that the type of `source` must be a subtype of the
+/// user-given type `user_ty`; this is basically a no-op but can
+/// influence region inference.
+#[derive(Clone, Debug)]
+struct Ascription<'tcx> {
+ span: Span,
+ source: Place<'tcx>,
+ user_ty: PatTyProj<'tcx>,
+ variance: ty::Variance,
+}
+
+#[derive(Clone, Debug)]
+crate struct MatchPair<'pat, 'tcx> {
+ // this place...
+ place: Place<'tcx>,
+
+ // ... must match this pattern.
+ pattern: &'pat Pat<'tcx>,
+}
+
+#[derive(Clone, Debug, PartialEq)]
+enum TestKind<'tcx> {
+ /// Test the branches of enum.
+ Switch {
+ /// The enum being tested
+ adt_def: &'tcx ty::AdtDef,
+ /// The set of variants that we should create a branch for. We also
+ /// create an additional "otherwise" case.
+ variants: BitSet<VariantIdx>,
+ },
+
+ /// Test what value an `integer`, `bool` or `char` has.
+ SwitchInt {
+ /// The type of the value that we're testing.
+ switch_ty: Ty<'tcx>,
+ /// The (ordered) set of values that we test for.
+ ///
+ /// For integers and `char`s we create a branch to each of the values in
+ /// `options`, as well as an "otherwise" branch for all other values, even
+ /// in the (rare) case that options is exhaustive.
+ ///
+ /// For `bool` we always generate two edges, one for `true` and one for
+ /// `false`.
+ options: Vec<u128>,
+ /// Reverse map used to ensure that the values in `options` are unique.
+ indices: FxHashMap<&'tcx ty::Const<'tcx>, usize>,
+ },
+
+ /// Test for equality with value, possibly after an unsizing coercion to
+ /// `ty`,
+ Eq {
+ value: &'tcx ty::Const<'tcx>,
+ // Integer types are handled by `SwitchInt`, and constants with ADT
+ // types are converted back into patterns, so this can only be `&str`,
+ // `&[T]`, `f32` or `f64`.
+ ty: Ty<'tcx>,
+ },
+
+ /// Test whether the value falls within an inclusive or exclusive range
+ Range(PatRange<'tcx>),
+
+ /// Test length of the slice is equal to len
+ Len { len: u64, op: BinOp },
+}
+
+#[derive(Debug)]
+crate struct Test<'tcx> {
+ span: Span,
+ kind: TestKind<'tcx>,
+}
+
+/// ArmHasGuard is isomorphic to a boolean flag. It indicates whether
+/// a match arm has a guard expression attached to it.
+#[derive(Copy, Clone, Debug)]
+crate struct ArmHasGuard(crate bool);
+
+///////////////////////////////////////////////////////////////////////////
+// Main matching algorithm
+
+impl<'a, 'tcx> Builder<'a, 'tcx> {
+ /// The main match algorithm. It begins with a set of candidates
+ /// `candidates` and has the job of generating code to determine
+ /// which of these candidates, if any, is the correct one. The
+ /// candidates are sorted such that the first item in the list
+ /// has the highest priority. When a candidate is found to match
+ /// the value, we will generate a branch to the appropriate
+ /// prebinding block.
+ ///
+ /// If we find that *NONE* of the candidates apply, we branch to the
+ /// `otherwise_block`. In principle, this means that the input list was not
+ /// exhaustive, though at present we sometimes are not smart enough to
+ /// recognize all exhaustive inputs.
+ ///
+ /// It might be surprising that the input can be inexhaustive.
+ /// Indeed, initially, it is not, because all matches are
+ /// exhaustive in Rust. But during processing we sometimes divide
+ /// up the list of candidates and recurse with a non-exhaustive
+ /// list. This is important to keep the size of the generated code
+ /// under control. See `test_candidates` for more details.
+ ///
+ /// If `fake_borrows` is Some, then places which need fake borrows
+ /// will be added to it.
+ fn match_candidates<'pat>(
+ &mut self,
+ span: Span,
+ start_block: &mut Option<BasicBlock>,
+ otherwise_block: Option<BasicBlock>,
+ candidates: &mut [&mut Candidate<'pat, 'tcx>],
+ fake_borrows: &mut Option<FxHashSet<Place<'tcx>>>,
+ ) {
+ debug!(
+ "matched_candidate(span={:?}, candidates={:?}, start_block={:?}, otherwise_block={:?})",
+ span, candidates, start_block, otherwise_block,
+ );
+
+ // Start by simplifying candidates. Once this process is complete, all
+ // the match pairs which remain require some form of test, whether it
+ // be a switch or pattern comparison.
+ for candidate in &mut *candidates {
+ self.simplify_candidate(candidate);
+ }
+
+ // The candidates are sorted by priority. Check to see whether the
+ // higher priority candidates (and hence at the front of the slice)
+ // have satisfied all their match pairs.
+ let fully_matched = candidates.iter().take_while(|c| c.match_pairs.is_empty()).count();
+ debug!("match_candidates: {:?} candidates fully matched", fully_matched);
+ let (matched_candidates, unmatched_candidates) = candidates.split_at_mut(fully_matched);
+
+ let block: BasicBlock = if !matched_candidates.is_empty() {
+ let otherwise_block =
+ self.select_matched_candidates(matched_candidates, start_block, fake_borrows);
+
+ if let Some(last_otherwise_block) = otherwise_block {
+ last_otherwise_block
+ } else {
+ // Any remaining candidates are unreachable.
+ if unmatched_candidates.is_empty() {
+ return;
+ }
+ self.cfg.start_new_block()
+ }
+ } else {
+ *start_block.get_or_insert_with(|| self.cfg.start_new_block())
+ };
+
+ // If there are no candidates that still need testing, we're
+ // done. Since all matches are exhaustive, execution should
+ // never reach this point.
+ if unmatched_candidates.is_empty() {
+ let source_info = self.source_info(span);
+ match otherwise_block {
+ Some(otherwise) => self.cfg.goto(block, source_info, otherwise),
+ None => self.cfg.terminate(block, source_info, TerminatorKind::Unreachable),
+ }
+ return;
+ }
+
+ // Test for the remaining candidates.
+ self.test_candidates(span, unmatched_candidates, block, otherwise_block, fake_borrows);
+ }
+
+ /// Link up matched candidates. For example, if we have something like
+ /// this:
+ ///
+ /// ...
+ /// Some(x) if cond => ...
+ /// Some(x) => ...
+ /// Some(x) if cond => ...
+ /// ...
+ ///
+ /// We generate real edges from:
+ /// * `start_block` to the `prebinding_block` of the first pattern,
+ /// * the otherwise block of the first pattern to the second pattern,
+ /// * the otherwise block of the third pattern to the a block with an
+ /// Unreachable terminator.
+ ///
+ /// As well as that we add fake edges from the otherwise blocks to the
+ /// prebinding block of the next candidate in the original set of
+ /// candidates.
+ fn select_matched_candidates(
+ &mut self,
+ matched_candidates: &mut [&mut Candidate<'_, 'tcx>],
+ start_block: &mut Option<BasicBlock>,
+ fake_borrows: &mut Option<FxHashSet<Place<'tcx>>>,
+ ) -> Option<BasicBlock> {
+ debug_assert!(
+ !matched_candidates.is_empty(),
+ "select_matched_candidates called with no candidates",
+ );
+
+ // Insert a borrows of prefixes of places that are bound and are
+ // behind a dereference projection.
+ //
+ // These borrows are taken to avoid situations like the following:
+ //
+ // match x[10] {
+ // _ if { x = &[0]; false } => (),
+ // y => (), // Out of bounds array access!
+ // }
+ //
+ // match *x {
+ // // y is bound by reference in the guard and then by copy in the
+ // // arm, so y is 2 in the arm!
+ // y if { y == 1 && (x = &2) == () } => y,
+ // _ => 3,
+ // }
+ if let Some(fake_borrows) = fake_borrows {
+ for Binding { source, .. } in
+ matched_candidates.iter().flat_map(|candidate| &candidate.bindings)
+ {
+ if let Some(i) =
+ source.projection.iter().rposition(|elem| *elem == ProjectionElem::Deref)
+ {
+ let proj_base = &source.projection[..i];
+
+ fake_borrows.insert(Place {
+ local: source.local.clone(),
+ projection: self.hir.tcx().intern_place_elems(proj_base),
+ });
+ }
+ }
+ }
+
+ let fully_matched_with_guard = matched_candidates
+ .iter()
+ .position(|c| c.otherwise_block.is_none())
+ .unwrap_or(matched_candidates.len() - 1);
+
+ let (reachable_candidates, unreachable_candidates) =
+ matched_candidates.split_at_mut(fully_matched_with_guard + 1);
+
+ let first_candidate = &reachable_candidates[0];
+ let first_prebinding_block = first_candidate.pre_binding_block;
+
+ // `goto -> first_prebinding_block` from the `start_block` if there is one.
+ if let Some(start_block) = *start_block {
+ let source_info = self.source_info(first_candidate.span);
+ self.cfg.goto(start_block, source_info, first_prebinding_block);
+ } else {
+ *start_block = Some(first_prebinding_block);
+ }
+
+ for (first_candidate, second_candidate) in reachable_candidates.iter().tuple_windows() {
+ let source_info = self.source_info(first_candidate.span);
+ if let Some(otherwise_block) = first_candidate.otherwise_block {
+ self.false_edges(
+ otherwise_block,
+ second_candidate.pre_binding_block,
+ first_candidate.next_candidate_pre_binding_block,
+ source_info,
+ );
+ } else {
+ bug!("candidate other than the last has no guard");
+ }
+ }
+
+ debug!("match_candidates: add false edges for unreachable {:?}", unreachable_candidates);
+ for candidate in unreachable_candidates {
+ if let Some(otherwise) = candidate.otherwise_block {
+ let source_info = self.source_info(candidate.span);
+ let unreachable = self.cfg.start_new_block();
+ self.false_edges(
+ otherwise,
+ unreachable,
+ candidate.next_candidate_pre_binding_block,
+ source_info,
+ );
+ self.cfg.terminate(unreachable, source_info, TerminatorKind::Unreachable);
+ }
+ }
+
+ let last_candidate = reachable_candidates.last().unwrap();
+ if let Some(otherwise) = last_candidate.otherwise_block {
+ let source_info = self.source_info(last_candidate.span);
+ let block = self.cfg.start_new_block();
+ self.false_edges(
+ otherwise,
+ block,
+ last_candidate.next_candidate_pre_binding_block,
+ source_info,
+ );
+ Some(block)
+ } else {
+ None
+ }
+ }
+
+ /// This is the most subtle part of the matching algorithm. At
+ /// this point, the input candidates have been fully simplified,
+ /// and so we know that all remaining match-pairs require some
+ /// sort of test. To decide what test to do, we take the highest
+ /// priority candidate (last one in the list) and extract the
+ /// first match-pair from the list. From this we decide what kind
+ /// of test is needed using `test`, defined in the `test` module.
+ ///
+ /// *Note:* taking the first match pair is somewhat arbitrary, and
+ /// we might do better here by choosing more carefully what to
+ /// test.
+ ///
+ /// For example, consider the following possible match-pairs:
+ ///
+ /// 1. `x @ Some(P)` -- we will do a `Switch` to decide what variant `x` has
+ /// 2. `x @ 22` -- we will do a `SwitchInt`
+ /// 3. `x @ 3..5` -- we will do a range test
+ /// 4. etc.
+ ///
+ /// Once we know what sort of test we are going to perform, this
+ /// Tests may also help us with other candidates. So we walk over
+ /// the candidates (from high to low priority) and check. This
+ /// gives us, for each outcome of the test, a transformed list of
+ /// candidates. For example, if we are testing the current
+ /// variant of `x.0`, and we have a candidate `{x.0 @ Some(v), x.1
+ /// @ 22}`, then we would have a resulting candidate of `{(x.0 as
+ /// Some).0 @ v, x.1 @ 22}`. Note that the first match-pair is now
+ /// simpler (and, in fact, irrefutable).
+ ///
+ /// But there may also be candidates that the test just doesn't
+ /// apply to. The classical example involves wildcards:
+ ///
+ /// ```
+ /// # let (x, y, z) = (true, true, true);
+ /// match (x, y, z) {
+ /// (true, _, true) => true, // (0)
+ /// (_, true, _) => true, // (1)
+ /// (false, false, _) => false, // (2)
+ /// (true, _, false) => false, // (3)
+ /// }
+ /// ```
+ ///
+ /// In that case, after we test on `x`, there are 2 overlapping candidate
+ /// sets:
+ ///
+ /// - If the outcome is that `x` is true, candidates 0, 1, and 3
+ /// - If the outcome is that `x` is false, candidates 1 and 2
+ ///
+ /// Here, the traditional "decision tree" method would generate 2
+ /// separate code-paths for the 2 separate cases.
+ ///
+ /// In some cases, this duplication can create an exponential amount of
+ /// code. This is most easily seen by noticing that this method terminates
+ /// with precisely the reachable arms being reachable - but that problem
+ /// is trivially NP-complete:
+ ///
+ /// ```rust
+ /// match (var0, var1, var2, var3, ..) {
+ /// (true, _, _, false, true, ...) => false,
+ /// (_, true, true, false, _, ...) => false,
+ /// (false, _, false, false, _, ...) => false,
+ /// ...
+ /// _ => true
+ /// }
+ /// ```
+ ///
+ /// Here the last arm is reachable only if there is an assignment to
+ /// the variables that does not match any of the literals. Therefore,
+ /// compilation would take an exponential amount of time in some cases.
+ ///
+ /// That kind of exponential worst-case might not occur in practice, but
+ /// our simplistic treatment of constants and guards would make it occur
+ /// in very common situations - for example #29740:
+ ///
+ /// ```rust
+ /// match x {
+ /// "foo" if foo_guard => ...,
+ /// "bar" if bar_guard => ...,
+ /// "baz" if baz_guard => ...,
+ /// ...
+ /// }
+ /// ```
+ ///
+ /// Here we first test the match-pair `x @ "foo"`, which is an `Eq` test.
+ ///
+ /// It might seem that we would end up with 2 disjoint candidate
+ /// sets, consisting of the first candidate or the other 3, but our
+ /// algorithm doesn't reason about "foo" being distinct from the other
+ /// constants; it considers the latter arms to potentially match after
+ /// both outcomes, which obviously leads to an exponential amount
+ /// of tests.
+ ///
+ /// To avoid these kinds of problems, our algorithm tries to ensure
+ /// the amount of generated tests is linear. When we do a k-way test,
+ /// we return an additional "unmatched" set alongside the obvious `k`
+ /// sets. When we encounter a candidate that would be present in more
+ /// than one of the sets, we put it and all candidates below it into the
+ /// "unmatched" set. This ensures these `k+1` sets are disjoint.
+ ///
+ /// After we perform our test, we branch into the appropriate candidate
+ /// set and recurse with `match_candidates`. These sub-matches are
+ /// obviously inexhaustive - as we discarded our otherwise set - so
+ /// we set their continuation to do `match_candidates` on the
+ /// "unmatched" set (which is again inexhaustive).
+ ///
+ /// If you apply this to the above test, you basically wind up
+ /// with an if-else-if chain, testing each candidate in turn,
+ /// which is precisely what we want.
+ ///
+ /// In addition to avoiding exponential-time blowups, this algorithm
+ /// also has nice property that each guard and arm is only generated
+ /// once.
+ fn test_candidates<'pat, 'b, 'c>(
+ &mut self,
+ span: Span,
+ mut candidates: &'b mut [&'c mut Candidate<'pat, 'tcx>],
+ block: BasicBlock,
+ mut otherwise_block: Option<BasicBlock>,
+ fake_borrows: &mut Option<FxHashSet<Place<'tcx>>>,
+ ) {
+ // extract the match-pair from the highest priority candidate
+ let match_pair = &candidates.first().unwrap().match_pairs[0];
+ let mut test = self.test(match_pair);
+ let match_place = match_pair.place.clone();
+
+ // most of the time, the test to perform is simply a function
+ // of the main candidate; but for a test like SwitchInt, we
+ // may want to add cases based on the candidates that are
+ // available
+ match test.kind {
+ TestKind::SwitchInt { switch_ty, ref mut options, ref mut indices } => {
+ for candidate in candidates.iter() {
+ if !self.add_cases_to_switch(
+ &match_place,
+ candidate,
+ switch_ty,
+ options,
+ indices,
+ ) {
+ break;
+ }
+ }
+ }
+ TestKind::Switch { adt_def: _, ref mut variants } => {
+ for candidate in candidates.iter() {
+ if !self.add_variants_to_switch(&match_place, candidate, variants) {
+ break;
+ }
+ }
+ }
+ _ => {}
+ }
+
+ // Insert a Shallow borrow of any places that is switched on.
+ fake_borrows.as_mut().map(|fb| fb.insert(match_place.clone()));
+
+ // perform the test, branching to one of N blocks. For each of
+ // those N possible outcomes, create a (initially empty)
+ // vector of candidates. Those are the candidates that still
+ // apply if the test has that particular outcome.
+ debug!("match_candidates: test={:?} match_pair={:?}", test, match_pair);
+ let mut target_candidates: Vec<Vec<&mut Candidate<'pat, 'tcx>>> = vec![];
+ target_candidates.resize_with(test.targets(), Default::default);
+
+ let total_candidate_count = candidates.len();
+
+ // Sort the candidates into the appropriate vector in
+ // `target_candidates`. Note that at some point we may
+ // encounter a candidate where the test is not relevant; at
+ // that point, we stop sorting.
+ while let Some(candidate) = candidates.first_mut() {
+ if let Some(idx) = self.sort_candidate(&match_place, &test, candidate) {
+ let (candidate, rest) = candidates.split_first_mut().unwrap();
+ target_candidates[idx].push(candidate);
+ candidates = rest;
+ } else {
+ break;
+ }
+ }
+ // at least the first candidate ought to be tested
+ assert!(total_candidate_count > candidates.len());
+ debug!("tested_candidates: {}", total_candidate_count - candidates.len());
+ debug!("untested_candidates: {}", candidates.len());
+
+ // HACK(matthewjasper) This is a closure so that we can let the test
+ // create its blocks before the rest of the match. This currently
+ // improves the speed of llvm when optimizing long string literal
+ // matches
+ let make_target_blocks = move |this: &mut Self| -> Vec<BasicBlock> {
+ // For each outcome of test, process the candidates that still
+ // apply. Collect a list of blocks where control flow will
+ // branch if one of the `target_candidate` sets is not
+ // exhaustive.
+ if !candidates.is_empty() {
+ let remainder_start = &mut None;
+ this.match_candidates(
+ span,
+ remainder_start,
+ otherwise_block,
+ candidates,
+ fake_borrows,
+ );
+ otherwise_block = Some(remainder_start.unwrap());
+ };
+
+ target_candidates
+ .into_iter()
+ .map(|mut candidates| {
+ if candidates.len() != 0 {
+ let candidate_start = &mut None;
+ this.match_candidates(
+ span,
+ candidate_start,
+ otherwise_block,
+ &mut *candidates,
+ fake_borrows,
+ );
+ candidate_start.unwrap()
+ } else {
+ *otherwise_block.get_or_insert_with(|| {
+ let unreachable = this.cfg.start_new_block();
+ let source_info = this.source_info(span);
+ this.cfg.terminate(
+ unreachable,
+ source_info,
+ TerminatorKind::Unreachable,
+ );
+ unreachable
+ })
+ }
+ })
+ .collect()
+ };
+
+ self.perform_test(block, &match_place, &test, make_target_blocks);
+ }
+
+ /// Determine the fake borrows that are needed from a set of places that
+ /// have to be stable across match guards.
+ ///
+ /// Returns a list of places that need a fake borrow and the temporary
+ /// that's used to store the fake borrow.
+ ///
+ /// Match exhaustiveness checking is not able to handle the case where the
+ /// place being matched on is mutated in the guards. We add "fake borrows"
+ /// to the guards that prevent any mutation of the place being matched.
+ /// There are a some subtleties:
+ ///
+ /// 1. Borrowing `*x` doesn't prevent assigning to `x`. If `x` is a shared
+ /// reference, the borrow isn't even tracked. As such we have to add fake
+ /// borrows of any prefixes of a place
+ /// 2. We don't want `match x { _ => (), }` to conflict with mutable
+ /// borrows of `x`, so we only add fake borrows for places which are
+ /// bound or tested by the match.
+ /// 3. We don't want the fake borrows to conflict with `ref mut` bindings,
+ /// so we use a special BorrowKind for them.
+ /// 4. The fake borrows may be of places in inactive variants, so it would
+ /// be UB to generate code for them. They therefore have to be removed
+ /// by a MIR pass run after borrow checking.
+ fn calculate_fake_borrows<'b>(
+ &mut self,
+ fake_borrows: &'b FxHashSet<Place<'tcx>>,
+ temp_span: Span,
+ ) -> Vec<(Place<'tcx>, Local)> {
+ let tcx = self.hir.tcx();
+
+ debug!("add_fake_borrows fake_borrows = {:?}", fake_borrows);
+
+ let mut all_fake_borrows = Vec::with_capacity(fake_borrows.len());
+
+ // Insert a Shallow borrow of the prefixes of any fake borrows.
+ for place in fake_borrows {
+ let mut cursor = place.projection.as_ref();
+ while let [proj_base @ .., elem] = cursor {
+ cursor = proj_base;
+
+ if let ProjectionElem::Deref = elem {
+ // Insert a shallow borrow after a deref. For other
+ // projections the borrow of prefix_cursor will
+ // conflict with any mutation of base.
+ all_fake_borrows.push(PlaceRef { local: &place.local, projection: proj_base });
+ }
+ }
+
+ all_fake_borrows.push(place.as_ref());
+ }
+
+ // Deduplicate and ensure a deterministic order.
+ all_fake_borrows.sort();
+ all_fake_borrows.dedup();
+
+ debug!("add_fake_borrows all_fake_borrows = {:?}", all_fake_borrows);
+
+ all_fake_borrows
+ .into_iter()
+ .map(|matched_place_ref| {
+ let matched_place = Place {
+ local: matched_place_ref.local.clone(),
+ projection: tcx.intern_place_elems(matched_place_ref.projection),
+ };
+ let fake_borrow_deref_ty = matched_place.ty(&self.local_decls, tcx).ty;
+ let fake_borrow_ty = tcx.mk_imm_ref(tcx.lifetimes.re_erased, fake_borrow_deref_ty);
+ let fake_borrow_temp =
+ self.local_decls.push(LocalDecl::new_temp(fake_borrow_ty, temp_span));
+
+ (matched_place, fake_borrow_temp)
+ })
+ .collect()
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Pat binding - used for `let` and function parameters as well.
+
+impl<'a, 'tcx> Builder<'a, 'tcx> {
+ /// Initializes each of the bindings from the candidate by
+ /// moving/copying/ref'ing the source as appropriate. Tests the guard, if
+ /// any, and then branches to the arm. Returns the block for the case where
+ /// the guard fails.
+ ///
+ /// Note: we do not check earlier that if there is a guard,
+ /// there cannot be move bindings. We avoid a use-after-move by only
+ /// moving the binding once the guard has evaluated to true (see below).
+ fn bind_and_guard_matched_candidate<'pat>(
+ &mut self,
+ candidate: Candidate<'pat, 'tcx>,
+ guard: Option<(&Guard<'tcx>, region::Scope)>,
+ fake_borrows: &Vec<(Place<'tcx>, Local)>,
+ scrutinee_span: Span,
+ ) -> BasicBlock {
+ debug!("bind_and_guard_matched_candidate(candidate={:?})", candidate);
+
+ debug_assert!(candidate.match_pairs.is_empty());
+
+ let candidate_source_info = self.source_info(candidate.span);
+
+ let mut block = candidate.pre_binding_block;
+
+ // If we are adding our own statements, then we need a fresh block.
+ let create_fresh_block = candidate.next_candidate_pre_binding_block.is_some()
+ || !candidate.bindings.is_empty()
+ || !candidate.ascriptions.is_empty()
+ || guard.is_some();
+
+ if create_fresh_block {
+ let fresh_block = self.cfg.start_new_block();
+ self.false_edges(
+ block,
+ fresh_block,
+ candidate.next_candidate_pre_binding_block,
+ candidate_source_info,
+ );
+ block = fresh_block;
+ self.ascribe_types(block, &candidate.ascriptions);
+ } else {
+ return block;
+ }
+
+ // rust-lang/rust#27282: The `autoref` business deserves some
+ // explanation here.
+ //
+ // The intent of the `autoref` flag is that when it is true,
+ // then any pattern bindings of type T will map to a `&T`
+ // within the context of the guard expression, but will
+ // continue to map to a `T` in the context of the arm body. To
+ // avoid surfacing this distinction in the user source code
+ // (which would be a severe change to the language and require
+ // far more revision to the compiler), when `autoref` is true,
+ // then any occurrence of the identifier in the guard
+ // expression will automatically get a deref op applied to it.
+ //
+ // So an input like:
+ //
+ // ```
+ // let place = Foo::new();
+ // match place { foo if inspect(foo)
+ // => feed(foo), ... }
+ // ```
+ //
+ // will be treated as if it were really something like:
+ //
+ // ```
+ // let place = Foo::new();
+ // match place { Foo { .. } if { let tmp1 = &place; inspect(*tmp1) }
+ // => { let tmp2 = place; feed(tmp2) }, ... }
+ //
+ // And an input like:
+ //
+ // ```
+ // let place = Foo::new();
+ // match place { ref mut foo if inspect(foo)
+ // => feed(foo), ... }
+ // ```
+ //
+ // will be treated as if it were really something like:
+ //
+ // ```
+ // let place = Foo::new();
+ // match place { Foo { .. } if { let tmp1 = & &mut place; inspect(*tmp1) }
+ // => { let tmp2 = &mut place; feed(tmp2) }, ... }
+ // ```
+ //
+ // In short, any pattern binding will always look like *some*
+ // kind of `&T` within the guard at least in terms of how the
+ // MIR-borrowck views it, and this will ensure that guard
+ // expressions cannot mutate their the match inputs via such
+ // bindings. (It also ensures that guard expressions can at
+ // most *copy* values from such bindings; non-Copy things
+ // cannot be moved via pattern bindings in guard expressions.)
+ //
+ // ----
+ //
+ // Implementation notes (under assumption `autoref` is true).
+ //
+ // To encode the distinction above, we must inject the
+ // temporaries `tmp1` and `tmp2`.
+ //
+ // There are two cases of interest: binding by-value, and binding by-ref.
+ //
+ // 1. Binding by-value: Things are simple.
+ //
+ // * Establishing `tmp1` creates a reference into the
+ // matched place. This code is emitted by
+ // bind_matched_candidate_for_guard.
+ //
+ // * `tmp2` is only initialized "lazily", after we have
+ // checked the guard. Thus, the code that can trigger
+ // moves out of the candidate can only fire after the
+ // guard evaluated to true. This initialization code is
+ // emitted by bind_matched_candidate_for_arm.
+ //
+ // 2. Binding by-reference: Things are tricky.
+ //
+ // * Here, the guard expression wants a `&&` or `&&mut`
+ // into the original input. This means we need to borrow
+ // the reference that we create for the arm.
+ // * So we eagerly create the reference for the arm and then take a
+ // reference to that.
+ if let Some((guard, region_scope)) = guard {
+ let tcx = self.hir.tcx();
+
+ self.bind_matched_candidate_for_guard(block, &candidate.bindings);
+ let guard_frame = GuardFrame {
+ locals: candidate
+ .bindings
+ .iter()
+ .map(|b| GuardFrameLocal::new(b.var_id, b.binding_mode))
+ .collect(),
+ };
+ debug!("entering guard building context: {:?}", guard_frame);
+ self.guard_context.push(guard_frame);
+
+ let re_erased = tcx.lifetimes.re_erased;
+ let scrutinee_source_info = self.source_info(scrutinee_span);
+ for (place, temp) in fake_borrows {
+ let borrow = Rvalue::Ref(re_erased, BorrowKind::Shallow, place.clone());
+ self.cfg.push_assign(block, scrutinee_source_info, &Place::from(*temp), borrow);
+ }
+
+ // the block to branch to if the guard fails; if there is no
+ // guard, this block is simply unreachable
+ let guard = match guard {
+ Guard::If(e) => self.hir.mirror(e.clone()),
+ };
+ let source_info = self.source_info(guard.span);
+ let guard_end = self.source_info(tcx.sess.source_map().end_point(guard.span));
+ let (post_guard_block, otherwise_post_guard_block) =
+ self.test_bool(block, guard, source_info);
+ let guard_frame = self.guard_context.pop().unwrap();
+ debug!("Exiting guard building context with locals: {:?}", guard_frame);
+
+ for &(_, temp) in fake_borrows {
+ let cause = FakeReadCause::ForMatchGuard;
+ self.cfg.push_fake_read(post_guard_block, guard_end, cause, Place::from(temp));
+ }
+
+ self.exit_scope(
+ source_info.span,
+ region_scope,
+ otherwise_post_guard_block,
+ candidate.otherwise_block.unwrap(),
+ );
+
+ // We want to ensure that the matched candidates are bound
+ // after we have confirmed this candidate *and* any
+ // associated guard; Binding them on `block` is too soon,
+ // because that would be before we've checked the result
+ // from the guard.
+ //
+ // But binding them on the arm is *too late*, because
+ // then all of the candidates for a single arm would be
+ // bound in the same place, that would cause a case like:
+ //
+ // ```rust
+ // match (30, 2) {
+ // (mut x, 1) | (2, mut x) if { true } => { ... }
+ // ... // ^^^^^^^ (this is `arm_block`)
+ // }
+ // ```
+ //
+ // would yield a `arm_block` something like:
+ //
+ // ```
+ // StorageLive(_4); // _4 is `x`
+ // _4 = &mut (_1.0: i32); // this is handling `(mut x, 1)` case
+ // _4 = &mut (_1.1: i32); // this is handling `(2, mut x)` case
+ // ```
+ //
+ // and that is clearly not correct.
+ let by_value_bindings = candidate.bindings.iter().filter(|binding| {
+ if let BindingMode::ByValue = binding.binding_mode { true } else { false }
+ });
+ // Read all of the by reference bindings to ensure that the
+ // place they refer to can't be modified by the guard.
+ for binding in by_value_bindings.clone() {
+ let local_id = self.var_local_id(binding.var_id, RefWithinGuard);
+ let cause = FakeReadCause::ForGuardBinding;
+ self.cfg.push_fake_read(post_guard_block, guard_end, cause, Place::from(local_id));
+ }
+ self.bind_matched_candidate_for_arm_body(post_guard_block, by_value_bindings);
+
+ post_guard_block
+ } else {
+ assert!(candidate.otherwise_block.is_none());
+ // (Here, it is not too early to bind the matched
+ // candidate on `block`, because there is no guard result
+ // that we have to inspect before we bind them.)
+ self.bind_matched_candidate_for_arm_body(block, &candidate.bindings);
+ block
+ }
+ }
+
+ /// Append `AscribeUserType` statements onto the end of `block`
+ /// for each ascription
+ fn ascribe_types(&mut self, block: BasicBlock, ascriptions: &[Ascription<'tcx>]) {
+ for ascription in ascriptions {
+ let source_info = self.source_info(ascription.span);
+
+ debug!(
+ "adding user ascription at span {:?} of place {:?} and {:?}",
+ source_info.span, ascription.source, ascription.user_ty,
+ );
+
+ let user_ty = ascription.user_ty.clone().user_ty(
+ &mut self.canonical_user_type_annotations,
+ ascription.source.ty(&self.local_decls, self.hir.tcx()).ty,
+ source_info.span,
+ );
+ self.cfg.push(
+ block,
+ Statement {
+ source_info,
+ kind: StatementKind::AscribeUserType(
+ box (ascription.source.clone(), user_ty),
+ ascription.variance,
+ ),
+ },
+ );
+ }
+ }
+
+ fn bind_matched_candidate_for_guard(&mut self, block: BasicBlock, bindings: &[Binding<'tcx>]) {
+ debug!("bind_matched_candidate_for_guard(block={:?}, bindings={:?})", block, bindings);
+
+ // Assign each of the bindings. Since we are binding for a
+ // guard expression, this will never trigger moves out of the
+ // candidate.
+ let re_erased = self.hir.tcx().lifetimes.re_erased;
+ for binding in bindings {
+ let source_info = self.source_info(binding.span);
+
+ // For each pattern ident P of type T, `ref_for_guard` is
+ // a reference R: &T pointing to the location matched by
+ // the pattern, and every occurrence of P within a guard
+ // denotes *R.
+ let ref_for_guard =
+ self.storage_live_binding(block, binding.var_id, binding.span, RefWithinGuard);
+ match binding.binding_mode {
+ BindingMode::ByValue => {
+ let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, binding.source.clone());
+ self.cfg.push_assign(block, source_info, &ref_for_guard, rvalue);
+ }
+ BindingMode::ByRef(borrow_kind) => {
+ let value_for_arm = self.storage_live_binding(
+ block,
+ binding.var_id,
+ binding.span,
+ OutsideGuard,
+ );
+
+ let rvalue = Rvalue::Ref(re_erased, borrow_kind, binding.source.clone());
+ self.cfg.push_assign(block, source_info, &value_for_arm, rvalue);
+ let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, value_for_arm);
+ self.cfg.push_assign(block, source_info, &ref_for_guard, rvalue);
+ }
+ }
+ }
+ }
+
+ fn bind_matched_candidate_for_arm_body<'b>(
+ &mut self,
+ block: BasicBlock,
+ bindings: impl IntoIterator<Item = &'b Binding<'tcx>>,
+ ) where
+ 'tcx: 'b,
+ {
+ debug!("bind_matched_candidate_for_arm_body(block={:?})", block);
+
+ let re_erased = self.hir.tcx().lifetimes.re_erased;
+ // Assign each of the bindings. This may trigger moves out of the candidate.
+ for binding in bindings {
+ let source_info = self.source_info(binding.span);
+ let local =
+ self.storage_live_binding(block, binding.var_id, binding.span, OutsideGuard);
+ self.schedule_drop_for_binding(binding.var_id, binding.span, OutsideGuard);
+ let rvalue = match binding.binding_mode {
+ BindingMode::ByValue => {
+ Rvalue::Use(self.consume_by_copy_or_move(binding.source.clone()))
+ }
+ BindingMode::ByRef(borrow_kind) => {
+ Rvalue::Ref(re_erased, borrow_kind, binding.source.clone())
+ }
+ };
+ self.cfg.push_assign(block, source_info, &local, rvalue);
+ }
+ }
+
+ /// Each binding (`ref mut var`/`ref var`/`mut var`/`var`, where the bound
+ /// `var` has type `T` in the arm body) in a pattern maps to 2 locals. The
+ /// first local is a binding for occurrences of `var` in the guard, which
+ /// will have type `&T`. The second local is a binding for occurrences of
+ /// `var` in the arm body, which will have type `T`.
+ fn declare_binding(
+ &mut self,
+ source_info: SourceInfo,
+ visibility_scope: SourceScope,
+ mutability: Mutability,
+ name: Name,
+ mode: BindingMode,
+ var_id: HirId,
+ var_ty: Ty<'tcx>,
+ user_ty: UserTypeProjections,
+ has_guard: ArmHasGuard,
+ opt_match_place: Option<(Option<Place<'tcx>>, Span)>,
+ pat_span: Span,
+ ) {
+ debug!(
+ "declare_binding(var_id={:?}, name={:?}, mode={:?}, var_ty={:?}, \
+ visibility_scope={:?}, source_info={:?})",
+ var_id, name, mode, var_ty, visibility_scope, source_info
+ );
+
+ let tcx = self.hir.tcx();
+ let debug_source_info = SourceInfo { span: source_info.span, scope: visibility_scope };
+ let binding_mode = match mode {
+ BindingMode::ByValue => ty::BindingMode::BindByValue(mutability.into()),
+ BindingMode::ByRef(_) => ty::BindingMode::BindByReference(mutability.into()),
+ };
+ debug!("declare_binding: user_ty={:?}", user_ty);
+ let local = LocalDecl::<'tcx> {
+ mutability,
+ ty: var_ty,
+ user_ty,
+ source_info,
+ internal: false,
+ is_block_tail: None,
+ local_info: LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
+ binding_mode,
+ // hypothetically, `visit_bindings` could try to unzip
+ // an outermost hir::Ty as we descend, matching up
+ // idents in pat; but complex w/ unclear UI payoff.
+ // Instead, just abandon providing diagnostic info.
+ opt_ty_info: None,
+ opt_match_place,
+ pat_span,
+ }))),
+ };
+ let for_arm_body = self.local_decls.push(local);
+ self.var_debug_info.push(VarDebugInfo {
+ name,
+ source_info: debug_source_info,
+ place: for_arm_body.into(),
+ });
+ let locals = if has_guard.0 {
+ let ref_for_guard = self.local_decls.push(LocalDecl::<'tcx> {
+ // This variable isn't mutated but has a name, so has to be
+ // immutable to avoid the unused mut lint.
+ mutability: Mutability::Not,
+ ty: tcx.mk_imm_ref(tcx.lifetimes.re_erased, var_ty),
+ user_ty: UserTypeProjections::none(),
+ source_info,
+ internal: false,
+ is_block_tail: None,
+ local_info: LocalInfo::User(ClearCrossCrate::Set(BindingForm::RefForGuard)),
+ });
+ self.var_debug_info.push(VarDebugInfo {
+ name,
+ source_info: debug_source_info,
+ place: ref_for_guard.into(),
+ });
+ LocalsForNode::ForGuard { ref_for_guard, for_arm_body }
+ } else {
+ LocalsForNode::One(for_arm_body)
+ };
+ debug!("declare_binding: vars={:?}", locals);
+ self.var_indices.insert(var_id, locals);
+ }
+}
--- /dev/null
+//! Simplifying Candidates
+//!
+//! *Simplifying* a match pair `place @ pattern` means breaking it down
+//! into bindings or other, simpler match pairs. For example:
+//!
+//! - `place @ (P1, P2)` can be simplified to `[place.0 @ P1, place.1 @ P2]`
+//! - `place @ x` can be simplified to `[]` by binding `x` to `place`
+//!
+//! The `simplify_candidate` routine just repeatedly applies these
+//! sort of simplifications until there is nothing left to
+//! simplify. Match pairs cannot be simplified if they require some
+//! sort of test: for example, testing which variant an enum is, or
+//! testing a value against a constant.
+
+use crate::build::matches::{Ascription, Binding, Candidate, MatchPair};
+use crate::build::Builder;
+use crate::hair::{self, *};
+use rustc::mir::interpret::truncate;
+use rustc::ty;
+use rustc::ty::layout::{Integer, IntegerExt, Size};
+use rustc_hir::RangeEnd;
+use syntax::attr::{SignedInt, UnsignedInt};
+
+use std::mem;
+
+impl<'a, 'tcx> Builder<'a, 'tcx> {
+ crate fn simplify_candidate<'pat>(&mut self, candidate: &mut Candidate<'pat, 'tcx>) {
+ // repeatedly simplify match pairs until fixed point is reached
+ loop {
+ let match_pairs = mem::take(&mut candidate.match_pairs);
+ let mut changed = false;
+ for match_pair in match_pairs {
+ match self.simplify_match_pair(match_pair, candidate) {
+ Ok(()) => {
+ changed = true;
+ }
+ Err(match_pair) => {
+ candidate.match_pairs.push(match_pair);
+ }
+ }
+ }
+ if !changed {
+ return; // if we were not able to simplify any, done.
+ }
+ }
+ }
+
+ /// Tries to simplify `match_pair`, returning `Ok(())` if
+ /// successful. If successful, new match pairs and bindings will
+ /// have been pushed into the candidate. If no simplification is
+ /// possible, `Err` is returned and no changes are made to
+ /// candidate.
+ fn simplify_match_pair<'pat>(
+ &mut self,
+ match_pair: MatchPair<'pat, 'tcx>,
+ candidate: &mut Candidate<'pat, 'tcx>,
+ ) -> Result<(), MatchPair<'pat, 'tcx>> {
+ let tcx = self.hir.tcx();
+ match *match_pair.pattern.kind {
+ PatKind::AscribeUserType {
+ ref subpattern,
+ ascription: hair::pattern::Ascription { variance, ref user_ty, user_ty_span },
+ } => {
+ // Apply the type ascription to the value at `match_pair.place`, which is the
+ // value being matched, taking the variance field into account.
+ candidate.ascriptions.push(Ascription {
+ span: user_ty_span,
+ user_ty: user_ty.clone(),
+ source: match_pair.place.clone(),
+ variance,
+ });
+
+ candidate.match_pairs.push(MatchPair::new(match_pair.place, subpattern));
+
+ Ok(())
+ }
+
+ PatKind::Wild => {
+ // nothing left to do
+ Ok(())
+ }
+
+ PatKind::Binding { name, mutability, mode, var, ty, ref subpattern } => {
+ candidate.bindings.push(Binding {
+ name,
+ mutability,
+ span: match_pair.pattern.span,
+ source: match_pair.place.clone(),
+ var_id: var,
+ var_ty: ty,
+ binding_mode: mode,
+ });
+
+ if let Some(subpattern) = subpattern.as_ref() {
+ // this is the `x @ P` case; have to keep matching against `P` now
+ candidate.match_pairs.push(MatchPair::new(match_pair.place, subpattern));
+ }
+
+ Ok(())
+ }
+
+ PatKind::Constant { .. } => {
+ // FIXME normalize patterns when possible
+ Err(match_pair)
+ }
+
+ PatKind::Range(PatRange { lo, hi, end }) => {
+ let (range, bias) = match lo.ty.kind {
+ ty::Char => {
+ (Some(('\u{0000}' as u128, '\u{10FFFF}' as u128, Size::from_bits(32))), 0)
+ }
+ ty::Int(ity) => {
+ let size = Integer::from_attr(&tcx, SignedInt(ity)).size();
+ let max = truncate(u128::max_value(), size);
+ let bias = 1u128 << (size.bits() - 1);
+ (Some((0, max, size)), bias)
+ }
+ ty::Uint(uty) => {
+ let size = Integer::from_attr(&tcx, UnsignedInt(uty)).size();
+ let max = truncate(u128::max_value(), size);
+ (Some((0, max, size)), 0)
+ }
+ _ => (None, 0),
+ };
+ if let Some((min, max, sz)) = range {
+ if let (Some(lo), Some(hi)) = (lo.val.try_to_bits(sz), hi.val.try_to_bits(sz)) {
+ // We want to compare ranges numerically, but the order of the bitwise
+ // representation of signed integers does not match their numeric order.
+ // Thus, to correct the ordering, we need to shift the range of signed
+ // integers to correct the comparison. This is achieved by XORing with a
+ // bias (see pattern/_match.rs for another pertinent example of this
+ // pattern).
+ let (lo, hi) = (lo ^ bias, hi ^ bias);
+ if lo <= min && (hi > max || hi == max && end == RangeEnd::Included) {
+ // Irrefutable pattern match.
+ return Ok(());
+ }
+ }
+ }
+ Err(match_pair)
+ }
+
+ PatKind::Slice { ref prefix, ref slice, ref suffix } => {
+ if prefix.is_empty() && slice.is_some() && suffix.is_empty() {
+ // irrefutable
+ self.prefix_slice_suffix(
+ &mut candidate.match_pairs,
+ &match_pair.place,
+ prefix,
+ slice.as_ref(),
+ suffix,
+ );
+ Ok(())
+ } else {
+ Err(match_pair)
+ }
+ }
+
+ PatKind::Variant { adt_def, substs, variant_index, ref subpatterns } => {
+ let irrefutable = adt_def.variants.iter_enumerated().all(|(i, v)| {
+ i == variant_index || {
+ self.hir.tcx().features().exhaustive_patterns
+ && !v
+ .uninhabited_from(self.hir.tcx(), substs, adt_def.adt_kind())
+ .is_empty()
+ }
+ }) && (adt_def.did.is_local()
+ || !adt_def.is_variant_list_non_exhaustive());
+ if irrefutable {
+ let place = tcx.mk_place_downcast(match_pair.place, adt_def, variant_index);
+ candidate.match_pairs.extend(self.field_match_pairs(place, subpatterns));
+ Ok(())
+ } else {
+ Err(match_pair)
+ }
+ }
+
+ PatKind::Array { ref prefix, ref slice, ref suffix } => {
+ self.prefix_slice_suffix(
+ &mut candidate.match_pairs,
+ &match_pair.place,
+ prefix,
+ slice.as_ref(),
+ suffix,
+ );
+ Ok(())
+ }
+
+ PatKind::Leaf { ref subpatterns } => {
+ // tuple struct, match subpats (if any)
+ candidate.match_pairs.extend(self.field_match_pairs(match_pair.place, subpatterns));
+ Ok(())
+ }
+
+ PatKind::Deref { ref subpattern } => {
+ let place = tcx.mk_place_deref(match_pair.place);
+ candidate.match_pairs.push(MatchPair::new(place, subpattern));
+ Ok(())
+ }
+
+ PatKind::Or { .. } => Err(match_pair),
+ }
+ }
+}
--- /dev/null
+// Testing candidates
+//
+// After candidates have been simplified, the only match pairs that
+// remain are those that require some sort of test. The functions here
+// identify what tests are needed, perform the tests, and then filter
+// the candidates based on the result.
+
+use crate::build::matches::{Candidate, MatchPair, Test, TestKind};
+use crate::build::Builder;
+use crate::hair::pattern::compare_const_vals;
+use crate::hair::*;
+use rustc::mir::*;
+use rustc::ty::layout::VariantIdx;
+use rustc::ty::util::IntTypeExt;
+use rustc::ty::{self, adjustment::PointerCast, Ty};
+use rustc_data_structures::fx::FxHashMap;
+use rustc_hir::RangeEnd;
+use rustc_index::bit_set::BitSet;
+use rustc_span::symbol::sym;
+
+use std::cmp::Ordering;
+
+impl<'a, 'tcx> Builder<'a, 'tcx> {
+ /// Identifies what test is needed to decide if `match_pair` is applicable.
+ ///
+ /// It is a bug to call this with a simplifiable pattern.
+ crate fn test<'pat>(&mut self, match_pair: &MatchPair<'pat, 'tcx>) -> Test<'tcx> {
+ match *match_pair.pattern.kind {
+ PatKind::Variant { ref adt_def, substs: _, variant_index: _, subpatterns: _ } => Test {
+ span: match_pair.pattern.span,
+ kind: TestKind::Switch {
+ adt_def,
+ variants: BitSet::new_empty(adt_def.variants.len()),
+ },
+ },
+
+ PatKind::Constant { .. } if is_switch_ty(match_pair.pattern.ty) => {
+ // For integers, we use a `SwitchInt` match, which allows
+ // us to handle more cases.
+ Test {
+ span: match_pair.pattern.span,
+ kind: TestKind::SwitchInt {
+ switch_ty: match_pair.pattern.ty,
+
+ // these maps are empty to start; cases are
+ // added below in add_cases_to_switch
+ options: vec![],
+ indices: Default::default(),
+ },
+ }
+ }
+
+ PatKind::Constant { value } => Test {
+ span: match_pair.pattern.span,
+ kind: TestKind::Eq { value, ty: match_pair.pattern.ty.clone() },
+ },
+
+ PatKind::Range(range) => {
+ assert_eq!(range.lo.ty, match_pair.pattern.ty);
+ assert_eq!(range.hi.ty, match_pair.pattern.ty);
+ Test { span: match_pair.pattern.span, kind: TestKind::Range(range) }
+ }
+
+ PatKind::Slice { ref prefix, ref slice, ref suffix } => {
+ let len = prefix.len() + suffix.len();
+ let op = if slice.is_some() { BinOp::Ge } else { BinOp::Eq };
+ Test {
+ span: match_pair.pattern.span,
+ kind: TestKind::Len { len: len as u64, op: op },
+ }
+ }
+
+ PatKind::Or { .. } => self
+ .hir
+ .tcx()
+ .sess
+ .span_fatal(match_pair.pattern.span, "or-patterns are not fully implemented yet"),
+
+ PatKind::AscribeUserType { .. }
+ | PatKind::Array { .. }
+ | PatKind::Wild
+ | PatKind::Binding { .. }
+ | PatKind::Leaf { .. }
+ | PatKind::Deref { .. } => self.error_simplifyable(match_pair),
+ }
+ }
+
+ crate fn add_cases_to_switch<'pat>(
+ &mut self,
+ test_place: &Place<'tcx>,
+ candidate: &Candidate<'pat, 'tcx>,
+ switch_ty: Ty<'tcx>,
+ options: &mut Vec<u128>,
+ indices: &mut FxHashMap<&'tcx ty::Const<'tcx>, usize>,
+ ) -> bool {
+ let match_pair = match candidate.match_pairs.iter().find(|mp| mp.place == *test_place) {
+ Some(match_pair) => match_pair,
+ _ => {
+ return false;
+ }
+ };
+
+ match *match_pair.pattern.kind {
+ PatKind::Constant { value } => {
+ indices.entry(value).or_insert_with(|| {
+ options.push(value.eval_bits(self.hir.tcx(), self.hir.param_env, switch_ty));
+ options.len() - 1
+ });
+ true
+ }
+ PatKind::Variant { .. } => {
+ panic!("you should have called add_variants_to_switch instead!");
+ }
+ PatKind::Range(range) => {
+ // Check that none of the switch values are in the range.
+ self.values_not_contained_in_range(range, indices).unwrap_or(false)
+ }
+ PatKind::Slice { .. }
+ | PatKind::Array { .. }
+ | PatKind::Wild
+ | PatKind::Or { .. }
+ | PatKind::Binding { .. }
+ | PatKind::AscribeUserType { .. }
+ | PatKind::Leaf { .. }
+ | PatKind::Deref { .. } => {
+ // don't know how to add these patterns to a switch
+ false
+ }
+ }
+ }
+
+ crate fn add_variants_to_switch<'pat>(
+ &mut self,
+ test_place: &Place<'tcx>,
+ candidate: &Candidate<'pat, 'tcx>,
+ variants: &mut BitSet<VariantIdx>,
+ ) -> bool {
+ let match_pair = match candidate.match_pairs.iter().find(|mp| mp.place == *test_place) {
+ Some(match_pair) => match_pair,
+ _ => {
+ return false;
+ }
+ };
+
+ match *match_pair.pattern.kind {
+ PatKind::Variant { adt_def: _, variant_index, .. } => {
+ // We have a pattern testing for variant `variant_index`
+ // set the corresponding index to true
+ variants.insert(variant_index);
+ true
+ }
+ _ => {
+ // don't know how to add these patterns to a switch
+ false
+ }
+ }
+ }
+
+ crate fn perform_test(
+ &mut self,
+ block: BasicBlock,
+ place: &Place<'tcx>,
+ test: &Test<'tcx>,
+ make_target_blocks: impl FnOnce(&mut Self) -> Vec<BasicBlock>,
+ ) {
+ debug!(
+ "perform_test({:?}, {:?}: {:?}, {:?})",
+ block,
+ place,
+ place.ty(&self.local_decls, self.hir.tcx()),
+ test
+ );
+
+ let source_info = self.source_info(test.span);
+ match test.kind {
+ TestKind::Switch { adt_def, ref variants } => {
+ let target_blocks = make_target_blocks(self);
+ // Variants is a BitVec of indexes into adt_def.variants.
+ let num_enum_variants = adt_def.variants.len();
+ let used_variants = variants.count();
+ debug_assert_eq!(target_blocks.len(), num_enum_variants + 1);
+ let otherwise_block = *target_blocks.last().unwrap();
+ let mut targets = Vec::with_capacity(used_variants + 1);
+ let mut values = Vec::with_capacity(used_variants);
+ let tcx = self.hir.tcx();
+ for (idx, discr) in adt_def.discriminants(tcx) {
+ if variants.contains(idx) {
+ debug_assert_ne!(
+ target_blocks[idx.index()],
+ otherwise_block,
+ "no canididates for tested discriminant: {:?}",
+ discr,
+ );
+ values.push(discr.val);
+ targets.push(target_blocks[idx.index()]);
+ } else {
+ debug_assert_eq!(
+ target_blocks[idx.index()],
+ otherwise_block,
+ "found canididates for untested discriminant: {:?}",
+ discr,
+ );
+ }
+ }
+ targets.push(otherwise_block);
+ debug!(
+ "num_enum_variants: {}, tested variants: {:?}, variants: {:?}",
+ num_enum_variants, values, variants
+ );
+ let discr_ty = adt_def.repr.discr_type().to_ty(tcx);
+ let discr = self.temp(discr_ty, test.span);
+ self.cfg.push_assign(
+ block,
+ source_info,
+ &discr,
+ Rvalue::Discriminant(place.clone()),
+ );
+ assert_eq!(values.len() + 1, targets.len());
+ self.cfg.terminate(
+ block,
+ source_info,
+ TerminatorKind::SwitchInt {
+ discr: Operand::Move(discr),
+ switch_ty: discr_ty,
+ values: From::from(values),
+ targets,
+ },
+ );
+ }
+
+ TestKind::SwitchInt { switch_ty, ref options, indices: _ } => {
+ let target_blocks = make_target_blocks(self);
+ let terminator = if switch_ty.kind == ty::Bool {
+ assert!(options.len() > 0 && options.len() <= 2);
+ if let [first_bb, second_bb] = *target_blocks {
+ let (true_bb, false_bb) = match options[0] {
+ 1 => (first_bb, second_bb),
+ 0 => (second_bb, first_bb),
+ v => span_bug!(test.span, "expected boolean value but got {:?}", v),
+ };
+ TerminatorKind::if_(
+ self.hir.tcx(),
+ Operand::Copy(place.clone()),
+ true_bb,
+ false_bb,
+ )
+ } else {
+ bug!("`TestKind::SwitchInt` on `bool` should have two targets")
+ }
+ } 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.clone()),
+ switch_ty,
+ values: options.clone().into(),
+ targets: target_blocks,
+ }
+ };
+ self.cfg.terminate(block, source_info, terminator);
+ }
+
+ TestKind::Eq { value, ty } => {
+ if !ty.is_scalar() {
+ // Use `PartialEq::eq` instead of `BinOp::Eq`
+ // (the binop can only handle primitives)
+ self.non_scalar_compare(
+ block,
+ make_target_blocks,
+ source_info,
+ value,
+ place,
+ ty,
+ );
+ } else {
+ if let [success, fail] = *make_target_blocks(self) {
+ assert_eq!(value.ty, ty);
+ let expect = self.literal_operand(test.span, value);
+ let val = Operand::Copy(place.clone());
+ self.compare(block, success, fail, source_info, BinOp::Eq, expect, val);
+ } else {
+ bug!("`TestKind::Eq` should have two target blocks");
+ }
+ }
+ }
+
+ TestKind::Range(PatRange { ref lo, ref hi, ref end }) => {
+ let lower_bound_success = self.cfg.start_new_block();
+ let target_blocks = make_target_blocks(self);
+
+ // Test `val` by computing `lo <= val && val <= hi`, using primitive comparisons.
+ let lo = self.literal_operand(test.span, lo);
+ let hi = self.literal_operand(test.span, hi);
+ let val = Operand::Copy(place.clone());
+
+ if let [success, fail] = *target_blocks {
+ self.compare(
+ block,
+ lower_bound_success,
+ fail,
+ source_info,
+ BinOp::Le,
+ lo,
+ val.clone(),
+ );
+ let op = match *end {
+ RangeEnd::Included => BinOp::Le,
+ RangeEnd::Excluded => BinOp::Lt,
+ };
+ self.compare(lower_bound_success, success, fail, source_info, op, val, hi);
+ } else {
+ bug!("`TestKind::Range` should have two target blocks");
+ }
+ }
+
+ TestKind::Len { len, op } => {
+ let target_blocks = make_target_blocks(self);
+
+ let usize_ty = self.hir.usize_ty();
+ let actual = self.temp(usize_ty, test.span);
+
+ // actual = len(place)
+ self.cfg.push_assign(block, source_info, &actual, Rvalue::Len(place.clone()));
+
+ // expected = <N>
+ let expected = self.push_usize(block, source_info, len);
+
+ if let [true_bb, false_bb] = *target_blocks {
+ // result = actual == expected OR result = actual < expected
+ // branch based on result
+ self.compare(
+ block,
+ true_bb,
+ false_bb,
+ source_info,
+ op,
+ Operand::Move(actual),
+ Operand::Move(expected),
+ );
+ } else {
+ bug!("`TestKind::Len` should have two target blocks");
+ }
+ }
+ }
+ }
+
+ /// Compare using the provided built-in comparison operator
+ fn compare(
+ &mut self,
+ block: BasicBlock,
+ success_block: BasicBlock,
+ fail_block: BasicBlock,
+ source_info: SourceInfo,
+ op: BinOp,
+ left: Operand<'tcx>,
+ right: Operand<'tcx>,
+ ) {
+ let bool_ty = self.hir.bool_ty();
+ let result = self.temp(bool_ty, source_info.span);
+
+ // result = op(left, right)
+ self.cfg.push_assign(block, source_info, &result, Rvalue::BinaryOp(op, left, right));
+
+ // branch based on result
+ self.cfg.terminate(
+ block,
+ source_info,
+ TerminatorKind::if_(self.hir.tcx(), Operand::Move(result), success_block, fail_block),
+ );
+ }
+
+ /// Compare two `&T` values using `<T as std::compare::PartialEq>::eq`
+ fn non_scalar_compare(
+ &mut self,
+ block: BasicBlock,
+ make_target_blocks: impl FnOnce(&mut Self) -> Vec<BasicBlock>,
+ source_info: SourceInfo,
+ value: &'tcx ty::Const<'tcx>,
+ place: &Place<'tcx>,
+ mut ty: Ty<'tcx>,
+ ) {
+ use rustc::middle::lang_items::EqTraitLangItem;
+
+ let mut expect = self.literal_operand(source_info.span, value);
+ let mut val = Operand::Copy(place.clone());
+
+ // If we're using `b"..."` as a pattern, we need to insert an
+ // unsizing coercion, as the byte string has the type `&[u8; N]`.
+ //
+ // We want to do this even when the scrutinee is a reference to an
+ // array, so we can call `<[u8]>::eq` rather than having to find an
+ // `<[u8; N]>::eq`.
+ let unsize = |ty: Ty<'tcx>| match ty.kind {
+ ty::Ref(region, rty, _) => match rty.kind {
+ ty::Array(inner_ty, n) => Some((region, inner_ty, n)),
+ _ => None,
+ },
+ _ => None,
+ };
+ let opt_ref_ty = unsize(ty);
+ let opt_ref_test_ty = unsize(value.ty);
+ match (opt_ref_ty, opt_ref_test_ty) {
+ // nothing to do, neither is an array
+ (None, None) => {}
+ (Some((region, elem_ty, _)), _) | (None, Some((region, elem_ty, _))) => {
+ let tcx = self.hir.tcx();
+ // make both a slice
+ ty = tcx.mk_imm_ref(region, tcx.mk_slice(elem_ty));
+ if opt_ref_ty.is_some() {
+ let temp = self.temp(ty, source_info.span);
+ self.cfg.push_assign(
+ block,
+ source_info,
+ &temp,
+ Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), val, ty),
+ );
+ val = Operand::Move(temp);
+ }
+ if opt_ref_test_ty.is_some() {
+ let slice = self.temp(ty, source_info.span);
+ self.cfg.push_assign(
+ block,
+ source_info,
+ &slice,
+ Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), expect, ty),
+ );
+ expect = Operand::Move(slice);
+ }
+ }
+ }
+
+ let deref_ty = match ty.kind {
+ ty::Ref(_, deref_ty, _) => deref_ty,
+ _ => bug!("non_scalar_compare called on non-reference type: {}", ty),
+ };
+
+ let eq_def_id = self.hir.tcx().require_lang_item(EqTraitLangItem, None);
+ let method = self.hir.trait_method(eq_def_id, sym::eq, deref_ty, &[deref_ty.into()]);
+
+ let bool_ty = self.hir.bool_ty();
+ let eq_result = self.temp(bool_ty, source_info.span);
+ let eq_block = self.cfg.start_new_block();
+ let cleanup = self.diverge_cleanup();
+ self.cfg.terminate(
+ block,
+ source_info,
+ TerminatorKind::Call {
+ func: Operand::Constant(box Constant {
+ span: source_info.span,
+
+ // FIXME(#54571): This constant comes from user input (a
+ // constant in a pattern). Are there forms where users can add
+ // type annotations here? For example, an associated constant?
+ // Need to experiment.
+ user_ty: None,
+
+ literal: method,
+ }),
+ args: vec![val, expect],
+ destination: Some((eq_result.clone(), eq_block)),
+ cleanup: Some(cleanup),
+ from_hir_call: false,
+ },
+ );
+
+ if let [success_block, fail_block] = *make_target_blocks(self) {
+ // check the result
+ self.cfg.terminate(
+ eq_block,
+ source_info,
+ TerminatorKind::if_(
+ self.hir.tcx(),
+ Operand::Move(eq_result),
+ success_block,
+ fail_block,
+ ),
+ );
+ } else {
+ bug!("`TestKind::Eq` should have two target blocks")
+ }
+ }
+
+ /// Given that we are performing `test` against `test_place`, this job
+ /// sorts out what the status of `candidate` will be after the test. See
+ /// `test_candidates` for the usage of this function. The returned index is
+ /// the index that this candidate should be placed in the
+ /// `target_candidates` vec. The candidate may be modified to update its
+ /// `match_pairs`.
+ ///
+ /// So, for example, if this candidate is `x @ Some(P0)` and the `Test` is
+ /// a variant test, then we would modify the candidate to be `(x as
+ /// Option).0 @ P0` and return the index corresponding to the variant
+ /// `Some`.
+ ///
+ /// However, in some cases, the test may just not be relevant to candidate.
+ /// For example, suppose we are testing whether `foo.x == 22`, but in one
+ /// match arm we have `Foo { x: _, ... }`... in that case, the test for
+ /// what value `x` has has no particular relevance to this candidate. In
+ /// such cases, this function just returns None without doing anything.
+ /// This is used by the overall `match_candidates` algorithm to structure
+ /// the match as a whole. See `match_candidates` for more details.
+ ///
+ /// FIXME(#29623). In some cases, we have some tricky choices to make. for
+ /// example, if we are testing that `x == 22`, but the candidate is `x @
+ /// 13..55`, what should we do? In the event that the test is true, we know
+ /// that the candidate applies, but in the event of false, we don't know
+ /// that it *doesn't* apply. For now, we return false, indicate that the
+ /// test does not apply to this candidate, but it might be we can get
+ /// tighter match code if we do something a bit different.
+ crate fn sort_candidate<'pat>(
+ &mut self,
+ test_place: &Place<'tcx>,
+ test: &Test<'tcx>,
+ candidate: &mut Candidate<'pat, 'tcx>,
+ ) -> Option<usize> {
+ // Find the match_pair for this place (if any). At present,
+ // afaik, there can be at most one. (In the future, if we
+ // adopted a more general `@` operator, there might be more
+ // than one, but it'd be very unusual to have two sides that
+ // both require tests; you'd expect one side to be simplified
+ // away.)
+ let (match_pair_index, match_pair) =
+ candidate.match_pairs.iter().enumerate().find(|&(_, mp)| mp.place == *test_place)?;
+
+ match (&test.kind, &*match_pair.pattern.kind) {
+ // If we are performing a variant switch, then this
+ // informs variant patterns, but nothing else.
+ (
+ &TestKind::Switch { adt_def: tested_adt_def, .. },
+ &PatKind::Variant { adt_def, variant_index, ref subpatterns, .. },
+ ) => {
+ assert_eq!(adt_def, tested_adt_def);
+ self.candidate_after_variant_switch(
+ match_pair_index,
+ adt_def,
+ variant_index,
+ subpatterns,
+ candidate,
+ );
+ Some(variant_index.as_usize())
+ }
+
+ (&TestKind::Switch { .. }, _) => None,
+
+ // If we are performing a switch over integers, then this informs integer
+ // equality, but nothing else.
+ //
+ // FIXME(#29623) we could use PatKind::Range to rule
+ // things out here, in some cases.
+ (
+ &TestKind::SwitchInt { switch_ty: _, options: _, ref indices },
+ &PatKind::Constant { ref value },
+ ) if is_switch_ty(match_pair.pattern.ty) => {
+ let index = indices[value];
+ self.candidate_without_match_pair(match_pair_index, candidate);
+ Some(index)
+ }
+
+ (
+ &TestKind::SwitchInt { switch_ty: _, ref options, ref indices },
+ &PatKind::Range(range),
+ ) => {
+ let not_contained =
+ self.values_not_contained_in_range(range, indices).unwrap_or(false);
+
+ if not_contained {
+ // No switch values are contained in the pattern range,
+ // so the pattern can be matched only if this test fails.
+ let otherwise = options.len();
+ Some(otherwise)
+ } else {
+ None
+ }
+ }
+
+ (&TestKind::SwitchInt { .. }, _) => None,
+
+ (
+ &TestKind::Len { len: test_len, op: BinOp::Eq },
+ &PatKind::Slice { ref prefix, ref slice, ref suffix },
+ ) => {
+ let pat_len = (prefix.len() + suffix.len()) as u64;
+ match (test_len.cmp(&pat_len), slice) {
+ (Ordering::Equal, &None) => {
+ // on true, min_len = len = $actual_length,
+ // on false, len != $actual_length
+ self.candidate_after_slice_test(
+ match_pair_index,
+ candidate,
+ prefix,
+ slice.as_ref(),
+ suffix,
+ );
+ Some(0)
+ }
+ (Ordering::Less, _) => {
+ // test_len < pat_len. If $actual_len = test_len,
+ // then $actual_len < pat_len and we don't have
+ // enough elements.
+ Some(1)
+ }
+ (Ordering::Equal, &Some(_)) | (Ordering::Greater, &Some(_)) => {
+ // This can match both if $actual_len = test_len >= pat_len,
+ // and if $actual_len > test_len. We can't advance.
+ None
+ }
+ (Ordering::Greater, &None) => {
+ // test_len != pat_len, so if $actual_len = test_len, then
+ // $actual_len != pat_len.
+ Some(1)
+ }
+ }
+ }
+
+ (
+ &TestKind::Len { len: test_len, op: BinOp::Ge },
+ &PatKind::Slice { ref prefix, ref slice, ref suffix },
+ ) => {
+ // the test is `$actual_len >= test_len`
+ let pat_len = (prefix.len() + suffix.len()) as u64;
+ match (test_len.cmp(&pat_len), slice) {
+ (Ordering::Equal, &Some(_)) => {
+ // $actual_len >= test_len = pat_len,
+ // so we can match.
+ self.candidate_after_slice_test(
+ match_pair_index,
+ candidate,
+ prefix,
+ slice.as_ref(),
+ suffix,
+ );
+ Some(0)
+ }
+ (Ordering::Less, _) | (Ordering::Equal, &None) => {
+ // test_len <= pat_len. If $actual_len < test_len,
+ // then it is also < pat_len, so the test passing is
+ // necessary (but insufficient).
+ Some(0)
+ }
+ (Ordering::Greater, &None) => {
+ // test_len > pat_len. If $actual_len >= test_len > pat_len,
+ // then we know we won't have a match.
+ Some(1)
+ }
+ (Ordering::Greater, &Some(_)) => {
+ // test_len < pat_len, and is therefore less
+ // strict. This can still go both ways.
+ None
+ }
+ }
+ }
+
+ (&TestKind::Range(test), &PatKind::Range(pat)) => {
+ if test == pat {
+ self.candidate_without_match_pair(match_pair_index, candidate);
+ return Some(0);
+ }
+
+ let no_overlap = (|| {
+ use rustc_hir::RangeEnd::*;
+ use std::cmp::Ordering::*;
+
+ let tcx = self.hir.tcx();
+
+ let test_ty = test.lo.ty;
+ let lo = compare_const_vals(tcx, test.lo, pat.hi, self.hir.param_env, test_ty)?;
+ let hi = compare_const_vals(tcx, test.hi, pat.lo, self.hir.param_env, test_ty)?;
+
+ match (test.end, pat.end, lo, hi) {
+ // pat < test
+ (_, _, Greater, _) |
+ (_, Excluded, Equal, _) |
+ // pat > test
+ (_, _, _, Less) |
+ (Excluded, _, _, Equal) => Some(true),
+ _ => Some(false),
+ }
+ })();
+
+ if let Some(true) = no_overlap {
+ // Testing range does not overlap with pattern range,
+ // so the pattern can be matched only if this test fails.
+ Some(1)
+ } else {
+ None
+ }
+ }
+
+ (&TestKind::Range(range), &PatKind::Constant { value }) => {
+ if let Some(false) = self.const_range_contains(range, value) {
+ // `value` is not contained in the testing range,
+ // so `value` can be matched only if this test fails.
+ Some(1)
+ } else {
+ None
+ }
+ }
+
+ (&TestKind::Range { .. }, _) => None,
+
+ (&TestKind::Eq { .. }, _) | (&TestKind::Len { .. }, _) => {
+ // These are all binary tests.
+ //
+ // FIXME(#29623) we can be more clever here
+ let pattern_test = self.test(&match_pair);
+ if pattern_test.kind == test.kind {
+ self.candidate_without_match_pair(match_pair_index, candidate);
+ Some(0)
+ } else {
+ None
+ }
+ }
+ }
+ }
+
+ fn candidate_without_match_pair(
+ &mut self,
+ match_pair_index: usize,
+ candidate: &mut Candidate<'_, 'tcx>,
+ ) {
+ candidate.match_pairs.remove(match_pair_index);
+ }
+
+ fn candidate_after_slice_test<'pat>(
+ &mut self,
+ match_pair_index: usize,
+ candidate: &mut Candidate<'pat, 'tcx>,
+ prefix: &'pat [Pat<'tcx>],
+ opt_slice: Option<&'pat Pat<'tcx>>,
+ suffix: &'pat [Pat<'tcx>],
+ ) {
+ let removed_place = candidate.match_pairs.remove(match_pair_index).place;
+ self.prefix_slice_suffix(
+ &mut candidate.match_pairs,
+ &removed_place,
+ prefix,
+ opt_slice,
+ suffix,
+ );
+ }
+
+ fn candidate_after_variant_switch<'pat>(
+ &mut self,
+ match_pair_index: usize,
+ adt_def: &'tcx ty::AdtDef,
+ variant_index: VariantIdx,
+ subpatterns: &'pat [FieldPat<'tcx>],
+ candidate: &mut Candidate<'pat, 'tcx>,
+ ) {
+ let match_pair = candidate.match_pairs.remove(match_pair_index);
+ let tcx = self.hir.tcx();
+
+ // So, if we have a match-pattern like `x @ Enum::Variant(P1, P2)`,
+ // we want to create a set of derived match-patterns like
+ // `(x as Variant).0 @ P1` and `(x as Variant).1 @ P1`.
+ let elem = ProjectionElem::Downcast(
+ Some(adt_def.variants[variant_index].ident.name),
+ variant_index,
+ );
+ let downcast_place = tcx.mk_place_elem(match_pair.place, elem); // `(x as Variant)`
+ let consequent_match_pairs = subpatterns.iter().map(|subpattern| {
+ // e.g., `(x as Variant).0`
+ let place =
+ tcx.mk_place_field(downcast_place.clone(), subpattern.field, subpattern.pattern.ty);
+ // e.g., `(x as Variant).0 @ P1`
+ MatchPair::new(place, &subpattern.pattern)
+ });
+
+ candidate.match_pairs.extend(consequent_match_pairs);
+ }
+
+ fn error_simplifyable<'pat>(&mut self, match_pair: &MatchPair<'pat, 'tcx>) -> ! {
+ span_bug!(match_pair.pattern.span, "simplifyable pattern found: {:?}", match_pair.pattern)
+ }
+
+ fn const_range_contains(
+ &self,
+ range: PatRange<'tcx>,
+ value: &'tcx ty::Const<'tcx>,
+ ) -> Option<bool> {
+ use std::cmp::Ordering::*;
+
+ let tcx = self.hir.tcx();
+
+ let a = compare_const_vals(tcx, range.lo, value, self.hir.param_env, range.lo.ty)?;
+ let b = compare_const_vals(tcx, value, range.hi, self.hir.param_env, range.lo.ty)?;
+
+ match (b, range.end) {
+ (Less, _) | (Equal, RangeEnd::Included) if a != Greater => Some(true),
+ _ => Some(false),
+ }
+ }
+
+ fn values_not_contained_in_range(
+ &self,
+ range: PatRange<'tcx>,
+ indices: &FxHashMap<&'tcx ty::Const<'tcx>, usize>,
+ ) -> Option<bool> {
+ for &val in indices.keys() {
+ if self.const_range_contains(range, val)? {
+ return Some(false);
+ }
+ }
+
+ Some(true)
+ }
+}
+
+impl Test<'_> {
+ pub(super) fn targets(&self) -> usize {
+ match self.kind {
+ TestKind::Eq { .. } | TestKind::Range(_) | TestKind::Len { .. } => 2,
+ TestKind::Switch { adt_def, .. } => {
+ // While the switch that we generate doesn't test for all
+ // variants, we have a target for each variant and the
+ // otherwise case, and we make sure that all of the cases not
+ // specified have the same block.
+ adt_def.variants.len() + 1
+ }
+ TestKind::SwitchInt { switch_ty, ref options, .. } => {
+ if switch_ty.is_bool() {
+ // `bool` is special cased in `perform_test` to always
+ // branch to two blocks.
+ 2
+ } else {
+ options.len() + 1
+ }
+ }
+ }
+ }
+}
+
+fn is_switch_ty(ty: Ty<'_>) -> bool {
+ ty.is_integral() || ty.is_char() || ty.is_bool()
+}
--- /dev/null
+use crate::build::matches::MatchPair;
+use crate::build::Builder;
+use crate::hair::*;
+use rustc::mir::*;
+use rustc::ty;
+use smallvec::SmallVec;
+use std::convert::TryInto;
+use std::u32;
+
+impl<'a, 'tcx> Builder<'a, 'tcx> {
+ crate fn field_match_pairs<'pat>(
+ &mut self,
+ place: Place<'tcx>,
+ subpatterns: &'pat [FieldPat<'tcx>],
+ ) -> Vec<MatchPair<'pat, 'tcx>> {
+ subpatterns
+ .iter()
+ .map(|fieldpat| {
+ let place = self.hir.tcx().mk_place_field(
+ place.clone(),
+ fieldpat.field,
+ fieldpat.pattern.ty,
+ );
+ MatchPair::new(place, &fieldpat.pattern)
+ })
+ .collect()
+ }
+
+ crate fn prefix_slice_suffix<'pat>(
+ &mut self,
+ match_pairs: &mut SmallVec<[MatchPair<'pat, 'tcx>; 1]>,
+ place: &Place<'tcx>,
+ prefix: &'pat [Pat<'tcx>],
+ opt_slice: Option<&'pat Pat<'tcx>>,
+ suffix: &'pat [Pat<'tcx>],
+ ) {
+ let tcx = self.hir.tcx();
+ let (min_length, exact_size) = match place.ty(&self.local_decls, tcx).ty.kind {
+ ty::Array(_, length) => {
+ (length.eval_usize(tcx, self.hir.param_env).try_into().unwrap(), true)
+ }
+ _ => ((prefix.len() + suffix.len()).try_into().unwrap(), false),
+ };
+
+ match_pairs.extend(prefix.iter().enumerate().map(|(idx, subpattern)| {
+ let elem =
+ ProjectionElem::ConstantIndex { offset: idx as u32, min_length, from_end: false };
+ let place = tcx.mk_place_elem(place.clone(), elem);
+ MatchPair::new(place, subpattern)
+ }));
+
+ if let Some(subslice_pat) = opt_slice {
+ let suffix_len = suffix.len() as u32;
+ let subslice = tcx.mk_place_elem(
+ place.clone(),
+ ProjectionElem::Subslice {
+ from: prefix.len() as u32,
+ to: if exact_size { min_length - suffix_len } else { suffix_len },
+ from_end: !exact_size,
+ },
+ );
+ match_pairs.push(MatchPair::new(subslice, subslice_pat));
+ }
+
+ match_pairs.extend(suffix.iter().rev().enumerate().map(|(idx, subpattern)| {
+ let end_offset = (idx + 1) as u32;
+ let elem = ProjectionElem::ConstantIndex {
+ offset: if exact_size { min_length - end_offset } else { end_offset },
+ min_length,
+ from_end: !exact_size,
+ };
+ let place = tcx.mk_place_elem(place.clone(), elem);
+ MatchPair::new(place, subpattern)
+ }));
+ }
+
+ /// Creates a false edge to `imaginary_target` and a real edge to
+ /// real_target. If `imaginary_target` is none, or is the same as the real
+ /// target, a Goto is generated instead to simplify the generated MIR.
+ crate fn false_edges(
+ &mut self,
+ from_block: BasicBlock,
+ real_target: BasicBlock,
+ imaginary_target: Option<BasicBlock>,
+ source_info: SourceInfo,
+ ) {
+ match imaginary_target {
+ Some(target) if target != real_target => {
+ self.cfg.terminate(
+ from_block,
+ source_info,
+ TerminatorKind::FalseEdges { real_target, imaginary_target: target },
+ );
+ }
+ _ => self.cfg.goto(from_block, source_info, real_target),
+ }
+ }
+}
+
+impl<'pat, 'tcx> MatchPair<'pat, 'tcx> {
+ crate fn new(place: Place<'tcx>, pattern: &'pat Pat<'tcx>) -> MatchPair<'pat, 'tcx> {
+ MatchPair { place, pattern }
+ }
+}
--- /dev/null
+//! Miscellaneous builder routines that are not specific to building any particular
+//! kind of thing.
+
+use crate::build::Builder;
+
+use rustc::ty::{self, Ty};
+
+use rustc::mir::*;
+use rustc_span::{Span, DUMMY_SP};
+
+impl<'a, 'tcx> Builder<'a, 'tcx> {
+ /// Adds a new temporary value of type `ty` storing the result of
+ /// evaluating `expr`.
+ ///
+ /// N.B., **No cleanup is scheduled for this temporary.** You should
+ /// call `schedule_drop` once the temporary is initialized.
+ crate fn temp(&mut self, ty: Ty<'tcx>, span: Span) -> Place<'tcx> {
+ let temp = self.local_decls.push(LocalDecl::new_temp(ty, span));
+ let place = Place::from(temp);
+ debug!("temp: created temp {:?} with type {:?}", place, self.local_decls[temp].ty);
+ place
+ }
+
+ /// Convenience function for creating a literal operand, one
+ /// without any user type annotation.
+ crate fn literal_operand(
+ &mut self,
+ span: Span,
+ literal: &'tcx ty::Const<'tcx>,
+ ) -> Operand<'tcx> {
+ let constant = box Constant { span, user_ty: None, literal };
+ Operand::Constant(constant)
+ }
+
+ crate fn unit_rvalue(&mut self) -> Rvalue<'tcx> {
+ Rvalue::Aggregate(box AggregateKind::Tuple, vec![])
+ }
+
+ // Returns a zero literal operand for the appropriate type, works for
+ // bool, char and integers.
+ crate fn zero_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> {
+ let literal = ty::Const::from_bits(self.hir.tcx(), 0, ty::ParamEnv::empty().and(ty));
+
+ self.literal_operand(span, literal)
+ }
+
+ crate fn push_usize(
+ &mut self,
+ block: BasicBlock,
+ source_info: SourceInfo,
+ value: u64,
+ ) -> Place<'tcx> {
+ let usize_ty = self.hir.usize_ty();
+ let temp = self.temp(usize_ty, source_info.span);
+ self.cfg.push_assign_constant(
+ block,
+ source_info,
+ &temp,
+ Constant {
+ span: source_info.span,
+ user_ty: None,
+ literal: self.hir.usize_literal(value),
+ },
+ );
+ temp
+ }
+
+ crate fn consume_by_copy_or_move(&self, place: Place<'tcx>) -> Operand<'tcx> {
+ let tcx = self.hir.tcx();
+ let ty = place.ty(&self.local_decls, tcx).ty;
+ if !self.hir.type_is_copy_modulo_regions(ty, DUMMY_SP) {
+ Operand::Move(place)
+ } else {
+ Operand::Copy(place)
+ }
+ }
+}
--- /dev/null
+use crate::build;
+use crate::build::scope::DropKind;
+use crate::hair::cx::Cx;
+use crate::hair::{BindingMode, LintLevel, PatKind};
+use rustc::middle::lang_items;
+use rustc::middle::region;
+use rustc::mir::*;
+use rustc::ty::subst::Subst;
+use rustc::ty::{self, Ty, TyCtxt};
+use rustc_hir as hir;
+use rustc_hir::def_id::DefId;
+use rustc_hir::{GeneratorKind, HirIdMap, Node};
+use rustc_index::vec::{Idx, IndexVec};
+use rustc_span::symbol::kw;
+use rustc_span::Span;
+use rustc_target::spec::abi::Abi;
+use rustc_target::spec::PanicStrategy;
+use std::u32;
+use syntax::attr::{self, UnwindAttr};
+
+use super::lints;
+
+crate fn mir_built(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::steal::Steal<BodyAndCache<'_>> {
+ tcx.alloc_steal_mir(mir_build(tcx, def_id))
+}
+
+/// Construct the MIR for a given `DefId`.
+fn mir_build(tcx: TyCtxt<'_>, def_id: DefId) -> BodyAndCache<'_> {
+ let id = tcx.hir().as_local_hir_id(def_id).unwrap();
+
+ // Figure out what primary body this item has.
+ let (body_id, return_ty_span) = match tcx.hir().get(id) {
+ Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(_, decl, body_id, _, _), .. }) => {
+ (*body_id, decl.output.span())
+ }
+ Node::Item(hir::Item {
+ kind: hir::ItemKind::Fn(hir::FnSig { decl, .. }, _, body_id),
+ ..
+ })
+ | Node::ImplItem(hir::ImplItem {
+ kind: hir::ImplItemKind::Method(hir::FnSig { decl, .. }, body_id),
+ ..
+ })
+ | Node::TraitItem(hir::TraitItem {
+ kind:
+ hir::TraitItemKind::Method(hir::FnSig { decl, .. }, hir::TraitMethod::Provided(body_id)),
+ ..
+ }) => (*body_id, decl.output.span()),
+ Node::Item(hir::Item { kind: hir::ItemKind::Static(ty, _, body_id), .. })
+ | Node::Item(hir::Item { kind: hir::ItemKind::Const(ty, body_id), .. })
+ | Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(ty, body_id), .. })
+ | Node::TraitItem(hir::TraitItem {
+ kind: hir::TraitItemKind::Const(ty, Some(body_id)),
+ ..
+ }) => (*body_id, ty.span),
+ Node::AnonConst(hir::AnonConst { body, hir_id, .. }) => (*body, tcx.hir().span(*hir_id)),
+
+ _ => span_bug!(tcx.hir().span(id), "can't build MIR for {:?}", def_id),
+ };
+
+ tcx.infer_ctxt().enter(|infcx| {
+ let cx = Cx::new(&infcx, id);
+ let body = if cx.tables().tainted_by_errors {
+ build::construct_error(cx, body_id)
+ } else if cx.body_owner_kind.is_fn_or_closure() {
+ // fetch the fully liberated fn signature (that is, all bound
+ // types/lifetimes replaced)
+ let fn_sig = cx.tables().liberated_fn_sigs()[id].clone();
+ let fn_def_id = tcx.hir().local_def_id(id);
+
+ let ty = tcx.type_of(fn_def_id);
+ let mut abi = fn_sig.abi;
+ let implicit_argument = match ty.kind {
+ ty::Closure(..) => {
+ // HACK(eddyb) Avoid having RustCall on closures,
+ // as it adds unnecessary (and wrong) auto-tupling.
+ abi = Abi::Rust;
+ Some(ArgInfo(liberated_closure_env_ty(tcx, id, body_id), None, None, None))
+ }
+ ty::Generator(..) => {
+ let gen_ty = tcx.body_tables(body_id).node_type(id);
+ Some(ArgInfo(gen_ty, None, None, None))
+ }
+ _ => None,
+ };
+
+ let safety = match fn_sig.unsafety {
+ hir::Unsafety::Normal => Safety::Safe,
+ hir::Unsafety::Unsafe => Safety::FnUnsafe,
+ };
+
+ let body = tcx.hir().body(body_id);
+ let explicit_arguments = body.params.iter().enumerate().map(|(index, arg)| {
+ let owner_id = tcx.hir().body_owner(body_id);
+ let opt_ty_info;
+ let self_arg;
+ if let Some(ref fn_decl) = tcx.hir().fn_decl_by_hir_id(owner_id) {
+ opt_ty_info = fn_decl.inputs.get(index).map(|ty| ty.span);
+ self_arg = if index == 0 && fn_decl.implicit_self.has_implicit_self() {
+ match fn_decl.implicit_self {
+ hir::ImplicitSelfKind::Imm => Some(ImplicitSelfKind::Imm),
+ hir::ImplicitSelfKind::Mut => Some(ImplicitSelfKind::Mut),
+ hir::ImplicitSelfKind::ImmRef => Some(ImplicitSelfKind::ImmRef),
+ hir::ImplicitSelfKind::MutRef => Some(ImplicitSelfKind::MutRef),
+ _ => None,
+ }
+ } else {
+ None
+ };
+ } else {
+ opt_ty_info = None;
+ self_arg = None;
+ }
+
+ // C-variadic fns also have a `VaList` input that's not listed in `fn_sig`
+ // (as it's created inside the body itself, not passed in from outside).
+ let ty = if fn_sig.c_variadic && index == fn_sig.inputs().len() {
+ let va_list_did =
+ tcx.require_lang_item(lang_items::VaListTypeLangItem, Some(arg.span));
+ let region = tcx.mk_region(ty::ReScope(region::Scope {
+ id: body.value.hir_id.local_id,
+ data: region::ScopeData::CallSite,
+ }));
+
+ tcx.type_of(va_list_did).subst(tcx, &[region.into()])
+ } else {
+ fn_sig.inputs()[index]
+ };
+
+ ArgInfo(ty, opt_ty_info, Some(&arg), self_arg)
+ });
+
+ let arguments = implicit_argument.into_iter().chain(explicit_arguments);
+
+ let (yield_ty, return_ty) = if body.generator_kind.is_some() {
+ let gen_sig = match ty.kind {
+ ty::Generator(gen_def_id, gen_substs, ..) => {
+ gen_substs.as_generator().sig(gen_def_id, tcx)
+ }
+ _ => span_bug!(tcx.hir().span(id), "generator w/o generator type: {:?}", ty),
+ };
+ (Some(gen_sig.yield_ty), gen_sig.return_ty)
+ } else {
+ (None, fn_sig.output())
+ };
+
+ let mut mir = build::construct_fn(
+ cx,
+ id,
+ arguments,
+ safety,
+ abi,
+ return_ty,
+ return_ty_span,
+ body,
+ );
+ mir.yield_ty = yield_ty;
+ mir
+ } else {
+ // Get the revealed type of this const. This is *not* the adjusted
+ // type of its body, which may be a subtype of this type. For
+ // example:
+ //
+ // fn foo(_: &()) {}
+ // static X: fn(&'static ()) = foo;
+ //
+ // The adjusted type of the body of X is `for<'a> fn(&'a ())` which
+ // is not the same as the type of X. We need the type of the return
+ // place to be the type of the constant because NLL typeck will
+ // equate them.
+
+ let return_ty = cx.tables().node_type(id);
+
+ build::construct_const(cx, body_id, return_ty, return_ty_span)
+ };
+
+ lints::check(tcx, &body, def_id);
+
+ let mut body = BodyAndCache::new(body);
+ body.ensure_predecessors();
+ body
+ })
+}
+
+///////////////////////////////////////////////////////////////////////////
+// BuildMir -- walks a crate, looking for fn items and methods to build MIR from
+
+fn liberated_closure_env_ty(
+ tcx: TyCtxt<'_>,
+ closure_expr_id: hir::HirId,
+ body_id: hir::BodyId,
+) -> Ty<'_> {
+ let closure_ty = tcx.body_tables(body_id).node_type(closure_expr_id);
+
+ let (closure_def_id, closure_substs) = match closure_ty.kind {
+ ty::Closure(closure_def_id, closure_substs) => (closure_def_id, closure_substs),
+ _ => bug!("closure expr does not have closure type: {:?}", closure_ty),
+ };
+
+ let closure_env_ty = tcx.closure_env_ty(closure_def_id, closure_substs).unwrap();
+ tcx.liberate_late_bound_regions(closure_def_id, &closure_env_ty)
+}
+
+#[derive(Debug, PartialEq, Eq)]
+enum BlockFrame {
+ /// Evaluation is currently within a statement.
+ ///
+ /// Examples include:
+ /// 1. `EXPR;`
+ /// 2. `let _ = EXPR;`
+ /// 3. `let x = EXPR;`
+ Statement {
+ /// If true, then statement discards result from evaluating
+ /// the expression (such as examples 1 and 2 above).
+ ignores_expr_result: bool,
+ },
+
+ /// Evaluation is currently within the tail expression of a block.
+ ///
+ /// Example: `{ STMT_1; STMT_2; EXPR }`
+ TailExpr {
+ /// If true, then the surrounding context of the block ignores
+ /// the result of evaluating the block's tail expression.
+ ///
+ /// Example: `let _ = { STMT_1; EXPR };`
+ tail_result_is_ignored: bool,
+ },
+
+ /// Generic mark meaning that the block occurred as a subexpression
+ /// where the result might be used.
+ ///
+ /// Examples: `foo(EXPR)`, `match EXPR { ... }`
+ SubExpr,
+}
+
+impl BlockFrame {
+ fn is_tail_expr(&self) -> bool {
+ match *self {
+ BlockFrame::TailExpr { .. } => true,
+
+ BlockFrame::Statement { .. } | BlockFrame::SubExpr => false,
+ }
+ }
+ fn is_statement(&self) -> bool {
+ match *self {
+ BlockFrame::Statement { .. } => true,
+
+ BlockFrame::TailExpr { .. } | BlockFrame::SubExpr => false,
+ }
+ }
+}
+
+#[derive(Debug)]
+struct BlockContext(Vec<BlockFrame>);
+
+struct Builder<'a, 'tcx> {
+ hir: Cx<'a, 'tcx>,
+ cfg: CFG<'tcx>,
+
+ fn_span: Span,
+ arg_count: usize,
+ generator_kind: Option<GeneratorKind>,
+
+ /// The current set of scopes, updated as we traverse;
+ /// see the `scope` module for more details.
+ scopes: scope::Scopes<'tcx>,
+
+ /// The block-context: each time we build the code within an hair::Block,
+ /// we push a frame here tracking whether we are building a statement or
+ /// if we are pushing the tail expression of the block. This is used to
+ /// embed information in generated temps about whether they were created
+ /// for a block tail expression or not.
+ ///
+ /// It would be great if we could fold this into `self.scopes`
+ /// somehow, but right now I think that is very tightly tied to
+ /// the code generation in ways that we cannot (or should not)
+ /// start just throwing new entries onto that vector in order to
+ /// distinguish the context of EXPR1 from the context of EXPR2 in
+ /// `{ STMTS; EXPR1 } + EXPR2`.
+ block_context: BlockContext,
+
+ /// The current unsafe block in scope, even if it is hidden by
+ /// a `PushUnsafeBlock`.
+ unpushed_unsafe: Safety,
+
+ /// The number of `push_unsafe_block` levels in scope.
+ push_unsafe_count: usize,
+
+ /// The vector of all scopes that we have created thus far;
+ /// we track this for debuginfo later.
+ source_scopes: IndexVec<SourceScope, SourceScopeData>,
+ source_scope: SourceScope,
+
+ /// The guard-context: each time we build the guard expression for
+ /// a match arm, we push onto this stack, and then pop when we
+ /// finish building it.
+ guard_context: Vec<GuardFrame>,
+
+ /// Maps `HirId`s of variable bindings to the `Local`s created for them.
+ /// (A match binding can have two locals; the 2nd is for the arm's guard.)
+ var_indices: HirIdMap<LocalsForNode>,
+ local_decls: IndexVec<Local, LocalDecl<'tcx>>,
+ canonical_user_type_annotations: ty::CanonicalUserTypeAnnotations<'tcx>,
+ upvar_mutbls: Vec<Mutability>,
+ unit_temp: Option<Place<'tcx>>,
+
+ var_debug_info: Vec<VarDebugInfo<'tcx>>,
+
+ /// Cached block with the `RESUME` terminator; this is created
+ /// when first set of cleanups are built.
+ cached_resume_block: Option<BasicBlock>,
+ /// Cached block with the `RETURN` terminator.
+ cached_return_block: Option<BasicBlock>,
+ /// Cached block with the `UNREACHABLE` terminator.
+ cached_unreachable_block: Option<BasicBlock>,
+}
+
+impl<'a, 'tcx> Builder<'a, 'tcx> {
+ fn is_bound_var_in_guard(&self, id: hir::HirId) -> bool {
+ self.guard_context.iter().any(|frame| frame.locals.iter().any(|local| local.id == id))
+ }
+
+ fn var_local_id(&self, id: hir::HirId, for_guard: ForGuard) -> Local {
+ self.var_indices[&id].local_id(for_guard)
+ }
+}
+
+impl BlockContext {
+ fn new() -> Self {
+ BlockContext(vec![])
+ }
+ fn push(&mut self, bf: BlockFrame) {
+ self.0.push(bf);
+ }
+ fn pop(&mut self) -> Option<BlockFrame> {
+ self.0.pop()
+ }
+
+ /// Traverses the frames on the `BlockContext`, searching for either
+ /// the first block-tail expression frame with no intervening
+ /// statement frame.
+ ///
+ /// Notably, this skips over `SubExpr` frames; this method is
+ /// meant to be used in the context of understanding the
+ /// relationship of a temp (created within some complicated
+ /// expression) with its containing expression, and whether the
+ /// value of that *containing expression* (not the temp!) is
+ /// ignored.
+ fn currently_in_block_tail(&self) -> Option<BlockTailInfo> {
+ for bf in self.0.iter().rev() {
+ match bf {
+ BlockFrame::SubExpr => continue,
+ BlockFrame::Statement { .. } => break,
+ &BlockFrame::TailExpr { tail_result_is_ignored } => {
+ return Some(BlockTailInfo { tail_result_is_ignored });
+ }
+ }
+ }
+
+ return None;
+ }
+
+ /// Looks at the topmost frame on the BlockContext and reports
+ /// whether its one that would discard a block tail result.
+ ///
+ /// Unlike `currently_within_ignored_tail_expression`, this does
+ /// *not* skip over `SubExpr` frames: here, we want to know
+ /// whether the block result itself is discarded.
+ fn currently_ignores_tail_results(&self) -> bool {
+ match self.0.last() {
+ // no context: conservatively assume result is read
+ None => false,
+
+ // sub-expression: block result feeds into some computation
+ Some(BlockFrame::SubExpr) => false,
+
+ // otherwise: use accumulated is_ignored state.
+ Some(BlockFrame::TailExpr { tail_result_is_ignored: ignored })
+ | Some(BlockFrame::Statement { ignores_expr_result: ignored }) => *ignored,
+ }
+ }
+}
+
+#[derive(Debug)]
+enum LocalsForNode {
+ /// In the usual case, a `HirId` for an identifier maps to at most
+ /// one `Local` declaration.
+ One(Local),
+
+ /// The exceptional case is identifiers in a match arm's pattern
+ /// that are referenced in a guard of that match arm. For these,
+ /// we have `2` Locals.
+ ///
+ /// * `for_arm_body` is the Local used in the arm body (which is
+ /// just like the `One` case above),
+ ///
+ /// * `ref_for_guard` is the Local used in the arm's guard (which
+ /// is a reference to a temp that is an alias of
+ /// `for_arm_body`).
+ ForGuard { ref_for_guard: Local, for_arm_body: Local },
+}
+
+#[derive(Debug)]
+struct GuardFrameLocal {
+ id: hir::HirId,
+}
+
+impl GuardFrameLocal {
+ fn new(id: hir::HirId, _binding_mode: BindingMode) -> Self {
+ GuardFrameLocal { id: id }
+ }
+}
+
+#[derive(Debug)]
+struct GuardFrame {
+ /// These are the id's of names that are bound by patterns of the
+ /// arm of *this* guard.
+ ///
+ /// (Frames higher up the stack will have the id's bound in arms
+ /// further out, such as in a case like:
+ ///
+ /// match E1 {
+ /// P1(id1) if (... (match E2 { P2(id2) if ... => B2 })) => B1,
+ /// }
+ ///
+ /// here, when building for FIXME.
+ locals: Vec<GuardFrameLocal>,
+}
+
+/// `ForGuard` indicates whether we are talking about:
+/// 1. The variable for use outside of guard expressions, or
+/// 2. The temp that holds reference to (1.), which is actually what the
+/// guard expressions see.
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+enum ForGuard {
+ RefWithinGuard,
+ OutsideGuard,
+}
+
+impl LocalsForNode {
+ fn local_id(&self, for_guard: ForGuard) -> Local {
+ match (self, for_guard) {
+ (&LocalsForNode::One(local_id), ForGuard::OutsideGuard)
+ | (
+ &LocalsForNode::ForGuard { ref_for_guard: local_id, .. },
+ ForGuard::RefWithinGuard,
+ )
+ | (&LocalsForNode::ForGuard { for_arm_body: local_id, .. }, ForGuard::OutsideGuard) => {
+ local_id
+ }
+
+ (&LocalsForNode::One(_), ForGuard::RefWithinGuard) => {
+ bug!("anything with one local should never be within a guard.")
+ }
+ }
+ }
+}
+
+struct CFG<'tcx> {
+ basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
+}
+
+rustc_index::newtype_index! {
+ struct ScopeId { .. }
+}
+
+///////////////////////////////////////////////////////////////////////////
+/// The `BlockAnd` "monad" packages up the new basic block along with a
+/// produced value (sometimes just unit, of course). The `unpack!`
+/// macro (and methods below) makes working with `BlockAnd` much more
+/// convenient.
+
+#[must_use = "if you don't use one of these results, you're leaving a dangling edge"]
+struct BlockAnd<T>(BasicBlock, T);
+
+trait BlockAndExtension {
+ fn and<T>(self, v: T) -> BlockAnd<T>;
+ fn unit(self) -> BlockAnd<()>;
+}
+
+impl BlockAndExtension for BasicBlock {
+ fn and<T>(self, v: T) -> BlockAnd<T> {
+ BlockAnd(self, v)
+ }
+
+ fn unit(self) -> BlockAnd<()> {
+ BlockAnd(self, ())
+ }
+}
+
+/// Update a block pointer and return the value.
+/// Use it like `let x = unpack!(block = self.foo(block, foo))`.
+macro_rules! unpack {
+ ($x:ident = $c:expr) => {{
+ let BlockAnd(b, v) = $c;
+ $x = b;
+ v
+ }};
+
+ ($c:expr) => {{
+ let BlockAnd(b, ()) = $c;
+ b
+ }};
+}
+
+fn should_abort_on_panic(tcx: TyCtxt<'_>, fn_def_id: DefId, _abi: Abi) -> bool {
+ // Validate `#[unwind]` syntax regardless of platform-specific panic strategy.
+ let attrs = &tcx.get_attrs(fn_def_id);
+ let unwind_attr = attr::find_unwind_attr(Some(tcx.sess.diagnostic()), attrs);
+
+ // We never unwind, so it's not relevant to stop an unwind.
+ if tcx.sess.panic_strategy() != PanicStrategy::Unwind {
+ return false;
+ }
+
+ // We cannot add landing pads, so don't add one.
+ if tcx.sess.no_landing_pads() {
+ return false;
+ }
+
+ // This is a special case: some functions have a C abi but are meant to
+ // unwind anyway. Don't stop them.
+ match unwind_attr {
+ None => false, // FIXME(#58794); should be `!(abi == Abi::Rust || abi == Abi::RustCall)`
+ Some(UnwindAttr::Allowed) => false,
+ Some(UnwindAttr::Aborts) => true,
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////
+/// the main entry point for building MIR for a function
+
+struct ArgInfo<'tcx>(
+ Ty<'tcx>,
+ Option<Span>,
+ Option<&'tcx hir::Param<'tcx>>,
+ Option<ImplicitSelfKind>,
+);
+
+fn construct_fn<'a, 'tcx, A>(
+ hir: Cx<'a, 'tcx>,
+ fn_id: hir::HirId,
+ arguments: A,
+ safety: Safety,
+ abi: Abi,
+ return_ty: Ty<'tcx>,
+ return_ty_span: Span,
+ body: &'tcx hir::Body<'tcx>,
+) -> Body<'tcx>
+where
+ A: Iterator<Item = ArgInfo<'tcx>>,
+{
+ let arguments: Vec<_> = arguments.collect();
+
+ let tcx = hir.tcx();
+ let tcx_hir = tcx.hir();
+ let span = tcx_hir.span(fn_id);
+
+ let fn_def_id = tcx_hir.local_def_id(fn_id);
+
+ let mut builder = Builder::new(
+ hir,
+ span,
+ arguments.len(),
+ safety,
+ return_ty,
+ return_ty_span,
+ body.generator_kind,
+ );
+
+ let call_site_scope =
+ region::Scope { id: body.value.hir_id.local_id, data: region::ScopeData::CallSite };
+ let arg_scope =
+ region::Scope { id: body.value.hir_id.local_id, data: region::ScopeData::Arguments };
+ let mut block = START_BLOCK;
+ let source_info = builder.source_info(span);
+ let call_site_s = (call_site_scope, source_info);
+ unpack!(
+ block = builder.in_scope(call_site_s, LintLevel::Inherited, |builder| {
+ if should_abort_on_panic(tcx, fn_def_id, abi) {
+ builder.schedule_abort();
+ }
+
+ let arg_scope_s = (arg_scope, source_info);
+ // `return_block` is called when we evaluate a `return` expression, so
+ // we just use `START_BLOCK` here.
+ unpack!(
+ block = builder.in_breakable_scope(
+ None,
+ START_BLOCK,
+ Place::return_place(),
+ |builder| {
+ builder.in_scope(arg_scope_s, LintLevel::Inherited, |builder| {
+ builder.args_and_body(
+ block,
+ fn_def_id,
+ &arguments,
+ arg_scope,
+ &body.value,
+ )
+ })
+ },
+ )
+ );
+ // Attribute epilogue to function's closing brace
+ let fn_end = span.shrink_to_hi();
+ let source_info = builder.source_info(fn_end);
+ let return_block = builder.return_block();
+ builder.cfg.goto(block, source_info, return_block);
+ builder.cfg.terminate(return_block, source_info, TerminatorKind::Return);
+ // Attribute any unreachable codepaths to the function's closing brace
+ if let Some(unreachable_block) = builder.cached_unreachable_block {
+ builder.cfg.terminate(unreachable_block, source_info, TerminatorKind::Unreachable);
+ }
+ return_block.unit()
+ })
+ );
+ assert_eq!(block, builder.return_block());
+
+ let mut spread_arg = None;
+ if abi == Abi::RustCall {
+ // RustCall pseudo-ABI untuples the last argument.
+ spread_arg = Some(Local::new(arguments.len()));
+ }
+ debug!("fn_id {:?} has attrs {:?}", fn_def_id, tcx.get_attrs(fn_def_id));
+
+ let mut body = builder.finish();
+ body.spread_arg = spread_arg;
+ body
+}
+
+fn construct_const<'a, 'tcx>(
+ hir: Cx<'a, 'tcx>,
+ body_id: hir::BodyId,
+ const_ty: Ty<'tcx>,
+ const_ty_span: Span,
+) -> Body<'tcx> {
+ let tcx = hir.tcx();
+ let owner_id = tcx.hir().body_owner(body_id);
+ let span = tcx.hir().span(owner_id);
+ let mut builder = Builder::new(hir, span, 0, Safety::Safe, const_ty, const_ty_span, None);
+
+ let mut block = START_BLOCK;
+ let ast_expr = &tcx.hir().body(body_id).value;
+ let expr = builder.hir.mirror(ast_expr);
+ unpack!(block = builder.into_expr(&Place::return_place(), block, expr));
+
+ let source_info = builder.source_info(span);
+ builder.cfg.terminate(block, source_info, TerminatorKind::Return);
+
+ // Constants can't `return` so a return block should not be created.
+ assert_eq!(builder.cached_return_block, None);
+
+ // Constants may be match expressions in which case an unreachable block may
+ // be created, so terminate it properly.
+ if let Some(unreachable_block) = builder.cached_unreachable_block {
+ builder.cfg.terminate(unreachable_block, source_info, TerminatorKind::Unreachable);
+ }
+
+ builder.finish()
+}
+
+fn construct_error<'a, 'tcx>(hir: Cx<'a, 'tcx>, body_id: hir::BodyId) -> Body<'tcx> {
+ let owner_id = hir.tcx().hir().body_owner(body_id);
+ let span = hir.tcx().hir().span(owner_id);
+ let ty = hir.tcx().types.err;
+ let mut builder = Builder::new(hir, span, 0, Safety::Safe, ty, span, None);
+ let source_info = builder.source_info(span);
+ builder.cfg.terminate(START_BLOCK, source_info, TerminatorKind::Unreachable);
+ builder.finish()
+}
+
+impl<'a, 'tcx> Builder<'a, 'tcx> {
+ fn new(
+ hir: Cx<'a, 'tcx>,
+ span: Span,
+ arg_count: usize,
+ safety: Safety,
+ return_ty: Ty<'tcx>,
+ return_span: Span,
+ generator_kind: Option<GeneratorKind>,
+ ) -> Builder<'a, 'tcx> {
+ let lint_level = LintLevel::Explicit(hir.root_lint_level);
+ let mut builder = Builder {
+ hir,
+ cfg: CFG { basic_blocks: IndexVec::new() },
+ fn_span: span,
+ arg_count,
+ generator_kind,
+ scopes: Default::default(),
+ block_context: BlockContext::new(),
+ source_scopes: IndexVec::new(),
+ source_scope: OUTERMOST_SOURCE_SCOPE,
+ guard_context: vec![],
+ push_unsafe_count: 0,
+ unpushed_unsafe: safety,
+ local_decls: IndexVec::from_elem_n(
+ LocalDecl::new_return_place(return_ty, return_span),
+ 1,
+ ),
+ canonical_user_type_annotations: IndexVec::new(),
+ upvar_mutbls: vec![],
+ var_indices: Default::default(),
+ unit_temp: None,
+ var_debug_info: vec![],
+ cached_resume_block: None,
+ cached_return_block: None,
+ cached_unreachable_block: None,
+ };
+
+ assert_eq!(builder.cfg.start_new_block(), START_BLOCK);
+ assert_eq!(
+ builder.new_source_scope(span, lint_level, Some(safety)),
+ OUTERMOST_SOURCE_SCOPE
+ );
+ builder.source_scopes[OUTERMOST_SOURCE_SCOPE].parent_scope = None;
+
+ builder
+ }
+
+ fn finish(self) -> Body<'tcx> {
+ for (index, block) in self.cfg.basic_blocks.iter().enumerate() {
+ if block.terminator.is_none() {
+ span_bug!(self.fn_span, "no terminator on block {:?}", index);
+ }
+ }
+
+ Body::new(
+ self.cfg.basic_blocks,
+ self.source_scopes,
+ self.local_decls,
+ self.canonical_user_type_annotations,
+ self.arg_count,
+ self.var_debug_info,
+ self.fn_span,
+ self.hir.control_flow_destroyed(),
+ self.generator_kind,
+ )
+ }
+
+ fn args_and_body(
+ &mut self,
+ mut block: BasicBlock,
+ fn_def_id: DefId,
+ arguments: &[ArgInfo<'tcx>],
+ argument_scope: region::Scope,
+ ast_body: &'tcx hir::Expr<'tcx>,
+ ) -> BlockAnd<()> {
+ // Allocate locals for the function arguments
+ for &ArgInfo(ty, _, arg_opt, _) in arguments.iter() {
+ let source_info = SourceInfo {
+ scope: OUTERMOST_SOURCE_SCOPE,
+ span: arg_opt.map_or(self.fn_span, |arg| arg.pat.span),
+ };
+ let arg_local = self.local_decls.push(LocalDecl {
+ mutability: Mutability::Mut,
+ ty,
+ user_ty: UserTypeProjections::none(),
+ source_info,
+ internal: false,
+ local_info: LocalInfo::Other,
+ is_block_tail: None,
+ });
+
+ // If this is a simple binding pattern, give debuginfo a nice name.
+ if let Some(arg) = arg_opt {
+ if let Some(ident) = arg.pat.simple_ident() {
+ self.var_debug_info.push(VarDebugInfo {
+ name: ident.name,
+ source_info,
+ place: arg_local.into(),
+ });
+ }
+ }
+ }
+
+ let tcx = self.hir.tcx();
+ let tcx_hir = tcx.hir();
+ let hir_tables = self.hir.tables();
+
+ // In analyze_closure() in upvar.rs we gathered a list of upvars used by a
+ // closure and we stored in a map called upvar_list in TypeckTables indexed
+ // with the closure's DefId. Here, we run through that vec of UpvarIds for
+ // the given closure and use the necessary information to create upvar
+ // debuginfo and to fill `self.upvar_mutbls`.
+ if let Some(upvars) = hir_tables.upvar_list.get(&fn_def_id) {
+ let closure_env_arg = Local::new(1);
+ let mut closure_env_projs = vec![];
+ let mut closure_ty = self.local_decls[closure_env_arg].ty;
+ if let ty::Ref(_, ty, _) = closure_ty.kind {
+ closure_env_projs.push(ProjectionElem::Deref);
+ closure_ty = ty;
+ }
+ let (def_id, upvar_substs) = match closure_ty.kind {
+ ty::Closure(def_id, substs) => (def_id, ty::UpvarSubsts::Closure(substs)),
+ ty::Generator(def_id, substs, _) => (def_id, ty::UpvarSubsts::Generator(substs)),
+ _ => span_bug!(self.fn_span, "upvars with non-closure env ty {:?}", closure_ty),
+ };
+ let upvar_tys = upvar_substs.upvar_tys(def_id, tcx);
+ let upvars_with_tys = upvars.iter().zip(upvar_tys);
+ self.upvar_mutbls = upvars_with_tys
+ .enumerate()
+ .map(|(i, ((&var_id, &upvar_id), ty))| {
+ let capture = hir_tables.upvar_capture(upvar_id);
+
+ let mut mutability = Mutability::Not;
+ let mut name = kw::Invalid;
+ if let Some(Node::Binding(pat)) = tcx_hir.find(var_id) {
+ if let hir::PatKind::Binding(_, _, ident, _) = pat.kind {
+ name = ident.name;
+ match hir_tables.extract_binding_mode(tcx.sess, pat.hir_id, pat.span) {
+ Some(ty::BindByValue(hir::Mutability::Mut)) => {
+ mutability = Mutability::Mut;
+ }
+ Some(_) => mutability = Mutability::Not,
+ _ => {}
+ }
+ }
+ }
+
+ let mut projs = closure_env_projs.clone();
+ projs.push(ProjectionElem::Field(Field::new(i), ty));
+ match capture {
+ ty::UpvarCapture::ByValue => {}
+ ty::UpvarCapture::ByRef(..) => {
+ projs.push(ProjectionElem::Deref);
+ }
+ };
+
+ self.var_debug_info.push(VarDebugInfo {
+ name,
+ source_info: SourceInfo {
+ scope: OUTERMOST_SOURCE_SCOPE,
+ span: tcx_hir.span(var_id),
+ },
+ place: Place {
+ local: closure_env_arg.into(),
+ projection: tcx.intern_place_elems(&projs),
+ },
+ });
+
+ mutability
+ })
+ .collect();
+ }
+
+ let mut scope = None;
+ // Bind the argument patterns
+ for (index, arg_info) in arguments.iter().enumerate() {
+ // Function arguments always get the first Local indices after the return place
+ let local = Local::new(index + 1);
+ let place = Place::from(local);
+ let &ArgInfo(_, opt_ty_info, arg_opt, ref self_binding) = arg_info;
+
+ // Make sure we drop (parts of) the argument even when not matched on.
+ self.schedule_drop(
+ arg_opt.as_ref().map_or(ast_body.span, |arg| arg.pat.span),
+ argument_scope,
+ local,
+ DropKind::Value,
+ );
+
+ if let Some(arg) = arg_opt {
+ let pattern = self.hir.pattern_from_hir(&arg.pat);
+ let original_source_scope = self.source_scope;
+ let span = pattern.span;
+ self.set_correct_source_scope_for_arg(arg.hir_id, original_source_scope, span);
+ match *pattern.kind {
+ // Don't introduce extra copies for simple bindings
+ PatKind::Binding {
+ mutability,
+ var,
+ mode: BindingMode::ByValue,
+ subpattern: None,
+ ..
+ } => {
+ self.local_decls[local].mutability = mutability;
+ self.local_decls[local].source_info.scope = self.source_scope;
+ self.local_decls[local].local_info = if let Some(kind) = self_binding {
+ LocalInfo::User(ClearCrossCrate::Set(BindingForm::ImplicitSelf(*kind)))
+ } else {
+ let binding_mode = ty::BindingMode::BindByValue(mutability.into());
+ LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(
+ VarBindingForm {
+ binding_mode,
+ opt_ty_info,
+ opt_match_place: Some((Some(place.clone()), span)),
+ pat_span: span,
+ },
+ )))
+ };
+ self.var_indices.insert(var, LocalsForNode::One(local));
+ }
+ _ => {
+ scope = self.declare_bindings(
+ scope,
+ ast_body.span,
+ &pattern,
+ matches::ArmHasGuard(false),
+ Some((Some(&place), span)),
+ );
+ unpack!(block = self.place_into_pattern(block, pattern, &place, false));
+ }
+ }
+ self.source_scope = original_source_scope;
+ }
+ }
+
+ // Enter the argument pattern bindings source scope, if it exists.
+ if let Some(source_scope) = scope {
+ self.source_scope = source_scope;
+ }
+
+ let body = self.hir.mirror(ast_body);
+ self.into(&Place::return_place(), block, body)
+ }
+
+ fn set_correct_source_scope_for_arg(
+ &mut self,
+ arg_hir_id: hir::HirId,
+ original_source_scope: SourceScope,
+ pattern_span: Span,
+ ) {
+ let tcx = self.hir.tcx();
+ let current_root = tcx.maybe_lint_level_root_bounded(arg_hir_id, self.hir.root_lint_level);
+ 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.root_lint_level,
+ );
+ if current_root != parent_root {
+ self.source_scope =
+ self.new_source_scope(pattern_span, LintLevel::Explicit(current_root), None);
+ }
+ }
+
+ fn get_unit_temp(&mut self) -> Place<'tcx> {
+ match self.unit_temp {
+ Some(ref tmp) => tmp.clone(),
+ None => {
+ let ty = self.hir.unit_ty();
+ let fn_span = self.fn_span;
+ let tmp = self.temp(ty, fn_span);
+ self.unit_temp = Some(tmp.clone());
+ tmp
+ }
+ }
+ }
+
+ fn return_block(&mut self) -> BasicBlock {
+ match self.cached_return_block {
+ Some(rb) => rb,
+ None => {
+ let rb = self.cfg.start_new_block();
+ self.cached_return_block = Some(rb);
+ rb
+ }
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Builder methods are broken up into modules, depending on what kind
+// of thing is being lowered. Note that they use the `unpack` macro
+// above extensively.
+
+mod block;
+mod cfg;
+mod expr;
+mod into;
+mod matches;
+mod misc;
+mod scope;
--- /dev/null
+/*!
+Managing the scope stack. The scopes are tied to lexical scopes, so as
+we descend the HAIR, we push a scope on the stack, build its
+contents, and then pop it off. Every scope is named by a
+`region::Scope`.
+
+### SEME Regions
+
+When pushing a new scope, we record the current point in the graph (a
+basic block); this marks the entry to the scope. We then generate more
+stuff in the control-flow graph. Whenever the scope is exited, either
+via a `break` or `return` or just by fallthrough, that marks an exit
+from the scope. Each lexical scope thus corresponds to a single-entry,
+multiple-exit (SEME) region in the control-flow graph.
+
+For now, we keep a mapping from each `region::Scope` to its
+corresponding SEME region for later reference (see caveat in next
+paragraph). This is because region scopes are tied to
+them. Eventually, when we shift to non-lexical lifetimes, there should
+be no need to remember this mapping.
+
+### Not so SEME Regions
+
+In the course of building matches, it sometimes happens that certain code
+(namely guards) gets executed multiple times. This means that the scope lexical
+scope may in fact correspond to multiple, disjoint SEME regions. So in fact our
+mapping is from one scope to a vector of SEME regions.
+
+Also in matches, the scopes assigned to arms are not even SEME regions! Each
+arm has a single region with one entry for each pattern. We manually
+manipulate the scheduled drops in this scope to avoid dropping things multiple
+times, although drop elaboration would clean this up for value drops.
+
+### Drops
+
+The primary purpose for scopes is to insert drops: while building
+the contents, we also accumulate places that need to be dropped upon
+exit from each scope. This is done by calling `schedule_drop`. Once a
+drop is scheduled, whenever we branch out we will insert drops of all
+those places onto the outgoing edge. Note that we don't know the full
+set of scheduled drops up front, and so whenever we exit from the
+scope we only drop the values scheduled thus far. For example, consider
+the scope S corresponding to this loop:
+
+```
+# let cond = true;
+loop {
+ let x = ..;
+ if cond { break; }
+ let y = ..;
+}
+```
+
+When processing the `let x`, we will add one drop to the scope for
+`x`. The break will then insert a drop for `x`. When we process `let
+y`, we will add another drop (in fact, to a subscope, but let's ignore
+that for now); any later drops would also drop `y`.
+
+### Early exit
+
+There are numerous "normal" ways to early exit a scope: `break`,
+`continue`, `return` (panics are handled separately). Whenever an
+early exit occurs, the method `exit_scope` is called. It is given the
+current point in execution where the early exit occurs, as well as the
+scope you want to branch to (note that all early exits from to some
+other enclosing scope). `exit_scope` will record this exit point and
+also add all drops.
+
+Panics are handled in a similar fashion, except that a panic always
+returns out to the `DIVERGE_BLOCK`. To trigger a panic, simply call
+`panic(p)` with the current point `p`. Or else you can call
+`diverge_cleanup`, which will produce a block that you can branch to
+which does the appropriate cleanup and then diverges. `panic(p)`
+simply calls `diverge_cleanup()` and adds an edge from `p` to the
+result.
+
+### Loop scopes
+
+In addition to the normal scope stack, we track a loop scope stack
+that contains only loops. It tracks where a `break` and `continue`
+should go to.
+
+*/
+
+use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder, CFG};
+use crate::hair::{Expr, ExprRef, LintLevel};
+use rustc::middle::region;
+use rustc::mir::*;
+use rustc_data_structures::fx::FxHashMap;
+use rustc_hir as hir;
+use rustc_hir::GeneratorKind;
+use rustc_span::{Span, DUMMY_SP};
+use std::collections::hash_map::Entry;
+use std::mem;
+
+#[derive(Debug)]
+struct Scope {
+ /// The source scope this scope was created in.
+ source_scope: SourceScope,
+
+ /// the region span of this scope within source code.
+ region_scope: region::Scope,
+
+ /// the span of that region_scope
+ region_scope_span: Span,
+
+ /// set of places to drop when exiting this scope. This starts
+ /// out empty but grows as variables are declared during the
+ /// building process. This is a stack, so we always drop from the
+ /// end of the vector (top of the stack) first.
+ drops: Vec<DropData>,
+
+ moved_locals: Vec<Local>,
+
+ /// The cache for drop chain on “normal” exit into a particular BasicBlock.
+ cached_exits: FxHashMap<(BasicBlock, region::Scope), BasicBlock>,
+
+ /// The cache for drop chain on "generator drop" exit.
+ cached_generator_drop: Option<BasicBlock>,
+
+ /// The cache for drop chain on "unwind" exit.
+ cached_unwind: CachedBlock,
+}
+
+#[derive(Debug, Default)]
+crate struct Scopes<'tcx> {
+ scopes: Vec<Scope>,
+ /// The current set of breakable scopes. See module comment for more details.
+ breakable_scopes: Vec<BreakableScope<'tcx>>,
+}
+
+#[derive(Debug)]
+struct DropData {
+ /// span where drop obligation was incurred (typically where place was declared)
+ span: Span,
+
+ /// local to drop
+ local: Local,
+
+ /// Whether this is a value Drop or a StorageDead.
+ kind: DropKind,
+
+ /// The cached blocks for unwinds.
+ cached_block: CachedBlock,
+}
+
+#[derive(Debug, Default, Clone, Copy)]
+struct CachedBlock {
+ /// The cached block for the cleanups-on-diverge path. This block
+ /// contains code to run the current drop and all the preceding
+ /// drops (i.e., those having lower index in Drop’s Scope drop
+ /// array)
+ unwind: Option<BasicBlock>,
+
+ /// The cached block for unwinds during cleanups-on-generator-drop path
+ ///
+ /// This is split from the standard unwind path here to prevent drop
+ /// elaboration from creating drop flags that would have to be captured
+ /// by the generator. I'm not sure how important this optimization is,
+ /// but it is here.
+ generator_drop: Option<BasicBlock>,
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub(crate) enum DropKind {
+ Value,
+ Storage,
+}
+
+#[derive(Clone, Debug)]
+struct BreakableScope<'tcx> {
+ /// Region scope of the loop
+ region_scope: region::Scope,
+ /// Where the body of the loop begins. `None` if block
+ continue_block: Option<BasicBlock>,
+ /// Block to branch into when the loop or block terminates (either by being
+ /// `break`-en out from, or by having its condition to become false)
+ break_block: BasicBlock,
+ /// The destination of the loop/block expression itself (i.e., where to put
+ /// the result of a `break` expression)
+ break_destination: Place<'tcx>,
+}
+
+/// The target of an expression that breaks out of a scope
+#[derive(Clone, Copy, Debug)]
+crate enum BreakableTarget {
+ Continue(region::Scope),
+ Break(region::Scope),
+ Return,
+}
+
+impl CachedBlock {
+ fn invalidate(&mut self) {
+ *self = CachedBlock::default();
+ }
+
+ fn get(&self, generator_drop: bool) -> Option<BasicBlock> {
+ if generator_drop { self.generator_drop } else { self.unwind }
+ }
+
+ fn ref_mut(&mut self, generator_drop: bool) -> &mut Option<BasicBlock> {
+ if generator_drop { &mut self.generator_drop } else { &mut self.unwind }
+ }
+}
+
+impl Scope {
+ /// Invalidates all the cached blocks in the scope.
+ ///
+ /// Should always be run for all inner scopes when a drop is pushed into some scope enclosing a
+ /// larger extent of code.
+ ///
+ /// `storage_only` controls whether to invalidate only drop paths that run `StorageDead`.
+ /// `this_scope_only` controls whether to invalidate only drop paths that refer to the current
+ /// top-of-scope (as opposed to dependent scopes).
+ fn invalidate_cache(
+ &mut self,
+ storage_only: bool,
+ generator_kind: Option<GeneratorKind>,
+ this_scope_only: bool,
+ ) {
+ // FIXME: maybe do shared caching of `cached_exits` etc. to handle functions
+ // with lots of `try!`?
+
+ // cached exits drop storage and refer to the top-of-scope
+ self.cached_exits.clear();
+
+ // the current generator drop and unwind refer to top-of-scope
+ self.cached_generator_drop = None;
+
+ let ignore_unwinds = storage_only && generator_kind.is_none();
+ if !ignore_unwinds {
+ self.cached_unwind.invalidate();
+ }
+
+ if !ignore_unwinds && !this_scope_only {
+ for drop_data in &mut self.drops {
+ drop_data.cached_block.invalidate();
+ }
+ }
+ }
+
+ /// Given a span and this scope's source scope, make a SourceInfo.
+ fn source_info(&self, span: Span) -> SourceInfo {
+ SourceInfo { span, scope: self.source_scope }
+ }
+
+ /// Whether there's anything to do for the cleanup path, that is,
+ /// when unwinding through this scope. This includes destructors,
+ /// but not StorageDead statements, which don't get emitted at all
+ /// for unwinding, for several reasons:
+ /// * clang doesn't emit llvm.lifetime.end for C++ unwinding
+ /// * LLVM's memory dependency analysis can't handle it atm
+ /// * polluting the cleanup MIR with StorageDead creates
+ /// landing pads even though there's no actual destructors
+ /// * freeing up stack space has no effect during unwinding
+ /// Note that for generators we do emit StorageDeads, for the
+ /// use of optimizations in the MIR generator transform.
+ fn needs_cleanup(&self) -> bool {
+ self.drops.iter().any(|drop| match drop.kind {
+ DropKind::Value => true,
+ DropKind::Storage => false,
+ })
+ }
+}
+
+impl<'tcx> Scopes<'tcx> {
+ fn len(&self) -> usize {
+ self.scopes.len()
+ }
+
+ fn push_scope(&mut self, region_scope: (region::Scope, SourceInfo), vis_scope: SourceScope) {
+ debug!("push_scope({:?})", region_scope);
+ self.scopes.push(Scope {
+ source_scope: vis_scope,
+ region_scope: region_scope.0,
+ region_scope_span: region_scope.1.span,
+ drops: vec![],
+ moved_locals: vec![],
+ cached_generator_drop: None,
+ cached_exits: Default::default(),
+ cached_unwind: CachedBlock::default(),
+ });
+ }
+
+ fn pop_scope(
+ &mut self,
+ region_scope: (region::Scope, SourceInfo),
+ ) -> (Scope, Option<BasicBlock>) {
+ let scope = self.scopes.pop().unwrap();
+ assert_eq!(scope.region_scope, region_scope.0);
+ let unwind_to =
+ self.scopes.last().and_then(|next_scope| next_scope.cached_unwind.get(false));
+ (scope, unwind_to)
+ }
+
+ fn may_panic(&self, scope_count: usize) -> bool {
+ let len = self.len();
+ self.scopes[(len - scope_count)..].iter().any(|s| s.needs_cleanup())
+ }
+
+ /// Finds the breakable scope for a given label. This is used for
+ /// resolving `return`, `break` and `continue`.
+ fn find_breakable_scope(
+ &self,
+ span: Span,
+ target: BreakableTarget,
+ ) -> (BasicBlock, region::Scope, Option<Place<'tcx>>) {
+ let get_scope = |scope: region::Scope| {
+ // find the loop-scope by its `region::Scope`.
+ self.breakable_scopes
+ .iter()
+ .rfind(|breakable_scope| breakable_scope.region_scope == scope)
+ .unwrap_or_else(|| span_bug!(span, "no enclosing breakable scope found"))
+ };
+ match target {
+ BreakableTarget::Return => {
+ let scope = &self.breakable_scopes[0];
+ if scope.break_destination != Place::return_place() {
+ span_bug!(span, "`return` in item with no return scope");
+ }
+ (scope.break_block, scope.region_scope, Some(scope.break_destination.clone()))
+ }
+ BreakableTarget::Break(scope) => {
+ let scope = get_scope(scope);
+ (scope.break_block, scope.region_scope, Some(scope.break_destination.clone()))
+ }
+ BreakableTarget::Continue(scope) => {
+ let scope = get_scope(scope);
+ let continue_block = scope
+ .continue_block
+ .unwrap_or_else(|| span_bug!(span, "missing `continue` block"));
+ (continue_block, scope.region_scope, None)
+ }
+ }
+ }
+
+ fn num_scopes_above(&self, region_scope: region::Scope, span: Span) -> usize {
+ let scope_count = self
+ .scopes
+ .iter()
+ .rev()
+ .position(|scope| scope.region_scope == region_scope)
+ .unwrap_or_else(|| span_bug!(span, "region_scope {:?} does not enclose", region_scope));
+ let len = self.len();
+ assert!(scope_count < len, "should not use `exit_scope` to pop ALL scopes");
+ scope_count
+ }
+
+ fn iter_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut Scope> + '_ {
+ self.scopes.iter_mut().rev()
+ }
+
+ fn top_scopes(&mut self, count: usize) -> impl DoubleEndedIterator<Item = &mut Scope> + '_ {
+ let len = self.len();
+ self.scopes[len - count..].iter_mut()
+ }
+
+ /// Returns the topmost active scope, which is known to be alive until
+ /// the next scope expression.
+ pub(super) fn topmost(&self) -> region::Scope {
+ self.scopes.last().expect("topmost_scope: no scopes present").region_scope
+ }
+
+ fn source_info(&self, index: usize, span: Span) -> SourceInfo {
+ self.scopes[self.len() - index].source_info(span)
+ }
+}
+
+impl<'a, 'tcx> Builder<'a, 'tcx> {
+ // Adding and removing scopes
+ // ==========================
+ // Start a breakable scope, which tracks where `continue`, `break` and
+ // `return` should branch to.
+ crate fn in_breakable_scope<F, R>(
+ &mut self,
+ loop_block: Option<BasicBlock>,
+ break_block: BasicBlock,
+ break_destination: Place<'tcx>,
+ f: F,
+ ) -> R
+ where
+ F: FnOnce(&mut Builder<'a, 'tcx>) -> R,
+ {
+ let region_scope = self.scopes.topmost();
+ let scope = BreakableScope {
+ region_scope,
+ continue_block: loop_block,
+ break_block,
+ break_destination,
+ };
+ self.scopes.breakable_scopes.push(scope);
+ let res = f(self);
+ let breakable_scope = self.scopes.breakable_scopes.pop().unwrap();
+ assert!(breakable_scope.region_scope == region_scope);
+ res
+ }
+
+ crate fn in_opt_scope<F, R>(
+ &mut self,
+ opt_scope: Option<(region::Scope, SourceInfo)>,
+ f: F,
+ ) -> BlockAnd<R>
+ where
+ F: FnOnce(&mut Builder<'a, 'tcx>) -> BlockAnd<R>,
+ {
+ debug!("in_opt_scope(opt_scope={:?})", opt_scope);
+ if let Some(region_scope) = opt_scope {
+ self.push_scope(region_scope);
+ }
+ let mut block;
+ let rv = unpack!(block = f(self));
+ if let Some(region_scope) = opt_scope {
+ unpack!(block = self.pop_scope(region_scope, block));
+ }
+ debug!("in_scope: exiting opt_scope={:?} block={:?}", opt_scope, block);
+ block.and(rv)
+ }
+
+ /// Convenience wrapper that pushes a scope and then executes `f`
+ /// to build its contents, popping the scope afterwards.
+ crate fn in_scope<F, R>(
+ &mut self,
+ region_scope: (region::Scope, SourceInfo),
+ lint_level: LintLevel,
+ f: F,
+ ) -> BlockAnd<R>
+ where
+ F: FnOnce(&mut Builder<'a, 'tcx>) -> BlockAnd<R>,
+ {
+ debug!("in_scope(region_scope={:?})", region_scope);
+ let source_scope = self.source_scope;
+ let tcx = self.hir.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 dependences 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.root_lint_level,
+ );
+ let current_root =
+ tcx.maybe_lint_level_root_bounded(current_hir_id, self.hir.root_lint_level);
+
+ if parent_root != current_root {
+ self.source_scope = self.new_source_scope(
+ region_scope.1.span,
+ LintLevel::Explicit(current_root),
+ None,
+ );
+ }
+ }
+ self.push_scope(region_scope);
+ let mut block;
+ let rv = unpack!(block = f(self));
+ unpack!(block = self.pop_scope(region_scope, block));
+ self.source_scope = source_scope;
+ debug!("in_scope: exiting region_scope={:?} block={:?}", region_scope, block);
+ block.and(rv)
+ }
+
+ /// Push a scope onto the stack. You can then build code in this
+ /// scope and call `pop_scope` afterwards. Note that these two
+ /// calls must be paired; using `in_scope` as a convenience
+ /// wrapper maybe preferable.
+ crate fn push_scope(&mut self, region_scope: (region::Scope, SourceInfo)) {
+ self.scopes.push_scope(region_scope, self.source_scope);
+ }
+
+ /// Pops a scope, which should have region scope `region_scope`,
+ /// adding any drops onto the end of `block` that are needed.
+ /// This must match 1-to-1 with `push_scope`.
+ crate fn pop_scope(
+ &mut self,
+ region_scope: (region::Scope, SourceInfo),
+ mut block: BasicBlock,
+ ) -> BlockAnd<()> {
+ debug!("pop_scope({:?}, {:?})", region_scope, block);
+ // If we are emitting a `drop` statement, we need to have the cached
+ // diverge cleanup pads ready in case that drop panics.
+ if self.scopes.may_panic(1) {
+ self.diverge_cleanup();
+ }
+ let (scope, unwind_to) = self.scopes.pop_scope(region_scope);
+ let unwind_to = unwind_to.unwrap_or_else(|| self.resume_block());
+
+ unpack!(
+ block = build_scope_drops(
+ &mut self.cfg,
+ self.generator_kind,
+ &scope,
+ block,
+ unwind_to,
+ self.arg_count,
+ false, // not generator
+ false, // not unwind path
+ )
+ );
+
+ block.unit()
+ }
+
+ crate fn break_scope(
+ &mut self,
+ mut block: BasicBlock,
+ value: Option<ExprRef<'tcx>>,
+ scope: BreakableTarget,
+ source_info: SourceInfo,
+ ) -> BlockAnd<()> {
+ let (mut target_block, region_scope, destination) =
+ self.scopes.find_breakable_scope(source_info.span, scope);
+ if let BreakableTarget::Return = scope {
+ // We call this now, rather than when we start lowering the
+ // function so that the return block doesn't precede the entire
+ // rest of the CFG. Some passes and LLVM prefer blocks to be in
+ // approximately CFG order.
+ target_block = self.return_block();
+ }
+ if let Some(destination) = destination {
+ if let Some(value) = value {
+ debug!("stmt_expr Break val block_context.push(SubExpr)");
+ self.block_context.push(BlockFrame::SubExpr);
+ unpack!(block = self.into(&destination, block, value));
+ self.block_context.pop();
+ } else {
+ self.cfg.push_assign_unit(block, source_info, &destination)
+ }
+ } else {
+ assert!(value.is_none(), "`return` and `break` should have a destination");
+ }
+ self.exit_scope(source_info.span, region_scope, block, target_block);
+ self.cfg.start_new_block().unit()
+ }
+
+ /// Branch out of `block` to `target`, exiting all scopes up to
+ /// and including `region_scope`. This will insert whatever drops are
+ /// needed. See module comment for details.
+ crate fn exit_scope(
+ &mut self,
+ span: Span,
+ region_scope: region::Scope,
+ mut block: BasicBlock,
+ target: BasicBlock,
+ ) {
+ debug!(
+ "exit_scope(region_scope={:?}, block={:?}, target={:?})",
+ region_scope, block, target
+ );
+ let scope_count = self.scopes.num_scopes_above(region_scope, span);
+
+ // If we are emitting a `drop` statement, we need to have the cached
+ // diverge cleanup pads ready in case that drop panics.
+ let may_panic = self.scopes.may_panic(scope_count);
+ if may_panic {
+ self.diverge_cleanup();
+ }
+
+ let mut scopes = self.scopes.top_scopes(scope_count + 1).rev();
+ let mut scope = scopes.next().unwrap();
+ for next_scope in scopes {
+ if scope.drops.is_empty() {
+ scope = next_scope;
+ continue;
+ }
+ let source_info = scope.source_info(span);
+ block = match scope.cached_exits.entry((target, region_scope)) {
+ Entry::Occupied(e) => {
+ self.cfg.goto(block, source_info, *e.get());
+ return;
+ }
+ Entry::Vacant(v) => {
+ let b = self.cfg.start_new_block();
+ self.cfg.goto(block, source_info, b);
+ v.insert(b);
+ b
+ }
+ };
+
+ let unwind_to = next_scope.cached_unwind.get(false).unwrap_or_else(|| {
+ debug_assert!(!may_panic, "cached block not present?");
+ START_BLOCK
+ });
+
+ unpack!(
+ block = build_scope_drops(
+ &mut self.cfg,
+ self.generator_kind,
+ scope,
+ block,
+ unwind_to,
+ self.arg_count,
+ false, // not generator
+ false, // not unwind path
+ )
+ );
+
+ scope = next_scope;
+ }
+
+ self.cfg.goto(block, self.scopes.source_info(scope_count, span), target);
+ }
+
+ /// Creates a path that performs all required cleanup for dropping a generator.
+ ///
+ /// This path terminates in GeneratorDrop. Returns the start of the path.
+ /// None indicates there’s no cleanup to do at this point.
+ crate fn generator_drop_cleanup(&mut self) -> Option<BasicBlock> {
+ // Fill in the cache for unwinds
+ self.diverge_cleanup_gen(true);
+
+ let src_info = self.scopes.source_info(self.scopes.len(), self.fn_span);
+ let resume_block = self.resume_block();
+ let mut scopes = self.scopes.iter_mut().peekable();
+ let mut block = self.cfg.start_new_block();
+ let result = block;
+
+ while let Some(scope) = scopes.next() {
+ block = if let Some(b) = scope.cached_generator_drop {
+ self.cfg.goto(block, src_info, b);
+ return Some(result);
+ } else {
+ let b = self.cfg.start_new_block();
+ scope.cached_generator_drop = Some(b);
+ self.cfg.goto(block, src_info, b);
+ b
+ };
+
+ let unwind_to = scopes
+ .peek()
+ .as_ref()
+ .map(|scope| {
+ scope
+ .cached_unwind
+ .get(true)
+ .unwrap_or_else(|| span_bug!(src_info.span, "cached block not present?"))
+ })
+ .unwrap_or(resume_block);
+
+ unpack!(
+ block = build_scope_drops(
+ &mut self.cfg,
+ self.generator_kind,
+ scope,
+ block,
+ unwind_to,
+ self.arg_count,
+ true, // is generator
+ true, // is cached path
+ )
+ );
+ }
+
+ self.cfg.terminate(block, src_info, TerminatorKind::GeneratorDrop);
+
+ Some(result)
+ }
+
+ /// Creates a new source scope, nested in the current one.
+ crate fn new_source_scope(
+ &mut self,
+ span: Span,
+ lint_level: LintLevel,
+ safety: Option<Safety>,
+ ) -> SourceScope {
+ let parent = self.source_scope;
+ debug!(
+ "new_source_scope({:?}, {:?}, {:?}) - parent({:?})={:?}",
+ span,
+ lint_level,
+ safety,
+ parent,
+ self.source_scopes.get(parent)
+ );
+ let scope_local_data = SourceScopeLocalData {
+ lint_root: if let LintLevel::Explicit(lint_root) = lint_level {
+ lint_root
+ } else {
+ self.source_scopes[parent].local_data.as_ref().assert_crate_local().lint_root
+ },
+ safety: safety.unwrap_or_else(|| {
+ self.source_scopes[parent].local_data.as_ref().assert_crate_local().safety
+ }),
+ };
+ self.source_scopes.push(SourceScopeData {
+ span,
+ parent_scope: Some(parent),
+ local_data: ClearCrossCrate::Set(scope_local_data),
+ })
+ }
+
+ /// Given a span and the current source scope, make a SourceInfo.
+ crate fn source_info(&self, span: Span) -> SourceInfo {
+ SourceInfo { span, scope: self.source_scope }
+ }
+
+ // Finding scopes
+ // ==============
+ /// Returns the scope that we should use as the lifetime of an
+ /// operand. Basically, an operand must live until it is consumed.
+ /// This is similar to, but not quite the same as, the temporary
+ /// scope (which can be larger or smaller).
+ ///
+ /// Consider:
+ ///
+ /// let x = foo(bar(X, Y));
+ ///
+ /// We wish to pop the storage for X and Y after `bar()` is
+ /// called, not after the whole `let` is completed.
+ ///
+ /// As another example, if the second argument diverges:
+ ///
+ /// foo(Box::new(2), panic!())
+ ///
+ /// We would allocate the box but then free it on the unwinding
+ /// path; we would also emit a free on the 'success' path from
+ /// panic, but that will turn out to be removed as dead-code.
+ ///
+ /// When building statics/constants, returns `None` since
+ /// intermediate values do not have to be dropped in that case.
+ crate fn local_scope(&self) -> Option<region::Scope> {
+ match self.hir.body_owner_kind {
+ hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) =>
+ // No need to free storage in this context.
+ {
+ None
+ }
+ hir::BodyOwnerKind::Closure | hir::BodyOwnerKind::Fn => Some(self.scopes.topmost()),
+ }
+ }
+
+ // Schedule an abort block - this is used for some ABIs that cannot unwind
+ crate fn schedule_abort(&mut self) -> BasicBlock {
+ let source_info = self.scopes.source_info(self.scopes.len(), self.fn_span);
+ let abortblk = self.cfg.start_new_cleanup_block();
+ self.cfg.terminate(abortblk, source_info, TerminatorKind::Abort);
+ self.cached_resume_block = Some(abortblk);
+ abortblk
+ }
+
+ // Scheduling drops
+ // ================
+ crate fn schedule_drop_storage_and_value(
+ &mut self,
+ span: Span,
+ region_scope: region::Scope,
+ local: Local,
+ ) {
+ self.schedule_drop(span, region_scope, local, DropKind::Storage);
+ self.schedule_drop(span, region_scope, local, DropKind::Value);
+ }
+
+ /// Indicates that `place` should be dropped on exit from
+ /// `region_scope`.
+ ///
+ /// When called with `DropKind::Storage`, `place` should be a local
+ /// with an index higher than the current `self.arg_count`.
+ crate fn schedule_drop(
+ &mut self,
+ span: Span,
+ region_scope: region::Scope,
+ local: Local,
+ drop_kind: DropKind,
+ ) {
+ let needs_drop = match drop_kind {
+ DropKind::Value => {
+ if !self.hir.needs_drop(self.local_decls[local].ty) {
+ return;
+ }
+ true
+ }
+ DropKind::Storage => {
+ if local.index() <= self.arg_count {
+ span_bug!(
+ span,
+ "`schedule_drop` called with local {:?} and arg_count {}",
+ local,
+ self.arg_count,
+ )
+ }
+ false
+ }
+ };
+
+ for scope in self.scopes.iter_mut() {
+ let this_scope = scope.region_scope == region_scope;
+ // When building drops, we try to cache chains of drops in such a way so these drops
+ // could be reused by the drops which would branch into the cached (already built)
+ // blocks. This, however, means that whenever we add a drop into a scope which already
+ // had some blocks built (and thus, cached) for it, we must invalidate all caches which
+ // might branch into the scope which had a drop just added to it. This is necessary,
+ // because otherwise some other code might use the cache to branch into already built
+ // chain of drops, essentially ignoring the newly added drop.
+ //
+ // For example consider there’s two scopes with a drop in each. These are built and
+ // thus the caches are filled:
+ //
+ // +--------------------------------------------------------+
+ // | +---------------------------------+ |
+ // | | +--------+ +-------------+ | +---------------+ |
+ // | | | return | <-+ | drop(outer) | <-+ | drop(middle) | |
+ // | | +--------+ +-------------+ | +---------------+ |
+ // | +------------|outer_scope cache|--+ |
+ // +------------------------------|middle_scope cache|------+
+ //
+ // Now, a new, inner-most scope is added along with a new drop into both inner-most and
+ // outer-most scopes:
+ //
+ // +------------------------------------------------------------+
+ // | +----------------------------------+ |
+ // | | +--------+ +-------------+ | +---------------+ | +-------------+
+ // | | | return | <+ | drop(new) | <-+ | drop(middle) | <--+| drop(inner) |
+ // | | +--------+ | | drop(outer) | | +---------------+ | +-------------+
+ // | | +-+ +-------------+ | |
+ // | +---|invalid outer_scope cache|----+ |
+ // +----=----------------|invalid middle_scope cache|-----------+
+ //
+ // If, when adding `drop(new)` we do not invalidate the cached blocks for both
+ // outer_scope and middle_scope, then, when building drops for the inner (right-most)
+ // scope, the old, cached blocks, without `drop(new)` will get used, producing the
+ // wrong results.
+ //
+ // The cache and its invalidation for unwind branch is somewhat special. The cache is
+ // per-drop, rather than per scope, which has a several different implications. Adding
+ // a new drop into a scope will not invalidate cached blocks of the prior drops in the
+ // scope. That is true, because none of the already existing drops will have an edge
+ // into a block with the newly added drop.
+ //
+ // Note that this code iterates scopes from the inner-most to the outer-most,
+ // invalidating caches of each scope visited. This way bare minimum of the
+ // caches gets invalidated. i.e., if a new drop is added into the middle scope, the
+ // cache of outer scope stays intact.
+ scope.invalidate_cache(!needs_drop, self.generator_kind, this_scope);
+ if this_scope {
+ let region_scope_span =
+ region_scope.span(self.hir.tcx(), &self.hir.region_scope_tree);
+ // Attribute scope exit drops to scope's closing brace.
+ let scope_end = self.hir.tcx().sess.source_map().end_point(region_scope_span);
+
+ scope.drops.push(DropData {
+ span: scope_end,
+ local,
+ kind: drop_kind,
+ cached_block: CachedBlock::default(),
+ });
+ return;
+ }
+ }
+ span_bug!(span, "region scope {:?} not in scope to drop {:?}", region_scope, local);
+ }
+
+ /// Indicates that the "local operand" stored in `local` is
+ /// *moved* at some point during execution (see `local_scope` for
+ /// more information about what a "local operand" is -- in short,
+ /// it's an intermediate operand created as part of preparing some
+ /// MIR instruction). We use this information to suppress
+ /// redundant drops on the non-unwind paths. This results in less
+ /// MIR, but also avoids spurious borrow check errors
+ /// (c.f. #64391).
+ ///
+ /// Example: when compiling the call to `foo` here:
+ ///
+ /// ```rust
+ /// foo(bar(), ...)
+ /// ```
+ ///
+ /// we would evaluate `bar()` to an operand `_X`. We would also
+ /// schedule `_X` to be dropped when the expression scope for
+ /// `foo(bar())` is exited. This is relevant, for example, if the
+ /// later arguments should unwind (it would ensure that `_X` gets
+ /// dropped). However, if no unwind occurs, then `_X` will be
+ /// unconditionally consumed by the `call`:
+ ///
+ /// ```
+ /// bb {
+ /// ...
+ /// _R = CALL(foo, _X, ...)
+ /// }
+ /// ```
+ ///
+ /// However, `_X` is still registered to be dropped, and so if we
+ /// do nothing else, we would generate a `DROP(_X)` that occurs
+ /// after the call. This will later be optimized out by the
+ /// drop-elaboation code, but in the meantime it can lead to
+ /// spurious borrow-check errors -- the problem, ironically, is
+ /// not the `DROP(_X)` itself, but the (spurious) unwind pathways
+ /// that it creates. See #64391 for an example.
+ crate fn record_operands_moved(&mut self, operands: &[Operand<'tcx>]) {
+ let scope = match self.local_scope() {
+ None => {
+ // if there is no local scope, operands won't be dropped anyway
+ return;
+ }
+
+ Some(local_scope) => self
+ .scopes
+ .iter_mut()
+ .find(|scope| scope.region_scope == local_scope)
+ .unwrap_or_else(|| bug!("scope {:?} not found in scope list!", local_scope)),
+ };
+
+ // look for moves of a local variable, like `MOVE(_X)`
+ let locals_moved = operands.iter().flat_map(|operand| match operand {
+ Operand::Copy(_) | Operand::Constant(_) => None,
+ Operand::Move(place) => place.as_local(),
+ });
+
+ for local in locals_moved {
+ // check if we have a Drop for this operand and -- if so
+ // -- add it to the list of moved operands. Note that this
+ // local might not have been an operand created for this
+ // call, it could come from other places too.
+ if scope.drops.iter().any(|drop| drop.local == local && drop.kind == DropKind::Value) {
+ scope.moved_locals.push(local);
+ }
+ }
+ }
+
+ // Other
+ // =====
+ /// Branch based on a boolean condition.
+ ///
+ /// This is a special case because the temporary for the condition needs to
+ /// be dropped on both the true and the false arm.
+ crate fn test_bool(
+ &mut self,
+ mut block: BasicBlock,
+ condition: Expr<'tcx>,
+ source_info: SourceInfo,
+ ) -> (BasicBlock, BasicBlock) {
+ let cond = unpack!(block = self.as_local_operand(block, condition));
+ let true_block = self.cfg.start_new_block();
+ let false_block = self.cfg.start_new_block();
+ let term = TerminatorKind::if_(self.hir.tcx(), cond.clone(), true_block, false_block);
+ self.cfg.terminate(block, source_info, term);
+
+ match cond {
+ // Don't try to drop a constant
+ Operand::Constant(_) => (),
+ // If constants and statics, we don't generate StorageLive for this
+ // temporary, so don't try to generate StorageDead for it either.
+ _ if self.local_scope().is_none() => (),
+ Operand::Copy(place) | Operand::Move(place) => {
+ if let Some(cond_temp) = place.as_local() {
+ // Manually drop the condition on both branches.
+ let top_scope = self.scopes.scopes.last_mut().unwrap();
+ let top_drop_data = top_scope.drops.pop().unwrap();
+
+ match top_drop_data.kind {
+ DropKind::Value { .. } => {
+ bug!("Drop scheduled on top of condition variable")
+ }
+ DropKind::Storage => {
+ let source_info = top_scope.source_info(top_drop_data.span);
+ let local = top_drop_data.local;
+ assert_eq!(local, cond_temp, "Drop scheduled on top of condition");
+ self.cfg.push(
+ true_block,
+ Statement { source_info, kind: StatementKind::StorageDead(local) },
+ );
+ self.cfg.push(
+ false_block,
+ Statement { source_info, kind: StatementKind::StorageDead(local) },
+ );
+ }
+ }
+
+ top_scope.invalidate_cache(true, self.generator_kind, true);
+ } else {
+ bug!("Expected as_local_operand to produce a temporary");
+ }
+ }
+ }
+
+ (true_block, false_block)
+ }
+
+ /// Creates a path that performs all required cleanup for unwinding.
+ ///
+ /// This path terminates in Resume. Returns the start of the path.
+ /// See module comment for more details.
+ crate fn diverge_cleanup(&mut self) -> BasicBlock {
+ self.diverge_cleanup_gen(false)
+ }
+
+ fn resume_block(&mut self) -> BasicBlock {
+ if let Some(target) = self.cached_resume_block {
+ target
+ } else {
+ let resumeblk = self.cfg.start_new_cleanup_block();
+ self.cfg.terminate(
+ resumeblk,
+ SourceInfo { scope: OUTERMOST_SOURCE_SCOPE, span: self.fn_span },
+ TerminatorKind::Resume,
+ );
+ self.cached_resume_block = Some(resumeblk);
+ resumeblk
+ }
+ }
+
+ fn diverge_cleanup_gen(&mut self, generator_drop: bool) -> BasicBlock {
+ // Build up the drops in **reverse** order. The end result will
+ // look like:
+ //
+ // scopes[n] -> scopes[n-1] -> ... -> scopes[0]
+ //
+ // However, we build this in **reverse order**. That is, we
+ // process scopes[0], then scopes[1], etc, pointing each one at
+ // the result generates from the one before. Along the way, we
+ // store caches. If everything is cached, we'll just walk right
+ // to left reading the cached results but never created anything.
+
+ // Find the last cached block
+ debug!("diverge_cleanup_gen(self.scopes = {:?})", self.scopes);
+ let cached_cleanup = self.scopes.iter_mut().enumerate().find_map(|(idx, ref scope)| {
+ let cached_block = scope.cached_unwind.get(generator_drop)?;
+ Some((cached_block, idx))
+ });
+ let (mut target, first_uncached) =
+ cached_cleanup.unwrap_or_else(|| (self.resume_block(), self.scopes.len()));
+
+ for scope in self.scopes.top_scopes(first_uncached) {
+ target = build_diverge_scope(
+ &mut self.cfg,
+ scope.region_scope_span,
+ scope,
+ target,
+ generator_drop,
+ self.generator_kind,
+ );
+ }
+
+ target
+ }
+
+ /// Utility function for *non*-scope code to build their own drops
+ crate fn build_drop_and_replace(
+ &mut self,
+ block: BasicBlock,
+ span: Span,
+ location: Place<'tcx>,
+ value: Operand<'tcx>,
+ ) -> BlockAnd<()> {
+ let source_info = self.source_info(span);
+ let next_target = self.cfg.start_new_block();
+ let diverge_target = self.diverge_cleanup();
+ self.cfg.terminate(
+ block,
+ source_info,
+ TerminatorKind::DropAndReplace {
+ location,
+ value,
+ target: next_target,
+ unwind: Some(diverge_target),
+ },
+ );
+ next_target.unit()
+ }
+
+ /// Creates an Assert terminator and return the success block.
+ /// If the boolean condition operand is not the expected value,
+ /// a runtime panic will be caused with the given message.
+ crate fn assert(
+ &mut self,
+ block: BasicBlock,
+ cond: Operand<'tcx>,
+ expected: bool,
+ msg: AssertMessage<'tcx>,
+ span: Span,
+ ) -> BasicBlock {
+ let source_info = self.source_info(span);
+
+ let success_block = self.cfg.start_new_block();
+ let cleanup = self.diverge_cleanup();
+
+ self.cfg.terminate(
+ block,
+ source_info,
+ TerminatorKind::Assert {
+ cond,
+ expected,
+ msg,
+ target: success_block,
+ cleanup: Some(cleanup),
+ },
+ );
+
+ success_block
+ }
+
+ // `match` arm scopes
+ // ==================
+ /// Unschedules any drops in the top scope.
+ ///
+ /// This is only needed for `match` arm scopes, because they have one
+ /// entrance per pattern, but only one exit.
+ pub(crate) fn clear_top_scope(&mut self, region_scope: region::Scope) {
+ let top_scope = self.scopes.scopes.last_mut().unwrap();
+
+ assert_eq!(top_scope.region_scope, region_scope);
+
+ top_scope.drops.clear();
+ top_scope.invalidate_cache(false, self.generator_kind, true);
+ }
+}
+
+/// Builds drops for pop_scope and exit_scope.
+fn build_scope_drops<'tcx>(
+ cfg: &mut CFG<'tcx>,
+ generator_kind: Option<GeneratorKind>,
+ scope: &Scope,
+ mut block: BasicBlock,
+ last_unwind_to: BasicBlock,
+ arg_count: usize,
+ generator_drop: bool,
+ is_cached_path: bool,
+) -> BlockAnd<()> {
+ debug!("build_scope_drops({:?} -> {:?})", block, scope);
+
+ // Build up the drops in evaluation order. The end result will
+ // look like:
+ //
+ // [SDs, drops[n]] --..> [SDs, drop[1]] -> [SDs, drop[0]] -> [[SDs]]
+ // | | |
+ // : | |
+ // V V
+ // [drop[n]] -...-> [drop[1]] ------> [drop[0]] ------> [last_unwind_to]
+ //
+ // The horizontal arrows represent the execution path when the drops return
+ // successfully. The downwards arrows represent the execution path when the
+ // drops panic (panicking while unwinding will abort, so there's no need for
+ // another set of arrows).
+ //
+ // For generators, we unwind from a drop on a local to its StorageDead
+ // statement. For other functions we don't worry about StorageDead. The
+ // drops for the unwind path should have already been generated by
+ // `diverge_cleanup_gen`.
+
+ for drop_idx in (0..scope.drops.len()).rev() {
+ let drop_data = &scope.drops[drop_idx];
+ let source_info = scope.source_info(drop_data.span);
+ let local = drop_data.local;
+
+ match drop_data.kind {
+ DropKind::Value => {
+ // If the operand has been moved, and we are not on an unwind
+ // path, then don't generate the drop. (We only take this into
+ // account for non-unwind paths so as not to disturb the
+ // caching mechanism.)
+ if !is_cached_path && scope.moved_locals.iter().any(|&o| o == local) {
+ continue;
+ }
+
+ let unwind_to = get_unwind_to(scope, generator_kind, drop_idx, generator_drop)
+ .unwrap_or(last_unwind_to);
+
+ let next = cfg.start_new_block();
+ cfg.terminate(
+ block,
+ source_info,
+ TerminatorKind::Drop {
+ location: local.into(),
+ target: next,
+ unwind: Some(unwind_to),
+ },
+ );
+ block = next;
+ }
+ DropKind::Storage => {
+ // Only temps and vars need their storage dead.
+ assert!(local.index() > arg_count);
+ cfg.push(block, Statement { source_info, kind: StatementKind::StorageDead(local) });
+ }
+ }
+ }
+ block.unit()
+}
+
+fn get_unwind_to(
+ scope: &Scope,
+ generator_kind: Option<GeneratorKind>,
+ unwind_from: usize,
+ generator_drop: bool,
+) -> Option<BasicBlock> {
+ for drop_idx in (0..unwind_from).rev() {
+ let drop_data = &scope.drops[drop_idx];
+ match (generator_kind, &drop_data.kind) {
+ (Some(_), DropKind::Storage) => {
+ return Some(drop_data.cached_block.get(generator_drop).unwrap_or_else(|| {
+ span_bug!(drop_data.span, "cached block not present for {:?}", drop_data)
+ }));
+ }
+ (None, DropKind::Value) => {
+ return Some(drop_data.cached_block.get(generator_drop).unwrap_or_else(|| {
+ span_bug!(drop_data.span, "cached block not present for {:?}", drop_data)
+ }));
+ }
+ _ => (),
+ }
+ }
+ None
+}
+
+fn build_diverge_scope<'tcx>(
+ cfg: &mut CFG<'tcx>,
+ span: Span,
+ scope: &mut Scope,
+ mut target: BasicBlock,
+ generator_drop: bool,
+ generator_kind: Option<GeneratorKind>,
+) -> BasicBlock {
+ // Build up the drops in **reverse** order. The end result will
+ // look like:
+ //
+ // [drops[n]] -...-> [drops[0]] -> [target]
+ //
+ // The code in this function reads from right to left. At each
+ // point, we check for cached blocks representing the
+ // remainder. If everything is cached, we'll just walk right to
+ // left reading the cached results but never create anything.
+
+ let source_scope = scope.source_scope;
+ let source_info = |span| SourceInfo { span, scope: source_scope };
+
+ // We keep track of StorageDead statements to prepend to our current block
+ // and store them here, in reverse order.
+ let mut storage_deads = vec![];
+
+ let mut target_built_by_us = false;
+
+ // Build up the drops. Here we iterate the vector in
+ // *forward* order, so that we generate drops[0] first (right to
+ // left in diagram above).
+ debug!("build_diverge_scope({:?})", scope.drops);
+ for (j, drop_data) in scope.drops.iter_mut().enumerate() {
+ debug!("build_diverge_scope drop_data[{}]: {:?}", j, drop_data);
+ // Only full value drops are emitted in the diverging path,
+ // not StorageDead, except in the case of generators.
+ //
+ // Note: This may not actually be what we desire (are we
+ // "freeing" stack storage as we unwind, or merely observing a
+ // frozen stack)? In particular, the intent may have been to
+ // match the behavior of clang, but on inspection eddyb says
+ // this is not what clang does.
+ match drop_data.kind {
+ DropKind::Storage if generator_kind.is_some() => {
+ storage_deads.push(Statement {
+ source_info: source_info(drop_data.span),
+ kind: StatementKind::StorageDead(drop_data.local),
+ });
+ if !target_built_by_us {
+ // We cannot add statements to an existing block, so we create a new
+ // block for our StorageDead statements.
+ let block = cfg.start_new_cleanup_block();
+ let source_info = SourceInfo { span: DUMMY_SP, scope: source_scope };
+ cfg.goto(block, source_info, target);
+ target = block;
+ target_built_by_us = true;
+ }
+ *drop_data.cached_block.ref_mut(generator_drop) = Some(target);
+ }
+ DropKind::Storage => {}
+ DropKind::Value => {
+ let cached_block = drop_data.cached_block.ref_mut(generator_drop);
+ target = if let Some(cached_block) = *cached_block {
+ storage_deads.clear();
+ target_built_by_us = false;
+ cached_block
+ } else {
+ push_storage_deads(cfg, target, &mut storage_deads);
+ let block = cfg.start_new_cleanup_block();
+ cfg.terminate(
+ block,
+ source_info(drop_data.span),
+ TerminatorKind::Drop {
+ location: drop_data.local.into(),
+ target,
+ unwind: None,
+ },
+ );
+ *cached_block = Some(block);
+ target_built_by_us = true;
+ block
+ };
+ }
+ };
+ }
+ push_storage_deads(cfg, target, &mut storage_deads);
+ *scope.cached_unwind.ref_mut(generator_drop) = Some(target);
+
+ assert!(storage_deads.is_empty());
+ debug!("build_diverge_scope({:?}, {:?}) = {:?}", scope, span, target);
+
+ target
+}
+
+fn push_storage_deads<'tcx>(
+ cfg: &mut CFG<'tcx>,
+ target: BasicBlock,
+ storage_deads: &mut Vec<Statement<'tcx>>,
+) {
+ if storage_deads.is_empty() {
+ return;
+ }
+ let statements = &mut cfg.block_data_mut(target).statements;
+ storage_deads.reverse();
+ debug!(
+ "push_storage_deads({:?}), storage_deads={:?}, statements={:?}",
+ target, storage_deads, statements
+ );
+ storage_deads.append(statements);
+ mem::swap(statements, storage_deads);
+ assert!(storage_deads.is_empty());
+}
--- /dev/null
+use rustc::mir::interpret::{ConstValue, Scalar};
+use rustc::ty::{self, layout::Size, ParamEnv, Ty, TyCtxt};
+use rustc_span::symbol::Symbol;
+use syntax::ast;
+
+#[derive(PartialEq)]
+crate enum LitToConstError {
+ UnparseableFloat,
+ Reported,
+}
+
+crate fn lit_to_const<'tcx>(
+ lit: &'tcx ast::LitKind,
+ tcx: TyCtxt<'tcx>,
+ ty: Ty<'tcx>,
+ neg: bool,
+) -> Result<&'tcx ty::Const<'tcx>, LitToConstError> {
+ use syntax::ast::*;
+
+ let trunc = |n| {
+ let param_ty = ParamEnv::reveal_all().and(ty);
+ let width = tcx.layout_of(param_ty).map_err(|_| LitToConstError::Reported)?.size;
+ trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits());
+ let result = truncate(n, width);
+ trace!("trunc result: {}", result);
+ Ok(ConstValue::Scalar(Scalar::from_uint(result, width)))
+ };
+
+ use rustc::mir::interpret::*;
+ let lit = match *lit {
+ LitKind::Str(ref s, _) => {
+ let s = s.as_str();
+ let allocation = Allocation::from_byte_aligned_bytes(s.as_bytes());
+ let allocation = tcx.intern_const_alloc(allocation);
+ ConstValue::Slice { data: allocation, start: 0, end: s.len() }
+ }
+ LitKind::ByteStr(ref data) => {
+ let id = tcx.allocate_bytes(data);
+ ConstValue::Scalar(Scalar::Ptr(id.into()))
+ }
+ LitKind::Byte(n) => ConstValue::Scalar(Scalar::from_uint(n, Size::from_bytes(1))),
+ LitKind::Int(n, _) if neg => {
+ let n = n as i128;
+ let n = n.overflowing_neg().0;
+ trunc(n as u128)?
+ }
+ LitKind::Int(n, _) => trunc(n)?,
+ LitKind::Float(n, _) => {
+ let fty = match ty.kind {
+ ty::Float(fty) => fty,
+ _ => bug!(),
+ };
+ parse_float(n, fty, neg).map_err(|_| LitToConstError::UnparseableFloat)?
+ }
+ LitKind::Bool(b) => ConstValue::Scalar(Scalar::from_bool(b)),
+ LitKind::Char(c) => ConstValue::Scalar(Scalar::from_char(c)),
+ LitKind::Err(_) => unreachable!(),
+ };
+ Ok(tcx.mk_const(ty::Const { val: ty::ConstKind::Value(lit), ty }))
+}
+
+fn parse_float<'tcx>(num: Symbol, fty: ast::FloatTy, neg: bool) -> Result<ConstValue<'tcx>, ()> {
+ let num = num.as_str();
+ use rustc_apfloat::ieee::{Double, Single};
+ let scalar = match fty {
+ ast::FloatTy::F32 => {
+ num.parse::<f32>().map_err(|_| ())?;
+ let mut f = num.parse::<Single>().unwrap_or_else(|e| {
+ panic!("apfloat::ieee::Single failed to parse `{}`: {:?}", num, e)
+ });
+ if neg {
+ f = -f;
+ }
+ Scalar::from_f32(f)
+ }
+ ast::FloatTy::F64 => {
+ num.parse::<f64>().map_err(|_| ())?;
+ let mut f = num.parse::<Double>().unwrap_or_else(|e| {
+ panic!("apfloat::ieee::Double failed to parse `{}`: {:?}", num, e)
+ });
+ if neg {
+ f = -f;
+ }
+ Scalar::from_f64(f)
+ }
+ };
+
+ Ok(ConstValue::Scalar(scalar))
+}
--- /dev/null
+use crate::hair::cx::to_ref::ToRef;
+use crate::hair::cx::Cx;
+use crate::hair::{self, *};
+
+use rustc::middle::region;
+use rustc::ty;
+use rustc_hir as hir;
+
+use rustc_index::vec::Idx;
+
+impl<'tcx> Mirror<'tcx> for &'tcx hir::Block<'tcx> {
+ type Output = Block<'tcx>;
+
+ fn make_mirror(self, cx: &mut Cx<'_, 'tcx>) -> Block<'tcx> {
+ // We have to eagerly lower the "spine" of the statements
+ // in order to get the lexical scoping correctly.
+ let stmts = mirror_stmts(cx, self.hir_id.local_id, &*self.stmts);
+ let opt_destruction_scope =
+ cx.region_scope_tree.opt_destruction_scope(self.hir_id.local_id);
+ Block {
+ targeted_by_break: self.targeted_by_break,
+ region_scope: region::Scope { id: self.hir_id.local_id, data: region::ScopeData::Node },
+ opt_destruction_scope,
+ span: self.span,
+ stmts,
+ expr: self.expr.to_ref(),
+ safety_mode: match self.rules {
+ hir::BlockCheckMode::DefaultBlock => BlockSafety::Safe,
+ hir::BlockCheckMode::UnsafeBlock(..) => BlockSafety::ExplicitUnsafe(self.hir_id),
+ hir::BlockCheckMode::PushUnsafeBlock(..) => BlockSafety::PushUnsafe,
+ hir::BlockCheckMode::PopUnsafeBlock(..) => BlockSafety::PopUnsafe,
+ },
+ }
+ }
+}
+
+fn mirror_stmts<'a, 'tcx>(
+ cx: &mut Cx<'a, 'tcx>,
+ block_id: hir::ItemLocalId,
+ stmts: &'tcx [hir::Stmt<'tcx>],
+) -> Vec<StmtRef<'tcx>> {
+ let mut result = vec![];
+ for (index, stmt) in stmts.iter().enumerate() {
+ let hir_id = stmt.hir_id;
+ let opt_dxn_ext = cx.region_scope_tree.opt_destruction_scope(hir_id.local_id);
+ match stmt.kind {
+ hir::StmtKind::Expr(ref expr) | hir::StmtKind::Semi(ref expr) => {
+ result.push(StmtRef::Mirror(Box::new(Stmt {
+ kind: StmtKind::Expr {
+ scope: region::Scope { id: hir_id.local_id, data: region::ScopeData::Node },
+ expr: expr.to_ref(),
+ },
+ opt_destruction_scope: opt_dxn_ext,
+ })))
+ }
+ hir::StmtKind::Item(..) => {
+ // ignore for purposes of the MIR
+ }
+ hir::StmtKind::Local(ref local) => {
+ let remainder_scope = region::Scope {
+ id: block_id,
+ data: region::ScopeData::Remainder(region::FirstStatementIndex::new(index)),
+ };
+
+ let mut pattern = cx.pattern_from_hir(&local.pat);
+
+ if let Some(ty) = &local.ty {
+ if let Some(&user_ty) = cx.tables.user_provided_types().get(ty.hir_id) {
+ debug!("mirror_stmts: user_ty={:?}", user_ty);
+ pattern = Pat {
+ ty: pattern.ty,
+ span: pattern.span,
+ kind: Box::new(PatKind::AscribeUserType {
+ ascription: hair::pattern::Ascription {
+ user_ty: PatTyProj::from_user_type(user_ty),
+ user_ty_span: ty.span,
+ variance: ty::Variance::Covariant,
+ },
+ subpattern: pattern,
+ }),
+ };
+ }
+ }
+
+ result.push(StmtRef::Mirror(Box::new(Stmt {
+ kind: StmtKind::Let {
+ remainder_scope: remainder_scope,
+ init_scope: region::Scope {
+ id: hir_id.local_id,
+ data: region::ScopeData::Node,
+ },
+ pattern,
+ initializer: local.init.to_ref(),
+ lint_level: LintLevel::Explicit(local.hir_id),
+ },
+ opt_destruction_scope: opt_dxn_ext,
+ })));
+ }
+ }
+ }
+ return result;
+}
+
+crate fn to_expr_ref<'a, 'tcx>(
+ cx: &mut Cx<'a, 'tcx>,
+ block: &'tcx hir::Block<'tcx>,
+) -> ExprRef<'tcx> {
+ let block_ty = cx.tables().node_type(block.hir_id);
+ let temp_lifetime = cx.region_scope_tree.temporary_scope(block.hir_id.local_id);
+ let expr = Expr {
+ ty: block_ty,
+ temp_lifetime,
+ span: block.span,
+ kind: ExprKind::Block { body: block },
+ };
+ expr.to_ref()
+}
--- /dev/null
+use crate::hair::cx::block;
+use crate::hair::cx::to_ref::ToRef;
+use crate::hair::cx::Cx;
+use crate::hair::util::UserAnnotatedTyHelpers;
+use crate::hair::*;
+use rustc::mir::interpret::{ErrorHandled, Scalar};
+use rustc::mir::BorrowKind;
+use rustc::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, PointerCast};
+use rustc::ty::subst::{InternalSubsts, SubstsRef};
+use rustc::ty::{self, AdtKind, Ty};
+use rustc_hir as hir;
+use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
+use rustc_hir::def_id::LocalDefId;
+use rustc_index::vec::Idx;
+use rustc_span::Span;
+
+impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr<'tcx> {
+ type Output = Expr<'tcx>;
+
+ fn make_mirror(self, cx: &mut Cx<'_, 'tcx>) -> Expr<'tcx> {
+ let temp_lifetime = cx.region_scope_tree.temporary_scope(self.hir_id.local_id);
+ let expr_scope = region::Scope { id: self.hir_id.local_id, data: region::ScopeData::Node };
+
+ debug!("Expr::make_mirror(): id={}, span={:?}", self.hir_id, self.span);
+
+ let mut expr = make_mirror_unadjusted(cx, self);
+
+ // Now apply adjustments, if any.
+ for adjustment in cx.tables().expr_adjustments(self) {
+ debug!("make_mirror: expr={:?} applying adjustment={:?}", expr, adjustment);
+ expr = apply_adjustment(cx, self, expr, adjustment);
+ }
+
+ // Next, wrap this up in the expr's scope.
+ expr = Expr {
+ temp_lifetime,
+ ty: expr.ty,
+ span: self.span,
+ kind: ExprKind::Scope {
+ region_scope: expr_scope,
+ value: expr.to_ref(),
+ lint_level: LintLevel::Explicit(self.hir_id),
+ },
+ };
+
+ // Finally, create a destruction scope, if any.
+ if let Some(region_scope) = cx.region_scope_tree.opt_destruction_scope(self.hir_id.local_id)
+ {
+ expr = Expr {
+ temp_lifetime,
+ ty: expr.ty,
+ span: self.span,
+ kind: ExprKind::Scope {
+ region_scope,
+ value: expr.to_ref(),
+ lint_level: LintLevel::Inherited,
+ },
+ };
+ }
+
+ // OK, all done!
+ expr
+ }
+}
+
+fn apply_adjustment<'a, 'tcx>(
+ cx: &mut Cx<'a, 'tcx>,
+ hir_expr: &'tcx hir::Expr<'tcx>,
+ mut expr: Expr<'tcx>,
+ adjustment: &Adjustment<'tcx>,
+) -> Expr<'tcx> {
+ let Expr { temp_lifetime, mut span, .. } = expr;
+
+ // Adjust the span from the block, to the last expression of the
+ // block. This is a better span when returning a mutable reference
+ // with too short a lifetime. The error message will use the span
+ // from the assignment to the return place, which should only point
+ // at the returned value, not the entire function body.
+ //
+ // fn return_short_lived<'a>(x: &'a mut i32) -> &'static mut i32 {
+ // x
+ // // ^ error message points at this expression.
+ // }
+ let mut adjust_span = |expr: &mut Expr<'tcx>| {
+ if let ExprKind::Block { body } = expr.kind {
+ if let Some(ref last_expr) = body.expr {
+ span = last_expr.span;
+ expr.span = span;
+ }
+ }
+ };
+
+ let kind = match adjustment.kind {
+ Adjust::Pointer(PointerCast::Unsize) => {
+ adjust_span(&mut expr);
+ ExprKind::Pointer { cast: PointerCast::Unsize, source: expr.to_ref() }
+ }
+ Adjust::Pointer(cast) => ExprKind::Pointer { cast, source: expr.to_ref() },
+ Adjust::NeverToAny => ExprKind::NeverToAny { source: expr.to_ref() },
+ Adjust::Deref(None) => {
+ adjust_span(&mut expr);
+ ExprKind::Deref { arg: expr.to_ref() }
+ }
+ Adjust::Deref(Some(deref)) => {
+ // We don't need to do call adjust_span here since
+ // deref coercions always start with a built-in deref.
+ let call = deref.method_call(cx.tcx(), expr.ty);
+
+ expr = Expr {
+ temp_lifetime,
+ ty: cx.tcx.mk_ref(deref.region, ty::TypeAndMut { ty: expr.ty, mutbl: deref.mutbl }),
+ span,
+ kind: ExprKind::Borrow {
+ borrow_kind: deref.mutbl.to_borrow_kind(),
+ arg: expr.to_ref(),
+ },
+ };
+
+ overloaded_place(cx, hir_expr, adjustment.target, Some(call), vec![expr.to_ref()])
+ }
+ Adjust::Borrow(AutoBorrow::Ref(_, m)) => {
+ ExprKind::Borrow { borrow_kind: m.to_borrow_kind(), arg: expr.to_ref() }
+ }
+ Adjust::Borrow(AutoBorrow::RawPtr(mutability)) => {
+ ExprKind::AddressOf { mutability, arg: expr.to_ref() }
+ }
+ };
+
+ Expr { temp_lifetime, ty: adjustment.target, span, kind }
+}
+
+fn make_mirror_unadjusted<'a, 'tcx>(
+ cx: &mut Cx<'a, 'tcx>,
+ expr: &'tcx hir::Expr<'tcx>,
+) -> Expr<'tcx> {
+ let expr_ty = cx.tables().expr_ty(expr);
+ let temp_lifetime = cx.region_scope_tree.temporary_scope(expr.hir_id.local_id);
+
+ let kind = match expr.kind {
+ // Here comes the interesting stuff:
+ hir::ExprKind::MethodCall(_, method_span, ref args) => {
+ // Rewrite a.b(c) into UFCS form like Trait::b(a, c)
+ let expr = method_callee(cx, expr, method_span, None);
+ let args = args.iter().map(|e| e.to_ref()).collect();
+ ExprKind::Call { ty: expr.ty, fun: expr.to_ref(), args, from_hir_call: true }
+ }
+
+ hir::ExprKind::Call(ref fun, ref args) => {
+ if cx.tables().is_method_call(expr) {
+ // The callee is something implementing Fn, FnMut, or FnOnce.
+ // Find the actual method implementation being called and
+ // build the appropriate UFCS call expression with the
+ // callee-object as expr parameter.
+
+ // rewrite f(u, v) into FnOnce::call_once(f, (u, v))
+
+ let method = method_callee(cx, expr, fun.span, None);
+
+ let arg_tys = args.iter().map(|e| cx.tables().expr_ty_adjusted(e));
+ let tupled_args = Expr {
+ ty: cx.tcx.mk_tup(arg_tys),
+ temp_lifetime,
+ span: expr.span,
+ kind: ExprKind::Tuple { fields: args.iter().map(ToRef::to_ref).collect() },
+ };
+
+ ExprKind::Call {
+ ty: method.ty,
+ fun: method.to_ref(),
+ args: vec![fun.to_ref(), tupled_args.to_ref()],
+ from_hir_call: true,
+ }
+ } else {
+ let adt_data =
+ if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = fun.kind {
+ // Tuple-like ADTs are represented as ExprKind::Call. We convert them here.
+ expr_ty.ty_adt_def().and_then(|adt_def| match path.res {
+ Res::Def(DefKind::Ctor(_, CtorKind::Fn), ctor_id) => {
+ Some((adt_def, adt_def.variant_index_with_ctor_id(ctor_id)))
+ }
+ Res::SelfCtor(..) => Some((adt_def, VariantIdx::new(0))),
+ _ => None,
+ })
+ } else {
+ None
+ };
+ if let Some((adt_def, index)) = adt_data {
+ let substs = cx.tables().node_substs(fun.hir_id);
+ let user_provided_types = cx.tables().user_provided_types();
+ let user_ty =
+ user_provided_types.get(fun.hir_id).map(|u_ty| *u_ty).map(|mut u_ty| {
+ if let UserType::TypeOf(ref mut did, _) = &mut u_ty.value {
+ *did = adt_def.did;
+ }
+ u_ty
+ });
+ debug!("make_mirror_unadjusted: (call) user_ty={:?}", user_ty);
+
+ let field_refs = args
+ .iter()
+ .enumerate()
+ .map(|(idx, e)| FieldExprRef { name: Field::new(idx), expr: e.to_ref() })
+ .collect();
+ ExprKind::Adt {
+ adt_def,
+ substs,
+ variant_index: index,
+ fields: field_refs,
+ user_ty,
+ base: None,
+ }
+ } else {
+ ExprKind::Call {
+ ty: cx.tables().node_type(fun.hir_id),
+ fun: fun.to_ref(),
+ args: args.to_ref(),
+ from_hir_call: true,
+ }
+ }
+ }
+ }
+
+ hir::ExprKind::AddrOf(hir::BorrowKind::Ref, mutbl, ref arg) => {
+ ExprKind::Borrow { borrow_kind: mutbl.to_borrow_kind(), arg: arg.to_ref() }
+ }
+
+ hir::ExprKind::AddrOf(hir::BorrowKind::Raw, mutability, ref arg) => {
+ ExprKind::AddressOf { mutability, arg: arg.to_ref() }
+ }
+
+ hir::ExprKind::Block(ref blk, _) => ExprKind::Block { body: &blk },
+
+ hir::ExprKind::Assign(ref lhs, ref rhs, _) => {
+ ExprKind::Assign { lhs: lhs.to_ref(), rhs: rhs.to_ref() }
+ }
+
+ hir::ExprKind::AssignOp(op, ref lhs, ref rhs) => {
+ if cx.tables().is_method_call(expr) {
+ overloaded_operator(cx, expr, vec![lhs.to_ref(), rhs.to_ref()])
+ } else {
+ ExprKind::AssignOp { op: bin_op(op.node), lhs: lhs.to_ref(), rhs: rhs.to_ref() }
+ }
+ }
+
+ hir::ExprKind::Lit(ref lit) => ExprKind::Literal {
+ literal: cx.const_eval_literal(&lit.node, expr_ty, lit.span, false),
+ user_ty: None,
+ },
+
+ hir::ExprKind::Binary(op, ref lhs, ref rhs) => {
+ if cx.tables().is_method_call(expr) {
+ overloaded_operator(cx, expr, vec![lhs.to_ref(), rhs.to_ref()])
+ } else {
+ // FIXME overflow
+ match (op.node, cx.constness) {
+ // Destroy control flow if `#![feature(const_if_match)]` is not enabled.
+ (hir::BinOpKind::And, hir::Constness::Const)
+ if !cx.tcx.features().const_if_match =>
+ {
+ cx.control_flow_destroyed.push((op.span, "`&&` operator".into()));
+ ExprKind::Binary { op: BinOp::BitAnd, lhs: lhs.to_ref(), rhs: rhs.to_ref() }
+ }
+ (hir::BinOpKind::Or, hir::Constness::Const)
+ if !cx.tcx.features().const_if_match =>
+ {
+ cx.control_flow_destroyed.push((op.span, "`||` operator".into()));
+ ExprKind::Binary { op: BinOp::BitOr, lhs: lhs.to_ref(), rhs: rhs.to_ref() }
+ }
+
+ (hir::BinOpKind::And, _) => ExprKind::LogicalOp {
+ op: LogicalOp::And,
+ lhs: lhs.to_ref(),
+ rhs: rhs.to_ref(),
+ },
+ (hir::BinOpKind::Or, _) => ExprKind::LogicalOp {
+ op: LogicalOp::Or,
+ lhs: lhs.to_ref(),
+ rhs: rhs.to_ref(),
+ },
+
+ _ => {
+ let op = bin_op(op.node);
+ ExprKind::Binary { op, lhs: lhs.to_ref(), rhs: rhs.to_ref() }
+ }
+ }
+ }
+ }
+
+ hir::ExprKind::Index(ref lhs, ref index) => {
+ if cx.tables().is_method_call(expr) {
+ overloaded_place(cx, expr, expr_ty, None, vec![lhs.to_ref(), index.to_ref()])
+ } else {
+ ExprKind::Index { lhs: lhs.to_ref(), index: index.to_ref() }
+ }
+ }
+
+ hir::ExprKind::Unary(hir::UnOp::UnDeref, ref arg) => {
+ if cx.tables().is_method_call(expr) {
+ overloaded_place(cx, expr, expr_ty, None, vec![arg.to_ref()])
+ } else {
+ ExprKind::Deref { arg: arg.to_ref() }
+ }
+ }
+
+ hir::ExprKind::Unary(hir::UnOp::UnNot, ref arg) => {
+ if cx.tables().is_method_call(expr) {
+ overloaded_operator(cx, expr, vec![arg.to_ref()])
+ } else {
+ ExprKind::Unary { op: UnOp::Not, arg: arg.to_ref() }
+ }
+ }
+
+ hir::ExprKind::Unary(hir::UnOp::UnNeg, ref arg) => {
+ if cx.tables().is_method_call(expr) {
+ overloaded_operator(cx, expr, vec![arg.to_ref()])
+ } else {
+ if let hir::ExprKind::Lit(ref lit) = arg.kind {
+ ExprKind::Literal {
+ literal: cx.const_eval_literal(&lit.node, expr_ty, lit.span, true),
+ user_ty: None,
+ }
+ } else {
+ ExprKind::Unary { op: UnOp::Neg, arg: arg.to_ref() }
+ }
+ }
+ }
+
+ hir::ExprKind::Struct(ref qpath, ref fields, ref base) => match expr_ty.kind {
+ ty::Adt(adt, substs) => match adt.adt_kind() {
+ AdtKind::Struct | AdtKind::Union => {
+ let user_provided_types = cx.tables().user_provided_types();
+ let user_ty = user_provided_types.get(expr.hir_id).map(|u_ty| *u_ty);
+ debug!("make_mirror_unadjusted: (struct/union) user_ty={:?}", user_ty);
+ ExprKind::Adt {
+ adt_def: adt,
+ variant_index: VariantIdx::new(0),
+ substs,
+ user_ty,
+ fields: field_refs(cx, fields),
+ base: base.as_ref().map(|base| FruInfo {
+ base: base.to_ref(),
+ field_types: cx.tables().fru_field_types()[expr.hir_id].clone(),
+ }),
+ }
+ }
+ AdtKind::Enum => {
+ let res = cx.tables().qpath_res(qpath, expr.hir_id);
+ match res {
+ Res::Def(DefKind::Variant, variant_id) => {
+ assert!(base.is_none());
+
+ let index = adt.variant_index_with_id(variant_id);
+ let user_provided_types = cx.tables().user_provided_types();
+ let user_ty = user_provided_types.get(expr.hir_id).map(|u_ty| *u_ty);
+ debug!("make_mirror_unadjusted: (variant) user_ty={:?}", user_ty);
+ ExprKind::Adt {
+ adt_def: adt,
+ variant_index: index,
+ substs,
+ user_ty,
+ fields: field_refs(cx, fields),
+ base: None,
+ }
+ }
+ _ => {
+ span_bug!(expr.span, "unexpected res: {:?}", res);
+ }
+ }
+ }
+ },
+ _ => {
+ span_bug!(expr.span, "unexpected type for struct literal: {:?}", expr_ty);
+ }
+ },
+
+ hir::ExprKind::Closure(..) => {
+ let closure_ty = cx.tables().expr_ty(expr);
+ let (def_id, substs, movability) = match closure_ty.kind {
+ ty::Closure(def_id, substs) => (def_id, UpvarSubsts::Closure(substs), None),
+ ty::Generator(def_id, substs, movability) => {
+ (def_id, UpvarSubsts::Generator(substs), Some(movability))
+ }
+ _ => {
+ span_bug!(expr.span, "closure expr w/o closure type: {:?}", closure_ty);
+ }
+ };
+ let upvars = cx
+ .tcx
+ .upvars(def_id)
+ .iter()
+ .flat_map(|upvars| upvars.iter())
+ .zip(substs.upvar_tys(def_id, cx.tcx))
+ .map(|((&var_hir_id, _), ty)| capture_upvar(cx, expr, var_hir_id, ty))
+ .collect();
+ ExprKind::Closure { closure_id: def_id, substs, upvars, movability }
+ }
+
+ hir::ExprKind::Path(ref qpath) => {
+ let res = cx.tables().qpath_res(qpath, expr.hir_id);
+ convert_path_expr(cx, expr, res)
+ }
+
+ hir::ExprKind::InlineAsm(ref asm) => ExprKind::InlineAsm {
+ asm: &asm.inner,
+ outputs: asm.outputs_exprs.to_ref(),
+ inputs: asm.inputs_exprs.to_ref(),
+ },
+
+ // Now comes the rote stuff:
+ hir::ExprKind::Repeat(ref v, ref count) => {
+ let def_id = cx.tcx.hir().local_def_id(count.hir_id);
+ let substs = InternalSubsts::identity_for_item(cx.tcx, def_id);
+ let span = cx.tcx.def_span(def_id);
+ let count =
+ match cx.tcx.const_eval_resolve(cx.param_env, def_id, substs, None, Some(span)) {
+ Ok(cv) => cv.eval_usize(cx.tcx, cx.param_env),
+ Err(ErrorHandled::Reported) => 0,
+ Err(ErrorHandled::TooGeneric) => {
+ let span = cx.tcx.def_span(def_id);
+ cx.tcx
+ .sess
+ .span_err(span, "array lengths can't depend on generic parameters");
+ 0
+ }
+ };
+
+ ExprKind::Repeat { value: v.to_ref(), count }
+ }
+ hir::ExprKind::Ret(ref v) => ExprKind::Return { value: v.to_ref() },
+ hir::ExprKind::Break(dest, ref value) => match dest.target_id {
+ Ok(target_id) => ExprKind::Break {
+ label: region::Scope { id: target_id.local_id, data: region::ScopeData::Node },
+ value: value.to_ref(),
+ },
+ Err(err) => bug!("invalid loop id for break: {}", err),
+ },
+ hir::ExprKind::Continue(dest) => match dest.target_id {
+ Ok(loop_id) => ExprKind::Continue {
+ label: region::Scope { id: loop_id.local_id, data: region::ScopeData::Node },
+ },
+ Err(err) => bug!("invalid loop id for continue: {}", err),
+ },
+ hir::ExprKind::Match(ref discr, ref arms, _) => ExprKind::Match {
+ scrutinee: discr.to_ref(),
+ arms: arms.iter().map(|a| convert_arm(cx, a)).collect(),
+ },
+ hir::ExprKind::Loop(ref body, _, _) => {
+ ExprKind::Loop { body: block::to_expr_ref(cx, body) }
+ }
+ hir::ExprKind::Field(ref source, ..) => ExprKind::Field {
+ lhs: source.to_ref(),
+ name: Field::new(cx.tcx.field_index(expr.hir_id, cx.tables)),
+ },
+ hir::ExprKind::Cast(ref source, ref cast_ty) => {
+ // Check for a user-given type annotation on this `cast`
+ let user_provided_types = cx.tables.user_provided_types();
+ let user_ty = user_provided_types.get(cast_ty.hir_id);
+
+ debug!(
+ "cast({:?}) has ty w/ hir_id {:?} and user provided ty {:?}",
+ expr, cast_ty.hir_id, user_ty,
+ );
+
+ // Check to see if this cast is a "coercion cast", where the cast is actually done
+ // using a coercion (or is a no-op).
+ let cast = if cx.tables().is_coercion_cast(source.hir_id) {
+ // Convert the lexpr to a vexpr.
+ ExprKind::Use { source: source.to_ref() }
+ } else if cx.tables().expr_ty(source).is_region_ptr() {
+ // Special cased so that we can type check that the element
+ // type of the source matches the pointed to type of the
+ // destination.
+ ExprKind::Pointer { source: source.to_ref(), cast: PointerCast::ArrayToPointer }
+ } else {
+ // check whether this is casting an enum variant discriminant
+ // to prevent cycles, we refer to the discriminant initializer
+ // which is always an integer and thus doesn't need to know the
+ // enum's layout (or its tag type) to compute it during const eval
+ // Example:
+ // enum Foo {
+ // A,
+ // B = A as isize + 4,
+ // }
+ // The correct solution would be to add symbolic computations to miri,
+ // so we wouldn't have to compute and store the actual value
+ let var = if let hir::ExprKind::Path(ref qpath) = source.kind {
+ let res = cx.tables().qpath_res(qpath, source.hir_id);
+ cx.tables().node_type(source.hir_id).ty_adt_def().and_then(
+ |adt_def| match res {
+ Res::Def(
+ DefKind::Ctor(CtorOf::Variant, CtorKind::Const),
+ variant_ctor_id,
+ ) => {
+ let idx = adt_def.variant_index_with_ctor_id(variant_ctor_id);
+ let (d, o) = adt_def.discriminant_def_for_variant(idx);
+ use rustc::ty::util::IntTypeExt;
+ let ty = adt_def.repr.discr_type();
+ let ty = ty.to_ty(cx.tcx());
+ Some((d, o, ty))
+ }
+ _ => None,
+ },
+ )
+ } else {
+ None
+ };
+
+ let source = if let Some((did, offset, var_ty)) = var {
+ let mk_const = |literal| {
+ Expr {
+ temp_lifetime,
+ ty: var_ty,
+ span: expr.span,
+ kind: ExprKind::Literal { literal, user_ty: None },
+ }
+ .to_ref()
+ };
+ let offset = mk_const(ty::Const::from_bits(
+ cx.tcx,
+ offset as u128,
+ cx.param_env.and(var_ty),
+ ));
+ match did {
+ Some(did) => {
+ // in case we are offsetting from a computed discriminant
+ // and not the beginning of discriminants (which is always `0`)
+ let substs = InternalSubsts::identity_for_item(cx.tcx(), did);
+ let lhs = mk_const(cx.tcx().mk_const(ty::Const {
+ val: ty::ConstKind::Unevaluated(did, substs, None),
+ ty: var_ty,
+ }));
+ let bin = ExprKind::Binary { op: BinOp::Add, lhs, rhs: offset };
+ Expr { temp_lifetime, ty: var_ty, span: expr.span, kind: bin }.to_ref()
+ }
+ None => offset,
+ }
+ } else {
+ source.to_ref()
+ };
+
+ ExprKind::Cast { source }
+ };
+
+ if let Some(user_ty) = user_ty {
+ // NOTE: Creating a new Expr and wrapping a Cast inside of it may be
+ // inefficient, revisit this when performance becomes an issue.
+ let cast_expr = Expr { temp_lifetime, ty: expr_ty, span: expr.span, kind: cast };
+ debug!("make_mirror_unadjusted: (cast) user_ty={:?}", user_ty);
+
+ ExprKind::ValueTypeAscription {
+ source: cast_expr.to_ref(),
+ user_ty: Some(*user_ty),
+ }
+ } else {
+ cast
+ }
+ }
+ hir::ExprKind::Type(ref source, ref ty) => {
+ let user_provided_types = cx.tables.user_provided_types();
+ let user_ty = user_provided_types.get(ty.hir_id).map(|u_ty| *u_ty);
+ debug!("make_mirror_unadjusted: (type) user_ty={:?}", user_ty);
+ if source.is_syntactic_place_expr() {
+ ExprKind::PlaceTypeAscription { source: source.to_ref(), user_ty }
+ } else {
+ ExprKind::ValueTypeAscription { source: source.to_ref(), user_ty }
+ }
+ }
+ hir::ExprKind::DropTemps(ref source) => ExprKind::Use { source: source.to_ref() },
+ hir::ExprKind::Box(ref value) => ExprKind::Box { value: value.to_ref() },
+ hir::ExprKind::Array(ref fields) => ExprKind::Array { fields: fields.to_ref() },
+ hir::ExprKind::Tup(ref fields) => ExprKind::Tuple { fields: fields.to_ref() },
+
+ hir::ExprKind::Yield(ref v, _) => ExprKind::Yield { value: v.to_ref() },
+ hir::ExprKind::Err => unreachable!(),
+ };
+
+ Expr { temp_lifetime, ty: expr_ty, span: expr.span, kind }
+}
+
+fn user_substs_applied_to_res<'tcx>(
+ cx: &mut Cx<'_, 'tcx>,
+ hir_id: hir::HirId,
+ res: Res,
+) -> Option<ty::CanonicalUserType<'tcx>> {
+ debug!("user_substs_applied_to_res: res={:?}", res);
+ let user_provided_type = match res {
+ // A reference to something callable -- e.g., a fn, method, or
+ // a tuple-struct or tuple-variant. This has the type of a
+ // `Fn` but with the user-given substitutions.
+ Res::Def(DefKind::Fn, _)
+ | Res::Def(DefKind::Method, _)
+ | Res::Def(DefKind::Ctor(_, CtorKind::Fn), _)
+ | Res::Def(DefKind::Const, _)
+ | Res::Def(DefKind::AssocConst, _) => {
+ cx.tables().user_provided_types().get(hir_id).map(|u_ty| *u_ty)
+ }
+
+ // A unit struct/variant which is used as a value (e.g.,
+ // `None`). This has the type of the enum/struct that defines
+ // this variant -- but with the substitutions given by the
+ // user.
+ Res::Def(DefKind::Ctor(_, CtorKind::Const), _) => {
+ cx.user_substs_applied_to_ty_of_hir_id(hir_id)
+ }
+
+ // `Self` is used in expression as a tuple struct constructor or an unit struct constructor
+ Res::SelfCtor(_) => cx.user_substs_applied_to_ty_of_hir_id(hir_id),
+
+ _ => bug!("user_substs_applied_to_res: unexpected res {:?} at {:?}", res, hir_id),
+ };
+ debug!("user_substs_applied_to_res: user_provided_type={:?}", user_provided_type);
+ user_provided_type
+}
+
+fn method_callee<'a, 'tcx>(
+ cx: &mut Cx<'a, 'tcx>,
+ expr: &hir::Expr<'_>,
+ span: Span,
+ overloaded_callee: Option<(DefId, SubstsRef<'tcx>)>,
+) -> Expr<'tcx> {
+ let temp_lifetime = cx.region_scope_tree.temporary_scope(expr.hir_id.local_id);
+ let (def_id, substs, user_ty) = match overloaded_callee {
+ Some((def_id, substs)) => (def_id, substs, None),
+ None => {
+ let (kind, def_id) = cx
+ .tables()
+ .type_dependent_def(expr.hir_id)
+ .unwrap_or_else(|| span_bug!(expr.span, "no type-dependent def for method callee"));
+ let user_ty = user_substs_applied_to_res(cx, expr.hir_id, Res::Def(kind, def_id));
+ debug!("method_callee: user_ty={:?}", user_ty);
+ (def_id, cx.tables().node_substs(expr.hir_id), user_ty)
+ }
+ };
+ let ty = cx.tcx().mk_fn_def(def_id, substs);
+ Expr {
+ temp_lifetime,
+ ty,
+ span,
+ kind: ExprKind::Literal { literal: ty::Const::zero_sized(cx.tcx(), ty), user_ty },
+ }
+}
+
+trait ToBorrowKind {
+ fn to_borrow_kind(&self) -> BorrowKind;
+}
+
+impl ToBorrowKind for AutoBorrowMutability {
+ fn to_borrow_kind(&self) -> BorrowKind {
+ use rustc::ty::adjustment::AllowTwoPhase;
+ match *self {
+ AutoBorrowMutability::Mut { allow_two_phase_borrow } => BorrowKind::Mut {
+ allow_two_phase_borrow: match allow_two_phase_borrow {
+ AllowTwoPhase::Yes => true,
+ AllowTwoPhase::No => false,
+ },
+ },
+ AutoBorrowMutability::Not => BorrowKind::Shared,
+ }
+ }
+}
+
+impl ToBorrowKind for hir::Mutability {
+ fn to_borrow_kind(&self) -> BorrowKind {
+ match *self {
+ hir::Mutability::Mut => BorrowKind::Mut { allow_two_phase_borrow: false },
+ hir::Mutability::Not => BorrowKind::Shared,
+ }
+ }
+}
+
+fn convert_arm<'tcx>(cx: &mut Cx<'_, 'tcx>, arm: &'tcx hir::Arm<'tcx>) -> Arm<'tcx> {
+ Arm {
+ pattern: cx.pattern_from_hir(&arm.pat),
+ guard: match arm.guard {
+ Some(hir::Guard::If(ref e)) => Some(Guard::If(e.to_ref())),
+ _ => None,
+ },
+ body: arm.body.to_ref(),
+ lint_level: LintLevel::Explicit(arm.hir_id),
+ scope: region::Scope { id: arm.hir_id.local_id, data: region::ScopeData::Node },
+ span: arm.span,
+ }
+}
+
+fn convert_path_expr<'a, 'tcx>(
+ cx: &mut Cx<'a, 'tcx>,
+ expr: &'tcx hir::Expr<'tcx>,
+ res: Res,
+) -> ExprKind<'tcx> {
+ let substs = cx.tables().node_substs(expr.hir_id);
+ match res {
+ // A regular function, constructor function or a constant.
+ Res::Def(DefKind::Fn, _)
+ | Res::Def(DefKind::Method, _)
+ | Res::Def(DefKind::Ctor(_, CtorKind::Fn), _)
+ | Res::SelfCtor(..) => {
+ let user_ty = user_substs_applied_to_res(cx, expr.hir_id, res);
+ debug!("convert_path_expr: user_ty={:?}", user_ty);
+ ExprKind::Literal {
+ literal: ty::Const::zero_sized(cx.tcx, cx.tables().node_type(expr.hir_id)),
+ user_ty,
+ }
+ }
+
+ Res::Def(DefKind::ConstParam, def_id) => {
+ let hir_id = cx.tcx.hir().as_local_hir_id(def_id).unwrap();
+ let item_id = cx.tcx.hir().get_parent_node(hir_id);
+ let item_def_id = cx.tcx.hir().local_def_id(item_id);
+ let generics = cx.tcx.generics_of(item_def_id);
+ let local_def_id = cx.tcx.hir().local_def_id(hir_id);
+ let index = generics.param_def_id_to_index[&local_def_id];
+ let name = cx.tcx.hir().name(hir_id);
+ let val = ty::ConstKind::Param(ty::ParamConst::new(index, name));
+ ExprKind::Literal {
+ literal: cx.tcx.mk_const(ty::Const { val, ty: cx.tables().node_type(expr.hir_id) }),
+ user_ty: None,
+ }
+ }
+
+ Res::Def(DefKind::Const, def_id) | Res::Def(DefKind::AssocConst, def_id) => {
+ let user_ty = user_substs_applied_to_res(cx, expr.hir_id, res);
+ debug!("convert_path_expr: (const) user_ty={:?}", user_ty);
+ ExprKind::Literal {
+ literal: cx.tcx.mk_const(ty::Const {
+ val: ty::ConstKind::Unevaluated(def_id, substs, None),
+ ty: cx.tables().node_type(expr.hir_id),
+ }),
+ user_ty,
+ }
+ }
+
+ Res::Def(DefKind::Ctor(_, CtorKind::Const), def_id) => {
+ let user_provided_types = cx.tables.user_provided_types();
+ let user_provided_type = user_provided_types.get(expr.hir_id).map(|u_ty| *u_ty);
+ debug!("convert_path_expr: user_provided_type={:?}", user_provided_type);
+ let ty = cx.tables().node_type(expr.hir_id);
+ match ty.kind {
+ // A unit struct/variant which is used as a value.
+ // We return a completely different ExprKind here to account for this special case.
+ ty::Adt(adt_def, substs) => ExprKind::Adt {
+ adt_def,
+ variant_index: adt_def.variant_index_with_ctor_id(def_id),
+ substs,
+ user_ty: user_provided_type,
+ fields: vec![],
+ base: None,
+ },
+ _ => bug!("unexpected ty: {:?}", ty),
+ }
+ }
+
+ // We encode uses of statics as a `*&STATIC` where the `&STATIC` part is
+ // a constant reference (or constant raw pointer for `static mut`) in MIR
+ Res::Def(DefKind::Static, id) => {
+ let ty = cx.tcx.static_ptr_ty(id);
+ let ptr = cx.tcx.alloc_map.lock().create_static_alloc(id);
+ let temp_lifetime = cx.region_scope_tree.temporary_scope(expr.hir_id.local_id);
+ ExprKind::Deref {
+ arg: Expr {
+ ty,
+ temp_lifetime,
+ span: expr.span,
+ kind: ExprKind::StaticRef {
+ literal: ty::Const::from_scalar(cx.tcx, Scalar::Ptr(ptr.into()), ty),
+ def_id: id,
+ },
+ }
+ .to_ref(),
+ }
+ }
+
+ Res::Local(var_hir_id) => convert_var(cx, expr, var_hir_id),
+
+ _ => span_bug!(expr.span, "res `{:?}` not yet implemented", res),
+ }
+}
+
+fn convert_var<'tcx>(
+ cx: &mut Cx<'_, 'tcx>,
+ expr: &'tcx hir::Expr<'tcx>,
+ var_hir_id: hir::HirId,
+) -> ExprKind<'tcx> {
+ let upvar_index = cx
+ .tables()
+ .upvar_list
+ .get(&cx.body_owner)
+ .and_then(|upvars| upvars.get_full(&var_hir_id).map(|(i, _, _)| i));
+
+ debug!(
+ "convert_var({:?}): upvar_index={:?}, body_owner={:?}",
+ var_hir_id, upvar_index, cx.body_owner
+ );
+
+ let temp_lifetime = cx.region_scope_tree.temporary_scope(expr.hir_id.local_id);
+
+ match upvar_index {
+ None => ExprKind::VarRef { id: var_hir_id },
+
+ Some(upvar_index) => {
+ let closure_def_id = cx.body_owner;
+ let upvar_id = ty::UpvarId {
+ var_path: ty::UpvarPath { hir_id: var_hir_id },
+ closure_expr_id: LocalDefId::from_def_id(closure_def_id),
+ };
+ let var_ty = cx.tables().node_type(var_hir_id);
+
+ // FIXME free regions in closures are not right
+ let closure_ty = cx
+ .tables()
+ .node_type(cx.tcx.hir().local_def_id_to_hir_id(upvar_id.closure_expr_id));
+
+ // FIXME we're just hard-coding the idea that the
+ // signature will be &self or &mut self and hence will
+ // have a bound region with number 0
+ let region = ty::ReFree(ty::FreeRegion {
+ scope: closure_def_id,
+ bound_region: ty::BoundRegion::BrAnon(0),
+ });
+ let region = cx.tcx.mk_region(region);
+
+ let self_expr = if let ty::Closure(_, closure_substs) = closure_ty.kind {
+ match cx.infcx.closure_kind(closure_def_id, closure_substs).unwrap() {
+ ty::ClosureKind::Fn => {
+ let ref_closure_ty = cx.tcx.mk_ref(
+ region,
+ ty::TypeAndMut { ty: closure_ty, mutbl: hir::Mutability::Not },
+ );
+ Expr {
+ ty: closure_ty,
+ temp_lifetime,
+ span: expr.span,
+ kind: ExprKind::Deref {
+ arg: Expr {
+ ty: ref_closure_ty,
+ temp_lifetime,
+ span: expr.span,
+ kind: ExprKind::SelfRef,
+ }
+ .to_ref(),
+ },
+ }
+ }
+ ty::ClosureKind::FnMut => {
+ let ref_closure_ty = cx.tcx.mk_ref(
+ region,
+ ty::TypeAndMut { ty: closure_ty, mutbl: hir::Mutability::Mut },
+ );
+ Expr {
+ ty: closure_ty,
+ temp_lifetime,
+ span: expr.span,
+ kind: ExprKind::Deref {
+ arg: Expr {
+ ty: ref_closure_ty,
+ temp_lifetime,
+ span: expr.span,
+ kind: ExprKind::SelfRef,
+ }
+ .to_ref(),
+ },
+ }
+ }
+ ty::ClosureKind::FnOnce => Expr {
+ ty: closure_ty,
+ temp_lifetime,
+ span: expr.span,
+ kind: ExprKind::SelfRef,
+ },
+ }
+ } else {
+ Expr { ty: closure_ty, temp_lifetime, span: expr.span, kind: ExprKind::SelfRef }
+ };
+
+ // at this point we have `self.n`, which loads up the upvar
+ let field_kind =
+ ExprKind::Field { lhs: self_expr.to_ref(), name: Field::new(upvar_index) };
+
+ // ...but the upvar might be an `&T` or `&mut T` capture, at which
+ // point we need an implicit deref
+ match cx.tables().upvar_capture(upvar_id) {
+ ty::UpvarCapture::ByValue => field_kind,
+ ty::UpvarCapture::ByRef(borrow) => ExprKind::Deref {
+ arg: Expr {
+ temp_lifetime,
+ ty: cx.tcx.mk_ref(
+ borrow.region,
+ ty::TypeAndMut { ty: var_ty, mutbl: borrow.kind.to_mutbl_lossy() },
+ ),
+ span: expr.span,
+ kind: field_kind,
+ }
+ .to_ref(),
+ },
+ }
+ }
+ }
+}
+
+fn bin_op(op: hir::BinOpKind) -> BinOp {
+ match op {
+ hir::BinOpKind::Add => BinOp::Add,
+ hir::BinOpKind::Sub => BinOp::Sub,
+ hir::BinOpKind::Mul => BinOp::Mul,
+ hir::BinOpKind::Div => BinOp::Div,
+ hir::BinOpKind::Rem => BinOp::Rem,
+ hir::BinOpKind::BitXor => BinOp::BitXor,
+ hir::BinOpKind::BitAnd => BinOp::BitAnd,
+ hir::BinOpKind::BitOr => BinOp::BitOr,
+ hir::BinOpKind::Shl => BinOp::Shl,
+ hir::BinOpKind::Shr => BinOp::Shr,
+ hir::BinOpKind::Eq => BinOp::Eq,
+ hir::BinOpKind::Lt => BinOp::Lt,
+ hir::BinOpKind::Le => BinOp::Le,
+ hir::BinOpKind::Ne => BinOp::Ne,
+ hir::BinOpKind::Ge => BinOp::Ge,
+ hir::BinOpKind::Gt => BinOp::Gt,
+ _ => bug!("no equivalent for ast binop {:?}", op),
+ }
+}
+
+fn overloaded_operator<'a, 'tcx>(
+ cx: &mut Cx<'a, 'tcx>,
+ expr: &'tcx hir::Expr<'tcx>,
+ args: Vec<ExprRef<'tcx>>,
+) -> ExprKind<'tcx> {
+ let fun = method_callee(cx, expr, expr.span, None);
+ ExprKind::Call { ty: fun.ty, fun: fun.to_ref(), args, from_hir_call: false }
+}
+
+fn overloaded_place<'a, 'tcx>(
+ cx: &mut Cx<'a, 'tcx>,
+ expr: &'tcx hir::Expr<'tcx>,
+ place_ty: Ty<'tcx>,
+ overloaded_callee: Option<(DefId, SubstsRef<'tcx>)>,
+ args: Vec<ExprRef<'tcx>>,
+) -> ExprKind<'tcx> {
+ // For an overloaded *x or x[y] expression of type T, the method
+ // call returns an &T and we must add the deref so that the types
+ // line up (this is because `*x` and `x[y]` represent places):
+
+ let recv_ty = match args[0] {
+ ExprRef::Hair(e) => cx.tables().expr_ty_adjusted(e),
+ ExprRef::Mirror(ref e) => e.ty,
+ };
+
+ // Reconstruct the output assuming it's a reference with the
+ // same region and mutability as the receiver. This holds for
+ // `Deref(Mut)::Deref(_mut)` and `Index(Mut)::index(_mut)`.
+ let (region, mutbl) = match recv_ty.kind {
+ ty::Ref(region, _, mutbl) => (region, mutbl),
+ _ => span_bug!(expr.span, "overloaded_place: receiver is not a reference"),
+ };
+ let ref_ty = cx.tcx.mk_ref(region, ty::TypeAndMut { ty: place_ty, mutbl });
+
+ // construct the complete expression `foo()` for the overloaded call,
+ // which will yield the &T type
+ let temp_lifetime = cx.region_scope_tree.temporary_scope(expr.hir_id.local_id);
+ let fun = method_callee(cx, expr, expr.span, overloaded_callee);
+ let ref_expr = Expr {
+ temp_lifetime,
+ ty: ref_ty,
+ span: expr.span,
+ kind: ExprKind::Call { ty: fun.ty, fun: fun.to_ref(), args, from_hir_call: false },
+ };
+
+ // construct and return a deref wrapper `*foo()`
+ ExprKind::Deref { arg: ref_expr.to_ref() }
+}
+
+fn capture_upvar<'tcx>(
+ cx: &mut Cx<'_, 'tcx>,
+ closure_expr: &'tcx hir::Expr<'tcx>,
+ var_hir_id: hir::HirId,
+ upvar_ty: Ty<'tcx>,
+) -> ExprRef<'tcx> {
+ let upvar_id = ty::UpvarId {
+ var_path: ty::UpvarPath { hir_id: var_hir_id },
+ closure_expr_id: cx.tcx.hir().local_def_id(closure_expr.hir_id).to_local(),
+ };
+ let upvar_capture = cx.tables().upvar_capture(upvar_id);
+ let temp_lifetime = cx.region_scope_tree.temporary_scope(closure_expr.hir_id.local_id);
+ let var_ty = cx.tables().node_type(var_hir_id);
+ let captured_var = Expr {
+ temp_lifetime,
+ ty: var_ty,
+ span: closure_expr.span,
+ kind: convert_var(cx, closure_expr, var_hir_id),
+ };
+ match upvar_capture {
+ ty::UpvarCapture::ByValue => captured_var.to_ref(),
+ ty::UpvarCapture::ByRef(upvar_borrow) => {
+ let borrow_kind = match upvar_borrow.kind {
+ ty::BorrowKind::ImmBorrow => BorrowKind::Shared,
+ ty::BorrowKind::UniqueImmBorrow => BorrowKind::Unique,
+ ty::BorrowKind::MutBorrow => BorrowKind::Mut { allow_two_phase_borrow: false },
+ };
+ Expr {
+ temp_lifetime,
+ ty: upvar_ty,
+ span: closure_expr.span,
+ kind: ExprKind::Borrow { borrow_kind, arg: captured_var.to_ref() },
+ }
+ .to_ref()
+ }
+ }
+}
+
+/// Converts a list of named fields (i.e., for struct-like struct/enum ADTs) into FieldExprRef.
+fn field_refs<'a, 'tcx>(
+ cx: &mut Cx<'a, 'tcx>,
+ fields: &'tcx [hir::Field<'tcx>],
+) -> Vec<FieldExprRef<'tcx>> {
+ fields
+ .iter()
+ .map(|field| FieldExprRef {
+ name: Field::new(cx.tcx.field_index(field.hir_id, cx.tables)),
+ expr: field.expr.to_ref(),
+ })
+ .collect()
+}
--- /dev/null
+//! This module contains the fcuntaiontliy to convert from the wacky tcx data
+//! structures into the HAIR. The `builder` is generally ignorant of the tcx,
+//! etc., and instead goes through the `Cx` for most of its work.
+
+use crate::hair::util::UserAnnotatedTyHelpers;
+use crate::hair::*;
+
+use crate::hair::constant::{lit_to_const, LitToConstError};
+use rustc::infer::InferCtxt;
+use rustc::middle::region;
+use rustc::ty::layout::VariantIdx;
+use rustc::ty::subst::Subst;
+use rustc::ty::subst::{GenericArg, InternalSubsts};
+use rustc::ty::{self, Ty, TyCtxt};
+use rustc_hir as hir;
+use rustc_hir::def_id::DefId;
+use rustc_hir::Node;
+use rustc_index::vec::Idx;
+use rustc_span::symbol::{sym, Symbol};
+use syntax::ast;
+use syntax::attr;
+
+#[derive(Clone)]
+crate struct Cx<'a, 'tcx> {
+ tcx: TyCtxt<'tcx>,
+ infcx: &'a InferCtxt<'a, 'tcx>,
+
+ crate root_lint_level: hir::HirId,
+ crate param_env: ty::ParamEnv<'tcx>,
+
+ /// Identity `InternalSubsts` for use with const-evaluation.
+ crate identity_substs: &'tcx InternalSubsts<'tcx>,
+
+ crate region_scope_tree: &'tcx region::ScopeTree,
+ crate tables: &'a ty::TypeckTables<'tcx>,
+
+ /// This is `Constness::Const` if we are compiling a `static`,
+ /// `const`, or the body of a `const fn`.
+ constness: hir::Constness,
+
+ /// The `DefId` of the owner of this body.
+ body_owner: DefId,
+
+ /// What kind of body is being compiled.
+ crate body_owner_kind: hir::BodyOwnerKind,
+
+ /// Whether this constant/function needs overflow checks.
+ check_overflow: bool,
+
+ /// See field with the same name on `mir::Body`.
+ control_flow_destroyed: Vec<(Span, String)>,
+}
+
+impl<'a, 'tcx> Cx<'a, 'tcx> {
+ crate fn new(infcx: &'a InferCtxt<'a, 'tcx>, src_id: hir::HirId) -> Cx<'a, 'tcx> {
+ let tcx = infcx.tcx;
+ let src_def_id = tcx.hir().local_def_id(src_id);
+ let tables = tcx.typeck_tables_of(src_def_id);
+ let body_owner_kind = tcx.hir().body_owner_kind(src_id);
+
+ let constness = match body_owner_kind {
+ hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => hir::Constness::Const,
+ hir::BodyOwnerKind::Closure | hir::BodyOwnerKind::Fn => hir::Constness::NotConst,
+ };
+
+ let attrs = tcx.hir().attrs(src_id);
+
+ // Some functions always have overflow checks enabled,
+ // however, they may not get codegen'd, depending on
+ // the settings for the crate they are codegened in.
+ let mut check_overflow = attr::contains_name(attrs, sym::rustc_inherit_overflow_checks);
+
+ // Respect -C overflow-checks.
+ check_overflow |= tcx.sess.overflow_checks();
+
+ // Constants always need overflow checks.
+ check_overflow |= constness == hir::Constness::Const;
+
+ Cx {
+ tcx,
+ infcx,
+ root_lint_level: src_id,
+ param_env: tcx.param_env(src_def_id),
+ identity_substs: InternalSubsts::identity_for_item(tcx, src_def_id),
+ region_scope_tree: tcx.region_scope_tree(src_def_id),
+ tables,
+ constness,
+ body_owner: src_def_id,
+ body_owner_kind,
+ check_overflow,
+ control_flow_destroyed: Vec::new(),
+ }
+ }
+
+ crate fn control_flow_destroyed(self) -> Vec<(Span, String)> {
+ self.control_flow_destroyed
+ }
+}
+
+impl<'a, 'tcx> Cx<'a, 'tcx> {
+ /// Normalizes `ast` into the appropriate "mirror" type.
+ crate fn mirror<M: Mirror<'tcx>>(&mut self, ast: M) -> M::Output {
+ ast.make_mirror(self)
+ }
+
+ crate fn usize_ty(&mut self) -> Ty<'tcx> {
+ self.tcx.types.usize
+ }
+
+ crate fn usize_literal(&mut self, value: u64) -> &'tcx ty::Const<'tcx> {
+ ty::Const::from_usize(self.tcx, value)
+ }
+
+ crate fn bool_ty(&mut self) -> Ty<'tcx> {
+ self.tcx.types.bool
+ }
+
+ crate fn unit_ty(&mut self) -> Ty<'tcx> {
+ self.tcx.mk_unit()
+ }
+
+ crate fn true_literal(&mut self) -> &'tcx ty::Const<'tcx> {
+ ty::Const::from_bool(self.tcx, true)
+ }
+
+ crate fn false_literal(&mut self) -> &'tcx ty::Const<'tcx> {
+ ty::Const::from_bool(self.tcx, false)
+ }
+
+ crate fn const_eval_literal(
+ &mut self,
+ lit: &'tcx ast::LitKind,
+ ty: Ty<'tcx>,
+ sp: Span,
+ neg: bool,
+ ) -> &'tcx ty::Const<'tcx> {
+ trace!("const_eval_literal: {:#?}, {:?}, {:?}, {:?}", lit, ty, sp, neg);
+
+ match lit_to_const(lit, self.tcx, ty, neg) {
+ Ok(c) => c,
+ Err(LitToConstError::UnparseableFloat) => {
+ // FIXME(#31407) this is only necessary because float parsing is buggy
+ self.tcx.sess.span_err(sp, "could not evaluate float literal (see issue #31407)");
+ // create a dummy value and continue compiling
+ Const::from_bits(self.tcx, 0, self.param_env.and(ty))
+ }
+ Err(LitToConstError::Reported) => {
+ // create a dummy value and continue compiling
+ Const::from_bits(self.tcx, 0, self.param_env.and(ty))
+ }
+ }
+ }
+
+ crate fn pattern_from_hir(&mut self, p: &hir::Pat<'_>) -> Pat<'tcx> {
+ let p = match self.tcx.hir().get(p.hir_id) {
+ Node::Pat(p) | Node::Binding(p) => p,
+ node => bug!("pattern became {:?}", node),
+ };
+ Pat::from_hir(self.tcx, self.param_env, self.tables(), p)
+ }
+
+ crate fn trait_method(
+ &mut self,
+ trait_def_id: DefId,
+ method_name: Symbol,
+ self_ty: Ty<'tcx>,
+ params: &[GenericArg<'tcx>],
+ ) -> &'tcx ty::Const<'tcx> {
+ let substs = self.tcx.mk_substs_trait(self_ty, params);
+ for item in self.tcx.associated_items(trait_def_id) {
+ // The unhygienic comparison here is acceptable because this is only
+ // used on known traits.
+ if item.kind == ty::AssocKind::Method && item.ident.name == method_name {
+ let method_ty = self.tcx.type_of(item.def_id);
+ let method_ty = method_ty.subst(self.tcx, substs);
+ return ty::Const::zero_sized(self.tcx, method_ty);
+ }
+ }
+
+ bug!("found no method `{}` in `{:?}`", method_name, trait_def_id);
+ }
+
+ crate fn all_fields(&mut self, adt_def: &ty::AdtDef, variant_index: VariantIdx) -> Vec<Field> {
+ (0..adt_def.variants[variant_index].fields.len()).map(Field::new).collect()
+ }
+
+ crate fn needs_drop(&mut self, ty: Ty<'tcx>) -> bool {
+ ty.needs_drop(self.tcx, self.param_env)
+ }
+
+ crate fn tcx(&self) -> TyCtxt<'tcx> {
+ self.tcx
+ }
+
+ crate fn tables(&self) -> &'a ty::TypeckTables<'tcx> {
+ self.tables
+ }
+
+ crate fn check_overflow(&self) -> bool {
+ self.check_overflow
+ }
+
+ crate fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>, span: Span) -> bool {
+ self.infcx.type_is_copy_modulo_regions(self.param_env, ty, span)
+ }
+}
+
+impl<'tcx> UserAnnotatedTyHelpers<'tcx> for Cx<'_, 'tcx> {
+ fn tcx(&self) -> TyCtxt<'tcx> {
+ self.tcx()
+ }
+
+ fn tables(&self) -> &ty::TypeckTables<'tcx> {
+ self.tables()
+ }
+}
+
+mod block;
+mod expr;
+mod to_ref;
--- /dev/null
+use crate::hair::*;
+
+use rustc_hir as hir;
+
+crate trait ToRef {
+ type Output;
+ fn to_ref(self) -> Self::Output;
+}
+
+impl<'tcx> ToRef for &'tcx hir::Expr<'tcx> {
+ type Output = ExprRef<'tcx>;
+
+ fn to_ref(self) -> ExprRef<'tcx> {
+ ExprRef::Hair(self)
+ }
+}
+
+impl<'tcx> ToRef for &'tcx &'tcx hir::Expr<'tcx> {
+ type Output = ExprRef<'tcx>;
+
+ fn to_ref(self) -> ExprRef<'tcx> {
+ ExprRef::Hair(&**self)
+ }
+}
+
+impl<'tcx> ToRef for Expr<'tcx> {
+ type Output = ExprRef<'tcx>;
+
+ fn to_ref(self) -> ExprRef<'tcx> {
+ ExprRef::Mirror(Box::new(self))
+ }
+}
+
+impl<'tcx, T, U> ToRef for &'tcx Option<T>
+where
+ &'tcx T: ToRef<Output = U>,
+{
+ type Output = Option<U>;
+
+ fn to_ref(self) -> Option<U> {
+ self.as_ref().map(|expr| expr.to_ref())
+ }
+}
+
+impl<'tcx, T, U> ToRef for &'tcx Vec<T>
+where
+ &'tcx T: ToRef<Output = U>,
+{
+ type Output = Vec<U>;
+
+ fn to_ref(self) -> Vec<U> {
+ self.iter().map(|expr| expr.to_ref()).collect()
+ }
+}
+
+impl<'tcx, T, U> ToRef for &'tcx [T]
+where
+ &'tcx T: ToRef<Output = U>,
+{
+ type Output = Vec<U>;
+
+ fn to_ref(self) -> Vec<U> {
+ self.iter().map(|expr| expr.to_ref()).collect()
+ }
+}
--- /dev/null
+//! The MIR is built from some high-level abstract IR
+//! (HAIR). This section defines the HAIR along with a trait for
+//! accessing it. The intention is to allow MIR construction to be
+//! unit-tested and separated from the Rust source and compiler data
+//! structures.
+
+use self::cx::Cx;
+use rustc::infer::canonical::Canonical;
+use rustc::middle::region;
+use rustc::mir::{BinOp, BorrowKind, Field, UnOp};
+use rustc::ty::adjustment::PointerCast;
+use rustc::ty::layout::VariantIdx;
+use rustc::ty::subst::SubstsRef;
+use rustc::ty::{AdtDef, Const, Ty, UpvarSubsts, UserType};
+use rustc_hir as hir;
+use rustc_hir::def_id::DefId;
+use rustc_span::Span;
+
+mod constant;
+crate mod cx;
+
+crate mod pattern;
+crate use self::pattern::PatTyProj;
+crate use self::pattern::{BindingMode, FieldPat, Pat, PatKind, PatRange};
+
+mod util;
+
+#[derive(Copy, Clone, Debug)]
+crate enum LintLevel {
+ Inherited,
+ Explicit(hir::HirId),
+}
+
+#[derive(Clone, Debug)]
+crate struct Block<'tcx> {
+ crate targeted_by_break: bool,
+ crate region_scope: region::Scope,
+ crate opt_destruction_scope: Option<region::Scope>,
+ crate span: Span,
+ crate stmts: Vec<StmtRef<'tcx>>,
+ crate expr: Option<ExprRef<'tcx>>,
+ crate safety_mode: BlockSafety,
+}
+
+#[derive(Copy, Clone, Debug)]
+crate enum BlockSafety {
+ Safe,
+ ExplicitUnsafe(hir::HirId),
+ PushUnsafe,
+ PopUnsafe,
+}
+
+#[derive(Clone, Debug)]
+crate enum StmtRef<'tcx> {
+ Mirror(Box<Stmt<'tcx>>),
+}
+
+#[derive(Clone, Debug)]
+crate struct Stmt<'tcx> {
+ crate kind: StmtKind<'tcx>,
+ crate opt_destruction_scope: Option<region::Scope>,
+}
+
+#[derive(Clone, Debug)]
+crate enum StmtKind<'tcx> {
+ Expr {
+ /// scope for this statement; may be used as lifetime of temporaries
+ scope: region::Scope,
+
+ /// expression being evaluated in this statement
+ expr: ExprRef<'tcx>,
+ },
+
+ Let {
+ /// scope for variables bound in this let; covers this and
+ /// remaining statements in block
+ remainder_scope: region::Scope,
+
+ /// scope for the initialization itself; might be used as
+ /// lifetime of temporaries
+ init_scope: region::Scope,
+
+ /// `let <PAT> = ...`
+ ///
+ /// if a type is included, it is added as an ascription pattern
+ pattern: Pat<'tcx>,
+
+ /// let pat: ty = <INIT> ...
+ initializer: Option<ExprRef<'tcx>>,
+
+ /// the lint level for this let-statement
+ lint_level: LintLevel,
+ },
+}
+
+// `Expr` is used a lot. Make sure it doesn't unintentionally get bigger.
+#[cfg(target_arch = "x86_64")]
+rustc_data_structures::static_assert_size!(Expr<'_>, 168);
+
+/// The Hair trait implementor lowers their expressions (`&'tcx H::Expr`)
+/// into instances of this `Expr` enum. This lowering can be done
+/// basically as lazily or as eagerly as desired: every recursive
+/// reference to an expression in this enum is an `ExprRef<'tcx>`, which
+/// may in turn be another instance of this enum (boxed), or else an
+/// unlowered `&'tcx H::Expr`. Note that instances of `Expr` are very
+/// short-lived. They are created by `Hair::to_expr`, analyzed and
+/// converted into MIR, and then discarded.
+///
+/// If you compare `Expr` to the full compiler AST, you will see it is
+/// a good bit simpler. In fact, a number of the more straight-forward
+/// MIR simplifications are already done in the impl of `Hair`. For
+/// example, method calls and overloaded operators are absent: they are
+/// expected to be converted into `Expr::Call` instances.
+#[derive(Clone, Debug)]
+crate struct Expr<'tcx> {
+ /// type of this expression
+ crate ty: Ty<'tcx>,
+
+ /// lifetime of this expression if it should be spilled into a
+ /// temporary; should be None only if in a constant context
+ crate temp_lifetime: Option<region::Scope>,
+
+ /// span of the expression in the source
+ crate span: Span,
+
+ /// kind of expression
+ crate kind: ExprKind<'tcx>,
+}
+
+#[derive(Clone, Debug)]
+crate enum ExprKind<'tcx> {
+ Scope {
+ region_scope: region::Scope,
+ lint_level: LintLevel,
+ value: ExprRef<'tcx>,
+ },
+ Box {
+ value: ExprRef<'tcx>,
+ },
+ Call {
+ ty: Ty<'tcx>,
+ fun: ExprRef<'tcx>,
+ args: Vec<ExprRef<'tcx>>,
+ // Whether this is from a call in HIR, rather than from an overloaded
+ // operator. True for overloaded function call.
+ from_hir_call: bool,
+ },
+ Deref {
+ arg: ExprRef<'tcx>,
+ }, // NOT overloaded!
+ Binary {
+ op: BinOp,
+ lhs: ExprRef<'tcx>,
+ rhs: ExprRef<'tcx>,
+ }, // NOT overloaded!
+ LogicalOp {
+ op: LogicalOp,
+ lhs: ExprRef<'tcx>,
+ rhs: ExprRef<'tcx>,
+ }, // NOT overloaded!
+ // LogicalOp is distinct from BinaryOp because of lazy evaluation of the operands.
+ Unary {
+ op: UnOp,
+ arg: ExprRef<'tcx>,
+ }, // NOT overloaded!
+ Cast {
+ source: ExprRef<'tcx>,
+ },
+ Use {
+ source: ExprRef<'tcx>,
+ }, // Use a lexpr to get a vexpr.
+ NeverToAny {
+ source: ExprRef<'tcx>,
+ },
+ Pointer {
+ cast: PointerCast,
+ source: ExprRef<'tcx>,
+ },
+ Loop {
+ body: ExprRef<'tcx>,
+ },
+ Match {
+ scrutinee: ExprRef<'tcx>,
+ arms: Vec<Arm<'tcx>>,
+ },
+ Block {
+ body: &'tcx hir::Block<'tcx>,
+ },
+ Assign {
+ lhs: ExprRef<'tcx>,
+ rhs: ExprRef<'tcx>,
+ },
+ AssignOp {
+ op: BinOp,
+ lhs: ExprRef<'tcx>,
+ rhs: ExprRef<'tcx>,
+ },
+ Field {
+ lhs: ExprRef<'tcx>,
+ name: Field,
+ },
+ Index {
+ lhs: ExprRef<'tcx>,
+ index: ExprRef<'tcx>,
+ },
+ VarRef {
+ id: hir::HirId,
+ },
+ /// first argument, used for self in a closure
+ SelfRef,
+ Borrow {
+ borrow_kind: BorrowKind,
+ arg: ExprRef<'tcx>,
+ },
+ /// A `&raw [const|mut] $place_expr` raw borrow resulting in type `*[const|mut] T`.
+ AddressOf {
+ mutability: hir::Mutability,
+ arg: ExprRef<'tcx>,
+ },
+ Break {
+ label: region::Scope,
+ value: Option<ExprRef<'tcx>>,
+ },
+ Continue {
+ label: region::Scope,
+ },
+ Return {
+ value: Option<ExprRef<'tcx>>,
+ },
+ Repeat {
+ value: ExprRef<'tcx>,
+ count: u64,
+ },
+ Array {
+ fields: Vec<ExprRef<'tcx>>,
+ },
+ Tuple {
+ fields: Vec<ExprRef<'tcx>>,
+ },
+ Adt {
+ adt_def: &'tcx AdtDef,
+ variant_index: VariantIdx,
+ substs: SubstsRef<'tcx>,
+
+ /// Optional user-given substs: for something like `let x =
+ /// Bar::<T> { ... }`.
+ user_ty: Option<Canonical<'tcx, UserType<'tcx>>>,
+
+ fields: Vec<FieldExprRef<'tcx>>,
+ base: Option<FruInfo<'tcx>>,
+ },
+ PlaceTypeAscription {
+ source: ExprRef<'tcx>,
+ /// Type that the user gave to this expression
+ user_ty: Option<Canonical<'tcx, UserType<'tcx>>>,
+ },
+ ValueTypeAscription {
+ source: ExprRef<'tcx>,
+ /// Type that the user gave to this expression
+ user_ty: Option<Canonical<'tcx, UserType<'tcx>>>,
+ },
+ Closure {
+ closure_id: DefId,
+ substs: UpvarSubsts<'tcx>,
+ upvars: Vec<ExprRef<'tcx>>,
+ movability: Option<hir::Movability>,
+ },
+ Literal {
+ literal: &'tcx Const<'tcx>,
+ user_ty: Option<Canonical<'tcx, UserType<'tcx>>>,
+ },
+ /// A literal containing the address of a `static`.
+ ///
+ /// This is only distinguished from `Literal` so that we can register some
+ /// info for diagnostics.
+ StaticRef {
+ literal: &'tcx Const<'tcx>,
+ def_id: DefId,
+ },
+ InlineAsm {
+ asm: &'tcx hir::InlineAsmInner,
+ outputs: Vec<ExprRef<'tcx>>,
+ inputs: Vec<ExprRef<'tcx>>,
+ },
+ Yield {
+ value: ExprRef<'tcx>,
+ },
+}
+
+#[derive(Clone, Debug)]
+crate enum ExprRef<'tcx> {
+ Hair(&'tcx hir::Expr<'tcx>),
+ Mirror(Box<Expr<'tcx>>),
+}
+
+#[derive(Clone, Debug)]
+crate struct FieldExprRef<'tcx> {
+ crate name: Field,
+ crate expr: ExprRef<'tcx>,
+}
+
+#[derive(Clone, Debug)]
+crate struct FruInfo<'tcx> {
+ crate base: ExprRef<'tcx>,
+ crate field_types: Vec<Ty<'tcx>>,
+}
+
+#[derive(Clone, Debug)]
+crate struct Arm<'tcx> {
+ crate pattern: Pat<'tcx>,
+ crate guard: Option<Guard<'tcx>>,
+ crate body: ExprRef<'tcx>,
+ crate lint_level: LintLevel,
+ crate scope: region::Scope,
+ crate span: Span,
+}
+
+impl<'tcx> Arm<'tcx> {
+ // HACK(or_patterns; Centril | dlrobertson): Remove this and
+ // correctly handle each case in which this method is used.
+ crate fn top_pats_hack(&self) -> &[Pat<'tcx>] {
+ match &*self.pattern.kind {
+ PatKind::Or { pats } => pats,
+ _ => std::slice::from_ref(&self.pattern),
+ }
+ }
+}
+
+#[derive(Clone, Debug)]
+crate enum Guard<'tcx> {
+ If(ExprRef<'tcx>),
+}
+
+#[derive(Copy, Clone, Debug)]
+crate enum LogicalOp {
+ And,
+ Or,
+}
+
+impl<'tcx> ExprRef<'tcx> {
+ crate fn span(&self) -> Span {
+ match self {
+ ExprRef::Hair(expr) => expr.span,
+ ExprRef::Mirror(expr) => expr.span,
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// The Mirror trait
+
+/// "Mirroring" is the process of converting from a HIR type into one
+/// of the HAIR types defined in this file. This is basically a "on
+/// the fly" desugaring step that hides a lot of the messiness in the
+/// tcx. For example, the mirror of a `&'tcx hir::Expr` is an
+/// `Expr<'tcx>`.
+///
+/// Mirroring is gradual: when you mirror an outer expression like `e1
+/// + e2`, the references to the inner expressions `e1` and `e2` are
+/// `ExprRef<'tcx>` instances, and they may or may not be eagerly
+/// mirrored. This allows a single AST node from the compiler to
+/// expand into one or more Hair nodes, which lets the Hair nodes be
+/// simpler.
+crate trait Mirror<'tcx> {
+ type Output;
+
+ fn make_mirror(self, cx: &mut Cx<'_, 'tcx>) -> Self::Output;
+}
+
+impl<'tcx> Mirror<'tcx> for Expr<'tcx> {
+ type Output = Expr<'tcx>;
+
+ fn make_mirror(self, _: &mut Cx<'_, 'tcx>) -> Expr<'tcx> {
+ self
+ }
+}
+
+impl<'tcx> Mirror<'tcx> for ExprRef<'tcx> {
+ type Output = Expr<'tcx>;
+
+ fn make_mirror(self, hir: &mut Cx<'_, 'tcx>) -> Expr<'tcx> {
+ match self {
+ ExprRef::Hair(h) => h.make_mirror(hir),
+ ExprRef::Mirror(m) => *m,
+ }
+ }
+}
+
+impl<'tcx> Mirror<'tcx> for Stmt<'tcx> {
+ type Output = Stmt<'tcx>;
+
+ fn make_mirror(self, _: &mut Cx<'_, 'tcx>) -> Stmt<'tcx> {
+ self
+ }
+}
+
+impl<'tcx> Mirror<'tcx> for StmtRef<'tcx> {
+ type Output = Stmt<'tcx>;
+
+ fn make_mirror(self, _: &mut Cx<'_, 'tcx>) -> Stmt<'tcx> {
+ match self {
+ StmtRef::Mirror(m) => *m,
+ }
+ }
+}
+
+impl<'tcx> Mirror<'tcx> for Block<'tcx> {
+ type Output = Block<'tcx>;
+
+ fn make_mirror(self, _: &mut Cx<'_, 'tcx>) -> Block<'tcx> {
+ self
+ }
+}
--- /dev/null
+/// Note: most tests relevant to this file can be found (at the time of writing)
+/// in src/tests/ui/pattern/usefulness.
+///
+/// This file includes the logic for exhaustiveness and usefulness checking for
+/// pattern-matching. Specifically, given a list of patterns for a type, we can
+/// tell whether:
+/// (a) the patterns cover every possible constructor for the type [exhaustiveness]
+/// (b) each pattern is necessary [usefulness]
+///
+/// The algorithm implemented here is a modified version of the one described in:
+/// http://moscova.inria.fr/~maranget/papers/warn/index.html
+/// However, to save future implementors from reading the original paper, we
+/// summarise the algorithm here to hopefully save time and be a little clearer
+/// (without being so rigorous).
+///
+/// The core of the algorithm revolves about a "usefulness" check. In particular, we
+/// are trying to compute a predicate `U(P, p)` where `P` is a list of patterns (we refer to this as
+/// a matrix). `U(P, p)` represents whether, given an existing list of patterns
+/// `P_1 ..= P_m`, adding a new pattern `p` will be "useful" (that is, cover previously-
+/// uncovered values of the type).
+///
+/// If we have this predicate, then we can easily compute both exhaustiveness of an
+/// entire set of patterns and the individual usefulness of each one.
+/// (a) the set of patterns is exhaustive iff `U(P, _)` is false (i.e., adding a wildcard
+/// match doesn't increase the number of values we're matching)
+/// (b) a pattern `P_i` is not useful if `U(P[0..=(i-1), P_i)` is false (i.e., adding a
+/// pattern to those that have come before it doesn't increase the number of values
+/// we're matching).
+///
+/// During the course of the algorithm, the rows of the matrix won't just be individual patterns,
+/// but rather partially-deconstructed patterns in the form of a list of patterns. The paper
+/// calls those pattern-vectors, and we will call them pattern-stacks. The same holds for the
+/// new pattern `p`.
+///
+/// For example, say we have the following:
+/// ```
+/// // x: (Option<bool>, Result<()>)
+/// match x {
+/// (Some(true), _) => {}
+/// (None, Err(())) => {}
+/// (None, Err(_)) => {}
+/// }
+/// ```
+/// Here, the matrix `P` starts as:
+/// [
+/// [(Some(true), _)],
+/// [(None, Err(()))],
+/// [(None, Err(_))],
+/// ]
+/// We can tell it's not exhaustive, because `U(P, _)` is true (we're not covering
+/// `[(Some(false), _)]`, for instance). In addition, row 3 is not useful, because
+/// all the values it covers are already covered by row 2.
+///
+/// A list of patterns can be thought of as a stack, because we are mainly interested in the top of
+/// the stack at any given point, and we can pop or apply constructors to get new pattern-stacks.
+/// To match the paper, the top of the stack is at the beginning / on the left.
+///
+/// There are two important operations on pattern-stacks necessary to understand the algorithm:
+/// 1. We can pop a given constructor off the top of a stack. This operation is called
+/// `specialize`, and is denoted `S(c, p)` where `c` is a constructor (like `Some` or
+/// `None`) and `p` a pattern-stack.
+/// If the pattern on top of the stack can cover `c`, this removes the constructor and
+/// pushes its arguments onto the stack. It also expands OR-patterns into distinct patterns.
+/// Otherwise the pattern-stack is discarded.
+/// This essentially filters those pattern-stacks whose top covers the constructor `c` and
+/// discards the others.
+///
+/// For example, the first pattern above initially gives a stack `[(Some(true), _)]`. If we
+/// pop the tuple constructor, we are left with `[Some(true), _]`, and if we then pop the
+/// `Some` constructor we get `[true, _]`. If we had popped `None` instead, we would get
+/// nothing back.
+///
+/// This returns zero or more new pattern-stacks, as follows. We look at the pattern `p_1`
+/// on top of the stack, and we have four cases:
+/// 1.1. `p_1 = c(r_1, .., r_a)`, i.e. the top of the stack has constructor `c`. We
+/// push onto the stack the arguments of this constructor, and return the result:
+/// r_1, .., r_a, p_2, .., p_n
+/// 1.2. `p_1 = c'(r_1, .., r_a')` where `c ≠ c'`. We discard the current stack and
+/// return nothing.
+/// 1.3. `p_1 = _`. We push onto the stack as many wildcards as the constructor `c` has
+/// arguments (its arity), and return the resulting stack:
+/// _, .., _, p_2, .., p_n
+/// 1.4. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting
+/// stack:
+/// S(c, (r_1, p_2, .., p_n))
+/// S(c, (r_2, p_2, .., p_n))
+///
+/// 2. We can pop a wildcard off the top of the stack. This is called `D(p)`, where `p` is
+/// a pattern-stack.
+/// This is used when we know there are missing constructor cases, but there might be
+/// existing wildcard patterns, so to check the usefulness of the matrix, we have to check
+/// all its *other* components.
+///
+/// It is computed as follows. We look at the pattern `p_1` on top of the stack,
+/// and we have three cases:
+/// 1.1. `p_1 = c(r_1, .., r_a)`. We discard the current stack and return nothing.
+/// 1.2. `p_1 = _`. We return the rest of the stack:
+/// p_2, .., p_n
+/// 1.3. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting
+/// stack.
+/// D((r_1, p_2, .., p_n))
+/// D((r_2, p_2, .., p_n))
+///
+/// Note that the OR-patterns are not always used directly in Rust, but are used to derive the
+/// exhaustive integer matching rules, so they're written here for posterity.
+///
+/// Both those operations extend straightforwardly to a list or pattern-stacks, i.e. a matrix, by
+/// working row-by-row. Popping a constructor ends up keeping only the matrix rows that start with
+/// the given constructor, and popping a wildcard keeps those rows that start with a wildcard.
+///
+///
+/// The algorithm for computing `U`
+/// -------------------------------
+/// The algorithm is inductive (on the number of columns: i.e., components of tuple patterns).
+/// That means we're going to check the components from left-to-right, so the algorithm
+/// operates principally on the first component of the matrix and new pattern-stack `p`.
+/// This algorithm is realised in the `is_useful` function.
+///
+/// Base case. (`n = 0`, i.e., an empty tuple pattern)
+/// - If `P` already contains an empty pattern (i.e., if the number of patterns `m > 0`),
+/// then `U(P, p)` is false.
+/// - Otherwise, `P` must be empty, so `U(P, p)` is true.
+///
+/// Inductive step. (`n > 0`, i.e., whether there's at least one column
+/// [which may then be expanded into further columns later])
+/// We're going to match on the top of the new pattern-stack, `p_1`.
+/// - If `p_1 == c(r_1, .., r_a)`, i.e. we have a constructor pattern.
+/// Then, the usefulness of `p_1` can be reduced to whether it is useful when
+/// we ignore all the patterns in the first column of `P` that involve other constructors.
+/// This is where `S(c, P)` comes in:
+/// `U(P, p) := U(S(c, P), S(c, p))`
+/// This special case is handled in `is_useful_specialized`.
+///
+/// For example, if `P` is:
+/// [
+/// [Some(true), _],
+/// [None, 0],
+/// ]
+/// and `p` is [Some(false), 0], then we don't care about row 2 since we know `p` only
+/// matches values that row 2 doesn't. For row 1 however, we need to dig into the
+/// arguments of `Some` to know whether some new value is covered. So we compute
+/// `U([[true, _]], [false, 0])`.
+///
+/// - If `p_1 == _`, then we look at the list of constructors that appear in the first
+/// component of the rows of `P`:
+/// + If there are some constructors that aren't present, then we might think that the
+/// wildcard `_` is useful, since it covers those constructors that weren't covered
+/// before.
+/// That's almost correct, but only works if there were no wildcards in those first
+/// components. So we need to check that `p` is useful with respect to the rows that
+/// start with a wildcard, if there are any. This is where `D` comes in:
+/// `U(P, p) := U(D(P), D(p))`
+///
+/// For example, if `P` is:
+/// [
+/// [_, true, _],
+/// [None, false, 1],
+/// ]
+/// and `p` is [_, false, _], the `Some` constructor doesn't appear in `P`. So if we
+/// only had row 2, we'd know that `p` is useful. However row 1 starts with a
+/// wildcard, so we need to check whether `U([[true, _]], [false, 1])`.
+///
+/// + Otherwise, all possible constructors (for the relevant type) are present. In this
+/// case we must check whether the wildcard pattern covers any unmatched value. For
+/// that, we can think of the `_` pattern as a big OR-pattern that covers all
+/// possible constructors. For `Option`, that would mean `_ = None | Some(_)` for
+/// example. The wildcard pattern is useful in this case if it is useful when
+/// specialized to one of the possible constructors. So we compute:
+/// `U(P, p) := ∃(k ϵ constructors) U(S(k, P), S(k, p))`
+///
+/// For example, if `P` is:
+/// [
+/// [Some(true), _],
+/// [None, false],
+/// ]
+/// and `p` is [_, false], both `None` and `Some` constructors appear in the first
+/// components of `P`. We will therefore try popping both constructors in turn: we
+/// compute U([[true, _]], [_, false]) for the `Some` constructor, and U([[false]],
+/// [false]) for the `None` constructor. The first case returns true, so we know that
+/// `p` is useful for `P`. Indeed, it matches `[Some(false), _]` that wasn't matched
+/// before.
+///
+/// - If `p_1 == r_1 | r_2`, then the usefulness depends on each `r_i` separately:
+/// `U(P, p) := U(P, (r_1, p_2, .., p_n))
+/// || U(P, (r_2, p_2, .., p_n))`
+///
+/// Modifications to the algorithm
+/// ------------------------------
+/// The algorithm in the paper doesn't cover some of the special cases that arise in Rust, for
+/// example uninhabited types and variable-length slice patterns. These are drawn attention to
+/// throughout the code below. I'll make a quick note here about how exhaustive integer matching is
+/// accounted for, though.
+///
+/// Exhaustive integer matching
+/// ---------------------------
+/// An integer type can be thought of as a (huge) sum type: 1 | 2 | 3 | ...
+/// So to support exhaustive integer matching, we can make use of the logic in the paper for
+/// OR-patterns. However, we obviously can't just treat ranges x..=y as individual sums, because
+/// they are likely gigantic. So we instead treat ranges as constructors of the integers. This means
+/// that we have a constructor *of* constructors (the integers themselves). We then need to work
+/// through all the inductive step rules above, deriving how the ranges would be treated as
+/// OR-patterns, and making sure that they're treated in the same way even when they're ranges.
+/// There are really only four special cases here:
+/// - When we match on a constructor that's actually a range, we have to treat it as if we would
+/// an OR-pattern.
+/// + It turns out that we can simply extend the case for single-value patterns in
+/// `specialize` to either be *equal* to a value constructor, or *contained within* a range
+/// constructor.
+/// + When the pattern itself is a range, you just want to tell whether any of the values in
+/// the pattern range coincide with values in the constructor range, which is precisely
+/// intersection.
+/// Since when encountering a range pattern for a value constructor, we also use inclusion, it
+/// means that whenever the constructor is a value/range and the pattern is also a value/range,
+/// we can simply use intersection to test usefulness.
+/// - When we're testing for usefulness of a pattern and the pattern's first component is a
+/// wildcard.
+/// + If all the constructors appear in the matrix, we have a slight complication. By default,
+/// the behaviour (i.e., a disjunction over specialised matrices for each constructor) is
+/// invalid, because we want a disjunction over every *integer* in each range, not just a
+/// disjunction over every range. This is a bit more tricky to deal with: essentially we need
+/// to form equivalence classes of subranges of the constructor range for which the behaviour
+/// of the matrix `P` and new pattern `p` are the same. This is described in more
+/// detail in `split_grouped_constructors`.
+/// + If some constructors are missing from the matrix, it turns out we don't need to do
+/// anything special (because we know none of the integers are actually wildcards: i.e., we
+/// can't span wildcards using ranges).
+use self::Constructor::*;
+use self::SliceKind::*;
+use self::Usefulness::*;
+use self::WitnessPreference::*;
+
+use rustc_data_structures::captures::Captures;
+use rustc_index::vec::Idx;
+
+use super::{compare_const_vals, PatternFoldable, PatternFolder};
+use super::{FieldPat, Pat, PatKind, PatRange};
+
+use rustc::ty::layout::{Integer, IntegerExt, Size, VariantIdx};
+use rustc::ty::{self, Const, Ty, TyCtxt, TypeFoldable, VariantDef};
+use rustc_hir::def_id::DefId;
+use rustc_hir::{HirId, RangeEnd};
+
+use rustc::lint;
+use rustc::mir::interpret::{truncate, AllocId, ConstValue, Pointer, Scalar};
+use rustc::mir::Field;
+use rustc::util::common::ErrorReported;
+
+use rustc_span::{Span, DUMMY_SP};
+use syntax::attr::{SignedInt, UnsignedInt};
+
+use arena::TypedArena;
+
+use smallvec::{smallvec, SmallVec};
+use std::borrow::Cow;
+use std::cmp::{self, max, min, Ordering};
+use std::convert::TryInto;
+use std::fmt;
+use std::iter::{FromIterator, IntoIterator};
+use std::ops::RangeInclusive;
+use std::u128;
+
+crate fn expand_pattern<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, pat: Pat<'tcx>) -> Pat<'tcx> {
+ LiteralExpander { tcx: cx.tcx, param_env: cx.param_env }.fold_pattern(&pat)
+}
+
+struct LiteralExpander<'tcx> {
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+}
+
+impl<'tcx> LiteralExpander<'tcx> {
+ /// Derefs `val` and potentially unsizes the value if `crty` is an array and `rty` a slice.
+ ///
+ /// `crty` and `rty` can differ because you can use array constants in the presence of slice
+ /// patterns. So the pattern may end up being a slice, but the constant is an array. We convert
+ /// the array to a slice in that case.
+ fn fold_const_value_deref(
+ &mut self,
+ val: ConstValue<'tcx>,
+ // the pattern's pointee type
+ rty: Ty<'tcx>,
+ // the constant's pointee type
+ crty: Ty<'tcx>,
+ ) -> ConstValue<'tcx> {
+ debug!("fold_const_value_deref {:?} {:?} {:?}", val, rty, crty);
+ match (val, &crty.kind, &rty.kind) {
+ // the easy case, deref a reference
+ (ConstValue::Scalar(p), x, y) if x == y => {
+ match p {
+ Scalar::Ptr(p) => {
+ let alloc = self.tcx.alloc_map.lock().unwrap_memory(p.alloc_id);
+ ConstValue::ByRef { alloc, offset: p.offset }
+ }
+ Scalar::Raw { .. } => {
+ let layout = self.tcx.layout_of(self.param_env.and(rty)).unwrap();
+ if layout.is_zst() {
+ // Deref of a reference to a ZST is a nop.
+ ConstValue::Scalar(Scalar::zst())
+ } else {
+ // FIXME(oli-obk): this is reachable for `const FOO: &&&u32 = &&&42;`
+ bug!("cannot deref {:#?}, {} -> {}", val, crty, rty);
+ }
+ }
+ }
+ }
+ // unsize array to slice if pattern is array but match value or other patterns are slice
+ (ConstValue::Scalar(Scalar::Ptr(p)), ty::Array(t, n), ty::Slice(u)) => {
+ assert_eq!(t, u);
+ ConstValue::Slice {
+ data: self.tcx.alloc_map.lock().unwrap_memory(p.alloc_id),
+ start: p.offset.bytes().try_into().unwrap(),
+ end: n.eval_usize(self.tcx, ty::ParamEnv::empty()).try_into().unwrap(),
+ }
+ }
+ // fat pointers stay the same
+ (ConstValue::Slice { .. }, _, _)
+ | (_, ty::Slice(_), ty::Slice(_))
+ | (_, ty::Str, ty::Str) => val,
+ // FIXME(oli-obk): this is reachable for `const FOO: &&&u32 = &&&42;` being used
+ _ => bug!("cannot deref {:#?}, {} -> {}", val, crty, rty),
+ }
+ }
+}
+
+impl<'tcx> PatternFolder<'tcx> for LiteralExpander<'tcx> {
+ fn fold_pattern(&mut self, pat: &Pat<'tcx>) -> Pat<'tcx> {
+ debug!("fold_pattern {:?} {:?} {:?}", pat, pat.ty.kind, pat.kind);
+ match (&pat.ty.kind, &*pat.kind) {
+ (
+ &ty::Ref(_, rty, _),
+ &PatKind::Constant {
+ value:
+ Const {
+ val: ty::ConstKind::Value(val),
+ ty: ty::TyS { kind: ty::Ref(_, crty, _), .. },
+ },
+ },
+ ) => Pat {
+ ty: pat.ty,
+ span: pat.span,
+ kind: box PatKind::Deref {
+ subpattern: Pat {
+ ty: rty,
+ span: pat.span,
+ kind: box PatKind::Constant {
+ value: self.tcx.mk_const(Const {
+ val: ty::ConstKind::Value(
+ self.fold_const_value_deref(*val, rty, crty),
+ ),
+ ty: rty,
+ }),
+ },
+ },
+ },
+ },
+
+ (
+ &ty::Ref(_, rty, _),
+ &PatKind::Constant {
+ value: Const { val, ty: ty::TyS { kind: ty::Ref(_, crty, _), .. } },
+ },
+ ) => bug!("cannot deref {:#?}, {} -> {}", val, crty, rty),
+
+ (_, &PatKind::Binding { subpattern: Some(ref s), .. }) => s.fold_with(self),
+ (_, &PatKind::AscribeUserType { subpattern: ref s, .. }) => s.fold_with(self),
+ _ => pat.super_fold_with(self),
+ }
+ }
+}
+
+impl<'tcx> Pat<'tcx> {
+ pub(super) fn is_wildcard(&self) -> bool {
+ match *self.kind {
+ PatKind::Binding { subpattern: None, .. } | PatKind::Wild => true,
+ _ => false,
+ }
+ }
+}
+
+/// A row of a matrix. Rows of len 1 are very common, which is why `SmallVec[_; 2]`
+/// works well.
+#[derive(Debug, Clone)]
+crate struct PatStack<'p, 'tcx>(SmallVec<[&'p Pat<'tcx>; 2]>);
+
+impl<'p, 'tcx> PatStack<'p, 'tcx> {
+ crate fn from_pattern(pat: &'p Pat<'tcx>) -> Self {
+ PatStack(smallvec![pat])
+ }
+
+ fn from_vec(vec: SmallVec<[&'p Pat<'tcx>; 2]>) -> Self {
+ PatStack(vec)
+ }
+
+ fn from_slice(s: &[&'p Pat<'tcx>]) -> Self {
+ PatStack(SmallVec::from_slice(s))
+ }
+
+ fn is_empty(&self) -> bool {
+ self.0.is_empty()
+ }
+
+ fn len(&self) -> usize {
+ self.0.len()
+ }
+
+ fn head(&self) -> &'p Pat<'tcx> {
+ self.0[0]
+ }
+
+ fn to_tail(&self) -> Self {
+ PatStack::from_slice(&self.0[1..])
+ }
+
+ fn iter(&self) -> impl Iterator<Item = &Pat<'tcx>> {
+ self.0.iter().map(|p| *p)
+ }
+
+ // If the first pattern is an or-pattern, expand this pattern. Otherwise, return `None`.
+ fn expand_or_pat(&self) -> Option<Vec<Self>> {
+ if self.is_empty() {
+ None
+ } else if let PatKind::Or { pats } = &*self.head().kind {
+ Some(
+ pats.iter()
+ .map(|pat| {
+ let mut new_patstack = PatStack::from_pattern(pat);
+ new_patstack.0.extend_from_slice(&self.0[1..]);
+ new_patstack
+ })
+ .collect(),
+ )
+ } else {
+ None
+ }
+ }
+
+ /// This computes `D(self)`. See top of the file for explanations.
+ fn specialize_wildcard(&self) -> Option<Self> {
+ if self.head().is_wildcard() { Some(self.to_tail()) } else { None }
+ }
+
+ /// This computes `S(constructor, self)`. See top of the file for explanations.
+ fn specialize_constructor(
+ &self,
+ cx: &mut MatchCheckCtxt<'p, 'tcx>,
+ constructor: &Constructor<'tcx>,
+ ctor_wild_subpatterns: &'p [Pat<'tcx>],
+ ) -> Option<PatStack<'p, 'tcx>> {
+ let new_heads = specialize_one_pattern(cx, self.head(), constructor, ctor_wild_subpatterns);
+ new_heads.map(|mut new_head| {
+ new_head.0.extend_from_slice(&self.0[1..]);
+ new_head
+ })
+ }
+}
+
+impl<'p, 'tcx> Default for PatStack<'p, 'tcx> {
+ fn default() -> Self {
+ PatStack(smallvec![])
+ }
+}
+
+impl<'p, 'tcx> FromIterator<&'p Pat<'tcx>> for PatStack<'p, 'tcx> {
+ fn from_iter<T>(iter: T) -> Self
+ where
+ T: IntoIterator<Item = &'p Pat<'tcx>>,
+ {
+ PatStack(iter.into_iter().collect())
+ }
+}
+
+/// A 2D matrix.
+#[derive(Clone)]
+crate struct Matrix<'p, 'tcx>(Vec<PatStack<'p, 'tcx>>);
+
+impl<'p, 'tcx> Matrix<'p, 'tcx> {
+ crate fn empty() -> Self {
+ Matrix(vec![])
+ }
+
+ /// Pushes a new row to the matrix. If the row starts with an or-pattern, this expands it.
+ crate fn push(&mut self, row: PatStack<'p, 'tcx>) {
+ if let Some(rows) = row.expand_or_pat() {
+ self.0.extend(rows);
+ } else {
+ self.0.push(row);
+ }
+ }
+
+ /// Iterate over the first component of each row
+ fn heads<'a>(&'a self) -> impl Iterator<Item = &'a Pat<'tcx>> + Captures<'p> {
+ self.0.iter().map(|r| r.head())
+ }
+
+ /// This computes `D(self)`. See top of the file for explanations.
+ fn specialize_wildcard(&self) -> Self {
+ self.0.iter().filter_map(|r| r.specialize_wildcard()).collect()
+ }
+
+ /// This computes `S(constructor, self)`. See top of the file for explanations.
+ fn specialize_constructor(
+ &self,
+ cx: &mut MatchCheckCtxt<'p, 'tcx>,
+ constructor: &Constructor<'tcx>,
+ ctor_wild_subpatterns: &'p [Pat<'tcx>],
+ ) -> Matrix<'p, 'tcx> {
+ self.0
+ .iter()
+ .filter_map(|r| r.specialize_constructor(cx, constructor, ctor_wild_subpatterns))
+ .collect()
+ }
+}
+
+/// Pretty-printer for matrices of patterns, example:
+/// +++++++++++++++++++++++++++++
+/// + _ + [] +
+/// +++++++++++++++++++++++++++++
+/// + true + [First] +
+/// +++++++++++++++++++++++++++++
+/// + true + [Second(true)] +
+/// +++++++++++++++++++++++++++++
+/// + false + [_] +
+/// +++++++++++++++++++++++++++++
+/// + _ + [_, _, tail @ ..] +
+/// +++++++++++++++++++++++++++++
+impl<'p, 'tcx> fmt::Debug for Matrix<'p, 'tcx> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "\n")?;
+
+ let &Matrix(ref m) = self;
+ let pretty_printed_matrix: Vec<Vec<String>> =
+ m.iter().map(|row| row.iter().map(|pat| format!("{:?}", pat)).collect()).collect();
+
+ let column_count = m.iter().map(|row| row.len()).max().unwrap_or(0);
+ assert!(m.iter().all(|row| row.len() == column_count));
+ let column_widths: Vec<usize> = (0..column_count)
+ .map(|col| pretty_printed_matrix.iter().map(|row| row[col].len()).max().unwrap_or(0))
+ .collect();
+
+ let total_width = column_widths.iter().cloned().sum::<usize>() + column_count * 3 + 1;
+ let br = "+".repeat(total_width);
+ write!(f, "{}\n", br)?;
+ for row in pretty_printed_matrix {
+ write!(f, "+")?;
+ for (column, pat_str) in row.into_iter().enumerate() {
+ write!(f, " ")?;
+ write!(f, "{:1$}", pat_str, column_widths[column])?;
+ write!(f, " +")?;
+ }
+ write!(f, "\n")?;
+ write!(f, "{}\n", br)?;
+ }
+ Ok(())
+ }
+}
+
+impl<'p, 'tcx> FromIterator<PatStack<'p, 'tcx>> for Matrix<'p, 'tcx> {
+ fn from_iter<T>(iter: T) -> Self
+ where
+ T: IntoIterator<Item = PatStack<'p, 'tcx>>,
+ {
+ let mut matrix = Matrix::empty();
+ for x in iter {
+ // Using `push` ensures we correctly expand or-patterns.
+ matrix.push(x);
+ }
+ matrix
+ }
+}
+
+crate struct MatchCheckCtxt<'a, 'tcx> {
+ crate tcx: TyCtxt<'tcx>,
+ /// The module in which the match occurs. This is necessary for
+ /// checking inhabited-ness of types because whether a type is (visibly)
+ /// inhabited can depend on whether it was defined in the current module or
+ /// not. E.g., `struct Foo { _private: ! }` cannot be seen to be empty
+ /// outside it's module and should not be matchable with an empty match
+ /// statement.
+ crate module: DefId,
+ param_env: ty::ParamEnv<'tcx>,
+ crate pattern_arena: &'a TypedArena<Pat<'tcx>>,
+}
+
+impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
+ crate fn create_and_enter<F, R>(
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ module: DefId,
+ f: F,
+ ) -> R
+ where
+ F: for<'b> FnOnce(MatchCheckCtxt<'b, 'tcx>) -> R,
+ {
+ let pattern_arena = TypedArena::default();
+
+ f(MatchCheckCtxt { tcx, param_env, module, pattern_arena: &pattern_arena })
+ }
+
+ fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool {
+ if self.tcx.features().exhaustive_patterns {
+ self.tcx.is_ty_uninhabited_from(self.module, ty)
+ } else {
+ false
+ }
+ }
+
+ // Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`.
+ crate fn is_foreign_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool {
+ match ty.kind {
+ ty::Adt(def, ..) => {
+ def.is_enum() && def.is_variant_list_non_exhaustive() && !def.did.is_local()
+ }
+ _ => false,
+ }
+ }
+
+ // Returns whether the given variant is from another crate and has its fields declared
+ // `#[non_exhaustive]`.
+ fn is_foreign_non_exhaustive_variant(&self, ty: Ty<'tcx>, variant: &VariantDef) -> bool {
+ match ty.kind {
+ ty::Adt(def, ..) => variant.is_field_list_non_exhaustive() && !def.did.is_local(),
+ _ => false,
+ }
+ }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+enum SliceKind {
+ /// Patterns of length `n` (`[x, y]`).
+ FixedLen(u64),
+ /// Patterns using the `..` notation (`[x, .., y]`).
+ /// Captures any array constructor of `length >= i + j`.
+ /// In the case where `array_len` is `Some(_)`,
+ /// this indicates that we only care about the first `i` and the last `j` values of the array,
+ /// and everything in between is a wildcard `_`.
+ VarLen(u64, u64),
+}
+
+impl SliceKind {
+ fn arity(self) -> u64 {
+ match self {
+ FixedLen(length) => length,
+ VarLen(prefix, suffix) => prefix + suffix,
+ }
+ }
+
+ /// Whether this pattern includes patterns of length `other_len`.
+ fn covers_length(self, other_len: u64) -> bool {
+ match self {
+ FixedLen(len) => len == other_len,
+ VarLen(prefix, suffix) => prefix + suffix <= other_len,
+ }
+ }
+
+ /// Returns a collection of slices that spans the values covered by `self`, subtracted by the
+ /// values covered by `other`: i.e., `self \ other` (in set notation).
+ fn subtract(self, other: Self) -> SmallVec<[Self; 1]> {
+ // Remember, `VarLen(i, j)` covers the union of `FixedLen` from `i + j` to infinity.
+ // Naming: we remove the "neg" constructors from the "pos" ones.
+ match self {
+ FixedLen(pos_len) => {
+ if other.covers_length(pos_len) {
+ smallvec![]
+ } else {
+ smallvec![self]
+ }
+ }
+ VarLen(pos_prefix, pos_suffix) => {
+ let pos_len = pos_prefix + pos_suffix;
+ match other {
+ FixedLen(neg_len) => {
+ if neg_len < pos_len {
+ smallvec![self]
+ } else {
+ (pos_len..neg_len)
+ .map(FixedLen)
+ // We know that `neg_len + 1 >= pos_len >= pos_suffix`.
+ .chain(Some(VarLen(neg_len + 1 - pos_suffix, pos_suffix)))
+ .collect()
+ }
+ }
+ VarLen(neg_prefix, neg_suffix) => {
+ let neg_len = neg_prefix + neg_suffix;
+ if neg_len <= pos_len {
+ smallvec![]
+ } else {
+ (pos_len..neg_len).map(FixedLen).collect()
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+/// A constructor for array and slice patterns.
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+struct Slice {
+ /// `None` if the matched value is a slice, `Some(n)` if it is an array of size `n`.
+ array_len: Option<u64>,
+ /// The kind of pattern it is: fixed-length `[x, y]` or variable length `[x, .., y]`.
+ kind: SliceKind,
+}
+
+impl Slice {
+ /// Returns what patterns this constructor covers: either fixed-length patterns or
+ /// variable-length patterns.
+ fn pattern_kind(self) -> SliceKind {
+ match self {
+ Slice { array_len: Some(len), kind: VarLen(prefix, suffix) }
+ if prefix + suffix == len =>
+ {
+ FixedLen(len)
+ }
+ _ => self.kind,
+ }
+ }
+
+ /// Returns what values this constructor covers: either values of only one given length, or
+ /// values of length above a given length.
+ /// This is different from `pattern_kind()` because in some cases the pattern only takes into
+ /// account a subset of the entries of the array, but still only captures values of a given
+ /// length.
+ fn value_kind(self) -> SliceKind {
+ match self {
+ Slice { array_len: Some(len), kind: VarLen(_, _) } => FixedLen(len),
+ _ => self.kind,
+ }
+ }
+
+ fn arity(self) -> u64 {
+ self.pattern_kind().arity()
+ }
+}
+
+#[derive(Clone, Debug, PartialEq)]
+enum Constructor<'tcx> {
+ /// The constructor of all patterns that don't vary by constructor,
+ /// e.g., struct patterns and fixed-length arrays.
+ Single,
+ /// Enum variants.
+ Variant(DefId),
+ /// Literal values.
+ ConstantValue(&'tcx ty::Const<'tcx>),
+ /// Ranges of integer literal values (`2`, `2..=5` or `2..5`).
+ IntRange(IntRange<'tcx>),
+ /// Ranges of floating-point literal values (`2.0..=5.2`).
+ FloatRange(&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>, RangeEnd),
+ /// Array and slice patterns.
+ Slice(Slice),
+ /// Fake extra constructor for enums that aren't allowed to be matched exhaustively.
+ NonExhaustive,
+}
+
+impl<'tcx> Constructor<'tcx> {
+ fn is_slice(&self) -> bool {
+ match self {
+ Slice(_) => true,
+ _ => false,
+ }
+ }
+
+ fn variant_index_for_adt<'a>(
+ &self,
+ cx: &MatchCheckCtxt<'a, 'tcx>,
+ adt: &'tcx ty::AdtDef,
+ ) -> VariantIdx {
+ match *self {
+ Variant(id) => adt.variant_index_with_id(id),
+ Single => {
+ assert!(!adt.is_enum());
+ VariantIdx::new(0)
+ }
+ ConstantValue(c) => cx.tcx.destructure_const(cx.param_env.and(c)).variant,
+ _ => bug!("bad constructor {:?} for adt {:?}", self, adt),
+ }
+ }
+
+ // Returns the set of constructors covered by `self` but not by
+ // anything in `other_ctors`.
+ fn subtract_ctors(&self, other_ctors: &Vec<Constructor<'tcx>>) -> Vec<Constructor<'tcx>> {
+ if other_ctors.is_empty() {
+ return vec![self.clone()];
+ }
+
+ match self {
+ // Those constructors can only match themselves.
+ Single | Variant(_) | ConstantValue(..) | FloatRange(..) => {
+ if other_ctors.iter().any(|c| c == self) { vec![] } else { vec![self.clone()] }
+ }
+ &Slice(slice) => {
+ let mut other_slices = other_ctors
+ .iter()
+ .filter_map(|c: &Constructor<'_>| match c {
+ Slice(slice) => Some(*slice),
+ // FIXME(oli-obk): implement `deref` for `ConstValue`
+ ConstantValue(..) => None,
+ _ => bug!("bad slice pattern constructor {:?}", c),
+ })
+ .map(Slice::value_kind);
+
+ match slice.value_kind() {
+ FixedLen(self_len) => {
+ if other_slices.any(|other_slice| other_slice.covers_length(self_len)) {
+ vec![]
+ } else {
+ vec![Slice(slice)]
+ }
+ }
+ kind @ VarLen(..) => {
+ let mut remaining_slices = vec![kind];
+
+ // For each used slice, subtract from the current set of slices.
+ for other_slice in other_slices {
+ remaining_slices = remaining_slices
+ .into_iter()
+ .flat_map(|remaining_slice| remaining_slice.subtract(other_slice))
+ .collect();
+
+ // If the constructors that have been considered so far already cover
+ // the entire range of `self`, no need to look at more constructors.
+ if remaining_slices.is_empty() {
+ break;
+ }
+ }
+
+ remaining_slices
+ .into_iter()
+ .map(|kind| Slice { array_len: slice.array_len, kind })
+ .map(Slice)
+ .collect()
+ }
+ }
+ }
+ IntRange(self_range) => {
+ let mut remaining_ranges = vec![self_range.clone()];
+ for other_ctor in other_ctors {
+ if let IntRange(other_range) = other_ctor {
+ if other_range == self_range {
+ // If the `self` range appears directly in a `match` arm, we can
+ // eliminate it straight away.
+ remaining_ranges = vec![];
+ } else {
+ // Otherwise explicitely compute the remaining ranges.
+ remaining_ranges = other_range.subtract_from(remaining_ranges);
+ }
+
+ // If the ranges that have been considered so far already cover the entire
+ // range of values, we can return early.
+ if remaining_ranges.is_empty() {
+ break;
+ }
+ }
+ }
+
+ // Convert the ranges back into constructors.
+ remaining_ranges.into_iter().map(IntRange).collect()
+ }
+ // This constructor is never covered by anything else
+ NonExhaustive => vec![NonExhaustive],
+ }
+ }
+
+ /// This returns one wildcard pattern for each argument to this constructor.
+ ///
+ /// This must be consistent with `apply`, `specialize_one_pattern`, and `arity`.
+ fn wildcard_subpatterns<'a>(
+ &self,
+ cx: &MatchCheckCtxt<'a, 'tcx>,
+ ty: Ty<'tcx>,
+ ) -> Vec<Pat<'tcx>> {
+ debug!("wildcard_subpatterns({:#?}, {:?})", self, ty);
+
+ match self {
+ Single | Variant(_) => match ty.kind {
+ ty::Tuple(ref fs) => {
+ fs.into_iter().map(|t| t.expect_ty()).map(Pat::wildcard_from_ty).collect()
+ }
+ ty::Ref(_, rty, _) => vec![Pat::wildcard_from_ty(rty)],
+ ty::Adt(adt, substs) => {
+ if adt.is_box() {
+ // Use T as the sub pattern type of Box<T>.
+ vec![Pat::wildcard_from_ty(substs.type_at(0))]
+ } else {
+ let variant = &adt.variants[self.variant_index_for_adt(cx, adt)];
+ let is_non_exhaustive = cx.is_foreign_non_exhaustive_variant(ty, variant);
+ variant
+ .fields
+ .iter()
+ .map(|field| {
+ let is_visible = adt.is_enum()
+ || field.vis.is_accessible_from(cx.module, cx.tcx);
+ let is_uninhabited = cx.is_uninhabited(field.ty(cx.tcx, substs));
+ match (is_visible, is_non_exhaustive, is_uninhabited) {
+ // Treat all uninhabited types in non-exhaustive variants as
+ // `TyErr`.
+ (_, true, true) => cx.tcx.types.err,
+ // Treat all non-visible fields as `TyErr`. They can't appear
+ // in any other pattern from this match (because they are
+ // private), so their type does not matter - but we don't want
+ // to know they are uninhabited.
+ (false, ..) => cx.tcx.types.err,
+ (true, ..) => {
+ let ty = field.ty(cx.tcx, substs);
+ match ty.kind {
+ // If the field type returned is an array of an unknown
+ // size return an TyErr.
+ ty::Array(_, len)
+ if len
+ .try_eval_usize(cx.tcx, cx.param_env)
+ .is_none() =>
+ {
+ cx.tcx.types.err
+ }
+ _ => ty,
+ }
+ }
+ }
+ })
+ .map(Pat::wildcard_from_ty)
+ .collect()
+ }
+ }
+ _ => vec![],
+ },
+ Slice(_) => match ty.kind {
+ ty::Slice(ty) | ty::Array(ty, _) => {
+ let arity = self.arity(cx, ty);
+ (0..arity).map(|_| Pat::wildcard_from_ty(ty)).collect()
+ }
+ _ => bug!("bad slice pattern {:?} {:?}", self, ty),
+ },
+ ConstantValue(..) | FloatRange(..) | IntRange(..) | NonExhaustive => vec![],
+ }
+ }
+
+ /// This computes the arity of a constructor. The arity of a constructor
+ /// is how many subpattern patterns of that constructor should be expanded to.
+ ///
+ /// For instance, a tuple pattern `(_, 42, Some([]))` has the arity of 3.
+ /// A struct pattern's arity is the number of fields it contains, etc.
+ ///
+ /// This must be consistent with `wildcard_subpatterns`, `specialize_one_pattern`, and `apply`.
+ fn arity<'a>(&self, cx: &MatchCheckCtxt<'a, 'tcx>, ty: Ty<'tcx>) -> u64 {
+ debug!("Constructor::arity({:#?}, {:?})", self, ty);
+ match self {
+ Single | Variant(_) => match ty.kind {
+ ty::Tuple(ref fs) => fs.len() as u64,
+ ty::Slice(..) | ty::Array(..) => bug!("bad slice pattern {:?} {:?}", self, ty),
+ ty::Ref(..) => 1,
+ ty::Adt(adt, _) => {
+ adt.variants[self.variant_index_for_adt(cx, adt)].fields.len() as u64
+ }
+ _ => 0,
+ },
+ Slice(slice) => slice.arity(),
+ ConstantValue(..) | FloatRange(..) | IntRange(..) | NonExhaustive => 0,
+ }
+ }
+
+ /// Apply a constructor to a list of patterns, yielding a new pattern. `pats`
+ /// must have as many elements as this constructor's arity.
+ ///
+ /// This must be consistent with `wildcard_subpatterns`, `specialize_one_pattern`, and `arity`.
+ ///
+ /// Examples:
+ /// `self`: `Constructor::Single`
+ /// `ty`: `(u32, u32, u32)`
+ /// `pats`: `[10, 20, _]`
+ /// returns `(10, 20, _)`
+ ///
+ /// `self`: `Constructor::Variant(Option::Some)`
+ /// `ty`: `Option<bool>`
+ /// `pats`: `[false]`
+ /// returns `Some(false)`
+ fn apply<'a>(
+ &self,
+ cx: &MatchCheckCtxt<'a, 'tcx>,
+ ty: Ty<'tcx>,
+ pats: impl IntoIterator<Item = Pat<'tcx>>,
+ ) -> Pat<'tcx> {
+ let mut subpatterns = pats.into_iter();
+
+ let pat = match self {
+ Single | Variant(_) => match ty.kind {
+ ty::Adt(..) | ty::Tuple(..) => {
+ let subpatterns = subpatterns
+ .enumerate()
+ .map(|(i, p)| FieldPat { field: Field::new(i), pattern: p })
+ .collect();
+
+ if let ty::Adt(adt, substs) = ty.kind {
+ if adt.is_enum() {
+ PatKind::Variant {
+ adt_def: adt,
+ substs,
+ variant_index: self.variant_index_for_adt(cx, adt),
+ subpatterns,
+ }
+ } else {
+ PatKind::Leaf { subpatterns }
+ }
+ } else {
+ PatKind::Leaf { subpatterns }
+ }
+ }
+ ty::Ref(..) => PatKind::Deref { subpattern: subpatterns.nth(0).unwrap() },
+ ty::Slice(_) | ty::Array(..) => bug!("bad slice pattern {:?} {:?}", self, ty),
+ _ => PatKind::Wild,
+ },
+ Slice(slice) => match slice.pattern_kind() {
+ FixedLen(_) => {
+ PatKind::Slice { prefix: subpatterns.collect(), slice: None, suffix: vec![] }
+ }
+ VarLen(prefix, _) => {
+ let mut prefix: Vec<_> = subpatterns.by_ref().take(prefix as usize).collect();
+ if slice.array_len.is_some() {
+ // Improves diagnostics a bit: if the type is a known-size array, instead
+ // of reporting `[x, _, .., _, y]`, we prefer to report `[x, .., y]`.
+ // This is incorrect if the size is not known, since `[_, ..]` captures
+ // arrays of lengths `>= 1` whereas `[..]` captures any length.
+ while !prefix.is_empty() && prefix.last().unwrap().is_wildcard() {
+ prefix.pop();
+ }
+ }
+ let suffix: Vec<_> = if slice.array_len.is_some() {
+ // Same as above.
+ subpatterns.skip_while(Pat::is_wildcard).collect()
+ } else {
+ subpatterns.collect()
+ };
+ let wild = Pat::wildcard_from_ty(ty);
+ PatKind::Slice { prefix, slice: Some(wild), suffix }
+ }
+ },
+ &ConstantValue(value) => PatKind::Constant { value },
+ &FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }),
+ IntRange(range) => return range.to_pat(cx.tcx),
+ NonExhaustive => PatKind::Wild,
+ };
+
+ Pat { ty, span: DUMMY_SP, kind: Box::new(pat) }
+ }
+
+ /// Like `apply`, but where all the subpatterns are wildcards `_`.
+ fn apply_wildcards<'a>(&self, cx: &MatchCheckCtxt<'a, 'tcx>, ty: Ty<'tcx>) -> Pat<'tcx> {
+ let subpatterns = self.wildcard_subpatterns(cx, ty).into_iter().rev();
+ self.apply(cx, ty, subpatterns)
+ }
+}
+
+#[derive(Clone, Debug)]
+crate enum Usefulness<'tcx, 'p> {
+ /// Carries a list of unreachable subpatterns. Used only in the presence of or-patterns.
+ Useful(Vec<&'p Pat<'tcx>>),
+ /// Carries a list of witnesses of non-exhaustiveness.
+ UsefulWithWitness(Vec<Witness<'tcx>>),
+ NotUseful,
+}
+
+impl<'tcx, 'p> Usefulness<'tcx, 'p> {
+ fn new_useful(preference: WitnessPreference) -> Self {
+ match preference {
+ ConstructWitness => UsefulWithWitness(vec![Witness(vec![])]),
+ LeaveOutWitness => Useful(vec![]),
+ }
+ }
+
+ fn is_useful(&self) -> bool {
+ match *self {
+ NotUseful => false,
+ _ => true,
+ }
+ }
+
+ fn apply_constructor(
+ self,
+ cx: &MatchCheckCtxt<'_, 'tcx>,
+ ctor: &Constructor<'tcx>,
+ ty: Ty<'tcx>,
+ ) -> Self {
+ match self {
+ UsefulWithWitness(witnesses) => UsefulWithWitness(
+ witnesses
+ .into_iter()
+ .map(|witness| witness.apply_constructor(cx, &ctor, ty))
+ .collect(),
+ ),
+ x => x,
+ }
+ }
+
+ fn apply_wildcard(self, ty: Ty<'tcx>) -> Self {
+ match self {
+ UsefulWithWitness(witnesses) => {
+ let wild = Pat::wildcard_from_ty(ty);
+ UsefulWithWitness(
+ witnesses
+ .into_iter()
+ .map(|mut witness| {
+ witness.0.push(wild.clone());
+ witness
+ })
+ .collect(),
+ )
+ }
+ x => x,
+ }
+ }
+
+ fn apply_missing_ctors(
+ self,
+ cx: &MatchCheckCtxt<'_, 'tcx>,
+ ty: Ty<'tcx>,
+ missing_ctors: &MissingConstructors<'tcx>,
+ ) -> Self {
+ match self {
+ UsefulWithWitness(witnesses) => {
+ let new_patterns: Vec<_> =
+ missing_ctors.iter().map(|ctor| ctor.apply_wildcards(cx, ty)).collect();
+ // Add the new patterns to each witness
+ UsefulWithWitness(
+ witnesses
+ .into_iter()
+ .flat_map(|witness| {
+ new_patterns.iter().map(move |pat| {
+ let mut witness = witness.clone();
+ witness.0.push(pat.clone());
+ witness
+ })
+ })
+ .collect(),
+ )
+ }
+ x => x,
+ }
+ }
+}
+
+#[derive(Copy, Clone, Debug)]
+crate enum WitnessPreference {
+ ConstructWitness,
+ LeaveOutWitness,
+}
+
+#[derive(Copy, Clone, Debug)]
+struct PatCtxt<'tcx> {
+ ty: Ty<'tcx>,
+ span: Span,
+}
+
+/// A witness of non-exhaustiveness for error reporting, represented
+/// as a list of patterns (in reverse order of construction) with
+/// wildcards inside to represent elements that can take any inhabitant
+/// of the type as a value.
+///
+/// A witness against a list of patterns should have the same types
+/// and length as the pattern matched against. Because Rust `match`
+/// is always against a single pattern, at the end the witness will
+/// have length 1, but in the middle of the algorithm, it can contain
+/// multiple patterns.
+///
+/// For example, if we are constructing a witness for the match against
+/// ```
+/// struct Pair(Option<(u32, u32)>, bool);
+///
+/// match (p: Pair) {
+/// Pair(None, _) => {}
+/// Pair(_, false) => {}
+/// }
+/// ```
+///
+/// We'll perform the following steps:
+/// 1. Start with an empty witness
+/// `Witness(vec![])`
+/// 2. Push a witness `Some(_)` against the `None`
+/// `Witness(vec![Some(_)])`
+/// 3. Push a witness `true` against the `false`
+/// `Witness(vec![Some(_), true])`
+/// 4. Apply the `Pair` constructor to the witnesses
+/// `Witness(vec![Pair(Some(_), true)])`
+///
+/// The final `Pair(Some(_), true)` is then the resulting witness.
+#[derive(Clone, Debug)]
+crate struct Witness<'tcx>(Vec<Pat<'tcx>>);
+
+impl<'tcx> Witness<'tcx> {
+ crate fn single_pattern(self) -> Pat<'tcx> {
+ assert_eq!(self.0.len(), 1);
+ self.0.into_iter().next().unwrap()
+ }
+
+ /// Constructs a partial witness for a pattern given a list of
+ /// patterns expanded by the specialization step.
+ ///
+ /// When a pattern P is discovered to be useful, this function is used bottom-up
+ /// to reconstruct a complete witness, e.g., a pattern P' that covers a subset
+ /// of values, V, where each value in that set is not covered by any previously
+ /// used patterns and is covered by the pattern P'. Examples:
+ ///
+ /// left_ty: tuple of 3 elements
+ /// pats: [10, 20, _] => (10, 20, _)
+ ///
+ /// left_ty: struct X { a: (bool, &'static str), b: usize}
+ /// pats: [(false, "foo"), 42] => X { a: (false, "foo"), b: 42 }
+ fn apply_constructor<'a>(
+ mut self,
+ cx: &MatchCheckCtxt<'a, 'tcx>,
+ ctor: &Constructor<'tcx>,
+ ty: Ty<'tcx>,
+ ) -> Self {
+ let arity = ctor.arity(cx, ty);
+ let pat = {
+ let len = self.0.len() as u64;
+ let pats = self.0.drain((len - arity) as usize..).rev();
+ ctor.apply(cx, ty, pats)
+ };
+
+ self.0.push(pat);
+
+ self
+ }
+}
+
+/// This determines the set of all possible constructors of a pattern matching
+/// values of type `left_ty`. For vectors, this would normally be an infinite set
+/// but is instead bounded by the maximum fixed length of slice patterns in
+/// the column of patterns being analyzed.
+///
+/// We make sure to omit constructors that are statically impossible. E.g., for
+/// `Option<!>`, we do not include `Some(_)` in the returned list of constructors.
+/// Invariant: this returns an empty `Vec` if and only if the type is uninhabited (as determined by
+/// `cx.is_uninhabited()`).
+fn all_constructors<'a, 'tcx>(
+ cx: &mut MatchCheckCtxt<'a, 'tcx>,
+ pcx: PatCtxt<'tcx>,
+) -> Vec<Constructor<'tcx>> {
+ debug!("all_constructors({:?})", pcx.ty);
+ let make_range = |start, end| {
+ IntRange(
+ // `unwrap()` is ok because we know the type is an integer.
+ IntRange::from_range(cx.tcx, start, end, pcx.ty, &RangeEnd::Included, pcx.span)
+ .unwrap(),
+ )
+ };
+ match pcx.ty.kind {
+ ty::Bool => {
+ [true, false].iter().map(|&b| ConstantValue(ty::Const::from_bool(cx.tcx, b))).collect()
+ }
+ ty::Array(ref sub_ty, len) if len.try_eval_usize(cx.tcx, cx.param_env).is_some() => {
+ let len = len.eval_usize(cx.tcx, cx.param_env);
+ if len != 0 && cx.is_uninhabited(sub_ty) {
+ vec![]
+ } else {
+ vec![Slice(Slice { array_len: Some(len), kind: VarLen(0, 0) })]
+ }
+ }
+ // Treat arrays of a constant but unknown length like slices.
+ ty::Array(ref sub_ty, _) | ty::Slice(ref sub_ty) => {
+ let kind = if cx.is_uninhabited(sub_ty) { FixedLen(0) } else { VarLen(0, 0) };
+ vec![Slice(Slice { array_len: None, kind })]
+ }
+ ty::Adt(def, substs) if def.is_enum() => {
+ let ctors: Vec<_> = if cx.tcx.features().exhaustive_patterns {
+ // If `exhaustive_patterns` is enabled, we exclude variants known to be
+ // uninhabited.
+ def.variants
+ .iter()
+ .filter(|v| {
+ !v.uninhabited_from(cx.tcx, substs, def.adt_kind())
+ .contains(cx.tcx, cx.module)
+ })
+ .map(|v| Variant(v.def_id))
+ .collect()
+ } else {
+ def.variants.iter().map(|v| Variant(v.def_id)).collect()
+ };
+
+ // If the enum is declared as `#[non_exhaustive]`, we treat it as if it had an
+ // additional "unknown" constructor.
+ // There is no point in enumerating all possible variants, because the user can't
+ // actually match against them all themselves. So we always return only the fictitious
+ // constructor.
+ // E.g., in an example like:
+ // ```
+ // let err: io::ErrorKind = ...;
+ // match err {
+ // io::ErrorKind::NotFound => {},
+ // }
+ // ```
+ // we don't want to show every possible IO error, but instead have only `_` as the
+ // witness.
+ let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(pcx.ty);
+
+ // If `exhaustive_patterns` is disabled and our scrutinee is an empty enum, we treat it
+ // as though it had an "unknown" constructor to avoid exposing its emptyness. Note that
+ // an empty match will still be considered exhaustive because that case is handled
+ // separately in `check_match`.
+ let is_secretly_empty =
+ def.variants.is_empty() && !cx.tcx.features().exhaustive_patterns;
+
+ if is_secretly_empty || is_declared_nonexhaustive { vec![NonExhaustive] } else { ctors }
+ }
+ ty::Char => {
+ vec![
+ // The valid Unicode Scalar Value ranges.
+ make_range('\u{0000}' as u128, '\u{D7FF}' as u128),
+ make_range('\u{E000}' as u128, '\u{10FFFF}' as u128),
+ ]
+ }
+ ty::Int(_) | ty::Uint(_)
+ if pcx.ty.is_ptr_sized_integral()
+ && !cx.tcx.features().precise_pointer_size_matching =>
+ {
+ // `usize`/`isize` are not allowed to be matched exhaustively unless the
+ // `precise_pointer_size_matching` feature is enabled. So we treat those types like
+ // `#[non_exhaustive]` enums by returning a special unmatcheable constructor.
+ vec![NonExhaustive]
+ }
+ ty::Int(ity) => {
+ let bits = Integer::from_attr(&cx.tcx, SignedInt(ity)).size().bits() as u128;
+ let min = 1u128 << (bits - 1);
+ let max = min - 1;
+ vec![make_range(min, max)]
+ }
+ ty::Uint(uty) => {
+ let size = Integer::from_attr(&cx.tcx, UnsignedInt(uty)).size();
+ let max = truncate(u128::max_value(), size);
+ vec![make_range(0, max)]
+ }
+ _ => {
+ if cx.is_uninhabited(pcx.ty) {
+ vec![]
+ } else {
+ vec![Single]
+ }
+ }
+ }
+}
+
+/// An inclusive interval, used for precise integer exhaustiveness checking.
+/// `IntRange`s always store a contiguous range. This means that values are
+/// encoded such that `0` encodes the minimum value for the integer,
+/// regardless of the signedness.
+/// For example, the pattern `-128..=127i8` is encoded as `0..=255`.
+/// This makes comparisons and arithmetic on interval endpoints much more
+/// straightforward. See `signed_bias` for details.
+///
+/// `IntRange` is never used to encode an empty range or a "range" that wraps
+/// around the (offset) space: i.e., `range.lo <= range.hi`.
+#[derive(Clone, Debug)]
+struct IntRange<'tcx> {
+ range: RangeInclusive<u128>,
+ ty: Ty<'tcx>,
+ span: Span,
+}
+
+impl<'tcx> IntRange<'tcx> {
+ #[inline]
+ fn is_integral(ty: Ty<'_>) -> bool {
+ match ty.kind {
+ ty::Char | ty::Int(_) | ty::Uint(_) => true,
+ _ => false,
+ }
+ }
+
+ fn is_singleton(&self) -> bool {
+ self.range.start() == self.range.end()
+ }
+
+ fn boundaries(&self) -> (u128, u128) {
+ (*self.range.start(), *self.range.end())
+ }
+
+ /// Don't treat `usize`/`isize` exhaustively unless the `precise_pointer_size_matching` feature
+ /// is enabled.
+ fn treat_exhaustively(&self, tcx: TyCtxt<'tcx>) -> bool {
+ !self.ty.is_ptr_sized_integral() || tcx.features().precise_pointer_size_matching
+ }
+
+ #[inline]
+ fn integral_size_and_signed_bias(tcx: TyCtxt<'tcx>, ty: Ty<'_>) -> Option<(Size, u128)> {
+ match ty.kind {
+ ty::Char => Some((Size::from_bytes(4), 0)),
+ ty::Int(ity) => {
+ let size = Integer::from_attr(&tcx, SignedInt(ity)).size();
+ Some((size, 1u128 << (size.bits() as u128 - 1)))
+ }
+ ty::Uint(uty) => Some((Integer::from_attr(&tcx, UnsignedInt(uty)).size(), 0)),
+ _ => None,
+ }
+ }
+
+ #[inline]
+ fn from_const(
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ value: &Const<'tcx>,
+ span: Span,
+ ) -> Option<IntRange<'tcx>> {
+ if let Some((target_size, bias)) = Self::integral_size_and_signed_bias(tcx, value.ty) {
+ let ty = value.ty;
+ let val = if let ty::ConstKind::Value(ConstValue::Scalar(Scalar::Raw { data, size })) =
+ value.val
+ {
+ // For this specific pattern we can skip a lot of effort and go
+ // straight to the result, after doing a bit of checking. (We
+ // could remove this branch and just use the next branch, which
+ // is more general but much slower.)
+ Scalar::<()>::check_raw(data, size, target_size);
+ data
+ } else if let Some(val) = value.try_eval_bits(tcx, param_env, ty) {
+ // This is a more general form of the previous branch.
+ val
+ } else {
+ return None;
+ };
+ let val = val ^ bias;
+ Some(IntRange { range: val..=val, ty, span })
+ } else {
+ None
+ }
+ }
+
+ #[inline]
+ fn from_range(
+ tcx: TyCtxt<'tcx>,
+ lo: u128,
+ hi: u128,
+ ty: Ty<'tcx>,
+ end: &RangeEnd,
+ span: Span,
+ ) -> Option<IntRange<'tcx>> {
+ if Self::is_integral(ty) {
+ // Perform a shift if the underlying types are signed,
+ // which makes the interval arithmetic simpler.
+ let bias = IntRange::signed_bias(tcx, ty);
+ let (lo, hi) = (lo ^ bias, hi ^ bias);
+ let offset = (*end == RangeEnd::Excluded) as u128;
+ if lo > hi || (lo == hi && *end == RangeEnd::Excluded) {
+ // This should have been caught earlier by E0030.
+ bug!("malformed range pattern: {}..={}", lo, (hi - offset));
+ }
+ Some(IntRange { range: lo..=(hi - offset), ty, span })
+ } else {
+ None
+ }
+ }
+
+ fn from_pat(
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ pat: &Pat<'tcx>,
+ ) -> Option<IntRange<'tcx>> {
+ match pat_constructor(tcx, param_env, pat)? {
+ IntRange(range) => Some(range),
+ _ => None,
+ }
+ }
+
+ // The return value of `signed_bias` should be XORed with an endpoint to encode/decode it.
+ fn signed_bias(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> u128 {
+ match ty.kind {
+ ty::Int(ity) => {
+ let bits = Integer::from_attr(&tcx, SignedInt(ity)).size().bits() as u128;
+ 1u128 << (bits - 1)
+ }
+ _ => 0,
+ }
+ }
+
+ /// Returns a collection of ranges that spans the values covered by `ranges`, subtracted
+ /// by the values covered by `self`: i.e., `ranges \ self` (in set notation).
+ fn subtract_from(&self, ranges: Vec<IntRange<'tcx>>) -> Vec<IntRange<'tcx>> {
+ let mut remaining_ranges = vec![];
+ let ty = self.ty;
+ let span = self.span;
+ let (lo, hi) = self.boundaries();
+ for subrange in ranges {
+ let (subrange_lo, subrange_hi) = subrange.range.into_inner();
+ if lo > subrange_hi || subrange_lo > hi {
+ // The pattern doesn't intersect with the subrange at all,
+ // so the subrange remains untouched.
+ remaining_ranges.push(IntRange { range: subrange_lo..=subrange_hi, ty, span });
+ } else {
+ if lo > subrange_lo {
+ // The pattern intersects an upper section of the
+ // subrange, so a lower section will remain.
+ remaining_ranges.push(IntRange { range: subrange_lo..=(lo - 1), ty, span });
+ }
+ if hi < subrange_hi {
+ // The pattern intersects a lower section of the
+ // subrange, so an upper section will remain.
+ remaining_ranges.push(IntRange { range: (hi + 1)..=subrange_hi, ty, span });
+ }
+ }
+ }
+ remaining_ranges
+ }
+
+ fn is_subrange(&self, other: &Self) -> bool {
+ other.range.start() <= self.range.start() && self.range.end() <= other.range.end()
+ }
+
+ fn intersection(&self, tcx: TyCtxt<'tcx>, other: &Self) -> Option<Self> {
+ let ty = self.ty;
+ let (lo, hi) = self.boundaries();
+ let (other_lo, other_hi) = other.boundaries();
+ if self.treat_exhaustively(tcx) {
+ if lo <= other_hi && other_lo <= hi {
+ let span = other.span;
+ Some(IntRange { range: max(lo, other_lo)..=min(hi, other_hi), ty, span })
+ } else {
+ None
+ }
+ } else {
+ // If the range should not be treated exhaustively, fallback to checking for inclusion.
+ if self.is_subrange(other) { Some(self.clone()) } else { None }
+ }
+ }
+
+ fn suspicious_intersection(&self, other: &Self) -> bool {
+ // `false` in the following cases:
+ // 1 ---- // 1 ---------- // 1 ---- // 1 ----
+ // 2 ---------- // 2 ---- // 2 ---- // 2 ----
+ //
+ // The following are currently `false`, but could be `true` in the future (#64007):
+ // 1 --------- // 1 ---------
+ // 2 ---------- // 2 ----------
+ //
+ // `true` in the following cases:
+ // 1 ------- // 1 -------
+ // 2 -------- // 2 -------
+ let (lo, hi) = self.boundaries();
+ let (other_lo, other_hi) = other.boundaries();
+ (lo == other_hi || hi == other_lo)
+ }
+
+ fn to_pat(&self, tcx: TyCtxt<'tcx>) -> Pat<'tcx> {
+ let (lo, hi) = self.boundaries();
+
+ let bias = IntRange::signed_bias(tcx, self.ty);
+ let (lo, hi) = (lo ^ bias, hi ^ bias);
+
+ let ty = ty::ParamEnv::empty().and(self.ty);
+ let lo_const = ty::Const::from_bits(tcx, lo, ty);
+ let hi_const = ty::Const::from_bits(tcx, hi, ty);
+
+ let kind = if lo == hi {
+ PatKind::Constant { value: lo_const }
+ } else {
+ PatKind::Range(PatRange { lo: lo_const, hi: hi_const, end: RangeEnd::Included })
+ };
+
+ // This is a brand new pattern, so we don't reuse `self.span`.
+ Pat { ty: self.ty, span: DUMMY_SP, kind: Box::new(kind) }
+ }
+}
+
+/// Ignore spans when comparing, they don't carry semantic information as they are only for lints.
+impl<'tcx> std::cmp::PartialEq for IntRange<'tcx> {
+ fn eq(&self, other: &Self) -> bool {
+ self.range == other.range && self.ty == other.ty
+ }
+}
+
+// A struct to compute a set of constructors equivalent to `all_ctors \ used_ctors`.
+struct MissingConstructors<'tcx> {
+ all_ctors: Vec<Constructor<'tcx>>,
+ used_ctors: Vec<Constructor<'tcx>>,
+}
+
+impl<'tcx> MissingConstructors<'tcx> {
+ fn new(all_ctors: Vec<Constructor<'tcx>>, used_ctors: Vec<Constructor<'tcx>>) -> Self {
+ MissingConstructors { all_ctors, used_ctors }
+ }
+
+ fn into_inner(self) -> (Vec<Constructor<'tcx>>, Vec<Constructor<'tcx>>) {
+ (self.all_ctors, self.used_ctors)
+ }
+
+ fn is_empty(&self) -> bool {
+ self.iter().next().is_none()
+ }
+ /// Whether this contains all the constructors for the given type or only a
+ /// subset.
+ fn all_ctors_are_missing(&self) -> bool {
+ self.used_ctors.is_empty()
+ }
+
+ /// Iterate over all_ctors \ used_ctors
+ fn iter<'a>(&'a self) -> impl Iterator<Item = Constructor<'tcx>> + Captures<'a> {
+ self.all_ctors.iter().flat_map(move |req_ctor| req_ctor.subtract_ctors(&self.used_ctors))
+ }
+}
+
+impl<'tcx> fmt::Debug for MissingConstructors<'tcx> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let ctors: Vec<_> = self.iter().collect();
+ write!(f, "{:?}", ctors)
+ }
+}
+
+/// Algorithm from http://moscova.inria.fr/~maranget/papers/warn/index.html.
+/// The algorithm from the paper has been modified to correctly handle empty
+/// types. The changes are:
+/// (0) We don't exit early if the pattern matrix has zero rows. We just
+/// continue to recurse over columns.
+/// (1) all_constructors will only return constructors that are statically
+/// possible. E.g., it will only return `Ok` for `Result<T, !>`.
+///
+/// This finds whether a (row) vector `v` of patterns is 'useful' in relation
+/// to a set of such vectors `m` - this is defined as there being a set of
+/// inputs that will match `v` but not any of the sets in `m`.
+///
+/// All the patterns at each column of the `matrix ++ v` matrix must
+/// have the same type, except that wildcard (PatKind::Wild) patterns
+/// with type `TyErr` are also allowed, even if the "type of the column"
+/// is not `TyErr`. That is used to represent private fields, as using their
+/// real type would assert that they are inhabited.
+///
+/// This is used both for reachability checking (if a pattern isn't useful in
+/// relation to preceding patterns, it is not reachable) and exhaustiveness
+/// checking (if a wildcard pattern is useful in relation to a matrix, the
+/// matrix isn't exhaustive).
+crate fn is_useful<'p, 'tcx>(
+ cx: &mut MatchCheckCtxt<'p, 'tcx>,
+ matrix: &Matrix<'p, 'tcx>,
+ v: &PatStack<'p, 'tcx>,
+ witness_preference: WitnessPreference,
+ hir_id: HirId,
+ is_top_level: bool,
+) -> Usefulness<'tcx, 'p> {
+ let &Matrix(ref rows) = matrix;
+ debug!("is_useful({:#?}, {:#?})", matrix, v);
+
+ // The base case. We are pattern-matching on () and the return value is
+ // based on whether our matrix has a row or not.
+ // NOTE: This could potentially be optimized by checking rows.is_empty()
+ // first and then, if v is non-empty, the return value is based on whether
+ // the type of the tuple we're checking is inhabited or not.
+ if v.is_empty() {
+ return if rows.is_empty() {
+ Usefulness::new_useful(witness_preference)
+ } else {
+ NotUseful
+ };
+ };
+
+ assert!(rows.iter().all(|r| r.len() == v.len()));
+
+ // If the first pattern is an or-pattern, expand it.
+ if let Some(vs) = v.expand_or_pat() {
+ // We need to push the already-seen patterns into the matrix in order to detect redundant
+ // branches like `Some(_) | Some(0)`. We also keep track of the unreachable subpatterns.
+ let mut matrix = matrix.clone();
+ let mut unreachable_pats = Vec::new();
+ let mut any_is_useful = false;
+ for v in vs {
+ let res = is_useful(cx, &matrix, &v, witness_preference, hir_id, false);
+ match res {
+ Useful(pats) => {
+ any_is_useful = true;
+ unreachable_pats.extend(pats);
+ }
+ NotUseful => unreachable_pats.push(v.head()),
+ UsefulWithWitness(_) => {
+ bug!("Encountered or-pat in `v` during exhaustiveness checking")
+ }
+ }
+ matrix.push(v);
+ }
+ return if any_is_useful { Useful(unreachable_pats) } else { NotUseful };
+ }
+
+ let (ty, span) = matrix
+ .heads()
+ .map(|r| (r.ty, r.span))
+ .find(|(ty, _)| !ty.references_error())
+ .unwrap_or((v.head().ty, v.head().span));
+ let pcx = PatCtxt {
+ // TyErr is used to represent the type of wildcard patterns matching
+ // against inaccessible (private) fields of structs, so that we won't
+ // be able to observe whether the types of the struct's fields are
+ // inhabited.
+ //
+ // If the field is truly inaccessible, then all the patterns
+ // matching against it must be wildcard patterns, so its type
+ // does not matter.
+ //
+ // However, if we are matching against non-wildcard patterns, we
+ // need to know the real type of the field so we can specialize
+ // against it. This primarily occurs through constants - they
+ // can include contents for fields that are inaccessible at the
+ // location of the match. In that case, the field's type is
+ // inhabited - by the constant - so we can just use it.
+ //
+ // FIXME: this might lead to "unstable" behavior with macro hygiene
+ // introducing uninhabited patterns for inaccessible fields. We
+ // need to figure out how to model that.
+ ty,
+ span,
+ };
+
+ debug!("is_useful_expand_first_col: pcx={:#?}, expanding {:#?}", pcx, v.head());
+
+ if let Some(constructor) = pat_constructor(cx.tcx, cx.param_env, v.head()) {
+ debug!("is_useful - expanding constructor: {:#?}", constructor);
+ split_grouped_constructors(
+ cx.tcx,
+ cx.param_env,
+ pcx,
+ vec![constructor],
+ matrix,
+ pcx.span,
+ Some(hir_id),
+ )
+ .into_iter()
+ .map(|c| is_useful_specialized(cx, matrix, v, c, pcx.ty, witness_preference, hir_id))
+ .find(|result| result.is_useful())
+ .unwrap_or(NotUseful)
+ } else {
+ debug!("is_useful - expanding wildcard");
+
+ let used_ctors: Vec<Constructor<'_>> =
+ matrix.heads().filter_map(|p| pat_constructor(cx.tcx, cx.param_env, p)).collect();
+ debug!("used_ctors = {:#?}", used_ctors);
+ // `all_ctors` are all the constructors for the given type, which
+ // should all be represented (or caught with the wild pattern `_`).
+ let all_ctors = all_constructors(cx, pcx);
+ debug!("all_ctors = {:#?}", all_ctors);
+
+ // `missing_ctors` is the set of constructors from the same type as the
+ // first column of `matrix` that are matched only by wildcard patterns
+ // from the first column.
+ //
+ // Therefore, if there is some pattern that is unmatched by `matrix`,
+ // it will still be unmatched if the first constructor is replaced by
+ // any of the constructors in `missing_ctors`
+
+ // Missing constructors are those that are not matched by any non-wildcard patterns in the
+ // current column. We only fully construct them on-demand, because they're rarely used and
+ // can be big.
+ let missing_ctors = MissingConstructors::new(all_ctors, used_ctors);
+
+ debug!("missing_ctors.empty()={:#?}", missing_ctors.is_empty(),);
+
+ if missing_ctors.is_empty() {
+ let (all_ctors, _) = missing_ctors.into_inner();
+ split_grouped_constructors(cx.tcx, cx.param_env, pcx, all_ctors, matrix, DUMMY_SP, None)
+ .into_iter()
+ .map(|c| {
+ is_useful_specialized(cx, matrix, v, c, pcx.ty, witness_preference, hir_id)
+ })
+ .find(|result| result.is_useful())
+ .unwrap_or(NotUseful)
+ } else {
+ let matrix = matrix.specialize_wildcard();
+ let v = v.to_tail();
+ let usefulness = is_useful(cx, &matrix, &v, witness_preference, hir_id, false);
+
+ // In this case, there's at least one "free"
+ // constructor that is only matched against by
+ // wildcard patterns.
+ //
+ // There are 2 ways we can report a witness here.
+ // Commonly, we can report all the "free"
+ // constructors as witnesses, e.g., if we have:
+ //
+ // ```
+ // enum Direction { N, S, E, W }
+ // let Direction::N = ...;
+ // ```
+ //
+ // we can report 3 witnesses: `S`, `E`, and `W`.
+ //
+ // However, there is a case where we don't want
+ // to do this and instead report a single `_` witness:
+ // if the user didn't actually specify a constructor
+ // in this arm, e.g., in
+ // ```
+ // let x: (Direction, Direction, bool) = ...;
+ // let (_, _, false) = x;
+ // ```
+ // we don't want to show all 16 possible witnesses
+ // `(<direction-1>, <direction-2>, true)` - we are
+ // satisfied with `(_, _, true)`. In this case,
+ // `used_ctors` is empty.
+ // The exception is: if we are at the top-level, for example in an empty match, we
+ // sometimes prefer reporting the list of constructors instead of just `_`.
+ let report_ctors_rather_than_wildcard = is_top_level && !IntRange::is_integral(pcx.ty);
+ if missing_ctors.all_ctors_are_missing() && !report_ctors_rather_than_wildcard {
+ // All constructors are unused. Add a wild pattern
+ // rather than each individual constructor.
+ usefulness.apply_wildcard(pcx.ty)
+ } else {
+ // Construct for each missing constructor a "wild" version of this
+ // constructor, that matches everything that can be built with
+ // it. For example, if `ctor` is a `Constructor::Variant` for
+ // `Option::Some`, we get the pattern `Some(_)`.
+ usefulness.apply_missing_ctors(cx, pcx.ty, &missing_ctors)
+ }
+ }
+ }
+}
+
+/// A shorthand for the `U(S(c, P), S(c, q))` operation from the paper. I.e., `is_useful` applied
+/// to the specialised version of both the pattern matrix `P` and the new pattern `q`.
+fn is_useful_specialized<'p, 'tcx>(
+ cx: &mut MatchCheckCtxt<'p, 'tcx>,
+ matrix: &Matrix<'p, 'tcx>,
+ v: &PatStack<'p, 'tcx>,
+ ctor: Constructor<'tcx>,
+ lty: Ty<'tcx>,
+ witness_preference: WitnessPreference,
+ hir_id: HirId,
+) -> Usefulness<'tcx, 'p> {
+ debug!("is_useful_specialized({:#?}, {:#?}, {:?})", v, ctor, lty);
+
+ let ctor_wild_subpatterns =
+ cx.pattern_arena.alloc_from_iter(ctor.wildcard_subpatterns(cx, lty));
+ let matrix = matrix.specialize_constructor(cx, &ctor, ctor_wild_subpatterns);
+ v.specialize_constructor(cx, &ctor, ctor_wild_subpatterns)
+ .map(|v| is_useful(cx, &matrix, &v, witness_preference, hir_id, false))
+ .map(|u| u.apply_constructor(cx, &ctor, lty))
+ .unwrap_or(NotUseful)
+}
+
+/// Determines the constructor that the given pattern can be specialized to.
+/// Returns `None` in case of a catch-all, which can't be specialized.
+fn pat_constructor<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ pat: &Pat<'tcx>,
+) -> Option<Constructor<'tcx>> {
+ match *pat.kind {
+ PatKind::AscribeUserType { .. } => bug!(), // Handled by `expand_pattern`
+ PatKind::Binding { .. } | PatKind::Wild => None,
+ PatKind::Leaf { .. } | PatKind::Deref { .. } => Some(Single),
+ PatKind::Variant { adt_def, variant_index, .. } => {
+ Some(Variant(adt_def.variants[variant_index].def_id))
+ }
+ PatKind::Constant { value } => {
+ if let Some(int_range) = IntRange::from_const(tcx, param_env, value, pat.span) {
+ Some(IntRange(int_range))
+ } else {
+ match (value.val, &value.ty.kind) {
+ (_, ty::Array(_, n)) => {
+ let len = n.eval_usize(tcx, param_env);
+ Some(Slice(Slice { array_len: Some(len), kind: FixedLen(len) }))
+ }
+ (ty::ConstKind::Value(ConstValue::Slice { start, end, .. }), ty::Slice(_)) => {
+ let len = (end - start) as u64;
+ Some(Slice(Slice { array_len: None, kind: FixedLen(len) }))
+ }
+ // FIXME(oli-obk): implement `deref` for `ConstValue`
+ // (ty::ConstKind::Value(ConstValue::ByRef { .. }), ty::Slice(_)) => { ... }
+ _ => Some(ConstantValue(value)),
+ }
+ }
+ }
+ PatKind::Range(PatRange { lo, hi, end }) => {
+ let ty = lo.ty;
+ if let Some(int_range) = IntRange::from_range(
+ tcx,
+ lo.eval_bits(tcx, param_env, lo.ty),
+ hi.eval_bits(tcx, param_env, hi.ty),
+ ty,
+ &end,
+ pat.span,
+ ) {
+ Some(IntRange(int_range))
+ } else {
+ Some(FloatRange(lo, hi, end))
+ }
+ }
+ PatKind::Array { ref prefix, ref slice, ref suffix }
+ | PatKind::Slice { ref prefix, ref slice, ref suffix } => {
+ let array_len = match pat.ty.kind {
+ ty::Array(_, length) => Some(length.eval_usize(tcx, param_env)),
+ ty::Slice(_) => None,
+ _ => span_bug!(pat.span, "bad ty {:?} for slice pattern", pat.ty),
+ };
+ let prefix = prefix.len() as u64;
+ let suffix = suffix.len() as u64;
+ let kind =
+ if slice.is_some() { VarLen(prefix, suffix) } else { FixedLen(prefix + suffix) };
+ Some(Slice(Slice { array_len, kind }))
+ }
+ PatKind::Or { .. } => bug!("Or-pattern should have been expanded earlier on."),
+ }
+}
+
+// checks whether a constant is equal to a user-written slice pattern. Only supports byte slices,
+// meaning all other types will compare unequal and thus equal patterns often do not cause the
+// second pattern to lint about unreachable match arms.
+fn slice_pat_covered_by_const<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ _span: Span,
+ const_val: &'tcx ty::Const<'tcx>,
+ prefix: &[Pat<'tcx>],
+ slice: &Option<Pat<'tcx>>,
+ suffix: &[Pat<'tcx>],
+ param_env: ty::ParamEnv<'tcx>,
+) -> Result<bool, ErrorReported> {
+ let const_val_val = if let ty::ConstKind::Value(val) = const_val.val {
+ val
+ } else {
+ bug!(
+ "slice_pat_covered_by_const: {:#?}, {:#?}, {:#?}, {:#?}",
+ const_val,
+ prefix,
+ slice,
+ suffix,
+ )
+ };
+
+ let data: &[u8] = match (const_val_val, &const_val.ty.kind) {
+ (ConstValue::ByRef { offset, alloc, .. }, ty::Array(t, n)) => {
+ assert_eq!(*t, tcx.types.u8);
+ let n = n.eval_usize(tcx, param_env);
+ let ptr = Pointer::new(AllocId(0), offset);
+ alloc.get_bytes(&tcx, ptr, Size::from_bytes(n)).unwrap()
+ }
+ (ConstValue::Slice { data, start, end }, ty::Slice(t)) => {
+ assert_eq!(*t, tcx.types.u8);
+ let ptr = Pointer::new(AllocId(0), Size::from_bytes(start as u64));
+ data.get_bytes(&tcx, ptr, Size::from_bytes((end - start) as u64)).unwrap()
+ }
+ // FIXME(oli-obk): create a way to extract fat pointers from ByRef
+ (_, ty::Slice(_)) => return Ok(false),
+ _ => bug!(
+ "slice_pat_covered_by_const: {:#?}, {:#?}, {:#?}, {:#?}",
+ const_val,
+ prefix,
+ slice,
+ suffix,
+ ),
+ };
+
+ let pat_len = prefix.len() + suffix.len();
+ if data.len() < pat_len || (slice.is_none() && data.len() > pat_len) {
+ return Ok(false);
+ }
+
+ for (ch, pat) in data[..prefix.len()]
+ .iter()
+ .zip(prefix)
+ .chain(data[data.len() - suffix.len()..].iter().zip(suffix))
+ {
+ match pat.kind {
+ box PatKind::Constant { value } => {
+ let b = value.eval_bits(tcx, param_env, pat.ty);
+ assert_eq!(b as u8 as u128, b);
+ if b as u8 != *ch {
+ return Ok(false);
+ }
+ }
+ _ => {}
+ }
+ }
+
+ Ok(true)
+}
+
+/// For exhaustive integer matching, some constructors are grouped within other constructors
+/// (namely integer typed values are grouped within ranges). However, when specialising these
+/// constructors, we want to be specialising for the underlying constructors (the integers), not
+/// the groups (the ranges). Thus we need to split the groups up. Splitting them up naïvely would
+/// mean creating a separate constructor for every single value in the range, which is clearly
+/// impractical. However, observe that for some ranges of integers, the specialisation will be
+/// identical across all values in that range (i.e., there are equivalence classes of ranges of
+/// constructors based on their `is_useful_specialized` outcome). These classes are grouped by
+/// the patterns that apply to them (in the matrix `P`). We can split the range whenever the
+/// patterns that apply to that range (specifically: the patterns that *intersect* with that range)
+/// change.
+/// Our solution, therefore, is to split the range constructor into subranges at every single point
+/// the group of intersecting patterns changes (using the method described below).
+/// And voilà! We're testing precisely those ranges that we need to, without any exhaustive matching
+/// on actual integers. The nice thing about this is that the number of subranges is linear in the
+/// number of rows in the matrix (i.e., the number of cases in the `match` statement), so we don't
+/// need to be worried about matching over gargantuan ranges.
+///
+/// Essentially, given the first column of a matrix representing ranges, looking like the following:
+///
+/// |------| |----------| |-------| ||
+/// |-------| |-------| |----| ||
+/// |---------|
+///
+/// We split the ranges up into equivalence classes so the ranges are no longer overlapping:
+///
+/// |--|--|||-||||--||---|||-------| |-|||| ||
+///
+/// The logic for determining how to split the ranges is fairly straightforward: we calculate
+/// boundaries for each interval range, sort them, then create constructors for each new interval
+/// between every pair of boundary points. (This essentially sums up to performing the intuitive
+/// merging operation depicted above.)
+///
+/// `hir_id` is `None` when we're evaluating the wildcard pattern, do not lint for overlapping in
+/// ranges that case.
+///
+/// This also splits variable-length slices into fixed-length slices.
+fn split_grouped_constructors<'p, 'tcx>(
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ pcx: PatCtxt<'tcx>,
+ ctors: Vec<Constructor<'tcx>>,
+ matrix: &Matrix<'p, 'tcx>,
+ span: Span,
+ hir_id: Option<HirId>,
+) -> Vec<Constructor<'tcx>> {
+ let ty = pcx.ty;
+ let mut split_ctors = Vec::with_capacity(ctors.len());
+ debug!("split_grouped_constructors({:#?}, {:#?})", matrix, ctors);
+
+ for ctor in ctors.into_iter() {
+ match ctor {
+ IntRange(ctor_range) if ctor_range.treat_exhaustively(tcx) => {
+ // Fast-track if the range is trivial. In particular, don't do the overlapping
+ // ranges check.
+ if ctor_range.is_singleton() {
+ split_ctors.push(IntRange(ctor_range));
+ continue;
+ }
+
+ /// Represents a border between 2 integers. Because the intervals spanning borders
+ /// must be able to cover every integer, we need to be able to represent
+ /// 2^128 + 1 such borders.
+ #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
+ enum Border {
+ JustBefore(u128),
+ AfterMax,
+ }
+
+ // A function for extracting the borders of an integer interval.
+ fn range_borders(r: IntRange<'_>) -> impl Iterator<Item = Border> {
+ let (lo, hi) = r.range.into_inner();
+ let from = Border::JustBefore(lo);
+ let to = match hi.checked_add(1) {
+ Some(m) => Border::JustBefore(m),
+ None => Border::AfterMax,
+ };
+ vec![from, to].into_iter()
+ }
+
+ // Collect the span and range of all the intersecting ranges to lint on likely
+ // incorrect range patterns. (#63987)
+ let mut overlaps = vec![];
+ // `borders` is the set of borders between equivalence classes: each equivalence
+ // class lies between 2 borders.
+ let row_borders = matrix
+ .0
+ .iter()
+ .flat_map(|row| {
+ IntRange::from_pat(tcx, param_env, row.head()).map(|r| (r, row.len()))
+ })
+ .flat_map(|(range, row_len)| {
+ let intersection = ctor_range.intersection(tcx, &range);
+ let should_lint = ctor_range.suspicious_intersection(&range);
+ if let (Some(range), 1, true) = (&intersection, row_len, should_lint) {
+ // FIXME: for now, only check for overlapping ranges on simple range
+ // patterns. Otherwise with the current logic the following is detected
+ // as overlapping:
+ // match (10u8, true) {
+ // (0 ..= 125, false) => {}
+ // (126 ..= 255, false) => {}
+ // (0 ..= 255, true) => {}
+ // }
+ overlaps.push(range.clone());
+ }
+ intersection
+ })
+ .flat_map(|range| range_borders(range));
+ let ctor_borders = range_borders(ctor_range.clone());
+ let mut borders: Vec<_> = row_borders.chain(ctor_borders).collect();
+ borders.sort_unstable();
+
+ lint_overlapping_patterns(tcx, hir_id, ctor_range, ty, overlaps);
+
+ // We're going to iterate through every adjacent pair of borders, making sure that
+ // each represents an interval of nonnegative length, and convert each such
+ // interval into a constructor.
+ split_ctors.extend(
+ borders
+ .windows(2)
+ .filter_map(|window| match (window[0], window[1]) {
+ (Border::JustBefore(n), Border::JustBefore(m)) => {
+ if n < m {
+ Some(IntRange { range: n..=(m - 1), ty, span })
+ } else {
+ None
+ }
+ }
+ (Border::JustBefore(n), Border::AfterMax) => {
+ Some(IntRange { range: n..=u128::MAX, ty, span })
+ }
+ (Border::AfterMax, _) => None,
+ })
+ .map(IntRange),
+ );
+ }
+ Slice(Slice { array_len, kind: VarLen(self_prefix, self_suffix) }) => {
+ // The exhaustiveness-checking paper does not include any details on
+ // checking variable-length slice patterns. However, they are matched
+ // by an infinite collection of fixed-length array patterns.
+ //
+ // Checking the infinite set directly would take an infinite amount
+ // of time. However, it turns out that for each finite set of
+ // patterns `P`, all sufficiently large array lengths are equivalent:
+ //
+ // Each slice `s` with a "sufficiently-large" length `l ≥ L` that applies
+ // to exactly the subset `Pₜ` of `P` can be transformed to a slice
+ // `sₘ` for each sufficiently-large length `m` that applies to exactly
+ // the same subset of `P`.
+ //
+ // Because of that, each witness for reachability-checking from one
+ // of the sufficiently-large lengths can be transformed to an
+ // equally-valid witness from any other length, so we only have
+ // to check slice lengths from the "minimal sufficiently-large length"
+ // and below.
+ //
+ // Note that the fact that there is a *single* `sₘ` for each `m`
+ // not depending on the specific pattern in `P` is important: if
+ // you look at the pair of patterns
+ // `[true, ..]`
+ // `[.., false]`
+ // Then any slice of length ≥1 that matches one of these two
+ // patterns can be trivially turned to a slice of any
+ // other length ≥1 that matches them and vice-versa - for
+ // but the slice from length 2 `[false, true]` that matches neither
+ // of these patterns can't be turned to a slice from length 1 that
+ // matches neither of these patterns, so we have to consider
+ // slices from length 2 there.
+ //
+ // Now, to see that that length exists and find it, observe that slice
+ // patterns are either "fixed-length" patterns (`[_, _, _]`) or
+ // "variable-length" patterns (`[_, .., _]`).
+ //
+ // For fixed-length patterns, all slices with lengths *longer* than
+ // the pattern's length have the same outcome (of not matching), so
+ // as long as `L` is greater than the pattern's length we can pick
+ // any `sₘ` from that length and get the same result.
+ //
+ // For variable-length patterns, the situation is more complicated,
+ // because as seen above the precise value of `sₘ` matters.
+ //
+ // However, for each variable-length pattern `p` with a prefix of length
+ // `plₚ` and suffix of length `slₚ`, only the first `plₚ` and the last
+ // `slₚ` elements are examined.
+ //
+ // Therefore, as long as `L` is positive (to avoid concerns about empty
+ // types), all elements after the maximum prefix length and before
+ // the maximum suffix length are not examined by any variable-length
+ // pattern, and therefore can be added/removed without affecting
+ // them - creating equivalent patterns from any sufficiently-large
+ // length.
+ //
+ // Of course, if fixed-length patterns exist, we must be sure
+ // that our length is large enough to miss them all, so
+ // we can pick `L = max(max(FIXED_LEN)+1, max(PREFIX_LEN) + max(SUFFIX_LEN))`
+ //
+ // for example, with the above pair of patterns, all elements
+ // but the first and last can be added/removed, so any
+ // witness of length ≥2 (say, `[false, false, true]`) can be
+ // turned to a witness from any other length ≥2.
+
+ let mut max_prefix_len = self_prefix;
+ let mut max_suffix_len = self_suffix;
+ let mut max_fixed_len = 0;
+
+ let head_ctors =
+ matrix.heads().filter_map(|pat| pat_constructor(tcx, param_env, pat));
+ for ctor in head_ctors {
+ match ctor {
+ Slice(slice) => match slice.pattern_kind() {
+ FixedLen(len) => {
+ max_fixed_len = cmp::max(max_fixed_len, len);
+ }
+ VarLen(prefix, suffix) => {
+ max_prefix_len = cmp::max(max_prefix_len, prefix);
+ max_suffix_len = cmp::max(max_suffix_len, suffix);
+ }
+ },
+ _ => {}
+ }
+ }
+
+ // For diagnostics, we keep the prefix and suffix lengths separate, so in the case
+ // where `max_fixed_len + 1` is the largest, we adapt `max_prefix_len` accordingly,
+ // so that `L = max_prefix_len + max_suffix_len`.
+ if max_fixed_len + 1 >= max_prefix_len + max_suffix_len {
+ // The subtraction can't overflow thanks to the above check.
+ // The new `max_prefix_len` is also guaranteed to be larger than its previous
+ // value.
+ max_prefix_len = max_fixed_len + 1 - max_suffix_len;
+ }
+
+ match array_len {
+ Some(len) => {
+ let kind = if max_prefix_len + max_suffix_len < len {
+ VarLen(max_prefix_len, max_suffix_len)
+ } else {
+ FixedLen(len)
+ };
+ split_ctors.push(Slice(Slice { array_len, kind }));
+ }
+ None => {
+ // `ctor` originally covered the range `(self_prefix +
+ // self_suffix..infinity)`. We now split it into two: lengths smaller than
+ // `max_prefix_len + max_suffix_len` are treated independently as
+ // fixed-lengths slices, and lengths above are captured by a final VarLen
+ // constructor.
+ split_ctors.extend(
+ (self_prefix + self_suffix..max_prefix_len + max_suffix_len)
+ .map(|len| Slice(Slice { array_len, kind: FixedLen(len) })),
+ );
+ split_ctors.push(Slice(Slice {
+ array_len,
+ kind: VarLen(max_prefix_len, max_suffix_len),
+ }));
+ }
+ }
+ }
+ // Any other constructor can be used unchanged.
+ _ => split_ctors.push(ctor),
+ }
+ }
+
+ debug!("split_grouped_constructors(..)={:#?}", split_ctors);
+ split_ctors
+}
+
+fn lint_overlapping_patterns<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ hir_id: Option<HirId>,
+ ctor_range: IntRange<'tcx>,
+ ty: Ty<'tcx>,
+ overlaps: Vec<IntRange<'tcx>>,
+) {
+ if let (true, Some(hir_id)) = (!overlaps.is_empty(), hir_id) {
+ let mut err = tcx.struct_span_lint_hir(
+ lint::builtin::OVERLAPPING_PATTERNS,
+ hir_id,
+ ctor_range.span,
+ "multiple patterns covering the same range",
+ );
+ err.span_label(ctor_range.span, "overlapping patterns");
+ for int_range in overlaps {
+ // Use the real type for user display of the ranges:
+ err.span_label(
+ int_range.span,
+ &format!(
+ "this range overlaps on `{}`",
+ IntRange { range: int_range.range, ty, span: DUMMY_SP }.to_pat(tcx),
+ ),
+ );
+ }
+ err.emit();
+ }
+}
+
+fn constructor_covered_by_range<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ ctor: &Constructor<'tcx>,
+ pat: &Pat<'tcx>,
+) -> Option<()> {
+ if let Single = ctor {
+ return Some(());
+ }
+
+ let (pat_from, pat_to, pat_end, ty) = match *pat.kind {
+ PatKind::Constant { value } => (value, value, RangeEnd::Included, value.ty),
+ PatKind::Range(PatRange { lo, hi, end }) => (lo, hi, end, lo.ty),
+ _ => bug!("`constructor_covered_by_range` called with {:?}", pat),
+ };
+ let (ctor_from, ctor_to, ctor_end) = match *ctor {
+ ConstantValue(value) => (value, value, RangeEnd::Included),
+ FloatRange(from, to, ctor_end) => (from, to, ctor_end),
+ _ => bug!("`constructor_covered_by_range` called with {:?}", ctor),
+ };
+ trace!("constructor_covered_by_range {:#?}, {:#?}, {:#?}, {}", ctor, pat_from, pat_to, ty);
+
+ let to = compare_const_vals(tcx, ctor_to, pat_to, param_env, ty)?;
+ let from = compare_const_vals(tcx, ctor_from, pat_from, param_env, ty)?;
+ let intersects = (from == Ordering::Greater || from == Ordering::Equal)
+ && (to == Ordering::Less || (pat_end == ctor_end && to == Ordering::Equal));
+ if intersects { Some(()) } else { None }
+}
+
+fn patterns_for_variant<'p, 'tcx>(
+ cx: &mut MatchCheckCtxt<'p, 'tcx>,
+ subpatterns: &'p [FieldPat<'tcx>],
+ ctor_wild_subpatterns: &'p [Pat<'tcx>],
+ is_non_exhaustive: bool,
+) -> PatStack<'p, 'tcx> {
+ let mut result: SmallVec<_> = ctor_wild_subpatterns.iter().collect();
+
+ for subpat in subpatterns {
+ if !is_non_exhaustive || !cx.is_uninhabited(subpat.pattern.ty) {
+ result[subpat.field.index()] = &subpat.pattern;
+ }
+ }
+
+ debug!(
+ "patterns_for_variant({:#?}, {:#?}) = {:#?}",
+ subpatterns, ctor_wild_subpatterns, result
+ );
+ PatStack::from_vec(result)
+}
+
+/// This is the main specialization step. It expands the pattern
+/// into `arity` patterns based on the constructor. For most patterns, the step is trivial,
+/// for instance tuple patterns are flattened and box patterns expand into their inner pattern.
+/// Returns `None` if the pattern does not have the given constructor.
+///
+/// OTOH, slice patterns with a subslice pattern (tail @ ..) can be expanded into multiple
+/// different patterns.
+/// Structure patterns with a partial wild pattern (Foo { a: 42, .. }) have their missing
+/// fields filled with wild patterns.
+fn specialize_one_pattern<'p, 'tcx>(
+ cx: &mut MatchCheckCtxt<'p, 'tcx>,
+ pat: &'p Pat<'tcx>,
+ constructor: &Constructor<'tcx>,
+ ctor_wild_subpatterns: &'p [Pat<'tcx>],
+) -> Option<PatStack<'p, 'tcx>> {
+ if let NonExhaustive = constructor {
+ // Only a wildcard pattern can match the special extra constructor
+ return if pat.is_wildcard() { Some(PatStack::default()) } else { None };
+ }
+
+ let result = match *pat.kind {
+ PatKind::AscribeUserType { .. } => bug!(), // Handled by `expand_pattern`
+
+ PatKind::Binding { .. } | PatKind::Wild => Some(ctor_wild_subpatterns.iter().collect()),
+
+ PatKind::Variant { adt_def, variant_index, ref subpatterns, .. } => {
+ let ref variant = adt_def.variants[variant_index];
+ let is_non_exhaustive = cx.is_foreign_non_exhaustive_variant(pat.ty, variant);
+ Some(Variant(variant.def_id))
+ .filter(|variant_constructor| variant_constructor == constructor)
+ .map(|_| {
+ patterns_for_variant(cx, subpatterns, ctor_wild_subpatterns, is_non_exhaustive)
+ })
+ }
+
+ PatKind::Leaf { ref subpatterns } => {
+ Some(patterns_for_variant(cx, subpatterns, ctor_wild_subpatterns, false))
+ }
+
+ PatKind::Deref { ref subpattern } => Some(PatStack::from_pattern(subpattern)),
+
+ PatKind::Constant { value } if constructor.is_slice() => {
+ // We extract an `Option` for the pointer because slices of zero
+ // elements don't necessarily point to memory, they are usually
+ // just integers. The only time they should be pointing to memory
+ // is when they are subslices of nonzero slices.
+ let (alloc, offset, n, ty) = match value.ty.kind {
+ ty::Array(t, n) => {
+ let n = n.eval_usize(cx.tcx, cx.param_env);
+ // Shortcut for `n == 0` where no matter what `alloc` and `offset` we produce,
+ // the result would be exactly what we early return here.
+ if n == 0 {
+ if ctor_wild_subpatterns.len() as u64 == 0 {
+ return Some(PatStack::from_slice(&[]));
+ } else {
+ return None;
+ }
+ }
+ match value.val {
+ ty::ConstKind::Value(ConstValue::ByRef { offset, alloc, .. }) => {
+ (Cow::Borrowed(alloc), offset, n, t)
+ }
+ _ => span_bug!(pat.span, "array pattern is {:?}", value,),
+ }
+ }
+ ty::Slice(t) => {
+ match value.val {
+ ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => {
+ let offset = Size::from_bytes(start as u64);
+ let n = (end - start) as u64;
+ (Cow::Borrowed(data), offset, n, t)
+ }
+ ty::ConstKind::Value(ConstValue::ByRef { .. }) => {
+ // FIXME(oli-obk): implement `deref` for `ConstValue`
+ return None;
+ }
+ _ => span_bug!(
+ pat.span,
+ "slice pattern constant must be scalar pair but is {:?}",
+ value,
+ ),
+ }
+ }
+ _ => span_bug!(
+ pat.span,
+ "unexpected const-val {:?} with ctor {:?}",
+ value,
+ constructor,
+ ),
+ };
+ if ctor_wild_subpatterns.len() as u64 == n {
+ // convert a constant slice/array pattern to a list of patterns.
+ let layout = cx.tcx.layout_of(cx.param_env.and(ty)).ok()?;
+ let ptr = Pointer::new(AllocId(0), offset);
+ (0..n)
+ .map(|i| {
+ let ptr = ptr.offset(layout.size * i, &cx.tcx).ok()?;
+ let scalar = alloc.read_scalar(&cx.tcx, ptr, layout.size).ok()?;
+ let scalar = scalar.not_undef().ok()?;
+ let value = ty::Const::from_scalar(cx.tcx, scalar, ty);
+ let pattern =
+ Pat { ty, span: pat.span, kind: box PatKind::Constant { value } };
+ Some(&*cx.pattern_arena.alloc(pattern))
+ })
+ .collect()
+ } else {
+ None
+ }
+ }
+
+ PatKind::Constant { .. } | PatKind::Range { .. } => {
+ // If the constructor is a:
+ // - Single value: add a row if the pattern contains the constructor.
+ // - Range: add a row if the constructor intersects the pattern.
+ if let IntRange(ctor) = constructor {
+ match IntRange::from_pat(cx.tcx, cx.param_env, pat) {
+ Some(pat) => ctor.intersection(cx.tcx, &pat).map(|_| {
+ // Constructor splitting should ensure that all intersections we encounter
+ // are actually inclusions.
+ assert!(ctor.is_subrange(&pat));
+ PatStack::default()
+ }),
+ _ => None,
+ }
+ } else {
+ // Fallback for non-ranges and ranges that involve
+ // floating-point numbers, which are not conveniently handled
+ // by `IntRange`. For these cases, the constructor may not be a
+ // range so intersection actually devolves into being covered
+ // by the pattern.
+ constructor_covered_by_range(cx.tcx, cx.param_env, constructor, pat)
+ .map(|()| PatStack::default())
+ }
+ }
+
+ PatKind::Array { ref prefix, ref slice, ref suffix }
+ | PatKind::Slice { ref prefix, ref slice, ref suffix } => match *constructor {
+ Slice(_) => {
+ let pat_len = prefix.len() + suffix.len();
+ if let Some(slice_count) = ctor_wild_subpatterns.len().checked_sub(pat_len) {
+ if slice_count == 0 || slice.is_some() {
+ Some(
+ prefix
+ .iter()
+ .chain(
+ ctor_wild_subpatterns
+ .iter()
+ .skip(prefix.len())
+ .take(slice_count)
+ .chain(suffix.iter()),
+ )
+ .collect(),
+ )
+ } else {
+ None
+ }
+ } else {
+ None
+ }
+ }
+ ConstantValue(cv) => {
+ match slice_pat_covered_by_const(
+ cx.tcx,
+ pat.span,
+ cv,
+ prefix,
+ slice,
+ suffix,
+ cx.param_env,
+ ) {
+ Ok(true) => Some(PatStack::default()),
+ Ok(false) => None,
+ Err(ErrorReported) => None,
+ }
+ }
+ _ => span_bug!(pat.span, "unexpected ctor {:?} for slice pat", constructor),
+ },
+
+ PatKind::Or { .. } => bug!("Or-pattern should have been expanded earlier on."),
+ };
+ debug!("specialize({:#?}, {:#?}) = {:#?}", pat, ctor_wild_subpatterns, result);
+
+ result
+}
--- /dev/null
+use super::_match::Usefulness::*;
+use super::_match::WitnessPreference::*;
+use super::_match::{expand_pattern, is_useful, MatchCheckCtxt, Matrix, PatStack};
+
+use super::{PatCtxt, PatKind, PatternError};
+
+use rustc::hir::map::Map;
+use rustc::lint;
+use rustc::session::parse::feature_err;
+use rustc::session::Session;
+use rustc::ty::{self, Ty, TyCtxt};
+use rustc_error_codes::*;
+use rustc_errors::{error_code, struct_span_err, Applicability, DiagnosticBuilder};
+use rustc_hir as hir;
+use rustc_hir::def::*;
+use rustc_hir::def_id::DefId;
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
+use rustc_hir::{HirId, Pat};
+use rustc_span::symbol::sym;
+use rustc_span::{MultiSpan, Span};
+use syntax::ast::Mutability;
+
+use std::slice;
+
+crate fn check_match(tcx: TyCtxt<'_>, def_id: DefId) {
+ let body_id = match tcx.hir().as_local_hir_id(def_id) {
+ None => return,
+ Some(id) => tcx.hir().body_owned_by(id),
+ };
+
+ let mut visitor =
+ MatchVisitor { tcx, tables: tcx.body_tables(body_id), param_env: tcx.param_env(def_id) };
+ visitor.visit_body(tcx.hir().body(body_id));
+}
+
+fn create_e0004(sess: &Session, sp: Span, error_message: String) -> DiagnosticBuilder<'_> {
+ struct_span_err!(sess, sp, E0004, "{}", &error_message)
+}
+
+struct MatchVisitor<'a, 'tcx> {
+ tcx: TyCtxt<'tcx>,
+ tables: &'a ty::TypeckTables<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+}
+
+impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> {
+ NestedVisitorMap::None
+ }
+
+ fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) {
+ intravisit::walk_expr(self, ex);
+
+ if let hir::ExprKind::Match(ref scrut, ref arms, source) = ex.kind {
+ self.check_match(scrut, arms, source);
+ }
+ }
+
+ fn visit_local(&mut self, loc: &'tcx hir::Local<'tcx>) {
+ intravisit::walk_local(self, loc);
+
+ let (msg, sp) = match loc.source {
+ hir::LocalSource::Normal => ("local binding", Some(loc.span)),
+ hir::LocalSource::ForLoopDesugar => ("`for` loop binding", None),
+ hir::LocalSource::AsyncFn => ("async fn binding", None),
+ hir::LocalSource::AwaitDesugar => ("`await` future binding", None),
+ };
+ self.check_irrefutable(&loc.pat, msg, sp);
+
+ // Check legality of move bindings and `@` patterns.
+ self.check_patterns(false, &loc.pat);
+ }
+
+ fn visit_body(&mut self, body: &'tcx hir::Body<'tcx>) {
+ intravisit::walk_body(self, body);
+
+ for param in body.params {
+ self.check_irrefutable(¶m.pat, "function argument", None);
+ self.check_patterns(false, ¶m.pat);
+ }
+ }
+}
+
+impl PatCtxt<'_, '_> {
+ fn report_inlining_errors(&self, pat_span: Span) {
+ for error in &self.errors {
+ match *error {
+ PatternError::StaticInPattern(span) => {
+ self.span_e0158(span, "statics cannot be referenced in patterns")
+ }
+ PatternError::AssocConstInPattern(span) => {
+ self.span_e0158(span, "associated consts cannot be referenced in patterns")
+ }
+ PatternError::FloatBug => {
+ // FIXME(#31407) this is only necessary because float parsing is buggy
+ ::rustc::mir::interpret::struct_error(
+ self.tcx.at(pat_span),
+ "could not evaluate float literal (see issue #31407)",
+ )
+ .emit();
+ }
+ PatternError::NonConstPath(span) => {
+ ::rustc::mir::interpret::struct_error(
+ self.tcx.at(span),
+ "runtime values cannot be referenced in patterns",
+ )
+ .emit();
+ }
+ }
+ }
+ }
+
+ fn span_e0158(&self, span: Span, text: &str) {
+ struct_span_err!(self.tcx.sess, span, E0158, "{}", text).emit();
+ }
+}
+
+impl<'tcx> MatchVisitor<'_, 'tcx> {
+ fn check_patterns(&mut self, has_guard: bool, pat: &Pat<'_>) {
+ check_legality_of_move_bindings(self, has_guard, pat);
+ check_borrow_conflicts_in_at_patterns(self, pat);
+ if !self.tcx.features().bindings_after_at {
+ check_legality_of_bindings_in_at_patterns(self, pat);
+ }
+ }
+
+ fn check_match(
+ &mut self,
+ scrut: &hir::Expr<'_>,
+ arms: &'tcx [hir::Arm<'tcx>],
+ source: hir::MatchSource,
+ ) {
+ for arm in arms {
+ // First, check legality of move bindings.
+ self.check_patterns(arm.guard.is_some(), &arm.pat);
+
+ // Second, perform some lints.
+ check_for_bindings_named_same_as_variants(self, &arm.pat);
+ }
+
+ let module = self.tcx.hir().get_module_parent(scrut.hir_id);
+ MatchCheckCtxt::create_and_enter(self.tcx, self.param_env, module, |ref mut cx| {
+ let mut have_errors = false;
+
+ let inlined_arms: Vec<_> = arms
+ .iter()
+ .map(|arm| {
+ let mut patcx = PatCtxt::new(self.tcx, self.param_env, self.tables);
+ patcx.include_lint_checks();
+ let pattern = patcx.lower_pattern(&arm.pat);
+ let pattern: &_ = cx.pattern_arena.alloc(expand_pattern(cx, pattern));
+ if !patcx.errors.is_empty() {
+ patcx.report_inlining_errors(arm.pat.span);
+ have_errors = true;
+ }
+ (pattern, &*arm.pat, arm.guard.is_some())
+ })
+ .collect();
+
+ // Bail out early if inlining failed.
+ if have_errors {
+ return;
+ }
+
+ // Fourth, check for unreachable arms.
+ let matrix = check_arms(cx, &inlined_arms, source);
+
+ // Fifth, check if the match is exhaustive.
+ let scrut_ty = self.tables.node_type(scrut.hir_id);
+ // Note: An empty match isn't the same as an empty matrix for diagnostics purposes,
+ // since an empty matrix can occur when there are arms, if those arms all have guards.
+ let is_empty_match = inlined_arms.is_empty();
+ check_exhaustive(cx, scrut_ty, scrut.span, &matrix, scrut.hir_id, is_empty_match);
+ })
+ }
+
+ fn check_irrefutable(&self, pat: &'tcx Pat<'tcx>, origin: &str, sp: Option<Span>) {
+ let module = self.tcx.hir().get_module_parent(pat.hir_id);
+ MatchCheckCtxt::create_and_enter(self.tcx, self.param_env, module, |ref mut cx| {
+ let mut patcx = PatCtxt::new(self.tcx, self.param_env, self.tables);
+ patcx.include_lint_checks();
+ let pattern = patcx.lower_pattern(pat);
+ let pattern_ty = pattern.ty;
+ let pattern = cx.pattern_arena.alloc(expand_pattern(cx, pattern));
+ let pats: Matrix<'_, '_> = vec![PatStack::from_pattern(pattern)].into_iter().collect();
+
+ let witnesses = match check_not_useful(cx, pattern_ty, &pats, pat.hir_id) {
+ Ok(_) => return,
+ Err(err) => err,
+ };
+
+ let joined_patterns = joined_uncovered_patterns(&witnesses);
+ let mut err = struct_span_err!(
+ self.tcx.sess,
+ pat.span,
+ E0005,
+ "refutable pattern in {}: {} not covered",
+ origin,
+ joined_patterns
+ );
+ let suggest_if_let = match &pat.kind {
+ hir::PatKind::Path(hir::QPath::Resolved(None, path))
+ if path.segments.len() == 1 && path.segments[0].args.is_none() =>
+ {
+ const_not_var(&mut err, cx.tcx, pat, path);
+ false
+ }
+ _ => {
+ err.span_label(
+ pat.span,
+ pattern_not_covered_label(&witnesses, &joined_patterns),
+ );
+ true
+ }
+ };
+
+ if let (Some(span), true) = (sp, suggest_if_let) {
+ err.note(
+ "`let` bindings require an \"irrefutable pattern\", like a `struct` or \
+ an `enum` with only one variant",
+ );
+ if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
+ err.span_suggestion(
+ span,
+ "you might want to use `if let` to ignore the variant that isn't matched",
+ format!("if {} {{ /* */ }}", &snippet[..snippet.len() - 1]),
+ Applicability::HasPlaceholders,
+ );
+ }
+ err.note(
+ "for more information, visit \
+ https://doc.rust-lang.org/book/ch18-02-refutability.html",
+ );
+ }
+
+ adt_defined_here(cx, &mut err, pattern_ty, &witnesses);
+ err.emit();
+ });
+ }
+}
+
+/// A path pattern was interpreted as a constant, not a new variable.
+/// This caused an irrefutable match failure in e.g. `let`.
+fn const_not_var(
+ err: &mut DiagnosticBuilder<'_>,
+ tcx: TyCtxt<'_>,
+ pat: &Pat<'_>,
+ path: &hir::Path<'_>,
+) {
+ let descr = path.res.descr();
+ err.span_label(
+ pat.span,
+ format!("interpreted as {} {} pattern, not a new variable", path.res.article(), descr,),
+ );
+
+ err.span_suggestion(
+ pat.span,
+ "introduce a variable instead",
+ format!("{}_var", path.segments[0].ident).to_lowercase(),
+ // Cannot use `MachineApplicable` as it's not really *always* correct
+ // because there may be such an identifier in scope or the user maybe
+ // really wanted to match against the constant. This is quite unlikely however.
+ Applicability::MaybeIncorrect,
+ );
+
+ if let Some(span) = tcx.hir().res_span(path.res) {
+ err.span_label(span, format!("{} defined here", descr));
+ }
+}
+
+fn check_for_bindings_named_same_as_variants(cx: &MatchVisitor<'_, '_>, pat: &Pat<'_>) {
+ pat.walk_always(|p| {
+ if let hir::PatKind::Binding(_, _, ident, None) = p.kind {
+ if let Some(ty::BindByValue(hir::Mutability::Not)) =
+ cx.tables.extract_binding_mode(cx.tcx.sess, p.hir_id, p.span)
+ {
+ let pat_ty = cx.tables.pat_ty(p).peel_refs();
+ if let ty::Adt(edef, _) = pat_ty.kind {
+ if edef.is_enum()
+ && edef.variants.iter().any(|variant| {
+ variant.ident == ident && variant.ctor_kind == CtorKind::Const
+ })
+ {
+ let ty_path = cx.tcx.def_path_str(edef.did);
+ cx.tcx
+ .struct_span_lint_hir(
+ lint::builtin::BINDINGS_WITH_VARIANT_NAME,
+ p.hir_id,
+ p.span,
+ &format!(
+ "pattern binding `{}` is named the same as one \
+ of the variants of the type `{}`",
+ ident, ty_path
+ ),
+ )
+ .code(error_code!(E0170))
+ .span_suggestion(
+ p.span,
+ "to match on the variant, qualify the path",
+ format!("{}::{}", ty_path, ident),
+ Applicability::MachineApplicable,
+ )
+ .emit();
+ }
+ }
+ }
+ }
+ });
+}
+
+/// Checks for common cases of "catchall" patterns that may not be intended as such.
+fn pat_is_catchall(pat: &Pat<'_>) -> bool {
+ match pat.kind {
+ hir::PatKind::Binding(.., None) => true,
+ hir::PatKind::Binding(.., Some(ref s)) => pat_is_catchall(s),
+ hir::PatKind::Ref(ref s, _) => pat_is_catchall(s),
+ hir::PatKind::Tuple(ref v, _) => v.iter().all(|p| pat_is_catchall(&p)),
+ _ => false,
+ }
+}
+
+/// Check for unreachable patterns.
+fn check_arms<'p, 'tcx>(
+ cx: &mut MatchCheckCtxt<'p, 'tcx>,
+ arms: &[(&'p super::Pat<'tcx>, &hir::Pat<'_>, bool)],
+ source: hir::MatchSource,
+) -> Matrix<'p, 'tcx> {
+ let mut seen = Matrix::empty();
+ let mut catchall = None;
+ for (arm_index, (pat, hir_pat, has_guard)) in arms.iter().enumerate() {
+ let v = PatStack::from_pattern(pat);
+
+ match is_useful(cx, &seen, &v, LeaveOutWitness, hir_pat.hir_id, true) {
+ NotUseful => {
+ match source {
+ hir::MatchSource::IfDesugar { .. } | hir::MatchSource::WhileDesugar => bug!(),
+
+ hir::MatchSource::IfLetDesugar { .. } | hir::MatchSource::WhileLetDesugar => {
+ // check which arm we're on.
+ match arm_index {
+ // The arm with the user-specified pattern.
+ 0 => {
+ cx.tcx.lint_hir(
+ lint::builtin::UNREACHABLE_PATTERNS,
+ hir_pat.hir_id,
+ pat.span,
+ "unreachable pattern",
+ );
+ }
+ // The arm with the wildcard pattern.
+ 1 => {
+ let msg = match source {
+ hir::MatchSource::IfLetDesugar { .. } => {
+ "irrefutable if-let pattern"
+ }
+ hir::MatchSource::WhileLetDesugar => {
+ "irrefutable while-let pattern"
+ }
+ _ => bug!(),
+ };
+ cx.tcx.lint_hir(
+ lint::builtin::IRREFUTABLE_LET_PATTERNS,
+ hir_pat.hir_id,
+ pat.span,
+ msg,
+ );
+ }
+ _ => bug!(),
+ }
+ }
+
+ hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => {
+ let mut err = cx.tcx.struct_span_lint_hir(
+ lint::builtin::UNREACHABLE_PATTERNS,
+ hir_pat.hir_id,
+ pat.span,
+ "unreachable pattern",
+ );
+ // if we had a catchall pattern, hint at that
+ if let Some(catchall) = catchall {
+ err.span_label(pat.span, "unreachable pattern");
+ err.span_label(catchall, "matches any value");
+ }
+ err.emit();
+ }
+
+ // Unreachable patterns in try and await expressions occur when one of
+ // the arms are an uninhabited type. Which is OK.
+ hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar => {}
+ }
+ }
+ Useful(unreachable_subpatterns) => {
+ for pat in unreachable_subpatterns {
+ cx.tcx.lint_hir(
+ lint::builtin::UNREACHABLE_PATTERNS,
+ hir_pat.hir_id,
+ pat.span,
+ "unreachable pattern",
+ );
+ }
+ }
+ UsefulWithWitness(_) => bug!(),
+ }
+ if !has_guard {
+ seen.push(v);
+ if catchall.is_none() && pat_is_catchall(hir_pat) {
+ catchall = Some(pat.span);
+ }
+ }
+ }
+ seen
+}
+
+fn check_not_useful<'p, 'tcx>(
+ cx: &mut MatchCheckCtxt<'p, 'tcx>,
+ ty: Ty<'tcx>,
+ matrix: &Matrix<'p, 'tcx>,
+ hir_id: HirId,
+) -> Result<(), Vec<super::Pat<'tcx>>> {
+ let wild_pattern = cx.pattern_arena.alloc(super::Pat::wildcard_from_ty(ty));
+ let v = PatStack::from_pattern(wild_pattern);
+ match is_useful(cx, matrix, &v, ConstructWitness, hir_id, true) {
+ NotUseful => Ok(()), // This is good, wildcard pattern isn't reachable.
+ UsefulWithWitness(pats) => Err(if pats.is_empty() {
+ bug!("Exhaustiveness check returned no witnesses")
+ } else {
+ pats.into_iter().map(|w| w.single_pattern()).collect()
+ }),
+ Useful(_) => bug!(),
+ }
+}
+
+fn check_exhaustive<'p, 'tcx>(
+ cx: &mut MatchCheckCtxt<'p, 'tcx>,
+ scrut_ty: Ty<'tcx>,
+ sp: Span,
+ matrix: &Matrix<'p, 'tcx>,
+ hir_id: HirId,
+ is_empty_match: bool,
+) {
+ // In the absence of the `exhaustive_patterns` feature, empty matches are not detected by
+ // `is_useful` to exhaustively match uninhabited types, so we manually check here.
+ if is_empty_match && !cx.tcx.features().exhaustive_patterns {
+ let scrutinee_is_visibly_uninhabited = match scrut_ty.kind {
+ ty::Never => true,
+ ty::Adt(def, _) => {
+ def.is_enum()
+ && def.variants.is_empty()
+ && !cx.is_foreign_non_exhaustive_enum(scrut_ty)
+ }
+ _ => false,
+ };
+ if scrutinee_is_visibly_uninhabited {
+ // If the type *is* uninhabited, an empty match is vacuously exhaustive.
+ return;
+ }
+ }
+
+ let witnesses = match check_not_useful(cx, scrut_ty, matrix, hir_id) {
+ Ok(_) => return,
+ Err(err) => err,
+ };
+
+ let non_empty_enum = match scrut_ty.kind {
+ ty::Adt(def, _) => def.is_enum() && !def.variants.is_empty(),
+ _ => false,
+ };
+ // In the case of an empty match, replace the '`_` not covered' diagnostic with something more
+ // informative.
+ let mut err;
+ if is_empty_match && !non_empty_enum {
+ err = create_e0004(
+ cx.tcx.sess,
+ sp,
+ format!("non-exhaustive patterns: type `{}` is non-empty", scrut_ty),
+ );
+ } else {
+ let joined_patterns = joined_uncovered_patterns(&witnesses);
+ err = create_e0004(
+ cx.tcx.sess,
+ sp,
+ format!("non-exhaustive patterns: {} not covered", joined_patterns),
+ );
+ err.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns));
+ };
+
+ adt_defined_here(cx, &mut err, scrut_ty, &witnesses);
+ err.help(
+ "ensure that all possible cases are being handled, \
+ possibly by adding wildcards or more match arms",
+ );
+ err.emit();
+}
+
+fn joined_uncovered_patterns(witnesses: &[super::Pat<'_>]) -> String {
+ const LIMIT: usize = 3;
+ match witnesses {
+ [] => bug!(),
+ [witness] => format!("`{}`", witness),
+ [head @ .., tail] if head.len() < LIMIT => {
+ let head: Vec<_> = head.iter().map(<_>::to_string).collect();
+ format!("`{}` and `{}`", head.join("`, `"), tail)
+ }
+ _ => {
+ let (head, tail) = witnesses.split_at(LIMIT);
+ let head: Vec<_> = head.iter().map(<_>::to_string).collect();
+ format!("`{}` and {} more", head.join("`, `"), tail.len())
+ }
+ }
+}
+
+fn pattern_not_covered_label(witnesses: &[super::Pat<'_>], joined_patterns: &str) -> String {
+ format!("pattern{} {} not covered", rustc_errors::pluralize!(witnesses.len()), joined_patterns)
+}
+
+/// Point at the definition of non-covered `enum` variants.
+fn adt_defined_here(
+ cx: &MatchCheckCtxt<'_, '_>,
+ err: &mut DiagnosticBuilder<'_>,
+ ty: Ty<'_>,
+ witnesses: &[super::Pat<'_>],
+) {
+ let ty = ty.peel_refs();
+ if let ty::Adt(def, _) = ty.kind {
+ if let Some(sp) = cx.tcx.hir().span_if_local(def.did) {
+ err.span_label(sp, format!("`{}` defined here", ty));
+ }
+
+ if witnesses.len() < 4 {
+ for sp in maybe_point_at_variant(ty, &witnesses) {
+ err.span_label(sp, "not covered");
+ }
+ }
+ }
+}
+
+fn maybe_point_at_variant(ty: Ty<'_>, patterns: &[super::Pat<'_>]) -> Vec<Span> {
+ let mut covered = vec![];
+ if let ty::Adt(def, _) = ty.kind {
+ // Don't point at variants that have already been covered due to other patterns to avoid
+ // visual clutter.
+ for pattern in patterns {
+ use PatKind::{AscribeUserType, Deref, Leaf, Or, Variant};
+ match &*pattern.kind {
+ AscribeUserType { subpattern, .. } | Deref { subpattern } => {
+ covered.extend(maybe_point_at_variant(ty, slice::from_ref(&subpattern)));
+ }
+ Variant { adt_def, variant_index, subpatterns, .. } if adt_def.did == def.did => {
+ let sp = def.variants[*variant_index].ident.span;
+ if covered.contains(&sp) {
+ continue;
+ }
+ covered.push(sp);
+
+ let pats = subpatterns
+ .iter()
+ .map(|field_pattern| field_pattern.pattern.clone())
+ .collect::<Box<[_]>>();
+ covered.extend(maybe_point_at_variant(ty, &pats));
+ }
+ Leaf { subpatterns } => {
+ let pats = subpatterns
+ .iter()
+ .map(|field_pattern| field_pattern.pattern.clone())
+ .collect::<Box<[_]>>();
+ covered.extend(maybe_point_at_variant(ty, &pats));
+ }
+ Or { pats } => {
+ let pats = pats.iter().cloned().collect::<Box<[_]>>();
+ covered.extend(maybe_point_at_variant(ty, &pats));
+ }
+ _ => {}
+ }
+ }
+ }
+ covered
+}
+
+/// Check the legality of legality of by-move bindings.
+fn check_legality_of_move_bindings(cx: &mut MatchVisitor<'_, '_>, has_guard: bool, pat: &Pat<'_>) {
+ let sess = cx.tcx.sess;
+ let tables = cx.tables;
+
+ // Find all by-ref spans.
+ let mut by_ref_spans = Vec::new();
+ pat.each_binding(|_, hir_id, span, _| {
+ if let Some(ty::BindByReference(_)) = tables.extract_binding_mode(sess, hir_id, span) {
+ by_ref_spans.push(span);
+ }
+ });
+
+ // Find bad by-move spans:
+ let by_move_spans = &mut Vec::new();
+ let mut check_move = |p: &Pat<'_>, sub: Option<&Pat<'_>>| {
+ // Check legality of moving out of the enum.
+ //
+ // `x @ Foo(..)` is legal, but `x @ Foo(y)` isn't.
+ if sub.map_or(false, |p| p.contains_bindings()) {
+ struct_span_err!(sess, p.span, E0007, "cannot bind by-move with sub-bindings")
+ .span_label(p.span, "binds an already bound by-move value by moving it")
+ .emit();
+ } else if !has_guard && !by_ref_spans.is_empty() {
+ by_move_spans.push(p.span);
+ }
+ };
+ pat.walk_always(|p| {
+ if let hir::PatKind::Binding(.., sub) = &p.kind {
+ if let Some(ty::BindByValue(_)) = tables.extract_binding_mode(sess, p.hir_id, p.span) {
+ let pat_ty = tables.node_type(p.hir_id);
+ if !pat_ty.is_copy_modulo_regions(cx.tcx, cx.param_env, pat.span) {
+ check_move(p, sub.as_deref());
+ }
+ }
+ }
+ });
+
+ // Found some bad by-move spans, error!
+ if !by_move_spans.is_empty() {
+ let mut err = struct_span_err!(
+ sess,
+ MultiSpan::from_spans(by_move_spans.clone()),
+ E0009,
+ "cannot bind by-move and by-ref in the same pattern",
+ );
+ for span in by_ref_spans.iter() {
+ err.span_label(*span, "by-ref pattern here");
+ }
+ for span in by_move_spans.iter() {
+ err.span_label(*span, "by-move pattern here");
+ }
+ err.emit();
+ }
+}
+
+/// Check that there are no borrow conflicts in `binding @ subpat` patterns.
+///
+/// For example, this would reject:
+/// - `ref x @ Some(ref mut y)`,
+/// - `ref mut x @ Some(ref y)`
+/// - `ref mut x @ Some(ref mut y)`.
+///
+/// This analysis is *not* subsumed by NLL.
+fn check_borrow_conflicts_in_at_patterns(cx: &MatchVisitor<'_, '_>, pat: &Pat<'_>) {
+ let tab = cx.tables;
+ let sess = cx.tcx.sess;
+ // Get the mutability of `p` if it's by-ref.
+ let extract_binding_mut = |hir_id, span| match tab.extract_binding_mode(sess, hir_id, span)? {
+ ty::BindByValue(_) => None,
+ ty::BindByReference(m) => Some(m),
+ };
+ pat.walk_always(|pat| {
+ // Extract `sub` in `binding @ sub`.
+ let (name, sub) = match &pat.kind {
+ hir::PatKind::Binding(.., name, Some(sub)) => (*name, sub),
+ _ => return,
+ };
+
+ // Extract the mutability.
+ let mut_outer = match extract_binding_mut(pat.hir_id, pat.span) {
+ None => return,
+ Some(m) => m,
+ };
+
+ // We now have `ref $mut_outer binding @ sub` (semantically).
+ // Recurse into each binding in `sub` and find mutability conflicts.
+ let mut conflicts_mut_mut = Vec::new();
+ let mut conflicts_mut_ref = Vec::new();
+ sub.each_binding(|_, hir_id, span, _| {
+ if let Some(mut_inner) = extract_binding_mut(hir_id, span) {
+ match (mut_outer, mut_inner) {
+ (Mutability::Not, Mutability::Not) => {}
+ (Mutability::Mut, Mutability::Mut) => conflicts_mut_mut.push(span),
+ _ => conflicts_mut_ref.push(span),
+ }
+ }
+ });
+
+ // Report errors if any.
+ let binding_span = pat.span.with_hi(name.span.hi());
+ if !conflicts_mut_mut.is_empty() {
+ // Report mutability conflicts for e.g. `ref mut x @ Some(ref mut y)`.
+ let msg = &format!("cannot borrow `{}` as mutable more than once at a time", name);
+ let mut err = sess.struct_span_err(pat.span, msg);
+ err.span_label(binding_span, "first mutable borrow occurs here");
+ for sp in conflicts_mut_mut {
+ err.span_label(sp, "another mutable borrow occurs here");
+ }
+ for sp in conflicts_mut_ref {
+ err.span_label(sp, "also borrowed as immutable here");
+ }
+ err.emit();
+ } else if !conflicts_mut_ref.is_empty() {
+ // Report mutability conflicts for e.g. `ref x @ Some(ref mut y)` or the converse.
+ let (primary, also) = match mut_outer {
+ Mutability::Mut => ("mutable", "immutable"),
+ Mutability::Not => ("immutable", "mutable"),
+ };
+ let msg = &format!(
+ "cannot borrow `{}` as {} because it is also borrowed as {}",
+ name, also, primary,
+ );
+ let mut err = sess.struct_span_err(pat.span, msg);
+ err.span_label(binding_span, &format!("{} borrow occurs here", primary));
+ for sp in conflicts_mut_ref {
+ err.span_label(sp, &format!("{} borrow occurs here", also));
+ }
+ err.emit();
+ }
+ });
+}
+
+/// Forbids bindings in `@` patterns. This used to be is necessary for memory safety,
+/// because of the way rvalues were handled in the borrow check. (See issue #14587.)
+fn check_legality_of_bindings_in_at_patterns(cx: &MatchVisitor<'_, '_>, pat: &Pat<'_>) {
+ AtBindingPatternVisitor { cx, bindings_allowed: true }.visit_pat(pat);
+
+ struct AtBindingPatternVisitor<'a, 'b, 'tcx> {
+ cx: &'a MatchVisitor<'b, 'tcx>,
+ bindings_allowed: bool,
+ }
+
+ impl<'v> Visitor<'v> for AtBindingPatternVisitor<'_, '_, '_> {
+ type Map = Map<'v>;
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> {
+ NestedVisitorMap::None
+ }
+
+ fn visit_pat(&mut self, pat: &Pat<'_>) {
+ match pat.kind {
+ hir::PatKind::Binding(.., ref subpat) => {
+ if !self.bindings_allowed {
+ feature_err(
+ &self.cx.tcx.sess.parse_sess,
+ sym::bindings_after_at,
+ pat.span,
+ "pattern bindings after an `@` are unstable",
+ )
+ .emit();
+ }
+
+ if subpat.is_some() {
+ let bindings_were_allowed = self.bindings_allowed;
+ self.bindings_allowed = false;
+ intravisit::walk_pat(self, pat);
+ self.bindings_allowed = bindings_were_allowed;
+ }
+ }
+ _ => intravisit::walk_pat(self, pat),
+ }
+ }
+ }
+}
--- /dev/null
+use rustc::infer::InferCtxt;
+use rustc::lint;
+use rustc::mir::Field;
+use rustc::traits::predicate_for_trait_def;
+use rustc::traits::{self, ObligationCause, PredicateObligation};
+use rustc::ty::{self, Ty, TyCtxt};
+use rustc_hir as hir;
+
+use rustc_index::vec::Idx;
+
+use rustc_span::Span;
+
+use std::cell::Cell;
+
+use super::{FieldPat, Pat, PatCtxt, PatKind};
+
+impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
+ /// Converts an evaluated constant to a pattern (if possible).
+ /// This means aggregate values (like structs and enums) are converted
+ /// to a pattern that matches the value (as if you'd compared via structural equality).
+ pub(super) fn const_to_pat(
+ &self,
+ cv: &'tcx ty::Const<'tcx>,
+ id: hir::HirId,
+ span: Span,
+ ) -> Pat<'tcx> {
+ debug!("const_to_pat: cv={:#?} id={:?}", cv, id);
+ debug!("const_to_pat: cv.ty={:?} span={:?}", cv.ty, span);
+
+ self.tcx.infer_ctxt().enter(|infcx| {
+ let mut convert = ConstToPat::new(self, id, span, infcx);
+ convert.to_pat(cv)
+ })
+ }
+}
+
+struct ConstToPat<'a, 'tcx> {
+ id: hir::HirId,
+ span: Span,
+ param_env: ty::ParamEnv<'tcx>,
+
+ // This tracks if we signal some hard error for a given const value, so that
+ // we will not subsequently issue an irrelevant lint for the same const
+ // value.
+ saw_const_match_error: Cell<bool>,
+
+ // inference context used for checking `T: Structural` bounds.
+ infcx: InferCtxt<'a, 'tcx>,
+
+ include_lint_checks: bool,
+}
+
+impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
+ fn new(
+ pat_ctxt: &PatCtxt<'_, 'tcx>,
+ id: hir::HirId,
+ span: Span,
+ infcx: InferCtxt<'a, 'tcx>,
+ ) -> Self {
+ ConstToPat {
+ id,
+ span,
+ infcx,
+ param_env: pat_ctxt.param_env,
+ include_lint_checks: pat_ctxt.include_lint_checks,
+ saw_const_match_error: Cell::new(false),
+ }
+ }
+
+ fn tcx(&self) -> TyCtxt<'tcx> {
+ self.infcx.tcx
+ }
+
+ fn search_for_structural_match_violation(
+ &self,
+ ty: Ty<'tcx>,
+ ) -> Option<traits::NonStructuralMatchTy<'tcx>> {
+ traits::search_for_structural_match_violation(self.id, self.span, self.tcx(), ty)
+ }
+
+ fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool {
+ traits::type_marked_structural(self.id, self.span, &self.infcx, ty)
+ }
+
+ fn to_pat(&mut self, cv: &'tcx ty::Const<'tcx>) -> Pat<'tcx> {
+ // This method is just a wrapper handling a validity check; the heavy lifting is
+ // performed by the recursive `recur` method, which is not meant to be
+ // invoked except by this method.
+ //
+ // once indirect_structural_match is a full fledged error, this
+ // level of indirection can be eliminated
+
+ let inlined_const_as_pat = self.recur(cv);
+
+ if self.include_lint_checks && !self.saw_const_match_error.get() {
+ // If we were able to successfully convert the const to some pat,
+ // double-check that all types in the const implement `Structural`.
+
+ let structural = self.search_for_structural_match_violation(cv.ty);
+ debug!(
+ "search_for_structural_match_violation cv.ty: {:?} returned: {:?}",
+ cv.ty, structural
+ );
+ if let Some(non_sm_ty) = structural {
+ let adt_def = match non_sm_ty {
+ traits::NonStructuralMatchTy::Adt(adt_def) => adt_def,
+ traits::NonStructuralMatchTy::Param => {
+ bug!("use of constant whose type is a parameter inside a pattern")
+ }
+ };
+ let path = self.tcx().def_path_str(adt_def.did);
+ let msg = format!(
+ "to use a constant of type `{}` in a pattern, \
+ `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
+ path, path,
+ );
+
+ // double-check there even *is* a semantic `PartialEq` to dispatch to.
+ //
+ // (If there isn't, then we can safely issue a hard
+ // error, because that's never worked, due to compiler
+ // using `PartialEq::eq` in this scenario in the past.)
+ //
+ // Note: To fix rust-lang/rust#65466, one could lift this check
+ // *before* any structural-match checking, and unconditionally error
+ // if `PartialEq` is not implemented. However, that breaks stable
+ // code at the moment, because types like `for <'a> fn(&'a ())` do
+ // not *yet* implement `PartialEq`. So for now we leave this here.
+ let ty_is_partial_eq: bool = {
+ let partial_eq_trait_id = self.tcx().lang_items().eq_trait().unwrap();
+ let obligation: PredicateObligation<'_> = predicate_for_trait_def(
+ self.tcx(),
+ self.param_env,
+ ObligationCause::misc(self.span, self.id),
+ partial_eq_trait_id,
+ 0,
+ cv.ty,
+ &[],
+ );
+ // FIXME: should this call a `predicate_must_hold` variant instead?
+ self.infcx.predicate_may_hold(&obligation)
+ };
+
+ if !ty_is_partial_eq {
+ // span_fatal avoids ICE from resolution of non-existent method (rare case).
+ self.tcx().sess.span_fatal(self.span, &msg);
+ } else {
+ self.tcx().lint_hir(
+ lint::builtin::INDIRECT_STRUCTURAL_MATCH,
+ self.id,
+ self.span,
+ &msg,
+ );
+ }
+ }
+ }
+
+ inlined_const_as_pat
+ }
+
+ // Recursive helper for `to_pat`; invoke that (instead of calling this directly).
+ fn recur(&self, cv: &'tcx ty::Const<'tcx>) -> Pat<'tcx> {
+ let id = self.id;
+ let span = self.span;
+ let tcx = self.tcx();
+ let param_env = self.param_env;
+
+ let field_pats = |vals: &[&'tcx ty::Const<'tcx>]| {
+ vals.iter()
+ .enumerate()
+ .map(|(idx, val)| {
+ let field = Field::new(idx);
+ FieldPat { field, pattern: self.recur(val) }
+ })
+ .collect()
+ };
+
+ let kind = match cv.ty.kind {
+ ty::Float(_) => {
+ tcx.lint_hir(
+ ::rustc::lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
+ id,
+ span,
+ "floating-point types cannot be used in patterns",
+ );
+ PatKind::Constant { value: cv }
+ }
+ ty::Adt(adt_def, _) if adt_def.is_union() => {
+ // Matching on union fields is unsafe, we can't hide it in constants
+ self.saw_const_match_error.set(true);
+ tcx.sess.span_err(span, "cannot use unions in constant patterns");
+ PatKind::Wild
+ }
+ // keep old code until future-compat upgraded to errors.
+ ty::Adt(adt_def, _) if !self.type_marked_structural(cv.ty) => {
+ debug!("adt_def {:?} has !type_marked_structural for cv.ty: {:?}", adt_def, cv.ty);
+ let path = tcx.def_path_str(adt_def.did);
+ let msg = format!(
+ "to use a constant of type `{}` in a pattern, \
+ `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
+ path, path,
+ );
+ self.saw_const_match_error.set(true);
+ tcx.sess.span_err(span, &msg);
+ PatKind::Wild
+ }
+ // keep old code until future-compat upgraded to errors.
+ ty::Ref(_, adt_ty @ ty::TyS { kind: ty::Adt(_, _), .. }, _)
+ if !self.type_marked_structural(adt_ty) =>
+ {
+ let adt_def =
+ if let ty::Adt(adt_def, _) = adt_ty.kind { adt_def } else { unreachable!() };
+
+ debug!(
+ "adt_def {:?} has !type_marked_structural for adt_ty: {:?}",
+ adt_def, adt_ty
+ );
+
+ // HACK(estebank): Side-step ICE #53708, but anything other than erroring here
+ // would be wrong. Returnging `PatKind::Wild` is not technically correct.
+ let path = tcx.def_path_str(adt_def.did);
+ let msg = format!(
+ "to use a constant of type `{}` in a pattern, \
+ `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
+ path, path,
+ );
+ self.saw_const_match_error.set(true);
+ tcx.sess.span_err(span, &msg);
+ PatKind::Wild
+ }
+ ty::Adt(adt_def, substs) if adt_def.is_enum() => {
+ let destructured = tcx.destructure_const(param_env.and(cv));
+ PatKind::Variant {
+ adt_def,
+ substs,
+ variant_index: destructured.variant,
+ subpatterns: field_pats(destructured.fields),
+ }
+ }
+ ty::Adt(_, _) => {
+ let destructured = tcx.destructure_const(param_env.and(cv));
+ PatKind::Leaf { subpatterns: field_pats(destructured.fields) }
+ }
+ ty::Tuple(_) => {
+ let destructured = tcx.destructure_const(param_env.and(cv));
+ PatKind::Leaf { subpatterns: field_pats(destructured.fields) }
+ }
+ ty::Array(..) => PatKind::Array {
+ prefix: tcx
+ .destructure_const(param_env.and(cv))
+ .fields
+ .iter()
+ .map(|val| self.recur(val))
+ .collect(),
+ slice: None,
+ suffix: Vec::new(),
+ },
+ _ => PatKind::Constant { value: cv },
+ };
+
+ Pat { span, ty: cv.ty, kind: Box::new(kind) }
+ }
+}
--- /dev/null
+//! Validation of patterns/matches.
+
+mod _match;
+mod check_match;
+mod const_to_pat;
+
+pub(crate) use self::check_match::check_match;
+
+use crate::hair::constant::*;
+use crate::hair::util::UserAnnotatedTyHelpers;
+
+use rustc::mir::interpret::{get_slice_bytes, sign_extend, ConstValue, ErrorHandled};
+use rustc::mir::UserTypeProjection;
+use rustc::mir::{BorrowKind, Field, Mutability};
+use rustc::ty::layout::VariantIdx;
+use rustc::ty::subst::{GenericArg, SubstsRef};
+use rustc::ty::{self, AdtDef, DefIdTree, Region, Ty, TyCtxt, UserType};
+use rustc::ty::{CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations};
+use rustc_errors::struct_span_err;
+use rustc_hir as hir;
+use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
+use rustc_hir::pat_util::EnumerateAndAdjustIterator;
+use rustc_hir::RangeEnd;
+use rustc_index::vec::Idx;
+use rustc_span::{Span, DUMMY_SP};
+use syntax::ast;
+
+use std::cmp::Ordering;
+use std::fmt;
+
+use rustc_error_codes::*;
+
+#[derive(Clone, Debug)]
+crate enum PatternError {
+ AssocConstInPattern(Span),
+ StaticInPattern(Span),
+ FloatBug,
+ NonConstPath(Span),
+}
+
+#[derive(Copy, Clone, Debug)]
+crate enum BindingMode {
+ ByValue,
+ ByRef(BorrowKind),
+}
+
+#[derive(Clone, Debug)]
+crate struct FieldPat<'tcx> {
+ crate field: Field,
+ crate pattern: Pat<'tcx>,
+}
+
+#[derive(Clone, Debug)]
+crate struct Pat<'tcx> {
+ crate ty: Ty<'tcx>,
+ crate span: Span,
+ crate kind: Box<PatKind<'tcx>>,
+}
+
+impl<'tcx> Pat<'tcx> {
+ pub(crate) fn wildcard_from_ty(ty: Ty<'tcx>) -> Self {
+ Pat { ty, span: DUMMY_SP, kind: Box::new(PatKind::Wild) }
+ }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq)]
+crate struct PatTyProj<'tcx> {
+ crate user_ty: CanonicalUserType<'tcx>,
+}
+
+impl<'tcx> PatTyProj<'tcx> {
+ pub(crate) fn from_user_type(user_annotation: CanonicalUserType<'tcx>) -> Self {
+ Self { user_ty: user_annotation }
+ }
+
+ pub(crate) fn user_ty(
+ self,
+ annotations: &mut CanonicalUserTypeAnnotations<'tcx>,
+ inferred_ty: Ty<'tcx>,
+ span: Span,
+ ) -> UserTypeProjection {
+ UserTypeProjection {
+ base: annotations.push(CanonicalUserTypeAnnotation {
+ span,
+ user_ty: self.user_ty,
+ inferred_ty,
+ }),
+ projs: Vec::new(),
+ }
+ }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq)]
+crate struct Ascription<'tcx> {
+ crate user_ty: PatTyProj<'tcx>,
+ /// Variance to use when relating the type `user_ty` to the **type of the value being
+ /// matched**. Typically, this is `Variance::Covariant`, since the value being matched must
+ /// have a type that is some subtype of the ascribed type.
+ ///
+ /// Note that this variance does not apply for any bindings within subpatterns. The type
+ /// assigned to those bindings must be exactly equal to the `user_ty` given here.
+ ///
+ /// The only place where this field is not `Covariant` is when matching constants, where
+ /// we currently use `Contravariant` -- this is because the constant type just needs to
+ /// be "comparable" to the type of the input value. So, for example:
+ ///
+ /// ```text
+ /// match x { "foo" => .. }
+ /// ```
+ ///
+ /// requires that `&'static str <: T_x`, where `T_x` is the type of `x`. Really, we should
+ /// probably be checking for a `PartialEq` impl instead, but this preserves the behavior
+ /// of the old type-check for now. See #57280 for details.
+ crate variance: ty::Variance,
+ crate user_ty_span: Span,
+}
+
+#[derive(Clone, Debug)]
+crate enum PatKind<'tcx> {
+ Wild,
+
+ AscribeUserType {
+ ascription: Ascription<'tcx>,
+ subpattern: Pat<'tcx>,
+ },
+
+ /// `x`, `ref x`, `x @ P`, etc.
+ Binding {
+ mutability: Mutability,
+ name: ast::Name,
+ mode: BindingMode,
+ var: hir::HirId,
+ ty: Ty<'tcx>,
+ subpattern: Option<Pat<'tcx>>,
+ },
+
+ /// `Foo(...)` or `Foo{...}` or `Foo`, where `Foo` is a variant name from an ADT with
+ /// multiple variants.
+ Variant {
+ adt_def: &'tcx AdtDef,
+ substs: SubstsRef<'tcx>,
+ variant_index: VariantIdx,
+ subpatterns: Vec<FieldPat<'tcx>>,
+ },
+
+ /// `(...)`, `Foo(...)`, `Foo{...}`, or `Foo`, where `Foo` is a variant name from an ADT with
+ /// a single variant.
+ Leaf {
+ subpatterns: Vec<FieldPat<'tcx>>,
+ },
+
+ /// `box P`, `&P`, `&mut P`, etc.
+ Deref {
+ subpattern: Pat<'tcx>,
+ },
+
+ Constant {
+ value: &'tcx ty::Const<'tcx>,
+ },
+
+ Range(PatRange<'tcx>),
+
+ /// Matches against a slice, checking the length and extracting elements.
+ /// irrefutable when there is a slice pattern and both `prefix` and `suffix` are empty.
+ /// e.g., `&[ref xs @ ..]`.
+ Slice {
+ prefix: Vec<Pat<'tcx>>,
+ slice: Option<Pat<'tcx>>,
+ suffix: Vec<Pat<'tcx>>,
+ },
+
+ /// Fixed match against an array; irrefutable.
+ Array {
+ prefix: Vec<Pat<'tcx>>,
+ slice: Option<Pat<'tcx>>,
+ suffix: Vec<Pat<'tcx>>,
+ },
+
+ /// An or-pattern, e.g. `p | q`.
+ /// Invariant: `pats.len() >= 2`.
+ Or {
+ pats: Vec<Pat<'tcx>>,
+ },
+}
+
+#[derive(Copy, Clone, Debug, PartialEq)]
+crate struct PatRange<'tcx> {
+ crate lo: &'tcx ty::Const<'tcx>,
+ crate hi: &'tcx ty::Const<'tcx>,
+ crate end: RangeEnd,
+}
+
+impl<'tcx> fmt::Display for Pat<'tcx> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ // Printing lists is a chore.
+ let mut first = true;
+ let mut start_or_continue = |s| {
+ if first {
+ first = false;
+ ""
+ } else {
+ s
+ }
+ };
+ let mut start_or_comma = || start_or_continue(", ");
+
+ match *self.kind {
+ PatKind::Wild => write!(f, "_"),
+ PatKind::AscribeUserType { ref subpattern, .. } => write!(f, "{}: _", subpattern),
+ PatKind::Binding { mutability, name, mode, ref subpattern, .. } => {
+ let is_mut = match mode {
+ BindingMode::ByValue => mutability == Mutability::Mut,
+ BindingMode::ByRef(bk) => {
+ write!(f, "ref ")?;
+ match bk {
+ BorrowKind::Mut { .. } => true,
+ _ => false,
+ }
+ }
+ };
+ if is_mut {
+ write!(f, "mut ")?;
+ }
+ write!(f, "{}", name)?;
+ if let Some(ref subpattern) = *subpattern {
+ write!(f, " @ {}", subpattern)?;
+ }
+ Ok(())
+ }
+ PatKind::Variant { ref subpatterns, .. } | PatKind::Leaf { ref subpatterns } => {
+ let variant = match *self.kind {
+ PatKind::Variant { adt_def, variant_index, .. } => {
+ Some(&adt_def.variants[variant_index])
+ }
+ _ => {
+ if let ty::Adt(adt, _) = self.ty.kind {
+ if !adt.is_enum() {
+ Some(&adt.variants[VariantIdx::new(0)])
+ } else {
+ None
+ }
+ } else {
+ None
+ }
+ }
+ };
+
+ if let Some(variant) = variant {
+ write!(f, "{}", variant.ident)?;
+
+ // Only for Adt we can have `S {...}`,
+ // which we handle separately here.
+ if variant.ctor_kind == CtorKind::Fictive {
+ write!(f, " {{ ")?;
+
+ let mut printed = 0;
+ for p in subpatterns {
+ if let PatKind::Wild = *p.pattern.kind {
+ continue;
+ }
+ let name = variant.fields[p.field.index()].ident;
+ write!(f, "{}{}: {}", start_or_comma(), name, p.pattern)?;
+ printed += 1;
+ }
+
+ if printed < variant.fields.len() {
+ write!(f, "{}..", start_or_comma())?;
+ }
+
+ return write!(f, " }}");
+ }
+ }
+
+ let num_fields = variant.map_or(subpatterns.len(), |v| v.fields.len());
+ if num_fields != 0 || variant.is_none() {
+ write!(f, "(")?;
+ for i in 0..num_fields {
+ write!(f, "{}", start_or_comma())?;
+
+ // Common case: the field is where we expect it.
+ if let Some(p) = subpatterns.get(i) {
+ if p.field.index() == i {
+ write!(f, "{}", p.pattern)?;
+ continue;
+ }
+ }
+
+ // Otherwise, we have to go looking for it.
+ if let Some(p) = subpatterns.iter().find(|p| p.field.index() == i) {
+ write!(f, "{}", p.pattern)?;
+ } else {
+ write!(f, "_")?;
+ }
+ }
+ write!(f, ")")?;
+ }
+
+ Ok(())
+ }
+ PatKind::Deref { ref subpattern } => {
+ match self.ty.kind {
+ ty::Adt(def, _) if def.is_box() => write!(f, "box ")?,
+ ty::Ref(_, _, mutbl) => {
+ write!(f, "&{}", mutbl.prefix_str())?;
+ }
+ _ => bug!("{} is a bad Deref pattern type", self.ty),
+ }
+ write!(f, "{}", subpattern)
+ }
+ PatKind::Constant { value } => write!(f, "{}", value),
+ PatKind::Range(PatRange { lo, hi, end }) => {
+ write!(f, "{}", lo)?;
+ write!(f, "{}", end)?;
+ write!(f, "{}", hi)
+ }
+ PatKind::Slice { ref prefix, ref slice, ref suffix }
+ | PatKind::Array { ref prefix, ref slice, ref suffix } => {
+ write!(f, "[")?;
+ for p in prefix {
+ write!(f, "{}{}", start_or_comma(), p)?;
+ }
+ if let Some(ref slice) = *slice {
+ write!(f, "{}", start_or_comma())?;
+ match *slice.kind {
+ PatKind::Wild => {}
+ _ => write!(f, "{}", slice)?,
+ }
+ write!(f, "..")?;
+ }
+ for p in suffix {
+ write!(f, "{}{}", start_or_comma(), p)?;
+ }
+ write!(f, "]")
+ }
+ PatKind::Or { ref pats } => {
+ for pat in pats {
+ write!(f, "{}{}", start_or_continue(" | "), pat)?;
+ }
+ Ok(())
+ }
+ }
+ }
+}
+
+crate struct PatCtxt<'a, 'tcx> {
+ crate tcx: TyCtxt<'tcx>,
+ crate param_env: ty::ParamEnv<'tcx>,
+ crate tables: &'a ty::TypeckTables<'tcx>,
+ crate errors: Vec<PatternError>,
+ include_lint_checks: bool,
+}
+
+impl<'a, 'tcx> Pat<'tcx> {
+ crate fn from_hir(
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ tables: &'a ty::TypeckTables<'tcx>,
+ pat: &'tcx hir::Pat<'tcx>,
+ ) -> Self {
+ let mut pcx = PatCtxt::new(tcx, param_env, tables);
+ let result = pcx.lower_pattern(pat);
+ if !pcx.errors.is_empty() {
+ let msg = format!("encountered errors lowering pattern: {:?}", pcx.errors);
+ tcx.sess.delay_span_bug(pat.span, &msg);
+ }
+ debug!("Pat::from_hir({:?}) = {:?}", pat, result);
+ result
+ }
+}
+
+impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
+ crate fn new(
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ tables: &'a ty::TypeckTables<'tcx>,
+ ) -> Self {
+ PatCtxt { tcx, param_env, tables, errors: vec![], include_lint_checks: false }
+ }
+
+ crate fn include_lint_checks(&mut self) -> &mut Self {
+ self.include_lint_checks = true;
+ self
+ }
+
+ crate fn lower_pattern(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Pat<'tcx> {
+ // When implicit dereferences have been inserted in this pattern, the unadjusted lowered
+ // pattern has the type that results *after* dereferencing. For example, in this code:
+ //
+ // ```
+ // match &&Some(0i32) {
+ // Some(n) => { ... },
+ // _ => { ... },
+ // }
+ // ```
+ //
+ // the type assigned to `Some(n)` in `unadjusted_pat` would be `Option<i32>` (this is
+ // determined in rustc_typeck::check::match). The adjustments would be
+ //
+ // `vec![&&Option<i32>, &Option<i32>]`.
+ //
+ // Applying the adjustments, we want to instead output `&&Some(n)` (as a HAIR pattern). So
+ // we wrap the unadjusted pattern in `PatKind::Deref` repeatedly, consuming the
+ // adjustments in *reverse order* (last-in-first-out, so that the last `Deref` inserted
+ // gets the least-dereferenced type).
+ let unadjusted_pat = self.lower_pattern_unadjusted(pat);
+ self.tables.pat_adjustments().get(pat.hir_id).unwrap_or(&vec![]).iter().rev().fold(
+ unadjusted_pat,
+ |pat, ref_ty| {
+ debug!("{:?}: wrapping pattern with type {:?}", pat, ref_ty);
+ Pat {
+ span: pat.span,
+ ty: ref_ty,
+ kind: Box::new(PatKind::Deref { subpattern: pat }),
+ }
+ },
+ )
+ }
+
+ fn lower_range_expr(
+ &mut self,
+ expr: &'tcx hir::Expr<'tcx>,
+ ) -> (PatKind<'tcx>, Option<Ascription<'tcx>>) {
+ match self.lower_lit(expr) {
+ PatKind::AscribeUserType { ascription, subpattern: Pat { kind: box kind, .. } } => {
+ (kind, Some(ascription))
+ }
+ kind => (kind, None),
+ }
+ }
+
+ fn lower_pattern_range(
+ &mut self,
+ ty: Ty<'tcx>,
+ lo: &'tcx ty::Const<'tcx>,
+ hi: &'tcx ty::Const<'tcx>,
+ end: RangeEnd,
+ span: Span,
+ ) -> PatKind<'tcx> {
+ assert_eq!(lo.ty, ty);
+ assert_eq!(hi.ty, ty);
+ let cmp = compare_const_vals(self.tcx, lo, hi, self.param_env, ty);
+ match (end, cmp) {
+ // `x..y` where `x < y`.
+ // Non-empty because the range includes at least `x`.
+ (RangeEnd::Excluded, Some(Ordering::Less)) => PatKind::Range(PatRange { lo, hi, end }),
+ // `x..y` where `x >= y`. The range is empty => error.
+ (RangeEnd::Excluded, _) => {
+ struct_span_err!(
+ self.tcx.sess,
+ span,
+ E0579,
+ "lower range bound must be less than upper"
+ )
+ .emit();
+ PatKind::Wild
+ }
+ // `x..=y` where `x == y`.
+ (RangeEnd::Included, Some(Ordering::Equal)) => PatKind::Constant { value: lo },
+ // `x..=y` where `x < y`.
+ (RangeEnd::Included, Some(Ordering::Less)) => PatKind::Range(PatRange { lo, hi, end }),
+ // `x..=y` where `x > y` hence the range is empty => error.
+ (RangeEnd::Included, _) => {
+ let mut err = struct_span_err!(
+ self.tcx.sess,
+ span,
+ E0030,
+ "lower range bound must be less than or equal to upper"
+ );
+ err.span_label(span, "lower bound larger than upper bound");
+ if self.tcx.sess.teach(&err.get_code().unwrap()) {
+ err.note(
+ "When matching against a range, the compiler \
+ verifies that the range is non-empty. Range \
+ patterns include both end-points, so this is \
+ equivalent to requiring the start of the range \
+ to be less than or equal to the end of the range.",
+ );
+ }
+ err.emit();
+ PatKind::Wild
+ }
+ }
+ }
+
+ fn normalize_range_pattern_ends(
+ &self,
+ ty: Ty<'tcx>,
+ lo: Option<&PatKind<'tcx>>,
+ hi: Option<&PatKind<'tcx>>,
+ ) -> Option<(&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>)> {
+ match (lo, hi) {
+ (Some(PatKind::Constant { value: lo }), Some(PatKind::Constant { value: hi })) => {
+ Some((lo, hi))
+ }
+ (Some(PatKind::Constant { value: lo }), None) => {
+ Some((lo, ty.numeric_max_val(self.tcx)?))
+ }
+ (None, Some(PatKind::Constant { value: hi })) => {
+ Some((ty.numeric_min_val(self.tcx)?, hi))
+ }
+ _ => None,
+ }
+ }
+
+ fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Pat<'tcx> {
+ let mut ty = self.tables.node_type(pat.hir_id);
+
+ if let ty::Error = ty.kind {
+ // Avoid ICEs (e.g., #50577 and #50585).
+ return Pat { span: pat.span, ty, kind: Box::new(PatKind::Wild) };
+ }
+
+ let kind = match pat.kind {
+ hir::PatKind::Wild => PatKind::Wild,
+
+ hir::PatKind::Lit(ref value) => self.lower_lit(value),
+
+ hir::PatKind::Range(ref lo_expr, ref hi_expr, end) => {
+ let (lo_expr, hi_expr) = (lo_expr.as_deref(), hi_expr.as_deref());
+ let lo_span = lo_expr.map_or(pat.span, |e| e.span);
+ let lo = lo_expr.map(|e| self.lower_range_expr(e));
+ let hi = hi_expr.map(|e| self.lower_range_expr(e));
+
+ let (lp, hp) = (lo.as_ref().map(|x| &x.0), hi.as_ref().map(|x| &x.0));
+ let mut kind = match self.normalize_range_pattern_ends(ty, lp, hp) {
+ Some((lc, hc)) => self.lower_pattern_range(ty, lc, hc, end, lo_span),
+ None => {
+ let msg = &format!(
+ "found bad range pattern `{:?}` outside of error recovery",
+ (&lo, &hi),
+ );
+ self.tcx.sess.delay_span_bug(pat.span, msg);
+ PatKind::Wild
+ }
+ };
+
+ // If we are handling a range with associated constants (e.g.
+ // `Foo::<'a>::A..=Foo::B`), we need to put the ascriptions for the associated
+ // constants somewhere. Have them on the range pattern.
+ for end in &[lo, hi] {
+ if let Some((_, Some(ascription))) = end {
+ let subpattern = Pat { span: pat.span, ty, kind: Box::new(kind) };
+ kind = PatKind::AscribeUserType { ascription: *ascription, subpattern };
+ }
+ }
+
+ kind
+ }
+
+ hir::PatKind::Path(ref qpath) => {
+ return self.lower_path(qpath, pat.hir_id, pat.span);
+ }
+
+ hir::PatKind::Ref(ref subpattern, _) | hir::PatKind::Box(ref subpattern) => {
+ PatKind::Deref { subpattern: self.lower_pattern(subpattern) }
+ }
+
+ hir::PatKind::Slice(ref prefix, ref slice, ref suffix) => {
+ self.slice_or_array_pattern(pat.span, ty, prefix, slice, suffix)
+ }
+
+ hir::PatKind::Tuple(ref pats, ddpos) => {
+ let tys = match ty.kind {
+ ty::Tuple(ref tys) => tys,
+ _ => span_bug!(pat.span, "unexpected type for tuple pattern: {:?}", ty),
+ };
+ let subpatterns = self.lower_tuple_subpats(pats, tys.len(), ddpos);
+ PatKind::Leaf { subpatterns }
+ }
+
+ hir::PatKind::Binding(_, id, ident, ref sub) => {
+ let bm =
+ *self.tables.pat_binding_modes().get(pat.hir_id).expect("missing binding mode");
+ let (mutability, mode) = match bm {
+ ty::BindByValue(mutbl) => (mutbl, BindingMode::ByValue),
+ ty::BindByReference(hir::Mutability::Mut) => (
+ Mutability::Not,
+ BindingMode::ByRef(BorrowKind::Mut { allow_two_phase_borrow: false }),
+ ),
+ ty::BindByReference(hir::Mutability::Not) => {
+ (Mutability::Not, BindingMode::ByRef(BorrowKind::Shared))
+ }
+ };
+
+ // A ref x pattern is the same node used for x, and as such it has
+ // x's type, which is &T, where we want T (the type being matched).
+ let var_ty = ty;
+ if let ty::BindByReference(_) = bm {
+ if let ty::Ref(_, rty, _) = ty.kind {
+ ty = rty;
+ } else {
+ bug!("`ref {}` has wrong type {}", ident, ty);
+ }
+ };
+
+ PatKind::Binding {
+ mutability,
+ mode,
+ name: ident.name,
+ var: id,
+ ty: var_ty,
+ subpattern: self.lower_opt_pattern(sub),
+ }
+ }
+
+ hir::PatKind::TupleStruct(ref qpath, ref pats, ddpos) => {
+ let res = self.tables.qpath_res(qpath, pat.hir_id);
+ let adt_def = match ty.kind {
+ ty::Adt(adt_def, _) => adt_def,
+ _ => span_bug!(pat.span, "tuple struct pattern not applied to an ADT {:?}", ty),
+ };
+ let variant_def = adt_def.variant_of_res(res);
+ let subpatterns = self.lower_tuple_subpats(pats, variant_def.fields.len(), ddpos);
+ self.lower_variant_or_leaf(res, pat.hir_id, pat.span, ty, subpatterns)
+ }
+
+ hir::PatKind::Struct(ref qpath, ref fields, _) => {
+ let res = self.tables.qpath_res(qpath, pat.hir_id);
+ let subpatterns = fields
+ .iter()
+ .map(|field| FieldPat {
+ field: Field::new(self.tcx.field_index(field.hir_id, self.tables)),
+ pattern: self.lower_pattern(&field.pat),
+ })
+ .collect();
+
+ self.lower_variant_or_leaf(res, pat.hir_id, pat.span, ty, subpatterns)
+ }
+
+ hir::PatKind::Or(ref pats) => PatKind::Or { pats: self.lower_patterns(pats) },
+ };
+
+ Pat { span: pat.span, ty, kind: Box::new(kind) }
+ }
+
+ fn lower_tuple_subpats(
+ &mut self,
+ pats: &'tcx [&'tcx hir::Pat<'tcx>],
+ expected_len: usize,
+ gap_pos: Option<usize>,
+ ) -> Vec<FieldPat<'tcx>> {
+ pats.iter()
+ .enumerate_and_adjust(expected_len, gap_pos)
+ .map(|(i, subpattern)| FieldPat {
+ field: Field::new(i),
+ pattern: self.lower_pattern(subpattern),
+ })
+ .collect()
+ }
+
+ fn lower_patterns(&mut self, pats: &'tcx [&'tcx hir::Pat<'tcx>]) -> Vec<Pat<'tcx>> {
+ pats.iter().map(|p| self.lower_pattern(p)).collect()
+ }
+
+ fn lower_opt_pattern(&mut self, pat: &'tcx Option<&'tcx hir::Pat<'tcx>>) -> Option<Pat<'tcx>> {
+ pat.as_ref().map(|p| self.lower_pattern(p))
+ }
+
+ fn slice_or_array_pattern(
+ &mut self,
+ span: Span,
+ ty: Ty<'tcx>,
+ prefix: &'tcx [&'tcx hir::Pat<'tcx>],
+ slice: &'tcx Option<&'tcx hir::Pat<'tcx>>,
+ suffix: &'tcx [&'tcx hir::Pat<'tcx>],
+ ) -> PatKind<'tcx> {
+ let prefix = self.lower_patterns(prefix);
+ let slice = self.lower_opt_pattern(slice);
+ let suffix = self.lower_patterns(suffix);
+ match ty.kind {
+ // Matching a slice, `[T]`.
+ ty::Slice(..) => PatKind::Slice { prefix, slice, suffix },
+ // Fixed-length array, `[T; len]`.
+ ty::Array(_, len) => {
+ let len = len.eval_usize(self.tcx, self.param_env);
+ assert!(len >= prefix.len() as u64 + suffix.len() as u64);
+ PatKind::Array { prefix, slice, suffix }
+ }
+ _ => span_bug!(span, "bad slice pattern type {:?}", ty),
+ }
+ }
+
+ fn lower_variant_or_leaf(
+ &mut self,
+ res: Res,
+ hir_id: hir::HirId,
+ span: Span,
+ ty: Ty<'tcx>,
+ subpatterns: Vec<FieldPat<'tcx>>,
+ ) -> PatKind<'tcx> {
+ let res = match res {
+ Res::Def(DefKind::Ctor(CtorOf::Variant, ..), variant_ctor_id) => {
+ let variant_id = self.tcx.parent(variant_ctor_id).unwrap();
+ Res::Def(DefKind::Variant, variant_id)
+ }
+ res => res,
+ };
+
+ let mut kind = match res {
+ Res::Def(DefKind::Variant, variant_id) => {
+ let enum_id = self.tcx.parent(variant_id).unwrap();
+ let adt_def = self.tcx.adt_def(enum_id);
+ if adt_def.is_enum() {
+ let substs = match ty.kind {
+ ty::Adt(_, substs) | ty::FnDef(_, substs) => substs,
+ ty::Error => {
+ // Avoid ICE (#50585)
+ return PatKind::Wild;
+ }
+ _ => bug!("inappropriate type for def: {:?}", ty),
+ };
+ PatKind::Variant {
+ adt_def,
+ substs,
+ variant_index: adt_def.variant_index_with_id(variant_id),
+ subpatterns,
+ }
+ } else {
+ PatKind::Leaf { subpatterns }
+ }
+ }
+
+ Res::Def(DefKind::Struct, _)
+ | Res::Def(DefKind::Ctor(CtorOf::Struct, ..), _)
+ | Res::Def(DefKind::Union, _)
+ | Res::Def(DefKind::TyAlias, _)
+ | Res::Def(DefKind::AssocTy, _)
+ | Res::SelfTy(..)
+ | Res::SelfCtor(..) => PatKind::Leaf { subpatterns },
+
+ _ => {
+ self.errors.push(PatternError::NonConstPath(span));
+ PatKind::Wild
+ }
+ };
+
+ if let Some(user_ty) = self.user_substs_applied_to_ty_of_hir_id(hir_id) {
+ debug!("lower_variant_or_leaf: kind={:?} user_ty={:?} span={:?}", kind, user_ty, span);
+ kind = PatKind::AscribeUserType {
+ subpattern: Pat { span, ty, kind: Box::new(kind) },
+ ascription: Ascription {
+ user_ty: PatTyProj::from_user_type(user_ty),
+ user_ty_span: span,
+ variance: ty::Variance::Covariant,
+ },
+ };
+ }
+
+ kind
+ }
+
+ /// Takes a HIR Path. If the path is a constant, evaluates it and feeds
+ /// it to `const_to_pat`. Any other path (like enum variants without fields)
+ /// is converted to the corresponding pattern via `lower_variant_or_leaf`.
+ fn lower_path(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, span: Span) -> Pat<'tcx> {
+ let ty = self.tables.node_type(id);
+ let res = self.tables.qpath_res(qpath, id);
+ let is_associated_const = match res {
+ Res::Def(DefKind::AssocConst, _) => true,
+ _ => false,
+ };
+ let kind = match res {
+ Res::Def(DefKind::Const, def_id) | Res::Def(DefKind::AssocConst, def_id) => {
+ let substs = self.tables.node_substs(id);
+ // Use `Reveal::All` here because patterns are always monomorphic even if their function isn't.
+ match self.tcx.const_eval_resolve(
+ self.param_env.with_reveal_all(),
+ def_id,
+ substs,
+ None,
+ Some(span),
+ ) {
+ Ok(value) => {
+ let pattern = self.const_to_pat(value, id, span);
+ if !is_associated_const {
+ return pattern;
+ }
+
+ let user_provided_types = self.tables().user_provided_types();
+ return if let Some(u_ty) = user_provided_types.get(id) {
+ let user_ty = PatTyProj::from_user_type(*u_ty);
+ Pat {
+ span,
+ kind: Box::new(PatKind::AscribeUserType {
+ subpattern: pattern,
+ ascription: Ascription {
+ /// Note that use `Contravariant` here. See the
+ /// `variance` field documentation for details.
+ variance: ty::Variance::Contravariant,
+ user_ty,
+ user_ty_span: span,
+ },
+ }),
+ ty: value.ty,
+ }
+ } else {
+ pattern
+ };
+ }
+ Err(ErrorHandled::TooGeneric) => {
+ self.errors.push(if is_associated_const {
+ PatternError::AssocConstInPattern(span)
+ } else {
+ PatternError::StaticInPattern(span)
+ });
+ PatKind::Wild
+ }
+ Err(_) => {
+ self.tcx.sess.span_err(span, "could not evaluate constant pattern");
+ PatKind::Wild
+ }
+ }
+ }
+ _ => self.lower_variant_or_leaf(res, id, span, ty, vec![]),
+ };
+
+ Pat { span, ty, kind: Box::new(kind) }
+ }
+
+ /// Converts literals, paths and negation of literals to patterns.
+ /// The special case for negation exists to allow things like `-128_i8`
+ /// which would overflow if we tried to evaluate `128_i8` and then negate
+ /// afterwards.
+ fn lower_lit(&mut self, expr: &'tcx hir::Expr<'tcx>) -> PatKind<'tcx> {
+ match expr.kind {
+ hir::ExprKind::Lit(ref lit) => {
+ let ty = self.tables.expr_ty(expr);
+ match lit_to_const(&lit.node, self.tcx, ty, false) {
+ Ok(val) => *self.const_to_pat(val, expr.hir_id, lit.span).kind,
+ Err(LitToConstError::UnparseableFloat) => {
+ self.errors.push(PatternError::FloatBug);
+ PatKind::Wild
+ }
+ Err(LitToConstError::Reported) => PatKind::Wild,
+ }
+ }
+ hir::ExprKind::Path(ref qpath) => *self.lower_path(qpath, expr.hir_id, expr.span).kind,
+ hir::ExprKind::Unary(hir::UnOp::UnNeg, ref expr) => {
+ let ty = self.tables.expr_ty(expr);
+ let lit = match expr.kind {
+ hir::ExprKind::Lit(ref lit) => lit,
+ _ => span_bug!(expr.span, "not a literal: {:?}", expr),
+ };
+ match lit_to_const(&lit.node, self.tcx, ty, true) {
+ Ok(val) => *self.const_to_pat(val, expr.hir_id, lit.span).kind,
+ Err(LitToConstError::UnparseableFloat) => {
+ self.errors.push(PatternError::FloatBug);
+ PatKind::Wild
+ }
+ Err(LitToConstError::Reported) => PatKind::Wild,
+ }
+ }
+ _ => span_bug!(expr.span, "not a literal: {:?}", expr),
+ }
+ }
+}
+
+impl<'tcx> UserAnnotatedTyHelpers<'tcx> for PatCtxt<'_, 'tcx> {
+ fn tcx(&self) -> TyCtxt<'tcx> {
+ self.tcx
+ }
+
+ fn tables(&self) -> &ty::TypeckTables<'tcx> {
+ self.tables
+ }
+}
+
+crate trait PatternFoldable<'tcx>: Sized {
+ fn fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self {
+ self.super_fold_with(folder)
+ }
+
+ fn super_fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self;
+}
+
+crate trait PatternFolder<'tcx>: Sized {
+ fn fold_pattern(&mut self, pattern: &Pat<'tcx>) -> Pat<'tcx> {
+ pattern.super_fold_with(self)
+ }
+
+ fn fold_pattern_kind(&mut self, kind: &PatKind<'tcx>) -> PatKind<'tcx> {
+ kind.super_fold_with(self)
+ }
+}
+
+impl<'tcx, T: PatternFoldable<'tcx>> PatternFoldable<'tcx> for Box<T> {
+ fn super_fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self {
+ let content: T = (**self).fold_with(folder);
+ box content
+ }
+}
+
+impl<'tcx, T: PatternFoldable<'tcx>> PatternFoldable<'tcx> for Vec<T> {
+ fn super_fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self {
+ self.iter().map(|t| t.fold_with(folder)).collect()
+ }
+}
+
+impl<'tcx, T: PatternFoldable<'tcx>> PatternFoldable<'tcx> for Option<T> {
+ fn super_fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self {
+ self.as_ref().map(|t| t.fold_with(folder))
+ }
+}
+
+macro_rules! CloneImpls {
+ (<$lt_tcx:tt> $($ty:ty),+) => {
+ $(
+ impl<$lt_tcx> PatternFoldable<$lt_tcx> for $ty {
+ fn super_fold_with<F: PatternFolder<$lt_tcx>>(&self, _: &mut F) -> Self {
+ Clone::clone(self)
+ }
+ }
+ )+
+ }
+}
+
+CloneImpls! { <'tcx>
+ Span, Field, Mutability, ast::Name, hir::HirId, usize, ty::Const<'tcx>,
+ Region<'tcx>, Ty<'tcx>, BindingMode, &'tcx AdtDef,
+ SubstsRef<'tcx>, &'tcx GenericArg<'tcx>, UserType<'tcx>,
+ UserTypeProjection, PatTyProj<'tcx>
+}
+
+impl<'tcx> PatternFoldable<'tcx> for FieldPat<'tcx> {
+ fn super_fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self {
+ FieldPat { field: self.field.fold_with(folder), pattern: self.pattern.fold_with(folder) }
+ }
+}
+
+impl<'tcx> PatternFoldable<'tcx> for Pat<'tcx> {
+ fn fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self {
+ folder.fold_pattern(self)
+ }
+
+ fn super_fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self {
+ Pat {
+ ty: self.ty.fold_with(folder),
+ span: self.span.fold_with(folder),
+ kind: self.kind.fold_with(folder),
+ }
+ }
+}
+
+impl<'tcx> PatternFoldable<'tcx> for PatKind<'tcx> {
+ fn fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self {
+ folder.fold_pattern_kind(self)
+ }
+
+ fn super_fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self {
+ match *self {
+ PatKind::Wild => PatKind::Wild,
+ PatKind::AscribeUserType {
+ ref subpattern,
+ ascription: Ascription { variance, ref user_ty, user_ty_span },
+ } => PatKind::AscribeUserType {
+ subpattern: subpattern.fold_with(folder),
+ ascription: Ascription {
+ user_ty: user_ty.fold_with(folder),
+ variance,
+ user_ty_span,
+ },
+ },
+ PatKind::Binding { mutability, name, mode, var, ty, ref subpattern } => {
+ PatKind::Binding {
+ mutability: mutability.fold_with(folder),
+ name: name.fold_with(folder),
+ mode: mode.fold_with(folder),
+ var: var.fold_with(folder),
+ ty: ty.fold_with(folder),
+ subpattern: subpattern.fold_with(folder),
+ }
+ }
+ PatKind::Variant { adt_def, substs, variant_index, ref subpatterns } => {
+ PatKind::Variant {
+ adt_def: adt_def.fold_with(folder),
+ substs: substs.fold_with(folder),
+ variant_index,
+ subpatterns: subpatterns.fold_with(folder),
+ }
+ }
+ PatKind::Leaf { ref subpatterns } => {
+ PatKind::Leaf { subpatterns: subpatterns.fold_with(folder) }
+ }
+ PatKind::Deref { ref subpattern } => {
+ PatKind::Deref { subpattern: subpattern.fold_with(folder) }
+ }
+ PatKind::Constant { value } => PatKind::Constant { value },
+ PatKind::Range(range) => PatKind::Range(range),
+ PatKind::Slice { ref prefix, ref slice, ref suffix } => PatKind::Slice {
+ prefix: prefix.fold_with(folder),
+ slice: slice.fold_with(folder),
+ suffix: suffix.fold_with(folder),
+ },
+ PatKind::Array { ref prefix, ref slice, ref suffix } => PatKind::Array {
+ prefix: prefix.fold_with(folder),
+ slice: slice.fold_with(folder),
+ suffix: suffix.fold_with(folder),
+ },
+ PatKind::Or { ref pats } => PatKind::Or { pats: pats.fold_with(folder) },
+ }
+ }
+}
+
+crate fn compare_const_vals<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ a: &'tcx ty::Const<'tcx>,
+ b: &'tcx ty::Const<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ ty: Ty<'tcx>,
+) -> Option<Ordering> {
+ trace!("compare_const_vals: {:?}, {:?}", a, b);
+
+ let from_bool = |v: bool| v.then_some(Ordering::Equal);
+
+ let fallback = || from_bool(a == b);
+
+ // Use the fallback if any type differs
+ if a.ty != b.ty || a.ty != ty {
+ return fallback();
+ }
+
+ // Early return for equal constants (so e.g. references to ZSTs can be compared, even if they
+ // are just integer addresses).
+ if a.val == b.val {
+ return from_bool(true);
+ }
+
+ let a_bits = a.try_eval_bits(tcx, param_env, ty);
+ let b_bits = b.try_eval_bits(tcx, param_env, ty);
+
+ if let (Some(a), Some(b)) = (a_bits, b_bits) {
+ use ::rustc_apfloat::Float;
+ return match ty.kind {
+ ty::Float(ast::FloatTy::F32) => {
+ let l = ::rustc_apfloat::ieee::Single::from_bits(a);
+ let r = ::rustc_apfloat::ieee::Single::from_bits(b);
+ l.partial_cmp(&r)
+ }
+ ty::Float(ast::FloatTy::F64) => {
+ let l = ::rustc_apfloat::ieee::Double::from_bits(a);
+ let r = ::rustc_apfloat::ieee::Double::from_bits(b);
+ l.partial_cmp(&r)
+ }
+ ty::Int(ity) => {
+ use rustc::ty::layout::{Integer, IntegerExt};
+ use syntax::attr::SignedInt;
+ let size = Integer::from_attr(&tcx, SignedInt(ity)).size();
+ let a = sign_extend(a, size);
+ let b = sign_extend(b, size);
+ Some((a as i128).cmp(&(b as i128)))
+ }
+ _ => Some(a.cmp(&b)),
+ };
+ }
+
+ if let ty::Str = ty.kind {
+ match (a.val, b.val) {
+ (
+ ty::ConstKind::Value(a_val @ ConstValue::Slice { .. }),
+ ty::ConstKind::Value(b_val @ ConstValue::Slice { .. }),
+ ) => {
+ let a_bytes = get_slice_bytes(&tcx, a_val);
+ let b_bytes = get_slice_bytes(&tcx, b_val);
+ return from_bool(a_bytes == b_bytes);
+ }
+ _ => (),
+ }
+ }
+
+ fallback()
+}
--- /dev/null
+use rustc::ty::{self, CanonicalUserType, TyCtxt, UserType};
+use rustc_hir as hir;
+
+crate trait UserAnnotatedTyHelpers<'tcx> {
+ fn tcx(&self) -> TyCtxt<'tcx>;
+
+ fn tables(&self) -> &ty::TypeckTables<'tcx>;
+
+ /// Looks up the type associated with this hir-id and applies the
+ /// user-given substitutions; the hir-id must map to a suitable
+ /// type.
+ fn user_substs_applied_to_ty_of_hir_id(
+ &self,
+ hir_id: hir::HirId,
+ ) -> Option<CanonicalUserType<'tcx>> {
+ let user_provided_types = self.tables().user_provided_types();
+ let mut user_ty = *user_provided_types.get(hir_id)?;
+ debug!("user_subts_applied_to_ty_of_hir_id: user_ty={:?}", user_ty);
+ let ty = self.tables().node_type(hir_id);
+ match ty.kind {
+ ty::Adt(adt_def, ..) => {
+ if let UserType::TypeOf(ref mut did, _) = &mut user_ty.value {
+ *did = adt_def.did;
+ }
+ Some(user_ty)
+ }
+ ty::FnDef(..) => Some(user_ty),
+ _ => bug!("ty: {:?} should not have user provided type {:?} recorded ", ty, user_ty),
+ }
+ }
+}
--- /dev/null
+//! Construction of MIR from HIR.
+//!
+//! This crate also contains the match exhaustiveness and usefulness checking.
+
+#![feature(box_patterns)]
+#![feature(box_syntax)]
+#![feature(crate_visibility_modifier)]
+#![feature(slice_patterns)]
+#![feature(bool_to_option)]
+#![recursion_limit = "256"]
+
+#[macro_use]
+extern crate log;
+#[macro_use]
+extern crate rustc;
+
+mod build;
+mod hair;
+mod lints;
+
+use rustc::ty::query::Providers;
+
+pub fn provide(providers: &mut Providers<'_>) {
+ providers.check_match = hair::pattern::check_match;
+ providers.mir_built = build::mir_built;
+}
--- /dev/null
+use rustc::hir::map::blocks::FnLikeNode;
+use rustc::lint::builtin::UNCONDITIONAL_RECURSION;
+use rustc::mir::{self, Body, TerminatorKind};
+use rustc::ty::subst::InternalSubsts;
+use rustc::ty::{self, AssocItem, AssocItemContainer, Instance, TyCtxt};
+use rustc_hir::def_id::DefId;
+use rustc_hir::intravisit::FnKind;
+use rustc_index::bit_set::BitSet;
+
+crate fn check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId) {
+ let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
+
+ if let Some(fn_like_node) = FnLikeNode::from_node(tcx.hir().get(hir_id)) {
+ check_fn_for_unconditional_recursion(tcx, fn_like_node.kind(), body, def_id);
+ }
+}
+
+fn check_fn_for_unconditional_recursion<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ fn_kind: FnKind<'_>,
+ body: &Body<'tcx>,
+ def_id: DefId,
+) {
+ if let FnKind::Closure(_) = fn_kind {
+ // closures can't recur, so they don't matter.
+ return;
+ }
+
+ //FIXME(#54444) rewrite this lint to use the dataflow framework
+
+ // Walk through this function (say `f`) looking to see if
+ // every possible path references itself, i.e., the function is
+ // called recursively unconditionally. This is done by trying
+ // to find a path from the entry node to the exit node that
+ // *doesn't* call `f` by traversing from the entry while
+ // pretending that calls of `f` are sinks (i.e., ignoring any
+ // exit edges from them).
+ //
+ // NB. this has an edge case with non-returning statements,
+ // like `loop {}` or `panic!()`: control flow never reaches
+ // the exit node through these, so one can have a function
+ // that never actually calls itself but is still picked up by
+ // this lint:
+ //
+ // fn f(cond: bool) {
+ // if !cond { panic!() } // could come from `assert!(cond)`
+ // f(false)
+ // }
+ //
+ // In general, functions of that form may be able to call
+ // itself a finite number of times and then diverge. The lint
+ // considers this to be an error for two reasons, (a) it is
+ // easier to implement, and (b) it seems rare to actually want
+ // to have behaviour like the above, rather than
+ // e.g., accidentally recursing after an assert.
+
+ let basic_blocks = body.basic_blocks();
+ let mut reachable_without_self_call_queue = vec![mir::START_BLOCK];
+ let mut reached_exit_without_self_call = false;
+ let mut self_call_locations = vec![];
+ let mut visited = BitSet::new_empty(basic_blocks.len());
+
+ let param_env = tcx.param_env(def_id);
+ let trait_substs_count = match tcx.opt_associated_item(def_id) {
+ Some(AssocItem { container: AssocItemContainer::TraitContainer(trait_def_id), .. }) => {
+ tcx.generics_of(trait_def_id).count()
+ }
+ _ => 0,
+ };
+ let caller_substs = &InternalSubsts::identity_for_item(tcx, def_id)[..trait_substs_count];
+
+ while let Some(bb) = reachable_without_self_call_queue.pop() {
+ if !visited.insert(bb) {
+ //already done
+ continue;
+ }
+
+ let block = &basic_blocks[bb];
+
+ if let Some(ref terminator) = block.terminator {
+ match terminator.kind {
+ TerminatorKind::Call { ref func, .. } => {
+ let func_ty = func.ty(body, tcx);
+
+ if let ty::FnDef(fn_def_id, substs) = func_ty.kind {
+ let (call_fn_id, call_substs) = if let Some(instance) =
+ Instance::resolve(tcx, param_env, fn_def_id, substs)
+ {
+ (instance.def_id(), instance.substs)
+ } else {
+ (fn_def_id, substs)
+ };
+
+ let is_self_call = call_fn_id == def_id
+ && &call_substs[..caller_substs.len()] == caller_substs;
+
+ if is_self_call {
+ self_call_locations.push(terminator.source_info);
+
+ //this is a self call so we shouldn't explore
+ //further down this path
+ continue;
+ }
+ }
+ }
+ TerminatorKind::Abort | TerminatorKind::Return => {
+ //found a path!
+ reached_exit_without_self_call = true;
+ break;
+ }
+ _ => {}
+ }
+
+ for successor in terminator.successors() {
+ reachable_without_self_call_queue.push(*successor);
+ }
+ }
+ }
+
+ // Check the number of self calls because a function that
+ // doesn't return (e.g., calls a `-> !` function or `loop { /*
+ // no break */ }`) shouldn't be linted unless it actually
+ // recurs.
+ if !reached_exit_without_self_call && !self_call_locations.is_empty() {
+ let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
+ let sp = tcx.sess.source_map().def_span(tcx.hir().span(hir_id));
+ let mut db = tcx.struct_span_lint_hir(
+ UNCONDITIONAL_RECURSION,
+ hir_id,
+ sp,
+ "function cannot return without recursing",
+ );
+ db.span_label(sp, "cannot return without recursing");
+ // offer some help to the programmer.
+ for location in &self_call_locations {
+ db.span_label(location.span, "recursive call site");
+ }
+ db.help("a `loop` may express intention better if this is on purpose");
+ db.emit();
+ }
+}
+++ /dev/null
-[package]
-authors = ["The Rust Project Developers"]
-build = "build.rs"
-name = "rustc_msan"
-version = "0.0.0"
-edition = "2018"
-
-[lib]
-name = "rustc_msan"
-path = "lib.rs"
-test = false
-
-[build-dependencies]
-build_helper = { path = "../build_helper" }
-cmake = "0.1.38"
-
-[dependencies]
-alloc = { path = "../liballoc" }
-core = { path = "../libcore" }
-compiler_builtins = "0.1.0"
+++ /dev/null
-use build_helper::sanitizer_lib_boilerplate;
-use std::env;
-
-use cmake::Config;
-
-fn main() {
- println!("cargo:rerun-if-env-changed=RUSTC_BUILD_SANITIZERS");
- if env::var("RUSTC_BUILD_SANITIZERS") != Ok("1".to_string()) {
- return;
- }
- if let Some(llvm_config) = env::var_os("LLVM_CONFIG") {
- build_helper::restore_library_path();
-
- let (native, target) = match sanitizer_lib_boilerplate("msan") {
- Ok(native) => native,
- _ => return,
- };
-
- Config::new(&native.src_dir)
- .define("COMPILER_RT_BUILD_SANITIZERS", "ON")
- .define("COMPILER_RT_BUILD_BUILTINS", "OFF")
- .define("COMPILER_RT_BUILD_XRAY", "OFF")
- .define("LLVM_CONFIG_PATH", llvm_config)
- .out_dir(&native.out_dir)
- .build_target(&target)
- .build();
- }
- println!("cargo:rerun-if-env-changed=LLVM_CONFIG");
-}
+++ /dev/null
-#![sanitizer_runtime]
-#![feature(nll)]
-#![feature(sanitizer_runtime)]
-#![feature(staged_api)]
-#![no_std]
-#![unstable(
- feature = "sanitizer_runtime_lib",
- reason = "internal implementation detail of sanitizers",
- issue = "none"
-)]
rustc_errors = { path = "../librustc_errors" }
rustc_error_codes = { path = "../librustc_error_codes" }
smallvec = { version = "1.0", features = ["union", "may_dangle"] }
+rustc_session = { path = "../librustc_session" }
rustc_span = { path = "../librustc_span" }
syntax = { path = "../libsyntax" }
unicode-normalization = "0.1.11"
//! [#64197]: https://github.com/rust-lang/rust/issues/64197
use crate::{parse_in, validate_attr};
-use rustc_errors::Applicability;
-use rustc_feature::Features;
-use rustc_span::edition::Edition;
-use rustc_span::symbol::sym;
-use rustc_span::Span;
+use rustc_data_structures::fx::FxHashMap;
+use rustc_error_codes::*;
+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,
+};
+use rustc_span::edition::{Edition, ALL_EDITIONS};
+use rustc_span::symbol::{sym, Symbol};
+use rustc_span::{Span, DUMMY_SP};
use syntax::ast::{self, AttrItem, Attribute, MetaItem};
use syntax::attr;
use syntax::attr::HasAttrs;
-use syntax::feature_gate::{feature_err, get_features};
use syntax::mut_visit::*;
use syntax::ptr::P;
-use syntax::sess::ParseSess;
+use syntax::sess::{feature_err, ParseSess};
use syntax::util::map_in_place::MapInPlace;
use smallvec::SmallVec;
pub features: Option<&'a Features>,
}
+fn get_features(
+ span_handler: &Handler,
+ krate_attrs: &[ast::Attribute],
+ crate_edition: Edition,
+ allow_features: &Option<Vec<String>>,
+) -> 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 active_features_up_to(edition: Edition) -> impl Iterator<Item = &'static Feature> {
+ ACTIVE_FEATURES.iter().filter(move |feature| {
+ if let Some(feature_edition) = feature.edition {
+ feature_edition <= edition
+ } else {
+ false
+ }
+ })
+ }
+
+ let mut features = Features::default();
+ let mut edition_enabled_features = FxHashMap::default();
+
+ for &edition in ALL_EDITIONS {
+ if edition <= crate_edition {
+ // The `crate_edition` implies its respective umbrella feature-gate
+ // (i.e., `#![feature(rust_20XX_preview)]` isn't needed on edition 20XX).
+ edition_enabled_features.insert(edition.feature_name(), edition);
+ }
+ }
+
+ for feature in active_features_up_to(crate_edition) {
+ feature.set(&mut features, DUMMY_SP);
+ edition_enabled_features.insert(feature.name, crate_edition);
+ }
+
+ // Process the edition umbrella feature-gates first, to ensure
+ // `edition_enabled_features` is completed before it's queried.
+ for attr in krate_attrs {
+ if !attr.check_name(sym::feature) {
+ continue;
+ }
+
+ let list = match attr.meta_item_list() {
+ Some(list) => list,
+ None => continue,
+ };
+
+ for mi in list {
+ if !mi.is_word() {
+ continue;
+ }
+
+ let name = mi.name_or_empty();
+
+ let edition = ALL_EDITIONS.iter().find(|e| name == e.feature_name()).copied();
+ if let Some(edition) = edition {
+ if edition <= crate_edition {
+ continue;
+ }
+
+ for feature in active_features_up_to(edition) {
+ // FIXME(Manishearth) there is currently no way to set
+ // lib features by edition
+ feature.set(&mut features, DUMMY_SP);
+ edition_enabled_features.insert(feature.name, edition);
+ }
+ }
+ }
+ }
+
+ for attr in krate_attrs {
+ if !attr.check_name(sym::feature) {
+ continue;
+ }
+
+ let list = match attr.meta_item_list() {
+ Some(list) => list,
+ None => 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",
+ format!("{}", ident.name),
+ Applicability::MaybeIncorrect,
+ )
+ .emit();
+ continue;
+ }
+ None => {
+ bad_input(mi.span()).span_label(mi.span(), "expected just one word").emit();
+ 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();
+ continue;
+ }
+
+ if ALL_EDITIONS.iter().any(|e| name == e.feature_name()) {
+ // Handled in the separate loop above.
+ continue;
+ }
+
+ let removed = REMOVED_FEATURES.iter().find(|f| name == f.name);
+ let stable_removed = STABLE_REMOVED_FEATURES.iter().find(|f| name == f.name);
+ if let Some(Feature { state, .. }) = removed.or(stable_removed) {
+ if let FeatureState::Removed { reason } | FeatureState::Stabilized { reason } =
+ state
+ {
+ feature_removed(span_handler, mi.span(), *reason);
+ continue;
+ }
+ }
+
+ if let Some(Feature { since, .. }) = ACCEPTED_FEATURES.iter().find(|f| name == f.name) {
+ let since = Some(Symbol::intern(since));
+ features.declared_lang_features.push((name, mi.span(), since));
+ continue;
+ }
+
+ if let Some(allowed) = allow_features.as_ref() {
+ if allowed.iter().find(|&f| name.as_str() == *f).is_none() {
+ struct_span_err!(
+ span_handler,
+ mi.span(),
+ E0725,
+ "the feature `{}` is not in the list of allowed features",
+ name
+ )
+ .emit();
+ continue;
+ }
+ }
+
+ if let Some(f) = ACTIVE_FEATURES.iter().find(|f| name == f.name) {
+ f.set(&mut features, mi.span());
+ features.declared_lang_features.push((name, mi.span(), None));
+ continue;
+ }
+
+ features.declared_lib_features.push((name, mi.span()));
+ }
+ }
+
+ features
+}
+
// `cfg_attr`-process the crate's attributes and compute the crate's features.
pub fn features(
mut krate: ast::Crate,
use std::iter::once;
use std::ops::Range;
+use rustc_errors::{Applicability, Handler};
use rustc_lexer::unescape::{EscapeError, Mode};
use rustc_span::{BytePos, Span};
-use syntax::errors::{Applicability, Handler};
-
pub(crate) fn emit_unescape_error(
handler: &Handler,
// interior part of the literal, without quotes
use rustc_data_structures::fx::FxHashSet;
use rustc_error_codes::*;
-use rustc_errors::{self, pluralize, Applicability, DiagnosticBuilder, Handler, PResult};
+use rustc_errors::{pluralize, struct_span_err};
+use rustc_errors::{Applicability, DiagnosticBuilder, Handler, PResult};
+use rustc_span::source_map::Spanned;
use rustc_span::symbol::kw;
use rustc_span::{MultiSpan, Span, SpanSnippetError, DUMMY_SP};
use syntax::ast::{
use syntax::ast::{AttrVec, ItemKind, Mutability, Pat, PatKind, PathSegment, QSelf, Ty, TyKind};
use syntax::print::pprust;
use syntax::ptr::P;
-use syntax::struct_span_err;
use syntax::token::{self, token_can_begin_expr, TokenKind};
use syntax::util::parser::AssocOp;
secondary_path: String,
},
UselessDocComment,
- InclusiveRangeWithNoEnd,
}
impl Error {
);
err
}
- Error::InclusiveRangeWithNoEnd => {
- let mut err = struct_span_err!(handler, sp, E0586, "inclusive range with no end",);
- err.help("inclusive ranges must be bounded at the end (`..=b` or `a..=b`)");
- err
- }
}
}
}
};
(
format!("expected one of {}, found {}", expect, actual),
- (
- self.sess.source_map().next_point(self.prev_span),
- format!("expected one of {}", short_expect),
- ),
+ (self.prev_span.shrink_to_hi(), format!("expected one of {}", short_expect)),
)
} else if expected.is_empty() {
(
} else {
(
format!("expected {}, found {}", expect, actual),
- (self.sess.source_map().next_point(self.prev_span), format!("expected {}", expect)),
+ (self.prev_span.shrink_to_hi(), format!("expected {}", expect)),
)
};
self.last_unexpected_token_span = Some(self.token.span);
}
}
+ /// Check to see if a pair of chained operators looks like an attempt at chained comparison,
+ /// e.g. `1 < x <= 3`. If so, suggest either splitting the comparison into two, or
+ /// parenthesising the leftmost comparison.
+ fn attempt_chained_comparison_suggestion(
+ &mut self,
+ err: &mut DiagnosticBuilder<'_>,
+ inner_op: &Expr,
+ outer_op: &Spanned<AssocOp>,
+ ) {
+ if let ExprKind::Binary(op, ref l1, ref r1) = inner_op.kind {
+ match (op.node, &outer_op.node) {
+ // `x < y < z` and friends.
+ (BinOpKind::Lt, AssocOp::Less) | (BinOpKind::Lt, AssocOp::LessEqual) |
+ (BinOpKind::Le, AssocOp::LessEqual) | (BinOpKind::Le, AssocOp::Less) |
+ // `x > y > z` and friends.
+ (BinOpKind::Gt, AssocOp::Greater) | (BinOpKind::Gt, AssocOp::GreaterEqual) |
+ (BinOpKind::Ge, AssocOp::GreaterEqual) | (BinOpKind::Ge, AssocOp::Greater) => {
+ let expr_to_str = |e: &Expr| {
+ self.span_to_snippet(e.span)
+ .unwrap_or_else(|_| pprust::expr_to_string(&e))
+ };
+ err.span_suggestion(
+ inner_op.span.to(outer_op.span),
+ "split the comparison into two...",
+ format!(
+ "{} {} {} && {} {}",
+ expr_to_str(&l1),
+ op.node.to_string(),
+ expr_to_str(&r1),
+ expr_to_str(&r1),
+ outer_op.node.to_ast_binop().unwrap().to_string(),
+ ),
+ Applicability::MaybeIncorrect,
+ );
+ err.span_suggestion(
+ inner_op.span.to(outer_op.span),
+ "...or parenthesize one of the comparisons",
+ format!(
+ "({} {} {}) {}",
+ expr_to_str(&l1),
+ op.node.to_string(),
+ expr_to_str(&r1),
+ outer_op.node.to_ast_binop().unwrap().to_string(),
+ ),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ _ => {}
+ }
+ }
+ }
+
/// Produces an error if comparison operators are chained (RFC #558).
/// We only need to check the LHS, not the RHS, because all comparison ops have same
/// precedence (see `fn precedence`) and are left-associative (see `fn fixity`).
/// / \
/// inner_op r2
/// / \
- /// l1 r1
+ /// l1 r1
pub(super) fn check_no_chained_comparison(
&mut self,
- lhs: &Expr,
- outer_op: &AssocOp,
+ inner_op: &Expr,
+ outer_op: &Spanned<AssocOp>,
) -> PResult<'a, Option<P<Expr>>> {
debug_assert!(
- outer_op.is_comparison(),
+ outer_op.node.is_comparison(),
"check_no_chained_comparison: {:?} is not comparison",
- outer_op,
+ outer_op.node,
);
let mk_err_expr =
|this: &Self, span| Ok(Some(this.mk_expr(span, ExprKind::Err, AttrVec::new())));
- match lhs.kind {
+ match inner_op.kind {
ExprKind::Binary(op, _, _) if op.node.is_comparison() => {
// Respan to include both operators.
let op_span = op.span.to(self.prev_span);
- let mut err = self
- .struct_span_err(op_span, "chained comparison operators require parentheses");
+ let mut err =
+ self.struct_span_err(op_span, "comparison operators cannot be chained");
+
+ // If it looks like a genuine attempt to chain operators (as opposed to a
+ // misformatted turbofish, for instance), suggest a correct form.
+ self.attempt_chained_comparison_suggestion(&mut err, inner_op, outer_op);
let suggest = |err: &mut DiagnosticBuilder<'_>| {
err.span_suggestion_verbose(
};
if op.node == BinOpKind::Lt &&
- *outer_op == AssocOp::Less || // Include `<` to provide this recommendation
- *outer_op == AssocOp::Greater
+ outer_op.node == AssocOp::Less || // Include `<` to provide this recommendation
+ outer_op.node == AssocOp::Greater
// even in a case like the following:
{
// Foo<Bar<Baz<Qux, ()>>>
- if *outer_op == AssocOp::Less {
+ if outer_op.node == AssocOp::Less {
let snapshot = self.clone();
self.bump();
// So far we have parsed `foo<bar<`, consume the rest of the type args.
// FIXME: actually check that the two expressions in the binop are
// paths and resynthesize new fn call expression instead of using
// `ExprKind::Err` placeholder.
- mk_err_expr(self, lhs.span.to(self.prev_span))
+ mk_err_expr(self, inner_op.span.to(self.prev_span))
}
Err(mut expr_err) => {
expr_err.cancel();
// FIXME: actually check that the two expressions in the binop are
// paths and resynthesize new fn call expression instead of using
// `ExprKind::Err` placeholder.
- mk_err_expr(self, lhs.span.to(self.prev_span))
+ mk_err_expr(self, inner_op.span.to(self.prev_span))
}
}
} else {
_ if self.prev_span == DUMMY_SP => (self.token.span, self.token.span),
// EOF, don't want to point at the following char, but rather the last token.
(token::Eof, None) => (self.prev_span, self.token.span),
- _ => (self.sess.source_map().next_point(self.prev_span), self.token.span),
+ _ => (self.prev_span.shrink_to_hi(), self.token.span),
};
let msg = format!(
"expected `{}`, found {}",
err.span_label(sp, "unclosed delimiter");
}
err.span_suggestion_short(
- self.sess.source_map().next_point(self.prev_span),
+ self.prev_span.shrink_to_hi(),
&format!("{} may belong here", delim.to_string()),
delim.to_string(),
Applicability::MaybeIncorrect,
-use super::diagnostics::Error;
use super::pat::{GateOr, PARAM_EXPECTED};
use super::{BlockMode, Parser, PathStyle, PrevTokenKind, Restrictions, TokenType};
use super::{SemiColonMode, SeqSep, TokenExpectType};
use crate::maybe_recover_from_interpolated_ty_qpath;
use rustc_errors::{Applicability, PResult};
-use rustc_span::source_map::{self, Span};
+use rustc_span::source_map::{self, Span, Spanned};
use rustc_span::symbol::{kw, sym, Symbol};
use std::mem;
use syntax::ast::{self, AttrStyle, AttrVec, CaptureBy, Field, Ident, Lit, DUMMY_NODE_ID};
};
let cur_op_span = self.token.span;
- let restrictions = if op.is_assign_like() {
+ let restrictions = if op.node.is_assign_like() {
self.restrictions & Restrictions::NO_STRUCT_LITERAL
} else {
self.restrictions
};
- let prec = op.precedence();
+ let prec = op.node.precedence();
if prec < min_prec {
break;
}
// Check for deprecated `...` syntax
- if self.token == token::DotDotDot && op == AssocOp::DotDotEq {
+ if self.token == token::DotDotDot && op.node == AssocOp::DotDotEq {
self.err_dotdotdot_syntax(self.token.span);
}
}
self.bump();
- if op.is_comparison() {
+ if op.node.is_comparison() {
if let Some(expr) = self.check_no_chained_comparison(&lhs, &op)? {
return Ok(expr);
}
}
+ let op = op.node;
// Special cases:
if op == AssocOp::As {
lhs = self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Cast)?;
}
fn should_continue_as_assoc_expr(&mut self, lhs: &Expr) -> bool {
- match (self.expr_is_complete(lhs), self.check_assoc_op()) {
+ match (self.expr_is_complete(lhs), self.check_assoc_op().map(|op| op.node)) {
// Semi-statement forms are odd:
// See https://github.com/rust-lang/rust/issues/29071
(true, None) => false,
/// The method does not advance the current token.
///
/// Also performs recovery for `and` / `or` which are mistaken for `&&` and `||` respectively.
- fn check_assoc_op(&self) -> Option<AssocOp> {
- match (AssocOp::from_token(&self.token), &self.token.kind) {
- (op @ Some(_), _) => op,
- (None, token::Ident(sym::and, false)) => {
- self.error_bad_logical_op("and", "&&", "conjunction");
- Some(AssocOp::LAnd)
- }
- (None, token::Ident(sym::or, false)) => {
- self.error_bad_logical_op("or", "||", "disjunction");
- Some(AssocOp::LOr)
- }
- _ => None,
- }
+ fn check_assoc_op(&self) -> Option<Spanned<AssocOp>> {
+ Some(Spanned {
+ node: match (AssocOp::from_token(&self.token), &self.token.kind) {
+ (Some(op), _) => op,
+ (None, token::Ident(sym::and, false)) => {
+ self.error_bad_logical_op("and", "&&", "conjunction");
+ AssocOp::LAnd
+ }
+ (None, token::Ident(sym::or, false)) => {
+ self.error_bad_logical_op("or", "||", "disjunction");
+ AssocOp::LOr
+ }
+ _ => return None,
+ },
+ span: self.token.span,
+ })
}
/// Error on `and` and `or` suggesting `&&` and `||` respectively.
// | |
// | parsed until here as `"y" & X`
err.span_suggestion_short(
- cm.next_point(arm_start_span),
+ arm_start_span.shrink_to_hi(),
"missing a comma here to end this `match` arm",
",".to_owned(),
Applicability::MachineApplicable,
limits: RangeLimits,
) -> PResult<'a, ExprKind> {
if end.is_none() && limits == RangeLimits::Closed {
- Err(self.span_fatal_err(self.token.span, Error::InclusiveRangeWithNoEnd))
+ self.error_inclusive_range_with_no_end(self.prev_span);
+ Ok(ExprKind::Err)
} else {
Ok(ExprKind::Range(start, end, limits))
}
self.expect_gt()?;
(params, span_lo.to(self.prev_span))
} else {
- (vec![], self.prev_span.between(self.token.span))
+ (vec![], self.prev_span.shrink_to_hi())
};
Ok(ast::Generics {
params,
use crate::maybe_whole;
use rustc_error_codes::*;
-use rustc_errors::{Applicability, DiagnosticBuilder, PResult, StashKey};
-use rustc_span::source_map::{self, respan, Span};
+use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, PResult, StashKey};
+use rustc_span::source_map::{self, respan, Span, Spanned};
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_span::BytePos;
use syntax::ast::{self, AttrKind, AttrStyle, AttrVec, Attribute, Ident, DUMMY_NODE_ID};
use syntax::ast::{FnHeader, ForeignItem, ForeignItemKind, Mutability, Visibility, VisibilityKind};
use syntax::print::pprust;
use syntax::ptr::P;
-use syntax::struct_span_err;
use syntax::token;
use syntax::tokenstream::{DelimSpan, TokenStream, TokenTree};
self.sess.gated_spans.gate(sym::const_extern_fn, lo.to(self.token.span));
}
let ext = self.parse_extern()?;
- self.bump(); // `fn`
+ self.expect_keyword(kw::Fn)?;
let header = FnHeader {
unsafety,
/// impl<'a, T> TYPE { /* impl items */ }
/// impl<'a, T> TRAIT for TYPE { /* impl items */ }
/// impl<'a, T> !TRAIT for TYPE { /* impl items */ }
+ /// impl<'a, T> const TRAIT for TYPE { /* impl items */ }
///
/// We actually parse slightly more relaxed grammar for better error reporting and recovery.
- /// `impl` GENERICS `!`? TYPE `for`? (TYPE | `..`) (`where` PREDICATES)? `{` BODY `}`
- /// `impl` GENERICS `!`? TYPE (`where` PREDICATES)? `{` BODY `}`
+ /// `impl` GENERICS `const`? `!`? TYPE `for`? (TYPE | `..`) (`where` PREDICATES)? `{` BODY `}`
+ /// `impl` GENERICS `const`? `!`? TYPE (`where` PREDICATES)? `{` BODY `}`
fn parse_item_impl(
&mut self,
unsafety: Unsafety,
let mut generics = if self.choose_generics_over_qpath() {
self.parse_generics()?
} else {
- Generics::default()
+ let mut generics = Generics::default();
+ // impl A for B {}
+ // /\ this is where `generics.span` should point when there are no type params.
+ generics.span = self.prev_span.shrink_to_hi();
+ generics
+ };
+
+ let constness = if self.eat_keyword(kw::Const) {
+ let span = self.prev_span;
+ self.sess.gated_spans.gate(sym::const_trait_impl, span);
+ Some(respan(span, Constness::Const))
+ } else {
+ None
};
// Disambiguate `impl !Trait for Type { ... }` and `impl ! { ... }` for the never type.
err_path(ty_first.span)
}
};
- let trait_ref = TraitRef { path, ref_id: ty_first.id };
+ let constness = constness.map(|c| c.node);
+ let trait_ref = TraitRef { path, constness, ref_id: ty_first.id };
ItemKind::Impl(
unsafety,
)
}
None => {
+ // Reject `impl const Type {}` here
+ if let Some(Spanned { node: Constness::Const, span }) = constness {
+ self.struct_span_err(span, "`const` cannot modify an inherent impl")
+ .help("only a trait impl can be `const`")
+ .emit();
+ }
+
// impl Type
ItemKind::Impl(
unsafety,
}
}
_ => {
- let sp = self.sess.source_map().next_point(self.prev_span);
+ let sp = self.prev_span.shrink_to_hi();
let mut err = self.struct_span_err(
sp,
&format!("expected `,`, or `}}`, found {}", super::token_descr(&self.token)),
// it's safe to peel off one character only when it has the close delim
self.prev_span.with_lo(self.prev_span.hi() - BytePos(1))
} else {
- self.sess.source_map().next_point(self.prev_span)
+ self.prev_span.shrink_to_hi()
};
self.struct_span_err(
Applicability::MaybeIncorrect,
)
.span_suggestion(
- self.sess.source_map().next_point(self.prev_span),
+ self.prev_span.shrink_to_hi(),
"add a semicolon",
';'.to_string(),
Applicability::MaybeIncorrect,
mod expr;
mod item;
mod module;
+pub use module::{ModulePath, ModulePathSuccess};
mod pat;
mod path;
mod ty;
use crate::{Directory, DirectoryOwnership};
use log::debug;
-use rustc_errors::{Applicability, DiagnosticBuilder, FatalError, PResult};
+use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, FatalError, PResult};
use rustc_span::source_map::respan;
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_span::{BytePos, FileName, Span, DUMMY_SP};
use syntax::print::pprust;
use syntax::ptr::P;
use syntax::sess::ParseSess;
-use syntax::struct_span_err;
use syntax::token::{self, DelimToken, Token, TokenKind};
use syntax::tokenstream::{self, DelimSpan, TokenStream, TokenTree, TreeAndJoint};
use syntax::util::comments::{doc_comment_style, strip_doc_comment_decoration};
/// Used to determine the path to externally loaded source files.
pub(super) directory: Directory<'a>,
/// `true` to parse sub-modules in other files.
- pub(super) recurse_into_file_modules: bool,
+ // Public for rustfmt usage.
+ pub recurse_into_file_modules: bool,
/// Name of the root module this parser originated from. If `None`, then the
/// name is not known. This does not change while the parser is descending
/// into modules, and sub-parsers have new values for this name.
token_cursor: TokenCursor,
desugar_doc_comments: bool,
/// `true` we should configure out of line modules as we parse.
- cfg_mods: bool,
+ // Public for rustfmt usage.
+ pub cfg_mods: bool,
/// This field is used to keep track of how many left angle brackets we have seen. This is
/// required in order to detect extra leading left angle brackets (`<` characters) and error
/// appropriately.
}
}
- fn parse_ident(&mut self) -> PResult<'a, ast::Ident> {
+ // Public for rustfmt usage.
+ pub fn parse_ident(&mut self) -> PResult<'a, ast::Ident> {
self.parse_ident_common(true)
}
/// If the next token is the given keyword, eats it and returns `true`.
/// Otherwise, returns `false`. An expectation is also added for diagnostics purposes.
- fn eat_keyword(&mut self, kw: Symbol) -> bool {
+ // Public for rustfmt usage.
+ pub fn eat_keyword(&mut self, kw: Symbol) -> bool {
if self.check_keyword(kw) {
self.bump();
true
break;
}
Err(mut expect_err) => {
- let sp = self.sess.source_map().next_point(self.prev_span);
+ let sp = self.prev_span.shrink_to_hi();
let token_str = pprust::token_kind_to_string(t);
// Attempt to keep parsing if it was a similar separator.
use std::path::{self, Path, PathBuf};
/// Information about the path to a module.
-pub(super) struct ModulePath {
+// Public for rustfmt usage.
+pub struct ModulePath {
name: String,
path_exists: bool,
pub result: Result<ModulePathSuccess, Error>,
}
-pub(super) struct ModulePathSuccess {
+// Public for rustfmt usage.
+pub struct ModulePathSuccess {
pub path: PathBuf,
pub directory_ownership: DirectoryOwnership,
}
}
}
- pub(super) fn submod_path_from_attr(attrs: &[Attribute], dir_path: &Path) -> Option<PathBuf> {
+ // Public for rustfmt usage.
+ pub fn submod_path_from_attr(attrs: &[Attribute], dir_path: &Path) -> Option<PathBuf> {
if let Some(s) = attr::first_attr_value_str_by_name(attrs, sym::path) {
let s = s.as_str();
}
/// Returns a path to a module.
- pub(super) fn default_submod_path(
+ // Public for rustfmt usage.
+ pub fn default_submod_path(
id: ast::Ident,
relative: Option<ast::Ident>,
dir_path: &Path,
use super::{Parser, PathStyle};
use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
-use rustc_errors::{Applicability, DiagnosticBuilder, PResult};
+use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, PResult};
use rustc_span::source_map::{respan, Span, Spanned};
use rustc_span::symbol::{kw, sym};
use syntax::ast::{self, AttrVec, Attribute, FieldPat, Mac, Pat, PatKind, RangeEnd, RangeSyntax};
maybe_whole!(self, NtPat, |x| x);
let lo = self.token.span;
- let pat = match self.token.kind {
- token::BinOp(token::And) | token::AndAnd => self.parse_pat_deref(expected)?,
- token::OpenDelim(token::Paren) => self.parse_pat_tuple_or_parens()?,
- token::OpenDelim(token::Bracket) => {
- // Parse `[pat, pat,...]` as a slice pattern.
- let (pats, _) =
- self.parse_delim_comma_seq(token::Bracket, |p| p.parse_pat_with_or_inner())?;
- PatKind::Slice(pats)
- }
- token::DotDot => {
- self.bump();
- if self.is_pat_range_end_start() {
- // Parse `..42` for recovery.
- self.parse_pat_range_to(RangeEnd::Excluded, "..")?
- } else {
- // A rest pattern `..`.
- PatKind::Rest
- }
- }
- token::DotDotEq => {
- // Parse `..=42` for recovery.
- self.bump();
- self.parse_pat_range_to(RangeEnd::Included(RangeSyntax::DotDotEq), "..=")?
- }
- token::DotDotDot => {
- // Parse `...42` for recovery.
- self.bump();
- self.parse_pat_range_to(RangeEnd::Included(RangeSyntax::DotDotDot), "...")?
+
+ let pat = if self.check(&token::BinOp(token::And)) || self.token.kind == token::AndAnd {
+ self.parse_pat_deref(expected)?
+ } else if self.check(&token::OpenDelim(token::Paren)) {
+ self.parse_pat_tuple_or_parens()?
+ } else if self.check(&token::OpenDelim(token::Bracket)) {
+ // Parse `[pat, pat,...]` as a slice pattern.
+ let (pats, _) =
+ self.parse_delim_comma_seq(token::Bracket, |p| p.parse_pat_with_or_inner())?;
+ PatKind::Slice(pats)
+ } else if self.check(&token::DotDot) && !self.is_pat_range_end_start(1) {
+ // A rest pattern `..`.
+ self.bump(); // `..`
+ PatKind::Rest
+ } else if let Some(form) = self.parse_range_end() {
+ self.parse_pat_range_to(form)? // `..=X`, `...X`, or `..X`.
+ } else if self.eat_keyword(kw::Underscore) {
+ // Parse _
+ PatKind::Wild
+ } else if self.eat_keyword(kw::Mut) {
+ self.parse_pat_ident_mut()?
+ } else if self.eat_keyword(kw::Ref) {
+ // Parse ref ident @ pat / ref mut ident @ pat
+ let mutbl = self.parse_mutability();
+ self.parse_pat_ident(BindingMode::ByRef(mutbl))?
+ } else if self.eat_keyword(kw::Box) {
+ // Parse `box pat`
+ let pat = self.parse_pat_with_range_pat(false, None)?;
+ self.sess.gated_spans.gate(sym::box_patterns, lo.to(self.prev_span));
+ PatKind::Box(pat)
+ } else if self.can_be_ident_pat() {
+ // Parse `ident @ pat`
+ // This can give false positives and parse nullary enums,
+ // they are dealt with later in resolve.
+ self.parse_pat_ident(BindingMode::ByValue(Mutability::Not))?
+ } else if self.is_start_of_pat_with_path() {
+ // Parse pattern starting with a path
+ let (qself, path) = if self.eat_lt() {
+ // Parse a qualified path
+ let (qself, path) = self.parse_qpath(PathStyle::Expr)?;
+ (Some(qself), path)
+ } else {
+ // Parse an unqualified path
+ (None, self.parse_path(PathStyle::Expr)?)
+ };
+ let span = lo.to(self.prev_span);
+
+ if qself.is_none() && self.check(&token::Not) {
+ self.parse_pat_mac_invoc(path)?
+ } else if let Some(form) = self.parse_range_end() {
+ let begin = self.mk_expr(span, ExprKind::Path(qself, path), AttrVec::new());
+ self.parse_pat_range_begin_with(begin, form)?
+ } else if self.check(&token::OpenDelim(token::Brace)) {
+ self.parse_pat_struct(qself, path)?
+ } else if self.check(&token::OpenDelim(token::Paren)) {
+ self.parse_pat_tuple_struct(qself, path)?
+ } else {
+ PatKind::Path(qself, path)
}
- // At this point, token != `&`, `&&`, `(`, `[`, `..`, `..=`, or `...`.
- _ => {
- if self.eat_keyword(kw::Underscore) {
- // Parse _
- PatKind::Wild
- } else if self.eat_keyword(kw::Mut) {
- self.parse_pat_ident_mut()?
- } else if self.eat_keyword(kw::Ref) {
- // Parse ref ident @ pat / ref mut ident @ pat
- let mutbl = self.parse_mutability();
- self.parse_pat_ident(BindingMode::ByRef(mutbl))?
- } else if self.eat_keyword(kw::Box) {
- // Parse `box pat`
- let pat = self.parse_pat_with_range_pat(false, None)?;
- self.sess.gated_spans.gate(sym::box_patterns, lo.to(self.prev_span));
- PatKind::Box(pat)
- } else if self.can_be_ident_pat() {
- // Parse `ident @ pat`
- // This can give false positives and parse nullary enums,
- // they are dealt with later in resolve.
- self.parse_pat_ident(BindingMode::ByValue(Mutability::Not))?
- } else if self.is_start_of_pat_with_path() {
- // Parse pattern starting with a path
- let (qself, path) = if self.eat_lt() {
- // Parse a qualified path
- let (qself, path) = self.parse_qpath(PathStyle::Expr)?;
- (Some(qself), path)
- } else {
- // Parse an unqualified path
- (None, self.parse_path(PathStyle::Expr)?)
- };
- match self.token.kind {
- token::Not if qself.is_none() => self.parse_pat_mac_invoc(path)?,
- token::DotDotDot | token::DotDotEq | token::DotDot => {
- self.parse_pat_range_starting_with_path(lo, qself, path)?
- }
- token::OpenDelim(token::Brace) => self.parse_pat_struct(qself, path)?,
- token::OpenDelim(token::Paren) => {
- self.parse_pat_tuple_struct(qself, path)?
- }
- _ => PatKind::Path(qself, path),
- }
- } else {
- // Try to parse everything else as literal with optional minus
- match self.parse_literal_maybe_minus() {
- Ok(begin)
- if self.check(&token::DotDot)
- || self.check(&token::DotDotEq)
- || self.check(&token::DotDotDot) =>
- {
- self.parse_pat_range_starting_with_lit(begin)?
- }
- Ok(begin) => PatKind::Lit(begin),
- Err(err) => return self.fatal_unexpected_non_pat(err, expected),
- }
- }
+ } else {
+ // Try to parse everything else as literal with optional minus
+ match self.parse_literal_maybe_minus() {
+ Ok(begin) => match self.parse_range_end() {
+ Some(form) => self.parse_pat_range_begin_with(begin, form)?,
+ None => PatKind::Lit(begin),
+ },
+ Err(err) => return self.fatal_unexpected_non_pat(err, expected),
}
};
let pat = self.recover_intersection_pat(pat)?;
if !allow_range_pat {
- self.ban_pat_range_if_ambiguous(&pat)?
+ self.ban_pat_range_if_ambiguous(&pat)
}
Ok(pat)
}
/// Ban a range pattern if it has an ambiguous interpretation.
- fn ban_pat_range_if_ambiguous(&self, pat: &Pat) -> PResult<'a, ()> {
+ fn ban_pat_range_if_ambiguous(&self, pat: &Pat) {
match pat.kind {
PatKind::Range(
..,
Spanned { node: RangeEnd::Included(RangeSyntax::DotDotDot), .. },
- ) => return Ok(()),
+ ) => return,
PatKind::Range(..) => {}
- _ => return Ok(()),
+ _ => return,
}
- let mut err =
- self.struct_span_err(pat.span, "the range pattern here has ambiguous interpretation");
- err.span_suggestion(
- pat.span,
- "add parentheses to clarify the precedence",
- format!("({})", pprust::pat_to_string(&pat)),
- // "ambiguous interpretation" implies that we have to be guessing
- Applicability::MaybeIncorrect,
- );
- Err(err)
+ self.struct_span_err(pat.span, "the range pattern here has ambiguous interpretation")
+ .span_suggestion(
+ pat.span,
+ "add parentheses to clarify the precedence",
+ format!("({})", pprust::pat_to_string(&pat)),
+ // "ambiguous interpretation" implies that we have to be guessing
+ Applicability::MaybeIncorrect,
+ )
+ .emit();
}
/// Parse `&pat` / `&mut pat`.
Ok(PatKind::Mac(mac))
}
- fn excluded_range_end(&self, span: Span) -> RangeEnd {
- self.sess.gated_spans.gate(sym::exclusive_range_pattern, span);
- RangeEnd::Excluded
- }
-
- /// Parse a range pattern `$path $form $end?` where `$form = ".." | "..." | "..=" ;`.
- /// The `$path` has already been parsed and the next token is the `$form`.
- fn parse_pat_range_starting_with_path(
- &mut self,
- lo: Span,
- qself: Option<QSelf>,
- path: Path,
- ) -> PResult<'a, PatKind> {
- let (end_kind, form) = match self.token.kind {
- token::DotDot => (self.excluded_range_end(self.token.span), ".."),
- token::DotDotDot => (RangeEnd::Included(RangeSyntax::DotDotDot), "..."),
- token::DotDotEq => (RangeEnd::Included(RangeSyntax::DotDotEq), "..="),
- _ => panic!("can only parse `..`/`...`/`..=` for ranges (checked above)"),
- };
- let op_span = self.token.span;
- // Parse range
- let span = lo.to(self.prev_span);
- let begin = self.mk_expr(span, ExprKind::Path(qself, path), AttrVec::new());
- self.bump();
- let end = self.parse_pat_range_end_opt(&begin, form)?;
- Ok(PatKind::Range(begin, end, respan(op_span, end_kind)))
- }
-
- /// Parse a range pattern `$literal $form $end?` where `$form = ".." | "..." | "..=" ;`.
- /// The `$path` has already been parsed and the next token is the `$form`.
- fn parse_pat_range_starting_with_lit(&mut self, begin: P<Expr>) -> PResult<'a, PatKind> {
- let op_span = self.token.span;
- let (end_kind, form) = if self.eat(&token::DotDotDot) {
- (RangeEnd::Included(RangeSyntax::DotDotDot), "...")
- } else if self.eat(&token::DotDotEq) {
- (RangeEnd::Included(RangeSyntax::DotDotEq), "..=")
- } else if self.eat(&token::DotDot) {
- (self.excluded_range_end(op_span), "..")
- } else {
- panic!("impossible case: we already matched on a range-operator token")
- };
- let end = self.parse_pat_range_end_opt(&begin, form)?;
- Ok(PatKind::Range(begin, end, respan(op_span, end_kind)))
- }
-
fn fatal_unexpected_non_pat(
&mut self,
mut err: DiagnosticBuilder<'a>,
Err(err)
}
- /// Is the current token suitable as the start of a range patterns end?
- fn is_pat_range_end_start(&self) -> bool {
- self.token.is_path_start() // e.g. `MY_CONST`;
- || self.token == token::Dot // e.g. `.5` for recovery;
- || self.token.can_begin_literal_or_bool() // e.g. `42`.
- || self.token.is_whole_expr()
- }
-
- /// Parse a range-to pattern, e.g. `..X` and `..=X` for recovery.
- fn parse_pat_range_to(&mut self, re: RangeEnd, form: &str) -> PResult<'a, PatKind> {
- let lo = self.prev_span;
- let end = self.parse_pat_range_end()?;
- let range_span = lo.to(end.span);
- let begin = self.mk_expr(range_span, ExprKind::Err, AttrVec::new());
-
- self.struct_span_err(range_span, &format!("`{}X` range patterns are not supported", form))
- .span_suggestion(
- range_span,
- "try using the minimum value for the type",
- format!("MIN{}{}", form, pprust::expr_to_string(&end)),
- Applicability::HasPlaceholders,
- )
- .emit();
-
- Ok(PatKind::Range(begin, end, respan(lo, re)))
+ /// Parses the range pattern end form `".." | "..." | "..=" ;`.
+ fn parse_range_end(&mut self) -> Option<Spanned<RangeEnd>> {
+ let re = if self.eat(&token::DotDotDot) {
+ RangeEnd::Included(RangeSyntax::DotDotDot)
+ } else if self.eat(&token::DotDotEq) {
+ RangeEnd::Included(RangeSyntax::DotDotEq)
+ } else if self.eat(&token::DotDot) {
+ self.sess.gated_spans.gate(sym::exclusive_range_pattern, self.prev_span);
+ RangeEnd::Excluded
+ } else {
+ return None;
+ };
+ Some(respan(self.prev_span, re))
}
- /// Parse the end of a `X..Y`, `X..=Y`, or `X...Y` range pattern or recover
- /// if that end is missing treating it as `X..`, `X..=`, or `X...` respectively.
- fn parse_pat_range_end_opt(&mut self, begin: &Expr, form: &str) -> PResult<'a, P<Expr>> {
- if self.is_pat_range_end_start() {
+ /// Parse a range pattern `$begin $form $end?` where `$form = ".." | "..." | "..=" ;`.
+ /// `$begin $form` has already been parsed.
+ fn parse_pat_range_begin_with(
+ &mut self,
+ begin: P<Expr>,
+ re: Spanned<RangeEnd>,
+ ) -> PResult<'a, PatKind> {
+ let end = if self.is_pat_range_end_start(0) {
// Parsing e.g. `X..=Y`.
- self.parse_pat_range_end()
+ Some(self.parse_pat_range_end()?)
} else {
// Parsing e.g. `X..`.
- let range_span = begin.span.to(self.prev_span);
+ self.sess.gated_spans.gate(sym::half_open_range_patterns, begin.span.to(re.span));
+ if let RangeEnd::Included(_) = re.node {
+ // FIXME(Centril): Consider semantic errors instead in `ast_validation`.
+ // Possibly also do this for `X..=` in *expression* contexts.
+ self.error_inclusive_range_with_no_end(re.span);
+ }
+ None
+ };
+ Ok(PatKind::Range(Some(begin), end, re))
+ }
- self.struct_span_err(
- range_span,
- &format!("`X{}` range patterns are not supported", form),
- )
- .span_suggestion(
- range_span,
- "try using the maximum value for the type",
- format!("{}{}MAX", pprust::expr_to_string(&begin), form),
- Applicability::HasPlaceholders,
+ pub(super) fn error_inclusive_range_with_no_end(&self, span: Span) {
+ use rustc_error_codes::E0586;
+ struct_span_err!(self.sess.span_diagnostic, span, E0586, "inclusive range with no end")
+ .span_suggestion_short(
+ span,
+ "use `..` instead",
+ "..".to_string(),
+ Applicability::MachineApplicable,
)
+ .note("inclusive ranges must be bounded at the end (`..=b` or `a..=b`)")
.emit();
+ }
- Ok(self.mk_expr(range_span, ExprKind::Err, AttrVec::new()))
+ /// Parse a range-to pattern, `..X` or `..=X` where `X` remains to be parsed.
+ ///
+ /// The form `...X` is prohibited to reduce confusion with the potential
+ /// expression syntax `...expr` for splatting in expressions.
+ fn parse_pat_range_to(&mut self, mut re: Spanned<RangeEnd>) -> PResult<'a, PatKind> {
+ let end = self.parse_pat_range_end()?;
+ self.sess.gated_spans.gate(sym::half_open_range_patterns, re.span.to(self.prev_span));
+ if let RangeEnd::Included(ref mut syn @ RangeSyntax::DotDotDot) = &mut re.node {
+ *syn = RangeSyntax::DotDotEq;
+ self.struct_span_err(re.span, "range-to patterns with `...` are not allowed")
+ .span_suggestion_short(
+ re.span,
+ "use `..=` instead",
+ "..=".to_string(),
+ Applicability::MachineApplicable,
+ )
+ .emit();
}
+ Ok(PatKind::Range(None, Some(end), re))
+ }
+
+ /// Is the token `dist` away from the current suitable as the start of a range patterns end?
+ fn is_pat_range_end_start(&self, dist: usize) -> bool {
+ self.look_ahead(dist, |t| {
+ t.is_path_start() // e.g. `MY_CONST`;
+ || t.kind == token::Dot // e.g. `.5` for recovery;
+ || t.can_begin_literal_or_bool() // e.g. `42`.
+ || t.is_whole_expr()
+ })
}
fn parse_pat_range_end(&mut self) -> PResult<'a, P<Expr>> {
use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
use rustc_error_codes::*;
-use rustc_errors::{pluralize, Applicability, PResult};
+use rustc_errors::{pluralize, struct_span_err, Applicability, PResult};
use rustc_span::source_map::Span;
-use rustc_span::symbol::kw;
+use rustc_span::symbol::{kw, sym};
use syntax::ast::{
self, BareFnTy, FunctionRetTy, GenericParam, Ident, Lifetime, MutTy, Ty, TyKind,
};
};
use syntax::ast::{Mac, Mutability};
use syntax::ptr::P;
-use syntax::struct_span_err;
use syntax::token::{self, Token};
+/// Any `?` or `?const` modifiers that appear at the start of a bound.
+struct BoundModifiers {
+ /// `?Trait`.
+ maybe: Option<Span>,
+
+ /// `?const Trait`.
+ maybe_const: Option<Span>,
+}
+
+impl BoundModifiers {
+ fn trait_bound_modifier(&self) -> TraitBoundModifier {
+ match self.maybe {
+ Some(_) => TraitBoundModifier::Maybe,
+ None => TraitBoundModifier::None,
+ }
+ }
+}
+
/// Returns `true` if `IDENT t` can start a type -- `IDENT::a::b`, `IDENT<u8, u8>`,
/// `IDENT<<u8 as Trait>::AssocTy>`.
///
lo: Span,
parse_plus: bool,
) -> PResult<'a, TyKind> {
- let poly_trait_ref = PolyTraitRef::new(generic_params, path, lo.to(self.prev_span));
+ assert_ne!(self.token, token::Question);
+
+ let poly_trait_ref = PolyTraitRef::new(generic_params, path, None, lo.to(self.prev_span));
let mut bounds = vec![GenericBound::Trait(poly_trait_ref, TraitBoundModifier::None)];
if parse_plus {
self.eat_plus(); // `+`, or `+=` gets split and `+` is discarded
let has_parens = self.eat(&token::OpenDelim(token::Paren));
let inner_lo = self.token.span;
let is_negative = self.eat(&token::Not);
- let question = self.eat(&token::Question).then_some(self.prev_span);
+
+ let modifiers = self.parse_ty_bound_modifiers();
let bound = if self.token.is_lifetime() {
- self.parse_generic_lt_bound(lo, inner_lo, has_parens, question)?
+ self.error_lt_bound_with_modifiers(modifiers);
+ self.parse_generic_lt_bound(lo, inner_lo, has_parens)?
} else {
- self.parse_generic_ty_bound(lo, has_parens, question)?
+ self.parse_generic_ty_bound(lo, has_parens, modifiers)?
};
+
Ok(if is_negative { Err(anchor_lo.to(self.prev_span)) } else { Ok(bound) })
}
lo: Span,
inner_lo: Span,
has_parens: bool,
- question: Option<Span>,
) -> PResult<'a, GenericBound> {
- self.error_opt_out_lifetime(question);
let bound = GenericBound::Outlives(self.expect_lifetime());
if has_parens {
// FIXME(Centril): Consider not erroring here and accepting `('lt)` instead,
Ok(bound)
}
- fn error_opt_out_lifetime(&self, question: Option<Span>) {
- if let Some(span) = question {
+ /// Emits an error if any trait bound modifiers were present.
+ fn error_lt_bound_with_modifiers(&self, modifiers: BoundModifiers) {
+ if let Some(span) = modifiers.maybe_const {
+ self.struct_span_err(
+ span,
+ "`?const` may only modify trait bounds, not lifetime bounds",
+ )
+ .emit();
+ }
+
+ if let Some(span) = modifiers.maybe {
self.struct_span_err(span, "`?` may only modify trait bounds, not lifetime bounds")
.emit();
}
Ok(())
}
+ /// Parses the modifiers that may precede a trait in a bound, e.g. `?Trait` or `?const Trait`.
+ ///
+ /// If no modifiers are present, this does not consume any tokens.
+ ///
+ /// ```
+ /// TY_BOUND_MODIFIERS = "?" ["const" ["?"]]
+ /// ```
+ fn parse_ty_bound_modifiers(&mut self) -> BoundModifiers {
+ if !self.eat(&token::Question) {
+ return BoundModifiers { maybe: None, maybe_const: None };
+ }
+
+ // `? ...`
+ let first_question = self.prev_span;
+ if !self.eat_keyword(kw::Const) {
+ return BoundModifiers { maybe: Some(first_question), maybe_const: None };
+ }
+
+ // `?const ...`
+ let maybe_const = first_question.to(self.prev_span);
+ self.sess.gated_spans.gate(sym::const_trait_bound_opt_out, maybe_const);
+ if !self.eat(&token::Question) {
+ return BoundModifiers { maybe: None, maybe_const: Some(maybe_const) };
+ }
+
+ // `?const ? ...`
+ let second_question = self.prev_span;
+ BoundModifiers { maybe: Some(second_question), maybe_const: Some(maybe_const) }
+ }
+
/// Parses a type bound according to:
/// ```
/// TY_BOUND = TY_BOUND_NOPAREN | (TY_BOUND_NOPAREN)
- /// TY_BOUND_NOPAREN = [?] [for<LT_PARAM_DEFS>] SIMPLE_PATH (e.g., `?for<'a: 'b> m::Trait<'a>`)
+ /// TY_BOUND_NOPAREN = [TY_BOUND_MODIFIERS] [for<LT_PARAM_DEFS>] SIMPLE_PATH
/// ```
+ ///
+ /// For example, this grammar accepts `?const ?for<'a: 'b> m::Trait<'a>`.
fn parse_generic_ty_bound(
&mut self,
lo: Span,
has_parens: bool,
- question: Option<Span>,
+ modifiers: BoundModifiers,
) -> PResult<'a, GenericBound> {
let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
let path = self.parse_path(PathStyle::Type)?;
if has_parens {
self.expect(&token::CloseDelim(token::Paren))?;
}
- let poly_trait = PolyTraitRef::new(lifetime_defs, path, lo.to(self.prev_span));
- let modifier = question.map_or(TraitBoundModifier::None, |_| TraitBoundModifier::Maybe);
- Ok(GenericBound::Trait(poly_trait, modifier))
+
+ let constness = modifiers.maybe_const.map(|_| ast::Constness::NotConst);
+ let poly_trait = PolyTraitRef::new(lifetime_defs, path, constness, lo.to(self.prev_span));
+ Ok(GenericBound::Trait(poly_trait, modifiers.trait_bound_modifier()))
}
/// Optionally parses `for<$generic_params>`.
use rustc_errors::{Applicability, PResult};
use rustc_feature::{AttributeTemplate, BUILTIN_ATTRIBUTE_MAP};
+use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT;
+use rustc_session::parse::ParseSess;
use rustc_span::{sym, Symbol};
use syntax::ast::{self, Attribute, MacArgs, MacDelimiter, MetaItem, MetaItemKind};
-use syntax::early_buffered_lints::ILL_FORMED_ATTRIBUTE_INPUT;
-use syntax::sess::ParseSess;
use syntax::tokenstream::DelimSpan;
pub fn check_meta(sess: &ParseSess, attr: &Attribute) {
log = "0.4"
rustc = { path = "../librustc" }
rustc_data_structures = { path = "../librustc_data_structures" }
+rustc_errors = { path = "../librustc_errors" }
rustc_feature = { path = "../librustc_feature" }
rustc_hir = { path = "../librustc_hir" }
rustc_index = { path = "../librustc_index" }
-rustc_parse = { path = "../librustc_parse" }
+rustc_session = { path = "../librustc_session" }
rustc_target = { path = "../librustc_target" }
syntax = { path = "../libsyntax" }
rustc_span = { path = "../librustc_span" }
-errors = { path = "../librustc_errors", package = "rustc_errors" }
rustc_error_codes = { path = "../librustc_error_codes" }
+++ /dev/null
-// Validate AST before lowering it to HIR.
-//
-// This pass is supposed to catch things that fit into AST data structures,
-// but not permitted by the language. It runs after expansion when AST is frozen,
-// so it can check for erroneous constructions produced by syntax extensions.
-// This pass is supposed to perform only simple checks not requiring name resolution
-// or type checking or some other kind of complex analysis.
-
-use errors::{Applicability, FatalError};
-use rustc::lint;
-use rustc::session::Session;
-use rustc_data_structures::fx::FxHashMap;
-use rustc_parse::validate_attr;
-use rustc_span::source_map::Spanned;
-use rustc_span::symbol::{kw, sym};
-use rustc_span::Span;
-use std::mem;
-use syntax::ast::*;
-use syntax::attr;
-use syntax::expand::is_proc_macro_attr;
-use syntax::print::pprust;
-use syntax::visit::{self, Visitor};
-use syntax::{span_err, struct_span_err, walk_list};
-
-use rustc_error_codes::*;
-
-struct AstValidator<'a> {
- session: &'a Session,
- has_proc_macro_decls: bool,
-
- /// Used to ban nested `impl Trait`, e.g., `impl Into<impl Debug>`.
- /// Nested `impl Trait` _is_ allowed in associated type position,
- /// e.g., `impl Iterator<Item = impl Debug>`.
- outer_impl_trait: Option<Span>,
-
- /// Used to ban `impl Trait` in path projections like `<impl Iterator>::Item`
- /// or `Foo::Bar<impl Trait>`
- is_impl_trait_banned: bool,
-
- /// Used to ban associated type bounds (i.e., `Type<AssocType: Bounds>`) in
- /// certain positions.
- is_assoc_ty_bound_banned: bool,
-
- lint_buffer: &'a mut lint::LintBuffer,
-}
-
-impl<'a> AstValidator<'a> {
- fn with_banned_impl_trait(&mut self, f: impl FnOnce(&mut Self)) {
- let old = mem::replace(&mut self.is_impl_trait_banned, true);
- f(self);
- self.is_impl_trait_banned = old;
- }
-
- fn with_banned_assoc_ty_bound(&mut self, f: impl FnOnce(&mut Self)) {
- let old = mem::replace(&mut self.is_assoc_ty_bound_banned, true);
- f(self);
- self.is_assoc_ty_bound_banned = old;
- }
-
- fn with_impl_trait(&mut self, outer: Option<Span>, f: impl FnOnce(&mut Self)) {
- let old = mem::replace(&mut self.outer_impl_trait, outer);
- f(self);
- self.outer_impl_trait = old;
- }
-
- fn visit_assoc_ty_constraint_from_generic_args(&mut self, constraint: &'a AssocTyConstraint) {
- match constraint.kind {
- AssocTyConstraintKind::Equality { .. } => {}
- AssocTyConstraintKind::Bound { .. } => {
- if self.is_assoc_ty_bound_banned {
- self.err_handler().span_err(
- constraint.span,
- "associated type bounds are not allowed within structs, enums, or unions",
- );
- }
- }
- }
- self.visit_assoc_ty_constraint(constraint);
- }
-
- // Mirrors `visit::walk_ty`, but tracks relevant state.
- fn walk_ty(&mut self, t: &'a Ty) {
- match t.kind {
- TyKind::ImplTrait(..) => {
- self.with_impl_trait(Some(t.span), |this| visit::walk_ty(this, t))
- }
- TyKind::Path(ref qself, ref path) => {
- // We allow these:
- // - `Option<impl Trait>`
- // - `option::Option<impl Trait>`
- // - `option::Option<T>::Foo<impl Trait>
- //
- // But not these:
- // - `<impl Trait>::Foo`
- // - `option::Option<impl Trait>::Foo`.
- //
- // To implement this, we disallow `impl Trait` from `qself`
- // (for cases like `<impl Trait>::Foo>`)
- // but we allow `impl Trait` in `GenericArgs`
- // iff there are no more PathSegments.
- if let Some(ref qself) = *qself {
- // `impl Trait` in `qself` is always illegal
- self.with_banned_impl_trait(|this| this.visit_ty(&qself.ty));
- }
-
- // Note that there should be a call to visit_path here,
- // so if any logic is added to process `Path`s a call to it should be
- // added both in visit_path and here. This code mirrors visit::walk_path.
- for (i, segment) in path.segments.iter().enumerate() {
- // Allow `impl Trait` iff we're on the final path segment
- if i == path.segments.len() - 1 {
- self.visit_path_segment(path.span, segment);
- } else {
- self.with_banned_impl_trait(|this| {
- this.visit_path_segment(path.span, segment)
- });
- }
- }
- }
- _ => visit::walk_ty(self, t),
- }
- }
-
- fn err_handler(&self) -> &errors::Handler {
- &self.session.diagnostic()
- }
-
- fn check_lifetime(&self, ident: Ident) {
- let valid_names = [kw::UnderscoreLifetime, kw::StaticLifetime, kw::Invalid];
- if !valid_names.contains(&ident.name) && ident.without_first_quote().is_reserved() {
- self.err_handler().span_err(ident.span, "lifetimes cannot use keyword names");
- }
- }
-
- fn check_label(&self, ident: Ident) {
- if ident.without_first_quote().is_reserved() {
- self.err_handler()
- .span_err(ident.span, &format!("invalid label name `{}`", ident.name));
- }
- }
-
- fn invalid_visibility(&self, vis: &Visibility, note: Option<&str>) {
- if let VisibilityKind::Inherited = vis.node {
- return;
- }
-
- let mut err =
- struct_span_err!(self.session, vis.span, E0449, "unnecessary visibility qualifier");
- if vis.node.is_pub() {
- err.span_label(vis.span, "`pub` not permitted here because it's implied");
- }
- if let Some(note) = note {
- err.note(note);
- }
- err.emit();
- }
-
- fn check_decl_no_pat(decl: &FnDecl, mut report_err: impl FnMut(Span, bool)) {
- for Param { pat, .. } in &decl.inputs {
- match pat.kind {
- PatKind::Ident(BindingMode::ByValue(Mutability::Not), _, None) | PatKind::Wild => {}
- PatKind::Ident(BindingMode::ByValue(Mutability::Mut), _, None) => {
- report_err(pat.span, true)
- }
- _ => report_err(pat.span, false),
- }
- }
- }
-
- fn check_trait_fn_not_async(&self, span: Span, asyncness: IsAsync) {
- if asyncness.is_async() {
- struct_span_err!(self.session, span, E0706, "trait fns cannot be declared `async`")
- .note("`async` trait functions are not currently supported")
- .note(
- "consider using the `async-trait` crate: \
- https://crates.io/crates/async-trait",
- )
- .emit();
- }
- }
-
- fn check_trait_fn_not_const(&self, constness: Spanned<Constness>) {
- if constness.node == Constness::Const {
- struct_span_err!(
- self.session,
- constness.span,
- E0379,
- "trait fns cannot be declared const"
- )
- .span_label(constness.span, "trait fns cannot be const")
- .emit();
- }
- }
-
- fn no_questions_in_bounds(&self, bounds: &GenericBounds, where_: &str, is_trait: bool) {
- for bound in bounds {
- if let GenericBound::Trait(ref poly, TraitBoundModifier::Maybe) = *bound {
- let mut err = self.err_handler().struct_span_err(
- poly.span,
- &format!("`?Trait` is not permitted in {}", where_),
- );
- if is_trait {
- let path_str = pprust::path_to_string(&poly.trait_ref.path);
- err.note(&format!("traits are `?{}` by default", path_str));
- }
- err.emit();
- }
- }
- }
-
- /// Matches `'-' lit | lit (cf. parser::Parser::parse_literal_maybe_minus)`,
- /// or paths for ranges.
- //
- // FIXME: do we want to allow `expr -> pattern` conversion to create path expressions?
- // That means making this work:
- //
- // ```rust,ignore (FIXME)
- // struct S;
- // macro_rules! m {
- // ($a:expr) => {
- // let $a = S;
- // }
- // }
- // m!(S);
- // ```
- fn check_expr_within_pat(&self, expr: &Expr, allow_paths: bool) {
- match expr.kind {
- ExprKind::Lit(..) | ExprKind::Err => {}
- ExprKind::Path(..) if allow_paths => {}
- ExprKind::Unary(UnOp::Neg, ref inner)
- if match inner.kind {
- ExprKind::Lit(_) => true,
- _ => false,
- } => {}
- _ => self.err_handler().span_err(
- expr.span,
- "arbitrary expressions aren't allowed \
- in patterns",
- ),
- }
- }
-
- fn check_late_bound_lifetime_defs(&self, params: &[GenericParam]) {
- // Check only lifetime parameters are present and that the lifetime
- // parameters that are present have no bounds.
- let non_lt_param_spans: Vec<_> = params
- .iter()
- .filter_map(|param| match param.kind {
- GenericParamKind::Lifetime { .. } => {
- if !param.bounds.is_empty() {
- let spans: Vec<_> = param.bounds.iter().map(|b| b.span()).collect();
- self.err_handler()
- .span_err(spans, "lifetime bounds cannot be used in this context");
- }
- None
- }
- _ => Some(param.ident.span),
- })
- .collect();
- if !non_lt_param_spans.is_empty() {
- self.err_handler().span_err(
- non_lt_param_spans,
- "only lifetime parameters can be used in this context",
- );
- }
- }
-
- fn check_fn_decl(&self, fn_decl: &FnDecl) {
- match &*fn_decl.inputs {
- [Param { ty, span, .. }] => {
- if let TyKind::CVarArgs = ty.kind {
- self.err_handler().span_err(
- *span,
- "C-variadic function must be declared with at least one named argument",
- );
- }
- }
- [ps @ .., _] => {
- for Param { ty, span, .. } in ps {
- if let TyKind::CVarArgs = ty.kind {
- self.err_handler().span_err(
- *span,
- "`...` must be the last argument of a C-variadic function",
- );
- }
- }
- }
- _ => {}
- }
-
- fn_decl
- .inputs
- .iter()
- .flat_map(|i| i.attrs.as_ref())
- .filter(|attr| {
- let arr = [sym::allow, sym::cfg, sym::cfg_attr, sym::deny, sym::forbid, sym::warn];
- !arr.contains(&attr.name_or_empty()) && attr::is_builtin_attr(attr)
- })
- .for_each(|attr| {
- if attr.is_doc_comment() {
- self.err_handler()
- .struct_span_err(
- attr.span,
- "documentation comments cannot be applied to function parameters",
- )
- .span_label(attr.span, "doc comments are not allowed here")
- .emit();
- } else {
- self.err_handler().span_err(
- attr.span,
- "allow, cfg, cfg_attr, deny, \
- forbid, and warn are the only allowed built-in attributes in function parameters",
- )
- }
- });
- }
-
- fn check_defaultness(&self, span: Span, defaultness: Defaultness) {
- if let Defaultness::Default = defaultness {
- self.err_handler()
- .struct_span_err(span, "`default` is only allowed on items in `impl` definitions")
- .emit();
- }
- }
-
- fn check_impl_item_provided<T>(&self, sp: Span, body: &Option<T>, ctx: &str, sugg: &str) {
- if body.is_some() {
- return;
- }
-
- self.err_handler()
- .struct_span_err(sp, &format!("associated {} in `impl` without body", ctx))
- .span_suggestion(
- self.session.source_map().end_point(sp),
- &format!("provide a definition for the {}", ctx),
- sugg.to_string(),
- Applicability::HasPlaceholders,
- )
- .emit();
- }
-
- fn check_impl_assoc_type_no_bounds(&self, bounds: &[GenericBound]) {
- let span = match bounds {
- [] => return,
- [b0] => b0.span(),
- [b0, .., bl] => b0.span().to(bl.span()),
- };
- self.err_handler()
- .struct_span_err(span, "bounds on associated `type`s in `impl`s have no effect")
- .emit();
- }
-
- fn check_c_varadic_type(&self, decl: &FnDecl) {
- for Param { ty, span, .. } in &decl.inputs {
- if let TyKind::CVarArgs = ty.kind {
- self.err_handler()
- .struct_span_err(
- *span,
- "only foreign or `unsafe extern \"C\" functions may be C-variadic",
- )
- .emit();
- }
- }
- }
-}
-
-enum GenericPosition {
- Param,
- Arg,
-}
-
-fn validate_generics_order<'a>(
- sess: &Session,
- handler: &errors::Handler,
- generics: impl Iterator<Item = (ParamKindOrd, Option<&'a [GenericBound]>, Span, Option<String>)>,
- pos: GenericPosition,
- span: Span,
-) {
- let mut max_param: Option<ParamKindOrd> = None;
- let mut out_of_order = FxHashMap::default();
- let mut param_idents = vec![];
- let mut found_type = false;
- let mut found_const = false;
-
- for (kind, bounds, span, ident) in generics {
- if let Some(ident) = ident {
- param_idents.push((kind, bounds, param_idents.len(), ident));
- }
- let max_param = &mut max_param;
- match max_param {
- Some(max_param) if *max_param > kind => {
- let entry = out_of_order.entry(kind).or_insert((*max_param, vec![]));
- entry.1.push(span);
- }
- Some(_) | None => *max_param = Some(kind),
- };
- match kind {
- ParamKindOrd::Type => found_type = true,
- ParamKindOrd::Const => found_const = true,
- _ => {}
- }
- }
-
- let mut ordered_params = "<".to_string();
- if !out_of_order.is_empty() {
- param_idents.sort_by_key(|&(po, _, i, _)| (po, i));
- let mut first = true;
- for (_, bounds, _, ident) in param_idents {
- if !first {
- ordered_params += ", ";
- }
- ordered_params += &ident;
- if let Some(bounds) = bounds {
- if !bounds.is_empty() {
- ordered_params += ": ";
- ordered_params += &pprust::bounds_to_string(&bounds);
- }
- }
- first = false;
- }
- }
- ordered_params += ">";
-
- let pos_str = match pos {
- GenericPosition::Param => "parameter",
- GenericPosition::Arg => "argument",
- };
-
- for (param_ord, (max_param, spans)) in &out_of_order {
- let mut err = handler.struct_span_err(
- spans.clone(),
- &format!(
- "{} {pos}s must be declared prior to {} {pos}s",
- param_ord,
- max_param,
- pos = pos_str,
- ),
- );
- if let GenericPosition::Param = pos {
- err.span_suggestion(
- span,
- &format!(
- "reorder the {}s: lifetimes, then types{}",
- pos_str,
- if sess.features_untracked().const_generics { ", then consts" } else { "" },
- ),
- ordered_params.clone(),
- Applicability::MachineApplicable,
- );
- }
- err.emit();
- }
-
- // FIXME(const_generics): we shouldn't have to abort here at all, but we currently get ICEs
- // if we don't. Const parameters and type parameters can currently conflict if they
- // are out-of-order.
- if !out_of_order.is_empty() && found_type && found_const {
- FatalError.raise();
- }
-}
-
-impl<'a> Visitor<'a> for AstValidator<'a> {
- fn visit_attribute(&mut self, attr: &Attribute) {
- validate_attr::check_meta(&self.session.parse_sess, attr);
- }
-
- fn visit_expr(&mut self, expr: &'a Expr) {
- match &expr.kind {
- ExprKind::Closure(_, _, _, fn_decl, _, _) => {
- self.check_fn_decl(fn_decl);
- }
- ExprKind::InlineAsm(..) if !self.session.target.target.options.allow_asm => {
- span_err!(self.session, expr.span, E0472, "asm! is unsupported on this target");
- }
- _ => {}
- }
-
- visit::walk_expr(self, expr);
- }
-
- fn visit_ty(&mut self, ty: &'a Ty) {
- match ty.kind {
- TyKind::BareFn(ref bfty) => {
- self.check_fn_decl(&bfty.decl);
- Self::check_decl_no_pat(&bfty.decl, |span, _| {
- struct_span_err!(
- self.session,
- span,
- E0561,
- "patterns aren't allowed in function pointer types"
- )
- .emit();
- });
- self.check_late_bound_lifetime_defs(&bfty.generic_params);
- }
- TyKind::TraitObject(ref bounds, ..) => {
- let mut any_lifetime_bounds = false;
- for bound in bounds {
- if let GenericBound::Outlives(ref lifetime) = *bound {
- if any_lifetime_bounds {
- span_err!(
- self.session,
- lifetime.ident.span,
- E0226,
- "only a single explicit lifetime bound is permitted"
- );
- break;
- }
- any_lifetime_bounds = true;
- }
- }
- self.no_questions_in_bounds(bounds, "trait object types", false);
- }
- TyKind::ImplTrait(_, ref bounds) => {
- if self.is_impl_trait_banned {
- struct_span_err!(
- self.session,
- ty.span,
- E0667,
- "`impl Trait` is not allowed in path parameters"
- )
- .emit();
- }
-
- if let Some(outer_impl_trait_sp) = self.outer_impl_trait {
- struct_span_err!(
- self.session,
- ty.span,
- E0666,
- "nested `impl Trait` is not allowed"
- )
- .span_label(outer_impl_trait_sp, "outer `impl Trait`")
- .span_label(ty.span, "nested `impl Trait` here")
- .emit();
- }
-
- if !bounds
- .iter()
- .any(|b| if let GenericBound::Trait(..) = *b { true } else { false })
- {
- self.err_handler().span_err(ty.span, "at least one trait must be specified");
- }
-
- self.walk_ty(ty);
- return;
- }
- _ => {}
- }
-
- self.walk_ty(ty)
- }
-
- fn visit_label(&mut self, label: &'a Label) {
- self.check_label(label.ident);
- visit::walk_label(self, label);
- }
-
- fn visit_lifetime(&mut self, lifetime: &'a Lifetime) {
- self.check_lifetime(lifetime.ident);
- visit::walk_lifetime(self, lifetime);
- }
-
- fn visit_item(&mut self, item: &'a Item) {
- if item.attrs.iter().any(|attr| is_proc_macro_attr(attr)) {
- self.has_proc_macro_decls = true;
- }
-
- match item.kind {
- ItemKind::Impl(unsafety, polarity, _, _, Some(..), ref ty, ref impl_items) => {
- self.invalid_visibility(&item.vis, None);
- if let TyKind::Err = ty.kind {
- self.err_handler()
- .struct_span_err(item.span, "`impl Trait for .. {}` is an obsolete syntax")
- .help("use `auto trait Trait {}` instead")
- .emit();
- }
- if unsafety == Unsafety::Unsafe && polarity == ImplPolarity::Negative {
- span_err!(self.session, item.span, E0198, "negative impls cannot be unsafe");
- }
- for impl_item in impl_items {
- self.invalid_visibility(&impl_item.vis, None);
- if let AssocItemKind::Fn(ref sig, _) = impl_item.kind {
- self.check_trait_fn_not_const(sig.header.constness);
- self.check_trait_fn_not_async(impl_item.span, sig.header.asyncness.node);
- }
- }
- }
- ItemKind::Impl(unsafety, polarity, defaultness, _, None, _, _) => {
- self.invalid_visibility(
- &item.vis,
- Some("place qualifiers on individual impl items instead"),
- );
- if unsafety == Unsafety::Unsafe {
- span_err!(self.session, item.span, E0197, "inherent impls cannot be unsafe");
- }
- if polarity == ImplPolarity::Negative {
- self.err_handler().span_err(item.span, "inherent impls cannot be negative");
- }
- if defaultness == Defaultness::Default {
- self.err_handler()
- .struct_span_err(item.span, "inherent impls cannot be default")
- .note("only trait implementations may be annotated with default")
- .emit();
- }
- }
- ItemKind::Fn(ref sig, ref generics, _) => {
- self.visit_fn_header(&sig.header);
- self.check_fn_decl(&sig.decl);
- // We currently do not permit const generics in `const fn`, as
- // this is tantamount to allowing compile-time dependent typing.
- if sig.header.constness.node == Constness::Const {
- // Look for const generics and error if we find any.
- for param in &generics.params {
- match param.kind {
- GenericParamKind::Const { .. } => {
- self.err_handler()
- .struct_span_err(
- item.span,
- "const parameters are not permitted in `const fn`",
- )
- .emit();
- }
- _ => {}
- }
- }
- }
- // Reject C-varadic type unless the function is `unsafe extern "C"` semantically.
- match sig.header.ext {
- Extern::Explicit(StrLit { symbol_unescaped: sym::C, .. })
- | Extern::Implicit
- if sig.header.unsafety == Unsafety::Unsafe => {}
- _ => self.check_c_varadic_type(&sig.decl),
- }
- }
- ItemKind::ForeignMod(..) => {
- self.invalid_visibility(
- &item.vis,
- Some("place qualifiers on individual foreign items instead"),
- );
- }
- ItemKind::Enum(ref def, _) => {
- for variant in &def.variants {
- self.invalid_visibility(&variant.vis, None);
- for field in variant.data.fields() {
- self.invalid_visibility(&field.vis, None);
- }
- }
- }
- ItemKind::Trait(is_auto, _, ref generics, ref bounds, ref trait_items) => {
- if is_auto == IsAuto::Yes {
- // Auto traits cannot have generics, super traits nor contain items.
- if !generics.params.is_empty() {
- struct_span_err!(
- self.session,
- item.span,
- E0567,
- "auto traits cannot have generic parameters"
- )
- .emit();
- }
- if !bounds.is_empty() {
- struct_span_err!(
- self.session,
- item.span,
- E0568,
- "auto traits cannot have super traits"
- )
- .emit();
- }
- if !trait_items.is_empty() {
- struct_span_err!(
- self.session,
- item.span,
- E0380,
- "auto traits cannot have methods or associated items"
- )
- .emit();
- }
- }
- self.no_questions_in_bounds(bounds, "supertraits", true);
- }
- ItemKind::Mod(_) => {
- // Ensure that `path` attributes on modules are recorded as used (cf. issue #35584).
- attr::first_attr_value_str_by_name(&item.attrs, sym::path);
- }
- ItemKind::Union(ref vdata, _) => {
- if let VariantData::Tuple(..) | VariantData::Unit(..) = vdata {
- self.err_handler()
- .span_err(item.span, "tuple and unit unions are not permitted");
- }
- if vdata.fields().is_empty() {
- self.err_handler().span_err(item.span, "unions cannot have zero fields");
- }
- }
- _ => {}
- }
-
- visit::walk_item(self, item)
- }
-
- fn visit_foreign_item(&mut self, fi: &'a ForeignItem) {
- match fi.kind {
- ForeignItemKind::Fn(ref decl, _) => {
- self.check_fn_decl(decl);
- Self::check_decl_no_pat(decl, |span, _| {
- struct_span_err!(
- self.session,
- span,
- E0130,
- "patterns aren't allowed in foreign function declarations"
- )
- .span_label(span, "pattern not allowed in foreign function")
- .emit();
- });
- }
- ForeignItemKind::Static(..) | ForeignItemKind::Ty | ForeignItemKind::Macro(..) => {}
- }
-
- visit::walk_foreign_item(self, fi)
- }
-
- // Mirrors `visit::walk_generic_args`, but tracks relevant state.
- fn visit_generic_args(&mut self, _: Span, generic_args: &'a GenericArgs) {
- match *generic_args {
- GenericArgs::AngleBracketed(ref data) => {
- walk_list!(self, visit_generic_arg, &data.args);
- validate_generics_order(
- self.session,
- self.err_handler(),
- data.args.iter().map(|arg| {
- (
- match arg {
- GenericArg::Lifetime(..) => ParamKindOrd::Lifetime,
- GenericArg::Type(..) => ParamKindOrd::Type,
- GenericArg::Const(..) => ParamKindOrd::Const,
- },
- None,
- arg.span(),
- None,
- )
- }),
- GenericPosition::Arg,
- generic_args.span(),
- );
-
- // Type bindings such as `Item = impl Debug` in `Iterator<Item = Debug>`
- // are allowed to contain nested `impl Trait`.
- self.with_impl_trait(None, |this| {
- walk_list!(
- this,
- visit_assoc_ty_constraint_from_generic_args,
- &data.constraints
- );
- });
- }
- GenericArgs::Parenthesized(ref data) => {
- walk_list!(self, visit_ty, &data.inputs);
- if let FunctionRetTy::Ty(ty) = &data.output {
- // `-> Foo` syntax is essentially an associated type binding,
- // so it is also allowed to contain nested `impl Trait`.
- self.with_impl_trait(None, |this| this.visit_ty(ty));
- }
- }
- }
- }
-
- fn visit_generics(&mut self, generics: &'a Generics) {
- let mut prev_ty_default = None;
- for param in &generics.params {
- if let GenericParamKind::Type { ref default, .. } = param.kind {
- if default.is_some() {
- prev_ty_default = Some(param.ident.span);
- } else if let Some(span) = prev_ty_default {
- self.err_handler()
- .span_err(span, "type parameters with a default must be trailing");
- break;
- }
- }
- }
-
- validate_generics_order(
- self.session,
- self.err_handler(),
- generics.params.iter().map(|param| {
- let ident = Some(param.ident.to_string());
- let (kind, ident) = match ¶m.kind {
- GenericParamKind::Lifetime { .. } => (ParamKindOrd::Lifetime, ident),
- GenericParamKind::Type { .. } => (ParamKindOrd::Type, ident),
- GenericParamKind::Const { ref ty } => {
- let ty = pprust::ty_to_string(ty);
- (ParamKindOrd::Const, Some(format!("const {}: {}", param.ident, ty)))
- }
- };
- (kind, Some(&*param.bounds), param.ident.span, ident)
- }),
- GenericPosition::Param,
- generics.span,
- );
-
- for predicate in &generics.where_clause.predicates {
- if let WherePredicate::EqPredicate(ref predicate) = *predicate {
- self.err_handler()
- .struct_span_err(
- predicate.span,
- "equality constraints are not yet supported in `where` clauses",
- )
- .span_label(predicate.span, "not supported")
- .note(
- "for more information, see https://github.com/rust-lang/rust/issues/20041",
- )
- .emit();
- }
- }
-
- visit::walk_generics(self, generics)
- }
-
- fn visit_generic_param(&mut self, param: &'a GenericParam) {
- if let GenericParamKind::Lifetime { .. } = param.kind {
- self.check_lifetime(param.ident);
- }
- visit::walk_generic_param(self, param);
- }
-
- fn visit_pat(&mut self, pat: &'a Pat) {
- match pat.kind {
- PatKind::Lit(ref expr) => {
- self.check_expr_within_pat(expr, false);
- }
- PatKind::Range(ref start, ref end, _) => {
- self.check_expr_within_pat(start, true);
- self.check_expr_within_pat(end, true);
- }
- _ => {}
- }
-
- visit::walk_pat(self, pat)
- }
-
- fn visit_where_predicate(&mut self, p: &'a WherePredicate) {
- if let &WherePredicate::BoundPredicate(ref bound_predicate) = p {
- // A type binding, eg `for<'c> Foo: Send+Clone+'c`
- self.check_late_bound_lifetime_defs(&bound_predicate.bound_generic_params);
- }
- visit::walk_where_predicate(self, p);
- }
-
- fn visit_poly_trait_ref(&mut self, t: &'a PolyTraitRef, m: &'a TraitBoundModifier) {
- self.check_late_bound_lifetime_defs(&t.bound_generic_params);
- visit::walk_poly_trait_ref(self, t, m);
- }
-
- fn visit_variant_data(&mut self, s: &'a VariantData) {
- self.with_banned_assoc_ty_bound(|this| visit::walk_struct_def(this, s))
- }
-
- fn visit_enum_def(
- &mut self,
- enum_definition: &'a EnumDef,
- generics: &'a Generics,
- item_id: NodeId,
- _: Span,
- ) {
- self.with_banned_assoc_ty_bound(|this| {
- visit::walk_enum_def(this, enum_definition, generics, item_id)
- })
- }
-
- fn visit_impl_item(&mut self, ii: &'a AssocItem) {
- match &ii.kind {
- AssocItemKind::Const(_, body) => {
- self.check_impl_item_provided(ii.span, body, "constant", " = <expr>;");
- }
- AssocItemKind::Fn(sig, body) => {
- self.check_impl_item_provided(ii.span, body, "function", " { <body> }");
- self.check_fn_decl(&sig.decl);
- }
- AssocItemKind::TyAlias(bounds, body) => {
- self.check_impl_item_provided(ii.span, body, "type", " = <type>;");
- self.check_impl_assoc_type_no_bounds(bounds);
- }
- _ => {}
- }
- visit::walk_impl_item(self, ii);
- }
-
- fn visit_trait_item(&mut self, ti: &'a AssocItem) {
- self.invalid_visibility(&ti.vis, None);
- self.check_defaultness(ti.span, ti.defaultness);
-
- if let AssocItemKind::Fn(sig, block) = &ti.kind {
- self.check_fn_decl(&sig.decl);
- self.check_trait_fn_not_async(ti.span, sig.header.asyncness.node);
- self.check_trait_fn_not_const(sig.header.constness);
- if block.is_none() {
- Self::check_decl_no_pat(&sig.decl, |span, mut_ident| {
- if mut_ident {
- self.lint_buffer.buffer_lint(
- lint::builtin::PATTERNS_IN_FNS_WITHOUT_BODY,
- ti.id,
- span,
- "patterns aren't allowed in methods without bodies",
- );
- } else {
- struct_span_err!(
- self.session,
- span,
- E0642,
- "patterns aren't allowed in methods without bodies"
- )
- .emit();
- }
- });
- }
- }
-
- visit::walk_trait_item(self, ti);
- }
-
- fn visit_assoc_item(&mut self, item: &'a AssocItem) {
- if let AssocItemKind::Fn(sig, _) = &item.kind {
- self.check_c_varadic_type(&sig.decl);
- }
- visit::walk_assoc_item(self, item);
- }
-}
-
-pub fn check_crate(session: &Session, krate: &Crate, lints: &mut lint::LintBuffer) -> bool {
- let mut validator = AstValidator {
- session,
- has_proc_macro_decls: false,
- outer_impl_trait: None,
- is_impl_trait_banned: false,
- is_assoc_ty_bound_banned: false,
- lint_buffer: lints,
- };
- visit::walk_crate(&mut validator, krate);
-
- validator.has_proc_macro_decls
-}
//! errors. We still look for those primitives in the MIR const-checker to ensure nothing slips
//! through, but errors for structured control flow in a `const` should be emitted here.
-use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc::hir::map::Map;
use rustc::session::config::nightly_options;
+use rustc::session::parse::feature_err;
use rustc::ty::query::Providers;
use rustc::ty::TyCtxt;
use rustc_error_codes::*;
+use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_span::{sym, Span, Symbol};
use syntax::ast::Mutability;
-use syntax::feature_gate::feature_err;
-use syntax::span_err;
use std::fmt;
required_gates.iter().copied().filter(|&g| !features.enabled(g)).collect();
match missing_gates.as_slice() {
- &[] => span_err!(self.tcx.sess, span, E0744, "{}", msg),
+ &[] => struct_span_err!(self.tcx.sess, span, E0744, "{}", msg).emit(),
// If the user enabled `#![feature(const_loop)]` but not `#![feature(const_if_match)]`,
// explain why their `while` loop is being rejected.
}
impl<'tcx> Visitor<'tcx> for CheckConstVisitor<'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<'_, Self::Map> {
NestedVisitorMap::OnlyBodies(&self.tcx.hir())
}
// closely. The idea is that all reachable symbols are live, codes called
// from live codes are live, and everything else is dead.
-use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor};
-use rustc::lint;
+use rustc::hir::map::Map;
use rustc::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc::middle::privacy;
use rustc::ty::{self, DefIdTree, TyCtxt};
use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind, Res};
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_hir::{Node, PatKind, TyKind};
+use rustc_session::lint;
use rustc_span;
use rustc_span::symbol::sym;
}
impl<'a, 'tcx> Visitor<'tcx> for MarkSymbolVisitor<'a, 'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<'_, Self::Map> {
NestedVisitorMap::None
}
}
impl Visitor<'tcx> for DeadVisitor<'tcx> {
+ type Map = Map<'tcx>;
+
/// Walk nested items in place so that we don't report dead-code
/// on inner functions when the outer function is already getting
/// an error. We could do this also by checking the parents, but
/// this is how the code is setup and it seems harmless enough.
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> {
NestedVisitorMap::All(&self.tcx.hir())
}
)),
};
if let Some(span) = tcx.hir().span_if_local(original_def_id) {
- span_note!(&mut err, span, "first defined here.");
+ err.span_note(span, "first defined here.");
} else {
err.note(&format!(
"first defined in crate `{}`.",
use rustc::session::{config, Session};
use rustc::ty::query::Providers;
use rustc::ty::TyCtxt;
+use rustc_errors::struct_span_err;
use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_hir::{HirId, ImplItem, Item, ItemKind, TraitItem};
use rustc_span::symbol::sym;
-use rustc_span::Span;
+use rustc_span::{Span, DUMMY_SP};
use syntax::attr;
use syntax::entry::EntryPointType;
if ctxt.main_fn.is_none() {
ctxt.main_fn = Some((item.hir_id, item.span));
} else {
- span_err!(ctxt.session, item.span, E0136, "multiple `main` functions");
+ struct_span_err!(ctxt.session, item.span, E0136, "multiple `main` functions")
+ .emit();
}
}
EntryPointType::OtherMain => {
}
// There is no main function.
- let mut err = struct_err!(
+ let mut err = struct_span_err!(
tcx.sess,
+ DUMMY_SP,
E0601,
"`main` function not found in crate `{}`",
tcx.crate_name(LOCAL_CRATE)
// pieces of AST and HIR. The resulting numbers are good approximations but not
// completely accurate (some things might be counted twice, others missed).
-use rustc::hir::intravisit as hir_visit;
+use rustc::hir::map::Map;
use rustc::util::common::to_readable_str;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir as hir;
+use rustc_hir::intravisit as hir_visit;
use rustc_hir::HirId;
use rustc_span::Span;
use syntax::ast::{self, AttrId, NodeId};
hir_visit::walk_param(self, param)
}
- fn nested_visit_map<'this>(&'this mut self) -> hir_visit::NestedVisitorMap<'this, 'v> {
+ type Map = Map<'v>;
+
+ fn nested_visit_map(&mut self) -> hir_visit::NestedVisitorMap<'_, Self::Map> {
panic!("visit_nested_xxx must be manually implemented in this visitor")
}
+use rustc::hir::map::Map;
use rustc::ty::layout::{LayoutError, Pointer, SizeSkeleton, VariantIdx};
use rustc::ty::query::Providers;
use rustc::ty::{self, Ty, TyCtxt};
+use rustc_errors::struct_span_err;
+use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
-
-use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor};
-use rustc_hir as hir;
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_index::vec::Idx;
use rustc_span::{sym, Span};
use rustc_target::spec::abi::Abi::RustIntrinsic;
}
impl Visitor<'tcx> for ItemVisitor<'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> {
NestedVisitorMap::None
}
}
impl Visitor<'tcx> for ExprVisitor<'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> {
NestedVisitorMap::None
}
extern crate rustc;
#[macro_use]
extern crate log;
-#[macro_use]
-extern crate syntax;
use rustc::ty::query::Providers;
-pub mod ast_validation;
mod check_const;
pub mod dead;
mod diagnostic_items;
// and `#[unstable (..)]`), but are not declared in one single location
// (unlike lang features), which means we need to collect them instead.
-use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor};
+use rustc::hir::map::Map;
use rustc::middle::lib_features::LibFeatures;
use rustc::ty::query::Providers;
use rustc::ty::TyCtxt;
+use rustc_errors::struct_span_err;
use rustc_hir::def_id::LOCAL_CRATE;
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_span::symbol::Symbol;
use rustc_span::{sym, Span};
use syntax::ast::{Attribute, MetaItem, MetaItemKind};
}
impl Visitor<'tcx> for LibFeatureCollector<'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> {
NestedVisitorMap::All(&self.tcx.hir())
}
use self::LiveNodeKind::*;
use self::VarKind::*;
-use errors::Applicability;
-use rustc::hir::intravisit::{self, FnKind, NestedVisitorMap, Visitor};
+use rustc::hir::map::Map;
use rustc::lint;
use rustc::ty::query::Providers;
use rustc::ty::{self, TyCtxt};
use rustc_data_structures::fx::FxIndexMap;
+use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::def::*;
use rustc_hir::def_id::DefId;
+use rustc_hir::intravisit::{self, FnKind, NestedVisitorMap, Visitor};
use rustc_hir::{Expr, HirId, HirIdMap, HirIdSet, Node};
use rustc_span::symbol::sym;
use rustc_span::Span;
}
impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> {
NestedVisitorMap::OnlyBodies(&self.tcx.hir())
}
.sess
.struct_span_err(expr.span, "`break` to unknown label")
.emit();
- errors::FatalError.raise()
+ rustc_errors::FatalError.raise()
}
}
}
// Checking for error conditions
impl<'a, 'tcx> Visitor<'tcx> for Liveness<'a, 'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> {
NestedVisitorMap::None
}
if ln == self.s.exit_ln { false } else { self.assigned_on_exit(ln, var).is_some() };
if is_assigned {
- self.ir.tcx.lint_hir_note(
- lint::builtin::UNUSED_VARIABLES,
- hir_id,
- spans,
- &format!("variable `{}` is assigned to, but never used", name),
- &format!("consider using `_{}` instead", name),
- );
+ self.ir
+ .tcx
+ .struct_span_lint_hir(
+ lint::builtin::UNUSED_VARIABLES,
+ hir_id,
+ spans,
+ &format!("variable `{}` is assigned to, but never used", name),
+ )
+ .note(&format!("consider using `_{}` instead", name))
+ .emit();
} else {
let mut err = self.ir.tcx.struct_span_lint_hir(
lint::builtin::UNUSED_VARIABLES,
use rustc::session::Session;
-use errors::Applicability;
-use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc::hir::map::Map;
use rustc::ty::query::Providers;
use rustc::ty::TyCtxt;
+use rustc_errors::{struct_span_err, Applicability};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_hir::{Destination, Movability, Node};
use rustc_span::Span;
-use syntax::struct_span_err;
use rustc_error_codes::*;
}
impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'hir> {
+ type Map = Map<'hir>;
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> {
NestedVisitorMap::OnlyBodies(&self.hir_map)
}
// makes all other generics or inline functions that it references
// reachable as well.
-use rustc::hir::intravisit;
-use rustc::hir::intravisit::{NestedVisitorMap, Visitor};
+use rustc::hir::map::Map;
use rustc::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
use rustc::middle::privacy;
use rustc::session::config;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_hir::def_id::{CrateNum, DefId};
+use rustc_hir::intravisit;
+use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_hir::{HirIdSet, Node};
use rustc_target::spec::abi::Abi;
}
impl<'a, 'tcx> Visitor<'tcx> for ReachableContext<'a, 'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> {
NestedVisitorMap::None
}
//!
//! [rustc guide]: https://rust-lang.github.io/rustc-guide/mir/borrowck.html
-use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor};
+use rustc::hir::map::Map;
use rustc::middle::region::*;
use rustc::ty::query::Providers;
use rustc::ty::TyCtxt;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_hir::{Arm, Block, Expr, Local, Node, Pat, PatKind, Stmt};
use rustc_index::vec::Idx;
use rustc_span::source_map;
use rustc_span::Span;
+use syntax::walk_list;
use std::mem;
}
impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> {
NestedVisitorMap::None
}
//! A pass that annotates every item and method with its stability level,
//! propagating default levels lexically from parent to children ast nodes.
-use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor};
+use rustc::hir::map::Map;
use rustc::lint;
use rustc::middle::privacy::AccessLevels;
use rustc::middle::stability::{DeprecationEntry, Index};
+use rustc::session::parse::feature_err;
use rustc::session::Session;
+use rustc::traits::misc::can_type_implement_copy;
use rustc::ty::query::Providers;
use rustc::ty::TyCtxt;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_hir::{Generics, HirId, Item, StructField, Variant};
use rustc_span::symbol::{sym, Symbol};
use rustc_span::Span;
use syntax::ast::Attribute;
use syntax::attr::{self, Stability};
-use syntax::feature_gate::feature_err;
use std::cmp::Ordering;
use std::mem::replace;
/// Because stability levels are scoped lexically, we want to walk
/// nested items in the context of the outer item, so enable
/// deep-walking.
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> {
NestedVisitorMap::All(&self.tcx.hir())
}
}
impl<'a, 'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'a, 'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> {
NestedVisitorMap::OnlyBodies(&self.tcx.hir())
}
}
impl Visitor<'tcx> for Checker<'tcx> {
+ type Map = Map<'tcx>;
+
/// Because stability levels are scoped lexically, we want to walk
/// nested items in the context of the outer item, so enable
/// deep-walking.
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> {
NestedVisitorMap::OnlyBodies(&self.tcx.hir())
}
.emit();
} else {
let param_env = self.tcx.param_env(def_id);
- if !param_env.can_type_implement_copy(self.tcx, ty).is_ok() {
+ if !can_type_implement_copy(self.tcx, param_env, ty).is_ok() {
feature_err(
&self.tcx.sess.parse_sess,
sym::untagged_unions,
[dependencies]
rustc = { path = "../librustc" }
+rustc_errors = { path = "../librustc_errors" }
rustc_hir = { path = "../librustc_hir" }
+rustc_lint = { path = "../librustc_lint" }
rustc_metadata = { path = "../librustc_metadata" }
syntax = { path = "../libsyntax" }
rustc_span = { path = "../librustc_span" }
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
#![feature(nll)]
-use rustc::lint::LintStore;
+use rustc_lint::LintStore;
pub mod build;
pub mod load;
use crate::Registry;
use rustc::middle::cstore::MetadataLoader;
use rustc::session::Session;
+use rustc_error_codes::*;
+use rustc_errors::struct_span_err;
use rustc_metadata::locator;
-
use rustc_span::symbol::sym;
use rustc_span::Span;
+use syntax::ast::{Crate, Ident};
+
use std::borrow::ToOwned;
use std::env;
use std::mem;
use std::path::PathBuf;
-use syntax::ast::{Crate, Ident};
-use syntax::struct_span_err;
-
-use rustc_error_codes::*;
/// Pointer to a registrar function.
type PluginRegistrarFn = fn(&mut Registry<'_>);
[dependencies]
rustc = { path = "../librustc" }
+rustc_errors = { path = "../librustc_errors" }
rustc_hir = { path = "../librustc_hir" }
rustc_typeck = { path = "../librustc_typeck" }
syntax = { path = "../libsyntax" }
#![feature(nll)]
#![recursion_limit = "256"]
-#[macro_use]
-extern crate syntax;
-
use rustc::bug;
-use rustc::hir::intravisit::{self, DeepVisitor, NestedVisitorMap, Visitor};
+use rustc::hir::map::Map;
use rustc::lint;
use rustc::middle::privacy::{AccessLevel, AccessLevels};
use rustc::ty::fold::TypeVisitor;
use rustc::ty::subst::InternalSubsts;
use rustc::ty::{self, GenericParamDefKind, TraitRef, Ty, TyCtxt, TypeFoldable};
use rustc_data_structures::fx::FxHashSet;
+use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
+use rustc_hir::intravisit::{self, DeepVisitor, NestedVisitorMap, Visitor};
use rustc_hir::{AssocItemKind, HirIdSet, Node, PatKind};
use rustc_span::hygiene::Transparency;
use rustc_span::symbol::{kw, sym};
}
impl Visitor<'tcx> for PubRestrictedVisitor<'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> {
NestedVisitorMap::All(&self.tcx.hir())
}
fn visit_vis(&mut self, vis: &'tcx hir::Visibility<'tcx>) {
if let Some(item) = module
.res
.and_then(|res| res.mod_def_id())
+ // If the module is `self`, i.e. the current crate,
+ // there will be no corresponding item.
+ .filter(|def_id| def_id.index != CRATE_DEF_INDEX || def_id.krate != LOCAL_CRATE)
.and_then(|def_id| self.tcx.hir().as_local_hir_id(def_id))
.map(|module_hir_id| self.tcx.hir().expect_item(module_hir_id))
{
}
impl Visitor<'tcx> for EmbargoVisitor<'tcx> {
+ type Map = Map<'tcx>;
+
/// We want to visit items in the context of their containing
/// module and so forth, so supply a crate for doing a deep walk.
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> {
NestedVisitorMap::All(&self.tcx.hir())
}
}
impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> {
+ type Map = Map<'tcx>;
+
/// We want to visit items in the context of their containing
/// module and so forth, so supply a crate for doing a deep walk.
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> {
NestedVisitorMap::All(&self.tcx.hir())
}
}
impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {
+ type Map = Map<'tcx>;
+
/// We want to visit items in the context of their containing
/// module and so forth, so supply a crate for doing a deep walk.
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> {
NestedVisitorMap::All(&self.tcx.hir())
}
}
impl<'a, 'b, 'tcx, 'v> Visitor<'v> for ObsoleteCheckTypeForPrivatenessVisitor<'a, 'b, 'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'v> {
+ type Map = Map<'v>;
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> {
NestedVisitorMap::None
}
}
impl<'a, 'tcx> Visitor<'tcx> for ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> {
+ type Map = Map<'tcx>;
+
/// We want to visit items in the context of their containing
/// module and so forth, so supply a crate for doing a deep walk.
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> {
NestedVisitorMap::All(&self.tcx.hir())
}
}
impl<'a, 'tcx> Visitor<'tcx> for PrivateItemsInPublicInterfacesVisitor<'a, 'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> {
NestedVisitorMap::OnlyBodies(&self.tcx.hir())
}
bitflags = "1.2.1"
log = "0.4"
syntax = { path = "../libsyntax" }
-rustc_expand = { path = "../librustc_expand" }
arena = { path = "../libarena" }
-errors = { path = "../librustc_errors", package = "rustc_errors" }
-rustc_span = { path = "../librustc_span" }
rustc = { path = "../librustc" }
rustc_ast_lowering = { path = "../librustc_ast_lowering" }
rustc_data_structures = { path = "../librustc_data_structures" }
+rustc_errors = { path = "../librustc_errors" }
+rustc_expand = { path = "../librustc_expand" }
rustc_feature = { path = "../librustc_feature" }
rustc_hir = { path = "../librustc_hir" }
rustc_metadata = { path = "../librustc_metadata" }
rustc_error_codes = { path = "../librustc_error_codes" }
rustc_session = { path = "../librustc_session" }
+rustc_span = { path = "../librustc_span" }
smallvec = { version = "1.0", features = ["union", "may_dangle"] }
use rustc::hir::exports::Export;
use rustc::middle::cstore::CrateStore;
use rustc::ty;
-use rustc_hir::def::{self, *};
-use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
-use rustc_metadata::creader::LoadedMacro;
-
use rustc_data_structures::sync::Lrc;
-use std::cell::Cell;
-use std::ptr;
-
-use errors::Applicability;
-
+use rustc_error_codes::*;
+use rustc_errors::{struct_span_err, Applicability};
use rustc_expand::base::SyntaxExtension;
use rustc_expand::expand::AstFragment;
+use rustc_hir::def::{self, *};
+use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
+use rustc_metadata::creader::LoadedMacro;
use rustc_span::hygiene::{ExpnId, MacroKind};
use rustc_span::source_map::{respan, Spanned};
use rustc_span::symbol::{kw, sym};
use syntax::ast::{AssocItem, AssocItemKind, MetaItemKind, StmtKind};
use syntax::ast::{Ident, Name};
use syntax::attr;
-use syntax::span_err;
use syntax::token::{self, Token};
use syntax::visit::{self, Visitor};
use log::debug;
-
-use rustc_error_codes::*;
+use std::cell::Cell;
+use std::ptr;
type Res = def::Res<NodeId>;
for attr in &item.attrs {
if attr.check_name(sym::macro_use) {
if self.parent_scope.module.parent.is_some() {
- span_err!(
+ struct_span_err!(
self.r.session,
item.span,
E0468,
"an `extern crate` loading macros must be at the crate root"
- );
+ )
+ .emit();
}
if let ItemKind::ExternCrate(Some(orig_name)) = item.kind {
if orig_name == kw::SelfLower {
- self.r.session.span_err(
- attr.span,
- "`macro_use` is not supported on `extern crate self`",
- );
+ self.r
+ .session
+ .struct_span_err(
+ attr.span,
+ "`macro_use` is not supported on `extern crate self`",
+ )
+ .emit();
}
}
- let ill_formed = |span| span_err!(self.r.session, span, E0466, "bad macro import");
+ let ill_formed =
+ |span| struct_span_err!(self.r.session, span, E0466, "bad macro import").emit();
match attr.meta() {
Some(meta) => match meta.kind {
MetaItemKind::Word => {
allow_shadowing,
);
} else {
- span_err!(self.r.session, ident.span, E0469, "imported macro not found");
+ struct_span_err!(self.r.session, ident.span, E0469, "imported macro not found")
+ .emit();
}
}
}
use crate::imports::ImportDirectiveSubclass;
use crate::Resolver;
-use errors::pluralize;
use rustc::{lint, ty};
use rustc_data_structures::fx::FxHashSet;
+use rustc_errors::pluralize;
+use rustc_session::lint::BuiltinLintDiagnostics;
use rustc_session::node_id::NodeMap;
use rustc_span::{MultiSpan, Span, DUMMY_SP};
use syntax::ast;
unused.use_tree_id,
ms,
&msg,
- lint::builtin::BuiltinLintDiagnostics::UnusedImports(fix_msg.into(), fixes),
+ BuiltinLintDiagnostics::UnusedImports(fix_msg.into(), fixes),
);
}
}
use std::cmp::Reverse;
-use errors::{Applicability, DiagnosticBuilder};
use log::debug;
use rustc::bug;
use rustc::session::Session;
use rustc::ty::{self, DefIdTree};
use rustc_data_structures::fx::FxHashSet;
+use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
use rustc_feature::BUILTIN_ATTRIBUTES;
use rustc_hir::def::Namespace::{self, *};
use rustc_hir::def::{self, DefKind, NonMacroAttrKind};
-use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX};
+use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
use rustc_span::hygiene::MacroKind;
use rustc_span::source_map::SourceMap;
use rustc_span::symbol::{kw, Symbol};
use rustc_span::{BytePos, MultiSpan, Span};
use syntax::ast::{self, Ident, Path};
use syntax::print::pprust;
-use syntax::struct_span_err;
use syntax::util::lev_distance::find_best_match_for_name;
use crate::imports::{ImportDirective, ImportDirectiveSubclass, ImportResolver};
suggestion.candidate.to_string(),
Applicability::MaybeIncorrect,
);
- let def_span =
- suggestion.res.opt_def_id().and_then(|def_id| self.definitions.opt_span(def_id));
+ let def_span = suggestion.res.opt_def_id().and_then(|def_id| match def_id.krate {
+ LOCAL_CRATE => self.definitions.opt_span(def_id),
+ _ => Some(
+ self.session
+ .source_map()
+ .def_span(self.cstore().get_span_untracked(def_id, self.session)),
+ ),
+ });
if let Some(span) = def_span {
err.span_label(
span,
use crate::{CrateLint, Module, ModuleOrUniformRoot, ParentScope, PerNS, ScopeSet, Weak};
use crate::{NameBinding, NameBindingKind, PathResult, PrivacyError, ToNameBinding};
-use errors::{pluralize, Applicability};
-
use rustc::hir::exports::Export;
-use rustc::lint::builtin::BuiltinLintDiagnostics;
use rustc::lint::builtin::{PUB_USE_OF_PRIVATE_EXTERN_CRATE, UNUSED_IMPORTS};
-use rustc::session::DiagnosticMessageId;
use rustc::ty;
use rustc::{bug, span_bug};
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::ptr_key::PtrKey;
+use rustc_errors::{pluralize, struct_span_err, Applicability};
use rustc_hir::def::{self, PartialRes};
use rustc_hir::def_id::DefId;
+use rustc_session::lint::BuiltinLintDiagnostics;
+use rustc_session::DiagnosticMessageId;
use rustc_span::hygiene::ExpnId;
use rustc_span::symbol::kw;
use rustc_span::{MultiSpan, Span};
use syntax::ast::{Ident, Name, NodeId};
+use syntax::unwrap_or;
use syntax::util::lev_distance::find_best_match_for_name;
-use syntax::{struct_span_err, unwrap_or};
use rustc_error_codes::*;
use crate::{Module, ModuleOrUniformRoot, NameBindingKind, ParentScope, PathResult};
use crate::{ResolutionError, Resolver, Segment, UseError};
-use log::debug;
use rustc::{bug, lint, span_bug};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::DiagnosticId;
use rustc_hir::def::Namespace::{self, *};
use rustc_hir::def::{self, CtorKind, DefKind, PartialRes, PerNS};
use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX};
use syntax::visit::{self, FnKind, Visitor};
use syntax::{unwrap_or, walk_list};
+use log::debug;
use std::collections::BTreeSet;
use std::mem::replace;
}
}
- fn error_code(self, has_unexpected_resolution: bool) -> &'static str {
- syntax::diagnostic_used!(E0404);
- syntax::diagnostic_used!(E0405);
- syntax::diagnostic_used!(E0412);
- syntax::diagnostic_used!(E0422);
- syntax::diagnostic_used!(E0423);
- syntax::diagnostic_used!(E0425);
- syntax::diagnostic_used!(E0531);
- syntax::diagnostic_used!(E0532);
- syntax::diagnostic_used!(E0573);
- syntax::diagnostic_used!(E0574);
- syntax::diagnostic_used!(E0575);
- syntax::diagnostic_used!(E0576);
+ fn error_code(self, has_unexpected_resolution: bool) -> DiagnosticId {
+ use rustc_errors::error_code;
match (self, has_unexpected_resolution) {
- (PathSource::Trait(_), true) => "E0404",
- (PathSource::Trait(_), false) => "E0405",
- (PathSource::Type, true) => "E0573",
- (PathSource::Type, false) => "E0412",
- (PathSource::Struct, true) => "E0574",
- (PathSource::Struct, false) => "E0422",
- (PathSource::Expr(..), true) => "E0423",
- (PathSource::Expr(..), false) => "E0425",
- (PathSource::Pat, true) | (PathSource::TupleStruct, true) => "E0532",
- (PathSource::Pat, false) | (PathSource::TupleStruct, false) => "E0531",
- (PathSource::TraitItem(..), true) => "E0575",
- (PathSource::TraitItem(..), false) => "E0576",
+ (PathSource::Trait(_), true) => error_code!(E0404),
+ (PathSource::Trait(_), false) => error_code!(E0405),
+ (PathSource::Type, true) => error_code!(E0573),
+ (PathSource::Type, false) => error_code!(E0412),
+ (PathSource::Struct, true) => error_code!(E0574),
+ (PathSource::Struct, false) => error_code!(E0422),
+ (PathSource::Expr(..), true) => error_code!(E0423),
+ (PathSource::Expr(..), false) => error_code!(E0425),
+ (PathSource::Pat, true) | (PathSource::TupleStruct, true) => error_code!(E0532),
+ (PathSource::Pat, false) | (PathSource::TupleStruct, false) => error_code!(E0531),
+ (PathSource::TraitItem(..), true) => error_code!(E0575),
+ (PathSource::TraitItem(..), false) => error_code!(E0576),
}
}
}
use crate::{CrateLint, Module, ModuleKind, ModuleOrUniformRoot};
use crate::{PathResult, PathSource, Segment};
-use errors::{Applicability, DiagnosticBuilder, DiagnosticId};
-use log::debug;
use rustc::session::config::nightly_options;
use rustc_data_structures::fx::FxHashSet;
+use rustc_error_codes::*;
+use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_hir::def::Namespace::{self, *};
use rustc_hir::def::{self, CtorKind, DefKind};
use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX};
use syntax::ast::{self, Expr, ExprKind, Ident, NodeId, Path, Ty, TyKind};
use syntax::util::lev_distance::find_best_match_for_name;
-use rustc_error_codes::*;
+use log::debug;
type Res = def::Res<ast::NodeId>;
let expected = source.descr_expected();
let path_str = Segment::names_to_string(path);
let item_str = path.last().unwrap().ident;
- let code = source.error_code(res.is_some());
let (base_msg, fallback_label, base_span, could_be_expr) = if let Some(res) = res {
(
format!("expected {}, found {} `{}`", expected, res.descr(), path_str),
)
};
- let code = DiagnosticId::Error(code.into());
+ let code = source.error_code(res.is_some());
let mut err = self.r.session.struct_span_err_with_code(base_span, &base_msg, code);
// Emit help message for fake-self from other languages (e.g., `this` in Javascript).
// Emit special messages for unresolved `Self` and `self`.
if is_self_type(path, ns) {
- syntax::diagnostic_used!(E0411);
- err.code(DiagnosticId::Error("E0411".into()));
+ err.code(rustc_errors::error_code!(E0411));
err.span_label(
span,
format!("`Self` is only available in impls, traits, and type definitions"),
if is_self_value(path, ns) {
debug!("smart_resolve_path_fragment: E0424, source={:?}", source);
- syntax::diagnostic_used!(E0424);
- err.code(DiagnosticId::Error("E0424".into()));
+ err.code(rustc_errors::error_code!(E0424));
err.span_label(span, match source {
PathSource::Pat => format!(
"`self` value is a keyword and may not be bound to variables or shadowed",
use Determinacy::*;
-use errors::{Applicability, DiagnosticBuilder};
use rustc::hir::exports::ExportMap;
-use rustc::hir::map::Definitions;
+use rustc::hir::map::{DefKey, Definitions};
use rustc::lint;
use rustc::middle::cstore::{CrateStore, MetadataLoaderDyn};
-use rustc::session::Session;
use rustc::span_bug;
use rustc::ty::query::Providers;
use rustc::ty::{self, DefIdTree, ResolverOutputs};
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
use rustc_data_structures::ptr_key::PtrKey;
use rustc_data_structures::sync::Lrc;
+use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
use rustc_expand::base::SyntaxExtension;
use rustc_hir::def::Namespace::*;
use rustc_hir::def::{self, CtorKind, CtorOf, DefKind, NonMacroAttrKind, PartialRes};
use rustc_hir::PrimTy::{self, Bool, Char, Float, Int, Str, Uint};
use rustc_hir::{GlobMap, TraitMap};
use rustc_metadata::creader::{CStore, CrateLoader};
+use rustc_session::lint::{BuiltinLintDiagnostics, LintBuffer};
use rustc_session::node_id::{NodeMap, NodeSet};
+use rustc_session::Session;
use rustc_span::hygiene::{ExpnId, ExpnKind, MacroKind, SyntaxContext, Transparency};
use rustc_span::source_map::Spanned;
use rustc_span::symbol::{kw, sym};
use syntax::ast::{ItemKind, Path};
use syntax::attr;
use syntax::print::pprust;
+use syntax::unwrap_or;
use syntax::visit::{self, Visitor};
-use syntax::{struct_span_err, unwrap_or};
use log::debug;
use std::cell::{Cell, RefCell};
/// when visiting the correspondent variants.
variant_vis: DefIdMap<ty::Visibility>,
- lint_buffer: lint::LintBuffer,
+ lint_buffer: LintBuffer,
next_node_id: NodeId,
}
/// This interface is used through the AST→HIR step, to embed full paths into the HIR. After that
/// the resolver is no longer needed as all the relevant information is inline.
impl rustc_ast_lowering::Resolver for Resolver<'_> {
- fn cstore(&self) -> &dyn CrateStore {
- self.cstore()
+ fn def_key(&mut self, id: DefId) -> DefKey {
+ if id.is_local() { self.definitions().def_key(id.index) } else { self.cstore().def_key(id) }
+ }
+
+ fn item_generics_num_lifetimes(&self, def_id: DefId, sess: &Session) -> usize {
+ self.cstore().item_generics_num_lifetimes(def_id, sess)
}
fn resolve_str_path(
&mut self.definitions
}
- fn lint_buffer(&mut self) -> &mut lint::LintBuffer {
+ fn lint_buffer(&mut self) -> &mut LintBuffer {
&mut self.lint_buffer
}
.chain(features.declared_lang_features.iter().map(|(feat, ..)| *feat))
.collect(),
variant_vis: Default::default(),
- lint_buffer: lint::LintBuffer::default(),
+ lint_buffer: LintBuffer::default(),
next_node_id: NodeId::from_u32(1),
}
}
self.next_node_id
}
- pub fn lint_buffer(&mut self) -> &mut lint::LintBuffer {
+ pub fn lint_buffer(&mut self) -> &mut LintBuffer {
&mut self.lint_buffer
}
if let Some(node_id) = poisoned {
self.lint_buffer.buffer_lint_with_diagnostic(
lint::builtin::PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
- node_id, ident.span,
+ node_id,
+ ident.span,
&format!("cannot find {} `{}` in this scope", ns.descr(), ident),
- lint::builtin::BuiltinLintDiagnostics::
- ProcMacroDeriveResolutionFallback(ident.span),
+ BuiltinLintDiagnostics::ProcMacroDeriveResolutionFallback(ident.span),
);
}
return Some(LexicalScopeBinding::Item(binding));
}
}
- let diag = lint::builtin::BuiltinLintDiagnostics::AbsPathWithModule(diag_span);
+ let diag = BuiltinLintDiagnostics::AbsPathWithModule(diag_span);
self.lint_buffer.buffer_lint_with_diagnostic(
lint::builtin::ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE,
diag_id,
cannot be referred to by absolute paths";
self.lint_buffer.buffer_lint_with_diagnostic(
lint::builtin::MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS,
- CRATE_NODE_ID, span_use, msg,
- lint::builtin::BuiltinLintDiagnostics::
- MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def),
+ CRATE_NODE_ID,
+ span_use,
+ msg,
+ BuiltinLintDiagnostics::MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def),
);
}
//! used between functions, and they operate in a purely top-down
//! way. Therefore, we break lifetime name resolution into a separate pass.
-use errors::{pluralize, Applicability, DiagnosticBuilder};
-use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc::hir::map::Map;
use rustc::lint;
use rustc::middle::resolve_lifetime::*;
use rustc::ty::{self, DefIdTree, GenericParamDefKind, TyCtxt};
use rustc::{bug, span_bug};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LOCAL_CRATE};
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_hir::{GenericArg, GenericParam, LifetimeName, Node, ParamName, QPath};
use rustc_hir::{GenericParamKind, HirIdMap, HirIdSet, LifetimeParamKind};
use rustc_span::symbol::{kw, sym};
use std::mem::{replace, take};
use syntax::ast;
use syntax::attr;
-use syntax::{help, span_err, struct_span_err, walk_list};
+use syntax::walk_list;
use log::debug;
}
impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> {
NestedVisitorMap::All(&self.tcx.hir())
}
|| krate.impl_items.contains_key(&parent_impl_id)
|| krate.trait_items.contains_key(&parent_trait_id))
{
- span_err!(
+ struct_span_err!(
self.tcx.sess,
lifetime.span,
E0657,
"`impl Trait` can only capture lifetimes \
bound at the fn or impl level"
- );
+ )
+ .emit();
self.uninsert_lifetime_on_error(lifetime, def.unwrap());
}
}
})
{
if self.trait_ref_hack {
- span_err!(
+ struct_span_err!(
self.tcx.sess,
trait_ref.span,
E0316,
"nested quantification of lifetimes"
- );
+ )
+ .emit();
}
let next_early_index = self.next_early_index();
let scope = Scope::Binder {
gather.visit_body(body);
impl<'v, 'a, 'tcx> Visitor<'v> for GatherLabels<'a, 'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'v> {
+ type Map = Map<'v>;
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> {
NestedVisitorMap::None
}
}
impl<'a> Visitor<'a> for SelfVisitor<'a> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'a> {
+ type Map = Map<'a>;
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> {
NestedVisitorMap::None
}
}
impl<'v, 'a> Visitor<'v> for GatherLifetimes<'a> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'v> {
+ type Map = Map<'v>;
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> {
NestedVisitorMap::None
}
}
if len == 0 {
- help!(
- db,
- "this function's return type contains a borrowed value, but \
- there is no value for it to be borrowed from"
+ db.help(
+ "this function's return type contains a borrowed value, \
+ but there is no value for it to be borrowed from",
);
self.suggest_lifetime(db, span, "consider giving it a 'static lifetime")
} else if elided_len == 0 {
- help!(
- db,
+ db.help(
"this function's return type contains a borrowed value with \
an elided lifetime, but the lifetime cannot be derived from \
- the arguments"
+ the arguments",
);
let msg = "consider giving it an explicit bounded or 'static lifetime";
self.suggest_lifetime(db, span, msg)
} else if elided_len == 1 {
- help!(
- db,
- "this function's return type contains a borrowed value, but \
- the signature does not say which {} it is borrowed from",
+ db.help(&format!(
+ "this function's return type contains a borrowed value, \
+ but the signature does not say which {} it is borrowed from",
m
- );
+ ));
true
} else {
- help!(
- db,
- "this function's return type contains a borrowed value, but \
- the signature does not say whether it is borrowed from {}",
+ db.help(&format!(
+ "this function's return type contains a borrowed value, \
+ but the signature does not say whether it is borrowed from {}",
m
- );
+ ));
true
}
}
}
impl<'v> Visitor<'v> for ConstrainedCollector {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'v> {
+ type Map = Map<'v>;
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> {
NestedVisitorMap::None
}
}
impl<'v> Visitor<'v> for AllCollector {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'v> {
+ type Map = Map<'v>;
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> {
NestedVisitorMap::None
}
use crate::{CrateLint, ParentScope, ResolutionError, Resolver, Scope, ScopeSet, Weak};
use crate::{ModuleKind, ModuleOrUniformRoot, NameBinding, PathResult, Segment, ToNameBinding};
use rustc::middle::stability;
+use rustc::session::parse::feature_err;
use rustc::session::Session;
use rustc::{lint, span_bug, ty};
use rustc_data_structures::fx::FxHashSet;
use rustc_span::{Span, DUMMY_SP};
use syntax::ast::{self, Ident, NodeId};
use syntax::attr::{self, StabilityLevel};
-use syntax::feature_gate::feature_err;
use syntax::print::pprust;
use rustc_data_structures::sync::Lrc;
[dependencies]
log = "0.4"
+rustc_error_codes = { path = "../librustc_error_codes" }
rustc_errors = { path = "../librustc_errors" }
rustc_feature = { path = "../librustc_feature" }
rustc_target = { path = "../librustc_target" }
pub use self::Level::*;
-use crate::node_id::NodeId;
+use crate::node_id::{NodeId, NodeMap};
use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey};
use rustc_span::edition::Edition;
-use rustc_span::{sym, MultiSpan, Symbol};
+use rustc_span::{sym, symbol::Ident, MultiSpan, Span, Symbol};
+
+pub mod builtin;
/// Setting for how to handle a lint.
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
}
}
-/// Stores buffered lint info which can later be passed to `librustc`.
+// This could be a closure, but then implementing derive trait
+// becomes hacky (and it gets allocated).
+#[derive(PartialEq)]
+pub enum BuiltinLintDiagnostics {
+ Normal,
+ BareTraitObject(Span, /* is_global */ bool),
+ AbsPathWithModule(Span),
+ ProcMacroDeriveResolutionFallback(Span),
+ MacroExpandedMacroExportsAccessedByAbsolutePaths(Span),
+ ElidedLifetimesInPaths(usize, Span, bool, Span, String),
+ UnknownCrateTypes(Span, String, String),
+ UnusedImports(String, Vec<(Span, String)>),
+ RedundantImport(Vec<(Span, bool)>, Ident),
+ DeprecatedMacro(Option<Symbol>, Span),
+}
+
+/// Lints that are buffered up early on in the `Session` before the
+/// `LintLevels` is calculated. These are later passed to `librustc`.
+#[derive(PartialEq)]
pub struct BufferedEarlyLint {
/// The span of code that we are linting on.
pub span: MultiSpan,
pub msg: String,
/// The `NodeId` of the AST node that generated the lint.
- pub id: NodeId,
+ pub node_id: NodeId,
/// A lint Id that can be passed to `rustc::lint::Lint::from_parser_lint_id`.
- pub lint_id: &'static Lint,
+ pub lint_id: LintId,
+
+ /// Customization of the `DiagnosticBuilder<'_>` for the lint.
+ pub diagnostic: BuiltinLintDiagnostics,
+}
+
+#[derive(Default)]
+pub struct LintBuffer {
+ pub map: NodeMap<Vec<BufferedEarlyLint>>,
+}
+
+impl LintBuffer {
+ pub fn add_early_lint(&mut self, early_lint: BufferedEarlyLint) {
+ let arr = self.map.entry(early_lint.node_id).or_default();
+ if !arr.contains(&early_lint) {
+ arr.push(early_lint);
+ }
+ }
+
+ pub fn add_lint(
+ &mut self,
+ lint: &'static Lint,
+ node_id: NodeId,
+ span: MultiSpan,
+ msg: &str,
+ diagnostic: BuiltinLintDiagnostics,
+ ) {
+ let lint_id = LintId::of(lint);
+ let msg = msg.to_string();
+ self.add_early_lint(BufferedEarlyLint { lint_id, node_id, span, msg, diagnostic });
+ }
+
+ pub fn take(&mut self, id: NodeId) -> Vec<BufferedEarlyLint> {
+ self.map.remove(&id).unwrap_or_default()
+ }
+
+ pub fn buffer_lint(
+ &mut self,
+ lint: &'static Lint,
+ id: NodeId,
+ sp: impl Into<MultiSpan>,
+ msg: &str,
+ ) {
+ self.add_lint(lint, id, sp.into(), msg, BuiltinLintDiagnostics::Normal)
+ }
+
+ pub fn buffer_lint_with_diagnostic(
+ &mut self,
+ lint: &'static Lint,
+ id: NodeId,
+ sp: impl Into<MultiSpan>,
+ msg: &str,
+ diagnostic: BuiltinLintDiagnostics,
+ ) {
+ self.add_lint(lint, id, sp.into(), msg, diagnostic)
+ }
}
/// Declares a static item of type `&'static Lint`.
};
);
}
+
+/// Declares a static `LintArray` and return it as an expression.
+#[macro_export]
+macro_rules! lint_array {
+ ($( $lint:expr ),* ,) => { lint_array!( $($lint),* ) };
+ ($( $lint:expr ),*) => {{
+ vec![$($lint),*]
+ }}
+}
+
+pub type LintArray = Vec<&'static Lint>;
+
+pub trait LintPass {
+ fn name(&self) -> &'static str;
+}
+
+/// Implements `LintPass for $name` with the given list of `Lint` statics.
+#[macro_export]
+macro_rules! impl_lint_pass {
+ ($name:ident => [$($lint:expr),* $(,)?]) => {
+ impl $crate::lint::LintPass for $name {
+ fn name(&self) -> &'static str { stringify!($name) }
+ }
+ impl $name {
+ pub fn get_lints() -> $crate::lint::LintArray { $crate::lint_array!($($lint),*) }
+ }
+ };
+}
+
+/// Declares a type named `$name` which implements `LintPass`.
+/// To the right of `=>` a comma separated list of `Lint` statics is given.
+#[macro_export]
+macro_rules! declare_lint_pass {
+ ($(#[$m:meta])* $name:ident => [$($lint:expr),* $(,)?]) => {
+ $(#[$m])* #[derive(Copy, Clone)] pub struct $name;
+ $crate::impl_lint_pass!($name => [$($lint),*]);
+ };
+}
--- /dev/null
+//! Some lints that are built in to the compiler.
+//!
+//! These are the built-in lints that are emitted direct in the main
+//! compiler code, rather than using their own custom pass. Those
+//! lints are all available in `rustc_lint::builtin`.
+
+use crate::lint::FutureIncompatibleInfo;
+use crate::{declare_lint, declare_lint_pass};
+use rustc_span::edition::Edition;
+
+declare_lint! {
+ pub ILL_FORMED_ATTRIBUTE_INPUT,
+ Deny,
+ "ill-formed attribute inputs that were previously accepted and used in practice",
+ @future_incompatible = FutureIncompatibleInfo {
+ reference: "issue #57571 <https://github.com/rust-lang/rust/issues/57571>",
+ edition: None,
+ };
+}
+
+declare_lint! {
+ pub META_VARIABLE_MISUSE,
+ Allow,
+ "possible meta-variable misuse at macro definition"
+}
+
+declare_lint! {
+ pub INCOMPLETE_INCLUDE,
+ Deny,
+ "trailing content in included file"
+}
+
+declare_lint! {
+ pub EXCEEDING_BITSHIFTS,
+ Deny,
+ "shift exceeds the type's number of bits"
+}
+
+declare_lint! {
+ pub CONST_ERR,
+ Deny,
+ "constant evaluation detected erroneous expression",
+ report_in_external_macro
+}
+
+declare_lint! {
+ pub UNUSED_IMPORTS,
+ Warn,
+ "imports that are never used"
+}
+
+declare_lint! {
+ pub UNUSED_EXTERN_CRATES,
+ Allow,
+ "extern crates that are never used"
+}
+
+declare_lint! {
+ pub UNUSED_QUALIFICATIONS,
+ Allow,
+ "detects unnecessarily qualified names"
+}
+
+declare_lint! {
+ pub UNKNOWN_LINTS,
+ Warn,
+ "unrecognized lint attribute"
+}
+
+declare_lint! {
+ pub UNUSED_VARIABLES,
+ Warn,
+ "detect variables which are not used in any way"
+}
+
+declare_lint! {
+ pub UNUSED_ASSIGNMENTS,
+ Warn,
+ "detect assignments that will never be read"
+}
+
+declare_lint! {
+ pub DEAD_CODE,
+ Warn,
+ "detect unused, unexported items"
+}
+
+declare_lint! {
+ pub UNUSED_ATTRIBUTES,
+ Warn,
+ "detects attributes that were not used by the compiler"
+}
+
+declare_lint! {
+ pub UNREACHABLE_CODE,
+ Warn,
+ "detects unreachable code paths",
+ report_in_external_macro
+}
+
+declare_lint! {
+ pub UNREACHABLE_PATTERNS,
+ Warn,
+ "detects unreachable patterns"
+}
+
+declare_lint! {
+ pub OVERLAPPING_PATTERNS,
+ Warn,
+ "detects overlapping patterns"
+}
+
+declare_lint! {
+ pub BINDINGS_WITH_VARIANT_NAME,
+ Warn,
+ "detects pattern bindings with the same name as one of the matched variants"
+}
+
+declare_lint! {
+ pub UNUSED_MACROS,
+ Warn,
+ "detects macros that were not used"
+}
+
+declare_lint! {
+ pub WARNINGS,
+ Warn,
+ "mass-change the level for lints which produce warnings"
+}
+
+declare_lint! {
+ pub UNUSED_FEATURES,
+ Warn,
+ "unused features found in crate-level `#[feature]` directives"
+}
+
+declare_lint! {
+ pub STABLE_FEATURES,
+ Warn,
+ "stable features found in `#[feature]` directive"
+}
+
+declare_lint! {
+ pub UNKNOWN_CRATE_TYPES,
+ Deny,
+ "unknown crate type found in `#[crate_type]` directive"
+}
+
+declare_lint! {
+ pub TRIVIAL_CASTS,
+ Allow,
+ "detects trivial casts which could be removed"
+}
+
+declare_lint! {
+ pub TRIVIAL_NUMERIC_CASTS,
+ Allow,
+ "detects trivial casts of numeric types which could be removed"
+}
+
+declare_lint! {
+ pub PRIVATE_IN_PUBLIC,
+ Warn,
+ "detect private items in public interfaces not caught by the old implementation",
+ @future_incompatible = FutureIncompatibleInfo {
+ reference: "issue #34537 <https://github.com/rust-lang/rust/issues/34537>",
+ edition: None,
+ };
+}
+
+declare_lint! {
+ pub EXPORTED_PRIVATE_DEPENDENCIES,
+ Warn,
+ "public interface leaks type from a private dependency"
+}
+
+declare_lint! {
+ pub PUB_USE_OF_PRIVATE_EXTERN_CRATE,
+ Deny,
+ "detect public re-exports of private extern crates",
+ @future_incompatible = FutureIncompatibleInfo {
+ reference: "issue #34537 <https://github.com/rust-lang/rust/issues/34537>",
+ edition: None,
+ };
+}
+
+declare_lint! {
+ pub INVALID_TYPE_PARAM_DEFAULT,
+ Deny,
+ "type parameter default erroneously allowed in invalid location",
+ @future_incompatible = FutureIncompatibleInfo {
+ reference: "issue #36887 <https://github.com/rust-lang/rust/issues/36887>",
+ edition: None,
+ };
+}
+
+declare_lint! {
+ pub RENAMED_AND_REMOVED_LINTS,
+ Warn,
+ "lints that have been renamed or removed"
+}
+
+declare_lint! {
+ pub SAFE_PACKED_BORROWS,
+ Warn,
+ "safe borrows of fields of packed structs were was erroneously allowed",
+ @future_incompatible = FutureIncompatibleInfo {
+ reference: "issue #46043 <https://github.com/rust-lang/rust/issues/46043>",
+ edition: None,
+ };
+}
+
+declare_lint! {
+ pub PATTERNS_IN_FNS_WITHOUT_BODY,
+ Deny,
+ "patterns in functions without body were erroneously allowed",
+ @future_incompatible = FutureIncompatibleInfo {
+ reference: "issue #35203 <https://github.com/rust-lang/rust/issues/35203>",
+ edition: None,
+ };
+}
+
+declare_lint! {
+ pub MISSING_FRAGMENT_SPECIFIER,
+ Deny,
+ "detects missing fragment specifiers in unused `macro_rules!` patterns",
+ @future_incompatible = FutureIncompatibleInfo {
+ reference: "issue #40107 <https://github.com/rust-lang/rust/issues/40107>",
+ edition: None,
+ };
+}
+
+declare_lint! {
+ pub LATE_BOUND_LIFETIME_ARGUMENTS,
+ Warn,
+ "detects generic lifetime arguments in path segments with late bound lifetime parameters",
+ @future_incompatible = FutureIncompatibleInfo {
+ reference: "issue #42868 <https://github.com/rust-lang/rust/issues/42868>",
+ edition: None,
+ };
+}
+
+declare_lint! {
+ pub ORDER_DEPENDENT_TRAIT_OBJECTS,
+ Deny,
+ "trait-object types were treated as different depending on marker-trait order",
+ @future_incompatible = FutureIncompatibleInfo {
+ reference: "issue #56484 <https://github.com/rust-lang/rust/issues/56484>",
+ edition: None,
+ };
+}
+
+declare_lint! {
+ pub DEPRECATED,
+ Warn,
+ "detects use of deprecated items",
+ report_in_external_macro
+}
+
+declare_lint! {
+ pub UNUSED_UNSAFE,
+ Warn,
+ "unnecessary use of an `unsafe` block"
+}
+
+declare_lint! {
+ pub UNUSED_MUT,
+ Warn,
+ "detect mut variables which don't need to be mutable"
+}
+
+declare_lint! {
+ pub UNCONDITIONAL_RECURSION,
+ Warn,
+ "functions that cannot return without calling themselves"
+}
+
+declare_lint! {
+ pub SINGLE_USE_LIFETIMES,
+ Allow,
+ "detects lifetime parameters that are only used once"
+}
+
+declare_lint! {
+ pub UNUSED_LIFETIMES,
+ Allow,
+ "detects lifetime parameters that are never used"
+}
+
+declare_lint! {
+ pub TYVAR_BEHIND_RAW_POINTER,
+ Warn,
+ "raw pointer to an inference variable",
+ @future_incompatible = FutureIncompatibleInfo {
+ reference: "issue #46906 <https://github.com/rust-lang/rust/issues/46906>",
+ edition: Some(Edition::Edition2018),
+ };
+}
+
+declare_lint! {
+ pub ELIDED_LIFETIMES_IN_PATHS,
+ Allow,
+ "hidden lifetime parameters in types are deprecated"
+}
+
+declare_lint! {
+ pub BARE_TRAIT_OBJECTS,
+ Warn,
+ "suggest using `dyn Trait` for trait objects"
+}
+
+declare_lint! {
+ pub ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE,
+ Allow,
+ "fully qualified paths that start with a module name \
+ instead of `crate`, `self`, or an extern crate name",
+ @future_incompatible = FutureIncompatibleInfo {
+ reference: "issue #53130 <https://github.com/rust-lang/rust/issues/53130>",
+ edition: Some(Edition::Edition2018),
+ };
+}
+
+declare_lint! {
+ pub ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
+ Warn,
+ "floating-point literals cannot be used in patterns",
+ @future_incompatible = FutureIncompatibleInfo {
+ reference: "issue #41620 <https://github.com/rust-lang/rust/issues/41620>",
+ edition: None,
+ };
+}
+
+declare_lint! {
+ pub UNSTABLE_NAME_COLLISIONS,
+ Warn,
+ "detects name collision with an existing but unstable method",
+ @future_incompatible = FutureIncompatibleInfo {
+ reference: "issue #48919 <https://github.com/rust-lang/rust/issues/48919>",
+ edition: None,
+ // Note: this item represents future incompatibility of all unstable functions in the
+ // standard library, and thus should never be removed or changed to an error.
+ };
+}
+
+declare_lint! {
+ pub IRREFUTABLE_LET_PATTERNS,
+ Warn,
+ "detects irrefutable patterns in if-let and while-let statements"
+}
+
+declare_lint! {
+ pub UNUSED_LABELS,
+ Warn,
+ "detects labels that are never used"
+}
+
+declare_lint! {
+ pub INTRA_DOC_LINK_RESOLUTION_FAILURE,
+ Warn,
+ "failures in resolving intra-doc link targets"
+}
+
+declare_lint! {
+ pub MISSING_DOC_CODE_EXAMPLES,
+ Allow,
+ "detects publicly-exported items without code samples in their documentation"
+}
+
+declare_lint! {
+ pub PRIVATE_DOC_TESTS,
+ Allow,
+ "detects code samples in docs of private items not documented by rustdoc"
+}
+
+declare_lint! {
+ pub WHERE_CLAUSES_OBJECT_SAFETY,
+ Warn,
+ "checks the object safety of where clauses",
+ @future_incompatible = FutureIncompatibleInfo {
+ reference: "issue #51443 <https://github.com/rust-lang/rust/issues/51443>",
+ edition: None,
+ };
+}
+
+declare_lint! {
+ pub PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
+ Warn,
+ "detects proc macro derives using inaccessible names from parent modules",
+ @future_incompatible = FutureIncompatibleInfo {
+ reference: "issue #50504 <https://github.com/rust-lang/rust/issues/50504>",
+ edition: None,
+ };
+}
+
+declare_lint! {
+ pub MACRO_USE_EXTERN_CRATE,
+ Allow,
+ "the `#[macro_use]` attribute is now deprecated in favor of using macros \
+ via the module system"
+}
+
+declare_lint! {
+ pub MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS,
+ Deny,
+ "macro-expanded `macro_export` macros from the current crate \
+ cannot be referred to by absolute paths",
+ @future_incompatible = FutureIncompatibleInfo {
+ reference: "issue #52234 <https://github.com/rust-lang/rust/issues/52234>",
+ edition: None,
+ };
+}
+
+declare_lint! {
+ pub EXPLICIT_OUTLIVES_REQUIREMENTS,
+ Allow,
+ "outlives requirements can be inferred"
+}
+
+declare_lint! {
+ pub INDIRECT_STRUCTURAL_MATCH,
+ // defaulting to allow until rust-lang/rust#62614 is fixed.
+ Allow,
+ "pattern with const indirectly referencing non-`#[structural_match]` type",
+ @future_incompatible = FutureIncompatibleInfo {
+ reference: "issue #62411 <https://github.com/rust-lang/rust/issues/62411>",
+ edition: None,
+ };
+}
+
+declare_lint! {
+ pub DEPRECATED_IN_FUTURE,
+ Allow,
+ "detects use of items that will be deprecated in a future version",
+ report_in_external_macro
+}
+
+declare_lint! {
+ pub AMBIGUOUS_ASSOCIATED_ITEMS,
+ Deny,
+ "ambiguous associated items",
+ @future_incompatible = FutureIncompatibleInfo {
+ reference: "issue #57644 <https://github.com/rust-lang/rust/issues/57644>",
+ edition: None,
+ };
+}
+
+declare_lint! {
+ pub MUTABLE_BORROW_RESERVATION_CONFLICT,
+ Warn,
+ "reservation of a two-phased borrow conflicts with other shared borrows",
+ @future_incompatible = FutureIncompatibleInfo {
+ reference: "issue #59159 <https://github.com/rust-lang/rust/issues/59159>",
+ edition: None,
+ };
+}
+
+declare_lint! {
+ pub SOFT_UNSTABLE,
+ Deny,
+ "a feature gate that doesn't break dependent crates",
+ @future_incompatible = FutureIncompatibleInfo {
+ reference: "issue #64266 <https://github.com/rust-lang/rust/issues/64266>",
+ edition: None,
+ };
+}
+
+declare_lint_pass! {
+ /// Does nothing as a lint pass, but registers some `Lint`s
+ /// that are used by other parts of the compiler.
+ HardwiredLints => [
+ ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
+ EXCEEDING_BITSHIFTS,
+ UNUSED_IMPORTS,
+ UNUSED_EXTERN_CRATES,
+ UNUSED_QUALIFICATIONS,
+ UNKNOWN_LINTS,
+ UNUSED_VARIABLES,
+ UNUSED_ASSIGNMENTS,
+ DEAD_CODE,
+ UNREACHABLE_CODE,
+ UNREACHABLE_PATTERNS,
+ OVERLAPPING_PATTERNS,
+ BINDINGS_WITH_VARIANT_NAME,
+ UNUSED_MACROS,
+ WARNINGS,
+ UNUSED_FEATURES,
+ STABLE_FEATURES,
+ UNKNOWN_CRATE_TYPES,
+ TRIVIAL_CASTS,
+ TRIVIAL_NUMERIC_CASTS,
+ PRIVATE_IN_PUBLIC,
+ EXPORTED_PRIVATE_DEPENDENCIES,
+ PUB_USE_OF_PRIVATE_EXTERN_CRATE,
+ INVALID_TYPE_PARAM_DEFAULT,
+ CONST_ERR,
+ RENAMED_AND_REMOVED_LINTS,
+ SAFE_PACKED_BORROWS,
+ PATTERNS_IN_FNS_WITHOUT_BODY,
+ MISSING_FRAGMENT_SPECIFIER,
+ LATE_BOUND_LIFETIME_ARGUMENTS,
+ ORDER_DEPENDENT_TRAIT_OBJECTS,
+ DEPRECATED,
+ UNUSED_UNSAFE,
+ UNUSED_MUT,
+ UNCONDITIONAL_RECURSION,
+ SINGLE_USE_LIFETIMES,
+ UNUSED_LIFETIMES,
+ UNUSED_LABELS,
+ TYVAR_BEHIND_RAW_POINTER,
+ ELIDED_LIFETIMES_IN_PATHS,
+ BARE_TRAIT_OBJECTS,
+ ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE,
+ UNSTABLE_NAME_COLLISIONS,
+ IRREFUTABLE_LET_PATTERNS,
+ INTRA_DOC_LINK_RESOLUTION_FAILURE,
+ MISSING_DOC_CODE_EXAMPLES,
+ PRIVATE_DOC_TESTS,
+ WHERE_CLAUSES_OBJECT_SAFETY,
+ PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
+ MACRO_USE_EXTERN_CRATE,
+ MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS,
+ ILL_FORMED_ATTRIBUTE_INPUT,
+ META_VARIABLE_MISUSE,
+ DEPRECATED_IN_FUTURE,
+ AMBIGUOUS_ASSOCIATED_ITEMS,
+ MUTABLE_BORROW_RESERVATION_CONFLICT,
+ INDIRECT_STRUCTURAL_MATCH,
+ SOFT_UNSTABLE,
+ ]
+}
"set the current terminal width"),
panic_abort_tests: bool = (false, parse_bool, [TRACKED],
"support compiling tests with panic=abort"),
- continue_parse_after_error: bool = (false, parse_bool, [TRACKED],
- "attempt to recover from parse errors (experimental)"),
dep_tasks: bool = (false, parse_bool, [UNTRACKED],
"print tasks that execute and the color their dep node gets (requires debug build)"),
incremental: Option<String> = (None, parse_opt_string, [UNTRACKED],
//! Contains `ParseSess` which holds state living beyond what one `Parser` might.
//! It also serves as an input to the parser itself.
-use crate::lint::BufferedEarlyLint;
+use crate::lint::{BufferedEarlyLint, BuiltinLintDiagnostics, Lint, LintId};
use crate::node_id::NodeId;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::sync::{Lock, Lrc, Once};
-use rustc_errors::{
- emitter::SilentEmitter, Applicability, ColorConfig, DiagnosticBuilder, Handler,
-};
-use rustc_feature::UnstableFeatures;
+use rustc_error_codes::E0658;
+use rustc_errors::{emitter::SilentEmitter, ColorConfig, Handler};
+use rustc_errors::{error_code, Applicability, DiagnosticBuilder};
+use rustc_feature::{find_feature_issue, GateIssue, UnstableFeatures};
use rustc_span::edition::Edition;
use rustc_span::hygiene::ExpnId;
use rustc_span::source_map::{FilePathMapping, SourceMap};
}
}
+/// Construct a diagnostic for a language feature error due to the given `span`.
+/// The `feature`'s `Symbol` is the one you used in `active.rs` and `rustc_span::symbols`.
+pub fn feature_err<'a>(
+ sess: &'a ParseSess,
+ feature: Symbol,
+ span: impl Into<MultiSpan>,
+ explain: &str,
+) -> DiagnosticBuilder<'a> {
+ feature_err_issue(sess, feature, span, GateIssue::Language, explain)
+}
+
+/// Construct a diagnostic for a feature gate error.
+///
+/// This variant allows you to control whether it is a library or language feature.
+/// Almost always, you want to use this for a language feature. If so, prefer `feature_err`.
+pub fn feature_err_issue<'a>(
+ sess: &'a ParseSess,
+ feature: Symbol,
+ span: impl Into<MultiSpan>,
+ issue: GateIssue,
+ explain: &str,
+) -> DiagnosticBuilder<'a> {
+ let mut err = sess.span_diagnostic.struct_span_err_with_code(span, explain, error_code!(E0658));
+
+ if let Some(n) = find_feature_issue(feature, issue) {
+ err.note(&format!(
+ "for more information, see https://github.com/rust-lang/rust/issues/{}",
+ n,
+ ));
+ }
+
+ // #23973: do not suggest `#![feature(...)]` if we are in beta/stable
+ if sess.unstable_features.is_nightly_build() {
+ err.help(&format!("add `#![feature({})]` to the crate attributes to enable", feature));
+ }
+
+ err
+}
+
/// Info about a parsing session.
pub struct ParseSess {
pub span_diagnostic: Handler,
pub fn buffer_lint(
&self,
- lint_id: &'static crate::lint::Lint,
+ lint: &'static Lint,
span: impl Into<MultiSpan>,
- id: NodeId,
+ node_id: NodeId,
msg: &str,
) {
self.buffered_lints.with_lock(|buffered_lints| {
buffered_lints.push(BufferedEarlyLint {
span: span.into(),
- id,
+ node_id,
msg: msg.into(),
- lint_id,
+ lint_id: LintId::of(lint),
+ diagnostic: BuiltinLintDiagnostics::Normal,
});
});
}
See https://github.com/rust-lang/rust/issues/61002 for details.",
);
}
+
+ // Sanitizers can only be used on some tested platforms.
+ if let Some(ref sanitizer) = sess.opts.debugging_opts.sanitizer {
+ const ASAN_SUPPORTED_TARGETS: &[&str] =
+ &["x86_64-unknown-linux-gnu", "x86_64-apple-darwin"];
+ const TSAN_SUPPORTED_TARGETS: &[&str] =
+ &["x86_64-unknown-linux-gnu", "x86_64-apple-darwin"];
+ const LSAN_SUPPORTED_TARGETS: &[&str] =
+ &["x86_64-unknown-linux-gnu", "x86_64-apple-darwin"];
+ const MSAN_SUPPORTED_TARGETS: &[&str] = &["x86_64-unknown-linux-gnu"];
+
+ let supported_targets = match *sanitizer {
+ Sanitizer::Address => ASAN_SUPPORTED_TARGETS,
+ Sanitizer::Thread => TSAN_SUPPORTED_TARGETS,
+ Sanitizer::Leak => LSAN_SUPPORTED_TARGETS,
+ Sanitizer::Memory => MSAN_SUPPORTED_TARGETS,
+ };
+
+ if !supported_targets.contains(&&*sess.opts.target_triple.triple()) {
+ sess.err(&format!(
+ "{:?}Sanitizer only works with the `{}` target",
+ sanitizer,
+ supported_targets.join("` or `")
+ ));
+ }
+ }
}
/// Hash value constructed out of all the `-C metadata` arguments passed to the
use rustc_data_structures::profiling::VerboseTimingGuard;
impl Session {
- pub fn timer<'a>(&'a self, what: &'a str) -> VerboseTimingGuard<'a> {
- self.prof.sparse_pass(what)
+ pub fn timer<'a>(&'a self, what: &'static str) -> VerboseTimingGuard<'a> {
+ self.prof.verbose_generic_activity(what)
}
- pub fn time<R>(&self, what: &str, f: impl FnOnce() -> R) -> R {
- self.prof.sparse_pass(what).run(f)
+ pub fn time<R>(&self, what: &'static str, f: impl FnOnce() -> R) -> R {
+ self.prof.verbose_generic_activity(what).run(f)
}
}
//! This API is completely unstable and subject to change.
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
-#![feature(const_fn)]
#![feature(crate_visibility_modifier)]
#![feature(nll)]
#![feature(optin_builtin_traits)]
self.ctxt() != SyntaxContext::root()
}
+ /// Returns `true` if `span` originates in a derive-macro's expansion.
+ pub fn in_derive_expansion(self) -> bool {
+ matches!(self.ctxt().outer_expn_data().kind, ExpnKind::Macro(MacroKind::Derive, _))
+ }
+
#[inline]
pub fn with_root_ctxt(lo: BytePos, hi: BytePos) -> Span {
Span::new(lo, hi, SyntaxContext::root())
// and to the end of the line. Be careful because the line
// numbers in Loc are 1-based, so we subtract 1 to get 0-based
// lines.
- for line_index in lo.line - 1..hi.line - 1 {
+ let hi_line = hi.line.saturating_sub(1);
+ for line_index in lo.line.saturating_sub(1)..hi_line {
let line_len = lo.file.get_line(line_index).map(|s| s.chars().count()).unwrap_or(0);
lines.push(LineInfo { line_index, start_col, end_col: CharPos::from_usize(line_len) });
start_col = CharPos::from_usize(0);
}
// For the last line, it extends from `start_col` to `hi.col`:
- lines.push(LineInfo { line_index: hi.line - 1, start_col, end_col: hi.col });
+ lines.push(LineInfo { line_index: hi_line, start_col, end_col: hi.col });
Ok(FileLines { file: lo.file, lines })
}
pub fn next_point(&self, sp: Span) -> Span {
let start_of_next_point = sp.hi().0;
- let width = self.find_width_of_character_at_span(sp, true);
+ let width = self.find_width_of_character_at_span(sp.shrink_to_hi(), true);
// If the width is 1, then the next span should point to the same `lo` and `hi`. However,
// in the case of a multibyte character, where the width != 1, the next span should
// span multiple bytes to include the whole character.
const_raw_ptr_deref,
const_raw_ptr_to_usize_cast,
const_transmute,
+ const_trait_bound_opt_out,
+ const_trait_impl,
contents,
context,
convert,
global_allocator,
global_asm,
globs,
+ half_open_range_patterns,
hash,
Hash,
HashSet,
}
// This module has a very short name because it's used a lot.
+#[allow(rustc::default_hash_types)]
pub mod sym {
use super::Symbol;
use std::convert::TryInto;
target_endian: "little".to_string(),
target_pointer_width: "32".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:o-p:32:32-f64:32:64-f80:128-n8:16:32-S128".to_string(),
+ data_layout: "e-m:o-p:32:32-p270:32:32-p271:32:32-p272:64:64-\
+ f64:32:64-f80:128-n8:16:32-S128"
+ .to_string(),
arch: "x86".to_string(),
target_os: "ios".to_string(),
target_env: String::new(),
target_endian: "little".to_string(),
target_pointer_width: "32".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:o-p:32:32-f64:32:64-f80:128-n8:16:32-S128".to_string(),
+ data_layout: "e-m:o-p:32:32-p270:32:32-p271:32:32-p272:64:64-\
+ f64:32:64-f80:128-n8:16:32-S128"
+ .to_string(),
arch: "x86".to_string(),
target_os: "macos".to_string(),
target_env: String::new(),
target_endian: "little".to_string(),
target_pointer_width: "32".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128".to_string(),
+ data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\
+ f64:32:64-f80:32-n8:16:32-S128"
+ .to_string(),
arch: "x86".to_string(),
target_os: "android".to_string(),
target_env: String::new(),
target_endian: "little".to_string(),
target_pointer_width: "32".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:x-p:32:32-i64:64-f80:32-n8:16:32-a:0:32-S32".to_string(),
+ data_layout: "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-\
+ i64:64-f80:32-n8:16:32-a:0:32-S32"
+ .to_string(),
arch: "x86".to_string(),
target_os: "windows".to_string(),
target_env: "gnu".to_string(),
target_endian: "little".to_string(),
target_pointer_width: "32".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:x-p:32:32-i64:64-f80:32-n8:16:32-a:0:32-S32".to_string(),
+ data_layout: "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-\
+ i64:64-f80:32-n8:16:32-a:0:32-S32"
+ .to_string(),
arch: "x86".to_string(),
target_os: "windows".to_string(),
target_env: "msvc".to_string(),
target_endian: "little".to_string(),
target_pointer_width: "32".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128".to_string(),
+ data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\
+ f64:32:64-f80:32-n8:16:32-S128"
+ .to_string(),
arch: "x86".to_string(),
target_os: "cloudabi".to_string(),
target_env: String::new(),
target_endian: "little".to_string(),
target_pointer_width: "32".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128".to_string(),
+ data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\
+ f64:32:64-f80:32-n8:16:32-S128"
+ .to_string(),
arch: "x86".to_string(),
target_os: "freebsd".to_string(),
target_env: String::new(),
target_endian: "little".to_string(),
target_pointer_width: "32".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128".to_string(),
+ data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\
+ f64:32:64-f80:32-n8:16:32-S128"
+ .to_string(),
arch: "x86".to_string(),
target_os: "haiku".to_string(),
target_env: String::new(),
target_endian: "little".to_string(),
target_pointer_width: "32".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128".to_string(),
+ data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\
+ f64:32:64-f80:32-n8:16:32-S128"
+ .to_string(),
arch: "x86".to_string(),
target_os: "linux".to_string(),
target_env: "gnu".to_string(),
target_endian: "little".to_string(),
target_pointer_width: "32".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128".to_string(),
+ data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\
+ f64:32:64-f80:32-n8:16:32-S128"
+ .to_string(),
arch: "x86".to_string(),
target_os: "linux".to_string(),
target_env: "musl".to_string(),
target_endian: "little".to_string(),
target_pointer_width: "32".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128".to_string(),
+ data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\
+ f64:32:64-f80:32-n8:16:32-S128"
+ .to_string(),
arch: "x86".to_string(),
target_os: "netbsd".to_string(),
target_env: String::new(),
target_endian: "little".to_string(),
target_pointer_width: "32".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128".to_string(),
+ data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\
+ f64:32:64-f80:32-n8:16:32-S128"
+ .to_string(),
arch: "x86".to_string(),
target_os: "openbsd".to_string(),
target_env: String::new(),
target_endian: "little".to_string(),
target_pointer_width: "32".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:x-p:32:32-i64:64-f80:32-n8:16:32-a:0:32-S32".to_string(),
+ data_layout: "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-\
+ i64:64-f80:32-n8:16:32-a:0:32-S32"
+ .to_string(),
target_os: "uefi".to_string(),
target_env: "".to_string(),
target_vendor: "unknown".to_string(),
target_endian: "little".to_string(),
target_pointer_width: "32".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:x-p:32:32-i64:64-f80:32-n8:16:32-a:0:32-S32".to_string(),
+ data_layout: "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-\
+ i64:64-f80:32-n8:16:32-a:0:32-S32"
+ .to_string(),
arch: "x86".to_string(),
target_os: "windows".to_string(),
target_env: "gnu".to_string(),
target_endian: "little".to_string(),
target_pointer_width: "32".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:x-p:32:32-i64:64-f80:32-n8:16:32-a:0:32-S32".to_string(),
+ data_layout: "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-\
+ i64:64-f80:32-n8:16:32-a:0:32-S32"
+ .to_string(),
arch: "x86".to_string(),
target_os: "windows".to_string(),
target_env: "msvc".to_string(),
target_endian: "little".to_string(),
target_pointer_width: "32".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128".to_string(),
+ data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\
+ f64:32:64-f80:32-n8:16:32-S128"
+ .to_string(),
arch: "x86".to_string(),
target_os: "vxworks".to_string(),
target_env: "gnu".to_string(),
/// Whether or not RelaxElfRelocation flag will be passed to the linker
pub relax_elf_relocations: bool,
+
+ /// Additional arguments to pass to LLVM, similar to the `-C llvm-args` codegen option.
+ pub llvm_args: Vec<String>,
}
impl Default for TargetOptions {
target_mcount: "mcount".to_string(),
llvm_abiname: "".to_string(),
relax_elf_relocations: false,
+ llvm_args: vec![],
}
}
}
key!(target_mcount);
key!(llvm_abiname);
key!(relax_elf_relocations, bool);
+ key!(llvm_args, list);
if let Some(array) = obj.find("abi-blacklist").and_then(Json::as_array) {
for name in array.iter().filter_map(|abi| abi.as_string()) {
target_option_val!(target_mcount);
target_option_val!(llvm_abiname);
target_option_val!(relax_elf_relocations);
+ target_option_val!(llvm_args);
if default.abi_blacklist != self.options.abi_blacklist {
d.insert(
target_endian: "little".to_string(),
target_pointer_width: "64".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:o-i64:64-f80:128-n8:16:32:64-S128".to_string(),
+ data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .to_string(),
arch: arch.to_string(),
target_os: "macos".to_string(),
target_env: String::new(),
target_endian: "little".to_string(),
target_pointer_width: "64".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:o-i64:64-f80:128-n8:16:32:64-S128".to_string(),
+ data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .to_string(),
arch: "x86_64".to_string(),
target_os: "ios".to_string(),
target_env: String::new(),
target_endian: "little".to_string(),
target_pointer_width: "64".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:o-i64:64-f80:128-n8:16:32:64-S128".to_string(),
+ data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .to_string(),
arch: "x86_64".to_string(),
target_os: "ios".to_string(),
target_env: String::new(),
target_os: "unknown".into(),
target_env: "sgx".into(),
target_vendor: "fortanix".into(),
- data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".into(),
+ data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .into(),
arch: "x86_64".into(),
linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
options: opts,
target_endian: "little".to_string(),
target_pointer_width: "64".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(),
+ data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .to_string(),
arch: "x86_64".to_string(),
target_os: "fuchsia".to_string(),
target_env: String::new(),
target_endian: "little".to_string(),
target_pointer_width: "64".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(),
+ data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .to_string(),
arch: "x86_64".to_string(),
target_os: "android".to_string(),
target_env: String::new(),
target_endian: "little".to_string(),
target_pointer_width: "64".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(),
+ data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .to_string(),
target_os: "none".to_string(),
target_env: "gnu".to_string(),
target_vendor: "unknown".to_string(),
target_endian: "little".to_string(),
target_pointer_width: "64".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:w-i64:64-f80:128-n8:16:32:64-S128".to_string(),
+ data_layout: "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .to_string(),
arch: "x86_64".to_string(),
target_os: "windows".to_string(),
target_env: "gnu".to_string(),
target_endian: "little".to_string(),
target_pointer_width: "64".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:w-i64:64-f80:128-n8:16:32:64-S128".to_string(),
+ data_layout: "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .to_string(),
arch: "x86_64".to_string(),
target_os: "windows".to_string(),
target_env: "msvc".to_string(),
target_endian: "little".to_string(),
target_pointer_width: "64".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(),
+ data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .to_string(),
arch: "x86_64".to_string(),
target_os: "netbsd".to_string(),
target_env: String::new(),
target_endian: "little".to_string(),
target_pointer_width: "64".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(),
+ data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .to_string(),
arch: "x86_64".to_string(),
target_os: "solaris".to_string(),
target_env: String::new(),
target_endian: "little".to_string(),
target_pointer_width: "64".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(),
+ data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .to_string(),
arch: "x86_64".to_string(),
target_os: "cloudabi".to_string(),
target_env: String::new(),
target_endian: "little".to_string(),
target_pointer_width: "64".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(),
+ data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .to_string(),
arch: "x86_64".to_string(),
target_os: "dragonfly".to_string(),
target_env: String::new(),
target_endian: "little".to_string(),
target_pointer_width: "64".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(),
+ data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .to_string(),
arch: "x86_64".to_string(),
target_os: "freebsd".to_string(),
target_env: String::new(),
target_endian: "little".to_string(),
target_pointer_width: "64".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(),
+ data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .to_string(),
arch: "x86_64".to_string(),
target_os: "haiku".to_string(),
target_env: String::new(),
target_endian: "little".to_string(),
target_pointer_width: "64".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(),
+ data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .to_string(),
arch: "x86_64".to_string(),
target_os: "hermit".to_string(),
target_env: String::new(),
target_endian: "little".to_string(),
target_pointer_width: "64".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(),
+ data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .to_string(),
arch: "x86_64".to_string(),
target_os: "hermit".to_string(),
target_env: String::new(),
target_endian: "little".to_string(),
target_pointer_width: "64".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(),
+ data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .to_string(),
arch: "x86_64".to_string(),
target_os: "l4re".to_string(),
target_env: "uclibc".to_string(),
target_endian: "little".to_string(),
target_pointer_width: "64".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(),
+ data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .to_string(),
arch: "x86_64".to_string(),
target_os: "linux".to_string(),
target_env: "gnu".to_string(),
target_endian: "little".to_string(),
target_pointer_width: "32".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:e-p:32:32-i64:64-f80:128-n8:16:32:64-S128".to_string(),
+ data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\
+ i64:64-f80:128-n8:16:32:64-S128"
+ .to_string(),
arch: "x86_64".to_string(),
target_os: "linux".to_string(),
target_env: "gnu".to_string(),
target_endian: "little".to_string(),
target_pointer_width: "64".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(),
+ data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .to_string(),
arch: "x86_64".to_string(),
target_os: "linux".to_string(),
target_env: "musl".to_string(),
target_endian: "little".to_string(),
target_pointer_width: "64".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(),
+ data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .to_string(),
arch: "x86_64".to_string(),
target_os: "netbsd".to_string(),
target_env: String::new(),
target_endian: "little".to_string(),
target_pointer_width: "64".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(),
+ data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .to_string(),
arch: "x86_64".to_string(),
target_os: "openbsd".to_string(),
target_env: String::new(),
target_endian: "little".to_string(),
target_pointer_width: "64".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(),
+ data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .to_string(),
arch: "x86_64".to_string(),
target_os: "redox".to_string(),
target_env: "relibc".to_string(),
target_endian: "little".to_string(),
target_pointer_width: "64".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:w-i64:64-f80:128-n8:16:32:64-S128".to_string(),
+ data_layout: "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .to_string(),
target_os: "uefi".to_string(),
target_env: "".to_string(),
target_vendor: "unknown".to_string(),
target_endian: "little".to_string(),
target_pointer_width: "64".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:w-i64:64-f80:128-n8:16:32:64-S128".to_string(),
+ data_layout: "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .to_string(),
arch: "x86_64".to_string(),
target_os: "windows".to_string(),
target_env: "gnu".to_string(),
target_endian: "little".to_string(),
target_pointer_width: "64".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:w-i64:64-f80:128-n8:16:32:64-S128".to_string(),
+ data_layout: "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .to_string(),
arch: "x86_64".to_string(),
target_os: "windows".to_string(),
target_env: "msvc".to_string(),
target_endian: "little".to_string(),
target_pointer_width: "64".to_string(),
target_c_int_width: "32".to_string(),
- data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(),
+ data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .to_string(),
arch: "x86_64".to_string(),
target_os: "vxworks".to_string(),
target_env: "gnu".to_string(),
use rustc::infer::InferCtxt;
use rustc::traits::query::outlives_bounds::OutlivesBound;
use rustc::traits::query::{CanonicalTyGoal, Fallible, NoSolution};
+use rustc::traits::wf;
use rustc::traits::FulfillmentContext;
use rustc::traits::{TraitEngine, TraitEngineExt};
use rustc::ty::outlives::Component;
use rustc::ty::query::Providers;
-use rustc::ty::wf;
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
use rustc_hir as hir;
use rustc_span::source_map::DUMMY_SP;
mod environment;
-use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc::hir::map::definitions::DefPathData;
+use rustc::hir::map::Map;
use rustc::traits::{
Clause, Clauses, DomainGoal, FromEnv, GoalKind, PolyDomainGoal, ProgramClause,
ProgramClauseCategory, WellFormed, WhereClause,
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId;
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_span::symbol::sym;
use syntax::ast;
}
impl Visitor<'tcx> for ClauseDumper<'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, Self::Map> {
NestedVisitorMap::OnlyBodies(&self.tcx.hir())
}
+++ /dev/null
-[package]
-authors = ["The Rust Project Developers"]
-build = "build.rs"
-name = "rustc_tsan"
-version = "0.0.0"
-edition = "2018"
-
-[lib]
-name = "rustc_tsan"
-path = "lib.rs"
-test = false
-
-[build-dependencies]
-build_helper = { path = "../build_helper" }
-cmake = "0.1.38"
-
-[dependencies]
-alloc = { path = "../liballoc" }
-core = { path = "../libcore" }
-compiler_builtins = "0.1.0"
+++ /dev/null
-use build_helper::sanitizer_lib_boilerplate;
-use std::env;
-
-use cmake::Config;
-
-fn main() {
- println!("cargo:rerun-if-env-changed=RUSTC_BUILD_SANITIZERS");
- if env::var("RUSTC_BUILD_SANITIZERS") != Ok("1".to_string()) {
- return;
- }
- if let Some(llvm_config) = env::var_os("LLVM_CONFIG") {
- build_helper::restore_library_path();
-
- let (native, target) = match sanitizer_lib_boilerplate("tsan") {
- Ok(native) => native,
- _ => return,
- };
-
- Config::new(&native.src_dir)
- .define("COMPILER_RT_BUILD_SANITIZERS", "ON")
- .define("COMPILER_RT_BUILD_BUILTINS", "OFF")
- .define("COMPILER_RT_BUILD_XRAY", "OFF")
- .define("LLVM_CONFIG_PATH", llvm_config)
- .out_dir(&native.out_dir)
- .build_target(&target)
- .build();
- native.fixup_sanitizer_lib_name("tsan");
- }
- println!("cargo:rerun-if-env-changed=LLVM_CONFIG");
-}
+++ /dev/null
-#![sanitizer_runtime]
-#![feature(nll)]
-#![feature(sanitizer_runtime)]
-#![feature(staged_api)]
-#![no_std]
-#![unstable(
- feature = "sanitizer_runtime_lib",
- reason = "internal implementation detail of sanitizers",
- issue = "none"
-)]
log = "0.4"
rustc = { path = "../librustc" }
rustc_data_structures = { path = "../librustc_data_structures" }
+rustc_errors = { path = "../librustc_errors" }
rustc_hir = { path = "../librustc_hir" }
-errors = { path = "../librustc_errors", package = "rustc_errors" }
rustc_target = { path = "../librustc_target" }
smallvec = { version = "1.0", features = ["union", "may_dangle"] }
syntax = { path = "../libsyntax" }
use crate::namespace::Namespace;
use crate::require_c_abi_if_c_variadic;
use crate::util::common::ErrorReported;
-use errors::{Applicability, DiagnosticId};
-use rustc::hir::intravisit::Visitor;
use rustc::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS;
+use rustc::session::parse::feature_err;
use rustc::traits;
+use rustc::traits::astconv_object_safety_violations;
+use rustc::traits::error_reporting::report_object_safety_error;
+use rustc::traits::wf::object_region_bounds;
use rustc::ty::subst::{self, InternalSubsts, Subst, SubstsRef};
-use rustc::ty::wf::object_region_bounds;
use rustc::ty::{self, Const, DefIdTree, ToPredicate, Ty, TyCtxt, TypeFoldable};
use rustc::ty::{GenericParamDef, GenericParamDefKind};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId};
use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind, Res};
use rustc_hir::def_id::DefId;
+use rustc_hir::intravisit::Visitor;
use rustc_hir::print;
use rustc_hir::{ExprKind, GenericArg, GenericArgs};
use rustc_span::symbol::sym;
use rustc_target::spec::abi;
use smallvec::SmallVec;
use syntax::ast;
-use syntax::errors::pluralize;
-use syntax::feature_gate::feature_err;
use syntax::util::lev_distance::find_best_match_for_name;
use std::collections::BTreeSet;
if unbound.is_none() {
unbound = Some(&ptr.trait_ref);
} else {
- span_err!(
+ struct_span_err!(
tcx.sess,
span,
E0203,
"type parameter has more than one relaxed default \
bound, only one is supported"
- );
+ )
+ .emit();
}
}
}
}
if regular_traits.is_empty() && auto_traits.is_empty() {
- span_err!(tcx.sess, span, E0224, "at least one trait is required for an object type");
+ struct_span_err!(
+ tcx.sess,
+ span,
+ E0224,
+ "at least one trait is required for an object type"
+ )
+ .emit();
return tcx.types.err;
}
// to avoid ICEs.
for item in ®ular_traits {
let object_safety_violations =
- tcx.astconv_object_safety_violations(item.trait_ref().def_id());
+ astconv_object_safety_violations(tcx, item.trait_ref().def_id());
if !object_safety_violations.is_empty() {
- tcx.report_object_safety_error(
+ report_object_safety_error(
+ tcx,
span,
item.trait_ref().def_id(),
object_safety_violations,
self.ast_region_to_region(lifetime, None)
} else {
self.re_infer(None, span).unwrap_or_else(|| {
- span_err!(
+ struct_span_err!(
tcx.sess,
span,
E0228,
"the lifetime bound for this object type cannot be deduced \
from context; please supply an explicit bound"
- );
+ )
+ .emit();
tcx.lifetimes.re_static
})
}
let msg = format!("expected type, found variant `{}`", assoc_ident);
tcx.sess.span_err(span, &msg);
} else if qself_ty.is_enum() {
- let mut err = tcx.sess.struct_span_err(
+ let mut err = struct_span_err!(
+ tcx.sess,
assoc_ident.span,
- &format!("no variant `{}` in enum `{}`", assoc_ident, qself_ty),
+ E0599,
+ "no variant named `{}` found for enum `{}`",
+ assoc_ident,
+ qself_ty,
);
let adt_def = qself_ty.ty_adt_def().expect("enum is not an ADT");
let def_id = tcx.hir().local_def_id(ast_const.hir_id);
let mut const_ = ty::Const {
- val: ty::ConstKind::Unevaluated(def_id, InternalSubsts::identity_for_item(tcx, def_id)),
+ val: ty::ConstKind::Unevaluated(
+ def_id,
+ InternalSubsts::identity_for_item(tcx, def_id),
+ None,
+ ),
ty,
};
// allowed. `allow_ty_infer` gates this behavior.
crate::collect::placeholder_type_error(
tcx,
- ident_span.unwrap_or(DUMMY_SP),
+ ident_span.map(|sp| sp.shrink_to_hi()).unwrap_or(DUMMY_SP),
generic_params,
visitor.0,
ident_span.is_some(),
// error.
let r = derived_region_bounds[0];
if derived_region_bounds[1..].iter().any(|r1| r != *r1) {
- span_err!(
+ struct_span_err!(
tcx.sess,
span,
E0227,
"ambiguous lifetime bound, explicit lifetime bound required"
- );
+ )
+ .emit();
}
return Some(r);
}
use rustc::ty::adjustment::{Adjust, Adjustment, OverloadedDeref};
use rustc::ty::{self, TraitRef, Ty, TyCtxt};
use rustc::ty::{ToPredicate, TypeFoldable};
+use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_span::Span;
use super::autoderef::Autoderef;
use super::method::MethodCallee;
use super::{Expectation, FnCtxt, Needs, TupleArgumentsFlag};
+use crate::type_error_struct;
-use errors::{Applicability, DiagnosticBuilder};
-use hir::def::Res;
-use hir::def_id::{DefId, LOCAL_CRATE};
use rustc::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
use rustc::ty::subst::SubstsRef;
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
use rustc::{infer, traits};
+use rustc_error_codes::*;
+use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
+use rustc_hir as hir;
+use rustc_hir::def::Res;
+use rustc_hir::def_id::{DefId, LOCAL_CRATE};
use rustc_span::Span;
use rustc_target::spec::abi;
use syntax::ast::Ident;
-use rustc_hir as hir;
-
-use rustc_error_codes::*;
-
/// Checks that it is legal to call methods of the trait corresponding
/// to `trait_id` (this only cares about the trait, not the specific
/// method that is called).
) = (parent_node, callee_node)
{
let start = sp.shrink_to_lo();
- let end = self.tcx.sess.source_map().next_point(callee_span);
+ let end = callee_span.shrink_to_hi();
err.multipart_suggestion(
"if you meant to create this closure and immediately call it, surround the \
closure with parenthesis",
let call_is_multiline =
self.tcx.sess.source_map().is_multiline(call_expr.span);
if call_is_multiline {
- let span = self.tcx.sess.source_map().next_point(callee.span);
err.span_suggestion(
- span,
+ callee.span.shrink_to_hi(),
"try adding a semicolon",
";".to_owned(),
Applicability::MaybeIncorrect,
use crate::hir::def_id::DefId;
use crate::lint;
+use crate::type_error_struct;
use crate::util::common::ErrorReported;
-use errors::{Applicability, DiagnosticBuilder};
use rustc::middle::lang_items;
use rustc::session::Session;
use rustc::traits;
+use rustc::traits::error_reporting::report_object_safety_error;
+use rustc::traits::object_safety_violations;
use rustc::ty::adjustment::AllowTwoPhase;
use rustc::ty::cast::{CastKind, CastTy};
use rustc::ty::error::TypeError;
use rustc::ty::subst::SubstsRef;
use rustc::ty::{self, Ty, TypeAndMut, TypeFoldable};
+use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
use rustc_hir as hir;
use rustc_span::Span;
use syntax::ast;
);
}
Err(_) => {
- span_help!(err, self.cast_span, "did you mean `&{}{}`?", mtstr, tstr)
+ let msg = &format!("did you mean `&{}{}`?", mtstr, tstr);
+ err.span_help(self.cast_span, msg);
}
}
} else {
- span_help!(
- err,
- self.span,
+ let msg = &format!(
"consider using an implicit coercion to `&{}{}` instead",
- mtstr,
- tstr
+ mtstr, tstr
);
+ err.span_help(self.span, msg);
}
}
ty::Adt(def, ..) if def.is_box() => {
Applicability::MachineApplicable,
);
}
- Err(_) => span_help!(err, self.cast_span, "did you mean `Box<{}>`?", tstr),
+ Err(_) => {
+ err.span_help(self.cast_span, &format!("did you mean `Box<{}>`?", tstr));
+ }
}
}
_ => {
- span_help!(err, self.expr.span, "consider using a box or reference as appropriate");
+ err.span_help(self.expr.span, "consider using a box or reference as appropriate");
}
}
err.emit();
}
fn report_object_unsafe_cast(&self, fcx: &FnCtxt<'a, 'tcx>, did: DefId) {
- let violations = fcx.tcx.object_safety_violations(did);
- let mut err = fcx.tcx.report_object_safety_error(self.cast_span, did, violations);
+ let violations = object_safety_violations(fcx.tcx, did);
+ let mut err = report_object_safety_error(fcx.tcx, self.cast_span, did, violations);
err.note(&format!("required by cast to type '{}'", fcx.ty_to_string(self.cast_ty)));
err.emit();
}
//! we may want to adjust precisely when coercions occur.
use crate::check::{FnCtxt, Needs};
-use errors::DiagnosticBuilder;
use rustc::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc::infer::{Coercion, InferOk, InferResult};
+use rustc::session::parse::feature_err;
use rustc::traits::{self, ObligationCause, ObligationCauseCode};
use rustc::ty::adjustment::{
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCast,
use rustc::ty::relate::RelateResult;
use rustc::ty::subst::SubstsRef;
use rustc::ty::{self, Ty, TypeAndMut};
+use rustc_error_codes::*;
+use rustc_errors::{struct_span_err, DiagnosticBuilder};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_span;
use rustc_target::spec::abi::Abi;
use smallvec::{smallvec, SmallVec};
use std::ops::Deref;
-use syntax::feature_gate;
-
-use rustc_error_codes::*;
struct Coerce<'a, 'tcx> {
fcx: &'a FnCtxt<'a, 'tcx>,
// and almost never more than 3. By using a SmallVec we avoid an
// allocation, at the (very small) cost of (occasionally) having to
// shift subsequent elements down when removing the front element.
- let mut queue: SmallVec<[_; 4]> = smallvec![self.tcx.predicate_for_trait_def(
+ let mut queue: SmallVec<[_; 4]> = smallvec![traits::predicate_for_trait_def(
+ self.tcx,
self.fcx.param_env,
cause,
coerce_unsized_did,
}
if has_unsized_tuple_coercion && !self.tcx.features().unsized_tuple_coercion {
- feature_gate::feature_err(
+ feature_err(
&self.tcx.sess.parse_sess,
sym::unsized_tuple_coercion,
self.cause.span,
-use errors::{Applicability, DiagnosticId};
-use rustc::hir::intravisit;
+use rustc::hir::map::Map;
use rustc::infer::{self, InferOk};
use rustc::traits::{self, ObligationCause, ObligationCauseCode, Reveal};
use rustc::ty::error::{ExpectedFound, TypeError};
use rustc::ty::util::ExplicitSelf;
use rustc::ty::{self, GenericParamDefKind, TyCtxt};
use rustc::util::common::ErrorReported;
+use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
+use rustc_hir::intravisit;
use rustc_hir::{GenericParamKind, ImplItemKind, TraitItemKind};
use rustc_span::Span;
-use syntax::errors::pluralize;
use super::{potentially_plural_count, FnCtxt, Inherited};
}
}
}
- fn nested_visit_map<'this>(
- &'this mut self,
- ) -> intravisit::NestedVisitorMap<'this, 'v>
+ type Map = Map<'v>;
+ fn nested_visit_map(
+ &mut self,
+ ) -> intravisit::NestedVisitorMap<'_, Self::Map>
{
intravisit::NestedVisitorMap::None
}
use rustc::infer::InferOk;
use rustc::traits::{self, ObligationCause};
-use errors::{Applicability, DiagnosticBuilder};
use rustc::ty::adjustment::AllowTwoPhase;
use rustc::ty::{self, AssocItem, Ty};
+use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_hir as hir;
use rustc_hir::{is_range_literal, print, Node};
use rustc_span::symbol::sym;
use crate::check::regionck::RegionCtxt;
-
use crate::hir;
use crate::hir::def_id::DefId;
use crate::util::common::ErrorReported;
use rustc::ty::relate::{Relate, RelateResult, TypeRelation};
use rustc::ty::subst::{Subst, SubstsRef};
use rustc::ty::{self, Predicate, Ty, TyCtxt};
+use rustc_errors::struct_span_err;
use rustc_span::Span;
use crate::check::FnCtxt;
use crate::check::Needs;
use crate::check::TupleArgumentsFlag::DontTupleArguments;
+use crate::type_error_struct;
use crate::util::common::ErrorReported;
-use errors::{pluralize, Applicability, DiagnosticBuilder, DiagnosticId};
use rustc::infer;
use rustc::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc::middle::lang_items;
use rustc::ty::TypeFoldable;
use rustc::ty::{AdtKind, Visibility};
use rustc_data_structures::fx::FxHashMap;
+use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, DiagnosticId};
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def_id::DefId;
// Prohibit struct expressions when non-exhaustive flag is set.
let adt = adt_ty.ty_adt_def().expect("`check_struct_path` returned non-ADT type");
if !adt.did.is_local() && variant.is_field_list_non_exhaustive() {
- span_err!(
+ struct_span_err!(
self.tcx.sess,
expr.span,
E0639,
"cannot create non-exhaustive {} using struct expression",
adt.variant_descr()
- );
+ )
+ .emit();
}
let error_happened = self.check_expr_struct_fields(
.insert(expr.hir_id, fru_field_types);
}
_ => {
- span_err!(
+ struct_span_err!(
self.tcx.sess,
base_expr.span,
E0436,
"functional record update syntax requires a struct"
- );
+ )
+ .emit();
}
}
}
//! types computed here.
use super::FnCtxt;
-use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor};
+use rustc::hir::map::Map;
use rustc::middle::region::{self, YieldData};
use rustc::ty::{self, Ty};
use rustc_data_structures::fx::FxHashMap;
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def_id::DefId;
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_hir::{Expr, ExprKind, Pat, PatKind};
use rustc_span::Span;
// librustc/middle/region.rs since `expr_count` is compared against the results
// there.
impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> {
NestedVisitorMap::None
}
//! intrinsics that the compiler exposes.
use crate::require_same_types;
+
use rustc::traits::{ObligationCause, ObligationCauseCode};
use rustc::ty::subst::Subst;
use rustc::ty::{self, Ty, TyCtxt};
-
+use rustc_error_codes::*;
+use rustc_errors::struct_span_err;
+use rustc_hir as hir;
use rustc_span::symbol::Symbol;
use rustc_target::spec::abi::Abi;
-use rustc_hir as hir;
-
-use rustc_error_codes::*;
-
use std::iter;
fn equate_intrinsic_type<'tcx>(
(2, params, param(1))
}
Err(_) => {
- span_err!(
+ struct_span_err!(
tcx.sess,
it.span,
E0439,
"invalid `simd_shuffle`, needs length: `{}`",
name
- );
+ )
+ .emit();
return;
}
},
_ => {
let msg = format!("unrecognized platform-specific intrinsic function: `{}`", name);
- tcx.sess.span_err(it.span, &msg);
+ tcx.sess.struct_span_err(it.span, &msg).emit();
return;
}
};
target_trait_def_id: DefId,
) -> ty::PolyTraitRef<'tcx> {
let upcast_trait_refs =
- self.tcx.upcast_choices(source_trait_ref.clone(), target_trait_def_id);
+ traits::upcast_choices(self.tcx, source_trait_ref.clone(), target_trait_def_id);
// must be exactly one trait ref or we'd get an ambig error etc
if upcast_trait_refs.len() != 1 {
use crate::check::FnCtxt;
use crate::namespace::Namespace;
-use errors::{Applicability, DiagnosticBuilder};
use rustc::infer::{self, InferOk};
use rustc::traits;
use rustc::ty::subst::Subst;
use rustc::ty::GenericParamDefKind;
use rustc::ty::{self, ToPolyTraitRef, ToPredicate, TraitRef, Ty, TypeFoldable};
use rustc_data_structures::sync::Lrc;
+use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind};
use rustc_hir::def_id::DefId;
};
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::sync::Lrc;
+use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_span::{symbol::Symbol, Span, DUMMY_SP};
use std::cmp::max;
// so we do a future-compat lint here for the 2015 edition
// (see https://github.com/rust-lang/rust/issues/46906)
if self.tcx.sess.rust_2018() {
- span_err!(
+ struct_span_err!(
self.tcx.sess,
span,
E0699,
"the type of this value must be known \
to call a method on a raw pointer on it"
- );
+ )
+ .emit();
} else {
self.tcx.lint_hir(
lint::builtin::TYVAR_BEHIND_RAW_POINTER,
use crate::check::FnCtxt;
use crate::middle::lang_items::FnOnceTraitLangItem;
use crate::namespace::Namespace;
-use errors::{pluralize, Applicability, DiagnosticBuilder};
-use rustc::hir::intravisit;
use rustc::hir::map as hir_map;
+use rustc::hir::map::Map;
use rustc::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc::traits::Obligation;
use rustc::ty::print::with_crate_prefix;
use rustc::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable};
use rustc_data_structures::fx::FxHashSet;
+use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
+use rustc_hir::intravisit;
use rustc_hir::{ExprKind, Node, QPath};
use rustc_span::{source_map, FileName, Span};
use syntax::ast;
let item_span =
self.tcx.sess.source_map().def_span(self.tcx.def_span(item.def_id));
let idx = if sources.len() > 1 {
- span_note!(
- err,
- item_span,
+ let msg = &format!(
"candidate #{} is defined in the trait `{}`",
idx + 1,
self.tcx.def_path_str(trait_did)
);
+ err.span_note(item_span, msg);
Some(idx + 1)
} else {
- span_note!(
- err,
- item_span,
+ let msg = &format!(
"the candidate is defined in the trait `{}`",
self.tcx.def_path_str(trait_did)
);
+ err.span_note(item_span, msg);
None
};
let path = self.tcx.def_path_str(trait_did);
tcx.sess,
span,
E0599,
- "no {} named `{}` found for type `{}` in the current scope",
+ "no {} named `{}` found for {} `{}` in the current scope",
item_kind,
item_name,
- ty_str
+ actual.prefix_string(),
+ ty_str,
);
if let Some(span) =
tcx.sess.confused_type_with_std_module.borrow().get(&span)
}
}
- fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<'_, Self::Map> {
intravisit::NestedVisitorMap::None
}
}
use crate::astconv::{AstConv, PathSeg};
use crate::middle::lang_items;
use crate::namespace::Namespace;
-use errors::{pluralize, Applicability, DiagnosticBuilder, DiagnosticId};
-use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor};
+use rustc::hir::map::Map;
use rustc::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse};
use rustc::infer::error_reporting::TypeAnnotationNeeded::E0282;
use rustc::infer::opaque_types::OpaqueTypeDecl;
use rustc::infer::{self, InferCtxt, InferOk, InferResult};
use rustc::middle::region;
use rustc::mir::interpret::ConstValue;
+use rustc::session::parse::feature_err;
+use rustc::traits::error_reporting::recursive_type_with_infinite_size_error;
use rustc::traits::{self, ObligationCause, ObligationCauseCode, TraitEngine};
use rustc::ty::adjustment::{
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCast,
self, AdtKind, CanonicalUserType, Const, GenericParamDefKind, RegionKind, ToPolyTraitRef,
ToPredicate, Ty, TyCtxt, UserType,
};
+use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, DiagnosticId};
use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind, Res};
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet, LOCAL_CRATE};
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_hir::{ExprKind, GenericArg, HirIdMap, ItemKind, Node, PatKind, QPath};
use rustc_index::vec::Idx;
use rustc_target::spec::abi::Abi;
use syntax::ast;
use syntax::attr;
-use syntax::feature_gate::feature_err;
use syntax::util::parser::ExprPrecedence;
use rustc_error_codes::*;
use crate::require_c_abi_if_c_variadic;
use crate::session::config::EntryFnType;
use crate::session::Session;
-use crate::util::captures::Captures;
use crate::util::common::{indenter, ErrorReported};
use crate::TypeAndSubsts;
pub use self::Expectation::*;
use self::TupleArgumentsFlag::*;
+#[macro_export]
+macro_rules! type_error_struct {
+ ($session:expr, $span:expr, $typ:expr, $code:ident, $($message:tt)*) => ({
+ if $typ.references_error() {
+ $session.diagnostic().struct_dummy()
+ } else {
+ rustc_errors::struct_span_err!($session, $span, $code, $($message)*)
+ }
+ })
+}
+
/// The type of a local binding, including the revealed type for anon types.
#[derive(Copy, Clone, Debug)]
pub struct LocalTy<'tcx> {
}
impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> {
NestedVisitorMap::None
}
match parent_item {
// Parent impl exists, and contains the parent item we're trying to specialize, but
// doesn't mark it `default`.
- Some(parent_item) if tcx.impl_item_is_final(&parent_item) => {
+ Some(parent_item) if traits::impl_item_is_final(tcx, &parent_item) => {
Some(Err(parent_impl.def_id()))
}
// grandparent. In that case, if parent is a `default impl`, inherited items use the
// "defaultness" from the grandparent, else they are final.
None => {
- if tcx.impl_is_default(parent_impl.def_id()) {
+ if traits::impl_is_default(tcx, parent_impl.def_id()) {
None
} else {
Some(Err(parent_impl.def_id()))
.map(|node_item| !node_item.node.is_from_trait())
.unwrap_or(false);
- if !is_implemented && !tcx.impl_is_default(impl_id) {
+ if !is_implemented && !traits::impl_is_default(tcx, impl_id) {
if !trait_item.defaultness.has_value() {
missing_items.push(trait_item);
} else if associated_type_overridden {
if !invalidated_items.is_empty() {
let invalidator = overridden_associated_type.unwrap();
- span_err!(
+ struct_span_err!(
tcx.sess,
invalidator.span,
E0399,
invalidator.ident,
invalidated_items.iter().map(|name| name.to_string()).collect::<Vec<_>>().join("`, `")
)
+ .emit();
}
}
// caught by case 1.
match rty.is_representable(tcx, sp) {
Representability::SelfRecursive(spans) => {
- let mut err = tcx.recursive_type_with_infinite_size_error(item_def_id);
+ let mut err = recursive_type_with_infinite_size_error(tcx, item_def_id);
for span in spans {
err.span_label(span, "recursive without indirection");
}
if def.is_struct() {
let fields = &def.non_enum_variant().fields;
if fields.is_empty() {
- span_err!(tcx.sess, sp, E0075, "SIMD vector cannot be empty");
+ struct_span_err!(tcx.sess, sp, E0075, "SIMD vector cannot be empty").emit();
return;
}
let e = fields[0].ty(tcx, substs);
ty::Param(_) => { /* struct<T>(T, T, T, T) is ok */ }
_ if e.is_machine() => { /* struct(u8, u8, u8, u8) is ok */ }
_ => {
- span_err!(
+ struct_span_err!(
tcx.sess,
sp,
E0077,
"SIMD vector element type should be machine type"
- );
+ )
+ .emit();
return;
}
}
}
fn report_unexpected_variant_res(tcx: TyCtxt<'_>, res: Res, span: Span, qpath: &QPath<'_>) {
- span_err!(
+ struct_span_err!(
tcx.sess,
span,
E0533,
"expected unit struct, unit variant or constant, found {} `{}`",
res.descr(),
hir::print::to_string(tcx.hir(), |s| s.print_qpath(qpath, false))
- );
+ )
+ .emit();
}
impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
arg_types.iter().map(|k| k.expect_ty()).collect()
}
_ => {
- span_err!(
+ struct_span_err!(
tcx.sess,
sp,
E0059,
"cannot use call notation; the first type parameter \
for the function trait is neither a tuple nor unit"
- );
+ )
+ .emit();
expected_arg_tys = vec![];
self.err_args(args.len())
}
.join(", ");
}
Some(Node::Expr(hir::Expr {
- kind: ExprKind::Closure(_, _, body_id, closure_span, _),
+ kind: ExprKind::Closure(_, _, body_id, _, _),
span: full_closure_span,
..
})) => {
if *full_closure_span == expr.span {
return false;
}
- err.span_label(*closure_span, "closure defined here");
msg = "call this closure";
let body = hir.body(*body_id);
sugg_call = body
| ExprKind::Loop(..)
| ExprKind::Match(..)
| ExprKind::Block(..) => {
- let sp = self.tcx.sess.source_map().next_point(cause_span);
err.span_suggestion(
- sp,
+ cause_span.shrink_to_hi(),
"try adding a semicolon",
";".to_string(),
Applicability::MachineApplicable,
use super::method::MethodCallee;
use super::{FnCtxt, Needs};
-use errors::{self, Applicability};
use rustc::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
use rustc::ty::TyKind::{Adt, Array, Char, FnDef, Never, Ref, Str, Tuple, Uint};
use rustc::ty::{self, Ty, TypeFoldable};
+use rustc_errors::{self, struct_span_err, Applicability};
use rustc_hir as hir;
use rustc_span::Span;
use syntax::ast::Ident;
lhs_expr.span,
msg,
format!("*{}", lstring),
- errors::Applicability::MachineApplicable,
+ rustc_errors::Applicability::MachineApplicable,
);
suggested_deref = true;
}
Some("std::ops::Add"),
),
hir::BinOpKind::Sub => (
- format!("cannot substract `{}` from `{}`", rhs_ty, lhs_ty),
+ format!("cannot subtract `{}` from `{}`", rhs_ty, lhs_ty),
Some("std::ops::Sub"),
),
hir::BinOpKind::Mul => (
/// suggest calling the function. Returns wether a suggestion was given.
fn add_type_neq_err_label(
&self,
- err: &mut errors::DiagnosticBuilder<'_>,
+ err: &mut rustc_errors::DiagnosticBuilder<'_>,
span: Span,
ty: Ty<'tcx>,
other_ty: Ty<'tcx>,
rhs_expr: &'tcx hir::Expr<'tcx>,
lhs_ty: Ty<'tcx>,
rhs_ty: Ty<'tcx>,
- err: &mut errors::DiagnosticBuilder<'_>,
+ err: &mut rustc_errors::DiagnosticBuilder<'_>,
is_assign: bool,
op: hir::BinOp,
) -> bool {
use crate::check::FnCtxt;
-use errors::{pluralize, Applicability, DiagnosticBuilder};
use rustc::infer;
use rustc::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc::traits::Pattern;
use rustc::ty::subst::GenericArg;
use rustc::ty::{self, BindingMode, Ty, TypeFoldable};
use rustc_data_structures::fx::FxHashMap;
+use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder};
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::pat_util::EnumerateAndAdjustIterator;
let ty = match pat.kind {
PatKind::Wild => expected,
PatKind::Lit(lt) => self.check_pat_lit(pat.span, lt, expected, ti),
- PatKind::Range(begin, end, _) => {
- match self.check_pat_range(pat.span, begin, end, expected, ti) {
- None => return,
- Some(ty) => ty,
- }
- }
+ PatKind::Range(lhs, rhs, _) => self.check_pat_range(pat.span, lhs, rhs, expected, ti),
PatKind::Binding(ba, var_id, _, sub) => {
self.check_pat_ident(pat, ba, var_id, sub, expected, def_bm, ti)
}
fn check_pat_range(
&self,
span: Span,
- lhs: &'tcx hir::Expr<'tcx>,
- rhs: &'tcx hir::Expr<'tcx>,
+ lhs: Option<&'tcx hir::Expr<'tcx>>,
+ rhs: Option<&'tcx hir::Expr<'tcx>>,
expected: Ty<'tcx>,
ti: TopInfo<'tcx>,
- ) -> Option<Ty<'tcx>> {
- let lhs_ty = self.check_expr(lhs);
- let rhs_ty = self.check_expr(rhs);
-
- // Check that both end-points are of numeric or char type.
- let numeric_or_char = |ty: Ty<'_>| ty.is_numeric() || ty.is_char() || ty.references_error();
- let lhs_fail = !numeric_or_char(lhs_ty);
- let rhs_fail = !numeric_or_char(rhs_ty);
-
- if lhs_fail || rhs_fail {
- self.emit_err_pat_range(span, lhs.span, rhs.span, lhs_fail, rhs_fail, lhs_ty, rhs_ty);
- return None;
+ ) -> Ty<'tcx> {
+ let calc_side = |opt_expr: Option<&'tcx hir::Expr<'tcx>>| match opt_expr {
+ None => (None, None),
+ Some(expr) => {
+ let ty = self.check_expr(expr);
+ // Check that the end-point is of numeric or char type.
+ let fail = !(ty.is_numeric() || ty.is_char() || ty.references_error());
+ (Some(ty), Some((fail, ty, expr.span)))
+ }
+ };
+ let (lhs_ty, lhs) = calc_side(lhs);
+ let (rhs_ty, rhs) = calc_side(rhs);
+
+ if let (Some((true, ..)), _) | (_, Some((true, ..))) = (lhs, rhs) {
+ // There exists a side that didn't meet our criteria that the end-point
+ // be of a numeric or char type, as checked in `calc_side` above.
+ self.emit_err_pat_range(span, lhs, rhs);
+ return self.tcx.types.err;
}
- // Now that we know the types can be unified we find the unified type and use
- // it to type the entire expression.
- let common_type = self.resolve_vars_if_possible(&lhs_ty);
+ // Now that we know the types can be unified we find the unified type
+ // and use it to type the entire expression.
+ let common_type = self.resolve_vars_if_possible(&lhs_ty.or(rhs_ty).unwrap_or(expected));
// Subtyping doesn't matter here, as the value is some kind of scalar.
- let demand_eqtype = |x_span, y_span, x_ty, y_ty| {
- self.demand_eqtype_pat_diag(x_span, expected, x_ty, ti).map(|mut err| {
- self.endpoint_has_type(&mut err, y_span, y_ty);
- err.emit();
- });
+ let demand_eqtype = |x, y| {
+ if let Some((_, x_ty, x_span)) = x {
+ self.demand_eqtype_pat_diag(x_span, expected, x_ty, ti).map(|mut err| {
+ if let Some((_, y_ty, y_span)) = y {
+ self.endpoint_has_type(&mut err, y_span, y_ty);
+ }
+ err.emit();
+ });
+ }
};
- demand_eqtype(lhs.span, rhs.span, lhs_ty, rhs_ty);
- demand_eqtype(rhs.span, lhs.span, rhs_ty, lhs_ty);
+ demand_eqtype(lhs, rhs);
+ demand_eqtype(rhs, lhs);
- Some(common_type)
+ common_type
}
fn endpoint_has_type(&self, err: &mut DiagnosticBuilder<'_>, span: Span, ty: Ty<'_>) {
fn emit_err_pat_range(
&self,
span: Span,
- begin_span: Span,
- end_span: Span,
- lhs_fail: bool,
- rhs_fail: bool,
- lhs_ty: Ty<'tcx>,
- rhs_ty: Ty<'tcx>,
+ lhs: Option<(bool, Ty<'tcx>, Span)>,
+ rhs: Option<(bool, Ty<'tcx>, Span)>,
) {
- let span = if lhs_fail && rhs_fail {
- span
- } else if lhs_fail {
- begin_span
- } else {
- end_span
+ let span = match (lhs, rhs) {
+ (Some((true, ..)), Some((true, ..))) => span,
+ (Some((true, _, sp)), _) => sp,
+ (_, Some((true, _, sp))) => sp,
+ _ => span_bug!(span, "emit_err_pat_range: no side failed or exists but still error?"),
};
-
let mut err = struct_span_err!(
self.tcx.sess,
span,
"only char and numeric types are allowed in range patterns"
);
let msg = |ty| format!("this is of type `{}` but it should be `char` or numeric", ty);
- let mut one_side_err = |first_span, first_ty, second_span, second_ty: Ty<'_>| {
+ let mut one_side_err = |first_span, first_ty, second: Option<(bool, Ty<'tcx>, Span)>| {
err.span_label(first_span, &msg(first_ty));
- self.endpoint_has_type(&mut err, second_span, second_ty);
+ if let Some((_, ty, sp)) = second {
+ self.endpoint_has_type(&mut err, sp, ty);
+ }
};
- if lhs_fail && rhs_fail {
- err.span_label(begin_span, &msg(lhs_ty));
- err.span_label(end_span, &msg(rhs_ty));
- } else if lhs_fail {
- one_side_err(begin_span, lhs_ty, end_span, rhs_ty);
- } else {
- one_side_err(end_span, rhs_ty, begin_span, lhs_ty);
+ match (lhs, rhs) {
+ (Some((true, lhs_ty, lhs_sp)), Some((true, rhs_ty, rhs_sp))) => {
+ err.span_label(lhs_sp, &msg(lhs_ty));
+ err.span_label(rhs_sp, &msg(rhs_ty));
+ }
+ (Some((true, lhs_ty, lhs_sp)), rhs) => one_side_err(lhs_sp, lhs_ty, rhs),
+ (lhs, Some((true, rhs_ty, rhs_sp))) => one_side_err(rhs_sp, rhs_ty, lhs),
+ _ => span_bug!(span, "Impossible, verified above."),
}
if self.tcx.sess.teach(&err.get_code().unwrap()) {
err.note(
// Require `..` if struct has non_exhaustive attribute.
if variant.is_field_list_non_exhaustive() && !adt.did.is_local() && !etc {
- span_err!(
+ struct_span_err!(
tcx.sess,
span,
E0638,
"`..` required with {} marked as non-exhaustive",
kind_name
- );
+ )
+ .emit();
}
// Report an error if incorrect number of the fields were specified.
if kind_name == "union" {
if fields.len() != 1 {
- tcx.sess.span_err(span, "union patterns should have exactly one field");
+ tcx.sess
+ .struct_span_err(span, "union patterns should have exactly one field")
+ .emit();
}
if etc {
- tcx.sess.span_err(span, "`..` cannot be used in union patterns");
+ tcx.sess.struct_span_err(span, "`..` cannot be used in union patterns").emit();
}
} else if !etc && unmentioned_fields.len() > 0 {
self.error_unmentioned_fields(span, &unmentioned_fields, variant);
use crate::check::FnCtxt;
use crate::mem_categorization as mc;
use crate::middle::region;
-use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor};
+use rustc::hir::map::Map;
use rustc::infer::outlives::env::OutlivesEnvironment;
use rustc::infer::{self, RegionObligation, SuppressRegionErrors};
use rustc::ty::adjustment;
use rustc::ty::{self, Ty};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_hir::PatKind;
use rustc_span::Span;
use std::mem;
// hierarchy, and in particular the relationships between free
// regions, until regionck, as described in #3238.
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> {
NestedVisitorMap::None
}
use crate::expr_use_visitor as euv;
use crate::mem_categorization as mc;
use crate::mem_categorization::PlaceBase;
-use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor};
+use rustc::hir::map::Map;
use rustc::infer::UpvarRegion;
use rustc::ty::{self, Ty, TyCtxt, UpvarSubsts};
use rustc_data_structures::fx::FxIndexMap;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::def_id::LocalDefId;
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_span::Span;
use syntax::ast;
}
impl<'a, 'tcx> Visitor<'tcx> for InferBorrowKindVisitor<'a, 'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> {
NestedVisitorMap::None
}
}
}
- // Returns a list of `ClosureUpvar`s for each upvar.
+ // Returns a list of `Ty`s for each upvar.
fn final_upvar_tys(&self, closure_id: hir::HirId) -> Vec<Ty<'tcx>> {
// Presently an unboxed closure type cannot "escape" out of a
// function, so we will only encounter ones that originated in the
use rustc::infer::opaque_types::may_define_opaque_type;
use rustc::middle::lang_items;
+use rustc::session::parse::feature_err;
use rustc::traits::{self, ObligationCause, ObligationCauseCode};
use rustc::ty::subst::{InternalSubsts, Subst};
use rustc::ty::{self, AdtKind, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeFoldable};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::{struct_span_err, DiagnosticBuilder};
use rustc_hir::def_id::DefId;
use rustc_hir::ItemKind;
-
-use errors::DiagnosticBuilder;
use rustc_span::symbol::sym;
use rustc_span::Span;
use syntax::ast;
-use syntax::feature_gate;
use rustc_hir as hir;
use rustc_hir::itemlikevisit::ParItemLikeVisitor;
ty::ImplPolarity::Negative => {
// FIXME(#27579): what amount of WF checking do we need for neg impls?
if trait_ref.is_some() && !is_auto {
- span_err!(
+ struct_span_err!(
tcx.sess,
item.span,
E0192,
"negative impls are only allowed for \
auto traits (e.g., `Send` and `Sync`)"
)
+ .emit()
}
}
ty::ImplPolarity::Reservation => {
let trait_ref = fcx.tcx.impl_trait_ref(item_def_id).unwrap();
let trait_ref =
fcx.normalize_associated_types_in(ast_trait_ref.path.span, &trait_ref);
- let obligations = ty::wf::trait_obligations(
+ let obligations = traits::wf::trait_obligations(
fcx,
fcx.param_env,
fcx.body_id,
let wf_obligations = predicates
.predicates
.iter()
- .flat_map(|p| ty::wf::predicate_obligations(fcx, fcx.param_env, fcx.body_id, p, span));
+ .flat_map(|p| traits::wf::predicate_obligations(fcx, fcx.param_env, fcx.body_id, p, span));
for obligation in wf_obligations.chain(default_obligations) {
debug!("next obligation cause: {:?}", obligation.cause);
if !receiver_is_valid(fcx, span, receiver_ty, self_ty, false) {
if receiver_is_valid(fcx, span, receiver_ty, self_ty, true) {
// Report error; would have worked with `arbitrary_self_types`.
- feature_gate::feature_err(
+ feature_err(
&fcx.tcx.sess.parse_sess,
sym::arbitrary_self_types,
span,
use crate::check::FnCtxt;
-use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor};
+use rustc::hir::map::Map;
use rustc::infer::error_reporting::TypeAnnotationNeeded::E0282;
use rustc::infer::InferCtxt;
use rustc::ty::adjustment::{Adjust, Adjustment, PointerCast};
use rustc_data_structures::sync::Lrc;
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, DefIdSet, DefIndex};
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_span::symbol::sym;
use rustc_span::Span;
// traffic in node-ids or update tables in the type context etc.
impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> {
NestedVisitorMap::None
}
use crate::lint;
use rustc::ty::TyCtxt;
-
-use errors::Applicability;
-use rustc_span::Span;
-use syntax::ast;
-
use rustc_data_structures::fx::FxHashMap;
+use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, DefIdSet, LOCAL_CRATE};
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_hir::print::visibility_qualified;
+use rustc_span::Span;
+use syntax::ast;
pub fn check_crate(tcx: TyCtxt<'_>) {
let mut used_trait_imports = DefIdSet::default();
//! Check properties that are required by built-in traits and set
//! up data structures required by type-checking/codegen.
+use rustc::infer;
use rustc::infer::outlives::env::OutlivesEnvironment;
use rustc::infer::SuppressRegionErrors;
use rustc::middle::lang_items::UnsizeTraitLangItem;
use rustc::middle::region;
-
-use rustc::infer;
+use rustc::traits::misc::{can_type_implement_copy, CopyImplementationError};
+use rustc::traits::predicate_for_trait_def;
use rustc::traits::{self, ObligationCause, TraitEngine};
use rustc::ty::adjustment::CoerceUnsizedInfo;
-use rustc::ty::util::CopyImplementationError;
use rustc::ty::TypeFoldable;
use rustc::ty::{self, Ty, TyCtxt};
-
+use rustc_error_codes::*;
+use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::ItemKind;
-use rustc_error_codes::*;
-
pub fn check_trait(tcx: TyCtxt<'_>, trait_def_id: DefId) {
Checker { tcx, trait_def_id }
.check(tcx.lang_items().drop_trait(), visit_implementation_of_drop)
debug!("visit_implementation_of_copy: self_type={:?} (free)", self_type);
- match param_env.can_type_implement_copy(tcx, self_type) {
+ match can_type_implement_copy(tcx, param_env, self_type) {
Ok(()) => {}
Err(CopyImplementationError::InfrigingFields(fields)) => {
let item = tcx.hir().expect_item(impl_hir_id);
let mut fulfill_cx = TraitEngine::new(infcx.tcx);
for field in coerced_fields {
- let predicate = tcx.predicate_for_trait_def(
+ let predicate = predicate_for_trait_def(
+ tcx,
param_env,
cause.clone(),
dispatch_from_dyn_trait,
if def_a != def_b {
let source_path = tcx.def_path_str(def_a.did);
let target_path = tcx.def_path_str(def_b.did);
- span_err!(
+ struct_span_err!(
tcx.sess,
span,
E0377,
definition; expected `{}`, found `{}`",
source_path,
target_path
- );
+ )
+ .emit();
return err_info;
}
.collect::<Vec<_>>();
if diff_fields.is_empty() {
- span_err!(
+ struct_span_err!(
tcx.sess,
span,
E0374,
"the trait `CoerceUnsized` may only be implemented \
for a coercion between structures with one field \
being coerced, none found"
- );
+ )
+ .emit();
return err_info;
} else if diff_fields.len() > 1 {
let item = tcx.hir().expect_item(impl_hir_id);
tcx.hir().span(impl_hir_id)
};
- let mut err = struct_span_err!(
+ struct_span_err!(
tcx.sess,
span,
E0375,
"implementing the trait \
`CoerceUnsized` requires multiple \
coercions"
- );
- err.note(
+ )
+ .note(
"`CoerceUnsized` may only be implemented for \
a coercion between structures with one field being coerced",
- );
- err.note(&format!(
+ )
+ .note(&format!(
"currently, {} fields need coercions: {}",
diff_fields.len(),
diff_fields
})
.collect::<Vec<_>>()
.join(", ")
- ));
- err.span_label(span, "requires multiple coercions");
- err.emit();
+ ))
+ .span_label(span, "requires multiple coercions")
+ .emit();
return err_info;
}
}
_ => {
- span_err!(
+ struct_span_err!(
tcx.sess,
span,
E0376,
"the trait `CoerceUnsized` may only be implemented \
for a coercion between structures"
- );
+ )
+ .emit();
return err_info;
}
};
// Register an obligation for `A: Trait<B>`.
let cause = traits::ObligationCause::misc(span, impl_hir_id);
- let predicate = tcx.predicate_for_trait_def(
+ let predicate = predicate_for_trait_def(
+ tcx,
param_env,
cause,
trait_def_id,
//! is computed by selecting an idea from this table.
use rustc::ty::{self, CrateInherentImpls, TyCtxt};
+use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use crate::namespace::Namespace;
use rustc::traits::{self, IntercrateMode};
use rustc::ty::TyCtxt;
+use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
use rustc_hir::itemlikevisit::ItemLikeVisitor;
// done by the orphan and overlap modules. Then we build up various
// mappings. That mapping code resides here.
-use crate::hir::def_id::{DefId, LOCAL_CRATE};
-use crate::hir::HirId;
use rustc::traits;
use rustc::ty::query::Providers;
use rustc::ty::{self, TyCtxt, TypeFoldable};
-
use rustc_error_codes::*;
+use rustc_errors::struct_span_err;
+use rustc_hir::def_id::{DefId, LOCAL_CRATE};
+use rustc_hir::HirId;
mod builtin;
mod inherent_impls;
tcx.ensure().coherent_trait(trait_def_id);
}
- tcx.sess.time("unsafety checking", || unsafety::check(tcx));
- tcx.sess.time("orphan checking", || orphan::check(tcx));
+ tcx.sess.time("unsafety_checking", || unsafety::check(tcx));
+ tcx.sess.time("orphan_checking", || orphan::check(tcx));
// these queries are executed for side-effects (error reporting):
tcx.ensure().crate_inherent_impls(LOCAL_CRATE);
use rustc::traits;
use rustc::ty::{self, TyCtxt};
+use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::itemlikevisit::ItemLikeVisitor;
//! crate or pertains to a type defined in this crate.
use rustc::ty::TyCtxt;
+use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_hir::Unsafety;
});
match (trait_def.unsafety, unsafe_attr, unsafety, polarity) {
(Unsafety::Normal, None, Unsafety::Unsafe, hir::ImplPolarity::Positive) => {
- span_err!(
+ struct_span_err!(
self.tcx.sess,
item.span,
E0199,
"implementing the trait `{}` is not unsafe",
trait_ref.print_only_trait_path()
- );
+ )
+ .emit();
}
(Unsafety::Unsafe, _, Unsafety::Normal, hir::ImplPolarity::Positive) => {
- span_err!(
+ struct_span_err!(
self.tcx.sess,
item.span,
E0200,
"the trait `{}` requires an `unsafe impl` declaration",
trait_ref.print_only_trait_path()
- );
+ )
+ .emit();
}
(
Unsafety::Normal,
hir::ImplPolarity::Positive,
) => {
- span_err!(
+ struct_span_err!(
self.tcx.sess,
item.span,
E0569,
"requires an `unsafe impl` declaration due to `#[{}]` attribute",
attr_name
- );
+ )
+ .emit();
}
(_, _, Unsafety::Unsafe, hir::ImplPolarity::Negative) => {
use crate::lint;
use crate::middle::resolve_lifetime as rl;
use crate::middle::weak_lang_items;
-use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor};
+use rustc::hir::map::Map;
use rustc::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
use rustc::mir::mono::Linkage;
+use rustc::session::parse::feature_err;
+use rustc::traits;
use rustc::ty::query::Providers;
use rustc::ty::subst::GenericArgKind;
use rustc::ty::subst::{InternalSubsts, Subst};
use rustc::ty::util::IntTypeExt;
use rustc::ty::{self, AdtKind, Const, DefIdTree, ToPolyTraitRef, Ty, TyCtxt};
use rustc::ty::{ReprOptions, ToPredicate};
-use rustc::util::captures::Captures;
+use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::FxHashMap;
+use rustc_errors::{struct_span_err, Applicability, StashKey};
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_hir::{GenericParamKind, Node, Unsafety};
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_span::{Span, DUMMY_SP};
use syntax::ast;
use syntax::ast::{Ident, MetaItemKind};
use syntax::attr::{list_contains_name, mark_used, InlineAttr, OptimizeAttr};
-use syntax::feature_gate;
-
-use errors::{Applicability, StashKey};
use rustc_error_codes::*;
crate struct PlaceholderHirTyCollector(crate Vec<Span>);
impl<'v> Visitor<'v> for PlaceholderHirTyCollector {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'v> {
+ type Map = Map<'v>;
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> {
NestedVisitorMap::None
}
fn visit_ty(&mut self, t: &'v hir::Ty<'v>) {
/// all already existing generic type parameters to avoid suggesting a name that is already in use.
crate fn placeholder_type_error(
tcx: TyCtxt<'tcx>,
- ident_span: Span,
+ span: Span,
generics: &[hir::GenericParam<'_>],
placeholder_types: Vec<Span>,
suggest: bool,
let mut sugg: Vec<_> =
placeholder_types.iter().map(|sp| (*sp, type_name.to_string())).collect();
if generics.is_empty() {
- sugg.push((ident_span.shrink_to_hi(), format!("<{}>", type_name)));
+ sugg.push((span, format!("<{}>", type_name)));
+ } else if let Some(arg) = generics.iter().find(|arg| match arg.name {
+ hir::ParamName::Plain(Ident { name: kw::Underscore, .. }) => true,
+ _ => false,
+ }) {
+ // Account for `_` already present in cases like `struct S<_>(_);` and suggest
+ // `struct S<T>(T);` instead of `struct S<_, T>(T);`.
+ sugg.push((arg.span, format!("{}", type_name)));
} else {
sugg.push((
generics.iter().last().unwrap().span.shrink_to_hi(),
let (generics, suggest) = match &item.kind {
hir::ItemKind::Union(_, generics)
| hir::ItemKind::Enum(_, generics)
- | hir::ItemKind::Struct(_, generics) => (&generics.params[..], true),
- hir::ItemKind::TyAlias(_, generics) => (&generics.params[..], false),
+ | hir::ItemKind::TraitAlias(generics, _)
+ | hir::ItemKind::Trait(_, _, generics, ..)
+ | hir::ItemKind::Impl(_, _, _, generics, ..)
+ | hir::ItemKind::Struct(_, generics) => (generics, true),
+ hir::ItemKind::OpaqueTy(hir::OpaqueTy { generics, .. })
+ | hir::ItemKind::TyAlias(_, generics) => (generics, false),
// `static`, `fn` and `const` are handled elsewhere to suggest appropriate type.
_ => return,
};
let mut visitor = PlaceholderHirTyCollector::default();
visitor.visit_item(item);
- placeholder_type_error(tcx, item.ident.span, generics, visitor.0, suggest);
+ placeholder_type_error(tcx, generics.span, &generics.params[..], visitor.0, suggest);
}
impl Visitor<'tcx> for CollectItemTypesVisitor<'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> {
NestedVisitorMap::OnlyBodies(&self.tcx.hir())
}
fn bad_placeholder_type(
tcx: TyCtxt<'tcx>,
mut spans: Vec<Span>,
-) -> errors::DiagnosticBuilder<'tcx> {
+) -> rustc_errors::DiagnosticBuilder<'tcx> {
spans.sort();
let mut err = struct_span_err!(
tcx.sess,
self.tcx().mk_projection(item_def_id, item_substs)
} else {
// There are no late-bound regions; we can just ignore the binder.
- span_err!(
+ struct_span_err!(
self.tcx().sess,
span,
E0212,
"cannot extract an associated type from a higher-ranked trait bound \
in this context"
- );
+ )
+ .emit();
self.tcx().types.err
}
}
let paren_sugar = tcx.has_attr(def_id, sym::rustc_paren_sugar);
if paren_sugar && !tcx.features().unboxed_closures {
- let mut err = tcx.sess.struct_span_err(
- item.span,
- "the `#[rustc_paren_sugar]` attribute is a temporary means of controlling \
+ tcx.sess
+ .struct_span_err(
+ item.span,
+ "the `#[rustc_paren_sugar]` attribute is a temporary means of controlling \
which traits can use parenthetical notation",
- );
- help!(
- &mut err,
- "add `#![feature(unboxed_closures)]` to \
- the crate attributes to use it"
- );
- err.emit();
+ )
+ .help("add `#![feature(unboxed_closures)]` to the crate attributes to use it")
+ .emit();
}
let is_marker = tcx.has_attr(def_id, sym::marker);
}
impl Visitor<'tcx> for LateBoundRegionsDetector<'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> {
NestedVisitorMap::None
}
}
fn report_assoc_ty_on_inherent_impl(tcx: TyCtxt<'_>, span: Span) {
- span_err!(
+ struct_span_err!(
tcx.sess,
span,
E0202,
"associated types are not yet supported in inherent impls (see #8995)"
- );
+ )
+ .emit();
}
fn infer_placeholder_type(
}
}
- Node::GenericParam(param) => {
- match ¶m.kind {
- hir::GenericParamKind::Type { default: Some(ref ty), .. } => icx.to_ty(ty),
- hir::GenericParamKind::Const { ty: ref hir_ty, .. } => {
- let ty = icx.to_ty(hir_ty);
- if !tcx.features().const_compare_raw_pointers {
- let err = match ty.peel_refs().kind {
- ty::FnPtr(_) => Some("function pointers"),
- ty::RawPtr(_) => Some("raw pointers"),
- _ => None,
- };
- if let Some(unsupported_type) = err {
- feature_gate::feature_err(
- &tcx.sess.parse_sess,
- sym::const_compare_raw_pointers,
- hir_ty.span,
- &format!(
- "using {} as const generic parameters is unstable",
- unsupported_type
- ),
- )
- .emit();
- };
- }
- if ty::search_for_structural_match_violation(param.hir_id, param.span, tcx, ty)
- .is_some()
- {
- struct_span_err!(
+ Node::GenericParam(param) => match ¶m.kind {
+ hir::GenericParamKind::Type { default: Some(ref ty), .. } => icx.to_ty(ty),
+ hir::GenericParamKind::Const { ty: ref hir_ty, .. } => {
+ let ty = icx.to_ty(hir_ty);
+ if !tcx.features().const_compare_raw_pointers {
+ let err = match ty.peel_refs().kind {
+ ty::FnPtr(_) => Some("function pointers"),
+ ty::RawPtr(_) => Some("raw pointers"),
+ _ => None,
+ };
+ if let Some(unsupported_type) = err {
+ feature_err(
+ &tcx.sess.parse_sess,
+ sym::const_compare_raw_pointers,
+ hir_ty.span,
+ &format!(
+ "using {} as const generic parameters is unstable",
+ unsupported_type
+ ),
+ )
+ .emit();
+ };
+ }
+ if traits::search_for_structural_match_violation(param.hir_id, param.span, tcx, ty)
+ .is_some()
+ {
+ struct_span_err!(
tcx.sess,
hir_ty.span,
E0741,
"the types of const generic parameters must derive `PartialEq` and `Eq`",
- ).span_label(
+ )
+ .span_label(
hir_ty.span,
format!("`{}` doesn't derive both `PartialEq` and `Eq`", ty),
- ).emit();
- }
- ty
+ )
+ .emit();
}
- x => bug!("unexpected non-type Node::GenericParam: {:?}", x),
+ ty
}
- }
+ x => bug!("unexpected non-type Node::GenericParam: {:?}", x),
+ },
x => {
bug!("unexpected sort of node in type_of_def_id(): {:?}", x);
}
impl<'tcx> intravisit::Visitor<'tcx> for ConstraintLocator<'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<'_, Self::Map> {
intravisit::NestedVisitorMap::All(&self.tcx.hir())
}
fn visit_item(&mut self, it: &'tcx Item<'tcx>) {
/// Whether `ty` is a type with `_` placeholders that can be infered. Used in diagnostics only to
/// use inference to provide suggestions for the appropriate type if possible.
fn is_suggestable_infer_ty(ty: &hir::Ty<'_>) -> bool {
+ use hir::TyKind::*;
match &ty.kind {
- hir::TyKind::Infer => true,
- hir::TyKind::Slice(ty) | hir::TyKind::Array(ty, _) => is_suggestable_infer_ty(ty),
- hir::TyKind::Tup(tys) => tys.iter().any(|ty| is_suggestable_infer_ty(ty)),
+ Infer => true,
+ Slice(ty) | Array(ty, _) => is_suggestable_infer_ty(ty),
+ Tup(tys) => tys.iter().any(is_suggestable_infer_ty),
+ Ptr(mut_ty) | Rptr(_, mut_ty) => is_suggestable_infer_ty(mut_ty.ty),
+ Def(_, generic_args) => generic_args
+ .iter()
+ .filter_map(|arg| match arg {
+ hir::GenericArg::Type(ty) => Some(ty),
+ _ => None,
+ })
+ .any(is_suggestable_infer_ty),
_ => false,
}
}
None => true,
};
if !allowed && id.is_local() {
- feature_gate::feature_err(
+ feature_err(
&tcx.sess.parse_sess,
feature_gate.unwrap(),
item.span(),
mark_used(attr);
inline_span = Some(attr.span);
if items.len() != 1 {
- span_err!(tcx.sess.diagnostic(), attr.span, E0534, "expected one argument");
+ struct_span_err!(
+ tcx.sess.diagnostic(),
+ attr.span,
+ E0534,
+ "expected one argument"
+ )
+ .emit();
InlineAttr::None
} else if list_contains_name(&items[..], sym::always) {
InlineAttr::Always
} else if list_contains_name(&items[..], sym::never) {
InlineAttr::Never
} else {
- span_err!(tcx.sess.diagnostic(), items[0].span(), E0535, "invalid argument");
+ struct_span_err!(
+ tcx.sess.diagnostic(),
+ items[0].span(),
+ E0535,
+ "invalid argument"
+ )
+ .emit();
InlineAttr::None
}
if !attr.has_name(sym::optimize) {
return ia;
}
- let err = |sp, s| span_err!(tcx.sess.diagnostic(), sp, E0722, "{}", s);
+ let err = |sp, s| struct_span_err!(tcx.sess.diagnostic(), sp, E0722, "{}", s).emit();
match attr.meta().map(|i| i.kind) {
Some(MetaItemKind::Word) => {
err(attr.span, "expected one argument");
use rustc::ty::query::Providers;
use rustc::ty::{self, TyCtxt, TypeFoldable};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::itemlikevisit::ItemLikeVisitor;
#[macro_use]
extern crate log;
-#[macro_use]
-extern crate syntax;
#[macro_use]
extern crate rustc;
use rustc::ty::{self, Ty, TyCtxt};
use rustc::util;
use rustc::util::common::ErrorReported;
+use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
use rustc_hir::Node;
// have valid types and not error
// FIXME(matthewjasper) We shouldn't need to do this.
tcx.sess.track_errors(|| {
- tcx.sess.time("type collecting", || {
+ tcx.sess.time("type_collecting", || {
for &module in tcx.hir().krate().modules.keys() {
tcx.ensure().collect_mod_item_types(tcx.hir().local_def_id(module));
}
if tcx.features().rustc_attrs {
tcx.sess.track_errors(|| {
- tcx.sess.time("outlives testing", || outlives::test::test_inferred_outlives(tcx));
+ tcx.sess.time("outlives_testing", || outlives::test::test_inferred_outlives(tcx));
})?;
}
tcx.sess.track_errors(|| {
- tcx.sess.time("impl wf inference", || impl_wf_check::impl_wf_check(tcx));
+ tcx.sess.time("impl_wf_inference", || impl_wf_check::impl_wf_check(tcx));
})?;
tcx.sess.track_errors(|| {
- tcx.sess.time("coherence checking", || coherence::check_coherence(tcx));
+ tcx.sess.time("coherence_checking", || coherence::check_coherence(tcx));
})?;
if tcx.features().rustc_attrs {
tcx.sess.track_errors(|| {
- tcx.sess.time("variance testing", || variance::test::test_variance(tcx));
+ tcx.sess.time("variance_testing", || variance::test::test_variance(tcx));
})?;
}
tcx.sess.track_errors(|| {
- tcx.sess.time("wf checking", || check::check_wf_new(tcx));
+ tcx.sess.time("wf_checking", || check::check_wf_new(tcx));
})?;
- tcx.sess.time("item-types checking", || {
+ tcx.sess.time("item_types_checking", || {
for &module in tcx.hir().krate().modules.keys() {
tcx.ensure().check_mod_item_types(tcx.hir().local_def_id(module));
}
});
- tcx.sess.time("item-bodies checking", || tcx.typeck_item_bodies(LOCAL_CRATE));
+ tcx.sess.time("item_bodies_checking", || tcx.typeck_item_bodies(LOCAL_CRATE));
check_unused::check_crate(tcx);
check_for_entry_fn(tcx);
use rustc::ty::TyCtxt;
+use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_span::symbol::sym;
// attribute and report an error with various results if found.
if self.tcx.has_attr(item_def_id, sym::rustc_outlives) {
let inferred_outlives_of = self.tcx.inferred_outlives_of(item_def_id);
- span_err!(self.tcx.sess, item.span, E0640, "{:?}", inferred_outlives_of);
+ struct_span_err!(self.tcx.sess, item.span, E0640, "{:?}", inferred_outlives_of).emit();
}
}
-use errors::{Applicability, DiagnosticBuilder, DiagnosticId};
use rustc::session::Session;
use rustc::ty::{Ty, TypeFoldable};
+use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticId};
use rustc_span::Span;
use rustc_error_codes::*;
}
fn code(&self) -> DiagnosticId {
- syntax::diagnostic_used!(E0617);
- DiagnosticId::Error("E0617".to_owned())
+ rustc_errors::error_code!(E0617)
}
fn common(&self) -> DiagnosticBuilder<'tcx> {
}
fn code(&self) -> DiagnosticId {
- syntax::diagnostic_used!(E0607);
- DiagnosticId::Error("E0607".to_owned())
+ rustc_errors::error_code!(E0607)
}
fn common(&self) -> DiagnosticBuilder<'tcx> {
use rustc::ty::TyCtxt;
+use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_span::symbol::sym;
// attribute and report an error with various results if found.
if self.tcx.has_attr(item_def_id, sym::rustc_variance) {
let variances_of = self.tcx.variances_of(item_def_id);
- span_err!(self.tcx.sess, item.span, E0208, "{:?}", variances_of);
+ struct_span_err!(self.tcx.sess, item.span, E0208, "{:?}", variances_of).emit();
}
}
impl ops::BitAndAssign for Cfg {
fn bitand_assign(&mut self, other: Cfg) {
- if *self == other {
- return;
- }
match (self, other) {
(&mut Cfg::False, _) | (_, Cfg::True) => {}
(s, Cfg::False) => *s = Cfg::False,
(s @ &mut Cfg::True, b) => *s = b,
- (&mut Cfg::All(ref mut a), Cfg::All(ref mut b)) => a.append(b),
- (&mut Cfg::All(ref mut a), ref mut b) => a.push(mem::replace(b, Cfg::True)),
+ (&mut Cfg::All(ref mut a), Cfg::All(ref mut b)) => {
+ for c in b.drain(..) {
+ if !a.contains(&c) {
+ a.push(c);
+ }
+ }
+ }
+ (&mut Cfg::All(ref mut a), ref mut b) => {
+ if !a.contains(b) {
+ a.push(mem::replace(b, Cfg::True));
+ }
+ }
(s, Cfg::All(mut a)) => {
let b = mem::replace(s, Cfg::True);
- a.push(b);
+ if !a.contains(&b) {
+ a.push(b);
+ }
*s = Cfg::All(a);
}
(s, b) => {
- let a = mem::replace(s, Cfg::True);
- *s = Cfg::All(vec![a, b]);
+ if *s != b {
+ let a = mem::replace(s, Cfg::True);
+ *s = Cfg::All(vec![a, b]);
+ }
}
}
}
impl ops::BitOrAssign for Cfg {
fn bitor_assign(&mut self, other: Cfg) {
- if *self == other {
- return;
- }
match (self, other) {
(&mut Cfg::True, _) | (_, Cfg::False) => {}
(s, Cfg::True) => *s = Cfg::True,
(s @ &mut Cfg::False, b) => *s = b,
- (&mut Cfg::Any(ref mut a), Cfg::Any(ref mut b)) => a.append(b),
- (&mut Cfg::Any(ref mut a), ref mut b) => a.push(mem::replace(b, Cfg::True)),
+ (&mut Cfg::Any(ref mut a), Cfg::Any(ref mut b)) => {
+ for c in b.drain(..) {
+ if !a.contains(&c) {
+ a.push(c);
+ }
+ }
+ }
+ (&mut Cfg::Any(ref mut a), ref mut b) => {
+ if !a.contains(b) {
+ a.push(mem::replace(b, Cfg::True));
+ }
+ }
(s, Cfg::Any(mut a)) => {
let b = mem::replace(s, Cfg::True);
- a.push(b);
+ if !a.contains(&b) {
+ a.push(b);
+ }
*s = Cfg::Any(a);
}
(s, b) => {
- let a = mem::replace(s, Cfg::True);
- *s = Cfg::Any(vec![a, b]);
+ if *s != b {
+ let a = mem::replace(s, Cfg::True);
+ *s = Cfg::Any(vec![a, b]);
+ }
}
}
}
x &= word_cfg("test3");
assert_eq!(x, word_cfg("test3"));
+ x &= word_cfg("test3");
+ assert_eq!(x, word_cfg("test3"));
+
+ x &= word_cfg("test4");
+ assert_eq!(x, Cfg::All(vec![word_cfg("test3"), word_cfg("test4")]));
+
x &= word_cfg("test4");
assert_eq!(x, Cfg::All(vec![word_cfg("test3"), word_cfg("test4")]));
])
);
+ x &= Cfg::All(vec![word_cfg("test6"), word_cfg("test7")]);
+ assert_eq!(
+ x,
+ Cfg::All(vec![
+ word_cfg("test3"),
+ word_cfg("test4"),
+ word_cfg("test5"),
+ word_cfg("test6"),
+ word_cfg("test7"),
+ ])
+ );
+
let mut y = Cfg::Any(vec![word_cfg("a"), word_cfg("b")]);
y &= x;
assert_eq!(
])
);
+ let mut z = word_cfg("test8");
+ z &= Cfg::All(vec![word_cfg("test9"), word_cfg("test10")]);
+ assert_eq!(z, Cfg::All(vec![word_cfg("test9"), word_cfg("test10"), word_cfg("test8")]));
+
+ let mut z = word_cfg("test11");
+ z &= Cfg::All(vec![word_cfg("test11"), word_cfg("test12")]);
+ assert_eq!(z, Cfg::All(vec![word_cfg("test11"), word_cfg("test12")]));
+
assert_eq!(
word_cfg("a") & word_cfg("b") & word_cfg("c"),
Cfg::All(vec![word_cfg("a"), word_cfg("b"), word_cfg("c")])
x |= word_cfg("test3");
assert_eq!(x, word_cfg("test3"));
+ x |= word_cfg("test3");
+ assert_eq!(x, word_cfg("test3"));
+
+ x |= word_cfg("test4");
+ assert_eq!(x, Cfg::Any(vec![word_cfg("test3"), word_cfg("test4")]));
+
x |= word_cfg("test4");
assert_eq!(x, Cfg::Any(vec![word_cfg("test3"), word_cfg("test4")]));
])
);
+ x |= Cfg::Any(vec![word_cfg("test6"), word_cfg("test7")]);
+ assert_eq!(
+ x,
+ Cfg::Any(vec![
+ word_cfg("test3"),
+ word_cfg("test4"),
+ word_cfg("test5"),
+ word_cfg("test6"),
+ word_cfg("test7"),
+ ])
+ );
+
let mut y = Cfg::All(vec![word_cfg("a"), word_cfg("b")]);
y |= x;
assert_eq!(
])
);
+ let mut z = word_cfg("test8");
+ z |= Cfg::Any(vec![word_cfg("test9"), word_cfg("test10")]);
+ assert_eq!(z, Cfg::Any(vec![word_cfg("test9"), word_cfg("test10"), word_cfg("test8")]));
+
+ let mut z = word_cfg("test11");
+ z |= Cfg::Any(vec![word_cfg("test11"), word_cfg("test12")]);
+ assert_eq!(z, Cfg::Any(vec![word_cfg("test11"), word_cfg("test12")]));
+
assert_eq!(
word_cfg("a") | word_cfg("b") | word_cfg("c"),
Cfg::Any(vec![word_cfg("a"), word_cfg("b"), word_cfg("c")])
use rustc_hir::def_id::DefId;
use rustc_hir::Mutability;
use rustc_metadata::creader::LoadedMacro;
+use rustc_mir::const_eval::is_min_const_fn;
use rustc_span::hygiene::MacroKind;
use rustc_span::symbol::sym;
use rustc_span::Span;
let sig = cx.tcx.fn_sig(did);
let constness =
- if cx.tcx.is_min_const_fn(did) { hir::Constness::Const } else { hir::Constness::NotConst };
+ if is_min_const_fn(cx.tcx, did) { hir::Constness::Const } else { hir::Constness::NotConst };
let asyncness = cx.tcx.asyncness(did);
let predicates = cx.tcx.predicates_of(did);
let (generics, decl) = clean::enter_impl_trait(cx, || {
use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX};
use rustc_index::vec::{Idx, IndexVec};
+use rustc_mir::const_eval::is_min_const_fn;
use rustc_span::hygiene::MacroKind;
use rustc_span::symbol::{kw, sym};
use rustc_span::{self, Pos};
enter_impl_trait(cx, || (self.generics.clean(cx), (self.decl, self.body).clean(cx)));
let did = cx.tcx.hir().local_def_id(self.id);
- let constness = if cx.tcx.is_min_const_fn(did) {
+ let constness = if is_min_const_fn(cx.tcx, did) {
hir::Constness::Const
} else {
hir::Constness::NotConst
};
let (all_types, ret_types) = get_all_types(&generics, &decl, cx);
if provided {
- let constness = if cx.tcx.is_min_const_fn(self.def_id) {
+ let constness = if is_min_const_fn(cx.tcx, self.def_id) {
hir::Constness::Const
} else {
hir::Constness::NotConst
false
}
- pub fn from_ast(diagnostic: &::errors::Handler, attrs: &[ast::Attribute]) -> Attributes {
+ pub fn from_ast(diagnostic: &::rustc_errors::Handler, attrs: &[ast::Attribute]) -> Attributes {
let mut doc_strings = vec![];
let mut sp = None;
let mut cfg = Cfg::True;
pub fn print_const(cx: &DocContext<'_>, n: &ty::Const<'_>) -> String {
match n.val {
- ty::ConstKind::Unevaluated(def_id, _) => {
- if let Some(hir_id) = cx.tcx.hir().as_local_hir_id(def_id) {
+ ty::ConstKind::Unevaluated(def_id, _, promoted) => {
+ let mut s = if let Some(hir_id) = cx.tcx.hir().as_local_hir_id(def_id) {
print_const_expr(cx, cx.tcx.hir().body_owned_by(hir_id))
} else {
inline::print_inlined_const(cx, def_id)
+ };
+ if let Some(promoted) = promoted {
+ s.push_str(&format!("::{:?}", promoted))
}
+ s
}
_ => {
let mut s = n.to_string();
use std::fmt;
use std::path::PathBuf;
-use errors;
use getopts;
use rustc::lint::Level;
use rustc::session;
use crate::html::markdown::IdMap;
use crate::html::static_files;
use crate::opts;
-use crate::passes::{self, DefaultPassOption};
+use crate::passes::{self, Condition, DefaultPassOption};
use crate::theme;
/// Configuration options for rustdoc.
///
/// Be aware: This option can come both from the CLI and from crate attributes!
pub default_passes: DefaultPassOption,
+ /// Document items that have lower than `pub` visibility.
+ pub document_private: bool,
+ /// Document items that have `doc(hidden)`.
+ pub document_hidden: bool,
/// Any passes manually selected by the user.
///
/// Be aware: This option can come both from the CLI and from crate attributes!
.field("test_args", &self.test_args)
.field("persist_doctests", &self.persist_doctests)
.field("default_passes", &self.default_passes)
+ .field("document_private", &self.document_private)
+ .field("document_hidden", &self.document_hidden)
.field("manual_passes", &self.manual_passes)
.field("display_warnings", &self.display_warnings)
.field("show_coverage", &self.show_coverage)
println!("{:>20} - {}", pass.name, pass.description);
}
println!("\nDefault passes for rustdoc:");
- for pass in passes::DEFAULT_PASSES {
- println!("{:>20}", pass.name);
- }
- println!("\nPasses run with `--document-private-items`:");
- for pass in passes::DEFAULT_PRIVATE_PASSES {
- println!("{:>20}", pass.name);
+ for p in passes::DEFAULT_PASSES {
+ print!("{:>20}", p.pass.name);
+ println_condition(p.condition);
}
if nightly_options::is_nightly_build() {
println!("\nPasses run with `--show-coverage`:");
- for pass in passes::DEFAULT_COVERAGE_PASSES {
- println!("{:>20}", pass.name);
+ for p in passes::COVERAGE_PASSES {
+ print!("{:>20}", p.pass.name);
+ println_condition(p.condition);
}
- println!("\nPasses run with `--show-coverage --document-private-items`:");
- for pass in passes::PRIVATE_COVERAGE_PASSES {
- println!("{:>20}", pass.name);
+ }
+
+ fn println_condition(condition: Condition) {
+ use Condition::*;
+ match condition {
+ Always => println!(),
+ WhenDocumentPrivate => println!(" (when --document-private-items)"),
+ WhenNotDocumentPrivate => println!(" (when not --document-private-items)"),
+ WhenNotDocumentHidden => println!(" (when not --document-hidden-items)"),
}
}
});
let show_coverage = matches.opt_present("show-coverage");
- let document_private = matches.opt_present("document-private-items");
let default_passes = if matches.opt_present("no-defaults") {
passes::DefaultPassOption::None
- } else if show_coverage && document_private {
- passes::DefaultPassOption::PrivateCoverage
} else if show_coverage {
passes::DefaultPassOption::Coverage
- } else if document_private {
- passes::DefaultPassOption::Private
} else {
passes::DefaultPassOption::Default
};
let runtool = matches.opt_str("runtool");
let runtool_args = matches.opt_strs("runtool-arg");
let enable_per_target_ignores = matches.opt_present("enable-per-target-ignores");
+ let document_private = matches.opt_present("document-private-items");
+ let document_hidden = matches.opt_present("document-hidden-items");
let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format);
should_test,
test_args,
default_passes,
+ document_private,
+ document_hidden,
manual_passes,
display_warnings,
show_coverage,
}
/// Prints deprecation warnings for deprecated options
-fn check_deprecated_options(matches: &getopts::Matches, diag: &errors::Handler) {
+fn check_deprecated_options(matches: &getopts::Matches, diag: &rustc_errors::Handler) {
let deprecated_flags = ["input-format", "output-format", "no-defaults", "passes"];
for flag in deprecated_flags.iter() {
-use rustc::lint;
use rustc::middle::cstore::CrateStore;
use rustc::middle::privacy::AccessLevels;
use rustc::session::config::ErrorOutputType;
use rustc_interface::interface;
use rustc_lint;
use rustc_resolve as resolve;
+use rustc_session::lint;
-use errors::emitter::{Emitter, EmitterWriter};
-use errors::json::JsonEmitter;
+use rustc_errors::emitter::{Emitter, EmitterWriter};
+use rustc_errors::json::JsonEmitter;
use rustc_span::source_map;
use rustc_span::symbol::sym;
use rustc_span::DUMMY_SP;
use crate::config::{Options as RustdocOptions, RenderOptions};
use crate::html::render::RenderInfo;
-use crate::passes;
+use crate::passes::{self, Condition::*, ConditionalPass};
pub use rustc::session::config::{CodegenOptions, DebuggingOptions, Input, Options};
pub use rustc::session::search_paths::SearchPath;
error_format: ErrorOutputType,
source_map: Option<Lrc<source_map::SourceMap>>,
debugging_opts: &DebuggingOptions,
-) -> errors::Handler {
+) -> rustc_errors::Handler {
let emitter: Box<dyn Emitter + sync::Send> = match error_format {
ErrorOutputType::HumanReadable(kind) => {
let (short, color_config) = kind.unzip();
}
};
- errors::Handler::with_emitter_and_flags(emitter, debugging_opts.diagnostic_handler_flags(true))
+ rustc_errors::Handler::with_emitter_and_flags(
+ emitter,
+ debugging_opts.diagnostic_handler_flags(true),
+ )
}
pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOptions) {
describe_lints,
lint_cap,
mut default_passes,
+ mut document_private,
+ document_hidden,
mut manual_passes,
display_warnings,
render_options,
let mut krate = clean::krate(&mut ctxt);
- fn report_deprecated_attr(name: &str, diag: &errors::Handler) {
+ fn report_deprecated_attr(name: &str, diag: &rustc_errors::Handler) {
let mut msg = diag.struct_warn(&format!(
"the `#![doc({})]` attribute is \
considered deprecated",
}
if attr.is_word() && name == sym::document_private_items {
- if default_passes == passes::DefaultPassOption::Default {
- default_passes = passes::DefaultPassOption::Private;
- }
+ document_private = true;
}
}
- let passes = passes::defaults(default_passes).iter().chain(
+ let passes = passes::defaults(default_passes).iter().copied().chain(
manual_passes.into_iter().flat_map(|name| {
if let Some(pass) = passes::find_pass(&name) {
- Some(pass)
+ Some(ConditionalPass::always(pass))
} else {
error!("unknown pass {}, skipping", name);
None
info!("Executing passes");
- for pass in passes {
- debug!("running pass {}", pass.name);
- krate = (pass.pass)(krate, &ctxt);
+ for p in passes {
+ let run = match p.condition {
+ Always => true,
+ WhenDocumentPrivate => document_private,
+ WhenNotDocumentPrivate => !document_private,
+ WhenNotDocumentHidden => !document_hidden,
+ };
+ if run {
+ debug!("running pass {}", p.pass.name);
+ krate = (p.pass.run)(krate, &ctxt);
+ }
}
ctxt.sess().abort_if_errors();
//! needs to read-after-write from a file, then it would be added to this
//! abstraction.
-use errors;
-
use std::fs;
use std::io;
use std::path::Path;
}
/// Prints all stored errors. Returns the number of printed errors.
- pub fn write_errors(&mut self, diag: &errors::Handler) -> usize {
+ pub fn write_errors(&mut self, diag: &rustc_errors::Handler) -> usize {
let mut printed = 0;
// In order to drop the sender part of the channel.
self.sender = None;
use crate::html::markdown::{ErrorCodes, IdMap, Markdown, Playground};
use crate::rustc_span::edition::Edition;
-use errors;
use rustc_feature::UnstableFeatures;
use std::fs;
use std::path::Path;
after_content: &[String],
md_before_content: &[String],
md_after_content: &[String],
- diag: &errors::Handler,
+ diag: &rustc_errors::Handler,
id_map: &mut IdMap,
edition: Edition,
playground: &Option<Playground>,
pub fn load_string<P: AsRef<Path>>(
file_path: P,
- diag: &errors::Handler,
+ diag: &rustc_errors::Handler,
) -> Result<String, LoadStringError> {
let file_path = file_path.as_ref();
let contents = match fs::read(file_path) {
}
}
-fn load_external_files(names: &[String], diag: &errors::Handler) -> Option<String> {
+fn load_external_files(names: &[String], diag: &rustc_errors::Handler) -> Option<String> {
let mut out = String::new();
for name in names {
let s = match load_string(name, diag) {
use std::str;
use std::sync::Arc;
-use errors;
use rustc::middle::privacy::AccessLevels;
use rustc::middle::stability;
use rustc_data_structures::flock;
mut krate: clean::Crate,
options: RenderOptions,
renderinfo: RenderInfo,
- diag: &errors::Handler,
+ diag: &rustc_errors::Handler,
edition: Edition,
) -> Result<(), Error> {
// need to save a copy of the options for rendering the index page
krate: &clean::Crate,
search_index: String,
options: &RenderOptions,
- diag: &errors::Handler,
+ diag: &rustc_errors::Handler,
) -> Result<(), Error> {
// Write out the shared files. Note that these are shared among all rustdoc
// docs placed in the output directory, so this needs to be a synchronized
}
.content .stability::before {
- content: '˪';
- font-size: 30px;
+ content: '⬑';
+ font-size: 25px;
position: absolute;
- top: -9px;
- left: -13px;
+ top: -6px;
+ left: -19px;
}
.content .impl-items .method, .content .impl-items > .type, .impl-items > .associatedconstant {
.content .highlighted.primitive { background-color: #00708a; }
.content .highlighted.keyword { background-color: #884719; }
+.content .stability::before { color: #ccc; }
+
.content span.enum, .content a.enum, .block a.current.enum { color: #82b089; }
.content span.struct, .content a.struct, .block a.current.struct { color: #2dbfb8; }
.content span.type, .content a.type, .block a.current.type { color: #ff7f00; }
.content .highlighted.primitive { background-color: #9aecff; }
.content .highlighted.keyword { background-color: #f99650; }
+.content .stability::before { color: #ccc; }
+
.content span.enum, .content a.enum, .block a.current.enum { color: #508157; }
.content span.struct, .content a.struct, .block a.current.struct { color: #ad448e; }
.content span.type, .content a.type, .block a.current.type { color: #ba5d00; }
#![feature(nll)]
#![feature(set_stdio)]
#![feature(test)]
+#![feature(vec_remove_item)]
#![feature(ptr_offset_from)]
#![feature(crate_visibility_modifier)]
-#![feature(const_fn)]
#![feature(drain_filter)]
#![feature(never_type)]
#![feature(unicode_internals)]
extern crate rustc_data_structures;
extern crate rustc_driver;
extern crate rustc_error_codes;
+extern crate rustc_errors;
extern crate rustc_expand;
extern crate rustc_feature;
extern crate rustc_hir;
extern crate rustc_lexer;
extern crate rustc_lint;
extern crate rustc_metadata;
+extern crate rustc_mir;
extern crate rustc_parse;
extern crate rustc_resolve;
+extern crate rustc_session;
extern crate rustc_span as rustc_span;
extern crate rustc_target;
extern crate rustc_typeck;
extern crate test as testing;
#[macro_use]
extern crate log;
-extern crate rustc_errors as errors;
use std::default::Default;
use std::env;
stable("document-private-items", |o| {
o.optflag("", "document-private-items", "document private items")
}),
+ unstable("document-hidden-items", |o| {
+ o.optflag("", "document-hidden-items", "document items that have doc(hidden)")
+ }),
stable("test", |o| o.optflag("", "test", "run code examples as tests")),
stable("test-args", |o| {
o.optmulti("", "test-args", "arguments to pass to the test runner", "ARGS")
match result {
Ok(output) => output,
- Err(_) => panic::resume_unwind(Box::new(errors::FatalErrorMarker)),
+ Err(_) => panic::resume_unwind(Box::new(rustc_errors::FatalErrorMarker)),
}
}
use std::io::prelude::*;
use std::path::PathBuf;
-use errors;
use rustc_feature::UnstableFeatures;
use rustc_span::edition::Edition;
use rustc_span::source_map::DUMMY_SP;
pub fn render(
input: PathBuf,
options: RenderOptions,
- diag: &errors::Handler,
+ diag: &rustc_errors::Handler,
edition: Edition,
) -> i32 {
let mut output = options.output;
}
/// Runs any tests/code examples in the markdown file `input`.
-pub fn test(mut options: Options, diag: &errors::Handler) -> i32 {
+pub fn test(mut options: Options, diag: &rustc_errors::Handler) -> i32 {
let input_str = match load_string(&options.input, diag) {
Ok(s) => s,
Err(LoadStringError::ReadFail) => return 1,
pub const CALCULATE_DOC_COVERAGE: Pass = Pass {
name: "calculate-doc-coverage",
- pass: calculate_doc_coverage,
+ run: calculate_doc_coverage,
description: "counts the number of items with and without documentation",
};
-use errors::{emitter::Emitter, Applicability, Diagnostic, Handler};
use rustc_data_structures::sync::{Lock, Lrc};
+use rustc_errors::{emitter::Emitter, Applicability, Diagnostic, Handler};
use rustc_parse::lexer::StringReader as Lexer;
use rustc_span::source_map::{FilePathMapping, SourceMap};
use rustc_span::{FileName, InnerSpan};
pub const CHECK_CODE_BLOCK_SYNTAX: Pass = Pass {
name: "check-code-block-syntax",
- pass: check_code_block_syntax,
+ run: check_code_block_syntax,
description: "validates syntax inside Rust code blocks",
};
pub const COLLAPSE_DOCS: Pass = Pass {
name: "collapse-docs",
- pass: collapse_docs,
+ run: collapse_docs,
description: "concatenates all document attributes into one document attribute",
};
-use errors::Applicability;
use rustc::lint;
use rustc::ty;
+use rustc_errors::Applicability;
use rustc_expand::base::SyntaxExtensionKind;
use rustc_feature::UnstableFeatures;
use rustc_hir as hir;
pub const COLLECT_INTRA_DOC_LINKS: Pass = Pass {
name: "collect-intra-doc-links",
- pass: collect_intra_doc_links,
+ run: collect_intra_doc_links,
description: "reads a crate's documentation to resolve intra-doc-links",
};
pub const COLLECT_TRAIT_IMPLS: Pass = Pass {
name: "collect-trait-impls",
- pass: collect_trait_impls,
+ run: collect_trait_impls,
description: "retrieves trait impls for items in the crate",
};
use std::mem;
use std::ops::Range;
+use self::Condition::*;
use crate::clean::{self, GetDefId, Item};
use crate::core::DocContext;
use crate::fold::{DocFolder, StripItem};
#[derive(Copy, Clone)]
pub struct Pass {
pub name: &'static str,
- pub pass: fn(clean::Crate, &DocContext<'_>) -> clean::Crate,
+ pub run: fn(clean::Crate, &DocContext<'_>) -> clean::Crate,
pub description: &'static str,
}
+/// In a list of passes, a pass that may or may not need to be run depending on options.
+#[derive(Copy, Clone)]
+pub struct ConditionalPass {
+ pub pass: Pass,
+ pub condition: Condition,
+}
+
+/// How to decide whether to run a conditional pass.
+#[derive(Copy, Clone)]
+pub enum Condition {
+ Always,
+ /// When `--document-private-items` is passed.
+ WhenDocumentPrivate,
+ /// When `--document-private-items` is not passed.
+ WhenNotDocumentPrivate,
+ /// When `--document-hidden-items` is not passed.
+ WhenNotDocumentHidden,
+}
+
/// The full list of passes.
pub const PASSES: &[Pass] = &[
CHECK_PRIVATE_ITEMS_DOC_TESTS,
];
/// The list of passes run by default.
-pub const DEFAULT_PASSES: &[Pass] = &[
- COLLECT_TRAIT_IMPLS,
- COLLAPSE_DOCS,
- UNINDENT_COMMENTS,
- CHECK_PRIVATE_ITEMS_DOC_TESTS,
- STRIP_HIDDEN,
- STRIP_PRIVATE,
- COLLECT_INTRA_DOC_LINKS,
- CHECK_CODE_BLOCK_SYNTAX,
- PROPAGATE_DOC_CFG,
+pub const DEFAULT_PASSES: &[ConditionalPass] = &[
+ ConditionalPass::always(COLLECT_TRAIT_IMPLS),
+ ConditionalPass::always(COLLAPSE_DOCS),
+ ConditionalPass::always(UNINDENT_COMMENTS),
+ ConditionalPass::always(CHECK_PRIVATE_ITEMS_DOC_TESTS),
+ ConditionalPass::new(STRIP_HIDDEN, WhenNotDocumentHidden),
+ 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(PROPAGATE_DOC_CFG),
];
-/// The list of default passes run with `--document-private-items` is passed to rustdoc.
-pub const DEFAULT_PRIVATE_PASSES: &[Pass] = &[
- COLLECT_TRAIT_IMPLS,
- COLLAPSE_DOCS,
- UNINDENT_COMMENTS,
- CHECK_PRIVATE_ITEMS_DOC_TESTS,
- STRIP_PRIV_IMPORTS,
- COLLECT_INTRA_DOC_LINKS,
- CHECK_CODE_BLOCK_SYNTAX,
- PROPAGATE_DOC_CFG,
+/// The list of default passes run when `--doc-coverage` is passed to rustdoc.
+pub const COVERAGE_PASSES: &[ConditionalPass] = &[
+ ConditionalPass::always(COLLECT_TRAIT_IMPLS),
+ ConditionalPass::new(STRIP_HIDDEN, WhenNotDocumentHidden),
+ ConditionalPass::new(STRIP_PRIVATE, WhenNotDocumentPrivate),
+ ConditionalPass::always(CALCULATE_DOC_COVERAGE),
];
-/// The list of default passes run when `--doc-coverage` is passed to rustdoc.
-pub const DEFAULT_COVERAGE_PASSES: &[Pass] =
- &[COLLECT_TRAIT_IMPLS, STRIP_HIDDEN, STRIP_PRIVATE, CALCULATE_DOC_COVERAGE];
+impl ConditionalPass {
+ pub const fn always(pass: Pass) -> Self {
+ Self::new(pass, Always)
+ }
-/// The list of default passes run when `--doc-coverage --document-private-items` is passed to
-/// rustdoc.
-pub const PRIVATE_COVERAGE_PASSES: &[Pass] = &[COLLECT_TRAIT_IMPLS, CALCULATE_DOC_COVERAGE];
+ pub const fn new(pass: Pass, condition: Condition) -> Self {
+ ConditionalPass { pass, condition }
+ }
+}
/// A shorthand way to refer to which set of passes to use, based on the presence of
-/// `--no-defaults` or `--document-private-items`.
+/// `--no-defaults` and `--show-coverage`.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum DefaultPassOption {
Default,
- Private,
Coverage,
- PrivateCoverage,
None,
}
/// Returns the given default set of passes.
-pub fn defaults(default_set: DefaultPassOption) -> &'static [Pass] {
+pub fn defaults(default_set: DefaultPassOption) -> &'static [ConditionalPass] {
match default_set {
DefaultPassOption::Default => DEFAULT_PASSES,
- DefaultPassOption::Private => DEFAULT_PRIVATE_PASSES,
- DefaultPassOption::Coverage => DEFAULT_COVERAGE_PASSES,
- DefaultPassOption::PrivateCoverage => PRIVATE_COVERAGE_PASSES,
+ DefaultPassOption::Coverage => COVERAGE_PASSES,
DefaultPassOption::None => &[],
}
}
/// If the given name matches a known pass, returns its information.
-pub fn find_pass(pass_name: &str) -> Option<&'static Pass> {
- PASSES.iter().find(|p| p.name == pass_name)
+pub fn find_pass(pass_name: &str) -> Option<Pass> {
+ PASSES.iter().find(|p| p.name == pass_name).copied()
}
struct Stripper<'a> {
pub const CHECK_PRIVATE_ITEMS_DOC_TESTS: Pass = Pass {
name: "check-private-items-doc-tests",
- pass: check_private_items_doc_tests,
+ run: check_private_items_doc_tests,
description: "check private items doc tests",
};
pub const PROPAGATE_DOC_CFG: Pass = Pass {
name: "propagate-doc-cfg",
- pass: propagate_doc_cfg,
+ run: propagate_doc_cfg,
description: "propagates `#[doc(cfg(...))]` to child items",
};
pub const STRIP_HIDDEN: Pass = Pass {
name: "strip-hidden",
- pass: strip_hidden,
+ run: strip_hidden,
description: "strips all doc(hidden) items from the output",
};
pub const STRIP_PRIV_IMPORTS: Pass = Pass {
name: "strip-priv-imports",
- pass: strip_priv_imports,
+ run: strip_priv_imports,
description: "strips all private import statements (`use`, `extern crate`) from a crate",
};
pub const STRIP_PRIVATE: Pass = Pass {
name: "strip-private",
- pass: strip_private,
+ run: strip_private,
description: "strips all private items from a crate which cannot be seen externally, \
implies strip-priv-imports",
};
pub const UNINDENT_COMMENTS: Pass = Pass {
name: "unindent-comments",
- pass: unindent_comments,
+ run: unindent_comments,
description: "removes excess indentation on comments in order for markdown to like it",
};
-use rustc::hir::intravisit;
use rustc::hir::map::Map;
use rustc::session::{self, config, DiagnosticOutput};
use rustc::util::common::ErrorReported;
use rustc_data_structures::sync::Lrc;
use rustc_feature::UnstableFeatures;
use rustc_hir as hir;
+use rustc_hir::intravisit;
use rustc_interface::interface;
use rustc_span::edition::Edition;
use rustc_span::source_map::SourceMap;
eprint!("{}", self.0);
}
}
-
let out = str::from_utf8(&output.stderr).unwrap();
let _bomb = Bomb(&out);
match (output.status.success(), compile_fail) {
(true, false) => {}
(false, true) => {
if !error_codes.is_empty() {
- error_codes.retain(|err| !out.contains(err));
+ error_codes.retain(|err| !out.contains(&format!("error[{}]: ", err)));
if !error_codes.is_empty() {
return Err(TestFailure::MissingErrorCodes(error_codes));
// crate already is included.
let result = rustc_driver::catch_fatal_errors(|| {
with_globals(edition, || {
- use errors::emitter::EmitterWriter;
- use errors::Handler;
+ use rustc_errors::emitter::EmitterWriter;
+ use rustc_errors::Handler;
use rustc_parse::maybe_new_parser_from_source_str;
use rustc_span::source_map::FilePathMapping;
use syntax::sess::ParseSess;
}
impl<'a, 'hir> intravisit::Visitor<'hir> for HirCollector<'a, 'hir> {
- fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'hir> {
+ type Map = Map<'hir>;
+
+ fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<'_, Self::Map> {
intravisit::NestedVisitorMap::All(&self.map)
}
use std::hash::{Hash, Hasher};
use std::path::Path;
-use errors::Handler;
+use rustc_errors::Handler;
#[cfg(test)]
mod tests;
[dev-dependencies]
rand = "0.7"
-[target.x86_64-apple-darwin.dependencies]
-rustc_asan = { path = "../librustc_asan" }
-rustc_tsan = { path = "../librustc_tsan" }
-
-[target.x86_64-unknown-linux-gnu.dependencies]
-rustc_asan = { path = "../librustc_asan" }
-rustc_lsan = { path = "../librustc_lsan" }
-rustc_msan = { path = "../librustc_msan" }
-rustc_tsan = { path = "../librustc_tsan" }
-
[target.'cfg(any(all(target_arch = "wasm32", not(target_os = "emscripten")), all(target_vendor = "fortanix", target_env = "sgx")))'.dependencies]
dlmalloc = { version = "0.1", features = ['rustc-dep-of-std'] }
self.map.raw_entry_mut().from_key(&value).or_insert(value, ()).0
}
+ /// Inserts an owned copy of the given `value` into the set if it is not
+ /// present, then returns a reference to the value in the set.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(hash_set_entry)]
+ ///
+ /// use std::collections::HashSet;
+ ///
+ /// let mut set: HashSet<String> = ["cat", "dog", "horse"]
+ /// .iter().map(|&pet| pet.to_owned()).collect();
+ ///
+ /// assert_eq!(set.len(), 3);
+ /// for &pet in &["cat", "dog", "fish"] {
+ /// let value = set.get_or_insert_owned(pet);
+ /// assert_eq!(value, pet);
+ /// }
+ /// assert_eq!(set.len(), 4); // a new "fish" was inserted
+ /// ```
+ #[inline]
+ #[unstable(feature = "hash_set_entry", issue = "60896")]
+ pub fn get_or_insert_owned<Q: ?Sized>(&mut self, value: &Q) -> &T
+ where
+ T: Borrow<Q>,
+ Q: Hash + Eq + ToOwned<Owned = T>,
+ {
+ // Although the raw entry gives us `&mut T`, we only return `&T` to be consistent with
+ // `get`. Key mutation is "raw" because you're not supposed to affect `Eq` or `Hash`.
+ self.map.raw_entry_mut().from_key(value).or_insert_with(|| (value.to_owned(), ())).0
+ }
+
/// Inserts a value computed from `f` into the set if the given `value` is
/// not present, then returns a reference to the value in the set.
///
/// assert!(
/// mem::size_of::<Box<dyn Error + Send + Sync>>() == mem::size_of_val(&a_boxed_error))
/// ```
+ #[inline]
fn from(err: String) -> Box<dyn Error + Send + Sync> {
struct StringError(String);
/// assert!(
/// mem::size_of::<Box<dyn Error + Send + Sync>>() == mem::size_of_val(&a_boxed_error))
/// ```
+ #[inline]
fn from(err: &str) -> Box<dyn Error + Send + Sync + 'a> {
From::from(String::from(err))
}
/// function will return the corresponding [`&str`] slice. Otherwise,
/// it will return an error with details of where UTF-8 validation failed.
///
- /// > **Note**: This method is currently implemented to check for validity
- /// > after a constant-time cast, but it is planned to alter its definition
- /// > in the future to perform the length calculation in addition to the
- /// > UTF-8 check whenever this method is called.
- ///
/// [`&str`]: ../primitive.str.html
///
/// # Examples
/// [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD] and return a
/// [`Cow`]`::`[`Owned`]`(`[`String`]`)` with the result.
///
- /// > **Note**: This method is currently implemented to check for validity
- /// > after a constant-time cast, but it is planned to alter its definition
- /// > in the future to perform the length calculation in addition to the
- /// > UTF-8 check whenever this method is called.
- ///
/// [`Cow`]: ../borrow/enum.Cow.html
/// [`Borrowed`]: ../borrow/enum.Cow.html#variant.Borrowed
/// [`Owned`]: ../borrow/enum.Cow.html#variant.Owned
/// assert!(!os_str.is_empty());
/// ```
#[stable(feature = "osstring_simple_functions", since = "1.9.0")]
+ #[inline]
pub fn is_empty(&self) -> bool {
self.inner.inner.is_empty()
}
#[stable(feature = "rust1", since = "1.0.0")]
impl AsRef<OsStr> for OsString {
+ #[inline]
fn as_ref(&self) -> &OsStr {
self
}
/// ```
#[stable(feature = "sockaddr_checker", since = "1.16.0")]
pub fn is_ipv4(&self) -> bool {
- match *self {
- SocketAddr::V4(_) => true,
- SocketAddr::V6(_) => false,
- }
+ matches!(*self, SocketAddr::V4(_))
}
/// Returns [`true`] if the [IP address] in this `SocketAddr` is an
/// ```
#[stable(feature = "sockaddr_checker", since = "1.16.0")]
pub fn is_ipv6(&self) -> bool {
- match *self {
- SocketAddr::V4(_) => false,
- SocketAddr::V6(_) => true,
- }
+ matches!(*self, SocketAddr::V6(_))
}
}
/// ```
#[stable(feature = "ipaddr_checker", since = "1.16.0")]
pub fn is_ipv4(&self) -> bool {
- match self {
- IpAddr::V4(_) => true,
- IpAddr::V6(_) => false,
- }
+ matches!(self, IpAddr::V4(_))
}
/// Returns [`true`] if this address is an [IPv6 address], and [`false`] otherwise.
/// ```
#[stable(feature = "ipaddr_checker", since = "1.16.0")]
pub fn is_ipv6(&self) -> bool {
- match self {
- IpAddr::V4(_) => false,
- IpAddr::V6(_) => true,
- }
+ matches!(self, IpAddr::V6(_))
}
}
#[stable(feature = "rust1", since = "1.0.0")]
pub fn is_verbatim(&self) -> bool {
use self::Prefix::*;
- match *self {
- Verbatim(_) | VerbatimDisk(_) | VerbatimUNC(..) => true,
- _ => false,
- }
+ matches!(*self, Verbatim(_) | VerbatimDisk(_) | VerbatimUNC(..))
}
#[inline]
fn is_drive(&self) -> bool {
- match *self {
- Prefix::Disk(_) => true,
- _ => false,
- }
+ matches!(*self, Prefix::Disk(_))
}
#[inline]
/// Converts a `OsString` into a `PathBuf`
///
/// This conversion does not allocate or copy memory.
+ #[inline]
fn from(s: OsString) -> PathBuf {
PathBuf { inner: s }
}
#[stable(feature = "rust1", since = "1.0.0")]
impl ops::Deref for PathBuf {
type Target = Path;
-
+ #[inline]
fn deref(&self) -> &Path {
Path::new(&self.inner)
}
#[stable(feature = "rust1", since = "1.0.0")]
impl AsRef<Path> for str {
+ #[inline]
fn as_ref(&self) -> &Path {
Path::new(self)
}
#[stable(feature = "rust1", since = "1.0.0")]
impl AsRef<Path> for PathBuf {
+ #[inline]
fn as_ref(&self) -> &Path {
self
}
// At this point, all spawned threads should be blocked,
// so we shouldn't get anything from the port
- assert!(match rx.try_recv() {
- Err(TryRecvError::Empty) => true,
- _ => false,
- });
+ assert!(matches!(rx.try_recv(), Err(TryRecvError::Empty)));
let mut leader_found = barrier.wait().is_leader();
// Just tests whether this channel has been sent on or not, this is only
// safe to use from the sender.
pub fn sent(&self) -> bool {
- unsafe {
- match *self.upgrade.get() {
- NothingSent => false,
- _ => true,
- }
- }
+ unsafe { !matches!(*self.upgrade.get(), NothingSent) }
}
pub fn recv(&self, deadline: Option<Instant>) -> Result<T, Failure<T>> {
use crate::marker::PhantomData;
use crate::memchr;
use crate::path::{self, PathBuf};
-use crate::ptr;
use crate::str;
use crate::sync::Mutex;
use crate::sys::hermit::abi;
unsafe {
ENV = Some(Mutex::new(HashMap::new()));
+ if env.is_null() {
+ return;
+ }
+
let mut guard = ENV.as_ref().unwrap().lock().unwrap();
let mut environ = env;
- while environ != ptr::null() && *environ != ptr::null() {
+ while !(*environ).is_null() {
if let Some((key, value)) = parse(CStr::from_ptr(*environ).to_bytes()) {
guard.insert(key, value);
}
- environ = environ.offset(1);
+ environ = environ.add(1);
}
}
static mut LOCALS: *mut BTreeMap<Key, *mut u8> = ptr::null_mut();
unsafe fn keys() -> &'static mut BTreeMap<Key, Option<Dtor>> {
- if KEYS == ptr::null_mut() {
+ if KEYS.is_null() {
KEYS = Box::into_raw(Box::new(BTreeMap::new()));
}
&mut *KEYS
}
unsafe fn locals() -> &'static mut BTreeMap<Key, *mut u8> {
- if LOCALS == ptr::null_mut() {
+ if LOCALS.is_null() {
LOCALS = Box::into_raw(Box::new(BTreeMap::new()));
}
&mut *LOCALS
any_non_null_dtor = false;
for (value, dtor) in TLS_KEY_IN_USE.iter().filter_map(&value_with_destructor) {
let value = value.replace(ptr::null_mut());
- if value != ptr::null_mut() {
+ if !value.is_null() {
any_non_null_dtor = true;
unsafe { dtor(value) }
}
pub struct File(FileDesc);
-// FIXME: This should be available on Linux with all `target_arch` and `target_env`.
-// https://github.com/rust-lang/libc/issues/1545
+// FIXME: This should be available on Linux with all `target_env`.
+// But currently only glibc exposes `statx` fn and structs.
+// We don't want to import unverified raw C structs here directly.
+// https://github.com/rust-lang/rust/pull/67774
macro_rules! cfg_has_statx {
({ $($then_tt:tt)* } else { $($else_tt:tt)* }) => {
cfg_if::cfg_if! {
- if #[cfg(all(target_os = "linux", target_env = "gnu", any(
- target_arch = "x86",
- target_arch = "arm",
- // target_arch = "mips",
- target_arch = "powerpc",
- target_arch = "x86_64",
- // target_arch = "aarch64",
- target_arch = "powerpc64",
- // target_arch = "mips64",
- // target_arch = "s390x",
- target_arch = "sparc64",
- target_arch = "riscv64",
- )))] {
+ if #[cfg(all(target_os = "linux", target_env = "gnu"))] {
$($then_tt)*
} else {
$($else_tt)*
}
};
($($block_inner:tt)*) => {
- #[cfg(all(target_os = "linux", target_env = "gnu", any(
- target_arch = "x86",
- target_arch = "arm",
- // target_arch = "mips",
- target_arch = "powerpc",
- target_arch = "x86_64",
- // target_arch = "aarch64",
- target_arch = "powerpc64",
- // target_arch = "mips64",
- // target_arch = "s390x",
- target_arch = "sparc64",
- target_arch = "riscv64",
- )))]
+ #[cfg(all(target_os = "linux", target_env = "gnu"))]
{
$($block_inner)*
}
let _guard = env_lock();
let mut environ = *environ();
let mut result = Vec::new();
- while environ != ptr::null() && *environ != ptr::null() {
- if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
- result.push(key_value);
+ if !environ.is_null() {
+ while !(*environ).is_null() {
+ if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
+ result.push(key_value);
+ }
+ environ = environ.add(1);
}
- environ = environ.offset(1);
}
return Env { iter: result.into_iter(), _dont_send_or_sync_me: PhantomData };
}
#[cfg(not(test))]
pub fn init() {
- // By default, some platforms will send a *signal* when an EPIPE error
- // would otherwise be delivered. This runtime doesn't install a SIGPIPE
- // handler, causing it to kill the program, which isn't exactly what we
- // want!
- //
- // Hence, we set SIGPIPE to ignore when the program starts up in order
- // to prevent this problem.
+ // ignore SIGPIPE
unsafe {
- reset_sigpipe();
+ assert!(signal(libc::SIGPIPE, libc::SIG_IGN) != libc::SIG_ERR);
}
-
- unsafe fn reset_sigpipe() {}
}
pub use libc::signal;
use crate::mem;
use crate::memchr;
use crate::path::{self, Path, PathBuf};
-use crate::ptr;
use crate::slice;
use crate::str;
use crate::sys::cvt;
unsafe {
let _guard = env_lock();
let mut environ = *environ();
- if environ == ptr::null() {
+ if environ.is_null() {
panic!("os::env() failure getting env string from OS: {}", io::Error::last_os_error());
}
let mut result = Vec::new();
- while *environ != ptr::null() {
+ while !(*environ).is_null() {
if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
result.push(key_value);
}
- environ = environ.offset(1);
+ environ = environ.add(1);
}
return Env { iter: result.into_iter(), _dont_send_or_sync_me: PhantomData };
}
+++ /dev/null
-//! Support for "weak linkage" to symbols on Unix
-//!
-//! Some I/O operations we do in libstd require newer versions of OSes but we
-//! need to maintain binary compatibility with older releases for now. In order
-//! to use the new functionality when available we use this module for
-//! detection.
-//!
-//! One option to use here is weak linkage, but that is unfortunately only
-//! really workable on Linux. Hence, use dlsym to get the symbol value at
-//! runtime. This is also done for compatibility with older versions of glibc,
-//! and to avoid creating dependencies on GLIBC_PRIVATE symbols. It assumes that
-//! we've been dynamically linked to the library the symbol comes from, but that
-//! is currently always the case for things like libpthread/libc.
-//!
-//! A long time ago this used weak linkage for the __pthread_get_minstack
-//! symbol, but that caused Debian to detect an unnecessarily strict versioned
-//! dependency on libc6 (#23628).
-
-use crate::ffi::CStr;
-use crate::marker;
-use crate::mem;
-use crate::sync::atomic::{AtomicUsize, Ordering};
-
-pub struct Weak<F> {
- name: &'static str,
- addr: AtomicUsize,
- _marker: marker::PhantomData<F>,
-}
-
-impl<F> Weak<F> {
- pub const fn new(name: &'static str) -> Weak<F> {
- Weak { name, addr: AtomicUsize::new(1), _marker: marker::PhantomData }
- }
-
- pub fn get(&self) -> Option<F> {
- assert_eq!(mem::size_of::<F>(), mem::size_of::<usize>());
- unsafe {
- if self.addr.load(Ordering::SeqCst) == 1 {
- self.addr.store(fetch(self.name), Ordering::SeqCst);
- }
- match self.addr.load(Ordering::SeqCst) {
- 0 => None,
- addr => Some(mem::transmute_copy::<usize, F>(&addr)),
- }
- }
- }
-}
-
-unsafe fn fetch(name: &str) -> usize {
- let name = match CStr::from_bytes_with_nul(name.as_bytes()) {
- Ok(cstr) => cstr,
- Err(..) => return 0,
- };
- assert!(false, "FIXME: fetch");
- libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr()) as usize
-}
use crate::marker::PhantomData;
use crate::os::wasi::prelude::*;
use crate::path::{self, PathBuf};
-use crate::ptr;
use crate::str;
use crate::sys::memchr;
use crate::sys::{unsupported, Void};
let _guard = env_lock();
let mut environ = libc::environ;
let mut result = Vec::new();
- while environ != ptr::null_mut() && *environ != ptr::null_mut() {
- if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
- result.push(key_value);
+ if !environ.is_null() {
+ while !(*environ).is_null() {
+ if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
+ result.push(key_value);
+ }
+ environ = environ.add(1);
}
- environ = environ.offset(1);
}
return Env { iter: result.into_iter(), _dont_send_or_sync_me: PhantomData };
}
];
module = c::GetModuleHandleW(NTDLL_DLL.as_ptr());
- if module != ptr::null_mut() {
+ if !module.is_null() {
errnum ^= c::FACILITY_NT_BIT as i32;
flags = c::FORMAT_MESSAGE_FROM_HMODULE;
}
self.inner.shrink_to(min_capacity)
}
+ #[inline]
pub fn as_slice(&self) -> &Slice {
unsafe { mem::transmute(&*self.inner) }
}
/// |:---------:|:--------------------------------------------------------------------:|
/// | Cloud ABI | [clock_time_get (Monotonic Clock)] |
/// | SGX | [`insecure_time` usercall]. More information on [timekeeping in SGX] |
-/// | UNIX | [clock_time_get (Monotonic Clock)] |
+/// | UNIX | [clock_gettime (Monotonic Clock)] |
/// | Darwin | [mach_absolute_time] |
/// | VXWorks | [clock_gettime (Monotonic Clock)] |
/// | WASI | [__wasi_clock_time_get (Monotonic Clock)] |
doctest = false
[dependencies]
-bitflags = "1.2.1"
rustc_serialize = { path = "../libserialize", package = "serialize" }
log = "0.4"
scoped-tls = "1.0"
-lazy_static = "1.0.0"
+rustc_errors = { path = "../librustc_errors" }
rustc_span = { path = "../librustc_span" }
-errors = { path = "../librustc_errors", package = "rustc_errors" }
rustc_data_structures = { path = "../librustc_data_structures" }
rustc_feature = { path = "../librustc_feature" }
rustc_index = { path = "../librustc_index" }
Lit(P<Expr>),
/// A range pattern (e.g., `1...2`, `1..=2` or `1..2`).
- Range(P<Expr>, P<Expr>, Spanned<RangeEnd>),
+ Range(Option<P<Expr>>, Option<P<Expr>>, Spanned<RangeEnd>),
/// A slice pattern `[a, b, c]`.
Slice(Vec<P<Pat>>),
pub fn to_bound(&self) -> Option<GenericBound> {
match &self.kind {
ExprKind::Path(None, path) => Some(GenericBound::Trait(
- PolyTraitRef::new(Vec::new(), path.clone(), self.span),
+ PolyTraitRef::new(Vec::new(), path.clone(), None, self.span),
TraitBoundModifier::None,
)),
_ => None,
pub struct TraitRef {
pub path: Path,
pub ref_id: NodeId,
+
+ /// The `const` modifier, if any, that appears before this trait.
+ ///
+ /// | | `constness` |
+ /// |----------------|-----------------------------|
+ /// | `Trait` | `None` |
+ /// | `const Trait` | `Some(Constness::Const)` |
+ /// | `?const Trait` | `Some(Constness::NotConst)` |
+ pub constness: Option<Constness>,
}
#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
}
impl PolyTraitRef {
- pub fn new(generic_params: Vec<GenericParam>, path: Path, span: Span) -> Self {
+ pub fn new(
+ generic_params: Vec<GenericParam>,
+ path: Path,
+ constness: Option<Constness>,
+ span: Span,
+ ) -> Self {
PolyTraitRef {
bound_generic_params: generic_params,
- trait_ref: TraitRef { path, ref_id: DUMMY_NODE_ID },
+ trait_ref: TraitRef { path, constness, ref_id: DUMMY_NODE_ID },
span,
}
}
use super::{mark_used, MetaItemKind};
use crate::ast::{self, Attribute, MetaItem, NestedMetaItem};
-use crate::feature_gate::feature_err;
use crate::print::pprust;
-use crate::sess::ParseSess;
+use crate::sess::{feature_err, ParseSess};
-use errors::{Applicability, Handler};
+use rustc_errors::{struct_span_err, Applicability, Handler};
use rustc_feature::{find_gated_cfg, is_builtin_attr_name, Features, GatedCfg};
use rustc_macros::HashStable_Generic;
use rustc_span::hygiene::Transparency;
fn handle_errors(sess: &ParseSess, span: Span, error: AttrError) {
let diag = &sess.span_diagnostic;
match error {
- AttrError::MultipleItem(item) => span_err!(diag, span, E0538, "multiple '{}' items", item),
+ AttrError::MultipleItem(item) => {
+ struct_span_err!(diag, span, E0538, "multiple '{}' items", item).emit();
+ }
AttrError::UnknownMetaItem(item, expected) => {
let expected = expected.iter().map(|name| format!("`{}`", name)).collect::<Vec<_>>();
struct_span_err!(diag, span, E0541, "unknown meta item '{}'", item)
.span_label(span, format!("expected one of {}", expected.join(", ")))
.emit();
}
- AttrError::MissingSince => span_err!(diag, span, E0542, "missing 'since'"),
- AttrError::MissingFeature => span_err!(diag, span, E0546, "missing 'feature'"),
+ AttrError::MissingSince => struct_span_err!(diag, span, E0542, "missing 'since'").emit(),
+ AttrError::MissingFeature => {
+ struct_span_err!(diag, span, E0546, "missing 'feature'").emit();
+ }
AttrError::MultipleStabilityLevels => {
- span_err!(diag, span, E0544, "multiple stability levels")
+ struct_span_err!(diag, span, E0544, "multiple stability levels").emit();
}
AttrError::UnsupportedLiteral(msg, is_bytestr) => {
let mut err = struct_span_err!(diag, span, E0565, "{}", msg);
*item = Some(v);
true
} else {
- span_err!(diagnostic, meta.span, E0539, "incorrect meta item");
+ struct_span_err!(diagnostic, meta.span, E0539, "incorrect meta item").emit();
false
}
};
match meta_name {
sym::rustc_deprecated => {
if rustc_depr.is_some() {
- span_err!(
+ struct_span_err!(
diagnostic,
item_sp,
E0540,
"multiple rustc_deprecated attributes"
- );
+ )
+ .emit();
continue 'outer;
}
continue;
}
_ => {
- span_err!(diagnostic, attr.span, E0543, "missing 'reason'");
+ struct_span_err!(diagnostic, attr.span, E0543, "missing 'reason'")
+ .emit();
continue;
}
}
// Disallowing this requires updates to some submodules
NonZeroU32::new(num)
} else {
- span_err!(
+ struct_span_err!(
diagnostic,
attr.span,
E0545,
"incorrect 'issue'"
- );
+ )
+ .emit();
continue;
}
}
continue;
}
_ => {
- span_err!(diagnostic, attr.span, E0547, "missing 'issue'");
+ struct_span_err!(diagnostic, attr.span, E0547, "missing 'issue'")
+ .emit();
continue;
}
}
if let Some(ref mut stab) = stab {
stab.rustc_depr = Some(rustc_depr);
} else {
- span_err!(
+ struct_span_err!(
diagnostic,
item_sp,
E0549,
"rustc_deprecated attribute must be paired with \
either stable or unstable attribute"
- );
+ )
+ .emit();
}
}
stab.promotable = promotable;
stab.allow_const_fn_ptr = allow_const_fn_ptr;
} else {
- span_err!(
+ struct_span_err!(
diagnostic,
item_sp,
E0717,
"rustc_promotable and rustc_allow_const_fn_ptr attributes \
must be paired with either a rustc_const_unstable or a rustc_const_stable \
attribute"
- );
+ )
+ .emit();
}
}
}
sym::not => {
if mis.len() != 1 {
- span_err!(sess.span_diagnostic, cfg.span, E0536, "expected 1 cfg-pattern");
+ struct_span_err!(
+ sess.span_diagnostic,
+ cfg.span,
+ E0536,
+ "expected 1 cfg-pattern"
+ )
+ .emit();
return false;
}
!eval_condition(mis[0].meta_item().unwrap(), sess, eval)
}
_ => {
- span_err!(
+ struct_span_err!(
sess.span_diagnostic,
cfg.span,
E0537,
"invalid predicate `{}`",
pprust::path_to_string(&cfg.path)
- );
+ )
+ .emit();
false
}
}
}
if depr.is_some() {
- span_err!(diagnostic, item_sp, E0550, "multiple deprecated attributes");
+ struct_span_err!(diagnostic, item_sp, E0550, "multiple deprecated attributes").emit();
break;
}
),
);
} else {
- span_err!(diagnostic, meta.span, E0551, "incorrect meta item");
+ struct_span_err!(diagnostic, meta.span, E0551, "incorrect meta item")
+ .emit();
}
false
};
}
if let Some(literal_error) = literal_error {
- span_err!(
+ struct_span_err!(
diagnostic,
item.span(),
E0589,
"invalid `repr(align)` attribute: {}",
literal_error
- );
+ )
+ .emit();
}
} else {
if let Some(meta_item) = item.meta_item() {
}
if !recognised {
// Not a word we recognize
- span_err!(diagnostic, item.span(), E0552, "unrecognized representation hint");
+ struct_span_err!(
+ diagnostic,
+ item.span(),
+ E0552,
+ "unrecognized representation hint"
+ )
+ .emit();
}
}
}
pub fn allow_internal_unstable<'a>(
attrs: &[Attribute],
- span_diagnostic: &'a errors::Handler,
+ span_diagnostic: &'a rustc_errors::Handler,
) -> Option<impl Iterator<Item = Symbol> + 'a> {
find_by_name(attrs, sym::allow_internal_unstable).and_then(|attr| {
attr.meta_item_list()
+++ /dev/null
-#[macro_export]
-macro_rules! diagnostic_used {
- ($code:ident) => {
- let _ = $code;
- };
-}
-
-#[macro_export]
-macro_rules! span_fatal {
- ($session:expr, $span:expr, $code:ident, $($message:tt)*) => ({
- $crate::diagnostic_used!($code);
- $session.span_fatal_with_code(
- $span,
- &format!($($message)*),
- $crate::errors::DiagnosticId::Error(stringify!($code).to_owned()),
- )
- })
-}
-
-#[macro_export]
-macro_rules! span_err {
- ($session:expr, $span:expr, $code:ident, $($message:tt)*) => ({
- $crate::diagnostic_used!($code);
- $session.span_err_with_code(
- $span,
- &format!($($message)*),
- $crate::errors::DiagnosticId::Error(stringify!($code).to_owned()),
- )
- })
-}
-
-#[macro_export]
-macro_rules! span_warn {
- ($session:expr, $span:expr, $code:ident, $($message:tt)*) => ({
- $crate::diagnostic_used!($code);
- $session.span_warn_with_code(
- $span,
- &format!($($message)*),
- $crate::errors::DiagnosticId::Error(stringify!($code).to_owned()),
- )
- })
-}
-
-#[macro_export]
-macro_rules! struct_err {
- ($session:expr, $code:ident, $($message:tt)*) => ({
- $crate::diagnostic_used!($code);
- $session.struct_err_with_code(
- &format!($($message)*),
- $crate::errors::DiagnosticId::Error(stringify!($code).to_owned()),
- )
- })
-}
-
-#[macro_export]
-macro_rules! span_err_or_warn {
- ($is_warning:expr, $session:expr, $span:expr, $code:ident, $($message:tt)*) => ({
- $crate::diagnostic_used!($code);
- if $is_warning {
- $session.span_warn_with_code(
- $span,
- &format!($($message)*),
- $crate::errors::DiagnosticId::Error(stringify!($code).to_owned()),
- )
- } else {
- $session.span_err_with_code(
- $span,
- &format!($($message)*),
- $crate::errors::DiagnosticId::Error(stringify!($code).to_owned()),
- )
- }
- })
-}
-
-#[macro_export]
-macro_rules! struct_span_fatal {
- ($session:expr, $span:expr, $code:ident, $($message:tt)*) => ({
- $crate::diagnostic_used!($code);
- $session.struct_span_fatal_with_code(
- $span,
- &format!($($message)*),
- $crate::errors::DiagnosticId::Error(stringify!($code).to_owned()),
- )
- })
-}
-
-#[macro_export]
-macro_rules! struct_span_err {
- ($session:expr, $span:expr, $code:ident, $($message:tt)*) => ({
- $crate::diagnostic_used!($code);
- $session.struct_span_err_with_code(
- $span,
- &format!($($message)*),
- $crate::errors::DiagnosticId::Error(stringify!($code).to_owned()),
- )
- })
-}
-
-#[macro_export]
-macro_rules! stringify_error_code {
- ($code:ident) => {{
- $crate::diagnostic_used!($code);
- $crate::errors::DiagnosticId::Error(stringify!($code).to_owned())
- }};
-}
-
-#[macro_export]
-macro_rules! type_error_struct {
- ($session:expr, $span:expr, $typ:expr, $code:ident, $($message:tt)*) => ({
- if $typ.references_error() {
- $session.diagnostic().struct_dummy()
- } else {
- struct_span_err!($session, $span, $code, $($message)*)
- }
- })
-}
-
-#[macro_export]
-macro_rules! struct_span_warn {
- ($session:expr, $span:expr, $code:ident, $($message:tt)*) => ({
- $crate::diagnostic_used!($code);
- $session.struct_span_warn_with_code(
- $span,
- &format!($($message)*),
- $crate::errors::DiagnosticId::Error(stringify!($code).to_owned()),
- )
- })
-}
-
-#[macro_export]
-macro_rules! struct_span_err_or_warn {
- ($is_warning:expr, $session:expr, $span:expr, $code:ident, $($message:tt)*) => ({
- $crate::diagnostic_used!($code);
- if $is_warning {
- $session.struct_span_warn_with_code(
- $span,
- &format!($($message)*),
- $crate::errors::DiagnosticId::Error(stringify!($code).to_owned()),
- )
- } else {
- $session.struct_span_err_with_code(
- $span,
- &format!($($message)*),
- $crate::errors::DiagnosticId::Error(stringify!($code).to_owned()),
- )
- }
- })
-}
-
-#[macro_export]
-macro_rules! span_note {
- ($err:expr, $span:expr, $($message:tt)*) => ({
- ($err).span_note($span, &format!($($message)*));
- })
-}
-
-#[macro_export]
-macro_rules! span_help {
- ($err:expr, $span:expr, $($message:tt)*) => ({
- ($err).span_help($span, &format!($($message)*));
- })
-}
-
-#[macro_export]
-macro_rules! help {
- ($err:expr, $($message:tt)*) => ({
- ($err).help(&format!($($message)*));
- })
-}
+++ /dev/null
-//! Allows the buffering of lints for later.
-//!
-//! Since we cannot have a dependency on `librustc`, we implement some types here that are somewhat
-//! redundant. Later, these types can be converted to types for use by the rest of the compiler.
-
-use rustc_session::declare_lint;
-pub use rustc_session::lint::BufferedEarlyLint;
-use rustc_session::lint::FutureIncompatibleInfo;
-
-declare_lint! {
- pub ILL_FORMED_ATTRIBUTE_INPUT,
- Deny,
- "ill-formed attribute inputs that were previously accepted and used in practice",
- @future_incompatible = FutureIncompatibleInfo {
- reference: "issue #57571 <https://github.com/rust-lang/rust/issues/57571>",
- edition: None,
- };
-}
-
-declare_lint! {
- pub META_VARIABLE_MISUSE,
- Allow,
- "possible meta-variable misuse at macro definition"
-}
-
-declare_lint! {
- pub INCOMPLETE_INCLUDE,
- Deny,
- "trailing content in included file"
-}
+++ /dev/null
-use crate::ast::{self, AssocTyConstraint, AssocTyConstraintKind, NodeId};
-use crate::ast::{GenericParam, GenericParamKind, PatKind, RangeEnd, VariantData};
-use crate::attr;
-use crate::sess::ParseSess;
-use crate::visit::{self, FnKind, Visitor};
-
-use errors::{Applicability, DiagnosticBuilder, Handler};
-use rustc_data_structures::fx::FxHashMap;
-use rustc_error_codes::*;
-use rustc_feature::{find_feature_issue, GateIssue};
-use rustc_feature::{AttributeGate, BUILTIN_ATTRIBUTE_MAP};
-use rustc_feature::{Feature, Features, State as FeatureState, UnstableFeatures};
-use rustc_feature::{
- ACCEPTED_FEATURES, ACTIVE_FEATURES, REMOVED_FEATURES, STABLE_REMOVED_FEATURES,
-};
-use rustc_span::edition::{Edition, ALL_EDITIONS};
-use rustc_span::source_map::Spanned;
-use rustc_span::symbol::{sym, Symbol};
-use rustc_span::{MultiSpan, Span, DUMMY_SP};
-
-use log::debug;
-
-macro_rules! gate_feature_fn {
- ($cx: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr, $level: expr) => {{
- let (cx, has_feature, span, name, explain, level) =
- (&*$cx, $has_feature, $span, $name, $explain, $level);
- let has_feature: bool = has_feature(&$cx.features);
- debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", name, span, has_feature);
- if !has_feature && !span.allows_unstable($name) {
- leveled_feature_err(cx.parse_sess, name, span, GateIssue::Language, explain, level)
- .emit();
- }
- }};
-}
-
-macro_rules! gate_feature {
- ($cx: expr, $feature: ident, $span: expr, $explain: expr) => {
- gate_feature_fn!(
- $cx,
- |x: &Features| x.$feature,
- $span,
- sym::$feature,
- $explain,
- GateStrength::Hard
- )
- };
- ($cx: expr, $feature: ident, $span: expr, $explain: expr, $level: expr) => {
- gate_feature_fn!($cx, |x: &Features| x.$feature, $span, sym::$feature, $explain, $level)
- };
-}
-
-pub fn check_attribute(attr: &ast::Attribute, parse_sess: &ParseSess, features: &Features) {
- PostExpansionVisitor { parse_sess, features }.visit_attribute(attr)
-}
-
-#[derive(Debug, Copy, Clone, PartialEq)]
-pub enum GateStrength {
- /// A hard error. (Most feature gates should use this.)
- Hard,
- /// Only a warning. (Use this only as backwards-compatibility demands.)
- Soft,
-}
-
-pub fn feature_err<'a>(
- sess: &'a ParseSess,
- feature: Symbol,
- span: impl Into<MultiSpan>,
- explain: &str,
-) -> DiagnosticBuilder<'a> {
- feature_err_issue(sess, feature, span, GateIssue::Language, explain)
-}
-
-pub fn feature_err_issue<'a>(
- sess: &'a ParseSess,
- feature: Symbol,
- span: impl Into<MultiSpan>,
- issue: GateIssue,
- explain: &str,
-) -> DiagnosticBuilder<'a> {
- leveled_feature_err(sess, feature, span, issue, explain, GateStrength::Hard)
-}
-
-fn leveled_feature_err<'a>(
- sess: &'a ParseSess,
- feature: Symbol,
- span: impl Into<MultiSpan>,
- issue: GateIssue,
- explain: &str,
- level: GateStrength,
-) -> DiagnosticBuilder<'a> {
- let diag = &sess.span_diagnostic;
-
- let mut err = match level {
- GateStrength::Hard => {
- diag.struct_span_err_with_code(span, explain, stringify_error_code!(E0658))
- }
- GateStrength::Soft => diag.struct_span_warn(span, explain),
- };
-
- if let Some(n) = find_feature_issue(feature, issue) {
- err.note(&format!(
- "for more information, see https://github.com/rust-lang/rust/issues/{}",
- n,
- ));
- }
-
- // #23973: do not suggest `#![feature(...)]` if we are in beta/stable
- if sess.unstable_features.is_nightly_build() {
- err.help(&format!("add `#![feature({})]` to the crate attributes to enable", feature));
- }
-
- // If we're on stable and only emitting a "soft" warning, add a note to
- // clarify that the feature isn't "on" (rather than being on but
- // warning-worthy).
- if !sess.unstable_features.is_nightly_build() && level == GateStrength::Soft {
- err.help("a nightly build of the compiler is required to enable this feature");
- }
-
- err
-}
-
-struct PostExpansionVisitor<'a> {
- parse_sess: &'a ParseSess,
- features: &'a Features,
-}
-
-macro_rules! gate_feature_post {
- ($cx: expr, $feature: ident, $span: expr, $explain: expr) => {{
- let (cx, span) = ($cx, $span);
- if !span.allows_unstable(sym::$feature) {
- gate_feature!(cx, $feature, span, $explain)
- }
- }};
- ($cx: expr, $feature: ident, $span: expr, $explain: expr, $level: expr) => {{
- let (cx, span) = ($cx, $span);
- if !span.allows_unstable(sym::$feature) {
- gate_feature!(cx, $feature, span, $explain, $level)
- }
- }};
-}
-
-impl<'a> PostExpansionVisitor<'a> {
- fn check_abi(&self, abi: ast::StrLit) {
- let ast::StrLit { symbol_unescaped, span, .. } = abi;
-
- match &*symbol_unescaped.as_str() {
- // Stable
- "Rust" | "C" | "cdecl" | "stdcall" | "fastcall" | "aapcs" | "win64" | "sysv64"
- | "system" => {}
- "rust-intrinsic" => {
- gate_feature_post!(&self, intrinsics, span, "intrinsics are subject to change");
- }
- "platform-intrinsic" => {
- gate_feature_post!(
- &self,
- platform_intrinsics,
- span,
- "platform intrinsics are experimental and possibly buggy"
- );
- }
- "vectorcall" => {
- gate_feature_post!(
- &self,
- abi_vectorcall,
- span,
- "vectorcall is experimental and subject to change"
- );
- }
- "thiscall" => {
- gate_feature_post!(
- &self,
- abi_thiscall,
- span,
- "thiscall is experimental and subject to change"
- );
- }
- "rust-call" => {
- gate_feature_post!(
- &self,
- unboxed_closures,
- span,
- "rust-call ABI is subject to change"
- );
- }
- "ptx-kernel" => {
- gate_feature_post!(
- &self,
- abi_ptx,
- span,
- "PTX ABIs are experimental and subject to change"
- );
- }
- "unadjusted" => {
- gate_feature_post!(
- &self,
- abi_unadjusted,
- span,
- "unadjusted ABI is an implementation detail and perma-unstable"
- );
- }
- "msp430-interrupt" => {
- gate_feature_post!(
- &self,
- abi_msp430_interrupt,
- span,
- "msp430-interrupt ABI is experimental and subject to change"
- );
- }
- "x86-interrupt" => {
- gate_feature_post!(
- &self,
- abi_x86_interrupt,
- span,
- "x86-interrupt ABI is experimental and subject to change"
- );
- }
- "amdgpu-kernel" => {
- gate_feature_post!(
- &self,
- abi_amdgpu_kernel,
- span,
- "amdgpu-kernel ABI is experimental and subject to change"
- );
- }
- "efiapi" => {
- gate_feature_post!(
- &self,
- abi_efiapi,
- span,
- "efiapi ABI is experimental and subject to change"
- );
- }
- abi => self
- .parse_sess
- .span_diagnostic
- .delay_span_bug(span, &format!("unrecognized ABI not caught in lowering: {}", abi)),
- }
- }
-
- fn check_extern(&self, ext: ast::Extern) {
- if let ast::Extern::Explicit(abi) = ext {
- self.check_abi(abi);
- }
- }
-
- fn maybe_report_invalid_custom_discriminants(&self, variants: &[ast::Variant]) {
- let has_fields = variants.iter().any(|variant| match variant.data {
- VariantData::Tuple(..) | VariantData::Struct(..) => true,
- VariantData::Unit(..) => false,
- });
-
- let discriminant_spans = variants
- .iter()
- .filter(|variant| match variant.data {
- VariantData::Tuple(..) | VariantData::Struct(..) => false,
- VariantData::Unit(..) => true,
- })
- .filter_map(|variant| variant.disr_expr.as_ref().map(|c| c.value.span))
- .collect::<Vec<_>>();
-
- if !discriminant_spans.is_empty() && has_fields {
- let mut err = feature_err(
- self.parse_sess,
- sym::arbitrary_enum_discriminant,
- discriminant_spans.clone(),
- "custom discriminant values are not allowed in enums with tuple or struct variants",
- );
- for sp in discriminant_spans {
- err.span_label(sp, "disallowed custom discriminant");
- }
- for variant in variants.iter() {
- match &variant.data {
- VariantData::Struct(..) => {
- err.span_label(variant.span, "struct variant defined here");
- }
- VariantData::Tuple(..) => {
- err.span_label(variant.span, "tuple variant defined here");
- }
- VariantData::Unit(..) => {}
- }
- }
- err.emit();
- }
- }
-
- fn check_gat(&self, generics: &ast::Generics, span: Span) {
- if !generics.params.is_empty() {
- gate_feature_post!(
- &self,
- generic_associated_types,
- span,
- "generic associated types are unstable"
- );
- }
- if !generics.where_clause.predicates.is_empty() {
- gate_feature_post!(
- &self,
- generic_associated_types,
- span,
- "where clauses on associated types are unstable"
- );
- }
- }
-
- /// Feature gate `impl Trait` inside `type Alias = $type_expr;`.
- fn check_impl_trait(&self, ty: &ast::Ty) {
- struct ImplTraitVisitor<'a> {
- vis: &'a PostExpansionVisitor<'a>,
- }
- impl Visitor<'_> for ImplTraitVisitor<'_> {
- fn visit_ty(&mut self, ty: &ast::Ty) {
- if let ast::TyKind::ImplTrait(..) = ty.kind {
- gate_feature_post!(
- &self.vis,
- type_alias_impl_trait,
- ty.span,
- "`impl Trait` in type aliases is unstable"
- );
- }
- visit::walk_ty(self, ty);
- }
- }
- ImplTraitVisitor { vis: self }.visit_ty(ty);
- }
-}
-
-impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
- fn visit_attribute(&mut self, attr: &ast::Attribute) {
- let attr_info =
- attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)).map(|a| **a);
- // Check feature gates for built-in attributes.
- if let Some((.., AttributeGate::Gated(_, name, descr, has_feature))) = attr_info {
- gate_feature_fn!(self, has_feature, attr.span, name, descr, GateStrength::Hard);
- }
- // Check unstable flavors of the `#[doc]` attribute.
- if attr.check_name(sym::doc) {
- for nested_meta in attr.meta_item_list().unwrap_or_default() {
- macro_rules! gate_doc { ($($name:ident => $feature:ident)*) => {
- $(if nested_meta.check_name(sym::$name) {
- let msg = concat!("`#[doc(", stringify!($name), ")]` is experimental");
- gate_feature!(self, $feature, attr.span, msg);
- })*
- }}
-
- gate_doc!(
- include => external_doc
- cfg => doc_cfg
- masked => doc_masked
- spotlight => doc_spotlight
- alias => doc_alias
- keyword => doc_keyword
- );
- }
- }
- }
-
- fn visit_name(&mut self, sp: Span, name: ast::Name) {
- if !name.as_str().is_ascii() {
- gate_feature_post!(
- &self,
- non_ascii_idents,
- self.parse_sess.source_map().def_span(sp),
- "non-ascii idents are not fully supported"
- );
- }
- }
-
- fn visit_item(&mut self, i: &'a ast::Item) {
- match i.kind {
- ast::ItemKind::ForeignMod(ref foreign_module) => {
- if let Some(abi) = foreign_module.abi {
- self.check_abi(abi);
- }
- }
-
- ast::ItemKind::Fn(..) => {
- if attr::contains_name(&i.attrs[..], sym::plugin_registrar) {
- gate_feature_post!(
- &self,
- plugin_registrar,
- i.span,
- "compiler plugins are experimental and possibly buggy"
- );
- }
- if attr::contains_name(&i.attrs[..], sym::start) {
- gate_feature_post!(
- &self,
- start,
- i.span,
- "`#[start]` functions are experimental \
- and their signature may change \
- over time"
- );
- }
- if attr::contains_name(&i.attrs[..], sym::main) {
- gate_feature_post!(
- &self,
- main,
- i.span,
- "declaration of a non-standard `#[main]` \
- function may change over time, for now \
- a top-level `fn main()` is required"
- );
- }
- }
-
- ast::ItemKind::Struct(..) => {
- for attr in attr::filter_by_name(&i.attrs[..], sym::repr) {
- for item in attr.meta_item_list().unwrap_or_else(Vec::new) {
- if item.check_name(sym::simd) {
- gate_feature_post!(
- &self,
- repr_simd,
- attr.span,
- "SIMD types are experimental and possibly buggy"
- );
- }
- }
- }
- }
-
- ast::ItemKind::Enum(ast::EnumDef { ref variants, .. }, ..) => {
- for variant in variants {
- match (&variant.data, &variant.disr_expr) {
- (ast::VariantData::Unit(..), _) => {}
- (_, Some(disr_expr)) => gate_feature_post!(
- &self,
- arbitrary_enum_discriminant,
- disr_expr.value.span,
- "discriminants on non-unit variants are experimental"
- ),
- _ => {}
- }
- }
-
- let has_feature = self.features.arbitrary_enum_discriminant;
- if !has_feature && !i.span.allows_unstable(sym::arbitrary_enum_discriminant) {
- self.maybe_report_invalid_custom_discriminants(&variants);
- }
- }
-
- ast::ItemKind::Impl(_, polarity, defaultness, ..) => {
- if polarity == ast::ImplPolarity::Negative {
- gate_feature_post!(
- &self,
- optin_builtin_traits,
- i.span,
- "negative trait bounds are not yet fully implemented; \
- use marker types for now"
- );
- }
-
- if let ast::Defaultness::Default = defaultness {
- gate_feature_post!(&self, specialization, i.span, "specialization is unstable");
- }
- }
-
- ast::ItemKind::Trait(ast::IsAuto::Yes, ..) => {
- gate_feature_post!(
- &self,
- optin_builtin_traits,
- i.span,
- "auto traits are experimental and possibly buggy"
- );
- }
-
- ast::ItemKind::TraitAlias(..) => {
- gate_feature_post!(&self, trait_alias, i.span, "trait aliases are experimental");
- }
-
- ast::ItemKind::MacroDef(ast::MacroDef { legacy: false, .. }) => {
- let msg = "`macro` is experimental";
- gate_feature_post!(&self, decl_macro, i.span, msg);
- }
-
- ast::ItemKind::TyAlias(ref ty, ..) => self.check_impl_trait(&ty),
-
- _ => {}
- }
-
- visit::walk_item(self, i);
- }
-
- fn visit_foreign_item(&mut self, i: &'a ast::ForeignItem) {
- match i.kind {
- ast::ForeignItemKind::Fn(..) | ast::ForeignItemKind::Static(..) => {
- let link_name = attr::first_attr_value_str_by_name(&i.attrs, sym::link_name);
- let links_to_llvm = match link_name {
- Some(val) => val.as_str().starts_with("llvm."),
- _ => false,
- };
- if links_to_llvm {
- gate_feature_post!(
- &self,
- link_llvm_intrinsics,
- i.span,
- "linking to LLVM intrinsics is experimental"
- );
- }
- }
- ast::ForeignItemKind::Ty => {
- gate_feature_post!(&self, extern_types, i.span, "extern types are experimental");
- }
- ast::ForeignItemKind::Macro(..) => {}
- }
-
- visit::walk_foreign_item(self, i)
- }
-
- fn visit_ty(&mut self, ty: &'a ast::Ty) {
- match ty.kind {
- ast::TyKind::BareFn(ref bare_fn_ty) => {
- self.check_extern(bare_fn_ty.ext);
- }
- ast::TyKind::Never => {
- gate_feature_post!(&self, never_type, ty.span, "The `!` type is experimental");
- }
- _ => {}
- }
- visit::walk_ty(self, ty)
- }
-
- fn visit_fn_ret_ty(&mut self, ret_ty: &'a ast::FunctionRetTy) {
- if let ast::FunctionRetTy::Ty(ref output_ty) = *ret_ty {
- if let ast::TyKind::Never = output_ty.kind {
- // Do nothing.
- } else {
- self.visit_ty(output_ty)
- }
- }
- }
-
- fn visit_expr(&mut self, e: &'a ast::Expr) {
- match e.kind {
- ast::ExprKind::Box(_) => {
- gate_feature_post!(
- &self,
- box_syntax,
- e.span,
- "box expression syntax is experimental; you can call `Box::new` instead"
- );
- }
- ast::ExprKind::Type(..) => {
- // To avoid noise about type ascription in common syntax errors, only emit if it
- // is the *only* error.
- if self.parse_sess.span_diagnostic.err_count() == 0 {
- gate_feature_post!(
- &self,
- type_ascription,
- e.span,
- "type ascription is experimental"
- );
- }
- }
- ast::ExprKind::TryBlock(_) => {
- gate_feature_post!(&self, try_blocks, e.span, "`try` expression is experimental");
- }
- ast::ExprKind::Block(_, opt_label) => {
- if let Some(label) = opt_label {
- gate_feature_post!(
- &self,
- label_break_value,
- label.ident.span,
- "labels on blocks are unstable"
- );
- }
- }
- _ => {}
- }
- visit::walk_expr(self, e)
- }
-
- fn visit_arm(&mut self, arm: &'a ast::Arm) {
- visit::walk_arm(self, arm)
- }
-
- fn visit_pat(&mut self, pattern: &'a ast::Pat) {
- match &pattern.kind {
- PatKind::Slice(pats) => {
- for pat in &*pats {
- let span = pat.span;
- let inner_pat = match &pat.kind {
- PatKind::Ident(.., Some(pat)) => pat,
- _ => pat,
- };
- if inner_pat.is_rest() {
- gate_feature_post!(
- &self,
- slice_patterns,
- span,
- "subslice patterns are unstable"
- );
- }
- }
- }
- PatKind::Box(..) => {
- gate_feature_post!(
- &self,
- box_patterns,
- pattern.span,
- "box pattern syntax is experimental"
- );
- }
- PatKind::Range(_, _, Spanned { node: RangeEnd::Excluded, .. }) => {
- gate_feature_post!(
- &self,
- exclusive_range_pattern,
- pattern.span,
- "exclusive range pattern syntax is experimental"
- );
- }
- _ => {}
- }
- visit::walk_pat(self, pattern)
- }
-
- fn visit_fn(
- &mut self,
- fn_kind: FnKind<'a>,
- fn_decl: &'a ast::FnDecl,
- span: Span,
- _node_id: NodeId,
- ) {
- if let Some(header) = fn_kind.header() {
- // Stability of const fn methods are covered in
- // `visit_trait_item` and `visit_impl_item` below; this is
- // because default methods don't pass through this point.
- self.check_extern(header.ext);
- }
-
- if fn_decl.c_variadic() {
- gate_feature_post!(&self, c_variadic, span, "C-variadic functions are unstable");
- }
-
- visit::walk_fn(self, fn_kind, fn_decl, span)
- }
-
- fn visit_generic_param(&mut self, param: &'a GenericParam) {
- match param.kind {
- GenericParamKind::Const { .. } => gate_feature_post!(
- &self,
- const_generics,
- param.ident.span,
- "const generics are unstable"
- ),
- _ => {}
- }
- visit::walk_generic_param(self, param)
- }
-
- fn visit_assoc_ty_constraint(&mut self, constraint: &'a AssocTyConstraint) {
- match constraint.kind {
- AssocTyConstraintKind::Bound { .. } => gate_feature_post!(
- &self,
- associated_type_bounds,
- constraint.span,
- "associated type bounds are unstable"
- ),
- _ => {}
- }
- visit::walk_assoc_ty_constraint(self, constraint)
- }
-
- fn visit_trait_item(&mut self, ti: &'a ast::AssocItem) {
- match ti.kind {
- ast::AssocItemKind::Fn(ref sig, ref block) => {
- if block.is_none() {
- self.check_extern(sig.header.ext);
- }
- if sig.header.constness.node == ast::Constness::Const {
- gate_feature_post!(&self, const_fn, ti.span, "const fn is unstable");
- }
- }
- ast::AssocItemKind::TyAlias(_, ref default) => {
- if let Some(_) = default {
- gate_feature_post!(
- &self,
- associated_type_defaults,
- ti.span,
- "associated type defaults are unstable"
- );
- }
- }
- _ => {}
- }
- visit::walk_trait_item(self, ti)
- }
-
- fn visit_assoc_item(&mut self, ii: &'a ast::AssocItem) {
- if ii.defaultness == ast::Defaultness::Default {
- gate_feature_post!(&self, specialization, ii.span, "specialization is unstable");
- }
-
- match ii.kind {
- ast::AssocItemKind::Fn(ref sig, _) => {
- if sig.decl.c_variadic() {
- gate_feature_post!(
- &self,
- c_variadic,
- ii.span,
- "C-variadic functions are unstable"
- );
- }
- }
- ast::AssocItemKind::TyAlias(_, ref ty) => {
- if let Some(ty) = ty {
- self.check_impl_trait(ty);
- }
- self.check_gat(&ii.generics, ii.span);
- }
- _ => {}
- }
- visit::walk_assoc_item(self, ii)
- }
-
- fn visit_vis(&mut self, vis: &'a ast::Visibility) {
- if let ast::VisibilityKind::Crate(ast::CrateSugar::JustCrate) = vis.node {
- gate_feature_post!(
- &self,
- crate_visibility_modifier,
- vis.span,
- "`crate` visibility modifier is experimental"
- );
- }
- visit::walk_vis(self, vis)
- }
-}
-
-pub fn get_features(
- span_handler: &Handler,
- krate_attrs: &[ast::Attribute],
- crate_edition: Edition,
- allow_features: &Option<Vec<String>>,
-) -> 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();
- }
-
- let mut features = Features::default();
- let mut edition_enabled_features = FxHashMap::default();
-
- for &edition in ALL_EDITIONS {
- if edition <= crate_edition {
- // The `crate_edition` implies its respective umbrella feature-gate
- // (i.e., `#![feature(rust_20XX_preview)]` isn't needed on edition 20XX).
- edition_enabled_features.insert(edition.feature_name(), edition);
- }
- }
-
- for feature in active_features_up_to(crate_edition) {
- feature.set(&mut features, DUMMY_SP);
- edition_enabled_features.insert(feature.name, crate_edition);
- }
-
- // Process the edition umbrella feature-gates first, to ensure
- // `edition_enabled_features` is completed before it's queried.
- for attr in krate_attrs {
- if !attr.check_name(sym::feature) {
- continue;
- }
-
- let list = match attr.meta_item_list() {
- Some(list) => list,
- None => continue,
- };
-
- for mi in list {
- if !mi.is_word() {
- continue;
- }
-
- let name = mi.name_or_empty();
-
- let edition = ALL_EDITIONS.iter().find(|e| name == e.feature_name()).copied();
- if let Some(edition) = edition {
- if edition <= crate_edition {
- continue;
- }
-
- for feature in active_features_up_to(edition) {
- // FIXME(Manishearth) there is currently no way to set
- // lib features by edition
- feature.set(&mut features, DUMMY_SP);
- edition_enabled_features.insert(feature.name, edition);
- }
- }
- }
- }
-
- for attr in krate_attrs {
- if !attr.check_name(sym::feature) {
- continue;
- }
-
- let list = match attr.meta_item_list() {
- Some(list) => list,
- None => 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",
- format!("{}", ident.name),
- Applicability::MaybeIncorrect,
- )
- .emit();
- continue;
- }
- None => {
- bad_input(mi.span()).span_label(mi.span(), "expected just one word").emit();
- continue;
- }
- };
-
- if let Some(edition) = edition_enabled_features.get(&name) {
- struct_span_warn!(
- span_handler,
- mi.span(),
- E0705,
- "the feature `{}` is included in the Rust {} edition",
- name,
- edition,
- )
- .emit();
- continue;
- }
-
- if ALL_EDITIONS.iter().any(|e| name == e.feature_name()) {
- // Handled in the separate loop above.
- continue;
- }
-
- let removed = REMOVED_FEATURES.iter().find(|f| name == f.name);
- let stable_removed = STABLE_REMOVED_FEATURES.iter().find(|f| name == f.name);
- if let Some(Feature { state, .. }) = removed.or(stable_removed) {
- if let FeatureState::Removed { reason } | FeatureState::Stabilized { reason } =
- state
- {
- feature_removed(span_handler, mi.span(), *reason);
- continue;
- }
- }
-
- if let Some(Feature { since, .. }) = ACCEPTED_FEATURES.iter().find(|f| name == f.name) {
- let since = Some(Symbol::intern(since));
- features.declared_lang_features.push((name, mi.span(), since));
- continue;
- }
-
- if let Some(allowed) = allow_features.as_ref() {
- if allowed.iter().find(|&f| name.as_str() == *f).is_none() {
- span_err!(
- span_handler,
- mi.span(),
- E0725,
- "the feature `{}` is not in the list of allowed features",
- name
- );
- continue;
- }
- }
-
- if let Some(f) = ACTIVE_FEATURES.iter().find(|f| name == f.name) {
- f.set(&mut features, mi.span());
- features.declared_lang_features.push((name, mi.span(), None));
- continue;
- }
-
- features.declared_lib_features.push((name, mi.span()));
- }
- }
-
- features
-}
-
-fn active_features_up_to(edition: Edition) -> impl Iterator<Item = &'static Feature> {
- ACTIVE_FEATURES.iter().filter(move |feature| {
- if let Some(feature_edition) = feature.edition { feature_edition <= edition } else { false }
- })
-}
-
-pub fn check_crate(
- krate: &ast::Crate,
- parse_sess: &ParseSess,
- features: &Features,
- unstable: UnstableFeatures,
-) {
- maybe_stage_features(&parse_sess.span_diagnostic, krate, unstable);
- let mut visitor = PostExpansionVisitor { parse_sess, features };
-
- let spans = parse_sess.gated_spans.spans.borrow();
- macro_rules! gate_all {
- ($gate:ident, $msg:literal) => {
- for span in spans.get(&sym::$gate).unwrap_or(&vec![]) {
- gate_feature!(&visitor, $gate, *span, $msg);
- }
- };
- }
- gate_all!(let_chains, "`let` expressions in this position are experimental");
- gate_all!(async_closure, "async closures are unstable");
- gate_all!(generators, "yield syntax is experimental");
- gate_all!(or_patterns, "or-patterns syntax is experimental");
- gate_all!(const_extern_fn, "`const extern fn` definitions are unstable");
- gate_all!(raw_ref_op, "raw address of syntax is experimental");
-
- // All uses of `gate_all!` below this point were added in #65742,
- // and subsequently disabled (with the non-early gating readded).
- macro_rules! gate_all {
- ($gate:ident, $msg:literal) => {
- // FIXME(eddyb) do something more useful than always
- // disabling these uses of early feature-gatings.
- if false {
- for span in spans.get(&sym::$gate).unwrap_or(&vec![]) {
- gate_feature!(&visitor, $gate, *span, $msg);
- }
- }
- };
- }
-
- gate_all!(trait_alias, "trait aliases are experimental");
- gate_all!(associated_type_bounds, "associated type bounds are unstable");
- gate_all!(crate_visibility_modifier, "`crate` visibility modifier is experimental");
- gate_all!(const_generics, "const generics are unstable");
- gate_all!(decl_macro, "`macro` is experimental");
- gate_all!(box_patterns, "box pattern syntax is experimental");
- gate_all!(exclusive_range_pattern, "exclusive range pattern syntax is experimental");
- gate_all!(try_blocks, "`try` blocks are unstable");
- gate_all!(label_break_value, "labels on blocks are unstable");
- gate_all!(box_syntax, "box expression syntax is experimental; you can call `Box::new` instead");
- // To avoid noise about type ascription in common syntax errors,
- // only emit if it is the *only* error. (Also check it last.)
- if parse_sess.span_diagnostic.err_count() == 0 {
- gate_all!(type_ascription, "type ascription is experimental");
- }
-
- visit::walk_crate(&mut visitor, krate);
-}
-
-fn maybe_stage_features(span_handler: &Handler, krate: &ast::Crate, unstable: UnstableFeatures) {
- if !unstable.is_nightly_build() {
- for attr in krate.attrs.iter().filter(|attr| attr.check_name(sym::feature)) {
- span_err!(
- span_handler,
- attr.span,
- E0554,
- "`#![feature]` may not be used on the {} release channel",
- option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)")
- );
- }
- }
-}
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/", test(attr(deny(warnings))))]
#![feature(bool_to_option)]
#![feature(box_syntax)]
-#![feature(const_fn)]
+#![feature(const_fn)] // For the `transmute` in `P::new`
#![feature(const_transmute)]
#![feature(crate_visibility_modifier)]
#![feature(label_break_value)]
#![recursion_limit = "256"]
use ast::AttrId;
-pub use errors;
use rustc_data_structures::sync::Lock;
use rustc_index::bit_set::GrowableBitSet;
use rustc_span::edition::{Edition, DEFAULT_EDITION};
scoped_tls::scoped_thread_local!(pub static GLOBALS: Globals);
-#[macro_use]
-pub mod diagnostics {
- #[macro_use]
- pub mod macros;
-}
-
pub mod util {
pub mod classify;
pub mod comments;
pub mod attr;
pub mod entry;
pub mod expand;
-pub mod feature_gate {
- mod check;
- pub use check::{check_attribute, check_crate, feature_err, feature_err_issue, get_features};
-}
pub mod mut_visit;
pub mod ptr;
-pub mod show_span;
pub use rustc_session::parse as sess;
pub mod token;
pub mod tokenstream;
pub mod pprust;
}
-pub mod early_buffered_lints;
-
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
/// Requirements for a `StableHashingContext` to be used in this crate.
}
}
-pub fn noop_visit_trait_ref<T: MutVisitor>(TraitRef { path, ref_id }: &mut TraitRef, vis: &mut T) {
+pub fn noop_visit_trait_ref<T: MutVisitor>(tr: &mut TraitRef, vis: &mut T) {
+ let TraitRef { path, ref_id, constness: _ } = tr;
vis.visit_path(path);
vis.visit_id(ref_id);
}
PatKind::Box(inner) => vis.visit_pat(inner),
PatKind::Ref(inner, _mutbl) => vis.visit_pat(inner),
PatKind::Range(e1, e2, Spanned { span: _, node: _ }) => {
- vis.visit_expr(e1);
- vis.visit_expr(e2);
+ visit_opt(e1, |e| vis.visit_expr(e));
+ visit_opt(e2, |e| vis.visit_expr(e));
vis.visit_span(span);
}
PatKind::Tuple(elems) | PatKind::Slice(elems) | PatKind::Or(elems) => {
}
PatKind::Lit(ref e) => self.print_expr(&**e),
PatKind::Range(ref begin, ref end, Spanned { node: ref end_kind, .. }) => {
- self.print_expr(begin);
- self.s.space();
+ if let Some(e) = begin {
+ self.print_expr(e);
+ self.s.space();
+ }
match *end_kind {
RangeEnd::Included(RangeSyntax::DotDotDot) => self.s.word("..."),
RangeEnd::Included(RangeSyntax::DotDotEq) => self.s.word("..="),
RangeEnd::Excluded => self.s.word(".."),
}
- self.print_expr(end);
+ if let Some(e) = end {
+ self.print_expr(e);
+ }
}
PatKind::Slice(ref elts) => {
self.s.word("[");
+++ /dev/null
-//! Span debugger
-//!
-//! This module shows spans for all expressions in the crate
-//! to help with compiler debugging.
-
-use std::str::FromStr;
-
-use crate::ast;
-use crate::visit;
-use crate::visit::Visitor;
-
-enum Mode {
- Expression,
- Pattern,
- Type,
-}
-
-impl FromStr for Mode {
- type Err = ();
- fn from_str(s: &str) -> Result<Mode, ()> {
- let mode = match s {
- "expr" => Mode::Expression,
- "pat" => Mode::Pattern,
- "ty" => Mode::Type,
- _ => return Err(()),
- };
- Ok(mode)
- }
-}
-
-struct ShowSpanVisitor<'a> {
- span_diagnostic: &'a errors::Handler,
- mode: Mode,
-}
-
-impl<'a> Visitor<'a> for ShowSpanVisitor<'a> {
- fn visit_expr(&mut self, e: &'a ast::Expr) {
- if let Mode::Expression = self.mode {
- self.span_diagnostic.span_warn(e.span, "expression");
- }
- visit::walk_expr(self, e);
- }
-
- fn visit_pat(&mut self, p: &'a ast::Pat) {
- if let Mode::Pattern = self.mode {
- self.span_diagnostic.span_warn(p.span, "pattern");
- }
- visit::walk_pat(self, p);
- }
-
- fn visit_ty(&mut self, t: &'a ast::Ty) {
- if let Mode::Type = self.mode {
- self.span_diagnostic.span_warn(t.span, "type");
- }
- visit::walk_ty(self, t);
- }
-
- fn visit_mac(&mut self, mac: &'a ast::Mac) {
- visit::walk_mac(self, mac);
- }
-}
-
-pub fn run(span_diagnostic: &errors::Handler, mode: &str, krate: &ast::Crate) {
- let mode = match mode.parse().ok() {
- Some(mode) => mode,
- None => return,
- };
- let mut v = ShowSpanVisitor { span_diagnostic, mode };
- visit::walk_crate(&mut v, krate);
-}
T: Iterator<Item = &'a Symbol>,
{
let max_dist = dist.map_or_else(|| cmp::max(lookup.len(), 3) / 3, |d| d);
+ let name_vec: Vec<&Symbol> = iter_names.collect();
- let (case_insensitive_match, levenstein_match) = iter_names
+ let (case_insensitive_match, levenshtein_match) = name_vec
+ .iter()
.filter_map(|&name| {
let dist = lev_distance(lookup, &name.as_str());
if dist <= max_dist { Some((name, dist)) } else { None }
})
// Here we are collecting the next structure:
- // (case_insensitive_match, (levenstein_match, levenstein_distance))
+ // (case_insensitive_match, (levenshtein_match, levenshtein_distance))
.fold((None, None), |result, (candidate, dist)| {
(
if candidate.as_str().to_uppercase() == lookup.to_uppercase() {
},
)
});
-
+ // Priority of matches:
+ // 1. Exact case insensitive match
+ // 2. Levenshtein distance match
+ // 3. Sorted word match
if let Some(candidate) = case_insensitive_match {
- Some(candidate) // exact case insensitive match has a higher priority
+ Some(*candidate)
+ } else if levenshtein_match.is_some() {
+ levenshtein_match.map(|(candidate, _)| *candidate)
} else {
- levenstein_match.map(|(candidate, _)| candidate)
+ find_match_by_sorted_words(name_vec, lookup)
}
}
+
+fn find_match_by_sorted_words<'a>(iter_names: Vec<&'a Symbol>, lookup: &str) -> Option<Symbol> {
+ iter_names.iter().fold(None, |result, candidate| {
+ if sort_by_words(&candidate.as_str()) == sort_by_words(lookup) {
+ Some(**candidate)
+ } else {
+ result
+ }
+ })
+}
+
+fn sort_by_words(name: &str) -> String {
+ let mut split_words: Vec<&str> = name.split('_').collect();
+ split_words.sort();
+ split_words.join("_")
+}
find_best_match_for_name(input.iter(), "aaaa", Some(4)),
Some(Symbol::intern("AAAA"))
);
+
+ let input = vec![Symbol::intern("a_longer_variable_name")];
+ assert_eq!(
+ find_best_match_for_name(input.iter(), "a_variable_longer_name", None),
+ Some(Symbol::intern("a_longer_variable_name"))
+ );
})
}
}
PatKind::Lit(ref expression) => visitor.visit_expr(expression),
PatKind::Range(ref lower_bound, ref upper_bound, _) => {
- visitor.visit_expr(lower_bound);
- visitor.visit_expr(upper_bound);
+ walk_list!(visitor, visit_expr, lower_bound);
+ walk_list!(visitor, visit_expr, upper_bound);
}
PatKind::Wild | PatKind::Rest => {}
PatKind::Tuple(ref elems) | PatKind::Slice(ref elems) | PatKind::Or(ref elems) => {
`RUST_TEST_TIME_DOCTEST` environment variables.
Expected format of environment variable is `VARIABLE=WARN_TIME,CRITICAL_TIME`.
+ Durations must be specified in milliseconds, e.g. `500,2000` means that the warn time
+ is 0.5 seconds, and the critical time is 2 seconds.
Not available for --format=terse",
"plain|colored",
path = "rustc.rs"
[dependencies]
-rustc_target = { path = "../librustc_target" }
rustc_driver = { path = "../librustc_driver" }
# Make sure rustc_codegen_ssa ends up in the sysroot, because this
extern "C" LLVMRustArchiveIteratorRef
LLVMRustArchiveIteratorNew(LLVMRustArchiveRef RustArchive) {
Archive *Archive = RustArchive->getBinary();
+#if LLVM_VERSION_GE(10, 0)
+ std::unique_ptr<Error> Err = std::make_unique<Error>(Error::success());
+#else
std::unique_ptr<Error> Err = llvm::make_unique<Error>(Error::success());
+#endif
auto Cur = Archive->child_begin(*Err);
if (*Err) {
LLVMRustSetLastError(toString(std::move(*Err)).c_str());
LLVMRustLinkerNew(LLVMModuleRef DstRef) {
Module *Dst = unwrap(DstRef);
- auto Ret = llvm::make_unique<RustLinker>(*Dst);
- return Ret.release();
+ return new RustLinker(*Dst);
}
extern "C" void
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/CodeGen/TargetSubtargetInfo.h"
+#include "llvm/InitializePasses.h"
#include "llvm/IR/AutoUpgrade.h"
#include "llvm/IR/AssemblyAnnotationWriter.h"
#include "llvm/IR/IntrinsicInst.h"
ObjectFile,
};
+#if LLVM_VERSION_GE(10, 0)
+static CodeGenFileType fromRust(LLVMRustFileType Type) {
+ switch (Type) {
+ case LLVMRustFileType::AssemblyFile:
+ return CGFT_AssemblyFile;
+ case LLVMRustFileType::ObjectFile:
+ return CGFT_ObjectFile;
+ default:
+ report_fatal_error("Bad FileType.");
+ }
+}
+#else
static TargetMachine::CodeGenFileType fromRust(LLVMRustFileType Type) {
switch (Type) {
case LLVMRustFileType::AssemblyFile:
report_fatal_error("Bad FileType.");
}
}
+#endif
extern "C" LLVMRustResult
LLVMRustWriteOutputFile(LLVMTargetMachineRef Target, LLVMPassManagerRef PMR,
int num_modules,
const char **preserved_symbols,
int num_symbols) {
+#if LLVM_VERSION_GE(10, 0)
+ auto Ret = std::make_unique<LLVMRustThinLTOData>();
+#else
auto Ret = llvm::make_unique<LLVMRustThinLTOData>();
+#endif
// Load each module's summary and merge it into one combined index
for (int i = 0; i < num_modules; i++) {
ExportedGUIDs.insert(GUID);
}
}
+#if LLVM_VERSION_GE(10, 0)
+ auto isExported = [&](StringRef ModuleIdentifier, ValueInfo VI) {
+ const auto &ExportList = Ret->ExportLists.find(ModuleIdentifier);
+ return (ExportList != Ret->ExportLists.end() &&
+ ExportList->second.count(VI)) ||
+ ExportedGUIDs.count(VI.getGUID());
+ };
+ thinLTOInternalizeAndPromoteInIndex(Ret->Index, isExported, isPrevailing);
+#else
auto isExported = [&](StringRef ModuleIdentifier, GlobalValue::GUID GUID) {
const auto &ExportList = Ret->ExportLists.find(ModuleIdentifier);
return (ExportList != Ret->ExportLists.end() &&
ExportedGUIDs.count(GUID);
};
thinLTOInternalizeAndPromoteInIndex(Ret->Index, isExported);
+#endif
return Ret.release();
}
extern "C" LLVMRustThinLTOBuffer*
LLVMRustThinLTOBufferCreate(LLVMModuleRef M) {
+#if LLVM_VERSION_GE(10, 0)
+ auto Ret = std::make_unique<LLVMRustThinLTOBuffer>();
+#else
auto Ret = llvm::make_unique<LLVMRustThinLTOBuffer>();
+#endif
{
raw_string_ostream OS(Ret->data);
{
if (isSet(Flags & LLVMRustDIFlags::FlagAppleBlock)) {
Result |= DINode::DIFlags::FlagAppleBlock;
}
+#if LLVM_VERSION_LT(10, 0)
if (isSet(Flags & LLVMRustDIFlags::FlagBlockByrefStruct)) {
Result |= DINode::DIFlags::FlagBlockByrefStruct;
}
+#endif
if (isSet(Flags & LLVMRustDIFlags::FlagVirtual)) {
Result |= DINode::DIFlags::FlagVirtual;
}
llvm::DIGlobalVariableExpression *VarExpr = Builder->createGlobalVariableExpression(
unwrapDI<DIDescriptor>(Context), Name, LinkageName,
unwrapDI<DIFile>(File), LineNo, unwrapDI<DIType>(Ty), IsLocalToUnit,
+#if LLVM_VERSION_GE(10, 0)
+ /* isDefined */ true,
+#endif
InitExpr, unwrapDIPtr<MDNode>(Decl),
#if LLVM_VERSION_GE(8, 0)
/* templateParams */ nullptr,
extern "C" size_t LLVMRustGetSectionName(LLVMSectionIteratorRef SI,
const char **Ptr) {
+#if LLVM_VERSION_GE(10, 0)
+ auto NameOrErr = (*unwrap(SI))->getName();
+ if (!NameOrErr)
+ report_fatal_error(NameOrErr.takeError());
+ *Ptr = NameOrErr->data();
+ return NameOrErr->size();
+#else
StringRef Ret;
if (std::error_code EC = (*unwrap(SI))->getName(Ret))
report_fatal_error(EC.message());
*Ptr = Ret.data();
return Ret.size();
+#endif
}
// LLVMArrayType function does not support 64-bit ElementCount
LLVMValueRef Dst, unsigned DstAlign,
LLVMValueRef Src, unsigned SrcAlign,
LLVMValueRef Size, bool IsVolatile) {
+#if LLVM_VERSION_GE(10, 0)
+ return wrap(unwrap(B)->CreateMemCpy(
+ unwrap(Dst), MaybeAlign(DstAlign),
+ unwrap(Src), MaybeAlign(SrcAlign),
+ unwrap(Size), IsVolatile));
+#else
return wrap(unwrap(B)->CreateMemCpy(
unwrap(Dst), DstAlign,
unwrap(Src), SrcAlign,
unwrap(Size), IsVolatile));
+#endif
}
extern "C" LLVMValueRef LLVMRustBuildMemMove(LLVMBuilderRef B,
LLVMValueRef Dst, unsigned DstAlign,
LLVMValueRef Src, unsigned SrcAlign,
LLVMValueRef Size, bool IsVolatile) {
+#if LLVM_VERSION_GE(10, 0)
+ return wrap(unwrap(B)->CreateMemMove(
+ unwrap(Dst), MaybeAlign(DstAlign),
+ unwrap(Src), MaybeAlign(SrcAlign),
+ unwrap(Size), IsVolatile));
+#else
return wrap(unwrap(B)->CreateMemMove(
unwrap(Dst), DstAlign,
unwrap(Src), SrcAlign,
unwrap(Size), IsVolatile));
+#endif
}
extern "C" LLVMValueRef
extern "C" LLVMRustModuleBuffer*
LLVMRustModuleBufferCreate(LLVMModuleRef M) {
+#if LLVM_VERSION_GE(10, 0)
+ auto Ret = std::make_unique<LLVMRustModuleBuffer>();
+#else
auto Ret = llvm::make_unique<LLVMRustModuleBuffer>();
+#endif
{
raw_string_ostream OS(Ret->data);
{
fn main() {
}
-// CHECK: define i32 @main(i32, i8**)
+// CHECK: define i32 @main(i32{{( %0)?}}, i8**{{( %1)?}})
// CHECK-LABEL: @cmp_bool
#[no_mangle]
pub fn cmp_bool(a: bool, b: bool) -> Ordering {
+// LLVM 10 produces (zext a) + (sext b), but the final lowering is (zext a) - (zext b).
// CHECK: zext i1
-// CHECK: zext i1
-// CHECK: sub nsw
+// CHECK: {{z|s}}ext i1
+// CHECK: {{sub|add}} nsw
a.cmp(&b)
}
// This checks the constants from {low,high}_align_const, they share the same
// constant, but the alignment differs, so the higher one should be used
-// CHECK: [[LOW_HIGH:@[0-9]+]] = {{.*}}, align 4
+// CHECK: [[LOW_HIGH:@[0-9]+]] = {{.*}} getelementptr inbounds (<{ [8 x i8] }>, <{ [8 x i8] }>* @2, i32 0, i32 0, i32 0), {{.*}},
#[derive(Copy, Clone)]
-
// repr(i16) is required for the {low,high}_align_const test
#[repr(i16)]
pub enum E<A, B> {
// CHECK-LABEL: @static_enum_const
#[no_mangle]
pub fn static_enum_const() -> E<i16, i32> {
- STATIC
+ STATIC
}
// CHECK-LABEL: @inline_enum_const
// CHECK-LABEL: @low_align_const
#[no_mangle]
pub fn low_align_const() -> E<i16, [i16; 3]> {
-// Check that low_align_const and high_align_const use the same constant
-// CHECK: i8* align 2 getelementptr inbounds (<{ [8 x i8] }>, <{ [8 x i8] }>* [[LOW_HIGH]], i32 0, i32 0, i32 0),
+ // Check that low_align_const and high_align_const use the same constant
+ // CHECK: load %"E<i16, [i16; 3]>"*, %"E<i16, [i16; 3]>"** bitcast (<{ i8*, [0 x i8] }>* [[LOW_HIGH]] to %"E<i16, [i16; 3]>"**),
*&E::A(0)
}
// CHECK-LABEL: @high_align_const
#[no_mangle]
pub fn high_align_const() -> E<i16, i32> {
-// Check that low_align_const and high_align_const use the same constant
-// CHECK: i8* align 4 getelementptr inbounds (<{ [8 x i8] }>, <{ [8 x i8] }>* [[LOW_HIGH]], i32 0, i32 0, i32 0),
+ // Check that low_align_const and high_align_const use the same constant
+ // CHECK: load %"E<i16, i32>"*, %"E<i16, i32>"** bitcast (<{ i8*, [0 x i8] }>* [[LOW_HIGH]] to %"E<i16, i32>"**),
*&E::A(0)
}
x
}
-// CHECK: @struct_return(%S* noalias nocapture sret dereferenceable(32))
+// CHECK: @struct_return(%S* noalias nocapture sret dereferenceable(32){{( %0)?}})
#[no_mangle]
pub fn struct_return() -> S {
S {
pub fn trait_borrow(_: &Drop) {
}
-// CHECK: @trait_box({}* noalias nonnull align 1, [3 x [[USIZE]]]* noalias readonly align {{.*}} dereferenceable({{.*}}))
+// CHECK: @trait_box({}* noalias nonnull align 1{{( %0)?}}, [3 x [[USIZE]]]* noalias readonly align {{.*}} dereferenceable({{.*}}){{( %1)?}})
#[no_mangle]
pub fn trait_box(_: Box<Drop>) {
}
#[no_mangle]
pub fn check_prefetch_read_data(data: &[i8]) {
unsafe {
- // CHECK: call void @llvm.prefetch(i8* %{{.*}}, i32 0, i32 0, i32 1)
+ // CHECK: call void @llvm.prefetch{{.*}}(i8* %{{.*}}, i32 0, i32 0, i32 1)
prefetch_read_data(data.as_ptr(), 0);
- // CHECK: call void @llvm.prefetch(i8* %{{.*}}, i32 0, i32 1, i32 1)
+ // CHECK: call void @llvm.prefetch{{.*}}(i8* %{{.*}}, i32 0, i32 1, i32 1)
prefetch_read_data(data.as_ptr(), 1);
- // CHECK: call void @llvm.prefetch(i8* %{{.*}}, i32 0, i32 2, i32 1)
+ // CHECK: call void @llvm.prefetch{{.*}}(i8* %{{.*}}, i32 0, i32 2, i32 1)
prefetch_read_data(data.as_ptr(), 2);
- // CHECK: call void @llvm.prefetch(i8* %{{.*}}, i32 0, i32 3, i32 1)
+ // CHECK: call void @llvm.prefetch{{.*}}(i8* %{{.*}}, i32 0, i32 3, i32 1)
prefetch_read_data(data.as_ptr(), 3);
}
}
#[no_mangle]
pub fn check_prefetch_write_data(data: &[i8]) {
unsafe {
- // CHECK: call void @llvm.prefetch(i8* %{{.*}}, i32 1, i32 0, i32 1)
+ // CHECK: call void @llvm.prefetch{{.*}}(i8* %{{.*}}, i32 1, i32 0, i32 1)
prefetch_write_data(data.as_ptr(), 0);
- // CHECK: call void @llvm.prefetch(i8* %{{.*}}, i32 1, i32 1, i32 1)
+ // CHECK: call void @llvm.prefetch{{.*}}(i8* %{{.*}}, i32 1, i32 1, i32 1)
prefetch_write_data(data.as_ptr(), 1);
- // CHECK: call void @llvm.prefetch(i8* %{{.*}}, i32 1, i32 2, i32 1)
+ // CHECK: call void @llvm.prefetch{{.*}}(i8* %{{.*}}, i32 1, i32 2, i32 1)
prefetch_write_data(data.as_ptr(), 2);
- // CHECK: call void @llvm.prefetch(i8* %{{.*}}, i32 1, i32 3, i32 1)
+ // CHECK: call void @llvm.prefetch{{.*}}(i8* %{{.*}}, i32 1, i32 3, i32 1)
prefetch_write_data(data.as_ptr(), 3);
}
}
#[no_mangle]
pub fn check_prefetch_read_instruction(data: &[i8]) {
unsafe {
- // CHECK: call void @llvm.prefetch(i8* %{{.*}}, i32 0, i32 0, i32 0)
+ // CHECK: call void @llvm.prefetch{{.*}}(i8* %{{.*}}, i32 0, i32 0, i32 0)
prefetch_read_instruction(data.as_ptr(), 0);
- // CHECK: call void @llvm.prefetch(i8* %{{.*}}, i32 0, i32 1, i32 0)
+ // CHECK: call void @llvm.prefetch{{.*}}(i8* %{{.*}}, i32 0, i32 1, i32 0)
prefetch_read_instruction(data.as_ptr(), 1);
- // CHECK: call void @llvm.prefetch(i8* %{{.*}}, i32 0, i32 2, i32 0)
+ // CHECK: call void @llvm.prefetch{{.*}}(i8* %{{.*}}, i32 0, i32 2, i32 0)
prefetch_read_instruction(data.as_ptr(), 2);
- // CHECK: call void @llvm.prefetch(i8* %{{.*}}, i32 0, i32 3, i32 0)
+ // CHECK: call void @llvm.prefetch{{.*}}(i8* %{{.*}}, i32 0, i32 3, i32 0)
prefetch_read_instruction(data.as_ptr(), 3);
}
}
#[no_mangle]
pub fn check_prefetch_write_instruction(data: &[i8]) {
unsafe {
- // CHECK: call void @llvm.prefetch(i8* %{{.*}}, i32 1, i32 0, i32 0)
+ // CHECK: call void @llvm.prefetch{{.*}}(i8* %{{.*}}, i32 1, i32 0, i32 0)
prefetch_write_instruction(data.as_ptr(), 0);
- // CHECK: call void @llvm.prefetch(i8* %{{.*}}, i32 1, i32 1, i32 0)
+ // CHECK: call void @llvm.prefetch{{.*}}(i8* %{{.*}}, i32 1, i32 1, i32 0)
prefetch_write_instruction(data.as_ptr(), 1);
- // CHECK: call void @llvm.prefetch(i8* %{{.*}}, i32 1, i32 2, i32 0)
+ // CHECK: call void @llvm.prefetch{{.*}}(i8* %{{.*}}, i32 1, i32 2, i32 0)
prefetch_write_instruction(data.as_ptr(), 2);
- // CHECK: call void @llvm.prefetch(i8* %{{.*}}, i32 1, i32 3, i32 0)
+ // CHECK: call void @llvm.prefetch{{.*}}(i8* %{{.*}}, i32 1, i32 3, i32 0)
prefetch_write_instruction(data.as_ptr(), 3);
}
}
// CHECK: Function Attrs: naked
#[no_mangle]
#[naked]
-// CHECK-NEXT: define void @naked_with_args(i{{[0-9]+}})
+// CHECK-NEXT: define void @naked_with_args(i{{[0-9]+( %0)?}})
pub fn naked_with_args(a: isize) {
// CHECK-NEXT: {{.+}}:
// CHECK-NEXT: %a = alloca i{{[0-9]+}}
}
// CHECK: Function Attrs: naked
-// CHECK-NEXT: define i{{[0-9]+}} @naked_with_args_and_return(i{{[0-9]+}})
+// CHECK-NEXT: define i{{[0-9]+}} @naked_with_args_and_return(i{{[0-9]+( %0)?}})
#[no_mangle]
#[naked]
pub fn naked_with_args_and_return(a: isize) -> isize {
// CHECK-LABEL: @repeat_take_collect
#[no_mangle]
pub fn repeat_take_collect() -> Vec<u8> {
-// CHECK: call void @llvm.memset.p0i8.[[USIZE]](i8* {{(nonnull )?}}align 1 %{{[0-9]+}}, i8 42, [[USIZE]] 100000, i1 false)
+// CHECK: call void @llvm.memset.p0i8.[[USIZE]](i8* {{(nonnull )?}}align 1{{.*}} %{{[0-9]+}}, i8 42, [[USIZE]] 100000, i1 false)
iter::repeat(42).take(100000).collect()
}
#[repr(transparent)]
pub struct Rgb8Wrap(Rgb8);
-// CHECK: i24 @test_Rgb8Wrap(i24)
+// CHECK: i24 @test_Rgb8Wrap(i24{{( %0)?}})
#[no_mangle]
pub extern "sysv64" fn test_Rgb8Wrap(_: Rgb8Wrap) -> Rgb8Wrap { loop {} }
#[repr(transparent)]
pub struct SmallUnion(FloatBits);
-// CHECK: i32 @test_SmallUnion(i32)
+// CHECK: i32 @test_SmallUnion(i32{{( %0)?}})
#[no_mangle]
pub extern "sysv64" fn test_SmallUnion(_: SmallUnion) -> SmallUnion { loop {} }
pub union UnionF32U32{a:f32, b:u32}
-// CHECK: define i32 @test_UnionF32U32(i32)
+// CHECK: define i32 @test_UnionF32U32(i32{{( %0)?}})
#[no_mangle]
pub fn test_UnionF32U32(_: UnionF32U32) -> UnionF32U32 { loop {} }
let mut sum = 0;
for i in 0..x {
//~^ ERROR E0015
+ //~| ERROR E0015
//~| ERROR E0658
//~| ERROR E0080
//~| ERROR E0744
//~| WARN denote infinite loops with
[(); { for _ in 0usize.. {}; 0}];
//~^ ERROR calls in constants are limited to constant functions
+ //~| ERROR calls in constants are limited to constant functions
//~| ERROR `for` is not allowed in a `const`
//~| ERROR references in constants may only refer to immutable values
//~| ERROR evaluation of constant value failed
+++ /dev/null
-// ignore-tidy-linelength
-// min-lldb-version: 310
-
-// As long as LLVM 5 and LLVM 6 are supported, we want to test the
-// enum debuginfo fallback mode. Once those are desupported, this
-// test can be removed, as there is another (non-"legacy") test that
-// tests the new mode.
-// ignore-llvm-version: 7.0 - 9.9.9
-// ignore-gdb-version: 7.11.90 - 7.12.9
-// ignore-gdb-version: 8.2 - 9.9
-
-// compile-flags:-g
-
-// === GDB TESTS ===================================================================================
-
-// gdb-command:run
-
-// gdb-command:print *the_a_ref
-// gdbg-check:$1 = {{RUST$ENUM$DISR = TheA, x = 0, y = 8970181431921507452}, {RUST$ENUM$DISR = TheA, [...]}}
-// gdbr-check:$1 = borrowed_enum_legacy::ABC::TheA{x: 0, y: 8970181431921507452}
-
-// gdb-command:print *the_b_ref
-// gdbg-check:$2 = {{RUST$ENUM$DISR = TheB, [...]}, {RUST$ENUM$DISR = TheB, __0 = 0, __1 = 286331153, __2 = 286331153}}
-// gdbr-check:$2 = borrowed_enum_legacy::ABC::TheB(0, 286331153, 286331153)
-
-// gdb-command:print *univariant_ref
-// gdbg-check:$3 = {{__0 = 4820353753753434}}
-// gdbr-check:$3 = borrowed_enum_legacy::Univariant::TheOnlyCase(4820353753753434)
-
-
-// === LLDB TESTS ==================================================================================
-
-// lldb-command:run
-
-// lldb-command:print *the_a_ref
-// lldbg-check:[...]$0 = TheA { x: 0, y: 8970181431921507452 }
-// lldbr-check:(borrowed_enum_legacy::ABC::TheA) *the_a_ref = TheA { borrowed_enum_legacy::ABC::TheA: 0, borrowed_enum_legacy::ABC::TheB: 8970181431921507452 }
-// lldb-command:print *the_b_ref
-// lldbg-check:[...]$1 = TheB(0, 286331153, 286331153)
-// lldbr-check:(borrowed_enum_legacy::ABC::TheB) *the_b_ref = { = 0 = 286331153 = 286331153 }
-// lldb-command:print *univariant_ref
-// lldbg-check:[...]$2 = TheOnlyCase(4820353753753434)
-// lldbr-check:(borrowed_enum_legacy::Univariant) *univariant_ref = { borrowed_enum_legacy::TheOnlyCase = { = 4820353753753434 } }
-
-#![allow(unused_variables)]
-#![feature(omit_gdb_pretty_printer_section)]
-#![omit_gdb_pretty_printer_section]
-
-// The first element is to ensure proper alignment, irrespective of the machines word size. Since
-// the size of the discriminant value is machine dependent, this has be taken into account when
-// datatype layout should be predictable as in this case.
-enum ABC {
- TheA { x: i64, y: i64 },
- TheB (i64, i32, i32),
-}
-
-// This is a special case since it does not have the implicit discriminant field.
-enum Univariant {
- TheOnlyCase(i64)
-}
-
-fn main() {
-
- // 0b0111110001111100011111000111110001111100011111000111110001111100 = 8970181431921507452
- // 0b01111100011111000111110001111100 = 2088533116
- // 0b0111110001111100 = 31868
- // 0b01111100 = 124
- let the_a = ABC::TheA { x: 0, y: 8970181431921507452 };
- let the_a_ref: &ABC = &the_a;
-
- // 0b0001000100010001000100010001000100010001000100010001000100010001 = 1229782938247303441
- // 0b00010001000100010001000100010001 = 286331153
- // 0b0001000100010001 = 4369
- // 0b00010001 = 17
- let the_b = ABC::TheB (0, 286331153, 286331153);
- let the_b_ref: &ABC = &the_b;
-
- let univariant = Univariant::TheOnlyCase(4820353753753434);
- let univariant_ref: &Univariant = &univariant;
-
- zzz(); // #break
-}
-
-fn zzz() {()}
+++ /dev/null
-// ignore-tidy-linelength
-// ignore-lldb: FIXME(#27089)
-// min-lldb-version: 310
-
-// As long as LLVM 5 and LLVM 6 are supported, we want to test the
-// enum debuginfo fallback mode. Once those are desupported, this
-// test can be removed, as there is another (non-"legacy") test that
-// tests the new mode.
-// ignore-llvm-version: 7.0 - 9.9.9
-// ignore-gdb-version: 8.2 - 9.9
-
-// compile-flags:-g
-
-// === GDB TESTS ===================================================================================
-// gdb-command:run
-
-// gdb-command:print eight_bytes1
-// gdbg-check:$1 = {{RUST$ENUM$DISR = Variant1, __0 = 100}, {RUST$ENUM$DISR = Variant1, __0 = 100}}
-// gdbr-check:$1 = generic_enum_with_different_disr_sizes_legacy::Enum::Variant1(100)
-
-// gdb-command:print four_bytes1
-// gdbg-check:$2 = {{RUST$ENUM$DISR = Variant1, __0 = 101}, {RUST$ENUM$DISR = Variant1, __0 = 101}}
-// gdbr-check:$2 = generic_enum_with_different_disr_sizes_legacy::Enum::Variant1(101)
-
-// gdb-command:print two_bytes1
-// gdbg-check:$3 = {{RUST$ENUM$DISR = Variant1, __0 = 102}, {RUST$ENUM$DISR = Variant1, __0 = 102}}
-// gdbr-check:$3 = generic_enum_with_different_disr_sizes_legacy::Enum::Variant1(102)
-
-// gdb-command:print one_byte1
-// gdbg-check:$4 = {{RUST$ENUM$DISR = Variant1, __0 = 65 'A'}, {RUST$ENUM$DISR = Variant1, __0 = 65 'A'}}
-// gdbr-check:$4 = generic_enum_with_different_disr_sizes_legacy::Enum::Variant1(65)
-
-
-// gdb-command:print eight_bytes2
-// gdbg-check:$5 = {{RUST$ENUM$DISR = Variant2, __0 = 100}, {RUST$ENUM$DISR = Variant2, __0 = 100}}
-// gdbr-check:$5 = generic_enum_with_different_disr_sizes_legacy::Enum::Variant2(100)
-
-// gdb-command:print four_bytes2
-// gdbg-check:$6 = {{RUST$ENUM$DISR = Variant2, __0 = 101}, {RUST$ENUM$DISR = Variant2, __0 = 101}}
-// gdbr-check:$6 = generic_enum_with_different_disr_sizes_legacy::Enum::Variant2(101)
-
-// gdb-command:print two_bytes2
-// gdbg-check:$7 = {{RUST$ENUM$DISR = Variant2, __0 = 102}, {RUST$ENUM$DISR = Variant2, __0 = 102}}
-// gdbr-check:$7 = generic_enum_with_different_disr_sizes_legacy::Enum::Variant2(102)
-
-// gdb-command:print one_byte2
-// gdbg-check:$8 = {{RUST$ENUM$DISR = Variant2, __0 = 65 'A'}, {RUST$ENUM$DISR = Variant2, __0 = 65 'A'}}
-// gdbr-check:$8 = generic_enum_with_different_disr_sizes_legacy::Enum::Variant2(65)
-
-// gdb-command:continue
-
-// === LLDB TESTS ==================================================================================
-// lldb-command:run
-
-// lldb-command:print eight_bytes1
-// lldb-check:[...]$0 = Variant1(100)
-// lldb-command:print four_bytes1
-// lldb-check:[...]$1 = Variant1(101)
-// lldb-command:print two_bytes1
-// lldb-check:[...]$2 = Variant1(102)
-// lldb-command:print one_byte1
-// lldb-check:[...]$3 = Variant1('A')
-
-// lldb-command:print eight_bytes2
-// lldb-check:[...]$4 = Variant2(100)
-// lldb-command:print four_bytes2
-// lldb-check:[...]$5 = Variant2(101)
-// lldb-command:print two_bytes2
-// lldb-check:[...]$6 = Variant2(102)
-// lldb-command:print one_byte2
-// lldb-check:[...]$7 = Variant2('A')
-
-// lldb-command:continue
-
-#![allow(unused_variables)]
-#![allow(dead_code)]
-#![feature(omit_gdb_pretty_printer_section)]
-#![omit_gdb_pretty_printer_section]
-
-// This test case makes sure that we get correct type descriptions for the enum
-// discriminant of different instantiations of the same generic enum type where,
-// dependending on the generic type parameter(s), the discriminant has a
-// different size in memory.
-
-enum Enum<T> {
- Variant1(T),
- Variant2(T)
-}
-
-fn main() {
- // These are ordered for descending size on purpose
- let eight_bytes1 = Enum::Variant1(100.0f64);
- let four_bytes1 = Enum::Variant1(101i32);
- let two_bytes1 = Enum::Variant1(102i16);
- let one_byte1 = Enum::Variant1(65u8);
-
- let eight_bytes2 = Enum::Variant2(100.0f64);
- let four_bytes2 = Enum::Variant2(101i32);
- let two_bytes2 = Enum::Variant2(102i16);
- let one_byte2 = Enum::Variant2(65u8);
-
- zzz(); // #break
-}
-
-fn zzz() { () }
+++ /dev/null
-// ignore-tidy-linelength
-// min-lldb-version: 310
-// ignore-gdb-version: 7.11.90 - 7.12.9
-
-// As long as LLVM 5 and LLVM 6 are supported, we want to test the
-// enum debuginfo fallback mode. Once those are desupported, this
-// test can be removed, as there is another (non-"legacy") test that
-// tests the new mode.
-// ignore-llvm-version: 7.0 - 9.9.9
-// ignore-gdb-version: 8.2 - 9.9
-
-// compile-flags:-g
-
-// gdb-command:set print union on
-// gdb-command:run
-
-// gdb-command:print case1
-// gdbg-check:$1 = {{RUST$ENUM$DISR = Case1, a = 0, b = 31868, c = 31868, d = 31868, e = 31868}, {RUST$ENUM$DISR = Case1, [...]}, {RUST$ENUM$DISR = Case1, [...]}}
-// gdbr-check:$1 = generic_struct_style_enum_legacy::Regular::Case1{a: 0, b: 31868, c: 31868, d: 31868, e: 31868}
-
-// gdb-command:print case2
-// gdbg-check:$2 = {{RUST$ENUM$DISR = Case2, [...]}, {RUST$ENUM$DISR = Case2, a = 0, b = 286331153, c = 286331153}, {RUST$ENUM$DISR = Case2, [...]}}
-// gdbr-check:$2 = generic_struct_style_enum_legacy::Regular::Case2{a: 0, b: 286331153, c: 286331153}
-
-// gdb-command:print case3
-// gdbg-check:$3 = {{RUST$ENUM$DISR = Case3, [...]}, {RUST$ENUM$DISR = Case3, [...]}, {RUST$ENUM$DISR = Case3, a = 0, b = 6438275382588823897}}
-// gdbr-check:$3 = generic_struct_style_enum_legacy::Regular::Case3{a: 0, b: 6438275382588823897}
-
-// gdb-command:print univariant
-// gdbg-check:$4 = {{a = -1}}
-// gdbr-check:$4 = generic_struct_style_enum_legacy::Univariant<i32>::TheOnlyCase{a: -1}
-
-
-#![feature(omit_gdb_pretty_printer_section)]
-#![omit_gdb_pretty_printer_section]
-
-use self::Regular::{Case1, Case2, Case3};
-use self::Univariant::TheOnlyCase;
-
-// NOTE: This is a copy of the non-generic test case. The `Txx` type parameters have to be
-// substituted with something of size `xx` bits and the same alignment as an integer type of the
-// same size.
-
-// The first element is to ensure proper alignment, irrespective of the machines word size. Since
-// the size of the discriminant value is machine dependent, this has be taken into account when
-// datatype layout should be predictable as in this case.
-enum Regular<T16, T32, T64> {
- Case1 { a: T64, b: T16, c: T16, d: T16, e: T16},
- Case2 { a: T64, b: T32, c: T32},
- Case3 { a: T64, b: T64 }
-}
-
-enum Univariant<T> {
- TheOnlyCase { a: T }
-}
-
-fn main() {
-
- // In order to avoid endianness trouble all of the following test values consist of a single
- // repeated byte. This way each interpretation of the union should look the same, no matter if
- // this is a big or little endian machine.
-
- // 0b0111110001111100011111000111110001111100011111000111110001111100 = 8970181431921507452
- // 0b01111100011111000111110001111100 = 2088533116
- // 0b0111110001111100 = 31868
- // 0b01111100 = 124
- let case1: Regular<u16, u32, i64> = Case1 { a: 0, b: 31868, c: 31868, d: 31868, e: 31868 };
-
- // 0b0001000100010001000100010001000100010001000100010001000100010001 = 1229782938247303441
- // 0b00010001000100010001000100010001 = 286331153
- // 0b0001000100010001 = 4369
- // 0b00010001 = 17
- let case2: Regular<i16, u32, i64> = Case2 { a: 0, b: 286331153, c: 286331153 };
-
- // 0b0101100101011001010110010101100101011001010110010101100101011001 = 6438275382588823897
- // 0b01011001010110010101100101011001 = 1499027801
- // 0b0101100101011001 = 22873
- // 0b01011001 = 89
- let case3: Regular<u16, i32, u64> = Case3 { a: 0, b: 6438275382588823897 };
-
- let univariant = TheOnlyCase { a: -1 };
-
- zzz(); // #break
-}
-
-fn zzz() {()}
+++ /dev/null
-// ignore-tidy-linelength
-// min-lldb-version: 310
-// ignore-gdb-version: 7.11.90 - 7.12.9
-
-// As long as LLVM 5 and LLVM 6 are supported, we want to test the
-// enum debuginfo fallback mode. Once those are desupported, this
-// test can be removed, as there is another (non-"legacy") test that
-// tests the new mode.
-// ignore-llvm-version: 7.0 - 9.9.9
-// ignore-gdb-version: 8.2 - 9.9
-
-// compile-flags:-g
-
-// === GDB TESTS ===================================================================================
-
-// gdb-command:set print union on
-// gdb-command:run
-
-// gdb-command:print case1
-// gdbg-check:$1 = {{RUST$ENUM$DISR = Case1, __0 = 0, __1 = 31868, __2 = 31868, __3 = 31868, __4 = 31868}, {RUST$ENUM$DISR = Case1, [...]}, {RUST$ENUM$DISR = Case1, [...]}}
-// gdbr-check:$1 = generic_tuple_style_enum_legacy::Regular::Case1(0, 31868, 31868, 31868, 31868)
-
-// gdb-command:print case2
-// gdbg-check:$2 = {{RUST$ENUM$DISR = Case2, [...]}, {RUST$ENUM$DISR = Case2, __0 = 0, __1 = 286331153, __2 = 286331153}, {RUST$ENUM$DISR = Case2, [...]}}
-// gdbr-check:$2 = generic_tuple_style_enum_legacy::Regular::Case2(0, 286331153, 286331153)
-
-// gdb-command:print case3
-// gdbg-check:$3 = {{RUST$ENUM$DISR = Case3, [...]}, {RUST$ENUM$DISR = Case3, [...]}, {RUST$ENUM$DISR = Case3, __0 = 0, __1 = 6438275382588823897}}
-// gdbr-check:$3 = generic_tuple_style_enum_legacy::Regular::Case3(0, 6438275382588823897)
-
-// gdb-command:print univariant
-// gdbg-check:$4 = {{__0 = -1}}
-// gdbr-check:$4 = generic_tuple_style_enum_legacy::Univariant<i64>::TheOnlyCase(-1)
-
-
-// === LLDB TESTS ==================================================================================
-
-// lldb-command:run
-
-// lldb-command:print case1
-// lldbg-check:[...]$0 = Case1(0, 31868, 31868, 31868, 31868)
-// lldbr-check:(generic_tuple_style_enum_legacy::Regular<u16, u32, u64>::Case1) case1 = { = 0 = 31868 = 31868 = 31868 = 31868 }
-
-// lldb-command:print case2
-// lldbg-check:[...]$1 = Case2(0, 286331153, 286331153)
-// lldbr-check:(generic_tuple_style_enum_legacy::Regular<i16, i32, i64>::Case2) case2 = Regular<i16, i32, i64>::Case2 { generic_tuple_style_enum_legacy::Regular<i16, i32, i64>::Case1: 0, generic_tuple_style_enum_legacy::Regular<i16, i32, i64>::Case2: 286331153, generic_tuple_style_enum_legacy::Regular<i16, i32, i64>::Case3: 286331153 }
-
-// lldb-command:print case3
-// lldbg-check:[...]$2 = Case3(0, 6438275382588823897)
-// lldbr-check:(generic_tuple_style_enum_legacy::Regular<i16, i32, i64>::Case3) case3 = Regular<i16, i32, i64>::Case3 { generic_tuple_style_enum_legacy::Regular<i16, i32, i64>::Case1: 0, generic_tuple_style_enum_legacy::Regular<i16, i32, i64>::Case2: 6438275382588823897 }
-
-// lldb-command:print univariant
-// lldbg-check:[...]$3 = TheOnlyCase(-1)
-// lldbr-check:(generic_tuple_style_enum_legacy::Univariant<i64>) univariant = { generic_tuple_style_enum_legacy::TheOnlyCase = { = -1 } }
-
-#![feature(omit_gdb_pretty_printer_section)]
-#![omit_gdb_pretty_printer_section]
-
-use self::Regular::{Case1, Case2, Case3};
-use self::Univariant::TheOnlyCase;
-
-// NOTE: This is a copy of the non-generic test case. The `Txx` type parameters have to be
-// substituted with something of size `xx` bits and the same alignment as an integer type of the
-// same size.
-
-// The first element is to ensure proper alignment, irrespective of the machines word size. Since
-// the size of the discriminant value is machine dependent, this has be taken into account when
-// datatype layout should be predictable as in this case.
-enum Regular<T16, T32, T64> {
- Case1(T64, T16, T16, T16, T16),
- Case2(T64, T32, T32),
- Case3(T64, T64)
-}
-
-enum Univariant<T64> {
- TheOnlyCase(T64)
-}
-
-fn main() {
-
- // In order to avoid endianness trouble all of the following test values consist of a single
- // repeated byte. This way each interpretation of the union should look the same, no matter if
- // this is a big or little endian machine.
-
- // 0b0111110001111100011111000111110001111100011111000111110001111100 = 8970181431921507452
- // 0b01111100011111000111110001111100 = 2088533116
- // 0b0111110001111100 = 31868
- // 0b01111100 = 124
- let case1: Regular<u16, u32, u64> = Case1(0_u64, 31868_u16, 31868_u16, 31868_u16, 31868_u16);
-
- // 0b0001000100010001000100010001000100010001000100010001000100010001 = 1229782938247303441
- // 0b00010001000100010001000100010001 = 286331153
- // 0b0001000100010001 = 4369
- // 0b00010001 = 17
- let case2: Regular<i16, i32, i64> = Case2(0_i64, 286331153_i32, 286331153_i32);
-
- // 0b0101100101011001010110010101100101011001010110010101100101011001 = 6438275382588823897
- // 0b01011001010110010101100101011001 = 1499027801
- // 0b0101100101011001 = 22873
- // 0b01011001 = 89
- let case3: Regular<i16, i32, i64> = Case3(0_i64, 6438275382588823897_i64);
-
- let univariant = TheOnlyCase(-1_i64);
-
- zzz(); // #break
-}
-
-fn zzz() { () }
+++ /dev/null
-// ignore-tidy-linelength
-// ignore-lldb
-
-// As long as LLVM 5 and LLVM 6 are supported, we want to test the
-// enum debuginfo fallback mode. Once those are desupported, this
-// test can be removed, as there is another (non-"legacy") test that
-// tests the new mode.
-// ignore-llvm-version: 7.0 - 9.9.9
-// ignore-gdb-version: 7.11.90 - 7.12.9
-// ignore-gdb-version: 8.2 - 9.9
-
-// compile-flags:-g
-
-// gdb-command:run
-
-// gdb-command:print stack_unique.value
-// gdb-check:$1 = 0
-// gdbg-command:print stack_unique.next.RUST$ENCODED$ENUM$0$Empty.val->value
-// gdbr-command:print stack_unique.next.val.value
-// gdb-check:$2 = 1
-
-// gdbg-command:print unique_unique->value
-// gdbr-command:print unique_unique.value
-// gdb-check:$3 = 2
-// gdbg-command:print unique_unique->next.RUST$ENCODED$ENUM$0$Empty.val->value
-// gdbr-command:print unique_unique.next.val.value
-// gdb-check:$4 = 3
-
-// gdb-command:print vec_unique[0].value
-// gdb-check:$5 = 6.5
-// gdbg-command:print vec_unique[0].next.RUST$ENCODED$ENUM$0$Empty.val->value
-// gdbr-command:print vec_unique[0].next.val.value
-// gdb-check:$6 = 7.5
-
-// gdbg-command:print borrowed_unique->value
-// gdbr-command:print borrowed_unique.value
-// gdb-check:$7 = 8.5
-// gdbg-command:print borrowed_unique->next.RUST$ENCODED$ENUM$0$Empty.val->value
-// gdbr-command:print borrowed_unique.next.val.value
-// gdb-check:$8 = 9.5
-
-// LONG CYCLE
-// gdb-command:print long_cycle1.value
-// gdb-check:$9 = 20
-// gdbg-command:print long_cycle1.next->value
-// gdbr-command:print long_cycle1.next.value
-// gdb-check:$10 = 21
-// gdbg-command:print long_cycle1.next->next->value
-// gdbr-command:print long_cycle1.next.next.value
-// gdb-check:$11 = 22
-// gdbg-command:print long_cycle1.next->next->next->value
-// gdbr-command:print long_cycle1.next.next.next.value
-// gdb-check:$12 = 23
-
-// gdb-command:print long_cycle2.value
-// gdb-check:$13 = 24
-// gdbg-command:print long_cycle2.next->value
-// gdbr-command:print long_cycle2.next.value
-// gdb-check:$14 = 25
-// gdbg-command:print long_cycle2.next->next->value
-// gdbr-command:print long_cycle2.next.next.value
-// gdb-check:$15 = 26
-
-// gdb-command:print long_cycle3.value
-// gdb-check:$16 = 27
-// gdbg-command:print long_cycle3.next->value
-// gdbr-command:print long_cycle3.next.value
-// gdb-check:$17 = 28
-
-// gdb-command:print long_cycle4.value
-// gdb-check:$18 = 29.5
-
-// gdbg-command:print (*****long_cycle_w_anonymous_types).value
-// gdbr-command:print long_cycle_w_anonymous_types.value
-// gdb-check:$19 = 30
-
-// gdbg-command:print (*****((*****long_cycle_w_anonymous_types).next.RUST$ENCODED$ENUM$0$Empty.val)).value
-// gdbr-command:print long_cycle_w_anonymous_types.next.val.value
-// gdb-check:$20 = 31
-
-// gdb-command:continue
-
-#![allow(unused_variables)]
-#![feature(box_syntax)]
-#![feature(omit_gdb_pretty_printer_section)]
-#![omit_gdb_pretty_printer_section]
-
-use self::Opt::{Empty, Val};
-
-enum Opt<T> {
- Empty,
- Val { val: T }
-}
-
-struct UniqueNode<T> {
- next: Opt<Box<UniqueNode<T>>>,
- value: T
-}
-
-struct LongCycle1<T> {
- next: Box<LongCycle2<T>>,
- value: T,
-}
-
-struct LongCycle2<T> {
- next: Box<LongCycle3<T>>,
- value: T,
-}
-
-struct LongCycle3<T> {
- next: Box<LongCycle4<T>>,
- value: T,
-}
-
-struct LongCycle4<T> {
- next: Option<Box<LongCycle1<T>>>,
- value: T,
-}
-
-struct LongCycleWithAnonymousTypes {
- next: Opt<Box<Box<Box<Box<Box<LongCycleWithAnonymousTypes>>>>>>,
- value: usize,
-}
-
-// This test case makes sure that recursive structs are properly described. The Node structs are
-// generic so that we can have a new type (that newly needs to be described) for the different
-// cases. The potential problem with recursive types is that the DI generation algorithm gets
-// trapped in an endless loop. To make sure, we actually test this in the different cases, we have
-// to operate on a new type each time, otherwise we would just hit the DI cache for all but the
-// first case.
-
-// The different cases below (stack_*, unique_*, box_*, etc) are set up so that the type description
-// algorithm will enter the type reference cycle that is created by a recursive definition from a
-// different context each time.
-
-// The "long cycle" cases are constructed to span a longer, indirect recursion cycle between types.
-// The different locals will cause the DI algorithm to enter the type reference cycle at different
-// points.
-
-fn main() {
- let stack_unique: UniqueNode<u16> = UniqueNode {
- next: Val {
- val: box UniqueNode {
- next: Empty,
- value: 1,
- }
- },
- value: 0,
- };
-
- let unique_unique: Box<UniqueNode<u32>> = box UniqueNode {
- next: Val {
- val: box UniqueNode {
- next: Empty,
- value: 3,
- }
- },
- value: 2,
- };
-
- let vec_unique: [UniqueNode<f32>; 1] = [UniqueNode {
- next: Val {
- val: box UniqueNode {
- next: Empty,
- value: 7.5,
- }
- },
- value: 6.5,
- }];
-
- let borrowed_unique: &UniqueNode<f64> = &UniqueNode {
- next: Val {
- val: box UniqueNode {
- next: Empty,
- value: 9.5,
- }
- },
- value: 8.5,
- };
-
- // LONG CYCLE
- let long_cycle1: LongCycle1<u16> = LongCycle1 {
- next: box LongCycle2 {
- next: box LongCycle3 {
- next: box LongCycle4 {
- next: None,
- value: 23,
- },
- value: 22,
- },
- value: 21
- },
- value: 20
- };
-
- let long_cycle2: LongCycle2<u32> = LongCycle2 {
- next: box LongCycle3 {
- next: box LongCycle4 {
- next: None,
- value: 26,
- },
- value: 25,
- },
- value: 24
- };
-
- let long_cycle3: LongCycle3<u64> = LongCycle3 {
- next: box LongCycle4 {
- next: None,
- value: 28,
- },
- value: 27,
- };
-
- let long_cycle4: LongCycle4<f32> = LongCycle4 {
- next: None,
- value: 29.5,
- };
-
- // It's important that LongCycleWithAnonymousTypes is encountered only at the end of the
- // `box` chain.
- let long_cycle_w_anonymous_types = box box box box box LongCycleWithAnonymousTypes {
- next: Val {
- val: box box box box box LongCycleWithAnonymousTypes {
- next: Empty,
- value: 31,
- }
- },
- value: 30
- };
-
- zzz(); // #break
-}
-
-fn zzz() {()}
+++ /dev/null
-// ignore-tidy-linelength
-// min-lldb-version: 310
-
-// As long as LLVM 5 and LLVM 6 are supported, we want to test the
-// enum debuginfo fallback mode. Once those are desupported, this
-// test can be removed, as there is another (non-"legacy") test that
-// tests the new mode.
-// ignore-llvm-version: 7.0 - 9.9.9
-// ignore-gdb-version: 7.11.90 - 7.12.9
-// ignore-gdb-version: 8.2 - 9.9
-
-// compile-flags:-g
-
-// === GDB TESTS ===================================================================================
-
-// gdb-command:set print union on
-// gdb-command:run
-
-// gdb-command:print case1
-// gdbg-check:$1 = {{RUST$ENUM$DISR = Case1, a = 0, b = 31868, c = 31868, d = 31868, e = 31868}, {RUST$ENUM$DISR = Case1, [...]}, {RUST$ENUM$DISR = Case1, [...]}}
-// gdbr-check:$1 = struct_style_enum_legacy::Regular::Case1{a: 0, b: 31868, c: 31868, d: 31868, e: 31868}
-
-// gdb-command:print case2
-// gdbg-check:$2 = {{RUST$ENUM$DISR = Case2, [...]}, {RUST$ENUM$DISR = Case2, a = 0, b = 286331153, c = 286331153}, {RUST$ENUM$DISR = Case2, [...]}}
-// gdbr-check:$2 = struct_style_enum_legacy::Regular::Case2{a: 0, b: 286331153, c: 286331153}
-
-// gdb-command:print case3
-// gdbg-check:$3 = {{RUST$ENUM$DISR = Case3, [...]}, {RUST$ENUM$DISR = Case3, [...]}, {RUST$ENUM$DISR = Case3, a = 0, b = 6438275382588823897}}
-// gdbr-check:$3 = struct_style_enum_legacy::Regular::Case3{a: 0, b: 6438275382588823897}
-
-// gdb-command:print univariant
-// gdbg-check:$4 = {{a = -1}}
-// gdbr-check:$4 = struct_style_enum_legacy::Univariant::TheOnlyCase{a: -1}
-
-
-// === LLDB TESTS ==================================================================================
-
-// lldb-command:run
-
-// lldb-command:print case1
-// lldbg-check:[...]$0 = Case1 { a: 0, b: 31868, c: 31868, d: 31868, e: 31868 }
-// lldbr-check:(struct_style_enum_legacy::Regular::Case1) case1 = { a = 0 b = 31868 c = 31868 d = 31868 e = 31868 }
-
-// lldb-command:print case2
-// lldbg-check:[...]$1 = Case2 { a: 0, b: 286331153, c: 286331153 }
-// lldbr-check:(struct_style_enum_legacy::Regular::Case2) case2 = Case2 { struct_style_enum_legacy::Regular::Case1: 0, struct_style_enum_legacy::Regular::Case2: 286331153, struct_style_enum_legacy::Regular::Case3: 286331153 }
-
-// lldb-command:print case3
-// lldbg-check:[...]$2 = Case3 { a: 0, b: 6438275382588823897 }
-// lldbr-check:(struct_style_enum_legacy::Regular::Case3) case3 = Case3 { struct_style_enum_legacy::Regular::Case1: 0, struct_style_enum_legacy::Regular::Case2: 6438275382588823897 }
-
-// lldb-command:print univariant
-// lldbg-check:[...]$3 = TheOnlyCase { a: -1 }
-// lldbr-check:(struct_style_enum_legacy::Univariant) univariant = Univariant { struct_style_enum_legacy::TheOnlyCase: TheOnlyCase { a: -1 } }
-
-#![allow(unused_variables)]
-#![feature(omit_gdb_pretty_printer_section)]
-#![omit_gdb_pretty_printer_section]
-
-use self::Regular::{Case1, Case2, Case3};
-use self::Univariant::TheOnlyCase;
-
-// The first element is to ensure proper alignment, irrespective of the machines word size. Since
-// the size of the discriminant value is machine dependent, this has be taken into account when
-// datatype layout should be predictable as in this case.
-enum Regular {
- Case1 { a: u64, b: u16, c: u16, d: u16, e: u16},
- Case2 { a: u64, b: u32, c: u32},
- Case3 { a: u64, b: u64 }
-}
-
-enum Univariant {
- TheOnlyCase { a: i64 }
-}
-
-fn main() {
-
- // In order to avoid endianness trouble all of the following test values consist of a single
- // repeated byte. This way each interpretation of the union should look the same, no matter if
- // this is a big or little endian machine.
-
- // 0b0111110001111100011111000111110001111100011111000111110001111100 = 8970181431921507452
- // 0b01111100011111000111110001111100 = 2088533116
- // 0b0111110001111100 = 31868
- // 0b01111100 = 124
- let case1 = Case1 { a: 0, b: 31868, c: 31868, d: 31868, e: 31868 };
-
- // 0b0001000100010001000100010001000100010001000100010001000100010001 = 1229782938247303441
- // 0b00010001000100010001000100010001 = 286331153
- // 0b0001000100010001 = 4369
- // 0b00010001 = 17
- let case2 = Case2 { a: 0, b: 286331153, c: 286331153 };
-
- // 0b0101100101011001010110010101100101011001010110010101100101011001 = 6438275382588823897
- // 0b01011001010110010101100101011001 = 1499027801
- // 0b0101100101011001 = 22873
- // 0b01011001 = 89
- let case3 = Case3 { a: 0, b: 6438275382588823897 };
-
- let univariant = TheOnlyCase { a: -1 };
-
- zzz(); // #break
-}
-
-fn zzz() {()}
+++ /dev/null
-// ignore-tidy-linelength
-// min-lldb-version: 310
-
-// As long as LLVM 5 and LLVM 6 are supported, we want to test the
-// enum debuginfo fallback mode. Once those are desupported, this
-// test can be removed, as there is another (non-"legacy") test that
-// tests the new mode.
-// ignore-llvm-version: 7.0 - 9.9.9
-// ignore-gdb-version: 7.11.90 - 7.12.9
-// ignore-gdb-version: 8.2 - 9.9
-
-// compile-flags:-g
-
-// === GDB TESTS ===================================================================================
-
-// gdb-command:set print union on
-// gdb-command:run
-
-// gdb-command:print case1
-// gdbg-check:$1 = {{RUST$ENUM$DISR = Case1, __0 = 0, __1 = 31868, __2 = 31868, __3 = 31868, __4 = 31868}, {RUST$ENUM$DISR = Case1, [...]}, {RUST$ENUM$DISR = Case1, [...]}}
-// gdbr-check:$1 = tuple_style_enum_legacy::Regular::Case1(0, 31868, 31868, 31868, 31868)
-
-// gdb-command:print case2
-// gdbg-check:$2 = {{RUST$ENUM$DISR = Case2, [...]}, {RUST$ENUM$DISR = Case2, __0 = 0, __1 = 286331153, __2 = 286331153}, {RUST$ENUM$DISR = Case2, [...]}}
-// gdbr-check:$2 = tuple_style_enum_legacy::Regular::Case2(0, 286331153, 286331153)
-
-// gdb-command:print case3
-// gdbg-check:$3 = {{RUST$ENUM$DISR = Case3, [...]}, {RUST$ENUM$DISR = Case3, [...]}, {RUST$ENUM$DISR = Case3, __0 = 0, __1 = 6438275382588823897}}
-// gdbr-check:$3 = tuple_style_enum_legacy::Regular::Case3(0, 6438275382588823897)
-
-// gdb-command:print univariant
-// gdbg-check:$4 = {{__0 = -1}}
-// gdbr-check:$4 = tuple_style_enum_legacy::Univariant::TheOnlyCase(-1)
-
-
-// === LLDB TESTS ==================================================================================
-
-// lldb-command:run
-
-// lldb-command:print case1
-// lldbg-check:[...]$0 = Case1(0, 31868, 31868, 31868, 31868)
-// lldbr-check:(tuple_style_enum_legacy::Regular::Case1) case1 = { = 0 = 31868 = 31868 = 31868 = 31868 }
-
-// lldb-command:print case2
-// lldbg-check:[...]$1 = Case2(0, 286331153, 286331153)
-// lldbr-check:(tuple_style_enum_legacy::Regular::Case2) case2 = Case2 { tuple_style_enum_legacy::Regular::Case1: 0, tuple_style_enum_legacy::Regular::Case2: 286331153, tuple_style_enum_legacy::Regular::Case3: 286331153 }
-
-// lldb-command:print case3
-// lldbg-check:[...]$2 = Case3(0, 6438275382588823897)
-// lldbr-check:(tuple_style_enum_legacy::Regular::Case3) case3 = Case3 { tuple_style_enum_legacy::Regular::Case1: 0, tuple_style_enum_legacy::Regular::Case2: 6438275382588823897 }
-
-// lldb-command:print univariant
-// lldbg-check:[...]$3 = TheOnlyCase(-1)
-// lldbr-check:(tuple_style_enum_legacy::Univariant) univariant = { tuple_style_enum_legacy::TheOnlyCase = { = -1 } }
-
-#![allow(unused_variables)]
-#![feature(omit_gdb_pretty_printer_section)]
-#![omit_gdb_pretty_printer_section]
-
-use self::Regular::{Case1, Case2, Case3};
-use self::Univariant::TheOnlyCase;
-
-// The first element is to ensure proper alignment, irrespective of the machines word size. Since
-// the size of the discriminant value is machine dependent, this has be taken into account when
-// datatype layout should be predictable as in this case.
-enum Regular {
- Case1(u64, u16, u16, u16, u16),
- Case2(u64, u32, u32),
- Case3(u64, u64)
-}
-
-enum Univariant {
- TheOnlyCase(i64)
-}
-
-fn main() {
-
- // In order to avoid endianness trouble all of the following test values consist of a single
- // repeated byte. This way each interpretation of the union should look the same, no matter if
- // this is a big or little endian machine.
-
- // 0b0111110001111100011111000111110001111100011111000111110001111100 = 8970181431921507452
- // 0b01111100011111000111110001111100 = 2088533116
- // 0b0111110001111100 = 31868
- // 0b01111100 = 124
- let case1 = Case1(0, 31868, 31868, 31868, 31868);
-
- // 0b0001000100010001000100010001000100010001000100010001000100010001 = 1229782938247303441
- // 0b00010001000100010001000100010001 = 286331153
- // 0b0001000100010001 = 4369
- // 0b00010001 = 17
- let case2 = Case2(0, 286331153, 286331153);
-
- // 0b0101100101011001010110010101100101011001010110010101100101011001 = 6438275382588823897
- // 0b01011001010110010101100101011001 = 1499027801
- // 0b0101100101011001 = 22873
- // 0b01011001 = 89
- let case3 = Case3(0, 6438275382588823897);
-
- let univariant = TheOnlyCase(-1);
-
- zzz(); // #break
-}
-
-fn zzz() {()}
+++ /dev/null
-// ignore-tidy-linelength
-// min-lldb-version: 310
-
-// As long as LLVM 5 and LLVM 6 are supported, we want to test the
-// enum debuginfo fallback mode. Once those are desupported, this
-// test can be removed, as there is another (non-"legacy") test that
-// tests the new mode.
-// ignore-llvm-version: 7.0 - 9.9.9
-// ignore-gdb-version: 7.11.90 - 7.12.9
-// ignore-gdb-version: 8.2 - 9.9
-
-// compile-flags:-g
-
-// === GDB TESTS ===================================================================================
-
-// gdb-command:run
-
-// gdb-command:print *the_a
-// gdbg-check:$1 = {{RUST$ENUM$DISR = TheA, x = 0, y = 8970181431921507452}, {RUST$ENUM$DISR = TheA, [...]}}
-// gdbr-check:$1 = unique_enum_legacy::ABC::TheA{x: 0, y: 8970181431921507452}
-
-// gdb-command:print *the_b
-// gdbg-check:$2 = {{RUST$ENUM$DISR = TheB, [...]}, {RUST$ENUM$DISR = TheB, __0 = 0, __1 = 286331153, __2 = 286331153}}
-// gdbr-check:$2 = unique_enum_legacy::ABC::TheB(0, 286331153, 286331153)
-
-// gdb-command:print *univariant
-// gdbg-check:$3 = {{__0 = 123234}}
-// gdbr-check:$3 = unique_enum_legacy::Univariant::TheOnlyCase(123234)
-
-
-// === LLDB TESTS ==================================================================================
-
-// lldb-command:run
-
-// lldb-command:print *the_a
-// lldbg-check:[...]$0 = TheA { x: 0, y: 8970181431921507452 }
-// lldbr-check:(unique_enum_legacy::ABC::TheA) *the_a = TheA { unique_enum_legacy::ABC::TheA: 0, unique_enum_legacy::ABC::TheB: 8970181431921507452 }
-
-// lldb-command:print *the_b
-// lldbg-check:[...]$1 = TheB(0, 286331153, 286331153)
-// lldbr-check:(unique_enum_legacy::ABC::TheB) *the_b = { = 0 = 286331153 = 286331153 }
-
-// lldb-command:print *univariant
-// lldbg-check:[...]$2 = TheOnlyCase(123234)
-// lldbr-check:(unique_enum_legacy::Univariant) *univariant = { unique_enum_legacy::TheOnlyCase = { = 123234 } }
-
-#![allow(unused_variables)]
-#![feature(box_syntax)]
-#![feature(omit_gdb_pretty_printer_section)]
-#![omit_gdb_pretty_printer_section]
-
-// The first element is to ensure proper alignment, irrespective of the machines word size. Since
-// the size of the discriminant value is machine dependent, this has be taken into account when
-// datatype layout should be predictable as in this case.
-enum ABC {
- TheA { x: i64, y: i64 },
- TheB (i64, i32, i32),
-}
-
-// This is a special case since it does not have the implicit discriminant field.
-enum Univariant {
- TheOnlyCase(i64)
-}
-
-fn main() {
-
- // In order to avoid endianness trouble all of the following test values consist of a single
- // repeated byte. This way each interpretation of the union should look the same, no matter if
- // this is a big or little endian machine.
-
- // 0b0111110001111100011111000111110001111100011111000111110001111100 = 8970181431921507452
- // 0b01111100011111000111110001111100 = 2088533116
- // 0b0111110001111100 = 31868
- // 0b01111100 = 124
- let the_a: Box<_> = box ABC::TheA { x: 0, y: 8970181431921507452 };
-
- // 0b0001000100010001000100010001000100010001000100010001000100010001 = 1229782938247303441
- // 0b00010001000100010001000100010001 = 286331153
- // 0b0001000100010001 = 4369
- // 0b00010001 = 17
- let the_b: Box<_> = box ABC::TheB (0, 286331153, 286331153);
-
- let univariant: Box<_> = box Univariant::TheOnlyCase(123234);
-
- zzz(); // #break
-}
-
-fn zzz() {()}
--- /dev/null
+extern "C" {
+ static X: i32;
+}
+
+static Y: i32 = 42;
+
+static mut BAR: *const &'static i32 = [&Y].as_ptr();
+
+static mut FOO: *const &'static i32 = [unsafe { &X }].as_ptr();
+
+fn main() {}
+
+// END RUST SOURCE
+// START rustc.FOO.PromoteTemps.before.mir
+// bb0: {
+// ...
+// _5 = const Scalar(AllocId(1).0x0) : &i32;
+// _4 = &(*_5);
+// _3 = [move _4];
+// _2 = &_3;
+// _1 = move _2 as &[&'static i32] (Pointer(Unsize));
+// _0 = const core::slice::<impl [&'static i32]>::as_ptr(move _1) -> [return: bb2, unwind: bb1];
+// }
+// ...
+// bb2: {
+// StorageDead(_5);
+// StorageDead(_3);
+// return;
+// }
+// END rustc.FOO.PromoteTemps.before.mir
+// START rustc.BAR.PromoteTemps.before.mir
+// bb0: {
+// ...
+// _5 = const Scalar(AllocId(0).0x0) : &i32;
+// _4 = &(*_5);
+// _3 = [move _4];
+// _2 = &_3;
+// _1 = move _2 as &[&'static i32] (Pointer(Unsize));
+// _0 = const core::slice::<impl [&'static i32]>::as_ptr(move _1) -> [return: bb2, unwind: bb1];
+// }
+// ...
+// bb2: {
+// StorageDead(_5);
+// StorageDead(_3);
+// return;
+// }
+// END rustc.BAR.PromoteTemps.before.mir
+// START rustc.BAR.PromoteTemps.after.mir
+// bb0: {
+// ...
+// _6 = const BAR::promoted[0];
+// _2 = &(*_6);
+// _1 = move _2 as &[&'static i32] (Pointer(Unsize));
+// _0 = const core::slice::<impl [&'static i32]>::as_ptr(move _1) -> [return: bb2, unwind: bb1];
+// }
+// ...
+// bb2: {
+// return;
+// }
+// END rustc.BAR.PromoteTemps.after.mir
+// START rustc.FOO.PromoteTemps.after.mir
+// bb0: {
+// ...
+// _6 = const FOO::promoted[0];
+// _2 = &(*_6);
+// _1 = move _2 as &[&'static i32] (Pointer(Unsize));
+// _0 = const core::slice::<impl [&'static i32]>::as_ptr(move _1) -> [return: bb2, unwind: bb1];
+// }
+// ...
+// bb2: {
+// return;
+// }
+// END rustc.FOO.PromoteTemps.after.mir
}
// END RUST SOURCE
+// START rustc.main.PromoteTemps.before.mir
+// bb0: {
+// ...
+// _3 = const 4i32;
+// _2 = &_3;
+// _1 = (*_2);
+// ...
+//}
+// END rustc.main.PromoteTemps.before.mir
+// START rustc.main.PromoteTemps.after.mir
+// bb0: {
+// ...
+// _4 = const main::promoted[0];
+// _2 = &(*_4);
+// _1 = (*_2);
+// ...
+//}
+// END rustc.main.PromoteTemps.after.mir
// START rustc.main.ConstProp.before.mir
// bb0: {
// ...
-// _2 = &(promoted[0]: i32);
+// _4 = const main::promoted[0];
+// _2 = _4;
// _1 = (*_2);
// ...
//}
// START rustc.main.ConstProp.after.mir
// bb0: {
// ...
-// _2 = &(promoted[0]: i32);
+// _4 = const main::promoted[0];
+// _2 = _4;
// _1 = const 4i32;
// ...
// }
--- /dev/null
+fn main() {
+ *(&(4, 5).1);
+}
+
+// END RUST SOURCE
+// START rustc.main.PromoteTemps.before.mir
+// bb0: {
+// ...
+// _3 = (const 4i32, const 5i32);
+// _2 = &(_3.1: i32);
+// _1 = (*_2);
+// ...
+//}
+// END rustc.main.PromoteTemps.before.mir
+// START rustc.main.PromoteTemps.after.mir
+// bb0: {
+// ...
+// _4 = const main::promoted[0];
+// _2 = &((*_4).1: i32);
+// _1 = (*_2);
+// ...
+//}
+// END rustc.main.PromoteTemps.after.mir
+// START rustc.main.ConstProp.before.mir
+// bb0: {
+// ...
+// _4 = const main::promoted[0];
+// _2 = &((*_4).1: i32);
+// _1 = (*_2);
+// ...
+//}
+// END rustc.main.ConstProp.before.mir
+// START rustc.main.ConstProp.after.mir
+// bb0: {
+// ...
+// _4 = const main::promoted[0];
+// _2 = &((*_4).1: i32);
+// _1 = const 5i32;
+// ...
+// }
+// END rustc.main.ConstProp.after.mir
// START rustc.main.ConstProp.before.mir
// bb0: {
// ...
-// _4 = &(promoted[0]: [u32; 3]);
+// _9 = const main::promoted[0];
+// _4 = _9;
// _3 = _4;
// _2 = move _3 as &[u32] (Pointer(Unsize));
// ...
// START rustc.main.ConstProp.after.mir
// bb0: {
// ...
-// _4 = &(promoted[0]: [u32; 3]);
+// _9 = const main::promoted[0];
+// _4 = _9;
// _3 = _4;
// _2 = move _3 as &[u32] (Pointer(Unsize));
// ...
// ...
// Retag(_3);
// Retag(_6);
-// StorageLive(_9);
-// _9 = (*_3);
-// StorageLive(_10);
-// _10 = (*_6);
-// _0 = Eq(move _9, move _10);
+// StorageLive(_11);
+// _11 = (*_3);
+// StorageLive(_12);
+// _12 = (*_6);
+// _0 = Eq(move _11, move _12);
// ...
// return;
// }
// }
// bb6: { // binding1 and guard
// StorageLive(_6);
-// _6 = &(((promoted[0]: std::option::Option<i32>) as Some).0: i32);
+// _11 = const full_tested_match::promoted[0];
+// _6 = &(((*_11) as Some).0: i32);
// _4 = &shallow _2;
// StorageLive(_7);
// _7 = const guard() -> [return: bb7, unwind: bb1];
endif
all:
- $(RUSTC) -g -Z sanitizer=address -Z print-link-args $(EXTRA_RUSTFLAG) overflow.rs | $(CGREP) librustc_asan
+ $(RUSTC) -g -Z sanitizer=address -Z print-link-args $(EXTRA_RUSTFLAG) overflow.rs | $(CGREP) rustc_rt.asan
# Verify that stack buffer overflow is detected:
$(TMPDIR)/overflow 2>&1 | $(CGREP) stack-buffer-overflow
# Verify that variable name is included in address sanitizer report:
+++ /dev/null
-# needs-sanitizer-support
-
--include ../tools.mk
-
-# NOTE the address sanitizer only supports x86_64 linux and macOS
-
-ifeq ($(TARGET),x86_64-apple-darwin)
-EXTRA_RUSTFLAG=-C rpath
-else
-ifeq ($(TARGET),x86_64-unknown-linux-gnu)
-EXTRA_RUSTFLAG=
-endif
-endif
-
-all:
- $(RUSTC) -Z sanitizer=address --crate-type proc-macro --target $(TARGET) hello.rs 2>&1 | $(CGREP) '-Z sanitizer'
+++ /dev/null
-fn main() {
- println!("Hello, world!");
-}
all:
$(RUSTC) -Z sanitizer=leak --target i686-unknown-linux-gnu hello.rs 2>&1 | \
- $(CGREP) 'LeakSanitizer only works with the `x86_64-unknown-linux-gnu` target'
+ $(CGREP) 'LeakSanitizer only works with the `x86_64-unknown-linux-gnu` or `x86_64-apple-darwin` target'
# FIXME(#46126) ThinLTO for libstd broke this test
all:
- $(RUSTC) -C opt-level=1 -g -Z sanitizer=leak -Z print-link-args leak.rs | $(CGREP) librustc_lsan
+ $(RUSTC) -C opt-level=1 -g -Z sanitizer=leak -Z print-link-args leak.rs | $(CGREP) rustc_rt.lsan
$(TMPDIR)/leak 2>&1 | $(CGREP) 'detected memory leaks'
# only-x86_64
all:
- $(RUSTC) -g -Z sanitizer=memory -Z print-link-args uninit.rs | $(CGREP) librustc_msan
+ $(RUSTC) -g -Z sanitizer=memory -Z print-link-args uninit.rs | $(CGREP) rustc_rt.msan
$(TMPDIR)/uninit 2>&1 | $(CGREP) use-of-uninitialized-value
- $(RUSTC) -g -Z sanitizer=memory -Z print-link-args maybeuninit.rs | $(CGREP) librustc_msan
+ $(RUSTC) -g -Z sanitizer=memory -Z print-link-args maybeuninit.rs | $(CGREP) rustc_rt.msan
$(TMPDIR)/maybeuninit 2>&1 | $(CGREP) use-of-uninitialized-value
// cross-compiled standard libraries.
#![feature(no_core, optin_builtin_traits)]
#![no_core]
-
#![feature(repr_simd, simd_ffi, link_llvm_intrinsics, lang_items, rustc_attrs)]
-
-#[repr(C)]
#[derive(Copy)]
#[repr(simd)]
pub struct f32x4(f32, f32, f32, f32);
-
-extern {
+extern "C" {
#[link_name = "llvm.sqrt.v4f32"]
fn vsqrt(x: f32x4) -> f32x4;
}
pub fn foo(x: f32x4) -> f32x4 {
- unsafe {vsqrt(x)}
+ unsafe { vsqrt(x) }
}
-#[repr(C)]
#[derive(Copy)]
#[repr(simd)]
pub struct i32x4(i32, i32, i32, i32);
-
-extern {
+extern "C" {
// _mm_sll_epi32
- #[cfg(any(target_arch = "x86",
- target_arch = "x86-64"))]
+ #[cfg(any(target_arch = "x86", target_arch = "x86-64"))]
#[link_name = "llvm.x86.sse2.psll.d"]
fn integer(a: i32x4, b: i32x4) -> i32x4;
// just some substitute foreign symbol, not an LLVM intrinsic; so
// we still get type checking, but not as detailed as (ab)using
// LLVM.
- #[cfg(not(any(target_arch = "x86",
- target_arch = "x86-64",
- target_arch = "arm",
- target_arch = "aarch64")))]
+ #[cfg(not(any(
+ target_arch = "x86",
+ target_arch = "x86-64",
+ target_arch = "arm",
+ target_arch = "aarch64"
+ )))]
fn integer(a: i32x4, b: i32x4) -> i32x4;
}
pub fn bar(a: i32x4, b: i32x4) -> i32x4 {
- unsafe {integer(a, b)}
+ unsafe { integer(a, b) }
}
#[lang = "sized"]
-pub trait Sized { }
+pub trait Sized {}
#[lang = "copy"]
-pub trait Copy { }
+pub trait Copy {}
impl Copy for f32 {}
impl Copy for i32 {}
#[macro_export]
#[rustc_builtin_macro]
-macro_rules! Copy { () => () }
+macro_rules! Copy {
+ () => {};
+}
#[no_mangle]
pub extern fn foo() {}
+
+#[no_mangle]
+pub static FOO: u64 = 42;
const my_exports = {};
let nexports = 0;
+
for (const entry of list) {
- if (entry.kind !== 'function')
- continue;
- my_exports[entry.name] = true;
- nexports += 1;
+ if (entry.kind == 'function'){
+ nexports += 1;
+ }
+ my_exports[entry.name] = entry.kind;
}
-if (my_exports.foo === undefined)
+if (my_exports.foo != "function")
throw new Error("`foo` wasn't defined");
+if (my_exports.FOO != "global")
+ throw new Error("`FOO` wasn't defined");
+
if (my_exports.main === undefined) {
if (nexports != 1)
throw new Error("should only have one function export");
+// ignore-tidy-linelength
+
#![crate_name = "foo"]
#![feature(doc_cfg)]
-// @has 'foo/index.html'
-// @!has '-' '//*[@class="stab portability"]' 'feature="sync" and'
-// @has '-' '//*[@class="stab portability"]' 'feature="sync"'
+// @has 'foo/struct.Foo.html'
+// @has '-' '//*[@class="stab portability"]' 'This is supported on feature="sync" only.'
#[doc(cfg(feature = "sync"))]
#[doc(cfg(feature = "sync"))]
pub struct Foo;
+// @has 'foo/bar/struct.Bar.html'
+// @has '-' '//*[@class="stab portability"]' 'This is supported on feature="sync" only.'
#[doc(cfg(feature = "sync"))]
pub mod bar {
#[doc(cfg(feature = "sync"))]
pub struct Bar;
}
+
+// @has 'foo/baz/struct.Baz.html'
+// @has '-' '//*[@class="stab portability"]' 'This is supported on feature="sync" and feature="send" only.'
+#[doc(cfg(all(feature = "sync", feature = "send")))]
+pub mod baz {
+ #[doc(cfg(feature = "sync"))]
+ pub struct Baz;
+}
+
+// @has 'foo/qux/struct.Qux.html'
+// @has '-' '//*[@class="stab portability"]' 'This is supported on feature="sync" and feature="send" only.'
+#[doc(cfg(feature = "sync"))]
+pub mod qux {
+ #[doc(cfg(all(feature = "sync", feature = "send")))]
+ pub struct Qux;
+}
+
+// @has 'foo/quux/struct.Quux.html'
+// @has '-' '//*[@class="stab portability"]' 'This is supported on feature="sync" and feature="send" and foo and bar only.'
+#[doc(cfg(all(feature = "sync", feature = "send", foo)))]
+pub mod quux {
+ #[doc(cfg(all(feature = "send", feature = "sync", bar)))]
+ pub struct Quux;
+}
+++ /dev/null
-// compile-flags: --document-private-items
-
-// @has issue_46380/struct.Hidden.html
-#[doc(hidden)]
-pub struct Hidden;
--- /dev/null
+// compile-flags: -Zunstable-options --document-private-items --document-hidden-items
+
+// @has issue_67851_both/struct.Hidden.html
+#[doc(hidden)]
+pub struct Hidden;
+
+// @has issue_67851_both/struct.Private.html
+struct Private;
--- /dev/null
+// compile-flags: -Zunstable-options --document-hidden-items
+
+// @has issue_67851_hidden/struct.Hidden.html
+#[doc(hidden)]
+pub struct Hidden;
+
+// @!has issue_67851_hidden/struct.Private.html
+struct Private;
--- /dev/null
+// @!has issue_67851_neither/struct.Hidden.html
+#[doc(hidden)]
+pub struct Hidden;
+
+// @!has issue_67851_neither/struct.Private.html
+struct Private;
--- /dev/null
+// compile-flags: --document-private-items
+
+// @!has issue_67851_private/struct.Hidden.html
+#[doc(hidden)]
+pub struct Hidden;
+
+// @has issue_67851_private/struct.Private.html
+struct Private;
#![feature(box_syntax, plugin, plugin_registrar, rustc_private)]
#![crate_type = "dylib"]
-#[macro_use] extern crate rustc;
-#[macro_use] extern crate rustc_session;
extern crate rustc_driver;
extern crate rustc_hir;
+#[macro_use] extern crate rustc_lint;
+#[macro_use] extern crate rustc_session;
extern crate rustc_span;
extern crate syntax;
-use rustc::hir::intravisit;
+use rustc_hir::intravisit;
use rustc_hir as hir;
use rustc_hir::Node;
-use rustc::lint::{LateContext, LintPass, LintArray, LateLintPass, LintContext};
+use rustc_lint::{LateContext, LintPass, LintArray, LateLintPass, LintContext};
use rustc_driver::plugin::Registry;
use rustc_span::source_map;
use syntax::print::pprust;
#![feature(plugin_registrar, rustc_private)]
#![feature(box_syntax)]
-#[macro_use] extern crate rustc;
-#[macro_use] extern crate rustc_session;
extern crate rustc_driver;
extern crate rustc_hir;
extern crate rustc_span;
+#[macro_use] extern crate rustc_lint;
+#[macro_use] extern crate rustc_session;
extern crate syntax;
-use rustc::lint::{LateContext, LintContext, LintPass, LateLintPass};
+use rustc_lint::{LateContext, LintContext, LintPass, LateLintPass};
use rustc_driver::plugin::Registry;
use rustc_span::symbol::Symbol;
use syntax::attr;
#![feature(plugin_registrar, rustc_private)]
#![feature(box_syntax)]
-#[macro_use] extern crate rustc;
-#[macro_use] extern crate rustc_session;
extern crate rustc_driver;
extern crate rustc_hir;
+#[macro_use] extern crate rustc_lint;
+#[macro_use] extern crate rustc_session;
extern crate rustc_span;
extern crate syntax;
-use rustc::lint::{LateContext, LintContext, LintPass, LateLintPass, LintArray};
+use rustc_lint::{LateContext, LintContext, LintPass, LateLintPass, LintArray};
use rustc_driver::plugin::Registry;
use rustc_span::symbol::Symbol;
use syntax::attr;
#![feature(box_syntax, rustc_private)]
// Load rustc as a plugin to get macros.
-#[macro_use] extern crate rustc;
-#[macro_use] extern crate rustc_session;
extern crate rustc_driver;
extern crate rustc_hir;
+#[macro_use] extern crate rustc_lint;
+#[macro_use] extern crate rustc_session;
-use rustc::lint::{LateContext, LintContext, LintPass, LateLintPass, LintArray, LintId};
+use rustc_lint::{LateContext, LintContext, LintPass, LateLintPass, LintArray, LintId};
use rustc_driver::plugin::Registry;
declare_lint!(TEST_LINT, Warn, "Warn about items named 'lintme'");
extern crate syntax;
// Load rustc as a plugin to get macros
-#[macro_use] extern crate rustc;
-#[macro_use] extern crate rustc_session;
extern crate rustc_driver;
+#[macro_use] extern crate rustc_lint;
+#[macro_use] extern crate rustc_session;
-use rustc::lint::{EarlyContext, LintContext, LintPass, EarlyLintPass, LintArray};
+use rustc_lint::{EarlyContext, LintContext, LintPass, EarlyLintPass, LintArray};
use rustc_driver::plugin::Registry;
use syntax::ast;
declare_lint!(TEST_LINT, Warn, "Warn about items named 'lintme'");
extern crate syntax;
// Load rustc as a plugin to get macros
-#[macro_use] extern crate rustc;
-#[macro_use] extern crate rustc_session;
extern crate rustc_driver;
+#[macro_use] extern crate rustc_lint;
+#[macro_use] extern crate rustc_session;
-use rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintContext, LintPass, LintId};
+use rustc_lint::{EarlyContext, EarlyLintPass, LintArray, LintContext, LintPass, LintId};
use rustc_driver::plugin::Registry;
use syntax::ast;
declare_tool_lint!(pub clippy::TEST_LINT, Warn, "Warn about stuff");
extern crate rustc;
extern crate rustc_session;
-use rustc::lint::{LintArray, LintPass};
-use rustc::{declare_lint_pass, impl_lint_pass};
-use rustc_session::declare_lint;
+use rustc_session::lint::{LintArray, LintPass};
+use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass};
declare_lint! {
pub TEST_LINT,
error: implementing `LintPass` by hand
- --> $DIR/lint_pass_impl_without_macro.rs:21:6
+ --> $DIR/lint_pass_impl_without_macro.rs:20:6
|
LL | impl LintPass for Foo {
| ^^^^^^^^
= help: try using `declare_lint_pass!` or `impl_lint_pass!` instead
error: implementing `LintPass` by hand
- --> $DIR/lint_pass_impl_without_macro.rs:31:14
+ --> $DIR/lint_pass_impl_without_macro.rs:30:14
|
LL | impl LintPass for Custom {
| ^^^^^^^^
#[allow(test_lint)]
//~^ ERROR allow(test_lint) overruled by outer forbid(test_lint)
+//~| ERROR allow(test_lint) overruled by outer forbid(test_lint)
+//~| ERROR allow(test_lint) overruled by outer forbid(test_lint)
pub fn main() {
lintme();
}
LL | #[allow(test_lint)]
| ^^^^^^^^^ overruled by previous forbid
+error[E0453]: allow(test_lint) overruled by outer forbid(test_lint)
+ --> $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 | #![forbid(test_lint)]
| ^^^^^^^^^
-error: aborting due to 2 previous errors
+error[E0453]: allow(test_lint) overruled by outer forbid(test_lint)
+ --> $DIR/lint-plugin-forbid-attrs.rs:11:9
+ |
+LL | #![forbid(test_lint)]
+ | --------- `forbid` level set here
+...
+LL | #[allow(test_lint)]
+ | ^^^^^^^^^ overruled by previous forbid
+
+error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0453`.
fn lintme() { } //~ ERROR item is named 'lintme'
#[allow(test_lint)] //~ ERROR allow(test_lint) overruled by outer forbid(test_lint)
+ //~| ERROR allow(test_lint) overruled by outer forbid(test_lint)
+ //~| ERROR allow(test_lint) overruled by outer forbid(test_lint)
pub fn main() {
lintme();
}
|
= note: `forbid` lint level was set on command line
+error[E0453]: allow(test_lint) overruled by outer forbid(test_lint)
+ --> $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
|
|
= note: requested on the command line with `-F test-lint`
-error: aborting due to 2 previous errors
+error[E0453]: allow(test_lint) overruled by outer forbid(test_lint)
+ --> $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
+
+error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0453`.
|
= note: requested on the command line with `-A test_lint`
+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: 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
|
|
= 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: 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`
+
#![allow(dead_code)]
#![cfg_attr(foo, warn(test_lint))]
//~^ 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
fn lintme() { } //~ ERROR item is named 'lintme'
#[allow(test_group)]
//~^ 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. Also `cfg_attr(cargo-clippy)` won't be necessary anymore
- --> $DIR/lint-tool-test.rs:12: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. Also `cfg_attr(cargo-clippy)` won't be necessary anymore
- --> $DIR/lint-tool-test.rs:26: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:28:8
+ --> $DIR/lint-tool-test.rs:33:8
|
LL | #[deny(this_lint_does_not_exist)]
| ^^^^^^^^^^^^^^^^^^^^^^^^
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. Also `cfg_attr(cargo-clippy)` won't be necessary anymore
+ --> $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. Also `cfg_attr(cargo-clippy)` won't be necessary anymore
+ --> $DIR/lint-tool-test.rs:29:9
+ |
+LL | #[allow(test_group)]
+ | ^^^^^^^^^^ help: change it to: `clippy::test_group`
+
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
|
|
= note: `#[warn(deprecated)]` on by default
+warning: lint name `test_lint` is deprecated and may not have an effect in the future. Also `cfg_attr(cargo-clippy)` won't be necessary anymore
+ --> $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. Also `cfg_attr(cargo-clippy)` won't be necessary anymore
+ --> $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:15:1
+ --> $DIR/lint-tool-test.rs:18:1
|
LL | fn lintme() { }
| ^^^^^^^^^^^^^^^
|
note: lint level defined here
- --> $DIR/lint-tool-test.rs:12: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:23:5
+ --> $DIR/lint-tool-test.rs:26:5
|
LL | fn lintmetoo() { }
| ^^^^^^^^^^^^^^^^^^
|
note: lint level defined here
- --> $DIR/lint-tool-test.rs:12:9
+ --> $DIR/lint-tool-test.rs:13:9
|
LL | #![deny(clippy_group)]
| ^^^^^^^^^^^^
= 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. Also `cfg_attr(cargo-clippy)` won't be necessary anymore
+ --> $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
}
const X: i32 = <i32>::ID;
-//~^ ERROR no associated item named `ID` found for type `i32`
+//~^ ERROR no associated item named `ID` found
fn main() {
assert_eq!(1, X);
| --------------- required by `A::C`
LL |
LL | fn f() -> ([u8; A::C], [u8; A::C]);
- | ^^^^ cannot infer type
+ | ^^^^
+ | |
+ | cannot infer type
+ | help: use the fully qualified path to an implementation: `<Type as A>::C`
|
= note: cannot resolve `_: A`
+ = note: associated constants cannot be accessed directly on a `trait`, they can only be accessed through a specific `impl`
error[E0283]: type annotations needed
--> $DIR/issue-63496.rs:4:33
| --------------- required by `A::C`
LL |
LL | fn f() -> ([u8; A::C], [u8; A::C]);
- | ^^^^ cannot infer type
+ | ^^^^
+ | |
+ | cannot infer type
+ | help: use the fully qualified path to an implementation: `<Type as A>::C`
|
= note: cannot resolve `_: A`
+ = note: associated constants cannot be accessed directly on a `trait`, they can only be accessed through a specific `impl`
error: aborting due to 2 previous errors
-error[E0599]: no variant or associated item named `mispellable` found for type `Enum` in the current scope
+error[E0599]: no variant or associated item named `mispellable` found for enum `Enum` in the current scope
--> $DIR/associated-item-enum.rs:17:11
|
LL | enum Enum { Variant }
| variant or associated item not found in `Enum`
| help: there is a method with a similar name: `misspellable`
-error[E0599]: no variant or associated item named `mispellable_trait` found for type `Enum` in the current scope
+error[E0599]: no variant or associated item named `mispellable_trait` found for enum `Enum` in the current scope
--> $DIR/associated-item-enum.rs:18:11
|
LL | enum Enum { Variant }
LL | Enum::mispellable_trait();
| ^^^^^^^^^^^^^^^^^ variant or associated item not found in `Enum`
-error[E0599]: no variant or associated item named `MISPELLABLE` found for type `Enum` in the current scope
+error[E0599]: no variant or associated item named `MISPELLABLE` found for enum `Enum` in the current scope
--> $DIR/associated-item-enum.rs:19:11
|
LL | enum Enum { Variant }
LL | const X: usize;
| --------------- required by `Bar::X`
LL | fn return_n(&self) -> [u8; Bar::X];
- | ^^^^^^ cannot infer type
+ | ^^^^^^
+ | |
+ | cannot infer type
+ | help: use the fully qualified path to an implementation: `<Type as Bar>::X`
|
= note: cannot resolve `_: Bar`
+ = note: associated constants cannot be accessed directly on a `trait`, they can only be accessed through a specific `impl`
error: aborting due to 2 previous errors
//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719]
trait TRSW1 where Self: Iterator<Item: Copy, Item: Send> {}
//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719]
+//~| ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719]
trait TRSW2 where Self: Iterator<Item: Copy, Item: Copy> {}
//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719]
+//~| ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719]
trait TRSW3 where Self: Iterator<Item: 'static, Item: 'static> {}
//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719]
+//~| ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719]
trait TRA1 { type A: Iterator<Item: Copy, Item: Send>; }
//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719]
trait TRA2 { type A: Iterator<Item: Copy, Item: Copy>; }
| `Item` bound here first
error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified
- --> $DIR/duplicate.rs:160:46
+ --> $DIR/duplicate.rs:158:46
+ |
+LL | trait TRSW1 where Self: Iterator<Item: Copy, Item: Send> {}
+ | ---------- ^^^^^^^^^^ re-bound here
+ | |
+ | `Item` bound here first
+
+error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified
+ --> $DIR/duplicate.rs:161:46
+ |
+LL | trait TRSW2 where Self: Iterator<Item: Copy, Item: Copy> {}
+ | ---------- ^^^^^^^^^^ re-bound here
+ | |
+ | `Item` bound here first
+
+error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified
+ --> $DIR/duplicate.rs:161:46
|
LL | trait TRSW2 where Self: Iterator<Item: Copy, Item: Copy> {}
| ---------- ^^^^^^^^^^ re-bound here
| `Item` bound here first
error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified
- --> $DIR/duplicate.rs:162:49
+ --> $DIR/duplicate.rs:164:49
+ |
+LL | trait TRSW3 where Self: Iterator<Item: 'static, Item: 'static> {}
+ | ------------- ^^^^^^^^^^^^^ re-bound here
+ | |
+ | `Item` bound here first
+
+error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified
+ --> $DIR/duplicate.rs:164:49
|
LL | trait TRSW3 where Self: Iterator<Item: 'static, Item: 'static> {}
| ------------- ^^^^^^^^^^^^^ re-bound here
| `Item` bound here first
error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified
- --> $DIR/duplicate.rs:164:43
+ --> $DIR/duplicate.rs:167:43
|
LL | trait TRA1 { type A: Iterator<Item: Copy, Item: Send>; }
| ---------- ^^^^^^^^^^ re-bound here
| `Item` bound here first
error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified
- --> $DIR/duplicate.rs:166:43
+ --> $DIR/duplicate.rs:169:43
|
LL | trait TRA2 { type A: Iterator<Item: Copy, Item: Copy>; }
| ---------- ^^^^^^^^^^ re-bound here
| `Item` bound here first
error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified
- --> $DIR/duplicate.rs:168:46
+ --> $DIR/duplicate.rs:171:46
|
LL | trait TRA3 { type A: Iterator<Item: 'static, Item: 'static>; }
| ------------- ^^^^^^^^^^^^^ re-bound here
| `Item` bound here first
error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified
- --> $DIR/duplicate.rs:171:40
+ --> $DIR/duplicate.rs:174:40
|
LL | type TADyn1 = dyn Iterator<Item: Copy, Item: Send>;
| ---------- ^^^^^^^^^^ re-bound here
| `Item` bound here first
error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified
- --> $DIR/duplicate.rs:175:44
+ --> $DIR/duplicate.rs:178:44
|
LL | type TADyn2 = Box<dyn Iterator<Item: Copy, Item: Copy>>;
| ---------- ^^^^^^^^^^ re-bound here
| `Item` bound here first
error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified
- --> $DIR/duplicate.rs:179:43
+ --> $DIR/duplicate.rs:182:43
|
LL | type TADyn3 = dyn Iterator<Item: 'static, Item: 'static>;
| ------------- ^^^^^^^^^^^^^ re-bound here
| ^^^^^^^^^^^^^
error: could not find defining uses
- --> $DIR/duplicate.rs:171:28
+ --> $DIR/duplicate.rs:174:28
|
LL | type TADyn1 = dyn Iterator<Item: Copy, Item: Send>;
| ^^^^^^^^^^
error: could not find defining uses
- --> $DIR/duplicate.rs:171:40
+ --> $DIR/duplicate.rs:174:40
|
LL | type TADyn1 = dyn Iterator<Item: Copy, Item: Send>;
| ^^^^^^^^^^
error: could not find defining uses
- --> $DIR/duplicate.rs:175:32
+ --> $DIR/duplicate.rs:178:32
|
LL | type TADyn2 = Box<dyn Iterator<Item: Copy, Item: Copy>>;
| ^^^^^^^^^^
error: could not find defining uses
- --> $DIR/duplicate.rs:175:44
+ --> $DIR/duplicate.rs:178:44
|
LL | type TADyn2 = Box<dyn Iterator<Item: Copy, Item: Copy>>;
| ^^^^^^^^^^
error: could not find defining uses
- --> $DIR/duplicate.rs:179:28
+ --> $DIR/duplicate.rs:182:28
|
LL | type TADyn3 = dyn Iterator<Item: 'static, Item: 'static>;
| ^^^^^^^^^^^^^
error: could not find defining uses
- --> $DIR/duplicate.rs:179:43
+ --> $DIR/duplicate.rs:182:43
|
LL | type TADyn3 = dyn Iterator<Item: 'static, Item: 'static>;
| ^^^^^^^^^^^^^
-error: aborting due to 93 previous errors
+error: aborting due to 96 previous errors
error[E0308]: mismatched types
--> $DIR/dont-suggest-missing-await.rs:14:18
|
+LL | async fn make_u32() -> u32 {
+ | --- the `Output` of this `async fn`'s found opaque type
+...
LL | take_u32(x)
| ^ expected `u32`, found opaque type
|
|
LL | fn is_qux<T: Qux>(t: T) { }
| ------ --- required by this bound in `is_qux`
+LL |
+LL | async fn bar() {
+ | - within this `impl std::future::Future`
...
LL | is_qux(bar());
| ^^^^^^ within `impl std::future::Future`, the trait `Qux` is not implemented for `Foo`
async fn async_ret_impl_trait<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> {
//~^ ERROR ambiguous lifetime bound
+ //~| ERROR ambiguous lifetime bound
(a, b)
}
|
= help: add #![feature(member_constraints)] to the crate attributes to enable
-error: aborting due to previous error
+error: ambiguous lifetime bound in `impl Trait`
+ --> $DIR/ret-impl-trait-no-fg.rs:9:64
+ |
+LL | async fn async_ret_impl_trait<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> {
+ | ^^^^^^^^^^^^^^^^^^ neither `'a` nor `'b` outlives the other
+ |
+ = help: add #![feature(member_constraints)] to the crate attributes to enable
+
+error: aborting due to 2 previous errors
error[E0308]: mismatched types
--> $DIR/suggest-missing-await-closure.rs:16:18
|
+LL | async fn make_u32() -> u32 {
+ | --- the `Output` of this `async fn`'s found opaque type
+...
LL | take_u32(x)
| ^
| |
error[E0308]: mismatched types
--> $DIR/suggest-missing-await.rs:13:14
|
+LL | async fn make_u32() -> u32 {
+ | --- the `Output` of this `async fn`'s found opaque type
+...
LL | take_u32(x)
| ^
| |
error[E0308]: mismatched types
--> $DIR/suggest-missing-await.rs:23:5
|
+LL | async fn dummy() {}
+ | - the `Output` of this `async fn`'s found opaque type
+...
LL | dummy()
| ^^^^^^^ expected `()`, found opaque type
|
async fn foo() {
bar().await;
//~^ ERROR type inside `async fn` body must be known in this context
+ //~| ERROR type inside `async fn` body must be known in this context
+ //~| ERROR type inside `async fn` body must be known in this context
//~| NOTE cannot infer type for type parameter `T`
+ //~| NOTE cannot infer type for type parameter `T`
+ //~| NOTE cannot infer type for type parameter `T`
+ //~| NOTE the type is part of the `async fn` body because of this `await`
//~| NOTE the type is part of the `async fn` body because of this `await`
+ //~| NOTE the type is part of the `async fn` body because of this `await`
+ //~| NOTE in this expansion of desugaring of `await`
+ //~| NOTE in this expansion of desugaring of `await`
//~| NOTE in this expansion of desugaring of `await`
}
fn main() {}
LL | bar().await;
| ^^^^^^^^^^^
-error: aborting due to previous error
+error[E0698]: type inside `async fn` body must be known in this context
+ --> $DIR/unresolved_type_param.rs:9:5
+ |
+LL | bar().await;
+ | ^^^ cannot infer type for type parameter `T` declared on the function `bar`
+ |
+note: the type is part of the `async fn` body because of this `await`
+ --> $DIR/unresolved_type_param.rs:9:5
+ |
+LL | bar().await;
+ | ^^^^^^^^^^^
+
+error[E0698]: type inside `async fn` body must be known in this context
+ --> $DIR/unresolved_type_param.rs:9:5
+ |
+LL | bar().await;
+ | ^^^ cannot infer type for type parameter `T` declared on the function `bar`
+ |
+note: the type is part of the `async fn` body because of this `await`
+ --> $DIR/unresolved_type_param.rs:9:5
+ |
+LL | bar().await;
+ | ^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0698`.
#[renamed_attr] //~ ERROR cannot use an explicitly registered attribute through an import
#[renamed_tool::attr] //~ ERROR cannot use a tool module through an import
+ //~| ERROR cannot use a tool module through an import
fn main() {}
LL | use tool as renamed_tool; // OK
| ^^^^^^^^^^^^^^^^^^^^
-error: aborting due to 2 previous errors
+error: cannot use a tool module through an import
+ --> $DIR/register-attr-tool-import.rs:13:3
+ |
+LL | #[renamed_tool::attr]
+ | ^^^^^^^^^^^^
+ |
+note: the tool module imported here
+ --> $DIR/register-attr-tool-import.rs:10:5
+ |
+LL | use tool as renamed_tool; // OK
+ | ^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
-error[E0599]: no method named `test_mut` found for type `std::vec::Vec<{integer}>` in the current scope
+error[E0599]: no method named `test_mut` found for struct `std::vec::Vec<{integer}>` in the current scope
--> $DIR/auto-ref-slice-plus-ref.rs:7:7
|
LL | a.test_mut();
= note: the following trait defines an item `test_mut`, perhaps you need to implement it:
candidate #1: `MyIter`
-error[E0599]: no method named `test` found for type `std::vec::Vec<{integer}>` in the current scope
+error[E0599]: no method named `test` found for struct `std::vec::Vec<{integer}>` in the current scope
--> $DIR/auto-ref-slice-plus-ref.rs:8:7
|
LL | a.test();
= note: the following trait defines an item `test`, perhaps you need to implement it:
candidate #1: `MyIter`
-error[E0599]: no method named `test` found for type `[{integer}; 1]` in the current scope
+error[E0599]: no method named `test` found for array `[{integer}; 1]` in the current scope
--> $DIR/auto-ref-slice-plus-ref.rs:10:11
|
LL | ([1]).test();
= note: the following trait defines an item `test`, perhaps you need to implement it:
candidate #1: `MyIter`
-error[E0599]: no method named `test` found for type `&[{integer}; 1]` in the current scope
+error[E0599]: no method named `test` found for reference `&[{integer}; 1]` in the current scope
--> $DIR/auto-ref-slice-plus-ref.rs:11:12
|
LL | (&[1]).test();
trait A {
fn a(&self) {
|| self.b()
- //~^ ERROR no method named `b` found for type `&Self` in the current scope
+ //~^ ERROR no method named `b` found
}
}
fn main() {}
-error[E0599]: no method named `b` found for type `&Self` in the current scope
+error[E0599]: no method named `b` found for reference `&Self` in the current scope
--> $DIR/issue-3563.rs:3:17
|
LL | || self.b()
-error[E0599]: no variant or associated item named `Hsl` found for type `Color` in the current scope
+error[E0599]: no variant or associated item named `Hsl` found for enum `Color` in the current scope
--> $DIR/bogus-tag.rs:7:16
|
LL | enum Color { Rgb(isize, isize, isize), Rgba(isize, isize, isize, isize), }
--- /dev/null
+#![feature(slice_patterns)]
+
+fn array() -> [(String, String); 3] {
+ Default::default()
+}
+
+// Const Index + Const Index
+
+fn move_out_from_begin_and_end() {
+ let a = array();
+ match a {
+ [_, _, _x] => {}
+ }
+ match a {
+ [.., _y] => {} //~ ERROR use of moved value
+ }
+}
+
+fn move_out_from_begin_field_and_end() {
+ let a = array();
+ match a {
+ [_, _, (_x, _)] => {}
+ }
+ match a {
+ [.., _y] => {} //~ ERROR use of moved value
+ }
+}
+
+fn move_out_from_begin_field_and_end_field() {
+ let a = array();
+ match a {
+ [_, _, (_x, _)] => {}
+ }
+ match a {
+ [.., (_y, _)] => {} //~ ERROR use of moved value
+ }
+}
+
+// Const Index + Slice
+
+fn move_out_by_const_index_and_subslice() {
+ let a = array();
+ match a {
+ [_x, _, _] => {}
+ }
+ match a {
+ //~^ ERROR use of moved value
+ [_y @ .., _, _] => {}
+ }
+}
+
+fn move_out_by_const_index_end_and_subslice() {
+ let a = array();
+ match a {
+ [.., _x] => {}
+ }
+ match a {
+ //~^ ERROR use of moved value
+ [_, _, _y @ ..] => {}
+ }
+}
+
+fn move_out_by_const_index_field_and_subslice() {
+ let a = array();
+ match a {
+ [(_x, _), _, _] => {}
+ }
+ match a {
+ //~^ ERROR use of moved value
+ [_y @ .., _, _] => {}
+ }
+}
+
+fn move_out_by_const_index_end_field_and_subslice() {
+ let a = array();
+ match a {
+ [.., (_x, _)] => {}
+ }
+ match a {
+ //~^ ERROR use of moved value
+ [_, _, _y @ ..] => {}
+ }
+}
+
+fn move_out_by_subslice_and_const_index_field() {
+ let a = array();
+ match a {
+ [_y @ .., _, _] => {}
+ }
+ match a {
+ [(_x, _), _, _] => {} //~ ERROR use of moved value
+ }
+}
+
+fn move_out_by_subslice_and_const_index_end_field() {
+ let a = array();
+ match a {
+ [_, _, _y @ ..] => {}
+ }
+ match a {
+ [.., (_x, _)] => {} //~ ERROR use of moved value
+ }
+}
+
+// Slice + Slice
+
+fn move_out_by_subslice_and_subslice() {
+ let a = array();
+ match a {
+ [x @ .., _] => {}
+ }
+ match a {
+ //~^ ERROR use of moved value
+ [_, _y @ ..] => {}
+ }
+}
+
+fn main() {}
--- /dev/null
+error[E0382]: use of moved value: `a[..]`
+ --> $DIR/borrowck-move-out-from-array-match.rs:15:14
+ |
+LL | [_, _, _x] => {}
+ | -- value moved here
+...
+LL | [.., _y] => {}
+ | ^^ value used here after move
+ |
+ = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `a[..]`
+ --> $DIR/borrowck-move-out-from-array-match.rs:25:14
+ |
+LL | [_, _, (_x, _)] => {}
+ | -- value moved here
+...
+LL | [.., _y] => {}
+ | ^^ value used here after partial move
+ |
+ = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `a[..].0`
+ --> $DIR/borrowck-move-out-from-array-match.rs:35:15
+ |
+LL | [_, _, (_x, _)] => {}
+ | -- value moved here
+...
+LL | [.., (_y, _)] => {}
+ | ^^ value used here after move
+ |
+ = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `a`
+ --> $DIR/borrowck-move-out-from-array-match.rs:46:11
+ |
+LL | [_x, _, _] => {}
+ | -- value moved here
+LL | }
+LL | match a {
+ | ^ value used here after partial move
+ |
+ = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `a`
+ --> $DIR/borrowck-move-out-from-array-match.rs:57:11
+ |
+LL | [.., _x] => {}
+ | -- value moved here
+LL | }
+LL | match a {
+ | ^ value used here after partial move
+ |
+ = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `a`
+ --> $DIR/borrowck-move-out-from-array-match.rs:68:11
+ |
+LL | [(_x, _), _, _] => {}
+ | -- value moved here
+LL | }
+LL | match a {
+ | ^ value used here after partial move
+ |
+ = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `a`
+ --> $DIR/borrowck-move-out-from-array-match.rs:79:11
+ |
+LL | [.., (_x, _)] => {}
+ | -- value moved here
+LL | }
+LL | match a {
+ | ^ value used here after partial move
+ |
+ = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `a[..].0`
+ --> $DIR/borrowck-move-out-from-array-match.rs:91:11
+ |
+LL | [_y @ .., _, _] => {}
+ | ------- value moved here
+...
+LL | [(_x, _), _, _] => {}
+ | ^^ value used here after move
+ |
+ = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `a[..].0`
+ --> $DIR/borrowck-move-out-from-array-match.rs:101:15
+ |
+LL | [_, _, _y @ ..] => {}
+ | ------- value moved here
+...
+LL | [.., (_x, _)] => {}
+ | ^^ value used here after move
+ |
+ = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `a`
+ --> $DIR/borrowck-move-out-from-array-match.rs:112:11
+ |
+LL | [x @ .., _] => {}
+ | ------ value moved here
+LL | }
+LL | match a {
+ | ^ value used here after partial move
+ |
+ = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+
+error: aborting due to 10 previous errors
+
+For more information about this error, try `rustc --explain E0382`.
--- /dev/null
+// Due to #53114, which causes a "read" of the `_` patterns,
+// the borrow-checker refuses this code, while it should probably be allowed.
+// Once the bug is fixed, the test, which is derived from a
+// passing test for `let` statements, should become check-pass.
+
+#![feature(slice_patterns)]
+
+fn array() -> [(String, String); 3] {
+ Default::default()
+}
+
+// Const Index + Const Index
+
+fn move_out_from_begin_and_one_from_end() {
+ let a = array();
+ match a {
+ [_, _, _x] => {}
+ }
+ match a {
+ //~^ ERROR use of moved value
+ [.., _y, _] => {}
+ }
+}
+
+fn move_out_from_begin_field_and_end_field() {
+ let a = array();
+ match a {
+ [_, _, (_x, _)] => {}
+ }
+ match a {
+ //~^ ERROR use of moved value
+ [.., (_, _y)] => {}
+ }
+}
+
+// Const Index + Slice
+
+fn move_out_by_const_index_and_subslice() {
+ let a = array();
+ match a {
+ [_x, _, _] => {}
+ }
+ match a {
+ //~^ ERROR use of moved value
+ [_, _y @ ..] => {}
+ }
+}
+
+fn move_out_by_const_index_end_and_subslice() {
+ let a = array();
+ match a {
+ [.., _x] => {}
+ }
+ match a {
+ //~^ ERROR use of moved value
+ [_y @ .., _] => {}
+ }
+}
+
+fn move_out_by_const_index_field_and_subslice() {
+ let a = array();
+ match a {
+ [(_x, _), _, _] => {}
+ }
+ match a {
+ //~^ ERROR use of moved value
+ [_, _y @ ..] => {}
+ }
+}
+
+fn move_out_by_const_index_end_field_and_subslice() {
+ let a = array();
+ match a {
+ [.., (_x, _)] => {}
+ }
+ match a {
+ //~^ ERROR use of moved value
+ [_y @ .., _] => {}
+ }
+}
+
+fn move_out_by_const_subslice_and_index_field() {
+ let a = array();
+ match a {
+ [_, _y @ ..] => {}
+ }
+ match a {
+ //~^ ERROR use of moved value
+ [(_x, _), _, _] => {}
+ }
+}
+
+fn move_out_by_const_subslice_and_end_index_field() {
+ let a = array();
+ match a {
+ [_y @ .., _] => {}
+ }
+ match a {
+ //~^ ERROR use of moved value
+ [.., (_x, _)] => {}
+ }
+}
+
+// Slice + Slice
+
+fn move_out_by_subslice_and_subslice() {
+ let a = array();
+ match a {
+ [x @ .., _, _] => {}
+ }
+ match a {
+ //~^ ERROR use of moved value
+ [_, _y @ ..] => {}
+ }
+}
+
+fn main() {}
--- /dev/null
+error[E0382]: use of moved value: `a`
+ --> $DIR/borrowck-move-out-from-array-no-overlap-match.rs:19:11
+ |
+LL | [_, _, _x] => {}
+ | -- value moved here
+LL | }
+LL | match a {
+ | ^ value used here after partial move
+ |
+ = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `a`
+ --> $DIR/borrowck-move-out-from-array-no-overlap-match.rs:30:11
+ |
+LL | [_, _, (_x, _)] => {}
+ | -- value moved here
+LL | }
+LL | match a {
+ | ^ value used here after partial move
+ |
+ = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `a`
+ --> $DIR/borrowck-move-out-from-array-no-overlap-match.rs:43:11
+ |
+LL | [_x, _, _] => {}
+ | -- value moved here
+LL | }
+LL | match a {
+ | ^ value used here after partial move
+ |
+ = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `a`
+ --> $DIR/borrowck-move-out-from-array-no-overlap-match.rs:54:11
+ |
+LL | [.., _x] => {}
+ | -- value moved here
+LL | }
+LL | match a {
+ | ^ value used here after partial move
+ |
+ = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `a`
+ --> $DIR/borrowck-move-out-from-array-no-overlap-match.rs:65:11
+ |
+LL | [(_x, _), _, _] => {}
+ | -- value moved here
+LL | }
+LL | match a {
+ | ^ value used here after partial move
+ |
+ = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `a`
+ --> $DIR/borrowck-move-out-from-array-no-overlap-match.rs:76:11
+ |
+LL | [.., (_x, _)] => {}
+ | -- value moved here
+LL | }
+LL | match a {
+ | ^ value used here after partial move
+ |
+ = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `a`
+ --> $DIR/borrowck-move-out-from-array-no-overlap-match.rs:87:11
+ |
+LL | [_, _y @ ..] => {}
+ | ------- value moved here
+LL | }
+LL | match a {
+ | ^ value used here after partial move
+ |
+ = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `a`
+ --> $DIR/borrowck-move-out-from-array-no-overlap-match.rs:98:11
+ |
+LL | [_y @ .., _] => {}
+ | ------- value moved here
+LL | }
+LL | match a {
+ | ^ value used here after partial move
+ |
+ = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `a`
+ --> $DIR/borrowck-move-out-from-array-no-overlap-match.rs:111:11
+ |
+LL | [x @ .., _, _] => {}
+ | ------ value moved here
+LL | }
+LL | match a {
+ | ^ value used here after partial move
+ |
+ = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+
+error: aborting due to 9 previous errors
+
+For more information about this error, try `rustc --explain E0382`.
--- /dev/null
+#![feature(slice_patterns)]
+
+fn array() -> [(String, String); 3] {
+ Default::default()
+}
+
+// Const Index + Const Index
+
+fn move_out_from_begin_and_end() {
+ let a = array();
+ match a {
+ [_, _, _x] => {}
+ }
+ match a {
+ [.., ref _y] => {} //~ ERROR [E0382]
+ }
+}
+
+fn move_out_from_begin_field_and_end() {
+ let a = array();
+ match a {
+ [_, _, (_x, _)] => {}
+ }
+ match a {
+ [.., ref _y] => {} //~ ERROR [E0382]
+ }
+}
+
+fn move_out_from_begin_field_and_end_field() {
+ let a = array();
+ match a {
+ [_, _, (_x, _)] => {}
+ }
+ match a {
+ [.., (ref _y, _)] => {} //~ ERROR [E0382]
+ }
+}
+
+// Const Index + Slice
+
+fn move_out_by_const_index_and_subslice() {
+ let a = array();
+ match a {
+ [_x, _, _] => {}
+ }
+ match a {
+ //~^ ERROR [E0382]
+ [ref _y @ .., _, _] => {}
+ }
+}
+
+fn move_out_by_const_index_end_and_subslice() {
+ let a = array();
+ match a {
+ [.., _x] => {}
+ }
+ match a {
+ //~^ ERROR [E0382]
+ [_, _, ref _y @ ..] => {}
+ }
+}
+
+fn move_out_by_const_index_field_and_subslice() {
+ let a = array();
+ match a {
+ [(_x, _), _, _] => {}
+ }
+ match a {
+ //~^ ERROR [E0382]
+ [ref _y @ .., _, _] => {}
+ }
+}
+
+fn move_out_by_const_index_end_field_and_subslice() {
+ let a = array();
+ match a {
+ [.., (_x, _)] => {}
+ }
+ match a {
+ //~^ ERROR [E0382]
+ [_, _, ref _y @ ..] => {}
+ }
+}
+
+fn move_out_by_subslice_and_const_index_field() {
+ let a = array();
+ match a {
+ [_y @ .., _, _] => {}
+ }
+ match a {
+ [(ref _x, _), _, _] => {} //~ ERROR [E0382]
+ }
+}
+
+fn move_out_by_subslice_and_const_index_end_field() {
+ let a = array();
+ match a {
+ [_, _, _y @ ..] => {}
+ }
+ match a {
+ [.., (ref _x, _)] => {} //~ ERROR [E0382]
+ }
+}
+
+// Slice + Slice
+
+fn move_out_by_subslice_and_subslice() {
+ let a = array();
+ match a {
+ [x @ .., _] => {}
+ }
+ match a {
+ //~^ ERROR [E0382]
+ [_, ref _y @ ..] => {}
+ }
+}
+
+// Move + Assign
+
+fn move_out_and_assign_end() {
+ let mut a = array();
+ match a {
+ [_, _, _x] => {}
+ }
+ a[2] = Default::default(); //~ ERROR [E0382]
+}
+
+fn move_out_and_assign_end_field() {
+ let mut a = array();
+ match a {
+ [_, _, (_x, _)] => {}
+ }
+ a[2].1 = Default::default(); //~ ERROR [E0382]
+}
+
+fn move_out_slice_and_assign_end() {
+ let mut a = array();
+ match a {
+ [_, _, _x @ ..] => {}
+ }
+ a[0] = Default::default(); //~ ERROR [E0382]
+}
+
+fn move_out_slice_and_assign_end_field() {
+ let mut a = array();
+ match a {
+ [_, _, _x @ ..] => {}
+ }
+ a[0].1 = Default::default(); //~ ERROR [E0382]
+}
+
+fn main() {}
--- /dev/null
+error[E0382]: borrow of moved value: `a[..]`
+ --> $DIR/borrowck-move-out-from-array-use-match.rs:15:14
+ |
+LL | [_, _, _x] => {}
+ | -- value moved here
+...
+LL | [.., ref _y] => {}
+ | ^^^^^^ value borrowed here after move
+ |
+ = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+
+error[E0382]: borrow of moved value: `a[..]`
+ --> $DIR/borrowck-move-out-from-array-use-match.rs:25:14
+ |
+LL | [_, _, (_x, _)] => {}
+ | -- value moved here
+...
+LL | [.., ref _y] => {}
+ | ^^^^^^ value borrowed here after partial move
+ |
+ = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
+
+error[E0382]: borrow of moved value: `a[..].0`
+ --> $DIR/borrowck-move-out-from-array-use-match.rs:35:15
+ |
+LL | [_, _, (_x, _)] => {}
+ | -- value moved here
+...
+LL | [.., (ref _y, _)] => {}
+ | ^^^^^^ value borrowed here after move
+ |
+ = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `a`
+ --> $DIR/borrowck-move-out-from-array-use-match.rs:46:11
+ |
+LL | [_x, _, _] => {}
+ | -- value moved here
+LL | }
+LL | match a {
+ | ^ value used here after partial move
+ |
+ = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `a`
+ --> $DIR/borrowck-move-out-from-array-use-match.rs:57:11
+ |
+LL | [.., _x] => {}
+ | -- value moved here
+LL | }
+LL | match a {
+ | ^ value used here after partial move
+ |
+ = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `a`
+ --> $DIR/borrowck-move-out-from-array-use-match.rs:68:11
+ |
+LL | [(_x, _), _, _] => {}
+ | -- value moved here
+LL | }
+LL | match a {
+ | ^ value used here after partial move
+ |
+ = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `a`
+ --> $DIR/borrowck-move-out-from-array-use-match.rs:79:11
+ |
+LL | [.., (_x, _)] => {}
+ | -- value moved here
+LL | }
+LL | match a {
+ | ^ value used here after partial move
+ |
+ = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
+
+error[E0382]: borrow of moved value: `a[..]`
+ --> $DIR/borrowck-move-out-from-array-use-match.rs:91:11
+ |
+LL | [_y @ .., _, _] => {}
+ | ------- value moved here
+...
+LL | [(ref _x, _), _, _] => {}
+ | ^^^^^^ value borrowed here after move
+ |
+ = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+
+error[E0382]: borrow of moved value: `a[..]`
+ --> $DIR/borrowck-move-out-from-array-use-match.rs:101:15
+ |
+LL | [_, _, _y @ ..] => {}
+ | ------- value moved here
+...
+LL | [.., (ref _x, _)] => {}
+ | ^^^^^^ value borrowed here after move
+ |
+ = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `a`
+ --> $DIR/borrowck-move-out-from-array-use-match.rs:112:11
+ |
+LL | [x @ .., _] => {}
+ | ------ value moved here
+LL | }
+LL | match a {
+ | ^ value used here after partial move
+ |
+ = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `a`
+ --> $DIR/borrowck-move-out-from-array-use-match.rs:125:5
+ |
+LL | [_, _, _x] => {}
+ | -- value moved here
+LL | }
+LL | a[2] = Default::default();
+ | ^^^^ value used here after partial move
+ |
+ = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `a`
+ --> $DIR/borrowck-move-out-from-array-use-match.rs:133:5
+ |
+LL | [_, _, (_x, _)] => {}
+ | -- value moved here
+LL | }
+LL | a[2].1 = Default::default();
+ | ^^^^ value used here after partial move
+ |
+ = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `a`
+ --> $DIR/borrowck-move-out-from-array-use-match.rs:141:5
+ |
+LL | [_, _, _x @ ..] => {}
+ | ------- value moved here
+LL | }
+LL | a[0] = Default::default();
+ | ^^^^ value used here after partial move
+ |
+ = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `a`
+ --> $DIR/borrowck-move-out-from-array-use-match.rs:149:5
+ |
+LL | [_, _, _x @ ..] => {}
+ | ------- value moved here
+LL | }
+LL | a[0].1 = Default::default();
+ | ^^^^ value used here after partial move
+ |
+ = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+
+error: aborting due to 14 previous errors
+
+For more information about this error, try `rustc --explain E0382`.
--- /dev/null
+// Due to #53114, which causes a "read" of the `_` patterns,
+// the borrow-checker refuses this code, while it should probably be allowed.
+// Once the bug is fixed, the test, which is derived from a
+// passing test for `let` statements, should become check-pass.
+
+#![feature(slice_patterns)]
+
+fn array() -> [(String, String); 3] {
+ Default::default()
+}
+
+// Const Index + Const Index
+
+fn move_out_from_begin_and_one_from_end() {
+ let a = array();
+ match a {
+ [_, _, _x] => {}
+ }
+ match a {
+ //~^ ERROR use of moved value
+ [.., ref _y, _] => {}
+ }
+}
+
+fn move_out_from_begin_field_and_end_field() {
+ let a = array();
+ match a {
+ [_, _, (_x, _)] => {}
+ }
+ match a {
+ //~^ ERROR use of moved value
+ [.., (_, ref _y)] => {}
+ }
+}
+
+// Const Index + Slice
+
+fn move_out_by_const_index_and_subslice() {
+ let a = array();
+ match a {
+ [_x, _, _] => {}
+ }
+ match a {
+ //~^ ERROR use of moved value
+ [_, ref _y @ ..] => {}
+ }
+}
+
+fn move_out_by_const_index_end_and_subslice() {
+ let a = array();
+ match a {
+ [.., _x] => {}
+ }
+ match a {
+ //~^ ERROR use of moved value
+ [ref _y @ .., _] => {}
+ }
+}
+
+fn move_out_by_const_index_field_and_subslice() {
+ let a = array();
+ match a {
+ [(_x, _), _, _] => {}
+ }
+ match a {
+ //~^ ERROR use of moved value
+ [_, ref _y @ ..] => {}
+ }
+}
+
+fn move_out_by_const_index_end_field_and_subslice() {
+ let a = array();
+ match a {
+ [.., (_x, _)] => {}
+ }
+ match a {
+ //~^ ERROR use of moved value
+ [ref _y @ .., _] => {}
+ }
+}
+
+fn move_out_by_const_subslice_and_index_field() {
+ let a = array();
+ match a {
+ [_, _y @ ..] => {}
+ }
+ match a {
+ //~^ ERROR use of moved value
+ [(ref _x, _), _, _] => {}
+ }
+}
+
+fn move_out_by_const_subslice_and_end_index_field() {
+ let a = array();
+ match a {
+ [_y @ .., _] => {}
+ }
+ match a {
+ //~^ ERROR use of moved value
+ [.., (ref _x, _)] => {}
+ }
+}
+
+// Slice + Slice
+
+fn move_out_by_subslice_and_subslice() {
+ let a = array();
+ match a {
+ [x @ .., _, _] => {}
+ }
+ match a {
+ //~^ ERROR use of moved value
+ [_, ref _y @ ..] => {}
+ }
+}
+
+fn main() {}
--- /dev/null
+error[E0382]: use of moved value: `a`
+ --> $DIR/borrowck-move-out-from-array-use-no-overlap-match.rs:19:11
+ |
+LL | [_, _, _x] => {}
+ | -- value moved here
+LL | }
+LL | match a {
+ | ^ value used here after partial move
+ |
+ = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `a`
+ --> $DIR/borrowck-move-out-from-array-use-no-overlap-match.rs:30:11
+ |
+LL | [_, _, (_x, _)] => {}
+ | -- value moved here
+LL | }
+LL | match a {
+ | ^ value used here after partial move
+ |
+ = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `a`
+ --> $DIR/borrowck-move-out-from-array-use-no-overlap-match.rs:43:11
+ |
+LL | [_x, _, _] => {}
+ | -- value moved here
+LL | }
+LL | match a {
+ | ^ value used here after partial move
+ |
+ = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `a`
+ --> $DIR/borrowck-move-out-from-array-use-no-overlap-match.rs:54:11
+ |
+LL | [.., _x] => {}
+ | -- value moved here
+LL | }
+LL | match a {
+ | ^ value used here after partial move
+ |
+ = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `a`
+ --> $DIR/borrowck-move-out-from-array-use-no-overlap-match.rs:65:11
+ |
+LL | [(_x, _), _, _] => {}
+ | -- value moved here
+LL | }
+LL | match a {
+ | ^ value used here after partial move
+ |
+ = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `a`
+ --> $DIR/borrowck-move-out-from-array-use-no-overlap-match.rs:76:11
+ |
+LL | [.., (_x, _)] => {}
+ | -- value moved here
+LL | }
+LL | match a {
+ | ^ value used here after partial move
+ |
+ = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `a`
+ --> $DIR/borrowck-move-out-from-array-use-no-overlap-match.rs:87:11
+ |
+LL | [_, _y @ ..] => {}
+ | ------- value moved here
+LL | }
+LL | match a {
+ | ^ value used here after partial move
+ |
+ = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `a`
+ --> $DIR/borrowck-move-out-from-array-use-no-overlap-match.rs:98:11
+ |
+LL | [_y @ .., _] => {}
+ | ------- value moved here
+LL | }
+LL | match a {
+ | ^ value used here after partial move
+ |
+ = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `a`
+ --> $DIR/borrowck-move-out-from-array-use-no-overlap-match.rs:111:11
+ |
+LL | [x @ .., _, _] => {}
+ | ------ value moved here
+LL | }
+LL | match a {
+ | ^ value used here after partial move
+ |
+ = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+
+error: aborting due to 9 previous errors
+
+For more information about this error, try `rustc --explain E0382`.
fn wrong_bound1<'b,'c,'d:'a+'c>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>) {
//~^ ERROR method not compatible with trait
+ //~| ERROR method not compatible with trait
//
// Note: This is a terrible error message. It is caused
// because, in the trait, 'b is early bound, and in the impl,
LL | fn wrong_bound1<'b,'c,'d:'a+'c>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>) {
| ^^
+error[E0308]: method not compatible with trait
+ --> $DIR/regions-bound-missing-bound-in-impl.rs:27:5
+ |
+LL | fn wrong_bound1<'b,'c,'d:'a+'c>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime mismatch
+ |
+ = note: expected fn pointer `fn(&'a isize, Inv<'c>, Inv<'c>, Inv<'_>)`
+ found fn pointer `fn(&'a isize, Inv<'_>, Inv<'c>, Inv<'_>)`
+note: the lifetime `'c` as defined on the method body at 27:24...
+ --> $DIR/regions-bound-missing-bound-in-impl.rs:27:24
+ |
+LL | fn wrong_bound1<'b,'c,'d:'a+'c>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>) {
+ | ^^
+note: ...does not necessarily outlive the lifetime `'c` as defined on the method body at 27:24
+ --> $DIR/regions-bound-missing-bound-in-impl.rs:27:24
+ |
+LL | fn wrong_bound1<'b,'c,'d:'a+'c>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>) {
+ | ^^
+
error[E0195]: lifetime parameters or bounds on method `wrong_bound2` do not match the trait declaration
- --> $DIR/regions-bound-missing-bound-in-impl.rs:41:20
+ --> $DIR/regions-bound-missing-bound-in-impl.rs:42:20
|
LL | fn wrong_bound2<'b,'c,'d:'a+'b>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>);
| ---------------- lifetimes in impl do not match this method in trait
| ^ lifetimes do not match method in trait
error[E0276]: impl has stricter requirements than trait
- --> $DIR/regions-bound-missing-bound-in-impl.rs:48:5
+ --> $DIR/regions-bound-missing-bound-in-impl.rs:49:5
|
LL | fn another_bound<'x: 'a>(self, x: Inv<'x>, y: Inv<'t>);
| ------------------------------------------------------- definition of `another_bound` from trait
LL | fn another_bound<'x: 't>(self, x: Inv<'x>, y: Inv<'t>) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `'x: 't`
-error: aborting due to 5 previous errors
+error: aborting due to 6 previous errors
Some errors have detailed explanations: E0195, E0276, E0308.
For more information about an error, try `rustc --explain E0195`.
-error[E0599]: no method named `eat` found for type `std::boxed::Box<dyn Noisy>` in the current scope
+error[E0599]: no method named `eat` found for struct `std::boxed::Box<dyn Noisy>` in the current scope
--> $DIR/class-cast-to-trait.rs:53:8
|
LL | nyan.eat();
error[E0308]: mismatched types
--> $DIR/closure-reform-bad.rs:11:15
|
+LL | let f = |s: &str| println!("{}{}", s, string);
+ | ------------------------------------- the found closure
LL | call_bare(f)
| ^ expected fn pointer, found closure
|
-error[E0599]: no method named `the_fn` found for type `&Lib::TheStruct` in the current scope
+error[E0599]: no method named `the_fn` found for reference `&Lib::TheStruct` in the current scope
--> $DIR/coherence_inherent.rs:31:11
|
LL | s.the_fn();
-error[E0599]: no method named `the_fn` found for type `&coherence_inherent_cc_lib::TheStruct` in the current scope
+error[E0599]: no method named `the_fn` found for reference `&coherence_inherent_cc_lib::TheStruct` in the current scope
--> $DIR/coherence_inherent_cc.rs:23:11
|
LL | s.the_fn();
#![allow(dead_code)]
#[repr(C)]
-enum A { A }
+enum A {
+ A,
+}
#[repr(u64)]
-enum B { B }
+enum B {
+ B,
+}
-#[repr(C, u64)] //~ WARNING conflicting representation hints
-enum C { C }
+#[repr(C, u64)] //~ ERROR conflicting representation hints
+enum C {
+ C,
+}
-#[repr(u32, u64)] //~ WARNING conflicting representation hints
-enum D { D }
+#[repr(u32, u64)] //~ ERROR conflicting representation hints
+enum D {
+ D,
+}
#[repr(C, packed)]
struct E(i32);
struct K(i32);
#[repr(packed, align(8))]
-union X { //~ ERROR type has conflicting packed and align representation hints
- i: i32
+union X {
+ //~^ ERROR type has conflicting packed and align representation hints
+ i: i32,
}
#[repr(packed)]
#[repr(align(8))]
-union Y { //~ ERROR type has conflicting packed and align representation hints
- i: i32
+union Y {
+ //~^ ERROR type has conflicting packed and align representation hints
+ i: i32,
}
#[repr(align(8))]
#[repr(packed)]
-union Z { //~ ERROR type has conflicting packed and align representation hints
- i: i32
+union Z {
+ //~^ ERROR type has conflicting packed and align representation hints
+ i: i32,
}
fn main() {}
-warning[E0566]: conflicting representation hints
- --> $DIR/conflicting-repr-hints.rs:9:8
+error[E0566]: conflicting representation hints
+ --> $DIR/conflicting-repr-hints.rs:13:8
|
LL | #[repr(C, u64)]
| ^ ^^^
-warning[E0566]: conflicting representation hints
- --> $DIR/conflicting-repr-hints.rs:12:8
+error[E0566]: conflicting representation hints
+ --> $DIR/conflicting-repr-hints.rs:18:8
|
LL | #[repr(u32, u64)]
| ^^^ ^^^
error[E0587]: type has conflicting packed and align representation hints
- --> $DIR/conflicting-repr-hints.rs:19:1
+ --> $DIR/conflicting-repr-hints.rs:27:1
|
LL | struct F(i32);
| ^^^^^^^^^^^^^^
error[E0587]: type has conflicting packed and align representation hints
- --> $DIR/conflicting-repr-hints.rs:23:1
+ --> $DIR/conflicting-repr-hints.rs:31:1
|
LL | struct G(i32);
| ^^^^^^^^^^^^^^
error[E0587]: type has conflicting packed and align representation hints
- --> $DIR/conflicting-repr-hints.rs:27:1
+ --> $DIR/conflicting-repr-hints.rs:35:1
|
LL | struct H(i32);
| ^^^^^^^^^^^^^^
error[E0634]: type has conflicting packed representation hints
- --> $DIR/conflicting-repr-hints.rs:30:1
+ --> $DIR/conflicting-repr-hints.rs:38:1
|
LL | struct I(i32);
| ^^^^^^^^^^^^^^
error[E0634]: type has conflicting packed representation hints
- --> $DIR/conflicting-repr-hints.rs:34:1
+ --> $DIR/conflicting-repr-hints.rs:42:1
|
LL | struct J(i32);
| ^^^^^^^^^^^^^^
error[E0587]: type has conflicting packed and align representation hints
- --> $DIR/conflicting-repr-hints.rs:40:1
+ --> $DIR/conflicting-repr-hints.rs:48:1
|
LL | / union X {
-LL | | i: i32
+LL | |
+LL | | i: i32,
LL | | }
| |_^
error[E0587]: type has conflicting packed and align representation hints
- --> $DIR/conflicting-repr-hints.rs:46:1
+ --> $DIR/conflicting-repr-hints.rs:55:1
|
LL | / union Y {
-LL | | i: i32
+LL | |
+LL | | i: i32,
LL | | }
| |_^
error[E0587]: type has conflicting packed and align representation hints
- --> $DIR/conflicting-repr-hints.rs:52:1
+ --> $DIR/conflicting-repr-hints.rs:62:1
|
LL | / union Z {
-LL | | i: i32
+LL | |
+LL | | i: i32,
LL | | }
| |_^
-error: aborting due to 8 previous errors
+error: aborting due to 10 previous errors
Some errors have detailed explanations: E0566, E0587.
For more information about an error, try `rustc --explain E0566`.
-error[E0599]: no method named `closure` found for type `Obj<[closure@$DIR/issue-18343.rs:6:28: 6:33]>` in the current scope
+error[E0599]: no method named `closure` found for struct `Obj<[closure@$DIR/issue-18343.rs:6:28: 6:33]>` in the current scope
--> $DIR/issue-18343.rs:7:7
|
LL | struct Obj<F> where F: FnMut() -> u32 {
-error[E0599]: no method named `closure` found for type `Obj<[closure@$DIR/issue-2392.rs:35:36: 35:41]>` in the current scope
+error[E0599]: no method named `closure` found for struct `Obj<[closure@$DIR/issue-2392.rs:35:36: 35:41]>` in the current scope
--> $DIR/issue-2392.rs:36:15
|
LL | struct Obj<F> where F: FnOnce() -> u32 {
LL | (o_closure.closure)();
| ^ ^
-error[E0599]: no method named `not_closure` found for type `Obj<[closure@$DIR/issue-2392.rs:35:36: 35:41]>` in the current scope
+error[E0599]: no method named `not_closure` found for struct `Obj<[closure@$DIR/issue-2392.rs:35:36: 35:41]>` in the current scope
--> $DIR/issue-2392.rs:38:15
|
LL | struct Obj<F> where F: FnOnce() -> u32 {
| |
| field, not a method
-error[E0599]: no method named `closure` found for type `Obj<fn() -> u32 {func}>` in the current scope
+error[E0599]: no method named `closure` found for struct `Obj<fn() -> u32 {func}>` in the current scope
--> $DIR/issue-2392.rs:42:12
|
LL | struct Obj<F> where F: FnOnce() -> u32 {
LL | (o_func.closure)();
| ^ ^
-error[E0599]: no method named `boxed_closure` found for type `BoxedObj` in the current scope
+error[E0599]: no method named `boxed_closure` found for struct `BoxedObj` in the current scope
--> $DIR/issue-2392.rs:45:14
|
LL | struct BoxedObj {
LL | (boxed_fn.boxed_closure)();
| ^ ^
-error[E0599]: no method named `boxed_closure` found for type `BoxedObj` in the current scope
+error[E0599]: no method named `boxed_closure` found for struct `BoxedObj` in the current scope
--> $DIR/issue-2392.rs:48:19
|
LL | struct BoxedObj {
LL | (boxed_closure.boxed_closure)();
| ^ ^
-error[E0599]: no method named `closure` found for type `Obj<fn() -> u32 {func}>` in the current scope
+error[E0599]: no method named `closure` found for struct `Obj<fn() -> u32 {func}>` in the current scope
--> $DIR/issue-2392.rs:53:12
|
LL | struct Obj<F> where F: FnOnce() -> u32 {
LL | (w.wrap.closure)();
| ^ ^
-error[E0599]: no method named `not_closure` found for type `Obj<fn() -> u32 {func}>` in the current scope
+error[E0599]: no method named `not_closure` found for struct `Obj<fn() -> u32 {func}>` in the current scope
--> $DIR/issue-2392.rs:55:12
|
LL | struct Obj<F> where F: FnOnce() -> u32 {
| |
| field, not a method
-error[E0599]: no method named `closure` found for type `Obj<std::boxed::Box<(dyn std::ops::FnOnce() -> u32 + 'static)>>` in the current scope
+error[E0599]: no method named `closure` found for struct `Obj<std::boxed::Box<(dyn std::ops::FnOnce() -> u32 + 'static)>>` in the current scope
--> $DIR/issue-2392.rs:58:24
|
LL | struct Obj<F> where F: FnOnce() -> u32 {
LL | (check_expression().closure)();
| ^ ^
-error[E0599]: no method named `f1` found for type `FuncContainer` in the current scope
+error[E0599]: no method named `f1` found for struct `FuncContainer` in the current scope
--> $DIR/issue-2392.rs:64:31
|
LL | struct FuncContainer {
LL | ((*self.container).f1)(1);
| ^ ^
-error[E0599]: no method named `f2` found for type `FuncContainer` in the current scope
+error[E0599]: no method named `f2` found for struct `FuncContainer` in the current scope
--> $DIR/issue-2392.rs:65:31
|
LL | struct FuncContainer {
LL | ((*self.container).f2)(1);
| ^ ^
-error[E0599]: no method named `f3` found for type `FuncContainer` in the current scope
+error[E0599]: no method named `f3` found for struct `FuncContainer` in the current scope
--> $DIR/issue-2392.rs:66:31
|
LL | struct FuncContainer {
-error[E0599]: no method named `example` found for type `Example` in the current scope
+error[E0599]: no method named `example` found for struct `Example` in the current scope
--> $DIR/issue-32128.rs:12:10
|
LL | struct Example {
-error[E0599]: no method named `closure` found for type `&Obj<[closure@$DIR/issue-33784.rs:25:43: 25:48]>` in the current scope
+error[E0599]: no method named `closure` found for reference `&Obj<[closure@$DIR/issue-33784.rs:25:43: 25:48]>` in the current scope
--> $DIR/issue-33784.rs:27:7
|
LL | p.closure();
LL | (p.closure)();
| ^ ^
-error[E0599]: no method named `fn_ptr` found for type `&&Obj<[closure@$DIR/issue-33784.rs:25:43: 25:48]>` in the current scope
+error[E0599]: no method named `fn_ptr` found for reference `&&Obj<[closure@$DIR/issue-33784.rs:25:43: 25:48]>` in the current scope
--> $DIR/issue-33784.rs:29:7
|
LL | q.fn_ptr();
LL | (q.fn_ptr)();
| ^ ^
-error[E0599]: no method named `c_fn_ptr` found for type `&D` in the current scope
+error[E0599]: no method named `c_fn_ptr` found for reference `&D` in the current scope
--> $DIR/issue-33784.rs:32:7
|
LL | s.c_fn_ptr();
-error[E0599]: no method named `dog_age` found for type `animal::Dog` in the current scope
+error[E0599]: no method named `dog_age` found for struct `animal::Dog` in the current scope
--> $DIR/private-field.rs:16:23
|
LL | pub struct Dog {
LL | let _: u32 = 5i32.try_into::<32>().unwrap();
| ^^ unexpected const argument
-error[E0599]: no method named `f` found for type `S` in the current scope
+error[E0599]: no method named `f` found for struct `S` in the current scope
--> $DIR/invalid-const-arg-for-type-param.rs:7:7
|
LL | struct S;
-// build-fail
+// build-pass
+
+#![warn(const_err)]
fn main() {
- &{[1, 2, 3][4]};
- //~^ ERROR index out of bounds
- //~| ERROR reaching this expression at runtime will panic or abort
+ &{ [1, 2, 3][4] };
+ //~^ WARN index out of bounds
+ //~| WARN reaching this expression at runtime will panic or abort
+ //~| WARN erroneous constant used [const_err]
}
-error: index out of bounds: the len is 3 but the index is 4
- --> $DIR/array-literal-index-oob.rs:4:7
+warning: index out of bounds: the len is 3 but the index is 4
+ --> $DIR/array-literal-index-oob.rs:6:8
|
-LL | &{[1, 2, 3][4]};
- | ^^^^^^^^^^^^
+LL | &{ [1, 2, 3][4] };
+ | ^^^^^^^^^^^^
|
- = note: `#[deny(const_err)]` on by default
+note: lint level defined here
+ --> $DIR/array-literal-index-oob.rs:3:9
+ |
+LL | #![warn(const_err)]
+ | ^^^^^^^^^
-error: reaching this expression at runtime will panic or abort
- --> $DIR/array-literal-index-oob.rs:4:7
+warning: reaching this expression at runtime will panic or abort
+ --> $DIR/array-literal-index-oob.rs:6:8
|
-LL | &{[1, 2, 3][4]};
- | --^^^^^^^^^^^^-
- | |
- | indexing out of bounds: the len is 3 but the index is 4
+LL | &{ [1, 2, 3][4] };
+ | ---^^^^^^^^^^^^--
+ | |
+ | indexing out of bounds: the len is 3 but the index is 4
-error: aborting due to 2 previous errors
+warning: erroneous constant used
+ --> $DIR/array-literal-index-oob.rs:6:5
+ |
+LL | &{ [1, 2, 3][4] };
+ | ^^^^^^^^^^^^^^^^^ referenced constant has errors
fn main() {
println!("{}", FOO);
//~^ ERROR
+ //~| WARN erroneous constant used [const_err]
}
LL | println!("{}", FOO);
| ^^^ referenced constant has errors
+warning: erroneous constant used
+ --> $DIR/conditional_array_execution.rs:11:20
+ |
+LL | println!("{}", FOO);
+ | ^^^ referenced constant has errors
+
error: aborting due to previous error
For more information about this error, try `rustc --explain E0080`.
match -128i8 {
NEG_NEG_128 => println!("A"),
//~^ ERROR could not evaluate constant pattern
+ //~| ERROR could not evaluate constant pattern
_ => println!("B"),
}
}
LL | NEG_NEG_128 => println!("A"),
| ^^^^^^^^^^^
-error: aborting due to previous error
+error: could not evaluate constant pattern
+ --> $DIR/const-eval-overflow-2.rs:15:9
+ |
+LL | NEG_NEG_128 => println!("A"),
+ | ^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
#![feature(const_fn)]
#![allow(const_err)]
-fn double(x: usize) -> usize { x * 2 }
+fn double(x: usize) -> usize {
+ x * 2
+}
const X: fn(usize) -> usize = double;
const fn bar(x: fn(usize) -> usize, y: usize) -> usize {
warning: skipping const checks
- --> $DIR/const_fn_ptr_fail2.rs:11:5
+ --> $DIR/const_fn_ptr_fail2.rs:13:5
|
LL | x(y)
| ^^^^
error[E0080]: evaluation of constant expression failed
- --> $DIR/const_fn_ptr_fail2.rs:18:5
+ --> $DIR/const_fn_ptr_fail2.rs:20:5
|
LL | assert_eq!(Y, 4);
| ^^^^^^^^^^^-^^^^^
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error[E0080]: evaluation of constant expression failed
- --> $DIR/const_fn_ptr_fail2.rs:20:5
+ --> $DIR/const_fn_ptr_fail2.rs:22:5
|
LL | assert_eq!(Z, 4);
| ^^^^^^^^^^^-^^^^^
}
fn main() {
- const X: u32 = 0-1;
+ const X: u32 = 0 - 1;
//~^ WARN any use of this value will cause
- const Y: u32 = foo(0-1);
+ const Y: u32 = foo(0 - 1);
//~^ WARN any use of this value will cause
println!("{} {}", X, Y);
//~^ ERROR evaluation of constant expression failed
//~| ERROR evaluation of constant expression failed
+ //~| WARN erroneous constant used [const_err]
+ //~| WARN erroneous constant used [const_err]
}
warning: any use of this value will cause an error
--> $DIR/issue-43197.rs:10:20
|
-LL | const X: u32 = 0-1;
- | ---------------^^^-
+LL | const X: u32 = 0 - 1;
+ | ---------------^^^^^-
| |
| attempt to subtract with overflow
|
warning: any use of this value will cause an error
--> $DIR/issue-43197.rs:12:24
|
-LL | const Y: u32 = foo(0-1);
- | -------------------^^^--
+LL | const Y: u32 = foo(0 - 1);
+ | -------------------^^^^^--
| |
| attempt to subtract with overflow
LL | println!("{} {}", X, Y);
| ^ referenced constant has errors
+warning: erroneous constant used
+ --> $DIR/issue-43197.rs:14:23
+ |
+LL | println!("{} {}", X, Y);
+ | ^ referenced constant has errors
+
error[E0080]: evaluation of constant expression failed
--> $DIR/issue-43197.rs:14:26
|
LL | println!("{} {}", X, Y);
| ^ referenced constant has errors
+warning: erroneous constant used
+ --> $DIR/issue-43197.rs:14:26
+ |
+LL | println!("{} {}", X, Y);
+ | ^ referenced constant has errors
+
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0080`.
fn main() {
println!("{}", <Bar<u16, u8> as Foo>::AMT);
- //~^ ERROR E0080
+ //~^ ERROR evaluation of constant expression failed [E0080]
}
struct Sum<A,B>(A,B);
impl<A: Unsigned, B: Unsigned> Unsigned for Sum<A,B> {
- const MAX: u8 = A::MAX + B::MAX; //~ ERROR any use of this value will cause an error
+ const MAX: u8 = A::MAX + B::MAX;
+ //~^ ERROR any use of this value will cause an error [const_err]
}
fn foo<T>(_: T) -> &'static u8 {
- &Sum::<U8,U8>::MAX //~ ERROR E0080
+ &Sum::<U8,U8>::MAX
+ //~^ ERROR E0080
}
fn main() {
= note: `#[deny(const_err)]` on by default
error[E0080]: evaluation of constant expression failed
- --> $DIR/issue-50814.rs:19:5
+ --> $DIR/issue-50814.rs:20:5
|
LL | &Sum::<U8,U8>::MAX
| ^-----------------
-// build-fail
+// build-pass
// compile-flags: -O
-#![deny(const_err)]
+#![warn(const_err)]
fn main() {
println!("{}", 0u32 - 1);
let _x = 0u32 - 1;
- //~^ ERROR const_err
- println!("{}", 1/(1-1));
- //~^ ERROR attempt to divide by zero [const_err]
- //~| ERROR const_err
- let _x = 1/(1-1);
- //~^ ERROR const_err
- println!("{}", 1/(false as u32));
- //~^ ERROR attempt to divide by zero [const_err]
- //~| ERROR const_err
- let _x = 1/(false as u32);
- //~^ ERROR const_err
+ //~^ WARN const_err
+ println!("{}", 1 / (1 - 1));
+ //~^ WARN attempt to divide by zero [const_err]
+ //~| WARN const_err
+ //~| WARN erroneous constant used [const_err]
+ let _x = 1 / (1 - 1);
+ //~^ WARN const_err
+ println!("{}", 1 / (false as u32));
+ //~^ WARN attempt to divide by zero [const_err]
+ //~| WARN const_err
+ //~| WARN erroneous constant used [const_err]
+ let _x = 1 / (false as u32);
+ //~^ WARN const_err
}
-error: this expression will panic at runtime
+warning: this expression will panic at runtime
--> $DIR/promoted_errors.rs:8:14
|
LL | let _x = 0u32 - 1;
note: lint level defined here
--> $DIR/promoted_errors.rs:4:9
|
-LL | #![deny(const_err)]
+LL | #![warn(const_err)]
| ^^^^^^^^^
-error: attempt to divide by zero
+warning: attempt to divide by zero
--> $DIR/promoted_errors.rs:10:20
|
-LL | println!("{}", 1/(1-1));
- | ^^^^^^^
+LL | println!("{}", 1 / (1 - 1));
+ | ^^^^^^^^^^^
-error: reaching this expression at runtime will panic or abort
+warning: reaching this expression at runtime will panic or abort
--> $DIR/promoted_errors.rs:10:20
|
-LL | println!("{}", 1/(1-1));
- | ^^^^^^^ dividing by zero
+LL | println!("{}", 1 / (1 - 1));
+ | ^^^^^^^^^^^ dividing by zero
-error: attempt to divide by zero
- --> $DIR/promoted_errors.rs:13:14
+warning: erroneous constant used
+ --> $DIR/promoted_errors.rs:10:20
+ |
+LL | println!("{}", 1 / (1 - 1));
+ | ^^^^^^^^^^^ referenced constant has errors
+
+warning: attempt to divide by zero
+ --> $DIR/promoted_errors.rs:14:14
|
-LL | let _x = 1/(1-1);
- | ^^^^^^^
+LL | let _x = 1 / (1 - 1);
+ | ^^^^^^^^^^^
-error: attempt to divide by zero
- --> $DIR/promoted_errors.rs:15:20
+warning: attempt to divide by zero
+ --> $DIR/promoted_errors.rs:16:20
|
-LL | println!("{}", 1/(false as u32));
- | ^^^^^^^^^^^^^^^^
+LL | println!("{}", 1 / (false as u32));
+ | ^^^^^^^^^^^^^^^^^^
-error: reaching this expression at runtime will panic or abort
- --> $DIR/promoted_errors.rs:15:20
+warning: reaching this expression at runtime will panic or abort
+ --> $DIR/promoted_errors.rs:16:20
|
-LL | println!("{}", 1/(false as u32));
- | ^^^^^^^^^^^^^^^^ dividing by zero
+LL | println!("{}", 1 / (false as u32));
+ | ^^^^^^^^^^^^^^^^^^ dividing by zero
-error: attempt to divide by zero
- --> $DIR/promoted_errors.rs:18:14
+warning: erroneous constant used
+ --> $DIR/promoted_errors.rs:16:20
|
-LL | let _x = 1/(false as u32);
- | ^^^^^^^^^^^^^^^^
+LL | println!("{}", 1 / (false as u32));
+ | ^^^^^^^^^^^^^^^^^^ referenced constant has errors
-error: aborting due to 7 previous errors
+warning: attempt to divide by zero
+ --> $DIR/promoted_errors.rs:20:14
+ |
+LL | let _x = 1 / (false as u32);
+ | ^^^^^^^^^^^^^^^^^^
-// build-fail
+// build-pass
// compile-flags: -C overflow-checks=on -O
-#![deny(const_err)]
+#![warn(const_err)]
fn main() {
println!("{}", 0u32 - 1);
- //~^ ERROR attempt to subtract with overflow
+ //~^ WARN attempt to subtract with overflow
let _x = 0u32 - 1;
- //~^ ERROR attempt to subtract with overflow
- println!("{}", 1/(1-1));
- //~^ ERROR attempt to divide by zero [const_err]
- //~| ERROR const_err
- let _x = 1/(1-1);
- //~^ ERROR const_err
- println!("{}", 1/(false as u32));
- //~^ ERROR attempt to divide by zero [const_err]
- //~| ERROR const_err
- let _x = 1/(false as u32);
- //~^ ERROR const_err
+ //~^ WARN attempt to subtract with overflow
+ println!("{}", 1 / (1 - 1));
+ //~^ WARN attempt to divide by zero [const_err]
+ //~| WARN const_err
+ //~| WARN erroneous constant used [const_err]
+ let _x = 1 / (1 - 1);
+ //~^ WARN const_err
+ println!("{}", 1 / (false as u32));
+ //~^ WARN attempt to divide by zero [const_err]
+ //~| WARN const_err
+ //~| WARN erroneous constant used [const_err]
+ let _x = 1 / (false as u32);
+ //~^ WARN const_err
}
-error: attempt to subtract with overflow
+warning: attempt to subtract with overflow
--> $DIR/promoted_errors2.rs:7:20
|
LL | println!("{}", 0u32 - 1);
note: lint level defined here
--> $DIR/promoted_errors2.rs:4:9
|
-LL | #![deny(const_err)]
+LL | #![warn(const_err)]
| ^^^^^^^^^
-error: attempt to subtract with overflow
+warning: attempt to subtract with overflow
--> $DIR/promoted_errors2.rs:9:14
|
LL | let _x = 0u32 - 1;
| ^^^^^^^^
-error: attempt to divide by zero
+warning: attempt to divide by zero
--> $DIR/promoted_errors2.rs:11:20
|
-LL | println!("{}", 1/(1-1));
- | ^^^^^^^
+LL | println!("{}", 1 / (1 - 1));
+ | ^^^^^^^^^^^
-error: reaching this expression at runtime will panic or abort
+warning: reaching this expression at runtime will panic or abort
--> $DIR/promoted_errors2.rs:11:20
|
-LL | println!("{}", 1/(1-1));
- | ^^^^^^^ dividing by zero
+LL | println!("{}", 1 / (1 - 1));
+ | ^^^^^^^^^^^ dividing by zero
-error: attempt to divide by zero
- --> $DIR/promoted_errors2.rs:14:14
+warning: erroneous constant used
+ --> $DIR/promoted_errors2.rs:11:20
+ |
+LL | println!("{}", 1 / (1 - 1));
+ | ^^^^^^^^^^^ referenced constant has errors
+
+warning: attempt to divide by zero
+ --> $DIR/promoted_errors2.rs:15:14
|
-LL | let _x = 1/(1-1);
- | ^^^^^^^
+LL | let _x = 1 / (1 - 1);
+ | ^^^^^^^^^^^
-error: attempt to divide by zero
- --> $DIR/promoted_errors2.rs:16:20
+warning: attempt to divide by zero
+ --> $DIR/promoted_errors2.rs:17:20
|
-LL | println!("{}", 1/(false as u32));
- | ^^^^^^^^^^^^^^^^
+LL | println!("{}", 1 / (false as u32));
+ | ^^^^^^^^^^^^^^^^^^
-error: reaching this expression at runtime will panic or abort
- --> $DIR/promoted_errors2.rs:16:20
+warning: reaching this expression at runtime will panic or abort
+ --> $DIR/promoted_errors2.rs:17:20
|
-LL | println!("{}", 1/(false as u32));
- | ^^^^^^^^^^^^^^^^ dividing by zero
+LL | println!("{}", 1 / (false as u32));
+ | ^^^^^^^^^^^^^^^^^^ dividing by zero
-error: attempt to divide by zero
- --> $DIR/promoted_errors2.rs:19:14
+warning: erroneous constant used
+ --> $DIR/promoted_errors2.rs:17:20
|
-LL | let _x = 1/(false as u32);
- | ^^^^^^^^^^^^^^^^
+LL | println!("{}", 1 / (false as u32));
+ | ^^^^^^^^^^^^^^^^^^ referenced constant has errors
-error: aborting due to 8 previous errors
+warning: attempt to divide by zero
+ --> $DIR/promoted_errors2.rs:21:14
+ |
+LL | let _x = 1 / (false as u32);
+ | ^^^^^^^^^^^^^^^^^^
match n {
0..=10 => {},
10..=BAR => {}, //~ ERROR could not evaluate constant pattern
+ //~| ERROR could not evaluate constant pattern
_ => {},
}
}
error[E0080]: it is undefined behavior to use this value
- --> $DIR/ref_to_int_match.rs:24:1
+ --> $DIR/ref_to_int_match.rs:25:1
|
LL | const BAR: Int = unsafe { Foo { r: &42 }.f };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected initialized plain (non-pointer) bytes
LL | 10..=BAR => {},
| ^^^
-error: aborting due to 2 previous errors
+error: could not evaluate constant pattern
+ --> $DIR/ref_to_int_match.rs:7:14
+ |
+LL | 10..=BAR => {},
+ | ^^^
+
+error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0080`.
LL | | let ptr: &[u8; 256] = mem::transmute(&0u8); // &0 gets promoted so it does not dangle
LL | | // Use address-of-element for pointer arithmetic. This could wrap around to NULL!
LL | | let out_of_bounds_ptr = &ptr[255];
- | | ^^^^^^^^^ Memory access failed: pointer must be in-bounds at offset 256, but is outside bounds of allocation 6 which has size 1
+ | | ^^^^^^^^^ Memory access failed: pointer must be in-bounds at offset 256, but is outside bounds of allocation 9 which has size 1
LL | | mem::transmute(out_of_bounds_ptr)
LL | | } };
| |____-
--- /dev/null
+fn main() {}
+
+#[cfg(FALSE)]
+fn container() {
+ const unsafe WhereIsFerris Now() {}
+ //~^ ERROR expected one of `extern` or `fn`
+}
--- /dev/null
+error: expected one of `extern` or `fn`, found `WhereIsFerris`
+ --> $DIR/issue-68062-const-extern-fns-dont-need-fn-specifier-2.rs:5:18
+ |
+LL | const unsafe WhereIsFerris Now() {}
+ | ^^^^^^^^^^^^^ expected one of `extern` or `fn`
+
+error: aborting due to previous error
+
--- /dev/null
+fn main() {}
+
+#[cfg(FALSE)]
+fn container() {
+ const extern "Rust" PUT_ANYTHING_YOU_WANT_HERE bug() -> usize { 1 }
+ //~^ ERROR expected `fn`
+ //~| ERROR `const extern fn` definitions are unstable
+}
--- /dev/null
+error: expected `fn`, found `PUT_ANYTHING_YOU_WANT_HERE`
+ --> $DIR/issue-68062-const-extern-fns-dont-need-fn-specifier.rs:5:25
+ |
+LL | const extern "Rust" PUT_ANYTHING_YOU_WANT_HERE bug() -> usize { 1 }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `fn`
+
+error[E0658]: `const extern fn` definitions are unstable
+ --> $DIR/issue-68062-const-extern-fns-dont-need-fn-specifier.rs:5:5
+ |
+LL | const extern "Rust" PUT_ANYTHING_YOU_WANT_HERE bug() -> usize { 1 }
+ | ^^^^^^^^^^^^
+ |
+ = note: for more information, see https://github.com/rust-lang/rust/issues/64926
+ = help: add `#![feature(const_extern_fn)]` to the crate attributes to enable
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
LL | random()
| ^^^^^^^^
-error[E0013]: constant functions cannot refer to statics, use a constant instead
+error[E0013]: constant functions cannot refer to statics
--> $DIR/const-fn-not-safe-for-const.rs:20:5
|
LL | Y
| ^
+ |
+ = help: consider extracting the value of the `static` to a `const`, and referring to that
-error[E0013]: constant functions cannot refer to statics, use a constant instead
+error[E0013]: constant functions cannot refer to statics
--> $DIR/const-fn-not-safe-for-const.rs:25:6
|
LL | &Y
| ^
+ |
+ = help: consider extracting the value of the `static` to a `const`, and referring to that
error: aborting due to 3 previous errors
// run-pass
#![feature(const_mut_refs)]
-#![feature(const_fn)]
struct Foo {
x: usize
// check-pass
#![feature(const_if_match)]
-#![feature(const_fn)]
enum E {
A,
error: fatal error triggered by #[rustc_error]
- --> $DIR/feature-gate-const-if-match.rs:109:1
+ --> $DIR/feature-gate-const-if-match.rs:108:1
|
LL | / fn main() {
LL | | let _ = [0; {
#![feature(rustc_attrs)]
#![cfg_attr(if_match, feature(const_if_match))]
-#![feature(const_fn)]
const _: i32 = if true { //[stock]~ ERROR `if` is not allowed in a `const`
5
error[E0658]: `if` is not allowed in a `const`
- --> $DIR/feature-gate-const-if-match.rs:11:16
+ --> $DIR/feature-gate-const-if-match.rs:10:16
|
LL | const _: i32 = if true {
| ________________^
= help: add `#![feature(const_if_match)]` to the crate attributes to enable
error[E0658]: `if` is not allowed in a `const`
- --> $DIR/feature-gate-const-if-match.rs:17:16
+ --> $DIR/feature-gate-const-if-match.rs:16:16
|
LL | const _: i32 = if let Some(true) = Some(false) {
| ________________^
= help: add `#![feature(const_if_match)]` to the crate attributes to enable
error[E0658]: `match` is not allowed in a `const`
- --> $DIR/feature-gate-const-if-match.rs:23:16
+ --> $DIR/feature-gate-const-if-match.rs:22:16
|
LL | const _: i32 = match 1 {
| ________________^
= help: add `#![feature(const_if_match)]` to the crate attributes to enable
error[E0658]: `if` is not allowed in a `static`
- --> $DIR/feature-gate-const-if-match.rs:30:13
+ --> $DIR/feature-gate-const-if-match.rs:29:13
|
LL | let x = if true { 0 } else { 1 };
| ^^^^^^^^^^^^^^^^^^^^^^^^
= help: add `#![feature(const_if_match)]` to the crate attributes to enable
error[E0658]: `match` is not allowed in a `static`
- --> $DIR/feature-gate-const-if-match.rs:32:13
+ --> $DIR/feature-gate-const-if-match.rs:31:13
|
LL | let x = match x { 0 => 1, _ => 0 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: add `#![feature(const_if_match)]` to the crate attributes to enable
error[E0658]: `if` is not allowed in a `static`
- --> $DIR/feature-gate-const-if-match.rs:34:5
+ --> $DIR/feature-gate-const-if-match.rs:33:5
|
LL | if let Some(x) = Some(x) { x } else { 1 }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: add `#![feature(const_if_match)]` to the crate attributes to enable
error[E0658]: `if` is not allowed in a `static mut`
- --> $DIR/feature-gate-const-if-match.rs:39:13
+ --> $DIR/feature-gate-const-if-match.rs:38:13
|
LL | let x = if true { 0 } else { 1 };
| ^^^^^^^^^^^^^^^^^^^^^^^^
= help: add `#![feature(const_if_match)]` to the crate attributes to enable
error[E0658]: `match` is not allowed in a `static mut`
- --> $DIR/feature-gate-const-if-match.rs:41:13
+ --> $DIR/feature-gate-const-if-match.rs:40:13
|
LL | let x = match x { 0 => 1, _ => 0 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: add `#![feature(const_if_match)]` to the crate attributes to enable
error[E0658]: `if` is not allowed in a `static mut`
- --> $DIR/feature-gate-const-if-match.rs:43:5
+ --> $DIR/feature-gate-const-if-match.rs:42:5
|
LL | if let Some(x) = Some(x) { x } else { 1 }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: add `#![feature(const_if_match)]` to the crate attributes to enable
error[E0658]: `if` is not allowed in a `const fn`
- --> $DIR/feature-gate-const-if-match.rs:48:5
+ --> $DIR/feature-gate-const-if-match.rs:47:5
|
LL | if true { 5 } else { 6 }
| ^^^^^^^^^^^^^^^^^^^^^^^^
= help: add `#![feature(const_if_match)]` to the crate attributes to enable
error[E0658]: `if` is not allowed in a `const fn`
- --> $DIR/feature-gate-const-if-match.rs:52:5
+ --> $DIR/feature-gate-const-if-match.rs:51:5
|
LL | / if let Some(true) = a {
LL | | 0
= help: add `#![feature(const_if_match)]` to the crate attributes to enable
error[E0658]: `match` is not allowed in a `const fn`
- --> $DIR/feature-gate-const-if-match.rs:60:5
+ --> $DIR/feature-gate-const-if-match.rs:59:5
|
LL | / match i {
LL | | i if i > 10 => i,
= help: add `#![feature(const_if_match)]` to the crate attributes to enable
error[E0658]: `if` is not allowed in a `const fn`
- --> $DIR/feature-gate-const-if-match.rs:91:17
+ --> $DIR/feature-gate-const-if-match.rs:90:17
|
LL | let x = if y { 0 } else { 1 };
| ^^^^^^^^^^^^^^^^^^^^^
= help: add `#![feature(const_if_match)]` to the crate attributes to enable
error[E0658]: `match` is not allowed in a `const fn`
- --> $DIR/feature-gate-const-if-match.rs:93:17
+ --> $DIR/feature-gate-const-if-match.rs:92:17
|
LL | let x = match x { 0 => 1, _ => 0 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: add `#![feature(const_if_match)]` to the crate attributes to enable
error[E0658]: `if` is not allowed in a `const fn`
- --> $DIR/feature-gate-const-if-match.rs:95:9
+ --> $DIR/feature-gate-const-if-match.rs:94:9
|
LL | if let Some(x) = Some(x) { x } else { 1 }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: add `#![feature(const_if_match)]` to the crate attributes to enable
error[E0658]: `if` is not allowed in a `const`
- --> $DIR/feature-gate-const-if-match.rs:111:17
+ --> $DIR/feature-gate-const-if-match.rs:110:17
|
LL | let x = if false { 0 } else { 1 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^
= help: add `#![feature(const_if_match)]` to the crate attributes to enable
error[E0658]: `match` is not allowed in a `const`
- --> $DIR/feature-gate-const-if-match.rs:113:17
+ --> $DIR/feature-gate-const-if-match.rs:112:17
|
LL | let x = match x { 0 => 1, _ => 0 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: add `#![feature(const_if_match)]` to the crate attributes to enable
error[E0658]: `if` is not allowed in a `const`
- --> $DIR/feature-gate-const-if-match.rs:115:9
+ --> $DIR/feature-gate-const-if-match.rs:114:9
|
LL | if let Some(x) = Some(x) { x } else { 1 }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: add `#![feature(const_if_match)]` to the crate attributes to enable
error[E0658]: `if` is not allowed in a `const`
- --> $DIR/feature-gate-const-if-match.rs:68:21
+ --> $DIR/feature-gate-const-if-match.rs:67:21
|
LL | const IF: i32 = if true { 5 } else { 6 };
| ^^^^^^^^^^^^^^^^^^^^^^^^
= help: add `#![feature(const_if_match)]` to the crate attributes to enable
error[E0658]: `if` is not allowed in a `const`
- --> $DIR/feature-gate-const-if-match.rs:71:25
+ --> $DIR/feature-gate-const-if-match.rs:70:25
|
LL | const IF_LET: i32 = if let Some(true) = None { 5 } else { 6 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: add `#![feature(const_if_match)]` to the crate attributes to enable
error[E0658]: `match` is not allowed in a `const`
- --> $DIR/feature-gate-const-if-match.rs:74:24
+ --> $DIR/feature-gate-const-if-match.rs:73:24
|
LL | const MATCH: i32 = match 0 { 1 => 2, _ => 0 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: add `#![feature(const_if_match)]` to the crate attributes to enable
error[E0658]: `if` is not allowed in a `const`
- --> $DIR/feature-gate-const-if-match.rs:79:21
+ --> $DIR/feature-gate-const-if-match.rs:78:21
|
LL | const IF: i32 = if true { 5 } else { 6 };
| ^^^^^^^^^^^^^^^^^^^^^^^^
= help: add `#![feature(const_if_match)]` to the crate attributes to enable
error[E0658]: `if` is not allowed in a `const`
- --> $DIR/feature-gate-const-if-match.rs:82:25
+ --> $DIR/feature-gate-const-if-match.rs:81:25
|
LL | const IF_LET: i32 = if let Some(true) = None { 5 } else { 6 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: add `#![feature(const_if_match)]` to the crate attributes to enable
error[E0658]: `match` is not allowed in a `const`
- --> $DIR/feature-gate-const-if-match.rs:85:24
+ --> $DIR/feature-gate-const-if-match.rs:84:24
|
LL | const MATCH: i32 = match 0 { 1 => 2, _ => 0 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: add `#![feature(const_if_match)]` to the crate attributes to enable
error[E0019]: constant contains unimplemented expression type
- --> $DIR/feature-gate-const-if-match.rs:115:21
+ --> $DIR/feature-gate-const-if-match.rs:114:21
|
LL | if let Some(x) = Some(x) { x } else { 1 }
| ^
#![feature(const_if_match)]
#![feature(const_panic)]
-#![feature(const_fn)]
const X: i32 = {
let mut x = 0;
// check-pass
-#![feature(const_if_match, const_fn)]
+#![feature(const_if_match)]
enum Foo {
Prob,
enum E {
$( $v = $s::V, )*
//~^ ERROR mismatched types
+ //~| ERROR mismatched types
}
}
}
LL | $( $v = $s::V.try_into().unwrap(), )*
| ^^^^^^^^^^^^^^^^^^^^^^^^^
-error: aborting due to previous error
+error[E0308]: mismatched types
+ --> $DIR/enum-discr-type-err.rs:18:21
+ |
+LL | $( $v = $s::V, )*
+ | ^^^^^ expected `isize`, found `i32`
+...
+LL | / mac! {
+LL | | A = F,
+LL | | B = T,
+LL | | }
+ | |_- in this macro invocation
+ |
+help: you can convert an `i32` to `isize` and panic if the converted value wouldn't fit
+ |
+LL | $( $v = $s::V.try_into().unwrap(), )*
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0308`.
match C {
C => {}
//~^ ERROR to use a constant of type `S` in a pattern, `S` must be annotated with
+ //~| ERROR to use a constant of type `S` in a pattern, `S` must be annotated with
}
const K: &T = &T;
match K { //~ ERROR non-exhaustive patterns: `&T` not covered
| ^
error[E0004]: non-exhaustive patterns: `&T` not covered
- --> $DIR/match_ice.rs:15:11
+ --> $DIR/match_ice.rs:16:11
|
LL | struct T;
| --------- `T` defined here
|
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
-error: aborting due to 2 previous errors
+error: to use a constant of type `S` in a pattern, `S` must be annotated with `#[derive(PartialEq, Eq)]`
+ --> $DIR/match_ice.rs:11:9
+ |
+LL | C => {}
+ | ^
+
+error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0004`.
static mut MUTABLE: u32 = 0;
const READ_MUT: u32 = unsafe { MUTABLE }; //~ WARN any use of this value will cause an error
//~^ WARN skipping const checks
+//~| WARN skipping const checks
// ok some day perhaps
const READ_IMMUT: &usize = { //~ ERROR it is undefined behavior to use this value
| ^^^^^^^
warning: skipping const checks
- --> $DIR/const_refers_to_static.rs:35:6
+ --> $DIR/const_refers_to_static.rs:29:32
+ |
+LL | const READ_MUT: u32 = unsafe { MUTABLE };
+ | ^^^^^^^
+
+warning: skipping const checks
+ --> $DIR/const_refers_to_static.rs:36:6
|
LL | &FOO
| ^^^
| constant accesses static
error[E0080]: it is undefined behavior to use this value
- --> $DIR/const_refers_to_static.rs:33:1
+ --> $DIR/const_refers_to_static.rs:34:1
|
LL | / const READ_IMMUT: &usize = {
LL | | static FOO: usize = 0;
LL | const MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-thread 'rustc' panicked at 'no errors encountered even though `delay_span_bug` issued', src/librustc_errors/lib.rs:347:17
+thread 'rustc' panicked at 'no errors encountered even though `delay_span_bug` issued', src/librustc_errors/lib.rs:346:17
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
error: internal compiler error: unexpected panic
//~^ WARN any use of this value will cause an error
fn main() {
- println!("{:?}", C); //~ ERROR: evaluation of constant expression failed
+ println!("{:?}", C);
+ //~^ ERROR: evaluation of constant expression failed
+ //~| WARN: erroneous constant used [const_err]
}
LL | println!("{:?}", C);
| ^ referenced constant has errors
+warning: erroneous constant used
+ --> $DIR/non_const_fn.rs:14:22
+ |
+LL | println!("{:?}", C);
+ | ^ referenced constant has errors
+
error: aborting due to previous error
For more information about this error, try `rustc --explain E0080`.
-error[E0599]: no associated item named `HOST_SIZE` found for type `Foo<A, B>` in the current scope
+error[E0599]: no associated item named `HOST_SIZE` found for struct `Foo<A, B>` in the current scope
--> $DIR/too_generic_eval_ice.rs:7:19
|
LL | pub struct Foo<A, B>(A, B);
fn main() {
match &b""[..] {
ZST => {} //~ ERROR could not evaluate constant pattern
+ //~| ERROR could not evaluate constant pattern
}
}
error: any use of this value will cause an error
- --> $DIR/transmute-size-mismatch-before-typeck.rs:14:29
+ --> $DIR/transmute-size-mismatch-before-typeck.rs:15:29
|
LL | const ZST: &[u8] = unsafe { std::mem::transmute(1usize) };
| ----------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^---
| ^^^
error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
- --> $DIR/transmute-size-mismatch-before-typeck.rs:14:29
+ --> $DIR/transmute-size-mismatch-before-typeck.rs:15:29
|
LL | const ZST: &[u8] = unsafe { std::mem::transmute(1usize) };
| ^^^^^^^^^^^^^^^^^^^
= note: source type: `usize` (word size)
= note: target type: `&'static [u8]` (2 * word size)
-error: aborting due to 3 previous errors
+error: could not evaluate constant pattern
+ --> $DIR/transmute-size-mismatch-before-typeck.rs:10:9
+ |
+LL | ZST => {}
+ | ^^^
+
+error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0512`.
fn main() {
let x: &'static () = &();
- assert_eq!(x as *const () as usize, 1);
+ assert_ne!(x as *const () as usize, 1);
let x: &'static Foo = &Foo;
- assert_eq!(x as *const Foo as usize, 4);
+ assert_ne!(x as *const Foo as usize, 4);
// statics must have a unique address
assert_ne!(&FOO as *const Foo as usize, 4);
- assert_eq!(<Vec<i32>>::new().as_ptr(), <&[i32]>::default().as_ptr());
- assert_eq!(<Box<[i32]>>::default().as_ptr(), (&[]).as_ptr());
+ // FIXME this two tests should be assert_eq!
+ // this stopped working since we are promoting to constants instead of statics
+ assert_ne!(<Vec<i32>>::new().as_ptr(), <&[i32]>::default().as_ptr());
+ assert_ne!(<Box<[i32]>>::default().as_ptr(), (&[]).as_ptr());
}
-error[E0599]: no method named `clone` found for type `Foo` in the current scope
+error[E0599]: no method named `clone` found for struct `Foo` in the current scope
--> $DIR/copy-a-resource.rs:18:16
|
LL | struct Foo {
trait Foo<X = Box<dyn Foo>> {
//~^ ERROR cycle detected
+ //~| ERROR cycle detected
}
fn main() { }
LL | trait Foo<X = Box<dyn Foo>> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error: aborting due to previous error
+error[E0391]: cycle detected when processing `Foo::X`
+ --> $DIR/cycle-trait-default-type-trait.rs:4:23
+ |
+LL | trait Foo<X = Box<dyn Foo>> {
+ | ^^^
+ |
+ = note: ...which again requires processing `Foo::X`, completing the cycle
+note: cycle used when collecting item types in top-level module
+ --> $DIR/cycle-trait-default-type-trait.rs:4:1
+ |
+LL | trait Foo<X = Box<dyn Foo>> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0391`.
--- /dev/null
+warning: floating-point types cannot be used in patterns
+ --> $DIR/deduplicate-diagnostics-2.rs:7:9
+ |
+LL | 1.0 => {}
+ | ^^^
+ |
+ = note: `#[warn(illegal_floating_point_literal_pattern)]` on by default
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
+
+warning: floating-point types cannot be used in patterns
+ --> $DIR/deduplicate-diagnostics-2.rs:11:9
+ |
+LL | 2.0 => {}
+ | ^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
+
+warning: floating-point types cannot be used in patterns
+ --> $DIR/deduplicate-diagnostics-2.rs:7:9
+ |
+LL | 1.0 => {}
+ | ^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
+
--- /dev/null
+warning: floating-point types cannot be used in patterns
+ --> $DIR/deduplicate-diagnostics-2.rs:7:9
+ |
+LL | 1.0 => {}
+ | ^^^
+ |
+ = note: `#[warn(illegal_floating_point_literal_pattern)]` on by default
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
+
+warning: floating-point types cannot be used in patterns
+ --> $DIR/deduplicate-diagnostics-2.rs:11:9
+ |
+LL | 2.0 => {}
+ | ^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
+
+warning: floating-point types cannot be used in patterns
+ --> $DIR/deduplicate-diagnostics-2.rs:7:9
+ |
+LL | 1.0 => {}
+ | ^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
+
+warning: floating-point types cannot be used in patterns
+ --> $DIR/deduplicate-diagnostics-2.rs:11:9
+ |
+LL | 2.0 => {}
+ | ^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
+
--- /dev/null
+// build-pass
+// revisions: duplicate deduplicate
+//[deduplicate] compile-flags: -Z deduplicate-diagnostics=yes
+
+fn main() {
+ match 0.0 {
+ 1.0 => {} //~ WARNING floating-point types cannot be used in patterns
+ //~| WARNING this was previously accepted
+ //~| WARNING floating-point types cannot be used in patterns
+ //~| WARNING this was previously accepted
+ 2.0 => {} //~ WARNING floating-point types cannot be used in patterns
+ //~| WARNING this was previously accepted
+ //[duplicate]~| WARNING floating-point types cannot be used in patterns
+ //[duplicate]~| WARNING this was previously accepted
+ _ => {}
+ }
+}
+error[E0452]: malformed lint attribute input
+ --> $DIR/deduplicate-diagnostics.rs:8:8
+ |
+LL | #[deny("literal")]
+ | ^^^^^^^^^ bad attribute argument
+
error: cannot find derive macro `Unresolved` in this scope
--> $DIR/deduplicate-diagnostics.rs:4:10
|
LL | #[derive(Unresolved)]
| ^^^^^^^^^^
-error: aborting due to previous error
+error: aborting due to 2 previous errors
+For more information about this error, try `rustc --explain E0452`.
+error[E0452]: malformed lint attribute input
+ --> $DIR/deduplicate-diagnostics.rs:8:8
+ |
+LL | #[deny("literal")]
+ | ^^^^^^^^^ bad attribute argument
+
error: cannot find derive macro `Unresolved` in this scope
--> $DIR/deduplicate-diagnostics.rs:4:10
|
LL | #[derive(Unresolved)]
| ^^^^^^^^^^
-error: aborting due to 2 previous errors
+error[E0452]: malformed lint attribute input
+ --> $DIR/deduplicate-diagnostics.rs:8:8
+ |
+LL | #[deny("literal")]
+ | ^^^^^^^^^ bad attribute argument
+
+error[E0452]: malformed lint attribute input
+ --> $DIR/deduplicate-diagnostics.rs:8:8
+ |
+LL | #[deny("literal")]
+ | ^^^^^^^^^ bad attribute argument
+
+error: aborting due to 5 previous errors
+For more information about this error, try `rustc --explain E0452`.
// revisions: duplicate deduplicate
-//[duplicate] compile-flags: -Z deduplicate-diagnostics=no
+//[deduplicate] compile-flags: -Z deduplicate-diagnostics=yes
#[derive(Unresolved)] //~ ERROR cannot find derive macro `Unresolved` in this scope
//[duplicate]~| ERROR cannot find derive macro `Unresolved` in this scope
struct S;
+#[deny("literal")] //~ ERROR malformed lint attribute input
+ //[duplicate]~| ERROR malformed lint attribute input
+ //[duplicate]~| ERROR malformed lint attribute input
fn main() {}
-error[E0599]: no method named `clone` found for type `Bar<NotClone>` in the current scope
+error[E0599]: no method named `clone` found for struct `Bar<NotClone>` in the current scope
--> $DIR/derive-assoc-type-not-impl.rs:18:30
|
LL | struct Bar<T: Foo> {
#[derive(PartialOrd,PartialEq)]
enum Enum {
A {
- x: Error //~ ERROR
+ x: Error //~ ERROR can't compare `Error` with `Error`
+ //~| ERROR can't compare `Error` with `Error`
+ //~| ERROR can't compare `Error` with `Error`
+ //~| ERROR can't compare `Error` with `Error`
+ //~| ERROR can't compare `Error` with `Error`
}
}
= help: the trait `std::cmp::PartialOrd` is not implemented for `Error`
= note: required by `std::cmp::PartialOrd::partial_cmp`
-error: aborting due to previous error
+error[E0277]: can't compare `Error` with `Error`
+ --> $DIR/derives-span-PartialOrd-enum-struct-variant.rs:13:6
+ |
+LL | x: Error
+ | ^^^^^^^^ no implementation for `Error < Error` and `Error > Error`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `Error`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error[E0277]: can't compare `Error` with `Error`
+ --> $DIR/derives-span-PartialOrd-enum-struct-variant.rs:13:6
+ |
+LL | x: Error
+ | ^^^^^^^^ no implementation for `Error < Error` and `Error > Error`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `Error`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error[E0277]: can't compare `Error` with `Error`
+ --> $DIR/derives-span-PartialOrd-enum-struct-variant.rs:13:6
+ |
+LL | x: Error
+ | ^^^^^^^^ no implementation for `Error < Error` and `Error > Error`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `Error`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error[E0277]: can't compare `Error` with `Error`
+ --> $DIR/derives-span-PartialOrd-enum-struct-variant.rs:13:6
+ |
+LL | x: Error
+ | ^^^^^^^^ no implementation for `Error < Error` and `Error > Error`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `Error`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error: aborting due to 5 previous errors
For more information about this error, try `rustc --explain E0277`.
#[derive(PartialOrd,PartialEq)]
enum Enum {
A(
- Error //~ ERROR
+ Error //~ ERROR can't compare `Error` with `Error`
+ //~| ERROR can't compare `Error` with `Error`
+ //~| ERROR can't compare `Error` with `Error`
+ //~| ERROR can't compare `Error` with `Error`
+ //~| ERROR can't compare `Error` with `Error`
)
}
= help: the trait `std::cmp::PartialOrd` is not implemented for `Error`
= note: required by `std::cmp::PartialOrd::partial_cmp`
-error: aborting due to previous error
+error[E0277]: can't compare `Error` with `Error`
+ --> $DIR/derives-span-PartialOrd-enum.rs:13:6
+ |
+LL | Error
+ | ^^^^^ no implementation for `Error < Error` and `Error > Error`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `Error`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error[E0277]: can't compare `Error` with `Error`
+ --> $DIR/derives-span-PartialOrd-enum.rs:13:6
+ |
+LL | Error
+ | ^^^^^ no implementation for `Error < Error` and `Error > Error`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `Error`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error[E0277]: can't compare `Error` with `Error`
+ --> $DIR/derives-span-PartialOrd-enum.rs:13:6
+ |
+LL | Error
+ | ^^^^^ no implementation for `Error < Error` and `Error > Error`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `Error`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error[E0277]: can't compare `Error` with `Error`
+ --> $DIR/derives-span-PartialOrd-enum.rs:13:6
+ |
+LL | Error
+ | ^^^^^ no implementation for `Error < Error` and `Error > Error`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `Error`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error: aborting due to 5 previous errors
For more information about this error, try `rustc --explain E0277`.
#[derive(PartialOrd,PartialEq)]
struct Struct {
- x: Error //~ ERROR
+ x: Error //~ ERROR can't compare `Error` with `Error`
+ //~| ERROR can't compare `Error` with `Error`
+ //~| ERROR can't compare `Error` with `Error`
+ //~| ERROR can't compare `Error` with `Error`
+ //~| ERROR can't compare `Error` with `Error`
}
fn main() {}
= help: the trait `std::cmp::PartialOrd` is not implemented for `Error`
= note: required by `std::cmp::PartialOrd::partial_cmp`
-error: aborting due to previous error
+error[E0277]: can't compare `Error` with `Error`
+ --> $DIR/derives-span-PartialOrd-struct.rs:12:5
+ |
+LL | x: Error
+ | ^^^^^^^^ no implementation for `Error < Error` and `Error > Error`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `Error`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error[E0277]: can't compare `Error` with `Error`
+ --> $DIR/derives-span-PartialOrd-struct.rs:12:5
+ |
+LL | x: Error
+ | ^^^^^^^^ no implementation for `Error < Error` and `Error > Error`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `Error`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error[E0277]: can't compare `Error` with `Error`
+ --> $DIR/derives-span-PartialOrd-struct.rs:12:5
+ |
+LL | x: Error
+ | ^^^^^^^^ no implementation for `Error < Error` and `Error > Error`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `Error`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error[E0277]: can't compare `Error` with `Error`
+ --> $DIR/derives-span-PartialOrd-struct.rs:12:5
+ |
+LL | x: Error
+ | ^^^^^^^^ no implementation for `Error < Error` and `Error > Error`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `Error`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error: aborting due to 5 previous errors
For more information about this error, try `rustc --explain E0277`.
#[derive(PartialOrd,PartialEq)]
struct Struct(
- Error //~ ERROR
+ Error //~ ERROR can't compare `Error` with `Error`
+ //~| ERROR can't compare `Error` with `Error`
+ //~| ERROR can't compare `Error` with `Error`
+ //~| ERROR can't compare `Error` with `Error`
+ //~| ERROR can't compare `Error` with `Error`
);
fn main() {}
= help: the trait `std::cmp::PartialOrd` is not implemented for `Error`
= note: required by `std::cmp::PartialOrd::partial_cmp`
-error: aborting due to previous error
+error[E0277]: can't compare `Error` with `Error`
+ --> $DIR/derives-span-PartialOrd-tuple-struct.rs:12:5
+ |
+LL | Error
+ | ^^^^^ no implementation for `Error < Error` and `Error > Error`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `Error`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error[E0277]: can't compare `Error` with `Error`
+ --> $DIR/derives-span-PartialOrd-tuple-struct.rs:12:5
+ |
+LL | Error
+ | ^^^^^ no implementation for `Error < Error` and `Error > Error`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `Error`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error[E0277]: can't compare `Error` with `Error`
+ --> $DIR/derives-span-PartialOrd-tuple-struct.rs:12:5
+ |
+LL | Error
+ | ^^^^^ no implementation for `Error < Error` and `Error > Error`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `Error`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error[E0277]: can't compare `Error` with `Error`
+ --> $DIR/derives-span-PartialOrd-tuple-struct.rs:12:5
+ |
+LL | Error
+ | ^^^^^ no implementation for `Error < Error` and `Error > Error`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `Error`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error: aborting due to 5 previous errors
For more information about this error, try `rustc --explain E0277`.
#[derive(Send)]
//~^ ERROR cannot find derive macro `Send` in this scope
+//~| ERROR cannot find derive macro `Send` in this scope
struct Test;
#[derive(Sync)]
//~^ ERROR cannot find derive macro `Sync` in this scope
+//~| ERROR cannot find derive macro `Sync` in this scope
struct Test1;
pub fn main() {}
error: cannot find derive macro `Sync` in this scope
- --> $DIR/deriving-bounds.rs:5:10
+ --> $DIR/deriving-bounds.rs:6:10
|
LL | #[derive(Sync)]
| ^^^^
|
note: unsafe traits like `Sync` should be implemented explicitly
- --> $DIR/deriving-bounds.rs:5:10
+ --> $DIR/deriving-bounds.rs:6:10
|
LL | #[derive(Sync)]
| ^^^^
+error: cannot find derive macro `Sync` in this scope
+ --> $DIR/deriving-bounds.rs:6:10
+ |
+LL | #[derive(Sync)]
+ | ^^^^
+ |
+note: unsafe traits like `Sync` should be implemented explicitly
+ --> $DIR/deriving-bounds.rs:6:10
+ |
+LL | #[derive(Sync)]
+ | ^^^^
+
+error: cannot find derive macro `Send` in this scope
+ --> $DIR/deriving-bounds.rs:1:10
+ |
+LL | #[derive(Send)]
+ | ^^^^
+ |
+note: unsafe traits like `Send` should be implemented explicitly
+ --> $DIR/deriving-bounds.rs:1:10
+ |
+LL | #[derive(Send)]
+ | ^^^^
+
error: cannot find derive macro `Send` in this scope
--> $DIR/deriving-bounds.rs:1:10
|
LL | #[derive(Send)]
| ^^^^
-error: aborting due to 2 previous errors
+error: aborting due to 4 previous errors
+// FIXME: missing sysroot spans (#53081)
+// ignore-i586-unknown-linux-gnu
+// ignore-i586-unknown-linux-musl
+// ignore-i686-unknown-linux-musl
#[derive(Eqr)]
//~^ ERROR cannot find derive macro `Eqr` in this scope
+//~| ERROR cannot find derive macro `Eqr` in this scope
struct Foo;
pub fn main() {}
error: cannot find derive macro `Eqr` in this scope
- --> $DIR/deriving-meta-unknown-trait.rs:1:10
+ --> $DIR/deriving-meta-unknown-trait.rs:5:10
|
LL | #[derive(Eqr)]
| ^^^ help: a derive macro with a similar name exists: `Eq`
+ |
+ ::: $SRC_DIR/libcore/cmp.rs:LL:COL
+ |
+LL | pub macro Eq($item:item) {
+ | ------------------------ similarly named derive macro `Eq` defined here
+
+error: cannot find derive macro `Eqr` in this scope
+ --> $DIR/deriving-meta-unknown-trait.rs:5:10
+ |
+LL | #[derive(Eqr)]
+ | ^^^ help: a derive macro with a similar name exists: `Eq`
+ |
+ ::: $SRC_DIR/libcore/cmp.rs:LL:COL
+ |
+LL | pub macro Eq($item:item) {
+ | ------------------------ similarly named derive macro `Eq` defined here
-error: aborting due to previous error
+error: aborting due to 2 previous errors
#[derive(FromPrimitive)] //~ ERROR cannot find derive macro `FromPrimitive` in this scope
+ //~| ERROR cannot find derive macro `FromPrimitive` in this scope
enum Foo {}
fn main() {}
LL | #[derive(FromPrimitive)]
| ^^^^^^^^^^^^^
-error: aborting due to previous error
+error: cannot find derive macro `FromPrimitive` in this scope
+ --> $DIR/deriving-primitive.rs:1:10
+ |
+LL | #[derive(FromPrimitive)]
+ | ^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
match 0u8 {
[u8]::AssocItem => {}
//~^ ERROR missing angle brackets in associated item path
- //~| ERROR no associated item named `AssocItem` found for type `[u8]` in the current scope
+ //~| ERROR no associated item named `AssocItem` found
(u8, u8)::AssocItem => {}
//~^ ERROR missing angle brackets in associated item path
- //~| ERROR no associated item named `AssocItem` found for type `(u8, u8)` in the current sco
+ //~| ERROR no associated item named `AssocItem` found
_::AssocItem => {}
//~^ ERROR missing angle brackets in associated item path
- //~| ERROR no associated item named `AssocItem` found for type `_` in the current scope
+ //~| ERROR no associated item named `AssocItem` found
}
match &0u8 {
&(u8,)::AssocItem => {}
//~^ ERROR missing angle brackets in associated item path
- //~| ERROR no associated item named `AssocItem` found for type `(u8,)` in the current scope
+ //~| ERROR no associated item named `AssocItem` found
}
}
macro_rules! pat {
($ty: ty) => ($ty::AssocItem)
//~^ ERROR missing angle brackets in associated item path
- //~| ERROR no associated item named `AssocItem` found for type `u8` in the current scope
+ //~| ERROR no associated item named `AssocItem` found
}
macro_rules! ty {
() => (u8)
pat!(u8) => {}
ty!()::AssocItem => {}
//~^ ERROR missing angle brackets in associated item path
- //~| ERROR no associated item named `AssocItem` found for type `u8` in the current scope
+ //~| ERROR no associated item named `AssocItem` found
}
}
LL | pat!(u8) => {}
| -------- in this macro invocation
-error[E0599]: no associated item named `AssocItem` found for type `[u8]` in the current scope
+error[E0599]: no associated item named `AssocItem` found for slice `[u8]` in the current scope
--> $DIR/bad-assoc-pat.rs:3:15
|
LL | [u8]::AssocItem => {}
| ^^^^^^^^^ associated item not found in `[u8]`
-error[E0599]: no associated item named `AssocItem` found for type `(u8, u8)` in the current scope
+error[E0599]: no associated item named `AssocItem` found for tuple `(u8, u8)` in the current scope
--> $DIR/bad-assoc-pat.rs:6:19
|
LL | (u8, u8)::AssocItem => {}
LL | _::AssocItem => {}
| ^^^^^^^^^ associated item not found in `_`
-error[E0599]: no associated item named `AssocItem` found for type `(u8,)` in the current scope
+error[E0599]: no associated item named `AssocItem` found for tuple `(u8,)` in the current scope
--> $DIR/bad-assoc-pat.rs:14:17
|
LL | &(u8,)::AssocItem => {}
}
fn main() {
- S.hello_method(); //~ no method named `hello_method` found for type `S` in the current scope
+ S.hello_method(); //~ no method named `hello_method` found
}
LL | pub hello_method(&self) {
| ^ missing `fn`, `type`, or `const`
-error[E0599]: no method named `hello_method` found for type `S` in the current scope
+error[E0599]: no method named `hello_method` found for struct `S` in the current scope
--> $DIR/issue-40006.rs:38:7
|
LL | struct S;
fn main() {
(0..13).collect<Vec<i32>>();
- //~^ ERROR chained comparison
+ //~^ ERROR comparison operators cannot be chained
Vec<i32>::new();
- //~^ ERROR chained comparison
+ //~^ ERROR comparison operators cannot be chained
(0..13).collect<Vec<i32>();
- //~^ ERROR chained comparison
+ //~^ ERROR comparison operators cannot be chained
}
-error: chained comparison operators require parentheses
+error: comparison operators cannot be chained
--> $DIR/issue-40396.rs:2:20
|
LL | (0..13).collect<Vec<i32>>();
| ^^^^^
|
+help: split the comparison into two...
+ |
+LL | (0..13).collect < Vec && Vec <i32>>();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: ...or parenthesize one of the comparisons
+ |
+LL | ((0..13).collect < Vec) <i32>>();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
help: use `::<...>` instead of `<...>` to specify type arguments
|
LL | (0..13).collect::<Vec<i32>>();
| ^^
-error: chained comparison operators require parentheses
+error: comparison operators cannot be chained
--> $DIR/issue-40396.rs:4:8
|
LL | Vec<i32>::new();
LL | Vec::<i32>::new();
| ^^
-error: chained comparison operators require parentheses
+error: comparison operators cannot be chained
--> $DIR/issue-40396.rs:6:20
|
LL | (0..13).collect<Vec<i32>();
| ^^^^^
|
+help: split the comparison into two...
+ |
+LL | (0..13).collect < Vec && Vec <i32>();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: ...or parenthesize one of the comparisons
+ |
+LL | ((0..13).collect < Vec) <i32>();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
help: use `::<...>` instead of `<...>` to specify type arguments
|
LL | (0..13).collect::<Vec<i32>();
let b = false;
let _ = a and b; //~ ERROR `and` is not a logical operator
+ //~| ERROR `and` is not a logical operator
if a and b { //~ ERROR `and` is not a logical operator
+ //~| ERROR `and` is not a logical operator
println!("both");
}
let b = false;
let _ = a or b; //~ ERROR `or` is not a logical operator
+ //~| ERROR `or` is not a logical operator
if a or b { //~ ERROR `or` is not a logical operator
+ //~| ERROR `or` is not a logical operator
println!("both");
}
}
let a = true;
let b = false;
if (a and b) { //~ ERROR `and` is not a logical operator
+ //~| ERROR `and` is not a logical operator
println!("both");
}
}
let a = true;
let b = false;
if (a or b) { //~ ERROR `or` is not a logical operator
+ //~| ERROR `or` is not a logical operator
println!("both");
}
}
let a = true;
let b = false;
while a and b { //~ ERROR `and` is not a logical operator
+ //~| ERROR `and` is not a logical operator
println!("both");
}
}
let a = true;
let b = false;
while a or b { //~ ERROR `or` is not a logical operator
+ //~| ERROR `or` is not a logical operator
println!("both");
}
}
= note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators
error: `and` is not a logical operator
- --> $DIR/issue-54109-and_instead_of_ampersands.rs:9:10
+ --> $DIR/issue-54109-and_instead_of_ampersands.rs:7:15
+ |
+LL | let _ = a and b;
+ | ^^^ help: use `&&` to perform logical conjunction
+ |
+ = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators
+
+error: `and` is not a logical operator
+ --> $DIR/issue-54109-and_instead_of_ampersands.rs:10:10
+ |
+LL | if a and b {
+ | ^^^ help: use `&&` to perform logical conjunction
+ |
+ = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators
+
+error: `and` is not a logical operator
+ --> $DIR/issue-54109-and_instead_of_ampersands.rs:10:10
|
LL | if a and b {
| ^^^ help: use `&&` to perform logical conjunction
= note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators
error: `or` is not a logical operator
- --> $DIR/issue-54109-and_instead_of_ampersands.rs:20:15
+ --> $DIR/issue-54109-and_instead_of_ampersands.rs:22:15
|
LL | let _ = a or b;
| ^^ help: use `||` to perform logical disjunction
= note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators
error: `or` is not a logical operator
- --> $DIR/issue-54109-and_instead_of_ampersands.rs:22:10
+ --> $DIR/issue-54109-and_instead_of_ampersands.rs:22:15
+ |
+LL | let _ = a or b;
+ | ^^ help: use `||` to perform logical disjunction
+ |
+ = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators
+
+error: `or` is not a logical operator
+ --> $DIR/issue-54109-and_instead_of_ampersands.rs:25:10
+ |
+LL | if a or b {
+ | ^^ help: use `||` to perform logical disjunction
+ |
+ = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators
+
+error: `or` is not a logical operator
+ --> $DIR/issue-54109-and_instead_of_ampersands.rs:25:10
|
LL | if a or b {
| ^^ help: use `||` to perform logical disjunction
= note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators
error: `and` is not a logical operator
- --> $DIR/issue-54109-and_instead_of_ampersands.rs:30:11
+ --> $DIR/issue-54109-and_instead_of_ampersands.rs:34:11
|
LL | if (a and b) {
| ^^^ help: use `&&` to perform logical conjunction
|
= note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators
+error: `and` is not a logical operator
+ --> $DIR/issue-54109-and_instead_of_ampersands.rs:34:11
+ |
+LL | if (a and b) {
+ | ^^^ help: use `&&` to perform logical conjunction
+ |
+ = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators
+
+error: `or` is not a logical operator
+ --> $DIR/issue-54109-and_instead_of_ampersands.rs:43:11
+ |
+LL | if (a or b) {
+ | ^^ help: use `||` to perform logical disjunction
+ |
+ = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators
+
error: `or` is not a logical operator
- --> $DIR/issue-54109-and_instead_of_ampersands.rs:38:11
+ --> $DIR/issue-54109-and_instead_of_ampersands.rs:43:11
|
LL | if (a or b) {
| ^^ help: use `||` to perform logical disjunction
= note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators
error: `and` is not a logical operator
- --> $DIR/issue-54109-and_instead_of_ampersands.rs:46:13
+ --> $DIR/issue-54109-and_instead_of_ampersands.rs:52:13
|
LL | while a and b {
| ^^^ help: use `&&` to perform logical conjunction
|
= note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators
+error: `and` is not a logical operator
+ --> $DIR/issue-54109-and_instead_of_ampersands.rs:52:13
+ |
+LL | while a and b {
+ | ^^^ help: use `&&` to perform logical conjunction
+ |
+ = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators
+
+error: `or` is not a logical operator
+ --> $DIR/issue-54109-and_instead_of_ampersands.rs:61:13
+ |
+LL | while a or b {
+ | ^^ help: use `||` to perform logical disjunction
+ |
+ = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators
+
error: `or` is not a logical operator
- --> $DIR/issue-54109-and_instead_of_ampersands.rs:54:13
+ --> $DIR/issue-54109-and_instead_of_ampersands.rs:61:13
|
LL | while a or b {
| ^^ help: use `||` to perform logical disjunction
= note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators
error[E0308]: mismatched types
- --> $DIR/issue-54109-and_instead_of_ampersands.rs:13:33
+ --> $DIR/issue-54109-and_instead_of_ampersands.rs:15:33
|
LL | let _recovery_witness: () = 0;
| -- ^ expected `()`, found integer
| |
| expected due to this
-error: aborting due to 9 previous errors
+error: aborting due to 17 previous errors
For more information about this error, try `rustc --explain E0308`.
fn main() {
T::new();
- //~^ ERROR no function or associated item named `new` found for type `T` in the current scope
+ //~^ ERROR no function or associated item named `new` found
}
-error[E0599]: no function or associated item named `new` found for type `T` in the current scope
+error[E0599]: no function or associated item named `new` found for struct `T` in the current scope
--> $DIR/dont-suggest-private-trait-method.rs:4:8
|
LL | struct T;
let xe1 = XEmpty1; //~ ERROR expected value, found struct `XEmpty1`
let xe1 = XEmpty1();
//~^ ERROR expected function, tuple struct or tuple variant, found struct `XEmpty1`
- let xe3 = XE::Empty3; //~ ERROR no variant or associated item named `Empty3` found for type
- let xe3 = XE::Empty3(); //~ ERROR no variant or associated item named `Empty3` found for type
+ let xe3 = XE::Empty3; //~ ERROR no variant or associated item named `Empty3` found for enum
+ let xe3 = XE::Empty3(); //~ ERROR no variant or associated item named `Empty3` found for enum
- XE::Empty1 {}; //~ ERROR no variant `Empty1` in enum `empty_struct::XE`
+ XE::Empty1 {}; //~ ERROR no variant named `Empty1` found for enum `empty_struct::XE`
}
| |
| did you mean `Empty1 { /* fields */ }`?
| help: a unit struct with a similar name exists: `XEmpty2`
+ |
+ ::: $DIR/auxiliary/empty-struct.rs:2:1
+ |
+LL | pub struct XEmpty2;
+ | ------------------- similarly named unit struct `XEmpty2` defined here
error[E0423]: expected function, tuple struct or tuple variant, found struct `Empty1`
--> $DIR/empty-struct-braces-expr.rs:16:14
| |
| did you mean `Empty1 { /* fields */ }`?
| help: a unit struct with a similar name exists: `XEmpty2`
+ |
+ ::: $DIR/auxiliary/empty-struct.rs:2:1
+ |
+LL | pub struct XEmpty2;
+ | ------------------- similarly named unit struct `XEmpty2` defined here
error[E0423]: expected value, found struct variant `E::Empty3`
--> $DIR/empty-struct-braces-expr.rs:18:14
| |
| did you mean `XEmpty1 { /* fields */ }`?
| help: a unit struct with a similar name exists: `XEmpty2`
+ |
+ ::: $DIR/auxiliary/empty-struct.rs:2:1
+ |
+LL | pub struct XEmpty2;
+ | ------------------- similarly named unit struct `XEmpty2` defined here
error[E0423]: expected function, tuple struct or tuple variant, found struct `XEmpty1`
--> $DIR/empty-struct-braces-expr.rs:23:15
| |
| did you mean `XEmpty1 { /* fields */ }`?
| help: a unit struct with a similar name exists: `XEmpty2`
+ |
+ ::: $DIR/auxiliary/empty-struct.rs:2:1
+ |
+LL | pub struct XEmpty2;
+ | ------------------- similarly named unit struct `XEmpty2` defined here
-error[E0599]: no variant or associated item named `Empty3` found for type `empty_struct::XE` in the current scope
+error[E0599]: no variant or associated item named `Empty3` found for enum `empty_struct::XE` in the current scope
--> $DIR/empty-struct-braces-expr.rs:25:19
|
LL | let xe3 = XE::Empty3;
| variant or associated item not found in `empty_struct::XE`
| help: there is a variant with a similar name: `XEmpty3`
-error[E0599]: no variant or associated item named `Empty3` found for type `empty_struct::XE` in the current scope
+error[E0599]: no variant or associated item named `Empty3` found for enum `empty_struct::XE` in the current scope
--> $DIR/empty-struct-braces-expr.rs:26:19
|
LL | let xe3 = XE::Empty3();
| variant or associated item not found in `empty_struct::XE`
| help: there is a variant with a similar name: `XEmpty3`
-error: no variant `Empty1` in enum `empty_struct::XE`
+error[E0599]: no variant named `Empty1` found for enum `empty_struct::XE`
--> $DIR/empty-struct-braces-expr.rs:28:9
|
LL | XE::Empty1 {};
| | |
| | help: a unit variant with a similar name exists: `XEmpty4`
| did you mean `XE::XEmpty3 { /* fields */ }`?
+ |
+ ::: $DIR/auxiliary/empty-struct.rs:7:5
+ |
+LL | XEmpty4,
+ | ------- similarly named unit variant `XEmpty4` defined here
error: aborting due to 2 previous errors
| |
| did you mean `Empty1 { /* fields */ }`?
| help: a tuple struct with a similar name exists: `XEmpty6`
+ |
+ ::: $DIR/auxiliary/empty-struct.rs:3:1
+ |
+LL | pub struct XEmpty6();
+ | --------------------- similarly named tuple struct `XEmpty6` defined here
error[E0532]: expected tuple struct or tuple variant, found struct `XEmpty1`
--> $DIR/empty-struct-braces-pat-2.rs:18:9
| |
| did you mean `XEmpty1 { /* fields */ }`?
| help: a tuple struct with a similar name exists: `XEmpty6`
+ |
+ ::: $DIR/auxiliary/empty-struct.rs:3:1
+ |
+LL | pub struct XEmpty6();
+ | --------------------- similarly named tuple struct `XEmpty6` defined here
error[E0532]: expected tuple struct or tuple variant, found struct `Empty1`
--> $DIR/empty-struct-braces-pat-2.rs:21:9
| |
| did you mean `Empty1 { /* fields */ }`?
| help: a tuple struct with a similar name exists: `XEmpty6`
+ |
+ ::: $DIR/auxiliary/empty-struct.rs:3:1
+ |
+LL | pub struct XEmpty6();
+ | --------------------- similarly named tuple struct `XEmpty6` defined here
error[E0532]: expected tuple struct or tuple variant, found struct `XEmpty1`
--> $DIR/empty-struct-braces-pat-2.rs:24:9
| |
| did you mean `XEmpty1 { /* fields */ }`?
| help: a tuple struct with a similar name exists: `XEmpty6`
+ |
+ ::: $DIR/auxiliary/empty-struct.rs:3:1
+ |
+LL | pub struct XEmpty6();
+ | --------------------- similarly named tuple struct `XEmpty6` defined here
error: aborting due to 4 previous errors
| | |
| | help: a tuple variant with a similar name exists: `XEmpty5`
| did you mean `XE::XEmpty3 { /* fields */ }`?
+ |
+ ::: $DIR/auxiliary/empty-struct.rs:8:5
+ |
+LL | XEmpty5(),
+ | --------- similarly named tuple variant `XEmpty5` defined here
error[E0532]: expected tuple struct or tuple variant, found struct variant `E::Empty3`
--> $DIR/empty-struct-braces-pat-3.rs:25:9
| | |
| | help: a tuple variant with a similar name exists: `XEmpty5`
| did you mean `XE::XEmpty3 { /* fields */ }`?
+ |
+ ::: $DIR/auxiliary/empty-struct.rs:8:5
+ |
+LL | XEmpty5(),
+ | --------- similarly named tuple variant `XEmpty5` defined here
error: aborting due to 4 previous errors
| | |
| | help: a unit variant with a similar name exists: `XEmpty4`
| did you mean `XE::XEmpty5( /* fields */ )`?
+ |
+ ::: $DIR/auxiliary/empty-struct.rs:7:5
+ |
+LL | XEmpty4,
+ | ------- similarly named unit variant `XEmpty4` defined here
error: aborting due to 4 previous errors
|
LL | Empty2() => ()
| ^^^^^^ help: a tuple struct with a similar name exists: `XEmpty6`
+ |
+ ::: $DIR/auxiliary/empty-struct.rs:3:1
+ |
+LL | pub struct XEmpty6();
+ | --------------------- similarly named tuple struct `XEmpty6` defined here
error[E0532]: expected tuple struct or tuple variant, found unit struct `XEmpty2`
--> $DIR/empty-struct-unit-pat.rs:24:9
|
LL | XEmpty2() => ()
| ^^^^^^^ help: a tuple struct with a similar name exists: `XEmpty6`
+ |
+ ::: $DIR/auxiliary/empty-struct.rs:3:1
+ |
+LL | pub struct XEmpty6();
+ | --------------------- similarly named tuple struct `XEmpty6` defined here
error[E0532]: expected tuple struct or tuple variant, found unit struct `Empty2`
--> $DIR/empty-struct-unit-pat.rs:28:9
|
LL | Empty2(..) => ()
| ^^^^^^ help: a tuple struct with a similar name exists: `XEmpty6`
+ |
+ ::: $DIR/auxiliary/empty-struct.rs:3:1
+ |
+LL | pub struct XEmpty6();
+ | --------------------- similarly named tuple struct `XEmpty6` defined here
error[E0532]: expected tuple struct or tuple variant, found unit struct `XEmpty2`
--> $DIR/empty-struct-unit-pat.rs:32:9
|
LL | XEmpty2(..) => ()
| ^^^^^^^ help: a tuple struct with a similar name exists: `XEmpty6`
+ |
+ ::: $DIR/auxiliary/empty-struct.rs:3:1
+ |
+LL | pub struct XEmpty6();
+ | --------------------- similarly named tuple struct `XEmpty6` defined here
error[E0532]: expected tuple struct or tuple variant, found unit variant `E::Empty4`
--> $DIR/empty-struct-unit-pat.rs:37:9
| ^^^^-------
| |
| help: a tuple variant with a similar name exists: `XEmpty5`
+ |
+ ::: $DIR/auxiliary/empty-struct.rs:8:5
+ |
+LL | XEmpty5(),
+ | --------- similarly named tuple variant `XEmpty5` defined here
error[E0532]: expected tuple struct or tuple variant, found unit variant `E::Empty4`
--> $DIR/empty-struct-unit-pat.rs:46:9
| ^^^^-------
| |
| help: a tuple variant with a similar name exists: `XEmpty5`
+ |
+ ::: $DIR/auxiliary/empty-struct.rs:8:5
+ |
+LL | XEmpty5(),
+ | --------- similarly named tuple variant `XEmpty5` defined here
error: aborting due to 8 previous errors
match 5u32 {
1000 ..= 5 => {}
//~^ ERROR lower range bound must be less than or equal to upper
+ //~| ERROR lower range bound must be less than or equal to upper
}
}
LL | 1000 ..= 5 => {}
| ^^^^ lower bound larger than upper bound
-error: aborting due to previous error
+error[E0030]: lower range bound must be less than or equal to upper
+ --> $DIR/E0030.rs:3:9
+ |
+LL | 1000 ..= 5 => {}
+ | ^^^^ lower bound larger than upper bound
+
+error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0030`.
#![allow(foo = "")] //~ ERROR E0452
-
+ //~| ERROR E0452
+ //~| ERROR E0452
+ //~| ERROR E0452
+ //~| ERROR E0452
+ //~| ERROR E0452
fn main() {
}
LL | #![allow(foo = "")]
| ^^^^^^^^ bad attribute argument
-error: aborting due to previous error
+error[E0452]: malformed lint attribute input
+ --> $DIR/E0452.rs:1:10
+ |
+LL | #![allow(foo = "")]
+ | ^^^^^^^^ bad attribute argument
+
+error[E0452]: malformed lint attribute input
+ --> $DIR/E0452.rs:1:10
+ |
+LL | #![allow(foo = "")]
+ | ^^^^^^^^ bad attribute argument
+
+error[E0452]: malformed lint attribute input
+ --> $DIR/E0452.rs:1:10
+ |
+LL | #![allow(foo = "")]
+ | ^^^^^^^^ bad attribute argument
+
+error[E0452]: malformed lint attribute input
+ --> $DIR/E0452.rs:1:10
+ |
+LL | #![allow(foo = "")]
+ | ^^^^^^^^ bad attribute argument
+
+error[E0452]: malformed lint attribute input
+ --> $DIR/E0452.rs:1:10
+ |
+LL | #![allow(foo = "")]
+ | ^^^^^^^^ bad attribute argument
+
+error: aborting due to 6 previous errors
For more information about this error, try `rustc --explain E0452`.
#[allow(non_snake_case)]
//~^ ERROR allow(non_snake_case) overruled by outer forbid(non_snake_case)
+//~| ERROR allow(non_snake_case) overruled by outer forbid(non_snake_case)
+//~| ERROR allow(non_snake_case) overruled by outer forbid(non_snake_case)
fn main() {
}
LL | #[allow(non_snake_case)]
| ^^^^^^^^^^^^^^ overruled by previous forbid
-error: aborting due to previous error
+error[E0453]: allow(non_snake_case) overruled by outer forbid(non_snake_case)
+ --> $DIR/E0453.rs:3:9
+ |
+LL | #![forbid(non_snake_case)]
+ | -------------- `forbid` level set here
+LL |
+LL | #[allow(non_snake_case)]
+ | ^^^^^^^^^^^^^^ overruled by previous forbid
+
+error[E0453]: allow(non_snake_case) overruled by outer forbid(non_snake_case)
+ --> $DIR/E0453.rs:3:9
+ |
+LL | #![forbid(non_snake_case)]
+ | -------------- `forbid` level set here
+LL |
+LL | #[allow(non_snake_case)]
+ | ^^^^^^^^^^^^^^ overruled by previous forbid
+
+error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0453`.
// repr currently doesn't support literals
#[repr("C")] //~ ERROR E0565
+ //~| ERROR E0565
struct A { }
fn main() { }
LL | #[repr("C")]
| ^^^
-error: aborting due to previous error
+error[E0565]: meta item in `repr` must be an identifier
+ --> $DIR/E0565.rs:2:8
+ |
+LL | #[repr("C")]
+ | ^^^
+
+error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0565`.
error[E0586]: inclusive range with no end
- --> $DIR/E0586.rs:3:22
+ --> $DIR/E0586.rs:3:19
|
LL | let x = &tmp[1..=];
- | ^
+ | ^^^ help: use `..` instead
|
- = help: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
+ = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
error: aborting due to previous error
-error[E0599]: no associated item named `NotEvenReal` found for type `Foo` in the current scope
+error[E0599]: no associated item named `NotEvenReal` found for struct `Foo` in the current scope
--> $DIR/E0599.rs:4:20
|
LL | struct Foo;
|
= note: requested on the command line with `-D bogus`
-error: aborting due to previous error
+error[E0602]: unknown lint: `bogus`
+ |
+ = note: requested on the command line with `-D bogus`
+
+error[E0602]: unknown lint: `bogus`
+ |
+ = note: requested on the command line with `-D bogus`
+
+error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0602`.
|
= note: an implementation of `std::ops::AddAssign` might be missing for `&str`
-error[E0599]: no method named `z` found for type `&str` in the current scope
+error[E0599]: no method named `z` found for reference `&str` in the current scope
--> $DIR/error-festival.rs:16:7
|
LL | x.z();
+++ /dev/null
-#![feature(exclusive_range_pattern)]
-
-fn main() {
- match [5..4, 99..105, 43..44] {
- [_, 99.., _] => {},
- //~^ ERROR `X..` range patterns are not supported
- //~| ERROR mismatched types
- _ => {},
- }
-}
+++ /dev/null
-error: `X..` range patterns are not supported
- --> $DIR/exclusive_range_pattern_syntax_collision.rs:5:13
- |
-LL | [_, 99.., _] => {},
- | ^^^^ help: try using the maximum value for the type: `99..MAX`
-
-error[E0308]: mismatched types
- --> $DIR/exclusive_range_pattern_syntax_collision.rs:5:13
- |
-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
- |
- = note: expected struct `std::ops::Range<{integer}>`
- found type `{integer}`
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0308`.
+++ /dev/null
-#![feature(exclusive_range_pattern)]
-
-fn main() {
- match [5..4, 99..105, 43..44] {
- [_, 99..] => {},
- //~^ ERROR `X..` range patterns are not supported
- //~| ERROR pattern requires 2 elements but array has 3
- //~| ERROR mismatched types
- _ => {},
- }
-}
+++ /dev/null
-error: `X..` range patterns are not supported
- --> $DIR/exclusive_range_pattern_syntax_collision2.rs:5:13
- |
-LL | [_, 99..] => {},
- | ^^^^ help: try using the maximum value for the type: `99..MAX`
-
-error[E0527]: pattern requires 2 elements but array has 3
- --> $DIR/exclusive_range_pattern_syntax_collision2.rs:5:9
- |
-LL | [_, 99..] => {},
- | ^^^^^^^^^ expected 3 elements
-
-error[E0308]: mismatched types
- --> $DIR/exclusive_range_pattern_syntax_collision2.rs:5:13
- |
-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
- |
- = note: expected struct `std::ops::Range<{integer}>`
- found type `{integer}`
-
-error: aborting due to 3 previous errors
-
-Some errors have detailed explanations: E0308, E0527.
-For more information about an error, try `rustc --explain E0308`.
+++ /dev/null
-#![feature(exclusive_range_pattern)]
-
-fn main() {
- match [5..4, 99..105, 43..44] {
- [..9, 99..100, _] => {},
- //~^ ERROR `..X` range patterns are not supported
- //~| ERROR mismatched types
- //~| ERROR mismatched types
- //~| ERROR mismatched types
- _ => {},
- }
-}
+++ /dev/null
-error: `..X` range patterns are not supported
- --> $DIR/exclusive_range_pattern_syntax_collision3.rs:5:10
- |
-LL | [..9, 99..100, _] => {},
- | ^^^ help: try using the minimum value for the type: `MIN..9`
-
-error[E0308]: mismatched types
- --> $DIR/exclusive_range_pattern_syntax_collision3.rs:5:12
- |
-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
- |
- = note: expected struct `std::ops::Range<{integer}>`
- found type `{integer}`
-
-error[E0308]: mismatched types
- --> $DIR/exclusive_range_pattern_syntax_collision3.rs:5:15
- |
-LL | match [5..4, 99..105, 43..44] {
- | ----------------------- this expression has type `[std::ops::Range<{integer}>; 3]`
-LL | [..9, 99..100, _] => {},
- | ^^ --- this is of type `{integer}`
- | |
- | expected struct `std::ops::Range`, found integer
- |
- = note: expected struct `std::ops::Range<{integer}>`
- found type `{integer}`
-
-error[E0308]: mismatched types
- --> $DIR/exclusive_range_pattern_syntax_collision3.rs:5:19
- |
-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
- | |
- | this is of type `{integer}`
- |
- = note: expected struct `std::ops::Range<{integer}>`
- found type `{integer}`
-
-error: aborting due to 4 previous errors
-
-For more information about this error, try `rustc --explain E0308`.
error[E0308]: mismatched types
--> $DIR/extern-types-distinct-types.rs:9:5
|
+LL | type A;
+ | ------- the found foreign type
+LL | type B;
+ | ------- the expected foreign type
+...
LL | r
| ^ expected extern type `B`, found extern type `A`
|
// the change when it happens.
//
// At the time of authoring, the attributes here are listed in the
-// order that they occur in libsyntax/feature_gate.rs.
+// order that they occur in `librustc_feature`.
//
// Any builtin attributes that:
//
mod derive {
#[derive(x3300)]
//~^ ERROR cannot find derive macro `x3300` in this scope
+ //~| ERROR cannot find derive macro `x3300` in this scope
union U { f: i32 }
#[derive(x3300)]
//~^ ERROR cannot find derive macro `x3300` in this scope
+ //~| ERROR cannot find derive macro `x3300` in this scope
enum E { }
#[derive(x3300)]
//~^ ERROR cannot find derive macro `x3300` in this scope
+ //~| ERROR cannot find derive macro `x3300` in this scope
struct S;
}
error: cannot find derive macro `x3300` in this scope
- --> $DIR/issue-43106-gating-of-derive-2.rs:12:14
+ --> $DIR/issue-43106-gating-of-derive-2.rs:14:14
|
LL | #[derive(x3300)]
| ^^^^^
error: cannot find derive macro `x3300` in this scope
- --> $DIR/issue-43106-gating-of-derive-2.rs:8:14
+ --> $DIR/issue-43106-gating-of-derive-2.rs:14:14
+ |
+LL | #[derive(x3300)]
+ | ^^^^^
+
+error: cannot find derive macro `x3300` in this scope
+ --> $DIR/issue-43106-gating-of-derive-2.rs:9:14
+ |
+LL | #[derive(x3300)]
+ | ^^^^^
+
+error: cannot find derive macro `x3300` in this scope
+ --> $DIR/issue-43106-gating-of-derive-2.rs:9:14
+ |
+LL | #[derive(x3300)]
+ | ^^^^^
+
+error: cannot find derive macro `x3300` in this scope
+ --> $DIR/issue-43106-gating-of-derive-2.rs:4:14
|
LL | #[derive(x3300)]
| ^^^^^
LL | #[derive(x3300)]
| ^^^^^
-error: aborting due to 3 previous errors
+error: aborting due to 6 previous errors
#[rustc_deprecated()] struct S;
//~^ ERROR stability attributes may not be used outside of the standard library
+ //~| ERROR stability attributes may not be used outside of the standard library
#[rustc_deprecated()] type T = S;
//~^ ERROR stability attributes may not be used outside of the standard library
| ^^^^^^^^^^^^^^^^^^^^^
error[E0734]: stability attributes may not be used outside of the standard library
- --> $DIR/issue-43106-gating-of-rustc_deprecated.rs:22:5
+ --> $DIR/issue-43106-gating-of-rustc_deprecated.rs:19:5
+ |
+LL | #[rustc_deprecated()] struct S;
+ | ^^^^^^^^^^^^^^^^^^^^^
+
+error[E0734]: stability attributes may not be used outside of the standard library
+ --> $DIR/issue-43106-gating-of-rustc_deprecated.rs:23:5
|
LL | #[rustc_deprecated()] type T = S;
| ^^^^^^^^^^^^^^^^^^^^^
error[E0734]: stability attributes may not be used outside of the standard library
- --> $DIR/issue-43106-gating-of-rustc_deprecated.rs:25:5
+ --> $DIR/issue-43106-gating-of-rustc_deprecated.rs:26:5
|
LL | #[rustc_deprecated()] impl S { }
| ^^^^^^^^^^^^^^^^^^^^^
-error: aborting due to 7 previous errors
+error: aborting due to 8 previous errors
For more information about this error, try `rustc --explain E0734`.
#[stable()] struct S;
//~^ ERROR stability attributes may not be used outside of the standard library
+ //~| ERROR stability attributes may not be used outside of the standard library
#[stable()] type T = S;
//~^ ERROR stability attributes may not be used outside of the standard library
| ^^^^^^^^^^^
error[E0734]: stability attributes may not be used outside of the standard library
- --> $DIR/issue-43106-gating-of-stable.rs:22:5
+ --> $DIR/issue-43106-gating-of-stable.rs:19:5
+ |
+LL | #[stable()] struct S;
+ | ^^^^^^^^^^^
+
+error[E0734]: stability attributes may not be used outside of the standard library
+ --> $DIR/issue-43106-gating-of-stable.rs:23:5
|
LL | #[stable()] type T = S;
| ^^^^^^^^^^^
error[E0734]: stability attributes may not be used outside of the standard library
- --> $DIR/issue-43106-gating-of-stable.rs:25:5
+ --> $DIR/issue-43106-gating-of-stable.rs:26:5
|
LL | #[stable()] impl S { }
| ^^^^^^^^^^^
-error: aborting due to 7 previous errors
+error: aborting due to 8 previous errors
For more information about this error, try `rustc --explain E0734`.
#[unstable()] struct S;
//~^ ERROR stability attributes may not be used outside of the standard library
+ //~| ERROR stability attributes may not be used outside of the standard library
#[unstable()] type T = S;
//~^ ERROR stability attributes may not be used outside of the standard library
| ^^^^^^^^^^^^^
error[E0734]: stability attributes may not be used outside of the standard library
- --> $DIR/issue-43106-gating-of-unstable.rs:22:5
+ --> $DIR/issue-43106-gating-of-unstable.rs:19:5
+ |
+LL | #[unstable()] struct S;
+ | ^^^^^^^^^^^^^
+
+error[E0734]: stability attributes may not be used outside of the standard library
+ --> $DIR/issue-43106-gating-of-unstable.rs:23:5
|
LL | #[unstable()] type T = S;
| ^^^^^^^^^^^^^
error[E0734]: stability attributes may not be used outside of the standard library
- --> $DIR/issue-43106-gating-of-unstable.rs:25:5
+ --> $DIR/issue-43106-gating-of-unstable.rs:26:5
|
LL | #[unstable()] impl S { }
| ^^^^^^^^^^^^^
-error: aborting due to 7 previous errors
+error: aborting due to 8 previous errors
For more information about this error, try `rustc --explain E0734`.
#[doc(include="asdf.md")] //~ ERROR: `#[doc(include)]` is experimental
+ //~| ERROR: `#[doc(include)]` is experimental
fn main() {}
= note: for more information, see https://github.com/rust-lang/rust/issues/44732
= help: add `#![feature(external_doc)]` to the crate attributes to enable
-error: aborting due to previous error
+error[E0658]: `#[doc(include)]` is experimental
+ --> $DIR/feature-gate-external_doc.rs:1:1
+ |
+LL | #[doc(include="asdf.md")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: for more information, see https://github.com/rust-lang/rust/issues/44732
+ = help: add `#![feature(external_doc)]` to the crate attributes to enable
+
+error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0658`.
#![warn(nonstandard_style, reason = "the standard should be respected")]
//~^ ERROR lint reasons are experimental
+//~| ERROR lint reasons are experimental
+//~| ERROR lint reasons are experimental
fn main() {}
= note: for more information, see https://github.com/rust-lang/rust/issues/54503
= help: add `#![feature(lint_reasons)]` to the crate attributes to enable
-error: aborting due to previous error
+error[E0658]: lint reasons are experimental
+ --> $DIR/feature-gate-lint-reasons.rs:1:28
+ |
+LL | #![warn(nonstandard_style, reason = "the standard should be respected")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: for more information, see https://github.com/rust-lang/rust/issues/54503
+ = help: add `#![feature(lint_reasons)]` to the crate attributes to enable
+
+error[E0658]: lint reasons are experimental
+ --> $DIR/feature-gate-lint-reasons.rs:1:28
+ |
+LL | #![warn(nonstandard_style, reason = "the standard should be respected")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: for more information, see https://github.com/rust-lang/rust/issues/54503
+ = help: add `#![feature(lint_reasons)]` to the crate attributes to enable
+
+error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0658`.
#[repr(simd)] //~ error: SIMD types are experimental
struct Foo(u64, u64);
-#[repr(C)] //~ warn: conflicting representation hints
+#[repr(C)] //~ ERROR conflicting representation hints
#[repr(simd)] //~ error: SIMD types are experimental
struct Bar(u64, u64);
= note: for more information, see https://github.com/rust-lang/rust/issues/27731
= help: add `#![feature(repr_simd)]` to the crate attributes to enable
-warning[E0566]: conflicting representation hints
+error[E0566]: conflicting representation hints
--> $DIR/feature-gate-repr-simd.rs:4:8
|
LL | #[repr(C)]
LL | #[repr(simd)]
| ^^^^
-error: aborting due to 2 previous errors
+error: aborting due to 3 previous errors
Some errors have detailed explanations: E0566, E0658.
For more information about an error, try `rustc --explain E0566`.
+++ /dev/null
-#![sanitizer_runtime] //~ ERROR the `#[sanitizer_runtime]` attribute is
-
-fn main() {}
+++ /dev/null
-error[E0658]: the `#[sanitizer_runtime]` attribute is used to identify crates that contain the runtime of a sanitizer and will never be stable
- --> $DIR/feature-gate-sanitizer-runtime.rs:1:1
- |
-LL | #![sanitizer_runtime]
- | ^^^^^^^^^^^^^^^^^^^^^
- |
- = help: add `#![feature(sanitizer_runtime)]` to the crate attributes to enable
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0658`.
error[E0716]: temporary value dropped while borrowed
- --> $DIR/auto-trait-regions.rs:44:24
+ --> $DIR/auto-trait-regions.rs:45:24
|
LL | let a = A(&mut true, &mut true, No);
| ^^^^ - temporary value is freed at the end of this statement
= note: consider using a `let` binding to create a longer lived value
error[E0716]: temporary value dropped while borrowed
- --> $DIR/auto-trait-regions.rs:44:35
+ --> $DIR/auto-trait-regions.rs:45:35
|
LL | let a = A(&mut true, &mut true, No);
| ^^^^ - temporary value is freed at the end of this statement
| ^^^^^^^^^^^^^^^
error: higher-ranked subtype error
- --> $DIR/auto-trait-regions.rs:48:5
+ --> $DIR/auto-trait-regions.rs:49:5
|
LL | assert_foo(gen);
| ^^^^^^^^^^^^^^^
};
assert_foo(gen);
//~^ ERROR implementation of `Foo` is not general enough
+ //~| ERROR implementation of `Foo` is not general enough
// Allow impls which matches any lifetime
let x = &OnlyFooIfRef(No);
};
assert_foo(gen);
//~^ ERROR not general enough
+ //~| ERROR not general enough
}
= note: ...but `Foo` is actually implemented for the type `&'1 OnlyFooIfStaticRef`, for some specific lifetime `'1`
error: implementation of `Foo` is not general enough
- --> $DIR/auto-trait-regions.rs:48:5
+ --> $DIR/auto-trait-regions.rs:30:5
+ |
+LL | auto trait Foo {}
+ | ----------------- trait `Foo` defined here
+...
+LL | assert_foo(gen);
+ | ^^^^^^^^^^ implementation of `Foo` is not general enough
+ |
+ = note: `Foo` would have to be implemented for the type `&'0 OnlyFooIfStaticRef`, for any lifetime `'0`...
+ = note: ...but `Foo` is actually implemented for the type `&'1 OnlyFooIfStaticRef`, for some specific lifetime `'1`
+
+error: implementation of `Foo` is not general enough
+ --> $DIR/auto-trait-regions.rs:49:5
+ |
+LL | auto trait Foo {}
+ | ----------------- trait `Foo` defined here
+...
+LL | assert_foo(gen);
+ | ^^^^^^^^^^ implementation of `Foo` is not general enough
+ |
+ = note: `Foo` would have to be implemented for the type `A<'0, '1>`, for any two lifetimes `'0` and `'1`...
+ = note: ...but `Foo` is actually implemented for the type `A<'_, '2>`, for some specific lifetime `'2`
+
+error: implementation of `Foo` is not general enough
+ --> $DIR/auto-trait-regions.rs:49:5
|
LL | auto trait Foo {}
| ----------------- trait `Foo` defined here
= note: `Foo` would have to be implemented for the type `A<'0, '1>`, for any two lifetimes `'0` and `'1`...
= note: ...but `Foo` is actually implemented for the type `A<'_, '2>`, for some specific lifetime `'2`
-error: aborting due to 2 previous errors
+error: aborting due to 4 previous errors
--- /dev/null
+#![feature(half_open_range_patterns)]
+#![feature(exclusive_range_pattern)]
+
+fn main() {
+ match [5..4, 99..105, 43..44] {
+ [_, 99.., _] => {},
+ //~^ ERROR mismatched types
+ _ => {},
+ }
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/exclusive_range_pattern_syntax_collision.rs:6:13
+ |
+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
+ |
+ = note: expected struct `std::ops::Range<{integer}>`
+ found type `{integer}`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+#![feature(half_open_range_patterns)]
+#![feature(exclusive_range_pattern)]
+
+fn main() {
+ match [5..4, 99..105, 43..44] {
+ [_, 99..] => {},
+ //~^ ERROR pattern requires 2 elements but array has 3
+ //~| ERROR mismatched types
+ _ => {},
+ }
+}
--- /dev/null
+error[E0527]: pattern requires 2 elements but array has 3
+ --> $DIR/exclusive_range_pattern_syntax_collision2.rs:6:9
+ |
+LL | [_, 99..] => {},
+ | ^^^^^^^^^ expected 3 elements
+
+error[E0308]: mismatched types
+ --> $DIR/exclusive_range_pattern_syntax_collision2.rs:6:13
+ |
+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
+ |
+ = note: expected struct `std::ops::Range<{integer}>`
+ found type `{integer}`
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0308, E0527.
+For more information about an error, try `rustc --explain E0308`.
--- /dev/null
+#![feature(half_open_range_patterns)]
+#![feature(exclusive_range_pattern)]
+
+fn main() {
+ match [5..4, 99..105, 43..44] {
+ [..9, 99..100, _] => {},
+ //~^ ERROR mismatched types
+ //~| ERROR mismatched types
+ //~| ERROR mismatched types
+ _ => {},
+ }
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/exclusive_range_pattern_syntax_collision3.rs:6:12
+ |
+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
+ |
+ = note: expected struct `std::ops::Range<{integer}>`
+ found type `{integer}`
+
+error[E0308]: mismatched types
+ --> $DIR/exclusive_range_pattern_syntax_collision3.rs:6:15
+ |
+LL | match [5..4, 99..105, 43..44] {
+ | ----------------------- this expression has type `[std::ops::Range<{integer}>; 3]`
+LL | [..9, 99..100, _] => {},
+ | ^^ --- this is of type `{integer}`
+ | |
+ | expected struct `std::ops::Range`, found integer
+ |
+ = note: expected struct `std::ops::Range<{integer}>`
+ found type `{integer}`
+
+error[E0308]: mismatched types
+ --> $DIR/exclusive_range_pattern_syntax_collision3.rs:6:19
+ |
+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
+ | |
+ | this is of type `{integer}`
+ |
+ = note: expected struct `std::ops::Range<{integer}>`
+ found type `{integer}`
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+#![feature(exclusive_range_pattern)]
+
+fn main() {}
+
+#[cfg(FALSE)]
+fn foo() {
+ if let ..=5 = 0 {}
+ //~^ ERROR half-open range patterns are unstable
+ if let ...5 = 0 {}
+ //~^ ERROR half-open range patterns are unstable
+ //~| ERROR range-to patterns with `...` are not allowed
+ if let ..5 = 0 {}
+ //~^ ERROR half-open range patterns are unstable
+ if let 5.. = 0 {}
+ //~^ ERROR half-open range patterns are unstable
+ if let 5..= = 0 {}
+ //~^ ERROR half-open range patterns are unstable
+ //~| ERROR inclusive range with no end
+ if let 5... = 0 {}
+ //~^ ERROR half-open range patterns are unstable
+ //~| ERROR inclusive range with no end
+}
--- /dev/null
+error: range-to patterns with `...` are not allowed
+ --> $DIR/feature-gate-half-open-range-patterns.rs:9:12
+ |
+LL | if let ...5 = 0 {}
+ | ^^^ help: use `..=` instead
+
+error[E0586]: inclusive range with no end
+ --> $DIR/feature-gate-half-open-range-patterns.rs:16:13
+ |
+LL | if let 5..= = 0 {}
+ | ^^^ help: use `..` instead
+ |
+ = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
+
+error[E0586]: inclusive range with no end
+ --> $DIR/feature-gate-half-open-range-patterns.rs:19:13
+ |
+LL | if let 5... = 0 {}
+ | ^^^ help: use `..` instead
+ |
+ = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
+
+error[E0658]: half-open range patterns are unstable
+ --> $DIR/feature-gate-half-open-range-patterns.rs:7:12
+ |
+LL | if let ..=5 = 0 {}
+ | ^^^^
+ |
+ = note: for more information, see https://github.com/rust-lang/rust/issues/67264
+ = help: add `#![feature(half_open_range_patterns)]` to the crate attributes to enable
+
+error[E0658]: half-open range patterns are unstable
+ --> $DIR/feature-gate-half-open-range-patterns.rs:9:12
+ |
+LL | if let ...5 = 0 {}
+ | ^^^^
+ |
+ = note: for more information, see https://github.com/rust-lang/rust/issues/67264
+ = help: add `#![feature(half_open_range_patterns)]` to the crate attributes to enable
+
+error[E0658]: half-open range patterns are unstable
+ --> $DIR/feature-gate-half-open-range-patterns.rs:12:12
+ |
+LL | if let ..5 = 0 {}
+ | ^^^
+ |
+ = note: for more information, see https://github.com/rust-lang/rust/issues/67264
+ = help: add `#![feature(half_open_range_patterns)]` to the crate attributes to enable
+
+error[E0658]: half-open range patterns are unstable
+ --> $DIR/feature-gate-half-open-range-patterns.rs:14:12
+ |
+LL | if let 5.. = 0 {}
+ | ^^^
+ |
+ = note: for more information, see https://github.com/rust-lang/rust/issues/67264
+ = help: add `#![feature(half_open_range_patterns)]` to the crate attributes to enable
+
+error[E0658]: half-open range patterns are unstable
+ --> $DIR/feature-gate-half-open-range-patterns.rs:16:12
+ |
+LL | if let 5..= = 0 {}
+ | ^^^^
+ |
+ = note: for more information, see https://github.com/rust-lang/rust/issues/67264
+ = help: add `#![feature(half_open_range_patterns)]` to the crate attributes to enable
+
+error[E0658]: half-open range patterns are unstable
+ --> $DIR/feature-gate-half-open-range-patterns.rs:19:12
+ |
+LL | if let 5... = 0 {}
+ | ^^^^
+ |
+ = note: for more information, see https://github.com/rust-lang/rust/issues/67264
+ = help: add `#![feature(half_open_range_patterns)]` to the crate attributes to enable
+
+error: aborting due to 9 previous errors
+
+Some errors have detailed explanations: E0586, E0658.
+For more information about an error, try `rustc --explain E0586`.
--- /dev/null
+#![feature(half_open_range_patterns)]
+#![feature(exclusive_range_pattern)]
+
+fn main() {
+ let "a".. = "a"; //~ ERROR only char and numeric types are allowed in range patterns
+ let .."a" = "a"; //~ ERROR only char and numeric types are allowed in range patterns
+ let ..="a" = "a"; //~ ERROR only char and numeric types are allowed in range patterns
+}
--- /dev/null
+error[E0029]: only char and numeric types are allowed in range patterns
+ --> $DIR/half-open-range-pats-bad-types.rs:5:9
+ |
+LL | let "a".. = "a";
+ | ^^^ this is of type `&'static str` but it should be `char` or numeric
+
+error[E0029]: only char and numeric types are allowed in range patterns
+ --> $DIR/half-open-range-pats-bad-types.rs:6:11
+ |
+LL | let .."a" = "a";
+ | ^^^ this is of type `&'static str` but it should be `char` or numeric
+
+error[E0029]: only char and numeric types are allowed in range patterns
+ --> $DIR/half-open-range-pats-bad-types.rs:7:12
+ |
+LL | let ..="a" = "a";
+ | ^^^ this is of type `&'static str` but it should be `char` or numeric
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0029`.
--- /dev/null
+// Test various non-exhaustive matches for `X..`, `..=X` and `..X` ranges.
+
+#![feature(half_open_range_patterns)]
+#![feature(exclusive_range_pattern)]
+#![allow(illegal_floating_point_literal_pattern)]
+
+fn main() {}
+
+macro_rules! m {
+ ($s:expr, $($t:tt)+) => {
+ match $s { $($t)+ => {} }
+ }
+}
+
+fn floats() {
+ m!(0f32, core::f32::NEG_INFINITY..); //~ ERROR non-exhaustive patterns: `_` not covered
+ m!(0f32, ..core::f32::INFINITY); //~ ERROR non-exhaustive patterns: `_` not covered
+}
+
+fn khar() {
+ const ALMOST_MAX: char = '\u{10fffe}';
+ const ALMOST_MIN: char = '\u{1}';
+ const VAL: char = 'a';
+ const VAL_1: char = 'b';
+ const VAL_2: char = 'c';
+ m!('a', ..core::char::MAX); //~ ERROR non-exhaustive patterns
+ m!('a', ..ALMOST_MAX); //~ ERROR non-exhaustive patterns
+ m!('a', ALMOST_MIN..); //~ ERROR non-exhaustive patterns
+ m!('a', ..=ALMOST_MAX); //~ ERROR non-exhaustive patterns
+ m!('a', ..=VAL | VAL_2..); //~ ERROR non-exhaustive patterns
+ m!('a', ..VAL_1 | VAL_2..); //~ ERROR non-exhaustive patterns
+}
+
+mod unsigned {
+ fn u8() {
+ const ALMOST_MAX: u8 = core::u8::MAX - 1;
+ const ALMOST_MIN: u8 = core::u8::MIN + 1;
+ const VAL: u8 = 42;
+ const VAL_1: u8 = VAL + 1;
+ const VAL_2: u8 = VAL + 2;
+ m!(0, ..core::u8::MAX); //~ ERROR non-exhaustive patterns
+ m!(0, ..ALMOST_MAX); //~ ERROR non-exhaustive patterns
+ m!(0, ALMOST_MIN..); //~ ERROR non-exhaustive patterns
+ m!(0, ..=ALMOST_MAX); //~ ERROR non-exhaustive patterns
+ m!(0, ..=VAL | VAL_2..); //~ ERROR non-exhaustive patterns
+ m!(0, ..VAL_1 | VAL_2..); //~ ERROR non-exhaustive patterns
+ }
+ fn u16() {
+ const ALMOST_MAX: u16 = core::u16::MAX - 1;
+ const ALMOST_MIN: u16 = core::u16::MIN + 1;
+ const VAL: u16 = 42;
+ const VAL_1: u16 = VAL + 1;
+ const VAL_2: u16 = VAL + 2;
+ m!(0, ..core::u16::MAX); //~ ERROR non-exhaustive patterns
+ m!(0, ..ALMOST_MAX); //~ ERROR non-exhaustive patterns
+ m!(0, ALMOST_MIN..); //~ ERROR non-exhaustive patterns
+ m!(0, ..=ALMOST_MAX); //~ ERROR non-exhaustive patterns
+ m!(0, ..=VAL | VAL_2..); //~ ERROR non-exhaustive patterns
+ m!(0, ..VAL_1 | VAL_2..); //~ ERROR non-exhaustive patterns
+ }
+ fn u32() {
+ const ALMOST_MAX: u32 = core::u32::MAX - 1;
+ const ALMOST_MIN: u32 = core::u32::MIN + 1;
+ const VAL: u32 = 42;
+ const VAL_1: u32 = VAL + 1;
+ const VAL_2: u32 = VAL + 2;
+ m!(0, ..core::u32::MAX); //~ ERROR non-exhaustive patterns
+ m!(0, ..ALMOST_MAX); //~ ERROR non-exhaustive patterns
+ m!(0, ALMOST_MIN..); //~ ERROR non-exhaustive patterns
+ m!(0, ..=ALMOST_MAX); //~ ERROR non-exhaustive patterns
+ m!(0, ..=VAL | VAL_2..); //~ ERROR non-exhaustive patterns
+ m!(0, ..VAL_1 | VAL_2..); //~ ERROR non-exhaustive patterns
+ }
+ fn u64() {
+ const ALMOST_MAX: u64 = core::u64::MAX - 1;
+ const ALMOST_MIN: u64 = core::u64::MIN + 1;
+ const VAL: u64 = 42;
+ const VAL_1: u64 = VAL + 1;
+ const VAL_2: u64 = VAL + 2;
+ m!(0, ..core::u64::MAX); //~ ERROR non-exhaustive patterns
+ m!(0, ..ALMOST_MAX); //~ ERROR non-exhaustive patterns
+ m!(0, ALMOST_MIN..); //~ ERROR non-exhaustive patterns
+ m!(0, ..=ALMOST_MAX); //~ ERROR non-exhaustive patterns
+ m!(0, ..=VAL | VAL_2..); //~ ERROR non-exhaustive patterns
+ m!(0, ..VAL_1 | VAL_2..); //~ ERROR non-exhaustive patterns
+ }
+ fn u128() {
+ const ALMOST_MAX: u128 = core::u128::MAX - 1;
+ const ALMOST_MIN: u128 = core::u128::MIN + 1;
+ const VAL: u128 = 42;
+ const VAL_1: u128 = VAL + 1;
+ const VAL_2: u128 = VAL + 2;
+ m!(0, ..core::u128::MAX); //~ ERROR non-exhaustive patterns
+ m!(0, ..ALMOST_MAX); //~ ERROR non-exhaustive patterns
+ m!(0, ALMOST_MIN..); //~ ERROR non-exhaustive patterns
+ m!(0, ..=ALMOST_MAX); //~ ERROR non-exhaustive patterns
+ m!(0, ..=VAL | VAL_2..); //~ ERROR non-exhaustive patterns
+ m!(0, ..VAL_1 | VAL_2..); //~ ERROR non-exhaustive patterns
+ }
+}
+
+mod signed {
+ fn i8() {
+ const ALMOST_MAX: i8 = core::i8::MAX - 1;
+ const ALMOST_MIN: i8 = core::i8::MIN + 1;
+ const VAL: i8 = 42;
+ const VAL_1: i8 = VAL + 1;
+ const VAL_2: i8 = VAL + 2;
+ m!(0, ..core::i8::MAX); //~ ERROR non-exhaustive patterns
+ m!(0, ..ALMOST_MAX); //~ ERROR non-exhaustive patterns
+ m!(0, ALMOST_MIN..); //~ ERROR non-exhaustive patterns
+ m!(0, ..=ALMOST_MAX); //~ ERROR non-exhaustive patterns
+ m!(0, ..=VAL | VAL_2..); //~ ERROR non-exhaustive patterns
+ m!(0, ..VAL_1 | VAL_2..); //~ ERROR non-exhaustive patterns
+ }
+ fn i16() {
+ const ALMOST_MAX: i16 = core::i16::MAX - 1;
+ const ALMOST_MIN: i16 = core::i16::MIN + 1;
+ const VAL: i16 = 42;
+ const VAL_1: i16 = VAL + 1;
+ const VAL_2: i16 = VAL + 2;
+ m!(0, ..core::i16::MAX); //~ ERROR non-exhaustive patterns
+ m!(0, ..ALMOST_MAX); //~ ERROR non-exhaustive patterns
+ m!(0, ALMOST_MIN..); //~ ERROR non-exhaustive patterns
+ m!(0, ..=ALMOST_MAX); //~ ERROR non-exhaustive patterns
+ m!(0, ..=VAL | VAL_2..); //~ ERROR non-exhaustive patterns
+ m!(0, ..VAL_1 | VAL_2..); //~ ERROR non-exhaustive patterns
+ }
+ fn i32() {
+ const ALMOST_MAX: i32 = core::i32::MAX - 1;
+ const ALMOST_MIN: i32 = core::i32::MIN + 1;
+ const VAL: i32 = 42;
+ const VAL_1: i32 = VAL + 1;
+ const VAL_2: i32 = VAL + 2;
+ m!(0, ..core::i32::MAX); //~ ERROR non-exhaustive patterns
+ m!(0, ..ALMOST_MAX); //~ ERROR non-exhaustive patterns
+ m!(0, ALMOST_MIN..); //~ ERROR non-exhaustive patterns
+ m!(0, ..=ALMOST_MAX); //~ ERROR non-exhaustive patterns
+ m!(0, ..=VAL | VAL_2..); //~ ERROR non-exhaustive patterns
+ m!(0, ..VAL_1 | VAL_2..); //~ ERROR non-exhaustive patterns
+ }
+ fn i64() {
+ const ALMOST_MAX: i64 = core::i64::MAX - 1;
+ const ALMOST_MIN: i64 = core::i64::MIN + 1;
+ const VAL: i64 = 42;
+ const VAL_1: i64 = VAL + 1;
+ const VAL_2: i64 = VAL + 2;
+ m!(0, ..core::i64::MAX); //~ ERROR non-exhaustive patterns
+ m!(0, ..ALMOST_MAX); //~ ERROR non-exhaustive patterns
+ m!(0, ALMOST_MIN..); //~ ERROR non-exhaustive patterns
+ m!(0, ..=ALMOST_MAX); //~ ERROR non-exhaustive patterns
+ m!(0, ..=VAL | VAL_2..); //~ ERROR non-exhaustive patterns
+ m!(0, ..VAL_1 | VAL_2..); //~ ERROR non-exhaustive patterns
+ }
+ fn i128() {
+ const ALMOST_MAX: i128 = core::i128::MAX - 1;
+ const ALMOST_MIN: i128 = core::i128::MIN + 1;
+ const VAL: i128 = 42;
+ const VAL_1: i128 = VAL + 1;
+ const VAL_2: i128 = VAL + 2;
+ m!(0, ..core::i128::MAX); //~ ERROR non-exhaustive patterns
+ m!(0, ..ALMOST_MAX); //~ ERROR non-exhaustive patterns
+ m!(0, ALMOST_MIN..); //~ ERROR non-exhaustive patterns
+ m!(0, ..=ALMOST_MAX); //~ ERROR non-exhaustive patterns
+ m!(0, ..=VAL | VAL_2..); //~ ERROR non-exhaustive patterns
+ m!(0, ..VAL_1 | VAL_2..); //~ ERROR non-exhaustive patterns
+ }
+}
--- /dev/null
+error[E0004]: non-exhaustive patterns: `_` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:16:8
+ |
+LL | m!(0f32, core::f32::NEG_INFINITY..);
+ | ^^^^ pattern `_` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:17:8
+ |
+LL | m!(0f32, ..core::f32::INFINITY);
+ | ^^^^ pattern `_` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `'\u{10ffff}'` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:26:8
+ |
+LL | m!('a', ..core::char::MAX);
+ | ^^^ pattern `'\u{10ffff}'` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `'\u{10fffe}'..='\u{10ffff}'` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:27:8
+ |
+LL | m!('a', ..ALMOST_MAX);
+ | ^^^ pattern `'\u{10fffe}'..='\u{10ffff}'` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `'\u{0}'` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:28:8
+ |
+LL | m!('a', ALMOST_MIN..);
+ | ^^^ pattern `'\u{0}'` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `'\u{10ffff}'` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:29:8
+ |
+LL | m!('a', ..=ALMOST_MAX);
+ | ^^^ pattern `'\u{10ffff}'` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `'b'` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:30:8
+ |
+LL | m!('a', ..=VAL | VAL_2..);
+ | ^^^ pattern `'b'` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `'b'` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:31:8
+ |
+LL | m!('a', ..VAL_1 | VAL_2..);
+ | ^^^ pattern `'b'` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `std::u8::MAX` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:41:12
+ |
+LL | m!(0, ..core::u8::MAX);
+ | ^ pattern `std::u8::MAX` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `254u8..=std::u8::MAX` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:42:12
+ |
+LL | m!(0, ..ALMOST_MAX);
+ | ^ pattern `254u8..=std::u8::MAX` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `0u8` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:43:12
+ |
+LL | m!(0, ALMOST_MIN..);
+ | ^ pattern `0u8` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `std::u8::MAX` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:44:12
+ |
+LL | m!(0, ..=ALMOST_MAX);
+ | ^ pattern `std::u8::MAX` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `43u8` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:45:12
+ |
+LL | m!(0, ..=VAL | VAL_2..);
+ | ^ pattern `43u8` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `43u8` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:46:12
+ |
+LL | m!(0, ..VAL_1 | VAL_2..);
+ | ^ pattern `43u8` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `std::u16::MAX` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:54:12
+ |
+LL | m!(0, ..core::u16::MAX);
+ | ^ pattern `std::u16::MAX` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `65534u16..=std::u16::MAX` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:55:12
+ |
+LL | m!(0, ..ALMOST_MAX);
+ | ^ pattern `65534u16..=std::u16::MAX` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `0u16` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:56:12
+ |
+LL | m!(0, ALMOST_MIN..);
+ | ^ pattern `0u16` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `std::u16::MAX` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:57:12
+ |
+LL | m!(0, ..=ALMOST_MAX);
+ | ^ pattern `std::u16::MAX` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `43u16` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:58:12
+ |
+LL | m!(0, ..=VAL | VAL_2..);
+ | ^ pattern `43u16` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `43u16` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:59:12
+ |
+LL | m!(0, ..VAL_1 | VAL_2..);
+ | ^ pattern `43u16` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `std::u32::MAX` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:67:12
+ |
+LL | m!(0, ..core::u32::MAX);
+ | ^ pattern `std::u32::MAX` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `4294967294u32..=std::u32::MAX` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:68:12
+ |
+LL | m!(0, ..ALMOST_MAX);
+ | ^ pattern `4294967294u32..=std::u32::MAX` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `0u32` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:69:12
+ |
+LL | m!(0, ALMOST_MIN..);
+ | ^ pattern `0u32` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `std::u32::MAX` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:70:12
+ |
+LL | m!(0, ..=ALMOST_MAX);
+ | ^ pattern `std::u32::MAX` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `43u32` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:71:12
+ |
+LL | m!(0, ..=VAL | VAL_2..);
+ | ^ pattern `43u32` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `43u32` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:72:12
+ |
+LL | m!(0, ..VAL_1 | VAL_2..);
+ | ^ pattern `43u32` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `std::u64::MAX` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:80:12
+ |
+LL | m!(0, ..core::u64::MAX);
+ | ^ pattern `std::u64::MAX` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `18446744073709551614u64..=std::u64::MAX` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:81:12
+ |
+LL | m!(0, ..ALMOST_MAX);
+ | ^ pattern `18446744073709551614u64..=std::u64::MAX` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `0u64` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:82:12
+ |
+LL | m!(0, ALMOST_MIN..);
+ | ^ pattern `0u64` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `std::u64::MAX` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:83:12
+ |
+LL | m!(0, ..=ALMOST_MAX);
+ | ^ pattern `std::u64::MAX` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `43u64` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:84:12
+ |
+LL | m!(0, ..=VAL | VAL_2..);
+ | ^ pattern `43u64` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `43u64` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:85:12
+ |
+LL | m!(0, ..VAL_1 | VAL_2..);
+ | ^ pattern `43u64` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `std::u128::MAX` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:93:12
+ |
+LL | m!(0, ..core::u128::MAX);
+ | ^ pattern `std::u128::MAX` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `340282366920938463463374607431768211454u128..=std::u128::MAX` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:94:12
+ |
+LL | m!(0, ..ALMOST_MAX);
+ | ^ pattern `340282366920938463463374607431768211454u128..=std::u128::MAX` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `0u128` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:95:12
+ |
+LL | m!(0, ALMOST_MIN..);
+ | ^ pattern `0u128` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `std::u128::MAX` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:96:12
+ |
+LL | m!(0, ..=ALMOST_MAX);
+ | ^ pattern `std::u128::MAX` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `43u128` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:97:12
+ |
+LL | m!(0, ..=VAL | VAL_2..);
+ | ^ pattern `43u128` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `43u128` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:98:12
+ |
+LL | m!(0, ..VAL_1 | VAL_2..);
+ | ^ pattern `43u128` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `std::i8::MAX` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:109:12
+ |
+LL | m!(0, ..core::i8::MAX);
+ | ^ pattern `std::i8::MAX` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `126i8..=std::i8::MAX` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:110:12
+ |
+LL | m!(0, ..ALMOST_MAX);
+ | ^ pattern `126i8..=std::i8::MAX` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `std::i8::MIN` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:111:12
+ |
+LL | m!(0, ALMOST_MIN..);
+ | ^ pattern `std::i8::MIN` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `std::i8::MAX` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:112:12
+ |
+LL | m!(0, ..=ALMOST_MAX);
+ | ^ pattern `std::i8::MAX` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `43i8` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:113:12
+ |
+LL | m!(0, ..=VAL | VAL_2..);
+ | ^ pattern `43i8` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `43i8` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:114:12
+ |
+LL | m!(0, ..VAL_1 | VAL_2..);
+ | ^ pattern `43i8` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `std::i16::MAX` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:122:12
+ |
+LL | m!(0, ..core::i16::MAX);
+ | ^ pattern `std::i16::MAX` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `32766i16..=std::i16::MAX` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:123:12
+ |
+LL | m!(0, ..ALMOST_MAX);
+ | ^ pattern `32766i16..=std::i16::MAX` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `std::i16::MIN` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:124:12
+ |
+LL | m!(0, ALMOST_MIN..);
+ | ^ pattern `std::i16::MIN` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `std::i16::MAX` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:125:12
+ |
+LL | m!(0, ..=ALMOST_MAX);
+ | ^ pattern `std::i16::MAX` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `43i16` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:126:12
+ |
+LL | m!(0, ..=VAL | VAL_2..);
+ | ^ pattern `43i16` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `43i16` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:127:12
+ |
+LL | m!(0, ..VAL_1 | VAL_2..);
+ | ^ pattern `43i16` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `std::i32::MAX` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:135:12
+ |
+LL | m!(0, ..core::i32::MAX);
+ | ^ pattern `std::i32::MAX` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `2147483646i32..=std::i32::MAX` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:136:12
+ |
+LL | m!(0, ..ALMOST_MAX);
+ | ^ pattern `2147483646i32..=std::i32::MAX` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `std::i32::MIN` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:137:12
+ |
+LL | m!(0, ALMOST_MIN..);
+ | ^ pattern `std::i32::MIN` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `std::i32::MAX` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:138:12
+ |
+LL | m!(0, ..=ALMOST_MAX);
+ | ^ pattern `std::i32::MAX` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `43i32` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:139:12
+ |
+LL | m!(0, ..=VAL | VAL_2..);
+ | ^ pattern `43i32` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `43i32` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:140:12
+ |
+LL | m!(0, ..VAL_1 | VAL_2..);
+ | ^ pattern `43i32` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `std::i64::MAX` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:148:12
+ |
+LL | m!(0, ..core::i64::MAX);
+ | ^ pattern `std::i64::MAX` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `9223372036854775806i64..=std::i64::MAX` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:149:12
+ |
+LL | m!(0, ..ALMOST_MAX);
+ | ^ pattern `9223372036854775806i64..=std::i64::MAX` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `std::i64::MIN` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:150:12
+ |
+LL | m!(0, ALMOST_MIN..);
+ | ^ pattern `std::i64::MIN` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `std::i64::MAX` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:151:12
+ |
+LL | m!(0, ..=ALMOST_MAX);
+ | ^ pattern `std::i64::MAX` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `43i64` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:152:12
+ |
+LL | m!(0, ..=VAL | VAL_2..);
+ | ^ pattern `43i64` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `43i64` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:153:12
+ |
+LL | m!(0, ..VAL_1 | VAL_2..);
+ | ^ pattern `43i64` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `std::i128::MAX` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:161:12
+ |
+LL | m!(0, ..core::i128::MAX);
+ | ^ pattern `std::i128::MAX` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `170141183460469231731687303715884105726i128..=std::i128::MAX` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:162:12
+ |
+LL | m!(0, ..ALMOST_MAX);
+ | ^ pattern `170141183460469231731687303715884105726i128..=std::i128::MAX` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `std::i128::MIN` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:163:12
+ |
+LL | m!(0, ALMOST_MIN..);
+ | ^ pattern `std::i128::MIN` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `std::i128::MAX` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:164:12
+ |
+LL | m!(0, ..=ALMOST_MAX);
+ | ^ pattern `std::i128::MAX` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `43i128` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:165:12
+ |
+LL | m!(0, ..=VAL | VAL_2..);
+ | ^ pattern `43i128` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: `43i128` not covered
+ --> $DIR/half-open-range-pats-exhaustive-fail.rs:166:12
+ |
+LL | m!(0, ..VAL_1 | VAL_2..);
+ | ^ pattern `43i128` not covered
+ |
+ = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error: aborting due to 68 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
--- /dev/null
+// check-pass
+
+// Test various exhaustive matches for `X..`, `..=X` and `..X` ranges.
+
+#![feature(half_open_range_patterns)]
+#![feature(exclusive_range_pattern)]
+
+fn main() {}
+
+macro_rules! m {
+ ($s:expr, $($t:tt)+) => {
+ match $s { $($t)+ => {} }
+ }
+}
+
+macro_rules! test_int {
+ ($s:expr, $min:path, $max:path) => {
+ m!($s, $min..);
+ m!($s, $min..5 | 5..);
+ m!($s, ..5 | 5..);
+ m!($s, ..=4 | 5..);
+ m!($s, ..=$max);
+ m!($s, ..$max | $max);
+ m!(($s, true), (..5, true) | (5.., true) | ($min.., false));
+ }
+}
+
+fn unsigned_int() {
+ test_int!(0u8, core::u8::MIN, core::u8::MAX);
+ test_int!(0u16, core::u16::MIN, core::u16::MAX);
+ test_int!(0u32, core::u32::MIN, core::u32::MAX);
+ test_int!(0u64, core::u64::MIN, core::u64::MAX);
+ test_int!(0u128, core::u128::MIN, core::u128::MAX);
+}
+
+fn signed_int() {
+ test_int!(0i8, core::i8::MIN, core::i8::MAX);
+ test_int!(0i16, core::i16::MIN, core::i16::MAX);
+ test_int!(0i32, core::i32::MIN, core::i32::MAX);
+ test_int!(0i64, core::i64::MIN, core::i64::MAX);
+ test_int!(0i128, core::i128::MIN, core::i128::MAX);
+}
+
+fn khar() {
+ m!('a', ..=core::char::MAX);
+ m!('a', '\u{0}'..);
+ m!('a', ..='\u{D7FF}' | '\u{E000}'..);
+ m!('a', ..'\u{D7FF}' | '\u{D7FF}' | '\u{E000}'..);
+}
--- /dev/null
+#![feature(half_open_range_patterns)]
+#![feature(exclusive_range_pattern)]
+#![allow(illegal_floating_point_literal_pattern)]
+
+macro_rules! m {
+ ($s:expr, $($t:tt)+) => {
+ match $s { $($t)+ => {} }
+ }
+}
+
+fn main() {
+ m!(0, ..core::u8::MIN);
+ //~^ ERROR lower range bound must be less than upper
+ //~| ERROR lower range bound must be less than upper
+ m!(0, ..core::u16::MIN);
+ //~^ ERROR lower range bound must be less than upper
+ //~| ERROR lower range bound must be less than upper
+ m!(0, ..core::u32::MIN);
+ //~^ ERROR lower range bound must be less than upper
+ //~| ERROR lower range bound must be less than upper
+ m!(0, ..core::u64::MIN);
+ //~^ ERROR lower range bound must be less than upper
+ //~| ERROR lower range bound must be less than upper
+ m!(0, ..core::u128::MIN);
+ //~^ ERROR lower range bound must be less than upper
+ //~| ERROR lower range bound must be less than upper
+
+ m!(0, ..core::i8::MIN);
+ //~^ ERROR lower range bound must be less than upper
+ //~| ERROR lower range bound must be less than upper
+ m!(0, ..core::i16::MIN);
+ //~^ ERROR lower range bound must be less than upper
+ //~| ERROR lower range bound must be less than upper
+ m!(0, ..core::i32::MIN);
+ //~^ ERROR lower range bound must be less than upper
+ //~| ERROR lower range bound must be less than upper
+ m!(0, ..core::i64::MIN);
+ //~^ ERROR lower range bound must be less than upper
+ //~| ERROR lower range bound must be less than upper
+ m!(0, ..core::i128::MIN);
+ //~^ ERROR lower range bound must be less than upper
+ //~| ERROR lower range bound must be less than upper
+
+ m!(0f32, ..core::f32::NEG_INFINITY);
+ //~^ ERROR lower range bound must be less than upper
+ //~| ERROR lower range bound must be less than upper
+ m!(0f64, ..core::f64::NEG_INFINITY);
+ //~^ ERROR lower range bound must be less than upper
+ //~| ERROR lower range bound must be less than upper
+
+ m!('a', ..'\u{0}');
+ //~^ ERROR lower range bound must be less than upper
+ //~| ERROR lower range bound must be less than upper
+}
--- /dev/null
+error[E0579]: lower range bound must be less than upper
+ --> $DIR/half-open-range-pats-hair-lower-empty.rs:12:11
+ |
+LL | m!(0, ..core::u8::MIN);
+ | ^^^^^^^^^^^^^^^
+
+error[E0579]: lower range bound must be less than upper
+ --> $DIR/half-open-range-pats-hair-lower-empty.rs:15:11
+ |
+LL | m!(0, ..core::u16::MIN);
+ | ^^^^^^^^^^^^^^^^
+
+error[E0579]: lower range bound must be less than upper
+ --> $DIR/half-open-range-pats-hair-lower-empty.rs:18:11
+ |
+LL | m!(0, ..core::u32::MIN);
+ | ^^^^^^^^^^^^^^^^
+
+error[E0579]: lower range bound must be less than upper
+ --> $DIR/half-open-range-pats-hair-lower-empty.rs:21:11
+ |
+LL | m!(0, ..core::u64::MIN);
+ | ^^^^^^^^^^^^^^^^
+
+error[E0579]: lower range bound must be less than upper
+ --> $DIR/half-open-range-pats-hair-lower-empty.rs:24:11
+ |
+LL | m!(0, ..core::u128::MIN);
+ | ^^^^^^^^^^^^^^^^^
+
+error[E0579]: lower range bound must be less than upper
+ --> $DIR/half-open-range-pats-hair-lower-empty.rs:28:11
+ |
+LL | m!(0, ..core::i8::MIN);
+ | ^^^^^^^^^^^^^^^
+
+error[E0579]: lower range bound must be less than upper
+ --> $DIR/half-open-range-pats-hair-lower-empty.rs:31:11
+ |
+LL | m!(0, ..core::i16::MIN);
+ | ^^^^^^^^^^^^^^^^
+
+error[E0579]: lower range bound must be less than upper
+ --> $DIR/half-open-range-pats-hair-lower-empty.rs:34:11
+ |
+LL | m!(0, ..core::i32::MIN);
+ | ^^^^^^^^^^^^^^^^
+
+error[E0579]: lower range bound must be less than upper
+ --> $DIR/half-open-range-pats-hair-lower-empty.rs:37:11
+ |
+LL | m!(0, ..core::i64::MIN);
+ | ^^^^^^^^^^^^^^^^
+
+error[E0579]: lower range bound must be less than upper
+ --> $DIR/half-open-range-pats-hair-lower-empty.rs:40:11
+ |
+LL | m!(0, ..core::i128::MIN);
+ | ^^^^^^^^^^^^^^^^^
+
+error[E0579]: lower range bound must be less than upper
+ --> $DIR/half-open-range-pats-hair-lower-empty.rs:44:14
+ |
+LL | m!(0f32, ..core::f32::NEG_INFINITY);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0579]: lower range bound must be less than upper
+ --> $DIR/half-open-range-pats-hair-lower-empty.rs:47:14
+ |
+LL | m!(0f64, ..core::f64::NEG_INFINITY);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0579]: lower range bound must be less than upper
+ --> $DIR/half-open-range-pats-hair-lower-empty.rs:51:13
+ |
+LL | m!('a', ..'\u{0}');
+ | ^^^^^^^^^
+
+error[E0579]: lower range bound must be less than upper
+ --> $DIR/half-open-range-pats-hair-lower-empty.rs:12:11
+ |
+LL | m!(0, ..core::u8::MIN);
+ | ^^^^^^^^^^^^^^^
+
+error[E0579]: lower range bound must be less than upper
+ --> $DIR/half-open-range-pats-hair-lower-empty.rs:15:11
+ |
+LL | m!(0, ..core::u16::MIN);
+ | ^^^^^^^^^^^^^^^^
+
+error[E0579]: lower range bound must be less than upper
+ --> $DIR/half-open-range-pats-hair-lower-empty.rs:18:11
+ |
+LL | m!(0, ..core::u32::MIN);
+ | ^^^^^^^^^^^^^^^^
+
+error[E0579]: lower range bound must be less than upper
+ --> $DIR/half-open-range-pats-hair-lower-empty.rs:21:11
+ |
+LL | m!(0, ..core::u64::MIN);
+ | ^^^^^^^^^^^^^^^^
+
+error[E0579]: lower range bound must be less than upper
+ --> $DIR/half-open-range-pats-hair-lower-empty.rs:24:11
+ |
+LL | m!(0, ..core::u128::MIN);
+ | ^^^^^^^^^^^^^^^^^
+
+error[E0579]: lower range bound must be less than upper
+ --> $DIR/half-open-range-pats-hair-lower-empty.rs:28:11
+ |
+LL | m!(0, ..core::i8::MIN);
+ | ^^^^^^^^^^^^^^^
+
+error[E0579]: lower range bound must be less than upper
+ --> $DIR/half-open-range-pats-hair-lower-empty.rs:31:11
+ |
+LL | m!(0, ..core::i16::MIN);
+ | ^^^^^^^^^^^^^^^^
+
+error[E0579]: lower range bound must be less than upper
+ --> $DIR/half-open-range-pats-hair-lower-empty.rs:34:11
+ |
+LL | m!(0, ..core::i32::MIN);
+ | ^^^^^^^^^^^^^^^^
+
+error[E0579]: lower range bound must be less than upper
+ --> $DIR/half-open-range-pats-hair-lower-empty.rs:37:11
+ |
+LL | m!(0, ..core::i64::MIN);
+ | ^^^^^^^^^^^^^^^^
+
+error[E0579]: lower range bound must be less than upper
+ --> $DIR/half-open-range-pats-hair-lower-empty.rs:40:11
+ |
+LL | m!(0, ..core::i128::MIN);
+ | ^^^^^^^^^^^^^^^^^
+
+error[E0579]: lower range bound must be less than upper
+ --> $DIR/half-open-range-pats-hair-lower-empty.rs:44:14
+ |
+LL | m!(0f32, ..core::f32::NEG_INFINITY);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0579]: lower range bound must be less than upper
+ --> $DIR/half-open-range-pats-hair-lower-empty.rs:47:14
+ |
+LL | m!(0f64, ..core::f64::NEG_INFINITY);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0579]: lower range bound must be less than upper
+ --> $DIR/half-open-range-pats-hair-lower-empty.rs:51:13
+ |
+LL | m!('a', ..'\u{0}');
+ | ^^^^^^^^^
+
+error: aborting due to 26 previous errors
+
+For more information about this error, try `rustc --explain E0579`.
--- /dev/null
+// Test that `...X` range-to patterns are syntactically invalid.
+//
+// See https://github.com/rust-lang/rust/pull/67258#issuecomment-565656155
+// for the reason why. To summarize, we might want to introduce `...expr` as
+// an expression form for splatting (or "untupling") in an expression context.
+// While there is no syntactic ambiguity with `...X` in a pattern context,
+// there's a potential confusion factor here, and we would prefer to keep patterns
+// and expressions in-sync. As such, we do not allow `...X` in patterns either.
+
+#![feature(half_open_range_patterns)]
+
+fn main() {}
+
+#[cfg(FALSE)]
+fn syntax() {
+ match scrutinee {
+ ...X => {} //~ ERROR range-to patterns with `...` are not allowed
+ ...0 => {} //~ ERROR range-to patterns with `...` are not allowed
+ ...'a' => {} //~ ERROR range-to patterns with `...` are not allowed
+ ...0.0f32 => {} //~ ERROR range-to patterns with `...` are not allowed
+ }
+}
+
+fn syntax2() {
+ macro_rules! mac {
+ ($e:expr) => {
+ let ...$e; //~ ERROR range-to patterns with `...` are not allowed
+ }
+ }
+
+ mac!(0);
+}
--- /dev/null
+error: range-to patterns with `...` are not allowed
+ --> $DIR/half-open-range-pats-inclusive-dotdotdot-bad-syntax.rs:17:9
+ |
+LL | ...X => {}
+ | ^^^ help: use `..=` instead
+
+error: range-to patterns with `...` are not allowed
+ --> $DIR/half-open-range-pats-inclusive-dotdotdot-bad-syntax.rs:18:9
+ |
+LL | ...0 => {}
+ | ^^^ help: use `..=` instead
+
+error: range-to patterns with `...` are not allowed
+ --> $DIR/half-open-range-pats-inclusive-dotdotdot-bad-syntax.rs:19:9
+ |
+LL | ...'a' => {}
+ | ^^^ help: use `..=` instead
+
+error: range-to patterns with `...` are not allowed
+ --> $DIR/half-open-range-pats-inclusive-dotdotdot-bad-syntax.rs:20:9
+ |
+LL | ...0.0f32 => {}
+ | ^^^ help: use `..=` instead
+
+error: range-to patterns with `...` are not allowed
+ --> $DIR/half-open-range-pats-inclusive-dotdotdot-bad-syntax.rs:27:17
+ |
+LL | let ...$e;
+ | ^^^ help: use `..=` instead
+...
+LL | mac!(0);
+ | -------- in this macro invocation
+
+error: aborting due to 5 previous errors
+
--- /dev/null
+// Test `X...` and `X..=` range patterns not being allowed syntactically.
+// FIXME(Centril): perhaps these should be semantic restrictions.
+
+#![feature(half_open_range_patterns)]
+
+fn main() {}
+
+#[cfg(FALSE)]
+fn foo() {
+ if let 0... = 1 {} //~ ERROR inclusive range with no end
+ if let 0..= = 1 {} //~ ERROR inclusive range with no end
+ const X: u8 = 0;
+ if let X... = 1 {} //~ ERROR inclusive range with no end
+ if let X..= = 1 {} //~ ERROR inclusive range with no end
+}
+
+fn bar() {
+ macro_rules! mac {
+ ($e:expr) => {
+ let $e...; //~ ERROR inclusive range with no end
+ let $e..=; //~ ERROR inclusive range with no end
+ }
+ }
+
+ mac!(0);
+}
--- /dev/null
+error[E0586]: inclusive range with no end
+ --> $DIR/half-open-range-pats-inclusive-no-end.rs:10:13
+ |
+LL | if let 0... = 1 {}
+ | ^^^ help: use `..` instead
+ |
+ = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
+
+error[E0586]: inclusive range with no end
+ --> $DIR/half-open-range-pats-inclusive-no-end.rs:11:13
+ |
+LL | if let 0..= = 1 {}
+ | ^^^ help: use `..` instead
+ |
+ = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
+
+error[E0586]: inclusive range with no end
+ --> $DIR/half-open-range-pats-inclusive-no-end.rs:13:13
+ |
+LL | if let X... = 1 {}
+ | ^^^ help: use `..` instead
+ |
+ = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
+
+error[E0586]: inclusive range with no end
+ --> $DIR/half-open-range-pats-inclusive-no-end.rs:14:13
+ |
+LL | if let X..= = 1 {}
+ | ^^^ help: use `..` instead
+ |
+ = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
+
+error[E0586]: inclusive range with no end
+ --> $DIR/half-open-range-pats-inclusive-no-end.rs:20:19
+ |
+LL | let $e...;
+ | ^^^ help: use `..` instead
+...
+LL | mac!(0);
+ | -------- in this macro invocation
+ |
+ = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
+
+error[E0586]: inclusive range with no end
+ --> $DIR/half-open-range-pats-inclusive-no-end.rs:21:19
+ |
+LL | let $e..=;
+ | ^^^ help: use `..` instead
+...
+LL | mac!(0);
+ | -------- in this macro invocation
+ |
+ = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0586`.
--- /dev/null
+#![feature(half_open_range_patterns)]
+
+fn main() {}
+
+#[cfg(FALSE)]
+fn syntax() {
+ match &0 {
+ &0.. | _ => {}
+ //~^ ERROR the range pattern here has ambiguous interpretation
+ &0..= | _ => {}
+ //~^ ERROR the range pattern here has ambiguous interpretation
+ //~| ERROR inclusive range with no end
+ &0... | _ => {}
+ //~^ ERROR inclusive range with no end
+ }
+
+ match &0 {
+ &..0 | _ => {}
+ //~^ ERROR the range pattern here has ambiguous interpretation
+ &..=0 | _ => {}
+ //~^ ERROR the range pattern here has ambiguous interpretation
+ &...0 | _ => {}
+ //~^ ERROR the range pattern here has ambiguous interpretation
+ //~| ERROR range-to patterns with `...` are not allowed
+ }
+}
--- /dev/null
+error: the range pattern here has ambiguous interpretation
+ --> $DIR/half-open-range-pats-ref-ambiguous-interp.rs:8:10
+ |
+LL | &0.. | _ => {}
+ | ^^^ help: add parentheses to clarify the precedence: `(0 ..)`
+
+error[E0586]: inclusive range with no end
+ --> $DIR/half-open-range-pats-ref-ambiguous-interp.rs:10:11
+ |
+LL | &0..= | _ => {}
+ | ^^^ help: use `..` instead
+ |
+ = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
+
+error: the range pattern here has ambiguous interpretation
+ --> $DIR/half-open-range-pats-ref-ambiguous-interp.rs:10:10
+ |
+LL | &0..= | _ => {}
+ | ^^^^ help: add parentheses to clarify the precedence: `(0 ..=)`
+
+error[E0586]: inclusive range with no end
+ --> $DIR/half-open-range-pats-ref-ambiguous-interp.rs:13:11
+ |
+LL | &0... | _ => {}
+ | ^^^ help: use `..` instead
+ |
+ = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
+
+error: the range pattern here has ambiguous interpretation
+ --> $DIR/half-open-range-pats-ref-ambiguous-interp.rs:18:10
+ |
+LL | &..0 | _ => {}
+ | ^^^ help: add parentheses to clarify the precedence: `(..0)`
+
+error: the range pattern here has ambiguous interpretation
+ --> $DIR/half-open-range-pats-ref-ambiguous-interp.rs:20:10
+ |
+LL | &..=0 | _ => {}
+ | ^^^^ help: add parentheses to clarify the precedence: `(..=0)`
+
+error: range-to patterns with `...` are not allowed
+ --> $DIR/half-open-range-pats-ref-ambiguous-interp.rs:22:10
+ |
+LL | &...0 | _ => {}
+ | ^^^ help: use `..=` instead
+
+error: the range pattern here has ambiguous interpretation
+ --> $DIR/half-open-range-pats-ref-ambiguous-interp.rs:22:10
+ |
+LL | &...0 | _ => {}
+ | ^^^^ help: add parentheses to clarify the precedence: `(..=0)`
+
+error: aborting due to 8 previous errors
+
+For more information about this error, try `rustc --explain E0586`.
--- /dev/null
+// run-pass
+
+// Test half-open range patterns against their expression equivalents
+// via `.contains(...)` and make sure the dynamic semantics match.
+
+#![feature(half_open_range_patterns)]
+#![feature(exclusive_range_pattern)]
+#![allow(illegal_floating_point_literal_pattern)]
+#![allow(unreachable_patterns)]
+
+macro_rules! yes {
+ ($scrutinee:expr, $($t:tt)+) => {
+ {
+ let m = match $scrutinee { $($t)+ => true, _ => false, };
+ let c = ($($t)+).contains(&$scrutinee);
+ assert_eq!(m, c);
+ m
+ }
+ }
+}
+
+fn range_to_inclusive() {
+ // `..=X` (`RangeToInclusive`-equivalent):
+ //---------------------------------------
+
+ // u8; `..=X`
+ assert!(yes!(core::u8::MIN, ..=core::u8::MIN));
+ assert!(yes!(core::u8::MIN, ..=5));
+ assert!(yes!(5u8, ..=5));
+ assert!(!yes!(6u8, ..=5));
+
+ // i16; `..=X`
+ assert!(yes!(core::i16::MIN, ..=core::i16::MIN));
+ assert!(yes!(core::i16::MIN, ..=0));
+ assert!(yes!(core::i16::MIN, ..=-5));
+ assert!(yes!(-5, ..=-5));
+ assert!(!yes!(-4, ..=-5));
+
+ // char; `..=X`
+ assert!(yes!('\u{0}', ..='\u{0}'));
+ assert!(yes!('\u{0}', ..='a'));
+ assert!(yes!('a', ..='a'));
+ assert!(!yes!('b', ..='a'));
+
+ // f32; `..=X`
+ assert!(yes!(core::f32::NEG_INFINITY, ..=core::f32::NEG_INFINITY));
+ assert!(yes!(core::f32::NEG_INFINITY, ..=1.0f32));
+ assert!(yes!(1.5f32, ..=1.5f32));
+ assert!(!yes!(1.6f32, ..=-1.5f32));
+
+ // f64; `..=X`
+ assert!(yes!(core::f64::NEG_INFINITY, ..=core::f64::NEG_INFINITY));
+ assert!(yes!(core::f64::NEG_INFINITY, ..=1.0f64));
+ assert!(yes!(1.5f64, ..=1.5f64));
+ assert!(!yes!(1.6f64, ..=-1.5f64));
+}
+
+fn range_to() {
+ // `..X` (`RangeTo`-equivalent):
+ //-----------------------------
+
+ // u8; `..X`
+ assert!(yes!(0u8, ..1));
+ assert!(yes!(0u8, ..5));
+ assert!(!yes!(5u8, ..5));
+ assert!(!yes!(6u8, ..5));
+
+ // u8; `..X`
+ const NU8: u8 = core::u8::MIN + 1;
+ assert!(yes!(core::u8::MIN, ..NU8));
+ assert!(yes!(0u8, ..5));
+ assert!(!yes!(5u8, ..5));
+ assert!(!yes!(6u8, ..5));
+
+ // i16; `..X`
+ const NI16: i16 = core::i16::MIN + 1;
+ assert!(yes!(core::i16::MIN, ..NI16));
+ assert!(yes!(core::i16::MIN, ..5));
+ assert!(yes!(-6, ..-5));
+ assert!(!yes!(-5, ..-5));
+
+ // char; `..X`
+ assert!(yes!('\u{0}', ..'\u{1}'));
+ assert!(yes!('\u{0}', ..'a'));
+ assert!(yes!('a', ..'b'));
+ assert!(!yes!('a', ..'a'));
+ assert!(!yes!('b', ..'a'));
+
+ // f32; `..X`
+ assert!(yes!(core::f32::NEG_INFINITY, ..1.0f32));
+ assert!(!yes!(1.5f32, ..1.5f32));
+ const E32: f32 = 1.5f32 + core::f32::EPSILON;
+ assert!(yes!(1.5f32, ..E32));
+ assert!(!yes!(1.6f32, ..1.5f32));
+
+ // f64; `..X`
+ assert!(yes!(core::f64::NEG_INFINITY, ..1.0f64));
+ assert!(!yes!(1.5f64, ..1.5f64));
+ const E64: f64 = 1.5f64 + core::f64::EPSILON;
+ assert!(yes!(1.5f64, ..E64));
+ assert!(!yes!(1.6f64, ..1.5f64));
+}
+
+fn range_from() {
+ // `X..` (`RangeFrom`-equivalent):
+ //--------------------------------
+
+ // u8; `X..`
+ assert!(yes!(core::u8::MIN, core::u8::MIN..));
+ assert!(yes!(core::u8::MAX, core::u8::MIN..));
+ assert!(!yes!(core::u8::MIN, 1..));
+ assert!(!yes!(4, 5..));
+ assert!(yes!(5, 5..));
+ assert!(yes!(6, 5..));
+ assert!(yes!(core::u8::MAX, core::u8::MAX..));
+
+ // i16; `X..`
+ assert!(yes!(core::i16::MIN, core::i16::MIN..));
+ assert!(yes!(core::i16::MAX, core::i16::MIN..));
+ const NI16: i16 = core::i16::MIN + 1;
+ assert!(!yes!(core::i16::MIN, NI16..));
+ assert!(!yes!(-4, 5..));
+ assert!(yes!(-4, -4..));
+ assert!(yes!(-3, -4..));
+ assert!(yes!(core::i16::MAX, core::i16::MAX..));
+
+ // char; `X..`
+ assert!(yes!('\u{0}', '\u{0}'..));
+ assert!(yes!(core::char::MAX, '\u{0}'..));
+ assert!(yes!('a', 'a'..));
+ assert!(yes!('b', 'a'..));
+ assert!(!yes!('a', 'b'..));
+ assert!(yes!(core::char::MAX, core::char::MAX..));
+
+ // f32; `X..`
+ assert!(yes!(core::f32::NEG_INFINITY, core::f32::NEG_INFINITY..));
+ assert!(yes!(core::f32::INFINITY, core::f32::NEG_INFINITY..));
+ assert!(!yes!(core::f32::NEG_INFINITY, 1.0f32..));
+ assert!(yes!(core::f32::INFINITY, 1.0f32..));
+ assert!(!yes!(1.0f32 - core::f32::EPSILON, 1.0f32..));
+ assert!(yes!(1.0f32, 1.0f32..));
+ assert!(yes!(core::f32::INFINITY, 1.0f32..));
+ assert!(yes!(core::f32::INFINITY, core::f32::INFINITY..));
+
+ // f64; `X..`
+ assert!(yes!(core::f64::NEG_INFINITY, core::f64::NEG_INFINITY..));
+ assert!(yes!(core::f64::INFINITY, core::f64::NEG_INFINITY..));
+ assert!(!yes!(core::f64::NEG_INFINITY, 1.0f64..));
+ assert!(yes!(core::f64::INFINITY, 1.0f64..));
+ assert!(!yes!(1.0f64 - core::f64::EPSILON, 1.0f64..));
+ assert!(yes!(1.0f64, 1.0f64..));
+ assert!(yes!(core::f64::INFINITY, 1.0f64..));
+ assert!(yes!(core::f64::INFINITY, core::f64::INFINITY..));
+}
+
+fn main() {
+ range_to_inclusive();
+ range_to();
+ range_from();
+}
--- /dev/null
+// check-pass
+
+// Test the parsing of half-open ranges.
+
+#![feature(exclusive_range_pattern)]
+#![feature(half_open_range_patterns)]
+
+fn main() {}
+
+#[cfg(FALSE)]
+fn syntax() {
+ match scrutinee {
+ X.. | 0.. | 'a'.. | 0.0f32.. => {}
+ ..=X | ..X => {}
+ ..=0 | ..0 => {}
+ ..='a' | ..'a' => {}
+ ..=0.0f32 | ..0.0f32 => {}
+ }
+}
+
+fn syntax2() {
+ macro_rules! mac {
+ ($e:expr) => {
+ match 0u8 { ..$e => {}, _ => {} }
+ match 0u8 { ..=$e => {}, _ => {} }
+ match 0u8 { $e.. => {}, _ => {} }
+ }
+ }
+ mac!(42u8);
+}
--- /dev/null
+// check-pass
+
+#![feature(half_open_range_patterns)]
+#![feature(exclusive_range_pattern)]
+
+fn main() {
+ const PAT: u8 = 1;
+
+ match 0 {
+ (.. PAT) => {}
+ _ => {}
+ }
+}
--- /dev/null
+#![feature(half_open_range_patterns)]
+#![feature(exclusive_range_pattern)]
+
+fn main() {
+ const PAT: u8 = 1;
+
+ match (0, 1) {
+ (PAT ..) => {} //~ ERROR mismatched types
+ }
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/pat-tuple-5.rs:8:10
+ |
+LL | match (0, 1) {
+ | ------ this expression has type `({integer}, {integer})`
+LL | (PAT ..) => {}
+ | ^^^ expected tuple, found `u8`
+ |
+ = note: expected tuple `({integer}, {integer})`
+ found type `u8`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
... |
LL | | foo_hrtb_bar_not(&mut t);
| | ------------------------ recursive call site
+LL | |
LL | | }
| |_^ cannot return without recursing
|
| ^^^^^^^^^^^^^^^^^^^^^^^^
warning: function cannot return without recursing
- --> $DIR/hrtb-perfect-forwarding.rs:49:1
+ --> $DIR/hrtb-perfect-forwarding.rs:50:1
|
LL | / fn foo_hrtb_bar_hrtb<T>(mut t: T)
LL | | where T : for<'a> Foo<&'a isize> + for<'b> Bar<&'b isize>
// isize>`, we require `T : for<'a> Bar<&'a isize>`, but the where
// clause only specifies `T : Bar<&'b isize>`.
foo_hrtb_bar_not(&mut t); //~ ERROR mismatched types
+ //~| ERROR mismatched types
}
fn foo_hrtb_bar_hrtb<T>(mut t: T)
= note: expected type `Bar<&'a isize>`
found type `Bar<&'b isize>`
-error: aborting due to previous error
+error[E0308]: mismatched types
+ --> $DIR/hrtb-perfect-forwarding.rs:46:5
+ |
+LL | foo_hrtb_bar_not(&mut t);
+ | ^^^^^^^^^^^^^^^^ one type is more general than the other
+ |
+ = note: expected type `Bar<&'a isize>`
+ found type `Bar<&'b isize>`
+
+error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0308`.
| ^^^^^^^^^^^^^^^^^^^^^^^^
error: higher-ranked subtype error
- --> $DIR/issue-30786.rs:116:17
+ --> $DIR/issue-30786.rs:114:18
+ |
+LL | let filter = map.filter(|x: &_| true);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: higher-ranked subtype error
+ --> $DIR/issue-30786.rs:114:18
+ |
+LL | let filter = map.filter(|x: &_| true);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: higher-ranked subtype error
+ --> $DIR/issue-30786.rs:114:18
+ |
+LL | let filter = map.filter(|x: &_| true);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: higher-ranked subtype error
+ --> $DIR/issue-30786.rs:119:17
+ |
+LL | let count = filter.count(); // Assert that we still have a valid stream.
+ | ^^^^^^^^^^^^^^
+
+error: higher-ranked subtype error
+ --> $DIR/issue-30786.rs:119:17
+ |
+LL | let count = filter.count(); // Assert that we still have a valid stream.
+ | ^^^^^^^^^^^^^^
+
+error: higher-ranked subtype error
+ --> $DIR/issue-30786.rs:119:17
+ |
+LL | let count = filter.count(); // Assert that we still have a valid stream.
+ | ^^^^^^^^^^^^^^
+
+error: higher-ranked subtype error
+ --> $DIR/issue-30786.rs:119:17
|
LL | let count = filter.count(); // Assert that we still have a valid stream.
| ^^^^^^^^^^^^^^
-error: aborting due to 3 previous errors
+error: aborting due to 9 previous errors
//[migrate]~| NOTE implementation of `Stream` is not general enough
let filter = map.filter(|x: &_| true);
//[nll]~^ ERROR higher-ranked subtype error
+ //[nll]~| ERROR higher-ranked subtype error
+ //[nll]~| ERROR higher-ranked subtype error
+ //[nll]~| ERROR higher-ranked subtype error
let count = filter.count(); // Assert that we still have a valid stream.
//[nll]~^ ERROR higher-ranked subtype error
-
+ //[nll]~| ERROR higher-ranked subtype error
+ //[nll]~| ERROR higher-ranked subtype error
+ //[nll]~| ERROR higher-ranked subtype error
}
LL | Vec::new();
| ^^^ use of undeclared type or module `Vec`
-error[E0599]: no method named `clone` found for type `()` in the current scope
+error[E0599]: no method named `clone` found for unit type `()` in the current scope
--> $DIR/no_implicit_prelude.rs:12:12
|
LL | fn f() { ::bar::m!(); }
}
mod baz {
- pub macro m() { ().f() } //~ ERROR no method named `f` found for type `()` in the current scope
+ pub macro m() { ().f() } //~ ERROR no method named `f` found
fn f() { ::bar::m!(); }
}
-error[E0599]: no method named `f` found for type `()` in the current scope
+error[E0599]: no method named `f` found for unit type `()` in the current scope
--> $DIR/trait_items.rs:17:24
|
LL | fn f() { ::baz::m!(); }
fn cycle1() -> impl Clone {
//~^ ERROR cycle detected
//~| ERROR cycle detected
+ //~| ERROR cycle detected
send(cycle2().clone());
//~^ ERROR cannot be sent between threads safely
| ^^^^^^^^^^^^^^^^^^^^^^^^^
= note: ...which requires evaluating trait selection obligation `impl std::clone::Clone: std::marker::Send`...
note: ...which requires processing `cycle2::{{opaque}}#0`...
- --> $DIR/auto-trait-leak.rs:21:16
+ --> $DIR/auto-trait-leak.rs:22:16
|
LL | fn cycle2() -> impl Clone {
| ^^^^^^^^^^
note: ...which requires processing `cycle2`...
- --> $DIR/auto-trait-leak.rs:21:1
+ --> $DIR/auto-trait-leak.rs:22:1
|
LL | fn cycle2() -> impl Clone {
| ^^^^^^^^^^^^^^^^^^^^^^^^^
| ^^^^^^^^^^^^^^^^^^^^^^^^^
= note: ...which requires evaluating trait selection obligation `impl std::clone::Clone: std::marker::Send`...
note: ...which requires processing `cycle2::{{opaque}}#0`...
- --> $DIR/auto-trait-leak.rs:21:16
+ --> $DIR/auto-trait-leak.rs:22:16
|
LL | fn cycle2() -> impl Clone {
| ^^^^^^^^^^
note: ...which requires processing `cycle2`...
- --> $DIR/auto-trait-leak.rs:21:1
+ --> $DIR/auto-trait-leak.rs:22:1
+ |
+LL | fn cycle2() -> impl Clone {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: ...which again requires processing `cycle1::{{opaque}}#0`, completing the cycle
+note: cycle used when checking item types in top-level module
+ --> $DIR/auto-trait-leak.rs:1:1
+ |
+LL | / use std::cell::Cell;
+LL | | use std::rc::Rc;
+LL | |
+LL | | fn send<T: Send>(_: T) {}
+... |
+LL | | Rc::new(String::from("foo"))
+LL | | }
+ | |_^
+
+error[E0391]: cycle detected when processing `cycle1::{{opaque}}#0`
+ --> $DIR/auto-trait-leak.rs:12:16
+ |
+LL | fn cycle1() -> impl Clone {
+ | ^^^^^^^^^^
+ |
+note: ...which requires processing `cycle1`...
+ --> $DIR/auto-trait-leak.rs:12:1
+ |
+LL | fn cycle1() -> impl Clone {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: ...which requires evaluating trait selection obligation `impl std::clone::Clone: std::marker::Send`...
+note: ...which requires processing `cycle2::{{opaque}}#0`...
+ --> $DIR/auto-trait-leak.rs:22:16
+ |
+LL | fn cycle2() -> impl Clone {
+ | ^^^^^^^^^^
+note: ...which requires processing `cycle2`...
+ --> $DIR/auto-trait-leak.rs:22:1
|
LL | fn cycle2() -> impl Clone {
| ^^^^^^^^^^^^^^^^^^^^^^^^^
| |_^
error[E0277]: `std::rc::Rc<std::string::String>` cannot be sent between threads safely
- --> $DIR/auto-trait-leak.rs:15:5
+ --> $DIR/auto-trait-leak.rs:16:5
|
LL | fn send<T: Send>(_: T) {}
| ---- ---- required by this bound in `send`
...
LL | send(cycle2().clone());
| ^^^^ `std::rc::Rc<std::string::String>` cannot be sent between threads safely
+...
+LL | fn cycle2() -> impl Clone {
+ | ---------- within this `impl std::clone::Clone`
|
= help: within `impl std::clone::Clone`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<std::string::String>`
= note: required because it appears within the type `impl std::clone::Clone`
-error: aborting due to 3 previous errors
+error: aborting due to 4 previous errors
Some errors have detailed explanations: E0277, E0391.
For more information about an error, try `rustc --explain E0277`.
error[E0277]: `std::rc::Rc<std::cell::Cell<i32>>` cannot be sent between threads safely
--> $DIR/auto-trait-leak2.rs:13:5
|
+LL | fn before() -> impl Fn(i32) {
+ | ------------ within this `impl std::ops::Fn<(i32,)>`
+...
LL | fn send<T: Send>(_: T) {}
| ---- ---- required by this bound in `send`
...
...
LL | send(after());
| ^^^^ `std::rc::Rc<std::cell::Cell<i32>>` cannot be sent between threads safely
+...
+LL | fn after() -> impl Fn(i32) {
+ | ------------ within this `impl std::ops::Fn<(i32,)>`
|
= help: within `impl std::ops::Fn<(i32,)>`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<std::cell::Cell<i32>>`
= note: required because it appears within the type `[closure@$DIR/auto-trait-leak2.rs:24:5: 24:22 p:std::rc::Rc<std::cell::Cell<i32>>]`
|
= note: `#[warn(incomplete_features)]` on by default
-error[E0599]: no method named `count_ones` found for type `impl std::marker::Copy` in the current scope
+error[E0599]: no method named `count_ones` found for opaque type `impl std::marker::Copy` in the current scope
--> $DIR/bindings-opaque.rs:11:17
|
LL | let _ = FOO.count_ones();
| ^^^^^^^^^^ method not found in `impl std::marker::Copy`
-error[E0599]: no method named `count_ones` found for type `impl std::marker::Copy` in the current scope
+error[E0599]: no method named `count_ones` found for opaque type `impl std::marker::Copy` in the current scope
--> $DIR/bindings-opaque.rs:13:17
|
LL | let _ = BAR.count_ones();
| ^^^^^^^^^^ method not found in `impl std::marker::Copy`
-error[E0599]: no method named `count_ones` found for type `impl std::marker::Copy` in the current scope
+error[E0599]: no method named `count_ones` found for opaque type `impl std::marker::Copy` in the current scope
--> $DIR/bindings-opaque.rs:15:17
|
LL | let _ = foo.count_ones();
error[E0308]: mismatched types
--> $DIR/equality2.rs:25:18
|
+LL | fn hide<T: Foo>(x: T) -> impl Foo {
+ | -------- the found opaque type
+...
LL | let _: u32 = hide(0_u32);
| --- ^^^^^^^^^^^ expected `u32`, found opaque type
| |
error[E0308]: mismatched types
--> $DIR/equality2.rs:31:18
|
+LL | fn hide<T: Foo>(x: T) -> impl Foo {
+ | -------- the found opaque type
+...
LL | let _: i32 = Leak::leak(hide(0_i32));
| --- ^^^^^^^^^^^^^^^^^^^^^^^ expected `i32`, found associated type
| |
error[E0308]: mismatched types
--> $DIR/equality2.rs:38:10
|
+LL | fn hide<T: Foo>(x: T) -> impl Foo {
+ | --------
+ | |
+ | the expected opaque type
+ | the found opaque type
+...
LL | x = (x.1,
| ^^^ expected `u32`, found `i32`
|
error[E0308]: mismatched types
--> $DIR/equality2.rs:41:10
|
+LL | fn hide<T: Foo>(x: T) -> impl Foo {
+ | --------
+ | |
+ | the expected opaque type
+ | the found opaque type
+...
LL | x.0);
| ^^^ expected `i32`, found `u32`
|
let f1 = Bar;
f1.foo(1usize);
- //~^ error: method named `foo` found for type `Bar` in the current scope
+ //~^ error: method named `foo` found for struct `Bar` in the current scope
}
-error[E0599]: no method named `foo` found for type `Bar` in the current scope
+error[E0599]: no method named `foo` found for struct `Bar` in the current scope
--> $DIR/issue-21659-show-relevant-trait-impls-3.rs:20:8
|
LL | struct Bar;
-error[E0599]: no method named `is_empty` found for type `Foo` in the current scope
+error[E0599]: no method named `is_empty` found for struct `Foo` in the current scope
--> $DIR/method-suggestion-no-duplication.rs:7:15
|
LL | struct Foo;
//~|items from traits can only be used if the trait is in scope
std::rc::Rc::new(&mut Box::new(&1u32)).method();
//~^items from traits can only be used if the trait is in scope
- //~| ERROR no method named `method` found for type
+ //~| ERROR no method named `method` found for struct
'a'.method();
//~^ ERROR no method named
LL | use no_method_suggested_traits::Reexported;
|
-error[E0599]: no method named `method` found for type `std::rc::Rc<&mut std::boxed::Box<&u32>>` in the current scope
+error[E0599]: no method named `method` found for struct `std::rc::Rc<&mut std::boxed::Box<&u32>>` in the current scope
--> $DIR/no-method-suggested-traits.rs:26:44
|
LL | std::rc::Rc::new(&mut Box::new(&1u32)).method();
LL | use foo::Bar;
|
-error[E0599]: no method named `method` found for type `std::rc::Rc<&mut std::boxed::Box<&char>>` in the current scope
+error[E0599]: no method named `method` found for struct `std::rc::Rc<&mut std::boxed::Box<&char>>` in the current scope
--> $DIR/no-method-suggested-traits.rs:32:43
|
LL | fn method(&self) {}
LL | use no_method_suggested_traits::foo::PubPub;
|
-error[E0599]: no method named `method` found for type `std::rc::Rc<&mut std::boxed::Box<&i32>>` in the current scope
+error[E0599]: no method named `method` found for struct `std::rc::Rc<&mut std::boxed::Box<&i32>>` in the current scope
--> $DIR/no-method-suggested-traits.rs:37:44
|
LL | std::rc::Rc::new(&mut Box::new(&1i32)).method();
LL | use no_method_suggested_traits::foo::PubPub;
|
-error[E0599]: no method named `method` found for type `Foo` in the current scope
+error[E0599]: no method named `method` found for struct `Foo` in the current scope
--> $DIR/no-method-suggested-traits.rs:40:9
|
LL | struct Foo;
candidate #3: `no_method_suggested_traits::qux::PrivPub`
candidate #4: `no_method_suggested_traits::Reexported`
-error[E0599]: no method named `method` found for type `std::rc::Rc<&mut std::boxed::Box<&Foo>>` in the current scope
+error[E0599]: no method named `method` found for struct `std::rc::Rc<&mut std::boxed::Box<&Foo>>` in the current scope
--> $DIR/no-method-suggested-traits.rs:42:43
|
LL | std::rc::Rc::new(&mut Box::new(&Foo)).method();
= note: the following trait defines an item `method2`, perhaps you need to implement it:
candidate #1: `foo::Bar`
-error[E0599]: no method named `method2` found for type `std::rc::Rc<&mut std::boxed::Box<&u64>>` in the current scope
+error[E0599]: no method named `method2` found for struct `std::rc::Rc<&mut std::boxed::Box<&u64>>` in the current scope
--> $DIR/no-method-suggested-traits.rs:47:44
|
LL | std::rc::Rc::new(&mut Box::new(&1u64)).method2();
= note: the following trait defines an item `method2`, perhaps you need to implement it:
candidate #1: `foo::Bar`
-error[E0599]: no method named `method2` found for type `no_method_suggested_traits::Foo` in the current scope
+error[E0599]: no method named `method2` found for struct `no_method_suggested_traits::Foo` in the current scope
--> $DIR/no-method-suggested-traits.rs:50:37
|
LL | no_method_suggested_traits::Foo.method2();
= note: the following trait defines an item `method2`, perhaps you need to implement it:
candidate #1: `foo::Bar`
-error[E0599]: no method named `method2` found for type `std::rc::Rc<&mut std::boxed::Box<&no_method_suggested_traits::Foo>>` in the current scope
+error[E0599]: no method named `method2` found for struct `std::rc::Rc<&mut std::boxed::Box<&no_method_suggested_traits::Foo>>` in the current scope
--> $DIR/no-method-suggested-traits.rs:52:71
|
LL | std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Foo)).method2();
= note: the following trait defines an item `method2`, perhaps you need to implement it:
candidate #1: `foo::Bar`
-error[E0599]: no method named `method2` found for type `no_method_suggested_traits::Bar` in the current scope
+error[E0599]: no method named `method2` found for enum `no_method_suggested_traits::Bar` in the current scope
--> $DIR/no-method-suggested-traits.rs:54:40
|
LL | no_method_suggested_traits::Bar::X.method2();
= note: the following trait defines an item `method2`, perhaps you need to implement it:
candidate #1: `foo::Bar`
-error[E0599]: no method named `method2` found for type `std::rc::Rc<&mut std::boxed::Box<&no_method_suggested_traits::Bar>>` in the current scope
+error[E0599]: no method named `method2` found for struct `std::rc::Rc<&mut std::boxed::Box<&no_method_suggested_traits::Bar>>` in the current scope
--> $DIR/no-method-suggested-traits.rs:56:74
|
LL | std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Bar::X)).method2();
= note: the following trait defines an item `method2`, perhaps you need to implement it:
candidate #1: `foo::Bar`
-error[E0599]: no method named `method3` found for type `Foo` in the current scope
+error[E0599]: no method named `method3` found for struct `Foo` in the current scope
--> $DIR/no-method-suggested-traits.rs:59:9
|
LL | struct Foo;
= note: the following trait defines an item `method3`, perhaps you need to implement it:
candidate #1: `no_method_suggested_traits::foo::PubPub`
-error[E0599]: no method named `method3` found for type `std::rc::Rc<&mut std::boxed::Box<&Foo>>` in the current scope
+error[E0599]: no method named `method3` found for struct `std::rc::Rc<&mut std::boxed::Box<&Foo>>` in the current scope
--> $DIR/no-method-suggested-traits.rs:61:43
|
LL | std::rc::Rc::new(&mut Box::new(&Foo)).method3();
= note: the following trait defines an item `method3`, perhaps you need to implement it:
candidate #1: `no_method_suggested_traits::foo::PubPub`
-error[E0599]: no method named `method3` found for type `Bar` in the current scope
+error[E0599]: no method named `method3` found for enum `Bar` in the current scope
--> $DIR/no-method-suggested-traits.rs:63:12
|
LL | enum Bar { X }
= note: the following trait defines an item `method3`, perhaps you need to implement it:
candidate #1: `no_method_suggested_traits::foo::PubPub`
-error[E0599]: no method named `method3` found for type `std::rc::Rc<&mut std::boxed::Box<&Bar>>` in the current scope
+error[E0599]: no method named `method3` found for struct `std::rc::Rc<&mut std::boxed::Box<&Bar>>` in the current scope
--> $DIR/no-method-suggested-traits.rs:65:46
|
LL | std::rc::Rc::new(&mut Box::new(&Bar::X)).method3();
LL | 1_usize.method3();
| ^^^^^^^ method not found in `usize`
-error[E0599]: no method named `method3` found for type `std::rc::Rc<&mut std::boxed::Box<&usize>>` in the current scope
+error[E0599]: no method named `method3` found for struct `std::rc::Rc<&mut std::boxed::Box<&usize>>` in the current scope
--> $DIR/no-method-suggested-traits.rs:70:47
|
LL | std::rc::Rc::new(&mut Box::new(&1_usize)).method3();
| ^^^^^^^ method not found in `std::rc::Rc<&mut std::boxed::Box<&usize>>`
-error[E0599]: no method named `method3` found for type `no_method_suggested_traits::Foo` in the current scope
+error[E0599]: no method named `method3` found for struct `no_method_suggested_traits::Foo` in the current scope
--> $DIR/no-method-suggested-traits.rs:71:37
|
LL | no_method_suggested_traits::Foo.method3();
| ^^^^^^^ method not found in `no_method_suggested_traits::Foo`
-error[E0599]: no method named `method3` found for type `std::rc::Rc<&mut std::boxed::Box<&no_method_suggested_traits::Foo>>` in the current scope
+error[E0599]: no method named `method3` found for struct `std::rc::Rc<&mut std::boxed::Box<&no_method_suggested_traits::Foo>>` in the current scope
--> $DIR/no-method-suggested-traits.rs:72:71
|
LL | std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Foo)).method3();
| ^^^^^^^ method not found in `std::rc::Rc<&mut std::boxed::Box<&no_method_suggested_traits::Foo>>`
-error[E0599]: no method named `method3` found for type `no_method_suggested_traits::Bar` in the current scope
+error[E0599]: no method named `method3` found for enum `no_method_suggested_traits::Bar` in the current scope
--> $DIR/no-method-suggested-traits.rs:74:40
|
LL | no_method_suggested_traits::Bar::X.method3();
| ^^^^^^^ method not found in `no_method_suggested_traits::Bar`
-error[E0599]: no method named `method3` found for type `std::rc::Rc<&mut std::boxed::Box<&no_method_suggested_traits::Bar>>` in the current scope
+error[E0599]: no method named `method3` found for struct `std::rc::Rc<&mut std::boxed::Box<&no_method_suggested_traits::Bar>>` in the current scope
--> $DIR/no-method-suggested-traits.rs:75:74
|
LL | std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Bar::X)).method3();
#[non_existent] //~ ERROR cannot determine resolution for the attribute macro `non_existent`
#[derive(NonExistent)] //~ ERROR cannot determine resolution for the derive macro `NonExistent`
+ //~| ERROR cannot determine resolution for the derive macro `NonExistent`
+ //~| ERROR cannot determine resolution for the derive macro `NonExistent`
struct S;
fn main() {}
|
= note: import resolution is stuck, try simplifying macro imports
-error: aborting due to 4 previous errors
+error: cannot determine resolution for the derive macro `NonExistent`
+ --> $DIR/issue-55457.rs:5:10
+ |
+LL | #[derive(NonExistent)]
+ | ^^^^^^^^^^^
+ |
+ = note: import resolution is stuck, try simplifying macro imports
+
+error: cannot determine resolution for the derive macro `NonExistent`
+ --> $DIR/issue-55457.rs:5:10
+ |
+LL | #[derive(NonExistent)]
+ | ^^^^^^^^^^^
+ |
+ = note: import resolution is stuck, try simplifying macro imports
+
+error: aborting due to 6 previous errors
For more information about this error, try `rustc --explain E0432`.
}
exported!(); //~ ERROR `exported` is ambiguous
+ //~| ERROR `exported` is ambiguous
mod inner2 {
define_exported!();
| ^^^^^^^^^
= help: consider adding an explicit import of `exported` to disambiguate
+error[E0659]: `exported` is ambiguous (glob import vs macro-expanded name in the same module during import/macro resolution)
+ --> $DIR/local-modularized-tricky-fail-1.rs:28:1
+ |
+LL | exported!();
+ | ^^^^^^^^ ambiguous name
+ |
+note: `exported` could refer to the macro defined here
+ --> $DIR/local-modularized-tricky-fail-1.rs:5:5
+ |
+LL | / macro_rules! exported {
+LL | | () => ()
+LL | | }
+ | |_____^
+...
+LL | define_exported!();
+ | ------------------- in this macro invocation
+note: `exported` could also refer to the macro imported here
+ --> $DIR/local-modularized-tricky-fail-1.rs:22:5
+ |
+LL | use inner1::*;
+ | ^^^^^^^^^
+ = help: consider adding an explicit import of `exported` to disambiguate
+
error[E0659]: `panic` is ambiguous (macro-expanded name vs less macro-expanded name from outer scope during import/macro resolution)
- --> $DIR/local-modularized-tricky-fail-1.rs:35:5
+ --> $DIR/local-modularized-tricky-fail-1.rs:36:5
|
LL | panic!();
| ^^^^^ ambiguous name
= help: use `crate::panic` to refer to this macro unambiguously
error[E0659]: `include` is ambiguous (macro-expanded name vs less macro-expanded name from outer scope during import/macro resolution)
- --> $DIR/local-modularized-tricky-fail-1.rs:46:1
+ --> $DIR/local-modularized-tricky-fail-1.rs:47:1
|
LL | include!();
| ^^^^^^^ ambiguous name
| ------------------ in this macro invocation
= help: use `crate::include` to refer to this macro unambiguously
-error: aborting due to 3 previous errors
+error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0659`.
mod m2 {
use two_macros::*;
m! { //~ ERROR ambiguous
+ //~| ERROR ambiguous
use foo::m;
}
}
| ^ ambiguous name
|
note: `m` could refer to the macro imported here
- --> $DIR/macros.rs:17:13
+ --> $DIR/macros.rs:18:13
+ |
+LL | use foo::m;
+ | ^^^^^^
+note: `m` could also refer to the macro imported here
+ --> $DIR/macros.rs:15:9
+ |
+LL | use two_macros::*;
+ | ^^^^^^^^^^^^^
+ = help: consider adding an explicit import of `m` to disambiguate
+
+error[E0659]: `m` is ambiguous (glob import vs macro-expanded name in the same module during import/macro resolution)
+ --> $DIR/macros.rs:16:5
+ |
+LL | m! {
+ | ^ ambiguous name
+ |
+note: `m` could refer to the macro imported here
+ --> $DIR/macros.rs:18:13
|
LL | use foo::m;
| ^^^^^^
= help: consider adding an explicit import of `m` to disambiguate
error[E0659]: `m` is ambiguous (macro-expanded name vs less macro-expanded name from outer scope during import/macro resolution)
- --> $DIR/macros.rs:29:9
+ --> $DIR/macros.rs:30:9
|
LL | m! {
| ^ ambiguous name
|
note: `m` could refer to the macro imported here
- --> $DIR/macros.rs:30:17
+ --> $DIR/macros.rs:31:17
|
LL | use two_macros::n as m;
| ^^^^^^^^^^^^^^^^^^
note: `m` could also refer to the macro imported here
- --> $DIR/macros.rs:22:9
+ --> $DIR/macros.rs:23:9
|
LL | use two_macros::m;
| ^^^^^^^^^^^^^
= help: use `self::m` to refer to this macro unambiguously
-error: aborting due to 2 previous errors
+error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0659`.
-// Make sure that invalid ranges generate an error during HIR lowering, not an ICE
+// Make sure that invalid ranges generate an error during parsing, not an ICE
pub fn main() {
..;
..1;
0..1;
..=; //~ERROR inclusive range with no end
- //~^HELP bounded at the end
+ //~^HELP use `..` instead
}
fn _foo1() {
..=1;
0..=1;
0..=; //~ERROR inclusive range with no end
- //~^HELP bounded at the end
+ //~^HELP use `..` instead
}
error[E0586]: inclusive range with no end
- --> $DIR/impossible_range.rs:8:8
+ --> $DIR/impossible_range.rs:8:5
|
LL | ..=;
- | ^
+ | ^^^ help: use `..` instead
|
- = help: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
+ = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
error[E0586]: inclusive range with no end
- --> $DIR/impossible_range.rs:15:9
+ --> $DIR/impossible_range.rs:15:6
|
LL | 0..=;
- | ^
+ | ^^^ help: use `..` instead
|
- = help: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
+ = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
error: aborting due to 2 previous errors
|
= help: consider adding a `#![recursion_limit="256"]` attribute to your crate
-error[E0599]: no method named `bar` found for type `Foo` in the current scope
+error[E0599]: no method named `bar` found for struct `Foo` in the current scope
--> $DIR/infinite-autoderef.rs:26:9
|
LL | struct Foo;
--- /dev/null
+#![feature(staged_api)]
+#![feature(const_if_match)]
+
+#[stable(feature = "rust1", since = "1.0.0")]
+#[rustc_const_stable(feature = "rust1", since = "1.0.0")]
+const fn foo() -> i32 {
+ if true { 4 } else { 5 } //~ loops and conditional expressions are not stable in const fn
+}
+
+fn main() {}
--- /dev/null
+error[E0723]: loops and conditional expressions are not stable in const fn
+ --> $DIR/internal-unstable-const.rs:7:5
+ |
+LL | if true { 4 } else { 5 }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: for more information, see issue https://github.com/rust-lang/rust/issues/57563
+ = help: add `#![feature(const_fn)]` to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0723`.
+++ /dev/null
-// run-pass
-
-#![allow(unused_mut)]
-// ignore-wasm32
-// ignore-emscripten
-// ignore-sgx no processes
-
-// compile-flags: -C debug_assertions=yes
-
-#![stable(feature = "rustc", since = "1.0.0")]
-#![feature(const_fn, rustc_private, staged_api, rustc_attrs)]
-#![allow(const_err)]
-
-extern crate libc;
-
-use std::env;
-use std::process::{Command, Stdio};
-
-// this will panic in debug mode and overflow in release mode
-//
-// NB we give bar an unused argument because otherwise memoization
-// of the const fn kicks in, causing a different code path in the
-// compiler to be executed (see PR #66294).
-#[stable(feature = "rustc", since = "1.0.0")]
-#[rustc_const_stable(feature = "rustc", since = "1.0.0")]
-#[rustc_promotable]
-const fn bar(_: bool) -> usize { 0 - 1 }
-
-fn foo() {
- let _: &'static _ = &bar(true);
-}
-
-#[cfg(unix)]
-fn check_status(status: std::process::ExitStatus)
-{
- use std::os::unix::process::ExitStatusExt;
-
- assert!(status.signal() == Some(libc::SIGILL)
- || status.signal() == Some(libc::SIGTRAP)
- || status.signal() == Some(libc::SIGABRT));
-}
-
-#[cfg(not(unix))]
-fn check_status(status: std::process::ExitStatus)
-{
- assert!(!status.success());
-}
-
-fn main() {
- let args: Vec<String> = env::args().collect();
- if args.len() > 1 && args[1] == "test" {
- foo();
- return;
- }
-
- let mut p = Command::new(&args[0])
- .stdout(Stdio::piped())
- .stdin(Stdio::piped())
- .arg("test").output().unwrap();
- check_status(p.status);
-}
-error[E0599]: no method named `foo` found for type `&b::B` in the current scope
+error[E0599]: no method named `foo` found for reference `&b::B` in the current scope
--> $DIR/issue-10465.rs:17:15
|
LL | b.foo();
= help: type parameters must be constrained to match other types
= note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
-error[E0599]: no method named `iter` found for type `&G` in the current scope
+error[E0599]: no method named `iter` found for reference `&G` in the current scope
--> $DIR/issue-13853.rs:27:23
|
LL | for node in graph.iter() {
|
LL | A => "A",
| ^ help: to match on the variant, qualify the path: `E::A`
+ |
+ = note: `#[warn(bindings_with_variant_name)]` on by default
warning[E0170]: pattern binding `B` is named the same as one of the variants of the type `E`
--> $DIR/issue-14221.rs:15:13
+// FIXME: missing sysroot spans (#53081)
+// ignore-i586-unknown-linux-gnu
+// ignore-i586-unknown-linux-musl
+// ignore-i686-unknown-linux-musl
use foo::MyEnum::Result;
use foo::NoResult; // Through a re-export
error[E0573]: expected type, found variant `NoResult`
- --> $DIR/issue-17546.rs:12:17
+ --> $DIR/issue-17546.rs:16:17
|
LL | fn new() -> NoResult<MyEnum, String> {
| ^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ ::: $SRC_DIR/libcore/result.rs:LL:COL
+ |
+LL | pub enum Result<T, E> {
+ | --------------------- similarly named enum `Result` defined here
|
help: try using the variant's enum
|
| ^^^^^^
error[E0573]: expected type, found variant `Result`
- --> $DIR/issue-17546.rs:22:17
+ --> $DIR/issue-17546.rs:26:17
|
LL | fn new() -> Result<foo::MyEnum, String> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not a type
and 1 other candidate
error[E0573]: expected type, found variant `Result`
- --> $DIR/issue-17546.rs:28:13
+ --> $DIR/issue-17546.rs:32:13
|
LL | fn new() -> Result<foo::MyEnum, String> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not a type
and 1 other candidate
error[E0573]: expected type, found variant `NoResult`
- --> $DIR/issue-17546.rs:33:15
+ --> $DIR/issue-17546.rs:37:15
|
LL | fn newer() -> NoResult<foo::MyEnum, String> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ ::: $SRC_DIR/libcore/result.rs:LL:COL
+ |
+LL | pub enum Result<T, E> {
+ | --------------------- similarly named enum `Result` defined here
|
help: try using the variant's enum
|
static mut S: usize = 3;
const C2: &'static mut usize = unsafe { &mut S };
//~^ ERROR: constants cannot refer to statics
+//~| ERROR: constants cannot refer to statics
//~| ERROR: references in constants may only refer to immutable values
fn main() {}
= note: for more information, see https://github.com/rust-lang/rust/issues/57349
= help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
-error[E0013]: constants cannot refer to statics, use a constant instead
+error[E0013]: constants cannot refer to statics
--> $DIR/issue-17718-const-bad-values.rs:5:46
|
LL | const C2: &'static mut usize = unsafe { &mut S };
| ^
+ |
+ = help: consider extracting the value of the `static` to a `const`, and referring to that
+
+error[E0013]: constants cannot refer to statics
+ --> $DIR/issue-17718-const-bad-values.rs:5:46
+ |
+LL | const C2: &'static mut usize = unsafe { &mut S };
+ | ^
+ |
+ = help: consider extracting the value of the `static` to a `const`, and referring to that
error[E0658]: references in constants may only refer to immutable values
--> $DIR/issue-17718-const-bad-values.rs:5:41
= note: for more information, see https://github.com/rust-lang/rust/issues/57349
= help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
-error: aborting due to 3 previous errors
+error: aborting due to 4 previous errors
Some errors have detailed explanations: E0013, E0658.
For more information about an error, try `rustc --explain E0013`.
-error[E0013]: constants cannot refer to statics, use a constant instead
+error[E0013]: constants cannot refer to statics
--> $DIR/issue-17718-references.rs:9:29
|
LL | const T2: &'static usize = &S;
| ^
+ |
+ = help: consider extracting the value of the `static` to a `const`, and referring to that
-error[E0013]: constants cannot refer to statics, use a constant instead
+error[E0013]: constants cannot refer to statics
--> $DIR/issue-17718-references.rs:14:19
|
LL | const T6: usize = S;
| ^
+ |
+ = help: consider extracting the value of the `static` to a `const`, and referring to that
-error[E0013]: constants cannot refer to statics, use a constant instead
+error[E0013]: constants cannot refer to statics
--> $DIR/issue-17718-references.rs:19:33
|
LL | const T10: Struct = Struct { a: S };
| ^
+ |
+ = help: consider extracting the value of the `static` to a `const`, and referring to that
error: aborting due to 3 previous errors
pub fn main() {
const z: &'static isize = {
static p: isize = 3;
- &p
- //~^ ERROR constants cannot refer to statics, use a constant instead
+ &p //~ ERROR constants cannot refer to statics
};
}
-error[E0013]: constants cannot refer to statics, use a constant instead
+error[E0013]: constants cannot refer to statics
--> $DIR/issue-18118-2.rs:4:10
|
LL | &p
| ^
+ |
+ = help: consider extracting the value of the `static` to a `const`, and referring to that
error: aborting due to previous error
|
LL | Bar if true
| ^^^ help: to match on the variant, qualify the path: `Foo::Bar`
+ |
+ = note: `#[warn(bindings_with_variant_name)]` on by default
warning[E0170]: pattern binding `Baz` is named the same as one of the variants of the type `Foo`
--> $DIR/issue-19100.rs:22:1
-error[E0599]: no method named `homura` found for type `&'static str` in the current scope
+error[E0599]: no method named `homura` found for reference `&'static str` in the current scope
--> $DIR/issue-19521.rs:2:8
|
LL | "".homura()();
-error[E0599]: no method named `kaname` found for type `Homura` in the current scope
+error[E0599]: no method named `kaname` found for struct `Homura` in the current scope
--> $DIR/issue-19692.rs:4:40
|
LL | struct Homura;
fn subscribe(&mut self, t : Box<dyn Subscriber<Input=<Self as Publisher>::Output> + 'a>) {
// Not obvious, but there is an implicit lifetime here -------^
//~^^ ERROR cannot infer
+ //~| ERROR cannot infer
//~| ERROR mismatched types
//~| ERROR mismatched types
//
= note: expected `Publisher<'_>`
found `Publisher<'_>`
-error: aborting due to 3 previous errors
+error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
+ --> $DIR/issue-20831-debruijn.rs:28:5
+ |
+LL | / fn subscribe(&mut self, t : Box<dyn Subscriber<Input=<Self as Publisher>::Output> + 'a>) {
+LL | | // Not obvious, but there is an implicit lifetime here -------^
+LL | |
+LL | |
+... |
+LL | | self.sub = t;
+LL | | }
+ | |_____^
+ |
+note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the method body at 28:5...
+ --> $DIR/issue-20831-debruijn.rs:28:5
+ |
+LL | / fn subscribe(&mut self, t : Box<dyn Subscriber<Input=<Self as Publisher>::Output> + 'a>) {
+LL | | // Not obvious, but there is an implicit lifetime here -------^
+LL | |
+LL | |
+... |
+LL | | self.sub = t;
+LL | | }
+ | |_____^
+note: ...but the lifetime must also be valid for the lifetime `'a` as defined on the impl at 26:6...
+ --> $DIR/issue-20831-debruijn.rs:26:6
+ |
+LL | impl<'a> Publisher<'a> for MyStruct<'a> {
+ | ^^
+note: ...so that the types are compatible
+ --> $DIR/issue-20831-debruijn.rs:28:5
+ |
+LL | / fn subscribe(&mut self, t : Box<dyn Subscriber<Input=<Self as Publisher>::Output> + 'a>) {
+LL | | // Not obvious, but there is an implicit lifetime here -------^
+LL | |
+LL | |
+... |
+LL | | self.sub = t;
+LL | | }
+ | |_____^
+ = note: expected `Publisher<'_>`
+ found `Publisher<'_>`
+
+error: aborting due to 4 previous errors
Some errors have detailed explanations: E0308, E0495.
For more information about an error, try `rustc --explain E0308`.
-error[E0599]: no method named `to_string` found for type `*const u8` in the current scope
+error[E0599]: no method named `to_string` found for raw pointer `*const u8` in the current scope
--> $DIR/issue-21596.rs:4:22
|
LL | println!("{}", z.to_string());
Pie = 0x1,
Apple = 0x2,
ApplePie = Delicious::Apple as isize | Delicious::PIE as isize,
- //~^ ERROR no variant or associated item named `PIE` found for type `Delicious`
+ //~^ ERROR no variant or associated item named `PIE` found
}
fn main() {}
-error[E0599]: no variant or associated item named `PIE` found for type `Delicious` in the current scope
+error[E0599]: no variant or associated item named `PIE` found for enum `Delicious` in the current scope
--> $DIR/issue-22933-2.rs:4:55
|
LL | enum Delicious {
const FOO: [u32; u8::MIN as usize] = [];
-//~^ ERROR no associated item named `MIN` found for type `u8`
+//~^ ERROR no associated item named `MIN` found
fn main() {}
fn main() {
use_token(&Token::Homura); //~ ERROR no variant or associated item named `Homura`
- Struct::method(); //~ ERROR no function or associated item named `method` found for type
- Struct::method; //~ ERROR no function or associated item named `method` found for type
- Struct::Assoc; //~ ERROR no associated item named `Assoc` found for type `Struct` in
+ Struct::method(); //~ ERROR no function or associated item named `method` found
+ Struct::method; //~ ERROR no function or associated item named `method` found
+ Struct::Assoc; //~ ERROR no associated item named `Assoc` found
}
-error[E0599]: no variant or associated item named `Homura` found for type `Token` in the current scope
+error[E0599]: no variant or associated item named `Homura` found for enum `Token` in the current scope
--> $DIR/issue-23173.rs:9:23
|
LL | enum Token { LeftParen, RightParen, Plus, Minus, /* etc */ }
LL | use_token(&Token::Homura);
| ^^^^^^ variant or associated item not found in `Token`
-error[E0599]: no function or associated item named `method` found for type `Struct` in the current scope
+error[E0599]: no function or associated item named `method` found for struct `Struct` in the current scope
--> $DIR/issue-23173.rs:10:13
|
LL | struct Struct {
LL | Struct::method();
| ^^^^^^ function or associated item not found in `Struct`
-error[E0599]: no function or associated item named `method` found for type `Struct` in the current scope
+error[E0599]: no function or associated item named `method` found for struct `Struct` in the current scope
--> $DIR/issue-23173.rs:11:13
|
LL | struct Struct {
LL | Struct::method;
| ^^^^^^ function or associated item not found in `Struct`
-error[E0599]: no associated item named `Assoc` found for type `Struct` in the current scope
+error[E0599]: no associated item named `Assoc` found for struct `Struct` in the current scope
--> $DIR/issue-23173.rs:12:13
|
LL | struct Struct {
pub enum SomeEnum {
- B = SomeEnum::A, //~ ERROR no variant or associated item named `A` found for type `SomeEnum`
+ B = SomeEnum::A, //~ ERROR no variant or associated item named `A` found
}
fn main() {}
-error[E0599]: no variant or associated item named `A` found for type `SomeEnum` in the current scope
+error[E0599]: no variant or associated item named `A` found for enum `SomeEnum` in the current scope
--> $DIR/issue-23217.rs:2:19
|
LL | pub enum SomeEnum {
error[E0308]: mismatched types
--> $DIR/issue-24036.rs:3:9
|
+LL | let mut x = |c| c + 1;
+ | --------- the expected closure
LL | x = |c| c + 1;
| ^^^^^^^^^ expected closure, found a different closure
|
macro_rules! foo {
($e:expr) => { $e.foo() }
- //~^ ERROR no method named `foo` found for type `i32` in the current scope
+ //~^ ERROR no method named `foo` found
}
fn main() {
foo!(a);
foo!(1i32.foo());
- //~^ ERROR no method named `foo` found for type `i32` in the current scope
+ //~^ ERROR no method named `foo` found
}
-error[E0599]: no method named `clone` found for type `C` in the current scope
+error[E0599]: no method named `clone` found for struct `C` in the current scope
--> $DIR/issue-2823.rs:13:16
|
LL | struct C {
LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8);
| ^^^^^^ help: specify the associated type: `BitXor<Output = Type>`
-error[E0599]: no function or associated item named `bitor` found for type `dyn std::ops::BitXor<_>` in the current scope
+error[E0599]: no function or associated item named `bitor` found for trait object `dyn std::ops::BitXor<_>` in the current scope
--> $DIR/issue-28344.rs:4:25
|
LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8);
LL | let g = BitXor::bitor;
| ^^^^^^ help: specify the associated type: `BitXor<Output = Type>`
-error[E0599]: no function or associated item named `bitor` found for type `dyn std::ops::BitXor<_>` in the current scope
+error[E0599]: no function or associated item named `bitor` found for trait object `dyn std::ops::BitXor<_>` in the current scope
--> $DIR/issue-28344.rs:8:21
|
LL | let g = BitXor::bitor;
pub trait Foo {}
impl Foo for [u8; usize::BYTES] {}
-//~^ ERROR no associated item named `BYTES` found for type `usize`
+//~^ ERROR no associated item named `BYTES` found
fn main() { }
a + a; //~ ERROR cannot add `A` to `A`
- a - a; //~ ERROR cannot substract `A` from `A`
+ a - a; //~ ERROR cannot subtract `A` from `A`
a * a; //~ ERROR cannot multiply `A` to `A`
|
= note: an implementation of `std::ops::Add` might be missing for `A`
-error[E0369]: cannot substract `A` from `A`
+error[E0369]: cannot subtract `A` from `A`
--> $DIR/issue-28837.rs:8:7
|
LL | a - a;
foo(|| {
match Foo::Bar(1) {
Foo::Baz(..) => (),
- //~^ ERROR no variant or associated item named `Baz` found for type `Foo`
+ //~^ ERROR no variant or associated item named `Baz` found
_ => (),
}
});
-error[E0599]: no variant or associated item named `Baz` found for type `Foo` in the current scope
+error[E0599]: no variant or associated item named `Baz` found for enum `Foo` in the current scope
--> $DIR/issue-28971.rs:7:18
|
LL | enum Foo {
fn main() {
Obj::func.x();
- //~^ ERROR no method named `x` found for type `fn() -> Ret {Obj::func}` in the current scope
+ //~^ ERROR no method named `x` found
func.x();
- //~^ ERROR no method named `x` found for type `fn() -> Ret {func}` in the current scope
+ //~^ ERROR no method named `x` found
}
-error[E0599]: no method named `x` found for type `fn() -> Ret {Obj::func}` in the current scope
+error[E0599]: no method named `x` found for fn item `fn() -> Ret {Obj::func}` in the current scope
--> $DIR/issue-29124.rs:15:15
|
LL | Obj::func.x();
|
= note: Obj::func is a function, perhaps you wish to call it
-error[E0599]: no method named `x` found for type `fn() -> Ret {func}` in the current scope
+error[E0599]: no method named `x` found for fn item `fn() -> Ret {func}` in the current scope
--> $DIR/issue-29124.rs:17:10
|
LL | func.x();
fn main() {
let ug = Graph::<i32, i32>::new_undirected();
- //~^ ERROR no function or associated item named `new_undirected` found for type
+ //~^ ERROR no function or associated item named `new_undirected` found
}
-error[E0599]: no function or associated item named `new_undirected` found for type `issue_30123_aux::Graph<i32, i32>` in the current scope
+error[E0599]: no function or associated item named `new_undirected` found for struct `issue_30123_aux::Graph<i32, i32>` in the current scope
--> $DIR/issue-30123.rs:7:33
|
LL | let ug = Graph::<i32, i32>::new_undirected();
|
LL | Nil => true,
| ^^^ help: to match on the variant, qualify the path: `Stack::Nil`
+ |
+ = note: `#[warn(bindings_with_variant_name)]` on by default
error: unreachable pattern
--> $DIR/issue-30302.rs:15:9
= note: expected type `u8`
found reference `&_`
-error[E0599]: no method named `collect` found for type `std::iter::Cloned<std::iter::TakeWhile<&mut std::vec::IntoIter<u8>, [closure@$DIR/issue-31173.rs:6:39: 9:6 found_e:_]>>` in the current scope
+error[E0599]: no method named `collect` found for struct `std::iter::Cloned<std::iter::TakeWhile<&mut std::vec::IntoIter<u8>, [closure@$DIR/issue-31173.rs:6:39: 9:6 found_e:_]>>` in the current scope
--> $DIR/issue-31173.rs:14:10
|
LL | .collect();
fn main() {
size_of_copy::<dyn Misc + Copy>();
//~^ ERROR only auto traits can be used as additional traits in a trait object
+ //~| ERROR only auto traits can be used as additional traits in a trait object
//~| ERROR the trait bound `dyn Misc: std::marker::Copy` is not satisfied
}
| first non-auto trait
| trait alias used in trait object type (first use)
+error[E0225]: only auto traits can be used as additional traits in a trait object
+ --> $DIR/issue-32963.rs:8:31
+ |
+LL | size_of_copy::<dyn Misc + Copy>();
+ | ---- ^^^^
+ | | |
+ | | additional non-auto trait
+ | | trait alias used in trait object type (additional use)
+ | first non-auto trait
+ | trait alias used in trait object type (first use)
+
error[E0277]: the trait bound `dyn Misc: std::marker::Copy` is not satisfied
--> $DIR/issue-32963.rs:8:5
|
LL | size_of_copy::<dyn Misc + Copy>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `dyn Misc`
-error: aborting due to 2 previous errors
+error: aborting due to 3 previous errors
Some errors have detailed explanations: E0225, E0277.
For more information about an error, try `rustc --explain E0225`.
#[derive(Clone,
Sync, //~ ERROR cannot find derive macro `Sync` in this scope
+ //~| ERROR cannot find derive macro `Sync` in this scope
Copy)]
enum Foo {}
LL | Sync,
| ^^^^
-error: aborting due to previous error
+error: cannot find derive macro `Sync` in this scope
+ --> $DIR/issue-33571.rs:2:10
+ |
+LL | Sync,
+ | ^^^^
+ |
+note: unsafe traits like `Sync` should be implemented explicitly
+ --> $DIR/issue-33571.rs:2:10
+ |
+LL | Sync,
+ | ^^^^
+
+error: aborting due to 2 previous errors
fn main() {
- let baz = ().foo(); //~ ERROR no method named `foo` found for type `()` in the current scope
+ let baz = ().foo(); //~ ERROR no method named `foo` found
<i32 as std::str::FromStr>::from_str(&baz); // No complaints about `str` being unsized
}
-error[E0599]: no method named `foo` found for type `()` in the current scope
+error[E0599]: no method named `foo` found for unit type `()` in the current scope
--> $DIR/issue-33575.rs:2:18
|
LL | let baz = ().foo();
fn bug(l: S) {
match l {
- S::B {} => {}, //~ ERROR no variant `B` in enum `S`
+ S::B {} => {}, //~ ERROR no variant named `B` found for enum `S`
}
}
-error: no variant `B` in enum `S`
+error[E0599]: no variant named `B` found for enum `S`
--> $DIR/issue-34209.rs:7:12
|
LL | enum S {
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0599`.
#[derive(PartialEq)] struct Comparable;
#[derive(PartialEq, PartialOrd)] struct Nope(Comparable);
//~^ ERROR can't compare `Comparable`
+//~| ERROR can't compare `Comparable`
+//~| ERROR can't compare `Comparable`
+//~| ERROR can't compare `Comparable`
+//~| ERROR can't compare `Comparable`
fn main() {}
= help: the trait `std::cmp::PartialOrd` is not implemented for `Comparable`
= note: required by `std::cmp::PartialOrd::partial_cmp`
-error: aborting due to previous error
+error[E0277]: can't compare `Comparable` with `Comparable`
+ --> $DIR/issue-34229.rs:2:46
+ |
+LL | #[derive(PartialEq, PartialOrd)] struct Nope(Comparable);
+ | ^^^^^^^^^^ no implementation for `Comparable < Comparable` and `Comparable > Comparable`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `Comparable`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error[E0277]: can't compare `Comparable` with `Comparable`
+ --> $DIR/issue-34229.rs:2:46
+ |
+LL | #[derive(PartialEq, PartialOrd)] struct Nope(Comparable);
+ | ^^^^^^^^^^ no implementation for `Comparable < Comparable` and `Comparable > Comparable`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `Comparable`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error[E0277]: can't compare `Comparable` with `Comparable`
+ --> $DIR/issue-34229.rs:2:46
+ |
+LL | #[derive(PartialEq, PartialOrd)] struct Nope(Comparable);
+ | ^^^^^^^^^^ no implementation for `Comparable < Comparable` and `Comparable > Comparable`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `Comparable`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error[E0277]: can't compare `Comparable` with `Comparable`
+ --> $DIR/issue-34229.rs:2:46
+ |
+LL | #[derive(PartialEq, PartialOrd)] struct Nope(Comparable);
+ | ^^^^^^^^^^ no implementation for `Comparable < Comparable` and `Comparable > Comparable`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `Comparable`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error: aborting due to 5 previous errors
For more information about this error, try `rustc --explain E0277`.
//~| ERROR expected expression, found reserved identifier `_`
//~| ERROR expected expression, found reserved identifier `_`
let sr2: Vec<(u32, _, _)> = sr.iter().map(|(faction, th_sender, th_receiver)| {}).collect();
- //~^ ERROR no method named `iter` found for type `()` in the current scope
+ //~^ ERROR no method named `iter` found
}
| |
| cannot assign to this expression
-error[E0599]: no method named `iter` found for type `()` in the current scope
+error[E0599]: no method named `iter` found for unit type `()` in the current scope
--> $DIR/issue-34334.rs:9:36
|
LL | let sr2: Vec<(u32, _, _)> = sr.iter().map(|(faction, th_sender, th_receiver)| {}).collect();
-error[E0599]: no method named `is_subset` found for type `&std::collections::HashSet<T>` in the current scope
+error[E0599]: no method named `is_subset` found for reference `&std::collections::HashSet<T>` in the current scope
--> $DIR/issue-35677.rs:4:10
|
LL | this.is_subset(other)
#![derive(Copy)] //~ ERROR `derive` may only be applied to structs, enums and unions
//~| ERROR cannot determine resolution for the derive macro `Copy`
+ //~| ERROR cannot determine resolution for the derive macro `Copy`
fn main() {}
|
= note: import resolution is stuck, try simplifying macro imports
-error: aborting due to 2 previous errors
+error: cannot determine resolution for the derive macro `Copy`
+ --> $DIR/issue-36617.rs:1:11
+ |
+LL | #![derive(Copy)]
+ | ^^^^
+ |
+ = note: import resolution is stuck, try simplifying macro imports
+
+error: aborting due to 3 previous errors
return 1+1 == 2
}
pub fn chirp(&self) {
- self.boom(); //~ ERROR no method named `boom` found for type `&Obj` in the current scope
+ self.boom(); //~ ERROR no method named `boom` found
}
}
-error[E0599]: no method named `boom` found for type `&Obj` in the current scope
+error[E0599]: no method named `boom` found for reference `&Obj` in the current scope
--> $DIR/issue-3707.rs:10:14
|
LL | self.boom();
fn foo<T: Iterator>() {
- T::Item; //~ ERROR no associated item named `Item` found for type `T` in the current scope
+ T::Item; //~ ERROR no associated item named `Item` found
}
fn main() { }
-error[E0599]: no associated item named `Item` found for type `T` in the current scope
+error[E0599]: no associated item named `Item` found for type parameter `T` in the current scope
--> $DIR/issue-38919.rs:2:8
|
LL | T::Item;
-error[E0599]: no method named `exec` found for type `&mut std::process::Command` in the current scope
+error[E0599]: no method named `exec` found for mutable reference `&mut std::process::Command` in the current scope
--> $DIR/issue-39175.rs:15:39
|
LL | Command::new("echo").arg("hello").exec();
pub struct Vector<T, D: Dim> {
entries: [T; D::dim()],
- //~^ ERROR no function or associated item named `dim` found for type `D` in the current scope
+ //~^ ERROR no function or associated item named `dim` found
_dummy: D,
}
-error[E0599]: no function or associated item named `dim` found for type `D` in the current scope
+error[E0599]: no function or associated item named `dim` found for type parameter `D` in the current scope
--> $DIR/issue-39559.rs:14:21
|
LL | entries: [T; D::dim()],
// run-pass
-#![allow(non_snake_case)]
-
// ignore-emscripten FIXME(#45351)
#![feature(repr_simd, platform_intrinsics)]
-#[repr(C)] //~ WARNING conflicting representation hints
#[repr(simd)]
#[derive(Copy, Clone, Debug)]
-pub struct char3(pub i8, pub i8, pub i8);
+pub struct Char3(pub i8, pub i8, pub i8);
-#[repr(C)] //~ WARNING conflicting representation hints
#[repr(simd)]
#[derive(Copy, Clone, Debug)]
-pub struct short3(pub i16, pub i16, pub i16);
+pub struct Short3(pub i16, pub i16, pub i16);
extern "platform-intrinsic" {
fn simd_cast<T, U>(x: T) -> U;
}
fn main() {
- let cast: short3 = unsafe { simd_cast(char3(10, -3, -9)) };
+ let cast: Short3 = unsafe { simd_cast(Char3(10, -3, -9)) };
println!("{:?}", cast);
}
+++ /dev/null
-warning[E0566]: conflicting representation hints
- --> $DIR/issue-39720.rs:8:8
- |
-LL | #[repr(C)]
- | ^
-LL | #[repr(simd)]
- | ^^^^
-
-warning[E0566]: conflicting representation hints
- --> $DIR/issue-39720.rs:13:8
- |
-LL | #[repr(C)]
- | ^
-LL | #[repr(simd)]
- | ^^^^
-
fn main() {
let p = Point::new(0.0, 0.0);
- //~^ ERROR no function or associated item named `new` found for type `Point`
+ //~^ ERROR no function or associated item named `new` found for struct `Point`
println!("{}", p.to_string());
}
LL | | }
| |_____^ not a member of trait `ToString_`
-error[E0599]: no function or associated item named `new` found for type `Point` in the current scope
+error[E0599]: no function or associated item named `new` found for struct `Point` in the current scope
--> $DIR/issue-3973.rs:22:20
|
LL | struct Point {
// Matching against float literals should result in a linter error
#![feature(exclusive_range_pattern)]
+#![feature(half_open_range_patterns)]
#![allow(unused)]
#![forbid(illegal_floating_point_literal_pattern)]
//~| ERROR floating-point types cannot be used in patterns
//~| WARNING this was previously accepted by the compiler but is being
5.0f32 => {}, //~ ERROR floating-point types cannot be used in patterns
+ //~| ERROR floating-point types cannot be used in patterns
+ //~| WARNING hard error
//~| WARNING hard error
-5.0 => {}, //~ ERROR floating-point types cannot be used in patterns
+ //~| ERROR floating-point types cannot be used in patterns
+ //~| WARNING hard error
//~| WARNING hard error
1.0 .. 33.0 => {}, //~ ERROR floating-point types cannot be used in patterns
//~| WARNING hard error
//~| ERROR floating-point types cannot be used in patterns
//~| WARNING hard error
+ //~| ERROR floating-point types cannot be used in patterns
+ //~| WARNING hard error
+ //~| ERROR floating-point types cannot be used in patterns
+ //~| WARNING hard error
39.0 ..= 70.0 => {}, //~ ERROR floating-point types cannot be used in patterns
+ //~| ERROR floating-point types cannot be used in patterns
//~| WARNING hard error
//~| ERROR floating-point types cannot be used in patterns
+ //~| ERROR floating-point types cannot be used in patterns
+ //~| WARNING hard error
+ //~| WARNING hard error
//~| WARNING hard error
+
+ ..71.0 => {}
+ //~^ ERROR floating-point types cannot be used in patterns
+ //~| ERROR floating-point types cannot be used in patterns
+ //~| WARNING hard error
+ //~| WARNING this was previously accepted by the compiler
+ ..=72.0 => {}
+ //~^ ERROR floating-point types cannot be used in patterns
+ //~| ERROR floating-point types cannot be used in patterns
+ //~| WARNING hard error
+ //~| WARNING this was previously accepted by the compiler
+ 71.0.. => {}
+ //~^ ERROR floating-point types cannot be used in patterns
+ //~| ERROR floating-point types cannot be used in patterns
+ //~| WARNING hard error
+ //~| WARNING this was previously accepted by the compiler
_ => {},
};
let y = 5.0;
// Same for tuples
match (x, 5) {
(3.14, 1) => {}, //~ ERROR floating-point types cannot be used
+ //~| ERROR floating-point types cannot be used
+ //~| WARNING hard error
//~| WARNING hard error
_ => {},
}
struct Foo { x: f32 };
match (Foo { x }) {
Foo { x: 2.0 } => {}, //~ ERROR floating-point types cannot be used
+ //~| ERROR floating-point types cannot be used
+ //~| WARNING hard error
//~| WARNING hard error
_ => {},
}
error: floating-point types cannot be used in patterns
- --> $DIR/issue-41255.rs:10:9
+ --> $DIR/issue-41255.rs:11:9
|
LL | 5.0 => {},
| ^^^
|
note: lint level defined here
- --> $DIR/issue-41255.rs:5:11
+ --> $DIR/issue-41255.rs:6:11
|
LL | #![forbid(illegal_floating_point_literal_pattern)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
error: floating-point types cannot be used in patterns
- --> $DIR/issue-41255.rs:14:9
+ --> $DIR/issue-41255.rs:15:9
|
LL | 5.0f32 => {},
| ^^^^^^
= note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
error: floating-point types cannot be used in patterns
- --> $DIR/issue-41255.rs:16:10
+ --> $DIR/issue-41255.rs:19:10
|
LL | -5.0 => {},
| ^^^
= note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
error: floating-point types cannot be used in patterns
- --> $DIR/issue-41255.rs:18:9
+ --> $DIR/issue-41255.rs:23:9
|
LL | 1.0 .. 33.0 => {},
| ^^^
= note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
error: floating-point types cannot be used in patterns
- --> $DIR/issue-41255.rs:18:16
+ --> $DIR/issue-41255.rs:23:16
|
LL | 1.0 .. 33.0 => {},
| ^^^^
= note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
error: floating-point types cannot be used in patterns
- --> $DIR/issue-41255.rs:22:9
+ --> $DIR/issue-41255.rs:31:9
|
LL | 39.0 ..= 70.0 => {},
| ^^^^
= note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
error: floating-point types cannot be used in patterns
- --> $DIR/issue-41255.rs:22:18
+ --> $DIR/issue-41255.rs:31:18
|
LL | 39.0 ..= 70.0 => {},
| ^^^^
= note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
error: floating-point types cannot be used in patterns
- --> $DIR/issue-41255.rs:31:10
+ --> $DIR/issue-41255.rs:40:11
+ |
+LL | ..71.0 => {}
+ | ^^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
+
+error: floating-point types cannot be used in patterns
+ --> $DIR/issue-41255.rs:45:12
+ |
+LL | ..=72.0 => {}
+ | ^^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
+
+error: floating-point types cannot be used in patterns
+ --> $DIR/issue-41255.rs:50:9
+ |
+LL | 71.0.. => {}
+ | ^^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
+
+error: floating-point types cannot be used in patterns
+ --> $DIR/issue-41255.rs:60:10
|
LL | (3.14, 1) => {},
| ^^^^
= note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
error: floating-point types cannot be used in patterns
- --> $DIR/issue-41255.rs:38:18
+ --> $DIR/issue-41255.rs:69:18
|
LL | Foo { x: 2.0 } => {},
| ^^^
= note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
error: floating-point types cannot be used in patterns
- --> $DIR/issue-41255.rs:10:9
+ --> $DIR/issue-41255.rs:11:9
|
LL | 5.0 => {},
| ^^^
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
-error: aborting due to 10 previous errors
+error: floating-point types cannot be used in patterns
+ --> $DIR/issue-41255.rs:15:9
+ |
+LL | 5.0f32 => {},
+ | ^^^^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
+
+error: floating-point types cannot be used in patterns
+ --> $DIR/issue-41255.rs:19:10
+ |
+LL | -5.0 => {},
+ | ^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
+
+error: floating-point types cannot be used in patterns
+ --> $DIR/issue-41255.rs:23:9
+ |
+LL | 1.0 .. 33.0 => {},
+ | ^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
+
+error: floating-point types cannot be used in patterns
+ --> $DIR/issue-41255.rs:23:16
+ |
+LL | 1.0 .. 33.0 => {},
+ | ^^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
+
+error: floating-point types cannot be used in patterns
+ --> $DIR/issue-41255.rs:31:9
+ |
+LL | 39.0 ..= 70.0 => {},
+ | ^^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
+
+error: floating-point types cannot be used in patterns
+ --> $DIR/issue-41255.rs:31:18
+ |
+LL | 39.0 ..= 70.0 => {},
+ | ^^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
+
+error: floating-point types cannot be used in patterns
+ --> $DIR/issue-41255.rs:40:11
+ |
+LL | ..71.0 => {}
+ | ^^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
+
+error: floating-point types cannot be used in patterns
+ --> $DIR/issue-41255.rs:45:12
+ |
+LL | ..=72.0 => {}
+ | ^^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
+
+error: floating-point types cannot be used in patterns
+ --> $DIR/issue-41255.rs:50:9
+ |
+LL | 71.0.. => {}
+ | ^^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
+
+error: floating-point types cannot be used in patterns
+ --> $DIR/issue-41255.rs:60:10
+ |
+LL | (3.14, 1) => {},
+ | ^^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
+
+error: floating-point types cannot be used in patterns
+ --> $DIR/issue-41255.rs:69:18
+ |
+LL | Foo { x: 2.0 } => {},
+ | ^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
+
+error: aborting due to 24 previous errors
fn main() {
let a = iterate(0, |x| x+1);
println!("{:?}", a.iter().take(10).collect::<Vec<usize>>());
- //~^ ERROR no method named `iter` found for type `Iterate<{integer}
+ //~^ ERROR no method named `iter` found
}
-error[E0599]: no method named `iter` found for type `Iterate<{integer}, [closure@$DIR/issue-41880.rs:26:24: 26:31]>` in the current scope
+error[E0599]: no method named `iter` found for struct `Iterate<{integer}, [closure@$DIR/issue-41880.rs:26:24: 26:31]>` in the current scope
--> $DIR/issue-41880.rs:27:24
|
LL | pub struct Iterate<T, F> {
-error[E0599]: no associated item named `String` found for type `std::string::String` in the current scope
+error[E0599]: no associated item named `String` found for struct `std::string::String` in the current scope
--> $DIR/issue-42880.rs:4:22
|
LL | let f = |&Value::String(_)| ();
match 1 {
NUM => unimplemented!(),
//~^ ERROR could not evaluate constant pattern
+ //~| ERROR could not evaluate constant pattern
_ => unimplemented!(),
}
}
LL | NUM => unimplemented!(),
| ^^^
-error: aborting due to 3 previous errors
+error: could not evaluate constant pattern
+ --> $DIR/issue-43105.rs:9:9
+ |
+LL | NUM => unimplemented!(),
+ | ^^^
+
+error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0015`.
extern crate xcrate_issue_43189_b;
fn main() {
().a();
- //~^ ERROR no method named `a` found for type `()` in the current scope [E0599]
+ //~^ ERROR no method named `a` found
}
-error[E0599]: no method named `a` found for type `()` in the current scope
+error[E0599]: no method named `a` found for unit type `()` in the current scope
--> $DIR/issue-43189.rs:10:8
|
LL | ().a();
#![feature(use_extern_macros)]
trait Foo {}
#[derive(Foo::Anything)] //~ ERROR failed to resolve: partially resolved path in a derive macro
+ //~| ERROR failed to resolve: partially resolved path in a derive macro
struct S;
fn main() {}
LL | #[derive(Foo::Anything)]
| ^^^^^^^^^^^^^ partially resolved path in a derive macro
-error: aborting due to previous error
+error[E0433]: failed to resolve: partially resolved path in a derive macro
+ --> $DIR/issue-46101.rs:3:10
+ |
+LL | #[derive(Foo::Anything)]
+ | ^^^^^^^^^^^^^ partially resolved path in a derive macro
+
+error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0433`.
-// check-pass
-
-#[repr(C,u8)] //~ WARNING conflicting representation hints
+#[repr(C, u8)] //~ ERROR conflicting representation hints
enum Foo {
A,
B,
}
-#[repr(C)] //~ WARNING conflicting representation hints
+#[repr(C)] //~ ERROR conflicting representation hints
#[repr(u8)]
enum Bar {
A,
-warning[E0566]: conflicting representation hints
- --> $DIR/issue-47094.rs:3:8
+error[E0566]: conflicting representation hints
+ --> $DIR/issue-47094.rs:1:8
|
-LL | #[repr(C,u8)]
- | ^ ^^
+LL | #[repr(C, u8)]
+ | ^ ^^
-warning[E0566]: conflicting representation hints
- --> $DIR/issue-47094.rs:9:8
+error[E0566]: conflicting representation hints
+ --> $DIR/issue-47094.rs:7:8
|
LL | #[repr(C)]
| ^
LL | #[repr(u8)]
| ^^
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0566`.
fn main() {
let _result = &Some(42).as_deref();
-//~^ ERROR no method named `as_deref` found for type `std::option::Option<{integer}>`
+//~^ ERROR no method named `as_deref` found for enum `std::option::Option<{integer}>`
}
-error[E0599]: no method named `as_deref` found for type `std::option::Option<{integer}>` in the current scope
+error[E0599]: no method named `as_deref` found for enum `std::option::Option<{integer}>` in the current scope
--> $DIR/option-as_deref.rs:2:29
|
LL | let _result = &Some(42).as_deref();
fn main() {
let _result = &mut Some(42).as_deref_mut();
-//~^ ERROR no method named `as_deref_mut` found for type `std::option::Option<{integer}>`
+//~^ ERROR no method named `as_deref_mut` found for enum `std::option::Option<{integer}>`
}
-error[E0599]: no method named `as_deref_mut` found for type `std::option::Option<{integer}>` in the current scope
+error[E0599]: no method named `as_deref_mut` found for enum `std::option::Option<{integer}>` in the current scope
--> $DIR/option-as_deref_mut.rs:2:33
|
LL | let _result = &mut Some(42).as_deref_mut();
-error[E0599]: no method named `as_deref` found for type `std::result::Result<{integer}, _>` in the current scope
+error[E0599]: no method named `as_deref` found for enum `std::result::Result<{integer}, _>` in the current scope
--> $DIR/result-as_deref.rs:4:27
|
LL | let _result = &Ok(42).as_deref();
-error[E0599]: no method named `as_deref_err` found for type `std::result::Result<_, {integer}>` in the current scope
+error[E0599]: no method named `as_deref_err` found for enum `std::result::Result<_, {integer}>` in the current scope
--> $DIR/result-as_deref_err.rs:4:28
|
LL | let _result = &Err(41).as_deref_err();
- | ^^^^^^^^^^^^ help: there is a method with a similar name: `as_deref_ok`
+ | ^^^^^^^^^^^^ help: there is a method with a similar name: `as_deref_mut`
|
= note: the method `as_deref_err` exists but the following trait bounds were not satisfied:
`{integer} : std::ops::Deref`
-error[E0599]: no method named `as_deref_mut` found for type `std::result::Result<{integer}, _>` in the current scope
+error[E0599]: no method named `as_deref_mut` found for enum `std::result::Result<{integer}, _>` in the current scope
--> $DIR/result-as_deref_mut.rs:4:31
|
LL | let _result = &mut Ok(42).as_deref_mut();
-error[E0599]: no method named `as_deref_mut_err` found for type `std::result::Result<_, {integer}>` in the current scope
+error[E0599]: no method named `as_deref_mut_err` found for enum `std::result::Result<_, {integer}>` in the current scope
--> $DIR/result-as_deref_mut_err.rs:4:32
|
LL | let _result = &mut Err(41).as_deref_mut_err();
- | ^^^^^^^^^^^^^^^^ help: there is a method with a similar name: `as_deref_mut_ok`
+ | ^^^^^^^^^^^^^^^^ help: there is a method with a similar name: `as_deref_mut`
|
= note: the method `as_deref_mut_err` exists but the following trait bounds were not satisfied:
`{integer} : std::ops::DerefMut`
+++ /dev/null
-#![feature(inner_deref)]
-
-fn main() {
- let _result = &mut Ok(42).as_deref_mut_ok();
-//~^ ERROR no method named `as_deref_mut_ok` found
-}
+++ /dev/null
-error[E0599]: no method named `as_deref_mut_ok` found for type `std::result::Result<{integer}, _>` in the current scope
- --> $DIR/result-as_deref_mut_ok.rs:4:31
- |
-LL | let _result = &mut Ok(42).as_deref_mut_ok();
- | ^^^^^^^^^^^^^^^ help: there is a method with a similar name: `as_deref_mut_err`
- |
- = note: the method `as_deref_mut_ok` exists but the following trait bounds were not satisfied:
- `{integer} : std::ops::DerefMut`
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0599`.
+++ /dev/null
-#![feature(inner_deref)]
-
-fn main() {
- let _result = &Ok(42).as_deref_ok();
-//~^ ERROR no method named `as_deref_ok` found
-}
+++ /dev/null
-error[E0599]: no method named `as_deref_ok` found for type `std::result::Result<{integer}, _>` in the current scope
- --> $DIR/result-as_deref_ok.rs:4:27
- |
-LL | let _result = &Ok(42).as_deref_ok();
- | ^^^^^^^^^^^ help: there is a method with a similar name: `as_deref_err`
- |
- = note: the method `as_deref_ok` exists but the following trait bounds were not satisfied:
- `{integer} : std::ops::Deref`
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0599`.
//~^ ERROR the trait `Copy` may not be implemented for this type
struct Foo(NotDefined, <i32 as Iterator>::Item, Vec<i32>, String);
//~^ ERROR cannot find type `NotDefined` in this scope
+//~| ERROR cannot find type `NotDefined` in this scope
//~| ERROR `i32` is not an iterator
fn main() {}
LL | struct Foo(NotDefined, <i32 as Iterator>::Item, Vec<i32>, String);
| ^^^^^^^^^^ not found in this scope
+error[E0412]: cannot find type `NotDefined` in this scope
+ --> $DIR/issue-50480.rs:3:12
+ |
+LL | struct Foo(NotDefined, <i32 as Iterator>::Item, Vec<i32>, String);
+ | ^^^^^^^^^^ not found in this scope
+
error[E0277]: `i32` is not an iterator
--> $DIR/issue-50480.rs:3:24
|
| |
| this field does not implement `Copy`
-error: aborting due to 3 previous errors
+error: aborting due to 4 previous errors
Some errors have detailed explanations: E0204, E0277, E0412.
For more information about an error, try `rustc --explain E0204`.
fn main() {
(&5isize as &dyn Foo).foo();
- //~^ ERROR: no method named `foo` found for type `&dyn Foo` in the current scope
+ //~^ ERROR: no method named `foo` found for reference `&dyn Foo` in the current scope
}
-error[E0599]: no method named `foo` found for type `&dyn Foo` in the current scope
+error[E0599]: no method named `foo` found for reference `&dyn Foo` in the current scope
--> $DIR/issue-5153.rs:10:27
|
LL | (&5isize as &dyn Foo).foo();
-error[E0013]: constants cannot refer to statics, use a constant instead
+error[E0013]: constants cannot refer to statics
--> $DIR/issue-52060.rs:4:26
|
LL | static B: [u32; 1] = [0; A.len()];
| ^
+ |
+ = help: consider extracting the value of the `static` to a `const`, and referring to that
error[E0080]: evaluation of constant value failed
--> $DIR/issue-52060.rs:4:26
fn $n() {
S::f::<i64>();
//~^ ERROR wrong number of type arguments
+ //~| ERROR wrong number of type arguments
}
)*
}
LL | impl_add!(a b);
| --------------- in this macro invocation
-error: aborting due to previous error
+error[E0107]: wrong number of type arguments: expected 0, found 1
+ --> $DIR/issue-53251.rs:11:24
+ |
+LL | S::f::<i64>();
+ | ^^^ unexpected type argument
+...
+LL | impl_add!(a b);
+ | --------------- in this macro invocation
+
+error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0107`.
LL | let _ = test.comps.inner.lock().unwrap();
| ^^^^^^^^^^^^^^^^
-error[E0599]: no method named `unwrap` found for type `std::sys_common::mutex::MutexGuard<'_>` in the current scope
+error[E0599]: no method named `unwrap` found for struct `std::sys_common::mutex::MutexGuard<'_>` in the current scope
--> $DIR/issue-54062.rs:10:37
|
LL | let _ = test.comps.inner.lock().unwrap();
-error[E0599]: no method named `f` found for type `fn(&u8)` in the current scope
+error[E0599]: no method named `f` found for fn pointer `fn(&u8)` in the current scope
--> $DIR/issue-57362-1.rs:20:7
|
LL | a.f();
-error[E0599]: no function or associated item named `make_g` found for type `for<'r> fn(&'r ())` in the current scope
+error[E0599]: no function or associated item named `make_g` found for fn pointer `for<'r> fn(&'r ())` in the current scope
--> $DIR/issue-57362-2.rs:22:25
|
LL | let x = <fn (&())>::make_g();
| ------------------ required by `Foo::SIZE`
LL |
LL | fn new(slice: &[u8; Foo::SIZE]) -> Self;
- | ^^^^^^^^^ cannot infer type
+ | ^^^^^^^^^
+ | |
+ | cannot infer type
+ | help: use the fully qualified path to an implementation: `<Type as Foo>::SIZE`
|
= note: cannot resolve `_: Foo`
+ = note: associated constants cannot be accessed directly on a `trait`, they can only be accessed through a specific `impl`
error: aborting due to 2 previous errors
Trait::exists(());
// no object safety error
Trait::nonexistent(());
- //~^ ERROR no function or associated item named `nonexistent` found for type `dyn Trait`
+ //~^ ERROR no function or associated item named `nonexistent` found
}
-error[E0599]: no function or associated item named `nonexistent` found for type `dyn Trait` in the current scope
+error[E0599]: no function or associated item named `nonexistent` found for trait object `dyn Trait` in the current scope
--> $DIR/issue-58734.rs:20:12
|
LL | Trait::nonexistent(());
trait MkSvc<Target, Req> = Svc<Target> where Self::Res: Svc<Req>;
//~^ ERROR associated type `Res` not found for `Self`
+//~| ERROR associated type `Res` not found for `Self`
fn main() {}
LL | trait MkSvc<Target, Req> = Svc<Target> where Self::Res: Svc<Req>;
| ^^^ associated type `Res` not found
-error: aborting due to previous error
+error[E0220]: associated type `Res` not found for `Self`
+ --> $DIR/issue-59029-1.rs:5:52
+ |
+LL | trait MkSvc<Target, Req> = Svc<Target> where Self::Res: Svc<Req>;
+ | ^^^ associated type `Res` not found
+
+error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0220`.
+// error-pattern:this file contains an unclosed delimiter
+// error-pattern:xpected `{`, found `macro_rules`
+
fn main() {}
fn foo(u: u8) { if u8 macro_rules! u8 { (u6) => { fn uuuuuuuuuuu() { use s loo mod u8 {
-//~^ ERROR expected `{`, found `macro_rules`
-//~ ERROR this file contains an unclosed delimiter
error: this file contains an unclosed delimiter
- --> $DIR/issue-62554.rs:5:52
+ --> $DIR/issue-62554.rs:6:89
|
LL | fn foo(u: u8) { if u8 macro_rules! u8 { (u6) => { fn uuuuuuuuuuu() { use s loo mod u8 {
- | - - - - - unclosed delimiter
- | | | | |
+ | - - - - - ^
+ | | | | | |
+ | | | | | unclosed delimiter
+ | | | | unclosed delimiter
+ | | | unclosed delimiter
+ | unclosed delimiter unclosed delimiter
+
+error: this file contains an unclosed delimiter
+ --> $DIR/issue-62554.rs:6:89
+ |
+LL | fn foo(u: u8) { if u8 macro_rules! u8 { (u6) => { fn uuuuuuuuuuu() { use s loo mod u8 {
+ | - - - - - ^
+ | | | | | |
+ | | | | | unclosed delimiter
+ | | | | unclosed delimiter
+ | | | unclosed delimiter
+ | unclosed delimiter unclosed delimiter
+
+error: this file contains an unclosed delimiter
+ --> $DIR/issue-62554.rs:6:89
+ |
+LL | fn foo(u: u8) { if u8 macro_rules! u8 { (u6) => { fn uuuuuuuuuuu() { use s loo mod u8 {
+ | - - - - - ^
+ | | | | | |
+ | | | | | unclosed delimiter
+ | | | | unclosed delimiter
+ | | | unclosed delimiter
+ | unclosed delimiter unclosed delimiter
+
+error: this file contains an unclosed delimiter
+ --> $DIR/issue-62554.rs:6:89
+ |
+LL | fn foo(u: u8) { if u8 macro_rules! u8 { (u6) => { fn uuuuuuuuuuu() { use s loo mod u8 {
+ | - - - - - ^
+ | | | | | |
+ | | | | | unclosed delimiter
+ | | | | unclosed delimiter
+ | | | unclosed delimiter
+ | unclosed delimiter unclosed delimiter
+
+error: this file contains an unclosed delimiter
+ --> $DIR/issue-62554.rs:6:89
+ |
+LL | fn foo(u: u8) { if u8 macro_rules! u8 { (u6) => { fn uuuuuuuuuuu() { use s loo mod u8 {
+ | - - - - - ^
+ | | | | | |
+ | | | | | unclosed delimiter
| | | | unclosed delimiter
| | | unclosed delimiter
| unclosed delimiter unclosed delimiter
-LL |
-LL |
- | ^
error: expected `{`, found `macro_rules`
- --> $DIR/issue-62554.rs:3:23
+ --> $DIR/issue-62554.rs:6:23
|
LL | fn foo(u: u8) { if u8 macro_rules! u8 { (u6) => { fn uuuuuuuuuuu() { use s loo mod u8 {
| -- ^^^^^^^^^^^ expected `{`
help: try placing this code inside a block
|
LL | fn foo(u: u8) { if u8 { macro_rules! u8 { (u6) => { fn uuuuuuuuuuu() { use s loo mod u8 {
-LL |
-LL |
LL | }
|
-error: aborting due to 2 previous errors
+error: aborting due to 6 previous errors
-error[E0599]: no method named `bar` found for type `Foo` in the current scope
+error[E0599]: no method named `bar` found for struct `Foo` in the current scope
--> $DIR/issue-64430.rs:7:9
|
LL | pub struct Foo;
trait Bar {}
fn do_stuff<T : Bar>(t : T) {
- t.foo() //~ ERROR no method named `foo` found for type `T` in the current scope
+ t.foo() //~ ERROR no method named `foo` found
}
fn main() {}
-error[E0599]: no method named `foo` found for type `T` in the current scope
+error[E0599]: no method named `foo` found for type parameter `T` in the current scope
--> $DIR/issue-65284-suggest-generic-trait-bound.rs:8:7
|
LL | t.foo()
--- /dev/null
+pub struct Foo {
+ pub bar: Vec<i32>ö
+ //~^ ERROR expected `,`, or `}`, found `ö`
+} //~ ERROR expected `:`, found `}`
+
+fn main() {}
--- /dev/null
+error: expected `,`, or `}`, found `ö`
+ --> $DIR/issue-68000-unicode-ident-after-missing-comma.rs:2:22
+ |
+LL | pub bar: Vec<i32>ö
+ | ^ help: try adding a comma: `,`
+
+error: expected `:`, found `}`
+ --> $DIR/issue-68000-unicode-ident-after-missing-comma.rs:4:1
+ |
+LL | pub bar: Vec<i32>ö
+ | - expected `:`
+LL |
+LL | }
+ | ^ unexpected token
+
+error: aborting due to 2 previous errors
+
match [x, 1.0] {
[NAN, _] => {}, //~ ERROR floating-point types cannot be used
- //~^ WARN this was previously accepted by the compiler but is being phased out
+ //~| ERROR floating-point types cannot be used
+ //~| WARN this was previously accepted by the compiler but is being phased out
+ //~| WARN this was previously accepted by the compiler but is being phased out
_ => {},
};
}
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
-error: aborting due to 3 previous errors
+error: floating-point types cannot be used in patterns
+ --> $DIR/issue-6804.rs:19:10
+ |
+LL | [NAN, _] => {},
+ | ^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
+
+error: aborting due to 4 previous errors
--- /dev/null
+macro_rules! x {
+ ($($c:tt)*) => {
+ $($c)ö* {} //~ ERROR missing condition for `if` expression
+ }; //~| ERROR mismatched types
+}
+
+fn main() {
+ x!(if);
+}
--- /dev/null
+error: missing condition for `if` expression
+ --> $DIR/issue-68091-unicode-ident-after-if.rs:3:14
+ |
+LL | $($c)ö* {}
+ | ^ expected if condition here
+
+error[E0308]: mismatched types
+ --> $DIR/issue-68091-unicode-ident-after-if.rs:3:17
+ |
+LL | $($c)ö* {}
+ | ^^ expected `bool`, found `()`
+...
+LL | x!(if);
+ | ------- in this macro invocation
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+macro_rules! x {
+ ($($c:tt)*) => {
+ $($c)ö* //~ ERROR macro expansion ends with an incomplete expression: expected expression
+ };
+}
+
+fn main() {
+ x!(!);
+}
--- /dev/null
+error: macro expansion ends with an incomplete expression: expected expression
+ --> $DIR/issue-68092-unicode-ident-after-incomplete-expr.rs:3:14
+ |
+LL | $($c)ö*
+ | ^ expected expression
+
+error: aborting due to previous error
+
--- /dev/null
+// check-pass
+
+pub extern crate self as name;
+pub use name::name as bug;
+
+fn main() {}
+// FIXME: missing sysroot spans (#53081)
+// ignore-i586-unknown-linux-gnu
+// ignore-i586-unknown-linux-musl
+// ignore-i686-unknown-linux-musl
struct Foo {
x: isize
}
error[E0412]: cannot find type `Fo` in this scope
- --> $DIR/issue-7607-1.rs:5:6
+ --> $DIR/issue-7607-1.rs:9:6
|
LL | impl Fo {
| ^^ help: a trait with a similar name exists: `Fn`
+ |
+ ::: $SRC_DIR/libcore/ops/function.rs:LL:COL
+ |
+LL | pub trait Fn<Args>: FnMut<Args> {
+ | ------------------------------- similarly named trait `Fn` defined here
error: aborting due to previous error
fn main() {
Foo::bar();
- //~^ ERROR no function or associated item named `bar` found for type `Foo`
+ //~^ ERROR no function or associated item named `bar` found for struct `Foo`
}
-error[E0599]: no function or associated item named `bar` found for type `Foo` in the current scope
+error[E0599]: no function or associated item named `bar` found for struct `Foo` in the current scope
--> $DIR/issue-7950.rs:6:10
|
LL | struct Foo;
| --- ---- required by this bound in `bar`
...
LL | bar(move|| foo(x));
- | ^^^ `std::rc::Rc<usize>` cannot be sent between threads safely
+ | ^^^ ------------- within this `[closure@$DIR/kindck-nonsendable-1.rs:9:9: 9:22 x:std::rc::Rc<usize>]`
+ | |
+ | `std::rc::Rc<usize>` cannot be sent between threads safely
|
= help: within `[closure@$DIR/kindck-nonsendable-1.rs:9:9: 9:22 x:std::rc::Rc<usize>]`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<usize>`
= note: required because it appears within the type `[closure@$DIR/kindck-nonsendable-1.rs:9:9: 9:22 x:std::rc::Rc<usize>]`
LL | use T;
|
-error[E0599]: no function or associated item named `f` found for type `Foo` in the current scope
+error[E0599]: no function or associated item named `f` found for type parameter `Foo` in the current scope
--> $DIR/lexical-scopes.rs:10:10
|
LL | Foo::f();
#[allow(deprecated)]
//~^ ERROR allow(deprecated) overruled by outer forbid(deprecated)
+//~| ERROR allow(deprecated) overruled by outer forbid(deprecated)
+//~| ERROR allow(deprecated) overruled by outer forbid(deprecated)
fn main() {
}
LL | #[allow(deprecated)]
| ^^^^^^^^^^ overruled by previous forbid
-error: aborting due to previous error
+error[E0453]: allow(deprecated) overruled by outer forbid(deprecated)
+ --> $DIR/lint-forbid-attr.rs:3:9
+ |
+LL | #![forbid(deprecated)]
+ | ---------- `forbid` level set here
+LL |
+LL | #[allow(deprecated)]
+ | ^^^^^^^^^^ overruled by previous forbid
+
+error[E0453]: allow(deprecated) overruled by outer forbid(deprecated)
+ --> $DIR/lint-forbid-attr.rs:3:9
+ |
+LL | #![forbid(deprecated)]
+ | ---------- `forbid` level set here
+LL |
+LL | #[allow(deprecated)]
+ | ^^^^^^^^^^ overruled by previous forbid
+
+error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0453`.
// compile-flags: -F deprecated
#[allow(deprecated)] //~ ERROR allow(deprecated) overruled by outer forbid(deprecated)
+ //~| ERROR allow(deprecated) overruled by outer forbid(deprecated)
+ //~| ERROR allow(deprecated) overruled by outer forbid(deprecated)
fn main() {
}
|
= note: `forbid` lint level was set on command line
-error: aborting due to previous error
+error[E0453]: allow(deprecated) overruled by outer forbid(deprecated)
+ --> $DIR/lint-forbid-cmdline.rs:3:9
+ |
+LL | #[allow(deprecated)]
+ | ^^^^^^^^^^ overruled by previous forbid
+ |
+ = note: `forbid` lint level was set on command line
+
+error[E0453]: allow(deprecated) overruled by outer forbid(deprecated)
+ --> $DIR/lint-forbid-cmdline.rs:3:9
+ |
+LL | #[allow(deprecated)]
+ | ^^^^^^^^^^ overruled by previous forbid
+ |
+ = note: `forbid` lint level was set on command line
+
+error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0453`.
#![deny = "foo"] //~ ERROR malformed `deny` attribute input
#![allow(bar = "baz")] //~ ERROR malformed lint attribute
-
+ //~| ERROR malformed lint attribute
+ //~| ERROR malformed lint attribute
+ //~| ERROR malformed lint attribute
+ //~| ERROR malformed lint attribute
+ //~| ERROR malformed lint attribute
fn main() { }
LL | #![allow(bar = "baz")]
| ^^^^^^^^^^^ bad attribute argument
+error[E0452]: malformed lint attribute input
+ --> $DIR/lint-malformed.rs:2:10
+ |
+LL | #![allow(bar = "baz")]
+ | ^^^^^^^^^^^ bad attribute argument
+
error: malformed `deny` attribute input
--> $DIR/lint-malformed.rs:1:1
|
LL | #![deny = "foo"]
| ^^^^^^^^^^^^^^^^ help: must be of the form: `#[deny(lint1, lint2, ..., /*opt*/ reason = "...")]`
-error: aborting due to 2 previous errors
+error[E0452]: malformed lint attribute input
+ --> $DIR/lint-malformed.rs:2:10
+ |
+LL | #![allow(bar = "baz")]
+ | ^^^^^^^^^^^ bad attribute argument
+
+error[E0452]: malformed lint attribute input
+ --> $DIR/lint-malformed.rs:2:10
+ |
+LL | #![allow(bar = "baz")]
+ | ^^^^^^^^^^^ bad attribute argument
+
+error[E0452]: malformed lint attribute input
+ --> $DIR/lint-malformed.rs:2:10
+ |
+LL | #![allow(bar = "baz")]
+ | ^^^^^^^^^^^ bad attribute argument
+
+error[E0452]: malformed lint attribute input
+ --> $DIR/lint-malformed.rs:2:10
+ |
+LL | #![allow(bar = "baz")]
+ | ^^^^^^^^^^^ bad attribute argument
+
+error: aborting due to 7 previous errors
For more information about this error, try `rustc --explain E0452`.
|
= note: requested on the command line with `-D raw_pointer_derive`
+warning: lint `raw_pointer_derive` has been removed: `using derive with raw pointers is ok`
+ |
+ = note: requested on the command line with `-D raw_pointer_derive`
+
+warning: lint `raw_pointer_derive` has been removed: `using derive with raw pointers is ok`
+ |
+ = note: requested on the command line with `-D raw_pointer_derive`
+
+warning: lint `raw_pointer_derive` has been removed: `using derive with raw pointers is ok`
+ |
+ = note: requested on the command line with `-D raw_pointer_derive`
+
error: unused variable: `unused`
--> $DIR/lint-removed-cmdline.rs:12:17
|
|
= note: requested on the command line with `-D bare_trait_object`
+warning: lint `bare_trait_object` has been renamed to `bare_trait_objects`
+ |
+ = note: requested on the command line with `-D bare_trait_object`
+
+warning: lint `bare_trait_object` has been renamed to `bare_trait_objects`
+ |
+ = note: requested on the command line with `-D bare_trait_object`
+
+warning: lint `bare_trait_object` has been renamed to `bare_trait_objects`
+ |
+ = note: requested on the command line with `-D bare_trait_object`
+
error: unused variable: `unused`
--> $DIR/lint-renamed-cmdline.rs:8:17
|
struct S1<T: TraitWithAssociatedTypes>(T::TypeUnstable);
struct S2<T: TraitWithAssociatedTypes>(T::TypeDeprecated);
//~^ WARN use of deprecated item 'lint_stability::TraitWithAssociatedTypes::TypeDeprecated': text
+ //~| WARN use of deprecated item 'lint_stability::TraitWithAssociatedTypes::TypeDeprecated': text
type A = dyn TraitWithAssociatedTypes<
TypeUnstable = u8,
TypeDeprecated = u16,
//~^ WARN use of deprecated item 'lint_stability::TraitWithAssociatedTypes::TypeDeprecated'
+ //~| WARN use of deprecated item 'lint_stability::TraitWithAssociatedTypes::TypeDeprecated'
+ //~| WARN use of deprecated item 'lint_stability::TraitWithAssociatedTypes::TypeDeprecated'
>;
let _ = DeprecatedStruct { //~ WARN use of deprecated item 'lint_stability::DeprecatedStruct'
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'lint_stability::DeprecatedStruct': text
- --> $DIR/lint-stability-deprecated.rs:106:17
+ --> $DIR/lint-stability-deprecated.rs:109:17
|
LL | let _ = DeprecatedStruct {
| ^^^^^^^^^^^^^^^^
warning: use of deprecated item 'lint_stability::DeprecatedUnstableStruct': text
- --> $DIR/lint-stability-deprecated.rs:109:17
+ --> $DIR/lint-stability-deprecated.rs:112:17
|
LL | let _ = DeprecatedUnstableStruct {
| ^^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'lint_stability::DeprecatedUnitStruct': text
- --> $DIR/lint-stability-deprecated.rs:116:17
+ --> $DIR/lint-stability-deprecated.rs:119:17
|
LL | let _ = DeprecatedUnitStruct;
| ^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'lint_stability::DeprecatedUnstableUnitStruct': text
- --> $DIR/lint-stability-deprecated.rs:117:17
+ --> $DIR/lint-stability-deprecated.rs:120:17
|
LL | let _ = DeprecatedUnstableUnitStruct;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'lint_stability::Enum::DeprecatedVariant': text
- --> $DIR/lint-stability-deprecated.rs:121:17
+ --> $DIR/lint-stability-deprecated.rs:124:17
|
LL | let _ = Enum::DeprecatedVariant;
| ^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'lint_stability::Enum::DeprecatedUnstableVariant': text
- --> $DIR/lint-stability-deprecated.rs:122:17
+ --> $DIR/lint-stability-deprecated.rs:125:17
|
LL | let _ = Enum::DeprecatedUnstableVariant;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'lint_stability::DeprecatedTupleStruct': text
- --> $DIR/lint-stability-deprecated.rs:126:17
+ --> $DIR/lint-stability-deprecated.rs:129:17
|
LL | let _ = DeprecatedTupleStruct (1);
| ^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'lint_stability::DeprecatedUnstableTupleStruct': text
- --> $DIR/lint-stability-deprecated.rs:127:17
+ --> $DIR/lint-stability-deprecated.rs:130:17
|
LL | let _ = DeprecatedUnstableTupleStruct (1);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'lint_stability::deprecated_text': text
- --> $DIR/lint-stability-deprecated.rs:136:25
+ --> $DIR/lint-stability-deprecated.rs:139:25
|
LL | macro_test_arg!(deprecated_text());
| ^^^^^^^^^^^^^^^
warning: use of deprecated item 'lint_stability::deprecated_unstable_text': text
- --> $DIR/lint-stability-deprecated.rs:137:25
+ --> $DIR/lint-stability-deprecated.rs:140:25
|
LL | macro_test_arg!(deprecated_unstable_text());
| ^^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'lint_stability::deprecated_text': text
- --> $DIR/lint-stability-deprecated.rs:138:41
+ --> $DIR/lint-stability-deprecated.rs:141:41
|
LL | macro_test_arg!(macro_test_arg!(deprecated_text()));
| ^^^^^^^^^^^^^^^
warning: use of deprecated item 'lint_stability::Trait::trait_deprecated': text
- --> $DIR/lint-stability-deprecated.rs:143:9
+ --> $DIR/lint-stability-deprecated.rs:146:9
|
LL | Trait::trait_deprecated(&foo);
| ^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'lint_stability::Trait::trait_deprecated': text
- --> $DIR/lint-stability-deprecated.rs:145:9
+ --> $DIR/lint-stability-deprecated.rs:148:9
|
LL | <Foo as Trait>::trait_deprecated(&foo);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_text': text
- --> $DIR/lint-stability-deprecated.rs:147:9
+ --> $DIR/lint-stability-deprecated.rs:150:9
|
LL | Trait::trait_deprecated_text(&foo);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_text': text
- --> $DIR/lint-stability-deprecated.rs:149:9
+ --> $DIR/lint-stability-deprecated.rs:152:9
|
LL | <Foo as Trait>::trait_deprecated_text(&foo);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable': text
- --> $DIR/lint-stability-deprecated.rs:151:9
+ --> $DIR/lint-stability-deprecated.rs:154:9
|
LL | Trait::trait_deprecated_unstable(&foo);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable': text
- --> $DIR/lint-stability-deprecated.rs:153:9
+ --> $DIR/lint-stability-deprecated.rs:156:9
|
LL | <Foo as Trait>::trait_deprecated_unstable(&foo);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable_text': text
- --> $DIR/lint-stability-deprecated.rs:155:9
+ --> $DIR/lint-stability-deprecated.rs:158:9
|
LL | ... Trait::trait_deprecated_unstable_text(&foo);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable_text': text
- --> $DIR/lint-stability-deprecated.rs:157:9
+ --> $DIR/lint-stability-deprecated.rs:160:9
|
LL | ... <Foo as Trait>::trait_deprecated_unstable_text(&foo);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'lint_stability::DeprecatedTrait': text
- --> $DIR/lint-stability-deprecated.rs:185:10
+ --> $DIR/lint-stability-deprecated.rs:188:10
|
LL | impl DeprecatedTrait for S {}
| ^^^^^^^^^^^^^^^
warning: use of deprecated item 'lint_stability::DeprecatedTrait': text
- --> $DIR/lint-stability-deprecated.rs:187:25
+ --> $DIR/lint-stability-deprecated.rs:190:25
|
LL | trait LocalTrait2 : DeprecatedTrait { }
| ^^^^^^^^^^^^^^^
warning: use of deprecated item 'inheritance::inherited_stability::unstable_mod::deprecated': text
- --> $DIR/lint-stability-deprecated.rs:206:9
+ --> $DIR/lint-stability-deprecated.rs:209:9
|
LL | unstable_mod::deprecated();
| ^^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'this_crate::deprecated': text
- --> $DIR/lint-stability-deprecated.rs:328:9
+ --> $DIR/lint-stability-deprecated.rs:331:9
|
LL | deprecated();
| ^^^^^^^^^^
warning: use of deprecated item 'this_crate::Trait::trait_deprecated': text
- --> $DIR/lint-stability-deprecated.rs:333:9
+ --> $DIR/lint-stability-deprecated.rs:336:9
|
LL | Trait::trait_deprecated(&foo);
| ^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'this_crate::Trait::trait_deprecated': text
- --> $DIR/lint-stability-deprecated.rs:335:9
+ --> $DIR/lint-stability-deprecated.rs:338:9
|
LL | <Foo as Trait>::trait_deprecated(&foo);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'this_crate::deprecated_text': text
- --> $DIR/lint-stability-deprecated.rs:337:9
+ --> $DIR/lint-stability-deprecated.rs:340:9
|
LL | deprecated_text();
| ^^^^^^^^^^^^^^^
warning: use of deprecated item 'this_crate::Trait::trait_deprecated_text': text
- --> $DIR/lint-stability-deprecated.rs:342:9
+ --> $DIR/lint-stability-deprecated.rs:345:9
|
LL | Trait::trait_deprecated_text(&foo);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'this_crate::Trait::trait_deprecated_text': text
- --> $DIR/lint-stability-deprecated.rs:344:9
+ --> $DIR/lint-stability-deprecated.rs:347:9
|
LL | <Foo as Trait>::trait_deprecated_text(&foo);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'this_crate::DeprecatedStruct': text
- --> $DIR/lint-stability-deprecated.rs:382:17
+ --> $DIR/lint-stability-deprecated.rs:385:17
|
LL | let _ = DeprecatedStruct {
| ^^^^^^^^^^^^^^^^
warning: use of deprecated item 'this_crate::DeprecatedUnitStruct': text
- --> $DIR/lint-stability-deprecated.rs:389:17
+ --> $DIR/lint-stability-deprecated.rs:392:17
|
LL | let _ = DeprecatedUnitStruct;
| ^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'this_crate::Enum::DeprecatedVariant': text
- --> $DIR/lint-stability-deprecated.rs:393:17
+ --> $DIR/lint-stability-deprecated.rs:396:17
|
LL | let _ = Enum::DeprecatedVariant;
| ^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'this_crate::DeprecatedTupleStruct': text
- --> $DIR/lint-stability-deprecated.rs:397:17
+ --> $DIR/lint-stability-deprecated.rs:400:17
|
LL | let _ = DeprecatedTupleStruct (1);
| ^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'this_crate::Trait::trait_deprecated': text
- --> $DIR/lint-stability-deprecated.rs:404:9
+ --> $DIR/lint-stability-deprecated.rs:407:9
|
LL | Trait::trait_deprecated(&foo);
| ^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'this_crate::Trait::trait_deprecated': text
- --> $DIR/lint-stability-deprecated.rs:406:9
+ --> $DIR/lint-stability-deprecated.rs:409:9
|
LL | <Foo as Trait>::trait_deprecated(&foo);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'this_crate::Trait::trait_deprecated_text': text
- --> $DIR/lint-stability-deprecated.rs:408:9
+ --> $DIR/lint-stability-deprecated.rs:411:9
|
LL | Trait::trait_deprecated_text(&foo);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'this_crate::Trait::trait_deprecated_text': text
- --> $DIR/lint-stability-deprecated.rs:410:9
+ --> $DIR/lint-stability-deprecated.rs:413:9
|
LL | <Foo as Trait>::trait_deprecated_text(&foo);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'this_crate::test_fn_body::fn_in_body': text
- --> $DIR/lint-stability-deprecated.rs:437:9
+ --> $DIR/lint-stability-deprecated.rs:440:9
|
LL | fn_in_body();
| ^^^^^^^^^^
warning: use of deprecated item 'this_crate::DeprecatedTrait': text
- --> $DIR/lint-stability-deprecated.rs:457:10
+ --> $DIR/lint-stability-deprecated.rs:460:10
|
LL | impl DeprecatedTrait for S { }
| ^^^^^^^^^^^^^^^
warning: use of deprecated item 'this_crate::DeprecatedTrait': text
- --> $DIR/lint-stability-deprecated.rs:459:24
+ --> $DIR/lint-stability-deprecated.rs:462:24
|
LL | trait LocalTrait : DeprecatedTrait { }
| ^^^^^^^^^^^^^^^
warning: use of deprecated item 'this_crate::MethodTester::test_method_body::fn_in_body': text
- --> $DIR/lint-stability-deprecated.rs:445:13
+ --> $DIR/lint-stability-deprecated.rs:448:13
|
LL | fn_in_body();
| ^^^^^^^^^^
| ^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'lint_stability::TraitWithAssociatedTypes::TypeDeprecated': text
- --> $DIR/lint-stability-deprecated.rs:102:13
+ --> $DIR/lint-stability-deprecated.rs:103:13
|
LL | TypeDeprecated = u16,
| ^^^^^^^^^^^^^^^^^^^^
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'lint_stability::DeprecatedStruct::i': text
- --> $DIR/lint-stability-deprecated.rs:107:13
+ --> $DIR/lint-stability-deprecated.rs:110:13
|
LL | i: 0
| ^^^^
warning: use of deprecated item 'lint_stability::DeprecatedUnstableStruct::i': text
- --> $DIR/lint-stability-deprecated.rs:111:13
+ --> $DIR/lint-stability-deprecated.rs:114:13
|
LL | i: 0
| ^^^^
warning: use of deprecated item 'lint_stability::Trait::trait_deprecated': text
- --> $DIR/lint-stability-deprecated.rs:142:13
+ --> $DIR/lint-stability-deprecated.rs:145:13
|
LL | foo.trait_deprecated();
| ^^^^^^^^^^^^^^^^
warning: use of deprecated item 'lint_stability::Trait::trait_deprecated': text
- --> $DIR/lint-stability-deprecated.rs:144:9
+ --> $DIR/lint-stability-deprecated.rs:147:9
|
LL | <Foo>::trait_deprecated(&foo);
| ^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_text': text
- --> $DIR/lint-stability-deprecated.rs:146:13
+ --> $DIR/lint-stability-deprecated.rs:149:13
|
LL | foo.trait_deprecated_text();
| ^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_text': text
- --> $DIR/lint-stability-deprecated.rs:148:9
+ --> $DIR/lint-stability-deprecated.rs:151:9
|
LL | <Foo>::trait_deprecated_text(&foo);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable': text
- --> $DIR/lint-stability-deprecated.rs:150:13
+ --> $DIR/lint-stability-deprecated.rs:153:13
|
LL | foo.trait_deprecated_unstable();
| ^^^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable': text
- --> $DIR/lint-stability-deprecated.rs:152:9
+ --> $DIR/lint-stability-deprecated.rs:155:9
|
LL | <Foo>::trait_deprecated_unstable(&foo);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable_text': text
- --> $DIR/lint-stability-deprecated.rs:154:13
+ --> $DIR/lint-stability-deprecated.rs:157:13
|
LL | foo.trait_deprecated_unstable_text();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable_text': text
- --> $DIR/lint-stability-deprecated.rs:156:9
+ --> $DIR/lint-stability-deprecated.rs:159:9
|
LL | ... <Foo>::trait_deprecated_unstable_text(&foo);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'lint_stability::Trait::trait_deprecated': text
- --> $DIR/lint-stability-deprecated.rs:173:13
+ --> $DIR/lint-stability-deprecated.rs:176:13
|
LL | foo.trait_deprecated();
| ^^^^^^^^^^^^^^^^
warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_text': text
- --> $DIR/lint-stability-deprecated.rs:174:13
+ --> $DIR/lint-stability-deprecated.rs:177:13
|
LL | foo.trait_deprecated_text();
| ^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable': text
- --> $DIR/lint-stability-deprecated.rs:175:13
+ --> $DIR/lint-stability-deprecated.rs:178:13
|
LL | foo.trait_deprecated_unstable();
| ^^^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable_text': text
- --> $DIR/lint-stability-deprecated.rs:176:13
+ --> $DIR/lint-stability-deprecated.rs:179:13
|
LL | foo.trait_deprecated_unstable_text();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'this_crate::MethodTester::method_deprecated': text
- --> $DIR/lint-stability-deprecated.rs:329:13
+ --> $DIR/lint-stability-deprecated.rs:332:13
|
LL | foo.method_deprecated();
| ^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'this_crate::MethodTester::method_deprecated': text
- --> $DIR/lint-stability-deprecated.rs:330:9
+ --> $DIR/lint-stability-deprecated.rs:333:9
|
LL | Foo::method_deprecated(&foo);
| ^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'this_crate::MethodTester::method_deprecated': text
- --> $DIR/lint-stability-deprecated.rs:331:9
+ --> $DIR/lint-stability-deprecated.rs:334:9
|
LL | <Foo>::method_deprecated(&foo);
| ^^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'this_crate::Trait::trait_deprecated': text
- --> $DIR/lint-stability-deprecated.rs:332:13
+ --> $DIR/lint-stability-deprecated.rs:335:13
|
LL | foo.trait_deprecated();
| ^^^^^^^^^^^^^^^^
warning: use of deprecated item 'this_crate::Trait::trait_deprecated': text
- --> $DIR/lint-stability-deprecated.rs:334:9
+ --> $DIR/lint-stability-deprecated.rs:337:9
|
LL | <Foo>::trait_deprecated(&foo);
| ^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'this_crate::MethodTester::method_deprecated_text': text
- --> $DIR/lint-stability-deprecated.rs:338:13
+ --> $DIR/lint-stability-deprecated.rs:341:13
|
LL | foo.method_deprecated_text();
| ^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'this_crate::MethodTester::method_deprecated_text': text
- --> $DIR/lint-stability-deprecated.rs:339:9
+ --> $DIR/lint-stability-deprecated.rs:342:9
|
LL | Foo::method_deprecated_text(&foo);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'this_crate::MethodTester::method_deprecated_text': text
- --> $DIR/lint-stability-deprecated.rs:340:9
+ --> $DIR/lint-stability-deprecated.rs:343:9
|
LL | <Foo>::method_deprecated_text(&foo);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'this_crate::Trait::trait_deprecated_text': text
- --> $DIR/lint-stability-deprecated.rs:341:13
+ --> $DIR/lint-stability-deprecated.rs:344:13
|
LL | foo.trait_deprecated_text();
| ^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'this_crate::Trait::trait_deprecated_text': text
- --> $DIR/lint-stability-deprecated.rs:343:9
+ --> $DIR/lint-stability-deprecated.rs:346:9
|
LL | <Foo>::trait_deprecated_text(&foo);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'this_crate::DeprecatedStruct::i': text
- --> $DIR/lint-stability-deprecated.rs:384:13
+ --> $DIR/lint-stability-deprecated.rs:387:13
|
LL | i: 0
| ^^^^
warning: use of deprecated item 'this_crate::Trait::trait_deprecated': text
- --> $DIR/lint-stability-deprecated.rs:403:13
+ --> $DIR/lint-stability-deprecated.rs:406:13
|
LL | foo.trait_deprecated();
| ^^^^^^^^^^^^^^^^
warning: use of deprecated item 'this_crate::Trait::trait_deprecated': text
- --> $DIR/lint-stability-deprecated.rs:405:9
+ --> $DIR/lint-stability-deprecated.rs:408:9
|
LL | <Foo>::trait_deprecated(&foo);
| ^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'this_crate::Trait::trait_deprecated_text': text
- --> $DIR/lint-stability-deprecated.rs:407:13
+ --> $DIR/lint-stability-deprecated.rs:410:13
|
LL | foo.trait_deprecated_text();
| ^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'this_crate::Trait::trait_deprecated_text': text
- --> $DIR/lint-stability-deprecated.rs:409:9
+ --> $DIR/lint-stability-deprecated.rs:412:9
|
LL | <Foo>::trait_deprecated_text(&foo);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'this_crate::Trait::trait_deprecated': text
- --> $DIR/lint-stability-deprecated.rs:426:13
+ --> $DIR/lint-stability-deprecated.rs:429:13
|
LL | foo.trait_deprecated();
| ^^^^^^^^^^^^^^^^
warning: use of deprecated item 'this_crate::Trait::trait_deprecated_text': text
- --> $DIR/lint-stability-deprecated.rs:427:13
+ --> $DIR/lint-stability-deprecated.rs:430:13
|
LL | foo.trait_deprecated_text();
| ^^^^^^^^^^^^^^^^^^^^^
+warning: use of deprecated item 'lint_stability::TraitWithAssociatedTypes::TypeDeprecated': text
+ --> $DIR/lint-stability-deprecated.rs:98:48
+ |
+LL | struct S2<T: TraitWithAssociatedTypes>(T::TypeDeprecated);
+ | ^^^^^^^^^^^^^^^^^
+
+warning: use of deprecated item 'lint_stability::TraitWithAssociatedTypes::TypeDeprecated': text
+ --> $DIR/lint-stability-deprecated.rs:103:13
+ |
+LL | TypeDeprecated = u16,
+ | ^^^^^^^^^^^^^^^^^^^^
+
+warning: use of deprecated item 'lint_stability::TraitWithAssociatedTypes::TypeDeprecated': text
+ --> $DIR/lint-stability-deprecated.rs:103:13
+ |
+LL | TypeDeprecated = u16,
+ | ^^^^^^^^^^^^^^^^^^^^
+
|
= note: requested on the command line with `-F private_no_mangle_statics`
+warning: lint `private_no_mangle_fns` has been removed: `no longer a warning, `#[no_mangle]` functions always exported`
+ |
+ = note: requested on the command line with `-F private_no_mangle_fns`
+
+warning: lint `private_no_mangle_statics` has been removed: `no longer a warning, `#[no_mangle]` statics always exported`
+ |
+ = note: requested on the command line with `-F private_no_mangle_statics`
+
+warning: lint `private_no_mangle_fns` has been removed: `no longer a warning, `#[no_mangle]` functions always exported`
+ |
+ = note: requested on the command line with `-F private_no_mangle_fns`
+
+warning: lint `private_no_mangle_statics` has been removed: `no longer a warning, `#[no_mangle]` statics always exported`
+ |
+ = note: requested on the command line with `-F private_no_mangle_statics`
+
+warning: lint `private_no_mangle_fns` has been removed: `no longer a warning, `#[no_mangle]` functions always exported`
+ |
+ = note: requested on the command line with `-F private_no_mangle_fns`
+
+warning: lint `private_no_mangle_statics` has been removed: `no longer a warning, `#[no_mangle]` statics always exported`
+ |
+ = note: requested on the command line with `-F private_no_mangle_statics`
+
error: const items should never be `#[no_mangle]`
--> $DIR/lint-unexported-no-mangle.rs:9:1
|
= help: did you mean: `dead_code`
= note: requested on the command line with `-D dead_cod`
-error: aborting due to 2 previous errors
+error[E0602]: unknown lint: `bogus`
+ |
+ = note: requested on the command line with `-D bogus`
+
+error[E0602]: unknown lint: `dead_cod`
+ |
+ = help: did you mean: `dead_code`
+ = note: requested on the command line with `-D dead_cod`
+
+error[E0602]: unknown lint: `bogus`
+ |
+ = note: requested on the command line with `-D bogus`
+
+error[E0602]: unknown lint: `dead_cod`
+ |
+ = help: did you mean: `dead_code`
+ = note: requested on the command line with `-D dead_cod`
+
+error: aborting due to 6 previous errors
For more information about this error, try `rustc --explain E0602`.
|
LL | Foo => {}
| ^^^ help: to match on the variant, qualify the path: `foo::Foo::Foo`
+ |
+ = note: `#[warn(bindings_with_variant_name)]` on by default
warning: unused variable: `Foo`
--> $DIR/lint-uppercase-variables.rs:22:9
#![forbid(unused, non_snake_case)]
#[allow(unused_variables)] //~ ERROR overruled
+ //~| ERROR overruled
+ //~| ERROR overruled
fn foo() {}
#[allow(unused)] //~ ERROR overruled
+ //~| ERROR overruled
+ //~| ERROR overruled
fn bar() {}
#[allow(nonstandard_style)] //~ ERROR overruled
+ //~| ERROR overruled
+ //~| ERROR overruled
fn main() {
println!("hello forbidden world")
}
| ^^^^^^^^^^^^^^^^ overruled by previous forbid
error[E0453]: allow(unused) overruled by outer forbid(unused)
- --> $DIR/outer-forbid.rs:12:9
+ --> $DIR/outer-forbid.rs:14:9
|
LL | #![forbid(unused, non_snake_case)]
| ------ `forbid` level set here
| ^^^^^^ overruled by previous forbid
error[E0453]: allow(nonstandard_style) overruled by outer forbid(non_snake_case)
- --> $DIR/outer-forbid.rs:15:9
+ --> $DIR/outer-forbid.rs:19:9
|
LL | #![forbid(unused, non_snake_case)]
| -------------- `forbid` level set here
LL | #[allow(nonstandard_style)]
| ^^^^^^^^^^^^^^^^^ overruled by previous forbid
-error: aborting due to 3 previous errors
+error[E0453]: allow(unused_variables) overruled by outer forbid(unused)
+ --> $DIR/outer-forbid.rs:9:9
+ |
+LL | #![forbid(unused, non_snake_case)]
+ | ------ `forbid` level set here
+LL |
+LL | #[allow(unused_variables)]
+ | ^^^^^^^^^^^^^^^^ overruled by previous forbid
+
+error[E0453]: allow(unused) overruled by outer forbid(unused)
+ --> $DIR/outer-forbid.rs:14:9
+ |
+LL | #![forbid(unused, non_snake_case)]
+ | ------ `forbid` level set here
+...
+LL | #[allow(unused)]
+ | ^^^^^^ overruled by previous forbid
+
+error[E0453]: allow(nonstandard_style) overruled by outer forbid(non_snake_case)
+ --> $DIR/outer-forbid.rs:19:9
+ |
+LL | #![forbid(unused, non_snake_case)]
+ | -------------- `forbid` level set here
+...
+LL | #[allow(nonstandard_style)]
+ | ^^^^^^^^^^^^^^^^^ overruled by previous forbid
+
+error[E0453]: allow(unused_variables) overruled by outer forbid(unused)
+ --> $DIR/outer-forbid.rs:9:9
+ |
+LL | #![forbid(unused, non_snake_case)]
+ | ------ `forbid` level set here
+LL |
+LL | #[allow(unused_variables)]
+ | ^^^^^^^^^^^^^^^^ overruled by previous forbid
+
+error[E0453]: allow(unused) overruled by outer forbid(unused)
+ --> $DIR/outer-forbid.rs:14:9
+ |
+LL | #![forbid(unused, non_snake_case)]
+ | ------ `forbid` level set here
+...
+LL | #[allow(unused)]
+ | ^^^^^^ overruled by previous forbid
+
+error[E0453]: allow(nonstandard_style) overruled by outer forbid(non_snake_case)
+ --> $DIR/outer-forbid.rs:19:9
+ |
+LL | #![forbid(unused, non_snake_case)]
+ | -------------- `forbid` level set here
+...
+LL | #[allow(nonstandard_style)]
+ | ^^^^^^^^^^^^^^^^^ overruled by previous forbid
+
+error: aborting due to 9 previous errors
For more information about this error, try `rustc --explain E0453`.
#![warn(absolute_paths_not_starting_with_crate, reason = 0)]
//~^ ERROR malformed lint attribute
+//~| ERROR malformed lint attribute
+//~| ERROR malformed lint attribute
+//~| NOTE reason must be a string literal
+//~| NOTE reason must be a string literal
//~| NOTE reason must be a string literal
#![warn(anonymous_parameters, reason = b"consider these, for we have condemned them")]
//~^ ERROR malformed lint attribute
+//~| ERROR malformed lint attribute
+//~| ERROR malformed lint attribute
+//~| NOTE reason must be a string literal
+//~| NOTE reason must be a string literal
//~| NOTE reason must be a string literal
#![warn(bare_trait_objects, reasons = "leaders to no sure land, guides their bearings lost")]
//~^ ERROR malformed lint attribute
+//~| ERROR malformed lint attribute
+//~| ERROR malformed lint attribute
+//~| ERROR malformed lint attribute
+//~| ERROR malformed lint attribute
+//~| ERROR malformed lint attribute
+//~| NOTE bad attribute argument
+//~| NOTE bad attribute argument
+//~| NOTE bad attribute argument
+//~| NOTE bad attribute argument
+//~| NOTE bad attribute argument
//~| NOTE bad attribute argument
#![warn(box_pointers, blerp = "or in league with robbers have reversed the signposts")]
//~^ ERROR malformed lint attribute
+//~| ERROR malformed lint attribute
+//~| ERROR malformed lint attribute
+//~| ERROR malformed lint attribute
+//~| ERROR malformed lint attribute
+//~| ERROR malformed lint attribute
+//~| NOTE bad attribute argument
+//~| NOTE bad attribute argument
+//~| NOTE bad attribute argument
+//~| NOTE bad attribute argument
+//~| NOTE bad attribute argument
//~| NOTE bad attribute argument
#![warn(elided_lifetimes_in_paths, reason("disrespectful to ancestors", "irresponsible to heirs"))]
//~^ ERROR malformed lint attribute
+//~| ERROR malformed lint attribute
+//~| ERROR malformed lint attribute
+//~| ERROR malformed lint attribute
+//~| ERROR malformed lint attribute
+//~| ERROR malformed lint attribute
+//~| NOTE bad attribute argument
+//~| NOTE bad attribute argument
+//~| NOTE bad attribute argument
+//~| NOTE bad attribute argument
+//~| NOTE bad attribute argument
//~| NOTE bad attribute argument
#![warn(ellipsis_inclusive_range_patterns, reason = "born barren", reason = "a freak growth")]
//~^ ERROR malformed lint attribute
+//~| ERROR malformed lint attribute
+//~| ERROR malformed lint attribute
+//~| NOTE reason in lint attribute must come last
+//~| NOTE reason in lint attribute must come last
//~| NOTE reason in lint attribute must come last
#![warn(keyword_idents, reason = "root in rubble", macro_use_extern_crate)]
//~^ ERROR malformed lint attribute
+//~| ERROR malformed lint attribute
+//~| ERROR malformed lint attribute
+//~| NOTE reason in lint attribute must come last
+//~| NOTE reason in lint attribute must come last
//~| NOTE reason in lint attribute must come last
#![warn(missing_copy_implementations, reason)]
//~^ WARN unknown lint
| ^ reason must be a string literal
error[E0452]: malformed lint attribute input
- --> $DIR/reasons-erroneous.rs:6:40
+ --> $DIR/reasons-erroneous.rs:10:40
|
LL | #![warn(anonymous_parameters, reason = b"consider these, for we have condemned them")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ reason must be a string literal
error[E0452]: malformed lint attribute input
- --> $DIR/reasons-erroneous.rs:9:29
+ --> $DIR/reasons-erroneous.rs:17:29
|
LL | #![warn(bare_trait_objects, reasons = "leaders to no sure land, guides their bearings lost")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ bad attribute argument
error[E0452]: malformed lint attribute input
- --> $DIR/reasons-erroneous.rs:12:23
+ --> $DIR/reasons-erroneous.rs:17:29
+ |
+LL | #![warn(bare_trait_objects, reasons = "leaders to no sure land, guides their bearings lost")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ bad attribute argument
+
+error[E0452]: malformed lint attribute input
+ --> $DIR/reasons-erroneous.rs:30:23
|
LL | #![warn(box_pointers, blerp = "or in league with robbers have reversed the signposts")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ bad attribute argument
error[E0452]: malformed lint attribute input
- --> $DIR/reasons-erroneous.rs:15:36
+ --> $DIR/reasons-erroneous.rs:30:23
+ |
+LL | #![warn(box_pointers, blerp = "or in league with robbers have reversed the signposts")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ bad attribute argument
+
+error[E0452]: malformed lint attribute input
+ --> $DIR/reasons-erroneous.rs:43:36
+ |
+LL | #![warn(elided_lifetimes_in_paths, reason("disrespectful to ancestors", "irresponsible to heirs"))]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ bad attribute argument
+
+error[E0452]: malformed lint attribute input
+ --> $DIR/reasons-erroneous.rs:43:36
|
LL | #![warn(elided_lifetimes_in_paths, reason("disrespectful to ancestors", "irresponsible to heirs"))]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ bad attribute argument
error[E0452]: malformed lint attribute input
- --> $DIR/reasons-erroneous.rs:18:44
+ --> $DIR/reasons-erroneous.rs:56:44
|
LL | #![warn(ellipsis_inclusive_range_patterns, reason = "born barren", reason = "a freak growth")]
| ^^^^^^^^^^^^^^^^^^^^^^ reason in lint attribute must come last
error[E0452]: malformed lint attribute input
- --> $DIR/reasons-erroneous.rs:21:25
+ --> $DIR/reasons-erroneous.rs:63:25
|
LL | #![warn(keyword_idents, reason = "root in rubble", macro_use_extern_crate)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^ reason in lint attribute must come last
warning: unknown lint: `reason`
- --> $DIR/reasons-erroneous.rs:24:39
+ --> $DIR/reasons-erroneous.rs:70:39
|
LL | #![warn(missing_copy_implementations, reason)]
| ^^^^^^
|
= note: `#[warn(unknown_lints)]` on by default
-error: aborting due to 7 previous errors
+error[E0452]: malformed lint attribute input
+ --> $DIR/reasons-erroneous.rs:3:58
+ |
+LL | #![warn(absolute_paths_not_starting_with_crate, reason = 0)]
+ | ^ reason must be a string literal
+
+error[E0452]: malformed lint attribute input
+ --> $DIR/reasons-erroneous.rs:10:40
+ |
+LL | #![warn(anonymous_parameters, reason = b"consider these, for we have condemned them")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ reason must be a string literal
+
+error[E0452]: malformed lint attribute input
+ --> $DIR/reasons-erroneous.rs:17:29
+ |
+LL | #![warn(bare_trait_objects, reasons = "leaders to no sure land, guides their bearings lost")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ bad attribute argument
+
+error[E0452]: malformed lint attribute input
+ --> $DIR/reasons-erroneous.rs:17:29
+ |
+LL | #![warn(bare_trait_objects, reasons = "leaders to no sure land, guides their bearings lost")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ bad attribute argument
+
+error[E0452]: malformed lint attribute input
+ --> $DIR/reasons-erroneous.rs:30:23
+ |
+LL | #![warn(box_pointers, blerp = "or in league with robbers have reversed the signposts")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ bad attribute argument
+
+error[E0452]: malformed lint attribute input
+ --> $DIR/reasons-erroneous.rs:30:23
+ |
+LL | #![warn(box_pointers, blerp = "or in league with robbers have reversed the signposts")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ bad attribute argument
+
+error[E0452]: malformed lint attribute input
+ --> $DIR/reasons-erroneous.rs:43:36
+ |
+LL | #![warn(elided_lifetimes_in_paths, reason("disrespectful to ancestors", "irresponsible to heirs"))]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ bad attribute argument
+
+error[E0452]: malformed lint attribute input
+ --> $DIR/reasons-erroneous.rs:43:36
+ |
+LL | #![warn(elided_lifetimes_in_paths, reason("disrespectful to ancestors", "irresponsible to heirs"))]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ bad attribute argument
+
+error[E0452]: malformed lint attribute input
+ --> $DIR/reasons-erroneous.rs:56:44
+ |
+LL | #![warn(ellipsis_inclusive_range_patterns, reason = "born barren", reason = "a freak growth")]
+ | ^^^^^^^^^^^^^^^^^^^^^^ reason in lint attribute must come last
+
+error[E0452]: malformed lint attribute input
+ --> $DIR/reasons-erroneous.rs:63:25
+ |
+LL | #![warn(keyword_idents, reason = "root in rubble", macro_use_extern_crate)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ reason in lint attribute must come last
+
+error[E0452]: malformed lint attribute input
+ --> $DIR/reasons-erroneous.rs:3:58
+ |
+LL | #![warn(absolute_paths_not_starting_with_crate, reason = 0)]
+ | ^ reason must be a string literal
+
+error[E0452]: malformed lint attribute input
+ --> $DIR/reasons-erroneous.rs:10:40
+ |
+LL | #![warn(anonymous_parameters, reason = b"consider these, for we have condemned them")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ reason must be a string literal
+
+error[E0452]: malformed lint attribute input
+ --> $DIR/reasons-erroneous.rs:17:29
+ |
+LL | #![warn(bare_trait_objects, reasons = "leaders to no sure land, guides their bearings lost")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ bad attribute argument
+
+error[E0452]: malformed lint attribute input
+ --> $DIR/reasons-erroneous.rs:17:29
+ |
+LL | #![warn(bare_trait_objects, reasons = "leaders to no sure land, guides their bearings lost")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ bad attribute argument
+
+error[E0452]: malformed lint attribute input
+ --> $DIR/reasons-erroneous.rs:30:23
+ |
+LL | #![warn(box_pointers, blerp = "or in league with robbers have reversed the signposts")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ bad attribute argument
+
+error[E0452]: malformed lint attribute input
+ --> $DIR/reasons-erroneous.rs:30:23
+ |
+LL | #![warn(box_pointers, blerp = "or in league with robbers have reversed the signposts")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ bad attribute argument
+
+error[E0452]: malformed lint attribute input
+ --> $DIR/reasons-erroneous.rs:43:36
+ |
+LL | #![warn(elided_lifetimes_in_paths, reason("disrespectful to ancestors", "irresponsible to heirs"))]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ bad attribute argument
+
+error[E0452]: malformed lint attribute input
+ --> $DIR/reasons-erroneous.rs:43:36
+ |
+LL | #![warn(elided_lifetimes_in_paths, reason("disrespectful to ancestors", "irresponsible to heirs"))]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ bad attribute argument
+
+error[E0452]: malformed lint attribute input
+ --> $DIR/reasons-erroneous.rs:56:44
+ |
+LL | #![warn(ellipsis_inclusive_range_patterns, reason = "born barren", reason = "a freak growth")]
+ | ^^^^^^^^^^^^^^^^^^^^^^ reason in lint attribute must come last
+
+error[E0452]: malformed lint attribute input
+ --> $DIR/reasons-erroneous.rs:63:25
+ |
+LL | #![warn(keyword_idents, reason = "root in rubble", macro_use_extern_crate)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ reason in lint attribute must come last
+
+error: aborting due to 30 previous errors
For more information about this error, try `rustc --explain E0452`.
#![forbid(
unsafe_code,
//~^ NOTE `forbid` level set here
+ //~| NOTE `forbid` level set here
+ //~| NOTE `forbid` level set here
reason = "our errors & omissions insurance policy doesn't cover unsafe Rust"
)]
#[allow(unsafe_code)]
//~^ ERROR allow(unsafe_code) overruled by outer forbid(unsafe_code)
+ //~| ERROR allow(unsafe_code) overruled by outer forbid(unsafe_code)
+ //~| ERROR allow(unsafe_code) overruled by outer forbid(unsafe_code)
//~| NOTE overruled by previous forbid
+ //~| NOTE overruled by previous forbid
+ //~| NOTE overruled by previous forbid
+ //~| NOTE our errors & omissions insurance policy doesn't cover unsafe Rust
+ //~| NOTE our errors & omissions insurance policy doesn't cover unsafe Rust
//~| NOTE our errors & omissions insurance policy doesn't cover unsafe Rust
unsafe {
*a_billion_dollar_mistake
error[E0453]: allow(unsafe_code) overruled by outer forbid(unsafe_code)
- --> $DIR/reasons-forbidden.rs:14:13
+ --> $DIR/reasons-forbidden.rs:16:13
|
LL | unsafe_code,
| ----------- `forbid` level set here
|
= note: our errors & omissions insurance policy doesn't cover unsafe Rust
-error: aborting due to previous error
+error[E0453]: allow(unsafe_code) overruled by outer forbid(unsafe_code)
+ --> $DIR/reasons-forbidden.rs:16:13
+ |
+LL | unsafe_code,
+ | ----------- `forbid` level set here
+...
+LL | #[allow(unsafe_code)]
+ | ^^^^^^^^^^^ overruled by previous forbid
+ |
+ = note: our errors & omissions insurance policy doesn't cover unsafe Rust
+
+error[E0453]: allow(unsafe_code) overruled by outer forbid(unsafe_code)
+ --> $DIR/reasons-forbidden.rs:16:13
+ |
+LL | unsafe_code,
+ | ----------- `forbid` level set here
+...
+LL | #[allow(unsafe_code)]
+ | ^^^^^^^^^^^ overruled by previous forbid
+ |
+ = note: our errors & omissions insurance policy doesn't cover unsafe Rust
+
+error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0453`.
#[derive(
core::RustcDecodable, //~ ERROR could not find `RustcDecodable` in `core`
+ //~| ERROR could not find `RustcDecodable` in `core`
core::RustcDecodable, //~ ERROR could not find `RustcDecodable` in `core`
+ //~| ERROR could not find `RustcDecodable` in `core`
)]
#[core::bench] //~ ERROR could not find `bench` in `core`
#[core::global_allocator] //~ ERROR could not find `global_allocator` in `core`
#[derive(
std::RustcDecodable, //~ ERROR could not find `RustcDecodable` in `std`
+ //~| ERROR could not find `RustcDecodable` in `std`
std::RustcDecodable, //~ ERROR could not find `RustcDecodable` in `std`
+ //~| ERROR could not find `RustcDecodable` in `std`
)]
#[std::bench] //~ ERROR could not find `bench` in `std`
#[std::global_allocator] //~ ERROR could not find `global_allocator` in `std`
error[E0433]: failed to resolve: could not find `bench` in `core`
- --> $DIR/builtin-std-paths-fail.rs:5:9
+ --> $DIR/builtin-std-paths-fail.rs:7:9
|
LL | #[core::bench]
| ^^^^^ could not find `bench` in `core`
error[E0433]: failed to resolve: could not find `global_allocator` in `core`
- --> $DIR/builtin-std-paths-fail.rs:6:9
+ --> $DIR/builtin-std-paths-fail.rs:8:9
|
LL | #[core::global_allocator]
| ^^^^^^^^^^^^^^^^ could not find `global_allocator` in `core`
error[E0433]: failed to resolve: could not find `test_case` in `core`
- --> $DIR/builtin-std-paths-fail.rs:7:9
+ --> $DIR/builtin-std-paths-fail.rs:9:9
|
LL | #[core::test_case]
| ^^^^^^^^^ could not find `test_case` in `core`
error[E0433]: failed to resolve: could not find `test` in `core`
- --> $DIR/builtin-std-paths-fail.rs:8:9
+ --> $DIR/builtin-std-paths-fail.rs:10:9
|
LL | #[core::test]
| ^^^^ could not find `test` in `core`
| ^^^^^^^^^^^^^^ could not find `RustcDecodable` in `core`
error[E0433]: failed to resolve: could not find `RustcDecodable` in `core`
- --> $DIR/builtin-std-paths-fail.rs:3:11
+ --> $DIR/builtin-std-paths-fail.rs:4:11
+ |
+LL | core::RustcDecodable,
+ | ^^^^^^^^^^^^^^ could not find `RustcDecodable` in `core`
+
+error[E0433]: failed to resolve: could not find `RustcDecodable` in `core`
+ --> $DIR/builtin-std-paths-fail.rs:4:11
+ |
+LL | core::RustcDecodable,
+ | ^^^^^^^^^^^^^^ could not find `RustcDecodable` in `core`
+
+error[E0433]: failed to resolve: could not find `RustcDecodable` in `core`
+ --> $DIR/builtin-std-paths-fail.rs:2:11
|
LL | core::RustcDecodable,
| ^^^^^^^^^^^^^^ could not find `RustcDecodable` in `core`
error[E0433]: failed to resolve: could not find `bench` in `std`
- --> $DIR/builtin-std-paths-fail.rs:15:8
+ --> $DIR/builtin-std-paths-fail.rs:19:8
|
LL | #[std::bench]
| ^^^^^ could not find `bench` in `std`
error[E0433]: failed to resolve: could not find `global_allocator` in `std`
- --> $DIR/builtin-std-paths-fail.rs:16:8
+ --> $DIR/builtin-std-paths-fail.rs:20:8
|
LL | #[std::global_allocator]
| ^^^^^^^^^^^^^^^^ could not find `global_allocator` in `std`
error[E0433]: failed to resolve: could not find `test_case` in `std`
- --> $DIR/builtin-std-paths-fail.rs:17:8
+ --> $DIR/builtin-std-paths-fail.rs:21:8
|
LL | #[std::test_case]
| ^^^^^^^^^ could not find `test_case` in `std`
error[E0433]: failed to resolve: could not find `test` in `std`
- --> $DIR/builtin-std-paths-fail.rs:18:8
+ --> $DIR/builtin-std-paths-fail.rs:22:8
|
LL | #[std::test]
| ^^^^ could not find `test` in `std`
error[E0433]: failed to resolve: could not find `RustcDecodable` in `std`
- --> $DIR/builtin-std-paths-fail.rs:12:10
+ --> $DIR/builtin-std-paths-fail.rs:14:10
+ |
+LL | std::RustcDecodable,
+ | ^^^^^^^^^^^^^^ could not find `RustcDecodable` in `std`
+
+error[E0433]: failed to resolve: could not find `RustcDecodable` in `std`
+ --> $DIR/builtin-std-paths-fail.rs:16:10
+ |
+LL | std::RustcDecodable,
+ | ^^^^^^^^^^^^^^ could not find `RustcDecodable` in `std`
+
+error[E0433]: failed to resolve: could not find `RustcDecodable` in `std`
+ --> $DIR/builtin-std-paths-fail.rs:16:10
|
LL | std::RustcDecodable,
| ^^^^^^^^^^^^^^ could not find `RustcDecodable` in `std`
error[E0433]: failed to resolve: could not find `RustcDecodable` in `std`
- --> $DIR/builtin-std-paths-fail.rs:13:10
+ --> $DIR/builtin-std-paths-fail.rs:14:10
|
LL | std::RustcDecodable,
| ^^^^^^^^^^^^^^ could not find `RustcDecodable` in `std`
-error: aborting due to 12 previous errors
+error: aborting due to 16 previous errors
For more information about this error, try `rustc --explain E0433`.
--- /dev/null
+// check-pass
+
+macro_rules! foo {
+ ($doc: expr) => {
+ fn f() {
+ #[doc = $doc]
+ ()
+ }
+ };
+}
+
+foo!("doc");
+
+fn main() {}
+// FIXME: missing sysroot spans (#53081)
+// ignore-i586-unknown-linux-gnu
+// ignore-i586-unknown-linux-musl
+// ignore-i686-unknown-linux-musl
fn main() {
printlx!("oh noes!"); //~ ERROR cannot find
}
error: cannot find macro `printlx` in this scope
- --> $DIR/macro-name-typo.rs:2:5
+ --> $DIR/macro-name-typo.rs:6:5
|
LL | printlx!("oh noes!");
| ^^^^^^^ help: a macro with a similar name exists: `println`
+ |
+ ::: $SRC_DIR/libstd/macros.rs:LL:COL
+ |
+LL | macro_rules! println {
+ | -------------------- similarly named macro `println` defined here
error: aborting due to previous error
+// FIXME: missing sysroot spans (#53081)
+// ignore-i586-unknown-linux-gnu
+// ignore-i586-unknown-linux-musl
+// ignore-i686-unknown-linux-musl
fn main() {
inline!(); //~ ERROR cannot find macro `inline` in this scope
}
error: cannot find macro `inline` in this scope
- --> $DIR/macro-path-prelude-fail-3.rs:2:5
+ --> $DIR/macro-path-prelude-fail-3.rs:6:5
|
LL | inline!();
| ^^^^^^ help: a macro with a similar name exists: `line`
+ |
+ ::: $SRC_DIR/libcore/macros/mod.rs:LL:COL
+ |
+LL | macro_rules! line {
+ | ----------------- similarly named macro `line` defined here
error: aborting due to previous error
|
LL | macro_two!();
| ^^^^^^^^^ help: a macro with a similar name exists: `macro_one`
+ |
+ ::: $DIR/auxiliary/two_macros.rs:2:1
+ |
+LL | macro_rules! macro_one { () => ("one") }
+ | ---------------------- similarly named macro `macro_one` defined here
error: aborting due to previous error
#[derive(::Absolute)] //~ ERROR failed to resolve
+ //~| ERROR failed to resolve
struct S;
fn main() {}
LL | #[derive(::Absolute)]
| ^^^^^^^^ maybe a missing crate `Absolute`?
-error: aborting due to previous error
+error[E0433]: failed to resolve: maybe a missing crate `Absolute`?
+ --> $DIR/meta-item-absolute-path.rs:1:12
+ |
+LL | #[derive(::Absolute)]
+ | ^^^^^^^^ maybe a missing crate `Absolute`?
+
+error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0433`.
_ => { }
};
//~^^^ ERROR lower range bound must be less than or equal to upper
+ //~| ERROR lower range bound must be less than or equal to upper
match 5 {
0 .. 0 => { }
_ => { }
};
//~^^^ ERROR lower range bound must be less than upper
+ //~| ERROR lower range bound must be less than upper
match 5u64 {
0xFFFF_FFFF_FFFF_FFFF ..= 1 => { }
_ => { }
};
//~^^^ ERROR lower range bound must be less than or equal to upper
+ //~| ERROR lower range bound must be less than or equal to upper
}
| ^ lower bound larger than upper bound
error[E0579]: lower range bound must be less than upper
- --> $DIR/match-range-fail-2.rs:11:9
+ --> $DIR/match-range-fail-2.rs:12:9
|
LL | 0 .. 0 => { }
| ^
error[E0030]: lower range bound must be less than or equal to upper
- --> $DIR/match-range-fail-2.rs:17:9
+ --> $DIR/match-range-fail-2.rs:19:9
|
LL | 0xFFFF_FFFF_FFFF_FFFF ..= 1 => { }
| ^^^^^^^^^^^^^^^^^^^^^ lower bound larger than upper bound
-error: aborting due to 3 previous errors
+error[E0030]: lower range bound must be less than or equal to upper
+ --> $DIR/match-range-fail-2.rs:5:9
+ |
+LL | 6 ..= 1 => { }
+ | ^ lower bound larger than upper bound
+
+error[E0579]: lower range bound must be less than upper
+ --> $DIR/match-range-fail-2.rs:12:9
+ |
+LL | 0 .. 0 => { }
+ | ^
+
+error[E0030]: lower range bound must be less than or equal to upper
+ --> $DIR/match-range-fail-2.rs:19:9
+ |
+LL | 0xFFFF_FFFF_FFFF_FFFF ..= 1 => { }
+ | ^^^^^^^^^^^^^^^^^^^^^ lower bound larger than upper bound
+
+error: aborting due to 6 previous errors
Some errors have detailed explanations: E0030, E0579.
For more information about an error, try `rustc --explain E0030`.
let y = Foo;
y.zero()
- .take() //~ ERROR no method named `take` found for type `Foo` in the current scope
+ .take() //~ ERROR no method named `take` found
.one(0);
}
LL | .two(0);
| ^^^ expected 2 parameters
-error[E0599]: no method named `take` found for type `Foo` in the current scope
+error[E0599]: no method named `take` found for struct `Foo` in the current scope
--> $DIR/method-call-err-msg.rs:18:7
|
LL | pub struct Foo;
--- /dev/null
+struct Bug {
+ A: [(); { *"" }.len()],
+ //~^ ERROR: cannot move a value of type str
+ //~| ERROR: cannot move out of a shared reference
+}
+
+fn main() {}
--- /dev/null
+error[E0161]: cannot move a value of type str: the size of str cannot be statically determined
+ --> $DIR/issue-67947.rs:2:13
+ |
+LL | A: [(); { *"" }.len()],
+ | ^^^^^^^
+
+error[E0507]: cannot move out of a shared reference
+ --> $DIR/issue-67947.rs:2:15
+ |
+LL | A: [(); { *"" }.len()],
+ | ^^^ move occurs because value has type `str`, which does not implement the `Copy` trait
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0161, E0507.
+For more information about an error, try `rustc --explain E0161`.
-error[E0599]: no method named `count` found for type `std::iter::Filter<std::iter::Fuse<std::iter::Once<&str>>, [closure@$DIR/issue-36053-2.rs:7:39: 7:53]>` in the current scope
+error[E0599]: no method named `count` found for struct `std::iter::Filter<std::iter::Fuse<std::iter::Once<&str>>, [closure@$DIR/issue-36053-2.rs:7:39: 7:53]>` in the current scope
--> $DIR/issue-36053-2.rs:7:55
|
LL | once::<&str>("str").fuse().filter(|a: &str| true).count();
fn main() {
let a: Result<(), Foo> = Ok(());
a.unwrap();
- //~^ ERROR no method named `unwrap` found for type `std::result::Result<(), Foo>`
+ //~^ ERROR no method named `unwrap` found
}
-error[E0599]: no method named `unwrap` found for type `std::result::Result<(), Foo>` in the current scope
+error[E0599]: no method named `unwrap` found for enum `std::result::Result<(), Foo>` in the current scope
--> $DIR/method-help-unsatisfied-bound.rs:5:7
|
LL | a.unwrap();
|
LL | check(xm1::S);
| ^^^^^^
+ |
+ ::: $DIR/auxiliary/namespace-mix.rs:3:5
+ |
+LL | pub struct TS();
+ | ---------------- similarly named tuple struct `TS` defined here
|
= note: can't use a type alias as a constructor
help: a tuple struct with a similar name exists
|
LL | check(xm7::V);
| ^^^^^^ did you mean `xm7::V { /* fields */ }`?
+ |
+ ::: $DIR/auxiliary/namespace-mix.rs:7:9
+ |
+LL | TV(),
+ | ---- similarly named tuple variant `TV` defined here
|
help: a tuple variant with a similar name exists
|
}
fn main() {
["hi"].bind(|x| [x] );
- //~^ ERROR no method named `bind` found for type `[&str; 1]` in the current scope
+ //~^ ERROR no method named `bind` found
}
|
= help: the trait `std::ops::Add<std::vec::Vec<B>>` is not implemented for `()`
-error[E0599]: no method named `bind` found for type `[&str; 1]` in the current scope
+error[E0599]: no method named `bind` found for array `[&str; 1]` in the current scope
--> $DIR/issue-2149.rs:13:12
|
LL | ["hi"].bind(|x| [x] );
error[E0277]: `std::rc::Rc<()>` cannot be sent between threads safely
--> $DIR/no-send-res-ports.rs:29:5
|
-LL | thread::spawn(move|| {
- | ^^^^^^^^^^^^^ `std::rc::Rc<()>` cannot be sent between threads safely
+LL | thread::spawn(move|| {
+ | _____^^^^^^^^^^^^^_-
+ | | |
+ | | `std::rc::Rc<()>` cannot be sent between threads safely
+LL | |
+LL | | let y = x;
+LL | | println!("{:?}", y);
+LL | | });
+ | |_____- within this `[closure@$DIR/no-send-res-ports.rs:29:19: 33:6 x:main::Foo]`
|
::: $SRC_DIR/libstd/thread/mod.rs:LL:COL
|
-LL | F: Send + 'static,
- | ---- required by this bound in `std::thread::spawn`
+LL | F: Send + 'static,
+ | ---- required by this bound in `std::thread::spawn`
|
= help: within `[closure@$DIR/no-send-res-ports.rs:29:19: 33:6 x:main::Foo]`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<()>`
= note: required because it appears within the type `Port<()>`
-error[E0599]: no method named `clone` found for type `libc::c_void` in the current scope
+error[E0599]: no method named `clone` found for enum `libc::c_void` in the current scope
--> $DIR/non-copyable-void.rs:11:23
|
LL | let _z = (*y).clone();
--- /dev/null
+// Issue #66530: We would ICE if someone compiled with `-o /dev/null`,
+// because we would try to generate auxiliary files in `/dev/` (which
+// at least the OS X file system rejects).
+//
+// An attempt to `-o` into a directory we cannot write into should indeed
+// be an error; but not an ICE.
+
+// compile-flags: -o /dev/null
+
+// The error-pattern check occurs *before* normalization, and the error patterns
+// are wildly different between build environments. So this is a cop-out (and we
+// rely on the checking of the normalized stderr output as our actual
+// "verification" of the diagnostic).
+
+// error-pattern: error
+
+// On Mac OS X, we get an error like the below
+// normalize-stderr-test "failed to write bytecode to /dev/null.non_ice_error_on_worker_io_fail.*" -> "io error modifying /dev/"
+
+// On Linux, we get an error like the below
+// normalize-stderr-test "couldn't create a temp dir.*" -> "io error modifying /dev/"
+
+// ignore-tidy-linelength
+// ignore-windows - this is a unix-specific test
+// ignore-emscripten - the file-system issues do not replicate here
+// ignore-wasm - the file-system issues do not replicate here
+// ignore-arm - the file-system issues do not replicate here, at least on armhf-gnu
+
+#![crate_type="lib"]
+
+#![cfg_attr(not(feature = "std"), no_std)]
+pub mod task {
+ pub mod __internal {
+ use crate::task::Waker;
+ }
+ pub use core::task::Waker;
+}
--- /dev/null
+warning: ignoring --out-dir flag due to -o flag
+
+error: io error modifying /dev/
+
+error: aborting due to previous error
+
-error[E0599]: no method named `clone` found for type `Foo` in the current scope
+error[E0599]: no method named `clone` found for struct `Foo` in the current scope
--> $DIR/noncopyable-class.rs:34:16
|
LL | struct Foo {
error[E0277]: the trait bound `S: std::clone::Clone` is not satisfied in `[closure@$DIR/not-clone-closure.rs:7:17: 9:6 a:S]`
--> $DIR/not-clone-closure.rs:11:23
|
-LL | let hello = hello.clone();
- | ^^^^^ within `[closure@$DIR/not-clone-closure.rs:7:17: 9:6 a:S]`, the trait `std::clone::Clone` is not implemented for `S`
+LL | let hello = move || {
+ | _________________-
+LL | | println!("Hello {}", a.0);
+LL | | };
+ | |_____- within this `[closure@$DIR/not-clone-closure.rs:7:17: 9:6 a:S]`
+LL |
+LL | let hello = hello.clone();
+ | ^^^^^ within `[closure@$DIR/not-clone-closure.rs:7:17: 9:6 a:S]`, the trait `std::clone::Clone` is not implemented for `S`
|
= note: required because it appears within the type `[closure@$DIR/not-clone-closure.rs:7:17: 9:6 a:S]`
-error[E0599]: no method named `owned` found for type `&dyn Foo` in the current scope
+error[E0599]: no method named `owned` found for reference `&dyn Foo` in the current scope
--> $DIR/object-pointer-types.rs:11:7
|
LL | x.owned();
= note: the following trait defines an item `owned`, perhaps you need to implement it:
candidate #1: `Foo`
-error[E0599]: no method named `owned` found for type `&mut dyn Foo` in the current scope
+error[E0599]: no method named `owned` found for mutable reference `&mut dyn Foo` in the current scope
--> $DIR/object-pointer-types.rs:17:7
|
LL | x.owned();
= note: the following trait defines an item `owned`, perhaps you need to implement it:
candidate #1: `Foo`
-error[E0599]: no method named `managed` found for type `std::boxed::Box<(dyn Foo + 'static)>` in the current scope
+error[E0599]: no method named `managed` found for struct `std::boxed::Box<(dyn Foo + 'static)>` in the current scope
--> $DIR/object-pointer-types.rs:23:7
|
LL | x.managed();
-// compile-flags: -Z continue-parse-after-error
-
// Test that the parser is error correcting missing idents. Despite a parsing
// error (or two), we still run type checking (and don't get extra errors there).
error: unexpected token: `;`
- --> $DIR/parse-error-correct.rs:8:15
+ --> $DIR/parse-error-correct.rs:6:15
|
LL | let x = y.;
| ^
error: unexpected token: `(`
- --> $DIR/parse-error-correct.rs:9:15
+ --> $DIR/parse-error-correct.rs:7:15
|
LL | let x = y.();
| ^
error[E0618]: expected function, found `{integer}`
- --> $DIR/parse-error-correct.rs:9:13
+ --> $DIR/parse-error-correct.rs:7:13
|
LL | let y = 42;
| - `{integer}` defined here
| call expression requires function
error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields
- --> $DIR/parse-error-correct.rs:11:15
+ --> $DIR/parse-error-correct.rs:9:15
|
LL | let x = y.foo;
| ^^^
-// compile-flags: -Z continue-parse-after-error
-
// Test that we can recover from missing braces in the parser.
trait Foo {
error: this file contains an unclosed delimiter
- --> $DIR/parser-recovery-1.rs:15:54
+ --> $DIR/parser-recovery-1.rs:13:54
|
LL | trait Foo {
| - unclosed delimiter
| ^
error: unexpected token: `;`
- --> $DIR/parser-recovery-1.rs:12:15
+ --> $DIR/parser-recovery-1.rs:10:15
|
LL | let x = y.;
| ^
error[E0425]: cannot find function `foo` in this scope
- --> $DIR/parser-recovery-1.rs:7:17
+ --> $DIR/parser-recovery-1.rs:5:17
|
LL | let x = foo();
| ^^^ not found in this scope
error[E0425]: cannot find value `y` in this scope
- --> $DIR/parser-recovery-1.rs:12:13
+ --> $DIR/parser-recovery-1.rs:10:13
|
LL | let x = y.;
| ^ not found in this scope
-// compile-flags: -Z continue-parse-after-error
-
// Test that we can recover from mismatched braces in the parser.
trait Foo {
error: unexpected token: `;`
- --> $DIR/parser-recovery-2.rs:12:15
+ --> $DIR/parser-recovery-2.rs:10:15
|
LL | let x = y.;
| ^
error: mismatched closing delimiter: `)`
- --> $DIR/parser-recovery-2.rs:8:5
+ --> $DIR/parser-recovery-2.rs:6:5
|
LL | fn bar() {
| - unclosed delimiter
| ^ mismatched closing delimiter
error[E0425]: cannot find function `foo` in this scope
- --> $DIR/parser-recovery-2.rs:7:17
+ --> $DIR/parser-recovery-2.rs:5:17
|
LL | let x = foo();
| ^^^ not found in this scope
error[E0425]: cannot find value `y` in this scope
- --> $DIR/parser-recovery-2.rs:12:13
+ --> $DIR/parser-recovery-2.rs:10:13
|
LL | let x = y.;
| ^ not found in this scope
-// compile-flags: -Z continue-parse-after-error
-
fn main() {
let x = "\x80"; //~ ERROR may only be used
let y = "\xff"; //~ ERROR may only be used
error: this form of character escape may only be used with characters in the range [\x00-\x7f]
- --> $DIR/ascii-only-character-escape.rs:4:14
+ --> $DIR/ascii-only-character-escape.rs:2:14
|
LL | let x = "\x80";
| ^^^^
error: this form of character escape may only be used with characters in the range [\x00-\x7f]
- --> $DIR/ascii-only-character-escape.rs:5:14
+ --> $DIR/ascii-only-character-escape.rs:3:14
|
LL | let y = "\xff";
| ^^^^
error: this form of character escape may only be used with characters in the range [\x00-\x7f]
- --> $DIR/ascii-only-character-escape.rs:6:14
+ --> $DIR/ascii-only-character-escape.rs:4:14
|
LL | let z = "\xe2";
| ^^^^
+#![feature(half_open_range_patterns)]
+
fn main() {}
#[cfg(FALSE)] fn e() { let _ = box #![attr] 0; }
// note: requires parens in patterns to allow disambiguation
#[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] 10 => () } }
-//~^ ERROR `X..=` range patterns are not supported
+//~^ ERROR inclusive range with no end
//~| ERROR expected one of `=>`, `if`, or `|`, found `#`
#[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] -10 => () } }
-//~^ ERROR `X..=` range patterns are not supported
+//~^ ERROR inclusive range with no end
//~| ERROR expected one of `=>`, `if`, or `|`, found `#`
#[cfg(FALSE)] fn e() { match 0 { 0..=-#[attr] 10 => () } }
//~^ ERROR unexpected token: `#`
#[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] FOO => () } }
-//~^ ERROR `X..=` range patterns are not supported
+//~^ ERROR inclusive range with no end
//~| ERROR expected one of `=>`, `if`, or `|`, found `#`
#[cfg(FALSE)] fn e() { let _ = x.#![attr]foo(); }
error: an inner attribute is not permitted in this context
- --> $DIR/attr-stmt-expr-attr-bad.rs:3:36
+ --> $DIR/attr-stmt-expr-attr-bad.rs:5:36
|
LL | #[cfg(FALSE)] fn e() { let _ = box #![attr] 0; }
| ^^^^^^^^
= note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
error: expected expression, found `]`
- --> $DIR/attr-stmt-expr-attr-bad.rs:5:40
+ --> $DIR/attr-stmt-expr-attr-bad.rs:7:40
|
LL | #[cfg(FALSE)] fn e() { let _ = [#[attr]]; }
| ^ expected expression
error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `#`
- --> $DIR/attr-stmt-expr-attr-bad.rs:7:35
+ --> $DIR/attr-stmt-expr-attr-bad.rs:9:35
|
LL | #[cfg(FALSE)] fn e() { let _ = foo#[attr](); }
| ^ expected one of 7 possible tokens
error: an inner attribute is not permitted in this context
- --> $DIR/attr-stmt-expr-attr-bad.rs:9:36
+ --> $DIR/attr-stmt-expr-attr-bad.rs:11:36
|
LL | #[cfg(FALSE)] fn e() { let _ = foo(#![attr]); }
| ^^^^^^^^
= note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
error: expected expression, found `)`
- --> $DIR/attr-stmt-expr-attr-bad.rs:9:44
+ --> $DIR/attr-stmt-expr-attr-bad.rs:11:44
|
LL | #[cfg(FALSE)] fn e() { let _ = foo(#![attr]); }
| ^ expected expression
error: an inner attribute is not permitted in this context
- --> $DIR/attr-stmt-expr-attr-bad.rs:12:38
+ --> $DIR/attr-stmt-expr-attr-bad.rs:14:38
|
LL | #[cfg(FALSE)] fn e() { let _ = x.foo(#![attr]); }
| ^^^^^^^^
= note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
error: expected expression, found `)`
- --> $DIR/attr-stmt-expr-attr-bad.rs:12:46
+ --> $DIR/attr-stmt-expr-attr-bad.rs:14:46
|
LL | #[cfg(FALSE)] fn e() { let _ = x.foo(#![attr]); }
| ^ expected expression
error: an inner attribute is not permitted in this context
- --> $DIR/attr-stmt-expr-attr-bad.rs:15:36
+ --> $DIR/attr-stmt-expr-attr-bad.rs:17:36
|
LL | #[cfg(FALSE)] fn e() { let _ = 0 + #![attr] 0; }
| ^^^^^^^^
= note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
error: an inner attribute is not permitted in this context
- --> $DIR/attr-stmt-expr-attr-bad.rs:17:33
+ --> $DIR/attr-stmt-expr-attr-bad.rs:19:33
|
LL | #[cfg(FALSE)] fn e() { let _ = !#![attr] 0; }
| ^^^^^^^^
= note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
error: an inner attribute is not permitted in this context
- --> $DIR/attr-stmt-expr-attr-bad.rs:19:33
+ --> $DIR/attr-stmt-expr-attr-bad.rs:21:33
|
LL | #[cfg(FALSE)] fn e() { let _ = -#![attr] 0; }
| ^^^^^^^^
= note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `#`
- --> $DIR/attr-stmt-expr-attr-bad.rs:21:34
+ --> $DIR/attr-stmt-expr-attr-bad.rs:23:34
|
LL | #[cfg(FALSE)] fn e() { let _ = x #![attr] as Y; }
| ^ expected one of 7 possible tokens
error: an inner attribute is not permitted in this context
- --> $DIR/attr-stmt-expr-attr-bad.rs:23:35
+ --> $DIR/attr-stmt-expr-attr-bad.rs:25:35
|
LL | #[cfg(FALSE)] fn e() { let _ = || #![attr] foo; }
| ^^^^^^^^
= note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
error: an inner attribute is not permitted in this context
- --> $DIR/attr-stmt-expr-attr-bad.rs:25:40
+ --> $DIR/attr-stmt-expr-attr-bad.rs:27:40
|
LL | #[cfg(FALSE)] fn e() { let _ = move || #![attr] foo; }
| ^^^^^^^^
= note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
error: an inner attribute is not permitted in this context
- --> $DIR/attr-stmt-expr-attr-bad.rs:27:35
+ --> $DIR/attr-stmt-expr-attr-bad.rs:29:35
|
LL | #[cfg(FALSE)] fn e() { let _ = || #![attr] {foo}; }
| ^^^^^^^^
= note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
error: an inner attribute is not permitted in this context
- --> $DIR/attr-stmt-expr-attr-bad.rs:29:40
+ --> $DIR/attr-stmt-expr-attr-bad.rs:31:40
|
LL | #[cfg(FALSE)] fn e() { let _ = move || #![attr] {foo}; }
| ^^^^^^^^
= note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
error: expected expression, found `..`
- --> $DIR/attr-stmt-expr-attr-bad.rs:31:40
+ --> $DIR/attr-stmt-expr-attr-bad.rs:33:40
|
LL | #[cfg(FALSE)] fn e() { let _ = #[attr] ..#[attr] 0; }
| ^^ expected expression
error: expected expression, found `..`
- --> $DIR/attr-stmt-expr-attr-bad.rs:33:40
+ --> $DIR/attr-stmt-expr-attr-bad.rs:35:40
|
LL | #[cfg(FALSE)] fn e() { let _ = #[attr] ..; }
| ^^ expected expression
error: an inner attribute is not permitted in this context
- --> $DIR/attr-stmt-expr-attr-bad.rs:35:41
+ --> $DIR/attr-stmt-expr-attr-bad.rs:37:41
|
LL | #[cfg(FALSE)] fn e() { let _ = #[attr] &#![attr] 0; }
| ^^^^^^^^
= note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
error: an inner attribute is not permitted in this context
- --> $DIR/attr-stmt-expr-attr-bad.rs:37:45
+ --> $DIR/attr-stmt-expr-attr-bad.rs:39:45
|
LL | #[cfg(FALSE)] fn e() { let _ = #[attr] &mut #![attr] 0; }
| ^^^^^^^^
= note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
error: attributes are not yet allowed on `if` expressions
- --> $DIR/attr-stmt-expr-attr-bad.rs:39:32
+ --> $DIR/attr-stmt-expr-attr-bad.rs:41:32
|
LL | #[cfg(FALSE)] fn e() { let _ = #[attr] if 0 {}; }
| ^^^^^^^
error: expected `{`, found `#`
- --> $DIR/attr-stmt-expr-attr-bad.rs:41:37
+ --> $DIR/attr-stmt-expr-attr-bad.rs:43:37
|
LL | #[cfg(FALSE)] fn e() { let _ = if 0 #[attr] {}; }
| -- ^ --- help: try placing this code inside a block: `{ {}; }`
| this `if` expression has a condition, but no block
error: an inner attribute is not permitted in this context
- --> $DIR/attr-stmt-expr-attr-bad.rs:43:38
+ --> $DIR/attr-stmt-expr-attr-bad.rs:45:38
|
LL | #[cfg(FALSE)] fn e() { let _ = if 0 {#![attr]}; }
| ^^^^^^^^
= note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
error: expected one of `.`, `;`, `?`, `else`, or an operator, found `#`
- --> $DIR/attr-stmt-expr-attr-bad.rs:45:40
+ --> $DIR/attr-stmt-expr-attr-bad.rs:47:40
|
LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} #[attr] else {}; }
| ^ expected one of `.`, `;`, `?`, `else`, or an operator
error: expected `{`, found `#`
- --> $DIR/attr-stmt-expr-attr-bad.rs:47:45
+ --> $DIR/attr-stmt-expr-attr-bad.rs:49:45
|
LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] {}; }
| ^ --- help: try placing this code inside a block: `{ {}; }`
| expected `{`
error: an inner attribute is not permitted in this context
- --> $DIR/attr-stmt-expr-attr-bad.rs:49:46
+ --> $DIR/attr-stmt-expr-attr-bad.rs:51:46
|
LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else {#![attr]}; }
| ^^^^^^^^
= note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
error: attributes are not yet allowed on `if` expressions
- --> $DIR/attr-stmt-expr-attr-bad.rs:51:45
+ --> $DIR/attr-stmt-expr-attr-bad.rs:53:45
|
LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] if 0 {}; }
| ^^^^^^^
error: expected `{`, found `#`
- --> $DIR/attr-stmt-expr-attr-bad.rs:51:45
+ --> $DIR/attr-stmt-expr-attr-bad.rs:53:45
|
LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] if 0 {}; }
| ^ -------- help: try placing this code inside a block: `{ if 0 {}; }`
| expected `{`
error: expected `{`, found `#`
- --> $DIR/attr-stmt-expr-attr-bad.rs:54:50
+ --> $DIR/attr-stmt-expr-attr-bad.rs:56:50
|
LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 #[attr] {}; }
| -- ^ --- help: try placing this code inside a block: `{ {}; }`
| this `if` expression has a condition, but no block
error: an inner attribute is not permitted in this context
- --> $DIR/attr-stmt-expr-attr-bad.rs:56:51
+ --> $DIR/attr-stmt-expr-attr-bad.rs:58:51
|
LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 {#![attr]}; }
| ^^^^^^^^
= note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
error: attributes are not yet allowed on `if` expressions
- --> $DIR/attr-stmt-expr-attr-bad.rs:58:32
+ --> $DIR/attr-stmt-expr-attr-bad.rs:60:32
|
LL | #[cfg(FALSE)] fn e() { let _ = #[attr] if let _ = 0 {}; }
| ^^^^^^^
error: expected `{`, found `#`
- --> $DIR/attr-stmt-expr-attr-bad.rs:60:45
+ --> $DIR/attr-stmt-expr-attr-bad.rs:62:45
|
LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 #[attr] {}; }
| -- ^ --- help: try placing this code inside a block: `{ {}; }`
| this `if` expression has a condition, but no block
error: an inner attribute is not permitted in this context
- --> $DIR/attr-stmt-expr-attr-bad.rs:62:46
+ --> $DIR/attr-stmt-expr-attr-bad.rs:64:46
|
LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {#![attr]}; }
| ^^^^^^^^
= note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
error: expected one of `.`, `;`, `?`, `else`, or an operator, found `#`
- --> $DIR/attr-stmt-expr-attr-bad.rs:64:48
+ --> $DIR/attr-stmt-expr-attr-bad.rs:66:48
|
LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} #[attr] else {}; }
| ^ expected one of `.`, `;`, `?`, `else`, or an operator
error: expected `{`, found `#`
- --> $DIR/attr-stmt-expr-attr-bad.rs:66:53
+ --> $DIR/attr-stmt-expr-attr-bad.rs:68:53
|
LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] {}; }
| ^ --- help: try placing this code inside a block: `{ {}; }`
| expected `{`
error: an inner attribute is not permitted in this context
- --> $DIR/attr-stmt-expr-attr-bad.rs:68:54
+ --> $DIR/attr-stmt-expr-attr-bad.rs:70:54
|
LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else {#![attr]}; }
| ^^^^^^^^
= note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
error: attributes are not yet allowed on `if` expressions
- --> $DIR/attr-stmt-expr-attr-bad.rs:70:53
+ --> $DIR/attr-stmt-expr-attr-bad.rs:72:53
|
LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] if let _ = 0 {}; }
| ^^^^^^^
error: expected `{`, found `#`
- --> $DIR/attr-stmt-expr-attr-bad.rs:70:53
+ --> $DIR/attr-stmt-expr-attr-bad.rs:72:53
|
LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] if let _ = 0 {}; }
| ^ ---------------- help: try placing this code inside a block: `{ if let _ = 0 {}; }`
| expected `{`
error: expected `{`, found `#`
- --> $DIR/attr-stmt-expr-attr-bad.rs:73:66
+ --> $DIR/attr-stmt-expr-attr-bad.rs:75:66
|
LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 #[attr] {}; }
| -- ^ --- help: try placing this code inside a block: `{ {}; }`
| this `if` expression has a condition, but no block
error: an inner attribute is not permitted in this context
- --> $DIR/attr-stmt-expr-attr-bad.rs:75:67
+ --> $DIR/attr-stmt-expr-attr-bad.rs:77:67
|
LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 {#![attr]}; }
| ^^^^^^^^
= note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
error: an inner attribute is not permitted following an outer attribute
- --> $DIR/attr-stmt-expr-attr-bad.rs:78:32
+ --> $DIR/attr-stmt-expr-attr-bad.rs:80:32
|
LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] let _ = 0; }
| ------- ^^^^^^^^ not permitted following an outer attibute
= note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
error: an inner attribute is not permitted following an outer attribute
- --> $DIR/attr-stmt-expr-attr-bad.rs:80:32
+ --> $DIR/attr-stmt-expr-attr-bad.rs:82:32
|
LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] 0; }
| ------- ^^^^^^^^ not permitted following an outer attibute
= note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
error: an inner attribute is not permitted following an outer attribute
- --> $DIR/attr-stmt-expr-attr-bad.rs:82:32
+ --> $DIR/attr-stmt-expr-attr-bad.rs:84:32
|
LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] foo!(); }
| ------- ^^^^^^^^ not permitted following an outer attibute
= note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
error: an inner attribute is not permitted following an outer attribute
- --> $DIR/attr-stmt-expr-attr-bad.rs:84:32
+ --> $DIR/attr-stmt-expr-attr-bad.rs:86:32
|
LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] foo![]; }
| ------- ^^^^^^^^ not permitted following an outer attibute
= note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
error: an inner attribute is not permitted following an outer attribute
- --> $DIR/attr-stmt-expr-attr-bad.rs:86:32
+ --> $DIR/attr-stmt-expr-attr-bad.rs:88:32
|
LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] foo!{}; }
| ------- ^^^^^^^^ not permitted following an outer attibute
|
= note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
-error: `X..=` range patterns are not supported
- --> $DIR/attr-stmt-expr-attr-bad.rs:92:34
+error[E0586]: inclusive range with no end
+ --> $DIR/attr-stmt-expr-attr-bad.rs:94:35
|
LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] 10 => () } }
- | ^^^^ help: try using the maximum value for the type: `0..=MAX`
+ | ^^^ help: use `..` instead
+ |
+ = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
error: expected one of `=>`, `if`, or `|`, found `#`
- --> $DIR/attr-stmt-expr-attr-bad.rs:92:38
+ --> $DIR/attr-stmt-expr-attr-bad.rs:94:38
|
LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] 10 => () } }
| ^ expected one of `=>`, `if`, or `|`
-error: `X..=` range patterns are not supported
- --> $DIR/attr-stmt-expr-attr-bad.rs:95:34
+error[E0586]: inclusive range with no end
+ --> $DIR/attr-stmt-expr-attr-bad.rs:97:35
|
LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] -10 => () } }
- | ^^^^ help: try using the maximum value for the type: `0..=MAX`
+ | ^^^ help: use `..` instead
+ |
+ = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
error: expected one of `=>`, `if`, or `|`, found `#`
- --> $DIR/attr-stmt-expr-attr-bad.rs:95:38
+ --> $DIR/attr-stmt-expr-attr-bad.rs:97:38
|
LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] -10 => () } }
| ^ expected one of `=>`, `if`, or `|`
error: unexpected token: `#`
- --> $DIR/attr-stmt-expr-attr-bad.rs:98:39
+ --> $DIR/attr-stmt-expr-attr-bad.rs:100:39
|
LL | #[cfg(FALSE)] fn e() { match 0 { 0..=-#[attr] 10 => () } }
| ^
-error: `X..=` range patterns are not supported
- --> $DIR/attr-stmt-expr-attr-bad.rs:100:34
+error[E0586]: inclusive range with no end
+ --> $DIR/attr-stmt-expr-attr-bad.rs:102:35
|
LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] FOO => () } }
- | ^^^^ help: try using the maximum value for the type: `0..=MAX`
+ | ^^^ help: use `..` instead
+ |
+ = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
error: expected one of `=>`, `if`, or `|`, found `#`
- --> $DIR/attr-stmt-expr-attr-bad.rs:100:38
+ --> $DIR/attr-stmt-expr-attr-bad.rs:102:38
|
LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] FOO => () } }
| ^ expected one of `=>`, `if`, or `|`
error: unexpected token: `#`
- --> $DIR/attr-stmt-expr-attr-bad.rs:104:34
+ --> $DIR/attr-stmt-expr-attr-bad.rs:106:34
|
LL | #[cfg(FALSE)] fn e() { let _ = x.#![attr]foo(); }
| ^
error: expected one of `.`, `;`, `?`, or an operator, found `#`
- --> $DIR/attr-stmt-expr-attr-bad.rs:104:34
+ --> $DIR/attr-stmt-expr-attr-bad.rs:106:34
|
LL | #[cfg(FALSE)] fn e() { let _ = x.#![attr]foo(); }
| ^ expected one of `.`, `;`, `?`, or an operator
error: unexpected token: `#`
- --> $DIR/attr-stmt-expr-attr-bad.rs:107:34
+ --> $DIR/attr-stmt-expr-attr-bad.rs:109:34
|
LL | #[cfg(FALSE)] fn e() { let _ = x.#[attr]foo(); }
| ^
error: expected one of `.`, `;`, `?`, or an operator, found `#`
- --> $DIR/attr-stmt-expr-attr-bad.rs:107:34
+ --> $DIR/attr-stmt-expr-attr-bad.rs:109:34
|
LL | #[cfg(FALSE)] fn e() { let _ = x.#[attr]foo(); }
| ^ expected one of `.`, `;`, `?`, or an operator
error: expected statement after outer attribute
- --> $DIR/attr-stmt-expr-attr-bad.rs:112:44
+ --> $DIR/attr-stmt-expr-attr-bad.rs:114:44
|
LL | #[cfg(FALSE)] fn e() { { fn foo() { #[attr]; } } }
| ^
error: expected statement after outer attribute
- --> $DIR/attr-stmt-expr-attr-bad.rs:114:45
+ --> $DIR/attr-stmt-expr-attr-bad.rs:116:45
|
LL | #[cfg(FALSE)] fn e() { { fn foo() { #[attr] } } }
| ^
error: aborting due to 57 previous errors
+For more information about this error, try `rustc --explain E0586`.
-// compile-flags: -Z continue-parse-after-error
-
// ignore-tidy-cr
// ignore-tidy-tab
+
fn main() {
// these literals are just silly.
''';
error: character constant must be escaped: '
- --> $DIR/bad-char-literals.rs:7:6
+ --> $DIR/bad-char-literals.rs:6:6
|
LL | ''';
| ^
error: character constant must be escaped: \n
- --> $DIR/bad-char-literals.rs:11:6
+ --> $DIR/bad-char-literals.rs:10:6
|
LL | '
| ______^
| |_
error: character constant must be escaped: \r
- --> $DIR/bad-char-literals.rs:16:6
+ --> $DIR/bad-char-literals.rs:15:6
|
LL | '\r';
| ^
error: character constant must be escaped: \t
- --> $DIR/bad-char-literals.rs:19:6
+ --> $DIR/bad-char-literals.rs:18:6
|
LL | ' ';
| ^^^^
T: ?for<'a> Trait, // OK
T: Tr +, // OK
T: ?'a, //~ ERROR `?` may only modify trait bounds, not lifetime bounds
+
+ T: ?const Tr, // OK
+ T: ?const ?Tr, // OK
+ T: ?const Tr + 'a, // OK
+ T: ?const 'a, //~ ERROR `?const` may only modify trait bounds, not lifetime bounds
>;
fn main() {}
LL | T: ?'a,
| ^
-error: aborting due to previous error
+error: `?const` may only modify trait bounds, not lifetime bounds
+ --> $DIR/bounds-type.rs:15:8
+ |
+LL | T: ?const 'a,
+ | ^^^^^^
+
+error: aborting due to 2 previous errors
-// compile-flags: -Z continue-parse-after-error
-
-
// ignore-tidy-tab
static FOO: u8 = b'\f'; //~ ERROR unknown byte escape
error: unknown byte escape: f
- --> $DIR/byte-literals.rs:6:21
+ --> $DIR/byte-literals.rs:3:21
|
LL | static FOO: u8 = b'\f';
| ^ unknown byte escape
error: unknown byte escape: f
- --> $DIR/byte-literals.rs:9:8
+ --> $DIR/byte-literals.rs:6:8
|
LL | b'\f';
| ^ unknown byte escape
error: invalid character in numeric character escape: Z
- --> $DIR/byte-literals.rs:10:10
+ --> $DIR/byte-literals.rs:7:10
|
LL | b'\x0Z';
| ^
error: byte constant must be escaped: \t
- --> $DIR/byte-literals.rs:11:7
+ --> $DIR/byte-literals.rs:8:7
|
LL | b' ';
| ^^^^
error: byte constant must be escaped: '
- --> $DIR/byte-literals.rs:12:7
+ --> $DIR/byte-literals.rs:9:7
|
LL | b''';
| ^
error: byte constant must be ASCII. Use a \xHH escape for a non-ASCII byte
- --> $DIR/byte-literals.rs:13:7
+ --> $DIR/byte-literals.rs:10:7
|
LL | b'é';
| ^
error: unterminated byte constant
- --> $DIR/byte-literals.rs:14:6
+ --> $DIR/byte-literals.rs:11:6
|
LL | b'a
| ^^^^
-// compile-flags: -Z continue-parse-after-error
-
static FOO: &'static [u8] = b"\f"; //~ ERROR unknown byte escape
pub fn main() {
error: unknown byte escape: f
- --> $DIR/byte-string-literals.rs:3:32
+ --> $DIR/byte-string-literals.rs:1:32
|
LL | static FOO: &'static [u8] = b"\f";
| ^ unknown byte escape
error: unknown byte escape: f
- --> $DIR/byte-string-literals.rs:6:8
+ --> $DIR/byte-string-literals.rs:4:8
|
LL | b"\f";
| ^ unknown byte escape
error: invalid character in numeric character escape: Z
- --> $DIR/byte-string-literals.rs:7:10
+ --> $DIR/byte-string-literals.rs:5:10
|
LL | b"\x0Z";
| ^
error: byte constant must be ASCII. Use a \xHH escape for a non-ASCII byte
- --> $DIR/byte-string-literals.rs:8:7
+ --> $DIR/byte-string-literals.rs:6:7
|
LL | b"é";
| ^
error: unterminated double quote byte string
- --> $DIR/byte-string-literals.rs:9:6
+ --> $DIR/byte-string-literals.rs:7:6
|
LL | b"a
| ______^
--- /dev/null
+// Check that we get nice suggestions when attempting a chained comparison.
+
+fn comp1() {
+ 1 < 2 <= 3; //~ ERROR comparison operators cannot be chained
+ //~^ ERROR mismatched types
+}
+
+fn comp2() {
+ 1 < 2 < 3; //~ ERROR comparison operators cannot be chained
+}
+
+fn comp3() {
+ 1 <= 2 < 3; //~ ERROR comparison operators cannot be chained
+ //~^ ERROR mismatched types
+}
+
+fn comp4() {
+ 1 <= 2 <= 3; //~ ERROR comparison operators cannot be chained
+ //~^ ERROR mismatched types
+}
+
+fn comp5() {
+ 1 > 2 >= 3; //~ ERROR comparison operators cannot be chained
+ //~^ ERROR mismatched types
+}
+
+fn comp6() {
+ 1 > 2 > 3; //~ ERROR comparison operators cannot be chained
+}
+
+fn comp7() {
+ 1 >= 2 > 3; //~ ERROR comparison operators cannot be chained
+}
+
+fn comp8() {
+ 1 >= 2 >= 3; //~ ERROR comparison operators cannot be chained
+ //~^ ERROR mismatched types
+}
+
+fn main() {}
--- /dev/null
+error: comparison operators cannot be chained
+ --> $DIR/chained-comparison-suggestion.rs:4:7
+ |
+LL | 1 < 2 <= 3;
+ | ^^^^^^
+ |
+help: split the comparison into two...
+ |
+LL | 1 < 2 && 2 <= 3;
+ | ^^^^^^^^^^^^^
+help: ...or parenthesize one of the comparisons
+ |
+LL | (1 < 2) <= 3;
+ | ^^^^^^^^^^
+
+error: comparison operators cannot be chained
+ --> $DIR/chained-comparison-suggestion.rs:9:7
+ |
+LL | 1 < 2 < 3;
+ | ^^^^^
+ |
+ = help: use `::<...>` instead of `<...>` to specify type arguments
+ = help: or use `(...)` if you meant to specify fn arguments
+help: split the comparison into two...
+ |
+LL | 1 < 2 && 2 < 3;
+ | ^^^^^^^^^^^^
+help: ...or parenthesize one of the comparisons
+ |
+LL | (1 < 2) < 3;
+ | ^^^^^^^^^
+
+error: comparison operators cannot be chained
+ --> $DIR/chained-comparison-suggestion.rs:13:7
+ |
+LL | 1 <= 2 < 3;
+ | ^^^^^^
+ |
+help: split the comparison into two...
+ |
+LL | 1 <= 2 && 2 < 3;
+ | ^^^^^^^^^^^^^
+help: ...or parenthesize one of the comparisons
+ |
+LL | (1 <= 2) < 3;
+ | ^^^^^^^^^^
+
+error: comparison operators cannot be chained
+ --> $DIR/chained-comparison-suggestion.rs:18:7
+ |
+LL | 1 <= 2 <= 3;
+ | ^^^^^^^
+ |
+help: split the comparison into two...
+ |
+LL | 1 <= 2 && 2 <= 3;
+ | ^^^^^^^^^^^^^^
+help: ...or parenthesize one of the comparisons
+ |
+LL | (1 <= 2) <= 3;
+ | ^^^^^^^^^^^
+
+error: comparison operators cannot be chained
+ --> $DIR/chained-comparison-suggestion.rs:23:7
+ |
+LL | 1 > 2 >= 3;
+ | ^^^^^^
+ |
+help: split the comparison into two...
+ |
+LL | 1 > 2 && 2 >= 3;
+ | ^^^^^^^^^^^^^
+help: ...or parenthesize one of the comparisons
+ |
+LL | (1 > 2) >= 3;
+ | ^^^^^^^^^^
+
+error: comparison operators cannot be chained
+ --> $DIR/chained-comparison-suggestion.rs:28:7
+ |
+LL | 1 > 2 > 3;
+ | ^^^^^
+ |
+ = help: use `::<...>` instead of `<...>` to specify type arguments
+ = help: or use `(...)` if you meant to specify fn arguments
+help: split the comparison into two...
+ |
+LL | 1 > 2 && 2 > 3;
+ | ^^^^^^^^^^^^
+help: ...or parenthesize one of the comparisons
+ |
+LL | (1 > 2) > 3;
+ | ^^^^^^^^^
+
+error: comparison operators cannot be chained
+ --> $DIR/chained-comparison-suggestion.rs:32:7
+ |
+LL | 1 >= 2 > 3;
+ | ^^^^^^
+ |
+ = help: use `::<...>` instead of `<...>` to specify type arguments
+ = help: or use `(...)` if you meant to specify fn arguments
+help: split the comparison into two...
+ |
+LL | 1 >= 2 && 2 > 3;
+ | ^^^^^^^^^^^^^
+help: ...or parenthesize one of the comparisons
+ |
+LL | (1 >= 2) > 3;
+ | ^^^^^^^^^^
+
+error: comparison operators cannot be chained
+ --> $DIR/chained-comparison-suggestion.rs:36:7
+ |
+LL | 1 >= 2 >= 3;
+ | ^^^^^^^
+ |
+help: split the comparison into two...
+ |
+LL | 1 >= 2 && 2 >= 3;
+ | ^^^^^^^^^^^^^^
+help: ...or parenthesize one of the comparisons
+ |
+LL | (1 >= 2) >= 3;
+ | ^^^^^^^^^^^
+
+error[E0308]: mismatched types
+ --> $DIR/chained-comparison-suggestion.rs:4:14
+ |
+LL | 1 < 2 <= 3;
+ | ^ expected `bool`, found integer
+
+error[E0308]: mismatched types
+ --> $DIR/chained-comparison-suggestion.rs:13:14
+ |
+LL | 1 <= 2 < 3;
+ | ^ expected `bool`, found integer
+
+error[E0308]: mismatched types
+ --> $DIR/chained-comparison-suggestion.rs:18:15
+ |
+LL | 1 <= 2 <= 3;
+ | ^ expected `bool`, found integer
+
+error[E0308]: mismatched types
+ --> $DIR/chained-comparison-suggestion.rs:23:14
+ |
+LL | 1 > 2 >= 3;
+ | ^ expected `bool`, found integer
+
+error[E0308]: mismatched types
+ --> $DIR/chained-comparison-suggestion.rs:36:15
+ |
+LL | 1 >= 2 >= 3;
+ | ^ expected `bool`, found integer
+
+error: aborting due to 13 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
-// compile-flags: -Z continue-parse-after-error
-
impl ! {} // OK
impl ! where u8: Copy {} // OK
error: missing `for` in a trait impl
- --> $DIR/impl-parsing.rs:6:11
+ --> $DIR/impl-parsing.rs:4:11
|
LL | impl Trait Type {}
| ^ help: add `for` here
error: missing `for` in a trait impl
- --> $DIR/impl-parsing.rs:7:11
+ --> $DIR/impl-parsing.rs:5:11
|
LL | impl Trait .. {}
| ^ help: add `for` here
error: expected a trait, found type
- --> $DIR/impl-parsing.rs:8:6
+ --> $DIR/impl-parsing.rs:6:6
|
LL | impl ?Sized for Type {}
| ^^^^^^
error: expected a trait, found type
- --> $DIR/impl-parsing.rs:9:6
+ --> $DIR/impl-parsing.rs:7:6
|
LL | impl ?Sized for .. {}
| ^^^^^^
error: expected `impl`, found `FAIL`
- --> $DIR/impl-parsing.rs:11:16
+ --> $DIR/impl-parsing.rs:9:16
|
LL | default unsafe FAIL
| ^^^^ expected `impl`
-// compile-flags: -Z continue-parse-after-error
-
fn main() {
let _ = b"\u{a66e}";
//~^ ERROR unicode escape sequences cannot be used as a byte or in a byte string
error: unicode escape sequences cannot be used as a byte or in a byte string
- --> $DIR/issue-23620-invalid-escapes.rs:4:15
+ --> $DIR/issue-23620-invalid-escapes.rs:2:15
|
LL | let _ = b"\u{a66e}";
| ^^^^^^^^
error: unicode escape sequences cannot be used as a byte or in a byte string
- --> $DIR/issue-23620-invalid-escapes.rs:7:15
+ --> $DIR/issue-23620-invalid-escapes.rs:5:15
|
LL | let _ = b'\u{a66e}';
| ^^^^^^^^
error: incorrect unicode escape sequence
- --> $DIR/issue-23620-invalid-escapes.rs:10:15
+ --> $DIR/issue-23620-invalid-escapes.rs:8:15
|
LL | let _ = b'\u';
| ^^ incorrect unicode escape sequence
= help: format of unicode escape sequences is `\u{...}`
error: numeric character escape is too short
- --> $DIR/issue-23620-invalid-escapes.rs:13:15
+ --> $DIR/issue-23620-invalid-escapes.rs:11:15
|
LL | let _ = b'\x5';
| ^^^
error: invalid character in numeric character escape: x
- --> $DIR/issue-23620-invalid-escapes.rs:16:17
+ --> $DIR/issue-23620-invalid-escapes.rs:14:17
|
LL | let _ = b'\xxy';
| ^
error: numeric character escape is too short
- --> $DIR/issue-23620-invalid-escapes.rs:19:14
+ --> $DIR/issue-23620-invalid-escapes.rs:17:14
|
LL | let _ = '\x5';
| ^^^
error: invalid character in numeric character escape: x
- --> $DIR/issue-23620-invalid-escapes.rs:22:16
+ --> $DIR/issue-23620-invalid-escapes.rs:20:16
|
LL | let _ = '\xxy';
| ^
error: unicode escape sequences cannot be used as a byte or in a byte string
- --> $DIR/issue-23620-invalid-escapes.rs:25:15
+ --> $DIR/issue-23620-invalid-escapes.rs:23:15
|
LL | let _ = b"\u{a4a4} \xf \u";
| ^^^^^^^^
error: invalid character in numeric character escape:
- --> $DIR/issue-23620-invalid-escapes.rs:25:27
+ --> $DIR/issue-23620-invalid-escapes.rs:23:27
|
LL | let _ = b"\u{a4a4} \xf \u";
| ^
error: incorrect unicode escape sequence
- --> $DIR/issue-23620-invalid-escapes.rs:25:28
+ --> $DIR/issue-23620-invalid-escapes.rs:23:28
|
LL | let _ = b"\u{a4a4} \xf \u";
| ^^ incorrect unicode escape sequence
= help: format of unicode escape sequences is `\u{...}`
error: invalid character in numeric character escape:
- --> $DIR/issue-23620-invalid-escapes.rs:30:17
+ --> $DIR/issue-23620-invalid-escapes.rs:28:17
|
LL | let _ = "\xf \u";
| ^
error: incorrect unicode escape sequence
- --> $DIR/issue-23620-invalid-escapes.rs:30:18
+ --> $DIR/issue-23620-invalid-escapes.rs:28:18
|
LL | let _ = "\xf \u";
| ^^ incorrect unicode escape sequence
= help: format of unicode escape sequences is `\u{...}`
error: incorrect unicode escape sequence
- --> $DIR/issue-23620-invalid-escapes.rs:34:14
+ --> $DIR/issue-23620-invalid-escapes.rs:32:14
|
LL | let _ = "\u8f";
| ^^--
"\u\\"
//~^ ERROR incorrect unicode escape sequence
//~| ERROR invalid trailing slash in literal
+//~| ERROR expected item, found `"\u\\"`
LL | "\u\"
| ^
-error: aborting due to 2 previous errors
+error: expected item, found `"\u\"`
+ --> $DIR/issue-62913.rs:1:1
+ |
+LL | "\u\"
+ | ^^^^^^ expected item
+
+error: aborting due to 3 previous errors
// ignore-tidy-trailing-newlines
-// error-pattern: aborting due to 6 previous errors
+// error-pattern: aborting due to 7 previous errors
fn main() {}
LL |
| ^
+error: this file contains an unclosed delimiter
+ --> $DIR/issue-62973.rs:8:2
+ |
+LL | fn p() { match s { v, E { [) {) }
+ | - - unclosed delimiter
+ | |
+ | unclosed delimiter
+LL |
+LL |
+ | ^
+
error: expected one of `,` or `}`, found `{`
--> $DIR/issue-62973.rs:6:25
|
| |
| unclosed delimiter
-error: aborting due to 6 previous errors
+error: aborting due to 7 previous errors
// check-pass
#![feature(exclusive_range_pattern)]
+#![feature(half_open_range_patterns)]
#![allow(ellipsis_inclusive_range_patterns)]
if let 2...$e = 3 {}
if let 2..=$e = 3 {}
if let 2..$e = 3 {}
+ if let ..$e = 3 {}
+ if let ..=$e = 3 {}
+ if let $e.. = 5 {}
+ if let $e..5 = 4 {}
+ if let $e..=5 = 4 {}
}
}
mac_expr!(4);
-// error-pattern: aborting due to 5 previous errors
+// error-pattern: aborting due to 6 previous errors
fn i(n{...,f #
| | unclosed delimiter
| unclosed delimiter
+error: this file contains an unclosed delimiter
+ --> $DIR/issue-63135.rs:3:16
+ |
+LL | fn i(n{...,f #
+ | - - ^
+ | | |
+ | | unclosed delimiter
+ | unclosed delimiter
+
error: expected field pattern, found `...`
--> $DIR/issue-63135.rs:3:8
|
LL | fn i(n{...,f #
| ^ expected one of `:` or `|`
-error: aborting due to 5 previous errors
+error: aborting due to 6 previous errors
+// ignore-tidy-linelength
+
// The problem in #66357 was that the call trace:
//
// - parse_fn_block_decl
fn f() { |[](* }
//~^ ERROR expected one of `,` or `:`, found `(`
-//~| ERROR expected one of `)`, `-`, `_`, `box`, `mut`, `ref`, `|`, identifier, or path, found `*`
+//~| ERROR expected one of `&`, `(`, `)`, `-`, `...`, `..=`, `..`, `[`, `_`, `box`, `mut`, `ref`, `|`, identifier, or path, found `*`
error: expected one of `,` or `:`, found `(`
- --> $DIR/issue-66357-unexpected-unreachable.rs:12:13
+ --> $DIR/issue-66357-unexpected-unreachable.rs:14:13
|
LL | fn f() { |[](* }
| ^ expected one of `,` or `:`
-error: expected one of `)`, `-`, `_`, `box`, `mut`, `ref`, `|`, identifier, or path, found `*`
- --> $DIR/issue-66357-unexpected-unreachable.rs:12:14
+error: expected one of `&`, `(`, `)`, `-`, `...`, `..=`, `..`, `[`, `_`, `box`, `mut`, `ref`, `|`, identifier, or path, found `*`
+ --> $DIR/issue-66357-unexpected-unreachable.rs:14:14
|
LL | fn f() { |[](* }
| -^ help: `)` may belong here
-// compile-flags: -Z continue-parse-after-error
static c3: char =
'\x1' //~ ERROR: numeric character escape is too short
;
error: numeric character escape is too short
- --> $DIR/lex-bad-char-literals-1.rs:3:6
+ --> $DIR/lex-bad-char-literals-1.rs:2:6
|
LL | '\x1'
| ^^^
error: numeric character escape is too short
- --> $DIR/lex-bad-char-literals-1.rs:7:6
+ --> $DIR/lex-bad-char-literals-1.rs:6:6
|
LL | "\x1"
| ^^^
error: unknown character escape: \u{25cf}
- --> $DIR/lex-bad-char-literals-1.rs:11:7
+ --> $DIR/lex-bad-char-literals-1.rs:10:7
|
LL | '\●'
| ^ unknown character escape
error: unknown character escape: \u{25cf}
- --> $DIR/lex-bad-char-literals-1.rs:15:7
+ --> $DIR/lex-bad-char-literals-1.rs:14:7
|
LL | "\●"
| ^ unknown character escape
-// compile-flags: -Z continue-parse-after-error
fn main() {
let _: char = '';
//~^ ERROR: empty character literal
error: empty character literal
- --> $DIR/lex-bad-char-literals-7.rs:3:20
+ --> $DIR/lex-bad-char-literals-7.rs:2:20
|
LL | let _: char = '';
| ^
error: empty unicode escape (must have at least 1 hex digit)
- --> $DIR/lex-bad-char-literals-7.rs:5:20
+ --> $DIR/lex-bad-char-literals-7.rs:4:20
|
LL | let _: char = '\u{}';
| ^^^^
error: unterminated character literal
- --> $DIR/lex-bad-char-literals-7.rs:12:13
+ --> $DIR/lex-bad-char-literals-7.rs:11:13
|
LL | let _ = ' hello // here's a comment
| ^^^^^^^^
-// compile-flags: -Z continue-parse-after-error
-
// ignore-tidy-cr
/// doc comment with bare CR: '\r'
error: bare CR not allowed in doc-comment
- --> $DIR/lex-bare-cr-string-literal-doc-comment.rs:5:32
+ --> $DIR/lex-bare-cr-string-literal-doc-comment.rs:3:32
|
LL | /// doc comment with bare CR: '\r'
| ^
error: bare CR not allowed in block doc-comment
- --> $DIR/lex-bare-cr-string-literal-doc-comment.rs:9:38
+ --> $DIR/lex-bare-cr-string-literal-doc-comment.rs:7:38
|
LL | /** block doc comment with bare CR: '\r' */
| ^
error: bare CR not allowed in doc-comment
- --> $DIR/lex-bare-cr-string-literal-doc-comment.rs:14:36
+ --> $DIR/lex-bare-cr-string-literal-doc-comment.rs:12:36
|
LL | //! doc comment with bare CR: '\r'
| ^
error: bare CR not allowed in block doc-comment
- --> $DIR/lex-bare-cr-string-literal-doc-comment.rs:17:42
+ --> $DIR/lex-bare-cr-string-literal-doc-comment.rs:15:42
|
LL | /*! block doc comment with bare CR: '\r' */
| ^
error: bare CR not allowed in string, use \r instead
- --> $DIR/lex-bare-cr-string-literal-doc-comment.rs:21:18
+ --> $DIR/lex-bare-cr-string-literal-doc-comment.rs:19:18
|
LL | let _s = "foo\rbar";
| ^
error: bare CR not allowed in raw string
- --> $DIR/lex-bare-cr-string-literal-doc-comment.rs:24:19
+ --> $DIR/lex-bare-cr-string-literal-doc-comment.rs:22:19
|
LL | let _s = r"bar\rfoo";
| ^
error: unknown character escape: \r
- --> $DIR/lex-bare-cr-string-literal-doc-comment.rs:27:19
+ --> $DIR/lex-bare-cr-string-literal-doc-comment.rs:25:19
|
LL | let _s = "foo\\rbar";
| ^ unknown character escape
// ignore-tidy-trailing-newlines
-// error-pattern: aborting due to 2 previous errors
+// error-pattern: aborting due to 3 previous errors
fn main((ؼ
\ No newline at end of file
| |unclosed delimiter
| unclosed delimiter
+error: this file contains an unclosed delimiter
+ --> $DIR/missing_right_paren.rs:3:11
+ |
+LL | fn main((ؼ
+ | -- ^
+ | ||
+ | |unclosed delimiter
+ | unclosed delimiter
+
error: expected one of `:` or `|`, found `)`
--> $DIR/missing_right_paren.rs:3:11
|
LL | fn main((ؼ
| ^ expected one of `:` or `|`
-error: aborting due to 2 previous errors
+error: aborting due to 3 previous errors
+++ /dev/null
-fn main() {
- const PAT: u8 = 0;
-
- match 0 {
- (.. PAT) => {}
- //~^ ERROR `..X` range patterns are not supported
- //~| ERROR exclusive range pattern syntax is experimental
- }
-}
-
-const RECOVERY_WITNESS: () = 0; //~ ERROR mismatched types
+++ /dev/null
-error: `..X` range patterns are not supported
- --> $DIR/pat-tuple-4.rs:5:10
- |
-LL | (.. PAT) => {}
- | ^^^^^^ help: try using the minimum value for the type: `MIN..PAT`
-
-error[E0658]: exclusive range pattern syntax is experimental
- --> $DIR/pat-tuple-4.rs:5:10
- |
-LL | (.. PAT) => {}
- | ^^^^^^
- |
- = note: for more information, see https://github.com/rust-lang/rust/issues/37854
- = help: add `#![feature(exclusive_range_pattern)]` to the crate attributes to enable
-
-error[E0308]: mismatched types
- --> $DIR/pat-tuple-4.rs:11:30
- |
-LL | const RECOVERY_WITNESS: () = 0;
- | ^ expected `()`, found integer
-
-error: aborting due to 3 previous errors
-
-Some errors have detailed explanations: E0308, E0658.
-For more information about an error, try `rustc --explain E0308`.
+++ /dev/null
-fn main() {
- const PAT: u8 = 0;
-
- match (0, 1) {
- (PAT ..) => {}
- //~^ ERROR `X..` range patterns are not supported
- //~| ERROR exclusive range pattern syntax is experimental
- //~| ERROR mismatched types
- }
-}
+++ /dev/null
-error: `X..` range patterns are not supported
- --> $DIR/pat-tuple-5.rs:5:10
- |
-LL | (PAT ..) => {}
- | ^^^^^^ help: try using the maximum value for the type: `PAT..MAX`
-
-error[E0658]: exclusive range pattern syntax is experimental
- --> $DIR/pat-tuple-5.rs:5:10
- |
-LL | (PAT ..) => {}
- | ^^^^^^
- |
- = note: for more information, see https://github.com/rust-lang/rust/issues/37854
- = help: add `#![feature(exclusive_range_pattern)]` to the crate attributes to enable
-
-error[E0308]: mismatched types
- --> $DIR/pat-tuple-5.rs:5:10
- |
-LL | match (0, 1) {
- | ------ this expression has type `({integer}, {integer})`
-LL | (PAT ..) => {}
- | ^^^ expected tuple, found `u8`
- |
- = note: expected tuple `({integer}, {integer})`
- found type `u8`
-
-error: aborting due to 3 previous errors
-
-Some errors have detailed explanations: E0308, E0658.
-For more information about an error, try `rustc --explain E0308`.
pub fn main() {
for _ in 1..= {} //~ERROR inclusive range with no end
- //~^HELP bounded at the end
+ //~^HELP use `..` instead
}
error[E0586]: inclusive range with no end
- --> $DIR/range_inclusive.rs:4:19
+ --> $DIR/range_inclusive.rs:4:15
|
LL | for _ in 1..= {}
- | ^
+ | ^^^ help: use `..` instead
|
- = help: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
+ = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
error: aborting due to previous error
// ignore-tidy-cr
-// compile-flags: -Z continue-parse-after-error
+
pub fn main() {
br"a\r"; //~ ERROR bare CR not allowed in raw string
br"é"; //~ ERROR raw byte string must be ASCII
// 2. Or at least we have parser recovery if they don't.
#![feature(exclusive_range_pattern)]
+#![feature(half_open_range_patterns)]
#![deny(ellipsis_inclusive_range_patterns)]
fn main() {}
}
fn exclusive_from() {
- if let 0.. = 0 {} //~ ERROR `X..` range patterns are not supported
- if let X.. = 0 {} //~ ERROR `X..` range patterns are not supported
- if let true.. = 0 {} //~ ERROR `X..` range patterns are not supported
+ if let 0.. = 0 {}
+ if let X.. = 0 {}
+ if let true.. = 0 {}
//~^ ERROR only char and numeric types
- if let .0.. = 0 {} //~ ERROR `X..` range patterns are not supported
+ if let .0.. = 0 {}
//~^ ERROR float literals must have an integer part
//~| ERROR mismatched types
}
fn inclusive_from() {
- if let 0..= = 0 {} //~ ERROR `X..=` range patterns are not supported
- if let X..= = 0 {} //~ ERROR `X..=` range patterns are not supported
- if let true..= = 0 {} //~ ERROR `X..=` range patterns are not supported
+ if let 0..= = 0 {} //~ ERROR inclusive range with no end
+ if let X..= = 0 {} //~ ERROR inclusive range with no end
+ if let true..= = 0 {} //~ ERROR inclusive range with no end
//~| ERROR only char and numeric types
- if let .0..= = 0 {} //~ ERROR `X..=` range patterns are not supported
+ if let .0..= = 0 {} //~ ERROR inclusive range with no end
//~^ ERROR float literals must have an integer part
//~| ERROR mismatched types
}
fn inclusive2_from() {
- if let 0... = 0 {} //~ ERROR `X...` range patterns are not supported
- //~^ ERROR `...` range patterns are deprecated
- if let X... = 0 {} //~ ERROR `X...` range patterns are not supported
- //~^ ERROR `...` range patterns are deprecated
- if let true... = 0 {} //~ ERROR `X...` range patterns are not supported
- //~^ ERROR `...` range patterns are deprecated
+ if let 0... = 0 {} //~ ERROR inclusive range with no end
+ if let X... = 0 {} //~ ERROR inclusive range with no end
+ if let true... = 0 {} //~ ERROR inclusive range with no end
//~| ERROR only char and numeric types
- if let .0... = 0 {} //~ ERROR `X...` range patterns are not supported
+ if let .0... = 0 {} //~ ERROR inclusive range with no end
//~^ ERROR float literals must have an integer part
- //~| ERROR `...` range patterns are deprecated
//~| ERROR mismatched types
}
fn exclusive_to() {
- if let ..0 = 0 {} //~ ERROR `..X` range patterns are not supported
- if let ..Y = 0 {} //~ ERROR `..X` range patterns are not supported
- if let ..true = 0 {} //~ ERROR `..X` range patterns are not supported
- //~| ERROR only char and numeric types
- if let .. .0 = 0 {} //~ ERROR `..X` range patterns are not supported
+ if let ..0 = 0 {}
+ if let ..Y = 0 {}
+ if let ..true = 0 {}
+ //~^ ERROR only char and numeric types
+ if let .. .0 = 0 {}
//~^ ERROR float literals must have an integer part
//~| ERROR mismatched types
}
fn inclusive_to() {
- if let ..=3 = 0 {} //~ ERROR `..=X` range patterns are not supported
- if let ..=Y = 0 {} //~ ERROR `..=X` range patterns are not supported
- if let ..=true = 0 {} //~ ERROR `..=X` range patterns are not supported
- //~| ERROR only char and numeric types
- if let ..=.0 = 0 {} //~ ERROR `..=X` range patterns are not supported
+ if let ..=3 = 0 {}
+ if let ..=Y = 0 {}
+ if let ..=true = 0 {}
+ //~^ ERROR only char and numeric types
+ if let ..=.0 = 0 {}
//~^ ERROR float literals must have an integer part
//~| ERROR mismatched types
}
fn inclusive2_to() {
- if let ...3 = 0 {} //~ ERROR `...X` range patterns are not supported
- //~^ ERROR `...` range patterns are deprecated
- if let ...Y = 0 {} //~ ERROR `...X` range patterns are not supported
- //~^ ERROR `...` range patterns are deprecated
- if let ...true = 0 {} //~ ERROR `...X` range patterns are not supported
- //~^ ERROR `...` range patterns are deprecated
+ if let ...3 = 0 {}
+ //~^ ERROR range-to patterns with `...` are not allowed
+ if let ...Y = 0 {}
+ //~^ ERROR range-to patterns with `...` are not allowed
+ if let ...true = 0 {}
+ //~^ ERROR range-to patterns with `...` are not allowed
//~| ERROR only char and numeric types
- if let ....3 = 0 {} //~ ERROR `...X` range patterns are not supported
+ if let ....3 = 0 {}
//~^ ERROR float literals must have an integer part
- //~| ERROR `...` range patterns are deprecated
+ //~| ERROR range-to patterns with `...` are not allowed
//~| ERROR mismatched types
}
macro_rules! mac {
($e:expr) => {
- let ..$e; //~ ERROR `..X` range patterns are not supported
- let ...$e; //~ ERROR `...X` range patterns are not supported
- //~^ ERROR `...` range patterns are deprecated
- let ..=$e; //~ ERROR `..=X` range patterns are not supported
- let $e..; //~ ERROR `X..` range patterns are not supported
- let $e...; //~ ERROR `X...` range patterns are not supported
- //~^ ERROR `...` range patterns are deprecated
- let $e..=; //~ ERROR `X..=` range patterns are not supported
+ let ..$e;
+ let ...$e;
+ //~^ ERROR range-to patterns with `...` are not allowed
+ let ..=$e;
+ let $e..;
+ let $e...; //~ ERROR inclusive range with no end
+ let $e..=; //~ ERROR inclusive range with no end
}
}
error: float literals must have an integer part
- --> $DIR/recover-range-pats.rs:21:12
+ --> $DIR/recover-range-pats.rs:22:12
|
LL | if let .0..Y = 0 {}
| ^^ help: must have an integer part: `0.0`
error: float literals must have an integer part
- --> $DIR/recover-range-pats.rs:23:16
+ --> $DIR/recover-range-pats.rs:24:16
|
LL | if let X.. .0 = 0 {}
| ^^ help: must have an integer part: `0.0`
error: float literals must have an integer part
- --> $DIR/recover-range-pats.rs:34:12
+ --> $DIR/recover-range-pats.rs:35:12
|
LL | if let .0..=Y = 0 {}
| ^^ help: must have an integer part: `0.0`
error: float literals must have an integer part
- --> $DIR/recover-range-pats.rs:36:16
+ --> $DIR/recover-range-pats.rs:37:16
|
LL | if let X..=.0 = 0 {}
| ^^ help: must have an integer part: `0.0`
error: float literals must have an integer part
- --> $DIR/recover-range-pats.rs:49:12
+ --> $DIR/recover-range-pats.rs:50:12
|
LL | if let .0...Y = 0 {}
| ^^ help: must have an integer part: `0.0`
error: float literals must have an integer part
- --> $DIR/recover-range-pats.rs:52:17
+ --> $DIR/recover-range-pats.rs:53:17
|
LL | if let X... .0 = 0 {}
| ^^ help: must have an integer part: `0.0`
-error: `X..` range patterns are not supported
- --> $DIR/recover-range-pats.rs:58:12
- |
-LL | if let 0.. = 0 {}
- | ^^^ help: try using the maximum value for the type: `0..MAX`
-
-error: `X..` range patterns are not supported
- --> $DIR/recover-range-pats.rs:59:12
- |
-LL | if let X.. = 0 {}
- | ^^^ help: try using the maximum value for the type: `X..MAX`
-
-error: `X..` range patterns are not supported
- --> $DIR/recover-range-pats.rs:60:12
- |
-LL | if let true.. = 0 {}
- | ^^^^^^ help: try using the maximum value for the type: `true..MAX`
-
error: float literals must have an integer part
- --> $DIR/recover-range-pats.rs:62:12
+ --> $DIR/recover-range-pats.rs:63:12
|
LL | if let .0.. = 0 {}
| ^^ help: must have an integer part: `0.0`
-error: `X..` range patterns are not supported
- --> $DIR/recover-range-pats.rs:62:12
- |
-LL | if let .0.. = 0 {}
- | ^^^^ help: try using the maximum value for the type: `0.0..MAX`
-
-error: `X..=` range patterns are not supported
- --> $DIR/recover-range-pats.rs:68:12
+error[E0586]: inclusive range with no end
+ --> $DIR/recover-range-pats.rs:69:13
|
LL | if let 0..= = 0 {}
- | ^^^^ help: try using the maximum value for the type: `0..=MAX`
+ | ^^^ help: use `..` instead
+ |
+ = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
-error: `X..=` range patterns are not supported
- --> $DIR/recover-range-pats.rs:69:12
+error[E0586]: inclusive range with no end
+ --> $DIR/recover-range-pats.rs:70:13
|
LL | if let X..= = 0 {}
- | ^^^^ help: try using the maximum value for the type: `X..=MAX`
+ | ^^^ help: use `..` instead
+ |
+ = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
-error: `X..=` range patterns are not supported
- --> $DIR/recover-range-pats.rs:70:12
+error[E0586]: inclusive range with no end
+ --> $DIR/recover-range-pats.rs:71:16
|
LL | if let true..= = 0 {}
- | ^^^^^^^ help: try using the maximum value for the type: `true..=MAX`
+ | ^^^ help: use `..` instead
+ |
+ = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
error: float literals must have an integer part
- --> $DIR/recover-range-pats.rs:72:12
+ --> $DIR/recover-range-pats.rs:73:12
|
LL | if let .0..= = 0 {}
| ^^ help: must have an integer part: `0.0`
-error: `X..=` range patterns are not supported
- --> $DIR/recover-range-pats.rs:72:12
+error[E0586]: inclusive range with no end
+ --> $DIR/recover-range-pats.rs:73:14
|
LL | if let .0..= = 0 {}
- | ^^^^^ help: try using the maximum value for the type: `0.0..=MAX`
+ | ^^^ help: use `..` instead
+ |
+ = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
-error: `X...` range patterns are not supported
- --> $DIR/recover-range-pats.rs:78:12
+error[E0586]: inclusive range with no end
+ --> $DIR/recover-range-pats.rs:79:13
|
LL | if let 0... = 0 {}
- | ^^^^ help: try using the maximum value for the type: `0...MAX`
+ | ^^^ help: use `..` instead
+ |
+ = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
-error: `X...` range patterns are not supported
- --> $DIR/recover-range-pats.rs:80:12
+error[E0586]: inclusive range with no end
+ --> $DIR/recover-range-pats.rs:80:13
|
LL | if let X... = 0 {}
- | ^^^^ help: try using the maximum value for the type: `X...MAX`
+ | ^^^ help: use `..` instead
+ |
+ = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
-error: `X...` range patterns are not supported
- --> $DIR/recover-range-pats.rs:82:12
+error[E0586]: inclusive range with no end
+ --> $DIR/recover-range-pats.rs:81:16
|
LL | if let true... = 0 {}
- | ^^^^^^^ help: try using the maximum value for the type: `true...MAX`
+ | ^^^ help: use `..` instead
+ |
+ = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
error: float literals must have an integer part
- --> $DIR/recover-range-pats.rs:85:12
+ --> $DIR/recover-range-pats.rs:83:12
|
LL | if let .0... = 0 {}
| ^^ help: must have an integer part: `0.0`
-error: `X...` range patterns are not supported
- --> $DIR/recover-range-pats.rs:85:12
+error[E0586]: inclusive range with no end
+ --> $DIR/recover-range-pats.rs:83:14
|
LL | if let .0... = 0 {}
- | ^^^^^ help: try using the maximum value for the type: `0.0...MAX`
-
-error: `..X` range patterns are not supported
- --> $DIR/recover-range-pats.rs:92:12
+ | ^^^ help: use `..` instead
|
-LL | if let ..0 = 0 {}
- | ^^^ help: try using the minimum value for the type: `MIN..0`
-
-error: `..X` range patterns are not supported
- --> $DIR/recover-range-pats.rs:93:12
- |
-LL | if let ..Y = 0 {}
- | ^^^ help: try using the minimum value for the type: `MIN..Y`
-
-error: `..X` range patterns are not supported
- --> $DIR/recover-range-pats.rs:94:12
- |
-LL | if let ..true = 0 {}
- | ^^^^^^ help: try using the minimum value for the type: `MIN..true`
+ = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
error: float literals must have an integer part
- --> $DIR/recover-range-pats.rs:96:15
+ --> $DIR/recover-range-pats.rs:93:15
|
LL | if let .. .0 = 0 {}
| ^^ help: must have an integer part: `0.0`
-error: `..X` range patterns are not supported
- --> $DIR/recover-range-pats.rs:96:12
- |
-LL | if let .. .0 = 0 {}
- | ^^^^^ help: try using the minimum value for the type: `MIN..0.0`
-
-error: `..=X` range patterns are not supported
- --> $DIR/recover-range-pats.rs:102:12
- |
-LL | if let ..=3 = 0 {}
- | ^^^^ help: try using the minimum value for the type: `MIN..=3`
-
-error: `..=X` range patterns are not supported
- --> $DIR/recover-range-pats.rs:103:12
- |
-LL | if let ..=Y = 0 {}
- | ^^^^ help: try using the minimum value for the type: `MIN..=Y`
-
-error: `..=X` range patterns are not supported
- --> $DIR/recover-range-pats.rs:104:12
- |
-LL | if let ..=true = 0 {}
- | ^^^^^^^ help: try using the minimum value for the type: `MIN..=true`
-
error: float literals must have an integer part
- --> $DIR/recover-range-pats.rs:106:15
+ --> $DIR/recover-range-pats.rs:103:15
|
LL | if let ..=.0 = 0 {}
| ^^ help: must have an integer part: `0.0`
-error: `..=X` range patterns are not supported
- --> $DIR/recover-range-pats.rs:106:12
- |
-LL | if let ..=.0 = 0 {}
- | ^^^^^ help: try using the minimum value for the type: `MIN..=0.0`
-
-error: `...X` range patterns are not supported
- --> $DIR/recover-range-pats.rs:112:12
+error: range-to patterns with `...` are not allowed
+ --> $DIR/recover-range-pats.rs:109:12
|
LL | if let ...3 = 0 {}
- | ^^^^ help: try using the minimum value for the type: `MIN...3`
+ | ^^^ help: use `..=` instead
-error: `...X` range patterns are not supported
- --> $DIR/recover-range-pats.rs:114:12
+error: range-to patterns with `...` are not allowed
+ --> $DIR/recover-range-pats.rs:111:12
|
LL | if let ...Y = 0 {}
- | ^^^^ help: try using the minimum value for the type: `MIN...Y`
+ | ^^^ help: use `..=` instead
-error: `...X` range patterns are not supported
- --> $DIR/recover-range-pats.rs:116:12
+error: range-to patterns with `...` are not allowed
+ --> $DIR/recover-range-pats.rs:113:12
|
LL | if let ...true = 0 {}
- | ^^^^^^^ help: try using the minimum value for the type: `MIN...true`
+ | ^^^ help: use `..=` instead
error: float literals must have an integer part
- --> $DIR/recover-range-pats.rs:119:15
+ --> $DIR/recover-range-pats.rs:116:15
|
LL | if let ....3 = 0 {}
| ^^ help: must have an integer part: `0.3`
-error: `...X` range patterns are not supported
- --> $DIR/recover-range-pats.rs:119:12
+error: range-to patterns with `...` are not allowed
+ --> $DIR/recover-range-pats.rs:116:12
|
LL | if let ....3 = 0 {}
- | ^^^^^ help: try using the minimum value for the type: `MIN...0.3`
-
-error: `..X` range patterns are not supported
- --> $DIR/recover-range-pats.rs:139:17
- |
-LL | let ..$e;
- | ^^ help: try using the minimum value for the type: `MIN..0`
-...
-LL | mac!(0);
- | -------- in this macro invocation
+ | ^^^ help: use `..=` instead
-error: `...X` range patterns are not supported
- --> $DIR/recover-range-pats.rs:140:17
+error: range-to patterns with `...` are not allowed
+ --> $DIR/recover-range-pats.rs:137:17
|
LL | let ...$e;
- | ^^^ help: try using the minimum value for the type: `MIN...0`
+ | ^^^ help: use `..=` instead
...
LL | mac!(0);
| -------- in this macro invocation
-error: `..=X` range patterns are not supported
- --> $DIR/recover-range-pats.rs:142:17
- |
-LL | let ..=$e;
- | ^^^ help: try using the minimum value for the type: `MIN..=0`
-...
-LL | mac!(0);
- | -------- in this macro invocation
-
-error: `X..` range patterns are not supported
- --> $DIR/recover-range-pats.rs:143:19
- |
-LL | let $e..;
- | ^^ help: try using the maximum value for the type: `0..MAX`
-...
-LL | mac!(0);
- | -------- in this macro invocation
-
-error: `X...` range patterns are not supported
- --> $DIR/recover-range-pats.rs:144:19
+error[E0586]: inclusive range with no end
+ --> $DIR/recover-range-pats.rs:141:19
|
LL | let $e...;
- | ^^^ help: try using the maximum value for the type: `0...MAX`
+ | ^^^ help: use `..` instead
...
LL | mac!(0);
| -------- in this macro invocation
+ |
+ = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
-error: `X..=` range patterns are not supported
- --> $DIR/recover-range-pats.rs:146:19
+error[E0586]: inclusive range with no end
+ --> $DIR/recover-range-pats.rs:142:19
|
LL | let $e..=;
- | ^^^ help: try using the maximum value for the type: `0..=MAX`
+ | ^^^ help: use `..` instead
...
LL | mac!(0);
| -------- in this macro invocation
+ |
+ = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
error: `...` range patterns are deprecated
- --> $DIR/recover-range-pats.rs:41:13
+ --> $DIR/recover-range-pats.rs:42:13
|
LL | if let 0...3 = 0 {}
| ^^^ help: use `..=` for an inclusive range
|
note: lint level defined here
- --> $DIR/recover-range-pats.rs:7:9
+ --> $DIR/recover-range-pats.rs:8:9
|
LL | #![deny(ellipsis_inclusive_range_patterns)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: `...` range patterns are deprecated
- --> $DIR/recover-range-pats.rs:42:13
+ --> $DIR/recover-range-pats.rs:43:13
|
LL | if let 0...Y = 0 {}
| ^^^ help: use `..=` for an inclusive range
error: `...` range patterns are deprecated
- --> $DIR/recover-range-pats.rs:43:13
+ --> $DIR/recover-range-pats.rs:44:13
|
LL | if let X...3 = 0 {}
| ^^^ help: use `..=` for an inclusive range
error: `...` range patterns are deprecated
- --> $DIR/recover-range-pats.rs:44:13
+ --> $DIR/recover-range-pats.rs:45:13
|
LL | if let X...Y = 0 {}
| ^^^ help: use `..=` for an inclusive range
error: `...` range patterns are deprecated
- --> $DIR/recover-range-pats.rs:45:16
+ --> $DIR/recover-range-pats.rs:46:16
|
LL | if let true...Y = 0 {}
| ^^^ help: use `..=` for an inclusive range
error: `...` range patterns are deprecated
- --> $DIR/recover-range-pats.rs:47:13
+ --> $DIR/recover-range-pats.rs:48:13
|
LL | if let X...true = 0 {}
| ^^^ help: use `..=` for an inclusive range
error: `...` range patterns are deprecated
- --> $DIR/recover-range-pats.rs:49:14
+ --> $DIR/recover-range-pats.rs:50:14
|
LL | if let .0...Y = 0 {}
| ^^^ help: use `..=` for an inclusive range
error: `...` range patterns are deprecated
- --> $DIR/recover-range-pats.rs:52:13
+ --> $DIR/recover-range-pats.rs:53:13
|
LL | if let X... .0 = 0 {}
| ^^^ help: use `..=` for an inclusive range
error: `...` range patterns are deprecated
- --> $DIR/recover-range-pats.rs:78:13
- |
-LL | if let 0... = 0 {}
- | ^^^ help: use `..=` for an inclusive range
-
-error: `...` range patterns are deprecated
- --> $DIR/recover-range-pats.rs:80:13
- |
-LL | if let X... = 0 {}
- | ^^^ help: use `..=` for an inclusive range
-
-error: `...` range patterns are deprecated
- --> $DIR/recover-range-pats.rs:82:16
- |
-LL | if let true... = 0 {}
- | ^^^ help: use `..=` for an inclusive range
-
-error: `...` range patterns are deprecated
- --> $DIR/recover-range-pats.rs:85:14
- |
-LL | if let .0... = 0 {}
- | ^^^ help: use `..=` for an inclusive range
-
-error: `...` range patterns are deprecated
- --> $DIR/recover-range-pats.rs:112:12
- |
-LL | if let ...3 = 0 {}
- | ^^^ help: use `..=` for an inclusive range
-
-error: `...` range patterns are deprecated
- --> $DIR/recover-range-pats.rs:114:12
- |
-LL | if let ...Y = 0 {}
- | ^^^ help: use `..=` for an inclusive range
-
-error: `...` range patterns are deprecated
- --> $DIR/recover-range-pats.rs:116:12
- |
-LL | if let ...true = 0 {}
- | ^^^ help: use `..=` for an inclusive range
-
-error: `...` range patterns are deprecated
- --> $DIR/recover-range-pats.rs:119:12
- |
-LL | if let ....3 = 0 {}
- | ^^^ help: use `..=` for an inclusive range
-
-error: `...` range patterns are deprecated
- --> $DIR/recover-range-pats.rs:129:20
+ --> $DIR/recover-range-pats.rs:126:20
|
LL | let $e1...$e2;
| ^^^ help: use `..=` for an inclusive range
LL | mac2!(0, 1);
| ------------ in this macro invocation
-error: `...` range patterns are deprecated
- --> $DIR/recover-range-pats.rs:140:17
- |
-LL | let ...$e;
- | ^^^ help: use `..=` for an inclusive range
-...
-LL | mac!(0);
- | -------- in this macro invocation
-
-error: `...` range patterns are deprecated
- --> $DIR/recover-range-pats.rs:144:19
- |
-LL | let $e...;
- | ^^^ help: use `..=` for an inclusive range
-...
-LL | mac!(0);
- | -------- in this macro invocation
-
error[E0029]: only char and numeric types are allowed in range patterns
- --> $DIR/recover-range-pats.rs:19:12
+ --> $DIR/recover-range-pats.rs:20:12
|
LL | if let true..Y = 0 {}
| ^^^^ - this is of type `u8`
| this is of type `bool` but it should be `char` or numeric
error[E0029]: only char and numeric types are allowed in range patterns
- --> $DIR/recover-range-pats.rs:20:15
+ --> $DIR/recover-range-pats.rs:21:15
|
LL | if let X..true = 0 {}
| - ^^^^ this is of type `bool` but it should be `char` or numeric
| this is of type `u8`
error[E0308]: mismatched types
- --> $DIR/recover-range-pats.rs:21:12
+ --> $DIR/recover-range-pats.rs:22:12
|
LL | if let .0..Y = 0 {}
| ^^ - this is of type `u8`
| expected integer, found floating-point number
error[E0308]: mismatched types
- --> $DIR/recover-range-pats.rs:23:16
+ --> $DIR/recover-range-pats.rs:24:16
|
LL | if let X.. .0 = 0 {}
| - ^^ - this expression has type `u8`
| this is of type `u8`
error[E0029]: only char and numeric types are allowed in range patterns
- --> $DIR/recover-range-pats.rs:32:12
+ --> $DIR/recover-range-pats.rs:33:12
|
LL | if let true..=Y = 0 {}
| ^^^^ - this is of type `u8`
| this is of type `bool` but it should be `char` or numeric
error[E0029]: only char and numeric types are allowed in range patterns
- --> $DIR/recover-range-pats.rs:33:16
+ --> $DIR/recover-range-pats.rs:34:16
|
LL | if let X..=true = 0 {}
| - ^^^^ this is of type `bool` but it should be `char` or numeric
| this is of type `u8`
error[E0308]: mismatched types
- --> $DIR/recover-range-pats.rs:34:12
+ --> $DIR/recover-range-pats.rs:35:12
|
LL | if let .0..=Y = 0 {}
| ^^ - this is of type `u8`
| expected integer, found floating-point number
error[E0308]: mismatched types
- --> $DIR/recover-range-pats.rs:36:16
+ --> $DIR/recover-range-pats.rs:37:16
|
LL | if let X..=.0 = 0 {}
| - ^^ - this expression has type `u8`
| this is of type `u8`
error[E0029]: only char and numeric types are allowed in range patterns
- --> $DIR/recover-range-pats.rs:45:12
+ --> $DIR/recover-range-pats.rs:46:12
|
LL | if let true...Y = 0 {}
| ^^^^ - this is of type `u8`
| this is of type `bool` but it should be `char` or numeric
error[E0029]: only char and numeric types are allowed in range patterns
- --> $DIR/recover-range-pats.rs:47:16
+ --> $DIR/recover-range-pats.rs:48:16
|
LL | if let X...true = 0 {}
| - ^^^^ this is of type `bool` but it should be `char` or numeric
| this is of type `u8`
error[E0308]: mismatched types
- --> $DIR/recover-range-pats.rs:49:12
+ --> $DIR/recover-range-pats.rs:50:12
|
LL | if let .0...Y = 0 {}
| ^^ - this is of type `u8`
| expected integer, found floating-point number
error[E0308]: mismatched types
- --> $DIR/recover-range-pats.rs:52:17
+ --> $DIR/recover-range-pats.rs:53:17
|
LL | if let X... .0 = 0 {}
| - ^^ - this expression has type `u8`
| this is of type `u8`
error[E0029]: only char and numeric types are allowed in range patterns
- --> $DIR/recover-range-pats.rs:60:12
+ --> $DIR/recover-range-pats.rs:61:12
|
LL | if let true.. = 0 {}
| ^^^^ this is of type `bool` but it should be `char` or numeric
error[E0308]: mismatched types
- --> $DIR/recover-range-pats.rs:62:12
+ --> $DIR/recover-range-pats.rs:63:12
|
LL | if let .0.. = 0 {}
| ^^ expected integer, found floating-point number
error[E0029]: only char and numeric types are allowed in range patterns
- --> $DIR/recover-range-pats.rs:70:12
+ --> $DIR/recover-range-pats.rs:71:12
|
LL | if let true..= = 0 {}
| ^^^^ this is of type `bool` but it should be `char` or numeric
error[E0308]: mismatched types
- --> $DIR/recover-range-pats.rs:72:12
+ --> $DIR/recover-range-pats.rs:73:12
|
LL | if let .0..= = 0 {}
| ^^ expected integer, found floating-point number
error[E0029]: only char and numeric types are allowed in range patterns
- --> $DIR/recover-range-pats.rs:82:12
+ --> $DIR/recover-range-pats.rs:81:12
|
LL | if let true... = 0 {}
| ^^^^ this is of type `bool` but it should be `char` or numeric
error[E0308]: mismatched types
- --> $DIR/recover-range-pats.rs:85:12
+ --> $DIR/recover-range-pats.rs:83:12
|
LL | if let .0... = 0 {}
| ^^ expected integer, found floating-point number
error[E0029]: only char and numeric types are allowed in range patterns
- --> $DIR/recover-range-pats.rs:94:14
+ --> $DIR/recover-range-pats.rs:91:14
|
LL | if let ..true = 0 {}
| ^^^^ this is of type `bool` but it should be `char` or numeric
error[E0308]: mismatched types
- --> $DIR/recover-range-pats.rs:96:15
+ --> $DIR/recover-range-pats.rs:93:15
|
LL | if let .. .0 = 0 {}
| ^^ expected integer, found floating-point number
error[E0029]: only char and numeric types are allowed in range patterns
- --> $DIR/recover-range-pats.rs:104:15
+ --> $DIR/recover-range-pats.rs:101:15
|
LL | if let ..=true = 0 {}
| ^^^^ this is of type `bool` but it should be `char` or numeric
error[E0308]: mismatched types
- --> $DIR/recover-range-pats.rs:106:15
+ --> $DIR/recover-range-pats.rs:103:15
|
LL | if let ..=.0 = 0 {}
| ^^ expected integer, found floating-point number
error[E0029]: only char and numeric types are allowed in range patterns
- --> $DIR/recover-range-pats.rs:116:15
+ --> $DIR/recover-range-pats.rs:113:15
|
LL | if let ...true = 0 {}
| ^^^^ this is of type `bool` but it should be `char` or numeric
error[E0308]: mismatched types
- --> $DIR/recover-range-pats.rs:119:15
+ --> $DIR/recover-range-pats.rs:116:15
|
LL | if let ....3 = 0 {}
| ^^ expected integer, found floating-point number
-error: aborting due to 85 previous errors
+error: aborting due to 60 previous errors
-Some errors have detailed explanations: E0029, E0308.
+Some errors have detailed explanations: E0029, E0308, E0586.
For more information about an error, try `rustc --explain E0029`.
fn main() {
false == false == false;
- //~^ ERROR chained comparison operators require parentheses
+ //~^ ERROR comparison operators cannot be chained
false == 0 < 2;
- //~^ ERROR chained comparison operators require parentheses
+ //~^ ERROR comparison operators cannot be chained
//~| ERROR mismatched types
//~| ERROR mismatched types
f<X>();
- //~^ ERROR chained comparison operators require parentheses
+ //~^ ERROR comparison operators cannot be chained
//~| HELP use `::<...>` instead of `<...>` to specify type arguments
f<Result<Option<X>, Option<Option<X>>>(1, 2);
- //~^ ERROR chained comparison operators require parentheses
+ //~^ ERROR comparison operators cannot be chained
+ //~| HELP split the comparison into two...
+ //~| ...or parenthesize one of the comparisons
//~| HELP use `::<...>` instead of `<...>` to specify type arguments
use std::convert::identity;
let _ = identity<u8>;
- //~^ ERROR chained comparison operators require parentheses
+ //~^ ERROR comparison operators cannot be chained
//~| HELP use `::<...>` instead of `<...>` to specify type arguments
//~| HELP or use `(...)` if you meant to specify fn arguments
}
-error: chained comparison operators require parentheses
+error: comparison operators cannot be chained
--> $DIR/require-parens-for-chained-comparison.rs:5:11
|
LL | false == false == false;
| ^^^^^^^^^^^
-error: chained comparison operators require parentheses
+error: comparison operators cannot be chained
--> $DIR/require-parens-for-chained-comparison.rs:8:11
|
LL | false == 0 < 2;
| ^^^^^^
-error: chained comparison operators require parentheses
+error: comparison operators cannot be chained
--> $DIR/require-parens-for-chained-comparison.rs:13:6
|
LL | f<X>();
LL | f::<X>();
| ^^
-error: chained comparison operators require parentheses
+error: comparison operators cannot be chained
--> $DIR/require-parens-for-chained-comparison.rs:17:6
|
LL | f<Result<Option<X>, Option<Option<X>>>(1, 2);
| ^^^^^^^^
|
+help: split the comparison into two...
+ |
+LL | f < Result && Result <Option<X>, Option<Option<X>>>(1, 2);
+ | ^^^^^^^^^^^^^^^^^^^^^^
+help: ...or parenthesize one of the comparisons
+ |
+LL | (f < Result) <Option<X>, Option<Option<X>>>(1, 2);
+ | ^^^^^^^^^^^^^^
help: use `::<...>` instead of `<...>` to specify type arguments
|
LL | f::<Result<Option<X>, Option<Option<X>>>(1, 2);
| ^^
-error: chained comparison operators require parentheses
- --> $DIR/require-parens-for-chained-comparison.rs:22:21
+error: comparison operators cannot be chained
+ --> $DIR/require-parens-for-chained-comparison.rs:24:21
|
LL | let _ = identity<u8>;
| ^^^^
-// compile-flags: -Z continue-parse-after-error
-
struct Foo {
x: isize,
y: isize,
error: field expressions may not have generic arguments
- --> $DIR/type-parameters-in-field-exprs.rs:13:10
+ --> $DIR/type-parameters-in-field-exprs.rs:11:10
|
LL | f.x::<isize>;
| ^^^^^^^
error: field expressions may not have generic arguments
- --> $DIR/type-parameters-in-field-exprs.rs:15:10
+ --> $DIR/type-parameters-in-field-exprs.rs:13:10
|
LL | f.x::<>;
| ^^
error: field expressions may not have generic arguments
- --> $DIR/type-parameters-in-field-exprs.rs:17:7
+ --> $DIR/type-parameters-in-field-exprs.rs:15:7
|
LL | f.x::();
| ^^^^^
//~^ ERROR cannot borrow `a` as mutable because it is also borrowed as immutable
//~| ERROR cannot borrow `a` as mutable because it is also borrowed as immutable
//~| ERROR cannot move out of `b` in pattern guard
+ //~| ERROR cannot move out of `b` in pattern guard
_ => {}
}
match Ok(U) {
//~^ ERROR cannot borrow `a` as immutable because it is also borrowed as mutable
//~| ERROR cannot borrow `a` as immutable because it is also borrowed as mutable
//~| ERROR cannot move out of `a` in pattern guard
+ //~| ERROR cannot move out of `a` in pattern guard
_ => {}
}
| immutable borrow occurs here
error: cannot borrow `a` as immutable because it is also borrowed as mutable
- --> $DIR/borrowck-pat-ref-mut-and-ref.rs:107:9
+ --> $DIR/borrowck-pat-ref-mut-and-ref.rs:108:9
|
LL | ref mut a @ Ok(ref b) | ref mut a @ Err(ref b) if { drop(a); false } => {}
| ---------^^^^^^-----^
| mutable borrow occurs here
error: cannot borrow `a` as immutable because it is also borrowed as mutable
- --> $DIR/borrowck-pat-ref-mut-and-ref.rs:107:33
+ --> $DIR/borrowck-pat-ref-mut-and-ref.rs:108:33
|
LL | ref mut a @ Ok(ref b) | ref mut a @ Err(ref b) if { drop(a); false } => {}
| ---------^^^^^^^-----^
| mutable borrow occurs here
error: cannot borrow `a` as mutable because it is also borrowed as immutable
- --> $DIR/borrowck-pat-ref-mut-and-ref.rs:114:9
+ --> $DIR/borrowck-pat-ref-mut-and-ref.rs:116:9
|
LL | let ref a @ (ref mut b, ref mut c) = (U, U);
| -----^^^^---------^^---------^
| immutable borrow occurs here
error: cannot borrow `a` as mutable because it is also borrowed as immutable
- --> $DIR/borrowck-pat-ref-mut-and-ref.rs:119:9
+ --> $DIR/borrowck-pat-ref-mut-and-ref.rs:121:9
|
LL | let ref a @ (ref mut b, ref mut c) = (U, U);
| -----^^^^---------^^---------^
| immutable borrow occurs here
error: cannot borrow `a` as mutable because it is also borrowed as immutable
- --> $DIR/borrowck-pat-ref-mut-and-ref.rs:126:9
+ --> $DIR/borrowck-pat-ref-mut-and-ref.rs:128:9
|
LL | let ref a @ (ref mut b, ref mut c) = (U, U);
| -----^^^^---------^^---------^
| immutable borrow occurs here
error: cannot borrow `a` as immutable because it is also borrowed as mutable
- --> $DIR/borrowck-pat-ref-mut-and-ref.rs:131:9
+ --> $DIR/borrowck-pat-ref-mut-and-ref.rs:133:9
|
LL | let ref mut a @ (ref b, ref c) = (U, U);
| ---------^^^^-----^^-----^
|
= note: variables bound in patterns cannot be moved from until after the end of the pattern guard
+error[E0507]: cannot move out of `b` in pattern guard
+ --> $DIR/borrowck-pat-ref-mut-and-ref.rs:100:66
+ |
+LL | ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) if { drop(b); false } => {}
+ | ^ move occurs because `b` has type `&mut main::U`, which does not implement the `Copy` trait
+ |
+ = note: variables bound in patterns cannot be moved from until after the end of the pattern guard
+
+error[E0507]: cannot move out of `a` in pattern guard
+ --> $DIR/borrowck-pat-ref-mut-and-ref.rs:108:66
+ |
+LL | ref mut a @ Ok(ref b) | ref mut a @ Err(ref b) if { drop(a); false } => {}
+ | ^ move occurs because `a` has type `&mut std::result::Result<main::U, main::U>`, which does not implement the `Copy` trait
+ |
+ = note: variables bound in patterns cannot be moved from until after the end of the pattern guard
+
error[E0507]: cannot move out of `a` in pattern guard
- --> $DIR/borrowck-pat-ref-mut-and-ref.rs:107:66
+ --> $DIR/borrowck-pat-ref-mut-and-ref.rs:108:66
|
LL | ref mut a @ Ok(ref b) | ref mut a @ Err(ref b) if { drop(a); false } => {}
| ^ move occurs because `a` has type `&mut std::result::Result<main::U, main::U>`, which does not implement the `Copy` trait
= note: variables bound in patterns cannot be moved from until after the end of the pattern guard
error[E0502]: cannot borrow `_` as mutable because it is also borrowed as immutable
- --> $DIR/borrowck-pat-ref-mut-and-ref.rs:119:18
+ --> $DIR/borrowck-pat-ref-mut-and-ref.rs:121:18
|
LL | let ref a @ (ref mut b, ref mut c) = (U, U);
| ---------^^^^^^^^^------------
| - immutable borrow later used here
error[E0502]: cannot borrow `_` as mutable because it is also borrowed as immutable
- --> $DIR/borrowck-pat-ref-mut-and-ref.rs:119:29
+ --> $DIR/borrowck-pat-ref-mut-and-ref.rs:121:29
|
LL | let ref a @ (ref mut b, ref mut c) = (U, U);
| --------------------^^^^^^^^^-
| - immutable borrow later used here
error[E0502]: cannot borrow `_` as mutable because it is also borrowed as immutable
- --> $DIR/borrowck-pat-ref-mut-and-ref.rs:126:18
+ --> $DIR/borrowck-pat-ref-mut-and-ref.rs:128:18
|
LL | let ref a @ (ref mut b, ref mut c) = (U, U);
| ---------^^^^^^^^^------------
| - immutable borrow later used here
error[E0502]: cannot borrow `_` as mutable because it is also borrowed as immutable
- --> $DIR/borrowck-pat-ref-mut-and-ref.rs:126:29
+ --> $DIR/borrowck-pat-ref-mut-and-ref.rs:128:29
|
LL | let ref a @ (ref mut b, ref mut c) = (U, U);
| --------------------^^^^^^^^^-
LL | drop(a);
| - immutable borrow later used here
-error: aborting due to 43 previous errors
+error: aborting due to 45 previous errors
Some errors have detailed explanations: E0502, E0507, E0594.
For more information about an error, try `rustc --explain E0502`.
-thread 'rustc' panicked at 'assertion failed: rows.iter().all(|r| r.len() == v.len())', src/librustc_mir/hair/pattern/_match.rs:LL:CC
+thread 'rustc' panicked at 'assertion failed: rows.iter().all(|r| r.len() == v.len())', src/librustc_mir_build/hair/pattern/_match.rs:LL:CC
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
error: internal compiler error: unexpected panic
|
LL | Bar => {},
| ^^^ help: to match on the variant, qualify the path: `Foo::Bar`
+ |
+ = note: `#[warn(bindings_with_variant_name)]` on by default
warning[E0170]: pattern binding `Baz` is named the same as one of the variants of the type `Foo`
--> $DIR/issue-67776-match-same-name-enum-variant-refs.rs:19:9
Pos = 1,
Neg = -1,
Arith = 1 + 1, //~ ERROR arbitrary expressions aren't allowed in patterns
- //~^ ERROR only char and numeric types are allowed in range patterns
+ //~| ERROR arbitrary expressions aren't allowed in patterns
+ //~| ERROR only char and numeric types are allowed in range patterns
});
fn main() {}
LL | Arith = 1 + 1,
| ^^^^^
+error: arbitrary expressions aren't allowed in patterns
+ --> $DIR/patkind-litrange-no-expr.rs:20:13
+ |
+LL | Arith = 1 + 1,
+ | ^^^^^
+
error[E0029]: only char and numeric types are allowed in range patterns
--> $DIR/patkind-litrange-no-expr.rs:20:13
|
LL | Arith = 1 + 1,
| ^^^^^ this is of type `_` but it should be `char` or numeric
-error: aborting due to 2 previous errors
+error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0029`.
//~^ WARNING floating-point types cannot be used in patterns
//~| WARNING floating-point types cannot be used in patterns
//~| WARNING floating-point types cannot be used in patterns
+ //~| WARNING floating-point types cannot be used in patterns
+ //~| WARNING this was previously accepted by the compiler
//~| WARNING this was previously accepted by the compiler
//~| WARNING this was previously accepted by the compiler
//~| WARNING this was previously accepted by the compiler
0.02f64 => {} //~ ERROR unreachable pattern
//~^ WARNING floating-point types cannot be used in patterns
+ //~| WARNING floating-point types cannot be used in patterns
+ //~| WARNING this was previously accepted by the compiler
//~| WARNING this was previously accepted by the compiler
_ => {}
};
= note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
warning: floating-point types cannot be used in patterns
- --> $DIR/match-range-fail-dominate.rs:40:7
+ --> $DIR/match-range-fail-dominate.rs:42:7
|
LL | 0.02f64 => {}
| ^^^^^^^
= note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
error: unreachable pattern
- --> $DIR/match-range-fail-dominate.rs:40:7
+ --> $DIR/match-range-fail-dominate.rs:42:7
|
LL | 0.02f64 => {}
| ^^^^^^^
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
+warning: floating-point types cannot be used in patterns
+ --> $DIR/match-range-fail-dominate.rs:33:19
+ |
+LL | 0.01f64 ..= 6.5f64 => {}
+ | ^^^^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
+
+warning: floating-point types cannot be used in patterns
+ --> $DIR/match-range-fail-dominate.rs:42:7
+ |
+LL | 0.02f64 => {}
+ | ^^^^^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
+
error: aborting due to 5 previous errors
fn test2() {
use bar::baz::{foo, bar};
//~^ ERROR: module `baz` is private
+ //~| ERROR: module `baz` is private
foo();
bar();
| ^^^
error[E0603]: module `baz` is private
- --> $DIR/privacy1.rs:140:18
+ --> $DIR/privacy1.rs:132:18
+ |
+LL | use bar::baz::{foo, bar};
+ | ^^^
+
+error[E0603]: module `baz` is private
+ --> $DIR/privacy1.rs:141:18
|
LL | use bar::baz;
| ^^^
error[E0603]: module `i` is private
- --> $DIR/privacy1.rs:164:20
+ --> $DIR/privacy1.rs:165:20
|
LL | use self::foo::i::A;
| ^
| ^^^
error[E0603]: trait `B` is private
- --> $DIR/privacy1.rs:156:17
+ --> $DIR/privacy1.rs:157:17
|
LL | impl ::bar::B for f32 { fn foo() -> f32 { 1.0 } }
| ^
LL | ::bar::baz::A.bar2();
| ^^^^
-error: aborting due to 17 previous errors
+error: aborting due to 18 previous errors
Some errors have detailed explanations: E0603, E0624.
For more information about an error, try `rustc --explain E0603`.
//~^ WARN private trait `m::PrivTr` in public interface
//~| WARN this was previously accepted
//~| WARN private type `m::Priv` in public interface
+ //~| WARN private type `m::Priv` in public interface
+ //~| WARN this was previously accepted
//~| WARN this was previously accepted
type Alias1: PrivTr;
type Alias2: PubTrAux1<Priv> = u8;
type Exist = impl PrivTr;
//~^ ERROR private trait `m::PrivTr` in public interface
+ //~| ERROR private trait `m::PrivTr` in public interface
fn infer_exist() -> Self::Exist { Priv }
}
}
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #34537 <https://github.com/rust-lang/rust/issues/34537>
+warning: private type `m::Priv` in public interface (error E0446)
+ --> $DIR/private-in-public-assoc-ty.rs:16:5
+ |
+LL | / pub trait PubTr {
+LL | |
+LL | |
+LL | |
+... |
+LL | | fn infer_exist() -> Self::Exist;
+LL | | }
+ | |_____^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #34537 <https://github.com/rust-lang/rust/issues/34537>
+
error[E0446]: private type `m::Priv` in public interface
- --> $DIR/private-in-public-assoc-ty.rs:25:9
+ --> $DIR/private-in-public-assoc-ty.rs:27:9
|
LL | struct Priv;
| - `m::Priv` declared as private
| ^^^^^^^^^^^^^^^^^^^ can't leak private type
error[E0446]: private type `m::Priv` in public interface
- --> $DIR/private-in-public-assoc-ty.rs:32:9
+ --> $DIR/private-in-public-assoc-ty.rs:34:9
|
LL | struct Priv;
| - `m::Priv` declared as private
| ^^^^^^^^^^^^^^^^^^^ can't leak private type
error[E0445]: private trait `m::PrivTr` in public interface
- --> $DIR/private-in-public-assoc-ty.rs:35:9
+ --> $DIR/private-in-public-assoc-ty.rs:37:9
+ |
+LL | trait PrivTr {}
+ | - `m::PrivTr` declared as private
+...
+LL | type Exist = impl PrivTr;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private trait
+
+error[E0445]: private trait `m::PrivTr` in public interface
+ --> $DIR/private-in-public-assoc-ty.rs:37:9
|
LL | trait PrivTr {}
| - `m::PrivTr` declared as private
LL | type Exist = impl PrivTr;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private trait
-error: aborting due to 3 previous errors
+error: aborting due to 4 previous errors
Some errors have detailed explanations: E0445, E0446.
For more information about an error, try `rustc --explain E0445`.
extern crate issue_50493;
#[derive(Derive)] //~ ERROR field `field` of struct `Restricted` is private
+ //~| ERROR field `field` of struct `Restricted` is private
struct Restricted {
pub(in restricted) field: usize, //~ visibilities can only be restricted to ancestor modules
}
error[E0742]: visibilities can only be restricted to ancestor modules
- --> $DIR/issue-50493.rs:8:12
+ --> $DIR/issue-50493.rs:9:12
|
LL | pub(in restricted) field: usize,
| ^^^^^^^^^^
LL | #[derive(Derive)]
| ^^^^^^
-error: aborting due to 2 previous errors
+error[E0616]: field `field` of struct `Restricted` is private
+ --> $DIR/issue-50493.rs:6:10
+ |
+LL | #[derive(Derive)]
+ | ^^^^^^
+
+error: aborting due to 3 previous errors
Some errors have detailed explanations: E0616, E0742.
For more information about an error, try `rustc --explain E0616`.
fn check_attr3() {}
#[derive(my_macro)] //~ ERROR cannot find derive macro `my_macro` in this scope
+ //~| ERROR cannot find derive macro `my_macro` in this scope
#[derive(crate::my_macro)] //~ ERROR can't use a procedural macro from the same crate that defines
//~| ERROR expected derive macro, found macro `crate::my_macro`
struct CheckDerive1;
| ^^^^^^^ not an attribute
error: can't use a procedural macro from the same crate that defines it
- --> $DIR/macro-namespace-reserved-2.rs:52:10
+ --> $DIR/macro-namespace-reserved-2.rs:53:10
|
LL | #[derive(my_macro_attr)]
| ^^^^^^^^^^^^^
error: expected derive macro, found attribute macro `my_macro_attr`
- --> $DIR/macro-namespace-reserved-2.rs:52:10
+ --> $DIR/macro-namespace-reserved-2.rs:53:10
|
LL | #[derive(my_macro_attr)]
| ^^^^^^^^^^^^^ not a derive macro
error: can't use a procedural macro from the same crate that defines it
- --> $DIR/macro-namespace-reserved-2.rs:55:10
+ --> $DIR/macro-namespace-reserved-2.rs:56:10
|
LL | #[derive(MyTrait)]
| ^^^^^^^
| ^^^^^^^^^^^^^^^ not an attribute
error: can't use a procedural macro from the same crate that defines it
- --> $DIR/macro-namespace-reserved-2.rs:49:10
+ --> $DIR/macro-namespace-reserved-2.rs:50:10
|
LL | #[derive(crate::my_macro)]
| ^^^^^^^^^^^^^^^
error: expected derive macro, found macro `crate::my_macro`
- --> $DIR/macro-namespace-reserved-2.rs:49:10
+ --> $DIR/macro-namespace-reserved-2.rs:50:10
|
LL | #[derive(crate::my_macro)]
| ^^^^^^^^^^^^^^^ not a derive macro
LL | #[derive(my_macro)]
| ^^^^^^^^
-error: aborting due to 19 previous errors
+error: cannot find derive macro `my_macro` in this scope
+ --> $DIR/macro-namespace-reserved-2.rs:48:10
+ |
+LL | #[derive(my_macro)]
+ | ^^^^^^^^
+
+error: aborting due to 20 previous errors
+// FIXME: missing sysroot spans (#53081)
+// ignore-i586-unknown-linux-gnu
+// ignore-i586-unknown-linux-musl
+// ignore-i686-unknown-linux-musl
// aux-build:parent-source-spans.rs
#![feature(decl_macro, proc_macro_hygiene)]
error: first final: "hello"
- --> $DIR/parent-source-spans.rs:15:12
+ --> $DIR/parent-source-spans.rs:19:12
|
LL | three!($a, $b);
| ^^
| ----------------------- in this macro invocation
error: second final: "world"
- --> $DIR/parent-source-spans.rs:15:16
+ --> $DIR/parent-source-spans.rs:19:16
|
LL | three!($a, $b);
| ^^
| ----------------------- in this macro invocation
error: first parent: "hello"
- --> $DIR/parent-source-spans.rs:9:5
+ --> $DIR/parent-source-spans.rs:13:5
|
LL | two!($a, $b);
| ^^^^^^^^^^^^^
| ----------------------- in this macro invocation
error: second parent: "world"
- --> $DIR/parent-source-spans.rs:9:5
+ --> $DIR/parent-source-spans.rs:13:5
|
LL | two!($a, $b);
| ^^^^^^^^^^^^^
| ----------------------- in this macro invocation
error: first grandparent: "hello"
- --> $DIR/parent-source-spans.rs:35:5
+ --> $DIR/parent-source-spans.rs:39:5
|
LL | one!("hello", "world");
| ^^^^^^^^^^^^^^^^^^^^^^^
error: second grandparent: "world"
- --> $DIR/parent-source-spans.rs:35:5
+ --> $DIR/parent-source-spans.rs:39:5
|
LL | one!("hello", "world");
| ^^^^^^^^^^^^^^^^^^^^^^^
error: first source: "hello"
- --> $DIR/parent-source-spans.rs:35:5
+ --> $DIR/parent-source-spans.rs:39:5
|
LL | one!("hello", "world");
| ^^^^^^^^^^^^^^^^^^^^^^^
error: second source: "world"
- --> $DIR/parent-source-spans.rs:35:5
+ --> $DIR/parent-source-spans.rs:39:5
|
LL | one!("hello", "world");
| ^^^^^^^^^^^^^^^^^^^^^^^
error: first final: "yay"
- --> $DIR/parent-source-spans.rs:15:12
+ --> $DIR/parent-source-spans.rs:19:12
|
LL | three!($a, $b);
| ^^
| -------------------- in this macro invocation
error: second final: "rust"
- --> $DIR/parent-source-spans.rs:15:16
+ --> $DIR/parent-source-spans.rs:19:16
|
LL | three!($a, $b);
| ^^
| -------------------- in this macro invocation
error: first parent: "yay"
- --> $DIR/parent-source-spans.rs:41:5
+ --> $DIR/parent-source-spans.rs:45:5
|
LL | two!("yay", "rust");
| ^^^^^^^^^^^^^^^^^^^^
error: second parent: "rust"
- --> $DIR/parent-source-spans.rs:41:5
+ --> $DIR/parent-source-spans.rs:45:5
|
LL | two!("yay", "rust");
| ^^^^^^^^^^^^^^^^^^^^
error: first source: "yay"
- --> $DIR/parent-source-spans.rs:41:5
+ --> $DIR/parent-source-spans.rs:45:5
|
LL | two!("yay", "rust");
| ^^^^^^^^^^^^^^^^^^^^
error: second source: "rust"
- --> $DIR/parent-source-spans.rs:41:5
+ --> $DIR/parent-source-spans.rs:45:5
|
LL | two!("yay", "rust");
| ^^^^^^^^^^^^^^^^^^^^
error: first final: "hip"
- --> $DIR/parent-source-spans.rs:47:12
+ --> $DIR/parent-source-spans.rs:51:12
|
LL | three!("hip", "hop");
| ^^^^^
error: second final: "hop"
- --> $DIR/parent-source-spans.rs:47:19
+ --> $DIR/parent-source-spans.rs:51:19
|
LL | three!("hip", "hop");
| ^^^^^
error: first source: "hip"
- --> $DIR/parent-source-spans.rs:47:12
+ --> $DIR/parent-source-spans.rs:51:12
|
LL | three!("hip", "hop");
| ^^^^^
error: second source: "hop"
- --> $DIR/parent-source-spans.rs:47:19
+ --> $DIR/parent-source-spans.rs:51:19
|
LL | three!("hip", "hop");
| ^^^^^
error[E0425]: cannot find value `ok` in this scope
- --> $DIR/parent-source-spans.rs:28:5
+ --> $DIR/parent-source-spans.rs:32:5
|
LL | parent_source_spans!($($tokens)*);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: a tuple variant with a similar name exists: `Ok`
...
LL | one!("hello", "world");
| ----------------------- in this macro invocation
+ |
+ ::: $SRC_DIR/libcore/result.rs:LL:COL
+ |
+LL | Ok(#[stable(feature = "rust1", since = "1.0.0")] T),
+ | --------------------------------------------------- similarly named tuple variant `Ok` defined here
error[E0425]: cannot find value `ok` in this scope
- --> $DIR/parent-source-spans.rs:28:5
+ --> $DIR/parent-source-spans.rs:32:5
|
LL | parent_source_spans!($($tokens)*);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: a tuple variant with a similar name exists: `Ok`
...
LL | two!("yay", "rust");
| -------------------- in this macro invocation
+ |
+ ::: $SRC_DIR/libcore/result.rs:LL:COL
+ |
+LL | Ok(#[stable(feature = "rust1", since = "1.0.0")] T),
+ | --------------------------------------------------- similarly named tuple variant `Ok` defined here
error[E0425]: cannot find value `ok` in this scope
- --> $DIR/parent-source-spans.rs:28:5
+ --> $DIR/parent-source-spans.rs:32:5
|
LL | parent_source_spans!($($tokens)*);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: a tuple variant with a similar name exists: `Ok`
...
LL | three!("hip", "hop");
| --------------------- in this macro invocation
+ |
+ ::: $SRC_DIR/libcore/result.rs:LL:COL
+ |
+LL | Ok(#[stable(feature = "rust1", since = "1.0.0")] T),
+ | --------------------------------------------------- similarly named tuple variant `Ok` defined here
error: aborting due to 21 previous errors
+// FIXME: missing sysroot spans (#53081)
+// ignore-i586-unknown-linux-gnu
+// ignore-i586-unknown-linux-musl
+// ignore-i686-unknown-linux-musl
// aux-build:derive-foo.rs
// aux-build:derive-clona.rs
// aux-build:test-macros.rs
#[derive(FooWithLongNan)]
//~^ ERROR cannot find
+//~| ERROR cannot find
struct Foo;
// Interpreted as an unstable custom attribute
#[derive(Dlone)]
//~^ ERROR cannot find
+//~| ERROR cannot find
struct A;
#[derive(Dlona)]
//~^ ERROR cannot find
+//~| ERROR cannot find
struct B;
#[derive(attr_proc_macra)]
//~^ ERROR cannot find
+//~| ERROR cannot find
struct C;
fn main() {
error: cannot find macro `bang_proc_macrp` in this scope
- --> $DIR/resolve-error.rs:56:5
+ --> $DIR/resolve-error.rs:64:5
|
LL | bang_proc_macrp!();
| ^^^^^^^^^^^^^^^ help: a macro with a similar name exists: `bang_proc_macro`
+ |
+ ::: $DIR/auxiliary/test-macros.rs:15:1
+ |
+LL | pub fn empty(_: TokenStream) -> TokenStream {
+ | ------------------------------------------- similarly named macro `bang_proc_macro` defined here
error: cannot find macro `Dlona` in this scope
- --> $DIR/resolve-error.rs:53:5
+ --> $DIR/resolve-error.rs:61:5
|
LL | Dlona!();
| ^^^^^
error: cannot find macro `attr_proc_macra` in this scope
- --> $DIR/resolve-error.rs:50:5
+ --> $DIR/resolve-error.rs:58:5
|
LL | / macro_rules! attr_proc_mac {
LL | | () => {}
| ^^^^^^^^^^^^^^^ help: a macro with a similar name exists: `attr_proc_mac`
error: cannot find macro `FooWithLongNama` in this scope
- --> $DIR/resolve-error.rs:47:5
+ --> $DIR/resolve-error.rs:55:5
|
LL | / macro_rules! FooWithLongNam {
LL | | () => {}
| ^^^^^^^^^^^^^^^ help: a macro with a similar name exists: `FooWithLongNam`
error: cannot find derive macro `attr_proc_macra` in this scope
- --> $DIR/resolve-error.rs:42:10
+ --> $DIR/resolve-error.rs:49:10
|
LL | #[derive(attr_proc_macra)]
| ^^^^^^^^^^^^^^^
+error: cannot find derive macro `attr_proc_macra` in this scope
+ --> $DIR/resolve-error.rs:49:10
+ |
+LL | #[derive(attr_proc_macra)]
+ | ^^^^^^^^^^^^^^^
+
+error: cannot find derive macro `Dlona` in this scope
+ --> $DIR/resolve-error.rs:44:10
+ |
+LL | #[derive(Dlona)]
+ | ^^^^^ help: a derive macro with a similar name exists: `Clona`
+ |
+ ::: $DIR/auxiliary/derive-clona.rs:11:1
+ |
+LL | pub fn derive_clonea(input: TokenStream) -> TokenStream {
+ | ------------------------------------------------------- similarly named derive macro `Clona` defined here
+
error: cannot find derive macro `Dlona` in this scope
- --> $DIR/resolve-error.rs:38:10
+ --> $DIR/resolve-error.rs:44:10
|
LL | #[derive(Dlona)]
| ^^^^^ help: a derive macro with a similar name exists: `Clona`
+ |
+ ::: $DIR/auxiliary/derive-clona.rs:11:1
+ |
+LL | pub fn derive_clonea(input: TokenStream) -> TokenStream {
+ | ------------------------------------------------------- similarly named derive macro `Clona` defined here
+
+error: cannot find derive macro `Dlone` in this scope
+ --> $DIR/resolve-error.rs:39:10
+ |
+LL | #[derive(Dlone)]
+ | ^^^^^ help: a derive macro with a similar name exists: `Clone`
+ |
+ ::: $SRC_DIR/libcore/clone.rs:LL:COL
+ |
+LL | pub macro Clone($item:item) {
+ | --------------------------- similarly named derive macro `Clone` defined here
error: cannot find derive macro `Dlone` in this scope
- --> $DIR/resolve-error.rs:34:10
+ --> $DIR/resolve-error.rs:39:10
|
LL | #[derive(Dlone)]
| ^^^^^ help: a derive macro with a similar name exists: `Clone`
+ |
+ ::: $SRC_DIR/libcore/clone.rs:LL:COL
+ |
+LL | pub macro Clone($item:item) {
+ | --------------------------- similarly named derive macro `Clone` defined here
error: cannot find attribute `FooWithLongNan` in this scope
- --> $DIR/resolve-error.rs:31:3
+ --> $DIR/resolve-error.rs:36:3
|
LL | #[FooWithLongNan]
| ^^^^^^^^^^^^^^
error: cannot find attribute `attr_proc_macra` in this scope
- --> $DIR/resolve-error.rs:27:3
+ --> $DIR/resolve-error.rs:32:3
|
LL | #[attr_proc_macra]
| ^^^^^^^^^^^^^^^ help: an attribute macro with a similar name exists: `attr_proc_macro`
+ |
+ ::: $DIR/auxiliary/test-macros.rs:20:1
+ |
+LL | pub fn empty_attr(_: TokenStream, _: TokenStream) -> TokenStream {
+ | ---------------------------------------------------------------- similarly named attribute macro `attr_proc_macro` defined here
error: cannot find derive macro `FooWithLongNan` in this scope
- --> $DIR/resolve-error.rs:22:10
+ --> $DIR/resolve-error.rs:26:10
|
LL | #[derive(FooWithLongNan)]
| ^^^^^^^^^^^^^^ help: a derive macro with a similar name exists: `FooWithLongName`
+ |
+ ::: $DIR/auxiliary/derive-foo.rs:11:1
+ |
+LL | pub fn derive_foo(input: TokenStream) -> TokenStream {
+ | ---------------------------------------------------- similarly named derive macro `FooWithLongName` defined here
+
+error: cannot find derive macro `FooWithLongNan` in this scope
+ --> $DIR/resolve-error.rs:26:10
+ |
+LL | #[derive(FooWithLongNan)]
+ | ^^^^^^^^^^^^^^ help: a derive macro with a similar name exists: `FooWithLongName`
+ |
+ ::: $DIR/auxiliary/derive-foo.rs:11:1
+ |
+LL | pub fn derive_foo(input: TokenStream) -> TokenStream {
+ | ---------------------------------------------------- similarly named derive macro `FooWithLongName` defined here
-error: aborting due to 10 previous errors
+error: aborting due to 14 previous errors
struct AllTheRanges {
a: Range<usize>,
//~^ ERROR can't compare
+ //~| ERROR can't compare
+ //~| ERROR can't compare
+ //~| ERROR can't compare
+ //~| ERROR can't compare
//~| ERROR Ord
b: RangeTo<usize>,
//~^ ERROR can't compare
+ //~| ERROR can't compare
+ //~| ERROR can't compare
+ //~| ERROR can't compare
+ //~| ERROR can't compare
//~| ERROR Ord
c: RangeFrom<usize>,
//~^ ERROR can't compare
+ //~| ERROR can't compare
+ //~| ERROR can't compare
+ //~| ERROR can't compare
+ //~| ERROR can't compare
//~| ERROR Ord
d: RangeFull,
//~^ ERROR can't compare
+ //~| ERROR can't compare
+ //~| ERROR can't compare
+ //~| ERROR can't compare
+ //~| ERROR can't compare
//~| ERROR Ord
e: RangeInclusive<usize>,
//~^ ERROR can't compare
+ //~| ERROR can't compare
+ //~| ERROR can't compare
+ //~| ERROR can't compare
+ //~| ERROR can't compare
//~| ERROR Ord
f: RangeToInclusive<usize>,
//~^ ERROR can't compare
+ //~| ERROR can't compare
+ //~| ERROR can't compare
+ //~| ERROR can't compare
+ //~| ERROR can't compare
//~| ERROR Ord
}
= note: required by `std::cmp::PartialOrd::partial_cmp`
error[E0277]: can't compare `std::ops::RangeTo<usize>` with `std::ops::RangeTo<usize>`
- --> $DIR/range_traits-1.rs:8:5
+ --> $DIR/range_traits-1.rs:12:5
|
LL | b: RangeTo<usize>,
| ^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeTo<usize> < std::ops::RangeTo<usize>` and `std::ops::RangeTo<usize> > std::ops::RangeTo<usize>`
= note: required by `std::cmp::PartialOrd::partial_cmp`
error[E0277]: can't compare `std::ops::RangeFrom<usize>` with `std::ops::RangeFrom<usize>`
- --> $DIR/range_traits-1.rs:11:5
+ --> $DIR/range_traits-1.rs:19:5
|
LL | c: RangeFrom<usize>,
| ^^^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeFrom<usize> < std::ops::RangeFrom<usize>` and `std::ops::RangeFrom<usize> > std::ops::RangeFrom<usize>`
= note: required by `std::cmp::PartialOrd::partial_cmp`
error[E0277]: can't compare `std::ops::RangeFull` with `std::ops::RangeFull`
- --> $DIR/range_traits-1.rs:14:5
+ --> $DIR/range_traits-1.rs:26:5
|
LL | d: RangeFull,
| ^^^^^^^^^^^^ no implementation for `std::ops::RangeFull < std::ops::RangeFull` and `std::ops::RangeFull > std::ops::RangeFull`
= note: required by `std::cmp::PartialOrd::partial_cmp`
error[E0277]: can't compare `std::ops::RangeInclusive<usize>` with `std::ops::RangeInclusive<usize>`
- --> $DIR/range_traits-1.rs:17:5
+ --> $DIR/range_traits-1.rs:33:5
|
LL | e: RangeInclusive<usize>,
| ^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeInclusive<usize> < std::ops::RangeInclusive<usize>` and `std::ops::RangeInclusive<usize> > std::ops::RangeInclusive<usize>`
= note: required by `std::cmp::PartialOrd::partial_cmp`
error[E0277]: can't compare `std::ops::RangeToInclusive<usize>` with `std::ops::RangeToInclusive<usize>`
- --> $DIR/range_traits-1.rs:20:5
+ --> $DIR/range_traits-1.rs:40:5
+ |
+LL | f: RangeToInclusive<usize>,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeToInclusive<usize> < std::ops::RangeToInclusive<usize>` and `std::ops::RangeToInclusive<usize> > std::ops::RangeToInclusive<usize>`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeToInclusive<usize>`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error[E0277]: can't compare `std::ops::Range<usize>` with `std::ops::Range<usize>`
+ --> $DIR/range_traits-1.rs:5:5
+ |
+LL | a: Range<usize>,
+ | ^^^^^^^^^^^^^^^ no implementation for `std::ops::Range<usize> < std::ops::Range<usize>` and `std::ops::Range<usize> > std::ops::Range<usize>`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::Range<usize>`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error[E0277]: can't compare `std::ops::RangeTo<usize>` with `std::ops::RangeTo<usize>`
+ --> $DIR/range_traits-1.rs:12:5
+ |
+LL | b: RangeTo<usize>,
+ | ^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeTo<usize> < std::ops::RangeTo<usize>` and `std::ops::RangeTo<usize> > std::ops::RangeTo<usize>`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeTo<usize>`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error[E0277]: can't compare `std::ops::RangeFrom<usize>` with `std::ops::RangeFrom<usize>`
+ --> $DIR/range_traits-1.rs:19:5
+ |
+LL | c: RangeFrom<usize>,
+ | ^^^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeFrom<usize> < std::ops::RangeFrom<usize>` and `std::ops::RangeFrom<usize> > std::ops::RangeFrom<usize>`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeFrom<usize>`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error[E0277]: can't compare `std::ops::RangeFull` with `std::ops::RangeFull`
+ --> $DIR/range_traits-1.rs:26:5
+ |
+LL | d: RangeFull,
+ | ^^^^^^^^^^^^ no implementation for `std::ops::RangeFull < std::ops::RangeFull` and `std::ops::RangeFull > std::ops::RangeFull`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeFull`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error[E0277]: can't compare `std::ops::RangeInclusive<usize>` with `std::ops::RangeInclusive<usize>`
+ --> $DIR/range_traits-1.rs:33:5
+ |
+LL | e: RangeInclusive<usize>,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeInclusive<usize> < std::ops::RangeInclusive<usize>` and `std::ops::RangeInclusive<usize> > std::ops::RangeInclusive<usize>`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeInclusive<usize>`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error[E0277]: can't compare `std::ops::RangeToInclusive<usize>` with `std::ops::RangeToInclusive<usize>`
+ --> $DIR/range_traits-1.rs:40:5
+ |
+LL | f: RangeToInclusive<usize>,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeToInclusive<usize> < std::ops::RangeToInclusive<usize>` and `std::ops::RangeToInclusive<usize> > std::ops::RangeToInclusive<usize>`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeToInclusive<usize>`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error[E0277]: can't compare `std::ops::Range<usize>` with `std::ops::Range<usize>`
+ --> $DIR/range_traits-1.rs:5:5
+ |
+LL | a: Range<usize>,
+ | ^^^^^^^^^^^^^^^ no implementation for `std::ops::Range<usize> < std::ops::Range<usize>` and `std::ops::Range<usize> > std::ops::Range<usize>`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::Range<usize>`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error[E0277]: can't compare `std::ops::RangeTo<usize>` with `std::ops::RangeTo<usize>`
+ --> $DIR/range_traits-1.rs:12:5
+ |
+LL | b: RangeTo<usize>,
+ | ^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeTo<usize> < std::ops::RangeTo<usize>` and `std::ops::RangeTo<usize> > std::ops::RangeTo<usize>`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeTo<usize>`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error[E0277]: can't compare `std::ops::RangeFrom<usize>` with `std::ops::RangeFrom<usize>`
+ --> $DIR/range_traits-1.rs:19:5
+ |
+LL | c: RangeFrom<usize>,
+ | ^^^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeFrom<usize> < std::ops::RangeFrom<usize>` and `std::ops::RangeFrom<usize> > std::ops::RangeFrom<usize>`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeFrom<usize>`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error[E0277]: can't compare `std::ops::RangeFull` with `std::ops::RangeFull`
+ --> $DIR/range_traits-1.rs:26:5
+ |
+LL | d: RangeFull,
+ | ^^^^^^^^^^^^ no implementation for `std::ops::RangeFull < std::ops::RangeFull` and `std::ops::RangeFull > std::ops::RangeFull`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeFull`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error[E0277]: can't compare `std::ops::RangeInclusive<usize>` with `std::ops::RangeInclusive<usize>`
+ --> $DIR/range_traits-1.rs:33:5
+ |
+LL | e: RangeInclusive<usize>,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeInclusive<usize> < std::ops::RangeInclusive<usize>` and `std::ops::RangeInclusive<usize> > std::ops::RangeInclusive<usize>`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeInclusive<usize>`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error[E0277]: can't compare `std::ops::RangeToInclusive<usize>` with `std::ops::RangeToInclusive<usize>`
+ --> $DIR/range_traits-1.rs:40:5
+ |
+LL | f: RangeToInclusive<usize>,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeToInclusive<usize> < std::ops::RangeToInclusive<usize>` and `std::ops::RangeToInclusive<usize> > std::ops::RangeToInclusive<usize>`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeToInclusive<usize>`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error[E0277]: can't compare `std::ops::Range<usize>` with `std::ops::Range<usize>`
+ --> $DIR/range_traits-1.rs:5:5
+ |
+LL | a: Range<usize>,
+ | ^^^^^^^^^^^^^^^ no implementation for `std::ops::Range<usize> < std::ops::Range<usize>` and `std::ops::Range<usize> > std::ops::Range<usize>`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::Range<usize>`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error[E0277]: can't compare `std::ops::RangeTo<usize>` with `std::ops::RangeTo<usize>`
+ --> $DIR/range_traits-1.rs:12:5
+ |
+LL | b: RangeTo<usize>,
+ | ^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeTo<usize> < std::ops::RangeTo<usize>` and `std::ops::RangeTo<usize> > std::ops::RangeTo<usize>`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeTo<usize>`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error[E0277]: can't compare `std::ops::RangeFrom<usize>` with `std::ops::RangeFrom<usize>`
+ --> $DIR/range_traits-1.rs:19:5
+ |
+LL | c: RangeFrom<usize>,
+ | ^^^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeFrom<usize> < std::ops::RangeFrom<usize>` and `std::ops::RangeFrom<usize> > std::ops::RangeFrom<usize>`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeFrom<usize>`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error[E0277]: can't compare `std::ops::RangeFull` with `std::ops::RangeFull`
+ --> $DIR/range_traits-1.rs:26:5
+ |
+LL | d: RangeFull,
+ | ^^^^^^^^^^^^ no implementation for `std::ops::RangeFull < std::ops::RangeFull` and `std::ops::RangeFull > std::ops::RangeFull`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeFull`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error[E0277]: can't compare `std::ops::RangeInclusive<usize>` with `std::ops::RangeInclusive<usize>`
+ --> $DIR/range_traits-1.rs:33:5
+ |
+LL | e: RangeInclusive<usize>,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeInclusive<usize> < std::ops::RangeInclusive<usize>` and `std::ops::RangeInclusive<usize> > std::ops::RangeInclusive<usize>`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeInclusive<usize>`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error[E0277]: can't compare `std::ops::RangeToInclusive<usize>` with `std::ops::RangeToInclusive<usize>`
+ --> $DIR/range_traits-1.rs:40:5
+ |
+LL | f: RangeToInclusive<usize>,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeToInclusive<usize> < std::ops::RangeToInclusive<usize>` and `std::ops::RangeToInclusive<usize> > std::ops::RangeToInclusive<usize>`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeToInclusive<usize>`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error[E0277]: can't compare `std::ops::Range<usize>` with `std::ops::Range<usize>`
+ --> $DIR/range_traits-1.rs:5:5
+ |
+LL | a: Range<usize>,
+ | ^^^^^^^^^^^^^^^ no implementation for `std::ops::Range<usize> < std::ops::Range<usize>` and `std::ops::Range<usize> > std::ops::Range<usize>`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::Range<usize>`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error[E0277]: can't compare `std::ops::RangeTo<usize>` with `std::ops::RangeTo<usize>`
+ --> $DIR/range_traits-1.rs:12:5
+ |
+LL | b: RangeTo<usize>,
+ | ^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeTo<usize> < std::ops::RangeTo<usize>` and `std::ops::RangeTo<usize> > std::ops::RangeTo<usize>`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeTo<usize>`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error[E0277]: can't compare `std::ops::RangeFrom<usize>` with `std::ops::RangeFrom<usize>`
+ --> $DIR/range_traits-1.rs:19:5
+ |
+LL | c: RangeFrom<usize>,
+ | ^^^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeFrom<usize> < std::ops::RangeFrom<usize>` and `std::ops::RangeFrom<usize> > std::ops::RangeFrom<usize>`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeFrom<usize>`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error[E0277]: can't compare `std::ops::RangeFull` with `std::ops::RangeFull`
+ --> $DIR/range_traits-1.rs:26:5
+ |
+LL | d: RangeFull,
+ | ^^^^^^^^^^^^ no implementation for `std::ops::RangeFull < std::ops::RangeFull` and `std::ops::RangeFull > std::ops::RangeFull`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeFull`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error[E0277]: can't compare `std::ops::RangeInclusive<usize>` with `std::ops::RangeInclusive<usize>`
+ --> $DIR/range_traits-1.rs:33:5
+ |
+LL | e: RangeInclusive<usize>,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeInclusive<usize> < std::ops::RangeInclusive<usize>` and `std::ops::RangeInclusive<usize> > std::ops::RangeInclusive<usize>`
+ |
+ = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeInclusive<usize>`
+ = note: required by `std::cmp::PartialOrd::partial_cmp`
+
+error[E0277]: can't compare `std::ops::RangeToInclusive<usize>` with `std::ops::RangeToInclusive<usize>`
+ --> $DIR/range_traits-1.rs:40:5
|
LL | f: RangeToInclusive<usize>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeToInclusive<usize> < std::ops::RangeToInclusive<usize>` and `std::ops::RangeToInclusive<usize> > std::ops::RangeToInclusive<usize>`
= note: required by `std::cmp::Ord::cmp`
error[E0277]: the trait bound `std::ops::RangeTo<usize>: std::cmp::Ord` is not satisfied
- --> $DIR/range_traits-1.rs:8:5
+ --> $DIR/range_traits-1.rs:12:5
|
LL | b: RangeTo<usize>,
| ^^^^^^^^^^^^^^^^^ the trait `std::cmp::Ord` is not implemented for `std::ops::RangeTo<usize>`
= note: required by `std::cmp::Ord::cmp`
error[E0277]: the trait bound `std::ops::RangeFrom<usize>: std::cmp::Ord` is not satisfied
- --> $DIR/range_traits-1.rs:11:5
+ --> $DIR/range_traits-1.rs:19:5
|
LL | c: RangeFrom<usize>,
| ^^^^^^^^^^^^^^^^^^^ the trait `std::cmp::Ord` is not implemented for `std::ops::RangeFrom<usize>`
= note: required by `std::cmp::Ord::cmp`
error[E0277]: the trait bound `std::ops::RangeFull: std::cmp::Ord` is not satisfied
- --> $DIR/range_traits-1.rs:14:5
+ --> $DIR/range_traits-1.rs:26:5
|
LL | d: RangeFull,
| ^^^^^^^^^^^^ the trait `std::cmp::Ord` is not implemented for `std::ops::RangeFull`
= note: required by `std::cmp::Ord::cmp`
error[E0277]: the trait bound `std::ops::RangeInclusive<usize>: std::cmp::Ord` is not satisfied
- --> $DIR/range_traits-1.rs:17:5
+ --> $DIR/range_traits-1.rs:33:5
|
LL | e: RangeInclusive<usize>,
| ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::cmp::Ord` is not implemented for `std::ops::RangeInclusive<usize>`
= note: required by `std::cmp::Ord::cmp`
error[E0277]: the trait bound `std::ops::RangeToInclusive<usize>: std::cmp::Ord` is not satisfied
- --> $DIR/range_traits-1.rs:20:5
+ --> $DIR/range_traits-1.rs:40:5
|
LL | f: RangeToInclusive<usize>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::cmp::Ord` is not implemented for `std::ops::RangeToInclusive<usize>`
|
= note: required by `std::cmp::Ord::cmp`
-error: aborting due to 12 previous errors
+error: aborting due to 36 previous errors
For more information about this error, try `rustc --explain E0277`.
//~| ERROR the parameter type `T` may not live long enough
//~| ERROR the parameter type `T` may not live long enough
//~| ERROR the parameter type `T` may not live long enough
+ //~| ERROR the parameter type `T` may not live long enough
}
fn main() {}
LL | box B(&*v) as Box<X>
| ^^^
-error: aborting due to 6 previous errors
+error[E0310]: the parameter type `T` may not live long enough
+ --> $DIR/regions-close-object-into-object-5.rs:17:11
+ |
+LL | fn f<'a, T, U>(v: Box<A<T>+'static>) -> Box<X+'static> {
+ | - help: consider adding an explicit lifetime bound `T: 'static`...
+LL | // oh dear!
+LL | box B(&*v) as Box<X>
+ | ^^^
+ |
+note: ...so that the type `(dyn A<T> + 'static)` is not borrowed for too long
+ --> $DIR/regions-close-object-into-object-5.rs:17:11
+ |
+LL | box B(&*v) as Box<X>
+ | ^^^
+
+error: aborting due to 7 previous errors
For more information about this error, try `rustc --explain E0310`.
// Here we get an error: we need `'a: 'b`.
fn bar<'a, 'b>() //~ ERROR cannot infer
+ //~| ERROR cannot infer
+ //~| ERROR cannot infer
where <() as Project<'a, 'b>>::Item : Eq
{
}
--> $DIR/regions-normalize-in-where-clause-list.rs:22:1
|
LL | / fn bar<'a, 'b>()
+LL | |
+LL | |
LL | | where <() as Project<'a, 'b>>::Item : Eq
LL | | {
LL | | }
--> $DIR/regions-normalize-in-where-clause-list.rs:22:1
|
LL | / fn bar<'a, 'b>()
+LL | |
+LL | |
LL | | where <() as Project<'a, 'b>>::Item : Eq
LL | | {
LL | | }
= note: expected `Project<'a, 'b>`
found `Project<'_, '_>`
-error: aborting due to previous error
+error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
+ --> $DIR/regions-normalize-in-where-clause-list.rs:22:1
+ |
+LL | / fn bar<'a, 'b>()
+LL | |
+LL | |
+LL | | where <() as Project<'a, 'b>>::Item : Eq
+LL | | {
+LL | | }
+ | |_^
+ |
+note: first, the lifetime cannot outlive the lifetime `'a` as defined on the function body at 22:8...
+ --> $DIR/regions-normalize-in-where-clause-list.rs:22:8
+ |
+LL | fn bar<'a, 'b>()
+ | ^^
+note: ...but the lifetime must also be valid for the lifetime `'b` as defined on the function body at 22:12...
+ --> $DIR/regions-normalize-in-where-clause-list.rs:22:12
+ |
+LL | fn bar<'a, 'b>()
+ | ^^
+note: ...so that the types are compatible
+ --> $DIR/regions-normalize-in-where-clause-list.rs:22:1
+ |
+LL | / fn bar<'a, 'b>()
+LL | |
+LL | |
+LL | | where <() as Project<'a, 'b>>::Item : Eq
+LL | | {
+LL | | }
+ | |_^
+ = note: expected `Project<'a, 'b>`
+ found `Project<'_, '_>`
+
+error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
+ --> $DIR/regions-normalize-in-where-clause-list.rs:22:1
+ |
+LL | / fn bar<'a, 'b>()
+LL | |
+LL | |
+LL | | where <() as Project<'a, 'b>>::Item : Eq
+LL | | {
+LL | | }
+ | |_^
+ |
+note: first, the lifetime cannot outlive the lifetime `'a` as defined on the function body at 22:8...
+ --> $DIR/regions-normalize-in-where-clause-list.rs:22:8
+ |
+LL | fn bar<'a, 'b>()
+ | ^^
+note: ...but the lifetime must also be valid for the lifetime `'b` as defined on the function body at 22:12...
+ --> $DIR/regions-normalize-in-where-clause-list.rs:22:12
+ |
+LL | fn bar<'a, 'b>()
+ | ^^
+note: ...so that the types are compatible
+ --> $DIR/regions-normalize-in-where-clause-list.rs:22:1
+ |
+LL | / fn bar<'a, 'b>()
+LL | |
+LL | |
+LL | | where <() as Project<'a, 'b>>::Item : Eq
+LL | | {
+LL | | }
+ | |_^
+ = note: expected `Project<'a, 'b>`
+ found `Project<'_, '_>`
+
+error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0495`.
#![allow(dead_code)]
#[repr(align(8))] //~ ERROR incorrect `repr(align)` attribute format
+ //~| ERROR incorrect `repr(align)` attribute format
struct A(u64);
#[repr(align(8))] //~ ERROR incorrect `repr(align)` attribute format
+ //~| ERROR incorrect `repr(align)` attribute format
struct B(u64);
fn main() {}
#![allow(dead_code)]
#[repr(align=8)] //~ ERROR incorrect `repr(align)` attribute format
+ //~| ERROR incorrect `repr(align)` attribute format
struct A(u64);
#[repr(align="8")] //~ ERROR incorrect `repr(align)` attribute format
+ //~| ERROR incorrect `repr(align)` attribute format
struct B(u64);
fn main() {}
| ^^^^^^^ help: use parentheses instead: `align(8)`
error[E0693]: incorrect `repr(align)` attribute format
- --> $DIR/repr-align-assign.rs:8:8
+ --> $DIR/repr-align-assign.rs:9:8
|
LL | #[repr(align="8")]
| ^^^^^^^^^ help: use parentheses instead: `align(8)`
-error: aborting due to 2 previous errors
+error[E0693]: incorrect `repr(align)` attribute format
+ --> $DIR/repr-align-assign.rs:5:8
+ |
+LL | #[repr(align=8)]
+ | ^^^^^^^ help: use parentheses instead: `align(8)`
+
+error[E0693]: incorrect `repr(align)` attribute format
+ --> $DIR/repr-align-assign.rs:9:8
+ |
+LL | #[repr(align="8")]
+ | ^^^^^^^^^ help: use parentheses instead: `align(8)`
+
+error: aborting due to 4 previous errors
#![allow(dead_code)]
#[repr(align(16.0))] //~ ERROR: invalid `repr(align)` attribute: not an unsuffixed integer
+ //~| ERROR: invalid `repr(align)` attribute: not an unsuffixed integer
struct S0(i32);
#[repr(align(15))] //~ ERROR: invalid `repr(align)` attribute: not a power of two
+ //~| ERROR: invalid `repr(align)` attribute: not a power of two
struct S1(i32);
#[repr(align(4294967296))] //~ ERROR: invalid `repr(align)` attribute: larger than 2^29
+ //~| ERROR: invalid `repr(align)` attribute: larger than 2^29
struct S2(i32);
#[repr(align(536870912))] // ok: this is the largest accepted alignment
struct S3(i32);
#[repr(align(16.0))] //~ ERROR: invalid `repr(align)` attribute: not an unsuffixed integer
+ //~| ERROR: invalid `repr(align)` attribute: not an unsuffixed integer
enum E0 { A, B }
#[repr(align(15))] //~ ERROR: invalid `repr(align)` attribute: not a power of two
+ //~| ERROR: invalid `repr(align)` attribute: not a power of two
enum E1 { A, B }
#[repr(align(4294967296))] //~ ERROR: invalid `repr(align)` attribute: larger than 2^29
+ //~| ERROR: invalid `repr(align)` attribute: larger than 2^29
enum E2 { A, B }
#[repr(align(536870912))] // ok: this is the largest accepted alignment
| ^^^^^^^^^^^
error[E0589]: invalid `repr(align)` attribute: not a power of two
- --> $DIR/repr-align.rs:6:8
+ --> $DIR/repr-align.rs:7:8
|
LL | #[repr(align(15))]
| ^^^^^^^^^
error[E0589]: invalid `repr(align)` attribute: larger than 2^29
- --> $DIR/repr-align.rs:9:8
+ --> $DIR/repr-align.rs:11:8
|
LL | #[repr(align(4294967296))]
| ^^^^^^^^^^^^^^^^^
error[E0589]: invalid `repr(align)` attribute: not an unsuffixed integer
- --> $DIR/repr-align.rs:15:8
+ --> $DIR/repr-align.rs:18:8
+ |
+LL | #[repr(align(16.0))]
+ | ^^^^^^^^^^^
+
+error[E0589]: invalid `repr(align)` attribute: not a power of two
+ --> $DIR/repr-align.rs:22:8
+ |
+LL | #[repr(align(15))]
+ | ^^^^^^^^^
+
+error[E0589]: invalid `repr(align)` attribute: larger than 2^29
+ --> $DIR/repr-align.rs:26:8
+ |
+LL | #[repr(align(4294967296))]
+ | ^^^^^^^^^^^^^^^^^
+
+error[E0589]: invalid `repr(align)` attribute: not an unsuffixed integer
+ --> $DIR/repr-align.rs:3:8
|
LL | #[repr(align(16.0))]
| ^^^^^^^^^^^
error[E0589]: invalid `repr(align)` attribute: not a power of two
+ --> $DIR/repr-align.rs:7:8
+ |
+LL | #[repr(align(15))]
+ | ^^^^^^^^^
+
+error[E0589]: invalid `repr(align)` attribute: larger than 2^29
+ --> $DIR/repr-align.rs:11:8
+ |
+LL | #[repr(align(4294967296))]
+ | ^^^^^^^^^^^^^^^^^
+
+error[E0589]: invalid `repr(align)` attribute: not an unsuffixed integer
--> $DIR/repr-align.rs:18:8
|
+LL | #[repr(align(16.0))]
+ | ^^^^^^^^^^^
+
+error[E0589]: invalid `repr(align)` attribute: not a power of two
+ --> $DIR/repr-align.rs:22:8
+ |
LL | #[repr(align(15))]
| ^^^^^^^^^
error[E0589]: invalid `repr(align)` attribute: larger than 2^29
- --> $DIR/repr-align.rs:21:8
+ --> $DIR/repr-align.rs:26:8
|
LL | #[repr(align(4294967296))]
| ^^^^^^^^^^^^^^^^^
-error: aborting due to 6 previous errors
+error: aborting due to 12 previous errors
For more information about this error, try `rustc --explain E0589`.
+// FIXME: missing sysroot spans (#53081)
+// ignore-i586-unknown-linux-gnu
+// ignore-i586-unknown-linux-musl
+// ignore-i686-unknown-linux-musl
const MAX_ITEM: usize = 10;
fn foo_bar() {}
error[E0412]: cannot find type `esize` in this scope
- --> $DIR/levenshtein.rs:5:11
+ --> $DIR/levenshtein.rs:9:11
|
LL | fn foo(c: esize) {} // Misspelled primitive type name.
| ^^^^^ help: a builtin type with a similar name exists: `isize`
error[E0412]: cannot find type `Baz` in this scope
- --> $DIR/levenshtein.rs:10:10
+ --> $DIR/levenshtein.rs:14:10
|
LL | enum Bar { }
| ------------ similarly named enum `Bar` defined here
| ^^^ help: an enum with a similar name exists: `Bar`
error[E0412]: cannot find type `Opiton` in this scope
- --> $DIR/levenshtein.rs:12:10
+ --> $DIR/levenshtein.rs:16:10
|
LL | type B = Opiton<u8>; // Misspelled type name from the prelude.
| ^^^^^^ help: an enum with a similar name exists: `Option`
+ |
+ ::: $SRC_DIR/libcore/option.rs:LL:COL
+ |
+LL | pub enum Option<T> {
+ | ------------------ similarly named enum `Option` defined here
error[E0412]: cannot find type `Baz` in this scope
- --> $DIR/levenshtein.rs:16:14
+ --> $DIR/levenshtein.rs:20:14
|
LL | type A = Baz; // No suggestion here, Bar is not visible
| ^^^ not found in this scope
error[E0425]: cannot find value `MAXITEM` in this scope
- --> $DIR/levenshtein.rs:24:20
+ --> $DIR/levenshtein.rs:28:20
|
LL | const MAX_ITEM: usize = 10;
| --------------------------- similarly named constant `MAX_ITEM` defined here
| ^^^^^^^ help: a constant with a similar name exists: `MAX_ITEM`
error[E0425]: cannot find function `foobar` in this scope
- --> $DIR/levenshtein.rs:26:5
+ --> $DIR/levenshtein.rs:30:5
|
LL | fn foo_bar() {}
| --------------- similarly named function `foo_bar` defined here
| ^^^^^^ help: a function with a similar name exists: `foo_bar`
error[E0412]: cannot find type `first` in module `m`
- --> $DIR/levenshtein.rs:28:15
+ --> $DIR/levenshtein.rs:32:15
|
LL | pub struct First;
| ----------------- similarly named struct `First` defined here
| ^^^^^ help: a struct with a similar name exists (notice the capitalization): `First`
error[E0425]: cannot find value `second` in module `m`
- --> $DIR/levenshtein.rs:28:26
+ --> $DIR/levenshtein.rs:32:26
|
LL | pub struct Second;
| ------------------ similarly named unit struct `Second` defined here
--- /dev/null
+// run-pass
+// ignore-wasm32-bare compiled with panic=abort by default
+
+#![feature(option_expect_none, option_unwrap_none)]
+
+//! Test that panic locations for `#[track_caller]` functions in std have the correct
+//! location reported.
+
+fn main() {
+ // inspect the `PanicInfo` we receive to ensure the right file is the source
+ std::panic::set_hook(Box::new(|info| {
+ let actual = info.location().unwrap();
+ if actual.file() != file!() {
+ eprintln!("expected a location in the test file, found {:?}", actual);
+ panic!();
+ }
+ }));
+
+ fn assert_panicked(f: impl FnOnce() + std::panic::UnwindSafe) {
+ std::panic::catch_unwind(f).unwrap_err();
+ }
+
+ let nope: Option<()> = None;
+ assert_panicked(|| nope.unwrap());
+ assert_panicked(|| nope.expect(""));
+
+ let yep: Option<()> = Some(());
+ assert_panicked(|| yep.unwrap_none());
+ assert_panicked(|| yep.expect_none(""));
+
+ let oops: Result<(), ()> = Err(());
+ assert_panicked(|| oops.unwrap());
+ assert_panicked(|| oops.expect(""));
+
+ let fine: Result<(), ()> = Ok(());
+ assert_panicked(|| fine.unwrap_err());
+ assert_panicked(|| fine.expect_err(""));
+}
use_expr!((let 0 = 1 && 0 == 0));
//~^ ERROR `let` expressions in this position are experimental [E0658]
//~| ERROR `let` expressions are not supported here
+ //~| ERROR `let` expressions are not supported here
use_expr!((let 0 = 1));
//~^ ERROR `let` expressions in this position are experimental [E0658]
//~| ERROR `let` expressions are not supported here
+ //~| ERROR `let` expressions are not supported here
#[cfg(FALSE)] (let 0 = 1);
//~^ ERROR `let` expressions in this position are experimental [E0658]
use_expr!(let 0 = 1);
error: no rules expected the token `let`
- --> $DIR/feature-gate.rs:131:15
+ --> $DIR/feature-gate.rs:133:15
|
LL | macro_rules! use_expr {
| --------------------- when calling this macro
= help: add `#![feature(let_chains)]` to the crate attributes to enable
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:129:20
+ --> $DIR/feature-gate.rs:131:20
|
LL | #[cfg(FALSE)] (let 0 = 1);
| ^^^^^^^^^
= help: add `#![feature(let_chains)]` to the crate attributes to enable
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:126:16
+ --> $DIR/feature-gate.rs:127:16
|
LL | use_expr!((let 0 = 1));
| ^^^^^^^^^
= note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:126:16
+ --> $DIR/feature-gate.rs:123:16
+ |
+LL | use_expr!((let 0 = 1 && 0 == 0));
+ | ^^^^^^^^^
+ |
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+ --> $DIR/feature-gate.rs:127:16
+ |
+LL | use_expr!((let 0 = 1));
+ | ^^^^^^^^^
+ |
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+ --> $DIR/feature-gate.rs:127:16
|
LL | use_expr!((let 0 = 1));
| ^^^^^^^^^
= note: only supported directly in conditions of `if`- and `while`-expressions
= note: as well as when nested within `&&` and parenthesis in those conditions
-error: aborting due to 63 previous errors
+error: aborting due to 65 previous errors
For more information about this error, try `rustc --explain E0658`.
--- /dev/null
+error: `?const` on trait bounds is not yet implemented
+ --> $DIR/feature-gate.rs:11:29
+ |
+LL | const fn get_assoc_const<S: ?const T>() -> i32 { <S as T>::CONST }
+ | ^^^^^^^^
+
+error: aborting due to previous error
+
--- /dev/null
+// revisions: stock gated
+// gate-test-const_trait_bound_opt_out
+
+#![cfg_attr(gated, feature(const_trait_bound_opt_out))]
+#![allow(incomplete_features)]
+
+trait T {
+ const CONST: i32;
+}
+
+const fn get_assoc_const<S: ?const T>() -> i32 { <S as T>::CONST }
+//[stock]~^ ERROR `?const` on trait bounds is experimental
+//[stock,gated]~^^ ERROR `?const` on trait bounds is not yet implemented
+
+fn main() {}
--- /dev/null
+error[E0658]: `?const` on trait bounds is experimental
+ --> $DIR/feature-gate.rs:11:29
+ |
+LL | const fn get_assoc_const<S: ?const T>() -> i32 { <S as T>::CONST }
+ | ^^^^^^
+ |
+ = note: for more information, see https://github.com/rust-lang/rust/issues/67794
+ = help: add `#![feature(const_trait_bound_opt_out)]` to the crate attributes to enable
+
+error: `?const` on trait bounds is not yet implemented
+ --> $DIR/feature-gate.rs:11:29
+ |
+LL | const fn get_assoc_const<S: ?const T>() -> i32 { <S as T>::CONST }
+ | ^^^^^^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
--- /dev/null
+#![feature(const_trait_bound_opt_out)]
+#![feature(associated_type_bounds)]
+#![allow(incomplete_features)]
+
+trait T {}
+struct S;
+impl T for S {}
+
+fn rpit() -> impl ?const T { S }
+//~^ ERROR `?const` is not permitted in `impl Trait`
+//~| ERROR `?const` on trait bounds is not yet implemented
+
+fn apit(_: impl ?const T) {}
+//~^ ERROR `?const` is not permitted in `impl Trait`
+//~| ERROR `?const` on trait bounds is not yet implemented
+
+fn rpit_assoc_bound() -> impl IntoIterator<Item: ?const T> { Some(S) }
+//~^ ERROR `?const` is not permitted in `impl Trait`
+//~| ERROR `?const` on trait bounds is not yet implemented
+
+fn apit_assoc_bound(_: impl IntoIterator<Item: ?const T>) {}
+//~^ ERROR `?const` is not permitted in `impl Trait`
+//~| ERROR `?const` on trait bounds is not yet implemented
+
+fn main() {}
--- /dev/null
+error: `?const` is not permitted in `impl Trait`
+ --> $DIR/in-impl-trait.rs:9:19
+ |
+LL | fn rpit() -> impl ?const T { S }
+ | ^^^^^^^^
+
+error: `?const` is not permitted in `impl Trait`
+ --> $DIR/in-impl-trait.rs:13:17
+ |
+LL | fn apit(_: impl ?const T) {}
+ | ^^^^^^^^
+
+error: `?const` is not permitted in `impl Trait`
+ --> $DIR/in-impl-trait.rs:17:50
+ |
+LL | fn rpit_assoc_bound() -> impl IntoIterator<Item: ?const T> { Some(S) }
+ | ^^^^^^^^
+
+error: `?const` is not permitted in `impl Trait`
+ --> $DIR/in-impl-trait.rs:21:48
+ |
+LL | fn apit_assoc_bound(_: impl IntoIterator<Item: ?const T>) {}
+ | ^^^^^^^^
+
+error: `?const` on trait bounds is not yet implemented
+ --> $DIR/in-impl-trait.rs:9:19
+ |
+LL | fn rpit() -> impl ?const T { S }
+ | ^^^^^^^^
+
+error: `?const` on trait bounds is not yet implemented
+ --> $DIR/in-impl-trait.rs:13:17
+ |
+LL | fn apit(_: impl ?const T) {}
+ | ^^^^^^^^
+
+error: `?const` on trait bounds is not yet implemented
+ --> $DIR/in-impl-trait.rs:17:50
+ |
+LL | fn rpit_assoc_bound() -> impl IntoIterator<Item: ?const T> { Some(S) }
+ | ^^^^^^^^
+
+error: `?const` on trait bounds is not yet implemented
+ --> $DIR/in-impl-trait.rs:21:48
+ |
+LL | fn apit_assoc_bound(_: impl IntoIterator<Item: ?const T>) {}
+ | ^^^^^^^^
+
+error: aborting due to 8 previous errors
+
--- /dev/null
+#![feature(const_trait_bound_opt_out)]
+#![allow(incomplete_features)]
+
+trait Super {}
+trait T: ?const Super {}
+//~^ ERROR `?const` is not permitted in supertraits
+//~| ERROR `?const` on trait bounds is not yet implemented
+
+fn main() {}
--- /dev/null
+error: `?const` is not permitted in supertraits
+ --> $DIR/in-trait-bounds.rs:5:10
+ |
+LL | trait T: ?const Super {}
+ | ^^^^^^^^^^^^
+
+error: `?const` on trait bounds is not yet implemented
+ --> $DIR/in-trait-bounds.rs:5:10
+ |
+LL | trait T: ?const Super {}
+ | ^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
--- /dev/null
+#![feature(const_trait_bound_opt_out)]
+#![allow(bare_trait_objects)]
+#![allow(incomplete_features)]
+
+struct S;
+trait T {}
+impl T for S {}
+
+// An inherent impl for the trait object `?const T`.
+impl ?const T {}
+//~^ ERROR `?const` is not permitted in trait objects
+//~| ERROR `?const` on trait bounds is not yet implemented
+
+fn trait_object() -> &'static dyn ?const T { &S }
+//~^ ERROR `?const` is not permitted in trait objects
+//~| ERROR `?const` on trait bounds is not yet implemented
+
+fn trait_object_in_apit(_: impl IntoIterator<Item = Box<dyn ?const T>>) {}
+//~^ ERROR `?const` is not permitted in trait objects
+//~| ERROR `?const` on trait bounds is not yet implemented
+
+fn main() {}
--- /dev/null
+error: `?const` is not permitted in trait objects
+ --> $DIR/in-trait-object.rs:10:6
+ |
+LL | impl ?const T {}
+ | ^^^^^^^^
+
+error: `?const` is not permitted in trait objects
+ --> $DIR/in-trait-object.rs:14:35
+ |
+LL | fn trait_object() -> &'static dyn ?const T { &S }
+ | ^^^^^^^^
+
+error: `?const` is not permitted in trait objects
+ --> $DIR/in-trait-object.rs:18:61
+ |
+LL | fn trait_object_in_apit(_: impl IntoIterator<Item = Box<dyn ?const T>>) {}
+ | ^^^^^^^^
+
+error: `?const` on trait bounds is not yet implemented
+ --> $DIR/in-trait-object.rs:10:6
+ |
+LL | impl ?const T {}
+ | ^^^^^^^^
+
+error: `?const` on trait bounds is not yet implemented
+ --> $DIR/in-trait-object.rs:14:35
+ |
+LL | fn trait_object() -> &'static dyn ?const T { &S }
+ | ^^^^^^^^
+
+error: `?const` on trait bounds is not yet implemented
+ --> $DIR/in-trait-object.rs:18:61
+ |
+LL | fn trait_object_in_apit(_: impl IntoIterator<Item = Box<dyn ?const T>>) {}
+ | ^^^^^^^^
+
+error: aborting due to 6 previous errors
+
--- /dev/null
+// compile-flags: -Z parse-only
+
+#![feature(const_trait_bound_opt_out)]
+#![allow(incomplete_features)]
+
+struct S<T: ?const ?const Tr>;
+//~^ ERROR expected identifier, found keyword `const`
+//~| ERROR expected one of `(`, `+`, `,`, `::`, `<`, `=`, or `>`
--- /dev/null
+error: expected identifier, found keyword `const`
+ --> $DIR/opt-out-twice.rs:6:21
+ |
+LL | struct S<T: ?const ?const Tr>;
+ | ^^^^^ expected identifier, found keyword
+
+error: expected one of `(`, `+`, `,`, `::`, `<`, `=`, or `>`, found `Tr`
+ --> $DIR/opt-out-twice.rs:6:27
+ |
+LL | struct S<T: ?const ?const Tr>;
+ | ^^ expected one of 7 possible tokens
+
+error: aborting due to 2 previous errors
+
--- /dev/null
+// compile-flags: -Z parse-only
+// check-pass
+
+#![feature(const_trait_bound_opt_out)]
+#![allow(incomplete_features)]
+
+struct S<
+ T: ?const ?for<'a> Tr<'a> + 'static + ?const std::ops::Add,
+ T: ?const ?for<'a: 'b> m::Trait<'a>,
+>;
--- /dev/null
+#![feature(const_trait_bound_opt_out)]
+#![allow(incomplete_features)]
+
+struct S<T: ?const ?Sized>(std::marker::PhantomData<T>);
+//~^ ERROR `?const` and `?` are mutually exclusive
+//~| ERROR `?const` on trait bounds is not yet implemented
+
+fn main() {}
--- /dev/null
+error: `?const` and `?` are mutually exclusive
+ --> $DIR/with-maybe-sized.rs:4:13
+ |
+LL | struct S<T: ?const ?Sized>(std::marker::PhantomData<T>);
+ | ^^^^^^^^^^^^^
+
+error: `?const` on trait bounds is not yet implemented
+ --> $DIR/with-maybe-sized.rs:4:13
+ |
+LL | struct S<T: ?const ?Sized>(std::marker::PhantomData<T>);
+ | ^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
--- /dev/null
+// compile-flags: -Z parse-only
+
+#![feature(const_trait_bound_opt_out)]
+#![allow(incomplete_features)]
+
+struct S<T: const Tr>;
+//~^ ERROR expected one of `!`, `(`, `,`, `=`, `>`, `?`, `for`, lifetime, or path
--- /dev/null
+error: expected one of `!`, `(`, `,`, `=`, `>`, `?`, `for`, lifetime, or path, found keyword `const`
+ --> $DIR/without-question-mark.rs:6:13
+ |
+LL | struct S<T: const Tr>;
+ | ^^^^^ expected one of 9 possible tokens
+
+error: aborting due to previous error
+
--- /dev/null
+error: const trait impls are not yet implemented
+ --> $DIR/feature-gate.rs:9:1
+ |
+LL | impl const T for S {}
+ | ^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
--- /dev/null
+// revisions: stock gated
+// gate-test-const_trait_impl
+
+#![cfg_attr(gated, feature(const_trait_impl))]
+#![allow(incomplete_features)]
+
+struct S;
+trait T {}
+impl const T for S {}
+//[stock]~^ ERROR const trait impls are experimental
+//[stock,gated]~^^ ERROR const trait impls are not yet implemented
+
+fn main() {}
--- /dev/null
+error[E0658]: const trait impls are experimental
+ --> $DIR/feature-gate.rs:9:6
+ |
+LL | impl const T for S {}
+ | ^^^^^
+ |
+ = note: for more information, see https://github.com/rust-lang/rust/issues/67792
+ = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+
+error: const trait impls are not yet implemented
+ --> $DIR/feature-gate.rs:9:1
+ |
+LL | impl const T for S {}
+ | ^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
--- /dev/null
+#![feature(const_trait_bound_opt_out)]
+#![feature(const_trait_impl)]
+#![allow(incomplete_features)]
+
+struct S;
+trait T {}
+
+impl ?const T for S {}
+//~^ ERROR expected a trait, found type
+
+fn main() {}
--- /dev/null
+error: expected a trait, found type
+ --> $DIR/impl-opt-out-trait.rs:8:6
+ |
+LL | impl ?const T for S {}
+ | ^^^^^^^^
+
+error: aborting due to previous error
+
--- /dev/null
+// compile-flags: -Z parse-only
+
+#![feature(const_trait_impl)]
+#![feature(const_trait_bound_opt_out)]
+#![allow(incomplete_features)]
+#![allow(bare_trait_objects)]
+
+struct S;
+trait T {}
+
+impl const T {}
+//~^ ERROR `const` cannot modify an inherent impl
+
+fn main() {}
--- /dev/null
+error: `const` cannot modify an inherent impl
+ --> $DIR/inherent-impl.rs:11:6
+ |
+LL | impl const T {}
+ | ^^^^^
+ |
+ = help: only a trait impl can be `const`
+
+error: aborting due to previous error
+
--- /dev/null
+// compile-flags: -Z parse-only
+// check-pass
+
+#![feature(const_trait_bound_opt_out)]
+#![feature(const_trait_impl)]
+#![allow(incomplete_features)]
+
+// For now, this parses since an error does not occur until AST lowering.
+impl ?const T {}
match WRAP_DIRECT_INLINE {
WRAP_DIRECT_INLINE => { panic!("WRAP_DIRECT_INLINE matched itself"); }
//~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
+ //~| ERROR must be annotated with `#[derive(PartialEq, Eq)]`
_ => { println!("WRAP_DIRECT_INLINE did not match itself"); }
}
}
LL | WRAP_DIRECT_INLINE => { panic!("WRAP_DIRECT_INLINE matched itself"); }
| ^^^^^^^^^^^^^^^^^^
-error: aborting due to previous error
+error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]`
+ --> $DIR/cant-hide-behind-direct-struct-embedded.rs:22:9
+ |
+LL | WRAP_DIRECT_INLINE => { panic!("WRAP_DIRECT_INLINE matched itself"); }
+ | ^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
match WRAP_DIRECT_PARAM {
WRAP_DIRECT_PARAM => { panic!("WRAP_DIRECT_PARAM matched itself"); }
//~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
+ //~| ERROR must be annotated with `#[derive(PartialEq, Eq)]`
_ => { println!("WRAP_DIRECT_PARAM did not match itself"); }
}
}
LL | WRAP_DIRECT_PARAM => { panic!("WRAP_DIRECT_PARAM matched itself"); }
| ^^^^^^^^^^^^^^^^^
-error: aborting due to previous error
+error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]`
+ --> $DIR/cant-hide-behind-direct-struct-param.rs:22:9
+ |
+LL | WRAP_DIRECT_PARAM => { panic!("WRAP_DIRECT_PARAM matched itself"); }
+ | ^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
match y {
FOO => { }
//~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
+ //~| ERROR must be annotated with `#[derive(PartialEq, Eq)]`
_ => { }
}
| ^^^
warning: floating-point types cannot be used in patterns
- --> $DIR/match-forbidden-without-eq.rs:20:9
+ --> $DIR/match-forbidden-without-eq.rs:21:9
|
LL | f32::INFINITY => { }
| ^^^^^^^^^^^^^
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
+error: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]`
+ --> $DIR/match-forbidden-without-eq.rs:13:9
+ |
+LL | FOO => { }
+ | ^^^
+
warning: floating-point types cannot be used in patterns
- --> $DIR/match-forbidden-without-eq.rs:20:9
+ --> $DIR/match-forbidden-without-eq.rs:21:9
|
LL | f32::INFINITY => { }
| ^^^^^^^^^^^^^
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
-error: aborting due to previous error
+error: aborting due to 2 previous errors
match [B(1)] {
FOO => { }
//~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
+ //~| ERROR must be annotated with `#[derive(PartialEq, Eq)]`
}
}
LL | FOO => { }
| ^^^
-error: aborting due to previous error
+error: to use a constant of type `B` in a pattern, `B` must be annotated with `#[derive(PartialEq, Eq)]`
+ --> $DIR/match-nonempty-array-forbidden-without-eq.rs:16:9
+ |
+LL | FOO => { }
+ | ^^^
+
+error: aborting due to 2 previous errors
match y {
FOO => { }
//~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
+ //~| ERROR must be annotated with `#[derive(PartialEq, Eq)]`
_ => { }
}
}
LL | FOO => { }
| ^^^
-error: aborting due to previous error
+error: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]`
+ --> $DIR/match-requires-both-partialeq-and-eq.rs:17:9
+ |
+LL | FOO => { }
+ | ^^^
+
+error: aborting due to 2 previous errors
#[built_in_attr] //~ ERROR cannot use a built-in attribute through an import
#[tool_mod::skip] //~ ERROR cannot use a tool module through an import
+ //~| ERROR cannot use a tool module through an import
fn main() {
let _: built_in_type; // OK
}
LL | use cross_crate::*;
| ^^^^^^^^^^^^^^
-error: aborting due to 2 previous errors
+error: cannot use a tool module through an import
+ --> $DIR/cross-crate.rs:8:3
+ |
+LL | #[tool_mod::skip]
+ | ^^^^^^^^
+ |
+note: the tool module imported here
+ --> $DIR/cross-crate.rs:5:5
+ |
+LL | use cross_crate::*;
+ | ^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
#[imported_inline] //~ ERROR cannot use a built-in attribute through an import
#[builtin::imported_inline] //~ ERROR cannot use a built-in attribute through an import
#[imported_rustfmt::skip] //~ ERROR cannot use a tool module through an import
+ //~| ERROR cannot use a tool module through an import
#[tool_mod::imported_rustfmt::skip] //~ ERROR cannot use a tool module through an import
+ //~| ERROR cannot use a tool module through an import
fn main() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: cannot use a tool module through an import
- --> $DIR/prelude-fail-2.rs:18:13
+ --> $DIR/prelude-fail-2.rs:19:13
|
LL | #[tool_mod::imported_rustfmt::skip]
| ^^^^^^^^^^^^^^^^
LL | pub use rustfmt as imported_rustfmt;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error: aborting due to 4 previous errors
+error: cannot use a tool module through an import
+ --> $DIR/prelude-fail-2.rs:17:3
+ |
+LL | #[imported_rustfmt::skip]
+ | ^^^^^^^^^^^^^^^^
+ |
+note: the tool module imported here
+ --> $DIR/prelude-fail-2.rs:10:5
+ |
+LL | use rustfmt as imported_rustfmt;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: cannot use a tool module through an import
+ --> $DIR/prelude-fail-2.rs:19:13
+ |
+LL | #[tool_mod::imported_rustfmt::skip]
+ | ^^^^^^^^^^^^^^^^
+ |
+note: the tool module imported here
+ --> $DIR/prelude-fail-2.rs:12:13
+ |
+LL | pub use rustfmt as imported_rustfmt;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 6 previous errors
-error[E0599]: no method named `foo` found for type `A` in the current scope
+error[E0599]: no method named `foo` found for struct `A` in the current scope
--> $DIR/point-at-arbitrary-self-type-method.rs:8:7
|
LL | struct A;
-error[E0599]: no method named `foo` found for type `A` in the current scope
+error[E0599]: no method named `foo` found for struct `A` in the current scope
--> $DIR/point-at-arbitrary-self-type-trait-method.rs:9:7
|
LL | trait B { fn foo(self: Box<Self>); }
//~^ ERROR cannot find function `bar` in this scope
self.bar();
- //~^ ERROR no method named `bar` found for type
+ //~^ ERROR no method named `bar` found for reference
}
}
LL | bar();
| ^^^ not found in this scope
-error[E0599]: no method named `bar` found for type `&Foo` in the current scope
+error[E0599]: no method named `bar` found for reference `&Foo` in the current scope
--> $DIR/suggest-self-2.rs:20:14
|
LL | self.bar();
-error[E0599]: no method named `f` found for type `()` in the current scope
+error[E0599]: no method named `f` found for unit type `()` in the current scope
--> $DIR/shadowed-trait-methods.rs:13:8
|
LL | ().f()
//~| ERROR cannot determine resolution for the derive macro `Debug`
//~| ERROR cannot determine resolution for the derive macro `PartialEq`
//~| ERROR cannot determine resolution for the derive macro `Eq`
+//~| ERROR cannot determine resolution for the derive macro `Debug`
+//~| ERROR cannot determine resolution for the derive macro `PartialEq`
+//~| ERROR cannot determine resolution for the derive macro `Eq`
struct DerivedOn;
fn main() {}
|
= note: import resolution is stuck, try simplifying macro imports
-error: aborting due to 4 previous errors
+error: cannot determine resolution for the derive macro `Eq`
+ --> $DIR/issue-43927-non-ADT-derive.rs:3:29
+ |
+LL | #![derive(Debug, PartialEq, Eq)] // should be an outer attribute!
+ | ^^
+ |
+ = note: import resolution is stuck, try simplifying macro imports
+
+error: cannot determine resolution for the derive macro `PartialEq`
+ --> $DIR/issue-43927-non-ADT-derive.rs:3:18
+ |
+LL | #![derive(Debug, PartialEq, Eq)] // should be an outer attribute!
+ | ^^^^^^^^^
+ |
+ = note: import resolution is stuck, try simplifying macro imports
+
+error: cannot determine resolution for the derive macro `Debug`
+ --> $DIR/issue-43927-non-ADT-derive.rs:3:11
+ |
+LL | #![derive(Debug, PartialEq, Eq)] // should be an outer attribute!
+ | ^^^^^
+ |
+ = note: import resolution is stuck, try simplifying macro imports
+
+error: aborting due to 7 previous errors
fn no_param_bound(u: usize, m: Myisize) -> usize {
u.f8(42) + u.f9(342) + m.fff(42)
- //~^ ERROR no method named `f9` found for type `usize` in the current scope
- //~| ERROR no method named `fff` found for type `Myisize` in the current scope
+ //~^ ERROR no method named `f9` found
+ //~| ERROR no method named `fff` found
}
fn param_bound<T: ManyImplTrait>(t: T) -> bool {
t.is_str()
- //~^ ERROR no method named `is_str` found for type `T` in the current scope
+ //~^ ERROR no method named `is_str` found
}
fn main() {
LL | u.f8(42) + UnusedTrait::f9(u, 342) + m.fff(42)
| ^^^^^^^^^^^^^^^^^^^^^^^
-error[E0599]: no method named `fff` found for type `Myisize` in the current scope
+error[E0599]: no method named `fff` found for struct `Myisize` in the current scope
--> $DIR/issue-7575.rs:62:30
|
LL | struct Myisize(isize);
LL | fn fff(i: isize) -> isize {
| ^^^^^^^^^^^^^^^^^^^^^^^^^
-error[E0599]: no method named `is_str` found for type `T` in the current scope
+error[E0599]: no method named `is_str` found for type parameter `T` in the current scope
--> $DIR/issue-7575.rs:70:7
|
LL | t.is_str()
fn main() {
println!("{}", MyStruct.foo_one());
- //~^ ERROR no method named `foo_one` found for type `MyStruct` in the current scope
+ //~^ ERROR no method named `foo_one` found
}
-error[E0599]: no method named `foo_one` found for type `MyStruct` in the current scope
+error[E0599]: no method named `foo_one` found for struct `MyStruct` in the current scope
--> $DIR/specialization-trait-not-implemented.rs:22:29
|
LL | struct MyStruct;
#![feature(rustc_attrs)]
#[rustc_dummy = 1usize] //~ ERROR: suffixed literals are not allowed in attributes
+ //~| ERROR: suffixed literals are not allowed in attributes
#[rustc_dummy = 1u8] //~ ERROR: suffixed literals are not allowed in attributes
+ //~| ERROR: suffixed literals are not allowed in attributes
#[rustc_dummy = 1u16] //~ ERROR: suffixed literals are not allowed in attributes
+ //~| ERROR: suffixed literals are not allowed in attributes
#[rustc_dummy = 1u32] //~ ERROR: suffixed literals are not allowed in attributes
+ //~| ERROR: suffixed literals are not allowed in attributes
#[rustc_dummy = 1u64] //~ ERROR: suffixed literals are not allowed in attributes
+ //~| ERROR: suffixed literals are not allowed in attributes
#[rustc_dummy = 1isize] //~ ERROR: suffixed literals are not allowed in attributes
+ //~| ERROR: suffixed literals are not allowed in attributes
#[rustc_dummy = 1i8] //~ ERROR: suffixed literals are not allowed in attributes
+ //~| ERROR: suffixed literals are not allowed in attributes
#[rustc_dummy = 1i16] //~ ERROR: suffixed literals are not allowed in attributes
+ //~| ERROR: suffixed literals are not allowed in attributes
#[rustc_dummy = 1i32] //~ ERROR: suffixed literals are not allowed in attributes
+ //~| ERROR: suffixed literals are not allowed in attributes
#[rustc_dummy = 1i64] //~ ERROR: suffixed literals are not allowed in attributes
+ //~| ERROR: suffixed literals are not allowed in attributes
#[rustc_dummy = 1.0f32] //~ ERROR: suffixed literals are not allowed in attributes
+ //~| ERROR: suffixed literals are not allowed in attributes
#[rustc_dummy = 1.0f64] //~ ERROR: suffixed literals are not allowed in attributes
+ //~| ERROR: suffixed literals are not allowed in attributes
fn main() {}
= help: instead of using a suffixed literal (1u8, 1.0f32, etc.), use an unsuffixed version (1, 1.0, etc.).
error: suffixed literals are not allowed in attributes
- --> $DIR/suffixed-literal-meta.rs:4:17
+ --> $DIR/suffixed-literal-meta.rs:5:17
|
LL | #[rustc_dummy = 1u8]
| ^^^
= help: instead of using a suffixed literal (1u8, 1.0f32, etc.), use an unsuffixed version (1, 1.0, etc.).
error: suffixed literals are not allowed in attributes
- --> $DIR/suffixed-literal-meta.rs:5:17
+ --> $DIR/suffixed-literal-meta.rs:7:17
|
LL | #[rustc_dummy = 1u16]
| ^^^^
= help: instead of using a suffixed literal (1u8, 1.0f32, etc.), use an unsuffixed version (1, 1.0, etc.).
error: suffixed literals are not allowed in attributes
- --> $DIR/suffixed-literal-meta.rs:6:17
+ --> $DIR/suffixed-literal-meta.rs:9:17
|
LL | #[rustc_dummy = 1u32]
| ^^^^
= help: instead of using a suffixed literal (1u8, 1.0f32, etc.), use an unsuffixed version (1, 1.0, etc.).
error: suffixed literals are not allowed in attributes
- --> $DIR/suffixed-literal-meta.rs:7:17
+ --> $DIR/suffixed-literal-meta.rs:11:17
|
LL | #[rustc_dummy = 1u64]
| ^^^^
= help: instead of using a suffixed literal (1u8, 1.0f32, etc.), use an unsuffixed version (1, 1.0, etc.).
error: suffixed literals are not allowed in attributes
- --> $DIR/suffixed-literal-meta.rs:8:17
+ --> $DIR/suffixed-literal-meta.rs:13:17
|
LL | #[rustc_dummy = 1isize]
| ^^^^^^
= help: instead of using a suffixed literal (1u8, 1.0f32, etc.), use an unsuffixed version (1, 1.0, etc.).
error: suffixed literals are not allowed in attributes
- --> $DIR/suffixed-literal-meta.rs:9:17
+ --> $DIR/suffixed-literal-meta.rs:15:17
|
LL | #[rustc_dummy = 1i8]
| ^^^
= help: instead of using a suffixed literal (1u8, 1.0f32, etc.), use an unsuffixed version (1, 1.0, etc.).
error: suffixed literals are not allowed in attributes
- --> $DIR/suffixed-literal-meta.rs:10:17
+ --> $DIR/suffixed-literal-meta.rs:17:17
|
LL | #[rustc_dummy = 1i16]
| ^^^^
= help: instead of using a suffixed literal (1u8, 1.0f32, etc.), use an unsuffixed version (1, 1.0, etc.).
error: suffixed literals are not allowed in attributes
- --> $DIR/suffixed-literal-meta.rs:11:17
+ --> $DIR/suffixed-literal-meta.rs:19:17
|
LL | #[rustc_dummy = 1i32]
| ^^^^
= help: instead of using a suffixed literal (1u8, 1.0f32, etc.), use an unsuffixed version (1, 1.0, etc.).
error: suffixed literals are not allowed in attributes
- --> $DIR/suffixed-literal-meta.rs:12:17
+ --> $DIR/suffixed-literal-meta.rs:21:17
|
LL | #[rustc_dummy = 1i64]
| ^^^^
|
= help: instead of using a suffixed literal (1u8, 1.0f32, etc.), use an unsuffixed version (1, 1.0, etc.).
+error: suffixed literals are not allowed in attributes
+ --> $DIR/suffixed-literal-meta.rs:23:17
+ |
+LL | #[rustc_dummy = 1.0f32]
+ | ^^^^^^
+ |
+ = help: instead of using a suffixed literal (1u8, 1.0f32, etc.), use an unsuffixed version (1, 1.0, etc.).
+
+error: suffixed literals are not allowed in attributes
+ --> $DIR/suffixed-literal-meta.rs:25:17
+ |
+LL | #[rustc_dummy = 1.0f64]
+ | ^^^^^^
+ |
+ = help: instead of using a suffixed literal (1u8, 1.0f32, etc.), use an unsuffixed version (1, 1.0, etc.).
+
+error: suffixed literals are not allowed in attributes
+ --> $DIR/suffixed-literal-meta.rs:3:17
+ |
+LL | #[rustc_dummy = 1usize]
+ | ^^^^^^
+ |
+ = help: instead of using a suffixed literal (1u8, 1.0f32, etc.), use an unsuffixed version (1, 1.0, etc.).
+
+error: suffixed literals are not allowed in attributes
+ --> $DIR/suffixed-literal-meta.rs:5:17
+ |
+LL | #[rustc_dummy = 1u8]
+ | ^^^
+ |
+ = help: instead of using a suffixed literal (1u8, 1.0f32, etc.), use an unsuffixed version (1, 1.0, etc.).
+
+error: suffixed literals are not allowed in attributes
+ --> $DIR/suffixed-literal-meta.rs:7:17
+ |
+LL | #[rustc_dummy = 1u16]
+ | ^^^^
+ |
+ = help: instead of using a suffixed literal (1u8, 1.0f32, etc.), use an unsuffixed version (1, 1.0, etc.).
+
+error: suffixed literals are not allowed in attributes
+ --> $DIR/suffixed-literal-meta.rs:9:17
+ |
+LL | #[rustc_dummy = 1u32]
+ | ^^^^
+ |
+ = help: instead of using a suffixed literal (1u8, 1.0f32, etc.), use an unsuffixed version (1, 1.0, etc.).
+
+error: suffixed literals are not allowed in attributes
+ --> $DIR/suffixed-literal-meta.rs:11:17
+ |
+LL | #[rustc_dummy = 1u64]
+ | ^^^^
+ |
+ = help: instead of using a suffixed literal (1u8, 1.0f32, etc.), use an unsuffixed version (1, 1.0, etc.).
+
error: suffixed literals are not allowed in attributes
--> $DIR/suffixed-literal-meta.rs:13:17
|
+LL | #[rustc_dummy = 1isize]
+ | ^^^^^^
+ |
+ = help: instead of using a suffixed literal (1u8, 1.0f32, etc.), use an unsuffixed version (1, 1.0, etc.).
+
+error: suffixed literals are not allowed in attributes
+ --> $DIR/suffixed-literal-meta.rs:15:17
+ |
+LL | #[rustc_dummy = 1i8]
+ | ^^^
+ |
+ = help: instead of using a suffixed literal (1u8, 1.0f32, etc.), use an unsuffixed version (1, 1.0, etc.).
+
+error: suffixed literals are not allowed in attributes
+ --> $DIR/suffixed-literal-meta.rs:17:17
+ |
+LL | #[rustc_dummy = 1i16]
+ | ^^^^
+ |
+ = help: instead of using a suffixed literal (1u8, 1.0f32, etc.), use an unsuffixed version (1, 1.0, etc.).
+
+error: suffixed literals are not allowed in attributes
+ --> $DIR/suffixed-literal-meta.rs:19:17
+ |
+LL | #[rustc_dummy = 1i32]
+ | ^^^^
+ |
+ = help: instead of using a suffixed literal (1u8, 1.0f32, etc.), use an unsuffixed version (1, 1.0, etc.).
+
+error: suffixed literals are not allowed in attributes
+ --> $DIR/suffixed-literal-meta.rs:21:17
+ |
+LL | #[rustc_dummy = 1i64]
+ | ^^^^
+ |
+ = help: instead of using a suffixed literal (1u8, 1.0f32, etc.), use an unsuffixed version (1, 1.0, etc.).
+
+error: suffixed literals are not allowed in attributes
+ --> $DIR/suffixed-literal-meta.rs:23:17
+ |
LL | #[rustc_dummy = 1.0f32]
| ^^^^^^
|
= help: instead of using a suffixed literal (1u8, 1.0f32, etc.), use an unsuffixed version (1, 1.0, etc.).
error: suffixed literals are not allowed in attributes
- --> $DIR/suffixed-literal-meta.rs:14:17
+ --> $DIR/suffixed-literal-meta.rs:25:17
|
LL | #[rustc_dummy = 1.0f64]
| ^^^^^^
|
= help: instead of using a suffixed literal (1u8, 1.0f32, etc.), use an unsuffixed version (1, 1.0, etc.).
-error: aborting due to 12 previous errors
+error: aborting due to 24 previous errors
+// FIXME: missing sysroot spans (#53081)
+// ignore-i586-unknown-linux-gnu
+// ignore-i586-unknown-linux-musl
+// ignore-i686-unknown-linux-musl
#[deprcated] //~ ERROR cannot find attribute `deprcated` in this scope
fn foo() {}
error[E0658]: attributes starting with `rustc` are reserved for use by the `rustc` compiler
- --> $DIR/attribute-typos.rs:7:3
+ --> $DIR/attribute-typos.rs:11:3
|
LL | #[rustc_err]
| ^^^^^^^^^
= help: add `#![feature(rustc_attrs)]` to the crate attributes to enable
error: cannot find attribute `rustc_err` in this scope
- --> $DIR/attribute-typos.rs:7:3
+ --> $DIR/attribute-typos.rs:11:3
|
LL | #[rustc_err]
| ^^^^^^^^^ help: a built-in attribute with a similar name exists: `rustc_error`
error: cannot find attribute `tests` in this scope
- --> $DIR/attribute-typos.rs:4:3
+ --> $DIR/attribute-typos.rs:8:3
|
LL | #[tests]
| ^^^^^ help: an attribute macro with a similar name exists: `test`
+ |
+ ::: $SRC_DIR/libcore/macros/mod.rs:LL:COL
+ |
+LL | pub macro test($item:item) {
+ | -------------------------- similarly named attribute macro `test` defined here
error: cannot find attribute `deprcated` in this scope
- --> $DIR/attribute-typos.rs:1:3
+ --> $DIR/attribute-typos.rs:5:3
|
LL | #[deprcated]
| ^^^^^^^^^ help: a built-in attribute with a similar name exists: `deprecated`
trait UseString: std::fmt::Debug + GetString {
fn use_string(&self) {
- println!("{:?}", self.get_a()); //~ ERROR no method named `get_a` found for type `&Self`
+ println!("{:?}", self.get_a()); //~ ERROR no method named `get_a` found
}
}
trait UseString2: GetString {
fn use_string(&self) {
- println!("{:?}", self.get_a()); //~ ERROR no method named `get_a` found for type `&Self`
+ println!("{:?}", self.get_a()); //~ ERROR no method named `get_a` found
}
}
trait UseString: std::fmt::Debug {
fn use_string(&self) {
- println!("{:?}", self.get_a()); //~ ERROR no method named `get_a` found for type `&Self`
+ println!("{:?}", self.get_a()); //~ ERROR no method named `get_a` found
}
}
trait UseString2 {
fn use_string(&self) {
- println!("{:?}", self.get_a()); //~ ERROR no method named `get_a` found for type `&Self`
+ println!("{:?}", self.get_a()); //~ ERROR no method named `get_a` found
}
}
-error[E0599]: no method named `get_a` found for type `&Self` in the current scope
+error[E0599]: no method named `get_a` found for reference `&Self` in the current scope
--> $DIR/constrain-trait.rs:15:31
|
LL | println!("{:?}", self.get_a());
LL | trait UseString: std::fmt::Debug + GetString {
| ^^^^^^^^^^^
-error[E0599]: no method named `get_a` found for type `&Self` in the current scope
+error[E0599]: no method named `get_a` found for reference `&Self` in the current scope
--> $DIR/constrain-trait.rs:21:31
|
LL | println!("{:?}", self.get_a());
--> $DIR/fn-or-tuple-struct-without-args.rs:46:20
|
LL | let closure = || 42;
- | -- closure defined here
+ | ----- the found closure
LL | let _: usize = closure;
| ----- ^^^^^^^
| | |
-error[E0599]: no method named `hello` found for type `impl Foo` in the current scope
+error[E0599]: no method named `hello` found for type parameter `impl Foo` in the current scope
--> $DIR/impl-trait-with-missing-trait-bounds-in-arg.rs:15:9
|
LL | foo.hello();
-error[E0599]: no method named `method` found for type `&T` in the current scope
+error[E0599]: no method named `method` found for reference `&T` in the current scope
--> $DIR/issue-21673.rs:6:7
|
LL | x.method()
LL | fn call_method<T: Foo + std::fmt::Debug>(x: &T) {
| ^^^^^^^^
-error[E0599]: no method named `method` found for type `T` in the current scope
+error[E0599]: no method named `method` found for type parameter `T` in the current scope
--> $DIR/issue-21673.rs:10:7
|
LL | x.method()
--- /dev/null
+fn main() {
+ let a_longer_variable_name = 1;
+ println!("{}", a_variable_longer_name); //~ ERROR E0425
+}
--- /dev/null
+error[E0425]: cannot find value `a_variable_longer_name` in this scope
+ --> $DIR/issue-66968-suggest-sorted-words.rs:3:20
+ |
+LL | println!("{}", a_variable_longer_name);
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: a local variable with a similar name exists: `a_longer_variable_name`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0425`.
//~| ERROR the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied
//~| ERROR the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied
- writeln!(fp, "hello world").unwrap(); //~ ERROR no method named `write_fmt` found for type
+ writeln!(fp, "hello world").unwrap(); //~ ERROR no method named `write_fmt` found for struct
}
= note: `std::io::Write` is implemented for `&mut dyn std::io::Write`, but not for `&dyn std::io::Write`
= note: required by `std::io::BufWriter`
-error[E0599]: no method named `write_fmt` found for type `std::io::BufWriter<&dyn std::io::Write>` in the current scope
+error[E0599]: no method named `write_fmt` found for struct `std::io::BufWriter<&dyn std::io::Write>` in the current scope
--> $DIR/mut-borrow-needed-by-trait.rs:22:5
|
LL | writeln!(fp, "hello world").unwrap();
error[E0308]: `if` and `else` have incompatible types
--> $DIR/opaque-type-error.rs:20:9
|
+LL | fn thing_two() -> impl Future<Output = Result<(), ()>> {
+ | ------------------------------------ the found opaque type
+...
LL | / if true {
LL | | thing_one()
| | ----------- expected because of this
fn foo1(s: &str) {
s.as_str();
- //~^ ERROR no method named `as_str` found for type `&str` in the current scope
+ //~^ ERROR no method named `as_str` found
}
fn foo2<'a>(s: &'a str) {
s.as_str();
- //~^ ERROR no method named `as_str` found for type `&'a str` in the current scope
+ //~^ ERROR no method named `as_str` found
}
fn foo3(s: &mut str) {
s.as_str();
- //~^ ERROR no method named `as_str` found for type `&mut str` in the current scope
+ //~^ ERROR no method named `as_str` found
}
fn foo4(s: &&str) {
s.as_str();
- //~^ ERROR no method named `as_str` found for type `&&str` in the current scope
+ //~^ ERROR no method named `as_str` found
}
fn main() {}
-error[E0599]: no method named `as_str` found for type `&str` in the current scope
+error[E0599]: no method named `as_str` found for reference `&str` in the current scope
--> $DIR/remove-as_str.rs:2:7
|
LL | s.as_str();
| -^^^^^^-- help: remove this method call
-error[E0599]: no method named `as_str` found for type `&'a str` in the current scope
+error[E0599]: no method named `as_str` found for reference `&'a str` in the current scope
--> $DIR/remove-as_str.rs:7:7
|
LL | s.as_str();
| -^^^^^^-- help: remove this method call
-error[E0599]: no method named `as_str` found for type `&mut str` in the current scope
+error[E0599]: no method named `as_str` found for mutable reference `&mut str` in the current scope
--> $DIR/remove-as_str.rs:12:7
|
LL | s.as_str();
| -^^^^^^-- help: remove this method call
-error[E0599]: no method named `as_str` found for type `&&str` in the current scope
+error[E0599]: no method named `as_str` found for reference `&&str` in the current scope
--> $DIR/remove-as_str.rs:17:7
|
LL | s.as_str();
let shared_state = RefCell::new(HasAssocMethod);
let state = shared_state.borrow_mut();
state.hello();
- //~^ ERROR no method named `hello` found for type `std::cell::RefMut<'_, HasAssocMethod>`
+ //~^ ERROR no method named `hello` found
}
-error[E0599]: no method named `hello` found for type `std::cell::RefMut<'_, HasAssocMethod>` in the current scope
+error[E0599]: no method named `hello` found for struct `std::cell::RefMut<'_, HasAssocMethod>` in the current scope
--> $DIR/suggest-assoc-fn-call-with-turbofish-through-deref.rs:11:11
|
LL | state.hello();
fn main() {
let x = GenericAssocMethod(33i32);
x.default_hello();
- //~^ ERROR no method named `default_hello` found for type `GenericAssocMethod<i32>`
+ //~^ ERROR no method named `default_hello` found
}
-error[E0599]: no method named `default_hello` found for type `GenericAssocMethod<i32>` in the current scope
+error[E0599]: no method named `default_hello` found for struct `GenericAssocMethod<i32>` in the current scope
--> $DIR/suggest-assoc-fn-call-with-turbofish.rs:9:7
|
LL | struct GenericAssocMethod<T>(T);
-error[E0599]: no method named `bat` found for type `Foo` in the current scope
+error[E0599]: no method named `bat` found for struct `Foo` in the current scope
--> $DIR/suggest-methods.rs:18:7
|
LL | struct Foo;
LL | f.bat(1.0);
| ^^^ help: there is a method with a similar name: `bar`
-error[E0599]: no method named `is_emtpy` found for type `std::string::String` in the current scope
+error[E0599]: no method named `is_emtpy` found for struct `std::string::String` in the current scope
--> $DIR/suggest-methods.rs:21:15
|
LL | let _ = s.is_emtpy();
}
fn main() {
- println!("My shape is {:?}", Shape::Squareee { size: 5}); //~ ERROR no variant `Squareee`
- println!("My shape is {:?}", Shape::Circl { size: 5}); //~ ERROR no variant `Circl`
- println!("My shape is {:?}", Shape::Rombus{ size: 5}); //~ ERROR no variant `Rombus`
+ println!("My shape is {:?}", Shape::Squareee { size: 5}); //~ ERROR no variant named `Squareee`
+ println!("My shape is {:?}", Shape::Circl { size: 5}); //~ ERROR no variant named `Circl`
+ println!("My shape is {:?}", Shape::Rombus{ size: 5}); //~ ERROR no variant named `Rombus`
Shape::Squareee; //~ ERROR no variant
Shape::Circl; //~ ERROR no variant
Shape::Rombus; //~ ERROR no variant
-error: no variant `Squareee` in enum `Shape`
+error[E0599]: no variant named `Squareee` found for enum `Shape`
--> $DIR/suggest-variants.rs:12:41
|
LL | enum Shape {
LL | println!("My shape is {:?}", Shape::Squareee { size: 5});
| ^^^^^^^^ help: there is a variant with a similar name: `Square`
-error: no variant `Circl` in enum `Shape`
+error[E0599]: no variant named `Circl` found for enum `Shape`
--> $DIR/suggest-variants.rs:13:41
|
LL | enum Shape {
LL | println!("My shape is {:?}", Shape::Circl { size: 5});
| ^^^^^ help: there is a variant with a similar name: `Circle`
-error: no variant `Rombus` in enum `Shape`
+error[E0599]: no variant named `Rombus` found for enum `Shape`
--> $DIR/suggest-variants.rs:14:41
|
LL | enum Shape {
LL | println!("My shape is {:?}", Shape::Rombus{ size: 5});
| ^^^^^^ variant not found in `Shape`
-error[E0599]: no variant or associated item named `Squareee` found for type `Shape` in the current scope
+error[E0599]: no variant or associated item named `Squareee` found for enum `Shape` in the current scope
--> $DIR/suggest-variants.rs:15:12
|
LL | enum Shape {
| variant or associated item not found in `Shape`
| help: there is a variant with a similar name: `Square`
-error[E0599]: no variant or associated item named `Circl` found for type `Shape` in the current scope
+error[E0599]: no variant or associated item named `Circl` found for enum `Shape` in the current scope
--> $DIR/suggest-variants.rs:16:12
|
LL | enum Shape {
| variant or associated item not found in `Shape`
| help: there is a variant with a similar name: `Circle`
-error[E0599]: no variant or associated item named `Rombus` found for type `Shape` in the current scope
+error[E0599]: no variant or associated item named `Rombus` found for enum `Shape` in the current scope
--> $DIR/suggest-variants.rs:17:12
|
LL | enum Shape {
LL | #[rustc_def_path]
| ^^^^^^^^^^^^^^^^^
-error: symbol-name(_ZN209_$LT$$u5b$$RF$dyn$u20$impl1..Foo$u2b$Assoc$u20$$u3d$$u20$extern$u20$$u22$C$u22$$u20$fn$LP$$RF$u8$C$$u20$...$RP$$u2b$impl1..AutoTrait$u3b$$u20$_$u5d$$u20$as$u20$impl1..main..$u7b$$u7b$closure$u7d$$u7d$..Bar$GT$6method17h92c563325b7ff21aE)
+error: symbol-name(_ZN209_$LT$$u5b$$RF$dyn$u20$impl1..Foo$u2b$Assoc$u20$$u3d$$u20$extern$u20$$u22$C$u22$$u20$fn$LP$$RF$u8$C$$u20$...$RP$$u2b$impl1..AutoTrait$u3b$$u20$_$u5d$$u20$as$u20$impl1..main..$u7b$$u7b$closure$u7d$$u7d$..Bar$GT$6method17hf07584432cd4d8beE)
--> $DIR/impl1.rs:62:13
|
LL | #[rustc_symbol_name]
| ^^^^^^^^^^^^^^^^^^^^
-error: demangling(<[&dyn impl1::Foo+Assoc = extern "C" fn(&u8, ::.)+impl1::AutoTrait; _] as impl1::main::{{closure}}::Bar>::method::h92c563325b7ff21a)
+error: demangling(<[&dyn impl1::Foo+Assoc = extern "C" fn(&u8, ::.)+impl1::AutoTrait; _] as impl1::main::{{closure}}::Bar>::method::hf07584432cd4d8be)
--> $DIR/impl1.rs:62:13
|
LL | #[rustc_symbol_name]
type B = rustfmt::skip; //~ ERROR expected type, found tool attribute `rustfmt::skip`
#[derive(rustfmt)] //~ ERROR cannot find derive macro `rustfmt` in this scope
+ //~| ERROR cannot find derive macro `rustfmt` in this scope
struct S;
// Interpreted as an unstable custom attribute
LL | #[derive(rustfmt)]
| ^^^^^^^
+error: cannot find derive macro `rustfmt` in this scope
+ --> $DIR/tool-attributes-misplaced-1.rs:4:10
+ |
+LL | #[derive(rustfmt)]
+ | ^^^^^^^
+
error: cannot find attribute `rustfmt` in this scope
- --> $DIR/tool-attributes-misplaced-1.rs:8:3
+ --> $DIR/tool-attributes-misplaced-1.rs:9:3
|
LL | #[rustfmt]
| ^^^^^^^
error: cannot find macro `rustfmt` in this scope
- --> $DIR/tool-attributes-misplaced-1.rs:14:5
+ --> $DIR/tool-attributes-misplaced-1.rs:15:5
|
LL | rustfmt!();
| ^^^^^^^
| ^^^^^^^^^^^^^ not a type
error[E0423]: expected value, found tool module `rustfmt`
- --> $DIR/tool-attributes-misplaced-1.rs:13:5
+ --> $DIR/tool-attributes-misplaced-1.rs:14:5
|
LL | rustfmt;
| ^^^^^^^ not a value
error[E0423]: expected value, found tool attribute `rustfmt::skip`
- --> $DIR/tool-attributes-misplaced-1.rs:16:5
+ --> $DIR/tool-attributes-misplaced-1.rs:17:5
|
LL | rustfmt::skip;
| ^^^^^^^^^^^^^ not a value
-error: aborting due to 7 previous errors
+error: aborting due to 8 previous errors
Some errors have detailed explanations: E0423, E0573.
For more information about an error, try `rustc --explain E0423`.
#[warn(foo::bar)]
//~^ ERROR an unknown tool name found in scoped lint: `foo::bar`
+//~| ERROR an unknown tool name found in scoped lint: `foo::bar`
+//~| ERROR an unknown tool name found in scoped lint: `foo::bar`
fn main() {}
LL | #[warn(foo::bar)]
| ^^^
-error: aborting due to previous error
+error[E0710]: an unknown tool name found in scoped lint: `foo::bar`
+ --> $DIR/tool_lints.rs:1:8
+ |
+LL | #[warn(foo::bar)]
+ | ^^^
+
+error[E0710]: an unknown tool name found in scoped lint: `foo::bar`
+ --> $DIR/tool_lints.rs:1:8
+ |
+LL | #[warn(foo::bar)]
+ | ^^^
+
+error: aborting due to 3 previous errors
fn main() {
let x = &42i32;
- x.foo(); //~ERROR: no method named `foo` found for type `&i32` in the current scope
+ x.foo(); //~ERROR: no method named `foo` found
}
-error[E0599]: no method named `foo` found for type `&i32` in the current scope
+error[E0599]: no method named `foo` found for reference `&i32` in the current scope
--> $DIR/trait-impl-1.rs:15:7
|
LL | x.foo();
// Methods, method call
// a, b, c are resolved as trait items, their traits need to be in scope
- S.a(); //~ ERROR no method named `a` found for type `S` in the current scope
- S.b(); //~ ERROR no method named `b` found for type `S` in the current scope
+ S.a(); //~ ERROR no method named `a` found
+ S.b(); //~ ERROR no method named `b` found
S.c(); // OK
// a, b, c are resolved as inherent items, their traits don't need to be in scope
let c = &S as &dyn C;
// Methods, UFCS
// a, b, c are resolved as trait items, their traits need to be in scope
S::a(&S);
- //~^ ERROR no function or associated item named `a` found for type `S`
+ //~^ ERROR no function or associated item named `a` found
S::b(&S);
- //~^ ERROR no function or associated item named `b` found for type `S`
+ //~^ ERROR no function or associated item named `b` found
S::c(&S); // OK
// a, b, c are resolved as inherent items, their traits don't need to be in scope
C::a(&S); //~ ERROR method `a` is private
// Associated constants
// A, B, C are resolved as trait items, their traits need to be in scope
- S::A; //~ ERROR no associated item named `A` found for type `S` in the current scope
- S::B; //~ ERROR no associated item named `B` found for type `S` in the current scope
+ S::A; //~ ERROR no associated item named `A` found
+ S::B; //~ ERROR no associated item named `B` found
S::C; // OK
// A, B, C are resolved as inherent items, their traits don't need to be in scope
C::A; //~ ERROR associated constant `A` is private
-error[E0599]: no method named `a` found for type `S` in the current scope
+error[E0599]: no method named `a` found for struct `S` in the current scope
--> $DIR/trait-item-privacy.rs:67:7
|
LL | struct S;
= note: the following trait defines an item `a`, perhaps you need to implement it:
candidate #1: `method::A`
-error[E0599]: no method named `b` found for type `S` in the current scope
+error[E0599]: no method named `b` found for struct `S` in the current scope
--> $DIR/trait-item-privacy.rs:68:7
|
LL | struct S;
LL | c.a();
| ^
-error[E0599]: no function or associated item named `a` found for type `S` in the current scope
+error[E0599]: no function or associated item named `a` found for struct `S` in the current scope
--> $DIR/trait-item-privacy.rs:78:8
|
LL | struct S;
= note: the following trait defines an item `a`, perhaps you need to implement it:
candidate #1: `method::A`
-error[E0599]: no function or associated item named `b` found for type `S` in the current scope
+error[E0599]: no function or associated item named `b` found for struct `S` in the current scope
--> $DIR/trait-item-privacy.rs:80:8
|
LL | struct S;
LL | C::a(&S);
| ^^^^
-error[E0599]: no associated item named `A` found for type `S` in the current scope
+error[E0599]: no associated item named `A` found for struct `S` in the current scope
--> $DIR/trait-item-privacy.rs:97:8
|
LL | struct S;
= note: the following trait defines an item `A`, perhaps you need to implement it:
candidate #1: `assoc_const::A`
-error[E0599]: no associated item named `B` found for type `S` in the current scope
+error[E0599]: no associated item named `B` found for struct `S` in the current scope
--> $DIR/trait-item-privacy.rs:98:8
|
LL | struct S;
mod foo {
define_struct! { (foo) } //~ ERROR cannot find type `foo` in this scope
+ //~| ERROR cannot find type `foo` in this scope
}
fn main() {}
LL | define_struct! { (foo) }
| ^^^ not found in this scope
-error: aborting due to 2 previous errors
+error[E0412]: cannot find type `foo` in this scope
+ --> $DIR/test2.rs:11:23
+ |
+LL | define_struct! { (foo) }
+ | ^^^ not found in this scope
+
+error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0412`.
mod foo {
define_struct! { foo } //~ ERROR cannot find type `foo` in this scope
+ //~| ERROR cannot find type `foo` in this scope
}
fn main() {}
LL | define_struct! { foo }
| ^^^ not found in this scope
-error: aborting due to 2 previous errors
+error[E0412]: cannot find type `foo` in this scope
+ --> $DIR/test3.rs:11:22
+ |
+LL | define_struct! { foo }
+ | ^^^ not found in this scope
+
+error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0412`.
| --- ^ expected `i32`, found opaque type
| |
| expected due to this
+...
+LL | type WrongGeneric<T> = impl 'static;
+ | ------------------------------------ the found opaque type
|
= note: expected type `i32`
found opaque type `WrongGeneric::<&{integer}>`
| --- ^ expected `i32`, found opaque type
| |
| expected due to this
+...
+LL | type WrongGeneric<T> = impl 'static;
+ | ------------------------------------ the found opaque type
|
= note: expected type `i32`
found opaque type `WrongGeneric::<&{integer}>`
--- /dev/null
+// Regression test for issue 67856
+
+#![feature(unboxed_closures)]
+#![feature(type_alias_impl_trait)]
+#![feature(fn_traits)]
+
+trait MyTrait {}
+impl MyTrait for () {}
+
+impl<F> FnOnce<()> for &F {
+ //~^ ERROR conflicting implementations
+ //~| ERROR type parameter `F` must be used
+ type Output = impl MyTrait;
+ extern "rust-call" fn call_once(self, _: ()) -> Self::Output {}
+}
+fn main() {}
--- /dev/null
+error[E0119]: conflicting implementations of trait `std::ops::FnOnce<()>` for type `&_`:
+ --> $DIR/incoherent-assoc-imp-trait.rs:10:1
+ |
+LL | impl<F> FnOnce<()> for &F {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: conflicting implementation in crate `core`:
+ - impl<A, F> std::ops::FnOnce<A> for &F
+ where F: std::ops::Fn<A>, F: ?Sized;
+
+error[E0210]: type parameter `F` must be used as the type parameter for some local type (e.g., `MyStruct<F>`)
+ --> $DIR/incoherent-assoc-imp-trait.rs:10:6
+ |
+LL | impl<F> FnOnce<()> for &F {
+ | ^ type parameter `F` must be used as the type parameter for some local type
+ |
+ = note: implementing a foreign trait is only possible if at least one of the types for which is it implemented is local
+ = note: only traits defined in the current crate can be implemented for a type parameter
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0119, E0210.
+For more information about an error, try `rustc --explain E0119`.
error[E0308]: mismatched types
--> $DIR/never_reveal_concrete_type.rs:13:27
|
+LL | type NoReveal = impl std::fmt::Debug;
+ | ------------------------------------- the found opaque type
+...
LL | let _: &'static str = x;
| ------------ ^ expected `&str`, found opaque type
| |
error[E0308]: mismatched types
--> $DIR/no_revealing_outside_defining_module.rs:15:19
|
+LL | pub type Boo = impl ::std::fmt::Debug;
+ | -------------------------------------- the found opaque type
+...
LL | let _: &str = bomp();
| ---- ^^^^^^ expected `&str`, found opaque type
| |
error[E0308]: mismatched types
--> $DIR/no_revealing_outside_defining_module.rs:19:5
|
+LL | pub type Boo = impl ::std::fmt::Debug;
+ | -------------------------------------- the expected opaque type
+...
LL | fn bomp() -> boo::Boo {
| -------- expected `Boo` because of return type
LL | ""
+#![feature(type_alias_impl_trait)] // Needed for single test `type Y = impl Trait<_>`
// This test checks that it is not possible to enable global type
// inference by using the `_` type placeholder.
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures
}
+fn test11(x: &usize) -> &_ {
+//~^ ERROR the type placeholder `_` is not allowed within types on item signatures
+ &x
+}
+
+unsafe fn test12(x: *const usize) -> *const *const _ {
+//~^ ERROR the type placeholder `_` is not allowed within types on item signatures
+ &x
+}
+
impl Clone for Test9 {
fn clone(&self) -> _ { Test9 }
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures
fn assoc_fn_test3() -> _;
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures
}
+
+struct BadStruct<_>(_);
+//~^ ERROR expected identifier, found reserved identifier `_`
+//~| ERROR the type placeholder `_` is not allowed within types on item signatures
+trait BadTrait<_> {}
+//~^ ERROR expected identifier, found reserved identifier `_`
+impl BadTrait<_> for BadStruct<_> {}
+//~^ ERROR the type placeholder `_` is not allowed within types on item signatures
+
+fn impl_trait() -> impl BadTrait<_> {
+//~^ ERROR the type placeholder `_` is not allowed within types on item signatures
+ unimplemented!()
+}
+
+struct BadStruct1<_, _>(_);
+//~^ ERROR expected identifier, found reserved identifier `_`
+//~| ERROR expected identifier, found reserved identifier `_`
+//~| ERROR the name `_` is already used
+//~| ERROR the type placeholder `_` is not allowed within types on item signatures
+struct BadStruct2<_, T>(_, T);
+//~^ ERROR expected identifier, found reserved identifier `_`
+//~| ERROR the type placeholder `_` is not allowed within types on item signatures
+
+type X = Box<_>;
+//~^ ERROR the type placeholder `_` is not allowed within types on item signatures
+
+struct Struct;
+trait Trait<T> {}
+impl Trait<usize> for Struct {}
+type Y = impl Trait<_>;
+//~^ ERROR the type placeholder `_` is not allowed within types on item signatures
+fn foo() -> Y {
+ Struct
+}
+error: expected identifier, found reserved identifier `_`
+ --> $DIR/typeck_type_placeholder_item.rs:146:18
+ |
+LL | struct BadStruct<_>(_);
+ | ^ expected identifier, found reserved identifier
+
+error: expected identifier, found reserved identifier `_`
+ --> $DIR/typeck_type_placeholder_item.rs:149:16
+ |
+LL | trait BadTrait<_> {}
+ | ^ expected identifier, found reserved identifier
+
+error: expected identifier, found reserved identifier `_`
+ --> $DIR/typeck_type_placeholder_item.rs:159:19
+ |
+LL | struct BadStruct1<_, _>(_);
+ | ^ expected identifier, found reserved identifier
+
+error: expected identifier, found reserved identifier `_`
+ --> $DIR/typeck_type_placeholder_item.rs:159:22
+ |
+LL | struct BadStruct1<_, _>(_);
+ | ^ expected identifier, found reserved identifier
+
+error: expected identifier, found reserved identifier `_`
+ --> $DIR/typeck_type_placeholder_item.rs:164:19
+ |
+LL | struct BadStruct2<_, T>(_, T);
+ | ^ expected identifier, found reserved identifier
+
+error[E0403]: the name `_` is already used for a generic parameter in this item's generic parameters
+ --> $DIR/typeck_type_placeholder_item.rs:159:22
+ |
+LL | struct BadStruct1<_, _>(_);
+ | - ^ already used
+ | |
+ | first use of `_`
+
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
- --> $DIR/typeck_type_placeholder_item.rs:4:14
+ --> $DIR/typeck_type_placeholder_item.rs:5:14
|
LL | fn test() -> _ { 5 }
| ^
| help: replace with the correct return type: `i32`
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
- --> $DIR/typeck_type_placeholder_item.rs:7:16
+ --> $DIR/typeck_type_placeholder_item.rs:8:16
|
LL | fn test2() -> (_, _) { (5, 5) }
| -^--^-
| help: replace with the correct return type: `(i32, i32)`
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
- --> $DIR/typeck_type_placeholder_item.rs:10:15
+ --> $DIR/typeck_type_placeholder_item.rs:11:15
|
LL | static TEST3: _ = "test";
| ^
| help: replace `_` with the correct type: `&'static str`
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
- --> $DIR/typeck_type_placeholder_item.rs:13:15
+ --> $DIR/typeck_type_placeholder_item.rs:14:15
|
LL | static TEST4: _ = 145;
| ^
| help: replace `_` with the correct type: `i32`
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
- --> $DIR/typeck_type_placeholder_item.rs:16:15
+ --> $DIR/typeck_type_placeholder_item.rs:17:15
|
LL | static TEST5: (_, _) = (1, 2);
| ^^^^^^ not allowed in type signatures
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
- --> $DIR/typeck_type_placeholder_item.rs:19:13
+ --> $DIR/typeck_type_placeholder_item.rs:20:13
|
LL | fn test6(_: _) { }
| ^ not allowed in type signatures
| ^^^ ^
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
- --> $DIR/typeck_type_placeholder_item.rs:22:18
+ --> $DIR/typeck_type_placeholder_item.rs:23:18
|
LL | fn test6_b<T>(_: _, _: T) { }
| ^ not allowed in type signatures
| ^^^ ^
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
- --> $DIR/typeck_type_placeholder_item.rs:25:30
+ --> $DIR/typeck_type_placeholder_item.rs:26:30
|
LL | fn test6_c<T, K, L, A, B>(_: _, _: (T, K, L, A, B)) { }
| ^ not allowed in type signatures
| ^^^ ^
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
- --> $DIR/typeck_type_placeholder_item.rs:28:13
+ --> $DIR/typeck_type_placeholder_item.rs:29:13
|
LL | fn test7(x: _) { let _x: usize = x; }
| ^ not allowed in type signatures
| ^^^ ^
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
- --> $DIR/typeck_type_placeholder_item.rs:31:22
+ --> $DIR/typeck_type_placeholder_item.rs:32:22
|
LL | fn test8(_f: fn() -> _) { }
| ^ not allowed in type signatures
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
- --> $DIR/typeck_type_placeholder_item.rs:31:22
+ --> $DIR/typeck_type_placeholder_item.rs:32:22
|
LL | fn test8(_f: fn() -> _) { }
| ^ not allowed in type signatures
| ^^^ ^
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
- --> $DIR/typeck_type_placeholder_item.rs:54:8
+ --> $DIR/typeck_type_placeholder_item.rs:46:26
+ |
+LL | fn test11(x: &usize) -> &_ {
+ | -^
+ | ||
+ | |not allowed in type signatures
+ | help: replace with the correct return type: `&&usize`
+
+error[E0121]: the type placeholder `_` is not allowed within types on item signatures
+ --> $DIR/typeck_type_placeholder_item.rs:51:52
+ |
+LL | unsafe fn test12(x: *const usize) -> *const *const _ {
+ | --------------^
+ | | |
+ | | not allowed in type signatures
+ | help: replace with the correct return type: `*const *const usize`
+
+error[E0121]: the type placeholder `_` is not allowed within types on item signatures
+ --> $DIR/typeck_type_placeholder_item.rs:65:8
|
LL | a: _,
| ^ not allowed in type signatures
|
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
- --> $DIR/typeck_type_placeholder_item.rs:60:21
+ --> $DIR/typeck_type_placeholder_item.rs:71:21
|
LL | fn fn_test() -> _ { 5 }
| ^
| help: replace with the correct return type: `i32`
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
- --> $DIR/typeck_type_placeholder_item.rs:63:23
+ --> $DIR/typeck_type_placeholder_item.rs:74:23
|
LL | fn fn_test2() -> (_, _) { (5, 5) }
| -^--^-
| help: replace with the correct return type: `(i32, i32)`
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
- --> $DIR/typeck_type_placeholder_item.rs:66:22
+ --> $DIR/typeck_type_placeholder_item.rs:77:22
|
LL | static FN_TEST3: _ = "test";
| ^
| help: replace `_` with the correct type: `&'static str`
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
- --> $DIR/typeck_type_placeholder_item.rs:69:22
+ --> $DIR/typeck_type_placeholder_item.rs:80:22
|
LL | static FN_TEST4: _ = 145;
| ^
| help: replace `_` with the correct type: `i32`
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
- --> $DIR/typeck_type_placeholder_item.rs:72:22
+ --> $DIR/typeck_type_placeholder_item.rs:83:22
|
LL | static FN_TEST5: (_, _) = (1, 2);
| ^^^^^^ not allowed in type signatures
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
- --> $DIR/typeck_type_placeholder_item.rs:75:20
+ --> $DIR/typeck_type_placeholder_item.rs:86:20
|
LL | fn fn_test6(_: _) { }
| ^ not allowed in type signatures
| ^^^ ^
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
- --> $DIR/typeck_type_placeholder_item.rs:78:20
+ --> $DIR/typeck_type_placeholder_item.rs:89:20
|
LL | fn fn_test7(x: _) { let _x: usize = x; }
| ^ not allowed in type signatures
| ^^^ ^
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
- --> $DIR/typeck_type_placeholder_item.rs:81:29
+ --> $DIR/typeck_type_placeholder_item.rs:92:29
|
LL | fn fn_test8(_f: fn() -> _) { }
| ^ not allowed in type signatures
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
- --> $DIR/typeck_type_placeholder_item.rs:81:29
+ --> $DIR/typeck_type_placeholder_item.rs:92:29
|
LL | fn fn_test8(_f: fn() -> _) { }
| ^ not allowed in type signatures
| ^^^ ^
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
- --> $DIR/typeck_type_placeholder_item.rs:104:12
+ --> $DIR/typeck_type_placeholder_item.rs:115:12
|
LL | a: _,
| ^ not allowed in type signatures
|
error[E0282]: type annotations needed
- --> $DIR/typeck_type_placeholder_item.rs:109:27
+ --> $DIR/typeck_type_placeholder_item.rs:120:27
|
LL | fn fn_test11(_: _) -> (_, _) { panic!() }
| ^^^^^^ cannot infer type
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
- --> $DIR/typeck_type_placeholder_item.rs:109:28
+ --> $DIR/typeck_type_placeholder_item.rs:120:28
|
LL | fn fn_test11(_: _) -> (_, _) { panic!() }
| ^ ^ not allowed in type signatures
| not allowed in type signatures
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
- --> $DIR/typeck_type_placeholder_item.rs:113:30
+ --> $DIR/typeck_type_placeholder_item.rs:124:30
|
LL | fn fn_test12(x: i32) -> (_, _) { (x, x) }
| -^--^-
| help: replace with the correct return type: `(i32, i32)`
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
- --> $DIR/typeck_type_placeholder_item.rs:116:33
+ --> $DIR/typeck_type_placeholder_item.rs:127:33
|
LL | fn fn_test13(x: _) -> (i32, _) { (x, x) }
| ------^-
| help: replace with the correct return type: `(i32, i32)`
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
- --> $DIR/typeck_type_placeholder_item.rs:121:31
+ --> $DIR/typeck_type_placeholder_item.rs:146:21
+ |
+LL | struct BadStruct<_>(_);
+ | ^ not allowed in type signatures
+ |
+help: use type parameters instead
+ |
+LL | struct BadStruct<T>(T);
+ | ^ ^
+
+error[E0121]: the type placeholder `_` is not allowed within types on item signatures
+ --> $DIR/typeck_type_placeholder_item.rs:151:15
+ |
+LL | impl BadTrait<_> for BadStruct<_> {}
+ | ^ ^ not allowed in type signatures
+ | |
+ | not allowed in type signatures
+ |
+help: use type parameters instead
+ |
+LL | impl<T> BadTrait<T> for BadStruct<T> {}
+ | ^^^ ^ ^
+
+error[E0121]: the type placeholder `_` is not allowed within types on item signatures
+ --> $DIR/typeck_type_placeholder_item.rs:154:34
+ |
+LL | fn impl_trait() -> impl BadTrait<_> {
+ | ^ not allowed in type signatures
+
+error[E0121]: the type placeholder `_` is not allowed within types on item signatures
+ --> $DIR/typeck_type_placeholder_item.rs:159:25
+ |
+LL | struct BadStruct1<_, _>(_);
+ | ^ not allowed in type signatures
+ |
+help: use type parameters instead
+ |
+LL | struct BadStruct1<T, _>(T);
+ | ^ ^
+
+error[E0121]: the type placeholder `_` is not allowed within types on item signatures
+ --> $DIR/typeck_type_placeholder_item.rs:164:25
+ |
+LL | struct BadStruct2<_, T>(_, T);
+ | ^ not allowed in type signatures
+ |
+help: use type parameters instead
+ |
+LL | struct BadStruct2<K, T>(K, T);
+ | ^ ^
+
+error[E0121]: the type placeholder `_` is not allowed within types on item signatures
+ --> $DIR/typeck_type_placeholder_item.rs:168:14
+ |
+LL | type X = Box<_>;
+ | ^ not allowed in type signatures
+
+error[E0121]: the type placeholder `_` is not allowed within types on item signatures
+ --> $DIR/typeck_type_placeholder_item.rs:42:27
+ |
+LL | fn test10(&self, _x : _) { }
+ | ^ not allowed in type signatures
+ |
+help: use type parameters instead
+ |
+LL | fn test10<T>(&self, _x : T) { }
+ | ^^^ ^
+
+error[E0121]: the type placeholder `_` is not allowed within types on item signatures
+ --> $DIR/typeck_type_placeholder_item.rs:132:31
|
LL | fn method_test1(&self, x: _);
| ^ not allowed in type signatures
| ^^^ ^
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
- --> $DIR/typeck_type_placeholder_item.rs:123:31
+ --> $DIR/typeck_type_placeholder_item.rs:134:31
|
LL | fn method_test2(&self, x: _) -> _;
| ^ ^ not allowed in type signatures
| ^^^ ^ ^
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
- --> $DIR/typeck_type_placeholder_item.rs:125:31
+ --> $DIR/typeck_type_placeholder_item.rs:136:31
|
LL | fn method_test3(&self) -> _;
| ^ not allowed in type signatures
| ^^^ ^
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
- --> $DIR/typeck_type_placeholder_item.rs:127:26
+ --> $DIR/typeck_type_placeholder_item.rs:138:26
|
LL | fn assoc_fn_test1(x: _);
| ^ not allowed in type signatures
| ^^^ ^
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
- --> $DIR/typeck_type_placeholder_item.rs:129:26
+ --> $DIR/typeck_type_placeholder_item.rs:140:26
|
LL | fn assoc_fn_test2(x: _) -> _;
| ^ ^ not allowed in type signatures
| ^^^ ^ ^
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
- --> $DIR/typeck_type_placeholder_item.rs:131:28
+ --> $DIR/typeck_type_placeholder_item.rs:142:28
|
LL | fn assoc_fn_test3() -> _;
| ^ not allowed in type signatures
| ^^^ ^
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
- --> $DIR/typeck_type_placeholder_item.rs:38:24
+ --> $DIR/typeck_type_placeholder_item.rs:60:37
|
-LL | fn test9(&self) -> _ { () }
- | ^
- | |
- | not allowed in type signatures
- | help: replace with the correct return type: `()`
+LL | fn clone_from(&mut self, other: _) { *self = Test9; }
+ | ^ not allowed in type signatures
+ |
+help: use type parameters instead
+ |
+LL | fn clone_from<T>(&mut self, other: T) { *self = Test9; }
+ | ^^^ ^
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
- --> $DIR/typeck_type_placeholder_item.rs:41:27
+ --> $DIR/typeck_type_placeholder_item.rs:102:34
|
-LL | fn test10(&self, _x : _) { }
- | ^ not allowed in type signatures
+LL | fn fn_test10(&self, _x : _) { }
+ | ^ not allowed in type signatures
|
help: use type parameters instead
|
-LL | fn test10<T>(&self, _x : T) { }
- | ^^^ ^
+LL | fn fn_test10<T>(&self, _x : T) { }
+ | ^^^ ^
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
- --> $DIR/typeck_type_placeholder_item.rs:46:24
+ --> $DIR/typeck_type_placeholder_item.rs:110:41
|
-LL | fn clone(&self) -> _ { Test9 }
+LL | fn clone_from(&mut self, other: _) { *self = FnTest9; }
+ | ^ not allowed in type signatures
+ |
+help: use type parameters instead
+ |
+LL | fn clone_from<T>(&mut self, other: T) { *self = FnTest9; }
+ | ^^^ ^
+
+error[E0121]: the type placeholder `_` is not allowed within types on item signatures
+ --> $DIR/typeck_type_placeholder_item.rs:174:21
+ |
+LL | type Y = impl Trait<_>;
+ | ^ not allowed in type signatures
+
+error[E0121]: the type placeholder `_` is not allowed within types on item signatures
+ --> $DIR/typeck_type_placeholder_item.rs:39:24
+ |
+LL | fn test9(&self) -> _ { () }
| ^
| |
| not allowed in type signatures
- | help: replace with the correct return type: `Test9`
+ | help: replace with the correct return type: `()`
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
- --> $DIR/typeck_type_placeholder_item.rs:49:37
+ --> $DIR/typeck_type_placeholder_item.rs:57:24
|
-LL | fn clone_from(&mut self, other: _) { *self = Test9; }
- | ^ not allowed in type signatures
- |
-help: use type parameters instead
- |
-LL | fn clone_from<T>(&mut self, other: T) { *self = Test9; }
- | ^^^ ^
+LL | fn clone(&self) -> _ { Test9 }
+ | ^
+ | |
+ | not allowed in type signatures
+ | help: replace with the correct return type: `Test9`
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
- --> $DIR/typeck_type_placeholder_item.rs:88:31
+ --> $DIR/typeck_type_placeholder_item.rs:99:31
|
LL | fn fn_test9(&self) -> _ { () }
| ^
| help: replace with the correct return type: `()`
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
- --> $DIR/typeck_type_placeholder_item.rs:91:34
- |
-LL | fn fn_test10(&self, _x : _) { }
- | ^ not allowed in type signatures
- |
-help: use type parameters instead
- |
-LL | fn fn_test10<T>(&self, _x : T) { }
- | ^^^ ^
-
-error[E0121]: the type placeholder `_` is not allowed within types on item signatures
- --> $DIR/typeck_type_placeholder_item.rs:96:28
+ --> $DIR/typeck_type_placeholder_item.rs:107:28
|
LL | fn clone(&self) -> _ { FnTest9 }
| ^
| not allowed in type signatures
| help: replace with the correct return type: `main::FnTest9`
-error[E0121]: the type placeholder `_` is not allowed within types on item signatures
- --> $DIR/typeck_type_placeholder_item.rs:99:41
- |
-LL | fn clone_from(&mut self, other: _) { *self = FnTest9; }
- | ^ not allowed in type signatures
- |
-help: use type parameters instead
- |
-LL | fn clone_from<T>(&mut self, other: T) { *self = FnTest9; }
- | ^^^ ^
-
-error: aborting due to 40 previous errors
+error: aborting due to 55 previous errors
-Some errors have detailed explanations: E0121, E0282.
+Some errors have detailed explanations: E0121, E0282, E0403.
For more information about an error, try `rustc --explain E0121`.
<u8 as A>::N::NN; //~ ERROR cannot find associated type `N` in `A`
let _: <u8 as Tr>::Y::NN; //~ ERROR ambiguous associated type
let _: <u8 as E>::Y::NN; //~ ERROR expected associated type, found variant `E::Y`
- <u8 as Tr>::Y::NN; //~ ERROR no associated item named `NN` found for type `<u8 as Tr>::Y`
+ <u8 as Tr>::Y::NN; //~ ERROR no associated item named `NN` found
<u8 as E>::Y::NN; //~ ERROR expected associated type, found variant `E::Y`
let _: <u8 as Tr::N>::NN; //~ ERROR cannot find associated type `NN` in `Tr::N`
let _: <u8 as Dr>::Z; //~ ERROR expected associated type, found method `Dr::Z`
<u8 as Dr>::X; //~ ERROR expected method or associated constant, found associated type `Dr::X`
let _: <u8 as Dr>::Z::N; //~ ERROR expected associated type, found method `Dr::Z`
- <u8 as Dr>::X::N; //~ ERROR no associated item named `N` found for type `<u8 as Dr>::X`
+ <u8 as Dr>::X::N; //~ ERROR no associated item named `N` found
}
LL | let _: <u8 as Tr>::Y::NN;
| ^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax: `<<u8 as Tr>::Y as Trait>::NN`
-error[E0599]: no associated item named `NN` found for type `<u8 as Tr>::Y` in the current scope
+error[E0599]: no associated item named `NN` found for associated type `<u8 as Tr>::Y` in the current scope
--> $DIR/ufcs-partially-resolved.rs:38:20
|
LL | <u8 as Tr>::Y::NN;
| ^^ associated item not found in `<u8 as Tr>::Y`
-error[E0599]: no associated item named `N` found for type `<u8 as Dr>::X` in the current scope
+error[E0599]: no associated item named `N` found for associated type `<u8 as Dr>::X` in the current scope
--> $DIR/ufcs-partially-resolved.rs:55:20
|
LL | <u8 as Dr>::X::N;
-error[E0599]: no method named `call` found for type `[closure@$DIR/unboxed-closures-static-call-wrong-trait.rs:6:26: 6:31]` in the current scope
+error[E0599]: no method named `call` found for closure `[closure@$DIR/unboxed-closures-static-call-wrong-trait.rs:6:26: 6:31]` in the current scope
--> $DIR/unboxed-closures-static-call-wrong-trait.rs:7:10
|
LL | mut_.call((0, ));
-error[E0599]: no method named `deref` found for type `&()` in the current scope
+error[E0599]: no method named `deref` found for reference `&()` in the current scope
--> $DIR/hygiene.rs:38:11
|
LL | (&()).deref();
LL | use std::ops::Deref;
|
-error[E0599]: no method named `deref_mut` found for type `&mut ()` in the current scope
+error[E0599]: no method named `deref_mut` found for mutable reference `&mut ()` in the current scope
--> $DIR/hygiene.rs:39:15
|
LL | (&mut ()).deref_mut();
-error[E0599]: no method named `deref` found for type `&()` in the current scope
+error[E0599]: no method named `deref` found for reference `&()` in the current scope
--> $DIR/shadow.rs:19:11
|
LL | x.deref();
fn main() {
match C {
C => {} //~ ERROR cannot use unions in constant patterns
+ //~| ERROR cannot use unions in constant patterns
_ => {}
}
}
LL | C => {}
| ^
-error: aborting due to previous error
+error: cannot use unions in constant patterns
+ --> $DIR/union-const-pat.rs:10:9
+ |
+LL | C => {}
+ | ^
+
+error: aborting due to 2 previous errors
fn main() {
let u = U5 { a: ManuallyDrop::new(CloneNoCopy) };
- let w = u.clone(); //~ ERROR no method named `clone` found for type `U5<CloneNoCopy>`
+ let w = u.clone(); //~ ERROR no method named `clone` found for union `U5<CloneNoCopy>`
}
|
= note: required by `std::clone::AssertParamIsCopy`
-error[E0599]: no method named `clone` found for type `U5<CloneNoCopy>` in the current scope
+error[E0599]: no method named `clone` found for union `U5<CloneNoCopy>` in the current scope
--> $DIR/union-derive-clone.rs:37:15
|
LL | union U5<T> {
-error[E0599]: no method named `clone` found for type `std::boxed::Box<dyn Foo>` in the current scope
+error[E0599]: no method named `clone` found for struct `std::boxed::Box<dyn Foo>` in the current scope
--> $DIR/unique-object-noncopyable.rs:24:16
|
LL | let _z = y.clone();
-error[E0599]: no method named `clone` found for type `std::boxed::Box<R>` in the current scope
+error[E0599]: no method named `clone` found for struct `std::boxed::Box<R>` in the current scope
--> $DIR/unique-pinned-nocopy.rs:12:16
|
LL | let _j = i.clone();
#![deny(foo::bar)] //~ ERROR an unknown tool name found in scoped lint: `foo::bar`
+ //~| ERROR an unknown tool name found in scoped lint: `foo::bar`
+ //~| ERROR an unknown tool name found in scoped lint: `foo::bar`
#[allow(foo::bar)] //~ ERROR an unknown tool name found in scoped lint: `foo::bar`
+ //~| ERROR an unknown tool name found in scoped lint: `foo::bar`
+ //~| ERROR an unknown tool name found in scoped lint: `foo::bar`
fn main() {}
| ^^^
error[E0710]: an unknown tool name found in scoped lint: `foo::bar`
- --> $DIR/unknown-lint-tool-name.rs:3:9
+ --> $DIR/unknown-lint-tool-name.rs:5:9
|
LL | #[allow(foo::bar)]
| ^^^
-error: aborting due to 2 previous errors
+error[E0710]: an unknown tool name found in scoped lint: `foo::bar`
+ --> $DIR/unknown-lint-tool-name.rs:1:9
+ |
+LL | #![deny(foo::bar)]
+ | ^^^
+
+error[E0710]: an unknown tool name found in scoped lint: `foo::bar`
+ --> $DIR/unknown-lint-tool-name.rs:5:9
+ |
+LL | #[allow(foo::bar)]
+ | ^^^
+
+error[E0710]: an unknown tool name found in scoped lint: `foo::bar`
+ --> $DIR/unknown-lint-tool-name.rs:1:9
+ |
+LL | #![deny(foo::bar)]
+ | ^^^
+
+error[E0710]: an unknown tool name found in scoped lint: `foo::bar`
+ --> $DIR/unknown-lint-tool-name.rs:5:9
+ |
+LL | #[allow(foo::bar)]
+ | ^^^
+
+error: aborting due to 6 previous errors
-error[E0599]: no function or associated item named `lol` found for type `dyn Foo<_>` in the current scope
+error[E0599]: no function or associated item named `lol` found for trait object `dyn Foo<_>` in the current scope
--> $DIR/unspecified-self-in-trait-ref.rs:10:18
|
LL | let a = Foo::lol();
| ^^^ function or associated item not found in `dyn Foo<_>`
-error[E0599]: no function or associated item named `lol` found for type `dyn Foo<_>` in the current scope
+error[E0599]: no function or associated item named `lol` found for trait object `dyn Foo<_>` in the current scope
--> $DIR/unspecified-self-in-trait-ref.rs:12:23
|
LL | let b = Foo::<_>::lol();
| ^^^ function or associated item not found in `dyn Foo<_>`
-error[E0599]: no function or associated item named `lol` found for type `dyn Bar<_, _>` in the current scope
+error[E0599]: no function or associated item named `lol` found for trait object `dyn Bar<_, _>` in the current scope
--> $DIR/unspecified-self-in-trait-ref.rs:14:18
|
LL | let c = Bar::lol();
| ^^^ function or associated item not found in `dyn Bar<_, _>`
-error[E0599]: no function or associated item named `lol` found for type `dyn Bar<usize, _>` in the current scope
+error[E0599]: no function or associated item named `lol` found for trait object `dyn Bar<usize, _>` in the current scope
--> $DIR/unspecified-self-in-trait-ref.rs:16:30
|
LL | let d = Bar::<usize, _>::lol();
mod foo {
use ::super::{S, Z}; //~ ERROR global paths cannot start with `super`
+ //~| ERROR global paths cannot start with `super`
pub fn g() {
use ::super::main; //~ ERROR global paths cannot start with `super`
| ^^^^^ global paths cannot start with `super`
error[E0433]: failed to resolve: global paths cannot start with `super`
- --> $DIR/use-super-global-path.rs:10:15
+ --> $DIR/use-super-global-path.rs:7:11
+ |
+LL | use ::super::{S, Z};
+ | ^^^^^ global paths cannot start with `super`
+
+error[E0433]: failed to resolve: global paths cannot start with `super`
+ --> $DIR/use-super-global-path.rs:11:15
|
LL | use ::super::main;
| ^^^^^ global paths cannot start with `super`
error[E0425]: cannot find function `main` in this scope
- --> $DIR/use-super-global-path.rs:11:9
+ --> $DIR/use-super-global-path.rs:12:9
|
LL | main();
| ^^^^ not found in this scope
LL | use main;
|
-error: aborting due to 3 previous errors
+error: aborting due to 4 previous errors
Some errors have detailed explanations: E0425, E0433.
For more information about an error, try `rustc --explain E0425`.
-Subproject commit e8642c7a2900bed28003a98d4db8b62290ac802f
+Subproject commit 920cdb59e1edf2c4cb2f266fa521f12c1b97a499
#![crate_name = "compiletest"]
-#![feature(test)]
+#![feature(vec_remove_item)]
#![deny(warnings)]
+// The `test` crate is the only unstable feature
+// allowed here, just to share similar code.
+#![feature(test)]
extern crate test;
rustc.args(&["--error-format", "json"]);
}
rustc.arg("-Zui-testing");
+ rustc.arg("-Zdeduplicate-diagnostics=no");
}
Ui => {
if !self.props.compile_flags.iter().any(|s| s.starts_with("--error-format")) {
rustc.args(&["--error-format", "json"]);
}
rustc.arg("-Zui-testing");
+ rustc.arg("-Zdeduplicate-diagnostics=no");
}
MirOpt => {
rustc.args(&[
("powerpc", "powerpc"),
("powerpc64", "powerpc64"),
("powerpc64le", "powerpc64"),
+ ("riscv64gc", "riscv64"),
("s390x", "s390x"),
("sparc", "sparc"),
("sparc64", "sparc64"),