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:
commit
93dc97a853
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 => {
|
||||
|
@ -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) {
|
||||
|
@ -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" { ... }`.
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
```
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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>) {
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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`
|
||||
}
|
@ -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
|
||||
|
30
src/test/ui/async-await/issues/issue-69307-nested.rs
Normal file
30
src/test/ui/async-await/issues/issue-69307-nested.rs
Normal 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() {}
|
23
src/test/ui/async-await/issues/issue-69307.rs
Normal file
23
src/test/ui/async-await/issues/issue-69307.rs
Normal 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() {}
|
@ -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() {}
|
||||
|
@ -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() {}
|
@ -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`.
|
@ -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() {}
|
||||
|
@ -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`.
|
||||
|
50
src/test/ui/rfc-2091-track-caller/track-caller-ffi.rs
Normal file
50
src/test/ui/rfc-2091-track-caller/track-caller-ffi.rs
Normal 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);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user