diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 4489a424c0d50..11afd359e5ad3 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -710,6 +710,12 @@ impl Pat {
}
}
+impl From
> for Pat {
+ fn from(value: P) -> Self {
+ *value
+ }
+}
+
/// A single field in a struct pattern.
///
/// Patterns like the fields of `Foo { x, ref y, ref mut z }`
@@ -1553,17 +1559,23 @@ impl Expr {
)
}
- /// Creates a dummy `P`.
+ /// Creates a dummy `Expr`.
///
/// Should only be used when it will be replaced afterwards or as a return value when an error was encountered.
- pub fn dummy() -> P {
- P(Expr {
+ pub fn dummy() -> Expr {
+ Expr {
id: DUMMY_NODE_ID,
kind: ExprKind::Dummy,
span: DUMMY_SP,
attrs: ThinVec::new(),
tokens: None,
- })
+ }
+ }
+}
+
+impl From> for Expr {
+ fn from(value: P) -> Self {
+ *value
}
}
@@ -2374,6 +2386,12 @@ impl Clone for Ty {
}
}
+impl From> for Ty {
+ fn from(value: P) -> Self {
+ *value
+ }
+}
+
impl Ty {
pub fn peel_refs(&self) -> &Self {
let mut final_ty = self;
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index 71a47dcfcba2b..07fbe8045fc2f 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -168,7 +168,7 @@ pub trait MutVisitor: Sized + MutVisitorResult {
walk_flat_map_arm(self, arm)
}
- fn visit_pat(&mut self, p: &mut P) {
+ fn visit_pat(&mut self, p: &mut Pat) {
walk_pat(self, p);
}
@@ -176,7 +176,7 @@ pub trait MutVisitor: Sized + MutVisitorResult {
walk_anon_const(self, c);
}
- fn visit_expr(&mut self, e: &mut P) {
+ fn visit_expr(&mut self, e: &mut Expr) {
walk_expr(self, e);
}
@@ -194,7 +194,7 @@ pub trait MutVisitor: Sized + MutVisitorResult {
walk_generic_arg(self, arg);
}
- fn visit_ty(&mut self, t: &mut P) {
+ fn visit_ty(&mut self, t: &mut Ty) {
walk_ty(self, t);
}
diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs
index da01e3e9607bb..fe44350863c9a 100644
--- a/compiler/rustc_builtin_macros/src/cfg_eval.rs
+++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs
@@ -155,7 +155,7 @@ impl CfgEval<'_> {
impl MutVisitor for CfgEval<'_> {
#[instrument(level = "trace", skip(self))]
- fn visit_expr(&mut self, expr: &mut P) {
+ fn visit_expr(&mut self, expr: &mut ast::Expr) {
self.0.configure_expr(expr, false);
mut_visit::walk_expr(self, expr);
}
diff --git a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs
index 0794192621a93..3a20b39798d7b 100644
--- a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs
@@ -1,5 +1,4 @@
use ast::HasAttrs;
-use ast::ptr::P;
use rustc_ast::mut_visit::MutVisitor;
use rustc_ast::visit::BoundKind;
use rustc_ast::{
@@ -378,11 +377,11 @@ struct TypeSubstitution<'a> {
}
impl<'a> ast::mut_visit::MutVisitor for TypeSubstitution<'a> {
- fn visit_ty(&mut self, ty: &mut P) {
+ fn visit_ty(&mut self, ty: &mut ast::Ty) {
if let Some(name) = ty.kind.is_simple_path()
&& name == self.from_name
{
- **ty = self.to_ty.clone();
+ *ty = self.to_ty.clone();
self.rewritten = true;
} else {
ast::mut_visit::walk_ty(self, ty);
diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs
index c5c13ac097a27..b07d9a5cfca8c 100644
--- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs
+++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs
@@ -114,7 +114,7 @@ fn match_args_from_caller_to_enzyme<'ll>(
let mul = unsafe {
llvm::LLVMBuildMul(
builder.llbuilder,
- cx.get_const_i64(elem_bytes_size),
+ cx.get_const_int(cx.type_i64(), elem_bytes_size),
next_outer_arg,
UNNAMED,
)
@@ -385,7 +385,7 @@ fn generate_enzyme_call<'ll>(
if attrs.width > 1 {
let enzyme_width = cx.create_metadata("enzyme_width".to_string()).unwrap();
args.push(cx.get_metadata_value(enzyme_width));
- args.push(cx.get_const_i64(attrs.width as u64));
+ args.push(cx.get_const_int(cx.type_i64(), attrs.width as u64));
}
let has_sret = has_sret(outer_fn);
diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs
index 3cfa96393e920..ae5add59322fe 100644
--- a/compiler/rustc_codegen_llvm/src/common.rs
+++ b/compiler/rustc_codegen_llvm/src/common.rs
@@ -99,14 +99,14 @@ impl<'ll, CX: Borrow>> BackendTypes for GenericCx<'ll, CX> {
type DIVariable = &'ll llvm::debuginfo::DIVariable;
}
-impl<'ll> CodegenCx<'ll, '_> {
+impl<'ll, CX: Borrow>> GenericCx<'ll, CX> {
pub(crate) fn const_array(&self, ty: &'ll Type, elts: &[&'ll Value]) -> &'ll Value {
let len = u64::try_from(elts.len()).expect("LLVMConstArray2 elements len overflow");
unsafe { llvm::LLVMConstArray2(ty, elts.as_ptr(), len) }
}
pub(crate) fn const_bytes(&self, bytes: &[u8]) -> &'ll Value {
- bytes_in_context(self.llcx, bytes)
+ bytes_in_context(self.llcx(), bytes)
}
pub(crate) fn const_get_elt(&self, v: &'ll Value, idx: u64) -> &'ll Value {
diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs
index bff95ea46fa7a..0324dff6ff256 100644
--- a/compiler/rustc_codegen_llvm/src/context.rs
+++ b/compiler/rustc_codegen_llvm/src/context.rs
@@ -679,11 +679,8 @@ impl<'ll, CX: Borrow>> GenericCx<'ll, CX> {
llvm::LLVMMetadataAsValue(self.llcx(), metadata)
}
- // FIXME(autodiff): We should split `ConstCodegenMethods` to pull the reusable parts
- // onto a trait that is also implemented for GenericCx.
- pub(crate) fn get_const_i64(&self, n: u64) -> &'ll Value {
- let ty = unsafe { llvm::LLVMInt64TypeInContext(self.llcx()) };
- unsafe { llvm::LLVMConstInt(ty, n, llvm::False) }
+ pub(crate) fn get_const_int(&self, ty: &'ll Type, val: u64) -> &'ll Value {
+ unsafe { llvm::LLVMConstInt(ty, val, llvm::False) }
}
pub(crate) fn get_function(&self, name: &str) -> Option<&'ll Value> {
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index 3cfeb01ea4771..9fd524ef45cd0 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -1768,7 +1768,7 @@ impl InvocationCollectorNode for ast::Crate {
}
}
-impl InvocationCollectorNode for P {
+impl InvocationCollectorNode for ast::Ty {
type OutputTy = P;
const KIND: AstFragmentKind = AstFragmentKind::Ty;
fn to_annotatable(self) -> Annotatable {
@@ -1791,7 +1791,7 @@ impl InvocationCollectorNode for P {
}
}
-impl InvocationCollectorNode for P {
+impl InvocationCollectorNode for ast::Pat {
type OutputTy = P;
const KIND: AstFragmentKind = AstFragmentKind::Pat;
fn to_annotatable(self) -> Annotatable {
@@ -1814,11 +1814,11 @@ impl InvocationCollectorNode for P {
}
}
-impl InvocationCollectorNode for P {
+impl InvocationCollectorNode for ast::Expr {
type OutputTy = P;
const KIND: AstFragmentKind = AstFragmentKind::Expr;
fn to_annotatable(self) -> Annotatable {
- Annotatable::Expr(self)
+ Annotatable::Expr(P(self))
}
fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
fragment.make_expr()
@@ -1955,29 +1955,29 @@ impl DummyAstNode for ast::Crate {
}
}
-impl DummyAstNode for P {
+impl DummyAstNode for ast::Ty {
fn dummy() -> Self {
- P(ast::Ty {
+ ast::Ty {
id: DUMMY_NODE_ID,
kind: TyKind::Dummy,
span: Default::default(),
tokens: Default::default(),
- })
+ }
}
}
-impl DummyAstNode for P {
+impl DummyAstNode for ast::Pat {
fn dummy() -> Self {
- P(ast::Pat {
+ ast::Pat {
id: DUMMY_NODE_ID,
kind: PatKind::Wild,
span: Default::default(),
tokens: Default::default(),
- })
+ }
}
}
-impl DummyAstNode for P {
+impl DummyAstNode for ast::Expr {
fn dummy() -> Self {
ast::Expr::dummy()
}
@@ -1985,7 +1985,7 @@ impl DummyAstNode for P {
impl DummyAstNode for AstNodeWrapper, MethodReceiverTag> {
fn dummy() -> Self {
- AstNodeWrapper::new(ast::Expr::dummy(), MethodReceiverTag)
+ AstNodeWrapper::new(P(ast::Expr::dummy()), MethodReceiverTag)
}
}
@@ -2272,7 +2272,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
}
}
- fn visit_node + DummyAstNode>(
+ fn visit_node> + DummyAstNode>(
&mut self,
node: &mut Node,
) {
@@ -2297,6 +2297,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
*node = self
.collect_attr((attr, pos, derives), n.to_annotatable(), Node::KIND)
.make_ast::()
+ .into()
}
},
None if node.is_mac_call() => {
@@ -2304,7 +2305,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
let (mac, attrs, _) = n.take_mac_call();
self.check_attributes(&attrs, &mac);
- *node = self.collect_bang(mac, Node::KIND).make_ast::()
+ *node = self.collect_bang(mac, Node::KIND).make_ast::().into()
}
None if node.delegation().is_some() => unreachable!(),
None => {
@@ -2414,15 +2415,15 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
self.visit_node(node)
}
- fn visit_ty(&mut self, node: &mut P) {
+ fn visit_ty(&mut self, node: &mut ast::Ty) {
self.visit_node(node)
}
- fn visit_pat(&mut self, node: &mut P) {
+ fn visit_pat(&mut self, node: &mut ast::Pat) {
self.visit_node(node)
}
- fn visit_expr(&mut self, node: &mut P) {
+ fn visit_expr(&mut self, node: &mut ast::Expr) {
// FIXME: Feature gating is performed inconsistently between `Expr` and `OptExpr`.
if let Some(attr) = node.attrs.first() {
self.cfg().maybe_emit_expr_attr_err(attr);
diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs
index 3dcb20c8c7682..2c486a02bdf18 100644
--- a/compiler/rustc_expand/src/placeholders.rs
+++ b/compiler/rustc_expand/src/placeholders.rs
@@ -332,9 +332,9 @@ impl MutVisitor for PlaceholderExpander {
}
}
- fn visit_expr(&mut self, expr: &mut P) {
+ fn visit_expr(&mut self, expr: &mut ast::Expr) {
match expr.kind {
- ast::ExprKind::MacCall(_) => *expr = self.remove(expr.id).make_expr(),
+ ast::ExprKind::MacCall(_) => *expr = *self.remove(expr.id).make_expr(),
_ => walk_expr(self, expr),
}
}
@@ -399,16 +399,16 @@ impl MutVisitor for PlaceholderExpander {
stmts
}
- fn visit_pat(&mut self, pat: &mut P) {
+ fn visit_pat(&mut self, pat: &mut ast::Pat) {
match pat.kind {
- ast::PatKind::MacCall(_) => *pat = self.remove(pat.id).make_pat(),
+ ast::PatKind::MacCall(_) => *pat = *self.remove(pat.id).make_pat(),
_ => walk_pat(self, pat),
}
}
- fn visit_ty(&mut self, ty: &mut P) {
+ fn visit_ty(&mut self, ty: &mut ast::Ty) {
match ty.kind {
- ast::TyKind::MacCall(_) => *ty = self.remove(ty.id).make_ty(),
+ ast::TyKind::MacCall(_) => *ty = *self.remove(ty.id).make_ty(),
_ => walk_ty(self, ty),
}
}
diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl
index 46ae414652870..f92f830780801 100644
--- a/compiler/rustc_lint/messages.ftl
+++ b/compiler/rustc_lint/messages.ftl
@@ -738,7 +738,8 @@ lint_redundant_semicolons =
[true] semicolons
*[false] semicolon
}
- .suggestion = remove {$multiple ->
+
+lint_redundant_semicolons_suggestion = remove {$multiple_semicolons ->
[true] these semicolons
*[false] this semicolon
}
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index 3d17dfbc45198..0b8c68404f16c 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -1538,8 +1538,16 @@ pub(crate) struct PassByValueDiag {
#[diag(lint_redundant_semicolons)]
pub(crate) struct RedundantSemicolonsDiag {
pub multiple: bool,
- #[suggestion(code = "", applicability = "maybe-incorrect")]
- pub suggestion: Span,
+ #[subdiagnostic]
+ pub suggestion: Option,
+}
+
+#[derive(Subdiagnostic)]
+#[suggestion(lint_redundant_semicolons_suggestion, code = "", applicability = "maybe-incorrect")]
+pub(crate) struct RedundantSemicolonsSuggestion {
+ pub multiple_semicolons: bool,
+ #[primary_span]
+ pub span: Span,
}
// traits.rs
diff --git a/compiler/rustc_lint/src/redundant_semicolon.rs b/compiler/rustc_lint/src/redundant_semicolon.rs
index b43e4938b736c..f6d2fbe42618e 100644
--- a/compiler/rustc_lint/src/redundant_semicolon.rs
+++ b/compiler/rustc_lint/src/redundant_semicolon.rs
@@ -2,7 +2,7 @@ use rustc_ast::{Block, StmtKind};
use rustc_session::{declare_lint, declare_lint_pass};
use rustc_span::Span;
-use crate::lints::RedundantSemicolonsDiag;
+use crate::lints::{RedundantSemicolonsDiag, RedundantSemicolonsSuggestion};
use crate::{EarlyContext, EarlyLintPass, LintContext};
declare_lint! {
@@ -44,16 +44,21 @@ impl EarlyLintPass for RedundantSemicolons {
fn maybe_lint_redundant_semis(cx: &EarlyContext<'_>, seq: &mut Option<(Span, bool)>) {
if let Some((span, multiple)) = seq.take() {
- // FIXME: Find a better way of ignoring the trailing
- // semicolon from macro expansion
if span == rustc_span::DUMMY_SP {
return;
}
+ // Ignore redundant semicolons inside macro expansion.(issue #142143)
+ let suggestion = if span.from_expansion() {
+ None
+ } else {
+ Some(RedundantSemicolonsSuggestion { multiple_semicolons: multiple, span })
+ };
+
cx.emit_span_lint(
REDUNDANT_SEMICOLONS,
span,
- RedundantSemicolonsDiag { multiple, suggestion: span },
+ RedundantSemicolonsDiag { multiple, suggestion },
);
}
}
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 93489aa8ee948..93c76c47f060b 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -4087,7 +4087,7 @@ impl<'a> CondChecker<'a> {
}
impl MutVisitor for CondChecker<'_> {
- fn visit_expr(&mut self, e: &mut P) {
+ fn visit_expr(&mut self, e: &mut Expr) {
self.depth += 1;
use ForbiddenLetReason::*;
diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index 7a226136e2353..64653ee2a04c9 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -1094,7 +1094,7 @@ impl<'a> Parser<'a> {
fn make_all_value_bindings_mutable(pat: &mut P) -> bool {
struct AddMut(bool);
impl MutVisitor for AddMut {
- fn visit_pat(&mut self, pat: &mut P) {
+ fn visit_pat(&mut self, pat: &mut Pat) {
if let PatKind::Ident(BindingMode(ByRef::No, m @ Mutability::Not), ..) =
&mut pat.kind
{
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 406a6bd335a74..04ca0b75c3113 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -3118,6 +3118,7 @@ pub(crate) mod dep_tracking {
}
impl_dep_tracking_hash_via_hash!(
+ (),
AutoDiff,
bool,
usize,
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index f76f258d00de9..9ca405333f43d 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -802,6 +802,7 @@ mod desc {
"either a boolean (`yes`, `no`, `on`, `off`, etc), or a non-negative number";
pub(crate) const parse_llvm_module_flag: &str = ":::. Type must currently be `u32`. Behavior should be one of (`error`, `warning`, `require`, `override`, `append`, `appendunique`, `max`, `min`)";
pub(crate) const parse_function_return: &str = "`keep` or `thunk-extern`";
+ pub(crate) const parse_wasm_c_abi: &str = "`spec`";
pub(crate) const parse_mir_include_spans: &str =
"either a boolean (`yes`, `no`, `on`, `off`, etc), or `nll` (default: `nll`)";
pub(crate) const parse_align: &str = "a number that is a power of 2 between 1 and 2^29";
@@ -1897,6 +1898,10 @@ pub mod parse {
true
}
+ pub(crate) fn parse_wasm_c_abi(_slot: &mut (), v: Option<&str>) -> bool {
+ v == Some("spec")
+ }
+
pub(crate) fn parse_mir_include_spans(slot: &mut MirIncludeSpans, v: Option<&str>) -> bool {
*slot = match v {
Some("on" | "yes" | "y" | "true") | None => MirIncludeSpans::On,
@@ -2631,6 +2636,11 @@ written to standard error output)"),
Requires `-Clto[=[fat,yes]]`"),
wasi_exec_model: Option = (None, parse_wasi_exec_model, [TRACKED],
"whether to build a wasi command or reactor"),
+ // This option only still exists to provide a more gradual transition path for people who need
+ // the spec-complaint C ABI to be used.
+ // FIXME remove this after a couple releases
+ wasm_c_abi: () = ((), parse_wasm_c_abi, [TRACKED],
+ "use spec-compliant C ABI for `wasm32-unknown-unknown` (deprecated, always enabled)"),
write_long_types_to_disk: bool = (true, parse_bool, [UNTRACKED],
"whether long type names should be written to files instead of being printed in errors"),
// tidy-alphabetical-end
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index 010355abd7817..7a49f0040722c 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -6,33 +6,36 @@
//! to a new platform, and allows for an unprecedented level of control over how
//! the compiler works.
//!
-//! # Using custom targets
+//! # Using targets and target.json
//!
-//! A target tuple, as passed via `rustc --target=TUPLE`, will first be
-//! compared against the list of built-in targets. This is to ease distributing
-//! rustc (no need for configuration files) and also to hold these built-in
-//! targets as immutable and sacred. If `TUPLE` is not one of the built-in
-//! targets, rustc will check if a file named `TUPLE` exists. If it does, it
-//! will be loaded as the target configuration. If the file does not exist,
-//! rustc will search each directory in the environment variable
-//! `RUST_TARGET_PATH` for a file named `TUPLE.json`. The first one found will
-//! be loaded. If no file is found in any of those directories, a fatal error
-//! will be given.
+//! Invoking "rustc --target=${TUPLE}" will result in rustc initiating the [`Target::search`] by
+//! - checking if "$TUPLE" is a complete path to a json (ending with ".json") and loading if so
+//! - checking builtin targets for "${TUPLE}"
+//! - checking directories in "${RUST_TARGET_PATH}" for "${TUPLE}.json"
+//! - checking for "${RUSTC_SYSROOT}/lib/rustlib/${TUPLE}/target.json"
//!
-//! Projects defining their own targets should use
-//! `--target=path/to/my-awesome-platform.json` instead of adding to
-//! `RUST_TARGET_PATH`.
+//! Code will then be compiled using the first discovered target spec.
//!
//! # Defining a new target
//!
-//! Targets are defined using [JSON](https://json.org/). The `Target` struct in
-//! this module defines the format the JSON file should take, though each
-//! underscore in the field names should be replaced with a hyphen (`-`) in the
-//! JSON file. Some fields are required in every target specification, such as
-//! `llvm-target`, `target-endian`, `target-pointer-width`, `data-layout`,
-//! `arch`, and `os`. In general, options passed to rustc with `-C` override
-//! the target's settings, though `target-feature` and `link-args` will *add*
-//! to the list specified by the target, rather than replace.
+//! Targets are defined using a struct which additionally has serialization to and from [JSON].
+//! The `Target` struct in this module loosely corresponds with the format the JSON takes.
+//! We usually try to make the fields equivalent but we have given up on a 1:1 correspondence
+//! between the JSON and the actual structure itself.
+//!
+//! Some fields are required in every target spec, and they should be embedded in Target directly.
+//! Optional keys are in TargetOptions, but Target derefs to it, for no practical difference.
+//! Most notable is the "data-layout" field which specifies Rust's notion of sizes and alignments
+//! for several key types, such as f64, pointers, and so on.
+//!
+//! At one point we felt `-C` options should override the target's settings, like in C compilers,
+//! but that was an essentially-unmarked route for making code incorrect and Rust unsound.
+//! Confronted with programmers who prefer a compiler with a good UX instead of a lethal weapon,
+//! we have almost-entirely recanted that notion, though we hope "target modifiers" will offer
+//! a way to have a decent UX yet still extend the necessary compiler controls, without
+//! requiring a new target spec for each and every single possible target micro-variant.
+//!
+//! [JSON]: https://json.org
use std::borrow::Cow;
use std::collections::BTreeMap;
diff --git a/library/alloc/src/collections/linked_list/tests.rs b/library/alloc/src/collections/linked_list/tests.rs
index 812fe229a0fc5..410e67d3fdb08 100644
--- a/library/alloc/src/collections/linked_list/tests.rs
+++ b/library/alloc/src/collections/linked_list/tests.rs
@@ -1,6 +1,4 @@
-// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint
-#![allow(static_mut_refs)]
-
+use std::cell::Cell;
use std::panic::{AssertUnwindSafe, catch_unwind};
use std::thread;
@@ -58,48 +56,33 @@ fn list_from(v: &[T]) -> LinkedList {
v.iter().cloned().collect()
}
+/// Starting from the head of the LinkedList,
+/// follow the next links, while checking the prev links,
+/// and check that length equals the count of visited nodes.
fn check_links(list: &LinkedList) {
- unsafe {
- let mut len = 0;
- let mut last_ptr: Option<&Node> = None;
- let mut node_ptr: &Node;
- match list.head {
- None => {
- // tail node should also be None.
- assert!(list.tail.is_none());
- assert_eq!(0, list.len);
- return;
- }
- Some(node) => node_ptr = &*node.as_ptr(),
- }
- loop {
- match (last_ptr, node_ptr.prev) {
- (None, None) => {}
- (None, _) => panic!("prev link for head"),
- (Some(p), Some(pptr)) => {
- assert_eq!(p as *const Node, pptr.as_ptr() as *const Node);
- }
- _ => panic!("prev link is none, not good"),
- }
- match node_ptr.next {
- Some(next) => {
- last_ptr = Some(node_ptr);
- node_ptr = &*next.as_ptr();
- len += 1;
- }
- None => {
- len += 1;
- break;
- }
- }
- }
-
- // verify that the tail node points to the last node.
- let tail = list.tail.as_ref().expect("some tail node").as_ref();
- assert_eq!(tail as *const Node, node_ptr as *const Node);
- // check that len matches interior links.
- assert_eq!(len, list.len);
+ let mut node: &Node = if let Some(node) = list.head {
+ // SAFETY: depends on correctness of LinkedList
+ unsafe { &*node.as_ptr() }
+ } else {
+ assert!(list.tail.is_none(), "empty list should have no tail node");
+ assert_eq!(list.len, 0, "empty list should have length 0");
+ return;
+ };
+
+ assert!(node.prev.is_none(), "head node should not have a prev link");
+ let mut prev;
+ let mut len = 1;
+ while let Some(next) = node.next {
+ prev = node;
+ // SAFETY: depends on correctness of LinkedList
+ node = unsafe { &*next.as_ptr() };
+ len += 1;
+ assert_eq!(node.prev.expect("missing prev link"), prev.into(), "bad prev link");
}
+
+ let tail = list.tail.expect("list is non-empty, so there should be a tail node");
+ assert_eq!(tail, node.into(), "tail node points to the last node");
+ assert_eq!(len, list.len, "len matches interior links");
}
#[test]
@@ -1027,21 +1010,26 @@ fn extract_if_drop_panic_leak() {
assert_eq!(d7.dropped(), 1);
}
-#[test]
-#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
-fn extract_if_pred_panic_leak() {
- static mut DROPS: i32 = 0;
+macro_rules! struct_with_counted_drop {
+ ($struct_name:ident$(($elt_ty:ty))?, $drop_counter:ident $(=> $drop_stmt:expr)?) => {
+ thread_local! {static $drop_counter: Cell = Cell::new(0);}
+
+ struct $struct_name$(($elt_ty))?;
- #[derive(Debug)]
- struct D(u32);
+ impl Drop for $struct_name {
+ fn drop(&mut self) {
+ $drop_counter.set($drop_counter.get() + 1);
- impl Drop for D {
- fn drop(&mut self) {
- unsafe {
- DROPS += 1;
+ $($drop_stmt(self))?
}
}
- }
+ };
+}
+
+#[test]
+#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
+fn extract_if_pred_panic_leak() {
+ struct_with_counted_drop!(D(u32), DROPS);
let mut q = LinkedList::new();
q.push_back(D(3));
@@ -1053,26 +1041,17 @@ fn extract_if_pred_panic_leak() {
q.push_front(D(1));
q.push_front(D(0));
- catch_unwind(AssertUnwindSafe(|| {
+ _ = catch_unwind(AssertUnwindSafe(|| {
q.extract_if(|item| if item.0 >= 2 { panic!() } else { true }).for_each(drop)
- }))
- .ok();
+ }));
- assert_eq!(unsafe { DROPS }, 2); // 0 and 1
+ assert_eq!(DROPS.get(), 2); // 0 and 1
assert_eq!(q.len(), 6);
}
#[test]
fn test_drop() {
- static mut DROPS: i32 = 0;
- struct Elem;
- impl Drop for Elem {
- fn drop(&mut self) {
- unsafe {
- DROPS += 1;
- }
- }
- }
+ struct_with_counted_drop!(Elem, DROPS);
let mut ring = LinkedList::new();
ring.push_back(Elem);
@@ -1081,20 +1060,12 @@ fn test_drop() {
ring.push_front(Elem);
drop(ring);
- assert_eq!(unsafe { DROPS }, 4);
+ assert_eq!(DROPS.get(), 4);
}
#[test]
fn test_drop_with_pop() {
- static mut DROPS: i32 = 0;
- struct Elem;
- impl Drop for Elem {
- fn drop(&mut self) {
- unsafe {
- DROPS += 1;
- }
- }
- }
+ struct_with_counted_drop!(Elem, DROPS);
let mut ring = LinkedList::new();
ring.push_back(Elem);
@@ -1104,23 +1075,15 @@ fn test_drop_with_pop() {
drop(ring.pop_back());
drop(ring.pop_front());
- assert_eq!(unsafe { DROPS }, 2);
+ assert_eq!(DROPS.get(), 2);
drop(ring);
- assert_eq!(unsafe { DROPS }, 4);
+ assert_eq!(DROPS.get(), 4);
}
#[test]
fn test_drop_clear() {
- static mut DROPS: i32 = 0;
- struct Elem;
- impl Drop for Elem {
- fn drop(&mut self) {
- unsafe {
- DROPS += 1;
- }
- }
- }
+ struct_with_counted_drop!(Elem, DROPS);
let mut ring = LinkedList::new();
ring.push_back(Elem);
@@ -1128,30 +1091,16 @@ fn test_drop_clear() {
ring.push_back(Elem);
ring.push_front(Elem);
ring.clear();
- assert_eq!(unsafe { DROPS }, 4);
+ assert_eq!(DROPS.get(), 4);
drop(ring);
- assert_eq!(unsafe { DROPS }, 4);
+ assert_eq!(DROPS.get(), 4);
}
#[test]
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
fn test_drop_panic() {
- static mut DROPS: i32 = 0;
-
- struct D(bool);
-
- impl Drop for D {
- fn drop(&mut self) {
- unsafe {
- DROPS += 1;
- }
-
- if self.0 {
- panic!("panic in `drop`");
- }
- }
- }
+ struct_with_counted_drop!(D(bool), DROPS => |this: &D| if this.0 { panic!("panic in `drop`"); } );
let mut q = LinkedList::new();
q.push_back(D(false));
@@ -1165,7 +1114,7 @@ fn test_drop_panic() {
catch_unwind(move || drop(q)).ok();
- assert_eq!(unsafe { DROPS }, 8);
+ assert_eq!(DROPS.get(), 8);
}
#[test]
diff --git a/library/core/src/array/iter.rs b/library/core/src/array/iter.rs
index 90f76d6d4c7be..a59b2f05305d2 100644
--- a/library/core/src/array/iter.rs
+++ b/library/core/src/array/iter.rs
@@ -224,6 +224,13 @@ impl IntoIter {
}
}
+#[stable(feature = "array_value_iter_default", since = "CURRENT_RUSTC_VERSION")]
+impl Default for IntoIter {
+ fn default() -> Self {
+ IntoIter::empty()
+ }
+}
+
#[stable(feature = "array_value_iter_impls", since = "1.40.0")]
impl Iterator for IntoIter {
type Item = T;
diff --git a/library/core/src/io/borrowed_buf.rs b/library/core/src/io/borrowed_buf.rs
index f86abf7f1e91c..6bd9c18e00bdf 100644
--- a/library/core/src/io/borrowed_buf.rs
+++ b/library/core/src/io/borrowed_buf.rs
@@ -281,10 +281,10 @@ impl<'a> BorrowedCursor<'a> {
/// Panics if there are less than `n` bytes initialized.
#[inline]
pub fn advance(&mut self, n: usize) -> &mut Self {
- let filled = self.buf.filled.strict_add(n);
- assert!(filled <= self.buf.init);
+ // The substraction cannot underflow by invariant of this type.
+ assert!(n <= self.buf.init - self.buf.filled);
- self.buf.filled = filled;
+ self.buf.filled += n;
self
}
diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs
index 32c306be94ecd..b0084edf2f85c 100644
--- a/library/proc_macro/src/lib.rs
+++ b/library/proc_macro/src/lib.rs
@@ -45,6 +45,7 @@ mod diagnostic;
mod escape;
mod to_tokens;
+use core::ops::BitOr;
use std::ffi::CStr;
use std::ops::{Range, RangeBounds};
use std::path::PathBuf;
@@ -237,7 +238,7 @@ impl Default for TokenStream {
}
#[unstable(feature = "proc_macro_quote", issue = "54722")]
-pub use quote::{quote, quote_span};
+pub use quote::{HasIterator, RepInterp, ThereIsNoIteratorInRepetition, ext, quote, quote_span};
fn tree_to_bridge_tree(
tree: TokenTree,
diff --git a/library/proc_macro/src/quote.rs b/library/proc_macro/src/quote.rs
index bcb15912bb65e..dbb55cd9fb300 100644
--- a/library/proc_macro/src/quote.rs
+++ b/library/proc_macro/src/quote.rs
@@ -5,9 +5,183 @@
//! items from `proc_macro`, to build a `proc_macro::TokenStream`.
use crate::{
- Delimiter, Group, Ident, Literal, Punct, Spacing, Span, ToTokens, TokenStream, TokenTree,
+ BitOr, Delimiter, Group, Ident, Literal, Punct, Spacing, Span, ToTokens, TokenStream, TokenTree,
};
+#[doc(hidden)]
+pub struct HasIterator; // True
+#[doc(hidden)]
+pub struct ThereIsNoIteratorInRepetition; // False
+
+impl BitOr for ThereIsNoIteratorInRepetition {
+ type Output = ThereIsNoIteratorInRepetition;
+ fn bitor(self, _rhs: ThereIsNoIteratorInRepetition) -> ThereIsNoIteratorInRepetition {
+ ThereIsNoIteratorInRepetition
+ }
+}
+
+impl BitOr for HasIterator {
+ type Output = HasIterator;
+ fn bitor(self, _rhs: ThereIsNoIteratorInRepetition) -> HasIterator {
+ HasIterator
+ }
+}
+
+impl BitOr for ThereIsNoIteratorInRepetition {
+ type Output = HasIterator;
+ fn bitor(self, _rhs: HasIterator) -> HasIterator {
+ HasIterator
+ }
+}
+
+impl BitOr for HasIterator {
+ type Output = HasIterator;
+ fn bitor(self, _rhs: HasIterator) -> HasIterator {
+ HasIterator
+ }
+}
+
+/// Extension traits used by the implementation of `quote!`. These are defined
+/// in separate traits, rather than as a single trait due to ambiguity issues.
+///
+/// These traits expose a `quote_into_iter` method which should allow calling
+/// whichever impl happens to be applicable. Calling that method repeatedly on
+/// the returned value should be idempotent.
+#[doc(hidden)]
+pub mod ext {
+ use core::slice;
+ use std::collections::btree_set::{self, BTreeSet};
+
+ use super::{
+ HasIterator as HasIter, RepInterp, ThereIsNoIteratorInRepetition as DoesNotHaveIter,
+ };
+ use crate::ToTokens;
+
+ /// Extension trait providing the `quote_into_iter` method on iterators.
+ #[doc(hidden)]
+ pub trait RepIteratorExt: Iterator + Sized {
+ fn quote_into_iter(self) -> (Self, HasIter) {
+ (self, HasIter)
+ }
+ }
+
+ impl RepIteratorExt for T {}
+
+ /// Extension trait providing the `quote_into_iter` method for
+ /// non-iterable types. These types interpolate the same value in each
+ /// iteration of the repetition.
+ #[doc(hidden)]
+ pub trait RepToTokensExt {
+ /// Pretend to be an iterator for the purposes of `quote_into_iter`.
+ /// This allows repeated calls to `quote_into_iter` to continue
+ /// correctly returning DoesNotHaveIter.
+ fn next(&self) -> Option<&Self> {
+ Some(self)
+ }
+
+ fn quote_into_iter(&self) -> (&Self, DoesNotHaveIter) {
+ (self, DoesNotHaveIter)
+ }
+ }
+
+ impl RepToTokensExt for T {}
+
+ /// Extension trait providing the `quote_into_iter` method for types that
+ /// can be referenced as an iterator.
+ #[doc(hidden)]
+ pub trait RepAsIteratorExt<'q> {
+ type Iter: Iterator;
+
+ fn quote_into_iter(&'q self) -> (Self::Iter, HasIter);
+ }
+
+ impl<'q, T: RepAsIteratorExt<'q> + ?Sized> RepAsIteratorExt<'q> for &T {
+ type Iter = T::Iter;
+
+ fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) {
+ ::quote_into_iter(*self)
+ }
+ }
+
+ impl<'q, T: RepAsIteratorExt<'q> + ?Sized> RepAsIteratorExt<'q> for &mut T {
+ type Iter = T::Iter;
+
+ fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) {
+ ::quote_into_iter(*self)
+ }
+ }
+
+ impl<'q, T: 'q> RepAsIteratorExt<'q> for [T] {
+ type Iter = slice::Iter<'q, T>;
+
+ fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) {
+ (self.iter(), HasIter)
+ }
+ }
+
+ impl<'q, T: 'q, const N: usize> RepAsIteratorExt<'q> for [T; N] {
+ type Iter = slice::Iter<'q, T>;
+
+ fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) {
+ (self.iter(), HasIter)
+ }
+ }
+
+ impl<'q, T: 'q> RepAsIteratorExt<'q> for Vec {
+ type Iter = slice::Iter<'q, T>;
+
+ fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) {
+ (self.iter(), HasIter)
+ }
+ }
+
+ impl<'q, T: 'q> RepAsIteratorExt<'q> for BTreeSet {
+ type Iter = btree_set::Iter<'q, T>;
+
+ fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) {
+ (self.iter(), HasIter)
+ }
+ }
+
+ impl<'q, T: RepAsIteratorExt<'q>> RepAsIteratorExt<'q> for RepInterp {
+ type Iter = T::Iter;
+
+ fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) {
+ self.0.quote_into_iter()
+ }
+ }
+}
+
+// Helper type used within interpolations to allow for repeated binding names.
+// Implements the relevant traits, and exports a dummy `next()` method.
+#[derive(Copy, Clone)]
+#[doc(hidden)]
+pub struct RepInterp(pub T);
+
+impl RepInterp {
+ // This method is intended to look like `Iterator::next`, and is called when
+ // a name is bound multiple times, as the previous binding will shadow the
+ // original `Iterator` object. This allows us to avoid advancing the
+ // iterator multiple times per iteration.
+ pub fn next(self) -> Option {
+ Some(self.0)
+ }
+}
+
+impl Iterator for RepInterp {
+ type Item = T::Item;
+
+ fn next(&mut self) -> Option {
+ self.0.next()
+ }
+}
+
+impl ToTokens for RepInterp {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ self.0.to_tokens(tokens);
+ }
+}
+
macro_rules! minimal_quote_tt {
(($($t:tt)*)) => { Group::new(Delimiter::Parenthesis, minimal_quote!($($t)*)) };
([$($t:tt)*]) => { Group::new(Delimiter::Bracket, minimal_quote!($($t)*)) };
@@ -20,7 +194,13 @@ macro_rules! minimal_quote_tt {
(>) => { Punct::new('>', Spacing::Alone) };
(&) => { Punct::new('&', Spacing::Alone) };
(=) => { Punct::new('=', Spacing::Alone) };
+ (#) => { Punct::new('#', Spacing::Alone) };
+ (|) => { Punct::new('|', Spacing::Alone) };
+ (:) => { Punct::new(':', Spacing::Alone) };
+ (*) => { Punct::new('*', Spacing::Alone) };
+ (_) => { Ident::new("_", Span::def_site()) };
($i:ident) => { Ident::new(stringify!($i), Span::def_site()) };
+ ($lit:literal) => { stringify!($lit).parse::().unwrap() };
}
macro_rules! minimal_quote_ts {
@@ -36,6 +216,39 @@ macro_rules! minimal_quote_ts {
[c.0, c.1].into_iter().collect::()
}
};
+ (=>) => {
+ {
+ let mut c = (
+ TokenTree::from(Punct::new('=', Spacing::Joint)),
+ TokenTree::from(Punct::new('>', Spacing::Alone))
+ );
+ c.0.set_span(Span::def_site());
+ c.1.set_span(Span::def_site());
+ [c.0, c.1].into_iter().collect::()
+ }
+ };
+ (+=) => {
+ {
+ let mut c = (
+ TokenTree::from(Punct::new('+', Spacing::Joint)),
+ TokenTree::from(Punct::new('=', Spacing::Alone))
+ );
+ c.0.set_span(Span::def_site());
+ c.1.set_span(Span::def_site());
+ [c.0, c.1].into_iter().collect::()
+ }
+ };
+ (!=) => {
+ {
+ let mut c = (
+ TokenTree::from(Punct::new('!', Spacing::Joint)),
+ TokenTree::from(Punct::new('=', Spacing::Alone))
+ );
+ c.0.set_span(Span::def_site());
+ c.1.set_span(Span::def_site());
+ [c.0, c.1].into_iter().collect::()
+ }
+ };
($t:tt) => { TokenTree::from(minimal_quote_tt!($t)) };
}
@@ -71,17 +284,99 @@ pub fn quote(stream: TokenStream) -> TokenStream {
let mut after_dollar = false;
let mut tokens = crate::TokenStream::new();
- for tree in stream {
+ let mut iter = stream.into_iter().peekable();
+ while let Some(tree) = iter.next() {
if after_dollar {
after_dollar = false;
match tree {
+ TokenTree::Group(tt) => {
+ // Handles repetition by expanding `$( CONTENTS ) SEP_OPT *` to `{ REP_EXPANDED }`.
+ let contents = tt.stream();
+
+ // The `*` token is also consumed here.
+ let sep_opt: Option = match (iter.next(), iter.peek()) {
+ (Some(TokenTree::Punct(sep)), Some(TokenTree::Punct(star)))
+ if sep.spacing() == Spacing::Joint && star.as_char() == '*' =>
+ {
+ iter.next();
+ Some(sep)
+ }
+ (Some(TokenTree::Punct(star)), _) if star.as_char() == '*' => None,
+ _ => panic!("`$(...)` must be followed by `*` in `quote!`"),
+ };
+
+ let mut rep_expanded = TokenStream::new();
+
+ // Append setup code for a `while`, where recursively quoted `CONTENTS`
+ // and `SEP_OPT` are repeatedly processed, to `REP_EXPANDED`.
+ let meta_vars = collect_meta_vars(contents.clone());
+ minimal_quote!(
+ use crate::ext::*;
+ (@ if sep_opt.is_some() {
+ minimal_quote!(let mut _i = 0usize;)
+ } else {
+ minimal_quote!(();)
+ })
+ let has_iter = crate::ThereIsNoIteratorInRepetition;
+ )
+ .to_tokens(&mut rep_expanded);
+ for meta_var in &meta_vars {
+ minimal_quote!(
+ #[allow(unused_mut)]
+ let (mut (@ meta_var), i) = (@ meta_var).quote_into_iter();
+ let has_iter = has_iter | i;
+ )
+ .to_tokens(&mut rep_expanded);
+ }
+ minimal_quote!(let _: crate::HasIterator = has_iter;)
+ .to_tokens(&mut rep_expanded);
+
+ // Append the `while` to `REP_EXPANDED`.
+ let mut while_body = TokenStream::new();
+ for meta_var in &meta_vars {
+ minimal_quote!(
+ let (@ meta_var) = match (@ meta_var).next() {
+ Some(_x) => crate::RepInterp(_x),
+ None => break,
+ };
+ )
+ .to_tokens(&mut while_body);
+ }
+ minimal_quote!(
+ (@ if let Some(sep) = sep_opt {
+ minimal_quote!(
+ if _i > 0 {
+ (@ minimal_quote!(crate::ToTokens::to_tokens(&crate::TokenTree::Punct(crate::Punct::new(
+ (@ TokenTree::from(Literal::character(sep.as_char()))),
+ (@ minimal_quote!(crate::Spacing::Alone)),
+ )), &mut ts);))
+ }
+ _i += 1;
+ )
+ } else {
+ minimal_quote!(();)
+ })
+ (@ quote(contents.clone())).to_tokens(&mut ts);
+ )
+ .to_tokens(&mut while_body);
+ rep_expanded.extend(vec![
+ TokenTree::Ident(Ident::new("while", Span::call_site())),
+ TokenTree::Ident(Ident::new("true", Span::call_site())),
+ TokenTree::Group(Group::new(Delimiter::Brace, while_body)),
+ ]);
+
+ minimal_quote!((@ TokenTree::Group(Group::new(Delimiter::Brace, rep_expanded)))).to_tokens(&mut tokens);
+ continue;
+ }
TokenTree::Ident(_) => {
minimal_quote!(crate::ToTokens::to_tokens(&(@ tree), &mut ts);)
.to_tokens(&mut tokens);
continue;
}
TokenTree::Punct(ref tt) if tt.as_char() == '$' => {}
- _ => panic!("`$` must be followed by an ident or `$` in `quote!`"),
+ _ => panic!(
+ "`$` must be followed by an ident or `$` or a repetition group in `quote!`"
+ ),
}
} else if let TokenTree::Punct(ref tt) = tree {
if tt.as_char() == '$' {
@@ -155,6 +450,33 @@ pub fn quote(stream: TokenStream) -> TokenStream {
}
}
+/// Helper function to support macro repetitions like `$( CONTENTS ) SEP_OPT *` in `quote!`.
+/// Recursively collects all `Ident`s (meta-variables) that follow a `$`
+/// from the given `CONTENTS` stream, preserving their order of appearance.
+fn collect_meta_vars(content_stream: TokenStream) -> Vec {
+ fn helper(stream: TokenStream, out: &mut Vec) {
+ let mut iter = stream.into_iter().peekable();
+ while let Some(tree) = iter.next() {
+ match &tree {
+ TokenTree::Punct(tt) if tt.as_char() == '$' => {
+ if let Some(TokenTree::Ident(id)) = iter.peek() {
+ out.push(id.clone());
+ iter.next();
+ }
+ }
+ TokenTree::Group(tt) => {
+ helper(tt.stream(), out);
+ }
+ _ => {}
+ }
+ }
+ }
+
+ let mut vars = Vec::new();
+ helper(content_stream, &mut vars);
+ vars
+}
+
/// Quote a `Span` into a `TokenStream`.
/// This is needed to implement a custom quoter.
#[unstable(feature = "proc_macro_quote", issue = "54722")]
diff --git a/library/std/src/sys/pal/windows/c.rs b/library/std/src/sys/pal/windows/c.rs
index ac1c5e9932e1c..53dec105d0c85 100644
--- a/library/std/src/sys/pal/windows/c.rs
+++ b/library/std/src/sys/pal/windows/c.rs
@@ -119,6 +119,23 @@ unsafe extern "system" {
pub fn ProcessPrng(pbdata: *mut u8, cbdata: usize) -> BOOL;
}
+windows_targets::link!("ntdll.dll" "system" fn NtCreateNamedPipeFile(
+ filehandle: *mut HANDLE,
+ desiredaccess: FILE_ACCESS_RIGHTS,
+ objectattributes: *const OBJECT_ATTRIBUTES,
+ iostatusblock: *mut IO_STATUS_BLOCK,
+ shareaccess: FILE_SHARE_MODE,
+ createdisposition: NTCREATEFILE_CREATE_DISPOSITION,
+ createoptions: NTCREATEFILE_CREATE_OPTIONS,
+ namedpipetype: u32,
+ readmode: u32,
+ completionmode: u32,
+ maximuminstances: u32,
+ inboundquota: u32,
+ outboundquota: u32,
+ defaulttimeout: *const u64,
+) -> NTSTATUS);
+
// Functions that aren't available on every version of Windows that we support,
// but we still use them and just provide some form of a fallback implementation.
compat_fn_with_fallback! {
diff --git a/library/std/src/sys/pal/windows/c/bindings.txt b/library/std/src/sys/pal/windows/c/bindings.txt
index a99c474c763c5..827d96e73db41 100644
--- a/library/std/src/sys/pal/windows/c/bindings.txt
+++ b/library/std/src/sys/pal/windows/c/bindings.txt
@@ -2060,6 +2060,14 @@ FILE_OPEN_REPARSE_POINT
FILE_OPEN_REQUIRING_OPLOCK
FILE_OVERWRITE
FILE_OVERWRITE_IF
+FILE_PIPE_ACCEPT_REMOTE_CLIENTS
+FILE_PIPE_BYTE_STREAM_MODE
+FILE_PIPE_BYTE_STREAM_TYPE
+FILE_PIPE_COMPLETE_OPERATION
+FILE_PIPE_MESSAGE_MODE
+FILE_PIPE_MESSAGE_TYPE
+FILE_PIPE_QUEUE_OPERATION
+FILE_PIPE_REJECT_REMOTE_CLIENTS
FILE_RANDOM_ACCESS
FILE_READ_ATTRIBUTES
FILE_READ_DATA
@@ -2294,7 +2302,16 @@ NtOpenFile
NtReadFile
NTSTATUS
NtWriteFile
+OBJ_CASE_INSENSITIVE
OBJ_DONT_REPARSE
+OBJ_EXCLUSIVE
+OBJ_FORCE_ACCESS_CHECK
+OBJ_IGNORE_IMPERSONATED_DEVICEMAP
+OBJ_INHERIT
+OBJ_KERNEL_HANDLE
+OBJ_OPENIF
+OBJ_OPENLINK
+OBJ_PERMANENT
OPEN_ALWAYS
OPEN_EXISTING
OpenProcessToken
diff --git a/library/std/src/sys/pal/windows/c/windows_sys.rs b/library/std/src/sys/pal/windows/c/windows_sys.rs
index 95bf8040229d0..b2e3aabc63353 100644
--- a/library/std/src/sys/pal/windows/c/windows_sys.rs
+++ b/library/std/src/sys/pal/windows/c/windows_sys.rs
@@ -1,4 +1,4 @@
-// Bindings generated by `windows-bindgen` 0.61.0
+// Bindings generated by `windows-bindgen` 0.61.1
#![allow(non_snake_case, non_upper_case_globals, non_camel_case_types, dead_code, clippy::all)]
@@ -2552,6 +2552,14 @@ pub const FILE_OPEN_REPARSE_POINT: NTCREATEFILE_CREATE_OPTIONS = 2097152u32;
pub const FILE_OPEN_REQUIRING_OPLOCK: NTCREATEFILE_CREATE_OPTIONS = 65536u32;
pub const FILE_OVERWRITE: NTCREATEFILE_CREATE_DISPOSITION = 4u32;
pub const FILE_OVERWRITE_IF: NTCREATEFILE_CREATE_DISPOSITION = 5u32;
+pub const FILE_PIPE_ACCEPT_REMOTE_CLIENTS: u32 = 0u32;
+pub const FILE_PIPE_BYTE_STREAM_MODE: u32 = 0u32;
+pub const FILE_PIPE_BYTE_STREAM_TYPE: u32 = 0u32;
+pub const FILE_PIPE_COMPLETE_OPERATION: u32 = 1u32;
+pub const FILE_PIPE_MESSAGE_MODE: u32 = 1u32;
+pub const FILE_PIPE_MESSAGE_TYPE: u32 = 1u32;
+pub const FILE_PIPE_QUEUE_OPERATION: u32 = 0u32;
+pub const FILE_PIPE_REJECT_REMOTE_CLIENTS: u32 = 2u32;
pub const FILE_RANDOM_ACCESS: NTCREATEFILE_CREATE_OPTIONS = 2048u32;
pub const FILE_READ_ATTRIBUTES: FILE_ACCESS_RIGHTS = 128u32;
pub const FILE_READ_DATA: FILE_ACCESS_RIGHTS = 1u32;
@@ -2983,7 +2991,16 @@ impl Default for OBJECT_ATTRIBUTES {
}
}
pub type OBJECT_ATTRIBUTE_FLAGS = u32;
+pub const OBJ_CASE_INSENSITIVE: OBJECT_ATTRIBUTE_FLAGS = 64u32;
pub const OBJ_DONT_REPARSE: OBJECT_ATTRIBUTE_FLAGS = 4096u32;
+pub const OBJ_EXCLUSIVE: OBJECT_ATTRIBUTE_FLAGS = 32u32;
+pub const OBJ_FORCE_ACCESS_CHECK: OBJECT_ATTRIBUTE_FLAGS = 1024u32;
+pub const OBJ_IGNORE_IMPERSONATED_DEVICEMAP: OBJECT_ATTRIBUTE_FLAGS = 2048u32;
+pub const OBJ_INHERIT: OBJECT_ATTRIBUTE_FLAGS = 2u32;
+pub const OBJ_KERNEL_HANDLE: OBJECT_ATTRIBUTE_FLAGS = 512u32;
+pub const OBJ_OPENIF: OBJECT_ATTRIBUTE_FLAGS = 128u32;
+pub const OBJ_OPENLINK: OBJECT_ATTRIBUTE_FLAGS = 256u32;
+pub const OBJ_PERMANENT: OBJECT_ATTRIBUTE_FLAGS = 16u32;
pub const OPEN_ALWAYS: FILE_CREATION_DISPOSITION = 4u32;
pub const OPEN_EXISTING: FILE_CREATION_DISPOSITION = 3u32;
#[repr(C)]
diff --git a/library/std/src/sys/pal/windows/pipe.rs b/library/std/src/sys/pal/windows/pipe.rs
index 00d469fbaf8c7..bc5d05c45052a 100644
--- a/library/std/src/sys/pal/windows/pipe.rs
+++ b/library/std/src/sys/pal/windows/pipe.rs
@@ -1,14 +1,9 @@
-use crate::ffi::OsStr;
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
+use crate::ops::Neg;
use crate::os::windows::prelude::*;
-use crate::path::Path;
-use crate::random::{DefaultRandomSource, Random};
-use crate::sync::atomic::Ordering::Relaxed;
-use crate::sync::atomic::{Atomic, AtomicUsize};
+use crate::sys::api::utf16;
use crate::sys::c;
-use crate::sys::fs::{File, OpenOptions};
use crate::sys::handle::Handle;
-use crate::sys::pal::windows::api::{self, WinError};
use crate::sys_common::{FromInner, IntoInner};
use crate::{mem, ptr};
@@ -62,92 +57,113 @@ pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Res
// Note that we specifically do *not* use `CreatePipe` here because
// unfortunately the anonymous pipes returned do not support overlapped
- // operations. Instead, we create a "hopefully unique" name and create a
- // named pipe which has overlapped operations enabled.
+ // operations. Instead, we use `NtCreateNamedPipeFile` to create the
+ // anonymous pipe with overlapped support.
//
- // Once we do this, we connect do it as usual via `CreateFileW`, and then
+ // Once we do this, we connect to it via `NtOpenFile`, and then
// we return those reader/writer halves. Note that the `ours` pipe return
// value is always the named pipe, whereas `theirs` is just the normal file.
// This should hopefully shield us from child processes which assume their
// stdout is a named pipe, which would indeed be odd!
unsafe {
- let ours;
- let mut name;
- let mut tries = 0;
- loop {
- tries += 1;
- name = format!(
- r"\\.\pipe\__rust_anonymous_pipe1__.{}.{}",
- c::GetCurrentProcessId(),
- random_number(),
+ let mut io_status = c::IO_STATUS_BLOCK::default();
+ let mut object_attributes = c::OBJECT_ATTRIBUTES::default();
+ object_attributes.Length = size_of::() as u32;
+
+ // Open a handle to the pipe filesystem (`\??\PIPE\`).
+ // This will be used when creating a new annon pipe.
+ let pipe_fs = {
+ let path = c::UNICODE_STRING::from_ref(utf16!(r"\??\PIPE\"));
+ object_attributes.ObjectName = &path;
+ let mut pipe_fs = ptr::null_mut();
+ let status = c::NtOpenFile(
+ &mut pipe_fs,
+ c::SYNCHRONIZE | c::GENERIC_READ,
+ &object_attributes,
+ &mut io_status,
+ c::FILE_SHARE_READ | c::FILE_SHARE_WRITE,
+ c::FILE_SYNCHRONOUS_IO_NONALERT, // synchronous access
);
- let wide_name = OsStr::new(&name).encode_wide().chain(Some(0)).collect::>();
- let mut flags = c::FILE_FLAG_FIRST_PIPE_INSTANCE | c::FILE_FLAG_OVERLAPPED;
- if ours_readable {
- flags |= c::PIPE_ACCESS_INBOUND;
+ if c::nt_success(status) {
+ Handle::from_raw_handle(pipe_fs)
} else {
- flags |= c::PIPE_ACCESS_OUTBOUND;
+ return Err(io::Error::from_raw_os_error(c::RtlNtStatusToDosError(status) as i32));
}
+ };
- let handle = c::CreateNamedPipeW(
- wide_name.as_ptr(),
- flags,
- c::PIPE_TYPE_BYTE
- | c::PIPE_READMODE_BYTE
- | c::PIPE_WAIT
- | c::PIPE_REJECT_REMOTE_CLIENTS,
+ // From now on we're using handles instead of paths to create and open pipes.
+ // So set the `ObjectName` to a zero length string.
+ let empty = c::UNICODE_STRING::default();
+ object_attributes.ObjectName = ∅
+
+ // Create our side of the pipe for async access.
+ let ours = {
+ // Use the pipe filesystem as the root directory.
+ // With no name provided, an anonymous pipe will be created.
+ object_attributes.RootDirectory = pipe_fs.as_raw_handle();
+
+ // A negative timeout value is a relative time (rather than an absolute time).
+ // The time is given in 100's of nanoseconds so this is 50 milliseconds.
+ // This value was chosen to be consistent with the default timeout set by `CreateNamedPipeW`
+ // See: https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-createnamedpipew
+ let timeout = (50_i64 * 10000).neg() as u64;
+
+ let mut ours = ptr::null_mut();
+ let status = c::NtCreateNamedPipeFile(
+ &mut ours,
+ c::SYNCHRONIZE | if ours_readable { c::GENERIC_READ } else { c::GENERIC_WRITE },
+ &object_attributes,
+ &mut io_status,
+ if ours_readable { c::FILE_SHARE_WRITE } else { c::FILE_SHARE_READ },
+ c::FILE_CREATE,
+ 0,
+ c::FILE_PIPE_BYTE_STREAM_TYPE,
+ c::FILE_PIPE_BYTE_STREAM_MODE,
+ c::FILE_PIPE_QUEUE_OPERATION,
+ // only allow one client pipe
1,
PIPE_BUFFER_CAPACITY,
PIPE_BUFFER_CAPACITY,
- 0,
- ptr::null_mut(),
+ &timeout,
);
-
- // We pass the `FILE_FLAG_FIRST_PIPE_INSTANCE` flag above, and we're
- // also just doing a best effort at selecting a unique name. If
- // `ERROR_ACCESS_DENIED` is returned then it could mean that we
- // accidentally conflicted with an already existing pipe, so we try
- // again.
- //
- // Don't try again too much though as this could also perhaps be a
- // legit error.
- if handle == c::INVALID_HANDLE_VALUE {
- let error = api::get_last_error();
- if tries < 10 && error == WinError::ACCESS_DENIED {
- continue;
- } else {
- return Err(io::Error::from_raw_os_error(error.code as i32));
- }
+ if c::nt_success(status) {
+ Handle::from_raw_handle(ours)
+ } else {
+ return Err(io::Error::from_raw_os_error(c::RtlNtStatusToDosError(status) as i32));
}
+ };
- ours = Handle::from_raw_handle(handle);
- break;
- }
+ // Open their side of the pipe for synchronous access.
+ let theirs = {
+ // We can reopen the anonymous pipe without a name by setting
+ // RootDirectory to the pipe handle and not setting a path name,
+ object_attributes.RootDirectory = ours.as_raw_handle();
- // Connect to the named pipe we just created. This handle is going to be
- // returned in `theirs`, so if `ours` is readable we want this to be
- // writable, otherwise if `ours` is writable we want this to be
- // readable.
- //
- // Additionally we don't enable overlapped mode on this because most
- // client processes aren't enabled to work with that.
- let mut opts = OpenOptions::new();
- opts.write(ours_readable);
- opts.read(!ours_readable);
- opts.share_mode(0);
- let size = size_of::();
- let mut sa = c::SECURITY_ATTRIBUTES {
- nLength: size as u32,
- lpSecurityDescriptor: ptr::null_mut(),
- bInheritHandle: their_handle_inheritable as i32,
+ if their_handle_inheritable {
+ object_attributes.Attributes |= c::OBJ_INHERIT;
+ }
+ let mut theirs = ptr::null_mut();
+ let status = c::NtOpenFile(
+ &mut theirs,
+ c::SYNCHRONIZE
+ | if ours_readable {
+ c::GENERIC_WRITE | c::FILE_READ_ATTRIBUTES
+ } else {
+ c::GENERIC_READ
+ },
+ &object_attributes,
+ &mut io_status,
+ 0,
+ c::FILE_NON_DIRECTORY_FILE | c::FILE_SYNCHRONOUS_IO_NONALERT,
+ );
+ if c::nt_success(status) {
+ Handle::from_raw_handle(theirs)
+ } else {
+ return Err(io::Error::from_raw_os_error(c::RtlNtStatusToDosError(status) as i32));
+ }
};
- opts.security_attributes(&mut sa);
- let theirs = File::open(Path::new(&name), &opts)?;
- Ok(Pipes {
- ours: AnonPipe { inner: ours },
- theirs: AnonPipe { inner: theirs.into_inner() },
- })
+ Ok(Pipes { ours: AnonPipe { inner: ours }, theirs: AnonPipe { inner: theirs } })
}
}
@@ -191,17 +207,6 @@ pub fn spawn_pipe_relay(
Ok(theirs)
}
-fn random_number() -> usize {
- static N: Atomic = AtomicUsize::new(0);
- loop {
- if N.load(Relaxed) != 0 {
- return N.fetch_add(1, Relaxed);
- }
-
- N.store(usize::random(&mut DefaultRandomSource), Relaxed);
- }
-}
-
impl AnonPipe {
pub fn handle(&self) -> &Handle {
&self.inner
diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs
index 560925abba6a9..52f421b478235 100644
--- a/src/bootstrap/src/core/build_steps/compile.rs
+++ b/src/bootstrap/src/core/build_steps/compile.rs
@@ -2105,19 +2105,20 @@ impl Step for Assemble {
if builder.config.llvm_enzyme && !builder.config.dry_run() {
debug!("`llvm_enzyme` requested");
let enzyme_install = builder.ensure(llvm::Enzyme { target: build_compiler.host });
- let llvm_config = builder.llvm_config(builder.config.host_target).unwrap();
- let llvm_version_major = llvm::get_llvm_version_major(builder, &llvm_config);
- let lib_ext = std::env::consts::DLL_EXTENSION;
- let libenzyme = format!("libEnzyme-{llvm_version_major}");
- let src_lib =
- enzyme_install.join("build/Enzyme").join(&libenzyme).with_extension(lib_ext);
- let libdir = builder.sysroot_target_libdir(build_compiler, build_compiler.host);
- let target_libdir =
- builder.sysroot_target_libdir(target_compiler, target_compiler.host);
- let dst_lib = libdir.join(&libenzyme).with_extension(lib_ext);
- let target_dst_lib = target_libdir.join(&libenzyme).with_extension(lib_ext);
- builder.copy_link(&src_lib, &dst_lib, FileType::NativeLibrary);
- builder.copy_link(&src_lib, &target_dst_lib, FileType::NativeLibrary);
+ if let Some(llvm_config) = builder.llvm_config(builder.config.host_target) {
+ let llvm_version_major = llvm::get_llvm_version_major(builder, &llvm_config);
+ let lib_ext = std::env::consts::DLL_EXTENSION;
+ let libenzyme = format!("libEnzyme-{llvm_version_major}");
+ let src_lib =
+ enzyme_install.join("build/Enzyme").join(&libenzyme).with_extension(lib_ext);
+ let libdir = builder.sysroot_target_libdir(build_compiler, build_compiler.host);
+ let target_libdir =
+ builder.sysroot_target_libdir(target_compiler, target_compiler.host);
+ let dst_lib = libdir.join(&libenzyme).with_extension(lib_ext);
+ let target_dst_lib = target_libdir.join(&libenzyme).with_extension(lib_ext);
+ builder.copy_link(&src_lib, &dst_lib, FileType::NativeLibrary);
+ builder.copy_link(&src_lib, &target_dst_lib, FileType::NativeLibrary);
+ }
}
// Build the libraries for this compiler to link to (i.e., the libraries
diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs
index 33738f7a242f6..fb2b45802a671 100644
--- a/src/librustdoc/html/render/write_shared.rs
+++ b/src/librustdoc/html/render/write_shared.rs
@@ -477,11 +477,7 @@ impl SourcesPart {
// This needs to be `var`, not `const`.
// This variable needs declared in the current global scope so that if
// src-script.js loads first, it can pick it up.
- SortedTemplate::from_before_after(
- r"var srcIndex = new Map(JSON.parse('[",
- r"]'));
-createSrcSidebar();",
- )
+ SortedTemplate::from_before_after(r"createSrcSidebar('[", r"]');")
}
fn get(cx: &Context<'_>, crate_name: &OrderedJson) -> Result, Error> {
diff --git a/src/librustdoc/html/render/write_shared/tests.rs b/src/librustdoc/html/render/write_shared/tests.rs
index a235f1d37243a..6f185e85345bc 100644
--- a/src/librustdoc/html/render/write_shared/tests.rs
+++ b/src/librustdoc/html/render/write_shared/tests.rs
@@ -22,23 +22,11 @@ fn but_last_line(s: &str) -> &str {
#[test]
fn sources_template() {
let mut template = SourcesPart::blank();
- assert_eq!(
- but_last_line(&template.to_string()),
- r"var srcIndex = new Map(JSON.parse('[]'));
-createSrcSidebar();"
- );
+ assert_eq!(but_last_line(&template.to_string()), r"createSrcSidebar('[]');");
template.append(EscapedJson::from(OrderedJson::serialize("u").unwrap()).to_string());
- assert_eq!(
- but_last_line(&template.to_string()),
- r#"var srcIndex = new Map(JSON.parse('["u"]'));
-createSrcSidebar();"#
- );
+ assert_eq!(but_last_line(&template.to_string()), r#"createSrcSidebar('["u"]');"#);
template.append(EscapedJson::from(OrderedJson::serialize("v").unwrap()).to_string());
- assert_eq!(
- but_last_line(&template.to_string()),
- r#"var srcIndex = new Map(JSON.parse('["u","v"]'));
-createSrcSidebar();"#
- );
+ assert_eq!(but_last_line(&template.to_string()), r#"createSrcSidebar('["u","v"]');"#);
}
#[test]
diff --git a/src/librustdoc/html/static/.eslintrc.js b/src/librustdoc/html/static/.eslintrc.js
index fbb096fe9c753..303c5667140cf 100644
--- a/src/librustdoc/html/static/.eslintrc.js
+++ b/src/librustdoc/html/static/.eslintrc.js
@@ -91,5 +91,6 @@ module.exports = {
"no-script-url": "error",
"no-sequences": "error",
"no-div-regex": "error",
+ "no-console": "error",
}
};
diff --git a/src/librustdoc/html/static/js/rustdoc.d.ts b/src/librustdoc/html/static/js/rustdoc.d.ts
index 0d2e19e019f34..6af16441de88b 100644
--- a/src/librustdoc/html/static/js/rustdoc.d.ts
+++ b/src/librustdoc/html/static/js/rustdoc.d.ts
@@ -4,8 +4,6 @@
/* eslint-disable */
declare global {
- /** Map from crate name to directory structure, for source view */
- declare var srcIndex: Map;
/** Defined and documented in `storage.js` */
declare function nonnull(x: T|null, msg: string|undefined);
/** Defined and documented in `storage.js` */
@@ -64,7 +62,7 @@ declare global {
* create's the sidebar in source code view.
* called in generated `src-files.js`.
*/
- createSrcSidebar?: function(),
+ createSrcSidebar?: function(string),
/**
* Set up event listeners for a scraped source example.
*/
@@ -129,7 +127,7 @@ declare namespace rustdoc {
/**
* A single parsed "atom" in a search query. For example,
- *
+ *
* std::fmt::Formatter, Write -> Result<()>
* ┏━━━━━━━━━━━━━━━━━━ ┌──── ┏━━━━━┅┅┅┅┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐
* ┃ │ ┗ QueryElement { ┊
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index dce5fddb3177e..b611a3e501dc1 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -1133,6 +1133,7 @@ class RoaringBitmap {
}
for (let j = 0; j < size; ++j) {
if (offsets && offsets[j] !== i) {
+ // eslint-disable-next-line no-console
console.log(this.containers);
throw new Error(`corrupt bitmap ${j}: ${i} / ${offsets[j]}`);
}
diff --git a/src/librustdoc/html/static/js/src-script.js b/src/librustdoc/html/static/js/src-script.js
index b9ab6e85603bc..0c6afbeed22e2 100644
--- a/src/librustdoc/html/static/js/src-script.js
+++ b/src/librustdoc/html/static/js/src-script.js
@@ -1,6 +1,3 @@
-// From rust:
-/* global srcIndex */
-
// Local js definitions:
/* global addClass, onEachLazy, removeClass, browserSupportsHistoryApi */
/* global updateLocalStorage, getVar, nonnull */
@@ -100,11 +97,15 @@ window.rustdocToggleSrcSidebar = () => {
// This function is called from "src-files.js", generated in `html/render/write_shared.rs`.
// eslint-disable-next-line no-unused-vars
-function createSrcSidebar() {
+/**
+ * @param {string} srcIndexStr - strinified json map from crate name to dir structure
+ */
+function createSrcSidebar(srcIndexStr) {
const container = nonnull(document.querySelector("nav.sidebar"));
const sidebar = document.createElement("div");
sidebar.id = "src-sidebar";
+ const srcIndex = new Map(JSON.parse(srcIndexStr));
let hasFoundFile = false;
diff --git a/src/tools/cargo b/src/tools/cargo
index fc1518ef02b77..2251525ae503f 160000
--- a/src/tools/cargo
+++ b/src/tools/cargo
@@ -1 +1 @@
-Subproject commit fc1518ef02b77327d70d4026b95ea719dd9b8c51
+Subproject commit 2251525ae503fa196f6d7f9ce6d32eccb2d5f044
diff --git a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
index b839b6f56728c..bd8420917f5e6 100644
--- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
+++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
@@ -128,7 +128,7 @@ fn remove_all_parens(pat: &mut P) {
}
impl MutVisitor for Visitor {
- fn visit_pat(&mut self, pat: &mut P) {
+ fn visit_pat(&mut self, pat: &mut Pat) {
let is_inner = mem::replace(&mut self.is_inner, true);
walk_pat(self, pat);
let inner = match &mut pat.kind {
@@ -145,7 +145,7 @@ fn remove_all_parens(pat: &mut P) {
fn insert_necessary_parens(pat: &mut P) {
struct Visitor;
impl MutVisitor for Visitor {
- fn visit_pat(&mut self, pat: &mut P) {
+ fn visit_pat(&mut self, pat: &mut Pat) {
use ast::BindingMode;
walk_pat(self, pat);
let target = match &mut pat.kind {
@@ -167,7 +167,7 @@ fn unnest_or_patterns(pat: &mut P) -> bool {
changed: bool,
}
impl MutVisitor for Visitor {
- fn visit_pat(&mut self, p: &mut P) {
+ fn visit_pat(&mut self, p: &mut Pat) {
// This is a bottom up transformation, so recurse first.
walk_pat(self, p);
diff --git a/tests/rustdoc-gui/globals.goml b/tests/rustdoc-gui/globals.goml
index f8c495ec18a69..7a0e2b9eb7462 100644
--- a/tests/rustdoc-gui/globals.goml
+++ b/tests/rustdoc-gui/globals.goml
@@ -6,7 +6,6 @@
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=sa'%3Bda'%3Bds"
wait-for: "#search-tabs"
assert-window-property-false: {"searchIndex": null}
-assert-window-property: {"srcIndex": null}
// Form input
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
@@ -14,11 +13,9 @@ write-into: (".search-input", "Foo")
press-key: 'Enter'
wait-for: "#search-tabs"
assert-window-property-false: {"searchIndex": null}
-assert-window-property: {"srcIndex": null}
// source sidebar
go-to: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html"
click: "#sidebar-button"
wait-for: "#src-sidebar details"
-assert-window-property-false: {"srcIndex": null}
assert-window-property: {"searchIndex": null}
diff --git a/tests/ui-fulldeps/pprust-expr-roundtrip.rs b/tests/ui-fulldeps/pprust-expr-roundtrip.rs
index f5cfa9e0bccff..8bca20852add4 100644
--- a/tests/ui-fulldeps/pprust-expr-roundtrip.rs
+++ b/tests/ui-fulldeps/pprust-expr-roundtrip.rs
@@ -187,9 +187,9 @@ fn iter_exprs(depth: usize, f: &mut dyn FnMut(P)) {
struct RemoveParens;
impl MutVisitor for RemoveParens {
- fn visit_expr(&mut self, e: &mut P) {
+ fn visit_expr(&mut self, e: &mut Expr) {
match e.kind.clone() {
- ExprKind::Paren(inner) => *e = inner,
+ ExprKind::Paren(inner) => *e = *inner,
_ => {}
};
mut_visit::walk_expr(self, e);
@@ -200,11 +200,11 @@ impl MutVisitor for RemoveParens {
struct AddParens;
impl MutVisitor for AddParens {
- fn visit_expr(&mut self, e: &mut P) {
+ fn visit_expr(&mut self, e: &mut Expr) {
mut_visit::walk_expr(self, e);
let expr = std::mem::replace(e, Expr::dummy());
- e.kind = ExprKind::Paren(expr);
+ e.kind = ExprKind::Paren(P(expr));
}
}
diff --git a/tests/ui-fulldeps/pprust-parenthesis-insertion.rs b/tests/ui-fulldeps/pprust-parenthesis-insertion.rs
index 08bed40abe86b..90e07bed40e75 100644
--- a/tests/ui-fulldeps/pprust-parenthesis-insertion.rs
+++ b/tests/ui-fulldeps/pprust-parenthesis-insertion.rs
@@ -43,7 +43,6 @@ use std::process::ExitCode;
use parser::parse_expr;
use rustc_ast::ast::{Expr, ExprKind};
use rustc_ast::mut_visit::{self, MutVisitor};
-use rustc_ast::ptr::P;
use rustc_ast_pretty::pprust;
use rustc_session::parse::ParseSess;
@@ -157,7 +156,7 @@ static EXPRS: &[&str] = &[
struct Unparenthesize;
impl MutVisitor for Unparenthesize {
- fn visit_expr(&mut self, e: &mut P) {
+ fn visit_expr(&mut self, e: &mut Expr) {
while let ExprKind::Paren(paren) = &mut e.kind {
*e = mem::replace(paren, Expr::dummy());
}
diff --git a/tests/ui/lint/redundant-semicolon/suggest-remove-semi-in-macro-expansion-issue-142143.rs b/tests/ui/lint/redundant-semicolon/suggest-remove-semi-in-macro-expansion-issue-142143.rs
new file mode 100644
index 0000000000000..4360eb964a4af
--- /dev/null
+++ b/tests/ui/lint/redundant-semicolon/suggest-remove-semi-in-macro-expansion-issue-142143.rs
@@ -0,0 +1,11 @@
+// Make sure we don't suggest remove redundant semicolon inside macro expansion.(issue #142143)
+
+#![deny(redundant_semicolons)]
+
+macro_rules! m {
+ ($stmt:stmt) => { #[allow(bad_style)] $stmt } //~ ERROR unnecessary trailing semicolon [redundant_semicolons]
+}
+
+fn main() {
+ m!(;);
+}
diff --git a/tests/ui/lint/redundant-semicolon/suggest-remove-semi-in-macro-expansion-issue-142143.stderr b/tests/ui/lint/redundant-semicolon/suggest-remove-semi-in-macro-expansion-issue-142143.stderr
new file mode 100644
index 0000000000000..7a38ec318ab6a
--- /dev/null
+++ b/tests/ui/lint/redundant-semicolon/suggest-remove-semi-in-macro-expansion-issue-142143.stderr
@@ -0,0 +1,18 @@
+error: unnecessary trailing semicolon
+ --> $DIR/suggest-remove-semi-in-macro-expansion-issue-142143.rs:6:43
+ |
+LL | ($stmt:stmt) => { #[allow(bad_style)] $stmt }
+ | ^^^^^
+...
+LL | m!(;);
+ | ----- in this macro invocation
+ |
+note: the lint level is defined here
+ --> $DIR/suggest-remove-semi-in-macro-expansion-issue-142143.rs:3:9
+ |
+LL | #![deny(redundant_semicolons)]
+ | ^^^^^^^^^^^^^^^^^^^^
+ = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/proc-macro/quote/auxiliary/basic.rs b/tests/ui/proc-macro/quote/auxiliary/basic.rs
index ef726bbfbe3a0..c50bb964eab06 100644
--- a/tests/ui/proc-macro/quote/auxiliary/basic.rs
+++ b/tests/ui/proc-macro/quote/auxiliary/basic.rs
@@ -4,6 +4,7 @@
extern crate proc_macro;
use std::borrow::Cow;
+use std::collections::BTreeSet;
use std::ffi::{CStr, CString};
use proc_macro::*;
@@ -12,6 +13,8 @@ use proc_macro::*;
pub fn run_tests(_: TokenStream) -> TokenStream {
test_quote_impl();
test_substitution();
+ test_iter();
+ test_array();
test_advanced();
test_integer();
test_floating();
@@ -24,6 +27,13 @@ pub fn run_tests(_: TokenStream) -> TokenStream {
test_ident();
test_underscore();
test_duplicate();
+ test_fancy_repetition();
+ test_nested_fancy_repetition();
+ test_duplicate_name_repetition();
+ test_duplicate_name_repetition_no_copy();
+ test_btreeset_repetition();
+ test_variable_name_conflict();
+ test_nonrep_in_repetition();
test_empty_quote();
test_box_str();
test_cow();
@@ -34,6 +44,7 @@ pub fn run_tests(_: TokenStream) -> TokenStream {
test_inner_block_comment();
test_outer_attr();
test_inner_attr();
+ test_star_after_repetition();
test_quote_raw_id();
TokenStream::new()
@@ -49,20 +60,9 @@ pub fn run_tests(_: TokenStream) -> TokenStream {
// - fn test_type_inference_for_span
// - wrong-type-span.rs
// - format_ident:
+// - fn test_closure
// - fn test_format_ident
// - fn test_format_ident_strip_raw
-// - repetition:
-// - fn test_iter
-// - fn test_array
-// - fn test_fancy_repetition
-// - fn test_nested_fancy_repetition
-// - fn test_duplicate_name_repetition
-// - fn test_duplicate_name_repetition_no_copy
-// - fn test_btreeset_repetition
-// - fn test_variable_name_conflict
-// - fn test_nonrep_in_repetition
-// - fn test_closure
-// - fn test_star_after_repetition
struct X;
@@ -99,6 +99,39 @@ fn test_substitution() {
assert_eq!(expected, tokens.to_string());
}
+fn test_iter() {
+ let primes = &[X, X, X, X];
+
+ assert_eq!("X X X X", quote!($($primes)*).to_string());
+
+ assert_eq!("X, X, X, X,", quote!($($primes,)*).to_string());
+
+ assert_eq!("X, X, X, X", quote!($($primes),*).to_string());
+}
+
+fn test_array() {
+ let array: [u8; 40] = [0; 40];
+ let _ = quote!($($array $array)*);
+
+ let ref_array: &[u8; 40] = &[0; 40];
+ let _ = quote!($($ref_array $ref_array)*);
+
+ let ref_slice: &[u8] = &[0; 40];
+ let _ = quote!($($ref_slice $ref_slice)*);
+
+ let array: [X; 2] = [X, X]; // !Copy
+ let _ = quote!($($array $array)*);
+
+ let ref_array: &[X; 2] = &[X, X];
+ let _ = quote!($($ref_array $ref_array)*);
+
+ let ref_slice: &[X] = &[X, X];
+ let _ = quote!($($ref_slice $ref_slice)*);
+
+ let array_of_array: [[u8; 2]; 2] = [[0; 2]; 2];
+ let _ = quote!($($($array_of_array)*)*);
+}
+
fn test_advanced() {
let generics = quote!( <'a, T> );
@@ -279,6 +312,88 @@ fn test_duplicate() {
assert_eq!(expected, tokens.to_string());
}
+fn test_fancy_repetition() {
+ let foo = vec!["a", "b"];
+ let bar = vec![true, false];
+
+ let tokens = quote! {
+ $($foo: $bar),*
+ };
+
+ let expected = r#""a" : true, "b" : false"#;
+ assert_eq!(expected, tokens.to_string());
+}
+
+fn test_nested_fancy_repetition() {
+ let nested = vec![vec!['a', 'b', 'c'], vec!['x', 'y', 'z']];
+
+ let tokens = quote! {
+ $(
+ $($nested)*
+ ),*
+ };
+
+ let expected = "'a' 'b' 'c', 'x' 'y' 'z'";
+ assert_eq!(expected, tokens.to_string());
+}
+
+fn test_duplicate_name_repetition() {
+ let foo = &["a", "b"];
+
+ let tokens = quote! {
+ $($foo: $foo),*
+ $($foo: $foo),*
+ };
+
+ let expected = r#""a" : "a", "b" : "b" "a" : "a", "b" : "b""#;
+ assert_eq!(expected, tokens.to_string());
+}
+
+fn test_duplicate_name_repetition_no_copy() {
+ let foo = vec!["a".to_owned(), "b".to_owned()];
+
+ let tokens = quote! {
+ $($foo: $foo),*
+ };
+
+ let expected = r#""a" : "a", "b" : "b""#;
+ assert_eq!(expected, tokens.to_string());
+}
+
+fn test_btreeset_repetition() {
+ let mut set = BTreeSet::new();
+ set.insert("a".to_owned());
+ set.insert("b".to_owned());
+
+ let tokens = quote! {
+ $($set: $set),*
+ };
+
+ let expected = r#""a" : "a", "b" : "b""#;
+ assert_eq!(expected, tokens.to_string());
+}
+
+fn test_variable_name_conflict() {
+ // The implementation of `#(...),*` uses the variable `_i` but it should be
+ // fine, if a little confusing when debugging.
+ let _i = vec!['a', 'b'];
+ let tokens = quote! { $($_i),* };
+ let expected = "'a', 'b'";
+ assert_eq!(expected, tokens.to_string());
+}
+
+fn test_nonrep_in_repetition() {
+ let rep = vec!["a", "b"];
+ let nonrep = "c";
+
+ let tokens = quote! {
+ $($rep $rep : $nonrep $nonrep),*
+ };
+
+ let expected = r#""a" "a" : "c" "c", "b" "b" : "c" "c""#;
+ assert_eq!(expected, tokens.to_string());
+}
+
fn test_empty_quote() {
let tokens = quote!();
assert_eq!("", tokens.to_string());
@@ -355,6 +470,19 @@ fn test_inner_attr() {
assert_eq!(expected, tokens.to_string());
}
+// https://github.com/dtolnay/quote/issues/130
+fn test_star_after_repetition() {
+ let c = vec!['0', '1'];
+ let tokens = quote! {
+ $(
+ f($c);
+ )*
+ *out = None;
+ };
+ let expected = "f('0'); f('1'); * out = None;";
+ assert_eq!(expected, tokens.to_string());
+}
+
fn test_quote_raw_id() {
let id = quote!(r#raw_id);
assert_eq!(id.to_string(), "r#raw_id");
diff --git a/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.rs b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.rs
index 2f67ae1bc6edc..418e3dd444dda 100644
--- a/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.rs
+++ b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.rs
@@ -1,7 +1,3 @@
-// FIXME(quote): `proc_macro::quote!` doesn't support repetition at the moment, so the stderr is
-// expected to be incorrect.
-//@ known-bug: #54722
-
#![feature(proc_macro_quote)]
extern crate proc_macro;
@@ -13,5 +9,5 @@ fn main() {
// Without some protection against repetitions with no iterator somewhere
// inside, this would loop infinitely.
- quote!($($nonrep $nonrep)*);
+ quote!($($nonrep $nonrep)*); //~ ERROR mismatched types
}
diff --git a/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.stderr b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.stderr
index 5f28a46f3181d..ecb12c1df3b6a 100644
--- a/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.stderr
+++ b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.stderr
@@ -1,10 +1,13 @@
-error: proc macro panicked
- --> $DIR/does-not-have-iter-interpolated-dup.rs:16:5
+error[E0308]: mismatched types
+ --> $DIR/does-not-have-iter-interpolated-dup.rs:12:5
|
LL | quote!($($nonrep $nonrep)*);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = help: message: `$` must be followed by an ident or `$` in `quote!`
+ | |
+ | expected `HasIterator`, found `ThereIsNoIteratorInRepetition`
+ | expected due to this
+ | here the type of `has_iter` is inferred to be `ThereIsNoIteratorInRepetition`
error: aborting due to 1 previous error
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.rs b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.rs
index 1efb3eac64247..507936770aa4f 100644
--- a/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.rs
+++ b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.rs
@@ -1,7 +1,3 @@
-// FIXME(quote): `proc_macro::quote!` doesn't support repetition at the moment, so the stderr is
-// expected to be incorrect.
-//@ known-bug: #54722
-
#![feature(proc_macro_quote)]
extern crate proc_macro;
@@ -13,5 +9,5 @@ fn main() {
// Without some protection against repetitions with no iterator somewhere
// inside, this would loop infinitely.
- quote!($($nonrep)*);
+ quote!($($nonrep)*); //~ ERROR mismatched types
}
diff --git a/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.stderr b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.stderr
index 595aa8587634a..093e2ebc09858 100644
--- a/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.stderr
+++ b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.stderr
@@ -1,10 +1,13 @@
-error: proc macro panicked
- --> $DIR/does-not-have-iter-interpolated.rs:16:5
+error[E0308]: mismatched types
+ --> $DIR/does-not-have-iter-interpolated.rs:12:5
|
LL | quote!($($nonrep)*);
| ^^^^^^^^^^^^^^^^^^^
- |
- = help: message: `$` must be followed by an ident or `$` in `quote!`
+ | |
+ | expected `HasIterator`, found `ThereIsNoIteratorInRepetition`
+ | expected due to this
+ | here the type of `has_iter` is inferred to be `ThereIsNoIteratorInRepetition`
error: aborting due to 1 previous error
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/proc-macro/quote/does-not-have-iter-separated.rs b/tests/ui/proc-macro/quote/does-not-have-iter-separated.rs
index 5f2ddabc390da..7e41b08f2636d 100644
--- a/tests/ui/proc-macro/quote/does-not-have-iter-separated.rs
+++ b/tests/ui/proc-macro/quote/does-not-have-iter-separated.rs
@@ -1,7 +1,3 @@
-// FIXME(quote): `proc_macro::quote!` doesn't support repetition at the moment, so the stderr is
-// expected to be incorrect.
-//@ known-bug: #54722
-
#![feature(proc_macro_quote)]
extern crate proc_macro;
@@ -9,5 +5,5 @@ extern crate proc_macro;
use proc_macro::quote;
fn main() {
- quote!($(a b),*);
+ quote!($(a b),*); //~ ERROR mismatched types
}
diff --git a/tests/ui/proc-macro/quote/does-not-have-iter-separated.stderr b/tests/ui/proc-macro/quote/does-not-have-iter-separated.stderr
index f6f5d7e007d0f..937209e675ec8 100644
--- a/tests/ui/proc-macro/quote/does-not-have-iter-separated.stderr
+++ b/tests/ui/proc-macro/quote/does-not-have-iter-separated.stderr
@@ -1,10 +1,12 @@
-error: proc macro panicked
- --> $DIR/does-not-have-iter-separated.rs:12:5
+error[E0308]: mismatched types
+ --> $DIR/does-not-have-iter-separated.rs:8:5
|
LL | quote!($(a b),*);
| ^^^^^^^^^^^^^^^^
- |
- = help: message: `$` must be followed by an ident or `$` in `quote!`
+ | |
+ | expected `HasIterator`, found `ThereIsNoIteratorInRepetition`
+ | expected due to this
error: aborting due to 1 previous error
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/proc-macro/quote/does-not-have-iter.rs b/tests/ui/proc-macro/quote/does-not-have-iter.rs
index 25ffd786cc614..038851ff76ed6 100644
--- a/tests/ui/proc-macro/quote/does-not-have-iter.rs
+++ b/tests/ui/proc-macro/quote/does-not-have-iter.rs
@@ -1,7 +1,3 @@
-// FIXME(quote): `proc_macro::quote!` doesn't support repetition at the moment, so the stderr is
-// expected to be incorrect.
-//@ known-bug: #54722
-
#![feature(proc_macro_quote)]
extern crate proc_macro;
@@ -9,5 +5,5 @@ extern crate proc_macro;
use proc_macro::quote;
fn main() {
- quote!($(a b)*);
+ quote!($(a b)*); //~ ERROR mismatched types
}
diff --git a/tests/ui/proc-macro/quote/does-not-have-iter.stderr b/tests/ui/proc-macro/quote/does-not-have-iter.stderr
index 0ed1daffc8cdf..e74ea3348992f 100644
--- a/tests/ui/proc-macro/quote/does-not-have-iter.stderr
+++ b/tests/ui/proc-macro/quote/does-not-have-iter.stderr
@@ -1,10 +1,12 @@
-error: proc macro panicked
- --> $DIR/does-not-have-iter.rs:12:5
+error[E0308]: mismatched types
+ --> $DIR/does-not-have-iter.rs:8:5
|
LL | quote!($(a b)*);
| ^^^^^^^^^^^^^^^
- |
- = help: message: `$` must be followed by an ident or `$` in `quote!`
+ | |
+ | expected `HasIterator`, found `ThereIsNoIteratorInRepetition`
+ | expected due to this
error: aborting due to 1 previous error
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/proc-macro/quote/not-quotable.stderr b/tests/ui/proc-macro/quote/not-quotable.stderr
index 62a02638e548b..d1c3d06f2b661 100644
--- a/tests/ui/proc-macro/quote/not-quotable.stderr
+++ b/tests/ui/proc-macro/quote/not-quotable.stderr
@@ -15,8 +15,8 @@ LL | let _ = quote! { $ip };
Cow<'_, T>
Option
Rc
- bool
- and 24 others
+ RepInterp
+ and 25 others
error: aborting due to 1 previous error
diff --git a/tests/ui/proc-macro/quote/not-repeatable.rs b/tests/ui/proc-macro/quote/not-repeatable.rs
index d115da7318156..0291e4ddf88d6 100644
--- a/tests/ui/proc-macro/quote/not-repeatable.rs
+++ b/tests/ui/proc-macro/quote/not-repeatable.rs
@@ -1,7 +1,3 @@
-// FIXME(quote): `proc_macro::quote!` doesn't support repetition at the moment, so the stderr is
-// expected to be incorrect.
-//@ known-bug: #54722
-
#![feature(proc_macro_quote)]
extern crate proc_macro;
@@ -12,5 +8,5 @@ struct Ipv4Addr;
fn main() {
let ip = Ipv4Addr;
- let _ = quote! { $($ip)* };
+ let _ = quote! { $($ip)* }; //~ ERROR the method `quote_into_iter` exists for struct `Ipv4Addr`, but its trait bounds were not satisfied
}
diff --git a/tests/ui/proc-macro/quote/not-repeatable.stderr b/tests/ui/proc-macro/quote/not-repeatable.stderr
index 18fbcd7379858..aeda08d7de68b 100644
--- a/tests/ui/proc-macro/quote/not-repeatable.stderr
+++ b/tests/ui/proc-macro/quote/not-repeatable.stderr
@@ -1,10 +1,25 @@
-error: proc macro panicked
- --> $DIR/not-repeatable.rs:15:13
+error[E0599]: the method `quote_into_iter` exists for struct `Ipv4Addr`, but its trait bounds were not satisfied
+ --> $DIR/not-repeatable.rs:11:13
|
+LL | struct Ipv4Addr;
+ | --------------- method `quote_into_iter` not found for this struct because it doesn't satisfy `Ipv4Addr: Iterator`, `Ipv4Addr: ToTokens`, `Ipv4Addr: proc_macro::ext::RepIteratorExt` or `Ipv4Addr: proc_macro::ext::RepToTokensExt`
+...
LL | let _ = quote! { $($ip)* };
- | ^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^ method cannot be called on `Ipv4Addr` due to unsatisfied trait bounds
|
- = help: message: `$` must be followed by an ident or `$` in `quote!`
+ = note: the following trait bounds were not satisfied:
+ `Ipv4Addr: Iterator`
+ which is required by `Ipv4Addr: proc_macro::ext::RepIteratorExt`
+ `&Ipv4Addr: Iterator`
+ which is required by `&Ipv4Addr: proc_macro::ext::RepIteratorExt`
+ `Ipv4Addr: ToTokens`
+ which is required by `Ipv4Addr: proc_macro::ext::RepToTokensExt`
+ `&mut Ipv4Addr: Iterator`
+ which is required by `&mut Ipv4Addr: proc_macro::ext::RepIteratorExt`
+note: the traits `Iterator` and `ToTokens` must be implemented
+ --> $SRC_DIR/proc_macro/src/to_tokens.rs:LL:COL
+ --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
error: aborting due to 1 previous error
+For more information about this error, try `rustc --explain E0599`.