Auto merge of #70943 - Centril:rollup-eowm2h3, r=Centril

Rollup of 7 pull requests

Successful merges:

 - #67705 (Use unrolled loop for searching NULL in [u16] on Windows)
 - #70367 (save/restore `pessimistic_yield` when entering bodies)
 - #70822 (Don't lint for self-recursion when the function can diverge)
 - #70868 (rustc_codegen_ssa: Refactor construction of linker arguments)
 - #70896 (Implement Chain with Option fuses)
 - #70916 (Support `#[track_caller]` on functions in `extern "Rust" { ... }`)
 - #70918 (rustc_session: forbid lints override regardless of position)

Failed merges:

r? @ghost
This commit is contained in:
bors 2020-04-09 09:57:17 +00:00
commit 93dc97a853
22 changed files with 899 additions and 568 deletions

View File

@ -170,7 +170,7 @@ The order of these command line arguments is taken into account. The following a
$ rustc lib.rs --crate-type=lib -D unused-variables -A unused-variables
```
You can make use of this behavior by overriding the level of one specific lint out of a group of lints. The following example denies all the lints in the `unused` group, but explicitly allows the `unused-variables` lint in that group:
You can make use of this behavior by overriding the level of one specific lint out of a group of lints. The following example denies all the lints in the `unused` group, but explicitly allows the `unused-variables` lint in that group (forbid still trumps everything regardless of ordering):
```bash
$ rustc lib.rs --crate-type=lib -D unused -A unused-variables

View File

