Auto merge of #73504 - RalfJung:rollup-iy8hsvl, r=RalfJung
Rollup of 10 pull requests Successful merges: - #72280 (Fix up autoderef when reborrowing) - #72785 (linker: MSVC supports linking static libraries as a whole archive) - #73011 (first stage of implementing LLVM code coverage) - #73044 (compiletest: Add directives to detect sanitizer support) - #73054 (memory access sanity checks: abort instead of panic) - #73136 (Change how compiler-builtins gets many CGUs) - #73280 (Add E0763) - #73317 (bootstrap: read config from $RUST_BOOTSTRAP_CONFIG) - #73350 (bootstrap/install.rs: support a nonexistent `prefix` in `x.py install`) - #73352 (Speed up bootstrap a little.) Failed merges: r? @ghost
This commit is contained in:
commit
72417d84fb
13
Cargo.toml
13
Cargo.toml
@ -42,6 +42,19 @@ debug-assertions = false
|
||||
debug = false
|
||||
debug-assertions = false
|
||||
|
||||
[profile.release.package.compiler_builtins]
|
||||
# For compiler-builtins we always use a high number of codegen units.
|
||||
# The goal here is to place every single intrinsic into its own object
|
||||
# file to avoid symbol clashes with the system libgcc if possible. Note
|
||||
# that this number doesn't actually produce this many object files, we
|
||||
# just don't create more than this number of object files.
|
||||
#
|
||||
# It's a bit of a bummer that we have to pass this here, unfortunately.
|
||||
# Ideally this would be specified through an env var to Cargo so Cargo
|
||||
# knows how many CGUs are for this specific crate, but for now
|
||||
# per-crate configuration isn't specifiable in the environment.
|
||||
codegen-units = 10000
|
||||
|
||||
# We want the RLS to use the version of Cargo that we've got vendored in this
|
||||
# repository to ensure that the same exact version of Cargo is used by both the
|
||||
# RLS and the Cargo binary itself. The RLS depends on Cargo as a git repository
|
||||
|
@ -209,7 +209,8 @@
|
||||
# Build the sanitizer runtimes
|
||||
#sanitizers = false
|
||||
|
||||
# Build the profiler runtime
|
||||
# Build the profiler runtime (required when compiling with options that depend
|
||||
# on this runtime, such as `-C profile-generate` or `-Z instrument-coverage`).
|
||||
#profiler = false
|
||||
|
||||
# Indicates whether the native libraries linked into Cargo will be statically
|
||||
|
@ -894,7 +894,7 @@ def bootstrap(help_triggered):
|
||||
build.clean = args.clean
|
||||
|
||||
try:
|
||||
toml_path = args.config or 'config.toml'
|
||||
toml_path = os.getenv('RUST_BOOTSTRAP_CONFIG') or args.config or 'config.toml'
|
||||
if not os.path.exists(toml_path):
|
||||
toml_path = os.path.join(build.rust_root, toml_path)
|
||||
|
||||
@ -947,6 +947,7 @@ def bootstrap(help_triggered):
|
||||
env["SRC"] = build.rust_root
|
||||
env["BOOTSTRAP_PARENT_ID"] = str(os.getpid())
|
||||
env["BOOTSTRAP_PYTHON"] = sys.executable
|
||||
env["BOOTSTRAP_CONFIG"] = toml_path
|
||||
env["BUILD_DIR"] = build.build_dir
|
||||
env["RUSTC_BOOTSTRAP"] = '1'
|
||||
env["CARGO"] = build.cargo()
|
||||
|
@ -99,9 +99,21 @@ struct StepDescription {
|
||||
name: &'static str,
|
||||
}
|
||||
|
||||
/// Collection of paths used to match a task rule.
|
||||
#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)]
|
||||
pub enum PathSet {
|
||||
/// A collection of individual paths.
|
||||
///
|
||||
/// These are generally matched as a path suffix. For example, a
|
||||
/// command-line value of `libstd` will match if `src/libstd` is in the
|
||||
/// set.
|
||||
Set(BTreeSet<PathBuf>),
|
||||
/// A "suite" of paths.
|
||||
///
|
||||
/// These can match as a path suffix (like `Set`), or as a prefix. For
|
||||
/// example, a command-line value of `src/test/ui/abi/variadic-ffi.rs`
|
||||
/// will match `src/test/ui`. A command-line value of `ui` would also
|
||||
/// match `src/test/ui`.
|
||||
Suite(PathBuf),
|
||||
}
|
||||
|
||||
@ -251,21 +263,33 @@ impl<'a> ShouldRun<'a> {
|
||||
self
|
||||
}
|
||||
|
||||
// Unlike `krate` this will create just one pathset. As such, it probably shouldn't actually
|
||||
// ever be used, but as we transition to having all rules properly handle passing krate(...) by
|
||||
// actually doing something different for every crate passed.
|
||||
/// Indicates it should run if the command-line selects the given crate or
|
||||
/// any of its (local) dependencies.
|
||||
///
|
||||
/// Compared to `krate`, this treats the dependencies as aliases for the
|
||||
/// same job. Generally it is preferred to use `krate`, and treat each
|
||||
/// individual path separately. For example `./x.py test src/liballoc`
|
||||
/// (which uses `krate`) will test just `liballoc`. However, `./x.py check
|
||||
/// src/liballoc` (which uses `all_krates`) will check all of `libtest`.
|
||||
/// `all_krates` should probably be removed at some point.
|
||||
pub fn all_krates(mut self, name: &str) -> Self {
|
||||
let mut set = BTreeSet::new();
|
||||
for krate in self.builder.in_tree_crates(name) {
|
||||
set.insert(PathBuf::from(&krate.path));
|
||||
let path = krate.local_path(self.builder);
|
||||
set.insert(path);
|
||||
}
|
||||
self.paths.insert(PathSet::Set(set));
|
||||
self
|
||||
}
|
||||
|
||||
/// Indicates it should run if the command-line selects the given crate or
|
||||
/// any of its (local) dependencies.
|
||||
///
|
||||
/// `make_run` will be called separately for each matching command-line path.
|
||||
pub fn krate(mut self, name: &str) -> Self {
|
||||
for krate in self.builder.in_tree_crates(name) {
|
||||
self.paths.insert(PathSet::one(&krate.path));
|
||||
let path = krate.local_path(self.builder);
|
||||
self.paths.insert(PathSet::one(path));
|
||||
}
|
||||
self
|
||||
}
|
||||
@ -488,13 +512,19 @@ impl<'a> Builder<'a> {
|
||||
should_run = (desc.should_run)(should_run);
|
||||
}
|
||||
let mut help = String::from("Available paths:\n");
|
||||
let mut add_path = |path: &Path| {
|
||||
help.push_str(&format!(" ./x.py {} {}\n", subcommand, path.display()));
|
||||
};
|
||||
for pathset in should_run.paths {
|
||||
if let PathSet::Set(set) = pathset {
|
||||
set.iter().for_each(|path| {
|
||||
help.push_str(
|
||||
format!(" ./x.py {} {}\n", subcommand, path.display()).as_str(),
|
||||
)
|
||||
})
|
||||
match pathset {
|
||||
PathSet::Set(set) => {
|
||||
for path in set {
|
||||
add_path(&path);
|
||||
}
|
||||
}
|
||||
PathSet::Suite(path) => {
|
||||
add_path(&path.join("..."));
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(help)
|
||||
|
@ -548,8 +548,8 @@ impl Step for Rustc {
|
||||
// Find dependencies for top level crates.
|
||||
let mut compiler_crates = HashSet::new();
|
||||
for root_crate in &["rustc_driver", "rustc_codegen_llvm", "rustc_codegen_ssa"] {
|
||||
let interned_root_crate = INTERNER.intern_str(root_crate);
|
||||
find_compiler_crates(builder, &interned_root_crate, &mut compiler_crates);
|
||||
compiler_crates
|
||||
.extend(builder.in_tree_crates(root_crate).into_iter().map(|krate| krate.name));
|
||||
}
|
||||
|
||||
for krate in &compiler_crates {
|
||||
@ -564,22 +564,6 @@ impl Step for Rustc {
|
||||
}
|
||||
}
|
||||
|
||||
fn find_compiler_crates(
|
||||
builder: &Builder<'_>,
|
||||
name: &Interned<String>,
|
||||
crates: &mut HashSet<Interned<String>>,
|
||||
) {
|
||||
// Add current crate.
|
||||
crates.insert(*name);
|
||||
|
||||
// Look for dependencies.
|
||||
for dep in builder.crates.get(name).unwrap().deps.iter() {
|
||||
if builder.crates.get(dep).unwrap().is_local(builder) {
|
||||
find_compiler_crates(builder, dep, crates);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct Rustdoc {
|
||||
stage: u32,
|
||||
|
@ -3,18 +3,16 @@
|
||||
//! This module implements the command-line parsing of the build system which
|
||||
//! has various flags to configure how it's run.
|
||||
|
||||
use std::fs;
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
use std::process;
|
||||
|
||||
use getopts::Options;
|
||||
|
||||
use crate::builder::Builder;
|
||||
use crate::config::Config;
|
||||
use crate::metadata;
|
||||
use crate::{Build, DocTests};
|
||||
|
||||
use crate::cache::{Interned, INTERNER};
|
||||
use crate::config::Config;
|
||||
use crate::{Build, DocTests};
|
||||
|
||||
/// Deserialized version of all flags for this compile.
|
||||
pub struct Flags {
|
||||
@ -438,19 +436,12 @@ Arguments:
|
||||
// Get any optional paths which occur after the subcommand
|
||||
let paths = matches.free[1..].iter().map(|p| p.into()).collect::<Vec<PathBuf>>();
|
||||
|
||||
let cfg_file = matches.opt_str("config").map(PathBuf::from).or_else(|| {
|
||||
if fs::metadata("config.toml").is_ok() {
|
||||
Some(PathBuf::from("config.toml"))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
let cfg_file = env::var_os("BOOTSTRAP_CONFIG").map(PathBuf::from);
|
||||
|
||||
// All subcommands except `clean` can have an optional "Available paths" section
|
||||
if matches.opt_present("verbose") {
|
||||
let config = Config::parse(&["build".to_string()]);
|
||||
let mut build = Build::new(config);
|
||||
metadata::build(&mut build);
|
||||
let build = Build::new(config);
|
||||
|
||||
let maybe_rules_help = Builder::get_help(&build, subcommand.as_str());
|
||||
extra_help.push_str(maybe_rules_help.unwrap_or_default().as_str());
|
||||
|
@ -70,7 +70,10 @@ fn install_sh(
|
||||
let libdir_default = PathBuf::from("lib");
|
||||
let mandir_default = datadir_default.join("man");
|
||||
let prefix = builder.config.prefix.as_ref().map_or(prefix_default, |p| {
|
||||
fs::canonicalize(p).unwrap_or_else(|_| panic!("could not canonicalize {}", p.display()))
|
||||
fs::create_dir_all(p)
|
||||
.unwrap_or_else(|err| panic!("could not create {}: {}", p.display(), err));
|
||||
fs::canonicalize(p)
|
||||
.unwrap_or_else(|err| panic!("could not canonicalize {}: {}", p.display(), err))
|
||||
});
|
||||
let sysconfdir = builder.config.sysconfdir.as_ref().unwrap_or(&sysconfdir_default);
|
||||
let datadir = builder.config.datadir.as_ref().unwrap_or(&datadir_default);
|
||||
|
@ -270,12 +270,7 @@ struct Crate {
|
||||
}
|
||||
|
||||
impl Crate {
|
||||
fn is_local(&self, build: &Build) -> bool {
|
||||
self.path.starts_with(&build.config.src) && !self.path.to_string_lossy().ends_with("_shim")
|
||||
}
|
||||
|
||||
fn local_path(&self, build: &Build) -> PathBuf {
|
||||
assert!(self.is_local(build));
|
||||
self.path.strip_prefix(&build.config.src).unwrap().into()
|
||||
}
|
||||
}
|
||||
@ -1090,17 +1085,29 @@ impl Build {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a Vec of all the dependencies of the given root crate,
|
||||
/// including transitive dependencies and the root itself. Only includes
|
||||
/// "local" crates (those in the local source tree, not from a registry).
|
||||
fn in_tree_crates(&self, root: &str) -> Vec<&Crate> {
|
||||
let mut ret = Vec::new();
|
||||
let mut list = vec![INTERNER.intern_str(root)];
|
||||
let mut visited = HashSet::new();
|
||||
while let Some(krate) = list.pop() {
|
||||
let krate = &self.crates[&krate];
|
||||
if krate.is_local(self) {
|
||||
ret.push(krate);
|
||||
}
|
||||
ret.push(krate);
|
||||
for dep in &krate.deps {
|
||||
if visited.insert(dep) && dep != "build_helper" {
|
||||
// Don't include optional deps if their features are not
|
||||
// enabled. Ideally this would be computed from `cargo
|
||||
// metadata --features …`, but that is somewhat slow. Just
|
||||
// skip `build_helper` since there aren't any operations we
|
||||
// want to perform on it. In the future, we may want to
|
||||
// consider just filtering all build and dev dependencies in
|
||||
// metadata::build.
|
||||
if visited.insert(dep)
|
||||
&& dep != "build_helper"
|
||||
&& (dep != "profiler_builtins" || self.config.profiler)
|
||||
&& (dep != "rustc_codegen_llvm" || self.config.llvm_enabled())
|
||||
{
|
||||
list.push(*dep);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,3 @@
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
|
||||
@ -12,7 +10,6 @@ use crate::{Build, Crate};
|
||||
#[derive(Deserialize)]
|
||||
struct Output {
|
||||
packages: Vec<Package>,
|
||||
resolve: Resolve,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@ -21,63 +18,25 @@ struct Package {
|
||||
name: String,
|
||||
source: Option<String>,
|
||||
manifest_path: String,
|
||||
dependencies: Vec<Dependency>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Resolve {
|
||||
nodes: Vec<ResolveNode>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct ResolveNode {
|
||||
id: String,
|
||||
dependencies: Vec<String>,
|
||||
struct Dependency {
|
||||
name: String,
|
||||
source: Option<String>,
|
||||
}
|
||||
|
||||
pub fn build(build: &mut Build) {
|
||||
let mut resolves = Vec::new();
|
||||
build_krate(&build.std_features(), build, &mut resolves, "src/libstd");
|
||||
build_krate("", build, &mut resolves, "src/libtest");
|
||||
build_krate(&build.rustc_features(), build, &mut resolves, "src/rustc");
|
||||
|
||||
let mut id2name = HashMap::with_capacity(build.crates.len());
|
||||
for (name, krate) in build.crates.iter() {
|
||||
id2name.insert(krate.id.clone(), name.clone());
|
||||
}
|
||||
|
||||
for node in resolves {
|
||||
let name = match id2name.get(&node.id) {
|
||||
Some(name) => name,
|
||||
None => continue,
|
||||
};
|
||||
|
||||
let krate = build.crates.get_mut(name).unwrap();
|
||||
for dep in node.dependencies.iter() {
|
||||
let dep = match id2name.get(dep) {
|
||||
Some(dep) => dep,
|
||||
None => continue,
|
||||
};
|
||||
krate.deps.insert(*dep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn build_krate(features: &str, build: &mut Build, resolves: &mut Vec<ResolveNode>, krate: &str) {
|
||||
// Run `cargo metadata` to figure out what crates we're testing.
|
||||
//
|
||||
// Down below we're going to call `cargo test`, but to test the right set
|
||||
// of packages we're going to have to know what `-p` arguments to pass it
|
||||
// to know what crates to test. Here we run `cargo metadata` to learn about
|
||||
// the dependency graph and what `-p` arguments there are.
|
||||
let mut cargo = Command::new(&build.initial_cargo);
|
||||
cargo
|
||||
.arg("metadata")
|
||||
.arg("--format-version")
|
||||
.arg("1")
|
||||
.arg("--features")
|
||||
.arg(features)
|
||||
.arg("--no-deps")
|
||||
.arg("--manifest-path")
|
||||
.arg(build.src.join(krate).join("Cargo.toml"));
|
||||
.arg(build.src.join("Cargo.toml"));
|
||||
let output = output(&mut cargo);
|
||||
let output: Output = serde_json::from_str(&output).unwrap();
|
||||
for package in output.packages {
|
||||
@ -85,8 +44,13 @@ fn build_krate(features: &str, build: &mut Build, resolves: &mut Vec<ResolveNode
|
||||
let name = INTERNER.intern_string(package.name);
|
||||
let mut path = PathBuf::from(package.manifest_path);
|
||||
path.pop();
|
||||
build.crates.insert(name, Crate { name, id: package.id, deps: HashSet::new(), path });
|
||||
let deps = package
|
||||
.dependencies
|
||||
.into_iter()
|
||||
.filter(|dep| dep.source.is_none())
|
||||
.map(|dep| INTERNER.intern_string(dep.name))
|
||||
.collect();
|
||||
build.crates.insert(name, Crate { name, id: package.id, deps, path });
|
||||
}
|
||||
}
|
||||
resolves.extend(output.resolve.nodes);
|
||||
}
|
||||
|
@ -1648,14 +1648,8 @@ impl Step for Crate {
|
||||
type Output = ();
|
||||
const DEFAULT: bool = true;
|
||||
|
||||
fn should_run(mut run: ShouldRun<'_>) -> ShouldRun<'_> {
|
||||
let builder = run.builder;
|
||||
for krate in run.builder.in_tree_crates("test") {
|
||||
if !(krate.name.starts_with("rustc_") && krate.name.ends_with("san")) {
|
||||
run = run.path(krate.local_path(&builder).to_str().unwrap());
|
||||
}
|
||||
}
|
||||
run
|
||||
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
|
||||
run.krate("test")
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig<'_>) {
|
||||
|
@ -1941,6 +1941,13 @@ extern "rust-intrinsic" {
|
||||
///
|
||||
/// Perma-unstable: do not use.
|
||||
pub fn miri_start_panic(payload: *mut u8) -> !;
|
||||
|
||||
/// Internal placeholder for injecting code coverage counters when the "instrument-coverage"
|
||||
/// option is enabled. The placeholder is replaced with `llvm.instrprof.increment` during code
|
||||
/// generation.
|
||||
#[cfg(not(bootstrap))]
|
||||
#[lang = "count_code_region"]
|
||||
pub fn count_code_region(index: u32);
|
||||
}
|
||||
|
||||
// Some functions are defined here because they accidentally got made
|
||||
@ -2057,9 +2064,14 @@ pub unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize) {
|
||||
fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize);
|
||||
}
|
||||
|
||||
debug_assert!(is_aligned_and_not_null(src), "attempt to copy from unaligned or null pointer");
|
||||
debug_assert!(is_aligned_and_not_null(dst), "attempt to copy to unaligned or null pointer");
|
||||
debug_assert!(is_nonoverlapping(src, dst, count), "attempt to copy to overlapping memory");
|
||||
if cfg!(debug_assertions)
|
||||
&& !(is_aligned_and_not_null(src)
|
||||
&& is_aligned_and_not_null(dst)
|
||||
&& is_nonoverlapping(src, dst, count))
|
||||
{
|
||||
// Not panicking to keep codegen impact smaller.
|
||||
abort();
|
||||
}
|
||||
copy_nonoverlapping(src, dst, count)
|
||||
}
|
||||
|
||||
@ -2122,8 +2134,10 @@ pub unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
|
||||
fn copy<T>(src: *const T, dst: *mut T, count: usize);
|
||||
}
|
||||
|
||||
debug_assert!(is_aligned_and_not_null(src), "attempt to copy from unaligned or null pointer");
|
||||
debug_assert!(is_aligned_and_not_null(dst), "attempt to copy to unaligned or null pointer");
|
||||
if cfg!(debug_assertions) && !(is_aligned_and_not_null(src) && is_aligned_and_not_null(dst)) {
|
||||
// Not panicking to keep codegen impact smaller.
|
||||
abort();
|
||||
}
|
||||
copy(src, dst, count)
|
||||
}
|
||||
|
||||
|
@ -70,7 +70,7 @@
|
||||
use crate::cmp::Ordering;
|
||||
use crate::fmt;
|
||||
use crate::hash;
|
||||
use crate::intrinsics::{self, is_aligned_and_not_null, is_nonoverlapping};
|
||||
use crate::intrinsics::{self, abort, is_aligned_and_not_null, is_nonoverlapping};
|
||||
use crate::mem::{self, MaybeUninit};
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
@ -420,9 +420,14 @@ pub unsafe fn swap<T>(x: *mut T, y: *mut T) {
|
||||
#[inline]
|
||||
#[stable(feature = "swap_nonoverlapping", since = "1.27.0")]
|
||||
pub unsafe fn swap_nonoverlapping<T>(x: *mut T, y: *mut T, count: usize) {
|
||||
debug_assert!(is_aligned_and_not_null(x), "attempt to swap unaligned or null pointer");
|
||||
debug_assert!(is_aligned_and_not_null(y), "attempt to swap unaligned or null pointer");
|
||||
debug_assert!(is_nonoverlapping(x, y, count), "attempt to swap overlapping memory");
|
||||
if cfg!(debug_assertions)
|
||||
&& !(is_aligned_and_not_null(x)
|
||||
&& is_aligned_and_not_null(y)
|
||||
&& is_nonoverlapping(x, y, count))
|
||||
{
|
||||
// Not panicking to keep codegen impact smaller.
|
||||
abort();
|
||||
}
|
||||
|
||||
let x = x as *mut u8;
|
||||
let y = y as *mut u8;
|
||||
@ -838,7 +843,10 @@ pub unsafe fn read_unaligned<T>(src: *const T) -> T {
|
||||
#[inline]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub unsafe fn write<T>(dst: *mut T, src: T) {
|
||||
debug_assert!(is_aligned_and_not_null(dst), "attempt to write to unaligned or null pointer");
|
||||
if cfg!(debug_assertions) && !is_aligned_and_not_null(dst) {
|
||||
// Not panicking to keep codegen impact smaller.
|
||||
abort();
|
||||
}
|
||||
intrinsics::move_val_init(&mut *dst, src)
|
||||
}
|
||||
|
||||
@ -1003,7 +1011,10 @@ pub unsafe fn write_unaligned<T>(dst: *mut T, src: T) {
|
||||
#[inline]
|
||||
#[stable(feature = "volatile", since = "1.9.0")]
|
||||
pub unsafe fn read_volatile<T>(src: *const T) -> T {
|
||||
debug_assert!(is_aligned_and_not_null(src), "attempt to read from unaligned or null pointer");
|
||||
if cfg!(debug_assertions) && !is_aligned_and_not_null(src) {
|
||||
// Not panicking to keep codegen impact smaller.
|
||||
abort();
|
||||
}
|
||||
intrinsics::volatile_load(src)
|
||||
}
|
||||
|
||||
@ -1072,7 +1083,10 @@ pub unsafe fn read_volatile<T>(src: *const T) -> T {
|
||||
#[inline]
|
||||
#[stable(feature = "volatile", since = "1.9.0")]
|
||||
pub unsafe fn write_volatile<T>(dst: *mut T, src: T) {
|
||||
debug_assert!(is_aligned_and_not_null(dst), "attempt to write to unaligned or null pointer");
|
||||
if cfg!(debug_assertions) && !is_aligned_and_not_null(dst) {
|
||||
// Not panicking to keep codegen impact smaller.
|
||||
abort();
|
||||
}
|
||||
intrinsics::volatile_store(dst, src);
|
||||
}
|
||||
|
||||
|
@ -997,6 +997,33 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
||||
self.call_lifetime_intrinsic("llvm.lifetime.end.p0i8", ptr, size);
|
||||
}
|
||||
|
||||
fn instrprof_increment(
|
||||
&mut self,
|
||||
fn_name: &'ll Value,
|
||||
hash: &'ll Value,
|
||||
num_counters: &'ll Value,
|
||||
index: &'ll Value,
|
||||
) -> &'ll Value {
|
||||
debug!(
|
||||
"instrprof_increment() with args ({:?}, {:?}, {:?}, {:?})",
|
||||
fn_name, hash, num_counters, index
|
||||
);
|
||||
|
||||
let llfn = unsafe { llvm::LLVMRustGetInstrprofIncrementIntrinsic(self.cx().llmod) };
|
||||
let args = &[fn_name, hash, num_counters, index];
|
||||
let args = self.check_call("call", llfn, args);
|
||||
|
||||
unsafe {
|
||||
llvm::LLVMRustBuildCall(
|
||||
self.llbuilder,
|
||||
llfn,
|
||||
args.as_ptr() as *const &llvm::Value,
|
||||
args.len() as c_uint,
|
||||
None,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn call(
|
||||
&mut self,
|
||||
llfn: &'ll Value,
|
||||
|
@ -749,6 +749,8 @@ impl CodegenCx<'b, 'tcx> {
|
||||
ifn!("llvm.lifetime.start.p0i8", fn(t_i64, i8p) -> void);
|
||||
ifn!("llvm.lifetime.end.p0i8", fn(t_i64, i8p) -> void);
|
||||
|
||||
ifn!("llvm.instrprof.increment", fn(i8p, t_i64, t_i32, t_i32) -> void);
|
||||
|
||||
ifn!("llvm.expect.i1", fn(i1, i1) -> i1);
|
||||
ifn!("llvm.eh.typeid.for", fn(i8p) -> t_i32);
|
||||
ifn!("llvm.localescape", fn(...) -> void);
|
||||
|
@ -7,6 +7,8 @@ use crate::type_of::LayoutLlvmExt;
|
||||
use crate::va_arg::emit_va_arg;
|
||||
use crate::value::Value;
|
||||
|
||||
use log::debug;
|
||||
|
||||
use rustc_ast::ast;
|
||||
use rustc_codegen_ssa::base::{compare_simd_types, to_immediate, wants_msvc_seh};
|
||||
use rustc_codegen_ssa::common::span_invalid_monomorphization_error;
|
||||
@ -21,6 +23,7 @@ use rustc_middle::ty::layout::{FnAbiExt, HasTyCtxt};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_span::Span;
|
||||
use rustc_span::Symbol;
|
||||
use rustc_target::abi::{self, HasDataLayout, LayoutOf, Primitive};
|
||||
use rustc_target::spec::PanicStrategy;
|
||||
|
||||
@ -86,6 +89,7 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
|
||||
args: &[OperandRef<'tcx, &'ll Value>],
|
||||
llresult: &'ll Value,
|
||||
span: Span,
|
||||
caller_instance: ty::Instance<'tcx>,
|
||||
) {
|
||||
let tcx = self.tcx;
|
||||
let callee_ty = instance.monomorphic_ty(tcx);
|
||||
@ -136,6 +140,28 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
|
||||
let llfn = self.get_intrinsic(&("llvm.debugtrap"));
|
||||
self.call(llfn, &[], None)
|
||||
}
|
||||
"count_code_region" => {
|
||||
if let ty::InstanceDef::Item(fn_def_id) = caller_instance.def {
|
||||
let caller_fn_path = tcx.def_path_str(fn_def_id);
|
||||
debug!(
|
||||
"count_code_region to llvm.instrprof.increment(fn_name={})",
|
||||
caller_fn_path
|
||||
);
|
||||
|
||||
// FIXME(richkadel): (1) Replace raw function name with mangled function name;
|
||||
// (2) Replace hardcoded `1234` in `hash` with a computed hash (as discussed in)
|
||||
// the MCP (compiler-team/issues/278); and replace the hardcoded `1` for
|
||||
// `num_counters` with the actual number of counters per function (when the
|
||||
// changes are made to inject more than one counter per function).
|
||||
let (fn_name, _len_val) = self.const_str(Symbol::intern(&caller_fn_path));
|
||||
let index = args[0].immediate();
|
||||
let hash = self.const_u64(1234);
|
||||
let num_counters = self.const_u32(1);
|
||||
self.instrprof_increment(fn_name, hash, num_counters, index)
|
||||
} else {
|
||||
bug!("intrinsic count_code_region: no src.instance");
|
||||
}
|
||||
}
|
||||
"va_start" => self.va_start(args[0].immediate()),
|
||||
"va_end" => self.va_end(args[0].immediate()),
|
||||
"va_copy" => {
|
||||
|
@ -1360,6 +1360,7 @@ extern "C" {
|
||||
|
||||
// Miscellaneous instructions
|
||||
pub fn LLVMBuildPhi(B: &Builder<'a>, Ty: &'a Type, Name: *const c_char) -> &'a Value;
|
||||
pub fn LLVMRustGetInstrprofIncrementIntrinsic(M: &Module) -> &'a Value;
|
||||
pub fn LLVMRustBuildCall(
|
||||
B: &Builder<'a>,
|
||||
Fn: &'a Value,
|
||||
|
@ -721,12 +721,14 @@ impl<'a> Linker for MsvcLinker<'a> {
|
||||
}
|
||||
|
||||
fn link_whole_staticlib(&mut self, lib: Symbol, _search_path: &[PathBuf]) {
|
||||
// not supported?
|
||||
self.link_staticlib(lib);
|
||||
self.cmd.arg(format!("/WHOLEARCHIVE:{}.lib", lib));
|
||||
}
|
||||
fn link_whole_rlib(&mut self, path: &Path) {
|
||||
// not supported?
|
||||
self.link_rlib(path);
|
||||
let mut arg = OsString::from("/WHOLEARCHIVE:");
|
||||
arg.push(path);
|
||||
self.cmd.arg(arg);
|
||||
}
|
||||
fn optimize(&mut self) {
|
||||
// Needs more investigation of `/OPT` arguments
|
||||
|
@ -175,6 +175,12 @@ impl ModuleConfig {
|
||||
if sess.opts.debugging_opts.profile && !is_compiler_builtins {
|
||||
passes.push("insert-gcov-profiling".to_owned());
|
||||
}
|
||||
|
||||
// The rustc option `-Zinstrument_coverage` injects intrinsic calls to
|
||||
// `llvm.instrprof.increment()`, which requires the LLVM `instrprof` pass.
|
||||
if sess.opts.debugging_opts.instrument_coverage {
|
||||
passes.push("instrprof".to_owned());
|
||||
}
|
||||
passes
|
||||
},
|
||||
vec![]
|
||||
|
@ -693,6 +693,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
&args,
|
||||
dest,
|
||||
terminator.source_info.span,
|
||||
self.instance,
|
||||
);
|
||||
|
||||
if let ReturnDest::IndirectOperand(dst, _) = ret_dest {
|
||||
|
@ -260,6 +260,14 @@ pub trait BuilderMethods<'a, 'tcx>:
|
||||
/// Called for `StorageDead`
|
||||
fn lifetime_end(&mut self, ptr: Self::Value, size: Size);
|
||||
|
||||
fn instrprof_increment(
|
||||
&mut self,
|
||||
fn_name: Self::Value,
|
||||
hash: Self::Value,
|
||||
num_counters: Self::Value,
|
||||
index: Self::Value,
|
||||
) -> Self::Value;
|
||||
|
||||
fn call(
|
||||
&mut self,
|
||||
llfn: Self::Value,
|
||||
|
@ -15,6 +15,7 @@ pub trait IntrinsicCallMethods<'tcx>: BackendTypes {
|
||||
args: &[OperandRef<'tcx, Self::Value>],
|
||||
llresult: Self::Value,
|
||||
span: Span,
|
||||
caller_instance: ty::Instance<'tcx>,
|
||||
);
|
||||
|
||||
fn abort(&mut self);
|
||||
|
@ -443,6 +443,7 @@ E0759: include_str!("./error_codes/E0759.md"),
|
||||
E0760: include_str!("./error_codes/E0760.md"),
|
||||
E0761: include_str!("./error_codes/E0761.md"),
|
||||
E0762: include_str!("./error_codes/E0762.md"),
|
||||
E0763: include_str!("./error_codes/E0763.md"),
|
||||
;
|
||||
// E0006, // merged with E0005
|
||||
// E0008, // cannot bind by-move into a pattern guard
|
||||
|
13
src/librustc_error_codes/error_codes/E0763.md
Normal file
13
src/librustc_error_codes/error_codes/E0763.md
Normal file
@ -0,0 +1,13 @@
|
||||
A byte constant wasn't correctly ended.
|
||||
|
||||
Erroneous code example:
|
||||
|
||||
```compile_fail,E0763
|
||||
let c = b'a; // error!
|
||||
```
|
||||
|
||||
To fix this error, add the missing quote:
|
||||
|
||||
```
|
||||
let c = b'a'; // ok!
|
||||
```
|
@ -242,6 +242,8 @@ language_item_table! {
|
||||
|
||||
StartFnLangItem, "start", start_fn, Target::Fn;
|
||||
|
||||
CountCodeRegionFnLangItem, "count_code_region", count_code_region_fn, Target::Fn;
|
||||
|
||||
EhPersonalityLangItem, "eh_personality", eh_personality, Target::Fn;
|
||||
EhCatchTypeinfoLangItem, "eh_catch_typeinfo", eh_catch_typeinfo, Target::Static;
|
||||
|
||||
|
@ -548,6 +548,7 @@ fn test_debugging_options_tracking_hash() {
|
||||
tracked!(human_readable_cgu_names, true);
|
||||
tracked!(inline_in_all_cgus, Some(true));
|
||||
tracked!(insert_sideeffect, true);
|
||||
tracked!(instrument_coverage, true);
|
||||
tracked!(instrument_mcount, true);
|
||||
tracked!(link_only, true);
|
||||
tracked!(merge_functions, Some(MergeFunctions::Disabled));
|
||||
|
@ -706,7 +706,9 @@ impl<'a> CrateLoader<'a> {
|
||||
}
|
||||
|
||||
fn inject_profiler_runtime(&mut self) {
|
||||
if (self.sess.opts.debugging_opts.profile || self.sess.opts.cg.profile_generate.enabled())
|
||||
if (self.sess.opts.debugging_opts.instrument_coverage
|
||||
|| self.sess.opts.debugging_opts.profile
|
||||
|| self.sess.opts.cg.profile_generate.enabled())
|
||||
&& !self.sess.opts.debugging_opts.no_profiler_runtime
|
||||
{
|
||||
info!("loading profiler");
|
||||
|
@ -29,6 +29,7 @@ use rustc_macros::HashStable;
|
||||
use rustc_serialize::{Decodable, Encodable};
|
||||
use rustc_span::symbol::Symbol;
|
||||
use rustc_span::{Span, DUMMY_SP};
|
||||
use rustc_target::abi;
|
||||
use rustc_target::asm::InlineAsmRegOrRegClass;
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::{self, Debug, Display, Formatter, Write};
|
||||
@ -2218,6 +2219,33 @@ impl<'tcx> Operand<'tcx> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Convenience helper to make a literal-like constant from a given scalar value.
|
||||
/// Since this is used to synthesize MIR, assumes `user_ty` is None.
|
||||
pub fn const_from_scalar(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
val: Scalar,
|
||||
span: Span,
|
||||
) -> Operand<'tcx> {
|
||||
debug_assert!({
|
||||
let param_env_and_ty = ty::ParamEnv::empty().and(ty);
|
||||
let type_size = tcx
|
||||
.layout_of(param_env_and_ty)
|
||||
.unwrap_or_else(|e| panic!("could not compute layout for {:?}: {:?}", ty, e))
|
||||
.size;
|
||||
let scalar_size = abi::Size::from_bytes(match val {
|
||||
Scalar::Raw { size, .. } => size,
|
||||
_ => panic!("Invalid scalar type {:?}", val),
|
||||
});
|
||||
scalar_size == type_size
|
||||
});
|
||||
Operand::Constant(box Constant {
|
||||
span,
|
||||
user_ty: None,
|
||||
literal: ty::Const::from_scalar(tcx, val, ty),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn to_copy(&self) -> Self {
|
||||
match *self {
|
||||
Operand::Copy(_) | Operand::Constant(_) => self.clone(),
|
||||
|
@ -389,6 +389,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
);
|
||||
self.copy_op(self.operand_index(args[0], index)?, dest)?;
|
||||
}
|
||||
// FIXME(#73156): Handle source code coverage in const eval
|
||||
sym::count_code_region => (),
|
||||
_ => return Ok(false),
|
||||
}
|
||||
|
||||
|
@ -454,18 +454,11 @@ fn default_visibility(tcx: TyCtxt<'_>, id: DefId, is_generic: bool) -> Visibilit
|
||||
fn merge_codegen_units<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
initial_partitioning: &mut PreInliningPartitioning<'tcx>,
|
||||
mut target_cgu_count: usize,
|
||||
target_cgu_count: usize,
|
||||
) {
|
||||
assert!(target_cgu_count >= 1);
|
||||
let codegen_units = &mut initial_partitioning.codegen_units;
|
||||
|
||||
if tcx.is_compiler_builtins(LOCAL_CRATE) {
|
||||
// Compiler builtins require some degree of control over how mono items
|
||||
// are partitioned into compilation units. Provide it by keeping the
|
||||
// original partitioning when compiling the compiler builtins crate.
|
||||
target_cgu_count = codegen_units.len();
|
||||
}
|
||||
|
||||
// Note that at this point in time the `codegen_units` here may not be in a
|
||||
// deterministic order (but we know they're deterministically the same set).
|
||||
// We want this merging to produce a deterministic ordering of codegen units
|
||||
|
92
src/librustc_mir/transform/instrument_coverage.rs
Normal file
92
src/librustc_mir/transform/instrument_coverage.rs
Normal file
@ -0,0 +1,92 @@
|
||||
use crate::transform::{MirPass, MirSource};
|
||||
use crate::util::patch::MirPatch;
|
||||
use rustc_hir::lang_items;
|
||||
use rustc_middle::mir::interpret::Scalar;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_span::Span;
|
||||
|
||||
/// Inserts call to count_code_region() as a placeholder to be replaced during code generation with
|
||||
/// the intrinsic llvm.instrprof.increment.
|
||||
pub struct InstrumentCoverage;
|
||||
|
||||
impl<'tcx> MirPass<'tcx> for InstrumentCoverage {
|
||||
fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) {
|
||||
if tcx.sess.opts.debugging_opts.instrument_coverage {
|
||||
debug!("instrumenting {:?}", src.def_id());
|
||||
instrument_coverage(tcx, body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The first counter (start of the function) is index zero.
|
||||
const INIT_FUNCTION_COUNTER: u32 = 0;
|
||||
|
||||
/// Injects calls to placeholder function `count_code_region()`.
|
||||
// FIXME(richkadel): As a first step, counters are only injected at the top of each function.
|
||||
// The complete solution will inject counters at each conditional code branch.
|
||||
pub fn instrument_coverage<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||
let span = body.span.shrink_to_lo();
|
||||
|
||||
let count_code_region_fn = function_handle(
|
||||
tcx,
|
||||
tcx.require_lang_item(lang_items::CountCodeRegionFnLangItem, None),
|
||||
span,
|
||||
);
|
||||
let counter_index = Operand::const_from_scalar(
|
||||
tcx,
|
||||
tcx.types.u32,
|
||||
Scalar::from_u32(INIT_FUNCTION_COUNTER),
|
||||
span,
|
||||
);
|
||||
|
||||
let mut patch = MirPatch::new(body);
|
||||
|
||||
let new_block = patch.new_block(placeholder_block(SourceInfo::outermost(body.span)));
|
||||
let next_block = START_BLOCK;
|
||||
|
||||
let temp = patch.new_temp(tcx.mk_unit(), body.span);
|
||||
patch.patch_terminator(
|
||||
new_block,
|
||||
TerminatorKind::Call {
|
||||
func: count_code_region_fn,
|
||||
args: vec![counter_index],
|
||||
// new_block will swapped with the next_block, after applying patch
|
||||
destination: Some((Place::from(temp), new_block)),
|
||||
cleanup: None,
|
||||
from_hir_call: false,
|
||||
fn_span: span,
|
||||
},
|
||||
);
|
||||
|
||||
patch.add_statement(new_block.start_location(), StatementKind::StorageLive(temp));
|
||||
patch.add_statement(next_block.start_location(), StatementKind::StorageDead(temp));
|
||||
|
||||
patch.apply(body);
|
||||
|
||||
// To insert the `new_block` in front of the first block in the counted branch (for example,
|
||||
// the START_BLOCK, at the top of the function), just swap the indexes, leaving the rest of the
|
||||
// graph unchanged.
|
||||
body.basic_blocks_mut().swap(next_block, new_block);
|
||||
}
|
||||
|
||||
fn function_handle<'tcx>(tcx: TyCtxt<'tcx>, fn_def_id: DefId, span: Span) -> Operand<'tcx> {
|
||||
let ret_ty = tcx.fn_sig(fn_def_id).output();
|
||||
let ret_ty = ret_ty.no_bound_vars().unwrap();
|
||||
let substs = tcx.mk_substs(::std::iter::once(ty::subst::GenericArg::from(ret_ty)));
|
||||
Operand::function_handle(tcx, fn_def_id, substs, span)
|
||||
}
|
||||
|
||||
fn placeholder_block<'tcx>(source_info: SourceInfo) -> BasicBlockData<'tcx> {
|
||||
BasicBlockData {
|
||||
statements: vec![],
|
||||
terminator: Some(Terminator {
|
||||
source_info,
|
||||
// this gets overwritten by the counter Call
|
||||
kind: TerminatorKind::Unreachable,
|
||||
}),
|
||||
is_cleanup: false,
|
||||
}
|
||||
}
|
@ -28,6 +28,7 @@ pub mod elaborate_drops;
|
||||
pub mod generator;
|
||||
pub mod inline;
|
||||
pub mod instcombine;
|
||||
pub mod instrument_coverage;
|
||||
pub mod no_landing_pads;
|
||||
pub mod nrvo;
|
||||
pub mod promote_consts;
|
||||
@ -288,6 +289,10 @@ fn mir_validated(
|
||||
// What we need to run borrowck etc.
|
||||
&promote_pass,
|
||||
&simplify::SimplifyCfg::new("qualify-consts"),
|
||||
// If the `instrument-coverage` option is enabled, analyze the CFG, identify each
|
||||
// conditional branch, construct a coverage map to be passed to LLVM, and inject counters
|
||||
// where needed.
|
||||
&instrument_coverage::InstrumentCoverage,
|
||||
]],
|
||||
);
|
||||
|
||||
|
@ -339,8 +339,15 @@ impl<'a> StringReader<'a> {
|
||||
}
|
||||
rustc_lexer::LiteralKind::Byte { terminated } => {
|
||||
if !terminated {
|
||||
self.fatal_span_(start + BytePos(1), suffix_start, "unterminated byte constant")
|
||||
.raise()
|
||||
self.sess
|
||||
.span_diagnostic
|
||||
.struct_span_fatal_with_code(
|
||||
self.mk_sp(start + BytePos(1), suffix_start),
|
||||
"unterminated byte constant",
|
||||
error_code!(E0763),
|
||||
)
|
||||
.emit();
|
||||
FatalError.raise();
|
||||
}
|
||||
(token::Byte, Mode::Byte, 2, 1) // b' '
|
||||
}
|
||||
|
@ -5,10 +5,12 @@ use rustc_errors::struct_span_err;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
|
||||
use rustc_hir::lang_items;
|
||||
use rustc_hir::lang_items::ITEM_REFS;
|
||||
use rustc_hir::weak_lang_items::WEAK_ITEMS_REFS;
|
||||
use rustc_middle::middle::lang_items::whitelisted;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::config::CrateType;
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::symbol::Symbol;
|
||||
use rustc_span::Span;
|
||||
|
||||
@ -70,11 +72,21 @@ fn verify<'tcx>(tcx: TyCtxt<'tcx>, items: &lang_items::LanguageItems) {
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Context<'a, 'tcx> {
|
||||
fn register(&mut self, name: Symbol, span: Span) {
|
||||
fn register(&mut self, name: Symbol, span: Span, hir_id: hir::HirId) {
|
||||
if let Some(&item) = WEAK_ITEMS_REFS.get(&name) {
|
||||
if self.items.require(item).is_err() {
|
||||
self.items.missing.push(item);
|
||||
}
|
||||
} else if name == sym::count_code_region {
|
||||
// `core::intrinsics::code_count_region()` is (currently) the only `extern` lang item
|
||||
// that is never actually linked. It is not a `weak_lang_item` that can be registered
|
||||
// when used, and should be registered here instead.
|
||||
if let Some((item_index, _)) = ITEM_REFS.get(&*name.as_str()).cloned() {
|
||||
if self.items.items[item_index].is_none() {
|
||||
let item_def_id = self.tcx.hir().local_def_id(hir_id).to_def_id();
|
||||
self.items.items[item_index] = Some(item_def_id);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
struct_span_err!(self.tcx.sess, span, E0264, "unknown external lang item: `{}`", name)
|
||||
.emit();
|
||||
@ -91,7 +103,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for Context<'a, 'tcx> {
|
||||
|
||||
fn visit_foreign_item(&mut self, i: &hir::ForeignItem<'_>) {
|
||||
if let Some((lang_item, _)) = hir::lang_items::extract(&i.attrs) {
|
||||
self.register(lang_item, i.span);
|
||||
self.register(lang_item, i.span, i.hir_id);
|
||||
}
|
||||
intravisit::walk_foreign_item(self, i)
|
||||
}
|
||||
|
@ -876,6 +876,9 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
|
||||
"fix undefined behavior when a thread doesn't eventually make progress \
|
||||
(such as entering an empty infinite loop) by inserting llvm.sideeffect \
|
||||
(default: no)"),
|
||||
instrument_coverage: bool = (false, parse_bool, [TRACKED],
|
||||
"instrument the generated code with LLVM code region counters to \
|
||||
(in the future) generate coverage reports (experimental; default: no)"),
|
||||
instrument_mcount: bool = (false, parse_bool, [TRACKED],
|
||||
"insert function instrument code for mcount-based tracing (default: no)"),
|
||||
keep_hygiene_data: bool = (false, parse_bool, [UNTRACKED],
|
||||
|
@ -240,6 +240,7 @@ symbols! {
|
||||
copy_closures,
|
||||
core,
|
||||
core_intrinsics,
|
||||
count_code_region,
|
||||
crate_id,
|
||||
crate_in_paths,
|
||||
crate_local,
|
||||
|
@ -1,5 +1,5 @@
|
||||
use super::method::MethodCallee;
|
||||
use super::{FnCtxt, Needs, PlaceOp};
|
||||
use super::{FnCtxt, PlaceOp};
|
||||
|
||||
use rustc_errors::struct_span_err;
|
||||
use rustc_hir as hir;
|
||||
@ -170,14 +170,13 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
|
||||
}
|
||||
|
||||
/// Returns the adjustment steps.
|
||||
pub fn adjust_steps(&self, fcx: &FnCtxt<'a, 'tcx>, needs: Needs) -> Vec<Adjustment<'tcx>> {
|
||||
fcx.register_infer_ok_obligations(self.adjust_steps_as_infer_ok(fcx, needs))
|
||||
pub fn adjust_steps(&self, fcx: &FnCtxt<'a, 'tcx>) -> Vec<Adjustment<'tcx>> {
|
||||
fcx.register_infer_ok_obligations(self.adjust_steps_as_infer_ok(fcx))
|
||||
}
|
||||
|
||||
pub fn adjust_steps_as_infer_ok(
|
||||
&self,
|
||||
fcx: &FnCtxt<'a, 'tcx>,
|
||||
needs: Needs,
|
||||
) -> InferOk<'tcx, Vec<Adjustment<'tcx>>> {
|
||||
let mut obligations = vec![];
|
||||
let targets = self.steps.iter().skip(1).map(|&(ty, _)| ty).chain(iter::once(self.cur_ty));
|
||||
@ -186,7 +185,7 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
|
||||
.iter()
|
||||
.map(|&(source, kind)| {
|
||||
if let AutoderefKind::Overloaded = kind {
|
||||
fcx.try_overloaded_deref(self.span, source, needs).and_then(
|
||||
fcx.try_overloaded_deref(self.span, source).and_then(
|
||||
|InferOk { value: method, obligations: o }| {
|
||||
obligations.extend(o);
|
||||
if let ty::Ref(region, _, mutbl) = method.sig.output().kind {
|
||||
@ -266,8 +265,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
&self,
|
||||
span: Span,
|
||||
base_ty: Ty<'tcx>,
|
||||
needs: Needs,
|
||||
) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
|
||||
self.try_overloaded_place_op(span, base_ty, &[], needs, PlaceOp::Deref)
|
||||
self.try_overloaded_place_op(span, base_ty, &[], PlaceOp::Deref)
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use super::autoderef::Autoderef;
|
||||
use super::method::MethodCallee;
|
||||
use super::{Expectation, FnCtxt, Needs, TupleArgumentsFlag};
|
||||
use super::{Expectation, FnCtxt, TupleArgumentsFlag};
|
||||
use crate::type_error_struct;
|
||||
|
||||
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
|
||||
@ -115,7 +115,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
// If the callee is a bare function or a closure, then we're all set.
|
||||
match adjusted_ty.kind {
|
||||
ty::FnDef(..) | ty::FnPtr(_) => {
|
||||
let adjustments = autoderef.adjust_steps(self, Needs::None);
|
||||
let adjustments = autoderef.adjust_steps(self);
|
||||
self.apply_adjustments(callee_expr, adjustments);
|
||||
return Some(CallStep::Builtin(adjusted_ty));
|
||||
}
|
||||
@ -135,7 +135,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
&closure_sig,
|
||||
)
|
||||
.0;
|
||||
let adjustments = autoderef.adjust_steps(self, Needs::None);
|
||||
let adjustments = autoderef.adjust_steps(self);
|
||||
self.record_deferred_call_resolution(
|
||||
def_id,
|
||||
DeferredCallResolution {
|
||||
@ -176,7 +176,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
self.try_overloaded_call_traits(call_expr, adjusted_ty, Some(arg_exprs))
|
||||
.or_else(|| self.try_overloaded_call_traits(call_expr, adjusted_ty, None))
|
||||
.map(|(autoref, method)| {
|
||||
let mut adjustments = autoderef.adjust_steps(self, Needs::None);
|
||||
let mut adjustments = autoderef.adjust_steps(self);
|
||||
adjustments.extend(autoref);
|
||||
self.apply_adjustments(callee_expr, adjustments);
|
||||
CallStep::Overloaded(method)
|
||||
@ -220,21 +220,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
let method = self.register_infer_ok_obligations(ok);
|
||||
let mut autoref = None;
|
||||
if borrow {
|
||||
if let ty::Ref(region, _, mutbl) = method.sig.inputs()[0].kind {
|
||||
let mutbl = match mutbl {
|
||||
hir::Mutability::Not => AutoBorrowMutability::Not,
|
||||
hir::Mutability::Mut => AutoBorrowMutability::Mut {
|
||||
// For initial two-phase borrow
|
||||
// deployment, conservatively omit
|
||||
// overloaded function call ops.
|
||||
allow_two_phase_borrow: AllowTwoPhase::No,
|
||||
},
|
||||
};
|
||||
autoref = Some(Adjustment {
|
||||
kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)),
|
||||
target: method.sig.inputs()[0],
|
||||
});
|
||||
}
|
||||
// Check for &self vs &mut self in the method signature. Since this is either
|
||||
// the Fn or FnMut trait, it should be one of those.
|
||||
let (region, mutbl) = if let ty::Ref(r, _, mutbl) = method.sig.inputs()[0].kind
|
||||
{
|
||||
(r, mutbl)
|
||||
} else {
|
||||
span_bug!(call_expr.span, "input to call/call_mut is not a ref?");
|
||||
};
|
||||
|
||||
let mutbl = match mutbl {
|
||||
hir::Mutability::Not => AutoBorrowMutability::Not,
|
||||
hir::Mutability::Mut => AutoBorrowMutability::Mut {
|
||||
// For initial two-phase borrow
|
||||
// deployment, conservatively omit
|
||||
// overloaded function call ops.
|
||||
allow_two_phase_borrow: AllowTwoPhase::No,
|
||||
},
|
||||
};
|
||||
autoref = Some(Adjustment {
|
||||
kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)),
|
||||
target: method.sig.inputs()[0],
|
||||
});
|
||||
}
|
||||
return Some((autoref, method));
|
||||
}
|
||||
|
@ -51,7 +51,7 @@
|
||||
//! we may want to adjust precisely when coercions occur.
|
||||
|
||||
use crate::astconv::AstConv;
|
||||
use crate::check::{FnCtxt, Needs};
|
||||
use crate::check::FnCtxt;
|
||||
use rustc_errors::{struct_span_err, DiagnosticBuilder};
|
||||
use rustc_hir as hir;
|
||||
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||
@ -421,9 +421,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
|
||||
return success(vec![], ty, obligations);
|
||||
}
|
||||
|
||||
let needs = Needs::maybe_mut_place(mutbl_b);
|
||||
let InferOk { value: mut adjustments, obligations: o } =
|
||||
autoderef.adjust_steps_as_infer_ok(self, needs);
|
||||
autoderef.adjust_steps_as_infer_ok(self);
|
||||
obligations.extend(o);
|
||||
obligations.extend(autoderef.into_obligations());
|
||||
|
||||
|
@ -29,9 +29,7 @@ use rustc_hir::{ExprKind, QPath};
|
||||
use rustc_infer::infer;
|
||||
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::adjustment::{
|
||||
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
|
||||
};
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase};
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_middle::ty::TypeFoldable;
|
||||
use rustc_middle::ty::{AdtKind, Visibility};
|
||||
@ -113,12 +111,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
self.check_expr_with_expectation(expr, ExpectHasType(expected))
|
||||
}
|
||||
|
||||
pub(super) fn check_expr_with_expectation(
|
||||
fn check_expr_with_expectation_and_needs(
|
||||
&self,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
expected: Expectation<'tcx>,
|
||||
needs: Needs,
|
||||
) -> Ty<'tcx> {
|
||||
self.check_expr_with_expectation_and_needs(expr, expected, Needs::None)
|
||||
let ty = self.check_expr_with_expectation(expr, expected);
|
||||
|
||||
// If the expression is used in a place whether mutable place is required
|
||||
// e.g. LHS of assignment, perform the conversion.
|
||||
if let Needs::MutPlace = needs {
|
||||
self.convert_place_derefs_to_mutable(expr);
|
||||
}
|
||||
|
||||
ty
|
||||
}
|
||||
|
||||
pub(super) fn check_expr(&self, expr: &'tcx hir::Expr<'tcx>) -> Ty<'tcx> {
|
||||
@ -143,11 +150,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
/// Note that inspecting a type's structure *directly* may expose the fact
|
||||
/// that there are actually multiple representations for `Error`, so avoid
|
||||
/// that when err needs to be handled differently.
|
||||
fn check_expr_with_expectation_and_needs(
|
||||
pub(super) fn check_expr_with_expectation(
|
||||
&self,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
expected: Expectation<'tcx>,
|
||||
needs: Needs,
|
||||
) -> Ty<'tcx> {
|
||||
debug!(">> type-checking: expr={:?} expected={:?}", expr, expected);
|
||||
|
||||
@ -171,7 +177,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
let old_diverges = self.diverges.replace(Diverges::Maybe);
|
||||
let old_has_errors = self.has_errors.replace(false);
|
||||
|
||||
let ty = self.check_expr_kind(expr, expected, needs);
|
||||
let ty = self.check_expr_kind(expr, expected);
|
||||
|
||||
// Warn for non-block expressions with diverging children.
|
||||
match expr.kind {
|
||||
@ -213,9 +219,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
&self,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
expected: Expectation<'tcx>,
|
||||
needs: Needs,
|
||||
) -> Ty<'tcx> {
|
||||
debug!("check_expr_kind(expr={:?}, expected={:?}, needs={:?})", expr, expected, needs,);
|
||||
debug!("check_expr_kind(expr={:?}, expected={:?})", expr, expected);
|
||||
|
||||
let tcx = self.tcx;
|
||||
match expr.kind {
|
||||
@ -226,9 +231,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
self.check_expr_assign(expr, expected, lhs, rhs, span)
|
||||
}
|
||||
ExprKind::AssignOp(op, ref lhs, ref rhs) => self.check_binop_assign(expr, op, lhs, rhs),
|
||||
ExprKind::Unary(unop, ref oprnd) => {
|
||||
self.check_expr_unary(unop, oprnd, expected, needs, expr)
|
||||
}
|
||||
ExprKind::Unary(unop, ref oprnd) => self.check_expr_unary(unop, oprnd, expected, expr),
|
||||
ExprKind::AddrOf(kind, mutbl, ref oprnd) => {
|
||||
self.check_expr_addr_of(kind, mutbl, oprnd, expected, expr)
|
||||
}
|
||||
@ -264,7 +267,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
ExprKind::Block(ref body, _) => self.check_block_with_expected(&body, expected),
|
||||
ExprKind::Call(ref callee, ref args) => self.check_call(expr, &callee, args, expected),
|
||||
ExprKind::MethodCall(ref segment, span, ref args, _) => {
|
||||
self.check_method_call(expr, segment, span, args, expected, needs)
|
||||
self.check_method_call(expr, segment, span, args, expected)
|
||||
}
|
||||
ExprKind::Cast(ref e, ref t) => self.check_expr_cast(e, t, expr),
|
||||
ExprKind::Type(ref e, ref t) => {
|
||||
@ -281,8 +284,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
ExprKind::Struct(ref qpath, fields, ref base_expr) => {
|
||||
self.check_expr_struct(expr, expected, qpath, fields, base_expr)
|
||||
}
|
||||
ExprKind::Field(ref base, field) => self.check_field(expr, needs, &base, field),
|
||||
ExprKind::Index(ref base, ref idx) => self.check_expr_index(base, idx, needs, expr),
|
||||
ExprKind::Field(ref base, field) => self.check_field(expr, &base, field),
|
||||
ExprKind::Index(ref base, ref idx) => self.check_expr_index(base, idx, expr),
|
||||
ExprKind::Yield(ref value, ref src) => self.check_expr_yield(value, expr, src),
|
||||
hir::ExprKind::Err => tcx.ty_error(),
|
||||
}
|
||||
@ -302,7 +305,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
unop: hir::UnOp,
|
||||
oprnd: &'tcx hir::Expr<'tcx>,
|
||||
expected: Expectation<'tcx>,
|
||||
needs: Needs,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
let tcx = self.tcx;
|
||||
@ -310,40 +312,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
hir::UnOp::UnNot | hir::UnOp::UnNeg => expected,
|
||||
hir::UnOp::UnDeref => NoExpectation,
|
||||
};
|
||||
let needs = match unop {
|
||||
hir::UnOp::UnDeref => needs,
|
||||
_ => Needs::None,
|
||||
};
|
||||
let mut oprnd_t = self.check_expr_with_expectation_and_needs(&oprnd, expected_inner, needs);
|
||||
let mut oprnd_t = self.check_expr_with_expectation(&oprnd, expected_inner);
|
||||
|
||||
if !oprnd_t.references_error() {
|
||||
oprnd_t = self.structurally_resolved_type(expr.span, oprnd_t);
|
||||
match unop {
|
||||
hir::UnOp::UnDeref => {
|
||||
if let Some(mt) = oprnd_t.builtin_deref(true) {
|
||||
oprnd_t = mt.ty;
|
||||
} else if let Some(ok) = self.try_overloaded_deref(expr.span, oprnd_t, needs) {
|
||||
let method = self.register_infer_ok_obligations(ok);
|
||||
if let ty::Ref(region, _, mutbl) = method.sig.inputs()[0].kind {
|
||||
let mutbl = match mutbl {
|
||||
hir::Mutability::Not => AutoBorrowMutability::Not,
|
||||
hir::Mutability::Mut => AutoBorrowMutability::Mut {
|
||||
// (It shouldn't actually matter for unary ops whether
|
||||
// we enable two-phase borrows or not, since a unary
|
||||
// op has no additional operands.)
|
||||
allow_two_phase_borrow: AllowTwoPhase::No,
|
||||
},
|
||||
};
|
||||
self.apply_adjustments(
|
||||
oprnd,
|
||||
vec![Adjustment {
|
||||
kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)),
|
||||
target: method.sig.inputs()[0],
|
||||
}],
|
||||
);
|
||||
}
|
||||
oprnd_t = self.make_overloaded_place_return_type(method).ty;
|
||||
self.write_method_call(expr.hir_id, method);
|
||||
if let Some(ty) = self.lookup_derefing(expr, oprnd, oprnd_t) {
|
||||
oprnd_t = ty;
|
||||
} else {
|
||||
let mut err = type_error_struct!(
|
||||
tcx.sess,
|
||||
@ -405,8 +381,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
_ => NoExpectation,
|
||||
}
|
||||
});
|
||||
let needs = Needs::maybe_mut_place(mutbl);
|
||||
let ty = self.check_expr_with_expectation_and_needs(&oprnd, hint, needs);
|
||||
let ty =
|
||||
self.check_expr_with_expectation_and_needs(&oprnd, hint, Needs::maybe_mut_place(mutbl));
|
||||
|
||||
let tm = ty::TypeAndMut { ty, mutbl };
|
||||
match kind {
|
||||
@ -861,10 +837,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
span: Span,
|
||||
args: &'tcx [hir::Expr<'tcx>],
|
||||
expected: Expectation<'tcx>,
|
||||
needs: Needs,
|
||||
) -> Ty<'tcx> {
|
||||
let rcvr = &args[0];
|
||||
let rcvr_t = self.check_expr_with_needs(&rcvr, needs);
|
||||
let rcvr_t = self.check_expr(&rcvr);
|
||||
// no need to check for bot/err -- callee does that
|
||||
let rcvr_t = self.structurally_resolved_type(args[0].span, rcvr_t);
|
||||
|
||||
@ -1443,11 +1418,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
fn check_field(
|
||||
&self,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
needs: Needs,
|
||||
base: &'tcx hir::Expr<'tcx>,
|
||||
field: Ident,
|
||||
) -> Ty<'tcx> {
|
||||
let expr_t = self.check_expr_with_needs(base, needs);
|
||||
let expr_t = self.check_expr(base);
|
||||
let expr_t = self.structurally_resolved_type(base.span, expr_t);
|
||||
let mut private_candidate = None;
|
||||
let mut autoderef = self.autoderef(expr.span, expr_t);
|
||||
@ -1467,7 +1441,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
// of error recovery.
|
||||
self.write_field_index(expr.hir_id, index);
|
||||
if field.vis.is_accessible_from(def_scope, self.tcx) {
|
||||
let adjustments = autoderef.adjust_steps(self, needs);
|
||||
let adjustments = autoderef.adjust_steps(self);
|
||||
self.apply_adjustments(base, adjustments);
|
||||
autoderef.finalize(self);
|
||||
|
||||
@ -1482,7 +1456,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
if let Ok(index) = fstr.parse::<usize>() {
|
||||
if fstr == index.to_string() {
|
||||
if let Some(field_ty) = tys.get(index) {
|
||||
let adjustments = autoderef.adjust_steps(self, needs);
|
||||
let adjustments = autoderef.adjust_steps(self);
|
||||
self.apply_adjustments(base, adjustments);
|
||||
autoderef.finalize(self);
|
||||
|
||||
@ -1721,10 +1695,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
&self,
|
||||
base: &'tcx hir::Expr<'tcx>,
|
||||
idx: &'tcx hir::Expr<'tcx>,
|
||||
needs: Needs,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
let base_t = self.check_expr_with_needs(&base, needs);
|
||||
let base_t = self.check_expr(&base);
|
||||
let idx_t = self.check_expr(&idx);
|
||||
|
||||
if base_t.references_error() {
|
||||
@ -1733,7 +1706,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
idx_t
|
||||
} else {
|
||||
let base_t = self.structurally_resolved_type(base.span, base_t);
|
||||
match self.lookup_indexing(expr, base, base_t, idx_t, needs) {
|
||||
match self.lookup_indexing(expr, base, base_t, idx_t) {
|
||||
Some((index_ty, element_ty)) => {
|
||||
// two-phase not needed because index_ty is never mutable
|
||||
self.demand_coerce(idx, idx_t, index_ty, None, AllowTwoPhase::No);
|
||||
|
@ -347,6 +347,8 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
|
||||
return;
|
||||
}
|
||||
|
||||
"count_code_region" => (0, vec![tcx.types.u32], tcx.mk_unit()),
|
||||
|
||||
ref other => {
|
||||
struct_span_err!(
|
||||
tcx.sess,
|
||||
|
@ -1,12 +1,12 @@
|
||||
use super::{probe, MethodCallee};
|
||||
|
||||
use crate::astconv::AstConv;
|
||||
use crate::check::{callee, FnCtxt, Needs, PlaceOp};
|
||||
use crate::check::{callee, FnCtxt};
|
||||
use crate::hir::def_id::DefId;
|
||||
use crate::hir::GenericArg;
|
||||
use rustc_hir as hir;
|
||||
use rustc_infer::infer::{self, InferOk};
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref, PointerCast};
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCast};
|
||||
use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
|
||||
use rustc_middle::ty::fold::TypeFoldable;
|
||||
use rustc_middle::ty::subst::{Subst, SubstsRef};
|
||||
@ -119,11 +119,6 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
|
||||
|
||||
// Create the final `MethodCallee`.
|
||||
let callee = MethodCallee { def_id: pick.item.def_id, substs: all_substs, sig: method_sig };
|
||||
|
||||
if let Some(hir::Mutability::Mut) = pick.autoref {
|
||||
self.convert_place_derefs_to_mutable();
|
||||
}
|
||||
|
||||
ConfirmResult { callee, illegal_sized_bound }
|
||||
}
|
||||
|
||||
@ -149,7 +144,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
|
||||
};
|
||||
assert_eq!(n, pick.autoderefs);
|
||||
|
||||
let mut adjustments = autoderef.adjust_steps(self, Needs::None);
|
||||
let mut adjustments = autoderef.adjust_steps(self);
|
||||
|
||||
let mut target = autoderef.unambiguous_final_ty(self);
|
||||
|
||||
@ -415,151 +410,6 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
|
||||
self.register_wf_obligation(fty.into(), self.span, traits::MiscObligation);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// RECONCILIATION
|
||||
|
||||
/// When we select a method with a mutable autoref, we have to go convert any
|
||||
/// auto-derefs, indices, etc from `Deref` and `Index` into `DerefMut` and `IndexMut`
|
||||
/// respectively.
|
||||
fn convert_place_derefs_to_mutable(&self) {
|
||||
// Gather up expressions we want to munge.
|
||||
let mut exprs = vec![self.self_expr];
|
||||
|
||||
loop {
|
||||
match exprs.last().unwrap().kind {
|
||||
hir::ExprKind::Field(ref expr, _)
|
||||
| hir::ExprKind::Index(ref expr, _)
|
||||
| hir::ExprKind::Unary(hir::UnOp::UnDeref, ref expr) => exprs.push(&expr),
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
|
||||
debug!("convert_place_derefs_to_mutable: exprs={:?}", exprs);
|
||||
|
||||
// Fix up autoderefs and derefs.
|
||||
for (i, &expr) in exprs.iter().rev().enumerate() {
|
||||
debug!("convert_place_derefs_to_mutable: i={} expr={:?}", i, expr);
|
||||
|
||||
// Fix up the autoderefs. Autorefs can only occur immediately preceding
|
||||
// overloaded place ops, and will be fixed by them in order to get
|
||||
// the correct region.
|
||||
let mut source = self.node_ty(expr.hir_id);
|
||||
// Do not mutate adjustments in place, but rather take them,
|
||||
// and replace them after mutating them, to avoid having the
|
||||
// tables borrowed during (`deref_mut`) method resolution.
|
||||
let previous_adjustments =
|
||||
self.tables.borrow_mut().adjustments_mut().remove(expr.hir_id);
|
||||
if let Some(mut adjustments) = previous_adjustments {
|
||||
let needs = Needs::MutPlace;
|
||||
for adjustment in &mut adjustments {
|
||||
if let Adjust::Deref(Some(ref mut deref)) = adjustment.kind {
|
||||
if let Some(ok) = self.try_overloaded_deref(expr.span, source, needs) {
|
||||
let method = self.register_infer_ok_obligations(ok);
|
||||
if let ty::Ref(region, _, mutbl) = method.sig.output().kind {
|
||||
*deref = OverloadedDeref { region, mutbl };
|
||||
}
|
||||
}
|
||||
}
|
||||
source = adjustment.target;
|
||||
}
|
||||
self.tables.borrow_mut().adjustments_mut().insert(expr.hir_id, adjustments);
|
||||
}
|
||||
|
||||
match expr.kind {
|
||||
hir::ExprKind::Index(ref base_expr, ref index_expr) => {
|
||||
// We need to get the final type in case dereferences were needed for the trait
|
||||
// to apply (#72002).
|
||||
let index_expr_ty = self.tables.borrow().expr_ty_adjusted(index_expr);
|
||||
self.convert_place_op_to_mutable(
|
||||
PlaceOp::Index,
|
||||
expr,
|
||||
base_expr,
|
||||
&[index_expr_ty],
|
||||
);
|
||||
}
|
||||
hir::ExprKind::Unary(hir::UnOp::UnDeref, ref base_expr) => {
|
||||
self.convert_place_op_to_mutable(PlaceOp::Deref, expr, base_expr, &[]);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_place_op_to_mutable(
|
||||
&self,
|
||||
op: PlaceOp,
|
||||
expr: &hir::Expr<'_>,
|
||||
base_expr: &hir::Expr<'_>,
|
||||
arg_tys: &[Ty<'tcx>],
|
||||
) {
|
||||
debug!("convert_place_op_to_mutable({:?}, {:?}, {:?}, {:?})", op, expr, base_expr, arg_tys);
|
||||
if !self.tables.borrow().is_method_call(expr) {
|
||||
debug!("convert_place_op_to_mutable - builtin, nothing to do");
|
||||
return;
|
||||
}
|
||||
|
||||
let base_ty = self
|
||||
.tables
|
||||
.borrow()
|
||||
.expr_adjustments(base_expr)
|
||||
.last()
|
||||
.map_or_else(|| self.node_ty(expr.hir_id), |adj| adj.target);
|
||||
let base_ty = self.resolve_vars_if_possible(&base_ty);
|
||||
|
||||
// Need to deref because overloaded place ops take self by-reference.
|
||||
let base_ty =
|
||||
base_ty.builtin_deref(false).expect("place op takes something that is not a ref").ty;
|
||||
|
||||
let method = self.try_overloaded_place_op(expr.span, base_ty, arg_tys, Needs::MutPlace, op);
|
||||
let method = match method {
|
||||
Some(ok) => self.register_infer_ok_obligations(ok),
|
||||
None => return self.tcx.sess.delay_span_bug(expr.span, "re-trying op failed"),
|
||||
};
|
||||
debug!("convert_place_op_to_mutable: method={:?}", method);
|
||||
self.write_method_call(expr.hir_id, method);
|
||||
|
||||
let (region, mutbl) = if let ty::Ref(r, _, mutbl) = method.sig.inputs()[0].kind {
|
||||
(r, mutbl)
|
||||
} else {
|
||||
span_bug!(expr.span, "input to place op is not a ref?");
|
||||
};
|
||||
|
||||
// Convert the autoref in the base expr to mutable with the correct
|
||||
// region and mutability.
|
||||
let base_expr_ty = self.node_ty(base_expr.hir_id);
|
||||
if let Some(adjustments) =
|
||||
self.tables.borrow_mut().adjustments_mut().get_mut(base_expr.hir_id)
|
||||
{
|
||||
let mut source = base_expr_ty;
|
||||
for adjustment in &mut adjustments[..] {
|
||||
if let Adjust::Borrow(AutoBorrow::Ref(..)) = adjustment.kind {
|
||||
debug!("convert_place_op_to_mutable: converting autoref {:?}", adjustment);
|
||||
let mutbl = match mutbl {
|
||||
hir::Mutability::Not => AutoBorrowMutability::Not,
|
||||
hir::Mutability::Mut => AutoBorrowMutability::Mut {
|
||||
// For initial two-phase borrow
|
||||
// deployment, conservatively omit
|
||||
// overloaded operators.
|
||||
allow_two_phase_borrow: AllowTwoPhase::No,
|
||||
},
|
||||
};
|
||||
adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(region, mutbl));
|
||||
adjustment.target =
|
||||
self.tcx.mk_ref(region, ty::TypeAndMut { ty: source, mutbl: mutbl.into() });
|
||||
}
|
||||
source = adjustment.target;
|
||||
}
|
||||
|
||||
// If we have an autoref followed by unsizing at the end, fix the unsize target.
|
||||
|
||||
if let [.., Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. }, Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), ref mut target }] =
|
||||
adjustments[..]
|
||||
{
|
||||
*target = method.sig.inputs()[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// MISCELLANY
|
||||
|
||||
|
@ -79,6 +79,7 @@ pub mod intrinsic;
|
||||
pub mod method;
|
||||
mod op;
|
||||
mod pat;
|
||||
mod place_op;
|
||||
mod regionck;
|
||||
mod upvar;
|
||||
mod wfcheck;
|
||||
@ -114,7 +115,7 @@ use rustc_infer::infer::{InferCtxt, InferOk, InferResult, RegionVariableOrigin,
|
||||
use rustc_middle::hir::map::blocks::FnLikeNode;
|
||||
use rustc_middle::mir::interpret::ConstValue;
|
||||
use rustc_middle::ty::adjustment::{
|
||||
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCast,
|
||||
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
|
||||
};
|
||||
use rustc_middle::ty::fold::{TypeFoldable, TypeFolder};
|
||||
use rustc_middle::ty::query::Providers;
|
||||
@ -156,7 +157,6 @@ use std::slice;
|
||||
use crate::require_c_abi_if_c_variadic;
|
||||
use crate::util::common::indenter;
|
||||
|
||||
use self::autoderef::Autoderef;
|
||||
use self::callee::DeferredCallResolution;
|
||||
use self::coercion::{CoerceMany, DynamicCoerceMany};
|
||||
use self::compare_method::{compare_const_impl, compare_impl_method, compare_ty_impl};
|
||||
@ -3333,6 +3333,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
return;
|
||||
}
|
||||
|
||||
let autoborrow_mut = adj.iter().any(|adj| {
|
||||
matches!(adj, &Adjustment {
|
||||
kind: Adjust::Borrow(AutoBorrow::Ref(_, AutoBorrowMutability::Mut { .. })),
|
||||
..
|
||||
})
|
||||
});
|
||||
|
||||
match self.tables.borrow_mut().adjustments_mut().entry(expr.hir_id) {
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(adj);
|
||||
@ -3362,6 +3369,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
*entry.get_mut() = adj;
|
||||
}
|
||||
}
|
||||
|
||||
// If there is an mutable auto-borrow, it is equivalent to `&mut <expr>`.
|
||||
// In this case implicit use of `Deref` and `Index` within `<expr>` should
|
||||
// instead be `DerefMut` and `IndexMut`, so fix those up.
|
||||
if autoborrow_mut {
|
||||
self.convert_place_derefs_to_mutable(expr);
|
||||
}
|
||||
}
|
||||
|
||||
/// Basically whenever we are converting from a type scheme into
|
||||
@ -3753,154 +3767,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
ret_ty.builtin_deref(true).unwrap()
|
||||
}
|
||||
|
||||
fn lookup_indexing(
|
||||
&self,
|
||||
expr: &hir::Expr<'_>,
|
||||
base_expr: &'tcx hir::Expr<'tcx>,
|
||||
base_ty: Ty<'tcx>,
|
||||
idx_ty: Ty<'tcx>,
|
||||
needs: Needs,
|
||||
) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> {
|
||||
// FIXME(#18741) -- this is almost but not quite the same as the
|
||||
// autoderef that normal method probing does. They could likely be
|
||||
// consolidated.
|
||||
|
||||
let mut autoderef = self.autoderef(base_expr.span, base_ty);
|
||||
let mut result = None;
|
||||
while result.is_none() && autoderef.next().is_some() {
|
||||
result = self.try_index_step(expr, base_expr, &autoderef, needs, idx_ty);
|
||||
}
|
||||
autoderef.finalize(self);
|
||||
result
|
||||
}
|
||||
|
||||
/// To type-check `base_expr[index_expr]`, we progressively autoderef
|
||||
/// (and otherwise adjust) `base_expr`, looking for a type which either
|
||||
/// supports builtin indexing or overloaded indexing.
|
||||
/// This loop implements one step in that search; the autoderef loop
|
||||
/// is implemented by `lookup_indexing`.
|
||||
fn try_index_step(
|
||||
&self,
|
||||
expr: &hir::Expr<'_>,
|
||||
base_expr: &hir::Expr<'_>,
|
||||
autoderef: &Autoderef<'a, 'tcx>,
|
||||
needs: Needs,
|
||||
index_ty: Ty<'tcx>,
|
||||
) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> {
|
||||
let adjusted_ty = autoderef.unambiguous_final_ty(self);
|
||||
debug!(
|
||||
"try_index_step(expr={:?}, base_expr={:?}, adjusted_ty={:?}, \
|
||||
index_ty={:?})",
|
||||
expr, base_expr, adjusted_ty, index_ty
|
||||
);
|
||||
|
||||
for &unsize in &[false, true] {
|
||||
let mut self_ty = adjusted_ty;
|
||||
if unsize {
|
||||
// We only unsize arrays here.
|
||||
if let ty::Array(element_ty, _) = adjusted_ty.kind {
|
||||
self_ty = self.tcx.mk_slice(element_ty);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// If some lookup succeeds, write callee into table and extract index/element
|
||||
// type from the method signature.
|
||||
// If some lookup succeeded, install method in table
|
||||
let input_ty = self.next_ty_var(TypeVariableOrigin {
|
||||
kind: TypeVariableOriginKind::AutoDeref,
|
||||
span: base_expr.span,
|
||||
});
|
||||
let method = self.try_overloaded_place_op(
|
||||
expr.span,
|
||||
self_ty,
|
||||
&[input_ty],
|
||||
needs,
|
||||
PlaceOp::Index,
|
||||
);
|
||||
|
||||
let result = method.map(|ok| {
|
||||
debug!("try_index_step: success, using overloaded indexing");
|
||||
let method = self.register_infer_ok_obligations(ok);
|
||||
|
||||
let mut adjustments = autoderef.adjust_steps(self, needs);
|
||||
if let ty::Ref(region, _, r_mutbl) = method.sig.inputs()[0].kind {
|
||||
let mutbl = match r_mutbl {
|
||||
hir::Mutability::Not => AutoBorrowMutability::Not,
|
||||
hir::Mutability::Mut => AutoBorrowMutability::Mut {
|
||||
// Indexing can be desugared to a method call,
|
||||
// so maybe we could use two-phase here.
|
||||
// See the documentation of AllowTwoPhase for why that's
|
||||
// not the case today.
|
||||
allow_two_phase_borrow: AllowTwoPhase::No,
|
||||
},
|
||||
};
|
||||
adjustments.push(Adjustment {
|
||||
kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)),
|
||||
target: self
|
||||
.tcx
|
||||
.mk_ref(region, ty::TypeAndMut { mutbl: r_mutbl, ty: adjusted_ty }),
|
||||
});
|
||||
}
|
||||
if unsize {
|
||||
adjustments.push(Adjustment {
|
||||
kind: Adjust::Pointer(PointerCast::Unsize),
|
||||
target: method.sig.inputs()[0],
|
||||
});
|
||||
}
|
||||
self.apply_adjustments(base_expr, adjustments);
|
||||
|
||||
self.write_method_call(expr.hir_id, method);
|
||||
(input_ty, self.make_overloaded_place_return_type(method).ty)
|
||||
});
|
||||
if result.is_some() {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn resolve_place_op(&self, op: PlaceOp, is_mut: bool) -> (Option<DefId>, Ident) {
|
||||
let (tr, name) = match (op, is_mut) {
|
||||
(PlaceOp::Deref, false) => (self.tcx.lang_items().deref_trait(), sym::deref),
|
||||
(PlaceOp::Deref, true) => (self.tcx.lang_items().deref_mut_trait(), sym::deref_mut),
|
||||
(PlaceOp::Index, false) => (self.tcx.lang_items().index_trait(), sym::index),
|
||||
(PlaceOp::Index, true) => (self.tcx.lang_items().index_mut_trait(), sym::index_mut),
|
||||
};
|
||||
(tr, Ident::with_dummy_span(name))
|
||||
}
|
||||
|
||||
fn try_overloaded_place_op(
|
||||
&self,
|
||||
span: Span,
|
||||
base_ty: Ty<'tcx>,
|
||||
arg_tys: &[Ty<'tcx>],
|
||||
needs: Needs,
|
||||
op: PlaceOp,
|
||||
) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
|
||||
debug!("try_overloaded_place_op({:?},{:?},{:?},{:?})", span, base_ty, needs, op);
|
||||
|
||||
// Try Mut first, if needed.
|
||||
let (mut_tr, mut_op) = self.resolve_place_op(op, true);
|
||||
let method = match (needs, mut_tr) {
|
||||
(Needs::MutPlace, Some(trait_did)) => {
|
||||
self.lookup_method_in_trait(span, mut_op, trait_did, base_ty, Some(arg_tys))
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
// Otherwise, fall back to the immutable version.
|
||||
let (imm_tr, imm_op) = self.resolve_place_op(op, false);
|
||||
match (method, imm_tr) {
|
||||
(None, Some(trait_did)) => {
|
||||
self.lookup_method_in_trait(span, imm_op, trait_did, base_ty, Some(arg_tys))
|
||||
}
|
||||
(method, _) => method,
|
||||
}
|
||||
}
|
||||
|
||||
fn check_method_argument_types(
|
||||
&self,
|
||||
sp: Span,
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! Code related to processing overloaded binary and unary operators.
|
||||
|
||||
use super::method::MethodCallee;
|
||||
use super::{FnCtxt, Needs};
|
||||
use super::FnCtxt;
|
||||
use rustc_errors::{self, struct_span_err, Applicability, DiagnosticBuilder};
|
||||
use rustc_hir as hir;
|
||||
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||
@ -165,7 +165,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
// trait matching creating lifetime constraints that are too strict.
|
||||
// e.g., adding `&'a T` and `&'b T`, given `&'x T: Add<&'x T>`, will result
|
||||
// in `&'a T <: &'x T` and `&'b T <: &'x T`, instead of `'a = 'b = 'x`.
|
||||
let lhs_ty = self.check_expr_with_needs(lhs_expr, Needs::None);
|
||||
let lhs_ty = self.check_expr(lhs_expr);
|
||||
let fresh_var = self.next_ty_var(TypeVariableOrigin {
|
||||
kind: TypeVariableOriginKind::MiscVariable,
|
||||
span: lhs_expr.span,
|
||||
@ -177,7 +177,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
// equivalence on the LHS of an assign-op like `+=`;
|
||||
// overwritten or mutably-borrowed places cannot be
|
||||
// coerced to a supertype.
|
||||
self.check_expr_with_needs(lhs_expr, Needs::MutPlace)
|
||||
self.check_expr(lhs_expr)
|
||||
}
|
||||
};
|
||||
let lhs_ty = self.resolve_vars_with_obligations(lhs_ty);
|
||||
|
336
src/librustc_typeck/check/place_op.rs
Normal file
336
src/librustc_typeck/check/place_op.rs
Normal file
@ -0,0 +1,336 @@
|
||||
use crate::check::autoderef::Autoderef;
|
||||
use crate::check::method::MethodCallee;
|
||||
use crate::check::{FnCtxt, PlaceOp};
|
||||
use rustc_hir as hir;
|
||||
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||
use rustc_infer::infer::InferOk;
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref, PointerCast};
|
||||
use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_span::symbol::{sym, Ident};
|
||||
use rustc_span::Span;
|
||||
|
||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
/// Type-check `*oprnd_expr` with `oprnd_expr` type-checked already.
|
||||
pub(super) fn lookup_derefing(
|
||||
&self,
|
||||
expr: &hir::Expr<'_>,
|
||||
oprnd_expr: &'tcx hir::Expr<'tcx>,
|
||||
oprnd_ty: Ty<'tcx>,
|
||||
) -> Option<Ty<'tcx>> {
|
||||
if let Some(mt) = oprnd_ty.builtin_deref(true) {
|
||||
return Some(mt.ty);
|
||||
}
|
||||
|
||||
let ok = self.try_overloaded_deref(expr.span, oprnd_ty)?;
|
||||
let method = self.register_infer_ok_obligations(ok);
|
||||
if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind {
|
||||
self.apply_adjustments(
|
||||
oprnd_expr,
|
||||
vec![Adjustment {
|
||||
kind: Adjust::Borrow(AutoBorrow::Ref(region, AutoBorrowMutability::Not)),
|
||||
target: method.sig.inputs()[0],
|
||||
}],
|
||||
);
|
||||
} else {
|
||||
span_bug!(expr.span, "input to deref is not a ref?");
|
||||
}
|
||||
let ty = self.make_overloaded_place_return_type(method).ty;
|
||||
self.write_method_call(expr.hir_id, method);
|
||||
Some(ty)
|
||||
}
|
||||
|
||||
/// Type-check `*base_expr[index_expr]` with `base_expr` and `index_expr` type-checked already.
|
||||
pub(super) fn lookup_indexing(
|
||||
&self,
|
||||
expr: &hir::Expr<'_>,
|
||||
base_expr: &'tcx hir::Expr<'tcx>,
|
||||
base_ty: Ty<'tcx>,
|
||||
idx_ty: Ty<'tcx>,
|
||||
) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> {
|
||||
// FIXME(#18741) -- this is almost but not quite the same as the
|
||||
// autoderef that normal method probing does. They could likely be
|
||||
// consolidated.
|
||||
|
||||
let mut autoderef = self.autoderef(base_expr.span, base_ty);
|
||||
let mut result = None;
|
||||
while result.is_none() && autoderef.next().is_some() {
|
||||
result = self.try_index_step(expr, base_expr, &autoderef, idx_ty);
|
||||
}
|
||||
autoderef.finalize(self);
|
||||
result
|
||||
}
|
||||
|
||||
/// To type-check `base_expr[index_expr]`, we progressively autoderef
|
||||
/// (and otherwise adjust) `base_expr`, looking for a type which either
|
||||
/// supports builtin indexing or overloaded indexing.
|
||||
/// This loop implements one step in that search; the autoderef loop
|
||||
/// is implemented by `lookup_indexing`.
|
||||
fn try_index_step(
|
||||
&self,
|
||||
expr: &hir::Expr<'_>,
|
||||
base_expr: &hir::Expr<'_>,
|
||||
autoderef: &Autoderef<'a, 'tcx>,
|
||||
index_ty: Ty<'tcx>,
|
||||
) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> {
|
||||
let adjusted_ty = autoderef.unambiguous_final_ty(self);
|
||||
debug!(
|
||||
"try_index_step(expr={:?}, base_expr={:?}, adjusted_ty={:?}, \
|
||||
index_ty={:?})",
|
||||
expr, base_expr, adjusted_ty, index_ty
|
||||
);
|
||||
|
||||
for &unsize in &[false, true] {
|
||||
let mut self_ty = adjusted_ty;
|
||||
if unsize {
|
||||
// We only unsize arrays here.
|
||||
if let ty::Array(element_ty, _) = adjusted_ty.kind {
|
||||
self_ty = self.tcx.mk_slice(element_ty);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// If some lookup succeeds, write callee into table and extract index/element
|
||||
// type from the method signature.
|
||||
// If some lookup succeeded, install method in table
|
||||
let input_ty = self.next_ty_var(TypeVariableOrigin {
|
||||
kind: TypeVariableOriginKind::AutoDeref,
|
||||
span: base_expr.span,
|
||||
});
|
||||
let method =
|
||||
self.try_overloaded_place_op(expr.span, self_ty, &[input_ty], PlaceOp::Index);
|
||||
|
||||
let result = method.map(|ok| {
|
||||
debug!("try_index_step: success, using overloaded indexing");
|
||||
let method = self.register_infer_ok_obligations(ok);
|
||||
|
||||
let mut adjustments = autoderef.adjust_steps(self);
|
||||
if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind {
|
||||
adjustments.push(Adjustment {
|
||||
kind: Adjust::Borrow(AutoBorrow::Ref(region, AutoBorrowMutability::Not)),
|
||||
target: self.tcx.mk_ref(
|
||||
region,
|
||||
ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: adjusted_ty },
|
||||
),
|
||||
});
|
||||
} else {
|
||||
span_bug!(expr.span, "input to index is not a ref?");
|
||||
}
|
||||
if unsize {
|
||||
adjustments.push(Adjustment {
|
||||
kind: Adjust::Pointer(PointerCast::Unsize),
|
||||
target: method.sig.inputs()[0],
|
||||
});
|
||||
}
|
||||
self.apply_adjustments(base_expr, adjustments);
|
||||
|
||||
self.write_method_call(expr.hir_id, method);
|
||||
(input_ty, self.make_overloaded_place_return_type(method).ty)
|
||||
});
|
||||
if result.is_some() {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Try to resolve an overloaded place op. We only deal with the immutable
|
||||
/// variant here (Deref/Index). In some contexts we would need the mutable
|
||||
/// variant (DerefMut/IndexMut); those would be later converted by
|
||||
/// `convert_place_derefs_to_mutable`.
|
||||
pub(super) fn try_overloaded_place_op(
|
||||
&self,
|
||||
span: Span,
|
||||
base_ty: Ty<'tcx>,
|
||||
arg_tys: &[Ty<'tcx>],
|
||||
op: PlaceOp,
|
||||
) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
|
||||
debug!("try_overloaded_place_op({:?},{:?},{:?})", span, base_ty, op);
|
||||
|
||||
let (imm_tr, imm_op) = match op {
|
||||
PlaceOp::Deref => (self.tcx.lang_items().deref_trait(), sym::deref),
|
||||
PlaceOp::Index => (self.tcx.lang_items().index_trait(), sym::index),
|
||||
};
|
||||
imm_tr.and_then(|trait_did| {
|
||||
self.lookup_method_in_trait(
|
||||
span,
|
||||
Ident::with_dummy_span(imm_op),
|
||||
trait_did,
|
||||
base_ty,
|
||||
Some(arg_tys),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn try_mutable_overloaded_place_op(
|
||||
&self,
|
||||
span: Span,
|
||||
base_ty: Ty<'tcx>,
|
||||
arg_tys: &[Ty<'tcx>],
|
||||
op: PlaceOp,
|
||||
) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
|
||||
debug!("try_mutable_overloaded_place_op({:?},{:?},{:?})", span, base_ty, op);
|
||||
|
||||
let (mut_tr, mut_op) = match op {
|
||||
PlaceOp::Deref => (self.tcx.lang_items().deref_mut_trait(), sym::deref_mut),
|
||||
PlaceOp::Index => (self.tcx.lang_items().index_mut_trait(), sym::index_mut),
|
||||
};
|
||||
mut_tr.and_then(|trait_did| {
|
||||
self.lookup_method_in_trait(
|
||||
span,
|
||||
Ident::with_dummy_span(mut_op),
|
||||
trait_did,
|
||||
base_ty,
|
||||
Some(arg_tys),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Convert auto-derefs, indices, etc of an expression from `Deref` and `Index`
|
||||
/// into `DerefMut` and `IndexMut` respectively.
|
||||
///
|
||||
/// This is a second pass of typechecking derefs/indices. We need this we do not
|
||||
/// always know whether a place needs to be mutable or not in the first pass.
|
||||
/// This happens whether there is an implicit mutable reborrow, e.g. when the type
|
||||
/// is used as the receiver of a method call.
|
||||
pub fn convert_place_derefs_to_mutable(&self, expr: &hir::Expr<'_>) {
|
||||
// Gather up expressions we want to munge.
|
||||
let mut exprs = vec![expr];
|
||||
|
||||
loop {
|
||||
match exprs.last().unwrap().kind {
|
||||
hir::ExprKind::Field(ref expr, _)
|
||||
| hir::ExprKind::Index(ref expr, _)
|
||||
| hir::ExprKind::Unary(hir::UnOp::UnDeref, ref expr) => exprs.push(&expr),
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
|
||||
debug!("convert_place_derefs_to_mutable: exprs={:?}", exprs);
|
||||
|
||||
// Fix up autoderefs and derefs.
|
||||
for (i, &expr) in exprs.iter().rev().enumerate() {
|
||||
debug!("convert_place_derefs_to_mutable: i={} expr={:?}", i, expr);
|
||||
|
||||
// Fix up the autoderefs. Autorefs can only occur immediately preceding
|
||||
// overloaded place ops, and will be fixed by them in order to get
|
||||
// the correct region.
|
||||
let mut source = self.node_ty(expr.hir_id);
|
||||
// Do not mutate adjustments in place, but rather take them,
|
||||
// and replace them after mutating them, to avoid having the
|
||||
// tables borrowed during (`deref_mut`) method resolution.
|
||||
let previous_adjustments =
|
||||
self.tables.borrow_mut().adjustments_mut().remove(expr.hir_id);
|
||||
if let Some(mut adjustments) = previous_adjustments {
|
||||
for adjustment in &mut adjustments {
|
||||
if let Adjust::Deref(Some(ref mut deref)) = adjustment.kind {
|
||||
if let Some(ok) = self.try_mutable_overloaded_place_op(
|
||||
expr.span,
|
||||
source,
|
||||
&[],
|
||||
PlaceOp::Deref,
|
||||
) {
|
||||
let method = self.register_infer_ok_obligations(ok);
|
||||
if let ty::Ref(region, _, mutbl) = method.sig.output().kind {
|
||||
*deref = OverloadedDeref { region, mutbl };
|
||||
}
|
||||
}
|
||||
}
|
||||
source = adjustment.target;
|
||||
}
|
||||
self.tables.borrow_mut().adjustments_mut().insert(expr.hir_id, adjustments);
|
||||
}
|
||||
|
||||
match expr.kind {
|
||||
hir::ExprKind::Index(ref base_expr, ref index_expr) => {
|
||||
// We need to get the final type in case dereferences were needed for the trait
|
||||
// to apply (#72002).
|
||||
let index_expr_ty = self.tables.borrow().expr_ty_adjusted(index_expr);
|
||||
self.convert_place_op_to_mutable(
|
||||
PlaceOp::Index,
|
||||
expr,
|
||||
base_expr,
|
||||
&[index_expr_ty],
|
||||
);
|
||||
}
|
||||
hir::ExprKind::Unary(hir::UnOp::UnDeref, ref base_expr) => {
|
||||
self.convert_place_op_to_mutable(PlaceOp::Deref, expr, base_expr, &[]);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_place_op_to_mutable(
|
||||
&self,
|
||||
op: PlaceOp,
|
||||
expr: &hir::Expr<'_>,
|
||||
base_expr: &hir::Expr<'_>,
|
||||
arg_tys: &[Ty<'tcx>],
|
||||
) {
|
||||
debug!("convert_place_op_to_mutable({:?}, {:?}, {:?}, {:?})", op, expr, base_expr, arg_tys);
|
||||
if !self.tables.borrow().is_method_call(expr) {
|
||||
debug!("convert_place_op_to_mutable - builtin, nothing to do");
|
||||
return;
|
||||
}
|
||||
|
||||
// Need to deref because overloaded place ops take self by-reference.
|
||||
let base_ty = self
|
||||
.tables
|
||||
.borrow()
|
||||
.expr_ty_adjusted(base_expr)
|
||||
.builtin_deref(false)
|
||||
.expect("place op takes something that is not a ref")
|
||||
.ty;
|
||||
|
||||
let method = self.try_mutable_overloaded_place_op(expr.span, base_ty, arg_tys, op);
|
||||
let method = match method {
|
||||
Some(ok) => self.register_infer_ok_obligations(ok),
|
||||
// Couldn't find the mutable variant of the place op, keep the
|
||||
// current, immutable version.
|
||||
None => return,
|
||||
};
|
||||
debug!("convert_place_op_to_mutable: method={:?}", method);
|
||||
self.write_method_call(expr.hir_id, method);
|
||||
|
||||
let region = if let ty::Ref(r, _, hir::Mutability::Mut) = method.sig.inputs()[0].kind {
|
||||
r
|
||||
} else {
|
||||
span_bug!(expr.span, "input to mutable place op is not a mut ref?");
|
||||
};
|
||||
|
||||
// Convert the autoref in the base expr to mutable with the correct
|
||||
// region and mutability.
|
||||
let base_expr_ty = self.node_ty(base_expr.hir_id);
|
||||
if let Some(adjustments) =
|
||||
self.tables.borrow_mut().adjustments_mut().get_mut(base_expr.hir_id)
|
||||
{
|
||||
let mut source = base_expr_ty;
|
||||
for adjustment in &mut adjustments[..] {
|
||||
if let Adjust::Borrow(AutoBorrow::Ref(..)) = adjustment.kind {
|
||||
debug!("convert_place_op_to_mutable: converting autoref {:?}", adjustment);
|
||||
let mutbl = AutoBorrowMutability::Mut {
|
||||
// Deref/indexing can be desugared to a method call,
|
||||
// so maybe we could use two-phase here.
|
||||
// See the documentation of AllowTwoPhase for why that's
|
||||
// not the case today.
|
||||
allow_two_phase_borrow: AllowTwoPhase::No,
|
||||
};
|
||||
adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(region, mutbl));
|
||||
adjustment.target =
|
||||
self.tcx.mk_ref(region, ty::TypeAndMut { ty: source, mutbl: mutbl.into() });
|
||||
}
|
||||
source = adjustment.target;
|
||||
}
|
||||
|
||||
// If we have an autoref followed by unsizing at the end, fix the unsize target.
|
||||
if let [.., Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. }, Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), ref mut target }] =
|
||||
adjustments[..]
|
||||
{
|
||||
*target = method.sig.inputs()[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@
|
||||
#include "llvm/IR/DiagnosticPrinter.h"
|
||||
#include "llvm/IR/GlobalVariable.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
#include "llvm/IR/Intrinsics.h"
|
||||
#include "llvm/Object/Archive.h"
|
||||
#include "llvm/Object/ObjectFile.h"
|
||||
#include "llvm/Bitcode/BitcodeWriterPass.h"
|
||||
@ -1364,6 +1365,11 @@ extern "C" LLVMValueRef LLVMRustBuildCall(LLVMBuilderRef B, LLVMValueRef Fn,
|
||||
unwrap(Fn), makeArrayRef(unwrap(Args), NumArgs), Bundles));
|
||||
}
|
||||
|
||||
extern "C" LLVMValueRef LLVMRustGetInstrprofIncrementIntrinsic(LLVMModuleRef M) {
|
||||
return wrap(llvm::Intrinsic::getDeclaration(unwrap(M),
|
||||
(llvm::Intrinsic::ID)llvm::Intrinsic::instrprof_increment));
|
||||
}
|
||||
|
||||
extern "C" LLVMValueRef LLVMRustBuildMemCpy(LLVMBuilderRef B,
|
||||
LLVMValueRef Dst, unsigned DstAlign,
|
||||
LLVMValueRef Src, unsigned SrcAlign,
|
||||
|
@ -1,40 +0,0 @@
|
||||
// Verifies that during compiler_builtins compilation the codegen units are kept
|
||||
// unmerged. Even when only a single codegen unit is requested with -Ccodegen-units=1.
|
||||
//
|
||||
// compile-flags: -Zprint-mono-items=eager -Ccodegen-units=1
|
||||
|
||||
#![compiler_builtins]
|
||||
#![crate_type="lib"]
|
||||
#![feature(compiler_builtins)]
|
||||
|
||||
mod atomics {
|
||||
//~ MONO_ITEM fn compiler_builtins::atomics[0]::sync_1[0] @@ compiler_builtins-cgu.0[External]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn sync_1() {}
|
||||
|
||||
//~ MONO_ITEM fn compiler_builtins::atomics[0]::sync_2[0] @@ compiler_builtins-cgu.0[External]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn sync_2() {}
|
||||
|
||||
//~ MONO_ITEM fn compiler_builtins::atomics[0]::sync_3[0] @@ compiler_builtins-cgu.0[External]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn sync_3() {}
|
||||
}
|
||||
|
||||
mod x {
|
||||
//~ MONO_ITEM fn compiler_builtins::x[0]::x[0] @@ compiler_builtins-cgu.1[External]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn x() {}
|
||||
}
|
||||
|
||||
mod y {
|
||||
//~ MONO_ITEM fn compiler_builtins::y[0]::y[0] @@ compiler_builtins-cgu.2[External]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn y() {}
|
||||
}
|
||||
|
||||
mod z {
|
||||
//~ MONO_ITEM fn compiler_builtins::z[0]::z[0] @@ compiler_builtins-cgu.3[External]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn z() {}
|
||||
}
|
@ -1,9 +1,7 @@
|
||||
// Verifies that MemorySanitizer track-origins level can be controlled
|
||||
// with -Zsanitizer-memory-track-origins option.
|
||||
//
|
||||
// needs-sanitizer-support
|
||||
// only-linux
|
||||
// only-x86_64
|
||||
// needs-sanitizer-memory
|
||||
// revisions:MSAN-0 MSAN-1 MSAN-2 MSAN-1-LTO MSAN-2-LTO
|
||||
//
|
||||
//[MSAN-0] compile-flags: -Zsanitizer=memory
|
||||
|
@ -1,11 +1,9 @@
|
||||
// Verifies that no_sanitize attribute prevents inlining when
|
||||
// given sanitizer is enabled, but has no effect on inlining otherwise.
|
||||
//
|
||||
// needs-sanitizer-support
|
||||
// only-x86_64
|
||||
//
|
||||
// needs-sanitizer-address
|
||||
// needs-sanitizer-leak
|
||||
// revisions: ASAN LSAN
|
||||
//
|
||||
//[ASAN] compile-flags: -Zsanitizer=address -C opt-level=3 -Z mir-opt-level=3
|
||||
//[LSAN] compile-flags: -Zsanitizer=leak -C opt-level=3 -Z mir-opt-level=3
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Verifies that no_sanitze attribute can be used to
|
||||
// selectively disable sanitizer instrumentation.
|
||||
//
|
||||
// needs-sanitizer-support
|
||||
// needs-sanitizer-address
|
||||
// compile-flags: -Zsanitizer=address
|
||||
|
||||
#![crate_type="lib"]
|
||||
|
@ -1,9 +1,8 @@
|
||||
// Verifies that AddressSanitizer and MemorySanitizer
|
||||
// recovery mode can be enabled with -Zsanitizer-recover.
|
||||
//
|
||||
// needs-sanitizer-support
|
||||
// only-linux
|
||||
// only-x86_64
|
||||
// needs-sanitizer-address
|
||||
// needs-sanitizer-memory
|
||||
// revisions:ASAN ASAN-RECOVER MSAN MSAN-RECOVER MSAN-RECOVER-LTO
|
||||
// no-prefer-dynamic
|
||||
//
|
||||
|
@ -1,4 +1,3 @@
|
||||
// ignore-debug: the debug assertions get in the way
|
||||
// compile-flags: -O
|
||||
|
||||
#![crate_type = "lib"]
|
||||
|
@ -1,4 +1,3 @@
|
||||
//
|
||||
// ignore-debug: the debug assertions get in the way
|
||||
// no-system-llvm
|
||||
// compile-flags: -O
|
||||
|
20
src/test/mir-opt/instrument_coverage.rs
Normal file
20
src/test/mir-opt/instrument_coverage.rs
Normal file
@ -0,0 +1,20 @@
|
||||
// Test that the initial version of Rust coverage injects count_code_region() placeholder calls,
|
||||
// at the top of each function. The placeholders are later converted into LLVM instrprof.increment
|
||||
// intrinsics, during codegen.
|
||||
|
||||
// needs-profiler-support
|
||||
// compile-flags: -Zinstrument-coverage
|
||||
// EMIT_MIR rustc.main.InstrumentCoverage.diff
|
||||
// EMIT_MIR rustc.bar.InstrumentCoverage.diff
|
||||
fn main() {
|
||||
loop {
|
||||
if bar() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
fn bar() -> bool {
|
||||
true
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
- // MIR for `bar` before InstrumentCoverage
|
||||
+ // MIR for `bar` after InstrumentCoverage
|
||||
|
||||
fn bar() -> bool {
|
||||
let mut _0: bool; // return place in scope 0 at $DIR/instrument_coverage.rs:18:13: 18:17
|
||||
+ let mut _1: (); // in scope 0 at $DIR/instrument_coverage.rs:18:1: 20:2
|
||||
|
||||
bb0: {
|
||||
+ StorageLive(_1); // scope 0 at $DIR/instrument_coverage.rs:18:1: 20:2
|
||||
+ _1 = const std::intrinsics::count_code_region(const 0u32) -> bb2; // scope 0 at $DIR/instrument_coverage.rs:18:1: 20:2
|
||||
+ // ty::Const
|
||||
+ // + ty: unsafe extern "rust-intrinsic" fn(u32) {std::intrinsics::count_code_region}
|
||||
+ // + val: Value(Scalar(<ZST>))
|
||||
+ // mir::Constant
|
||||
+ // + span: $DIR/instrument_coverage.rs:18:1: 18:1
|
||||
+ // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(u32) {std::intrinsics::count_code_region}, val: Value(Scalar(<ZST>)) }
|
||||
+ // ty::Const
|
||||
+ // + ty: u32
|
||||
+ // + val: Value(Scalar(0x00000000))
|
||||
+ // mir::Constant
|
||||
+ // + span: $DIR/instrument_coverage.rs:18:1: 18:1
|
||||
+ // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) }
|
||||
+ }
|
||||
+
|
||||
+ bb1 (cleanup): {
|
||||
+ resume; // scope 0 at $DIR/instrument_coverage.rs:18:1: 20:2
|
||||
+ }
|
||||
+
|
||||
+ bb2: {
|
||||
+ StorageDead(_1); // scope 0 at $DIR/instrument_coverage.rs:19:5: 19:9
|
||||
_0 = const true; // scope 0 at $DIR/instrument_coverage.rs:19:5: 19:9
|
||||
// ty::Const
|
||||
// + ty: bool
|
||||
// + val: Value(Scalar(0x01))
|
||||
// mir::Constant
|
||||
// + span: $DIR/instrument_coverage.rs:19:5: 19:9
|
||||
// + literal: Const { ty: bool, val: Value(Scalar(0x01)) }
|
||||
return; // scope 0 at $DIR/instrument_coverage.rs:20:2: 20:2
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,82 @@
|
||||
- // MIR for `main` before InstrumentCoverage
|
||||
+ // MIR for `main` after InstrumentCoverage
|
||||
|
||||
fn main() -> () {
|
||||
let mut _0: (); // return place in scope 0 at $DIR/instrument_coverage.rs:9:11: 9:11
|
||||
let mut _1: (); // in scope 0 at $DIR/instrument_coverage.rs:9:1: 15:2
|
||||
let mut _2: bool; // in scope 0 at $DIR/instrument_coverage.rs:11:12: 11:17
|
||||
let mut _3: !; // in scope 0 at $DIR/instrument_coverage.rs:11:18: 13:10
|
||||
+ let mut _4: (); // in scope 0 at $DIR/instrument_coverage.rs:9:1: 15:2
|
||||
|
||||
bb0: {
|
||||
- falseUnwind -> [real: bb1, cleanup: bb2]; // scope 0 at $DIR/instrument_coverage.rs:10:5: 14:6
|
||||
+ StorageLive(_4); // scope 0 at $DIR/instrument_coverage.rs:9:1: 15:2
|
||||
+ _4 = const std::intrinsics::count_code_region(const 0u32) -> bb7; // scope 0 at $DIR/instrument_coverage.rs:9:1: 15:2
|
||||
+ // ty::Const
|
||||
+ // + ty: unsafe extern "rust-intrinsic" fn(u32) {std::intrinsics::count_code_region}
|
||||
+ // + val: Value(Scalar(<ZST>))
|
||||
+ // mir::Constant
|
||||
+ // + span: $DIR/instrument_coverage.rs:9:1: 9:1
|
||||
+ // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(u32) {std::intrinsics::count_code_region}, val: Value(Scalar(<ZST>)) }
|
||||
+ // ty::Const
|
||||
+ // + ty: u32
|
||||
+ // + val: Value(Scalar(0x00000000))
|
||||
+ // mir::Constant
|
||||
+ // + span: $DIR/instrument_coverage.rs:9:1: 9:1
|
||||
+ // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) }
|
||||
}
|
||||
|
||||
bb1: {
|
||||
StorageLive(_2); // scope 0 at $DIR/instrument_coverage.rs:11:12: 11:17
|
||||
_2 = const bar() -> [return: bb3, unwind: bb2]; // scope 0 at $DIR/instrument_coverage.rs:11:12: 11:17
|
||||
// ty::Const
|
||||
// + ty: fn() -> bool {bar}
|
||||
// + val: Value(Scalar(<ZST>))
|
||||
// mir::Constant
|
||||
// + span: $DIR/instrument_coverage.rs:11:12: 11:15
|
||||
// + literal: Const { ty: fn() -> bool {bar}, val: Value(Scalar(<ZST>)) }
|
||||
}
|
||||
|
||||
bb2 (cleanup): {
|
||||
resume; // scope 0 at $DIR/instrument_coverage.rs:9:1: 15:2
|
||||
}
|
||||
|
||||
bb3: {
|
||||
FakeRead(ForMatchedPlace, _2); // scope 0 at $DIR/instrument_coverage.rs:11:12: 11:17
|
||||
switchInt(_2) -> [false: bb5, otherwise: bb4]; // scope 0 at $DIR/instrument_coverage.rs:11:9: 13:10
|
||||
}
|
||||
|
||||
bb4: {
|
||||
falseEdge -> [real: bb6, imaginary: bb5]; // scope 0 at $DIR/instrument_coverage.rs:11:9: 13:10
|
||||
}
|
||||
|
||||
bb5: {
|
||||
_1 = const (); // scope 0 at $DIR/instrument_coverage.rs:11:9: 13:10
|
||||
// ty::Const
|
||||
// + ty: ()
|
||||
// + val: Value(Scalar(<ZST>))
|
||||
// mir::Constant
|
||||
// + span: $DIR/instrument_coverage.rs:11:9: 13:10
|
||||
// + literal: Const { ty: (), val: Value(Scalar(<ZST>)) }
|
||||
StorageDead(_2); // scope 0 at $DIR/instrument_coverage.rs:14:5: 14:6
|
||||
goto -> bb0; // scope 0 at $DIR/instrument_coverage.rs:10:5: 14:6
|
||||
}
|
||||
|
||||
bb6: {
|
||||
_0 = const (); // scope 0 at $DIR/instrument_coverage.rs:12:13: 12:18
|
||||
// ty::Const
|
||||
// + ty: ()
|
||||
// + val: Value(Scalar(<ZST>))
|
||||
// mir::Constant
|
||||
// + span: $DIR/instrument_coverage.rs:12:13: 12:18
|
||||
// + literal: Const { ty: (), val: Value(Scalar(<ZST>)) }
|
||||
StorageDead(_2); // scope 0 at $DIR/instrument_coverage.rs:14:5: 14:6
|
||||
return; // scope 0 at $DIR/instrument_coverage.rs:15:2: 15:2
|
||||
+ }
|
||||
+
|
||||
+ bb7: {
|
||||
+ StorageDead(_4); // scope 0 at $DIR/instrument_coverage.rs:10:5: 14:6
|
||||
+ falseUnwind -> [real: bb1, cleanup: bb2]; // scope 0 at $DIR/instrument_coverage.rs:10:5: 14:6
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
# needs-sanitizer-support
|
||||
# only-x86_64
|
||||
# needs-sanitizer-address
|
||||
# only-linux
|
||||
|
||||
-include ../tools.mk
|
||||
|
@ -1,5 +1,5 @@
|
||||
# needs-sanitizer-support
|
||||
# only-x86_64
|
||||
# needs-sanitizer-address
|
||||
# only-linux
|
||||
|
||||
-include ../tools.mk
|
||||
|
@ -1,5 +1,5 @@
|
||||
# needs-sanitizer-support
|
||||
# only-x86_64
|
||||
# needs-sanitizer-address
|
||||
# only-linux
|
||||
|
||||
-include ../tools.mk
|
||||
|
@ -1,4 +1,5 @@
|
||||
// needs-sanitizer-support
|
||||
// needs-sanitizer-address
|
||||
// compile-flags: --test -Z sanitizer=address
|
||||
//
|
||||
// #43031: Verify that rustdoc passes `-Z` options to rustc. Use an extern
|
||||
|
@ -8,5 +8,5 @@ pub fn main() {
|
||||
b' '; //~ ERROR byte constant must be escaped
|
||||
b'''; //~ ERROR byte constant must be escaped
|
||||
b'é'; //~ ERROR byte constant must be ASCII
|
||||
b'a //~ ERROR unterminated byte constant
|
||||
b'a //~ ERROR unterminated byte constant [E0763]
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ error: byte constant must be ASCII. Use a \xHH escape for a non-ASCII byte
|
||||
LL | b'é';
|
||||
| ^
|
||||
|
||||
error: unterminated byte constant
|
||||
error[E0763]: unterminated byte constant
|
||||
--> $DIR/byte-literals.rs:11:6
|
||||
|
|
||||
LL | b'a
|
||||
@ -42,3 +42,4 @@ LL | b'a
|
||||
|
||||
error: aborting due to 7 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0763`.
|
||||
|
@ -1,5 +1,5 @@
|
||||
// needs-sanitizer-support
|
||||
// only-x86_64
|
||||
// needs-sanitizer-address
|
||||
//
|
||||
// compile-flags: -Z sanitizer=address -O -g
|
||||
//
|
||||
|
@ -1,5 +1,5 @@
|
||||
// needs-sanitizer-support
|
||||
// only-x86_64
|
||||
// needs-sanitizer-address
|
||||
//
|
||||
// compile-flags: -Z sanitizer=address -O
|
||||
//
|
||||
|
@ -2,8 +2,10 @@
|
||||
// the `#[cfg(sanitize = "option")]` attribute is configured.
|
||||
|
||||
// needs-sanitizer-support
|
||||
// only-linux
|
||||
// only-x86_64
|
||||
// needs-sanitizer-address
|
||||
// needs-sanitizer-leak
|
||||
// needs-sanitizer-memory
|
||||
// needs-sanitizer-thread
|
||||
// check-pass
|
||||
// revisions: address leak memory thread
|
||||
//[address]compile-flags: -Zsanitizer=address --cfg address
|
||||
|
@ -4,7 +4,7 @@
|
||||
// miscompilation which was subsequently detected by AddressSanitizer as UB.
|
||||
//
|
||||
// needs-sanitizer-support
|
||||
// only-x86_64
|
||||
// needs-sanitizer-address
|
||||
//
|
||||
// compile-flags: -Copt-level=0 -Zsanitizer=address
|
||||
// run-pass
|
||||
|
@ -1,5 +1,5 @@
|
||||
// needs-sanitizer-support
|
||||
// only-x86_64
|
||||
// needs-sanitizer-leak
|
||||
//
|
||||
// compile-flags: -Z sanitizer=leak -O
|
||||
//
|
||||
|
@ -1,6 +1,5 @@
|
||||
// needs-sanitizer-support
|
||||
// only-linux
|
||||
// only-x86_64
|
||||
// needs-sanitizer-memory
|
||||
//
|
||||
// compile-flags: -Z sanitizer=memory -Zsanitizer-memory-track-origins -O
|
||||
//
|
||||
|
@ -4,7 +4,7 @@
|
||||
//
|
||||
// min-llvm-version 9.0
|
||||
// needs-sanitizer-support
|
||||
// only-x86_64
|
||||
// needs-sanitizer-address
|
||||
//
|
||||
// no-prefer-dynamic
|
||||
// revisions: opt0 opt1
|
||||
|
@ -11,7 +11,7 @@
|
||||
// would occasionally fail, making test flaky.
|
||||
//
|
||||
// needs-sanitizer-support
|
||||
// only-x86_64
|
||||
// needs-sanitizer-thread
|
||||
//
|
||||
// compile-flags: -Z sanitizer=thread -O
|
||||
//
|
||||
|
@ -1,5 +1,5 @@
|
||||
// needs-sanitizer-support
|
||||
// only-x86_64
|
||||
// needs-sanitizer-address
|
||||
//
|
||||
// compile-flags: -Zsanitizer=address
|
||||
// run-fail
|
||||
|
25
src/test/ui/typeck/issue-68590-reborrow-through-derefmut.rs
Normal file
25
src/test/ui/typeck/issue-68590-reborrow-through-derefmut.rs
Normal file
@ -0,0 +1,25 @@
|
||||
// check-pass
|
||||
|
||||
// rust-lang/rust#68590: confusing diagnostics when reborrowing through DerefMut.
|
||||
|
||||
use std::cell::RefCell;
|
||||
|
||||
struct A;
|
||||
|
||||
struct S<'a> {
|
||||
a: &'a mut A,
|
||||
}
|
||||
|
||||
fn take_a(_: &mut A) {}
|
||||
|
||||
fn test<'a>(s: &RefCell<S<'a>>) {
|
||||
let mut guard = s.borrow_mut();
|
||||
take_a(guard.a);
|
||||
let _s2 = S { a: guard.a };
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let a = &mut A;
|
||||
let s = RefCell::new(S { a });
|
||||
test(&s);
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
// check-pass
|
||||
|
||||
// rust-lang/rust#72225: confusing diagnostics when calling FnMut through DerefMut.
|
||||
|
||||
use std::cell::RefCell;
|
||||
|
||||
struct S {
|
||||
f: Box<dyn FnMut()>
|
||||
}
|
||||
|
||||
fn test(s: &RefCell<S>) {
|
||||
let mut guard = s.borrow_mut();
|
||||
(guard.f)();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let s = RefCell::new(S {
|
||||
f: Box::new(|| ())
|
||||
});
|
||||
test(&s);
|
||||
}
|
@ -43,6 +43,10 @@ impl EarlyProps {
|
||||
let mut props = EarlyProps::default();
|
||||
let rustc_has_profiler_support = env::var_os("RUSTC_PROFILER_SUPPORT").is_some();
|
||||
let rustc_has_sanitizer_support = env::var_os("RUSTC_SANITIZER_SUPPORT").is_some();
|
||||
let has_asan = util::ASAN_SUPPORTED_TARGETS.contains(&&*config.target);
|
||||
let has_lsan = util::LSAN_SUPPORTED_TARGETS.contains(&&*config.target);
|
||||
let has_msan = util::MSAN_SUPPORTED_TARGETS.contains(&&*config.target);
|
||||
let has_tsan = util::TSAN_SUPPORTED_TARGETS.contains(&&*config.target);
|
||||
|
||||
iter_header(testfile, None, rdr, &mut |ln| {
|
||||
// we should check if any only-<platform> exists and if it exists
|
||||
@ -74,7 +78,25 @@ impl EarlyProps {
|
||||
props.ignore = true;
|
||||
}
|
||||
|
||||
if !rustc_has_sanitizer_support && config.parse_needs_sanitizer_support(ln) {
|
||||
if !rustc_has_sanitizer_support
|
||||
&& config.parse_name_directive(ln, "needs-sanitizer-support")
|
||||
{
|
||||
props.ignore = true;
|
||||
}
|
||||
|
||||
if !has_asan && config.parse_name_directive(ln, "needs-sanitizer-address") {
|
||||
props.ignore = true;
|
||||
}
|
||||
|
||||
if !has_lsan && config.parse_name_directive(ln, "needs-sanitizer-leak") {
|
||||
props.ignore = true;
|
||||
}
|
||||
|
||||
if !has_msan && config.parse_name_directive(ln, "needs-sanitizer-memory") {
|
||||
props.ignore = true;
|
||||
}
|
||||
|
||||
if !has_tsan && config.parse_name_directive(ln, "needs-sanitizer-thread") {
|
||||
props.ignore = true;
|
||||
}
|
||||
|
||||
@ -829,10 +851,6 @@ impl Config {
|
||||
self.parse_name_directive(line, "needs-profiler-support")
|
||||
}
|
||||
|
||||
fn parse_needs_sanitizer_support(&self, line: &str) -> bool {
|
||||
self.parse_name_directive(line, "needs-sanitizer-support")
|
||||
}
|
||||
|
||||
/// Parses a name-value directive which contains config-specific information, e.g., `ignore-x86`
|
||||
/// or `normalize-stderr-32bit`.
|
||||
fn parse_cfg_name_directive(&self, line: &str, prefix: &str) -> ParsedNameDirective {
|
||||
|
@ -201,3 +201,22 @@ fn debugger() {
|
||||
config.debugger = Some(Debugger::Lldb);
|
||||
assert!(parse_rs(&config, "// ignore-lldb").ignore);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sanitizers() {
|
||||
let mut config = config();
|
||||
|
||||
// Target that supports all sanitizers:
|
||||
config.target = "x86_64-unknown-linux-gnu".to_owned();
|
||||
assert!(!parse_rs(&config, "// needs-sanitizer-address").ignore);
|
||||
assert!(!parse_rs(&config, "// needs-sanitizer-leak").ignore);
|
||||
assert!(!parse_rs(&config, "// needs-sanitizer-memory").ignore);
|
||||
assert!(!parse_rs(&config, "// needs-sanitizer-thread").ignore);
|
||||
|
||||
// Target that doesn't support sanitizers:
|
||||
config.target = "wasm32-unknown-emscripten".to_owned();
|
||||
assert!(parse_rs(&config, "// needs-sanitizer-address").ignore);
|
||||
assert!(parse_rs(&config, "// needs-sanitizer-leak").ignore);
|
||||
assert!(parse_rs(&config, "// needs-sanitizer-memory").ignore);
|
||||
assert!(parse_rs(&config, "// needs-sanitizer-thread").ignore);
|
||||
}
|
||||
|
@ -82,6 +82,17 @@ const ARCH_TABLE: &'static [(&'static str, &'static str)] = &[
|
||||
("xcore", "xcore"),
|
||||
];
|
||||
|
||||
pub const ASAN_SUPPORTED_TARGETS: &'static [&'static str] =
|
||||
&["aarch64-fuchsia", "x86_64-apple-darwin", "x86_64-fuchsia", "x86_64-unknown-linux-gnu"];
|
||||
|
||||
pub const LSAN_SUPPORTED_TARGETS: &'static [&'static str] =
|
||||
&["x86_64-apple-darwin", "x86_64-unknown-linux-gnu"];
|
||||
|
||||
pub const MSAN_SUPPORTED_TARGETS: &'static [&'static str] = &["x86_64-unknown-linux-gnu"];
|
||||
|
||||
pub const TSAN_SUPPORTED_TARGETS: &'static [&'static str] =
|
||||
&["x86_64-apple-darwin", "x86_64-unknown-linux-gnu"];
|
||||
|
||||
pub fn matches_os(triple: &str, name: &str) -> bool {
|
||||
// For the wasm32 bare target we ignore anything also ignored on emscripten
|
||||
// and then we also recognize `wasm32-bare` as the os for the target
|
||||
|
Loading…
Reference in New Issue
Block a user