Merge from rustc

This commit is contained in:
The Miri Cronjob Bot 2024-12-31 05:12:50 +00:00
commit e898da11d2
495 changed files with 5342 additions and 1962 deletions

View file

@ -182,9 +182,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.94"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7"
checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
dependencies = [
"backtrace",
]
@ -367,7 +367,7 @@ dependencies = [
name = "cargo-miri"
version = "0.1.0"
dependencies = [
"cargo_metadata",
"cargo_metadata 0.18.1",
"directories",
"rustc-build-sysroot",
"rustc_tools_util",
@ -399,15 +399,29 @@ dependencies = [
"thiserror 1.0.69",
]
[[package]]
name = "cargo_metadata"
version = "0.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8769706aad5d996120af43197bf46ef6ad0fda35216b4505f926a365a232d924"
dependencies = [
"camino",
"cargo-platform",
"semver",
"serde",
"serde_json",
"thiserror 2.0.9",
]
[[package]]
name = "cargotest2"
version = "0.1.0"
[[package]]
name = "cc"
version = "1.2.5"
version = "1.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c31a0499c1dc64f458ad13872de75c0eb7e3fdb0e67964610c914b034fc5956e"
checksum = "8d6dbb628b8f8555f86d0323c2eb39e3ec81901f4b83e091db8a6a76d316a333"
dependencies = [
"shlex",
]
@ -519,7 +533,7 @@ dependencies = [
"heck 0.5.0",
"proc-macro2",
"quote",
"syn 2.0.90",
"syn 2.0.93",
]
[[package]]
@ -533,7 +547,7 @@ name = "clippy"
version = "0.1.85"
dependencies = [
"anstream",
"cargo_metadata",
"cargo_metadata 0.18.1",
"clippy_config",
"clippy_lints",
"clippy_utils",
@ -550,7 +564,7 @@ dependencies = [
"rustc_tools_util",
"serde",
"serde_json",
"syn 2.0.90",
"syn 2.0.93",
"tempfile",
"termize",
"tokio",
@ -589,7 +603,7 @@ name = "clippy_lints"
version = "0.1.85"
dependencies = [
"arrayvec",
"cargo_metadata",
"cargo_metadata 0.18.1",
"clippy_config",
"clippy_utils",
"itertools",
@ -660,7 +674,7 @@ dependencies = [
"nom",
"proc-macro2",
"quote",
"syn 2.0.90",
"syn 2.0.93",
]
[[package]]
@ -885,7 +899,7 @@ dependencies = [
"proc-macro2",
"quote",
"strsim",
"syn 2.0.90",
"syn 2.0.93",
]
[[package]]
@ -896,7 +910,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
dependencies = [
"darling_core",
"quote",
"syn 2.0.90",
"syn 2.0.93",
]
[[package]]
@ -933,7 +947,7 @@ checksum = "62d671cc41a825ebabc75757b62d3d168c577f9149b2d49ece1dad1f72119d25"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn 2.0.93",
]
[[package]]
@ -954,7 +968,7 @@ dependencies = [
"darling",
"proc-macro2",
"quote",
"syn 2.0.90",
"syn 2.0.93",
]
[[package]]
@ -964,7 +978,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
dependencies = [
"derive_builder_core",
"syn 2.0.90",
"syn 2.0.93",
]
[[package]]
@ -976,7 +990,7 @@ dependencies = [
"darling",
"proc-macro2",
"quote",
"syn 2.0.90",
"syn 2.0.93",
]
[[package]]
@ -1054,7 +1068,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn 2.0.93",
]
[[package]]
@ -1351,7 +1365,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn 2.0.93",
]
[[package]]
@ -1389,7 +1403,7 @@ name = "generate-copyright"
version = "0.1.0"
dependencies = [
"anyhow",
"cargo_metadata",
"cargo_metadata 0.18.1",
"rinja",
"serde",
"serde_json",
@ -1463,9 +1477,9 @@ dependencies = [
[[package]]
name = "glob"
version = "0.3.1"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
[[package]]
name = "globset"
@ -1585,7 +1599,7 @@ dependencies = [
"markup5ever",
"proc-macro2",
"quote",
"syn 2.0.90",
"syn 2.0.93",
]
[[package]]
@ -1774,7 +1788,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn 2.0.93",
]
[[package]]
@ -2730,7 +2744,7 @@ dependencies = [
"pest_meta",
"proc-macro2",
"quote",
"syn 2.0.90",
"syn 2.0.93",
]
[[package]]
@ -2952,9 +2966,9 @@ checksum = "07589615d719a60c8dd8a4622e7946465dfef20d1a428f969e3443e7386d5f45"
[[package]]
name = "quote"
version = "1.0.37"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
dependencies = [
"proc-macro2",
]
@ -3140,7 +3154,7 @@ dependencies = [
"rinja_parser",
"rustc-hash 2.1.0",
"serde",
"syn 2.0.90",
"syn 2.0.93",
]
[[package]]
@ -3780,7 +3794,7 @@ dependencies = [
"fluent-syntax",
"proc-macro2",
"quote",
"syn 2.0.90",
"syn 2.0.93",
"unic-langid",
]
@ -3915,7 +3929,7 @@ version = "0.0.0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn 2.0.93",
]
[[package]]
@ -4063,7 +4077,7 @@ version = "0.0.0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn 2.0.93",
"synstructure",
]
@ -4651,7 +4665,7 @@ version = "0.0.0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn 2.0.93",
"synstructure",
]
@ -4740,7 +4754,7 @@ dependencies = [
"proc-macro2",
"quote",
"serde",
"syn 2.0.90",
"syn 2.0.93",
]
[[package]]
@ -4750,7 +4764,7 @@ dependencies = [
"annotate-snippets 0.9.2",
"anyhow",
"bytecount",
"cargo_metadata",
"cargo_metadata 0.18.1",
"clap",
"clap-cargo",
"diff",
@ -4787,9 +4801,9 @@ dependencies = [
[[package]]
name = "rustversion"
version = "1.0.18"
version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248"
checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
[[package]]
name = "ruzstd"
@ -4862,22 +4876,22 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.216"
version = "1.0.217"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e"
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.216"
version = "1.0.217"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e"
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn 2.0.93",
]
[[package]]
@ -5135,9 +5149,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.90"
version = "2.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
checksum = "9c786062daee0d6db1132800e623df74274a0a87322d8e183338e01b3d98d058"
dependencies = [
"proc-macro2",
"quote",
@ -5152,7 +5166,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn 2.0.93",
]
[[package]]
@ -5294,7 +5308,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn 2.0.93",
]
[[package]]
@ -5305,7 +5319,7 @@ checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn 2.0.93",
]
[[package]]
@ -5344,7 +5358,7 @@ name = "tidy"
version = "0.1.0"
dependencies = [
"build_helper",
"cargo_metadata",
"cargo_metadata 0.19.1",
"fluent-syntax",
"ignore",
"miropt-test-tools",
@ -5506,7 +5520,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn 2.0.93",
]
[[package]]
@ -5622,7 +5636,7 @@ dependencies = [
"anyhow",
"bstr",
"cargo-platform",
"cargo_metadata",
"cargo_metadata 0.18.1",
"color-eyre",
"colored",
"comma",
@ -5677,15 +5691,15 @@ checksum = "1ed7f4237ba393424195053097c1516bd4590dc82b84f2f97c5c69e12704555b"
dependencies = [
"proc-macro-hack",
"quote",
"syn 2.0.90",
"syn 2.0.93",
"unic-langid-impl",
]
[[package]]
name = "unicase"
version = "2.8.0"
version = "2.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df"
checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539"
[[package]]
name = "unicode-ident"
@ -5883,7 +5897,7 @@ dependencies = [
"log",
"proc-macro2",
"quote",
"syn 2.0.90",
"syn 2.0.93",
"wasm-bindgen-shared",
]
@ -5905,7 +5919,7 @@ checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn 2.0.93",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@ -6087,7 +6101,7 @@ dependencies = [
"rayon",
"serde",
"serde_json",
"syn 2.0.90",
"syn 2.0.93",
"windows-metadata",
]
@ -6120,7 +6134,7 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn 2.0.93",
]
[[package]]
@ -6131,7 +6145,7 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn 2.0.93",
]
[[package]]
@ -6410,7 +6424,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn 2.0.93",
"synstructure",
]
@ -6432,7 +6446,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn 2.0.93",
]
[[package]]
@ -6452,7 +6466,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn 2.0.93",
"synstructure",
]
@ -6475,5 +6489,5 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn 2.0.93",
]

View file

@ -213,7 +213,9 @@ impl<'a> State<'a> {
fn print_expr_call(&mut self, func: &ast::Expr, args: &[P<ast::Expr>], fixup: FixupContext) {
let needs_paren = match func.kind {
ast::ExprKind::Field(..) => true,
// In order to call a named field, needs parens: `(self.fun)()`
// But not for an unnamed field: `self.0()`
ast::ExprKind::Field(_, name) => !name.is_numeric(),
_ => func.precedence() < ExprPrecedence::Unambiguous,
};
@ -245,19 +247,21 @@ impl<'a> State<'a> {
base_args: &[P<ast::Expr>],
fixup: FixupContext,
) {
// Unlike in `print_expr_call`, no change to fixup here because
// The fixup here is different than in `print_expr_call` because
// statement boundaries never occur in front of a `.` (or `?`) token.
//
// match () { _ => f }.method();
// Needs parens:
//
// (loop { break x; })();
//
// Does not need parens:
//
// loop { break x; }.method();
//
// Parenthesizing only for precedence and not with regard to statement
// boundaries, `$receiver.method()` can be parsed back as a statement
// containing an expression if and only if `$receiver` can be parsed as
// a statement containing an expression.
self.print_expr_cond_paren(
receiver,
receiver.precedence() < ExprPrecedence::Unambiguous,
fixup,
fixup.leftmost_subexpression_with_dot(),
);
self.word(".");
@ -503,7 +507,7 @@ impl<'a> State<'a> {
self.print_expr_cond_paren(
expr,
expr.precedence() < ExprPrecedence::Unambiguous,
fixup,
fixup.leftmost_subexpression_with_dot(),
);
self.word_nbsp(".match");
}
@ -567,7 +571,7 @@ impl<'a> State<'a> {
self.print_expr_cond_paren(
expr,
expr.precedence() < ExprPrecedence::Unambiguous,
fixup,
fixup.leftmost_subexpression_with_dot(),
);
self.word(".await");
}
@ -606,7 +610,7 @@ impl<'a> State<'a> {
self.print_expr_cond_paren(
expr,
expr.precedence() < ExprPrecedence::Unambiguous,
fixup,
fixup.leftmost_subexpression_with_dot(),
);
self.word(".");
self.print_ident(*ident);
@ -763,7 +767,11 @@ impl<'a> State<'a> {
}
}
ast::ExprKind::Try(e) => {
self.print_expr_cond_paren(e, e.precedence() < ExprPrecedence::Unambiguous, fixup);
self.print_expr_cond_paren(
e,
e.precedence() < ExprPrecedence::Unambiguous,
fixup.leftmost_subexpression_with_dot(),
);
self.word("?")
}
ast::ExprKind::TryBlock(blk) => {

View file

@ -138,6 +138,20 @@ impl FixupContext {
}
}
/// Transform this fixup into the one that should apply when printing a
/// leftmost subexpression followed by a `.` or `?` token, which confer
/// different statement boundary rules compared to other leftmost
/// subexpressions.
pub(crate) fn leftmost_subexpression_with_dot(self) -> Self {
FixupContext {
stmt: self.stmt || self.leftmost_subexpression_in_stmt,
leftmost_subexpression_in_stmt: false,
match_arm: self.match_arm || self.leftmost_subexpression_in_match_arm,
leftmost_subexpression_in_match_arm: false,
..self
}
}
/// Transform this fixup into the one that should apply when printing any
/// subexpression that is neither a leftmost subexpression nor surrounded in
/// delimiters.

View file

@ -8,11 +8,12 @@ use rustc_middle::ty::TyCtxt;
pub use super::borrow_set::{BorrowData, BorrowSet, TwoPhaseActivation};
pub use super::constraints::OutlivesConstraint;
pub use super::dataflow::{BorrowIndex, Borrows, calculate_borrows_out_of_scope_at_location};
pub use super::facts::{AllFacts as PoloniusInput, PoloniusRegionVid, RustcFacts};
pub use super::location::{LocationTable, RichLocation};
pub use super::nll::PoloniusOutput;
pub use super::place_ext::PlaceExt;
pub use super::places_conflict::{PlaceConflictBias, places_conflict};
pub use super::polonius::legacy::{
AllFacts as PoloniusInput, LocationTable, PoloniusOutput, PoloniusRegionVid, RichLocation,
RustcFacts,
};
pub use super::region_infer::RegionInferenceContext;
/// Options determining the output behavior of [`get_body_with_borrowck_facts`].

View file

@ -1,6 +1,9 @@
//! Borrow checker diagnostics.
use std::collections::BTreeMap;
use rustc_abi::{FieldIdx, VariantIdx};
use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::{Applicability, Diag, MultiSpan};
use rustc_hir::def::{CtorKind, Namespace};
use rustc_hir::{self as hir, CoroutineKind, LangItem};
@ -17,10 +20,10 @@ use rustc_middle::mir::{
use rustc_middle::ty::print::Print;
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
use rustc_middle::util::{CallDesugaringKind, call_kind};
use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult};
use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult, MoveOutIndex};
use rustc_span::def_id::LocalDefId;
use rustc_span::source_map::Spanned;
use rustc_span::{DUMMY_SP, Span, Symbol, sym};
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::{
@ -68,6 +71,126 @@ pub(super) struct DescribePlaceOpt {
pub(super) struct IncludingTupleField(pub(super) bool);
enum BufferedDiag<'infcx> {
Error(Diag<'infcx>),
NonError(Diag<'infcx, ()>),
}
impl<'infcx> BufferedDiag<'infcx> {
fn sort_span(&self) -> Span {
match self {
BufferedDiag::Error(diag) => diag.sort_span,
BufferedDiag::NonError(diag) => diag.sort_span,
}
}
}
#[derive(Default)]
pub(crate) struct BorrowckDiagnosticsBuffer<'infcx, 'tcx> {
/// This field keeps track of move errors that are to be reported for given move indices.
///
/// There are situations where many errors can be reported for a single move out (see
/// #53807) and we want only the best of those errors.
///
/// The `report_use_of_moved_or_uninitialized` function checks this map and replaces the
/// diagnostic (if there is one) if the `Place` of the error being reported is a prefix of
/// the `Place` of the previous most diagnostic. This happens instead of buffering the
/// error. Once all move errors have been reported, any diagnostics in this map are added
/// to the buffer to be emitted.
///
/// `BTreeMap` is used to preserve the order of insertions when iterating. This is necessary
/// when errors in the map are being re-added to the error buffer so that errors with the
/// same primary span come out in a consistent order.
buffered_move_errors: BTreeMap<Vec<MoveOutIndex>, (PlaceRef<'tcx>, Diag<'infcx>)>,
buffered_mut_errors: FxIndexMap<Span, (Diag<'infcx>, usize)>,
/// Buffer of diagnostics to be reported. A mixture of error and non-error diagnostics.
buffered_diags: Vec<BufferedDiag<'infcx>>,
}
impl<'infcx, 'tcx> BorrowckDiagnosticsBuffer<'infcx, 'tcx> {
pub(crate) fn buffer_non_error(&mut self, diag: Diag<'infcx, ()>) {
self.buffered_diags.push(BufferedDiag::NonError(diag));
}
}
impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
pub(crate) fn buffer_error(&mut self, diag: Diag<'infcx>) {
self.diags_buffer.buffered_diags.push(BufferedDiag::Error(diag));
}
pub(crate) fn buffer_non_error(&mut self, diag: Diag<'infcx, ()>) {
self.diags_buffer.buffer_non_error(diag);
}
pub(crate) fn buffer_move_error(
&mut self,
move_out_indices: Vec<MoveOutIndex>,
place_and_err: (PlaceRef<'tcx>, Diag<'infcx>),
) -> bool {
if let Some((_, diag)) =
self.diags_buffer.buffered_move_errors.insert(move_out_indices, place_and_err)
{
// Cancel the old diagnostic so we don't ICE
diag.cancel();
false
} else {
true
}
}
pub(crate) fn get_buffered_mut_error(&mut self, span: Span) -> Option<(Diag<'infcx>, usize)> {
// FIXME(#120456) - is `swap_remove` correct?
self.diags_buffer.buffered_mut_errors.swap_remove(&span)
}
pub(crate) fn buffer_mut_error(&mut self, span: Span, diag: Diag<'infcx>, count: usize) {
self.diags_buffer.buffered_mut_errors.insert(span, (diag, count));
}
pub(crate) fn emit_errors(&mut self) -> Option<ErrorGuaranteed> {
let mut res = self.infcx.tainted_by_errors();
// Buffer any move errors that we collected and de-duplicated.
for (_, (_, diag)) in std::mem::take(&mut self.diags_buffer.buffered_move_errors) {
// We have already set tainted for this error, so just buffer it.
self.buffer_error(diag);
}
for (_, (mut diag, count)) in std::mem::take(&mut self.diags_buffer.buffered_mut_errors) {
if count > 10 {
#[allow(rustc::diagnostic_outside_of_impl)]
#[allow(rustc::untranslatable_diagnostic)]
diag.note(format!("...and {} other attempted mutable borrows", count - 10));
}
self.buffer_error(diag);
}
if !self.diags_buffer.buffered_diags.is_empty() {
self.diags_buffer.buffered_diags.sort_by_key(|buffered_diag| buffered_diag.sort_span());
for buffered_diag in self.diags_buffer.buffered_diags.drain(..) {
match buffered_diag {
BufferedDiag::Error(diag) => res = Some(diag.emit()),
BufferedDiag::NonError(diag) => diag.emit(),
}
}
}
res
}
pub(crate) fn has_buffered_diags(&self) -> bool {
self.diags_buffer.buffered_diags.is_empty()
}
pub(crate) fn has_move_error(
&self,
move_out_indices: &[MoveOutIndex],
) -> Option<&(PlaceRef<'tcx>, Diag<'infcx>)> {
self.diags_buffer.buffered_move_errors.get(move_out_indices)
}
}
impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
/// Adds a suggestion when a closure is invoked twice with a moved variable or when a closure
/// is moved after being invoked.

View file

@ -31,7 +31,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
diag: &mut Diag<'_>,
) {
// We look at all the locals. Why locals? Because it's the best thing
// I could think of that's correlated with the *instantiated* higer-ranked
// I could think of that's correlated with the *instantiated* higher-ranked
// binder for calls, since we don't really store those anywhere else.
for ty in self.body.local_decls.iter().map(|local| local.ty) {
if !ty.has_opaque_types() {

View file

@ -16,14 +16,12 @@
// tidy-alphabetical-end
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::marker::PhantomData;
use std::ops::Deref;
use rustc_abi::FieldIdx;
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_data_structures::graph::dominators::Dominators;
use rustc_errors::Diag;
use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
use rustc_index::bit_set::{BitSet, MixedBitSet};
@ -41,7 +39,7 @@ use rustc_mir_dataflow::impls::{
EverInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces,
};
use rustc_mir_dataflow::move_paths::{
InitIndex, InitLocation, LookupResult, MoveData, MoveOutIndex, MovePathIndex,
InitIndex, InitLocation, LookupResult, MoveData, MovePathIndex,
};
use rustc_mir_dataflow::{Analysis, EntryStates, Results, ResultsVisitor, visit_results};
use rustc_session::lint::builtin::UNUSED_MUT;
@ -52,12 +50,13 @@ use tracing::{debug, instrument};
use crate::borrow_set::{BorrowData, BorrowSet};
use crate::consumers::{BodyWithBorrowckFacts, ConsumerOptions};
use crate::dataflow::{BorrowIndex, Borrowck, BorrowckDomain, Borrows};
use crate::diagnostics::{AccessKind, IllegalMoveOriginKind, MoveError, RegionName};
use crate::location::LocationTable;
use crate::nll::PoloniusOutput;
use crate::diagnostics::{
AccessKind, BorrowckDiagnosticsBuffer, IllegalMoveOriginKind, MoveError, RegionName,
};
use crate::path_utils::*;
use crate::place_ext::PlaceExt;
use crate::places_conflict::{PlaceConflictBias, places_conflict};
use crate::polonius::legacy::{LocationTable, PoloniusOutput};
use crate::prefixes::PrefixSet;
use crate::region_infer::RegionInferenceContext;
use crate::renumber::RegionCtxt;
@ -69,8 +68,6 @@ mod constraints;
mod dataflow;
mod def_use;
mod diagnostics;
mod facts;
mod location;
mod member_constraints;
mod nll;
mod path_utils;
@ -120,11 +117,10 @@ fn mir_borrowck(tcx: TyCtxt<'_>, def: LocalDefId) -> &BorrowCheckResult<'_> {
return tcx.arena.alloc(result);
}
let promoted: &IndexSlice<_, _> = &promoted.borrow();
let opt_closure_req = do_mir_borrowck(tcx, input_body, promoted, None).0;
let borrowck_result = do_mir_borrowck(tcx, input_body, &*promoted.borrow(), None).0;
debug!("mir_borrowck done");
tcx.arena.alloc(opt_closure_req)
tcx.arena.alloc(borrowck_result)
}
/// Perform the actual borrow checking.
@ -215,14 +211,21 @@ fn do_mir_borrowck<'tcx>(
consumer_options,
);
// Dump MIR results into a file, if that is enabled. This let us
// Dump MIR results into a file, if that is enabled. This lets us
// write unit-tests, as well as helping with debugging.
nll::dump_nll_mir(&infcx, body, &regioncx, &opt_closure_req, &borrow_set);
// We also have a `#[rustc_regions]` annotation that causes us to dump
// information.
let diags = &mut diags::BorrowckDiags::new();
nll::dump_annotation(&infcx, body, &regioncx, &opt_closure_req, &opaque_type_values, diags);
let diags_buffer = &mut BorrowckDiagnosticsBuffer::default();
nll::dump_annotation(
&infcx,
body,
&regioncx,
&opt_closure_req,
&opaque_type_values,
diags_buffer,
);
let movable_coroutine =
// The first argument is the coroutine type passed by value
@ -261,7 +264,7 @@ fn do_mir_borrowck<'tcx>(
next_region_name: RefCell::new(1),
polonius_output: None,
move_errors: Vec::new(),
diags,
diags_buffer,
};
MoveVisitor { ctxt: &mut promoted_mbcx }.visit_body(promoted_body);
promoted_mbcx.report_move_errors();
@ -300,7 +303,7 @@ fn do_mir_borrowck<'tcx>(
next_region_name: RefCell::new(1),
polonius_output,
move_errors: Vec::new(),
diags,
diags_buffer,
};
// Compute and report region errors, if any.
@ -570,7 +573,7 @@ struct MirBorrowckCtxt<'a, 'infcx, 'tcx> {
/// Results of Polonius analysis.
polonius_output: Option<Box<PoloniusOutput>>,
diags: &'a mut diags::BorrowckDiags<'infcx, 'tcx>,
diags_buffer: &'a mut BorrowckDiagnosticsBuffer<'infcx, 'tcx>,
move_errors: Vec<MoveError<'tcx>>,
}
@ -2403,146 +2406,6 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
}
}
mod diags {
use rustc_errors::ErrorGuaranteed;
use super::*;
enum BufferedDiag<'infcx> {
Error(Diag<'infcx>),
NonError(Diag<'infcx, ()>),
}
impl<'infcx> BufferedDiag<'infcx> {
fn sort_span(&self) -> Span {
match self {
BufferedDiag::Error(diag) => diag.sort_span,
BufferedDiag::NonError(diag) => diag.sort_span,
}
}
}
pub(crate) struct BorrowckDiags<'infcx, 'tcx> {
/// This field keeps track of move errors that are to be reported for given move indices.
///
/// There are situations where many errors can be reported for a single move out (see
/// #53807) and we want only the best of those errors.
///
/// The `report_use_of_moved_or_uninitialized` function checks this map and replaces the
/// diagnostic (if there is one) if the `Place` of the error being reported is a prefix of
/// the `Place` of the previous most diagnostic. This happens instead of buffering the
/// error. Once all move errors have been reported, any diagnostics in this map are added
/// to the buffer to be emitted.
///
/// `BTreeMap` is used to preserve the order of insertions when iterating. This is necessary
/// when errors in the map are being re-added to the error buffer so that errors with the
/// same primary span come out in a consistent order.
buffered_move_errors: BTreeMap<Vec<MoveOutIndex>, (PlaceRef<'tcx>, Diag<'infcx>)>,
buffered_mut_errors: FxIndexMap<Span, (Diag<'infcx>, usize)>,
/// Buffer of diagnostics to be reported. A mixture of error and non-error diagnostics.
buffered_diags: Vec<BufferedDiag<'infcx>>,
}
impl<'infcx, 'tcx> BorrowckDiags<'infcx, 'tcx> {
pub(crate) fn new() -> Self {
BorrowckDiags {
buffered_move_errors: BTreeMap::new(),
buffered_mut_errors: Default::default(),
buffered_diags: Default::default(),
}
}
pub(crate) fn buffer_error(&mut self, diag: Diag<'infcx>) {
self.buffered_diags.push(BufferedDiag::Error(diag));
}
pub(crate) fn buffer_non_error(&mut self, diag: Diag<'infcx, ()>) {
self.buffered_diags.push(BufferedDiag::NonError(diag));
}
}
impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
pub(crate) fn buffer_error(&mut self, diag: Diag<'infcx>) {
self.diags.buffer_error(diag);
}
pub(crate) fn buffer_non_error(&mut self, diag: Diag<'infcx, ()>) {
self.diags.buffer_non_error(diag);
}
pub(crate) fn buffer_move_error(
&mut self,
move_out_indices: Vec<MoveOutIndex>,
place_and_err: (PlaceRef<'tcx>, Diag<'infcx>),
) -> bool {
if let Some((_, diag)) =
self.diags.buffered_move_errors.insert(move_out_indices, place_and_err)
{
// Cancel the old diagnostic so we don't ICE
diag.cancel();
false
} else {
true
}
}
pub(crate) fn get_buffered_mut_error(
&mut self,
span: Span,
) -> Option<(Diag<'infcx>, usize)> {
// FIXME(#120456) - is `swap_remove` correct?
self.diags.buffered_mut_errors.swap_remove(&span)
}
pub(crate) fn buffer_mut_error(&mut self, span: Span, diag: Diag<'infcx>, count: usize) {
self.diags.buffered_mut_errors.insert(span, (diag, count));
}
pub(crate) fn emit_errors(&mut self) -> Option<ErrorGuaranteed> {
let mut res = self.infcx.tainted_by_errors();
// Buffer any move errors that we collected and de-duplicated.
for (_, (_, diag)) in std::mem::take(&mut self.diags.buffered_move_errors) {
// We have already set tainted for this error, so just buffer it.
self.diags.buffer_error(diag);
}
for (_, (mut diag, count)) in std::mem::take(&mut self.diags.buffered_mut_errors) {
if count > 10 {
#[allow(rustc::diagnostic_outside_of_impl)]
#[allow(rustc::untranslatable_diagnostic)]
diag.note(format!("...and {} other attempted mutable borrows", count - 10));
}
self.diags.buffer_error(diag);
}
if !self.diags.buffered_diags.is_empty() {
self.diags.buffered_diags.sort_by_key(|buffered_diag| buffered_diag.sort_span());
for buffered_diag in self.diags.buffered_diags.drain(..) {
match buffered_diag {
BufferedDiag::Error(diag) => res = Some(diag.emit()),
BufferedDiag::NonError(diag) => diag.emit(),
}
}
}
res
}
pub(crate) fn has_buffered_diags(&self) -> bool {
self.diags.buffered_diags.is_empty()
}
pub(crate) fn has_move_error(
&self,
move_out_indices: &[MoveOutIndex],
) -> Option<&(PlaceRef<'tcx>, Diag<'infcx>)> {
self.diags.buffered_move_errors.get(move_out_indices)
}
}
}
/// The degree of overlap between 2 places for borrow-checking.
enum Overlap {
/// The places might partially overlap - in this case, we give

View file

@ -26,17 +26,14 @@ use tracing::{debug, instrument};
use crate::borrow_set::BorrowSet;
use crate::consumers::ConsumerOptions;
use crate::diagnostics::RegionErrors;
use crate::facts::{AllFacts, AllFactsExt, RustcFacts};
use crate::location::LocationTable;
use crate::diagnostics::{BorrowckDiagnosticsBuffer, RegionErrors};
use crate::polonius::LocalizedOutlivesConstraintSet;
use crate::polonius::legacy::{AllFacts, AllFactsExt, LocationTable, PoloniusOutput};
use crate::region_infer::RegionInferenceContext;
use crate::type_check::{self, MirTypeckResults};
use crate::universal_regions::UniversalRegions;
use crate::{BorrowckInferCtxt, polonius, renumber};
pub type PoloniusOutput = Output<RustcFacts>;
/// The output of `nll::compute_regions`. This includes the computed `RegionInferenceContext`, any
/// closure requirements to propagate, and any generated errors.
pub(crate) struct NllOutput<'tcx> {
@ -100,24 +97,28 @@ pub(crate) fn compute_regions<'a, 'tcx>(
let elements = Rc::new(DenseLocationMap::new(body));
// Run the MIR type-checker.
let MirTypeckResults { constraints, universal_region_relations, opaque_type_values } =
type_check::type_check(
infcx,
body,
promoted,
universal_regions,
location_table,
borrow_set,
&mut all_facts,
flow_inits,
move_data,
Rc::clone(&elements),
);
let MirTypeckResults {
constraints,
universal_region_relations,
opaque_type_values,
mut polonius_context,
} = type_check::type_check(
infcx,
body,
promoted,
universal_regions,
location_table,
borrow_set,
&mut all_facts,
flow_inits,
move_data,
Rc::clone(&elements),
);
// Create the region inference context, taking ownership of the
// region inference data that was contained in `infcx`, and the
// base constraints generated by the type-check.
let var_origins = infcx.get_region_var_origins();
let var_infos = infcx.get_region_var_infos();
// If requested, emit legacy polonius facts.
polonius::legacy::emit_facts(
@ -133,7 +134,7 @@ pub(crate) fn compute_regions<'a, 'tcx>(
let mut regioncx = RegionInferenceContext::new(
infcx,
var_origins,
var_infos,
constraints,
universal_region_relations,
elements,
@ -141,12 +142,9 @@ pub(crate) fn compute_regions<'a, 'tcx>(
// If requested for `-Zpolonius=next`, convert NLL constraints to localized outlives
// constraints.
let localized_outlives_constraints =
if infcx.tcx.sess.opts.unstable_opts.polonius.is_next_enabled() {
Some(polonius::create_localized_constraints(&mut regioncx, body))
} else {
None
};
let localized_outlives_constraints = polonius_context
.as_mut()
.map(|polonius_context| polonius_context.create_localized_constraints(&mut regioncx, body));
// If requested: dump NLL facts, and run legacy polonius analysis.
let polonius_output = all_facts.as_ref().and_then(|all_facts| {
@ -300,7 +298,7 @@ pub(super) fn dump_annotation<'tcx, 'infcx>(
regioncx: &RegionInferenceContext<'tcx>,
closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
opaque_type_values: &FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>>,
diags: &mut crate::diags::BorrowckDiags<'infcx, 'tcx>,
diagnostics_buffer: &mut BorrowckDiagnosticsBuffer<'infcx, 'tcx>,
) {
let tcx = infcx.tcx;
let base_def_id = tcx.typeck_root_def_id(body.source.def_id());
@ -346,7 +344,7 @@ pub(super) fn dump_annotation<'tcx, 'infcx>(
err.note(format!("Inferred opaque type values:\n{opaque_type_values:#?}"));
}
diags.buffer_non_error(err);
diagnostics_buffer.buffer_non_error(err);
}
fn for_each_region_constraint<'tcx>(

View file

@ -4,9 +4,8 @@ use rustc_middle::ty::TyCtxt;
use rustc_mir_dataflow::move_paths::{LookupResult, MoveData};
use tracing::debug;
use super::{AllFacts, LocationIndex, LocationTable};
use crate::def_use::{self, DefUse};
use crate::facts::AllFacts;
use crate::location::{LocationIndex, LocationTable};
use crate::universal_regions::UniversalRegions;
/// Emit polonius facts for variable defs, uses, drops, and path accesses.

View file

@ -4,18 +4,20 @@ use std::fs::{self, File};
use std::io::Write;
use std::path::Path;
use polonius_engine::{AllFacts as PoloniusFacts, Atom};
use polonius_engine::{AllFacts as PoloniusFacts, Atom, Output};
use rustc_macros::extension;
use rustc_middle::mir::Local;
use rustc_middle::ty::{RegionVid, TyCtxt};
use rustc_mir_dataflow::move_paths::MovePathIndex;
use super::{LocationIndex, LocationTable};
use crate::BorrowIndex;
use crate::location::{LocationIndex, LocationTable};
#[derive(Copy, Clone, Debug)]
pub struct RustcFacts;
pub type PoloniusOutput = Output<RustcFacts>;
rustc_index::newtype_index! {
/// A (kinda) newtype of `RegionVid` so we can implement `Atom` on it.
#[orderable]
@ -246,6 +248,6 @@ impl FactCell for RegionVid {
impl FactCell for LocationIndex {
fn to_string(&self, location_table: &LocationTable) -> String {
format!("{:?}", location_table.to_location(*self))
format!("{:?}", location_table.to_rich_location(*self))
}
}

View file

@ -9,9 +9,8 @@ use rustc_middle::mir::{
use rustc_middle::ty::TyCtxt;
use tracing::debug;
use super::{AllFacts, LocationTable};
use crate::borrow_set::BorrowSet;
use crate::facts::AllFacts;
use crate::location::LocationTable;
use crate::path_utils::*;
use crate::{
AccessDepth, Activation, ArtificialField, BorrowIndex, Deep, LocalMutationIsAllowed, Read,

View file

@ -6,9 +6,8 @@ use rustc_middle::mir::{
use rustc_middle::ty::TyCtxt;
use tracing::debug;
use super::{AllFacts, LocationTable};
use crate::borrow_set::BorrowSet;
use crate::facts::AllFacts;
use crate::location::LocationTable;
use crate::places_conflict;
/// Emit `loan_killed_at` and `cfg_edge` facts at the same time.

View file

@ -65,7 +65,7 @@ impl LocationTable {
LocationIndex::from_usize(start_index + statement_index * 2 + 1)
}
pub fn to_location(&self, index: LocationIndex) -> RichLocation {
pub fn to_rich_location(&self, index: LocationIndex) -> RichLocation {
let point_index = index.index();
// Find the basic block. We have a vector with the
@ -97,6 +97,13 @@ impl LocationTable {
RichLocation::Mid(Location { block, statement_index })
}
}
pub fn to_location(&self, index: LocationIndex) -> Location {
match self.to_rich_location(index) {
RichLocation::Start(location) => location,
RichLocation::Mid(location) => location,
}
}
}
impl LocationIndex {

View file

@ -13,8 +13,6 @@ use tracing::debug;
use crate::borrow_set::BorrowSet;
use crate::constraints::OutlivesConstraint;
use crate::facts::{AllFacts, PoloniusRegionVid};
use crate::location::LocationTable;
use crate::type_check::MirTypeckRegionConstraints;
use crate::type_check::free_region_relations::UniversalRegionRelations;
use crate::universal_regions::UniversalRegions;
@ -22,6 +20,10 @@ use crate::universal_regions::UniversalRegions;
mod accesses;
mod loan_invalidations;
mod loan_kills;
mod location;
pub use self::location::*;
mod facts;
pub use self::facts::*;
/// When requested, emit most of the facts needed by polonius:
/// - moves and assignments

View file

@ -0,0 +1,336 @@
use std::collections::BTreeMap;
use rustc_index::bit_set::SparseBitMatrix;
use rustc_index::interval::SparseIntervalMatrix;
use rustc_middle::mir::{Body, Location};
use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeVisitable};
use rustc_mir_dataflow::points::PointIndex;
use super::{
ConstraintDirection, LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet,
PoloniusContext,
};
use crate::region_infer::values::LivenessValues;
use crate::universal_regions::UniversalRegions;
impl PoloniusContext {
/// Record the variance of each region contained within the given value.
pub(crate) fn record_live_region_variance<'tcx>(
&mut self,
tcx: TyCtxt<'tcx>,
universal_regions: &UniversalRegions<'tcx>,
value: impl TypeVisitable<TyCtxt<'tcx>> + Relate<TyCtxt<'tcx>>,
) {
let mut extractor = VarianceExtractor {
tcx,
ambient_variance: ty::Variance::Covariant,
directions: &mut self.live_region_variances,
universal_regions,
};
extractor.relate(value, value).expect("Can't have a type error relating to itself");
}
/// Unlike NLLs, in polonius we traverse the cfg to look for regions live across an edge, so we
/// need to transpose the "points where each region is live" matrix to a "live regions per point"
/// matrix.
// FIXME: avoid this conversion by always storing liveness data in this shape in the rest of
// borrowck.
pub(crate) fn record_live_regions_per_point(
&mut self,
num_regions: usize,
points_per_live_region: &SparseIntervalMatrix<RegionVid, PointIndex>,
) {
let mut live_regions_per_point = SparseBitMatrix::new(num_regions);
for region in points_per_live_region.rows() {
for point in points_per_live_region.row(region).unwrap().iter() {
live_regions_per_point.insert(point, region);
}
}
self.live_regions = Some(live_regions_per_point);
}
}
/// Propagate loans throughout the CFG: for each statement in the MIR, create localized outlives
/// constraints for loans that are propagated to the next statements.
pub(super) fn create_liveness_constraints<'tcx>(
body: &Body<'tcx>,
liveness: &LivenessValues,
live_regions: &SparseBitMatrix<PointIndex, RegionVid>,
live_region_variances: &BTreeMap<RegionVid, ConstraintDirection>,
universal_regions: &UniversalRegions<'tcx>,
localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet,
) {
for (block, bb) in body.basic_blocks.iter_enumerated() {
let statement_count = bb.statements.len();
for statement_index in 0..=statement_count {
let current_location = Location { block, statement_index };
let current_point = liveness.point_from_location(current_location);
if statement_index < statement_count {
// Intra-block edges, straight line constraints from each point to its successor
// within the same block.
let next_location = Location { block, statement_index: statement_index + 1 };
let next_point = liveness.point_from_location(next_location);
propagate_loans_between_points(
current_point,
next_point,
live_regions,
live_region_variances,
universal_regions,
localized_outlives_constraints,
);
} else {
// Inter-block edges, from the block's terminator to each successor block's entry
// point.
for successor_block in bb.terminator().successors() {
let next_location = Location { block: successor_block, statement_index: 0 };
let next_point = liveness.point_from_location(next_location);
propagate_loans_between_points(
current_point,
next_point,
live_regions,
live_region_variances,
universal_regions,
localized_outlives_constraints,
);
}
}
}
}
}
/// Propagate loans within a region between two points in the CFG, if that region is live at both
/// the source and target points.
fn propagate_loans_between_points(
current_point: PointIndex,
next_point: PointIndex,
live_regions: &SparseBitMatrix<PointIndex, RegionVid>,
live_region_variances: &BTreeMap<RegionVid, ConstraintDirection>,
universal_regions: &UniversalRegions<'_>,
localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet,
) {
// Universal regions are semantically live at all points.
// Note: we always have universal regions but they're not always (or often) involved in the
// subset graph. For now, we emit all their edges unconditionally, but some of these subgraphs
// will be disconnected from the rest of the graph and thus, unnecessary.
//
// FIXME: only emit the edges of universal regions that existential regions can reach.
for region in universal_regions.universal_regions_iter() {
localized_outlives_constraints.push(LocalizedOutlivesConstraint {
source: region,
from: current_point,
target: region,
to: next_point,
});
}
let Some(current_live_regions) = live_regions.row(current_point) else {
// There are no constraints to add: there are no live regions at the current point.
return;
};
let Some(next_live_regions) = live_regions.row(next_point) else {
// There are no constraints to add: there are no live regions at the next point.
return;
};
for region in next_live_regions.iter() {
if !current_live_regions.contains(region) {
continue;
}
// `region` is indeed live at both points, add a constraint between them, according to
// variance.
if let Some(&direction) = live_region_variances.get(&region) {
add_liveness_constraint(
region,
current_point,
next_point,
direction,
localized_outlives_constraints,
);
} else {
// Note: there currently are cases related to promoted and const generics, where we
// don't yet have variance information (possibly about temporary regions created when
// typeck sanitizes the promoteds). Until that is done, we conservatively fallback to
// maximizing reachability by adding a bidirectional edge here. This will not limit
// traversal whatsoever, and thus propagate liveness when needed.
//
// FIXME: add the missing variance information and remove this fallback bidirectional
// edge.
let fallback = ConstraintDirection::Bidirectional;
add_liveness_constraint(
region,
current_point,
next_point,
fallback,
localized_outlives_constraints,
);
}
}
}
/// Adds `LocalizedOutlivesConstraint`s between two connected points, according to the given edge
/// direction.
fn add_liveness_constraint(
region: RegionVid,
current_point: PointIndex,
next_point: PointIndex,
direction: ConstraintDirection,
localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet,
) {
match direction {
ConstraintDirection::Forward => {
// Covariant cases: loans flow in the regular direction, from the current point to the
// next point.
localized_outlives_constraints.push(LocalizedOutlivesConstraint {
source: region,
from: current_point,
target: region,
to: next_point,
});
}
ConstraintDirection::Backward => {
// Contravariant cases: loans flow in the inverse direction, from the next point to the
// current point.
localized_outlives_constraints.push(LocalizedOutlivesConstraint {
source: region,
from: next_point,
target: region,
to: current_point,
});
}
ConstraintDirection::Bidirectional => {
// For invariant cases, loans can flow in both directions: we add both edges.
localized_outlives_constraints.push(LocalizedOutlivesConstraint {
source: region,
from: current_point,
target: region,
to: next_point,
});
localized_outlives_constraints.push(LocalizedOutlivesConstraint {
source: region,
from: next_point,
target: region,
to: current_point,
});
}
}
}
/// Extracts variances for regions contained within types. Follows the same structure as
/// `rustc_infer`'s `Generalizer`: we try to relate a type with itself to track and extract the
/// variances of regions.
struct VarianceExtractor<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
ambient_variance: ty::Variance,
directions: &'a mut BTreeMap<RegionVid, ConstraintDirection>,
universal_regions: &'a UniversalRegions<'tcx>,
}
impl<'tcx> VarianceExtractor<'_, 'tcx> {
fn record_variance(&mut self, region: ty::Region<'tcx>, variance: ty::Variance) {
// We're only interested in the variance of vars and free regions.
//
// Note: even if we currently bail for two cases of unexpected region kinds here, missing
// variance data is not a soundness problem: the regions with missing variance will still be
// present in the constraint graph as they are live, and liveness edges construction has a
// fallback for this case.
//
// FIXME: that being said, we need to investigate these cases better to not ignore regions
// in general.
if region.is_bound() {
// We ignore these because they cannot be turned into the vids we need.
return;
}
if region.is_erased() {
// These cannot be turned into a vid either, and we also ignore them: the fact that they
// show up here looks like either an issue upstream or a combination with unexpectedly
// continuing compilation too far when we're in a tainted by errors situation.
//
// FIXME: investigate the `generic_const_exprs` test that triggers this issue,
// `ui/const-generics/generic_const_exprs/issue-97047-ice-2.rs`
return;
}
let direction = match variance {
ty::Covariant => ConstraintDirection::Forward,
ty::Contravariant => ConstraintDirection::Backward,
ty::Invariant => ConstraintDirection::Bidirectional,
ty::Bivariant => {
// We don't add edges for bivariant cases.
return;
}
};
let region = self.universal_regions.to_region_vid(region);
self.directions
.entry(region)
.and_modify(|entry| {
// If there's already a recorded direction for this region, we combine the two:
// - combining the same direction is idempotent
// - combining different directions is trivially bidirectional
if entry != &direction {
*entry = ConstraintDirection::Bidirectional;
}
})
.or_insert(direction);
}
}
impl<'tcx> TypeRelation<TyCtxt<'tcx>> for VarianceExtractor<'_, 'tcx> {
fn cx(&self) -> TyCtxt<'tcx> {
self.tcx
}
fn relate_with_variance<T: Relate<TyCtxt<'tcx>>>(
&mut self,
variance: ty::Variance,
_info: ty::VarianceDiagInfo<TyCtxt<'tcx>>,
a: T,
b: T,
) -> RelateResult<'tcx, T> {
let old_ambient_variance = self.ambient_variance;
self.ambient_variance = self.ambient_variance.xform(variance);
let r = self.relate(a, b)?;
self.ambient_variance = old_ambient_variance;
Ok(r)
}
fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
assert_eq!(a, b); // we are misusing TypeRelation here; both LHS and RHS ought to be ==
relate::structurally_relate_tys(self, a, b)
}
fn regions(
&mut self,
a: ty::Region<'tcx>,
b: ty::Region<'tcx>,
) -> RelateResult<'tcx, ty::Region<'tcx>> {
assert_eq!(a, b); // we are misusing TypeRelation here; both LHS and RHS ought to be ==
self.record_variance(a, self.ambient_variance);
Ok(a)
}
fn consts(
&mut self,
a: ty::Const<'tcx>,
b: ty::Const<'tcx>,
) -> RelateResult<'tcx, ty::Const<'tcx>> {
assert_eq!(a, b); // we are misusing TypeRelation here; both LHS and RHS ought to be ==
relate::structurally_relate_consts(self, a, b)
}
fn binders<T>(
&mut self,
a: ty::Binder<'tcx, T>,
_: ty::Binder<'tcx, T>,
) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
where
T: Relate<TyCtxt<'tcx>>,
{
self.relate(a.skip_binder(), a.skip_binder())?;
Ok(a)
}
}

View file

@ -34,45 +34,88 @@
//!
mod constraints;
pub(crate) use constraints::*;
mod dump;
pub(crate) use dump::dump_polonius_mir;
pub(crate) mod legacy;
mod liveness_constraints;
use std::collections::BTreeMap;
use rustc_index::bit_set::SparseBitMatrix;
use rustc_middle::mir::{Body, Location};
use rustc_middle::ty::RegionVid;
use rustc_mir_dataflow::points::PointIndex;
pub(crate) use self::constraints::*;
pub(crate) use self::dump::dump_polonius_mir;
use self::liveness_constraints::create_liveness_constraints;
use crate::RegionInferenceContext;
use crate::constraints::OutlivesConstraint;
use crate::region_infer::values::LivenessValues;
use crate::type_check::Locations;
use crate::universal_regions::UniversalRegions;
/// Creates a constraint set for `-Zpolonius=next` by:
/// - converting NLL typeck constraints to be localized
/// - encoding liveness constraints
pub(crate) fn create_localized_constraints<'tcx>(
regioncx: &mut RegionInferenceContext<'tcx>,
body: &Body<'tcx>,
) -> LocalizedOutlivesConstraintSet {
let mut localized_outlives_constraints = LocalizedOutlivesConstraintSet::default();
convert_typeck_constraints(
body,
regioncx.liveness_constraints(),
regioncx.outlives_constraints(),
&mut localized_outlives_constraints,
);
create_liveness_constraints(
body,
regioncx.liveness_constraints(),
regioncx.universal_regions(),
&mut localized_outlives_constraints,
);
/// This struct holds the data needed to create the Polonius localized constraints.
pub(crate) struct PoloniusContext {
/// The set of regions that are live at a given point in the CFG, used to create localized
/// outlives constraints between regions that are live at connected points in the CFG.
live_regions: Option<SparseBitMatrix<PointIndex, RegionVid>>,
// FIXME: here, we can trace loan reachability in the constraint graph and record this as loan
// liveness for the next step in the chain, the NLL loan scope and active loans computations.
/// The expected edge direction per live region: the kind of directed edge we'll create as
/// liveness constraints depends on the variance of types with respect to each contained region.
live_region_variances: BTreeMap<RegionVid, ConstraintDirection>,
}
localized_outlives_constraints
/// The direction a constraint can flow into. Used to create liveness constraints according to
/// variance.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum ConstraintDirection {
/// For covariant cases, we add a forward edge `O at P1 -> O at P2`.
Forward,
/// For contravariant cases, we add a backward edge `O at P2 -> O at P1`
Backward,
/// For invariant cases, we add both the forward and backward edges `O at P1 <-> O at P2`.
Bidirectional,
}
impl PoloniusContext {
pub(crate) fn new() -> PoloniusContext {
Self { live_region_variances: BTreeMap::new(), live_regions: None }
}
/// Creates a constraint set for `-Zpolonius=next` by:
/// - converting NLL typeck constraints to be localized
/// - encoding liveness constraints
pub(crate) fn create_localized_constraints<'tcx>(
&self,
regioncx: &RegionInferenceContext<'tcx>,
body: &Body<'tcx>,
) -> LocalizedOutlivesConstraintSet {
let mut localized_outlives_constraints = LocalizedOutlivesConstraintSet::default();
convert_typeck_constraints(
body,
regioncx.liveness_constraints(),
regioncx.outlives_constraints(),
&mut localized_outlives_constraints,
);
let live_regions = self.live_regions.as_ref().expect(
"live regions per-point data should have been created at the end of MIR typeck",
);
create_liveness_constraints(
body,
regioncx.liveness_constraints(),
live_regions,
&self.live_region_variances,
regioncx.universal_regions(),
&mut localized_outlives_constraints,
);
// FIXME: here, we can trace loan reachability in the constraint graph and record this as loan
// liveness for the next step in the chain, the NLL loan scope and active loans computations.
localized_outlives_constraints
}
}
/// Propagate loans throughout the subset graph at a given point (with some subtleties around the
@ -109,72 +152,3 @@ fn convert_typeck_constraints<'tcx>(
}
}
}
/// Propagate loans throughout the CFG: for each statement in the MIR, create localized outlives
/// constraints for loans that are propagated to the next statements.
pub(crate) fn create_liveness_constraints<'tcx>(
body: &Body<'tcx>,
liveness: &LivenessValues,
universal_regions: &UniversalRegions<'tcx>,
localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet,
) {
for (block, bb) in body.basic_blocks.iter_enumerated() {
let statement_count = bb.statements.len();
for statement_index in 0..=statement_count {
let current_location = Location { block, statement_index };
let current_point = liveness.point_from_location(current_location);
if statement_index < statement_count {
// Intra-block edges, straight line constraints from each point to its successor
// within the same block.
let next_location = Location { block, statement_index: statement_index + 1 };
let next_point = liveness.point_from_location(next_location);
propagate_loans_between_points(
current_point,
next_point,
liveness,
universal_regions,
localized_outlives_constraints,
);
} else {
// Inter-block edges, from the block's terminator to each successor block's entry
// point.
for successor_block in bb.terminator().successors() {
let next_location = Location { block: successor_block, statement_index: 0 };
let next_point = liveness.point_from_location(next_location);
propagate_loans_between_points(
current_point,
next_point,
liveness,
universal_regions,
localized_outlives_constraints,
);
}
}
}
}
}
/// Propagate loans within a region between two points in the CFG, if that region is live at both
/// the source and target points.
fn propagate_loans_between_points(
current_point: PointIndex,
next_point: PointIndex,
_liveness: &LivenessValues,
universal_regions: &UniversalRegions<'_>,
localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet,
) {
// Universal regions are semantically live at all points.
// Note: we always have universal regions but they're not always (or often) involved in the
// subset graph. For now, we emit all their edges unconditionally, but some of these subgraphs
// will be disconnected from the rest of the graph and thus, unnecessary.
// FIXME: only emit the edges of universal regions that existential regions can reach.
for region in universal_regions.universal_regions_iter() {
localized_outlives_constraints.push(LocalizedOutlivesConstraint {
source: region,
from: current_point,
target: region,
to: next_point,
});
}
}

View file

@ -30,7 +30,7 @@ use crate::constraints::{ConstraintSccIndex, OutlivesConstraint, OutlivesConstra
use crate::dataflow::BorrowIndex;
use crate::diagnostics::{RegionErrorKind, RegionErrors, UniverseInfo};
use crate::member_constraints::{MemberConstraintSet, NllMemberConstraintIndex};
use crate::nll::PoloniusOutput;
use crate::polonius::legacy::PoloniusOutput;
use crate::region_infer::reverse_sccs::ReverseSccGraph;
use crate::region_infer::values::{LivenessValues, RegionElement, RegionValues, ToElementIndex};
use crate::type_check::free_region_relations::UniversalRegionRelations;
@ -1950,8 +1950,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
target_test: impl Fn(RegionVid) -> bool,
) -> (BlameConstraint<'tcx>, Vec<ExtraConstraintInfo>) {
// Find all paths
let (path, target_region) =
self.find_constraint_paths_between_regions(from_region, target_test).unwrap();
let (path, target_region) = self
.find_constraint_paths_between_regions(from_region, target_test)
.or_else(|| {
self.find_constraint_paths_between_regions(from_region, |r| {
self.cannot_name_placeholder(from_region, r)
})
})
.unwrap();
debug!(
"path={:#?}",
path.iter()

View file

@ -99,6 +99,14 @@ impl LivenessValues {
}
}
/// Returns the liveness matrix of points where each region is live. Panics if the liveness
/// values have been created without any per-point data (that is, for promoteds).
pub(crate) fn points(&self) -> &SparseIntervalMatrix<RegionVid, PointIndex> {
self.points
.as_ref()
.expect("this `LivenessValues` wasn't created using `with_specific_points`")
}
/// Iterate through each region that has a value in this set.
pub(crate) fn regions(&self) -> impl Iterator<Item = RegionVid> + '_ {
self.points.as_ref().expect("use with_specific_points").rows()

View file

@ -3,6 +3,7 @@ use rustc_data_structures::fx::FxHashSet;
use rustc_middle::mir::visit::{TyContext, Visitor};
use rustc_middle::mir::{Body, Local, Location, SourceInfo};
use rustc_middle::span_bug;
use rustc_middle::ty::relate::Relate;
use rustc_middle::ty::visit::TypeVisitable;
use rustc_middle::ty::{GenericArgsRef, Region, RegionVid, Ty, TyCtxt};
use rustc_mir_dataflow::ResultsCursor;
@ -13,6 +14,7 @@ use tracing::debug;
use super::TypeChecker;
use crate::constraints::OutlivesConstraintSet;
use crate::polonius::PoloniusContext;
use crate::region_infer::values::LivenessValues;
use crate::universal_regions::UniversalRegions;
@ -56,7 +58,13 @@ pub(super) fn generate<'a, 'tcx>(
// Mark regions that should be live where they appear within rvalues or within a call: like
// args, regions, and types.
record_regular_live_regions(typeck.tcx(), &mut typeck.constraints.liveness_constraints, body);
record_regular_live_regions(
typeck.tcx(),
&mut typeck.constraints.liveness_constraints,
&typeck.universal_regions,
&mut typeck.polonius_context,
body,
);
}
// The purpose of `compute_relevant_live_locals` is to define the subset of `Local`
@ -130,9 +138,12 @@ fn regions_that_outlive_free_regions<'tcx>(
fn record_regular_live_regions<'tcx>(
tcx: TyCtxt<'tcx>,
liveness_constraints: &mut LivenessValues,
universal_regions: &UniversalRegions<'tcx>,
polonius_context: &mut Option<PoloniusContext>,
body: &Body<'tcx>,
) {
let mut visitor = LiveVariablesVisitor { tcx, liveness_constraints };
let mut visitor =
LiveVariablesVisitor { tcx, liveness_constraints, universal_regions, polonius_context };
for (bb, data) in body.basic_blocks.iter_enumerated() {
visitor.visit_basic_block_data(bb, data);
}
@ -142,6 +153,8 @@ fn record_regular_live_regions<'tcx>(
struct LiveVariablesVisitor<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
liveness_constraints: &'a mut LivenessValues,
universal_regions: &'a UniversalRegions<'tcx>,
polonius_context: &'a mut Option<PoloniusContext>,
}
impl<'a, 'tcx> Visitor<'tcx> for LiveVariablesVisitor<'a, 'tcx> {
@ -184,12 +197,17 @@ impl<'a, 'tcx> LiveVariablesVisitor<'a, 'tcx> {
/// all regions appearing in the type of `value` must be live at `location`.
fn record_regions_live_at<T>(&mut self, value: T, location: Location)
where
T: TypeVisitable<TyCtxt<'tcx>>,
T: TypeVisitable<TyCtxt<'tcx>> + Relate<TyCtxt<'tcx>>,
{
debug!("record_regions_live_at(value={:?}, location={:?})", value, location);
self.tcx.for_each_free_region(&value, |live_region| {
let live_region_vid = live_region.as_var();
self.liveness_constraints.add_location(live_region_vid, location);
});
// When using `-Zpolonius=next`, we record the variance of each live region.
if let Some(polonius_context) = self.polonius_context {
polonius_context.record_live_region_variance(self.tcx, self.universal_regions, value);
}
}
}

View file

@ -5,6 +5,7 @@ use rustc_infer::infer::canonical::QueryRegionConstraints;
use rustc_infer::infer::outlives::for_liveness;
use rustc_middle::mir::{BasicBlock, Body, ConstraintCategory, Local, Location};
use rustc_middle::traits::query::DropckOutlivesResult;
use rustc_middle::ty::relate::Relate;
use rustc_middle::ty::{Ty, TyCtxt, TypeVisitable, TypeVisitableExt};
use rustc_mir_dataflow::ResultsCursor;
use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
@ -14,7 +15,6 @@ use rustc_span::DUMMY_SP;
use rustc_trait_selection::traits::query::type_op::{DropckOutlives, TypeOp, TypeOpOutput};
use tracing::debug;
use crate::location::RichLocation;
use crate::polonius;
use crate::region_infer::values::{self, LiveLoans};
use crate::type_check::liveness::local_use_map::LocalUseMap;
@ -210,7 +210,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> {
///
/// Add facts for all locals with free regions, since regions may outlive
/// the function body only at certain nodes in the CFG.
fn add_extra_drop_facts(&mut self, relevant_live_locals: &[Local]) -> Option<()> {
fn add_extra_drop_facts(&mut self, relevant_live_locals: &[Local]) {
// This collect is more necessary than immediately apparent
// because these facts go into `add_drop_live_facts_for()`,
// which also writes to `all_facts`, and so this is genuinely
@ -220,41 +220,30 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> {
// and probably maybe plausibly does not need to go back in.
// It may be necessary to just pick out the parts of
// `add_drop_live_facts_for()` that make sense.
let Some(facts) = self.cx.typeck.all_facts.as_ref() else { return };
let facts_to_add: Vec<_> = {
let drop_used = &self.cx.typeck.all_facts.as_ref()?.var_dropped_at;
let relevant_live_locals: FxIndexSet<_> =
relevant_live_locals.iter().copied().collect();
drop_used
facts
.var_dropped_at
.iter()
.filter_map(|(local, location_index)| {
let local_ty = self.cx.body.local_decls[*local].ty;
if relevant_live_locals.contains(local) || !local_ty.has_free_regions() {
.filter_map(|&(local, location_index)| {
let local_ty = self.cx.body.local_decls[local].ty;
if relevant_live_locals.contains(&local) || !local_ty.has_free_regions() {
return None;
}
let location = match self.cx.typeck.location_table.to_location(*location_index)
{
RichLocation::Start(l) => l,
RichLocation::Mid(l) => l,
};
Some((*local, local_ty, location))
let location = self.cx.typeck.location_table.to_location(location_index);
Some((local, local_ty, location))
})
.collect()
};
// FIXME: these locations seem to have a special meaning (e.g. everywhere, at the end,
// ...), but I don't know which one. Please help me rename it to something descriptive!
// Also, if this IntervalSet is used in many places, it maybe should have a newtype'd
// name with a description of what it means for future mortals passing by.
let locations = IntervalSet::new(self.cx.elements.num_points());
let live_at = IntervalSet::new(self.cx.elements.num_points());
for (local, local_ty, location) in facts_to_add {
self.cx.add_drop_live_facts_for(local, local_ty, &[location], &locations);
self.cx.add_drop_live_facts_for(local, local_ty, &[location], &live_at);
}
Some(())
}
/// Clear the value of fields that are "per local variable".
@ -532,11 +521,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
/// Stores the result that all regions in `value` are live for the
/// points `live_at`.
fn add_use_live_facts_for(
&mut self,
value: impl TypeVisitable<TyCtxt<'tcx>>,
live_at: &IntervalSet<PointIndex>,
) {
fn add_use_live_facts_for(&mut self, value: Ty<'tcx>, live_at: &IntervalSet<PointIndex>) {
debug!("add_use_live_facts_for(value={:?})", value);
Self::make_all_regions_live(self.elements, self.typeck, value, live_at);
}
@ -603,7 +588,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
fn make_all_regions_live(
elements: &DenseLocationMap,
typeck: &mut TypeChecker<'_, 'tcx>,
value: impl TypeVisitable<TyCtxt<'tcx>>,
value: impl TypeVisitable<TyCtxt<'tcx>> + Relate<TyCtxt<'tcx>>,
live_at: &IntervalSet<PointIndex>,
) {
debug!("make_all_regions_live(value={:?})", value);
@ -621,6 +606,15 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
typeck.constraints.liveness_constraints.add_points(live_region_vid, live_at);
},
});
// When using `-Zpolonius=next`, we record the variance of each live region.
if let Some(polonius_context) = typeck.polonius_context {
polonius_context.record_live_region_variance(
typeck.infcx.tcx,
typeck.universal_regions,
value,
);
}
}
fn compute_drop_data(typeck: &TypeChecker<'_, 'tcx>, dropped_ty: Ty<'tcx>) -> DropData<'tcx> {

View file

@ -47,9 +47,9 @@ use tracing::{debug, instrument, trace};
use crate::borrow_set::BorrowSet;
use crate::constraints::{OutlivesConstraint, OutlivesConstraintSet};
use crate::diagnostics::UniverseInfo;
use crate::facts::AllFacts;
use crate::location::LocationTable;
use crate::member_constraints::MemberConstraintSet;
use crate::polonius::PoloniusContext;
use crate::polonius::legacy::{AllFacts, LocationTable};
use crate::region_infer::TypeTest;
use crate::region_infer::values::{LivenessValues, PlaceholderIndex, PlaceholderIndices};
use crate::renumber::RegionCtxt;
@ -140,8 +140,20 @@ pub(crate) fn type_check<'a, 'tcx>(
&mut constraints,
);
let pre_obligations = infcx.take_registered_region_obligations();
assert!(
pre_obligations.is_empty(),
"there should be no incoming region obligations = {pre_obligations:#?}",
);
debug!(?normalized_inputs_and_output);
let mut polonius_context = if infcx.tcx.sess.opts.unstable_opts.polonius.is_next_enabled() {
Some(PoloniusContext::new())
} else {
None
};
let mut typeck = TypeChecker {
infcx,
last_span: body.span,
@ -156,6 +168,7 @@ pub(crate) fn type_check<'a, 'tcx>(
all_facts,
borrow_set,
constraints: &mut constraints,
polonius_context: &mut polonius_context,
};
typeck.check_user_type_annotations();
@ -172,7 +185,18 @@ pub(crate) fn type_check<'a, 'tcx>(
let opaque_type_values =
opaque_types::take_opaques_and_register_member_constraints(&mut typeck);
MirTypeckResults { constraints, universal_region_relations, opaque_type_values }
if let Some(polonius_context) = typeck.polonius_context.as_mut() {
let num_regions = infcx.num_region_vars();
let points_per_live_region = typeck.constraints.liveness_constraints.points();
polonius_context.record_live_regions_per_point(num_regions, points_per_live_region);
}
MirTypeckResults {
constraints,
universal_region_relations,
opaque_type_values,
polonius_context,
}
}
#[track_caller]
@ -540,6 +564,8 @@ struct TypeChecker<'a, 'tcx> {
all_facts: &'a mut Option<AllFacts>,
borrow_set: &'a BorrowSet<'tcx>,
constraints: &'a mut MirTypeckRegionConstraints<'tcx>,
/// When using `-Zpolonius=next`, the helper data used to create polonius constraints.
polonius_context: &'a mut Option<PoloniusContext>,
}
/// Holder struct for passing results from MIR typeck to the rest of the non-lexical regions
@ -548,6 +574,7 @@ pub(crate) struct MirTypeckResults<'tcx> {
pub(crate) constraints: MirTypeckRegionConstraints<'tcx>,
pub(crate) universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
pub(crate) opaque_type_values: FxIndexMap<OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>>,
pub(crate) polonius_context: Option<PoloniusContext>,
}
/// A collection of region constraints that must be satisfied for the

View file

@ -337,7 +337,7 @@ impl<'tcx> UniversalRegions<'tcx> {
self.indices.indices.iter().map(|(&r, &v)| (r, v))
}
/// See `UniversalRegionIndices::to_region_vid`.
/// See [UniversalRegionIndices::to_region_vid].
pub(crate) fn to_region_vid(&self, r: ty::Region<'tcx>) -> RegionVid {
self.indices.to_region_vid(r)
}

View file

@ -16,8 +16,8 @@ index 7165c3e48af..968552ad435 100644
[dependencies]
core = { path = "../core" }
-compiler_builtins = { version = "=0.1.138", features = ['rustc-dep-of-std'] }
+compiler_builtins = { version = "=0.1.138", features = ['rustc-dep-of-std', 'no-f16-f128'] }
-compiler_builtins = { version = "=0.1.140", features = ['rustc-dep-of-std'] }
+compiler_builtins = { version = "=0.1.140", features = ['rustc-dep-of-std', 'no-f16-f128'] }
[dev-dependencies]
rand = { version = "0.8.5", default-features = false, features = ["alloc"] }

View file

@ -189,7 +189,7 @@ pub(crate) fn target_machine_factory(
let reloc_model = to_llvm_relocation_model(sess.relocation_model());
let (opt_level, _) = to_llvm_opt_settings(optlvl);
let use_softfp = if sess.target.arch == "arm" && sess.target.abi == "eabihf" {
let use_softfp = if sess.target.arch == "arm" {
sess.opts.cg.soft_float
} else {
// `validate_commandline_args_with_session_available` has already warned about this being

View file

@ -340,6 +340,37 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
self.const_i32(cache_type),
])
}
sym::carrying_mul_add => {
let (size, signed) = fn_args.type_at(0).int_size_and_signed(self.tcx);
let wide_llty = self.type_ix(size.bits() * 2);
let args = args.as_array().unwrap();
let [a, b, c, d] = args.map(|a| self.intcast(a.immediate(), wide_llty, signed));
let wide = if signed {
let prod = self.unchecked_smul(a, b);
let acc = self.unchecked_sadd(prod, c);
self.unchecked_sadd(acc, d)
} else {
let prod = self.unchecked_umul(a, b);
let acc = self.unchecked_uadd(prod, c);
self.unchecked_uadd(acc, d)
};
let narrow_llty = self.type_ix(size.bits());
let low = self.trunc(wide, narrow_llty);
let bits_const = self.const_uint(wide_llty, size.bits());
// No need for ashr when signed; LLVM changes it to lshr anyway.
let high = self.lshr(wide, bits_const);
// FIXME: could be `trunc nuw`, even for signed.
let high = self.trunc(high, narrow_llty);
let pair_llty = self.type_struct(&[narrow_llty, narrow_llty], false);
let pair = self.const_poison(pair_llty);
let pair = self.insert_value(pair, low, 0);
let pair = self.insert_value(pair, high, 1);
pair
}
sym::ctlz
| sym::ctlz_nonzero
| sym::cttz

View file

@ -17,6 +17,7 @@
#![feature(iter_intersperse)]
#![feature(let_chains)]
#![feature(rustdoc_internals)]
#![feature(slice_as_array)]
#![feature(try_blocks)]
#![warn(unreachable_pub)]
// tidy-alphabetical-end

View file

@ -10,7 +10,7 @@ arrayvec = { version = "0.7", default-features = false }
bitflags = "2.4.1"
# Pinned so `cargo update` bumps don't cause breakage. Please also update the
# `cc` in `rustc_llvm` if you update the `cc` here.
cc = "=1.2.5"
cc = "=1.2.6"
either = "1.5.0"
itertools = "0.12"
pathdiff = "0.2.0"

View file

@ -2,7 +2,7 @@ use std::env;
use std::error::Error;
use std::ffi::OsString;
use std::fs::{self, File};
use std::io::{self, Write};
use std::io::{self, BufWriter, Write};
use std::path::{Path, PathBuf};
use ar_archive_writer::{
@ -509,9 +509,10 @@ impl<'a> ArArchiveBuilder<'a> {
io_error_context("couldn't create a directory for the temp file", err)
})?;
let archive_tmpfile_path = archive_tmpdir.path().join("tmp.a");
let mut archive_tmpfile = File::create_new(&archive_tmpfile_path)
let archive_tmpfile = File::create_new(&archive_tmpfile_path)
.map_err(|err| io_error_context("couldn't create the temp file", err))?;
let mut archive_tmpfile = BufWriter::new(archive_tmpfile);
write_archive_to_stream(
&mut archive_tmpfile,
&entries,
@ -519,6 +520,8 @@ impl<'a> ArArchiveBuilder<'a> {
false,
/* is_ec = */ self.sess.target.arch == "arm64ec",
)?;
archive_tmpfile.flush()?;
drop(archive_tmpfile);
let any_entries = !entries.is_empty();
drop(entries);

View file

@ -75,7 +75,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
// If we're swapping something that's *not* an `OperandValue::Ref`,
// then we can do it directly and avoid the alloca.
// Otherwise, we'll let the fallback MIR body take care of it.
if let sym::typed_swap = name {
if let sym::typed_swap_nonoverlapping = name {
let pointee_ty = fn_args.type_at(0);
let pointee_layout = bx.layout_of(pointee_ty);
if !bx.is_backend_ref(pointee_layout)

View file

@ -382,7 +382,7 @@ pub trait BuilderMethods<'a, 'tcx>:
/// Avoids `alloca`s for Immediates and ScalarPairs.
///
/// FIXME: Maybe do something smarter for Ref types too?
/// For now, the `typed_swap` intrinsic just doesn't call this for those
/// For now, the `typed_swap_nonoverlapping` intrinsic just doesn't call this for those
/// cases (in non-debug), preferring the fallback body instead.
fn typed_place_swap(
&mut self,

View file

@ -15,20 +15,14 @@ fn parent_impl_constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Constness
}
}
/// Checks whether an item is considered to be `const`. If it is a constructor, it is const.
/// If it is an assoc method or function,
/// return if it has a `const` modifier. If it is an intrinsic, report whether said intrinsic
/// has a `rustc_const_{un,}stable` attribute. Otherwise, panic.
/// Checks whether a function-like definition is considered to be `const`.
fn constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Constness {
let node = tcx.hir_node_by_def_id(def_id);
match node {
hir::Node::Ctor(hir::VariantData::Tuple(..))
| hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(..), .. }) => {
hir::Constness::Const
}
hir::Node::ForeignItem(_) => {
// Foreign items cannot be evaluated at compile-time.
hir::Node::Ctor(hir::VariantData::Tuple(..)) => hir::Constness::Const,
hir::Node::ForeignItem(item) if let hir::ForeignItemKind::Fn(..) = item.kind => {
// Foreign functions cannot be evaluated at compile-time.
hir::Constness::NotConst
}
hir::Node::Expr(e) if let hir::ExprKind::Closure(c) = e.kind => c.constness,

View file

@ -883,19 +883,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
.local_to_op(mir::RETURN_PLACE, None)
.expect("return place should always be live");
let dest = self.frame().return_place.clone();
let res = if self.stack().len() == 1 {
// The initializer of constants and statics will get validated separately
// after the constant has been fully evaluated. While we could fall back to the default
// code path, that will cause -Zenforce-validity to cycle on static initializers.
// Reading from a static's memory is not allowed during its evaluation, and will always
// trigger a cycle error. Validation must read from the memory of the current item.
// For Miri this means we do not validate the root frame return value,
// but Miri anyway calls `read_target_isize` on that so separate validation
// is not needed.
self.copy_op_no_dest_validation(&op, &dest)
} else {
self.copy_op_allow_transmute(&op, &dest)
};
let res = self.copy_op_allow_transmute(&op, &dest);
trace!("return value: {:?}", self.dump_place(&dest.into()));
// We delay actually short-circuiting on this error until *after* the stack frame is
// popped, since we want this error to be attributed to the caller, whose type defines

View file

@ -424,8 +424,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let result = self.raw_eq_intrinsic(&args[0], &args[1])?;
self.write_scalar(result, dest)?;
}
sym::typed_swap => {
self.typed_swap_intrinsic(&args[0], &args[1])?;
sym::typed_swap_nonoverlapping => {
self.typed_swap_nonoverlapping_intrinsic(&args[0], &args[1])?;
}
sym::vtable_size => {
@ -638,19 +638,35 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
}
/// Does a *typed* swap of `*left` and `*right`.
fn typed_swap_intrinsic(
fn typed_swap_nonoverlapping_intrinsic(
&mut self,
left: &OpTy<'tcx, <M as Machine<'tcx>>::Provenance>,
right: &OpTy<'tcx, <M as Machine<'tcx>>::Provenance>,
) -> InterpResult<'tcx> {
let left = self.deref_pointer(left)?;
let right = self.deref_pointer(right)?;
debug_assert_eq!(left.layout, right.layout);
assert_eq!(left.layout, right.layout);
assert!(left.layout.is_sized());
let kind = MemoryKind::Stack;
let temp = self.allocate(left.layout, kind)?;
self.copy_op(&left, &temp)?;
self.copy_op(&right, &left)?;
self.copy_op(&temp, &right)?;
self.copy_op(&left, &temp)?; // checks alignment of `left`
// We want to always enforce non-overlapping, even if this is a scalar type.
// Therefore we directly use the underlying `mem_copy` here.
self.mem_copy(right.ptr(), left.ptr(), left.layout.size, /*nonoverlapping*/ true)?;
// This means we also need to do the validation of the value that used to be in `right`
// ourselves. This value is now in `left.` The one that started out in `left` already got
// validated by the copy above.
if M::enforce_validity(self, left.layout) {
self.validate_operand(
&left.clone().into(),
M::enforce_validity_recursively(self, left.layout),
/*reset_provenance_and_padding*/ true,
)?;
}
self.copy_op(&temp, &right)?; // checks alignment of `right`
self.deallocate_ptr(temp.ptr(), None, kind)?;
interp_ok(())
}

View file

@ -1359,6 +1359,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let src_alloc = self.get_alloc_raw(src_alloc_id)?;
let src_range = alloc_range(src_offset, size);
assert!(!self.memory.validation_in_progress, "we can't be copying during validation");
// For the overlapping case, it is crucial that we trigger the read hook
// before the write hook -- the aliasing model cares about the order.
M::before_memory_read(
tcx,
&self.machine,

View file

@ -773,22 +773,6 @@ where
interp_ok(())
}
/// Copies the data from an operand to a place.
/// The layouts of the `src` and `dest` may disagree.
/// Does not perform validation of the destination.
/// The only known use case for this function is checking the return
/// value of a static during stack frame popping.
#[inline(always)]
pub(super) fn copy_op_no_dest_validation(
&mut self,
src: &impl Projectable<'tcx, M::Provenance>,
dest: &impl Writeable<'tcx, M::Provenance>,
) -> InterpResult<'tcx> {
self.copy_op_inner(
src, dest, /* allow_transmute */ true, /* validate_dest */ false,
)
}
/// Copies the data from an operand to a place.
/// The layouts of the `src` and `dest` may disagree.
#[inline(always)]
@ -797,9 +781,7 @@ where
src: &impl Projectable<'tcx, M::Provenance>,
dest: &impl Writeable<'tcx, M::Provenance>,
) -> InterpResult<'tcx> {
self.copy_op_inner(
src, dest, /* allow_transmute */ true, /* validate_dest */ true,
)
self.copy_op_inner(src, dest, /* allow_transmute */ true)
}
/// Copies the data from an operand to a place.
@ -810,9 +792,7 @@ where
src: &impl Projectable<'tcx, M::Provenance>,
dest: &impl Writeable<'tcx, M::Provenance>,
) -> InterpResult<'tcx> {
self.copy_op_inner(
src, dest, /* allow_transmute */ false, /* validate_dest */ true,
)
self.copy_op_inner(src, dest, /* allow_transmute */ false)
}
/// Copies the data from an operand to a place.
@ -824,22 +804,21 @@ where
src: &impl Projectable<'tcx, M::Provenance>,
dest: &impl Writeable<'tcx, M::Provenance>,
allow_transmute: bool,
validate_dest: bool,
) -> InterpResult<'tcx> {
// These are technically *two* typed copies: `src` is a not-yet-loaded value,
// so we're going a typed copy at `src` type from there to some intermediate storage.
// so we're doing a typed copy at `src` type from there to some intermediate storage.
// And then we're doing a second typed copy from that intermediate storage to `dest`.
// But as an optimization, we only make a single direct copy here.
// Do the actual copy.
self.copy_op_no_validate(src, dest, allow_transmute)?;
if validate_dest && M::enforce_validity(self, dest.layout()) {
if M::enforce_validity(self, dest.layout()) {
let dest = dest.to_place();
// Given that there were two typed copies, we have to ensure this is valid at both types,
// and we have to ensure this loses provenance and padding according to both types.
// But if the types are identical, we only do one pass.
if allow_transmute && src.layout().ty != dest.layout().ty {
if src.layout().ty != dest.layout().ty {
self.validate_operand(
&dest.transmute(src.layout(), self)?,
M::enforce_validity_recursively(self, src.layout()),

View file

@ -1,19 +1,19 @@
Attempt was made to import an unimportable value. This can happen when trying
to import a method from a trait.
Attempt was made to import an unimportable type. This can happen when trying
to import a type from a trait.
Erroneous code example:
```compile_fail,E0253
mod foo {
pub trait MyTrait {
fn do_something();
type SomeType;
}
}
use foo::MyTrait::do_something;
// error: `do_something` is not directly importable
use foo::MyTrait::SomeType;
// error: `SomeType` is not directly importable
fn main() {}
```
It's invalid to directly import methods belonging to a trait or concrete type.
It's invalid to directly import types belonging to a trait.

View file

@ -2216,6 +2216,11 @@ impl HumanEmitter {
show_code_change
{
for part in parts {
let snippet = if let Ok(snippet) = sm.span_to_snippet(part.span) {
snippet
} else {
String::new()
};
let span_start_pos = sm.lookup_char_pos(part.span.lo()).col_display;
let span_end_pos = sm.lookup_char_pos(part.span.hi()).col_display;
@ -2263,13 +2268,80 @@ impl HumanEmitter {
}
if let DisplaySuggestion::Diff = show_code_change {
// Colorize removal with red in diff format.
buffer.set_style_range(
row_num - 2,
(padding as isize + span_start_pos as isize) as usize,
(padding as isize + span_end_pos as isize) as usize,
Style::Removal,
true,
);
// Below, there's some tricky buffer indexing going on. `row_num` at this
// point corresponds to:
//
// |
// LL | CODE
// | ++++ <- `row_num`
//
// in the buffer. When we have a diff format output, we end up with
//
// |
// LL - OLDER <- row_num - 2
// LL + NEWER
// | <- row_num
//
// The `row_num - 2` is to select the buffer line that has the "old version
// of the diff" at that point. When the removal is a single line, `i` is
// `0`, `newlines` is `1` so `(newlines - i - 1)` ends up being `0`, so row
// points at `LL - OLDER`. When the removal corresponds to multiple lines,
// we end up with `newlines > 1` and `i` being `0..newlines - 1`.
//
// |
// LL - OLDER <- row_num - 2 - (newlines - last_i - 1)
// LL - CODE
// LL - BEING
// LL - REMOVED <- row_num - 2 - (newlines - first_i - 1)
// LL + NEWER
// | <- row_num
let newlines = snippet.lines().count();
if newlines > 0 && row_num > newlines {
// Account for removals where the part being removed spans multiple
// lines.
// FIXME: We check the number of rows because in some cases, like in
// `tests/ui/lint/invalid-nan-comparison-suggestion.rs`, the rendered
// suggestion will only show the first line of code being replaced. The
// proper way of doing this would be to change the suggestion rendering
// logic to show the whole prior snippet, but the current output is not
// too bad to begin with, so we side-step that issue here.
for (i, line) in snippet.lines().enumerate() {
let line = normalize_whitespace(line);
let row = row_num - 2 - (newlines - i - 1);
// On the first line, we highlight between the start of the part
// span, and the end of that line.
// On the last line, we highlight between the start of the line, and
// the column of the part span end.
// On all others, we highlight the whole line.
let start = if i == 0 {
(padding as isize + span_start_pos as isize) as usize
} else {
padding
};
let end = if i == 0 {
(padding as isize
+ span_start_pos as isize
+ line.len() as isize)
as usize
} else if i == newlines - 1 {
(padding as isize + span_end_pos as isize) as usize
} else {
(padding as isize + line.len() as isize) as usize
};
buffer.set_style_range(row, start, end, Style::Removal, true);
}
} else {
// The removed code fits all in one line.
buffer.set_style_range(
row_num - 2,
(padding as isize + span_start_pos as isize) as usize,
(padding as isize + span_end_pos as isize) as usize,
Style::Removal,
true,
);
}
}
// length of the code after substitution

View file

@ -697,8 +697,10 @@ fn transcribe_metavar_expr<'a>(
MetaVarExprConcatElem::Var(ident) => {
match matched_from_ident(dcx, *ident, interp)? {
NamedMatch::MatchedSeq(named_matches) => {
let curr_idx = repeats.last().unwrap().0;
match &named_matches[curr_idx] {
let Some((curr_idx, _)) = repeats.last() else {
return Err(dcx.struct_span_err(sp.entire(), "invalid syntax"));
};
match &named_matches[*curr_idx] {
// FIXME(c410-f3r) Nested repetitions are unimplemented
MatchedSeq(_) => unimplemented!(),
MatchedSingle(pnr) => {

View file

@ -519,6 +519,8 @@ declare_features! (
(unstable, impl_trait_in_bindings, "1.64.0", Some(63065)),
/// Allows `impl Trait` as output type in `Fn` traits in return position of functions.
(unstable, impl_trait_in_fn_trait_return, "1.64.0", Some(99697)),
/// Allows `use` associated functions from traits.
(unstable, import_trait_associated_functions, "CURRENT_RUSTC_VERSION", Some(134691)),
/// Allows associated types in inherent impls.
(incomplete, inherent_associated_types, "1.52.0", Some(8995)),
/// Allow anonymous constants from an inline `const` block in pattern position

View file

@ -94,6 +94,7 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -
| sym::add_with_overflow
| sym::sub_with_overflow
| sym::mul_with_overflow
| sym::carrying_mul_add
| sym::wrapping_add
| sym::wrapping_sub
| sym::wrapping_mul
@ -436,6 +437,10 @@ pub fn check_intrinsic_type(
(1, 0, vec![param(0), param(0)], Ty::new_tup(tcx, &[param(0), tcx.types.bool]))
}
sym::carrying_mul_add => {
(2, 0, vec![param(0); 4], Ty::new_tup(tcx, &[param(1), param(0)]))
}
sym::ptr_guaranteed_cmp => (
1,
0,
@ -496,7 +501,9 @@ pub fn check_intrinsic_type(
(1, 0, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], tcx.types.unit)
}
sym::typed_swap => (1, 0, vec![Ty::new_mut_ptr(tcx, param(0)); 2], tcx.types.unit),
sym::typed_swap_nonoverlapping => {
(1, 0, vec![Ty::new_mut_ptr(tcx, param(0)); 2], tcx.types.unit)
}
sym::discriminant_value => {
let assoc_items = tcx.associated_item_def_ids(

View file

@ -179,7 +179,12 @@ impl<T: Idx> BitSet<T> {
/// Insert `elem`. Returns whether the set has changed.
#[inline]
pub fn insert(&mut self, elem: T) -> bool {
assert!(elem.index() < self.domain_size);
assert!(
elem.index() < self.domain_size,
"inserting element at index {} but domain size is {}",
elem.index(),
self.domain_size,
);
let (word_index, mask) = word_index_and_mask(elem);
let word_ref = &mut self.words[word_index];
let word = *word_ref;

View file

@ -945,7 +945,7 @@ impl<'tcx> InferCtxt<'tcx> {
/// Clone the list of variable regions. This is used only during NLL processing
/// to put the set of region variables into the NLL region context.
pub fn get_region_var_origins(&self) -> VarInfos {
pub fn get_region_var_infos(&self) -> VarInfos {
let inner = self.inner.borrow();
assert!(!UndoLogs::<UndoLog<'_>>::in_snapshot(&inner.undo_log));
let storage = inner.region_constraint_storage.as_ref().expect("regions already resolved");

View file

@ -299,10 +299,6 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
self.storage.var_infos.len()
}
pub fn region_constraint_data(&self) -> &RegionConstraintData<'tcx> {
&self.storage.data
}
/// Takes (and clears) the current set of constraints. Note that
/// the set of variables remains intact, but all relationships
/// between them are reset. This is used during NLL checking to

View file

@ -0,0 +1,185 @@
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Diag;
use rustc_hir as hir;
use rustc_middle::ty;
use rustc_session::{declare_lint, impl_lint_pass};
use rustc_span::Symbol;
use rustc_span::symbol::sym;
use crate::{LateContext, LateLintPass};
declare_lint! {
/// The `default_overrides_default_fields` lint checks for manual `impl` blocks of the
/// `Default` trait of types with default field values.
///
/// ### Example
///
/// ```rust,compile_fail
/// #![feature(default_field_values)]
/// struct Foo {
/// x: i32 = 101,
/// y: NonDefault,
/// }
///
/// struct NonDefault;
///
/// #[deny(default_overrides_default_fields)]
/// impl Default for Foo {
/// fn default() -> Foo {
/// Foo { x: 100, y: NonDefault }
/// }
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// Manually writing a `Default` implementation for a type that has
/// default field values runs the risk of diverging behavior between
/// `Type { .. }` and `<Type as Default>::default()`, which would be a
/// foot-gun for users of that type that would expect these to be
/// equivalent. If `Default` can't be derived due to some fields not
/// having a `Default` implementation, we encourage the use of `..` for
/// the fields that do have a default field value.
pub DEFAULT_OVERRIDES_DEFAULT_FIELDS,
Deny,
"detect `Default` impl that should use the type's default field values",
@feature_gate = default_field_values;
}
#[derive(Default)]
pub(crate) struct DefaultCouldBeDerived;
impl_lint_pass!(DefaultCouldBeDerived => [DEFAULT_OVERRIDES_DEFAULT_FIELDS]);
impl<'tcx> LateLintPass<'tcx> for DefaultCouldBeDerived {
fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) {
// Look for manual implementations of `Default`.
let Some(default_def_id) = cx.tcx.get_diagnostic_item(sym::Default) else { return };
let hir::ImplItemKind::Fn(_sig, body_id) = impl_item.kind else { return };
let assoc = cx.tcx.associated_item(impl_item.owner_id);
let parent = assoc.container_id(cx.tcx);
if cx.tcx.has_attr(parent, sym::automatically_derived) {
// We don't care about what `#[derive(Default)]` produces in this lint.
return;
}
let Some(trait_ref) = cx.tcx.impl_trait_ref(parent) else { return };
let trait_ref = trait_ref.instantiate_identity();
if trait_ref.def_id != default_def_id {
return;
}
let ty = trait_ref.self_ty();
let ty::Adt(def, _) = ty.kind() else { return };
// We now know we have a manually written definition of a `<Type as Default>::default()`.
let hir = cx.tcx.hir();
let type_def_id = def.did();
let body = hir.body(body_id);
// FIXME: evaluate bodies with statements and evaluate bindings to see if they would be
// derivable.
let hir::ExprKind::Block(hir::Block { stmts: _, expr: Some(expr), .. }, None) =
body.value.kind
else {
return;
};
// Keep a mapping of field name to `hir::FieldDef` for every field in the type. We'll use
// these to check for things like checking whether it has a default or using its span for
// suggestions.
let orig_fields = match hir.get_if_local(type_def_id) {
Some(hir::Node::Item(hir::Item {
kind:
hir::ItemKind::Struct(hir::VariantData::Struct { fields, recovered: _ }, _generics),
..
})) => fields.iter().map(|f| (f.ident.name, f)).collect::<FxHashMap<_, _>>(),
_ => return,
};
// We check `fn default()` body is a single ADT literal and get all the fields that are
// being set.
let hir::ExprKind::Struct(_qpath, fields, tail) = expr.kind else { return };
// We have a struct literal
//
// struct Foo {
// field: Type,
// }
//
// impl Default for Foo {
// fn default() -> Foo {
// Foo {
// field: val,
// }
// }
// }
//
// We would suggest `#[derive(Default)]` if `field` has a default value, regardless of what
// it is; we don't want to encourage divergent behavior between `Default::default()` and
// `..`.
if let hir::StructTailExpr::Base(_) = tail {
// This is *very* niche. We'd only get here if someone wrote
// impl Default for Ty {
// fn default() -> Ty {
// Ty { ..something() }
// }
// }
// where `something()` would have to be a call or path.
// We have nothing meaninful to do with this.
return;
}
// At least one of the fields with a default value have been overriden in
// the `Default` implementation. We suggest removing it and relying on `..`
// instead.
let any_default_field_given =
fields.iter().any(|f| orig_fields.get(&f.ident.name).and_then(|f| f.default).is_some());
if !any_default_field_given {
// None of the default fields were actually provided explicitly, so the manual impl
// doesn't override them (the user used `..`), so there's no risk of divergent behavior.
return;
}
let Some(local) = parent.as_local() else { return };
let hir_id = cx.tcx.local_def_id_to_hir_id(local);
let hir::Node::Item(item) = cx.tcx.hir_node(hir_id) else { return };
cx.tcx.node_span_lint(DEFAULT_OVERRIDES_DEFAULT_FIELDS, hir_id, item.span, |diag| {
mk_lint(diag, orig_fields, fields);
});
}
}
fn mk_lint(
diag: &mut Diag<'_, ()>,
orig_fields: FxHashMap<Symbol, &hir::FieldDef<'_>>,
fields: &[hir::ExprField<'_>],
) {
diag.primary_message("`Default` impl doesn't use the declared default field values");
// For each field in the struct expression
// - if the field in the type has a default value, it should be removed
// - elif the field is an expression that could be a default value, it should be used as the
// field's default value (FIXME: not done).
// - else, we wouldn't touch this field, it would remain in the manual impl
let mut removed_all_fields = true;
for field in fields {
if orig_fields.get(&field.ident.name).and_then(|f| f.default).is_some() {
diag.span_label(field.expr.span, "this field has a default value");
} else {
removed_all_fields = false;
}
}
diag.help(if removed_all_fields {
"to avoid divergence in behavior between `Struct { .. }` and \
`<Struct as Default>::default()`, derive the `Default`"
} else {
"use the default values in the `impl` with `Struct { mandatory_field, .. }` to avoid them \
diverging over time"
});
}

View file

@ -41,6 +41,7 @@ mod async_fn_in_trait;
pub mod builtin;
mod context;
mod dangling;
mod default_could_be_derived;
mod deref_into_dyn_supertrait;
mod drop_forget_useless;
mod early;
@ -85,6 +86,7 @@ use async_closures::AsyncClosureUsage;
use async_fn_in_trait::AsyncFnInTrait;
use builtin::*;
use dangling::*;
use default_could_be_derived::DefaultCouldBeDerived;
use deref_into_dyn_supertrait::*;
use drop_forget_useless::*;
use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums;
@ -189,6 +191,7 @@ late_lint_methods!(
BuiltinCombinedModuleLateLintPass,
[
ForLoopsOverFallibles: ForLoopsOverFallibles,
DefaultCouldBeDerived: DefaultCouldBeDerived::default(),
DerefIntoDynSupertrait: DerefIntoDynSupertrait,
DropForgetUseless: DropForgetUseless,
ImproperCTypesDeclarations: ImproperCTypesDeclarations,

View file

@ -12,5 +12,5 @@ libc = "0.2.73"
# tidy-alphabetical-start
# Pinned so `cargo update` bumps don't cause breakage. Please also update the
# pinned `cc` in `rustc_codegen_ssa` if you update `cc` here.
cc = "=1.2.5"
cc = "=1.2.6"
# tidy-alphabetical-end

View file

@ -220,7 +220,7 @@ fn main() {
let mut cmd = Command::new(&llvm_config);
cmd.arg(llvm_link_arg).arg("--libs");
// Don't link system libs if cross-compiling unless targetting Windows.
// Don't link system libs if cross-compiling unless targeting Windows.
// On Windows system DLLs aren't linked directly, instead import libraries are used.
// These import libraries are independent of the host.
if !is_crossed || target.contains("windows") {

View file

@ -132,6 +132,7 @@ rustc_queries! {
}
/// Return the span for a definition.
///
/// Contrary to `def_span` below, this query returns the full absolute span of the definition.
/// This span is meant for dep-tracking rather than diagnostics. It should not be used outside
/// of rustc_middle::hir::source_map.
@ -142,6 +143,7 @@ rustc_queries! {
}
/// Represents crate as a whole (as distinct from the top-level crate module).
///
/// If you call `hir_crate` (e.g., indirectly by calling `tcx.hir().krate()`),
/// we will have to assume that any change means that you need to be recompiled.
/// This is because the `hir_crate` query gives you access to all other items.
@ -202,28 +204,40 @@ rustc_queries! {
feedable
}
/// Given the def_id of a const-generic parameter, computes the associated default const
/// parameter. e.g. `fn example<const N: usize=3>` called on `N` would return `3`.
/// Returns the *default* of the const pararameter given by `DefId`.
///
/// E.g., given `struct Ty<const N: usize = 3>;` this returns `3` for `N`.
query const_param_default(param: DefId) -> ty::EarlyBinder<'tcx, ty::Const<'tcx>> {
desc { |tcx| "computing const default for a given parameter `{}`", tcx.def_path_str(param) }
desc { |tcx| "computing the default for const parameter `{}`", tcx.def_path_str(param) }
cache_on_disk_if { param.is_local() }
separate_provide_extern
}
/// Returns the [`Ty`][rustc_middle::ty::Ty] of the given [`DefId`]. If the [`DefId`] points
/// to an alias, it will "skip" this alias to return the aliased type.
/// Returns the *type* of the definition given by `DefId`.
///
/// [`DefId`]: rustc_hir::def_id::DefId
/// For type aliases (whether eager or lazy) and associated types, this returns
/// the underlying aliased type (not the corresponding [alias type]).
///
/// For opaque types, this returns and thus reveals the hidden type! If you
/// want to detect cycle errors use `type_of_opaque` instead.
///
/// To clarify, for type definitions, this does *not* return the "type of a type"
/// (aka *kind* or *sort*) in the type-theoretical sense! It merely returns
/// the type primarily *associated with* it.
///
/// # Panics
///
/// This query will panic if the given definition doesn't (and can't
/// conceptually) have an (underlying) type.
///
/// [alias type]: rustc_middle::ty::AliasTy
query type_of(key: DefId) -> ty::EarlyBinder<'tcx, Ty<'tcx>> {
desc { |tcx|
"{action} `{path}`",
action = {
use rustc_hir::def::DefKind;
match tcx.def_kind(key) {
DefKind::TyAlias => "expanding type alias",
DefKind::TraitAlias => "expanding trait alias",
_ => "computing type of",
}
action = match tcx.def_kind(key) {
DefKind::TyAlias => "expanding type alias",
DefKind::TraitAlias => "expanding trait alias",
_ => "computing type of",
},
path = tcx.def_path_str(key),
}
@ -232,9 +246,14 @@ rustc_queries! {
feedable
}
/// Specialized instance of `type_of` that detects cycles that are due to
/// revealing opaque because of an auto trait bound. Unless `CyclePlaceholder` needs
/// to be handled separately, call `type_of` instead.
/// Returns the *hidden type* of the opaque type given by `DefId` unless a cycle occurred.
///
/// This is a specialized instance of [`Self::type_of`] that detects query cycles.
/// Unless `CyclePlaceholder` needs to be handled separately, call [`Self::type_of`] instead.
///
/// # Panics
///
/// This query will panic if the given definition is not an opaque type.
query type_of_opaque(key: DefId) -> Result<ty::EarlyBinder<'tcx, Ty<'tcx>>, CyclePlaceholder> {
desc { |tcx|
"computing type of opaque `{path}`",
@ -243,9 +262,22 @@ rustc_queries! {
cycle_stash
}
/// Returns whether the type alias given by `DefId` is lazy.
///
/// I.e., if the type alias expands / ought to expand to a [weak] [alias type]
/// instead of the underyling aliased type.
///
/// Relevant for features `lazy_type_alias` and `type_alias_impl_trait`.
///
/// # Panics
///
/// This query *may* panic if the given definition is not a type alias.
///
/// [weak]: rustc_middle::ty::Weak
/// [alias type]: rustc_middle::ty::AliasTy
query type_alias_is_lazy(key: DefId) -> bool {
desc { |tcx|
"computing whether `{path}` is a lazy type alias",
"computing whether the type alias `{path}` is lazy",
path = tcx.def_path_str(key),
}
separate_provide_extern
@ -299,8 +331,7 @@ rustc_queries! {
desc { "checking lint expectations (RFC 2383)" }
}
/// Maps from the `DefId` of an item (trait/struct/enum/fn) to its
/// associated generics.
/// Returns the *generics* of the definition given by `DefId`.
query generics_of(key: DefId) -> &'tcx ty::Generics {
desc { |tcx| "computing generics of `{}`", tcx.def_path_str(key) }
arena_cache
@ -309,10 +340,13 @@ rustc_queries! {
feedable
}
/// Maps from the `DefId` of an item (trait/struct/enum/fn) to the
/// predicates (where-clauses) that must be proven true in order
/// to reference it. This is almost always the "predicates query"
/// that you want.
/// Returns the (elaborated) *predicates* of the definition given by `DefId`
/// that must be proven true at usage sites (and which can be assumed at definition site).
///
/// This is almost always *the* "predicates query" that you want.
///
/// **Tip**: You can use `#[rustc_dump_predicates]` on an item to basically print
/// the result of this query for use in UI tests or for debugging purposes.
query predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> {
desc { |tcx| "computing predicates of `{}`", tcx.def_path_str(key) }
cache_on_disk_if { key.is_local() }
@ -328,25 +362,24 @@ rustc_queries! {
}
}
/// Returns the list of bounds that are required to be satisfied
/// by a implementation or definition. For associated types, these
/// must be satisfied for an implementation to be well-formed,
/// and for opaque types, these are required to be satisfied by
/// the hidden-type of the opaque.
/// Returns the explicitly user-written *bounds* on the associated or opaque type given by `DefId`
/// that must be proven true at definition site (and which can be assumed at usage sites).
///
/// Syntactially, these are the bounds written on the trait's type
/// definition, or those after the `impl` keyword for an opaque:
/// For associated types, these must be satisfied for an implementation
/// to be well-formed, and for opaque types, these are required to be
/// satisfied by the hidden type of the opaque.
///
/// ```ignore (incomplete)
/// type X: Bound + 'lt
/// // ^^^^^^^^^^^
/// impl Debug + Display
/// // ^^^^^^^^^^^^^^^
/// Bounds from the parent (e.g. with nested `impl Trait`) are not included.
///
/// Syntactially, these are the bounds written on associated types in trait
/// definitions, or those after the `impl` keyword for an opaque:
///
/// ```ignore (illustrative)
/// trait Trait { type X: Bound + 'lt; }
/// // ^^^^^^^^^^^
/// fn function() -> impl Debug + Display { /*...*/ }
/// // ^^^^^^^^^^^^^^^
/// ```
///
/// `key` is the `DefId` of the associated type or opaque type.
///
/// Bounds from the parent (e.g. with nested impl trait) are not included.
query explicit_item_bounds(key: DefId) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> {
desc { |tcx| "finding item bounds for `{}`", tcx.def_path_str(key) }
cache_on_disk_if { key.is_local() }
@ -354,10 +387,12 @@ rustc_queries! {
feedable
}
/// The set of item bounds (see [`TyCtxt::explicit_item_bounds`]) that
/// share the `Self` type of the item. These are a subset of the bounds
/// that may explicitly be used for things like closure signature
/// deduction.
/// Returns the explicitly user-written *bounds* that share the `Self` type of the item.
///
/// These are a subset of the [explicit item bounds] that may explicitly be used for things
/// like closure signature deduction.
///
/// [explicit item bounds]: Self::explicit_item_bounds
query explicit_item_super_predicates(key: DefId) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> {
desc { |tcx| "finding item bounds for `{}`", tcx.def_path_str(key) }
cache_on_disk_if { key.is_local() }
@ -365,26 +400,29 @@ rustc_queries! {
feedable
}
/// Elaborated version of the predicates from `explicit_item_bounds`.
/// Returns the (elaborated) *bounds* on the associated or opaque type given by `DefId`
/// that must be proven true at definition site (and which can be assumed at usage sites).
///
/// For example:
/// Bounds from the parent (e.g. with nested `impl Trait`) are not included.
///
/// **Tip**: You can use `#[rustc_dump_item_bounds]` on an item to basically print
/// the result of this query for use in UI tests or for debugging purposes.
///
/// # Examples
///
/// ```
/// trait MyTrait {
/// type MyAType: Eq + ?Sized;
/// }
/// trait Trait { type Assoc: Eq + ?Sized; }
/// ```
///
/// `explicit_item_bounds` returns `[<Self as MyTrait>::MyAType: Eq]`,
/// and `item_bounds` returns
/// While [`Self::explicit_item_bounds`] returns `[<Self as Trait>::Assoc: Eq]`
/// here, `item_bounds` returns:
///
/// ```text
/// [
/// <Self as Trait>::MyAType: Eq,
/// <Self as Trait>::MyAType: PartialEq<<Self as Trait>::MyAType>
/// <Self as Trait>::Assoc: Eq,
/// <Self as Trait>::Assoc: PartialEq<<Self as Trait>::Assoc>
/// ]
/// ```
///
/// Bounds from the parent (e.g. with nested impl trait) are not included.
query item_bounds(key: DefId) -> ty::EarlyBinder<'tcx, ty::Clauses<'tcx>> {
desc { |tcx| "elaborating item bounds for `{}`", tcx.def_path_str(key) }
}
@ -615,27 +653,35 @@ rustc_queries! {
desc { "getting wasm import module map" }
}
/// Returns everything that looks like a predicate written explicitly
/// by the user on a trait item.
/// Returns the explicitly user-written *predicates and bounds* of the trait given by `DefId`.
///
/// Traits are unusual, because predicates on associated types are
/// converted into bounds on that type for backwards compatibility:
///
/// ```
/// trait X where Self::U: Copy { type U; }
/// ```
///
/// becomes
///
/// ```
/// trait X { type U: Copy; }
/// ```
///
/// `explicit_predicates_of` and `explicit_item_bounds` will then take
/// the appropriate subsets of the predicates here.
/// [`Self::explicit_predicates_of`] and [`Self::explicit_item_bounds`] will
/// then take the appropriate subsets of the predicates here.
///
/// # Panics
///
/// This query will panic if the given definition is not a trait.
query trait_explicit_predicates_and_bounds(key: LocalDefId) -> ty::GenericPredicates<'tcx> {
desc { |tcx| "computing explicit predicates of trait `{}`", tcx.def_path_str(key) }
}
/// Returns the predicates written explicitly by the user.
/// Returns the explicitly user-written *predicates* of the definition given by `DefId`
/// that must be proven true at usage sites (and which can be assumed at definition site).
///
/// You should probably use `predicates_of` unless you're looking for
/// You should probably use [`Self::predicates_of`] unless you're looking for
/// predicates with explicit spans for diagnostics purposes.
query explicit_predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> {
desc { |tcx| "computing explicit predicates of `{}`", tcx.def_path_str(key) }
@ -644,18 +690,24 @@ rustc_queries! {
feedable
}
/// Returns the inferred outlives predicates (e.g., for `struct
/// Foo<'a, T> { x: &'a T }`, this would return `T: 'a`).
/// Returns the *inferred outlives-predicates* of the item given by `DefId`.
///
/// E.g., for `struct Foo<'a, T> { x: &'a T }`, this would return `[T: 'a]`.
///
/// **Tip**: You can use `#[rustc_outlives]` on an item to basically print the
/// result of this query for use in UI tests or for debugging purposes.
query inferred_outlives_of(key: DefId) -> &'tcx [(ty::Clause<'tcx>, Span)] {
desc { |tcx| "computing inferred outlives predicates of `{}`", tcx.def_path_str(key) }
desc { |tcx| "computing inferred outlives-predicates of `{}`", tcx.def_path_str(key) }
cache_on_disk_if { key.is_local() }
separate_provide_extern
feedable
}
/// Maps from the `DefId` of a trait to the list of super-predicates of the trait,
/// *before* elaboration (so it doesn't contain transitive super-predicates). This
/// is a subset of the full list of predicates. We store these in a separate map
/// Returns the explicitly user-written *super-predicates* of the trait given by `DefId`.
///
/// These predicates are unelaborated and consequently don't contain transitive super-predicates.
///
/// This is a subset of the full list of predicates. We store these in a separate map
/// because we must evaluate them even during type conversion, often before the full
/// predicates are available (note that super-predicates must not be cyclic).
query explicit_super_predicates_of(key: DefId) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> {
@ -664,8 +716,9 @@ rustc_queries! {
separate_provide_extern
}
/// The predicates of the trait that are implied during elaboration. This is a
/// superset of the super-predicates of the trait, but a subset of the predicates
/// The predicates of the trait that are implied during elaboration.
///
/// This is a superset of the super-predicates of the trait, but a subset of the predicates
/// of the trait. For regular traits, this includes all super-predicates and their
/// associated type bounds. For trait aliases, currently, this includes all of the
/// predicates of the trait alias.
@ -745,14 +798,27 @@ rustc_queries! {
desc { |tcx| "computing drop-check constraints for `{}`", tcx.def_path_str(key) }
}
/// Returns the constness of function-like things (tuple struct/variant constructors, functions,
/// methods)
/// Returns the constness of the function-like[^1] definition given by `DefId`.
///
/// Will ICE if used on things that are always const or never const.
/// Tuple struct/variant constructors are *always* const, foreign functions are
/// *never* const. The rest is const iff marked with keyword `const` (or rather
/// its parent in the case of associated functions).
///
/// **Do not call this function manually.** It is only meant to cache the base data for the
/// <div class="warning">
///
/// **Do not call this query** directly. It is only meant to cache the base data for the
/// higher-level functions. Consider using `is_const_fn` or `is_const_trait_impl` instead.
/// Also note that neither of them takes into account feature gates and stability.
///
/// Also note that neither of them takes into account feature gates, stability and
/// const predicates/conditions!
///
/// </div>
///
/// # Panics
///
/// This query will panic if the given definition is not function-like[^1].
///
/// [^1]: Tuple struct/variant constructors, closures and free, associated and foreign functions.
query constness(key: DefId) -> hir::Constness {
desc { |tcx| "checking if item is const: `{}`", tcx.def_path_str(key) }
separate_provide_extern
@ -798,13 +864,25 @@ rustc_queries! {
separate_provide_extern
}
/// Gets a map with the variance of every item; use `variances_of` instead.
/// Gets a map with the variances of every item in the local crate.
///
/// <div class="warning">
///
/// **Do not call this query** directly, use [`Self::variances_of`] instead.
///
/// </div>
query crate_variances(_: ()) -> &'tcx ty::CrateVariancesMap<'tcx> {
arena_cache
desc { "computing the variances for items in this crate" }
}
/// Maps from the `DefId` of a type or region parameter to its (inferred) variance.
/// Returns the (inferred) variances of the item given by `DefId`.
///
/// The list of variances corresponds to the list of (early-bound) generic
/// parameters of the item (including its parents).
///
/// **Tip**: You can use `#[rustc_variance]` on an item to basically print the
/// result of this query for use in UI tests or for debugging purposes.
query variances_of(def_id: DefId) -> &'tcx [ty::Variance] {
desc { |tcx| "computing the variances of `{}`", tcx.def_path_str(def_id) }
cache_on_disk_if { def_id.is_local() }
@ -812,10 +890,16 @@ rustc_queries! {
cycle_delay_bug
}
/// Maps from thee `DefId` of a type to its (inferred) outlives.
/// Gets a map with the inferred outlives-predicates of every item in the local crate.
///
/// <div class="warning">
///
/// **Do not call this query** directly, use [`Self::inferred_outlives_of`] instead.
///
/// </div>
query inferred_outlives_crate(_: ()) -> &'tcx ty::CratePredicatesMap<'tcx> {
arena_cache
desc { "computing the inferred outlives predicates for items in this crate" }
desc { "computing the inferred outlives-predicates for items in this crate" }
}
/// Maps from an impl/trait or struct/variant `DefId`
@ -1038,20 +1122,35 @@ rustc_queries! {
}
/// Gets a complete map from all types to their inherent impls.
/// Not meant to be used directly outside of coherence.
///
/// <div class="warning">
///
/// **Not meant to be used** directly outside of coherence.
///
/// </div>
query crate_inherent_impls(k: ()) -> (&'tcx CrateInherentImpls, Result<(), ErrorGuaranteed>) {
desc { "finding all inherent impls defined in crate" }
}
/// Checks all types in the crate for overlap in their inherent impls. Reports errors.
/// Not meant to be used directly outside of coherence.
///
/// <div class="warning">
///
/// **Not meant to be used** directly outside of coherence.
///
/// </div>
query crate_inherent_impls_validity_check(_: ()) -> Result<(), ErrorGuaranteed> {
desc { "check for inherent impls that should not be defined in crate" }
ensure_forwards_result_if_red
}
/// Checks all types in the crate for overlap in their inherent impls. Reports errors.
/// Not meant to be used directly outside of coherence.
///
/// <div class="warning">
///
/// **Not meant to be used** directly outside of coherence.
///
/// </div>
query crate_inherent_impls_overlap_check(_: ()) -> Result<(), ErrorGuaranteed> {
desc { "check for overlap between inherent impls defined in this crate" }
ensure_forwards_result_if_red
@ -1089,8 +1188,12 @@ rustc_queries! {
}
/// Computes the tag (if any) for a given type and variant.
///
/// `None` means that the variant doesn't need a tag (because it is niched).
/// Will panic for uninhabited variants.
///
/// # Panics
///
/// This query will panic for uninhabited variants and if the passed type is not an enum.
query tag_for_variant(
key: (Ty<'tcx>, abi::VariantIdx)
) -> Option<ty::ScalarInt> {
@ -1099,7 +1202,12 @@ rustc_queries! {
/// Evaluates a constant and returns the computed allocation.
///
/// **Do not use this** directly, use the `eval_to_const_value` or `eval_to_valtree` instead.
/// <div class="warning">
///
/// **Do not call this query** directly, use [`Self::eval_to_const_value_raw`] or
/// [`Self::eval_to_valtree`] instead.
///
/// </div>
query eval_to_allocation_raw(key: ty::PseudoCanonicalInput<'tcx, GlobalId<'tcx>>)
-> EvalToAllocationRawResult<'tcx> {
desc { |tcx|
@ -1120,12 +1228,18 @@ rustc_queries! {
feedable
}
/// Evaluates const items or anonymous constants
/// (such as enum variant explicit discriminants or array lengths)
/// into a representation suitable for the type system and const generics.
/// Evaluates const items or anonymous constants[^1] into a representation
/// suitable for the type system and const generics.
///
/// **Do not use this** directly, use one of the following wrappers: `tcx.const_eval_poly`,
/// `tcx.const_eval_resolve`, `tcx.const_eval_instance`, or `tcx.const_eval_global_id`.
/// <div class="warning">
///
/// **Do not call this** directly, use one of the following wrappers:
/// [`TyCtxt::const_eval_poly`], [`TyCtxt::const_eval_resolve`],
/// [`TyCtxt::const_eval_instance`], or [`TyCtxt::const_eval_global_id`].
///
/// </div>
///
/// [^1]: Such as enum variant explicit discriminants or array lengths.
query eval_to_const_value_raw(key: ty::PseudoCanonicalInput<'tcx, GlobalId<'tcx>>)
-> EvalToConstValueResult<'tcx> {
desc { |tcx|
@ -1252,13 +1366,13 @@ rustc_queries! {
separate_provide_extern
}
/// Determines whether an item is annotated with `doc(hidden)`.
/// Determines whether an item is annotated with `#[doc(hidden)]`.
query is_doc_hidden(def_id: DefId) -> bool {
desc { |tcx| "checking whether `{}` is `doc(hidden)`", tcx.def_path_str(def_id) }
separate_provide_extern
}
/// Determines whether an item is annotated with `doc(notable_trait)`.
/// Determines whether an item is annotated with `#[doc(notable_trait)]`.
query is_doc_notable_trait(def_id: DefId) -> bool {
desc { |tcx| "checking whether `{}` is `doc(notable_trait)`", tcx.def_path_str(def_id) }
}
@ -1796,13 +1910,22 @@ rustc_queries! {
query is_late_bound_map(owner_id: hir::OwnerId) -> Option<&'tcx FxIndexSet<ItemLocalId>> {
desc { |tcx| "testing if a region is late bound inside `{}`", tcx.def_path_str(owner_id) }
}
/// For a given item's generic parameter, gets the default lifetimes to be used
/// for each parameter if a trait object were to be passed for that parameter.
/// For example, for `T` in `struct Foo<'a, T>`, this would be `'static`.
/// For `T` in `struct Foo<'a, T: 'a>`, this would instead be `'a`.
/// This query will panic if passed something that is not a type parameter.
/// Returns the *default lifetime* to be used if a trait object type were to be passed for
/// the type parameter given by `DefId`.
///
/// **Tip**: You can use `#[rustc_object_lifetime_default]` on an item to basically
/// print the result of this query for use in UI tests or for debugging purposes.
///
/// # Examples
///
/// - For `T` in `struct Foo<'a, T: 'a>(&'a T);`, this would be `Param('a)`
/// - For `T` in `struct Bar<'a, T>(&'a T);`, this would be `Empty`
///
/// # Panics
///
/// This query will panic if the given definition is not a type parameter.
query object_lifetime_default(def_id: DefId) -> ObjectLifetimeDefault {
desc { "looking up lifetime defaults for generic parameter `{}`", tcx.def_path_str(def_id) }
desc { "looking up lifetime defaults for type parameter `{}`", tcx.def_path_str(def_id) }
separate_provide_extern
}
query late_bound_vars_map(owner_id: hir::OwnerId)

View file

@ -117,7 +117,7 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> {
self.report_arguments_mismatch(expr.span, caller_sig, callee_sig);
}
// FIXME(explicit_tail_calls): this currenly fails for cases where opaques are used.
// FIXME(explicit_tail_calls): this currently fails for cases where opaques are used.
// e.g.
// ```
// fn a() -> impl Sized { become b() } // ICE

View file

@ -755,7 +755,7 @@ impl<'a> Parser<'a> {
// When there are a few keywords in the last ten elements of `self.expected_token_types`
// and the current token is an identifier, it's probably a misspelled keyword. This handles
// code like `async Move {}`, misspelled `if` in match guard, misspelled `else` in
// `if`-`else` and mispelled `where` in a where clause.
// `if`-`else` and misspelled `where` in a where clause.
if !expected_keywords.is_empty()
&& !curr_ident.is_used_keyword()
&& let Some(misspelled_kw) = find_similar_kw(curr_ident, &expected_keywords)
@ -1336,7 +1336,7 @@ impl<'a> Parser<'a> {
) -> bool {
if let ExprKind::Binary(op, l1, r1) = &inner_op.kind {
if let ExprKind::Field(_, ident) = l1.kind
&& ident.as_str().parse::<i32>().is_err()
&& !ident.is_numeric()
&& !matches!(r1.kind, ExprKind::Lit(_))
{
// The parser has encountered `foo.bar<baz`, the likelihood of the turbofish

View file

@ -1183,7 +1183,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
let in_module_is_extern = !in_module.def_id().is_local();
in_module.for_each_child(self, |this, ident, ns, name_binding| {
// avoid non-importable candidates
if !name_binding.is_importable() {
if !name_binding.is_importable()
// FIXME(import_trait_associated_functions): remove this when `import_trait_associated_functions` is stable
|| name_binding.is_assoc_const_or_fn()
&& !this.tcx.features().import_trait_associated_functions()
{
return;
}

View file

@ -17,9 +17,10 @@ use rustc_session::lint::builtin::{
AMBIGUOUS_GLOB_REEXPORTS, HIDDEN_GLOB_REEXPORTS, PUB_USE_OF_PRIVATE_EXTERN_CRATE,
REDUNDANT_IMPORTS, UNUSED_IMPORTS,
};
use rustc_session::parse::feature_err;
use rustc_span::edit_distance::find_best_match_for_name;
use rustc_span::hygiene::LocalExpnId;
use rustc_span::{Ident, Span, Symbol, kw};
use rustc_span::{Ident, Span, Symbol, kw, sym};
use smallvec::SmallVec;
use tracing::debug;
@ -829,6 +830,17 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
// Don't update the resolution, because it was never added.
Err(Determined) if target.name == kw::Underscore => {}
Ok(binding) if binding.is_importable() => {
if binding.is_assoc_const_or_fn()
&& !this.tcx.features().import_trait_associated_functions()
{
feature_err(
this.tcx.sess,
sym::import_trait_associated_functions,
import.span,
"`use` associated items of traits is unstable",
)
.emit();
}
let imported_binding = this.import(binding, import);
target_bindings[ns].set(Some(imported_binding));
this.define(parent, target, ns, imported_binding);

View file

@ -920,10 +920,13 @@ impl<'ra> NameBindingData<'ra> {
}
fn is_importable(&self) -> bool {
!matches!(
self.res(),
Res::Def(DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy, _)
)
!matches!(self.res(), Res::Def(DefKind::AssocTy, _))
}
// FIXME(import_trait_associated_functions): associate `const` or `fn` are not importable unless
// the feature `import_trait_associated_functions` is enable
fn is_assoc_const_or_fn(&self) -> bool {
matches!(self.res(), Res::Def(DefKind::AssocConst | DefKind::AssocFn, _))
}
fn macro_kind(&self) -> Option<MacroKind> {

View file

@ -1320,7 +1320,7 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
}
if sess.opts.cg.soft_float {
if sess.target.arch == "arm" && sess.target.abi == "eabihf" {
if sess.target.arch == "arm" {
sess.dcx().emit_warn(errors::SoftFloatDeprecated);
} else {
// All `use_softfp` does is the equivalent of `-mfloat-abi` in GCC/clang, which only exists on ARM targets.

View file

@ -555,6 +555,7 @@ symbols! {
call_ref_future,
caller_location,
capture_disjoint_fields,
carrying_mul_add,
catch_unwind,
cause,
cdylib,
@ -1092,6 +1093,7 @@ symbols! {
import,
import_name_type,
import_shadowing,
import_trait_associated_functions,
imported_main,
in_band_lifetimes,
include,
@ -2058,7 +2060,7 @@ symbols! {
type_macros,
type_name,
type_privacy_lints,
typed_swap,
typed_swap_nonoverlapping,
u128,
u128_legacy_const_max,
u128_legacy_const_min,
@ -2707,6 +2709,12 @@ impl Ident {
pub fn is_raw_guess(self) -> bool {
self.name.can_be_raw() && self.is_reserved()
}
/// Whether this would be the identifier for a tuple field like `self.0`, as
/// opposed to a named field like `self.thing`.
pub fn is_numeric(self) -> bool {
!self.name.is_empty() && self.as_str().bytes().all(|b| b.is_ascii_digit())
}
}
/// Collect all the keywords in a given edition into a vector.

View file

@ -1,4 +1,4 @@
use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target, base};
use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, StackProbeType, Target, base};
pub(crate) fn target() -> Target {
let mut base = base::linux::opts();
@ -7,6 +7,7 @@ pub(crate) fn target() -> Target {
base.stack_probes = StackProbeType::Inline;
base.linker_flavor = LinkerFlavor::Gnu(Cc::No, Lld::Yes);
base.linker = Some("rust-lld".into());
base.panic_strategy = PanicStrategy::Abort;
Target {
llvm_target: "x86_64-unknown-linux-none".into(),
@ -14,7 +15,7 @@ pub(crate) fn target() -> Target {
description: None,
tier: None,
host_tools: None,
std: Some(true),
std: Some(false),
},
pointer_width: 64,
data_layout:

View file

@ -161,8 +161,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
let outlives_env = OutlivesEnvironment::new(full_env);
let _ = infcx.process_registered_region_obligations(&outlives_env, |ty, _| Ok(ty));
let region_data =
infcx.inner.borrow_mut().unwrap_region_constraints().region_constraint_data().clone();
let region_data = infcx.inner.borrow_mut().unwrap_region_constraints().data().clone();
let vid_to_region = self.map_vid_to_region(&region_data);

View file

@ -819,7 +819,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
candidates.vec.push(AutoImplCandidate)
}
}
ty::Error(_) => {} // do not add an auto trait impl for `ty::Error` for now.
ty::Error(_) => {
candidates.vec.push(AutoImplCandidate);
}
}
}
}

View file

@ -1843,7 +1843,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
// a global and a non-global where-clause.
//
// Our handling of where-bounds is generally fairly messy but necessary for backwards
// compatability, see #50825 for why we need to handle global where-bounds like this.
// compatibility, see #50825 for why we need to handle global where-bounds like this.
let is_global = |c: ty::PolyTraitPredicate<'tcx>| c.is_global() && !c.has_bound_vars();
let param_candidates = candidates
.iter()

View file

@ -61,9 +61,9 @@ dependencies = [
[[package]]
name = "compiler_builtins"
version = "0.1.138"
version = "0.1.140"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53f0ea7fff95b51f84371588f06062557e96bbe363d2b36218ddb806f3ca8611"
checksum = "df14d41c5d172a886df3753d54238eefb0f61c96cbd8b363c33ccc92c457bee3"
dependencies = [
"cc",
"rustc-std-workspace-core",
@ -403,9 +403,9 @@ dependencies = [
[[package]]
name = "unwinding"
version = "0.2.4"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2c6cb20f236dae10c69b0b45d82ef50af8b7e45c10e429e7901d26b49b4dbf3"
checksum = "51f06a05848f650946acef3bf525fe96612226b61f74ae23ffa4e98bfbb8ab3c"
dependencies = [
"compiler_builtins",
"gimli 0.31.1",

View file

@ -10,7 +10,7 @@ edition = "2021"
[dependencies]
core = { path = "../core" }
compiler_builtins = { version = "=0.1.138", features = ['rustc-dep-of-std'] }
compiler_builtins = { version = "=0.1.140", features = ['rustc-dep-of-std'] }
[dev-dependencies]
rand = { version = "0.8.5", default-features = false, features = ["alloc"] }

View file

@ -761,6 +761,26 @@ impl<T> Box<[T]> {
};
unsafe { Ok(RawVec::from_raw_parts_in(ptr.as_ptr(), len, Global).into_box(len)) }
}
/// Converts the boxed slice into a boxed array.
///
/// This operation does not reallocate; the underlying array of the slice is simply reinterpreted as an array type.
///
/// If `N` is not exactly equal to the length of `self`, then this method returns `None`.
#[unstable(feature = "slice_as_array", issue = "133508")]
#[inline]
#[must_use]
pub fn into_array<const N: usize>(self) -> Option<Box<[T; N]>> {
if self.len() == N {
let ptr = Self::into_raw(self) as *mut [T; N];
// SAFETY: The underlying array of a slice has the exact same layout as an actual array `[T; N]` if `N` is equal to the slice's length.
let me = unsafe { Box::from_raw(ptr) };
Some(me)
} else {
None
}
}
}
impl<T, A: Allocator> Box<[T], A> {

View file

@ -2289,6 +2289,10 @@ impl<K, V> FusedIterator for RangeMut<'_, K, V> {}
#[stable(feature = "rust1", since = "1.0.0")]
impl<K: Ord, V> FromIterator<(K, V)> for BTreeMap<K, V> {
/// Constructs a `BTreeMap<K, V>` from an iterator of key-value pairs.
///
/// If the iterator produces any pairs with equal keys,
/// all but one of the corresponding values will be dropped.
fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> BTreeMap<K, V> {
let mut inputs: Vec<_> = iter.into_iter().collect();
@ -2403,7 +2407,10 @@ where
#[stable(feature = "std_collections_from_array", since = "1.56.0")]
impl<K: Ord, V, const N: usize> From<[(K, V); N]> for BTreeMap<K, V> {
/// Converts a `[(K, V); N]` into a `BTreeMap<(K, V)>`.
/// Converts a `[(K, V); N]` into a `BTreeMap<K, V>`.
///
/// If any entries in the array have equal keys,
/// all but one of the corresponding values will be dropped.
///
/// ```
/// use std::collections::BTreeMap;

View file

@ -1491,6 +1491,11 @@ impl<T: Ord, A: Allocator + Clone> BTreeSet<T, A> {
impl<T: Ord, const N: usize> From<[T; N]> for BTreeSet<T> {
/// Converts a `[T; N]` into a `BTreeSet<T>`.
///
/// If the array contains any equal values,
/// all but one will be dropped.
///
/// # Examples
///
/// ```
/// use std::collections::BTreeSet;
///

View file

@ -83,7 +83,7 @@
#[doc(inline)]
#[stable(feature = "alloc_c_string", since = "1.64.0")]
pub use self::c_str::CString;
#[doc(no_inline)]
#[doc(inline)]
#[stable(feature = "alloc_c_string", since = "1.64.0")]
pub use self::c_str::{FromVecWithNulError, IntoStringError, NulError};

View file

@ -420,7 +420,7 @@ impl<A: Allocator> RawVecInner<A> {
match Self::try_allocate_in(capacity, AllocInit::Uninitialized, alloc, elem_layout) {
Ok(this) => {
unsafe {
// Make it more obvious that a subsquent Vec::reserve(capacity) will not allocate.
// Make it more obvious that a subsequent Vec::reserve(capacity) will not allocate.
hint::assert_unchecked(!this.needs_to_grow(0, capacity, elem_layout));
}
this

View file

@ -1084,6 +1084,26 @@ impl<T> Rc<[T]> {
))
}
}
/// Converts the reference-counted slice into a reference-counted array.
///
/// This operation does not reallocate; the underlying array of the slice is simply reinterpreted as an array type.
///
/// If `N` is not exactly equal to the length of `self`, then this method returns `None`.
#[unstable(feature = "slice_as_array", issue = "133508")]
#[inline]
#[must_use]
pub fn into_array<const N: usize>(self) -> Option<Rc<[T; N]>> {
if self.len() == N {
let ptr = Self::into_raw(self) as *const [T; N];
// SAFETY: The underlying array of a slice has the exact same layout as an actual array `[T; N]` if `N` is equal to the slice's length.
let me = unsafe { Rc::from_raw(ptr) };
Some(me)
} else {
None
}
}
}
impl<T, A: Allocator> Rc<[T], A> {

View file

@ -1203,6 +1203,26 @@ impl<T> Arc<[T]> {
))
}
}
/// Converts the reference-counted slice into a reference-counted array.
///
/// This operation does not reallocate; the underlying array of the slice is simply reinterpreted as an array type.
///
/// If `N` is not exactly equal to the length of `self`, then this method returns `None`.
#[unstable(feature = "slice_as_array", issue = "133508")]
#[inline]
#[must_use]
pub fn into_array<const N: usize>(self) -> Option<Arc<[T; N]>> {
if self.len() == N {
let ptr = Self::into_raw(self) as *const [T; N];
// SAFETY: The underlying array of a slice has the exact same layout as an actual array `[T; N]` if `N` is equal to the slice's length.
let me = unsafe { Arc::from_raw(ptr) };
Some(me)
} else {
None
}
}
}
impl<T, A: Allocator> Arc<[T], A> {

View file

@ -33,7 +33,7 @@ fn check_is_sorted<T: Ord + Clone + Debug, S: Sort>(v: &mut [T]) {
known_good_stable_sort::sort(known_good_sorted_vec.as_mut_slice());
if is_small_test {
eprintln!("Orginal: {:?}", v_orig);
eprintln!("Original: {:?}", v_orig);
eprintln!("Expected: {:?}", known_good_sorted_vec);
eprintln!("Got: {:?}", v);
} else {

View file

@ -25,7 +25,7 @@ macro_rules! pow_bench_template {
let mut exp_iter = black_box(&exp_array).into_iter();
(0..ITERATIONS).fold((0 as IntType, false), |acc, _| {
// Sometimes constants don't propogate all the way to the
// Sometimes constants don't propagate all the way to the
// inside of the loop, so we call a custom expression every cycle
// rather than iter::repeat(CONST)
let base: IntType = $base_macro!(base_iter);

View file

@ -12,10 +12,10 @@
#[doc(inline)]
#[stable(feature = "core_c_str", since = "1.64.0")]
pub use self::c_str::CStr;
#[doc(no_inline)]
#[doc(inline)]
#[stable(feature = "cstr_from_bytes_until_nul", since = "1.69.0")]
pub use self::c_str::FromBytesUntilNulError;
#[doc(no_inline)]
#[doc(inline)]
#[stable(feature = "core_c_str", since = "1.64.0")]
pub use self::c_str::FromBytesWithNulError;
use crate::fmt;

View file

@ -0,0 +1,111 @@
#![unstable(
feature = "core_intrinsics_fallbacks",
reason = "The fallbacks will never be stable, as they exist only to be called \
by the fallback MIR, but they're exported so they can be tested on \
platforms where the fallback MIR isn't actually used",
issue = "none"
)]
#![allow(missing_docs)]
#[const_trait]
pub trait CarryingMulAdd: Copy + 'static {
type Unsigned: Copy + 'static;
fn carrying_mul_add(
self,
multiplicand: Self,
addend: Self,
carry: Self,
) -> (Self::Unsigned, Self);
}
macro_rules! impl_carrying_mul_add_by_widening {
($($t:ident $u:ident $w:ident,)+) => {$(
#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")]
impl const CarryingMulAdd for $t {
type Unsigned = $u;
#[inline]
fn carrying_mul_add(self, a: Self, b: Self, c: Self) -> ($u, $t) {
let wide = (self as $w) * (a as $w) + (b as $w) + (c as $w);
(wide as _, (wide >> Self::BITS) as _)
}
}
)+};
}
impl_carrying_mul_add_by_widening! {
u8 u8 u16,
u16 u16 u32,
u32 u32 u64,
u64 u64 u128,
usize usize UDoubleSize,
i8 u8 i16,
i16 u16 i32,
i32 u32 i64,
i64 u64 i128,
isize usize UDoubleSize,
}
#[cfg(target_pointer_width = "16")]
type UDoubleSize = u32;
#[cfg(target_pointer_width = "32")]
type UDoubleSize = u64;
#[cfg(target_pointer_width = "64")]
type UDoubleSize = u128;
#[inline]
const fn wide_mul_u128(a: u128, b: u128) -> (u128, u128) {
#[inline]
const fn to_low_high(x: u128) -> [u128; 2] {
const MASK: u128 = u64::MAX as _;
[x & MASK, x >> 64]
}
#[inline]
const fn from_low_high(x: [u128; 2]) -> u128 {
x[0] | (x[1] << 64)
}
#[inline]
const fn scalar_mul(low_high: [u128; 2], k: u128) -> [u128; 3] {
let [x, c] = to_low_high(k * low_high[0]);
let [y, z] = to_low_high(k * low_high[1] + c);
[x, y, z]
}
let a = to_low_high(a);
let b = to_low_high(b);
let low = scalar_mul(a, b[0]);
let high = scalar_mul(a, b[1]);
let r0 = low[0];
let [r1, c] = to_low_high(low[1] + high[0]);
let [r2, c] = to_low_high(low[2] + high[1] + c);
let r3 = high[2] + c;
(from_low_high([r0, r1]), from_low_high([r2, r3]))
}
#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")]
impl const CarryingMulAdd for u128 {
type Unsigned = u128;
#[inline]
fn carrying_mul_add(self, b: u128, c: u128, d: u128) -> (u128, u128) {
let (low, mut high) = wide_mul_u128(self, b);
let (low, carry) = u128::overflowing_add(low, c);
high += carry as u128;
let (low, carry) = u128::overflowing_add(low, d);
high += carry as u128;
(low, high)
}
}
#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")]
impl const CarryingMulAdd for i128 {
type Unsigned = u128;
#[inline]
fn carrying_mul_add(self, b: i128, c: i128, d: i128) -> (u128, i128) {
let (low, high) = wide_mul_u128(self as u128, b as u128);
let mut high = high as i128;
high = high.wrapping_add(i128::wrapping_mul(self >> 127, b));
high = high.wrapping_add(i128::wrapping_mul(self, b >> 127));
let (low, carry) = u128::overflowing_add(low, c as u128);
high = high.wrapping_add((carry as i128) + (c >> 127));
let (low, carry) = u128::overflowing_add(low, d as u128);
high = high.wrapping_add((carry as i128) + (d >> 127));
(low, high)
}
}

View file

@ -68,6 +68,7 @@ use crate::marker::{DiscriminantKind, Tuple};
use crate::mem::SizedTypeProperties;
use crate::{ptr, ub_checks};
pub mod fallback;
pub mod mir;
pub mod simd;
@ -3305,6 +3306,34 @@ pub const fn mul_with_overflow<T: Copy>(_x: T, _y: T) -> (T, bool) {
unimplemented!()
}
/// Performs full-width multiplication and addition with a carry:
/// `multiplier * multiplicand + addend + carry`.
///
/// This is possible without any overflow. For `uN`:
/// MAX * MAX + MAX + MAX
/// => (2ⁿ-1) × (2ⁿ-1) + (2ⁿ-1) + (2ⁿ-1)
/// => (2²ⁿ - 2ⁿ⁺¹ + 1) + (2ⁿ⁺¹ - 2)
/// => 2²ⁿ - 1
///
/// For `iN`, the upper bound is MIN * MIN + MAX + MAX => 2²ⁿ⁻² + 2ⁿ - 2,
/// and the lower bound is MAX * MIN + MIN + MIN => -2²ⁿ⁻² - 2ⁿ + 2ⁿ⁺¹.
///
/// This currently supports unsigned integers *only*, no signed ones.
/// The stabilized versions of this intrinsic are available on integers.
#[unstable(feature = "core_intrinsics", issue = "none")]
#[rustc_const_unstable(feature = "const_carrying_mul_add", issue = "85532")]
#[rustc_nounwind]
#[cfg_attr(not(bootstrap), rustc_intrinsic)]
#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_is_spec)]
pub const fn carrying_mul_add<T: ~const fallback::CarryingMulAdd<Unsigned = U>, U>(
multiplier: T,
multiplicand: T,
addend: T,
carry: T,
) -> (U, T) {
multiplier.carrying_mul_add(multiplicand, addend, carry)
}
/// Performs an exact division, resulting in undefined behavior where
/// `x % y != 0` or `y == 0` or `x == T::MIN && y == -1`
///
@ -3940,6 +3969,21 @@ pub const fn is_val_statically_known<T: Copy>(_arg: T) -> bool {
false
}
#[rustc_nounwind]
#[inline]
#[rustc_intrinsic]
#[rustc_intrinsic_const_stable_indirect]
#[rustc_allow_const_fn_unstable(const_swap_nonoverlapping)] // this is anyway not called since CTFE implements the intrinsic
#[cfg(bootstrap)]
pub const unsafe fn typed_swap<T>(x: *mut T, y: *mut T) {
// SAFETY: The caller provided single non-overlapping items behind
// pointers, so swapping them with `count: 1` is fine.
unsafe { ptr::swap_nonoverlapping(x, y, 1) };
}
#[cfg(bootstrap)]
pub use typed_swap as typed_swap_nonoverlapping;
/// Non-overlapping *typed* swap of a single value.
///
/// The codegen backends will replace this with a better implementation when
@ -3953,9 +3997,10 @@ pub const fn is_val_statically_known<T: Copy>(_arg: T) -> bool {
#[rustc_nounwind]
#[inline]
#[rustc_intrinsic]
// Const-unstable because `swap_nonoverlapping` is const-unstable.
#[rustc_const_unstable(feature = "const_typed_swap", issue = "none")]
pub const unsafe fn typed_swap<T>(x: *mut T, y: *mut T) {
#[rustc_intrinsic_const_stable_indirect]
#[rustc_allow_const_fn_unstable(const_swap_nonoverlapping)] // this is anyway not called since CTFE implements the intrinsic
#[cfg(not(bootstrap))]
pub const unsafe fn typed_swap_nonoverlapping<T>(x: *mut T, y: *mut T) {
// SAFETY: The caller provided single non-overlapping items behind
// pointers, so swapping them with `count: 1` is fine.
unsafe { ptr::swap_nonoverlapping(x, y, 1) };
@ -4364,13 +4409,11 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us
///
/// Behavior is undefined if any of the following conditions are violated:
///
/// * `src` must be [valid] for reads of `count * size_of::<T>()` bytes, and must remain valid even
/// when `dst` is written for `count * size_of::<T>()` bytes. (This means if the memory ranges
/// overlap, the two pointers must not be subject to aliasing restrictions relative to each
/// other.)
/// * `src` must be [valid] for reads of `count * size_of::<T>()` bytes.
///
/// * `dst` must be [valid] for writes of `count * size_of::<T>()` bytes, and must remain valid even
/// when `src` is read for `count * size_of::<T>()` bytes.
/// when `src` is read for `count * size_of::<T>()` bytes. (This means if the memory ranges
/// overlap, the `dst` pointer must not be invalidated by `src` reads.)
///
/// * Both `src` and `dst` must be properly aligned.
///

View file

@ -3051,6 +3051,7 @@ pub trait Iterator {
///
/// // we can still use `iter`, as there are more elements.
/// assert_eq!(iter.next(), Some(&-1));
/// assert_eq!(iter.next_back(), Some(&3));
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]

View file

@ -110,8 +110,8 @@
#![cfg_attr(bootstrap, feature(do_not_recommend))]
#![feature(array_ptr_get)]
#![feature(asm_experimental_arch)]
#![feature(const_carrying_mul_add)]
#![feature(const_eval_select)]
#![feature(const_typed_swap)]
#![feature(core_intrinsics)]
#![feature(coverage_attribute)]
#![feature(internal_impls_macro)]

View file

@ -725,12 +725,12 @@ pub unsafe fn uninitialized<T>() -> T {
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_swap", issue = "83163")]
#[rustc_const_stable(feature = "const_swap", since = "CURRENT_RUSTC_VERSION")]
#[rustc_diagnostic_item = "mem_swap"]
pub const fn swap<T>(x: &mut T, y: &mut T) {
// SAFETY: `&mut` guarantees these are typed readable and writable
// as well as non-overlapping.
unsafe { intrinsics::typed_swap(x, y) }
unsafe { intrinsics::typed_swap_nonoverlapping(x, y) }
}
/// Replaces `dest` with the default value of `T`, returning the previous `dest` value.

View file

@ -228,134 +228,6 @@ macro_rules! midpoint_impl {
};
}
macro_rules! widening_impl {
($SelfT:ty, $WideT:ty, $BITS:literal, unsigned) => {
/// Calculates the complete product `self * rhs` without the possibility to overflow.
///
/// This returns the low-order (wrapping) bits and the high-order (overflow) bits
/// of the result as two separate values, in that order.
///
/// If you also need to add a carry to the wide result, then you want
/// [`Self::carrying_mul`] instead.
///
/// # Examples
///
/// Basic usage:
///
/// Please note that this example is shared between integer types.
/// Which explains why `u32` is used here.
///
/// ```
/// #![feature(bigint_helper_methods)]
/// assert_eq!(5u32.widening_mul(2), (10, 0));
/// assert_eq!(1_000_000_000u32.widening_mul(10), (1410065408, 2));
/// ```
#[unstable(feature = "bigint_helper_methods", issue = "85532")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
pub const fn widening_mul(self, rhs: Self) -> (Self, Self) {
// note: longer-term this should be done via an intrinsic,
// but for now we can deal without an impl for u128/i128
// SAFETY: overflow will be contained within the wider types
let wide = unsafe { (self as $WideT).unchecked_mul(rhs as $WideT) };
(wide as $SelfT, (wide >> $BITS) as $SelfT)
}
/// Calculates the "full multiplication" `self * rhs + carry`
/// without the possibility to overflow.
///
/// This returns the low-order (wrapping) bits and the high-order (overflow) bits
/// of the result as two separate values, in that order.
///
/// Performs "long multiplication" which takes in an extra amount to add, and may return an
/// additional amount of overflow. This allows for chaining together multiple
/// multiplications to create "big integers" which represent larger values.
///
/// If you don't need the `carry`, then you can use [`Self::widening_mul`] instead.
///
/// # Examples
///
/// Basic usage:
///
/// Please note that this example is shared between integer types.
/// Which explains why `u32` is used here.
///
/// ```
/// #![feature(bigint_helper_methods)]
/// assert_eq!(5u32.carrying_mul(2, 0), (10, 0));
/// assert_eq!(5u32.carrying_mul(2, 10), (20, 0));
/// assert_eq!(1_000_000_000u32.carrying_mul(10, 0), (1410065408, 2));
/// assert_eq!(1_000_000_000u32.carrying_mul(10, 10), (1410065418, 2));
#[doc = concat!("assert_eq!(",
stringify!($SelfT), "::MAX.carrying_mul(", stringify!($SelfT), "::MAX, ", stringify!($SelfT), "::MAX), ",
"(0, ", stringify!($SelfT), "::MAX));"
)]
/// ```
///
/// This is the core operation needed for scalar multiplication when
/// implementing it for wider-than-native types.
///
/// ```
/// #![feature(bigint_helper_methods)]
/// fn scalar_mul_eq(little_endian_digits: &mut Vec<u16>, multiplicand: u16) {
/// let mut carry = 0;
/// for d in little_endian_digits.iter_mut() {
/// (*d, carry) = d.carrying_mul(multiplicand, carry);
/// }
/// if carry != 0 {
/// little_endian_digits.push(carry);
/// }
/// }
///
/// let mut v = vec![10, 20];
/// scalar_mul_eq(&mut v, 3);
/// assert_eq!(v, [30, 60]);
///
/// assert_eq!(0x87654321_u64 * 0xFEED, 0x86D3D159E38D);
/// let mut v = vec![0x4321, 0x8765];
/// scalar_mul_eq(&mut v, 0xFEED);
/// assert_eq!(v, [0xE38D, 0xD159, 0x86D3]);
/// ```
///
/// If `carry` is zero, this is similar to [`overflowing_mul`](Self::overflowing_mul),
/// except that it gives the value of the overflow instead of just whether one happened:
///
/// ```
/// #![feature(bigint_helper_methods)]
/// let r = u8::carrying_mul(7, 13, 0);
/// assert_eq!((r.0, r.1 != 0), u8::overflowing_mul(7, 13));
/// let r = u8::carrying_mul(13, 42, 0);
/// assert_eq!((r.0, r.1 != 0), u8::overflowing_mul(13, 42));
/// ```
///
/// The value of the first field in the returned tuple matches what you'd get
/// by combining the [`wrapping_mul`](Self::wrapping_mul) and
/// [`wrapping_add`](Self::wrapping_add) methods:
///
/// ```
/// #![feature(bigint_helper_methods)]
/// assert_eq!(
/// 789_u16.carrying_mul(456, 123).0,
/// 789_u16.wrapping_mul(456).wrapping_add(123),
/// );
/// ```
#[unstable(feature = "bigint_helper_methods", issue = "85532")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
pub const fn carrying_mul(self, rhs: Self, carry: Self) -> (Self, Self) {
// note: longer-term this should be done via an intrinsic,
// but for now we can deal without an impl for u128/i128
// SAFETY: overflow will be contained within the wider types
let wide = unsafe {
(self as $WideT).unchecked_mul(rhs as $WideT).unchecked_add(carry as $WideT)
};
(wide as $SelfT, (wide >> $BITS) as $SelfT)
}
};
}
impl i8 {
int_impl! {
Self = i8,
@ -576,7 +448,6 @@ impl u8 {
from_xe_bytes_doc = u8_xe_bytes_doc!(),
bound_condition = "",
}
widening_impl! { u8, u16, 8, unsigned }
midpoint_impl! { u8, u16, unsigned }
/// Checks if the value is within the ASCII range.
@ -1192,7 +1063,6 @@ impl u16 {
from_xe_bytes_doc = "",
bound_condition = "",
}
widening_impl! { u16, u32, 16, unsigned }
midpoint_impl! { u16, u32, unsigned }
/// Checks if the value is a Unicode surrogate code point, which are disallowed values for [`char`].
@ -1240,7 +1110,6 @@ impl u32 {
from_xe_bytes_doc = "",
bound_condition = "",
}
widening_impl! { u32, u64, 32, unsigned }
midpoint_impl! { u32, u64, unsigned }
}
@ -1264,7 +1133,6 @@ impl u64 {
from_xe_bytes_doc = "",
bound_condition = "",
}
widening_impl! { u64, u128, 64, unsigned }
midpoint_impl! { u64, u128, unsigned }
}
@ -1314,7 +1182,6 @@ impl usize {
from_xe_bytes_doc = usize_isize_from_xe_bytes_doc!(),
bound_condition = " on 16-bit targets",
}
widening_impl! { usize, u32, 16, unsigned }
midpoint_impl! { usize, u32, unsigned }
}
@ -1339,7 +1206,6 @@ impl usize {
from_xe_bytes_doc = usize_isize_from_xe_bytes_doc!(),
bound_condition = " on 32-bit targets",
}
widening_impl! { usize, u64, 32, unsigned }
midpoint_impl! { usize, u64, unsigned }
}
@ -1364,7 +1230,6 @@ impl usize {
from_xe_bytes_doc = usize_isize_from_xe_bytes_doc!(),
bound_condition = " on 64-bit targets",
}
widening_impl! { usize, u128, 64, unsigned }
midpoint_impl! { usize, u128, unsigned }
}

View file

@ -3347,6 +3347,122 @@ macro_rules! uint_impl {
unsafe { mem::transmute(bytes) }
}
/// Calculates the complete product `self * rhs` without the possibility to overflow.
///
/// This returns the low-order (wrapping) bits and the high-order (overflow) bits
/// of the result as two separate values, in that order.
///
/// If you also need to add a carry to the wide result, then you want
/// [`Self::carrying_mul`] instead.
///
/// # Examples
///
/// Basic usage:
///
/// Please note that this example is shared between integer types.
/// Which explains why `u32` is used here.
///
/// ```
/// #![feature(bigint_helper_methods)]
/// assert_eq!(5u32.widening_mul(2), (10, 0));
/// assert_eq!(1_000_000_000u32.widening_mul(10), (1410065408, 2));
/// ```
#[unstable(feature = "bigint_helper_methods", issue = "85532")]
#[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
pub const fn widening_mul(self, rhs: Self) -> (Self, Self) {
Self::carrying_mul(self, rhs, 0)
}
/// Calculates the "full multiplication" `self * rhs + carry`
/// without the possibility to overflow.
///
/// This returns the low-order (wrapping) bits and the high-order (overflow) bits
/// of the result as two separate values, in that order.
///
/// Performs "long multiplication" which takes in an extra amount to add, and may return an
/// additional amount of overflow. This allows for chaining together multiple
/// multiplications to create "big integers" which represent larger values.
///
/// If you don't need the `carry`, then you can use [`Self::widening_mul`] instead.
///
/// # Examples
///
/// Basic usage:
///
/// Please note that this example is shared between integer types.
/// Which explains why `u32` is used here.
///
/// ```
/// #![feature(bigint_helper_methods)]
/// assert_eq!(5u32.carrying_mul(2, 0), (10, 0));
/// assert_eq!(5u32.carrying_mul(2, 10), (20, 0));
/// assert_eq!(1_000_000_000u32.carrying_mul(10, 0), (1410065408, 2));
/// assert_eq!(1_000_000_000u32.carrying_mul(10, 10), (1410065418, 2));
#[doc = concat!("assert_eq!(",
stringify!($SelfT), "::MAX.carrying_mul(", stringify!($SelfT), "::MAX, ", stringify!($SelfT), "::MAX), ",
"(0, ", stringify!($SelfT), "::MAX));"
)]
/// ```
///
/// This is the core operation needed for scalar multiplication when
/// implementing it for wider-than-native types.
///
/// ```
/// #![feature(bigint_helper_methods)]
/// fn scalar_mul_eq(little_endian_digits: &mut Vec<u16>, multiplicand: u16) {
/// let mut carry = 0;
/// for d in little_endian_digits.iter_mut() {
/// (*d, carry) = d.carrying_mul(multiplicand, carry);
/// }
/// if carry != 0 {
/// little_endian_digits.push(carry);
/// }
/// }
///
/// let mut v = vec![10, 20];
/// scalar_mul_eq(&mut v, 3);
/// assert_eq!(v, [30, 60]);
///
/// assert_eq!(0x87654321_u64 * 0xFEED, 0x86D3D159E38D);
/// let mut v = vec![0x4321, 0x8765];
/// scalar_mul_eq(&mut v, 0xFEED);
/// assert_eq!(v, [0xE38D, 0xD159, 0x86D3]);
/// ```
///
/// If `carry` is zero, this is similar to [`overflowing_mul`](Self::overflowing_mul),
/// except that it gives the value of the overflow instead of just whether one happened:
///
/// ```
/// #![feature(bigint_helper_methods)]
/// let r = u8::carrying_mul(7, 13, 0);
/// assert_eq!((r.0, r.1 != 0), u8::overflowing_mul(7, 13));
/// let r = u8::carrying_mul(13, 42, 0);
/// assert_eq!((r.0, r.1 != 0), u8::overflowing_mul(13, 42));
/// ```
///
/// The value of the first field in the returned tuple matches what you'd get
/// by combining the [`wrapping_mul`](Self::wrapping_mul) and
/// [`wrapping_add`](Self::wrapping_add) methods:
///
/// ```
/// #![feature(bigint_helper_methods)]
/// assert_eq!(
/// 789_u16.carrying_mul(456, 123).0,
/// 789_u16.wrapping_mul(456).wrapping_add(123),
/// );
/// ```
#[unstable(feature = "bigint_helper_methods", issue = "85532")]
#[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
pub const fn carrying_mul(self, rhs: Self, carry: Self) -> (Self, Self) {
intrinsics::carrying_mul_add(self, rhs, 0, carry)
}
/// New code should prefer to use
#[doc = concat!("[`", stringify!($SelfT), "::MIN", "`] instead.")]
///

View file

@ -595,7 +595,7 @@
//! [drop-impl]: self#implementing-drop-for-types-with-address-sensitive-states
//!
//! The [`drop`] function takes [`&mut self`], but this is called *even if that `self` has been
//! pinned*! Implementing [`Drop`] for a type with address-sensitive states, because if `self` was
//! pinned*! Implementing [`Drop`] for a type with address-sensitive states requires some care, because if `self` was
//! indeed in an address-sensitive state before [`drop`] was called, it is as if the compiler
//! automatically called [`Pin::get_unchecked_mut`].
//!

View file

@ -1009,9 +1009,8 @@ pub const fn slice_from_raw_parts_mut<T>(data: *mut T, len: usize) -> *mut [T] {
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_swap", issue = "83163")]
#[rustc_const_stable(feature = "const_swap", since = "CURRENT_RUSTC_VERSION")]
#[rustc_diagnostic_item = "ptr_swap"]
#[rustc_const_stable_indirect]
pub const unsafe fn swap<T>(x: *mut T, y: *mut T) {
// Give ourselves some scratch space to work with.
// We do not have to worry about drops: `MaybeUninit` does nothing when dropped.

View file

@ -1594,7 +1594,7 @@ impl<T: ?Sized> *mut T {
///
/// [`ptr::swap`]: crate::ptr::swap()
#[stable(feature = "pointer_methods", since = "1.26.0")]
#[rustc_const_unstable(feature = "const_swap", issue = "83163")]
#[rustc_const_stable(feature = "const_swap", since = "CURRENT_RUSTC_VERSION")]
#[inline(always)]
pub const unsafe fn swap(self, with: *mut T)
where

View file

@ -1146,7 +1146,7 @@ impl<T: ?Sized> NonNull<T> {
/// [`ptr::swap`]: crate::ptr::swap()
#[inline(always)]
#[stable(feature = "non_null_convenience", since = "1.80.0")]
#[rustc_const_unstable(feature = "const_swap", issue = "83163")]
#[rustc_const_stable(feature = "const_swap", since = "CURRENT_RUSTC_VERSION")]
pub const unsafe fn swap(self, with: NonNull<T>)
where
T: Sized,

View file

@ -913,7 +913,7 @@ impl<T> [T] {
/// assert!(v == ["a", "b", "e", "d", "c"]);
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_swap", issue = "83163")]
#[rustc_const_stable(feature = "const_swap", since = "CURRENT_RUSTC_VERSION")]
#[inline]
#[track_caller]
pub const fn swap(&mut self, a: usize, b: usize) {

View file

@ -125,3 +125,71 @@ fn test_three_way_compare_in_const_contexts() {
assert_eq!(SIGNED_EQUAL, Equal);
assert_eq!(SIGNED_GREATER, Greater);
}
fn fallback_cma<T: core::intrinsics::fallback::CarryingMulAdd>(
a: T,
b: T,
c: T,
d: T,
) -> (T::Unsigned, T) {
a.carrying_mul_add(b, c, d)
}
#[test]
fn carrying_mul_add_fallback_u32() {
let r = fallback_cma::<u32>(0x9e37_79b9, 0x7f4a_7c15, 0xf39c_c060, 0x5ced_c834);
assert_eq!(r, (0x2087_20c1, 0x4eab_8e1d));
let r = fallback_cma::<u32>(0x1082_276b, 0xf3a2_7251, 0xf86c_6a11, 0xd0c1_8e95);
assert_eq!(r, (0x7aa0_1781, 0x0fb6_0528));
}
#[test]
fn carrying_mul_add_fallback_i32() {
let r = fallback_cma::<i32>(-1, -1, -1, -1);
assert_eq!(r, (u32::MAX, -1));
let r = fallback_cma::<i32>(1, -1, 1, 1);
assert_eq!(r, (1, 0));
}
#[test]
fn carrying_mul_add_fallback_u128() {
assert_eq!(fallback_cma::<u128>(u128::MAX, u128::MAX, 0, 0), (1, u128::MAX - 1));
assert_eq!(fallback_cma::<u128>(1, 1, 1, 1), (3, 0));
assert_eq!(fallback_cma::<u128>(0, 0, u128::MAX, u128::MAX), (u128::MAX - 1, 1));
assert_eq!(
fallback_cma::<u128>(u128::MAX, u128::MAX, u128::MAX, u128::MAX),
(u128::MAX, u128::MAX),
);
let r = fallback_cma::<u128>(
0x243f6a8885a308d313198a2e03707344,
0xa4093822299f31d0082efa98ec4e6c89,
0x452821e638d01377be5466cf34e90c6c,
0xc0ac29b7c97c50dd3f84d5b5b5470917,
);
assert_eq!(r, (0x8050ec20ed554e40338d277e00b674e7, 0x1739ee6cea07da409182d003859b59d8));
let r = fallback_cma::<u128>(
0x9216d5d98979fb1bd1310ba698dfb5ac,
0x2ffd72dbd01adfb7b8e1afed6a267e96,
0xba7c9045f12c7f9924a19947b3916cf7,
0x0801f2e2858efc16636920d871574e69,
);
assert_eq!(r, (0x185525545fdb2fefb502a3a602efd628, 0x1b62d35fe3bff6b566f99667ef7ebfd6));
}
#[test]
fn carrying_mul_add_fallback_i128() {
assert_eq!(fallback_cma::<i128>(-1, -1, 0, 0), (1, 0));
let r = fallback_cma::<i128>(-1, -1, -1, -1);
assert_eq!(r, (u128::MAX, -1));
let r = fallback_cma::<i128>(1, -1, 1, 1);
assert_eq!(r, (1, 0));
assert_eq!(
fallback_cma::<i128>(i128::MAX, i128::MAX, i128::MAX, i128::MAX),
(u128::MAX, i128::MAX / 2),
);
assert_eq!(
fallback_cma::<i128>(i128::MIN, i128::MIN, i128::MAX, i128::MAX),
(u128::MAX - 1, -(i128::MIN / 2)),
);
}

View file

@ -15,10 +15,10 @@
#![feature(clone_to_uninit)]
#![feature(const_black_box)]
#![feature(const_eval_select)]
#![feature(const_swap)]
#![feature(const_swap_nonoverlapping)]
#![feature(const_trait_impl)]
#![feature(core_intrinsics)]
#![feature(core_intrinsics_fallbacks)]
#![feature(core_io_borrowed_buf)]
#![feature(core_private_bignum)]
#![feature(core_private_diy_float)]

View file

@ -17,7 +17,7 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] }
panic_unwind = { path = "../panic_unwind", optional = true }
panic_abort = { path = "../panic_abort" }
core = { path = "../core", public = true }
compiler_builtins = { version = "=0.1.138" }
compiler_builtins = { version = "=0.1.140" }
unwind = { path = "../unwind" }
hashbrown = { version = "0.15", default-features = false, features = [
'rustc-dep-of-std',

View file

@ -1446,6 +1446,11 @@ impl<K, V, const N: usize> From<[(K, V); N]> for HashMap<K, V, RandomState>
where
K: Eq + Hash,
{
/// Converts a `[(K, V); N]` into a `HashMap<K, V>`.
///
/// If any entries in the array have equal keys,
/// all but one of the corresponding values will be dropped.
///
/// # Examples
///
/// ```
@ -3219,6 +3224,10 @@ where
K: Eq + Hash,
S: BuildHasher + Default,
{
/// Constructs a `HashMap<K, V>` from an iterator of key-value pairs.
///
/// If the iterator produces any pairs with equal keys,
/// all but one of the corresponding values will be dropped.
fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> HashMap<K, V, S> {
let mut map = HashMap::with_hasher(Default::default());
map.extend(iter);

View file

@ -1091,6 +1091,11 @@ impl<T, const N: usize> From<[T; N]> for HashSet<T, RandomState>
where
T: Eq + Hash,
{
/// Converts a `[T; N]` into a `HashSet<T>`.
///
/// If the array contains any equal values,
/// all but one will be dropped.
///
/// # Examples
///
/// ```

View file

@ -179,19 +179,19 @@ pub use core::ffi::{
c_ulong, c_ulonglong, c_ushort,
};
#[doc(no_inline)]
#[doc(inline)]
#[stable(feature = "cstr_from_bytes_until_nul", since = "1.69.0")]
pub use self::c_str::FromBytesUntilNulError;
#[doc(no_inline)]
#[doc(inline)]
#[stable(feature = "cstr_from_bytes", since = "1.10.0")]
pub use self::c_str::FromBytesWithNulError;
#[doc(no_inline)]
#[doc(inline)]
#[stable(feature = "cstring_from_vec_with_nul", since = "1.58.0")]
pub use self::c_str::FromVecWithNulError;
#[doc(no_inline)]
#[doc(inline)]
#[stable(feature = "cstring_into", since = "1.7.0")]
pub use self::c_str::IntoStringError;
#[doc(no_inline)]
#[doc(inline)]
#[stable(feature = "rust1", since = "1.0.0")]
pub use self::c_str::NulError;
#[doc(inline)]

View file

@ -1953,3 +1953,32 @@ fn test_rename_directory_to_non_empty_directory() {
error!(fs::rename(source_path, target_path), 145); // ERROR_DIR_NOT_EMPTY
}
#[test]
fn test_rename_symlink() {
let tmpdir = tmpdir();
let original = tmpdir.join("original");
let dest = tmpdir.join("dest");
let not_exist = Path::new("does not exist");
symlink_file(not_exist, &original).unwrap();
fs::rename(&original, &dest).unwrap();
// Make sure that renaming `original` to `dest` preserves the symlink.
assert_eq!(fs::read_link(&dest).unwrap().as_path(), not_exist);
}
#[test]
#[cfg(windows)]
fn test_rename_junction() {
let tmpdir = tmpdir();
let original = tmpdir.join("original");
let dest = tmpdir.join("dest");
let not_exist = Path::new("does not exist");
junction_point(&not_exist, &original).unwrap();
fs::rename(&original, &dest).unwrap();
// Make sure that renaming `original` to `dest` preserves the junction point.
// Junction links are always absolute so we just check the file name is correct.
assert_eq!(fs::read_link(&dest).unwrap().file_name(), Some(not_exist.as_os_str()));
}

View file

@ -10,26 +10,22 @@ use crate::sync::{Condvar, Mutex};
/// # Examples
///
/// ```
/// use std::sync::{Arc, Barrier};
/// use std::sync::Barrier;
/// use std::thread;
///
/// let n = 10;
/// let mut handles = Vec::with_capacity(n);
/// let barrier = Arc::new(Barrier::new(n));
/// for _ in 0..n {
/// let c = Arc::clone(&barrier);
/// // The same messages will be printed together.
/// // You will NOT see any interleaving.
/// handles.push(thread::spawn(move || {
/// println!("before wait");
/// c.wait();
/// println!("after wait");
/// }));
/// }
/// // Wait for other threads to finish.
/// for handle in handles {
/// handle.join().unwrap();
/// }
/// let barrier = Barrier::new(n);
/// thread::scope(|s| {
/// for _ in 0..n {
/// // The same messages will be printed together.
/// // You will NOT see any interleaving.
/// s.spawn(|| {
/// println!("before wait");
/// barrier.wait();
/// println!("after wait");
/// });
/// }
/// });
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Barrier {
@ -105,26 +101,22 @@ impl Barrier {
/// # Examples
///
/// ```
/// use std::sync::{Arc, Barrier};
/// use std::sync::Barrier;
/// use std::thread;
///
/// let n = 10;
/// let mut handles = Vec::with_capacity(n);
/// let barrier = Arc::new(Barrier::new(n));
/// for _ in 0..n {
/// let c = Arc::clone(&barrier);
/// // The same messages will be printed together.
/// // You will NOT see any interleaving.
/// handles.push(thread::spawn(move || {
/// println!("before wait");
/// c.wait();
/// println!("after wait");
/// }));
/// }
/// // Wait for other threads to finish.
/// for handle in handles {
/// handle.join().unwrap();
/// }
/// let barrier = Barrier::new(n);
/// thread::scope(|s| {
/// for _ in 0..n {
/// // The same messages will be printed together.
/// // You will NOT see any interleaving.
/// s.spawn(|| {
/// println!("before wait");
/// barrier.wait();
/// println!("after wait");
/// });
/// }
/// });
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn wait(&self) -> BarrierWaitResult {

View file

@ -168,7 +168,8 @@ cfg_has_statx! {{
) -> c_int
}
if STATX_SAVED_STATE.load(Ordering::Relaxed) == STATX_STATE::Unavailable as u8 {
let statx_availability = STATX_SAVED_STATE.load(Ordering::Relaxed);
if statx_availability == STATX_STATE::Unavailable as u8 {
return None;
}
@ -200,6 +201,9 @@ cfg_has_statx! {{
return None;
}
}
if statx_availability == STATX_STATE::Unknown as u8 {
STATX_SAVED_STATE.store(STATX_STATE::Present as u8, Ordering::Relaxed);
}
// We cannot fill `stat64` exhaustively because of private padding fields.
let mut stat: stat64 = mem::zeroed();
@ -1944,7 +1948,7 @@ fn open_from(from: &Path) -> io::Result<(crate::fs::File, crate::fs::Metadata)>
#[cfg(target_os = "espidf")]
fn open_to_and_set_permissions(
to: &Path,
_reader_metadata: crate::fs::Metadata,
_reader_metadata: &crate::fs::Metadata,
) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
use crate::fs::OpenOptions;
let writer = OpenOptions::new().open(to)?;
@ -1955,7 +1959,7 @@ fn open_to_and_set_permissions(
#[cfg(not(target_os = "espidf"))]
fn open_to_and_set_permissions(
to: &Path,
reader_metadata: crate::fs::Metadata,
reader_metadata: &crate::fs::Metadata,
) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
use crate::fs::OpenOptions;
use crate::os::unix::fs::{OpenOptionsExt, PermissionsExt};
@ -1980,30 +1984,63 @@ fn open_to_and_set_permissions(
Ok((writer, writer_metadata))
}
#[cfg(not(any(target_os = "linux", target_os = "android", target_vendor = "apple")))]
pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
let (mut reader, reader_metadata) = open_from(from)?;
let (mut writer, _) = open_to_and_set_permissions(to, reader_metadata)?;
mod cfm {
use crate::fs::{File, Metadata};
use crate::io::{BorrowedCursor, IoSlice, IoSliceMut, Read, Result, Write};
io::copy(&mut reader, &mut writer)
}
#[allow(dead_code)]
pub struct CachedFileMetadata(pub File, pub Metadata);
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
let (mut reader, reader_metadata) = open_from(from)?;
let max_len = u64::MAX;
let (mut writer, _) = open_to_and_set_permissions(to, reader_metadata)?;
use super::kernel_copy::{CopyResult, copy_regular_files};
match copy_regular_files(reader.as_raw_fd(), writer.as_raw_fd(), max_len) {
CopyResult::Ended(bytes) => Ok(bytes),
CopyResult::Error(e, _) => Err(e),
CopyResult::Fallback(written) => match io::copy::generic_copy(&mut reader, &mut writer) {
Ok(bytes) => Ok(bytes + written),
Err(e) => Err(e),
},
impl Read for CachedFileMetadata {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
self.0.read(buf)
}
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize> {
self.0.read_vectored(bufs)
}
fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> Result<()> {
self.0.read_buf(cursor)
}
#[inline]
fn is_read_vectored(&self) -> bool {
self.0.is_read_vectored()
}
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> {
self.0.read_to_end(buf)
}
fn read_to_string(&mut self, buf: &mut String) -> Result<usize> {
self.0.read_to_string(buf)
}
}
impl Write for CachedFileMetadata {
fn write(&mut self, buf: &[u8]) -> Result<usize> {
self.0.write(buf)
}
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result<usize> {
self.0.write_vectored(bufs)
}
#[inline]
fn is_write_vectored(&self) -> bool {
self.0.is_write_vectored()
}
#[inline]
fn flush(&mut self) -> Result<()> {
self.0.flush()
}
}
}
#[cfg(any(target_os = "linux", target_os = "android"))]
pub(crate) use cfm::CachedFileMetadata;
#[cfg(not(target_vendor = "apple"))]
pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
let (reader, reader_metadata) = open_from(from)?;
let (writer, writer_metadata) = open_to_and_set_permissions(to, &reader_metadata)?;
io::copy(
&mut cfm::CachedFileMetadata(reader, reader_metadata),
&mut cfm::CachedFileMetadata(writer, writer_metadata),
)
}
#[cfg(target_vendor = "apple")]
@ -2040,7 +2077,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
}
// Fall back to using `fcopyfile` if `fclonefileat` does not succeed.
let (writer, writer_metadata) = open_to_and_set_permissions(to, reader_metadata)?;
let (writer, writer_metadata) = open_to_and_set_permissions(to, &reader_metadata)?;
// We ensure that `FreeOnDrop` never contains a null pointer so it is
// always safe to call `copyfile_state_free`

View file

@ -65,6 +65,7 @@ use crate::process::{ChildStderr, ChildStdin, ChildStdout};
use crate::ptr;
use crate::sync::atomic::{AtomicBool, AtomicU8, Ordering};
use crate::sys::cvt;
use crate::sys::fs::CachedFileMetadata;
use crate::sys::weak::syscall;
#[cfg(test)]
@ -192,7 +193,7 @@ impl<R: CopyRead, W: CopyWrite> SpecCopy for Copier<'_, '_, R, W> {
let w_cfg = writer.properties();
// before direct operations on file descriptors ensure that all source and sink buffers are empty
let mut flush = || -> crate::io::Result<u64> {
let mut flush = || -> Result<u64> {
let bytes = reader.drain_to(writer, u64::MAX)?;
// BufWriter buffered bytes have already been accounted for in earlier write() calls
writer.flush()?;
@ -537,6 +538,18 @@ impl<T: ?Sized + CopyWrite> CopyWrite for BufWriter<T> {
}
}
impl CopyRead for CachedFileMetadata {
fn properties(&self) -> CopyParams {
CopyParams(FdMeta::Metadata(self.1.clone()), Some(self.0.as_raw_fd()))
}
}
impl CopyWrite for CachedFileMetadata {
fn properties(&self) -> CopyParams {
CopyParams(FdMeta::Metadata(self.1.clone()), Some(self.0.as_raw_fd()))
}
}
fn fd_to_meta<T: AsRawFd>(fd: &T) -> FdMeta {
let fd = fd.as_raw_fd();
let file: ManuallyDrop<File> = ManuallyDrop::new(unsafe { File::from_raw_fd(fd) });

View file

@ -2426,6 +2426,7 @@ Windows.Win32.System.Console.ENABLE_VIRTUAL_TERMINAL_PROCESSING
Windows.Win32.System.Console.ENABLE_WINDOW_INPUT
Windows.Win32.System.Console.ENABLE_WRAP_AT_EOL_OUTPUT
Windows.Win32.System.Console.GetConsoleMode
Windows.Win32.System.Console.GetConsoleOutputCP
Windows.Win32.System.Console.GetStdHandle
Windows.Win32.System.Console.ReadConsoleW
Windows.Win32.System.Console.STD_ERROR_HANDLE

View file

@ -34,6 +34,7 @@ windows_targets::link!("kernel32.dll" "system" fn FreeEnvironmentStringsW(penv :
windows_targets::link!("kernel32.dll" "system" fn GetActiveProcessorCount(groupnumber : u16) -> u32);
windows_targets::link!("kernel32.dll" "system" fn GetCommandLineW() -> PCWSTR);
windows_targets::link!("kernel32.dll" "system" fn GetConsoleMode(hconsolehandle : HANDLE, lpmode : *mut CONSOLE_MODE) -> BOOL);
windows_targets::link!("kernel32.dll" "system" fn GetConsoleOutputCP() -> u32);
windows_targets::link!("kernel32.dll" "system" fn GetCurrentDirectoryW(nbufferlength : u32, lpbuffer : PWSTR) -> u32);
windows_targets::link!("kernel32.dll" "system" fn GetCurrentProcess() -> HANDLE);
windows_targets::link!("kernel32.dll" "system" fn GetCurrentProcessId() -> u32);
@ -3333,6 +3334,7 @@ pub struct XSAVE_FORMAT {
pub XmmRegisters: [M128A; 8],
pub Reserved4: [u8; 224],
}
#[cfg(target_arch = "arm")]
#[repr(C)]
pub struct WSADATA {

View file

@ -323,7 +323,7 @@ impl File {
let alloc = c::FILE_ALLOCATION_INFO { AllocationSize: 0 };
let result = c::SetFileInformationByHandle(
handle.as_raw_handle(),
c::FileEndOfFileInfo,
c::FileAllocationInfo,
(&raw const alloc).cast::<c_void>(),
mem::size_of::<c::FILE_ALLOCATION_INFO>() as u32,
);
@ -1295,15 +1295,18 @@ pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
} else {
// SAFETY: The struct has been initialized by GetFileInformationByHandleEx
let file_attribute_tag_info = unsafe { file_attribute_tag_info.assume_init() };
let file_type = FileType::new(
file_attribute_tag_info.FileAttributes,
file_attribute_tag_info.ReparseTag,
);
if file_attribute_tag_info.FileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0
&& file_attribute_tag_info.ReparseTag != c::IO_REPARSE_TAG_MOUNT_POINT
{
// The file is not a mount point: Reopen the file without inhibiting reparse point behavior.
None
} else {
// The file is a mount point: Don't reopen the file so that the mount point gets renamed.
if file_type.is_symlink() {
// The file is a mount point, junction point or symlink so
// don't reopen the file so that the link gets renamed.
Some(Ok(handle))
} else {
// Otherwise reopen the file without inhibiting reparse point behavior.
None
}
}
}

View file

@ -84,21 +84,43 @@ fn is_console(handle: c::HANDLE) -> bool {
unsafe { c::GetConsoleMode(handle, &mut mode) != 0 }
}
/// Returns true if the attached console's code page is currently UTF-8.
#[cfg(not(target_vendor = "win7"))]
fn is_utf8_console() -> bool {
unsafe { c::GetConsoleOutputCP() == c::CP_UTF8 }
}
#[cfg(target_vendor = "win7")]
fn is_utf8_console() -> bool {
// Windows 7 has a fun "feature" where WriteFile on a console handle will return
// the number of UTF-16 code units written and not the number of bytes from the input string.
// So we always claim the console isn't UTF-8 to trigger the WriteConsole fallback code.
false
}
fn write(handle_id: u32, data: &[u8], incomplete_utf8: &mut IncompleteUtf8) -> io::Result<usize> {
if data.is_empty() {
return Ok(0);
}
let handle = get_handle(handle_id)?;
if !is_console(handle) {
if !is_console(handle) || is_utf8_console() {
unsafe {
let handle = Handle::from_raw_handle(handle);
let ret = handle.write(data);
let _ = handle.into_raw_handle(); // Don't close the handle
return ret;
}
} else {
write_console_utf16(data, incomplete_utf8, handle)
}
}
fn write_console_utf16(
data: &[u8],
incomplete_utf8: &mut IncompleteUtf8,
handle: c::HANDLE,
) -> io::Result<usize> {
if incomplete_utf8.len > 0 {
assert!(
incomplete_utf8.len < 4,

View file

@ -136,7 +136,7 @@ pub(crate) fn set_current(thread: Thread) -> Result<(), Thread> {
/// one thread and is guaranteed not to call the global allocator.
#[inline]
pub(crate) fn current_id() -> ThreadId {
// If accessing the persistant thread ID takes multiple TLS accesses, try
// If accessing the persistent thread ID takes multiple TLS accesses, try
// to retrieve it from the current thread handle, which will only take one
// TLS access.
if !id::CHEAP {

View file

@ -22,7 +22,7 @@ cfg-if = "1.0"
libc = { version = "0.2.140", features = ['rustc-dep-of-std'], default-features = false }
[target.'cfg(target_os = "xous")'.dependencies]
unwinding = { version = "0.2.3", features = ['rustc-dep-of-std', 'unwinder', 'fde-custom'], default-features = false }
unwinding = { version = "0.2.5", features = ['rustc-dep-of-std', 'unwinder', 'fde-custom'], default-features = false }
[features]

View file

@ -7,7 +7,6 @@ default-run = "bootstrap"
[features]
build-metrics = ["sysinfo"]
bootstrap-self-test = [] # enabled in the bootstrap unit tests
[lib]
path = "src/lib.rs"

Some files were not shown because too many files have changed in this diff Show more