@ -1,8 +1,7 @@
use crate::iter::{DoubleEndedIterator, FusedIterator, Iterator, TrustedLen};
use crate::ops::Try;
use crate::usize;
use super::super::{DoubleEndedIterator, FusedIterator, Iterator, TrustedLen};
/// An iterator that links two iterators together, in a chain.
///
/// This `struct` is created by the [`chain`] method on [`Iterator`]. See its
@ -14,37 +13,34 @@ use super::super::{DoubleEndedIterator, FusedIterator, Iterator, TrustedLen};
#[must_use = "iterators are lazy and do nothing unless consumed"]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Chain<A, B> {
a: A,
b: B,
state: ChainState,
// These are "fused" with `Option` so we don't need separate state to track which part is
// already exhausted, and we may also get niche layout for `None`. We don't use the real `Fuse`
// adapter because its specialization for `FusedIterator` unconditionally descends into the
// iterator, and that could be expensive to keep revisiting stuff like nested chains. It also
// hurts compiler performance to add more iterator layers to `Chain`.
a: Option<A>,
b: Option<B>,
}
impl<A, B> Chain<A, B> {
pub(in super::super) fn new(a: A, b: B) -> Chain<A, B> {
Chain { a, b, state: ChainState::Both }
Chain { a: Some(a), b: Some(b) }
}
}
// The iterator protocol specifies that iteration ends with the return value
// `None` from `.next()` (or `.next_back()`) and it is unspecified what
// further calls return. The chain adaptor must account for this since it uses
// two subiterators.
//
// It uses three states:
//
// - Both: `a` and `b` are remaining
// - Front: `a` remaining
// - Back: `b` remaining
//
// The fourth state (neither iterator is remaining) only occurs after Chain has
// returned None once, so we don't need to store this state.
#[derive(Clone, Debug)]
enum ChainState {
// both front and back iterator are remaining
Both,
// only front is remaining
Front,
// only back is remaining
Back,
/// Fuse the iterator if the expression is `None`.
macro_rules! fuse {
($self:ident . $iter:ident . $($call:tt)+) => {
match $self.$iter {
Some(ref mut iter) => match iter.$($call)+ {
None => {
$self.$iter = None;
None
}
item => item,
},
None => None,
}
};
}
#[stable(feature = "rust1", since = "1.0.0")]
@ -57,88 +53,68 @@ where
#[inline]
fn next(&mut self) -> Option<A::Item> {
match self.state {
ChainState::Both => match self.a.next() {
elt @ Some(..) => elt,
None => {
self.state = ChainState::Back;
self.b.next()
}
},
ChainState::Front => self.a.next(),
ChainState::Back => self.b.next(),
match fuse!(self.a.next()) {
None => fuse!(self.b.next()),
item => item,
}
}
#[inline]
#[rustc_inherit_overflow_checks]
fn count(self) -> usize {
match self.state {
ChainState::Both => self.a.count() + self.b.count(),
ChainState::Front => self.a.count(),
ChainState::Back => self.b.count(),
}
let a_count = match self.a {
Some(a) => a.count(),
None => 0,
};
let b_count = match self.b {
Some(b) => b.count(),
None => 0,
};
a_count + b_count
}
fn try_fold<Acc, F, R>(&mut self, init: Acc, mut f: F) -> R
fn try_fold<Acc, F, R>(&mut self, mut acc: Acc, mut f: F) -> R
where
Self: Sized,
F: FnMut(Acc, Self::Item) -> R,
R: Try<Ok = Acc>,
{
let mut accum = init;
match self.state {
ChainState::Both | ChainState::Front => {
accum = self.a.try_fold(accum, &mut f)?;
if let ChainState::Both = self.state {
self.state = ChainState::Back;
}
}
_ => {}
if let Some(ref mut a) = self.a {
acc = a.try_fold(acc, &mut f)?;
self.a = None;
}
if let ChainState::Back = self.state {
accum = self.b.try_fold(accum, &mut f)?;
if let Some(ref mut b) = self.b {
acc = b.try_fold(acc, f)?;
self.b = None;
}
Try::from_ok(accum)
Try::from_ok(acc)
}
fn fold<Acc, F>(self, init: Acc, mut f: F) -> Acc
fn fold<Acc, F>(self, mut acc: Acc, mut f: F) -> Acc
where
F: FnMut(Acc, Self::Item) -> Acc,
{
let mut accum = init;
match self.state {
ChainState::Both | ChainState::Front => {
accum = self.a.fold(accum, &mut f);
}
_ => {}
if let Some(a) = self.a {
acc = a.fold(acc, &mut f);
}
match self.state {
ChainState::Both | ChainState::Back => {
accum = self.b.fold(accum, &mut f);
}
_ => {}
if let Some(b) = self.b {
acc = b.fold(acc, f);
}
accum
acc
}
#[inline]
fn nth(&mut self, mut n: usize) -> Option<A::Item> {
match self.state {
ChainState::Both | ChainState::Front => {
for x in self.a.by_ref() {
if n == 0 {
return Some(x);
}
n -= 1;
}
if let ChainState::Both = self.state {
self.state = ChainState::Back;
if let Some(ref mut a) = self.a {
while let Some(x) = a.next() {
if n == 0 {
return Some(x);
}
n -= 1;
}
ChainState::Back => {}
self.a = None;
}
if let ChainState::Back = self.state { self.b.nth(n) } else { None }
fuse!(self.b.nth(n))
}
#[inline]
@ -146,39 +122,32 @@ where
where
P: FnMut(&Self::Item) -> bool,
{
match self.state {
ChainState::Both => match self.a.find(&mut predicate) {
None => {
self.state = ChainState::Back;
self.b.find(predicate)
}
v => v,
},
ChainState::Front => self.a.find(predicate),
ChainState::Back => self.b.find(predicate),
match fuse!(self.a.find(&mut predicate)) {
None => fuse!(self.b.find(predicate)),
item => item,
}
}
#[inline]
fn last(self) -> Option<A::Item> {
match self.state {
ChainState::Both => {
// Must exhaust a before b.
let a_last = self.a.last();
let b_last = self.b.last();
b_last.or(a_last)
}
ChainState::Front => self.a.last(),
ChainState::Back => self.b.last(),
}
// Must exhaust a before b.
let a_last = match self.a {
Some(a) => a.last(),
None => None,
};
let b_last = match self.b {
Some(b) => b.last(),
None => None,
};
b_last.or(a_last)
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
match self.state {
ChainState::Both => {
let (a_lower, a_upper) = self.a.size_hint();
let (b_lower, b_upper) = self.b.size_hint();
match self {
Chain { a: Some(a), b: Some(b) } => {
let (a_lower, a_upper) = a.size_hint();
let (b_lower, b_upper) = b.size_hint();
let lower = a_lower.saturating_add(b_lower);
@ -189,8 +158,9 @@ where
(lower, upper)
}
ChainState::Front => self.a.size_hint(),
ChainState::Back => self.b.size_hint(),
Chain { a: Some(a), b: None } => a.size_hint(),
Chain { a: None, b: Some(b) } => b.size_hint(),
Chain { a: None, b: None } => (0, Some(0)),
}
}
}
@ -203,82 +173,71 @@ where
{
#[inline]
fn next_back(&mut self) -> Option<A::Item> {
match self.state {
ChainState::Both => match self.b.next_back() {
elt @ Some(..) => elt,
None => {
self.state = ChainState::Front;
self.a.next_back()
}
},
ChainState::Front => self.a.next_back(),
ChainState::Back => self.b.next_back(),
match fuse!(self.b.next_back()) {
None => fuse!(self.a.next_back()),
item => item,
}
}
#[inline]
fn nth_back(&mut self, mut n: usize) -> Option<A::Item> {
match self.state {
ChainState::Both | ChainState::Back => {
for x in self.b.by_ref().rev() {
if n == 0 {
return Some(x);
}
n -= 1;
}
if let ChainState::Both = self.state {
self.state = ChainState::Front;
if let Some(ref mut b) = self.b {
while let Some(x) = b.next_back() {
if n == 0 {
return Some(x);
}
n -= 1;
}
ChainState::Front => {}
self.b = None;
}
if let ChainState::Front = self.state { self.a.nth_back(n) } else { None }
fuse!(self.a.nth_back(n))
}
fn try_rfold<Acc, F, R>(&mut self, init: Acc, mut f: F) -> R
#[inline]
fn rfind<P>(&mut self, mut predicate: P) -> Option<Self::Item>
where
P: FnMut(&Self::Item) -> bool,
{
match fuse!(self.b.rfind(&mut predicate)) {
None => fuse!(self.a.rfind(predicate)),
item => item,
}
}
fn try_rfold<Acc, F, R>(&mut self, mut acc: Acc, mut f: F) -> R
where
Self: Sized,
F: FnMut(Acc, Self::Item) -> R,
R: Try<Ok = Acc>,
{
let mut accum = init;
match self.state {
ChainState::Both | ChainState::Back => {
accum = self.b.try_rfold(accum, &mut f)?;
if let ChainState::Both = self.state {
self.state = ChainState::Front;
}
}
_ => {}
if let Some(ref mut b) = self.b {
acc = b.try_rfold(acc, &mut f)?;
self.b = None;
}
if let ChainState::Front = self.state {
accum = self.a.try_rfold(accum, &mut f)?;
if let Some(ref mut a) = self.a {
acc = a.try_rfold(acc, f)?;
self.a = None;
}
Try::from_ok(accum)
Try::from_ok(acc)
}
fn rfold<Acc, F>(self, init: Acc, mut f: F) -> Acc
fn rfold<Acc, F>(self, mut acc: Acc, mut f: F) -> Acc
where
F: FnMut(Acc, Self::Item) -> Acc,
{
let mut accum = init;
match self.state {
ChainState::Both | ChainState::Back => {
accum = self.b.rfold(accum, &mut f);
}
_ => {}
if let Some(b) = self.b {
acc = b.rfold(acc, &mut f);
}
match self.state {
ChainState::Both | ChainState::Front => {
accum = self.a.rfold(accum, &mut f);
}
_ => {}
if let Some(a) = self.a {
acc = a.rfold(acc, f);
}
accum
acc
}
}
// Note: *both* must be fused to handle double-ended iterators.
// Now that we "fuse" both sides, we *could* implement this unconditionally,
// but we should be cautious about committing to that in the public API.
#[stable(feature = "fused", since = "1.26.0")]
impl<A, B> FusedIterator for Chain<A, B>
where

View File

@ -154,7 +154,7 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>(
// The third parameter is for env vars, used on windows to set up the
// path for MSVC to find its DLLs, and gcc to find its bundled
// toolchain
pub fn get_linker(sess: &Session, linker: &Path, flavor: LinkerFlavor) -> (PathBuf, Command) {
fn get_linker(sess: &Session, linker: &Path, flavor: LinkerFlavor) -> Command {
let msvc_tool = windows_registry::find_tool(&sess.opts.target_triple.triple(), "link.exe");
// If our linker looks like a batch script on Windows then to execute this
@ -193,6 +193,7 @@ pub fn get_linker(sess: &Session, linker: &Path, flavor: LinkerFlavor) -> (PathB
_ => None,
};
if let Some(ref a) = arch {
// FIXME: Move this to `fn linker_with_args`.
let mut arg = OsString::from("/LIBPATH:");
arg.push(format!("{}\\lib\\{}\\store", root_lib_path.display(), a.to_string()));
cmd.arg(&arg);
@ -232,7 +233,7 @@ pub fn get_linker(sess: &Session, linker: &Path, flavor: LinkerFlavor) -> (PathB
}
cmd.env("PATH", env::join_paths(new_path).unwrap());
(linker.to_path_buf(), cmd)
cmd
}
pub fn each_linked_rlib(
@ -284,11 +285,7 @@ pub fn each_linked_rlib(
/// building an `.rlib` (stomping over one another), or writing an `.rmeta` into a
/// directory being searched for `extern crate` (observing an incomplete file).
/// The returned path is the temporary file containing the complete metadata.
pub fn emit_metadata<'a>(
sess: &'a Session,
metadata: &EncodedMetadata,
tmpdir: &TempDir,
) -> PathBuf {
pub fn emit_metadata(sess: &Session, metadata: &EncodedMetadata, tmpdir: &TempDir) -> PathBuf {
let out_filename = tmpdir.path().join(METADATA_FILENAME);
let result = fs::write(&out_filename, &metadata.raw_data);
@ -487,95 +484,18 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>(
target_cpu: &str,
) {
info!("preparing {:?} to {:?}", crate_type, out_filename);
let (linker, flavor) = linker_and_flavor(sess);
let (linker_path, flavor) = linker_and_flavor(sess);
let mut cmd = linker_with_args::<B>(
&linker_path,
flavor,
sess,
crate_type,
tmpdir,
out_filename,
codegen_results,
target_cpu,
);
let any_dynamic_crate = crate_type == config::CrateType::Dylib
|| codegen_results.crate_info.dependency_formats.iter().any(|(ty, list)| {
*ty == crate_type && list.iter().any(|&linkage| linkage == Linkage::Dynamic)
});
// The invocations of cc share some flags across platforms
let (pname, mut cmd) = get_linker(sess, &linker, flavor);
if let Some(args) = sess.target.target.options.pre_link_args.get(&flavor) {
cmd.args(args);
}
if let Some(args) = sess.target.target.options.pre_link_args_crt.get(&flavor) {
if sess.crt_static(Some(crate_type)) {
cmd.args(args);
}
}
cmd.args(&sess.opts.debugging_opts.pre_link_args);
if sess.target.target.options.is_like_fuchsia {
let prefix = match sess.opts.debugging_opts.sanitizer {
Some(Sanitizer::Address) => "asan/",
_ => "",
};
cmd.arg(format!("--dynamic-linker={}ld.so.1", prefix));
}
let pre_link_objects = if crate_type == config::CrateType::Executable {
&sess.target.target.options.pre_link_objects_exe
} else {
&sess.target.target.options.pre_link_objects_dll
};
for obj in pre_link_objects {
cmd.arg(get_file_path(sess, obj));
}
if crate_type == config::CrateType::Executable && sess.crt_static(Some(crate_type)) {
for obj in &sess.target.target.options.pre_link_objects_exe_crt {
cmd.arg(get_file_path(sess, obj));
}
}
if sess.target.target.options.is_like_emscripten {
cmd.arg("-s");
cmd.arg(if sess.panic_strategy() == PanicStrategy::Abort {
"DISABLE_EXCEPTION_CATCHING=1"
} else {
"DISABLE_EXCEPTION_CATCHING=0"
});
}
{
let mut linker = codegen_results.linker_info.to_linker(cmd, &sess, flavor, target_cpu);
link_sanitizer_runtime(sess, crate_type, &mut *linker);
link_args::<B>(
&mut *linker,
flavor,
sess,
crate_type,
tmpdir,
out_filename,
codegen_results,
);
cmd = linker.finalize();
}
if let Some(args) = sess.target.target.options.late_link_args.get(&flavor) {
cmd.args(args);
}
if any_dynamic_crate {
if let Some(args) = sess.target.target.options.late_link_args_dynamic.get(&flavor) {
cmd.args(args);
}
} else {
if let Some(args) = sess.target.target.options.late_link_args_static.get(&flavor) {
cmd.args(args);
}
}
for obj in &sess.target.target.options.post_link_objects {
cmd.arg(get_file_path(sess, obj));
}
if sess.crt_static(Some(crate_type)) {
for obj in &sess.target.target.options.post_link_objects_crt {
cmd.arg(get_file_path(sess, obj));
}
}
if let Some(args) = sess.target.target.options.post_link_args.get(&flavor) {
cmd.args(args);
}
for &(ref k, ref v) in &sess.target.target.options.link_env {
cmd.env(k, v);
}
@ -597,7 +517,7 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>(
let mut i = 0;
loop {
i += 1;
prog = sess.time("run_linker", || exec_linker(sess, &mut cmd, out_filename, tmpdir));
prog = sess.time("run_linker", || exec_linker(sess, &cmd, out_filename, tmpdir));
let output = match prog {
Ok(ref output) => output,
Err(_) => break,
@ -698,7 +618,7 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>(
output.extend_from_slice(&prog.stdout);
sess.struct_err(&format!(
"linking with `{}` failed: {}",
pname.display(),
linker_path.display(),
prog.status
))
.note(&format!("{:?}", &cmd))
@ -714,9 +634,12 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>(
let mut linker_error = {
if linker_not_found {
sess.struct_err(&format!("linker `{}` not found", pname.display()))
sess.struct_err(&format!("linker `{}` not found", linker_path.display()))
} else {
sess.struct_err(&format!("could not exec the linker `{}`", pname.display()))
sess.struct_err(&format!(
"could not exec the linker `{}`",
linker_path.display()
))
}
};
@ -787,7 +710,7 @@ fn link_sanitizer_runtime(sess: &Session, crate_type: config::CrateType, linker:
// PR #41352 for details).
let libname = format!("rustc{}_rt.{}", channel, name);
let rpath = default_tlib.to_str().expect("non-utf8 component in path");
linker.args(&["-Wl,-rpath".into(), "-Xlinker".into(), rpath.into()]);
linker.args(&["-Wl,-rpath", "-Xlinker", rpath]);
linker.link_dylib(Symbol::intern(&libname));
}
"x86_64-unknown-linux-gnu" | "x86_64-fuchsia" | "aarch64-fuchsia" => {
@ -817,7 +740,7 @@ pub fn ignored_for_lto(sess: &Session, info: &CrateInfo, cnum: CrateNum) -> bool
&& (info.compiler_builtins == Some(cnum) || info.is_no_builtins.contains(&cnum))
}
pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) {
fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) {
fn infer_from(
sess: &Session,
linker: Option<PathBuf>,
@ -905,7 +828,7 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) {
/// Returns a boolean indicating whether we should preserve the object files on
/// the filesystem for their debug information. This is often useful with
/// split-dwarf like schemes.
pub fn preserve_objects_for_their_debuginfo(sess: &Session) -> bool {
fn preserve_objects_for_their_debuginfo(sess: &Session) -> bool {
// If the objects don't have debuginfo there's nothing to preserve.
if sess.opts.debuginfo == config::DebugInfo::None {
return false;
@ -959,7 +882,7 @@ enum RlibFlavor {
StaticlibBase,
}
pub fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLibrary]) {
fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLibrary]) {
let lib_args: Vec<_> = all_native_libs
.iter()
.filter(|l| relevant_lib(sess, l))
@ -1061,7 +984,7 @@ fn get_crt_libs_path(sess: &Session) -> Option<PathBuf> {
}
}
pub fn get_file_path(sess: &Session, name: &str) -> PathBuf {
fn get_object_file_path(sess: &Session, name: &str) -> PathBuf {
// prefer system {,dll}crt2.o libs, see get_crt_libs_path comment for more details
if sess.target.target.llvm_target.contains("windows-gnu") {
if let Some(compiler_libs_path) = get_crt_libs_path(sess) {
@ -1085,9 +1008,9 @@ pub fn get_file_path(sess: &Session, name: &str) -> PathBuf {
PathBuf::from(name)
}
pub fn exec_linker(
fn exec_linker(
sess: &Session,
cmd: &mut Command,
cmd: &Command,
out_filename: &Path,
tmpdir: &Path,
) -> io::Result<Output> {
@ -1233,134 +1156,142 @@ pub fn exec_linker(
}
}
fn link_args<'a, B: ArchiveBuilder<'a>>(
cmd: &mut dyn Linker,
flavor: LinkerFlavor,
sess: &'a Session,
crate_type: config::CrateType,
tmpdir: &Path,
out_filename: &Path,
codegen_results: &CodegenResults,
) {
// Linker plugins should be specified early in the list of arguments
cmd.linker_plugin_lto();
// The default library location, we need this to find the runtime.
// The location of crates will be determined as needed.
let lib_path = sess.target_filesearch(PathKind::All).get_lib_path();
// target descriptor
let t = &sess.target.target;
// prefer system mingw-w64 libs, see get_crt_libs_path comment for more details
if cfg!(windows) && sess.target.target.llvm_target.contains("windows-gnu") {
if let Some(compiler_libs_path) = get_crt_libs_path(sess) {
cmd.include_path(&compiler_libs_path);
}
/// Add begin object files defined by the target spec.
fn add_pre_link_objects(cmd: &mut dyn Linker, sess: &Session, crate_type: config::CrateType) {
let pre_link_objects = if crate_type == config::CrateType::Executable {
&sess.target.target.options.pre_link_objects_exe
} else {
&sess.target.target.options.pre_link_objects_dll
};
for obj in pre_link_objects {
cmd.add_object(&get_object_file_path(sess, obj));
}
cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path));
if crate_type == config::CrateType::Executable && sess.crt_static(Some(crate_type)) {
for obj in &sess.target.target.options.pre_link_objects_exe_crt {
cmd.add_object(&get_object_file_path(sess, obj));
}
}
}
/// Add end object files defined by the target spec.
fn add_post_link_objects(cmd: &mut dyn Linker, sess: &Session, crate_type: config::CrateType) {
for obj in &sess.target.target.options.post_link_objects {
cmd.add_object(&get_object_file_path(sess, obj));
}
if sess.crt_static(Some(crate_type)) {
for obj in &sess.target.target.options.post_link_objects_crt {
cmd.add_object(&get_object_file_path(sess, obj));
}
}
}
/// Add arbitrary "pre-link" args defined by the target spec or from command line.
/// FIXME: Determine where exactly these args need to be inserted.
fn add_pre_link_args(
cmd: &mut dyn Linker,
sess: &Session,
flavor: LinkerFlavor,
crate_type: config::CrateType,
) {
if let Some(args) = sess.target.target.options.pre_link_args.get(&flavor) {
cmd.args(args);
}
if let Some(args) = sess.target.target.options.pre_link_args_crt.get(&flavor) {
if sess.crt_static(Some(crate_type)) {
cmd.args(args);
}
}
cmd.args(&sess.opts.debugging_opts.pre_link_args);
}
/// Add arbitrary "user defined" args defined from command line and by `#[link_args]` attributes.
/// FIXME: Determine where exactly these args need to be inserted.
fn add_user_defined_link_args(
cmd: &mut dyn Linker,
sess: &Session,
codegen_results: &CodegenResults,
) {
cmd.args(&sess.opts.cg.link_args);
cmd.args(&*codegen_results.crate_info.link_args);
}
/// Add arbitrary "late link" args defined by the target spec.
/// FIXME: Determine where exactly these args need to be inserted.
fn add_late_link_args(
cmd: &mut dyn Linker,
sess: &Session,
flavor: LinkerFlavor,
crate_type: config::CrateType,
codegen_results: &CodegenResults,
) {
if let Some(args) = sess.target.target.options.late_link_args.get(&flavor) {
cmd.args(args);
}
let any_dynamic_crate = crate_type == config::CrateType::Dylib
|| codegen_results.crate_info.dependency_formats.iter().any(|(ty, list)| {
*ty == crate_type && list.iter().any(|&linkage| linkage == Linkage::Dynamic)
});
if any_dynamic_crate {
if let Some(args) = sess.target.target.options.late_link_args_dynamic.get(&flavor) {
cmd.args(args);
}
} else {
if let Some(args) = sess.target.target.options.late_link_args_static.get(&flavor) {
cmd.args(args);
}
}
}
/// Add arbitrary "post-link" args defined by the target spec.
/// FIXME: Determine where exactly these args need to be inserted.
fn add_post_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
if let Some(args) = sess.target.target.options.post_link_args.get(&flavor) {
cmd.args(args);
}
}
/// Add object files containing code from the current crate.
fn add_local_crate_regular_objects(cmd: &mut dyn Linker, codegen_results: &CodegenResults) {
for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) {
cmd.add_object(obj);
}
cmd.output_filename(out_filename);
}
if crate_type == config::CrateType::Executable && sess.target.target.options.is_like_windows {
if let Some(ref s) = codegen_results.windows_subsystem {
cmd.subsystem(s);
}
/// Add object files for allocator code linked once for the whole crate tree.
fn add_local_crate_allocator_objects(cmd: &mut dyn Linker, codegen_results: &CodegenResults) {
if let Some(obj) = codegen_results.allocator_module.as_ref().and_then(|m| m.object.as_ref()) {
cmd.add_object(obj);
}
}
// If we're building something like a dynamic library then some platforms
// need to make sure that all symbols are exported correctly from the
// dynamic library.
cmd.export_symbols(tmpdir, crate_type);
/// Add object files containing metadata for the current crate.
fn add_local_crate_metadata_objects(
cmd: &mut dyn Linker,
crate_type: config::CrateType,
codegen_results: &CodegenResults,
) {
// When linking a dynamic library, we put the metadata into a section of the
// executable. This metadata is in a separate object file from the main
// object file, so we link that in here.
if crate_type == config::CrateType::Dylib || crate_type == config::CrateType::ProcMacro {
let obj = codegen_results.metadata_module.as_ref().and_then(|m| m.object.as_ref());
if let Some(obj) = obj {
if let Some(obj) = codegen_results.metadata_module.as_ref().and_then(|m| m.object.as_ref())
{
cmd.add_object(obj);
}
}
}
let obj = codegen_results.allocator_module.as_ref().and_then(|m| m.object.as_ref());
if let Some(obj) = obj {
cmd.add_object(obj);
}
// Try to strip as much out of the generated object by removing unused
// sections if possible. See more comments in linker.rs
if !sess.opts.cg.link_dead_code {
let keep_metadata = crate_type == config::CrateType::Dylib;
cmd.gc_sections(keep_metadata);
}
let attr_link_args = codegen_results.crate_info.link_args.iter();
let user_link_args: Vec<_> =
sess.opts.cg.link_args.iter().chain(attr_link_args).cloned().collect();
if crate_type == config::CrateType::Executable {
let mut position_independent_executable = false;
if t.options.position_independent_executables {
if is_pic(sess)
&& !sess.crt_static(Some(crate_type))
&& !user_link_args.iter().any(|x| x == "-static")
{
position_independent_executable = true;
}
}
if position_independent_executable {
cmd.position_independent_executable();
} else {
// recent versions of gcc can be configured to generate position
// independent executables by default. We have to pass -no-pie to
// explicitly turn that off. Not applicable to ld.
if sess.target.target.options.linker_is_gnu && flavor != LinkerFlavor::Ld {
cmd.no_position_independent_executable();
}
}
}
let relro_level = match sess.opts.debugging_opts.relro_level {
Some(level) => level,
None => t.options.relro_level,
};
match relro_level {
RelroLevel::Full => {
cmd.full_relro();
}
RelroLevel::Partial => {
cmd.partial_relro();
}
RelroLevel::Off => {
cmd.no_relro();
}
RelroLevel::None => {}
}
// Pass optimization flags down to the linker.
cmd.optimize();
// Pass debuginfo flags down to the linker.
cmd.debuginfo();
// We want to, by default, prevent the compiler from accidentally leaking in
// any system libraries, so we may explicitly ask linkers to not link to any
// libraries by default. Note that this does not happen for windows because
// windows pulls in some large number of libraries and I couldn't quite
// figure out which subset we wanted.
//
// This is all naturally configurable via the standard methods as well.
if !sess.opts.cg.default_linker_libraries.unwrap_or(false) && t.options.no_default_libraries {
cmd.no_default_libraries();
}
/// Link native libraries corresponding to the current crate and all libraries corresponding to
/// all its dependency crates.
/// FIXME: Consider combining this with the functions above adding object files for the local crate.
fn link_local_crate_native_libs_and_dependent_crate_libs<'a, B: ArchiveBuilder<'a>>(
cmd: &mut dyn Linker,
sess: &'a Session,
crate_type: config::CrateType,
codegen_results: &CodegenResults,
tmpdir: &Path,
) {
// Take careful note of the ordering of the arguments we pass to the linker
// here. Linkers will assume that things on the left depend on things to the
// right. Things on the right cannot depend on things on the left. This is
@ -1398,22 +1329,73 @@ fn link_args<'a, B: ArchiveBuilder<'a>>(
if sess.opts.debugging_opts.link_native_libraries.unwrap_or(true) {
add_upstream_native_libraries(cmd, sess, codegen_results, crate_type);
}
// Tell the linker what we're doing.
}
/// Add sysroot and other globally set directories to the directory search list.
fn add_library_search_dirs(cmd: &mut dyn Linker, sess: &Session) {
// Prefer system mingw-w64 libs, see get_crt_libs_path comment for more details.
if cfg!(windows) && sess.target.target.llvm_target.contains("windows-gnu") {
if let Some(compiler_libs_path) = get_crt_libs_path(sess) {
cmd.include_path(&compiler_libs_path);
}
}
// The default library location, we need this to find the runtime.
// The location of crates will be determined as needed.
let lib_path = sess.target_filesearch(PathKind::All).get_lib_path();
cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path));
}
/// Add options requesting executables to be position-independent or not position-independent.
fn add_position_independent_executable_args(
cmd: &mut dyn Linker,
sess: &Session,
flavor: LinkerFlavor,
crate_type: config::CrateType,
codegen_results: &CodegenResults,
) {
if crate_type != config::CrateType::Executable {
cmd.build_dylib(out_filename);
}
if crate_type == config::CrateType::Executable && sess.crt_static(Some(crate_type)) {
cmd.build_static_executable();
return;
}
if sess.opts.cg.profile_generate.enabled() {
cmd.pgo_gen();
if sess.target.target.options.position_independent_executables {
let attr_link_args = &*codegen_results.crate_info.link_args;
let mut user_defined_link_args = sess.opts.cg.link_args.iter().chain(attr_link_args);
if is_pic(sess)
&& !sess.crt_static(Some(crate_type))
&& !user_defined_link_args.any(|x| x == "-static")
{
cmd.position_independent_executable();
return;
}
}
if sess.opts.debugging_opts.control_flow_guard != CFGuard::Disabled {
cmd.control_flow_guard();
// Recent versions of gcc can be configured to generate position
// independent executables by default. We have to pass -no-pie to
// explicitly turn that off. Not applicable to ld.
if sess.target.target.options.linker_is_gnu && flavor != LinkerFlavor::Ld {
cmd.no_position_independent_executable();
}
}
/// Add options making relocation sections in the produced ELF files read-only
/// and suppressing lazy binding.
fn add_relro_args(cmd: &mut dyn Linker, sess: &Session) {
match sess.opts.debugging_opts.relro_level.unwrap_or(sess.target.target.options.relro_level) {
RelroLevel::Full => cmd.full_relro(),
RelroLevel::Partial => cmd.partial_relro(),
RelroLevel::Off => cmd.no_relro(),
RelroLevel::None => {}
}
}
/// Add library search paths used at runtime by dynamic linkers.
fn add_rpath_args(
cmd: &mut dyn Linker,
sess: &Session,
codegen_results: &CodegenResults,
out_filename: &Path,
) {
// FIXME (#2397): At some point we want to rpath our guesses as to
// where extern libraries might live, based on the
// addl_lib_search_paths
@ -1437,10 +1419,177 @@ fn link_args<'a, B: ArchiveBuilder<'a>>(
};
cmd.args(&rpath::get_rpath_flags(&mut rpath_config));
}
}
// Finally add all the linker arguments provided on the command line along
// with any #[link_args] attributes found inside the crate
cmd.args(&user_link_args);
/// Produce the linker command line containing linker path and arguments.
/// `NO-OPT-OUT` marks the arguments that cannot be removed from the command line
/// by the user without creating a custom target specification.
/// `OBJECT-FILES` specify whether the arguments can add object files.
/// `CUSTOMIZATION-POINT` means that arbitrary arguments defined by the user
/// or by the target spec can be inserted here.
/// `AUDIT-ORDER` - need to figure out whether the option is order-dependent or not.
fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
path: &Path,
flavor: LinkerFlavor,
sess: &'a Session,
crate_type: config::CrateType,
tmpdir: &Path,
out_filename: &Path,
codegen_results: &CodegenResults,
target_cpu: &str,
) -> Command {
let base_cmd = get_linker(sess, path, flavor);
// FIXME: Move `/LIBPATH` addition for uwp targets from the linker construction
// to the linker args construction.
assert!(base_cmd.get_args().is_empty() || sess.target.target.target_vendor == "uwp");
let cmd = &mut *codegen_results.linker_info.to_linker(base_cmd, &sess, flavor, target_cpu);
// NO-OPT-OUT, OBJECT-FILES-MAYBE, CUSTOMIZATION-POINT
add_pre_link_args(cmd, sess, flavor, crate_type);
// NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER
if sess.target.target.options.is_like_fuchsia {
let prefix = match sess.opts.debugging_opts.sanitizer {
Some(Sanitizer::Address) => "asan/",
_ => "",
};
cmd.arg(format!("--dynamic-linker={}ld.so.1", prefix));
}
// NO-OPT-OUT, OBJECT-FILES-YES
add_pre_link_objects(cmd, sess, crate_type);
// NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER
if sess.target.target.options.is_like_emscripten {
cmd.arg("-s");
cmd.arg(if sess.panic_strategy() == PanicStrategy::Abort {
"DISABLE_EXCEPTION_CATCHING=1"
} else {
"DISABLE_EXCEPTION_CATCHING=0"
});
}
// OBJECT-FILES-YES, AUDIT-ORDER
link_sanitizer_runtime(sess, crate_type, cmd);
// OBJECT-FILES-NO, AUDIT-ORDER
// Linker plugins should be specified early in the list of arguments
// FIXME: How "early" exactly?
cmd.linker_plugin_lto();
// NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER
// FIXME: Order-dependent, at least relatively to other args adding searh directories.
add_library_search_dirs(cmd, sess);
// OBJECT-FILES-YES
add_local_crate_regular_objects(cmd, codegen_results);
// NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER
cmd.output_filename(out_filename);
// OBJECT-FILES-NO, AUDIT-ORDER
if crate_type == config::CrateType::Executable && sess.target.target.options.is_like_windows {
if let Some(ref s) = codegen_results.windows_subsystem {
cmd.subsystem(s);
}
}
// NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER
// If we're building something like a dynamic library then some platforms
// need to make sure that all symbols are exported correctly from the
// dynamic library.
cmd.export_symbols(tmpdir, crate_type);
// OBJECT-FILES-YES
add_local_crate_metadata_objects(cmd, crate_type, codegen_results);
// OBJECT-FILES-YES
add_local_crate_allocator_objects(cmd, codegen_results);
// OBJECT-FILES-NO, AUDIT-ORDER
// FIXME: Order dependent, applies to the following objects. Where should it be placed?
// Try to strip as much out of the generated object by removing unused
// sections if possible. See more comments in linker.rs
if !sess.opts.cg.link_dead_code {
let keep_metadata = crate_type == config::CrateType::Dylib;
cmd.gc_sections(keep_metadata);
}
// NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER
add_position_independent_executable_args(cmd, sess, flavor, crate_type, codegen_results);
// OBJECT-FILES-NO, AUDIT-ORDER
add_relro_args(cmd, sess);
// OBJECT-FILES-NO, AUDIT-ORDER
// Pass optimization flags down to the linker.
cmd.optimize();
// OBJECT-FILES-NO, AUDIT-ORDER
// Pass debuginfo flags down to the linker.
cmd.debuginfo();
// OBJECT-FILES-NO, AUDIT-ORDER
// We want to, by default, prevent the compiler from accidentally leaking in
// any system libraries, so we may explicitly ask linkers to not link to any
// libraries by default. Note that this does not happen for windows because
// windows pulls in some large number of libraries and I couldn't quite
// figure out which subset we wanted.
//
// This is all naturally configurable via the standard methods as well.
if !sess.opts.cg.default_linker_libraries.unwrap_or(false)
&& sess.target.target.options.no_default_libraries
{
cmd.no_default_libraries();
}
// OBJECT-FILES-YES
link_local_crate_native_libs_and_dependent_crate_libs::<B>(
cmd,
sess,
crate_type,
codegen_results,
tmpdir,
);
// NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER
// Tell the linker what we're doing.
if crate_type != config::CrateType::Executable {
cmd.build_dylib(out_filename);
}
if crate_type == config::CrateType::Executable && sess.crt_static(Some(crate_type)) {
cmd.build_static_executable();
}
// OBJECT-FILES-NO, AUDIT-ORDER
if sess.opts.cg.profile_generate.enabled() {
cmd.pgo_gen();
}
// OBJECT-FILES-NO, AUDIT-ORDER
if sess.opts.debugging_opts.control_flow_guard != CFGuard::Disabled {
cmd.control_flow_guard();
}
// OBJECT-FILES-NO, AUDIT-ORDER
add_rpath_args(cmd, sess, codegen_results, out_filename);
// OBJECT-FILES-MAYBE, CUSTOMIZATION-POINT
add_user_defined_link_args(cmd, sess, codegen_results);
// NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER
cmd.finalize();
// NO-OPT-OUT, OBJECT-FILES-MAYBE, CUSTOMIZATION-POINT
add_late_link_args(cmd, sess, flavor, crate_type, codegen_results);
// NO-OPT-OUT, OBJECT-FILES-YES
add_post_link_objects(cmd, sess, crate_type);
// NO-OPT-OUT, OBJECT-FILES-MAYBE, CUSTOMIZATION-POINT
add_post_link_args(cmd, sess, flavor);
cmd.take_cmd()
}
// # Native library linking
@ -1454,7 +1603,7 @@ fn link_args<'a, B: ArchiveBuilder<'a>>(
// Also note that the native libraries linked here are only the ones located
// in the current crate. Upstream crates with native library dependencies
// may have their native library pulled in above.
pub fn add_local_native_libraries(
fn add_local_native_libraries(
cmd: &mut dyn Linker,
sess: &Session,
codegen_results: &CodegenResults,
@ -1784,7 +1933,7 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>(
// generic function calls a native function, then the generic function must
// be instantiated in the target crate, meaning that the native symbol must
// also be resolved in the target crate.
pub fn add_upstream_native_libraries(
fn add_upstream_native_libraries(
cmd: &mut dyn Linker,
sess: &Session,
codegen_results: &CodegenResults,
@ -1841,14 +1990,14 @@ pub fn add_upstream_native_libraries(
}
}
pub fn relevant_lib(sess: &Session, lib: &NativeLibrary) -> bool {
fn relevant_lib(sess: &Session, lib: &NativeLibrary) -> bool {
match lib.cfg {
Some(ref cfg) => rustc_attr::cfg_matches(cfg, &sess.parse_sess, None),
None => true,
}
}
pub fn are_upstream_rust_objects_already_included(sess: &Session) -> bool {
fn are_upstream_rust_objects_already_included(sess: &Session) -> bool {
match sess.lto() {
config::Lto::Fat => true,
config::Lto::Thin => {

View File

@ -6,6 +6,7 @@ use std::ffi::{OsStr, OsString};
use std::fs::{self, File};
use std::io::prelude::*;
use std::io::{self, BufWriter};
use std::mem;
use std::path::{Path, PathBuf};
use rustc_data_structures::fx::FxHashMap;
@ -87,6 +88,7 @@ impl LinkerInfo {
/// used to dispatch on whether a GNU-like linker (generally `ld.exe`) or an
/// MSVC linker (e.g., `link.exe`) is being used.
pub trait Linker {
fn cmd(&mut self) -> &mut Command;
fn link_dylib(&mut self, lib: Symbol);
fn link_rust_dylib(&mut self, lib: Symbol, path: &Path);
fn link_framework(&mut self, framework: Symbol);
@ -111,14 +113,26 @@ pub trait Linker {
fn no_default_libraries(&mut self);
fn build_dylib(&mut self, out_filename: &Path);
fn build_static_executable(&mut self);
fn args(&mut self, args: &[String]);
fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType);
fn subsystem(&mut self, subsystem: &str);
fn group_start(&mut self);
fn group_end(&mut self);
fn linker_plugin_lto(&mut self);
// Should have been finalize(self), but we don't support self-by-value on trait objects (yet?).
fn finalize(&mut self) -> Command;
fn finalize(&mut self);
}
impl dyn Linker + '_ {
pub fn arg(&mut self, arg: impl AsRef<OsStr>) {
self.cmd().arg(arg);
}
pub fn args(&mut self, args: impl IntoIterator<Item: AsRef<OsStr>>) {
self.cmd().args(args);
}
pub fn take_cmd(&mut self) -> Command {
mem::replace(self.cmd(), Command::new(""))
}
}
pub struct GccLinker<'a> {
@ -208,6 +222,9 @@ impl<'a> GccLinker<'a> {
}
impl<'a> Linker for GccLinker<'a> {
fn cmd(&mut self) -> &mut Command {
&mut self.cmd
}
fn link_dylib(&mut self, lib: Symbol) {
self.hint_dynamic();
self.cmd.arg(format!("-l{}", lib));
@ -251,9 +268,6 @@ impl<'a> Linker for GccLinker<'a> {
fn build_static_executable(&mut self) {
self.cmd.arg("-static");
}
fn args(&mut self, args: &[String]) {
self.cmd.args(args);
}
fn link_rust_dylib(&mut self, lib: Symbol, _path: &Path) {
self.hint_dynamic();
@ -505,10 +519,8 @@ impl<'a> Linker for GccLinker<'a> {
self.linker_arg(&subsystem);
}
fn finalize(&mut self) -> Command {
fn finalize(&mut self) {
self.hint_dynamic(); // Reset to default before returning the composed command line.
::std::mem::replace(&mut self.cmd, Command::new(""))
}
fn group_start(&mut self) {
@ -545,15 +557,15 @@ pub struct MsvcLinker<'a> {
}
impl<'a> Linker for MsvcLinker<'a> {
fn cmd(&mut self) -> &mut Command {
&mut self.cmd
}
fn link_rlib(&mut self, lib: &Path) {
self.cmd.arg(lib);
}
fn add_object(&mut self, path: &Path) {
self.cmd.arg(path);
}
fn args(&mut self, args: &[String]) {
self.cmd.args(args);
}
fn build_dylib(&mut self, out_filename: &Path) {
self.cmd.arg("/DLL");
@ -758,9 +770,7 @@ impl<'a> Linker for MsvcLinker<'a> {
}
}
fn finalize(&mut self) -> Command {
::std::mem::replace(&mut self.cmd, Command::new(""))
}
fn finalize(&mut self) {}
// MSVC doesn't need group indicators
fn group_start(&mut self) {}
@ -778,6 +788,9 @@ pub struct EmLinker<'a> {
}
impl<'a> Linker for EmLinker<'a> {
fn cmd(&mut self) -> &mut Command {
&mut self.cmd
}
fn include_path(&mut self, path: &Path) {
self.cmd.arg("-L").arg(path);
}
@ -837,10 +850,6 @@ impl<'a> Linker for EmLinker<'a> {
// noop
}
fn args(&mut self, args: &[String]) {
self.cmd.args(args);
}
fn framework_path(&mut self, _path: &Path) {
bug!("frameworks are not supported on Emscripten")
}
@ -928,9 +937,7 @@ impl<'a> Linker for EmLinker<'a> {
// noop
}
fn finalize(&mut self) -> Command {
::std::mem::replace(&mut self.cmd, Command::new(""))
}
fn finalize(&mut self) {}
// Appears not necessary on Emscripten
fn group_start(&mut self) {}
@ -992,6 +999,10 @@ impl<'a> WasmLd<'a> {
}
impl<'a> Linker for WasmLd<'a> {
fn cmd(&mut self) -> &mut Command {
&mut self.cmd
}
fn link_dylib(&mut self, lib: Symbol) {
self.cmd.arg("-l").sym_arg(lib);
}
@ -1030,10 +1041,6 @@ impl<'a> Linker for WasmLd<'a> {
fn build_static_executable(&mut self) {}
fn args(&mut self, args: &[String]) {
self.cmd.args(args);
}
fn link_rust_dylib(&mut self, lib: Symbol, _path: &Path) {
self.cmd.arg("-l").sym_arg(lib);
}
@ -1098,9 +1105,7 @@ impl<'a> Linker for WasmLd<'a> {
fn no_position_independent_executable(&mut self) {}
fn finalize(&mut self) -> Command {
::std::mem::replace(&mut self.cmd, Command::new(""))
}
fn finalize(&mut self) {}
// Not needed for now with LLD
fn group_start(&mut self) {}
@ -1162,6 +1167,10 @@ pub struct PtxLinker<'a> {
}
impl<'a> Linker for PtxLinker<'a> {
fn cmd(&mut self) -> &mut Command {
&mut self.cmd
}
fn link_rlib(&mut self, path: &Path) {
self.cmd.arg("--rlib").arg(path);
}
@ -1182,10 +1191,6 @@ impl<'a> Linker for PtxLinker<'a> {
self.cmd.arg("--bitcode").arg(path);
}
fn args(&mut self, args: &[String]) {
self.cmd.args(args);
}
fn optimize(&mut self) {
match self.sess.lto() {
Lto::Thin | Lto::Fat | Lto::ThinLocal => {
@ -1200,14 +1205,12 @@ impl<'a> Linker for PtxLinker<'a> {
self.cmd.arg("-o").arg(path);
}
fn finalize(&mut self) -> Command {
fn finalize(&mut self) {
// Provide the linker with fallback to internal `target-cpu`.
self.cmd.arg("--fallback-arch").arg(match self.sess.opts.cg.target_cpu {
Some(ref s) => s,
None => &self.sess.target.target.options.cpu,
});
::std::mem::replace(&mut self.cmd, Command::new(""))
}
fn link_dylib(&mut self, _lib: Symbol) {

View File

@ -416,7 +416,6 @@ E0734: include_str!("./error_codes/E0734.md"),
E0735: include_str!("./error_codes/E0735.md"),
E0736: include_str!("./error_codes/E0736.md"),
E0737: include_str!("./error_codes/E0737.md"),
E0738: include_str!("./error_codes/E0738.md"),
E0739: include_str!("./error_codes/E0739.md"),
E0740: include_str!("./error_codes/E0740.md"),
E0741: include_str!("./error_codes/E0741.md"),
@ -614,4 +613,5 @@ E0751: include_str!("./error_codes/E0751.md"),
E0722, // Malformed `#[optimize]` attribute
E0724, // `#[ffi_returns_twice]` is only allowed in foreign functions
E0726, // non-explicit (not `'_`) elided lifetime in unsupported position
// E0738, // Removed; errored on `#[track_caller] fn`s in `extern "Rust" { ... }`.
}

View File

@ -1,11 +0,0 @@
`#[track_caller]` cannot be used to annotate foreign functions.
Erroneous example:
```compile_fail,E0738
#![feature(track_caller)]
extern "Rust" {
#[track_caller]
fn bar();
}
```

View File

@ -178,11 +178,11 @@ fn mir_build(tcx: TyCtxt<'_>, def_id: DefId) -> BodyAndCache<'_> {
build::construct_const(cx, body_id, return_ty, return_ty_span)
};
lints::check(tcx, &body, def_id);
let mut body = BodyAndCache::new(body);
body.ensure_predecessors();
lints::check(tcx, &body.unwrap_read_only(), def_id);
// The borrow checker will replace all the regions here with its own
// inference variables. There's no point having non-erased regions here.
// The exception is `body.user_type_annotations`, which is used unmodified

View File

@ -1,138 +1,175 @@
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::FnKind;
use rustc_index::bit_set::BitSet;
use rustc_index::vec::IndexVec;
use rustc_middle::hir::map::blocks::FnLikeNode;
use rustc_middle::mir::{self, Body, TerminatorKind};
use rustc_middle::mir::{BasicBlock, Body, ReadOnlyBodyAndCache, TerminatorKind, START_BLOCK};
use rustc_middle::ty::subst::InternalSubsts;
use rustc_middle::ty::{self, AssocItem, AssocItemContainer, Instance, TyCtxt};
use rustc_session::lint::builtin::UNCONDITIONAL_RECURSION;
use std::collections::VecDeque;
crate fn check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId) {
crate fn check<'tcx>(tcx: TyCtxt<'tcx>, body: &ReadOnlyBodyAndCache<'_, 'tcx>, def_id: DefId) {
let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
if let Some(fn_like_node) = FnLikeNode::from_node(tcx.hir().get(hir_id)) {
check_fn_for_unconditional_recursion(tcx, fn_like_node.kind(), body, def_id);
if let FnKind::Closure(_) = fn_like_node.kind() {
// closures can't recur, so they don't matter.
return;
}
check_fn_for_unconditional_recursion(tcx, body, def_id);
}
}
fn check_fn_for_unconditional_recursion<'tcx>(
tcx: TyCtxt<'tcx>,
fn_kind: FnKind<'_>,
body: &Body<'tcx>,
body: &ReadOnlyBodyAndCache<'_, 'tcx>,
def_id: DefId,
) {
if let FnKind::Closure(_) = fn_kind {
// closures can't recur, so they don't matter.
return;
}
let self_calls = find_blocks_calling_self(tcx, &body, def_id);
//FIXME(#54444) rewrite this lint to use the dataflow framework
// Stores a list of `Span`s for every basic block. Those are the spans of self-calls where we
// know that one of them will definitely be reached. If the list is empty, the block either
// wasn't processed yet or will not always go to a self-call.
let mut results = IndexVec::from_elem_n(vec![], body.basic_blocks().len());
// Walk through this function (say `f`) looking to see if
// every possible path references itself, i.e., the function is
// called recursively unconditionally. This is done by trying
// to find a path from the entry node to the exit node that
// *doesn't* call `f` by traversing from the entry while
// pretending that calls of `f` are sinks (i.e., ignoring any
// exit edges from them).
//
// NB. this has an edge case with non-returning statements,
// like `loop {}` or `panic!()`: control flow never reaches
// the exit node through these, so one can have a function
// that never actually calls itself but is still picked up by
// this lint:
//
// fn f(cond: bool) {
// if !cond { panic!() } // could come from `assert!(cond)`
// f(false)
// }
//
// In general, functions of that form may be able to call
// itself a finite number of times and then diverge. The lint
// considers this to be an error for two reasons, (a) it is
// easier to implement, and (b) it seems rare to actually want
// to have behaviour like the above, rather than
// e.g., accidentally recursing after an assert.
// We start the analysis at the self calls and work backwards.
let mut queue: VecDeque<_> = self_calls.iter().collect();
let basic_blocks = body.basic_blocks();
let mut reachable_without_self_call_queue = vec![mir::START_BLOCK];
let mut reached_exit_without_self_call = false;
let mut self_call_locations = vec![];
let mut visited = BitSet::new_empty(basic_blocks.len());
let param_env = tcx.param_env(def_id);
let trait_substs_count = match tcx.opt_associated_item(def_id) {
Some(AssocItem { container: AssocItemContainer::TraitContainer(trait_def_id), .. }) => {
tcx.generics_of(trait_def_id).count()
}
_ => 0,
};
let caller_substs = &InternalSubsts::identity_for_item(tcx, def_id)[..trait_substs_count];
while let Some(bb) = reachable_without_self_call_queue.pop() {
if !visited.insert(bb) {
//already done
while let Some(bb) = queue.pop_front() {
if !results[bb].is_empty() {
// Already propagated.
continue;
}
let block = &basic_blocks[bb];
let locations = if self_calls.contains(bb) {
// `bb` *is* a self-call.
// We don't look at successors here because they are irrelevant here and we don't want
// to lint them (eg. `f(); f()` should only lint the first call).
vec![bb]
} else {
// If *all* successors of `bb` lead to a self-call, emit notes at all of their
// locations.
if let Some(ref terminator) = block.terminator {
match terminator.kind {
TerminatorKind::Call { ref func, .. } => {
let func_ty = func.ty(body, tcx);
if let ty::FnDef(fn_def_id, substs) = func_ty.kind {
let (call_fn_id, call_substs) = if let Some(instance) =
Instance::resolve(tcx, param_env, fn_def_id, substs)
{
(instance.def_id(), instance.substs)
} else {
(fn_def_id, substs)
};
let is_self_call = call_fn_id == def_id
&& &call_substs[..caller_substs.len()] == caller_substs;
if is_self_call {
self_call_locations.push(terminator.source_info);
//this is a self call so we shouldn't explore
//further down this path
continue;
}
}
// Determine all "relevant" successors. We ignore successors only reached via unwinding.
let terminator = body[bb].terminator();
let relevant_successors = match &terminator.kind {
TerminatorKind::Call { destination: None, .. }
| TerminatorKind::Yield { .. }
| TerminatorKind::GeneratorDrop => None.into_iter().chain(&[]),
TerminatorKind::SwitchInt { targets, .. } => None.into_iter().chain(targets),
TerminatorKind::Goto { target }
| TerminatorKind::Drop { target, .. }
| TerminatorKind::DropAndReplace { target, .. }
| TerminatorKind::Assert { target, .. }
| TerminatorKind::FalseEdges { real_target: target, .. }
| TerminatorKind::FalseUnwind { real_target: target, .. }
| TerminatorKind::Call { destination: Some((_, target)), .. } => {
Some(target).into_iter().chain(&[])
}
TerminatorKind::Abort | TerminatorKind::Return => {
//found a path!
reached_exit_without_self_call = true;
break;
TerminatorKind::Resume
| TerminatorKind::Abort
| TerminatorKind::Return
| TerminatorKind::Unreachable => {
// We propagate backwards, so these should never be encountered here.
unreachable!("unexpected terminator {:?}", terminator.kind)
}
_ => {}
}
};
for successor in terminator.successors() {
reachable_without_self_call_queue.push(*successor);
// If all our successors are known to lead to self-calls, then we do too.
let all_are_self_calls =
relevant_successors.clone().all(|&succ| !results[succ].is_empty());
if all_are_self_calls {
// We'll definitely lead to a self-call. Merge all call locations of the successors
// for linting them later.
relevant_successors.flat_map(|&succ| results[succ].iter().copied()).collect()
} else {
// At least 1 successor does not always lead to a self-call, so we also don't.
vec![]
}
};
if !locations.is_empty() {
// This is a newly confirmed-to-always-reach-self-call block.
results[bb] = locations;
// Propagate backwards through the CFG.
debug!("propagate loc={:?} in {:?} -> {:?}", results[bb], bb, body.predecessors()[bb]);
queue.extend(body.predecessors()[bb].iter().copied());
}
}
// Check the number of self calls because a function that
// doesn't return (e.g., calls a `-> !` function or `loop { /*
// no break */ }`) shouldn't be linted unless it actually
// recurs.
if !reached_exit_without_self_call && !self_call_locations.is_empty() {
debug!("unconditional recursion results: {:?}", results);
let self_call_locations = &mut results[START_BLOCK];
self_call_locations.sort();
self_call_locations.dedup();
if !self_call_locations.is_empty() {
let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
let sp = tcx.sess.source_map().guess_head_span(tcx.hir().span(hir_id));
tcx.struct_span_lint_hir(UNCONDITIONAL_RECURSION, hir_id, sp, |lint| {
let mut db = lint.build("function cannot return without recursing");
db.span_label(sp, "cannot return without recursing");
// offer some help to the programmer.
for location in &self_call_locations {
db.span_label(location.span, "recursive call site");
for bb in self_call_locations {
let span = body.basic_blocks()[*bb].terminator().source_info.span;
db.span_label(span, "recursive call site");
}
db.help("a `loop` may express intention better if this is on purpose");
db.emit();
});
}
}
/// Finds blocks with `Call` terminators that would end up calling back into the same method.
fn find_blocks_calling_self<'tcx>(
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
def_id: DefId,
) -> BitSet<BasicBlock> {
let param_env = tcx.param_env(def_id);
// If this is trait/impl method, extract the trait's substs.
let trait_substs_count = match tcx.opt_associated_item(def_id) {
Some(AssocItem { container: AssocItemContainer::TraitContainer(trait_def_id), .. }) => {
tcx.generics_of(trait_def_id).count()
}
_ => 0,
};
let trait_substs = &InternalSubsts::identity_for_item(tcx, def_id)[..trait_substs_count];
let mut self_calls = BitSet::new_empty(body.basic_blocks().len());
for (bb, data) in body.basic_blocks().iter_enumerated() {
if let TerminatorKind::Call { func, .. } = &data.terminator().kind {
let func_ty = func.ty(body, tcx);
if let ty::FnDef(fn_def_id, substs) = func_ty.kind {
let (call_fn_id, call_substs) =
if let Some(instance) = Instance::resolve(tcx, param_env, fn_def_id, substs) {
(instance.def_id(), instance.substs)
} else {
(fn_def_id, substs)
};
// FIXME(#57965): Make this work across function boundaries
// If this is a trait fn, the substs on the trait have to match, or we might be
// calling into an entirely different method (for example, a call from the default
// method in the trait to `<A as Trait<B>>::method`, where `A` and/or `B` are
// specific types).
let is_self_call =
call_fn_id == def_id && &call_substs[..trait_substs.len()] == trait_substs;
if is_self_call {
self_calls.insert(bb);
}
}
}
}
self_calls
}

View File

@ -141,7 +141,7 @@ impl CheckAttrVisitor<'tcx> {
target: Target,
) -> bool {
match target {
Target::Fn if attr::contains_name(attrs, sym::naked) => {
_ if attr::contains_name(attrs, sym::naked) => {
struct_span_err!(
self.tcx.sess,
*attr_span,
@ -151,17 +151,7 @@ impl CheckAttrVisitor<'tcx> {
.emit();
false
}
Target::ForeignFn => {
struct_span_err!(
self.tcx.sess,
*attr_span,
E0738,
"`#[track_caller]` is not supported on foreign functions",
)
.emit();
false
}
Target::Fn | Target::Method(..) => true,
Target::Fn | Target::Method(..) | Target::ForeignFn => true,
_ => {
struct_span_err!(
self.tcx.sess,

View File

@ -717,9 +717,17 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> {
self.cx.parent
);
// Save all state that is specific to the outer function
// body. These will be restored once down below, once we've
// visited the body.
let outer_ec = mem::replace(&mut self.expr_and_pat_count, 0);
let outer_cx = self.cx;
let outer_ts = mem::take(&mut self.terminating_scopes);
// The 'pessimistic yield' flag is set to true when we are
// processing a `+=` statement and have to make pessimistic
// control flow assumptions. This doesn't apply to nested
// bodies within the `+=` statements. See #69307.
let outer_pessimistic_yield = mem::replace(&mut self.pessimistic_yield, false);
self.terminating_scopes.insert(body.value.hir_id.local_id);
if let Some(root_id) = self.cx.root_id {
@ -771,6 +779,7 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> {
self.expr_and_pat_count = outer_ec;
self.cx = outer_cx;
self.terminating_scopes = outer_ts;
self.pessimistic_yield = outer_pessimistic_yield;
}
fn visit_arm(&mut self, a: &'tcx Arm<'tcx>) {

View File

@ -1017,7 +1017,13 @@ pub fn get_cmd_lint_options(
let mut describe_lints = false;
for &level in &[lint::Allow, lint::Warn, lint::Deny, lint::Forbid] {
for (arg_pos, lint_name) in matches.opt_strs_pos(level.as_str()) {
for (passed_arg_pos, lint_name) in matches.opt_strs_pos(level.as_str()) {
let arg_pos = if let lint::Forbid = level {
// forbid is always specified last, so it can't be overridden
usize::max_value()
} else {
passed_arg_pos
};
if lint_name == "help" {
describe_lints = true;
} else {

View File

@ -81,10 +81,54 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind {
}
}
pub fn unrolled_find_u16s(needle: u16, haystack: &[u16]) -> Option<usize> {
let ptr = haystack.as_ptr();
let mut len = haystack.len();
let mut start = &haystack[..];
// For performance reasons unfold the loop eight times.
while len >= 8 {
if start[0] == needle {
return Some((start.as_ptr() as usize - ptr as usize) / 2);
}
if start[1] == needle {
return Some((start[1..].as_ptr() as usize - ptr as usize) / 2);
}
if start[2] == needle {
return Some((start[2..].as_ptr() as usize - ptr as usize) / 2);
}
if start[3] == needle {
return Some((start[3..].as_ptr() as usize - ptr as usize) / 2);
}
if start[4] == needle {
return Some((start[4..].as_ptr() as usize - ptr as usize) / 2);
}
if start[5] == needle {
return Some((start[5..].as_ptr() as usize - ptr as usize) / 2);
}
if start[6] == needle {
return Some((start[6..].as_ptr() as usize - ptr as usize) / 2);
}
if start[7] == needle {
return Some((start[7..].as_ptr() as usize - ptr as usize) / 2);
}
start = &start[8..];
len -= 8;
}
for (i, c) in start.iter().enumerate() {
if *c == needle {
return Some((start.as_ptr() as usize - ptr as usize) / 2 + i);
}
}
None
}
pub fn to_u16s<S: AsRef<OsStr>>(s: S) -> crate::io::Result<Vec<u16>> {
fn inner(s: &OsStr) -> crate::io::Result<Vec<u16>> {
let mut maybe_result: Vec<u16> = s.encode_wide().collect();
if maybe_result.iter().any(|&u| u == 0) {
if unrolled_find_u16s(0, &maybe_result).is_some() {
return Err(crate::io::Error::new(
ErrorKind::InvalidInput,
"strings passed to WinAPI cannot contain NULs",
@ -214,7 +258,7 @@ fn wide_char_to_multi_byte(
}
pub fn truncate_utf16_at_nul(v: &[u16]) -> &[u16] {
match v.iter().position(|c| *c == 0) {
match unrolled_find_u16s(0, v) {
// don't include the 0
Some(i) => &v[..i],
None => v,

View File

@ -0,0 +1,7 @@
// aux-build:lint-group-plugin-test.rs
// compile-flags: -F unused -A unused
fn main() {
let x = 1;
//~^ ERROR unused variable: `x`
}

View File

@ -0,0 +1,10 @@
error: unused variable: `x`
--> $DIR/lint-group-forbid-always-trumps-cli.rs:5:9
|
LL | let x = 1;
| ^ help: if this is intentional, prefix it with an underscore: `_x`
|
= note: `-F unused-variables` implied by `-F unused`
error: aborting due to previous error

View File

@ -0,0 +1,30 @@
// Regression test for #69307
//
// Having a `async { .. foo.await .. }` block appear inside of a `+=`
// expression was causing an ICE due to a failure to save/restore
// state in the AST numbering pass when entering a nested body.
//
// check-pass
// edition:2018
fn block_on<F>(_: F) -> usize {
0
}
fn main() {}
async fn bar() {
let mut sum = 0;
sum += {
block_on(async {
baz().await;
let mut inner = 1;
inner += block_on(async {
baz().await;
0
})
})
};
}
async fn baz() {}

View File

@ -0,0 +1,23 @@
// Regression test for #69307
//
// Having a `async { .. foo.await .. }` block appear inside of a `+=`
// expression was causing an ICE due to a failure to save/restore
// state in the AST numbering pass when entering a nested body.
//
// check-pass
// edition:2018
fn block_on<F>(_: F) -> usize {
0
}
fn main() {}
async fn bar() {
let mut sum = 0;
sum += block_on(async {
baz().await;
});
}
async fn baz() {}

View File

@ -131,4 +131,22 @@ trait Bar {
}
}
// Do not trigger on functions that may diverge instead of self-recursing (#54444)
pub fn loops(x: bool) {
if x {
loops(x);
} else {
loop {}
}
}
pub fn panics(x: bool) {
if x {
panics(!x);
} else {
panic!("panics");
}
}
fn main() {}

View File

@ -1,9 +0,0 @@
#![feature(track_caller)]
#![allow(dead_code)]
extern "Rust" {
#[track_caller] //~ ERROR: `#[track_caller]` is not supported on foreign functions
fn bar();
}
fn main() {}

View File

@ -1,9 +0,0 @@
error[E0738]: `#[track_caller]` is not supported on foreign functions
--> $DIR/error-extern-fn.rs:5:5
|
LL | #[track_caller]
| ^^^^^^^^^^^^^^^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0738`.

View File

@ -1,8 +1,21 @@
#![feature(naked_functions, track_caller)]
#[track_caller]
#[track_caller] //~ ERROR cannot use `#[track_caller]` with `#[naked]`
#[naked]
fn f() {}
//~^^^ ERROR cannot use `#[track_caller]` with `#[naked]`
struct S;
impl S {
#[track_caller] //~ ERROR cannot use `#[track_caller]` with `#[naked]`
#[naked]
fn g() {}
}
extern "Rust" {
#[track_caller] //~ ERROR cannot use `#[track_caller]` with `#[naked]`
#[naked]
fn h();
}
fn main() {}

View File

@ -4,6 +4,18 @@ error[E0736]: cannot use `#[track_caller]` with `#[naked]`
LL | #[track_caller]
| ^^^^^^^^^^^^^^^
error: aborting due to previous error
error[E0736]: cannot use `#[track_caller]` with `#[naked]`
--> $DIR/error-with-naked.rs:16:5
|
LL | #[track_caller]
| ^^^^^^^^^^^^^^^
error[E0736]: cannot use `#[track_caller]` with `#[naked]`
--> $DIR/error-with-naked.rs:10:5
|
LL | #[track_caller]
| ^^^^^^^^^^^^^^^
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0736`.

View File

@ -0,0 +1,50 @@
// run-pass
#![feature(track_caller)]
use std::panic::Location;
extern "Rust" {
#[track_caller]
fn rust_track_caller_ffi_test_tracked() -> &'static Location<'static>;
fn rust_track_caller_ffi_test_untracked() -> &'static Location<'static>;
}
fn rust_track_caller_ffi_test_nested_tracked() -> &'static Location<'static> {
unsafe { rust_track_caller_ffi_test_tracked() }
}
mod provides {
use std::panic::Location;
#[track_caller] // UB if we did not have this!
#[no_mangle]
fn rust_track_caller_ffi_test_tracked() -> &'static Location<'static> {
Location::caller()
}
#[no_mangle]
fn rust_track_caller_ffi_test_untracked() -> &'static Location<'static> {
Location::caller()
}
}
fn main() {
let location = Location::caller();
assert_eq!(location.file(), file!());
assert_eq!(location.line(), 31);
assert_eq!(location.column(), 20);
let tracked = unsafe { rust_track_caller_ffi_test_tracked() };
assert_eq!(tracked.file(), file!());
assert_eq!(tracked.line(), 36);
assert_eq!(tracked.column(), 28);
let untracked = unsafe { rust_track_caller_ffi_test_untracked() };
assert_eq!(untracked.file(), file!());
assert_eq!(untracked.line(), 26);
assert_eq!(untracked.column(), 9);
let contained = rust_track_caller_ffi_test_nested_tracked();
assert_eq!(contained.file(), file!());
assert_eq!(contained.line(), 14);
assert_eq!(contained.column(), 14);
}