Auto merge of #3306 - rust-lang:rustup-2024-02-19, r=RalfJung

Automatic Rustup
This commit is contained in:
bors 2024-02-19 06:59:32 +00:00
commit 010f029c90
460 changed files with 10180 additions and 3980 deletions

View file

@ -931,43 +931,19 @@ dependencies = [
[[package]]
name = "darling"
version = "0.14.4"
version = "0.20.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850"
checksum = "c376d08ea6aa96aafe61237c7200d1241cb177b7d3a542d791f2d118e9cbb955"
dependencies = [
"darling_core 0.14.4",
"darling_macro 0.14.4",
]
[[package]]
name = "darling"
version = "0.20.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc5d6b04b3fd0ba9926f945895de7d806260a2d7431ba82e7edaecb043c4c6b8"
dependencies = [
"darling_core 0.20.5",
"darling_macro 0.20.5",
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.14.4"
version = "0.20.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim 0.10.0",
"syn 1.0.109",
]
[[package]]
name = "darling_core"
version = "0.20.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04e48a959bcd5c761246f5d090ebc2fbf7b9cd527a492b07a67510c108f1e7e3"
checksum = "33043dcd19068b8192064c704b3f83eb464f91f1ff527b44a4e2b08d9cdb8855"
dependencies = [
"fnv",
"ident_case",
@ -979,22 +955,11 @@ dependencies = [
[[package]]
name = "darling_macro"
version = "0.14.4"
version = "0.20.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e"
checksum = "c5a91391accf613803c2a9bf9abccdbaa07c54b4244a5b64883f9c3c137c86be"
dependencies = [
"darling_core 0.14.4",
"quote",
"syn 1.0.109",
]
[[package]]
name = "darling_macro"
version = "0.20.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d1545d67a2149e1d93b7e5c7752dce5a7426eb5d1357ddcfd89336b94444f77"
dependencies = [
"darling_core 0.20.5",
"darling_core",
"quote",
"syn 2.0.48",
]
@ -1036,33 +1001,33 @@ dependencies = [
[[package]]
name = "derive_builder"
version = "0.12.0"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d67778784b508018359cbc8696edb3db78160bab2c2a28ba7f56ef6932997f8"
checksum = "0350b5cb0331628a5916d6c5c0b72e97393b8b6b03b47a9284f4e7f5a405ffd7"
dependencies = [
"derive_builder_macro",
]
[[package]]
name = "derive_builder_core"
version = "0.12.0"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f"
checksum = "d48cda787f839151732d396ac69e3473923d54312c070ee21e9effcaa8ca0b1d"
dependencies = [
"darling 0.14.4",
"darling",
"proc-macro2",
"quote",
"syn 1.0.109",
"syn 2.0.48",
]
[[package]]
name = "derive_builder_macro"
version = "0.12.0"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e"
checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b"
dependencies = [
"derive_builder_core",
"syn 1.0.109",
"syn 2.0.48",
]
[[package]]
@ -1084,7 +1049,7 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e8ef033054e131169b8f0f9a7af8f5533a9436fadf3c500ed547f730f07090d"
dependencies = [
"darling 0.20.5",
"darling",
"proc-macro2",
"quote",
"syn 2.0.48",
@ -6206,11 +6171,12 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.48.0"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
dependencies = [
"windows-targets 0.48.5",
"windows-core",
"windows-targets 0.52.0",
]
[[package]]

View file

@ -112,8 +112,8 @@ fn start<T: Termination + 'static>(
static mut NUM: u8 = 6 * 7;
// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint
#[allow(static_mut_ref)]
// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_refs` lint
#[allow(static_mut_refs)]
static NUM_REF: &'static u8 = unsafe { &NUM };
unsafe fn zeroed<T>() -> T {

View file

@ -99,8 +99,8 @@ fn start<T: Termination + 'static>(
static mut NUM: u8 = 6 * 7;
// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint
#[allow(static_mut_ref)]
// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_refs` lint
#[allow(static_mut_refs)]
static NUM_REF: &'static u8 = unsafe { &NUM };
macro_rules! assert {

View file

@ -51,5 +51,5 @@ default-features = false
features = ["read_core", "elf", "macho", "pe", "xcoff", "unaligned", "archive", "write"]
[target.'cfg(windows)'.dependencies.windows]
version = "0.48.0"
version = "0.52.0"
features = ["Win32_Globalization"]

View file

@ -362,8 +362,11 @@ impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for LinkingFailed<'_> {
// which by now we have no way to translate.
if contains_undefined_ref {
diag.note(fluent::codegen_ssa_extern_funcs_not_found)
.note(fluent::codegen_ssa_specify_libraries_to_link)
.note(fluent::codegen_ssa_use_cargo_directive);
.note(fluent::codegen_ssa_specify_libraries_to_link);
if rustc_session::utils::was_invoked_from_cargo() {
diag.note(fluent::codegen_ssa_use_cargo_directive);
}
}
diag
}

View file

@ -33,7 +33,7 @@ tracing = "0.1"
version = "0.12"
[target.'cfg(windows)'.dependencies.windows]
version = "0.48.0"
version = "0.52.0"
features = [
"Win32_Foundation",
"Win32_Storage_FileSystem",

View file

@ -69,7 +69,6 @@ impl Lock {
&mut overlapped,
)
}
.ok()
.map_err(|e| {
let err = io::Error::from_raw_os_error(e.code().0);
debug!("failed acquiring file lock: {}", err);

View file

@ -4,7 +4,7 @@ use crate::sync::Lrc;
// Use our fake Send/Sync traits when on not parallel compiler,
// so that `OwnedSlice` only implements/requires Send/Sync
// for parallel compiler builds.
use crate::sync::{Send, Sync};
use crate::sync;
/// An owned slice.
///
@ -33,7 +33,7 @@ pub struct OwnedSlice {
// \/
// ⊂(´・◡・⊂ )∘˚˳° (I am the phantom remnant of #97770)
#[expect(dead_code)]
owner: Lrc<dyn Send + Sync>,
owner: Lrc<dyn sync::Send + sync::Sync>,
}
/// Makes an [`OwnedSlice`] out of an `owner` and a `slicer` function.
@ -60,7 +60,7 @@ pub struct OwnedSlice {
/// ```
pub fn slice_owned<O, F>(owner: O, slicer: F) -> OwnedSlice
where
O: Send + Sync + 'static,
O: sync::Send + sync::Sync + 'static,
F: FnOnce(&O) -> &[u8],
{
try_slice_owned(owner, |x| Ok::<_, !>(slicer(x))).into_ok()
@ -71,7 +71,7 @@ where
/// See [`slice_owned`] for the infallible version.
pub fn try_slice_owned<O, F, E>(owner: O, slicer: F) -> Result<OwnedSlice, E>
where
O: Send + Sync + 'static,
O: sync::Send + sync::Sync + 'static,
F: FnOnce(&O) -> Result<&[u8], E>,
{
// We wrap the owner of the bytes in, so it doesn't move.
@ -139,11 +139,11 @@ impl Borrow<[u8]> for OwnedSlice {
// Safety: `OwnedSlice` is conceptually `(&'self.1 [u8], Arc<dyn Send + Sync>)`, which is `Send`
#[cfg(parallel_compiler)]
unsafe impl Send for OwnedSlice {}
unsafe impl sync::Send for OwnedSlice {}
// Safety: `OwnedSlice` is conceptually `(&'self.1 [u8], Arc<dyn Send + Sync>)`, which is `Sync`
#[cfg(parallel_compiler)]
unsafe impl Sync for OwnedSlice {}
unsafe impl sync::Sync for OwnedSlice {}
#[cfg(test)]
mod tests;

View file

@ -866,16 +866,14 @@ cfg_match! {
use std::mem;
use windows::{
// FIXME: change back to K32GetProcessMemoryInfo when windows crate
// updated to 0.49.0+ to drop dependency on psapi.dll
Win32::System::ProcessStatus::{GetProcessMemoryInfo, PROCESS_MEMORY_COUNTERS},
Win32::System::ProcessStatus::{K32GetProcessMemoryInfo, PROCESS_MEMORY_COUNTERS},
Win32::System::Threading::GetCurrentProcess,
};
let mut pmc = PROCESS_MEMORY_COUNTERS::default();
let pmc_size = mem::size_of_val(&pmc);
unsafe {
GetProcessMemoryInfo(
K32GetProcessMemoryInfo(
GetCurrentProcess(),
&mut pmc,
pmc_size as u32,

View file

@ -60,7 +60,7 @@ libc = "0.2"
# tidy-alphabetical-end
[target.'cfg(windows)'.dependencies.windows]
version = "0.48.0"
version = "0.52.0"
features = [
"Win32_System_Diagnostics_Debug",
]

View file

@ -1,22 +1,26 @@
Reference of mutable static.
You have created a reference to a mutable static.
Erroneous code example:
```compile_fail,edition2024,E0796
static mut X: i32 = 23;
static mut Y: i32 = 24;
unsafe {
let y = &X;
let ref x = X;
let (x, y) = (&X, &Y);
foo(&X);
fn work() {
let _val = unsafe { X };
}
fn foo<'a>(_x: &'a i32) {}
let x_ref = unsafe { &mut X };
work();
// The next line has Undefined Behavior!
// `x_ref` is a mutable reference and allows no aliases,
// but `work` has been reading the reference between
// the moment `x_ref` was created and when it was used.
// This violates the uniqueness of `x_ref`.
*x_ref = 42;
```
Mutable statics can be written to by multiple threads: aliasing violations or
data races will cause undefined behavior.
A reference to a mutable static has lifetime `'static`. This is very dangerous
as it is easy to accidentally overlap the lifetime of that reference with
other, conflicting accesses to the same static.
Reference of mutable static is a hard error from 2024 edition.
References to mutable statics are a hard error in the 2024 edition.

View file

@ -30,7 +30,7 @@ unicode-width = "0.1.4"
# tidy-alphabetical-end
[target.'cfg(windows)'.dependencies.windows]
version = "0.48.0"
version = "0.52.0"
features = [
"Win32_Foundation",
"Win32_Security",

View file

@ -27,7 +27,8 @@ pub fn acquire_global_lock(name: &str) -> Box<dyn Any> {
impl Drop for Handle {
fn drop(&mut self) {
unsafe {
CloseHandle(self.0);
// FIXME can panic here
CloseHandle(self.0).unwrap();
}
}
}
@ -37,7 +38,8 @@ pub fn acquire_global_lock(name: &str) -> Box<dyn Any> {
impl Drop for Guard {
fn drop(&mut self) {
unsafe {
ReleaseMutex((self.0).0);
// FIXME can panic here
ReleaseMutex((self.0).0).unwrap();
}
}
}

View file

@ -61,6 +61,11 @@ expand_invalid_cfg_multiple_predicates = multiple `cfg` predicates are specified
expand_invalid_cfg_no_parens = `cfg` is not followed by parentheses
expand_invalid_cfg_no_predicate = `cfg` predicate is not specified
expand_invalid_cfg_predicate_literal = `cfg` predicate key cannot be a literal
expand_invalid_fragment_specifier =
invalid fragment specifier `{$fragment}`
.help = {$help}
expand_macro_body_stability =
macros cannot have body stability attributes
.label = invalid body stability attribute

View file

@ -408,3 +408,13 @@ pub struct DuplicateMatcherBinding {
#[label(expand_label2)]
pub prev: Span,
}
#[derive(Diagnostic)]
#[diag(expand_invalid_fragment_specifier)]
#[help]
pub struct InvalidFragmentSpecifier {
#[primary_span]
pub span: Span,
pub fragment: Ident,
pub help: String,
}

View file

@ -6,6 +6,7 @@
#![feature(if_let_guard)]
#![feature(let_chains)]
#![feature(macro_metavar_expr)]
#![feature(map_try_insert)]
#![feature(proc_macro_diagnostic)]
#![feature(proc_macro_internals)]
#![feature(proc_macro_span)]

View file

@ -1,3 +1,4 @@
use crate::errors;
use crate::mbe::macro_parser::count_metavar_decls;
use crate::mbe::{Delimited, KleeneOp, KleeneToken, MetaVarExpr, SequenceRepetition, TokenTree};
@ -60,11 +61,11 @@ pub(super) fn parse(
Some(&tokenstream::TokenTree::Token(Token { kind: token::Colon, span }, _)) => {
match trees.next() {
Some(tokenstream::TokenTree::Token(token, _)) => match token.ident() {
Some((frag, _)) => {
Some((fragment, _)) => {
let span = token.span.with_lo(start_sp.lo());
let kind =
token::NonterminalKind::from_symbol(frag.name, || {
token::NonterminalKind::from_symbol(fragment.name, || {
// FIXME(#85708) - once we properly decode a foreign
// crate's `SyntaxContext::root`, then we can replace
// this with just `span.edition()`. A
@ -81,14 +82,13 @@ pub(super) fn parse(
})
.unwrap_or_else(
|| {
let msg = format!(
"invalid fragment specifier `{}`",
frag.name
sess.dcx().emit_err(
errors::InvalidFragmentSpecifier {
span,
fragment,
help: VALID_FRAGMENT_NAMES_MSG.into(),
},
);
sess.dcx()
.struct_span_err(span, msg)
.with_help(VALID_FRAGMENT_NAMES_MSG)
.emit();
token::NonterminalKind::Ident
},
);

View file

@ -13,7 +13,7 @@ use rustc_errors::DiagnosticBuilder;
use rustc_errors::{pluralize, PResult};
use rustc_span::hygiene::{LocalExpnId, Transparency};
use rustc_span::symbol::{sym, Ident, MacroRulesNormalizedIdent};
use rustc_span::{Span, SyntaxContext};
use rustc_span::{with_metavar_spans, Span, SyntaxContext};
use smallvec::{smallvec, SmallVec};
use std::mem;
@ -254,7 +254,8 @@ pub(super) fn transcribe<'a>(
MatchedTokenTree(tt) => {
// `tt`s are emitted into the output stream directly as "raw tokens",
// without wrapping them into groups.
result.push(maybe_use_metavar_location(cx, &stack, sp, tt));
let tt = maybe_use_metavar_location(cx, &stack, sp, tt, &mut marker);
result.push(tt);
}
MatchedNonterminal(nt) => {
// Other variables are emitted into the output stream as groups with
@ -319,6 +320,17 @@ pub(super) fn transcribe<'a>(
}
}
/// Store the metavariable span for this original span into a side table.
/// FIXME: Try to put the metavariable span into `SpanData` instead of a side table (#118517).
/// An optimal encoding for inlined spans will need to be selected to minimize regressions.
/// The side table approach is relatively good, but not perfect due to collisions.
/// In particular, collisions happen when token is passed as an argument through several macro
/// calls, like in recursive macros.
/// The old heuristic below is used to improve spans in case of collisions, but diagnostics are
/// still degraded sometimes in those cases.
///
/// The old heuristic:
///
/// Usually metavariables `$var` produce interpolated tokens, which have an additional place for
/// keeping both the original span and the metavariable span. For `tt` metavariables that's not the
/// case however, and there's no place for keeping a second span. So we try to give the single
@ -338,15 +350,12 @@ pub(super) fn transcribe<'a>(
/// These are typically used for passing larger amounts of code, and tokens in that code usually
/// combine with each other and not with tokens outside of the sequence.
/// - The metavariable span comes from a different crate, then we prefer the more local span.
///
/// FIXME: Find a way to keep both original and metavariable spans for all tokens without
/// regressing compilation time too much. Several experiments for adding such spans were made in
/// the past (PR #95580, #118517, #118671) and all showed some regressions.
fn maybe_use_metavar_location(
cx: &ExtCtxt<'_>,
stack: &[Frame<'_>],
metavar_span: Span,
mut metavar_span: Span,
orig_tt: &TokenTree,
marker: &mut Marker,
) -> TokenTree {
let undelimited_seq = matches!(
stack.last(),
@ -357,18 +366,44 @@ fn maybe_use_metavar_location(
..
})
);
if undelimited_seq || cx.source_map().is_imported(metavar_span) {
if undelimited_seq {
// Do not record metavar spans for tokens from undelimited sequences, for perf reasons.
return orig_tt.clone();
}
let insert = |mspans: &mut FxHashMap<_, _>, s, ms| match mspans.try_insert(s, ms) {
Ok(_) => true,
Err(err) => *err.entry.get() == ms, // Tried to insert the same span, still success
};
marker.visit_span(&mut metavar_span);
let no_collision = match orig_tt {
TokenTree::Token(token, ..) => {
with_metavar_spans(|mspans| insert(mspans, token.span, metavar_span))
}
TokenTree::Delimited(dspan, ..) => with_metavar_spans(|mspans| {
insert(mspans, dspan.open, metavar_span)
&& insert(mspans, dspan.close, metavar_span)
&& insert(mspans, dspan.entire(), metavar_span)
}),
};
if no_collision || cx.source_map().is_imported(metavar_span) {
return orig_tt.clone();
}
// Setting metavar spans for the heuristic spans gives better opportunities for combining them
// with neighboring spans even despite their different syntactic contexts.
match orig_tt {
TokenTree::Token(Token { kind, span }, spacing) => {
let span = metavar_span.with_ctxt(span.ctxt());
with_metavar_spans(|mspans| insert(mspans, span, metavar_span));
TokenTree::Token(Token { kind: kind.clone(), span }, *spacing)
}
TokenTree::Delimited(dspan, dspacing, delimiter, tts) => {
let open = metavar_span.shrink_to_lo().with_ctxt(dspan.open.ctxt());
let close = metavar_span.shrink_to_hi().with_ctxt(dspan.close.ctxt());
let open = metavar_span.with_ctxt(dspan.open.ctxt());
let close = metavar_span.with_ctxt(dspan.close.ctxt());
with_metavar_spans(|mspans| {
insert(mspans, open, metavar_span) && insert(mspans, close, metavar_span)
});
let dspan = DelimSpan::from_pair(open, close);
TokenTree::Delimited(dspan, *dspacing, *delimiter, tts.clone())
}

View file

@ -2998,6 +2998,12 @@ impl<'hir> Item<'hir> {
ItemId { owner_id: self.owner_id }
}
/// Check if this is an [`ItemKind::Enum`], [`ItemKind::Struct`] or
/// [`ItemKind::Union`].
pub fn is_adt(&self) -> bool {
matches!(self.kind, ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Union(..))
}
expect_methods_self_kind! {
expect_extern_crate, Option<Symbol>, ItemKind::ExternCrate(s), *s;

View file

@ -198,6 +198,8 @@ hir_analysis_invalid_union_field =
hir_analysis_invalid_union_field_sugg =
wrap the field type in `ManuallyDrop<...>`
hir_analysis_invalid_unnamed_field_ty = unnamed fields can only have struct or union types
hir_analysis_late_bound_const_in_apit = `impl Trait` can only mention const parameters from an fn or impl
.label = const parameter declared here
@ -373,19 +375,24 @@ hir_analysis_start_not_target_feature = `#[start]` function is not allowed to ha
hir_analysis_start_not_track_caller = `#[start]` function is not allowed to be `#[track_caller]`
.label = `#[start]` function is not allowed to be `#[track_caller]`
hir_analysis_static_mut_ref = reference of mutable static is disallowed
.label = reference of mutable static
.note = mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
.suggestion = shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer
.suggestion_mut = mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer
hir_analysis_static_mut_ref = creating a {$shared} reference to a mutable static
.label = {$shared} reference to mutable static
.note = {$shared ->
[shared] this shared reference has lifetime `'static`, but if the static ever gets mutated, or a mutable reference is created, then any further use of this shared reference is Undefined Behavior
*[mutable] this mutable reference has lifetime `'static`, but if the static gets accessed (read or written) by any other means, or any other reference is created, then any further use of this mutable reference is Undefined Behavior
}
.suggestion = use `addr_of!` instead to create a raw pointer
.suggestion_mut = use `addr_of_mut!` instead to create a raw pointer
hir_analysis_static_mut_ref_lint = {$shared}reference of mutable static is discouraged
.label = shared reference of mutable static
.label_mut = mutable reference of mutable static
.suggestion = shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer
.suggestion_mut = mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer
.note = reference of mutable static is a hard error from 2024 edition
.why_note = mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
hir_analysis_static_mut_refs_lint = creating a {$shared} reference to mutable static is discouraged
.label = {$shared} reference to mutable static
.suggestion = use `addr_of!` instead to create a raw pointer
.suggestion_mut = use `addr_of_mut!` instead to create a raw pointer
.note = this will be a hard error in the 2024 edition
.why_note = {$shared ->
[shared] this shared reference has lifetime `'static`, but if the static ever gets mutated, or a mutable reference is created, then any further use of this shared reference is Undefined Behavior
*[mutable] this mutable reference has lifetime `'static`, but if the static gets accessed (read or written) by any other means, or any other reference is created, then any further use of this mutable reference is Undefined Behavior
}
hir_analysis_static_specialize = cannot specialize on `'static` lifetime
@ -462,6 +469,7 @@ hir_analysis_unrecognized_atomic_operation =
hir_analysis_unrecognized_intrinsic_function =
unrecognized intrinsic function: `{$name}`
.label = unrecognized intrinsic
.help = if you're adding an intrinsic, be sure to update `check_intrinsic_type`
hir_analysis_unused_associated_type_bounds =
unnecessary associated type bound for not object safe associated type

View file

@ -9,9 +9,7 @@ use rustc_span::{ErrorGuaranteed, Span};
use rustc_trait_selection::traits;
use smallvec::SmallVec;
use crate::astconv::{
AstConv, ConvertedBinding, ConvertedBindingKind, OnlySelfBounds, PredicateFilter,
};
use crate::astconv::{AstConv, OnlySelfBounds, PredicateFilter};
use crate::bounds::Bounds;
use crate::errors;
@ -238,7 +236,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
&self,
hir_ref_id: hir::HirId,
trait_ref: ty::PolyTraitRef<'tcx>,
binding: &ConvertedBinding<'_, 'tcx>,
binding: &hir::TypeBinding<'tcx>,
bounds: &mut Bounds<'tcx>,
speculative: bool,
dup_bindings: &mut FxIndexMap<DefId, Span>,
@ -263,21 +261,20 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
let tcx = self.tcx();
let assoc_kind =
if binding.gen_args.parenthesized == hir::GenericArgsParentheses::ReturnTypeNotation {
ty::AssocKind::Fn
} else if let ConvertedBindingKind::Equality(term) = binding.kind
&& let ty::TermKind::Const(_) = term.node.unpack()
{
ty::AssocKind::Const
} else {
ty::AssocKind::Type
};
let assoc_kind = if binding.gen_args.parenthesized
== hir::GenericArgsParentheses::ReturnTypeNotation
{
ty::AssocKind::Fn
} else if let hir::TypeBindingKind::Equality { term: hir::Term::Const(_) } = binding.kind {
ty::AssocKind::Const
} else {
ty::AssocKind::Type
};
let candidate = if self.trait_defines_associated_item_named(
trait_ref.def_id(),
assoc_kind,
binding.item_name,
binding.ident,
) {
// Simple case: The assoc item is defined in the current trait.
trait_ref
@ -289,14 +286,14 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
trait_ref.skip_binder().print_only_trait_name(),
None,
assoc_kind,
binding.item_name,
binding.ident,
path_span,
Some(&binding),
Some(binding),
)?
};
let (assoc_ident, def_scope) =
tcx.adjust_ident_and_get_scope(binding.item_name, candidate.def_id(), hir_ref_id);
tcx.adjust_ident_and_get_scope(binding.ident, candidate.def_id(), hir_ref_id);
// We have already adjusted the item name above, so compare with `.normalize_to_macros_2_0()`
// instead of calling `filter_by_name_and_kind` which would needlessly normalize the
@ -312,7 +309,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
.dcx()
.struct_span_err(
binding.span,
format!("{} `{}` is private", assoc_item.kind, binding.item_name),
format!("{} `{}` is private", assoc_item.kind, binding.ident),
)
.with_span_label(binding.span, format!("private {}", assoc_item.kind))
.emit();
@ -327,7 +324,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
tcx.dcx().emit_err(errors::ValueOfAssociatedStructAlreadySpecified {
span: binding.span,
prev_span: *prev_span,
item_name: binding.item_name,
item_name: binding.ident,
def_path: tcx.def_path_str(assoc_item.container_id(tcx)),
});
})
@ -390,14 +387,12 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
{
alias_ty
} else {
return Err(self.tcx().dcx().emit_err(
crate::errors::ReturnTypeNotationOnNonRpitit {
span: binding.span,
ty: tcx.liberate_late_bound_regions(assoc_item.def_id, output),
fn_span: tcx.hir().span_if_local(assoc_item.def_id),
note: (),
},
));
return Err(tcx.dcx().emit_err(crate::errors::ReturnTypeNotationOnNonRpitit {
span: binding.span,
ty: tcx.liberate_late_bound_regions(assoc_item.def_id, output),
fn_span: tcx.hir().span_if_local(assoc_item.def_id),
note: (),
}));
};
// Finally, move the fn return type's bound vars over to account for the early bound
@ -410,9 +405,11 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
let bound_vars = tcx.late_bound_vars(binding.hir_id);
ty::Binder::bind_with_vars(instantiation_output, bound_vars)
} else {
// Append the generic arguments of the associated type to the `trait_ref`.
// Create the generic arguments for the associated type or constant by joining the
// parent arguments (the arguments of the trait) and the own arguments (the ones of
// the associated item itself) and construct an alias type using them.
candidate.map_bound(|trait_ref| {
let ident = Ident::new(assoc_item.name, binding.item_name.span);
let ident = Ident::new(assoc_item.name, binding.ident.span);
let item_segment = hir::PathSegment {
ident,
hir_id: binding.hir_id,
@ -421,77 +418,82 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
infer_args: false,
};
let args_trait_ref_and_assoc_item = self.create_args_for_associated_item(
let alias_args = self.create_args_for_associated_item(
path_span,
assoc_item.def_id,
&item_segment,
trait_ref.args,
);
debug!(?alias_args);
debug!(?args_trait_ref_and_assoc_item);
ty::AliasTy::new(tcx, assoc_item.def_id, args_trait_ref_and_assoc_item)
// Note that we're indeed also using `AliasTy` (alias *type*) for associated
// *constants* to represent *const projections*. Alias *term* would be a more
// appropriate name but alas.
ty::AliasTy::new(tcx, assoc_item.def_id, alias_args)
})
};
if !speculative {
// Find any late-bound regions declared in `ty` that are not
// declared in the trait-ref or assoc_item. These are not well-formed.
//
// Example:
//
// for<'a> <T as Iterator>::Item = &'a str // <-- 'a is bad
// for<'a> <T as FnMut<(&'a u32,)>>::Output = &'a str // <-- 'a is ok
if let ConvertedBindingKind::Equality(ty) = binding.kind {
let late_bound_in_trait_ref =
tcx.collect_constrained_late_bound_regions(&projection_ty);
let late_bound_in_ty =
tcx.collect_referenced_late_bound_regions(&trait_ref.rebind(ty.node));
debug!(?late_bound_in_trait_ref);
debug!(?late_bound_in_ty);
// FIXME: point at the type params that don't have appropriate lifetimes:
// struct S1<F: for<'a> Fn(&i32, &i32) -> &'a i32>(F);
// ---- ---- ^^^^^^^
self.validate_late_bound_regions(
late_bound_in_trait_ref,
late_bound_in_ty,
|br_name| {
struct_span_code_err!(
tcx.dcx(),
binding.span,
E0582,
"binding for associated type `{}` references {}, \
which does not appear in the trait input types",
binding.item_name,
br_name
)
},
);
}
}
match binding.kind {
ConvertedBindingKind::Equality(..) if let ty::AssocKind::Fn = assoc_kind => {
return Err(self.tcx().dcx().emit_err(
crate::errors::ReturnTypeNotationEqualityBound { span: binding.span },
));
hir::TypeBindingKind::Equality { .. } if let ty::AssocKind::Fn = assoc_kind => {
return Err(tcx.dcx().emit_err(crate::errors::ReturnTypeNotationEqualityBound {
span: binding.span,
}));
}
ConvertedBindingKind::Equality(term) => {
hir::TypeBindingKind::Equality { term } => {
let term = match term {
hir::Term::Ty(ty) => self.ast_ty_to_ty(ty).into(),
hir::Term::Const(ct) => ty::Const::from_anon_const(tcx, ct.def_id).into(),
};
if !speculative {
// Find any late-bound regions declared in `ty` that are not
// declared in the trait-ref or assoc_item. These are not well-formed.
//
// Example:
//
// for<'a> <T as Iterator>::Item = &'a str // <-- 'a is bad
// for<'a> <T as FnMut<(&'a u32,)>>::Output = &'a str // <-- 'a is ok
let late_bound_in_projection_ty =
tcx.collect_constrained_late_bound_regions(&projection_ty);
let late_bound_in_term =
tcx.collect_referenced_late_bound_regions(&trait_ref.rebind(term));
debug!(?late_bound_in_projection_ty);
debug!(?late_bound_in_term);
// FIXME: point at the type params that don't have appropriate lifetimes:
// struct S1<F: for<'a> Fn(&i32, &i32) -> &'a i32>(F);
// ---- ---- ^^^^^^^
// NOTE(associated_const_equality): This error should be impossible to trigger
// with associated const equality bounds.
self.validate_late_bound_regions(
late_bound_in_projection_ty,
late_bound_in_term,
|br_name| {
struct_span_code_err!(
tcx.dcx(),
binding.span,
E0582,
"binding for associated type `{}` references {}, \
which does not appear in the trait input types",
binding.ident,
br_name
)
},
);
}
// "Desugar" a constraint like `T: Iterator<Item = u32>` this to
// the "projection predicate" for:
//
// `<T as Iterator>::Item = u32`
bounds.push_projection_bound(
tcx,
projection_ty.map_bound(|projection_ty| ty::ProjectionPredicate {
projection_ty,
term: term.node,
}),
projection_ty
.map_bound(|projection_ty| ty::ProjectionPredicate { projection_ty, term }),
binding.span,
);
}
ConvertedBindingKind::Constraint(ast_bounds) => {
hir::TypeBindingKind::Constraint { bounds: ast_bounds } => {
// "Desugar" a constraint like `T: Iterator<Item: Debug>` to
//
// `<T as Iterator>::Item: Debug`

View file

@ -1,4 +1,4 @@
use crate::astconv::{AstConv, ConvertedBindingKind};
use crate::astconv::AstConv;
use crate::errors::{
self, AssocTypeBindingNotAllowed, ManualImplementation, MissingTypeParams,
ParenthesizedFnTraitExpansion,
@ -111,7 +111,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
assoc_kind: ty::AssocKind,
assoc_name: Ident,
span: Span,
binding: Option<&super::ConvertedBinding<'_, 'tcx>>,
binding: Option<&hir::TypeBinding<'tcx>>,
) -> ErrorGuaranteed
where
I: Iterator<Item = ty::PolyTraitRef<'tcx>>,
@ -243,7 +243,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
None,
) && suggested_name != assoc_name.name
{
// We suggested constraining a type parameter, but the associated type on it
// We suggested constraining a type parameter, but the associated item on it
// was also not an exact match, so we also suggest changing it.
err.span_suggestion_verbose(
assoc_name.span,
@ -258,16 +258,17 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
}
}
// If we still couldn't find any associated type, and only one associated type exists,
// If we still couldn't find any associated item, and only one associated item exists,
// suggests using it.
if let [candidate_name] = all_candidate_names.as_slice() {
// this should still compile, except on `#![feature(associated_type_defaults)]`
// where it could suggests `type A = Self::A`, thus recursing infinitely
let applicability = if tcx.features().associated_type_defaults {
Applicability::Unspecified
} else {
Applicability::MaybeIncorrect
};
// This should still compile, except on `#![feature(associated_type_defaults)]`
// where it could suggests `type A = Self::A`, thus recursing infinitely.
let applicability =
if assoc_kind == ty::AssocKind::Type && tcx.features().associated_type_defaults {
Applicability::Unspecified
} else {
Applicability::MaybeIncorrect
};
err.sugg = Some(errors::AssocItemNotFoundSugg::Other {
span: assoc_name.span,
@ -289,13 +290,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
assoc_kind: ty::AssocKind,
ident: Ident,
span: Span,
binding: Option<&super::ConvertedBinding<'_, 'tcx>>,
binding: Option<&hir::TypeBinding<'tcx>>,
) -> ErrorGuaranteed {
let tcx = self.tcx();
let bound_on_assoc_const_label = if let ty::AssocKind::Const = assoc_item.kind
&& let Some(binding) = binding
&& let ConvertedBindingKind::Constraint(_) = binding.kind
&& let hir::TypeBindingKind::Constraint { .. } = binding.kind
{
let lo = if binding.gen_args.span_ext.is_dummy() {
ident.span
@ -309,25 +310,29 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
// FIXME(associated_const_equality): This has quite a few false positives and negatives.
let wrap_in_braces_sugg = if let Some(binding) = binding
&& let ConvertedBindingKind::Equality(term) = binding.kind
&& let ty::TermKind::Ty(ty) = term.node.unpack()
&& let hir::TypeBindingKind::Equality { term: hir::Term::Ty(hir_ty) } = binding.kind
&& let ty = self.ast_ty_to_ty(hir_ty)
&& (ty.is_enum() || ty.references_error())
&& tcx.features().associated_const_equality
{
Some(errors::AssocKindMismatchWrapInBracesSugg {
lo: term.span.shrink_to_lo(),
hi: term.span.shrink_to_hi(),
lo: hir_ty.span.shrink_to_lo(),
hi: hir_ty.span.shrink_to_hi(),
})
} else {
None
};
// For equality bounds, we want to blame the term (RHS) instead of the item (LHS) since
// one can argue that that's more “untuitive” to the user.
// one can argue that that's more “intuitive” to the user.
let (span, expected_because_label, expected, got) = if let Some(binding) = binding
&& let ConvertedBindingKind::Equality(term) = binding.kind
&& let hir::TypeBindingKind::Equality { term } = binding.kind
{
(term.span, Some(ident.span), assoc_item.kind, assoc_kind)
let span = match term {
hir::Term::Ty(ty) => ty.span,
hir::Term::Const(ct) => tcx.def_span(ct.def_id),
};
(span, Some(ident.span), assoc_item.kind, assoc_kind)
} else {
(ident.span, None, assoc_kind, assoc_item.kind)
};

View file

@ -35,7 +35,6 @@ use rustc_middle::ty::{
};
use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS;
use rustc_span::edit_distance::find_best_match_for_name;
use rustc_span::source_map::{respan, Spanned};
use rustc_span::symbol::{kw, Ident, Symbol};
use rustc_span::{sym, BytePos, Span, DUMMY_SP};
use rustc_target::spec::abi;
@ -151,21 +150,6 @@ pub trait AstConv<'tcx> {
fn infcx(&self) -> Option<&InferCtxt<'tcx>>;
}
#[derive(Debug)]
struct ConvertedBinding<'a, 'tcx> {
hir_id: hir::HirId,
item_name: Ident,
kind: ConvertedBindingKind<'a, 'tcx>,
gen_args: &'tcx GenericArgs<'tcx>,
span: Span,
}
#[derive(Debug)]
enum ConvertedBindingKind<'a, 'tcx> {
Equality(Spanned<ty::Term<'tcx>>),
Constraint(&'a [hir::GenericBound<'tcx>]),
}
/// New-typed boolean indicating whether explicit late-bound lifetimes
/// are present in a set of generic arguments.
///
@ -316,7 +300,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
/// Given the type/lifetime/const arguments provided to some path (along with
/// an implicit `Self`, if this is a trait reference), returns the complete
/// set of generic arguments. This may involve applying defaulted type parameters.
/// Constraints on associated types are created from `create_assoc_bindings_for_generic_args`.
///
/// Constraints on associated types are not converted here but
/// separately in `add_predicates_for_ast_type_binding`.
///
/// Example:
///
@ -329,8 +315,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
/// 2. The path in question is the path to the trait `std::ops::Index`,
/// which will have been resolved to a `def_id`
/// 3. The `generic_args` contains info on the `<...>` contents. The `usize` type
/// parameters are returned in the `GenericArgsRef`, the associated type bindings like
/// `Output = u32` are returned from `create_assoc_bindings_for_generic_args`.
/// parameters are returned in the `GenericArgsRef`
/// 4. Associated type bindings like `Output = u32` are contained in `generic_args.bindings`.
///
/// Note that the type listing given here is *exactly* what the user provided.
///
@ -591,52 +577,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
(args, arg_count)
}
fn create_assoc_bindings_for_generic_args<'a>(
&self,
generic_args: &'a hir::GenericArgs<'tcx>,
) -> Vec<ConvertedBinding<'a, 'tcx>> {
// Convert associated-type bindings or constraints into a separate vector.
// Example: Given this:
//
// T: Iterator<Item = u32>
//
// The `T` is passed in as a self-type; the `Item = u32` is
// not a "type parameter" of the `Iterator` trait, but rather
// a restriction on `<T as Iterator>::Item`, so it is passed
// back separately.
let assoc_bindings = generic_args
.bindings
.iter()
.map(|binding| {
let kind = match &binding.kind {
hir::TypeBindingKind::Equality { term } => match term {
hir::Term::Ty(ty) => ConvertedBindingKind::Equality(respan(
ty.span,
self.ast_ty_to_ty(ty).into(),
)),
hir::Term::Const(c) => {
let span = self.tcx().def_span(c.def_id);
let c = Const::from_anon_const(self.tcx(), c.def_id);
ConvertedBindingKind::Equality(respan(span, c.into()))
}
},
hir::TypeBindingKind::Constraint { bounds } => {
ConvertedBindingKind::Constraint(bounds)
}
};
ConvertedBinding {
hir_id: binding.hir_id,
item_name: binding.ident,
kind,
gen_args: binding.gen_args,
span: binding.span,
}
})
.collect();
assoc_bindings
}
pub fn create_args_for_associated_item(
&self,
span: Span,
@ -742,18 +682,16 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let bound_vars = tcx.late_bound_vars(trait_ref.hir_ref_id);
debug!(?bound_vars);
let assoc_bindings = self.create_assoc_bindings_for_generic_args(args);
let poly_trait_ref = ty::Binder::bind_with_vars(
ty::TraitRef::new(tcx, trait_def_id, generic_args),
bound_vars,
);
debug!(?poly_trait_ref, ?assoc_bindings);
debug!(?poly_trait_ref);
bounds.push_trait_bound(tcx, poly_trait_ref, span, polarity);
let mut dup_bindings = FxIndexMap::default();
for binding in &assoc_bindings {
for binding in args.bindings {
// Don't register additional associated type bounds for negative bounds,
// since we should have emitten an error for them earlier, and they will
// not be well-formed!
@ -1029,7 +967,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
assoc_kind: ty::AssocKind,
assoc_name: Ident,
span: Span,
binding: Option<&ConvertedBinding<'_, 'tcx>>,
binding: Option<&hir::TypeBinding<'tcx>>,
) -> Result<ty::PolyTraitRef<'tcx>, ErrorGuaranteed>
where
I: Iterator<Item = ty::PolyTraitRef<'tcx>>,
@ -1069,7 +1007,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
// Provide a more specific error code index entry for equality bindings.
err.code(
if let Some(binding) = binding
&& let ConvertedBindingKind::Equality(_) = binding.kind
&& let hir::TypeBindingKind::Equality { .. } = binding.kind
{
E0222
} else {
@ -1094,16 +1032,21 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
);
if let Some(binding) = binding {
match binding.kind {
ConvertedBindingKind::Equality(term) => {
hir::TypeBindingKind::Equality { term } => {
let term: ty::Term<'_> = match term {
hir::Term::Ty(ty) => self.ast_ty_to_ty(ty).into(),
hir::Term::Const(ct) => {
ty::Const::from_anon_const(tcx, ct.def_id).into()
}
};
// FIXME(#97583): This isn't syntactically well-formed!
where_bounds.push(format!(
" T: {trait}::{assoc_name} = {term}",
trait = bound.print_only_trait_path(),
term = term.node,
));
}
// FIXME: Provide a suggestion.
ConvertedBindingKind::Constraint(_bounds) => {}
hir::TypeBindingKind::Constraint { bounds: _ } => {}
}
} else {
err.span_suggestion_verbose(

View file

@ -5,10 +5,9 @@ use super::compare_impl_item::check_type_bounds;
use super::compare_impl_item::{compare_impl_method, compare_impl_ty};
use super::*;
use rustc_attr as attr;
use rustc_errors::{codes::*, ErrorGuaranteed, MultiSpan};
use rustc_errors::{codes::*, MultiSpan};
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, DefKind};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::Node;
use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt};
use rustc_infer::traits::{Obligation, TraitEngineExt as _};
@ -129,17 +128,20 @@ fn check_unnamed_fields(tcx: TyCtxt<'_>, def: ty::AdtDef<'_>) {
for field in variant.fields.iter().filter(|f| f.is_unnamed()) {
let field_ty = tcx.type_of(field.did).instantiate_identity();
if let Some(adt) = field_ty.ty_adt_def()
&& !adt.is_anonymous()
&& !adt.repr().c()
&& !adt.is_enum()
{
let field_ty_span = tcx.def_span(adt.did());
tcx.dcx().emit_err(errors::UnnamedFieldsRepr::FieldMissingReprC {
span: tcx.def_span(field.did),
field_ty_span,
field_ty,
field_adt_kind: adt.descr(),
sugg_span: field_ty_span.shrink_to_lo(),
});
if !adt.is_anonymous() && !adt.repr().c() {
let field_ty_span = tcx.def_span(adt.did());
tcx.dcx().emit_err(errors::UnnamedFieldsRepr::FieldMissingReprC {
span: tcx.def_span(field.did),
field_ty_span,
field_ty,
field_adt_kind: adt.descr(),
sugg_span: field_ty_span.shrink_to_lo(),
});
}
} else {
tcx.dcx().emit_err(errors::InvalidUnnamedFieldTy { span: tcx.def_span(field.did) });
}
}
}

View file

@ -1,6 +1,6 @@
use rustc_hir as hir;
use rustc_hir_pretty::qpath_to_string;
use rustc_lint_defs::builtin::STATIC_MUT_REF;
use rustc_lint_defs::builtin::STATIC_MUT_REFS;
use rustc_middle::ty::TyCtxt;
use rustc_span::Span;
use rustc_type_ir::Mutability;
@ -66,32 +66,24 @@ fn handle_static_mut_ref(
hir_id: hir::HirId,
) {
if e2024 {
let sugg = if mutable {
errors::StaticMutRefSugg::Mut { span, var }
let (sugg, shared) = if mutable {
(errors::StaticMutRefSugg::Mut { span, var }, "mutable")
} else {
errors::StaticMutRefSugg::Shared { span, var }
(errors::StaticMutRefSugg::Shared { span, var }, "shared")
};
tcx.sess.parse_sess.dcx.emit_err(errors::StaticMutRef { span, sugg });
tcx.sess.parse_sess.dcx.emit_err(errors::StaticMutRef { span, sugg, shared });
return;
}
let (label, sugg, shared) = if mutable {
(
errors::RefOfMutStaticLabel::Mut { span },
errors::RefOfMutStaticSugg::Mut { span, var },
"mutable ",
)
let (sugg, shared) = if mutable {
(errors::RefOfMutStaticSugg::Mut { span, var }, "mutable")
} else {
(
errors::RefOfMutStaticLabel::Shared { span },
errors::RefOfMutStaticSugg::Shared { span, var },
"shared ",
)
(errors::RefOfMutStaticSugg::Shared { span, var }, "shared")
};
tcx.emit_node_span_lint(
STATIC_MUT_REF,
STATIC_MUT_REFS,
hir_id,
span,
errors::RefOfMutStatic { shared, why_note: (), label, sugg },
errors::RefOfMutStatic { span, sugg, shared },
);
}

View file

@ -144,6 +144,7 @@ impl<'tcx> InherentCollect<'tcx> {
let id = id.owner_id.def_id;
let item_span = self.tcx.def_span(id);
let self_ty = self.tcx.type_of(id).instantiate_identity();
let self_ty = peel_off_weak_aliases(self.tcx, self_ty);
match *self_ty.kind() {
ty::Adt(def, _) => self.check_def_id(id, self_ty, def.did()),
ty::Foreign(did) => self.check_def_id(id, self_ty, did),
@ -166,7 +167,7 @@ impl<'tcx> InherentCollect<'tcx> {
| ty::Never
| ty::FnPtr(_)
| ty::Tuple(..) => self.check_primitive_impl(id, self_ty),
ty::Alias(..) | ty::Param(_) => {
ty::Alias(ty::Projection | ty::Inherent | ty::Opaque, _) | ty::Param(_) => {
Err(self.tcx.dcx().emit_err(errors::InherentNominal { span: item_span }))
}
ty::FnDef(..)
@ -174,6 +175,7 @@ impl<'tcx> InherentCollect<'tcx> {
| ty::CoroutineClosure(..)
| ty::Coroutine(..)
| ty::CoroutineWitness(..)
| ty::Alias(ty::Weak, _)
| ty::Bound(..)
| ty::Placeholder(_)
| ty::Infer(_) => {
@ -184,3 +186,30 @@ impl<'tcx> InherentCollect<'tcx> {
}
}
}
/// Peel off all weak alias types in this type until there are none left.
///
/// <div class="warning">
///
/// This assumes that `ty` gets normalized later and that any overflows occurring
/// during said normalization get reported.
///
/// </div>
fn peel_off_weak_aliases<'tcx>(tcx: TyCtxt<'tcx>, mut ty: Ty<'tcx>) -> Ty<'tcx> {
let ty::Alias(ty::Weak, _) = ty.kind() else { return ty };
let limit = tcx.recursion_limit();
let mut depth = 0;
while let ty::Alias(ty::Weak, alias) = ty.kind() {
if !limit.value_within_limit(depth) {
let guar = tcx.dcx().delayed_bug("overflow expanding weak alias type");
return Ty::new_error(tcx, guar);
}
ty = tcx.type_of(alias.def_id).instantiate(tcx, alias.args);
depth += 1;
}
ty
}

View file

@ -943,7 +943,15 @@ impl<'tcx> FieldUniquenessCheckContext<'tcx> {
}
}
hir::TyKind::Path(hir::QPath::Resolved(_, hir::Path { res, .. })) => {
self.check_field_in_nested_adt(self.tcx.adt_def(res.def_id()), field.span);
// If this is a direct path to an ADT, we can check it
// If this is a type alias or non-ADT, `check_unnamed_fields` should verify it
if let Some(def_id) = res.opt_def_id()
&& let Some(local) = def_id.as_local()
&& let Node::Item(item) = self.tcx.hir_node_by_def_id(local)
&& item.is_adt()
{
self.check_field_in_nested_adt(self.tcx.adt_def(def_id), field.span);
}
}
// Abort due to errors (there must be an error if an unnamed field
// has any type kind other than an anonymous adt or a named adt)

View file

@ -307,7 +307,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
tcx,
&mut predicates,
trait_ref,
&mut cgp::parameters_for_impl(self_ty, trait_ref),
&mut cgp::parameters_for_impl(tcx, self_ty, trait_ref),
);
}

View file

@ -1,4 +1,5 @@
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor};
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::Span;
@ -27,12 +28,13 @@ impl From<ty::ParamConst> for Parameter {
/// Returns the set of parameters constrained by the impl header.
pub fn parameters_for_impl<'tcx>(
tcx: TyCtxt<'tcx>,
impl_self_ty: Ty<'tcx>,
impl_trait_ref: Option<ty::TraitRef<'tcx>>,
) -> FxHashSet<Parameter> {
let vec = match impl_trait_ref {
Some(tr) => parameters_for(&tr, false),
None => parameters_for(&impl_self_ty, false),
Some(tr) => parameters_for(tcx, &tr, false),
None => parameters_for(tcx, &impl_self_ty, false),
};
vec.into_iter().collect()
}
@ -43,26 +45,47 @@ pub fn parameters_for_impl<'tcx>(
/// of parameters whose values are needed in order to constrain `ty` - these
/// differ, with the latter being a superset, in the presence of projections.
pub fn parameters_for<'tcx>(
tcx: TyCtxt<'tcx>,
t: &impl TypeVisitable<TyCtxt<'tcx>>,
include_nonconstraining: bool,
) -> Vec<Parameter> {
let mut collector = ParameterCollector { parameters: vec![], include_nonconstraining };
let mut collector =
ParameterCollector { tcx, parameters: vec![], include_nonconstraining, depth: 0 };
t.visit_with(&mut collector);
collector.parameters
}
struct ParameterCollector {
struct ParameterCollector<'tcx> {
tcx: TyCtxt<'tcx>,
parameters: Vec<Parameter>,
include_nonconstraining: bool,
depth: usize,
}
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ParameterCollector {
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ParameterCollector<'tcx> {
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
match *t.kind() {
ty::Alias(..) if !self.include_nonconstraining => {
// projections are not injective
ty::Alias(ty::Projection | ty::Inherent | ty::Opaque, _)
if !self.include_nonconstraining =>
{
// Projections are not injective in general.
return ControlFlow::Continue(());
}
ty::Alias(ty::Weak, alias) if !self.include_nonconstraining => {
if !self.tcx.recursion_limit().value_within_limit(self.depth) {
// Other constituent types may still constrain some generic params, consider
// `<T> (Overflow, T)` for example. Therefore we want to continue instead of
// breaking. Only affects diagnostics.
return ControlFlow::Continue(());
}
self.depth += 1;
return ensure_sufficient_stack(|| {
self.tcx
.type_of(alias.def_id)
.instantiate(self.tcx, alias.args)
.visit_with(self)
});
}
ty::Param(data) => {
self.parameters.push(Parameter::from(data));
}
@ -82,7 +105,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ParameterCollector {
fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
match c.kind() {
ty::ConstKind::Unevaluated(..) if !self.include_nonconstraining => {
// Constant expressions are not injective
// Constant expressions are not injective in general.
return c.ty().visit_with(self);
}
ty::ConstKind::Param(data) => {
@ -201,12 +224,12 @@ pub fn setup_constraining_predicates<'tcx>(
// `<<T as Bar>::Baz as Iterator>::Output = <U as Iterator>::Output`
// Then the projection only applies if `T` is known, but it still
// does not determine `U`.
let inputs = parameters_for(&projection.projection_ty, true);
let inputs = parameters_for(tcx, &projection.projection_ty, true);
let relies_only_on_inputs = inputs.iter().all(|p| input_parameters.contains(p));
if !relies_only_on_inputs {
continue;
}
input_parameters.extend(parameters_for(&projection.term, false));
input_parameters.extend(parameters_for(tcx, &projection.term, false));
} else {
continue;
}

View file

@ -143,6 +143,7 @@ pub struct WrongNumberOfGenericArgumentsToIntrinsic<'a> {
#[derive(Diagnostic)]
#[diag(hir_analysis_unrecognized_intrinsic_function, code = E0093)]
#[help]
pub struct UnrecognizedIntrinsicFunction {
#[primary_span]
#[label]
@ -661,6 +662,13 @@ pub(crate) struct InvalidUnionField {
pub note: (),
}
#[derive(Diagnostic)]
#[diag(hir_analysis_invalid_unnamed_field_ty)]
pub struct InvalidUnnamedFieldTy {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_return_type_notation_on_non_rpitit)]
pub(crate) struct ReturnTypeNotationOnNonRpitit<'tcx> {
@ -1455,12 +1463,13 @@ pub struct OnlyCurrentTraitsPointerSugg<'a> {
#[derive(Diagnostic)]
#[diag(hir_analysis_static_mut_ref, code = E0796)]
#[note]
pub struct StaticMutRef {
pub struct StaticMutRef<'a> {
#[primary_span]
#[label]
pub span: Span,
#[subdiagnostic]
pub sugg: StaticMutRefSugg,
pub shared: &'a str,
}
#[derive(Subdiagnostic)]
@ -1491,30 +1500,15 @@ pub enum StaticMutRefSugg {
// STATIC_MUT_REF lint
#[derive(LintDiagnostic)]
#[diag(hir_analysis_static_mut_ref_lint)]
#[diag(hir_analysis_static_mut_refs_lint)]
#[note]
#[note(hir_analysis_why_note)]
pub struct RefOfMutStatic<'a> {
pub shared: &'a str,
#[note(hir_analysis_why_note)]
pub why_note: (),
#[subdiagnostic]
pub label: RefOfMutStaticLabel,
#[label]
pub span: Span,
#[subdiagnostic]
pub sugg: RefOfMutStaticSugg,
}
#[derive(Subdiagnostic)]
pub enum RefOfMutStaticLabel {
#[label(hir_analysis_label)]
Shared {
#[primary_span]
span: Span,
},
#[label(hir_analysis_label_mut)]
Mut {
#[primary_span]
span: Span,
},
pub shared: &'a str,
}
#[derive(Subdiagnostic)]

View file

@ -94,7 +94,7 @@ fn enforce_impl_params_are_constrained(
let impl_predicates = tcx.predicates_of(impl_def_id);
let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).map(ty::EarlyBinder::instantiate_identity);
let mut input_parameters = cgp::parameters_for_impl(impl_self_ty, impl_trait_ref);
let mut input_parameters = cgp::parameters_for_impl(tcx, impl_self_ty, impl_trait_ref);
cgp::identify_constrained_generic_params(
tcx,
impl_predicates,
@ -111,7 +111,7 @@ fn enforce_impl_params_are_constrained(
match item.kind {
ty::AssocKind::Type => {
if item.defaultness(tcx).has_value() {
cgp::parameters_for(&tcx.type_of(def_id).instantiate_identity(), true)
cgp::parameters_for(tcx, &tcx.type_of(def_id).instantiate_identity(), true)
} else {
vec![]
}

View file

@ -266,15 +266,15 @@ fn unconstrained_parent_impl_args<'tcx>(
continue;
}
unconstrained_parameters.extend(cgp::parameters_for(&projection_ty, true));
unconstrained_parameters.extend(cgp::parameters_for(tcx, &projection_ty, true));
for param in cgp::parameters_for(&projected_ty, false) {
for param in cgp::parameters_for(tcx, &projected_ty, false) {
if !unconstrained_parameters.contains(&param) {
constrained_params.insert(param.0);
}
}
unconstrained_parameters.extend(cgp::parameters_for(&projected_ty, true));
unconstrained_parameters.extend(cgp::parameters_for(tcx, &projected_ty, true));
}
}
@ -312,7 +312,7 @@ fn check_duplicate_params<'tcx>(
parent_args: &Vec<GenericArg<'tcx>>,
span: Span,
) -> Result<(), ErrorGuaranteed> {
let mut base_params = cgp::parameters_for(parent_args, true);
let mut base_params = cgp::parameters_for(tcx, parent_args, true);
base_params.sort_by_key(|param| param.0);
if let (_, [duplicate, ..]) = base_params.partition_dedup() {
let param = impl1_args[duplicate.0 as usize];

View file

@ -1,5 +1,5 @@
use crate::{errors, structured_errors::StructuredDiagnostic};
use rustc_errors::{codes::*, DiagnosticBuilder, ErrCode};
use rustc_errors::{codes::*, DiagnosticBuilder};
use rustc_middle::ty::{Ty, TypeVisitableExt};
use rustc_session::Session;
use rustc_span::Span;

View file

@ -1,5 +1,5 @@
use crate::{errors, structured_errors::StructuredDiagnostic};
use rustc_errors::{codes::*, DiagnosticBuilder, ErrCode};
use rustc_errors::{codes::*, DiagnosticBuilder};
use rustc_middle::ty::{Ty, TypeVisitableExt};
use rustc_session::Session;
use rustc_span::Span;

View file

@ -1,7 +1,5 @@
use crate::structured_errors::StructuredDiagnostic;
use rustc_errors::{
codes::*, pluralize, Applicability, Diagnostic, DiagnosticBuilder, ErrCode, MultiSpan,
};
use rustc_errors::{codes::*, pluralize, Applicability, Diagnostic, DiagnosticBuilder, MultiSpan};
use rustc_hir as hir;
use rustc_middle::ty::{self as ty, AssocItems, AssocKind, TyCtxt};
use rustc_session::Session;

View file

@ -293,7 +293,7 @@ pub enum HelpUseLatestEdition {
impl HelpUseLatestEdition {
pub fn new() -> Self {
let edition = LATEST_STABLE_EDITION;
if std::env::var_os("CARGO").is_some() {
if rustc_session::utils::was_invoked_from_cargo() {
Self::Cargo { edition }
} else {
Self::Standalone { edition }

View file

@ -27,7 +27,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_data_structures::unord::UnordMap;
use rustc_errors::{
codes::*, pluralize, struct_span_code_err, AddToDiagnostic, Applicability, Diagnostic,
DiagnosticBuilder, ErrCode, ErrorGuaranteed, StashKey,
DiagnosticBuilder, ErrorGuaranteed, StashKey,
};
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, DefKind, Res};

View file

@ -13,7 +13,7 @@ use itertools::Itertools;
use rustc_ast as ast;
use rustc_data_structures::fx::FxIndexSet;
use rustc_errors::{
codes::*, pluralize, Applicability, Diagnostic, ErrCode, ErrorGuaranteed, MultiSpan, StashKey,
codes::*, pluralize, Applicability, Diagnostic, ErrorGuaranteed, MultiSpan, StashKey,
};
use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind, Res};

View file

@ -53,7 +53,7 @@ use crate::expectation::Expectation;
use crate::fn_ctxt::LoweredTy;
use crate::gather_locals::GatherLocalsVisitor;
use rustc_data_structures::unord::UnordSet;
use rustc_errors::{codes::*, struct_span_code_err, ErrCode, ErrorGuaranteed};
use rustc_errors::{codes::*, struct_span_code_err, ErrorGuaranteed};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit::Visitor;

View file

@ -492,7 +492,7 @@ fn lock_directory(
lock_err,
session_dir,
is_unsupported_lock,
is_cargo: std::env::var_os("CARGO").map(|_| ()),
is_cargo: rustc_session::utils::was_invoked_from_cargo().then_some(()),
}))
}
}

View file

@ -5,7 +5,7 @@ use crate::errors::{
use crate::infer::error_reporting::TypeErrCtxt;
use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use crate::infer::InferCtxt;
use rustc_errors::{codes::*, DiagnosticBuilder, ErrCode, IntoDiagnosticArg};
use rustc_errors::{codes::*, DiagnosticBuilder, IntoDiagnosticArg};
use rustc_hir as hir;
use rustc_hir::def::Res;
use rustc_hir::def::{CtorOf, DefKind, Namespace};

View file

@ -205,7 +205,7 @@ pub(super) fn builtin(
Vec::new()
};
let is_from_cargo = std::env::var_os("CARGO").is_some();
let is_from_cargo = rustc_session::utils::was_invoked_from_cargo();
let mut is_feature_cfg = name == sym::feature;
if is_feature_cfg && is_from_cargo {
@ -340,7 +340,7 @@ pub(super) fn builtin(
.copied()
.flatten()
.collect();
let is_from_cargo = std::env::var_os("CARGO").is_some();
let is_from_cargo = rustc_session::utils::was_invoked_from_cargo();
// Show the full list if all possible values for a given name, but don't do it
// for names as the possibilities could be very long

View file

@ -325,6 +325,7 @@ fn register_builtins(store: &mut LintStore) {
store.register_renamed("or_patterns_back_compat", "rust_2021_incompatible_or_patterns");
store.register_renamed("non_fmt_panic", "non_fmt_panics");
store.register_renamed("unused_tuple_struct_fields", "dead_code");
store.register_renamed("static_mut_ref", "static_mut_refs");
// These were moved to tool lints, but rustc still sees them when compiling normally, before
// tool lints are registered, so `check_tool_name_for_backwards_compat` doesn't work. Use

View file

@ -89,7 +89,7 @@ declare_lint_pass! {
SINGLE_USE_LIFETIMES,
SOFT_UNSTABLE,
STABLE_FEATURES,
STATIC_MUT_REF,
STATIC_MUT_REFS,
SUSPICIOUS_AUTO_TRAIT_IMPLS,
TEST_UNSTABLE_LINT,
TEXT_DIRECTION_CODEPOINT_IN_COMMENT,
@ -1769,7 +1769,7 @@ declare_lint! {
}
declare_lint! {
/// The `static_mut_ref` lint checks for shared or mutable references
/// The `static_mut_refs` lint checks for shared or mutable references
/// of mutable static inside `unsafe` blocks and `unsafe` functions.
///
/// ### Example
@ -1807,9 +1807,9 @@ declare_lint! {
/// Shared or mutable references of mutable static are almost always a mistake and
/// can lead to undefined behavior and various other problems in your code.
///
/// This lint is "warn" by default on editions up to 2021, from 2024 there is
/// This lint is "warn" by default on editions up to 2021, in 2024 there is
/// a hard error instead.
pub STATIC_MUT_REF,
pub STATIC_MUT_REFS,
Warn,
"shared references or mutable references of mutable static is discouraged",
@future_incompatible = FutureIncompatibleInfo {

View file

@ -13,7 +13,7 @@ use rustc_data_structures::unhash::UnhashMap;
use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind};
use rustc_expand::proc_macro::{AttrProcMacro, BangProcMacro, DeriveProcMacro};
use rustc_hir::def::Res;
use rustc_hir::def_id::{DefIdMap, CRATE_DEF_INDEX, LOCAL_CRATE};
use rustc_hir::def_id::{CRATE_DEF_INDEX, LOCAL_CRATE};
use rustc_hir::definitions::{DefPath, DefPathData};
use rustc_hir::diagnostic_items::DiagnosticItems;
use rustc_index::Idx;

View file

@ -15,7 +15,7 @@ use crate::ty::{Region, UserTypeAnnotationIndex};
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
use rustc_data_structures::packed::Pu128;
use rustc_hir::def_id::DefId;
use rustc_hir::{self, CoroutineKind};
use rustc_hir::CoroutineKind;
use rustc_index::IndexVec;
use rustc_span::source_map::Spanned;
use rustc_target::abi::{FieldIdx, VariantIdx};

View file

@ -1,161 +0,0 @@
//! Propagates constants for early reporting of statically known
//! assertion failures
use rustc_index::bit_set::BitSet;
use rustc_index::IndexVec;
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::*;
use rustc_middle::ty::{ParamEnv, TyCtxt};
use rustc_target::abi::Size;
/// The maximum number of bytes that we'll allocate space for a local or the return value.
/// Needed for #66397, because otherwise we eval into large places and that can cause OOM or just
/// Severely regress performance.
const MAX_ALLOC_LIMIT: u64 = 1024;
/// Macro for machine-specific `InterpError` without allocation.
/// (These will never be shown to the user, but they help diagnose ICEs.)
pub(crate) macro throw_machine_stop_str($($tt:tt)*) {{
// We make a new local type for it. The type itself does not carry any information,
// but its vtable (for the `MachineStopType` trait) does.
#[derive(Debug)]
struct Zst;
// Printing this type shows the desired string.
impl std::fmt::Display for Zst {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, $($tt)*)
}
}
impl rustc_middle::mir::interpret::MachineStopType for Zst {
fn diagnostic_message(&self) -> rustc_errors::DiagnosticMessage {
self.to_string().into()
}
fn add_args(
self: Box<Self>,
_: &mut dyn FnMut(rustc_errors::DiagnosticArgName, rustc_errors::DiagnosticArgValue),
) {}
}
throw_machine_stop!(Zst)
}}
/// The mode that `ConstProp` is allowed to run in for a given `Local`.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ConstPropMode {
/// The `Local` can be propagated into and reads of this `Local` can also be propagated.
FullConstProp,
/// The `Local` can only be propagated into and from its own block.
OnlyInsideOwnBlock,
/// The `Local` cannot be part of propagation at all. Any statement
/// referencing it either for reading or writing will not get propagated.
NoPropagation,
}
pub struct CanConstProp {
can_const_prop: IndexVec<Local, ConstPropMode>,
// False at the beginning. Once set, no more assignments are allowed to that local.
found_assignment: BitSet<Local>,
}
impl CanConstProp {
/// Returns true if `local` can be propagated
pub fn check<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
body: &Body<'tcx>,
) -> IndexVec<Local, ConstPropMode> {
let mut cpv = CanConstProp {
can_const_prop: IndexVec::from_elem(ConstPropMode::FullConstProp, &body.local_decls),
found_assignment: BitSet::new_empty(body.local_decls.len()),
};
for (local, val) in cpv.can_const_prop.iter_enumerated_mut() {
let ty = body.local_decls[local].ty;
match tcx.layout_of(param_env.and(ty)) {
Ok(layout) if layout.size < Size::from_bytes(MAX_ALLOC_LIMIT) => {}
// Either the layout fails to compute, then we can't use this local anyway
// or the local is too large, then we don't want to.
_ => {
*val = ConstPropMode::NoPropagation;
continue;
}
}
}
// Consider that arguments are assigned on entry.
for arg in body.args_iter() {
cpv.found_assignment.insert(arg);
}
cpv.visit_body(body);
cpv.can_const_prop
}
}
impl<'tcx> Visitor<'tcx> for CanConstProp {
fn visit_place(&mut self, place: &Place<'tcx>, mut context: PlaceContext, loc: Location) {
use rustc_middle::mir::visit::PlaceContext::*;
// Dereferencing just read the addess of `place.local`.
if place.projection.first() == Some(&PlaceElem::Deref) {
context = NonMutatingUse(NonMutatingUseContext::Copy);
}
self.visit_local(place.local, context, loc);
self.visit_projection(place.as_ref(), context, loc);
}
fn visit_local(&mut self, local: Local, context: PlaceContext, _: Location) {
use rustc_middle::mir::visit::PlaceContext::*;
match context {
// These are just stores, where the storing is not propagatable, but there may be later
// mutations of the same local via `Store`
| MutatingUse(MutatingUseContext::Call)
| MutatingUse(MutatingUseContext::AsmOutput)
| MutatingUse(MutatingUseContext::Deinit)
// Actual store that can possibly even propagate a value
| MutatingUse(MutatingUseContext::Store)
| MutatingUse(MutatingUseContext::SetDiscriminant) => {
if !self.found_assignment.insert(local) {
match &mut self.can_const_prop[local] {
// If the local can only get propagated in its own block, then we don't have
// to worry about multiple assignments, as we'll nuke the const state at the
// end of the block anyway, and inside the block we overwrite previous
// states as applicable.
ConstPropMode::OnlyInsideOwnBlock => {}
ConstPropMode::NoPropagation => {}
other @ ConstPropMode::FullConstProp => {
trace!(
"local {:?} can't be propagated because of multiple assignments. Previous state: {:?}",
local, other,
);
*other = ConstPropMode::OnlyInsideOwnBlock;
}
}
}
}
// Reading constants is allowed an arbitrary number of times
NonMutatingUse(NonMutatingUseContext::Copy)
| NonMutatingUse(NonMutatingUseContext::Move)
| NonMutatingUse(NonMutatingUseContext::Inspect)
| NonMutatingUse(NonMutatingUseContext::PlaceMention)
| NonUse(_) => {}
// These could be propagated with a smarter analysis or just some careful thinking about
// whether they'd be fine right now.
MutatingUse(MutatingUseContext::Yield)
| MutatingUse(MutatingUseContext::Drop)
| MutatingUse(MutatingUseContext::Retag)
// These can't ever be propagated under any scheme, as we can't reason about indirect
// mutation.
| NonMutatingUse(NonMutatingUseContext::SharedBorrow)
| NonMutatingUse(NonMutatingUseContext::FakeBorrow)
| NonMutatingUse(NonMutatingUseContext::AddressOf)
| MutatingUse(MutatingUseContext::Borrow)
| MutatingUse(MutatingUseContext::AddressOf) => {
trace!("local {:?} can't be propagated because it's used: {:?}", local, context);
self.can_const_prop[local] = ConstPropMode::NoPropagation;
}
MutatingUse(MutatingUseContext::Projection)
| NonMutatingUse(NonMutatingUseContext::Projection) => bug!("visit_place should not pass {context:?} for {local:?}"),
}
}
}

View file

@ -9,17 +9,14 @@ use rustc_const_eval::interpret::{
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def::DefKind;
use rustc_hir::HirId;
use rustc_index::bit_set::BitSet;
use rustc_index::{Idx, IndexVec};
use rustc_middle::mir::visit::Visitor;
use rustc_index::{bit_set::BitSet, Idx, IndexVec};
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::*;
use rustc_middle::ty::layout::{LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout};
use rustc_middle::ty::{self, ConstInt, ParamEnv, ScalarInt, Ty, TyCtxt, TypeVisitableExt};
use rustc_span::Span;
use rustc_target::abi::{Abi, FieldIdx, HasDataLayout, Size, TargetDataLayout, VariantIdx};
use crate::const_prop::CanConstProp;
use crate::const_prop::ConstPropMode;
use crate::dataflow_const_prop::DummyMachine;
use crate::errors::{AssertLint, AssertLintKind};
use crate::MirLint;
@ -849,3 +846,128 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
}
}
}
/// The maximum number of bytes that we'll allocate space for a local or the return value.
/// Needed for #66397, because otherwise we eval into large places and that can cause OOM or just
/// Severely regress performance.
const MAX_ALLOC_LIMIT: u64 = 1024;
/// The mode that `ConstProp` is allowed to run in for a given `Local`.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ConstPropMode {
/// The `Local` can be propagated into and reads of this `Local` can also be propagated.
FullConstProp,
/// The `Local` can only be propagated into and from its own block.
OnlyInsideOwnBlock,
/// The `Local` cannot be part of propagation at all. Any statement
/// referencing it either for reading or writing will not get propagated.
NoPropagation,
}
pub struct CanConstProp {
can_const_prop: IndexVec<Local, ConstPropMode>,
// False at the beginning. Once set, no more assignments are allowed to that local.
found_assignment: BitSet<Local>,
}
impl CanConstProp {
/// Returns true if `local` can be propagated
pub fn check<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
body: &Body<'tcx>,
) -> IndexVec<Local, ConstPropMode> {
let mut cpv = CanConstProp {
can_const_prop: IndexVec::from_elem(ConstPropMode::FullConstProp, &body.local_decls),
found_assignment: BitSet::new_empty(body.local_decls.len()),
};
for (local, val) in cpv.can_const_prop.iter_enumerated_mut() {
let ty = body.local_decls[local].ty;
match tcx.layout_of(param_env.and(ty)) {
Ok(layout) if layout.size < Size::from_bytes(MAX_ALLOC_LIMIT) => {}
// Either the layout fails to compute, then we can't use this local anyway
// or the local is too large, then we don't want to.
_ => {
*val = ConstPropMode::NoPropagation;
continue;
}
}
}
// Consider that arguments are assigned on entry.
for arg in body.args_iter() {
cpv.found_assignment.insert(arg);
}
cpv.visit_body(body);
cpv.can_const_prop
}
}
impl<'tcx> Visitor<'tcx> for CanConstProp {
fn visit_place(&mut self, place: &Place<'tcx>, mut context: PlaceContext, loc: Location) {
use rustc_middle::mir::visit::PlaceContext::*;
// Dereferencing just read the addess of `place.local`.
if place.projection.first() == Some(&PlaceElem::Deref) {
context = NonMutatingUse(NonMutatingUseContext::Copy);
}
self.visit_local(place.local, context, loc);
self.visit_projection(place.as_ref(), context, loc);
}
fn visit_local(&mut self, local: Local, context: PlaceContext, _: Location) {
use rustc_middle::mir::visit::PlaceContext::*;
match context {
// These are just stores, where the storing is not propagatable, but there may be later
// mutations of the same local via `Store`
| MutatingUse(MutatingUseContext::Call)
| MutatingUse(MutatingUseContext::AsmOutput)
| MutatingUse(MutatingUseContext::Deinit)
// Actual store that can possibly even propagate a value
| MutatingUse(MutatingUseContext::Store)
| MutatingUse(MutatingUseContext::SetDiscriminant) => {
if !self.found_assignment.insert(local) {
match &mut self.can_const_prop[local] {
// If the local can only get propagated in its own block, then we don't have
// to worry about multiple assignments, as we'll nuke the const state at the
// end of the block anyway, and inside the block we overwrite previous
// states as applicable.
ConstPropMode::OnlyInsideOwnBlock => {}
ConstPropMode::NoPropagation => {}
other @ ConstPropMode::FullConstProp => {
trace!(
"local {:?} can't be propagated because of multiple assignments. Previous state: {:?}",
local, other,
);
*other = ConstPropMode::OnlyInsideOwnBlock;
}
}
}
}
// Reading constants is allowed an arbitrary number of times
NonMutatingUse(NonMutatingUseContext::Copy)
| NonMutatingUse(NonMutatingUseContext::Move)
| NonMutatingUse(NonMutatingUseContext::Inspect)
| NonMutatingUse(NonMutatingUseContext::PlaceMention)
| NonUse(_) => {}
// These could be propagated with a smarter analysis or just some careful thinking about
// whether they'd be fine right now.
MutatingUse(MutatingUseContext::Yield)
| MutatingUse(MutatingUseContext::Drop)
| MutatingUse(MutatingUseContext::Retag)
// These can't ever be propagated under any scheme, as we can't reason about indirect
// mutation.
| NonMutatingUse(NonMutatingUseContext::SharedBorrow)
| NonMutatingUse(NonMutatingUseContext::FakeBorrow)
| NonMutatingUse(NonMutatingUseContext::AddressOf)
| MutatingUse(MutatingUseContext::Borrow)
| MutatingUse(MutatingUseContext::AddressOf) => {
trace!("local {:?} can't be propagated because it's used: {:?}", local, context);
self.can_const_prop[local] = ConstPropMode::NoPropagation;
}
MutatingUse(MutatingUseContext::Projection)
| NonMutatingUse(NonMutatingUseContext::Projection) => bug!("visit_place should not pass {context:?} for {local:?}"),
}
}
}

View file

@ -21,7 +21,32 @@ use rustc_span::def_id::DefId;
use rustc_span::DUMMY_SP;
use rustc_target::abi::{Abi, FieldIdx, Size, VariantIdx, FIRST_VARIANT};
use crate::const_prop::throw_machine_stop_str;
/// Macro for machine-specific `InterpError` without allocation.
/// (These will never be shown to the user, but they help diagnose ICEs.)
pub(crate) macro throw_machine_stop_str($($tt:tt)*) {{
// We make a new local type for it. The type itself does not carry any information,
// but its vtable (for the `MachineStopType` trait) does.
#[derive(Debug)]
struct Zst;
// Printing this type shows the desired string.
impl std::fmt::Display for Zst {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, $($tt)*)
}
}
impl rustc_middle::mir::interpret::MachineStopType for Zst {
fn diagnostic_message(&self) -> rustc_errors::DiagnosticMessage {
self.to_string().into()
}
fn add_args(
self: Box<Self>,
_: &mut dyn FnMut(rustc_errors::DiagnosticArgName, rustc_errors::DiagnosticArgValue),
) {}
}
throw_machine_stop!(Zst)
}}
// These constants are somewhat random guesses and have not been optimized.
// If `tcx.sess.mir_opt_level() >= 4`, we ignore the limits (this can become very expensive).

View file

@ -59,7 +59,6 @@ mod remove_place_mention;
mod add_subtyping_projections;
pub mod cleanup_post_borrowck;
mod const_debuginfo;
mod const_prop;
mod const_prop_lint;
mod copy_prop;
mod coroutine;

View file

@ -2545,7 +2545,7 @@ pub enum HelpUseLatestEdition {
impl HelpUseLatestEdition {
pub fn new() -> Self {
let edition = LATEST_STABLE_EDITION;
if std::env::var_os("CARGO").is_some() {
if rustc_session::utils::was_invoked_from_cargo() {
Self::Cargo { edition }
} else {
Self::Standalone { edition }

View file

@ -8,11 +8,11 @@
use crate::def_collector::collect_definitions;
use crate::imports::{ImportData, ImportKind};
use crate::macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef};
use crate::Namespace::{self, MacroNS, TypeNS, ValueNS};
use crate::Namespace::{MacroNS, TypeNS, ValueNS};
use crate::{errors, BindingKey, MacroData, NameBindingData};
use crate::{Determinacy, ExternPreludeEntry, Finalize, Module, ModuleKind, ModuleOrUniformRoot};
use crate::{NameBinding, NameBindingKind, ParentScope, PathResult, PerNS, ResolutionError};
use crate::{Resolver, ResolverArenas, Segment, ToNameBinding, VisResolutionError};
use crate::{NameBinding, NameBindingKind, ParentScope, PathResult, ResolutionError};
use crate::{Resolver, ResolverArenas, Segment, ToNameBinding, Used, VisResolutionError};
use rustc_ast::visit::{self, AssocCtxt, Visitor};
use rustc_ast::{self as ast, AssocItem, AssocItemKind, MetaItemKind, StmtKind};
@ -362,7 +362,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
root_span,
root_id,
vis: Cell::new(Some(vis)),
used: Cell::new(false),
used: Default::default(),
});
self.r.indeterminate_imports.push(import);
@ -885,7 +885,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
span: item.span,
module_path: Vec::new(),
vis: Cell::new(Some(vis)),
used: Cell::new(used),
used: Cell::new(used.then_some(Used::Other)),
});
self.r.potentially_unused_imports.push(import);
let imported_binding = self.r.import(binding, import);
@ -1090,7 +1090,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
span,
module_path: Vec::new(),
vis: Cell::new(Some(ty::Visibility::Restricted(CRATE_DEF_ID))),
used: Cell::new(false),
used: Default::default(),
})
};
@ -1261,7 +1261,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
span,
module_path: Vec::new(),
vis: Cell::new(Some(vis)),
used: Cell::new(true),
used: Cell::new(Some(Used::Other)),
});
let import_binding = self.r.import(binding, import);
self.r.define(self.r.graph_root, ident, MacroNS, import_binding);

View file

@ -27,9 +27,10 @@ use crate::imports::ImportKind;
use crate::module_to_string;
use crate::Resolver;
use crate::NameBindingKind;
use rustc_ast as ast;
use rustc_ast::visit::{self, Visitor};
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
use rustc_data_structures::unord::UnordSet;
use rustc_errors::{pluralize, MultiSpan};
use rustc_hir::def::{DefKind, Res};
@ -38,14 +39,14 @@ use rustc_session::lint::BuiltinLintDiagnostics;
use rustc_span::symbol::{kw, Ident};
use rustc_span::{Span, DUMMY_SP};
struct UnusedImport<'a> {
use_tree: &'a ast::UseTree,
struct UnusedImport {
use_tree: ast::UseTree,
use_tree_id: ast::NodeId,
item_span: Span,
unused: UnordSet<ast::NodeId>,
}
impl<'a> UnusedImport<'a> {
impl UnusedImport {
fn add(&mut self, id: ast::NodeId) {
self.unused.insert(id);
}
@ -54,7 +55,7 @@ impl<'a> UnusedImport<'a> {
struct UnusedImportCheckVisitor<'a, 'b, 'tcx> {
r: &'a mut Resolver<'b, 'tcx>,
/// All the (so far) unused imports, grouped path list
unused_imports: FxIndexMap<ast::NodeId, UnusedImport<'a>>,
unused_imports: FxIndexMap<ast::NodeId, UnusedImport>,
extern_crate_items: Vec<ExternCrateToLint>,
base_use_tree: Option<&'a ast::UseTree>,
base_id: ast::NodeId,
@ -100,9 +101,9 @@ impl<'a, 'b, 'tcx> UnusedImportCheckVisitor<'a, 'b, 'tcx> {
}
}
fn unused_import(&mut self, id: ast::NodeId) -> &mut UnusedImport<'a> {
fn unused_import(&mut self, id: ast::NodeId) -> &mut UnusedImport {
let use_tree_id = self.base_id;
let use_tree = self.base_use_tree.unwrap();
let use_tree = self.base_use_tree.unwrap().clone();
let item_span = self.item_span;
self.unused_imports.entry(id).or_insert_with(|| UnusedImport {
@ -197,7 +198,7 @@ enum UnusedSpanResult {
}
fn calc_unused_spans(
unused_import: &UnusedImport<'_>,
unused_import: &UnusedImport,
use_tree: &ast::UseTree,
use_tree_id: ast::NodeId,
) -> UnusedSpanResult {
@ -287,7 +288,7 @@ impl Resolver<'_, '_> {
for import in self.potentially_unused_imports.iter() {
match import.kind {
_ if import.used.get()
_ if import.used.get().is_some()
|| import.expect_vis().is_public()
|| import.span.is_dummy() =>
{
@ -336,7 +337,7 @@ impl Resolver<'_, '_> {
for unused in visitor.unused_imports.values() {
let mut fixes = Vec::new();
let spans = match calc_unused_spans(unused, unused.use_tree, unused.use_tree_id) {
let spans = match calc_unused_spans(unused, &unused.use_tree, unused.use_tree_id) {
UnusedSpanResult::Used => continue,
UnusedSpanResult::FlatUnused(span, remove) => {
fixes.push((remove, String::new()));
@ -483,5 +484,30 @@ impl Resolver<'_, '_> {
BuiltinLintDiagnostics::ExternCrateNotIdiomatic { vis_span, ident_span },
);
}
let unused_imports = visitor.unused_imports;
let mut check_redundant_imports = FxIndexSet::default();
for module in self.arenas.local_modules().iter() {
for (_key, resolution) in self.resolutions(*module).borrow().iter() {
let resolution = resolution.borrow();
if let Some(binding) = resolution.binding
&& let NameBindingKind::Import { import, .. } = binding.kind
&& let ImportKind::Single { id, .. } = import.kind
{
if let Some(unused_import) = unused_imports.get(&import.root_id)
&& unused_import.unused.contains(&id)
{
continue;
}
check_redundant_imports.insert(import);
}
}
}
for import in check_redundant_imports {
self.check_for_redundant_imports(import);
}
}
}

View file

@ -1,5 +1,5 @@
use crate::{ImplTraitContext, Resolver};
use rustc_ast::visit::{self, FnKind};
use rustc_ast::visit::FnKind;
use rustc_ast::*;
use rustc_expand::expand::AstFragment;
use rustc_hir::def::{CtorKind, CtorOf, DefKind};

View file

@ -33,8 +33,8 @@ use crate::errors::{AddedMacroUse, ChangeImportBinding, ChangeImportBindingSugge
use crate::errors::{ConsiderAddingADerive, ExplicitUnsafeTraits, MaybeMissingMacroRulesName};
use crate::imports::{Import, ImportKind};
use crate::late::{PatternSource, Rib};
use crate::path_names_to_string;
use crate::{errors as errs, BindingKey};
use crate::{path_names_to_string, Used};
use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind, BindingError, Finalize};
use crate::{HasGenericParams, MacroRulesScope, Module, ModuleKind, ModuleOrUniformRoot};
use crate::{LexicalScopeBinding, NameBinding, NameBindingKind, PrivacyError, VisResolutionError};
@ -1503,7 +1503,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
);
// Silence the 'unused import' warning we might get,
// since this diagnostic already covers that import.
self.record_use(ident, binding, false);
self.record_use(ident, binding, Used::Other);
return;
}
}

View file

@ -12,8 +12,8 @@ use rustc_span::Span;
use crate::errors::{ParamKindInEnumDiscriminant, ParamKindInNonTrivialAnonConst};
use crate::late::{ConstantHasGenerics, NoConstantGenericsReason, PathSource, Rib, RibKind};
use crate::macros::{sub_namespace_match, MacroRulesScope};
use crate::BindingKey;
use crate::{errors, AmbiguityError, AmbiguityErrorMisc, AmbiguityKind, Determinacy, Finalize};
use crate::{BindingKey, Used};
use crate::{ImportKind, LexicalScopeBinding, Module, ModuleKind, ModuleOrUniformRoot};
use crate::{NameBinding, NameBindingKind, ParentScope, PathResult, PrivacyError, Res};
use crate::{ResolutionError, Resolver, Scope, ScopeSet, Segment, ToNameBinding, Weak};
@ -339,7 +339,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
ident,
ns,
parent_scope,
finalize,
finalize.map(|finalize| Finalize { used: Used::Scope, ..finalize }),
ignore_binding,
);
if let Ok(binding) = item {
@ -506,7 +506,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
ns,
adjusted_parent_scope,
!matches!(scope_set, ScopeSet::Late(..)),
finalize,
finalize.map(|finalize| Finalize { used: Used::Scope, ..finalize }),
ignore_binding,
);
match binding {
@ -857,7 +857,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
.into_iter()
.find_map(|binding| if binding == ignore_binding { None } else { binding });
if let Some(Finalize { path_span, report_private, .. }) = finalize {
if let Some(Finalize { path_span, report_private, used, .. }) = finalize {
let Some(binding) = binding else {
return Err((Determined, Weak::No));
};
@ -901,7 +901,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
}
}
self.record_use(ident, binding, restricted_shadowing);
self.record_use(ident, binding, used);
return Ok(binding);
}

View file

@ -10,9 +10,9 @@ use crate::errors::{
use crate::Determinacy::{self, *};
use crate::Namespace::*;
use crate::{module_to_string, names_to_string, ImportSuggestion};
use crate::{AmbiguityKind, BindingKey, ModuleKind, ResolutionError, Resolver, Segment};
use crate::{AmbiguityKind, BindingKey, ResolutionError, Resolver, Segment};
use crate::{Finalize, Module, ModuleOrUniformRoot, ParentScope, PerNS, ScopeSet};
use crate::{NameBinding, NameBindingData, NameBindingKind, PathResult};
use crate::{NameBinding, NameBindingData, NameBindingKind, PathResult, Used};
use rustc_ast::NodeId;
use rustc_data_structures::fx::FxHashSet;
@ -173,7 +173,7 @@ pub(crate) struct ImportData<'a> {
/// The resolution of `module_path`.
pub imported_module: Cell<Option<ModuleOrUniformRoot<'a>>>,
pub vis: Cell<Option<ty::Visibility>>,
pub used: Cell<bool>,
pub used: Cell<Option<Used>>,
}
/// All imports are unique and allocated on a same arena,
@ -286,7 +286,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
}
self.arenas.alloc_name_binding(NameBindingData {
kind: NameBindingKind::Import { binding, import, used: Cell::new(false) },
kind: NameBindingKind::Import { binding, import },
ambiguity: None,
warn_ambiguity: false,
span: import.span,
@ -485,9 +485,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
resolution.single_imports.remove(&import);
})
});
self.record_use(target, dummy_binding, false);
self.record_use(target, dummy_binding, Used::Other);
} else if import.imported_module.get().is_none() {
import.used.set(true);
import.used.set(Some(Used::Other));
if let Some(id) = import.id() {
self.used_imports.insert(id);
}
@ -1056,11 +1056,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
&& initial_binding.is_extern_crate()
&& !initial_binding.is_import()
{
this.record_use(
ident,
target_binding,
import.module_path.is_empty(),
);
let used = if import.module_path.is_empty() {
Used::Scope
} else {
Used::Other
};
this.record_use(ident, target_binding, used);
}
}
initial_binding.res()
@ -1299,22 +1300,23 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
}
});
self.check_for_redundant_imports(ident, import, source_bindings, target_bindings, target);
debug!("(resolving single import) successfully resolved import");
None
}
fn check_for_redundant_imports(
&mut self,
ident: Ident,
import: Import<'a>,
source_bindings: &PerNS<Cell<Result<NameBinding<'a>, Determinacy>>>,
target_bindings: &PerNS<Cell<Option<NameBinding<'a>>>>,
target: Ident,
) {
pub(crate) fn check_for_redundant_imports(&mut self, import: Import<'a>) {
// This function is only called for single imports.
let ImportKind::Single { id, .. } = import.kind else { unreachable!() };
let ImportKind::Single {
source, target, ref source_bindings, ref target_bindings, id, ..
} = import.kind
else {
unreachable!()
};
// Skip if the import is of the form `use source as target` and source != target.
if source != target {
return;
}
// Skip if the import was produced by a macro.
if import.parent_scope.expansion != LocalExpnId::ROOT {
@ -1323,16 +1325,20 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
// Skip if we are inside a named module (in contrast to an anonymous
// module defined by a block).
if let ModuleKind::Def(..) = import.parent_scope.module.kind {
// Skip if the import is public or was used through non scope-based resolution,
// e.g. through a module-relative path.
if import.used.get() == Some(Used::Other)
|| self.effective_visibilities.is_exported(self.local_def_id(id))
{
return;
}
let mut is_redundant = PerNS { value_ns: None, type_ns: None, macro_ns: None };
let mut is_redundant = true;
let mut redundant_span = PerNS { value_ns: None, type_ns: None, macro_ns: None };
self.per_ns(|this, ns| {
if let Ok(binding) = source_bindings[ns].get() {
if is_redundant && let Ok(binding) = source_bindings[ns].get() {
if binding.res() == Res::Err {
return;
}
@ -1346,18 +1352,19 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
target_bindings[ns].get(),
) {
Ok(other_binding) => {
is_redundant[ns] = Some(
binding.res() == other_binding.res() && !other_binding.is_ambiguity(),
);
redundant_span[ns] = Some((other_binding.span, other_binding.is_import()));
is_redundant =
binding.res() == other_binding.res() && !other_binding.is_ambiguity();
if is_redundant {
redundant_span[ns] =
Some((other_binding.span, other_binding.is_import()));
}
}
Err(_) => is_redundant[ns] = Some(false),
Err(_) => is_redundant = false,
}
}
});
if !is_redundant.is_empty() && is_redundant.present_items().all(|is_redundant| is_redundant)
{
if is_redundant && !redundant_span.is_empty() {
let mut redundant_spans: Vec<_> = redundant_span.present_items().collect();
redundant_spans.sort();
redundant_spans.dedup();
@ -1365,8 +1372,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
UNUSED_IMPORTS,
id,
import.span,
format!("the item `{ident}` is imported redundantly"),
BuiltinLintDiagnostics::RedundantImport(redundant_spans, ident),
format!("the item `{source}` is imported redundantly"),
BuiltinLintDiagnostics::RedundantImport(redundant_spans, source),
);
}
}

View file

@ -7,23 +7,22 @@
//! `build_reduced_graph.rs`, `macros.rs` and `imports.rs`.
use crate::errors::ImportsCannotReferTo;
use crate::BindingKey;
use crate::{path_names_to_string, rustdoc, BindingError, Finalize, LexicalScopeBinding};
use crate::{BindingKey, Used};
use crate::{Module, ModuleOrUniformRoot, NameBinding, ParentScope, PathResult};
use crate::{ResolutionError, Resolver, Segment, UseError};
use rustc_ast::ptr::P;
use rustc_ast::visit::{self, AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor};
use rustc_ast::visit::{AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor};
use rustc_ast::*;
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
use rustc_errors::{
codes::*, struct_span_code_err, Applicability, DiagnosticArgValue, ErrCode, IntoDiagnosticArg,
StashKey,
codes::*, struct_span_code_err, Applicability, DiagnosticArgValue, IntoDiagnosticArg, StashKey,
};
use rustc_hir::def::Namespace::{self, *};
use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, NonMacroAttrKind, PartialRes, PerNS};
use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE};
use rustc_hir::{BindingAnnotation, PrimTy, TraitCandidate};
use rustc_hir::{PrimTy, TraitCandidate};
use rustc_metadata::creader::CStore;
use rustc_middle::middle::resolve_bound_vars::Set1;
use rustc_middle::{bug, span_bug};
@ -3623,7 +3622,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
// whether they can be shadowed by fresh bindings or not, so force an error.
// issues/33118#issuecomment-233962221 (see below) still applies here,
// but we have to ignore it for backward compatibility.
self.r.record_use(ident, binding, false);
self.r.record_use(ident, binding, Used::Other);
return None;
}
LexicalScopeBinding::Item(binding) => (binding.res(), Some(binding)),
@ -3638,7 +3637,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
) if is_syntactic_ambiguity => {
// Disambiguate in favor of a unit struct/variant or constant pattern.
if let Some(binding) = binding {
self.r.record_use(ident, binding, false);
self.r.record_use(ident, binding, Used::Other);
}
Some(res)
}

View file

@ -175,6 +175,23 @@ enum ImplTraitContext {
Universal,
}
/// Used for tracking import use types which will be used for redundant import checking.
/// ### Used::Scope Example
/// ```rust,compile_fail
/// #![deny(unused_imports)]
/// use std::mem::drop;
/// fn main() {
/// let s = Box::new(32);
/// drop(s);
/// }
/// ```
/// Used::Other is for other situations like module-relative uses.
#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)]
enum Used {
Scope,
Other,
}
#[derive(Debug)]
struct BindingError {
name: Symbol,
@ -695,7 +712,7 @@ impl<'a> ToNameBinding<'a> for NameBinding<'a> {
enum NameBindingKind<'a> {
Res(Res),
Module(Module<'a>),
Import { binding: NameBinding<'a>, import: Import<'a>, used: Cell<bool> },
Import { binding: NameBinding<'a>, import: Import<'a> },
}
impl<'a> NameBindingKind<'a> {
@ -1784,15 +1801,15 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
false
}
fn record_use(&mut self, ident: Ident, used_binding: NameBinding<'a>, is_lexical_scope: bool) {
self.record_use_inner(ident, used_binding, is_lexical_scope, used_binding.warn_ambiguity);
fn record_use(&mut self, ident: Ident, used_binding: NameBinding<'a>, used: Used) {
self.record_use_inner(ident, used_binding, used, used_binding.warn_ambiguity);
}
fn record_use_inner(
&mut self,
ident: Ident,
used_binding: NameBinding<'a>,
is_lexical_scope: bool,
used: Used,
warn_ambiguity: bool,
) {
if let Some((b2, kind)) = used_binding.ambiguity {
@ -1810,27 +1827,35 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
self.ambiguity_errors.push(ambiguity_error);
}
}
if let NameBindingKind::Import { import, binding, ref used } = used_binding.kind {
if let NameBindingKind::Import { import, binding } = used_binding.kind {
if let ImportKind::MacroUse { warn_private: true } = import.kind {
let msg = format!("macro `{ident}` is private");
self.lint_buffer().buffer_lint(PRIVATE_MACRO_USE, import.root_id, ident.span, msg);
}
// Avoid marking `extern crate` items that refer to a name from extern prelude,
// but not introduce it, as used if they are accessed from lexical scope.
if is_lexical_scope {
if used == Used::Scope {
if let Some(entry) = self.extern_prelude.get(&ident.normalize_to_macros_2_0()) {
if !entry.introduced_by_item && entry.binding == Some(used_binding) {
return;
}
}
}
used.set(true);
import.used.set(true);
let old_used = import.used.get();
let new_used = Some(used);
if new_used > old_used {
import.used.set(new_used);
}
if let Some(id) = import.id() {
self.used_imports.insert(id);
}
self.add_to_glob_map(import, ident);
self.record_use_inner(ident, binding, false, warn_ambiguity || binding.warn_ambiguity);
self.record_use_inner(
ident,
binding,
Used::Other,
warn_ambiguity || binding.warn_ambiguity,
);
}
}
@ -1985,7 +2010,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
if !entry.is_import() {
self.crate_loader(|c| c.process_path_extern(ident.name, ident.span));
} else if entry.introduced_by_item {
self.record_use(ident, binding, false);
self.record_use(ident, binding, Used::Other);
}
}
binding
@ -2115,7 +2140,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
let is_import = name_binding.is_import();
let span = name_binding.span;
if let Res::Def(DefKind::Fn, _) = res {
self.record_use(ident, name_binding, false);
self.record_use(ident, name_binding, Used::Other);
}
self.main_def = Some(MainDefinition { res, is_import, span });
}
@ -2176,6 +2201,8 @@ struct Finalize {
/// Whether to report privacy errors or silently return "no resolution" for them,
/// similarly to speculative resolution.
report_private: bool,
/// Tracks whether an item is used in scope or used relatively to a module.
used: Used,
}
impl Finalize {
@ -2184,7 +2211,7 @@ impl Finalize {
}
fn with_root_span(node_id: NodeId, path_span: Span, root_span: Span) -> Finalize {
Finalize { node_id, path_span, root_span, report_private: true }
Finalize { node_id, path_span, root_span, report_private: true, used: Used::Other }
}
}

View file

@ -5,7 +5,7 @@ use crate::errors::CannotDetermineMacroResolution;
use crate::errors::{self, AddAsNonDerive, CannotFindIdentInThisScope};
use crate::errors::{MacroExpectedFound, RemoveSurroundingDerive};
use crate::Namespace::*;
use crate::{BuiltinMacroState, Determinacy, MacroData};
use crate::{BuiltinMacroState, Determinacy, MacroData, Used};
use crate::{DeriveData, Finalize, ParentScope, ResolutionError, Resolver, ScopeSet};
use crate::{ModuleKind, ModuleOrUniformRoot, NameBinding, PathResult, Segment, ToNameBinding};
use rustc_ast::expand::StrippedCfgItem;
@ -794,7 +794,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
) {
Ok(binding) => {
let initial_res = initial_binding.map(|initial_binding| {
self.record_use(ident, initial_binding, false);
self.record_use(ident, initial_binding, Used::Other);
initial_binding.res()
});
let res = binding.res();

View file

@ -30,7 +30,7 @@ libc = "0.2"
# tidy-alphabetical-end
[target.'cfg(windows)'.dependencies.windows]
version = "0.48.0"
version = "0.52.0"
features = [
"Win32_Foundation",
"Win32_System_LibraryLoader",

View file

@ -143,7 +143,6 @@ fn current_dll_path() -> Result<PathBuf, String> {
&mut module,
)
}
.ok()
.map_err(|e| e.to_string())?;
let mut filename = vec![0; 1024];

View file

@ -22,7 +22,7 @@ use rustc_errors::emitter::{DynEmitter, HumanEmitter, HumanReadableErrorType};
use rustc_errors::json::JsonEmitter;
use rustc_errors::registry::Registry;
use rustc_errors::{
codes::*, fallback_fluent_bundle, DiagCtxt, DiagnosticBuilder, DiagnosticMessage, ErrCode,
codes::*, fallback_fluent_bundle, DiagCtxt, DiagnosticBuilder, DiagnosticMessage,
ErrorGuaranteed, FatalAbort, FluentBundle, IntoDiagnostic, LazyFallbackBundle, TerminalUrl,
};
use rustc_macros::HashStable_Generic;

View file

@ -1,7 +1,10 @@
use crate::session::Session;
use rustc_data_structures::profiling::VerboseTimingGuard;
use rustc_fs_util::try_canonicalize;
use std::path::{Path, PathBuf};
use std::{
path::{Path, PathBuf},
sync::OnceLock,
};
impl Session {
pub fn timer(&self, what: &'static str) -> VerboseTimingGuard<'_> {
@ -158,3 +161,18 @@ pub fn extra_compiler_flags() -> Option<(Vec<String>, bool)> {
if !result.is_empty() { Some((result, excluded_cargo_defaults)) } else { None }
}
/// Returns whenever rustc was launched by Cargo as opposed to another build system.
///
/// To be used in diagnostics to avoid printing Cargo specific suggestions to other
/// build systems (like Bazel, Buck2, Makefile, ...).
pub fn was_invoked_from_cargo() -> bool {
static FROM_CARGO: OnceLock<bool> = OnceLock::new();
// To be able to detect Cargo, we use the simplest and least intrusive
// way: we check whenever the `CARGO_CRATE_NAME` env is set.
//
// Note that it is common in Makefiles to define the `CARGO` env even
// though we may not have been called by Cargo, so we avoid using it.
*FROM_CARGO.get_or_init(|| std::env::var_os("CARGO_CRATE_NAME").is_some())
}

View file

@ -72,6 +72,7 @@ pub mod fatal_error;
pub mod profiling;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stable_hasher::{Hash128, Hash64, HashStable, StableHasher};
use rustc_data_structures::sync::{FreezeLock, FreezeWriteGuard, Lock, Lrc};
@ -98,6 +99,9 @@ mod tests;
pub struct SessionGlobals {
symbol_interner: symbol::Interner,
span_interner: Lock<span_encoding::SpanInterner>,
/// Maps a macro argument token into use of the corresponding metavariable in the macro body.
/// Collisions are possible and processed in `maybe_use_metavar_location` on best effort basis.
metavar_spans: Lock<FxHashMap<Span, Span>>,
hygiene_data: Lock<hygiene::HygieneData>,
/// A reference to the source map in the `Session`. It's an `Option`
@ -115,6 +119,7 @@ impl SessionGlobals {
SessionGlobals {
symbol_interner: symbol::Interner::fresh(),
span_interner: Lock::new(span_encoding::SpanInterner::default()),
metavar_spans: Default::default(),
hygiene_data: Lock::new(hygiene::HygieneData::new(edition)),
source_map: Lock::new(None),
}
@ -168,6 +173,11 @@ pub fn create_default_session_globals_then<R>(f: impl FnOnce() -> R) -> R {
// deserialization.
scoped_tls::scoped_thread_local!(static SESSION_GLOBALS: SessionGlobals);
#[inline]
pub fn with_metavar_spans<R>(f: impl FnOnce(&mut FxHashMap<Span, Span>) -> R) -> R {
with_session_globals(|session_globals| f(&mut session_globals.metavar_spans.lock()))
}
// FIXME: We should use this enum or something like it to get rid of the
// use of magic `/rust/1.x/...` paths across the board.
#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Decodable)]
@ -824,29 +834,64 @@ impl Span {
)
}
/// Check if you can select metavar spans for the given spans to get matching contexts.
fn try_metavars(a: SpanData, b: SpanData, a_orig: Span, b_orig: Span) -> (SpanData, SpanData) {
let get = |mspans: &FxHashMap<_, _>, s| mspans.get(&s).copied();
match with_metavar_spans(|mspans| (get(mspans, a_orig), get(mspans, b_orig))) {
(None, None) => {}
(Some(meta_a), None) => {
let meta_a = meta_a.data();
if meta_a.ctxt == b.ctxt {
return (meta_a, b);
}
}
(None, Some(meta_b)) => {
let meta_b = meta_b.data();
if a.ctxt == meta_b.ctxt {
return (a, meta_b);
}
}
(Some(meta_a), Some(meta_b)) => {
let meta_b = meta_b.data();
if a.ctxt == meta_b.ctxt {
return (a, meta_b);
}
let meta_a = meta_a.data();
if meta_a.ctxt == b.ctxt {
return (meta_a, b);
} else if meta_a.ctxt == meta_b.ctxt {
return (meta_a, meta_b);
}
}
}
(a, b)
}
/// Prepare two spans to a combine operation like `to` or `between`.
/// FIXME: consider using declarative macro metavariable spans for the given spans if they are
/// better suitable for combining (#119412).
fn prepare_to_combine(
a_orig: Span,
b_orig: Span,
) -> Result<(SpanData, SpanData, Option<LocalDefId>), Span> {
let (a, b) = (a_orig.data(), b_orig.data());
if a.ctxt != b.ctxt {
// Context mismatches usually happen when procedural macros combine spans copied from
// the macro input with spans produced by the macro (`Span::*_site`).
// In that case we consider the combined span to be produced by the macro and return
// the original macro-produced span as the result.
// Otherwise we just fall back to returning the first span.
// Combining locations typically doesn't make sense in case of context mismatches.
// `is_root` here is a fast path optimization.
let a_is_callsite = a.ctxt.is_root() || a.ctxt == b.span().source_callsite().ctxt();
return Err(if a_is_callsite { b_orig } else { a_orig });
if a.ctxt == b.ctxt {
return Ok((a, b, if a.parent == b.parent { a.parent } else { None }));
}
let parent = if a.parent == b.parent { a.parent } else { None };
Ok((a, b, parent))
let (a, b) = Span::try_metavars(a, b, a_orig, b_orig);
if a.ctxt == b.ctxt {
return Ok((a, b, if a.parent == b.parent { a.parent } else { None }));
}
// Context mismatches usually happen when procedural macros combine spans copied from
// the macro input with spans produced by the macro (`Span::*_site`).
// In that case we consider the combined span to be produced by the macro and return
// the original macro-produced span as the result.
// Otherwise we just fall back to returning the first span.
// Combining locations typically doesn't make sense in case of context mismatches.
// `is_root` here is a fast path optimization.
let a_is_callsite = a.ctxt.is_root() || a.ctxt == b.span().source_callsite().ctxt();
Err(if a_is_callsite { b_orig } else { a_orig })
}
/// This span, but in a larger context, may switch to the metavariable span if suitable.

View file

@ -1,7 +1,5 @@
use super::*;
use rustc_data_structures::sync::FreezeLock;
fn init_source_map() -> SourceMap {
let sm = SourceMap::new(FilePathMapping::empty());
sm.new_source_file(PathBuf::from("blork.rs").into(), "first line.\nsecond line".to_string());

View file

@ -3689,6 +3689,24 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
if let Node::Expr(expr) = tcx.hir_node(arg_hir_id)
&& let Some(typeck_results) = &self.typeck_results
{
if let hir::Expr { kind: hir::ExprKind::MethodCall(_, rcvr, _, _), .. } = expr
&& let Some(ty) = typeck_results.node_type_opt(rcvr.hir_id)
&& let Some(failed_pred) = failed_pred.to_opt_poly_trait_pred()
&& let pred = failed_pred.map_bound(|pred| pred.with_self_ty(tcx, ty))
&& self.predicate_must_hold_modulo_regions(&Obligation::misc(
tcx, expr.span, body_id, param_env, pred,
))
{
err.span_suggestion_verbose(
expr.span.with_lo(rcvr.span.hi()),
format!(
"consider removing this method call, as the receiver has type `{ty}` and \
`{pred}` trivially holds",
),
"",
Applicability::MaybeIncorrect,
);
}
if let hir::Expr { kind: hir::ExprKind::Block(block, _), .. } = expr {
let inner_expr = expr.peel_blocks();
let ty = typeck_results
@ -3824,7 +3842,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
}
}
if let Node::Expr(expr) = tcx.hir_node(call_hir_id) {
if let Node::Expr(expr) = call_node {
if let hir::ExprKind::Call(hir::Expr { span, .. }, _)
| hir::ExprKind::MethodCall(
hir::PathSegment { ident: Ident { span, .. }, .. },

View file

@ -829,7 +829,7 @@
# target triples containing `-none`, `nvptx`, `switch`, or `-uefi`.
#no-std = <platform-specific> (bool)
# This is an array of the codegen backends that will be compiled a rustc
# This is an array of the codegen backends that will be
# compiled for this target, overriding the global rust.codegen-backends option.
# See that option for more info.
#codegen-backends = rust.codegen-backends (array)

View file

@ -261,7 +261,7 @@ impl<T, A: Allocator> RawVec<T, A> {
// and could hypothetically handle differences between stride and size, but this memory
// has already been allocated so we know it can't overflow and currently rust does not
// support such types. So we can do better by skipping some checks and avoid an unwrap.
let _: () = const { assert!(mem::size_of::<T>() % mem::align_of::<T>() == 0) };
const { assert!(mem::size_of::<T>() % mem::align_of::<T>() == 0) };
unsafe {
let align = mem::align_of::<T>();
let size = mem::size_of::<T>().unchecked_mul(self.cap.0);
@ -465,7 +465,7 @@ impl<T, A: Allocator> RawVec<T, A> {
let (ptr, layout) = if let Some(mem) = self.current_memory() { mem } else { return Ok(()) };
// See current_memory() why this assert is here
let _: () = const { assert!(mem::size_of::<T>() % mem::align_of::<T>() == 0) };
const { assert!(mem::size_of::<T>() % mem::align_of::<T>() == 0) };
// If shrinking to 0, deallocate the buffer. We don't reach this point
// for the T::IS_ZST case since current_memory() will have returned

View file

@ -261,8 +261,9 @@ cfg_if::cfg_if! {
}
}
// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint
#[allow(static_mut_ref)]
// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_refs` lint
#[cfg_attr(bootstrap, allow(static_mut_ref))]
#[cfg_attr(not(bootstrap), allow(static_mut_refs))]
pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
use core::intrinsics::atomic_store_seqcst;
@ -324,8 +325,9 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
_CxxThrowException(throw_ptr, &mut THROW_INFO as *mut _ as *mut _);
}
// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint
#[allow(static_mut_ref)]
// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_refs` lint
#[cfg_attr(bootstrap, allow(static_mut_ref))]
#[cfg_attr(not(bootstrap), allow(static_mut_refs))]
pub unsafe fn cleanup(payload: *mut u8) -> Box<dyn Any + Send> {
// A null payload here means that we got here from the catch (...) of
// __rust_try. This happens when a non-Rust foreign exception is caught.

View file

@ -1,169 +0,0 @@
//! This module contains the LLVM intrinsics bindings that provide the functionality for this
//! crate.
//!
//! The LLVM assembly language is documented here: <https://llvm.org/docs/LangRef.html>
//!
//! A quick glossary of jargon that may appear in this module, mostly paraphrasing LLVM's LangRef:
//! - poison: "undefined behavior as a value". specifically, it is like uninit memory (such as padding bytes). it is "safe" to create poison, BUT
//! poison MUST NOT be observed from safe code, as operations on poison return poison, like NaN. unlike NaN, which has defined comparisons,
//! poison is neither true nor false, and LLVM may also convert it to undef (at which point it is both). so, it can't be conditioned on, either.
//! - undef: "a value that is every value". functionally like poison, insofar as Rust is concerned. poison may become this. note:
//! this means that division by poison or undef is like division by zero, which means it inflicts...
//! - "UB": poison and undef cover most of what people call "UB". "UB" means this operation immediately invalidates the program:
//! LLVM is allowed to lower it to `ud2` or other opcodes that may cause an illegal instruction exception, and this is the "good end".
//! The "bad end" is that LLVM may reverse time to the moment control flow diverged on a path towards undefined behavior,
//! and destroy the other branch, potentially deleting safe code and violating Rust's `unsafe` contract.
//!
//! Note that according to LLVM, vectors are not arrays, but they are equivalent when stored to and loaded from memory.
//!
//! Unless stated otherwise, all intrinsics for binary operations require SIMD vectors of equal types and lengths.
// These intrinsics aren't linked directly from LLVM and are mostly undocumented, however they are
// mostly lowered to the matching LLVM instructions by the compiler in a fairly straightforward manner.
// The associated LLVM instruction or intrinsic is documented alongside each Rust intrinsic function.
extern "platform-intrinsic" {
/// add/fadd
pub(crate) fn simd_add<T>(x: T, y: T) -> T;
/// sub/fsub
pub(crate) fn simd_sub<T>(lhs: T, rhs: T) -> T;
/// mul/fmul
pub(crate) fn simd_mul<T>(x: T, y: T) -> T;
/// udiv/sdiv/fdiv
/// ints and uints: {s,u}div incur UB if division by zero occurs.
/// ints: sdiv is UB for int::MIN / -1.
/// floats: fdiv is never UB, but may create NaNs or infinities.
pub(crate) fn simd_div<T>(lhs: T, rhs: T) -> T;
/// urem/srem/frem
/// ints and uints: {s,u}rem incur UB if division by zero occurs.
/// ints: srem is UB for int::MIN / -1.
/// floats: frem is equivalent to libm::fmod in the "default" floating point environment, sans errno.
pub(crate) fn simd_rem<T>(lhs: T, rhs: T) -> T;
/// shl
/// for (u)ints. poison if rhs >= lhs::BITS
pub(crate) fn simd_shl<T>(lhs: T, rhs: T) -> T;
/// ints: ashr
/// uints: lshr
/// poison if rhs >= lhs::BITS
pub(crate) fn simd_shr<T>(lhs: T, rhs: T) -> T;
/// and
pub(crate) fn simd_and<T>(x: T, y: T) -> T;
/// or
pub(crate) fn simd_or<T>(x: T, y: T) -> T;
/// xor
pub(crate) fn simd_xor<T>(x: T, y: T) -> T;
/// fptoui/fptosi/uitofp/sitofp
/// casting floats to integers is truncating, so it is safe to convert values like e.g. 1.5
/// but the truncated value must fit in the target type or the result is poison.
/// use `simd_as` instead for a cast that performs a saturating conversion.
pub(crate) fn simd_cast<T, U>(x: T) -> U;
/// follows Rust's `T as U` semantics, including saturating float casts
/// which amounts to the same as `simd_cast` for many cases
pub(crate) fn simd_as<T, U>(x: T) -> U;
/// neg/fneg
/// ints: ultimately becomes a call to cg_ssa's BuilderMethods::neg. cg_llvm equates this to `simd_sub(Simd::splat(0), x)`.
/// floats: LLVM's fneg, which changes the floating point sign bit. Some arches have instructions for it.
/// Rust panics for Neg::neg(int::MIN) due to overflow, but it is not UB in LLVM without `nsw`.
pub(crate) fn simd_neg<T>(x: T) -> T;
/// fabs
pub(crate) fn simd_fabs<T>(x: T) -> T;
// minnum/maxnum
pub(crate) fn simd_fmin<T>(x: T, y: T) -> T;
pub(crate) fn simd_fmax<T>(x: T, y: T) -> T;
// these return Simd<int, N> with the same BITS size as the inputs
pub(crate) fn simd_eq<T, U>(x: T, y: T) -> U;
pub(crate) fn simd_ne<T, U>(x: T, y: T) -> U;
pub(crate) fn simd_lt<T, U>(x: T, y: T) -> U;
pub(crate) fn simd_le<T, U>(x: T, y: T) -> U;
pub(crate) fn simd_gt<T, U>(x: T, y: T) -> U;
pub(crate) fn simd_ge<T, U>(x: T, y: T) -> U;
// shufflevector
// idx: LLVM calls it a "shuffle mask vector constant", a vector of i32s
pub(crate) fn simd_shuffle<T, U, V>(x: T, y: T, idx: U) -> V;
/// llvm.masked.gather
/// like a loop of pointer reads
/// val: vector of values to select if a lane is masked
/// ptr: vector of pointers to read from
/// mask: a "wide" mask of integers, selects as if simd_select(mask, read(ptr), val)
/// note, the LLVM intrinsic accepts a mask vector of `<N x i1>`
/// FIXME: review this if/when we fix up our mask story in general?
pub(crate) fn simd_gather<T, U, V>(val: T, ptr: U, mask: V) -> T;
/// llvm.masked.scatter
/// like gather, but more spicy, as it writes instead of reads
pub(crate) fn simd_scatter<T, U, V>(val: T, ptr: U, mask: V);
// {s,u}add.sat
pub(crate) fn simd_saturating_add<T>(x: T, y: T) -> T;
// {s,u}sub.sat
pub(crate) fn simd_saturating_sub<T>(lhs: T, rhs: T) -> T;
// reductions
// llvm.vector.reduce.{add,fadd}
pub(crate) fn simd_reduce_add_ordered<T, U>(x: T, y: U) -> U;
// llvm.vector.reduce.{mul,fmul}
pub(crate) fn simd_reduce_mul_ordered<T, U>(x: T, y: U) -> U;
#[allow(unused)]
pub(crate) fn simd_reduce_all<T>(x: T) -> bool;
#[allow(unused)]
pub(crate) fn simd_reduce_any<T>(x: T) -> bool;
pub(crate) fn simd_reduce_max<T, U>(x: T) -> U;
pub(crate) fn simd_reduce_min<T, U>(x: T) -> U;
pub(crate) fn simd_reduce_and<T, U>(x: T) -> U;
pub(crate) fn simd_reduce_or<T, U>(x: T) -> U;
pub(crate) fn simd_reduce_xor<T, U>(x: T) -> U;
// truncate integer vector to bitmask
// `fn simd_bitmask(vector) -> unsigned integer` takes a vector of integers and
// returns either an unsigned integer or array of `u8`.
// Every element in the vector becomes a single bit in the returned bitmask.
// If the vector has less than 8 lanes, a u8 is returned with zeroed trailing bits.
// The bit order of the result depends on the byte endianness. LSB-first for little
// endian and MSB-first for big endian.
//
// UB if called on a vector with values other than 0 and -1.
#[allow(unused)]
pub(crate) fn simd_bitmask<T, U>(x: T) -> U;
// select
// first argument is a vector of integers, -1 (all bits 1) is "true"
// logically equivalent to (yes & m) | (no & (m^-1),
// but you can use it on floats.
pub(crate) fn simd_select<M, T>(m: M, yes: T, no: T) -> T;
#[allow(unused)]
pub(crate) fn simd_select_bitmask<M, T>(m: M, yes: T, no: T) -> T;
/// getelementptr (without inbounds)
/// equivalent to wrapping_offset
pub(crate) fn simd_arith_offset<T, U>(ptr: T, offset: U) -> T;
/// equivalent to `T as U` semantics, specifically for pointers
pub(crate) fn simd_cast_ptr<T, U>(ptr: T) -> U;
/// expose a pointer as an address
pub(crate) fn simd_expose_addr<T, U>(ptr: T) -> U;
/// convert an exposed address back to a pointer
pub(crate) fn simd_from_exposed_addr<T, U>(addr: T) -> U;
// Integer operations
pub(crate) fn simd_bswap<T>(x: T) -> T;
pub(crate) fn simd_bitreverse<T>(x: T) -> T;
pub(crate) fn simd_ctlz<T>(x: T) -> T;
pub(crate) fn simd_cttz<T>(x: T) -> T;
}

View file

@ -1,20 +1,38 @@
#![no_std]
#![feature(
const_intrinsic_copy,
const_refs_to_cell,
const_maybe_uninit_as_mut_ptr,
const_mut_refs,
convert_float_to_int,
core_intrinsics,
decl_macro,
inline_const,
intra_doc_pointers,
platform_intrinsics,
repr_simd,
simd_ffi,
staged_api,
stdsimd,
strict_provenance,
ptr_metadata
)]
#![cfg_attr(
all(
any(target_arch = "aarch64", target_arch = "arm",),
any(
all(target_feature = "v6", not(target_feature = "mclass")),
all(target_feature = "mclass", target_feature = "dsp"),
)
),
feature(stdarch_arm_dsp)
)]
#![cfg_attr(
all(target_arch = "arm", target_feature = "v7"),
feature(stdarch_arm_neon_intrinsics)
)]
#![cfg_attr(
any(target_arch = "powerpc", target_arch = "powerpc64"),
feature(stdarch_powerpc)
)]
#![warn(missing_docs, clippy::missing_inline_in_public_items)] // basically all items, really
#![deny(unsafe_op_in_unsafe_fn, clippy::undocumented_unsafe_blocks)]
#![allow(internal_features)]

View file

@ -12,9 +12,7 @@
)]
mod mask_impl;
use crate::simd::{
cmp::SimdPartialEq, intrinsics, LaneCount, Simd, SimdCast, SimdElement, SupportedLaneCount,
};
use crate::simd::{LaneCount, Simd, SimdCast, SimdElement, SupportedLaneCount};
use core::cmp::Ordering;
use core::{fmt, mem};
@ -35,7 +33,7 @@ mod sealed {
fn eq(self, other: Self) -> bool;
fn as_usize(self) -> usize;
fn to_usize(self) -> usize;
type Unsigned: SimdElement;
@ -60,14 +58,23 @@ macro_rules! impl_element {
where
LaneCount<N>: SupportedLaneCount,
{
(value.simd_eq(Simd::splat(0 as _)) | value.simd_eq(Simd::splat(-1 as _))).all()
// We can't use `Simd` directly, because `Simd`'s functions call this function and
// we will end up with an infinite loop.
// Safety: `value` is an integer vector
unsafe {
use core::intrinsics::simd;
let falses: Simd<Self, N> = simd::simd_eq(value, Simd::splat(0 as _));
let trues: Simd<Self, N> = simd::simd_eq(value, Simd::splat(-1 as _));
let valid: Simd<Self, N> = simd::simd_or(falses, trues);
simd::simd_reduce_all(valid)
}
}
#[inline]
fn eq(self, other: Self) -> bool { self == other }
#[inline]
fn as_usize(self) -> usize {
fn to_usize(self) -> usize {
self as usize
}
@ -141,8 +148,9 @@ where
// but these are "dependently-sized" types, so copy elision it is!
unsafe {
let bytes: [u8; N] = mem::transmute_copy(&array);
let bools: Simd<i8, N> = intrinsics::simd_ne(Simd::from_array(bytes), Simd::splat(0u8));
Mask::from_int_unchecked(intrinsics::simd_cast(bools))
let bools: Simd<i8, N> =
core::intrinsics::simd::simd_ne(Simd::from_array(bytes), Simd::splat(0u8));
Mask::from_int_unchecked(core::intrinsics::simd::simd_cast(bools))
}
}
@ -160,7 +168,7 @@ where
// This would be hypothetically valid as an "in-place" transmute,
// but these are "dependently-sized" types, so copy elision it is!
unsafe {
let mut bytes: Simd<i8, N> = intrinsics::simd_cast(self.to_int());
let mut bytes: Simd<i8, N> = core::intrinsics::simd::simd_cast(self.to_int());
bytes &= Simd::splat(1i8);
mem::transmute_copy(&bytes)
}
@ -175,7 +183,10 @@ where
#[must_use = "method returns a new mask and does not mutate the original value"]
pub unsafe fn from_int_unchecked(value: Simd<T, N>) -> Self {
// Safety: the caller must confirm this invariant
unsafe { Self(mask_impl::Mask::from_int_unchecked(value)) }
unsafe {
core::intrinsics::assume(<T as Sealed>::valid(value));
Self(mask_impl::Mask::from_int_unchecked(value))
}
}
/// Converts a vector of integers to a mask, where 0 represents `false` and -1
@ -374,15 +385,17 @@ where
);
// Safety: the input and output are integer vectors
let index: Simd<T, N> = unsafe { intrinsics::simd_cast(index) };
let index: Simd<T, N> = unsafe { core::intrinsics::simd::simd_cast(index) };
let masked_index = self.select(index, Self::splat(true).to_int());
// Safety: the input and output are integer vectors
let masked_index: Simd<T::Unsigned, N> = unsafe { intrinsics::simd_cast(masked_index) };
let masked_index: Simd<T::Unsigned, N> =
unsafe { core::intrinsics::simd::simd_cast(masked_index) };
// Safety: the input is an integer vector
let min_index: T::Unsigned = unsafe { intrinsics::simd_reduce_min(masked_index) };
let min_index: T::Unsigned =
unsafe { core::intrinsics::simd::simd_reduce_min(masked_index) };
// Safety: the return value is the unsigned version of T
let min_index: T = unsafe { core::mem::transmute_copy(&min_index) };
@ -390,7 +403,7 @@ where
if min_index.eq(T::TRUE) {
None
} else {
Some(min_index.as_usize())
Some(min_index.to_usize())
}
}
}

View file

@ -1,6 +1,5 @@
#![allow(unused_imports)]
use super::MaskElement;
use crate::simd::intrinsics;
use crate::simd::{LaneCount, Simd, SupportedLaneCount};
use core::marker::PhantomData;
@ -109,14 +108,18 @@ where
#[must_use = "method returns a new vector and does not mutate the original value"]
pub fn to_int(self) -> Simd<T, N> {
unsafe {
intrinsics::simd_select_bitmask(self.0, Simd::splat(T::TRUE), Simd::splat(T::FALSE))
core::intrinsics::simd::simd_select_bitmask(
self.0,
Simd::splat(T::TRUE),
Simd::splat(T::FALSE),
)
}
}
#[inline]
#[must_use = "method returns a new mask and does not mutate the original value"]
pub unsafe fn from_int_unchecked(value: Simd<T, N>) -> Self {
unsafe { Self(intrinsics::simd_bitmask(value), PhantomData) }
unsafe { Self(core::intrinsics::simd::simd_bitmask(value), PhantomData) }
}
#[inline]

View file

@ -1,6 +1,5 @@
//! Masks that take up full SIMD vector registers.
use crate::simd::intrinsics;
use crate::simd::{LaneCount, MaskElement, Simd, SupportedLaneCount};
#[repr(transparent)]
@ -138,7 +137,7 @@ where
U: MaskElement,
{
// Safety: masks are simply integer vectors of 0 and -1, and we can cast the element type.
unsafe { Mask(intrinsics::simd_cast(self.0)) }
unsafe { Mask(core::intrinsics::simd::simd_cast(self.0)) }
}
#[inline]
@ -150,13 +149,16 @@ where
unsafe {
// Compute the bitmask
let mut bytes: <LaneCount<N> as SupportedLaneCount>::BitMask =
intrinsics::simd_bitmask(self.0);
core::intrinsics::simd::simd_bitmask(self.0);
// LLVM assumes bit order should match endianness
if cfg!(target_endian = "big") {
for x in bytes.as_mut() {
*x = x.reverse_bits()
}
if N % 8 > 0 {
bytes.as_mut()[N / 8] >>= 8 - N % 8;
}
}
bitmask.as_mut_array()[..bytes.as_ref().len()].copy_from_slice(bytes.as_ref());
@ -180,10 +182,13 @@ where
for x in bytes.as_mut() {
*x = x.reverse_bits();
}
if N % 8 > 0 {
bytes.as_mut()[N / 8] >>= 8 - N % 8;
}
}
// Compute the regular mask
Self::from_int_unchecked(intrinsics::simd_select_bitmask(
Self::from_int_unchecked(core::intrinsics::simd::simd_select_bitmask(
bytes,
Self::splat(true).to_int(),
Self::splat(false).to_int(),
@ -199,7 +204,7 @@ where
let resized = self.to_int().resize::<M>(T::FALSE);
// Safety: `resized` is an integer vector with length M, which must match T
let bitmask: U = unsafe { intrinsics::simd_bitmask(resized) };
let bitmask: U = unsafe { core::intrinsics::simd::simd_bitmask(resized) };
// LLVM assumes bit order should match endianness
if cfg!(target_endian = "big") {
@ -223,7 +228,7 @@ where
// SAFETY: `mask` is the correct bitmask type for a u64 bitmask
let mask: Simd<T, M> = unsafe {
intrinsics::simd_select_bitmask(
core::intrinsics::simd::simd_select_bitmask(
bitmask,
Simd::<T, M>::splat(T::TRUE),
Simd::<T, M>::splat(T::FALSE),
@ -274,14 +279,14 @@ where
#[must_use = "method returns a new bool and does not mutate the original value"]
pub fn any(self) -> bool {
// Safety: use `self` as an integer vector
unsafe { intrinsics::simd_reduce_any(self.to_int()) }
unsafe { core::intrinsics::simd::simd_reduce_any(self.to_int()) }
}
#[inline]
#[must_use = "method returns a new vector and does not mutate the original value"]
pub fn all(self) -> bool {
// Safety: use `self` as an integer vector
unsafe { intrinsics::simd_reduce_all(self.to_int()) }
unsafe { core::intrinsics::simd::simd_reduce_all(self.to_int()) }
}
}
@ -306,7 +311,7 @@ where
#[must_use = "method returns a new mask and does not mutate the original value"]
fn bitand(self, rhs: Self) -> Self {
// Safety: `self` is an integer vector
unsafe { Self(intrinsics::simd_and(self.0, rhs.0)) }
unsafe { Self(core::intrinsics::simd::simd_and(self.0, rhs.0)) }
}
}
@ -320,7 +325,7 @@ where
#[must_use = "method returns a new mask and does not mutate the original value"]
fn bitor(self, rhs: Self) -> Self {
// Safety: `self` is an integer vector
unsafe { Self(intrinsics::simd_or(self.0, rhs.0)) }
unsafe { Self(core::intrinsics::simd::simd_or(self.0, rhs.0)) }
}
}
@ -334,7 +339,7 @@ where
#[must_use = "method returns a new mask and does not mutate the original value"]
fn bitxor(self, rhs: Self) -> Self {
// Safety: `self` is an integer vector
unsafe { Self(intrinsics::simd_xor(self.0, rhs.0)) }
unsafe { Self(core::intrinsics::simd::simd_xor(self.0, rhs.0)) }
}
}

View file

@ -1,8 +1,6 @@
#[macro_use]
mod swizzle;
pub(crate) mod intrinsics;
mod alias;
mod cast;
mod fmt;
@ -27,8 +25,6 @@ pub mod simd {
pub mod cmp;
pub(crate) use crate::core_simd::intrinsics;
pub use crate::core_simd::alias::*;
pub use crate::core_simd::cast::*;
pub use crate::core_simd::lane_count::{LaneCount, SupportedLaneCount};

View file

@ -37,7 +37,7 @@ where
macro_rules! unsafe_base {
($lhs:ident, $rhs:ident, {$simd_call:ident}, $($_:tt)*) => {
// Safety: $lhs and $rhs are vectors
unsafe { $crate::simd::intrinsics::$simd_call($lhs, $rhs) }
unsafe { core::intrinsics::simd::$simd_call($lhs, $rhs) }
};
}
@ -55,7 +55,7 @@ macro_rules! wrap_bitshift {
#[allow(clippy::suspicious_arithmetic_impl)]
// Safety: $lhs and the bitand result are vectors
unsafe {
$crate::simd::intrinsics::$simd_call(
core::intrinsics::simd::$simd_call(
$lhs,
$rhs.bitand(Simd::splat(<$int>::BITS as $int - 1)),
)
@ -97,7 +97,7 @@ macro_rules! int_divrem_guard {
$rhs
};
// Safety: $lhs and rhs are vectors
unsafe { $crate::simd::intrinsics::$simd_call($lhs, rhs) }
unsafe { core::intrinsics::simd::$simd_call($lhs, rhs) }
}
};
}

View file

@ -1,4 +1,3 @@
use crate::simd::intrinsics;
use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount};
use core::ops::{Neg, Not}; // unary ops
@ -15,7 +14,7 @@ macro_rules! neg {
#[must_use = "operator returns a new vector without mutating the input"]
fn neg(self) -> Self::Output {
// Safety: `self` is a signed vector
unsafe { intrinsics::simd_neg(self) }
unsafe { core::intrinsics::simd::simd_neg(self) }
}
})*
}

View file

@ -1,4 +1,3 @@
use crate::simd::intrinsics;
use crate::simd::{LaneCount, Mask, MaskElement, Simd, SimdElement, SupportedLaneCount};
impl<T, const N: usize> Mask<T, N>
@ -29,7 +28,7 @@ where
{
// Safety: The mask has been cast to a vector of integers,
// and the operands to select between are vectors of the same type and length.
unsafe { intrinsics::simd_select(self.to_int(), true_values, false_values) }
unsafe { core::intrinsics::simd::simd_select(self.to_int(), true_values, false_values) }
}
/// Choose elements from two masks.

View file

@ -1,5 +1,4 @@
use crate::simd::{
intrinsics,
ptr::{SimdConstPtr, SimdMutPtr},
LaneCount, Mask, Simd, SimdElement, SupportedLaneCount,
};
@ -31,14 +30,14 @@ macro_rules! impl_number {
fn simd_eq(self, other: Self) -> Self::Mask {
// Safety: `self` is a vector, and the result of the comparison
// is always a valid mask.
unsafe { Mask::from_int_unchecked(intrinsics::simd_eq(self, other)) }
unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_eq(self, other)) }
}
#[inline]
fn simd_ne(self, other: Self) -> Self::Mask {
// Safety: `self` is a vector, and the result of the comparison
// is always a valid mask.
unsafe { Mask::from_int_unchecked(intrinsics::simd_ne(self, other)) }
unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_ne(self, other)) }
}
}
)*
@ -60,14 +59,14 @@ macro_rules! impl_mask {
fn simd_eq(self, other: Self) -> Self::Mask {
// Safety: `self` is a vector, and the result of the comparison
// is always a valid mask.
unsafe { Self::from_int_unchecked(intrinsics::simd_eq(self.to_int(), other.to_int())) }
unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_eq(self.to_int(), other.to_int())) }
}
#[inline]
fn simd_ne(self, other: Self) -> Self::Mask {
// Safety: `self` is a vector, and the result of the comparison
// is always a valid mask.
unsafe { Self::from_int_unchecked(intrinsics::simd_ne(self.to_int(), other.to_int())) }
unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_ne(self.to_int(), other.to_int())) }
}
}
)*

View file

@ -1,6 +1,5 @@
use crate::simd::{
cmp::SimdPartialEq,
intrinsics,
ptr::{SimdConstPtr, SimdMutPtr},
LaneCount, Mask, Simd, SupportedLaneCount,
};
@ -57,28 +56,28 @@ macro_rules! impl_integer {
fn simd_lt(self, other: Self) -> Self::Mask {
// Safety: `self` is a vector, and the result of the comparison
// is always a valid mask.
unsafe { Mask::from_int_unchecked(intrinsics::simd_lt(self, other)) }
unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_lt(self, other)) }
}
#[inline]
fn simd_le(self, other: Self) -> Self::Mask {
// Safety: `self` is a vector, and the result of the comparison
// is always a valid mask.
unsafe { Mask::from_int_unchecked(intrinsics::simd_le(self, other)) }
unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_le(self, other)) }
}
#[inline]
fn simd_gt(self, other: Self) -> Self::Mask {
// Safety: `self` is a vector, and the result of the comparison
// is always a valid mask.
unsafe { Mask::from_int_unchecked(intrinsics::simd_gt(self, other)) }
unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_gt(self, other)) }
}
#[inline]
fn simd_ge(self, other: Self) -> Self::Mask {
// Safety: `self` is a vector, and the result of the comparison
// is always a valid mask.
unsafe { Mask::from_int_unchecked(intrinsics::simd_ge(self, other)) }
unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_ge(self, other)) }
}
}
@ -123,28 +122,28 @@ macro_rules! impl_float {
fn simd_lt(self, other: Self) -> Self::Mask {
// Safety: `self` is a vector, and the result of the comparison
// is always a valid mask.
unsafe { Mask::from_int_unchecked(intrinsics::simd_lt(self, other)) }
unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_lt(self, other)) }
}
#[inline]
fn simd_le(self, other: Self) -> Self::Mask {
// Safety: `self` is a vector, and the result of the comparison
// is always a valid mask.
unsafe { Mask::from_int_unchecked(intrinsics::simd_le(self, other)) }
unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_le(self, other)) }
}
#[inline]
fn simd_gt(self, other: Self) -> Self::Mask {
// Safety: `self` is a vector, and the result of the comparison
// is always a valid mask.
unsafe { Mask::from_int_unchecked(intrinsics::simd_gt(self, other)) }
unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_gt(self, other)) }
}
#[inline]
fn simd_ge(self, other: Self) -> Self::Mask {
// Safety: `self` is a vector, and the result of the comparison
// is always a valid mask.
unsafe { Mask::from_int_unchecked(intrinsics::simd_ge(self, other)) }
unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_ge(self, other)) }
}
}
)*
@ -164,28 +163,28 @@ macro_rules! impl_mask {
fn simd_lt(self, other: Self) -> Self::Mask {
// Safety: `self` is a vector, and the result of the comparison
// is always a valid mask.
unsafe { Self::from_int_unchecked(intrinsics::simd_lt(self.to_int(), other.to_int())) }
unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_lt(self.to_int(), other.to_int())) }
}
#[inline]
fn simd_le(self, other: Self) -> Self::Mask {
// Safety: `self` is a vector, and the result of the comparison
// is always a valid mask.
unsafe { Self::from_int_unchecked(intrinsics::simd_le(self.to_int(), other.to_int())) }
unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_le(self.to_int(), other.to_int())) }
}
#[inline]
fn simd_gt(self, other: Self) -> Self::Mask {
// Safety: `self` is a vector, and the result of the comparison
// is always a valid mask.
unsafe { Self::from_int_unchecked(intrinsics::simd_gt(self.to_int(), other.to_int())) }
unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_gt(self.to_int(), other.to_int())) }
}
#[inline]
fn simd_ge(self, other: Self) -> Self::Mask {
// Safety: `self` is a vector, and the result of the comparison
// is always a valid mask.
unsafe { Self::from_int_unchecked(intrinsics::simd_ge(self.to_int(), other.to_int())) }
unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_ge(self.to_int(), other.to_int())) }
}
}

View file

@ -1,7 +1,7 @@
use super::sealed::Sealed;
use crate::simd::{
cmp::{SimdPartialEq, SimdPartialOrd},
intrinsics, LaneCount, Mask, Simd, SimdCast, SimdElement, SupportedLaneCount,
LaneCount, Mask, Simd, SimdCast, SimdElement, SupportedLaneCount,
};
/// Operations on SIMD vectors of floats.
@ -259,7 +259,7 @@ macro_rules! impl_trait {
fn cast<T: SimdCast>(self) -> Self::Cast<T>
{
// Safety: supported types are guaranteed by SimdCast
unsafe { intrinsics::simd_as(self) }
unsafe { core::intrinsics::simd::simd_as(self) }
}
#[inline]
@ -269,7 +269,7 @@ macro_rules! impl_trait {
Self::Scalar: core::convert::FloatToInt<I>,
{
// Safety: supported types are guaranteed by SimdCast, the caller is responsible for the extra invariants
unsafe { intrinsics::simd_cast(self) }
unsafe { core::intrinsics::simd::simd_cast(self) }
}
#[inline]
@ -289,7 +289,7 @@ macro_rules! impl_trait {
#[inline]
fn abs(self) -> Self {
// Safety: `self` is a float vector
unsafe { intrinsics::simd_fabs(self) }
unsafe { core::intrinsics::simd::simd_fabs(self) }
}
#[inline]
@ -363,13 +363,13 @@ macro_rules! impl_trait {
#[inline]
fn simd_min(self, other: Self) -> Self {
// Safety: `self` and `other` are float vectors
unsafe { intrinsics::simd_fmin(self, other) }
unsafe { core::intrinsics::simd::simd_fmin(self, other) }
}
#[inline]
fn simd_max(self, other: Self) -> Self {
// Safety: `self` and `other` are floating point vectors
unsafe { intrinsics::simd_fmax(self, other) }
unsafe { core::intrinsics::simd::simd_fmax(self, other) }
}
#[inline]
@ -391,7 +391,7 @@ macro_rules! impl_trait {
self.as_array().iter().sum()
} else {
// Safety: `self` is a float vector
unsafe { intrinsics::simd_reduce_add_ordered(self, 0.) }
unsafe { core::intrinsics::simd::simd_reduce_add_ordered(self, 0.) }
}
}
@ -402,20 +402,20 @@ macro_rules! impl_trait {
self.as_array().iter().product()
} else {
// Safety: `self` is a float vector
unsafe { intrinsics::simd_reduce_mul_ordered(self, 1.) }
unsafe { core::intrinsics::simd::simd_reduce_mul_ordered(self, 1.) }
}
}
#[inline]
fn reduce_max(self) -> Self::Scalar {
// Safety: `self` is a float vector
unsafe { intrinsics::simd_reduce_max(self) }
unsafe { core::intrinsics::simd::simd_reduce_max(self) }
}
#[inline]
fn reduce_min(self) -> Self::Scalar {
// Safety: `self` is a float vector
unsafe { intrinsics::simd_reduce_min(self) }
unsafe { core::intrinsics::simd::simd_reduce_min(self) }
}
}
)*

View file

@ -1,6 +1,6 @@
use super::sealed::Sealed;
use crate::simd::{
cmp::SimdPartialOrd, intrinsics, num::SimdUint, LaneCount, Mask, Simd, SimdCast, SimdElement,
cmp::SimdPartialOrd, num::SimdUint, LaneCount, Mask, Simd, SimdCast, SimdElement,
SupportedLaneCount,
};
@ -237,19 +237,19 @@ macro_rules! impl_trait {
#[inline]
fn cast<T: SimdCast>(self) -> Self::Cast<T> {
// Safety: supported types are guaranteed by SimdCast
unsafe { intrinsics::simd_as(self) }
unsafe { core::intrinsics::simd::simd_as(self) }
}
#[inline]
fn saturating_add(self, second: Self) -> Self {
// Safety: `self` is a vector
unsafe { intrinsics::simd_saturating_add(self, second) }
unsafe { core::intrinsics::simd::simd_saturating_add(self, second) }
}
#[inline]
fn saturating_sub(self, second: Self) -> Self {
// Safety: `self` is a vector
unsafe { intrinsics::simd_saturating_sub(self, second) }
unsafe { core::intrinsics::simd::simd_saturating_sub(self, second) }
}
#[inline]
@ -293,55 +293,55 @@ macro_rules! impl_trait {
#[inline]
fn reduce_sum(self) -> Self::Scalar {
// Safety: `self` is an integer vector
unsafe { intrinsics::simd_reduce_add_ordered(self, 0) }
unsafe { core::intrinsics::simd::simd_reduce_add_ordered(self, 0) }
}
#[inline]
fn reduce_product(self) -> Self::Scalar {
// Safety: `self` is an integer vector
unsafe { intrinsics::simd_reduce_mul_ordered(self, 1) }
unsafe { core::intrinsics::simd::simd_reduce_mul_ordered(self, 1) }
}
#[inline]
fn reduce_max(self) -> Self::Scalar {
// Safety: `self` is an integer vector
unsafe { intrinsics::simd_reduce_max(self) }
unsafe { core::intrinsics::simd::simd_reduce_max(self) }
}
#[inline]
fn reduce_min(self) -> Self::Scalar {
// Safety: `self` is an integer vector
unsafe { intrinsics::simd_reduce_min(self) }
unsafe { core::intrinsics::simd::simd_reduce_min(self) }
}
#[inline]
fn reduce_and(self) -> Self::Scalar {
// Safety: `self` is an integer vector
unsafe { intrinsics::simd_reduce_and(self) }
unsafe { core::intrinsics::simd::simd_reduce_and(self) }
}
#[inline]
fn reduce_or(self) -> Self::Scalar {
// Safety: `self` is an integer vector
unsafe { intrinsics::simd_reduce_or(self) }
unsafe { core::intrinsics::simd::simd_reduce_or(self) }
}
#[inline]
fn reduce_xor(self) -> Self::Scalar {
// Safety: `self` is an integer vector
unsafe { intrinsics::simd_reduce_xor(self) }
unsafe { core::intrinsics::simd::simd_reduce_xor(self) }
}
#[inline]
fn swap_bytes(self) -> Self {
// Safety: `self` is an integer vector
unsafe { intrinsics::simd_bswap(self) }
unsafe { core::intrinsics::simd::simd_bswap(self) }
}
#[inline]
fn reverse_bits(self) -> Self {
// Safety: `self` is an integer vector
unsafe { intrinsics::simd_bitreverse(self) }
unsafe { core::intrinsics::simd::simd_bitreverse(self) }
}
#[inline]

View file

@ -1,5 +1,5 @@
use super::sealed::Sealed;
use crate::simd::{intrinsics, LaneCount, Simd, SimdCast, SimdElement, SupportedLaneCount};
use crate::simd::{LaneCount, Simd, SimdCast, SimdElement, SupportedLaneCount};
/// Operations on SIMD vectors of unsigned integers.
pub trait SimdUint: Copy + Sealed {
@ -117,7 +117,7 @@ macro_rules! impl_trait {
#[inline]
fn cast<T: SimdCast>(self) -> Self::Cast<T> {
// Safety: supported types are guaranteed by SimdCast
unsafe { intrinsics::simd_as(self) }
unsafe { core::intrinsics::simd::simd_as(self) }
}
#[inline]
@ -129,79 +129,79 @@ macro_rules! impl_trait {
#[inline]
fn saturating_add(self, second: Self) -> Self {
// Safety: `self` is a vector
unsafe { intrinsics::simd_saturating_add(self, second) }
unsafe { core::intrinsics::simd::simd_saturating_add(self, second) }
}
#[inline]
fn saturating_sub(self, second: Self) -> Self {
// Safety: `self` is a vector
unsafe { intrinsics::simd_saturating_sub(self, second) }
unsafe { core::intrinsics::simd::simd_saturating_sub(self, second) }
}
#[inline]
fn reduce_sum(self) -> Self::Scalar {
// Safety: `self` is an integer vector
unsafe { intrinsics::simd_reduce_add_ordered(self, 0) }
unsafe { core::intrinsics::simd::simd_reduce_add_ordered(self, 0) }
}
#[inline]
fn reduce_product(self) -> Self::Scalar {
// Safety: `self` is an integer vector
unsafe { intrinsics::simd_reduce_mul_ordered(self, 1) }
unsafe { core::intrinsics::simd::simd_reduce_mul_ordered(self, 1) }
}
#[inline]
fn reduce_max(self) -> Self::Scalar {
// Safety: `self` is an integer vector
unsafe { intrinsics::simd_reduce_max(self) }
unsafe { core::intrinsics::simd::simd_reduce_max(self) }
}
#[inline]
fn reduce_min(self) -> Self::Scalar {
// Safety: `self` is an integer vector
unsafe { intrinsics::simd_reduce_min(self) }
unsafe { core::intrinsics::simd::simd_reduce_min(self) }
}
#[inline]
fn reduce_and(self) -> Self::Scalar {
// Safety: `self` is an integer vector
unsafe { intrinsics::simd_reduce_and(self) }
unsafe { core::intrinsics::simd::simd_reduce_and(self) }
}
#[inline]
fn reduce_or(self) -> Self::Scalar {
// Safety: `self` is an integer vector
unsafe { intrinsics::simd_reduce_or(self) }
unsafe { core::intrinsics::simd::simd_reduce_or(self) }
}
#[inline]
fn reduce_xor(self) -> Self::Scalar {
// Safety: `self` is an integer vector
unsafe { intrinsics::simd_reduce_xor(self) }
unsafe { core::intrinsics::simd::simd_reduce_xor(self) }
}
#[inline]
fn swap_bytes(self) -> Self {
// Safety: `self` is an integer vector
unsafe { intrinsics::simd_bswap(self) }
unsafe { core::intrinsics::simd::simd_bswap(self) }
}
#[inline]
fn reverse_bits(self) -> Self {
// Safety: `self` is an integer vector
unsafe { intrinsics::simd_bitreverse(self) }
unsafe { core::intrinsics::simd::simd_bitreverse(self) }
}
#[inline]
fn leading_zeros(self) -> Self {
// Safety: `self` is an integer vector
unsafe { intrinsics::simd_ctlz(self) }
unsafe { core::intrinsics::simd::simd_ctlz(self) }
}
#[inline]
fn trailing_zeros(self) -> Self {
// Safety: `self` is an integer vector
unsafe { intrinsics::simd_cttz(self) }
unsafe { core::intrinsics::simd::simd_cttz(self) }
}
#[inline]

View file

@ -1,7 +1,5 @@
use super::sealed::Sealed;
use crate::simd::{
cmp::SimdPartialEq, intrinsics, num::SimdUint, LaneCount, Mask, Simd, SupportedLaneCount,
};
use crate::simd::{cmp::SimdPartialEq, num::SimdUint, LaneCount, Mask, Simd, SupportedLaneCount};
/// Operations on SIMD vectors of constant pointers.
pub trait SimdConstPtr: Copy + Sealed {
@ -103,13 +101,13 @@ where
assert_eq!(size_of::<<U as Pointee>::Metadata>(), 0);
// Safety: pointers can be cast
unsafe { intrinsics::simd_cast_ptr(self) }
unsafe { core::intrinsics::simd::simd_cast_ptr(self) }
}
#[inline]
fn cast_mut(self) -> Self::MutPtr {
// Safety: pointers can be cast
unsafe { intrinsics::simd_cast_ptr(self) }
unsafe { core::intrinsics::simd::simd_cast_ptr(self) }
}
#[inline]
@ -135,19 +133,19 @@ where
#[inline]
fn expose_addr(self) -> Self::Usize {
// Safety: `self` is a pointer vector
unsafe { intrinsics::simd_expose_addr(self) }
unsafe { core::intrinsics::simd::simd_expose_addr(self) }
}
#[inline]
fn from_exposed_addr(addr: Self::Usize) -> Self {
// Safety: `self` is a pointer vector
unsafe { intrinsics::simd_from_exposed_addr(addr) }
unsafe { core::intrinsics::simd::simd_from_exposed_addr(addr) }
}
#[inline]
fn wrapping_offset(self, count: Self::Isize) -> Self {
// Safety: simd_arith_offset takes a vector of pointers and a vector of offsets
unsafe { intrinsics::simd_arith_offset(self, count) }
unsafe { core::intrinsics::simd::simd_arith_offset(self, count) }
}
#[inline]

View file

@ -1,7 +1,5 @@
use super::sealed::Sealed;
use crate::simd::{
cmp::SimdPartialEq, intrinsics, num::SimdUint, LaneCount, Mask, Simd, SupportedLaneCount,
};
use crate::simd::{cmp::SimdPartialEq, num::SimdUint, LaneCount, Mask, Simd, SupportedLaneCount};
/// Operations on SIMD vectors of mutable pointers.
pub trait SimdMutPtr: Copy + Sealed {
@ -100,13 +98,13 @@ where
assert_eq!(size_of::<<U as Pointee>::Metadata>(), 0);
// Safety: pointers can be cast
unsafe { intrinsics::simd_cast_ptr(self) }
unsafe { core::intrinsics::simd::simd_cast_ptr(self) }
}
#[inline]
fn cast_const(self) -> Self::ConstPtr {
// Safety: pointers can be cast
unsafe { intrinsics::simd_cast_ptr(self) }
unsafe { core::intrinsics::simd::simd_cast_ptr(self) }
}
#[inline]
@ -132,19 +130,19 @@ where
#[inline]
fn expose_addr(self) -> Self::Usize {
// Safety: `self` is a pointer vector
unsafe { intrinsics::simd_expose_addr(self) }
unsafe { core::intrinsics::simd::simd_expose_addr(self) }
}
#[inline]
fn from_exposed_addr(addr: Self::Usize) -> Self {
// Safety: `self` is a pointer vector
unsafe { intrinsics::simd_from_exposed_addr(addr) }
unsafe { core::intrinsics::simd::simd_from_exposed_addr(addr) }
}
#[inline]
fn wrapping_offset(self, count: Self::Isize) -> Self {
// Safety: simd_arith_offset takes a vector of pointers and a vector of offsets
unsafe { intrinsics::simd_arith_offset(self, count) }
unsafe { core::intrinsics::simd::simd_arith_offset(self, count) }
}
#[inline]

View file

@ -1,4 +1,3 @@
use crate::simd::intrinsics;
use crate::simd::{LaneCount, Mask, MaskElement, Simd, SimdElement, SupportedLaneCount};
/// Constructs a new SIMD vector by copying elements from selected elements in other vectors.
@ -88,7 +87,7 @@ pub trait Swizzle<const N: usize> {
{
// Safety: `vector` is a vector, and the index is a const array of u32.
unsafe {
intrinsics::simd_shuffle(
core::intrinsics::simd::simd_shuffle(
vector,
vector,
const {
@ -124,7 +123,7 @@ pub trait Swizzle<const N: usize> {
{
// Safety: `first` and `second` are vectors, and the index is a const array of u32.
unsafe {
intrinsics::simd_shuffle(
core::intrinsics::simd::simd_shuffle(
first,
second,
const {

View file

@ -44,7 +44,7 @@ where
))]
8 => transize(vtbl1_u8, self, idxs),
#[cfg(target_feature = "ssse3")]
16 => transize(x86::_mm_shuffle_epi8, self, idxs),
16 => transize(x86::_mm_shuffle_epi8, self, zeroing_idxs(idxs)),
#[cfg(target_feature = "simd128")]
16 => transize(wasm::i8x16_swizzle, self, idxs),
#[cfg(all(
@ -54,9 +54,9 @@ where
))]
16 => transize(vqtbl1q_u8, self, idxs),
#[cfg(all(target_feature = "avx2", not(target_feature = "avx512vbmi")))]
32 => transize_raw(avx2_pshufb, self, idxs),
32 => transize(avx2_pshufb, self, idxs),
#[cfg(all(target_feature = "avx512vl", target_feature = "avx512vbmi"))]
32 => transize(x86::_mm256_permutexvar_epi8, self, idxs),
32 => transize(x86::_mm256_permutexvar_epi8, zeroing_idxs(idxs), self),
// Notable absence: avx512bw shuffle
// If avx512bw is available, odds of avx512vbmi are good
// FIXME: initial AVX512VBMI variant didn't actually pass muster
@ -129,45 +129,25 @@ unsafe fn avx2_pshufb(bytes: Simd<u8, 32>, idxs: Simd<u8, 32>) -> Simd<u8, 32> {
#[inline(always)]
unsafe fn transize<T, const N: usize>(
f: unsafe fn(T, T) -> T,
bytes: Simd<u8, N>,
idxs: Simd<u8, N>,
a: Simd<u8, N>,
b: Simd<u8, N>,
) -> Simd<u8, N>
where
LaneCount<N>: SupportedLaneCount,
{
let idxs = zeroing_idxs(idxs);
// SAFETY: Same obligation to use this function as to use mem::transmute_copy.
unsafe { mem::transmute_copy(&f(mem::transmute_copy(&bytes), mem::transmute_copy(&idxs))) }
unsafe { mem::transmute_copy(&f(mem::transmute_copy(&a), mem::transmute_copy(&b))) }
}
/// Make indices that yield 0 for this architecture
/// Make indices that yield 0 for x86
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[allow(unused)]
#[inline(always)]
fn zeroing_idxs<const N: usize>(idxs: Simd<u8, N>) -> Simd<u8, N>
where
LaneCount<N>: SupportedLaneCount,
{
// On x86, make sure the top bit is set.
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
let idxs = {
use crate::simd::cmp::SimdPartialOrd;
idxs.simd_lt(Simd::splat(N as u8))
.select(idxs, Simd::splat(u8::MAX))
};
// Simply do nothing on most architectures.
idxs
}
/// As transize but no implicit call to `zeroing_idxs`.
#[allow(dead_code)]
#[inline(always)]
unsafe fn transize_raw<T, const N: usize>(
f: unsafe fn(T, T) -> T,
bytes: Simd<u8, N>,
idxs: Simd<u8, N>,
) -> Simd<u8, N>
where
LaneCount<N>: SupportedLaneCount,
{
// SAFETY: Same obligation to use this function as to use mem::transmute_copy.
unsafe { mem::transmute_copy(&f(mem::transmute_copy(&bytes), mem::transmute_copy(&idxs))) }
use crate::simd::cmp::SimdPartialOrd;
idxs.simd_lt(Simd::splat(N as u8))
.select(idxs, Simd::splat(u8::MAX))
}

View file

@ -1,6 +1,5 @@
use crate::simd::{
cmp::SimdPartialOrd,
intrinsics,
ptr::{SimdConstPtr, SimdMutPtr},
LaneCount, Mask, MaskElement, SupportedLaneCount, Swizzle,
};
@ -194,7 +193,7 @@ where
/// With padding, `read_unaligned` will read past the end of an array of N elements.
///
/// # Safety
/// Reading `ptr` must be safe, as if by `<*const [T; N]>::read_unaligned`.
/// Reading `ptr` must be safe, as if by `<*const [T; N]>::read`.
#[inline]
const unsafe fn load(ptr: *const [T; N]) -> Self {
// There are potentially simpler ways to write this function, but this should result in
@ -215,7 +214,7 @@ where
/// See `load` as to why this function is necessary.
///
/// # Safety
/// Writing to `ptr` must be safe, as if by `<*mut [T; N]>::write_unaligned`.
/// Writing to `ptr` must be safe, as if by `<*mut [T; N]>::write`.
#[inline]
const unsafe fn store(self, ptr: *mut [T; N]) {
// There are potentially simpler ways to write this function, but this should result in
@ -491,7 +490,7 @@ where
or: Self,
) -> Self {
// Safety: The caller is responsible for upholding all invariants
unsafe { intrinsics::simd_gather(or, source, enable.to_int()) }
unsafe { core::intrinsics::simd::simd_gather(or, source, enable.to_int()) }
}
/// Writes the values in a SIMD vector to potentially discontiguous indices in `slice`.
@ -650,7 +649,7 @@ where
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
pub unsafe fn scatter_select_ptr(self, dest: Simd<*mut T, N>, enable: Mask<isize, N>) {
// Safety: The caller is responsible for upholding all invariants
unsafe { intrinsics::simd_scatter(self, dest, enable.to_int()) }
unsafe { core::intrinsics::simd::simd_scatter(self, dest, enable.to_int()) }
}
}
@ -692,7 +691,8 @@ where
fn eq(&self, other: &Self) -> bool {
// Safety: All SIMD vectors are SimdPartialEq, and the comparison produces a valid mask.
let mask = unsafe {
let tfvec: Simd<<T as SimdElement>::Mask, N> = intrinsics::simd_eq(*self, *other);
let tfvec: Simd<<T as SimdElement>::Mask, N> =
core::intrinsics::simd::simd_eq(*self, *other);
Mask::from_int_unchecked(tfvec)
};
@ -705,7 +705,8 @@ where
fn ne(&self, other: &Self) -> bool {
// Safety: All SIMD vectors are SimdPartialEq, and the comparison produces a valid mask.
let mask = unsafe {
let tfvec: Simd<<T as SimdElement>::Mask, N> = intrinsics::simd_ne(*self, *other);
let tfvec: Simd<<T as SimdElement>::Mask, N> =
core::intrinsics::simd::simd_ne(*self, *other);
Mask::from_int_unchecked(tfvec)
};

View file

@ -99,6 +99,19 @@ macro_rules! test_mask_api {
assert_eq!(Mask::<$type, 2>::from_bitmask(bitmask), mask);
}
#[cfg(feature = "all_lane_counts")]
#[test]
fn roundtrip_bitmask_conversion_odd() {
let values = [
true, false, true, false, true, true, false, false, false, true, true,
];
let mask = Mask::<$type, 11>::from_array(values);
let bitmask = mask.to_bitmask();
assert_eq!(bitmask, 0b11000110101);
assert_eq!(Mask::<$type, 11>::from_bitmask(bitmask), mask);
}
#[test]
fn cast() {
fn cast_impl<T: core_simd::simd::MaskElement>()
@ -134,6 +147,35 @@ macro_rules! test_mask_api {
assert_eq!(bitmask.resize::<2>(0).to_ne_bytes()[..2], [0b01001001, 0b10000011]);
assert_eq!(Mask::<$type, 16>::from_bitmask_vector(bitmask), mask);
}
// rust-lang/portable-simd#379
#[test]
fn roundtrip_bitmask_vector_conversion_small() {
use core_simd::simd::ToBytes;
let values = [
true, false, true, true
];
let mask = Mask::<$type, 4>::from_array(values);
let bitmask = mask.to_bitmask_vector();
assert_eq!(bitmask.resize::<1>(0).to_ne_bytes()[0], 0b00001101);
assert_eq!(Mask::<$type, 4>::from_bitmask_vector(bitmask), mask);
}
/* FIXME doesn't work with non-powers-of-two, yet
// rust-lang/portable-simd#379
#[cfg(feature = "all_lane_counts")]
#[test]
fn roundtrip_bitmask_vector_conversion_odd() {
use core_simd::simd::ToBytes;
let values = [
true, false, true, false, true, true, false, false, false, true, true,
];
let mask = Mask::<$type, 11>::from_array(values);
let bitmask = mask.to_bitmask_vector();
assert_eq!(bitmask.resize::<2>(0).to_ne_bytes()[..2], [0b00110101, 0b00000110]);
assert_eq!(Mask::<$type, 11>::from_bitmask_vector(bitmask), mask);
}
*/
}
}
}

View file

@ -1,7 +1,7 @@
#![cfg_attr(feature = "as_crate", no_std)] // We are std!
#![cfg_attr(
feature = "as_crate",
feature(platform_intrinsics),
feature(core_intrinsics),
feature(portable_simd),
allow(internal_features)
)]
@ -10,6 +10,8 @@ use core::simd;
#[cfg(feature = "as_crate")]
use core_simd::simd;
use core::intrinsics::simd as intrinsics;
use simd::{LaneCount, Simd, SupportedLaneCount};
#[cfg(feature = "as_crate")]
@ -22,28 +24,6 @@ use experimental as sealed;
use crate::sealed::Sealed;
// "platform intrinsics" are essentially "codegen intrinsics"
// each of these may be scalarized and lowered to a libm call
extern "platform-intrinsic" {
// ceil
fn simd_ceil<T>(x: T) -> T;
// floor
fn simd_floor<T>(x: T) -> T;
// round
fn simd_round<T>(x: T) -> T;
// trunc
fn simd_trunc<T>(x: T) -> T;
// fsqrt
fn simd_fsqrt<T>(x: T) -> T;
// fma
fn simd_fma<T>(x: T, y: T, z: T) -> T;
}
/// This trait provides a possibly-temporary implementation of float functions
/// that may, in the absence of hardware support, canonicalize to calling an
/// operating system's `math.h` dynamically-loaded library (also known as a
@ -74,7 +54,7 @@ pub trait StdFloat: Sealed + Sized {
#[inline]
#[must_use = "method returns a new vector and does not mutate the original value"]
fn mul_add(self, a: Self, b: Self) -> Self {
unsafe { simd_fma(self, a, b) }
unsafe { intrinsics::simd_fma(self, a, b) }
}
/// Produces a vector where every lane has the square root value
@ -82,35 +62,35 @@ pub trait StdFloat: Sealed + Sized {
#[inline]
#[must_use = "method returns a new vector and does not mutate the original value"]
fn sqrt(self) -> Self {
unsafe { simd_fsqrt(self) }
unsafe { intrinsics::simd_fsqrt(self) }
}
/// Returns the smallest integer greater than or equal to each lane.
#[must_use = "method returns a new vector and does not mutate the original value"]
#[inline]
fn ceil(self) -> Self {
unsafe { simd_ceil(self) }
unsafe { intrinsics::simd_ceil(self) }
}
/// Returns the largest integer value less than or equal to each lane.
#[must_use = "method returns a new vector and does not mutate the original value"]
#[inline]
fn floor(self) -> Self {
unsafe { simd_floor(self) }
unsafe { intrinsics::simd_floor(self) }
}
/// Rounds to the nearest integer value. Ties round toward zero.
#[must_use = "method returns a new vector and does not mutate the original value"]
#[inline]
fn round(self) -> Self {
unsafe { simd_round(self) }
unsafe { intrinsics::simd_round(self) }
}
/// Returns the floating point's integer value, with its fractional part removed.
#[must_use = "method returns a new vector and does not mutate the original value"]
#[inline]
fn trunc(self) -> Self {
unsafe { simd_trunc(self) }
unsafe { intrinsics::simd_trunc(self) }
}
/// Returns the floating point's fractional value, with its integer part removed.

View file

@ -1,4 +1,8 @@
#![feature(stdsimd, powerpc_target_feature)]
#![feature(powerpc_target_feature)]
#![cfg_attr(
any(target_arch = "powerpc", target_arch = "powerpc64"),
feature(stdarch_powerpc)
)]
pub mod array;

View file

@ -78,7 +78,7 @@ pub fn current_dir() -> io::Result<PathBuf> {
/// assert!(env::set_current_dir(&root).is_ok());
/// println!("Successfully changed working directory to {}!", root.display());
/// ```
#[doc(alias = "chdir")]
#[doc(alias = "chdir", alias = "SetCurrentDirectory", alias = "SetCurrentDirectoryW")]
#[stable(feature = "env", since = "1.0.0")]
pub fn set_current_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
os_imp::chdir(path.as_ref())
@ -655,6 +655,7 @@ pub fn home_dir() -> Option<PathBuf> {
/// }
/// ```
#[must_use]
#[doc(alias = "GetTempPath", alias = "GetTempPath2")]
#[stable(feature = "env", since = "1.0.0")]
pub fn temp_dir() -> PathBuf {
os_imp::temp_dir()

View file

@ -127,6 +127,11 @@
//! trait, which provides a [`from_wide`] method to convert a native Windows
//! string (without the terminating nul character) to an [`OsString`].
//!
//! ## Other platforms
//!
//! Many other platforms provide their own extension traits in a
//! `std::os::*::ffi` module.
//!
//! ## On all platforms
//!
//! On all platforms, [`OsStr`] consists of a sequence of bytes that is encoded as a superset of
@ -135,6 +140,8 @@
//! For limited, inexpensive conversions from and to bytes, see [`OsStr::as_encoded_bytes`] and
//! [`OsStr::from_encoded_bytes_unchecked`].
//!
//! For basic string processing, see [`OsStr::slice_encoded_bytes`].
//!
//! [Unicode scalar value]: https://www.unicode.org/glossary/#unicode_scalar_value
//! [Unicode code point]: https://www.unicode.org/glossary/#code_point
//! [`env::set_var()`]: crate::env::set_var "env::set_var"

View file

@ -9,7 +9,7 @@ use crate::hash::{Hash, Hasher};
use crate::ops::{self, Range};
use crate::rc::Rc;
use crate::slice;
use crate::str::{from_utf8 as str_from_utf8, FromStr};
use crate::str::FromStr;
use crate::sync::Arc;
use crate::sys::os_str::{Buf, Slice};
@ -997,42 +997,15 @@ impl OsStr {
/// ```
#[unstable(feature = "os_str_slice", issue = "118485")]
pub fn slice_encoded_bytes<R: ops::RangeBounds<usize>>(&self, range: R) -> &Self {
#[track_caller]
fn check_valid_boundary(bytes: &[u8], index: usize) {
if index == 0 || index == bytes.len() {
return;
}
// Fast path
if bytes[index - 1].is_ascii() || bytes[index].is_ascii() {
return;
}
let (before, after) = bytes.split_at(index);
// UTF-8 takes at most 4 bytes per codepoint, so we don't
// need to check more than that.
let after = after.get(..4).unwrap_or(after);
match str_from_utf8(after) {
Ok(_) => return,
Err(err) if err.valid_up_to() != 0 => return,
Err(_) => (),
}
for len in 2..=4.min(index) {
let before = &before[index - len..];
if str_from_utf8(before).is_ok() {
return;
}
}
panic!("byte index {index} is not an OsStr boundary");
}
let encoded_bytes = self.as_encoded_bytes();
let Range { start, end } = slice::range(range, ..encoded_bytes.len());
check_valid_boundary(encoded_bytes, start);
check_valid_boundary(encoded_bytes, end);
// `check_public_boundary` should panic if the index does not lie on an
// `OsStr` boundary as described above. It's possible to do this in an
// encoding-agnostic way, but details of the internal encoding might
// permit a more efficient implementation.
self.inner.check_public_boundary(start);
self.inner.check_public_boundary(end);
// SAFETY: `slice::range` ensures that `start` and `end` are valid
let slice = unsafe { encoded_bytes.get_unchecked(start..end) };

View file

@ -194,15 +194,65 @@ fn slice_encoded_bytes() {
}
#[test]
#[should_panic(expected = "byte index 2 is not an OsStr boundary")]
#[should_panic]
fn slice_out_of_bounds() {
let crab = OsStr::new("🦀");
let _ = crab.slice_encoded_bytes(..5);
}
#[test]
#[should_panic]
fn slice_mid_char() {
let crab = OsStr::new("🦀");
let _ = crab.slice_encoded_bytes(..2);
}
#[cfg(unix)]
#[test]
#[should_panic(expected = "byte index 1 is not an OsStr boundary")]
fn slice_invalid_data() {
use crate::os::unix::ffi::OsStrExt;
let os_string = OsStr::from_bytes(b"\xFF\xFF");
let _ = os_string.slice_encoded_bytes(1..);
}
#[cfg(unix)]
#[test]
#[should_panic(expected = "byte index 1 is not an OsStr boundary")]
fn slice_partial_utf8() {
use crate::os::unix::ffi::{OsStrExt, OsStringExt};
let part_crab = OsStr::from_bytes(&"🦀".as_bytes()[..3]);
let mut os_string = OsString::from_vec(vec![0xFF]);
os_string.push(part_crab);
let _ = os_string.slice_encoded_bytes(1..);
}
#[cfg(unix)]
#[test]
fn slice_invalid_edge() {
use crate::os::unix::ffi::{OsStrExt, OsStringExt};
let os_string = OsStr::from_bytes(b"a\xFFa");
assert_eq!(os_string.slice_encoded_bytes(..1), "a");
assert_eq!(os_string.slice_encoded_bytes(1..), OsStr::from_bytes(b"\xFFa"));
assert_eq!(os_string.slice_encoded_bytes(..2), OsStr::from_bytes(b"a\xFF"));
assert_eq!(os_string.slice_encoded_bytes(2..), "a");
let os_string = OsStr::from_bytes(&"abc🦀".as_bytes()[..6]);
assert_eq!(os_string.slice_encoded_bytes(..3), "abc");
assert_eq!(os_string.slice_encoded_bytes(3..), OsStr::from_bytes(b"\xF0\x9F\xA6"));
let mut os_string = OsString::from_vec(vec![0xFF]);
os_string.push("🦀");
assert_eq!(os_string.slice_encoded_bytes(..1), OsStr::from_bytes(b"\xFF"));
assert_eq!(os_string.slice_encoded_bytes(1..), "🦀");
}
#[cfg(windows)]
#[test]
#[should_panic(expected = "byte index 3 is not an OsStr boundary")]
#[should_panic(expected = "byte index 3 lies between surrogate codepoints")]
fn slice_between_surrogates() {
use crate::os::windows::ffi::OsStringExt;
@ -216,10 +266,14 @@ fn slice_between_surrogates() {
fn slice_surrogate_edge() {
use crate::os::windows::ffi::OsStringExt;
let os_string = OsString::from_wide(&[0xD800]);
let mut with_crab = os_string.clone();
with_crab.push("🦀");
let surrogate = OsString::from_wide(&[0xD800]);
let mut pre_crab = surrogate.clone();
pre_crab.push("🦀");
assert_eq!(pre_crab.slice_encoded_bytes(..3), surrogate);
assert_eq!(pre_crab.slice_encoded_bytes(3..), "🦀");
assert_eq!(with_crab.slice_encoded_bytes(..3), os_string);
assert_eq!(with_crab.slice_encoded_bytes(3..), "🦀");
let mut post_crab = OsString::from("🦀");
post_crab.push(&surrogate);
assert_eq!(post_crab.slice_encoded_bytes(..4), "🦀");
assert_eq!(post_crab.slice_encoded_bytes(4..), surrogate);
}

View file

@ -656,6 +656,7 @@ impl File {
///
/// Note that this method alters the permissions of the underlying file,
/// even though it takes `&self` rather than `&mut self`.
#[doc(alias = "fchmod", alias = "SetFileInformationByHandle")]
#[stable(feature = "set_permissions_atomic", since = "1.16.0")]
pub fn set_permissions(&self, perm: Permissions) -> io::Result<()> {
self.inner.set_permissions(perm.0)
@ -1314,6 +1315,7 @@ impl Metadata {
/// Ok(())
/// }
/// ```
#[doc(alias = "mtime", alias = "ftLastWriteTime")]
#[stable(feature = "fs_time", since = "1.10.0")]
pub fn modified(&self) -> io::Result<SystemTime> {
self.0.modified().map(FromInner::from_inner)
@ -1349,6 +1351,7 @@ impl Metadata {
/// Ok(())
/// }
/// ```
#[doc(alias = "atime", alias = "ftLastAccessTime")]
#[stable(feature = "fs_time", since = "1.10.0")]
pub fn accessed(&self) -> io::Result<SystemTime> {
self.0.accessed().map(FromInner::from_inner)
@ -1381,6 +1384,7 @@ impl Metadata {
/// Ok(())
/// }
/// ```
#[doc(alias = "btime", alias = "birthtime", alias = "ftCreationTime")]
#[stable(feature = "fs_time", since = "1.10.0")]
pub fn created(&self) -> io::Result<SystemTime> {
self.0.created().map(FromInner::from_inner)
@ -1879,6 +1883,7 @@ impl AsInner<fs_imp::DirEntry> for DirEntry {
/// Ok(())
/// }
/// ```
#[doc(alias = "rm", alias = "unlink", alias = "DeleteFile")]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn remove_file<P: AsRef<Path>>(path: P) -> io::Result<()> {
fs_imp::unlink(path.as_ref())
@ -1917,6 +1922,7 @@ pub fn remove_file<P: AsRef<Path>>(path: P) -> io::Result<()> {
/// Ok(())
/// }
/// ```
#[doc(alias = "stat")]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
fs_imp::stat(path.as_ref()).map(Metadata)
@ -1951,6 +1957,7 @@ pub fn metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
/// Ok(())
/// }
/// ```
#[doc(alias = "lstat")]
#[stable(feature = "symlink_metadata", since = "1.1.0")]
pub fn symlink_metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
fs_imp::lstat(path.as_ref()).map(Metadata)
@ -1994,6 +2001,7 @@ pub fn symlink_metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
/// Ok(())
/// }
/// ```
#[doc(alias = "mv", alias = "MoveFile", alias = "MoveFileEx")]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> {
fs_imp::rename(from.as_ref(), to.as_ref())
@ -2052,6 +2060,9 @@ pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()>
/// Ok(())
/// }
/// ```
#[doc(alias = "cp")]
#[doc(alias = "CopyFile", alias = "CopyFileEx")]
#[doc(alias = "fclonefileat", alias = "fcopyfile")]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<u64> {
fs_imp::copy(from.as_ref(), to.as_ref())
@ -2096,6 +2107,7 @@ pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<u64> {
/// Ok(())
/// }
/// ```
#[doc(alias = "CreateHardLink", alias = "linkat")]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn hard_link<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) -> io::Result<()> {
fs_imp::link(original.as_ref(), link.as_ref())
@ -2245,7 +2257,7 @@ pub fn canonicalize<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
/// Ok(())
/// }
/// ```
#[doc(alias = "mkdir")]
#[doc(alias = "mkdir", alias = "CreateDirectory")]
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "fs_create_dir")]
pub fn create_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
@ -2326,7 +2338,7 @@ pub fn create_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
/// Ok(())
/// }
/// ```
#[doc(alias = "rmdir")]
#[doc(alias = "rmdir", alias = "RemoveDirectory")]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
fs_imp::rmdir(path.as_ref())
@ -2449,6 +2461,7 @@ pub fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
/// Ok(())
/// }
/// ```
#[doc(alias = "ls", alias = "opendir", alias = "FindFirstFile", alias = "FindNextFile")]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn read_dir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> {
fs_imp::readdir(path.as_ref()).map(ReadDir)
@ -2484,6 +2497,7 @@ pub fn read_dir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> {
/// Ok(())
/// }
/// ```
#[doc(alias = "chmod", alias = "SetFileAttributes")]
#[stable(feature = "set_permissions", since = "1.1.0")]
pub fn set_permissions<P: AsRef<Path>>(path: P, perm: Permissions) -> io::Result<()> {
fs_imp::set_perm(path.as_ref(), perm.0)

View file

@ -397,7 +397,6 @@ pub trait OpenOptionsExt {
///
/// ```no_run
/// # #![feature(rustc_private)]
/// use libc;
/// use std::fs::OpenOptions;
/// use std::os::unix::fs::OpenOptionsExt;
///

View file

@ -173,51 +173,61 @@ pub trait FileExt {
///
/// This corresponds to the `fd_tell` syscall and is similar to
/// `seek` where you offset 0 bytes from the current position.
#[doc(alias = "fd_tell")]
fn tell(&self) -> io::Result<u64>;
/// Adjust the flags associated with this file.
///
/// This corresponds to the `fd_fdstat_set_flags` syscall.
#[doc(alias = "fd_fdstat_set_flags")]
fn fdstat_set_flags(&self, flags: u16) -> io::Result<()>;
/// Adjust the rights associated with this file.
///
/// This corresponds to the `fd_fdstat_set_rights` syscall.
#[doc(alias = "fd_fdstat_set_rights")]
fn fdstat_set_rights(&self, rights: u64, inheriting: u64) -> io::Result<()>;
/// Provide file advisory information on a file descriptor.
///
/// This corresponds to the `fd_advise` syscall.
#[doc(alias = "fd_advise")]
fn advise(&self, offset: u64, len: u64, advice: u8) -> io::Result<()>;
/// Force the allocation of space in a file.
///
/// This corresponds to the `fd_allocate` syscall.
#[doc(alias = "fd_allocate")]
fn allocate(&self, offset: u64, len: u64) -> io::Result<()>;
/// Create a directory.
///
/// This corresponds to the `path_create_directory` syscall.
#[doc(alias = "path_create_directory")]
fn create_directory<P: AsRef<Path>>(&self, dir: P) -> io::Result<()>;
/// Read the contents of a symbolic link.
///
/// This corresponds to the `path_readlink` syscall.
#[doc(alias = "path_readlink")]
fn read_link<P: AsRef<Path>>(&self, path: P) -> io::Result<PathBuf>;
/// Return the attributes of a file or directory.
///
/// This corresponds to the `path_filestat_get` syscall.
#[doc(alias = "path_filestat_get")]
fn metadata_at<P: AsRef<Path>>(&self, lookup_flags: u32, path: P) -> io::Result<Metadata>;
/// Unlink a file.
///
/// This corresponds to the `path_unlink_file` syscall.
#[doc(alias = "path_unlink_file")]
fn remove_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()>;
/// Remove a directory.
///
/// This corresponds to the `path_remove_directory` syscall.
#[doc(alias = "path_remove_directory")]
fn remove_directory<P: AsRef<Path>>(&self, path: P) -> io::Result<()>;
}
@ -359,6 +369,7 @@ pub trait OpenOptionsExt {
/// Open a file or directory.
///
/// This corresponds to the `path_open` syscall.
#[doc(alias = "path_open")]
fn open_at<P: AsRef<Path>>(&self, file: &File, path: P) -> io::Result<File>;
}
@ -500,6 +511,7 @@ impl DirEntryExt for fs::DirEntry {
/// Create a hard link.
///
/// This corresponds to the `path_link` syscall.
#[doc(alias = "path_link")]
pub fn link<P: AsRef<Path>, U: AsRef<Path>>(
old_fd: &File,
old_flags: u32,
@ -518,6 +530,7 @@ pub fn link<P: AsRef<Path>, U: AsRef<Path>>(
/// Rename a file or directory.
///
/// This corresponds to the `path_rename` syscall.
#[doc(alias = "path_rename")]
pub fn rename<P: AsRef<Path>, U: AsRef<Path>>(
old_fd: &File,
old_path: P,
@ -534,6 +547,7 @@ pub fn rename<P: AsRef<Path>, U: AsRef<Path>>(
/// Create a symbolic link.
///
/// This corresponds to the `path_symlink` syscall.
#[doc(alias = "path_symlink")]
pub fn symlink<P: AsRef<Path>, U: AsRef<Path>>(
old_path: P,
fd: &File,

View file

@ -337,8 +337,9 @@ pub mod panic_count {
#[doc(hidden)]
#[cfg(not(feature = "panic_immediate_abort"))]
#[unstable(feature = "update_panic_count", issue = "none")]
// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint
#[allow(static_mut_ref)]
// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_refs` lint
#[cfg_attr(bootstrap, allow(static_mut_ref))]
#[cfg_attr(not(bootstrap), allow(static_mut_refs))]
pub mod panic_count {
use crate::cell::Cell;
use crate::sync::atomic::{AtomicUsize, Ordering};

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