diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 307e22b0df1f..b40066d05d35 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -4,3 +4,5 @@ a06baa56b95674fc626b3c3fd680d6a65357fe60 95e00bfed801e264e9c4ac817004153ca0f19eb6 # reformat with new rustfmt 971c549ca334b7b7406e61e958efcca9c4152822 +# refactor infcx building +283abbf0e7d20176f76006825b5c52e9a4234e4c diff --git a/.mailmap b/.mailmap index 564a68955d30..f887d29096e0 100644 --- a/.mailmap +++ b/.mailmap @@ -217,7 +217,7 @@ Hsiang-Cheng Yang Ian Jackson Ian Jackson Ian Jackson -Ibraheem Ahmed +Ibraheem Ahmed Ilyong Cho inquisitivecrystal <22333129+inquisitivecrystal@users.noreply.github.com> Irina Popa diff --git a/.reuse/dep5 b/.reuse/dep5 index e040f73b9e1f..5135f92a9d82 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -6,11 +6,6 @@ Files: * Copyright: The Rust Project Developers (see https://thanks.rust-lang.org) License: MIT or Apache-2.0 -Files: library/std/src/sync/mpsc/mpsc_queue.rs - library/std/src/sync/mpsc/spsc_queue.rs -Copyright: 2010-2011 Dmitry Vyukov -License: BSD-2-Clause - Files: src/librustdoc/html/static/fonts/FiraSans* Copyright: 2014, Mozilla Foundation, 2014, Telefonica S.A. License: OFL-1.1 diff --git a/Cargo.lock b/Cargo.lock index ddacf9cf0246..9f64aa44314d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -273,7 +273,7 @@ dependencies = [ [[package]] name = "cargo" -version = "0.67.0" +version = "0.68.0" dependencies = [ "anyhow", "atty", @@ -473,9 +473,9 @@ version = "0.1.0" [[package]] name = "cc" -version = "1.0.73" +version = "1.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +checksum = "76a284da2e6fe2092f2353e51713435363112dfd60030e22add80be333fb928f" dependencies = [ "jobserver", ] @@ -502,9 +502,9 @@ dependencies = [ [[package]] name = "chalk-derive" -version = "0.80.0" +version = "0.87.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0001adf0cf12361e08b65e1898ea138f8f77d8f5177cbf29b6b3b3532252bd6" +checksum = "d552b2fa341f5fc35c6b917b1d289d3c3a34d0b74e579390ea6192d6152a8cdb" dependencies = [ "proc-macro2", "quote", @@ -514,9 +514,9 @@ dependencies = [ [[package]] name = "chalk-engine" -version = "0.80.0" +version = "0.87.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c44ee96f2d67cb5193d1503f185db1abad9933a1c6e6b4169c176f90baecd393" +checksum = "7e54ac43048cb31c470d7b3e3acd409090ef4a5abddfe02455187aebc3d6879f" dependencies = [ "chalk-derive", "chalk-ir", @@ -527,9 +527,9 @@ dependencies = [ [[package]] name = "chalk-ir" -version = "0.80.0" +version = "0.87.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d8a95548f23618fda86426e4304e563ec2bb7ba0216139f0748d63c107b5f1" +checksum = "43aa55deff4e7fbdb09fa014543372f2c95a06835ac487b9ce57b5099b950838" dependencies = [ "bitflags", "chalk-derive", @@ -538,9 +538,9 @@ dependencies = [ [[package]] name = "chalk-solve" -version = "0.80.0" +version = "0.87.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f37f492dacfafe2e21319b80827da2779932909bb392f0cc86b2bd5c07c1b4e1" +checksum = "61213deefc36ba265ad01c4d997e18bcddf7922862a4594a47ca4575afb3dab4" dependencies = [ "chalk-derive", "chalk-ir", diff --git a/LICENSES/BSD-2-Clause.txt b/LICENSES/BSD-2-Clause.txt deleted file mode 100644 index 5f662b354cd4..000000000000 --- a/LICENSES/BSD-2-Clause.txt +++ /dev/null @@ -1,9 +0,0 @@ -Copyright (c) - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 4ef43735a62c..c999b06b0ab2 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1208,7 +1208,7 @@ impl Expr { ExprKind::Tup(_) => ExprPrecedence::Tup, ExprKind::Binary(op, ..) => ExprPrecedence::Binary(op.node), ExprKind::Unary(..) => ExprPrecedence::Unary, - ExprKind::Lit(_) => ExprPrecedence::Lit, + ExprKind::Lit(_) | ExprKind::IncludedBytes(..) => ExprPrecedence::Lit, ExprKind::Type(..) | ExprKind::Cast(..) => ExprPrecedence::Cast, ExprKind::Let(..) => ExprPrecedence::Let, ExprKind::If(..) => ExprPrecedence::If, @@ -1446,6 +1446,12 @@ pub enum ExprKind { /// with an optional value to be returned. Yeet(Option>), + /// Bytes included via `include_bytes!` + /// Added for optimization purposes to avoid the need to escape + /// large binary blobs - should always behave like [`ExprKind::Lit`] + /// with a `ByteStr` literal. + IncludedBytes(Lrc<[u8]>), + /// Placeholder for an expression that wasn't syntactically well formed in some way. Err, } diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs index eeb7e56e2b12..9c1dfeb1a614 100644 --- a/compiler/rustc_ast/src/lib.rs +++ b/compiler/rustc_ast/src/lib.rs @@ -29,6 +29,7 @@ extern crate rustc_macros; extern crate tracing; pub mod util { + pub mod case; pub mod classify; pub mod comments; pub mod literal; diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index b970e57e0173..3ab8267263d1 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1428,7 +1428,7 @@ pub fn noop_visit_expr( } ExprKind::Try(expr) => vis.visit_expr(expr), ExprKind::TryBlock(body) => vis.visit_block(body), - ExprKind::Lit(_) | ExprKind::Err => {} + ExprKind::Lit(_) | ExprKind::IncludedBytes(..) | ExprKind::Err => {} } vis.visit_id(id); vis.visit_span(span); diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 83b10d906e29..f6aac0b55f1a 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -5,6 +5,7 @@ pub use TokenKind::*; use crate::ast; use crate::ptr::P; +use crate::util::case::Case; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::Lrc; @@ -615,6 +616,15 @@ impl Token { self.is_non_raw_ident_where(|id| id.name == kw) } + /// Returns `true` if the token is a given keyword, `kw` or if `case` is `Insensitive` and this token is an identifier equal to `kw` ignoring the case. + pub fn is_keyword_case(&self, kw: Symbol, case: Case) -> bool { + self.is_keyword(kw) + || (case == Case::Insensitive + && self.is_non_raw_ident_where(|id| { + id.name.as_str().to_lowercase() == kw.as_str().to_lowercase() + })) + } + pub fn is_path_segment_keyword(&self) -> bool { self.is_non_raw_ident_where(Ident::is_path_segment_keyword) } diff --git a/compiler/rustc_ast/src/util/case.rs b/compiler/rustc_ast/src/util/case.rs new file mode 100644 index 000000000000..1afd7dea7408 --- /dev/null +++ b/compiler/rustc_ast/src/util/case.rs @@ -0,0 +1,6 @@ +/// Whatever to ignore case (`fn` vs `Fn` vs `FN`) or not. Used for recovering. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum Case { + Sensitive, + Insensitive, +} diff --git a/compiler/rustc_ast/src/util/literal.rs b/compiler/rustc_ast/src/util/literal.rs index 8f342175f7d3..e267f8cd1002 100644 --- a/compiler/rustc_ast/src/util/literal.rs +++ b/compiler/rustc_ast/src/util/literal.rs @@ -2,6 +2,7 @@ use crate::ast::{self, Lit, LitKind}; use crate::token::{self, Token}; +use rustc_data_structures::sync::Lrc; use rustc_lexer::unescape::{byte_from_char, unescape_byte, unescape_char, unescape_literal, Mode}; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::Span; @@ -231,6 +232,13 @@ impl Lit { Lit { token_lit: kind.to_token_lit(), kind, span } } + /// Recovers an AST literal from a string of bytes produced by `include_bytes!`. + /// This requires ASCII-escaping the string, which can result in poor performance + /// for very large strings of bytes. + pub fn from_included_bytes(bytes: &Lrc<[u8]>, span: Span) -> Lit { + Self::from_lit_kind(LitKind::ByteStr(bytes.clone()), span) + } + /// Losslessly convert an AST literal into a token. pub fn to_token(&self) -> Token { let kind = match self.token_lit.kind { diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 6f56c1ef0e8d..da0545ce80c3 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -251,7 +251,7 @@ pub trait Visitor<'ast>: Sized { macro_rules! walk_list { ($visitor: expr, $method: ident, $list: expr $(, $($extra_args: expr),* )?) => { { - #[cfg_attr(not(bootstrap), allow(for_loops_over_fallibles))] + #[allow(for_loops_over_fallibles)] for elem in $list { $visitor.$method(elem $(, $($extra_args,)* )?) } @@ -901,7 +901,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { } ExprKind::Try(ref subexpression) => visitor.visit_expr(subexpression), ExprKind::TryBlock(ref body) => visitor.visit_block(body), - ExprKind::Lit(_) | ExprKind::Err => {} + ExprKind::Lit(_) | ExprKind::IncludedBytes(..) | ExprKind::Err => {} } visitor.visit_expr_post(expression) diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index ec9c39350205..a4ae493af86b 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -87,6 +87,10 @@ impl<'hir> LoweringContext<'_, 'hir> { ExprKind::Lit(ref l) => { hir::ExprKind::Lit(respan(self.lower_span(l.span), l.kind.clone())) } + ExprKind::IncludedBytes(ref bytes) => hir::ExprKind::Lit(respan( + self.lower_span(e.span), + LitKind::ByteStr(bytes.clone()), + )), ExprKind::Cast(ref expr, ref ty) => { let expr = self.lower_expr(expr); let ty = diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index 1af1633b5244..7fdfc79164b4 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -323,7 +323,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // ``` fn lower_expr_within_pat(&mut self, expr: &Expr, allow_paths: bool) -> &'hir hir::Expr<'hir> { match expr.kind { - ExprKind::Lit(..) | ExprKind::ConstBlock(..) | ExprKind::Err => {} + ExprKind::Lit(..) + | ExprKind::ConstBlock(..) + | ExprKind::IncludedBytes(..) + | ExprKind::Err => {} ExprKind::Path(..) if allow_paths => {} ExprKind::Unary(UnOp::Neg, ref inner) if matches!(inner.kind, ExprKind::Lit(_)) => {} _ => { diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index bcefa8ce0b9c..930276242c3c 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -322,6 +322,10 @@ impl<'a> State<'a> { ast::ExprKind::Lit(ref lit) => { self.print_literal(lit); } + ast::ExprKind::IncludedBytes(ref bytes) => { + let lit = ast::Lit::from_included_bytes(bytes, expr.span); + self.print_literal(&lit) + } ast::ExprKind::Cast(ref expr, ref ty) => { let prec = AssocOp::As.precedence() as i8; self.print_expr_maybe_paren(expr, prec); diff --git a/compiler/rustc_borrowck/src/borrow_set.rs b/compiler/rustc_borrowck/src/borrow_set.rs index 41279588e633..563ff056ae46 100644 --- a/compiler/rustc_borrowck/src/borrow_set.rs +++ b/compiler/rustc_borrowck/src/borrow_set.rs @@ -1,3 +1,5 @@ +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] use crate::nll::ToRegionVid; use crate::path_utils::allow_two_phase_borrow; use crate::place_ext::PlaceExt; diff --git a/compiler/rustc_borrowck/src/borrowck_errors.rs b/compiler/rustc_borrowck/src/borrowck_errors.rs index 08ea00d71ef9..01be379120dc 100644 --- a/compiler/rustc_borrowck/src/borrowck_errors.rs +++ b/compiler/rustc_borrowck/src/borrowck_errors.rs @@ -8,9 +8,18 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { pub(crate) fn cannot_move_when_borrowed( &self, span: Span, - desc: &str, + borrow_span: Span, + place: &str, + borrow_place: &str, + value_place: &str, ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { - struct_span_err!(self, span, E0505, "cannot move out of {} because it is borrowed", desc,) + self.infcx.tcx.sess.create_err(crate::session_diagnostics::MoveBorrow { + place, + span, + borrow_place, + value_place, + borrow_span, + }) } pub(crate) fn cannot_use_when_mutably_borrowed( diff --git a/compiler/rustc_borrowck/src/constraint_generation.rs b/compiler/rustc_borrowck/src/constraint_generation.rs index f185e402fc6d..11b31c3f1402 100644 --- a/compiler/rustc_borrowck/src/constraint_generation.rs +++ b/compiler/rustc_borrowck/src/constraint_generation.rs @@ -1,3 +1,5 @@ +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] use rustc_infer::infer::InferCtxt; use rustc_middle::mir::visit::TyContext; use rustc_middle::mir::visit::Visitor; diff --git a/compiler/rustc_borrowck/src/constraints/mod.rs b/compiler/rustc_borrowck/src/constraints/mod.rs index 9d9c4abb0aa5..84a93e5f72e9 100644 --- a/compiler/rustc_borrowck/src/constraints/mod.rs +++ b/compiler/rustc_borrowck/src/constraints/mod.rs @@ -1,3 +1,6 @@ +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] + use rustc_data_structures::graph::scc::Sccs; use rustc_index::vec::IndexVec; use rustc_middle::mir::ConstraintCategory; diff --git a/compiler/rustc_borrowck/src/consumers.rs b/compiler/rustc_borrowck/src/consumers.rs index b162095f8a6c..86da767f3227 100644 --- a/compiler/rustc_borrowck/src/consumers.rs +++ b/compiler/rustc_borrowck/src/consumers.rs @@ -1,3 +1,5 @@ +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] //! This file provides API for compiler consumers. use rustc_hir::def_id::LocalDefId; diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index 9f7a4d49989a..8070c0e6710e 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -1,3 +1,5 @@ +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] use rustc_data_structures::fx::FxHashMap; use rustc_index::bit_set::BitSet; use rustc_middle::mir::{self, BasicBlock, Body, Location, Place}; diff --git a/compiler/rustc_borrowck/src/def_use.rs b/compiler/rustc_borrowck/src/def_use.rs index a5c0d77429de..8e62a0198be4 100644 --- a/compiler/rustc_borrowck/src/def_use.rs +++ b/compiler/rustc_borrowck/src/def_use.rs @@ -1,3 +1,5 @@ +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] use rustc_middle::mir::visit::{ MutatingUseContext, NonMutatingUseContext, NonUseContext, PlaceContext, }; diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs index 897a161f7856..b99bfda1a51f 100644 --- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs @@ -1,3 +1,6 @@ +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] + use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed}; use rustc_infer::infer::canonical::Canonical; use rustc_infer::infer::error_reporting::nice_region_error::NiceRegionError; diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 86cae5d09b5a..9e0aa57b2553 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -224,10 +224,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } - use_spans.var_span_label_path_only( - &mut err, - format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()), - ); + use_spans.var_path_only_subdiag(&mut err, desired_action); if !is_loop_move { err.span_label( @@ -404,10 +401,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let used = desired_action.as_general_verb_in_past_tense(); let mut err = struct_span_err!(self, span, E0381, "{used} binding {desc}{isnt_initialized}"); - use_spans.var_span_label_path_only( - &mut err, - format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()), - ); + use_spans.var_path_only_subdiag(&mut err, desired_action); if let InitializationRequiringAction::PartialAssignment | InitializationRequiringAction::Assignment = desired_action @@ -673,16 +667,16 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let move_spans = self.move_spans(place.as_ref(), location); let span = move_spans.args_or_use(); - let mut err = - self.cannot_move_when_borrowed(span, &self.describe_any_place(place.as_ref())); - err.span_label(borrow_span, format!("borrow of {} occurs here", borrow_msg)); - err.span_label(span, format!("move out of {} occurs here", value_msg)); - - borrow_spans.var_span_label_path_only( - &mut err, - format!("borrow occurs due to use{}", borrow_spans.describe()), + let mut err = self.cannot_move_when_borrowed( + span, + borrow_span, + &self.describe_any_place(place.as_ref()), + &borrow_msg, + &value_msg, ); + borrow_spans.var_path_only_subdiag(&mut err, crate::InitializationRequiringAction::Borrow); + move_spans.var_span_label( &mut err, format!("move occurs due to use{}", move_spans.describe()), @@ -724,22 +718,15 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { borrow_span, &self.describe_any_place(borrow.borrowed_place.as_ref()), ); - borrow_spans.var_subdiag( - &mut err, - |var_span| { - use crate::session_diagnostics::CaptureVarCause::*; - let place = &borrow.borrowed_place; - let desc_place = self.describe_any_place(place.as_ref()); - match borrow_spans { - UseSpans::ClosureUse { generator_kind, .. } => match generator_kind { - Some(_) => BorrowUsePlaceGenerator { place: desc_place, var_span }, - None => BorrowUsePlaceClosure { place: desc_place, var_span }, - }, - _ => BorrowUsePlace { place: desc_place, var_span }, - } - }, - "mutable", - ); + borrow_spans.var_subdiag(&mut err, Some(borrow.kind), |kind, var_span| { + use crate::session_diagnostics::CaptureVarCause::*; + let place = &borrow.borrowed_place; + let desc_place = self.describe_any_place(place.as_ref()); + match kind { + Some(_) => BorrowUsePlaceGenerator { place: desc_place, var_span }, + None => BorrowUsePlaceClosure { place: desc_place, var_span }, + } + }); self.explain_why_borrow_contains_point(location, borrow, None) .add_explanation_to_diagnostic( diff --git a/compiler/rustc_borrowck/src/diagnostics/find_all_local_uses.rs b/compiler/rustc_borrowck/src/diagnostics/find_all_local_uses.rs index b3edc35dc364..498e9834354b 100644 --- a/compiler/rustc_borrowck/src/diagnostics/find_all_local_uses.rs +++ b/compiler/rustc_borrowck/src/diagnostics/find_all_local_uses.rs @@ -1,3 +1,6 @@ +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] + use std::collections::BTreeSet; use rustc_middle::mir::visit::{PlaceContext, Visitor}; diff --git a/compiler/rustc_borrowck/src/diagnostics/find_use.rs b/compiler/rustc_borrowck/src/diagnostics/find_use.rs index b5a3081e56a7..15f42e26cbf4 100644 --- a/compiler/rustc_borrowck/src/diagnostics/find_use.rs +++ b/compiler/rustc_borrowck/src/diagnostics/find_use.rs @@ -1,3 +1,6 @@ +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] + use std::collections::VecDeque; use std::rc::Rc; diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 61518378e3d0..7f26af67c71b 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -595,11 +595,34 @@ impl UseSpans<'_> { } } - // Add a span label to the use of the captured variable, if it exists. - // only adds label to the `path_span` - pub(super) fn var_span_label_path_only(self, err: &mut Diagnostic, message: impl Into) { - if let UseSpans::ClosureUse { path_span, .. } = self { - err.span_label(path_span, message); + /// Add a span label to the use of the captured variable, if it exists. + /// only adds label to the `path_span` + pub(super) fn var_path_only_subdiag( + self, + err: &mut Diagnostic, + action: crate::InitializationRequiringAction, + ) { + use crate::session_diagnostics::CaptureVarPathUseCause::*; + use crate::InitializationRequiringAction::*; + if let UseSpans::ClosureUse { generator_kind, path_span, .. } = self { + match generator_kind { + Some(_) => { + err.subdiagnostic(match action { + Borrow => BorrowInGenerator { path_span }, + MatchOn | Use => UseInGenerator { path_span }, + Assignment => AssignInGenerator { path_span }, + PartialAssignment => AssignPartInGenerator { path_span }, + }); + } + None => { + err.subdiagnostic(match action { + Borrow => BorrowInClosure { path_span }, + MatchOn | Use => UseInClosure { path_span }, + Assignment => AssignInClosure { path_span }, + PartialAssignment => AssignPartInClosure { path_span }, + }); + } + } } } @@ -627,19 +650,28 @@ impl UseSpans<'_> { pub(super) fn var_subdiag( self, err: &mut Diagnostic, - f: impl Fn(Span) -> crate::session_diagnostics::CaptureVarCause, - kind_desc: impl Into, + kind: Option, + f: impl Fn(Option, Span) -> crate::session_diagnostics::CaptureVarCause, ) { - if let UseSpans::ClosureUse { capture_kind_span, path_span, .. } = self { - if capture_kind_span == path_span { - err.subdiagnostic(f(capture_kind_span)); - } else { - err.subdiagnostic(crate::session_diagnostics::CaptureVarKind { - kind_desc: kind_desc.into(), - kind_span: capture_kind_span, + use crate::session_diagnostics::CaptureVarKind::*; + if let UseSpans::ClosureUse { generator_kind, capture_kind_span, path_span, .. } = self { + if capture_kind_span != path_span { + err.subdiagnostic(match kind { + Some(kd) => match kd { + rustc_middle::mir::BorrowKind::Shared + | rustc_middle::mir::BorrowKind::Shallow + | rustc_middle::mir::BorrowKind::Unique => { + Immute { kind_span: capture_kind_span } + } + + rustc_middle::mir::BorrowKind::Mut { .. } => { + Mut { kind_span: capture_kind_span } + } + }, + None => Move { kind_span: capture_kind_span }, }); - err.subdiagnostic(f(path_span)); - } + }; + err.subdiagnostic(f(generator_kind, path_span)); } } diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 8ad40c0aa0a5..7457369aa58c 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -1,6 +1,4 @@ -use rustc_errors::{ - Applicability, Diagnostic, DiagnosticBuilder, EmissionGuarantee, ErrorGuaranteed, -}; +use rustc_errors::{Applicability, Diagnostic}; use rustc_hir as hir; use rustc_hir::intravisit::Visitor; use rustc_hir::Node; @@ -629,25 +627,20 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { self.buffer_error(err); } - fn suggest_map_index_mut_alternatives( - &self, - ty: Ty<'_>, - err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, - span: Span, - ) { + fn suggest_map_index_mut_alternatives(&self, ty: Ty<'tcx>, err: &mut Diagnostic, span: Span) { let Some(adt) = ty.ty_adt_def() else { return }; let did = adt.did(); if self.infcx.tcx.is_diagnostic_item(sym::HashMap, did) || self.infcx.tcx.is_diagnostic_item(sym::BTreeMap, did) { - struct V<'a, 'b, 'tcx, G: EmissionGuarantee> { + struct V<'a, 'tcx> { assign_span: Span, - err: &'a mut DiagnosticBuilder<'b, G>, + err: &'a mut Diagnostic, ty: Ty<'tcx>, suggested: bool, } - impl<'a, 'b: 'a, 'hir, 'tcx, G: EmissionGuarantee> Visitor<'hir> for V<'a, 'b, 'tcx, G> { - fn visit_stmt(&mut self, stmt: &'hir hir::Stmt<'hir>) { + impl<'a, 'tcx> Visitor<'tcx> for V<'a, 'tcx> { + fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) { hir::intravisit::walk_stmt(self, stmt); let expr = match stmt.kind { hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr) => expr, @@ -705,7 +698,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { ), (rv.span.shrink_to_hi(), ")".to_string()), ], - ].into_iter(), + ], Applicability::MachineApplicable, ); self.suggested = true; diff --git a/compiler/rustc_borrowck/src/diagnostics/var_name.rs b/compiler/rustc_borrowck/src/diagnostics/var_name.rs index 9ba29f04b1a9..b385f95b67c6 100644 --- a/compiler/rustc_borrowck/src/diagnostics/var_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/var_name.rs @@ -1,3 +1,6 @@ +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] + use crate::Upvar; use crate::{nll::ToRegionVid, region_infer::RegionInferenceContext}; use rustc_index::vec::{Idx, IndexVec}; diff --git a/compiler/rustc_borrowck/src/facts.rs b/compiler/rustc_borrowck/src/facts.rs index 22134d5a71ce..51ed27c167d3 100644 --- a/compiler/rustc_borrowck/src/facts.rs +++ b/compiler/rustc_borrowck/src/facts.rs @@ -1,3 +1,5 @@ +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] use crate::location::{LocationIndex, LocationTable}; use crate::BorrowIndex; use polonius_engine::AllFacts as PoloniusFacts; diff --git a/compiler/rustc_borrowck/src/invalidation.rs b/compiler/rustc_borrowck/src/invalidation.rs index 3157f861d93b..f5317a143aed 100644 --- a/compiler/rustc_borrowck/src/invalidation.rs +++ b/compiler/rustc_borrowck/src/invalidation.rs @@ -1,3 +1,5 @@ +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] use rustc_data_structures::graph::dominators::Dominators; use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::{self, BasicBlock, Body, Location, NonDivergingIntrinsic, Place, Rvalue}; diff --git a/compiler/rustc_borrowck/src/location.rs b/compiler/rustc_borrowck/src/location.rs index 877944d3d70c..9fa7e218b1b6 100644 --- a/compiler/rustc_borrowck/src/location.rs +++ b/compiler/rustc_borrowck/src/location.rs @@ -1,3 +1,5 @@ +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] use rustc_index::vec::{Idx, IndexVec}; use rustc_middle::mir::{BasicBlock, Body, Location}; diff --git a/compiler/rustc_borrowck/src/member_constraints.rs b/compiler/rustc_borrowck/src/member_constraints.rs index 43253a2aab00..b48f9f97daad 100644 --- a/compiler/rustc_borrowck/src/member_constraints.rs +++ b/compiler/rustc_borrowck/src/member_constraints.rs @@ -1,3 +1,5 @@ +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashMap; use rustc_index::vec::IndexVec; diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index 4e0205f8d43a..f8856b56d140 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -1,3 +1,5 @@ +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] //! The entry point of the NLL borrow checker. use rustc_data_structures::vec_map::VecMap; diff --git a/compiler/rustc_borrowck/src/path_utils.rs b/compiler/rustc_borrowck/src/path_utils.rs index b2c8dfc82c20..f8a99a2699e6 100644 --- a/compiler/rustc_borrowck/src/path_utils.rs +++ b/compiler/rustc_borrowck/src/path_utils.rs @@ -1,3 +1,5 @@ +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] use crate::borrow_set::{BorrowData, BorrowSet, TwoPhaseActivation}; use crate::places_conflict; use crate::AccessDepth; diff --git a/compiler/rustc_borrowck/src/place_ext.rs b/compiler/rustc_borrowck/src/place_ext.rs index 93d202e49a15..9f6b1fdfcb54 100644 --- a/compiler/rustc_borrowck/src/place_ext.rs +++ b/compiler/rustc_borrowck/src/place_ext.rs @@ -1,3 +1,5 @@ +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] use crate::borrow_set::LocalsStateAtExit; use rustc_hir as hir; use rustc_middle::mir::ProjectionElem; diff --git a/compiler/rustc_borrowck/src/places_conflict.rs b/compiler/rustc_borrowck/src/places_conflict.rs index 0e71efd6f8d3..8a87d1972ebf 100644 --- a/compiler/rustc_borrowck/src/places_conflict.rs +++ b/compiler/rustc_borrowck/src/places_conflict.rs @@ -1,3 +1,5 @@ +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] use crate::ArtificialField; use crate::Overlap; use crate::{AccessDepth, Deep, Shallow}; diff --git a/compiler/rustc_borrowck/src/prefixes.rs b/compiler/rustc_borrowck/src/prefixes.rs index 2b50cbac9a02..6f2813498637 100644 --- a/compiler/rustc_borrowck/src/prefixes.rs +++ b/compiler/rustc_borrowck/src/prefixes.rs @@ -1,3 +1,5 @@ +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] //! From the NLL RFC: "The deep [aka 'supporting'] prefixes for an //! place are formed by stripping away fields and derefs, except that //! we stop when we reach the deref of a shared reference. [...] " diff --git a/compiler/rustc_borrowck/src/region_infer/dump_mir.rs b/compiler/rustc_borrowck/src/region_infer/dump_mir.rs index cc9450999525..6524b594e44d 100644 --- a/compiler/rustc_borrowck/src/region_infer/dump_mir.rs +++ b/compiler/rustc_borrowck/src/region_infer/dump_mir.rs @@ -1,3 +1,5 @@ +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] //! As part of generating the regions, if you enable `-Zdump-mir=nll`, //! we will generate an annotated copy of the MIR that includes the //! state of region inference. This code handles emitting the region diff --git a/compiler/rustc_borrowck/src/region_infer/graphviz.rs b/compiler/rustc_borrowck/src/region_infer/graphviz.rs index f31ccd74ca6f..2e15586e03b3 100644 --- a/compiler/rustc_borrowck/src/region_infer/graphviz.rs +++ b/compiler/rustc_borrowck/src/region_infer/graphviz.rs @@ -1,3 +1,5 @@ +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] //! This module provides linkage between RegionInferenceContext and //! `rustc_graphviz` traits, specialized to attaching borrowck analysis //! data to rendered labels. diff --git a/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs b/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs index 1e6798eee3df..167f66460969 100644 --- a/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs +++ b/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs @@ -1,3 +1,5 @@ +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] use crate::constraints::ConstraintSccIndex; use crate::RegionInferenceContext; use itertools::Itertools; diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs index de20a4bb465c..7498ddccf196 100644 --- a/compiler/rustc_borrowck/src/region_infer/values.rs +++ b/compiler/rustc_borrowck/src/region_infer/values.rs @@ -1,3 +1,5 @@ +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] use rustc_data_structures::fx::FxIndexSet; use rustc_index::bit_set::SparseBitMatrix; use rustc_index::interval::IntervalSet; diff --git a/compiler/rustc_borrowck/src/renumber.rs b/compiler/rustc_borrowck/src/renumber.rs index f3023769081f..084754830bdb 100644 --- a/compiler/rustc_borrowck/src/renumber.rs +++ b/compiler/rustc_borrowck/src/renumber.rs @@ -1,3 +1,5 @@ +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] use rustc_index::vec::IndexVec; use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin}; use rustc_middle::mir::visit::{MutVisitor, TyContext}; diff --git a/compiler/rustc_borrowck/src/session_diagnostics.rs b/compiler/rustc_borrowck/src/session_diagnostics.rs index 824f20a31bb0..577332c0744b 100644 --- a/compiler/rustc_borrowck/src/session_diagnostics.rs +++ b/compiler/rustc_borrowck/src/session_diagnostics.rs @@ -150,21 +150,70 @@ pub(crate) enum RequireStaticErr { } #[derive(Subdiagnostic)] -#[label(borrowck_capture_kind_label)] -pub(crate) struct CaptureVarKind { - pub kind_desc: String, - #[primary_span] - pub kind_span: Span, +pub(crate) enum CaptureVarPathUseCause { + #[label(borrowck_borrow_due_to_use_generator)] + BorrowInGenerator { + #[primary_span] + path_span: Span, + }, + #[label(borrowck_use_due_to_use_generator)] + UseInGenerator { + #[primary_span] + path_span: Span, + }, + #[label(borrowck_assign_due_to_use_generator)] + AssignInGenerator { + #[primary_span] + path_span: Span, + }, + #[label(borrowck_assign_part_due_to_use_generator)] + AssignPartInGenerator { + #[primary_span] + path_span: Span, + }, + #[label(borrowck_borrow_due_to_use_closure)] + BorrowInClosure { + #[primary_span] + path_span: Span, + }, + #[label(borrowck_use_due_to_use_closure)] + UseInClosure { + #[primary_span] + path_span: Span, + }, + #[label(borrowck_assign_due_to_use_closure)] + AssignInClosure { + #[primary_span] + path_span: Span, + }, + #[label(borrowck_assign_part_due_to_use_closure)] + AssignPartInClosure { + #[primary_span] + path_span: Span, + }, +} + +#[derive(Subdiagnostic)] +pub(crate) enum CaptureVarKind { + #[label(borrowck_capture_immute)] + Immute { + #[primary_span] + kind_span: Span, + }, + #[label(borrowck_capture_mut)] + Mut { + #[primary_span] + kind_span: Span, + }, + #[label(borrowck_capture_move)] + Move { + #[primary_span] + kind_span: Span, + }, } #[derive(Subdiagnostic)] pub(crate) enum CaptureVarCause { - #[label(borrowck_var_borrow_by_use_place)] - BorrowUsePlace { - place: String, - #[primary_span] - var_span: Span, - }, #[label(borrowck_var_borrow_by_use_place_in_generator)] BorrowUsePlaceGenerator { place: String, @@ -178,3 +227,16 @@ pub(crate) enum CaptureVarCause { var_span: Span, }, } + +#[derive(Diagnostic)] +#[diag(borrowck_cannot_move_when_borrowed, code = "E0505")] +pub(crate) struct MoveBorrow<'a> { + pub place: &'a str, + pub borrow_place: &'a str, + pub value_place: &'a str, + #[primary_span] + #[label(move_label)] + pub span: Span, + #[label] + pub borrow_span: Span, +} diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 9c1d0bb8b235..6ccc29b09c0a 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1,3 +1,5 @@ +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] //! This pass type-checks the MIR to ensure it is not broken. use std::rc::Rc; diff --git a/compiler/rustc_borrowck/src/used_muts.rs b/compiler/rustc_borrowck/src/used_muts.rs index 8833753b12c5..e297b1230ea0 100644 --- a/compiler/rustc_borrowck/src/used_muts.rs +++ b/compiler/rustc_borrowck/src/used_muts.rs @@ -1,3 +1,5 @@ +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] use rustc_data_structures::fx::FxHashSet; use rustc_middle::mir::visit::{PlaceContext, Visitor}; use rustc_middle::mir::{ diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs index bb6839360262..f72cd14bea04 100644 --- a/compiler/rustc_builtin_macros/src/assert/context.rs +++ b/compiler/rustc_builtin_macros/src/assert/context.rs @@ -303,6 +303,7 @@ impl<'cx, 'a> Context<'cx, 'a> { | ExprKind::Field(_, _) | ExprKind::ForLoop(_, _, _, _) | ExprKind::If(_, _, _) + | ExprKind::IncludedBytes(..) | ExprKind::InlineAsm(_) | ExprKind::Let(_, _, _) | ExprKind::Lit(_) diff --git a/compiler/rustc_builtin_macros/src/cfg_accessible.rs b/compiler/rustc_builtin_macros/src/cfg_accessible.rs index cb5359dd1e27..86df3c44eb33 100644 --- a/compiler/rustc_builtin_macros/src/cfg_accessible.rs +++ b/compiler/rustc_builtin_macros/src/cfg_accessible.rs @@ -34,6 +34,7 @@ impl MultiItemModifier for Expander { span: Span, meta_item: &ast::MetaItem, item: Annotatable, + _is_derive_const: bool, ) -> ExpandResult, Annotatable> { let template = AttributeTemplate { list: Some("path"), ..Default::default() }; let attr = &ecx.attribute(meta_item.clone()); diff --git a/compiler/rustc_builtin_macros/src/concat.rs b/compiler/rustc_builtin_macros/src/concat.rs index 41f4e8c234d5..01454d0e98e6 100644 --- a/compiler/rustc_builtin_macros/src/concat.rs +++ b/compiler/rustc_builtin_macros/src/concat.rs @@ -43,6 +43,9 @@ pub fn expand_concat( has_errors = true; } }, + ast::ExprKind::IncludedBytes(..) => { + cx.span_err(e.span, "cannot concatenate a byte string literal") + } ast::ExprKind::Err => { has_errors = true; } diff --git a/compiler/rustc_builtin_macros/src/concat_bytes.rs b/compiler/rustc_builtin_macros/src/concat_bytes.rs index 66e86bf21826..4886ca786a58 100644 --- a/compiler/rustc_builtin_macros/src/concat_bytes.rs +++ b/compiler/rustc_builtin_macros/src/concat_bytes.rs @@ -108,6 +108,16 @@ fn handle_array_element( None } }, + ast::ExprKind::IncludedBytes(..) => { + if !*has_errors { + cx.struct_span_err(expr.span, "cannot concatenate doubly nested array") + .note("byte strings are treated as arrays of bytes") + .help("try flattening the array") + .emit(); + } + *has_errors = true; + None + } _ => { missing_literals.push(expr.span); None @@ -167,6 +177,9 @@ pub fn expand_concat_bytes( has_errors = true; } }, + ast::ExprKind::IncludedBytes(ref bytes) => { + accumulator.extend_from_slice(bytes); + } ast::ExprKind::Err => { has_errors = true; } diff --git a/compiler/rustc_builtin_macros/src/derive.rs b/compiler/rustc_builtin_macros/src/derive.rs index e0fb7affb349..01f237e6ab5f 100644 --- a/compiler/rustc_builtin_macros/src/derive.rs +++ b/compiler/rustc_builtin_macros/src/derive.rs @@ -10,7 +10,7 @@ use rustc_session::Session; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; -pub(crate) struct Expander; +pub(crate) struct Expander(pub bool); impl MultiItemModifier for Expander { fn expand( @@ -19,6 +19,7 @@ impl MultiItemModifier for Expander { span: Span, meta_item: &ast::MetaItem, item: Annotatable, + _: bool, ) -> ExpandResult, Annotatable> { let sess = ecx.sess; if report_bad_target(sess, &item, span) { @@ -58,20 +59,20 @@ impl MultiItemModifier for Expander { report_path_args(sess, &meta); meta.path }) - .map(|path| (path, dummy_annotatable(), None)) + .map(|path| (path, dummy_annotatable(), None, self.0)) .collect(); // Do not configure or clone items unless necessary. match &mut resolutions[..] { [] => {} - [(_, first_item, _), others @ ..] => { + [(_, first_item, ..), others @ ..] => { *first_item = cfg_eval( sess, features, item.clone(), ecx.current_expansion.lint_node_id, ); - for (_, item, _) in others { + for (_, item, _, _) in others { *item = first_item.clone(); } } diff --git a/compiler/rustc_builtin_macros/src/deriving/bounds.rs b/compiler/rustc_builtin_macros/src/deriving/bounds.rs index 7bd344467d03..345db700298a 100644 --- a/compiler/rustc_builtin_macros/src/deriving/bounds.rs +++ b/compiler/rustc_builtin_macros/src/deriving/bounds.rs @@ -12,6 +12,7 @@ pub fn expand_deriving_copy( mitem: &MetaItem, item: &Annotatable, push: &mut dyn FnMut(Annotatable), + is_const: bool, ) { let trait_def = TraitDef { span, @@ -22,6 +23,7 @@ pub fn expand_deriving_copy( supports_unions: true, methods: Vec::new(), associated_types: Vec::new(), + is_const, }; trait_def.expand(cx, mitem, item, push); diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs index fa8685f5f4e5..55cbb6547232 100644 --- a/compiler/rustc_builtin_macros/src/deriving/clone.rs +++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs @@ -14,6 +14,7 @@ pub fn expand_deriving_clone( mitem: &MetaItem, item: &Annotatable, push: &mut dyn FnMut(Annotatable), + is_const: bool, ) { // The simple form is `fn clone(&self) -> Self { *self }`, possibly with // some additional `AssertParamIsClone` assertions. @@ -87,6 +88,7 @@ pub fn expand_deriving_clone( combine_substructure: substructure, }], associated_types: Vec::new(), + is_const, }; trait_def.expand_ext(cx, mitem, item, push, is_simple) diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs index eab67b0d354c..6190b7a8c779 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs @@ -15,6 +15,7 @@ pub fn expand_deriving_eq( mitem: &MetaItem, item: &Annotatable, push: &mut dyn FnMut(Annotatable), + is_const: bool, ) { let span = cx.with_def_site_ctxt(span); let inline = cx.meta_word(span, sym::inline); @@ -42,6 +43,7 @@ pub fn expand_deriving_eq( })), }], associated_types: Vec::new(), + is_const, }; super::inject_impl_of_structural_trait(cx, span, item, path_std!(marker::StructuralEq), push); diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs index 7f117981a9a2..d2412b20a09c 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs @@ -13,6 +13,7 @@ pub fn expand_deriving_ord( mitem: &MetaItem, item: &Annotatable, push: &mut dyn FnMut(Annotatable), + is_const: bool, ) { let inline = cx.meta_word(span, sym::inline); let attrs = thin_vec![cx.attribute(inline)]; @@ -34,6 +35,7 @@ pub fn expand_deriving_ord( combine_substructure: combine_substructure(Box::new(|a, b, c| cs_cmp(a, b, c))), }], associated_types: Vec::new(), + is_const, }; trait_def.expand(cx, mitem, item, push) diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs index 236cbccaf9fe..353f28fc45fb 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs @@ -14,6 +14,7 @@ pub fn expand_deriving_partial_eq( mitem: &MetaItem, item: &Annotatable, push: &mut dyn FnMut(Annotatable), + is_const: bool, ) { fn cs_eq(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr { let base = true; @@ -89,6 +90,7 @@ pub fn expand_deriving_partial_eq( supports_unions: false, methods, associated_types: Vec::new(), + is_const, }; trait_def.expand(cx, mitem, item, push) } diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs index 4173403a1b84..e7458b23ef38 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs @@ -13,6 +13,7 @@ pub fn expand_deriving_partial_ord( mitem: &MetaItem, item: &Annotatable, push: &mut dyn FnMut(Annotatable), + is_const: bool, ) { let ordering_ty = Path(path_std!(cmp::Ordering)); let ret_ty = @@ -43,6 +44,7 @@ pub fn expand_deriving_partial_ord( supports_unions: false, methods: vec![partial_cmp_def], associated_types: Vec::new(), + is_const, }; trait_def.expand(cx, mitem, item, push) } diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs index 2cf614ed9476..e6d5759bb521 100644 --- a/compiler/rustc_builtin_macros/src/deriving/debug.rs +++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs @@ -13,6 +13,7 @@ pub fn expand_deriving_debug( mitem: &MetaItem, item: &Annotatable, push: &mut dyn FnMut(Annotatable), + is_const: bool, ) { // &mut ::std::fmt::Formatter let fmtr = Ref(Box::new(Path(path_std!(fmt::Formatter))), ast::Mutability::Mut); @@ -37,6 +38,7 @@ pub fn expand_deriving_debug( })), }], associated_types: Vec::new(), + is_const, }; trait_def.expand(cx, mitem, item, push) } diff --git a/compiler/rustc_builtin_macros/src/deriving/decodable.rs b/compiler/rustc_builtin_macros/src/deriving/decodable.rs index d669f616802f..37aa665e5c60 100644 --- a/compiler/rustc_builtin_macros/src/deriving/decodable.rs +++ b/compiler/rustc_builtin_macros/src/deriving/decodable.rs @@ -16,6 +16,7 @@ pub fn expand_deriving_rustc_decodable( mitem: &MetaItem, item: &Annotatable, push: &mut dyn FnMut(Annotatable), + is_const: bool, ) { let krate = sym::rustc_serialize; let typaram = sym::__D; @@ -55,6 +56,7 @@ pub fn expand_deriving_rustc_decodable( })), }], associated_types: Vec::new(), + is_const, }; trait_def.expand(cx, mitem, item, push) diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs index 17df9fb279ad..234957ab8a16 100644 --- a/compiler/rustc_builtin_macros/src/deriving/default.rs +++ b/compiler/rustc_builtin_macros/src/deriving/default.rs @@ -16,6 +16,7 @@ pub fn expand_deriving_default( mitem: &ast::MetaItem, item: &Annotatable, push: &mut dyn FnMut(Annotatable), + is_const: bool, ) { item.visit_with(&mut DetectNonVariantDefaultAttr { cx }); @@ -47,6 +48,7 @@ pub fn expand_deriving_default( })), }], associated_types: Vec::new(), + is_const, }; trait_def.expand(cx, mitem, item, push) } diff --git a/compiler/rustc_builtin_macros/src/deriving/encodable.rs b/compiler/rustc_builtin_macros/src/deriving/encodable.rs index f83f58b97d38..baacaa8b979e 100644 --- a/compiler/rustc_builtin_macros/src/deriving/encodable.rs +++ b/compiler/rustc_builtin_macros/src/deriving/encodable.rs @@ -100,6 +100,7 @@ pub fn expand_deriving_rustc_encodable( mitem: &MetaItem, item: &Annotatable, push: &mut dyn FnMut(Annotatable), + is_const: bool, ) { let krate = sym::rustc_serialize; let typaram = sym::__S; @@ -139,6 +140,7 @@ pub fn expand_deriving_rustc_encodable( })), }], associated_types: Vec::new(), + is_const, }; trait_def.expand(cx, mitem, item, push) diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 16ee3aa89bb1..3972c3b49341 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -171,7 +171,7 @@ use rustc_ast::{GenericArg, GenericParamKind, VariantData}; use rustc_attr as attr; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::Span; +use rustc_span::{Span, DUMMY_SP}; use std::cell::RefCell; use std::iter; use std::ops::Not; @@ -204,6 +204,8 @@ pub struct TraitDef<'a> { pub methods: Vec>, pub associated_types: Vec<(Ident, Ty)>, + + pub is_const: bool, } pub struct MethodDef<'a> { @@ -730,7 +732,7 @@ impl<'a> TraitDef<'a> { unsafety: ast::Unsafe::No, polarity: ast::ImplPolarity::Positive, defaultness: ast::Defaultness::Final, - constness: ast::Const::No, + constness: if self.is_const { ast::Const::Yes(DUMMY_SP) } else { ast::Const::No }, generics: trait_generics, of_trait: opt_trait_ref, self_ty: self_type, diff --git a/compiler/rustc_builtin_macros/src/deriving/hash.rs b/compiler/rustc_builtin_macros/src/deriving/hash.rs index 6e9d5f08b944..8fb1a4ba262a 100644 --- a/compiler/rustc_builtin_macros/src/deriving/hash.rs +++ b/compiler/rustc_builtin_macros/src/deriving/hash.rs @@ -13,6 +13,7 @@ pub fn expand_deriving_hash( mitem: &MetaItem, item: &Annotatable, push: &mut dyn FnMut(Annotatable), + is_const: bool, ) { let path = Path::new_(pathvec_std!(hash::Hash), vec![], PathKind::Std); @@ -39,6 +40,7 @@ pub fn expand_deriving_hash( })), }], associated_types: Vec::new(), + is_const, }; hash_trait_def.expand(cx, mitem, item, push); diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs index ee346047a0bc..73a1df5d426d 100644 --- a/compiler/rustc_builtin_macros/src/deriving/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs @@ -38,9 +38,10 @@ pub mod partial_ord; pub mod generic; -pub(crate) struct BuiltinDerive( - pub(crate) fn(&mut ExtCtxt<'_>, Span, &MetaItem, &Annotatable, &mut dyn FnMut(Annotatable)), -); +pub(crate) type BuiltinDeriveFn = + fn(&mut ExtCtxt<'_>, Span, &MetaItem, &Annotatable, &mut dyn FnMut(Annotatable), bool); + +pub(crate) struct BuiltinDerive(pub(crate) BuiltinDeriveFn); impl MultiItemModifier for BuiltinDerive { fn expand( @@ -49,6 +50,7 @@ impl MultiItemModifier for BuiltinDerive { span: Span, meta_item: &MetaItem, item: Annotatable, + is_derive_const: bool, ) -> ExpandResult, Annotatable> { // FIXME: Built-in derives often forget to give spans contexts, // so we are doing it here in a centralized way. @@ -57,21 +59,28 @@ impl MultiItemModifier for BuiltinDerive { match item { Annotatable::Stmt(stmt) => { if let ast::StmtKind::Item(item) = stmt.into_inner().kind { - (self.0)(ecx, span, meta_item, &Annotatable::Item(item), &mut |a| { - // Cannot use 'ecx.stmt_item' here, because we need to pass 'ecx' - // to the function - items.push(Annotatable::Stmt(P(ast::Stmt { - id: ast::DUMMY_NODE_ID, - kind: ast::StmtKind::Item(a.expect_item()), - span, - }))); - }); + (self.0)( + ecx, + span, + meta_item, + &Annotatable::Item(item), + &mut |a| { + // Cannot use 'ecx.stmt_item' here, because we need to pass 'ecx' + // to the function + items.push(Annotatable::Stmt(P(ast::Stmt { + id: ast::DUMMY_NODE_ID, + kind: ast::StmtKind::Item(a.expect_item()), + span, + }))); + }, + is_derive_const, + ); } else { unreachable!("should have already errored on non-item statement") } } _ => { - (self.0)(ecx, span, meta_item, &item, &mut |a| items.push(a)); + (self.0)(ecx, span, meta_item, &item, &mut |a| items.push(a), is_derive_const); } } ExpandResult::Ready(items) diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index bde0102186a4..1cbbfb432647 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -99,7 +99,8 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { bench: test::expand_bench, cfg_accessible: cfg_accessible::Expander, cfg_eval: cfg_eval::expand, - derive: derive::Expander, + derive: derive::Expander(false), + derive_const: derive::Expander(true), global_allocator: global_allocator::expand, test: test::expand_test, test_case: test::expand_test_case, diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs index d78bbc3c9322..3411bd40c9de 100644 --- a/compiler/rustc_builtin_macros/src/source_util.rs +++ b/compiler/rustc_builtin_macros/src/source_util.rs @@ -216,7 +216,10 @@ pub fn expand_include_bytes( } }; match cx.source_map().load_binary_file(&file) { - Ok(bytes) => base::MacEager::expr(cx.expr_byte_str(sp, bytes)), + Ok(bytes) => { + let expr = cx.expr(sp, ast::ExprKind::IncludedBytes(bytes.into())); + base::MacEager::expr(expr) + } Err(e) => { cx.span_err(sp, &format!("couldn't read {}: {}", file.display(), e)); DummyResult::any(sp) diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index fee5d04cdae8..b62840d4bc82 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -112,7 +112,7 @@ pub fn expand_test_or_bench( }; // Note: non-associated fn items are already handled by `expand_test_or_bench` - if !matches!(item.kind, ast::ItemKind::Fn(_)) { + let ast::ItemKind::Fn(fn_) = &item.kind else { let diag = &cx.sess.parse_sess.span_diagnostic; let msg = "the `#[test]` attribute may only be used on a non-associated function"; let mut err = match item.kind { @@ -130,7 +130,7 @@ pub fn expand_test_or_bench( .emit(); return vec![Annotatable::Item(item)]; - } + }; // has_*_signature will report any errors in the type so compilation // will fail. We shouldn't try to expand in this case because the errors @@ -141,12 +141,14 @@ pub fn expand_test_or_bench( return vec![Annotatable::Item(item)]; } - let (sp, attr_sp) = (cx.with_def_site_ctxt(item.span), cx.with_def_site_ctxt(attr_sp)); + let sp = cx.with_def_site_ctxt(item.span); + let ret_ty_sp = cx.with_def_site_ctxt(fn_.sig.decl.output.span()); + let attr_sp = cx.with_def_site_ctxt(attr_sp); let test_id = Ident::new(sym::test, attr_sp); // creates test::$name - let test_path = |name| cx.path(sp, vec![test_id, Ident::from_str_and_span(name, sp)]); + let test_path = |name| cx.path(ret_ty_sp, vec![test_id, Ident::from_str_and_span(name, sp)]); // creates test::ShouldPanic::$name let should_panic_path = |name| { @@ -192,7 +194,7 @@ pub fn expand_test_or_bench( vec![ // super::$test_fn(b) cx.expr_call( - sp, + ret_ty_sp, cx.expr_path(cx.path(sp, vec![item.ident])), vec![cx.expr_ident(sp, b)], ), @@ -216,7 +218,11 @@ pub fn expand_test_or_bench( cx.expr_path(test_path("assert_test_result")), vec![ // $test_fn() - cx.expr_call(sp, cx.expr_path(cx.path(sp, vec![item.ident])), vec![]), // ) + cx.expr_call( + ret_ty_sp, + cx.expr_path(cx.path(sp, vec![item.ident])), + vec![], + ), // ) ], ), // } ), // ) diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index 148b66d959e8..df1150ec0b8c 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -128,7 +128,7 @@ pub(crate) fn codegen_const_value<'tcx>( ty: Ty<'tcx>, ) -> CValue<'tcx> { let layout = fx.layout_of(ty); - assert!(!layout.is_unsized(), "sized const value"); + assert!(layout.is_sized(), "unsized const value"); if layout.is_zst() { return CValue::by_ref(crate::Pointer::dangling(layout.align.pref), layout); diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index c3dfbd37279f..c5bd574623df 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -19,7 +19,7 @@ fn codegen_field<'tcx>( }; if let Some(extra) = extra { - if !field_layout.is_unsized() { + if field_layout.is_sized() { return simple(fx); } match field_layout.ty.kind() { @@ -364,7 +364,7 @@ impl<'tcx> CPlace<'tcx> { fx: &mut FunctionCx<'_, '_, 'tcx>, layout: TyAndLayout<'tcx>, ) -> CPlace<'tcx> { - assert!(!layout.is_unsized()); + assert!(layout.is_sized()); if layout.size.bytes() == 0 { return CPlace { inner: CPlaceInner::Addr(Pointer::dangling(layout.align.pref), None), @@ -825,7 +825,7 @@ impl<'tcx> CPlace<'tcx> { fx: &FunctionCx<'_, '_, 'tcx>, variant: VariantIdx, ) -> Self { - assert!(!self.layout().is_unsized()); + assert!(self.layout().is_sized()); let layout = self.layout().for_variant(fx, variant); CPlace { inner: self.inner, layout } } diff --git a/compiler/rustc_codegen_gcc/src/type_.rs b/compiler/rustc_codegen_gcc/src/type_.rs index 862ed62c68b2..bdf7318ce48c 100644 --- a/compiler/rustc_codegen_gcc/src/type_.rs +++ b/compiler/rustc_codegen_gcc/src/type_.rs @@ -277,7 +277,7 @@ pub fn struct_fields<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, layout: TyAndLayout offset = target_offset + field.size; prev_effective_align = effective_field_align; } - if !layout.is_unsized() && field_count > 0 { + if layout.is_sized() && field_count > 0 { if offset > layout.size { bug!("layout: {:#?} stride: {:?} offset: {:?}", layout, layout.size, offset); } diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 11053a8f6c45..97d0de47b3a6 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -765,11 +765,21 @@ pub(crate) unsafe fn codegen( drop(handlers); } + // `.dwo` files are only emitted if: + // + // - Object files are being emitted (i.e. bitcode only or metadata only compilations will not + // produce dwarf objects, even if otherwise enabled) + // - Target supports Split DWARF + // - Split debuginfo is enabled + // - Split DWARF kind is `split` (i.e. debuginfo is split into `.dwo` files, not different + // sections in the `.o` files). + let dwarf_object_emitted = matches!(config.emit_obj, EmitObj::ObjectCode(_)) + && cgcx.target_can_use_split_dwarf + && cgcx.split_debuginfo != SplitDebuginfo::Off + && cgcx.split_dwarf_kind == SplitDwarfKind::Split; Ok(module.into_compiled_module( config.emit_obj != EmitObj::None, - cgcx.target_can_use_split_dwarf - && cgcx.split_debuginfo != SplitDebuginfo::Off - && cgcx.split_dwarf_kind == SplitDwarfKind::Split, + dwarf_object_emitted, config.emit_bc, &cgcx.output_filenames, )) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs index a40cfc8b23fb..5cd0e1cb63ae 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs @@ -72,7 +72,7 @@ pub(crate) fn fat_pointer_kind<'ll, 'tcx>( layout.is_unsized() ); - if !layout.is_unsized() { + if layout.is_sized() { return None; } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 825011941a24..cf590a43826e 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -340,17 +340,26 @@ impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> { sym::black_box => { args[0].val.store(self, result); - + let result_val_span = [result.llval]; // We need to "use" the argument in some way LLVM can't introspect, and on // targets that support it we can typically leverage inline assembly to do // this. LLVM's interpretation of inline assembly is that it's, well, a black // box. This isn't the greatest implementation since it probably deoptimizes // more than we want, but it's so far good enough. + // + // For zero-sized types, the location pointed to by the result may be + // uninitialized. Do not "use" the result in this case; instead just clobber + // the memory. + let (constraint, inputs): (&str, &[_]) = if result.layout.is_zst() { + ("~{memory}", &[]) + } else { + ("r,~{memory}", &result_val_span) + }; crate::asm::inline_asm_call( self, "", - "r,~{memory}", - &[result.llval], + constraint, + inputs, self.type_void(), true, false, diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs index dc1165835e7c..182adf817857 100644 --- a/compiler/rustc_codegen_llvm/src/type_of.rs +++ b/compiler/rustc_codegen_llvm/src/type_of.rs @@ -140,7 +140,7 @@ fn struct_llfields<'a, 'tcx>( prev_effective_align = effective_field_align; } let padding_used = result.len() > field_count; - if !layout.is_unsized() && field_count > 0 { + if layout.is_sized() && field_count > 0 { if offset > layout.size { bug!("layout: {:#?} stride: {:?} offset: {:?}", layout, layout.size, offset); } diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs index 18789d00fd3a..2b1b06d1644c 100644 --- a/compiler/rustc_codegen_ssa/src/back/archive.rs +++ b/compiler/rustc_codegen_ssa/src/back/archive.rs @@ -4,8 +4,11 @@ use rustc_session::cstore::DllImport; use rustc_session::Session; use rustc_span::symbol::Symbol; +use super::metadata::search_for_section; + use object::read::archive::ArchiveFile; +use std::error::Error; use std::fs::File; use std::io; use std::path::{Path, PathBuf}; @@ -56,6 +59,9 @@ pub trait ArchiveBuilderBuilder { if !bundled_lib_file_names.contains(&Symbol::intern(name)) { continue; // We need to extract only native libraries. } + let data = search_for_section(rlib, data, ".bundled_lib").map_err(|e| { + ExtractBundledLibsError::ExtractSection { rlib, error: Box::::from(e) } + })?; std::fs::write(&outdir.join(&name), data) .map_err(|e| ExtractBundledLibsError::WriteFile { rlib, error: Box::new(e) })?; } diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 5a1ad792924f..4445e5f6c3a6 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -6,9 +6,9 @@ use rustc_data_structures::memmap::Mmap; use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_errors::{ErrorGuaranteed, Handler}; use rustc_fs_util::fix_windows_verbatim_for_gcc; -use rustc_hir::def_id::CrateNum; +use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_metadata::find_native_static_library; -use rustc_metadata::fs::{emit_metadata, METADATA_FILENAME}; +use rustc_metadata::fs::{emit_wrapper_file, METADATA_FILENAME}; use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols::SymbolExportKind; use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, LdImpl, Lto, Strip}; @@ -24,12 +24,12 @@ use rustc_span::symbol::Symbol; use rustc_span::DebuggerVisualizerFile; use rustc_target::spec::crt_objects::{CrtObjects, LinkSelfContainedDefault}; use rustc_target::spec::{Cc, LinkOutputKind, LinkerFlavor, LinkerFlavorCli, Lld, PanicStrategy}; -use rustc_target::spec::{RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, Target}; +use rustc_target::spec::{RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo}; use super::archive::{ArchiveBuilder, ArchiveBuilderBuilder}; use super::command::Command; use super::linker::{self, Linker}; -use super::metadata::{create_rmeta_file, MetadataPosition}; +use super::metadata::{create_wrapper_file, MetadataPosition}; use super::rpath::{self, RPathConfig}; use crate::{ errors, looks_like_rust_object_file, CodegenResults, CompiledModule, CrateInfo, NativeLib, @@ -44,7 +44,7 @@ use std::borrow::Borrow; use std::cell::OnceCell; use std::collections::BTreeSet; use std::ffi::OsString; -use std::fs::{File, OpenOptions}; +use std::fs::{read, File, OpenOptions}; use std::io::{BufWriter, Write}; use std::ops::Deref; use std::path::{Path, PathBuf}; @@ -292,8 +292,8 @@ fn link_rlib<'a>( let trailing_metadata = match flavor { RlibFlavor::Normal => { let (metadata, metadata_position) = - create_rmeta_file(sess, codegen_results.metadata.raw_data()); - let metadata = emit_metadata(sess, &metadata, tmpdir); + create_wrapper_file(sess, b".rmeta".to_vec(), codegen_results.metadata.raw_data()); + let metadata = emit_wrapper_file(sess, &metadata, tmpdir, METADATA_FILENAME); match metadata_position { MetadataPosition::First => { // Most of the time metadata in rlib files is wrapped in a "dummy" object @@ -376,12 +376,18 @@ fn link_rlib<'a>( let location = find_native_static_library(name.as_str(), lib.verbatim, &lib_search_paths, sess); if sess.opts.unstable_opts.packed_bundled_libs && flavor == RlibFlavor::Normal { - packed_bundled_libs.push(find_native_static_library( - lib.filename.unwrap().as_str(), + let filename = lib.filename.unwrap(); + let lib_path = find_native_static_library( + filename.as_str(), Some(true), &lib_search_paths, sess, - )); + ); + let src = read(lib_path) + .map_err(|e| sess.emit_fatal(errors::ReadFileError { message: e }))?; + let (data, _) = create_wrapper_file(sess, b".bundled_lib".to_vec(), &src); + let wrapper_file = emit_wrapper_file(sess, &data, tmpdir, filename.as_str()); + packed_bundled_libs.push(wrapper_file); continue; } ab.add_archive(&location, Box::new(|_| false)).unwrap_or_else(|error| { @@ -2007,15 +2013,9 @@ fn linker_with_args<'a>( cmd.add_as_needed(); // Local native libraries of all kinds. - // - // If `-Zlink-native-libraries=false` is set, then the assumption is that an - // external build system already has the native dependencies defined, and it - // will provide them to the linker itself. - if sess.opts.unstable_opts.link_native_libraries { - add_local_native_libraries(cmd, sess, codegen_results); - } + add_local_native_libraries(cmd, sess, archive_builder_builder, codegen_results, tmpdir); - // Upstream rust libraries and their (possibly bundled) static native libraries. + // Upstream rust crates and their non-dynamic native libraries. add_upstream_rust_crates( cmd, sess, @@ -2026,13 +2026,7 @@ fn linker_with_args<'a>( ); // Dynamic native libraries from upstream crates. - // - // FIXME: Merge this to `add_upstream_rust_crates` so that all native libraries are linked - // together with their respective upstream crates, and in their originally specified order. - // This may be slightly breaking due to our use of `--as-needed` and needs a crater run. - if sess.opts.unstable_opts.link_native_libraries { - add_upstream_native_libraries(cmd, sess, codegen_results); - } + add_upstream_native_libraries(cmd, sess, archive_builder_builder, codegen_results, tmpdir); // Link with the import library generated for any raw-dylib functions. for (raw_dylib_name, raw_dylib_imports) in @@ -2276,42 +2270,46 @@ fn collect_natvis_visualizers( visualizer_paths } -/// # Native library linking -/// -/// User-supplied library search paths (-L on the command line). These are the same paths used to -/// find Rust crates, so some of them may have been added already by the previous crate linking -/// code. This only allows them to be found at compile time so it is still entirely up to outside -/// forces to make sure that library can be found at runtime. -/// -/// Also note that the native libraries linked here are only the ones located in the current crate. -/// Upstream crates with native library dependencies may have their native library pulled in above. -fn add_local_native_libraries( +fn add_native_libs_from_crate( cmd: &mut dyn Linker, sess: &Session, + archive_builder_builder: &dyn ArchiveBuilderBuilder, codegen_results: &CodegenResults, + tmpdir: &Path, + search_paths: &OnceCell>, + bundled_libs: &FxHashSet, + cnum: CrateNum, + link_static: bool, + link_dynamic: bool, ) { - let filesearch = sess.target_filesearch(PathKind::All); - for search_path in filesearch.search_paths() { - match search_path.kind { - PathKind::Framework => { - cmd.framework_path(&search_path.dir); - } - _ => { - cmd.include_path(&fix_windows_verbatim_for_gcc(&search_path.dir)); - } - } + if !sess.opts.unstable_opts.link_native_libraries { + // If `-Zlink-native-libraries=false` is set, then the assumption is that an + // external build system already has the native dependencies defined, and it + // will provide them to the linker itself. + return; } - let relevant_libs = - codegen_results.crate_info.used_libraries.iter().filter(|l| relevant_lib(sess, l)); + if link_static && cnum != LOCAL_CRATE && !bundled_libs.is_empty() { + // If rlib contains native libs as archives, unpack them to tmpdir. + let rlib = &codegen_results.crate_info.used_crate_source[&cnum].rlib.as_ref().unwrap().0; + archive_builder_builder + .extract_bundled_libs(rlib, tmpdir, &bundled_libs) + .unwrap_or_else(|e| sess.emit_fatal(e)); + } + + let native_libs = match cnum { + LOCAL_CRATE => &codegen_results.crate_info.used_libraries, + _ => &codegen_results.crate_info.native_libraries[&cnum], + }; - let search_path = OnceCell::new(); let mut last = (None, NativeLibKind::Unspecified, None); - for lib in relevant_libs { + for lib in native_libs { let Some(name) = lib.name else { continue; }; - let name = name.as_str(); + if !relevant_lib(sess, lib) { + continue; + } // Skip if this library is the same as the last. last = if (lib.name, lib.kind, lib.verbatim) == last { @@ -2320,46 +2318,119 @@ fn add_local_native_libraries( (lib.name, lib.kind, lib.verbatim) }; + let name = name.as_str(); let verbatim = lib.verbatim.unwrap_or(false); match lib.kind { + NativeLibKind::Static { bundle, whole_archive } => { + if link_static { + let bundle = bundle.unwrap_or(true); + let whole_archive = whole_archive == Some(true) + // Backward compatibility case: this can be a rlib (so `+whole-archive` + // cannot be added explicitly if necessary, see the error in `fn link_rlib`) + // compiled as an executable due to `--test`. Use whole-archive implicitly, + // like before the introduction of native lib modifiers. + || (whole_archive == None + && bundle + && cnum == LOCAL_CRATE + && sess.opts.test); + + if bundle && cnum != LOCAL_CRATE { + if let Some(filename) = lib.filename { + // If rlib contains native libs as archives, they are unpacked to tmpdir. + let path = tmpdir.join(filename.as_str()); + if whole_archive { + cmd.link_whole_rlib(&path); + } else { + cmd.link_rlib(&path); + } + } + } else { + if whole_archive { + cmd.link_whole_staticlib( + name, + verbatim, + &search_paths.get_or_init(|| archive_search_paths(sess)), + ); + } else { + // HACK/FIXME: Fixup a circular dependency between libgcc and libc + // with glibc. This logic should be moved to the libc crate. + if cnum != LOCAL_CRATE + && sess.target.os == "linux" + && sess.target.env == "gnu" + && name == "c" + { + cmd.link_staticlib("gcc", false); + } + cmd.link_staticlib(name, verbatim) + } + } + } + } NativeLibKind::Dylib { as_needed } => { - cmd.link_dylib(name, verbatim, as_needed.unwrap_or(true)) + if link_dynamic { + cmd.link_dylib(name, verbatim, as_needed.unwrap_or(true)) + } + } + NativeLibKind::Unspecified => { + if link_dynamic { + cmd.link_dylib(name, verbatim, true); + } } - NativeLibKind::Unspecified => cmd.link_dylib(name, verbatim, true), NativeLibKind::Framework { as_needed } => { - cmd.link_framework(name, as_needed.unwrap_or(true)) - } - NativeLibKind::Static { whole_archive, bundle, .. } => { - if whole_archive == Some(true) - // Backward compatibility case: this can be a rlib (so `+whole-archive` cannot - // be added explicitly if necessary, see the error in `fn link_rlib`) compiled - // as an executable due to `--test`. Use whole-archive implicitly, like before - // the introduction of native lib modifiers. - || (whole_archive == None && bundle != Some(false) && sess.opts.test) - { - cmd.link_whole_staticlib( - name, - verbatim, - &search_path.get_or_init(|| archive_search_paths(sess)), - ); - } else { - cmd.link_staticlib(name, verbatim) + if link_dynamic { + cmd.link_framework(name, as_needed.unwrap_or(true)) } } NativeLibKind::RawDylib => { - // Ignore RawDylib here, they are handled separately in linker_with_args(). + // Handled separately in `linker_with_args`. } NativeLibKind::LinkArg => { - cmd.arg(name); + if link_static { + cmd.arg(name); + } } } } } -/// # Linking Rust crates and their non-bundled static libraries -/// -/// Rust crates are not considered at all when creating an rlib output. All dependencies will be -/// linked when producing the final output (instead of the intermediate rlib version). +fn add_local_native_libraries( + cmd: &mut dyn Linker, + sess: &Session, + archive_builder_builder: &dyn ArchiveBuilderBuilder, + codegen_results: &CodegenResults, + tmpdir: &Path, +) { + if sess.opts.unstable_opts.link_native_libraries { + // User-supplied library search paths (-L on the command line). These are the same paths + // used to find Rust crates, so some of them may have been added already by the previous + // crate linking code. This only allows them to be found at compile time so it is still + // entirely up to outside forces to make sure that library can be found at runtime. + for search_path in sess.target_filesearch(PathKind::All).search_paths() { + match search_path.kind { + PathKind::Framework => cmd.framework_path(&search_path.dir), + _ => cmd.include_path(&fix_windows_verbatim_for_gcc(&search_path.dir)), + } + } + } + + let search_paths = OnceCell::new(); + // All static and dynamic native library dependencies are linked to the local crate. + let link_static = true; + let link_dynamic = true; + add_native_libs_from_crate( + cmd, + sess, + archive_builder_builder, + codegen_results, + tmpdir, + &search_paths, + &Default::default(), + LOCAL_CRATE, + link_static, + link_dynamic, + ); +} + fn add_upstream_rust_crates<'a>( cmd: &mut dyn Linker, sess: &'a Session, @@ -2375,7 +2446,6 @@ fn add_upstream_rust_crates<'a>( // Linking to a rlib involves just passing it to the linker (the linker // will slurp up the object files inside), and linking to a dynamic library // involves just passing the right -l flag. - let (_, data) = codegen_results .crate_info .dependency_formats @@ -2383,348 +2453,236 @@ fn add_upstream_rust_crates<'a>( .find(|(ty, _)| *ty == crate_type) .expect("failed to find crate type in dependency format list"); - // Invoke get_used_crates to ensure that we get a topological sorting of - // crates. - let deps = &codegen_results.crate_info.used_crates; + let search_paths = OnceCell::new(); + for &cnum in &codegen_results.crate_info.used_crates { + // We may not pass all crates through to the linker. Some crates may appear statically in + // an existing dylib, meaning we'll pick up all the symbols from the dylib. + // We must always link crates `compiler_builtins` and `profiler_builtins` statically. + // Even if they were already included into a dylib + // (e.g. `libstd` when `-C prefer-dynamic` is used). + // FIXME: `dependency_formats` can report `profiler_builtins` as `NotLinked` for some + // reason, it shouldn't do that because `profiler_builtins` should indeed be linked. + let linkage = data[cnum.as_usize() - 1]; + let link_static_crate = linkage == Linkage::Static + || (linkage == Linkage::IncludedFromDylib || linkage == Linkage::NotLinked) + && (codegen_results.crate_info.compiler_builtins == Some(cnum) + || codegen_results.crate_info.profiler_runtime == Some(cnum)); - let mut compiler_builtins = None; - let search_path = OnceCell::new(); - - for &cnum in deps.iter() { - // We may not pass all crates through to the linker. Some crates may - // appear statically in an existing dylib, meaning we'll pick up all the - // symbols from the dylib. - let src = &codegen_results.crate_info.used_crate_source[&cnum]; - match data[cnum.as_usize() - 1] { - _ if codegen_results.crate_info.profiler_runtime == Some(cnum) => { - add_static_crate( - cmd, - sess, - archive_builder_builder, - codegen_results, - tmpdir, - cnum, - &Default::default(), - ); - } - // compiler-builtins are always placed last to ensure that they're - // linked correctly. - _ if codegen_results.crate_info.compiler_builtins == Some(cnum) => { - assert!(compiler_builtins.is_none()); - compiler_builtins = Some(cnum); - } - Linkage::NotLinked | Linkage::IncludedFromDylib => {} - Linkage::Static => { - let bundled_libs = if sess.opts.unstable_opts.packed_bundled_libs { - codegen_results.crate_info.native_libraries[&cnum] + let mut bundled_libs = Default::default(); + match linkage { + Linkage::Static | Linkage::IncludedFromDylib | Linkage::NotLinked => { + if link_static_crate { + bundled_libs = codegen_results.crate_info.native_libraries[&cnum] .iter() .filter_map(|lib| lib.filename) - .collect::>() - } else { - Default::default() - }; - add_static_crate( - cmd, - sess, - archive_builder_builder, - codegen_results, - tmpdir, - cnum, - &bundled_libs, - ); - - // Link static native libs with "-bundle" modifier only if the crate they originate from - // is being linked statically to the current crate. If it's linked dynamically - // or is an rlib already included via some other dylib crate, the symbols from - // native libs will have already been included in that dylib. - // - // If `-Zlink-native-libraries=false` is set, then the assumption is that an - // external build system already has the native dependencies defined, and it - // will provide them to the linker itself. - if sess.opts.unstable_opts.link_native_libraries { - if sess.opts.unstable_opts.packed_bundled_libs { - // If rlib contains native libs as archives, unpack them to tmpdir. - let rlib = &src.rlib.as_ref().unwrap().0; - archive_builder_builder - .extract_bundled_libs(rlib, tmpdir, &bundled_libs) - .unwrap_or_else(|e| sess.emit_fatal(e)); - } - - let mut last = (None, NativeLibKind::Unspecified, None); - for lib in &codegen_results.crate_info.native_libraries[&cnum] { - let Some(name) = lib.name else { - continue; - }; - let name = name.as_str(); - if !relevant_lib(sess, lib) { - continue; - } - - // Skip if this library is the same as the last. - last = if (lib.name, lib.kind, lib.verbatim) == last { - continue; - } else { - (lib.name, lib.kind, lib.verbatim) - }; - - match lib.kind { - NativeLibKind::Static { - bundle: Some(false), - whole_archive: Some(true), - } => { - cmd.link_whole_staticlib( - name, - lib.verbatim.unwrap_or(false), - search_path.get_or_init(|| archive_search_paths(sess)), - ); - } - NativeLibKind::Static { - bundle: Some(false), - whole_archive: Some(false) | None, - } => { - // HACK/FIXME: Fixup a circular dependency between libgcc and libc - // with glibc. This logic should be moved to the libc crate. - if sess.target.os == "linux" - && sess.target.env == "gnu" - && name == "c" - { - cmd.link_staticlib("gcc", false); - } - cmd.link_staticlib(name, lib.verbatim.unwrap_or(false)); - } - NativeLibKind::LinkArg => { - cmd.arg(name); - } - NativeLibKind::Dylib { .. } - | NativeLibKind::Framework { .. } - | NativeLibKind::Unspecified - | NativeLibKind::RawDylib => {} - NativeLibKind::Static { bundle: Some(true) | None, whole_archive } => { - if sess.opts.unstable_opts.packed_bundled_libs { - // If rlib contains native libs as archives, they are unpacked to tmpdir. - let path = tmpdir.join(lib.filename.unwrap().as_str()); - if whole_archive == Some(true) { - cmd.link_whole_rlib(&path); - } else { - cmd.link_rlib(&path); - } - } - } - } - } + .collect(); + add_static_crate( + cmd, + sess, + archive_builder_builder, + codegen_results, + tmpdir, + cnum, + &bundled_libs, + ); } } - Linkage::Dynamic => add_dynamic_crate(cmd, sess, &src.dylib.as_ref().unwrap().0), + Linkage::Dynamic => { + let src = &codegen_results.crate_info.used_crate_source[&cnum]; + add_dynamic_crate(cmd, sess, &src.dylib.as_ref().unwrap().0); + } } - } - // compiler-builtins are always placed last to ensure that they're - // linked correctly. - // We must always link the `compiler_builtins` crate statically. Even if it - // was already "included" in a dylib (e.g., `libstd` when `-C prefer-dynamic` - // is used) - if let Some(cnum) = compiler_builtins { - add_static_crate( + // Static libraries are linked for a subset of linked upstream crates. + // 1. If the upstream crate is a directly linked rlib then we must link the native library + // because the rlib is just an archive. + // 2. If the upstream crate is a dylib or a rlib linked through dylib, then we do not link + // the native library because it is already linked into the dylib, and even if + // inline/const/generic functions from the dylib can refer to symbols from the native + // library, those symbols should be exported and available from the dylib anyway. + // 3. Libraries bundled into `(compiler,profiler)_builtins` are special, see above. + let link_static = link_static_crate; + // Dynamic libraries are not linked here, see the FIXME in `add_upstream_native_libraries`. + let link_dynamic = false; + add_native_libs_from_crate( cmd, sess, archive_builder_builder, codegen_results, tmpdir, + &search_paths, + &bundled_libs, cnum, - &Default::default(), - ); - } - - // Converts a library file-stem into a cc -l argument - fn unlib<'a>(target: &Target, stem: &'a str) -> &'a str { - if stem.starts_with("lib") && !target.is_like_windows { &stem[3..] } else { stem } - } - - // Adds the static "rlib" versions of all crates to the command line. - // There's a bit of magic which happens here specifically related to LTO, - // namely that we remove upstream object files. - // - // When performing LTO, almost(*) all of the bytecode from the upstream - // libraries has already been included in our object file output. As a - // result we need to remove the object files in the upstream libraries so - // the linker doesn't try to include them twice (or whine about duplicate - // symbols). We must continue to include the rest of the rlib, however, as - // it may contain static native libraries which must be linked in. - // - // (*) Crates marked with `#![no_builtins]` don't participate in LTO and - // their bytecode wasn't included. The object files in those libraries must - // still be passed to the linker. - // - // Note, however, that if we're not doing LTO we can just pass the rlib - // blindly to the linker (fast) because it's fine if it's not actually - // included as we're at the end of the dependency chain. - fn add_static_crate<'a>( - cmd: &mut dyn Linker, - sess: &'a Session, - archive_builder_builder: &dyn ArchiveBuilderBuilder, - codegen_results: &CodegenResults, - tmpdir: &Path, - cnum: CrateNum, - bundled_lib_file_names: &FxHashSet, - ) { - let src = &codegen_results.crate_info.used_crate_source[&cnum]; - let cratepath = &src.rlib.as_ref().unwrap().0; - - let mut link_upstream = |path: &Path| { - cmd.link_rlib(&fix_windows_verbatim_for_gcc(path)); - }; - - // See the comment above in `link_staticlib` and `link_rlib` for why if - // there's a static library that's not relevant we skip all object - // files. - let native_libs = &codegen_results.crate_info.native_libraries[&cnum]; - let skip_native = native_libs.iter().any(|lib| { - matches!(lib.kind, NativeLibKind::Static { bundle: None | Some(true), .. }) - && !relevant_lib(sess, lib) - }); - - if (!are_upstream_rust_objects_already_included(sess) - || ignored_for_lto(sess, &codegen_results.crate_info, cnum)) - && !skip_native - { - link_upstream(cratepath); - return; - } - - let dst = tmpdir.join(cratepath.file_name().unwrap()); - let name = cratepath.file_name().unwrap().to_str().unwrap(); - let name = &name[3..name.len() - 5]; // chop off lib/.rlib - let bundled_lib_file_names = bundled_lib_file_names.clone(); - - sess.prof.generic_activity_with_arg("link_altering_rlib", name).run(|| { - let canonical_name = name.replace('-', "_"); - let upstream_rust_objects_already_included = - are_upstream_rust_objects_already_included(sess); - let is_builtins = sess.target.no_builtins - || !codegen_results.crate_info.is_no_builtins.contains(&cnum); - - let mut archive = archive_builder_builder.new_archive_builder(sess); - if let Err(error) = archive.add_archive( - cratepath, - Box::new(move |f| { - if f == METADATA_FILENAME { - return true; - } - - let canonical = f.replace('-', "_"); - - let is_rust_object = - canonical.starts_with(&canonical_name) && looks_like_rust_object_file(&f); - - // If we've been requested to skip all native object files - // (those not generated by the rust compiler) then we can skip - // this file. See above for why we may want to do this. - let skip_because_cfg_say_so = skip_native && !is_rust_object; - - // If we're performing LTO and this is a rust-generated object - // file, then we don't need the object file as it's part of the - // LTO module. Note that `#![no_builtins]` is excluded from LTO, - // though, so we let that object file slide. - let skip_because_lto = - upstream_rust_objects_already_included && is_rust_object && is_builtins; - - // We skip native libraries because: - // 1. This native libraries won't be used from the generated rlib, - // so we can throw them away to avoid the copying work. - // 2. We can't allow it to be a single remaining entry in archive - // as some linkers may complain on that. - if bundled_lib_file_names.contains(&Symbol::intern(f)) { - return true; - } - - if skip_because_cfg_say_so || skip_because_lto { - return true; - } - - false - }), - ) { - sess.emit_fatal(errors::RlibArchiveBuildFailure { error }); - } - if archive.build(&dst) { - link_upstream(&dst); - } - }); - } - - // Same thing as above, but for dynamic crates instead of static crates. - fn add_dynamic_crate(cmd: &mut dyn Linker, sess: &Session, cratepath: &Path) { - // Just need to tell the linker about where the library lives and - // what its name is - let parent = cratepath.parent(); - if let Some(dir) = parent { - cmd.include_path(&fix_windows_verbatim_for_gcc(dir)); - } - let filestem = cratepath.file_stem().unwrap().to_str().unwrap(); - cmd.link_rust_dylib( - &unlib(&sess.target, filestem), - parent.unwrap_or_else(|| Path::new("")), + link_static, + link_dynamic, ); } } -/// Link in all of our upstream crates' native dependencies. Remember that all of these upstream -/// native dependencies are all non-static dependencies. We've got two cases then: -/// -/// 1. The upstream crate is an rlib. In this case we *must* link in the native dependency because -/// the rlib is just an archive. -/// -/// 2. The upstream crate is a dylib. In order to use the dylib, we have to have the dependency -/// present on the system somewhere. Thus, we don't gain a whole lot from not linking in the -/// dynamic dependency to this crate as well. -/// -/// The use case for this is a little subtle. In theory the native dependencies of a crate are -/// purely an implementation detail of the crate itself, but the problem arises with generic and -/// inlined functions. If a generic function calls a native function, then the generic function -/// must be instantiated in the target crate, meaning that the native symbol must also be resolved -/// in the target crate. fn add_upstream_native_libraries( cmd: &mut dyn Linker, sess: &Session, + archive_builder_builder: &dyn ArchiveBuilderBuilder, codegen_results: &CodegenResults, + tmpdir: &Path, ) { - let mut last = (None, NativeLibKind::Unspecified, None); + let search_path = OnceCell::new(); for &cnum in &codegen_results.crate_info.used_crates { - for lib in codegen_results.crate_info.native_libraries[&cnum].iter() { - let Some(name) = lib.name else { - continue; - }; - let name = name.as_str(); - if !relevant_lib(sess, &lib) { - continue; - } - - // Skip if this library is the same as the last. - last = if (lib.name, lib.kind, lib.verbatim) == last { - continue; - } else { - (lib.name, lib.kind, lib.verbatim) - }; - - let verbatim = lib.verbatim.unwrap_or(false); - match lib.kind { - NativeLibKind::Dylib { as_needed } => { - cmd.link_dylib(name, verbatim, as_needed.unwrap_or(true)) - } - NativeLibKind::Unspecified => cmd.link_dylib(name, verbatim, true), - NativeLibKind::Framework { as_needed } => { - cmd.link_framework(name, as_needed.unwrap_or(true)) - } - // ignore static native libraries here as we've - // already included them in add_local_native_libraries and - // add_upstream_rust_crates - NativeLibKind::Static { .. } => {} - NativeLibKind::RawDylib | NativeLibKind::LinkArg => {} - } - } + // Static libraries are not linked here, they are linked in `add_upstream_rust_crates`. + // FIXME: Merge this function to `add_upstream_rust_crates` so that all native libraries + // are linked together with their respective upstream crates, and in their originally + // specified order. This is slightly breaking due to our use of `--as-needed` (see crater + // results in https://github.com/rust-lang/rust/pull/102832#issuecomment-1279772306). + let link_static = false; + // Dynamic libraries are linked for all linked upstream crates. + // 1. If the upstream crate is a directly linked rlib then we must link the native library + // because the rlib is just an archive. + // 2. If the upstream crate is a dylib or a rlib linked through dylib, then we have to link + // the native library too because inline/const/generic functions from the dylib can refer + // to symbols from the native library, so the native library providing those symbols should + // be available when linking our final binary. + let link_dynamic = true; + add_native_libs_from_crate( + cmd, + sess, + archive_builder_builder, + codegen_results, + tmpdir, + &search_path, + &Default::default(), + cnum, + link_static, + link_dynamic, + ); } } +// Adds the static "rlib" versions of all crates to the command line. +// There's a bit of magic which happens here specifically related to LTO, +// namely that we remove upstream object files. +// +// When performing LTO, almost(*) all of the bytecode from the upstream +// libraries has already been included in our object file output. As a +// result we need to remove the object files in the upstream libraries so +// the linker doesn't try to include them twice (or whine about duplicate +// symbols). We must continue to include the rest of the rlib, however, as +// it may contain static native libraries which must be linked in. +// +// (*) Crates marked with `#![no_builtins]` don't participate in LTO and +// their bytecode wasn't included. The object files in those libraries must +// still be passed to the linker. +// +// Note, however, that if we're not doing LTO we can just pass the rlib +// blindly to the linker (fast) because it's fine if it's not actually +// included as we're at the end of the dependency chain. +fn add_static_crate<'a>( + cmd: &mut dyn Linker, + sess: &'a Session, + archive_builder_builder: &dyn ArchiveBuilderBuilder, + codegen_results: &CodegenResults, + tmpdir: &Path, + cnum: CrateNum, + bundled_lib_file_names: &FxHashSet, +) { + let src = &codegen_results.crate_info.used_crate_source[&cnum]; + let cratepath = &src.rlib.as_ref().unwrap().0; + + let mut link_upstream = |path: &Path| { + cmd.link_rlib(&fix_windows_verbatim_for_gcc(path)); + }; + + // See the comment above in `link_staticlib` and `link_rlib` for why if + // there's a static library that's not relevant we skip all object + // files. + let native_libs = &codegen_results.crate_info.native_libraries[&cnum]; + let skip_native = native_libs.iter().any(|lib| { + matches!(lib.kind, NativeLibKind::Static { bundle: None | Some(true), .. }) + && !relevant_lib(sess, lib) + }); + + if (!are_upstream_rust_objects_already_included(sess) + || ignored_for_lto(sess, &codegen_results.crate_info, cnum)) + && !skip_native + { + link_upstream(cratepath); + return; + } + + let dst = tmpdir.join(cratepath.file_name().unwrap()); + let name = cratepath.file_name().unwrap().to_str().unwrap(); + let name = &name[3..name.len() - 5]; // chop off lib/.rlib + let bundled_lib_file_names = bundled_lib_file_names.clone(); + + sess.prof.generic_activity_with_arg("link_altering_rlib", name).run(|| { + let canonical_name = name.replace('-', "_"); + let upstream_rust_objects_already_included = + are_upstream_rust_objects_already_included(sess); + let is_builtins = + sess.target.no_builtins || !codegen_results.crate_info.is_no_builtins.contains(&cnum); + + let mut archive = archive_builder_builder.new_archive_builder(sess); + if let Err(e) = archive.add_archive( + cratepath, + Box::new(move |f| { + if f == METADATA_FILENAME { + return true; + } + + let canonical = f.replace('-', "_"); + + let is_rust_object = + canonical.starts_with(&canonical_name) && looks_like_rust_object_file(&f); + + // If we've been requested to skip all native object files + // (those not generated by the rust compiler) then we can skip + // this file. See above for why we may want to do this. + let skip_because_cfg_say_so = skip_native && !is_rust_object; + + // If we're performing LTO and this is a rust-generated object + // file, then we don't need the object file as it's part of the + // LTO module. Note that `#![no_builtins]` is excluded from LTO, + // though, so we let that object file slide. + let skip_because_lto = + upstream_rust_objects_already_included && is_rust_object && is_builtins; + + // We skip native libraries because: + // 1. This native libraries won't be used from the generated rlib, + // so we can throw them away to avoid the copying work. + // 2. We can't allow it to be a single remaining entry in archive + // as some linkers may complain on that. + if bundled_lib_file_names.contains(&Symbol::intern(f)) { + return true; + } + + if skip_because_cfg_say_so || skip_because_lto { + return true; + } + + false + }), + ) { + sess.fatal(&format!("failed to build archive from rlib: {}", e)); + } + if archive.build(&dst) { + link_upstream(&dst); + } + }); +} + +// Same thing as above, but for dynamic crates instead of static crates. +fn add_dynamic_crate(cmd: &mut dyn Linker, sess: &Session, cratepath: &Path) { + // Just need to tell the linker about where the library lives and + // what its name is + let parent = cratepath.parent(); + if let Some(dir) = parent { + cmd.include_path(&fix_windows_verbatim_for_gcc(dir)); + } + let stem = cratepath.file_stem().unwrap().to_str().unwrap(); + // Convert library file-stem into a cc -l argument. + let prefix = if stem.starts_with("lib") && !sess.target.is_like_windows { 3 } else { 0 }; + cmd.link_rust_dylib(&stem[prefix..], parent.unwrap_or_else(|| Path::new(""))); +} + fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool { match lib.cfg { Some(ref cfg) => rustc_attr::cfg_matches(cfg, &sess.parse_sess, CRATE_NODE_ID, None), diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index c49b19bdf009..7f0c2861f7e2 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -1260,11 +1260,11 @@ impl<'a> Linker for WasmLd<'a> { } fn link_whole_staticlib(&mut self, lib: &str, _verbatim: bool, _search_path: &[PathBuf]) { - self.cmd.arg("-l").arg(lib); + self.cmd.arg("--whole-archive").arg("-l").arg(lib).arg("--no-whole-archive"); } fn link_whole_rlib(&mut self, lib: &Path) { - self.cmd.arg(lib); + self.cmd.arg("--whole-archive").arg(lib).arg("--no-whole-archive"); } fn gc_sections(&mut self, _keep_metadata: bool) { diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index 99ddd1764785..780a38500368 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -60,7 +60,7 @@ impl MetadataLoader for DefaultMetadataLoader { let data = entry .data(data) .map_err(|e| format!("failed to parse rlib '{}': {}", path.display(), e))?; - return search_for_metadata(path, data, ".rmeta"); + return search_for_section(path, data, ".rmeta"); } } @@ -69,11 +69,11 @@ impl MetadataLoader for DefaultMetadataLoader { } fn get_dylib_metadata(&self, _target: &Target, path: &Path) -> Result { - load_metadata_with(path, |data| search_for_metadata(path, data, ".rustc")) + load_metadata_with(path, |data| search_for_section(path, data, ".rustc")) } } -fn search_for_metadata<'a>( +pub(super) fn search_for_section<'a>( path: &Path, bytes: &'a [u8], section: &str, @@ -223,7 +223,11 @@ pub enum MetadataPosition { // * ELF - All other targets are similar to Windows in that there's a // `SHF_EXCLUDE` flag we can set on sections in an object file to get // automatically removed from the final output. -pub fn create_rmeta_file(sess: &Session, metadata: &[u8]) -> (Vec, MetadataPosition) { +pub fn create_wrapper_file( + sess: &Session, + section_name: Vec, + data: &[u8], +) -> (Vec, MetadataPosition) { let Some(mut file) = create_object_file(sess) else { // This is used to handle all "other" targets. This includes targets // in two categories: @@ -241,11 +245,11 @@ pub fn create_rmeta_file(sess: &Session, metadata: &[u8]) -> (Vec, MetadataP // WebAssembly and for targets not supported by the `object` crate // yet it means that work will need to be done in the `object` crate // to add a case above. - return (metadata.to_vec(), MetadataPosition::Last); + return (data.to_vec(), MetadataPosition::Last); }; let section = file.add_section( file.segment_name(StandardSegment::Debug).to_vec(), - b".rmeta".to_vec(), + section_name, SectionKind::Debug, ); match file.format() { @@ -259,7 +263,7 @@ pub fn create_rmeta_file(sess: &Session, metadata: &[u8]) -> (Vec, MetadataP } _ => {} }; - file.append_section_data(section, metadata, 1); + file.append_section_data(section, data, 1); (file.write().unwrap(), MetadataPosition::First) } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index c1411690f828..4f396e970ad7 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -833,20 +833,30 @@ impl CrateInfo { // // In order to get this left-to-right dependency ordering, we use the reverse // postorder of all crates putting the leaves at the right-most positions. - let used_crates = tcx + let mut compiler_builtins = None; + let mut used_crates: Vec<_> = tcx .postorder_cnums(()) .iter() .rev() .copied() - .filter(|&cnum| !tcx.dep_kind(cnum).macros_only()) + .filter(|&cnum| { + let link = !tcx.dep_kind(cnum).macros_only(); + if link && tcx.is_compiler_builtins(cnum) { + compiler_builtins = Some(cnum); + return false; + } + link + }) .collect(); + // `compiler_builtins` are always placed last to ensure that they're linked correctly. + used_crates.extend(compiler_builtins); let mut info = CrateInfo { target_cpu, exported_symbols, linked_symbols, local_crate_name, - compiler_builtins: None, + compiler_builtins, profiler_runtime: None, is_no_builtins: Default::default(), native_libraries: Default::default(), @@ -872,9 +882,6 @@ impl CrateInfo { let used_crate_source = tcx.used_crate_source(cnum); info.used_crate_source.insert(cnum, used_crate_source.clone()); - if tcx.is_compiler_builtins(cnum) { - info.compiler_builtins = Some(cnum); - } if tcx.is_profiler_runtime(cnum) { info.profiler_runtime = Some(cnum); } diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 36c94462b0b3..bfc4515de098 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -507,6 +507,9 @@ pub enum ExtractBundledLibsError<'a> { #[diag(codegen_ssa_extract_bundled_libs_write_file)] WriteFile { rlib: &'a Path, error: Box }, + + #[diag(codegen_ssa_extract_bundled_libs_write_file)] + ExtractSection { rlib: &'a Path, error: Box }, } #[derive(Diagnostic)] @@ -521,3 +524,9 @@ pub enum AppleSdkRootError<'a> { #[diag(codegen_ssa_apple_sdk_error_sdk_path)] SdkPath { sdk_name: &'a str, error: Error }, } + +#[derive(Diagnostic)] +#[diag(codegen_ssa_read_file)] +pub struct ReadFileError { + pub message: std::io::Error, +} diff --git a/compiler/rustc_codegen_ssa/src/glue.rs b/compiler/rustc_codegen_ssa/src/glue.rs index e6f402ef19d8..6015d48decae 100644 --- a/compiler/rustc_codegen_ssa/src/glue.rs +++ b/compiler/rustc_codegen_ssa/src/glue.rs @@ -15,7 +15,7 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( ) -> (Bx::Value, Bx::Value) { let layout = bx.layout_of(t); debug!("size_and_align_of_dst(ty={}, info={:?}): layout: {:?}", t, info, layout); - if !layout.is_unsized() { + if layout.is_sized() { let size = bx.const_usize(layout.size.bytes()); let align = bx.const_usize(layout.align.abi.bytes()); return (size, align); diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index 9c18df5643f1..908555385891 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -29,7 +29,7 @@ pub struct PlaceRef<'tcx, V> { impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { pub fn new_sized(llval: V, layout: TyAndLayout<'tcx>) -> PlaceRef<'tcx, V> { - assert!(!layout.is_unsized()); + assert!(layout.is_sized()); PlaceRef { llval, llextra: None, layout, align: layout.align.abi } } @@ -38,7 +38,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { layout: TyAndLayout<'tcx>, align: Align, ) -> PlaceRef<'tcx, V> { - assert!(!layout.is_unsized()); + assert!(layout.is_sized()); PlaceRef { llval, llextra: None, layout, align } } @@ -48,7 +48,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { bx: &mut Bx, layout: TyAndLayout<'tcx>, ) -> Self { - assert!(!layout.is_unsized(), "tried to statically allocate unsized place"); + assert!(layout.is_sized(), "tried to statically allocate unsized place"); let tmp = bx.alloca(bx.cx().backend_type(layout), layout.align.abi); Self::new_sized(tmp, layout) } @@ -145,7 +145,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { ); return simple(); } - _ if !field.is_unsized() => return simple(), + _ if field.is_sized() => return simple(), ty::Slice(..) | ty::Str | ty::Foreign(..) => return simple(), ty::Adt(def, _) => { if def.repr().packed() { @@ -209,7 +209,9 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { bx: &mut Bx, cast_to: Ty<'tcx>, ) -> V { - let cast_to = bx.cx().immediate_backend_type(bx.cx().layout_of(cast_to)); + let cast_to_layout = bx.cx().layout_of(cast_to); + let cast_to_size = cast_to_layout.layout.size(); + let cast_to = bx.cx().immediate_backend_type(cast_to_layout); if self.layout.abi.is_uninhabited() { return bx.cx().const_undef(cast_to); } @@ -229,7 +231,8 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { // Read the tag/niche-encoded discriminant from memory. let tag = self.project_field(bx, tag_field); - let tag = bx.load_operand(tag); + let tag_op = bx.load_operand(tag); + let tag_imm = tag_op.immediate(); // Decode the discriminant (specifically if it's niche-encoded). match *tag_encoding { @@ -242,68 +245,161 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { Int(_, signed) => !tag_scalar.is_bool() && signed, _ => false, }; - bx.intcast(tag.immediate(), cast_to, signed) + bx.intcast(tag_imm, cast_to, signed) } TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start } => { - // Rebase from niche values to discriminants, and check - // whether the result is in range for the niche variants. - let niche_llty = bx.cx().immediate_backend_type(tag.layout); - let tag = tag.immediate(); - - // We first compute the "relative discriminant" (wrt `niche_variants`), - // that is, if `n = niche_variants.end() - niche_variants.start()`, - // we remap `niche_start..=niche_start + n` (which may wrap around) - // to (non-wrap-around) `0..=n`, to be able to check whether the - // discriminant corresponds to a niche variant with one comparison. - // We also can't go directly to the (variant index) discriminant - // and check that it is in the range `niche_variants`, because - // that might not fit in the same type, on top of needing an extra - // comparison (see also the comment on `let niche_discr`). - let relative_discr = if niche_start == 0 { - // Avoid subtracting `0`, which wouldn't work for pointers. - // FIXME(eddyb) check the actual primitive type here. - tag + // Cast to an integer so we don't have to treat a pointer as a + // special case. + let (tag, tag_llty) = if tag_scalar.primitive().is_ptr() { + let t = bx.type_isize(); + let tag = bx.ptrtoint(tag_imm, t); + (tag, t) } else { - bx.sub(tag, bx.cx().const_uint_big(niche_llty, niche_start)) + (tag_imm, bx.cx().immediate_backend_type(tag_op.layout)) }; + + let tag_size = tag_scalar.size(bx.cx()); + let max_unsigned = tag_size.unsigned_int_max(); + let max_signed = tag_size.signed_int_max() as u128; + let min_signed = max_signed + 1; let relative_max = niche_variants.end().as_u32() - niche_variants.start().as_u32(); - let is_niche = if relative_max == 0 { - // Avoid calling `const_uint`, which wouldn't work for pointers. - // Also use canonical == 0 instead of non-canonical u<= 0. - // FIXME(eddyb) check the actual primitive type here. - bx.icmp(IntPredicate::IntEQ, relative_discr, bx.cx().const_null(niche_llty)) + let niche_end = niche_start.wrapping_add(relative_max as u128) & max_unsigned; + let range = tag_scalar.valid_range(bx.cx()); + + let sle = |lhs: u128, rhs: u128| -> bool { + // Signed and unsigned comparisons give the same results, + // except that in signed comparisons an integer with the + // sign bit set is less than one with the sign bit clear. + // Toggle the sign bit to do a signed comparison. + (lhs ^ min_signed) <= (rhs ^ min_signed) + }; + + // We have a subrange `niche_start..=niche_end` inside `range`. + // If the value of the tag is inside this subrange, it's a + // "niche value", an increment of the discriminant. Otherwise it + // indicates the untagged variant. + // A general algorithm to extract the discriminant from the tag + // is: + // relative_tag = tag - niche_start + // is_niche = relative_tag <= (ule) relative_max + // discr = if is_niche { + // cast(relative_tag) + niche_variants.start() + // } else { + // untagged_variant + // } + // However, we will likely be able to emit simpler code. + + // Find the least and greatest values in `range`, considered + // both as signed and unsigned. + let (low_unsigned, high_unsigned) = if range.start <= range.end { + (range.start, range.end) } else { - let relative_max = bx.cx().const_uint(niche_llty, relative_max as u64); - bx.icmp(IntPredicate::IntULE, relative_discr, relative_max) + (0, max_unsigned) + }; + let (low_signed, high_signed) = if sle(range.start, range.end) { + (range.start, range.end) + } else { + (min_signed, max_signed) }; - // NOTE(eddyb) this addition needs to be performed on the final - // type, in case the niche itself can't represent all variant - // indices (e.g. `u8` niche with more than `256` variants, - // but enough uninhabited variants so that the remaining variants - // fit in the niche). - // In other words, `niche_variants.end - niche_variants.start` - // is representable in the niche, but `niche_variants.end` - // might not be, in extreme cases. - let niche_discr = { - let relative_discr = if relative_max == 0 { - // HACK(eddyb) since we have only one niche, we know which - // one it is, and we can avoid having a dynamic value here. - bx.cx().const_uint(cast_to, 0) + let niches_ule = niche_start <= niche_end; + let niches_sle = sle(niche_start, niche_end); + let cast_smaller = cast_to_size <= tag_size; + + // In the algorithm above, we can change + // cast(relative_tag) + niche_variants.start() + // into + // cast(tag) + (niche_variants.start() - niche_start) + // if either the casted type is no larger than the original + // type, or if the niche values are contiguous (in either the + // signed or unsigned sense). + let can_incr_after_cast = cast_smaller || niches_ule || niches_sle; + + let data_for_boundary_niche = || -> Option<(IntPredicate, u128)> { + if !can_incr_after_cast { + None + } else if niche_start == low_unsigned { + Some((IntPredicate::IntULE, niche_end)) + } else if niche_end == high_unsigned { + Some((IntPredicate::IntUGE, niche_start)) + } else if niche_start == low_signed { + Some((IntPredicate::IntSLE, niche_end)) + } else if niche_end == high_signed { + Some((IntPredicate::IntSGE, niche_start)) } else { - bx.intcast(relative_discr, cast_to, false) - }; - bx.add( - relative_discr, - bx.cx().const_uint(cast_to, niche_variants.start().as_u32() as u64), - ) + None + } }; - bx.select( + let (is_niche, tagged_discr, delta) = if relative_max == 0 { + // Best case scenario: only one tagged variant. This will + // likely become just a comparison and a jump. + // The algorithm is: + // is_niche = tag == niche_start + // discr = if is_niche { + // niche_start + // } else { + // untagged_variant + // } + let niche_start = bx.cx().const_uint_big(tag_llty, niche_start); + let is_niche = bx.icmp(IntPredicate::IntEQ, tag, niche_start); + let tagged_discr = + bx.cx().const_uint(cast_to, niche_variants.start().as_u32() as u64); + (is_niche, tagged_discr, 0) + } else if let Some((predicate, constant)) = data_for_boundary_niche() { + // The niche values are either the lowest or the highest in + // `range`. We can avoid the first subtraction in the + // algorithm. + // The algorithm is now this: + // is_niche = tag <= niche_end + // discr = if is_niche { + // cast(tag) + (niche_variants.start() - niche_start) + // } else { + // untagged_variant + // } + // (the first line may instead be tag >= niche_start, + // and may be a signed or unsigned comparison) + let is_niche = + bx.icmp(predicate, tag, bx.cx().const_uint_big(tag_llty, constant)); + let cast_tag = if cast_smaller { + bx.intcast(tag, cast_to, false) + } else if niches_ule { + bx.zext(tag, cast_to) + } else { + bx.sext(tag, cast_to) + }; + + let delta = (niche_variants.start().as_u32() as u128).wrapping_sub(niche_start); + (is_niche, cast_tag, delta) + } else { + // The special cases don't apply, so we'll have to go with + // the general algorithm. + let relative_discr = bx.sub(tag, bx.cx().const_uint_big(tag_llty, niche_start)); + let cast_tag = bx.intcast(relative_discr, cast_to, false); + let is_niche = bx.icmp( + IntPredicate::IntULE, + relative_discr, + bx.cx().const_uint(tag_llty, relative_max as u64), + ); + (is_niche, cast_tag, niche_variants.start().as_u32() as u128) + }; + + let tagged_discr = if delta == 0 { + tagged_discr + } else { + bx.add(tagged_discr, bx.cx().const_uint_big(cast_to, delta)) + }; + + let discr = bx.select( is_niche, - niche_discr, + tagged_discr, bx.cx().const_uint(cast_to, untagged_variant.as_u32() as u64), - ) + ); + + // In principle we could insert assumes on the possible range of `discr`, but + // currently in LLVM this seems to be a pessimization. + + discr } } } diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 1b1052fdf47a..8f5e503d659d 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -46,7 +46,7 @@ fn eval_body_using_ecx<'mir, 'tcx>( ecx.tcx.def_kind(cid.instance.def_id()) ); let layout = ecx.layout_of(body.bound_return_ty().subst(tcx, cid.instance.substs))?; - assert!(!layout.is_unsized()); + assert!(layout.is_sized()); let ret = ecx.allocate(layout, MemoryKind::Stack)?; trace!( diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index f7d64f6d4f48..ab82268dde3a 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -572,7 +572,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { metadata: &MemPlaceMeta, layout: &TyAndLayout<'tcx>, ) -> InterpResult<'tcx, Option<(Size, Align)>> { - if !layout.is_unsized() { + if layout.is_sized() { return Ok(Some((layout.size, layout.align.abi))); } match layout.ty.kind() { diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index b92a68788475..e68456a1d731 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -713,7 +713,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { rhs: &OpTy<'tcx, >::Provenance>, ) -> InterpResult<'tcx, Scalar> { let layout = self.layout_of(lhs.layout.ty.builtin_deref(true).unwrap().ty)?; - assert!(!layout.is_unsized()); + assert!(layout.is_sized()); let get_bytes = |this: &InterpCx<'mir, 'tcx, M>, op: &OpTy<'tcx, >::Provenance>, diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index e5e015c1e180..a529972db9d6 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -683,7 +683,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Use size and align of the type. let ty = self.tcx.type_of(def_id); let layout = self.tcx.layout_of(ParamEnv::empty().and(ty)).unwrap(); - assert!(!layout.is_unsized()); + assert!(layout.is_sized()); (layout.size, layout.align.abi, AllocKind::LiveData) } Some(GlobalAlloc::Memory(alloc)) => { diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index dd00678aa0ce..f0a83b7a0268 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -280,7 +280,7 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> { layout: TyAndLayout<'tcx>, cx: &impl HasDataLayout, ) -> InterpResult<'tcx, Self> { - assert!(!layout.is_unsized()); + assert!(layout.is_sized()); self.offset_with_meta(offset, MemPlaceMeta::None, layout, cx) } } @@ -376,7 +376,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Read an immediate from a place, asserting that that is possible with the given layout. /// - /// If this suceeds, the `ImmTy` is never `Uninit`. + /// If this succeeds, the `ImmTy` is never `Uninit`. #[inline(always)] pub fn read_immediate( &self, diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index b0625b5f412e..29d2312612ea 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -201,7 +201,7 @@ impl<'tcx, Prov: Provenance> MPlaceTy<'tcx, Prov> { layout: TyAndLayout<'tcx>, cx: &impl HasDataLayout, ) -> InterpResult<'tcx, Self> { - assert!(!layout.is_unsized()); + assert!(layout.is_sized()); self.offset_with_meta(offset, MemPlaceMeta::None, layout, cx) } @@ -316,8 +316,7 @@ where Ok(MPlaceTy { mplace, layout, align }) } - /// Take an operand, representing a pointer, and dereference it to a place -- that - /// will always be a MemPlace. Lives in `place.rs` because it creates a place. + /// Take an operand, representing a pointer, and dereference it to a place. #[instrument(skip(self), level = "debug")] pub fn deref_operand( &self, @@ -331,7 +330,7 @@ where } let mplace = self.ref_to_mplace(&val)?; - self.check_mplace_access(mplace, CheckInAllocMsg::DerefTest)?; + self.check_mplace(mplace)?; Ok(mplace) } @@ -340,7 +339,7 @@ where &self, place: &MPlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx, Option>> { - assert!(!place.layout.is_unsized()); + assert!(place.layout.is_sized()); assert!(!place.meta.has_meta()); let size = place.layout.size; self.get_ptr_alloc(place.ptr, size, place.align) @@ -351,24 +350,25 @@ where &mut self, place: &MPlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx, Option>> { - assert!(!place.layout.is_unsized()); + assert!(place.layout.is_sized()); assert!(!place.meta.has_meta()); let size = place.layout.size; self.get_ptr_alloc_mut(place.ptr, size, place.align) } /// Check if this mplace is dereferenceable and sufficiently aligned. - fn check_mplace_access( - &self, - mplace: MPlaceTy<'tcx, M::Provenance>, - msg: CheckInAllocMsg, - ) -> InterpResult<'tcx> { + pub fn check_mplace(&self, mplace: MPlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx> { let (size, align) = self .size_and_align_of_mplace(&mplace)? .unwrap_or((mplace.layout.size, mplace.layout.align.abi)); assert!(mplace.align <= align, "dynamic alignment less strict than static one?"); let align = M::enforce_alignment(self).then_some(align); - self.check_ptr_access_align(mplace.ptr, size, align.unwrap_or(Align::ONE), msg)?; + self.check_ptr_access_align( + mplace.ptr, + size, + align.unwrap_or(Align::ONE), + CheckInAllocMsg::DerefTest, + )?; Ok(()) } @@ -485,7 +485,7 @@ where src: Immediate, dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { - assert!(!dest.layout.is_unsized(), "Cannot write unsized data"); + assert!(dest.layout.is_sized(), "Cannot write unsized data"); trace!("write_immediate: {:?} <- {:?}: {}", *dest, src, dest.layout.ty); // See if we can avoid an allocation. This is the counterpart to `read_immediate_raw`, @@ -746,7 +746,7 @@ where layout: TyAndLayout<'tcx>, kind: MemoryKind, ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { - assert!(!layout.is_unsized()); + assert!(layout.is_sized()); let ptr = self.allocate_ptr(layout.size, layout.align.abi, kind)?; Ok(MPlaceTy::from_aligned_ptr(ptr.into(), layout)) } diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index c6e04cbfb6bf..3c286fa61bec 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -209,7 +209,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Repeat(ref operand, _) => { let src = self.eval_operand(operand, None)?; - assert!(!src.layout.is_unsized()); + assert!(src.layout.is_sized()); let dest = self.force_allocation(&dest)?; let length = dest.len(self)?; diff --git a/compiler/rustc_const_eval/src/interpret/traits.rs b/compiler/rustc_const_eval/src/interpret/traits.rs index cab23b7241ff..fa15d466ac12 100644 --- a/compiler/rustc_const_eval/src/interpret/traits.rs +++ b/compiler/rustc_const_eval/src/interpret/traits.rs @@ -53,7 +53,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ) -> InterpResult<'tcx, (Size, Align)> { let (ty, _trait_ref) = self.get_ptr_vtable(vtable)?; let layout = self.layout_of(ty)?; - assert!(!layout.is_unsized(), "there are no vtables for unsized types"); + assert!(layout.is_sized(), "there are no vtables for unsized types"); Ok((layout.size, layout.align.abi)) } } diff --git a/compiler/rustc_data_structures/src/intern.rs b/compiler/rustc_data_structures/src/intern.rs index 009b5d5340af..11cbff8ea6a8 100644 --- a/compiler/rustc_data_structures/src/intern.rs +++ b/compiler/rustc_data_structures/src/intern.rs @@ -110,11 +110,6 @@ where } } -/// A helper trait so that `Interned` things can cache stable hashes reproducibly. -pub trait InternedHashingContext { - fn with_def_path_and_no_spans(&mut self, f: impl FnOnce(&mut Self)); -} - /// A helper type that you can wrap round your own type in order to automatically /// cache the stable hash on creation and not recompute it whenever the stable hash /// of the type is computed. @@ -161,11 +156,15 @@ impl Deref for WithStableHash { impl Hash for WithStableHash { #[inline] fn hash(&self, s: &mut H) { - self.internee.hash(s) + if self.stable_hash != Fingerprint::ZERO { + self.stable_hash.hash(s) + } else { + self.internee.hash(s) + } } } -impl, CTX: InternedHashingContext> HashStable for WithStableHash { +impl, CTX> HashStable for WithStableHash { fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { if self.stable_hash == Fingerprint::ZERO || cfg!(debug_assertions) { // No cached hash available. This can only mean that incremental is disabled. @@ -176,7 +175,7 @@ impl, CTX: InternedHashingContext> HashStable for WithSt // otherwise the hashes will differ between cached and non-cached mode. let stable_hash: Fingerprint = { let mut hasher = StableHasher::new(); - hcx.with_def_path_and_no_spans(|hcx| self.internee.hash_stable(hcx, &mut hasher)); + self.internee.hash_stable(hcx, &mut hasher); hasher.finish() }; if cfg!(debug_assertions) && self.stable_hash != Fingerprint::ZERO { diff --git a/compiler/rustc_error_codes/src/error_codes/E0706.md b/compiler/rustc_error_codes/src/error_codes/E0706.md index d379b8a2384c..fabd855a222f 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0706.md +++ b/compiler/rustc_error_codes/src/error_codes/E0706.md @@ -56,4 +56,4 @@ You might be interested in visiting the [async book] for further information. [`async-trait` crate]: https://crates.io/crates/async-trait [async-is-hard]: https://smallcultfollowing.com/babysteps/blog/2019/10/26/async-fn-in-traits-are-hard/ [Generic Associated Types]: https://github.com/rust-lang/rust/issues/44265 -[async book]: https://rust-lang.github.io/async-book/07_workarounds/06_async_in_traits.html +[async book]: https://rust-lang.github.io/async-book/07_workarounds/05_async_in_traits.html diff --git a/compiler/rustc_error_messages/locales/en-US/borrowck.ftl b/compiler/rustc_error_messages/locales/en-US/borrowck.ftl index 80fc4c6e4f5d..de47ada82644 100644 --- a/compiler/rustc_error_messages/locales/en-US/borrowck.ftl +++ b/compiler/rustc_error_messages/locales/en-US/borrowck.ftl @@ -70,3 +70,56 @@ borrowck_var_borrow_by_use_place_in_closure = borrowck_var_borrow_by_use_place = borrow occurs due to use of {$place} + +borrowck_borrow_due_to_use_generator = + borrow occurs due to use in generator + +borrowck_use_due_to_use_generator = + use occurs due to use in generator + +borrowck_assign_due_to_use_generator = + assign occurs due to use in generator + +borrowck_assign_part_due_to_use_generator = + assign to part occurs due to use in generator + +borrowck_borrow_due_to_use_closure = + borrow occurs due to use in closure + +borrowck_use_due_to_use_closure = + use occurs due to use in closure + +borrowck_assign_due_to_use_closure = + assign occurs due to use in closure + +borrowck_assign_part_due_to_use_closure = + assign to part occurs due to use in closure + +borrowck_capture_immute = + capture is immutable because of use here + +borrowck_capture_mut = + capture is mutable because of use here + +borrowck_capture_move = + capture is moved because of use here + +borrowck_var_move_by_use_place_in_generator = + move occurs due to use of {$place} in generator + +borrowck_var_move_by_use_place_in_closure = + move occurs due to use of {$place} in closure + +borrowck_cannot_move_when_borrowed = + cannot move out of {$place -> + [value] value + *[other] {$place} + } because it is borrowed + .label = borrow of {$borrow_place -> + [value] value + *[other] {$borrow_place} + } occurs here + .move_label = move out of {$value_place -> + [value] value + *[other] {$value_place} + } occurs here diff --git a/compiler/rustc_error_messages/locales/en-US/codegen_ssa.ftl b/compiler/rustc_error_messages/locales/en-US/codegen_ssa.ftl index ad0d75821017..eb6b403d00e8 100644 --- a/compiler/rustc_error_messages/locales/en-US/codegen_ssa.ftl +++ b/compiler/rustc_error_messages/locales/en-US/codegen_ssa.ftl @@ -182,3 +182,5 @@ codegen_ssa_extract_bundled_libs_write_file = failed to write file '{$rlib}': {$ codegen_ssa_unsupported_arch = unsupported arch `{$arch}` for os `{$os}` codegen_ssa_apple_sdk_error_sdk_path = failed to get {$sdk_name} SDK path: {error} + +codegen_ssa_read_file = failed to read file: {message} diff --git a/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl b/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl index 74088f4dfbe7..d27edd47470e 100644 --- a/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl +++ b/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl @@ -150,3 +150,6 @@ hir_analysis_const_bound_for_non_const_trait = hir_analysis_self_in_impl_self = `Self` is not valid in the self type of an impl block .note = replace `Self` with a different type + +hir_analysis_op_trait_generic_params = + `{$method_name}` must not have any generic parameters diff --git a/compiler/rustc_error_messages/locales/en-US/infer.ftl b/compiler/rustc_error_messages/locales/en-US/infer.ftl index fa975ff2c20c..c9d83746d545 100644 --- a/compiler/rustc_error_messages/locales/en-US/infer.ftl +++ b/compiler/rustc_error_messages/locales/en-US/infer.ftl @@ -126,10 +126,10 @@ infer_data_lifetime_flow = ...but data with one lifetime flows into the other he infer_declared_multiple = this type is declared with multiple lifetimes... infer_types_declared_different = these two types are declared with different lifetimes... infer_data_flows = ...but data{$label_var1_exists -> - [true] -> {" "}from `{$label_var1}` + [true] {" "}from `{$label_var1}` *[false] -> {""} } flows{$label_var2_exists -> - [true] -> {" "}into `{$label_var2}` + [true] {" "}into `{$label_var2}` *[false] -> {""} } here diff --git a/compiler/rustc_error_messages/locales/en-US/parser.ftl b/compiler/rustc_error_messages/locales/en-US/parser.ftl index 6d42b23fb3a2..815e8f4d3567 100644 --- a/compiler/rustc_error_messages/locales/en-US/parser.ftl +++ b/compiler/rustc_error_messages/locales/en-US/parser.ftl @@ -375,3 +375,12 @@ parser_async_move_order_incorrect = the order of `move` and `async` is incorrect parser_double_colon_in_bound = expected `:` followed by trait or lifetime .suggestion = use single colon + +parser_fn_ptr_with_generics = function pointer types may not have generic parameters + .suggestion = consider moving the lifetime {$arity -> + [one] parameter + *[other] parameters + } to {$for_param_list_exists -> + [true] the + *[false] a + } `for` parameter list diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index e34acba10577..0b1b75471a66 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -30,7 +30,8 @@ use intl_memoizer::concurrent::IntlLangMemoizer; #[cfg(not(parallel_compiler))] use intl_memoizer::IntlLangMemoizer; -pub use fluent_bundle::{FluentArgs, FluentError, FluentValue}; +pub use fluent_bundle::{self, FluentArgs, FluentError, FluentValue}; + pub use unic_langid::{langid, LanguageIdentifier}; // Generates `DEFAULT_LOCALE_RESOURCES` static and `fluent_generated` module. diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 43101bbb9d31..66c986977ecc 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -64,8 +64,7 @@ impl<'source> Into> for DiagnosticArgValue<'source> { /// Trait implemented by error types. This should not be implemented manually. Instead, use /// `#[derive(Subdiagnostic)]` -- see [rustc_macros::Subdiagnostic]. -#[cfg_attr(bootstrap, rustc_diagnostic_item = "AddSubdiagnostic")] -#[cfg_attr(not(bootstrap), rustc_diagnostic_item = "AddToDiagnostic")] +#[rustc_diagnostic_item = "AddToDiagnostic"] pub trait AddToDiagnostic where Self: Sized, @@ -742,7 +741,7 @@ impl Diagnostic { &mut self, sp: Span, msg: impl Into, - suggestions: impl Iterator, + suggestions: impl IntoIterator, applicability: Applicability, ) -> &mut Self { self.span_suggestions_with_style( @@ -759,11 +758,11 @@ impl Diagnostic { &mut self, sp: Span, msg: impl Into, - suggestions: impl Iterator, + suggestions: impl IntoIterator, applicability: Applicability, style: SuggestionStyle, ) -> &mut Self { - let mut suggestions: Vec<_> = suggestions.collect(); + let mut suggestions: Vec<_> = suggestions.into_iter().collect(); suggestions.sort(); debug_assert!( @@ -790,10 +789,10 @@ impl Diagnostic { pub fn multipart_suggestions( &mut self, msg: impl Into, - suggestions: impl Iterator>, + suggestions: impl IntoIterator>, applicability: Applicability, ) -> &mut Self { - let suggestions: Vec<_> = suggestions.collect(); + let suggestions: Vec<_> = suggestions.into_iter().collect(); debug_assert!( !(suggestions .iter() diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index 730061fca993..a2ed988643ff 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -16,8 +16,7 @@ use std::thread::panicking; /// Trait implemented by error types. This should not be implemented manually. Instead, use /// `#[derive(Diagnostic)]` -- see [rustc_macros::Diagnostic]. -#[cfg_attr(bootstrap, rustc_diagnostic_item = "SessionDiagnostic")] -#[cfg_attr(not(bootstrap), rustc_diagnostic_item = "IntoDiagnostic")] +#[rustc_diagnostic_item = "IntoDiagnostic"] pub trait IntoDiagnostic<'a, T: EmissionGuarantee = ErrorGuaranteed> { /// Write out as a diagnostic out of `Handler`. #[must_use] @@ -599,13 +598,13 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { &mut self, sp: Span, msg: impl Into, - suggestions: impl Iterator, + suggestions: impl IntoIterator, applicability: Applicability, ) -> &mut Self); forward!(pub fn multipart_suggestions( &mut self, msg: impl Into, - suggestions: impl Iterator>, + suggestions: impl IntoIterator>, applicability: Applicability, ) -> &mut Self); forward!(pub fn span_suggestion_short( diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index a8fd1a17a511..170d4341ae71 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -1254,6 +1254,10 @@ impl HandlerInner { } if diagnostic.has_future_breakage() { + // Future breakages aren't emitted if they're Level::Allowed, + // but they still need to be constructed and stashed below, + // so they'll trigger the good-path bug check. + self.suppressed_expected_diag = true; self.future_breakage_diagnostics.push(diagnostic.clone()); } diff --git a/compiler/rustc_errors/src/translation.rs b/compiler/rustc_errors/src/translation.rs index a7737b467b75..a452fac07478 100644 --- a/compiler/rustc_errors/src/translation.rs +++ b/compiler/rustc_errors/src/translation.rs @@ -1,7 +1,10 @@ use crate::snippet::Style; use crate::{DiagnosticArg, DiagnosticMessage, FluentBundle}; use rustc_data_structures::sync::Lrc; -use rustc_error_messages::FluentArgs; +use rustc_error_messages::{ + fluent_bundle::resolver::errors::{ReferenceKind, ResolverError}, + FluentArgs, FluentError, +}; use std::borrow::Cow; /// Convert diagnostic arguments (a rustc internal type that exists to implement @@ -102,14 +105,31 @@ pub trait Translate { .or_else(|| translate_with_bundle(self.fallback_fluent_bundle())) .map(|(translated, errs)| { // Always bail out for errors with the fallback bundle. - assert!( - errs.is_empty(), - "identifier: {:?}, attr: {:?}, args: {:?}, errors: {:?}", - identifier, - attr, - args, - errs - ); + + let mut help_messages = vec![]; + + if !errs.is_empty() { + for error in &errs { + match error { + FluentError::ResolverError(ResolverError::Reference( + ReferenceKind::Message { id, .. }, + )) if args.iter().any(|(arg_id, _)| arg_id == id) => { + help_messages.push(format!("Argument `{id}` exists but was not referenced correctly. Try using `{{${id}}}` instead")); + } + _ => {} + } + } + + panic!( + "Encountered errors while formatting message for `{identifier}`\n\ + help: {}\n\ + attr: `{attr:?}`\n\ + args: `{args:?}`\n\ + errors: `{errs:?}`", + help_messages.join("\nhelp: ") + ); + } + translated }) .expect("failed to find message in primary or fallback fluent bundles") diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index c8de60ccb89b..1294f1e17d41 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -250,6 +250,7 @@ pub trait MultiItemModifier { span: Span, meta_item: &ast::MetaItem, item: Annotatable, + is_derive_const: bool, ) -> ExpandResult, Annotatable>; } @@ -263,6 +264,7 @@ where span: Span, meta_item: &ast::MetaItem, item: Annotatable, + _is_derive_const: bool, ) -> ExpandResult, Annotatable> { ExpandResult::Ready(self(ecx, span, meta_item, item)) } @@ -873,7 +875,7 @@ impl SyntaxExtension { /// Error type that denotes indeterminacy. pub struct Indeterminate; -pub type DeriveResolutions = Vec<(ast::Path, Annotatable, Option>)>; +pub type DeriveResolutions = Vec<(ast::Path, Annotatable, Option>, bool)>; pub trait ResolverExpand { fn next_node_id(&mut self) -> NodeId; diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 57713fb3cd61..3d37e2c65685 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -337,6 +337,7 @@ pub enum InvocationKind { }, Derive { path: ast::Path, + is_const: bool, item: Annotatable, }, } @@ -478,13 +479,13 @@ impl<'a, 'b> MacroExpander<'a, 'b> { derive_invocations.reserve(derives.len()); derives .into_iter() - .map(|(path, item, _exts)| { + .map(|(path, item, _exts, is_const)| { // FIXME: Consider using the derive resolutions (`_exts`) // instead of enqueuing the derives to be resolved again later. let expn_id = LocalExpnId::fresh_empty(); derive_invocations.push(( Invocation { - kind: InvocationKind::Derive { path, item }, + kind: InvocationKind::Derive { path, item, is_const }, fragment_kind, expansion_data: ExpansionData { id: expn_id, @@ -717,7 +718,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { SyntaxExtensionKind::LegacyAttr(expander) => { match validate_attr::parse_meta(&self.cx.sess.parse_sess, &attr) { Ok(meta) => { - let items = match expander.expand(self.cx, span, &meta, item) { + let items = match expander.expand(self.cx, span, &meta, item, false) { ExpandResult::Ready(items) => items, ExpandResult::Retry(item) => { // Reassemble the original invocation for retrying. @@ -749,19 +750,19 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } _ => unreachable!(), }, - InvocationKind::Derive { path, item } => match ext { + InvocationKind::Derive { path, item, is_const } => match ext { SyntaxExtensionKind::Derive(expander) | SyntaxExtensionKind::LegacyDerive(expander) => { if let SyntaxExtensionKind::Derive(..) = ext { self.gate_proc_macro_input(&item); } let meta = ast::MetaItem { kind: MetaItemKind::Word, span, path }; - let items = match expander.expand(self.cx, span, &meta, item) { + let items = match expander.expand(self.cx, span, &meta, item, is_const) { ExpandResult::Ready(items) => items, ExpandResult::Retry(item) => { // Reassemble the original invocation for retrying. return ExpandResult::Retry(Invocation { - kind: InvocationKind::Derive { path: meta.path, item }, + kind: InvocationKind::Derive { path: meta.path, item, is_const }, ..invoc }); } diff --git a/compiler/rustc_expand/src/mbe.rs b/compiler/rustc_expand/src/mbe.rs index f42576b16f52..63bafd7b046f 100644 --- a/compiler/rustc_expand/src/mbe.rs +++ b/compiler/rustc_expand/src/mbe.rs @@ -52,7 +52,7 @@ impl KleeneToken { /// A Kleene-style [repetition operator](https://en.wikipedia.org/wiki/Kleene_star) /// for token sequences. #[derive(Clone, PartialEq, Encodable, Decodable, Debug, Copy)] -enum KleeneOp { +pub(crate) enum KleeneOp { /// Kleene star (`*`) for zero or more repetitions ZeroOrMore, /// Kleene plus (`+`) for one or more repetitions diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index c8bdc39311c6..95cec8d7ae29 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -73,17 +73,17 @@ pub(crate) use NamedMatch::*; pub(crate) use ParseResult::*; -use crate::mbe::{KleeneOp, TokenTree}; +use crate::mbe::{macro_rules::Tracker, KleeneOp, TokenTree}; use rustc_ast::token::{self, DocComment, Nonterminal, NonterminalKind, Token}; -use rustc_lint_defs::pluralize; -use rustc_parse::parser::{NtOrTt, Parser}; -use rustc_span::symbol::MacroRulesNormalizedIdent; -use rustc_span::Span; - use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lrc; +use rustc_errors::ErrorGuaranteed; +use rustc_lint_defs::pluralize; +use rustc_parse::parser::{NtOrTt, Parser}; use rustc_span::symbol::Ident; +use rustc_span::symbol::MacroRulesNormalizedIdent; +use rustc_span::Span; use std::borrow::Cow; use std::collections::hash_map::Entry::{Occupied, Vacant}; @@ -96,7 +96,8 @@ use std::collections::hash_map::Entry::{Occupied, Vacant}; /// /// This means a matcher can be represented by `&[MatcherLoc]`, and traversal mostly involves /// simply incrementing the current matcher position index by one. -pub(super) enum MatcherLoc { +#[derive(Debug)] +pub(crate) enum MatcherLoc { Token { token: Token, }, @@ -270,13 +271,17 @@ pub(crate) enum ParseResult { Failure(Token, &'static str), /// Fatal error (malformed macro?). Abort compilation. Error(rustc_span::Span, String), - ErrorReported, + ErrorReported(ErrorGuaranteed), } /// A `ParseResult` where the `Success` variant contains a mapping of /// `MacroRulesNormalizedIdent`s to `NamedMatch`es. This represents the mapping /// of metavars to the token trees they bind to. -pub(crate) type NamedParseResult = ParseResult>; +pub(crate) type NamedParseResult = ParseResult; + +/// Contains a mapping of `MacroRulesNormalizedIdent`s to `NamedMatch`es. +/// This represents the mapping of metavars to the token trees they bind to. +pub(crate) type NamedMatches = FxHashMap; /// Count how many metavars declarations are in `matcher`. pub(super) fn count_metavar_decls(matcher: &[TokenTree]) -> usize { @@ -400,17 +405,21 @@ impl TtParser { /// /// `Some(result)` if everything is finished, `None` otherwise. Note that matches are kept /// track of through the mps generated. - fn parse_tt_inner( + fn parse_tt_inner<'matcher, T: Tracker<'matcher>>( &mut self, - matcher: &[MatcherLoc], + matcher: &'matcher [MatcherLoc], token: &Token, + track: &mut T, ) -> Option { // Matcher positions that would be valid if the macro invocation was over now. Only // modified if `token == Eof`. let mut eof_mps = EofMatcherPositions::None; while let Some(mut mp) = self.cur_mps.pop() { - match &matcher[mp.idx] { + let matcher_loc = &matcher[mp.idx]; + track.before_match_loc(self, matcher_loc); + + match matcher_loc { MatcherLoc::Token { token: t } => { // If it's a doc comment, we just ignore it and move on to the next tt in the // matcher. This is a bug, but #95267 showed that existing programs rely on @@ -450,7 +459,7 @@ impl TtParser { // Try zero matches of this sequence, by skipping over it. self.cur_mps.push(MatcherPos { idx: idx_first_after, - matches: mp.matches.clone(), // a cheap clone + matches: Lrc::clone(&mp.matches), }); } @@ -463,8 +472,8 @@ impl TtParser { // sequence. If that's not possible, `ending_mp` will fail quietly when it is // processed next time around the loop. let ending_mp = MatcherPos { - idx: mp.idx + 1, // +1 skips the Kleene op - matches: mp.matches.clone(), // a cheap clone + idx: mp.idx + 1, // +1 skips the Kleene op + matches: Lrc::clone(&mp.matches), }; self.cur_mps.push(ending_mp); @@ -479,8 +488,8 @@ impl TtParser { // separator yet. Try ending the sequence. If that's not possible, `ending_mp` // will fail quietly when it is processed next time around the loop. let ending_mp = MatcherPos { - idx: mp.idx + 2, // +2 skips the separator and the Kleene op - matches: mp.matches.clone(), // a cheap clone + idx: mp.idx + 2, // +2 skips the separator and the Kleene op + matches: Lrc::clone(&mp.matches), }; self.cur_mps.push(ending_mp); @@ -552,10 +561,11 @@ impl TtParser { } /// Match the token stream from `parser` against `matcher`. - pub(super) fn parse_tt( + pub(super) fn parse_tt<'matcher, T: Tracker<'matcher>>( &mut self, parser: &mut Cow<'_, Parser<'_>>, - matcher: &[MatcherLoc], + matcher: &'matcher [MatcherLoc], + track: &mut T, ) -> NamedParseResult { // A queue of possible matcher positions. We initialize it with the matcher position in // which the "dot" is before the first token of the first token tree in `matcher`. @@ -571,7 +581,8 @@ impl TtParser { // Process `cur_mps` until either we have finished the input or we need to get some // parsing from the black-box parser done. - if let Some(res) = self.parse_tt_inner(matcher, &parser.token) { + let res = self.parse_tt_inner(matcher, &parser.token, track); + if let Some(res) = res { return res; } @@ -612,14 +623,14 @@ impl TtParser { // edition-specific matching behavior for non-terminals. let nt = match parser.to_mut().parse_nonterminal(kind) { Err(mut err) => { - err.span_label( + let guarantee = err.span_label( span, format!( "while parsing argument for this `{kind}` macro fragment" ), ) .emit(); - return ErrorReported; + return ErrorReported(guarantee); } Ok(nt) => nt, }; diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index f6fe38174f7c..5e17d8a021e9 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -14,7 +14,9 @@ use rustc_ast::{NodeId, DUMMY_NODE_ID}; use rustc_ast_pretty::pprust; use rustc_attr::{self as attr, TransparencyError}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; -use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, DiagnosticMessage}; +use rustc_errors::{ + Applicability, Diagnostic, DiagnosticBuilder, DiagnosticMessage, ErrorGuaranteed, +}; use rustc_feature::Features; use rustc_lint_defs::builtin::{ RUST_2021_INCOMPATIBLE_OR_PATTERNS, SEMICOLON_IN_EXPRESSIONS_FROM_MACROS, @@ -33,6 +35,8 @@ use std::borrow::Cow; use std::collections::hash_map::Entry; use std::{mem, slice}; +use super::macro_parser::{NamedMatches, NamedParseResult}; + pub(crate) struct ParserAnyMacro<'a> { parser: Parser<'a>, @@ -205,8 +209,32 @@ fn trace_macros_note(cx_expansions: &mut FxIndexMap>, sp: Span cx_expansions.entry(sp).or_default().push(message); } +pub(super) trait Tracker<'matcher> { + /// This is called before trying to match next MatcherLoc on the current token. + fn before_match_loc(&mut self, parser: &TtParser, matcher: &'matcher MatcherLoc); + + /// This is called after an arm has been parsed, either successfully or unsuccessfully. When this is called, + /// `before_match_loc` was called at least once (with a `MatcherLoc::Eof`). + fn after_arm(&mut self, result: &NamedParseResult); + + /// For tracing. + fn description() -> &'static str; +} + +/// A noop tracker that is used in the hot path of the expansion, has zero overhead thanks to monomorphization. +struct NoopTracker; + +impl<'matcher> Tracker<'matcher> for NoopTracker { + fn before_match_loc(&mut self, _: &TtParser, _: &'matcher MatcherLoc) {} + fn after_arm(&mut self, _: &NamedParseResult) {} + fn description() -> &'static str { + "none" + } +} + /// Expands the rules based macro defined by `lhses` and `rhses` for a given /// input `arg`. +#[instrument(skip(cx, transparency, arg, lhses, rhses))] fn expand_macro<'cx>( cx: &'cx mut ExtCtxt<'_>, sp: Span, @@ -228,9 +256,188 @@ fn expand_macro<'cx>( trace_macros_note(&mut cx.expansions, sp, msg); } - // Which arm's failure should we report? (the one furthest along) - let mut best_failure: Option<(Token, &str)> = None; + // Track nothing for the best performance. + let try_success_result = try_match_macro(sess, name, &arg, lhses, &mut NoopTracker); + match try_success_result { + Ok((i, named_matches)) => { + let (rhs, rhs_span): (&mbe::Delimited, DelimSpan) = match &rhses[i] { + mbe::TokenTree::Delimited(span, delimited) => (&delimited, *span), + _ => cx.span_bug(sp, "malformed macro rhs"), + }; + let arm_span = rhses[i].span(); + + let rhs_spans = rhs.tts.iter().map(|t| t.span()).collect::>(); + // rhs has holes ( `$id` and `$(...)` that need filled) + let mut tts = match transcribe(cx, &named_matches, &rhs, rhs_span, transparency) { + Ok(tts) => tts, + Err(mut err) => { + err.emit(); + return DummyResult::any(arm_span); + } + }; + + // Replace all the tokens for the corresponding positions in the macro, to maintain + // proper positions in error reporting, while maintaining the macro_backtrace. + if rhs_spans.len() == tts.len() { + tts = tts.map_enumerated(|i, tt| { + let mut tt = tt.clone(); + let mut sp = rhs_spans[i]; + sp = sp.with_ctxt(tt.span().ctxt()); + tt.set_span(sp); + tt + }); + } + + if cx.trace_macros() { + let msg = format!("to `{}`", pprust::tts_to_string(&tts)); + trace_macros_note(&mut cx.expansions, sp, msg); + } + + let mut p = Parser::new(sess, tts, false, None); + p.last_type_ascription = cx.current_expansion.prior_type_ascription; + + if is_local { + cx.resolver.record_macro_rule_usage(node_id, i); + } + + // Let the context choose how to interpret the result. + // Weird, but useful for X-macros. + return Box::new(ParserAnyMacro { + parser: p, + + // Pass along the original expansion site and the name of the macro + // so we can print a useful error message if the parse of the expanded + // macro leaves unparsed tokens. + site_span: sp, + macro_ident: name, + lint_node_id: cx.current_expansion.lint_node_id, + is_trailing_mac: cx.current_expansion.is_trailing_mac, + arm_span, + is_local, + }); + } + Err(CanRetry::No(_)) => { + debug!("Will not retry matching as an error was emitted already"); + return DummyResult::any(sp); + } + Err(CanRetry::Yes) => { + // Retry and emit a better error below. + } + } + + // An error occurred, try the expansion again, tracking the expansion closely for better diagnostics. + let mut tracker = CollectTrackerAndEmitter::new(cx, sp); + + let try_success_result = try_match_macro(sess, name, &arg, lhses, &mut tracker); + assert!(try_success_result.is_err(), "Macro matching returned a success on the second try"); + + if let Some(result) = tracker.result { + // An irrecoverable error occurred and has been emitted. + return result; + } + + let Some((token, label)) = tracker.best_failure else { + return tracker.result.expect("must have encountered Error or ErrorReported"); + }; + + let span = token.span.substitute_dummy(sp); + + let mut err = cx.struct_span_err(span, &parse_failure_msg(&token)); + err.span_label(span, label); + if !def_span.is_dummy() && !cx.source_map().is_imported(def_span) { + err.span_label(cx.source_map().guess_head_span(def_span), "when calling this macro"); + } + + annotate_doc_comment(&mut err, sess.source_map(), span); + + // Check whether there's a missing comma in this macro call, like `println!("{}" a);` + if let Some((arg, comma_span)) = arg.add_comma() { + for lhs in lhses { + let parser = parser_from_cx(sess, arg.clone()); + let mut tt_parser = TtParser::new(name); + + if let Success(_) = + tt_parser.parse_tt(&mut Cow::Borrowed(&parser), lhs, &mut NoopTracker) + { + if comma_span.is_dummy() { + err.note("you might be missing a comma"); + } else { + err.span_suggestion_short( + comma_span, + "missing comma here", + ", ", + Applicability::MachineApplicable, + ); + } + } + } + } + err.emit(); + cx.trace_macros_diag(); + DummyResult::any(sp) +} + +/// The tracker used for the slow error path that collects useful info for diagnostics. +struct CollectTrackerAndEmitter<'a, 'cx> { + cx: &'a mut ExtCtxt<'cx>, + /// Which arm's failure should we report? (the one furthest along) + best_failure: Option<(Token, &'static str)>, + root_span: Span, + result: Option>, +} + +impl<'a, 'cx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'a, 'cx> { + fn before_match_loc(&mut self, _parser: &TtParser, _matcher: &'matcher MatcherLoc) { + // Empty for now. + } + + fn after_arm(&mut self, result: &NamedParseResult) { + match result { + Success(_) => { + unreachable!("should not collect detailed info for successful macro match"); + } + Failure(token, msg) => match self.best_failure { + Some((ref best_token, _)) if best_token.span.lo() >= token.span.lo() => {} + _ => self.best_failure = Some((token.clone(), msg)), + }, + Error(err_sp, msg) => { + let span = err_sp.substitute_dummy(self.root_span); + self.cx.struct_span_err(span, msg).emit(); + self.result = Some(DummyResult::any(span)); + } + ErrorReported(_) => self.result = Some(DummyResult::any(self.root_span)), + } + } + + fn description() -> &'static str { + "detailed" + } +} + +impl<'a, 'cx> CollectTrackerAndEmitter<'a, 'cx> { + fn new(cx: &'a mut ExtCtxt<'cx>, root_span: Span) -> Self { + Self { cx, best_failure: None, root_span, result: None } + } +} + +enum CanRetry { + Yes, + /// We are not allowed to retry macro expansion as a fatal error has been emitted already. + No(ErrorGuaranteed), +} + +/// Try expanding the macro. Returns the index of the successful arm and its named_matches if it was successful, +/// and nothing if it failed. On failure, it's the callers job to use `track` accordingly to record all errors +/// correctly. +#[instrument(level = "debug", skip(sess, arg, lhses, track), fields(tracking = %T::description()))] +fn try_match_macro<'matcher, T: Tracker<'matcher>>( + sess: &ParseSess, + name: Ident, + arg: &TokenStream, + lhses: &'matcher [Vec], + track: &mut T, +) -> Result<(usize, NamedMatches), CanRetry> { // We create a base parser that can be used for the "black box" parts. // Every iteration needs a fresh copy of that parser. However, the parser // is not mutated on many of the iterations, particularly when dealing with @@ -252,125 +459,52 @@ fn expand_macro<'cx>( // this situation.) // FIXME(Nilstrieb): Stop recovery from happening on this parser and retry later with recovery if the macro failed to match. let parser = parser_from_cx(sess, arg.clone()); - // Try each arm's matchers. let mut tt_parser = TtParser::new(name); for (i, lhs) in lhses.iter().enumerate() { + let _tracing_span = trace_span!("Matching arm", %i); + // Take a snapshot of the state of pre-expansion gating at this point. // This is used so that if a matcher is not `Success(..)`ful, // then the spans which became gated when parsing the unsuccessful matcher // are not recorded. On the first `Success(..)`ful matcher, the spans are merged. let mut gated_spans_snapshot = mem::take(&mut *sess.gated_spans.spans.borrow_mut()); - match tt_parser.parse_tt(&mut Cow::Borrowed(&parser), lhs) { + let result = tt_parser.parse_tt(&mut Cow::Borrowed(&parser), lhs, track); + + track.after_arm(&result); + + match result { Success(named_matches) => { + debug!("Parsed arm successfully"); // The matcher was `Success(..)`ful. // Merge the gated spans from parsing the matcher with the pre-existing ones. sess.gated_spans.merge(gated_spans_snapshot); - let (rhs, rhs_span): (&mbe::Delimited, DelimSpan) = match &rhses[i] { - mbe::TokenTree::Delimited(span, delimited) => (&delimited, *span), - _ => cx.span_bug(sp, "malformed macro rhs"), - }; - let arm_span = rhses[i].span(); - - let rhs_spans = rhs.tts.iter().map(|t| t.span()).collect::>(); - // rhs has holes ( `$id` and `$(...)` that need filled) - let mut tts = match transcribe(cx, &named_matches, &rhs, rhs_span, transparency) { - Ok(tts) => tts, - Err(mut err) => { - err.emit(); - return DummyResult::any(arm_span); - } - }; - - // Replace all the tokens for the corresponding positions in the macro, to maintain - // proper positions in error reporting, while maintaining the macro_backtrace. - if rhs_spans.len() == tts.len() { - tts = tts.map_enumerated(|i, tt| { - let mut tt = tt.clone(); - let mut sp = rhs_spans[i]; - sp = sp.with_ctxt(tt.span().ctxt()); - tt.set_span(sp); - tt - }); - } - - if cx.trace_macros() { - let msg = format!("to `{}`", pprust::tts_to_string(&tts)); - trace_macros_note(&mut cx.expansions, sp, msg); - } - - let mut p = Parser::new(sess, tts, false, None); - p.last_type_ascription = cx.current_expansion.prior_type_ascription; - - if is_local { - cx.resolver.record_macro_rule_usage(node_id, i); - } - - // Let the context choose how to interpret the result. - // Weird, but useful for X-macros. - return Box::new(ParserAnyMacro { - parser: p, - - // Pass along the original expansion site and the name of the macro - // so we can print a useful error message if the parse of the expanded - // macro leaves unparsed tokens. - site_span: sp, - macro_ident: name, - lint_node_id: cx.current_expansion.lint_node_id, - is_trailing_mac: cx.current_expansion.is_trailing_mac, - arm_span, - is_local, - }); + return Ok((i, named_matches)); } - Failure(token, msg) => match best_failure { - Some((ref best_token, _)) if best_token.span.lo() >= token.span.lo() => {} - _ => best_failure = Some((token, msg)), - }, - Error(err_sp, ref msg) => { - let span = err_sp.substitute_dummy(sp); - cx.struct_span_err(span, &msg).emit(); - return DummyResult::any(span); + Failure(_, _) => { + trace!("Failed to match arm, trying the next one"); + // Try the next arm. + } + Error(_, _) => { + debug!("Fatal error occurred during matching"); + // We haven't emitted an error yet, so we can retry. + return Err(CanRetry::Yes); + } + ErrorReported(guarantee) => { + debug!("Fatal error occurred and was reported during matching"); + // An error has been reported already, we cannot retry as that would cause duplicate errors. + return Err(CanRetry::No(guarantee)); } - ErrorReported => return DummyResult::any(sp), } // The matcher was not `Success(..)`ful. // Restore to the state before snapshotting and maybe try again. mem::swap(&mut gated_spans_snapshot, &mut sess.gated_spans.spans.borrow_mut()); } - drop(parser); - let (token, label) = best_failure.expect("ran no matchers"); - let span = token.span.substitute_dummy(sp); - let mut err = cx.struct_span_err(span, &parse_failure_msg(&token)); - err.span_label(span, label); - if !def_span.is_dummy() && !cx.source_map().is_imported(def_span) { - err.span_label(cx.source_map().guess_head_span(def_span), "when calling this macro"); - } - annotate_doc_comment(&mut err, sess.source_map(), span); - // Check whether there's a missing comma in this macro call, like `println!("{}" a);` - if let Some((arg, comma_span)) = arg.add_comma() { - for lhs in lhses { - let parser = parser_from_cx(sess, arg.clone()); - if let Success(_) = tt_parser.parse_tt(&mut Cow::Borrowed(&parser), lhs) { - if comma_span.is_dummy() { - err.note("you might be missing a comma"); - } else { - err.span_suggestion_short( - comma_span, - "missing comma here", - ", ", - Applicability::MachineApplicable, - ); - } - } - } - } - err.emit(); - cx.trace_macros_diag(); - DummyResult::any(sp) + Err(CanRetry::Yes) } // Note that macro-by-example's input is also matched against a token tree: @@ -452,28 +586,29 @@ pub fn compile_declarative_macro( let parser = Parser::new(&sess.parse_sess, body, true, rustc_parse::MACRO_ARGUMENTS); let mut tt_parser = TtParser::new(Ident::with_dummy_span(if macro_rules { kw::MacroRules } else { kw::Macro })); - let argument_map = match tt_parser.parse_tt(&mut Cow::Borrowed(&parser), &argument_gram) { - Success(m) => m, - Failure(token, msg) => { - let s = parse_failure_msg(&token); - let sp = token.span.substitute_dummy(def.span); - let mut err = sess.parse_sess.span_diagnostic.struct_span_err(sp, &s); - err.span_label(sp, msg); - annotate_doc_comment(&mut err, sess.source_map(), sp); - err.emit(); - return dummy_syn_ext(); - } - Error(sp, msg) => { - sess.parse_sess - .span_diagnostic - .struct_span_err(sp.substitute_dummy(def.span), &msg) - .emit(); - return dummy_syn_ext(); - } - ErrorReported => { - return dummy_syn_ext(); - } - }; + let argument_map = + match tt_parser.parse_tt(&mut Cow::Owned(parser), &argument_gram, &mut NoopTracker) { + Success(m) => m, + Failure(token, msg) => { + let s = parse_failure_msg(&token); + let sp = token.span.substitute_dummy(def.span); + let mut err = sess.parse_sess.span_diagnostic.struct_span_err(sp, &s); + err.span_label(sp, msg); + annotate_doc_comment(&mut err, sess.source_map(), sp); + err.emit(); + return dummy_syn_ext(); + } + Error(sp, msg) => { + sess.parse_sess + .span_diagnostic + .struct_span_err(sp.substitute_dummy(def.span), &msg) + .emit(); + return dummy_syn_ext(); + } + ErrorReported(_) => { + return dummy_syn_ext(); + } + }; let mut valid = true; diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs index 1a2ab9d190eb..e9a691920689 100644 --- a/compiler/rustc_expand/src/proc_macro.rs +++ b/compiler/rustc_expand/src/proc_macro.rs @@ -112,6 +112,7 @@ impl MultiItemModifier for DeriveProcMacro { span: Span, _meta_item: &ast::MetaItem, item: Annotatable, + _is_derive_const: bool, ) -> ExpandResult, Annotatable> { // We need special handling for statement items // (e.g. `fn foo() { #[derive(Debug)] struct Bar; }`) diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index cc2858d3f73a..a929f6cb0a5d 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -525,6 +525,13 @@ impl server::TokenStream for Rustc<'_, '_> { ast::ExprKind::Lit(l) => { Ok(tokenstream::TokenStream::token_alone(token::Literal(l.token_lit), l.span)) } + ast::ExprKind::IncludedBytes(bytes) => { + let lit = ast::Lit::from_included_bytes(bytes, expr.span); + Ok(tokenstream::TokenStream::token_alone( + token::TokenKind::Literal(lit.token_lit), + expr.span, + )) + } ast::ExprKind::Unary(ast::UnOp::Neg, e) => match &e.kind { ast::ExprKind::Lit(l) => match l.token_lit { token::Lit { kind: token::Integer | token::Float, .. } => { diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index c2aa096a9931..1646727a1c85 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -54,9 +54,9 @@ declare_features! ( /// Allows using ADX intrinsics from `core::arch::{x86, x86_64}`. (accepted, adx_target_feature, "1.61.0", Some(44839), None), /// Allows explicit discriminants on non-unit enum variants. - (accepted, arbitrary_enum_discriminant, "CURRENT_RUSTC_VERSION", Some(60553), None), + (accepted, arbitrary_enum_discriminant, "1.66.0", Some(60553), None), /// Allows using `sym` operands in inline assembly. - (accepted, asm_sym, "CURRENT_RUSTC_VERSION", Some(93333), None), + (accepted, asm_sym, "1.66.0", Some(93333), None), /// Allows the definition of associated constants in `trait` or `impl` blocks. (accepted, associated_consts, "1.20.0", Some(29646), None), /// Allows using associated `type`s in `trait`s. @@ -174,7 +174,7 @@ declare_features! ( // FIXME: explain `globs`. (accepted, globs, "1.0.0", None, None), /// Allows using `..=X` as a pattern. - (accepted, half_open_range_patterns, "CURRENT_RUSTC_VERSION", Some(67264), None), + (accepted, half_open_range_patterns, "1.66.0", Some(67264), None), /// Allows using the `u128` and `i128` types. (accepted, i128_type, "1.26.0", Some(35118), None), /// Allows the use of `if let` expressions. diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index e94e038f9283..5cf2fdde3925 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -311,7 +311,7 @@ declare_features! ( /// Allows `async || body` closures. (active, async_closure, "1.37.0", Some(62290), None), /// Alows async functions to be declared, implemented, and used in traits. - (incomplete, async_fn_in_trait, "CURRENT_RUSTC_VERSION", Some(91611), None), + (incomplete, async_fn_in_trait, "1.66.0", Some(91611), None), /// Allows `extern "C-unwind" fn` to enable unwinding across ABI boundaries. (active, c_unwind, "1.52.0", Some(74990), None), /// Allows using C-variadics. @@ -414,7 +414,7 @@ declare_features! ( /// Allows non-trivial generic constants which have to have wfness manually propagated to callers (incomplete, generic_const_exprs, "1.56.0", Some(76560), None), /// Allows using `..=X` as a patterns in slices. - (active, half_open_range_patterns_in_slices, "CURRENT_RUSTC_VERSION", Some(67264), None), + (active, half_open_range_patterns_in_slices, "1.66.0", Some(67264), None), /// Allows `if let` guard in match arms. (active, if_let_guard, "1.47.0", Some(51114), None), /// Allows `impl Trait` as output type in `Fn` traits in return position of functions. @@ -512,9 +512,9 @@ declare_features! ( (active, thread_local, "1.0.0", Some(29594), None), /// Allows defining `trait X = A + B;` alias items. (active, trait_alias, "1.24.0", Some(41517), None), - /// Allows upcasting trait objects via supertraits. - /// Trait upcasting is casting, e.g., `dyn Foo -> dyn Bar` where `Foo: Bar`. - (incomplete, trait_upcasting, "1.56.0", Some(65991), None), + /// Allows dyn upcasting trait objects via supertraits. + /// Dyn upcasting is casting, e.g., `dyn Foo -> dyn Bar` where `Foo: Bar`. + (active, trait_upcasting, "1.56.0", Some(65991), None), /// Allows #[repr(transparent)] on unions (RFC 2645). (active, transparent_unions, "1.37.0", Some(60405), None), /// Allows inconsistent bounds in where clauses. diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index dc3a74956843..01477265f617 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -147,7 +147,7 @@ pub enum AttributeDuplicates { FutureWarnPreceding, } -/// A conveniece macro to deal with `$($expr)?`. +/// A convenience macro to deal with `$($expr)?`. macro_rules! or_default { ($default:expr,) => { $default diff --git a/compiler/rustc_hir_analysis/src/check/compare_method.rs b/compiler/rustc_hir_analysis/src/check/compare_method.rs index 04bf7c83b320..7c99896b4571 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_method.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_method.rs @@ -9,14 +9,15 @@ use rustc_hir::intravisit; use rustc_hir::{GenericParamKind, ImplItemKind, TraitItemKind}; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::infer::{self, TyCtxtInferExt}; +use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::util; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::util::ExplicitSelf; -use rustc_middle::ty::InternalSubsts; use rustc_middle::ty::{ - self, AssocItem, DefIdTree, Ty, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable, + self, AssocItem, DefIdTree, TraitRef, Ty, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeVisitable, }; +use rustc_middle::ty::{FnSig, InternalSubsts}; use rustc_middle::ty::{GenericParamDefKind, ToPredicate, TyCtxt}; use rustc_span::Span; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; @@ -303,102 +304,19 @@ fn compare_predicate_entailment<'tcx>( } if let Err(terr) = result { - debug!("sub_types failed: impl ty {:?}, trait ty {:?}", impl_fty, trait_fty); + debug!(?terr, "sub_types failed: impl ty {:?}, trait ty {:?}", impl_fty, trait_fty); - let (impl_err_span, trait_err_span) = - extract_spans_for_error_reporting(&infcx, terr, &cause, impl_m, trait_m); - - cause.span = impl_err_span; - - let mut diag = struct_span_err!( - tcx.sess, - cause.span(), - E0053, - "method `{}` has an incompatible type for trait", - trait_m.name - ); - match &terr { - TypeError::ArgumentMutability(0) | TypeError::ArgumentSorts(_, 0) - if trait_m.fn_has_self_parameter => - { - let ty = trait_sig.inputs()[0]; - let sugg = match ExplicitSelf::determine(ty, |_| ty == impl_trait_ref.self_ty()) { - ExplicitSelf::ByValue => "self".to_owned(), - ExplicitSelf::ByReference(_, hir::Mutability::Not) => "&self".to_owned(), - ExplicitSelf::ByReference(_, hir::Mutability::Mut) => "&mut self".to_owned(), - _ => format!("self: {ty}"), - }; - - // When the `impl` receiver is an arbitrary self type, like `self: Box`, the - // span points only at the type `Box, but we want to cover the whole - // argument pattern and type. - let span = match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind { - ImplItemKind::Fn(ref sig, body) => tcx - .hir() - .body_param_names(body) - .zip(sig.decl.inputs.iter()) - .map(|(param, ty)| param.span.to(ty.span)) - .next() - .unwrap_or(impl_err_span), - _ => bug!("{:?} is not a method", impl_m), - }; - - diag.span_suggestion( - span, - "change the self-receiver type to match the trait", - sugg, - Applicability::MachineApplicable, - ); - } - TypeError::ArgumentMutability(i) | TypeError::ArgumentSorts(_, i) => { - if trait_sig.inputs().len() == *i { - // Suggestion to change output type. We do not suggest in `async` functions - // to avoid complex logic or incorrect output. - match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind { - ImplItemKind::Fn(ref sig, _) - if sig.header.asyncness == hir::IsAsync::NotAsync => - { - let msg = "change the output type to match the trait"; - let ap = Applicability::MachineApplicable; - match sig.decl.output { - hir::FnRetTy::DefaultReturn(sp) => { - let sugg = format!("-> {} ", trait_sig.output()); - diag.span_suggestion_verbose(sp, msg, sugg, ap); - } - hir::FnRetTy::Return(hir_ty) => { - let sugg = trait_sig.output(); - diag.span_suggestion(hir_ty.span, msg, sugg, ap); - } - }; - } - _ => {} - }; - } else if let Some(trait_ty) = trait_sig.inputs().get(*i) { - diag.span_suggestion( - impl_err_span, - "change the parameter type to match the trait", - trait_ty, - Applicability::MachineApplicable, - ); - } - } - _ => {} - } - - infcx.err_ctxt().note_type_err( - &mut diag, - &cause, - trait_err_span.map(|sp| (sp, "type in trait".to_owned())), - Some(infer::ValuePairs::Terms(ExpectedFound { - expected: trait_fty.into(), - found: impl_fty.into(), - })), + let emitted = report_trait_method_mismatch( + tcx, + &mut cause, + &infcx, terr, - false, - false, + (trait_m, trait_fty), + (impl_m, impl_fty), + &trait_sig, + &impl_trait_ref, ); - - return Err(diag.emit()); + return Err(emitted); } // Check that all obligations are satisfied by the implementation's @@ -424,6 +342,7 @@ fn compare_predicate_entailment<'tcx>( Ok(()) } +#[instrument(skip(tcx), level = "debug", ret)] pub fn collect_trait_impl_trait_tys<'tcx>( tcx: TyCtxt<'tcx>, def_id: DefId, @@ -437,7 +356,7 @@ pub fn collect_trait_impl_trait_tys<'tcx>( let impl_m_hir_id = tcx.hir().local_def_id_to_hir_id(impl_m.def_id.expect_local()); let return_span = tcx.hir().fn_decl_by_hir_id(impl_m_hir_id).unwrap().output.span(); - let cause = ObligationCause::new( + let mut cause = ObligationCause::new( return_span, impl_m_hir_id, ObligationCauseCode::CompareImplItemObligation { @@ -514,23 +433,35 @@ pub fn collect_trait_impl_trait_tys<'tcx>( } } + debug!(?trait_sig, ?impl_sig, "equating function signatures"); + + let trait_fty = tcx.mk_fn_ptr(ty::Binder::dummy(trait_sig)); + let impl_fty = tcx.mk_fn_ptr(ty::Binder::dummy(impl_sig)); + // Unify the whole function signature. We need to do this to fully infer // the lifetimes of the return type, but do this after unifying just the // return types, since we want to avoid duplicating errors from // `compare_predicate_entailment`. - match infcx - .at(&cause, param_env) - .eq(tcx.mk_fn_ptr(ty::Binder::dummy(trait_sig)), tcx.mk_fn_ptr(ty::Binder::dummy(impl_sig))) - { + match infcx.at(&cause, param_env).eq(trait_fty, impl_fty) { Ok(infer::InferOk { value: (), obligations }) => { ocx.register_obligations(obligations); } Err(terr) => { - let guar = tcx.sess.delay_span_bug( - return_span, - format!("could not unify `{trait_sig}` and `{impl_sig}`: {terr:?}"), + // This function gets called during `compare_predicate_entailment` when normalizing a + // signature that contains RPITIT. When the method signatures don't match, we have to + // emit an error now because `compare_predicate_entailment` will not report the error + // when normalization fails. + let emitted = report_trait_method_mismatch( + tcx, + &mut cause, + infcx, + terr, + (trait_m, trait_fty), + (impl_m, impl_fty), + &trait_sig, + &impl_trait_ref, ); - return Err(guar); + return Err(emitted); } } @@ -690,6 +621,112 @@ impl<'tcx> TypeFolder<'tcx> for ImplTraitInTraitCollector<'_, 'tcx> { } } +fn report_trait_method_mismatch<'tcx>( + tcx: TyCtxt<'tcx>, + cause: &mut ObligationCause<'tcx>, + infcx: &InferCtxt<'tcx>, + terr: TypeError<'tcx>, + (trait_m, trait_fty): (&AssocItem, Ty<'tcx>), + (impl_m, impl_fty): (&AssocItem, Ty<'tcx>), + trait_sig: &FnSig<'tcx>, + impl_trait_ref: &TraitRef<'tcx>, +) -> ErrorGuaranteed { + let (impl_err_span, trait_err_span) = + extract_spans_for_error_reporting(&infcx, terr, &cause, impl_m, trait_m); + + cause.span = impl_err_span; + + let mut diag = struct_span_err!( + tcx.sess, + cause.span(), + E0053, + "method `{}` has an incompatible type for trait", + trait_m.name + ); + match &terr { + TypeError::ArgumentMutability(0) | TypeError::ArgumentSorts(_, 0) + if trait_m.fn_has_self_parameter => + { + let ty = trait_sig.inputs()[0]; + let sugg = match ExplicitSelf::determine(ty, |_| ty == impl_trait_ref.self_ty()) { + ExplicitSelf::ByValue => "self".to_owned(), + ExplicitSelf::ByReference(_, hir::Mutability::Not) => "&self".to_owned(), + ExplicitSelf::ByReference(_, hir::Mutability::Mut) => "&mut self".to_owned(), + _ => format!("self: {ty}"), + }; + + // When the `impl` receiver is an arbitrary self type, like `self: Box`, the + // span points only at the type `Box, but we want to cover the whole + // argument pattern and type. + let span = match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind { + ImplItemKind::Fn(ref sig, body) => tcx + .hir() + .body_param_names(body) + .zip(sig.decl.inputs.iter()) + .map(|(param, ty)| param.span.to(ty.span)) + .next() + .unwrap_or(impl_err_span), + _ => bug!("{:?} is not a method", impl_m), + }; + + diag.span_suggestion( + span, + "change the self-receiver type to match the trait", + sugg, + Applicability::MachineApplicable, + ); + } + TypeError::ArgumentMutability(i) | TypeError::ArgumentSorts(_, i) => { + if trait_sig.inputs().len() == *i { + // Suggestion to change output type. We do not suggest in `async` functions + // to avoid complex logic or incorrect output. + match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind { + ImplItemKind::Fn(ref sig, _) + if sig.header.asyncness == hir::IsAsync::NotAsync => + { + let msg = "change the output type to match the trait"; + let ap = Applicability::MachineApplicable; + match sig.decl.output { + hir::FnRetTy::DefaultReturn(sp) => { + let sugg = format!("-> {} ", trait_sig.output()); + diag.span_suggestion_verbose(sp, msg, sugg, ap); + } + hir::FnRetTy::Return(hir_ty) => { + let sugg = trait_sig.output(); + diag.span_suggestion(hir_ty.span, msg, sugg, ap); + } + }; + } + _ => {} + }; + } else if let Some(trait_ty) = trait_sig.inputs().get(*i) { + diag.span_suggestion( + impl_err_span, + "change the parameter type to match the trait", + trait_ty, + Applicability::MachineApplicable, + ); + } + } + _ => {} + } + + infcx.err_ctxt().note_type_err( + &mut diag, + &cause, + trait_err_span.map(|sp| (sp, "type in trait".to_owned())), + Some(infer::ValuePairs::Terms(ExpectedFound { + expected: trait_fty.into(), + found: impl_fty.into(), + })), + terr, + false, + false, + ); + + return diag.emit(); +} + fn check_region_bounds_on_impl_item<'tcx>( tcx: TyCtxt<'tcx>, impl_m: &ty::AssocItem, diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 8a70f41c8a84..837ff0bdf3e3 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -117,7 +117,7 @@ pub(super) fn enter_wf_checking_ctxt<'tcx, F>( } fn check_well_formed(tcx: TyCtxt<'_>, def_id: hir::OwnerId) { - let node = tcx.hir().expect_owner(def_id); + let node = tcx.hir().owner(def_id); match node { hir::OwnerNode::Crate(_) => {} hir::OwnerNode::Item(item) => check_item(tcx, item), diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 4bca16c3a1cc..0e7a5ebf5ab6 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -644,12 +644,6 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { } } - // Desugared from `impl Trait`, so visited by the function's return type. - hir::ItemKind::OpaqueTy(hir::OpaqueTy { - origin: hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..), - .. - }) => {} - // Don't call `type_of` on opaque types, since that depends on type // checking function bodies. `check_item_type` ensures that it's called // instead. @@ -657,27 +651,32 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { tcx.ensure().generics_of(def_id); tcx.ensure().predicates_of(def_id); tcx.ensure().explicit_item_bounds(def_id); + tcx.ensure().item_bounds(def_id); } - hir::ItemKind::TyAlias(..) - | hir::ItemKind::Static(..) - | hir::ItemKind::Const(..) - | hir::ItemKind::Fn(..) => { + + hir::ItemKind::TyAlias(..) => { tcx.ensure().generics_of(def_id); tcx.ensure().type_of(def_id); tcx.ensure().predicates_of(def_id); - match it.kind { - hir::ItemKind::Fn(..) => tcx.ensure().fn_sig(def_id), - hir::ItemKind::OpaqueTy(..) => tcx.ensure().item_bounds(def_id), - hir::ItemKind::Const(ty, ..) | hir::ItemKind::Static(ty, ..) => { - if !is_suggestable_infer_ty(ty) { - let mut visitor = HirPlaceholderCollector::default(); - visitor.visit_item(it); - placeholder_type_error(tcx, None, visitor.0, false, None, it.kind.descr()); - } - } - _ => (), + } + + hir::ItemKind::Static(ty, ..) | hir::ItemKind::Const(ty, ..) => { + tcx.ensure().generics_of(def_id); + tcx.ensure().type_of(def_id); + tcx.ensure().predicates_of(def_id); + if !is_suggestable_infer_ty(ty) { + let mut visitor = HirPlaceholderCollector::default(); + visitor.visit_item(it); + placeholder_type_error(tcx, None, visitor.0, false, None, it.kind.descr()); } } + + hir::ItemKind::Fn(..) => { + tcx.ensure().generics_of(def_id); + tcx.ensure().type_of(def_id); + tcx.ensure().predicates_of(def_id); + tcx.ensure().fn_sig(def_id); + } } } diff --git a/compiler/rustc_hir_analysis/src/collect/lifetimes.rs b/compiler/rustc_hir_analysis/src/collect/lifetimes.rs index c64177eea3f8..6ee7aa9cdac6 100644 --- a/compiler/rustc_hir_analysis/src/collect/lifetimes.rs +++ b/compiler/rustc_hir_analysis/src/collect/lifetimes.rs @@ -94,11 +94,6 @@ struct LifetimeContext<'a, 'tcx> { tcx: TyCtxt<'tcx>, map: &'a mut NamedRegionMap, scope: ScopeRef<'a>, - - /// Indicates that we only care about the definition of a trait. This should - /// be false if the `Item` we are resolving lifetimes for is not a trait or - /// we eventually need lifetimes resolve for trait items. - trait_definition_only: bool, } #[derive(Debug)] @@ -166,7 +161,9 @@ enum Scope<'a> { s: ScopeRef<'a>, }, - Root, + Root { + opt_parent_item: Option, + }, } #[derive(Copy, Clone, Debug)] @@ -214,95 +211,58 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> { .field("s", &"..") .finish(), Scope::TraitRefBoundary { s: _ } => f.debug_struct("TraitRefBoundary").finish(), - Scope::Root => f.debug_struct("Root").finish(), + Scope::Root { opt_parent_item } => { + f.debug_struct("Root").field("opt_parent_item", &opt_parent_item).finish() + } } } } type ScopeRef<'a> = &'a Scope<'a>; -const ROOT_SCOPE: ScopeRef<'static> = &Scope::Root; - pub(crate) fn provide(providers: &mut ty::query::Providers) { *providers = ty::query::Providers { - resolve_lifetimes_trait_definition, resolve_lifetimes, - named_region_map: |tcx, id| resolve_lifetimes_for(tcx, id).defs.get(&id), + named_region_map: |tcx, id| tcx.resolve_lifetimes(id).defs.get(&id), is_late_bound_map, object_lifetime_default, - late_bound_vars_map: |tcx, id| resolve_lifetimes_for(tcx, id).late_bound_vars.get(&id), + late_bound_vars_map: |tcx, id| tcx.resolve_lifetimes(id).late_bound_vars.get(&id), ..*providers }; } -/// Like `resolve_lifetimes`, but does not resolve lifetimes for trait items. -/// Also does not generate any diagnostics. -/// -/// This is ultimately a subset of the `resolve_lifetimes` work. It effectively -/// resolves lifetimes only within the trait "header" -- that is, the trait -/// and supertrait list. In contrast, `resolve_lifetimes` resolves all the -/// lifetimes within the trait and its items. There is room to refactor this, -/// for example to resolve lifetimes for each trait item in separate queries, -/// but it's convenient to do the entire trait at once because the lifetimes -/// from the trait definition are in scope within the trait items as well. -/// -/// The reason for this separate call is to resolve what would otherwise -/// be a cycle. Consider this example: -/// -/// ```ignore UNSOLVED (maybe @jackh726 knows what lifetime parameter to give Sub) -/// trait Base<'a> { -/// type BaseItem; -/// } -/// trait Sub<'b>: for<'a> Base<'a> { -/// type SubItem: Sub; -/// } -/// ``` -/// -/// When we resolve `Sub` and all its items, we also have to resolve `Sub`. -/// To figure out the index of `'b`, we have to know about the supertraits -/// of `Sub` so that we can determine that the `for<'a>` will be in scope. -/// (This is because we -- currently at least -- flatten all the late-bound -/// lifetimes into a single binder.) This requires us to resolve the -/// *trait definition* of `Sub`; basically just enough lifetime information -/// to look at the supertraits. -#[instrument(level = "debug", skip(tcx))] -fn resolve_lifetimes_trait_definition( - tcx: TyCtxt<'_>, - local_def_id: LocalDefId, -) -> ResolveLifetimes { - convert_named_region_map(do_resolve(tcx, local_def_id, true)) -} - /// Computes the `ResolveLifetimes` map that contains data for an entire `Item`. /// You should not read the result of this query directly, but rather use /// `named_region_map`, `is_late_bound_map`, etc. #[instrument(level = "debug", skip(tcx))] -fn resolve_lifetimes(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> ResolveLifetimes { - convert_named_region_map(do_resolve(tcx, local_def_id, false)) -} - -fn do_resolve( - tcx: TyCtxt<'_>, - local_def_id: LocalDefId, - trait_definition_only: bool, -) -> NamedRegionMap { - let item = tcx.hir().expect_item(local_def_id); +fn resolve_lifetimes(tcx: TyCtxt<'_>, local_def_id: hir::OwnerId) -> ResolveLifetimes { let mut named_region_map = NamedRegionMap { defs: Default::default(), late_bound_vars: Default::default() }; let mut visitor = LifetimeContext { tcx, map: &mut named_region_map, - scope: ROOT_SCOPE, - trait_definition_only, + scope: &Scope::Root { opt_parent_item: None }, }; - visitor.visit_item(item); + match tcx.hir().owner(local_def_id) { + hir::OwnerNode::Item(item) => visitor.visit_item(item), + hir::OwnerNode::ForeignItem(item) => visitor.visit_foreign_item(item), + hir::OwnerNode::TraitItem(item) => { + let scope = + Scope::Root { opt_parent_item: Some(tcx.local_parent(item.owner_id.def_id)) }; + visitor.scope = &scope; + visitor.visit_trait_item(item) + } + hir::OwnerNode::ImplItem(item) => { + let scope = + Scope::Root { opt_parent_item: Some(tcx.local_parent(item.owner_id.def_id)) }; + visitor.scope = &scope; + visitor.visit_impl_item(item) + } + hir::OwnerNode::Crate(_) => {} + } - named_region_map -} - -fn convert_named_region_map(named_region_map: NamedRegionMap) -> ResolveLifetimes { let mut rl = ResolveLifetimes::default(); for (hir_id, v) in named_region_map.defs { @@ -319,53 +279,6 @@ fn convert_named_region_map(named_region_map: NamedRegionMap) -> ResolveLifetime rl } -/// Given `any` owner (structs, traits, trait methods, etc.), does lifetime resolution. -/// There are two important things this does. -/// First, we have to resolve lifetimes for -/// the entire *`Item`* that contains this owner, because that's the largest "scope" -/// where we can have relevant lifetimes. -/// Second, if we are asking for lifetimes in a trait *definition*, we use `resolve_lifetimes_trait_definition` -/// instead of `resolve_lifetimes`, which does not descend into the trait items and does not emit diagnostics. -/// This allows us to avoid cycles. Importantly, if we ask for lifetimes for lifetimes that have an owner -/// other than the trait itself (like the trait methods or associated types), then we just use the regular -/// `resolve_lifetimes`. -fn resolve_lifetimes_for<'tcx>(tcx: TyCtxt<'tcx>, def_id: hir::OwnerId) -> &'tcx ResolveLifetimes { - let item_id = item_for(tcx, def_id.def_id); - let local_def_id = item_id.owner_id.def_id; - if item_id.owner_id == def_id { - let item = tcx.hir().item(item_id); - match item.kind { - hir::ItemKind::Trait(..) => tcx.resolve_lifetimes_trait_definition(local_def_id), - _ => tcx.resolve_lifetimes(local_def_id), - } - } else { - tcx.resolve_lifetimes(local_def_id) - } -} - -/// Finds the `Item` that contains the given `LocalDefId` -fn item_for(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> hir::ItemId { - match tcx.hir().find_by_def_id(local_def_id) { - Some(Node::Item(item)) => { - return item.item_id(); - } - _ => {} - } - let item = { - let hir_id = tcx.hir().local_def_id_to_hir_id(local_def_id); - let mut parent_iter = tcx.hir().parent_iter(hir_id); - loop { - let node = parent_iter.next().map(|n| n.1); - match node { - Some(hir::Node::Item(item)) => break item.item_id(), - Some(hir::Node::Crate(_)) | None => bug!("Called `item_for` on an Item."), - _ => {} - } - } - }; - item -} - fn late_region_as_bound_region<'tcx>(tcx: TyCtxt<'tcx>, region: &Region) -> ty::BoundVariableKind { match region { Region::LateBound(_, _, def_id) => { @@ -383,7 +296,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { let mut supertrait_lifetimes = vec![]; loop { match scope { - Scope::Body { .. } | Scope::Root => { + Scope::Body { .. } | Scope::Root { .. } => { break (vec![], BinderScopeType::Normal); } @@ -414,21 +327,12 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } } impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { - type NestedFilter = nested_filter::All; + type NestedFilter = nested_filter::OnlyBodies; fn nested_visit_map(&mut self) -> Self::Map { self.tcx.hir() } - // We want to nest trait/impl items in their parent, but nothing else. - fn visit_nested_item(&mut self, _: hir::ItemId) {} - - fn visit_trait_item_ref(&mut self, ii: &'tcx hir::TraitItemRef) { - if !self.trait_definition_only { - intravisit::walk_trait_item_ref(self, ii) - } - } - fn visit_nested_body(&mut self, body: hir::BodyId) { let body = self.tcx.hir().body(body); self.with(Scope::Body { id: body.id(), s: self.scope }, |this| { @@ -548,7 +452,9 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { intravisit::walk_item(this, item) }); } - hir::ItemKind::OpaqueTy(hir::OpaqueTy { .. }) => { + hir::ItemKind::OpaqueTy(hir::OpaqueTy { + origin: hir::OpaqueTyOrigin::TyAlias, .. + }) => { // Opaque types are visited when we visit the // `TyKind::OpaqueDef`, so that they have the lifetimes from // their parent opaque_ty in scope. @@ -557,34 +463,53 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { // their owner, we can keep going until we find the Item that owns that. We then // conservatively add all resolved lifetimes. Otherwise we run into problems in // cases like `type Foo<'a> = impl Bar`. - for (_hir_id, node) in self.tcx.hir().parent_iter(item.owner_id.into()) { - match node { - hir::Node::Item(parent_item) => { - let resolved_lifetimes: &ResolveLifetimes = self.tcx.resolve_lifetimes( - item_for(self.tcx, parent_item.owner_id.def_id).owner_id.def_id, - ); - // We need to add *all* deps, since opaque tys may want them from *us* - for (&owner, defs) in resolved_lifetimes.defs.iter() { - defs.iter().for_each(|(&local_id, region)| { - self.map.defs.insert(hir::HirId { owner, local_id }, *region); - }); - } - for (&owner, late_bound_vars) in - resolved_lifetimes.late_bound_vars.iter() - { - late_bound_vars.iter().for_each(|(&local_id, late_bound_vars)| { - self.record_late_bound_vars( - hir::HirId { owner, local_id }, - late_bound_vars.clone(), - ); - }); - } - break; + let parent_item = self.tcx.hir().get_parent_item(item.hir_id()); + let resolved_lifetimes: &ResolveLifetimes = self.tcx.resolve_lifetimes(parent_item); + // We need to add *all* deps, since opaque tys may want them from *us* + for (&owner, defs) in resolved_lifetimes.defs.iter() { + defs.iter().for_each(|(&local_id, region)| { + self.map.defs.insert(hir::HirId { owner, local_id }, *region); + }); + } + for (&owner, late_bound_vars) in resolved_lifetimes.late_bound_vars.iter() { + late_bound_vars.iter().for_each(|(&local_id, late_bound_vars)| { + self.record_late_bound_vars( + hir::HirId { owner, local_id }, + late_bound_vars.clone(), + ); + }); + } + } + hir::ItemKind::OpaqueTy(hir::OpaqueTy { + origin: hir::OpaqueTyOrigin::FnReturn(_) | hir::OpaqueTyOrigin::AsyncFn(_), + generics, + .. + }) => { + // We want to start our early-bound indices at the end of the parent scope, + // not including any parent `impl Trait`s. + let mut lifetimes = FxIndexMap::default(); + debug!(?generics.params); + for param in generics.params { + match param.kind { + GenericParamKind::Lifetime { .. } => { + let (def_id, reg) = Region::early(self.tcx.hir(), ¶m); + lifetimes.insert(def_id, reg); } - hir::Node::Crate(_) => bug!("No Item about an OpaqueTy"), - _ => {} + GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {} } } + + let scope = Scope::Binder { + hir_id: item.hir_id(), + lifetimes, + s: self.scope, + scope_type: BinderScopeType::Normal, + where_bound_origin: None, + }; + self.with(scope, |this| { + let scope = Scope::TraitRefBoundary { s: this.scope }; + this.with(scope, |this| intravisit::walk_item(this, item)) + }); } hir::ItemKind::TyAlias(_, ref generics) | hir::ItemKind::Enum(_, ref generics) @@ -609,7 +534,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { hir_id: item.hir_id(), lifetimes, scope_type: BinderScopeType::Normal, - s: ROOT_SCOPE, + s: self.scope, where_bound_origin: None, }; self.with(scope, |this| { @@ -712,7 +637,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { // ^ ^ this gets resolved in the scope of // the opaque_ty generics let opaque_ty = self.tcx.hir().item(item_id); - let (generics, bounds) = match opaque_ty.kind { + match opaque_ty.kind { hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin: hir::OpaqueTyOrigin::TyAlias, .. @@ -733,10 +658,8 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin: hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..), - ref generics, - bounds, .. - }) => (generics, bounds), + }) => {} ref i => bug!("`impl Trait` pointed to non-opaque type?? {:#?}", i), }; @@ -766,65 +689,28 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { // Ensure that the parent of the def is an item, not HRTB let parent_id = self.tcx.hir().get_parent_node(hir_id); if !parent_id.is_owner() { - if !self.trait_definition_only { - struct_span_err!( - self.tcx.sess, - lifetime.span, - E0657, - "`impl Trait` can only capture lifetimes \ - bound at the fn or impl level" - ) - .emit(); - } + struct_span_err!( + self.tcx.sess, + lifetime.span, + E0657, + "`impl Trait` can only capture lifetimes bound at the fn or impl level" + ) + .emit(); self.uninsert_lifetime_on_error(lifetime, def.unwrap()); } if let hir::Node::Item(hir::Item { kind: hir::ItemKind::OpaqueTy { .. }, .. }) = self.tcx.hir().get(parent_id) { - if !self.trait_definition_only { - let mut err = self.tcx.sess.struct_span_err( - lifetime.span, - "higher kinded lifetime bounds on nested opaque types are not supported yet", - ); - err.span_note(self.tcx.def_span(def_id), "lifetime declared here"); - err.emit(); - } + let mut err = self.tcx.sess.struct_span_err( + lifetime.span, + "higher kinded lifetime bounds on nested opaque types are not supported yet", + ); + err.span_note(self.tcx.def_span(def_id), "lifetime declared here"); + err.emit(); self.uninsert_lifetime_on_error(lifetime, def.unwrap()); } } - - // We want to start our early-bound indices at the end of the parent scope, - // not including any parent `impl Trait`s. - let mut lifetimes = FxIndexMap::default(); - debug!(?generics.params); - for param in generics.params { - match param.kind { - GenericParamKind::Lifetime { .. } => { - let (def_id, reg) = Region::early(self.tcx.hir(), ¶m); - lifetimes.insert(def_id, reg); - } - GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {} - } - } - self.record_late_bound_vars(ty.hir_id, vec![]); - - let scope = Scope::Binder { - hir_id: ty.hir_id, - lifetimes, - s: self.scope, - scope_type: BinderScopeType::Normal, - where_bound_origin: None, - }; - self.with(scope, |this| { - let scope = Scope::TraitRefBoundary { s: this.scope }; - this.with(scope, |this| { - this.visit_generics(generics); - for bound in bounds { - this.visit_param_bound(bound); - } - }) - }); } _ => intravisit::walk_ty(self, ty), } @@ -1193,12 +1079,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { F: for<'b> FnOnce(&mut LifetimeContext<'b, 'tcx>), { let LifetimeContext { tcx, map, .. } = self; - let mut this = LifetimeContext { - tcx: *tcx, - map, - scope: &wrap_scope, - trait_definition_only: self.trait_definition_only, - }; + let mut this = LifetimeContext { tcx: *tcx, map, scope: &wrap_scope }; let span = debug_span!("scope", scope = ?TruncatedScopeDebug(&this.scope)); { let _enter = span.enter(); @@ -1303,7 +1184,13 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { scope = s; } - Scope::Root => { + Scope::Root { opt_parent_item } => { + if let Some(parent_item) = opt_parent_item + && let parent_generics = self.tcx.generics_of(parent_item) + && parent_generics.param_def_id_to_index.contains_key(®ion_def_id.to_def_id()) + { + break Some(Region::EarlyBound(region_def_id.to_def_id())); + } break None; } @@ -1417,7 +1304,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { err.emit(); return; } - Scope::Root => break, + Scope::Root { .. } => break, Scope::Binder { s, .. } | Scope::Body { s, .. } | Scope::Elision { s, .. } @@ -1495,7 +1382,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { let mut scope = self.scope; loop { match *scope { - Scope::Root => break false, + Scope::Root { .. } => break false, Scope::Body { .. } => break true, @@ -1732,7 +1619,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { scope = s; } - Scope::Root | Scope::Elision { .. } => break Region::Static, + Scope::Root { .. } | Scope::Elision { .. } => break Region::Static, Scope::Body { .. } | Scope::ObjectLifetimeDefault { lifetime: None, .. } => return, diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs index 267077cdab4e..55cca0cd2d7b 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs @@ -69,6 +69,7 @@ use crate::constrained_generic_params as cgp; use crate::errors::SubstsOnOverriddenImpl; use rustc_data_structures::fx::FxHashSet; +use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::TyCtxtInferExt; @@ -80,6 +81,7 @@ use rustc_span::Span; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; use rustc_trait_selection::traits::{self, translate_substs, wf, ObligationCtxt}; +use tracing::instrument; pub(super) fn check_min_specialization(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) { if let Some(node) = parent_specialization_node(tcx, impl_def_id) { @@ -103,13 +105,11 @@ fn parent_specialization_node(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId) -> Opti } /// Check that `impl1` is a sound specialization +#[instrument(level = "debug", skip(tcx))] fn check_always_applicable(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId, impl2_node: Node) { if let Some((impl1_substs, impl2_substs)) = get_impl_substs(tcx, impl1_def_id, impl2_node) { let impl2_def_id = impl2_node.def_id(); - debug!( - "check_always_applicable(\nimpl1_def_id={:?},\nimpl2_def_id={:?},\nimpl2_substs={:?}\n)", - impl1_def_id, impl2_def_id, impl2_substs - ); + debug!(?impl2_def_id, ?impl2_substs); let parent_substs = if impl2_node.is_from_trait() { impl2_substs.to_vec() @@ -118,12 +118,33 @@ fn check_always_applicable(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId, impl2_node }; let span = tcx.def_span(impl1_def_id); + check_constness(tcx, impl1_def_id, impl2_node, span); check_static_lifetimes(tcx, &parent_substs, span); check_duplicate_params(tcx, impl1_substs, &parent_substs, span); check_predicates(tcx, impl1_def_id, impl1_substs, impl2_node, impl2_substs, span); } } +/// Check that the specializing impl `impl1` is at least as const as the base +/// impl `impl2` +fn check_constness(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId, impl2_node: Node, span: Span) { + if impl2_node.is_from_trait() { + // This isn't a specialization + return; + } + + let impl1_constness = tcx.constness(impl1_def_id.to_def_id()); + let impl2_constness = tcx.constness(impl2_node.def_id()); + + if let hir::Constness::Const = impl2_constness { + if let hir::Constness::NotConst = impl1_constness { + tcx.sess + .struct_span_err(span, "cannot specialize on const impl with non-const impl") + .emit(); + } + } +} + /// Given a specializing impl `impl1`, and the base impl `impl2`, returns two /// substitutions `(S1, S2)` that equate their trait references. The returned /// types are expressed in terms of the generics of `impl1`. @@ -278,15 +299,15 @@ fn check_static_lifetimes<'tcx>( /// Check whether predicates on the specializing impl (`impl1`) are allowed. /// -/// Each predicate `P` must be: +/// Each predicate `P` must be one of: /// -/// * global (not reference any parameters) -/// * `T: Tr` predicate where `Tr` is an always-applicable trait -/// * on the base `impl impl2` -/// * Currently this check is done using syntactic equality, which is -/// conservative but generally sufficient. -/// * a well-formed predicate of a type argument of the trait being implemented, +/// * Global (not reference any parameters). +/// * A `T: Tr` predicate where `Tr` is an always-applicable trait. +/// * Present on the base impl `impl2`. +/// * This check is done using the `trait_predicates_eq` function below. +/// * A well-formed predicate of a type argument of the trait being implemented, /// including the `Self`-type. +#[instrument(level = "debug", skip(tcx))] fn check_predicates<'tcx>( tcx: TyCtxt<'tcx>, impl1_def_id: LocalDefId, @@ -322,10 +343,7 @@ fn check_predicates<'tcx>( .map(|obligation| obligation.predicate) .collect() }; - debug!( - "check_always_applicable(\nimpl1_predicates={:?},\nimpl2_predicates={:?}\n)", - impl1_predicates, impl2_predicates, - ); + debug!(?impl1_predicates, ?impl2_predicates); // Since impls of always applicable traits don't get to assume anything, we // can also assume their supertraits apply. @@ -373,25 +391,83 @@ fn check_predicates<'tcx>( ); for (predicate, span) in impl1_predicates { - if !impl2_predicates.contains(&predicate) { + if !impl2_predicates.iter().any(|pred2| trait_predicates_eq(tcx, predicate, *pred2, span)) { check_specialization_on(tcx, predicate, span) } } } +/// Checks if some predicate on the specializing impl (`predicate1`) is the same +/// as some predicate on the base impl (`predicate2`). +/// +/// This basically just checks syntactic equivalence, but is a little more +/// forgiving since we want to equate `T: Tr` with `T: ~const Tr` so this can work: +/// +/// ```ignore (illustrative) +/// #[rustc_specialization_trait] +/// trait Specialize { } +/// +/// impl Tr for T { } +/// impl const Tr for T { } +/// ``` +/// +/// However, we *don't* want to allow the reverse, i.e., when the bound on the +/// specializing impl is not as const as the bound on the base impl: +/// +/// ```ignore (illustrative) +/// impl const Tr for T { } +/// impl const Tr for T { } // should be T: ~const Bound +/// ``` +/// +/// So we make that check in this function and try to raise a helpful error message. +fn trait_predicates_eq<'tcx>( + tcx: TyCtxt<'tcx>, + predicate1: ty::Predicate<'tcx>, + predicate2: ty::Predicate<'tcx>, + span: Span, +) -> bool { + let pred1_kind = predicate1.kind().skip_binder(); + let pred2_kind = predicate2.kind().skip_binder(); + let (trait_pred1, trait_pred2) = match (pred1_kind, pred2_kind) { + (ty::PredicateKind::Trait(pred1), ty::PredicateKind::Trait(pred2)) => (pred1, pred2), + // Just use plain syntactic equivalence if either of the predicates aren't + // trait predicates or have bound vars. + _ => return predicate1 == predicate2, + }; + + let predicates_equal_modulo_constness = { + let pred1_unconsted = + ty::TraitPredicate { constness: ty::BoundConstness::NotConst, ..trait_pred1 }; + let pred2_unconsted = + ty::TraitPredicate { constness: ty::BoundConstness::NotConst, ..trait_pred2 }; + pred1_unconsted == pred2_unconsted + }; + + if !predicates_equal_modulo_constness { + return false; + } + + // Check that the predicate on the specializing impl is at least as const as + // the one on the base. + match (trait_pred2.constness, trait_pred1.constness) { + (ty::BoundConstness::ConstIfConst, ty::BoundConstness::NotConst) => { + tcx.sess.struct_span_err(span, "missing `~const` qualifier for specialization").emit(); + } + _ => {} + } + + true +} + +#[instrument(level = "debug", skip(tcx))] fn check_specialization_on<'tcx>(tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tcx>, span: Span) { - debug!("can_specialize_on(predicate = {:?})", predicate); match predicate.kind().skip_binder() { // Global predicates are either always true or always false, so we // are fine to specialize on. _ if predicate.is_global() => (), // We allow specializing on explicitly marked traits with no associated // items. - ty::PredicateKind::Trait(ty::TraitPredicate { - trait_ref, - constness: ty::BoundConstness::NotConst, - polarity: _, - }) => { + ty::PredicateKind::Trait(ty::TraitPredicate { trait_ref, constness: _, polarity: _ }) => { if !matches!( trait_predicate_kind(tcx, predicate), Some(TraitSpecializationKind::Marker) diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index 3001e7994767..8d3acee48884 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -35,7 +35,7 @@ struct ClosureSignatures<'tcx> { bound_sig: ty::PolyFnSig<'tcx>, /// The signature within the function body. /// This mostly differs in the sense that lifetimes are now early bound and any - /// opaque types from the signature expectation are overriden in case there are + /// opaque types from the signature expectation are overridden in case there are /// explicit hidden types written by the user in the closure signature. liberated_sig: ty::FnSig<'tcx>, } diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 4d8ab2c1c7ad..71949b421181 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -62,6 +62,7 @@ use rustc_span::{self, BytePos, DesugaringKind, Span}; use rustc_target::spec::abi::Abi; use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; +use rustc_trait_selection::traits::TraitEngineExt as _; use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode}; use smallvec::{smallvec, SmallVec}; @@ -1038,7 +1039,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let Ok(ok) = coerce.coerce(source, target) else { return false; }; - let mut fcx = traits::FulfillmentContext::new_in_snapshot(); + let mut fcx = >::new_in_snapshot(self.tcx); fcx.register_predicate_obligations(self, ok.obligations); fcx.select_where_possible(&self).is_empty() }) diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 16febfc46da9..9ca7730daa68 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -30,6 +30,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, error: Option>, ) { + if expr_ty == expected { + return; + } + self.annotate_expected_due_to_let_ty(err, expr, error); // Use `||` to give these suggestions a precedence @@ -42,7 +46,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { || self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty) || self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected) || self.suggest_copied_or_cloned(err, expr, expr_ty, expected) - || self.suggest_into(err, expr, expr_ty, expected); + || self.suggest_into(err, expr, expr_ty, expected) + || self.suggest_option_to_bool(err, expr, expr_ty, expected) + || self.suggest_floating_point_literal(err, expr, expected); self.note_type_is_not_clone(err, expected, expr_ty, expr); self.note_need_for_fn_pointer(err, expected, expr_ty); diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index cfb408396da0..afac6e7d94a8 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -125,3 +125,11 @@ pub struct AddMissingParenthesesInRange { #[suggestion_part(code = ")")] pub right: Span, } + +#[derive(Diagnostic)] +#[diag(hir_analysis_op_trait_generic_params)] +pub struct OpMethodGenericParams { + #[primary_span] + pub span: Span, + pub method_name: String, +} diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 43669489e69b..e948d832e328 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -103,8 +103,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if let Some(mut err) = self.demand_suptype_diag(expr.span, expected_ty, ty) { - let expr = expr.peel_drop_temps(); - self.suggest_deref_ref_or_into(&mut err, expr, expected_ty, ty, None); + // FIXME(compiler-errors): We probably should fold some of the + // `suggest_` functions from `emit_coerce_suggestions` into here, + // since some of those aren't necessarily just coerce suggestions. + let _ = self.suggest_deref_ref_or_into( + &mut err, + expr.peel_drop_temps(), + expected_ty, + ty, + None, + ) || self.suggest_option_to_bool(&mut err, expr, ty, expected_ty); extend_err(&mut err); err.emit(); } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 6ed7a93d4633..c6bd771fad25 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -18,6 +18,7 @@ use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryRespons use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282; use rustc_infer::infer::{InferOk, InferResult}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; +use rustc_middle::ty::error::TypeError; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::{ @@ -32,9 +33,7 @@ use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::{Span, DUMMY_SP}; use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; -use rustc_trait_selection::traits::{ - self, ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt, -}; +use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode, ObligationCtxt}; use std::collections::hash_map::Entry; use std::slice; @@ -766,34 +765,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let expect_args = self .fudge_inference_if_ok(|| { + let ocx = ObligationCtxt::new_in_snapshot(self); + // Attempt to apply a subtyping relationship between the formal // return type (likely containing type variables if the function // is polymorphic) and the expected return type. // No argument expectations are produced if unification fails. let origin = self.misc(call_span); - let ures = self.at(&origin, self.param_env).sup(ret_ty, formal_ret); - - // FIXME(#27336) can't use ? here, Try::from_error doesn't default - // to identity so the resulting type is not constrained. - match ures { - Ok(ok) => { - // Process any obligations locally as much as - // we can. We don't care if some things turn - // out unconstrained or ambiguous, as we're - // just trying to get hints here. - let errors = self.save_and_restore_in_snapshot_flag(|_| { - let mut fulfill = >::new(self.tcx); - for obligation in ok.obligations { - fulfill.register_predicate_obligation(self, obligation); - } - fulfill.select_where_possible(self) - }); - - if !errors.is_empty() { - return Err(()); - } - } - Err(_) => return Err(()), + ocx.sup(&origin, self.param_env, ret_ty, formal_ret)?; + if !ocx.select_where_possible().is_empty() { + return Err(TypeError::Mismatch); } // Record all the argument types, with the substitutions diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index a7a60a19bd37..8cf70eb5431a 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -597,6 +597,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; + let mk_trace = |span, (formal_ty, expected_ty), provided_ty| { + let mismatched_ty = if expected_ty == provided_ty { + // If expected == provided, then we must have failed to sup + // the formal type. Avoid printing out "expected Ty, found Ty" + // in that case. + formal_ty + } else { + expected_ty + }; + TypeTrace::types(&self.misc(span), true, mismatched_ty, provided_ty) + }; + // The algorithm here is inspired by levenshtein distance and longest common subsequence. // We'll try to detect 4 different types of mistakes: // - An extra parameter has been provided that doesn't satisfy *any* of the other inputs @@ -661,10 +673,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // A tuple wrap suggestion actually occurs within, // so don't do anything special here. err = self.err_ctxt().report_and_explain_type_error( - TypeTrace::types( - &self.misc(*lo), - true, - formal_and_expected_inputs[mismatch_idx.into()].1, + mk_trace( + *lo, + formal_and_expected_inputs[mismatch_idx.into()], provided_arg_tys[mismatch_idx.into()].0, ), terr, @@ -748,9 +759,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { errors.drain_filter(|error| { let Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(e))) = error else { return false }; let (provided_ty, provided_span) = provided_arg_tys[*provided_idx]; - let (expected_ty, _) = formal_and_expected_inputs[*expected_idx]; - let cause = &self.misc(provided_span); - let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); + let trace = mk_trace(provided_span, formal_and_expected_inputs[*expected_idx], provided_ty); if !matches!(trace.cause.as_failure_code(*e), FailureCode::Error0308(_)) { self.err_ctxt().report_and_explain_type_error(trace, *e).emit(); return true; @@ -774,8 +783,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { let (formal_ty, expected_ty) = formal_and_expected_inputs[*expected_idx]; let (provided_ty, provided_arg_span) = provided_arg_tys[*provided_idx]; - let cause = &self.misc(provided_arg_span); - let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); + let trace = mk_trace(provided_arg_span, (formal_ty, expected_ty), provided_ty); let mut err = self.err_ctxt().report_and_explain_type_error(trace, *err); self.emit_coerce_suggestions( &mut err, @@ -847,8 +855,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (formal_ty, expected_ty) = formal_and_expected_inputs[expected_idx]; let (provided_ty, provided_span) = provided_arg_tys[provided_idx]; if let Compatibility::Incompatible(error) = compatibility { - let cause = &self.misc(provided_span); - let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); + let trace = mk_trace(provided_span, (formal_ty, expected_ty), provided_ty); if let Some(e) = error { self.err_ctxt().note_type_err( &mut err, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index e3b3fb499b16..06e6e4350fcb 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -13,7 +13,7 @@ use rustc_hir_analysis::astconv::AstConv; use rustc_infer::infer::{self, TyCtxtInferExt}; use rustc_infer::traits::{self, StatementAsExpression}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{self, Binder, IsSuggestable, ToPredicate, Ty}; +use rustc_middle::ty::{self, Binder, DefIdTree, IsSuggestable, ToPredicate, Ty}; use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::symbol::sym; use rustc_span::Span; @@ -374,7 +374,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let annotation_span = ty.span; err.span_suggestion( annotation_span.with_hi(annotation_span.lo()), - format!("alternatively, consider changing the type annotation"), + "alternatively, consider changing the type annotation", suggest_annotation, Applicability::MaybeIncorrect, ); @@ -1116,6 +1116,53 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false } + /// When expecting a `bool` and finding an `Option`, suggests using `let Some(..)` or `.is_some()` + pub(crate) fn suggest_option_to_bool( + &self, + diag: &mut Diagnostic, + expr: &hir::Expr<'_>, + expr_ty: Ty<'tcx>, + expected_ty: Ty<'tcx>, + ) -> bool { + if !expected_ty.is_bool() { + return false; + } + + let ty::Adt(def, _) = expr_ty.peel_refs().kind() else { return false; }; + if !self.tcx.is_diagnostic_item(sym::Option, def.did()) { + return false; + } + + let hir = self.tcx.hir(); + let cond_parent = hir.parent_iter(expr.hir_id).skip_while(|(_, node)| { + matches!(node, hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(op, _, _), .. }) if op.node == hir::BinOpKind::And) + }).next(); + // Don't suggest: + // `let Some(_) = a.is_some() && b` + // ++++++++++ + // since the user probably just misunderstood how `let else` + // and `&&` work together. + if let Some((_, hir::Node::Local(local))) = cond_parent + && let hir::PatKind::Path(qpath) | hir::PatKind::TupleStruct(qpath, _, _) = &local.pat.kind + && let hir::QPath::Resolved(None, path) = qpath + && let Some(did) = path.res.opt_def_id() + .and_then(|did| self.tcx.opt_parent(did)) + .and_then(|did| self.tcx.opt_parent(did)) + && self.tcx.is_diagnostic_item(sym::Option, did) + { + return false; + } + + diag.span_suggestion( + expr.span.shrink_to_hi(), + "use `Option::is_some` to test if the `Option` has a value", + ".is_some()", + Applicability::MachineApplicable, + ); + + true + } + /// Suggest wrapping the block in square brackets instead of curly braces /// in case the block was mistaken array syntax, e.g. `{ 1 }` -> `[ 1 ]`. pub(crate) fn suggest_block_to_brackets( @@ -1157,6 +1204,48 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + #[instrument(skip(self, err))] + pub(crate) fn suggest_floating_point_literal( + &self, + err: &mut Diagnostic, + expr: &hir::Expr<'_>, + expected_ty: Ty<'tcx>, + ) -> bool { + if !expected_ty.is_floating_point() { + return false; + } + match expr.kind { + ExprKind::Struct(QPath::LangItem(LangItem::Range, ..), [start, end], _) => { + err.span_suggestion_verbose( + start.span.shrink_to_hi().with_hi(end.span.lo()), + "remove the unnecessary `.` operator for a floating point literal", + '.', + Applicability::MaybeIncorrect, + ); + true + } + ExprKind::Struct(QPath::LangItem(LangItem::RangeFrom, ..), [start], _) => { + err.span_suggestion_verbose( + expr.span.with_lo(start.span.hi()), + "remove the unnecessary `.` operator for a floating point literal", + '.', + Applicability::MaybeIncorrect, + ); + true + } + ExprKind::Struct(QPath::LangItem(LangItem::RangeTo, ..), [end], _) => { + err.span_suggestion_verbose( + expr.span.until(end.span), + "remove the unnecessary `.` operator and add an integer part for a floating point literal", + "0.", + Applicability::MaybeIncorrect, + ); + true + } + _ => false, + } + } + fn is_loop(&self, id: hir::HirId) -> bool { let node = self.tcx.hir().get(id); matches!(node, Node::Expr(Expr { kind: ExprKind::Loop(..), .. })) diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index 2c7b3bbf31c2..4a8b77493654 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -10,6 +10,7 @@ mod suggest; pub use self::suggest::SelfSource; pub use self::MethodError::*; +use crate::errors::OpMethodGenericParams; use crate::{Expectation, FnCtxt}; use rustc_data_structures::sync::Lrc; use rustc_errors::{Applicability, Diagnostic}; @@ -443,7 +444,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let def_id = method_item.def_id; let generics = tcx.generics_of(def_id); - assert_eq!(generics.params.len(), 0); + + if generics.params.len() != 0 { + tcx.sess.emit_fatal(OpMethodGenericParams { + span: tcx.def_span(method_item.def_id), + method_name: m_name.to_string(), + }); + } debug!("lookup_in_trait_adjusted: method_item={:?}", method_item); let mut obligations = vec![]; diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 799043842201..43a5145b7e74 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -1900,6 +1900,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | ty::Str | ty::Projection(_) | ty::Param(_) => format!("{deref_ty}"), + // we need to test something like <&[_]>::len + // and Vec::function(); + // <&[_]>::len doesn't need an extra "<>" between + // but for Adt type like Vec::function() + // we would suggest <[_]>::function(); + _ if self.tcx.sess.source_map().span_wrapped_by_angle_bracket(ty.span) => format!("{deref_ty}"), _ => format!("<{deref_ty}>"), }; err.span_suggestion_verbose( diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index ccba197dc80b..fd3b3e4d59fa 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -778,32 +778,6 @@ impl<'tcx> InferCtxt<'tcx> { } } - /// Clear the "currently in a snapshot" flag, invoke the closure, - /// then restore the flag to its original value. This flag is a - /// debugging measure designed to detect cases where we start a - /// snapshot, create type variables, and register obligations - /// which may involve those type variables in the fulfillment cx, - /// potentially leaving "dangling type variables" behind. - /// In such cases, an assertion will fail when attempting to - /// register obligations, within a snapshot. Very useful, much - /// better than grovelling through megabytes of `RUSTC_LOG` output. - /// - /// HOWEVER, in some cases the flag is unhelpful. In particular, we - /// sometimes create a "mini-fulfilment-cx" in which we enroll - /// obligations. As long as this fulfillment cx is fully drained - /// before we return, this is not a problem, as there won't be any - /// escaping obligations in the main cx. In those cases, you can - /// use this function. - pub fn save_and_restore_in_snapshot_flag(&self, func: F) -> R - where - F: FnOnce(&Self) -> R, - { - let flag = self.in_snapshot.replace(false); - let result = func(self); - self.in_snapshot.set(flag); - result - } - fn start_snapshot(&self) -> CombinedSnapshot<'tcx> { debug!("start_snapshot()"); diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index dfe52312ff00..efae26690063 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -163,7 +163,7 @@ fn shallow_lint_levels_on(tcx: TyCtxt<'_>, owner: hir::OwnerId) -> ShallowLintLe // Otherwise, we need to visit the attributes in source code order, so we fetch HIR and do // a standard visit. // FIXME(#102522) Just iterate on attrs once that iteration order matches HIR's. - _ => match tcx.hir().expect_owner(owner) { + _ => match tcx.hir().owner(owner) { hir::OwnerNode::Item(item) => levels.visit_item(item), hir::OwnerNode::ForeignItem(item) => levels.visit_foreign_item(item), hir::OwnerNode::TraitItem(item) => levels.visit_trait_item(item), diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 37caab2da0f5..3e2efb7d3610 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -360,7 +360,7 @@ fn lint_int_literal<'tcx>( } if lint_overflowing_range_endpoint(cx, lit, v, max, e, t.name_str()) { - // The overflowing literal lint was emited by `lint_overflowing_range_endpoint`. + // The overflowing literal lint was emitted by `lint_overflowing_range_endpoint`. return; } @@ -429,7 +429,7 @@ fn lint_uint_literal<'tcx>( } } if lint_overflowing_range_endpoint(cx, lit, lit_val, max, e, t.name_str()) { - // The overflowing literal lint was emited by `lint_overflowing_range_endpoint`. + // The overflowing literal lint was emitted by `lint_overflowing_range_endpoint`. return; } if let Some(repr_str) = get_bin_hex_repr(cx, lit) { diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 46706e498445..ff0fb9bae923 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -9,7 +9,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_infer::traits::util::elaborate_predicates_with_span; use rustc_middle::ty::adjustment; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, DefIdTree, Ty}; use rustc_span::symbol::Symbol; use rustc_span::symbol::{kw, sym}; use rustc_span::{BytePos, Span}; @@ -87,17 +87,33 @@ declare_lint_pass!(UnusedResults => [UNUSED_MUST_USE, UNUSED_RESULTS]); impl<'tcx> LateLintPass<'tcx> for UnusedResults { fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) { - let expr = match s.kind { - hir::StmtKind::Semi(ref expr) => &**expr, - _ => return, - }; + let hir::StmtKind::Semi(expr) = s.kind else { return; }; if let hir::ExprKind::Ret(..) = expr.kind { return; } + if let hir::ExprKind::Match(await_expr, _arms, hir::MatchSource::AwaitDesugar) = expr.kind + && let ty = cx.typeck_results().expr_ty(&await_expr) + && let ty::Opaque(future_def_id, _) = ty.kind() + && cx.tcx.ty_is_opaque_future(ty) + // FIXME: This also includes non-async fns that return `impl Future`. + && let async_fn_def_id = cx.tcx.parent(*future_def_id) + && check_must_use_def( + cx, + async_fn_def_id, + expr.span, + "output of future returned by ", + "", + ) + { + // We have a bare `foo().await;` on an opaque type from an async function that was + // annotated with `#[must_use]`. + return; + } + let ty = cx.typeck_results().expr_ty(&expr); - let type_permits_lack_of_use = check_must_use_ty(cx, ty, &expr, s.span, "", "", 1); + let type_permits_lack_of_use = check_must_use_ty(cx, ty, &expr, expr.span, "", "", 1); let mut fn_warned = false; let mut op_warned = false; @@ -119,7 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { _ => None, }; if let Some(def_id) = maybe_def_id { - fn_warned = check_must_use_def(cx, def_id, s.span, "return value of ", ""); + fn_warned = check_must_use_def(cx, def_id, expr.span, "return value of ", ""); } else if type_permits_lack_of_use { // We don't warn about unused unit or uninhabited types. // (See https://github.com/rust-lang/rust/issues/43806 for details.) @@ -565,10 +581,24 @@ trait UnusedDelimLint { lint.set_arg("delim", Self::DELIM_STR); lint.set_arg("item", msg); if let Some((lo, hi)) = spans { - let replacement = vec![ - (lo, if keep_space.0 { " ".into() } else { "".into() }), - (hi, if keep_space.1 { " ".into() } else { "".into() }), - ]; + let sm = cx.sess().source_map(); + let lo_replace = + if keep_space.0 && + let Ok(snip) = sm.span_to_prev_source(lo) && !snip.ends_with(" ") { + " ".to_string() + } else { + "".to_string() + }; + + let hi_replace = + if keep_space.1 && + let Ok(snip) = sm.span_to_next_source(hi) && !snip.starts_with(" ") { + " ".to_string() + } else { + "".to_string() + }; + + let replacement = vec![(lo, lo_replace), (hi, hi_replace)]; lint.multipart_suggestion( fluent::suggestion, replacement, @@ -765,6 +795,7 @@ impl UnusedParens { value: &ast::Pat, avoid_or: bool, avoid_mut: bool, + keep_space: (bool, bool), ) { use ast::{BindingAnnotation, PatKind}; @@ -789,7 +820,7 @@ impl UnusedParens { } else { None }; - self.emit_unused_delims(cx, value.span, spans, "pattern", (false, false)); + self.emit_unused_delims(cx, value.span, spans, "pattern", keep_space); } } } @@ -798,7 +829,7 @@ impl EarlyLintPass for UnusedParens { fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { match e.kind { ExprKind::Let(ref pat, _, _) | ExprKind::ForLoop(ref pat, ..) => { - self.check_unused_parens_pat(cx, pat, false, false); + self.check_unused_parens_pat(cx, pat, false, false, (true, true)); } // We ignore parens in cases like `if (((let Some(0) = Some(1))))` because we already // handle a hard error for them during AST lowering in `lower_expr_mut`, but we still @@ -842,6 +873,7 @@ impl EarlyLintPass for UnusedParens { fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &ast::Pat) { use ast::{Mutability, PatKind::*}; + let keep_space = (false, false); match &p.kind { // Do not lint on `(..)` as that will result in the other arms being useless. Paren(_) @@ -849,33 +881,33 @@ impl EarlyLintPass for UnusedParens { | Wild | Rest | Lit(..) | MacCall(..) | Range(..) | Ident(.., None) | Path(..) => {}, // These are list-like patterns; parens can always be removed. TupleStruct(_, _, ps) | Tuple(ps) | Slice(ps) | Or(ps) => for p in ps { - self.check_unused_parens_pat(cx, p, false, false); + self.check_unused_parens_pat(cx, p, false, false, keep_space); }, Struct(_, _, fps, _) => for f in fps { - self.check_unused_parens_pat(cx, &f.pat, false, false); + self.check_unused_parens_pat(cx, &f.pat, false, false, keep_space); }, // Avoid linting on `i @ (p0 | .. | pn)` and `box (p0 | .. | pn)`, #64106. - Ident(.., Some(p)) | Box(p) => self.check_unused_parens_pat(cx, p, true, false), + Ident(.., Some(p)) | Box(p) => self.check_unused_parens_pat(cx, p, true, false, keep_space), // Avoid linting on `&(mut x)` as `&mut x` has a different meaning, #55342. // Also avoid linting on `& mut? (p0 | .. | pn)`, #64106. - Ref(p, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not), + Ref(p, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not, keep_space), } } fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) { if let StmtKind::Local(ref local) = s.kind { - self.check_unused_parens_pat(cx, &local.pat, true, false); + self.check_unused_parens_pat(cx, &local.pat, true, false, (false, false)); } ::check_stmt(self, cx, s) } fn check_param(&mut self, cx: &EarlyContext<'_>, param: &ast::Param) { - self.check_unused_parens_pat(cx, ¶m.pat, true, false); + self.check_unused_parens_pat(cx, ¶m.pat, true, false, (false, false)); } fn check_arm(&mut self, cx: &EarlyContext<'_>, arm: &ast::Arm) { - self.check_unused_parens_pat(cx, &arm.pat, false, false); + self.check_unused_parens_pat(cx, &arm.pat, false, false, (false, false)); } fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) { diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs index 28e092c1eb72..d35e4191cc0b 100644 --- a/compiler/rustc_llvm/build.rs +++ b/compiler/rustc_llvm/build.rs @@ -334,7 +334,7 @@ fn main() { "c++" } else if target.contains("netbsd") && llvm_static_stdcpp.is_some() { // NetBSD uses a separate library when relocation is required - "stdc++_pic" + "stdc++_p" } else if llvm_use_libcxx.is_some() { "c++" } else { diff --git a/compiler/rustc_metadata/src/fs.rs b/compiler/rustc_metadata/src/fs.rs index f360a586476e..c41ae8d55cda 100644 --- a/compiler/rustc_metadata/src/fs.rs +++ b/compiler/rustc_metadata/src/fs.rs @@ -22,9 +22,14 @@ pub const METADATA_FILENAME: &str = "lib.rmeta"; /// building an `.rlib` (stomping over one another), or writing an `.rmeta` into a /// directory being searched for `extern crate` (observing an incomplete file). /// The returned path is the temporary file containing the complete metadata. -pub fn emit_metadata(sess: &Session, metadata: &[u8], tmpdir: &MaybeTempDir) -> PathBuf { - let out_filename = tmpdir.as_ref().join(METADATA_FILENAME); - let result = fs::write(&out_filename, metadata); +pub fn emit_wrapper_file( + sess: &Session, + data: &[u8], + tmpdir: &MaybeTempDir, + name: &str, +) -> PathBuf { + let out_filename = tmpdir.as_ref().join(name); + let result = fs::write(&out_filename, data); if let Err(err) = result { sess.emit_fatal(FailedWriteError { filename: out_filename, err }); diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs index 98cf6fef54a8..1987f88e6b8c 100644 --- a/compiler/rustc_metadata/src/lib.rs +++ b/compiler/rustc_metadata/src/lib.rs @@ -41,6 +41,6 @@ pub mod errors; pub mod fs; pub mod locator; -pub use fs::{emit_metadata, METADATA_FILENAME}; +pub use fs::{emit_wrapper_file, METADATA_FILENAME}; pub use native_libs::find_native_static_library; pub use rmeta::{encode_metadata, EncodedMetadata, METADATA_HEADER}; diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index 8e7d0cf2ab1b..5f6e498dbeaa 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -8,7 +8,7 @@ doctest = false [dependencies] bitflags = "1.2.1" -chalk-ir = "0.80.0" +chalk-ir = "0.87.0" either = "1.5.0" gsgdt = "0.1.2" polonius-engine = "0.13.0" diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 83a4d16d7a92..14f50ae87de0 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -353,6 +353,10 @@ impl<'hir> Map<'hir> { node.node.generics() } + pub fn owner(self, id: OwnerId) -> OwnerNode<'hir> { + self.tcx.hir_owner(id).unwrap_or_else(|| bug!("expected owner for {:?}", id)).node + } + pub fn item(self, id: ItemId) -> &'hir Item<'hir> { self.tcx.hir_owner(id.owner_id).unwrap().node.expect_item() } @@ -822,8 +826,11 @@ impl<'hir> Map<'hir> { ) } - pub fn expect_owner(self, id: OwnerId) -> OwnerNode<'hir> { - self.tcx.hir_owner(id).unwrap_or_else(|| bug!("expected owner for {:?}", id)).node + pub fn expect_owner(self, def_id: LocalDefId) -> OwnerNode<'hir> { + self.tcx + .hir_owner(OwnerId { def_id }) + .unwrap_or_else(|| bug!("expected owner for {:?}", def_id)) + .node } pub fn expect_item(self, id: LocalDefId) -> &'hir Item<'hir> { diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index a58cbc3767ed..6bdf591fdd79 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -43,7 +43,6 @@ #![feature(type_alias_impl_trait)] #![feature(associated_type_bounds)] #![feature(rustc_attrs)] -#![cfg_attr(bootstrap, feature(half_open_range_patterns))] #![feature(control_flow_enum)] #![feature(associated_type_defaults)] #![feature(trusted_step)] diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index 5e3dfcbcc496..32ec58557692 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -106,6 +106,7 @@ use rustc_ast::LitKind; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::{HashMapExt, Lock}; use rustc_data_structures::tiny_list::TinyList; +use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::DefId; use rustc_macros::HashStable; use rustc_middle::ty::print::with_no_trimmed_paths; @@ -176,7 +177,7 @@ pub enum LitToConstError { /// This is used for graceful error handling (`delay_span_bug`) in /// type checking (`Const::from_anon_const`). TypeError, - Reported, + Reported(ErrorGuaranteed), } #[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)] diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 5290d5aae46c..4781651071d3 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1541,7 +1541,7 @@ impl<'tcx> Place<'tcx> { /// If MirPhase >= Derefered and if projection contains Deref, /// It's guaranteed to be in the first place pub fn has_deref(&self) -> bool { - // To make sure this is not accidently used in wrong mir phase + // To make sure this is not accidentally used in wrong mir phase debug_assert!( self.projection.is_empty() || !self.projection[1..].contains(&PlaceElem::Deref) ); @@ -1957,6 +1957,7 @@ impl BorrowKind { } } + // FIXME: won't be used after diagnostic migration pub fn describe_mutability(&self) -> &str { match *self { BorrowKind::Shared | BorrowKind::Shallow | BorrowKind::Unique => "immutable", @@ -2251,7 +2252,9 @@ impl<'tcx> ConstantKind<'tcx> { match tcx.const_eval_resolve(param_env, uneval, None) { Ok(val) => Self::Val(val, ty), Err(ErrorHandled::TooGeneric | ErrorHandled::Linted) => self, - Err(_) => Self::Ty(tcx.const_error(ty)), + Err(ErrorHandled::Reported(guar)) => { + Self::Ty(tcx.const_error_with_guaranteed(ty, guar)) + } } } } diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 85ef51f129bb..fed943169dfb 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -85,7 +85,7 @@ pub enum MirPhase { /// /// Also note that the lint pass which reports eg `200_u8 + 200_u8` as an error is run as a part /// of analysis to runtime MIR lowering. To ensure lints are reported reliably, this means that - /// transformations which may supress such errors should not run on analysis MIR. + /// transformations which may suppress such errors should not run on analysis MIR. Runtime(RuntimePhase), } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 00242e7eed77..1564cf414bd2 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1614,19 +1614,10 @@ rustc_queries! { desc { |tcx| "getting the native library for `{}`", tcx.def_path_str(def_id) } } - /// Does lifetime resolution, but does not descend into trait items. This - /// should only be used for resolving lifetimes of on trait definitions, - /// and is used to avoid cycles. Importantly, `resolve_lifetimes` still visits - /// the same lifetimes and is responsible for diagnostics. - /// See `rustc_resolve::late::lifetimes for details. - query resolve_lifetimes_trait_definition(_: LocalDefId) -> ResolveLifetimes { - arena_cache - desc { "resolving lifetimes for a trait definition" } - } /// Does lifetime resolution on items. Importantly, we can't resolve /// lifetimes directly on things like trait methods, because of trait params. /// See `rustc_resolve::late::lifetimes for details. - query resolve_lifetimes(_: LocalDefId) -> ResolveLifetimes { + query resolve_lifetimes(_: hir::OwnerId) -> ResolveLifetimes { arena_cache desc { "resolving lifetimes" } } diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 33fdf1a83709..e2e2761501b4 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -2,7 +2,6 @@ use crate::mir::interpret::LitToConstInput; use crate::mir::ConstantKind; use crate::ty::{self, InternalSubsts, ParamEnv, ParamEnvAnd, Ty, TyCtxt}; use rustc_data_structures::intern::Interned; -use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_macros::HashStable; @@ -225,7 +224,7 @@ impl<'tcx> Const<'tcx> { if let Some(val) = self.kind().try_eval_for_typeck(tcx, param_env) { match val { Ok(val) => Const::from_value(tcx, val, self.ty()), - Err(ErrorGuaranteed { .. }) => tcx.const_error(self.ty()), + Err(guar) => tcx.const_error_with_guaranteed(self.ty(), guar), } } else { // Either the constant isn't evaluatable or ValTree creation failed. @@ -240,7 +239,7 @@ impl<'tcx> Const<'tcx> { if let Some(val) = self.kind().try_eval_for_mir(tcx, param_env) { match val { Ok(const_val) => ConstantKind::from_value(const_val, self.ty()), - Err(ErrorGuaranteed { .. }) => ConstantKind::Ty(tcx.const_error(self.ty())), + Err(guar) => ConstantKind::Ty(tcx.const_error_with_guaranteed(self.ty(), guar)), } } else { ConstantKind::Ty(self) diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 2a93df771e1e..8f96f5a9eb3e 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -198,12 +198,8 @@ impl<'tcx> CtxtInterners<'tcx> { Fingerprint::ZERO } else { let mut hasher = StableHasher::new(); - let mut hcx = StableHashingContext::ignore_spans( - sess, - definitions, - cstore, - source_span, - ); + let mut hcx = + StableHashingContext::new(sess, definitions, cstore, source_span); kind.hash_stable(&mut hcx, &mut hasher); hasher.finish() }; diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index b509ae6dd3b8..18eb06b83c9d 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2506,6 +2506,10 @@ impl<'tcx> TyCtxt<'tcx> { self.trait_def(trait_def_id).has_auto_impl } + pub fn trait_is_coinductive(self, trait_def_id: DefId) -> bool { + self.trait_is_auto(trait_def_id) || self.lang_items().sized_trait() == Some(trait_def_id) + } + /// Returns layout of a generator. Layout might be unavailable if the /// generator is tainted by errors. pub fn generator_layout(self, def_id: DefId) -> Option<&'tcx GeneratorLayout<'tcx>> { diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 2432b5175197..48e803597b02 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -2225,46 +2225,12 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { // this is not *quite* right and changes the ordering of some output // anyways. let (new_value, map) = if self.should_print_verbose() { - let regions: Vec<_> = value - .bound_vars() - .into_iter() - .map(|var| { - let ty::BoundVariableKind::Region(var) = var else { - // This doesn't really matter because it doesn't get used, - // it's just an empty value - return ty::BrAnon(0, None); - }; - match var { - ty::BrAnon(..) | ty::BrEnv => { - start_or_continue(&mut self, "for<", ", "); - let name = next_name(&self); - debug!(?name); - do_continue(&mut self, name); - ty::BrNamed(CRATE_DEF_ID.to_def_id(), name) - } - ty::BrNamed(def_id, kw::UnderscoreLifetime) => { - start_or_continue(&mut self, "for<", ", "); - let name = next_name(&self); - do_continue(&mut self, name); - ty::BrNamed(def_id, name) - } - ty::BrNamed(def_id, name) => { - start_or_continue(&mut self, "for<", ", "); - do_continue(&mut self, name); - ty::BrNamed(def_id, name) - } - } - }) - .collect(); + for var in value.bound_vars().iter() { + start_or_continue(&mut self, "for<", ", "); + write!(self, "{:?}", var)?; + } start_or_continue(&mut self, "", "> "); - - self.tcx.replace_late_bound_regions(value.clone(), |br| { - let kind = regions[br.var.as_usize()]; - self.tcx.mk_region(ty::ReLateBound( - ty::INNERMOST, - ty::BoundRegion { var: br.var, kind }, - )) - }) + (value.clone().skip_binder(), BTreeMap::default()) } else { let tcx = self.tcx; diff --git a/compiler/rustc_middle/src/ty/subst.rs b/compiler/rustc_middle/src/ty/subst.rs index 0660e9b79a70..2bcb2d824842 100644 --- a/compiler/rustc_middle/src/ty/subst.rs +++ b/compiler/rustc_middle/src/ty/subst.rs @@ -506,6 +506,9 @@ impl<'tcx, T: TypeVisitable<'tcx>> TypeVisitable<'tcx> for &'tcx ty::List { } } +/// Similar to [`super::Binder`] except that it tracks early bound generics, i.e. `struct Foo(T)` +/// needs `T` substituted immediately. This type primarily exists to avoid forgetting to call +/// `subst`. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] #[derive(Encodable, Decodable, HashStable)] pub struct EarlyBinder(pub T); diff --git a/compiler/rustc_middle/src/ty/vtable.rs b/compiler/rustc_middle/src/ty/vtable.rs index 5ca51c25a9ce..6eae94511e4d 100644 --- a/compiler/rustc_middle/src/ty/vtable.rs +++ b/compiler/rustc_middle/src/ty/vtable.rs @@ -66,7 +66,7 @@ pub(super) fn vtable_allocation_provider<'tcx>( let layout = tcx .layout_of(ty::ParamEnv::reveal_all().and(ty)) .expect("failed to build vtable representation"); - assert!(!layout.is_unsized(), "can't create a vtable for an unsized type"); + assert!(layout.is_sized(), "can't create a vtable for an unsized type"); let size = layout.size.bytes(); let align = layout.align.abi.bytes(); diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs index f4b4c3fb05a7..f4562cdfb88d 100644 --- a/compiler/rustc_middle/src/values.rs +++ b/compiler/rustc_middle/src/values.rs @@ -185,7 +185,8 @@ fn find_item_ty_spans( }); if check_params && let Some(args) = path.segments.last().unwrap().args { let params_in_repr = tcx.params_in_repr(def_id); - for (i, arg) in args.args.iter().enumerate() { + // the domain size check is needed because the HIR may not be well-formed at this point + for (i, arg) in args.args.iter().enumerate().take(params_in_repr.domain_size()) { if let hir::GenericArg::Type(ty) = arg && params_in_repr.contains(i as u32) { find_item_ty_spans(tcx, ty, needle, spans, seen_representable); } diff --git a/compiler/rustc_mir_build/src/build/block.rs b/compiler/rustc_mir_build/src/build/block.rs index 183db56d7a08..db05592ed0ea 100644 --- a/compiler/rustc_mir_build/src/build/block.rs +++ b/compiler/rustc_mir_build/src/build/block.rs @@ -118,7 +118,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { else_block: Some(else_block), } => { // When lowering the statement `let = else { };`, - // the `` block is nested in the parent scope enclosing this statment. + // the `` block is nested in the parent scope enclosing this statement. // That scope is usually either the enclosing block scope, // or the remainder scope of the last statement. // This is to make sure that temporaries instantiated in `` are dropped diff --git a/compiler/rustc_mir_build/src/build/expr/as_constant.rs b/compiler/rustc_mir_build/src/build/expr/as_constant.rs index 98df9c3f0e8d..7d8a940bde5c 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_constant.rs @@ -9,6 +9,7 @@ use rustc_middle::mir::interpret::{ use rustc_middle::mir::*; use rustc_middle::thir::*; use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, TyCtxt}; +use rustc_span::DUMMY_SP; use rustc_target::abi::Size; impl<'a, 'tcx> Builder<'a, 'tcx> { @@ -26,7 +27,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let literal = match lit_to_mir_constant(tcx, LitToConstInput { lit: &lit.node, ty, neg }) { Ok(c) => c, - Err(LitToConstError::Reported) => ConstantKind::Ty(tcx.const_error(ty)), + Err(LitToConstError::Reported(guar)) => { + ConstantKind::Ty(tcx.const_error_with_guaranteed(ty, guar)) + } Err(LitToConstError::TypeError) => { bug!("encountered type error in `lit_to_mir_constant") } @@ -105,7 +108,15 @@ pub(crate) fn lit_to_mir_constant<'tcx>( let LitToConstInput { lit, ty, neg } = lit_input; let trunc = |n| { let param_ty = ty::ParamEnv::reveal_all().and(ty); - let width = tcx.layout_of(param_ty).map_err(|_| LitToConstError::Reported)?.size; + let width = tcx + .layout_of(param_ty) + .map_err(|_| { + LitToConstError::Reported(tcx.sess.delay_span_bug( + DUMMY_SP, + format!("couldn't compute width of literal: {:?}", lit_input.lit), + )) + })? + .size; trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits()); let result = width.truncate(n); trace!("trunc result: {}", result); @@ -136,12 +147,20 @@ pub(crate) fn lit_to_mir_constant<'tcx>( (ast::LitKind::Int(n, _), ty::Uint(_)) | (ast::LitKind::Int(n, _), ty::Int(_)) => { trunc(if neg { (*n as i128).overflowing_neg().0 as u128 } else { *n })? } - (ast::LitKind::Float(n, _), ty::Float(fty)) => { - parse_float_into_constval(*n, *fty, neg).ok_or(LitToConstError::Reported)? - } + (ast::LitKind::Float(n, _), ty::Float(fty)) => parse_float_into_constval(*n, *fty, neg) + .ok_or_else(|| { + LitToConstError::Reported(tcx.sess.delay_span_bug( + DUMMY_SP, + format!("couldn't parse float literal: {:?}", lit_input.lit), + )) + })?, (ast::LitKind::Bool(b), ty::Bool) => ConstValue::Scalar(Scalar::from_bool(*b)), (ast::LitKind::Char(c), ty::Char) => ConstValue::Scalar(Scalar::from_char(*c)), - (ast::LitKind::Err, _) => return Err(LitToConstError::Reported), + (ast::LitKind::Err, _) => { + return Err(LitToConstError::Reported( + tcx.sess.delay_span_bug(DUMMY_SP, "encountered LitKind::Err during mir build"), + )); + } _ => return Err(LitToConstError::TypeError), }; diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs index f626571b5b2c..85e8801bda3e 100644 --- a/compiler/rustc_mir_build/src/thir/constant.rs +++ b/compiler/rustc_mir_build/src/thir/constant.rs @@ -1,6 +1,7 @@ use rustc_ast as ast; use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput}; use rustc_middle::ty::{self, ParamEnv, ScalarInt, TyCtxt}; +use rustc_span::DUMMY_SP; pub(crate) fn lit_to_const<'tcx>( tcx: TyCtxt<'tcx>, @@ -10,7 +11,15 @@ pub(crate) fn lit_to_const<'tcx>( let trunc = |n| { let param_ty = ParamEnv::reveal_all().and(ty); - let width = tcx.layout_of(param_ty).map_err(|_| LitToConstError::Reported)?.size; + let width = tcx + .layout_of(param_ty) + .map_err(|_| { + LitToConstError::Reported(tcx.sess.delay_span_bug( + DUMMY_SP, + format!("couldn't compute width of literal: {:?}", lit_input.lit), + )) + })? + .size; trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits()); let result = width.truncate(n); trace!("trunc result: {}", result); @@ -44,7 +53,11 @@ pub(crate) fn lit_to_const<'tcx>( } (ast::LitKind::Bool(b), ty::Bool) => ty::ValTree::from_scalar_int((*b).into()), (ast::LitKind::Char(c), ty::Char) => ty::ValTree::from_scalar_int((*c).into()), - (ast::LitKind::Err, _) => return Err(LitToConstError::Reported), + (ast::LitKind::Err, _) => { + return Err(LitToConstError::Reported( + tcx.sess.delay_span_bug(DUMMY_SP, "encountered LitKind::Err during mir build"), + )); + } _ => return Err(LitToConstError::TypeError), }; diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 2526522a25c8..80b532aec6c1 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -577,6 +577,9 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { self.errors.push(PatternError::ConstParamInPattern(span)); return PatKind::Wild; } + ConstKind::Error(_) => { + return PatKind::Wild; + } _ => bug!("Expected ConstKind::Param"), }, mir::ConstantKind::Val(_, _) => self.const_to_pat(value, id, span, false).kind, @@ -614,7 +617,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { LitToConstInput { lit: &lit.node, ty: self.typeck_results.expr_ty(expr), neg }; match self.tcx.at(expr.span).lit_to_mir_constant(lit_input) { Ok(constant) => self.const_to_pat(constant, expr.hir_id, lit.span, false).kind, - Err(LitToConstError::Reported) => PatKind::Wild, + Err(LitToConstError::Reported(_)) => PatKind::Wild, Err(LitToConstError::TypeError) => bug!("lower_lit: had type error"), } } diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs index 4e4515888454..4f30e8a0be03 100644 --- a/compiler/rustc_mir_transform/src/const_prop.rs +++ b/compiler/rustc_mir_transform/src/const_prop.rs @@ -385,7 +385,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { // I don't know how return types can seem to be unsized but this happens in the // `type/type-unsatisfiable.rs` test. .filter(|ret_layout| { - !ret_layout.is_unsized() && ret_layout.size < Size::from_bytes(MAX_ALLOC_LIMIT) + ret_layout.is_sized() && ret_layout.size < Size::from_bytes(MAX_ALLOC_LIMIT) }) .unwrap_or_else(|| ecx.layout_of(tcx.types.unit).unwrap()); diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/const_prop_lint.rs index 479c4e577d4e..163446c52e4c 100644 --- a/compiler/rustc_mir_transform/src/const_prop_lint.rs +++ b/compiler/rustc_mir_transform/src/const_prop_lint.rs @@ -199,7 +199,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { // I don't know how return types can seem to be unsized but this happens in the // `type/type-unsatisfiable.rs` test. .filter(|ret_layout| { - !ret_layout.is_unsized() && ret_layout.size < Size::from_bytes(MAX_ALLOC_LIMIT) + ret_layout.is_sized() && ret_layout.size < Size::from_bytes(MAX_ALLOC_LIMIT) }) .unwrap_or_else(|| ecx.layout_of(tcx.types.unit).unwrap()); diff --git a/compiler/rustc_mir_transform/src/required_consts.rs b/compiler/rustc_mir_transform/src/required_consts.rs index cc75947d9dda..0ea8f2ba93fd 100644 --- a/compiler/rustc_mir_transform/src/required_consts.rs +++ b/compiler/rustc_mir_transform/src/required_consts.rs @@ -17,7 +17,7 @@ impl<'tcx> Visitor<'tcx> for RequiredConstsVisitor<'_, 'tcx> { let literal = constant.literal; match literal { ConstantKind::Ty(c) => match c.kind() { - ConstKind::Param(_) => {} + ConstKind::Param(_) | ConstKind::Error(_) => {} _ => bug!("only ConstKind::Param should be encountered here, got {:#?}", c), }, ConstantKind::Unevaluated(..) => self.required_consts.push(*constant), diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index e3acc11811f4..a39398950a53 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -1165,10 +1165,12 @@ pub(crate) struct ParenthesesInForHead { #[derive(Subdiagnostic)] #[multipart_suggestion(suggestion, applicability = "machine-applicable")] pub(crate) struct ParenthesesInForHeadSugg { - #[suggestion_part(code = "")] + #[suggestion_part(code = "{left_snippet}")] pub left: Span, - #[suggestion_part(code = "")] + pub left_snippet: String, + #[suggestion_part(code = "{right_snippet}")] pub right: Span, + pub right_snippet: String, } #[derive(Diagnostic)] @@ -1278,3 +1280,24 @@ pub(crate) struct DoubleColonInBound { #[suggestion(code = ": ", applicability = "machine-applicable")] pub between: Span, } + +#[derive(Diagnostic)] +#[diag(parser_fn_ptr_with_generics)] +pub(crate) struct FnPtrWithGenerics { + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub sugg: Option, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(suggestion, applicability = "maybe-incorrect")] +pub(crate) struct FnPtrWithGenericsSugg { + #[suggestion_part(code = "{snippet}")] + pub left: Span, + pub snippet: String, + #[suggestion_part(code = "")] + pub right: Span, + pub arity: usize, + pub for_param_list_exists: bool, +} diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 3dcadb4c9115..c78479b098ba 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -3,6 +3,7 @@ #![feature(array_windows)] #![feature(box_patterns)] #![feature(if_let_guard)] +#![feature(iter_intersperse)] #![feature(let_chains)] #![feature(never_type)] #![feature(rustc_attrs)] diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index 1b16ecb5ec2d..c8160548763c 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -5,7 +5,8 @@ use rustc_ast::tokenstream::{AttrTokenTree, DelimSpan, LazyAttrTokenStream, Spac use rustc_ast::{self as ast}; use rustc_ast::{AttrVec, Attribute, HasAttrs, HasTokens}; use rustc_errors::PResult; -use rustc_span::{sym, Span}; +use rustc_session::parse::ParseSess; +use rustc_span::{sym, Span, DUMMY_SP}; use std::convert::TryInto; use std::ops::Range; @@ -39,8 +40,13 @@ impl AttrWrapper { pub fn empty() -> AttrWrapper { AttrWrapper { attrs: AttrVec::new(), start_pos: usize::MAX } } - // FIXME: Delay span bug here? - pub(crate) fn take_for_recovery(self) -> AttrVec { + + pub(crate) fn take_for_recovery(self, sess: &ParseSess) -> AttrVec { + sess.span_diagnostic.delay_span_bug( + self.attrs.get(0).map(|attr| attr.span).unwrap_or(DUMMY_SP), + "AttrVec is taken for recovery but no error is produced", + ); + self.attrs } diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index c609aa93da3a..0bbe073fe2af 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -1653,15 +1653,29 @@ impl<'a> Parser<'a> { (token::CloseDelim(Delimiter::Parenthesis), Some(begin_par_sp)) => { self.bump(); + let sm = self.sess.source_map(); + let left = begin_par_sp; + let right = self.prev_token.span; + let left_snippet = if let Ok(snip) = sm.span_to_prev_source(left) && + !snip.ends_with(" ") { + " ".to_string() + } else { + "".to_string() + }; + + let right_snippet = if let Ok(snip) = sm.span_to_next_source(right) && + !snip.starts_with(" ") { + " ".to_string() + } else { + "".to_string() + }; + self.sess.emit_err(ParenthesesInForHead { - span: vec![begin_par_sp, self.prev_token.span], + span: vec![left, right], // With e.g. `for (x) in y)` this would replace `(x) in y)` // with `x) in y)` which is syntactically invalid. // However, this is prevented before we get here. - sugg: ParenthesesInForHeadSugg { - left: begin_par_sp, - right: self.prev_token.span, - }, + sugg: ParenthesesInForHeadSugg { left, right, left_snippet, right_snippet }, }); // Unwrap `(pat)` into `pat` to avoid the `unused_parens` lint. diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index da2d20e47ee6..b072573af23f 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -33,6 +33,7 @@ use core::mem; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; use rustc_ast::tokenstream::Spacing; +use rustc_ast::util::case::Case; use rustc_ast::util::classify; use rustc_ast::util::literal::LitError; use rustc_ast::util::parser::{prec_let_scrutinee_needs_par, AssocOp, Fixity}; @@ -2090,7 +2091,7 @@ impl<'a> Parser<'a> { if self.eat_keyword(kw::Static) { Movability::Static } else { Movability::Movable }; let asyncness = if self.token.uninterpolated_span().rust_2018() { - self.parse_asyncness() + self.parse_asyncness(Case::Sensitive) } else { Async::No }; @@ -2271,7 +2272,7 @@ impl<'a> Parser<'a> { self.mk_block_err(cond_span.shrink_to_hi()) } } else { - let attrs = self.parse_outer_attributes()?.take_for_recovery(); // For recovery. + let attrs = self.parse_outer_attributes()?; // For recovery. let block = if self.check(&token::OpenDelim(Delimiter::Brace)) { self.parse_block()? } else { @@ -2288,7 +2289,7 @@ impl<'a> Parser<'a> { })? } }; - self.error_on_if_block_attrs(lo, false, block.span, &attrs); + self.error_on_if_block_attrs(lo, false, block.span, attrs); block }; let els = if self.eat_keyword(kw::Else) { Some(self.parse_else_expr()?) } else { None }; @@ -2349,7 +2350,7 @@ impl<'a> Parser<'a> { /// Parses an `else { ... }` expression (`else` token already eaten). fn parse_else_expr(&mut self) -> PResult<'a, P> { let else_span = self.prev_token.span; // `else` - let attrs = self.parse_outer_attributes()?.take_for_recovery(); // For recovery. + let attrs = self.parse_outer_attributes()?; // For recovery. let expr = if self.eat_keyword(kw::If) { self.parse_if_expr()? } else if self.check(&TokenKind::OpenDelim(Delimiter::Brace)) { @@ -2384,7 +2385,7 @@ impl<'a> Parser<'a> { }, } }; - self.error_on_if_block_attrs(else_span, true, expr.span, &attrs); + self.error_on_if_block_attrs(else_span, true, expr.span, attrs); Ok(expr) } @@ -2393,8 +2394,13 @@ impl<'a> Parser<'a> { ctx_span: Span, is_ctx_else: bool, branch_span: Span, - attrs: &[ast::Attribute], + attrs: AttrWrapper, ) { + if attrs.is_empty() { + return; + } + + let attrs: &[ast::Attribute] = &attrs.take_for_recovery(self.sess); let (attributes, last) = match attrs { [] => return, [x0 @ xn] | [x0, .., xn] => (x0.span.to(xn.span), xn.span), diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index bda301c52e96..494f0cf56a80 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -8,6 +8,7 @@ use rustc_ast::ast::*; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, TokenKind}; use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree}; +use rustc_ast::util::case::Case; use rustc_ast::{self as ast, AttrVec, Attribute, DUMMY_NODE_ID}; use rustc_ast::{Async, Const, Defaultness, IsAuto, Mutability, Unsafe, UseTree, UseTreeKind}; use rustc_ast::{BindingAnnotation, Block, FnDecl, FnSig, Param, SelfKind}; @@ -34,7 +35,7 @@ impl<'a> Parser<'a> { /// Parses a `mod { ... }` or `mod ;` item. fn parse_item_mod(&mut self, attrs: &mut AttrVec) -> PResult<'a, ItemInfo> { - let unsafety = self.parse_unsafety(); + let unsafety = self.parse_unsafety(Case::Sensitive); self.expect_keyword(kw::Mod)?; let id = self.parse_ident()?; let mod_kind = if self.eat(&token::Semi) { @@ -143,8 +144,15 @@ impl<'a> Parser<'a> { let lo = self.token.span; let vis = self.parse_visibility(FollowedByType::No)?; let mut def = self.parse_defaultness(); - let kind = - self.parse_item_kind(&mut attrs, mac_allowed, lo, &vis, &mut def, fn_parse_mode)?; + let kind = self.parse_item_kind( + &mut attrs, + mac_allowed, + lo, + &vis, + &mut def, + fn_parse_mode, + Case::Sensitive, + )?; if let Some((ident, kind)) = kind { self.error_on_unconsumed_default(def, &kind); let span = lo.to(self.prev_token.span); @@ -205,16 +213,17 @@ impl<'a> Parser<'a> { vis: &Visibility, def: &mut Defaultness, fn_parse_mode: FnParseMode, + case: Case, ) -> PResult<'a, Option> { let def_final = def == &Defaultness::Final; - let mut def = || mem::replace(def, Defaultness::Final); + let mut def_ = || mem::replace(def, Defaultness::Final); - let info = if self.eat_keyword(kw::Use) { + let info = if self.eat_keyword_case(kw::Use, case) { self.parse_use_item()? - } else if self.check_fn_front_matter(def_final) { + } else if self.check_fn_front_matter(def_final, case) { // FUNCTION ITEM let (ident, sig, generics, body) = self.parse_fn(attrs, fn_parse_mode, lo, vis)?; - (ident, ItemKind::Fn(Box::new(Fn { defaultness: def(), sig, generics, body }))) + (ident, ItemKind::Fn(Box::new(Fn { defaultness: def_(), sig, generics, body }))) } else if self.eat_keyword(kw::Extern) { if self.eat_keyword(kw::Crate) { // EXTERN CRATE @@ -225,7 +234,7 @@ impl<'a> Parser<'a> { } } else if self.is_unsafe_foreign_mod() { // EXTERN BLOCK - let unsafety = self.parse_unsafety(); + let unsafety = self.parse_unsafety(Case::Sensitive); self.expect_keyword(kw::Extern)?; self.parse_item_foreign_mod(attrs, unsafety)? } else if self.is_static_global() { @@ -234,15 +243,15 @@ impl<'a> Parser<'a> { let m = self.parse_mutability(); let (ident, ty, expr) = self.parse_item_global(Some(m))?; (ident, ItemKind::Static(ty, m, expr)) - } else if let Const::Yes(const_span) = self.parse_constness() { + } else if let Const::Yes(const_span) = self.parse_constness(Case::Sensitive) { // CONST ITEM if self.token.is_keyword(kw::Impl) { // recover from `const impl`, suggest `impl const` - self.recover_const_impl(const_span, attrs, def())? + self.recover_const_impl(const_span, attrs, def_())? } else { self.recover_const_mut(const_span); let (ident, ty, expr) = self.parse_item_global(None)?; - (ident, ItemKind::Const(def(), ty, expr)) + (ident, ItemKind::Const(def_(), ty, expr)) } } else if self.check_keyword(kw::Trait) || self.check_auto_or_unsafe_trait_item() { // TRAIT ITEM @@ -251,7 +260,7 @@ impl<'a> Parser<'a> { || self.check_keyword(kw::Unsafe) && self.is_keyword_ahead(1, &[kw::Impl]) { // IMPL ITEM - self.parse_item_impl(attrs, def())? + self.parse_item_impl(attrs, def_())? } else if self.check_keyword(kw::Mod) || self.check_keyword(kw::Unsafe) && self.is_keyword_ahead(1, &[kw::Mod]) { @@ -259,7 +268,7 @@ impl<'a> Parser<'a> { self.parse_item_mod(attrs)? } else if self.eat_keyword(kw::Type) { // TYPE ITEM - self.parse_type_alias(def())? + self.parse_type_alias(def_())? } else if self.eat_keyword(kw::Enum) { // ENUM ITEM self.parse_item_enum()? @@ -286,6 +295,19 @@ impl<'a> Parser<'a> { } else if self.isnt_macro_invocation() && vis.kind.is_pub() { self.recover_missing_kw_before_item()?; return Ok(None); + } else if self.isnt_macro_invocation() && case == Case::Sensitive { + _ = def_; + + // Recover wrong cased keywords + return self.parse_item_kind( + attrs, + macros_allowed, + lo, + vis, + def, + fn_parse_mode, + Case::Insensitive, + ); } else if macros_allowed && self.check_path() { // MACRO INVOCATION ITEM (Ident::empty(), ItemKind::MacCall(P(self.parse_item_macro(vis)?))) @@ -538,7 +560,7 @@ impl<'a> Parser<'a> { attrs: &mut AttrVec, defaultness: Defaultness, ) -> PResult<'a, ItemInfo> { - let unsafety = self.parse_unsafety(); + let unsafety = self.parse_unsafety(Case::Sensitive); self.expect_keyword(kw::Impl)?; // First, parse generic parameters if necessary. @@ -552,7 +574,7 @@ impl<'a> Parser<'a> { generics }; - let constness = self.parse_constness(); + let constness = self.parse_constness(Case::Sensitive); if let Const::Yes(span) = constness { self.sess.gated_spans.gate(sym::const_trait_impl, span); } @@ -796,7 +818,7 @@ impl<'a> Parser<'a> { /// Parses `unsafe? auto? trait Foo { ... }` or `trait Foo = Bar;`. fn parse_item_trait(&mut self, attrs: &mut AttrVec, lo: Span) -> PResult<'a, ItemInfo> { - let unsafety = self.parse_unsafety(); + let unsafety = self.parse_unsafety(Case::Sensitive); // Parse optional `auto` prefix. let is_auto = if self.eat_keyword(kw::Auto) { IsAuto::Yes } else { IsAuto::No }; @@ -971,6 +993,23 @@ impl<'a> Parser<'a> { if self.eat(&token::ModSep) { self.parse_use_tree_glob_or_nested()? } else { + // Recover from using a colon as path separator. + while self.eat_noexpect(&token::Colon) { + self.struct_span_err(self.prev_token.span, "expected `::`, found `:`") + .span_suggestion_short( + self.prev_token.span, + "use double colon", + "::", + Applicability::MachineApplicable, + ) + .note_once("import paths are delimited using `::`") + .emit(); + + // We parse the rest of the path and append it to the original prefix. + self.parse_path_segments(&mut prefix.segments, PathStyle::Mod, None)?; + prefix.span = lo.to(self.prev_token.span); + } + UseTreeKind::Simple(self.parse_rename()?, DUMMY_NODE_ID, DUMMY_NODE_ID) } }; @@ -1745,7 +1784,7 @@ impl<'a> Parser<'a> { let (ident, is_raw) = self.ident_or_err()?; if !is_raw && ident.is_reserved() { let snapshot = self.create_snapshot_for_diagnostic(); - let err = if self.check_fn_front_matter(false) { + let err = if self.check_fn_front_matter(false, Case::Sensitive) { let inherited_vis = Visibility { span: rustc_span::DUMMY_SP, kind: VisibilityKind::Inherited, @@ -2155,7 +2194,7 @@ impl<'a> Parser<'a> { /// /// `check_pub` adds additional `pub` to the checks in case users place it /// wrongly, can be used to ensure `pub` never comes after `default`. - pub(super) fn check_fn_front_matter(&mut self, check_pub: bool) -> bool { + pub(super) fn check_fn_front_matter(&mut self, check_pub: bool, case: Case) -> bool { // We use an over-approximation here. // `const const`, `fn const` won't parse, but we're not stepping over other syntax either. // `pub` is added in case users got confused with the ordering like `async pub fn`, @@ -2165,23 +2204,30 @@ impl<'a> Parser<'a> { } else { &[kw::Const, kw::Async, kw::Unsafe, kw::Extern] }; - self.check_keyword(kw::Fn) // Definitely an `fn`. + self.check_keyword_case(kw::Fn, case) // Definitely an `fn`. // `$qual fn` or `$qual $qual`: - || quals.iter().any(|&kw| self.check_keyword(kw)) + || quals.iter().any(|&kw| self.check_keyword_case(kw, case)) && self.look_ahead(1, |t| { // `$qual fn`, e.g. `const fn` or `async fn`. - t.is_keyword(kw::Fn) + t.is_keyword_case(kw::Fn, case) // Two qualifiers `$qual $qual` is enough, e.g. `async unsafe`. - || t.is_non_raw_ident_where(|i| quals.contains(&i.name) - // Rule out 2015 `const async: T = val`. - && i.is_reserved() + || ( + ( + t.is_non_raw_ident_where(|i| + quals.contains(&i.name) + // Rule out 2015 `const async: T = val`. + && i.is_reserved() + ) + || case == Case::Insensitive + && t.is_non_raw_ident_where(|i| quals.iter().any(|qual| qual.as_str() == i.name.as_str().to_lowercase())) + ) // Rule out unsafe extern block. && !self.is_unsafe_foreign_mod()) }) // `extern ABI fn` - || self.check_keyword(kw::Extern) + || self.check_keyword_case(kw::Extern, case) && self.look_ahead(1, |t| t.can_begin_literal_maybe_minus()) - && self.look_ahead(2, |t| t.is_keyword(kw::Fn)) + && self.look_ahead(2, |t| t.is_keyword_case(kw::Fn, case)) } /// Parses all the "front matter" (or "qualifiers") for a `fn` declaration, @@ -2197,22 +2243,22 @@ impl<'a> Parser<'a> { /// `Visibility::Inherited` when no visibility is known. pub(super) fn parse_fn_front_matter(&mut self, orig_vis: &Visibility) -> PResult<'a, FnHeader> { let sp_start = self.token.span; - let constness = self.parse_constness(); + let constness = self.parse_constness(Case::Insensitive); let async_start_sp = self.token.span; - let asyncness = self.parse_asyncness(); + let asyncness = self.parse_asyncness(Case::Insensitive); let unsafe_start_sp = self.token.span; - let unsafety = self.parse_unsafety(); + let unsafety = self.parse_unsafety(Case::Insensitive); let ext_start_sp = self.token.span; - let ext = self.parse_extern(); + let ext = self.parse_extern(Case::Insensitive); if let Async::Yes { span, .. } = asyncness { self.ban_async_in_2015(span); } - if !self.eat_keyword(kw::Fn) { + if !self.eat_keyword_case(kw::Fn, Case::Insensitive) { // It is possible for `expect_one_of` to recover given the contents of // `self.expected_tokens`, therefore, do not use `self.unexpected()` which doesn't // account for this. diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 2e59c005e315..14dc490fb023 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -22,6 +22,7 @@ use rustc_ast::token::{self, Delimiter, Nonterminal, Token, TokenKind}; use rustc_ast::tokenstream::AttributesData; use rustc_ast::tokenstream::{self, DelimSpan, Spacing}; use rustc_ast::tokenstream::{TokenStream, TokenTree}; +use rustc_ast::util::case::Case; use rustc_ast::AttrId; use rustc_ast::DUMMY_NODE_ID; use rustc_ast::{self as ast, AnonConst, AttrStyle, AttrVec, Const, Extern}; @@ -636,6 +637,20 @@ impl<'a> Parser<'a> { self.token.is_keyword(kw) } + fn check_keyword_case(&mut self, kw: Symbol, case: Case) -> bool { + if self.check_keyword(kw) { + return true; + } + + if case == Case::Insensitive + && let Some((ident, /* is_raw */ false)) = self.token.ident() + && ident.as_str().to_lowercase() == kw.as_str().to_lowercase() { + true + } else { + false + } + } + /// If the next token is the given keyword, eats it and returns `true`. /// Otherwise, returns `false`. An expectation is also added for diagnostics purposes. // Public for rustfmt usage. @@ -648,6 +663,33 @@ impl<'a> Parser<'a> { } } + /// Eats a keyword, optionally ignoring the case. + /// If the case differs (and is ignored) an error is issued. + /// This is useful for recovery. + fn eat_keyword_case(&mut self, kw: Symbol, case: Case) -> bool { + if self.eat_keyword(kw) { + return true; + } + + if case == Case::Insensitive + && let Some((ident, /* is_raw */ false)) = self.token.ident() + && ident.as_str().to_lowercase() == kw.as_str().to_lowercase() { + self + .struct_span_err(ident.span, format!("keyword `{kw}` is written in a wrong case")) + .span_suggestion( + ident.span, + "write it in the correct case", + kw, + Applicability::MachineApplicable + ).emit(); + + self.bump(); + return true; + } + + false + } + fn eat_keyword_noexpect(&mut self, kw: Symbol) -> bool { if self.token.is_keyword(kw) { self.bump(); @@ -1127,8 +1169,8 @@ impl<'a> Parser<'a> { } /// Parses asyncness: `async` or nothing. - fn parse_asyncness(&mut self) -> Async { - if self.eat_keyword(kw::Async) { + fn parse_asyncness(&mut self, case: Case) -> Async { + if self.eat_keyword_case(kw::Async, case) { let span = self.prev_token.uninterpolated_span(); Async::Yes { span, closure_id: DUMMY_NODE_ID, return_impl_trait_id: DUMMY_NODE_ID } } else { @@ -1137,8 +1179,8 @@ impl<'a> Parser<'a> { } /// Parses unsafety: `unsafe` or nothing. - fn parse_unsafety(&mut self) -> Unsafe { - if self.eat_keyword(kw::Unsafe) { + fn parse_unsafety(&mut self, case: Case) -> Unsafe { + if self.eat_keyword_case(kw::Unsafe, case) { Unsafe::Yes(self.prev_token.uninterpolated_span()) } else { Unsafe::No @@ -1146,10 +1188,10 @@ impl<'a> Parser<'a> { } /// Parses constness: `const` or nothing. - fn parse_constness(&mut self) -> Const { + fn parse_constness(&mut self, case: Case) -> Const { // Avoid const blocks to be parsed as const items if self.look_ahead(1, |t| t != &token::OpenDelim(Delimiter::Brace)) - && self.eat_keyword(kw::Const) + && self.eat_keyword_case(kw::Const, case) { Const::Yes(self.prev_token.uninterpolated_span()) } else { @@ -1404,8 +1446,8 @@ impl<'a> Parser<'a> { } /// Parses `extern string_literal?`. - fn parse_extern(&mut self) -> Extern { - if self.eat_keyword(kw::Extern) { + fn parse_extern(&mut self, case: Case) -> Extern { + if self.eat_keyword_case(kw::Extern, case) { let mut extern_span = self.prev_token.span; let abi = self.parse_abi(); if let Some(abi) = abi { diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index fdc1af27f82e..d46565dea893 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -631,7 +631,9 @@ impl<'a> Parser<'a> { /// - A single-segment path. pub(super) fn expr_is_valid_const_arg(&self, expr: &P) -> bool { match &expr.kind { - ast::ExprKind::Block(_, _) | ast::ExprKind::Lit(_) => true, + ast::ExprKind::Block(_, _) + | ast::ExprKind::Lit(_) + | ast::ExprKind::IncludedBytes(..) => true, ast::ExprKind::Unary(ast::UnOp::Neg, expr) => { matches!(expr.kind, ast::ExprKind::Lit(_)) } diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 12753c6785c9..9684145ad994 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -19,7 +19,7 @@ use rustc_ast as ast; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, TokenKind}; use rustc_ast::util::classify; -use rustc_ast::{AttrStyle, AttrVec, Attribute, LocalKind, MacCall, MacCallStmt, MacStmtStyle}; +use rustc_ast::{AttrStyle, AttrVec, LocalKind, MacCall, MacCallStmt, MacStmtStyle}; use rustc_ast::{Block, BlockCheckMode, Expr, ExprKind, HasAttrs, Local, Stmt}; use rustc_ast::{StmtKind, DUMMY_NODE_ID}; use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, PResult}; @@ -101,7 +101,7 @@ impl<'a> Parser<'a> { self.mk_stmt(lo.to(item.span), StmtKind::Item(P(item))) } else if self.eat(&token::Semi) { // Do not attempt to parse an expression if we're done here. - self.error_outer_attrs(&attrs.take_for_recovery()); + self.error_outer_attrs(attrs); self.mk_stmt(lo, StmtKind::Empty) } else if self.token != token::CloseDelim(Delimiter::Brace) { // Remainder are line-expr stmts. @@ -120,7 +120,7 @@ impl<'a> Parser<'a> { } self.mk_stmt(lo.to(e.span), StmtKind::Expr(e)) } else { - self.error_outer_attrs(&attrs.take_for_recovery()); + self.error_outer_attrs(attrs); return Ok(None); })) } @@ -199,8 +199,10 @@ impl<'a> Parser<'a> { /// Error on outer attributes in this context. /// Also error if the previous token was a doc comment. - fn error_outer_attrs(&self, attrs: &[Attribute]) { - if let [.., last] = attrs { + fn error_outer_attrs(&self, attrs: AttrWrapper) { + if !attrs.is_empty() + && let attrs = attrs.take_for_recovery(self.sess) + && let attrs @ [.., last] = &*attrs { if last.is_doc_comment() { self.sess.emit_err(DocCommentDoesNotDocumentAnything { span: last.span, diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 2a8512acf8cf..d6854f070251 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -1,9 +1,11 @@ use super::{Parser, PathStyle, TokenType}; +use crate::errors::{FnPtrWithGenerics, FnPtrWithGenericsSugg}; use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; +use rustc_ast::util::case::Case; use rustc_ast::{ self as ast, BareFnTy, FnRetTy, GenericBound, GenericBounds, GenericParam, Generics, Lifetime, MacCall, MutTy, Mutability, PolyTraitRef, TraitBoundModifier, TraitObjectSyntax, Ty, TyKind, @@ -267,16 +269,21 @@ impl<'a> Parser<'a> { } else if self.eat_keyword(kw::Underscore) { // A type to be inferred `_` TyKind::Infer - } else if self.check_fn_front_matter(false) { + } else if self.check_fn_front_matter(false, Case::Sensitive) { // Function pointer type - self.parse_ty_bare_fn(lo, Vec::new(), recover_return_sign)? + self.parse_ty_bare_fn(lo, Vec::new(), None, recover_return_sign)? } else if self.check_keyword(kw::For) { // Function pointer type or bound list (trait object type) starting with a poly-trait. // `for<'lt> [unsafe] [extern "ABI"] fn (&'lt S) -> T` // `for<'lt> Trait1<'lt> + Trait2 + 'a` let lifetime_defs = self.parse_late_bound_lifetime_defs()?; - if self.check_fn_front_matter(false) { - self.parse_ty_bare_fn(lo, lifetime_defs, recover_return_sign)? + if self.check_fn_front_matter(false, Case::Sensitive) { + self.parse_ty_bare_fn( + lo, + lifetime_defs, + Some(self.prev_token.span.shrink_to_lo()), + recover_return_sign, + )? } else { let path = self.parse_path(PathStyle::Type)?; let parse_plus = allow_plus == AllowPlus::Yes && self.check_plus(); @@ -401,7 +408,7 @@ impl<'a> Parser<'a> { .span_suggestions( span.shrink_to_hi(), "add `mut` or `const` here", - ["mut ".to_string(), "const ".to_string()].into_iter(), + ["mut ".to_string(), "const ".to_string()], Applicability::HasPlaceholders, ) .emit(); @@ -518,7 +525,8 @@ impl<'a> Parser<'a> { fn parse_ty_bare_fn( &mut self, lo: Span, - params: Vec, + mut params: Vec, + param_insertion_point: Option, recover_return_sign: RecoverReturnSign, ) -> PResult<'a, TyKind> { let inherited_vis = rustc_ast::Visibility { @@ -529,6 +537,9 @@ impl<'a> Parser<'a> { let span_start = self.token.span; let ast::FnHeader { ext, unsafety, constness, asyncness } = self.parse_fn_front_matter(&inherited_vis)?; + if self.may_recover() && self.token.kind == TokenKind::Lt { + self.recover_fn_ptr_with_generics(lo, &mut params, param_insertion_point)?; + } let decl = self.parse_fn_decl(|_| false, AllowPlus::No, recover_return_sign)?; let whole_span = lo.to(self.prev_token.span); if let ast::Const::Yes(span) = constness { @@ -544,6 +555,48 @@ impl<'a> Parser<'a> { Ok(TyKind::BareFn(P(BareFnTy { ext, unsafety, generic_params: params, decl, decl_span }))) } + /// Recover from function pointer types with a generic parameter list (e.g. `fn<'a>(&'a str)`). + fn recover_fn_ptr_with_generics( + &mut self, + lo: Span, + params: &mut Vec, + param_insertion_point: Option, + ) -> PResult<'a, ()> { + let generics = self.parse_generics()?; + let arity = generics.params.len(); + + let mut lifetimes: Vec<_> = generics + .params + .into_iter() + .filter(|param| matches!(param.kind, ast::GenericParamKind::Lifetime)) + .collect(); + + let sugg = if !lifetimes.is_empty() { + let snippet = + lifetimes.iter().map(|param| param.ident.as_str()).intersperse(", ").collect(); + + let (left, snippet) = if let Some(span) = param_insertion_point { + (span, if params.is_empty() { snippet } else { format!(", {snippet}") }) + } else { + (lo.shrink_to_lo(), format!("for<{snippet}> ")) + }; + + Some(FnPtrWithGenericsSugg { + left, + snippet, + right: generics.span, + arity, + for_param_list_exists: param_insertion_point.is_some(), + }) + } else { + None + }; + + self.sess.emit_err(FnPtrWithGenerics { span: generics.span, sugg }); + params.append(&mut lifetimes); + Ok(()) + } + /// Emit an error for the given bad function pointer qualifier. fn error_fn_ptr_bad_qualifier(&self, span: Span, qual_span: Span, qual: &str) { self.struct_span_err(span, &format!("an `fn` pointer type cannot be `{}`", qual)) diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 0c34b0e1bf3f..2b6ff0a5cb9d 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -141,7 +141,7 @@ impl CheckAttrVisitor<'_> { sym::collapse_debuginfo => self.check_collapse_debuginfo(attr, span, target), sym::const_trait => self.check_const_trait(attr, span, target), sym::must_not_suspend => self.check_must_not_suspend(&attr, span, target), - sym::must_use => self.check_must_use(hir_id, &attr, span, target), + sym::must_use => self.check_must_use(hir_id, &attr, target), sym::rustc_pass_by_value => self.check_pass_by_value(&attr, span, target), sym::rustc_allow_incoherent_impl => { self.check_allow_incoherent_impl(&attr, span, target) @@ -1176,17 +1176,7 @@ impl CheckAttrVisitor<'_> { } /// Warns against some misuses of `#[must_use]` - fn check_must_use(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool { - let node = self.tcx.hir().get(hir_id); - if let Some(kind) = node.fn_kind() && let rustc_hir::IsAsync::Async = kind.asyncness() { - self.tcx.emit_spanned_lint( - UNUSED_ATTRIBUTES, - hir_id, - attr.span, - errors::MustUseAsync { span } - ); - } - + fn check_must_use(&self, hir_id: HirId, attr: &Attribute, target: Target) -> bool { if !matches!( target, Target::Fn diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index 33220fd2b395..140f02c046a6 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -560,13 +560,14 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { } fn visit_expr(&mut self, e: &'v ast::Expr) { + #[rustfmt::skip] record_variants!( (self, e, e.kind, Id::None, ast, Expr, ExprKind), [ Box, Array, ConstBlock, Call, MethodCall, Tup, Binary, Unary, Lit, Cast, Type, Let, If, While, ForLoop, Loop, Match, Closure, Block, Async, Await, TryBlock, Assign, AssignOp, Field, Index, Range, Underscore, Path, AddrOf, Break, Continue, Ret, - InlineAsm, MacCall, Struct, Repeat, Paren, Try, Yield, Yeet, Err + InlineAsm, MacCall, Struct, Repeat, Paren, Try, Yield, Yeet, IncludedBytes, Err ] ); ast_visit::walk_expr(self, e) diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index c6fe40f72fc6..c181de48a9ad 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -87,6 +87,7 @@ use self::VarKind::*; use rustc_ast::InlineAsmOptions; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; +use rustc_errors::Diagnostic; use rustc_hir as hir; use rustc_hir::def::*; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -1690,7 +1691,7 @@ impl<'tcx> Liveness<'_, 'tcx> { &self, name: &str, opt_body: Option<&hir::Body<'_>>, - err: &mut rustc_errors::DiagnosticBuilder<'_, ()>, + err: &mut Diagnostic, ) -> bool { let mut has_litstring = false; let Some(opt_body) = opt_body else {return false;}; diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 78afa2f25f8e..af49d438a22c 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -536,6 +536,14 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> { return; } + // if the const impl is derived using the `derive_const` attribute, + // then it would be "stable" at least for the impl. + // We gate usages of it using `feature(const_trait_impl)` anyways + // so there is no unstable leakage + if self.tcx.is_builtin_derive(def_id.to_def_id()) { + return; + } + let is_const = self.tcx.is_const_fn(def_id.to_def_id()) || self.tcx.is_const_trait_impl_raw(def_id.to_def_id()); let is_stable = self diff --git a/compiler/rustc_query_impl/src/on_disk_cache.rs b/compiler/rustc_query_impl/src/on_disk_cache.rs index a6cb8f7bd553..eaed9aeb8502 100644 --- a/compiler/rustc_query_impl/src/on_disk_cache.rs +++ b/compiler/rustc_query_impl/src/on_disk_cache.rs @@ -119,12 +119,11 @@ pub type EncodedDepNodeIndex = Vec<(SerializedDepNodeIndex, AbsoluteBytePos)>; struct SourceFileIndex(u32); #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Encodable, Decodable)] -pub struct AbsoluteBytePos(u32); +pub struct AbsoluteBytePos(u64); impl AbsoluteBytePos { fn new(pos: usize) -> AbsoluteBytePos { - debug_assert!(pos <= u32::MAX as usize); - AbsoluteBytePos(pos as u32) + AbsoluteBytePos(pos.try_into().expect("Incremental cache file size overflowed u64.")) } fn to_usize(self) -> usize { diff --git a/compiler/rustc_query_system/src/ich/hcx.rs b/compiler/rustc_query_system/src/ich/hcx.rs index 148eabb38e23..6378ec10875d 100644 --- a/compiler/rustc_query_system/src/ich/hcx.rs +++ b/compiler/rustc_query_system/src/ich/hcx.rs @@ -49,15 +49,13 @@ pub(super) enum BodyResolver<'tcx> { impl<'a> StableHashingContext<'a> { #[inline] - fn new_with_or_without_spans( + pub fn new( sess: &'a Session, definitions: &'a Definitions, cstore: &'a dyn CrateStore, source_span: &'a IndexVec, - always_ignore_spans: bool, ) -> Self { - let hash_spans_initial = - !always_ignore_spans && !sess.opts.unstable_opts.incremental_ignore_spans; + let hash_spans_initial = !sess.opts.unstable_opts.incremental_ignore_spans; StableHashingContext { body_resolver: BodyResolver::Forbidden, @@ -71,33 +69,6 @@ impl<'a> StableHashingContext<'a> { } } - #[inline] - pub fn new( - sess: &'a Session, - definitions: &'a Definitions, - cstore: &'a dyn CrateStore, - source_span: &'a IndexVec, - ) -> Self { - Self::new_with_or_without_spans( - sess, - definitions, - cstore, - source_span, - /*always_ignore_spans=*/ false, - ) - } - - #[inline] - pub fn ignore_spans( - sess: &'a Session, - definitions: &'a Definitions, - cstore: &'a dyn CrateStore, - source_span: &'a IndexVec, - ) -> Self { - let always_ignore_spans = true; - Self::new_with_or_without_spans(sess, definitions, cstore, source_span, always_ignore_spans) - } - #[inline] pub fn without_hir_bodies(&mut self, f: impl FnOnce(&mut StableHashingContext<'_>)) { f(&mut StableHashingContext { body_resolver: BodyResolver::Ignore, ..self.clone() }); @@ -202,10 +173,4 @@ impl<'a> rustc_span::HashStableContext for StableHashingContext<'a> { } } -impl<'a> rustc_data_structures::intern::InternedHashingContext for StableHashingContext<'a> { - fn with_def_path_and_no_spans(&mut self, f: impl FnOnce(&mut Self)) { - self.while_hashing_spans(false, f); - } -} - impl<'a> rustc_session::HashStableContext for StableHashingContext<'a> {} diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 423c57275333..e7e419c9b423 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -469,9 +469,11 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { } // Replace `use foo::{ self };` with `use foo;` + let self_span = source.ident.span; source = module_path.pop().unwrap(); if rename.is_none() { - ident = source.ident; + // Keep the span of `self`, but the name of `foo` + ident = Ident { name: source.ident.name, span: self_span }; } } } else { diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs index fa6d34be0cc3..82dcc7efb1ba 100644 --- a/compiler/rustc_resolve/src/effective_visibilities.rs +++ b/compiler/rustc_resolve/src/effective_visibilities.rs @@ -72,7 +72,7 @@ impl<'r, 'a> EffectiveVisibilitiesVisitor<'r, 'a> { update(node_id); if let ImportKind::Single { additional_ids: (id1, id2), .. } = import.kind { // In theory all the single import IDs have individual visibilities and - // effective visibilities, but in practice these IDs go straigth to HIR + // effective visibilities, but in practice these IDs go straight to HIR // where all their few uses assume that their (effective) visibility // applies to the whole syntactic `use` item. So they all get the same // value which is the maximum of all bindings. Maybe HIR for imports diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 6d2ee25df320..ede67813883d 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -527,6 +527,7 @@ struct DiagnosticMetadata<'ast> { /// Used to detect possible new binding written without `let` and to provide structured suggestion. in_assignment: Option<&'ast Expr>, + is_assign_rhs: bool, /// If we are currently in a trait object definition. Used to point at the bounds when /// encountering a struct or enum. @@ -3963,10 +3964,15 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.resolve_expr(elem, Some(expr)); self.visit_expr(idx); } - ExprKind::Assign(..) => { - let old = self.diagnostic_metadata.in_assignment.replace(expr); - visit::walk_expr(self, expr); - self.diagnostic_metadata.in_assignment = old; + ExprKind::Assign(ref lhs, ref rhs, _) => { + if !self.diagnostic_metadata.is_assign_rhs { + self.diagnostic_metadata.in_assignment = Some(expr); + } + self.visit_expr(lhs); + self.diagnostic_metadata.is_assign_rhs = true; + self.diagnostic_metadata.in_assignment = None; + self.visit_expr(rhs); + self.diagnostic_metadata.is_assign_rhs = false; } _ => { visit::walk_expr(self, expr); diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 346024bb5984..95eff92ef5e2 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -219,26 +219,26 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { let (mod_prefix, mod_str, suggestion) = if path.len() == 1 { debug!(?self.diagnostic_metadata.current_impl_items); debug!(?self.diagnostic_metadata.current_function); - let suggestion = if let Some(items) = self.diagnostic_metadata.current_impl_items + let suggestion = if self.current_trait_ref.is_none() && let Some((fn_kind, _)) = self.diagnostic_metadata.current_function - && self.current_trait_ref.is_none() && let Some(FnCtxt::Assoc(_)) = fn_kind.ctxt() + && let Some(items) = self.diagnostic_metadata.current_impl_items && let Some(item) = items.iter().find(|i| { - if let AssocItemKind::Fn(fn_) = &i.kind - && !fn_.sig.decl.has_self() - && i.ident.name == item_str.name + if let AssocItemKind::Fn(_) = &i.kind && i.ident.name == item_str.name { debug!(?item_str.name); - debug!(?fn_.sig.decl.inputs); return true } false }) + && let AssocItemKind::Fn(fn_) = &item.kind { + debug!(?fn_); + let self_sugg = if fn_.sig.decl.has_self() { "self." } else { "Self::" }; Some(( - item_span, + item_span.shrink_to_lo(), "consider using the associated function", - format!("Self::{}", item.ident) + self_sugg.to_string() )) } else { None @@ -396,11 +396,13 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } fn suggest_self_or_self_ref(&mut self, err: &mut Diagnostic, path: &[Segment], span: Span) { - let is_assoc_fn = self.self_type_is_available(); + if !self.self_type_is_available() { + return; + } let Some(path_last_segment) = path.last() else { return }; let item_str = path_last_segment.ident; // Emit help message for fake-self from other languages (e.g., `this` in Javascript). - if ["this", "my"].contains(&item_str.as_str()) && is_assoc_fn { + if ["this", "my"].contains(&item_str.as_str()) { err.span_suggestion_short( span, "you might have meant to use `self` here instead", @@ -437,7 +439,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { fn try_lookup_name_relaxed( &mut self, - err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, + err: &mut Diagnostic, source: PathSource<'_>, path: &[Segment], span: Span, @@ -451,7 +453,6 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { let is_enum_variant = &|res| matches!(res, Res::Def(DefKind::Variant, _)); let path_str = Segment::names_to_string(path); let ident_span = path.last().map_or(span, |ident| ident.ident.span); - let mut candidates = self .r .lookup_import_candidates(ident, ns, &self.parent_scope, is_expected) @@ -497,7 +498,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { .contains(span) { // Already reported this issue on the lhs of the type ascription. - err.delay_as_bug(); + err.downgrade_to_delayed_bug(); return (true, candidates); } } @@ -616,7 +617,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { fn suggest_trait_and_bounds( &mut self, - err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, + err: &mut Diagnostic, source: PathSource<'_>, res: Option, span: Span, @@ -691,7 +692,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { fn suggest_typo( &mut self, - err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, + err: &mut Diagnostic, source: PathSource<'_>, path: &[Segment], span: Span, @@ -750,7 +751,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { fn err_code_special_cases( &mut self, - err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, + err: &mut Diagnostic, source: PathSource<'_>, path: &[Segment], span: Span, @@ -1544,7 +1545,6 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { _ => None, } } - // Fields are generally expected in the same contexts as locals. if filter_fn(Res::Local(ast::DUMMY_NODE_ID)) { if let Some(node_id) = @@ -1812,29 +1812,22 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { false } - fn let_binding_suggestion(&self, err: &mut Diagnostic, ident_span: Span) -> bool { - // try to give a suggestion for this pattern: `name = 1`, which is common in other languages - let mut added_suggestion = false; - if let Some(Expr { kind: ExprKind::Assign(lhs, _rhs, _), .. }) = self.diagnostic_metadata.in_assignment && + // try to give a suggestion for this pattern: `name = blah`, which is common in other languages + // suggest `let name = blah` to introduce a new binding + fn let_binding_suggestion(&mut self, err: &mut Diagnostic, ident_span: Span) -> bool { + if let Some(Expr { kind: ExprKind::Assign(lhs, .. ), .. }) = self.diagnostic_metadata.in_assignment && let ast::ExprKind::Path(None, _) = lhs.kind { - let sm = self.r.session.source_map(); - let line_span = sm.span_extend_to_line(ident_span); - let ident_name = sm.span_to_snippet(ident_span).unwrap(); - // HACK(chenyukang): make sure ident_name is at the starting of the line to protect against macros - if sm - .span_to_snippet(line_span) - .map_or(false, |s| s.trim().starts_with(&ident_name)) - { + if !ident_span.from_expansion() { err.span_suggestion_verbose( ident_span.shrink_to_lo(), "you might have meant to introduce a new binding", "let ".to_string(), Applicability::MaybeIncorrect, ); - added_suggestion = true; + return true; } } - added_suggestion + false } fn find_module(&mut self, def_id: DefId) -> Option<(Module<'a>, ImportSuggestion)> { @@ -1943,7 +1936,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { err.span_suggestions( span, &msg, - suggestable_variants.into_iter(), + suggestable_variants, Applicability::MaybeIncorrect, ); } @@ -1997,7 +1990,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { err.span_suggestions( span, msg, - suggestable_variants.into_iter(), + suggestable_variants, Applicability::MaybeIncorrect, ); } @@ -2027,7 +2020,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { err.span_suggestions( span, msg, - suggestable_variants_with_placeholders.into_iter(), + suggestable_variants_with_placeholders, Applicability::HasPlaceholders, ); } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index a1ff477c6fef..9ca3588fff45 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1932,6 +1932,11 @@ impl<'a> Resolver<'a> { } } + /// For rustdoc. + pub fn get_partial_res(&self, node_id: NodeId) -> Option { + self.partial_res_map.get(&node_id).copied() + } + /// Retrieves the span of the given `DefId` if `DefId` is in the local crate. #[inline] pub fn opt_span(&self, def_id: DefId) -> Option { diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 9526296f9511..8c7972f8eebb 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -356,7 +356,7 @@ impl<'a> ResolverExpand for Resolver<'a> { has_derive_copy: false, }); let parent_scope = self.invocation_parent_scopes[&expn_id]; - for (i, (path, _, opt_ext)) in entry.resolutions.iter_mut().enumerate() { + for (i, (path, _, opt_ext, _)) in entry.resolutions.iter_mut().enumerate() { if opt_ext.is_none() { *opt_ext = Some( match self.resolve_macro_path( diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index 3baa2e03cbad..7ea028b12efc 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -753,6 +753,50 @@ impl SourceMap { } } + /// Given a 'Span', tries to tell if the next character is '>' + /// and the previous charactoer is '<' after skipping white space + /// return true if wrapped by '<>' + pub fn span_wrapped_by_angle_bracket(&self, span: Span) -> bool { + self.span_to_source(span, |src, start_index, end_index| { + if src.get(start_index..end_index).is_none() { + return Ok(false); + } + // test the right side to match '>' after skipping white space + let end_src = &src[end_index..]; + let mut i = 0; + while let Some(cc) = end_src.chars().nth(i) { + if cc == ' ' { + i = i + 1; + } else if cc == '>' { + // found > in the right; + break; + } else { + // failed to find '>' return false immediately + return Ok(false); + } + } + // test the left side to match '<' after skipping white space + i = start_index; + let start_src = &src[0..start_index]; + while let Some(cc) = start_src.chars().nth(i) { + if cc == ' ' { + if i == 0 { + return Ok(false); + } + i = i - 1; + } else if cc == '<' { + // found < in the left + break; + } else { + // failed to find '<' return false immediately + return Ok(false); + } + } + return Ok(true); + }) + .map_or(false, |is_accessible| is_accessible) + } + /// Given a `Span`, tries to get a shorter span ending just after the first occurrence of `char` /// `c`. pub fn span_through_char(&self, sp: Span, c: char) -> Span { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 54a61483a11a..b48db73618b0 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -620,6 +620,7 @@ symbols! { deref_mut, deref_target, derive, + derive_const, derive_default_enum, destruct, destructuring_assignment, diff --git a/compiler/rustc_target/src/abi/call/loongarch.rs b/compiler/rustc_target/src/abi/call/loongarch.rs new file mode 100644 index 000000000000..d29b479de5da --- /dev/null +++ b/compiler/rustc_target/src/abi/call/loongarch.rs @@ -0,0 +1,342 @@ +use crate::abi::call::{ArgAbi, ArgExtension, CastTarget, FnAbi, PassMode, Reg, RegKind, Uniform}; +use crate::abi::{self, Abi, FieldsShape, HasDataLayout, Size, TyAbiInterface, TyAndLayout}; +use crate::spec::HasTargetSpec; + +#[derive(Copy, Clone)] +enum RegPassKind { + Float(Reg), + Integer(Reg), + Unknown, +} + +#[derive(Copy, Clone)] +enum FloatConv { + FloatPair(Reg, Reg), + Float(Reg), + MixedPair(Reg, Reg), +} + +#[derive(Copy, Clone)] +struct CannotUseFpConv; + +fn is_loongarch_aggregate<'a, Ty>(arg: &ArgAbi<'a, Ty>) -> bool { + match arg.layout.abi { + Abi::Vector { .. } => true, + _ => arg.layout.is_aggregate(), + } +} + +fn should_use_fp_conv_helper<'a, Ty, C>( + cx: &C, + arg_layout: &TyAndLayout<'a, Ty>, + xlen: u64, + flen: u64, + field1_kind: &mut RegPassKind, + field2_kind: &mut RegPassKind, +) -> Result<(), CannotUseFpConv> +where + Ty: TyAbiInterface<'a, C> + Copy, +{ + match arg_layout.abi { + Abi::Scalar(scalar) => match scalar.primitive() { + abi::Int(..) | abi::Pointer => { + if arg_layout.size.bits() > xlen { + return Err(CannotUseFpConv); + } + match (*field1_kind, *field2_kind) { + (RegPassKind::Unknown, _) => { + *field1_kind = RegPassKind::Integer(Reg { + kind: RegKind::Integer, + size: arg_layout.size, + }); + } + (RegPassKind::Float(_), RegPassKind::Unknown) => { + *field2_kind = RegPassKind::Integer(Reg { + kind: RegKind::Integer, + size: arg_layout.size, + }); + } + _ => return Err(CannotUseFpConv), + } + } + abi::F32 | abi::F64 => { + if arg_layout.size.bits() > flen { + return Err(CannotUseFpConv); + } + match (*field1_kind, *field2_kind) { + (RegPassKind::Unknown, _) => { + *field1_kind = + RegPassKind::Float(Reg { kind: RegKind::Float, size: arg_layout.size }); + } + (_, RegPassKind::Unknown) => { + *field2_kind = + RegPassKind::Float(Reg { kind: RegKind::Float, size: arg_layout.size }); + } + _ => return Err(CannotUseFpConv), + } + } + }, + Abi::Vector { .. } | Abi::Uninhabited => return Err(CannotUseFpConv), + Abi::ScalarPair(..) | Abi::Aggregate { .. } => match arg_layout.fields { + FieldsShape::Primitive => { + unreachable!("aggregates can't have `FieldsShape::Primitive`") + } + FieldsShape::Union(_) => { + if !arg_layout.is_zst() { + return Err(CannotUseFpConv); + } + } + FieldsShape::Array { count, .. } => { + for _ in 0..count { + let elem_layout = arg_layout.field(cx, 0); + should_use_fp_conv_helper( + cx, + &elem_layout, + xlen, + flen, + field1_kind, + field2_kind, + )?; + } + } + FieldsShape::Arbitrary { .. } => { + match arg_layout.variants { + abi::Variants::Multiple { .. } => return Err(CannotUseFpConv), + abi::Variants::Single { .. } => (), + } + for i in arg_layout.fields.index_by_increasing_offset() { + let field = arg_layout.field(cx, i); + should_use_fp_conv_helper(cx, &field, xlen, flen, field1_kind, field2_kind)?; + } + } + }, + } + Ok(()) +} + +fn should_use_fp_conv<'a, Ty, C>( + cx: &C, + arg: &TyAndLayout<'a, Ty>, + xlen: u64, + flen: u64, +) -> Option +where + Ty: TyAbiInterface<'a, C> + Copy, +{ + let mut field1_kind = RegPassKind::Unknown; + let mut field2_kind = RegPassKind::Unknown; + if should_use_fp_conv_helper(cx, arg, xlen, flen, &mut field1_kind, &mut field2_kind).is_err() { + return None; + } + match (field1_kind, field2_kind) { + (RegPassKind::Integer(l), RegPassKind::Float(r)) => Some(FloatConv::MixedPair(l, r)), + (RegPassKind::Float(l), RegPassKind::Integer(r)) => Some(FloatConv::MixedPair(l, r)), + (RegPassKind::Float(l), RegPassKind::Float(r)) => Some(FloatConv::FloatPair(l, r)), + (RegPassKind::Float(f), RegPassKind::Unknown) => Some(FloatConv::Float(f)), + _ => None, + } +} + +fn classify_ret<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, xlen: u64, flen: u64) -> bool +where + Ty: TyAbiInterface<'a, C> + Copy, +{ + if let Some(conv) = should_use_fp_conv(cx, &arg.layout, xlen, flen) { + match conv { + FloatConv::Float(f) => { + arg.cast_to(f); + } + FloatConv::FloatPair(l, r) => { + arg.cast_to(CastTarget::pair(l, r)); + } + FloatConv::MixedPair(l, r) => { + arg.cast_to(CastTarget::pair(l, r)); + } + } + return false; + } + + let total = arg.layout.size; + + // "Scalars wider than 2✕XLEN are passed by reference and are replaced in + // the argument list with the address." + // "Aggregates larger than 2✕XLEN bits are passed by reference and are + // replaced in the argument list with the address, as are C++ aggregates + // with nontrivial copy constructors, destructors, or vtables." + if total.bits() > 2 * xlen { + // We rely on the LLVM backend lowering code to lower passing a scalar larger than 2*XLEN. + if is_loongarch_aggregate(arg) { + arg.make_indirect(); + } + return true; + } + + let xlen_reg = match xlen { + 32 => Reg::i32(), + 64 => Reg::i64(), + _ => unreachable!("Unsupported XLEN: {}", xlen), + }; + if is_loongarch_aggregate(arg) { + if total.bits() <= xlen { + arg.cast_to(xlen_reg); + } else { + arg.cast_to(Uniform { unit: xlen_reg, total: Size::from_bits(xlen * 2) }); + } + return false; + } + + // "When passed in registers, scalars narrower than XLEN bits are widened + // according to the sign of their type up to 32 bits, then sign-extended to + // XLEN bits." + extend_integer_width(arg, xlen); + false +} + +fn classify_arg<'a, Ty, C>( + cx: &C, + arg: &mut ArgAbi<'a, Ty>, + xlen: u64, + flen: u64, + is_vararg: bool, + avail_gprs: &mut u64, + avail_fprs: &mut u64, +) where + Ty: TyAbiInterface<'a, C> + Copy, +{ + if !is_vararg { + match should_use_fp_conv(cx, &arg.layout, xlen, flen) { + Some(FloatConv::Float(f)) if *avail_fprs >= 1 => { + *avail_fprs -= 1; + arg.cast_to(f); + return; + } + Some(FloatConv::FloatPair(l, r)) if *avail_fprs >= 2 => { + *avail_fprs -= 2; + arg.cast_to(CastTarget::pair(l, r)); + return; + } + Some(FloatConv::MixedPair(l, r)) if *avail_fprs >= 1 && *avail_gprs >= 1 => { + *avail_gprs -= 1; + *avail_fprs -= 1; + arg.cast_to(CastTarget::pair(l, r)); + return; + } + _ => (), + } + } + + let total = arg.layout.size; + let align = arg.layout.align.abi.bits(); + + // "Scalars wider than 2✕XLEN are passed by reference and are replaced in + // the argument list with the address." + // "Aggregates larger than 2✕XLEN bits are passed by reference and are + // replaced in the argument list with the address, as are C++ aggregates + // with nontrivial copy constructors, destructors, or vtables." + if total.bits() > 2 * xlen { + // We rely on the LLVM backend lowering code to lower passing a scalar larger than 2*XLEN. + if is_loongarch_aggregate(arg) { + arg.make_indirect(); + } + if *avail_gprs >= 1 { + *avail_gprs -= 1; + } + return; + } + + let double_xlen_reg = match xlen { + 32 => Reg::i64(), + 64 => Reg::i128(), + _ => unreachable!("Unsupported XLEN: {}", xlen), + }; + + let xlen_reg = match xlen { + 32 => Reg::i32(), + 64 => Reg::i64(), + _ => unreachable!("Unsupported XLEN: {}", xlen), + }; + + if total.bits() > xlen { + let align_regs = align > xlen; + if is_loongarch_aggregate(arg) { + arg.cast_to(Uniform { + unit: if align_regs { double_xlen_reg } else { xlen_reg }, + total: Size::from_bits(xlen * 2), + }); + } + if align_regs && is_vararg { + *avail_gprs -= *avail_gprs % 2; + } + if *avail_gprs >= 2 { + *avail_gprs -= 2; + } else { + *avail_gprs = 0; + } + return; + } else if is_loongarch_aggregate(arg) { + arg.cast_to(xlen_reg); + if *avail_gprs >= 1 { + *avail_gprs -= 1; + } + return; + } + + // "When passed in registers, scalars narrower than XLEN bits are widened + // according to the sign of their type up to 32 bits, then sign-extended to + // XLEN bits." + if *avail_gprs >= 1 { + extend_integer_width(arg, xlen); + *avail_gprs -= 1; + } +} + +fn extend_integer_width<'a, Ty>(arg: &mut ArgAbi<'a, Ty>, xlen: u64) { + if let Abi::Scalar(scalar) = arg.layout.abi { + if let abi::Int(i, _) = scalar.primitive() { + // 32-bit integers are always sign-extended + if i.size().bits() == 32 && xlen > 32 { + if let PassMode::Direct(ref mut attrs) = arg.mode { + attrs.ext(ArgExtension::Sext); + return; + } + } + } + } + + arg.extend_integer_width_to(xlen); +} + +pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) +where + Ty: TyAbiInterface<'a, C> + Copy, + C: HasDataLayout + HasTargetSpec, +{ + let xlen = cx.data_layout().pointer_size.bits(); + let flen = match &cx.target_spec().llvm_abiname[..] { + "ilp32f" | "lp64f" => 32, + "ilp32d" | "lp64d" => 64, + _ => 0, + }; + + let mut avail_gprs = 8; + let mut avail_fprs = 8; + + if !fn_abi.ret.is_ignore() && classify_ret(cx, &mut fn_abi.ret, xlen, flen) { + avail_gprs -= 1; + } + + for (i, arg) in fn_abi.args.iter_mut().enumerate() { + if arg.is_ignore() { + continue; + } + classify_arg( + cx, + arg, + xlen, + flen, + i >= fn_abi.fixed_count as usize, + &mut avail_gprs, + &mut avail_fprs, + ); + } +} diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs index 9e5f0e4d158b..c5a6f9893b6f 100644 --- a/compiler/rustc_target/src/abi/call/mod.rs +++ b/compiler/rustc_target/src/abi/call/mod.rs @@ -10,6 +10,7 @@ mod arm; mod avr; mod bpf; mod hexagon; +mod loongarch; mod m68k; mod mips; mod mips64; @@ -696,6 +697,7 @@ impl<'a, Ty> FnAbi<'a, Ty> { "amdgpu" => amdgpu::compute_abi_info(cx, self), "arm" => arm::compute_abi_info(cx, self), "avr" => avr::compute_abi_info(self), + "loongarch64" => loongarch::compute_abi_info(cx, self), "m68k" => m68k::compute_abi_info(self), "mips" => mips::compute_abi_info(cx, self), "mips64" => mips64::compute_abi_info(cx, self), diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs index 7171ca7bf895..decbefc2f7c6 100644 --- a/compiler/rustc_target/src/abi/mod.rs +++ b/compiler/rustc_target/src/abi/mod.rs @@ -1083,6 +1083,11 @@ impl Abi { } } + #[inline] + pub fn is_sized(&self) -> bool { + !self.is_unsized() + } + /// Returns `true` if this is a single signed integer scalar #[inline] pub fn is_signed(&self) -> bool { @@ -1490,6 +1495,11 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { self.abi.is_unsized() } + #[inline] + pub fn is_sized(&self) -> bool { + self.abi.is_sized() + } + /// Returns `true` if the type is a ZST and not unsized. pub fn is_zst(&self) -> bool { match self.abi { diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_nto_qnx_710.rs b/compiler/rustc_target/src/spec/aarch64_unknown_nto_qnx_710.rs new file mode 100644 index 000000000000..916b6137b650 --- /dev/null +++ b/compiler/rustc_target/src/spec/aarch64_unknown_nto_qnx_710.rs @@ -0,0 +1,28 @@ +use super::nto_qnx_base; +use crate::spec::{Cc, LinkerFlavor, Lld, Target, TargetOptions}; + +pub fn target() -> Target { + Target { + llvm_target: "aarch64-unknown-unknown".into(), + pointer_width: 64, + // from: https://llvm.org/docs/LangRef.html#data-layout + // e = little endian + // m:e = ELF mangling: Private symbols get a .L prefix + // i8:8:32 = 8-bit-integer, minimum_alignment=8, preferred_alignment=32 + // i16:16:32 = 16-bit-integer, minimum_alignment=16, preferred_alignment=32 + // i64:64 = 64-bit-integer, minimum_alignment=64, preferred_alignment=64 + // i128:128 = 128-bit-integer, minimum_alignment=128, preferred_alignment=128 + // n32:64 = 32 and 64 are native integer widths; Elements of this set are considered to support most general arithmetic operations efficiently. + // S128 = 128 bits are the natural alignment of the stack in bits. + data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(), + arch: "aarch64".into(), + options: TargetOptions { + max_atomic_width: Some(128), + pre_link_args: TargetOptions::link_args( + LinkerFlavor::Gnu(Cc::Yes, Lld::No), + &["-Vgcc_ntoaarch64le_cxx"], + ), + ..nto_qnx_base::opts() + }, + } +} diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 1bcb02ecb305..664592b02a12 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -74,6 +74,7 @@ mod linux_musl_base; mod linux_uclibc_base; mod msvc_base; mod netbsd_base; +mod nto_qnx_base; mod openbsd_base; mod redox_base; mod solaris_base; @@ -113,7 +114,7 @@ pub enum Lld { /// relevant now. /// /// The second goal is to keep the number of flavors to the minimum if possible. -/// LLD somewhat forces our hand here because that linker is self-sufficent only if its executable +/// LLD somewhat forces our hand here because that linker is self-sufficient only if its executable /// (`argv[0]`) is named in specific way, otherwise it doesn't work and requires a /// `-flavor LLD_FLAVOR` argument to choose which logic to use. Our shipped `rust-lld` in /// particular is not named in such specific way, so it needs the flavor option, so we make our @@ -1242,6 +1243,9 @@ supported_targets! { ("x86_64-unknown-none", x86_64_unknown_none), ("mips64-openwrt-linux-musl", mips64_openwrt_linux_musl), + + ("aarch64-unknown-nto-qnx7.1.0", aarch64_unknown_nto_qnx_710), + ("x86_64-pc-nto-qnx7.1.0", x86_64_pc_nto_qnx710), } /// Cow-Vec-Str: Cow<'static, [Cow<'static, str>]> @@ -1911,6 +1915,7 @@ impl Target { Abi::Stdcall { unwind } } Abi::System { unwind } => Abi::C { unwind }, + Abi::EfiApi if self.arch == "arm" => Abi::Aapcs { unwind: false }, Abi::EfiApi if self.arch == "x86_64" => Abi::Win64 { unwind: false }, Abi::EfiApi => Abi::C { unwind: false }, diff --git a/compiler/rustc_target/src/spec/nto_qnx_base.rs b/compiler/rustc_target/src/spec/nto_qnx_base.rs new file mode 100644 index 000000000000..6fb581ef5ce3 --- /dev/null +++ b/compiler/rustc_target/src/spec/nto_qnx_base.rs @@ -0,0 +1,19 @@ +use crate::spec::{cvs, RelroLevel, TargetOptions}; + +pub fn opts() -> TargetOptions { + TargetOptions { + crt_static_respected: true, + dynamic_linking: true, + env: "nto71".into(), + executables: true, + families: cvs!["unix"], + has_rpath: true, + has_thread_local: false, + linker: Some("qcc".into()), + os: "nto".into(), + position_independent_executables: true, + static_position_independent_executables: true, + relro_level: RelroLevel::Full, + ..Default::default() + } +} diff --git a/compiler/rustc_target/src/spec/x86_64_pc_nto_qnx710.rs b/compiler/rustc_target/src/spec/x86_64_pc_nto_qnx710.rs new file mode 100644 index 000000000000..e9b3acee2e7f --- /dev/null +++ b/compiler/rustc_target/src/spec/x86_64_pc_nto_qnx710.rs @@ -0,0 +1,21 @@ +use super::nto_qnx_base; +use crate::spec::{Cc, LinkerFlavor, Lld, Target, TargetOptions}; + +pub fn target() -> Target { + Target { + llvm_target: "x86_64-pc-unknown".into(), + pointer_width: 64, + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + .into(), + arch: "x86_64".into(), + options: TargetOptions { + cpu: "x86-64".into(), + max_atomic_width: Some(64), + pre_link_args: TargetOptions::link_args( + LinkerFlavor::Gnu(Cc::Yes, Lld::No), + &["-Vgcc_ntox86_64_cxx"], + ), + ..nto_qnx_base::opts() + }, + } +} diff --git a/compiler/rustc_trait_selection/src/autoderef.rs b/compiler/rustc_trait_selection/src/autoderef.rs index 46ee2f35976a..54c738d83897 100644 --- a/compiler/rustc_trait_selection/src/autoderef.rs +++ b/compiler/rustc_trait_selection/src/autoderef.rs @@ -1,6 +1,6 @@ use crate::errors::AutoDerefReachedRecursionLimit; use crate::traits::query::evaluate_obligation::InferCtxtExt; -use crate::traits::{self, TraitEngine}; +use crate::traits::{self, TraitEngine, TraitEngineExt}; use rustc_hir as hir; use rustc_infer::infer::InferCtxt; use rustc_middle::ty::{self, TraitRef, Ty, TyCtxt}; @@ -139,7 +139,7 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { return None; } - let mut fulfillcx = traits::FulfillmentContext::new_in_snapshot(); + let mut fulfillcx = >::new_in_snapshot(tcx); let normalized_ty = fulfillcx.normalize_projection_type( &self.infcx, self.param_env, diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index 23c3715860ea..19f404cb5b78 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -58,10 +58,10 @@ pub struct NoValueInOnUnimplemented { pub span: Span, } -pub struct NegativePositiveConflict<'a> { +pub struct NegativePositiveConflict<'tcx> { pub impl_span: Span, - pub trait_desc: &'a str, - pub self_desc: &'a Option, + pub trait_desc: ty::TraitRef<'tcx>, + pub self_ty: Option>, pub negative_impl_span: Result, pub positive_impl_span: Result, } @@ -73,10 +73,10 @@ impl IntoDiagnostic<'_> for NegativePositiveConflict<'_> { handler: &Handler, ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> { let mut diag = handler.struct_err(fluent::trait_selection_negative_positive_conflict); - diag.set_arg("trait_desc", self.trait_desc); + diag.set_arg("trait_desc", self.trait_desc.print_only_trait_path().to_string()); diag.set_arg( "self_desc", - self.self_desc.clone().map_or_else(|| String::from("none"), |ty| ty), + self.self_ty.map_or_else(|| "none".to_string(), |ty| ty.to_string()), ); diag.set_span(self.impl_span); diag.code(rustc_errors::error_code!(E0751)); diff --git a/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs b/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs index d32a990f182d..8f9d5eaac9d1 100644 --- a/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs @@ -19,7 +19,7 @@ pub struct FulfillmentContext<'tcx> { } impl FulfillmentContext<'_> { - pub(crate) fn new() -> Self { + pub(super) fn new() -> Self { FulfillmentContext { obligations: FxIndexSet::default(), relationships: FxHashMap::default(), diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 8aab75490a81..3cf2959a9ffc 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -64,13 +64,13 @@ pub fn add_placeholder_note(err: &mut Diagnostic) { /// with a suitably-freshened `ImplHeader` with those types /// substituted. Otherwise, returns `None`. #[instrument(skip(tcx, skip_leak_check), level = "debug")] -pub fn overlapping_impls( - tcx: TyCtxt<'_>, +pub fn overlapping_impls<'tcx>( + tcx: TyCtxt<'tcx>, impl1_def_id: DefId, impl2_def_id: DefId, skip_leak_check: SkipLeakCheck, overlap_mode: OverlapMode, -) -> Option> { +) -> Option> { // Before doing expensive operations like entering an inference context, do // a quick check via fast_reject to tell if the impl headers could possibly // unify. diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index e64586407c92..98c13ffdafb0 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -3,13 +3,14 @@ pub mod on_unimplemented; pub mod suggestions; use super::{ - FulfillmentContext, FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes, - Obligation, ObligationCause, ObligationCauseCode, OutputTypeParameterMismatch, Overflow, - PredicateObligation, SelectionContext, SelectionError, TraitNotObjectSafe, + FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes, Obligation, ObligationCause, + ObligationCauseCode, OutputTypeParameterMismatch, Overflow, PredicateObligation, + SelectionContext, SelectionError, TraitNotObjectSafe, }; use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode}; use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use crate::infer::{self, InferCtxt, TyCtxtInferExt}; +use crate::traits::engine::TraitEngineExt as _; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use crate::traits::query::normalize::AtExt as _; use crate::traits::specialize::to_pretty_impl_header; @@ -352,7 +353,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { }) .to_predicate(self.tcx), ); - let mut fulfill_cx = FulfillmentContext::new_in_snapshot(); + let mut fulfill_cx = >::new_in_snapshot(self.tcx); fulfill_cx.register_predicate_obligation(self, obligation); if fulfill_cx.select_all_or_error(self).is_empty() { return Ok(( diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 02e880a8e6e9..22e7d3ea5dd5 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -1116,7 +1116,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.span_suggestions( span.shrink_to_lo(), "consider borrowing here", - ["&".to_string(), "&mut ".to_string()].into_iter(), + ["&".to_string(), "&mut ".to_string()], Applicability::MaybeIncorrect, ); } else { diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index a417e1440b9e..b486c07f354b 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -85,7 +85,7 @@ static_assert_size!(PendingPredicateObligation<'_>, 72); impl<'a, 'tcx> FulfillmentContext<'tcx> { /// Creates a new fulfillment context. - pub fn new() -> FulfillmentContext<'tcx> { + pub(super) fn new() -> FulfillmentContext<'tcx> { FulfillmentContext { predicates: ObligationForest::new(), relationships: FxHashMap::default(), @@ -93,7 +93,7 @@ impl<'a, 'tcx> FulfillmentContext<'tcx> { } } - pub fn new_in_snapshot() -> FulfillmentContext<'tcx> { + pub(super) fn new_in_snapshot() -> FulfillmentContext<'tcx> { FulfillmentContext { predicates: ObligationForest::new(), relationships: FxHashMap::default(), diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index de158a15d54b..a12f67125bbc 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -959,7 +959,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn coinductive_predicate(&self, predicate: ty::Predicate<'tcx>) -> bool { let result = match predicate.kind().skip_binder() { - ty::PredicateKind::Trait(ref data) => self.tcx().trait_is_auto(data.def_id()), + ty::PredicateKind::Trait(ref data) => self.tcx().trait_is_coinductive(data.def_id()), ty::PredicateKind::WellFormed(_) => true, _ => false, }; diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index 43819b3f490b..7cc12eff20e8 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -10,16 +10,18 @@ //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/specialization.html pub mod specialization_graph; +use rustc_infer::traits::{TraitEngine, TraitEngineExt as _}; use specialization_graph::GraphExt; use crate::errors::NegativePositiveConflict; use crate::infer::{InferCtxt, InferOk, TyCtxtInferExt}; +use crate::traits::engine::TraitEngineExt as _; use crate::traits::select::IntercrateAmbiguityCause; use crate::traits::{self, coherence, FutureCompatOverlapErrorKind, ObligationCause}; use rustc_data_structures::fx::FxIndexSet; -use rustc_errors::{struct_span_err, DiagnosticBuilder, EmissionGuarantee}; +use rustc_errors::{error_code, DelayDm, Diagnostic}; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_middle::ty::{self, ImplSubject, TyCtxt}; +use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt}; use rustc_middle::ty::{InternalSubsts, SubstsRef}; use rustc_session::lint::builtin::COHERENCE_LEAK_CHECK; use rustc_session::lint::builtin::ORDER_DEPENDENT_TRAIT_OBJECTS; @@ -30,10 +32,10 @@ use super::SelectionContext; /// Information pertinent to an overlapping impl error. #[derive(Debug)] -pub struct OverlapError { +pub struct OverlapError<'tcx> { pub with_impl: DefId, - pub trait_desc: String, - pub self_desc: Option, + pub trait_ref: ty::TraitRef<'tcx>, + pub self_ty: Option>, pub intercrate_ambiguity_causes: FxIndexSet, pub involves_placeholder: bool, } @@ -200,36 +202,32 @@ fn fulfill_implication<'tcx>( return Err(()); }; + // Needs to be `in_snapshot` because this function is used to rebase + // substitutions, which may happen inside of a select within a probe. + let mut engine = >::new_in_snapshot(infcx.tcx); // attempt to prove all of the predicates for impl2 given those for impl1 // (which are packed up in penv) + engine.register_predicate_obligations(infcx, obligations.chain(more_obligations)); - infcx.save_and_restore_in_snapshot_flag(|infcx| { - let errors = traits::fully_solve_obligations(&infcx, obligations.chain(more_obligations)); - match &errors[..] { - [] => { - debug!( - "fulfill_implication: an impl for {:?} specializes {:?}", - source_trait, target_trait - ); + let errors = engine.select_all_or_error(infcx); + if !errors.is_empty() { + // no dice! + debug!( + "fulfill_implication: for impls on {:?} and {:?}, \ + could not fulfill: {:?} given {:?}", + source_trait, + target_trait, + errors, + param_env.caller_bounds() + ); + return Err(()); + } - // Now resolve the *substitution* we built for the target earlier, replacing - // the inference variables inside with whatever we got from fulfillment. - Ok(infcx.resolve_vars_if_possible(target_substs)) - } - errors => { - // no dice! - debug!( - "fulfill_implication: for impls on {:?} and {:?}, \ - could not fulfill: {:?} given {:?}", - source_trait, - target_trait, - errors, - param_env.caller_bounds() - ); - Err(()) - } - } - }) + debug!("fulfill_implication: an impl for {:?} specializes {:?}", source_trait, target_trait); + + // Now resolve the *substitution* we built for the target earlier, replacing + // the inference variables inside with whatever we got from fulfillment. + Ok(infcx.resolve_vars_if_possible(target_substs)) } // Query provider for `specialization_graph_of`. @@ -277,9 +275,9 @@ pub(super) fn specialization_graph_provider( // it negatively impacts perf. #[cold] #[inline(never)] -fn report_overlap_conflict( - tcx: TyCtxt<'_>, - overlap: OverlapError, +fn report_overlap_conflict<'tcx>( + tcx: TyCtxt<'tcx>, + overlap: OverlapError<'tcx>, impl_def_id: LocalDefId, used_to_be_allowed: Option, sg: &mut specialization_graph::Graph, @@ -315,9 +313,9 @@ fn report_overlap_conflict( } } -fn report_negative_positive_conflict( - tcx: TyCtxt<'_>, - overlap: &OverlapError, +fn report_negative_positive_conflict<'tcx>( + tcx: TyCtxt<'tcx>, + overlap: &OverlapError<'tcx>, local_impl_def_id: LocalDefId, negative_impl_def_id: DefId, positive_impl_def_id: DefId, @@ -325,17 +323,17 @@ fn report_negative_positive_conflict( ) { let mut err = tcx.sess.create_err(NegativePositiveConflict { impl_span: tcx.def_span(local_impl_def_id), - trait_desc: &overlap.trait_desc, - self_desc: &overlap.self_desc, + trait_desc: overlap.trait_ref, + self_ty: overlap.self_ty, negative_impl_span: tcx.span_of_impl(negative_impl_def_id), positive_impl_span: tcx.span_of_impl(positive_impl_def_id), }); sg.has_errored = Some(err.emit()); } -fn report_conflicting_impls( - tcx: TyCtxt<'_>, - overlap: OverlapError, +fn report_conflicting_impls<'tcx>( + tcx: TyCtxt<'tcx>, + overlap: OverlapError<'tcx>, impl_def_id: LocalDefId, used_to_be_allowed: Option, sg: &mut specialization_graph::Graph, @@ -345,12 +343,12 @@ fn report_conflicting_impls( // Work to be done after we've built the DiagnosticBuilder. We have to define it // now because the struct_lint methods don't return back the DiagnosticBuilder // that's passed in. - fn decorate<'a, 'b, G: EmissionGuarantee>( - tcx: TyCtxt<'_>, - overlap: OverlapError, + fn decorate<'tcx>( + tcx: TyCtxt<'tcx>, + overlap: &OverlapError<'tcx>, impl_span: Span, - err: &'b mut DiagnosticBuilder<'a, G>, - ) -> &'b mut DiagnosticBuilder<'a, G> { + err: &mut Diagnostic, + ) { match tcx.span_of_impl(overlap.with_impl) { Ok(span) => { err.span_label(span, "first implementation here"); @@ -359,7 +357,7 @@ fn report_conflicting_impls( impl_span, format!( "conflicting implementation{}", - overlap.self_desc.map_or_else(String::new, |ty| format!(" for `{}`", ty)) + overlap.self_ty.map_or_else(String::new, |ty| format!(" for `{}`", ty)) ), ); } @@ -381,26 +379,28 @@ fn report_conflicting_impls( if overlap.involves_placeholder { coherence::add_placeholder_note(err); } - err } - let msg = format!( - "conflicting implementations of trait `{}`{}{}", - overlap.trait_desc, - overlap.self_desc.as_deref().map_or_else(String::new, |ty| format!(" for type `{ty}`")), - match used_to_be_allowed { - Some(FutureCompatOverlapErrorKind::Issue33140) => ": (E0119)", - _ => "", - } - ); + let msg = DelayDm(|| { + format!( + "conflicting implementations of trait `{}`{}{}", + overlap.trait_ref.print_only_trait_path(), + overlap.self_ty.map_or_else(String::new, |ty| format!(" for type `{ty}`")), + match used_to_be_allowed { + Some(FutureCompatOverlapErrorKind::Issue33140) => ": (E0119)", + _ => "", + } + ) + }); match used_to_be_allowed { None => { let reported = if overlap.with_impl.is_local() || tcx.orphan_check_impl(impl_def_id).is_ok() { - let mut err = struct_span_err!(tcx.sess, impl_span, E0119, "{msg}",); - decorate(tcx, overlap, impl_span, &mut err); + let mut err = tcx.sess.struct_span_err(impl_span, msg); + err.code(error_code!(E0119)); + decorate(tcx, &overlap, impl_span, &mut err); Some(err.emit()) } else { Some(tcx.sess.delay_span_bug(impl_span, "impl should have failed the orphan check")) @@ -417,7 +417,10 @@ fn report_conflicting_impls( tcx.hir().local_def_id_to_hir_id(impl_def_id), impl_span, msg, - |err| decorate(tcx, overlap, impl_span, err), + |err| { + decorate(tcx, &overlap, impl_span, err); + err + }, ); } }; diff --git a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs index 63f89a33e8ad..4546c9533930 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs @@ -3,7 +3,6 @@ use super::OverlapError; use crate::traits; use rustc_hir::def_id::DefId; use rustc_middle::ty::fast_reject::{self, SimplifiedType, TreatParams}; -use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, TyCtxt, TypeVisitable}; pub use rustc_middle::traits::specialization_graph::*; @@ -15,15 +14,15 @@ pub enum FutureCompatOverlapErrorKind { } #[derive(Debug)] -pub struct FutureCompatOverlapError { - pub error: OverlapError, +pub struct FutureCompatOverlapError<'tcx> { + pub error: OverlapError<'tcx>, pub kind: FutureCompatOverlapErrorKind, } /// The result of attempting to insert an impl into a group of children. -enum Inserted { +enum Inserted<'tcx> { /// The impl was inserted as a new child in this group of children. - BecameNewSibling(Option), + BecameNewSibling(Option>), /// The impl should replace existing impls [X1, ..], because the impl specializes X1, X2, etc. ReplaceChildren(Vec), @@ -42,12 +41,12 @@ trait ChildrenExt<'tcx> { impl_def_id: DefId, simplified_self: Option, overlap_mode: OverlapMode, - ) -> Result; + ) -> Result, OverlapError<'tcx>>; } -impl ChildrenExt<'_> for Children { +impl<'tcx> ChildrenExt<'tcx> for Children { /// Insert an impl into this set of children without comparing to any existing impls. - fn insert_blindly(&mut self, tcx: TyCtxt<'_>, impl_def_id: DefId) { + fn insert_blindly(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId) { let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); if let Some(st) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), TreatParams::AsInfer) { @@ -62,7 +61,7 @@ impl ChildrenExt<'_> for Children { /// Removes an impl from this set of children. Used when replacing /// an impl with a parent. The impl must be present in the list of /// children already. - fn remove_existing(&mut self, tcx: TyCtxt<'_>, impl_def_id: DefId) { + fn remove_existing(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId) { let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); let vec: &mut Vec; if let Some(st) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), TreatParams::AsInfer) @@ -82,11 +81,11 @@ impl ChildrenExt<'_> for Children { /// specialization relationships. fn insert( &mut self, - tcx: TyCtxt<'_>, + tcx: TyCtxt<'tcx>, impl_def_id: DefId, simplified_self: Option, overlap_mode: OverlapMode, - ) -> Result { + ) -> Result, OverlapError<'tcx>> { let mut last_lint = None; let mut replace_children = Vec::new(); @@ -103,30 +102,23 @@ impl ChildrenExt<'_> for Children { impl_def_id, simplified_self, possible_sibling, ); - let create_overlap_error = |overlap: traits::coherence::OverlapResult<'_>| { + let create_overlap_error = |overlap: traits::coherence::OverlapResult<'tcx>| { let trait_ref = overlap.impl_header.trait_ref.unwrap(); let self_ty = trait_ref.self_ty(); - // FIXME: should postpone string formatting until we decide to actually emit. - with_no_trimmed_paths!({ - OverlapError { - with_impl: possible_sibling, - trait_desc: trait_ref.print_only_trait_path().to_string(), - // Only report the `Self` type if it has at least - // some outer concrete shell; otherwise, it's - // not adding much information. - self_desc: if self_ty.has_concrete_skeleton() { - Some(self_ty.to_string()) - } else { - None - }, - intercrate_ambiguity_causes: overlap.intercrate_ambiguity_causes, - involves_placeholder: overlap.involves_placeholder, - } - }) + OverlapError { + with_impl: possible_sibling, + trait_ref, + // Only report the `Self` type if it has at least + // some outer concrete shell; otherwise, it's + // not adding much information. + self_ty: if self_ty.has_concrete_skeleton() { Some(self_ty) } else { None }, + intercrate_ambiguity_causes: overlap.intercrate_ambiguity_causes, + involves_placeholder: overlap.involves_placeholder, + } }; - let report_overlap_error = |overlap: traits::coherence::OverlapResult<'_>, + let report_overlap_error = |overlap: traits::coherence::OverlapResult<'tcx>, last_lint: &mut _| { // Found overlap, but no specialization; error out or report future-compat warning. @@ -255,31 +247,31 @@ where } } -pub trait GraphExt { +pub trait GraphExt<'tcx> { /// Insert a local impl into the specialization graph. If an existing impl /// conflicts with it (has overlap, but neither specializes the other), /// information about the area of overlap is returned in the `Err`. fn insert( &mut self, - tcx: TyCtxt<'_>, + tcx: TyCtxt<'tcx>, impl_def_id: DefId, overlap_mode: OverlapMode, - ) -> Result, OverlapError>; + ) -> Result>, OverlapError<'tcx>>; /// Insert cached metadata mapping from a child impl back to its parent. - fn record_impl_from_cstore(&mut self, tcx: TyCtxt<'_>, parent: DefId, child: DefId); + fn record_impl_from_cstore(&mut self, tcx: TyCtxt<'tcx>, parent: DefId, child: DefId); } -impl GraphExt for Graph { +impl<'tcx> GraphExt<'tcx> for Graph { /// Insert a local impl into the specialization graph. If an existing impl /// conflicts with it (has overlap, but neither specializes the other), /// information about the area of overlap is returned in the `Err`. fn insert( &mut self, - tcx: TyCtxt<'_>, + tcx: TyCtxt<'tcx>, impl_def_id: DefId, overlap_mode: OverlapMode, - ) -> Result, OverlapError> { + ) -> Result>, OverlapError<'tcx>> { assert!(impl_def_id.is_local()); let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); @@ -376,7 +368,7 @@ impl GraphExt for Graph { } /// Insert cached metadata mapping from a child impl back to its parent. - fn record_impl_from_cstore(&mut self, tcx: TyCtxt<'_>, parent: DefId, child: DefId) { + fn record_impl_from_cstore(&mut self, tcx: TyCtxt<'tcx>, parent: DefId, child: DefId) { if self.parent.insert(child, parent).is_some() { bug!( "When recording an impl from the crate store, information about its parent \ diff --git a/compiler/rustc_traits/Cargo.toml b/compiler/rustc_traits/Cargo.toml index 951554c77fbb..9474e6df5677 100644 --- a/compiler/rustc_traits/Cargo.toml +++ b/compiler/rustc_traits/Cargo.toml @@ -12,9 +12,9 @@ rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_ast = { path = "../rustc_ast" } rustc_span = { path = "../rustc_span" } -chalk-ir = "0.80.0" -chalk-engine = "0.80.0" -chalk-solve = "0.80.0" +chalk-ir = "0.87.0" +chalk-engine = "0.87.0" +chalk-solve = "0.87.0" smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } rustc_infer = { path = "../rustc_infer" } rustc_trait_selection = { path = "../rustc_trait_selection" } diff --git a/compiler/rustc_traits/src/chalk/db.rs b/compiler/rustc_traits/src/chalk/db.rs index 07f92299f72b..d15707e5cedd 100644 --- a/compiler/rustc_traits/src/chalk/db.rs +++ b/compiler/rustc_traits/src/chalk/db.rs @@ -142,6 +142,8 @@ impl<'tcx> chalk_solve::RustIrDatabase> for RustIrDatabase<'t Some(CoerceUnsized) } else if lang_items.dispatch_from_dyn_trait() == Some(def_id) { Some(DispatchFromDyn) + } else if lang_items.tuple_trait() == Some(def_id) { + Some(Tuple) } else { None }; @@ -570,6 +572,7 @@ impl<'tcx> chalk_solve::RustIrDatabase> for RustIrDatabase<'t CoerceUnsized => lang_items.coerce_unsized_trait(), DiscriminantKind => lang_items.discriminant_kind_trait(), DispatchFromDyn => lang_items.dispatch_from_dyn_trait(), + Tuple => lang_items.tuple_trait(), }; def_id.map(chalk_ir::TraitId) } diff --git a/compiler/rustc_traits/src/chalk/lowering.rs b/compiler/rustc_traits/src/chalk/lowering.rs index b64d53e60dee..25cedefa2612 100644 --- a/compiler/rustc_traits/src/chalk/lowering.rs +++ b/compiler/rustc_traits/src/chalk/lowering.rs @@ -507,9 +507,6 @@ impl<'tcx> LowerInto<'tcx, Region<'tcx>> for &chalk_ir::Lifetime return interner.tcx.lifetimes.re_static, - chalk_ir::LifetimeData::Empty(_) => { - bug!("Chalk should not have been passed an empty lifetime.") - } chalk_ir::LifetimeData::Erased => return interner.tcx.lifetimes.re_erased, chalk_ir::LifetimeData::Phantom(void, _) => match *void {}, }; diff --git a/compiler/rustc_traits/src/dropck_outlives.rs b/compiler/rustc_traits/src/dropck_outlives.rs index d5a8ca5ea784..7b4ad9fea137 100644 --- a/compiler/rustc_traits/src/dropck_outlives.rs +++ b/compiler/rustc_traits/src/dropck_outlives.rs @@ -2,20 +2,18 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::DefId; use rustc_infer::infer::canonical::{Canonical, QueryResponse}; use rustc_infer::infer::TyCtxtInferExt; -use rustc_infer::traits::TraitEngineExt as _; use rustc_middle::ty::query::Providers; use rustc_middle::ty::InternalSubsts; use rustc_middle::ty::{self, EarlyBinder, ParamEnvAnd, Ty, TyCtxt}; use rustc_span::source_map::{Span, DUMMY_SP}; +use rustc_trait_selection::infer::InferCtxtBuilderExt; use rustc_trait_selection::traits::query::dropck_outlives::trivial_dropck_outlives; use rustc_trait_selection::traits::query::dropck_outlives::{ DropckConstraint, DropckOutlivesResult, }; use rustc_trait_selection::traits::query::normalize::AtExt; use rustc_trait_selection::traits::query::{CanonicalTyGoal, NoSolution}; -use rustc_trait_selection::traits::{ - Normalized, ObligationCause, TraitEngine, TraitEngineExt as _, -}; +use rustc_trait_selection::traits::{Normalized, ObligationCause}; pub(crate) fn provide(p: &mut Providers) { *p = Providers { dropck_outlives, adt_dtorck_constraint, ..*p }; @@ -27,120 +25,109 @@ fn dropck_outlives<'tcx>( ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, DropckOutlivesResult<'tcx>>>, NoSolution> { debug!("dropck_outlives(goal={:#?})", canonical_goal); - let (ref infcx, goal, canonical_inference_vars) = - tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical_goal); - let tcx = infcx.tcx; - let ParamEnvAnd { param_env, value: for_ty } = goal; + tcx.infer_ctxt().enter_canonical_trait_query(&canonical_goal, |ocx, goal| { + let tcx = ocx.infcx.tcx; + let ParamEnvAnd { param_env, value: for_ty } = goal; - let mut result = DropckOutlivesResult { kinds: vec![], overflows: vec![] }; + let mut result = DropckOutlivesResult { kinds: vec![], overflows: vec![] }; - // A stack of types left to process. Each round, we pop - // something from the stack and invoke - // `dtorck_constraint_for_ty`. This may produce new types that - // have to be pushed on the stack. This continues until we have explored - // all the reachable types from the type `for_ty`. - // - // Example: Imagine that we have the following code: - // - // ```rust - // struct A { - // value: B, - // children: Vec, - // } - // - // struct B { - // value: u32 - // } - // - // fn f() { - // let a: A = ...; - // .. - // } // here, `a` is dropped - // ``` - // - // at the point where `a` is dropped, we need to figure out - // which types inside of `a` contain region data that may be - // accessed by any destructors in `a`. We begin by pushing `A` - // onto the stack, as that is the type of `a`. We will then - // invoke `dtorck_constraint_for_ty` which will expand `A` - // into the types of its fields `(B, Vec)`. These will get - // pushed onto the stack. Eventually, expanding `Vec` will - // lead to us trying to push `A` a second time -- to prevent - // infinite recursion, we notice that `A` was already pushed - // once and stop. - let mut ty_stack = vec![(for_ty, 0)]; + // A stack of types left to process. Each round, we pop + // something from the stack and invoke + // `dtorck_constraint_for_ty`. This may produce new types that + // have to be pushed on the stack. This continues until we have explored + // all the reachable types from the type `for_ty`. + // + // Example: Imagine that we have the following code: + // + // ```rust + // struct A { + // value: B, + // children: Vec, + // } + // + // struct B { + // value: u32 + // } + // + // fn f() { + // let a: A = ...; + // .. + // } // here, `a` is dropped + // ``` + // + // at the point where `a` is dropped, we need to figure out + // which types inside of `a` contain region data that may be + // accessed by any destructors in `a`. We begin by pushing `A` + // onto the stack, as that is the type of `a`. We will then + // invoke `dtorck_constraint_for_ty` which will expand `A` + // into the types of its fields `(B, Vec)`. These will get + // pushed onto the stack. Eventually, expanding `Vec` will + // lead to us trying to push `A` a second time -- to prevent + // infinite recursion, we notice that `A` was already pushed + // once and stop. + let mut ty_stack = vec![(for_ty, 0)]; - // Set used to detect infinite recursion. - let mut ty_set = FxHashSet::default(); + // Set used to detect infinite recursion. + let mut ty_set = FxHashSet::default(); - let mut fulfill_cx = >::new(infcx.tcx); + let cause = ObligationCause::dummy(); + let mut constraints = DropckConstraint::empty(); + while let Some((ty, depth)) = ty_stack.pop() { + debug!( + "{} kinds, {} overflows, {} ty_stack", + result.kinds.len(), + result.overflows.len(), + ty_stack.len() + ); + dtorck_constraint_for_ty(tcx, DUMMY_SP, for_ty, depth, ty, &mut constraints)?; - let cause = ObligationCause::dummy(); - let mut constraints = DropckConstraint::empty(); - while let Some((ty, depth)) = ty_stack.pop() { - debug!( - "{} kinds, {} overflows, {} ty_stack", - result.kinds.len(), - result.overflows.len(), - ty_stack.len() - ); - dtorck_constraint_for_ty(tcx, DUMMY_SP, for_ty, depth, ty, &mut constraints)?; + // "outlives" represent types/regions that may be touched + // by a destructor. + result.kinds.append(&mut constraints.outlives); + result.overflows.append(&mut constraints.overflows); - // "outlives" represent types/regions that may be touched - // by a destructor. - result.kinds.append(&mut constraints.outlives); - result.overflows.append(&mut constraints.overflows); + // If we have even one overflow, we should stop trying to evaluate further -- + // chances are, the subsequent overflows for this evaluation won't provide useful + // information and will just decrease the speed at which we can emit these errors + // (since we'll be printing for just that much longer for the often enormous types + // that result here). + if !result.overflows.is_empty() { + break; + } - // If we have even one overflow, we should stop trying to evaluate further -- - // chances are, the subsequent overflows for this evaluation won't provide useful - // information and will just decrease the speed at which we can emit these errors - // (since we'll be printing for just that much longer for the often enormous types - // that result here). - if !result.overflows.is_empty() { - break; - } + // dtorck types are "types that will get dropped but which + // do not themselves define a destructor", more or less. We have + // to push them onto the stack to be expanded. + for ty in constraints.dtorck_types.drain(..) { + let Normalized { value: ty, obligations } = + ocx.infcx.at(&cause, param_env).normalize(ty)?; + ocx.register_obligations(obligations); - // dtorck types are "types that will get dropped but which - // do not themselves define a destructor", more or less. We have - // to push them onto the stack to be expanded. - for ty in constraints.dtorck_types.drain(..) { - match infcx.at(&cause, param_env).normalize(ty) { - Ok(Normalized { value: ty, obligations }) => { - fulfill_cx.register_predicate_obligations(infcx, obligations); + debug!("dropck_outlives: ty from dtorck_types = {:?}", ty); - debug!("dropck_outlives: ty from dtorck_types = {:?}", ty); + match ty.kind() { + // All parameters live for the duration of the + // function. + ty::Param(..) => {} - match ty.kind() { - // All parameters live for the duration of the - // function. - ty::Param(..) => {} + // A projection that we couldn't resolve - it + // might have a destructor. + ty::Projection(..) | ty::Opaque(..) => { + result.kinds.push(ty.into()); + } - // A projection that we couldn't resolve - it - // might have a destructor. - ty::Projection(..) | ty::Opaque(..) => { - result.kinds.push(ty.into()); - } - - _ => { - if ty_set.insert(ty) { - ty_stack.push((ty, depth + 1)); - } + _ => { + if ty_set.insert(ty) { + ty_stack.push((ty, depth + 1)); } } } - - // We don't actually expect to fail to normalize. - // That implies a WF error somewhere else. - Err(NoSolution) => { - return Err(NoSolution); - } } } - } - debug!("dropck_outlives: result = {:#?}", result); - - infcx.make_canonicalized_query_response(canonical_inference_vars, result, &mut *fulfill_cx) + debug!("dropck_outlives: result = {:#?}", result); + Ok(result) + }) } /// Returns a set of constraints that needs to be satisfied in diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index 3cef47c0f8ba..cb41c4f94e2e 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -235,7 +235,9 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { neg, }) { Ok(c) => c, - Err(LitToConstError::Reported) => self.tcx.const_error(node.ty), + Err(LitToConstError::Reported(guar)) => { + self.tcx.const_error_with_guaranteed(node.ty, guar) + } Err(LitToConstError::TypeError) => { bug!("encountered type error in lit_to_const") } diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 52ba0eee97cd..b59be0a0ea79 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -668,7 +668,7 @@ fn layout_of_uncached<'tcx>( let mut abi = Abi::Aggregate { sized: true }; let index = VariantIdx::new(0); for field in &variants[index] { - assert!(!field.is_unsized()); + assert!(field.is_sized()); align = align.max(field.align); // If all non-ZST fields have the same ABI, forward this ABI diff --git a/config.toml.example b/config.toml.example index 2373fb2ec4fb..c94a27b12a3a 100644 --- a/config.toml.example +++ b/config.toml.example @@ -87,9 +87,10 @@ changelog-seen = 2 # this flag will indicate that this version check should not be done. #version-check = true -# Link libstdc++ statically into the rustc_llvm instead of relying on a -# dynamic version to be available. -#static-libstdcpp = true +# When true, link libstdc++ statically into the rustc_llvm. +# This is useful if you don't want to use the dynamic version of that +# library provided by LLVM. +#static-libstdcpp = false # Whether to use Ninja to build LLVM. This runs much faster than make. #ninja = true diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs index 8c6663569a55..e5fbfc55761f 100644 --- a/library/alloc/src/alloc.rs +++ b/library/alloc/src/alloc.rs @@ -28,20 +28,16 @@ extern "Rust" { // The rustc fork of LLVM 14 and earlier also special-cases these function names to be able to optimize them // like `malloc`, `realloc`, and `free`, respectively. #[rustc_allocator] - #[cfg_attr(not(bootstrap), rustc_nounwind)] - #[cfg_attr(bootstrap, rustc_allocator_nounwind)] + #[rustc_nounwind] fn __rust_alloc(size: usize, align: usize) -> *mut u8; #[rustc_deallocator] - #[cfg_attr(not(bootstrap), rustc_nounwind)] - #[cfg_attr(bootstrap, rustc_allocator_nounwind)] + #[rustc_nounwind] fn __rust_dealloc(ptr: *mut u8, size: usize, align: usize); #[rustc_reallocator] - #[cfg_attr(not(bootstrap), rustc_nounwind)] - #[cfg_attr(bootstrap, rustc_allocator_nounwind)] + #[rustc_nounwind] fn __rust_realloc(ptr: *mut u8, old_size: usize, align: usize, new_size: usize) -> *mut u8; #[rustc_allocator_zeroed] - #[cfg_attr(not(bootstrap), rustc_nounwind)] - #[cfg_attr(bootstrap, rustc_allocator_nounwind)] + #[rustc_nounwind] fn __rust_alloc_zeroed(size: usize, align: usize) -> *mut u8; } diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 66f4c19e0f91..97186589a4f4 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -1661,7 +1661,7 @@ impl TryFrom> for Box<[T; N]> { } #[cfg(not(no_global_oom_handling))] -#[stable(feature = "boxed_array_try_from_vec", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "boxed_array_try_from_vec", since = "1.66.0")] impl TryFrom> for Box<[T; N]> { type Error = Vec; diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index c4c75e46a2a3..8a7719347123 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -580,7 +580,7 @@ impl BTreeMap { /// map.insert(1, "a"); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_btree_new", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_btree_new", since = "1.66.0")] #[must_use] pub const fn new() -> BTreeMap { BTreeMap { root: None, length: 0, alloc: ManuallyDrop::new(Global), _marker: PhantomData } @@ -711,7 +711,7 @@ impl BTreeMap { /// map.insert(2, "a"); /// assert_eq!(map.first_key_value(), Some((&1, &"b"))); /// ``` - #[stable(feature = "map_first_last", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "map_first_last", since = "1.66.0")] pub fn first_key_value(&self) -> Option<(&K, &V)> where K: Ord, @@ -739,7 +739,7 @@ impl BTreeMap { /// assert_eq!(*map.get(&1).unwrap(), "first"); /// assert_eq!(*map.get(&2).unwrap(), "b"); /// ``` - #[stable(feature = "map_first_last", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "map_first_last", since = "1.66.0")] pub fn first_entry(&mut self) -> Option> where K: Ord, @@ -773,7 +773,7 @@ impl BTreeMap { /// } /// assert!(map.is_empty()); /// ``` - #[stable(feature = "map_first_last", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "map_first_last", since = "1.66.0")] pub fn pop_first(&mut self) -> Option<(K, V)> where K: Ord, @@ -796,7 +796,7 @@ impl BTreeMap { /// map.insert(2, "a"); /// assert_eq!(map.last_key_value(), Some((&2, &"a"))); /// ``` - #[stable(feature = "map_first_last", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "map_first_last", since = "1.66.0")] pub fn last_key_value(&self) -> Option<(&K, &V)> where K: Ord, @@ -824,7 +824,7 @@ impl BTreeMap { /// assert_eq!(*map.get(&1).unwrap(), "a"); /// assert_eq!(*map.get(&2).unwrap(), "last"); /// ``` - #[stable(feature = "map_first_last", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "map_first_last", since = "1.66.0")] pub fn last_entry(&mut self) -> Option> where K: Ord, @@ -858,7 +858,7 @@ impl BTreeMap { /// } /// assert!(map.is_empty()); /// ``` - #[stable(feature = "map_first_last", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "map_first_last", since = "1.66.0")] pub fn pop_last(&mut self) -> Option<(K, V)> where K: Ord, diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index b8e5cf8eb5a8..4ddb21192520 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -343,7 +343,7 @@ impl BTreeSet { /// let mut set: BTreeSet = BTreeSet::new(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_btree_new", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_btree_new", since = "1.66.0")] #[must_use] pub const fn new() -> BTreeSet { BTreeSet { map: BTreeMap::new() } @@ -796,7 +796,7 @@ impl BTreeSet { /// assert_eq!(set.first(), Some(&1)); /// ``` #[must_use] - #[stable(feature = "map_first_last", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "map_first_last", since = "1.66.0")] pub fn first(&self) -> Option<&T> where T: Ord, @@ -822,7 +822,7 @@ impl BTreeSet { /// assert_eq!(set.last(), Some(&2)); /// ``` #[must_use] - #[stable(feature = "map_first_last", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "map_first_last", since = "1.66.0")] pub fn last(&self) -> Option<&T> where T: Ord, @@ -846,7 +846,7 @@ impl BTreeSet { /// } /// assert!(set.is_empty()); /// ``` - #[stable(feature = "map_first_last", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "map_first_last", since = "1.66.0")] pub fn pop_first(&mut self) -> Option where T: Ord, @@ -870,7 +870,7 @@ impl BTreeSet { /// } /// assert!(set.is_empty()); /// ``` - #[stable(feature = "map_first_last", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "map_first_last", since = "1.66.0")] pub fn pop_last(&mut self) -> Option where T: Ord, diff --git a/library/core/src/alloc/layout.rs b/library/core/src/alloc/layout.rs index 920e559cc4aa..f50d9a8e1bdf 100644 --- a/library/core/src/alloc/layout.rs +++ b/library/core/src/alloc/layout.rs @@ -7,8 +7,8 @@ use crate::cmp; use crate::error::Error; use crate::fmt; -use crate::mem::{self, ValidAlign}; -use crate::ptr::NonNull; +use crate::mem; +use crate::ptr::{Alignment, NonNull}; // While this function is used in one place and its implementation // could be inlined, the previous attempts to do so made rustc @@ -46,7 +46,7 @@ pub struct Layout { // // (However, we do not analogously require `align >= sizeof(void*)`, // even though that is *also* a requirement of `posix_memalign`.) - align: ValidAlign, + align: Alignment, } impl Layout { @@ -71,11 +71,11 @@ impl Layout { } // SAFETY: just checked that align is a power of two. - Layout::from_size_valid_align(size, unsafe { ValidAlign::new_unchecked(align) }) + Layout::from_size_alignment(size, unsafe { Alignment::new_unchecked(align) }) } #[inline(always)] - const fn max_size_for_align(align: ValidAlign) -> usize { + const fn max_size_for_align(align: Alignment) -> usize { // (power-of-two implies align != 0.) // Rounded up size is: @@ -95,7 +95,7 @@ impl Layout { /// Internal helper constructor to skip revalidating alignment validity. #[inline] - const fn from_size_valid_align(size: usize, align: ValidAlign) -> Result { + const fn from_size_alignment(size: usize, align: Alignment) -> Result { if size > Self::max_size_for_align(align) { return Err(LayoutError); } @@ -117,7 +117,7 @@ impl Layout { #[rustc_allow_const_fn_unstable(ptr_alignment_type)] pub const unsafe fn from_size_align_unchecked(size: usize, align: usize) -> Self { // SAFETY: the caller is required to uphold the preconditions. - unsafe { Layout { size, align: ValidAlign::new_unchecked(align) } } + unsafe { Layout { size, align: Alignment::new_unchecked(align) } } } /// The minimum size in bytes for a memory block of this layout. @@ -321,7 +321,7 @@ impl Layout { let alloc_size = padded_size.checked_mul(n).ok_or(LayoutError)?; // The safe constructor is called here to enforce the isize size limit. - Layout::from_size_valid_align(alloc_size, self.align).map(|layout| (layout, padded_size)) + Layout::from_size_alignment(alloc_size, self.align).map(|layout| (layout, padded_size)) } /// Creates a layout describing the record for `self` followed by @@ -379,7 +379,7 @@ impl Layout { let new_size = offset.checked_add(next.size()).ok_or(LayoutError)?; // The safe constructor is called here to enforce the isize size limit. - let layout = Layout::from_size_valid_align(new_size, new_align)?; + let layout = Layout::from_size_alignment(new_size, new_align)?; Ok((layout, offset)) } @@ -400,7 +400,7 @@ impl Layout { pub fn repeat_packed(&self, n: usize) -> Result { let size = self.size().checked_mul(n).ok_or(LayoutError)?; // The safe constructor is called here to enforce the isize size limit. - Layout::from_size_valid_align(size, self.align) + Layout::from_size_alignment(size, self.align) } /// Creates a layout describing the record for `self` followed by @@ -414,7 +414,7 @@ impl Layout { pub fn extend_packed(&self, next: Self) -> Result { let new_size = self.size().checked_add(next.size()).ok_or(LayoutError)?; // The safe constructor is called here to enforce the isize size limit. - Layout::from_size_valid_align(new_size, self.align) + Layout::from_size_alignment(new_size, self.align) } /// Creates a layout describing the record for a `[T; n]`. @@ -425,10 +425,10 @@ impl Layout { #[inline] pub fn array(n: usize) -> Result { // Reduce the amount of code we need to monomorphize per `T`. - return inner(mem::size_of::(), ValidAlign::of::(), n); + return inner(mem::size_of::(), Alignment::of::(), n); #[inline] - fn inner(element_size: usize, align: ValidAlign, n: usize) -> Result { + fn inner(element_size: usize, align: Alignment, n: usize) -> Result { // We need to check two things about the size: // - That the total size won't overflow a `usize`, and // - That the total size still fits in an `isize`. @@ -443,7 +443,7 @@ impl Layout { // SAFETY: We just checked above that the `array_size` will not // exceed `isize::MAX` even when rounded up to the alignment. - // And `ValidAlign` guarantees it's a power of two. + // And `Alignment` guarantees it's a power of two. unsafe { Ok(Layout::from_size_align_unchecked(array_size, align.as_usize())) } } } diff --git a/library/core/src/char/convert.rs b/library/core/src/char/convert.rs index 7c5f82f5ea49..f1a51a550f57 100644 --- a/library/core/src/char/convert.rs +++ b/library/core/src/char/convert.rs @@ -18,7 +18,6 @@ pub(super) const fn from_u32(i: u32) -> Option { } /// Converts a `u32` to a `char`, ignoring validity. See [`char::from_u32_unchecked`]. -#[rustc_const_unstable(feature = "const_char_convert", issue = "89259")] #[inline] #[must_use] pub(super) const unsafe fn from_u32_unchecked(i: u32) -> char { diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index bb83599369c1..c05b68e30bc9 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -140,7 +140,7 @@ impl char { /// assert_eq!(None, c); /// ``` #[stable(feature = "assoc_char_funcs", since = "1.52.0")] - #[rustc_const_unstable(feature = "const_char_convert", issue = "89259")] + #[rustc_const_stable(feature = "const_char_convert", since = "CURRENT_RUSTC_VERSION")] #[must_use] #[inline] pub const fn from_u32(i: u32) -> Option { @@ -183,7 +183,7 @@ impl char { /// assert_eq!('❤', c); /// ``` #[stable(feature = "assoc_char_funcs", since = "1.52.0")] - #[rustc_const_unstable(feature = "const_char_convert", issue = "89259")] + #[rustc_const_unstable(feature = "const_char_from_u32_unchecked", issue = "89259")] #[must_use] #[inline] pub const unsafe fn from_u32_unchecked(i: u32) -> char { @@ -241,7 +241,7 @@ impl char { /// let _c = char::from_digit(1, 37); /// ``` #[stable(feature = "assoc_char_funcs", since = "1.52.0")] - #[rustc_const_unstable(feature = "const_char_convert", issue = "89259")] + #[rustc_const_stable(feature = "const_char_convert", since = "CURRENT_RUSTC_VERSION")] #[must_use] #[inline] pub const fn from_digit(num: u32, radix: u32) -> Option { @@ -338,7 +338,7 @@ impl char { /// let _ = '1'.to_digit(37); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_char_convert", issue = "89259")] + #[rustc_const_stable(feature = "const_char_convert", since = "CURRENT_RUSTC_VERSION")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] diff --git a/library/core/src/char/mod.rs b/library/core/src/char/mod.rs index b34a7121631c..55552376280a 100644 --- a/library/core/src/char/mod.rs +++ b/library/core/src/char/mod.rs @@ -110,7 +110,7 @@ pub fn decode_utf16>(iter: I) -> DecodeUtf16 Option { @@ -120,7 +120,7 @@ pub const fn from_u32(i: u32) -> Option { /// Converts a `u32` to a `char`, ignoring validity. Use [`char::from_u32_unchecked`]. /// instead. #[stable(feature = "char_from_unchecked", since = "1.5.0")] -#[rustc_const_unstable(feature = "const_char_convert", issue = "89259")] +#[rustc_const_unstable(feature = "const_char_from_u32_unchecked", issue = "89259")] #[must_use] #[inline] pub const unsafe fn from_u32_unchecked(i: u32) -> char { @@ -130,7 +130,7 @@ pub const unsafe fn from_u32_unchecked(i: u32) -> char { /// Converts a digit in the given radix to a `char`. Use [`char::from_digit`] instead. #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_char_convert", issue = "89259")] +#[rustc_const_stable(feature = "const_char_convert", since = "CURRENT_RUSTC_VERSION")] #[must_use] #[inline] pub const fn from_digit(num: u32, radix: u32) -> Option { diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index f0fa2e1d2c19..5db5cbfc3dfd 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -24,6 +24,7 @@ use crate::const_closure::ConstFnMutClosure; use crate::marker::Destruct; +#[cfg(bootstrap)] use crate::marker::StructuralPartialEq; use self::Ordering::*; @@ -331,6 +332,7 @@ pub struct AssertParamIsEq { /// assert_eq!(Ordering::Greater, result); /// ``` #[derive(Clone, Copy, Eq, Debug, Hash)] +#[cfg_attr(not(bootstrap), derive_const(PartialOrd, Ord, PartialEq))] #[stable(feature = "rust1", since = "1.0.0")] #[repr(i8)] pub enum Ordering { @@ -877,10 +879,12 @@ pub macro Ord($item:item) { } #[stable(feature = "rust1", since = "1.0.0")] +#[cfg(bootstrap)] impl StructuralPartialEq for Ordering {} #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_cmp", issue = "92391")] +#[cfg(bootstrap)] impl const PartialEq for Ordering { #[inline] fn eq(&self, other: &Self) -> bool { @@ -890,6 +894,7 @@ impl const PartialEq for Ordering { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_cmp", issue = "92391")] +#[cfg(bootstrap)] impl const Ord for Ordering { #[inline] fn cmp(&self, other: &Ordering) -> Ordering { @@ -899,6 +904,7 @@ impl const Ord for Ordering { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_cmp", issue = "92391")] +#[cfg(bootstrap)] impl const PartialOrd for Ordering { #[inline] fn partial_cmp(&self, other: &Ordering) -> Option { diff --git a/library/core/src/default.rs b/library/core/src/default.rs index a5b4e965552c..d96b53de0a33 100644 --- a/library/core/src/default.rs +++ b/library/core/src/default.rs @@ -99,7 +99,7 @@ /// ``` #[cfg_attr(not(test), rustc_diagnostic_item = "Default")] #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(bootstrap), const_trait)] +#[const_trait] pub trait Default: Sized { /// Returns the "default value" for a type. /// diff --git a/library/core/src/error.rs b/library/core/src/error.rs index b24ca037d1af..c9c7cdf4defc 100644 --- a/library/core/src/error.rs +++ b/library/core/src/error.rs @@ -493,7 +493,7 @@ impl Error for crate::char::ParseCharError { } } -#[stable(feature = "duration_checked_float", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "duration_checked_float", since = "1.66.0")] impl Error for crate::time::TryFromFloatSecsError {} #[stable(feature = "frombyteswithnulerror_impls", since = "1.17.0")] diff --git a/library/core/src/hash/mod.rs b/library/core/src/hash/mod.rs index aa13435e6808..c755afa39eb6 100644 --- a/library/core/src/hash/mod.rs +++ b/library/core/src/hash/mod.rs @@ -86,7 +86,8 @@ #![stable(feature = "rust1", since = "1.0.0")] use crate::fmt; -use crate::marker; +use crate::intrinsics::const_eval_select; +use crate::marker::{self, Destruct}; #[stable(feature = "rust1", since = "1.0.0")] #[allow(deprecated)] @@ -183,6 +184,7 @@ mod sip; /// [impl]: ../../std/primitive.str.html#impl-Hash-for-str #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "Hash"] +#[const_trait] pub trait Hash { /// Feeds this value into the given [`Hasher`]. /// @@ -234,13 +236,25 @@ pub trait Hash { /// [`hash`]: Hash::hash /// [`hash_slice`]: Hash::hash_slice #[stable(feature = "hash_slice", since = "1.3.0")] - fn hash_slice(data: &[Self], state: &mut H) + fn hash_slice(data: &[Self], state: &mut H) where Self: Sized, { - for piece in data { - piece.hash(state); + //FIXME(const_trait_impl): revert to only a for loop + fn rt(data: &[T], state: &mut H) { + for piece in data { + piece.hash(state) + } } + const fn ct(data: &[T], state: &mut H) { + let mut i = 0; + while i < data.len() { + data[i].hash(state); + i += 1; + } + } + // SAFETY: same behavior, CT just uses while instead of for + unsafe { const_eval_select((data, state), ct, rt) }; } } @@ -313,6 +327,7 @@ pub use macros::Hash; /// [`write_u8`]: Hasher::write_u8 /// [`write_u32`]: Hasher::write_u32 #[stable(feature = "rust1", since = "1.0.0")] +#[const_trait] pub trait Hasher { /// Returns the hash value for the values written so far. /// @@ -558,7 +573,8 @@ pub trait Hasher { } #[stable(feature = "indirect_hasher_impl", since = "1.22.0")] -impl Hasher for &mut H { +#[rustc_const_unstable(feature = "const_hash", issue = "104061")] +impl const Hasher for &mut H { fn finish(&self) -> u64 { (**self).finish() } @@ -638,6 +654,7 @@ impl Hasher for &mut H { /// [`build_hasher`]: BuildHasher::build_hasher /// [`HashMap`]: ../../std/collections/struct.HashMap.html #[stable(since = "1.7.0", feature = "build_hasher")] +#[const_trait] pub trait BuildHasher { /// Type of the hasher that will be created. #[stable(since = "1.7.0", feature = "build_hasher")] @@ -698,9 +715,10 @@ pub trait BuildHasher { /// ); /// ``` #[unstable(feature = "build_hasher_simple_hash_one", issue = "86161")] - fn hash_one(&self, x: T) -> u64 + fn hash_one(&self, x: T) -> u64 where Self: Sized, + Self::Hasher: ~const Hasher + ~const Destruct, { let mut hasher = self.build_hasher(); x.hash(&mut hasher); @@ -764,7 +782,8 @@ impl fmt::Debug for BuildHasherDefault { } #[stable(since = "1.7.0", feature = "build_hasher")] -impl BuildHasher for BuildHasherDefault { +#[rustc_const_unstable(feature = "const_hash", issue = "104061")] +impl const BuildHasher for BuildHasherDefault { type Hasher = H; fn build_hasher(&self) -> H { @@ -806,14 +825,15 @@ mod impls { macro_rules! impl_write { ($(($ty:ident, $meth:ident),)*) => {$( #[stable(feature = "rust1", since = "1.0.0")] - impl Hash for $ty { + #[rustc_const_unstable(feature = "const_hash", issue = "104061")] + impl const Hash for $ty { #[inline] - fn hash(&self, state: &mut H) { + fn hash(&self, state: &mut H) { state.$meth(*self) } #[inline] - fn hash_slice(data: &[$ty], state: &mut H) { + fn hash_slice(data: &[$ty], state: &mut H) { let newlen = data.len() * mem::size_of::<$ty>(); let ptr = data.as_ptr() as *const u8; // SAFETY: `ptr` is valid and aligned, as this macro is only used @@ -842,33 +862,37 @@ mod impls { } #[stable(feature = "rust1", since = "1.0.0")] - impl Hash for bool { + #[rustc_const_unstable(feature = "const_hash", issue = "104061")] + impl const Hash for bool { #[inline] - fn hash(&self, state: &mut H) { + fn hash(&self, state: &mut H) { state.write_u8(*self as u8) } } #[stable(feature = "rust1", since = "1.0.0")] - impl Hash for char { + #[rustc_const_unstable(feature = "const_hash", issue = "104061")] + impl const Hash for char { #[inline] - fn hash(&self, state: &mut H) { + fn hash(&self, state: &mut H) { state.write_u32(*self as u32) } } #[stable(feature = "rust1", since = "1.0.0")] - impl Hash for str { + #[rustc_const_unstable(feature = "const_hash", issue = "104061")] + impl const Hash for str { #[inline] - fn hash(&self, state: &mut H) { + fn hash(&self, state: &mut H) { state.write_str(self); } } #[stable(feature = "never_hash", since = "1.29.0")] - impl Hash for ! { + #[rustc_const_unstable(feature = "const_hash", issue = "104061")] + impl const Hash for ! { #[inline] - fn hash(&self, _: &mut H) { + fn hash(&self, _: &mut H) { *self } } @@ -876,9 +900,10 @@ mod impls { macro_rules! impl_hash_tuple { () => ( #[stable(feature = "rust1", since = "1.0.0")] - impl Hash for () { + #[rustc_const_unstable(feature = "const_hash", issue = "104061")] + impl const Hash for () { #[inline] - fn hash(&self, _state: &mut H) {} + fn hash(&self, _state: &mut H) {} } ); @@ -886,10 +911,11 @@ mod impls { maybe_tuple_doc! { $($name)+ @ #[stable(feature = "rust1", since = "1.0.0")] - impl<$($name: Hash),+> Hash for ($($name,)+) where last_type!($($name,)+): ?Sized { + #[rustc_const_unstable(feature = "const_hash", issue = "104061")] + impl<$($name: ~const Hash),+> const Hash for ($($name,)+) where last_type!($($name,)+): ?Sized { #[allow(non_snake_case)] #[inline] - fn hash(&self, state: &mut S) { + fn hash(&self, state: &mut S) { let ($(ref $name,)+) = *self; $($name.hash(state);)+ } @@ -932,24 +958,27 @@ mod impls { impl_hash_tuple! { T B C D E F G H I J K L } #[stable(feature = "rust1", since = "1.0.0")] - impl Hash for [T] { + #[rustc_const_unstable(feature = "const_hash", issue = "104061")] + impl const Hash for [T] { #[inline] - fn hash(&self, state: &mut H) { + fn hash(&self, state: &mut H) { state.write_length_prefix(self.len()); Hash::hash_slice(self, state) } } #[stable(feature = "rust1", since = "1.0.0")] - impl Hash for &T { + #[rustc_const_unstable(feature = "const_hash", issue = "104061")] + impl const Hash for &T { #[inline] - fn hash(&self, state: &mut H) { + fn hash(&self, state: &mut H) { (**self).hash(state); } } #[stable(feature = "rust1", since = "1.0.0")] - impl Hash for &mut T { + #[rustc_const_unstable(feature = "const_hash", issue = "104061")] + impl const Hash for &mut T { #[inline] fn hash(&self, state: &mut H) { (**self).hash(state); diff --git a/library/core/src/hash/sip.rs b/library/core/src/hash/sip.rs index 81bf1dfdf451..7f8287bf56f6 100644 --- a/library/core/src/hash/sip.rs +++ b/library/core/src/hash/sip.rs @@ -118,7 +118,7 @@ macro_rules! load_int_le { /// Safety: this performs unchecked indexing of `buf` at `start..start+len`, so /// that must be in-bounds. #[inline] -unsafe fn u8to64_le(buf: &[u8], start: usize, len: usize) -> u64 { +const unsafe fn u8to64_le(buf: &[u8], start: usize, len: usize) -> u64 { debug_assert!(len < 8); let mut i = 0; // current byte index (from LSB) in the output u64 let mut out = 0; @@ -138,7 +138,8 @@ unsafe fn u8to64_le(buf: &[u8], start: usize, len: usize) -> u64 { out |= (unsafe { *buf.get_unchecked(start + i) } as u64) << (i * 8); i += 1; } - debug_assert_eq!(i, len); + //FIXME(fee1-dead): use debug_assert_eq + debug_assert!(i == len); out } @@ -150,8 +151,9 @@ impl SipHasher { since = "1.13.0", note = "use `std::collections::hash_map::DefaultHasher` instead" )] + #[rustc_const_unstable(feature = "const_hash", issue = "104061")] #[must_use] - pub fn new() -> SipHasher { + pub const fn new() -> SipHasher { SipHasher::new_with_keys(0, 0) } @@ -162,8 +164,9 @@ impl SipHasher { since = "1.13.0", note = "use `std::collections::hash_map::DefaultHasher` instead" )] + #[rustc_const_unstable(feature = "const_hash", issue = "104061")] #[must_use] - pub fn new_with_keys(key0: u64, key1: u64) -> SipHasher { + pub const fn new_with_keys(key0: u64, key1: u64) -> SipHasher { SipHasher(SipHasher24 { hasher: Hasher::new_with_keys(key0, key1) }) } } @@ -176,7 +179,8 @@ impl SipHasher13 { since = "1.13.0", note = "use `std::collections::hash_map::DefaultHasher` instead" )] - pub fn new() -> SipHasher13 { + #[rustc_const_unstable(feature = "const_hash", issue = "104061")] + pub const fn new() -> SipHasher13 { SipHasher13::new_with_keys(0, 0) } @@ -187,14 +191,15 @@ impl SipHasher13 { since = "1.13.0", note = "use `std::collections::hash_map::DefaultHasher` instead" )] - pub fn new_with_keys(key0: u64, key1: u64) -> SipHasher13 { + #[rustc_const_unstable(feature = "const_hash", issue = "104061")] + pub const fn new_with_keys(key0: u64, key1: u64) -> SipHasher13 { SipHasher13 { hasher: Hasher::new_with_keys(key0, key1) } } } impl Hasher { #[inline] - fn new_with_keys(key0: u64, key1: u64) -> Hasher { + const fn new_with_keys(key0: u64, key1: u64) -> Hasher { let mut state = Hasher { k0: key0, k1: key1, @@ -209,7 +214,7 @@ impl Hasher { } #[inline] - fn reset(&mut self) { + const fn reset(&mut self) { self.length = 0; self.state.v0 = self.k0 ^ 0x736f6d6570736575; self.state.v1 = self.k1 ^ 0x646f72616e646f6d; @@ -220,7 +225,8 @@ impl Hasher { } #[stable(feature = "rust1", since = "1.0.0")] -impl super::Hasher for SipHasher { +#[rustc_const_unstable(feature = "const_hash", issue = "104061")] +impl const super::Hasher for SipHasher { #[inline] fn write(&mut self, msg: &[u8]) { self.0.hasher.write(msg) @@ -238,7 +244,8 @@ impl super::Hasher for SipHasher { } #[unstable(feature = "hashmap_internals", issue = "none")] -impl super::Hasher for SipHasher13 { +#[rustc_const_unstable(feature = "const_hash", issue = "104061")] +impl const super::Hasher for SipHasher13 { #[inline] fn write(&mut self, msg: &[u8]) { self.hasher.write(msg) @@ -255,7 +262,7 @@ impl super::Hasher for SipHasher13 { } } -impl super::Hasher for Hasher { +impl const super::Hasher for Hasher { // Note: no integer hashing methods (`write_u*`, `write_i*`) are defined // for this type. We could add them, copy the `short_write` implementation // in librustc_data_structures/sip128.rs, and add `write_u*`/`write_i*` @@ -335,7 +342,7 @@ impl super::Hasher for Hasher { } } -impl Clone for Hasher { +impl const Clone for Hasher { #[inline] fn clone(&self) -> Hasher { Hasher { @@ -359,6 +366,7 @@ impl Default for Hasher { } #[doc(hidden)] +#[const_trait] trait Sip { fn c_rounds(_: &mut State); fn d_rounds(_: &mut State); @@ -367,7 +375,7 @@ trait Sip { #[derive(Debug, Clone, Default)] struct Sip13Rounds; -impl Sip for Sip13Rounds { +impl const Sip for Sip13Rounds { #[inline] fn c_rounds(state: &mut State) { compress!(state); @@ -384,7 +392,7 @@ impl Sip for Sip13Rounds { #[derive(Debug, Clone, Default)] struct Sip24Rounds; -impl Sip for Sip24Rounds { +impl const Sip for Sip24Rounds { #[inline] fn c_rounds(state: &mut State) { compress!(state); diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index 3412d3730d01..c53175ba4f3f 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -220,7 +220,7 @@ pub fn spin_loop() { /// /// [`std::convert::identity`]: crate::convert::identity #[inline] -#[stable(feature = "bench_black_box", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "bench_black_box", since = "1.66.0")] #[rustc_const_unstable(feature = "const_black_box", issue = "none")] pub const fn black_box(dummy: T) -> T { crate::intrinsics::black_box(dummy) diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index cec603dcb10e..819ccf5a3e9e 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -793,7 +793,7 @@ extern "rust-intrinsic" { /// uninitialized at that point in the control flow. /// /// This intrinsic should not be used outside of the compiler. - #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)] + #[rustc_safe_intrinsic] pub fn rustc_peek(_: T) -> T; /// Aborts the execution of the process. @@ -811,7 +811,7 @@ extern "rust-intrinsic" { /// On Unix, the /// process will probably terminate with a signal like `SIGABRT`, `SIGILL`, `SIGTRAP`, `SIGSEGV` or /// `SIGBUS`. The precise behaviour is not guaranteed and not stable. - #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)] + #[rustc_safe_intrinsic] pub fn abort() -> !; /// Informs the optimizer that this point in the code is not reachable, @@ -850,7 +850,7 @@ extern "rust-intrinsic" { /// /// This intrinsic does not have a stable counterpart. #[rustc_const_unstable(feature = "const_likely", issue = "none")] - #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)] + #[rustc_safe_intrinsic] pub fn likely(b: bool) -> bool; /// Hints to the compiler that branch condition is likely to be false. @@ -865,7 +865,7 @@ extern "rust-intrinsic" { /// /// This intrinsic does not have a stable counterpart. #[rustc_const_unstable(feature = "const_likely", issue = "none")] - #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)] + #[rustc_safe_intrinsic] pub fn unlikely(b: bool) -> bool; /// Executes a breakpoint trap, for inspection by a debugger. @@ -885,7 +885,7 @@ extern "rust-intrinsic" { /// /// The stabilized version of this intrinsic is [`core::mem::size_of`]. #[rustc_const_stable(feature = "const_size_of", since = "1.40.0")] - #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)] + #[rustc_safe_intrinsic] pub fn size_of() -> usize; /// The minimum alignment of a type. @@ -897,7 +897,7 @@ extern "rust-intrinsic" { /// /// The stabilized version of this intrinsic is [`core::mem::align_of`]. #[rustc_const_stable(feature = "const_min_align_of", since = "1.40.0")] - #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)] + #[rustc_safe_intrinsic] pub fn min_align_of() -> usize; /// The preferred alignment of a type. /// @@ -926,7 +926,7 @@ extern "rust-intrinsic" { /// /// The stabilized version of this intrinsic is [`core::any::type_name`]. #[rustc_const_unstable(feature = "const_type_name", issue = "63084")] - #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)] + #[rustc_safe_intrinsic] pub fn type_name() -> &'static str; /// Gets an identifier which is globally unique to the specified type. This @@ -940,7 +940,7 @@ extern "rust-intrinsic" { /// /// The stabilized version of this intrinsic is [`core::any::TypeId::of`]. #[rustc_const_unstable(feature = "const_type_id", issue = "77125")] - #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)] + #[rustc_safe_intrinsic] pub fn type_id() -> u64; /// A guard for unsafe functions that cannot ever be executed if `T` is uninhabited: @@ -948,7 +948,7 @@ extern "rust-intrinsic" { /// /// This intrinsic does not have a stable counterpart. #[rustc_const_stable(feature = "const_assert_type", since = "1.59.0")] - #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)] + #[rustc_safe_intrinsic] pub fn assert_inhabited(); /// A guard for unsafe functions that cannot ever be executed if `T` does not permit @@ -956,7 +956,7 @@ extern "rust-intrinsic" { /// /// This intrinsic does not have a stable counterpart. #[rustc_const_unstable(feature = "const_assert_type2", issue = "none")] - #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)] + #[rustc_safe_intrinsic] pub fn assert_zero_valid(); /// A guard for unsafe functions that cannot ever be executed if `T` has invalid @@ -964,7 +964,7 @@ extern "rust-intrinsic" { /// /// This intrinsic does not have a stable counterpart. #[rustc_const_unstable(feature = "const_assert_type2", issue = "none")] - #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)] + #[rustc_safe_intrinsic] pub fn assert_uninit_valid(); /// Gets a reference to a static `Location` indicating where it was called. @@ -976,7 +976,7 @@ extern "rust-intrinsic" { /// /// Consider using [`core::panic::Location::caller`] instead. #[rustc_const_unstable(feature = "const_caller_location", issue = "76156")] - #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)] + #[rustc_safe_intrinsic] pub fn caller_location() -> &'static crate::panic::Location<'static>; /// Moves a value out of scope without running drop glue. @@ -989,7 +989,7 @@ extern "rust-intrinsic" { /// Therefore, implementations must not require the user to uphold /// any safety invariants. #[rustc_const_unstable(feature = "const_intrinsic_forget", issue = "none")] - #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)] + #[rustc_safe_intrinsic] pub fn forget(_: T); /// Reinterprets the bits of a value of one type as another type. @@ -1269,7 +1269,7 @@ extern "rust-intrinsic" { /// /// The stabilized version of this intrinsic is [`mem::needs_drop`](crate::mem::needs_drop). #[rustc_const_stable(feature = "const_needs_drop", since = "1.40.0")] - #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)] + #[rustc_safe_intrinsic] pub fn needs_drop() -> bool; /// Calculates the offset from a pointer. @@ -1314,7 +1314,7 @@ extern "rust-intrinsic" { /// any safety invariants. /// /// Consider using [`pointer::mask`] instead. - #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)] + #[rustc_safe_intrinsic] pub fn ptr_mask(ptr: *const T, mask: usize) -> *const T; /// Equivalent to the appropriate `llvm.memcpy.p0i8.0i8.*` intrinsic, with @@ -1506,7 +1506,7 @@ extern "rust-intrinsic" { /// /// The stabilized version of this intrinsic is /// [`f32::min`] - #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)] + #[rustc_safe_intrinsic] pub fn minnumf32(x: f32, y: f32) -> f32; /// Returns the minimum of two `f64` values. /// @@ -1517,7 +1517,7 @@ extern "rust-intrinsic" { /// /// The stabilized version of this intrinsic is /// [`f64::min`] - #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)] + #[rustc_safe_intrinsic] pub fn minnumf64(x: f64, y: f64) -> f64; /// Returns the maximum of two `f32` values. /// @@ -1528,7 +1528,7 @@ extern "rust-intrinsic" { /// /// The stabilized version of this intrinsic is /// [`f32::max`] - #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)] + #[rustc_safe_intrinsic] pub fn maxnumf32(x: f32, y: f32) -> f32; /// Returns the maximum of two `f64` values. /// @@ -1539,7 +1539,7 @@ extern "rust-intrinsic" { /// /// The stabilized version of this intrinsic is /// [`f64::max`] - #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)] + #[rustc_safe_intrinsic] pub fn maxnumf64(x: f64, y: f64) -> f64; /// Copies the sign from `y` to `x` for `f32` values. @@ -1660,7 +1660,7 @@ extern "rust-intrinsic" { /// primitives via the `count_ones` method. For example, /// [`u32::count_ones`] #[rustc_const_stable(feature = "const_ctpop", since = "1.40.0")] - #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)] + #[rustc_safe_intrinsic] pub fn ctpop(x: T) -> T; /// Returns the number of leading unset bits (zeroes) in an integer type `T`. @@ -1698,7 +1698,7 @@ extern "rust-intrinsic" { /// assert_eq!(num_leading, 16); /// ``` #[rustc_const_stable(feature = "const_ctlz", since = "1.40.0")] - #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)] + #[rustc_safe_intrinsic] pub fn ctlz(x: T) -> T; /// Like `ctlz`, but extra-unsafe as it returns `undef` when @@ -1755,7 +1755,7 @@ extern "rust-intrinsic" { /// assert_eq!(num_trailing, 16); /// ``` #[rustc_const_stable(feature = "const_cttz", since = "1.40.0")] - #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)] + #[rustc_safe_intrinsic] pub fn cttz(x: T) -> T; /// Like `cttz`, but extra-unsafe as it returns `undef` when @@ -1788,7 +1788,7 @@ extern "rust-intrinsic" { /// primitives via the `swap_bytes` method. For example, /// [`u32::swap_bytes`] #[rustc_const_stable(feature = "const_bswap", since = "1.40.0")] - #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)] + #[rustc_safe_intrinsic] pub fn bswap(x: T) -> T; /// Reverses the bits in an integer type `T`. @@ -1802,7 +1802,7 @@ extern "rust-intrinsic" { /// primitives via the `reverse_bits` method. For example, /// [`u32::reverse_bits`] #[rustc_const_stable(feature = "const_bitreverse", since = "1.40.0")] - #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)] + #[rustc_safe_intrinsic] pub fn bitreverse(x: T) -> T; /// Performs checked integer addition. @@ -1816,7 +1816,7 @@ extern "rust-intrinsic" { /// primitives via the `overflowing_add` method. For example, /// [`u32::overflowing_add`] #[rustc_const_stable(feature = "const_int_overflow", since = "1.40.0")] - #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)] + #[rustc_safe_intrinsic] pub fn add_with_overflow(x: T, y: T) -> (T, bool); /// Performs checked integer subtraction @@ -1830,7 +1830,7 @@ extern "rust-intrinsic" { /// primitives via the `overflowing_sub` method. For example, /// [`u32::overflowing_sub`] #[rustc_const_stable(feature = "const_int_overflow", since = "1.40.0")] - #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)] + #[rustc_safe_intrinsic] pub fn sub_with_overflow(x: T, y: T) -> (T, bool); /// Performs checked integer multiplication @@ -1844,7 +1844,7 @@ extern "rust-intrinsic" { /// primitives via the `overflowing_mul` method. For example, /// [`u32::overflowing_mul`] #[rustc_const_stable(feature = "const_int_overflow", since = "1.40.0")] - #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)] + #[rustc_safe_intrinsic] pub fn mul_with_overflow(x: T, y: T) -> (T, bool); /// Performs an exact division, resulting in undefined behavior where @@ -1919,7 +1919,7 @@ extern "rust-intrinsic" { /// primitives via the `rotate_left` method. For example, /// [`u32::rotate_left`] #[rustc_const_stable(feature = "const_int_rotate", since = "1.40.0")] - #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)] + #[rustc_safe_intrinsic] pub fn rotate_left(x: T, y: T) -> T; /// Performs rotate right. @@ -1933,7 +1933,7 @@ extern "rust-intrinsic" { /// primitives via the `rotate_right` method. For example, /// [`u32::rotate_right`] #[rustc_const_stable(feature = "const_int_rotate", since = "1.40.0")] - #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)] + #[rustc_safe_intrinsic] pub fn rotate_right(x: T, y: T) -> T; /// Returns (a + b) mod 2N, where N is the width of T in bits. @@ -1947,7 +1947,7 @@ extern "rust-intrinsic" { /// primitives via the `wrapping_add` method. For example, /// [`u32::wrapping_add`] #[rustc_const_stable(feature = "const_int_wrapping", since = "1.40.0")] - #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)] + #[rustc_safe_intrinsic] pub fn wrapping_add(a: T, b: T) -> T; /// Returns (a - b) mod 2N, where N is the width of T in bits. /// @@ -1960,7 +1960,7 @@ extern "rust-intrinsic" { /// primitives via the `wrapping_sub` method. For example, /// [`u32::wrapping_sub`] #[rustc_const_stable(feature = "const_int_wrapping", since = "1.40.0")] - #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)] + #[rustc_safe_intrinsic] pub fn wrapping_sub(a: T, b: T) -> T; /// Returns (a * b) mod 2N, where N is the width of T in bits. /// @@ -1973,7 +1973,7 @@ extern "rust-intrinsic" { /// primitives via the `wrapping_mul` method. For example, /// [`u32::wrapping_mul`] #[rustc_const_stable(feature = "const_int_wrapping", since = "1.40.0")] - #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)] + #[rustc_safe_intrinsic] pub fn wrapping_mul(a: T, b: T) -> T; /// Computes `a + b`, saturating at numeric bounds. @@ -1987,7 +1987,7 @@ extern "rust-intrinsic" { /// primitives via the `saturating_add` method. For example, /// [`u32::saturating_add`] #[rustc_const_stable(feature = "const_int_saturating", since = "1.40.0")] - #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)] + #[rustc_safe_intrinsic] pub fn saturating_add(a: T, b: T) -> T; /// Computes `a - b`, saturating at numeric bounds. /// @@ -2000,7 +2000,7 @@ extern "rust-intrinsic" { /// primitives via the `saturating_sub` method. For example, /// [`u32::saturating_sub`] #[rustc_const_stable(feature = "const_int_saturating", since = "1.40.0")] - #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)] + #[rustc_safe_intrinsic] pub fn saturating_sub(a: T, b: T) -> T; /// Returns the value of the discriminant for the variant in 'v'; @@ -2013,7 +2013,7 @@ extern "rust-intrinsic" { /// /// The stabilized version of this intrinsic is [`core::mem::discriminant`]. #[rustc_const_unstable(feature = "const_discriminant", issue = "69821")] - #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)] + #[rustc_safe_intrinsic] pub fn discriminant_value(v: &T) -> ::Discriminant; /// Returns the number of variants of the type `T` cast to a `usize`; @@ -2026,7 +2026,7 @@ extern "rust-intrinsic" { /// /// The to-be-stabilized version of this intrinsic is [`mem::variant_count`]. #[rustc_const_unstable(feature = "variant_count", issue = "73662")] - #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)] + #[rustc_safe_intrinsic] pub fn variant_count() -> usize; /// Rust's "try catch" construct which invokes the function pointer `try_fn` @@ -2060,7 +2060,7 @@ extern "rust-intrinsic" { /// Therefore, implementations must not require the user to uphold /// any safety invariants. #[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")] - #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)] + #[rustc_safe_intrinsic] pub fn ptr_guaranteed_cmp(ptr: *const T, other: *const T) -> u8; /// Allocates a block of memory at compile time. @@ -2111,7 +2111,7 @@ extern "rust-intrinsic" { /// /// [`std::hint::black_box`]: crate::hint::black_box #[rustc_const_unstable(feature = "const_black_box", issue = "none")] - #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)] + #[rustc_safe_intrinsic] pub fn black_box(dummy: T) -> T; /// `ptr` must point to a vtable. diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 659409557c91..2d12805270f9 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -104,7 +104,7 @@ #![feature(const_black_box)] #![feature(const_caller_location)] #![feature(const_cell_into_inner)] -#![feature(const_char_convert)] +#![feature(const_char_from_u32_unchecked)] #![feature(const_clone)] #![feature(const_cmp)] #![feature(const_discriminant)] @@ -112,6 +112,7 @@ #![feature(const_float_bits_conv)] #![feature(const_float_classify)] #![feature(const_fmt_arguments_new)] +#![feature(const_hash)] #![feature(const_heap)] #![feature(const_convert)] #![feature(const_index_range_slice_index)] @@ -184,6 +185,7 @@ #![feature(const_refs_to_cell)] #![feature(decl_macro)] #![feature(deprecated_suggestion)] +#![cfg_attr(not(bootstrap), feature(derive_const))] #![feature(doc_cfg)] #![feature(doc_notable_trait)] #![feature(rustdoc_internals)] diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 2850d84acc32..32bdc26bc51a 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -1464,6 +1464,19 @@ pub(crate) mod builtin { /* compiler built-in */ } + /// Attribute macro used to apply derive macros for implementing traits + /// in a const context. + /// + /// See [the reference] for more info. + /// + /// [the reference]: ../../../reference/attributes/derive.html + #[unstable(feature = "derive_const", issue = "none")] + #[rustc_builtin_macro] + #[cfg(not(bootstrap))] + pub macro derive_const($item:item) { + /* compiler built-in */ + } + /// Attribute macro applied to a function to turn it into a unit test. /// /// See [the reference] for more info. diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 7757c95de9d2..3f491836551d 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -1172,7 +1172,7 @@ impl MaybeUninit { /// #![feature(maybe_uninit_as_bytes, maybe_uninit_slice)] /// use std::mem::MaybeUninit; /// - /// let val = 0x12345678i32; + /// let val = 0x12345678_i32; /// let uninit = MaybeUninit::new(val); /// let uninit_bytes = uninit.as_bytes(); /// let bytes = unsafe { MaybeUninit::slice_assume_init_ref(uninit_bytes) }; @@ -1198,7 +1198,7 @@ impl MaybeUninit { /// #![feature(maybe_uninit_as_bytes)] /// use std::mem::MaybeUninit; /// - /// let val = 0x12345678i32; + /// let val = 0x12345678_i32; /// let mut uninit = MaybeUninit::new(val); /// let uninit_bytes = uninit.as_bytes_mut(); /// if cfg!(target_endian = "little") { diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 2e1a667097c0..956a69eda8a5 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -21,11 +21,6 @@ mod maybe_uninit; #[stable(feature = "maybe_uninit", since = "1.36.0")] pub use maybe_uninit::MaybeUninit; -// FIXME: This is left here for now to avoid complications around pending reverts. -// Once is fully resolved, -// this should be removed and the references in `alloc::Layout` updated. -pub(crate) use ptr::Alignment as ValidAlign; - mod transmutability; #[unstable(feature = "transmutability", issue = "99571")] pub use transmutability::{Assume, BikeshedIntrinsicFrom}; diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 404ddff4f9da..e64eb1cf7aed 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -107,6 +107,9 @@ macro_rules! int_impl { /// Returns the number of leading zeros in the binary representation of `self`. /// + /// Depending on what you're doing with the value, you might also be interested in the + /// [`ilog2`] function which returns a consistent number, even if the type widens. + /// /// # Examples /// /// Basic usage: @@ -116,6 +119,7 @@ macro_rules! int_impl { /// /// assert_eq!(n.leading_zeros(), 0); /// ``` + #[doc = concat!("[`ilog2`]: ", stringify!($SelfT), "::ilog2")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ @@ -467,8 +471,8 @@ macro_rules! int_impl { #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".checked_add_unsigned(2), Some(3));")] #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MAX - 2).checked_add_unsigned(3), None);")] /// ``` - #[stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "mixed_integer_ops", since = "1.66.0")] + #[rustc_const_stable(feature = "mixed_integer_ops", since = "1.66.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -535,8 +539,8 @@ macro_rules! int_impl { #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".checked_sub_unsigned(2), Some(-1));")] #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN + 2).checked_sub_unsigned(3), None);")] /// ``` - #[stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "mixed_integer_ops", since = "1.66.0")] + #[rustc_const_stable(feature = "mixed_integer_ops", since = "1.66.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -905,8 +909,8 @@ macro_rules! int_impl { #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".saturating_add_unsigned(2), 3);")] #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.saturating_add_unsigned(100), ", stringify!($SelfT), "::MAX);")] /// ``` - #[stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "mixed_integer_ops", since = "1.66.0")] + #[rustc_const_stable(feature = "mixed_integer_ops", since = "1.66.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -951,8 +955,8 @@ macro_rules! int_impl { #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".saturating_sub_unsigned(127), -27);")] #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.saturating_sub_unsigned(100), ", stringify!($SelfT), "::MIN);")] /// ``` - #[stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "mixed_integer_ops", since = "1.66.0")] + #[rustc_const_stable(feature = "mixed_integer_ops", since = "1.66.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -1129,8 +1133,8 @@ macro_rules! int_impl { #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_add_unsigned(27), 127);")] #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.wrapping_add_unsigned(2), ", stringify!($SelfT), "::MIN + 1);")] /// ``` - #[stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "mixed_integer_ops", since = "1.66.0")] + #[rustc_const_stable(feature = "mixed_integer_ops", since = "1.66.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] @@ -1169,8 +1173,8 @@ macro_rules! int_impl { #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".wrapping_sub_unsigned(127), -127);")] #[doc = concat!("assert_eq!((-2", stringify!($SelfT), ").wrapping_sub_unsigned(", stringify!($UnsignedT), "::MAX), -1);")] /// ``` - #[stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "mixed_integer_ops", since = "1.66.0")] + #[rustc_const_stable(feature = "mixed_integer_ops", since = "1.66.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] @@ -1566,8 +1570,8 @@ macro_rules! int_impl { #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN).overflowing_add_unsigned(", stringify!($UnsignedT), "::MAX), (", stringify!($SelfT), "::MAX, false));")] #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MAX - 2).overflowing_add_unsigned(3), (", stringify!($SelfT), "::MIN, true));")] /// ``` - #[stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "mixed_integer_ops", since = "1.66.0")] + #[rustc_const_stable(feature = "mixed_integer_ops", since = "1.66.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -1648,8 +1652,8 @@ macro_rules! int_impl { #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MAX).overflowing_sub_unsigned(", stringify!($UnsignedT), "::MAX), (", stringify!($SelfT), "::MIN, false));")] #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN + 2).overflowing_sub_unsigned(3), (", stringify!($SelfT), "::MAX, true));")] /// ``` - #[stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "mixed_integer_ops", since = "1.66.0")] + #[rustc_const_stable(feature = "mixed_integer_ops", since = "1.66.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -2068,11 +2072,15 @@ macro_rules! int_impl { pub const fn rem_euclid(self, rhs: Self) -> Self { let r = self % rhs; if r < 0 { - if rhs < 0 { - r - rhs - } else { - r + rhs - } + // Semantically equivalent to `if rhs < 0 { r - rhs } else { r + rhs }`. + // If `rhs` is not `Self::MIN`, then `r + abs(rhs)` will not overflow + // and is clearly equivalent, because `r` is negative. + // Otherwise, `rhs` is `Self::MIN`, then we have + // `r.wrapping_add(Self::MIN.wrapping_abs())`, which evaluates + // to `r.wrapping_add(Self::MIN)`, which is equivalent to + // `r - Self::MIN`, which is what we wanted (and will not overflow + // for negative `r`). + r.wrapping_add(rhs.wrapping_abs()) } else { r } diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 0563f28278d3..741d7ec6f592 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -109,6 +109,9 @@ macro_rules! uint_impl { /// Returns the number of leading zeros in the binary representation of `self`. /// + /// Depending on what you're doing with the value, you might also be interested in the + /// [`ilog2`] function which returns a consistent number, even if the type widens. + /// /// # Examples /// /// Basic usage: @@ -118,6 +121,7 @@ macro_rules! uint_impl { /// /// assert_eq!(n.leading_zeros(), 2); /// ``` + #[doc = concat!("[`ilog2`]: ", stringify!($SelfT), "::ilog2")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_math", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ @@ -478,8 +482,8 @@ macro_rules! uint_impl { #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".checked_add_signed(-2), None);")] #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MAX - 2).checked_add_signed(3), None);")] /// ``` - #[stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "mixed_integer_ops", since = "1.66.0")] + #[rustc_const_stable(feature = "mixed_integer_ops", since = "1.66.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -1028,8 +1032,8 @@ macro_rules! uint_impl { #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".saturating_add_signed(-2), 0);")] #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MAX - 2).saturating_add_signed(4), ", stringify!($SelfT), "::MAX);")] /// ``` - #[stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "mixed_integer_ops", since = "1.66.0")] + #[rustc_const_stable(feature = "mixed_integer_ops", since = "1.66.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -1168,8 +1172,8 @@ macro_rules! uint_impl { #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".wrapping_add_signed(-2), ", stringify!($SelfT), "::MAX);")] #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MAX - 2).wrapping_add_signed(4), 1);")] /// ``` - #[stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "mixed_integer_ops", since = "1.66.0")] + #[rustc_const_stable(feature = "mixed_integer_ops", since = "1.66.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -1537,8 +1541,8 @@ macro_rules! uint_impl { #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".overflowing_add_signed(-2), (", stringify!($SelfT), "::MAX, true));")] #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MAX - 2).overflowing_add_signed(4), (1, true));")] /// ``` - #[stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "mixed_integer_ops", since = "1.66.0")] + #[rustc_const_stable(feature = "mixed_integer_ops", since = "1.66.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] diff --git a/library/core/src/ops/deref.rs b/library/core/src/ops/deref.rs index 4f4c99c4ad97..c67867f4436e 100644 --- a/library/core/src/ops/deref.rs +++ b/library/core/src/ops/deref.rs @@ -61,7 +61,7 @@ #[doc(alias = "&*")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "Deref"] -#[cfg_attr(not(bootstrap), const_trait)] +#[const_trait] pub trait Deref { /// The resulting type after dereferencing. #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/core/src/ops/function.rs b/library/core/src/ops/function.rs index 11b43b621c7b..910a92ec0d99 100644 --- a/library/core/src/ops/function.rs +++ b/library/core/src/ops/function.rs @@ -75,6 +75,7 @@ use crate::marker::Tuple; )] #[fundamental] // so that regex can rely that `&str: !FnMut` #[must_use = "closures are lazy and do nothing unless called"] +#[const_trait] pub trait Fn: FnMut { /// Performs the call operation. #[unstable(feature = "fn_traits", issue = "29625")] @@ -244,6 +245,7 @@ pub trait Fn: FnMut { )] #[fundamental] // so that regex can rely that `&str: !FnMut` #[must_use = "closures are lazy and do nothing unless called"] +#[const_trait] pub trait FnMut: FnOnce { /// Performs the call operation. #[unstable(feature = "fn_traits", issue = "29625")] @@ -413,6 +415,7 @@ pub trait FnMut: FnOnce { )] #[fundamental] // so that regex can rely that `&str: !FnMut` #[must_use = "closures are lazy and do nothing unless called"] +#[const_trait] pub trait FnOnce { /// The returned type after the call operator is used. #[lang = "fn_once_output"] diff --git a/library/core/src/ops/index.rs b/library/core/src/ops/index.rs index dd4e3ac1c2fe..5e3dc48b6ca1 100644 --- a/library/core/src/ops/index.rs +++ b/library/core/src/ops/index.rs @@ -55,7 +55,7 @@ #[doc(alias = "]")] #[doc(alias = "[")] #[doc(alias = "[]")] -#[cfg_attr(not(bootstrap), const_trait)] +#[const_trait] pub trait Index { /// The returned type after indexing. #[stable(feature = "rust1", since = "1.0.0")] @@ -164,7 +164,7 @@ see chapter in The Book : Index { /// Performs the mutable indexing (`container[index]`) operation. /// diff --git a/library/core/src/option.rs b/library/core/src/option.rs index a81dbc6924fb..f284b4359557 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -1720,7 +1720,7 @@ impl Option<(T, U)> { /// assert_eq!(y.unzip(), (None, None)); /// ``` #[inline] - #[stable(feature = "unzip_option", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "unzip_option", since = "1.66.0")] #[rustc_const_unstable(feature = "const_option", issue = "67441")] pub const fn unzip(self) -> (Option, Option) where diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index a9de7c94e5a1..4fd1eb234137 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -70,8 +70,7 @@ pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! { #[cold] #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] #[cfg_attr(feature = "panic_immediate_abort", inline)] -#[cfg_attr(not(bootstrap), rustc_nounwind)] -#[cfg_attr(bootstrap, rustc_allocator_nounwind)] +#[rustc_nounwind] pub fn panic_str_nounwind(msg: &'static str) -> ! { if cfg!(feature = "panic_immediate_abort") { super::intrinsics::abort() @@ -158,8 +157,7 @@ fn panic_bounds_check(index: usize, len: usize) -> ! { #[cold] #[inline(never)] #[lang = "panic_no_unwind"] // needed by codegen for panic in nounwind function -#[cfg_attr(not(bootstrap), rustc_nounwind)] -#[cfg_attr(bootstrap, rustc_allocator_nounwind)] +#[rustc_nounwind] fn panic_no_unwind() -> ! { panic_str_nounwind("panic in a function that cannot unwind") } diff --git a/library/core/src/prelude/v1.rs b/library/core/src/prelude/v1.rs index 804a179bdb3c..d3d255a802d7 100644 --- a/library/core/src/prelude/v1.rs +++ b/library/core/src/prelude/v1.rs @@ -81,6 +81,10 @@ pub use crate::macros::builtin::alloc_error_handler; #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] pub use crate::macros::builtin::{bench, derive, global_allocator, test, test_case}; +#[unstable(feature = "derive_const", issue = "none")] +#[cfg(not(bootstrap))] +pub use crate::macros::builtin::derive_const; + #[unstable( feature = "cfg_accessible", issue = "64797", diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 565c38d222a2..742fe4f3f633 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -35,7 +35,8 @@ //! be used for inter-thread synchronization. //! * The result of casting a reference to a pointer is valid for as long as the //! underlying object is live and no reference (just raw pointers) is used to -//! access the same memory. +//! access the same memory. That is, reference and pointer accesses cannot be +//! interleaved. //! //! These axioms, along with careful use of [`offset`] for pointer arithmetic, //! are enough to correctly implement many useful things in unsafe code. Stronger guarantees @@ -64,7 +65,6 @@ //! separate allocated object), heap allocations (each allocation created by the global allocator is //! a separate allocated object), and `static` variables. //! -//! //! # Strict Provenance //! //! **The following text is non-normative, insufficiently formal, and is an extremely strict @@ -1862,7 +1862,6 @@ macro_rules! fnptr_impls_safety_abi { fnptr_impls_safety_abi! { #[stable(feature = "fnptr_impls", since = "1.4.0")] $FnTy, $($Arg),* } }; (@c_unwind $FnTy: ty, $($Arg: ident),*) => { - #[cfg(not(bootstrap))] fnptr_impls_safety_abi! { #[unstable(feature = "c_unwind", issue = "74990")] $FnTy, $($Arg),* } }; (#[$meta:meta] $FnTy: ty, $($Arg: ident),*) => { diff --git a/library/core/src/time.rs b/library/core/src/time.rs index 37c3611d0a90..ba1cb6efa04b 100644 --- a/library/core/src/time.rs +++ b/library/core/src/time.rs @@ -1232,7 +1232,7 @@ impl fmt::Debug for Duration { /// } /// ``` #[derive(Debug, Clone, PartialEq, Eq)] -#[stable(feature = "duration_checked_float", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "duration_checked_float", since = "1.66.0")] pub struct TryFromFloatSecsError { kind: TryFromFloatSecsErrorKind, } @@ -1250,7 +1250,7 @@ impl TryFromFloatSecsError { } } -#[stable(feature = "duration_checked_float", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "duration_checked_float", since = "1.66.0")] impl fmt::Display for TryFromFloatSecsError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.description().fmt(f) @@ -1401,7 +1401,7 @@ impl Duration { /// let res = Duration::try_from_secs_f32(val); /// assert_eq!(res, Ok(Duration::new(1, 2_929_688))); /// ``` - #[stable(feature = "duration_checked_float", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "duration_checked_float", since = "1.66.0")] #[rustc_const_unstable(feature = "duration_consts_float", issue = "72440")] #[inline] pub const fn try_from_secs_f32(secs: f32) -> Result { @@ -1478,7 +1478,7 @@ impl Duration { /// let res = Duration::try_from_secs_f64(val); /// assert_eq!(res, Ok(Duration::new(1, 2_929_688))); /// ``` - #[stable(feature = "duration_checked_float", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "duration_checked_float", since = "1.66.0")] #[rustc_const_unstable(feature = "duration_consts_float", issue = "72440")] #[inline] pub const fn try_from_secs_f64(secs: f64) -> Result { diff --git a/library/core/tests/hash/mod.rs b/library/core/tests/hash/mod.rs index f7934d062a37..267245f05dcd 100644 --- a/library/core/tests/hash/mod.rs +++ b/library/core/tests/hash/mod.rs @@ -9,16 +9,19 @@ struct MyHasher { hash: u64, } -impl Default for MyHasher { +impl const Default for MyHasher { fn default() -> MyHasher { MyHasher { hash: 0 } } } -impl Hasher for MyHasher { +impl const Hasher for MyHasher { fn write(&mut self, buf: &[u8]) { - for byte in buf { - self.hash += *byte as u64; + // FIXME(const_trait_impl): change to for loop + let mut i = 0; + while i < buf.len() { + self.hash += buf[i] as u64; + i += 1; } } fn write_str(&mut self, s: &str) { @@ -32,12 +35,25 @@ impl Hasher for MyHasher { #[test] fn test_writer_hasher() { - fn hash(t: &T) -> u64 { + const fn hash(t: &T) -> u64 { let mut s = MyHasher { hash: 0 }; t.hash(&mut s); s.finish() } + const { + // FIXME(fee1-dead): assert_eq + assert!(hash(&()) == 0); + assert!(hash(&5_u8) == 5); + assert!(hash(&5_u16) == 5); + assert!(hash(&5_u32) == 5); + + assert!(hash(&'a') == 97); + + let s: &str = "a"; + assert!(hash(&s) == 97 + 0xFF); + }; + assert_eq!(hash(&()), 0); assert_eq!(hash(&5_u8), 5); @@ -97,7 +113,7 @@ struct CustomHasher { output: u64, } -impl Hasher for CustomHasher { +impl const Hasher for CustomHasher { fn finish(&self) -> u64 { self.output } @@ -109,27 +125,29 @@ impl Hasher for CustomHasher { } } -impl Default for CustomHasher { +impl const Default for CustomHasher { fn default() -> CustomHasher { CustomHasher { output: 0 } } } -impl Hash for Custom { - fn hash(&self, state: &mut H) { +impl const Hash for Custom { + fn hash(&self, state: &mut H) { state.write_u64(self.hash); } } #[test] fn test_custom_state() { - fn hash(t: &T) -> u64 { + const fn hash(t: &T) -> u64 { let mut c = CustomHasher { output: 0 }; t.hash(&mut c); c.finish() } assert_eq!(hash(&Custom { hash: 5 }), 5); + + const { assert!(hash(&Custom { hash: 6 }) == 6) }; } // FIXME: Instantiated functions with i128 in the signature is not supported in Emscripten. diff --git a/library/core/tests/hash/sip.rs b/library/core/tests/hash/sip.rs index 877d08418305..3abf6efcfa9b 100644 --- a/library/core/tests/hash/sip.rs +++ b/library/core/tests/hash/sip.rs @@ -8,7 +8,6 @@ use core::{mem, slice}; struct Bytes<'a>(&'a [u8]); impl<'a> Hash for Bytes<'a> { - #[allow(unused_must_use)] fn hash(&self, state: &mut H) { let Bytes(v) = *self; state.write(v); @@ -24,6 +23,20 @@ fn hash(x: &T) -> u64 { hash_with(SipHasher::new(), x) } +#[test] +const fn test_const_sip() { + let val1 = 0x45; + let val2 = 0xfeed; + + const fn const_hash(x: &T) -> u64 { + let mut st = SipHasher::new(); + x.hash(&mut st); + st.finish() + } + + assert!(const_hash(&(val1)) != const_hash(&(val2))); +} + #[test] #[allow(unused_must_use)] fn test_siphash_1_3() { diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index a7db2a02bd74..61d31b344873 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -11,6 +11,7 @@ #![feature(const_caller_location)] #![feature(const_cell_into_inner)] #![feature(const_convert)] +#![feature(const_hash)] #![feature(const_heap)] #![feature(const_maybe_uninit_as_mut_ptr)] #![feature(const_maybe_uninit_assume_init_read)] diff --git a/library/core/tests/option.rs b/library/core/tests/option.rs index f36f7c268064..dca6321cf62f 100644 --- a/library/core/tests/option.rs +++ b/library/core/tests/option.rs @@ -57,7 +57,7 @@ fn test_get_resource() { } #[test] -#[cfg_attr(not(bootstrap), allow(for_loops_over_fallibles))] +#[allow(for_loops_over_fallibles)] fn test_option_dance() { let x = Some(()); let mut y = Some(5); diff --git a/library/portable-simd/crates/core_simd/src/intrinsics.rs b/library/portable-simd/crates/core_simd/src/intrinsics.rs index 962c83a78cb3..704e6ed0159f 100644 --- a/library/portable-simd/crates/core_simd/src/intrinsics.rs +++ b/library/portable-simd/crates/core_simd/src/intrinsics.rs @@ -103,7 +103,7 @@ extern "platform-intrinsic" { /// val: vector of values to select if a lane is masked /// ptr: vector of pointers to read from /// mask: a "wide" mask of integers, selects as if simd_select(mask, read(ptr), val) - /// note, the LLVM intrinsic accepts a mask vector of + /// note, the LLVM intrinsic accepts a mask vector of `` /// FIXME: review this if/when we fix up our mask story in general? pub(crate) fn simd_gather(val: T, ptr: U, mask: V) -> T; /// llvm.masked.scatter diff --git a/library/portable-simd/crates/core_simd/src/ops.rs b/library/portable-simd/crates/core_simd/src/ops.rs index 5a077a469d83..fc1e0bc426df 100644 --- a/library/portable-simd/crates/core_simd/src/ops.rs +++ b/library/portable-simd/crates/core_simd/src/ops.rs @@ -40,7 +40,7 @@ macro_rules! unsafe_base { /// SAFETY: This macro should not be used for anything except Shl or Shr, and passed the appropriate shift intrinsic. /// It handles performing a bitand in addition to calling the shift operator, so that the result -/// is well-defined: LLVM can return a poison value if you shl, lshr, or ashr if rhs >= ::BITS +/// is well-defined: LLVM can return a poison value if you shl, lshr, or ashr if `rhs >= ::BITS` /// At worst, this will maybe add another instruction and cycle, /// at best, it may open up more optimization opportunities, /// or simply be elided entirely, especially for SIMD ISAs which default to this. diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index 8001fcff6484..0d3fc2c5244e 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -546,7 +546,7 @@ impl Span { /// Note: The observable result of a macro should only rely on the tokens and /// not on this source text. The result of this function is a best effort to /// be used for diagnostics only. - #[stable(feature = "proc_macro_source_text", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "proc_macro_source_text", since = "1.66.0")] pub fn source_text(&self) -> Option { self.0.source_text() } diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index 708edc5de475..df490358827e 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -3161,14 +3161,16 @@ impl DefaultHasher { #[stable(feature = "hashmap_default_hasher", since = "1.13.0")] #[inline] #[allow(deprecated)] + #[rustc_const_unstable(feature = "const_hash", issue = "104061")] #[must_use] - pub fn new() -> DefaultHasher { + pub const fn new() -> DefaultHasher { DefaultHasher(SipHasher13::new_with_keys(0, 0)) } } #[stable(feature = "hashmap_default_hasher", since = "1.13.0")] -impl Default for DefaultHasher { +#[rustc_const_unstable(feature = "const_hash", issue = "104061")] +impl const Default for DefaultHasher { /// Creates a new `DefaultHasher` using [`new`]. /// See its documentation for more. /// @@ -3180,7 +3182,8 @@ impl Default for DefaultHasher { } #[stable(feature = "hashmap_default_hasher", since = "1.13.0")] -impl Hasher for DefaultHasher { +#[rustc_const_unstable(feature = "const_hash", issue = "104061")] +impl const Hasher for DefaultHasher { // The underlying `SipHasher13` doesn't override the other // `write_*` methods, so it's ok not to forward them here. diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 385585dada89..9334c833bb65 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -352,6 +352,7 @@ // // Only for const-ness: #![feature(const_collections_with_hasher)] +#![feature(const_hash)] #![feature(const_io_structs)] #![feature(const_ip)] #![feature(const_ipv4)] diff --git a/library/std/src/os/android/net.rs b/library/std/src/os/android/net.rs index ff96125c37bd..7cecd1bbfaa9 100644 --- a/library/std/src/os/android/net.rs +++ b/library/std/src/os/android/net.rs @@ -1,4 +1,9 @@ -//! Linux and Android-specific definitions for socket options. +//! Android-specific networking functionality. #![unstable(feature = "tcp_quickack", issue = "96256")] -pub use crate::os::net::tcp::TcpStreamExt; + +#[unstable(feature = "unix_socket_abstract", issue = "85410")] +pub use crate::os::net::linux_ext::addr::SocketAddrExt; + +#[unstable(feature = "tcp_quickack", issue = "96256")] +pub use crate::os::net::linux_ext::tcp::TcpStreamExt; diff --git a/library/std/src/os/linux/net.rs b/library/std/src/os/linux/net.rs index ff96125c37bd..94081c8dd31c 100644 --- a/library/std/src/os/linux/net.rs +++ b/library/std/src/os/linux/net.rs @@ -1,4 +1,9 @@ -//! Linux and Android-specific definitions for socket options. +//! Linux-specific networking functionality. #![unstable(feature = "tcp_quickack", issue = "96256")] -pub use crate::os::net::tcp::TcpStreamExt; + +#[unstable(feature = "unix_socket_abstract", issue = "85410")] +pub use crate::os::net::linux_ext::addr::SocketAddrExt; + +#[unstable(feature = "tcp_quickack", issue = "96256")] +pub use crate::os::net::linux_ext::tcp::TcpStreamExt; diff --git a/library/std/src/os/net/linux_ext/addr.rs b/library/std/src/os/net/linux_ext/addr.rs new file mode 100644 index 000000000000..df3fc8e6a3b6 --- /dev/null +++ b/library/std/src/os/net/linux_ext/addr.rs @@ -0,0 +1,64 @@ +//! Linux and Android-specific extensions to socket addresses. + +use crate::os::unix::net::SocketAddr; +use crate::sealed::Sealed; + +/// Platform-specific extensions to [`SocketAddr`]. +#[unstable(feature = "unix_socket_abstract", issue = "85410")] +pub trait SocketAddrExt: Sealed { + /// Creates a Unix socket address in the abstract namespace. + /// + /// The abstract namespace is a Linux-specific extension that allows Unix + /// sockets to be bound without creating an entry in the filesystem. + /// Abstract sockets are unaffected by filesystem layout or permissions, + /// and no cleanup is necessary when the socket is closed. + /// + /// An abstract socket address name may contain any bytes, including zero. + /// + /// # Errors + /// + /// Returns an error if the name is longer than `SUN_LEN - 1`. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(unix_socket_abstract)] + /// use std::os::unix::net::{UnixListener, SocketAddr}; + /// use std::os::linux::net::SocketAddrExt; + /// + /// fn main() -> std::io::Result<()> { + /// let addr = SocketAddr::from_abstract_name(b"hidden")?; + /// let listener = match UnixListener::bind_addr(&addr) { + /// Ok(sock) => sock, + /// Err(err) => { + /// println!("Couldn't bind: {err:?}"); + /// return Err(err); + /// } + /// }; + /// Ok(()) + /// } + /// ``` + fn from_abstract_name(name: &N) -> crate::io::Result + where + N: AsRef<[u8]>; + + /// Returns the contents of this address if it is in the abstract namespace. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(unix_socket_abstract)] + /// use std::os::unix::net::{UnixListener, SocketAddr}; + /// use std::os::linux::net::SocketAddrExt; + /// + /// fn main() -> std::io::Result<()> { + /// let name = b"hidden"; + /// let name_addr = SocketAddr::from_abstract_name(name)?; + /// let socket = UnixListener::bind_addr(&name_addr)?; + /// let local_addr = socket.local_addr().expect("Couldn't get local address"); + /// assert_eq!(local_addr.as_abstract_name(), Some(&name[..])); + /// Ok(()) + /// } + /// ``` + fn as_abstract_name(&self) -> Option<&[u8]>; +} diff --git a/library/std/src/os/net/linux_ext/mod.rs b/library/std/src/os/net/linux_ext/mod.rs new file mode 100644 index 000000000000..318ebacfd7a0 --- /dev/null +++ b/library/std/src/os/net/linux_ext/mod.rs @@ -0,0 +1,12 @@ +//! Linux and Android-specific networking functionality. + +#![doc(cfg(any(target_os = "linux", target_os = "android")))] + +#[unstable(feature = "unix_socket_abstract", issue = "85410")] +pub(crate) mod addr; + +#[unstable(feature = "tcp_quickack", issue = "96256")] +pub(crate) mod tcp; + +#[cfg(test)] +mod tests; diff --git a/library/std/src/os/net/tcp.rs b/library/std/src/os/net/linux_ext/tcp.rs similarity index 100% rename from library/std/src/os/net/tcp.rs rename to library/std/src/os/net/linux_ext/tcp.rs diff --git a/library/std/src/os/net/tests.rs b/library/std/src/os/net/linux_ext/tests.rs similarity index 88% rename from library/std/src/os/net/tests.rs rename to library/std/src/os/net/linux_ext/tests.rs index 4704e3156913..2db4deed0363 100644 --- a/library/std/src/os/net/tests.rs +++ b/library/std/src/os/net/linux_ext/tests.rs @@ -1,9 +1,8 @@ -#[cfg(any(target_os = "android", target_os = "linux",))] #[test] fn quickack() { use crate::{ net::{test::next_test_ip4, TcpListener, TcpStream}, - os::net::tcp::TcpStreamExt, + os::net::linux_ext::tcp::TcpStreamExt, }; macro_rules! t { diff --git a/library/std/src/os/net/mod.rs b/library/std/src/os/net/mod.rs index d6d84d24ec48..5ec267c41e97 100644 --- a/library/std/src/os/net/mod.rs +++ b/library/std/src/os/net/mod.rs @@ -1,7 +1,4 @@ -//! Linux and Android-specific definitions for socket options. +//! OS-specific networking functionality. -#![unstable(feature = "tcp_quickack", issue = "96256")] -#![doc(cfg(any(target_os = "linux", target_os = "android",)))] -pub mod tcp; -#[cfg(test)] -mod tests; +#[cfg(any(target_os = "linux", target_os = "android", doc))] +pub(super) mod linux_ext; diff --git a/library/std/src/os/unix/net/addr.rs b/library/std/src/os/unix/net/addr.rs index 094085e19428..81ac829d21bc 100644 --- a/library/std/src/os/unix/net/addr.rs +++ b/library/std/src/os/unix/net/addr.rs @@ -1,6 +1,9 @@ use crate::ffi::OsStr; +#[cfg(any(doc, target_os = "android", target_os = "linux"))] +use crate::os::net::linux_ext; use crate::os::unix::ffi::OsStrExt; use crate::path::Path; +use crate::sealed::Sealed; use crate::sys::cvt; use crate::{fmt, io, mem, ptr}; @@ -224,31 +227,6 @@ impl SocketAddr { if let AddressKind::Pathname(path) = self.address() { Some(path) } else { None } } - /// Returns the contents of this address if it is an abstract namespace - /// without the leading null byte. - /// - /// # Examples - /// - /// ```no_run - /// #![feature(unix_socket_abstract)] - /// use std::os::unix::net::{UnixListener, SocketAddr}; - /// - /// fn main() -> std::io::Result<()> { - /// let namespace = b"hidden"; - /// let namespace_addr = SocketAddr::from_abstract_namespace(&namespace[..])?; - /// let socket = UnixListener::bind_addr(&namespace_addr)?; - /// let local_addr = socket.local_addr().expect("Couldn't get local address"); - /// assert_eq!(local_addr.as_abstract_namespace(), Some(&namespace[..])); - /// Ok(()) - /// } - /// ``` - #[doc(cfg(any(target_os = "android", target_os = "linux")))] - #[cfg(any(doc, target_os = "android", target_os = "linux",))] - #[unstable(feature = "unix_socket_abstract", issue = "85410")] - pub fn as_abstract_namespace(&self) -> Option<&[u8]> { - if let AddressKind::Abstract(name) = self.address() { Some(name) } else { None } - } - fn address(&self) -> AddressKind<'_> { let len = self.len as usize - sun_path_offset(&self.addr); let path = unsafe { mem::transmute::<&[libc::c_char], &[u8]>(&self.addr.sun_path) }; @@ -265,62 +243,41 @@ impl SocketAddr { AddressKind::Pathname(OsStr::from_bytes(&path[..len - 1]).as_ref()) } } +} - /// Creates an abstract domain socket address from a namespace - /// - /// An abstract address does not create a file unlike traditional path-based - /// Unix sockets. The advantage of this is that the address will disappear when - /// the socket bound to it is closed, so no filesystem clean up is required. - /// - /// The leading null byte for the abstract namespace is automatically added. - /// - /// This is a Linux-specific extension. See more at [`unix(7)`]. - /// - /// [`unix(7)`]: https://man7.org/linux/man-pages/man7/unix.7.html - /// - /// # Errors - /// - /// This will return an error if the given namespace is too long - /// - /// # Examples - /// - /// ```no_run - /// #![feature(unix_socket_abstract)] - /// use std::os::unix::net::{UnixListener, SocketAddr}; - /// - /// fn main() -> std::io::Result<()> { - /// let addr = SocketAddr::from_abstract_namespace(b"hidden")?; - /// let listener = match UnixListener::bind_addr(&addr) { - /// Ok(sock) => sock, - /// Err(err) => { - /// println!("Couldn't bind: {err:?}"); - /// return Err(err); - /// } - /// }; - /// Ok(()) - /// } - /// ``` - #[doc(cfg(any(target_os = "android", target_os = "linux")))] - #[cfg(any(doc, target_os = "android", target_os = "linux",))] - #[unstable(feature = "unix_socket_abstract", issue = "85410")] - pub fn from_abstract_namespace(namespace: &[u8]) -> io::Result { +#[unstable(feature = "unix_socket_abstract", issue = "85410")] +impl Sealed for SocketAddr {} + +#[doc(cfg(any(target_os = "android", target_os = "linux")))] +#[cfg(any(doc, target_os = "android", target_os = "linux"))] +#[unstable(feature = "unix_socket_abstract", issue = "85410")] +impl linux_ext::addr::SocketAddrExt for SocketAddr { + fn as_abstract_name(&self) -> Option<&[u8]> { + if let AddressKind::Abstract(name) = self.address() { Some(name) } else { None } + } + + fn from_abstract_name(name: &N) -> crate::io::Result + where + N: AsRef<[u8]>, + { + let name = name.as_ref(); unsafe { let mut addr: libc::sockaddr_un = mem::zeroed(); addr.sun_family = libc::AF_UNIX as libc::sa_family_t; - if namespace.len() + 1 > addr.sun_path.len() { + if name.len() + 1 > addr.sun_path.len() { return Err(io::const_io_error!( io::ErrorKind::InvalidInput, - "namespace must be shorter than SUN_LEN", + "abstract socket name must be shorter than SUN_LEN", )); } crate::ptr::copy_nonoverlapping( - namespace.as_ptr(), + name.as_ptr(), addr.sun_path.as_mut_ptr().add(1) as *mut u8, - namespace.len(), + name.len(), ); - let len = (sun_path_offset(&addr) + 1 + namespace.len()) as libc::socklen_t; + let len = (sun_path_offset(&addr) + 1 + name.len()) as libc::socklen_t; SocketAddr::from_parts(addr, len) } } diff --git a/library/std/src/os/unix/net/tests.rs b/library/std/src/os/unix/net/tests.rs index e4499f9b6a6d..37fcfa8446b0 100644 --- a/library/std/src/os/unix/net/tests.rs +++ b/library/std/src/os/unix/net/tests.rs @@ -7,6 +7,12 @@ use crate::sys_common::io::test::tmpdir; use crate::thread; use crate::time::Duration; +#[cfg(target_os = "android")] +use crate::os::android::net::SocketAddrExt; + +#[cfg(target_os = "linux")] +use crate::os::linux::net::SocketAddrExt; + macro_rules! or_panic { ($e:expr) => { match $e { @@ -404,7 +410,7 @@ fn test_abstract_stream_connect() { let msg1 = b"hello"; let msg2 = b"world"; - let socket_addr = or_panic!(SocketAddr::from_abstract_namespace(b"namespace")); + let socket_addr = or_panic!(SocketAddr::from_abstract_name(b"name")); let listener = or_panic!(UnixListener::bind_addr(&socket_addr)); let thread = thread::spawn(move || { @@ -418,7 +424,7 @@ fn test_abstract_stream_connect() { let mut stream = or_panic!(UnixStream::connect_addr(&socket_addr)); let peer = or_panic!(stream.peer_addr()); - assert_eq!(peer.as_abstract_namespace().unwrap(), b"namespace"); + assert_eq!(peer.as_abstract_name().unwrap(), b"name"); or_panic!(stream.write_all(msg1)); let mut buf = vec![]; @@ -432,7 +438,7 @@ fn test_abstract_stream_connect() { #[cfg(any(target_os = "android", target_os = "linux"))] #[test] fn test_abstract_stream_iter() { - let addr = or_panic!(SocketAddr::from_abstract_namespace(b"hidden")); + let addr = or_panic!(SocketAddr::from_abstract_name(b"hidden")); let listener = or_panic!(UnixListener::bind_addr(&addr)); let thread = thread::spawn(move || { @@ -454,13 +460,13 @@ fn test_abstract_stream_iter() { #[cfg(any(target_os = "android", target_os = "linux"))] #[test] fn test_abstract_datagram_bind_send_to_addr() { - let addr1 = or_panic!(SocketAddr::from_abstract_namespace(b"ns1")); + let addr1 = or_panic!(SocketAddr::from_abstract_name(b"ns1")); let sock1 = or_panic!(UnixDatagram::bind_addr(&addr1)); let local = or_panic!(sock1.local_addr()); - assert_eq!(local.as_abstract_namespace().unwrap(), b"ns1"); + assert_eq!(local.as_abstract_name().unwrap(), b"ns1"); - let addr2 = or_panic!(SocketAddr::from_abstract_namespace(b"ns2")); + let addr2 = or_panic!(SocketAddr::from_abstract_name(b"ns2")); let sock2 = or_panic!(UnixDatagram::bind_addr(&addr2)); let msg = b"hello world"; @@ -469,13 +475,13 @@ fn test_abstract_datagram_bind_send_to_addr() { let (len, addr) = or_panic!(sock2.recv_from(&mut buf)); assert_eq!(msg, &buf[..]); assert_eq!(len, 11); - assert_eq!(addr.as_abstract_namespace().unwrap(), b"ns1"); + assert_eq!(addr.as_abstract_name().unwrap(), b"ns1"); } #[cfg(any(target_os = "android", target_os = "linux"))] #[test] fn test_abstract_datagram_connect_addr() { - let addr1 = or_panic!(SocketAddr::from_abstract_namespace(b"ns3")); + let addr1 = or_panic!(SocketAddr::from_abstract_name(b"ns3")); let bsock1 = or_panic!(UnixDatagram::bind_addr(&addr1)); let sock = or_panic!(UnixDatagram::unbound()); @@ -489,7 +495,7 @@ fn test_abstract_datagram_connect_addr() { assert_eq!(addr.is_unnamed(), true); assert_eq!(msg, &buf[..]); - let addr2 = or_panic!(SocketAddr::from_abstract_namespace(b"ns4")); + let addr2 = or_panic!(SocketAddr::from_abstract_name(b"ns4")); let bsock2 = or_panic!(UnixDatagram::bind_addr(&addr2)); or_panic!(sock.connect_addr(&addr2)); @@ -499,8 +505,8 @@ fn test_abstract_datagram_connect_addr() { #[cfg(any(target_os = "android", target_os = "linux"))] #[test] -fn test_abstract_namespace_too_long() { - match SocketAddr::from_abstract_namespace( +fn test_abstract_name_too_long() { + match SocketAddr::from_abstract_name( b"abcdefghijklmnopqrstuvwxyzabcdefghijklmn\ opqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghi\ jklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", @@ -513,11 +519,11 @@ fn test_abstract_namespace_too_long() { #[cfg(any(target_os = "android", target_os = "linux"))] #[test] -fn test_abstract_namespace_no_pathname_and_not_unnamed() { - let namespace = b"local"; - let addr = or_panic!(SocketAddr::from_abstract_namespace(&namespace[..])); +fn test_abstract_no_pathname_and_not_unnamed() { + let name = b"local"; + let addr = or_panic!(SocketAddr::from_abstract_name(name)); assert_eq!(addr.as_pathname(), None); - assert_eq!(addr.as_abstract_namespace(), Some(&namespace[..])); + assert_eq!(addr.as_abstract_name(), Some(&name[..])); assert_eq!(addr.is_unnamed(), false); } diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 9d63281627d6..af88b9070c18 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -2142,7 +2142,10 @@ impl Path { /// Returns the `Path` without its final component, if there is one. /// - /// Returns [`None`] if the path terminates in a root or prefix. + /// This means it returns `Some("")` for relative paths with one component. + /// + /// Returns [`None`] if the path terminates in a root or prefix, or if it's + /// the empty string. /// /// # Examples /// @@ -2156,6 +2159,14 @@ impl Path { /// let grand_parent = parent.parent().unwrap(); /// assert_eq!(grand_parent, Path::new("/")); /// assert_eq!(grand_parent.parent(), None); + /// + /// let relative_path = Path::new("foo/bar"); + /// let parent = relative_path.parent(); + /// assert_eq!(parent, Some(Path::new("foo"))); + /// let grand_parent = parent.and_then(Path::parent); + /// assert_eq!(grand_parent, Some(Path::new(""))); + /// let great_grand_parent = grand_parent.and_then(Path::parent); + /// assert_eq!(great_grand_parent, None); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[doc(alias = "dirname")] diff --git a/library/std/src/personality/gcc.rs b/library/std/src/personality/gcc.rs index 7f0b0439cf08..5fc1b91a1c35 100644 --- a/library/std/src/personality/gcc.rs +++ b/library/std/src/personality/gcc.rs @@ -219,7 +219,7 @@ cfg_if::cfg_if! { } cfg_if::cfg_if! { - if #[cfg(all(windows, target_arch = "x86_64", target_env = "gnu"))] { + if #[cfg(all(windows, any(target_arch = "aarch64", target_arch = "x86_64"), target_env = "gnu"))] { // On x86_64 MinGW targets, the unwinding mechanism is SEH however the unwind // handler data (aka LSDA) uses GCC-compatible encoding. #[lang = "eh_personality"] diff --git a/library/std/src/prelude/v1.rs b/library/std/src/prelude/v1.rs index 36d9e8921ef3..d5ac16e6b94e 100644 --- a/library/std/src/prelude/v1.rs +++ b/library/std/src/prelude/v1.rs @@ -65,6 +65,10 @@ pub use core::prelude::v1::alloc_error_handler; #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] pub use core::prelude::v1::{bench, derive, global_allocator, test, test_case}; +#[unstable(feature = "derive_const", issue = "none")] +#[cfg(not(bootstrap))] +pub use core::prelude::v1::derive_const; + // Do not `doc(no_inline)` either. #[unstable( feature = "cfg_accessible", diff --git a/library/std/src/sync/condvar.rs b/library/std/src/sync/condvar.rs index eb1e7135a6e4..76a1b4a2a86c 100644 --- a/library/std/src/sync/condvar.rs +++ b/library/std/src/sync/condvar.rs @@ -3,7 +3,7 @@ mod tests; use crate::fmt; use crate::sync::{mutex, poison, LockResult, MutexGuard, PoisonError}; -use crate::sys_common::condvar as sys; +use crate::sys::locks as sys; use crate::time::{Duration, Instant}; /// A type indicating whether a timed wait on a condition variable returned diff --git a/library/std/src/sync/mod.rs b/library/std/src/sync/mod.rs index 7b507a169b39..4fee8d3e92fc 100644 --- a/library/std/src/sync/mod.rs +++ b/library/std/src/sync/mod.rs @@ -182,6 +182,7 @@ pub mod mpsc; mod barrier; mod condvar; mod lazy_lock; +mod mpmc; mod mutex; mod once; mod once_lock; diff --git a/library/std/src/sync/mpmc/array.rs b/library/std/src/sync/mpmc/array.rs new file mode 100644 index 000000000000..4db7b4990b97 --- /dev/null +++ b/library/std/src/sync/mpmc/array.rs @@ -0,0 +1,513 @@ +//! Bounded channel based on a preallocated array. +//! +//! This flavor has a fixed, positive capacity. +//! +//! The implementation is based on Dmitry Vyukov's bounded MPMC queue. +//! +//! Source: +//! - +//! - + +use super::context::Context; +use super::error::*; +use super::select::{Operation, Selected, Token}; +use super::utils::{Backoff, CachePadded}; +use super::waker::SyncWaker; + +use crate::cell::UnsafeCell; +use crate::mem::MaybeUninit; +use crate::ptr; +use crate::sync::atomic::{self, AtomicUsize, Ordering}; +use crate::time::Instant; + +/// A slot in a channel. +struct Slot { + /// The current stamp. + stamp: AtomicUsize, + + /// The message in this slot. + msg: UnsafeCell>, +} + +/// The token type for the array flavor. +#[derive(Debug)] +pub(crate) struct ArrayToken { + /// Slot to read from or write to. + slot: *const u8, + + /// Stamp to store into the slot after reading or writing. + stamp: usize, +} + +impl Default for ArrayToken { + #[inline] + fn default() -> Self { + ArrayToken { slot: ptr::null(), stamp: 0 } + } +} + +/// Bounded channel based on a preallocated array. +pub(crate) struct Channel { + /// The head of the channel. + /// + /// This value is a "stamp" consisting of an index into the buffer, a mark bit, and a lap, but + /// packed into a single `usize`. The lower bits represent the index, while the upper bits + /// represent the lap. The mark bit in the head is always zero. + /// + /// Messages are popped from the head of the channel. + head: CachePadded, + + /// The tail of the channel. + /// + /// This value is a "stamp" consisting of an index into the buffer, a mark bit, and a lap, but + /// packed into a single `usize`. The lower bits represent the index, while the upper bits + /// represent the lap. The mark bit indicates that the channel is disconnected. + /// + /// Messages are pushed into the tail of the channel. + tail: CachePadded, + + /// The buffer holding slots. + buffer: Box<[Slot]>, + + /// The channel capacity. + cap: usize, + + /// A stamp with the value of `{ lap: 1, mark: 0, index: 0 }`. + one_lap: usize, + + /// If this bit is set in the tail, that means the channel is disconnected. + mark_bit: usize, + + /// Senders waiting while the channel is full. + senders: SyncWaker, + + /// Receivers waiting while the channel is empty and not disconnected. + receivers: SyncWaker, +} + +impl Channel { + /// Creates a bounded channel of capacity `cap`. + pub(crate) fn with_capacity(cap: usize) -> Self { + assert!(cap > 0, "capacity must be positive"); + + // Compute constants `mark_bit` and `one_lap`. + let mark_bit = (cap + 1).next_power_of_two(); + let one_lap = mark_bit * 2; + + // Head is initialized to `{ lap: 0, mark: 0, index: 0 }`. + let head = 0; + // Tail is initialized to `{ lap: 0, mark: 0, index: 0 }`. + let tail = 0; + + // Allocate a buffer of `cap` slots initialized + // with stamps. + let buffer: Box<[Slot]> = (0..cap) + .map(|i| { + // Set the stamp to `{ lap: 0, mark: 0, index: i }`. + Slot { stamp: AtomicUsize::new(i), msg: UnsafeCell::new(MaybeUninit::uninit()) } + }) + .collect(); + + Channel { + buffer, + cap, + one_lap, + mark_bit, + head: CachePadded::new(AtomicUsize::new(head)), + tail: CachePadded::new(AtomicUsize::new(tail)), + senders: SyncWaker::new(), + receivers: SyncWaker::new(), + } + } + + /// Attempts to reserve a slot for sending a message. + fn start_send(&self, token: &mut Token) -> bool { + let backoff = Backoff::new(); + let mut tail = self.tail.load(Ordering::Relaxed); + + loop { + // Check if the channel is disconnected. + if tail & self.mark_bit != 0 { + token.array.slot = ptr::null(); + token.array.stamp = 0; + return true; + } + + // Deconstruct the tail. + let index = tail & (self.mark_bit - 1); + let lap = tail & !(self.one_lap - 1); + + // Inspect the corresponding slot. + debug_assert!(index < self.buffer.len()); + let slot = unsafe { self.buffer.get_unchecked(index) }; + let stamp = slot.stamp.load(Ordering::Acquire); + + // If the tail and the stamp match, we may attempt to push. + if tail == stamp { + let new_tail = if index + 1 < self.cap { + // Same lap, incremented index. + // Set to `{ lap: lap, mark: 0, index: index + 1 }`. + tail + 1 + } else { + // One lap forward, index wraps around to zero. + // Set to `{ lap: lap.wrapping_add(1), mark: 0, index: 0 }`. + lap.wrapping_add(self.one_lap) + }; + + // Try moving the tail. + match self.tail.compare_exchange_weak( + tail, + new_tail, + Ordering::SeqCst, + Ordering::Relaxed, + ) { + Ok(_) => { + // Prepare the token for the follow-up call to `write`. + token.array.slot = slot as *const Slot as *const u8; + token.array.stamp = tail + 1; + return true; + } + Err(_) => { + backoff.spin(); + tail = self.tail.load(Ordering::Relaxed); + } + } + } else if stamp.wrapping_add(self.one_lap) == tail + 1 { + atomic::fence(Ordering::SeqCst); + let head = self.head.load(Ordering::Relaxed); + + // If the head lags one lap behind the tail as well... + if head.wrapping_add(self.one_lap) == tail { + // ...then the channel is full. + return false; + } + + backoff.spin(); + tail = self.tail.load(Ordering::Relaxed); + } else { + // Snooze because we need to wait for the stamp to get updated. + backoff.snooze(); + tail = self.tail.load(Ordering::Relaxed); + } + } + } + + /// Writes a message into the channel. + pub(crate) unsafe fn write(&self, token: &mut Token, msg: T) -> Result<(), T> { + // If there is no slot, the channel is disconnected. + if token.array.slot.is_null() { + return Err(msg); + } + + let slot: &Slot = &*(token.array.slot as *const Slot); + + // Write the message into the slot and update the stamp. + slot.msg.get().write(MaybeUninit::new(msg)); + slot.stamp.store(token.array.stamp, Ordering::Release); + + // Wake a sleeping receiver. + self.receivers.notify(); + Ok(()) + } + + /// Attempts to reserve a slot for receiving a message. + fn start_recv(&self, token: &mut Token) -> bool { + let backoff = Backoff::new(); + let mut head = self.head.load(Ordering::Relaxed); + + loop { + // Deconstruct the head. + let index = head & (self.mark_bit - 1); + let lap = head & !(self.one_lap - 1); + + // Inspect the corresponding slot. + debug_assert!(index < self.buffer.len()); + let slot = unsafe { self.buffer.get_unchecked(index) }; + let stamp = slot.stamp.load(Ordering::Acquire); + + // If the the stamp is ahead of the head by 1, we may attempt to pop. + if head + 1 == stamp { + let new = if index + 1 < self.cap { + // Same lap, incremented index. + // Set to `{ lap: lap, mark: 0, index: index + 1 }`. + head + 1 + } else { + // One lap forward, index wraps around to zero. + // Set to `{ lap: lap.wrapping_add(1), mark: 0, index: 0 }`. + lap.wrapping_add(self.one_lap) + }; + + // Try moving the head. + match self.head.compare_exchange_weak( + head, + new, + Ordering::SeqCst, + Ordering::Relaxed, + ) { + Ok(_) => { + // Prepare the token for the follow-up call to `read`. + token.array.slot = slot as *const Slot as *const u8; + token.array.stamp = head.wrapping_add(self.one_lap); + return true; + } + Err(_) => { + backoff.spin(); + head = self.head.load(Ordering::Relaxed); + } + } + } else if stamp == head { + atomic::fence(Ordering::SeqCst); + let tail = self.tail.load(Ordering::Relaxed); + + // If the tail equals the head, that means the channel is empty. + if (tail & !self.mark_bit) == head { + // If the channel is disconnected... + if tail & self.mark_bit != 0 { + // ...then receive an error. + token.array.slot = ptr::null(); + token.array.stamp = 0; + return true; + } else { + // Otherwise, the receive operation is not ready. + return false; + } + } + + backoff.spin(); + head = self.head.load(Ordering::Relaxed); + } else { + // Snooze because we need to wait for the stamp to get updated. + backoff.snooze(); + head = self.head.load(Ordering::Relaxed); + } + } + } + + /// Reads a message from the channel. + pub(crate) unsafe fn read(&self, token: &mut Token) -> Result { + if token.array.slot.is_null() { + // The channel is disconnected. + return Err(()); + } + + let slot: &Slot = &*(token.array.slot as *const Slot); + + // Read the message from the slot and update the stamp. + let msg = slot.msg.get().read().assume_init(); + slot.stamp.store(token.array.stamp, Ordering::Release); + + // Wake a sleeping sender. + self.senders.notify(); + Ok(msg) + } + + /// Attempts to send a message into the channel. + pub(crate) fn try_send(&self, msg: T) -> Result<(), TrySendError> { + let token = &mut Token::default(); + if self.start_send(token) { + unsafe { self.write(token, msg).map_err(TrySendError::Disconnected) } + } else { + Err(TrySendError::Full(msg)) + } + } + + /// Sends a message into the channel. + pub(crate) fn send( + &self, + msg: T, + deadline: Option, + ) -> Result<(), SendTimeoutError> { + let token = &mut Token::default(); + loop { + // Try sending a message several times. + let backoff = Backoff::new(); + loop { + if self.start_send(token) { + let res = unsafe { self.write(token, msg) }; + return res.map_err(SendTimeoutError::Disconnected); + } + + if backoff.is_completed() { + break; + } else { + backoff.spin(); + } + } + + if let Some(d) = deadline { + if Instant::now() >= d { + return Err(SendTimeoutError::Timeout(msg)); + } + } + + Context::with(|cx| { + // Prepare for blocking until a receiver wakes us up. + let oper = Operation::hook(token); + self.senders.register(oper, cx); + + // Has the channel become ready just now? + if !self.is_full() || self.is_disconnected() { + let _ = cx.try_select(Selected::Aborted); + } + + // Block the current thread. + let sel = cx.wait_until(deadline); + + match sel { + Selected::Waiting => unreachable!(), + Selected::Aborted | Selected::Disconnected => { + self.senders.unregister(oper).unwrap(); + } + Selected::Operation(_) => {} + } + }); + } + } + + /// Attempts to receive a message without blocking. + pub(crate) fn try_recv(&self) -> Result { + let token = &mut Token::default(); + + if self.start_recv(token) { + unsafe { self.read(token).map_err(|_| TryRecvError::Disconnected) } + } else { + Err(TryRecvError::Empty) + } + } + + /// Receives a message from the channel. + pub(crate) fn recv(&self, deadline: Option) -> Result { + let token = &mut Token::default(); + loop { + if self.start_recv(token) { + let res = unsafe { self.read(token) }; + return res.map_err(|_| RecvTimeoutError::Disconnected); + } + + if let Some(d) = deadline { + if Instant::now() >= d { + return Err(RecvTimeoutError::Timeout); + } + } + + Context::with(|cx| { + // Prepare for blocking until a sender wakes us up. + let oper = Operation::hook(token); + self.receivers.register(oper, cx); + + // Has the channel become ready just now? + if !self.is_empty() || self.is_disconnected() { + let _ = cx.try_select(Selected::Aborted); + } + + // Block the current thread. + let sel = cx.wait_until(deadline); + + match sel { + Selected::Waiting => unreachable!(), + Selected::Aborted | Selected::Disconnected => { + self.receivers.unregister(oper).unwrap(); + // If the channel was disconnected, we still have to check for remaining + // messages. + } + Selected::Operation(_) => {} + } + }); + } + } + + /// Returns the current number of messages inside the channel. + pub(crate) fn len(&self) -> usize { + loop { + // Load the tail, then load the head. + let tail = self.tail.load(Ordering::SeqCst); + let head = self.head.load(Ordering::SeqCst); + + // If the tail didn't change, we've got consistent values to work with. + if self.tail.load(Ordering::SeqCst) == tail { + let hix = head & (self.mark_bit - 1); + let tix = tail & (self.mark_bit - 1); + + return if hix < tix { + tix - hix + } else if hix > tix { + self.cap - hix + tix + } else if (tail & !self.mark_bit) == head { + 0 + } else { + self.cap + }; + } + } + } + + /// Returns the capacity of the channel. + #[allow(clippy::unnecessary_wraps)] // This is intentional. + pub(crate) fn capacity(&self) -> Option { + Some(self.cap) + } + + /// Disconnects the channel and wakes up all blocked senders and receivers. + /// + /// Returns `true` if this call disconnected the channel. + pub(crate) fn disconnect(&self) -> bool { + let tail = self.tail.fetch_or(self.mark_bit, Ordering::SeqCst); + + if tail & self.mark_bit == 0 { + self.senders.disconnect(); + self.receivers.disconnect(); + true + } else { + false + } + } + + /// Returns `true` if the channel is disconnected. + pub(crate) fn is_disconnected(&self) -> bool { + self.tail.load(Ordering::SeqCst) & self.mark_bit != 0 + } + + /// Returns `true` if the channel is empty. + pub(crate) fn is_empty(&self) -> bool { + let head = self.head.load(Ordering::SeqCst); + let tail = self.tail.load(Ordering::SeqCst); + + // Is the tail equal to the head? + // + // Note: If the head changes just before we load the tail, that means there was a moment + // when the channel was not empty, so it is safe to just return `false`. + (tail & !self.mark_bit) == head + } + + /// Returns `true` if the channel is full. + pub(crate) fn is_full(&self) -> bool { + let tail = self.tail.load(Ordering::SeqCst); + let head = self.head.load(Ordering::SeqCst); + + // Is the head lagging one lap behind tail? + // + // Note: If the tail changes just before we load the head, that means there was a moment + // when the channel was not full, so it is safe to just return `false`. + head.wrapping_add(self.one_lap) == tail & !self.mark_bit + } +} + +impl Drop for Channel { + fn drop(&mut self) { + // Get the index of the head. + let hix = self.head.load(Ordering::Relaxed) & (self.mark_bit - 1); + + // Loop over all slots that hold a message and drop them. + for i in 0..self.len() { + // Compute the index of the next slot holding a message. + let index = if hix + i < self.cap { hix + i } else { hix + i - self.cap }; + + unsafe { + debug_assert!(index < self.buffer.len()); + let slot = self.buffer.get_unchecked_mut(index); + let msg = &mut *slot.msg.get(); + msg.as_mut_ptr().drop_in_place(); + } + } + } +} diff --git a/library/std/src/sync/mpmc/context.rs b/library/std/src/sync/mpmc/context.rs new file mode 100644 index 000000000000..bbfc6ce00ffc --- /dev/null +++ b/library/std/src/sync/mpmc/context.rs @@ -0,0 +1,155 @@ +//! Thread-local channel context. + +use super::select::Selected; +use super::waker::current_thread_id; + +use crate::cell::Cell; +use crate::ptr; +use crate::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; +use crate::sync::Arc; +use crate::thread::{self, Thread}; +use crate::time::Instant; + +/// Thread-local context. +#[derive(Debug, Clone)] +pub struct Context { + inner: Arc, +} + +/// Inner representation of `Context`. +#[derive(Debug)] +struct Inner { + /// Selected operation. + select: AtomicUsize, + + /// A slot into which another thread may store a pointer to its `Packet`. + packet: AtomicPtr<()>, + + /// Thread handle. + thread: Thread, + + /// Thread id. + thread_id: usize, +} + +impl Context { + /// Creates a new context for the duration of the closure. + #[inline] + pub fn with(f: F) -> R + where + F: FnOnce(&Context) -> R, + { + thread_local! { + /// Cached thread-local context. + static CONTEXT: Cell> = Cell::new(Some(Context::new())); + } + + let mut f = Some(f); + let mut f = |cx: &Context| -> R { + let f = f.take().unwrap(); + f(cx) + }; + + CONTEXT + .try_with(|cell| match cell.take() { + None => f(&Context::new()), + Some(cx) => { + cx.reset(); + let res = f(&cx); + cell.set(Some(cx)); + res + } + }) + .unwrap_or_else(|_| f(&Context::new())) + } + + /// Creates a new `Context`. + #[cold] + fn new() -> Context { + Context { + inner: Arc::new(Inner { + select: AtomicUsize::new(Selected::Waiting.into()), + packet: AtomicPtr::new(ptr::null_mut()), + thread: thread::current(), + thread_id: current_thread_id(), + }), + } + } + + /// Resets `select` and `packet`. + #[inline] + fn reset(&self) { + self.inner.select.store(Selected::Waiting.into(), Ordering::Release); + self.inner.packet.store(ptr::null_mut(), Ordering::Release); + } + + /// Attempts to select an operation. + /// + /// On failure, the previously selected operation is returned. + #[inline] + pub fn try_select(&self, select: Selected) -> Result<(), Selected> { + self.inner + .select + .compare_exchange( + Selected::Waiting.into(), + select.into(), + Ordering::AcqRel, + Ordering::Acquire, + ) + .map(|_| ()) + .map_err(|e| e.into()) + } + + /// Stores a packet. + /// + /// This method must be called after `try_select` succeeds and there is a packet to provide. + #[inline] + pub fn store_packet(&self, packet: *mut ()) { + if !packet.is_null() { + self.inner.packet.store(packet, Ordering::Release); + } + } + + /// Waits until an operation is selected and returns it. + /// + /// If the deadline is reached, `Selected::Aborted` will be selected. + #[inline] + pub fn wait_until(&self, deadline: Option) -> Selected { + loop { + // Check whether an operation has been selected. + let sel = Selected::from(self.inner.select.load(Ordering::Acquire)); + if sel != Selected::Waiting { + return sel; + } + + // If there's a deadline, park the current thread until the deadline is reached. + if let Some(end) = deadline { + let now = Instant::now(); + + if now < end { + thread::park_timeout(end - now); + } else { + // The deadline has been reached. Try aborting select. + return match self.try_select(Selected::Aborted) { + Ok(()) => Selected::Aborted, + Err(s) => s, + }; + } + } else { + thread::park(); + } + } + } + + /// Unparks the thread this context belongs to. + #[inline] + pub fn unpark(&self) { + self.inner.thread.unpark(); + } + + /// Returns the id of the thread this context belongs to. + #[inline] + pub fn thread_id(&self) -> usize { + self.inner.thread_id + } +} diff --git a/library/std/src/sync/mpmc/counter.rs b/library/std/src/sync/mpmc/counter.rs new file mode 100644 index 000000000000..a5a6bdc67f13 --- /dev/null +++ b/library/std/src/sync/mpmc/counter.rs @@ -0,0 +1,137 @@ +use crate::ops; +use crate::process; +use crate::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; + +/// Reference counter internals. +struct Counter { + /// The number of senders associated with the channel. + senders: AtomicUsize, + + /// The number of receivers associated with the channel. + receivers: AtomicUsize, + + /// Set to `true` if the last sender or the last receiver reference deallocates the channel. + destroy: AtomicBool, + + /// The internal channel. + chan: C, +} + +/// Wraps a channel into the reference counter. +pub(crate) fn new(chan: C) -> (Sender, Receiver) { + let counter = Box::into_raw(Box::new(Counter { + senders: AtomicUsize::new(1), + receivers: AtomicUsize::new(1), + destroy: AtomicBool::new(false), + chan, + })); + let s = Sender { counter }; + let r = Receiver { counter }; + (s, r) +} + +/// The sending side. +pub(crate) struct Sender { + counter: *mut Counter, +} + +impl Sender { + /// Returns the internal `Counter`. + fn counter(&self) -> &Counter { + unsafe { &*self.counter } + } + + /// Acquires another sender reference. + pub(crate) fn acquire(&self) -> Sender { + let count = self.counter().senders.fetch_add(1, Ordering::Relaxed); + + // Cloning senders and calling `mem::forget` on the clones could potentially overflow the + // counter. It's very difficult to recover sensibly from such degenerate scenarios so we + // just abort when the count becomes very large. + if count > isize::MAX as usize { + process::abort(); + } + + Sender { counter: self.counter } + } + + /// Releases the sender reference. + /// + /// Function `disconnect` will be called if this is the last sender reference. + pub(crate) unsafe fn release bool>(&self, disconnect: F) { + if self.counter().senders.fetch_sub(1, Ordering::AcqRel) == 1 { + disconnect(&self.counter().chan); + + if self.counter().destroy.swap(true, Ordering::AcqRel) { + drop(Box::from_raw(self.counter)); + } + } + } +} + +impl ops::Deref for Sender { + type Target = C; + + fn deref(&self) -> &C { + &self.counter().chan + } +} + +impl PartialEq for Sender { + fn eq(&self, other: &Sender) -> bool { + self.counter == other.counter + } +} + +/// The receiving side. +pub(crate) struct Receiver { + counter: *mut Counter, +} + +impl Receiver { + /// Returns the internal `Counter`. + fn counter(&self) -> &Counter { + unsafe { &*self.counter } + } + + /// Acquires another receiver reference. + pub(crate) fn acquire(&self) -> Receiver { + let count = self.counter().receivers.fetch_add(1, Ordering::Relaxed); + + // Cloning receivers and calling `mem::forget` on the clones could potentially overflow the + // counter. It's very difficult to recover sensibly from such degenerate scenarios so we + // just abort when the count becomes very large. + if count > isize::MAX as usize { + process::abort(); + } + + Receiver { counter: self.counter } + } + + /// Releases the receiver reference. + /// + /// Function `disconnect` will be called if this is the last receiver reference. + pub(crate) unsafe fn release bool>(&self, disconnect: F) { + if self.counter().receivers.fetch_sub(1, Ordering::AcqRel) == 1 { + disconnect(&self.counter().chan); + + if self.counter().destroy.swap(true, Ordering::AcqRel) { + drop(Box::from_raw(self.counter)); + } + } + } +} + +impl ops::Deref for Receiver { + type Target = C; + + fn deref(&self) -> &C { + &self.counter().chan + } +} + +impl PartialEq for Receiver { + fn eq(&self, other: &Receiver) -> bool { + self.counter == other.counter + } +} diff --git a/library/std/src/sync/mpmc/error.rs b/library/std/src/sync/mpmc/error.rs new file mode 100644 index 000000000000..1b8a1f387974 --- /dev/null +++ b/library/std/src/sync/mpmc/error.rs @@ -0,0 +1,46 @@ +use crate::error; +use crate::fmt; + +pub use crate::sync::mpsc::{RecvError, RecvTimeoutError, SendError, TryRecvError, TrySendError}; + +/// An error returned from the [`send_timeout`] method. +/// +/// The error contains the message being sent so it can be recovered. +/// +/// [`send_timeout`]: super::Sender::send_timeout +#[derive(PartialEq, Eq, Clone, Copy)] +pub enum SendTimeoutError { + /// The message could not be sent because the channel is full and the operation timed out. + /// + /// If this is a zero-capacity channel, then the error indicates that there was no receiver + /// available to receive the message and the operation timed out. + Timeout(T), + + /// The message could not be sent because the channel is disconnected. + Disconnected(T), +} + +impl fmt::Debug for SendTimeoutError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + "SendTimeoutError(..)".fmt(f) + } +} + +impl fmt::Display for SendTimeoutError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + SendTimeoutError::Timeout(..) => "timed out waiting on send operation".fmt(f), + SendTimeoutError::Disconnected(..) => "sending on a disconnected channel".fmt(f), + } + } +} + +impl error::Error for SendTimeoutError {} + +impl From> for SendTimeoutError { + fn from(err: SendError) -> SendTimeoutError { + match err { + SendError(e) => SendTimeoutError::Disconnected(e), + } + } +} diff --git a/library/std/src/sync/mpmc/list.rs b/library/std/src/sync/mpmc/list.rs new file mode 100644 index 000000000000..2d5b2fb3b231 --- /dev/null +++ b/library/std/src/sync/mpmc/list.rs @@ -0,0 +1,638 @@ +//! Unbounded channel implemented as a linked list. + +use super::context::Context; +use super::error::*; +use super::select::{Operation, Selected, Token}; +use super::utils::{Backoff, CachePadded}; +use super::waker::SyncWaker; + +use crate::cell::UnsafeCell; +use crate::marker::PhantomData; +use crate::mem::MaybeUninit; +use crate::ptr; +use crate::sync::atomic::{self, AtomicPtr, AtomicUsize, Ordering}; +use crate::time::Instant; + +// Bits indicating the state of a slot: +// * If a message has been written into the slot, `WRITE` is set. +// * If a message has been read from the slot, `READ` is set. +// * If the block is being destroyed, `DESTROY` is set. +const WRITE: usize = 1; +const READ: usize = 2; +const DESTROY: usize = 4; + +// Each block covers one "lap" of indices. +const LAP: usize = 32; +// The maximum number of messages a block can hold. +const BLOCK_CAP: usize = LAP - 1; +// How many lower bits are reserved for metadata. +const SHIFT: usize = 1; +// Has two different purposes: +// * If set in head, indicates that the block is not the last one. +// * If set in tail, indicates that the channel is disconnected. +const MARK_BIT: usize = 1; + +/// A slot in a block. +struct Slot { + /// The message. + msg: UnsafeCell>, + + /// The state of the slot. + state: AtomicUsize, +} + +impl Slot { + /// Waits until a message is written into the slot. + fn wait_write(&self) { + let backoff = Backoff::new(); + while self.state.load(Ordering::Acquire) & WRITE == 0 { + backoff.snooze(); + } + } +} + +/// A block in a linked list. +/// +/// Each block in the list can hold up to `BLOCK_CAP` messages. +struct Block { + /// The next block in the linked list. + next: AtomicPtr>, + + /// Slots for messages. + slots: [Slot; BLOCK_CAP], +} + +impl Block { + /// Creates an empty block. + fn new() -> Block { + // SAFETY: This is safe because: + // [1] `Block::next` (AtomicPtr) may be safely zero initialized. + // [2] `Block::slots` (Array) may be safely zero initialized because of [3, 4]. + // [3] `Slot::msg` (UnsafeCell) may be safely zero initialized because it + // holds a MaybeUninit. + // [4] `Slot::state` (AtomicUsize) may be safely zero initialized. + unsafe { MaybeUninit::zeroed().assume_init() } + } + + /// Waits until the next pointer is set. + fn wait_next(&self) -> *mut Block { + let backoff = Backoff::new(); + loop { + let next = self.next.load(Ordering::Acquire); + if !next.is_null() { + return next; + } + backoff.snooze(); + } + } + + /// Sets the `DESTROY` bit in slots starting from `start` and destroys the block. + unsafe fn destroy(this: *mut Block, start: usize) { + // It is not necessary to set the `DESTROY` bit in the last slot because that slot has + // begun destruction of the block. + for i in start..BLOCK_CAP - 1 { + let slot = (*this).slots.get_unchecked(i); + + // Mark the `DESTROY` bit if a thread is still using the slot. + if slot.state.load(Ordering::Acquire) & READ == 0 + && slot.state.fetch_or(DESTROY, Ordering::AcqRel) & READ == 0 + { + // If a thread is still using the slot, it will continue destruction of the block. + return; + } + } + + // No thread is using the block, now it is safe to destroy it. + drop(Box::from_raw(this)); + } +} + +/// A position in a channel. +#[derive(Debug)] +struct Position { + /// The index in the channel. + index: AtomicUsize, + + /// The block in the linked list. + block: AtomicPtr>, +} + +/// The token type for the list flavor. +#[derive(Debug)] +pub(crate) struct ListToken { + /// The block of slots. + block: *const u8, + + /// The offset into the block. + offset: usize, +} + +impl Default for ListToken { + #[inline] + fn default() -> Self { + ListToken { block: ptr::null(), offset: 0 } + } +} + +/// Unbounded channel implemented as a linked list. +/// +/// Each message sent into the channel is assigned a sequence number, i.e. an index. Indices are +/// represented as numbers of type `usize` and wrap on overflow. +/// +/// Consecutive messages are grouped into blocks in order to put less pressure on the allocator and +/// improve cache efficiency. +pub(crate) struct Channel { + /// The head of the channel. + head: CachePadded>, + + /// The tail of the channel. + tail: CachePadded>, + + /// Receivers waiting while the channel is empty and not disconnected. + receivers: SyncWaker, + + /// Indicates that dropping a `Channel` may drop messages of type `T`. + _marker: PhantomData, +} + +impl Channel { + /// Creates a new unbounded channel. + pub(crate) fn new() -> Self { + Channel { + head: CachePadded::new(Position { + block: AtomicPtr::new(ptr::null_mut()), + index: AtomicUsize::new(0), + }), + tail: CachePadded::new(Position { + block: AtomicPtr::new(ptr::null_mut()), + index: AtomicUsize::new(0), + }), + receivers: SyncWaker::new(), + _marker: PhantomData, + } + } + + /// Attempts to reserve a slot for sending a message. + fn start_send(&self, token: &mut Token) -> bool { + let backoff = Backoff::new(); + let mut tail = self.tail.index.load(Ordering::Acquire); + let mut block = self.tail.block.load(Ordering::Acquire); + let mut next_block = None; + + loop { + // Check if the channel is disconnected. + if tail & MARK_BIT != 0 { + token.list.block = ptr::null(); + return true; + } + + // Calculate the offset of the index into the block. + let offset = (tail >> SHIFT) % LAP; + + // If we reached the end of the block, wait until the next one is installed. + if offset == BLOCK_CAP { + backoff.snooze(); + tail = self.tail.index.load(Ordering::Acquire); + block = self.tail.block.load(Ordering::Acquire); + continue; + } + + // If we're going to have to install the next block, allocate it in advance in order to + // make the wait for other threads as short as possible. + if offset + 1 == BLOCK_CAP && next_block.is_none() { + next_block = Some(Box::new(Block::::new())); + } + + // If this is the first message to be sent into the channel, we need to allocate the + // first block and install it. + if block.is_null() { + let new = Box::into_raw(Box::new(Block::::new())); + + if self + .tail + .block + .compare_exchange(block, new, Ordering::Release, Ordering::Relaxed) + .is_ok() + { + self.head.block.store(new, Ordering::Release); + block = new; + } else { + next_block = unsafe { Some(Box::from_raw(new)) }; + tail = self.tail.index.load(Ordering::Acquire); + block = self.tail.block.load(Ordering::Acquire); + continue; + } + } + + let new_tail = tail + (1 << SHIFT); + + // Try advancing the tail forward. + match self.tail.index.compare_exchange_weak( + tail, + new_tail, + Ordering::SeqCst, + Ordering::Acquire, + ) { + Ok(_) => unsafe { + // If we've reached the end of the block, install the next one. + if offset + 1 == BLOCK_CAP { + let next_block = Box::into_raw(next_block.unwrap()); + self.tail.block.store(next_block, Ordering::Release); + self.tail.index.fetch_add(1 << SHIFT, Ordering::Release); + (*block).next.store(next_block, Ordering::Release); + } + + token.list.block = block as *const u8; + token.list.offset = offset; + return true; + }, + Err(_) => { + backoff.spin(); + tail = self.tail.index.load(Ordering::Acquire); + block = self.tail.block.load(Ordering::Acquire); + } + } + } + } + + /// Writes a message into the channel. + pub(crate) unsafe fn write(&self, token: &mut Token, msg: T) -> Result<(), T> { + // If there is no slot, the channel is disconnected. + if token.list.block.is_null() { + return Err(msg); + } + + // Write the message into the slot. + let block = token.list.block as *mut Block; + let offset = token.list.offset; + let slot = (*block).slots.get_unchecked(offset); + slot.msg.get().write(MaybeUninit::new(msg)); + slot.state.fetch_or(WRITE, Ordering::Release); + + // Wake a sleeping receiver. + self.receivers.notify(); + Ok(()) + } + + /// Attempts to reserve a slot for receiving a message. + fn start_recv(&self, token: &mut Token) -> bool { + let backoff = Backoff::new(); + let mut head = self.head.index.load(Ordering::Acquire); + let mut block = self.head.block.load(Ordering::Acquire); + + loop { + // Calculate the offset of the index into the block. + let offset = (head >> SHIFT) % LAP; + + // If we reached the end of the block, wait until the next one is installed. + if offset == BLOCK_CAP { + backoff.snooze(); + head = self.head.index.load(Ordering::Acquire); + block = self.head.block.load(Ordering::Acquire); + continue; + } + + let mut new_head = head + (1 << SHIFT); + + if new_head & MARK_BIT == 0 { + atomic::fence(Ordering::SeqCst); + let tail = self.tail.index.load(Ordering::Relaxed); + + // If the tail equals the head, that means the channel is empty. + if head >> SHIFT == tail >> SHIFT { + // If the channel is disconnected... + if tail & MARK_BIT != 0 { + // ...then receive an error. + token.list.block = ptr::null(); + return true; + } else { + // Otherwise, the receive operation is not ready. + return false; + } + } + + // If head and tail are not in the same block, set `MARK_BIT` in head. + if (head >> SHIFT) / LAP != (tail >> SHIFT) / LAP { + new_head |= MARK_BIT; + } + } + + // The block can be null here only if the first message is being sent into the channel. + // In that case, just wait until it gets initialized. + if block.is_null() { + backoff.snooze(); + head = self.head.index.load(Ordering::Acquire); + block = self.head.block.load(Ordering::Acquire); + continue; + } + + // Try moving the head index forward. + match self.head.index.compare_exchange_weak( + head, + new_head, + Ordering::SeqCst, + Ordering::Acquire, + ) { + Ok(_) => unsafe { + // If we've reached the end of the block, move to the next one. + if offset + 1 == BLOCK_CAP { + let next = (*block).wait_next(); + let mut next_index = (new_head & !MARK_BIT).wrapping_add(1 << SHIFT); + if !(*next).next.load(Ordering::Relaxed).is_null() { + next_index |= MARK_BIT; + } + + self.head.block.store(next, Ordering::Release); + self.head.index.store(next_index, Ordering::Release); + } + + token.list.block = block as *const u8; + token.list.offset = offset; + return true; + }, + Err(_) => { + backoff.spin(); + head = self.head.index.load(Ordering::Acquire); + block = self.head.block.load(Ordering::Acquire); + } + } + } + } + + /// Reads a message from the channel. + pub(crate) unsafe fn read(&self, token: &mut Token) -> Result { + if token.list.block.is_null() { + // The channel is disconnected. + return Err(()); + } + + // Read the message. + let block = token.list.block as *mut Block; + let offset = token.list.offset; + let slot = (*block).slots.get_unchecked(offset); + slot.wait_write(); + let msg = slot.msg.get().read().assume_init(); + + // Destroy the block if we've reached the end, or if another thread wanted to destroy but + // couldn't because we were busy reading from the slot. + if offset + 1 == BLOCK_CAP { + Block::destroy(block, 0); + } else if slot.state.fetch_or(READ, Ordering::AcqRel) & DESTROY != 0 { + Block::destroy(block, offset + 1); + } + + Ok(msg) + } + + /// Attempts to send a message into the channel. + pub(crate) fn try_send(&self, msg: T) -> Result<(), TrySendError> { + self.send(msg, None).map_err(|err| match err { + SendTimeoutError::Disconnected(msg) => TrySendError::Disconnected(msg), + SendTimeoutError::Timeout(_) => unreachable!(), + }) + } + + /// Sends a message into the channel. + pub(crate) fn send( + &self, + msg: T, + _deadline: Option, + ) -> Result<(), SendTimeoutError> { + let token = &mut Token::default(); + assert!(self.start_send(token)); + unsafe { self.write(token, msg).map_err(SendTimeoutError::Disconnected) } + } + + /// Attempts to receive a message without blocking. + pub(crate) fn try_recv(&self) -> Result { + let token = &mut Token::default(); + + if self.start_recv(token) { + unsafe { self.read(token).map_err(|_| TryRecvError::Disconnected) } + } else { + Err(TryRecvError::Empty) + } + } + + /// Receives a message from the channel. + pub(crate) fn recv(&self, deadline: Option) -> Result { + let token = &mut Token::default(); + loop { + if self.start_recv(token) { + unsafe { + return self.read(token).map_err(|_| RecvTimeoutError::Disconnected); + } + } + + if let Some(d) = deadline { + if Instant::now() >= d { + return Err(RecvTimeoutError::Timeout); + } + } + + // Prepare for blocking until a sender wakes us up. + Context::with(|cx| { + let oper = Operation::hook(token); + self.receivers.register(oper, cx); + + // Has the channel become ready just now? + if !self.is_empty() || self.is_disconnected() { + let _ = cx.try_select(Selected::Aborted); + } + + // Block the current thread. + let sel = cx.wait_until(deadline); + + match sel { + Selected::Waiting => unreachable!(), + Selected::Aborted | Selected::Disconnected => { + self.receivers.unregister(oper).unwrap(); + // If the channel was disconnected, we still have to check for remaining + // messages. + } + Selected::Operation(_) => {} + } + }); + } + } + + /// Returns the current number of messages inside the channel. + pub(crate) fn len(&self) -> usize { + loop { + // Load the tail index, then load the head index. + let mut tail = self.tail.index.load(Ordering::SeqCst); + let mut head = self.head.index.load(Ordering::SeqCst); + + // If the tail index didn't change, we've got consistent indices to work with. + if self.tail.index.load(Ordering::SeqCst) == tail { + // Erase the lower bits. + tail &= !((1 << SHIFT) - 1); + head &= !((1 << SHIFT) - 1); + + // Fix up indices if they fall onto block ends. + if (tail >> SHIFT) & (LAP - 1) == LAP - 1 { + tail = tail.wrapping_add(1 << SHIFT); + } + if (head >> SHIFT) & (LAP - 1) == LAP - 1 { + head = head.wrapping_add(1 << SHIFT); + } + + // Rotate indices so that head falls into the first block. + let lap = (head >> SHIFT) / LAP; + tail = tail.wrapping_sub((lap * LAP) << SHIFT); + head = head.wrapping_sub((lap * LAP) << SHIFT); + + // Remove the lower bits. + tail >>= SHIFT; + head >>= SHIFT; + + // Return the difference minus the number of blocks between tail and head. + return tail - head - tail / LAP; + } + } + } + + /// Returns the capacity of the channel. + pub(crate) fn capacity(&self) -> Option { + None + } + + /// Disconnects senders and wakes up all blocked receivers. + /// + /// Returns `true` if this call disconnected the channel. + pub(crate) fn disconnect_senders(&self) -> bool { + let tail = self.tail.index.fetch_or(MARK_BIT, Ordering::SeqCst); + + if tail & MARK_BIT == 0 { + self.receivers.disconnect(); + true + } else { + false + } + } + + /// Disconnects receivers. + /// + /// Returns `true` if this call disconnected the channel. + pub(crate) fn disconnect_receivers(&self) -> bool { + let tail = self.tail.index.fetch_or(MARK_BIT, Ordering::SeqCst); + + if tail & MARK_BIT == 0 { + // If receivers are dropped first, discard all messages to free + // memory eagerly. + self.discard_all_messages(); + true + } else { + false + } + } + + /// Discards all messages. + /// + /// This method should only be called when all receivers are dropped. + fn discard_all_messages(&self) { + let backoff = Backoff::new(); + let mut tail = self.tail.index.load(Ordering::Acquire); + loop { + let offset = (tail >> SHIFT) % LAP; + if offset != BLOCK_CAP { + break; + } + + // New updates to tail will be rejected by MARK_BIT and aborted unless it's + // at boundary. We need to wait for the updates take affect otherwise there + // can be memory leaks. + backoff.snooze(); + tail = self.tail.index.load(Ordering::Acquire); + } + + let mut head = self.head.index.load(Ordering::Acquire); + let mut block = self.head.block.load(Ordering::Acquire); + + unsafe { + // Drop all messages between head and tail and deallocate the heap-allocated blocks. + while head >> SHIFT != tail >> SHIFT { + let offset = (head >> SHIFT) % LAP; + + if offset < BLOCK_CAP { + // Drop the message in the slot. + let slot = (*block).slots.get_unchecked(offset); + slot.wait_write(); + let p = &mut *slot.msg.get(); + p.as_mut_ptr().drop_in_place(); + } else { + (*block).wait_next(); + // Deallocate the block and move to the next one. + let next = (*block).next.load(Ordering::Acquire); + drop(Box::from_raw(block)); + block = next; + } + + head = head.wrapping_add(1 << SHIFT); + } + + // Deallocate the last remaining block. + if !block.is_null() { + drop(Box::from_raw(block)); + } + } + head &= !MARK_BIT; + self.head.block.store(ptr::null_mut(), Ordering::Release); + self.head.index.store(head, Ordering::Release); + } + + /// Returns `true` if the channel is disconnected. + pub(crate) fn is_disconnected(&self) -> bool { + self.tail.index.load(Ordering::SeqCst) & MARK_BIT != 0 + } + + /// Returns `true` if the channel is empty. + pub(crate) fn is_empty(&self) -> bool { + let head = self.head.index.load(Ordering::SeqCst); + let tail = self.tail.index.load(Ordering::SeqCst); + head >> SHIFT == tail >> SHIFT + } + + /// Returns `true` if the channel is full. + pub(crate) fn is_full(&self) -> bool { + false + } +} + +impl Drop for Channel { + fn drop(&mut self) { + let mut head = self.head.index.load(Ordering::Relaxed); + let mut tail = self.tail.index.load(Ordering::Relaxed); + let mut block = self.head.block.load(Ordering::Relaxed); + + // Erase the lower bits. + head &= !((1 << SHIFT) - 1); + tail &= !((1 << SHIFT) - 1); + + unsafe { + // Drop all messages between head and tail and deallocate the heap-allocated blocks. + while head != tail { + let offset = (head >> SHIFT) % LAP; + + if offset < BLOCK_CAP { + // Drop the message in the slot. + let slot = (*block).slots.get_unchecked(offset); + let p = &mut *slot.msg.get(); + p.as_mut_ptr().drop_in_place(); + } else { + // Deallocate the block and move to the next one. + let next = (*block).next.load(Ordering::Relaxed); + drop(Box::from_raw(block)); + block = next; + } + + head = head.wrapping_add(1 << SHIFT); + } + + // Deallocate the last remaining block. + if !block.is_null() { + drop(Box::from_raw(block)); + } + } + } +} diff --git a/library/std/src/sync/mpmc/mod.rs b/library/std/src/sync/mpmc/mod.rs new file mode 100644 index 000000000000..cef99c588430 --- /dev/null +++ b/library/std/src/sync/mpmc/mod.rs @@ -0,0 +1,430 @@ +//! Multi-producer multi-consumer channels. + +// This module is not currently exposed publicly, but is used +// as the implementation for the channels in `sync::mpsc`. The +// implementation comes from the crossbeam-channel crate: +// +// Copyright (c) 2019 The Crossbeam Project Developers +// +// Permission is hereby granted, free of charge, to any +// person obtaining a copy of this software and associated +// documentation files (the "Software"), to deal in the +// Software without restriction, including without +// limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +mod array; +mod context; +mod counter; +mod error; +mod list; +mod select; +mod utils; +mod waker; +mod zero; + +use crate::fmt; +use crate::panic::{RefUnwindSafe, UnwindSafe}; +use crate::time::{Duration, Instant}; +use error::*; + +/// Creates a channel of unbounded capacity. +/// +/// This channel has a growable buffer that can hold any number of messages at a time. +pub fn channel() -> (Sender, Receiver) { + let (s, r) = counter::new(list::Channel::new()); + let s = Sender { flavor: SenderFlavor::List(s) }; + let r = Receiver { flavor: ReceiverFlavor::List(r) }; + (s, r) +} + +/// Creates a channel of bounded capacity. +/// +/// This channel has a buffer that can hold at most `cap` messages at a time. +/// +/// A special case is zero-capacity channel, which cannot hold any messages. Instead, send and +/// receive operations must appear at the same time in order to pair up and pass the message over. +pub fn sync_channel(cap: usize) -> (Sender, Receiver) { + if cap == 0 { + let (s, r) = counter::new(zero::Channel::new()); + let s = Sender { flavor: SenderFlavor::Zero(s) }; + let r = Receiver { flavor: ReceiverFlavor::Zero(r) }; + (s, r) + } else { + let (s, r) = counter::new(array::Channel::with_capacity(cap)); + let s = Sender { flavor: SenderFlavor::Array(s) }; + let r = Receiver { flavor: ReceiverFlavor::Array(r) }; + (s, r) + } +} + +/// The sending side of a channel. +pub struct Sender { + flavor: SenderFlavor, +} + +/// Sender flavors. +enum SenderFlavor { + /// Bounded channel based on a preallocated array. + Array(counter::Sender>), + + /// Unbounded channel implemented as a linked list. + List(counter::Sender>), + + /// Zero-capacity channel. + Zero(counter::Sender>), +} + +unsafe impl Send for Sender {} +unsafe impl Sync for Sender {} + +impl UnwindSafe for Sender {} +impl RefUnwindSafe for Sender {} + +impl Sender { + /// Attempts to send a message into the channel without blocking. + /// + /// This method will either send a message into the channel immediately or return an error if + /// the channel is full or disconnected. The returned error contains the original message. + /// + /// If called on a zero-capacity channel, this method will send the message only if there + /// happens to be a receive operation on the other side of the channel at the same time. + pub fn try_send(&self, msg: T) -> Result<(), TrySendError> { + match &self.flavor { + SenderFlavor::Array(chan) => chan.try_send(msg), + SenderFlavor::List(chan) => chan.try_send(msg), + SenderFlavor::Zero(chan) => chan.try_send(msg), + } + } + + /// Blocks the current thread until a message is sent or the channel is disconnected. + /// + /// If the channel is full and not disconnected, this call will block until the send operation + /// can proceed. If the channel becomes disconnected, this call will wake up and return an + /// error. The returned error contains the original message. + /// + /// If called on a zero-capacity channel, this method will wait for a receive operation to + /// appear on the other side of the channel. + pub fn send(&self, msg: T) -> Result<(), SendError> { + match &self.flavor { + SenderFlavor::Array(chan) => chan.send(msg, None), + SenderFlavor::List(chan) => chan.send(msg, None), + SenderFlavor::Zero(chan) => chan.send(msg, None), + } + .map_err(|err| match err { + SendTimeoutError::Disconnected(msg) => SendError(msg), + SendTimeoutError::Timeout(_) => unreachable!(), + }) + } +} + +// The methods below are not used by `sync::mpsc`, but +// are useful and we'll likely want to expose them +// eventually +#[allow(unused)] +impl Sender { + /// Waits for a message to be sent into the channel, but only for a limited time. + /// + /// If the channel is full and not disconnected, this call will block until the send operation + /// can proceed or the operation times out. If the channel becomes disconnected, this call will + /// wake up and return an error. The returned error contains the original message. + /// + /// If called on a zero-capacity channel, this method will wait for a receive operation to + /// appear on the other side of the channel. + pub fn send_timeout(&self, msg: T, timeout: Duration) -> Result<(), SendTimeoutError> { + match Instant::now().checked_add(timeout) { + Some(deadline) => self.send_deadline(msg, deadline), + // So far in the future that it's practically the same as waiting indefinitely. + None => self.send(msg).map_err(SendTimeoutError::from), + } + } + + /// Waits for a message to be sent into the channel, but only until a given deadline. + /// + /// If the channel is full and not disconnected, this call will block until the send operation + /// can proceed or the operation times out. If the channel becomes disconnected, this call will + /// wake up and return an error. The returned error contains the original message. + /// + /// If called on a zero-capacity channel, this method will wait for a receive operation to + /// appear on the other side of the channel. + pub fn send_deadline(&self, msg: T, deadline: Instant) -> Result<(), SendTimeoutError> { + match &self.flavor { + SenderFlavor::Array(chan) => chan.send(msg, Some(deadline)), + SenderFlavor::List(chan) => chan.send(msg, Some(deadline)), + SenderFlavor::Zero(chan) => chan.send(msg, Some(deadline)), + } + } + + /// Returns `true` if the channel is empty. + /// + /// Note: Zero-capacity channels are always empty. + pub fn is_empty(&self) -> bool { + match &self.flavor { + SenderFlavor::Array(chan) => chan.is_empty(), + SenderFlavor::List(chan) => chan.is_empty(), + SenderFlavor::Zero(chan) => chan.is_empty(), + } + } + + /// Returns `true` if the channel is full. + /// + /// Note: Zero-capacity channels are always full. + pub fn is_full(&self) -> bool { + match &self.flavor { + SenderFlavor::Array(chan) => chan.is_full(), + SenderFlavor::List(chan) => chan.is_full(), + SenderFlavor::Zero(chan) => chan.is_full(), + } + } + + /// Returns the number of messages in the channel. + pub fn len(&self) -> usize { + match &self.flavor { + SenderFlavor::Array(chan) => chan.len(), + SenderFlavor::List(chan) => chan.len(), + SenderFlavor::Zero(chan) => chan.len(), + } + } + + /// If the channel is bounded, returns its capacity. + pub fn capacity(&self) -> Option { + match &self.flavor { + SenderFlavor::Array(chan) => chan.capacity(), + SenderFlavor::List(chan) => chan.capacity(), + SenderFlavor::Zero(chan) => chan.capacity(), + } + } + + /// Returns `true` if senders belong to the same channel. + pub fn same_channel(&self, other: &Sender) -> bool { + match (&self.flavor, &other.flavor) { + (SenderFlavor::Array(ref a), SenderFlavor::Array(ref b)) => a == b, + (SenderFlavor::List(ref a), SenderFlavor::List(ref b)) => a == b, + (SenderFlavor::Zero(ref a), SenderFlavor::Zero(ref b)) => a == b, + _ => false, + } + } +} + +impl Drop for Sender { + fn drop(&mut self) { + unsafe { + match &self.flavor { + SenderFlavor::Array(chan) => chan.release(|c| c.disconnect()), + SenderFlavor::List(chan) => chan.release(|c| c.disconnect_senders()), + SenderFlavor::Zero(chan) => chan.release(|c| c.disconnect()), + } + } + } +} + +impl Clone for Sender { + fn clone(&self) -> Self { + let flavor = match &self.flavor { + SenderFlavor::Array(chan) => SenderFlavor::Array(chan.acquire()), + SenderFlavor::List(chan) => SenderFlavor::List(chan.acquire()), + SenderFlavor::Zero(chan) => SenderFlavor::Zero(chan.acquire()), + }; + + Sender { flavor } + } +} + +impl fmt::Debug for Sender { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("Sender { .. }") + } +} + +/// The receiving side of a channel. +pub struct Receiver { + flavor: ReceiverFlavor, +} + +/// Receiver flavors. +enum ReceiverFlavor { + /// Bounded channel based on a preallocated array. + Array(counter::Receiver>), + + /// Unbounded channel implemented as a linked list. + List(counter::Receiver>), + + /// Zero-capacity channel. + Zero(counter::Receiver>), +} + +unsafe impl Send for Receiver {} +unsafe impl Sync for Receiver {} + +impl UnwindSafe for Receiver {} +impl RefUnwindSafe for Receiver {} + +impl Receiver { + /// Attempts to receive a message from the channel without blocking. + /// + /// This method will either receive a message from the channel immediately or return an error + /// if the channel is empty. + /// + /// If called on a zero-capacity channel, this method will receive a message only if there + /// happens to be a send operation on the other side of the channel at the same time. + pub fn try_recv(&self) -> Result { + match &self.flavor { + ReceiverFlavor::Array(chan) => chan.try_recv(), + ReceiverFlavor::List(chan) => chan.try_recv(), + ReceiverFlavor::Zero(chan) => chan.try_recv(), + } + } + + /// Blocks the current thread until a message is received or the channel is empty and + /// disconnected. + /// + /// If the channel is empty and not disconnected, this call will block until the receive + /// operation can proceed. If the channel is empty and becomes disconnected, this call will + /// wake up and return an error. + /// + /// If called on a zero-capacity channel, this method will wait for a send operation to appear + /// on the other side of the channel. + pub fn recv(&self) -> Result { + match &self.flavor { + ReceiverFlavor::Array(chan) => chan.recv(None), + ReceiverFlavor::List(chan) => chan.recv(None), + ReceiverFlavor::Zero(chan) => chan.recv(None), + } + .map_err(|_| RecvError) + } + + /// Waits for a message to be received from the channel, but only for a limited time. + /// + /// If the channel is empty and not disconnected, this call will block until the receive + /// operation can proceed or the operation times out. If the channel is empty and becomes + /// disconnected, this call will wake up and return an error. + /// + /// If called on a zero-capacity channel, this method will wait for a send operation to appear + /// on the other side of the channel. + pub fn recv_timeout(&self, timeout: Duration) -> Result { + match Instant::now().checked_add(timeout) { + Some(deadline) => self.recv_deadline(deadline), + // So far in the future that it's practically the same as waiting indefinitely. + None => self.recv().map_err(RecvTimeoutError::from), + } + } + + /// Waits for a message to be received from the channel, but only for a limited time. + /// + /// If the channel is empty and not disconnected, this call will block until the receive + /// operation can proceed or the operation times out. If the channel is empty and becomes + /// disconnected, this call will wake up and return an error. + /// + /// If called on a zero-capacity channel, this method will wait for a send operation to appear + /// on the other side of the channel. + pub fn recv_deadline(&self, deadline: Instant) -> Result { + match &self.flavor { + ReceiverFlavor::Array(chan) => chan.recv(Some(deadline)), + ReceiverFlavor::List(chan) => chan.recv(Some(deadline)), + ReceiverFlavor::Zero(chan) => chan.recv(Some(deadline)), + } + } +} + +// The methods below are not used by `sync::mpsc`, but +// are useful and we'll likely want to expose them +// eventually +#[allow(unused)] +impl Receiver { + /// Returns `true` if the channel is empty. + /// + /// Note: Zero-capacity channels are always empty. + pub fn is_empty(&self) -> bool { + match &self.flavor { + ReceiverFlavor::Array(chan) => chan.is_empty(), + ReceiverFlavor::List(chan) => chan.is_empty(), + ReceiverFlavor::Zero(chan) => chan.is_empty(), + } + } + + /// Returns `true` if the channel is full. + /// + /// Note: Zero-capacity channels are always full. + pub fn is_full(&self) -> bool { + match &self.flavor { + ReceiverFlavor::Array(chan) => chan.is_full(), + ReceiverFlavor::List(chan) => chan.is_full(), + ReceiverFlavor::Zero(chan) => chan.is_full(), + } + } + + /// Returns the number of messages in the channel. + pub fn len(&self) -> usize { + match &self.flavor { + ReceiverFlavor::Array(chan) => chan.len(), + ReceiverFlavor::List(chan) => chan.len(), + ReceiverFlavor::Zero(chan) => chan.len(), + } + } + + /// If the channel is bounded, returns its capacity. + pub fn capacity(&self) -> Option { + match &self.flavor { + ReceiverFlavor::Array(chan) => chan.capacity(), + ReceiverFlavor::List(chan) => chan.capacity(), + ReceiverFlavor::Zero(chan) => chan.capacity(), + } + } + + /// Returns `true` if receivers belong to the same channel. + pub fn same_channel(&self, other: &Receiver) -> bool { + match (&self.flavor, &other.flavor) { + (ReceiverFlavor::Array(a), ReceiverFlavor::Array(b)) => a == b, + (ReceiverFlavor::List(a), ReceiverFlavor::List(b)) => a == b, + (ReceiverFlavor::Zero(a), ReceiverFlavor::Zero(b)) => a == b, + _ => false, + } + } +} + +impl Drop for Receiver { + fn drop(&mut self) { + unsafe { + match &self.flavor { + ReceiverFlavor::Array(chan) => chan.release(|c| c.disconnect()), + ReceiverFlavor::List(chan) => chan.release(|c| c.disconnect_receivers()), + ReceiverFlavor::Zero(chan) => chan.release(|c| c.disconnect()), + } + } + } +} + +impl Clone for Receiver { + fn clone(&self) -> Self { + let flavor = match &self.flavor { + ReceiverFlavor::Array(chan) => ReceiverFlavor::Array(chan.acquire()), + ReceiverFlavor::List(chan) => ReceiverFlavor::List(chan.acquire()), + ReceiverFlavor::Zero(chan) => ReceiverFlavor::Zero(chan.acquire()), + }; + + Receiver { flavor } + } +} + +impl fmt::Debug for Receiver { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("Receiver { .. }") + } +} diff --git a/library/std/src/sync/mpmc/select.rs b/library/std/src/sync/mpmc/select.rs new file mode 100644 index 000000000000..56a83fee2e11 --- /dev/null +++ b/library/std/src/sync/mpmc/select.rs @@ -0,0 +1,71 @@ +/// Temporary data that gets initialized during a blocking operation, and is consumed by +/// `read` or `write`. +/// +/// Each field contains data associated with a specific channel flavor. +#[derive(Debug, Default)] +pub struct Token { + pub(crate) array: super::array::ArrayToken, + pub(crate) list: super::list::ListToken, + #[allow(dead_code)] + pub(crate) zero: super::zero::ZeroToken, +} + +/// Identifier associated with an operation by a specific thread on a specific channel. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Operation(usize); + +impl Operation { + /// Creates an operation identifier from a mutable reference. + /// + /// This function essentially just turns the address of the reference into a number. The + /// reference should point to a variable that is specific to the thread and the operation, + /// and is alive for the entire duration of a blocking operation. + #[inline] + pub fn hook(r: &mut T) -> Operation { + let val = r as *mut T as usize; + // Make sure that the pointer address doesn't equal the numerical representation of + // `Selected::{Waiting, Aborted, Disconnected}`. + assert!(val > 2); + Operation(val) + } +} + +/// Current state of a blocking operation. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Selected { + /// Still waiting for an operation. + Waiting, + + /// The attempt to block the current thread has been aborted. + Aborted, + + /// An operation became ready because a channel is disconnected. + Disconnected, + + /// An operation became ready because a message can be sent or received. + Operation(Operation), +} + +impl From for Selected { + #[inline] + fn from(val: usize) -> Selected { + match val { + 0 => Selected::Waiting, + 1 => Selected::Aborted, + 2 => Selected::Disconnected, + oper => Selected::Operation(Operation(oper)), + } + } +} + +impl Into for Selected { + #[inline] + fn into(self) -> usize { + match self { + Selected::Waiting => 0, + Selected::Aborted => 1, + Selected::Disconnected => 2, + Selected::Operation(Operation(val)) => val, + } + } +} diff --git a/library/std/src/sync/mpmc/utils.rs b/library/std/src/sync/mpmc/utils.rs new file mode 100644 index 000000000000..d0904b4b94cb --- /dev/null +++ b/library/std/src/sync/mpmc/utils.rs @@ -0,0 +1,144 @@ +use crate::cell::Cell; +use crate::ops::{Deref, DerefMut}; + +/// Pads and aligns a value to the length of a cache line. +#[derive(Clone, Copy, Default, Hash, PartialEq, Eq)] +// Starting from Intel's Sandy Bridge, spatial prefetcher is now pulling pairs of 64-byte cache +// lines at a time, so we have to align to 128 bytes rather than 64. +// +// Sources: +// - https://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-optimization-manual.pdf +// - https://github.com/facebook/folly/blob/1b5288e6eea6df074758f877c849b6e73bbb9fbb/folly/lang/Align.h#L107 +// +// ARM's big.LITTLE architecture has asymmetric cores and "big" cores have 128-byte cache line size. +// +// Sources: +// - https://www.mono-project.com/news/2016/09/12/arm64-icache/ +// +// powerpc64 has 128-byte cache line size. +// +// Sources: +// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_ppc64x.go#L9 +#[cfg_attr( + any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "powerpc64",), + repr(align(128)) +)] +// arm, mips, mips64, and riscv64 have 32-byte cache line size. +// +// Sources: +// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_arm.go#L7 +// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_mips.go#L7 +// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_mipsle.go#L7 +// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_mips64x.go#L9 +// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_riscv64.go#L7 +#[cfg_attr( + any( + target_arch = "arm", + target_arch = "mips", + target_arch = "mips64", + target_arch = "riscv64", + ), + repr(align(32)) +)] +// s390x has 256-byte cache line size. +// +// Sources: +// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_s390x.go#L7 +#[cfg_attr(target_arch = "s390x", repr(align(256)))] +// x86 and wasm have 64-byte cache line size. +// +// Sources: +// - https://github.com/golang/go/blob/dda2991c2ea0c5914714469c4defc2562a907230/src/internal/cpu/cpu_x86.go#L9 +// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_wasm.go#L7 +// +// All others are assumed to have 64-byte cache line size. +#[cfg_attr( + not(any( + target_arch = "x86_64", + target_arch = "aarch64", + target_arch = "powerpc64", + target_arch = "arm", + target_arch = "mips", + target_arch = "mips64", + target_arch = "riscv64", + target_arch = "s390x", + )), + repr(align(64)) +)] +pub struct CachePadded { + value: T, +} + +impl CachePadded { + /// Pads and aligns a value to the length of a cache line. + pub fn new(value: T) -> CachePadded { + CachePadded:: { value } + } +} + +impl Deref for CachePadded { + type Target = T; + + fn deref(&self) -> &T { + &self.value + } +} + +impl DerefMut for CachePadded { + fn deref_mut(&mut self) -> &mut T { + &mut self.value + } +} + +const SPIN_LIMIT: u32 = 6; +const YIELD_LIMIT: u32 = 10; + +/// Performs exponential backoff in spin loops. +pub struct Backoff { + step: Cell, +} + +impl Backoff { + /// Creates a new `Backoff`. + pub fn new() -> Self { + Backoff { step: Cell::new(0) } + } + + /// Backs off in a lock-free loop. + /// + /// This method should be used when we need to retry an operation because another thread made + /// progress. + #[inline] + pub fn spin(&self) { + let step = self.step.get().min(SPIN_LIMIT); + for _ in 0..step.pow(2) { + crate::hint::spin_loop(); + } + + if self.step.get() <= SPIN_LIMIT { + self.step.set(self.step.get() + 1); + } + } + + /// Backs off in a blocking loop. + #[inline] + pub fn snooze(&self) { + if self.step.get() <= SPIN_LIMIT { + for _ in 0..self.step.get().pow(2) { + crate::hint::spin_loop() + } + } else { + crate::thread::yield_now(); + } + + if self.step.get() <= YIELD_LIMIT { + self.step.set(self.step.get() + 1); + } + } + + /// Returns `true` if exponential backoff has completed and blocking the thread is advised. + #[inline] + pub fn is_completed(&self) -> bool { + self.step.get() > YIELD_LIMIT + } +} diff --git a/library/std/src/sync/mpmc/waker.rs b/library/std/src/sync/mpmc/waker.rs new file mode 100644 index 000000000000..4912ca4f8150 --- /dev/null +++ b/library/std/src/sync/mpmc/waker.rs @@ -0,0 +1,204 @@ +//! Waking mechanism for threads blocked on channel operations. + +use super::context::Context; +use super::select::{Operation, Selected}; + +use crate::ptr; +use crate::sync::atomic::{AtomicBool, Ordering}; +use crate::sync::Mutex; + +/// Represents a thread blocked on a specific channel operation. +pub(crate) struct Entry { + /// The operation. + pub(crate) oper: Operation, + + /// Optional packet. + pub(crate) packet: *mut (), + + /// Context associated with the thread owning this operation. + pub(crate) cx: Context, +} + +/// A queue of threads blocked on channel operations. +/// +/// This data structure is used by threads to register blocking operations and get woken up once +/// an operation becomes ready. +pub(crate) struct Waker { + /// A list of select operations. + selectors: Vec, + + /// A list of operations waiting to be ready. + observers: Vec, +} + +impl Waker { + /// Creates a new `Waker`. + #[inline] + pub(crate) fn new() -> Self { + Waker { selectors: Vec::new(), observers: Vec::new() } + } + + /// Registers a select operation. + #[inline] + pub(crate) fn register(&mut self, oper: Operation, cx: &Context) { + self.register_with_packet(oper, ptr::null_mut(), cx); + } + + /// Registers a select operation and a packet. + #[inline] + pub(crate) fn register_with_packet(&mut self, oper: Operation, packet: *mut (), cx: &Context) { + self.selectors.push(Entry { oper, packet, cx: cx.clone() }); + } + + /// Unregisters a select operation. + #[inline] + pub(crate) fn unregister(&mut self, oper: Operation) -> Option { + if let Some((i, _)) = + self.selectors.iter().enumerate().find(|&(_, entry)| entry.oper == oper) + { + let entry = self.selectors.remove(i); + Some(entry) + } else { + None + } + } + + /// Attempts to find another thread's entry, select the operation, and wake it up. + #[inline] + pub(crate) fn try_select(&mut self) -> Option { + self.selectors + .iter() + .position(|selector| { + // Does the entry belong to a different thread? + selector.cx.thread_id() != current_thread_id() + && selector // Try selecting this operation. + .cx + .try_select(Selected::Operation(selector.oper)) + .is_ok() + && { + // Provide the packet. + selector.cx.store_packet(selector.packet); + // Wake the thread up. + selector.cx.unpark(); + true + } + }) + // Remove the entry from the queue to keep it clean and improve + // performance. + .map(|pos| self.selectors.remove(pos)) + } + + /// Notifies all operations waiting to be ready. + #[inline] + pub(crate) fn notify(&mut self) { + for entry in self.observers.drain(..) { + if entry.cx.try_select(Selected::Operation(entry.oper)).is_ok() { + entry.cx.unpark(); + } + } + } + + /// Notifies all registered operations that the channel is disconnected. + #[inline] + pub(crate) fn disconnect(&mut self) { + for entry in self.selectors.iter() { + if entry.cx.try_select(Selected::Disconnected).is_ok() { + // Wake the thread up. + // + // Here we don't remove the entry from the queue. Registered threads must + // unregister from the waker by themselves. They might also want to recover the + // packet value and destroy it, if necessary. + entry.cx.unpark(); + } + } + + self.notify(); + } +} + +impl Drop for Waker { + #[inline] + fn drop(&mut self) { + debug_assert_eq!(self.selectors.len(), 0); + debug_assert_eq!(self.observers.len(), 0); + } +} + +/// A waker that can be shared among threads without locking. +/// +/// This is a simple wrapper around `Waker` that internally uses a mutex for synchronization. +pub(crate) struct SyncWaker { + /// The inner `Waker`. + inner: Mutex, + + /// `true` if the waker is empty. + is_empty: AtomicBool, +} + +impl SyncWaker { + /// Creates a new `SyncWaker`. + #[inline] + pub(crate) fn new() -> Self { + SyncWaker { inner: Mutex::new(Waker::new()), is_empty: AtomicBool::new(true) } + } + + /// Registers the current thread with an operation. + #[inline] + pub(crate) fn register(&self, oper: Operation, cx: &Context) { + let mut inner = self.inner.lock().unwrap(); + inner.register(oper, cx); + self.is_empty + .store(inner.selectors.is_empty() && inner.observers.is_empty(), Ordering::SeqCst); + } + + /// Unregisters an operation previously registered by the current thread. + #[inline] + pub(crate) fn unregister(&self, oper: Operation) -> Option { + let mut inner = self.inner.lock().unwrap(); + let entry = inner.unregister(oper); + self.is_empty + .store(inner.selectors.is_empty() && inner.observers.is_empty(), Ordering::SeqCst); + entry + } + + /// Attempts to find one thread (not the current one), select its operation, and wake it up. + #[inline] + pub(crate) fn notify(&self) { + if !self.is_empty.load(Ordering::SeqCst) { + let mut inner = self.inner.lock().unwrap(); + if !self.is_empty.load(Ordering::SeqCst) { + inner.try_select(); + inner.notify(); + self.is_empty.store( + inner.selectors.is_empty() && inner.observers.is_empty(), + Ordering::SeqCst, + ); + } + } + } + + /// Notifies all threads that the channel is disconnected. + #[inline] + pub(crate) fn disconnect(&self) { + let mut inner = self.inner.lock().unwrap(); + inner.disconnect(); + self.is_empty + .store(inner.selectors.is_empty() && inner.observers.is_empty(), Ordering::SeqCst); + } +} + +impl Drop for SyncWaker { + #[inline] + fn drop(&mut self) { + debug_assert!(self.is_empty.load(Ordering::SeqCst)); + } +} + +/// Returns a unique id for the current thread. +#[inline] +pub fn current_thread_id() -> usize { + // `u8` is not drop so this variable will be available during thread destruction, + // whereas `thread::current()` would not be + thread_local! { static DUMMY: u8 = 0 } + DUMMY.with(|x| (x as *const u8).addr()) +} diff --git a/library/std/src/sync/mpmc/zero.rs b/library/std/src/sync/mpmc/zero.rs new file mode 100644 index 000000000000..fccd6c29a7e4 --- /dev/null +++ b/library/std/src/sync/mpmc/zero.rs @@ -0,0 +1,318 @@ +//! Zero-capacity channel. +//! +//! This kind of channel is also known as *rendezvous* channel. + +use super::context::Context; +use super::error::*; +use super::select::{Operation, Selected, Token}; +use super::utils::Backoff; +use super::waker::Waker; + +use crate::cell::UnsafeCell; +use crate::marker::PhantomData; +use crate::sync::atomic::{AtomicBool, Ordering}; +use crate::sync::Mutex; +use crate::time::Instant; +use crate::{fmt, ptr}; + +/// A pointer to a packet. +pub(crate) struct ZeroToken(*mut ()); + +impl Default for ZeroToken { + fn default() -> Self { + Self(ptr::null_mut()) + } +} + +impl fmt::Debug for ZeroToken { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&(self.0 as usize), f) + } +} + +/// A slot for passing one message from a sender to a receiver. +struct Packet { + /// Equals `true` if the packet is allocated on the stack. + on_stack: bool, + + /// Equals `true` once the packet is ready for reading or writing. + ready: AtomicBool, + + /// The message. + msg: UnsafeCell>, +} + +impl Packet { + /// Creates an empty packet on the stack. + fn empty_on_stack() -> Packet { + Packet { on_stack: true, ready: AtomicBool::new(false), msg: UnsafeCell::new(None) } + } + + /// Creates a packet on the stack, containing a message. + fn message_on_stack(msg: T) -> Packet { + Packet { on_stack: true, ready: AtomicBool::new(false), msg: UnsafeCell::new(Some(msg)) } + } + + /// Waits until the packet becomes ready for reading or writing. + fn wait_ready(&self) { + let backoff = Backoff::new(); + while !self.ready.load(Ordering::Acquire) { + backoff.snooze(); + } + } +} + +/// Inner representation of a zero-capacity channel. +struct Inner { + /// Senders waiting to pair up with a receive operation. + senders: Waker, + + /// Receivers waiting to pair up with a send operation. + receivers: Waker, + + /// Equals `true` when the channel is disconnected. + is_disconnected: bool, +} + +/// Zero-capacity channel. +pub(crate) struct Channel { + /// Inner representation of the channel. + inner: Mutex, + + /// Indicates that dropping a `Channel` may drop values of type `T`. + _marker: PhantomData, +} + +impl Channel { + /// Constructs a new zero-capacity channel. + pub(crate) fn new() -> Self { + Channel { + inner: Mutex::new(Inner { + senders: Waker::new(), + receivers: Waker::new(), + is_disconnected: false, + }), + _marker: PhantomData, + } + } + + /// Writes a message into the packet. + pub(crate) unsafe fn write(&self, token: &mut Token, msg: T) -> Result<(), T> { + // If there is no packet, the channel is disconnected. + if token.zero.0.is_null() { + return Err(msg); + } + + let packet = &*(token.zero.0 as *const Packet); + packet.msg.get().write(Some(msg)); + packet.ready.store(true, Ordering::Release); + Ok(()) + } + + /// Reads a message from the packet. + pub(crate) unsafe fn read(&self, token: &mut Token) -> Result { + // If there is no packet, the channel is disconnected. + if token.zero.0.is_null() { + return Err(()); + } + + let packet = &*(token.zero.0 as *const Packet); + + if packet.on_stack { + // The message has been in the packet from the beginning, so there is no need to wait + // for it. However, after reading the message, we need to set `ready` to `true` in + // order to signal that the packet can be destroyed. + let msg = packet.msg.get().replace(None).unwrap(); + packet.ready.store(true, Ordering::Release); + Ok(msg) + } else { + // Wait until the message becomes available, then read it and destroy the + // heap-allocated packet. + packet.wait_ready(); + let msg = packet.msg.get().replace(None).unwrap(); + drop(Box::from_raw(token.zero.0 as *mut Packet)); + Ok(msg) + } + } + + /// Attempts to send a message into the channel. + pub(crate) fn try_send(&self, msg: T) -> Result<(), TrySendError> { + let token = &mut Token::default(); + let mut inner = self.inner.lock().unwrap(); + + // If there's a waiting receiver, pair up with it. + if let Some(operation) = inner.receivers.try_select() { + token.zero.0 = operation.packet; + drop(inner); + unsafe { + self.write(token, msg).ok().unwrap(); + } + Ok(()) + } else if inner.is_disconnected { + Err(TrySendError::Disconnected(msg)) + } else { + Err(TrySendError::Full(msg)) + } + } + + /// Sends a message into the channel. + pub(crate) fn send( + &self, + msg: T, + deadline: Option, + ) -> Result<(), SendTimeoutError> { + let token = &mut Token::default(); + let mut inner = self.inner.lock().unwrap(); + + // If there's a waiting receiver, pair up with it. + if let Some(operation) = inner.receivers.try_select() { + token.zero.0 = operation.packet; + drop(inner); + unsafe { + self.write(token, msg).ok().unwrap(); + } + return Ok(()); + } + + if inner.is_disconnected { + return Err(SendTimeoutError::Disconnected(msg)); + } + + Context::with(|cx| { + // Prepare for blocking until a receiver wakes us up. + let oper = Operation::hook(token); + let mut packet = Packet::::message_on_stack(msg); + inner.senders.register_with_packet(oper, &mut packet as *mut Packet as *mut (), cx); + inner.receivers.notify(); + drop(inner); + + // Block the current thread. + let sel = cx.wait_until(deadline); + + match sel { + Selected::Waiting => unreachable!(), + Selected::Aborted => { + self.inner.lock().unwrap().senders.unregister(oper).unwrap(); + let msg = unsafe { packet.msg.get().replace(None).unwrap() }; + Err(SendTimeoutError::Timeout(msg)) + } + Selected::Disconnected => { + self.inner.lock().unwrap().senders.unregister(oper).unwrap(); + let msg = unsafe { packet.msg.get().replace(None).unwrap() }; + Err(SendTimeoutError::Disconnected(msg)) + } + Selected::Operation(_) => { + // Wait until the message is read, then drop the packet. + packet.wait_ready(); + Ok(()) + } + } + }) + } + + /// Attempts to receive a message without blocking. + pub(crate) fn try_recv(&self) -> Result { + let token = &mut Token::default(); + let mut inner = self.inner.lock().unwrap(); + + // If there's a waiting sender, pair up with it. + if let Some(operation) = inner.senders.try_select() { + token.zero.0 = operation.packet; + drop(inner); + unsafe { self.read(token).map_err(|_| TryRecvError::Disconnected) } + } else if inner.is_disconnected { + Err(TryRecvError::Disconnected) + } else { + Err(TryRecvError::Empty) + } + } + + /// Receives a message from the channel. + pub(crate) fn recv(&self, deadline: Option) -> Result { + let token = &mut Token::default(); + let mut inner = self.inner.lock().unwrap(); + + // If there's a waiting sender, pair up with it. + if let Some(operation) = inner.senders.try_select() { + token.zero.0 = operation.packet; + drop(inner); + unsafe { + return self.read(token).map_err(|_| RecvTimeoutError::Disconnected); + } + } + + if inner.is_disconnected { + return Err(RecvTimeoutError::Disconnected); + } + + Context::with(|cx| { + // Prepare for blocking until a sender wakes us up. + let oper = Operation::hook(token); + let mut packet = Packet::::empty_on_stack(); + inner.receivers.register_with_packet( + oper, + &mut packet as *mut Packet as *mut (), + cx, + ); + inner.senders.notify(); + drop(inner); + + // Block the current thread. + let sel = cx.wait_until(deadline); + + match sel { + Selected::Waiting => unreachable!(), + Selected::Aborted => { + self.inner.lock().unwrap().receivers.unregister(oper).unwrap(); + Err(RecvTimeoutError::Timeout) + } + Selected::Disconnected => { + self.inner.lock().unwrap().receivers.unregister(oper).unwrap(); + Err(RecvTimeoutError::Disconnected) + } + Selected::Operation(_) => { + // Wait until the message is provided, then read it. + packet.wait_ready(); + unsafe { Ok(packet.msg.get().replace(None).unwrap()) } + } + } + }) + } + + /// Disconnects the channel and wakes up all blocked senders and receivers. + /// + /// Returns `true` if this call disconnected the channel. + pub(crate) fn disconnect(&self) -> bool { + let mut inner = self.inner.lock().unwrap(); + + if !inner.is_disconnected { + inner.is_disconnected = true; + inner.senders.disconnect(); + inner.receivers.disconnect(); + true + } else { + false + } + } + + /// Returns the current number of messages inside the channel. + pub(crate) fn len(&self) -> usize { + 0 + } + + /// Returns the capacity of the channel. + #[allow(clippy::unnecessary_wraps)] // This is intentional. + pub(crate) fn capacity(&self) -> Option { + Some(0) + } + + /// Returns `true` if the channel is empty. + pub(crate) fn is_empty(&self) -> bool { + true + } + + /// Returns `true` if the channel is full. + pub(crate) fn is_full(&self) -> bool { + true + } +} diff --git a/library/std/src/sync/mpsc/blocking.rs b/library/std/src/sync/mpsc/blocking.rs deleted file mode 100644 index 021df7b096cb..000000000000 --- a/library/std/src/sync/mpsc/blocking.rs +++ /dev/null @@ -1,82 +0,0 @@ -//! Generic support for building blocking abstractions. - -use crate::sync::atomic::{AtomicBool, Ordering}; -use crate::sync::Arc; -use crate::thread::{self, Thread}; -use crate::time::Instant; - -struct Inner { - thread: Thread, - woken: AtomicBool, -} - -unsafe impl Send for Inner {} -unsafe impl Sync for Inner {} - -#[derive(Clone)] -pub struct SignalToken { - inner: Arc, -} - -pub struct WaitToken { - inner: Arc, -} - -impl !Send for WaitToken {} - -impl !Sync for WaitToken {} - -pub fn tokens() -> (WaitToken, SignalToken) { - let inner = Arc::new(Inner { thread: thread::current(), woken: AtomicBool::new(false) }); - let wait_token = WaitToken { inner: inner.clone() }; - let signal_token = SignalToken { inner }; - (wait_token, signal_token) -} - -impl SignalToken { - pub fn signal(&self) -> bool { - let wake = self - .inner - .woken - .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) - .is_ok(); - if wake { - self.inner.thread.unpark(); - } - wake - } - - /// Converts to an unsafe raw pointer. Useful for storing in a pipe's state - /// flag. - #[inline] - pub unsafe fn to_raw(self) -> *mut u8 { - Arc::into_raw(self.inner) as *mut u8 - } - - /// Converts from an unsafe raw pointer. Useful for retrieving a pipe's state - /// flag. - #[inline] - pub unsafe fn from_raw(signal_ptr: *mut u8) -> SignalToken { - SignalToken { inner: Arc::from_raw(signal_ptr as *mut Inner) } - } -} - -impl WaitToken { - pub fn wait(self) { - while !self.inner.woken.load(Ordering::SeqCst) { - thread::park() - } - } - - /// Returns `true` if we wake up normally. - pub fn wait_max_until(self, end: Instant) -> bool { - while !self.inner.woken.load(Ordering::SeqCst) { - let now = Instant::now(); - if now >= end { - return false; - } - thread::park_timeout(end - now) - } - true - } -} diff --git a/library/std/src/sync/mpsc/cache_aligned.rs b/library/std/src/sync/mpsc/cache_aligned.rs deleted file mode 100644 index 9197f0d6e6c8..000000000000 --- a/library/std/src/sync/mpsc/cache_aligned.rs +++ /dev/null @@ -1,25 +0,0 @@ -use crate::ops::{Deref, DerefMut}; - -#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[cfg_attr(target_arch = "aarch64", repr(align(128)))] -#[cfg_attr(not(target_arch = "aarch64"), repr(align(64)))] -pub(super) struct CacheAligned(pub T); - -impl Deref for CacheAligned { - type Target = T; - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for CacheAligned { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl CacheAligned { - pub(super) fn new(t: T) -> Self { - CacheAligned(t) - } -} diff --git a/library/std/src/sync/mpsc/mod.rs b/library/std/src/sync/mpsc/mod.rs index e85a87239651..27fba761ada1 100644 --- a/library/std/src/sync/mpsc/mod.rs +++ b/library/std/src/sync/mpsc/mod.rs @@ -143,175 +143,16 @@ mod tests; #[cfg(all(test, not(target_os = "emscripten")))] mod sync_tests; -// A description of how Rust's channel implementation works -// -// Channels are supposed to be the basic building block for all other -// concurrent primitives that are used in Rust. As a result, the channel type -// needs to be highly optimized, flexible, and broad enough for use everywhere. -// -// The choice of implementation of all channels is to be built on lock-free data -// structures. The channels themselves are then consequently also lock-free data -// structures. As always with lock-free code, this is a very "here be dragons" -// territory, especially because I'm unaware of any academic papers that have -// gone into great length about channels of these flavors. -// -// ## Flavors of channels -// -// From the perspective of a consumer of this library, there is only one flavor -// of channel. This channel can be used as a stream and cloned to allow multiple -// senders. Under the hood, however, there are actually three flavors of -// channels in play. -// -// * Flavor::Oneshots - these channels are highly optimized for the one-send use -// case. They contain as few atomics as possible and -// involve one and exactly one allocation. -// * Streams - these channels are optimized for the non-shared use case. They -// use a different concurrent queue that is more tailored for this -// use case. The initial allocation of this flavor of channel is not -// optimized. -// * Shared - this is the most general form of channel that this module offers, -// a channel with multiple senders. This type is as optimized as it -// can be, but the previous two types mentioned are much faster for -// their use-cases. -// -// ## Concurrent queues -// -// The basic idea of Rust's Sender/Receiver types is that send() never blocks, -// but recv() obviously blocks. This means that under the hood there must be -// some shared and concurrent queue holding all of the actual data. -// -// With two flavors of channels, two flavors of queues are also used. We have -// chosen to use queues from a well-known author that are abbreviated as SPSC -// and MPSC (single producer, single consumer and multiple producer, single -// consumer). SPSC queues are used for streams while MPSC queues are used for -// shared channels. -// -// ### SPSC optimizations -// -// The SPSC queue found online is essentially a linked list of nodes where one -// half of the nodes are the "queue of data" and the other half of nodes are a -// cache of unused nodes. The unused nodes are used such that an allocation is -// not required on every push() and a free doesn't need to happen on every -// pop(). -// -// As found online, however, the cache of nodes is of an infinite size. This -// means that if a channel at one point in its life had 50k items in the queue, -// then the queue will always have the capacity for 50k items. I believed that -// this was an unnecessary limitation of the implementation, so I have altered -// the queue to optionally have a bound on the cache size. -// -// By default, streams will have an unbounded SPSC queue with a small-ish cache -// size. The hope is that the cache is still large enough to have very fast -// send() operations while not too large such that millions of channels can -// coexist at once. -// -// ### MPSC optimizations -// -// Right now the MPSC queue has not been optimized. Like the SPSC queue, it uses -// a linked list under the hood to earn its unboundedness, but I have not put -// forth much effort into having a cache of nodes similar to the SPSC queue. -// -// For now, I believe that this is "ok" because shared channels are not the most -// common type, but soon we may wish to revisit this queue choice and determine -// another candidate for backend storage of shared channels. -// -// ## Overview of the Implementation -// -// Now that there's a little background on the concurrent queues used, it's -// worth going into much more detail about the channels themselves. The basic -// pseudocode for a send/recv are: -// -// -// send(t) recv() -// queue.push(t) return if queue.pop() -// if increment() == -1 deschedule { -// wakeup() if decrement() > 0 -// cancel_deschedule() -// } -// queue.pop() -// -// As mentioned before, there are no locks in this implementation, only atomic -// instructions are used. -// -// ### The internal atomic counter -// -// Every channel has a shared counter with each half to keep track of the size -// of the queue. This counter is used to abort descheduling by the receiver and -// to know when to wake up on the sending side. -// -// As seen in the pseudocode, senders will increment this count and receivers -// will decrement the count. The theory behind this is that if a sender sees a -// -1 count, it will wake up the receiver, and if the receiver sees a 1+ count, -// then it doesn't need to block. -// -// The recv() method has a beginning call to pop(), and if successful, it needs -// to decrement the count. It is a crucial implementation detail that this -// decrement does *not* happen to the shared counter. If this were the case, -// then it would be possible for the counter to be very negative when there were -// no receivers waiting, in which case the senders would have to determine when -// it was actually appropriate to wake up a receiver. -// -// Instead, the "steal count" is kept track of separately (not atomically -// because it's only used by receivers), and then the decrement() call when -// descheduling will lump in all of the recent steals into one large decrement. -// -// The implication of this is that if a sender sees a -1 count, then there's -// guaranteed to be a waiter waiting! -// -// ## Native Implementation -// -// A major goal of these channels is to work seamlessly on and off the runtime. -// All of the previous race conditions have been worded in terms of -// scheduler-isms (which is obviously not available without the runtime). -// -// For now, native usage of channels (off the runtime) will fall back onto -// mutexes/cond vars for descheduling/atomic decisions. The no-contention path -// is still entirely lock-free, the "deschedule" blocks above are surrounded by -// a mutex and the "wakeup" blocks involve grabbing a mutex and signaling on a -// condition variable. -// -// ## Select -// -// Being able to support selection over channels has greatly influenced this -// design, and not only does selection need to work inside the runtime, but also -// outside the runtime. -// -// The implementation is fairly straightforward. The goal of select() is not to -// return some data, but only to return which channel can receive data without -// blocking. The implementation is essentially the entire blocking procedure -// followed by an increment as soon as its woken up. The cancellation procedure -// involves an increment and swapping out of to_wake to acquire ownership of the -// thread to unblock. -// -// Sadly this current implementation requires multiple allocations, so I have -// seen the throughput of select() be much worse than it should be. I do not -// believe that there is anything fundamental that needs to change about these -// channels, however, in order to support a more efficient select(). -// -// FIXME: Select is now removed, so these factors are ready to be cleaned up! -// -// # Conclusion -// -// And now that you've seen all the races that I found and attempted to fix, -// here's the code for you to find some more! +// MPSC channels are built as a wrapper around MPMC channels, which +// were ported from the `crossbeam-channel` crate. MPMC channels are +// not exposed publicly, but if you are curious about the implementation, +// that's where everything is. -use crate::cell::UnsafeCell; use crate::error; use crate::fmt; -use crate::mem; -use crate::sync::Arc; +use crate::sync::mpmc; use crate::time::{Duration, Instant}; -mod blocking; -mod mpsc_queue; -mod oneshot; -mod shared; -mod spsc_queue; -mod stream; -mod sync; - -mod cache_aligned; - /// The receiving half of Rust's [`channel`] (or [`sync_channel`]) type. /// This half can only be owned by one thread. /// @@ -341,7 +182,7 @@ mod cache_aligned; #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "Receiver")] pub struct Receiver { - inner: UnsafeCell>, + inner: mpmc::Receiver, } // The receiver port can be sent from place to place, so long as it @@ -498,7 +339,7 @@ pub struct IntoIter { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub struct Sender { - inner: UnsafeCell>, + inner: mpmc::Sender, } // The send port can be sent from place to place, so long as it @@ -557,7 +398,7 @@ impl !Sync for Sender {} /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub struct SyncSender { - inner: Arc>, + inner: mpmc::Sender, } #[stable(feature = "rust1", since = "1.0.0")] @@ -643,34 +484,6 @@ pub enum TrySendError { Disconnected(#[stable(feature = "rust1", since = "1.0.0")] T), } -enum Flavor { - Oneshot(Arc>), - Stream(Arc>), - Shared(Arc>), - Sync(Arc>), -} - -#[doc(hidden)] -trait UnsafeFlavor { - fn inner_unsafe(&self) -> &UnsafeCell>; - unsafe fn inner_mut(&self) -> &mut Flavor { - &mut *self.inner_unsafe().get() - } - unsafe fn inner(&self) -> &Flavor { - &*self.inner_unsafe().get() - } -} -impl UnsafeFlavor for Sender { - fn inner_unsafe(&self) -> &UnsafeCell> { - &self.inner - } -} -impl UnsafeFlavor for Receiver { - fn inner_unsafe(&self) -> &UnsafeCell> { - &self.inner - } -} - /// Creates a new asynchronous channel, returning the sender/receiver halves. /// All data sent on the [`Sender`] will become available on the [`Receiver`] in /// the same order as it was sent, and no [`send`] will block the calling thread @@ -711,8 +524,8 @@ impl UnsafeFlavor for Receiver { #[must_use] #[stable(feature = "rust1", since = "1.0.0")] pub fn channel() -> (Sender, Receiver) { - let a = Arc::new(oneshot::Packet::new()); - (Sender::new(Flavor::Oneshot(a.clone())), Receiver::new(Flavor::Oneshot(a))) + let (tx, rx) = mpmc::channel(); + (Sender { inner: tx }, Receiver { inner: rx }) } /// Creates a new synchronous, bounded channel. @@ -760,8 +573,8 @@ pub fn channel() -> (Sender, Receiver) { #[must_use] #[stable(feature = "rust1", since = "1.0.0")] pub fn sync_channel(bound: usize) -> (SyncSender, Receiver) { - let a = Arc::new(sync::Packet::new(bound)); - (SyncSender::new(a.clone()), Receiver::new(Flavor::Sync(a))) + let (tx, rx) = mpmc::sync_channel(bound); + (SyncSender { inner: tx }, Receiver { inner: rx }) } //////////////////////////////////////////////////////////////////////////////// @@ -769,10 +582,6 @@ pub fn sync_channel(bound: usize) -> (SyncSender, Receiver) { //////////////////////////////////////////////////////////////////////////////// impl Sender { - fn new(inner: Flavor) -> Sender { - Sender { inner: UnsafeCell::new(inner) } - } - /// Attempts to send a value on this channel, returning it back if it could /// not be sent. /// @@ -802,40 +611,7 @@ impl Sender { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn send(&self, t: T) -> Result<(), SendError> { - let (new_inner, ret) = match *unsafe { self.inner() } { - Flavor::Oneshot(ref p) => { - if !p.sent() { - return p.send(t).map_err(SendError); - } else { - let a = Arc::new(stream::Packet::new()); - let rx = Receiver::new(Flavor::Stream(a.clone())); - match p.upgrade(rx) { - oneshot::UpSuccess => { - let ret = a.send(t); - (a, ret) - } - oneshot::UpDisconnected => (a, Err(t)), - oneshot::UpWoke(token) => { - // This send cannot panic because the thread is - // asleep (we're looking at it), so the receiver - // can't go away. - a.send(t).ok().unwrap(); - token.signal(); - (a, Ok(())) - } - } - } - } - Flavor::Stream(ref p) => return p.send(t).map_err(SendError), - Flavor::Shared(ref p) => return p.send(t).map_err(SendError), - Flavor::Sync(..) => unreachable!(), - }; - - unsafe { - let tmp = Sender::new(Flavor::Stream(new_inner)); - mem::swap(self.inner_mut(), tmp.inner_mut()); - } - ret.map_err(SendError) + self.inner.send(t) } } @@ -847,57 +623,14 @@ impl Clone for Sender { /// (including the original) need to be dropped in order for /// [`Receiver::recv`] to stop blocking. fn clone(&self) -> Sender { - let packet = match *unsafe { self.inner() } { - Flavor::Oneshot(ref p) => { - let a = Arc::new(shared::Packet::new()); - { - let guard = a.postinit_lock(); - let rx = Receiver::new(Flavor::Shared(a.clone())); - let sleeper = match p.upgrade(rx) { - oneshot::UpSuccess | oneshot::UpDisconnected => None, - oneshot::UpWoke(task) => Some(task), - }; - a.inherit_blocker(sleeper, guard); - } - a - } - Flavor::Stream(ref p) => { - let a = Arc::new(shared::Packet::new()); - { - let guard = a.postinit_lock(); - let rx = Receiver::new(Flavor::Shared(a.clone())); - let sleeper = match p.upgrade(rx) { - stream::UpSuccess | stream::UpDisconnected => None, - stream::UpWoke(task) => Some(task), - }; - a.inherit_blocker(sleeper, guard); - } - a - } - Flavor::Shared(ref p) => { - p.clone_chan(); - return Sender::new(Flavor::Shared(p.clone())); - } - Flavor::Sync(..) => unreachable!(), - }; - - unsafe { - let tmp = Sender::new(Flavor::Shared(packet.clone())); - mem::swap(self.inner_mut(), tmp.inner_mut()); - } - Sender::new(Flavor::Shared(packet)) + Sender { inner: self.inner.clone() } } } #[stable(feature = "rust1", since = "1.0.0")] impl Drop for Sender { fn drop(&mut self) { - match *unsafe { self.inner() } { - Flavor::Oneshot(ref p) => p.drop_chan(), - Flavor::Stream(ref p) => p.drop_chan(), - Flavor::Shared(ref p) => p.drop_chan(), - Flavor::Sync(..) => unreachable!(), - } + let _ = self.inner; } } @@ -913,10 +646,6 @@ impl fmt::Debug for Sender { //////////////////////////////////////////////////////////////////////////////// impl SyncSender { - fn new(inner: Arc>) -> SyncSender { - SyncSender { inner } - } - /// Sends a value on this synchronous channel. /// /// This function will *block* until space in the internal buffer becomes @@ -955,7 +684,7 @@ impl SyncSender { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn send(&self, t: T) -> Result<(), SendError> { - self.inner.send(t).map_err(SendError) + self.inner.send(t) } /// Attempts to send a value on this channel without blocking. @@ -1016,15 +745,14 @@ impl SyncSender { #[stable(feature = "rust1", since = "1.0.0")] impl Clone for SyncSender { fn clone(&self) -> SyncSender { - self.inner.clone_chan(); - SyncSender::new(self.inner.clone()) + SyncSender { inner: self.inner.clone() } } } #[stable(feature = "rust1", since = "1.0.0")] impl Drop for SyncSender { fn drop(&mut self) { - self.inner.drop_chan(); + let _ = self.inner; } } @@ -1040,10 +768,6 @@ impl fmt::Debug for SyncSender { //////////////////////////////////////////////////////////////////////////////// impl Receiver { - fn new(inner: Flavor) -> Receiver { - Receiver { inner: UnsafeCell::new(inner) } - } - /// Attempts to return a pending value on this receiver without blocking. /// /// This method will never block the caller in order to wait for data to @@ -1069,35 +793,7 @@ impl Receiver { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn try_recv(&self) -> Result { - loop { - let new_port = match *unsafe { self.inner() } { - Flavor::Oneshot(ref p) => match p.try_recv() { - Ok(t) => return Ok(t), - Err(oneshot::Empty) => return Err(TryRecvError::Empty), - Err(oneshot::Disconnected) => return Err(TryRecvError::Disconnected), - Err(oneshot::Upgraded(rx)) => rx, - }, - Flavor::Stream(ref p) => match p.try_recv() { - Ok(t) => return Ok(t), - Err(stream::Empty) => return Err(TryRecvError::Empty), - Err(stream::Disconnected) => return Err(TryRecvError::Disconnected), - Err(stream::Upgraded(rx)) => rx, - }, - Flavor::Shared(ref p) => match p.try_recv() { - Ok(t) => return Ok(t), - Err(shared::Empty) => return Err(TryRecvError::Empty), - Err(shared::Disconnected) => return Err(TryRecvError::Disconnected), - }, - Flavor::Sync(ref p) => match p.try_recv() { - Ok(t) => return Ok(t), - Err(sync::Empty) => return Err(TryRecvError::Empty), - Err(sync::Disconnected) => return Err(TryRecvError::Disconnected), - }, - }; - unsafe { - mem::swap(self.inner_mut(), new_port.inner_mut()); - } - } + self.inner.try_recv() } /// Attempts to wait for a value on this receiver, returning an error if the @@ -1156,31 +852,7 @@ impl Receiver { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn recv(&self) -> Result { - loop { - let new_port = match *unsafe { self.inner() } { - Flavor::Oneshot(ref p) => match p.recv(None) { - Ok(t) => return Ok(t), - Err(oneshot::Disconnected) => return Err(RecvError), - Err(oneshot::Upgraded(rx)) => rx, - Err(oneshot::Empty) => unreachable!(), - }, - Flavor::Stream(ref p) => match p.recv(None) { - Ok(t) => return Ok(t), - Err(stream::Disconnected) => return Err(RecvError), - Err(stream::Upgraded(rx)) => rx, - Err(stream::Empty) => unreachable!(), - }, - Flavor::Shared(ref p) => match p.recv(None) { - Ok(t) => return Ok(t), - Err(shared::Disconnected) => return Err(RecvError), - Err(shared::Empty) => unreachable!(), - }, - Flavor::Sync(ref p) => return p.recv(None).map_err(|_| RecvError), - }; - unsafe { - mem::swap(self.inner_mut(), new_port.inner_mut()); - } - } + self.inner.recv() } /// Attempts to wait for a value on this receiver, returning an error if the @@ -1198,34 +870,6 @@ impl Receiver { /// However, since channels are buffered, messages sent before the disconnect /// will still be properly received. /// - /// # Known Issues - /// - /// There is currently a known issue (see [`#39364`]) that causes `recv_timeout` - /// to panic unexpectedly with the following example: - /// - /// ```no_run - /// use std::sync::mpsc::channel; - /// use std::thread; - /// use std::time::Duration; - /// - /// let (tx, rx) = channel::(); - /// - /// thread::spawn(move || { - /// let d = Duration::from_millis(10); - /// loop { - /// println!("recv"); - /// let _r = rx.recv_timeout(d); - /// } - /// }); - /// - /// thread::sleep(Duration::from_millis(100)); - /// let _c1 = tx.clone(); - /// - /// thread::sleep(Duration::from_secs(1)); - /// ``` - /// - /// [`#39364`]: https://github.com/rust-lang/rust/issues/39364 - /// /// # Examples /// /// Successfully receiving value before encountering timeout: @@ -1268,17 +912,7 @@ impl Receiver { /// ``` #[stable(feature = "mpsc_recv_timeout", since = "1.12.0")] pub fn recv_timeout(&self, timeout: Duration) -> Result { - // Do an optimistic try_recv to avoid the performance impact of - // Instant::now() in the full-channel case. - match self.try_recv() { - Ok(result) => Ok(result), - Err(TryRecvError::Disconnected) => Err(RecvTimeoutError::Disconnected), - Err(TryRecvError::Empty) => match Instant::now().checked_add(timeout) { - Some(deadline) => self.recv_deadline(deadline), - // So far in the future that it's practically the same as waiting indefinitely. - None => self.recv().map_err(RecvTimeoutError::from), - }, - } + self.inner.recv_timeout(timeout) } /// Attempts to wait for a value on this receiver, returning an error if the @@ -1339,46 +973,7 @@ impl Receiver { /// ``` #[unstable(feature = "deadline_api", issue = "46316")] pub fn recv_deadline(&self, deadline: Instant) -> Result { - use self::RecvTimeoutError::*; - - loop { - let port_or_empty = match *unsafe { self.inner() } { - Flavor::Oneshot(ref p) => match p.recv(Some(deadline)) { - Ok(t) => return Ok(t), - Err(oneshot::Disconnected) => return Err(Disconnected), - Err(oneshot::Upgraded(rx)) => Some(rx), - Err(oneshot::Empty) => None, - }, - Flavor::Stream(ref p) => match p.recv(Some(deadline)) { - Ok(t) => return Ok(t), - Err(stream::Disconnected) => return Err(Disconnected), - Err(stream::Upgraded(rx)) => Some(rx), - Err(stream::Empty) => None, - }, - Flavor::Shared(ref p) => match p.recv(Some(deadline)) { - Ok(t) => return Ok(t), - Err(shared::Disconnected) => return Err(Disconnected), - Err(shared::Empty) => None, - }, - Flavor::Sync(ref p) => match p.recv(Some(deadline)) { - Ok(t) => return Ok(t), - Err(sync::Disconnected) => return Err(Disconnected), - Err(sync::Empty) => None, - }, - }; - - if let Some(new_port) = port_or_empty { - unsafe { - mem::swap(self.inner_mut(), new_port.inner_mut()); - } - } - - // If we're already passed the deadline, and we're here without - // data, return a timeout, else try again. - if Instant::now() >= deadline { - return Err(Timeout); - } - } + self.inner.recv_deadline(deadline) } /// Returns an iterator that will block waiting for messages, but never @@ -1500,12 +1095,7 @@ impl IntoIterator for Receiver { #[stable(feature = "rust1", since = "1.0.0")] impl Drop for Receiver { fn drop(&mut self) { - match *unsafe { self.inner() } { - Flavor::Oneshot(ref p) => p.drop_port(), - Flavor::Stream(ref p) => p.drop_port(), - Flavor::Shared(ref p) => p.drop_port(), - Flavor::Sync(ref p) => p.drop_port(), - } + let _ = self.inner; } } diff --git a/library/std/src/sync/mpsc/mpsc_queue.rs b/library/std/src/sync/mpsc/mpsc_queue.rs deleted file mode 100644 index 7322512e3b45..000000000000 --- a/library/std/src/sync/mpsc/mpsc_queue.rs +++ /dev/null @@ -1,124 +0,0 @@ -//! A mostly lock-free multi-producer, single consumer queue. -//! -//! This module contains an implementation of a concurrent MPSC queue. This -//! queue can be used to share data between threads, and is also used as the -//! building block of channels in rust. -//! -//! Note that the current implementation of this queue has a caveat of the `pop` -//! method, and see the method for more information about it. Due to this -//! caveat, this queue might not be appropriate for all use-cases. - -// The original implementation is based off: -// https://www.1024cores.net/home/lock-free-algorithms/queues/non-intrusive-mpsc-node-based-queue -// -// Note that back when the code was imported, it was licensed under the BSD-2-Clause license: -// http://web.archive.org/web/20110411011612/https://www.1024cores.net/home/lock-free-algorithms/queues/unbounded-spsc-queue -// -// The original author of the code agreed to relicense it under `MIT OR Apache-2.0` in 2017, so as -// of today the license of this file is the same as the rest of the codebase: -// https://github.com/rust-lang/rust/pull/42149 - -#[cfg(all(test, not(target_os = "emscripten")))] -mod tests; - -pub use self::PopResult::*; - -use core::cell::UnsafeCell; -use core::ptr; - -use crate::boxed::Box; -use crate::sync::atomic::{AtomicPtr, Ordering}; - -/// A result of the `pop` function. -pub enum PopResult { - /// Some data has been popped - Data(T), - /// The queue is empty - Empty, - /// The queue is in an inconsistent state. Popping data should succeed, but - /// some pushers have yet to make enough progress in order allow a pop to - /// succeed. It is recommended that a pop() occur "in the near future" in - /// order to see if the sender has made progress or not - Inconsistent, -} - -struct Node { - next: AtomicPtr>, - value: Option, -} - -/// The multi-producer single-consumer structure. This is not cloneable, but it -/// may be safely shared so long as it is guaranteed that there is only one -/// popper at a time (many pushers are allowed). -pub struct Queue { - head: AtomicPtr>, - tail: UnsafeCell<*mut Node>, -} - -unsafe impl Send for Queue {} -unsafe impl Sync for Queue {} - -impl Node { - unsafe fn new(v: Option) -> *mut Node { - Box::into_raw(box Node { next: AtomicPtr::new(ptr::null_mut()), value: v }) - } -} - -impl Queue { - /// Creates a new queue that is safe to share among multiple producers and - /// one consumer. - pub fn new() -> Queue { - let stub = unsafe { Node::new(None) }; - Queue { head: AtomicPtr::new(stub), tail: UnsafeCell::new(stub) } - } - - /// Pushes a new value onto this queue. - pub fn push(&self, t: T) { - unsafe { - let n = Node::new(Some(t)); - let prev = self.head.swap(n, Ordering::AcqRel); - (*prev).next.store(n, Ordering::Release); - } - } - - /// Pops some data from this queue. - /// - /// Note that the current implementation means that this function cannot - /// return `Option`. It is possible for this queue to be in an - /// inconsistent state where many pushes have succeeded and completely - /// finished, but pops cannot return `Some(t)`. This inconsistent state - /// happens when a pusher is pre-empted at an inopportune moment. - /// - /// This inconsistent state means that this queue does indeed have data, but - /// it does not currently have access to it at this time. - pub fn pop(&self) -> PopResult { - unsafe { - let tail = *self.tail.get(); - let next = (*tail).next.load(Ordering::Acquire); - - if !next.is_null() { - *self.tail.get() = next; - assert!((*tail).value.is_none()); - assert!((*next).value.is_some()); - let ret = (*next).value.take().unwrap(); - let _: Box> = Box::from_raw(tail); - return Data(ret); - } - - if self.head.load(Ordering::Acquire) == tail { Empty } else { Inconsistent } - } - } -} - -impl Drop for Queue { - fn drop(&mut self) { - unsafe { - let mut cur = *self.tail.get(); - while !cur.is_null() { - let next = (*cur).next.load(Ordering::Relaxed); - let _: Box> = Box::from_raw(cur); - cur = next; - } - } - } -} diff --git a/library/std/src/sync/mpsc/mpsc_queue/tests.rs b/library/std/src/sync/mpsc/mpsc_queue/tests.rs deleted file mode 100644 index 34b2a9a98ac3..000000000000 --- a/library/std/src/sync/mpsc/mpsc_queue/tests.rs +++ /dev/null @@ -1,47 +0,0 @@ -use super::{Data, Empty, Inconsistent, Queue}; -use crate::sync::mpsc::channel; -use crate::sync::Arc; -use crate::thread; - -#[test] -fn test_full() { - let q: Queue> = Queue::new(); - q.push(Box::new(1)); - q.push(Box::new(2)); -} - -#[test] -fn test() { - let nthreads = 8; - let nmsgs = if cfg!(miri) { 100 } else { 1000 }; - let q = Queue::new(); - match q.pop() { - Empty => {} - Inconsistent | Data(..) => panic!(), - } - let (tx, rx) = channel(); - let q = Arc::new(q); - - for _ in 0..nthreads { - let tx = tx.clone(); - let q = q.clone(); - thread::spawn(move || { - for i in 0..nmsgs { - q.push(i); - } - tx.send(()).unwrap(); - }); - } - - let mut i = 0; - while i < nthreads * nmsgs { - match q.pop() { - Empty | Inconsistent => {} - Data(_) => i += 1, - } - } - drop(tx); - for _ in 0..nthreads { - rx.recv().unwrap(); - } -} diff --git a/library/std/src/sync/mpsc/oneshot.rs b/library/std/src/sync/mpsc/oneshot.rs deleted file mode 100644 index 0e259b8aecb9..000000000000 --- a/library/std/src/sync/mpsc/oneshot.rs +++ /dev/null @@ -1,315 +0,0 @@ -/// Oneshot channels/ports -/// -/// This is the initial flavor of channels/ports used for comm module. This is -/// an optimization for the one-use case of a channel. The major optimization of -/// this type is to have one and exactly one allocation when the chan/port pair -/// is created. -/// -/// Another possible optimization would be to not use an Arc box because -/// in theory we know when the shared packet can be deallocated (no real need -/// for the atomic reference counting), but I was having trouble how to destroy -/// the data early in a drop of a Port. -/// -/// # Implementation -/// -/// Oneshots are implemented around one atomic usize variable. This variable -/// indicates both the state of the port/chan but also contains any threads -/// blocked on the port. All atomic operations happen on this one word. -/// -/// In order to upgrade a oneshot channel, an upgrade is considered a disconnect -/// on behalf of the channel side of things (it can be mentally thought of as -/// consuming the port). This upgrade is then also stored in the shared packet. -/// The one caveat to consider is that when a port sees a disconnected channel -/// it must check for data because there is no "data plus upgrade" state. -pub use self::Failure::*; -use self::MyUpgrade::*; -pub use self::UpgradeResult::*; - -use crate::cell::UnsafeCell; -use crate::ptr; -use crate::sync::atomic::{AtomicPtr, Ordering}; -use crate::sync::mpsc::blocking::{self, SignalToken}; -use crate::sync::mpsc::Receiver; -use crate::time::Instant; - -// Various states you can find a port in. -const EMPTY: *mut u8 = ptr::invalid_mut::(0); // initial state: no data, no blocked receiver -const DATA: *mut u8 = ptr::invalid_mut::(1); // data ready for receiver to take -const DISCONNECTED: *mut u8 = ptr::invalid_mut::(2); // channel is disconnected OR upgraded -// Any other value represents a pointer to a SignalToken value. The -// protocol ensures that when the state moves *to* a pointer, -// ownership of the token is given to the packet, and when the state -// moves *from* a pointer, ownership of the token is transferred to -// whoever changed the state. - -pub struct Packet { - // Internal state of the chan/port pair (stores the blocked thread as well) - state: AtomicPtr, - // One-shot data slot location - data: UnsafeCell>, - // when used for the second time, a oneshot channel must be upgraded, and - // this contains the slot for the upgrade - upgrade: UnsafeCell>, -} - -pub enum Failure { - Empty, - Disconnected, - Upgraded(Receiver), -} - -pub enum UpgradeResult { - UpSuccess, - UpDisconnected, - UpWoke(SignalToken), -} - -enum MyUpgrade { - NothingSent, - SendUsed, - GoUp(Receiver), -} - -impl Packet { - pub fn new() -> Packet { - Packet { - data: UnsafeCell::new(None), - upgrade: UnsafeCell::new(NothingSent), - state: AtomicPtr::new(EMPTY), - } - } - - pub fn send(&self, t: T) -> Result<(), T> { - unsafe { - // Sanity check - match *self.upgrade.get() { - NothingSent => {} - _ => panic!("sending on a oneshot that's already sent on "), - } - assert!((*self.data.get()).is_none()); - ptr::write(self.data.get(), Some(t)); - ptr::write(self.upgrade.get(), SendUsed); - - match self.state.swap(DATA, Ordering::SeqCst) { - // Sent the data, no one was waiting - EMPTY => Ok(()), - - // Couldn't send the data, the port hung up first. Return the data - // back up the stack. - DISCONNECTED => { - self.state.swap(DISCONNECTED, Ordering::SeqCst); - ptr::write(self.upgrade.get(), NothingSent); - Err((&mut *self.data.get()).take().unwrap()) - } - - // Not possible, these are one-use channels - DATA => unreachable!(), - - // There is a thread waiting on the other end. We leave the 'DATA' - // state inside so it'll pick it up on the other end. - ptr => { - SignalToken::from_raw(ptr).signal(); - Ok(()) - } - } - } - } - - // Just tests whether this channel has been sent on or not, this is only - // safe to use from the sender. - pub fn sent(&self) -> bool { - unsafe { !matches!(*self.upgrade.get(), NothingSent) } - } - - pub fn recv(&self, deadline: Option) -> Result> { - // Attempt to not block the thread (it's a little expensive). If it looks - // like we're not empty, then immediately go through to `try_recv`. - if self.state.load(Ordering::SeqCst) == EMPTY { - let (wait_token, signal_token) = blocking::tokens(); - let ptr = unsafe { signal_token.to_raw() }; - - // race with senders to enter the blocking state - if self.state.compare_exchange(EMPTY, ptr, Ordering::SeqCst, Ordering::SeqCst).is_ok() { - if let Some(deadline) = deadline { - let timed_out = !wait_token.wait_max_until(deadline); - // Try to reset the state - if timed_out { - self.abort_selection().map_err(Upgraded)?; - } - } else { - wait_token.wait(); - debug_assert!(self.state.load(Ordering::SeqCst) != EMPTY); - } - } else { - // drop the signal token, since we never blocked - drop(unsafe { SignalToken::from_raw(ptr) }); - } - } - - self.try_recv() - } - - pub fn try_recv(&self) -> Result> { - unsafe { - match self.state.load(Ordering::SeqCst) { - EMPTY => Err(Empty), - - // We saw some data on the channel, but the channel can be used - // again to send us an upgrade. As a result, we need to re-insert - // into the channel that there's no data available (otherwise we'll - // just see DATA next time). This is done as a cmpxchg because if - // the state changes under our feet we'd rather just see that state - // change. - DATA => { - let _ = self.state.compare_exchange( - DATA, - EMPTY, - Ordering::SeqCst, - Ordering::SeqCst, - ); - match (&mut *self.data.get()).take() { - Some(data) => Ok(data), - None => unreachable!(), - } - } - - // There's no guarantee that we receive before an upgrade happens, - // and an upgrade flags the channel as disconnected, so when we see - // this we first need to check if there's data available and *then* - // we go through and process the upgrade. - DISCONNECTED => match (&mut *self.data.get()).take() { - Some(data) => Ok(data), - None => match ptr::replace(self.upgrade.get(), SendUsed) { - SendUsed | NothingSent => Err(Disconnected), - GoUp(upgrade) => Err(Upgraded(upgrade)), - }, - }, - - // We are the sole receiver; there cannot be a blocking - // receiver already. - _ => unreachable!(), - } - } - } - - // Returns whether the upgrade was completed. If the upgrade wasn't - // completed, then the port couldn't get sent to the other half (it will - // never receive it). - pub fn upgrade(&self, up: Receiver) -> UpgradeResult { - unsafe { - let prev = match *self.upgrade.get() { - NothingSent => NothingSent, - SendUsed => SendUsed, - _ => panic!("upgrading again"), - }; - ptr::write(self.upgrade.get(), GoUp(up)); - - match self.state.swap(DISCONNECTED, Ordering::SeqCst) { - // If the channel is empty or has data on it, then we're good to go. - // Senders will check the data before the upgrade (in case we - // plastered over the DATA state). - DATA | EMPTY => UpSuccess, - - // If the other end is already disconnected, then we failed the - // upgrade. Be sure to trash the port we were given. - DISCONNECTED => { - ptr::replace(self.upgrade.get(), prev); - UpDisconnected - } - - // If someone's waiting, we gotta wake them up - ptr => UpWoke(SignalToken::from_raw(ptr)), - } - } - } - - pub fn drop_chan(&self) { - match self.state.swap(DISCONNECTED, Ordering::SeqCst) { - DATA | DISCONNECTED | EMPTY => {} - - // If someone's waiting, we gotta wake them up - ptr => unsafe { - SignalToken::from_raw(ptr).signal(); - }, - } - } - - pub fn drop_port(&self) { - match self.state.swap(DISCONNECTED, Ordering::SeqCst) { - // An empty channel has nothing to do, and a remotely disconnected - // channel also has nothing to do b/c we're about to run the drop - // glue - DISCONNECTED | EMPTY => {} - - // There's data on the channel, so make sure we destroy it promptly. - // This is why not using an arc is a little difficult (need the box - // to stay valid while we take the data). - DATA => unsafe { - (&mut *self.data.get()).take().unwrap(); - }, - - // We're the only ones that can block on this port - _ => unreachable!(), - } - } - - //////////////////////////////////////////////////////////////////////////// - // select implementation - //////////////////////////////////////////////////////////////////////////// - - // Remove a previous selecting thread from this port. This ensures that the - // blocked thread will no longer be visible to any other threads. - // - // The return value indicates whether there's data on this port. - pub fn abort_selection(&self) -> Result> { - let state = match self.state.load(Ordering::SeqCst) { - // Each of these states means that no further activity will happen - // with regard to abortion selection - s @ (EMPTY | DATA | DISCONNECTED) => s, - - // If we've got a blocked thread, then use an atomic to gain ownership - // of it (may fail) - ptr => self - .state - .compare_exchange(ptr, EMPTY, Ordering::SeqCst, Ordering::SeqCst) - .unwrap_or_else(|x| x), - }; - - // Now that we've got ownership of our state, figure out what to do - // about it. - match state { - EMPTY => unreachable!(), - // our thread used for select was stolen - DATA => Ok(true), - - // If the other end has hung up, then we have complete ownership - // of the port. First, check if there was data waiting for us. This - // is possible if the other end sent something and then hung up. - // - // We then need to check to see if there was an upgrade requested, - // and if so, the upgraded port needs to have its selection aborted. - DISCONNECTED => unsafe { - if (*self.data.get()).is_some() { - Ok(true) - } else { - match ptr::replace(self.upgrade.get(), SendUsed) { - GoUp(port) => Err(port), - _ => Ok(true), - } - } - }, - - // We woke ourselves up from select. - ptr => unsafe { - drop(SignalToken::from_raw(ptr)); - Ok(false) - }, - } - } -} - -impl Drop for Packet { - fn drop(&mut self) { - assert_eq!(self.state.load(Ordering::SeqCst), DISCONNECTED); - } -} diff --git a/library/std/src/sync/mpsc/shared.rs b/library/std/src/sync/mpsc/shared.rs deleted file mode 100644 index 51917bd96bd6..000000000000 --- a/library/std/src/sync/mpsc/shared.rs +++ /dev/null @@ -1,501 +0,0 @@ -/// Shared channels. -/// -/// This is the flavor of channels which are not necessarily optimized for any -/// particular use case, but are the most general in how they are used. Shared -/// channels are cloneable allowing for multiple senders. -/// -/// High level implementation details can be found in the comment of the parent -/// module. You'll also note that the implementation of the shared and stream -/// channels are quite similar, and this is no coincidence! -pub use self::Failure::*; -use self::StartResult::*; - -use core::cmp; -use core::intrinsics::abort; - -use crate::cell::UnsafeCell; -use crate::ptr; -use crate::sync::atomic::{AtomicBool, AtomicIsize, AtomicPtr, AtomicUsize, Ordering}; -use crate::sync::mpsc::blocking::{self, SignalToken}; -use crate::sync::mpsc::mpsc_queue as mpsc; -use crate::sync::{Mutex, MutexGuard}; -use crate::thread; -use crate::time::Instant; - -const DISCONNECTED: isize = isize::MIN; -const FUDGE: isize = 1024; -const MAX_REFCOUNT: usize = (isize::MAX) as usize; -#[cfg(test)] -const MAX_STEALS: isize = 5; -#[cfg(not(test))] -const MAX_STEALS: isize = 1 << 20; -const EMPTY: *mut u8 = ptr::null_mut(); // initial state: no data, no blocked receiver - -pub struct Packet { - queue: mpsc::Queue, - cnt: AtomicIsize, // How many items are on this channel - steals: UnsafeCell, // How many times has a port received without blocking? - to_wake: AtomicPtr, // SignalToken for wake up - - // The number of channels which are currently using this packet. - channels: AtomicUsize, - - // See the discussion in Port::drop and the channel send methods for what - // these are used for - port_dropped: AtomicBool, - sender_drain: AtomicIsize, - - // this lock protects various portions of this implementation during - // select() - select_lock: Mutex<()>, -} - -pub enum Failure { - Empty, - Disconnected, -} - -#[derive(PartialEq, Eq)] -enum StartResult { - Installed, - Abort, -} - -impl Packet { - // Creation of a packet *must* be followed by a call to postinit_lock - // and later by inherit_blocker - pub fn new() -> Packet { - Packet { - queue: mpsc::Queue::new(), - cnt: AtomicIsize::new(0), - steals: UnsafeCell::new(0), - to_wake: AtomicPtr::new(EMPTY), - channels: AtomicUsize::new(2), - port_dropped: AtomicBool::new(false), - sender_drain: AtomicIsize::new(0), - select_lock: Mutex::new(()), - } - } - - // This function should be used after newly created Packet - // was wrapped with an Arc - // In other case mutex data will be duplicated while cloning - // and that could cause problems on platforms where it is - // represented by opaque data structure - pub fn postinit_lock(&self) -> MutexGuard<'_, ()> { - self.select_lock.lock().unwrap() - } - - // This function is used at the creation of a shared packet to inherit a - // previously blocked thread. This is done to prevent spurious wakeups of - // threads in select(). - // - // This can only be called at channel-creation time - pub fn inherit_blocker(&self, token: Option, guard: MutexGuard<'_, ()>) { - if let Some(token) = token { - assert_eq!(self.cnt.load(Ordering::SeqCst), 0); - assert_eq!(self.to_wake.load(Ordering::SeqCst), EMPTY); - self.to_wake.store(unsafe { token.to_raw() }, Ordering::SeqCst); - self.cnt.store(-1, Ordering::SeqCst); - - // This store is a little sketchy. What's happening here is that - // we're transferring a blocker from a oneshot or stream channel to - // this shared channel. In doing so, we never spuriously wake them - // up and rather only wake them up at the appropriate time. This - // implementation of shared channels assumes that any blocking - // recv() will undo the increment of steals performed in try_recv() - // once the recv is complete. This thread that we're inheriting, - // however, is not in the middle of recv. Hence, the first time we - // wake them up, they're going to wake up from their old port, move - // on to the upgraded port, and then call the block recv() function. - // - // When calling this function, they'll find there's data immediately - // available, counting it as a steal. This in fact wasn't a steal - // because we appropriately blocked them waiting for data. - // - // To offset this bad increment, we initially set the steal count to - // -1. You'll find some special code in abort_selection() as well to - // ensure that this -1 steal count doesn't escape too far. - unsafe { - *self.steals.get() = -1; - } - } - - // When the shared packet is constructed, we grabbed this lock. The - // purpose of this lock is to ensure that abort_selection() doesn't - // interfere with this method. After we unlock this lock, we're - // signifying that we're done modifying self.cnt and self.to_wake and - // the port is ready for the world to continue using it. - drop(guard); - } - - pub fn send(&self, t: T) -> Result<(), T> { - // See Port::drop for what's going on - if self.port_dropped.load(Ordering::SeqCst) { - return Err(t); - } - - // Note that the multiple sender case is a little trickier - // semantically than the single sender case. The logic for - // incrementing is "add and if disconnected store disconnected". - // This could end up leading some senders to believe that there - // wasn't a disconnect if in fact there was a disconnect. This means - // that while one thread is attempting to re-store the disconnected - // states, other threads could walk through merrily incrementing - // this very-negative disconnected count. To prevent senders from - // spuriously attempting to send when the channels is actually - // disconnected, the count has a ranged check here. - // - // This is also done for another reason. Remember that the return - // value of this function is: - // - // `true` == the data *may* be received, this essentially has no - // meaning - // `false` == the data will *never* be received, this has a lot of - // meaning - // - // In the SPSC case, we have a check of 'queue.is_empty()' to see - // whether the data was actually received, but this same condition - // means nothing in a multi-producer context. As a result, this - // preflight check serves as the definitive "this will never be - // received". Once we get beyond this check, we have permanently - // entered the realm of "this may be received" - if self.cnt.load(Ordering::SeqCst) < DISCONNECTED + FUDGE { - return Err(t); - } - - self.queue.push(t); - match self.cnt.fetch_add(1, Ordering::SeqCst) { - -1 => { - self.take_to_wake().signal(); - } - - // In this case, we have possibly failed to send our data, and - // we need to consider re-popping the data in order to fully - // destroy it. We must arbitrate among the multiple senders, - // however, because the queues that we're using are - // single-consumer queues. In order to do this, all exiting - // pushers will use an atomic count in order to count those - // flowing through. Pushers who see 0 are required to drain as - // much as possible, and then can only exit when they are the - // only pusher (otherwise they must try again). - n if n < DISCONNECTED + FUDGE => { - // see the comment in 'try' for a shared channel for why this - // window of "not disconnected" is ok. - self.cnt.store(DISCONNECTED, Ordering::SeqCst); - - if self.sender_drain.fetch_add(1, Ordering::SeqCst) == 0 { - loop { - // drain the queue, for info on the thread yield see the - // discussion in try_recv - loop { - match self.queue.pop() { - mpsc::Data(..) => {} - mpsc::Empty => break, - mpsc::Inconsistent => thread::yield_now(), - } - } - // maybe we're done, if we're not the last ones - // here, then we need to go try again. - if self.sender_drain.fetch_sub(1, Ordering::SeqCst) == 1 { - break; - } - } - - // At this point, there may still be data on the queue, - // but only if the count hasn't been incremented and - // some other sender hasn't finished pushing data just - // yet. That sender in question will drain its own data. - } - } - - // Can't make any assumptions about this case like in the SPSC case. - _ => {} - } - - Ok(()) - } - - pub fn recv(&self, deadline: Option) -> Result { - // This code is essentially the exact same as that found in the stream - // case (see stream.rs) - match self.try_recv() { - Err(Empty) => {} - data => return data, - } - - let (wait_token, signal_token) = blocking::tokens(); - if self.decrement(signal_token) == Installed { - if let Some(deadline) = deadline { - let timed_out = !wait_token.wait_max_until(deadline); - if timed_out { - self.abort_selection(false); - } - } else { - wait_token.wait(); - } - } - - match self.try_recv() { - data @ Ok(..) => unsafe { - *self.steals.get() -= 1; - data - }, - data => data, - } - } - - // Essentially the exact same thing as the stream decrement function. - // Returns true if blocking should proceed. - fn decrement(&self, token: SignalToken) -> StartResult { - unsafe { - assert_eq!( - self.to_wake.load(Ordering::SeqCst), - EMPTY, - "This is a known bug in the Rust standard library. See https://github.com/rust-lang/rust/issues/39364" - ); - let ptr = token.to_raw(); - self.to_wake.store(ptr, Ordering::SeqCst); - - let steals = ptr::replace(self.steals.get(), 0); - - match self.cnt.fetch_sub(1 + steals, Ordering::SeqCst) { - DISCONNECTED => { - self.cnt.store(DISCONNECTED, Ordering::SeqCst); - } - // If we factor in our steals and notice that the channel has no - // data, we successfully sleep - n => { - assert!(n >= 0); - if n - steals <= 0 { - return Installed; - } - } - } - - self.to_wake.store(EMPTY, Ordering::SeqCst); - drop(SignalToken::from_raw(ptr)); - Abort - } - } - - pub fn try_recv(&self) -> Result { - let ret = match self.queue.pop() { - mpsc::Data(t) => Some(t), - mpsc::Empty => None, - - // This is a bit of an interesting case. The channel is reported as - // having data available, but our pop() has failed due to the queue - // being in an inconsistent state. This means that there is some - // pusher somewhere which has yet to complete, but we are guaranteed - // that a pop will eventually succeed. In this case, we spin in a - // yield loop because the remote sender should finish their enqueue - // operation "very quickly". - // - // Avoiding this yield loop would require a different queue - // abstraction which provides the guarantee that after M pushes have - // succeeded, at least M pops will succeed. The current queues - // guarantee that if there are N active pushes, you can pop N times - // once all N have finished. - mpsc::Inconsistent => { - let data; - loop { - thread::yield_now(); - match self.queue.pop() { - mpsc::Data(t) => { - data = t; - break; - } - mpsc::Empty => panic!("inconsistent => empty"), - mpsc::Inconsistent => {} - } - } - Some(data) - } - }; - match ret { - // See the discussion in the stream implementation for why we - // might decrement steals. - Some(data) => unsafe { - if *self.steals.get() > MAX_STEALS { - match self.cnt.swap(0, Ordering::SeqCst) { - DISCONNECTED => { - self.cnt.store(DISCONNECTED, Ordering::SeqCst); - } - n => { - let m = cmp::min(n, *self.steals.get()); - *self.steals.get() -= m; - self.bump(n - m); - } - } - assert!(*self.steals.get() >= 0); - } - *self.steals.get() += 1; - Ok(data) - }, - - // See the discussion in the stream implementation for why we try - // again. - None => { - match self.cnt.load(Ordering::SeqCst) { - n if n != DISCONNECTED => Err(Empty), - _ => { - match self.queue.pop() { - mpsc::Data(t) => Ok(t), - mpsc::Empty => Err(Disconnected), - // with no senders, an inconsistency is impossible. - mpsc::Inconsistent => unreachable!(), - } - } - } - } - } - } - - // Prepares this shared packet for a channel clone, essentially just bumping - // a refcount. - pub fn clone_chan(&self) { - let old_count = self.channels.fetch_add(1, Ordering::SeqCst); - - // See comments on Arc::clone() on why we do this (for `mem::forget`). - if old_count > MAX_REFCOUNT { - abort(); - } - } - - // Decrement the reference count on a channel. This is called whenever a - // Chan is dropped and may end up waking up a receiver. It's the receiver's - // responsibility on the other end to figure out that we've disconnected. - pub fn drop_chan(&self) { - match self.channels.fetch_sub(1, Ordering::SeqCst) { - 1 => {} - n if n > 1 => return, - n => panic!("bad number of channels left {n}"), - } - - match self.cnt.swap(DISCONNECTED, Ordering::SeqCst) { - -1 => { - self.take_to_wake().signal(); - } - DISCONNECTED => {} - n => { - assert!(n >= 0); - } - } - } - - // See the long discussion inside of stream.rs for why the queue is drained, - // and why it is done in this fashion. - pub fn drop_port(&self) { - self.port_dropped.store(true, Ordering::SeqCst); - let mut steals = unsafe { *self.steals.get() }; - while { - match self.cnt.compare_exchange( - steals, - DISCONNECTED, - Ordering::SeqCst, - Ordering::SeqCst, - ) { - Ok(_) => false, - Err(old) => old != DISCONNECTED, - } - } { - // See the discussion in 'try_recv' for why we yield - // control of this thread. - loop { - match self.queue.pop() { - mpsc::Data(..) => { - steals += 1; - } - mpsc::Empty | mpsc::Inconsistent => break, - } - } - } - } - - // Consumes ownership of the 'to_wake' field. - fn take_to_wake(&self) -> SignalToken { - let ptr = self.to_wake.load(Ordering::SeqCst); - self.to_wake.store(EMPTY, Ordering::SeqCst); - assert!(ptr != EMPTY); - unsafe { SignalToken::from_raw(ptr) } - } - - //////////////////////////////////////////////////////////////////////////// - // select implementation - //////////////////////////////////////////////////////////////////////////// - - // increment the count on the channel (used for selection) - fn bump(&self, amt: isize) -> isize { - match self.cnt.fetch_add(amt, Ordering::SeqCst) { - DISCONNECTED => { - self.cnt.store(DISCONNECTED, Ordering::SeqCst); - DISCONNECTED - } - n => n, - } - } - - // Cancels a previous thread waiting on this port, returning whether there's - // data on the port. - // - // This is similar to the stream implementation (hence fewer comments), but - // uses a different value for the "steals" variable. - pub fn abort_selection(&self, _was_upgrade: bool) -> bool { - // Before we do anything else, we bounce on this lock. The reason for - // doing this is to ensure that any upgrade-in-progress is gone and - // done with. Without this bounce, we can race with inherit_blocker - // about looking at and dealing with to_wake. Once we have acquired the - // lock, we are guaranteed that inherit_blocker is done. - { - let _guard = self.select_lock.lock().unwrap(); - } - - // Like the stream implementation, we want to make sure that the count - // on the channel goes non-negative. We don't know how negative the - // stream currently is, so instead of using a steal value of 1, we load - // the channel count and figure out what we should do to make it - // positive. - let steals = { - let cnt = self.cnt.load(Ordering::SeqCst); - if cnt < 0 && cnt != DISCONNECTED { -cnt } else { 0 } - }; - let prev = self.bump(steals + 1); - - if prev == DISCONNECTED { - assert_eq!(self.to_wake.load(Ordering::SeqCst), EMPTY); - true - } else { - let cur = prev + steals + 1; - assert!(cur >= 0); - if prev < 0 { - drop(self.take_to_wake()); - } else { - while self.to_wake.load(Ordering::SeqCst) != EMPTY { - thread::yield_now(); - } - } - unsafe { - // if the number of steals is -1, it was the pre-emptive -1 steal - // count from when we inherited a blocker. This is fine because - // we're just going to overwrite it with a real value. - let old = self.steals.get(); - assert!(*old == 0 || *old == -1); - *old = steals; - prev >= 0 - } - } - } -} - -impl Drop for Packet { - fn drop(&mut self) { - // Note that this load is not only an assert for correctness about - // disconnection, but also a proper fence before the read of - // `to_wake`, so this assert cannot be removed with also removing - // the `to_wake` assert. - assert_eq!(self.cnt.load(Ordering::SeqCst), DISCONNECTED); - assert_eq!(self.to_wake.load(Ordering::SeqCst), EMPTY); - assert_eq!(self.channels.load(Ordering::SeqCst), 0); - } -} diff --git a/library/std/src/sync/mpsc/spsc_queue.rs b/library/std/src/sync/mpsc/spsc_queue.rs deleted file mode 100644 index 61f91313ea96..000000000000 --- a/library/std/src/sync/mpsc/spsc_queue.rs +++ /dev/null @@ -1,244 +0,0 @@ -//! A single-producer single-consumer concurrent queue -//! -//! This module contains the implementation of an SPSC queue which can be used -//! concurrently between two threads. This data structure is safe to use and -//! enforces the semantics that there is one pusher and one popper. - -// The original implementation is based off: -// https://www.1024cores.net/home/lock-free-algorithms/queues/unbounded-spsc-queue -// -// Note that back when the code was imported, it was licensed under the BSD-2-Clause license: -// http://web.archive.org/web/20110411011612/https://www.1024cores.net/home/lock-free-algorithms/queues/unbounded-spsc-queue -// -// The original author of the code agreed to relicense it under `MIT OR Apache-2.0` in 2017, so as -// of today the license of this file is the same as the rest of the codebase: -// https://github.com/rust-lang/rust/pull/42149 - -#[cfg(all(test, not(target_os = "emscripten")))] -mod tests; - -use core::cell::UnsafeCell; -use core::ptr; - -use crate::boxed::Box; -use crate::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; - -use super::cache_aligned::CacheAligned; - -// Node within the linked list queue of messages to send -struct Node { - // FIXME: this could be an uninitialized T if we're careful enough, and - // that would reduce memory usage (and be a bit faster). - // is it worth it? - value: Option, // nullable for re-use of nodes - cached: bool, // This node goes into the node cache - next: AtomicPtr>, // next node in the queue -} - -/// The single-producer single-consumer queue. This structure is not cloneable, -/// but it can be safely shared in an Arc if it is guaranteed that there -/// is only one popper and one pusher touching the queue at any one point in -/// time. -pub struct Queue { - // consumer fields - consumer: CacheAligned>, - - // producer fields - producer: CacheAligned>, -} - -struct Consumer { - tail: UnsafeCell<*mut Node>, // where to pop from - tail_prev: AtomicPtr>, // where to pop from - cache_bound: usize, // maximum cache size - cached_nodes: AtomicUsize, // number of nodes marked as cacheable - addition: Addition, -} - -struct Producer { - head: UnsafeCell<*mut Node>, // where to push to - first: UnsafeCell<*mut Node>, // where to get new nodes from - tail_copy: UnsafeCell<*mut Node>, // between first/tail - addition: Addition, -} - -unsafe impl Send for Queue {} - -unsafe impl Sync for Queue {} - -impl Node { - fn new() -> *mut Node { - Box::into_raw(box Node { - value: None, - cached: false, - next: AtomicPtr::new(ptr::null_mut::>()), - }) - } -} - -impl Queue { - /// Creates a new queue. With given additional elements in the producer and - /// consumer portions of the queue. - /// - /// Due to the performance implications of cache-contention, - /// we wish to keep fields used mainly by the producer on a separate cache - /// line than those used by the consumer. - /// Since cache lines are usually 64 bytes, it is unreasonably expensive to - /// allocate one for small fields, so we allow users to insert additional - /// fields into the cache lines already allocated by this for the producer - /// and consumer. - /// - /// This is unsafe as the type system doesn't enforce a single - /// consumer-producer relationship. It also allows the consumer to `pop` - /// items while there is a `peek` active due to all methods having a - /// non-mutable receiver. - /// - /// # Arguments - /// - /// * `bound` - This queue implementation is implemented with a linked - /// list, and this means that a push is always a malloc. In - /// order to amortize this cost, an internal cache of nodes is - /// maintained to prevent a malloc from always being - /// necessary. This bound is the limit on the size of the - /// cache (if desired). If the value is 0, then the cache has - /// no bound. Otherwise, the cache will never grow larger than - /// `bound` (although the queue itself could be much larger. - pub unsafe fn with_additions( - bound: usize, - producer_addition: ProducerAddition, - consumer_addition: ConsumerAddition, - ) -> Self { - let n1 = Node::new(); - let n2 = Node::new(); - (*n1).next.store(n2, Ordering::Relaxed); - Queue { - consumer: CacheAligned::new(Consumer { - tail: UnsafeCell::new(n2), - tail_prev: AtomicPtr::new(n1), - cache_bound: bound, - cached_nodes: AtomicUsize::new(0), - addition: consumer_addition, - }), - producer: CacheAligned::new(Producer { - head: UnsafeCell::new(n2), - first: UnsafeCell::new(n1), - tail_copy: UnsafeCell::new(n1), - addition: producer_addition, - }), - } - } - - /// Pushes a new value onto this queue. Note that to use this function - /// safely, it must be externally guaranteed that there is only one pusher. - pub fn push(&self, t: T) { - unsafe { - // Acquire a node (which either uses a cached one or allocates a new - // one), and then append this to the 'head' node. - let n = self.alloc(); - assert!((*n).value.is_none()); - (*n).value = Some(t); - (*n).next.store(ptr::null_mut(), Ordering::Relaxed); - (**self.producer.head.get()).next.store(n, Ordering::Release); - *(&self.producer.head).get() = n; - } - } - - unsafe fn alloc(&self) -> *mut Node { - // First try to see if we can consume the 'first' node for our uses. - if *self.producer.first.get() != *self.producer.tail_copy.get() { - let ret = *self.producer.first.get(); - *self.producer.0.first.get() = (*ret).next.load(Ordering::Relaxed); - return ret; - } - // If the above fails, then update our copy of the tail and try - // again. - *self.producer.0.tail_copy.get() = self.consumer.tail_prev.load(Ordering::Acquire); - if *self.producer.first.get() != *self.producer.tail_copy.get() { - let ret = *self.producer.first.get(); - *self.producer.0.first.get() = (*ret).next.load(Ordering::Relaxed); - return ret; - } - // If all of that fails, then we have to allocate a new node - // (there's nothing in the node cache). - Node::new() - } - - /// Attempts to pop a value from this queue. Remember that to use this type - /// safely you must ensure that there is only one popper at a time. - pub fn pop(&self) -> Option { - unsafe { - // The `tail` node is not actually a used node, but rather a - // sentinel from where we should start popping from. Hence, look at - // tail's next field and see if we can use it. If we do a pop, then - // the current tail node is a candidate for going into the cache. - let tail = *self.consumer.tail.get(); - let next = (*tail).next.load(Ordering::Acquire); - if next.is_null() { - return None; - } - assert!((*next).value.is_some()); - let ret = (*next).value.take(); - - *self.consumer.0.tail.get() = next; - if self.consumer.cache_bound == 0 { - self.consumer.tail_prev.store(tail, Ordering::Release); - } else { - let cached_nodes = self.consumer.cached_nodes.load(Ordering::Relaxed); - if cached_nodes < self.consumer.cache_bound && !(*tail).cached { - self.consumer.cached_nodes.store(cached_nodes, Ordering::Relaxed); - (*tail).cached = true; - } - - if (*tail).cached { - self.consumer.tail_prev.store(tail, Ordering::Release); - } else { - (*self.consumer.tail_prev.load(Ordering::Relaxed)) - .next - .store(next, Ordering::Relaxed); - // We have successfully erased all references to 'tail', so - // now we can safely drop it. - let _: Box> = Box::from_raw(tail); - } - } - ret - } - } - - /// Attempts to peek at the head of the queue, returning `None` if the queue - /// has no data currently - /// - /// # Warning - /// The reference returned is invalid if it is not used before the consumer - /// pops the value off the queue. If the producer then pushes another value - /// onto the queue, it will overwrite the value pointed to by the reference. - pub fn peek(&self) -> Option<&mut T> { - // This is essentially the same as above with all the popping bits - // stripped out. - unsafe { - let tail = *self.consumer.tail.get(); - let next = (*tail).next.load(Ordering::Acquire); - if next.is_null() { None } else { (*next).value.as_mut() } - } - } - - pub fn producer_addition(&self) -> &ProducerAddition { - &self.producer.addition - } - - pub fn consumer_addition(&self) -> &ConsumerAddition { - &self.consumer.addition - } -} - -impl Drop for Queue { - fn drop(&mut self) { - unsafe { - let mut cur = *self.producer.first.get(); - while !cur.is_null() { - let next = (*cur).next.load(Ordering::Relaxed); - let _n: Box> = Box::from_raw(cur); - cur = next; - } - } - } -} diff --git a/library/std/src/sync/mpsc/spsc_queue/tests.rs b/library/std/src/sync/mpsc/spsc_queue/tests.rs deleted file mode 100644 index eb6d5c2cf66d..000000000000 --- a/library/std/src/sync/mpsc/spsc_queue/tests.rs +++ /dev/null @@ -1,102 +0,0 @@ -use super::Queue; -use crate::sync::mpsc::channel; -use crate::sync::Arc; -use crate::thread; - -#[test] -fn smoke() { - unsafe { - let queue = Queue::with_additions(0, (), ()); - queue.push(1); - queue.push(2); - assert_eq!(queue.pop(), Some(1)); - assert_eq!(queue.pop(), Some(2)); - assert_eq!(queue.pop(), None); - queue.push(3); - queue.push(4); - assert_eq!(queue.pop(), Some(3)); - assert_eq!(queue.pop(), Some(4)); - assert_eq!(queue.pop(), None); - } -} - -#[test] -fn peek() { - unsafe { - let queue = Queue::with_additions(0, (), ()); - queue.push(vec![1]); - - // Ensure the borrowchecker works - match queue.peek() { - Some(vec) => { - assert_eq!(&*vec, &[1]); - } - None => unreachable!(), - } - - match queue.pop() { - Some(vec) => { - assert_eq!(&*vec, &[1]); - } - None => unreachable!(), - } - } -} - -#[test] -fn drop_full() { - unsafe { - let q: Queue> = Queue::with_additions(0, (), ()); - q.push(Box::new(1)); - q.push(Box::new(2)); - } -} - -#[test] -fn smoke_bound() { - unsafe { - let q = Queue::with_additions(0, (), ()); - q.push(1); - q.push(2); - assert_eq!(q.pop(), Some(1)); - assert_eq!(q.pop(), Some(2)); - assert_eq!(q.pop(), None); - q.push(3); - q.push(4); - assert_eq!(q.pop(), Some(3)); - assert_eq!(q.pop(), Some(4)); - assert_eq!(q.pop(), None); - } -} - -#[test] -fn stress() { - unsafe { - stress_bound(0); - stress_bound(1); - } - - unsafe fn stress_bound(bound: usize) { - let count = if cfg!(miri) { 1000 } else { 100000 }; - let q = Arc::new(Queue::with_additions(bound, (), ())); - - let (tx, rx) = channel(); - let q2 = q.clone(); - let _t = thread::spawn(move || { - for _ in 0..count { - loop { - match q2.pop() { - Some(1) => break, - Some(_) => panic!(), - None => {} - } - } - } - tx.send(()).unwrap(); - }); - for _ in 0..count { - q.push(1); - } - rx.recv().unwrap(); - } -} diff --git a/library/std/src/sync/mpsc/stream.rs b/library/std/src/sync/mpsc/stream.rs deleted file mode 100644 index 4592e9141600..000000000000 --- a/library/std/src/sync/mpsc/stream.rs +++ /dev/null @@ -1,457 +0,0 @@ -/// Stream channels -/// -/// This is the flavor of channels which are optimized for one sender and one -/// receiver. The sender will be upgraded to a shared channel if the channel is -/// cloned. -/// -/// High level implementation details can be found in the comment of the parent -/// module. -pub use self::Failure::*; -use self::Message::*; -pub use self::UpgradeResult::*; - -use core::cmp; - -use crate::cell::UnsafeCell; -use crate::ptr; -use crate::thread; -use crate::time::Instant; - -use crate::sync::atomic::{AtomicBool, AtomicIsize, AtomicPtr, Ordering}; -use crate::sync::mpsc::blocking::{self, SignalToken}; -use crate::sync::mpsc::spsc_queue as spsc; -use crate::sync::mpsc::Receiver; - -const DISCONNECTED: isize = isize::MIN; -#[cfg(test)] -const MAX_STEALS: isize = 5; -#[cfg(not(test))] -const MAX_STEALS: isize = 1 << 20; -const EMPTY: *mut u8 = ptr::null_mut(); // initial state: no data, no blocked receiver - -pub struct Packet { - // internal queue for all messages - queue: spsc::Queue, ProducerAddition, ConsumerAddition>, -} - -struct ProducerAddition { - cnt: AtomicIsize, // How many items are on this channel - to_wake: AtomicPtr, // SignalToken for the blocked thread to wake up - - port_dropped: AtomicBool, // flag if the channel has been destroyed. -} - -struct ConsumerAddition { - steals: UnsafeCell, // How many times has a port received without blocking? -} - -pub enum Failure { - Empty, - Disconnected, - Upgraded(Receiver), -} - -pub enum UpgradeResult { - UpSuccess, - UpDisconnected, - UpWoke(SignalToken), -} - -// Any message could contain an "upgrade request" to a new shared port, so the -// internal queue it's a queue of T, but rather Message -enum Message { - Data(T), - GoUp(Receiver), -} - -impl Packet { - pub fn new() -> Packet { - Packet { - queue: unsafe { - spsc::Queue::with_additions( - 128, - ProducerAddition { - cnt: AtomicIsize::new(0), - to_wake: AtomicPtr::new(EMPTY), - - port_dropped: AtomicBool::new(false), - }, - ConsumerAddition { steals: UnsafeCell::new(0) }, - ) - }, - } - } - - pub fn send(&self, t: T) -> Result<(), T> { - // If the other port has deterministically gone away, then definitely - // must return the data back up the stack. Otherwise, the data is - // considered as being sent. - if self.queue.producer_addition().port_dropped.load(Ordering::SeqCst) { - return Err(t); - } - - match self.do_send(Data(t)) { - UpSuccess | UpDisconnected => {} - UpWoke(token) => { - token.signal(); - } - } - Ok(()) - } - - pub fn upgrade(&self, up: Receiver) -> UpgradeResult { - // If the port has gone away, then there's no need to proceed any - // further. - if self.queue.producer_addition().port_dropped.load(Ordering::SeqCst) { - return UpDisconnected; - } - - self.do_send(GoUp(up)) - } - - fn do_send(&self, t: Message) -> UpgradeResult { - self.queue.push(t); - match self.queue.producer_addition().cnt.fetch_add(1, Ordering::SeqCst) { - // As described in the mod's doc comment, -1 == wakeup - -1 => UpWoke(self.take_to_wake()), - // As described before, SPSC queues must be >= -2 - -2 => UpSuccess, - - // Be sure to preserve the disconnected state, and the return value - // in this case is going to be whether our data was received or not. - // This manifests itself on whether we have an empty queue or not. - // - // Primarily, are required to drain the queue here because the port - // will never remove this data. We can only have at most one item to - // drain (the port drains the rest). - DISCONNECTED => { - self.queue.producer_addition().cnt.store(DISCONNECTED, Ordering::SeqCst); - let first = self.queue.pop(); - let second = self.queue.pop(); - assert!(second.is_none()); - - match first { - Some(..) => UpSuccess, // we failed to send the data - None => UpDisconnected, // we successfully sent data - } - } - - // Otherwise we just sent some data on a non-waiting queue, so just - // make sure the world is sane and carry on! - n => { - assert!(n >= 0); - UpSuccess - } - } - } - - // Consumes ownership of the 'to_wake' field. - fn take_to_wake(&self) -> SignalToken { - let ptr = self.queue.producer_addition().to_wake.load(Ordering::SeqCst); - self.queue.producer_addition().to_wake.store(EMPTY, Ordering::SeqCst); - assert!(ptr != EMPTY); - unsafe { SignalToken::from_raw(ptr) } - } - - // Decrements the count on the channel for a sleeper, returning the sleeper - // back if it shouldn't sleep. Note that this is the location where we take - // steals into account. - fn decrement(&self, token: SignalToken) -> Result<(), SignalToken> { - assert_eq!(self.queue.producer_addition().to_wake.load(Ordering::SeqCst), EMPTY); - let ptr = unsafe { token.to_raw() }; - self.queue.producer_addition().to_wake.store(ptr, Ordering::SeqCst); - - let steals = unsafe { ptr::replace(self.queue.consumer_addition().steals.get(), 0) }; - - match self.queue.producer_addition().cnt.fetch_sub(1 + steals, Ordering::SeqCst) { - DISCONNECTED => { - self.queue.producer_addition().cnt.store(DISCONNECTED, Ordering::SeqCst); - } - // If we factor in our steals and notice that the channel has no - // data, we successfully sleep - n => { - assert!(n >= 0); - if n - steals <= 0 { - return Ok(()); - } - } - } - - self.queue.producer_addition().to_wake.store(EMPTY, Ordering::SeqCst); - Err(unsafe { SignalToken::from_raw(ptr) }) - } - - pub fn recv(&self, deadline: Option) -> Result> { - // Optimistic preflight check (scheduling is expensive). - match self.try_recv() { - Err(Empty) => {} - data => return data, - } - - // Welp, our channel has no data. Deschedule the current thread and - // initiate the blocking protocol. - let (wait_token, signal_token) = blocking::tokens(); - if self.decrement(signal_token).is_ok() { - if let Some(deadline) = deadline { - let timed_out = !wait_token.wait_max_until(deadline); - if timed_out { - self.abort_selection(/* was_upgrade = */ false).map_err(Upgraded)?; - } - } else { - wait_token.wait(); - } - } - - match self.try_recv() { - // Messages which actually popped from the queue shouldn't count as - // a steal, so offset the decrement here (we already have our - // "steal" factored into the channel count above). - data @ (Ok(..) | Err(Upgraded(..))) => unsafe { - *self.queue.consumer_addition().steals.get() -= 1; - data - }, - - data => data, - } - } - - pub fn try_recv(&self) -> Result> { - match self.queue.pop() { - // If we stole some data, record to that effect (this will be - // factored into cnt later on). - // - // Note that we don't allow steals to grow without bound in order to - // prevent eventual overflow of either steals or cnt as an overflow - // would have catastrophic results. Sometimes, steals > cnt, but - // other times cnt > steals, so we don't know the relation between - // steals and cnt. This code path is executed only rarely, so we do - // a pretty slow operation, of swapping 0 into cnt, taking steals - // down as much as possible (without going negative), and then - // adding back in whatever we couldn't factor into steals. - Some(data) => unsafe { - if *self.queue.consumer_addition().steals.get() > MAX_STEALS { - match self.queue.producer_addition().cnt.swap(0, Ordering::SeqCst) { - DISCONNECTED => { - self.queue - .producer_addition() - .cnt - .store(DISCONNECTED, Ordering::SeqCst); - } - n => { - let m = cmp::min(n, *self.queue.consumer_addition().steals.get()); - *self.queue.consumer_addition().steals.get() -= m; - self.bump(n - m); - } - } - assert!(*self.queue.consumer_addition().steals.get() >= 0); - } - *self.queue.consumer_addition().steals.get() += 1; - match data { - Data(t) => Ok(t), - GoUp(up) => Err(Upgraded(up)), - } - }, - - None => { - match self.queue.producer_addition().cnt.load(Ordering::SeqCst) { - n if n != DISCONNECTED => Err(Empty), - - // This is a little bit of a tricky case. We failed to pop - // data above, and then we have viewed that the channel is - // disconnected. In this window more data could have been - // sent on the channel. It doesn't really make sense to - // return that the channel is disconnected when there's - // actually data on it, so be extra sure there's no data by - // popping one more time. - // - // We can ignore steals because the other end is - // disconnected and we'll never need to really factor in our - // steals again. - _ => match self.queue.pop() { - Some(Data(t)) => Ok(t), - Some(GoUp(up)) => Err(Upgraded(up)), - None => Err(Disconnected), - }, - } - } - } - } - - pub fn drop_chan(&self) { - // Dropping a channel is pretty simple, we just flag it as disconnected - // and then wakeup a blocker if there is one. - match self.queue.producer_addition().cnt.swap(DISCONNECTED, Ordering::SeqCst) { - -1 => { - self.take_to_wake().signal(); - } - DISCONNECTED => {} - n => { - assert!(n >= 0); - } - } - } - - pub fn drop_port(&self) { - // Dropping a port seems like a fairly trivial thing. In theory all we - // need to do is flag that we're disconnected and then everything else - // can take over (we don't have anyone to wake up). - // - // The catch for Ports is that we want to drop the entire contents of - // the queue. There are multiple reasons for having this property, the - // largest of which is that if another chan is waiting in this channel - // (but not received yet), then waiting on that port will cause a - // deadlock. - // - // So if we accept that we must now destroy the entire contents of the - // queue, this code may make a bit more sense. The tricky part is that - // we can't let any in-flight sends go un-dropped, we have to make sure - // *everything* is dropped and nothing new will come onto the channel. - - // The first thing we do is set a flag saying that we're done for. All - // sends are gated on this flag, so we're immediately guaranteed that - // there are a bounded number of active sends that we'll have to deal - // with. - self.queue.producer_addition().port_dropped.store(true, Ordering::SeqCst); - - // Now that we're guaranteed to deal with a bounded number of senders, - // we need to drain the queue. This draining process happens atomically - // with respect to the "count" of the channel. If the count is nonzero - // (with steals taken into account), then there must be data on the - // channel. In this case we drain everything and then try again. We will - // continue to fail while active senders send data while we're dropping - // data, but eventually we're guaranteed to break out of this loop - // (because there is a bounded number of senders). - let mut steals = unsafe { *self.queue.consumer_addition().steals.get() }; - while { - match self.queue.producer_addition().cnt.compare_exchange( - steals, - DISCONNECTED, - Ordering::SeqCst, - Ordering::SeqCst, - ) { - Ok(_) => false, - Err(old) => old != DISCONNECTED, - } - } { - while self.queue.pop().is_some() { - steals += 1; - } - } - - // At this point in time, we have gated all future senders from sending, - // and we have flagged the channel as being disconnected. The senders - // still have some responsibility, however, because some sends might not - // complete until after we flag the disconnection. There are more - // details in the sending methods that see DISCONNECTED - } - - //////////////////////////////////////////////////////////////////////////// - // select implementation - //////////////////////////////////////////////////////////////////////////// - - // increment the count on the channel (used for selection) - fn bump(&self, amt: isize) -> isize { - match self.queue.producer_addition().cnt.fetch_add(amt, Ordering::SeqCst) { - DISCONNECTED => { - self.queue.producer_addition().cnt.store(DISCONNECTED, Ordering::SeqCst); - DISCONNECTED - } - n => n, - } - } - - // Removes a previous thread from being blocked in this port - pub fn abort_selection(&self, was_upgrade: bool) -> Result> { - // If we're aborting selection after upgrading from a oneshot, then - // we're guarantee that no one is waiting. The only way that we could - // have seen the upgrade is if data was actually sent on the channel - // half again. For us, this means that there is guaranteed to be data on - // this channel. Furthermore, we're guaranteed that there was no - // start_selection previously, so there's no need to modify `self.cnt` - // at all. - // - // Hence, because of these invariants, we immediately return `Ok(true)`. - // Note that the data might not actually be sent on the channel just yet. - // The other end could have flagged the upgrade but not sent data to - // this end. This is fine because we know it's a small bounded windows - // of time until the data is actually sent. - if was_upgrade { - assert_eq!(unsafe { *self.queue.consumer_addition().steals.get() }, 0); - assert_eq!(self.queue.producer_addition().to_wake.load(Ordering::SeqCst), EMPTY); - return Ok(true); - } - - // We want to make sure that the count on the channel goes non-negative, - // and in the stream case we can have at most one steal, so just assume - // that we had one steal. - let steals = 1; - let prev = self.bump(steals + 1); - - // If we were previously disconnected, then we know for sure that there - // is no thread in to_wake, so just keep going - let has_data = if prev == DISCONNECTED { - assert_eq!(self.queue.producer_addition().to_wake.load(Ordering::SeqCst), EMPTY); - true // there is data, that data is that we're disconnected - } else { - let cur = prev + steals + 1; - assert!(cur >= 0); - - // If the previous count was negative, then we just made things go - // positive, hence we passed the -1 boundary and we're responsible - // for removing the to_wake() field and trashing it. - // - // If the previous count was positive then we're in a tougher - // situation. A possible race is that a sender just incremented - // through -1 (meaning it's going to try to wake a thread up), but it - // hasn't yet read the to_wake. In order to prevent a future recv() - // from waking up too early (this sender picking up the plastered - // over to_wake), we spin loop here waiting for to_wake to be 0. - // Note that this entire select() implementation needs an overhaul, - // and this is *not* the worst part of it, so this is not done as a - // final solution but rather out of necessity for now to get - // something working. - if prev < 0 { - drop(self.take_to_wake()); - } else { - while self.queue.producer_addition().to_wake.load(Ordering::SeqCst) != EMPTY { - thread::yield_now(); - } - } - unsafe { - assert_eq!(*self.queue.consumer_addition().steals.get(), 0); - *self.queue.consumer_addition().steals.get() = steals; - } - - // if we were previously positive, then there's surely data to - // receive - prev >= 0 - }; - - // Now that we've determined that this queue "has data", we peek at the - // queue to see if the data is an upgrade or not. If it's an upgrade, - // then we need to destroy this port and abort selection on the - // upgraded port. - if has_data { - match self.queue.peek() { - Some(&mut GoUp(..)) => match self.queue.pop() { - Some(GoUp(port)) => Err(port), - _ => unreachable!(), - }, - _ => Ok(true), - } - } else { - Ok(false) - } - } -} - -impl Drop for Packet { - fn drop(&mut self) { - // Note that this load is not only an assert for correctness about - // disconnection, but also a proper fence before the read of - // `to_wake`, so this assert cannot be removed with also removing - // the `to_wake` assert. - assert_eq!(self.queue.producer_addition().cnt.load(Ordering::SeqCst), DISCONNECTED); - assert_eq!(self.queue.producer_addition().to_wake.load(Ordering::SeqCst), EMPTY); - } -} diff --git a/library/std/src/sync/mpsc/sync.rs b/library/std/src/sync/mpsc/sync.rs deleted file mode 100644 index 733761671a04..000000000000 --- a/library/std/src/sync/mpsc/sync.rs +++ /dev/null @@ -1,495 +0,0 @@ -use self::Blocker::*; -/// Synchronous channels/ports -/// -/// This channel implementation differs significantly from the asynchronous -/// implementations found next to it (oneshot/stream/share). This is an -/// implementation of a synchronous, bounded buffer channel. -/// -/// Each channel is created with some amount of backing buffer, and sends will -/// *block* until buffer space becomes available. A buffer size of 0 is valid, -/// which means that every successful send is paired with a successful recv. -/// -/// This flavor of channels defines a new `send_opt` method for channels which -/// is the method by which a message is sent but the thread does not panic if it -/// cannot be delivered. -/// -/// Another major difference is that send() will *always* return back the data -/// if it couldn't be sent. This is because it is deterministically known when -/// the data is received and when it is not received. -/// -/// Implementation-wise, it can all be summed up with "use a mutex plus some -/// logic". The mutex used here is an OS native mutex, meaning that no user code -/// is run inside of the mutex (to prevent context switching). This -/// implementation shares almost all code for the buffered and unbuffered cases -/// of a synchronous channel. There are a few branches for the unbuffered case, -/// but they're mostly just relevant to blocking senders. -pub use self::Failure::*; - -use core::intrinsics::abort; -use core::mem; -use core::ptr; - -use crate::sync::atomic::{AtomicUsize, Ordering}; -use crate::sync::mpsc::blocking::{self, SignalToken, WaitToken}; -use crate::sync::{Mutex, MutexGuard}; -use crate::time::Instant; - -const MAX_REFCOUNT: usize = (isize::MAX) as usize; - -pub struct Packet { - /// Only field outside of the mutex. Just done for kicks, but mainly because - /// the other shared channel already had the code implemented - channels: AtomicUsize, - - lock: Mutex>, -} - -unsafe impl Send for Packet {} - -unsafe impl Sync for Packet {} - -struct State { - disconnected: bool, // Is the channel disconnected yet? - queue: Queue, // queue of senders waiting to send data - blocker: Blocker, // currently blocked thread on this channel - buf: Buffer, // storage for buffered messages - cap: usize, // capacity of this channel - - /// A curious flag used to indicate whether a sender failed or succeeded in - /// blocking. This is used to transmit information back to the thread that it - /// must dequeue its message from the buffer because it was not received. - /// This is only relevant in the 0-buffer case. This obviously cannot be - /// safely constructed, but it's guaranteed to always have a valid pointer - /// value. - canceled: Option<&'static mut bool>, -} - -unsafe impl Send for State {} - -/// Possible flavors of threads who can be blocked on this channel. -enum Blocker { - BlockedSender(SignalToken), - BlockedReceiver(SignalToken), - NoneBlocked, -} - -/// Simple queue for threading threads together. Nodes are stack-allocated, so -/// this structure is not safe at all -struct Queue { - head: *mut Node, - tail: *mut Node, -} - -struct Node { - token: Option, - next: *mut Node, -} - -unsafe impl Send for Node {} - -/// A simple ring-buffer -struct Buffer { - buf: Vec>, - start: usize, - size: usize, -} - -#[derive(Debug)] -pub enum Failure { - Empty, - Disconnected, -} - -/// Atomically blocks the current thread, placing it into `slot`, unlocking `lock` -/// in the meantime. This re-locks the mutex upon returning. -fn wait<'a, 'b, T>( - lock: &'a Mutex>, - mut guard: MutexGuard<'b, State>, - f: fn(SignalToken) -> Blocker, -) -> MutexGuard<'a, State> { - let (wait_token, signal_token) = blocking::tokens(); - match mem::replace(&mut guard.blocker, f(signal_token)) { - NoneBlocked => {} - _ => unreachable!(), - } - drop(guard); // unlock - wait_token.wait(); // block - lock.lock().unwrap() // relock -} - -/// Same as wait, but waiting at most until `deadline`. -fn wait_timeout_receiver<'a, 'b, T>( - lock: &'a Mutex>, - deadline: Instant, - mut guard: MutexGuard<'b, State>, - success: &mut bool, -) -> MutexGuard<'a, State> { - let (wait_token, signal_token) = blocking::tokens(); - match mem::replace(&mut guard.blocker, BlockedReceiver(signal_token)) { - NoneBlocked => {} - _ => unreachable!(), - } - drop(guard); // unlock - *success = wait_token.wait_max_until(deadline); // block - let mut new_guard = lock.lock().unwrap(); // relock - if !*success { - abort_selection(&mut new_guard); - } - new_guard -} - -fn abort_selection(guard: &mut MutexGuard<'_, State>) -> bool { - match mem::replace(&mut guard.blocker, NoneBlocked) { - NoneBlocked => true, - BlockedSender(token) => { - guard.blocker = BlockedSender(token); - true - } - BlockedReceiver(token) => { - drop(token); - false - } - } -} - -/// Wakes up a thread, dropping the lock at the correct time -fn wakeup(token: SignalToken, guard: MutexGuard<'_, State>) { - // We need to be careful to wake up the waiting thread *outside* of the mutex - // in case it incurs a context switch. - drop(guard); - token.signal(); -} - -impl Packet { - pub fn new(capacity: usize) -> Packet { - Packet { - channels: AtomicUsize::new(1), - lock: Mutex::new(State { - disconnected: false, - blocker: NoneBlocked, - cap: capacity, - canceled: None, - queue: Queue { head: ptr::null_mut(), tail: ptr::null_mut() }, - buf: Buffer { - buf: (0..capacity + if capacity == 0 { 1 } else { 0 }).map(|_| None).collect(), - start: 0, - size: 0, - }, - }), - } - } - - // wait until a send slot is available, returning locked access to - // the channel state. - fn acquire_send_slot(&self) -> MutexGuard<'_, State> { - let mut node = Node { token: None, next: ptr::null_mut() }; - loop { - let mut guard = self.lock.lock().unwrap(); - // are we ready to go? - if guard.disconnected || guard.buf.size() < guard.buf.capacity() { - return guard; - } - // no room; actually block - let wait_token = guard.queue.enqueue(&mut node); - drop(guard); - wait_token.wait(); - } - } - - pub fn send(&self, t: T) -> Result<(), T> { - let mut guard = self.acquire_send_slot(); - if guard.disconnected { - return Err(t); - } - guard.buf.enqueue(t); - - match mem::replace(&mut guard.blocker, NoneBlocked) { - // if our capacity is 0, then we need to wait for a receiver to be - // available to take our data. After waiting, we check again to make - // sure the port didn't go away in the meantime. If it did, we need - // to hand back our data. - NoneBlocked if guard.cap == 0 => { - let mut canceled = false; - assert!(guard.canceled.is_none()); - guard.canceled = Some(unsafe { mem::transmute(&mut canceled) }); - let mut guard = wait(&self.lock, guard, BlockedSender); - if canceled { Err(guard.buf.dequeue()) } else { Ok(()) } - } - - // success, we buffered some data - NoneBlocked => Ok(()), - - // success, someone's about to receive our buffered data. - BlockedReceiver(token) => { - wakeup(token, guard); - Ok(()) - } - - BlockedSender(..) => panic!("lolwut"), - } - } - - pub fn try_send(&self, t: T) -> Result<(), super::TrySendError> { - let mut guard = self.lock.lock().unwrap(); - if guard.disconnected { - Err(super::TrySendError::Disconnected(t)) - } else if guard.buf.size() == guard.buf.capacity() { - Err(super::TrySendError::Full(t)) - } else if guard.cap == 0 { - // With capacity 0, even though we have buffer space we can't - // transfer the data unless there's a receiver waiting. - match mem::replace(&mut guard.blocker, NoneBlocked) { - NoneBlocked => Err(super::TrySendError::Full(t)), - BlockedSender(..) => unreachable!(), - BlockedReceiver(token) => { - guard.buf.enqueue(t); - wakeup(token, guard); - Ok(()) - } - } - } else { - // If the buffer has some space and the capacity isn't 0, then we - // just enqueue the data for later retrieval, ensuring to wake up - // any blocked receiver if there is one. - assert!(guard.buf.size() < guard.buf.capacity()); - guard.buf.enqueue(t); - match mem::replace(&mut guard.blocker, NoneBlocked) { - BlockedReceiver(token) => wakeup(token, guard), - NoneBlocked => {} - BlockedSender(..) => unreachable!(), - } - Ok(()) - } - } - - // Receives a message from this channel - // - // When reading this, remember that there can only ever be one receiver at - // time. - pub fn recv(&self, deadline: Option) -> Result { - let mut guard = self.lock.lock().unwrap(); - - let mut woke_up_after_waiting = false; - // Wait for the buffer to have something in it. No need for a - // while loop because we're the only receiver. - if !guard.disconnected && guard.buf.size() == 0 { - if let Some(deadline) = deadline { - guard = - wait_timeout_receiver(&self.lock, deadline, guard, &mut woke_up_after_waiting); - } else { - guard = wait(&self.lock, guard, BlockedReceiver); - woke_up_after_waiting = true; - } - } - - // N.B., channel could be disconnected while waiting, so the order of - // these conditionals is important. - if guard.disconnected && guard.buf.size() == 0 { - return Err(Disconnected); - } - - // Pick up the data, wake up our neighbors, and carry on - assert!(guard.buf.size() > 0 || (deadline.is_some() && !woke_up_after_waiting)); - - if guard.buf.size() == 0 { - return Err(Empty); - } - - let ret = guard.buf.dequeue(); - self.wakeup_senders(woke_up_after_waiting, guard); - Ok(ret) - } - - pub fn try_recv(&self) -> Result { - let mut guard = self.lock.lock().unwrap(); - - // Easy cases first - if guard.disconnected && guard.buf.size() == 0 { - return Err(Disconnected); - } - if guard.buf.size() == 0 { - return Err(Empty); - } - - // Be sure to wake up neighbors - let ret = Ok(guard.buf.dequeue()); - self.wakeup_senders(false, guard); - ret - } - - // Wake up pending senders after some data has been received - // - // * `waited` - flag if the receiver blocked to receive some data, or if it - // just picked up some data on the way out - // * `guard` - the lock guard that is held over this channel's lock - fn wakeup_senders(&self, waited: bool, mut guard: MutexGuard<'_, State>) { - let pending_sender1: Option = guard.queue.dequeue(); - - // If this is a no-buffer channel (cap == 0), then if we didn't wait we - // need to ACK the sender. If we waited, then the sender waking us up - // was already the ACK. - let pending_sender2 = if guard.cap == 0 && !waited { - match mem::replace(&mut guard.blocker, NoneBlocked) { - NoneBlocked => None, - BlockedReceiver(..) => unreachable!(), - BlockedSender(token) => { - guard.canceled.take(); - Some(token) - } - } - } else { - None - }; - mem::drop(guard); - - // only outside of the lock do we wake up the pending threads - if let Some(token) = pending_sender1 { - token.signal(); - } - if let Some(token) = pending_sender2 { - token.signal(); - } - } - - // Prepares this shared packet for a channel clone, essentially just bumping - // a refcount. - pub fn clone_chan(&self) { - let old_count = self.channels.fetch_add(1, Ordering::SeqCst); - - // See comments on Arc::clone() on why we do this (for `mem::forget`). - if old_count > MAX_REFCOUNT { - abort(); - } - } - - pub fn drop_chan(&self) { - // Only flag the channel as disconnected if we're the last channel - match self.channels.fetch_sub(1, Ordering::SeqCst) { - 1 => {} - _ => return, - } - - // Not much to do other than wake up a receiver if one's there - let mut guard = self.lock.lock().unwrap(); - if guard.disconnected { - return; - } - guard.disconnected = true; - match mem::replace(&mut guard.blocker, NoneBlocked) { - NoneBlocked => {} - BlockedSender(..) => unreachable!(), - BlockedReceiver(token) => wakeup(token, guard), - } - } - - pub fn drop_port(&self) { - let mut guard = self.lock.lock().unwrap(); - - if guard.disconnected { - return; - } - guard.disconnected = true; - - // If the capacity is 0, then the sender may want its data back after - // we're disconnected. Otherwise it's now our responsibility to destroy - // the buffered data. As with many other portions of this code, this - // needs to be careful to destroy the data *outside* of the lock to - // prevent deadlock. - let _data = if guard.cap != 0 { mem::take(&mut guard.buf.buf) } else { Vec::new() }; - let mut queue = - mem::replace(&mut guard.queue, Queue { head: ptr::null_mut(), tail: ptr::null_mut() }); - - let waiter = match mem::replace(&mut guard.blocker, NoneBlocked) { - NoneBlocked => None, - BlockedSender(token) => { - *guard.canceled.take().unwrap() = true; - Some(token) - } - BlockedReceiver(..) => unreachable!(), - }; - mem::drop(guard); - - while let Some(token) = queue.dequeue() { - token.signal(); - } - if let Some(token) = waiter { - token.signal(); - } - } -} - -impl Drop for Packet { - fn drop(&mut self) { - assert_eq!(self.channels.load(Ordering::SeqCst), 0); - let mut guard = self.lock.lock().unwrap(); - assert!(guard.queue.dequeue().is_none()); - assert!(guard.canceled.is_none()); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Buffer, a simple ring buffer backed by Vec -//////////////////////////////////////////////////////////////////////////////// - -impl Buffer { - fn enqueue(&mut self, t: T) { - let pos = (self.start + self.size) % self.buf.len(); - self.size += 1; - let prev = mem::replace(&mut self.buf[pos], Some(t)); - assert!(prev.is_none()); - } - - fn dequeue(&mut self) -> T { - let start = self.start; - self.size -= 1; - self.start = (self.start + 1) % self.buf.len(); - let result = &mut self.buf[start]; - result.take().unwrap() - } - - fn size(&self) -> usize { - self.size - } - fn capacity(&self) -> usize { - self.buf.len() - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Queue, a simple queue to enqueue threads with (stack-allocated nodes) -//////////////////////////////////////////////////////////////////////////////// - -impl Queue { - fn enqueue(&mut self, node: &mut Node) -> WaitToken { - let (wait_token, signal_token) = blocking::tokens(); - node.token = Some(signal_token); - node.next = ptr::null_mut(); - - if self.tail.is_null() { - self.head = node as *mut Node; - self.tail = node as *mut Node; - } else { - unsafe { - (*self.tail).next = node as *mut Node; - self.tail = node as *mut Node; - } - } - - wait_token - } - - fn dequeue(&mut self) -> Option { - if self.head.is_null() { - return None; - } - let node = self.head; - self.head = unsafe { (*node).next }; - if self.head.is_null() { - self.tail = ptr::null_mut(); - } - unsafe { - (*node).next = ptr::null_mut(); - Some((*node).token.take().unwrap()) - } - } -} diff --git a/library/std/src/sync/mpsc/tests.rs b/library/std/src/sync/mpsc/tests.rs index f6d0796f604f..82c52eb4fef4 100644 --- a/library/std/src/sync/mpsc/tests.rs +++ b/library/std/src/sync/mpsc/tests.rs @@ -706,3 +706,17 @@ fn issue_32114() { let _ = tx.send(123); assert_eq!(tx.send(123), Err(SendError(123))); } + +#[test] +fn issue_39364() { + let (tx, rx) = channel::<()>(); + let t = thread::spawn(move || { + thread::sleep(Duration::from_millis(300)); + let _ = tx.clone(); + crate::mem::forget(tx); + }); + + let _ = rx.recv_timeout(Duration::from_millis(500)); + t.join().unwrap(); + let _ = rx.recv_timeout(Duration::from_millis(500)); +} diff --git a/library/std/src/sync/mutex.rs b/library/std/src/sync/mutex.rs index de851c8fbbed..065045f44206 100644 --- a/library/std/src/sync/mutex.rs +++ b/library/std/src/sync/mutex.rs @@ -5,7 +5,7 @@ use crate::cell::UnsafeCell; use crate::fmt; use crate::ops::{Deref, DerefMut}; use crate::sync::{poison, LockResult, TryLockError, TryLockResult}; -use crate::sys_common::mutex as sys; +use crate::sys::locks as sys; /// A mutual exclusion primitive useful for protecting shared data /// @@ -163,7 +163,7 @@ use crate::sys_common::mutex as sys; #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "Mutex")] pub struct Mutex { - inner: sys::MovableMutex, + inner: sys::Mutex, poison: poison::Flag, data: UnsafeCell, } @@ -217,11 +217,7 @@ impl Mutex { #[rustc_const_stable(feature = "const_locks", since = "1.63.0")] #[inline] pub const fn new(t: T) -> Mutex { - Mutex { - inner: sys::MovableMutex::new(), - poison: poison::Flag::new(), - data: UnsafeCell::new(t), - } + Mutex { inner: sys::Mutex::new(), poison: poison::Flag::new(), data: UnsafeCell::new(t) } } } @@ -264,7 +260,7 @@ impl Mutex { #[stable(feature = "rust1", since = "1.0.0")] pub fn lock(&self) -> LockResult> { unsafe { - self.inner.raw_lock(); + self.inner.lock(); MutexGuard::new(self) } } @@ -526,7 +522,7 @@ impl Drop for MutexGuard<'_, T> { fn drop(&mut self) { unsafe { self.lock.poison.done(&self.poison); - self.lock.inner.raw_unlock(); + self.lock.inner.unlock(); } } } @@ -545,7 +541,7 @@ impl fmt::Display for MutexGuard<'_, T> { } } -pub fn guard_lock<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a sys::MovableMutex { +pub fn guard_lock<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a sys::Mutex { &guard.lock.inner } diff --git a/library/std/src/sync/rwlock.rs b/library/std/src/sync/rwlock.rs index 8b387760768c..7c409cb3e977 100644 --- a/library/std/src/sync/rwlock.rs +++ b/library/std/src/sync/rwlock.rs @@ -6,7 +6,7 @@ use crate::fmt; use crate::ops::{Deref, DerefMut}; use crate::ptr::NonNull; use crate::sync::{poison, LockResult, TryLockError, TryLockResult}; -use crate::sys_common::rwlock as sys; +use crate::sys::locks as sys; /// A reader-writer lock /// @@ -78,7 +78,7 @@ use crate::sys_common::rwlock as sys; #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "RwLock")] pub struct RwLock { - inner: sys::MovableRwLock, + inner: sys::RwLock, poison: poison::Flag, data: UnsafeCell, } @@ -109,7 +109,7 @@ pub struct RwLockReadGuard<'a, T: ?Sized + 'a> { // `NonNull` is also covariant over `T`, just like we would have with `&T`. `NonNull` // is preferable over `const* T` to allow for niche optimization. data: NonNull, - inner_lock: &'a sys::MovableRwLock, + inner_lock: &'a sys::RwLock, } #[stable(feature = "rust1", since = "1.0.0")] @@ -158,11 +158,7 @@ impl RwLock { #[rustc_const_stable(feature = "const_locks", since = "1.63.0")] #[inline] pub const fn new(t: T) -> RwLock { - RwLock { - inner: sys::MovableRwLock::new(), - poison: poison::Flag::new(), - data: UnsafeCell::new(t), - } + RwLock { inner: sys::RwLock::new(), poison: poison::Flag::new(), data: UnsafeCell::new(t) } } } diff --git a/library/std/src/sys/hermit/mod.rs b/library/std/src/sys/hermit/mod.rs index e6534df8938e..6811fadb0188 100644 --- a/library/std/src/sys/hermit/mod.rs +++ b/library/std/src/sys/hermit/mod.rs @@ -51,9 +51,9 @@ pub mod locks { mod futex_condvar; mod futex_mutex; mod futex_rwlock; - pub(crate) use futex_condvar::MovableCondvar; - pub(crate) use futex_mutex::{MovableMutex, Mutex}; - pub(crate) use futex_rwlock::{MovableRwLock, RwLock}; + pub(crate) use futex_condvar::Condvar; + pub(crate) use futex_mutex::Mutex; + pub(crate) use futex_rwlock::RwLock; } use crate::io::ErrorKind; diff --git a/library/std/src/sys/itron/condvar.rs b/library/std/src/sys/itron/condvar.rs index 008cd8fb1e39..f70aa434e483 100644 --- a/library/std/src/sys/itron/condvar.rs +++ b/library/std/src/sys/itron/condvar.rs @@ -12,18 +12,13 @@ pub struct Condvar { unsafe impl Send for Condvar {} unsafe impl Sync for Condvar {} -pub type MovableCondvar = Condvar; - impl Condvar { #[inline] pub const fn new() -> Condvar { Condvar { waiters: SpinMutex::new(waiter_queue::WaiterQueue::new()) } } - #[inline] - pub unsafe fn init(&mut self) {} - - pub unsafe fn notify_one(&self) { + pub fn notify_one(&self) { self.waiters.with_locked(|waiters| { if let Some(task) = waiters.pop_front() { // Unpark the task @@ -39,7 +34,7 @@ impl Condvar { }); } - pub unsafe fn notify_all(&self) { + pub fn notify_all(&self) { self.waiters.with_locked(|waiters| { while let Some(task) = waiters.pop_front() { // Unpark the task diff --git a/library/std/src/sys/itron/mutex.rs b/library/std/src/sys/itron/mutex.rs index 085662e6d44b..f2eed8e771c4 100644 --- a/library/std/src/sys/itron/mutex.rs +++ b/library/std/src/sys/itron/mutex.rs @@ -11,8 +11,6 @@ pub struct Mutex { mtx: SpinIdOnceCell<()>, } -pub type MovableMutex = Mutex; - /// Create a mutex object. This function never panics. fn new_mtx() -> Result { ItronError::err_if_negative(unsafe { @@ -39,7 +37,7 @@ impl Mutex { } } - pub unsafe fn lock(&self) { + pub fn lock(&self) { let mtx = self.raw(); expect_success(unsafe { abi::loc_mtx(mtx) }, &"loc_mtx"); } @@ -49,7 +47,7 @@ impl Mutex { expect_success_aborting(unsafe { abi::unl_mtx(mtx) }, &"unl_mtx"); } - pub unsafe fn try_lock(&self) -> bool { + pub fn try_lock(&self) -> bool { let mtx = self.raw(); match unsafe { abi::ploc_mtx(mtx) } { abi::E_TMOUT => false, diff --git a/library/std/src/sys/sgx/condvar.rs b/library/std/src/sys/sgx/condvar.rs index 36534e0eff3f..aa1174664aeb 100644 --- a/library/std/src/sys/sgx/condvar.rs +++ b/library/std/src/sys/sgx/condvar.rs @@ -4,42 +4,43 @@ use crate::time::Duration; use super::waitqueue::{SpinMutex, WaitQueue, WaitVariable}; +/// FIXME: `UnsafeList` is not movable. +struct AllocatedCondvar(SpinMutex>); + pub struct Condvar { - inner: SpinMutex>, + inner: LazyBox, } -pub(crate) type MovableCondvar = LazyBox; - -impl LazyInit for Condvar { +impl LazyInit for AllocatedCondvar { fn init() -> Box { - Box::new(Self::new()) + Box::new(AllocatedCondvar(SpinMutex::new(WaitVariable::new(())))) } } impl Condvar { pub const fn new() -> Condvar { - Condvar { inner: SpinMutex::new(WaitVariable::new(())) } + Condvar { inner: LazyBox::new() } } #[inline] - pub unsafe fn notify_one(&self) { - let _ = WaitQueue::notify_one(self.inner.lock()); + pub fn notify_one(&self) { + let _ = WaitQueue::notify_one(self.inner.0.lock()); } #[inline] - pub unsafe fn notify_all(&self) { - let _ = WaitQueue::notify_all(self.inner.lock()); + pub fn notify_all(&self) { + let _ = WaitQueue::notify_all(self.inner.0.lock()); } pub unsafe fn wait(&self, mutex: &Mutex) { - let guard = self.inner.lock(); + let guard = self.inner.0.lock(); WaitQueue::wait(guard, || unsafe { mutex.unlock() }); - unsafe { mutex.lock() } + mutex.lock() } pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { - let success = WaitQueue::wait_timeout(&self.inner, dur, || unsafe { mutex.unlock() }); - unsafe { mutex.lock() }; + let success = WaitQueue::wait_timeout(&self.inner.0, dur, || unsafe { mutex.unlock() }); + mutex.lock(); success } } diff --git a/library/std/src/sys/sgx/mutex.rs b/library/std/src/sys/sgx/mutex.rs index aa747d56b0d3..0dbf020ebe06 100644 --- a/library/std/src/sys/sgx/mutex.rs +++ b/library/std/src/sys/sgx/mutex.rs @@ -1,28 +1,28 @@ use super::waitqueue::{try_lock_or_false, SpinMutex, WaitQueue, WaitVariable}; use crate::sys_common::lazy_box::{LazyBox, LazyInit}; +/// FIXME: `UnsafeList` is not movable. +struct AllocatedMutex(SpinMutex>); + pub struct Mutex { - inner: SpinMutex>, + inner: LazyBox, } -// not movable: see UnsafeList implementation -pub(crate) type MovableMutex = LazyBox; - -impl LazyInit for Mutex { +impl LazyInit for AllocatedMutex { fn init() -> Box { - Box::new(Self::new()) + Box::new(AllocatedMutex(SpinMutex::new(WaitVariable::new(false)))) } } // Implementation according to “Operating Systems: Three Easy Pieces”, chapter 28 impl Mutex { pub const fn new() -> Mutex { - Mutex { inner: SpinMutex::new(WaitVariable::new(false)) } + Mutex { inner: LazyBox::new() } } #[inline] - pub unsafe fn lock(&self) { - let mut guard = self.inner.lock(); + pub fn lock(&self) { + let mut guard = self.inner.0.lock(); if *guard.lock_var() { // Another thread has the lock, wait WaitQueue::wait(guard, || {}) @@ -35,7 +35,7 @@ impl Mutex { #[inline] pub unsafe fn unlock(&self) { - let guard = self.inner.lock(); + let guard = self.inner.0.lock(); if let Err(mut guard) = WaitQueue::notify_one(guard) { // No other waiters, unlock *guard.lock_var_mut() = false; @@ -45,8 +45,8 @@ impl Mutex { } #[inline] - pub unsafe fn try_lock(&self) -> bool { - let mut guard = try_lock_or_false!(self.inner); + pub fn try_lock(&self) -> bool { + let mut guard = try_lock_or_false!(self.inner.0); if *guard.lock_var() { // Another thread has the lock false diff --git a/library/std/src/sys/sgx/rwlock.rs b/library/std/src/sys/sgx/rwlock.rs index a97fb9ab026f..d89de18ca5ff 100644 --- a/library/std/src/sys/sgx/rwlock.rs +++ b/library/std/src/sys/sgx/rwlock.rs @@ -7,42 +7,45 @@ use crate::sys_common::lazy_box::{LazyBox, LazyInit}; use super::waitqueue::{ try_lock_or_false, NotifiedTcs, SpinMutex, SpinMutexGuard, WaitQueue, WaitVariable, }; -use crate::mem; +use crate::alloc::Layout; -pub struct RwLock { +struct AllocatedRwLock { readers: SpinMutex>>, writer: SpinMutex>, } -pub(crate) type MovableRwLock = LazyBox; +pub struct RwLock { + inner: LazyBox, +} -impl LazyInit for RwLock { +impl LazyInit for AllocatedRwLock { fn init() -> Box { - Box::new(Self::new()) + Box::new(AllocatedRwLock { + readers: SpinMutex::new(WaitVariable::new(None)), + writer: SpinMutex::new(WaitVariable::new(false)), + }) } } -// Check at compile time that RwLock size matches C definition (see test_c_rwlock_initializer below) -// -// # Safety -// Never called, as it is a compile time check. -#[allow(dead_code)] -unsafe fn rw_lock_size_assert(r: RwLock) { - unsafe { mem::transmute::(r) }; -} +// Check at compile time that RwLock's size and alignment matches the C definition +// in libunwind (see also `test_c_rwlock_initializer` in `tests`). +const _: () = { + let rust = Layout::new::(); + let c = Layout::new::<*mut ()>(); + assert!(rust.size() == c.size()); + assert!(rust.align() == c.align()); +}; impl RwLock { pub const fn new() -> RwLock { - RwLock { - readers: SpinMutex::new(WaitVariable::new(None)), - writer: SpinMutex::new(WaitVariable::new(false)), - } + RwLock { inner: LazyBox::new() } } #[inline] - pub unsafe fn read(&self) { - let mut rguard = self.readers.lock(); - let wguard = self.writer.lock(); + pub fn read(&self) { + let lock = &*self.inner; + let mut rguard = lock.readers.lock(); + let wguard = lock.writer.lock(); if *wguard.lock_var() || !wguard.queue_empty() { // Another thread has or is waiting for the write lock, wait drop(wguard); @@ -57,8 +60,9 @@ impl RwLock { #[inline] pub unsafe fn try_read(&self) -> bool { - let mut rguard = try_lock_or_false!(self.readers); - let wguard = try_lock_or_false!(self.writer); + let lock = &*self.inner; + let mut rguard = try_lock_or_false!(lock.readers); + let wguard = try_lock_or_false!(lock.writer); if *wguard.lock_var() || !wguard.queue_empty() { // Another thread has or is waiting for the write lock false @@ -71,9 +75,10 @@ impl RwLock { } #[inline] - pub unsafe fn write(&self) { - let rguard = self.readers.lock(); - let mut wguard = self.writer.lock(); + pub fn write(&self) { + let lock = &*self.inner; + let rguard = lock.readers.lock(); + let mut wguard = lock.writer.lock(); if *wguard.lock_var() || rguard.lock_var().is_some() { // Another thread has the lock, wait drop(rguard); @@ -86,9 +91,10 @@ impl RwLock { } #[inline] - pub unsafe fn try_write(&self) -> bool { - let rguard = try_lock_or_false!(self.readers); - let mut wguard = try_lock_or_false!(self.writer); + pub fn try_write(&self) -> bool { + let lock = &*self.inner; + let rguard = try_lock_or_false!(lock.readers); + let mut wguard = try_lock_or_false!(lock.writer); if *wguard.lock_var() || rguard.lock_var().is_some() { // Another thread has the lock false @@ -122,8 +128,9 @@ impl RwLock { #[inline] pub unsafe fn read_unlock(&self) { - let rguard = self.readers.lock(); - let wguard = self.writer.lock(); + let lock = &*self.inner; + let rguard = lock.readers.lock(); + let wguard = lock.writer.lock(); unsafe { self.__read_unlock(rguard, wguard) }; } @@ -158,8 +165,9 @@ impl RwLock { #[inline] pub unsafe fn write_unlock(&self) { - let rguard = self.readers.lock(); - let wguard = self.writer.lock(); + let lock = &*self.inner; + let rguard = lock.readers.lock(); + let wguard = lock.writer.lock(); unsafe { self.__write_unlock(rguard, wguard) }; } @@ -167,8 +175,9 @@ impl RwLock { #[inline] #[cfg_attr(test, allow(dead_code))] unsafe fn unlock(&self) { - let rguard = self.readers.lock(); - let wguard = self.writer.lock(); + let lock = &*self.inner; + let rguard = lock.readers.lock(); + let wguard = lock.writer.lock(); if *wguard.lock_var() == true { unsafe { self.__write_unlock(rguard, wguard) }; } else { @@ -201,6 +210,7 @@ pub unsafe extern "C" fn __rust_rwlock_wrlock(p: *mut RwLock) -> i32 { unsafe { (*p).write() }; return 0; } + #[cfg(not(test))] #[no_mangle] pub unsafe extern "C" fn __rust_rwlock_unlock(p: *mut RwLock) -> i32 { diff --git a/library/std/src/sys/sgx/rwlock/tests.rs b/library/std/src/sys/sgx/rwlock/tests.rs index 4799961154a4..5fd6670afd43 100644 --- a/library/std/src/sys/sgx/rwlock/tests.rs +++ b/library/std/src/sys/sgx/rwlock/tests.rs @@ -1,22 +1,12 @@ use super::*; +use crate::ptr; // Verify that the byte pattern libunwind uses to initialize an RwLock is // equivalent to the value of RwLock::new(). If the value changes, // `src/UnwindRustSgx.h` in libunwind needs to be changed too. #[test] fn test_c_rwlock_initializer() { - #[rustfmt::skip] - const C_RWLOCK_INIT: &[u8] = &[ - /* 0x00 */ 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - /* 0x10 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - /* 0x20 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - /* 0x30 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - /* 0x40 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - /* 0x50 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - /* 0x60 */ 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - /* 0x70 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - /* 0x80 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - ]; + const C_RWLOCK_INIT: *mut () = ptr::null_mut(); // For the test to work, we need the padding/unused bytes in RwLock to be // initialized as 0. In practice, this is the case with statics. @@ -26,6 +16,6 @@ fn test_c_rwlock_initializer() { // If the assertion fails, that not necessarily an issue with the value // of C_RWLOCK_INIT. It might just be an issue with the way padding // bytes are initialized in the test code. - assert_eq!(&crate::mem::transmute_copy::<_, [u8; 144]>(&RUST_RWLOCK_INIT), C_RWLOCK_INIT); + assert_eq!(crate::mem::transmute_copy::<_, *mut ()>(&RUST_RWLOCK_INIT), C_RWLOCK_INIT); }; } diff --git a/library/std/src/sys/solid/rwlock.rs b/library/std/src/sys/solid/rwlock.rs index 0a770cf03f2f..ecb4eb83b9b0 100644 --- a/library/std/src/sys/solid/rwlock.rs +++ b/library/std/src/sys/solid/rwlock.rs @@ -12,8 +12,6 @@ pub struct RwLock { rwl: SpinIdOnceCell<()>, } -pub type MovableRwLock = RwLock; - // Safety: `num_readers` is protected by `mtx_num_readers` unsafe impl Send for RwLock {} unsafe impl Sync for RwLock {} @@ -37,13 +35,13 @@ impl RwLock { } #[inline] - pub unsafe fn read(&self) { + pub fn read(&self) { let rwl = self.raw(); expect_success(unsafe { abi::rwl_loc_rdl(rwl) }, &"rwl_loc_rdl"); } #[inline] - pub unsafe fn try_read(&self) -> bool { + pub fn try_read(&self) -> bool { let rwl = self.raw(); match unsafe { abi::rwl_ploc_rdl(rwl) } { abi::E_TMOUT => false, @@ -55,13 +53,13 @@ impl RwLock { } #[inline] - pub unsafe fn write(&self) { + pub fn write(&self) { let rwl = self.raw(); expect_success(unsafe { abi::rwl_loc_wrl(rwl) }, &"rwl_loc_wrl"); } #[inline] - pub unsafe fn try_write(&self) -> bool { + pub fn try_write(&self) -> bool { let rwl = self.raw(); match unsafe { abi::rwl_ploc_wrl(rwl) } { abi::E_TMOUT => false, diff --git a/library/std/src/sys/unix/locks/fuchsia_mutex.rs b/library/std/src/sys/unix/locks/fuchsia_mutex.rs index 117611ce43f2..5d89e5a13fd3 100644 --- a/library/std/src/sys/unix/locks/fuchsia_mutex.rs +++ b/library/std/src/sys/unix/locks/fuchsia_mutex.rs @@ -53,8 +53,6 @@ const CONTESTED_BIT: u32 = 1; // This can never be a valid `zx_handle_t`. const UNLOCKED: u32 = 0; -pub type MovableMutex = Mutex; - pub struct Mutex { futex: AtomicU32, } @@ -86,23 +84,27 @@ impl Mutex { } #[inline] - pub unsafe fn try_lock(&self) -> bool { - let thread_self = zx_thread_self(); + pub fn try_lock(&self) -> bool { + let thread_self = unsafe { zx_thread_self() }; self.futex.compare_exchange(UNLOCKED, to_state(thread_self), Acquire, Relaxed).is_ok() } #[inline] - pub unsafe fn lock(&self) { - let thread_self = zx_thread_self(); + pub fn lock(&self) { + let thread_self = unsafe { zx_thread_self() }; if let Err(state) = self.futex.compare_exchange(UNLOCKED, to_state(thread_self), Acquire, Relaxed) { - self.lock_contested(state, thread_self); + unsafe { + self.lock_contested(state, thread_self); + } } } + /// # Safety + /// `thread_self` must be the handle for the current thread. #[cold] - fn lock_contested(&self, mut state: u32, thread_self: zx_handle_t) { + unsafe fn lock_contested(&self, mut state: u32, thread_self: zx_handle_t) { let owned_state = mark_contested(to_state(thread_self)); loop { // Mark the mutex as contested if it is not already. diff --git a/library/std/src/sys/unix/locks/futex_condvar.rs b/library/std/src/sys/unix/locks/futex_condvar.rs index c0576c17880e..4bd65dd25c29 100644 --- a/library/std/src/sys/unix/locks/futex_condvar.rs +++ b/library/std/src/sys/unix/locks/futex_condvar.rs @@ -3,8 +3,6 @@ use crate::sync::atomic::{AtomicU32, Ordering::Relaxed}; use crate::sys::futex::{futex_wait, futex_wake, futex_wake_all}; use crate::time::Duration; -pub type MovableCondvar = Condvar; - pub struct Condvar { // The value of this atomic is simply incremented on every notification. // This is used by `.wait()` to not miss any notifications after @@ -21,12 +19,12 @@ impl Condvar { // All the memory orderings here are `Relaxed`, // because synchronization is done by unlocking and locking the mutex. - pub unsafe fn notify_one(&self) { + pub fn notify_one(&self) { self.futex.fetch_add(1, Relaxed); futex_wake(&self.futex); } - pub unsafe fn notify_all(&self) { + pub fn notify_all(&self) { self.futex.fetch_add(1, Relaxed); futex_wake_all(&self.futex); } diff --git a/library/std/src/sys/unix/locks/futex_mutex.rs b/library/std/src/sys/unix/locks/futex_mutex.rs index 33b13dad4d65..c01229586c30 100644 --- a/library/std/src/sys/unix/locks/futex_mutex.rs +++ b/library/std/src/sys/unix/locks/futex_mutex.rs @@ -4,8 +4,6 @@ use crate::sync::atomic::{ }; use crate::sys::futex::{futex_wait, futex_wake}; -pub type MovableMutex = Mutex; - pub struct Mutex { /// 0: unlocked /// 1: locked, no other threads waiting @@ -20,12 +18,12 @@ impl Mutex { } #[inline] - pub unsafe fn try_lock(&self) -> bool { + pub fn try_lock(&self) -> bool { self.futex.compare_exchange(0, 1, Acquire, Relaxed).is_ok() } #[inline] - pub unsafe fn lock(&self) { + pub fn lock(&self) { if self.futex.compare_exchange(0, 1, Acquire, Relaxed).is_err() { self.lock_contended(); } diff --git a/library/std/src/sys/unix/locks/futex_rwlock.rs b/library/std/src/sys/unix/locks/futex_rwlock.rs index 0cc92244ecad..aa0de900238f 100644 --- a/library/std/src/sys/unix/locks/futex_rwlock.rs +++ b/library/std/src/sys/unix/locks/futex_rwlock.rs @@ -4,8 +4,6 @@ use crate::sync::atomic::{ }; use crate::sys::futex::{futex_wait, futex_wake, futex_wake_all}; -pub type MovableRwLock = RwLock; - pub struct RwLock { // The state consists of a 30-bit reader counter, a 'readers waiting' flag, and a 'writers waiting' flag. // Bits 0..30: @@ -70,14 +68,14 @@ impl RwLock { } #[inline] - pub unsafe fn try_read(&self) -> bool { + pub fn try_read(&self) -> bool { self.state .fetch_update(Acquire, Relaxed, |s| is_read_lockable(s).then(|| s + READ_LOCKED)) .is_ok() } #[inline] - pub unsafe fn read(&self) { + pub fn read(&self) { let state = self.state.load(Relaxed); if !is_read_lockable(state) || self @@ -144,14 +142,14 @@ impl RwLock { } #[inline] - pub unsafe fn try_write(&self) -> bool { + pub fn try_write(&self) -> bool { self.state .fetch_update(Acquire, Relaxed, |s| is_unlocked(s).then(|| s + WRITE_LOCKED)) .is_ok() } #[inline] - pub unsafe fn write(&self) { + pub fn write(&self) { if self.state.compare_exchange_weak(0, WRITE_LOCKED, Acquire, Relaxed).is_err() { self.write_contended(); } diff --git a/library/std/src/sys/unix/locks/mod.rs b/library/std/src/sys/unix/locks/mod.rs index 9bb314b7010a..b2e0e49ad736 100644 --- a/library/std/src/sys/unix/locks/mod.rs +++ b/library/std/src/sys/unix/locks/mod.rs @@ -10,22 +10,22 @@ cfg_if::cfg_if! { mod futex_mutex; mod futex_rwlock; mod futex_condvar; - pub(crate) use futex_mutex::{Mutex, MovableMutex}; - pub(crate) use futex_rwlock::MovableRwLock; - pub(crate) use futex_condvar::MovableCondvar; + pub(crate) use futex_mutex::Mutex; + pub(crate) use futex_rwlock::RwLock; + pub(crate) use futex_condvar::Condvar; } else if #[cfg(target_os = "fuchsia")] { mod fuchsia_mutex; mod futex_rwlock; mod futex_condvar; - pub(crate) use fuchsia_mutex::{Mutex, MovableMutex}; - pub(crate) use futex_rwlock::MovableRwLock; - pub(crate) use futex_condvar::MovableCondvar; + pub(crate) use fuchsia_mutex::Mutex; + pub(crate) use futex_rwlock::RwLock; + pub(crate) use futex_condvar::Condvar; } else { mod pthread_mutex; mod pthread_rwlock; mod pthread_condvar; - pub(crate) use pthread_mutex::{Mutex, MovableMutex}; - pub(crate) use pthread_rwlock::MovableRwLock; - pub(crate) use pthread_condvar::MovableCondvar; + pub(crate) use pthread_mutex::Mutex; + pub(crate) use pthread_rwlock::RwLock; + pub(crate) use pthread_condvar::Condvar; } } diff --git a/library/std/src/sys/unix/locks/pthread_condvar.rs b/library/std/src/sys/unix/locks/pthread_condvar.rs index 4741c0c6736e..1ddb09905db2 100644 --- a/library/std/src/sys/unix/locks/pthread_condvar.rs +++ b/library/std/src/sys/unix/locks/pthread_condvar.rs @@ -1,17 +1,17 @@ use crate::cell::UnsafeCell; +use crate::ptr; +use crate::sync::atomic::{AtomicPtr, Ordering::Relaxed}; use crate::sys::locks::{pthread_mutex, Mutex}; use crate::sys_common::lazy_box::{LazyBox, LazyInit}; use crate::time::Duration; +struct AllocatedCondvar(UnsafeCell); + pub struct Condvar { - inner: UnsafeCell, + inner: LazyBox, + mutex: AtomicPtr, } -pub(crate) type MovableCondvar = LazyBox; - -unsafe impl Send for Condvar {} -unsafe impl Sync for Condvar {} - const TIMESPEC_MAX: libc::timespec = libc::timespec { tv_sec: ::MAX, tv_nsec: 1_000_000_000 - 1 }; @@ -19,81 +19,104 @@ fn saturating_cast_to_time_t(value: u64) -> libc::time_t { if value > ::MAX as u64 { ::MAX } else { value as libc::time_t } } -impl LazyInit for Condvar { +#[inline] +fn raw(c: &Condvar) -> *mut libc::pthread_cond_t { + c.inner.0.get() +} + +unsafe impl Send for AllocatedCondvar {} +unsafe impl Sync for AllocatedCondvar {} + +impl LazyInit for AllocatedCondvar { fn init() -> Box { - let mut condvar = Box::new(Self::new()); - unsafe { condvar.init() }; + let condvar = Box::new(AllocatedCondvar(UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER))); + + cfg_if::cfg_if! { + if #[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "watchos", + target_os = "l4re", + target_os = "android", + target_os = "redox" + ))] { + // `pthread_condattr_setclock` is unfortunately not supported on these platforms. + } else if #[cfg(any(target_os = "espidf", target_os = "horizon"))] { + // NOTE: ESP-IDF's PTHREAD_COND_INITIALIZER support is not released yet + // So on that platform, init() should always be called + // Moreover, that platform does not have pthread_condattr_setclock support, + // hence that initialization should be skipped as well + // + // Similar story for the 3DS (horizon). + let r = unsafe { libc::pthread_cond_init(condvar.0.get(), crate::ptr::null()) }; + assert_eq!(r, 0); + } else { + use crate::mem::MaybeUninit; + let mut attr = MaybeUninit::::uninit(); + let r = unsafe { libc::pthread_condattr_init(attr.as_mut_ptr()) }; + assert_eq!(r, 0); + let r = unsafe { libc::pthread_condattr_setclock(attr.as_mut_ptr(), libc::CLOCK_MONOTONIC) }; + assert_eq!(r, 0); + let r = unsafe { libc::pthread_cond_init(condvar.0.get(), attr.as_ptr()) }; + assert_eq!(r, 0); + let r = unsafe { libc::pthread_condattr_destroy(attr.as_mut_ptr()) }; + assert_eq!(r, 0); + } + } + condvar } } +impl Drop for AllocatedCondvar { + #[inline] + fn drop(&mut self) { + let r = unsafe { libc::pthread_cond_destroy(self.0.get()) }; + if cfg!(target_os = "dragonfly") { + // On DragonFly pthread_cond_destroy() returns EINVAL if called on + // a condvar that was just initialized with + // libc::PTHREAD_COND_INITIALIZER. Once it is used or + // pthread_cond_init() is called, this behaviour no longer occurs. + debug_assert!(r == 0 || r == libc::EINVAL); + } else { + debug_assert_eq!(r, 0); + } + } +} + impl Condvar { pub const fn new() -> Condvar { - // Might be moved and address is changing it is better to avoid - // initialization of potentially opaque OS data before it landed - Condvar { inner: UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER) } - } - - #[cfg(any( - target_os = "macos", - target_os = "ios", - target_os = "watchos", - target_os = "l4re", - target_os = "android", - target_os = "redox" - ))] - unsafe fn init(&mut self) {} - - // NOTE: ESP-IDF's PTHREAD_COND_INITIALIZER support is not released yet - // So on that platform, init() should always be called - // Moreover, that platform does not have pthread_condattr_setclock support, - // hence that initialization should be skipped as well - // - // Similar story for the 3DS (horizon). - #[cfg(any(target_os = "espidf", target_os = "horizon"))] - unsafe fn init(&mut self) { - let r = libc::pthread_cond_init(self.inner.get(), crate::ptr::null()); - assert_eq!(r, 0); - } - - #[cfg(not(any( - target_os = "macos", - target_os = "ios", - target_os = "watchos", - target_os = "l4re", - target_os = "android", - target_os = "redox", - target_os = "espidf", - target_os = "horizon" - )))] - unsafe fn init(&mut self) { - use crate::mem::MaybeUninit; - let mut attr = MaybeUninit::::uninit(); - let r = libc::pthread_condattr_init(attr.as_mut_ptr()); - assert_eq!(r, 0); - let r = libc::pthread_condattr_setclock(attr.as_mut_ptr(), libc::CLOCK_MONOTONIC); - assert_eq!(r, 0); - let r = libc::pthread_cond_init(self.inner.get(), attr.as_ptr()); - assert_eq!(r, 0); - let r = libc::pthread_condattr_destroy(attr.as_mut_ptr()); - assert_eq!(r, 0); + Condvar { inner: LazyBox::new(), mutex: AtomicPtr::new(ptr::null_mut()) } } #[inline] - pub unsafe fn notify_one(&self) { - let r = libc::pthread_cond_signal(self.inner.get()); + fn verify(&self, mutex: *mut libc::pthread_mutex_t) { + // Relaxed is okay here because we never read through `self.addr`, and only use it to + // compare addresses. + match self.mutex.compare_exchange(ptr::null_mut(), mutex, Relaxed, Relaxed) { + Ok(_) => {} // Stored the address + Err(n) if n == mutex => {} // Lost a race to store the same address + _ => panic!("attempted to use a condition variable with two mutexes"), + } + } + + #[inline] + pub fn notify_one(&self) { + let r = unsafe { libc::pthread_cond_signal(raw(self)) }; debug_assert_eq!(r, 0); } #[inline] - pub unsafe fn notify_all(&self) { - let r = libc::pthread_cond_broadcast(self.inner.get()); + pub fn notify_all(&self) { + let r = unsafe { libc::pthread_cond_broadcast(raw(self)) }; debug_assert_eq!(r, 0); } #[inline] pub unsafe fn wait(&self, mutex: &Mutex) { - let r = libc::pthread_cond_wait(self.inner.get(), pthread_mutex::raw(mutex)); + let mutex = pthread_mutex::raw(mutex); + self.verify(mutex); + let r = libc::pthread_cond_wait(raw(self), mutex); debug_assert_eq!(r, 0); } @@ -112,6 +135,9 @@ impl Condvar { pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { use crate::mem; + let mutex = pthread_mutex::raw(mutex); + self.verify(mutex); + let mut now: libc::timespec = mem::zeroed(); let r = libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut now); assert_eq!(r, 0); @@ -127,7 +153,7 @@ impl Condvar { let timeout = sec.map(|s| libc::timespec { tv_sec: s, tv_nsec: nsec as _ }).unwrap_or(TIMESPEC_MAX); - let r = libc::pthread_cond_timedwait(self.inner.get(), pthread_mutex::raw(mutex), &timeout); + let r = libc::pthread_cond_timedwait(raw(self), mutex, &timeout); assert!(r == libc::ETIMEDOUT || r == 0); r == 0 } @@ -144,9 +170,11 @@ impl Condvar { target_os = "horizon" ))] pub unsafe fn wait_timeout(&self, mutex: &Mutex, mut dur: Duration) -> bool { - use crate::ptr; use crate::time::Instant; + let mutex = pthread_mutex::raw(mutex); + self.verify(mutex); + // 1000 years let max_dur = Duration::from_secs(1000 * 365 * 86400); @@ -187,36 +215,11 @@ impl Condvar { .unwrap_or(TIMESPEC_MAX); // And wait! - let r = libc::pthread_cond_timedwait(self.inner.get(), pthread_mutex::raw(mutex), &timeout); + let r = libc::pthread_cond_timedwait(raw(self), mutex, &timeout); debug_assert!(r == libc::ETIMEDOUT || r == 0); // ETIMEDOUT is not a totally reliable method of determining timeout due // to clock shifts, so do the check ourselves stable_now.elapsed() < dur } - - #[inline] - #[cfg(not(target_os = "dragonfly"))] - unsafe fn destroy(&mut self) { - let r = libc::pthread_cond_destroy(self.inner.get()); - debug_assert_eq!(r, 0); - } - - #[inline] - #[cfg(target_os = "dragonfly")] - unsafe fn destroy(&mut self) { - let r = libc::pthread_cond_destroy(self.inner.get()); - // On DragonFly pthread_cond_destroy() returns EINVAL if called on - // a condvar that was just initialized with - // libc::PTHREAD_COND_INITIALIZER. Once it is used or - // pthread_cond_init() is called, this behaviour no longer occurs. - debug_assert!(r == 0 || r == libc::EINVAL); - } -} - -impl Drop for Condvar { - #[inline] - fn drop(&mut self) { - unsafe { self.destroy() }; - } } diff --git a/library/std/src/sys/unix/locks/pthread_mutex.rs b/library/std/src/sys/unix/locks/pthread_mutex.rs index 5964935ddb54..8a78bc1fd739 100644 --- a/library/std/src/sys/unix/locks/pthread_mutex.rs +++ b/library/std/src/sys/unix/locks/pthread_mutex.rs @@ -3,56 +3,24 @@ use crate::mem::{forget, MaybeUninit}; use crate::sys::cvt_nz; use crate::sys_common::lazy_box::{LazyBox, LazyInit}; -pub struct Mutex { - inner: UnsafeCell, -} +struct AllocatedMutex(UnsafeCell); -pub(crate) type MovableMutex = LazyBox; +pub struct Mutex { + inner: LazyBox, +} #[inline] pub unsafe fn raw(m: &Mutex) -> *mut libc::pthread_mutex_t { - m.inner.get() + m.inner.0.get() } -unsafe impl Send for Mutex {} -unsafe impl Sync for Mutex {} +unsafe impl Send for AllocatedMutex {} +unsafe impl Sync for AllocatedMutex {} -impl LazyInit for Mutex { +impl LazyInit for AllocatedMutex { fn init() -> Box { - let mut mutex = Box::new(Self::new()); - unsafe { mutex.init() }; - mutex - } + let mutex = Box::new(AllocatedMutex(UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER))); - fn destroy(mutex: Box) { - // We're not allowed to pthread_mutex_destroy a locked mutex, - // so check first if it's unlocked. - if unsafe { mutex.try_lock() } { - unsafe { mutex.unlock() }; - drop(mutex); - } else { - // The mutex is locked. This happens if a MutexGuard is leaked. - // In this case, we just leak the Mutex too. - forget(mutex); - } - } - - fn cancel_init(_: Box) { - // In this case, we can just drop it without any checks, - // since it cannot have been locked yet. - } -} - -impl Mutex { - pub const fn new() -> Mutex { - // Might be moved to a different address, so it is better to avoid - // initialization of potentially opaque OS data before it landed. - // Be very careful using this newly constructed `Mutex`, reentrant - // locking is undefined behavior until `init` is called! - Mutex { inner: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER) } - } - #[inline] - unsafe fn init(&mut self) { // Issue #33770 // // A pthread mutex initialized with PTHREAD_MUTEX_INITIALIZER will have @@ -77,49 +45,77 @@ impl Mutex { // references, we instead create the mutex with type // PTHREAD_MUTEX_NORMAL which is guaranteed to deadlock if we try to // re-lock it from the same thread, thus avoiding undefined behavior. - let mut attr = MaybeUninit::::uninit(); - cvt_nz(libc::pthread_mutexattr_init(attr.as_mut_ptr())).unwrap(); - let attr = PthreadMutexAttr(&mut attr); - cvt_nz(libc::pthread_mutexattr_settype(attr.0.as_mut_ptr(), libc::PTHREAD_MUTEX_NORMAL)) + unsafe { + let mut attr = MaybeUninit::::uninit(); + cvt_nz(libc::pthread_mutexattr_init(attr.as_mut_ptr())).unwrap(); + let attr = PthreadMutexAttr(&mut attr); + cvt_nz(libc::pthread_mutexattr_settype( + attr.0.as_mut_ptr(), + libc::PTHREAD_MUTEX_NORMAL, + )) .unwrap(); - cvt_nz(libc::pthread_mutex_init(self.inner.get(), attr.0.as_ptr())).unwrap(); + cvt_nz(libc::pthread_mutex_init(mutex.0.get(), attr.0.as_ptr())).unwrap(); + } + + mutex } - #[inline] - pub unsafe fn lock(&self) { - let r = libc::pthread_mutex_lock(self.inner.get()); - debug_assert_eq!(r, 0); + + fn destroy(mutex: Box) { + // We're not allowed to pthread_mutex_destroy a locked mutex, + // so check first if it's unlocked. + if unsafe { libc::pthread_mutex_trylock(mutex.0.get()) == 0 } { + unsafe { libc::pthread_mutex_unlock(mutex.0.get()) }; + drop(mutex); + } else { + // The mutex is locked. This happens if a MutexGuard is leaked. + // In this case, we just leak the Mutex too. + forget(mutex); + } } - #[inline] - pub unsafe fn unlock(&self) { - let r = libc::pthread_mutex_unlock(self.inner.get()); - debug_assert_eq!(r, 0); - } - #[inline] - pub unsafe fn try_lock(&self) -> bool { - libc::pthread_mutex_trylock(self.inner.get()) == 0 - } - #[inline] - #[cfg(not(target_os = "dragonfly"))] - unsafe fn destroy(&mut self) { - let r = libc::pthread_mutex_destroy(self.inner.get()); - debug_assert_eq!(r, 0); - } - #[inline] - #[cfg(target_os = "dragonfly")] - unsafe fn destroy(&mut self) { - let r = libc::pthread_mutex_destroy(self.inner.get()); - // On DragonFly pthread_mutex_destroy() returns EINVAL if called on a - // mutex that was just initialized with libc::PTHREAD_MUTEX_INITIALIZER. - // Once it is used (locked/unlocked) or pthread_mutex_init() is called, - // this behaviour no longer occurs. - debug_assert!(r == 0 || r == libc::EINVAL); + + fn cancel_init(_: Box) { + // In this case, we can just drop it without any checks, + // since it cannot have been locked yet. } } -impl Drop for Mutex { +impl Drop for AllocatedMutex { #[inline] fn drop(&mut self) { - unsafe { self.destroy() }; + let r = unsafe { libc::pthread_mutex_destroy(self.0.get()) }; + if cfg!(target_os = "dragonfly") { + // On DragonFly pthread_mutex_destroy() returns EINVAL if called on a + // mutex that was just initialized with libc::PTHREAD_MUTEX_INITIALIZER. + // Once it is used (locked/unlocked) or pthread_mutex_init() is called, + // this behaviour no longer occurs. + debug_assert!(r == 0 || r == libc::EINVAL); + } else { + debug_assert_eq!(r, 0); + } + } +} + +impl Mutex { + #[inline] + pub const fn new() -> Mutex { + Mutex { inner: LazyBox::new() } + } + + #[inline] + pub unsafe fn lock(&self) { + let r = libc::pthread_mutex_lock(raw(self)); + debug_assert_eq!(r, 0); + } + + #[inline] + pub unsafe fn unlock(&self) { + let r = libc::pthread_mutex_unlock(raw(self)); + debug_assert_eq!(r, 0); + } + + #[inline] + pub unsafe fn try_lock(&self) -> bool { + libc::pthread_mutex_trylock(raw(self)) == 0 } } diff --git a/library/std/src/sys/unix/locks/pthread_rwlock.rs b/library/std/src/sys/unix/locks/pthread_rwlock.rs index adfe2a88338f..04662be9d827 100644 --- a/library/std/src/sys/unix/locks/pthread_rwlock.rs +++ b/library/std/src/sys/unix/locks/pthread_rwlock.rs @@ -3,20 +3,26 @@ use crate::mem::forget; use crate::sync::atomic::{AtomicUsize, Ordering}; use crate::sys_common::lazy_box::{LazyBox, LazyInit}; -pub struct RwLock { +struct AllocatedRwLock { inner: UnsafeCell, write_locked: UnsafeCell, // guarded by the `inner` RwLock num_readers: AtomicUsize, } -pub(crate) type MovableRwLock = LazyBox; +unsafe impl Send for AllocatedRwLock {} +unsafe impl Sync for AllocatedRwLock {} -unsafe impl Send for RwLock {} -unsafe impl Sync for RwLock {} +pub struct RwLock { + inner: LazyBox, +} -impl LazyInit for RwLock { +impl LazyInit for AllocatedRwLock { fn init() -> Box { - Box::new(Self::new()) + Box::new(AllocatedRwLock { + inner: UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER), + write_locked: UnsafeCell::new(false), + num_readers: AtomicUsize::new(0), + }) } fn destroy(mut rwlock: Box) { @@ -35,17 +41,39 @@ impl LazyInit for RwLock { } } -impl RwLock { - pub const fn new() -> RwLock { - RwLock { - inner: UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER), - write_locked: UnsafeCell::new(false), - num_readers: AtomicUsize::new(0), +impl AllocatedRwLock { + #[inline] + unsafe fn raw_unlock(&self) { + let r = libc::pthread_rwlock_unlock(self.inner.get()); + debug_assert_eq!(r, 0); + } +} + +impl Drop for AllocatedRwLock { + fn drop(&mut self) { + let r = unsafe { libc::pthread_rwlock_destroy(self.inner.get()) }; + // On DragonFly pthread_rwlock_destroy() returns EINVAL if called on a + // rwlock that was just initialized with + // libc::PTHREAD_RWLOCK_INITIALIZER. Once it is used (locked/unlocked) + // or pthread_rwlock_init() is called, this behaviour no longer occurs. + if cfg!(target_os = "dragonfly") { + debug_assert!(r == 0 || r == libc::EINVAL); + } else { + debug_assert_eq!(r, 0); } } +} + +impl RwLock { #[inline] - pub unsafe fn read(&self) { - let r = libc::pthread_rwlock_rdlock(self.inner.get()); + pub const fn new() -> RwLock { + RwLock { inner: LazyBox::new() } + } + + #[inline] + pub fn read(&self) { + let lock = &*self.inner; + let r = unsafe { libc::pthread_rwlock_rdlock(lock.inner.get()) }; // According to POSIX, when a thread tries to acquire this read lock // while it already holds the write lock @@ -62,51 +90,61 @@ impl RwLock { // got the write lock more than once, or got a read and a write lock. if r == libc::EAGAIN { panic!("rwlock maximum reader count exceeded"); - } else if r == libc::EDEADLK || (r == 0 && *self.write_locked.get()) { + } else if r == libc::EDEADLK || (r == 0 && unsafe { *lock.write_locked.get() }) { // Above, we make sure to only access `write_locked` when `r == 0` to avoid // data races. if r == 0 { // `pthread_rwlock_rdlock` succeeded when it should not have. - self.raw_unlock(); + unsafe { + lock.raw_unlock(); + } } panic!("rwlock read lock would result in deadlock"); } else { // POSIX does not make guarantees about all the errors that may be returned. // See issue #94705 for more details. assert_eq!(r, 0, "unexpected error during rwlock read lock: {:?}", r); - self.num_readers.fetch_add(1, Ordering::Relaxed); + lock.num_readers.fetch_add(1, Ordering::Relaxed); } } + #[inline] - pub unsafe fn try_read(&self) -> bool { - let r = libc::pthread_rwlock_tryrdlock(self.inner.get()); + pub fn try_read(&self) -> bool { + let lock = &*self.inner; + let r = unsafe { libc::pthread_rwlock_tryrdlock(lock.inner.get()) }; if r == 0 { - if *self.write_locked.get() { + if unsafe { *lock.write_locked.get() } { // `pthread_rwlock_tryrdlock` succeeded when it should not have. - self.raw_unlock(); + unsafe { + lock.raw_unlock(); + } false } else { - self.num_readers.fetch_add(1, Ordering::Relaxed); + lock.num_readers.fetch_add(1, Ordering::Relaxed); true } } else { false } } + #[inline] - pub unsafe fn write(&self) { - let r = libc::pthread_rwlock_wrlock(self.inner.get()); + pub fn write(&self) { + let lock = &*self.inner; + let r = unsafe { libc::pthread_rwlock_wrlock(lock.inner.get()) }; // See comments above for why we check for EDEADLK and write_locked. For the same reason, // we also need to check that there are no readers (tracked in `num_readers`). if r == libc::EDEADLK - || (r == 0 && *self.write_locked.get()) - || self.num_readers.load(Ordering::Relaxed) != 0 + || (r == 0 && unsafe { *lock.write_locked.get() }) + || lock.num_readers.load(Ordering::Relaxed) != 0 { // Above, we make sure to only access `write_locked` when `r == 0` to avoid // data races. if r == 0 { // `pthread_rwlock_wrlock` succeeded when it should not have. - self.raw_unlock(); + unsafe { + lock.raw_unlock(); + } } panic!("rwlock write lock would result in deadlock"); } else { @@ -114,60 +152,44 @@ impl RwLock { // return EDEADLK or 0. We rely on that. debug_assert_eq!(r, 0); } - *self.write_locked.get() = true; + + unsafe { + *lock.write_locked.get() = true; + } } + #[inline] pub unsafe fn try_write(&self) -> bool { - let r = libc::pthread_rwlock_trywrlock(self.inner.get()); + let lock = &*self.inner; + let r = libc::pthread_rwlock_trywrlock(lock.inner.get()); if r == 0 { - if *self.write_locked.get() || self.num_readers.load(Ordering::Relaxed) != 0 { + if *lock.write_locked.get() || lock.num_readers.load(Ordering::Relaxed) != 0 { // `pthread_rwlock_trywrlock` succeeded when it should not have. - self.raw_unlock(); + lock.raw_unlock(); false } else { - *self.write_locked.get() = true; + *lock.write_locked.get() = true; true } } else { false } } - #[inline] - unsafe fn raw_unlock(&self) { - let r = libc::pthread_rwlock_unlock(self.inner.get()); - debug_assert_eq!(r, 0); - } + #[inline] pub unsafe fn read_unlock(&self) { - debug_assert!(!*self.write_locked.get()); - self.num_readers.fetch_sub(1, Ordering::Relaxed); - self.raw_unlock(); + let lock = &*self.inner; + debug_assert!(!*lock.write_locked.get()); + lock.num_readers.fetch_sub(1, Ordering::Relaxed); + lock.raw_unlock(); } + #[inline] pub unsafe fn write_unlock(&self) { - debug_assert_eq!(self.num_readers.load(Ordering::Relaxed), 0); - debug_assert!(*self.write_locked.get()); - *self.write_locked.get() = false; - self.raw_unlock(); - } - #[inline] - unsafe fn destroy(&mut self) { - let r = libc::pthread_rwlock_destroy(self.inner.get()); - // On DragonFly pthread_rwlock_destroy() returns EINVAL if called on a - // rwlock that was just initialized with - // libc::PTHREAD_RWLOCK_INITIALIZER. Once it is used (locked/unlocked) - // or pthread_rwlock_init() is called, this behaviour no longer occurs. - if cfg!(target_os = "dragonfly") { - debug_assert!(r == 0 || r == libc::EINVAL); - } else { - debug_assert_eq!(r, 0); - } - } -} - -impl Drop for RwLock { - #[inline] - fn drop(&mut self) { - unsafe { self.destroy() }; + let lock = &*self.inner; + debug_assert_eq!(lock.num_readers.load(Ordering::Relaxed), 0); + debug_assert!(*lock.write_locked.get()); + *lock.write_locked.get() = false; + lock.raw_unlock(); } } diff --git a/library/std/src/sys/unsupported/locks/condvar.rs b/library/std/src/sys/unsupported/locks/condvar.rs index 527a26a12bce..3f0943b50ee4 100644 --- a/library/std/src/sys/unsupported/locks/condvar.rs +++ b/library/std/src/sys/unsupported/locks/condvar.rs @@ -3,8 +3,6 @@ use crate::time::Duration; pub struct Condvar {} -pub type MovableCondvar = Condvar; - impl Condvar { #[inline] #[rustc_const_stable(feature = "const_locks", since = "1.63.0")] @@ -13,10 +11,10 @@ impl Condvar { } #[inline] - pub unsafe fn notify_one(&self) {} + pub fn notify_one(&self) {} #[inline] - pub unsafe fn notify_all(&self) {} + pub fn notify_all(&self) {} pub unsafe fn wait(&self, _mutex: &Mutex) { panic!("condvar wait not supported") diff --git a/library/std/src/sys/unsupported/locks/mod.rs b/library/std/src/sys/unsupported/locks/mod.rs index 602a2d6231a2..0e0f9eccb213 100644 --- a/library/std/src/sys/unsupported/locks/mod.rs +++ b/library/std/src/sys/unsupported/locks/mod.rs @@ -1,6 +1,6 @@ mod condvar; mod mutex; mod rwlock; -pub use condvar::{Condvar, MovableCondvar}; -pub use mutex::{MovableMutex, Mutex}; -pub use rwlock::MovableRwLock; +pub use condvar::Condvar; +pub use mutex::Mutex; +pub use rwlock::RwLock; diff --git a/library/std/src/sys/unsupported/locks/mutex.rs b/library/std/src/sys/unsupported/locks/mutex.rs index 87ea475c6e3e..4a13c55fb8be 100644 --- a/library/std/src/sys/unsupported/locks/mutex.rs +++ b/library/std/src/sys/unsupported/locks/mutex.rs @@ -5,8 +5,6 @@ pub struct Mutex { locked: Cell, } -pub type MovableMutex = Mutex; - unsafe impl Send for Mutex {} unsafe impl Sync for Mutex {} // no threads on this platform @@ -18,7 +16,7 @@ impl Mutex { } #[inline] - pub unsafe fn lock(&self) { + pub fn lock(&self) { assert_eq!(self.locked.replace(true), false, "cannot recursively acquire mutex"); } @@ -28,7 +26,7 @@ impl Mutex { } #[inline] - pub unsafe fn try_lock(&self) -> bool { + pub fn try_lock(&self) -> bool { self.locked.replace(true) == false } } diff --git a/library/std/src/sys/unsupported/locks/rwlock.rs b/library/std/src/sys/unsupported/locks/rwlock.rs index 5292691b9555..789ef9b29e52 100644 --- a/library/std/src/sys/unsupported/locks/rwlock.rs +++ b/library/std/src/sys/unsupported/locks/rwlock.rs @@ -5,8 +5,6 @@ pub struct RwLock { mode: Cell, } -pub type MovableRwLock = RwLock; - unsafe impl Send for RwLock {} unsafe impl Sync for RwLock {} // no threads on this platform @@ -18,7 +16,7 @@ impl RwLock { } #[inline] - pub unsafe fn read(&self) { + pub fn read(&self) { let m = self.mode.get(); if m >= 0 { self.mode.set(m + 1); @@ -28,7 +26,7 @@ impl RwLock { } #[inline] - pub unsafe fn try_read(&self) -> bool { + pub fn try_read(&self) -> bool { let m = self.mode.get(); if m >= 0 { self.mode.set(m + 1); @@ -39,14 +37,14 @@ impl RwLock { } #[inline] - pub unsafe fn write(&self) { + pub fn write(&self) { if self.mode.replace(-1) != 0 { rtabort!("rwlock locked for reading") } } #[inline] - pub unsafe fn try_write(&self) -> bool { + pub fn try_write(&self) -> bool { if self.mode.get() == 0 { self.mode.set(-1); true diff --git a/library/std/src/sys/wasm/mod.rs b/library/std/src/sys/wasm/mod.rs index 93838390bee6..d68c3e5f1dfb 100644 --- a/library/std/src/sys/wasm/mod.rs +++ b/library/std/src/sys/wasm/mod.rs @@ -55,9 +55,9 @@ cfg_if::cfg_if! { mod futex_condvar; mod futex_mutex; mod futex_rwlock; - pub(crate) use futex_condvar::{Condvar, MovableCondvar}; - pub(crate) use futex_mutex::{Mutex, MovableMutex}; - pub(crate) use futex_rwlock::MovableRwLock; + pub(crate) use futex_condvar::Condvar; + pub(crate) use futex_mutex::Mutex; + pub(crate) use futex_rwlock::RwLock; } #[path = "atomics/futex.rs"] pub mod futex; diff --git a/library/std/src/sys/windows/locks/condvar.rs b/library/std/src/sys/windows/locks/condvar.rs index be9a2abbe35d..66fafa2c00b0 100644 --- a/library/std/src/sys/windows/locks/condvar.rs +++ b/library/std/src/sys/windows/locks/condvar.rs @@ -8,8 +8,6 @@ pub struct Condvar { inner: UnsafeCell, } -pub type MovableCondvar = Condvar; - unsafe impl Send for Condvar {} unsafe impl Sync for Condvar {} @@ -41,12 +39,12 @@ impl Condvar { } #[inline] - pub unsafe fn notify_one(&self) { - c::WakeConditionVariable(self.inner.get()) + pub fn notify_one(&self) { + unsafe { c::WakeConditionVariable(self.inner.get()) } } #[inline] - pub unsafe fn notify_all(&self) { - c::WakeAllConditionVariable(self.inner.get()) + pub fn notify_all(&self) { + unsafe { c::WakeAllConditionVariable(self.inner.get()) } } } diff --git a/library/std/src/sys/windows/locks/mod.rs b/library/std/src/sys/windows/locks/mod.rs index 602a2d6231a2..0e0f9eccb213 100644 --- a/library/std/src/sys/windows/locks/mod.rs +++ b/library/std/src/sys/windows/locks/mod.rs @@ -1,6 +1,6 @@ mod condvar; mod mutex; mod rwlock; -pub use condvar::{Condvar, MovableCondvar}; -pub use mutex::{MovableMutex, Mutex}; -pub use rwlock::MovableRwLock; +pub use condvar::Condvar; +pub use mutex::Mutex; +pub use rwlock::RwLock; diff --git a/library/std/src/sys/windows/locks/mutex.rs b/library/std/src/sys/windows/locks/mutex.rs index 91207f5f4665..ef2f84082cd5 100644 --- a/library/std/src/sys/windows/locks/mutex.rs +++ b/library/std/src/sys/windows/locks/mutex.rs @@ -21,9 +21,6 @@ pub struct Mutex { srwlock: UnsafeCell, } -// Windows SRW Locks are movable (while not borrowed). -pub type MovableMutex = Mutex; - unsafe impl Send for Mutex {} unsafe impl Sync for Mutex {} @@ -39,13 +36,15 @@ impl Mutex { } #[inline] - pub unsafe fn lock(&self) { - c::AcquireSRWLockExclusive(raw(self)); + pub fn lock(&self) { + unsafe { + c::AcquireSRWLockExclusive(raw(self)); + } } #[inline] - pub unsafe fn try_lock(&self) -> bool { - c::TryAcquireSRWLockExclusive(raw(self)) != 0 + pub fn try_lock(&self) -> bool { + unsafe { c::TryAcquireSRWLockExclusive(raw(self)) != 0 } } #[inline] diff --git a/library/std/src/sys/windows/locks/rwlock.rs b/library/std/src/sys/windows/locks/rwlock.rs index fa5ffe5749f2..e69415baac42 100644 --- a/library/std/src/sys/windows/locks/rwlock.rs +++ b/library/std/src/sys/windows/locks/rwlock.rs @@ -5,8 +5,6 @@ pub struct RwLock { inner: UnsafeCell, } -pub type MovableRwLock = RwLock; - unsafe impl Send for RwLock {} unsafe impl Sync for RwLock {} @@ -16,20 +14,20 @@ impl RwLock { RwLock { inner: UnsafeCell::new(c::SRWLOCK_INIT) } } #[inline] - pub unsafe fn read(&self) { - c::AcquireSRWLockShared(self.inner.get()) + pub fn read(&self) { + unsafe { c::AcquireSRWLockShared(self.inner.get()) } } #[inline] - pub unsafe fn try_read(&self) -> bool { - c::TryAcquireSRWLockShared(self.inner.get()) != 0 + pub fn try_read(&self) -> bool { + unsafe { c::TryAcquireSRWLockShared(self.inner.get()) != 0 } } #[inline] - pub unsafe fn write(&self) { - c::AcquireSRWLockExclusive(self.inner.get()) + pub fn write(&self) { + unsafe { c::AcquireSRWLockExclusive(self.inner.get()) } } #[inline] - pub unsafe fn try_write(&self) -> bool { - c::TryAcquireSRWLockExclusive(self.inner.get()) != 0 + pub fn try_write(&self) -> bool { + unsafe { c::TryAcquireSRWLockExclusive(self.inner.get()) != 0 } } #[inline] pub unsafe fn read_unlock(&self) { diff --git a/library/std/src/sys_common/condvar.rs b/library/std/src/sys_common/condvar.rs deleted file mode 100644 index 8bc5b24115d1..000000000000 --- a/library/std/src/sys_common/condvar.rs +++ /dev/null @@ -1,57 +0,0 @@ -use crate::sys::locks as imp; -use crate::sys_common::mutex::MovableMutex; -use crate::time::Duration; - -mod check; - -type CondvarCheck = ::Check; - -/// An OS-based condition variable. -pub struct Condvar { - inner: imp::MovableCondvar, - check: CondvarCheck, -} - -impl Condvar { - /// Creates a new condition variable for use. - #[inline] - #[rustc_const_stable(feature = "const_locks", since = "1.63.0")] - pub const fn new() -> Self { - Self { inner: imp::MovableCondvar::new(), check: CondvarCheck::new() } - } - - /// Signals one waiter on this condition variable to wake up. - #[inline] - pub fn notify_one(&self) { - unsafe { self.inner.notify_one() }; - } - - /// Awakens all current waiters on this condition variable. - #[inline] - pub fn notify_all(&self) { - unsafe { self.inner.notify_all() }; - } - - /// Waits for a signal on the specified mutex. - /// - /// Behavior is undefined if the mutex is not locked by the current thread. - /// - /// May panic if used with more than one mutex. - #[inline] - pub unsafe fn wait(&self, mutex: &MovableMutex) { - self.check.verify(mutex); - self.inner.wait(mutex.raw()) - } - - /// Waits for a signal on the specified mutex with a timeout duration - /// specified by `dur` (a relative time into the future). - /// - /// Behavior is undefined if the mutex is not locked by the current thread. - /// - /// May panic if used with more than one mutex. - #[inline] - pub unsafe fn wait_timeout(&self, mutex: &MovableMutex, dur: Duration) -> bool { - self.check.verify(mutex); - self.inner.wait_timeout(mutex.raw(), dur) - } -} diff --git a/library/std/src/sys_common/condvar/check.rs b/library/std/src/sys_common/condvar/check.rs deleted file mode 100644 index 4ac9e62bf869..000000000000 --- a/library/std/src/sys_common/condvar/check.rs +++ /dev/null @@ -1,58 +0,0 @@ -use crate::ptr; -use crate::sync::atomic::{AtomicPtr, Ordering}; -use crate::sys::locks as imp; -use crate::sys_common::lazy_box::{LazyBox, LazyInit}; -use crate::sys_common::mutex::MovableMutex; - -pub trait CondvarCheck { - type Check; -} - -/// For boxed mutexes, a `Condvar` will check it's only ever used with the same -/// mutex, based on its (stable) address. -impl CondvarCheck for LazyBox { - type Check = SameMutexCheck; -} - -pub struct SameMutexCheck { - addr: AtomicPtr<()>, -} - -#[allow(dead_code)] -impl SameMutexCheck { - pub const fn new() -> Self { - Self { addr: AtomicPtr::new(ptr::null_mut()) } - } - pub fn verify(&self, mutex: &MovableMutex) { - let addr = mutex.raw() as *const imp::Mutex as *const () as *mut _; - // Relaxed is okay here because we never read through `self.addr`, and only use it to - // compare addresses. - match self.addr.compare_exchange( - ptr::null_mut(), - addr, - Ordering::Relaxed, - Ordering::Relaxed, - ) { - Ok(_) => {} // Stored the address - Err(n) if n == addr => {} // Lost a race to store the same address - _ => panic!("attempted to use a condition variable with two mutexes"), - } - } -} - -/// Unboxed mutexes may move, so `Condvar` can not require its address to stay -/// constant. -impl CondvarCheck for imp::Mutex { - type Check = NoCheck; -} - -pub struct NoCheck; - -#[allow(dead_code)] -impl NoCheck { - #[rustc_const_stable(feature = "const_locks", since = "1.63.0")] - pub const fn new() -> Self { - Self - } - pub fn verify(&self, _: &MovableMutex) {} -} diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs index 8c19f9332dc5..069b13e9d85e 100644 --- a/library/std/src/sys_common/mod.rs +++ b/library/std/src/sys_common/mod.rs @@ -21,16 +21,13 @@ mod tests; pub mod backtrace; -pub mod condvar; pub mod fs; pub mod io; pub mod lazy_box; pub mod memchr; -pub mod mutex; pub mod once; pub mod process; pub mod remutex; -pub mod rwlock; pub mod thread; pub mod thread_info; pub mod thread_local_dtor; diff --git a/library/std/src/sys_common/mutex.rs b/library/std/src/sys_common/mutex.rs deleted file mode 100644 index 98046f20f896..000000000000 --- a/library/std/src/sys_common/mutex.rs +++ /dev/null @@ -1,50 +0,0 @@ -use crate::sys::locks as imp; - -/// An OS-based mutual exclusion lock. -/// -/// This mutex cleans up its resources in its `Drop` implementation, may safely -/// be moved (when not borrowed), and does not cause UB when used reentrantly. -/// -/// This mutex does not implement poisoning. -/// -/// This is either a wrapper around `LazyBox` or `imp::Mutex`, -/// depending on the platform. It is boxed on platforms where `imp::Mutex` may -/// not be moved. -pub struct MovableMutex(imp::MovableMutex); - -unsafe impl Sync for MovableMutex {} - -impl MovableMutex { - /// Creates a new mutex. - #[inline] - #[rustc_const_stable(feature = "const_locks", since = "1.63.0")] - pub const fn new() -> Self { - Self(imp::MovableMutex::new()) - } - - pub(super) fn raw(&self) -> &imp::Mutex { - &self.0 - } - - /// Locks the mutex blocking the current thread until it is available. - #[inline] - pub fn raw_lock(&self) { - unsafe { self.0.lock() } - } - - /// Attempts to lock the mutex without blocking, returning whether it was - /// successfully acquired or not. - #[inline] - pub fn try_lock(&self) -> bool { - unsafe { self.0.try_lock() } - } - - /// Unlocks the mutex. - /// - /// Behavior is undefined if the current thread does not actually hold the - /// mutex. - #[inline] - pub unsafe fn raw_unlock(&self) { - self.0.unlock() - } -} diff --git a/library/std/src/sys_common/remutex.rs b/library/std/src/sys_common/remutex.rs index b448ae3a9977..4c054da64714 100644 --- a/library/std/src/sys_common/remutex.rs +++ b/library/std/src/sys_common/remutex.rs @@ -1,11 +1,11 @@ #[cfg(all(test, not(target_os = "emscripten")))] mod tests; -use super::mutex as sys; use crate::cell::UnsafeCell; use crate::ops::Deref; use crate::panic::{RefUnwindSafe, UnwindSafe}; use crate::sync::atomic::{AtomicUsize, Ordering::Relaxed}; +use crate::sys::locks as sys; /// A re-entrant mutual exclusion /// @@ -39,7 +39,7 @@ use crate::sync::atomic::{AtomicUsize, Ordering::Relaxed}; /// synchronization is left to the mutex, making relaxed memory ordering for /// the `owner` field fine in all cases. pub struct ReentrantMutex { - mutex: sys::MovableMutex, + mutex: sys::Mutex, owner: AtomicUsize, lock_count: UnsafeCell, data: T, @@ -74,7 +74,7 @@ impl ReentrantMutex { /// Creates a new reentrant mutex in an unlocked state. pub const fn new(t: T) -> ReentrantMutex { ReentrantMutex { - mutex: sys::MovableMutex::new(), + mutex: sys::Mutex::new(), owner: AtomicUsize::new(0), lock_count: UnsafeCell::new(0), data: t, @@ -100,7 +100,7 @@ impl ReentrantMutex { if self.owner.load(Relaxed) == this_thread { self.increment_lock_count(); } else { - self.mutex.raw_lock(); + self.mutex.lock(); self.owner.store(this_thread, Relaxed); debug_assert_eq!(*self.lock_count.get(), 0); *self.lock_count.get() = 1; @@ -162,7 +162,7 @@ impl Drop for ReentrantMutexGuard<'_, T> { *self.lock.lock_count.get() -= 1; if *self.lock.lock_count.get() == 0 { self.lock.owner.store(0, Relaxed); - self.lock.mutex.raw_unlock(); + self.lock.mutex.unlock(); } } } diff --git a/library/std/src/sys_common/rwlock.rs b/library/std/src/sys_common/rwlock.rs deleted file mode 100644 index 042981dac60b..000000000000 --- a/library/std/src/sys_common/rwlock.rs +++ /dev/null @@ -1,71 +0,0 @@ -use crate::sys::locks as imp; - -/// An OS-based reader-writer lock. -/// -/// This rwlock cleans up its resources in its `Drop` implementation and may -/// safely be moved (when not borrowed). -/// -/// This rwlock does not implement poisoning. -/// -/// This is either a wrapper around `LazyBox` or `imp::RwLock`, -/// depending on the platform. It is boxed on platforms where `imp::RwLock` may -/// not be moved. -pub struct MovableRwLock(imp::MovableRwLock); - -impl MovableRwLock { - /// Creates a new reader-writer lock for use. - #[inline] - #[rustc_const_stable(feature = "const_locks", since = "1.63.0")] - pub const fn new() -> Self { - Self(imp::MovableRwLock::new()) - } - - /// Acquires shared access to the underlying lock, blocking the current - /// thread to do so. - #[inline] - pub fn read(&self) { - unsafe { self.0.read() } - } - - /// Attempts to acquire shared access to this lock, returning whether it - /// succeeded or not. - /// - /// This function does not block the current thread. - #[inline] - pub fn try_read(&self) -> bool { - unsafe { self.0.try_read() } - } - - /// Acquires write access to the underlying lock, blocking the current thread - /// to do so. - #[inline] - pub fn write(&self) { - unsafe { self.0.write() } - } - - /// Attempts to acquire exclusive access to this lock, returning whether it - /// succeeded or not. - /// - /// This function does not block the current thread. - #[inline] - pub fn try_write(&self) -> bool { - unsafe { self.0.try_write() } - } - - /// Unlocks previously acquired shared access to this lock. - /// - /// Behavior is undefined if the current thread does not have shared access. - #[inline] - pub unsafe fn read_unlock(&self) { - self.0.read_unlock() - } - - /// Unlocks previously acquired exclusive access to this lock. - /// - /// Behavior is undefined if the current thread does not currently have - /// exclusive access. - #[inline] - pub unsafe fn write_unlock(&self) { - self.0.write_unlock() - } -} diff --git a/library/std/src/thread/local/tests.rs b/library/std/src/thread/local/tests.rs index 1df1ca758c05..80dc4c038d61 100644 --- a/library/std/src/thread/local/tests.rs +++ b/library/std/src/thread/local/tests.rs @@ -1,15 +1,34 @@ use crate::cell::{Cell, UnsafeCell}; use crate::sync::atomic::{AtomicU8, Ordering}; -use crate::sync::mpsc::{channel, Sender}; +use crate::sync::{Arc, Condvar, Mutex}; use crate::thread::{self, LocalKey}; use crate::thread_local; -struct Foo(Sender<()>); +#[derive(Clone, Default)] +struct Signal(Arc<(Mutex, Condvar)>); + +impl Signal { + fn notify(&self) { + let (set, cvar) = &*self.0; + *set.lock().unwrap() = true; + cvar.notify_one(); + } + + fn wait(&self) { + let (set, cvar) = &*self.0; + let mut set = set.lock().unwrap(); + while !*set { + set = cvar.wait(set).unwrap(); + } + } +} + +struct Foo(Signal); impl Drop for Foo { fn drop(&mut self) { - let Foo(ref s) = *self; - s.send(()).unwrap(); + let Foo(ref f) = *self; + f.notify(); } } @@ -69,14 +88,15 @@ fn smoke_dtor() { run(&FOO2); fn run(key: &'static LocalKey>>) { - let (tx, rx) = channel(); + let signal = Signal::default(); + let signal2 = signal.clone(); let t = thread::spawn(move || unsafe { - let mut tx = Some(tx); + let mut signal = Some(signal2); key.with(|f| { - *f.get() = Some(Foo(tx.take().unwrap())); + *f.get() = Some(Foo(signal.take().unwrap())); }); }); - rx.recv().unwrap(); + signal.wait(); t.join().unwrap(); } } @@ -165,48 +185,50 @@ fn self_referential() { // requires the destructor to be run to pass the test). #[test] fn dtors_in_dtors_in_dtors() { - struct S1(Sender<()>); + struct S1(Signal); thread_local!(static K1: UnsafeCell> = UnsafeCell::new(None)); thread_local!(static K2: UnsafeCell> = UnsafeCell::new(None)); impl Drop for S1 { fn drop(&mut self) { - let S1(ref tx) = *self; + let S1(ref signal) = *self; unsafe { - let _ = K2.try_with(|s| *s.get() = Some(Foo(tx.clone()))); + let _ = K2.try_with(|s| *s.get() = Some(Foo(signal.clone()))); } } } - let (tx, rx) = channel(); + let signal = Signal::default(); + let signal2 = signal.clone(); let _t = thread::spawn(move || unsafe { - let mut tx = Some(tx); - K1.with(|s| *s.get() = Some(S1(tx.take().unwrap()))); + let mut signal = Some(signal2); + K1.with(|s| *s.get() = Some(S1(signal.take().unwrap()))); }); - rx.recv().unwrap(); + signal.wait(); } #[test] fn dtors_in_dtors_in_dtors_const_init() { - struct S1(Sender<()>); + struct S1(Signal); thread_local!(static K1: UnsafeCell> = const { UnsafeCell::new(None) }); thread_local!(static K2: UnsafeCell> = const { UnsafeCell::new(None) }); impl Drop for S1 { fn drop(&mut self) { - let S1(ref tx) = *self; + let S1(ref signal) = *self; unsafe { - let _ = K2.try_with(|s| *s.get() = Some(Foo(tx.clone()))); + let _ = K2.try_with(|s| *s.get() = Some(Foo(signal.clone()))); } } } - let (tx, rx) = channel(); + let signal = Signal::default(); + let signal2 = signal.clone(); let _t = thread::spawn(move || unsafe { - let mut tx = Some(tx); - K1.with(|s| *s.get() = Some(S1(tx.take().unwrap()))); + let mut signal = Some(signal2); + K1.with(|s| *s.get() = Some(S1(signal.take().unwrap()))); }); - rx.recv().unwrap(); + signal.wait(); } // This test tests that TLS destructors have run before the thread joins. The diff --git a/library/std/src/time.rs b/library/std/src/time.rs index 34e18b5fa877..ecd06ebf743a 100644 --- a/library/std/src/time.rs +++ b/library/std/src/time.rs @@ -43,7 +43,7 @@ use crate::sys_common::{FromInner, IntoInner}; #[stable(feature = "time", since = "1.3.0")] pub use core::time::Duration; -#[stable(feature = "duration_checked_float", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "duration_checked_float", since = "1.66.0")] pub use core::time::TryFromFloatSecsError; /// A measurement of a monotonically nondecreasing clock. diff --git a/library/unwind/Cargo.toml b/library/unwind/Cargo.toml index 32c4a7eb5c18..eab2717c4523 100644 --- a/library/unwind/Cargo.toml +++ b/library/unwind/Cargo.toml @@ -20,7 +20,7 @@ compiler_builtins = "0.1.0" cfg-if = "1.0" [build-dependencies] -cc = "1.0.69" +cc = "1.0.76" [features] diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs index 32c0ef3e1168..2efd2d5dd4aa 100644 --- a/library/unwind/src/lib.rs +++ b/library/unwind/src/lib.rs @@ -103,7 +103,3 @@ extern "C" {} #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] #[link(name = "unwind", kind = "static", modifiers = "-bundle")] extern "C" {} - -#[cfg(all(target_os = "windows", target_env = "gnu", target_abi = "llvm"))] -#[link(name = "unwind", kind = "static", modifiers = "-bundle")] -extern "C" {} diff --git a/library/unwind/src/libunwind.rs b/library/unwind/src/libunwind.rs index a5b6193b086f..15500f7fd354 100644 --- a/library/unwind/src/libunwind.rs +++ b/library/unwind/src/libunwind.rs @@ -36,9 +36,12 @@ pub const unwinder_private_data_size: usize = 20; #[cfg(all(target_arch = "arm", any(target_os = "ios", target_os = "watchos")))] pub const unwinder_private_data_size: usize = 5; -#[cfg(all(target_arch = "aarch64", target_pointer_width = "64"))] +#[cfg(all(target_arch = "aarch64", target_pointer_width = "64", not(target_os = "windows")))] pub const unwinder_private_data_size: usize = 2; +#[cfg(all(target_arch = "aarch64", target_pointer_width = "64", target_os = "windows"))] +pub const unwinder_private_data_size: usize = 6; + #[cfg(all(target_arch = "aarch64", target_pointer_width = "32"))] pub const unwinder_private_data_size: usize = 5; @@ -90,7 +93,10 @@ pub type _Unwind_Exception_Cleanup_Fn = // rustc_codegen_ssa::src::back::symbol_export, rustc_middle::middle::exported_symbols // and RFC 2841 #[cfg_attr( - all(feature = "llvm-libunwind", any(target_os = "fuchsia", target_os = "linux")), + any( + all(feature = "llvm-libunwind", any(target_os = "fuchsia", target_os = "linux")), + all(target_os = "windows", target_env = "gnu", target_abi = "llvm") + ), link(name = "unwind", kind = "static", modifiers = "-bundle") )] extern "C-unwind" { diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 6fd363935079..8d999302a6d7 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -2,14 +2,13 @@ use std::any::{type_name, Any}; use std::cell::{Cell, RefCell}; use std::collections::BTreeSet; use std::env; -use std::ffi::{OsStr, OsString}; +use std::ffi::OsStr; use std::fmt::{Debug, Write}; -use std::fs::{self, File}; +use std::fs::{self}; use std::hash::Hash; -use std::io::{BufRead, BufReader, ErrorKind}; use std::ops::Deref; use std::path::{Component, Path, PathBuf}; -use std::process::{Command, Stdio}; +use std::process::Command; use std::time::{Duration, Instant}; use crate::cache::{Cache, Interned, INTERNER}; @@ -24,14 +23,12 @@ use crate::test; use crate::tool::{self, SourceType}; use crate::util::{self, add_dylib_path, add_link_lib_path, exe, libdir, output, t}; use crate::EXTRA_CHECK_CFGS; -use crate::{check, Config}; -use crate::{compile, Crate}; +use crate::{check, compile, Crate}; use crate::{Build, CLang, DocTests, GitRepo, Mode}; pub use crate::Compiler; // FIXME: replace with std::lazy after it gets stabilized and reaches beta -use once_cell::sync::{Lazy, OnceCell}; -use xz2::bufread::XzDecoder; +use once_cell::sync::Lazy; pub struct Builder<'a> { pub build: &'a Build, @@ -853,241 +850,6 @@ impl<'a> Builder<'a> { StepDescription::run(v, self, paths); } - /// Modifies the interpreter section of 'fname' to fix the dynamic linker, - /// or the RPATH section, to fix the dynamic library search path - /// - /// This is only required on NixOS and uses the PatchELF utility to - /// change the interpreter/RPATH of ELF executables. - /// - /// Please see https://nixos.org/patchelf.html for more information - pub(crate) fn fix_bin_or_dylib(&self, fname: &Path) { - // FIXME: cache NixOS detection? - match Command::new("uname").arg("-s").stderr(Stdio::inherit()).output() { - Err(_) => return, - Ok(output) if !output.status.success() => return, - Ok(output) => { - let mut s = output.stdout; - if s.last() == Some(&b'\n') { - s.pop(); - } - if s != b"Linux" { - return; - } - } - } - - // If the user has asked binaries to be patched for Nix, then - // don't check for NixOS or `/lib`, just continue to the patching. - // NOTE: this intentionally comes after the Linux check: - // - patchelf only works with ELF files, so no need to run it on Mac or Windows - // - On other Unix systems, there is no stable syscall interface, so Nix doesn't manage the global libc. - if !self.config.patch_binaries_for_nix { - // Use `/etc/os-release` instead of `/etc/NIXOS`. - // The latter one does not exist on NixOS when using tmpfs as root. - const NIX_IDS: &[&str] = &["ID=nixos", "ID='nixos'", "ID=\"nixos\""]; - let os_release = match File::open("/etc/os-release") { - Err(e) if e.kind() == ErrorKind::NotFound => return, - Err(e) => panic!("failed to access /etc/os-release: {}", e), - Ok(f) => f, - }; - if !BufReader::new(os_release).lines().any(|l| NIX_IDS.contains(&t!(l).trim())) { - return; - } - if Path::new("/lib").exists() { - return; - } - } - - // At this point we're pretty sure the user is running NixOS or using Nix - println!("info: you seem to be using Nix. Attempting to patch {}", fname.display()); - - // Only build `.nix-deps` once. - static NIX_DEPS_DIR: OnceCell = OnceCell::new(); - let mut nix_build_succeeded = true; - let nix_deps_dir = NIX_DEPS_DIR.get_or_init(|| { - // Run `nix-build` to "build" each dependency (which will likely reuse - // the existing `/nix/store` copy, or at most download a pre-built copy). - // - // Importantly, we create a gc-root called `.nix-deps` in the `build/` - // directory, but still reference the actual `/nix/store` path in the rpath - // as it makes it significantly more robust against changes to the location of - // the `.nix-deps` location. - // - // bintools: Needed for the path of `ld-linux.so` (via `nix-support/dynamic-linker`). - // zlib: Needed as a system dependency of `libLLVM-*.so`. - // patchelf: Needed for patching ELF binaries (see doc comment above). - let nix_deps_dir = self.out.join(".nix-deps"); - const NIX_EXPR: &str = " - with (import {}); - symlinkJoin { - name = \"rust-stage0-dependencies\"; - paths = [ - zlib - patchelf - stdenv.cc.bintools - ]; - } - "; - nix_build_succeeded = self.try_run(Command::new("nix-build").args(&[ - Path::new("-E"), - Path::new(NIX_EXPR), - Path::new("-o"), - &nix_deps_dir, - ])); - nix_deps_dir - }); - if !nix_build_succeeded { - return; - } - - let mut patchelf = Command::new(nix_deps_dir.join("bin/patchelf")); - let rpath_entries = { - // ORIGIN is a relative default, all binary and dynamic libraries we ship - // appear to have this (even when `../lib` is redundant). - // NOTE: there are only two paths here, delimited by a `:` - let mut entries = OsString::from("$ORIGIN/../lib:"); - entries.push(t!(fs::canonicalize(nix_deps_dir))); - entries.push("/lib"); - entries - }; - patchelf.args(&[OsString::from("--set-rpath"), rpath_entries]); - if !fname.extension().map_or(false, |ext| ext == "so") { - // Finally, set the correct .interp for binaries - let dynamic_linker_path = nix_deps_dir.join("nix-support/dynamic-linker"); - // FIXME: can we support utf8 here? `args` doesn't accept Vec, only OsString ... - let dynamic_linker = t!(String::from_utf8(t!(fs::read(dynamic_linker_path)))); - patchelf.args(&["--set-interpreter", dynamic_linker.trim_end()]); - } - - self.try_run(patchelf.arg(fname)); - } - - pub(crate) fn download_component(&self, url: &str, dest_path: &Path, help_on_error: &str) { - self.verbose(&format!("download {url}")); - // Use a temporary file in case we crash while downloading, to avoid a corrupt download in cache/. - let tempfile = self.tempdir().join(dest_path.file_name().unwrap()); - // While bootstrap itself only supports http and https downloads, downstream forks might - // need to download components from other protocols. The match allows them adding more - // protocols without worrying about merge conflicts if we change the HTTP implementation. - match url.split_once("://").map(|(proto, _)| proto) { - Some("http") | Some("https") => { - self.download_http_with_retries(&tempfile, url, help_on_error) - } - Some(other) => panic!("unsupported protocol {other} in {url}"), - None => panic!("no protocol in {url}"), - } - t!(std::fs::rename(&tempfile, dest_path)); - } - - fn download_http_with_retries(&self, tempfile: &Path, url: &str, help_on_error: &str) { - println!("downloading {}", url); - // Try curl. If that fails and we are on windows, fallback to PowerShell. - let mut curl = Command::new("curl"); - curl.args(&[ - "-#", - "-y", - "30", - "-Y", - "10", // timeout if speed is < 10 bytes/sec for > 30 seconds - "--connect-timeout", - "30", // timeout if cannot connect within 30 seconds - "--retry", - "3", - "-Sf", - "-o", - ]); - curl.arg(tempfile); - curl.arg(url); - if !self.check_run(&mut curl) { - if self.build.build.contains("windows-msvc") { - println!("Fallback to PowerShell"); - for _ in 0..3 { - if self.try_run(Command::new("PowerShell.exe").args(&[ - "/nologo", - "-Command", - "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;", - &format!( - "(New-Object System.Net.WebClient).DownloadFile('{}', '{}')", - url, tempfile.to_str().expect("invalid UTF-8 not supported with powershell downloads"), - ), - ])) { - return; - } - println!("\nspurious failure, trying again"); - } - } - if !help_on_error.is_empty() { - eprintln!("{}", help_on_error); - } - crate::detail_exit(1); - } - } - - pub(crate) fn unpack(&self, tarball: &Path, dst: &Path, pattern: &str) { - println!("extracting {} to {}", tarball.display(), dst.display()); - if !dst.exists() { - t!(fs::create_dir_all(dst)); - } - - // `tarball` ends with `.tar.xz`; strip that suffix - // example: `rust-dev-nightly-x86_64-unknown-linux-gnu` - let uncompressed_filename = - Path::new(tarball.file_name().expect("missing tarball filename")).file_stem().unwrap(); - let directory_prefix = Path::new(Path::new(uncompressed_filename).file_stem().unwrap()); - - // decompress the file - let data = t!(File::open(tarball)); - let decompressor = XzDecoder::new(BufReader::new(data)); - - let mut tar = tar::Archive::new(decompressor); - for member in t!(tar.entries()) { - let mut member = t!(member); - let original_path = t!(member.path()).into_owned(); - // skip the top-level directory - if original_path == directory_prefix { - continue; - } - let mut short_path = t!(original_path.strip_prefix(directory_prefix)); - if !short_path.starts_with(pattern) { - continue; - } - short_path = t!(short_path.strip_prefix(pattern)); - let dst_path = dst.join(short_path); - self.verbose(&format!("extracting {} to {}", original_path.display(), dst.display())); - if !t!(member.unpack_in(dst)) { - panic!("path traversal attack ??"); - } - let src_path = dst.join(original_path); - if src_path.is_dir() && dst_path.exists() { - continue; - } - t!(fs::rename(src_path, dst_path)); - } - t!(fs::remove_dir_all(dst.join(directory_prefix))); - } - - /// Returns whether the SHA256 checksum of `path` matches `expected`. - pub(crate) fn verify(&self, path: &Path, expected: &str) -> bool { - use sha2::Digest; - - self.verbose(&format!("verifying {}", path.display())); - let mut hasher = sha2::Sha256::new(); - // FIXME: this is ok for rustfmt (4.1 MB large at time of writing), but it seems memory-intensive for rustc and larger components. - // Consider using streaming IO instead? - let contents = if self.config.dry_run { vec![] } else { t!(fs::read(path)) }; - hasher.update(&contents); - let found = hex::encode(hasher.finalize().as_slice()); - let verified = found == expected; - if !verified && !self.config.dry_run { - println!( - "invalid checksum: \n\ - found: {found}\n\ - expected: {expected}", - ); - } - return verified; - } - /// Obtain a compiler at a given stage and for a given host. Explicitly does /// not take `Compiler` since all `Compiler` instances are meant to be /// obtained through this function, since it ensures that they are valid @@ -1292,7 +1054,7 @@ impl<'a> Builder<'a> { /// Note that this returns `None` if LLVM is disabled, or if we're in a /// check build or dry-run, where there's no need to build all of LLVM. fn llvm_config(&self, target: TargetSelection) -> Option { - if self.config.llvm_enabled() && self.kind != Kind::Check && !self.config.dry_run { + if self.config.llvm_enabled() && self.kind != Kind::Check && !self.config.dry_run() { let llvm_config = self.ensure(native::Llvm { target }); if llvm_config.is_file() { return Some(llvm_config); @@ -1301,19 +1063,6 @@ impl<'a> Builder<'a> { None } - /// Convenience wrapper to allow `builder.llvm_link_shared()` instead of `builder.config.llvm_link_shared(&builder)`. - pub(crate) fn llvm_link_shared(&self) -> bool { - Config::llvm_link_shared(self) - } - - pub(crate) fn download_rustc(&self) -> bool { - Config::download_rustc(self) - } - - pub(crate) fn initial_rustfmt(&self) -> Option { - Config::initial_rustfmt(self) - } - /// Prepares an invocation of `cargo` to be run. /// /// This will create a `Command` that represents a pending execution of @@ -1644,7 +1393,7 @@ impl<'a> Builder<'a> { // // Only clear out the directory if we're compiling std; otherwise, we // should let Cargo take care of things for us (via depdep info) - if !self.config.dry_run && mode == Mode::Std && cmd == "build" { + if !self.config.dry_run() && mode == Mode::Std && cmd == "build" { self.clear_if_dirty(&out_dir, &self.rustc(compiler)); } @@ -2142,7 +1891,7 @@ impl<'a> Builder<'a> { (out, dur - deps) }; - if self.config.print_step_timings && !self.config.dry_run { + if self.config.print_step_timings && !self.config.dry_run() { let step_string = format!("{:?}", step); let brace_index = step_string.find("{").unwrap_or(0); let type_string = type_name::(); @@ -2216,7 +1965,7 @@ impl<'a> Builder<'a> { } pub(crate) fn open_in_browser(&self, path: impl AsRef) { - if self.config.dry_run || !self.config.cmd.open() { + if self.config.dry_run() || !self.config.cmd.open() { return; } diff --git a/src/bootstrap/builder/tests.rs b/src/bootstrap/builder/tests.rs index 88bbcc93d072..5f21d2b0067d 100644 --- a/src/bootstrap/builder/tests.rs +++ b/src/bootstrap/builder/tests.rs @@ -1,5 +1,5 @@ use super::*; -use crate::config::{Config, TargetSelection}; +use crate::config::{Config, DryRun, TargetSelection}; use std::thread; fn configure(cmd: &str, host: &[&str], target: &[&str]) -> Config { @@ -10,7 +10,7 @@ fn configure_with_args(cmd: &[String], host: &[&str], target: &[&str]) -> Config let mut config = Config::parse(cmd); // don't save toolstates config.save_toolstates = None; - config.dry_run = true; + config.dry_run = DryRun::SelfCheck; // Ignore most submodules, since we don't need them for a dry run. // But make sure to check out the `doc` and `rust-analyzer` submodules, since some steps need them diff --git a/src/bootstrap/channel.rs b/src/bootstrap/channel.rs index 258352a21a4a..eae81b9fc69c 100644 --- a/src/bootstrap/channel.rs +++ b/src/bootstrap/channel.rs @@ -13,8 +13,10 @@ use crate::util::output; use crate::util::t; use crate::Build; +#[derive(Clone, Default)] pub enum GitInfo { /// This is not a git repository. + #[default] Absent, /// This is a git repository. /// If the info should be used (`ignore_git` is false), this will be @@ -25,6 +27,7 @@ pub enum GitInfo { RecordedForTarball(Info), } +#[derive(Clone)] pub struct Info { pub commit_date: String, pub sha: String, diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 18e780a108d5..54906a4918bc 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -206,7 +206,6 @@ fn copy_third_party_objects( } if target == "x86_64-fortanix-unknown-sgx" - || target.contains("pc-windows-gnullvm") || builder.config.llvm_libunwind(target) == LlvmLibunwind::InTree && (target.contains("linux") || target.contains("fuchsia")) { @@ -447,7 +446,7 @@ fn copy_sanitizers( ) -> Vec { let runtimes: Vec = builder.ensure(native::Sanitizers { target }); - if builder.config.dry_run { + if builder.config.dry_run() { return Vec::new(); } @@ -764,10 +763,10 @@ pub fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetS cargo.env("CFG_LIBDIR_RELATIVE", libdir_relative); - if let Some(ref ver_date) = builder.rust_info.commit_date() { + if let Some(ref ver_date) = builder.rust_info().commit_date() { cargo.env("CFG_VER_DATE", ver_date); } - if let Some(ref ver_hash) = builder.rust_info.sha() { + if let Some(ref ver_hash) = builder.rust_info().sha() { cargo.env("CFG_VER_HASH", ver_hash); } if !builder.unstable_features() { @@ -986,7 +985,7 @@ impl Step for CodegenBackend { compiler.stage, backend, &compiler.host, target )); let files = run_cargo(builder, cargo, vec![], &tmp_stamp, vec![], false); - if builder.config.dry_run { + if builder.config.dry_run() { return; } let mut files = files.into_iter().filter(|f| { @@ -1034,7 +1033,7 @@ fn copy_codegen_backends_to_sysroot( let dst = builder.sysroot_codegen_backends(target_compiler); t!(fs::create_dir_all(&dst), dst); - if builder.config.dry_run { + if builder.config.dry_run() { return; } @@ -1332,7 +1331,7 @@ impl Step for Assemble { if builder.config.rust_codegen_backends.contains(&INTERNER.intern_str("llvm")) { let llvm_config_bin = builder.ensure(native::Llvm { target: target_compiler.host }); - if !builder.config.dry_run { + if !builder.config.dry_run() { let llvm_bin_dir = output(Command::new(llvm_config_bin).arg("--bindir")); let llvm_bin_dir = Path::new(llvm_bin_dir.trim()); @@ -1402,7 +1401,7 @@ pub fn run_cargo( additional_target_deps: Vec<(PathBuf, DependencyType)>, is_check: bool, ) -> Vec { - if builder.config.dry_run { + if builder.config.dry_run() { return Vec::new(); } @@ -1542,7 +1541,7 @@ pub fn stream_cargo( cb: &mut dyn FnMut(CargoMessage<'_>), ) -> bool { let mut cargo = Command::from(cargo); - if builder.config.dry_run { + if builder.config.dry_run() { return true; } // Instruct Cargo to give us json messages on stdout, critically leaving diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index ba50ce9ec242..af004aa50985 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -7,19 +7,18 @@ use std::cell::{Cell, RefCell}; use std::cmp; use std::collections::{HashMap, HashSet}; use std::env; -use std::ffi::OsStr; use std::fmt; use std::fs; use std::path::{Path, PathBuf}; use std::process::Command; use std::str::FromStr; -use crate::builder::{Builder, TaskPath}; +use crate::builder::TaskPath; use crate::cache::{Interned, INTERNER}; -use crate::channel::GitInfo; +use crate::channel::{self, GitInfo}; pub use crate::flags::Subcommand; use crate::flags::{Color, Flags}; -use crate::util::{exe, output, program_out_of_date, t}; +use crate::util::{exe, output, t}; use once_cell::sync::OnceCell; use serde::{Deserialize, Deserializer}; @@ -33,6 +32,17 @@ macro_rules! check_ci_llvm { }; } +#[derive(Clone, Default)] +pub enum DryRun { + /// This isn't a dry run. + #[default] + Disabled, + /// This is a dry run enabled by bootstrap itself, so it can verify that no work is done. + SelfCheck, + /// This is a dry run enabled by the `--dry-run` flag. + UserSelected, +} + /// Global configuration for the entire build and/or bootstrap. /// /// This structure is derived from a combination of both `config.toml` and @@ -84,7 +94,7 @@ pub struct Config { pub jobs: Option, pub cmd: Subcommand, pub incremental: bool, - pub dry_run: bool, + pub dry_run: DryRun, /// `None` if we shouldn't download CI compiler artifacts, or the commit to download if we should. #[cfg(not(test))] download_rustc_commit: Option, @@ -213,6 +223,7 @@ pub struct Config { #[cfg(test)] pub initial_rustfmt: RefCell, pub out: PathBuf, + pub rust_info: channel::GitInfo, } #[derive(Default, Deserialize)] @@ -781,7 +792,7 @@ impl Config { config.llvm_optimize = true; config.ninja_in_file = true; config.llvm_version_check = true; - config.llvm_static_stdcpp = true; + config.llvm_static_stdcpp = false; config.backtrace = true; config.rust_optimize = true; config.rust_optimize_tests = true; @@ -820,7 +831,7 @@ impl Config { config.jobs = flags.jobs.map(threads_from_config); config.cmd = flags.cmd; config.incremental = flags.incremental; - config.dry_run = flags.dry_run; + config.dry_run = if flags.dry_run { DryRun::UserSelected } else { DryRun::Disabled }; config.keep_stage = flags.keep_stage; config.keep_stage_std = flags.keep_stage_std; config.color = flags.color; @@ -965,7 +976,7 @@ impl Config { .unwrap_or_else(|| config.out.join(config.build.triple).join("stage0/bin/cargo")); // NOTE: it's important this comes *after* we set `initial_rustc` just above. - if config.dry_run { + if config.dry_run() { let dir = config.out.join("tmp-dry-run"); t!(fs::create_dir_all(&dir)); config.out = dir; @@ -1193,7 +1204,7 @@ impl Config { config.rust_codegen_units_std = rust.codegen_units_std.map(threads_from_config); config.rust_profile_use = flags.rust_profile_use.or(rust.profile_use); config.rust_profile_generate = flags.rust_profile_generate.or(rust.profile_generate); - config.download_rustc_commit = download_ci_rustc_commit(&config, rust.download_rustc); + config.download_rustc_commit = config.download_ci_rustc_commit(rust.download_rustc); config.rust_lto = rust .lto @@ -1315,6 +1326,7 @@ impl Config { let default = config.channel == "dev"; config.ignore_git = ignore_git.unwrap_or(default); + config.rust_info = GitInfo::new(config.ignore_git, &config.src); let download_rustc = config.download_rustc_commit.is_some(); // See https://github.com/rust-lang/compiler-team/issues/326 @@ -1372,6 +1384,13 @@ impl Config { config } + pub(crate) fn dry_run(&self) -> bool { + match self.dry_run { + DryRun::Disabled => false, + DryRun::SelfCheck | DryRun::UserSelected => true, + } + } + /// A git invocation which runs inside the source directory. /// /// Use this rather than `Command::new("git")` in order to support out-of-tree builds. @@ -1383,8 +1402,8 @@ impl Config { /// Bootstrap embeds a version number into the name of shared libraries it uploads in CI. /// Return the version it would have used for the given commit. - pub(crate) fn artifact_version_part(&self, builder: &Builder<'_>, commit: &str) -> String { - let (channel, version) = if builder.rust_info.is_managed_git_subrepository() { + pub(crate) fn artifact_version_part(&self, commit: &str) -> String { + let (channel, version) = if self.rust_info.is_managed_git_subrepository() { let mut channel = self.git(); channel.arg("show").arg(format!("{}:src/ci/channel", commit)); let channel = output(&mut channel); @@ -1393,14 +1412,14 @@ impl Config { let version = output(&mut version); (channel.trim().to_owned(), version.trim().to_owned()) } else { - let channel = fs::read_to_string(builder.src.join("src/ci/channel")); - let version = fs::read_to_string(builder.src.join("src/version")); + let channel = fs::read_to_string(self.src.join("src/ci/channel")); + let version = fs::read_to_string(self.src.join("src/version")); match (channel, version) { (Ok(channel), Ok(version)) => { (channel.trim().to_owned(), version.trim().to_owned()) } (channel, version) => { - let src = builder.src.display(); + let src = self.src.display(); eprintln!("error: failed to determine artifact channel and/or version"); eprintln!( "help: consider using a git checkout or ensure these files are readable" @@ -1459,17 +1478,17 @@ impl Config { /// /// If `false`, llvm should be linked statically. /// This is computed on demand since LLVM might have to first be downloaded from CI. - pub(crate) fn llvm_link_shared(builder: &Builder<'_>) -> bool { - let mut opt = builder.config.llvm_link_shared.get(); - if opt.is_none() && builder.config.dry_run { + pub(crate) fn llvm_link_shared(&self) -> bool { + let mut opt = self.llvm_link_shared.get(); + if opt.is_none() && self.dry_run() { // just assume static for now - dynamic linking isn't supported on all platforms return false; } let llvm_link_shared = *opt.get_or_insert_with(|| { - if builder.config.llvm_from_ci { - crate::native::maybe_download_ci_llvm(builder); - let ci_llvm = builder.config.ci_llvm_root(); + if self.llvm_from_ci { + self.maybe_download_ci_llvm(); + let ci_llvm = self.ci_llvm_root(); let link_type = t!( std::fs::read_to_string(ci_llvm.join("link-type.txt")), format!("CI llvm missing: {}", ci_llvm.display()) @@ -1481,36 +1500,36 @@ impl Config { false } }); - builder.config.llvm_link_shared.set(opt); + self.llvm_link_shared.set(opt); llvm_link_shared } /// Return whether we will use a downloaded, pre-compiled version of rustc, or just build from source. - pub(crate) fn download_rustc(builder: &Builder<'_>) -> bool { + pub(crate) fn download_rustc(&self) -> bool { static DOWNLOAD_RUSTC: OnceCell = OnceCell::new(); - if builder.config.dry_run && DOWNLOAD_RUSTC.get().is_none() { + if self.dry_run() && DOWNLOAD_RUSTC.get().is_none() { // avoid trying to actually download the commit return false; } - *DOWNLOAD_RUSTC.get_or_init(|| match &builder.config.download_rustc_commit { + *DOWNLOAD_RUSTC.get_or_init(|| match &self.download_rustc_commit { None => false, Some(commit) => { - download_ci_rustc(builder, commit); + self.download_ci_rustc(commit); true } }) } - pub(crate) fn initial_rustfmt(builder: &Builder<'_>) -> Option { - match &mut *builder.config.initial_rustfmt.borrow_mut() { + pub(crate) fn initial_rustfmt(&self) -> Option { + match &mut *self.initial_rustfmt.borrow_mut() { RustfmtState::SystemToolchain(p) | RustfmtState::Downloaded(p) => Some(p.clone()), RustfmtState::Unavailable => None, r @ RustfmtState::LazyEvaluated => { - if builder.config.dry_run { + if self.dry_run() { return Some(PathBuf::new()); } - let path = maybe_download_rustfmt(builder); + let path = self.maybe_download_rustfmt(); *r = if let Some(p) = &path { RustfmtState::Downloaded(p.clone()) } else { @@ -1521,8 +1540,10 @@ impl Config { } } - pub fn verbose(&self) -> bool { - self.verbose > 0 + pub fn verbose(&self, msg: &str) { + if self.verbose > 0 { + println!("{}", msg); + } } pub fn sanitizers_enabled(&self, target: TargetSelection) -> bool { @@ -1560,6 +1581,66 @@ impl Config { pub fn submodules(&self, rust_info: &GitInfo) -> bool { self.submodules.unwrap_or(rust_info.is_managed_git_subrepository()) } + + /// Returns the commit to download, or `None` if we shouldn't download CI artifacts. + fn download_ci_rustc_commit(&self, download_rustc: Option) -> Option { + // If `download-rustc` is not set, default to rebuilding. + let if_unchanged = match download_rustc { + None | Some(StringOrBool::Bool(false)) => return None, + Some(StringOrBool::Bool(true)) => false, + Some(StringOrBool::String(s)) if s == "if-unchanged" => true, + Some(StringOrBool::String(other)) => { + panic!("unrecognized option for download-rustc: {}", other) + } + }; + + // Handle running from a directory other than the top level + let top_level = output(self.git().args(&["rev-parse", "--show-toplevel"])); + let top_level = top_level.trim_end(); + let compiler = format!("{top_level}/compiler/"); + let library = format!("{top_level}/library/"); + + // Look for a version to compare to based on the current commit. + // Only commits merged by bors will have CI artifacts. + let merge_base = output( + self.git() + .arg("rev-list") + .arg(format!("--author={}", self.stage0_metadata.config.git_merge_commit_email)) + .args(&["-n1", "--first-parent", "HEAD"]), + ); + let commit = merge_base.trim_end(); + if commit.is_empty() { + println!("error: could not find commit hash for downloading rustc"); + println!("help: maybe your repository history is too shallow?"); + println!("help: consider disabling `download-rustc`"); + println!("help: or fetch enough history to include one upstream commit"); + crate::detail_exit(1); + } + + // Warn if there were changes to the compiler or standard library since the ancestor commit. + let has_changes = !t!(self + .git() + .args(&["diff-index", "--quiet", &commit, "--", &compiler, &library]) + .status()) + .success(); + if has_changes { + if if_unchanged { + if self.verbose > 0 { + println!( + "warning: saw changes to compiler/ or library/ since {commit}; \ + ignoring `download-rustc`" + ); + } + return None; + } + println!( + "warning: `download-rustc` is enabled, but there are changes to \ + compiler/ or library/" + ); + } + + Some(commit.to_string()) + } } fn set(field: &mut T, val: Option) { @@ -1574,204 +1655,3 @@ fn threads_from_config(v: u32) -> u32 { n => n, } } - -/// Returns the commit to download, or `None` if we shouldn't download CI artifacts. -fn download_ci_rustc_commit( - config: &Config, - download_rustc: Option, -) -> Option { - // If `download-rustc` is not set, default to rebuilding. - let if_unchanged = match download_rustc { - None | Some(StringOrBool::Bool(false)) => return None, - Some(StringOrBool::Bool(true)) => false, - Some(StringOrBool::String(s)) if s == "if-unchanged" => true, - Some(StringOrBool::String(other)) => { - panic!("unrecognized option for download-rustc: {}", other) - } - }; - - // Handle running from a directory other than the top level - let top_level = output(config.git().args(&["rev-parse", "--show-toplevel"])); - let top_level = top_level.trim_end(); - let compiler = format!("{top_level}/compiler/"); - let library = format!("{top_level}/library/"); - - // Look for a version to compare to based on the current commit. - // Only commits merged by bors will have CI artifacts. - let merge_base = output( - config - .git() - .arg("rev-list") - .arg(format!("--author={}", config.stage0_metadata.config.git_merge_commit_email)) - .args(&["-n1", "--first-parent", "HEAD"]), - ); - let commit = merge_base.trim_end(); - if commit.is_empty() { - println!("error: could not find commit hash for downloading rustc"); - println!("help: maybe your repository history is too shallow?"); - println!("help: consider disabling `download-rustc`"); - println!("help: or fetch enough history to include one upstream commit"); - crate::detail_exit(1); - } - - // Warn if there were changes to the compiler or standard library since the ancestor commit. - let has_changes = !t!(config - .git() - .args(&["diff-index", "--quiet", &commit, "--", &compiler, &library]) - .status()) - .success(); - if has_changes { - if if_unchanged { - if config.verbose > 0 { - println!( - "warning: saw changes to compiler/ or library/ since {commit}; \ - ignoring `download-rustc`" - ); - } - return None; - } - println!( - "warning: `download-rustc` is enabled, but there are changes to \ - compiler/ or library/" - ); - } - - Some(commit.to_string()) -} - -fn maybe_download_rustfmt(builder: &Builder<'_>) -> Option { - let RustfmtMetadata { date, version } = builder.config.stage0_metadata.rustfmt.as_ref()?; - let channel = format!("{version}-{date}"); - - let host = builder.config.build; - let rustfmt_path = builder.config.initial_rustc.with_file_name(exe("rustfmt", host)); - let bin_root = builder.config.out.join(host.triple).join("stage0"); - let rustfmt_stamp = bin_root.join(".rustfmt-stamp"); - if rustfmt_path.exists() && !program_out_of_date(&rustfmt_stamp, &channel) { - return Some(rustfmt_path); - } - - let filename = format!("rustfmt-{version}-{build}.tar.xz", build = host.triple); - download_component(builder, DownloadSource::Dist, filename, "rustfmt-preview", &date, "stage0"); - - builder.fix_bin_or_dylib(&bin_root.join("bin").join("rustfmt")); - builder.fix_bin_or_dylib(&bin_root.join("bin").join("cargo-fmt")); - - builder.create(&rustfmt_stamp, &channel); - Some(rustfmt_path) -} - -fn download_ci_rustc(builder: &Builder<'_>, commit: &str) { - builder.verbose(&format!("using downloaded stage2 artifacts from CI (commit {commit})")); - let version = builder.config.artifact_version_part(builder, commit); - let host = builder.config.build.triple; - let bin_root = builder.out.join(host).join("ci-rustc"); - let rustc_stamp = bin_root.join(".rustc-stamp"); - - if !bin_root.join("bin").join("rustc").exists() || program_out_of_date(&rustc_stamp, commit) { - if bin_root.exists() { - t!(fs::remove_dir_all(&bin_root)); - } - let filename = format!("rust-std-{version}-{host}.tar.xz"); - let pattern = format!("rust-std-{host}"); - download_ci_component(builder, filename, &pattern, commit); - let filename = format!("rustc-{version}-{host}.tar.xz"); - download_ci_component(builder, filename, "rustc", commit); - // download-rustc doesn't need its own cargo, it can just use beta's. - let filename = format!("rustc-dev-{version}-{host}.tar.xz"); - download_ci_component(builder, filename, "rustc-dev", commit); - - builder.fix_bin_or_dylib(&bin_root.join("bin").join("rustc")); - builder.fix_bin_or_dylib(&bin_root.join("bin").join("rustdoc")); - let lib_dir = bin_root.join("lib"); - for lib in t!(fs::read_dir(&lib_dir), lib_dir.display().to_string()) { - let lib = t!(lib); - if lib.path().extension() == Some(OsStr::new("so")) { - builder.fix_bin_or_dylib(&lib.path()); - } - } - t!(fs::write(rustc_stamp, commit)); - } -} - -pub(crate) enum DownloadSource { - CI, - Dist, -} - -/// Download a single component of a CI-built toolchain (not necessarily a published nightly). -// NOTE: intentionally takes an owned string to avoid downloading multiple times by accident -fn download_ci_component(builder: &Builder<'_>, filename: String, prefix: &str, commit: &str) { - download_component(builder, DownloadSource::CI, filename, prefix, commit, "ci-rustc") -} - -fn download_component( - builder: &Builder<'_>, - mode: DownloadSource, - filename: String, - prefix: &str, - key: &str, - destination: &str, -) { - let cache_dst = builder.out.join("cache"); - let cache_dir = cache_dst.join(key); - if !cache_dir.exists() { - t!(fs::create_dir_all(&cache_dir)); - } - - let bin_root = builder.out.join(builder.config.build.triple).join(destination); - let tarball = cache_dir.join(&filename); - let (base_url, url, should_verify) = match mode { - DownloadSource::CI => ( - builder.config.stage0_metadata.config.artifacts_server.clone(), - format!("{key}/{filename}"), - false, - ), - DownloadSource::Dist => { - let dist_server = env::var("RUSTUP_DIST_SERVER") - .unwrap_or(builder.config.stage0_metadata.config.dist_server.to_string()); - // NOTE: make `dist` part of the URL because that's how it's stored in src/stage0.json - (dist_server, format!("dist/{key}/{filename}"), true) - } - }; - - // For the beta compiler, put special effort into ensuring the checksums are valid. - // FIXME: maybe we should do this for download-rustc as well? but it would be a pain to update - // this on each and every nightly ... - let checksum = if should_verify { - let error = format!( - "src/stage0.json doesn't contain a checksum for {url}. \ - Pre-built artifacts might not be available for this \ - target at this time, see https://doc.rust-lang.org/nightly\ - /rustc/platform-support.html for more information." - ); - let sha256 = builder.config.stage0_metadata.checksums_sha256.get(&url).expect(&error); - if tarball.exists() { - if builder.verify(&tarball, sha256) { - builder.unpack(&tarball, &bin_root, prefix); - return; - } else { - builder.verbose(&format!( - "ignoring cached file {} due to failed verification", - tarball.display() - )); - builder.remove(&tarball); - } - } - Some(sha256) - } else if tarball.exists() { - builder.unpack(&tarball, &bin_root, prefix); - return; - } else { - None - }; - - builder.download_component(&format!("{base_url}/{url}"), &tarball, ""); - if let Some(sha256) = checksum { - if !builder.verify(&tarball, sha256) { - panic!("failed to verify {}", tarball.display()); - } - } - - builder.unpack(&tarball, &bin_root, prefix); -} diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index 110a3ee4918d..aacd2c7eab98 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -924,13 +924,13 @@ impl Step for PlainSourceTarball { // Create the version file builder.create(&plain_dst_src.join("version"), &builder.rust_version()); - if let Some(info) = builder.rust_info.info() { + if let Some(info) = builder.rust_info().info() { channel::write_commit_hash_file(&plain_dst_src, &info.sha); channel::write_commit_info_file(&plain_dst_src, info); } // If we're building from git sources, we need to vendor a complete distribution. - if builder.rust_info.is_managed_git_subrepository() { + if builder.rust_info().is_managed_git_subrepository() { // Ensure we have the submodules checked out. builder.update_submodule(Path::new("src/tools/rust-analyzer")); @@ -945,7 +945,7 @@ impl Step for PlainSourceTarball { .arg(builder.src.join("./src/bootstrap/Cargo.toml")) .current_dir(&plain_dst_src); - let config = if !builder.config.dry_run { + let config = if !builder.config.dry_run() { t!(String::from_utf8(t!(cmd.output()).stdout)) } else { String::new() @@ -1386,7 +1386,7 @@ impl Step for Extended { let etc = builder.src.join("src/etc/installer"); // Avoid producing tarballs during a dry run. - if builder.config.dry_run { + if builder.config.dry_run() { return; } @@ -1818,7 +1818,7 @@ impl Step for Extended { let _time = timeit(builder); builder.run(&mut cmd); - if !builder.config.dry_run { + if !builder.config.dry_run() { t!(fs::rename(exe.join(&filename), distdir(builder).join(&filename))); } } @@ -1882,12 +1882,12 @@ fn maybe_install_llvm(builder: &Builder<'_>, target: TargetSelection, dst_libdir if llvm_dylib_path.exists() { builder.install(&llvm_dylib_path, dst_libdir, 0o644); } - !builder.config.dry_run + !builder.config.dry_run() } else if let Ok(llvm_config) = crate::native::prebuilt_llvm_config(builder, target) { let mut cmd = Command::new(llvm_config); cmd.arg("--libfiles"); builder.verbose(&format!("running {:?}", cmd)); - let files = if builder.config.dry_run { "".into() } else { output(&mut cmd) }; + let files = if builder.config.dry_run() { "".into() } else { output(&mut cmd) }; let build_llvm_out = &builder.llvm_out(builder.config.build); let target_llvm_out = &builder.llvm_out(target); for file in files.trim_end().split(' ') { @@ -1899,7 +1899,7 @@ fn maybe_install_llvm(builder: &Builder<'_>, target: TargetSelection, dst_libdir }; builder.install(&file, dst_libdir, 0o644); } - !builder.config.dry_run + !builder.config.dry_run() } else { false } diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index 280e232ca2dd..3180a12c85be 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -151,7 +151,7 @@ impl Step for RustbookSrc { let index = out.join("index.html"); let rustbook = builder.tool_exe(Tool::Rustbook); let mut rustbook_cmd = builder.tool_cmd(Tool::Rustbook); - if builder.config.dry_run || up_to_date(&src, &index) && up_to_date(&rustbook, &index) { + if builder.config.dry_run() || up_to_date(&src, &index) && up_to_date(&rustbook, &index) { return; } builder.info(&format!("Rustbook ({}) - {}", target, name)); @@ -331,8 +331,8 @@ impl Step for Standalone { && up_to_date(&footer, &html) && up_to_date(&favicon, &html) && up_to_date(&full_toc, &html) - && (builder.config.dry_run || up_to_date(&version_info, &html)) - && (builder.config.dry_run || up_to_date(&rustdoc, &html)) + && (builder.config.dry_run() || up_to_date(&version_info, &html)) + && (builder.config.dry_run() || up_to_date(&rustdoc, &html)) { continue; } @@ -402,11 +402,11 @@ impl Step for SharedAssets { let version_input = builder.src.join("src").join("doc").join("version_info.html.template"); let version_info = out.join("version_info.html"); - if !builder.config.dry_run && !up_to_date(&version_input, &version_info) { + if !builder.config.dry_run() && !up_to_date(&version_input, &version_info) { let info = t!(fs::read_to_string(&version_input)) .replace("VERSION", &builder.rust_release()) - .replace("SHORT_HASH", builder.rust_info.sha_short().unwrap_or("")) - .replace("STAMP", builder.rust_info.sha().unwrap_or("")); + .replace("SHORT_HASH", builder.rust_info().sha_short().unwrap_or("")) + .replace("STAMP", builder.rust_info().sha().unwrap_or("")); t!(fs::write(&version_info, &info)); } @@ -900,7 +900,7 @@ impl Step for UnstableBookGen { } fn symlink_dir_force(config: &Config, src: &Path, dst: &Path) -> io::Result<()> { - if config.dry_run { + if config.dry_run() { return Ok(()); } if let Ok(m) = fs::symlink_metadata(dst) { @@ -965,7 +965,7 @@ impl Step for RustcBook { cmd.arg("--rustc"); cmd.arg(&rustc); cmd.arg("--rustc-target").arg(&self.target.rustc_target_arg()); - if builder.config.verbose() { + if builder.is_verbose() { cmd.arg("--verbose"); } if self.validate { diff --git a/src/bootstrap/download.rs b/src/bootstrap/download.rs new file mode 100644 index 000000000000..d0f389df9734 --- /dev/null +++ b/src/bootstrap/download.rs @@ -0,0 +1,519 @@ +use std::{ + env, + ffi::{OsStr, OsString}, + fs::{self, File}, + io::{BufRead, BufReader, ErrorKind}, + path::{Path, PathBuf}, + process::{Command, Stdio}, +}; + +use once_cell::sync::OnceCell; +use xz2::bufread::XzDecoder; + +use crate::{ + config::RustfmtMetadata, + native::detect_llvm_sha, + t, + util::{check_run, exe, program_out_of_date, try_run}, + Config, +}; + +/// Generic helpers that are useful anywhere in bootstrap. +impl Config { + pub fn is_verbose(&self) -> bool { + self.verbose > 0 + } + + pub(crate) fn create(&self, path: &Path, s: &str) { + if self.dry_run() { + return; + } + t!(fs::write(path, s)); + } + + pub(crate) fn remove(&self, f: &Path) { + if self.dry_run() { + return; + } + fs::remove_file(f).unwrap_or_else(|_| panic!("failed to remove {:?}", f)); + } + + /// Create a temporary directory in `out` and return its path. + /// + /// NOTE: this temporary directory is shared between all steps; + /// if you need an empty directory, create a new subdirectory inside it. + pub(crate) fn tempdir(&self) -> PathBuf { + let tmp = self.out.join("tmp"); + t!(fs::create_dir_all(&tmp)); + tmp + } + + /// Runs a command, printing out nice contextual information if it fails. + /// Exits if the command failed to execute at all, otherwise returns its + /// `status.success()`. + pub(crate) fn try_run(&self, cmd: &mut Command) -> bool { + if self.dry_run() { + return true; + } + self.verbose(&format!("running: {:?}", cmd)); + try_run(cmd, self.is_verbose()) + } + + /// Runs a command, printing out nice contextual information if it fails. + /// Returns false if do not execute at all, otherwise returns its + /// `status.success()`. + pub(crate) fn check_run(&self, cmd: &mut Command) -> bool { + if self.dry_run() { + return true; + } + self.verbose(&format!("running: {:?}", cmd)); + check_run(cmd, self.is_verbose()) + } + + /// Modifies the interpreter section of 'fname' to fix the dynamic linker, + /// or the RPATH section, to fix the dynamic library search path + /// + /// This is only required on NixOS and uses the PatchELF utility to + /// change the interpreter/RPATH of ELF executables. + /// + /// Please see https://nixos.org/patchelf.html for more information + fn fix_bin_or_dylib(&self, fname: &Path) { + // FIXME: cache NixOS detection? + match Command::new("uname").arg("-s").stderr(Stdio::inherit()).output() { + Err(_) => return, + Ok(output) if !output.status.success() => return, + Ok(output) => { + let mut s = output.stdout; + if s.last() == Some(&b'\n') { + s.pop(); + } + if s != b"Linux" { + return; + } + } + } + + // If the user has asked binaries to be patched for Nix, then + // don't check for NixOS or `/lib`, just continue to the patching. + // NOTE: this intentionally comes after the Linux check: + // - patchelf only works with ELF files, so no need to run it on Mac or Windows + // - On other Unix systems, there is no stable syscall interface, so Nix doesn't manage the global libc. + if !self.patch_binaries_for_nix { + // Use `/etc/os-release` instead of `/etc/NIXOS`. + // The latter one does not exist on NixOS when using tmpfs as root. + const NIX_IDS: &[&str] = &["ID=nixos", "ID='nixos'", "ID=\"nixos\""]; + let os_release = match File::open("/etc/os-release") { + Err(e) if e.kind() == ErrorKind::NotFound => return, + Err(e) => panic!("failed to access /etc/os-release: {}", e), + Ok(f) => f, + }; + if !BufReader::new(os_release).lines().any(|l| NIX_IDS.contains(&t!(l).trim())) { + return; + } + if Path::new("/lib").exists() { + return; + } + } + + // At this point we're pretty sure the user is running NixOS or using Nix + println!("info: you seem to be using Nix. Attempting to patch {}", fname.display()); + + // Only build `.nix-deps` once. + static NIX_DEPS_DIR: OnceCell = OnceCell::new(); + let mut nix_build_succeeded = true; + let nix_deps_dir = NIX_DEPS_DIR.get_or_init(|| { + // Run `nix-build` to "build" each dependency (which will likely reuse + // the existing `/nix/store` copy, or at most download a pre-built copy). + // + // Importantly, we create a gc-root called `.nix-deps` in the `build/` + // directory, but still reference the actual `/nix/store` path in the rpath + // as it makes it significantly more robust against changes to the location of + // the `.nix-deps` location. + // + // bintools: Needed for the path of `ld-linux.so` (via `nix-support/dynamic-linker`). + // zlib: Needed as a system dependency of `libLLVM-*.so`. + // patchelf: Needed for patching ELF binaries (see doc comment above). + let nix_deps_dir = self.out.join(".nix-deps"); + const NIX_EXPR: &str = " + with (import {}); + symlinkJoin { + name = \"rust-stage0-dependencies\"; + paths = [ + zlib + patchelf + stdenv.cc.bintools + ]; + } + "; + nix_build_succeeded = self.try_run(Command::new("nix-build").args(&[ + Path::new("-E"), + Path::new(NIX_EXPR), + Path::new("-o"), + &nix_deps_dir, + ])); + nix_deps_dir + }); + if !nix_build_succeeded { + return; + } + + let mut patchelf = Command::new(nix_deps_dir.join("bin/patchelf")); + let rpath_entries = { + // ORIGIN is a relative default, all binary and dynamic libraries we ship + // appear to have this (even when `../lib` is redundant). + // NOTE: there are only two paths here, delimited by a `:` + let mut entries = OsString::from("$ORIGIN/../lib:"); + entries.push(t!(fs::canonicalize(nix_deps_dir))); + entries.push("/lib"); + entries + }; + patchelf.args(&[OsString::from("--set-rpath"), rpath_entries]); + if !fname.extension().map_or(false, |ext| ext == "so") { + // Finally, set the correct .interp for binaries + let dynamic_linker_path = nix_deps_dir.join("nix-support/dynamic-linker"); + // FIXME: can we support utf8 here? `args` doesn't accept Vec, only OsString ... + let dynamic_linker = t!(String::from_utf8(t!(fs::read(dynamic_linker_path)))); + patchelf.args(&["--set-interpreter", dynamic_linker.trim_end()]); + } + + self.try_run(patchelf.arg(fname)); + } + + fn download_file(&self, url: &str, dest_path: &Path, help_on_error: &str) { + self.verbose(&format!("download {url}")); + // Use a temporary file in case we crash while downloading, to avoid a corrupt download in cache/. + let tempfile = self.tempdir().join(dest_path.file_name().unwrap()); + // While bootstrap itself only supports http and https downloads, downstream forks might + // need to download components from other protocols. The match allows them adding more + // protocols without worrying about merge conflicts if we change the HTTP implementation. + match url.split_once("://").map(|(proto, _)| proto) { + Some("http") | Some("https") => { + self.download_http_with_retries(&tempfile, url, help_on_error) + } + Some(other) => panic!("unsupported protocol {other} in {url}"), + None => panic!("no protocol in {url}"), + } + t!(std::fs::rename(&tempfile, dest_path)); + } + + fn download_http_with_retries(&self, tempfile: &Path, url: &str, help_on_error: &str) { + println!("downloading {}", url); + // Try curl. If that fails and we are on windows, fallback to PowerShell. + let mut curl = Command::new("curl"); + curl.args(&[ + "-#", + "-y", + "30", + "-Y", + "10", // timeout if speed is < 10 bytes/sec for > 30 seconds + "--connect-timeout", + "30", // timeout if cannot connect within 30 seconds + "--retry", + "3", + "-Sf", + "-o", + ]); + curl.arg(tempfile); + curl.arg(url); + if !self.check_run(&mut curl) { + if self.build.contains("windows-msvc") { + println!("Fallback to PowerShell"); + for _ in 0..3 { + if self.try_run(Command::new("PowerShell.exe").args(&[ + "/nologo", + "-Command", + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;", + &format!( + "(New-Object System.Net.WebClient).DownloadFile('{}', '{}')", + url, tempfile.to_str().expect("invalid UTF-8 not supported with powershell downloads"), + ), + ])) { + return; + } + println!("\nspurious failure, trying again"); + } + } + if !help_on_error.is_empty() { + eprintln!("{}", help_on_error); + } + crate::detail_exit(1); + } + } + + fn unpack(&self, tarball: &Path, dst: &Path, pattern: &str) { + println!("extracting {} to {}", tarball.display(), dst.display()); + if !dst.exists() { + t!(fs::create_dir_all(dst)); + } + + // `tarball` ends with `.tar.xz`; strip that suffix + // example: `rust-dev-nightly-x86_64-unknown-linux-gnu` + let uncompressed_filename = + Path::new(tarball.file_name().expect("missing tarball filename")).file_stem().unwrap(); + let directory_prefix = Path::new(Path::new(uncompressed_filename).file_stem().unwrap()); + + // decompress the file + let data = t!(File::open(tarball)); + let decompressor = XzDecoder::new(BufReader::new(data)); + + let mut tar = tar::Archive::new(decompressor); + for member in t!(tar.entries()) { + let mut member = t!(member); + let original_path = t!(member.path()).into_owned(); + // skip the top-level directory + if original_path == directory_prefix { + continue; + } + let mut short_path = t!(original_path.strip_prefix(directory_prefix)); + if !short_path.starts_with(pattern) { + continue; + } + short_path = t!(short_path.strip_prefix(pattern)); + let dst_path = dst.join(short_path); + self.verbose(&format!("extracting {} to {}", original_path.display(), dst.display())); + if !t!(member.unpack_in(dst)) { + panic!("path traversal attack ??"); + } + let src_path = dst.join(original_path); + if src_path.is_dir() && dst_path.exists() { + continue; + } + t!(fs::rename(src_path, dst_path)); + } + t!(fs::remove_dir_all(dst.join(directory_prefix))); + } + + /// Returns whether the SHA256 checksum of `path` matches `expected`. + fn verify(&self, path: &Path, expected: &str) -> bool { + use sha2::Digest; + + self.verbose(&format!("verifying {}", path.display())); + let mut hasher = sha2::Sha256::new(); + // FIXME: this is ok for rustfmt (4.1 MB large at time of writing), but it seems memory-intensive for rustc and larger components. + // Consider using streaming IO instead? + let contents = if self.dry_run() { vec![] } else { t!(fs::read(path)) }; + hasher.update(&contents); + let found = hex::encode(hasher.finalize().as_slice()); + let verified = found == expected; + if !verified && !self.dry_run() { + println!( + "invalid checksum: \n\ + found: {found}\n\ + expected: {expected}", + ); + } + return verified; + } +} + +enum DownloadSource { + CI, + Dist, +} + +/// Functions that are only ever called once, but named for clarify and to avoid thousand-line functions. +impl Config { + pub(crate) fn maybe_download_rustfmt(&self) -> Option { + let RustfmtMetadata { date, version } = self.stage0_metadata.rustfmt.as_ref()?; + let channel = format!("{version}-{date}"); + + let host = self.build; + let rustfmt_path = self.initial_rustc.with_file_name(exe("rustfmt", host)); + let bin_root = self.out.join(host.triple).join("stage0"); + let rustfmt_stamp = bin_root.join(".rustfmt-stamp"); + if rustfmt_path.exists() && !program_out_of_date(&rustfmt_stamp, &channel) { + return Some(rustfmt_path); + } + + let filename = format!("rustfmt-{version}-{build}.tar.xz", build = host.triple); + self.download_component(DownloadSource::Dist, filename, "rustfmt-preview", &date, "stage0"); + + self.fix_bin_or_dylib(&bin_root.join("bin").join("rustfmt")); + self.fix_bin_or_dylib(&bin_root.join("bin").join("cargo-fmt")); + + self.create(&rustfmt_stamp, &channel); + Some(rustfmt_path) + } + + pub(crate) fn download_ci_rustc(&self, commit: &str) { + self.verbose(&format!("using downloaded stage2 artifacts from CI (commit {commit})")); + let version = self.artifact_version_part(commit); + let host = self.build.triple; + let bin_root = self.out.join(host).join("ci-rustc"); + let rustc_stamp = bin_root.join(".rustc-stamp"); + + if !bin_root.join("bin").join("rustc").exists() || program_out_of_date(&rustc_stamp, commit) + { + if bin_root.exists() { + t!(fs::remove_dir_all(&bin_root)); + } + let filename = format!("rust-std-{version}-{host}.tar.xz"); + let pattern = format!("rust-std-{host}"); + self.download_ci_component(filename, &pattern, commit); + let filename = format!("rustc-{version}-{host}.tar.xz"); + self.download_ci_component(filename, "rustc", commit); + // download-rustc doesn't need its own cargo, it can just use beta's. + let filename = format!("rustc-dev-{version}-{host}.tar.xz"); + self.download_ci_component(filename, "rustc-dev", commit); + let filename = format!("rust-src-{version}.tar.xz"); + self.download_ci_component(filename, "rust-src", commit); + + self.fix_bin_or_dylib(&bin_root.join("bin").join("rustc")); + self.fix_bin_or_dylib(&bin_root.join("bin").join("rustdoc")); + let lib_dir = bin_root.join("lib"); + for lib in t!(fs::read_dir(&lib_dir), lib_dir.display().to_string()) { + let lib = t!(lib); + if lib.path().extension() == Some(OsStr::new("so")) { + self.fix_bin_or_dylib(&lib.path()); + } + } + t!(fs::write(rustc_stamp, commit)); + } + } + + /// Download a single component of a CI-built toolchain (not necessarily a published nightly). + // NOTE: intentionally takes an owned string to avoid downloading multiple times by accident + fn download_ci_component(&self, filename: String, prefix: &str, commit: &str) { + Self::download_component(self, DownloadSource::CI, filename, prefix, commit, "ci-rustc") + } + + fn download_component( + &self, + mode: DownloadSource, + filename: String, + prefix: &str, + key: &str, + destination: &str, + ) { + let cache_dst = self.out.join("cache"); + let cache_dir = cache_dst.join(key); + if !cache_dir.exists() { + t!(fs::create_dir_all(&cache_dir)); + } + + let bin_root = self.out.join(self.build.triple).join(destination); + let tarball = cache_dir.join(&filename); + let (base_url, url, should_verify) = match mode { + DownloadSource::CI => ( + self.stage0_metadata.config.artifacts_server.clone(), + format!("{key}/{filename}"), + false, + ), + DownloadSource::Dist => { + let dist_server = env::var("RUSTUP_DIST_SERVER") + .unwrap_or(self.stage0_metadata.config.dist_server.to_string()); + // NOTE: make `dist` part of the URL because that's how it's stored in src/stage0.json + (dist_server, format!("dist/{key}/{filename}"), true) + } + }; + + // For the beta compiler, put special effort into ensuring the checksums are valid. + // FIXME: maybe we should do this for download-rustc as well? but it would be a pain to update + // this on each and every nightly ... + let checksum = if should_verify { + let error = format!( + "src/stage0.json doesn't contain a checksum for {url}. \ + Pre-built artifacts might not be available for this \ + target at this time, see https://doc.rust-lang.org/nightly\ + /rustc/platform-support.html for more information." + ); + let sha256 = self.stage0_metadata.checksums_sha256.get(&url).expect(&error); + if tarball.exists() { + if self.verify(&tarball, sha256) { + self.unpack(&tarball, &bin_root, prefix); + return; + } else { + self.verbose(&format!( + "ignoring cached file {} due to failed verification", + tarball.display() + )); + self.remove(&tarball); + } + } + Some(sha256) + } else if tarball.exists() { + self.unpack(&tarball, &bin_root, prefix); + return; + } else { + None + }; + + self.download_file(&format!("{base_url}/{url}"), &tarball, ""); + if let Some(sha256) = checksum { + if !self.verify(&tarball, sha256) { + panic!("failed to verify {}", tarball.display()); + } + } + + self.unpack(&tarball, &bin_root, prefix); + } + + pub(crate) fn maybe_download_ci_llvm(&self) { + if !self.llvm_from_ci { + return; + } + let llvm_root = self.ci_llvm_root(); + let llvm_stamp = llvm_root.join(".llvm-stamp"); + let llvm_sha = detect_llvm_sha(&self, self.rust_info.is_managed_git_subrepository()); + let key = format!("{}{}", llvm_sha, self.llvm_assertions); + if program_out_of_date(&llvm_stamp, &key) && !self.dry_run() { + self.download_ci_llvm(&llvm_sha); + for entry in t!(fs::read_dir(llvm_root.join("bin"))) { + self.fix_bin_or_dylib(&t!(entry).path()); + } + + // Update the timestamp of llvm-config to force rustc_llvm to be + // rebuilt. This is a hacky workaround for a deficiency in Cargo where + // the rerun-if-changed directive doesn't handle changes very well. + // https://github.com/rust-lang/cargo/issues/10791 + // Cargo only compares the timestamp of the file relative to the last + // time `rustc_llvm` build script ran. However, the timestamps of the + // files in the tarball are in the past, so it doesn't trigger a + // rebuild. + let now = filetime::FileTime::from_system_time(std::time::SystemTime::now()); + let llvm_config = llvm_root.join("bin").join(exe("llvm-config", self.build)); + t!(filetime::set_file_times(&llvm_config, now, now)); + + let llvm_lib = llvm_root.join("lib"); + for entry in t!(fs::read_dir(&llvm_lib)) { + let lib = t!(entry).path(); + if lib.extension().map_or(false, |ext| ext == "so") { + self.fix_bin_or_dylib(&lib); + } + } + t!(fs::write(llvm_stamp, key)); + } + } + + fn download_ci_llvm(&self, llvm_sha: &str) { + let llvm_assertions = self.llvm_assertions; + + let cache_prefix = format!("llvm-{}-{}", llvm_sha, llvm_assertions); + let cache_dst = self.out.join("cache"); + let rustc_cache = cache_dst.join(cache_prefix); + if !rustc_cache.exists() { + t!(fs::create_dir_all(&rustc_cache)); + } + let base = if llvm_assertions { + &self.stage0_metadata.config.artifacts_with_llvm_assertions_server + } else { + &self.stage0_metadata.config.artifacts_server + }; + let version = self.artifact_version_part(llvm_sha); + let filename = format!("rust-dev-{}-{}.tar.xz", version, self.build.triple); + let tarball = rustc_cache.join(&filename); + if !tarball.exists() { + let help_on_error = "error: failed to download llvm from ci + + help: old builds get deleted after a certain time + help: if trying to compile an old commit of rustc, disable `download-ci-llvm` in config.toml: + + [llvm] + download-ci-llvm = false + "; + self.download_file(&format!("{base}/{llvm_sha}/{filename}"), &tarball, help_on_error); + } + let llvm_root = self.ci_llvm_root(); + self.unpack(&tarball, &llvm_root, "rust-dev"); + } +} diff --git a/src/bootstrap/format.rs b/src/bootstrap/format.rs index 37322670e656..5e7264fe765a 100644 --- a/src/bootstrap/format.rs +++ b/src/bootstrap/format.rs @@ -43,7 +43,7 @@ struct RustfmtConfig { } pub fn format(build: &Builder<'_>, check: bool, paths: &[PathBuf]) { - if build.config.dry_run { + if build.config.dry_run() { return; } let mut builder = ignore::types::TypesBuilder::new(); diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index f5def8ba8341..f4fa556b9745 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -112,15 +112,14 @@ use std::path::{Path, PathBuf}; use std::process::Command; use std::str; -use config::Target; +use channel::GitInfo; +use config::{DryRun, Target}; use filetime::FileTime; use once_cell::sync::OnceCell; use crate::builder::Kind; use crate::config::{LlvmLibunwind, TargetSelection}; -use crate::util::{ - check_run, exe, libdir, mtime, output, run, run_suppressed, try_run, try_run_suppressed, CiEnv, -}; +use crate::util::{exe, libdir, mtime, output, run, run_suppressed, try_run_suppressed, CiEnv}; mod bolt; mod builder; @@ -133,6 +132,7 @@ mod compile; mod config; mod dist; mod doc; +mod download; mod flags; mod format; mod install; @@ -281,7 +281,6 @@ pub struct Build { src: PathBuf, out: PathBuf, bootstrap_out: PathBuf, - rust_info: channel::GitInfo, cargo_info: channel::GitInfo, rust_analyzer_info: channel::GitInfo, clippy_info: channel::GitInfo, @@ -396,6 +395,28 @@ pub enum CLang { Cxx, } +macro_rules! forward { + ( $( $fn:ident( $($param:ident: $ty:ty),* ) $( -> $ret:ty)? ),+ $(,)? ) => { + impl Build { + $( fn $fn(&self, $($param: $ty),* ) $( -> $ret)? { + self.config.$fn( $($param),* ) + } )+ + } + } +} + +forward! { + verbose(msg: &str), + is_verbose() -> bool, + create(path: &Path, s: &str), + remove(f: &Path), + tempdir() -> PathBuf, + try_run(cmd: &mut Command) -> bool, + llvm_link_shared() -> bool, + download_rustc() -> bool, + initial_rustfmt() -> Option, +} + impl Build { /// Creates a new set of build configuration from the `flags` on the command /// line and the filesystem `config`. @@ -430,7 +451,7 @@ impl Build { // we always try to use git for LLVM builds let in_tree_llvm_info = channel::GitInfo::new(false, &src.join("src/llvm-project")); - let initial_target_libdir_str = if config.dry_run { + let initial_target_libdir_str = if config.dry_run() { "/dummy/lib/path/to/lib/".to_string() } else { output( @@ -444,7 +465,7 @@ impl Build { let initial_target_dir = Path::new(&initial_target_libdir_str).parent().unwrap(); let initial_lld = initial_target_dir.join("bin").join("rust-lld"); - let initial_sysroot = if config.dry_run { + let initial_sysroot = if config.dry_run() { "/dummy".to_string() } else { output(Command::new(&config.initial_rustc).arg("--print").arg("sysroot")) @@ -499,7 +520,6 @@ impl Build { out, bootstrap_out, - rust_info, cargo_info, rust_analyzer_info, clippy_info, @@ -570,7 +590,7 @@ impl Build { t!(std::fs::read_dir(dir)).next().is_none() } - if !self.config.submodules(&self.rust_info) { + if !self.config.submodules(&self.rust_info()) { return; } @@ -636,7 +656,7 @@ impl Build { /// This avoids contributors checking in a submodule change by accident. pub fn maybe_update_submodules(&self) { // Avoid running git when there isn't a git checkout. - if !self.config.submodules(&self.rust_info) { + if !self.config.submodules(&self.rust_info()) { return; } let output = output( @@ -689,13 +709,13 @@ impl Build { } } - if !self.config.dry_run { + if !self.config.dry_run() { { - self.config.dry_run = true; + self.config.dry_run = DryRun::SelfCheck; let builder = builder::Builder::new(&self); builder.execute_cli(); } - self.config.dry_run = false; + self.config.dry_run = DryRun::Disabled; let builder = builder::Builder::new(&self); builder.execute_cli(); } else { @@ -735,6 +755,10 @@ impl Build { cleared } + fn rust_info(&self) -> &GitInfo { + &self.config.rust_info + } + /// Gets the space-separated set of activated features for the standard /// library. fn std_features(&self, target: TargetSelection) -> String { @@ -947,7 +971,7 @@ impl Build { /// Runs a command, printing out nice contextual information if it fails. fn run(&self, cmd: &mut Command) { - if self.config.dry_run { + if self.config.dry_run() { return; } self.verbose(&format!("running: {:?}", cmd)); @@ -956,57 +980,24 @@ impl Build { /// Runs a command, printing out nice contextual information if it fails. fn run_quiet(&self, cmd: &mut Command) { - if self.config.dry_run { + if self.config.dry_run() { return; } self.verbose(&format!("running: {:?}", cmd)); run_suppressed(cmd) } - /// Runs a command, printing out nice contextual information if it fails. - /// Exits if the command failed to execute at all, otherwise returns its - /// `status.success()`. - fn try_run(&self, cmd: &mut Command) -> bool { - if self.config.dry_run { - return true; - } - self.verbose(&format!("running: {:?}", cmd)); - try_run(cmd, self.is_verbose()) - } - /// Runs a command, printing out nice contextual information if it fails. /// Exits if the command failed to execute at all, otherwise returns its /// `status.success()`. fn try_run_quiet(&self, cmd: &mut Command) -> bool { - if self.config.dry_run { + if self.config.dry_run() { return true; } self.verbose(&format!("running: {:?}", cmd)); try_run_suppressed(cmd) } - /// Runs a command, printing out nice contextual information if it fails. - /// Returns false if do not execute at all, otherwise returns its - /// `status.success()`. - fn check_run(&self, cmd: &mut Command) -> bool { - if self.config.dry_run { - return true; - } - self.verbose(&format!("running: {:?}", cmd)); - check_run(cmd, self.is_verbose()) - } - - pub fn is_verbose(&self) -> bool { - self.verbosity > 0 - } - - /// Prints a message if this build is configured in verbose mode. - fn verbose(&self, msg: &str) { - if self.is_verbose() { - println!("{}", msg); - } - } - pub fn is_verbose_than(&self, level: usize) -> bool { self.verbosity > level } @@ -1019,10 +1010,12 @@ impl Build { } fn info(&self, msg: &str) { - if self.config.dry_run { - return; + match self.config.dry_run { + DryRun::SelfCheck => return, + DryRun::Disabled | DryRun::UserSelected => { + println!("{}", msg); + } } - println!("{}", msg); } /// Returns the number of parallel jobs that have been configured for this @@ -1267,7 +1260,7 @@ impl Build { match &self.config.channel[..] { "stable" => num.to_string(), "beta" => { - if self.rust_info.is_managed_git_subrepository() && !self.config.ignore_git { + if self.rust_info().is_managed_git_subrepository() && !self.config.ignore_git { format!("{}-beta.{}", num, self.beta_prerelease_version()) } else { format!("{}-beta", num) @@ -1327,7 +1320,7 @@ impl Build { /// Note that this is a descriptive string which includes the commit date, /// sha, version, etc. fn rust_version(&self) -> String { - let mut version = self.rust_info.version(self, &self.version); + let mut version = self.rust_info().version(self, &self.version); if let Some(ref s) = self.config.description { version.push_str(" ("); version.push_str(s); @@ -1338,7 +1331,7 @@ impl Build { /// Returns the full commit hash. fn rust_sha(&self) -> Option<&str> { - self.rust_info.sha() + self.rust_info().sha() } /// Returns the `a.b.c` version that the given package is at. @@ -1400,7 +1393,7 @@ impl Build { } fn read_stamp_file(&self, stamp: &Path) -> Vec<(PathBuf, DependencyType)> { - if self.config.dry_run { + if self.config.dry_run() { return Vec::new(); } @@ -1424,23 +1417,13 @@ impl Build { paths } - /// Create a temporary directory in `out` and return its path. - /// - /// NOTE: this temporary directory is shared between all steps; - /// if you need an empty directory, create a new subdirectory inside it. - fn tempdir(&self) -> PathBuf { - let tmp = self.out.join("tmp"); - t!(fs::create_dir_all(&tmp)); - tmp - } - /// Copies a file from `src` to `dst` pub fn copy(&self, src: &Path, dst: &Path) { self.copy_internal(src, dst, false); } fn copy_internal(&self, src: &Path, dst: &Path, dereference_symlinks: bool) { - if self.config.dry_run { + if self.config.dry_run() { return; } self.verbose_than(1, &format!("Copy {:?} to {:?}", src, dst)); @@ -1477,7 +1460,7 @@ impl Build { /// Copies the `src` directory recursively to `dst`. Both are assumed to exist /// when this function is called. pub fn cp_r(&self, src: &Path, dst: &Path) { - if self.config.dry_run { + if self.config.dry_run() { return; } for f in self.read_dir(src) { @@ -1530,7 +1513,7 @@ impl Build { } fn install(&self, src: &Path, dstdir: &Path, perms: u32) { - if self.config.dry_run { + if self.config.dry_run() { return; } let dst = dstdir.join(src.file_name().unwrap()); @@ -1543,29 +1526,22 @@ impl Build { chmod(&dst, perms); } - fn create(&self, path: &Path, s: &str) { - if self.config.dry_run { - return; - } - t!(fs::write(path, s)); - } - fn read(&self, path: &Path) -> String { - if self.config.dry_run { + if self.config.dry_run() { return String::new(); } t!(fs::read_to_string(path)) } fn create_dir(&self, dir: &Path) { - if self.config.dry_run { + if self.config.dry_run() { return; } t!(fs::create_dir_all(dir)) } fn remove_dir(&self, dir: &Path) { - if self.config.dry_run { + if self.config.dry_run() { return; } t!(fs::remove_dir_all(dir)) @@ -1574,7 +1550,7 @@ impl Build { fn read_dir(&self, dir: &Path) -> impl Iterator { let iter = match fs::read_dir(dir) { Ok(v) => v, - Err(_) if self.config.dry_run => return vec![].into_iter(), + Err(_) if self.config.dry_run() => return vec![].into_iter(), Err(err) => panic!("could not read dir {:?}: {:?}", dir, err), }; iter.map(|e| t!(e)).collect::>().into_iter() @@ -1585,14 +1561,7 @@ impl Build { use std::os::unix::fs::symlink as symlink_file; #[cfg(windows)] use std::os::windows::fs::symlink_file; - if !self.config.dry_run { symlink_file(src.as_ref(), link.as_ref()) } else { Ok(()) } - } - - fn remove(&self, f: &Path) { - if self.config.dry_run { - return; - } - fs::remove_file(f).unwrap_or_else(|_| panic!("failed to remove {:?}", f)); + if !self.config.dry_run() { symlink_file(src.as_ref(), link.as_ref()) } else { Ok(()) } } /// Returns if config.ninja is enabled, and checks for ninja existence, diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index 94a61b727a32..f6c453ebe107 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -19,9 +19,9 @@ use std::process::Command; use crate::bolt::{instrument_with_bolt_inplace, optimize_library_with_bolt_inplace}; use crate::builder::{Builder, RunConfig, ShouldRun, Step}; use crate::channel; -use crate::config::TargetSelection; +use crate::config::{Config, TargetSelection}; use crate::util::get_clang_cl_resource_dir; -use crate::util::{self, exe, output, program_out_of_date, t, up_to_date}; +use crate::util::{self, exe, output, t, up_to_date}; use crate::{CLang, GitRepo}; pub struct Meta { @@ -65,7 +65,7 @@ pub fn prebuilt_llvm_config( builder: &Builder<'_>, target: TargetSelection, ) -> Result { - maybe_download_ci_llvm(builder); + builder.config.maybe_download_ci_llvm(); // If we're using a custom LLVM bail out here, but we can only use a // custom LLVM for the build triple. @@ -117,7 +117,7 @@ pub fn prebuilt_llvm_config( } /// This retrieves the LLVM sha we *want* to use, according to git history. -pub(crate) fn detect_llvm_sha(config: &crate::config::Config, is_git: bool) -> String { +pub(crate) fn detect_llvm_sha(config: &Config, is_git: bool) -> String { let llvm_sha = if is_git { let mut rev_list = config.git(); rev_list.args(&[ @@ -155,7 +155,7 @@ pub(crate) fn detect_llvm_sha(config: &crate::config::Config, is_git: bool) -> S /// This checks both the build triple platform to confirm we're usable at all, /// and then verifies if the current HEAD matches the detected LLVM SHA head, /// in which case LLVM is indicated as not available. -pub(crate) fn is_ci_llvm_available(config: &crate::config::Config, asserts: bool) -> bool { +pub(crate) fn is_ci_llvm_available(config: &Config, asserts: bool) -> bool { // This is currently all tier 1 targets and tier 2 targets with host tools // (since others may not have CI artifacts) // https://doc.rust-lang.org/rustc/platform-support.html#tier-1 @@ -217,80 +217,6 @@ pub(crate) fn is_ci_llvm_available(config: &crate::config::Config, asserts: bool true } -pub(crate) fn maybe_download_ci_llvm(builder: &Builder<'_>) { - let config = &builder.config; - if !config.llvm_from_ci { - return; - } - let llvm_root = config.ci_llvm_root(); - let llvm_stamp = llvm_root.join(".llvm-stamp"); - let llvm_sha = detect_llvm_sha(&config, builder.rust_info.is_managed_git_subrepository()); - let key = format!("{}{}", llvm_sha, config.llvm_assertions); - if program_out_of_date(&llvm_stamp, &key) && !config.dry_run { - download_ci_llvm(builder, &llvm_sha); - for entry in t!(fs::read_dir(llvm_root.join("bin"))) { - builder.fix_bin_or_dylib(&t!(entry).path()); - } - - // Update the timestamp of llvm-config to force rustc_llvm to be - // rebuilt. This is a hacky workaround for a deficiency in Cargo where - // the rerun-if-changed directive doesn't handle changes very well. - // https://github.com/rust-lang/cargo/issues/10791 - // Cargo only compares the timestamp of the file relative to the last - // time `rustc_llvm` build script ran. However, the timestamps of the - // files in the tarball are in the past, so it doesn't trigger a - // rebuild. - let now = filetime::FileTime::from_system_time(std::time::SystemTime::now()); - let llvm_config = llvm_root.join("bin").join(exe("llvm-config", builder.config.build)); - t!(filetime::set_file_times(&llvm_config, now, now)); - - let llvm_lib = llvm_root.join("lib"); - for entry in t!(fs::read_dir(&llvm_lib)) { - let lib = t!(entry).path(); - if lib.extension().map_or(false, |ext| ext == "so") { - builder.fix_bin_or_dylib(&lib); - } - } - t!(fs::write(llvm_stamp, key)); - } -} - -fn download_ci_llvm(builder: &Builder<'_>, llvm_sha: &str) { - let llvm_assertions = builder.config.llvm_assertions; - - let cache_prefix = format!("llvm-{}-{}", llvm_sha, llvm_assertions); - let cache_dst = builder.out.join("cache"); - let rustc_cache = cache_dst.join(cache_prefix); - if !rustc_cache.exists() { - t!(fs::create_dir_all(&rustc_cache)); - } - let base = if llvm_assertions { - &builder.config.stage0_metadata.config.artifacts_with_llvm_assertions_server - } else { - &builder.config.stage0_metadata.config.artifacts_server - }; - let version = builder.config.artifact_version_part(builder, llvm_sha); - let filename = format!("rust-dev-{}-{}.tar.xz", version, builder.build.build.triple); - let tarball = rustc_cache.join(&filename); - if !tarball.exists() { - let help_on_error = "error: failed to download llvm from ci - -help: old builds get deleted after a certain time -help: if trying to compile an old commit of rustc, disable `download-ci-llvm` in config.toml: - -[llvm] -download-ci-llvm = false -"; - builder.download_component( - &format!("{base}/{llvm_sha}/{filename}"), - &tarball, - help_on_error, - ); - } - let llvm_root = builder.config.ci_llvm_root(); - builder.unpack(&tarball, &llvm_root, "rust-dev"); -} - #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct Llvm { pub target: TargetSelection, @@ -505,7 +431,7 @@ impl Step for Llvm { // https://llvm.org/docs/HowToCrossCompileLLVM.html if target != builder.config.build { let llvm_config = builder.ensure(Llvm { target: builder.config.build }); - if !builder.config.dry_run { + if !builder.config.dry_run() { let llvm_bindir = output(Command::new(&llvm_config).arg("--bindir")); let host_bin = Path::new(llvm_bindir.trim()); cfg.define( @@ -519,7 +445,7 @@ impl Step for Llvm { if builder.config.llvm_clang { let build_bin = builder.llvm_out(builder.config.build).join("build").join("bin"); let clang_tblgen = build_bin.join("clang-tblgen").with_extension(EXE_EXTENSION); - if !builder.config.dry_run && !clang_tblgen.exists() { + if !builder.config.dry_run() && !clang_tblgen.exists() { panic!("unable to find {}", clang_tblgen.display()); } cfg.define("CLANG_TABLEGEN", clang_tblgen); @@ -553,7 +479,7 @@ impl Step for Llvm { // tools. Figure out how to filter them down and only build the right // tools and libs on all platforms. - if builder.config.dry_run { + if builder.config.dry_run() { return build_llvm_config; } @@ -611,7 +537,7 @@ fn check_llvm_version(builder: &Builder<'_>, llvm_config: &Path) { return; } - if builder.config.dry_run { + if builder.config.dry_run() { return; } @@ -872,7 +798,7 @@ impl Step for Lld { /// Compile LLD for `target`. fn run(self, builder: &Builder<'_>) -> PathBuf { - if builder.config.dry_run { + if builder.config.dry_run() { return PathBuf::from("lld-out-dir-test-gen"); } let target = self.target; @@ -990,7 +916,7 @@ impl Step for TestHelpers { /// Compiles the `rust_test_helpers.c` library which we used in various /// `run-pass` tests for ABI testing. fn run(self, builder: &Builder<'_>) { - if builder.config.dry_run { + if builder.config.dry_run() { return; } // The x86_64-fortanix-unknown-sgx target doesn't have a working C @@ -1066,7 +992,7 @@ impl Step for Sanitizers { } let llvm_config = builder.ensure(Llvm { target: builder.config.build }); - if builder.config.dry_run { + if builder.config.dry_run() { return runtimes; } @@ -1240,7 +1166,7 @@ impl Step for CrtBeginEnd { fn run(self, builder: &Builder<'_>) -> Self::Output { let out_dir = builder.native_dir(self.target).join("crt"); - if builder.config.dry_run { + if builder.config.dry_run() { return out_dir; } @@ -1304,7 +1230,7 @@ impl Step for Libunwind { /// Build linunwind.a fn run(self, builder: &Builder<'_>) -> Self::Output { - if builder.config.dry_run { + if builder.config.dry_run() { return PathBuf::new(); } diff --git a/src/bootstrap/sanity.rs b/src/bootstrap/sanity.rs index bdfd5fe5c421..631d42acb93f 100644 --- a/src/bootstrap/sanity.rs +++ b/src/bootstrap/sanity.rs @@ -74,7 +74,7 @@ pub fn check(build: &mut Build) { let mut cmd_finder = Finder::new(); // If we've got a git directory we're gonna need git to update // submodules and learn about various other aspects. - if build.rust_info.is_managed_git_subrepository() { + if build.rust_info().is_managed_git_subrepository() { cmd_finder.must_have("git"); } @@ -163,7 +163,7 @@ than building it. continue; } - if !build.config.dry_run { + if !build.config.dry_run() { cmd_finder.must_have(build.cc(*target)); if let Some(ar) = build.ar(*target) { cmd_finder.must_have(ar); @@ -172,7 +172,7 @@ than building it. } for host in &build.hosts { - if !build.config.dry_run { + if !build.config.dry_run() { cmd_finder.must_have(build.cxx(*host).unwrap()); } } diff --git a/src/bootstrap/tarball.rs b/src/bootstrap/tarball.rs index d999b6c15033..fc850a22b2f6 100644 --- a/src/bootstrap/tarball.rs +++ b/src/bootstrap/tarball.rs @@ -298,7 +298,7 @@ impl<'a> Tarball<'a> { fn run(self, build_cli: impl FnOnce(&Tarball<'a>, &mut Command)) -> GeneratedTarball { t!(std::fs::create_dir_all(&self.overlay_dir)); self.builder.create(&self.overlay_dir.join("version"), &self.overlay.version(self.builder)); - if let Some(info) = self.builder.rust_info.info() { + if let Some(info) = self.builder.rust_info().info() { channel::write_commit_hash_file(&self.overlay_dir, &info.sha); channel::write_commit_info_file(&self.overlay_dir, info); } @@ -323,7 +323,7 @@ impl<'a> Tarball<'a> { // Ensure there are no symbolic links in the tarball. In particular, // rustup-toolchain-install-master and most versions of Windows can't handle symbolic links. let decompressed_output = self.temp_dir.join(&package_name); - if !self.builder.config.dry_run && !self.permit_symlinks { + if !self.builder.config.dry_run() && !self.permit_symlinks { for entry in walkdir::WalkDir::new(&decompressed_output) { let entry = t!(entry); if entry.path_is_symlink() { diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index 46ad04708127..fd362b8367cc 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -467,7 +467,13 @@ pub struct Miri { impl Miri { /// Run `cargo miri setup` for the given target, return where the Miri sysroot was put. - pub fn build_miri_sysroot(builder: &Builder<'_>, compiler: Compiler, miri: &Path, target: TargetSelection) -> String { + pub fn build_miri_sysroot( + builder: &Builder<'_>, + compiler: Compiler, + miri: &Path, + target: TargetSelection, + ) -> String { + let miri_sysroot = builder.out.join(compiler.host.triple).join("miri-sysrot"); let mut cargo = tool::prepare_tool_cargo( builder, compiler, @@ -486,6 +492,8 @@ impl Miri { cargo.env("MIRI_LIB_SRC", builder.src.join("library")); // Tell it where to find Miri. cargo.env("MIRI", &miri); + // Tell it where to put the sysroot. + cargo.env("MIRI_SYSROOT", &miri_sysroot); // Debug things. cargo.env("RUST_BACKTRACE", "1"); @@ -500,7 +508,7 @@ impl Miri { cargo.arg("--print-sysroot"); // FIXME: Is there a way in which we can re-use the usual `run` helpers? - if builder.config.dry_run { + if builder.config.dry_run() { String::new() } else { builder.verbose(&format!("running: {:?}", cargo)); @@ -1529,7 +1537,7 @@ note: if you're sure you want to do this, please open an issue as to why. In the let mut copts_passed = false; if builder.config.llvm_enabled() { let llvm_config = builder.ensure(native::Llvm { target: builder.config.build }); - if !builder.config.dry_run { + if !builder.config.dry_run() { let llvm_version = output(Command::new(&llvm_config).arg("--version")); let llvm_components = output(Command::new(&llvm_config).arg("--components")); // Remove trailing newline from llvm-config output. @@ -1547,14 +1555,14 @@ note: if you're sure you want to do this, please open an issue as to why. In the // requirement, but the `-L` library path is not propagated across // separate compilations. We can add LLVM's library path to the // platform-specific environment variable as a workaround. - if !builder.config.dry_run && suite.ends_with("fulldeps") { + if !builder.config.dry_run() && suite.ends_with("fulldeps") { let llvm_libdir = output(Command::new(&llvm_config).arg("--libdir")); add_link_lib_path(vec![llvm_libdir.trim().into()], &mut cmd); } // Only pass correct values for these flags for the `run-make` suite as it // requires that a C++ compiler was configured which isn't always the case. - if !builder.config.dry_run && matches!(suite, "run-make" | "run-make-fulldeps") { + if !builder.config.dry_run() && matches!(suite, "run-make" | "run-make-fulldeps") { // The llvm/bin directory contains many useful cross-platform // tools. Pass the path to run-make tests so they can use them. let llvm_bin_path = llvm_config @@ -1582,7 +1590,7 @@ note: if you're sure you want to do this, please open an issue as to why. In the // Only pass correct values for these flags for the `run-make` suite as it // requires that a C++ compiler was configured which isn't always the case. - if !builder.config.dry_run && matches!(suite, "run-make" | "run-make-fulldeps") { + if !builder.config.dry_run() && matches!(suite, "run-make" | "run-make-fulldeps") { cmd.arg("--cc") .arg(builder.cc(target)) .arg("--cxx") diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs index d39522069470..ba329ea6c759 100644 --- a/src/bootstrap/tool.rs +++ b/src/bootstrap/tool.rs @@ -522,7 +522,7 @@ impl Step for Rustdoc { builder.ensure(compile::Rustc::new(build_compiler, target_compiler.host)); // NOTE: this implies that `download-rustc` is pretty useless when compiling with the stage0 // compiler, since you do just as much work. - if !builder.config.dry_run && builder.download_rustc() && build_compiler.stage == 0 { + if !builder.config.dry_run() && builder.download_rustc() && build_compiler.stage == 0 { println!( "warning: `download-rustc` does nothing when building stage1 tools; consider using `--stage 2` instead" ); diff --git a/src/bootstrap/toolstate.rs b/src/bootstrap/toolstate.rs index 1a1774432275..1969e0b6f872 100644 --- a/src/bootstrap/toolstate.rs +++ b/src/bootstrap/toolstate.rs @@ -158,7 +158,7 @@ impl Step for ToolStateCheck { /// stable tool. That is, the status is not allowed to get worse /// (test-pass to test-fail or build-fail). fn run(self, builder: &Builder<'_>) { - if builder.config.dry_run { + if builder.config.dry_run() { return; } @@ -265,7 +265,7 @@ impl Builder<'_> { // If we're in a dry run setting we don't want to save toolstates as // that means if we e.g. panic down the line it'll look like we tested // everything (but we actually haven't). - if self.config.dry_run { + if self.config.dry_run() { return; } // Toolstate isn't tracked for clippy or rustfmt, but since most tools do, we avoid checking diff --git a/src/bootstrap/util.rs b/src/bootstrap/util.rs index 20c3801f0a50..58220783228b 100644 --- a/src/bootstrap/util.rs +++ b/src/bootstrap/util.rs @@ -44,7 +44,13 @@ pub use t; /// Given an executable called `name`, return the filename for the /// executable for a particular target. pub fn exe(name: &str, target: TargetSelection) -> String { - if target.contains("windows") { format!("{}.exe", name) } else { name.to_string() } + if target.contains("windows") { + format!("{}.exe", name) + } else if target.contains("uefi") { + format!("{}.efi", name) + } else { + name.to_string() + } } /// Returns `true` if the file name given looks like a dynamic library. @@ -105,7 +111,7 @@ pub struct TimeIt(bool, Instant); /// Returns an RAII structure that prints out how long it took to drop. pub fn timeit(builder: &Builder<'_>) -> TimeIt { - TimeIt(builder.config.dry_run, Instant::now()) + TimeIt(builder.config.dry_run(), Instant::now()) } impl Drop for TimeIt { @@ -128,7 +134,7 @@ pub(crate) fn program_out_of_date(stamp: &Path, key: &str) -> bool { /// Symlinks two directories, using junctions on Windows and normal symlinks on /// Unix. pub fn symlink_dir(config: &Config, src: &Path, dest: &Path) -> io::Result<()> { - if config.dry_run { + if config.dry_run() { return Ok(()); } let _ = fs::remove_dir(dest); diff --git a/src/ci/docker/host-x86_64/dist-s390x-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-s390x-linux/Dockerfile index 7d77fdd30d1d..43a449b3a192 100644 --- a/src/ci/docker/host-x86_64/dist-s390x-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-s390x-linux/Dockerfile @@ -28,5 +28,5 @@ ENV \ ENV HOSTS=s390x-unknown-linux-gnu -ENV RUST_CONFIGURE_ARGS --enable-extended --enable-lld --disable-docs +ENV RUST_CONFIGURE_ARGS --enable-extended --enable-lld --enable-profiler --disable-docs ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/host-x86_64/dist-x86_64-netbsd/Dockerfile b/src/ci/docker/host-x86_64/dist-x86_64-netbsd/Dockerfile index fed4be4c30a7..d03c364547e0 100644 --- a/src/ci/docker/host-x86_64/dist-x86_64-netbsd/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-x86_64-netbsd/Dockerfile @@ -1,7 +1,8 @@ -FROM ubuntu:16.04 +FROM ubuntu:20.04 COPY scripts/cross-apt-packages.sh /scripts/ RUN sh /scripts/cross-apt-packages.sh +RUN DEBIAN_FRONTEND=noninteractive apt-get install -y zlib1g-dev COPY host-x86_64/dist-x86_64-netbsd/build-netbsd-toolchain.sh /tmp/ RUN /tmp/build-netbsd-toolchain.sh @@ -9,9 +10,6 @@ RUN /tmp/build-netbsd-toolchain.sh COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh -COPY scripts/cmake.sh /scripts/ -RUN /scripts/cmake.sh - ENV PATH=$PATH:/x-tools/x86_64-unknown-netbsd/bin ENV \ @@ -21,6 +19,5 @@ ENV \ ENV HOSTS=x86_64-unknown-netbsd -ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs \ - --set llvm.allow-old-toolchain +ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/host-x86_64/dist-x86_64-netbsd/build-netbsd-toolchain.sh b/src/ci/docker/host-x86_64/dist-x86_64-netbsd/build-netbsd-toolchain.sh index 5dfa47b4eed7..e0c008b76fa8 100755 --- a/src/ci/docker/host-x86_64/dist-x86_64-netbsd/build-netbsd-toolchain.sh +++ b/src/ci/docker/host-x86_64/dist-x86_64-netbsd/build-netbsd-toolchain.sh @@ -25,19 +25,19 @@ cd netbsd mkdir -p /x-tools/x86_64-unknown-netbsd/sysroot -URL=https://ci-mirrors.rust-lang.org/rustc +# URL=https://ci-mirrors.rust-lang.org/rustc -# Originally from ftp://ftp.netbsd.org/pub/NetBSD/NetBSD-$BSD/source/sets/*.tgz -curl $URL/2018-03-01-netbsd-src.tgz | tar xzf - -curl $URL/2018-03-01-netbsd-gnusrc.tgz | tar xzf - -curl $URL/2018-03-01-netbsd-sharesrc.tgz | tar xzf - -curl $URL/2018-03-01-netbsd-syssrc.tgz | tar xzf - +SOURCE_URL=https://cdn.netbsd.org/pub/NetBSD/NetBSD-9.0/source/sets +curl $SOURCE_URL/src.tgz | tar xzf - +curl $SOURCE_URL/gnusrc.tgz | tar xzf - +curl $SOURCE_URL/sharesrc.tgz | tar xzf - +curl $SOURCE_URL/syssrc.tgz | tar xzf - -# Originally from ftp://ftp.netbsd.org/pub/NetBSD/NetBSD-$BSD/amd64/binary/sets/*.tgz -curl $URL/2018-03-01-netbsd-base.tgz | \ - tar xzf - -C /x-tools/x86_64-unknown-netbsd/sysroot ./usr/include ./usr/lib ./lib -curl $URL/2018-03-01-netbsd-comp.tgz | \ - tar xzf - -C /x-tools/x86_64-unknown-netbsd/sysroot ./usr/include ./usr/lib +BINARY_URL=https://cdn.netbsd.org/pub/NetBSD/NetBSD-9.0/amd64/binary/sets +curl $BINARY_URL/base.tar.xz | \ + tar xJf - -C /x-tools/x86_64-unknown-netbsd/sysroot ./usr/include ./usr/lib ./lib +curl $BINARY_URL/comp.tar.xz | \ + tar xJf - -C /x-tools/x86_64-unknown-netbsd/sysroot ./usr/include ./usr/lib cd usr/src diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version index cc96715b2857..ed0d9e9902b9 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version @@ -1 +1 @@ -0.12.7 \ No newline at end of file +0.13.1 \ No newline at end of file diff --git a/src/ci/run.sh b/src/ci/run.sh index 9a247fb60a8e..7de06ec35c36 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -123,6 +123,10 @@ else # (And PGO is its own can of worms). if [ "$NO_DOWNLOAD_CI_LLVM" = "" ]; then RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set llvm.download-ci-llvm=if-available" + else + # When building for CI we want to use the static C++ Standard library + # included with LLVM, since a dynamic libstdcpp may not be available. + RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set llvm.static-libstdcpp" fi fi diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 86bb2c0d3816..2d3b83094614 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -33,6 +33,7 @@ - [nvptx64-nvidia-cuda](platform-support/nvptx64-nvidia-cuda.md) - [riscv32imac-unknown-xous-elf](platform-support/riscv32imac-unknown-xous-elf.md) - [*-pc-windows-gnullvm](platform-support/pc-windows-gnullvm.md) + - [\*-nto-qnx-\*](platform-support/nto-qnx.md) - [*-unknown-openbsd](platform-support/openbsd.md) - [\*-unknown-uefi](platform-support/unknown-uefi.md) - [wasm64-unknown-unknown](platform-support/wasm64-unknown-unknown.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 0315f1e3725f..28929acb9b48 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -214,6 +214,7 @@ target | std | host | notes [`aarch64-kmc-solid_asp3`](platform-support/kmc-solid.md) | ✓ | | ARM64 SOLID with TOPPERS/ASP3 [`aarch64-nintendo-switch-freestanding`](platform-support/aarch64-nintendo-switch-freestanding.md) | * | | ARM64 Nintendo Switch, Horizon [`aarch64-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | ✓ | +[`aarch64-unknown-nto-qnx7.1.0`](platform-support/nto-qnx.md) | ? | | ARM64 QNX Neutrino 7.1 RTOS | `aarch64-unknown-freebsd` | ✓ | ✓ | ARM64 FreeBSD `aarch64-unknown-hermit` | ✓ | | ARM64 HermitCore `aarch64-unknown-linux-gnu_ilp32` | ✓ | ✓ | ARM64 Linux (ILP32 ABI) @@ -303,6 +304,7 @@ target | std | host | notes `x86_64-apple-ios-macabi` | ✓ | | Apple Catalyst on x86_64 `x86_64-apple-tvos` | * | | x86 64-bit tvOS [`x86_64-apple-watchos-sim`](platform-support/apple-watchos.md) | ✓ | | x86 64-bit Apple WatchOS simulator +[`x86_64-pc-nto-qnx7.1.0`](platform-support/nto-qnx.md) | ? | | x86 64-bit QNX Neutrino 7.1 RTOS | [`x86_64-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | ✓ | `x86_64-pc-windows-msvc` | * | | 64-bit Windows XP support `x86_64-sun-solaris` | ? | | Deprecated target for 64-bit Solaris 10/11, illumos diff --git a/src/doc/rustc/src/platform-support/nto-qnx.md b/src/doc/rustc/src/platform-support/nto-qnx.md new file mode 100644 index 000000000000..2f6ea94d113a --- /dev/null +++ b/src/doc/rustc/src/platform-support/nto-qnx.md @@ -0,0 +1,118 @@ +# nto-qnx + +**Tier: 3** + +[BlackBerry® QNX®][BlackBerry] Neutrino (nto) Real-time operating system. +The support has been implemented jointly by [Elektrobit Automotive GmbH][Elektrobit] +and [BlackBerry][BlackBerry]. + +[BlackBerry]: https://blackberry.qnx.com +[Elektrobit]: https://www.elektrobit.com + +## Target maintainers + +- Florian Bartels, `Florian.Bartels@elektrobit.com`, https://github.com/flba-eb +- Tristan Roach, `TRoach@blackberry.com`, https://github.com/gh-tr + +## Requirements + +Currently, only cross-compilation for QNX Neutrino on AArch64 and x86_64 are supported (little endian). +Adding other architectures that are supported by QNX Neutrino is possible. + +The standard library does not yet support QNX Neutrino. Therefore, only `no_std` code can +be compiled. + +`core` and `alloc` (with default allocator) are supported. + +Applications must link against `libc.so` (see example). This is required because applications +always link against the `crt` library and `crt` depends on `libc.so`. + +The correct version of `qcc` must be available by setting the `$PATH` variable (e.g. by sourcing `qnxsdp-env.sh` of the +QNX Neutrino toolchain). + +### Small example application + +```rust,ignore (platform-specific) +#![no_std] +#![no_main] +#![feature(lang_items)] + +// We must always link against libc, even if no external functions are used +// "extern C" - Block can be empty but must be present +#[link(name = "c")] +extern "C" { + pub fn printf(format: *const core::ffi::c_char, ...) -> core::ffi::c_int; +} + +#[no_mangle] +pub extern "C" fn main(_argc: isize, _argv: *const *const u8) -> isize { + const HELLO: &'static str = "Hello World, the answer is %d\n\0"; + unsafe { + printf(HELLO.as_ptr() as *const _, 42); + } + 0 +} + +use core::panic::PanicInfo; + +#[panic_handler] +fn panic(_panic: &PanicInfo<'_>) -> ! { + loop {} +} + +#[lang = "eh_personality"] +#[no_mangle] +pub extern "C" fn rust_eh_personality() {} +``` + +The QNX Neutrino support of Rust has been tested with QNX Neutrino 7.1. + +There are no further known requirements. + +## Conditional compilation + +For conditional compilation, following QNX Neutrino specific attributes are defined: + +- `target_os` = `"nto"` +- `target_env` = `"nto71"` (for QNX Neutrino 7.1) + +## Building the target + +1. Create a `config.toml` + +Example content: + +```toml +profile = "compiler" +changelog-seen = 2 +``` + +2. Compile the Rust toolchain for an `x86_64-unknown-linux-gnu` host (for both `aarch64` and `x86_64` targets) + +Run the following: + +```bash +env \ + CC_aarch64-unknown-nto-qnx7.1.0="qcc" \ + CFLAGS_aarch64-unknown-nto-qnx7.1.0="-Vgcc_ntoaarch64le_cxx" \ + CXX_aarch64-unknown-nto-qnx7.1.0="qcc" \ + AR_aarch64_unknown_nto_qnx7.1.0="ntoaarch64-ar" \ + CC_x86_64-pc-nto-qnx7.1.0="qcc" \ + CFLAGS_x86_64-pc-nto-qnx7.1.0="-Vgcc_ntox86_64_cxx" \ + CXX_x86_64-pc-nto-qnx7.1.0="qcc" \ + AR_x86_64_pc_nto_qnx7.1.0="ntox86_64-ar" \ + ./x.py build --target aarch64-unknown-nto-qnx7.1.0 --target x86_64-pc-nto-qnx7.1.0 --target x86_64-unknown-linux-gnu rustc library/core library/alloc/ +``` + +## Building Rust programs + +Rust does not yet ship pre-compiled artifacts for this target. To compile for this target, you must either build Rust with the target enabled (see "Building the target" above), or build your own copy of `core` by using +`build-std` or similar. + +## Testing + +Compiled executables can directly be run on QNX Neutrino. + +## Cross-compilation toolchains and C code + +Compiling C code requires the same environment variables to be set as compiling the Rust toolchain (see above), to ensure `qcc` is used with proper arguments. To ensure compatibility, do not specify any further arguments that for example change calling conventions or memory layout. diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 56a873e3e823..c2f78fd5950f 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1942,6 +1942,79 @@ fn clean_bare_fn_ty<'tcx>( BareFunctionDecl { unsafety: bare_fn.unsafety, abi: bare_fn.abi, decl, generic_params } } +/// This visitor is used to go through only the "top level" of a item and not enter any sub +/// item while looking for a given `Ident` which is stored into `item` if found. +struct OneLevelVisitor<'hir> { + map: rustc_middle::hir::map::Map<'hir>, + item: Option<&'hir hir::Item<'hir>>, + looking_for: Ident, + target_hir_id: hir::HirId, +} + +impl<'hir> OneLevelVisitor<'hir> { + fn new(map: rustc_middle::hir::map::Map<'hir>, target_hir_id: hir::HirId) -> Self { + Self { map, item: None, looking_for: Ident::empty(), target_hir_id } + } + + fn reset(&mut self, looking_for: Ident) { + self.looking_for = looking_for; + self.item = None; + } +} + +impl<'hir> hir::intravisit::Visitor<'hir> for OneLevelVisitor<'hir> { + type NestedFilter = rustc_middle::hir::nested_filter::All; + + fn nested_visit_map(&mut self) -> Self::Map { + self.map + } + + fn visit_item(&mut self, item: &'hir hir::Item<'hir>) { + if self.item.is_none() + && item.ident == self.looking_for + && matches!(item.kind, hir::ItemKind::Use(_, _)) + || item.hir_id() == self.target_hir_id + { + self.item = Some(item); + } + } +} + +/// Because a `Use` item directly links to the imported item, we need to manually go through each +/// import one by one. To do so, we go to the parent item and look for the `Ident` into it. Then, +/// if we found the "end item" (the imported one), we stop there because we don't need its +/// documentation. Otherwise, we repeat the same operation until we find the "end item". +fn get_all_import_attributes<'hir>( + mut item: &hir::Item<'hir>, + tcx: TyCtxt<'hir>, + target_hir_id: hir::HirId, + attributes: &mut Vec, +) { + let hir_map = tcx.hir(); + let mut visitor = OneLevelVisitor::new(hir_map, target_hir_id); + // If the item is an import and has at least a path with two parts, we go into it. + while let hir::ItemKind::Use(path, _) = item.kind && + path.segments.len() > 1 && + let hir::def::Res::Def(_, def_id) = path.segments[path.segments.len() - 2].res + { + if let Some(hir::Node::Item(parent_item)) = hir_map.get_if_local(def_id) { + // We add the attributes from this import into the list. + attributes.extend_from_slice(hir_map.attrs(item.hir_id())); + // We get the `Ident` we will be looking for into `item`. + let looking_for = path.segments[path.segments.len() - 1].ident; + visitor.reset(looking_for); + hir::intravisit::walk_item(&mut visitor, parent_item); + if let Some(i) = visitor.item { + item = i; + } else { + break; + } + } else { + break; + } + } +} + fn clean_maybe_renamed_item<'tcx>( cx: &mut DocContext<'tcx>, item: &hir::Item<'tcx>, @@ -2023,13 +2096,20 @@ fn clean_maybe_renamed_item<'tcx>( } _ => unreachable!("not yet converted"), }; - if let Some(import_id) = import_id { - let (attrs, cfg) = inline::merge_attrs( - cx, - Some(cx.tcx.parent_module(import_id).to_def_id()), - inline::load_attrs(cx, def_id), - Some(inline::load_attrs(cx, cx.tcx.hir().local_def_id(import_id).to_def_id())), - ); + + let mut extra_attrs = Vec::new(); + if let Some(hir::Node::Item(use_node)) = + import_id.and_then(|hir_id| cx.tcx.hir().find(hir_id)) + { + // We get all the various imports' attributes. + get_all_import_attributes(use_node, cx.tcx, item.hir_id(), &mut extra_attrs); + } + + if !extra_attrs.is_empty() { + extra_attrs.extend_from_slice(inline::load_attrs(cx, def_id)); + let attrs = Attributes::from_ast(&extra_attrs); + let cfg = extra_attrs.cfg(cx.tcx, &cx.cache.hidden_cfg); + vec![Item::from_def_id_and_attrs_and_parts( def_id, Some(name), diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index a5c3d35b1b59..39e2a9022267 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -107,10 +107,6 @@ impl Buffer { self.buffer } - pub(crate) fn insert_str(&mut self, idx: usize, s: &str) { - self.buffer.insert_str(idx, s); - } - pub(crate) fn push_str(&mut self, s: &str) { self.buffer.push_str(s); } diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index 51843a505f70..73690c86f4f7 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -69,11 +69,13 @@ pub(crate) struct Context<'tcx> { /// the source files are present in the html rendering, then this will be /// `true`. pub(crate) include_sources: bool, + /// Collection of all types with notable traits referenced in the current module. + pub(crate) types_with_notable_traits: FxHashSet, } // `Context` is cloned a lot, so we don't want the size to grow unexpectedly. #[cfg(all(not(windows), target_arch = "x86_64", target_pointer_width = "64"))] -rustc_data_structures::static_assert_size!(Context<'_>, 128); +rustc_data_structures::static_assert_size!(Context<'_>, 160); /// Shared mutable state used in [`Context`] and elsewhere. pub(crate) struct SharedContext<'tcx> { @@ -532,6 +534,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { deref_id_map: FxHashMap::default(), shared: Rc::new(scx), include_sources, + types_with_notable_traits: FxHashSet::default(), }; if emit_crate { @@ -560,6 +563,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { id_map: IdMap::new(), shared: Rc::clone(&self.shared), include_sources: self.include_sources, + types_with_notable_traits: FxHashSet::default(), } } @@ -803,6 +807,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { } } } + Ok(()) } diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 3a041ae15d61..8731efb5e874 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -59,7 +59,7 @@ use rustc_span::{ symbol::{sym, Symbol}, BytePos, FileName, RealFileName, }; -use serde::ser::SerializeSeq; +use serde::ser::{SerializeMap, SerializeSeq}; use serde::{Serialize, Serializer}; use crate::clean::{self, ItemId, RenderedLink, SelfTy}; @@ -803,7 +803,7 @@ fn assoc_method( d: &clean::FnDecl, link: AssocItemLink<'_>, parent: ItemType, - cx: &Context<'_>, + cx: &mut Context<'_>, render_mode: RenderMode, ) { let tcx = cx.tcx(); @@ -836,6 +836,8 @@ fn assoc_method( + name.as_str().len() + generics_len; + let notable_traits = d.output.as_return().and_then(|output| notable_traits_button(output, cx)); + let (indent, indent_str, end_newline) = if parent == ItemType::Trait { header_len += 4; let indent_str = " "; @@ -861,9 +863,9 @@ fn assoc_method( name = name, generics = g.print(cx), decl = d.full_print(header_len, indent, cx), - notable_traits = notable_traits_decl(d, cx), + notable_traits = notable_traits.unwrap_or_default(), where_clause = print_where_clause(g, cx, indent, end_newline), - ) + ); } /// Writes a span containing the versions at which an item became stable and/or const-stable. For @@ -963,7 +965,7 @@ fn render_assoc_item( item: &clean::Item, link: AssocItemLink<'_>, parent: ItemType, - cx: &Context<'_>, + cx: &mut Context<'_>, render_mode: RenderMode, ) { match &*item.kind { @@ -1273,88 +1275,133 @@ fn should_render_item(item: &clean::Item, deref_mut_: bool, tcx: TyCtxt<'_>) -> } } -fn notable_traits_decl(decl: &clean::FnDecl, cx: &Context<'_>) -> String { - let mut out = Buffer::html(); +pub(crate) fn notable_traits_button(ty: &clean::Type, cx: &mut Context<'_>) -> Option { + let mut has_notable_trait = false; - if let Some((did, ty)) = decl.output.as_return().and_then(|t| Some((t.def_id(cx.cache())?, t))) + let did = ty.def_id(cx.cache())?; + + // Box has pass-through impls for Read, Write, Iterator, and Future when the + // boxed type implements one of those. We don't want to treat every Box return + // as being notably an Iterator (etc), though, so we exempt it. Pin has the same + // issue, with a pass-through impl for Future. + if Some(did) == cx.tcx().lang_items().owned_box() + || Some(did) == cx.tcx().lang_items().pin_type() { - // Box has pass-through impls for Read, Write, Iterator, and Future when the - // boxed type implements one of those. We don't want to treat every Box return - // as being notably an Iterator (etc), though, so we exempt it. Pin has the same - // issue, with a pass-through impl for Future. - if Some(did) == cx.tcx().lang_items().owned_box() - || Some(did) == cx.tcx().lang_items().pin_type() - { - return "".to_string(); - } - if let Some(impls) = cx.cache().impls.get(&did) { - for i in impls { - let impl_ = i.inner_impl(); - if !impl_.for_.without_borrowed_ref().is_same(ty.without_borrowed_ref(), cx.cache()) + return None; + } + + if let Some(impls) = cx.cache().impls.get(&did) { + for i in impls { + let impl_ = i.inner_impl(); + if !impl_.for_.without_borrowed_ref().is_same(ty.without_borrowed_ref(), cx.cache()) { + // Two different types might have the same did, + // without actually being the same. + continue; + } + if let Some(trait_) = &impl_.trait_ { + let trait_did = trait_.def_id(); + + if cx.cache().traits.get(&trait_did).map_or(false, |t| t.is_notable_trait(cx.tcx())) { - // Two different types might have the same did, - // without actually being the same. - continue; - } - if let Some(trait_) = &impl_.trait_ { - let trait_did = trait_.def_id(); - - if cx - .cache() - .traits - .get(&trait_did) - .map_or(false, |t| t.is_notable_trait(cx.tcx())) - { - if out.is_empty() { - write!( - &mut out, - "Notable traits for {}\ - ", - impl_.for_.print(cx) - ); - } - - //use the "where" class here to make it small - write!( - &mut out, - "{}", - impl_.print(false, cx) - ); - for it in &impl_.items { - if let clean::AssocTypeItem(ref tydef, ref _bounds) = *it.kind { - out.push_str(" "); - let empty_set = FxHashSet::default(); - let src_link = - AssocItemLink::GotoSource(trait_did.into(), &empty_set); - assoc_type( - &mut out, - it, - &tydef.generics, - &[], // intentionally leaving out bounds - Some(&tydef.type_), - src_link, - 0, - cx, - ); - out.push_str(";"); - } - } - } + has_notable_trait = true; } } } } - if !out.is_empty() { - out.insert_str( - 0, - "ⓘ\ - ", - ); - out.push_str(""); + if has_notable_trait { + cx.types_with_notable_traits.insert(ty.clone()); + Some(format!( + " ", + ty = Escape(&format!("{:#}", ty.print(cx))), + )) + } else { + None + } +} + +fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) { + let mut out = Buffer::html(); + + let did = ty.def_id(cx.cache()).expect("notable_traits_button already checked this"); + + let impls = cx.cache().impls.get(&did).expect("notable_traits_button already checked this"); + + for i in impls { + let impl_ = i.inner_impl(); + if !impl_.for_.without_borrowed_ref().is_same(ty.without_borrowed_ref(), cx.cache()) { + // Two different types might have the same did, + // without actually being the same. + continue; + } + if let Some(trait_) = &impl_.trait_ { + let trait_did = trait_.def_id(); + + if cx.cache().traits.get(&trait_did).map_or(false, |t| t.is_notable_trait(cx.tcx())) { + if out.is_empty() { + write!( + &mut out, + "

Notable traits for {}

\ +
",
+                        impl_.for_.print(cx)
+                    );
+                }
+
+                //use the "where" class here to make it small
+                write!(
+                    &mut out,
+                    "{}",
+                    impl_.print(false, cx)
+                );
+                for it in &impl_.items {
+                    if let clean::AssocTypeItem(ref tydef, ref _bounds) = *it.kind {
+                        out.push_str("    ");
+                        let empty_set = FxHashSet::default();
+                        let src_link = AssocItemLink::GotoSource(trait_did.into(), &empty_set);
+                        assoc_type(
+                            &mut out,
+                            it,
+                            &tydef.generics,
+                            &[], // intentionally leaving out bounds
+                            Some(&tydef.type_),
+                            src_link,
+                            0,
+                            cx,
+                        );
+                        out.push_str(";");
+                    }
+                }
+            }
+        }
+    }
+    if out.is_empty() {
+        write!(&mut out, "
",); } - out.into_inner() + (format!("{:#}", ty.print(cx)), out.into_inner()) +} + +pub(crate) fn notable_traits_json<'a>( + tys: impl Iterator, + cx: &Context<'_>, +) -> String { + let mut mp: Vec<(String, String)> = tys.map(|ty| notable_traits_decl(ty, cx)).collect(); + mp.sort_by(|(name1, _html1), (name2, _html2)| name1.cmp(name2)); + struct NotableTraitsMap(Vec<(String, String)>); + impl Serialize for NotableTraitsMap { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut map = serializer.serialize_map(Some(self.0.len()))?; + for item in &self.0 { + map.serialize_entry(&item.0, &item.1)?; + } + map.end() + } + } + serde_json::to_string(&NotableTraitsMap(mp)) + .expect("serialize (string, string) -> json object cannot fail") } #[derive(Clone, Copy, Debug)] @@ -2890,9 +2937,6 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite })() .unwrap_or(rustc_span::DUMMY_SP); - // The root path is the inverse of Context::current - let root_path = vec!["../"; cx.current.len() - 1].join(""); - let mut decoration_info = FxHashMap::default(); decoration_info.insert("highlight focus", vec![byte_ranges.remove(0)]); decoration_info.insert("highlight", byte_ranges); @@ -2902,7 +2946,7 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite contents_subset, file_span, cx, - &root_path, + &cx.root_path(), highlight::DecorationInfo(decoration_info), sources::SourceContext::Embedded { offset: line_min, needs_expansion }, ); diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index ce4fc4d68fac..ac11a860a4f0 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -17,9 +17,10 @@ use std::rc::Rc; use super::{ collect_paths_for_type, document, ensure_trailing_slash, get_filtered_impls_for_reference, - item_ty_to_section, notable_traits_decl, render_all_impls, render_assoc_item, - render_assoc_items, render_attributes_in_code, render_attributes_in_pre, render_impl, - render_rightside, render_stability_since_raw, AssocItemLink, Context, ImplRenderingParameters, + item_ty_to_section, notable_traits_button, notable_traits_json, render_all_impls, + render_assoc_item, render_assoc_items, render_attributes_in_code, render_attributes_in_pre, + render_impl, render_rightside, render_stability_since_raw, AssocItemLink, Context, + ImplRenderingParameters, }; use crate::clean; use crate::config::ModuleSorting; @@ -183,6 +184,16 @@ pub(super) fn print_item( unreachable!(); } } + + // Render notable-traits.js used for all methods in this module. + if !cx.types_with_notable_traits.is_empty() { + write!( + buf, + r#""#, + notable_traits_json(cx.types_with_notable_traits.iter(), cx) + ); + cx.types_with_notable_traits.clear(); + } } /// For large structs, enums, unions, etc, determine whether to hide their fields @@ -516,6 +527,9 @@ fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &cle + name.as_str().len() + generics_len; + let notable_traits = + f.decl.output.as_return().and_then(|output| notable_traits_button(output, cx)); + wrap_into_item_decl(w, |w| { wrap_item(w, "fn", |w| { render_attributes_in_pre(w, it, ""); @@ -533,11 +547,11 @@ fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &cle generics = f.generics.print(cx), where_clause = print_where_clause(&f.generics, cx, 0, Ending::Newline), decl = f.decl.full_print(header_len, 0, cx), - notable_traits = notable_traits_decl(&f.decl, cx), + notable_traits = notable_traits.unwrap_or_default(), ); }); }); - document(w, cx, it, None, HeadingOffset::H2) + document(w, cx, it, None, HeadingOffset::H2); } fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Trait) { diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs index 8a01c01049d6..50135d601900 100644 --- a/src/librustdoc/html/sources.rs +++ b/src/librustdoc/html/sources.rs @@ -276,25 +276,26 @@ pub(crate) fn print_src( let mut line_numbers = Buffer::empty_from(buf); let extra; line_numbers.write_str("
");
+    let current_href = &context
+        .href_from_span(clean::Span::new(file_span), false)
+        .expect("only local crates should have sources emitted");
     match source_context {
         SourceContext::Standalone => {
             extra = None;
             for line in 1..=lines {
-                writeln!(line_numbers, "{0}", line)
+                writeln!(line_numbers, "{line}")
             }
         }
         SourceContext::Embedded { offset, needs_expansion } => {
             extra =
                 if needs_expansion { Some(r#""#) } else { None };
-            for line in 1..=lines {
-                writeln!(line_numbers, "{0}", line + offset)
+            for line_number in 1..=lines {
+                let line = line_number + offset;
+                writeln!(line_numbers, "{line}")
             }
         }
     }
     line_numbers.write_str("
"); - let current_href = &context - .href_from_span(clean::Span::new(file_span), false) - .expect("only local crates should have sources emitted"); highlight::render_source_with_highlighting( s, buf, diff --git a/src/librustdoc/html/static/css/noscript.css b/src/librustdoc/html/static/css/noscript.css index 301f03a16427..54e8b6561f34 100644 --- a/src/librustdoc/html/static/css/noscript.css +++ b/src/librustdoc/html/static/css/noscript.css @@ -22,3 +22,9 @@ nav.sub { .source .sidebar { display: none; } + +.notable-traits { + /* layout requires javascript + https://github.com/rust-lang/rust/issues/102576 */ + display: none; +} diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index f58d2c609426..3f295b96dc56 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -579,15 +579,16 @@ ul.block, .block li { border-color: var(--example-line-numbers-border-color); } -.src-line-numbers span { - cursor: pointer; +.src-line-numbers a, .src-line-numbers span { color: var(--src-line-numbers-span-color); } -.src-line-numbers .line-highlighted { - background-color: var(--src-line-number-highlighted-background-color); -} .src-line-numbers :target { background-color: transparent; + border-right: none; + padding-right: 0; +} +.src-line-numbers .line-highlighted { + background-color: var(--src-line-number-highlighted-background-color); } .search-loading { @@ -830,6 +831,9 @@ table, line-height: 1.5; font-weight: 500; } +#crate-search:hover, #crate-search:focus { + border-color: var(--crate-search-hover-border); +} /* cancel stylistic differences in padding in firefox for "appearance: none"-style (or equivalent) \ - \ ${setting_name}\ `; } diff --git a/src/librustdoc/html/static/js/source-script.js b/src/librustdoc/html/static/js/source-script.js index 0b9368dd8994..5db768c1c575 100644 --- a/src/librustdoc/html/static/js/source-script.js +++ b/src/librustdoc/html/static/js/source-script.js @@ -157,7 +157,7 @@ function highlightSourceLines(match) { x.scrollIntoView(); } onEachLazy(document.getElementsByClassName("src-line-numbers"), e => { - onEachLazy(e.getElementsByTagName("span"), i_e => { + onEachLazy(e.getElementsByTagName("a"), i_e => { removeClass(i_e, "line-highlighted"); }); }); @@ -188,8 +188,13 @@ const handleSourceHighlight = (function() { return ev => { let cur_line_id = parseInt(ev.target.id, 10); - // It can happen when clicking not on a line number span. - if (isNaN(cur_line_id)) { + // This event handler is attached to the entire line number column, but it should only + // be run if one of the anchors is clicked. It also shouldn't do anything if the anchor + // is clicked with a modifier key (to open a new browser tab). + if (isNaN(cur_line_id) || + ev.ctrlKey || + ev.altKey || + ev.metaKey) { return; } ev.preventDefault(); diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 091a1ba70cab..acfbd072121a 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -674,7 +674,7 @@ impl FromWithTcx for Variant { impl FromWithTcx for Discriminant { fn from_tcx(disr: clean::Discriminant, tcx: TyCtxt<'_>) -> Self { Discriminant { - // expr is only none if going throught the inlineing path, which gets + // expr is only none if going through the inlineing path, which gets // `rustc_middle` types, not `rustc_hir`, but because JSON never inlines // the expr is always some. expr: disr.expr(tcx).unwrap(), diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs index d13efe6c113b..beb705400913 100644 --- a/src/librustdoc/json/mod.rs +++ b/src/librustdoc/json/mod.rs @@ -277,7 +277,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> { let e = ExternalCrate { crate_num: LOCAL_CRATE }; - // FIXME(adotinthevoid): Remove this, as it's not consistant with not + // FIXME(adotinthevoid): Remove this, as it's not consistent with not // inlining foreign items. let foreign_trait_items = self.get_trait_items(); let mut index = (*self.index).clone().into_inner(); diff --git a/src/librustdoc/passes/collect_intra_doc_links/early.rs b/src/librustdoc/passes/collect_intra_doc_links/early.rs index d121a3e2aa4a..1b373cfe5bb7 100644 --- a/src/librustdoc/passes/collect_intra_doc_links/early.rs +++ b/src/librustdoc/passes/collect_intra_doc_links/early.rs @@ -354,7 +354,14 @@ impl Visitor<'_> for EarlyDocLinkResolver<'_, '_> { self.parent_scope.module = old_module; } else { match &item.kind { - ItemKind::Impl(box ast::Impl { of_trait: Some(..), .. }) => { + ItemKind::Impl(box ast::Impl { of_trait: Some(trait_ref), .. }) => { + if let Some(partial_res) = self.resolver.get_partial_res(trait_ref.ref_id) + && let Some(res) = partial_res.full_res() + && let Some(trait_def_id) = res.opt_def_id() + && !trait_def_id.is_local() + && self.visited_mods.insert(trait_def_id) { + self.resolve_doc_links_extern_impl(trait_def_id, false); + } self.all_trait_impls.push(self.resolver.local_def_id(item.id).to_def_id()); } ItemKind::MacroDef(macro_def) if macro_def.macro_rules => { diff --git a/src/llvm-project b/src/llvm-project index 2a2ea6b49e79..a1232c451fc2 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit 2a2ea6b49e79325e0d10d33fac2b10ea3bebcc7c +Subproject commit a1232c451fc27173f8718e05d174b2503ca0b607 diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index 4bc91fc4030e..817b3e484194 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -53,7 +53,7 @@ pub struct ItemSummary { /// `["std", "io", "lazy", "Lazy"]` for `std::io::lazy::Lazy`). /// /// Note that items can appear in multiple paths, and the one chosen is implementation - /// defined. Currenty, this is the full path to where the item was defined. Eg + /// defined. Currently, this is the full path to where the item was defined. Eg /// [`String`] is currently `["alloc", "string", "String"]` and [`HashMap`] is /// `["std", "collections", "hash", "map", "HashMap"]`, but this is subject to change. pub path: Vec, @@ -351,7 +351,7 @@ pub enum Variant { /// A variant with unnamed fields. /// /// Unlike most of json, `#[doc(hidden)]` fields will be given as `None` - /// instead of being ommited, because order matters. + /// instead of being omitted, because order matters. /// /// ```rust /// enum Demo { diff --git a/src/stage0.json b/src/stage0.json index 72308d50c8eb..c3f50272b67c 100644 --- a/src/stage0.json +++ b/src/stage0.json @@ -17,349 +17,349 @@ "tool is executed." ], "compiler": { - "date": "2022-09-20", + "date": "2022-11-01", "version": "beta" }, "rustfmt": { - "date": "2022-09-20", + "date": "2022-11-01", "version": "nightly" }, "checksums_sha256": { - "dist/2022-09-20/cargo-beta-aarch64-apple-darwin.tar.gz": "3186f69cc7efaf3f933ad77798ddf58bf11c0719dc1dec53fadc502a236ef753", - "dist/2022-09-20/cargo-beta-aarch64-apple-darwin.tar.xz": "5ad195346a21a80c700ca08223060ea66298fe8e4cbac19148d14b92a9319b01", - "dist/2022-09-20/cargo-beta-aarch64-pc-windows-msvc.tar.gz": "6a7647d761ce3adba9d4ceff2e6c1929e9d96d767961a7a062f41ec09a1abb85", - "dist/2022-09-20/cargo-beta-aarch64-pc-windows-msvc.tar.xz": "13566a68dd2000fb33a990c21b62b82e77d1bd1f3384152f439cf96318f07f3e", - "dist/2022-09-20/cargo-beta-aarch64-unknown-linux-gnu.tar.gz": "cc698fe69e27a077c6d2aa8dc7319847b1ecd78ad4e3519161957c7cab90c592", - "dist/2022-09-20/cargo-beta-aarch64-unknown-linux-gnu.tar.xz": "1cd369b8ab90e85da78784cf08a92aee87f0804b448676637ee48cb3409dc026", - "dist/2022-09-20/cargo-beta-aarch64-unknown-linux-musl.tar.gz": "856170acebfd7900448fe02bd835d633b2930e2401c4211d72e5dd8c38943606", - "dist/2022-09-20/cargo-beta-aarch64-unknown-linux-musl.tar.xz": "5bbc32a426071c84d39395c64e1f9cfe0db29ab10c255c2a8a8f748b624cdb7a", - "dist/2022-09-20/cargo-beta-arm-unknown-linux-gnueabi.tar.gz": "50518c889d2408a7edf524c0340c8ff6881fd14f505dca0d419deefdb94c3afb", - "dist/2022-09-20/cargo-beta-arm-unknown-linux-gnueabi.tar.xz": "8143057e446c169e614c153ffbe2428e94404af96d06b1d3103028f695388211", - "dist/2022-09-20/cargo-beta-arm-unknown-linux-gnueabihf.tar.gz": "987064edfdb30dde07ef9b2cbd072f66ca042bf95ae724909cafbc13bcf69885", - "dist/2022-09-20/cargo-beta-arm-unknown-linux-gnueabihf.tar.xz": "253161f50a399818a77360f97443ef818dfca3d384e86048655b08c8a799bafc", - "dist/2022-09-20/cargo-beta-armv7-unknown-linux-gnueabihf.tar.gz": "7b9a0feee8f3e1e3c58df38f947904d76006c938a2395650e094337ede9918e9", - "dist/2022-09-20/cargo-beta-armv7-unknown-linux-gnueabihf.tar.xz": "2194b642b8ed595b8534ded204a72f910215c8f42482ac64644f784a3b2ae8b3", - "dist/2022-09-20/cargo-beta-i686-pc-windows-gnu.tar.gz": "7cc2c490988dd1ae095198664317cb0b5c8071fc31bf49aed1eca21eb2766cd8", - "dist/2022-09-20/cargo-beta-i686-pc-windows-gnu.tar.xz": "32641252e12cdadf0a232b43103fc56af621a48056864ff2ee9a034dd2da8f1f", - "dist/2022-09-20/cargo-beta-i686-pc-windows-msvc.tar.gz": "32581e6bf22e7d2c7a147a87158161e3fa07f46ec0252e632a3bafb824382a28", - "dist/2022-09-20/cargo-beta-i686-pc-windows-msvc.tar.xz": "a9a68905a4540389d28e40cc2137cf2fcca77c425089cd99072a34ba15e3ab6a", - "dist/2022-09-20/cargo-beta-i686-unknown-linux-gnu.tar.gz": "b5d9ecd7be4ab25919cd0731bb28a2612965943c5ccedf35ac09c169ed2c97db", - "dist/2022-09-20/cargo-beta-i686-unknown-linux-gnu.tar.xz": "387f7d95d04503293f708f65821f55878449eb5a0efe3344005dca18b84e6563", - "dist/2022-09-20/cargo-beta-mips-unknown-linux-gnu.tar.gz": "f8fbf21aac677276cdf246748d59d183e566bfcacabcd3eab6f19d6c857193ef", - "dist/2022-09-20/cargo-beta-mips-unknown-linux-gnu.tar.xz": "70e561d77632d1463839a8ea9cb72ff49afb61dbba95fa321bdba74be384b21f", - "dist/2022-09-20/cargo-beta-mips64-unknown-linux-gnuabi64.tar.gz": "e81b5a5fc70a1f7ed5920a3860b1259a2cecd9a1d981f2a564cd936de53ecf8a", - "dist/2022-09-20/cargo-beta-mips64-unknown-linux-gnuabi64.tar.xz": "ef9de44d1d37812bfeb67b353f1e308bf46d62c9fe191980de3a62fbfe5167a4", - "dist/2022-09-20/cargo-beta-mips64el-unknown-linux-gnuabi64.tar.gz": "68c1cd309775626f19431f7dbb73789b17ee629b588e05bf0231313913cb6a8a", - "dist/2022-09-20/cargo-beta-mips64el-unknown-linux-gnuabi64.tar.xz": "db290a98987c8bd527f1efe9ff09055fdce8eea0673d2c5eba0640649396b8d0", - "dist/2022-09-20/cargo-beta-mipsel-unknown-linux-gnu.tar.gz": "f4a7da60f164c03bd274f8c98b58a524d7a73476c726e2ef5695f2be950421c7", - "dist/2022-09-20/cargo-beta-mipsel-unknown-linux-gnu.tar.xz": "8fc46e05ba0830eec88e1d764b02fb9a4836883fd180800a8edd3a4cf0acbdae", - "dist/2022-09-20/cargo-beta-powerpc-unknown-linux-gnu.tar.gz": "5f528af9436bfa31d559544220fcb59001a90bff18363390f7fab82f259defde", - "dist/2022-09-20/cargo-beta-powerpc-unknown-linux-gnu.tar.xz": "502d6da5158075cd997833314f9ca7a527aecc8e28c9e26ee9796c2e9ac91cf5", - "dist/2022-09-20/cargo-beta-powerpc64-unknown-linux-gnu.tar.gz": "16a7b922fa6c6019541e859386dbd38e64507d951376c847f83c6b983c72b417", - "dist/2022-09-20/cargo-beta-powerpc64-unknown-linux-gnu.tar.xz": "7f3e361f9bfcdb5fd765f86ed372e00df62af4ae5714d9a2b3324f3929518677", - "dist/2022-09-20/cargo-beta-powerpc64le-unknown-linux-gnu.tar.gz": "f38265c64d6ac34d4632f38368d910bd9471aaf8736595623126cb53e810e307", - "dist/2022-09-20/cargo-beta-powerpc64le-unknown-linux-gnu.tar.xz": "99e7f3795aea0abb019b80b1f35aa8e1638fee35e548424ca52fd23c5bf82c71", - "dist/2022-09-20/cargo-beta-riscv64gc-unknown-linux-gnu.tar.gz": "bb9e89b65fca9a1fad5293e8a52b27331f08e9660237c0b1e7f750064d45ab1d", - "dist/2022-09-20/cargo-beta-riscv64gc-unknown-linux-gnu.tar.xz": "ea81188084da2f2771a1d9414c20065a19544b1af0e56dc71eae7ca00ff72708", - "dist/2022-09-20/cargo-beta-s390x-unknown-linux-gnu.tar.gz": "745577c8c52065d84cedd32608ca0e17f1a46bb86b4d619cd01785486dc99480", - "dist/2022-09-20/cargo-beta-s390x-unknown-linux-gnu.tar.xz": "85944275d5d05943c89ebf8e487bee35ed8586aa9f1903f83490c12ba74ad8cf", - "dist/2022-09-20/cargo-beta-x86_64-apple-darwin.tar.gz": "6c3f841c718404d4917353c7fefee7491df62d7456633bfb99dc850a49aab285", - "dist/2022-09-20/cargo-beta-x86_64-apple-darwin.tar.xz": "0c5a5c3ceec9fcf3b8dbb9fd10172251622e873671049b042b55ede34b8797a8", - "dist/2022-09-20/cargo-beta-x86_64-pc-windows-gnu.tar.gz": "385a01c7a11a5f51253e8182d82763295037d625e7bcf27d54b5f0349cea488c", - "dist/2022-09-20/cargo-beta-x86_64-pc-windows-gnu.tar.xz": "fe498e30198a43586be82c6fbd794093299eddba51fd668d81aed88bef0471ae", - "dist/2022-09-20/cargo-beta-x86_64-pc-windows-msvc.tar.gz": "3ee6926fd5f491eecf574aba631d09d97a9332b936eb7bb0ab348ac3fa02db02", - "dist/2022-09-20/cargo-beta-x86_64-pc-windows-msvc.tar.xz": "d15a8c24d04b308b91d9114b583087e2a13e75920a1837a78fe330cf6892ce4e", - "dist/2022-09-20/cargo-beta-x86_64-unknown-freebsd.tar.gz": "89368ca5eae65a569ae98e66834e93b240ef43a007e02e768ae9bbd5de4a4cf6", - "dist/2022-09-20/cargo-beta-x86_64-unknown-freebsd.tar.xz": "20b46e126c900892d576e972e39409c9009bafa4b3c159e624179d77afa912a9", - "dist/2022-09-20/cargo-beta-x86_64-unknown-illumos.tar.gz": "1712fd404274c993b95aa44dea6b9ff3b0f9857d8d1646e0cbf454d3386f5e32", - "dist/2022-09-20/cargo-beta-x86_64-unknown-illumos.tar.xz": "bd3f848d22bfa19060d459167b6154cc79070e0066f8da79587390fb92404288", - "dist/2022-09-20/cargo-beta-x86_64-unknown-linux-gnu.tar.gz": "0e5869b406dbaab0ef123459a93d4d6a34e85e9bd72d8a206bef69aac9e84d5c", - "dist/2022-09-20/cargo-beta-x86_64-unknown-linux-gnu.tar.xz": "5b0b255fb82d0e751187c6cc6b64298ca014ef86782984ef9e57a0b2ab373f24", - "dist/2022-09-20/cargo-beta-x86_64-unknown-linux-musl.tar.gz": "09dcaeb783d7c57aa8c708295cf46bdcb3873a20ca30794b3c1a8797b2cc9476", - "dist/2022-09-20/cargo-beta-x86_64-unknown-linux-musl.tar.xz": "918ca6c81e9e19f9d84d80e508af0050f2ec2ce2d0d0aa40cd3afd524d69917b", - "dist/2022-09-20/cargo-beta-x86_64-unknown-netbsd.tar.gz": "e66e3ecf93bad48573cf34ed44d508908370a8cc0c2764001cddbef022fb6e73", - "dist/2022-09-20/cargo-beta-x86_64-unknown-netbsd.tar.xz": "03514df0f9f2193824e227c19b84f282d7cb90145206bcfa21cf4f8223047462", - "dist/2022-09-20/rust-std-beta-aarch64-apple-darwin.tar.gz": "7a6a03adcf6481d90d39e929f99a50ed170e98fe61c3fae5264c3aa4d99530ca", - "dist/2022-09-20/rust-std-beta-aarch64-apple-darwin.tar.xz": "ccc8d4fb07a0a9c57b60d05bcf6d076a8b3cdb397930182ebfe99a2e5cd629da", - "dist/2022-09-20/rust-std-beta-aarch64-apple-ios-sim.tar.gz": "3e70853d9fbc3dab4a39303b2281ad63d36a9ae2fd8d6bb7d96f184644e20531", - "dist/2022-09-20/rust-std-beta-aarch64-apple-ios-sim.tar.xz": "59bb5bb6cd8d7269bfd29a952cd26280f5091fb24af4e7bf10cd59b80323d85a", - "dist/2022-09-20/rust-std-beta-aarch64-apple-ios.tar.gz": "da321d56c24e6c2aa326cc082912498c27231f0f0fea27ab925807108d6f329e", - "dist/2022-09-20/rust-std-beta-aarch64-apple-ios.tar.xz": "21e78983abd4523ac9efb0405734ebfd6c8c09b9cc89b9d052b1a58bb7ab798c", - "dist/2022-09-20/rust-std-beta-aarch64-fuchsia.tar.gz": "dd64a476c35b1a6aefed6bcc756cb4562a60ec0277e5661241018678d7a04268", - "dist/2022-09-20/rust-std-beta-aarch64-fuchsia.tar.xz": "bcbb6f3457c9b6e1c9109094536445ff208e78b5a24485af6de580ba7e279861", - "dist/2022-09-20/rust-std-beta-aarch64-linux-android.tar.gz": "63ab6db951f5cefbd1ab1661ffbac9749fec8d4165047dfbcb76b7dcb1468e48", - "dist/2022-09-20/rust-std-beta-aarch64-linux-android.tar.xz": "f3e89fe21779ecd8373280f38e29db8941c0836cba7314414d854ba685e075e4", - "dist/2022-09-20/rust-std-beta-aarch64-pc-windows-msvc.tar.gz": "fa187421633e7ca45948258e804fd4a8177070b9a4b964ac95231018cd5e724c", - "dist/2022-09-20/rust-std-beta-aarch64-pc-windows-msvc.tar.xz": "af981f04545aca6d3fe301a22773ff38077e8c8d437b862a3d7f1e8040bfaebc", - "dist/2022-09-20/rust-std-beta-aarch64-unknown-linux-gnu.tar.gz": "22fb1351c35e4d5b12d043cdf1de51a13176fc60518fa89226f3af9dc2e727b6", - "dist/2022-09-20/rust-std-beta-aarch64-unknown-linux-gnu.tar.xz": "690a7880a563138bbd583b537ddb80bd738d8fceb4cab083bc8bbd1fa1ee2f99", - "dist/2022-09-20/rust-std-beta-aarch64-unknown-linux-musl.tar.gz": "2d5135344f76decf74633d95e2fc98c416093ca962cac608564abf600ff117bc", - "dist/2022-09-20/rust-std-beta-aarch64-unknown-linux-musl.tar.xz": "8f63623f1e0cae99c5d1c4bb1c636fb773ed06dc1d33a251c9253278f7bc1300", - "dist/2022-09-20/rust-std-beta-aarch64-unknown-none-softfloat.tar.gz": "105123c48c7e0946872b8ca0fcc897c2a1fcccb9b71b1805eca01e713a509c0e", - "dist/2022-09-20/rust-std-beta-aarch64-unknown-none-softfloat.tar.xz": "9c9ec824e1db607bfac14ce8a5d1e73aeb8670e655acb4577a8f6ee783202aeb", - "dist/2022-09-20/rust-std-beta-aarch64-unknown-none.tar.gz": "b25fb3d1c41e193b724469898efe9d0f5d282de06d5224ae573c8870ccb5ed4d", - "dist/2022-09-20/rust-std-beta-aarch64-unknown-none.tar.xz": "4f87a8fc869f80072ad2a07896e50ee97b3412badeb69bb37f67ef47ff0d00d5", - "dist/2022-09-20/rust-std-beta-arm-linux-androideabi.tar.gz": "9ee992110e4bcf9a00bec8635cbe5bbeb241d2fb6b567060fa0507406708c8dd", - "dist/2022-09-20/rust-std-beta-arm-linux-androideabi.tar.xz": "aa5981a138a103843462a5a6987fcf0c7c335a5896517505d2e54fb288d7af1e", - "dist/2022-09-20/rust-std-beta-arm-unknown-linux-gnueabi.tar.gz": "854d0d7c9932d4533d642e663ffa465741a3d0ec400a2c4b74324debaa0da27f", - "dist/2022-09-20/rust-std-beta-arm-unknown-linux-gnueabi.tar.xz": "83b7b2dc823608777dc0e2f290fb5a2f8e6e35f4930ce2170309c14a54f136b3", - "dist/2022-09-20/rust-std-beta-arm-unknown-linux-gnueabihf.tar.gz": "9c84cc8ff79098be62011d572cd928faa4cf76c9c3e94060babc042073f3b7a1", - "dist/2022-09-20/rust-std-beta-arm-unknown-linux-gnueabihf.tar.xz": "b34a7bdd0ddac9e8b4ea8e1db8d86389c623a6629edbbd0052f890df75465fa6", - "dist/2022-09-20/rust-std-beta-arm-unknown-linux-musleabi.tar.gz": "49ddce546458f47683928fe341f5eacaec11c3c5a378ce8802c4b97425100905", - "dist/2022-09-20/rust-std-beta-arm-unknown-linux-musleabi.tar.xz": "e64130a854017b921a086749550fe92339cef0890ee645acbe23a30f5169a8ad", - "dist/2022-09-20/rust-std-beta-arm-unknown-linux-musleabihf.tar.gz": "770bab36bd3bbc07739f0cc89c09689189edf7518e740f794be9aa7aed0917f5", - "dist/2022-09-20/rust-std-beta-arm-unknown-linux-musleabihf.tar.xz": "b390f1111f5566b37d3f9384126a6d3dd0cdf9468dea19747394cdaae1c87c7c", - "dist/2022-09-20/rust-std-beta-armebv7r-none-eabi.tar.gz": "de22ffe26c7ef21d316933618b37c832353a7e5a1fb8b84af7bca98626fbc78e", - "dist/2022-09-20/rust-std-beta-armebv7r-none-eabi.tar.xz": "82b77c48fdb685b63bea0d40437d5dcee41500cec0360e393511b2c69d2c7381", - "dist/2022-09-20/rust-std-beta-armebv7r-none-eabihf.tar.gz": "94d427c65134e1e208f662acb3eb65a455016e3bed162bc4fcea9c6455e0474e", - "dist/2022-09-20/rust-std-beta-armebv7r-none-eabihf.tar.xz": "cdd71383ec150dbb2514a48300344a8547d7fee2c797e693cd7803354c5fca13", - "dist/2022-09-20/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.gz": "0241f8f9ffd00c02860158da46e20290d2ba563f93e8fa22324da2b87c347a0d", - "dist/2022-09-20/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.xz": "a780eb139416f2383f16fa63e72e56f0a77c67d2ac2f10f803802660e8ca30bc", - "dist/2022-09-20/rust-std-beta-armv5te-unknown-linux-musleabi.tar.gz": "1628e0c7a8cd8fb2b90db0e3d30f0d36768dbdeca640af967a14b9031d2d4c3f", - "dist/2022-09-20/rust-std-beta-armv5te-unknown-linux-musleabi.tar.xz": "4b06bbd8a0ae240bb19c93b54bd9da3bfe6c3f37a70d2b73dfcc9d1eff8fdaae", - "dist/2022-09-20/rust-std-beta-armv7-linux-androideabi.tar.gz": "9aaeffdd99e9dedb2bffa79adef2096ef29b48ffd2681ede2ea8d63179082f89", - "dist/2022-09-20/rust-std-beta-armv7-linux-androideabi.tar.xz": "d1d0ae4718eb43d4759cbe14a90b422edf0f451c8cb8624368800eff0f05c13e", - "dist/2022-09-20/rust-std-beta-armv7-unknown-linux-gnueabi.tar.gz": "5cf573ce6729200cc211924e45796a9aaf85001272a8690803944dbc91b5a2fb", - "dist/2022-09-20/rust-std-beta-armv7-unknown-linux-gnueabi.tar.xz": "51cecc72479f18c94620b2b022b89403e8ba519e36cb7a6f8c208a9ab6adb17c", - "dist/2022-09-20/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.gz": "c6e6962438e0a8ebdc7a7c74712d75642acfeb8c4b0753354a7d3b64da6948cc", - "dist/2022-09-20/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.xz": "4b812cfa63c380448b0e2ca2e03cfad73ba9951080eff1f76feaee25f67bdf39", - "dist/2022-09-20/rust-std-beta-armv7-unknown-linux-musleabi.tar.gz": "89cdaf10019fc3ddb83ca1848adf8ac3411820a9095dc337b9a962f1e58be058", - "dist/2022-09-20/rust-std-beta-armv7-unknown-linux-musleabi.tar.xz": "e21ac9106eae28b25270f6c1ce70e2c94273919c9c72d22f0fe3ad0b8f0a57f0", - "dist/2022-09-20/rust-std-beta-armv7-unknown-linux-musleabihf.tar.gz": "e218b7e7d148379411502023d6ede2831d76e29d5d2427d030f916a0c14c8ffa", - "dist/2022-09-20/rust-std-beta-armv7-unknown-linux-musleabihf.tar.xz": "2797698cf0f218c17eb22f43f806663723f9c89084fdc08d40adbbcde5a79f88", - "dist/2022-09-20/rust-std-beta-armv7a-none-eabi.tar.gz": "1d5c29d803cb8ef005baf44ca6a0b1fccc4227ab3585046f0d69cbf153be10d7", - "dist/2022-09-20/rust-std-beta-armv7a-none-eabi.tar.xz": "50259e9b9672baf54e176c252e9068cf020c4a79a825bafc5ee21fb46b9af815", - "dist/2022-09-20/rust-std-beta-armv7r-none-eabi.tar.gz": "805fc5ae72249f27ddbdd8afdc188b4f67dfe51822eb813e681259da51dbc75c", - "dist/2022-09-20/rust-std-beta-armv7r-none-eabi.tar.xz": "75151349dc9b6fd3b3a78d38827e517adc6935356bae0c5bc93bae62e1759db8", - "dist/2022-09-20/rust-std-beta-armv7r-none-eabihf.tar.gz": "ad4e0347c3e9b3f4936f26798ae2a8c502a4599c3357baf0b0a4cc3516c471bf", - "dist/2022-09-20/rust-std-beta-armv7r-none-eabihf.tar.xz": "4de3e5a729597473759b2db1f7e2ab633c98bb1d8125de6f458fe3bd0ee7d8c7", - "dist/2022-09-20/rust-std-beta-asmjs-unknown-emscripten.tar.gz": "0b3c92539f3aec14501e09db3aa02854ef98fb4dc89721306798ea163041e669", - "dist/2022-09-20/rust-std-beta-asmjs-unknown-emscripten.tar.xz": "26deacf11e62187673718ea8805b23dc2dd352efdedc11b396e117637a96ceae", - "dist/2022-09-20/rust-std-beta-i586-pc-windows-msvc.tar.gz": "a11ca7a7f67e225365423a704d7139d3c9193699493f1f193d579c126d492475", - "dist/2022-09-20/rust-std-beta-i586-pc-windows-msvc.tar.xz": "08d8aad1d608d2ff626f4e7e4300a31fc3f96c0ef1e5bd0ec179b98d4194fca9", - "dist/2022-09-20/rust-std-beta-i586-unknown-linux-gnu.tar.gz": "3524e1ba92b9ccd1fcfd40a6018efa697d63177cc0a8c9cd016be833aff371f4", - "dist/2022-09-20/rust-std-beta-i586-unknown-linux-gnu.tar.xz": "a6df83285ba5732eca9adf3f39d0a4087249b29cd0c33ee2272f68930df43191", - "dist/2022-09-20/rust-std-beta-i586-unknown-linux-musl.tar.gz": "6951d7aecd555a0dc485f57ad16703af65315c464aebcf73171bbda2273dba0a", - "dist/2022-09-20/rust-std-beta-i586-unknown-linux-musl.tar.xz": "f07c4a267740e46a4013b130e9d1e10492e45dc0226a08f5c909076ade466737", - "dist/2022-09-20/rust-std-beta-i686-linux-android.tar.gz": "dd47fbd29b3b025388352fafe693f25e43c1287f69c2185fecfb0d60e13a7fc3", - "dist/2022-09-20/rust-std-beta-i686-linux-android.tar.xz": "0eebd41330762a4bad438c40f134601e59a7b73043952b2e090a801adff41727", - "dist/2022-09-20/rust-std-beta-i686-pc-windows-gnu.tar.gz": "58cd47de74c201bfed62a8980c2447f97e7c129726d3d28c2140d880fa6d7975", - "dist/2022-09-20/rust-std-beta-i686-pc-windows-gnu.tar.xz": "7fd68f0f9eea4d8256132af2f2c269c58a278b757888e591716a553b87ffcf8c", - "dist/2022-09-20/rust-std-beta-i686-pc-windows-msvc.tar.gz": "816ef343a7ed908706d8f4e7cb915787a4e27c2390cc7c3f6e2210f3ad7c4cda", - "dist/2022-09-20/rust-std-beta-i686-pc-windows-msvc.tar.xz": "2475326f3d32e8ae309750c1639cdc6cce3474fb5d4820b31b46c9c12401b63e", - "dist/2022-09-20/rust-std-beta-i686-unknown-freebsd.tar.gz": "24a897b9916bcd4ad775d96f9751b06663eed599086d0665b83dd4c16af871ab", - "dist/2022-09-20/rust-std-beta-i686-unknown-freebsd.tar.xz": "959725ac2f49d1944c53846d920ab4e8769976d4025bc32bc63e5372b751a8de", - "dist/2022-09-20/rust-std-beta-i686-unknown-linux-gnu.tar.gz": "35eb28ff2d3b383ac1b34bf6eded87f824ef93eb2c2d12c300b136c7c735ced7", - "dist/2022-09-20/rust-std-beta-i686-unknown-linux-gnu.tar.xz": "31085015fbfa608e6d0828a367d84b48679c6a33d55ae32affe37307818b1086", - "dist/2022-09-20/rust-std-beta-i686-unknown-linux-musl.tar.gz": "ef294d01caeba013cc3173b5fab5daac4f0c64e57410f778f2891dff03f23875", - "dist/2022-09-20/rust-std-beta-i686-unknown-linux-musl.tar.xz": "4ad7915a9e54f7d911864adbc097941a9c051e0c97c8eee1c04158a5755e4e4a", - "dist/2022-09-20/rust-std-beta-mips-unknown-linux-gnu.tar.gz": "cd8e8fe2af17def5bf19a8a0993317af5c33833de850a489ef2dee54c61dbca7", - "dist/2022-09-20/rust-std-beta-mips-unknown-linux-gnu.tar.xz": "f2f95555e3564f7b16bcde9ae4c6a30752900519fba68304c4f74b7e508bacc3", - "dist/2022-09-20/rust-std-beta-mips-unknown-linux-musl.tar.gz": "7d42b6d7f028c637f7f9a2f0c14fde880e00098bf4141289620232a263fa8eb0", - "dist/2022-09-20/rust-std-beta-mips-unknown-linux-musl.tar.xz": "50dc97ba9ce28d4252f03f78e23bc05a702d5c1c5ad67b70a358406419f25721", - "dist/2022-09-20/rust-std-beta-mips64-unknown-linux-gnuabi64.tar.gz": "7c14decb2404c616319c99415f65c1383264151f3802ffedfdff4962db310828", - "dist/2022-09-20/rust-std-beta-mips64-unknown-linux-gnuabi64.tar.xz": "230849a3b6dff89671bf94c73391eee43107c81ff65c795eb1c9f30b9bb52176", - "dist/2022-09-20/rust-std-beta-mips64-unknown-linux-muslabi64.tar.gz": "0a63c8f568d9ef12b75b9bcd53faf727bc029b4a1268c53fef913e58be94eff7", - "dist/2022-09-20/rust-std-beta-mips64-unknown-linux-muslabi64.tar.xz": "5b1020d65f651cd1778448618bca55906eef981842b73c18be1b5ec785d6bf06", - "dist/2022-09-20/rust-std-beta-mips64el-unknown-linux-gnuabi64.tar.gz": "3f747d83397942c88ad79a7bde1f98a57d0b416620f08ab57edb64f3b101f493", - "dist/2022-09-20/rust-std-beta-mips64el-unknown-linux-gnuabi64.tar.xz": "babdc0c87bdc8146fc6645da34776be98575019eacb95529c00060f8afcbb1f4", - "dist/2022-09-20/rust-std-beta-mips64el-unknown-linux-muslabi64.tar.gz": "f0bc0b5fddf7ff8251fefa4068fdb623b47bbd1e81c2c732ce2304e4ce78bb20", - "dist/2022-09-20/rust-std-beta-mips64el-unknown-linux-muslabi64.tar.xz": "33e3cc3766f941f3668a93240d627c7357b22a13facff5937821d92a21afe444", - "dist/2022-09-20/rust-std-beta-mipsel-unknown-linux-gnu.tar.gz": "33ae5fdfab257ddb004eb80ff1a8d0351675df06b97951d5c47071ac8b18ba9a", - "dist/2022-09-20/rust-std-beta-mipsel-unknown-linux-gnu.tar.xz": "9f16e21c54944afe83a5e1a3e489a2dcad947f367cbb17c6ffcfd2c503e03f25", - "dist/2022-09-20/rust-std-beta-mipsel-unknown-linux-musl.tar.gz": "065f8929bef2ff2ec0067c788fe64e0a08af0f5e11ac5d67e29e5225557d6d9a", - "dist/2022-09-20/rust-std-beta-mipsel-unknown-linux-musl.tar.xz": "c3c54ba9dfee447e33d76ba8060bb9b6081103fa6809ad77c3221ea064ed3fc8", - "dist/2022-09-20/rust-std-beta-nvptx64-nvidia-cuda.tar.gz": "5627e25a24792131e8c0a1c605908bd56ab5a55614b8e17c23233fdc14d25e81", - "dist/2022-09-20/rust-std-beta-nvptx64-nvidia-cuda.tar.xz": "3ee1b5228665f2d5d7ac500d6fa6a2d0fd771eaafb5c393419713a11bf8d0875", - "dist/2022-09-20/rust-std-beta-powerpc-unknown-linux-gnu.tar.gz": "cbf922bba400e798cd32e5404a804400f79ed03ba5cc433173eac96ba9e06976", - "dist/2022-09-20/rust-std-beta-powerpc-unknown-linux-gnu.tar.xz": "bc573b0012568f0bf397870660f7527697b1c65e1a7387d2419c6f63ba001bdd", - "dist/2022-09-20/rust-std-beta-powerpc64-unknown-linux-gnu.tar.gz": "917b948988edd4e5c5e441102f55b1541318d47c0cd5d958a69ddc6fbfda84d1", - "dist/2022-09-20/rust-std-beta-powerpc64-unknown-linux-gnu.tar.xz": "23e45711ce82b6baf9d27d909d8fe4bc6a5f32c91dfbc280708abfe5c362bc89", - "dist/2022-09-20/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.gz": "448710ce6f3e72d0831ae882fa37196685a28dacb6f0f499370fc2882427044f", - "dist/2022-09-20/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.xz": "831bc2bcb21ab9fe82fc64eb377d6d80b47198dc82677b7bd630d89819914e20", - "dist/2022-09-20/rust-std-beta-riscv32i-unknown-none-elf.tar.gz": "14a61cc3d6740a9033c6570264e6c9356120235f42e5ded8ebec1d592f17b47f", - "dist/2022-09-20/rust-std-beta-riscv32i-unknown-none-elf.tar.xz": "c73aa37081b4248324a459b48378b4a412a3561a12bad3ae064c8336ec862dd6", - "dist/2022-09-20/rust-std-beta-riscv32imac-unknown-none-elf.tar.gz": "f78a367b9977471dae06fffd6049f03585e826fe648a2d1d8ee20ff19dfc913f", - "dist/2022-09-20/rust-std-beta-riscv32imac-unknown-none-elf.tar.xz": "e8ef0dfd9d7df08fcdb7136619d8b9cd31435e0de130f87c799117dba9614a54", - "dist/2022-09-20/rust-std-beta-riscv32imc-unknown-none-elf.tar.gz": "a7cd118c91f4de3a1e1c5d9326080bf39661f708c675272cf697ab9675001705", - "dist/2022-09-20/rust-std-beta-riscv32imc-unknown-none-elf.tar.xz": "58023f2bd169455d97537aca228749e9a980e18d72f67ae8c0caff4ad2d4fd64", - "dist/2022-09-20/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.gz": "726efb0f7cbac9d13deac41d0937ce5f707d8e858b3bf9096c40fd41b2663d4a", - "dist/2022-09-20/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.xz": "73395ddaa22a91a1de120704f22d86b0624de52af773755046ec809b76b88954", - "dist/2022-09-20/rust-std-beta-riscv64gc-unknown-none-elf.tar.gz": "2504e53a04ab1360309dd9e733798a94fa3a92cc575148073dc4870bb26367e9", - "dist/2022-09-20/rust-std-beta-riscv64gc-unknown-none-elf.tar.xz": "f182ac3b6222a333d73f3f612fb31f9d19d4e03456e16b0967cb55d1292ec05a", - "dist/2022-09-20/rust-std-beta-riscv64imac-unknown-none-elf.tar.gz": "9612105b0d40225d54f6b02bcf75e0d7d232331fa22a24de4895f97519dfd6a6", - "dist/2022-09-20/rust-std-beta-riscv64imac-unknown-none-elf.tar.xz": "448b0cd5cc35096e0fd51a7cb1948e87b7c46eafe665dbfda802a4947e08665a", - "dist/2022-09-20/rust-std-beta-s390x-unknown-linux-gnu.tar.gz": "72459d840371f7203e15622a82ccb5e3559db52934445943fc41b11a58b07302", - "dist/2022-09-20/rust-std-beta-s390x-unknown-linux-gnu.tar.xz": "39ca5b49d6e909c81deb8c7391e01da9ef51cabea55fa55dc16cf654abb089a1", - "dist/2022-09-20/rust-std-beta-sparc64-unknown-linux-gnu.tar.gz": "ff74af39ca1446f06f891beb22f1e24fb0b450d97c889dc2e27e0f53fc19b26f", - "dist/2022-09-20/rust-std-beta-sparc64-unknown-linux-gnu.tar.xz": "f56aa4d390eb4b25aa51685ee773b589ebc87bbed7495a07d8af0d3b3cc7c715", - "dist/2022-09-20/rust-std-beta-sparcv9-sun-solaris.tar.gz": "df727ef09f0549051ff5b4fcd04cb22d4244cea85f97e43880e563c45b558cd6", - "dist/2022-09-20/rust-std-beta-sparcv9-sun-solaris.tar.xz": "f575d16acc0a8cc5b96e038d828ac023c302e38efed271d5101a885d3af35cfc", - "dist/2022-09-20/rust-std-beta-thumbv6m-none-eabi.tar.gz": "838be75f56d84d88ab01b0897d9b166b6ea26c527705df2d2a7368968439505f", - "dist/2022-09-20/rust-std-beta-thumbv6m-none-eabi.tar.xz": "b5b33e2de72e71ef5024fb50f0f6e91f32b4747f666aa7069e695b15119a1963", - "dist/2022-09-20/rust-std-beta-thumbv7em-none-eabi.tar.gz": "dbaffcc17215c4342a40c049a9538be44837bcf86a7d65fb2c877f831beca337", - "dist/2022-09-20/rust-std-beta-thumbv7em-none-eabi.tar.xz": "14fabdf3f4cda3bdb3ac139c95d31c2a20e3a88cd9f83e803f9c9bd6e3f9f83a", - "dist/2022-09-20/rust-std-beta-thumbv7em-none-eabihf.tar.gz": "3acc833a086a9b46db81cbf03fcf2dc366a4b3d32eaeedcc2deee8ceea31449a", - "dist/2022-09-20/rust-std-beta-thumbv7em-none-eabihf.tar.xz": "f6cf227b6da03855f1624bb325bd802b8ae6b15d7bbfbfbf8b9de1a74aedb6fe", - "dist/2022-09-20/rust-std-beta-thumbv7m-none-eabi.tar.gz": "e5054ca1e295709654b214847691f4fa9f031104725632dc056853382e74e733", - "dist/2022-09-20/rust-std-beta-thumbv7m-none-eabi.tar.xz": "b44f0462a5f44e762653fecd816dcd5ba5a2f9d8dd3efcec259b250f3af5237d", - "dist/2022-09-20/rust-std-beta-thumbv7neon-linux-androideabi.tar.gz": "4fb8000ce077d346d85005343284f56ae936eac334c72cb8c170dbe810aad740", - "dist/2022-09-20/rust-std-beta-thumbv7neon-linux-androideabi.tar.xz": "77955483882897980f4365bdc71fca9f39d675ff210519702b9cf3f2a6bcc2a8", - "dist/2022-09-20/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.gz": "566b9ea01182e6c7e3844911ed08fe5eb1d848c3de89d4f123d80fd70ff37ddd", - "dist/2022-09-20/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.xz": "33cc9c12d909bd4166be8570e27da8c1884ac1ce82d9548e16d926d9885ff62d", - "dist/2022-09-20/rust-std-beta-thumbv8m.base-none-eabi.tar.gz": "62a345ea2aa55b1a02053e7c49988c5f14d6b90d487b9c4916e40ad31957d1f5", - "dist/2022-09-20/rust-std-beta-thumbv8m.base-none-eabi.tar.xz": "37fd80af2188d837e870d3c8399767be15070344fc87d2b37b5126095252afc5", - "dist/2022-09-20/rust-std-beta-thumbv8m.main-none-eabi.tar.gz": "32a10d7eaaf2189d74700187fbc8c2ebde08b5efa06440c2b4f65fb85eac3ecc", - "dist/2022-09-20/rust-std-beta-thumbv8m.main-none-eabi.tar.xz": "dae8d6deecc5277be729803ed55dc134c1ef5bae49fa0fe7ea4f0eee9d7d19fb", - "dist/2022-09-20/rust-std-beta-thumbv8m.main-none-eabihf.tar.gz": "44457f84d46ccd201771cdae9520caebf813d3283b08f73fabab59b52b485a98", - "dist/2022-09-20/rust-std-beta-thumbv8m.main-none-eabihf.tar.xz": "1e5bcc82a403c1c4c5fa41fa78de9aa4378a2901e3170d9542eb3b21d130e36a", - "dist/2022-09-20/rust-std-beta-wasm32-unknown-emscripten.tar.gz": "b7d31d0ec6fd1355d1323555ec8d1cb4b9f30bab32a75d0d8efaf465ab2aedcc", - "dist/2022-09-20/rust-std-beta-wasm32-unknown-emscripten.tar.xz": "a25b5c180ca97d074d563c6fe1d826db23eba9212d12cb3d60d39b7841a5618d", - "dist/2022-09-20/rust-std-beta-wasm32-unknown-unknown.tar.gz": "31aeed83d12732c40e51cf59a3dd8dd1fe7ba2ed047cab65f8bfca8c72d158ed", - "dist/2022-09-20/rust-std-beta-wasm32-unknown-unknown.tar.xz": "28757fc2c5304b3110b2358d252fb4aaa8d811999bd9881e118bc71b0e6b01a3", - "dist/2022-09-20/rust-std-beta-wasm32-wasi.tar.gz": "b69ecad8480c2d33b854ba3387a0563df53546b8a2b55639fa20d1c104f35050", - "dist/2022-09-20/rust-std-beta-wasm32-wasi.tar.xz": "a09185a76891928cc65e4139373df6f22fd0060388ccc4530cc0be5310f8aaa7", - "dist/2022-09-20/rust-std-beta-x86_64-apple-darwin.tar.gz": "97f5a3dd01a7b5efe44662f2376826c184d212754c730bfaa21cd03235676142", - "dist/2022-09-20/rust-std-beta-x86_64-apple-darwin.tar.xz": "131b37e9d3c2335fb51427a7e0ab43362efccf90a4e001ef52e54aa221634eb8", - "dist/2022-09-20/rust-std-beta-x86_64-apple-ios.tar.gz": "d900fc396731c95a57d43519a109202cd2ed8e574df300cd6124c6390d1584a3", - "dist/2022-09-20/rust-std-beta-x86_64-apple-ios.tar.xz": "694dc51239481fe8f5ec2291b62435e0a7622591f2709ea4709749c7d8c01db3", - "dist/2022-09-20/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.gz": "c2fad6b25e33e4e52fab6d20b6e1bbf78f230e9b387f260b48f940bff67386f5", - "dist/2022-09-20/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.xz": "c3fe305a1082cbf1f5bbdaab5a94c0ad88037a4f99e556cf06ff1270a806f437", - "dist/2022-09-20/rust-std-beta-x86_64-fuchsia.tar.gz": "75b5aaf5206953d16a418c83f6e6d21a0adcbb0c81b5c1a8f467f3e5aa33c038", - "dist/2022-09-20/rust-std-beta-x86_64-fuchsia.tar.xz": "7fc38dbfb7833e9b6336f4aa38706a92e1922231ee875100e16274c571110757", - "dist/2022-09-20/rust-std-beta-x86_64-linux-android.tar.gz": "bc4a42ffc51bc3be27907a73de98fe4c8cf3b205fd1e7c75a9cb3bd30bcc5fbb", - "dist/2022-09-20/rust-std-beta-x86_64-linux-android.tar.xz": "68e02875090c7d382e8b21d0102712e7c9d583d657b6c51b4f939e3a9b7f884c", - "dist/2022-09-20/rust-std-beta-x86_64-pc-solaris.tar.gz": "11edbb1de67f00263f31635bdf006966143aa18423f574cb64ef966301a0fe3a", - "dist/2022-09-20/rust-std-beta-x86_64-pc-solaris.tar.xz": "44743e8f113e3e5bc7ce66902533f6ac59538f8f61fa6ab6c311bbf2ebe75e43", - "dist/2022-09-20/rust-std-beta-x86_64-pc-windows-gnu.tar.gz": "a705945830b23a25f272600470d687a1a9f5d4f8a01c5fed9e495a444b2aa9ee", - "dist/2022-09-20/rust-std-beta-x86_64-pc-windows-gnu.tar.xz": "9e4a045a2e0f754ec6afd897f1bcb66bccfc2b5bb91f141fa8d3d47723eb6090", - "dist/2022-09-20/rust-std-beta-x86_64-pc-windows-msvc.tar.gz": "910de4547c1112170b0981fecfc926cff4c47cc622648b83fea9f79c171cb05a", - "dist/2022-09-20/rust-std-beta-x86_64-pc-windows-msvc.tar.xz": "f9be82ac8c5f64c17cfbf0270b8f71ec71e6b3ba7c9521980a73d6f848f6014c", - "dist/2022-09-20/rust-std-beta-x86_64-sun-solaris.tar.gz": "e1097c0bd31baa61902be4a8a47a674faa15466ce9352213cf7808f50d5854ff", - "dist/2022-09-20/rust-std-beta-x86_64-sun-solaris.tar.xz": "82866dc52808549acd683d5b7c47fe97c992ea70cb6fff941007960a25f3b645", - "dist/2022-09-20/rust-std-beta-x86_64-unknown-freebsd.tar.gz": "2b7c60a6b830d557a6df8865bc8dd658c84037a5893b11db8e11dadb527b5d6f", - "dist/2022-09-20/rust-std-beta-x86_64-unknown-freebsd.tar.xz": "14787b285c55ab885d1360897849882eac861f36029cc72ec9819d035998ee9f", - "dist/2022-09-20/rust-std-beta-x86_64-unknown-illumos.tar.gz": "4bfdf27eb12af6c4df132603a111ae26f4d06846af366a38e594e66c092373d5", - "dist/2022-09-20/rust-std-beta-x86_64-unknown-illumos.tar.xz": "66b602046eaa83cf2b69bc75af4dd11dbc5d0c6878537d0af451d17121fdbbe9", - "dist/2022-09-20/rust-std-beta-x86_64-unknown-linux-gnu.tar.gz": "78d12361e71630978928896a63e6cf7e3e866c09de761029b4e8e959850ac025", - "dist/2022-09-20/rust-std-beta-x86_64-unknown-linux-gnu.tar.xz": "d55f9cb4a8c47fc5d0123cedf622b94b33f57a59022129e31f451e1b80f1815e", - "dist/2022-09-20/rust-std-beta-x86_64-unknown-linux-gnux32.tar.gz": "74bbecb0f35ad8a1aae65fb09ef21e38bfbe6d5b4c6b1d741832eb8f40eb4b1f", - "dist/2022-09-20/rust-std-beta-x86_64-unknown-linux-gnux32.tar.xz": "135f19e88ac0fb7ed02072c82a17f0d12abaf40055efd0a6b43bbbbc9c4445cd", - "dist/2022-09-20/rust-std-beta-x86_64-unknown-linux-musl.tar.gz": "0b3abb866d7b82bc5add9fa01d59b9223d2124d69dfd78a13a4dfcc17196f510", - "dist/2022-09-20/rust-std-beta-x86_64-unknown-linux-musl.tar.xz": "ec5989076c015e5b7d1a307ecb19f2ed12df7b5e2836d3b410f3743f9066683d", - "dist/2022-09-20/rust-std-beta-x86_64-unknown-netbsd.tar.gz": "1e236c2ad7aa296aa389751ce64d1cecf86053d23126e1211da71674603e6900", - "dist/2022-09-20/rust-std-beta-x86_64-unknown-netbsd.tar.xz": "de43876b381f7b91f7a8e1d1df5b92a3d60b22a62333f9a645f3e6055e91be3b", - "dist/2022-09-20/rust-std-beta-x86_64-unknown-none.tar.gz": "568e95b842c3d8f9f733fc3b5b59a8c673d200b4d16d5db36ce24ee355e18c1d", - "dist/2022-09-20/rust-std-beta-x86_64-unknown-none.tar.xz": "557727f83c7998b9349bb2c05f5ac5fcb2f0bef28e55f27b165fb7a2d9347396", - "dist/2022-09-20/rust-std-beta-x86_64-unknown-redox.tar.gz": "e3c00ab70a0a69a9567bc525cc283318186521a6d57ccf4a42e2eb640ed50ce6", - "dist/2022-09-20/rust-std-beta-x86_64-unknown-redox.tar.xz": "757f2208edb49f23adf702a34b093551f0e193f6a6cd8c24b2cf4f199f77b2dd", - "dist/2022-09-20/rustc-beta-aarch64-apple-darwin.tar.gz": "a036bf0b4d0c8ab907ef2cb8cf8eacff41f7b82d519a2988a529c3d926539ee8", - "dist/2022-09-20/rustc-beta-aarch64-apple-darwin.tar.xz": "3c49210d4b867cefb4050507104b2672fc70e15f42615ced22469831b34b3267", - "dist/2022-09-20/rustc-beta-aarch64-pc-windows-msvc.tar.gz": "21fefdaa0b70d7f4839751926ce102e19164a373e4d310c0f0b3655f3adbff47", - "dist/2022-09-20/rustc-beta-aarch64-pc-windows-msvc.tar.xz": "7c88775b4efbb2416deb2b0d9ba86d5178d34059b18165b276658973f29d5971", - "dist/2022-09-20/rustc-beta-aarch64-unknown-linux-gnu.tar.gz": "99e30eb1612fd18b42a1b89602f448788ddcbbac2430577fc963a2c0c4708c55", - "dist/2022-09-20/rustc-beta-aarch64-unknown-linux-gnu.tar.xz": "385eaeb8f260a187ef828904e5267551062210543614dbf98d1c1e392853b913", - "dist/2022-09-20/rustc-beta-aarch64-unknown-linux-musl.tar.gz": "8f3b377a74c586cc8f7cc165eac0794bec560e04c885ae552af4e5cf42490a1b", - "dist/2022-09-20/rustc-beta-aarch64-unknown-linux-musl.tar.xz": "2fb2add792a9377cbe86aaedec389d023c35343ef801a97a2392f323e92c386f", - "dist/2022-09-20/rustc-beta-arm-unknown-linux-gnueabi.tar.gz": "658ec925f51b2a5da9ab8cb193c33c05cc294915d8c0c5a2e93f9ade383375df", - "dist/2022-09-20/rustc-beta-arm-unknown-linux-gnueabi.tar.xz": "75d19b64530691739654763b89468a7101457d638da04e25f549078594b67b68", - "dist/2022-09-20/rustc-beta-arm-unknown-linux-gnueabihf.tar.gz": "c167fb9f352fed99d427094a5c2b96da0486f30ccb4188756def7c232083319a", - "dist/2022-09-20/rustc-beta-arm-unknown-linux-gnueabihf.tar.xz": "ec1c19536b6fc3020ccfff727bddc934f89da0592797d49bf7149e96f7175451", - "dist/2022-09-20/rustc-beta-armv7-unknown-linux-gnueabihf.tar.gz": "05c5e668d5b40db5cd18b21192d8f0f1401d2304f715eff08ff49c3c97d740dc", - "dist/2022-09-20/rustc-beta-armv7-unknown-linux-gnueabihf.tar.xz": "ad620c5de3ae9a0b30b3c49dc89bfcd08a061da01f004a724ad5400efd4a7189", - "dist/2022-09-20/rustc-beta-i686-pc-windows-gnu.tar.gz": "2be9e0f5fe27b7085f65d32cb20875392bdcb177c582c58d1df842b316dcb9c5", - "dist/2022-09-20/rustc-beta-i686-pc-windows-gnu.tar.xz": "5841c0f4558d24ccd0c4e6996e399fe3ff13d5d1ffb2bda38bab6d60020fa649", - "dist/2022-09-20/rustc-beta-i686-pc-windows-msvc.tar.gz": "a19c5330c111ad4b19a724b22dc3381eb9f05a85bf9299dd13eefabbf6499504", - "dist/2022-09-20/rustc-beta-i686-pc-windows-msvc.tar.xz": "902811dec71e36989af12c8dc15b79759e5cf4e2250841bad2b9db2eb94195a1", - "dist/2022-09-20/rustc-beta-i686-unknown-linux-gnu.tar.gz": "36474bb89e67bc867cb7a4a5101d00be221d7a8b6a625535a5b2a74f505d5af4", - "dist/2022-09-20/rustc-beta-i686-unknown-linux-gnu.tar.xz": "4329562f89817b02e3eac219ad3051d9de5fab89e0678d91378c02a90fea7d59", - "dist/2022-09-20/rustc-beta-mips-unknown-linux-gnu.tar.gz": "bb7f5c8abc99d07a337eaeb3c1dc3861a4f148c364f58b039886f43abf6a7d01", - "dist/2022-09-20/rustc-beta-mips-unknown-linux-gnu.tar.xz": "0b02a4d1aac7c9d4b38fd760937020975e5de209ad23b7285cceae7992449d47", - "dist/2022-09-20/rustc-beta-mips64-unknown-linux-gnuabi64.tar.gz": "7ceae3da1fb1f865df3315ca450ec3cb4657086dd61c7a47879f98188aa38100", - "dist/2022-09-20/rustc-beta-mips64-unknown-linux-gnuabi64.tar.xz": "5a5273ed85d3012b8067dbc3e93f1af105e4cd80ed8055daade24f43dfb41977", - "dist/2022-09-20/rustc-beta-mips64el-unknown-linux-gnuabi64.tar.gz": "dbd12a141765f29be2a602531db7f9a02bc32617635448f602befc90f1a574c3", - "dist/2022-09-20/rustc-beta-mips64el-unknown-linux-gnuabi64.tar.xz": "e3abf34a09040149f8725ed1fca6e9c412c4cf290f9424541a819f0e2aa363b2", - "dist/2022-09-20/rustc-beta-mipsel-unknown-linux-gnu.tar.gz": "e13b7057525302dc3585a71461aa022ea0c059c0b0069fec44f86549eea94d18", - "dist/2022-09-20/rustc-beta-mipsel-unknown-linux-gnu.tar.xz": "b7bedce1d0ce44b4014e10122201c10443c0e8ced80084a6ebf1add1dbd3236f", - "dist/2022-09-20/rustc-beta-powerpc-unknown-linux-gnu.tar.gz": "fa738ae0d068e85044d2fce10f6a8bebe7b608630b9b7a822b2d7b84c6c59002", - "dist/2022-09-20/rustc-beta-powerpc-unknown-linux-gnu.tar.xz": "b53d773465368d9484cd36063d7ff202c1ca8d18422b4f6727cba54beb88f4bf", - "dist/2022-09-20/rustc-beta-powerpc64-unknown-linux-gnu.tar.gz": "9fad6a7ae30e5ccb4f0779ffdd117f6cb30e6c5f6efd5247c208f9ee3296a27f", - "dist/2022-09-20/rustc-beta-powerpc64-unknown-linux-gnu.tar.xz": "e8a92259aa371d350dc29171467c3e99fc178b636343ca82188c7b602a39ab58", - "dist/2022-09-20/rustc-beta-powerpc64le-unknown-linux-gnu.tar.gz": "5b64e0924a705e267177c8d80970f510487de350dda47cbc9bb758ec1b212a17", - "dist/2022-09-20/rustc-beta-powerpc64le-unknown-linux-gnu.tar.xz": "f6ca5a88f0ea25521e1135100cc7404547ffbbc4422b3c9a06177c94d7871ef4", - "dist/2022-09-20/rustc-beta-riscv64gc-unknown-linux-gnu.tar.gz": "8db27ac2a1b8322f529428ee7278347263a9ff71101d37bfb04056137f63de78", - "dist/2022-09-20/rustc-beta-riscv64gc-unknown-linux-gnu.tar.xz": "2475866ff4afe38755a27ffde9c09302066d0c936a4778883fee9a37c1b59f31", - "dist/2022-09-20/rustc-beta-s390x-unknown-linux-gnu.tar.gz": "4cd81c652fd3e59cbdd42872f2e37bdcc1fa61a550eb8ffed7783e7ad3350577", - "dist/2022-09-20/rustc-beta-s390x-unknown-linux-gnu.tar.xz": "1f8529e51192433d1a281484072f94d910ff81161efef230e6a2be82765f6f26", - "dist/2022-09-20/rustc-beta-x86_64-apple-darwin.tar.gz": "430311100511fe9fe176f01030b78fa8160840bf4d9b4ed798a2a7fe089b4f7c", - "dist/2022-09-20/rustc-beta-x86_64-apple-darwin.tar.xz": "57be1bb1dc7d7d0f0479d11e36d6315a9d19eb0102610b7f1dbd5151fe6ff5c2", - "dist/2022-09-20/rustc-beta-x86_64-pc-windows-gnu.tar.gz": "826f3f6839c4e18e6d58a32de8f067bb57be2fb2c6cdf74f55d55ef76f5c5e21", - "dist/2022-09-20/rustc-beta-x86_64-pc-windows-gnu.tar.xz": "5f8b9704071e6b372a5c67a29bcb9ba5978ffdedd62e057680aaba17dfc91ba1", - "dist/2022-09-20/rustc-beta-x86_64-pc-windows-msvc.tar.gz": "06a29a85bfce9981504f1630c5f3ea86171948080a93d8dadb4a306dd678af80", - "dist/2022-09-20/rustc-beta-x86_64-pc-windows-msvc.tar.xz": "36c2944aa3db18dfa1632c7b52c67e6bad9effb03960af8cbf82fdf32924019b", - "dist/2022-09-20/rustc-beta-x86_64-unknown-freebsd.tar.gz": "c20829efb9888d8097c9f5f472598b06868bf918a9d033d4b6fd031323878492", - "dist/2022-09-20/rustc-beta-x86_64-unknown-freebsd.tar.xz": "515b35360865016b7efe6f973730ce4c66021df0edeed8eb490b69f4bf50006d", - "dist/2022-09-20/rustc-beta-x86_64-unknown-illumos.tar.gz": "e63231ee48425687c97c654faba961a1b12379696459f284b6a4ea7ea52fb2af", - "dist/2022-09-20/rustc-beta-x86_64-unknown-illumos.tar.xz": "fae9048709741bcd21f4dc2ad8119576ba8dbe6b6442e79a443c207a4c52cc47", - "dist/2022-09-20/rustc-beta-x86_64-unknown-linux-gnu.tar.gz": "3785a8837c6fdf230b79992e4a3fd6a8b6faa269461bf908e427ffbd59728adb", - "dist/2022-09-20/rustc-beta-x86_64-unknown-linux-gnu.tar.xz": "a238209d54c2f9fea99a18bf43c4c0ae9bbc9cb10075e63d77af131728d64892", - "dist/2022-09-20/rustc-beta-x86_64-unknown-linux-musl.tar.gz": "520ba16bf1b892f5c3d3fd6c1ba695ff48e0babd4ed5be97615887589e60c204", - "dist/2022-09-20/rustc-beta-x86_64-unknown-linux-musl.tar.xz": "4251abe5dde29e9d2ffd7560e7f8eeb5c1b4ad6078b27896f63fcad5db6dabeb", - "dist/2022-09-20/rustc-beta-x86_64-unknown-netbsd.tar.gz": "ccdde196a376d8f06d3457a1b6d85b4b3692acc9e4bd055fb93dcb217ecd4494", - "dist/2022-09-20/rustc-beta-x86_64-unknown-netbsd.tar.xz": "22f5defadc7b4b4231315b420b6ee102c188a03381580feb7e22b75e16661017", - "dist/2022-09-20/rustfmt-nightly-aarch64-apple-darwin.tar.gz": "8fead022ff05d4112e4cf7e637a459651dc793d9c38f1e823437f6c0c1bf6791", - "dist/2022-09-20/rustfmt-nightly-aarch64-apple-darwin.tar.xz": "5e5f06c2c7a0567bb096b676ecde4bc87cd56c1a60d5e99feb0ac0b679280e1a", - "dist/2022-09-20/rustfmt-nightly-aarch64-pc-windows-msvc.tar.gz": "f365e910e58e962526bc2ffc01b47ea7b99b2be199baeed82e3bb0609147994b", - "dist/2022-09-20/rustfmt-nightly-aarch64-pc-windows-msvc.tar.xz": "3e8a10a285b3c248691e20090d2805d0aabdfc0555a5463bc472899fba085759", - "dist/2022-09-20/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.gz": "4316414f83c30535f0c46efba5fd011755f4afa6cc3440b39e8ec154ae451b69", - "dist/2022-09-20/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.xz": "f3bf1d0198db6efb751fd61d096615d09dec94a2732b028728d74a3513e9bc47", - "dist/2022-09-20/rustfmt-nightly-aarch64-unknown-linux-musl.tar.gz": "d0f4c30d1ed5144ce0a2931290cb730efa5616375ff846692faba0f04b2fed4a", - "dist/2022-09-20/rustfmt-nightly-aarch64-unknown-linux-musl.tar.xz": "3c20a791400f994ea5ae681700e9bd1773b9203821a5458448a038b70fe98794", - "dist/2022-09-20/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.gz": "587eabfb4ab41ec7fc1f344f8d8674feb1787cb402dbc10754c43eb9352233f6", - "dist/2022-09-20/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.xz": "0f5c05ce846c42f4afd9127fa5bc0af070e7a03911ed93630177d6304ec66fe9", - "dist/2022-09-20/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.gz": "dfcab7b0d9b93e1ce639a7a1b9774a41e1e70b67fb91814393531476e7ff6d97", - "dist/2022-09-20/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.xz": "aa66edb8208b73e3dc939ce82afb78b9104022427fc2278a50ca004c54b1fd5e", - "dist/2022-09-20/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.gz": "9d31be70cf5aa67219bf85e303651b928e89f54831a14ad004ef606291206991", - "dist/2022-09-20/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.xz": "8b1249489bbf015af1865ddeb83560fbbc52ca84937e14a2ae928eb4fa854322", - "dist/2022-09-20/rustfmt-nightly-i686-pc-windows-gnu.tar.gz": "4154363e7fc7888088ba020c7454a2e0ea75a64e01532ccc709dba3a16c48d78", - "dist/2022-09-20/rustfmt-nightly-i686-pc-windows-gnu.tar.xz": "48ba35bdfe87b78428fde9b7ff6fbd7682d7f94113b874e8308bd3c5e734154c", - "dist/2022-09-20/rustfmt-nightly-i686-pc-windows-msvc.tar.gz": "bf42294f1c3053b8ff6dcf13219056a5e83fb0680e5e53621c626f825f2f11c6", - "dist/2022-09-20/rustfmt-nightly-i686-pc-windows-msvc.tar.xz": "a08b27ac0b47af60618f07d884320e80665eab550536e19828b5fe139a59499d", - "dist/2022-09-20/rustfmt-nightly-i686-unknown-linux-gnu.tar.gz": "748c42c63c3363d820b132a96bd95cd5203c5f808fb4885710065b9c609ae183", - "dist/2022-09-20/rustfmt-nightly-i686-unknown-linux-gnu.tar.xz": "a2bfe3e12d0ecb5881a749a3d11652d45efcd9ee3647ea7c6b6cbc94071e34e4", - "dist/2022-09-20/rustfmt-nightly-mips-unknown-linux-gnu.tar.gz": "a26f46c9d777ca55db8ef595219aea45c3ff7547ee7cfe07d01b9535dc00e1dd", - "dist/2022-09-20/rustfmt-nightly-mips-unknown-linux-gnu.tar.xz": "d3ba043430fb0177023d4028c90a797d9b469d4c2fb2c539bb52e2dd070723cb", - "dist/2022-09-20/rustfmt-nightly-mips64-unknown-linux-gnuabi64.tar.gz": "1f7b83d238dcd63de150d5fe457b8c04347626a583f049cac7989644787c2432", - "dist/2022-09-20/rustfmt-nightly-mips64-unknown-linux-gnuabi64.tar.xz": "25f682b00e0362c16cb8d9879854d0a9781dd7a1e0f980a0c5064fad3764e8ef", - "dist/2022-09-20/rustfmt-nightly-mips64el-unknown-linux-gnuabi64.tar.gz": "09aaccf92485ad1d69623410f947b264835236d20472f974e348015bb8d2353f", - "dist/2022-09-20/rustfmt-nightly-mips64el-unknown-linux-gnuabi64.tar.xz": "cff865ff4979885f158af7b437ebe67ea2645489a6067abd97eeaa97b57041d8", - "dist/2022-09-20/rustfmt-nightly-mipsel-unknown-linux-gnu.tar.gz": "1ca400758f5e44fa8ed01d8fc6d5622259cde42597fe80dc5e0e1b4129270c77", - "dist/2022-09-20/rustfmt-nightly-mipsel-unknown-linux-gnu.tar.xz": "bd71382043ea4e934640a60660169d8785af453415d88945066bd6b8a42b0099", - "dist/2022-09-20/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.gz": "86feeca2cb543476419583bc1b10dbf8d91afd25ac77a9f04f789aff4f34e3e6", - "dist/2022-09-20/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.xz": "2ce655da2899200f9e4a331a45f005f4faea11cfdf5b74397a68d376dab88bf9", - "dist/2022-09-20/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.gz": "ed7e14c85bb1dd2900991775e812c56a76891a70dbea013bff06f73e7d1beaba", - "dist/2022-09-20/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.xz": "09710d4622fd7a9695d3907be6433c69f2ff590415fab776a05ba74ea5be63a9", - "dist/2022-09-20/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.gz": "02da320a5aac0eaffa83cd49b37d0cbcecab7686cef166f30dd5ed02fa9cb023", - "dist/2022-09-20/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.xz": "1f4465079a5493ec23f433cae4af3f633885b38f6ba7693387ccd355010b955e", - "dist/2022-09-20/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.gz": "a9a3bdba43897b0ab44c54537ce80fc969c378480348af715e293403b55b83be", - "dist/2022-09-20/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.xz": "2d58863182dac199cde47e12d3d92a64b5d068afc7e97e1047ae8b369fbe6df9", - "dist/2022-09-20/rustfmt-nightly-s390x-unknown-linux-gnu.tar.gz": "002667d802f1ff1224318c2090caaa3eefd18dabd1bcd40ad957b0c09acd047a", - "dist/2022-09-20/rustfmt-nightly-s390x-unknown-linux-gnu.tar.xz": "a34aa6451cf5faf985967ec5df78a41b21ae317bba7f8836e09f87571a7f9c16", - "dist/2022-09-20/rustfmt-nightly-x86_64-apple-darwin.tar.gz": "8950773a8433fcc68f16c1e07210c3cf07c9e9240df92b0a90c67ca285d932f8", - "dist/2022-09-20/rustfmt-nightly-x86_64-apple-darwin.tar.xz": "1ce5bb4a0a3490f95037a1512f4b5539c65bfdc5d7f6d42005019345bedb71e8", - "dist/2022-09-20/rustfmt-nightly-x86_64-pc-windows-gnu.tar.gz": "cdbef502285d5eec5788299354f391a2263858d630466cd1cc6d48748aefb1af", - "dist/2022-09-20/rustfmt-nightly-x86_64-pc-windows-gnu.tar.xz": "d5e04d0abda5e8ec429794a58f6404a6b95cc66cb218a52299c3bfaf1ec35485", - "dist/2022-09-20/rustfmt-nightly-x86_64-pc-windows-msvc.tar.gz": "d1ec51e70918e4f50463839eb86528582424d69eecc6af5cd07987621acc713e", - "dist/2022-09-20/rustfmt-nightly-x86_64-pc-windows-msvc.tar.xz": "64a108e60c1ff0e1c004d63e13a8180e5b266382e8434aa94eaff3c654158e51", - "dist/2022-09-20/rustfmt-nightly-x86_64-unknown-freebsd.tar.gz": "d439ce2780177b3123de9a4c6727ea19831a215d19363d12b0bcc3bc19fc5074", - "dist/2022-09-20/rustfmt-nightly-x86_64-unknown-freebsd.tar.xz": "851adf8da102068b4195a78ba587e168b7c4471b5a2e66451385a503362d091d", - "dist/2022-09-20/rustfmt-nightly-x86_64-unknown-illumos.tar.gz": "7bad988696f60f49c0ab1929f8cfe843effaa455ab1d20b002dadd1e10bc4ded", - "dist/2022-09-20/rustfmt-nightly-x86_64-unknown-illumos.tar.xz": "8bee32774f9af0b6f382640592b65d4187401a59bd3bea4a139e2dc43471bc0c", - "dist/2022-09-20/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.gz": "16a3ff01a6bc471da2195c7e8d0a6623b911d956db388b66e39096a7c81ae1d4", - "dist/2022-09-20/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.xz": "721115ea0ec7902f74d203eaadc71c79c4489caf9a23b0a81c513fddce5fb9b2", - "dist/2022-09-20/rustfmt-nightly-x86_64-unknown-linux-musl.tar.gz": "9042aba5f18ef575cff2e106c34b707d8fe013f4140e4a066ce80f2103563809", - "dist/2022-09-20/rustfmt-nightly-x86_64-unknown-linux-musl.tar.xz": "9106ee07a9f173ae845ae2b5ce30798e15deffd46149430ec5aacceaed7848b8", - "dist/2022-09-20/rustfmt-nightly-x86_64-unknown-netbsd.tar.gz": "9acb78526c40efdfa0087373d802b30b75238e4c46e0bb18262e16416be49b4b", - "dist/2022-09-20/rustfmt-nightly-x86_64-unknown-netbsd.tar.xz": "c7a73c5b9034417998800dabb08ba71e12713a299457381a4437ea454cead1bd" + "dist/2022-11-01/cargo-beta-aarch64-apple-darwin.tar.gz": "ebc0b11a2af0385bf11a5183dc159d890161be45d231acc34c6326aa25b84b95", + "dist/2022-11-01/cargo-beta-aarch64-apple-darwin.tar.xz": "a0e44bf77337518e2200c34cb297a91dd4db51f0d331ca4cc496989da61676b3", + "dist/2022-11-01/cargo-beta-aarch64-pc-windows-msvc.tar.gz": "a4beae1c53df4d35fe991ebc713e37246d4d89e5543ec740274605a7124806b3", + "dist/2022-11-01/cargo-beta-aarch64-pc-windows-msvc.tar.xz": "5f8ec5c8b012d7e6bc28ca3d700c1c7c742f6532adb044539cee3b2280c1056c", + "dist/2022-11-01/cargo-beta-aarch64-unknown-linux-gnu.tar.gz": "54d8fc5ce70b1f06164e17e34d33abde7260c6b1f3356d98d77271ec89766fb1", + "dist/2022-11-01/cargo-beta-aarch64-unknown-linux-gnu.tar.xz": "f2debb6ae264fefc49380997759bb0b5022ac1c65ced9bc17bc146671be37116", + "dist/2022-11-01/cargo-beta-aarch64-unknown-linux-musl.tar.gz": "7a8e10be17c8cd624fb3ae2bb7eaab3c493b637c2c1c1100b5333982d1dfd962", + "dist/2022-11-01/cargo-beta-aarch64-unknown-linux-musl.tar.xz": "553decfc64b56d9967ae067bc942ef7117c81d6976b5fa4cf8e5171397836af7", + "dist/2022-11-01/cargo-beta-arm-unknown-linux-gnueabi.tar.gz": "64bdb603cdc05b983393d707e9e6e6cd1c71dd8213d08b3d0d1cdf168ceb165b", + "dist/2022-11-01/cargo-beta-arm-unknown-linux-gnueabi.tar.xz": "0afe4ca54c65668257dcad5941c678498ab917bbf82a808f39c093719a53f2ed", + "dist/2022-11-01/cargo-beta-arm-unknown-linux-gnueabihf.tar.gz": "c7fe3bacc9c4acb9b42677281655904b5ed5aec27042b9a8cf9743b737b6b657", + "dist/2022-11-01/cargo-beta-arm-unknown-linux-gnueabihf.tar.xz": "57f985ccaa2452778c90733e2586a991969dc15697bdbc9547da8a62c871b674", + "dist/2022-11-01/cargo-beta-armv7-unknown-linux-gnueabihf.tar.gz": "873b2a0c2990eef29d689984293394e6972b4659bd6e4c31fb9bc9c8f1c679f9", + "dist/2022-11-01/cargo-beta-armv7-unknown-linux-gnueabihf.tar.xz": "f8a9e74159594d57ce8dda1f7ce7ee4e1d494b9135a0f32b3afc89a637cad8ae", + "dist/2022-11-01/cargo-beta-i686-pc-windows-gnu.tar.gz": "9570141b118c2339237aac12c1e6d71c138ccef784db2effdfd9d02fb12d0d0d", + "dist/2022-11-01/cargo-beta-i686-pc-windows-gnu.tar.xz": "183b63cded6c4cc26feaa14be036a619289b155a6718f4964f94c38a9208742b", + "dist/2022-11-01/cargo-beta-i686-pc-windows-msvc.tar.gz": "9382bf364c5fc9400fb22b046c0a951001961efac221f5cd0f9bf45b1005d36e", + "dist/2022-11-01/cargo-beta-i686-pc-windows-msvc.tar.xz": "aae0a58b9711365ce1d76966af7387f310b496859a9e02ddbff8e23da93226c7", + "dist/2022-11-01/cargo-beta-i686-unknown-linux-gnu.tar.gz": "507727f9b5a920ea28e7104c9aae681c50fa8aaea446a3e10b991a9408adaefc", + "dist/2022-11-01/cargo-beta-i686-unknown-linux-gnu.tar.xz": "4ebfaf11ffc346eec9f05b2d93123483b784b83a322cca6f5fd406066ecf0fcc", + "dist/2022-11-01/cargo-beta-mips-unknown-linux-gnu.tar.gz": "6407889854bee2e45a00585abb4fc8b387103e33e3e67244dba4e140abe46480", + "dist/2022-11-01/cargo-beta-mips-unknown-linux-gnu.tar.xz": "1aeba894f0ca756dd9c3d9b99c7c94bf1f49d5d87ea919249fd0fcf195eb9c52", + "dist/2022-11-01/cargo-beta-mips64-unknown-linux-gnuabi64.tar.gz": "292a95a8de3387832173d9adde633b3d34a019879f97bf196cb41556c3909337", + "dist/2022-11-01/cargo-beta-mips64-unknown-linux-gnuabi64.tar.xz": "872819f00ab0a848401d7dfbb18cf139f85b3d8e48eee0a034cf7f0b970bd865", + "dist/2022-11-01/cargo-beta-mips64el-unknown-linux-gnuabi64.tar.gz": "15eb49c334688e48e83f2565c620b3f1af29775599406efa1814c78ee80673cc", + "dist/2022-11-01/cargo-beta-mips64el-unknown-linux-gnuabi64.tar.xz": "e74f6884e71109d36d03f7147b7e506f374ba291aadbe4246f6c429bd6fffd1f", + "dist/2022-11-01/cargo-beta-mipsel-unknown-linux-gnu.tar.gz": "7f3cf8b35465e4df5fc18cc7cb4f4db6e1b240a39f7583126d7f8ad6d18e8bf0", + "dist/2022-11-01/cargo-beta-mipsel-unknown-linux-gnu.tar.xz": "c59f2893999dd88a55c0a5bdb4436640ae9c18f943baf48f63eff6069f7a3e8d", + "dist/2022-11-01/cargo-beta-powerpc-unknown-linux-gnu.tar.gz": "566c315b6206a63bf33acf178547bb757a8803e3cfc71f1f63ee033eb6a17138", + "dist/2022-11-01/cargo-beta-powerpc-unknown-linux-gnu.tar.xz": "814a8e8f8f5caf5bb4018e54ffc2c1bd9d23df94dcaffbc04881b91bb3c8aefe", + "dist/2022-11-01/cargo-beta-powerpc64-unknown-linux-gnu.tar.gz": "5db8a63532be5fb9511238d7976075496aba6c732302dcc27bed9ae61188f917", + "dist/2022-11-01/cargo-beta-powerpc64-unknown-linux-gnu.tar.xz": "6b95c8cc4dda3847f53fb62ea711ca99c1b1b1639249b8b01d54a9ecbc4421ec", + "dist/2022-11-01/cargo-beta-powerpc64le-unknown-linux-gnu.tar.gz": "2ad497be28760f7e4ec6dfa6421a6c10ab049e0dbf45ecb3a2dbde5db7a959de", + "dist/2022-11-01/cargo-beta-powerpc64le-unknown-linux-gnu.tar.xz": "6e9b982857c64518c10392779528e7065191262a95e091ee289c8668b6cbfc4c", + "dist/2022-11-01/cargo-beta-riscv64gc-unknown-linux-gnu.tar.gz": "88a0751ef36816f9e26e9f6d72809687b1f6821b32a3a17c58feaa32f882aecf", + "dist/2022-11-01/cargo-beta-riscv64gc-unknown-linux-gnu.tar.xz": "bd6f626002a0c5a3af975419a1258a77c9db91e0db5d4acccbc7dbf25ffd17c8", + "dist/2022-11-01/cargo-beta-s390x-unknown-linux-gnu.tar.gz": "69bad5758f27f53d3e48abcd5aa70b16eb29d5445233c65ab50a8ad0a1629077", + "dist/2022-11-01/cargo-beta-s390x-unknown-linux-gnu.tar.xz": "06212f4cb605fb79d811060d3096bc4b43cf00e1a4fe4a375154b56ff60c92f5", + "dist/2022-11-01/cargo-beta-x86_64-apple-darwin.tar.gz": "741f3490b5562afd57cdda846ab322c69e20940bcc11f3ca5690d662d5de280b", + "dist/2022-11-01/cargo-beta-x86_64-apple-darwin.tar.xz": "2d698df7c00b7c227ca388830732a8787b2a85b328b554c0f8c417813d97ef46", + "dist/2022-11-01/cargo-beta-x86_64-pc-windows-gnu.tar.gz": "9c22b476f25c3f0946cb834da3904516248137cf22c5eed30432401ff061a4cf", + "dist/2022-11-01/cargo-beta-x86_64-pc-windows-gnu.tar.xz": "1604c5d60379227d26d819bd2f7a57c79a9e000a6077ec06e95b418bb0351180", + "dist/2022-11-01/cargo-beta-x86_64-pc-windows-msvc.tar.gz": "673d8941202c2113a431fcef396e604d7ea79000c97a64ef6e93b26956f75fe7", + "dist/2022-11-01/cargo-beta-x86_64-pc-windows-msvc.tar.xz": "3d613d04b48a2eb8644e2bfbb07a88cefe02c7b5cc7bf061b8ef307980230d47", + "dist/2022-11-01/cargo-beta-x86_64-unknown-freebsd.tar.gz": "e0ce6fa69af565e3b79f7059a4de88e39955d7ea6866d56c2b0946b47929192f", + "dist/2022-11-01/cargo-beta-x86_64-unknown-freebsd.tar.xz": "de602b7802b1448a861df05c41430dcde4f07358a05711784a1ca37836525b74", + "dist/2022-11-01/cargo-beta-x86_64-unknown-illumos.tar.gz": "c4eacf4821c126b321a67e0233d2f84571b3dcf25686165cad00d9645787f03d", + "dist/2022-11-01/cargo-beta-x86_64-unknown-illumos.tar.xz": "01ec5ab637010498b784ea2fe6aacea626fc341792eaa5a50756f9b483a765e5", + "dist/2022-11-01/cargo-beta-x86_64-unknown-linux-gnu.tar.gz": "2e6efadbcf138ab72750c1375bfeaf2d5102559aa9b745294b9973821e193703", + "dist/2022-11-01/cargo-beta-x86_64-unknown-linux-gnu.tar.xz": "e089b1b4248ad8e05ba54cfb278101a74aa34154bd2d44dd50119026bf436d1d", + "dist/2022-11-01/cargo-beta-x86_64-unknown-linux-musl.tar.gz": "ca079fce260accce11c1fb27e550421cd0900027e29b18e24e54a298d78031c3", + "dist/2022-11-01/cargo-beta-x86_64-unknown-linux-musl.tar.xz": "ff33e9fd6f06e02277f580f13d82f753987f4dad7d7926405b63dcb362eec498", + "dist/2022-11-01/cargo-beta-x86_64-unknown-netbsd.tar.gz": "46101fc5f53595ae53f3ceb755cc72c078471479a337b5319c85e629e5df3b28", + "dist/2022-11-01/cargo-beta-x86_64-unknown-netbsd.tar.xz": "b063425ccc69284e8788211bbde5a7843bd16a3b9c779fab68a11d22ebdf319b", + "dist/2022-11-01/rust-std-beta-aarch64-apple-darwin.tar.gz": "77bb5db904089e087032c24fa2e011536e13d3982299285a7515beb97f445078", + "dist/2022-11-01/rust-std-beta-aarch64-apple-darwin.tar.xz": "63aae4b9f10f15fb48b2ac20aa7f112a685d49bdf94d8997d036472e928fcbde", + "dist/2022-11-01/rust-std-beta-aarch64-apple-ios-sim.tar.gz": "8f63b6be668e6a25411582db9145c9de8192d58acb42c490b0de89489a3e36c6", + "dist/2022-11-01/rust-std-beta-aarch64-apple-ios-sim.tar.xz": "d862bdeaf2c78b15babaf74cf1c6feaa5c4871a90095f3d4239d81f44217cff4", + "dist/2022-11-01/rust-std-beta-aarch64-apple-ios.tar.gz": "e5b1e9420d387a1442c77bed10efebd7b0268713820a728a067bb4ead6088041", + "dist/2022-11-01/rust-std-beta-aarch64-apple-ios.tar.xz": "569c667e422ca7ac373d59b6e13c299cdb7f334164c84e6f0c8d0f076352fbf0", + "dist/2022-11-01/rust-std-beta-aarch64-fuchsia.tar.gz": "3f945c43c09704b3df6af66a2132da12243b13752094383965d6a8a83c6edb0a", + "dist/2022-11-01/rust-std-beta-aarch64-fuchsia.tar.xz": "3662f02892ab184be99f93a9d0f99e030a73cc61447934b74fcba84e05b022b1", + "dist/2022-11-01/rust-std-beta-aarch64-linux-android.tar.gz": "ab04a0228074e974d70a15e594d57479fe22ed37c8acfa5104201dbbe57747a7", + "dist/2022-11-01/rust-std-beta-aarch64-linux-android.tar.xz": "fb96925878a24dc9e90d356e96cf4fd1fc9152c39f8914f9a9bb676d78069cba", + "dist/2022-11-01/rust-std-beta-aarch64-pc-windows-msvc.tar.gz": "45e824f75ac530ee9eaf0b0a01cacd5b8dd64ddf5203c032c49fd2bc4fabb245", + "dist/2022-11-01/rust-std-beta-aarch64-pc-windows-msvc.tar.xz": "a6488faf4c87cabb4467f4cbe7348d553045c2f10f450bc6e000fcf18ca9b073", + "dist/2022-11-01/rust-std-beta-aarch64-unknown-linux-gnu.tar.gz": "e2a66e04b24aad8a8898d6c0270d8dcff63205213cea3b893807ef186e8c0936", + "dist/2022-11-01/rust-std-beta-aarch64-unknown-linux-gnu.tar.xz": "a4244ac1600726b5abe6b5f9a171fc2e4cc57bbe7cecdeaf23b69e906f05e303", + "dist/2022-11-01/rust-std-beta-aarch64-unknown-linux-musl.tar.gz": "9e3e0f675ca50b7a2a1afeacdaf5d7f2f4ec1536f596ff99aadacfcb59fd42f5", + "dist/2022-11-01/rust-std-beta-aarch64-unknown-linux-musl.tar.xz": "7e7a8fb4fe0283b71deb79c5ccb1ae61b2099392b3c8e09d03d4a68fbab7a184", + "dist/2022-11-01/rust-std-beta-aarch64-unknown-none-softfloat.tar.gz": "7543df1d71d805b079d19ccd785f777918b3f11b131bca05d079cb5d3952a38b", + "dist/2022-11-01/rust-std-beta-aarch64-unknown-none-softfloat.tar.xz": "eb082e894047cd77ac3fcc9c03eaaef77e6bafbd075cb0d62ba3a3ba277f5d64", + "dist/2022-11-01/rust-std-beta-aarch64-unknown-none.tar.gz": "144fc6973b06ffb12b5ad0bbfc9fcdcb2a0732de50bb140d62d6af3d6b462908", + "dist/2022-11-01/rust-std-beta-aarch64-unknown-none.tar.xz": "8ee2ba2d4eca35a426fb089e0f0b50b2ac3ad1ab036c5f8f4786e2953405092f", + "dist/2022-11-01/rust-std-beta-arm-linux-androideabi.tar.gz": "4a46d6591c1983d0853f7596f7b76e7c82b6b0cbfd97802b565a17aece0d13be", + "dist/2022-11-01/rust-std-beta-arm-linux-androideabi.tar.xz": "3888fe036b5fa9a5dfa009462a002a05c70e56eb70db3a0c872fab1432e9c9ed", + "dist/2022-11-01/rust-std-beta-arm-unknown-linux-gnueabi.tar.gz": "529d668389506443f87bd93e98dc72d12be9a4ab41675dc6a1c7373e934ca017", + "dist/2022-11-01/rust-std-beta-arm-unknown-linux-gnueabi.tar.xz": "dffa1a94f4166435d6fe2a76a4d35deb8c128cc93146f181979416816e77e29a", + "dist/2022-11-01/rust-std-beta-arm-unknown-linux-gnueabihf.tar.gz": "2913bc06d6b49c52804a8dc18d1d3cb1b564e0272cba93f8594747731d360f9c", + "dist/2022-11-01/rust-std-beta-arm-unknown-linux-gnueabihf.tar.xz": "c12bb97fcbeeb0a9a71b2575b2d5113948c515616f720dae3891e2aa886d03a7", + "dist/2022-11-01/rust-std-beta-arm-unknown-linux-musleabi.tar.gz": "a844ad8a80fa07b9196dc040d5171749daf94443c57348bca04e69b8dad37cba", + "dist/2022-11-01/rust-std-beta-arm-unknown-linux-musleabi.tar.xz": "c261662fa988748ed03722d6034228c893e02a0e473f906bba61c1f43be7cd79", + "dist/2022-11-01/rust-std-beta-arm-unknown-linux-musleabihf.tar.gz": "1ae5967f4fb80047711519dafea21fed8d6afd308566033e468c11587073d216", + "dist/2022-11-01/rust-std-beta-arm-unknown-linux-musleabihf.tar.xz": "150393cde94d8349eb488a161572589c53fed456c8401e5b1a59d1dd87003f7c", + "dist/2022-11-01/rust-std-beta-armebv7r-none-eabi.tar.gz": "b1777a389e4db0ccd80ece774865bc99731c4b483be80c909f1b5a2a185dc5a1", + "dist/2022-11-01/rust-std-beta-armebv7r-none-eabi.tar.xz": "877a00491650bac92e93760c2457b644d2b5ee28d410c1e29fc4b40c05da493a", + "dist/2022-11-01/rust-std-beta-armebv7r-none-eabihf.tar.gz": "3dfbf001db319a41874e2c0de2f55407285d88156fa0563cfe3c3bb1939998fb", + "dist/2022-11-01/rust-std-beta-armebv7r-none-eabihf.tar.xz": "b2d6a543cdf64a5c147001ea30d07bd13b98e2918a343bff08bb57eed1f81462", + "dist/2022-11-01/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.gz": "cd8f0803ef86052d09606601b09dde05d1997a93fad7a22604fda1176157040e", + "dist/2022-11-01/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.xz": "748cef6595fcd30da6735c29476639ac80cba94eb627d6654665d656da2979ec", + "dist/2022-11-01/rust-std-beta-armv5te-unknown-linux-musleabi.tar.gz": "1dcae3588a3e552778ff1079a92750bee15835f08f8b9ff1123e4e6c5a73c087", + "dist/2022-11-01/rust-std-beta-armv5te-unknown-linux-musleabi.tar.xz": "3711105029d28fd91f413f488b7041ea42c70e5a244f992e9259b4e9d52abed1", + "dist/2022-11-01/rust-std-beta-armv7-linux-androideabi.tar.gz": "a2af3f6d3681e1c545d0c21bf04fbfe3de1cdb2273fadcbbb4408f5590054d11", + "dist/2022-11-01/rust-std-beta-armv7-linux-androideabi.tar.xz": "23e658070e1cbe8011d48678f57bedbbde819cd64f43509858af563a7073a3fd", + "dist/2022-11-01/rust-std-beta-armv7-unknown-linux-gnueabi.tar.gz": "ff7b429d5a6d33f0e467b333225f7c42de279ccf3e91f3ef7c5463dc06939579", + "dist/2022-11-01/rust-std-beta-armv7-unknown-linux-gnueabi.tar.xz": "8d41b293656c5cf93f46754499e5723a89dd997d3723bfbe56f953a7d864c435", + "dist/2022-11-01/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.gz": "6dd89ed0f20a0ea4a279dd4f810c7908c3e8a377da8a2983f8890efeea169177", + "dist/2022-11-01/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.xz": "504fb533fca6c46ad98c728781ab31170d65e5b35cbc9199aab97b1146a24702", + "dist/2022-11-01/rust-std-beta-armv7-unknown-linux-musleabi.tar.gz": "f9a731fd3ea961f0c5eff24e6290aed19d79d5444bf562670abc0cd46ee309fe", + "dist/2022-11-01/rust-std-beta-armv7-unknown-linux-musleabi.tar.xz": "825acb16e4bbba0c9b535e635b972ec581fe6ef115c5a41bace9b85c704eccad", + "dist/2022-11-01/rust-std-beta-armv7-unknown-linux-musleabihf.tar.gz": "41da8404f0e3cef386f6efef9b27fde27de77de71140dceeaddd8e15260ce45d", + "dist/2022-11-01/rust-std-beta-armv7-unknown-linux-musleabihf.tar.xz": "c8f81fa9cfb40ce92f2c95ef8b57e8a62d819628111e1dfe0c6760fb48802be3", + "dist/2022-11-01/rust-std-beta-armv7a-none-eabi.tar.gz": "fa8c3168dff5c167c6ed25f9c605941ab51e73e70c0dd162a5fd17287c5fd5a5", + "dist/2022-11-01/rust-std-beta-armv7a-none-eabi.tar.xz": "36a5ff7865f8a16b867ab3fff4ac32f0c62c260a5c27385098e67b75b21790fb", + "dist/2022-11-01/rust-std-beta-armv7r-none-eabi.tar.gz": "0417cef6468fd66bf626729e7c0089b47b149cfc43e8e0d4281f76f73ed17edc", + "dist/2022-11-01/rust-std-beta-armv7r-none-eabi.tar.xz": "1de6cb38a68ef336e1edf2c1c51d999482898df99e2bc078cafe6ac5380bf3f2", + "dist/2022-11-01/rust-std-beta-armv7r-none-eabihf.tar.gz": "91003d4648fb01306d6e0a0214e089d444a57c5ff09138040f07cc81e89af639", + "dist/2022-11-01/rust-std-beta-armv7r-none-eabihf.tar.xz": "884306ac77518ece0cb2f22d898e3d2aa50698bd4181ca23a1dada6d82778682", + "dist/2022-11-01/rust-std-beta-asmjs-unknown-emscripten.tar.gz": "f17ca8f54eca5d73006659fd08142d537eff23731b6e5a35bd67efafe0dc8cb1", + "dist/2022-11-01/rust-std-beta-asmjs-unknown-emscripten.tar.xz": "b04a17d33d7b9b1caae666dfa5ee9a98e5dc079773b6345f6c49733731e14bfe", + "dist/2022-11-01/rust-std-beta-i586-pc-windows-msvc.tar.gz": "55e61aa74bdb50df54394a0f62b9edc88682c37b51fe9d8d5c05c0619eacd1e3", + "dist/2022-11-01/rust-std-beta-i586-pc-windows-msvc.tar.xz": "ec3d887742289ef9c171ae56ca20c3e9cf1972cc3e6c511611404070c55dac8a", + "dist/2022-11-01/rust-std-beta-i586-unknown-linux-gnu.tar.gz": "a36444f0ba0e7e03d06fbf65d830cb7067c675ed061e8f6efd6ed445d5955e88", + "dist/2022-11-01/rust-std-beta-i586-unknown-linux-gnu.tar.xz": "dfc07297ee8cb63f76d2019ae822352e6b42e5cccd225eaa5597a63ecff3624f", + "dist/2022-11-01/rust-std-beta-i586-unknown-linux-musl.tar.gz": "e8de9f830cf277be584b54d86d6621a249fb2987fdf32d5f16cde9b492722d45", + "dist/2022-11-01/rust-std-beta-i586-unknown-linux-musl.tar.xz": "f9d8bd74788e2209ecb8d0cc49d94b4e2752c9239f89bcdff3e8fae315d1d923", + "dist/2022-11-01/rust-std-beta-i686-linux-android.tar.gz": "b15636654925fdba1e9ec1704573e4af1fc5f1158a0657b245901e22c06cd378", + "dist/2022-11-01/rust-std-beta-i686-linux-android.tar.xz": "9abbfcaa40d86e8a4cf49f2a58b1c7b2f422b6890303cb43feb83cfb8f650a42", + "dist/2022-11-01/rust-std-beta-i686-pc-windows-gnu.tar.gz": "30953eb457a397966221dad058ff7ebd99ca4497f184016b5a61db0f122bdee9", + "dist/2022-11-01/rust-std-beta-i686-pc-windows-gnu.tar.xz": "f9d6d266eb3bb46c058615786483d817138aa29efc3c62c3cd9c87e572956b12", + "dist/2022-11-01/rust-std-beta-i686-pc-windows-msvc.tar.gz": "b55202c349a4e9a493a2de7a3d48788befce32274998d3dfc1d1b6f4a96ba9e3", + "dist/2022-11-01/rust-std-beta-i686-pc-windows-msvc.tar.xz": "6dd8d42e5712d699704e85bb90cd42e0142a4fab7cf7f80132cb0902cc415ccb", + "dist/2022-11-01/rust-std-beta-i686-unknown-freebsd.tar.gz": "d2a7c9e7f1dba3a317692a46f8efec8d7ba1e9e943c88d3f342a820c34829aa0", + "dist/2022-11-01/rust-std-beta-i686-unknown-freebsd.tar.xz": "ecf6abb631dd6887b5630d1ea0b8778fc1539405e6c00d7585c8afa2230ef9ec", + "dist/2022-11-01/rust-std-beta-i686-unknown-linux-gnu.tar.gz": "93c5912258a49a003a12ca01101f5935d5894f9a133301a47047cca934a7439e", + "dist/2022-11-01/rust-std-beta-i686-unknown-linux-gnu.tar.xz": "f8a6e67723cb968e874827a6148a5e25d3d45c56577faee627010347d0f03d92", + "dist/2022-11-01/rust-std-beta-i686-unknown-linux-musl.tar.gz": "8838592167a8d68f463dc18e55d5d2d55c474426e8a4ec0f28fd2cd4230cf638", + "dist/2022-11-01/rust-std-beta-i686-unknown-linux-musl.tar.xz": "c8330a06862a7f375b57774b382a54a1280c33ddc1b94d5d5ec45eb6ff0de8cb", + "dist/2022-11-01/rust-std-beta-mips-unknown-linux-gnu.tar.gz": "4b50cc174eb1da9dc831de828e6ee2fc8a81abf8e6dd52b041e4ab00eaff73ac", + "dist/2022-11-01/rust-std-beta-mips-unknown-linux-gnu.tar.xz": "4820db058569be7350a81396fdedf9a28233b8061c9bcf607cf2d1058cbf437a", + "dist/2022-11-01/rust-std-beta-mips-unknown-linux-musl.tar.gz": "dfbc460e8322114bde5614b0b45e90066805adbaca999ccdc4f2aae456fc3f1f", + "dist/2022-11-01/rust-std-beta-mips-unknown-linux-musl.tar.xz": "d98c19268b0c84f44f1224f432847a93eb809a85ca48fbe2e4b68fb436bc36aa", + "dist/2022-11-01/rust-std-beta-mips64-unknown-linux-gnuabi64.tar.gz": "8617edc6d62591d50dbadc4a7bc41b31b66bee6fee830af46636c5206027217f", + "dist/2022-11-01/rust-std-beta-mips64-unknown-linux-gnuabi64.tar.xz": "98a6132c8dd7558eb5f44007fa681a3a91b2dfd98d1f68e59f0a4660dc37b500", + "dist/2022-11-01/rust-std-beta-mips64-unknown-linux-muslabi64.tar.gz": "81f794c54d7a8c680c52a8fc1a0e479526744205d51266007fc3c542496957ba", + "dist/2022-11-01/rust-std-beta-mips64-unknown-linux-muslabi64.tar.xz": "dedc5b1a76f8454d1b3d7fda0a05398e5a9ae4cf16ddc4b44477799217a1fb75", + "dist/2022-11-01/rust-std-beta-mips64el-unknown-linux-gnuabi64.tar.gz": "6d9b3d469ae92e38144d9578de8cf0c891e4bf3e667e4e465eb6f0d498140c3c", + "dist/2022-11-01/rust-std-beta-mips64el-unknown-linux-gnuabi64.tar.xz": "f9deb84c24bd0f21ed02d763d3ad8dd92c009de4ceb2b78ec06d90d66609c5f6", + "dist/2022-11-01/rust-std-beta-mips64el-unknown-linux-muslabi64.tar.gz": "073882815493668dd484b8f107efc047f6e07d8c563703d0e7f73ef33dae0efc", + "dist/2022-11-01/rust-std-beta-mips64el-unknown-linux-muslabi64.tar.xz": "d1ab3758d1b08937a3f98737ff9fad20377e5bc43d7ab3a9359b4131ea11dcbc", + "dist/2022-11-01/rust-std-beta-mipsel-unknown-linux-gnu.tar.gz": "bc82f3d23dfb7b331558180f474c334ca3798322e19cc64657cbe894d0682901", + "dist/2022-11-01/rust-std-beta-mipsel-unknown-linux-gnu.tar.xz": "fcc12f82ea0c02e8434420165f1ee072bf4587a82ff5ecf34d19f754ffc091ef", + "dist/2022-11-01/rust-std-beta-mipsel-unknown-linux-musl.tar.gz": "1c4507c7824c02b1af2857c88ff1624e9ead3f38c1456aa031586b43223e9490", + "dist/2022-11-01/rust-std-beta-mipsel-unknown-linux-musl.tar.xz": "932598fbcc35ee4958be4778450f5b809ce9eabb2aa3d7573fd79744ed4d18ad", + "dist/2022-11-01/rust-std-beta-nvptx64-nvidia-cuda.tar.gz": "c17f11707c1edef2353fba7e3f4800cecb8a273233825817b6d07ed78d6acd50", + "dist/2022-11-01/rust-std-beta-nvptx64-nvidia-cuda.tar.xz": "7e90a819b8887f0b1a3ab02fb9a56a9b9eb752408a7bb934c99c7f6ddda48a71", + "dist/2022-11-01/rust-std-beta-powerpc-unknown-linux-gnu.tar.gz": "c437a6fc7cd7869df7cdbb52c51ae1e602ed1206517c38689deb73da6d7b4877", + "dist/2022-11-01/rust-std-beta-powerpc-unknown-linux-gnu.tar.xz": "76c1fc55b16a809ab1c8dfce894899f40d24b20dc670d318a7679953beb6c3a1", + "dist/2022-11-01/rust-std-beta-powerpc64-unknown-linux-gnu.tar.gz": "d62396390fb85d5543a80ffbeaf7c32b5297a513dce14790124c35835813032b", + "dist/2022-11-01/rust-std-beta-powerpc64-unknown-linux-gnu.tar.xz": "4595485492c650aa53bb9deaeb425ea956f2052c5b5503bb477778f7bcaf6ac6", + "dist/2022-11-01/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.gz": "ee841bb8fbb0075a0bf51db2007bee2962830a89649c00fd15c67b31fd9226a3", + "dist/2022-11-01/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.xz": "cac036fafa93f2860a5a2622394e12938c35e629ff81d7cc5930d99c980f9321", + "dist/2022-11-01/rust-std-beta-riscv32i-unknown-none-elf.tar.gz": "025d70e57d608b81d61799c84ccce9bca3603736c4d3e006fc662c3a7b39e8db", + "dist/2022-11-01/rust-std-beta-riscv32i-unknown-none-elf.tar.xz": "9450b1b1f95e188bcb9050085d612c8bef36e819881255fc20d70da1f45fa61e", + "dist/2022-11-01/rust-std-beta-riscv32imac-unknown-none-elf.tar.gz": "ff707c6d209f9d8e421fc530a11d41a46daaebdb4aebd5cfbaab761b2cf192ff", + "dist/2022-11-01/rust-std-beta-riscv32imac-unknown-none-elf.tar.xz": "8eb48a94c58440e2afc8ef7bbdbc725f403fe38724c0afde4e7c29a1ba2c7591", + "dist/2022-11-01/rust-std-beta-riscv32imc-unknown-none-elf.tar.gz": "e921a841b7a9e02e28182e91c921746042330d90f0478fc7e01230cb1b881c1c", + "dist/2022-11-01/rust-std-beta-riscv32imc-unknown-none-elf.tar.xz": "122135e161a4cc7dd857e3cb35b64ff7db450dcc07cbb990c8aa83e06bb4b346", + "dist/2022-11-01/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.gz": "084824d6daeca6a0662ef1e11df84c651138d8d4e7d5c8ef66c5811354b16211", + "dist/2022-11-01/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.xz": "09e3df606e10a0a59a67bf7b49825a04c23062e6050cebed674e0bdb2c396fcc", + "dist/2022-11-01/rust-std-beta-riscv64gc-unknown-none-elf.tar.gz": "f9929f62ffec9c6b3342da8dd21b1c14526e033174a4f86015182acdbb93a985", + "dist/2022-11-01/rust-std-beta-riscv64gc-unknown-none-elf.tar.xz": "932450fc6b5e8fa4813886baa389b53c6ff1c5b1e71f7370017b9658b04fd13c", + "dist/2022-11-01/rust-std-beta-riscv64imac-unknown-none-elf.tar.gz": "e7776d188a04779e7f6a7257bf367d8671e7d5d804d74db426592f683cabf562", + "dist/2022-11-01/rust-std-beta-riscv64imac-unknown-none-elf.tar.xz": "bbc765adc116c6a1bcbf659853b7169d95b240ffc15227cbb1d60b46d63e120a", + "dist/2022-11-01/rust-std-beta-s390x-unknown-linux-gnu.tar.gz": "a0ff6e9ea827d7f93563aaec213eacd00efe4be9c921b448405b2af8bbf0066e", + "dist/2022-11-01/rust-std-beta-s390x-unknown-linux-gnu.tar.xz": "7603744cbbbbdec5b2a322aabe68751e848ac7379c710866c59dcc22e4b873bd", + "dist/2022-11-01/rust-std-beta-sparc64-unknown-linux-gnu.tar.gz": "1f67446eb09505e87a5218b8504dfc678d0a712a5add763362f3c74306010bea", + "dist/2022-11-01/rust-std-beta-sparc64-unknown-linux-gnu.tar.xz": "1baca6f0e7f18a8eb9efcf35bca4320a493d51f08e62bf96a31036e2f2c433fc", + "dist/2022-11-01/rust-std-beta-sparcv9-sun-solaris.tar.gz": "455e52fa3f232c2239112030483c0a657e7f69754d8d36ab72423c76c056fb68", + "dist/2022-11-01/rust-std-beta-sparcv9-sun-solaris.tar.xz": "913801ca45eb1d70c9ddfcdd66aa21edaafccc85acf9864e88991bf8a5a7cf25", + "dist/2022-11-01/rust-std-beta-thumbv6m-none-eabi.tar.gz": "14e4f69fbf710f16275ccb582a90eee1399ea1226945c7c96f75335df9118966", + "dist/2022-11-01/rust-std-beta-thumbv6m-none-eabi.tar.xz": "40549d9d9c923a73381b8e45628cfa1896d0e78caabf2aa921c767e0bc979136", + "dist/2022-11-01/rust-std-beta-thumbv7em-none-eabi.tar.gz": "d40bd56883abc142155188674580c4e29100fd7303fccc70b0c55b964721a156", + "dist/2022-11-01/rust-std-beta-thumbv7em-none-eabi.tar.xz": "874c97a01d06e1516a89797d7a6effeabf34afb4933956aa34e907a65ea78690", + "dist/2022-11-01/rust-std-beta-thumbv7em-none-eabihf.tar.gz": "cf7acd2b4a083522c01f1909891aaba27502ea0a3a5eff93dfb41971f832bba6", + "dist/2022-11-01/rust-std-beta-thumbv7em-none-eabihf.tar.xz": "e9544acbefa3effe55537de85311b00077a0567d64345aa80414752037212b5f", + "dist/2022-11-01/rust-std-beta-thumbv7m-none-eabi.tar.gz": "247e9dae16f46c64da895528f3e902030110e2aad8270f169c636ca14bfc28aa", + "dist/2022-11-01/rust-std-beta-thumbv7m-none-eabi.tar.xz": "b7de9e8bf7b7d04fc9575390d69eacbcc62a39c35c81f37d2170424cffe6a356", + "dist/2022-11-01/rust-std-beta-thumbv7neon-linux-androideabi.tar.gz": "bd3dc986a11967e8ed050a88d03d1c0814b08cc1ab0cf929561fbf5a941a335e", + "dist/2022-11-01/rust-std-beta-thumbv7neon-linux-androideabi.tar.xz": "262b4c4ccbe20c9e913a7417c8ca72c6fb7e71f187103929057dcd0fc0b49cea", + "dist/2022-11-01/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.gz": "85f6a725e5a726afab9ae019944567b42ee769db98a8d3c335d449eca92344e0", + "dist/2022-11-01/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.xz": "07e897f4320f249b3f458e44e5440591962105a3b6032b54f4448c0bd21da964", + "dist/2022-11-01/rust-std-beta-thumbv8m.base-none-eabi.tar.gz": "e92855841ae93990f88f3893a1bf511853fc3f10938eda767d5c7ff7d310aa4f", + "dist/2022-11-01/rust-std-beta-thumbv8m.base-none-eabi.tar.xz": "3c3412a67f769ead9e8bafbcb5ff6dfc8ef89f0d8234baee7b39ab9df9fadebf", + "dist/2022-11-01/rust-std-beta-thumbv8m.main-none-eabi.tar.gz": "f3cd623fdd466e5c0b5749dc4e90a75122f1989f6fcae0ace8c76f3b394a0752", + "dist/2022-11-01/rust-std-beta-thumbv8m.main-none-eabi.tar.xz": "3793ab2a42f1bc59ad560ad1af75ed90c49e25f665330b5b8ce50ed73ef88508", + "dist/2022-11-01/rust-std-beta-thumbv8m.main-none-eabihf.tar.gz": "cc6c715e320c7fc5fd90f446f7c2ce6b356e95934d05f79c4e2d0fc304f212bd", + "dist/2022-11-01/rust-std-beta-thumbv8m.main-none-eabihf.tar.xz": "42a47ce6768b24c2b40c6a724003a401bfb37201a773e3c31ee413cc559cda70", + "dist/2022-11-01/rust-std-beta-wasm32-unknown-emscripten.tar.gz": "4c09e5b03a921d8c1d8a10d9535e81be3b3bbed961d229311cc691396ae10cbb", + "dist/2022-11-01/rust-std-beta-wasm32-unknown-emscripten.tar.xz": "775f7223bc5d962b5356a4884565a948d3cb5289fafe3e2eb2b8ad67550d72b4", + "dist/2022-11-01/rust-std-beta-wasm32-unknown-unknown.tar.gz": "bc027d9170132c36faa47da1ff8f26d26d383a5145cb9dd2dce20e769ea300ba", + "dist/2022-11-01/rust-std-beta-wasm32-unknown-unknown.tar.xz": "9a721d3550132930820d9b809074535d2b63ecb91d5c061effded92b503bf0c2", + "dist/2022-11-01/rust-std-beta-wasm32-wasi.tar.gz": "047d58ef5e10ab51a81dbc11646fca184945a1c52e7a91552449c67952c8d898", + "dist/2022-11-01/rust-std-beta-wasm32-wasi.tar.xz": "a490ce6ebc77a4a49c2fdeec471dd9e586b2aa26f1e7f2fc1323cc06b2b336d5", + "dist/2022-11-01/rust-std-beta-x86_64-apple-darwin.tar.gz": "df73bc81d446792d9366772944a04f69ad32f427e1949e05d4f7c202c350c269", + "dist/2022-11-01/rust-std-beta-x86_64-apple-darwin.tar.xz": "450aec3ec53594869bbf16ffe1713dfa19b8dcadd812a4af811bd56f1f58c929", + "dist/2022-11-01/rust-std-beta-x86_64-apple-ios.tar.gz": "fb698f63336a186983b09c2c49109dd080c22653f3367dabfcbae564144aff35", + "dist/2022-11-01/rust-std-beta-x86_64-apple-ios.tar.xz": "0d475ba4a4444f4da5fb39d26c9cdbc0352ea799d7e30f57e2e79d8c3c7a7021", + "dist/2022-11-01/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.gz": "137234fc37b93ef4fa543f4e33217079137b4dbb51efbea669b93e561932b5e9", + "dist/2022-11-01/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.xz": "01e1978d9359a5112aa77409ff17c3d0e0dec774815f679065db6c6293aaa623", + "dist/2022-11-01/rust-std-beta-x86_64-fuchsia.tar.gz": "662e62862b1586f29372339319680c88b7cebe41e98401b5dd62e320755f0d62", + "dist/2022-11-01/rust-std-beta-x86_64-fuchsia.tar.xz": "4a644c6c85c8e427d68a669b0f598669023e2c0db2b69b94a7124c18772052dd", + "dist/2022-11-01/rust-std-beta-x86_64-linux-android.tar.gz": "752a57eb3de0060c1ffc6eb0af71d88d5f881b543b11b209593be2b18af1f902", + "dist/2022-11-01/rust-std-beta-x86_64-linux-android.tar.xz": "19effccfd9d63e955cb0736968c4c300c6d919217a64cde464c30a499ae9fd9c", + "dist/2022-11-01/rust-std-beta-x86_64-pc-solaris.tar.gz": "aa8a36ec1892c68a1c1ea0d9ac1b92b03c975a0d8ee538aaee5d757ad84d5b2e", + "dist/2022-11-01/rust-std-beta-x86_64-pc-solaris.tar.xz": "955ad79007d397a9e24d819e95017880b25424bdac01386cb8fc6d50247b1274", + "dist/2022-11-01/rust-std-beta-x86_64-pc-windows-gnu.tar.gz": "9f15bf80a2384f2fd333dee41289fdd8529170192dcbdd8cba0a73d32715ccc3", + "dist/2022-11-01/rust-std-beta-x86_64-pc-windows-gnu.tar.xz": "539bcefcd6b888c5f38abca47792dcff1676ef31eeb9a4a045703582262758c1", + "dist/2022-11-01/rust-std-beta-x86_64-pc-windows-msvc.tar.gz": "748fd22a993be659f85c3799871c4de09a99fcd7805c6d0e9d5a18dddfd2e26b", + "dist/2022-11-01/rust-std-beta-x86_64-pc-windows-msvc.tar.xz": "68c22dfa2ef5ecd2d43661716e8a8394eaa36e8e960d34dc421bbbe57c3e0d23", + "dist/2022-11-01/rust-std-beta-x86_64-sun-solaris.tar.gz": "f06118445fc6671d491c61dd8e6ff83ca21fc1d692058eea072cbe01ff798fb2", + "dist/2022-11-01/rust-std-beta-x86_64-sun-solaris.tar.xz": "b3fdd56baadf3a8bffd17730d61b2ccef25ffa25d5cd826bb9a45940bf573fb5", + "dist/2022-11-01/rust-std-beta-x86_64-unknown-freebsd.tar.gz": "2dfab0336a523182d200c7a6096fb29c199339b282ba03b469a9a1e5c5a5bb0b", + "dist/2022-11-01/rust-std-beta-x86_64-unknown-freebsd.tar.xz": "ee5b9158ca0c2e829bb79ac526d17a2ba27ca4e305e134241ba1f8347a9bace5", + "dist/2022-11-01/rust-std-beta-x86_64-unknown-illumos.tar.gz": "fe62b766d11e9ac55db5011a63086af5d87ce560c0656dc214c668db752675e4", + "dist/2022-11-01/rust-std-beta-x86_64-unknown-illumos.tar.xz": "e4b1068de2812c62e7ac0ec080f605fa90123a94563dc4f898221275fbd5178b", + "dist/2022-11-01/rust-std-beta-x86_64-unknown-linux-gnu.tar.gz": "c5ce6885405ba4d1694a7eb767837320ece5a02133e94c1c22ac07143d6f752c", + "dist/2022-11-01/rust-std-beta-x86_64-unknown-linux-gnu.tar.xz": "eac46cc9200125be2548d6b9f2c2d37b046b8b43b25dd7f7347d88ef6795a3c7", + "dist/2022-11-01/rust-std-beta-x86_64-unknown-linux-gnux32.tar.gz": "1b3d1d051cf355eb26bf9de5096d984f83dc92fdeab3bdcd18d88152c0e2a2bf", + "dist/2022-11-01/rust-std-beta-x86_64-unknown-linux-gnux32.tar.xz": "a17cf4a9df1b1be17f5163f05665bc40638e62210d8e0623fb1afeeb96acad2a", + "dist/2022-11-01/rust-std-beta-x86_64-unknown-linux-musl.tar.gz": "90a2e5712bc37f28a0d1f71c54cc04233049c638e4f0592b50adea352e21038f", + "dist/2022-11-01/rust-std-beta-x86_64-unknown-linux-musl.tar.xz": "ad76d090357f5e272b1598c35dd24137fb9950e1bdc50b9332fa1d2fcc33a00b", + "dist/2022-11-01/rust-std-beta-x86_64-unknown-netbsd.tar.gz": "37e0954add559b24c08ad284fb80294e435491159db63ea78a6183af5926dcec", + "dist/2022-11-01/rust-std-beta-x86_64-unknown-netbsd.tar.xz": "d6542bd592edd3924999e690416b6bc559486388add76fa77044114b70700fac", + "dist/2022-11-01/rust-std-beta-x86_64-unknown-none.tar.gz": "d021e49b68b8321354d99ae0fe80a6b042ec798ca7fe37cc92d4f0c0480f7ebf", + "dist/2022-11-01/rust-std-beta-x86_64-unknown-none.tar.xz": "f6202c50c6d3575fdb398a8c98adeb0d86794b60c3951887c90a9e4acb6a89c0", + "dist/2022-11-01/rust-std-beta-x86_64-unknown-redox.tar.gz": "b8ca678975c0c18d0fda1bb118b35366d1261e366639b8bb455b6bc59388082f", + "dist/2022-11-01/rust-std-beta-x86_64-unknown-redox.tar.xz": "119f9e65dc3484f677064e068da42a1e7b8dc0be21d0cbf5185c9836589b39be", + "dist/2022-11-01/rustc-beta-aarch64-apple-darwin.tar.gz": "11aa79c56a9dea2d5305ed049485a1257912fc0dfca1feff37b768971f4c1701", + "dist/2022-11-01/rustc-beta-aarch64-apple-darwin.tar.xz": "a031051ccf97100bd8b4d2e4df7a67371cdf300df4697e1d05a7cec33a7d8c09", + "dist/2022-11-01/rustc-beta-aarch64-pc-windows-msvc.tar.gz": "4d015042d7d06929488f607bc56d925002e6f352d74fe192dc30e7feebb9947c", + "dist/2022-11-01/rustc-beta-aarch64-pc-windows-msvc.tar.xz": "d72824112c96514d927df46f6e755898d26ddd5b805f6c2c0411c773105ad61f", + "dist/2022-11-01/rustc-beta-aarch64-unknown-linux-gnu.tar.gz": "3e70261ed7c130cb7256717cec0c37476961932be228e46e028818f9076dfccf", + "dist/2022-11-01/rustc-beta-aarch64-unknown-linux-gnu.tar.xz": "452f07f63888cf27ca2d061751602bb07a43348eca9cab30db27940a36f496e5", + "dist/2022-11-01/rustc-beta-aarch64-unknown-linux-musl.tar.gz": "995a6410305d43234eb94710ddc251bafd9f5fe4ecacc51c4dc1447f364be30a", + "dist/2022-11-01/rustc-beta-aarch64-unknown-linux-musl.tar.xz": "2d586e5d1a72194ce2798d4f07c873d52ea441cabe5040ff682664d618b98d4e", + "dist/2022-11-01/rustc-beta-arm-unknown-linux-gnueabi.tar.gz": "65954bc862cd149cae2702f25b186fa2166d80cb45bfe6867d075381f2614464", + "dist/2022-11-01/rustc-beta-arm-unknown-linux-gnueabi.tar.xz": "f3a5f8318efee7eb9ba4d861876b0a5415f308c9dc2cea751a10b2e259303627", + "dist/2022-11-01/rustc-beta-arm-unknown-linux-gnueabihf.tar.gz": "d4be89140f0bd4ef9f73a1b54f949973ce560c4dd62c664974f82278ca0d6079", + "dist/2022-11-01/rustc-beta-arm-unknown-linux-gnueabihf.tar.xz": "5b381b513c27f95f9d170e9c532839a27facfe6eb4dd215c078b44fde40e3ba3", + "dist/2022-11-01/rustc-beta-armv7-unknown-linux-gnueabihf.tar.gz": "ffdf714a07408901962c861103b062adf334e0febc1abfa8c538c40b0070793e", + "dist/2022-11-01/rustc-beta-armv7-unknown-linux-gnueabihf.tar.xz": "ada55533236ef8c629ca72f929bb87db4b68f8c3d4c6fb3e7001f892a84a2b82", + "dist/2022-11-01/rustc-beta-i686-pc-windows-gnu.tar.gz": "b7e059973b61a4d7a0c96b4642629bf72668380a5ad8a2962181b1229ac2174c", + "dist/2022-11-01/rustc-beta-i686-pc-windows-gnu.tar.xz": "9aa3bc05e1782b8ff5d278f5b5baac4b0ae523ad8bba2bacd46e1bca11cd38b9", + "dist/2022-11-01/rustc-beta-i686-pc-windows-msvc.tar.gz": "acab77f5641be0d7102e6b911f134aa36b6fcad5ac594100889ed0e494eccca3", + "dist/2022-11-01/rustc-beta-i686-pc-windows-msvc.tar.xz": "e9af106c009e5fa0da36450a7a89a148ec176bd672ff636010846ab978804e4a", + "dist/2022-11-01/rustc-beta-i686-unknown-linux-gnu.tar.gz": "546e7b52f7f9e8c9a99163265dbc8a5ce65dac0fef4f6e1dc8b1bed79f0a24c3", + "dist/2022-11-01/rustc-beta-i686-unknown-linux-gnu.tar.xz": "b5ea7fc6016a4abcae3337261724ca2bd21025856134e1c2a1a1922d12ec19a8", + "dist/2022-11-01/rustc-beta-mips-unknown-linux-gnu.tar.gz": "0f3e0c8e7883dc7ebbec38e1f3446a33651ebba9a725443856b09ae7e8bcfec0", + "dist/2022-11-01/rustc-beta-mips-unknown-linux-gnu.tar.xz": "42871f7f098008f61f6cfd3cf78240156280cc7f5e52860d8125e22b3733a207", + "dist/2022-11-01/rustc-beta-mips64-unknown-linux-gnuabi64.tar.gz": "ded0d4da36a0658d46c6705c04fa40d0894b6e113776d2ef8e954e9675e98f9a", + "dist/2022-11-01/rustc-beta-mips64-unknown-linux-gnuabi64.tar.xz": "2f9ec1ba69a7abbe4efbc5fa00715f520b4c69792b96e98ed8a72e3f798eb137", + "dist/2022-11-01/rustc-beta-mips64el-unknown-linux-gnuabi64.tar.gz": "090431409021fa0167576c717cf5daac750f9baf7badc3bc031547dad8dedb18", + "dist/2022-11-01/rustc-beta-mips64el-unknown-linux-gnuabi64.tar.xz": "0542d0336c8cdacf8a830d2a7c3218b76a00ae37db23fb2f12b928bb7b7dd488", + "dist/2022-11-01/rustc-beta-mipsel-unknown-linux-gnu.tar.gz": "2d6db76bc5242af8c2199c5e74f152bbd8103477855379e7c5c200b498ccf901", + "dist/2022-11-01/rustc-beta-mipsel-unknown-linux-gnu.tar.xz": "0dc803a305497cc905f3937691e4f1679c72a385b57ee931b19ac5347052c502", + "dist/2022-11-01/rustc-beta-powerpc-unknown-linux-gnu.tar.gz": "809f547fb5c27c7d15816642839f9ff5fee20f03a3ce390d5b2bfdc983a7c7e2", + "dist/2022-11-01/rustc-beta-powerpc-unknown-linux-gnu.tar.xz": "bd8403226676b78b40c7a494b3a89f9bed956e7eedf3a65a61cba41a6382f5b6", + "dist/2022-11-01/rustc-beta-powerpc64-unknown-linux-gnu.tar.gz": "12cd357dc72d67911a521dc0ea44a8d05bc4c214a7f6b9e88872ddc03811dc15", + "dist/2022-11-01/rustc-beta-powerpc64-unknown-linux-gnu.tar.xz": "d92f790cabb85373455b5adee9e692dc934dff60eccb70c077f29cde35e7cd00", + "dist/2022-11-01/rustc-beta-powerpc64le-unknown-linux-gnu.tar.gz": "a175a2b7d948459c12f44592c1ee5c79825a120557ff0c488fb0bd4e45c7ee99", + "dist/2022-11-01/rustc-beta-powerpc64le-unknown-linux-gnu.tar.xz": "d047a4ed562cc91469785fed44d97061d60e1f9c677b5de05245648373df111f", + "dist/2022-11-01/rustc-beta-riscv64gc-unknown-linux-gnu.tar.gz": "1b1f20032337e6a0b5e4745a3542a5638747bf2f3b62b2eb855c0ea1ac54d81c", + "dist/2022-11-01/rustc-beta-riscv64gc-unknown-linux-gnu.tar.xz": "c8a46c9c002ce19e940a449a4787055b4ad45076a606bd68626a1c8d892d8191", + "dist/2022-11-01/rustc-beta-s390x-unknown-linux-gnu.tar.gz": "547c670fd6a5f38f98e7b47daaf6822fd5a1abd5a7c11b6f2b5838cb145c615e", + "dist/2022-11-01/rustc-beta-s390x-unknown-linux-gnu.tar.xz": "16a0783135c22b64541cbf9201e5f84ab4befbc9ec0117f3e9639cd42dcb81bf", + "dist/2022-11-01/rustc-beta-x86_64-apple-darwin.tar.gz": "3121d060a0306c539334fb42c0c6edb6295eb4b5d05b63e55df98d5dc1cb0eba", + "dist/2022-11-01/rustc-beta-x86_64-apple-darwin.tar.xz": "4697febb60fdecb5cd70bde0cffad77cdcf8cce057349b4e1f26e3dd4f2f4a51", + "dist/2022-11-01/rustc-beta-x86_64-pc-windows-gnu.tar.gz": "c33bb5e98d83f0a7393c631b6b53eb4a8517bdbf506e1ceb6f0bdd8493fa24b9", + "dist/2022-11-01/rustc-beta-x86_64-pc-windows-gnu.tar.xz": "167e1ab52c4478e6aa8b2bea563f2d8caf3605158731a892181f9d24b027ffff", + "dist/2022-11-01/rustc-beta-x86_64-pc-windows-msvc.tar.gz": "efbe536d85810f2edb6bb7232617f12d3f208e077d177c24f507ff02c8e83a11", + "dist/2022-11-01/rustc-beta-x86_64-pc-windows-msvc.tar.xz": "b6acaa3206a3af7fe0e97d4d9211fc76ba972afcdd188443a72027dd34236658", + "dist/2022-11-01/rustc-beta-x86_64-unknown-freebsd.tar.gz": "8eb739094411afb56ad791b84aa2ddcd2c98b6ca5a4c1cd7fa631571702f1d67", + "dist/2022-11-01/rustc-beta-x86_64-unknown-freebsd.tar.xz": "4572c19bf416c188a3691cc9542422b92a124594bdf83c82213d07a3aaeef465", + "dist/2022-11-01/rustc-beta-x86_64-unknown-illumos.tar.gz": "eca080758173b3bee5a1ed7d04473a8334422fc58c762031877e690a255202c8", + "dist/2022-11-01/rustc-beta-x86_64-unknown-illumos.tar.xz": "68b1ced7efbd6bb4cac647e7417b2ad982f58a1cc546b9391213e85e5852ce6c", + "dist/2022-11-01/rustc-beta-x86_64-unknown-linux-gnu.tar.gz": "3a4870b33c0f223dc295fcf3f1c4e331a2631dbc07279f4ca7452d86c5f6e730", + "dist/2022-11-01/rustc-beta-x86_64-unknown-linux-gnu.tar.xz": "556821823576a5c0387f0dc89139d3cddc2a17072199607c352fe4b190b7f02f", + "dist/2022-11-01/rustc-beta-x86_64-unknown-linux-musl.tar.gz": "4e1723c8f268eecc9bf3efb16241ce03bf109b9f7c6f4f62e430b7ccd1c092cb", + "dist/2022-11-01/rustc-beta-x86_64-unknown-linux-musl.tar.xz": "47bb3fb8f8529f19fa9725a43a57abd8bc3c7b2a30e17f86b137df0c57a3c549", + "dist/2022-11-01/rustc-beta-x86_64-unknown-netbsd.tar.gz": "530c24d950028d0745110672fad230da8a2a0e4cd4e5ac5afcf1ff8562288925", + "dist/2022-11-01/rustc-beta-x86_64-unknown-netbsd.tar.xz": "cd3654b33b3a8e7fbcde2e380bf2914cb07fe6f8355c8810a5bcfe3a05d63f84", + "dist/2022-11-01/rustfmt-nightly-aarch64-apple-darwin.tar.gz": "47527c62b813c0612b80c864b3720b7e0673eb2dd762887254fd6a80f11c94b0", + "dist/2022-11-01/rustfmt-nightly-aarch64-apple-darwin.tar.xz": "82248dd276ecc0fd45031ba131cb2c870a4b3c09b822d8ad4454f26f506d7810", + "dist/2022-11-01/rustfmt-nightly-aarch64-pc-windows-msvc.tar.gz": "fdc9cc842850023e7c22ac22173a18aa5383a2e2fecb713c802e59d55cc5232d", + "dist/2022-11-01/rustfmt-nightly-aarch64-pc-windows-msvc.tar.xz": "10ddbe6a89cadde47f6f52ef0c4f9ab08f4ced2281fadd1ecbc6a0e4736c9787", + "dist/2022-11-01/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.gz": "2135c6d129fa7ecd831e451e173c38677ea39975a91cd6092252e4c0bd93eeaa", + "dist/2022-11-01/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.xz": "387d43021bd0ec1586155d1b977470646a68e2625fc192331b76180755687d37", + "dist/2022-11-01/rustfmt-nightly-aarch64-unknown-linux-musl.tar.gz": "41da916cbac667f5f238c3aee3bfb230c3345a4d625779c1fcf57813c9138696", + "dist/2022-11-01/rustfmt-nightly-aarch64-unknown-linux-musl.tar.xz": "f28cf712bc617f1755e78a7a442633a7aff78857b98d9aae473effc5684ce8aa", + "dist/2022-11-01/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.gz": "065fd7fdcb9f38a9c08b256b46627c8ce38a6433dc162034a306f4d4f4627a31", + "dist/2022-11-01/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.xz": "1fc14261867b540e6d014cc5a21c557d0a4bb31d2619ae98a330585915365614", + "dist/2022-11-01/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.gz": "1a9ebea072c333e99a3339a87ac3971deb4fe2baca9bd0e8429321a81cce847f", + "dist/2022-11-01/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.xz": "1c7a72cf8e9cda52d02bd5f4244164aea829914087501cb0bedd75f05f464a91", + "dist/2022-11-01/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.gz": "1f8405178138601f65dbe10f93d326c705ea91f9e7200f253d6123f618d09ad8", + "dist/2022-11-01/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.xz": "00365767eb739ecd82c6264795768baba07a101aacec59e137a7495afd0b3288", + "dist/2022-11-01/rustfmt-nightly-i686-pc-windows-gnu.tar.gz": "b2e4c4672f440e1f97913497ee158280cb8ed70c81cb47a85e5382cb3de0b03c", + "dist/2022-11-01/rustfmt-nightly-i686-pc-windows-gnu.tar.xz": "01bea91a3ab8203b32cbd1fb2945a1eca68179e8f4011e387a230587fc2736a4", + "dist/2022-11-01/rustfmt-nightly-i686-pc-windows-msvc.tar.gz": "2d0db2a9f187d300c183cfe2ac6778547ab6492720c0e9df3e78f5b06004e758", + "dist/2022-11-01/rustfmt-nightly-i686-pc-windows-msvc.tar.xz": "b89b02b9fdedb9a93dee602dd9c818e97c397ef73c3f1d0164ddd2ab809cddc2", + "dist/2022-11-01/rustfmt-nightly-i686-unknown-linux-gnu.tar.gz": "4498a8e6d0ae7a793a9f3c84e3bbe9218c37053a1f3dd6a0b4ad7edd1a41493f", + "dist/2022-11-01/rustfmt-nightly-i686-unknown-linux-gnu.tar.xz": "27041aa61921b767be6670f0f08aad1a1ab8d09d0e86cd2e431e54744ed25d0b", + "dist/2022-11-01/rustfmt-nightly-mips-unknown-linux-gnu.tar.gz": "870923556049bd4be8da03fa6d876fa8249e4acf0ea2c83850c4e23a09fe577f", + "dist/2022-11-01/rustfmt-nightly-mips-unknown-linux-gnu.tar.xz": "8c05e1f60a59064c05db7522245d482b559ae858a5c9c772db81a05daa60a4c6", + "dist/2022-11-01/rustfmt-nightly-mips64-unknown-linux-gnuabi64.tar.gz": "1a88c20701cc6f7dd2b3e32bef72a78936c39095a35237fc4a4b5a497790a048", + "dist/2022-11-01/rustfmt-nightly-mips64-unknown-linux-gnuabi64.tar.xz": "9b5ccf5413650144a79f382efd12204aeddf3421ea6f06615afc489cdf30691e", + "dist/2022-11-01/rustfmt-nightly-mips64el-unknown-linux-gnuabi64.tar.gz": "4bcf264ba7ce42aee79d76ba0f19818aff71ee666ac4ac417c2a60b0dafa8865", + "dist/2022-11-01/rustfmt-nightly-mips64el-unknown-linux-gnuabi64.tar.xz": "fd6ff248063cd53ee6b0538c8b3c8af1758ae5c42cc2f5fc805ab96799033f7d", + "dist/2022-11-01/rustfmt-nightly-mipsel-unknown-linux-gnu.tar.gz": "a03cf4d831ba58d1e562d6fd48dd7558d9034046ae7050883eb1d0fc2cad6895", + "dist/2022-11-01/rustfmt-nightly-mipsel-unknown-linux-gnu.tar.xz": "30be7166fa091929d1a4b5eed4b72c4b5c94898861f4e91fb45a2b9ad4333ca6", + "dist/2022-11-01/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.gz": "59f3910a559994863f1910ffcf34cae348d0c07128d00ce5ac085bbca349f7f5", + "dist/2022-11-01/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.xz": "817df1ddab344e47df34c73918c5bbb3a7b33048f8ac5c5794cb35624f5bce24", + "dist/2022-11-01/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.gz": "7825a5f19cb29245de96eb22183fbfc38b75eda0ba63d2255fa062f9c6764bbf", + "dist/2022-11-01/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.xz": "0d6384aa1162d821edb6d22326b0a1d481e6735d4343a70df7bead694bb71567", + "dist/2022-11-01/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.gz": "aef195c86920cfecafc29f80ce6a88c704f09d72011ad1fd462564bf858c75a6", + "dist/2022-11-01/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.xz": "da43e621a113d88f7c4805f70cb5208bac66f97c68485a60f95cf11f5ae0f55c", + "dist/2022-11-01/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.gz": "2603b5061d059655e3298df94875fa4876d5ea9af1e04dd197ec5cefa3e1eb4c", + "dist/2022-11-01/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.xz": "f79606c20ce3bf64a9ede63e878cda199e7f1b0b13f40bd51d7108b3d4c72cb0", + "dist/2022-11-01/rustfmt-nightly-s390x-unknown-linux-gnu.tar.gz": "f6c46ffbb38f8838c496e1eddea7d6f27392699abfafd0d13b234eee39238181", + "dist/2022-11-01/rustfmt-nightly-s390x-unknown-linux-gnu.tar.xz": "9244fc29cd3c32c971f44fcdaa26623b8976efaf0a4705b573798af5b0b0896e", + "dist/2022-11-01/rustfmt-nightly-x86_64-apple-darwin.tar.gz": "bd502f9105d56af252da1038687a4e942a477c7047cac9730de7414cdbbfbc48", + "dist/2022-11-01/rustfmt-nightly-x86_64-apple-darwin.tar.xz": "426785558da44683a554c542475c9402932c72d63148c37917e8cc6e429ad413", + "dist/2022-11-01/rustfmt-nightly-x86_64-pc-windows-gnu.tar.gz": "52646c86ad464c5803f74ab37166dc1692383bc5fd94818bd579e518c327251e", + "dist/2022-11-01/rustfmt-nightly-x86_64-pc-windows-gnu.tar.xz": "67d1490a41932c2a89981e18c9735d4437faedd1e708e26f75dfd21d4709488b", + "dist/2022-11-01/rustfmt-nightly-x86_64-pc-windows-msvc.tar.gz": "9feb7b704a6d3e6b019a99ecd033042ce81a4b126e4288e0b4772266c6e0a65e", + "dist/2022-11-01/rustfmt-nightly-x86_64-pc-windows-msvc.tar.xz": "fba240009d3f27e04200133120c46112ac64281e99952da44d6fe8a01557f236", + "dist/2022-11-01/rustfmt-nightly-x86_64-unknown-freebsd.tar.gz": "ce15bda4992ada52f94dae6b1a0e220f26324acefb62094035abe112aa878fec", + "dist/2022-11-01/rustfmt-nightly-x86_64-unknown-freebsd.tar.xz": "189a8579cf3fe99b9c084821ce1ee9bec6977470341e2ae45b859dcdacf65d21", + "dist/2022-11-01/rustfmt-nightly-x86_64-unknown-illumos.tar.gz": "15abcd9e43f2c87fc894b3e280a99865508f9079badcbe7be07c6b79e85f01b4", + "dist/2022-11-01/rustfmt-nightly-x86_64-unknown-illumos.tar.xz": "f52dab31a428e568518b00d3afc1426569810bcd20a7db1c0093200c6db86d24", + "dist/2022-11-01/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.gz": "57ec35b95a5fd803b2d4dacf7657847111a6cc9bda3cda962174965cd6005085", + "dist/2022-11-01/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.xz": "96987349e20e3f602bb6f518924660c09a4575887730b1bbe36adee921921956", + "dist/2022-11-01/rustfmt-nightly-x86_64-unknown-linux-musl.tar.gz": "76d6f5882573169985f5b8a9e13cee8bbe3bd3b423ad287280a0809c6a5efc5a", + "dist/2022-11-01/rustfmt-nightly-x86_64-unknown-linux-musl.tar.xz": "9a2c79685b4ac57efea65e43dafa28b59cead1c14e98f10e0196cb2cfd2fa0b6", + "dist/2022-11-01/rustfmt-nightly-x86_64-unknown-netbsd.tar.gz": "0be98b45af7e666955e6e0adb5b4cc3f5517c8d144702b10daedd053450cd5d5", + "dist/2022-11-01/rustfmt-nightly-x86_64-unknown-netbsd.tar.xz": "ae9ac6e1c0e14bfba746f3a85bfa3f009113d0edbf880a2cf20ece6046ee27bb" } } diff --git a/src/test/codegen/abi-efiapi.rs b/src/test/codegen/abi-efiapi.rs index b4fda5f8c842..9061d7432a3f 100644 --- a/src/test/codegen/abi-efiapi.rs +++ b/src/test/codegen/abi-efiapi.rs @@ -27,7 +27,7 @@ trait Copy { } //x86_64: define win64cc void @has_efiapi //i686: define void @has_efiapi //aarch64: define dso_local void @has_efiapi -//arm: define dso_local void @has_efiapi +//arm: define dso_local arm_aapcscc void @has_efiapi //riscv: define dso_local void @has_efiapi #[no_mangle] pub extern "efiapi" fn has_efiapi() {} diff --git a/src/test/codegen/enum-match.rs b/src/test/codegen/enum-match.rs new file mode 100644 index 000000000000..efab189fd7b8 --- /dev/null +++ b/src/test/codegen/enum-match.rs @@ -0,0 +1,112 @@ +// compile-flags: -Copt-level=1 +// only-x86_64 + +#![crate_type = "lib"] + +// Check each of the 3 cases for `codegen_get_discr`. + +// Case 0: One tagged variant. +pub enum Enum0 { + A(bool), + B, +} + +// CHECK: define i8 @match0{{.*}} +// CHECK-NEXT: start: +// CHECK-NEXT: %1 = icmp eq i8 %0, 2 +// CHECK-NEXT: %2 = and i8 %0, 1 +// CHECK-NEXT: %.0 = select i1 %1, i8 13, i8 %2 +#[no_mangle] +pub fn match0(e: Enum0) -> u8 { + use Enum0::*; + match e { + A(b) => b as u8, + B => 13, + } +} + +// Case 1: Niche values are on a boundary for `range`. +pub enum Enum1 { + A(bool), + B, + C, +} + +// CHECK: define i8 @match1{{.*}} +// CHECK-NEXT: start: +// CHECK-NEXT: %1 = icmp ugt i8 %0, 1 +// CHECK-NEXT: %2 = zext i8 %0 to i64 +// CHECK-NEXT: %3 = add nsw i64 %2, -1 +// CHECK-NEXT: %_2 = select i1 %1, i64 %3, i64 0 +// CHECK-NEXT: switch i64 %_2, label {{.*}} [ +#[no_mangle] +pub fn match1(e: Enum1) -> u8 { + use Enum1::*; + match e { + A(b) => b as u8, + B => 13, + C => 100, + } +} + +// Case 2: Special cases don't apply. +pub enum X { + _2=2, _3, _4, _5, _6, _7, _8, _9, _10, _11, + _12, _13, _14, _15, _16, _17, _18, _19, _20, + _21, _22, _23, _24, _25, _26, _27, _28, _29, + _30, _31, _32, _33, _34, _35, _36, _37, _38, + _39, _40, _41, _42, _43, _44, _45, _46, _47, + _48, _49, _50, _51, _52, _53, _54, _55, _56, + _57, _58, _59, _60, _61, _62, _63, _64, _65, + _66, _67, _68, _69, _70, _71, _72, _73, _74, + _75, _76, _77, _78, _79, _80, _81, _82, _83, + _84, _85, _86, _87, _88, _89, _90, _91, _92, + _93, _94, _95, _96, _97, _98, _99, _100, _101, + _102, _103, _104, _105, _106, _107, _108, _109, + _110, _111, _112, _113, _114, _115, _116, _117, + _118, _119, _120, _121, _122, _123, _124, _125, + _126, _127, _128, _129, _130, _131, _132, _133, + _134, _135, _136, _137, _138, _139, _140, _141, + _142, _143, _144, _145, _146, _147, _148, _149, + _150, _151, _152, _153, _154, _155, _156, _157, + _158, _159, _160, _161, _162, _163, _164, _165, + _166, _167, _168, _169, _170, _171, _172, _173, + _174, _175, _176, _177, _178, _179, _180, _181, + _182, _183, _184, _185, _186, _187, _188, _189, + _190, _191, _192, _193, _194, _195, _196, _197, + _198, _199, _200, _201, _202, _203, _204, _205, + _206, _207, _208, _209, _210, _211, _212, _213, + _214, _215, _216, _217, _218, _219, _220, _221, + _222, _223, _224, _225, _226, _227, _228, _229, + _230, _231, _232, _233, _234, _235, _236, _237, + _238, _239, _240, _241, _242, _243, _244, _245, + _246, _247, _248, _249, _250, _251, _252, _253, +} + +pub enum Enum2 { + A(X), + B, + C, + D, + E, +} + +// CHECK: define i8 @match2{{.*}} +// CHECK-NEXT: start: +// CHECK-NEXT: %1 = add i8 %0, 2 +// CHECK-NEXT: %2 = zext i8 %1 to i64 +// CHECK-NEXT: %3 = icmp ult i8 %1, 4 +// CHECK-NEXT: %4 = add nuw nsw i64 %2, 1 +// CHECK-NEXT: %_2 = select i1 %3, i64 %4, i64 0 +// CHECK-NEXT: switch i64 %_2, label {{.*}} [ +#[no_mangle] +pub fn match2(e: Enum2) -> u8 { + use Enum2::*; + match e { + A(b) => b as u8, + B => 13, + C => 100, + D => 200, + E => 250, + } +} diff --git a/src/test/debuginfo/mutex.rs b/src/test/debuginfo/mutex.rs index 314ba40b0e3d..61ec6a81243d 100644 --- a/src/test/debuginfo/mutex.rs +++ b/src/test/debuginfo/mutex.rs @@ -10,7 +10,7 @@ // // cdb-command:dx m,d // cdb-check:m,d [Type: std::sync::mutex::Mutex] -// cdb-check: [...] inner [Type: std::sys_common::mutex::MovableMutex] +// cdb-check: [...] inner [Type: std::sys::windows::locks::mutex::Mutex] // cdb-check: [...] poison [Type: std::sync::poison::Flag] // cdb-check: [...] data : 0 [Type: core::cell::UnsafeCell] diff --git a/src/test/debuginfo/rwlock-read.rs b/src/test/debuginfo/rwlock-read.rs index ed9aae16b0db..bc42f92f053d 100644 --- a/src/test/debuginfo/rwlock-read.rs +++ b/src/test/debuginfo/rwlock-read.rs @@ -16,7 +16,7 @@ // cdb-command:dx r // cdb-check:r [Type: std::sync::rwlock::RwLockReadGuard] // cdb-check: [...] data : NonNull([...]: 0) [Type: core::ptr::non_null::NonNull] -// cdb-check: [...] inner_lock : [...] [Type: std::sys_common::rwlock::MovableRwLock *] +// cdb-check: [...] inner_lock : [...] [Type: std::sys::windows::locks::rwlock::RwLock *] #[allow(unused_variables)] diff --git a/src/test/incremental/issue-101518.rs b/src/test/incremental/issue-101518.rs new file mode 100644 index 000000000000..501be175fce5 --- /dev/null +++ b/src/test/incremental/issue-101518.rs @@ -0,0 +1,31 @@ +// revisions: cfail1 +// should-ice +// error-pattern: forcing query +// known-bug: #101518 + +#[derive(PartialEq, Eq)] +struct Id<'a> { + ns: &'a str, +} +fn visit_struct() { + let id = Id { ns: "random1" }; + const FLAG: Id<'static> = Id { + ns: "needs_to_be_the_same", + }; + match id { + FLAG => {} + _ => {} + } +} +fn visit_struct2() { + let id = Id { ns: "random2" }; + const FLAG: Id<'static> = Id { + ns: "needs_to_be_the_same", + }; + match id { + FLAG => {} + _ => {} + } +} + +fn main() {} diff --git a/src/test/run-make-fulldeps/link-dedup/Makefile b/src/test/run-make-fulldeps/link-dedup/Makefile index 5c9603352989..eff18ab48ab4 100644 --- a/src/test/run-make-fulldeps/link-dedup/Makefile +++ b/src/test/run-make-fulldeps/link-dedup/Makefile @@ -9,4 +9,4 @@ all: $(RUSTC) empty.rs --cfg bar 2>&1 | $(CGREP) '"-ltesta" "-ltestb" "-ltesta"' $(RUSTC) empty.rs 2>&1 | $(CGREP) '"-ltesta"' $(RUSTC) empty.rs 2>&1 | $(CGREP) -v '"-ltestb"' - $(RUSTC) empty.rs 2>&1 | $(CGREP) -v '"-ltesta" "-ltesta"' + $(RUSTC) empty.rs 2>&1 | $(CGREP) -v '"-ltesta" "-ltesta" "-ltesta"' diff --git a/src/test/run-make-fulldeps/link-dedup/depa.rs b/src/test/run-make-fulldeps/link-dedup/depa.rs index e48ffd6413cd..19178c5bd495 100644 --- a/src/test/run-make-fulldeps/link-dedup/depa.rs +++ b/src/test/run-make-fulldeps/link-dedup/depa.rs @@ -5,3 +5,6 @@ extern "C" {} #[link(name = "testa")] extern "C" {} + +#[link(name = "testa")] +extern "C" {} diff --git a/src/test/run-make-fulldeps/split-debuginfo/Makefile b/src/test/run-make-fulldeps/split-debuginfo/Makefile index 1032f3408f06..1831ab38fab4 100644 --- a/src/test/run-make-fulldeps/split-debuginfo/Makefile +++ b/src/test/run-make-fulldeps/split-debuginfo/Makefile @@ -3,7 +3,7 @@ include ../tools.mk all: off packed unpacked ifeq ($(UNAME),Darwin) -# If disabled, don't run dsymutil +# If disabled, don't run `dsymutil`. off: rm -rf $(TMPDIR)/*.dSYM $(RUSTC) foo.rs -g -C split-debuginfo=off @@ -29,98 +29,280 @@ unpacked: [ ! -d $(TMPDIR)/foo.dSYM ] else ifdef IS_WINDOWS -# Windows only supports =packed +# Windows only supports packed debuginfo - nothing to test. off: packed: unpacked: else +# Some non-Windows, non-Darwin platforms are not stable, and some are. ifeq ($(UNAME),Linux) UNSTABLEOPTS := else UNSTABLEOPTS := -Zunstable-options endif +# - Debuginfo in `.o` files +# - `.o` deleted +# - `.dwo` never created +# - `.dwp` never created off: $(RUSTC) foo.rs -g -C $(UNSTABLEOPTS) split-debuginfo=off [ ! -f $(TMPDIR)/*.dwp ] [ ! -f $(TMPDIR)/*.dwo ] - $(RUSTC) foo.rs -g [ ! -f $(TMPDIR)/*.dwp ] [ ! -f $(TMPDIR)/*.dwo ] -packed: packed-split packed-single +packed: packed-split packed-single packed-lto packed-remapped packed-crosscrate +# - Debuginfo in `.dwo` files +# - `.o` deleted +# - `.dwo` deleted +# - `.dwp` present packed-split: $(RUSTC) foo.rs -g $(UNSTABLEOPTS) -C split-debuginfo=packed -Zsplit-dwarf-kind=split - ls $(TMPDIR)/*.dwp - rm -rf $(TMPDIR)/*.dwp $(TMPDIR)/*.dwo + ls $(TMPDIR)/*.o && exit 1 || exit 0 + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + rm $(TMPDIR)/foo.dwp + rm $(TMPDIR)/$(call BIN,foo) +# - Debuginfo in `.o` files +# - `.o` deleted +# - `.dwo` never created +# - `.dwp` present packed-single: $(RUSTC) foo.rs -g $(UNSTABLEOPTS) -C split-debuginfo=packed -Zsplit-dwarf-kind=single - ls $(TMPDIR)/*.dwp + ls $(TMPDIR)/*.o && exit 1 || exit 0 ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - rm -rf $(TMPDIR)/*.dwp + rm $(TMPDIR)/foo.dwp + rm $(TMPDIR)/$(call BIN,foo) + +packed-lto: packed-lto-split packed-lto-single + +# - rmeta file added to rlib, no object files are generated and thus no debuginfo is generated +# - `.o` never created +# - `.dwo` never created +# - `.dwp` never created +packed-lto-split: + $(RUSTC) baz.rs -g $(UNSTABLEOPTS) -Csplit-debuginfo=packed -Zsplit-dwarf-kind=split \ + --crate-type=rlib -Clinker-plugin-lto + ls $(TMPDIR)/*.o && exit 1 || exit 0 + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + rm $(TMPDIR)/libbaz.rlib + +# - rmeta file added to rlib, no object files are generated and thus no debuginfo is generated +# - `.o` never created +# - `.dwo` never created +# - `.dwp` never created +packed-lto-single: + $(RUSTC) baz.rs -g $(UNSTABLEOPTS) -Csplit-debuginfo=packed -Zsplit-dwarf-kind=single \ + --crate-type=rlib -Clinker-plugin-lto + ls $(TMPDIR)/*.o && exit 1 || exit 0 + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + rm $(TMPDIR)/libbaz.rlib packed-remapped: packed-remapped-split packed-remapped-single +# - Debuginfo in `.dwo` files +# - `.o` and binary refer to remapped `.dwo` paths which do not exist +# - `.o` deleted +# - `.dwo` deleted +# - `.dwp` present packed-remapped-split: $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -C debuginfo=2 \ -Z split-dwarf-kind=split --remap-path-prefix $(TMPDIR)=/a foo.rs -g objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 + ls $(TMPDIR)/*.o && exit 1 || exit 0 + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + rm $(TMPDIR)/foo.dwp + rm $(TMPDIR)/$(call BIN,foo) +# - Debuginfo in `.o` files +# - `.o` and binary refer to remapped `.o` paths which do not exist +# - `.o` deleted +# - `.dwo` never created +# - `.dwp` present packed-remapped-single: $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -C debuginfo=2 \ -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a foo.rs -g objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 + ls $(TMPDIR)/*.o && exit 1 || exit 0 + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + rm $(TMPDIR)/foo.dwp + rm $(TMPDIR)/$(call BIN,foo) packed-crosscrate: packed-crosscrate-split packed-crosscrate-single +# - Debuginfo in `.dwo` files +# - (bar) `.rlib` file created, contains `.dwo` +# - (bar) `.o` deleted +# - (bar) `.dwo` deleted +# - (bar) `.dwp` never created +# - (main) `.o` deleted +# - (main) `.dwo` deleted +# - (main) `.dwp` present packed-crosscrate-split: $(RUSTC) --crate-type lib $(UNSTABLEOPTS) -C split-debuginfo=packed \ -Zsplit-dwarf-kind=split -C debuginfo=2 -g bar.rs ls $(TMPDIR)/*.rlib + ls $(TMPDIR)/*.o && exit 1 || exit 0 ls $(TMPDIR)/*.dwo && exit 1 || exit 0 ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - $(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib -Z unstable-options $(UNSTABLEOPTS) \ + $(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib $(UNSTABLEOPTS) \ -C split-debuginfo=packed -Zsplit-dwarf-kind=split -C debuginfo=2 -g main.rs - rm $(TMPDIR)/*.dwo + ls $(TMPDIR)/*.o && exit 1 || exit 0 + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 rm $(TMPDIR)/main.dwp rm $(TMPDIR)/$(call BIN,main) +# - Debuginfo in `.o` files +# - (bar) `.rlib` file created, contains `.o` +# - (bar) `.o` deleted +# - (bar) `.dwo` never created +# - (bar) `.dwp` never created +# - (main) `.o` deleted +# - (main) `.dwo` never created +# - (main) `.dwp` present packed-crosscrate-single: $(RUSTC) --crate-type lib $(UNSTABLEOPTS) -C split-debuginfo=packed \ -Zsplit-dwarf-kind=single -C debuginfo=2 -g bar.rs ls $(TMPDIR)/*.rlib + ls $(TMPDIR)/*.o && exit 1 || exit 0 ls $(TMPDIR)/*.dwo && exit 1 || exit 0 ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - $(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib -Z unstable-options $(UNSTABLEOPTS) \ + $(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib $(UNSTABLEOPTS) \ -C split-debuginfo=packed -Zsplit-dwarf-kind=single -C debuginfo=2 -g main.rs + ls $(TMPDIR)/*.o && exit 1 || exit 0 ls $(TMPDIR)/*.dwo && exit 1 || exit 0 rm $(TMPDIR)/main.dwp rm $(TMPDIR)/$(call BIN,main) -unpacked: unpacked-split unpacked-single unpacked-remapped-split unpacked-remapped-single +unpacked: unpacked-split unpacked-single unpacked-lto unpacked-remapped unpacked-crosscrate +# - Debuginfo in `.dwo` files +# - `.o` deleted +# - `.dwo` present +# - `.dwp` never created unpacked-split: $(RUSTC) foo.rs -g $(UNSTABLEOPTS) -C split-debuginfo=unpacked -Zsplit-dwarf-kind=split + ls $(TMPDIR)/*.o && exit 1 || exit 0 + rm $(TMPDIR)/*.dwo ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo - rm -rf $(TMPDIR)/*.dwp $(TMPDIR)/*.dwo + rm $(TMPDIR)/$(call BIN,foo) +# - Debuginfo in `.o` files +# - `.o` present +# - `.dwo` never created +# - `.dwp` never created unpacked-single: $(RUSTC) foo.rs -g $(UNSTABLEOPTS) -C split-debuginfo=unpacked -Zsplit-dwarf-kind=single - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + ls $(TMPDIR)/*.o ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + rm $(TMPDIR)/$(call BIN,foo) +unpacked-lto: packed-lto-split packed-lto-single + +# - rmeta file added to rlib, no object files are generated and thus no debuginfo is generated +# - `.o` never created +# - `.dwo` never created +# - `.dwp` never created +unpacked-lto-split: + $(RUSTC) baz.rs -g $(UNSTABLEOPTS) -Csplit-debuginfo=unpacked -Zsplit-dwarf-kind=split \ + --crate-type=rlib -Clinker-plugin-lto + ls $(TMPDIR)/*.o && exit 1 || exit 0 + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + rm $(TMPDIR)/libbaz.rlib + +# - rmeta file added to rlib, no object files are generated and thus no debuginfo is generated +# - `.o` never created +# - `.dwo` never created +# - `.dwp` never created +unpacked-lto-single: + $(RUSTC) baz.rs -g $(UNSTABLEOPTS) -Csplit-debuginfo=unpacked -Zsplit-dwarf-kind=single \ + --crate-type=rlib -Clinker-plugin-lto + ls $(TMPDIR)/*.o && exit 1 || exit 0 + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + rm $(TMPDIR)/libbaz.rlib + +unpacked-remapped: unpacked-remapped-split unpacked-remapped-single + +# - Debuginfo in `.dwo` files +# - `.o` and binary refer to remapped `.dwo` paths which do not exist +# - `.o` deleted +# - `.dwo` present +# - `.dwp` never created unpacked-remapped-split: $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -C debuginfo=2 \ -Z split-dwarf-kind=split --remap-path-prefix $(TMPDIR)=/a foo.rs -g objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 + ls $(TMPDIR)/*.o && exit 1 || exit 0 + rm $(TMPDIR)/*.dwo + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + rm $(TMPDIR)/$(call BIN,foo) +# - Debuginfo in `.o` files +# - `.o` and binary refer to remapped `.o` paths which do not exist +# - `.o` present +# - `.dwo` never created +# - `.dwp` never created unpacked-remapped-single: $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -C debuginfo=2 \ -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a foo.rs -g objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 + ls $(TMPDIR)/*.o + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + rm $(TMPDIR)/$(call BIN,foo) + +unpacked-crosscrate: packed-crosscrate-split packed-crosscrate-single + +# - Debuginfo in `.dwo` files +# - (bar) `.rlib` file created, contains `.dwo` +# - (bar) `.o` deleted +# - (bar) `.dwo` present +# - (bar) `.dwp` never created +# - (main) `.o` deleted +# - (main) `.dwo` present +# - (main) `.dwp` never created +unpacked-crosscrate-split: + $(RUSTC) --crate-type lib $(UNSTABLEOPTS) -C split-debuginfo=unpacked \ + -Zsplit-dwarf-kind=split -C debuginfo=2 -g bar.rs + ls $(TMPDIR)/*.rlib + ls $(TMPDIR)/*.o && exit 1 || exit 0 + ls $(TMPDIR)/*.dwo + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + $(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib $(UNSTABLEOPTS) \ + -C split-debuginfo=unpacked -Zsplit-dwarf-kind=split -C debuginfo=2 -g main.rs + ls $(TMPDIR)/*.o && exit 1 || exit 0 + rm $(TMPDIR)/*.dwo + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + rm $(TMPDIR)/$(call BIN,main) + +# - Debuginfo in `.o` files +# - (bar) `.rlib` file created, contains `.o` +# - (bar) `.o` present +# - (bar) `.dwo` never created +# - (bar) `.dwp` never created +# - (main) `.o` present +# - (main) `.dwo` never created +# - (main) `.dwp` never created +unpacked-crosscrate-single: + $(RUSTC) --crate-type lib $(UNSTABLEOPTS) -C split-debuginfo=unpacked \ + -Zsplit-dwarf-kind=single -C debuginfo=2 -g bar.rs + ls $(TMPDIR)/*.rlib + ls $(TMPDIR)/*.o + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + $(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib $(UNSTABLEOPTS) \ + -C split-debuginfo=unpacked -Zsplit-dwarf-kind=single -C debuginfo=2 -g main.rs + ls $(TMPDIR)/*.o + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + rm $(TMPDIR)/$(call BIN,main) endif endif diff --git a/src/test/run-make-fulldeps/split-debuginfo/baz.rs b/src/test/run-make-fulldeps/split-debuginfo/baz.rs new file mode 100644 index 000000000000..8b1a393741c9 --- /dev/null +++ b/src/test/run-make-fulldeps/split-debuginfo/baz.rs @@ -0,0 +1 @@ +// empty diff --git a/src/test/rustdoc-gui/code-tags.goml b/src/test/rustdoc-gui/code-tags.goml index 837a2c1d57f5..94c1a6525aaa 100644 --- a/src/test/rustdoc-gui/code-tags.goml +++ b/src/test/rustdoc-gui/code-tags.goml @@ -1,4 +1,8 @@ // This test ensures that items and documentation code blocks are wrapped in

+
+// We need to disable this check because `implementors/test_docs/trait.AnotherOne.js`
+// doesn't exist.
+fail-on-request-error: false
 goto: "file://" + |DOC_PATH| + "/test_docs/fn.foo.html"
 size: (1080, 600)
 // There should be four doc codeblocks.
diff --git a/src/test/rustdoc-gui/docblock-code-block-line-number.goml b/src/test/rustdoc-gui/docblock-code-block-line-number.goml
index 911ee34be94e..fec21ad35c3e 100644
--- a/src/test/rustdoc-gui/docblock-code-block-line-number.goml
+++ b/src/test/rustdoc-gui/docblock-code-block-line-number.goml
@@ -30,10 +30,10 @@ wait-for: "#settings"
 assert-css: ("#settings", {"display": "block"})
 
 // Then, click the toggle button.
-click: "input#line-numbers + .slider"
+click: "input#line-numbers"
 wait-for: 100 // wait-for-false does not exist
 assert-false: "pre.example-line-numbers"
 
 // Finally, turn it on again.
-click: "input#line-numbers + .slider"
+click: "input#line-numbers"
 wait-for: "pre.example-line-numbers"
diff --git a/src/test/rustdoc-gui/item-decl-colors.goml b/src/test/rustdoc-gui/item-decl-colors.goml
index ce688287a743..9a46f256056f 100644
--- a/src/test/rustdoc-gui/item-decl-colors.goml
+++ b/src/test/rustdoc-gui/item-decl-colors.goml
@@ -1,4 +1,9 @@
 // This test ensures that the color of the items in the type decl are working as expected.
+
+// We need to disable this check because `implementors/test_docs/trait.TraitWithoutGenerics.js`
+// doesn't exist.
+fail-on-request-error: false
+
 define-function: (
     "check-colors",
     (
diff --git a/src/test/rustdoc-gui/no-docblock.goml b/src/test/rustdoc-gui/no-docblock.goml
index 2366a60f5c61..17a955064d73 100644
--- a/src/test/rustdoc-gui/no-docblock.goml
+++ b/src/test/rustdoc-gui/no-docblock.goml
@@ -1,4 +1,9 @@
 // This test checks that there are margins applied to methods with no docblocks.
+
+// We need to disable this check because `implementors/test_docs/trait.TraitWithNoDocblock.js`
+// doesn't exist.
+fail-on-request-error: false
+
 goto: "file://" + |DOC_PATH| + "/test_docs/trait.TraitWithNoDocblocks.html"
 // Check that the two methods are more than 24px apart.
 compare-elements-position-near-false: ("//*[@id='tymethod.first_fn']", "//*[@id='tymethod.second_fn']", {"y": 24})
diff --git a/src/test/rustdoc-gui/notable-trait.goml b/src/test/rustdoc-gui/notable-trait.goml
index efe0cb15f08a..4c3943d88583 100644
--- a/src/test/rustdoc-gui/notable-trait.goml
+++ b/src/test/rustdoc-gui/notable-trait.goml
@@ -22,25 +22,26 @@ assert-position: (
 )
 assert-position: (
     "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
-    {"x": 951},
+    {"x": 955},
 )
-// The tooltip should be beside the `i`
+// The tooltip should be below the `i`
+// Also, clicking the tooltip should bring its text into the DOM
+assert-count: ("//*[@class='notable popover']", 0)
 click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
+assert-count: ("//*[@class='notable popover']", 1)
 compare-elements-position-near: (
     "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
-    "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits-tooltiptext force-tooltip']",
-    {"y": 2}
+    "//*[@class='notable popover']",
+    {"y": 30}
 )
 compare-elements-position-false: (
     "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
-    "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits-tooltiptext force-tooltip']",
+    "//*[@class='notable popover']",
     ("x")
 )
-// The docblock should be flush with the border.
-assert-css: (
-    "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits-tooltiptext force-tooltip']/*[@class='docblock']",
-    {"margin-left": "0px"}
-)
+click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
+move-cursor-to: "//h1"
+assert-count: ("//*[@class='notable popover']", 0)
 
 // Now only the `i` should be on the next line.
 size: (1055, 600)
@@ -71,7 +72,7 @@ assert-position: (
 )
 assert-position: (
     "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
-    {"x": 519},
+    {"x": 523},
 )
 
 // Checking on mobile now.
@@ -95,34 +96,126 @@ assert-position: (
 )
 assert-position: (
     "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
-    {"x": 289},
+    {"x": 293},
 )
-// The tooltip should be below `i`
-compare-elements-position-near-false: (
+// The tooltip should STILL be below `i`
+click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
+assert-count: ("//*[@class='notable popover']", 1)
+compare-elements-position-near: (
     "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
-    "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits-tooltiptext force-tooltip']",
-    {"y": 2}
+    "//*[@class='notable popover']",
+    {"y": 30}
 )
 compare-elements-position-false: (
     "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
-    "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits-tooltiptext force-tooltip']",
+    "//*[@class='notable popover']",
     ("x")
 )
-compare-elements-position-near: (
-    "//*[@id='method.create_an_iterator_from_read']/parent::*",
-    "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits-tooltiptext force-tooltip']",
-    {"x": 5}
+assert-position: (
+    "//*[@class='notable popover']",
+    {"x": 0}
 )
-// The docblock should be flush with the border.
-assert-css: (
-    "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits-tooltiptext force-tooltip']/*[@class='docblock']",
-    {"margin-left": "0px"}
+click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
+move-cursor-to: "//h1"
+assert-count: ("//*[@class='notable popover']", 0)
+
+// Now check the colors.
+define-function: (
+    "check-colors",
+    (theme, header_color, content_color, type_color, trait_color),
+    [
+        ("goto", "file://" + |DOC_PATH| + "/test_docs/struct.NotableStructWithLongName.html"),
+        // This is needed to ensure that the text color is computed.
+        ("show-text", true),
+
+        // Setting the theme.
+        ("local-storage", {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}),
+        // We reload the page so the local storage settings are being used.
+        ("reload"),
+
+        ("move-cursor-to", "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"),
+        ("assert-count", (".notable.popover", 1)),
+
+        ("assert-css", (
+             ".notable.popover h3",
+             {"color": |header_color|},
+             ALL,
+        )),
+        ("assert-css", (
+             ".notable.popover pre",
+             {"color": |content_color|},
+             ALL,
+        )),
+        ("assert-css", (
+             ".notable.popover pre a.struct",
+             {"color": |type_color|},
+             ALL,
+        )),
+        ("assert-css", (
+             ".notable.popover pre a.trait",
+             {"color": |trait_color|},
+             ALL,
+        )),
+    ]
 )
 
-// Checking on very small mobile. The `i` should be on its own line.
-size: (365, 600)
-compare-elements-position-false: (
-    "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']",
-    "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
-    ("y", "x"),
+call-function: (
+    "check-colors",
+    {
+        "theme": "ayu",
+        "content_color": "rgb(230, 225, 207)",
+        "header_color": "rgb(255, 255, 255)",
+        "type_color": "rgb(255, 160, 165)",
+        "trait_color": "rgb(57, 175, 215)",
+    },
 )
+
+call-function: (
+    "check-colors",
+    {
+        "theme": "dark",
+        "content_color": "rgb(221, 221, 221)",
+        "header_color": "rgb(221, 221, 221)",
+        "type_color": "rgb(45, 191, 184)",
+        "trait_color": "rgb(183, 140, 242)",
+    },
+)
+
+call-function: (
+    "check-colors",
+    {
+        "theme": "light",
+        "content_color": "rgb(0, 0, 0)",
+        "header_color": "rgb(0, 0, 0)",
+        "type_color": "rgb(173, 55, 138)",
+        "trait_color": "rgb(110, 79, 201)",
+    },
+)
+
+reload:
+
+// Check that pressing escape works
+click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
+move-cursor-to: "//*[@class='notable popover']"
+assert-count: ("//*[@class='notable popover']", 1)
+press-key: "Escape"
+assert-count: ("//*[@class='notable popover']", 0)
+
+// Check that clicking outside works.
+click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
+assert-count: ("//*[@class='notable popover']", 1)
+click: ".search-input"
+assert-count: ("//*[@class='notable popover']", 0)
+
+// Check that pressing tab over and over works.
+click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
+move-cursor-to: "//*[@class='notable popover']"
+assert-count: ("//*[@class='notable popover']", 1)
+press-key: "Tab"
+press-key: "Tab"
+press-key: "Tab"
+press-key: "Tab"
+press-key: "Tab"
+press-key: "Tab"
+press-key: "Tab"
+assert-count: ("//*[@class='notable popover']", 0)
diff --git a/src/test/rustdoc-gui/search-no-result.goml b/src/test/rustdoc-gui/search-no-result.goml
new file mode 100644
index 000000000000..b88be32c94a4
--- /dev/null
+++ b/src/test/rustdoc-gui/search-no-result.goml
@@ -0,0 +1,36 @@
+// The goal of this test is to check the color of the "no result" links.
+goto: "file://" + |DOC_PATH| + "/lib2/index.html?search=sdkfskjfsdks"
+show-text: true
+
+define-function: (
+    "check-no-result",
+    (theme, link, link_hover),
+    [
+        // Changing theme.
+        ("local-storage", {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}),
+        ("reload"),
+        ("wait-for", "#results"),
+        ("assert", ".search-failed.active"),
+        ("assert-css", ("#results a", {"color": |link|}, ALL)),
+        ("move-cursor-to", "#results a"),
+        ("assert-css", ("#results a:hover", {"color": |link_hover|})),
+        // Moving the cursor to some other place to not create issues with next function run.
+        ("move-cursor-to", ".search-input"),
+    ]
+)
+
+call-function: ("check-no-result", {
+    "theme": "ayu",
+    "link": "rgb(57, 175, 215)",
+    "link_hover": "rgb(57, 175, 215)",
+})
+call-function: ("check-no-result", {
+    "theme": "dark",
+    "link": "rgb(210, 153, 29)",
+    "link_hover": "rgb(210, 153, 29)",
+})
+call-function: ("check-no-result", {
+    "theme": "light",
+    "link": "rgb(56, 115, 173)",
+    "link_hover": "rgb(56, 115, 173)",
+})
diff --git a/src/test/rustdoc-gui/search-result-display.goml b/src/test/rustdoc-gui/search-result-display.goml
index b8abd9f90624..fa349c872ae2 100644
--- a/src/test/rustdoc-gui/search-result-display.goml
+++ b/src/test/rustdoc-gui/search-result-display.goml
@@ -35,3 +35,43 @@ assert-css: ("#crate-search", {"width": "527px"})
 assert-css: (".search-results-title", {"height": "44px", "width": "640px"})
 // And we check that the `