Auto merge of #93138 - matthiaskrgr:rollup-m8akifd, r=matthiaskrgr
Rollup of 17 pull requests Successful merges: - #91032 (Introduce drop range tracking to generator interior analysis) - #92856 (Exclude "test" from doc_auto_cfg) - #92860 (Fix errors on blanket impls by ignoring the children of generated impls) - #93038 (Fix star handling in block doc comments) - #93061 (Only suggest adding `!` to expressions that can be macro invocation) - #93067 (rustdoc mobile: fix scroll offset when jumping to internal id) - #93086 (Add tests to ensure that `let_chains` works with `if_let_guard`) - #93087 (Fix src/test/run-make/raw-dylib-alt-calling-convention) - #93091 (⬆ chalk to 0.76.0) - #93094 (src/test/rustdoc-json: Check for `struct_field`s in `variant_tuple_struct.rs`) - #93098 (Show a more informative panic message when `DefPathHash` does not exist) - #93099 (rustdoc: auto create output directory when "--output-format json") - #93102 (Pretty printer algorithm revamp step 3) - #93104 (Support --bless for pp-exact pretty printer tests) - #93114 (update comment for `ensure_monomorphic_enough`) - #93128 (Add script to prevent point releases with same number as existing ones) - #93136 (Backport the 1.58.1 release notes to master) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
523be2e05d
62 changed files with 1925 additions and 339 deletions
9
.github/workflows/ci.yml
vendored
9
.github/workflows/ci.yml
vendored
|
@ -128,6 +128,9 @@ jobs:
|
||||||
- name: ensure backported commits are in upstream branches
|
- name: ensure backported commits are in upstream branches
|
||||||
run: src/ci/scripts/verify-backported-commits.sh
|
run: src/ci/scripts/verify-backported-commits.sh
|
||||||
if: success() && !env.SKIP_JOB
|
if: success() && !env.SKIP_JOB
|
||||||
|
- name: ensure the stable version number is correct
|
||||||
|
run: src/ci/scripts/verify-stable-version-number.sh
|
||||||
|
if: success() && !env.SKIP_JOB
|
||||||
- name: run the build
|
- name: run the build
|
||||||
run: src/ci/scripts/run-build-from-ci.sh
|
run: src/ci/scripts/run-build-from-ci.sh
|
||||||
env:
|
env:
|
||||||
|
@ -502,6 +505,9 @@ jobs:
|
||||||
- name: ensure backported commits are in upstream branches
|
- name: ensure backported commits are in upstream branches
|
||||||
run: src/ci/scripts/verify-backported-commits.sh
|
run: src/ci/scripts/verify-backported-commits.sh
|
||||||
if: success() && !env.SKIP_JOB
|
if: success() && !env.SKIP_JOB
|
||||||
|
- name: ensure the stable version number is correct
|
||||||
|
run: src/ci/scripts/verify-stable-version-number.sh
|
||||||
|
if: success() && !env.SKIP_JOB
|
||||||
- name: run the build
|
- name: run the build
|
||||||
run: src/ci/scripts/run-build-from-ci.sh
|
run: src/ci/scripts/run-build-from-ci.sh
|
||||||
env:
|
env:
|
||||||
|
@ -612,6 +618,9 @@ jobs:
|
||||||
- name: ensure backported commits are in upstream branches
|
- name: ensure backported commits are in upstream branches
|
||||||
run: src/ci/scripts/verify-backported-commits.sh
|
run: src/ci/scripts/verify-backported-commits.sh
|
||||||
if: success() && !env.SKIP_JOB
|
if: success() && !env.SKIP_JOB
|
||||||
|
- name: ensure the stable version number is correct
|
||||||
|
run: src/ci/scripts/verify-stable-version-number.sh
|
||||||
|
if: success() && !env.SKIP_JOB
|
||||||
- name: run the build
|
- name: run the build
|
||||||
run: src/ci/scripts/run-build-from-ci.sh
|
run: src/ci/scripts/run-build-from-ci.sh
|
||||||
env:
|
env:
|
||||||
|
|
19
Cargo.lock
19
Cargo.lock
|
@ -544,9 +544,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chalk-derive"
|
name = "chalk-derive"
|
||||||
version = "0.75.0"
|
version = "0.76.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d54e3b5f9e3425e6b119ff07568d8d006bfa5a8d6f78a9cbc3530b1e962e316c"
|
checksum = "58c24b8052ea1e3adbb6f9ab7ba5fcc18b9d12591c042de4c833f709ce81e0e0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -556,9 +556,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chalk-engine"
|
name = "chalk-engine"
|
||||||
version = "0.75.0"
|
version = "0.76.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bdc891073396b167163db77123b0a3c00088edc00466cecc5531f33e3e989523"
|
checksum = "0eca186b6ea9af798312f4b568fd094c82e7946ac08be5dc5fea22decc6d2ed8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chalk-derive",
|
"chalk-derive",
|
||||||
"chalk-ir",
|
"chalk-ir",
|
||||||
|
@ -569,9 +569,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chalk-ir"
|
name = "chalk-ir"
|
||||||
version = "0.75.0"
|
version = "0.76.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2b79e5a1d04b79311e90c69356a2c62027853906a7e33b3e070b93c055fc3e8a"
|
checksum = "f3cad5c3f1edd4b4a2c9bda24ae558ceb4f88336f88f944c2e35d0bfeb13c818"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"chalk-derive",
|
"chalk-derive",
|
||||||
|
@ -580,13 +580,14 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chalk-solve"
|
name = "chalk-solve"
|
||||||
version = "0.75.0"
|
version = "0.76.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a5d2a1db6605aba70a58820bd80ac422b218913a510f1a40beef9efc5371ea1d"
|
checksum = "94533188d3452bc72cbd5618d166f45fc7646b674ad3fe9667d557bc25236dee"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chalk-derive",
|
"chalk-derive",
|
||||||
"chalk-ir",
|
"chalk-ir",
|
||||||
"ena",
|
"ena",
|
||||||
|
"indexmap",
|
||||||
"itertools 0.10.1",
|
"itertools 0.10.1",
|
||||||
"petgraph",
|
"petgraph",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
|
@ -4413,6 +4414,7 @@ dependencies = [
|
||||||
"rustc_attr",
|
"rustc_attr",
|
||||||
"rustc_data_structures",
|
"rustc_data_structures",
|
||||||
"rustc_errors",
|
"rustc_errors",
|
||||||
|
"rustc_graphviz",
|
||||||
"rustc_hir",
|
"rustc_hir",
|
||||||
"rustc_hir_pretty",
|
"rustc_hir_pretty",
|
||||||
"rustc_index",
|
"rustc_index",
|
||||||
|
@ -4420,6 +4422,7 @@ dependencies = [
|
||||||
"rustc_lint",
|
"rustc_lint",
|
||||||
"rustc_macros",
|
"rustc_macros",
|
||||||
"rustc_middle",
|
"rustc_middle",
|
||||||
|
"rustc_serialize",
|
||||||
"rustc_session",
|
"rustc_session",
|
||||||
"rustc_span",
|
"rustc_span",
|
||||||
"rustc_target",
|
"rustc_target",
|
||||||
|
|
15
RELEASES.md
15
RELEASES.md
|
@ -1,3 +1,18 @@
|
||||||
|
Version 1.58.1 (2022-01-19)
|
||||||
|
===========================
|
||||||
|
|
||||||
|
* Fix race condition in `std::fs::remove_dir_all` ([CVE-2022-21658])
|
||||||
|
* [Handle captured arguments in the `useless_format` Clippy lint][clippy/8295]
|
||||||
|
* [Move `non_send_fields_in_send_ty` Clippy lint to nursery][clippy/8075]
|
||||||
|
* [Fix wrong error message displayed when some imports are missing][91254]
|
||||||
|
* [Fix rustfmt not formatting generated files from stdin][92912]
|
||||||
|
|
||||||
|
[CVE-2022-21658]: https://www.cve.org/CVERecord?id=CVE-2022-21658]
|
||||||
|
[91254]: https://github.com/rust-lang/rust/pull/91254
|
||||||
|
[92912]: https://github.com/rust-lang/rust/pull/92912
|
||||||
|
[clippy/8075]: https://github.com/rust-lang/rust-clippy/pull/8075
|
||||||
|
[clippy/8295]: https://github.com/rust-lang/rust-clippy/pull/8295
|
||||||
|
|
||||||
Version 1.58.0 (2022-01-13)
|
Version 1.58.0 (2022-01-13)
|
||||||
==========================
|
==========================
|
||||||
|
|
||||||
|
|
|
@ -137,7 +137,7 @@ mod ring;
|
||||||
use ring::RingBuffer;
|
use ring::RingBuffer;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::fmt;
|
use std::iter;
|
||||||
|
|
||||||
/// How to break. Described in more detail in the module docs.
|
/// How to break. Described in more detail in the module docs.
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
|
@ -175,27 +175,10 @@ impl Token {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Token {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
match *self {
|
|
||||||
Token::String(ref s) => write!(f, "STR({},{})", s, s.len()),
|
|
||||||
Token::Break(_) => f.write_str("BREAK"),
|
|
||||||
Token::Begin(_) => f.write_str("BEGIN"),
|
|
||||||
Token::End => f.write_str("END"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
enum PrintStackBreak {
|
enum PrintFrame {
|
||||||
Fits,
|
Fits,
|
||||||
Broken(Breaks),
|
Broken { offset: isize, breaks: Breaks },
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
struct PrintStackElem {
|
|
||||||
offset: isize,
|
|
||||||
pbreak: PrintStackBreak,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const SIZE_INFINITY: isize = 0xffff;
|
const SIZE_INFINITY: isize = 0xffff;
|
||||||
|
@ -220,7 +203,7 @@ pub struct Printer {
|
||||||
/// advancing.
|
/// advancing.
|
||||||
scan_stack: VecDeque<usize>,
|
scan_stack: VecDeque<usize>,
|
||||||
/// Stack of blocks-in-progress being flushed by print
|
/// Stack of blocks-in-progress being flushed by print
|
||||||
print_stack: Vec<PrintStackElem>,
|
print_stack: Vec<PrintFrame>,
|
||||||
/// Buffered indentation to avoid writing trailing whitespace
|
/// Buffered indentation to avoid writing trailing whitespace
|
||||||
pending_indentation: isize,
|
pending_indentation: isize,
|
||||||
/// The token most recently popped from the left boundary of the
|
/// The token most recently popped from the left boundary of the
|
||||||
|
@ -260,8 +243,8 @@ impl Printer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Be very careful with this!
|
/// Be very careful with this!
|
||||||
pub fn replace_last_token_still_buffered(&mut self, t: Token) {
|
pub fn replace_last_token_still_buffered(&mut self, token: Token) {
|
||||||
self.buf.last_mut().unwrap().token = t;
|
self.buf.last_mut().unwrap().token = token;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scan_eof(&mut self) {
|
fn scan_eof(&mut self) {
|
||||||
|
@ -271,14 +254,14 @@ impl Printer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scan_begin(&mut self, b: BeginToken) {
|
fn scan_begin(&mut self, token: BeginToken) {
|
||||||
if self.scan_stack.is_empty() {
|
if self.scan_stack.is_empty() {
|
||||||
self.left_total = 1;
|
self.left_total = 1;
|
||||||
self.right_total = 1;
|
self.right_total = 1;
|
||||||
self.buf.clear();
|
self.buf.clear();
|
||||||
}
|
}
|
||||||
let right = self.buf.push(BufEntry { token: Token::Begin(b), size: -self.right_total });
|
let right = self.buf.push(BufEntry { token: Token::Begin(token), size: -self.right_total });
|
||||||
self.scan_stack.push_front(right);
|
self.scan_stack.push_back(right);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scan_end(&mut self) {
|
fn scan_end(&mut self) {
|
||||||
|
@ -286,11 +269,11 @@ impl Printer {
|
||||||
self.print_end();
|
self.print_end();
|
||||||
} else {
|
} else {
|
||||||
let right = self.buf.push(BufEntry { token: Token::End, size: -1 });
|
let right = self.buf.push(BufEntry { token: Token::End, size: -1 });
|
||||||
self.scan_stack.push_front(right);
|
self.scan_stack.push_back(right);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scan_break(&mut self, b: BreakToken) {
|
fn scan_break(&mut self, token: BreakToken) {
|
||||||
if self.scan_stack.is_empty() {
|
if self.scan_stack.is_empty() {
|
||||||
self.left_total = 1;
|
self.left_total = 1;
|
||||||
self.right_total = 1;
|
self.right_total = 1;
|
||||||
|
@ -298,17 +281,17 @@ impl Printer {
|
||||||
} else {
|
} else {
|
||||||
self.check_stack(0);
|
self.check_stack(0);
|
||||||
}
|
}
|
||||||
let right = self.buf.push(BufEntry { token: Token::Break(b), size: -self.right_total });
|
let right = self.buf.push(BufEntry { token: Token::Break(token), size: -self.right_total });
|
||||||
self.scan_stack.push_front(right);
|
self.scan_stack.push_back(right);
|
||||||
self.right_total += b.blank_space;
|
self.right_total += token.blank_space;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scan_string(&mut self, s: Cow<'static, str>) {
|
fn scan_string(&mut self, string: Cow<'static, str>) {
|
||||||
if self.scan_stack.is_empty() {
|
if self.scan_stack.is_empty() {
|
||||||
self.print_string(&s);
|
self.print_string(&string);
|
||||||
} else {
|
} else {
|
||||||
let len = s.len() as isize;
|
let len = string.len() as isize;
|
||||||
self.buf.push(BufEntry { token: Token::String(s), size: len });
|
self.buf.push(BufEntry { token: Token::String(string), size: len });
|
||||||
self.right_total += len;
|
self.right_total += len;
|
||||||
self.check_stream();
|
self.check_stream();
|
||||||
}
|
}
|
||||||
|
@ -316,8 +299,8 @@ impl Printer {
|
||||||
|
|
||||||
fn check_stream(&mut self) {
|
fn check_stream(&mut self) {
|
||||||
while self.right_total - self.left_total > self.space {
|
while self.right_total - self.left_total > self.space {
|
||||||
if *self.scan_stack.back().unwrap() == self.buf.index_of_first() {
|
if *self.scan_stack.front().unwrap() == self.buf.index_of_first() {
|
||||||
self.scan_stack.pop_back().unwrap();
|
self.scan_stack.pop_front().unwrap();
|
||||||
self.buf.first_mut().unwrap().size = SIZE_INFINITY;
|
self.buf.first_mut().unwrap().size = SIZE_INFINITY;
|
||||||
}
|
}
|
||||||
self.advance_left();
|
self.advance_left();
|
||||||
|
@ -328,56 +311,52 @@ impl Printer {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn advance_left(&mut self) {
|
fn advance_left(&mut self) {
|
||||||
let mut left_size = self.buf.first().unwrap().size;
|
while self.buf.first().unwrap().size >= 0 {
|
||||||
|
let left = self.buf.pop_first().unwrap();
|
||||||
|
|
||||||
while left_size >= 0 {
|
match &left.token {
|
||||||
let left = self.buf.first().unwrap().token.clone();
|
Token::String(string) => {
|
||||||
|
self.left_total += string.len() as isize;
|
||||||
let len = match left {
|
self.print_string(string);
|
||||||
Token::Break(b) => b.blank_space,
|
|
||||||
Token::String(ref s) => {
|
|
||||||
let len = s.len() as isize;
|
|
||||||
assert_eq!(len, left_size);
|
|
||||||
len
|
|
||||||
}
|
}
|
||||||
_ => 0,
|
Token::Break(token) => {
|
||||||
};
|
self.left_total += token.blank_space;
|
||||||
|
self.print_break(*token, left.size);
|
||||||
|
}
|
||||||
|
Token::Begin(token) => self.print_begin(*token, left.size),
|
||||||
|
Token::End => self.print_end(),
|
||||||
|
}
|
||||||
|
|
||||||
self.print(left, left_size);
|
self.last_printed = Some(left.token);
|
||||||
|
|
||||||
self.left_total += len;
|
|
||||||
|
|
||||||
self.buf.advance_left();
|
|
||||||
if self.buf.is_empty() {
|
if self.buf.is_empty() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
left_size = self.buf.first().unwrap().size;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_stack(&mut self, mut k: usize) {
|
fn check_stack(&mut self, mut depth: usize) {
|
||||||
while let Some(&x) = self.scan_stack.front() {
|
while let Some(&index) = self.scan_stack.back() {
|
||||||
let mut entry = &mut self.buf[x];
|
let mut entry = &mut self.buf[index];
|
||||||
match entry.token {
|
match entry.token {
|
||||||
Token::Begin(_) => {
|
Token::Begin(_) => {
|
||||||
if k == 0 {
|
if depth == 0 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
self.scan_stack.pop_front().unwrap();
|
self.scan_stack.pop_back().unwrap();
|
||||||
entry.size += self.right_total;
|
entry.size += self.right_total;
|
||||||
k -= 1;
|
depth -= 1;
|
||||||
}
|
}
|
||||||
Token::End => {
|
Token::End => {
|
||||||
// paper says + not =, but that makes no sense.
|
// paper says + not =, but that makes no sense.
|
||||||
self.scan_stack.pop_front().unwrap();
|
self.scan_stack.pop_back().unwrap();
|
||||||
entry.size = 1;
|
entry.size = 1;
|
||||||
k += 1;
|
depth += 1;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
self.scan_stack.pop_front().unwrap();
|
self.scan_stack.pop_back().unwrap();
|
||||||
entry.size += self.right_total;
|
entry.size += self.right_total;
|
||||||
if k == 0 {
|
if depth == 0 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -385,29 +364,19 @@ impl Printer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_newline(&mut self, amount: isize) {
|
fn get_top(&self) -> PrintFrame {
|
||||||
self.out.push('\n');
|
*self
|
||||||
self.pending_indentation = 0;
|
.print_stack
|
||||||
self.indent(amount);
|
.last()
|
||||||
|
.unwrap_or(&PrintFrame::Broken { offset: 0, breaks: Breaks::Inconsistent })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn indent(&mut self, amount: isize) {
|
fn print_begin(&mut self, token: BeginToken, size: isize) {
|
||||||
self.pending_indentation += amount;
|
if size > self.space {
|
||||||
}
|
let col = self.margin - self.space + token.offset;
|
||||||
|
self.print_stack.push(PrintFrame::Broken { offset: col, breaks: token.breaks });
|
||||||
fn get_top(&self) -> PrintStackElem {
|
|
||||||
*self.print_stack.last().unwrap_or({
|
|
||||||
&PrintStackElem { offset: 0, pbreak: PrintStackBreak::Broken(Breaks::Inconsistent) }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn print_begin(&mut self, b: BeginToken, l: isize) {
|
|
||||||
if l > self.space {
|
|
||||||
let col = self.margin - self.space + b.offset;
|
|
||||||
self.print_stack
|
|
||||||
.push(PrintStackElem { offset: col, pbreak: PrintStackBreak::Broken(b.breaks) });
|
|
||||||
} else {
|
} else {
|
||||||
self.print_stack.push(PrintStackElem { offset: 0, pbreak: PrintStackBreak::Fits });
|
self.print_stack.push(PrintFrame::Fits);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -415,34 +384,26 @@ impl Printer {
|
||||||
self.print_stack.pop().unwrap();
|
self.print_stack.pop().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_break(&mut self, b: BreakToken, l: isize) {
|
fn print_break(&mut self, token: BreakToken, size: isize) {
|
||||||
let top = self.get_top();
|
let break_offset =
|
||||||
match top.pbreak {
|
match self.get_top() {
|
||||||
PrintStackBreak::Fits => {
|
PrintFrame::Fits => None,
|
||||||
self.space -= b.blank_space;
|
PrintFrame::Broken { offset, breaks: Breaks::Consistent } => Some(offset),
|
||||||
self.indent(b.blank_space);
|
PrintFrame::Broken { offset, breaks: Breaks::Inconsistent } => {
|
||||||
}
|
if size > self.space { Some(offset) } else { None }
|
||||||
PrintStackBreak::Broken(Breaks::Consistent) => {
|
|
||||||
self.print_newline(top.offset + b.offset);
|
|
||||||
self.space = self.margin - (top.offset + b.offset);
|
|
||||||
}
|
|
||||||
PrintStackBreak::Broken(Breaks::Inconsistent) => {
|
|
||||||
if l > self.space {
|
|
||||||
self.print_newline(top.offset + b.offset);
|
|
||||||
self.space = self.margin - (top.offset + b.offset);
|
|
||||||
} else {
|
|
||||||
self.indent(b.blank_space);
|
|
||||||
self.space -= b.blank_space;
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
if let Some(offset) = break_offset {
|
||||||
|
self.out.push('\n');
|
||||||
|
self.pending_indentation = offset + token.offset;
|
||||||
|
self.space = self.margin - (offset + token.offset);
|
||||||
|
} else {
|
||||||
|
self.pending_indentation += token.blank_space;
|
||||||
|
self.space -= token.blank_space;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_string(&mut self, s: &str) {
|
fn print_string(&mut self, string: &str) {
|
||||||
let len = s.len() as isize;
|
|
||||||
// assert!(len <= space);
|
|
||||||
self.space -= len;
|
|
||||||
|
|
||||||
// Write the pending indent. A more concise way of doing this would be:
|
// Write the pending indent. A more concise way of doing this would be:
|
||||||
//
|
//
|
||||||
// write!(self.out, "{: >n$}", "", n = self.pending_indentation as usize)?;
|
// write!(self.out, "{: >n$}", "", n = self.pending_indentation as usize)?;
|
||||||
|
@ -450,30 +411,18 @@ impl Printer {
|
||||||
// But that is significantly slower. This code is sufficiently hot, and indents can get
|
// But that is significantly slower. This code is sufficiently hot, and indents can get
|
||||||
// sufficiently large, that the difference is significant on some workloads.
|
// sufficiently large, that the difference is significant on some workloads.
|
||||||
self.out.reserve(self.pending_indentation as usize);
|
self.out.reserve(self.pending_indentation as usize);
|
||||||
self.out.extend(std::iter::repeat(' ').take(self.pending_indentation as usize));
|
self.out.extend(iter::repeat(' ').take(self.pending_indentation as usize));
|
||||||
self.pending_indentation = 0;
|
self.pending_indentation = 0;
|
||||||
self.out.push_str(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn print(&mut self, token: Token, l: isize) {
|
self.out.push_str(string);
|
||||||
match &token {
|
self.space -= string.len() as isize;
|
||||||
Token::Begin(b) => self.print_begin(*b, l),
|
|
||||||
Token::End => self.print_end(),
|
|
||||||
Token::Break(b) => self.print_break(*b, l),
|
|
||||||
Token::String(s) => {
|
|
||||||
let len = s.len() as isize;
|
|
||||||
assert_eq!(len, l);
|
|
||||||
self.print_string(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.last_printed = Some(token);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convenience functions to talk to the printer.
|
// Convenience functions to talk to the printer.
|
||||||
|
|
||||||
/// "raw box"
|
/// "raw box"
|
||||||
pub fn rbox(&mut self, indent: usize, b: Breaks) {
|
pub fn rbox(&mut self, indent: usize, breaks: Breaks) {
|
||||||
self.scan_begin(BeginToken { offset: indent as isize, breaks: b })
|
self.scan_begin(BeginToken { offset: indent as isize, breaks })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inconsistent breaking box
|
/// Inconsistent breaking box
|
||||||
|
@ -500,8 +449,8 @@ impl Printer {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn word<S: Into<Cow<'static, str>>>(&mut self, wrd: S) {
|
pub fn word<S: Into<Cow<'static, str>>>(&mut self, wrd: S) {
|
||||||
let s = wrd.into();
|
let string = wrd.into();
|
||||||
self.scan_string(s)
|
self.scan_string(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spaces(&mut self, n: usize) {
|
fn spaces(&mut self, n: usize) {
|
||||||
|
|
|
@ -32,11 +32,6 @@ impl<T> RingBuffer<T> {
|
||||||
index
|
index
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn advance_left(&mut self) {
|
|
||||||
self.data.pop_front().unwrap();
|
|
||||||
self.offset += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.data.clear();
|
self.data.clear();
|
||||||
}
|
}
|
||||||
|
@ -53,6 +48,12 @@ impl<T> RingBuffer<T> {
|
||||||
self.data.front_mut()
|
self.data.front_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn pop_first(&mut self) -> Option<T> {
|
||||||
|
let first = self.data.pop_front()?;
|
||||||
|
self.offset += 1;
|
||||||
|
Some(first)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn last(&self) -> Option<&T> {
|
pub fn last(&self) -> Option<&T> {
|
||||||
self.data.back()
|
self.data.back()
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,11 @@ use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitor};
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::ops::ControlFlow;
|
use std::ops::ControlFlow;
|
||||||
|
|
||||||
/// Returns `true` if a used generic parameter requires substitution.
|
/// Checks whether a type contains generic parameters which require substitution.
|
||||||
|
///
|
||||||
|
/// In case it does, returns a `TooGeneric` const eval error. Note that due to polymorphization
|
||||||
|
/// types may be "concrete enough" even though they still contain generic parameters in
|
||||||
|
/// case these parameters are unused.
|
||||||
crate fn ensure_monomorphic_enough<'tcx, T>(tcx: TyCtxt<'tcx>, ty: T) -> InterpResult<'tcx>
|
crate fn ensure_monomorphic_enough<'tcx, T>(tcx: TyCtxt<'tcx>, ty: T) -> InterpResult<'tcx>
|
||||||
where
|
where
|
||||||
T: TypeFoldable<'tcx>,
|
T: TypeFoldable<'tcx>,
|
||||||
|
|
|
@ -449,13 +449,17 @@ impl Definitions {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn local_def_path_hash_to_def_id(&self, hash: DefPathHash) -> LocalDefId {
|
pub fn local_def_path_hash_to_def_id(
|
||||||
|
&self,
|
||||||
|
hash: DefPathHash,
|
||||||
|
err: &mut dyn FnMut() -> !,
|
||||||
|
) -> LocalDefId {
|
||||||
debug_assert!(hash.stable_crate_id() == self.stable_crate_id);
|
debug_assert!(hash.stable_crate_id() == self.stable_crate_id);
|
||||||
self.table
|
self.table
|
||||||
.def_path_hash_to_index
|
.def_path_hash_to_index
|
||||||
.get(&hash)
|
.get(&hash)
|
||||||
.map(|local_def_index| LocalDefId { local_def_index })
|
.map(|local_def_index| LocalDefId { local_def_index })
|
||||||
.unwrap()
|
.unwrap_or_else(|| err())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn def_path_hash_to_def_index_map(&self) -> &DefPathHashMap {
|
pub fn def_path_hash_to_def_index_map(&self) -> &DefPathHashMap {
|
||||||
|
|
|
@ -29,7 +29,7 @@ rustc_index = { path = "../rustc_index" }
|
||||||
rustc_serialize = { path = "../rustc_serialize" }
|
rustc_serialize = { path = "../rustc_serialize" }
|
||||||
rustc_ast = { path = "../rustc_ast" }
|
rustc_ast = { path = "../rustc_ast" }
|
||||||
rustc_span = { path = "../rustc_span" }
|
rustc_span = { path = "../rustc_span" }
|
||||||
chalk-ir = "0.75.0"
|
chalk-ir = "0.76.0"
|
||||||
smallvec = { version = "1.6.1", features = ["union", "may_dangle"] }
|
smallvec = { version = "1.6.1", features = ["union", "may_dangle"] }
|
||||||
rustc_session = { path = "../rustc_session" }
|
rustc_session = { path = "../rustc_session" }
|
||||||
rustc_type_ir = { path = "../rustc_type_ir" }
|
rustc_type_ir = { path = "../rustc_type_ir" }
|
||||||
|
|
|
@ -266,7 +266,9 @@ impl DepNodeExt for DepNode {
|
||||||
/// has been removed.
|
/// has been removed.
|
||||||
fn extract_def_id<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Option<DefId> {
|
fn extract_def_id<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Option<DefId> {
|
||||||
if self.kind.fingerprint_style(tcx) == FingerprintStyle::DefPathHash {
|
if self.kind.fingerprint_style(tcx) == FingerprintStyle::DefPathHash {
|
||||||
Some(tcx.def_path_hash_to_def_id(DefPathHash(self.hash.into())))
|
Some(tcx.def_path_hash_to_def_id(DefPathHash(self.hash.into()), &mut || {
|
||||||
|
panic!("Failed to extract DefId: {:?} {}", self.kind, self.hash)
|
||||||
|
}))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
|
@ -308,7 +308,7 @@ pub struct ScopeTree {
|
||||||
/// The reason is that semantically, until the `box` expression returns,
|
/// The reason is that semantically, until the `box` expression returns,
|
||||||
/// the values are still owned by their containing expressions. So
|
/// the values are still owned by their containing expressions. So
|
||||||
/// we'll see that `&x`.
|
/// we'll see that `&x`.
|
||||||
pub yield_in_scope: FxHashMap<Scope, YieldData>,
|
pub yield_in_scope: FxHashMap<Scope, Vec<YieldData>>,
|
||||||
|
|
||||||
/// The number of visit_expr and visit_pat calls done in the body.
|
/// The number of visit_expr and visit_pat calls done in the body.
|
||||||
/// Used to sanity check visit_expr/visit_pat call count when
|
/// Used to sanity check visit_expr/visit_pat call count when
|
||||||
|
@ -423,8 +423,8 @@ impl ScopeTree {
|
||||||
|
|
||||||
/// Checks whether the given scope contains a `yield`. If so,
|
/// Checks whether the given scope contains a `yield`. If so,
|
||||||
/// returns `Some(YieldData)`. If not, returns `None`.
|
/// returns `Some(YieldData)`. If not, returns `None`.
|
||||||
pub fn yield_in_scope(&self, scope: Scope) -> Option<YieldData> {
|
pub fn yield_in_scope(&self, scope: Scope) -> Option<&Vec<YieldData>> {
|
||||||
self.yield_in_scope.get(&scope).cloned()
|
self.yield_in_scope.get(&scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gives the number of expressions visited in a body.
|
/// Gives the number of expressions visited in a body.
|
||||||
|
|
|
@ -1322,7 +1322,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||||
/// Converts a `DefPathHash` to its corresponding `DefId` in the current compilation
|
/// Converts a `DefPathHash` to its corresponding `DefId` in the current compilation
|
||||||
/// session, if it still exists. This is used during incremental compilation to
|
/// session, if it still exists. This is used during incremental compilation to
|
||||||
/// turn a deserialized `DefPathHash` into its current `DefId`.
|
/// turn a deserialized `DefPathHash` into its current `DefId`.
|
||||||
pub fn def_path_hash_to_def_id(self, hash: DefPathHash) -> DefId {
|
pub fn def_path_hash_to_def_id(self, hash: DefPathHash, err: &mut dyn FnMut() -> !) -> DefId {
|
||||||
debug!("def_path_hash_to_def_id({:?})", hash);
|
debug!("def_path_hash_to_def_id({:?})", hash);
|
||||||
|
|
||||||
let stable_crate_id = hash.stable_crate_id();
|
let stable_crate_id = hash.stable_crate_id();
|
||||||
|
@ -1330,7 +1330,10 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||||
// If this is a DefPathHash from the local crate, we can look up the
|
// If this is a DefPathHash from the local crate, we can look up the
|
||||||
// DefId in the tcx's `Definitions`.
|
// DefId in the tcx's `Definitions`.
|
||||||
if stable_crate_id == self.sess.local_stable_crate_id() {
|
if stable_crate_id == self.sess.local_stable_crate_id() {
|
||||||
self.untracked_resolutions.definitions.local_def_path_hash_to_def_id(hash).to_def_id()
|
self.untracked_resolutions
|
||||||
|
.definitions
|
||||||
|
.local_def_path_hash_to_def_id(hash, err)
|
||||||
|
.to_def_id()
|
||||||
} else {
|
} else {
|
||||||
// If this is a DefPathHash from an upstream crate, let the CrateStore map
|
// If this is a DefPathHash from an upstream crate, let the CrateStore map
|
||||||
// it to a DefId.
|
// it to a DefId.
|
||||||
|
|
|
@ -366,7 +366,8 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h
|
||||||
let target_scopes = visitor.fixup_scopes.drain(start_point..);
|
let target_scopes = visitor.fixup_scopes.drain(start_point..);
|
||||||
|
|
||||||
for scope in target_scopes {
|
for scope in target_scopes {
|
||||||
let mut yield_data = visitor.scope_tree.yield_in_scope.get_mut(&scope).unwrap();
|
let mut yield_data =
|
||||||
|
visitor.scope_tree.yield_in_scope.get_mut(&scope).unwrap().last_mut().unwrap();
|
||||||
let count = yield_data.expr_and_pat_count;
|
let count = yield_data.expr_and_pat_count;
|
||||||
let span = yield_data.span;
|
let span = yield_data.span;
|
||||||
|
|
||||||
|
@ -429,7 +430,13 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h
|
||||||
};
|
};
|
||||||
let data =
|
let data =
|
||||||
YieldData { span, expr_and_pat_count: visitor.expr_and_pat_count, source: *source };
|
YieldData { span, expr_and_pat_count: visitor.expr_and_pat_count, source: *source };
|
||||||
visitor.scope_tree.yield_in_scope.insert(scope, data);
|
match visitor.scope_tree.yield_in_scope.get_mut(&scope) {
|
||||||
|
Some(yields) => yields.push(data),
|
||||||
|
None => {
|
||||||
|
visitor.scope_tree.yield_in_scope.insert(scope, vec![data]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if visitor.pessimistic_yield {
|
if visitor.pessimistic_yield {
|
||||||
debug!("resolve_expr in pessimistic_yield - marking scope {:?} for fixup", scope);
|
debug!("resolve_expr in pessimistic_yield - marking scope {:?} for fixup", scope);
|
||||||
visitor.fixup_scopes.push(scope);
|
visitor.fixup_scopes.push(scope);
|
||||||
|
|
|
@ -761,7 +761,9 @@ impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for DefId {
|
||||||
// If we get to this point, then all of the query inputs were green,
|
// If we get to this point, then all of the query inputs were green,
|
||||||
// which means that the definition with this hash is guaranteed to
|
// which means that the definition with this hash is guaranteed to
|
||||||
// still exist in the current compilation session.
|
// still exist in the current compilation session.
|
||||||
Ok(d.tcx().def_path_hash_to_def_id(def_path_hash))
|
Ok(d.tcx().def_path_hash_to_def_id(def_path_hash, &mut || {
|
||||||
|
panic!("Failed to convert DefPathHash {:?}", def_path_hash)
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2517,6 +2517,10 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
||||||
self.visit_expr(elem);
|
self.visit_expr(elem);
|
||||||
self.resolve_anon_const(ct, IsRepeatExpr::Yes);
|
self.resolve_anon_const(ct, IsRepeatExpr::Yes);
|
||||||
}
|
}
|
||||||
|
ExprKind::Index(ref elem, ref idx) => {
|
||||||
|
self.resolve_expr(elem, Some(expr));
|
||||||
|
self.visit_expr(idx);
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
visit::walk_expr(self, expr);
|
visit::walk_expr(self, expr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -970,7 +970,13 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
|
||||||
};
|
};
|
||||||
|
|
||||||
match (res, source) {
|
match (res, source) {
|
||||||
(Res::Def(DefKind::Macro(MacroKind::Bang), _), _) => {
|
(
|
||||||
|
Res::Def(DefKind::Macro(MacroKind::Bang), _),
|
||||||
|
PathSource::Expr(Some(Expr {
|
||||||
|
kind: ExprKind::Index(..) | ExprKind::Call(..), ..
|
||||||
|
}))
|
||||||
|
| PathSource::Struct,
|
||||||
|
) => {
|
||||||
err.span_label(span, fallback_label);
|
err.span_label(span, fallback_label);
|
||||||
err.span_suggestion_verbose(
|
err.span_suggestion_verbose(
|
||||||
span.shrink_to_hi(),
|
span.shrink_to_hi(),
|
||||||
|
@ -982,6 +988,9 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
|
||||||
err.note("if you want the `try` keyword, you need Rust 2018 or later");
|
err.note("if you want the `try` keyword, you need Rust 2018 or later");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
(Res::Def(DefKind::Macro(MacroKind::Bang), _), _) => {
|
||||||
|
err.span_label(span, fallback_label);
|
||||||
|
}
|
||||||
(Res::Def(DefKind::TyAlias, def_id), PathSource::Trait(_)) => {
|
(Res::Def(DefKind::TyAlias, def_id), PathSource::Trait(_)) => {
|
||||||
err.span_label(span, "type aliases cannot be used as traits");
|
err.span_label(span, "type aliases cannot be used as traits");
|
||||||
if self.r.session.is_nightly_build() {
|
if self.r.session.is_nightly_build() {
|
||||||
|
|
|
@ -12,9 +12,9 @@ rustc_hir = { path = "../rustc_hir" }
|
||||||
rustc_index = { path = "../rustc_index" }
|
rustc_index = { path = "../rustc_index" }
|
||||||
rustc_ast = { path = "../rustc_ast" }
|
rustc_ast = { path = "../rustc_ast" }
|
||||||
rustc_span = { path = "../rustc_span" }
|
rustc_span = { path = "../rustc_span" }
|
||||||
chalk-ir = "0.75.0"
|
chalk-ir = "0.76.0"
|
||||||
chalk-engine = "0.75.0"
|
chalk-engine = "0.76.0"
|
||||||
chalk-solve = "0.75.0"
|
chalk-solve = "0.76.0"
|
||||||
smallvec = { version = "1.6.1", features = ["union", "may_dangle"] }
|
smallvec = { version = "1.6.1", features = ["union", "may_dangle"] }
|
||||||
rustc_infer = { path = "../rustc_infer" }
|
rustc_infer = { path = "../rustc_infer" }
|
||||||
rustc_trait_selection = { path = "../rustc_trait_selection" }
|
rustc_trait_selection = { path = "../rustc_trait_selection" }
|
||||||
|
|
|
@ -15,6 +15,7 @@ rustc_middle = { path = "../rustc_middle" }
|
||||||
rustc_attr = { path = "../rustc_attr" }
|
rustc_attr = { path = "../rustc_attr" }
|
||||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||||
rustc_errors = { path = "../rustc_errors" }
|
rustc_errors = { path = "../rustc_errors" }
|
||||||
|
rustc_graphviz = { path = "../rustc_graphviz" }
|
||||||
rustc_hir = { path = "../rustc_hir" }
|
rustc_hir = { path = "../rustc_hir" }
|
||||||
rustc_hir_pretty = { path = "../rustc_hir_pretty" }
|
rustc_hir_pretty = { path = "../rustc_hir_pretty" }
|
||||||
rustc_target = { path = "../rustc_target" }
|
rustc_target = { path = "../rustc_target" }
|
||||||
|
@ -27,3 +28,4 @@ rustc_infer = { path = "../rustc_infer" }
|
||||||
rustc_trait_selection = { path = "../rustc_trait_selection" }
|
rustc_trait_selection = { path = "../rustc_trait_selection" }
|
||||||
rustc_ty_utils = { path = "../rustc_ty_utils" }
|
rustc_ty_utils = { path = "../rustc_ty_utils" }
|
||||||
rustc_lint = { path = "../rustc_lint" }
|
rustc_lint = { path = "../rustc_lint" }
|
||||||
|
rustc_serialize = { path = "../rustc_serialize" }
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
//! is calculated in `rustc_const_eval::transform::generator` and may be a subset of the
|
//! is calculated in `rustc_const_eval::transform::generator` and may be a subset of the
|
||||||
//! types computed here.
|
//! types computed here.
|
||||||
|
|
||||||
|
use self::drop_ranges::DropRanges;
|
||||||
use super::FnCtxt;
|
use super::FnCtxt;
|
||||||
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
|
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
|
||||||
use rustc_errors::pluralize;
|
use rustc_errors::pluralize;
|
||||||
|
@ -19,6 +20,8 @@ use rustc_span::Span;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
|
mod drop_ranges;
|
||||||
|
|
||||||
struct InteriorVisitor<'a, 'tcx> {
|
struct InteriorVisitor<'a, 'tcx> {
|
||||||
fcx: &'a FnCtxt<'a, 'tcx>,
|
fcx: &'a FnCtxt<'a, 'tcx>,
|
||||||
types: FxIndexSet<ty::GeneratorInteriorTypeCause<'tcx>>,
|
types: FxIndexSet<ty::GeneratorInteriorTypeCause<'tcx>>,
|
||||||
|
@ -34,6 +37,7 @@ struct InteriorVisitor<'a, 'tcx> {
|
||||||
guard_bindings: SmallVec<[SmallVec<[HirId; 4]>; 1]>,
|
guard_bindings: SmallVec<[SmallVec<[HirId; 4]>; 1]>,
|
||||||
guard_bindings_set: HirIdSet,
|
guard_bindings_set: HirIdSet,
|
||||||
linted_values: HirIdSet,
|
linted_values: HirIdSet,
|
||||||
|
drop_ranges: DropRanges,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> {
|
impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> {
|
||||||
|
@ -48,9 +52,11 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> {
|
||||||
) {
|
) {
|
||||||
use rustc_span::DUMMY_SP;
|
use rustc_span::DUMMY_SP;
|
||||||
|
|
||||||
|
let ty = self.fcx.resolve_vars_if_possible(ty);
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
"generator_interior: attempting to record type {:?} {:?} {:?} {:?}",
|
"attempting to record type ty={:?}; hir_id={:?}; scope={:?}; expr={:?}; source_span={:?}; expr_count={:?}",
|
||||||
ty, scope, expr, source_span
|
ty, hir_id, scope, expr, source_span, self.expr_count,
|
||||||
);
|
);
|
||||||
|
|
||||||
let live_across_yield = scope
|
let live_across_yield = scope
|
||||||
|
@ -63,21 +69,27 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> {
|
||||||
//
|
//
|
||||||
// See the mega-comment at `yield_in_scope` for a proof.
|
// See the mega-comment at `yield_in_scope` for a proof.
|
||||||
|
|
||||||
debug!(
|
yield_data
|
||||||
"comparing counts yield: {} self: {}, source_span = {:?}",
|
.iter()
|
||||||
yield_data.expr_and_pat_count, self.expr_count, source_span
|
.find(|yield_data| {
|
||||||
);
|
debug!(
|
||||||
|
"comparing counts yield: {} self: {}, source_span = {:?}",
|
||||||
|
yield_data.expr_and_pat_count, self.expr_count, source_span
|
||||||
|
);
|
||||||
|
|
||||||
// If it is a borrowing happening in the guard,
|
if self.drop_ranges.is_dropped_at(hir_id, yield_data.expr_and_pat_count)
|
||||||
// it needs to be recorded regardless because they
|
{
|
||||||
// do live across this yield point.
|
debug!("value is dropped at yield point; not recording");
|
||||||
if guard_borrowing_from_pattern
|
return false;
|
||||||
|| yield_data.expr_and_pat_count >= self.expr_count
|
}
|
||||||
{
|
|
||||||
Some(yield_data)
|
// If it is a borrowing happening in the guard,
|
||||||
} else {
|
// it needs to be recorded regardless because they
|
||||||
None
|
// do live across this yield point.
|
||||||
}
|
guard_borrowing_from_pattern
|
||||||
|
|| yield_data.expr_and_pat_count >= self.expr_count
|
||||||
|
})
|
||||||
|
.cloned()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
|
@ -85,7 +97,6 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> {
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(yield_data) = live_across_yield {
|
if let Some(yield_data) = live_across_yield {
|
||||||
let ty = self.fcx.resolve_vars_if_possible(ty);
|
|
||||||
debug!(
|
debug!(
|
||||||
"type in expr = {:?}, scope = {:?}, type = {:?}, count = {}, yield_span = {:?}",
|
"type in expr = {:?}, scope = {:?}, type = {:?}, count = {}, yield_span = {:?}",
|
||||||
expr, scope, ty, self.expr_count, yield_data.span
|
expr, scope, ty, self.expr_count, yield_data.span
|
||||||
|
@ -154,7 +165,6 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> {
|
||||||
self.expr_count,
|
self.expr_count,
|
||||||
expr.map(|e| e.span)
|
expr.map(|e| e.span)
|
||||||
);
|
);
|
||||||
let ty = self.fcx.resolve_vars_if_possible(ty);
|
|
||||||
if let Some((unresolved_type, unresolved_type_span)) =
|
if let Some((unresolved_type, unresolved_type_span)) =
|
||||||
self.fcx.unresolved_type_vars(&ty)
|
self.fcx.unresolved_type_vars(&ty)
|
||||||
{
|
{
|
||||||
|
@ -186,6 +196,7 @@ pub fn resolve_interior<'a, 'tcx>(
|
||||||
guard_bindings: <_>::default(),
|
guard_bindings: <_>::default(),
|
||||||
guard_bindings_set: <_>::default(),
|
guard_bindings_set: <_>::default(),
|
||||||
linted_values: <_>::default(),
|
linted_values: <_>::default(),
|
||||||
|
drop_ranges: drop_ranges::compute_drop_ranges(fcx, def_id, body),
|
||||||
};
|
};
|
||||||
intravisit::walk_body(&mut visitor, body);
|
intravisit::walk_body(&mut visitor, body);
|
||||||
|
|
||||||
|
@ -313,6 +324,7 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {
|
||||||
|
|
||||||
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
|
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
|
||||||
let mut guard_borrowing_from_pattern = false;
|
let mut guard_borrowing_from_pattern = false;
|
||||||
|
|
||||||
match &expr.kind {
|
match &expr.kind {
|
||||||
ExprKind::Call(callee, args) => match &callee.kind {
|
ExprKind::Call(callee, args) => match &callee.kind {
|
||||||
ExprKind::Path(qpath) => {
|
ExprKind::Path(qpath) => {
|
||||||
|
|
|
@ -0,0 +1,269 @@
|
||||||
|
//! Drop range analysis finds the portions of the tree where a value is guaranteed to be dropped
|
||||||
|
//! (i.e. moved, uninitialized, etc.). This is used to exclude the types of those values from the
|
||||||
|
//! generator type. See `InteriorVisitor::record` for where the results of this analysis are used.
|
||||||
|
//!
|
||||||
|
//! There are three phases to this analysis:
|
||||||
|
//! 1. Use `ExprUseVisitor` to identify the interesting values that are consumed and borrowed.
|
||||||
|
//! 2. Use `DropRangeVisitor` to find where the interesting values are dropped or reinitialized,
|
||||||
|
//! and also build a control flow graph.
|
||||||
|
//! 3. Use `DropRanges::propagate_to_fixpoint` to flow the dropped/reinitialized information through
|
||||||
|
//! the CFG and find the exact points where we know a value is definitely dropped.
|
||||||
|
//!
|
||||||
|
//! The end result is a data structure that maps the post-order index of each node in the HIR tree
|
||||||
|
//! to a set of values that are known to be dropped at that location.
|
||||||
|
|
||||||
|
use self::cfg_build::build_control_flow_graph;
|
||||||
|
use self::record_consumed_borrow::find_consumed_and_borrowed;
|
||||||
|
use crate::check::FnCtxt;
|
||||||
|
use hir::def_id::DefId;
|
||||||
|
use hir::{Body, HirId, HirIdMap, Node};
|
||||||
|
use rustc_data_structures::fx::FxHashMap;
|
||||||
|
use rustc_hir as hir;
|
||||||
|
use rustc_index::bit_set::BitSet;
|
||||||
|
use rustc_index::vec::IndexVec;
|
||||||
|
use rustc_middle::hir::map::Map;
|
||||||
|
use rustc_middle::hir::place::{PlaceBase, PlaceWithHirId};
|
||||||
|
use rustc_middle::ty;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
mod cfg_build;
|
||||||
|
mod cfg_propagate;
|
||||||
|
mod cfg_visualize;
|
||||||
|
mod record_consumed_borrow;
|
||||||
|
|
||||||
|
pub fn compute_drop_ranges<'a, 'tcx>(
|
||||||
|
fcx: &'a FnCtxt<'a, 'tcx>,
|
||||||
|
def_id: DefId,
|
||||||
|
body: &'tcx Body<'tcx>,
|
||||||
|
) -> DropRanges {
|
||||||
|
let consumed_borrowed_places = find_consumed_and_borrowed(fcx, def_id, body);
|
||||||
|
|
||||||
|
let num_exprs = fcx.tcx.region_scope_tree(def_id).body_expr_count(body.id()).unwrap_or(0);
|
||||||
|
let mut drop_ranges = build_control_flow_graph(
|
||||||
|
fcx.tcx.hir(),
|
||||||
|
fcx.tcx,
|
||||||
|
&fcx.typeck_results.borrow(),
|
||||||
|
consumed_borrowed_places,
|
||||||
|
body,
|
||||||
|
num_exprs,
|
||||||
|
);
|
||||||
|
|
||||||
|
drop_ranges.propagate_to_fixpoint();
|
||||||
|
|
||||||
|
DropRanges { tracked_value_map: drop_ranges.tracked_value_map, nodes: drop_ranges.nodes }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Applies `f` to consumable node in the HIR subtree pointed to by `place`.
|
||||||
|
///
|
||||||
|
/// This includes the place itself, and if the place is a reference to a local
|
||||||
|
/// variable then `f` is also called on the HIR node for that variable as well.
|
||||||
|
///
|
||||||
|
/// For example, if `place` points to `foo()`, then `f` is called once for the
|
||||||
|
/// result of `foo`. On the other hand, if `place` points to `x` then `f` will
|
||||||
|
/// be called both on the `ExprKind::Path` node that represents the expression
|
||||||
|
/// as well as the HirId of the local `x` itself.
|
||||||
|
fn for_each_consumable<'tcx>(hir: Map<'tcx>, place: TrackedValue, mut f: impl FnMut(TrackedValue)) {
|
||||||
|
f(place);
|
||||||
|
let node = hir.find(place.hir_id());
|
||||||
|
if let Some(Node::Expr(expr)) = node {
|
||||||
|
match expr.kind {
|
||||||
|
hir::ExprKind::Path(hir::QPath::Resolved(
|
||||||
|
_,
|
||||||
|
hir::Path { res: hir::def::Res::Local(hir_id), .. },
|
||||||
|
)) => {
|
||||||
|
f(TrackedValue::Variable(*hir_id));
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rustc_index::newtype_index! {
|
||||||
|
pub struct PostOrderId {
|
||||||
|
DEBUG_FORMAT = "id({})",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rustc_index::newtype_index! {
|
||||||
|
pub struct TrackedValueIndex {
|
||||||
|
DEBUG_FORMAT = "hidx({})",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Identifies a value whose drop state we need to track.
|
||||||
|
#[derive(PartialEq, Eq, Hash, Debug, Clone, Copy)]
|
||||||
|
enum TrackedValue {
|
||||||
|
/// Represents a named variable, such as a let binding, parameter, or upvar.
|
||||||
|
///
|
||||||
|
/// The HirId points to the variable's definition site.
|
||||||
|
Variable(HirId),
|
||||||
|
/// A value produced as a result of an expression.
|
||||||
|
///
|
||||||
|
/// The HirId points to the expression that returns this value.
|
||||||
|
Temporary(HirId),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TrackedValue {
|
||||||
|
fn hir_id(&self) -> HirId {
|
||||||
|
match self {
|
||||||
|
TrackedValue::Variable(hir_id) | TrackedValue::Temporary(hir_id) => *hir_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a reason why we might not be able to convert a HirId or Place
|
||||||
|
/// into a tracked value.
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum TrackedValueConversionError {
|
||||||
|
/// Place projects are not currently supported.
|
||||||
|
///
|
||||||
|
/// The reasoning around these is kind of subtle, so we choose to be more
|
||||||
|
/// conservative around these for now. There is not reason in theory we
|
||||||
|
/// cannot support these, we just have not implemented it yet.
|
||||||
|
PlaceProjectionsNotSupported,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&PlaceWithHirId<'_>> for TrackedValue {
|
||||||
|
type Error = TrackedValueConversionError;
|
||||||
|
|
||||||
|
fn try_from(place_with_id: &PlaceWithHirId<'_>) -> Result<Self, Self::Error> {
|
||||||
|
if !place_with_id.place.projections.is_empty() {
|
||||||
|
debug!(
|
||||||
|
"TrackedValue from PlaceWithHirId: {:?} has projections, which are not supported.",
|
||||||
|
place_with_id
|
||||||
|
);
|
||||||
|
return Err(TrackedValueConversionError::PlaceProjectionsNotSupported);
|
||||||
|
}
|
||||||
|
|
||||||
|
match place_with_id.place.base {
|
||||||
|
PlaceBase::Rvalue | PlaceBase::StaticItem => {
|
||||||
|
Ok(TrackedValue::Temporary(place_with_id.hir_id))
|
||||||
|
}
|
||||||
|
PlaceBase::Local(hir_id)
|
||||||
|
| PlaceBase::Upvar(ty::UpvarId { var_path: ty::UpvarPath { hir_id }, .. }) => {
|
||||||
|
Ok(TrackedValue::Variable(hir_id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DropRanges {
|
||||||
|
tracked_value_map: FxHashMap<TrackedValue, TrackedValueIndex>,
|
||||||
|
nodes: IndexVec<PostOrderId, NodeInfo>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DropRanges {
|
||||||
|
pub fn is_dropped_at(&self, hir_id: HirId, location: usize) -> bool {
|
||||||
|
self.tracked_value_map
|
||||||
|
.get(&TrackedValue::Temporary(hir_id))
|
||||||
|
.or(self.tracked_value_map.get(&TrackedValue::Variable(hir_id)))
|
||||||
|
.cloned()
|
||||||
|
.map_or(false, |tracked_value_id| {
|
||||||
|
self.expect_node(location.into()).drop_state.contains(tracked_value_id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the NodeInfo for a node, panicking if it does not exist
|
||||||
|
fn expect_node(&self, id: PostOrderId) -> &NodeInfo {
|
||||||
|
&self.nodes[id]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tracks information needed to compute drop ranges.
|
||||||
|
struct DropRangesBuilder {
|
||||||
|
/// The core of DropRangesBuilder is a set of nodes, which each represent
|
||||||
|
/// one expression. We primarily refer to them by their index in a
|
||||||
|
/// post-order traversal of the HIR tree, since this is what
|
||||||
|
/// generator_interior uses to talk about yield positions.
|
||||||
|
///
|
||||||
|
/// This IndexVec keeps the relevant details for each node. See the
|
||||||
|
/// NodeInfo struct for more details, but this information includes things
|
||||||
|
/// such as the set of control-flow successors, which variables are dropped
|
||||||
|
/// or reinitialized, and whether each variable has been inferred to be
|
||||||
|
/// known-dropped or potentially reintiialized at each point.
|
||||||
|
nodes: IndexVec<PostOrderId, NodeInfo>,
|
||||||
|
/// We refer to values whose drop state we are tracking by the HirId of
|
||||||
|
/// where they are defined. Within a NodeInfo, however, we store the
|
||||||
|
/// drop-state in a bit vector indexed by a HirIdIndex
|
||||||
|
/// (see NodeInfo::drop_state). The hir_id_map field stores the mapping
|
||||||
|
/// from HirIds to the HirIdIndex that is used to represent that value in
|
||||||
|
/// bitvector.
|
||||||
|
tracked_value_map: FxHashMap<TrackedValue, TrackedValueIndex>,
|
||||||
|
|
||||||
|
/// When building the control flow graph, we don't always know the
|
||||||
|
/// post-order index of the target node at the point we encounter it.
|
||||||
|
/// For example, this happens with break and continue. In those cases,
|
||||||
|
/// we store a pair of the PostOrderId of the source and the HirId
|
||||||
|
/// of the target. Once we have gathered all of these edges, we make a
|
||||||
|
/// pass over the set of deferred edges (see process_deferred_edges in
|
||||||
|
/// cfg_build.rs), look up the PostOrderId for the target (since now the
|
||||||
|
/// post-order index for all nodes is known), and add missing control flow
|
||||||
|
/// edges.
|
||||||
|
deferred_edges: Vec<(PostOrderId, HirId)>,
|
||||||
|
/// This maps HirIds of expressions to their post-order index. It is
|
||||||
|
/// used in process_deferred_edges to correctly add back-edges.
|
||||||
|
post_order_map: HirIdMap<PostOrderId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for DropRangesBuilder {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("DropRanges")
|
||||||
|
.field("hir_id_map", &self.tracked_value_map)
|
||||||
|
.field("post_order_maps", &self.post_order_map)
|
||||||
|
.field("nodes", &self.nodes.iter_enumerated().collect::<BTreeMap<_, _>>())
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// DropRanges keeps track of what values are definitely dropped at each point in the code.
|
||||||
|
///
|
||||||
|
/// Values of interest are defined by the hir_id of their place. Locations in code are identified
|
||||||
|
/// by their index in the post-order traversal. At its core, DropRanges maps
|
||||||
|
/// (hir_id, post_order_id) -> bool, where a true value indicates that the value is definitely
|
||||||
|
/// dropped at the point of the node identified by post_order_id.
|
||||||
|
impl DropRangesBuilder {
|
||||||
|
/// Returns the number of values (hir_ids) that are tracked
|
||||||
|
fn num_values(&self) -> usize {
|
||||||
|
self.tracked_value_map.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn node_mut(&mut self, id: PostOrderId) -> &mut NodeInfo {
|
||||||
|
let size = self.num_values();
|
||||||
|
self.nodes.ensure_contains_elem(id, || NodeInfo::new(size));
|
||||||
|
&mut self.nodes[id]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_control_edge(&mut self, from: PostOrderId, to: PostOrderId) {
|
||||||
|
trace!("adding control edge from {:?} to {:?}", from, to);
|
||||||
|
self.node_mut(from.into()).successors.push(to.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct NodeInfo {
|
||||||
|
/// IDs of nodes that can follow this one in the control flow
|
||||||
|
///
|
||||||
|
/// If the vec is empty, then control proceeds to the next node.
|
||||||
|
successors: Vec<PostOrderId>,
|
||||||
|
|
||||||
|
/// List of hir_ids that are dropped by this node.
|
||||||
|
drops: Vec<TrackedValueIndex>,
|
||||||
|
|
||||||
|
/// List of hir_ids that are reinitialized by this node.
|
||||||
|
reinits: Vec<TrackedValueIndex>,
|
||||||
|
|
||||||
|
/// Set of values that are definitely dropped at this point.
|
||||||
|
drop_state: BitSet<TrackedValueIndex>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NodeInfo {
|
||||||
|
fn new(num_values: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
successors: vec![],
|
||||||
|
drops: vec![],
|
||||||
|
reinits: vec![],
|
||||||
|
drop_state: BitSet::new_filled(num_values),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,473 @@
|
||||||
|
use super::{
|
||||||
|
for_each_consumable, record_consumed_borrow::ConsumedAndBorrowedPlaces, DropRangesBuilder,
|
||||||
|
NodeInfo, PostOrderId, TrackedValue, TrackedValueIndex,
|
||||||
|
};
|
||||||
|
use hir::{
|
||||||
|
intravisit::{self, Visitor},
|
||||||
|
Body, Expr, ExprKind, Guard, HirId,
|
||||||
|
};
|
||||||
|
use rustc_data_structures::fx::FxHashMap;
|
||||||
|
use rustc_hir as hir;
|
||||||
|
use rustc_index::vec::IndexVec;
|
||||||
|
use rustc_middle::{
|
||||||
|
hir::map::Map,
|
||||||
|
ty::{TyCtxt, TypeckResults},
|
||||||
|
};
|
||||||
|
use std::mem::swap;
|
||||||
|
|
||||||
|
/// Traverses the body to find the control flow graph and locations for the
|
||||||
|
/// relevant places are dropped or reinitialized.
|
||||||
|
///
|
||||||
|
/// The resulting structure still needs to be iterated to a fixed point, which
|
||||||
|
/// can be done with propagate_to_fixpoint in cfg_propagate.
|
||||||
|
pub(super) fn build_control_flow_graph<'tcx>(
|
||||||
|
hir: Map<'tcx>,
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
typeck_results: &TypeckResults<'tcx>,
|
||||||
|
consumed_borrowed_places: ConsumedAndBorrowedPlaces,
|
||||||
|
body: &'tcx Body<'tcx>,
|
||||||
|
num_exprs: usize,
|
||||||
|
) -> DropRangesBuilder {
|
||||||
|
let mut drop_range_visitor =
|
||||||
|
DropRangeVisitor::new(hir, tcx, typeck_results, consumed_borrowed_places, num_exprs);
|
||||||
|
intravisit::walk_body(&mut drop_range_visitor, body);
|
||||||
|
|
||||||
|
drop_range_visitor.drop_ranges.process_deferred_edges();
|
||||||
|
|
||||||
|
drop_range_visitor.drop_ranges
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This struct is used to gather the information for `DropRanges` to determine the regions of the
|
||||||
|
/// HIR tree for which a value is dropped.
|
||||||
|
///
|
||||||
|
/// We are interested in points where a variables is dropped or initialized, and the control flow
|
||||||
|
/// of the code. We identify locations in code by their post-order traversal index, so it is
|
||||||
|
/// important for this traversal to match that in `RegionResolutionVisitor` and `InteriorVisitor`.
|
||||||
|
///
|
||||||
|
/// We make several simplifying assumptions, with the goal of being more conservative than
|
||||||
|
/// necessary rather than less conservative (since being less conservative is unsound, but more
|
||||||
|
/// conservative is still safe). These assumptions are:
|
||||||
|
///
|
||||||
|
/// 1. Moving a variable `a` counts as a move of the whole variable.
|
||||||
|
/// 2. Moving a partial path like `a.b.c` is ignored.
|
||||||
|
/// 3. Reinitializing through a field (e.g. `a.b.c = 5`) counds as a reinitialization of all of
|
||||||
|
/// `a`.
|
||||||
|
///
|
||||||
|
/// Some examples:
|
||||||
|
///
|
||||||
|
/// Rule 1:
|
||||||
|
/// ```rust
|
||||||
|
/// let mut a = (vec![0], vec![0]);
|
||||||
|
/// drop(a);
|
||||||
|
/// // `a` is not considered initialized.
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Rule 2:
|
||||||
|
/// ```rust
|
||||||
|
/// let mut a = (vec![0], vec![0]);
|
||||||
|
/// drop(a.0);
|
||||||
|
/// drop(a.1);
|
||||||
|
/// // `a` is still considered initialized.
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Rule 3:
|
||||||
|
/// ```rust
|
||||||
|
/// let mut a = (vec![0], vec![0]);
|
||||||
|
/// drop(a);
|
||||||
|
/// a.1 = vec![1];
|
||||||
|
/// // all of `a` is considered initialized
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
struct DropRangeVisitor<'a, 'tcx> {
|
||||||
|
hir: Map<'tcx>,
|
||||||
|
places: ConsumedAndBorrowedPlaces,
|
||||||
|
drop_ranges: DropRangesBuilder,
|
||||||
|
expr_index: PostOrderId,
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
typeck_results: &'a TypeckResults<'tcx>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> {
|
||||||
|
fn new(
|
||||||
|
hir: Map<'tcx>,
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
typeck_results: &'a TypeckResults<'tcx>,
|
||||||
|
places: ConsumedAndBorrowedPlaces,
|
||||||
|
num_exprs: usize,
|
||||||
|
) -> Self {
|
||||||
|
debug!("consumed_places: {:?}", places.consumed);
|
||||||
|
let drop_ranges = DropRangesBuilder::new(
|
||||||
|
places.consumed.iter().flat_map(|(_, places)| places.iter().cloned()),
|
||||||
|
hir,
|
||||||
|
num_exprs,
|
||||||
|
);
|
||||||
|
Self { hir, places, drop_ranges, expr_index: PostOrderId::from_u32(0), typeck_results, tcx }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn record_drop(&mut self, value: TrackedValue) {
|
||||||
|
if self.places.borrowed.contains(&value) {
|
||||||
|
debug!("not marking {:?} as dropped because it is borrowed at some point", value);
|
||||||
|
} else {
|
||||||
|
debug!("marking {:?} as dropped at {:?}", value, self.expr_index);
|
||||||
|
let count = self.expr_index;
|
||||||
|
self.drop_ranges.drop_at(value, count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ExprUseVisitor's consume callback doesn't go deep enough for our purposes in all
|
||||||
|
/// expressions. This method consumes a little deeper into the expression when needed.
|
||||||
|
fn consume_expr(&mut self, expr: &hir::Expr<'_>) {
|
||||||
|
debug!("consuming expr {:?}, count={:?}", expr.hir_id, self.expr_index);
|
||||||
|
let places = self
|
||||||
|
.places
|
||||||
|
.consumed
|
||||||
|
.get(&expr.hir_id)
|
||||||
|
.map_or(vec![], |places| places.iter().cloned().collect());
|
||||||
|
for place in places {
|
||||||
|
for_each_consumable(self.hir, place, |value| self.record_drop(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Marks an expression as being reinitialized.
|
||||||
|
///
|
||||||
|
/// Note that we always approximated on the side of things being more
|
||||||
|
/// initialized than they actually are, as opposed to less. In cases such
|
||||||
|
/// as `x.y = ...`, we would consider all of `x` as being initialized
|
||||||
|
/// instead of just the `y` field.
|
||||||
|
///
|
||||||
|
/// This is because it is always safe to consider something initialized
|
||||||
|
/// even when it is not, but the other way around will cause problems.
|
||||||
|
///
|
||||||
|
/// In the future, we will hopefully tighten up these rules to be more
|
||||||
|
/// precise.
|
||||||
|
fn reinit_expr(&mut self, expr: &hir::Expr<'_>) {
|
||||||
|
// Walk the expression to find the base. For example, in an expression
|
||||||
|
// like `*a[i].x`, we want to find the `a` and mark that as
|
||||||
|
// reinitialized.
|
||||||
|
match expr.kind {
|
||||||
|
ExprKind::Path(hir::QPath::Resolved(
|
||||||
|
_,
|
||||||
|
hir::Path { res: hir::def::Res::Local(hir_id), .. },
|
||||||
|
)) => {
|
||||||
|
// This is the base case, where we have found an actual named variable.
|
||||||
|
|
||||||
|
let location = self.expr_index;
|
||||||
|
debug!("reinitializing {:?} at {:?}", hir_id, location);
|
||||||
|
self.drop_ranges.reinit_at(TrackedValue::Variable(*hir_id), location);
|
||||||
|
}
|
||||||
|
|
||||||
|
ExprKind::Field(base, _) => self.reinit_expr(base),
|
||||||
|
|
||||||
|
// Most expressions do not refer to something where we need to track
|
||||||
|
// reinitializations.
|
||||||
|
//
|
||||||
|
// Some of these may be interesting in the future
|
||||||
|
ExprKind::Path(..)
|
||||||
|
| ExprKind::Box(..)
|
||||||
|
| ExprKind::ConstBlock(..)
|
||||||
|
| ExprKind::Array(..)
|
||||||
|
| ExprKind::Call(..)
|
||||||
|
| ExprKind::MethodCall(..)
|
||||||
|
| ExprKind::Tup(..)
|
||||||
|
| ExprKind::Binary(..)
|
||||||
|
| ExprKind::Unary(..)
|
||||||
|
| ExprKind::Lit(..)
|
||||||
|
| ExprKind::Cast(..)
|
||||||
|
| ExprKind::Type(..)
|
||||||
|
| ExprKind::DropTemps(..)
|
||||||
|
| ExprKind::Let(..)
|
||||||
|
| ExprKind::If(..)
|
||||||
|
| ExprKind::Loop(..)
|
||||||
|
| ExprKind::Match(..)
|
||||||
|
| ExprKind::Closure(..)
|
||||||
|
| ExprKind::Block(..)
|
||||||
|
| ExprKind::Assign(..)
|
||||||
|
| ExprKind::AssignOp(..)
|
||||||
|
| ExprKind::Index(..)
|
||||||
|
| ExprKind::AddrOf(..)
|
||||||
|
| ExprKind::Break(..)
|
||||||
|
| ExprKind::Continue(..)
|
||||||
|
| ExprKind::Ret(..)
|
||||||
|
| ExprKind::InlineAsm(..)
|
||||||
|
| ExprKind::Struct(..)
|
||||||
|
| ExprKind::Repeat(..)
|
||||||
|
| ExprKind::Yield(..)
|
||||||
|
| ExprKind::Err => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// For an expression with an uninhabited return type (e.g. a function that returns !),
|
||||||
|
/// this adds a self edge to to the CFG to model the fact that the function does not
|
||||||
|
/// return.
|
||||||
|
fn handle_uninhabited_return(&mut self, expr: &Expr<'tcx>) {
|
||||||
|
let ty = self.typeck_results.expr_ty(expr);
|
||||||
|
let ty = self.tcx.erase_regions(ty);
|
||||||
|
let m = self.tcx.parent_module(expr.hir_id).to_def_id();
|
||||||
|
let param_env = self.tcx.param_env(m.expect_local());
|
||||||
|
if self.tcx.is_ty_uninhabited_from(m, ty, param_env) {
|
||||||
|
// This function will not return. We model this fact as an infinite loop.
|
||||||
|
self.drop_ranges.add_control_edge(self.expr_index + 1, self.expr_index + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> {
|
||||||
|
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
|
||||||
|
let mut reinit = None;
|
||||||
|
match expr.kind {
|
||||||
|
ExprKind::Assign(lhs, rhs, _) => {
|
||||||
|
self.visit_expr(lhs);
|
||||||
|
self.visit_expr(rhs);
|
||||||
|
|
||||||
|
reinit = Some(lhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
ExprKind::If(test, if_true, if_false) => {
|
||||||
|
self.visit_expr(test);
|
||||||
|
|
||||||
|
let fork = self.expr_index;
|
||||||
|
|
||||||
|
self.drop_ranges.add_control_edge(fork, self.expr_index + 1);
|
||||||
|
self.visit_expr(if_true);
|
||||||
|
let true_end = self.expr_index;
|
||||||
|
|
||||||
|
self.drop_ranges.add_control_edge(fork, self.expr_index + 1);
|
||||||
|
if let Some(if_false) = if_false {
|
||||||
|
self.visit_expr(if_false);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.drop_ranges.add_control_edge(true_end, self.expr_index + 1);
|
||||||
|
}
|
||||||
|
ExprKind::Match(scrutinee, arms, ..) => {
|
||||||
|
// We walk through the match expression almost like a chain of if expressions.
|
||||||
|
// Here's a diagram to follow along with:
|
||||||
|
//
|
||||||
|
// ┌─┐
|
||||||
|
// match │A│ {
|
||||||
|
// ┌───┴─┘
|
||||||
|
// │
|
||||||
|
// ┌▼┌───►┌─┐ ┌─┐
|
||||||
|
// │B│ if │C│ =>│D│,
|
||||||
|
// └─┘ ├─┴──►└─┴──────┐
|
||||||
|
// ┌──┘ │
|
||||||
|
// ┌──┘ │
|
||||||
|
// │ │
|
||||||
|
// ┌▼┌───►┌─┐ ┌─┐ │
|
||||||
|
// │E│ if │F│ =>│G│, │
|
||||||
|
// └─┘ ├─┴──►└─┴┐ │
|
||||||
|
// │ │ │
|
||||||
|
// } ▼ ▼ │
|
||||||
|
// ┌─┐◄───────────────────┘
|
||||||
|
// │H│
|
||||||
|
// └─┘
|
||||||
|
//
|
||||||
|
// The order we want is that the scrutinee (A) flows into the first pattern (B),
|
||||||
|
// which flows into the guard (C). Then the guard either flows into the arm body
|
||||||
|
// (D) or into the start of the next arm (E). Finally, the body flows to the end
|
||||||
|
// of the match block (H).
|
||||||
|
//
|
||||||
|
// The subsequent arms follow the same ordering. First we go to the pattern, then
|
||||||
|
// the guard (if present, otherwise it flows straight into the body), then into
|
||||||
|
// the body and then to the end of the match expression.
|
||||||
|
//
|
||||||
|
// The comments below show which edge is being added.
|
||||||
|
self.visit_expr(scrutinee);
|
||||||
|
|
||||||
|
let (guard_exit, arm_end_ids) = arms.iter().fold(
|
||||||
|
(self.expr_index, vec![]),
|
||||||
|
|(incoming_edge, mut arm_end_ids), hir::Arm { pat, body, guard, .. }| {
|
||||||
|
// A -> B, or C -> E
|
||||||
|
self.drop_ranges.add_control_edge(incoming_edge, self.expr_index + 1);
|
||||||
|
self.visit_pat(pat);
|
||||||
|
// B -> C and E -> F are added implicitly due to the traversal order.
|
||||||
|
match guard {
|
||||||
|
Some(Guard::If(expr)) => self.visit_expr(expr),
|
||||||
|
Some(Guard::IfLet(pat, expr)) => {
|
||||||
|
self.visit_pat(pat);
|
||||||
|
self.visit_expr(expr);
|
||||||
|
}
|
||||||
|
None => (),
|
||||||
|
}
|
||||||
|
// Likewise, C -> D and F -> G are added implicitly.
|
||||||
|
|
||||||
|
// Save C, F, so we can add the other outgoing edge.
|
||||||
|
let to_next_arm = self.expr_index;
|
||||||
|
|
||||||
|
// The default edge does not get added since we also have an explicit edge,
|
||||||
|
// so we also need to add an edge to the next node as well.
|
||||||
|
//
|
||||||
|
// This adds C -> D, F -> G
|
||||||
|
self.drop_ranges.add_control_edge(self.expr_index, self.expr_index + 1);
|
||||||
|
self.visit_expr(body);
|
||||||
|
|
||||||
|
// Save the end of the body so we can add the exit edge once we know where
|
||||||
|
// the exit is.
|
||||||
|
arm_end_ids.push(self.expr_index);
|
||||||
|
|
||||||
|
// Pass C to the next iteration, as well as vec![D]
|
||||||
|
//
|
||||||
|
// On the last round through, we pass F and vec![D, G] so that we can
|
||||||
|
// add all the exit edges.
|
||||||
|
(to_next_arm, arm_end_ids)
|
||||||
|
},
|
||||||
|
);
|
||||||
|
// F -> H
|
||||||
|
self.drop_ranges.add_control_edge(guard_exit, self.expr_index + 1);
|
||||||
|
|
||||||
|
arm_end_ids.into_iter().for_each(|arm_end| {
|
||||||
|
// D -> H, G -> H
|
||||||
|
self.drop_ranges.add_control_edge(arm_end, self.expr_index + 1)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ExprKind::Loop(body, ..) => {
|
||||||
|
let loop_begin = self.expr_index + 1;
|
||||||
|
if body.stmts.is_empty() && body.expr.is_none() {
|
||||||
|
// For empty loops we won't have updated self.expr_index after visiting the
|
||||||
|
// body, meaning we'd get an edge from expr_index to expr_index + 1, but
|
||||||
|
// instead we want an edge from expr_index + 1 to expr_index + 1.
|
||||||
|
self.drop_ranges.add_control_edge(loop_begin, loop_begin);
|
||||||
|
} else {
|
||||||
|
self.visit_block(body);
|
||||||
|
self.drop_ranges.add_control_edge(self.expr_index, loop_begin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExprKind::Break(hir::Destination { target_id: Ok(target), .. }, ..)
|
||||||
|
| ExprKind::Continue(hir::Destination { target_id: Ok(target), .. }, ..) => {
|
||||||
|
self.drop_ranges.add_control_edge_hir_id(self.expr_index, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
ExprKind::Call(f, args) => {
|
||||||
|
self.visit_expr(f);
|
||||||
|
for arg in args {
|
||||||
|
self.visit_expr(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.handle_uninhabited_return(expr);
|
||||||
|
}
|
||||||
|
ExprKind::MethodCall(_, _, exprs, _) => {
|
||||||
|
for expr in exprs {
|
||||||
|
self.visit_expr(expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.handle_uninhabited_return(expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
ExprKind::AddrOf(..)
|
||||||
|
| ExprKind::Array(..)
|
||||||
|
| ExprKind::AssignOp(..)
|
||||||
|
| ExprKind::Binary(..)
|
||||||
|
| ExprKind::Block(..)
|
||||||
|
| ExprKind::Box(..)
|
||||||
|
| ExprKind::Break(..)
|
||||||
|
| ExprKind::Cast(..)
|
||||||
|
| ExprKind::Closure(..)
|
||||||
|
| ExprKind::ConstBlock(..)
|
||||||
|
| ExprKind::Continue(..)
|
||||||
|
| ExprKind::DropTemps(..)
|
||||||
|
| ExprKind::Err
|
||||||
|
| ExprKind::Field(..)
|
||||||
|
| ExprKind::Index(..)
|
||||||
|
| ExprKind::InlineAsm(..)
|
||||||
|
| ExprKind::Let(..)
|
||||||
|
| ExprKind::Lit(..)
|
||||||
|
| ExprKind::Path(..)
|
||||||
|
| ExprKind::Repeat(..)
|
||||||
|
| ExprKind::Ret(..)
|
||||||
|
| ExprKind::Struct(..)
|
||||||
|
| ExprKind::Tup(..)
|
||||||
|
| ExprKind::Type(..)
|
||||||
|
| ExprKind::Unary(..)
|
||||||
|
| ExprKind::Yield(..) => intravisit::walk_expr(self, expr),
|
||||||
|
}
|
||||||
|
|
||||||
|
self.expr_index = self.expr_index + 1;
|
||||||
|
self.drop_ranges.add_node_mapping(expr.hir_id, self.expr_index);
|
||||||
|
self.consume_expr(expr);
|
||||||
|
if let Some(expr) = reinit {
|
||||||
|
self.reinit_expr(expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) {
|
||||||
|
intravisit::walk_pat(self, pat);
|
||||||
|
|
||||||
|
// Increment expr_count here to match what InteriorVisitor expects.
|
||||||
|
self.expr_index = self.expr_index + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DropRangesBuilder {
|
||||||
|
fn new(
|
||||||
|
tracked_values: impl Iterator<Item = TrackedValue>,
|
||||||
|
hir: Map<'_>,
|
||||||
|
num_exprs: usize,
|
||||||
|
) -> Self {
|
||||||
|
let mut tracked_value_map = FxHashMap::<_, TrackedValueIndex>::default();
|
||||||
|
let mut next = <_>::from(0u32);
|
||||||
|
for value in tracked_values {
|
||||||
|
for_each_consumable(hir, value, |value| {
|
||||||
|
if !tracked_value_map.contains_key(&value) {
|
||||||
|
tracked_value_map.insert(value, next);
|
||||||
|
next = next + 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
debug!("hir_id_map: {:?}", tracked_value_map);
|
||||||
|
let num_values = tracked_value_map.len();
|
||||||
|
Self {
|
||||||
|
tracked_value_map,
|
||||||
|
nodes: IndexVec::from_fn_n(|_| NodeInfo::new(num_values), num_exprs + 1),
|
||||||
|
deferred_edges: <_>::default(),
|
||||||
|
post_order_map: <_>::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tracked_value_index(&self, tracked_value: TrackedValue) -> TrackedValueIndex {
|
||||||
|
*self.tracked_value_map.get(&tracked_value).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds an entry in the mapping from HirIds to PostOrderIds
|
||||||
|
///
|
||||||
|
/// Needed so that `add_control_edge_hir_id` can work.
|
||||||
|
fn add_node_mapping(&mut self, node_hir_id: HirId, post_order_id: PostOrderId) {
|
||||||
|
self.post_order_map.insert(node_hir_id, post_order_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Like add_control_edge, but uses a hir_id as the target.
|
||||||
|
///
|
||||||
|
/// This can be used for branches where we do not know the PostOrderId of the target yet,
|
||||||
|
/// such as when handling `break` or `continue`.
|
||||||
|
fn add_control_edge_hir_id(&mut self, from: PostOrderId, to: HirId) {
|
||||||
|
self.deferred_edges.push((from, to));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drop_at(&mut self, value: TrackedValue, location: PostOrderId) {
|
||||||
|
let value = self.tracked_value_index(value);
|
||||||
|
self.node_mut(location.into()).drops.push(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reinit_at(&mut self, value: TrackedValue, location: PostOrderId) {
|
||||||
|
let value = match self.tracked_value_map.get(&value) {
|
||||||
|
Some(value) => *value,
|
||||||
|
// If there's no value, this is never consumed and therefore is never dropped. We can
|
||||||
|
// ignore this.
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
self.node_mut(location.into()).reinits.push(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Looks up PostOrderId for any control edges added by HirId and adds a proper edge for them.
|
||||||
|
///
|
||||||
|
/// Should be called after visiting the HIR but before solving the control flow, otherwise some
|
||||||
|
/// edges will be missed.
|
||||||
|
fn process_deferred_edges(&mut self) {
|
||||||
|
let mut edges = vec![];
|
||||||
|
swap(&mut edges, &mut self.deferred_edges);
|
||||||
|
edges.into_iter().for_each(|(from, to)| {
|
||||||
|
let to = *self.post_order_map.get(&to).expect("Expression ID not found");
|
||||||
|
trace!("Adding deferred edge from {:?} to {:?}", from, to);
|
||||||
|
self.add_control_edge(from, to)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
use super::{DropRangesBuilder, PostOrderId};
|
||||||
|
use rustc_index::{bit_set::BitSet, vec::IndexVec};
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
impl DropRangesBuilder {
|
||||||
|
pub fn propagate_to_fixpoint(&mut self) {
|
||||||
|
trace!("before fixpoint: {:#?}", self);
|
||||||
|
let preds = self.compute_predecessors();
|
||||||
|
|
||||||
|
trace!("predecessors: {:#?}", preds.iter_enumerated().collect::<BTreeMap<_, _>>());
|
||||||
|
|
||||||
|
let mut new_state = BitSet::new_empty(self.num_values());
|
||||||
|
let mut changed_nodes = BitSet::new_empty(self.nodes.len());
|
||||||
|
let mut unchanged_mask = BitSet::new_filled(self.nodes.len());
|
||||||
|
changed_nodes.insert(0u32.into());
|
||||||
|
|
||||||
|
let mut propagate = || {
|
||||||
|
let mut changed = false;
|
||||||
|
unchanged_mask.insert_all();
|
||||||
|
for id in self.nodes.indices() {
|
||||||
|
trace!("processing {:?}, changed_nodes: {:?}", id, changed_nodes);
|
||||||
|
// Check if any predecessor has changed, and if not then short-circuit.
|
||||||
|
//
|
||||||
|
// We handle the start node specially, since it doesn't have any predecessors,
|
||||||
|
// but we need to start somewhere.
|
||||||
|
if match id.index() {
|
||||||
|
0 => !changed_nodes.contains(id),
|
||||||
|
_ => !preds[id].iter().any(|pred| changed_nodes.contains(*pred)),
|
||||||
|
} {
|
||||||
|
trace!("short-circuiting because none of {:?} have changed", preds[id]);
|
||||||
|
unchanged_mask.remove(id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if id.index() == 0 {
|
||||||
|
new_state.clear();
|
||||||
|
} else {
|
||||||
|
// If we are not the start node and we have no predecessors, treat
|
||||||
|
// everything as dropped because there's no way to get here anyway.
|
||||||
|
new_state.insert_all();
|
||||||
|
};
|
||||||
|
|
||||||
|
for pred in &preds[id] {
|
||||||
|
new_state.intersect(&self.nodes[*pred].drop_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
for drop in &self.nodes[id].drops {
|
||||||
|
new_state.insert(*drop);
|
||||||
|
}
|
||||||
|
|
||||||
|
for reinit in &self.nodes[id].reinits {
|
||||||
|
new_state.remove(*reinit);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.nodes[id].drop_state.intersect(&new_state) {
|
||||||
|
changed_nodes.insert(id);
|
||||||
|
changed = true;
|
||||||
|
} else {
|
||||||
|
unchanged_mask.remove(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
changed_nodes.intersect(&unchanged_mask);
|
||||||
|
changed
|
||||||
|
};
|
||||||
|
|
||||||
|
while propagate() {
|
||||||
|
trace!("drop_state changed, re-running propagation");
|
||||||
|
}
|
||||||
|
|
||||||
|
trace!("after fixpoint: {:#?}", self);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compute_predecessors(&self) -> IndexVec<PostOrderId, Vec<PostOrderId>> {
|
||||||
|
let mut preds = IndexVec::from_fn_n(|_| vec![], self.nodes.len());
|
||||||
|
for (id, node) in self.nodes.iter_enumerated() {
|
||||||
|
// If the node has no explicit successors, we assume that control
|
||||||
|
// will from this node into the next one.
|
||||||
|
//
|
||||||
|
// If there are successors listed, then we assume that all
|
||||||
|
// possible successors are given and we do not include the default.
|
||||||
|
if node.successors.len() == 0 && id.index() != self.nodes.len() - 1 {
|
||||||
|
preds[id + 1].push(id);
|
||||||
|
} else {
|
||||||
|
for succ in &node.successors {
|
||||||
|
preds[*succ].push(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
preds
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
//! Implementation of GraphWalk for DropRanges so we can visualize the control
|
||||||
|
//! flow graph when needed for debugging.
|
||||||
|
|
||||||
|
use rustc_graphviz as dot;
|
||||||
|
|
||||||
|
use super::{DropRangesBuilder, PostOrderId};
|
||||||
|
|
||||||
|
/// Writes the CFG for DropRangesBuilder to a .dot file for visualization.
|
||||||
|
///
|
||||||
|
/// It is not normally called, but is kept around to easily add debugging
|
||||||
|
/// code when needed.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(super) fn write_graph_to_file(drop_ranges: &DropRangesBuilder, filename: &str) {
|
||||||
|
dot::render(drop_ranges, &mut std::fs::File::create(filename).unwrap()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> dot::GraphWalk<'a> for DropRangesBuilder {
|
||||||
|
type Node = PostOrderId;
|
||||||
|
|
||||||
|
type Edge = (PostOrderId, PostOrderId);
|
||||||
|
|
||||||
|
fn nodes(&'a self) -> dot::Nodes<'a, Self::Node> {
|
||||||
|
self.nodes.iter_enumerated().map(|(i, _)| i).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn edges(&'a self) -> dot::Edges<'a, Self::Edge> {
|
||||||
|
self.nodes
|
||||||
|
.iter_enumerated()
|
||||||
|
.flat_map(|(i, node)| {
|
||||||
|
if node.successors.len() == 0 {
|
||||||
|
vec![(i, i + 1)]
|
||||||
|
} else {
|
||||||
|
node.successors.iter().map(move |&s| (i, s)).collect()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn source(&'a self, edge: &Self::Edge) -> Self::Node {
|
||||||
|
edge.0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn target(&'a self, edge: &Self::Edge) -> Self::Node {
|
||||||
|
edge.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> dot::Labeller<'a> for DropRangesBuilder {
|
||||||
|
type Node = PostOrderId;
|
||||||
|
|
||||||
|
type Edge = (PostOrderId, PostOrderId);
|
||||||
|
|
||||||
|
fn graph_id(&'a self) -> dot::Id<'a> {
|
||||||
|
dot::Id::new("drop_ranges").unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn node_id(&'a self, n: &Self::Node) -> dot::Id<'a> {
|
||||||
|
dot::Id::new(format!("id{}", n.index())).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn node_label(&'a self, n: &Self::Node) -> dot::LabelText<'a> {
|
||||||
|
dot::LabelText::LabelStr(
|
||||||
|
format!(
|
||||||
|
"{:?}, local_id: {}",
|
||||||
|
n,
|
||||||
|
self.post_order_map
|
||||||
|
.iter()
|
||||||
|
.find(|(_hir_id, &post_order_id)| post_order_id == *n)
|
||||||
|
.map_or("<unknown>".into(), |(hir_id, _)| format!(
|
||||||
|
"{}",
|
||||||
|
hir_id.local_id.index()
|
||||||
|
))
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,118 @@
|
||||||
|
use super::TrackedValue;
|
||||||
|
use crate::{
|
||||||
|
check::FnCtxt,
|
||||||
|
expr_use_visitor::{self, ExprUseVisitor},
|
||||||
|
};
|
||||||
|
use hir::{def_id::DefId, Body, HirId, HirIdMap};
|
||||||
|
use rustc_data_structures::stable_set::FxHashSet;
|
||||||
|
use rustc_hir as hir;
|
||||||
|
use rustc_middle::hir::map::Map;
|
||||||
|
|
||||||
|
pub(super) fn find_consumed_and_borrowed<'a, 'tcx>(
|
||||||
|
fcx: &'a FnCtxt<'a, 'tcx>,
|
||||||
|
def_id: DefId,
|
||||||
|
body: &'tcx Body<'tcx>,
|
||||||
|
) -> ConsumedAndBorrowedPlaces {
|
||||||
|
let mut expr_use_visitor = ExprUseDelegate::new(fcx.tcx.hir());
|
||||||
|
expr_use_visitor.consume_body(fcx, def_id, body);
|
||||||
|
expr_use_visitor.places
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct ConsumedAndBorrowedPlaces {
|
||||||
|
/// Records the variables/expressions that are dropped by a given expression.
|
||||||
|
///
|
||||||
|
/// The key is the hir-id of the expression, and the value is a set or hir-ids for variables
|
||||||
|
/// or values that are consumed by that expression.
|
||||||
|
///
|
||||||
|
/// Note that this set excludes "partial drops" -- for example, a statement like `drop(x.y)` is
|
||||||
|
/// not considered a drop of `x`, although it would be a drop of `x.y`.
|
||||||
|
pub(super) consumed: HirIdMap<FxHashSet<TrackedValue>>,
|
||||||
|
/// A set of hir-ids of values or variables that are borrowed at some point within the body.
|
||||||
|
pub(super) borrowed: FxHashSet<TrackedValue>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Works with ExprUseVisitor to find interesting values for the drop range analysis.
|
||||||
|
///
|
||||||
|
/// Interesting values are those that are either dropped or borrowed. For dropped values, we also
|
||||||
|
/// record the parent expression, which is the point where the drop actually takes place.
|
||||||
|
struct ExprUseDelegate<'tcx> {
|
||||||
|
hir: Map<'tcx>,
|
||||||
|
places: ConsumedAndBorrowedPlaces,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> ExprUseDelegate<'tcx> {
|
||||||
|
fn new(hir: Map<'tcx>) -> Self {
|
||||||
|
Self {
|
||||||
|
hir,
|
||||||
|
places: ConsumedAndBorrowedPlaces {
|
||||||
|
consumed: <_>::default(),
|
||||||
|
borrowed: <_>::default(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn consume_body(&mut self, fcx: &'_ FnCtxt<'_, 'tcx>, def_id: DefId, body: &'tcx Body<'tcx>) {
|
||||||
|
// Run ExprUseVisitor to find where values are consumed.
|
||||||
|
ExprUseVisitor::new(
|
||||||
|
self,
|
||||||
|
&fcx.infcx,
|
||||||
|
def_id.expect_local(),
|
||||||
|
fcx.param_env,
|
||||||
|
&fcx.typeck_results.borrow(),
|
||||||
|
)
|
||||||
|
.consume_body(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mark_consumed(&mut self, consumer: HirId, target: TrackedValue) {
|
||||||
|
if !self.places.consumed.contains_key(&consumer) {
|
||||||
|
self.places.consumed.insert(consumer, <_>::default());
|
||||||
|
}
|
||||||
|
self.places.consumed.get_mut(&consumer).map(|places| places.insert(target));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> expr_use_visitor::Delegate<'tcx> for ExprUseDelegate<'tcx> {
|
||||||
|
fn consume(
|
||||||
|
&mut self,
|
||||||
|
place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>,
|
||||||
|
diag_expr_id: HirId,
|
||||||
|
) {
|
||||||
|
let parent = match self.hir.find_parent_node(place_with_id.hir_id) {
|
||||||
|
Some(parent) => parent,
|
||||||
|
None => place_with_id.hir_id,
|
||||||
|
};
|
||||||
|
debug!(
|
||||||
|
"consume {:?}; diag_expr_id={:?}, using parent {:?}",
|
||||||
|
place_with_id, diag_expr_id, parent
|
||||||
|
);
|
||||||
|
place_with_id
|
||||||
|
.try_into()
|
||||||
|
.map_or((), |tracked_value| self.mark_consumed(parent, tracked_value));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn borrow(
|
||||||
|
&mut self,
|
||||||
|
place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>,
|
||||||
|
_diag_expr_id: HirId,
|
||||||
|
_bk: rustc_middle::ty::BorrowKind,
|
||||||
|
) {
|
||||||
|
place_with_id
|
||||||
|
.try_into()
|
||||||
|
.map_or(false, |tracked_value| self.places.borrowed.insert(tracked_value));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mutate(
|
||||||
|
&mut self,
|
||||||
|
_assignee_place: &expr_use_visitor::PlaceWithHirId<'tcx>,
|
||||||
|
_diag_expr_id: HirId,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fake_read(
|
||||||
|
&mut self,
|
||||||
|
_place: expr_use_visitor::Place<'tcx>,
|
||||||
|
_cause: rustc_middle::mir::FakeReadCause,
|
||||||
|
_diag_expr_id: HirId,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
|
@ -43,7 +43,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
let item_def_id = self.tcx.hir().local_def_id(item_id);
|
let item_def_id = self.tcx.hir().local_def_id(item_id);
|
||||||
|
|
||||||
// This attribute causes us to dump some writeback information
|
// This attribute causes us to dump some writeback information
|
||||||
// in the form of errors, which is uSymbol for unit tests.
|
// in the form of errors, which is used for unit tests.
|
||||||
let rustc_dump_user_substs =
|
let rustc_dump_user_substs =
|
||||||
self.tcx.has_attr(item_def_id.to_def_id(), sym::rustc_dump_user_substs);
|
self.tcx.has_attr(item_def_id.to_def_id(), sym::rustc_dump_user_substs);
|
||||||
|
|
||||||
|
|
|
@ -206,6 +206,10 @@ x--expand-yaml-anchors--remove:
|
||||||
run: src/ci/scripts/verify-backported-commits.sh
|
run: src/ci/scripts/verify-backported-commits.sh
|
||||||
<<: *step
|
<<: *step
|
||||||
|
|
||||||
|
- name: ensure the stable version number is correct
|
||||||
|
run: src/ci/scripts/verify-stable-version-number.sh
|
||||||
|
<<: *step
|
||||||
|
|
||||||
- name: run the build
|
- name: run the build
|
||||||
run: src/ci/scripts/run-build-from-ci.sh
|
run: src/ci/scripts/run-build-from-ci.sh
|
||||||
env:
|
env:
|
||||||
|
|
30
src/ci/scripts/verify-stable-version-number.sh
Executable file
30
src/ci/scripts/verify-stable-version-number.sh
Executable file
|
@ -0,0 +1,30 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# On the stable channel, check whether we're trying to build artifacts with the
|
||||||
|
# same version number of a release that's already been published, and fail the
|
||||||
|
# build if that's the case.
|
||||||
|
#
|
||||||
|
# It's a mistake whenever that happens: the release process won't start if it
|
||||||
|
# detects a duplicate version number, and the artifacts would have to be
|
||||||
|
# rebuilt anyway.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
IFS=$'\n\t'
|
||||||
|
|
||||||
|
if [[ "$(cat src/ci/channel)" != "stable" ]]; then
|
||||||
|
echo "This script only works on the stable channel. Skipping the check."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
version="$(cat src/version)"
|
||||||
|
url="https://static.rust-lang.org/dist/channel-rust-${version}.toml"
|
||||||
|
|
||||||
|
if curl --silent --fail "${url}" >/dev/null; then
|
||||||
|
echo "The version number ${version} matches an existing release."
|
||||||
|
echo
|
||||||
|
echo "If you're trying to prepare a point release, remember to change the"
|
||||||
|
echo "version number in the src/version file."
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo "The version number ${version} does not match any released version!"
|
||||||
|
exit 0
|
||||||
|
fi
|
|
@ -8,6 +8,7 @@ use std::mem;
|
||||||
use std::ops;
|
use std::ops;
|
||||||
|
|
||||||
use rustc_ast::{LitKind, MetaItem, MetaItemKind, NestedMetaItem};
|
use rustc_ast::{LitKind, MetaItem, MetaItemKind, NestedMetaItem};
|
||||||
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
use rustc_feature::Features;
|
use rustc_feature::Features;
|
||||||
use rustc_session::parse::ParseSess;
|
use rustc_session::parse::ParseSess;
|
||||||
use rustc_span::symbol::{sym, Symbol};
|
use rustc_span::symbol::{sym, Symbol};
|
||||||
|
@ -43,15 +44,72 @@ crate struct InvalidCfgError {
|
||||||
|
|
||||||
impl Cfg {
|
impl Cfg {
|
||||||
/// Parses a `NestedMetaItem` into a `Cfg`.
|
/// Parses a `NestedMetaItem` into a `Cfg`.
|
||||||
fn parse_nested(nested_cfg: &NestedMetaItem) -> Result<Cfg, InvalidCfgError> {
|
fn parse_nested(
|
||||||
|
nested_cfg: &NestedMetaItem,
|
||||||
|
exclude: &FxHashSet<Cfg>,
|
||||||
|
) -> Result<Option<Cfg>, InvalidCfgError> {
|
||||||
match nested_cfg {
|
match nested_cfg {
|
||||||
NestedMetaItem::MetaItem(ref cfg) => Cfg::parse(cfg),
|
NestedMetaItem::MetaItem(ref cfg) => Cfg::parse_without(cfg, exclude),
|
||||||
NestedMetaItem::Literal(ref lit) => {
|
NestedMetaItem::Literal(ref lit) => {
|
||||||
Err(InvalidCfgError { msg: "unexpected literal", span: lit.span })
|
Err(InvalidCfgError { msg: "unexpected literal", span: lit.span })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
crate fn parse_without(
|
||||||
|
cfg: &MetaItem,
|
||||||
|
exclude: &FxHashSet<Cfg>,
|
||||||
|
) -> Result<Option<Cfg>, InvalidCfgError> {
|
||||||
|
let name = match cfg.ident() {
|
||||||
|
Some(ident) => ident.name,
|
||||||
|
None => {
|
||||||
|
return Err(InvalidCfgError {
|
||||||
|
msg: "expected a single identifier",
|
||||||
|
span: cfg.span,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match cfg.kind {
|
||||||
|
MetaItemKind::Word => {
|
||||||
|
let cfg = Cfg::Cfg(name, None);
|
||||||
|
if exclude.contains(&cfg) { Ok(None) } else { Ok(Some(cfg)) }
|
||||||
|
}
|
||||||
|
MetaItemKind::NameValue(ref lit) => match lit.kind {
|
||||||
|
LitKind::Str(value, _) => {
|
||||||
|
let cfg = Cfg::Cfg(name, Some(value));
|
||||||
|
if exclude.contains(&cfg) { Ok(None) } else { Ok(Some(cfg)) }
|
||||||
|
}
|
||||||
|
_ => Err(InvalidCfgError {
|
||||||
|
// FIXME: if the main #[cfg] syntax decided to support non-string literals,
|
||||||
|
// this should be changed as well.
|
||||||
|
msg: "value of cfg option should be a string literal",
|
||||||
|
span: lit.span,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
MetaItemKind::List(ref items) => {
|
||||||
|
let sub_cfgs =
|
||||||
|
items.iter().filter_map(|i| Cfg::parse_nested(i, exclude).transpose());
|
||||||
|
let ret = match name {
|
||||||
|
sym::all => sub_cfgs.fold(Ok(Cfg::True), |x, y| Ok(x? & y?)),
|
||||||
|
sym::any => sub_cfgs.fold(Ok(Cfg::False), |x, y| Ok(x? | y?)),
|
||||||
|
sym::not => {
|
||||||
|
let mut sub_cfgs = sub_cfgs.collect::<Vec<_>>();
|
||||||
|
if sub_cfgs.len() == 1 {
|
||||||
|
Ok(!sub_cfgs.pop().unwrap()?)
|
||||||
|
} else {
|
||||||
|
Err(InvalidCfgError { msg: "expected 1 cfg-pattern", span: cfg.span })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Err(InvalidCfgError { msg: "invalid predicate", span: cfg.span }),
|
||||||
|
};
|
||||||
|
match ret {
|
||||||
|
Ok(c) => Ok(Some(c)),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Parses a `MetaItem` into a `Cfg`.
|
/// Parses a `MetaItem` into a `Cfg`.
|
||||||
///
|
///
|
||||||
/// The `MetaItem` should be the content of the `#[cfg(...)]`, e.g., `unix` or
|
/// The `MetaItem` should be the content of the `#[cfg(...)]`, e.g., `unix` or
|
||||||
|
@ -60,42 +118,7 @@ impl Cfg {
|
||||||
/// If the content is not properly formatted, it will return an error indicating what and where
|
/// If the content is not properly formatted, it will return an error indicating what and where
|
||||||
/// the error is.
|
/// the error is.
|
||||||
crate fn parse(cfg: &MetaItem) -> Result<Cfg, InvalidCfgError> {
|
crate fn parse(cfg: &MetaItem) -> Result<Cfg, InvalidCfgError> {
|
||||||
let name = match cfg.ident() {
|
Self::parse_without(cfg, &FxHashSet::default()).map(|ret| ret.unwrap())
|
||||||
Some(ident) => ident.name,
|
|
||||||
None => {
|
|
||||||
return Err(InvalidCfgError {
|
|
||||||
msg: "expected a single identifier",
|
|
||||||
span: cfg.span,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
match cfg.kind {
|
|
||||||
MetaItemKind::Word => Ok(Cfg::Cfg(name, None)),
|
|
||||||
MetaItemKind::NameValue(ref lit) => match lit.kind {
|
|
||||||
LitKind::Str(value, _) => Ok(Cfg::Cfg(name, Some(value))),
|
|
||||||
_ => Err(InvalidCfgError {
|
|
||||||
// FIXME: if the main #[cfg] syntax decided to support non-string literals,
|
|
||||||
// this should be changed as well.
|
|
||||||
msg: "value of cfg option should be a string literal",
|
|
||||||
span: lit.span,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
MetaItemKind::List(ref items) => {
|
|
||||||
let mut sub_cfgs = items.iter().map(Cfg::parse_nested);
|
|
||||||
match name {
|
|
||||||
sym::all => sub_cfgs.fold(Ok(Cfg::True), |x, y| Ok(x? & y?)),
|
|
||||||
sym::any => sub_cfgs.fold(Ok(Cfg::False), |x, y| Ok(x? | y?)),
|
|
||||||
sym::not => {
|
|
||||||
if sub_cfgs.len() == 1 {
|
|
||||||
Ok(!sub_cfgs.next().unwrap()?)
|
|
||||||
} else {
|
|
||||||
Err(InvalidCfgError { msg: "expected 1 cfg-pattern", span: cfg.span })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => Err(InvalidCfgError { msg: "invalid predicate", span: cfg.span }),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks whether the given configuration can be matched in the current session.
|
/// Checks whether the given configuration can be matched in the current session.
|
||||||
|
|
|
@ -831,8 +831,9 @@ impl AttributesExt for [ast::Attribute] {
|
||||||
self.iter()
|
self.iter()
|
||||||
.filter(|attr| attr.has_name(sym::cfg))
|
.filter(|attr| attr.has_name(sym::cfg))
|
||||||
.filter_map(|attr| single(attr.meta_item_list()?))
|
.filter_map(|attr| single(attr.meta_item_list()?))
|
||||||
.filter_map(|attr| Cfg::parse(attr.meta_item()?).ok())
|
.filter_map(|attr| {
|
||||||
.filter(|cfg| !hidden_cfg.contains(cfg))
|
Cfg::parse_without(attr.meta_item()?, hidden_cfg).ok().flatten()
|
||||||
|
})
|
||||||
.fold(Cfg::True, |cfg, new_cfg| cfg & new_cfg)
|
.fold(Cfg::True, |cfg, new_cfg| cfg & new_cfg)
|
||||||
} else {
|
} else {
|
||||||
Cfg::True
|
Cfg::True
|
||||||
|
|
|
@ -1731,12 +1731,19 @@ details.rustdoc-toggle[open] > summary.hideme::after {
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 700px) {
|
@media (max-width: 700px) {
|
||||||
|
/* When linking to an item with an `id` (for instance, by clicking a link in the sidebar,
|
||||||
|
or visiting a URL with a fragment like `#method.new`, we don't want the item to be obscured
|
||||||
|
by the topbar. Anything with an `id` gets scroll-margin-top equal to .mobile-topbar's size.
|
||||||
|
*/
|
||||||
|
*[id] {
|
||||||
|
scroll-margin-top: 45px;
|
||||||
|
}
|
||||||
|
|
||||||
.rustdoc {
|
.rustdoc {
|
||||||
padding-top: 0px;
|
padding-top: 0px;
|
||||||
/* Sidebar should overlay main content, rather than pushing main content to the right.
|
/* Sidebar should overlay main content, rather than pushing main content to the right.
|
||||||
Turn off `display: flex` on the body element. */
|
Turn off `display: flex` on the body element. */
|
||||||
display: block;
|
display: block;
|
||||||
scroll-margin-top: 45px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
mod conversions;
|
mod conversions;
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::fs::File;
|
use std::fs::{create_dir_all, File};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
@ -18,13 +18,14 @@ use rustc_session::Session;
|
||||||
|
|
||||||
use rustdoc_json_types as types;
|
use rustdoc_json_types as types;
|
||||||
|
|
||||||
use crate::clean;
|
|
||||||
use crate::clean::types::{ExternalCrate, ExternalLocation};
|
use crate::clean::types::{ExternalCrate, ExternalLocation};
|
||||||
use crate::config::RenderOptions;
|
use crate::config::RenderOptions;
|
||||||
|
use crate::docfs::PathError;
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::formats::cache::Cache;
|
use crate::formats::cache::Cache;
|
||||||
use crate::formats::FormatRenderer;
|
use crate::formats::FormatRenderer;
|
||||||
use crate::json::conversions::{from_item_id, IntoWithTcx};
|
use crate::json::conversions::{from_item_id, IntoWithTcx};
|
||||||
|
use crate::{clean, try_err};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
crate struct JsonRenderer<'tcx> {
|
crate struct JsonRenderer<'tcx> {
|
||||||
|
@ -171,8 +172,21 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
|
||||||
/// the hashmap because certain items (traits and types) need to have their mappings for trait
|
/// the hashmap because certain items (traits and types) need to have their mappings for trait
|
||||||
/// implementations filled out before they're inserted.
|
/// implementations filled out before they're inserted.
|
||||||
fn item(&mut self, item: clean::Item) -> Result<(), Error> {
|
fn item(&mut self, item: clean::Item) -> Result<(), Error> {
|
||||||
|
let local_blanket_impl = match item.def_id {
|
||||||
|
clean::ItemId::Blanket { impl_id, .. } => impl_id.is_local(),
|
||||||
|
clean::ItemId::Auto { .. }
|
||||||
|
| clean::ItemId::DefId(_)
|
||||||
|
| clean::ItemId::Primitive(_, _) => false,
|
||||||
|
};
|
||||||
|
|
||||||
// Flatten items that recursively store other items
|
// Flatten items that recursively store other items
|
||||||
item.kind.inner_items().for_each(|i| self.item(i.clone()).unwrap());
|
// FIXME(CraftSpider): We skip children of local blanket implementations, as we'll have
|
||||||
|
// already seen the actual generic impl, and the generated ones don't need documenting.
|
||||||
|
// This is necessary due to the visibility, return type, and self arg of the generated
|
||||||
|
// impls not quite matching, and will no longer be necessary when the mismatch is fixed.
|
||||||
|
if !local_blanket_impl {
|
||||||
|
item.kind.inner_items().for_each(|i| self.item(i.clone()).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
let id = item.def_id;
|
let id = item.def_id;
|
||||||
if let Some(mut new_item) = self.convert_item(item) {
|
if let Some(mut new_item) = self.convert_item(item) {
|
||||||
|
@ -256,10 +270,13 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
|
||||||
.collect(),
|
.collect(),
|
||||||
format_version: types::FORMAT_VERSION,
|
format_version: types::FORMAT_VERSION,
|
||||||
};
|
};
|
||||||
let mut p = self.out_path.clone();
|
let out_dir = self.out_path.clone();
|
||||||
|
try_err!(create_dir_all(&out_dir), out_dir);
|
||||||
|
|
||||||
|
let mut p = out_dir;
|
||||||
p.push(output.index.get(&output.root).unwrap().name.clone().unwrap());
|
p.push(output.index.get(&output.root).unwrap().name.clone().unwrap());
|
||||||
p.set_extension("json");
|
p.set_extension("json");
|
||||||
let file = File::create(&p).map_err(|error| Error { error: error.to_string(), file: p })?;
|
let file = try_err!(File::create(&p), p);
|
||||||
serde_json::ser::to_writer(&file, &output).unwrap();
|
serde_json::ser::to_writer(&file, &output).unwrap();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,6 +141,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
})
|
})
|
||||||
|
.chain([Cfg::Cfg(sym::test, None)].into_iter())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
self.cx.cache.exact_paths = self.exact_paths;
|
self.cx.cache.exact_paths = self.exact_paths;
|
||||||
|
|
|
@ -1,12 +1,17 @@
|
||||||
# Test the behavior of #[link(.., kind = "raw-dylib")] with alternative calling conventions.
|
# Test the behavior of #[link(.., kind = "raw-dylib")] with alternative calling conventions.
|
||||||
|
|
||||||
# only-i686-pc-windows-msvc
|
# only-x86
|
||||||
|
# only-windows
|
||||||
|
|
||||||
-include ../../run-make-fulldeps/tools.mk
|
-include ../../run-make-fulldeps/tools.mk
|
||||||
|
|
||||||
all:
|
all:
|
||||||
$(call COMPILE_OBJ,"$(TMPDIR)"/extern.obj,extern.c)
|
$(call COMPILE_OBJ,"$(TMPDIR)"/extern.obj,extern.c)
|
||||||
|
ifdef IS_MSVC
|
||||||
$(CC) "$(TMPDIR)"/extern.obj -link -dll -out:"$(TMPDIR)"/extern.dll
|
$(CC) "$(TMPDIR)"/extern.obj -link -dll -out:"$(TMPDIR)"/extern.dll
|
||||||
|
else
|
||||||
|
$(CC) "$(TMPDIR)"/extern.obj -shared -o "$(TMPDIR)"/extern.dll
|
||||||
|
endif
|
||||||
$(RUSTC) --crate-type lib --crate-name raw_dylib_alt_calling_convention_test lib.rs
|
$(RUSTC) --crate-type lib --crate-name raw_dylib_alt_calling_convention_test lib.rs
|
||||||
$(RUSTC) --crate-type bin driver.rs -L "$(TMPDIR)"
|
$(RUSTC) --crate-type bin driver.rs -L "$(TMPDIR)"
|
||||||
"$(TMPDIR)"/driver > "$(TMPDIR)"/output.txt
|
"$(TMPDIR)"/driver > "$(TMPDIR)"/output.txt
|
||||||
|
|
|
@ -62,9 +62,12 @@ pub fn library_function() {
|
||||||
fastcall_fn_2(16, 3.5);
|
fastcall_fn_2(16, 3.5);
|
||||||
fastcall_fn_3(3.5);
|
fastcall_fn_3(3.5);
|
||||||
fastcall_fn_4(1, 2, 3.0);
|
fastcall_fn_4(1, 2, 3.0);
|
||||||
fastcall_fn_5(S { x: 1, y: 2 }, 16);
|
// FIXME: 91167
|
||||||
|
// rustc generates incorrect code for the calls to fastcall_fn_5 and fastcall_fn_7
|
||||||
|
// on i686-pc-windows-gnu; commenting these out until the indicated issue is fixed.
|
||||||
|
//fastcall_fn_5(S { x: 1, y: 2 }, 16);
|
||||||
fastcall_fn_6(Some(&S { x: 10, y: 12 }));
|
fastcall_fn_6(Some(&S { x: 10, y: 12 }));
|
||||||
fastcall_fn_7(S2 { x: 15, y: 16 }, 3);
|
//fastcall_fn_7(S2 { x: 15, y: 16 }, 3);
|
||||||
fastcall_fn_8(S3 { x: [1, 2, 3, 4, 5] }, S3 { x: [6, 7, 8, 9, 10] });
|
fastcall_fn_8(S3 { x: [1, 2, 3, 4, 5] }, S3 { x: [6, 7, 8, 9, 10] });
|
||||||
fastcall_fn_9(1, 3.0);
|
fastcall_fn_9(1, 3.0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,8 +11,6 @@ fastcall_fn_1(14)
|
||||||
fastcall_fn_2(16, 3.5)
|
fastcall_fn_2(16, 3.5)
|
||||||
fastcall_fn_3(3.5)
|
fastcall_fn_3(3.5)
|
||||||
fastcall_fn_4(1, 2, 3.0)
|
fastcall_fn_4(1, 2, 3.0)
|
||||||
fastcall_fn_5(S { x: 1, y: 2 }, 16)
|
|
||||||
fastcall_fn_6(S { x: 10, y: 12 })
|
fastcall_fn_6(S { x: 10, y: 12 })
|
||||||
fastcall_fn_7(S2 { x: 15, y: 16 }, 3)
|
|
||||||
fastcall_fn_8(S3 { x: [1, 2, 3, 4, 5] }, S3 { x: [6, 7, 8, 9, 10] })
|
fastcall_fn_8(S3 { x: [1, 2, 3, 4, 5] }, S3 { x: [6, 7, 8, 9, 10] })
|
||||||
fastcall_fn_9(1, 3.0)
|
fastcall_fn_9(1, 3.0)
|
||||||
|
|
|
@ -29,3 +29,9 @@ assert-css: (".sidebar", {"display": "block", "left": "-1000px"})
|
||||||
|
|
||||||
// Check that the topbar is visible
|
// Check that the topbar is visible
|
||||||
assert-property: (".mobile-topbar", {"clientHeight": "45"})
|
assert-property: (".mobile-topbar", {"clientHeight": "45"})
|
||||||
|
|
||||||
|
// Check that clicking an element from the sidebar scrolls to the right place
|
||||||
|
// so the target is not obscured by the topbar.
|
||||||
|
click: ".sidebar-menu-toggle"
|
||||||
|
click: ".sidebar-links a"
|
||||||
|
assert-position: ("#method\.must_use", {"y": 45})
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
// @has - "$.index[*][?(@.name=='EnumStruct')].kind" \"enum\"
|
// @has - "$.index[*][?(@.name=='EnumStruct')].kind" \"enum\"
|
||||||
pub enum EnumStruct {
|
pub enum EnumStruct {
|
||||||
// @has - "$.index[*][?(@.name=='VariantS')].inner.variant_kind" \"struct\"
|
// @has - "$.index[*][?(@.name=='VariantS')].inner.variant_kind" \"struct\"
|
||||||
// @has - "$.index[*][?(@.name=='x')]"
|
// @has - "$.index[*][?(@.name=='x')].kind" \"struct_field\"
|
||||||
// @has - "$.index[*][?(@.name=='y')]"
|
// @has - "$.index[*][?(@.name=='y')].kind" \"struct_field\"
|
||||||
VariantS {
|
VariantS {
|
||||||
x: u32,
|
x: u32,
|
||||||
y: String,
|
y: String,
|
||||||
|
|
|
@ -2,5 +2,7 @@
|
||||||
// @has - "$.index[*][?(@.name=='EnumTupleStruct')].kind" \"enum\"
|
// @has - "$.index[*][?(@.name=='EnumTupleStruct')].kind" \"enum\"
|
||||||
pub enum EnumTupleStruct {
|
pub enum EnumTupleStruct {
|
||||||
// @has - "$.index[*][?(@.name=='VariantA')].inner.variant_kind" \"tuple\"
|
// @has - "$.index[*][?(@.name=='VariantA')].inner.variant_kind" \"tuple\"
|
||||||
|
// @has - "$.index[*][?(@.name=='0')].kind" \"struct_field\"
|
||||||
|
// @has - "$.index[*][?(@.name=='1')].kind" \"struct_field\"
|
||||||
VariantA(u32, String),
|
VariantA(u32, String),
|
||||||
}
|
}
|
||||||
|
|
14
src/test/rustdoc-json/impls/blanket_with_local.rs
Normal file
14
src/test/rustdoc-json/impls/blanket_with_local.rs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
// Test for the ICE in rust/83718
|
||||||
|
// A blanket impl plus a local type together shouldn't result in mismatched ID issues
|
||||||
|
|
||||||
|
// @has blanket_with_local.json "$.index[*][?(@.name=='Load')]"
|
||||||
|
pub trait Load {
|
||||||
|
fn load() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P> Load for P {
|
||||||
|
fn load() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// @has - "$.index[*][?(@.name=='Wrapper')]"
|
||||||
|
pub struct Wrapper {}
|
|
@ -3,6 +3,12 @@
|
||||||
#![crate_name = "foo"]
|
#![crate_name = "foo"]
|
||||||
|
|
||||||
// @has foo/fn.foo.html
|
// @has foo/fn.foo.html
|
||||||
// @has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-test'
|
// @has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-doctest'
|
||||||
#[cfg(not(test))]
|
#[cfg(not(doctest))]
|
||||||
pub fn foo() {}
|
pub fn foo() {}
|
||||||
|
|
||||||
|
// @has foo/fn.bar.html
|
||||||
|
// @has - '//*[@class="item-info"]/*[@class="stab portability"]' 'doc'
|
||||||
|
// @!has - '//*[@class="item-info"]/*[@class="stab portability"]' 'test'
|
||||||
|
#[cfg(any(test, doc))]
|
||||||
|
pub fn bar() {}
|
||||||
|
|
|
@ -26,7 +26,7 @@ pub struct Hyperdulia;
|
||||||
|
|
||||||
// @has 'oud/struct.Oystercatcher.html'
|
// @has 'oud/struct.Oystercatcher.html'
|
||||||
// @count - '//*[@class="stab portability"]' 1
|
// @count - '//*[@class="stab portability"]' 1
|
||||||
// @matches - '//*[@class="stab portability"]' 'crate features solecism and oystercatcher'
|
// @matches - '//*[@class="stab portability"]' 'crate feature oystercatcher only'
|
||||||
// compile-flags:--cfg feature="oystercatcher"
|
// compile-flags:--cfg feature="oystercatcher"
|
||||||
#[cfg(all(feature = "solecism", feature = "oystercatcher"))]
|
#[cfg(all(feature = "solecism", feature = "oystercatcher"))]
|
||||||
pub struct Oystercatcher;
|
pub struct Oystercatcher;
|
||||||
|
|
|
@ -18,7 +18,7 @@ async fn fut() {}
|
||||||
async fn fut_arg<T>(_: T) {}
|
async fn fut_arg<T>(_: T) {}
|
||||||
|
|
||||||
async fn local_dropped_before_await() {
|
async fn local_dropped_before_await() {
|
||||||
// FIXME: it'd be nice for this to be allowed in a `Send` `async fn`
|
// this is okay now because of the drop
|
||||||
let x = non_send();
|
let x = non_send();
|
||||||
drop(x);
|
drop(x);
|
||||||
fut().await;
|
fut().await;
|
||||||
|
@ -35,21 +35,40 @@ async fn non_send_temporary_in_match() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_formatter() -> std::fmt::Formatter<'static> {
|
||||||
|
panic!()
|
||||||
|
}
|
||||||
|
|
||||||
async fn non_sync_with_method_call() {
|
async fn non_sync_with_method_call() {
|
||||||
// FIXME: it'd be nice for this to work.
|
let f: &mut std::fmt::Formatter = &mut get_formatter();
|
||||||
|
// It would by nice for this to work.
|
||||||
|
if non_sync().fmt(f).unwrap() == () {
|
||||||
|
fut().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn non_sync_with_method_call_panic() {
|
||||||
let f: &mut std::fmt::Formatter = panic!();
|
let f: &mut std::fmt::Formatter = panic!();
|
||||||
if non_sync().fmt(f).unwrap() == () {
|
if non_sync().fmt(f).unwrap() == () {
|
||||||
fut().await;
|
fut().await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn non_sync_with_method_call_infinite_loop() {
|
||||||
|
let f: &mut std::fmt::Formatter = loop {};
|
||||||
|
if non_sync().fmt(f).unwrap() == () {
|
||||||
|
fut().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn assert_send(_: impl Send) {}
|
fn assert_send(_: impl Send) {}
|
||||||
|
|
||||||
pub fn pass_assert() {
|
pub fn pass_assert() {
|
||||||
assert_send(local_dropped_before_await());
|
assert_send(local_dropped_before_await());
|
||||||
//~^ ERROR future cannot be sent between threads safely
|
|
||||||
assert_send(non_send_temporary_in_match());
|
assert_send(non_send_temporary_in_match());
|
||||||
//~^ ERROR future cannot be sent between threads safely
|
//~^ ERROR future cannot be sent between threads safely
|
||||||
assert_send(non_sync_with_method_call());
|
assert_send(non_sync_with_method_call());
|
||||||
//~^ ERROR future cannot be sent between threads safely
|
//~^ ERROR future cannot be sent between threads safely
|
||||||
|
assert_send(non_sync_with_method_call_panic());
|
||||||
|
assert_send(non_sync_with_method_call_infinite_loop());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,28 +1,5 @@
|
||||||
error: future cannot be sent between threads safely
|
error: future cannot be sent between threads safely
|
||||||
--> $DIR/async-fn-nonsend.rs:49:17
|
--> $DIR/async-fn-nonsend.rs:68:17
|
||||||
|
|
|
||||||
LL | assert_send(local_dropped_before_await());
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `local_dropped_before_await` is not `Send`
|
|
||||||
|
|
|
||||||
= help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `Rc<()>`
|
|
||||||
note: future is not `Send` as this value is used across an await
|
|
||||||
--> $DIR/async-fn-nonsend.rs:24:10
|
|
||||||
|
|
|
||||||
LL | let x = non_send();
|
|
||||||
| - has type `impl Debug` which is not `Send`
|
|
||||||
LL | drop(x);
|
|
||||||
LL | fut().await;
|
|
||||||
| ^^^^^^ await occurs here, with `x` maybe used later
|
|
||||||
LL | }
|
|
||||||
| - `x` is later dropped here
|
|
||||||
note: required by a bound in `assert_send`
|
|
||||||
--> $DIR/async-fn-nonsend.rs:46:24
|
|
||||||
|
|
|
||||||
LL | fn assert_send(_: impl Send) {}
|
|
||||||
| ^^^^ required by this bound in `assert_send`
|
|
||||||
|
|
||||||
error: future cannot be sent between threads safely
|
|
||||||
--> $DIR/async-fn-nonsend.rs:51:17
|
|
||||||
|
|
|
|
||||||
LL | assert_send(non_send_temporary_in_match());
|
LL | assert_send(non_send_temporary_in_match());
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `non_send_temporary_in_match` is not `Send`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `non_send_temporary_in_match` is not `Send`
|
||||||
|
@ -32,41 +9,41 @@ note: future is not `Send` as this value is used across an await
|
||||||
--> $DIR/async-fn-nonsend.rs:33:25
|
--> $DIR/async-fn-nonsend.rs:33:25
|
||||||
|
|
|
|
||||||
LL | match Some(non_send()) {
|
LL | match Some(non_send()) {
|
||||||
| ---------- has type `impl Debug` which is not `Send`
|
| ---------------- has type `Option<impl Debug>` which is not `Send`
|
||||||
LL | Some(_) => fut().await,
|
LL | Some(_) => fut().await,
|
||||||
| ^^^^^^ await occurs here, with `non_send()` maybe used later
|
| ^^^^^^ await occurs here, with `Some(non_send())` maybe used later
|
||||||
...
|
...
|
||||||
LL | }
|
LL | }
|
||||||
| - `non_send()` is later dropped here
|
| - `Some(non_send())` is later dropped here
|
||||||
note: required by a bound in `assert_send`
|
note: required by a bound in `assert_send`
|
||||||
--> $DIR/async-fn-nonsend.rs:46:24
|
--> $DIR/async-fn-nonsend.rs:64:24
|
||||||
|
|
|
|
||||||
LL | fn assert_send(_: impl Send) {}
|
LL | fn assert_send(_: impl Send) {}
|
||||||
| ^^^^ required by this bound in `assert_send`
|
| ^^^^ required by this bound in `assert_send`
|
||||||
|
|
||||||
error: future cannot be sent between threads safely
|
error: future cannot be sent between threads safely
|
||||||
--> $DIR/async-fn-nonsend.rs:53:17
|
--> $DIR/async-fn-nonsend.rs:70:17
|
||||||
|
|
|
|
||||||
LL | assert_send(non_sync_with_method_call());
|
LL | assert_send(non_sync_with_method_call());
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `non_sync_with_method_call` is not `Send`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `non_sync_with_method_call` is not `Send`
|
||||||
|
|
|
|
||||||
= help: the trait `Send` is not implemented for `dyn std::fmt::Write`
|
= help: the trait `Send` is not implemented for `dyn std::fmt::Write`
|
||||||
note: future is not `Send` as this value is used across an await
|
note: future is not `Send` as this value is used across an await
|
||||||
--> $DIR/async-fn-nonsend.rs:42:14
|
--> $DIR/async-fn-nonsend.rs:46:14
|
||||||
|
|
|
|
||||||
LL | let f: &mut std::fmt::Formatter = panic!();
|
LL | let f: &mut std::fmt::Formatter = &mut get_formatter();
|
||||||
| - has type `&mut Formatter<'_>` which is not `Send`
|
| --------------- has type `Formatter<'_>` which is not `Send`
|
||||||
LL | if non_sync().fmt(f).unwrap() == () {
|
...
|
||||||
LL | fut().await;
|
LL | fut().await;
|
||||||
| ^^^^^^ await occurs here, with `f` maybe used later
|
| ^^^^^^ await occurs here, with `get_formatter()` maybe used later
|
||||||
LL | }
|
LL | }
|
||||||
LL | }
|
LL | }
|
||||||
| - `f` is later dropped here
|
| - `get_formatter()` is later dropped here
|
||||||
note: required by a bound in `assert_send`
|
note: required by a bound in `assert_send`
|
||||||
--> $DIR/async-fn-nonsend.rs:46:24
|
--> $DIR/async-fn-nonsend.rs:64:24
|
||||||
|
|
|
|
||||||
LL | fn assert_send(_: impl Send) {}
|
LL | fn assert_send(_: impl Send) {}
|
||||||
| ^^^^ required by this bound in `assert_send`
|
| ^^^^ required by this bound in `assert_send`
|
||||||
|
|
||||||
error: aborting due to 3 previous errors
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
|
29
src/test/ui/async-await/partial-drop-partial-reinit.rs
Normal file
29
src/test/ui/async-await/partial-drop-partial-reinit.rs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
// edition:2021
|
||||||
|
#![feature(negative_impls)]
|
||||||
|
#![allow(unused)]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
gimme_send(foo());
|
||||||
|
//~^ ERROR cannot be sent between threads safely
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gimme_send<T: Send>(t: T) {
|
||||||
|
drop(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NotSend {}
|
||||||
|
|
||||||
|
impl Drop for NotSend {
|
||||||
|
fn drop(&mut self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl !Send for NotSend {}
|
||||||
|
|
||||||
|
async fn foo() {
|
||||||
|
let mut x = (NotSend {},);
|
||||||
|
drop(x.0);
|
||||||
|
x.0 = NotSend {};
|
||||||
|
bar().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn bar() {}
|
27
src/test/ui/async-await/partial-drop-partial-reinit.stderr
Normal file
27
src/test/ui/async-await/partial-drop-partial-reinit.stderr
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
error[E0277]: `NotSend` cannot be sent between threads safely
|
||||||
|
--> $DIR/partial-drop-partial-reinit.rs:6:16
|
||||||
|
|
|
||||||
|
LL | gimme_send(foo());
|
||||||
|
| ---------- ^^^^^ `NotSend` cannot be sent between threads safely
|
||||||
|
| |
|
||||||
|
| required by a bound introduced by this call
|
||||||
|
...
|
||||||
|
LL | async fn foo() {
|
||||||
|
| - within this `impl Future<Output = ()>`
|
||||||
|
|
|
||||||
|
= help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `NotSend`
|
||||||
|
= note: required because it appears within the type `(NotSend,)`
|
||||||
|
= note: required because it appears within the type `{ResumeTy, (NotSend,), impl Future<Output = ()>, ()}`
|
||||||
|
= note: required because it appears within the type `[static generator@$DIR/partial-drop-partial-reinit.rs:22:16: 27:2]`
|
||||||
|
= note: required because it appears within the type `from_generator::GenFuture<[static generator@$DIR/partial-drop-partial-reinit.rs:22:16: 27:2]>`
|
||||||
|
= note: required because it appears within the type `impl Future<Output = [async output]>`
|
||||||
|
= note: required because it appears within the type `impl Future<Output = ()>`
|
||||||
|
note: required by a bound in `gimme_send`
|
||||||
|
--> $DIR/partial-drop-partial-reinit.rs:10:18
|
||||||
|
|
|
||||||
|
LL | fn gimme_send<T: Send>(t: T) {
|
||||||
|
| ^^^^ required by this bound in `gimme_send`
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0277`.
|
|
@ -10,20 +10,12 @@ async fn foo() {
|
||||||
//~^ ERROR type inside `async fn` body must be known in this context
|
//~^ ERROR type inside `async fn` body must be known in this context
|
||||||
//~| ERROR type inside `async fn` body must be known in this context
|
//~| ERROR type inside `async fn` body must be known in this context
|
||||||
//~| ERROR type inside `async fn` body must be known in this context
|
//~| ERROR type inside `async fn` body must be known in this context
|
||||||
//~| ERROR type inside `async fn` body must be known in this context
|
|
||||||
//~| ERROR type inside `async fn` body must be known in this context
|
|
||||||
//~| NOTE cannot infer type for type parameter `T`
|
|
||||||
//~| NOTE cannot infer type for type parameter `T`
|
|
||||||
//~| NOTE cannot infer type for type parameter `T`
|
//~| NOTE cannot infer type for type parameter `T`
|
||||||
//~| NOTE cannot infer type for type parameter `T`
|
//~| NOTE cannot infer type for type parameter `T`
|
||||||
//~| NOTE cannot infer type for type parameter `T`
|
//~| NOTE cannot infer type for type parameter `T`
|
||||||
//~| NOTE the type is part of the `async fn` body because of this `await`
|
//~| NOTE the type is part of the `async fn` body because of this `await`
|
||||||
//~| NOTE the type is part of the `async fn` body because of this `await`
|
//~| NOTE the type is part of the `async fn` body because of this `await`
|
||||||
//~| NOTE the type is part of the `async fn` body because of this `await`
|
//~| NOTE the type is part of the `async fn` body because of this `await`
|
||||||
//~| NOTE the type is part of the `async fn` body because of this `await`
|
|
||||||
//~| NOTE the type is part of the `async fn` body because of this `await`
|
|
||||||
//~| NOTE in this expansion of desugaring of `await`
|
|
||||||
//~| NOTE in this expansion of desugaring of `await`
|
|
||||||
//~| NOTE in this expansion of desugaring of `await`
|
//~| NOTE in this expansion of desugaring of `await`
|
||||||
//~| NOTE in this expansion of desugaring of `await`
|
//~| NOTE in this expansion of desugaring of `await`
|
||||||
//~| NOTE in this expansion of desugaring of `await`
|
//~| NOTE in this expansion of desugaring of `await`
|
||||||
|
|
|
@ -34,30 +34,6 @@ note: the type is part of the `async fn` body because of this `await`
|
||||||
LL | bar().await;
|
LL | bar().await;
|
||||||
| ^^^^^^
|
| ^^^^^^
|
||||||
|
|
||||||
error[E0698]: type inside `async fn` body must be known in this context
|
error: aborting due to 3 previous errors
|
||||||
--> $DIR/unresolved_type_param.rs:9:5
|
|
||||||
|
|
|
||||||
LL | bar().await;
|
|
||||||
| ^^^ cannot infer type for type parameter `T` declared on the function `bar`
|
|
||||||
|
|
|
||||||
note: the type is part of the `async fn` body because of this `await`
|
|
||||||
--> $DIR/unresolved_type_param.rs:9:10
|
|
||||||
|
|
|
||||||
LL | bar().await;
|
|
||||||
| ^^^^^^
|
|
||||||
|
|
||||||
error[E0698]: type inside `async fn` body must be known in this context
|
|
||||||
--> $DIR/unresolved_type_param.rs:9:5
|
|
||||||
|
|
|
||||||
LL | bar().await;
|
|
||||||
| ^^^ cannot infer type for type parameter `T` declared on the function `bar`
|
|
||||||
|
|
|
||||||
note: the type is part of the `async fn` body because of this `await`
|
|
||||||
--> $DIR/unresolved_type_param.rs:9:10
|
|
||||||
|
|
|
||||||
LL | bar().await;
|
|
||||||
| ^^^^^^
|
|
||||||
|
|
||||||
error: aborting due to 5 previous errors
|
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0698`.
|
For more information about this error, try `rustc --explain E0698`.
|
||||||
|
|
121
src/test/ui/generator/drop-control-flow.rs
Normal file
121
src/test/ui/generator/drop-control-flow.rs
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
// build-pass
|
||||||
|
|
||||||
|
// A test to ensure generators capture values that were conditionally dropped,
|
||||||
|
// and also that values that are dropped along all paths to a yield do not get
|
||||||
|
// included in the generator type.
|
||||||
|
|
||||||
|
#![feature(generators, negative_impls)]
|
||||||
|
#![allow(unused_assignments, dead_code)]
|
||||||
|
|
||||||
|
struct Ptr;
|
||||||
|
impl<'a> Drop for Ptr {
|
||||||
|
fn drop(&mut self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NonSend;
|
||||||
|
impl !Send for NonSend {}
|
||||||
|
|
||||||
|
fn assert_send<T: Send>(_: T) {}
|
||||||
|
|
||||||
|
// This test case is reduced from src/test/ui/drop/dynamic-drop-async.rs
|
||||||
|
fn one_armed_if(arg: bool) {
|
||||||
|
let _ = || {
|
||||||
|
let arr = [Ptr];
|
||||||
|
if arg {
|
||||||
|
drop(arr);
|
||||||
|
}
|
||||||
|
yield;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn two_armed_if(arg: bool) {
|
||||||
|
assert_send(|| {
|
||||||
|
let arr = [Ptr];
|
||||||
|
if arg {
|
||||||
|
drop(arr);
|
||||||
|
} else {
|
||||||
|
drop(arr);
|
||||||
|
}
|
||||||
|
yield;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn if_let(arg: Option<i32>) {
|
||||||
|
let _ = || {
|
||||||
|
let arr = [Ptr];
|
||||||
|
if let Some(_) = arg {
|
||||||
|
drop(arr);
|
||||||
|
}
|
||||||
|
yield;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_in_if(arg: bool) {
|
||||||
|
assert_send(|| {
|
||||||
|
let mut x = NonSend;
|
||||||
|
drop(x);
|
||||||
|
if arg {
|
||||||
|
x = NonSend;
|
||||||
|
} else {
|
||||||
|
yield;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_in_match_arm(arg: Option<i32>) {
|
||||||
|
assert_send(|| {
|
||||||
|
let mut x = NonSend;
|
||||||
|
drop(x);
|
||||||
|
match arg {
|
||||||
|
Some(_) => x = NonSend,
|
||||||
|
None => yield,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reinit() {
|
||||||
|
let _ = || {
|
||||||
|
let mut arr = [Ptr];
|
||||||
|
drop(arr);
|
||||||
|
arr = [Ptr];
|
||||||
|
yield;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn loop_uninit() {
|
||||||
|
let _ = || {
|
||||||
|
let mut arr = [Ptr];
|
||||||
|
let mut count = 0;
|
||||||
|
drop(arr);
|
||||||
|
while count < 3 {
|
||||||
|
yield;
|
||||||
|
arr = [Ptr];
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nested_loop() {
|
||||||
|
let _ = || {
|
||||||
|
let mut arr = [Ptr];
|
||||||
|
let mut count = 0;
|
||||||
|
drop(arr);
|
||||||
|
while count < 3 {
|
||||||
|
for _ in 0..3 {
|
||||||
|
yield;
|
||||||
|
}
|
||||||
|
arr = [Ptr];
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
one_armed_if(true);
|
||||||
|
if_let(Some(41));
|
||||||
|
init_in_if(true);
|
||||||
|
init_in_match_arm(Some(41));
|
||||||
|
reinit();
|
||||||
|
loop_uninit();
|
||||||
|
nested_loop();
|
||||||
|
}
|
15
src/test/ui/generator/drop-yield-twice.rs
Normal file
15
src/test/ui/generator/drop-yield-twice.rs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#![feature(negative_impls, generators)]
|
||||||
|
|
||||||
|
struct Foo(i32);
|
||||||
|
impl !Send for Foo {}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
assert_send(|| { //~ ERROR generator cannot be sent between threads safely
|
||||||
|
let guard = Foo(42);
|
||||||
|
yield;
|
||||||
|
drop(guard);
|
||||||
|
yield;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assert_send<T: Send>(_: T) {}
|
25
src/test/ui/generator/drop-yield-twice.stderr
Normal file
25
src/test/ui/generator/drop-yield-twice.stderr
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
error: generator cannot be sent between threads safely
|
||||||
|
--> $DIR/drop-yield-twice.rs:7:5
|
||||||
|
|
|
||||||
|
LL | assert_send(|| {
|
||||||
|
| ^^^^^^^^^^^ generator is not `Send`
|
||||||
|
|
|
||||||
|
= help: within `[generator@$DIR/drop-yield-twice.rs:7:17: 12:6]`, the trait `Send` is not implemented for `Foo`
|
||||||
|
note: generator is not `Send` as this value is used across a yield
|
||||||
|
--> $DIR/drop-yield-twice.rs:9:9
|
||||||
|
|
|
||||||
|
LL | let guard = Foo(42);
|
||||||
|
| ----- has type `Foo` which is not `Send`
|
||||||
|
LL | yield;
|
||||||
|
| ^^^^^ yield occurs here, with `guard` maybe used later
|
||||||
|
...
|
||||||
|
LL | })
|
||||||
|
| - `guard` is later dropped here
|
||||||
|
note: required by a bound in `assert_send`
|
||||||
|
--> $DIR/drop-yield-twice.rs:15:19
|
||||||
|
|
|
||||||
|
LL | fn assert_send<T: Send>(_: T) {}
|
||||||
|
| ^^^^ required by this bound in `assert_send`
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
16
src/test/ui/generator/issue-57478.rs
Normal file
16
src/test/ui/generator/issue-57478.rs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
// check-pass
|
||||||
|
|
||||||
|
#![feature(negative_impls, generators)]
|
||||||
|
|
||||||
|
struct Foo;
|
||||||
|
impl !Send for Foo {}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
assert_send(|| {
|
||||||
|
let guard = Foo;
|
||||||
|
drop(guard);
|
||||||
|
yield;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assert_send<T: Send>(_: T) {}
|
40
src/test/ui/generator/partial-drop.rs
Normal file
40
src/test/ui/generator/partial-drop.rs
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
#![feature(negative_impls, generators)]
|
||||||
|
|
||||||
|
struct Foo;
|
||||||
|
impl !Send for Foo {}
|
||||||
|
|
||||||
|
struct Bar {
|
||||||
|
foo: Foo,
|
||||||
|
x: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
assert_send(|| {
|
||||||
|
//~^ ERROR generator cannot be sent between threads safely
|
||||||
|
// FIXME: it would be nice to make this work.
|
||||||
|
let guard = Bar { foo: Foo, x: 42 };
|
||||||
|
drop(guard.foo);
|
||||||
|
yield;
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_send(|| {
|
||||||
|
//~^ ERROR generator cannot be sent between threads safely
|
||||||
|
// FIXME: it would be nice to make this work.
|
||||||
|
let guard = Bar { foo: Foo, x: 42 };
|
||||||
|
drop(guard);
|
||||||
|
guard.foo = Foo;
|
||||||
|
guard.x = 23;
|
||||||
|
yield;
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_send(|| {
|
||||||
|
//~^ ERROR generator cannot be sent between threads safely
|
||||||
|
// FIXME: it would be nice to make this work.
|
||||||
|
let guard = Bar { foo: Foo, x: 42 };
|
||||||
|
let Bar { foo, x } = guard;
|
||||||
|
drop(foo);
|
||||||
|
yield;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assert_send<T: Send>(_: T) {}
|
71
src/test/ui/generator/partial-drop.stderr
Normal file
71
src/test/ui/generator/partial-drop.stderr
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
error: generator cannot be sent between threads safely
|
||||||
|
--> $DIR/partial-drop.rs:12:5
|
||||||
|
|
|
||||||
|
LL | assert_send(|| {
|
||||||
|
| ^^^^^^^^^^^ generator is not `Send`
|
||||||
|
|
|
||||||
|
= help: within `[generator@$DIR/partial-drop.rs:12:17: 18:6]`, the trait `Send` is not implemented for `Foo`
|
||||||
|
note: generator is not `Send` as this value is used across a yield
|
||||||
|
--> $DIR/partial-drop.rs:17:9
|
||||||
|
|
|
||||||
|
LL | let guard = Bar { foo: Foo, x: 42 };
|
||||||
|
| ----- has type `Bar` which is not `Send`
|
||||||
|
LL | drop(guard.foo);
|
||||||
|
LL | yield;
|
||||||
|
| ^^^^^ yield occurs here, with `guard` maybe used later
|
||||||
|
LL | });
|
||||||
|
| - `guard` is later dropped here
|
||||||
|
note: required by a bound in `assert_send`
|
||||||
|
--> $DIR/partial-drop.rs:40:19
|
||||||
|
|
|
||||||
|
LL | fn assert_send<T: Send>(_: T) {}
|
||||||
|
| ^^^^ required by this bound in `assert_send`
|
||||||
|
|
||||||
|
error: generator cannot be sent between threads safely
|
||||||
|
--> $DIR/partial-drop.rs:20:5
|
||||||
|
|
|
||||||
|
LL | assert_send(|| {
|
||||||
|
| ^^^^^^^^^^^ generator is not `Send`
|
||||||
|
|
|
||||||
|
= help: within `[generator@$DIR/partial-drop.rs:20:17: 28:6]`, the trait `Send` is not implemented for `Foo`
|
||||||
|
note: generator is not `Send` as this value is used across a yield
|
||||||
|
--> $DIR/partial-drop.rs:27:9
|
||||||
|
|
|
||||||
|
LL | let guard = Bar { foo: Foo, x: 42 };
|
||||||
|
| ----- has type `Bar` which is not `Send`
|
||||||
|
...
|
||||||
|
LL | yield;
|
||||||
|
| ^^^^^ yield occurs here, with `guard` maybe used later
|
||||||
|
LL | });
|
||||||
|
| - `guard` is later dropped here
|
||||||
|
note: required by a bound in `assert_send`
|
||||||
|
--> $DIR/partial-drop.rs:40:19
|
||||||
|
|
|
||||||
|
LL | fn assert_send<T: Send>(_: T) {}
|
||||||
|
| ^^^^ required by this bound in `assert_send`
|
||||||
|
|
||||||
|
error: generator cannot be sent between threads safely
|
||||||
|
--> $DIR/partial-drop.rs:30:5
|
||||||
|
|
|
||||||
|
LL | assert_send(|| {
|
||||||
|
| ^^^^^^^^^^^ generator is not `Send`
|
||||||
|
|
|
||||||
|
= help: within `[generator@$DIR/partial-drop.rs:30:17: 37:6]`, the trait `Send` is not implemented for `Foo`
|
||||||
|
note: generator is not `Send` as this value is used across a yield
|
||||||
|
--> $DIR/partial-drop.rs:36:9
|
||||||
|
|
|
||||||
|
LL | let guard = Bar { foo: Foo, x: 42 };
|
||||||
|
| ----- has type `Bar` which is not `Send`
|
||||||
|
...
|
||||||
|
LL | yield;
|
||||||
|
| ^^^^^ yield occurs here, with `guard` maybe used later
|
||||||
|
LL | });
|
||||||
|
| - `guard` is later dropped here
|
||||||
|
note: required by a bound in `assert_send`
|
||||||
|
--> $DIR/partial-drop.rs:40:19
|
||||||
|
|
|
||||||
|
LL | fn assert_send<T: Send>(_: T) {}
|
||||||
|
| ^^^^ required by this bound in `assert_send`
|
||||||
|
|
||||||
|
error: aborting due to 3 previous errors
|
||||||
|
|
25
src/test/ui/generator/reinit-in-match-guard.rs
Normal file
25
src/test/ui/generator/reinit-in-match-guard.rs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
// build-pass
|
||||||
|
|
||||||
|
#![feature(generators)]
|
||||||
|
|
||||||
|
#![allow(unused_assignments, dead_code)]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let _ = || {
|
||||||
|
let mut x = vec![22_usize];
|
||||||
|
std::mem::drop(x);
|
||||||
|
match y() {
|
||||||
|
true if {
|
||||||
|
x = vec![];
|
||||||
|
false
|
||||||
|
} => {}
|
||||||
|
_ => {
|
||||||
|
yield;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn y() -> bool {
|
||||||
|
true
|
||||||
|
}
|
|
@ -11,16 +11,10 @@ LL | struct SemiTransparent;
|
||||||
| ----------------------- similarly named unit struct `SemiTransparent` defined here
|
| ----------------------- similarly named unit struct `SemiTransparent` defined here
|
||||||
...
|
...
|
||||||
LL | semitransparent;
|
LL | semitransparent;
|
||||||
| ^^^^^^^^^^^^^^^ not a value
|
| ^^^^^^^^^^^^^^^
|
||||||
|
|
| |
|
||||||
help: use `!` to invoke the macro
|
| not a value
|
||||||
|
|
| help: a unit struct with a similar name exists: `SemiTransparent`
|
||||||
LL | semitransparent!;
|
|
||||||
| +
|
|
||||||
help: a unit struct with a similar name exists
|
|
||||||
|
|
|
||||||
LL | SemiTransparent;
|
|
||||||
| ~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
error[E0423]: expected value, found macro `opaque`
|
error[E0423]: expected value, found macro `opaque`
|
||||||
--> $DIR/rustc-macro-transparency.rs:30:5
|
--> $DIR/rustc-macro-transparency.rs:30:5
|
||||||
|
@ -29,16 +23,10 @@ LL | struct Opaque;
|
||||||
| -------------- similarly named unit struct `Opaque` defined here
|
| -------------- similarly named unit struct `Opaque` defined here
|
||||||
...
|
...
|
||||||
LL | opaque;
|
LL | opaque;
|
||||||
| ^^^^^^ not a value
|
| ^^^^^^
|
||||||
|
|
| |
|
||||||
help: use `!` to invoke the macro
|
| not a value
|
||||||
|
|
| help: a unit struct with a similar name exists (notice the capitalization): `Opaque`
|
||||||
LL | opaque!;
|
|
||||||
| +
|
|
||||||
help: a unit struct with a similar name exists
|
|
||||||
|
|
|
||||||
LL | Opaque;
|
|
||||||
| ~~~~~~
|
|
||||||
|
|
||||||
error: aborting due to 3 previous errors
|
error: aborting due to 3 previous errors
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ async fn wheeee<T>(t: T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn yes() {
|
async fn yes() {
|
||||||
wheeee(No {}).await; //~ ERROR `No` held across
|
wheeee(&No {}).await; //~ ERROR `No` held across
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
error: `No` held across a suspend point, but should not be
|
error: `No` held across a suspend point, but should not be
|
||||||
--> $DIR/dedup.rs:16:12
|
--> $DIR/dedup.rs:16:13
|
||||||
|
|
|
|
||||||
LL | wheeee(No {}).await;
|
LL | wheeee(&No {}).await;
|
||||||
| ^^^^^ ------ the value is held across this suspend point
|
| ^^^^^ ------ the value is held across this suspend point
|
||||||
|
|
|
|
||||||
note: the lint level is defined here
|
note: the lint level is defined here
|
||||||
--> $DIR/dedup.rs:3:9
|
--> $DIR/dedup.rs:3:9
|
||||||
|
@ -10,10 +10,10 @@ note: the lint level is defined here
|
||||||
LL | #![deny(must_not_suspend)]
|
LL | #![deny(must_not_suspend)]
|
||||||
| ^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^
|
||||||
help: consider using a block (`{ ... }`) to shrink the value's scope, ending before the suspend point
|
help: consider using a block (`{ ... }`) to shrink the value's scope, ending before the suspend point
|
||||||
--> $DIR/dedup.rs:16:12
|
--> $DIR/dedup.rs:16:13
|
||||||
|
|
|
|
||||||
LL | wheeee(No {}).await;
|
LL | wheeee(&No {}).await;
|
||||||
| ^^^^^
|
| ^^^^^
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
|
11
src/test/ui/resolve/resolve-hint-macro.fixed
Normal file
11
src/test/ui/resolve/resolve-hint-macro.fixed
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
// run-rustfix
|
||||||
|
fn main() {
|
||||||
|
assert_eq!(1, 1);
|
||||||
|
//~^ ERROR expected function, found macro `assert_eq`
|
||||||
|
assert_eq! { 1, 1 };
|
||||||
|
//~^ ERROR expected struct, variant or union type, found macro `assert_eq`
|
||||||
|
//~| ERROR expected identifier, found `1`
|
||||||
|
//~| ERROR expected identifier, found `1`
|
||||||
|
assert![true];
|
||||||
|
//~^ ERROR expected value, found macro `assert`
|
||||||
|
}
|
|
@ -1,4 +1,11 @@
|
||||||
|
// run-rustfix
|
||||||
fn main() {
|
fn main() {
|
||||||
assert_eq(1, 1);
|
assert_eq(1, 1);
|
||||||
//~^ ERROR expected function, found macro `assert_eq`
|
//~^ ERROR expected function, found macro `assert_eq`
|
||||||
|
assert_eq { 1, 1 };
|
||||||
|
//~^ ERROR expected struct, variant or union type, found macro `assert_eq`
|
||||||
|
//~| ERROR expected identifier, found `1`
|
||||||
|
//~| ERROR expected identifier, found `1`
|
||||||
|
assert[true];
|
||||||
|
//~^ ERROR expected value, found macro `assert`
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,21 @@
|
||||||
|
error: expected identifier, found `1`
|
||||||
|
--> $DIR/resolve-hint-macro.rs:5:17
|
||||||
|
|
|
||||||
|
LL | assert_eq { 1, 1 };
|
||||||
|
| --------- ^ expected identifier
|
||||||
|
| |
|
||||||
|
| while parsing this struct
|
||||||
|
|
||||||
|
error: expected identifier, found `1`
|
||||||
|
--> $DIR/resolve-hint-macro.rs:5:20
|
||||||
|
|
|
||||||
|
LL | assert_eq { 1, 1 };
|
||||||
|
| --------- ^ expected identifier
|
||||||
|
| |
|
||||||
|
| while parsing this struct
|
||||||
|
|
||||||
error[E0423]: expected function, found macro `assert_eq`
|
error[E0423]: expected function, found macro `assert_eq`
|
||||||
--> $DIR/resolve-hint-macro.rs:2:5
|
--> $DIR/resolve-hint-macro.rs:3:5
|
||||||
|
|
|
|
||||||
LL | assert_eq(1, 1);
|
LL | assert_eq(1, 1);
|
||||||
| ^^^^^^^^^ not a function
|
| ^^^^^^^^^ not a function
|
||||||
|
@ -9,6 +25,29 @@ help: use `!` to invoke the macro
|
||||||
LL | assert_eq!(1, 1);
|
LL | assert_eq!(1, 1);
|
||||||
| +
|
| +
|
||||||
|
|
||||||
error: aborting due to previous error
|
error[E0574]: expected struct, variant or union type, found macro `assert_eq`
|
||||||
|
--> $DIR/resolve-hint-macro.rs:5:5
|
||||||
|
|
|
||||||
|
LL | assert_eq { 1, 1 };
|
||||||
|
| ^^^^^^^^^ not a struct, variant or union type
|
||||||
|
|
|
||||||
|
help: use `!` to invoke the macro
|
||||||
|
|
|
||||||
|
LL | assert_eq! { 1, 1 };
|
||||||
|
| +
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0423`.
|
error[E0423]: expected value, found macro `assert`
|
||||||
|
--> $DIR/resolve-hint-macro.rs:9:5
|
||||||
|
|
|
||||||
|
LL | assert[true];
|
||||||
|
| ^^^^^^ not a value
|
||||||
|
|
|
||||||
|
help: use `!` to invoke the macro
|
||||||
|
|
|
||||||
|
LL | assert![true];
|
||||||
|
| +
|
||||||
|
|
||||||
|
error: aborting due to 5 previous errors
|
||||||
|
|
||||||
|
Some errors have detailed explanations: E0423, E0574.
|
||||||
|
For more information about an error, try `rustc --explain E0423`.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// check-pass
|
// check-pass
|
||||||
|
|
||||||
#![feature(let_chains)]
|
#![feature(if_let_guard, let_chains)]
|
||||||
|
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
|
||||||
|
@ -16,6 +16,16 @@ fn main() {
|
||||||
&& let None = local_start {
|
&& let None = local_start {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
match opt {
|
||||||
|
Some(ref first) if let second = first && let _third = second => {},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
match opt {
|
||||||
|
Some(ref first) if let Range { start: local_start, end: _ } = first
|
||||||
|
&& let None = local_start => {},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
while let first = &opt && let Some(ref second) = first && let None = second.start {
|
while let first = &opt && let Some(ref second) = first && let None = second.start {
|
||||||
}
|
}
|
||||||
while let Some(ref first) = opt && let second = first && let _third = second {
|
while let Some(ref first) = opt && let second = first && let _third = second {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// run-pass
|
// run-pass
|
||||||
|
|
||||||
#![feature(let_chains)]
|
#![feature(if_let_guard, let_chains)]
|
||||||
|
|
||||||
fn check_if_let(opt: Option<Option<Option<i32>>>, value: i32) -> bool {
|
fn check_if_let(opt: Option<Option<Option<i32>>>, value: i32) -> bool {
|
||||||
if let Some(first) = opt
|
if let Some(first) = opt
|
||||||
|
@ -15,6 +15,17 @@ fn check_if_let(opt: Option<Option<Option<i32>>>, value: i32) -> bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_let_guard(opt: Option<Option<Option<i32>>>, value: i32) -> bool {
|
||||||
|
match opt {
|
||||||
|
Some(first) if let Some(second) = first && let Some(third) = second && third == value => {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn check_while_let(opt: Option<Option<Option<i32>>>, value: i32) -> bool {
|
fn check_while_let(opt: Option<Option<Option<i32>>>, value: i32) -> bool {
|
||||||
while let Some(first) = opt
|
while let Some(first) = opt
|
||||||
&& let Some(second) = first
|
&& let Some(second) = first
|
||||||
|
@ -30,6 +41,9 @@ fn main() {
|
||||||
assert_eq!(check_if_let(Some(Some(Some(1))), 1), true);
|
assert_eq!(check_if_let(Some(Some(Some(1))), 1), true);
|
||||||
assert_eq!(check_if_let(Some(Some(Some(1))), 9), false);
|
assert_eq!(check_if_let(Some(Some(Some(1))), 9), false);
|
||||||
|
|
||||||
|
assert_eq!(check_let_guard(Some(Some(Some(1))), 1), true);
|
||||||
|
assert_eq!(check_let_guard(Some(Some(Some(1))), 9), false);
|
||||||
|
|
||||||
assert_eq!(check_while_let(Some(Some(Some(1))), 1), true);
|
assert_eq!(check_while_let(Some(Some(Some(1))), 1), true);
|
||||||
assert_eq!(check_while_let(Some(Some(Some(1))), 9), false);
|
assert_eq!(check_while_let(Some(Some(Some(1))), 9), false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -500,7 +500,19 @@ impl<'test> TestCx<'test> {
|
||||||
expected = expected.replace(&cr, "");
|
expected = expected.replace(&cr, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
self.compare_source(&expected, &actual);
|
if !self.config.bless {
|
||||||
|
self.compare_source(&expected, &actual);
|
||||||
|
} else if expected != actual {
|
||||||
|
let filepath_buf;
|
||||||
|
let filepath = match &self.props.pp_exact {
|
||||||
|
Some(file) => {
|
||||||
|
filepath_buf = self.testpaths.file.parent().unwrap().join(file);
|
||||||
|
&filepath_buf
|
||||||
|
}
|
||||||
|
None => &self.testpaths.file,
|
||||||
|
};
|
||||||
|
fs::write(filepath, &actual).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
// If we're only making sure that the output matches then just stop here
|
// If we're only making sure that the output matches then just stop here
|
||||||
if self.props.pretty_compare_only {
|
if self.props.pretty_compare_only {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue