1
Fork 0

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:
bors 2022-01-21 03:04:43 +00:00
commit 523be2e05d
62 changed files with 1925 additions and 339 deletions

View file

@ -128,6 +128,9 @@ jobs:
- name: ensure backported commits are in upstream branches
run: src/ci/scripts/verify-backported-commits.sh
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
run: src/ci/scripts/run-build-from-ci.sh
env:
@ -502,6 +505,9 @@ jobs:
- name: ensure backported commits are in upstream branches
run: src/ci/scripts/verify-backported-commits.sh
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
run: src/ci/scripts/run-build-from-ci.sh
env:
@ -612,6 +618,9 @@ jobs:
- name: ensure backported commits are in upstream branches
run: src/ci/scripts/verify-backported-commits.sh
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
run: src/ci/scripts/run-build-from-ci.sh
env:

View file

@ -544,9 +544,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chalk-derive"
version = "0.75.0"
version = "0.76.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d54e3b5f9e3425e6b119ff07568d8d006bfa5a8d6f78a9cbc3530b1e962e316c"
checksum = "58c24b8052ea1e3adbb6f9ab7ba5fcc18b9d12591c042de4c833f709ce81e0e0"
dependencies = [
"proc-macro2",
"quote",
@ -556,9 +556,9 @@ dependencies = [
[[package]]
name = "chalk-engine"
version = "0.75.0"
version = "0.76.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdc891073396b167163db77123b0a3c00088edc00466cecc5531f33e3e989523"
checksum = "0eca186b6ea9af798312f4b568fd094c82e7946ac08be5dc5fea22decc6d2ed8"
dependencies = [
"chalk-derive",
"chalk-ir",
@ -569,9 +569,9 @@ dependencies = [
[[package]]
name = "chalk-ir"
version = "0.75.0"
version = "0.76.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b79e5a1d04b79311e90c69356a2c62027853906a7e33b3e070b93c055fc3e8a"
checksum = "f3cad5c3f1edd4b4a2c9bda24ae558ceb4f88336f88f944c2e35d0bfeb13c818"
dependencies = [
"bitflags",
"chalk-derive",
@ -580,13 +580,14 @@ dependencies = [
[[package]]
name = "chalk-solve"
version = "0.75.0"
version = "0.76.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5d2a1db6605aba70a58820bd80ac422b218913a510f1a40beef9efc5371ea1d"
checksum = "94533188d3452bc72cbd5618d166f45fc7646b674ad3fe9667d557bc25236dee"
dependencies = [
"chalk-derive",
"chalk-ir",
"ena",
"indexmap",
"itertools 0.10.1",
"petgraph",
"rustc-hash",
@ -4413,6 +4414,7 @@ dependencies = [
"rustc_attr",
"rustc_data_structures",
"rustc_errors",
"rustc_graphviz",
"rustc_hir",
"rustc_hir_pretty",
"rustc_index",
@ -4420,6 +4422,7 @@ dependencies = [
"rustc_lint",
"rustc_macros",
"rustc_middle",
"rustc_serialize",
"rustc_session",
"rustc_span",
"rustc_target",

View file

@ -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)
==========================

View file

@ -137,7 +137,7 @@ mod ring;
use ring::RingBuffer;
use std::borrow::Cow;
use std::collections::VecDeque;
use std::fmt;
use std::iter;
/// How to break. Described in more detail in the module docs.
#[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)]
enum PrintStackBreak {
enum PrintFrame {
Fits,
Broken(Breaks),
}
#[derive(Copy, Clone)]
struct PrintStackElem {
offset: isize,
pbreak: PrintStackBreak,
Broken { offset: isize, breaks: Breaks },
}
const SIZE_INFINITY: isize = 0xffff;
@ -220,7 +203,7 @@ pub struct Printer {
/// advancing.
scan_stack: VecDeque<usize>,
/// Stack of blocks-in-progress being flushed by print
print_stack: Vec<PrintStackElem>,
print_stack: Vec<PrintFrame>,
/// Buffered indentation to avoid writing trailing whitespace
pending_indentation: isize,
/// The token most recently popped from the left boundary of the
@ -260,8 +243,8 @@ impl Printer {
}
/// Be very careful with this!
pub fn replace_last_token_still_buffered(&mut self, t: Token) {
self.buf.last_mut().unwrap().token = t;
pub fn replace_last_token_still_buffered(&mut self, token: Token) {
self.buf.last_mut().unwrap().token = token;
}
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() {
self.left_total = 1;
self.right_total = 1;
self.buf.clear();
}
let right = self.buf.push(BufEntry { token: Token::Begin(b), size: -self.right_total });
self.scan_stack.push_front(right);
let right = self.buf.push(BufEntry { token: Token::Begin(token), size: -self.right_total });
self.scan_stack.push_back(right);
}
fn scan_end(&mut self) {
@ -286,11 +269,11 @@ impl Printer {
self.print_end();
} else {
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() {
self.left_total = 1;
self.right_total = 1;
@ -298,17 +281,17 @@ impl Printer {
} else {
self.check_stack(0);
}
let right = self.buf.push(BufEntry { token: Token::Break(b), size: -self.right_total });
self.scan_stack.push_front(right);
self.right_total += b.blank_space;
let right = self.buf.push(BufEntry { token: Token::Break(token), size: -self.right_total });
self.scan_stack.push_back(right);
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() {
self.print_string(&s);
self.print_string(&string);
} else {
let len = s.len() as isize;
self.buf.push(BufEntry { token: Token::String(s), size: len });
let len = string.len() as isize;
self.buf.push(BufEntry { token: Token::String(string), size: len });
self.right_total += len;
self.check_stream();
}
@ -316,8 +299,8 @@ impl Printer {
fn check_stream(&mut self) {
while self.right_total - self.left_total > self.space {
if *self.scan_stack.back().unwrap() == self.buf.index_of_first() {
self.scan_stack.pop_back().unwrap();
if *self.scan_stack.front().unwrap() == self.buf.index_of_first() {
self.scan_stack.pop_front().unwrap();
self.buf.first_mut().unwrap().size = SIZE_INFINITY;
}
self.advance_left();
@ -328,56 +311,52 @@ impl Printer {
}
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 {
let left = self.buf.first().unwrap().token.clone();
let len = match left {
Token::Break(b) => b.blank_space,
Token::String(ref s) => {
let len = s.len() as isize;
assert_eq!(len, left_size);
len
match &left.token {
Token::String(string) => {
self.left_total += string.len() as isize;
self.print_string(string);
}
_ => 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() {
break;
}
left_size = self.buf.first().unwrap().size;
}
}
fn check_stack(&mut self, mut k: usize) {
while let Some(&x) = self.scan_stack.front() {
let mut entry = &mut self.buf[x];
fn check_stack(&mut self, mut depth: usize) {
while let Some(&index) = self.scan_stack.back() {
let mut entry = &mut self.buf[index];
match entry.token {
Token::Begin(_) => {
if k == 0 {
if depth == 0 {
break;
}
self.scan_stack.pop_front().unwrap();
self.scan_stack.pop_back().unwrap();
entry.size += self.right_total;
k -= 1;
depth -= 1;
}
Token::End => {
// paper says + not =, but that makes no sense.
self.scan_stack.pop_front().unwrap();
self.scan_stack.pop_back().unwrap();
entry.size = 1;
k += 1;
depth += 1;
}
_ => {
self.scan_stack.pop_front().unwrap();
self.scan_stack.pop_back().unwrap();
entry.size += self.right_total;
if k == 0 {
if depth == 0 {
break;
}
}
@ -385,29 +364,19 @@ impl Printer {
}
}
fn print_newline(&mut self, amount: isize) {
self.out.push('\n');
self.pending_indentation = 0;
self.indent(amount);
fn get_top(&self) -> PrintFrame {
*self
.print_stack
.last()
.unwrap_or(&PrintFrame::Broken { offset: 0, breaks: Breaks::Inconsistent })
}
fn indent(&mut self, amount: isize) {
self.pending_indentation += amount;
}
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) });
fn print_begin(&mut self, token: BeginToken, size: isize) {
if size > self.space {
let col = self.margin - self.space + token.offset;
self.print_stack.push(PrintFrame::Broken { offset: col, breaks: token.breaks });
} 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();
}
fn print_break(&mut self, b: BreakToken, l: isize) {
let top = self.get_top();
match top.pbreak {
PrintStackBreak::Fits => {
self.space -= b.blank_space;
self.indent(b.blank_space);
}
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;
fn print_break(&mut self, token: BreakToken, size: isize) {
let break_offset =
match self.get_top() {
PrintFrame::Fits => None,
PrintFrame::Broken { offset, breaks: Breaks::Consistent } => Some(offset),
PrintFrame::Broken { offset, breaks: Breaks::Inconsistent } => {
if size > self.space { Some(offset) } else { None }
}
}
};
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) {
let len = s.len() as isize;
// assert!(len <= space);
self.space -= len;
fn print_string(&mut self, string: &str) {
// Write the pending indent. A more concise way of doing this would be:
//
// 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
// sufficiently large, that the difference is significant on some workloads.
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.out.push_str(s);
}
fn print(&mut self, token: Token, l: isize) {
match &token {
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);
self.out.push_str(string);
self.space -= string.len() as isize;
}
// Convenience functions to talk to the printer.
/// "raw box"
pub fn rbox(&mut self, indent: usize, b: Breaks) {
self.scan_begin(BeginToken { offset: indent as isize, breaks: b })
pub fn rbox(&mut self, indent: usize, breaks: Breaks) {
self.scan_begin(BeginToken { offset: indent as isize, breaks })
}
/// Inconsistent breaking box
@ -500,8 +449,8 @@ impl Printer {
}
pub fn word<S: Into<Cow<'static, str>>>(&mut self, wrd: S) {
let s = wrd.into();
self.scan_string(s)
let string = wrd.into();
self.scan_string(string)
}
fn spaces(&mut self, n: usize) {

View file

@ -32,11 +32,6 @@ impl<T> RingBuffer<T> {
index
}
pub fn advance_left(&mut self) {
self.data.pop_front().unwrap();
self.offset += 1;
}
pub fn clear(&mut self) {
self.data.clear();
}
@ -53,6 +48,12 @@ impl<T> RingBuffer<T> {
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> {
self.data.back()
}

View file

@ -3,7 +3,11 @@ use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitor};
use std::convert::TryInto;
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>
where
T: TypeFoldable<'tcx>,

View file

@ -449,13 +449,17 @@ impl Definitions {
}
#[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);
self.table
.def_path_hash_to_index
.get(&hash)
.map(|local_def_index| LocalDefId { local_def_index })
.unwrap()
.unwrap_or_else(|| err())
}
pub fn def_path_hash_to_def_index_map(&self) -> &DefPathHashMap {

View file

@ -29,7 +29,7 @@ rustc_index = { path = "../rustc_index" }
rustc_serialize = { path = "../rustc_serialize" }
rustc_ast = { path = "../rustc_ast" }
rustc_span = { path = "../rustc_span" }
chalk-ir = "0.75.0"
chalk-ir = "0.76.0"
smallvec = { version = "1.6.1", features = ["union", "may_dangle"] }
rustc_session = { path = "../rustc_session" }
rustc_type_ir = { path = "../rustc_type_ir" }

View file

@ -266,7 +266,9 @@ impl DepNodeExt for DepNode {
/// has been removed.
fn extract_def_id<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Option<DefId> {
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 {
None
}

View file

@ -308,7 +308,7 @@ pub struct ScopeTree {
/// The reason is that semantically, until the `box` expression returns,
/// the values are still owned by their containing expressions. So
/// 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.
/// 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,
/// returns `Some(YieldData)`. If not, returns `None`.
pub fn yield_in_scope(&self, scope: Scope) -> Option<YieldData> {
self.yield_in_scope.get(&scope).cloned()
pub fn yield_in_scope(&self, scope: Scope) -> Option<&Vec<YieldData>> {
self.yield_in_scope.get(&scope)
}
/// Gives the number of expressions visited in a body.

View file

@ -1322,7 +1322,7 @@ impl<'tcx> TyCtxt<'tcx> {
/// Converts a `DefPathHash` to its corresponding `DefId` in the current compilation
/// session, if it still exists. This is used during incremental compilation to
/// 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);
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
// DefId in the tcx's `Definitions`.
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 {
// If this is a DefPathHash from an upstream crate, let the CrateStore map
// it to a DefId.

View file

@ -366,7 +366,8 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h
let target_scopes = visitor.fixup_scopes.drain(start_point..);
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 span = yield_data.span;
@ -429,7 +430,13 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h
};
let data =
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 {
debug!("resolve_expr in pessimistic_yield - marking scope {:?} for fixup", scope);
visitor.fixup_scopes.push(scope);

View file

@ -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,
// which means that the definition with this hash is guaranteed to
// 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)
}))
}
}

View file

@ -2517,6 +2517,10 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
self.visit_expr(elem);
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);
}

View file

@ -970,7 +970,13 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
};
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_suggestion_verbose(
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");
}
}
(Res::Def(DefKind::Macro(MacroKind::Bang), _), _) => {
err.span_label(span, fallback_label);
}
(Res::Def(DefKind::TyAlias, def_id), PathSource::Trait(_)) => {
err.span_label(span, "type aliases cannot be used as traits");
if self.r.session.is_nightly_build() {

View file

@ -12,9 +12,9 @@ rustc_hir = { path = "../rustc_hir" }
rustc_index = { path = "../rustc_index" }
rustc_ast = { path = "../rustc_ast" }
rustc_span = { path = "../rustc_span" }
chalk-ir = "0.75.0"
chalk-engine = "0.75.0"
chalk-solve = "0.75.0"
chalk-ir = "0.76.0"
chalk-engine = "0.76.0"
chalk-solve = "0.76.0"
smallvec = { version = "1.6.1", features = ["union", "may_dangle"] }
rustc_infer = { path = "../rustc_infer" }
rustc_trait_selection = { path = "../rustc_trait_selection" }

View file

@ -15,6 +15,7 @@ rustc_middle = { path = "../rustc_middle" }
rustc_attr = { path = "../rustc_attr" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }
rustc_graphviz = { path = "../rustc_graphviz" }
rustc_hir = { path = "../rustc_hir" }
rustc_hir_pretty = { path = "../rustc_hir_pretty" }
rustc_target = { path = "../rustc_target" }
@ -27,3 +28,4 @@ rustc_infer = { path = "../rustc_infer" }
rustc_trait_selection = { path = "../rustc_trait_selection" }
rustc_ty_utils = { path = "../rustc_ty_utils" }
rustc_lint = { path = "../rustc_lint" }
rustc_serialize = { path = "../rustc_serialize" }

View file

@ -3,6 +3,7 @@
//! is calculated in `rustc_const_eval::transform::generator` and may be a subset of the
//! types computed here.
use self::drop_ranges::DropRanges;
use super::FnCtxt;
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
use rustc_errors::pluralize;
@ -19,6 +20,8 @@ use rustc_span::Span;
use smallvec::SmallVec;
use tracing::debug;
mod drop_ranges;
struct InteriorVisitor<'a, 'tcx> {
fcx: &'a FnCtxt<'a, 'tcx>,
types: FxIndexSet<ty::GeneratorInteriorTypeCause<'tcx>>,
@ -34,6 +37,7 @@ struct InteriorVisitor<'a, 'tcx> {
guard_bindings: SmallVec<[SmallVec<[HirId; 4]>; 1]>,
guard_bindings_set: HirIdSet,
linted_values: HirIdSet,
drop_ranges: DropRanges,
}
impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> {
@ -48,9 +52,11 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> {
) {
use rustc_span::DUMMY_SP;
let ty = self.fcx.resolve_vars_if_possible(ty);
debug!(
"generator_interior: attempting to record type {:?} {:?} {:?} {:?}",
ty, scope, expr, source_span
"attempting to record type ty={:?}; hir_id={:?}; scope={:?}; expr={:?}; source_span={:?}; expr_count={:?}",
ty, hir_id, scope, expr, source_span, self.expr_count,
);
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.
debug!(
"comparing counts yield: {} self: {}, source_span = {:?}",
yield_data.expr_and_pat_count, self.expr_count, source_span
);
yield_data
.iter()
.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,
// it needs to be recorded regardless because they
// do live across this yield point.
if guard_borrowing_from_pattern
|| yield_data.expr_and_pat_count >= self.expr_count
{
Some(yield_data)
} else {
None
}
if self.drop_ranges.is_dropped_at(hir_id, yield_data.expr_and_pat_count)
{
debug!("value is dropped at yield point; not recording");
return false;
}
// If it is a borrowing happening in the guard,
// it needs to be recorded regardless because they
// do live across this yield point.
guard_borrowing_from_pattern
|| yield_data.expr_and_pat_count >= self.expr_count
})
.cloned()
})
})
.unwrap_or_else(|| {
@ -85,7 +97,6 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> {
});
if let Some(yield_data) = live_across_yield {
let ty = self.fcx.resolve_vars_if_possible(ty);
debug!(
"type in expr = {:?}, scope = {:?}, type = {:?}, count = {}, yield_span = {:?}",
expr, scope, ty, self.expr_count, yield_data.span
@ -154,7 +165,6 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> {
self.expr_count,
expr.map(|e| e.span)
);
let ty = self.fcx.resolve_vars_if_possible(ty);
if let Some((unresolved_type, unresolved_type_span)) =
self.fcx.unresolved_type_vars(&ty)
{
@ -186,6 +196,7 @@ pub fn resolve_interior<'a, 'tcx>(
guard_bindings: <_>::default(),
guard_bindings_set: <_>::default(),
linted_values: <_>::default(),
drop_ranges: drop_ranges::compute_drop_ranges(fcx, def_id, 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>) {
let mut guard_borrowing_from_pattern = false;
match &expr.kind {
ExprKind::Call(callee, args) => match &callee.kind {
ExprKind::Path(qpath) => {

View file

@ -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),
}
}
}

View file

@ -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)
});
}
}

View file

@ -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
}
}

View file

@ -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(),
)
}
}

View file

@ -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,
) {
}
}

View file

@ -43,7 +43,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let item_def_id = self.tcx.hir().local_def_id(item_id);
// 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 =
self.tcx.has_attr(item_def_id.to_def_id(), sym::rustc_dump_user_substs);

View file

@ -206,6 +206,10 @@ x--expand-yaml-anchors--remove:
run: src/ci/scripts/verify-backported-commits.sh
<<: *step
- name: ensure the stable version number is correct
run: src/ci/scripts/verify-stable-version-number.sh
<<: *step
- name: run the build
run: src/ci/scripts/run-build-from-ci.sh
env:

View 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

View file

@ -8,6 +8,7 @@ use std::mem;
use std::ops;
use rustc_ast::{LitKind, MetaItem, MetaItemKind, NestedMetaItem};
use rustc_data_structures::fx::FxHashSet;
use rustc_feature::Features;
use rustc_session::parse::ParseSess;
use rustc_span::symbol::{sym, Symbol};
@ -43,15 +44,72 @@ crate struct InvalidCfgError {
impl 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 {
NestedMetaItem::MetaItem(ref cfg) => Cfg::parse(cfg),
NestedMetaItem::MetaItem(ref cfg) => Cfg::parse_without(cfg, exclude),
NestedMetaItem::Literal(ref lit) => {
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`.
///
/// 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
/// the error is.
crate fn parse(cfg: &MetaItem) -> Result<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 => 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 }),
}
}
}
Self::parse_without(cfg, &FxHashSet::default()).map(|ret| ret.unwrap())
}
/// Checks whether the given configuration can be matched in the current session.

View file

@ -831,8 +831,9 @@ impl AttributesExt for [ast::Attribute] {
self.iter()
.filter(|attr| attr.has_name(sym::cfg))
.filter_map(|attr| single(attr.meta_item_list()?))
.filter_map(|attr| Cfg::parse(attr.meta_item()?).ok())
.filter(|cfg| !hidden_cfg.contains(cfg))
.filter_map(|attr| {
Cfg::parse_without(attr.meta_item()?, hidden_cfg).ok().flatten()
})
.fold(Cfg::True, |cfg, new_cfg| cfg & new_cfg)
} else {
Cfg::True

View file

@ -1731,12 +1731,19 @@ details.rustdoc-toggle[open] > summary.hideme::after {
}
@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 {
padding-top: 0px;
/* Sidebar should overlay main content, rather than pushing main content to the right.
Turn off `display: flex` on the body element. */
display: block;
scroll-margin-top: 45px;
}
main {

View file

@ -7,7 +7,7 @@
mod conversions;
use std::cell::RefCell;
use std::fs::File;
use std::fs::{create_dir_all, File};
use std::path::PathBuf;
use std::rc::Rc;
@ -18,13 +18,14 @@ use rustc_session::Session;
use rustdoc_json_types as types;
use crate::clean;
use crate::clean::types::{ExternalCrate, ExternalLocation};
use crate::config::RenderOptions;
use crate::docfs::PathError;
use crate::error::Error;
use crate::formats::cache::Cache;
use crate::formats::FormatRenderer;
use crate::json::conversions::{from_item_id, IntoWithTcx};
use crate::{clean, try_err};
#[derive(Clone)]
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
/// implementations filled out before they're inserted.
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
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;
if let Some(mut new_item) = self.convert_item(item) {
@ -256,10 +270,13 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
.collect(),
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.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();
Ok(())
}

View file

@ -141,6 +141,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
})
.collect::<Vec<_>>()
})
.chain([Cfg::Cfg(sym::test, None)].into_iter())
.collect();
self.cx.cache.exact_paths = self.exact_paths;

View file

@ -1,12 +1,17 @@
# 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
all:
$(call COMPILE_OBJ,"$(TMPDIR)"/extern.obj,extern.c)
ifdef IS_MSVC
$(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 bin driver.rs -L "$(TMPDIR)"
"$(TMPDIR)"/driver > "$(TMPDIR)"/output.txt

View file

@ -62,9 +62,12 @@ pub fn library_function() {
fastcall_fn_2(16, 3.5);
fastcall_fn_3(3.5);
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_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_9(1, 3.0);
}

View file

@ -11,8 +11,6 @@ fastcall_fn_1(14)
fastcall_fn_2(16, 3.5)
fastcall_fn_3(3.5)
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_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_9(1, 3.0)

View file

@ -29,3 +29,9 @@ assert-css: (".sidebar", {"display": "block", "left": "-1000px"})
// Check that the topbar is visible
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})

View file

@ -2,8 +2,8 @@
// @has - "$.index[*][?(@.name=='EnumStruct')].kind" \"enum\"
pub enum EnumStruct {
// @has - "$.index[*][?(@.name=='VariantS')].inner.variant_kind" \"struct\"
// @has - "$.index[*][?(@.name=='x')]"
// @has - "$.index[*][?(@.name=='y')]"
// @has - "$.index[*][?(@.name=='x')].kind" \"struct_field\"
// @has - "$.index[*][?(@.name=='y')].kind" \"struct_field\"
VariantS {
x: u32,
y: String,

View file

@ -2,5 +2,7 @@
// @has - "$.index[*][?(@.name=='EnumTupleStruct')].kind" \"enum\"
pub enum EnumTupleStruct {
// @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),
}

View 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 {}

View file

@ -3,6 +3,12 @@
#![crate_name = "foo"]
// @has foo/fn.foo.html
// @has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-test'
#[cfg(not(test))]
// @has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-doctest'
#[cfg(not(doctest))]
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() {}

View file

@ -26,7 +26,7 @@ pub struct Hyperdulia;
// @has 'oud/struct.Oystercatcher.html'
// @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"
#[cfg(all(feature = "solecism", feature = "oystercatcher"))]
pub struct Oystercatcher;

View file

@ -18,7 +18,7 @@ async fn fut() {}
async fn fut_arg<T>(_: T) {}
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();
drop(x);
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() {
// 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!();
if non_sync().fmt(f).unwrap() == () {
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) {}
pub fn pass_assert() {
assert_send(local_dropped_before_await());
//~^ ERROR future cannot be sent between threads safely
assert_send(non_send_temporary_in_match());
//~^ ERROR future cannot be sent between threads safely
assert_send(non_sync_with_method_call());
//~^ 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());
}

View file

@ -1,28 +1,5 @@
error: future cannot be sent between threads safely
--> $DIR/async-fn-nonsend.rs:49: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
--> $DIR/async-fn-nonsend.rs:68:17
|
LL | assert_send(non_send_temporary_in_match());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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
|
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,
| ^^^^^^ await occurs here, with `non_send()` maybe used later
| ^^^^^^ await occurs here, with `Some(non_send())` maybe used later
...
LL | }
| - `non_send()` is later dropped here
| - `Some(non_send())` is later dropped here
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) {}
| ^^^^ required by this bound in `assert_send`
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());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `non_sync_with_method_call` is not `Send`
|
= 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
--> $DIR/async-fn-nonsend.rs:42:14
--> $DIR/async-fn-nonsend.rs:46:14
|
LL | let f: &mut std::fmt::Formatter = panic!();
| - has type `&mut Formatter<'_>` which is not `Send`
LL | if non_sync().fmt(f).unwrap() == () {
LL | let f: &mut std::fmt::Formatter = &mut get_formatter();
| --------------- has type `Formatter<'_>` which is not `Send`
...
LL | fut().await;
| ^^^^^^ await occurs here, with `f` maybe used later
| ^^^^^^ await occurs here, with `get_formatter()` maybe used later
LL | }
LL | }
| - `f` is later dropped here
| - `get_formatter()` is later dropped here
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) {}
| ^^^^ required by this bound in `assert_send`
error: aborting due to 3 previous errors
error: aborting due to 2 previous errors

View 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() {}

View 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`.

View file

@ -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
//~| 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 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`

View file

@ -34,30 +34,6 @@ note: the type is part of the `async fn` body because of this `await`
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[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
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0698`.

View 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();
}

View 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) {}

View 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

View 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) {}

View 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) {}

View 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

View 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
}

View file

@ -11,16 +11,10 @@ LL | struct SemiTransparent;
| ----------------------- similarly named unit struct `SemiTransparent` defined here
...
LL | semitransparent;
| ^^^^^^^^^^^^^^^ not a value
|
help: use `!` to invoke the macro
|
LL | semitransparent!;
| +
help: a unit struct with a similar name exists
|
LL | SemiTransparent;
| ~~~~~~~~~~~~~~~
| ^^^^^^^^^^^^^^^
| |
| not a value
| help: a unit struct with a similar name exists: `SemiTransparent`
error[E0423]: expected value, found macro `opaque`
--> $DIR/rustc-macro-transparency.rs:30:5
@ -29,16 +23,10 @@ LL | struct Opaque;
| -------------- similarly named unit struct `Opaque` defined here
...
LL | opaque;
| ^^^^^^ not a value
|
help: use `!` to invoke the macro
|
LL | opaque!;
| +
help: a unit struct with a similar name exists
|
LL | Opaque;
| ~~~~~~
| ^^^^^^
| |
| not a value
| help: a unit struct with a similar name exists (notice the capitalization): `Opaque`
error: aborting due to 3 previous errors

View file

@ -13,7 +13,7 @@ async fn wheeee<T>(t: T) {
}
async fn yes() {
wheeee(No {}).await; //~ ERROR `No` held across
wheeee(&No {}).await; //~ ERROR `No` held across
}
fn main() {

View file

@ -1,8 +1,8 @@
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;
| ^^^^^ ------ the value is held across this suspend point
LL | wheeee(&No {}).await;
| ^^^^^ ------ the value is held across this suspend point
|
note: the lint level is defined here
--> $DIR/dedup.rs:3:9
@ -10,10 +10,10 @@ note: the lint level is defined here
LL | #![deny(must_not_suspend)]
| ^^^^^^^^^^^^^^^^
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

View 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`
}

View file

@ -1,4 +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`
}

View file

@ -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`
--> $DIR/resolve-hint-macro.rs:2:5
--> $DIR/resolve-hint-macro.rs:3:5
|
LL | assert_eq(1, 1);
| ^^^^^^^^^ not a function
@ -9,6 +25,29 @@ help: use `!` to invoke the macro
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`.

View file

@ -1,6 +1,6 @@
// check-pass
#![feature(let_chains)]
#![feature(if_let_guard, let_chains)]
use std::ops::Range;
@ -16,6 +16,16 @@ fn main() {
&& 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 Some(ref first) = opt && let second = first && let _third = second {

View file

@ -1,6 +1,6 @@
// run-pass
#![feature(let_chains)]
#![feature(if_let_guard, let_chains)]
fn check_if_let(opt: Option<Option<Option<i32>>>, value: i32) -> bool {
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 {
while let Some(first) = opt
&& 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))), 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))), 9), false);
}

View file

@ -500,7 +500,19 @@ impl<'test> TestCx<'test> {
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 self.props.pretty_compare_only {