Auto merge of #86588 - JohnTitor:rollup-ibgjbkf, r=JohnTitor

Rollup of 8 pull requests

Successful merges:

 - #86137 (Error code cleanup and enforce checks)
 - #86296 (Add documentation for various THIR structs)
 - #86415 (Document associativity of iterator folds.)
 - #86533 (Support lowercase error codes in `--explain`)
 - #86536 (Edition 2021 enables disjoint capture)
 - #86560 (Update cargo)
 - #86561 (chore(rustdoc): Remove unused impl block)
 - #86566 (Use `use_verbose` for `mir::Constant`)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2021-06-24 04:48:46 +00:00
commit 964a81eb37
25 changed files with 319 additions and 158 deletions

View File

@ -180,7 +180,7 @@ dependencies = [
"merge",
"num_cpus",
"once_cell",
"opener",
"opener 0.5.0",
"pretty_assertions",
"serde",
"serde_json",
@ -195,7 +195,9 @@ version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31accafdb70df7871592c058eca3985b71104e15ac32f64706022c58867da931"
dependencies = [
"lazy_static",
"memchr",
"regex-automata",
]
[[package]]
@ -253,7 +255,7 @@ checksum = "81a18687293a1546b67c246452202bbbf143d239cb43494cc163da14979082da"
[[package]]
name = "cargo"
version = "0.55.0"
version = "0.56.0"
dependencies = [
"anyhow",
"atty",
@ -288,11 +290,10 @@ dependencies = [
"log",
"memchr",
"num_cpus",
"opener",
"opener 0.5.0",
"openssl",
"percent-encoding 2.1.0",
"pretty_env_logger",
"rand 0.8.3",
"rustc-workspace-hack",
"rustfix 0.6.0",
"semver 1.0.3",
@ -375,10 +376,12 @@ dependencies = [
"flate2",
"git2",
"glob",
"itertools 0.10.0",
"lazy_static",
"remove_dir_all",
"serde_json",
"tar",
"termcolor",
"toml",
"url 2.2.2",
]
@ -575,7 +578,7 @@ dependencies = [
"bytecount",
"clap",
"itertools 0.9.0",
"opener",
"opener 0.4.1",
"regex",
"shell-escape",
"walkdir",
@ -2418,6 +2421,16 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "opener"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ea3ebcd72a54701f56345f16785a6d3ac2df7e986d273eb4395c0b01db17952"
dependencies = [
"bstr",
"winapi 0.3.9",
]
[[package]]
name = "openssl"
version = "0.10.33"
@ -3558,6 +3571,7 @@ dependencies = [
name = "rustc-workspace-hack"
version = "1.0.0"
dependencies = [
"bstr",
"byteorder",
"crossbeam-utils 0.8.3",
"libc",
@ -5158,9 +5172,9 @@ dependencies = [
[[package]]
name = "termcolor"
version = "1.1.0"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f"
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
dependencies = [
"winapi-util",
]

View File

@ -528,8 +528,12 @@ fn stderr_isatty() -> bool {
}
fn handle_explain(registry: Registry, code: &str, output: ErrorOutputType) {
let normalised =
if code.starts_with('E') { code.to_string() } else { format!("E{0:0>4}", code) };
let upper_cased_code = code.to_ascii_uppercase();
let normalised = if upper_cased_code.starts_with('E') {
upper_cased_code
} else {
format!("E{0:0>4}", code)
};
match registry.try_find_description(&normalised) {
Ok(Some(description)) => {
let mut is_in_code_block = false;

View File

@ -609,7 +609,7 @@ E0783: include_str!("./error_codes/E0783.md"),
// E0540, // multiple rustc_deprecated attributes
E0544, // multiple stability levels
// E0548, // replaced with a generic attribute input check
E0553, // multiple rustc_const_unstable attributes
// E0553, // multiple rustc_const_unstable attributes
// E0555, // replaced with a generic attribute input check
// E0558, // replaced with a generic attribute input check
// E0563, // cannot determine a type for this `impl Trait` removed in 6383de15
@ -620,10 +620,9 @@ E0783: include_str!("./error_codes/E0783.md"),
// E0612, // merged into E0609
// E0613, // Removed (merged with E0609)
E0625, // thread-local statics cannot be accessed at compile-time
E0629, // missing 'feature' (rustc_const_unstable)
// rustc_const_unstable attribute must be paired with stable/unstable
// attribute
E0630,
// E0629, // missing 'feature' (rustc_const_unstable)
// E0630, // rustc_const_unstable attribute must be paired with stable/unstable
// attribute
E0632, // cannot provide explicit generic arguments when `impl Trait` is
// used in argument position
E0640, // infer outlives requirements

View File

@ -1,3 +1,13 @@
//! THIR datatypes and definitions. See the [rustc dev guide] for more info.
//!
//! If you compare the THIR [`ExprKind`] to [`hir::ExprKind`], you will see it is
//! a good bit simpler. In fact, a number of the more straight-forward
//! MIR simplifications are already done in the lowering to THIR. For
//! example, method calls and overloaded operators are absent: they are
//! expected to be converted into [`ExprKind::Call`] instances.
//!
//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/thir.html
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
use rustc_hir as hir;
use rustc_hir::def::CtorKind;
@ -24,6 +34,7 @@ use std::fmt;
use std::ops::Index;
newtype_index! {
/// An index to an [`Arm`] stored in [`Thir::arms`]
#[derive(HashStable)]
pub struct ArmId {
DEBUG_FORMAT = "a{}"
@ -31,6 +42,7 @@ newtype_index! {
}
newtype_index! {
/// An index to an [`Expr`] stored in [`Thir::exprs`]
#[derive(HashStable)]
pub struct ExprId {
DEBUG_FORMAT = "e{}"
@ -39,6 +51,7 @@ newtype_index! {
newtype_index! {
#[derive(HashStable)]
/// An index to a [`Stmt`] stored in [`Thir::stmts`]
pub struct StmtId {
DEBUG_FORMAT = "s{}"
}
@ -46,6 +59,9 @@ newtype_index! {
macro_rules! thir_with_elements {
($($name:ident: $id:ty => $value:ty,)*) => {
/// A container for a THIR body.
///
/// This can be indexed directly by any THIR index (e.g. [`ExprId`]).
#[derive(Debug, HashStable)]
pub struct Thir<'tcx> {
$(
@ -88,18 +104,28 @@ pub enum LintLevel {
#[derive(Debug, HashStable)]
pub struct Block {
/// Whether the block itself has a label. Used by `label: {}`
/// and `try` blocks.
///
/// This does *not* include labels on loops, e.g. `'label: loop {}`.
pub targeted_by_break: bool,
pub region_scope: region::Scope,
pub opt_destruction_scope: Option<region::Scope>,
/// The span of the block, including the opening braces,
/// the label, and the `unsafe` keyword, if present.
pub span: Span,
/// The statements in the blocK.
pub stmts: Box<[StmtId]>,
/// The trailing expression of the block, if any.
pub expr: Option<ExprId>,
pub safety_mode: BlockSafety,
}
#[derive(Debug, HashStable)]
pub struct Adt<'tcx> {
/// The ADT we're constructing.
pub adt_def: &'tcx AdtDef,
/// The variant of the ADT.
pub variant_index: VariantIdx,
pub substs: SubstsRef<'tcx>,
@ -108,13 +134,16 @@ pub struct Adt<'tcx> {
pub user_ty: Option<Canonical<'tcx, UserType<'tcx>>>,
pub fields: Box<[FieldExpr]>,
/// The base, e.g. `Foo {x: 1, .. base}`.
pub base: Option<FruInfo<'tcx>>,
}
#[derive(Copy, Clone, Debug, HashStable)]
pub enum BlockSafety {
Safe,
/// A compiler-generated unsafe block
BuiltinUnsafe,
/// An `unsafe` block. The `HirId` is the ID of the block.
ExplicitUnsafe(hir::HirId),
}
@ -126,32 +155,34 @@ pub struct Stmt<'tcx> {
#[derive(Debug, HashStable)]
pub enum StmtKind<'tcx> {
/// An expression with a trailing semicolon.
Expr {
/// scope for this statement; may be used as lifetime of temporaries
/// The scope for this statement; may be used as lifetime of temporaries.
scope: region::Scope,
/// expression being evaluated in this statement
/// The expression being evaluated in this statement.
expr: ExprId,
},
/// A `let` binding.
Let {
/// scope for variables bound in this let; covers this and
/// remaining statements in block
/// The scope for variables bound in this `let`; it covers this and
/// all the remaining statements in the block.
remainder_scope: region::Scope,
/// scope for the initialization itself; might be used as
/// lifetime of temporaries
/// The scope for the initialization itself; might be used as
/// lifetime of temporaries.
init_scope: region::Scope,
/// `let <PAT> = ...`
///
/// if a type is included, it is added as an ascription pattern
/// If a type annotation is included, it is added as an ascription pattern.
pattern: Pat<'tcx>,
/// let pat: ty = <INIT> ...
/// `let pat: ty = <INIT>`
initializer: Option<ExprId>,
/// the lint level for this let-statement
/// The lint level for this `let` statement.
lint_level: LintLevel,
},
}
@ -160,27 +191,14 @@ pub enum StmtKind<'tcx> {
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
rustc_data_structures::static_assert_size!(Expr<'_>, 104);
/// The Thir trait implementor lowers their expressions (`&'tcx H::Expr`)
/// into instances of this `Expr` enum. This lowering can be done
/// basically as lazily or as eagerly as desired: every recursive
/// reference to an expression in this enum is an `ExprId`, which
/// may in turn be another instance of this enum (boxed), or else an
/// unlowered `&'tcx H::Expr`. Note that instances of `Expr` are very
/// short-lived. They are created by `Thir::to_expr`, analyzed and
/// converted into MIR, and then discarded.
///
/// If you compare `Expr` to the full compiler AST, you will see it is
/// a good bit simpler. In fact, a number of the more straight-forward
/// MIR simplifications are already done in the impl of `Thir`. For
/// example, method calls and overloaded operators are absent: they are
/// expected to be converted into `Expr::Call` instances.
/// A THIR expression.
#[derive(Debug, HashStable)]
pub struct Expr<'tcx> {
/// type of this expression
/// The type of this expression
pub ty: Ty<'tcx>,
/// lifetime of this expression if it should be spilled into a
/// temporary; should be None only if in a constant context
/// The lifetime of this expression if it should be spilled into a
/// temporary; should be `None` only if in a constant context
pub temp_lifetime: Option<region::Scope>,
/// span of the expression in the source
@ -192,88 +210,120 @@ pub struct Expr<'tcx> {
#[derive(Debug, HashStable)]
pub enum ExprKind<'tcx> {
/// `Scope`s are used to explicitely mark destruction scopes,
/// and to track the `HirId` of the expressions within the scope.
Scope {
region_scope: region::Scope,
lint_level: LintLevel,
value: ExprId,
},
/// A `box <value>` expression.
Box {
value: ExprId,
},
/// An `if` expression.
If {
cond: ExprId,
then: ExprId,
else_opt: Option<ExprId>,
},
/// A function call. Method calls and overloaded operators are converted to plain function calls.
Call {
/// The type of the function. This is often a [`FnDef`] or a [`FnPtr`].
///
/// [`FnDef`]: ty::TyKind::FnDef
/// [`FnPtr`]: ty::TyKind::FnPtr
ty: Ty<'tcx>,
/// The function itself.
fun: ExprId,
/// The arguments passed to the function.
///
/// Note: in some cases (like calling a closure), the function call `f(...args)` gets
/// rewritten as a call to a function trait method (e.g. `FnOnce::call_once(f, (...args))`).
args: Box<[ExprId]>,
/// Whether this is from a call in HIR, rather than from an overloaded
/// operator. `true` for overloaded function call.
/// Whether this is from an overloaded operator rather than a
/// function call from HIR. `true` for overloaded function call.
from_hir_call: bool,
/// This `Span` is the span of the function, without the dot and receiver
/// (e.g. `foo(a, b)` in `x.foo(a, b)`
/// The span of the function, without the dot and receiver
/// (e.g. `foo(a, b)` in `x.foo(a, b)`).
fn_span: Span,
},
/// A *non-overloaded* dereference.
Deref {
arg: ExprId,
}, // NOT overloaded!
},
/// A *non-overloaded* binary operation.
Binary {
op: BinOp,
lhs: ExprId,
rhs: ExprId,
}, // NOT overloaded!
},
/// A logical operation. This is distinct from `BinaryOp` because
/// the operands need to be lazily evaluated.
LogicalOp {
op: LogicalOp,
lhs: ExprId,
rhs: ExprId,
}, // NOT overloaded!
// LogicalOp is distinct from BinaryOp because of lazy evaluation of the operands.
},
/// A *non-overloaded* unary operation. Note that here the deref (`*`)
/// operator is represented by `ExprKind::Deref`.
Unary {
op: UnOp,
arg: ExprId,
}, // NOT overloaded!
},
/// A cast: `<source> as <type>`. The type we cast to is the type of
/// the parent expression.
Cast {
source: ExprId,
},
Use {
source: ExprId,
}, // Use a lexpr to get a vexpr.
/// A coercion from `!` to any type.
NeverToAny {
source: ExprId,
},
/// A pointer cast. More information can be found in [`PointerCast`].
Pointer {
cast: PointerCast,
source: ExprId,
},
/// A `loop` expression.
Loop {
body: ExprId,
},
/// A `match` expression.
Match {
scrutinee: ExprId,
arms: Box<[ArmId]>,
},
/// A block.
Block {
body: Block,
},
/// An assignment: `lhs = rhs`.
Assign {
lhs: ExprId,
rhs: ExprId,
},
/// A *non-overloaded* operation assignment, e.g. `lhs += rhs`.
AssignOp {
op: BinOp,
lhs: ExprId,
rhs: ExprId,
},
/// Access to a struct or tuple field.
Field {
lhs: ExprId,
/// This can be a named (`.foo`) or unnamed (`.0`) field.
name: Field,
},
/// A *non-overloaded* indexing operation.
Index {
lhs: ExprId,
index: ExprId,
},
/// A local variable.
VarRef {
id: hir::HirId,
},
@ -285,6 +335,7 @@ pub enum ExprKind<'tcx> {
/// HirId of the root variable
var_hir_id: hir::HirId,
},
/// A borrow, e.g. `&arg`.
Borrow {
borrow_kind: BorrowKind,
arg: ExprId,
@ -294,40 +345,51 @@ pub enum ExprKind<'tcx> {
mutability: hir::Mutability,
arg: ExprId,
},
/// A `break` expression.
Break {
label: region::Scope,
value: Option<ExprId>,
},
/// A `continue` expression.
Continue {
label: region::Scope,
},
/// A `return` expression.
Return {
value: Option<ExprId>,
},
/// An inline `const` block, e.g. `const {}`.
ConstBlock {
value: &'tcx Const<'tcx>,
},
/// An array literal constructed from one repeated element, e.g. `[1; 5]`.
Repeat {
value: ExprId,
count: &'tcx Const<'tcx>,
},
/// An array, e.g. `[a, b, c, d]`.
Array {
fields: Box<[ExprId]>,
},
/// A tuple, e.g. `(a, b, c, d)`.
Tuple {
fields: Box<[ExprId]>,
},
/// An ADT constructor, e.g. `Foo {x: 1, y: 2}`.
Adt(Box<Adt<'tcx>>),
/// A type ascription on a place.
PlaceTypeAscription {
source: ExprId,
/// Type that the user gave to this expression
user_ty: Option<Canonical<'tcx, UserType<'tcx>>>,
},
/// A type ascription on a value, e.g. `42: i32`.
ValueTypeAscription {
source: ExprId,
/// Type that the user gave to this expression
user_ty: Option<Canonical<'tcx, UserType<'tcx>>>,
},
/// A closure definition.
Closure {
closure_id: DefId,
substs: UpvarSubsts<'tcx>,
@ -335,6 +397,7 @@ pub enum ExprKind<'tcx> {
movability: Option<hir::Movability>,
fake_reads: Vec<(ExprId, FakeReadCause, hir::HirId)>,
},
/// A literal.
Literal {
literal: &'tcx Const<'tcx>,
user_ty: Option<Canonical<'tcx, UserType<'tcx>>>,
@ -351,6 +414,7 @@ pub enum ExprKind<'tcx> {
literal: &'tcx Const<'tcx>,
def_id: DefId,
},
/// Inline assembly, i.e. `asm!()`.
InlineAsm {
template: &'tcx [InlineAsmTemplatePiece],
operands: Box<[InlineAsmOperand<'tcx>]>,
@ -359,16 +423,21 @@ pub enum ExprKind<'tcx> {
},
/// An expression taking a reference to a thread local.
ThreadLocalRef(DefId),
/// Inline LLVM assembly, i.e. `llvm_asm!()`.
LlvmInlineAsm {
asm: &'tcx hir::LlvmInlineAsmInner,
outputs: Box<[ExprId]>,
inputs: Box<[ExprId]>,
},
/// A `yield` expression.
Yield {
value: ExprId,
},
}
/// Represents the association of a field identifier and an expression.
///
/// This is used in struct constructors.
#[derive(Debug, HashStable)]
pub struct FieldExpr {
pub name: Field,
@ -381,6 +450,7 @@ pub struct FruInfo<'tcx> {
pub field_types: Box<[Ty<'tcx>]>,
}
/// A `match` arm.
#[derive(Debug, HashStable)]
pub struct Arm<'tcx> {
pub pattern: Pat<'tcx>,
@ -391,6 +461,7 @@ pub struct Arm<'tcx> {
pub span: Span,
}
/// A `match` guard.
#[derive(Debug, HashStable)]
pub enum Guard<'tcx> {
If(ExprId),
@ -399,7 +470,9 @@ pub enum Guard<'tcx> {
#[derive(Copy, Clone, Debug, HashStable)]
pub enum LogicalOp {
/// The `&&` operator.
And,
/// The `||` operator.
Or,
}
@ -516,6 +589,7 @@ pub struct Ascription<'tcx> {
#[derive(Clone, Debug, PartialEq, HashStable)]
pub enum PatKind<'tcx> {
/// A wildward pattern: `_`.
Wild,
AscribeUserType {

View File

@ -426,14 +426,14 @@ impl ExtraComments<'tcx> {
}
}
fn use_verbose(ty: &&TyS<'tcx>) -> bool {
fn use_verbose(ty: &&TyS<'tcx>, fn_def: bool) -> bool {
match ty.kind() {
ty::Int(_) | ty::Uint(_) | ty::Bool | ty::Char | ty::Float(_) => false,
// Unit type
ty::Tuple(g_args) if g_args.is_empty() => false,
ty::Tuple(g_args) => g_args.iter().any(|g_arg| use_verbose(&g_arg.expect_ty())),
ty::Array(ty, _) => use_verbose(ty),
ty::FnDef(..) => false,
ty::Tuple(g_args) => g_args.iter().any(|g_arg| use_verbose(&g_arg.expect_ty(), fn_def)),
ty::Array(ty, _) => use_verbose(ty, fn_def),
ty::FnDef(..) => fn_def,
_ => true,
}
}
@ -442,28 +442,20 @@ impl Visitor<'tcx> for ExtraComments<'tcx> {
fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) {
self.super_constant(constant, location);
let Constant { span, user_ty, literal } = constant;
match literal.ty().kind() {
ty::Int(_) | ty::Uint(_) | ty::Bool | ty::Char => {}
// Unit type
ty::Tuple(tys) if tys.is_empty() => {}
_ => {
self.push("mir::Constant");
self.push(&format!(
"+ span: {}",
self.tcx.sess.source_map().span_to_embeddable_string(*span)
));
if let Some(user_ty) = user_ty {
self.push(&format!("+ user_ty: {:?}", user_ty));
}
match literal {
ConstantKind::Ty(literal) => self.push(&format!("+ literal: {:?}", literal)),
ConstantKind::Val(val, ty) => {
// To keep the diffs small, we render this almost like we render ty::Const
self.push(&format!(
"+ literal: Const {{ ty: {}, val: Value({:?}) }}",
ty, val
))
}
if use_verbose(&literal.ty(), true) {
self.push("mir::Constant");
self.push(&format!(
"+ span: {}",
self.tcx.sess.source_map().span_to_embeddable_string(*span)
));
if let Some(user_ty) = user_ty {
self.push(&format!("+ user_ty: {:?}", user_ty));
}
match literal {
ConstantKind::Ty(literal) => self.push(&format!("+ literal: {:?}", literal)),
ConstantKind::Val(val, ty) => {
// To keep the diffs small, we render this almost like we render ty::Const
self.push(&format!("+ literal: Const {{ ty: {}, val: Value({:?}) }}", ty, val))
}
}
}
@ -472,7 +464,7 @@ impl Visitor<'tcx> for ExtraComments<'tcx> {
fn visit_const(&mut self, constant: &&'tcx ty::Const<'tcx>, _: Location) {
self.super_const(constant);
let ty::Const { ty, val, .. } = constant;
if use_verbose(ty) {
if use_verbose(ty, false) {
self.push("ty::Const");
self.push(&format!("+ ty: {:?}", ty));
let val = match val {

View File

@ -177,7 +177,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// We now fake capture information for all variables that are mentioned within the closure
// We do this after handling migrations so that min_captures computes before
if !self.tcx.features().capture_disjoint_fields {
if !enable_precise_capture(self.tcx, span) {
let mut capture_information: InferredCaptureInformation<'tcx> = Default::default();
if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) {
@ -212,7 +212,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// If we have an origin, store it.
if let Some(origin) = delegate.current_origin.clone() {
let origin = if self.tcx.features().capture_disjoint_fields {
let origin = if enable_precise_capture(self.tcx, span) {
(origin.0, restrict_capture_precision(origin.1))
} else {
(origin.0, Place { projections: vec![], ..origin.1 })
@ -1924,3 +1924,13 @@ fn determine_place_ancestry_relation(
PlaceAncestryRelation::Divergent
}
}
/// Precise capture is enabled if the feature gate `capture_disjoint_fields` is enabled or if
/// user is using Rust Edition 2021 or higher.
///
/// `span` is the span of the closure.
fn enable_precise_capture(tcx: TyCtxt<'_>, span: Span) -> bool {
// We use span here to ensure that if the closure was generated by a macro with a different
// edition.
tcx.features().capture_disjoint_fields || span.rust_2021()
}

View File

@ -248,6 +248,11 @@ pub trait DoubleEndedIterator: Iterator {
/// Folding is useful whenever you have a collection of something, and want
/// to produce a single value from it.
///
/// Note: `rfold()` combines elements in a *right-associative* fashion. For associative
/// operators like `+`, the order the elements are combined in is not important, but for non-associative
/// operators like `-` the order will affect the final result.
/// For a *left-associative* version of `rfold()`, see [`Iterator::fold()`].
///
/// # Examples
///
/// Basic usage:
@ -262,7 +267,8 @@ pub trait DoubleEndedIterator: Iterator {
/// assert_eq!(sum, 6);
/// ```
///
/// This example builds a string, starting with an initial value
/// This example demonstrates the right-associative nature of `rfold()`:
/// it builds a string, starting with an initial value
/// and continuing with each element from the back until the front:
///
/// ```
@ -276,6 +282,7 @@ pub trait DoubleEndedIterator: Iterator {
///
/// assert_eq!(result, "(1 + (2 + (3 + (4 + (5 + 0)))))");
/// ```
#[doc(alias = "foldr")]
#[inline]
#[stable(feature = "iter_rfold", since = "1.27.0")]
fn rfold<B, F>(mut self, init: B, mut f: F) -> B

View File

@ -2083,6 +2083,11 @@ pub trait Iterator {
/// Note: [`reduce()`] can be used to use the first element as the initial
/// value, if the accumulator type and item type is the same.
///
/// Note: `fold()` combines elements in a *left-associative* fashion. For associative
/// operators like `+`, the order the elements are combined in is not important, but for non-associative
/// operators like `-` the order will affect the final result.
/// For a *right-associative* version of `fold()`, see [`DoubleEndedIterator::rfold()`].
///
/// # Note to Implementors
///
/// Several of the other (forward) methods have default implementations in
@ -2116,6 +2121,21 @@ pub trait Iterator {
///
/// And so, our final result, `6`.
///
/// This example demonstrates the left-associative nature of `fold()`:
/// it builds a string, starting with an initial value
/// and continuing with each element from the front until the back:
///
/// ```
/// let numbers = [1, 2, 3, 4, 5];
///
/// let zero = "0".to_string();
///
/// let result = numbers.iter().fold(zero, |acc, &x| {
/// format!("({} + {})", acc, x)
/// });
///
/// assert_eq!(result, "(((((0 + 1) + 2) + 3) + 4) + 5)");
/// ```
/// It's common for people who haven't used iterators a lot to
/// use a `for` loop with a list of things to build up a result. Those
/// can be turned into `fold()`s:
@ -2140,7 +2160,7 @@ pub trait Iterator {
/// ```
///
/// [`reduce()`]: Iterator::reduce
#[doc(alias = "inject")]
#[doc(alias = "inject", alias = "foldl")]
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
fn fold<B, F>(mut self, init: B, mut f: F) -> B

View File

@ -48,7 +48,7 @@ toml = "0.5"
lazy_static = "1.3.0"
time = "0.1"
ignore = "0.4.10"
opener = "0.4"
opener = "0.5"
merge = "0.1.0"
once_cell = "1.7.2"

View File

@ -101,28 +101,6 @@ crate struct RenderType {
generics: Option<Vec<Generic>>,
}
impl Serialize for RenderType {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if let Some(name) = &self.name {
let mut seq = serializer.serialize_seq(None)?;
if let Some(id) = self.idx {
seq.serialize_element(&id)?;
} else {
seq.serialize_element(&name)?;
}
if let Some(generics) = &self.generics {
seq.serialize_element(&generics)?;
}
seq.end()
} else {
serializer.serialize_none()
}
}
}
/// A type used for the search index.
#[derive(Debug)]
crate struct Generic {

View File

@ -14,9 +14,6 @@
- _2 = CheckedAdd(const 1_u32, const 1_u32); // scope 0 at $DIR/checked_add.rs:5:18: 5:23
- assert(!move (_2.1: bool), "attempt to compute `{} + {}`, which would overflow", const 1_u32, const 1_u32) -> bb1; // scope 0 at $DIR/checked_add.rs:5:18: 5:23
+ _2 = const (2_u32, false); // scope 0 at $DIR/checked_add.rs:5:18: 5:23
+ // mir::Constant
+ // + span: $DIR/checked_add.rs:5:18: 5:23
+ // + literal: Const { ty: (u32, bool), val: Value(ByRef { alloc: Allocation { bytes: [2, 0, 0, 0, 0, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [31], len: Size { raw: 8 } }, align: Align { pow2: 2 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) }
+ assert(!const false, "attempt to compute `{} + {}`, which would overflow", const 1_u32, const 1_u32) -> bb1; // scope 0 at $DIR/checked_add.rs:5:18: 5:23
}

View File

@ -18,9 +18,6 @@
- assert(!move (_3.1: bool), "attempt to compute `{} + {}`, which would overflow", move _2, const 1_u8) -> bb1; // scope 0 at $DIR/indirect.rs:5:13: 5:29
+ _2 = const 2_u8; // scope 0 at $DIR/indirect.rs:5:13: 5:25
+ _3 = const (3_u8, false); // scope 0 at $DIR/indirect.rs:5:13: 5:29
+ // mir::Constant
+ // + span: $DIR/indirect.rs:5:13: 5:29
+ // + literal: Const { ty: (u8, bool), val: Value(ByRef { alloc: Allocation { bytes: [3, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [3], len: Size { raw: 2 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) }
+ assert(!const false, "attempt to compute `{} + {}`, which would overflow", const 2_u8, const 1_u8) -> bb1; // scope 0 at $DIR/indirect.rs:5:13: 5:29
}

View File

@ -15,9 +15,6 @@
(_3.1: u8) = const 2_u8; // scope 0 at $DIR/issue-67019.rs:11:11: 11:17
- (_2.0: (u8, u8)) = move _3; // scope 0 at $DIR/issue-67019.rs:11:10: 11:19
+ (_2.0: (u8, u8)) = const (1_u8, 2_u8); // scope 0 at $DIR/issue-67019.rs:11:10: 11:19
+ // mir::Constant
+ // + span: $DIR/issue-67019.rs:11:10: 11:19
+ // + literal: Const { ty: (u8, u8), val: Value(ByRef { alloc: Allocation { bytes: [1, 2], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [3], len: Size { raw: 2 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) }
StorageDead(_3); // scope 0 at $DIR/issue-67019.rs:11:18: 11:19
_1 = test(move _2) -> bb1; // scope 0 at $DIR/issue-67019.rs:11:5: 11:20
// mir::Constant

View File

@ -20,9 +20,6 @@
StorageLive(_2); // scope 1 at $DIR/mutable_variable_aggregate.rs:7:9: 7:10
- _2 = _1; // scope 1 at $DIR/mutable_variable_aggregate.rs:7:13: 7:14
+ _2 = const (42_i32, 99_i32); // scope 1 at $DIR/mutable_variable_aggregate.rs:7:13: 7:14
+ // mir::Constant
+ // + span: $DIR/mutable_variable_aggregate.rs:7:13: 7:14
+ // + literal: Const { ty: (i32, i32), val: Value(ByRef { alloc: Allocation { bytes: [42, 0, 0, 0, 99, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [255], len: Size { raw: 8 } }, align: Align { pow2: 2 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) }
nop; // scope 0 at $DIR/mutable_variable_aggregate.rs:4:11: 8:2
StorageDead(_2); // scope 1 at $DIR/mutable_variable_aggregate.rs:8:1: 8:2
StorageDead(_1); // scope 0 at $DIR/mutable_variable_aggregate.rs:8:1: 8:2

View File

@ -27,9 +27,6 @@
- _2 = CheckedAdd(const 2_i32, const 2_i32); // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18
- assert(!move (_2.1: bool), "attempt to compute `{} + {}`, which would overflow", const 2_i32, const 2_i32) -> bb1; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18
+ _2 = const (4_i32, false); // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18
+ // mir::Constant
+ // + span: $DIR/optimizes_into_variable.rs:12:13: 12:18
+ // + literal: Const { ty: (i32, bool), val: Value(ByRef { alloc: Allocation { bytes: [4, 0, 0, 0, 0, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [31], len: Size { raw: 8 } }, align: Align { pow2: 2 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) }
+ assert(!const false, "attempt to compute `{} + {}`, which would overflow", const 2_i32, const 2_i32) -> bb1; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18
}

View File

@ -27,9 +27,6 @@
- _2 = CheckedAdd(const 2_i32, const 2_i32); // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18
- assert(!move (_2.1: bool), "attempt to compute `{} + {}`, which would overflow", const 2_i32, const 2_i32) -> bb1; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18
+ _2 = const (4_i32, false); // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18
+ // mir::Constant
+ // + span: $DIR/optimizes_into_variable.rs:12:13: 12:18
+ // + literal: Const { ty: (i32, bool), val: Value(ByRef { alloc: Allocation { bytes: [4, 0, 0, 0, 0, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [31], len: Size { raw: 8 } }, align: Align { pow2: 2 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) }
+ assert(!const false, "attempt to compute `{} + {}`, which would overflow", const 2_i32, const 2_i32) -> bb1; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18
}

View File

@ -9,9 +9,6 @@
- _1 = CheckedAdd(const 2_u32, const 2_u32); // scope 0 at $DIR/return_place.rs:6:5: 6:10
- assert(!move (_1.1: bool), "attempt to compute `{} + {}`, which would overflow", const 2_u32, const 2_u32) -> bb1; // scope 0 at $DIR/return_place.rs:6:5: 6:10
+ _1 = const (4_u32, false); // scope 0 at $DIR/return_place.rs:6:5: 6:10
+ // mir::Constant
+ // + span: $DIR/return_place.rs:6:5: 6:10
+ // + literal: Const { ty: (u32, bool), val: Value(ByRef { alloc: Allocation { bytes: [4, 0, 0, 0, 0, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [31], len: Size { raw: 8 } }, align: Align { pow2: 2 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) }
+ assert(!const false, "attempt to compute `{} + {}`, which would overflow", const 2_u32, const 2_u32) -> bb1; // scope 0 at $DIR/return_place.rs:6:5: 6:10
}

View File

@ -18,9 +18,6 @@
StorageLive(_3); // scope 1 at $DIR/tuple_literal_propagation.rs:5:13: 5:14
- _3 = _1; // scope 1 at $DIR/tuple_literal_propagation.rs:5:13: 5:14
+ _3 = const (1_u32, 2_u32); // scope 1 at $DIR/tuple_literal_propagation.rs:5:13: 5:14
+ // mir::Constant
+ // + span: $DIR/tuple_literal_propagation.rs:5:13: 5:14
+ // + literal: Const { ty: (u32, u32), val: Value(ByRef { alloc: Allocation { bytes: [1, 0, 0, 0, 2, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [255], len: Size { raw: 8 } }, align: Align { pow2: 2 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) }
_2 = consume(move _3) -> bb1; // scope 1 at $DIR/tuple_literal_propagation.rs:5:5: 5:15
// mir::Constant
// + span: $DIR/tuple_literal_propagation.rs:5:5: 5:12

View File

@ -12,9 +12,6 @@
- _0 = Baz { x: move _2, y: const 0f32, z: const false }; // scope 0 at $DIR/deaggregator_test.rs:9:5: 9:35
+ (_0.0: usize) = move _2; // scope 0 at $DIR/deaggregator_test.rs:9:5: 9:35
+ (_0.1: f32) = const 0f32; // scope 0 at $DIR/deaggregator_test.rs:9:5: 9:35
// mir::Constant
// + span: $DIR/deaggregator_test.rs:9:20: 9:23
// + literal: Const { ty: f32, val: Value(Scalar(0x00000000)) }
+ (_0.2: bool) = const false; // scope 0 at $DIR/deaggregator_test.rs:9:5: 9:35
StorageDead(_2); // scope 0 at $DIR/deaggregator_test.rs:9:34: 9:35
return; // scope 0 at $DIR/deaggregator_test.rs:10:2: 10:2

View File

@ -12,9 +12,6 @@
StorageLive(_3); // scope 0 at $DIR/if-condition-int.rs:53:8: 53:9
_3 = _1; // scope 0 at $DIR/if-condition-int.rs:53:8: 53:9
_2 = Eq(move _3, const -42f32); // scope 0 at $DIR/if-condition-int.rs:53:8: 53:18
// mir::Constant
// + span: $DIR/if-condition-int.rs:53:13: 53:18
// + literal: Const { ty: f32, val: Value(Scalar(0xc2280000)) }
StorageDead(_3); // scope 0 at $DIR/if-condition-int.rs:53:17: 53:18
switchInt(move _2) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/if-condition-int.rs:53:5: 53:35
}

View File

@ -38,9 +38,6 @@
// mir::Constant
// + span: $DIR/simplify-locals-removes-unused-consts.rs:14:5: 14:12
// + literal: Const { ty: fn(((), ())) {use_zst}, val: Value(Scalar(<ZST>)) }
// mir::Constant
// + span: $DIR/simplify-locals-removes-unused-consts.rs:14:5: 14:22
// + literal: Const { ty: ((), ()), val: Value(Scalar(<ZST>)) }
}
bb1: {

View File

@ -0,0 +1,23 @@
// edition:2021
// run-pass
// Test that edition 2021 enables disjoint capture by default.
struct Point {
x: i32,
y: i32,
}
fn main() {
let mut p = Point { x: 10, y: 10 };
let c = || {
println!("{}", p.x);
};
// `c` should only capture `p.x`, therefore mutating `p.y` is allowed.
let py = &mut p.y;
c();
*py = 20;
}

@ -1 +1 @@
Subproject commit 44456677b5d1d82fe981c955dc5c67734b31f340
Subproject commit 9233aa06c801801cff75df65df718d70905a235e

View File

@ -61,6 +61,7 @@ features = [
]
[dependencies]
bstr = { version = "0.2.13", features = ["default"] }
byteorder = { version = "1", features = ['default', 'std'] }
curl-sys = { version = "0.4.13", features = ["http2", "libnghttp2-sys"], optional = true }
crossbeam-utils = { version = "0.8.0", features = ["nightly"] }

View File

@ -6,20 +6,33 @@ use std::ffi::OsStr;
use std::fs::read_to_string;
use std::path::Path;
use regex::Regex;
// A few of those error codes can't be tested but all the others can and *should* be tested!
const EXEMPTED_FROM_TEST: &[&str] = &[
"E0227", "E0279", "E0280", "E0313", "E0314", "E0315", "E0377", "E0461", "E0462", "E0464",
"E0465", "E0473", "E0474", "E0475", "E0476", "E0479", "E0480", "E0481", "E0482", "E0483",
"E0484", "E0485", "E0486", "E0487", "E0488", "E0489", "E0514", "E0519", "E0523", "E0553",
"E0554", "E0570", "E0629", "E0630", "E0640", "E0717", "E0729",
"E0227", "E0279", "E0280", "E0313", "E0377", "E0461", "E0462", "E0464", "E0465", "E0476",
"E0482", "E0514", "E0519", "E0523", "E0554", "E0570", "E0640", "E0717", "E0729",
];
// Some error codes don't have any tests apparently...
const IGNORE_EXPLANATION_CHECK: &[&str] = &["E0570", "E0601", "E0602", "E0729"];
// If the file path contains any of these, we don't want to try to extract error codes from it.
//
// We need to declare each path in the windows version (with backslash).
const PATHS_TO_IGNORE_FOR_EXTRACTION: &[&str] =
&["src/test/", "src\\test\\", "src/doc/", "src\\doc\\", "src/tools/", "src\\tools\\"];
#[derive(Default, Debug)]
struct ErrorCodeStatus {
has_test: bool,
has_explanation: bool,
is_used: bool,
}
fn check_error_code_explanation(
f: &str,
error_codes: &mut HashMap<String, bool>,
error_codes: &mut HashMap<String, ErrorCodeStatus>,
err_code: String,
) -> bool {
let mut invalid_compile_fail_format = false;
@ -30,7 +43,7 @@ fn check_error_code_explanation(
if s.starts_with("```") {
if s.contains("compile_fail") && s.contains('E') {
if !found_error_code {
error_codes.insert(err_code.clone(), true);
error_codes.get_mut(&err_code).map(|x| x.has_test = true);
found_error_code = true;
}
} else if s.contains("compile-fail") {
@ -38,7 +51,7 @@ fn check_error_code_explanation(
}
} else if s.starts_with("#### Note: this error code is no longer emitted by the compiler") {
if !found_error_code {
error_codes.get_mut(&err_code).map(|x| *x = true);
error_codes.get_mut(&err_code).map(|x| x.has_test = true);
found_error_code = true;
}
}
@ -77,7 +90,7 @@ macro_rules! some_or_continue {
fn extract_error_codes(
f: &str,
error_codes: &mut HashMap<String, bool>,
error_codes: &mut HashMap<String, ErrorCodeStatus>,
path: &Path,
errors: &mut Vec<String>,
) {
@ -90,15 +103,16 @@ fn extract_error_codes(
.split_once(':')
.expect(
format!(
"Expected a line with the format `E0xxx: include_str!(\"..\")`, but got {} without a `:` delimiter",
"Expected a line with the format `E0xxx: include_str!(\"..\")`, but got {} \
without a `:` delimiter",
s,
).as_str()
)
.as_str(),
)
.0
.to_owned();
if !error_codes.contains_key(&err_code) {
error_codes.insert(err_code.clone(), false);
}
error_codes.entry(err_code.clone()).or_default().has_explanation = true;
// Now we extract the tests from the markdown file!
let md_file_name = match s.split_once("include_str!(\"") {
None => continue,
@ -145,7 +159,7 @@ fn extract_error_codes(
.to_string();
if !error_codes.contains_key(&err_code) {
// this check should *never* fail!
error_codes.insert(err_code, false);
error_codes.insert(err_code, ErrorCodeStatus::default());
}
} else if s == ";" {
reached_no_explanation = true;
@ -153,7 +167,7 @@ fn extract_error_codes(
}
}
fn extract_error_codes_from_tests(f: &str, error_codes: &mut HashMap<String, bool>) {
fn extract_error_codes_from_tests(f: &str, error_codes: &mut HashMap<String, ErrorCodeStatus>) {
for line in f.lines() {
let s = line.trim();
if s.starts_with("error[E") || s.starts_with("warning[E") {
@ -164,8 +178,24 @@ fn extract_error_codes_from_tests(f: &str, error_codes: &mut HashMap<String, boo
Some((_, err_code)) => err_code,
},
};
let nb = error_codes.entry(err_code.to_owned()).or_insert(false);
*nb = true;
error_codes.entry(err_code.to_owned()).or_default().has_test = true;
}
}
}
fn extract_error_codes_from_source(
f: &str,
error_codes: &mut HashMap<String, ErrorCodeStatus>,
regex: &Regex,
) {
for line in f.lines() {
if line.trim_start().starts_with("//") {
continue;
}
for cap in regex.captures_iter(line) {
if let Some(error_code) = cap.get(1) {
error_codes.entry(error_code.as_str().to_owned()).or_default().is_used = true;
}
}
}
}
@ -174,8 +204,17 @@ pub fn check(paths: &[&Path], bad: &mut bool) {
let mut errors = Vec::new();
let mut found_explanations = 0;
let mut found_tests = 0;
let mut error_codes: HashMap<String, ErrorCodeStatus> = HashMap::new();
// We want error codes which match the following cases:
//
// * foo(a, E0111, a)
// * foo(a, E0111)
// * foo(E0111, a)
// * #[error = "E0111"]
let regex = Regex::new(r#"[(,"\s](E\d{4})[,)"]"#).unwrap();
println!("Checking which error codes lack tests...");
let mut error_codes: HashMap<String, bool> = HashMap::new();
for path in paths {
super::walk(path, &mut |path| super::filter_dirs(path), &mut |entry, contents| {
let file_name = entry.file_name();
@ -185,6 +224,11 @@ pub fn check(paths: &[&Path], bad: &mut bool) {
} else if entry.path().extension() == Some(OsStr::new("stderr")) {
extract_error_codes_from_tests(contents, &mut error_codes);
found_tests += 1;
} else if entry.path().extension() == Some(OsStr::new("rs")) {
let path = entry.path().to_string_lossy();
if PATHS_TO_IGNORE_FOR_EXTRACTION.iter().all(|c| !path.contains(c)) {
extract_error_codes_from_source(contents, &mut error_codes, &regex);
}
}
});
}
@ -199,15 +243,43 @@ pub fn check(paths: &[&Path], bad: &mut bool) {
if errors.is_empty() {
println!("Found {} error codes", error_codes.len());
for (err_code, nb) in &error_codes {
if !*nb && !EXEMPTED_FROM_TEST.contains(&err_code.as_str()) {
for (err_code, error_status) in &error_codes {
if !error_status.has_test && !EXEMPTED_FROM_TEST.contains(&err_code.as_str()) {
errors.push(format!("Error code {} needs to have at least one UI test!", err_code));
} else if *nb && EXEMPTED_FROM_TEST.contains(&err_code.as_str()) {
} else if error_status.has_test && EXEMPTED_FROM_TEST.contains(&err_code.as_str()) {
errors.push(format!(
"Error code {} has a UI test, it shouldn't be listed into EXEMPTED_FROM_TEST!",
err_code
));
}
if !error_status.is_used && !error_status.has_explanation {
errors.push(format!(
"Error code {} isn't used and doesn't have an error explanation, it should be \
commented in error_codes.rs file",
err_code
));
}
}
}
if errors.is_empty() {
// Checking if local constants need to be cleaned.
for err_code in EXEMPTED_FROM_TEST {
match error_codes.get(err_code.to_owned()) {
Some(status) => {
if status.has_test {
errors.push(format!(
"{} error code has a test and therefore should be \
removed from the `EXEMPTED_FROM_TEST` constant",
err_code
));
}
}
None => errors.push(format!(
"{} error code isn't used anymore and therefore should be removed \
from `EXEMPTED_FROM_TEST` constant",
err_code
)),
}
}
}
errors.sort();