diff options
1090 files changed, 19192 insertions, 9258 deletions
diff --git a/.editorconfig b/.editorconfig index 1b137cf4ebe..6c8560aa1f5 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,10 +8,13 @@ root = true end_of_line = lf charset = utf-8 insert_final_newline = true +trim_trailing_whitespace = true +indent_style = space +indent_size = 4 # some tests need trailing whitespace in output snapshots -[!tests/] -trim_trailing_whitespace = true +[tests/**] +trim_trailing_whitespace = false # for actual source code files of test, we still don't want trailing whitespace [tests/**.{rs,js}] trim_trailing_whitespace = true @@ -19,9 +22,9 @@ trim_trailing_whitespace = true [tests/ui/{frontmatter/frontmatter-whitespace-3.rs,parser/shebang/shebang-space.rs}] trim_trailing_whitespace = false -[!src/llvm-project] -indent_style = space -indent_size = 4 +[src/llvm-project] +indent_style = unset +indent_size = unset [*.rs] max_line_length = 100 diff --git a/Cargo.lock b/Cargo.lock index a8df53f3a41..81f19668c24 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -336,7 +336,7 @@ dependencies = [ "curl", "indexmap", "serde", - "toml 0.7.8", + "toml 0.8.23", ] [[package]] @@ -3204,9 +3204,9 @@ dependencies = [ [[package]] name = "rustc-build-sysroot" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb13874a0e55baf4ac3d49d38206aecb31a55b75d6c4d04fd850b53942c8cc8" +checksum = "3b881c015c729b43105bbd3702a9bdecee28fafaa21126d1d62e454ec011a4b7" dependencies = [ "anyhow", "rustc_version", @@ -4022,6 +4022,7 @@ dependencies = [ name = "rustc_lint" version = "0.0.0" dependencies = [ + "bitflags", "rustc_abi", "rustc_ast", "rustc_ast_pretty", @@ -4072,7 +4073,6 @@ name = "rustc_log" version = "0.0.0" dependencies = [ "tracing", - "tracing-core", "tracing-subscriber", "tracing-tree", ] @@ -5152,9 +5152,9 @@ dependencies = [ [[package]] name = "stringdex" -version = "0.0.1-alpha4" +version = "0.0.1-alpha9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2841fd43df5b1ff1b042e167068a1fe9b163dc93041eae56ab2296859013a9a0" +checksum = "7081029913fd7d591c0112182aba8c98ae886b4f12edb208130496cd17dc3c15" dependencies = [ "stacker", ] @@ -5541,9 +5541,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", "valuable", diff --git a/Cargo.toml b/Cargo.toml index 2c5044f6a35..67c7a9d67ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,33 +59,6 @@ exclude = [ "obj", ] -[workspace.dependencies] -# tidy-alphabetical-start -bitflags = "2.9.3" -derive-where = "1.6.0" -either = "1.15.0" -indexmap = "2.10.0" -itertools = "0.12.1" -# FIXME: Remove this pin once this rustix issue is resolved -# https://github.com/bytecodealliance/rustix/issues/1496 -libc = "=0.2.174" -measureme = "12.0.3" -memchr = "2.7.5" -odht = { version = "0.3.1", features = ["nightly"] } -polonius-engine = "0.13.0" -proc-macro2 = "1.0.101" -quote = "1.0.40" -rustc-demangle = "0.1.26" -rustc-hash = "2.1.1" -rustc-literal-escaper = "0.0.5" -rustc_apfloat = "0.2.3" -scoped-tls = "1.0.1" -serde_json = "1.0.142" -tempfile = "3.20.0" -thin-vec = "0.2.14" -tracing = "0.1.37" -# tidy-alphabetical-end - [profile.release.package.rustc_thread_pool] # The rustc fork of Rayon has deadlock detection code which intermittently # causes overflows in the CI (see https://github.com/rust-lang/rust/issues/90227) diff --git a/bootstrap.example.toml b/bootstrap.example.toml index 16fd9241a17..eac93957797 100644 --- a/bootstrap.example.toml +++ b/bootstrap.example.toml @@ -856,6 +856,9 @@ # as libstd features, this option can also be used to configure features such as optimize_for_size. #rust.std-features = ["panic_unwind"] +# Trigger a `DebugBreak` after an internal compiler error during bootstrap on Windows +#rust.break-on-ice = true + # ============================================================================= # Distribution options # diff --git a/compiler/rustc_abi/Cargo.toml b/compiler/rustc_abi/Cargo.toml index 3de6d186b8a..83d96d8d04d 100644 --- a/compiler/rustc_abi/Cargo.toml +++ b/compiler/rustc_abi/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -bitflags.workspace = true +bitflags = "2.4.1" rand = { version = "0.9.0", default-features = false, optional = true } rand_xoshiro = { version = "0.7.0", optional = true } rustc_data_structures = { path = "../rustc_data_structures", optional = true } @@ -15,7 +15,7 @@ rustc_index = { path = "../rustc_index", default-features = false } rustc_macros = { path = "../rustc_macros", optional = true } rustc_serialize = { path = "../rustc_serialize", optional = true } rustc_span = { path = "../rustc_span", optional = true } -tracing.workspace = true +tracing = "0.1" # tidy-alphabetical-end [features] diff --git a/compiler/rustc_abi/src/extern_abi.rs b/compiler/rustc_abi/src/extern_abi.rs index 41d744e1946..e3b2b1eff72 100644 --- a/compiler/rustc_abi/src/extern_abi.rs +++ b/compiler/rustc_abi/src/extern_abi.rs @@ -6,6 +6,8 @@ use std::hash::{Hash, Hasher}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableOrd}; #[cfg(feature = "nightly")] use rustc_macros::{Decodable, Encodable}; +#[cfg(feature = "nightly")] +use rustc_span::Symbol; use crate::AbiFromStrErr; @@ -226,6 +228,13 @@ impl StableOrd for ExternAbi { #[cfg(feature = "nightly")] rustc_error_messages::into_diag_arg_using_display!(ExternAbi); +#[cfg(feature = "nightly")] +pub enum CVariadicStatus { + NotSupported, + Stable, + Unstable { feature: Symbol }, +} + impl ExternAbi { /// An ABI "like Rust" /// @@ -238,23 +247,33 @@ impl ExternAbi { matches!(self, Rust | RustCall | RustCold) } - pub fn supports_varargs(self) -> bool { + /// Returns whether the ABI supports C variadics. This only controls whether we allow *imports* + /// of such functions via `extern` blocks; there's a separate check during AST construction + /// guarding *definitions* of variadic functions. + #[cfg(feature = "nightly")] + pub fn supports_c_variadic(self) -> CVariadicStatus { // * C and Cdecl obviously support varargs. // * C can be based on Aapcs, SysV64 or Win64, so they must support varargs. // * EfiApi is based on Win64 or C, so it also supports it. + // * System automatically falls back to C when used with variadics, therefore supports it. // // * Stdcall does not, because it would be impossible for the callee to clean // up the arguments. (callee doesn't know how many arguments are there) // * Same for Fastcall, Vectorcall and Thiscall. // * Other calling conventions are related to hardware or the compiler itself. + // + // All of the supported ones must have a test in `tests/codegen/cffi/c-variadic-ffi.rs`. match self { Self::C { .. } | Self::Cdecl { .. } | Self::Aapcs { .. } | Self::Win64 { .. } | Self::SysV64 { .. } - | Self::EfiApi => true, - _ => false, + | Self::EfiApi => CVariadicStatus::Stable, + Self::System { .. } => { + CVariadicStatus::Unstable { feature: rustc_span::sym::extern_system_varargs } + } + _ => CVariadicStatus::NotSupported, } } } diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index a1a0de5aaf8..369874521e5 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -63,6 +63,8 @@ mod tests; pub use callconv::{Heterogeneous, HomogeneousAggregate, Reg, RegKind}; pub use canon_abi::{ArmCall, CanonAbi, InterruptKind, X86Call}; +#[cfg(feature = "nightly")] +pub use extern_abi::CVariadicStatus; pub use extern_abi::{ExternAbi, all_names}; #[cfg(feature = "nightly")] pub use layout::{FIRST_VARIANT, FieldIdx, Layout, TyAbiInterface, TyAndLayout, VariantIdx}; diff --git a/compiler/rustc_ast/Cargo.toml b/compiler/rustc_ast/Cargo.toml index 7f948a65220..155e14a3796 100644 --- a/compiler/rustc_ast/Cargo.toml +++ b/compiler/rustc_ast/Cargo.toml @@ -5,9 +5,9 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -bitflags.workspace = true -memchr.workspace = true -rustc-literal-escaper.workspace = true +bitflags = "2.4.1" +memchr = "2.7.4" +rustc-literal-escaper = "0.0.5" rustc_ast_ir = { path = "../rustc_ast_ir" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_index = { path = "../rustc_index" } @@ -15,6 +15,6 @@ rustc_macros = { path = "../rustc_macros" } rustc_serialize = { path = "../rustc_serialize" } rustc_span = { path = "../rustc_span" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } -thin-vec.workspace = true -tracing.workspace = true +thin-vec = "0.2.12" +tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index de3e0e0c87f..802a6fa3249 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -937,7 +937,7 @@ pub enum PatKind { #[derive(Clone, Copy, Encodable, Decodable, Debug, PartialEq, Walkable)] pub enum PatFieldsRest { /// `module::StructName { field, ..}` - Rest, + Rest(Span), /// `module::StructName { field, syntax error }` Recovered(ErrorGuaranteed), /// `module::StructName { field }` @@ -4051,8 +4051,8 @@ mod size_asserts { static_assert_size!(Local, 96); static_assert_size!(MetaItemLit, 40); static_assert_size!(Param, 40); - static_assert_size!(Pat, 72); - static_assert_size!(PatKind, 48); + static_assert_size!(Pat, 80); + static_assert_size!(PatKind, 56); static_assert_size!(Path, 24); static_assert_size!(PathSegment, 24); static_assert_size!(Stmt, 32); diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index ea98bebd305..6dc6d1026f6 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -22,8 +22,7 @@ pub enum CommentKind { Block, } -// This type must not implement `Hash` due to the unusual `PartialEq` impl below. -#[derive(Copy, Clone, Debug, Encodable, Decodable, HashStable_Generic)] +#[derive(Copy, Clone, PartialEq, Debug, Encodable, Decodable, HashStable_Generic)] pub enum InvisibleOrigin { // From the expansion of a metavariable in a declarative macro. MetaVar(MetaVarKind), @@ -45,20 +44,6 @@ impl InvisibleOrigin { } } -impl PartialEq for InvisibleOrigin { - #[inline] - fn eq(&self, _other: &InvisibleOrigin) -> bool { - // When we had AST-based nonterminals we couldn't compare them, and the - // old `Nonterminal` type had an `eq` that always returned false, - // resulting in this restriction: - // https://doc.rust-lang.org/nightly/reference/macros-by-example.html#forwarding-a-matched-fragment - // This `eq` emulates that behaviour. We could consider lifting this - // restriction now but there are still cases involving invisible - // delimiters that make it harder than it first appears. - false - } -} - /// Annoyingly similar to `NonterminalKind`, but the slight differences are important. #[derive(Debug, Copy, Clone, PartialEq, Eq, Encodable, Decodable, Hash, HashStable_Generic)] pub enum MetaVarKind { @@ -142,7 +127,8 @@ impl Delimiter { } } - // This exists because `InvisibleOrigin`s should be compared. It is only used for assertions. + // This exists because `InvisibleOrigin`s should not be compared. It is only used for + // assertions. pub fn eq_ignoring_invisible_origin(&self, other: &Delimiter) -> bool { match (self, other) { (Delimiter::Parenthesis, Delimiter::Parenthesis) => true, diff --git a/compiler/rustc_ast_lowering/Cargo.toml b/compiler/rustc_ast_lowering/Cargo.toml index 317742a3bb8..6ac258155fe 100644 --- a/compiler/rustc_ast_lowering/Cargo.toml +++ b/compiler/rustc_ast_lowering/Cargo.toml @@ -24,6 +24,6 @@ rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } -thin-vec.workspace = true -tracing.workspace = true +thin-vec = "0.2.12" +tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index cbd17d66b75..3674814b796 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1434,10 +1434,10 @@ impl<'hir> LoweringContext<'_, 'hir> { self.dcx().emit_err(FunctionalRecordUpdateDestructuringAssignment { span: e.span, }); - true + Some(self.lower_span(e.span)) } - StructRest::Rest(_) => true, - StructRest::None => false, + StructRest::Rest(span) => Some(self.lower_span(*span)), + StructRest::None => None, }; let struct_pat = hir::PatKind::Struct(qpath, field_pats, fields_omitted); return self.pat_without_dbm(lhs.span, struct_pat); diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 70595391b85..72f20a95ff0 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -2028,7 +2028,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ( hir::ParamName::Plain(self.lower_ident(param.ident)), - hir::GenericParamKind::Const { ty, default, synthetic: false }, + hir::GenericParamKind::Const { ty, default }, ) } } @@ -2508,7 +2508,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fields: &'hir [hir::PatField<'hir>], ) -> &'hir hir::Pat<'hir> { let qpath = hir::QPath::LangItem(lang_item, self.lower_span(span)); - self.pat(span, hir::PatKind::Struct(qpath, fields, false)) + self.pat(span, hir::PatKind::Struct(qpath, fields, None)) } fn pat_ident(&mut self, span: Span, ident: Ident) -> (&'hir hir::Pat<'hir>, HirId) { diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index b8f86247875..ed159f37051 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -106,10 +106,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { break hir::PatKind::Struct( qpath, fs, - matches!( - etc, - ast::PatFieldsRest::Rest | ast::PatFieldsRest::Recovered(_) - ), + match etc { + ast::PatFieldsRest::Rest(sp) => Some(self.lower_span(*sp)), + ast::PatFieldsRest::Recovered(_) => Some(Span::default()), + _ => None, + }, ); } PatKind::Tuple(pats) => { diff --git a/compiler/rustc_ast_passes/Cargo.toml b/compiler/rustc_ast_passes/Cargo.toml index 6d2bcbed22b..3e04f8b11ec 100644 --- a/compiler/rustc_ast_passes/Cargo.toml +++ b/compiler/rustc_ast_passes/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -itertools.workspace = true +itertools = "0.12" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } @@ -18,5 +18,5 @@ rustc_macros = { path = "../rustc_macros" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } -thin-vec.workspace = true +thin-vec = "0.2.12" # tidy-alphabetical-end diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index a95f1443968..e5f1fcdc4b4 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -57,7 +57,7 @@ ast_passes_auto_super_lifetime = auto traits cannot have super traits or lifetim .label = {ast_passes_auto_super_lifetime} .suggestion = remove the super traits or lifetime bounds -ast_passes_bad_c_variadic = only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg +ast_passes_bad_c_variadic = defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention ast_passes_body_in_extern = incorrect `{$kind}` inside `extern` block .cannot_have = cannot have a body diff --git a/compiler/rustc_ast_pretty/Cargo.toml b/compiler/rustc_ast_pretty/Cargo.toml index 0c7e55305b4..b704040be96 100644 --- a/compiler/rustc_ast_pretty/Cargo.toml +++ b/compiler/rustc_ast_pretty/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -itertools.workspace = true +itertools = "0.12" rustc_ast = { path = "../rustc_ast" } rustc_lexer = { path = "../rustc_lexer" } rustc_span = { path = "../rustc_span" } @@ -13,5 +13,5 @@ rustc_span = { path = "../rustc_span" } [dev-dependencies] # tidy-alphabetical-start -thin-vec.workspace = true +thin-vec = "0.2.12" # tidy-alphabetical-end diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index a056ce3e29d..41b520b04c9 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1769,7 +1769,7 @@ impl<'a> State<'a> { }, |f| f.pat.span, ); - if let ast::PatFieldsRest::Rest | ast::PatFieldsRest::Recovered(_) = etc { + if let ast::PatFieldsRest::Rest(_) | ast::PatFieldsRest::Recovered(_) = etc { if !fields.is_empty() { self.word_space(","); } diff --git a/compiler/rustc_attr_parsing/Cargo.toml b/compiler/rustc_attr_parsing/Cargo.toml index 8bfde43fd33..79193f394fe 100644 --- a/compiler/rustc_attr_parsing/Cargo.toml +++ b/compiler/rustc_attr_parsing/Cargo.toml @@ -18,5 +18,5 @@ rustc_parse = { path = "../rustc_parse" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } -thin-vec.workspace = true +thin-vec = "0.2.12" # tidy-alphabetical-end diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index b8a748563d5..7fa1293463c 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -151,7 +151,7 @@ attr_parsing_unrecognized_repr_hint = attr_parsing_unstable_cfg_target_compact = compact `cfg(target(..))` is experimental and subject to change -attr_parsing_unstable_feature_bound_incompatible_stability = Item annotated with `#[unstable_feature_bound]` should not be stable +attr_parsing_unstable_feature_bound_incompatible_stability = item annotated with `#[unstable_feature_bound]` should not be stable .help = If this item is meant to be stable, do not use any functions annotated with `#[unstable_feature_bound]`. Otherwise, mark this item as unstable with `#[unstable]` attr_parsing_unsupported_literal_cfg_boolean = diff --git a/compiler/rustc_borrowck/Cargo.toml b/compiler/rustc_borrowck/Cargo.toml index 5f9dc41766b..9e7d55180a2 100644 --- a/compiler/rustc_borrowck/Cargo.toml +++ b/compiler/rustc_borrowck/Cargo.toml @@ -5,9 +5,9 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -either.workspace = true -itertools.workspace = true -polonius-engine.workspace = true +either = "1.5.0" +itertools = "0.12" +polonius-engine = "0.13.0" rustc_abi = { path = "../rustc_abi" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } @@ -25,5 +25,5 @@ rustc_span = { path = "../rustc_span" } rustc_trait_selection = { path = "../rustc_trait_selection" } rustc_traits = { path = "../rustc_traits" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } -tracing.workspace = true +tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs index 72615cb33b3..95c5366a6c8 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs @@ -503,7 +503,16 @@ pub(crate) fn apply_computed_concrete_opaque_types<'tcx>( let mut errors = Vec::new(); for &(key, hidden_type) in opaque_types { let Some(expected) = get_concrete_opaque_type(concrete_opaque_types, key.def_id) else { - assert!(tcx.use_typing_mode_borrowck(), "non-defining use in defining scope"); + if !tcx.use_typing_mode_borrowck() { + if let ty::Alias(ty::Opaque, alias_ty) = hidden_type.ty.kind() + && alias_ty.def_id == key.def_id.to_def_id() + && alias_ty.args == key.args + { + continue; + } else { + unreachable!("non-defining use in defining scope"); + } + } errors.push(DeferredOpaqueTypeError::NonDefiningUseInDefiningScope { span: hidden_type.span, opaque_type_key: key, diff --git a/compiler/rustc_builtin_macros/Cargo.toml b/compiler/rustc_builtin_macros/Cargo.toml index 9ca44d67150..e56b9e641a1 100644 --- a/compiler/rustc_builtin_macros/Cargo.toml +++ b/compiler/rustc_builtin_macros/Cargo.toml @@ -30,6 +30,6 @@ rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } -thin-vec.workspace = true -tracing.workspace = true +thin-vec = "0.2.12" +tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh index 52e02c857c7..62f1cc6a893 100755 --- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh +++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh @@ -121,7 +121,7 @@ rm tests/ui/abi/large-byval-align.rs # exceeds implementation limit of Cranelift # ============================================================ rm -r tests/run-make/remap-path-prefix-dwarf # requires llvm-dwarfdump rm -r tests/run-make/strip # same -rm -r tests/run-make/compiler-builtins # Expects lib/rustlib/src/rust to contains the standard library source +rm -r tests/run-make-cargo/compiler-builtins # Expects lib/rustlib/src/rust to contains the standard library source rm -r tests/run-make/translation # same rm -r tests/run-make/missing-unstable-trait-bound # This disables support for unstable features, but running cg_clif needs some unstable features rm -r tests/run-make/const-trait-stable-toolchain # same @@ -166,5 +166,5 @@ index 073116933bd..c3e4578204d 100644 EOF echo "[TEST] rustc test suite" -./x.py test --stage 0 --test-args=--no-capture tests/{codegen-units,run-make,ui,incremental} +./x.py test --stage 0 --test-args=--no-capture tests/{codegen-units,run-make,run-make-cargo,ui,incremental} popd diff --git a/compiler/rustc_codegen_gcc/build_system/src/test.rs b/compiler/rustc_codegen_gcc/build_system/src/test.rs index 3dd3fce2eec..1823aa71f40 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/test.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/test.rs @@ -1083,11 +1083,12 @@ where fn test_rustc(env: &Env, args: &TestArg) -> Result<(), String> { test_rustc_inner(env, args, |_| Ok(false), false, "run-make")?; + test_rustc_inner(env, args, |_| Ok(false), false, "run-make-cargo")?; test_rustc_inner(env, args, |_| Ok(false), false, "ui") } fn test_failing_rustc(env: &Env, args: &TestArg) -> Result<(), String> { - let result1 = test_rustc_inner( + let run_make_result = test_rustc_inner( env, args, retain_files_callback("tests/failing-run-make-tests.txt", "run-make"), @@ -1095,7 +1096,15 @@ fn test_failing_rustc(env: &Env, args: &TestArg) -> Result<(), String> { "run-make", ); - let result2 = test_rustc_inner( + let run_make_cargo_result = test_rustc_inner( + env, + args, + retain_files_callback("tests/failing-run-make-tests.txt", "run-make-cargo"), + false, + "run-make", + ); + + let ui_result = test_rustc_inner( env, args, retain_files_callback("tests/failing-ui-tests.txt", "ui"), @@ -1103,7 +1112,7 @@ fn test_failing_rustc(env: &Env, args: &TestArg) -> Result<(), String> { "ui", ); - result1.and(result2) + run_make_result.and(run_make_cargo_result).and(ui_result) } fn test_successful_rustc(env: &Env, args: &TestArg) -> Result<(), String> { @@ -1120,6 +1129,13 @@ fn test_successful_rustc(env: &Env, args: &TestArg) -> Result<(), String> { remove_files_callback("tests/failing-run-make-tests.txt", "run-make"), false, "run-make", + )?; + test_rustc_inner( + env, + args, + remove_files_callback("tests/failing-run-make-tests.txt", "run-make-cargo"), + false, + "run-make-cargo", ) } diff --git a/compiler/rustc_codegen_gcc/src/back/lto.rs b/compiler/rustc_codegen_gcc/src/back/lto.rs index fcee6b6df62..d29bba2570f 100644 --- a/compiler/rustc_codegen_gcc/src/back/lto.rs +++ b/compiler/rustc_codegen_gcc/src/back/lto.rs @@ -204,7 +204,7 @@ fn fat_lto( let path = tmp_path.path().to_path_buf().join(&module.name); let path = path.to_str().expect("path"); let context = &module.module_llvm.context; - let config = cgcx.config(module.kind); + let config = &cgcx.module_config; // NOTE: we need to set the optimization level here in order for LTO to do its job. context.set_optimization_level(to_gcc_opt_level(config.opt_level)); context.add_command_line_option("-flto=auto"); @@ -305,12 +305,9 @@ pub(crate) fn run_thin( ) } -pub(crate) fn prepare_thin( - module: ModuleCodegen<GccContext>, - _emit_summary: bool, -) -> (String, ThinBuffer) { +pub(crate) fn prepare_thin(module: ModuleCodegen<GccContext>) -> (String, ThinBuffer) { let name = module.name; - //let buffer = ThinBuffer::new(module.module_llvm.context, true, emit_summary); + //let buffer = ThinBuffer::new(module.module_llvm.context, true); let buffer = ThinBuffer::new(&module.module_llvm.context); (name, buffer) } @@ -650,10 +647,6 @@ impl ThinBufferMethods for ThinBuffer { fn data(&self) -> &[u8] { &[] } - - fn thin_link_data(&self) -> &[u8] { - unimplemented!(); - } } pub struct ThinData; //(Arc<TempDir>); diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 2d7df79ba95..f76f933cad4 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -408,11 +408,8 @@ impl WriteBackendMethods for GccCodegenBackend { back::write::codegen(cgcx, module, config) } - fn prepare_thin( - module: ModuleCodegen<Self::Module>, - emit_summary: bool, - ) -> (String, Self::ThinBuffer) { - back::lto::prepare_thin(module, emit_summary) + fn prepare_thin(module: ModuleCodegen<Self::Module>) -> (String, Self::ThinBuffer) { + back::lto::prepare_thin(module) } fn serialize_module(_module: ModuleCodegen<Self::Module>) -> (String, Self::ModuleBuffer) { diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index b04310f3d54..2d11628250c 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -8,15 +8,15 @@ test = false [dependencies] # tidy-alphabetical-start -bitflags.workspace = true +bitflags = "2.4.1" # To avoid duplicate dependencies, this should match the version of gimli used # by `rustc_codegen_ssa` via its `thorin-dwp` dependency. gimli = "0.31" -itertools.workspace = true -libc.workspace = true -measureme.workspace = true +itertools = "0.12" +libc = "0.2" +measureme = "12.0.1" object = { version = "0.37.0", default-features = false, features = ["std", "read"] } -rustc-demangle.workspace = true +rustc-demangle = "0.1.21" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_codegen_ssa = { path = "../rustc_codegen_ssa" } @@ -38,9 +38,9 @@ rustc_span = { path = "../rustc_span" } rustc_symbol_mangling = { path = "../rustc_symbol_mangling" } rustc_target = { path = "../rustc_target" } serde = { version = "1", features = ["derive"] } -serde_json.workspace = true +serde_json = "1" smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } -tracing.workspace = true +tracing = "0.1" # tidy-alphabetical-end [features] diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 5affb26483a..573c51a9539 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -29,8 +29,18 @@ pub(crate) fn apply_to_callsite(callsite: &Value, idx: AttributePlace, attrs: &[ } /// Get LLVM attribute for the provided inline heuristic. -#[inline] -fn inline_attr<'ll>(cx: &CodegenCx<'ll, '_>, inline: InlineAttr) -> Option<&'ll Attribute> { +pub(crate) fn inline_attr<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + instance: ty::Instance<'tcx>, +) -> Option<&'ll Attribute> { + // `optnone` requires `noinline` + let codegen_fn_attrs = cx.tcx.codegen_fn_attrs(instance.def_id()); + let inline = match (codegen_fn_attrs.inline, &codegen_fn_attrs.optimize) { + (_, OptimizeAttr::DoNotOptimize) => InlineAttr::Never, + (InlineAttr::None, _) if instance.def.requires_inline(cx.tcx) => InlineAttr::Hint, + (inline, _) => inline, + }; + if !cx.tcx.sess.opts.unstable_opts.inline_llvm { // disable LLVM inlining return Some(AttributeKind::NoInline.create_attr(cx.llcx)); @@ -286,6 +296,19 @@ pub(crate) fn tune_cpu_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribu .map(|tune_cpu| llvm::CreateAttrStringValue(cx.llcx, "tune-cpu", tune_cpu)) } +/// Get the `target-features` LLVM attribute. +pub(crate) fn target_features_attr<'ll>( + cx: &CodegenCx<'ll, '_>, + function_features: Vec<String>, +) -> Option<&'ll Attribute> { + let global_features = cx.tcx.global_backend_features(()).iter().map(String::as_str); + let function_features = function_features.iter().map(String::as_str); + let target_features = + global_features.chain(function_features).intersperse(",").collect::<String>(); + (!target_features.is_empty()) + .then(|| llvm::CreateAttrStringValue(cx.llcx, "target-features", &target_features)) +} + /// Get the `NonLazyBind` LLVM attribute, /// if the codegen options allow skipping the PLT. pub(crate) fn non_lazy_bind_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { @@ -346,14 +369,6 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( OptimizeAttr::Speed => {} } - // `optnone` requires `noinline` - let inline = match (codegen_fn_attrs.inline, &codegen_fn_attrs.optimize) { - (_, OptimizeAttr::DoNotOptimize) => InlineAttr::Never, - (InlineAttr::None, _) if instance.def.requires_inline(cx.tcx) => InlineAttr::Hint, - (inline, _) => inline, - }; - to_add.extend(inline_attr(cx, inline)); - if cx.sess().must_emit_unwind_tables() { to_add.push(uwtable_attr(cx.llcx, cx.sess().opts.unstable_opts.use_sync_unwind)); } @@ -488,6 +503,14 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( let function_features = codegen_fn_attrs.target_features.iter().map(|f| f.name.as_str()).collect::<Vec<&str>>(); + // Apply function attributes as per usual if there are no user defined + // target features otherwise this will get applied at the callsite. + if function_features.is_empty() { + if let Some(inline_attr) = inline_attr(cx, instance) { + to_add.push(inline_attr); + } + } + let function_features = function_features .iter() // Convert to LLVMFeatures and filter out unavailable ones @@ -513,13 +536,7 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( } } - let global_features = cx.tcx.global_backend_features(()).iter().map(|s| s.as_str()); - let function_features = function_features.iter().map(|s| s.as_str()); - let target_features: String = - global_features.chain(function_features).intersperse(",").collect(); - if !target_features.is_empty() { - to_add.push(llvm::CreateAttrStringValue(cx.llcx, "target-features", &target_features)); - } + to_add.extend(target_features_attr(cx, function_features)); attributes::apply_to_llfn(llfn, Function, &to_add); } diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index fc38c4f3e51..f571716d9dd 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -43,9 +43,7 @@ fn prepare_lto( .map(|symbol| CString::new(symbol.to_owned()).unwrap()) .collect::<Vec<CString>>(); - if cgcx.regular_module_config.instrument_coverage - || cgcx.regular_module_config.pgo_gen.enabled() - { + if cgcx.module_config.instrument_coverage || cgcx.module_config.pgo_gen.enabled() { // These are weak symbols that point to the profile version and the // profile name, which need to be treated as exported so LTO doesn't nix // them. @@ -55,15 +53,15 @@ fn prepare_lto( symbols_below_threshold.extend(PROFILER_WEAK_SYMBOLS.iter().map(|&sym| sym.to_owned())); } - if cgcx.regular_module_config.sanitizer.contains(SanitizerSet::MEMORY) { + if cgcx.module_config.sanitizer.contains(SanitizerSet::MEMORY) { let mut msan_weak_symbols = Vec::new(); // Similar to profiling, preserve weak msan symbol during LTO. - if cgcx.regular_module_config.sanitizer_recover.contains(SanitizerSet::MEMORY) { + if cgcx.module_config.sanitizer_recover.contains(SanitizerSet::MEMORY) { msan_weak_symbols.push(c"__msan_keep_going"); } - if cgcx.regular_module_config.sanitizer_memory_track_origins != 0 { + if cgcx.module_config.sanitizer_memory_track_origins != 0 { msan_weak_symbols.push(c"__msan_track_origins"); } @@ -187,12 +185,9 @@ pub(crate) fn run_thin( thin_lto(cgcx, dcx, modules, upstream_modules, cached_modules, &symbols_below_threshold) } -pub(crate) fn prepare_thin( - module: ModuleCodegen<ModuleLlvm>, - emit_summary: bool, -) -> (String, ThinBuffer) { +pub(crate) fn prepare_thin(module: ModuleCodegen<ModuleLlvm>) -> (String, ThinBuffer) { let name = module.name; - let buffer = ThinBuffer::new(module.module_llvm.llmod(), true, emit_summary); + let buffer = ThinBuffer::new(module.module_llvm.llmod(), true); (name, buffer) } @@ -583,7 +578,7 @@ pub(crate) fn run_pass_manager( thin: bool, ) { let _timer = cgcx.prof.generic_activity_with_arg("LLVM_lto_optimize", &*module.name); - let config = cgcx.config(module.kind); + let config = &cgcx.module_config; // Now we have one massive module inside of llmod. Time to run the // LTO-specific optimization passes that LLVM provides. @@ -689,9 +684,9 @@ unsafe impl Send for ThinBuffer {} unsafe impl Sync for ThinBuffer {} impl ThinBuffer { - pub(crate) fn new(m: &llvm::Module, is_thin: bool, emit_summary: bool) -> ThinBuffer { + pub(crate) fn new(m: &llvm::Module, is_thin: bool) -> ThinBuffer { unsafe { - let buffer = llvm::LLVMRustThinLTOBufferCreate(m, is_thin, emit_summary); + let buffer = llvm::LLVMRustThinLTOBufferCreate(m, is_thin); ThinBuffer(buffer) } } @@ -700,21 +695,21 @@ impl ThinBuffer { let mut ptr = NonNull::new(ptr).unwrap(); ThinBuffer(unsafe { ptr.as_mut() }) } -} -impl ThinBufferMethods for ThinBuffer { - fn data(&self) -> &[u8] { + pub(crate) fn thin_link_data(&self) -> &[u8] { unsafe { - let ptr = llvm::LLVMRustThinLTOBufferPtr(self.0) as *const _; - let len = llvm::LLVMRustThinLTOBufferLen(self.0); + let ptr = llvm::LLVMRustThinLTOBufferThinLinkDataPtr(self.0) as *const _; + let len = llvm::LLVMRustThinLTOBufferThinLinkDataLen(self.0); slice::from_raw_parts(ptr, len) } } +} - fn thin_link_data(&self) -> &[u8] { +impl ThinBufferMethods for ThinBuffer { + fn data(&self) -> &[u8] { unsafe { - let ptr = llvm::LLVMRustThinLTOBufferThinLinkDataPtr(self.0) as *const _; - let len = llvm::LLVMRustThinLTOBufferThinLinkDataLen(self.0); + let ptr = llvm::LLVMRustThinLTOBufferPtr(self.0) as *const _; + let len = llvm::LLVMRustThinLTOBufferLen(self.0); slice::from_raw_parts(ptr, len) } } @@ -745,7 +740,7 @@ pub(crate) fn optimize_thin_module( let module_llvm = ModuleLlvm::parse(cgcx, module_name, thin_module.data(), dcx); let mut module = ModuleCodegen::new_regular(thin_module.name(), module_llvm); // Given that the newly created module lacks a thinlto buffer for embedding, we need to re-add it here. - if cgcx.config(ModuleKind::Regular).embed_bitcode() { + if cgcx.module_config.embed_bitcode() { module.thin_lto_buffer = Some(thin_module.data().to_vec()); } { diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 7ea2ae6673b..423f0da4878 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -837,7 +837,7 @@ pub(crate) fn codegen( "LLVM_module_codegen_make_bitcode", &*module.name, ); - ThinBuffer::new(llmod, config.emit_thin_lto, false) + ThinBuffer::new(llmod, config.emit_thin_lto) }; let data = thin.data(); let _timer = cgcx diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 37379586d58..7d0691366e6 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -1392,7 +1392,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { fn call( &mut self, llty: &'ll Type, - fn_attrs: Option<&CodegenFnAttrs>, + fn_call_attrs: Option<&CodegenFnAttrs>, fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>, llfn: &'ll Value, args: &[&'ll Value], @@ -1409,10 +1409,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } // Emit CFI pointer type membership test - self.cfi_type_test(fn_attrs, fn_abi, instance, llfn); + self.cfi_type_test(fn_call_attrs, fn_abi, instance, llfn); // Emit KCFI operand bundle - let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, instance, llfn); + let kcfi_bundle = self.kcfi_operand_bundle(fn_call_attrs, fn_abi, instance, llfn); if let Some(kcfi_bundle) = kcfi_bundle.as_ref().map(|b| b.as_ref()) { bundles.push(kcfi_bundle); } @@ -1429,6 +1429,29 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { c"".as_ptr(), ) }; + + if let Some(instance) = instance { + // Attributes on the function definition being called + let fn_defn_attrs = self.cx.tcx.codegen_fn_attrs(instance.def_id()); + if let Some(fn_call_attrs) = fn_call_attrs + && !fn_call_attrs.target_features.is_empty() + // If there is an inline attribute and a target feature that matches + // we will add the attribute to the callsite otherwise we'll omit + // this and not add the attribute to prevent soundness issues. + && let Some(inlining_rule) = attributes::inline_attr(&self.cx, instance) + && self.cx.tcx.is_target_feature_call_safe( + &fn_call_attrs.target_features, + &fn_defn_attrs.target_features, + ) + { + attributes::apply_to_callsite( + call, + llvm::AttributePlace::Function, + &[inlining_rule], + ); + } + } + if let Some(fn_abi) = fn_abi { fn_abi.apply_attrs_callsite(self, call); } diff --git a/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs b/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs index 1280ab1442a..0737a18384b 100644 --- a/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs +++ b/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs @@ -193,7 +193,7 @@ fn gen_define_handling<'ll>( // reference) types. let num_ptr_types = types .iter() - .map(|&x| matches!(cx.type_kind(x), rustc_codegen_ssa::common::TypeKind::Pointer)) + .filter(|&x| matches!(cx.type_kind(x), rustc_codegen_ssa::common::TypeKind::Pointer)) .count(); // We do not know their size anymore at this level, so hardcode a placeholder. diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 4fd6110ac4a..a69fa54a54a 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -217,6 +217,10 @@ pub(crate) unsafe fn create_module<'ll>( // LLVM 22.0 updated the default layout on avr: https://github.com/llvm/llvm-project/pull/153010 target_data_layout = target_data_layout.replace("n8:16", "n8") } + if sess.target.arch == "nvptx64" { + // LLVM 22 updated the NVPTX layout to indicate 256-bit vector load/store: https://github.com/llvm/llvm-project/pull/155198 + target_data_layout = target_data_layout.replace("-i256:256", ""); + } } // Ensure the data-layout values hardcoded remain the defaults. @@ -849,7 +853,7 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { fn declare_c_main(&self, fn_type: Self::Type) -> Option<Self::Function> { let entry_name = self.sess().target.entry_name.as_ref(); if self.get_declared_value(entry_name).is_none() { - Some(self.declare_entry_fn( + let llfn = self.declare_entry_fn( entry_name, llvm::CallConv::from_conv( self.sess().target.entry_abi, @@ -857,7 +861,13 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { ), llvm::UnnamedAddr::Global, fn_type, - )) + ); + attributes::apply_to_llfn( + llfn, + llvm::AttributePlace::Function, + attributes::target_features_attr(self, vec![]).as_slice(), + ); + Some(llfn) } else { // If the symbol already exists, it is an error: for example, the user wrote // #[no_mangle] extern "C" fn main(..) {..} diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 49d3dedbeab..85f71f331a4 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -383,7 +383,9 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { | sym::rotate_left | sym::rotate_right | sym::saturating_add - | sym::saturating_sub => { + | sym::saturating_sub + | sym::unchecked_funnel_shl + | sym::unchecked_funnel_shr => { let ty = args[0].layout.ty; if !ty.is_integral() { tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType { @@ -424,18 +426,26 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { sym::bitreverse => { self.call_intrinsic("llvm.bitreverse", &[llty], &[args[0].immediate()]) } - sym::rotate_left | sym::rotate_right => { - let is_left = name == sym::rotate_left; - let val = args[0].immediate(); - let raw_shift = args[1].immediate(); - // rotate = funnel shift with first two args the same + sym::rotate_left + | sym::rotate_right + | sym::unchecked_funnel_shl + | sym::unchecked_funnel_shr => { + let is_left = name == sym::rotate_left || name == sym::unchecked_funnel_shl; + let lhs = args[0].immediate(); + let (rhs, raw_shift) = + if name == sym::rotate_left || name == sym::rotate_right { + // rotate = funnel shift with first two args the same + (lhs, args[1].immediate()) + } else { + (args[1].immediate(), args[2].immediate()) + }; let llvm_name = format!("llvm.fsh{}", if is_left { 'l' } else { 'r' }); // llvm expects shift to be the same type as the values, but rust // always uses `u32`. - let raw_shift = self.intcast(raw_shift, self.val_ty(val), false); + let raw_shift = self.intcast(raw_shift, self.val_ty(lhs), false); - self.call_intrinsic(llvm_name, &[llty], &[val, val, raw_shift]) + self.call_intrinsic(llvm_name, &[llty], &[lhs, rhs, raw_shift]) } sym::saturating_add | sym::saturating_sub => { let is_add = name == sym::saturating_add; diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 628cb34fd9e..6fb23d09843 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -211,11 +211,8 @@ impl WriteBackendMethods for LlvmCodegenBackend { ) -> CompiledModule { back::write::codegen(cgcx, module, config) } - fn prepare_thin( - module: ModuleCodegen<Self::Module>, - emit_summary: bool, - ) -> (String, Self::ThinBuffer) { - back::lto::prepare_thin(module, emit_summary) + fn prepare_thin(module: ModuleCodegen<Self::Module>) -> (String, Self::ThinBuffer) { + back::lto::prepare_thin(module) } fn serialize_module(module: ModuleCodegen<Self::Module>) -> (String, Self::ModuleBuffer) { (module.name, back::lto::ModuleBuffer::new(module.module_llvm.llmod())) diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index b66fc157b3c..0679f55ab7f 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2602,7 +2602,6 @@ unsafe extern "C" { pub(crate) fn LLVMRustThinLTOBufferCreate( M: &Module, is_thin: bool, - emit_summary: bool, ) -> &'static mut ThinLTOBuffer; pub(crate) fn LLVMRustThinLTOBufferFree(M: &'static mut ThinLTOBuffer); pub(crate) fn LLVMRustThinLTOBufferPtr(M: &ThinLTOBuffer) -> *const c_char; diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 6f87b5749c6..2dfbc581643 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -6,12 +6,12 @@ edition = "2024" [dependencies] # tidy-alphabetical-start ar_archive_writer = "0.5" -bitflags.workspace = true +bitflags = "2.4.1" bstr = "1.11.3" # `cc` updates often break things, so we pin it here. Cargo enforces "max 1 semver-compat version # per crate", so if you change this, you need to also change it in `rustc_llvm`. cc = "=1.2.16" -itertools.workspace = true +itertools = "0.12" pathdiff = "0.2.0" regex = "1.4" rustc_abi = { path = "../rustc_abi" } @@ -37,18 +37,18 @@ rustc_span = { path = "../rustc_span" } rustc_symbol_mangling = { path = "../rustc_symbol_mangling" } rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } -serde_json.workspace = true +serde_json = "1.0.59" smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } -tempfile.workspace = true -thin-vec.workspace = true +tempfile = "3.2" +thin-vec = "0.2.12" thorin-dwp = "0.9" -tracing.workspace = true +tracing = "0.1" wasm-encoder = "0.219" # tidy-alphabetical-end [target.'cfg(unix)'.dependencies] # tidy-alphabetical-start -libc.workspace = true +libc = "0.2.50" # tidy-alphabetical-end [dependencies.object] diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 44b9941691a..dd688b8b345 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -40,15 +40,15 @@ codegen_ssa_dynamic_linking_with_lto = .note = only 'staticlib', 'bin', and 'cdylib' outputs are supported with LTO codegen_ssa_error_calling_dlltool = - Error calling dlltool '{$dlltool_path}': {$error} + error calling dlltool '{$dlltool_path}': {$error} codegen_ssa_error_creating_import_library = - Error creating import library for {$lib_name}: {$error} + error creating import library for {$lib_name}: {$error} codegen_ssa_error_creating_remark_dir = failed to create remark directory: {$error} codegen_ssa_error_writing_def_file = - Error writing .DEF file: {$error} + error writing .DEF file: {$error} codegen_ssa_expected_name_value_pair = expected name value pair @@ -264,9 +264,9 @@ codegen_ssa_shuffle_indices_evaluation = could not evaluate shuffle_indices at c codegen_ssa_specify_libraries_to_link = use the `-l` flag to specify native libraries to link -codegen_ssa_static_library_native_artifacts = Link against the following native artifacts when linking against this static library. The order and any duplication can be significant on some platforms. +codegen_ssa_static_library_native_artifacts = link against the following native artifacts when linking against this static library. The order and any duplication can be significant on some platforms. -codegen_ssa_static_library_native_artifacts_to_file = Native artifacts to link against have been written to {$path}. The order and any duplication can be significant on some platforms. +codegen_ssa_static_library_native_artifacts_to_file = native artifacts to link against have been written to {$path}. The order and any duplication can be significant on some platforms. codegen_ssa_stripping_debug_info_failed = stripping debug info with `{$util}` failed: {$status} .note = {$output} @@ -364,13 +364,13 @@ codegen_ssa_unable_to_run = unable to run `{$util}`: {$error} codegen_ssa_unable_to_run_dsymutil = unable to run `dsymutil`: {$error} -codegen_ssa_unable_to_write_debugger_visualizer = Unable to write debugger visualizer file `{$path}`: {$error} +codegen_ssa_unable_to_write_debugger_visualizer = unable to write debugger visualizer file `{$path}`: {$error} codegen_ssa_unexpected_parameter_name = unexpected parameter name .label = expected `{$prefix_nops}` or `{$entry_nops}` codegen_ssa_unknown_archive_kind = - Don't know how to build archive of type: {$kind} + don't know how to build archive of type: {$kind} codegen_ssa_unknown_ctarget_feature = unknown and unstable feature specified for `-Ctarget-feature`: `{$feature}` diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 19c919c0e4e..48b01ea2df1 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -58,6 +58,7 @@ use super::linker::{self, Linker}; use super::metadata::{MetadataPosition, create_wrapper_file}; use super::rpath::{self, RPathConfig}; use super::{apple, versioned_llvm_target}; +use crate::base::needs_allocator_shim_for_linking; use crate::{ CodegenResults, CompiledModule, CrateInfo, NativeLib, errors, looks_like_rust_object_file, }; @@ -2080,9 +2081,17 @@ fn add_local_crate_regular_objects(cmd: &mut dyn Linker, codegen_results: &Codeg } /// Add object files for allocator code linked once for the whole crate tree. -fn add_local_crate_allocator_objects(cmd: &mut dyn Linker, codegen_results: &CodegenResults) { - if let Some(obj) = codegen_results.allocator_module.as_ref().and_then(|m| m.object.as_ref()) { - cmd.add_object(obj); +fn add_local_crate_allocator_objects( + cmd: &mut dyn Linker, + codegen_results: &CodegenResults, + crate_type: CrateType, +) { + if needs_allocator_shim_for_linking(&codegen_results.crate_info.dependency_formats, crate_type) + { + if let Some(obj) = codegen_results.allocator_module.as_ref().and_then(|m| m.object.as_ref()) + { + cmd.add_object(obj); + } } } @@ -2281,7 +2290,7 @@ fn linker_with_args( codegen_results, metadata, ); - add_local_crate_allocator_objects(cmd, codegen_results); + add_local_crate_allocator_objects(cmd, codegen_results, crate_type); // Avoid linking to dynamic libraries unless they satisfy some undefined symbols // at the point at which they are specified on the command line. diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index df1e91b12f9..a2efd420a32 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -11,8 +11,9 @@ use rustc_metadata::{ }; use rustc_middle::bug; use rustc_middle::middle::dependency_format::Linkage; -use rustc_middle::middle::exported_symbols; -use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo, SymbolExportKind}; +use rustc_middle::middle::exported_symbols::{ + self, ExportedSymbol, SymbolExportInfo, SymbolExportKind, SymbolExportLevel, +}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_session::config::{self, CrateType, DebugInfo, LinkerPluginLto, Lto, OptLevel, Strip}; @@ -22,6 +23,8 @@ use tracing::{debug, warn}; use super::command::Command; use super::symbol_export; +use crate::back::symbol_export::allocator_shim_symbols; +use crate::base::needs_allocator_shim_for_linking; use crate::errors; #[cfg(test)] @@ -1827,7 +1830,7 @@ fn exported_symbols_for_non_proc_macro( let export_threshold = symbol_export::crates_export_threshold(&[crate_type]); for_each_exported_symbols_include_dep(tcx, crate_type, |symbol, info, cnum| { // Do not export mangled symbols from cdylibs and don't attempt to export compiler-builtins - // from any cdylib. The latter doesn't work anyway as we use hidden visibility for + // from any dylib. The latter doesn't work anyway as we use hidden visibility for // compiler-builtins. Most linkers silently ignore it, but ld64 gives a warning. if info.level.is_below_threshold(export_threshold) && !tcx.is_compiler_builtins(cnum) { symbols.push(( @@ -1838,6 +1841,14 @@ fn exported_symbols_for_non_proc_macro( } }); + // Mark allocator shim symbols as exported only if they were generated. + if export_threshold == SymbolExportLevel::Rust + && needs_allocator_shim_for_linking(tcx.dependency_formats(()), crate_type) + && tcx.allocator_kind(()).is_some() + { + symbols.extend(allocator_shim_symbols(tcx)); + } + symbols } diff --git a/compiler/rustc_codegen_ssa/src/back/lto.rs b/compiler/rustc_codegen_ssa/src/back/lto.rs index c95038375a1..e6df6a2469f 100644 --- a/compiler/rustc_codegen_ssa/src/back/lto.rs +++ b/compiler/rustc_codegen_ssa/src/back/lto.rs @@ -8,8 +8,9 @@ use rustc_middle::ty::TyCtxt; use rustc_session::config::{CrateType, Lto}; use tracing::info; -use crate::back::symbol_export::{self, symbol_name_for_instance_in_crate}; +use crate::back::symbol_export::{self, allocator_shim_symbols, symbol_name_for_instance_in_crate}; use crate::back::write::CodegenContext; +use crate::base::allocator_kind_for_codegen; use crate::errors::{DynamicLinkingWithLTO, LtoDisallowed, LtoDylib, LtoProcMacro}; use crate::traits::*; @@ -115,6 +116,11 @@ pub(super) fn exported_symbols_for_lto( } } + // Mark allocator shim symbols as exported only if they were generated. + if export_threshold == SymbolExportLevel::Rust && allocator_kind_for_codegen(tcx).is_some() { + symbols_below_threshold.extend(allocator_shim_symbols(tcx).map(|(name, _kind)| name)); + } + symbols_below_threshold } diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index d8a1480e911..b49e67217fb 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -18,7 +18,7 @@ use rustc_symbol_mangling::mangle_internal_symbol; use rustc_target::spec::TlsModel; use tracing::debug; -use crate::base::allocator_kind_for_codegen; +use crate::back::symbol_export; fn threshold(tcx: TyCtxt<'_>) -> SymbolExportLevel { crates_export_threshold(tcx.crate_types()) @@ -217,31 +217,6 @@ fn exported_non_generic_symbols_provider_local<'tcx>( )); } - // Mark allocator shim symbols as exported only if they were generated. - if allocator_kind_for_codegen(tcx).is_some() { - for symbol_name in ALLOCATOR_METHODS - .iter() - .map(|method| mangle_internal_symbol(tcx, global_fn_name(method.name).as_str())) - .chain([ - mangle_internal_symbol(tcx, "__rust_alloc_error_handler"), - mangle_internal_symbol(tcx, OomStrategy::SYMBOL), - mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE), - ]) - { - let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, &symbol_name)); - - symbols.push(( - exported_symbol, - SymbolExportInfo { - level: SymbolExportLevel::Rust, - kind: SymbolExportKind::Text, - used: false, - rustc_std_internal_symbol: true, - }, - )); - } - } - // Sort so we get a stable incr. comp. hash. symbols.sort_by_cached_key(|s| s.0.symbol_name_for_local_instance(tcx)); @@ -516,6 +491,31 @@ pub(crate) fn provide(providers: &mut Providers) { upstream_monomorphizations_for_provider; } +pub(crate) fn allocator_shim_symbols( + tcx: TyCtxt<'_>, +) -> impl Iterator<Item = (String, SymbolExportKind)> { + ALLOCATOR_METHODS + .iter() + .map(move |method| mangle_internal_symbol(tcx, global_fn_name(method.name).as_str())) + .chain([ + mangle_internal_symbol(tcx, "__rust_alloc_error_handler"), + mangle_internal_symbol(tcx, OomStrategy::SYMBOL), + mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE), + ]) + .map(move |symbol_name| { + let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, &symbol_name)); + + ( + symbol_export::exporting_symbol_name_for_instance_in_crate( + tcx, + exported_symbol, + LOCAL_CRATE, + ), + SymbolExportKind::Text, + ) + }) +} + fn symbol_export_level(tcx: TyCtxt<'_>, sym_def_id: DefId) -> SymbolExportLevel { // We export anything that's not mangled at the "C" layer as it probably has // to do with ABI concerns. We do not, however, apply such treatment to diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 92582dcc399..cbaf67d7345 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -138,12 +138,23 @@ impl ModuleConfig { let emit_obj = if !should_emit_obj { EmitObj::None - } else if sess.target.obj_is_bitcode || sess.opts.cg.linker_plugin_lto.enabled() { + } else if sess.target.obj_is_bitcode + || (sess.opts.cg.linker_plugin_lto.enabled() && !no_builtins) + { // This case is selected if the target uses objects as bitcode, or // if linker plugin LTO is enabled. In the linker plugin LTO case // the assumption is that the final link-step will read the bitcode // and convert it to object code. This may be done by either the // native linker or rustc itself. + // + // Note, however, that the linker-plugin-lto requested here is + // explicitly ignored for `#![no_builtins]` crates. These crates are + // specifically ignored by rustc's LTO passes and wouldn't work if + // loaded into the linker. These crates define symbols that LLVM + // lowers intrinsics to, and these symbol dependencies aren't known + // until after codegen. As a result any crate marked + // `#![no_builtins]` is assumed to not participate in LTO and + // instead goes on to generate object code. EmitObj::Bitcode } else if need_bitcode_in_object(tcx) { EmitObj::ObjectCode(BitcodeSection::Full) @@ -322,8 +333,8 @@ pub struct CodegenContext<B: WriteBackendMethods> { pub crate_types: Vec<CrateType>, pub output_filenames: Arc<OutputFilenames>, pub invocation_temp: Option<String>, - pub regular_module_config: Arc<ModuleConfig>, - pub allocator_module_config: Arc<ModuleConfig>, + pub module_config: Arc<ModuleConfig>, + pub allocator_config: Arc<ModuleConfig>, pub tm_factory: TargetMachineFactoryFn<B>, pub msvc_imps_needed: bool, pub is_pe_coff: bool, @@ -361,13 +372,6 @@ impl<B: WriteBackendMethods> CodegenContext<B> { pub fn create_dcx(&self) -> DiagCtxt { DiagCtxt::new(Box::new(self.diag_emitter.clone())) } - - pub fn config(&self, kind: ModuleKind) -> &ModuleConfig { - match kind { - ModuleKind::Regular => &self.regular_module_config, - ModuleKind::Allocator => &self.allocator_module_config, - } - } } fn generate_thin_lto_work<B: ExtraBackendMethods>( @@ -431,6 +435,7 @@ pub(crate) fn start_async_codegen<B: ExtraBackendMethods>( backend: B, tcx: TyCtxt<'_>, target_cpu: String, + allocator_module: Option<ModuleCodegen<B::Module>>, ) -> OngoingCodegen<B> { let (coordinator_send, coordinator_receive) = channel(); @@ -454,6 +459,7 @@ pub(crate) fn start_async_codegen<B: ExtraBackendMethods>( coordinator_receive, Arc::new(regular_config), Arc::new(allocator_config), + allocator_module, coordinator_send.clone(), ); @@ -709,15 +715,6 @@ pub(crate) enum WorkItem<B: WriteBackendMethods> { } impl<B: WriteBackendMethods> WorkItem<B> { - fn module_kind(&self) -> ModuleKind { - match *self { - WorkItem::Optimize(ref m) => m.kind, - WorkItem::CopyPostLtoArtifacts(_) | WorkItem::FatLto { .. } | WorkItem::ThinLto(_) => { - ModuleKind::Regular - } - } - } - /// Generate a short description of this work item suitable for use as a thread name. fn short_description(&self) -> String { // `pthread_setname()` on *nix ignores anything beyond the first 15 @@ -806,7 +803,7 @@ pub(crate) fn compute_per_cgu_lto_type( let linker_does_lto = opts.cg.linker_plugin_lto.enabled(); // When we're automatically doing ThinLTO for multi-codegen-unit - // builds we don't actually want to LTO the allocator modules if + // builds we don't actually want to LTO the allocator module if // it shows up. This is due to various linker shenanigans that // we'll encounter later. let is_allocator = module_kind == ModuleKind::Allocator; @@ -832,11 +829,17 @@ pub(crate) fn compute_per_cgu_lto_type( fn execute_optimize_work_item<B: ExtraBackendMethods>( cgcx: &CodegenContext<B>, mut module: ModuleCodegen<B::Module>, - module_config: &ModuleConfig, ) -> WorkItemResult<B> { + let _timer = cgcx.prof.generic_activity_with_arg("codegen_module_optimize", &*module.name); + let dcx = cgcx.create_dcx(); let dcx = dcx.handle(); + let module_config = match module.kind { + ModuleKind::Regular => &cgcx.module_config, + ModuleKind::Allocator => &cgcx.allocator_config, + }; + B::optimize(cgcx, dcx, &mut module, module_config); // After we've done the initial round of optimizations we need to @@ -848,7 +851,7 @@ fn execute_optimize_work_item<B: ExtraBackendMethods>( // If we're doing some form of incremental LTO then we need to be sure to // save our module to disk first. - let bitcode = if cgcx.config(module.kind).emit_pre_lto_bc { + let bitcode = if module_config.emit_pre_lto_bc { let filename = pre_lto_bitcode_filename(&module.name); cgcx.incr_comp_session_dir.as_ref().map(|path| path.join(&filename)) } else { @@ -861,7 +864,7 @@ fn execute_optimize_work_item<B: ExtraBackendMethods>( WorkItemResult::Finished(module) } ComputedLtoType::Thin => { - let (name, thin_buffer) = B::prepare_thin(module, false); + let (name, thin_buffer) = B::prepare_thin(module); if let Some(path) = bitcode { fs::write(&path, thin_buffer.data()).unwrap_or_else(|e| { panic!("Error writing pre-lto-bitcode file `{}`: {}", path.display(), e); @@ -888,8 +891,11 @@ fn execute_optimize_work_item<B: ExtraBackendMethods>( fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>( cgcx: &CodegenContext<B>, module: CachedModuleCodegen, - module_config: &ModuleConfig, ) -> WorkItemResult<B> { + let _timer = cgcx + .prof + .generic_activity_with_arg("codegen_copy_artifacts_from_incr_cache", &*module.name); + let incr_comp_session_dir = cgcx.incr_comp_session_dir.as_ref().unwrap(); let mut links_from_incr_cache = Vec::new(); @@ -948,6 +954,7 @@ fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>( } }; + let module_config = &cgcx.module_config; let should_emit_obj = module_config.emit_obj != EmitObj::None; let assembly = load_from_incr_cache(module_config.emit_asm, OutputType::Assembly); let llvm_ir = load_from_incr_cache(module_config.emit_ir, OutputType::LlvmAssembly); @@ -959,8 +966,8 @@ fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>( WorkItemResult::Finished(CompiledModule { links_from_incr_cache, - name: module.name, kind: ModuleKind::Regular, + name: module.name, object, dwarf_object, bytecode, @@ -975,8 +982,9 @@ fn execute_fat_lto_work_item<B: ExtraBackendMethods>( each_linked_rlib_for_lto: &[PathBuf], mut needs_fat_lto: Vec<FatLtoInput<B>>, import_only_modules: Vec<(SerializedModule<B::ModuleBuffer>, WorkProduct)>, - module_config: &ModuleConfig, ) -> WorkItemResult<B> { + let _timer = cgcx.prof.generic_activity_with_arg("codegen_module_perform_lto", "everything"); + for (module, wp) in import_only_modules { needs_fat_lto.push(FatLtoInput::Serialized { name: wp.cgu_name, buffer: module }) } @@ -987,17 +995,18 @@ fn execute_fat_lto_work_item<B: ExtraBackendMethods>( each_linked_rlib_for_lto, needs_fat_lto, ); - let module = B::codegen(cgcx, module, module_config); + let module = B::codegen(cgcx, module, &cgcx.module_config); WorkItemResult::Finished(module) } fn execute_thin_lto_work_item<B: ExtraBackendMethods>( cgcx: &CodegenContext<B>, module: lto::ThinModule<B>, - module_config: &ModuleConfig, ) -> WorkItemResult<B> { + let _timer = cgcx.prof.generic_activity_with_arg("codegen_module_perform_lto", module.name()); + let module = B::optimize_thin(cgcx, module); - let module = B::codegen(cgcx, module, module_config); + let module = B::codegen(cgcx, module, &cgcx.module_config); WorkItemResult::Finished(module) } @@ -1082,6 +1091,7 @@ fn start_executing_work<B: ExtraBackendMethods>( coordinator_receive: Receiver<Message<B>>, regular_config: Arc<ModuleConfig>, allocator_config: Arc<ModuleConfig>, + allocator_module: Option<ModuleCodegen<B::Module>>, tx_to_llvm_workers: Sender<Message<B>>, ) -> thread::JoinHandle<Result<CompiledModules, ()>> { let coordinator_send = tx_to_llvm_workers; @@ -1146,8 +1156,8 @@ fn start_executing_work<B: ExtraBackendMethods>( expanded_args: tcx.sess.expanded_args.clone(), diag_emitter: shared_emitter.clone(), output_filenames: Arc::clone(tcx.output_filenames(())), - regular_module_config: regular_config, - allocator_module_config: allocator_config, + module_config: regular_config, + allocator_config, tm_factory: backend.target_machine_factory(tcx.sess, ol, backend_features), msvc_imps_needed: msvc_imps_needed(tcx), is_pe_coff: tcx.sess.target.is_like_windows, @@ -1301,7 +1311,6 @@ fn start_executing_work<B: ExtraBackendMethods>( // This is where we collect codegen units that have gone all the way // through codegen and LLVM. let mut compiled_modules = vec![]; - let mut compiled_allocator_module = None; let mut needs_fat_lto = Vec::new(); let mut needs_thin_lto = Vec::new(); let mut lto_import_only_modules = Vec::new(); @@ -1342,6 +1351,17 @@ fn start_executing_work<B: ExtraBackendMethods>( let mut llvm_start_time: Option<VerboseTimingGuard<'_>> = None; + let compiled_allocator_module = allocator_module.and_then(|allocator_module| { + match execute_optimize_work_item(&cgcx, allocator_module) { + WorkItemResult::Finished(compiled_module) => return Some(compiled_module), + WorkItemResult::NeedsFatLto(fat_lto_input) => needs_fat_lto.push(fat_lto_input), + WorkItemResult::NeedsThinLto(name, thin_buffer) => { + needs_thin_lto.push((name, thin_buffer)) + } + } + None + }); + // Run the message loop while there's still anything that needs message // processing. Note that as soon as codegen is aborted we simply want to // wait for all existing work to finish, so many of the conditions here @@ -1575,15 +1595,7 @@ fn start_executing_work<B: ExtraBackendMethods>( match result { Ok(WorkItemResult::Finished(compiled_module)) => { - match compiled_module.kind { - ModuleKind::Regular => { - compiled_modules.push(compiled_module); - } - ModuleKind::Allocator => { - assert!(compiled_allocator_module.is_none()); - compiled_allocator_module = Some(compiled_module); - } - } + compiled_modules.push(compiled_module); } Ok(WorkItemResult::NeedsFatLto(fat_lto_input)) => { assert!(!started_lto); @@ -1711,46 +1723,22 @@ fn spawn_work<'a, B: ExtraBackendMethods>( let cgcx = cgcx.clone(); B::spawn_named_thread(cgcx.time_trace, work.short_description(), move || { - let result = std::panic::catch_unwind(AssertUnwindSafe(|| { - let module_config = cgcx.config(work.module_kind()); - - match work { - WorkItem::Optimize(m) => { - let _timer = - cgcx.prof.generic_activity_with_arg("codegen_module_optimize", &*m.name); - execute_optimize_work_item(&cgcx, m, module_config) - } - WorkItem::CopyPostLtoArtifacts(m) => { - let _timer = cgcx.prof.generic_activity_with_arg( - "codegen_copy_artifacts_from_incr_cache", - &*m.name, - ); - execute_copy_from_cache_work_item(&cgcx, m, module_config) - } - WorkItem::FatLto { - exported_symbols_for_lto, - each_linked_rlib_for_lto, - needs_fat_lto, - import_only_modules, - } => { - let _timer = cgcx - .prof - .generic_activity_with_arg("codegen_module_perform_lto", "everything"); - execute_fat_lto_work_item( - &cgcx, - &exported_symbols_for_lto, - &each_linked_rlib_for_lto, - needs_fat_lto, - import_only_modules, - module_config, - ) - } - WorkItem::ThinLto(m) => { - let _timer = - cgcx.prof.generic_activity_with_arg("codegen_module_perform_lto", m.name()); - execute_thin_lto_work_item(&cgcx, m, module_config) - } - } + let result = std::panic::catch_unwind(AssertUnwindSafe(|| match work { + WorkItem::Optimize(m) => execute_optimize_work_item(&cgcx, m), + WorkItem::CopyPostLtoArtifacts(m) => execute_copy_from_cache_work_item(&cgcx, m), + WorkItem::FatLto { + exported_symbols_for_lto, + each_linked_rlib_for_lto, + needs_fat_lto, + import_only_modules, + } => execute_fat_lto_work_item( + &cgcx, + &exported_symbols_for_lto, + &each_linked_rlib_for_lto, + needs_fat_lto, + import_only_modules, + ), + WorkItem::ThinLto(m) => execute_thin_lto_work_item(&cgcx, m), })); let msg = match result { diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 8abaf201aba..45b028aa8ef 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -17,6 +17,7 @@ use rustc_hir::lang_items::LangItem; use rustc_hir::{ItemId, Target}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::middle::debugger_visualizer::{DebuggerVisualizerFile, DebuggerVisualizerType}; +use rustc_middle::middle::dependency_format::Dependencies; use rustc_middle::middle::exported_symbols::{self, SymbolExportKind}; use rustc_middle::middle::lang_items; use rustc_middle::mir::BinOp; @@ -630,14 +631,30 @@ pub fn allocator_kind_for_codegen(tcx: TyCtxt<'_>) -> Option<AllocatorKind> { // If the crate doesn't have an `allocator_kind` set then there's definitely // no shim to generate. Otherwise we also check our dependency graph for all // our output crate types. If anything there looks like its a `Dynamic` - // linkage, then it's already got an allocator shim and we'll be using that - // one instead. If nothing exists then it's our job to generate the - // allocator! - let any_dynamic_crate = tcx.dependency_formats(()).iter().any(|(_, list)| { + // linkage for all crate types we may link as, then it's already got an + // allocator shim and we'll be using that one instead. If nothing exists + // then it's our job to generate the allocator! If crate types disagree + // about whether an allocator shim is necessary or not, we generate one + // and let needs_allocator_shim_for_linking decide at link time whether or + // not to use it for any particular linker invocation. + let all_crate_types_any_dynamic_crate = tcx.dependency_formats(()).iter().all(|(_, list)| { use rustc_middle::middle::dependency_format::Linkage; list.iter().any(|&linkage| linkage == Linkage::Dynamic) }); - if any_dynamic_crate { None } else { tcx.allocator_kind(()) } + if all_crate_types_any_dynamic_crate { None } else { tcx.allocator_kind(()) } +} + +/// Decide if this particular crate type needs an allocator shim linked in. +/// This may return true even when allocator_kind_for_codegen returns false. In +/// this case no allocator shim shall be linked. +pub(crate) fn needs_allocator_shim_for_linking( + dependency_formats: &Dependencies, + crate_type: CrateType, +) -> bool { + use rustc_middle::middle::dependency_format::Linkage; + let any_dynamic_crate = + dependency_formats[&crate_type].iter().any(|&linkage| linkage == Linkage::Dynamic); + !any_dynamic_crate } pub fn codegen_crate<B: ExtraBackendMethods>( @@ -647,7 +664,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>( ) -> OngoingCodegen<B> { // Skip crate items and just output metadata in -Z no-codegen mode. if tcx.sess.opts.unstable_opts.no_codegen || !tcx.sess.opts.output_types.should_codegen() { - let ongoing_codegen = start_async_codegen(backend, tcx, target_cpu); + let ongoing_codegen = start_async_codegen(backend, tcx, target_cpu, None); ongoing_codegen.codegen_finished(tcx); @@ -678,7 +695,27 @@ pub fn codegen_crate<B: ExtraBackendMethods>( } } - let ongoing_codegen = start_async_codegen(backend.clone(), tcx, target_cpu); + // Codegen an allocator shim, if necessary. + let allocator_module = if let Some(kind) = allocator_kind_for_codegen(tcx) { + let llmod_id = + cgu_name_builder.build_cgu_name(LOCAL_CRATE, &["crate"], Some("allocator")).to_string(); + + tcx.sess.time("write_allocator_module", || { + let module = backend.codegen_allocator( + tcx, + &llmod_id, + kind, + // If allocator_kind is Some then alloc_error_handler_kind must + // also be Some. + tcx.alloc_error_handler_kind(()).unwrap(), + ); + Some(ModuleCodegen::new_allocator(llmod_id, module)) + }) + } else { + None + }; + + let ongoing_codegen = start_async_codegen(backend.clone(), tcx, target_cpu, allocator_module); // For better throughput during parallel processing by LLVM, we used to sort // CGUs largest to smallest. This would lead to better thread utilization @@ -795,35 +832,6 @@ pub fn codegen_crate<B: ExtraBackendMethods>( } } - // Codegen an allocator shim, if necessary. - // Do this last to ensure the LLVM_passes timer doesn't start while no CGUs have been codegened - // yet for the backend to optimize. - if let Some(kind) = allocator_kind_for_codegen(tcx) { - let llmod_id = - cgu_name_builder.build_cgu_name(LOCAL_CRATE, &["crate"], Some("allocator")).to_string(); - let module_llvm = tcx.sess.time("write_allocator_module", || { - backend.codegen_allocator( - tcx, - &llmod_id, - kind, - // If allocator_kind is Some then alloc_error_handler_kind must - // also be Some. - tcx.alloc_error_handler_kind(()).unwrap(), - ) - }); - - ongoing_codegen.wait_for_signal_to_codegen_item(); - ongoing_codegen.check_for_errors(tcx.sess); - - // These modules are generally cheap and won't throw off scheduling. - let cost = 0; - submit_codegened_module_to_llvm( - &ongoing_codegen.coordinator, - ModuleCodegen::new_allocator(llmod_id, module_llvm), - cost, - ); - } - ongoing_codegen.codegen_finished(tcx); // Since the main thread is sometimes blocked during codegen, we keep track diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 961bb788149..008340e614d 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -428,9 +428,16 @@ fn check_result( // llvm/llvm-project#70563). if !codegen_fn_attrs.target_features.is_empty() && matches!(codegen_fn_attrs.inline, InlineAttr::Always) + && !tcx.features().target_feature_inline_always() && let Some(span) = interesting_spans.inline { - tcx.dcx().span_err(span, "cannot use `#[inline(always)]` with `#[target_feature]`"); + feature_err( + tcx.sess, + sym::target_feature_inline_always, + span, + "cannot use `#[inline(always)]` with `#[target_feature]`", + ) + .emit(); } // warn that inline has no effect when no_sanitize is present diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index fe0500a5d4c..baba8f9ca3e 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -119,7 +119,7 @@ impl<M> ModuleCodegen<M> { }); CompiledModule { - name: self.name.clone(), + name: self.name, kind: self.kind, object, dwarf_object, diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 8a67b8d6e5f..f6f2e3f2a3a 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -1,3 +1,4 @@ +use itertools::Itertools as _; use rustc_abi::{self as abi, FIRST_VARIANT}; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout}; @@ -111,14 +112,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let size = bx.const_usize(dest.layout.size.bytes()); // Use llvm.memset.p0i8.* to initialize all same byte arrays - if let Some(int) = bx.cx().const_to_opt_u128(v, false) { - let bytes = &int.to_le_bytes()[..cg_elem.layout.size.bytes_usize()]; - let first = bytes[0]; - if bytes[1..].iter().all(|&b| b == first) { - let fill = bx.cx().const_u8(first); - bx.memset(start, fill, size, dest.val.align, MemFlags::empty()); - return true; - } + if let Some(int) = bx.cx().const_to_opt_u128(v, false) + && let bytes = &int.to_le_bytes()[..cg_elem.layout.size.bytes_usize()] + && let Ok(&byte) = bytes.iter().all_equal_value() + { + let fill = bx.cx().const_u8(byte); + bx.memset(start, fill, size, dest.val.align, MemFlags::empty()); + return true; } // Use llvm.memset.p0i8.* to initialize byte arrays @@ -130,13 +130,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { false }; - match cg_elem.val { - OperandValue::Immediate(v) => { - if try_init_all_same(bx, v) { - return; - } - } - _ => (), + if let OperandValue::Immediate(v) = cg_elem.val + && try_init_all_same(bx, v) + { + return; } let count = self diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index cc7c4e46d7b..1ac1d7ef2e2 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -50,16 +50,12 @@ pub trait WriteBackendMethods: Clone + 'static { module: ModuleCodegen<Self::Module>, config: &ModuleConfig, ) -> CompiledModule; - fn prepare_thin( - module: ModuleCodegen<Self::Module>, - want_summary: bool, - ) -> (String, Self::ThinBuffer); + fn prepare_thin(module: ModuleCodegen<Self::Module>) -> (String, Self::ThinBuffer); fn serialize_module(module: ModuleCodegen<Self::Module>) -> (String, Self::ModuleBuffer); } pub trait ThinBufferMethods: Send + Sync { fn data(&self) -> &[u8]; - fn thin_link_data(&self) -> &[u8]; } pub trait ModuleBufferMethods: Send + Sync { diff --git a/compiler/rustc_const_eval/Cargo.toml b/compiler/rustc_const_eval/Cargo.toml index acf19b0f2fc..51dcee8d882 100644 --- a/compiler/rustc_const_eval/Cargo.toml +++ b/compiler/rustc_const_eval/Cargo.toml @@ -5,9 +5,9 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -either.workspace = true +either = "1" rustc_abi = { path = "../rustc_abi" } -rustc_apfloat.workspace = true +rustc_apfloat = "0.2.0" rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } @@ -22,5 +22,5 @@ rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } -tracing.workspace = true +tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index 60518dafbf2..700d7c26752 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -457,7 +457,7 @@ const_eval_validation_failure = it is undefined behavior to use this value const_eval_validation_failure_note = - The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. const_eval_validation_front_matter_invalid_value = constructing invalid value const_eval_validation_front_matter_invalid_value_with_path = constructing invalid value at {$path} diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index 852fc11350b..c8296e05f6b 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml @@ -6,15 +6,15 @@ edition = "2024" [dependencies] # tidy-alphabetical-start arrayvec = { version = "0.7", default-features = false } -bitflags.workspace = true -either.workspace = true +bitflags = "2.4.1" +either = "1.0" elsa = "1.11.0" ena = "0.14.3" -indexmap.workspace = true +indexmap = "2.4.0" jobserver_crate = { version = "0.1.28", package = "jobserver" } -measureme.workspace = true +measureme = "12.0.1" parking_lot = "0.12" -rustc-hash.workspace = true +rustc-hash = "2.0.0" rustc-stable-hash = { version = "0.1.0", features = ["nightly"] } rustc_arena = { path = "../rustc_arena" } rustc_graphviz = { path = "../rustc_graphviz" } @@ -25,9 +25,9 @@ rustc_serialize = { path = "../rustc_serialize" } rustc_thread_pool = { path = "../rustc_thread_pool" } smallvec = { version = "1.8.1", features = ["const_generics", "union", "may_dangle"] } stacker = "0.1.17" -tempfile.workspace = true -thin-vec.workspace = true -tracing.workspace = true +tempfile = "3.2" +thin-vec = "0.2.12" +tracing = "0.1" # tidy-alphabetical-end [dependencies.hashbrown] @@ -47,7 +47,7 @@ features = [ [target.'cfg(unix)'.dependencies] # tidy-alphabetical-start -libc.workspace = true +libc = "0.2" # tidy-alphabetical-end [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index 196c8aa3547..ae1dbd2cf51 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -49,14 +49,14 @@ rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } rustc_ty_utils = { path = "../rustc_ty_utils" } -serde_json.workspace = true +serde_json = "1.0.59" shlex = "1.0" -tracing.workspace = true +tracing = { version = "0.1.35" } # tidy-alphabetical-end [target.'cfg(all(unix, any(target_env = "gnu", target_os = "macos")))'.dependencies] # tidy-alphabetical-start -libc.workspace = true +libc = "0.2" # tidy-alphabetical-end [target.'cfg(windows)'.dependencies.windows] diff --git a/compiler/rustc_driver_impl/messages.ftl b/compiler/rustc_driver_impl/messages.ftl index 2c6a0291ac2..b62cdc35f51 100644 --- a/compiler/rustc_driver_impl/messages.ftl +++ b/compiler/rustc_driver_impl/messages.ftl @@ -14,7 +14,7 @@ driver_impl_ice_version = rustc {$version} running on {$triple} driver_impl_rlink_corrupt_file = corrupt metadata encountered in `{$file}` -driver_impl_rlink_empty_version_number = The input does not contain version number +driver_impl_rlink_empty_version_number = the input does not contain version number driver_impl_rlink_encoding_version_mismatch = .rlink file was produced with encoding version `{$version_array}`, but the current version is `{$rlink_version}` @@ -24,6 +24,6 @@ driver_impl_rlink_rustc_version_mismatch = .rlink file was produced by rustc ver driver_impl_rlink_unable_to_read = failed to read rlink file: `{$err}` -driver_impl_rlink_wrong_file_type = The input does not look like a .rlink file +driver_impl_rlink_wrong_file_type = the input does not look like a .rlink file driver_impl_unstable_feature_usage = cannot dump feature usage metrics: {$error} diff --git a/compiler/rustc_error_messages/Cargo.toml b/compiler/rustc_error_messages/Cargo.toml index 7d2dc20e136..db22e065907 100644 --- a/compiler/rustc_error_messages/Cargo.toml +++ b/compiler/rustc_error_messages/Cargo.toml @@ -17,6 +17,6 @@ rustc_data_structures = { path = "../rustc_data_structures" } rustc_macros = { path = "../rustc_macros" } rustc_serialize = { path = "../rustc_serialize" } rustc_span = { path = "../rustc_span" } -tracing.workspace = true +tracing = "0.1" unic-langid = { version = "0.9.0", features = ["macros"] } # tidy-alphabetical-end diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml index 67a17ce88fa..7912b8e6098 100644 --- a/compiler/rustc_errors/Cargo.toml +++ b/compiler/rustc_errors/Cargo.toml @@ -21,10 +21,10 @@ rustc_macros = { path = "../rustc_macros" } rustc_serialize = { path = "../rustc_serialize" } rustc_span = { path = "../rustc_span" } serde = { version = "1.0.125", features = ["derive"] } -serde_json.workspace = true +serde_json = "1.0.59" termcolor = "1.2.0" termize = "0.2" -tracing.workspace = true +tracing = "0.1" # tidy-alphabetical-end [target.'cfg(windows)'.dependencies.windows] diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 96a4ed3218f..ae23ef1e255 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -945,11 +945,6 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { None, "Span must not be empty and have no suggestion", ); - debug_assert_eq!( - parts.array_windows().find(|[a, b]| a.span.overlaps(b.span)), - None, - "suggestion must not have overlapping parts", - ); self.push_suggestion(CodeSuggestion { substitutions: vec![Substitution { parts }], diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index b94370e8e9b..93b1e6b7615 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -2354,7 +2354,6 @@ impl HumanEmitter { .sum(); let underline_start = (span_start_pos + start) as isize + offset; let underline_end = (span_start_pos + start + sub_len) as isize + offset; - assert!(underline_start >= 0 && underline_end >= 0); let padding: usize = max_line_num_len + 3; for p in underline_start..underline_end { if let DisplaySuggestion::Underline = show_code_change diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 71fc54f0d33..8869799ce90 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -381,6 +381,17 @@ impl CodeSuggestion { // Assumption: all spans are in the same file, and all spans // are disjoint. Sort in ascending order. substitution.parts.sort_by_key(|part| part.span.lo()); + // Verify the assumption that all spans are disjoint + assert_eq!( + substitution.parts.array_windows().find(|[a, b]| a.span.overlaps(b.span)), + None, + "all spans must be disjoint", + ); + + // Account for cases where we are suggesting the same code that's already + // there. This shouldn't happen often, but in some cases for multipart + // suggestions it's much easier to handle it here than in the origin. + substitution.parts.retain(|p| is_different(sm, &p.snippet, p.span)); // Find the bounding span. let lo = substitution.parts.iter().map(|part| part.span.lo()).min()?; @@ -470,16 +481,12 @@ impl CodeSuggestion { _ => 1, }) .sum(); - if !is_different(sm, &part.snippet, part.span) { - // Account for cases where we are suggesting the same code that's already - // there. This shouldn't happen often, but in some cases for multipart - // suggestions it's much easier to handle it here than in the origin. - } else { - line_highlight.push(SubstitutionHighlight { - start: (cur_lo.col.0 as isize + acc) as usize, - end: (cur_lo.col.0 as isize + acc + len) as usize, - }); - } + + line_highlight.push(SubstitutionHighlight { + start: (cur_lo.col.0 as isize + acc) as usize, + end: (cur_lo.col.0 as isize + acc + len) as usize, + }); + buf.push_str(&part.snippet); let cur_hi = sm.lookup_char_pos(part.span.hi()); // Account for the difference between the width of the current code and the @@ -1160,7 +1167,7 @@ impl<'a> DiagCtxtHandle<'a> { // - It's only produce with JSON output. // - It's not emitted the usual way, via `emit_diagnostic`. // - The `$message_type` field is "unused_externs" rather than the usual - // "diagnosic". + // "diagnostic". // // We count it as a lint error because it has a lint level. The value // of `loud` (which comes from "unused-externs" or diff --git a/compiler/rustc_expand/Cargo.toml b/compiler/rustc_expand/Cargo.toml index 9bb7143af51..f897833d85c 100644 --- a/compiler/rustc_expand/Cargo.toml +++ b/compiler/rustc_expand/Cargo.toml @@ -29,6 +29,6 @@ rustc_serialize = { path = "../rustc_serialize" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } -thin-vec.workspace = true -tracing.workspace = true +thin-vec = "0.2.12" +tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index 0324057e331..ab8e059b7b7 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -77,7 +77,7 @@ use std::rc::Rc; pub(crate) use NamedMatch::*; pub(crate) use ParseResult::*; -use rustc_ast::token::{self, DocComment, NonterminalKind, Token}; +use rustc_ast::token::{self, DocComment, NonterminalKind, Token, TokenKind}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::ErrorGuaranteed; use rustc_lint_defs::pluralize; @@ -397,7 +397,23 @@ fn token_name_eq(t1: &Token, t2: &Token) -> bool { { ident1.name == ident2.name && is_raw1 == is_raw2 } else { - t1.kind == t2.kind + // Note: we SHOULD NOT use `t1.kind == t2.kind` here, and we should instead compare the + // tokens using the special comparison logic below. + // It makes sure that variants containing `InvisibleOrigin` will + // never compare equal to one another. + // + // When we had AST-based nonterminals we couldn't compare them, and the + // old `Nonterminal` type had an `eq` that always returned false, + // resulting in this restriction: + // <https://doc.rust-lang.org/nightly/reference/macros-by-example.html#forwarding-a-matched-fragment> + // This comparison logic emulates that behaviour. We could consider lifting this + // restriction now but there are still cases involving invisible + // delimiters that make it harder than it first appears. + match (t1.kind, t2.kind) { + (TokenKind::OpenInvisible(_) | TokenKind::CloseInvisible(_), _) + | (_, TokenKind::OpenInvisible(_) | TokenKind::CloseInvisible(_)) => false, + (a, b) => a == b, + } } } diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index 174844d6ad6..e4e4866b64c 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -556,7 +556,12 @@ fn metavar_expr_concat<'tx>( }; match &named_matches[*curr_idx] { // FIXME(c410-f3r) Nested repetitions are unimplemented - MatchedSeq(_) => unimplemented!(), + MatchedSeq(_) => { + return Err(dcx.struct_span_err( + ident.span, + "nested repetitions with `${concat(...)}` metavariable expressions are not yet supported", + )); + } MatchedSingle(pnr) => extract_symbol_from_pnr(dcx, pnr, ident.span)?, } } diff --git a/compiler/rustc_feature/Cargo.toml b/compiler/rustc_feature/Cargo.toml index b58f2375852..a4746ac455c 100644 --- a/compiler/rustc_feature/Cargo.toml +++ b/compiler/rustc_feature/Cargo.toml @@ -9,5 +9,5 @@ rustc_data_structures = { path = "../rustc_data_structures" } rustc_hir = { path = "../rustc_hir" } rustc_span = { path = "../rustc_span" } serde = { version = "1.0.125", features = ["derive"] } -serde_json.workspace = true +serde_json = "1.0.59" # tidy-alphabetical-end diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index 9fe55216f93..6af4cfb0e56 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -203,6 +203,9 @@ declare_features! ( (accepted, expr_fragment_specifier_2024, "1.83.0", Some(123742)), /// Allows arbitrary expressions in key-value attributes at parse time. (accepted, extended_key_value_attributes, "1.54.0", Some(78835)), + /// Allows using `aapcs`, `efiapi`, `sysv64` and `win64` as calling conventions + /// for functions with varargs. + (accepted, extended_varargs_abi_support, "CURRENT_RUSTC_VERSION", Some(100189)), /// Allows resolving absolute paths as paths from other crates. (accepted, extern_absolute_paths, "1.30.0", Some(44660)), /// Allows `extern crate foo as bar;`. This puts `bar` into extern prelude. diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 92b435b4b01..4f35bf63a1a 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -492,9 +492,6 @@ declare_features! ( (incomplete, explicit_tail_calls, "1.72.0", Some(112788)), /// Allows using `#[export_stable]` which indicates that an item is exportable. (incomplete, export_stable, "1.88.0", Some(139939)), - /// Allows using `aapcs`, `efiapi`, `sysv64` and `win64` as calling conventions - /// for functions with varargs. - (unstable, extended_varargs_abi_support, "1.65.0", Some(100189)), /// Allows using `system` as a calling convention with varargs. (unstable, extern_system_varargs, "1.86.0", Some(136946)), /// Allows defining `extern type`s. @@ -645,6 +642,8 @@ declare_features! ( (unstable, super_let, "1.88.0", Some(139076)), /// Allows subtrait items to shadow supertrait items. (unstable, supertrait_item_shadowing, "1.86.0", Some(89151)), + /// Allows the use of target_feature when a function is marked inline(always). + (unstable, target_feature_inline_always, "CURRENT_RUSTC_VERSION", Some(145574)), /// Allows using `#[thread_local]` on `static` items. (unstable, thread_local, "1.0.0", Some(29594)), /// Allows defining `trait X = A + B;` alias items. diff --git a/compiler/rustc_fluent_macro/Cargo.toml b/compiler/rustc_fluent_macro/Cargo.toml index 60afd9aca87..d7ef4280aef 100644 --- a/compiler/rustc_fluent_macro/Cargo.toml +++ b/compiler/rustc_fluent_macro/Cargo.toml @@ -11,8 +11,8 @@ proc-macro = true annotate-snippets = "0.11" fluent-bundle = "0.16" fluent-syntax = "0.12" -proc-macro2.workspace = true -quote.workspace = true +proc-macro2 = "1" +quote = "1" syn = { version = "2", features = ["full"] } unic-langid = { version = "0.9.0", features = ["macros"] } # tidy-alphabetical-end diff --git a/compiler/rustc_fs_util/Cargo.toml b/compiler/rustc_fs_util/Cargo.toml index 37970e81fea..90a6acade8b 100644 --- a/compiler/rustc_fs_util/Cargo.toml +++ b/compiler/rustc_fs_util/Cargo.toml @@ -5,5 +5,5 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -tempfile.workspace = true +tempfile = "3.7.1" # tidy-alphabetical-end diff --git a/compiler/rustc_hir/Cargo.toml b/compiler/rustc_hir/Cargo.toml index e74fcfe7455..1008a3e787d 100644 --- a/compiler/rustc_hir/Cargo.toml +++ b/compiler/rustc_hir/Cargo.toml @@ -5,8 +5,8 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -bitflags.workspace = true -odht.workspace = true +bitflags = "2.9.1" +odht = { version = "0.3.1", features = ["nightly"] } rustc_abi = { path = "../rustc_abi" } rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } @@ -21,6 +21,6 @@ rustc_serialize = { path = "../rustc_serialize" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } -thin-vec.workspace = true -tracing.workspace = true +thin-vec = "0.2.12" +tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index e397c286de2..ae03121e5f7 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -784,7 +784,6 @@ pub enum GenericParamKind<'hir> { ty: &'hir Ty<'hir>, /// Optional default value for the const generic param default: Option<&'hir ConstArg<'hir>>, - synthetic: bool, }, } @@ -1884,8 +1883,8 @@ pub enum PatKind<'hir> { Binding(BindingMode, HirId, Ident, Option<&'hir Pat<'hir>>), /// A struct or struct variant pattern (e.g., `Variant {x, y, ..}`). - /// The `bool` is `true` in the presence of a `..`. - Struct(QPath<'hir>, &'hir [PatField<'hir>], bool), + /// The `Option` contains the span of a possible `..`. + Struct(QPath<'hir>, &'hir [PatField<'hir>], Option<Span>), /// A tuple struct/variant pattern `Variant(x, y, .., z)`. /// If the `..` pattern fragment is present, then `DotDotPos` denotes its position. @@ -4979,8 +4978,8 @@ mod size_asserts { static_assert_size!(ItemKind<'_>, 64); static_assert_size!(LetStmt<'_>, 72); static_assert_size!(Param<'_>, 32); - static_assert_size!(Pat<'_>, 72); - static_assert_size!(PatKind<'_>, 48); + static_assert_size!(Pat<'_>, 80); + static_assert_size!(PatKind<'_>, 56); static_assert_size!(Path<'_>, 40); static_assert_size!(PathSegment<'_>, 48); static_assert_size!(QPath<'_>, 24); diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 9b2f8ae75fa..25a7ae239f3 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -1085,7 +1085,7 @@ pub fn walk_generic_param<'v, V: Visitor<'v>>( GenericParamKind::Type { ref default, .. } => { visit_opt!(visitor, visit_ty_unambig, default) } - GenericParamKind::Const { ref ty, ref default, synthetic: _ } => { + GenericParamKind::Const { ref ty, ref default } => { try_visit!(visitor.visit_ty_unambig(ty)); if let Some(default) = default { try_visit!(visitor.visit_const_param_default(*hir_id, default)); diff --git a/compiler/rustc_hir_analysis/Cargo.toml b/compiler/rustc_hir_analysis/Cargo.toml index bb86beb2251..e5017794d8f 100644 --- a/compiler/rustc_hir_analysis/Cargo.toml +++ b/compiler/rustc_hir_analysis/Cargo.toml @@ -9,7 +9,7 @@ doctest = false [dependencies] # tidy-alphabetical-start -itertools.workspace = true +itertools = "0.12" rustc_abi = { path = "../rustc_abi" } rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } @@ -29,5 +29,5 @@ rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } -tracing.workspace = true +tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index eccb88a938f..08b344638dd 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -978,7 +978,7 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), tcx.ensure_ok().fn_sig(def_id); let item = tcx.hir_foreign_item(item); let hir::ForeignItemKind::Fn(sig, ..) = item.kind else { bug!() }; - require_c_abi_if_c_variadic(tcx, sig.decl, abi, item.span); + check_c_variadic_abi(tcx, sig.decl, abi, item.span); } DefKind::Static { .. } => { tcx.ensure_ok().codegen_fn_attrs(def_id); diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index cfc6bc2f3a0..aa2d27ab809 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -449,6 +449,9 @@ pub(crate) fn check_intrinsic_type( } sym::unchecked_shl | sym::unchecked_shr => (2, 0, vec![param(0), param(1)], param(0)), sym::rotate_left | sym::rotate_right => (1, 0, vec![param(0), tcx.types.u32], param(0)), + sym::unchecked_funnel_shl | sym::unchecked_funnel_shr => { + (1, 0, vec![param(0), param(0), tcx.types.u32], param(0)) + } sym::unchecked_add | sym::unchecked_sub | sym::unchecked_mul => { (1, 0, vec![param(0), param(0)], param(0)) } diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 2e4b151d4dc..b5c0ca4727c 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -98,7 +98,7 @@ use tracing::debug; use self::compare_impl_item::collect_return_position_impl_trait_in_trait_tys; use self::region::region_scope_tree; -use crate::{errors, require_c_abi_if_c_variadic}; +use crate::{check_c_variadic_abi, errors}; /// Adds query implementations to the [Providers] vtable, see [`rustc_middle::query`] pub(super) fn provide(providers: &mut Providers) { diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index f5770b7312d..2ba7ed46f92 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -490,12 +490,8 @@ fn resolve_local<'tcx>( // // Iterate up to the enclosing destruction scope to find the same scope that will also // be used for the result of the block itself. - while let Some(s) = visitor.cx.var_parent { - let parent = visitor.scope_tree.parent_map.get(&s).cloned(); - if let Some(Scope { data: ScopeData::Destruction, .. }) = parent { - break; - } - visitor.cx.var_parent = parent; + if let Some(inner_scope) = visitor.cx.var_parent { + (visitor.cx.var_parent, _) = visitor.scope_tree.default_temporary_scope(inner_scope) } } } diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index ce0e51f106f..333cea23c41 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -305,7 +305,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { ty::GenericParamDefKind::Type { has_default: default.is_some(), synthetic } } - GenericParamKind::Const { ty: _, default, synthetic } => { + GenericParamKind::Const { ty: _, default } => { if default.is_some() { match param_default_policy.expect("no policy for generic param default") { ParamDefaultPolicy::Allowed => {} @@ -316,7 +316,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { } } - ty::GenericParamDefKind::Const { has_default: default.is_some(), synthetic } + ty::GenericParamDefKind::Const { has_default: default.is_some() } } }; Some(ty::GenericParamDef { @@ -523,7 +523,7 @@ impl<'v> Visitor<'v> for AnonConstInParamTyDetector { type Result = ControlFlow<()>; fn visit_generic_param(&mut self, p: &'v hir::GenericParam<'v>) -> Self::Result { - if let GenericParamKind::Const { ty, default: _, synthetic: _ } = p.kind { + if let GenericParamKind::Const { ty, default: _ } = p.kind { let prev = self.in_param_ty; self.in_param_ty = true; let res = self.visit_ty_unambig(ty); diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs index fc519c194bb..f5a64ede398 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs @@ -419,14 +419,7 @@ pub(crate) fn check_generic_arg_count( .filter(|param| matches!(param.kind, ty::GenericParamDefKind::Type { synthetic: true, .. })) .count(); let named_type_param_count = param_counts.types - has_self as usize - synth_type_param_count; - let synth_const_param_count = gen_params - .own_params - .iter() - .filter(|param| { - matches!(param.kind, ty::GenericParamDefKind::Const { synthetic: true, .. }) - }) - .count(); - let named_const_param_count = param_counts.consts - synth_const_param_count; + let named_const_param_count = param_counts.consts; let infer_lifetimes = (gen_pos != GenericArgPosition::Type || seg.infer_args) && !gen_args.has_lifetime_params(); diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index c7b984d9b25..c5e079fe89a 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -52,11 +52,11 @@ use rustc_trait_selection::traits::{self, FulfillmentError}; use tracing::{debug, instrument}; use crate::check::check_abi; +use crate::check_c_variadic_abi; use crate::errors::{AmbiguousLifetimeBound, BadReturnTypeNotation}; use crate::hir_ty_lowering::errors::{GenericsArgsErrExtend, prohibit_assoc_item_constraint}; use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args}; use crate::middle::resolve_bound_vars as rbv; -use crate::require_c_abi_if_c_variadic; /// A path segment that is semantically allowed to have generic arguments. #[derive(Debug)] @@ -2412,7 +2412,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { Ty::new_tup_from_iter(tcx, fields.iter().map(|t| self.lower_ty(t))) } hir::TyKind::FnPtr(bf) => { - require_c_abi_if_c_variadic(tcx, bf.decl, bf.abi, hir_ty.span); + check_c_variadic_abi(tcx, bf.decl, bf.abi, hir_ty.span); Ty::new_fn_ptr( tcx, diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 44a5ceed469..2562ab7542a 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -90,7 +90,7 @@ mod outlives; mod variance; pub use errors::NoVariantNamed; -use rustc_abi::ExternAbi; +use rustc_abi::{CVariadicStatus, ExternAbi}; use rustc_hir::def::DefKind; use rustc_hir::lints::DelayedLint; use rustc_hir::{self as hir}; @@ -99,7 +99,6 @@ use rustc_middle::mir::interpret::GlobalId; use rustc_middle::query::Providers; use rustc_middle::ty::{self, Const, Ty, TyCtxt}; use rustc_session::parse::feature_err; -use rustc_span::symbol::sym; use rustc_span::{ErrorGuaranteed, Span}; use rustc_trait_selection::traits; @@ -108,46 +107,34 @@ use crate::hir_ty_lowering::{FeedConstTy, HirTyLowerer}; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } -fn require_c_abi_if_c_variadic( - tcx: TyCtxt<'_>, - decl: &hir::FnDecl<'_>, - abi: ExternAbi, - span: Span, -) { - // ABIs which can stably use varargs - if !decl.c_variadic || matches!(abi, ExternAbi::C { .. } | ExternAbi::Cdecl { .. }) { +fn check_c_variadic_abi(tcx: TyCtxt<'_>, decl: &hir::FnDecl<'_>, abi: ExternAbi, span: Span) { + if !decl.c_variadic { + // Not even a variadic function. return; } - // ABIs with feature-gated stability - let extended_abi_support = tcx.features().extended_varargs_abi_support(); - let extern_system_varargs = tcx.features().extern_system_varargs(); - - // If the feature gate has been enabled, we can stop here - if extern_system_varargs && let ExternAbi::System { .. } = abi { - return; - }; - if extended_abi_support && abi.supports_varargs() { - return; - }; - - // Looks like we need to pick an error to emit. - // Is there any feature which we could have enabled to make this work? - let unstable_explain = - format!("C-variadic functions with the {abi} calling convention are unstable"); - match abi { - ExternAbi::System { .. } => { - feature_err(&tcx.sess, sym::extern_system_varargs, span, unstable_explain) + match abi.supports_c_variadic() { + CVariadicStatus::Stable => {} + CVariadicStatus::NotSupported => { + tcx.dcx() + .create_err(errors::VariadicFunctionCompatibleConvention { + span, + convention: &format!("{abi}"), + }) + .emit(); } - abi if abi.supports_varargs() => { - feature_err(&tcx.sess, sym::extended_varargs_abi_support, span, unstable_explain) + CVariadicStatus::Unstable { feature } => { + if !tcx.features().enabled(feature) { + feature_err( + &tcx.sess, + feature, + span, + format!("C-variadic functions with the {abi} calling convention are unstable"), + ) + .emit(); + } } - _ => tcx.dcx().create_err(errors::VariadicFunctionCompatibleConvention { - span, - convention: &format!("{abi}"), - }), } - .emit(); } /// Adds query implementations to the [Providers] vtable, see [`rustc_middle::query`] diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index be5859b57c5..b9d8eed54a9 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1958,12 +1958,12 @@ impl<'a> State<'a> { self.print_qpath(qpath, true); self.nbsp(); self.word("{"); - let empty = fields.is_empty() && !etc; + let empty = fields.is_empty() && etc.is_none(); if !empty { self.space(); } self.commasep_cmnt(Consistent, fields, |s, f| s.print_patfield(f), |f| f.pat.span); - if etc { + if etc.is_some() { if !fields.is_empty() { self.word_space(","); } @@ -2379,7 +2379,7 @@ impl<'a> State<'a> { self.print_type(default); } } - GenericParamKind::Const { ty, ref default, synthetic: _ } => { + GenericParamKind::Const { ty, ref default } => { self.word_space(":"); self.print_type(ty); if let Some(default) = default { diff --git a/compiler/rustc_hir_typeck/Cargo.toml b/compiler/rustc_hir_typeck/Cargo.toml index 5af0c6134ca..f00125c3e09 100644 --- a/compiler/rustc_hir_typeck/Cargo.toml +++ b/compiler/rustc_hir_typeck/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -itertools.workspace = true +itertools = "0.12" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_attr_parsing = { path = "../rustc_attr_parsing" } @@ -25,5 +25,5 @@ rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } -tracing.workspace = true +tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index e66601631fc..b99f811db1a 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -1897,7 +1897,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { fcx.suggest_semicolon_at_end(cond_expr.span, &mut err); } } - }; + } // If this is due to an explicit `return`, suggest adding a return type. if let Some((fn_id, fn_decl)) = fcx.get_fn_decl(block_or_return_id) diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index a652e08905a..7adbee7ff28 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -2585,12 +2585,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .filter(|item| item.is_fn() && !item.is_method()) .filter_map(|item| { // Only assoc fns that return `Self` - let fn_sig = self.tcx.fn_sig(item.def_id).skip_binder(); - let ret_ty = fn_sig.output(); - let ret_ty = self.tcx.normalize_erasing_late_bound_regions( - self.typing_env(self.param_env), - ret_ty, - ); + let fn_sig = self + .tcx + .fn_sig(item.def_id) + .instantiate(self.tcx, self.fresh_args_for_item(span, item.def_id)); + let ret_ty = self.tcx.instantiate_bound_regions_with_erased(fn_sig.output()); if !self.can_eq(self.param_env, ret_ty, adt_ty) { return None; } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 5aec50c8b53..94b635c41b4 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1912,7 +1912,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::StmtKind::Expr(ref expr) => { // Check with expected type of `()`. self.check_expr_has_type_or_error(expr, self.tcx.types.unit, |err| { - if expr.can_have_side_effects() { + if self.is_next_stmt_expr_continuation(stmt.hir_id) + && let hir::ExprKind::Match(..) | hir::ExprKind::If(..) = expr.kind + { + // We have something like `match () { _ => true } && true`. Suggest + // wrapping in parentheses. We find the statement or expression + // following the `match` (`&& true`) and see if it is something that + // can reasonably be interpreted as a binop following an expression. + err.multipart_suggestion( + "parentheses are required to parse this as an expression", + vec![ + (expr.span.shrink_to_lo(), "(".to_string()), + (expr.span.shrink_to_hi(), ")".to_string()), + ], + Applicability::MachineApplicable, + ); + } else if expr.can_have_side_effects() { self.suggest_semicolon_at_end(expr.span, err); } }); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index aca3840712e..84ea2ec0f8a 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1,3 +1,4 @@ +// ignore-tidy-filelength use core::cmp::min; use core::iter; @@ -766,56 +767,121 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { needs_block: bool, parent_is_closure: bool, ) { - if expected.is_unit() { - // `BlockTailExpression` only relevant if the tail expr would be - // useful on its own. - match expression.kind { - ExprKind::Call(..) - | ExprKind::MethodCall(..) - | ExprKind::Loop(..) - | ExprKind::If(..) - | ExprKind::Match(..) - | ExprKind::Block(..) - if expression.can_have_side_effects() - // If the expression is from an external macro, then do not suggest - // adding a semicolon, because there's nowhere to put it. - // See issue #81943. - && !expression.span.in_external_macro(self.tcx.sess.source_map()) => + if !expected.is_unit() { + return; + } + // `BlockTailExpression` only relevant if the tail expr would be + // useful on its own. + match expression.kind { + ExprKind::Call(..) + | ExprKind::MethodCall(..) + | ExprKind::Loop(..) + | ExprKind::If(..) + | ExprKind::Match(..) + | ExprKind::Block(..) + if expression.can_have_side_effects() + // If the expression is from an external macro, then do not suggest + // adding a semicolon, because there's nowhere to put it. + // See issue #81943. + && !expression.span.in_external_macro(self.tcx.sess.source_map()) => + { + if needs_block { + err.multipart_suggestion( + "consider using a semicolon here", + vec![ + (expression.span.shrink_to_lo(), "{ ".to_owned()), + (expression.span.shrink_to_hi(), "; }".to_owned()), + ], + Applicability::MachineApplicable, + ); + } else if let hir::Node::Block(block) = self.tcx.parent_hir_node(expression.hir_id) + && let hir::Node::Expr(expr) = self.tcx.parent_hir_node(block.hir_id) + && let hir::Node::Expr(if_expr) = self.tcx.parent_hir_node(expr.hir_id) + && let hir::ExprKind::If(_cond, _then, Some(_else)) = if_expr.kind + && let hir::Node::Stmt(stmt) = self.tcx.parent_hir_node(if_expr.hir_id) + && let hir::StmtKind::Expr(_) = stmt.kind + && self.is_next_stmt_expr_continuation(stmt.hir_id) { - if needs_block { - err.multipart_suggestion( - "consider using a semicolon here", - vec![ - (expression.span.shrink_to_lo(), "{ ".to_owned()), - (expression.span.shrink_to_hi(), "; }".to_owned()), - ], - Applicability::MachineApplicable, - ); - } else { - err.span_suggestion( - expression.span.shrink_to_hi(), - "consider using a semicolon here", - ";", - Applicability::MachineApplicable, - ); - } + err.multipart_suggestion( + "parentheses are required to parse this as an expression", + vec![ + (stmt.span.shrink_to_lo(), "(".to_string()), + (stmt.span.shrink_to_hi(), ")".to_string()), + ], + Applicability::MachineApplicable, + ); + } else { + err.span_suggestion( + expression.span.shrink_to_hi(), + "consider using a semicolon here", + ";", + Applicability::MachineApplicable, + ); } - ExprKind::Path(..) | ExprKind::Lit(_) - if parent_is_closure - && !expression.span.in_external_macro(self.tcx.sess.source_map()) => + } + ExprKind::Path(..) | ExprKind::Lit(_) + if parent_is_closure + && !expression.span.in_external_macro(self.tcx.sess.source_map()) => + { + err.span_suggestion_verbose( + expression.span.shrink_to_lo(), + "consider ignoring the value", + "_ = ", + Applicability::MachineApplicable, + ); + } + _ => { + if let hir::Node::Block(block) = self.tcx.parent_hir_node(expression.hir_id) + && let hir::Node::Expr(expr) = self.tcx.parent_hir_node(block.hir_id) + && let hir::Node::Expr(if_expr) = self.tcx.parent_hir_node(expr.hir_id) + && let hir::ExprKind::If(_cond, _then, Some(_else)) = if_expr.kind + && let hir::Node::Stmt(stmt) = self.tcx.parent_hir_node(if_expr.hir_id) + && let hir::StmtKind::Expr(_) = stmt.kind + && self.is_next_stmt_expr_continuation(stmt.hir_id) { - err.span_suggestion_verbose( - expression.span.shrink_to_lo(), - "consider ignoring the value", - "_ = ", + // The error is pointing at an arm of an if-expression, and we want to get the + // `Span` of the whole if-expression for the suggestion. This only works for a + // single level of nesting, which is fine. + // We have something like `if true { false } else { true } && true`. Suggest + // wrapping in parentheses. We find the statement or expression following the + // `if` (`&& true`) and see if it is something that can reasonably be + // interpreted as a binop following an expression. + err.multipart_suggestion( + "parentheses are required to parse this as an expression", + vec![ + (stmt.span.shrink_to_lo(), "(".to_string()), + (stmt.span.shrink_to_hi(), ")".to_string()), + ], Applicability::MachineApplicable, ); } - _ => (), } } } + pub(crate) fn is_next_stmt_expr_continuation(&self, hir_id: HirId) -> bool { + if let hir::Node::Block(b) = self.tcx.parent_hir_node(hir_id) + && let mut stmts = b.stmts.iter().skip_while(|s| s.hir_id != hir_id) + && let Some(_) = stmts.next() // The statement the statement that was passed in + && let Some(next) = match (stmts.next(), b.expr) { // The following statement + (Some(next), _) => match next.kind { + hir::StmtKind::Expr(next) | hir::StmtKind::Semi(next) => Some(next), + _ => None, + }, + (None, Some(next)) => Some(next), + _ => None, + } + && let hir::ExprKind::AddrOf(..) // prev_stmt && next + | hir::ExprKind::Unary(..) // prev_stmt * next + | hir::ExprKind::Err(_) = next.kind + // prev_stmt + next + { + true + } else { + false + } + } + /// A possible error is to forget to add a return type that is needed: /// /// ```compile_fail,E0308 diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 7dc736e5e6b..f735c0a4160 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -605,7 +605,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }, PatKind::Struct(_, fields, has_rest_pat) => match opt_path_res.unwrap() { Ok(ResolvedPat { ty, kind: ResolvedPatKind::Struct { variant } }) => self - .check_pat_struct(pat, fields, has_rest_pat, ty, variant, expected, pat_info), + .check_pat_struct( + pat, + fields, + has_rest_pat.is_some(), + ty, + variant, + expected, + pat_info, + ), Err(guar) => { let ty_err = Ty::new_error(self.tcx, guar); for field in fields { @@ -2428,7 +2436,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let len = unmentioned_fields.len(); let (prefix, postfix, sp) = match fields { [] => match &pat.kind { - PatKind::Struct(path, [], false) => { + PatKind::Struct(path, [], None) => { (" { ", " }", path.span().shrink_to_hi().until(pat.span.shrink_to_hi())) } _ => return err, diff --git a/compiler/rustc_incremental/Cargo.toml b/compiler/rustc_incremental/Cargo.toml index 8d7f2eb8414..db0a5841887 100644 --- a/compiler/rustc_incremental/Cargo.toml +++ b/compiler/rustc_incremental/Cargo.toml @@ -19,6 +19,6 @@ rustc_middle = { path = "../rustc_middle" } rustc_serialize = { path = "../rustc_serialize" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } -thin-vec.workspace = true -tracing.workspace = true +thin-vec = "0.2.12" +tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_index/src/interval.rs b/compiler/rustc_index/src/interval.rs index 0225c5c4f32..dda5253e7c5 100644 --- a/compiler/rustc_index/src/interval.rs +++ b/compiler/rustc_index/src/interval.rs @@ -140,6 +140,30 @@ impl<I: Idx> IntervalSet<I> { result } + /// Specialized version of `insert` when we know that the inserted point is *after* any + /// contained. + pub fn append(&mut self, point: I) { + let point = point.index() as u32; + + if let Some((_, last_end)) = self.map.last_mut() { + assert!(*last_end <= point); + if point == *last_end { + // The point is already in the set. + } else if point == *last_end + 1 { + *last_end = point; + } else { + self.map.push((point, point)); + } + } else { + self.map.push((point, point)); + } + + debug_assert!( + self.check_invariants(), + "wrong intervals after append {point:?} to {self:?}" + ); + } + pub fn contains(&self, needle: I) -> bool { let needle = needle.index() as u32; let Some(last) = self.map.partition_point(|r| r.0 <= needle).checked_sub(1) else { @@ -176,6 +200,32 @@ impl<I: Idx> IntervalSet<I> { }) } + pub fn disjoint(&self, other: &IntervalSet<I>) -> bool + where + I: Step, + { + let helper = move || { + let mut self_iter = self.iter_intervals(); + let mut other_iter = other.iter_intervals(); + + let mut self_current = self_iter.next()?; + let mut other_current = other_iter.next()?; + + loop { + if self_current.end <= other_current.start { + self_current = self_iter.next()?; + continue; + } + if other_current.end <= self_current.start { + other_current = other_iter.next()?; + continue; + } + return Some(false); + } + }; + helper().unwrap_or(true) + } + pub fn is_empty(&self) -> bool { self.map.is_empty() } @@ -325,6 +375,10 @@ impl<R: Idx, C: Step + Idx> SparseIntervalMatrix<R, C> { self.ensure_row(row).insert(point) } + pub fn append(&mut self, row: R, point: C) { + self.ensure_row(row).append(point) + } + pub fn contains(&self, row: R, point: C) -> bool { self.row(row).is_some_and(|r| r.contains(point)) } diff --git a/compiler/rustc_index_macros/Cargo.toml b/compiler/rustc_index_macros/Cargo.toml index 8593bde2615..34f3109a526 100644 --- a/compiler/rustc_index_macros/Cargo.toml +++ b/compiler/rustc_index_macros/Cargo.toml @@ -8,8 +8,8 @@ proc-macro = true [dependencies] # tidy-alphabetical-start -proc-macro2.workspace = true -quote.workspace = true +proc-macro2 = "1" +quote = "1" syn = { version = "2.0.9", features = ["full", "extra-traits"] } # tidy-alphabetical-end diff --git a/compiler/rustc_infer/Cargo.toml b/compiler/rustc_infer/Cargo.toml index 6d97fa6af1f..08c03614884 100644 --- a/compiler/rustc_infer/Cargo.toml +++ b/compiler/rustc_infer/Cargo.toml @@ -18,6 +18,6 @@ rustc_middle = { path = "../rustc_middle" } rustc_span = { path = "../rustc_span" } rustc_type_ir = { path = "../rustc_type_ir" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } -thin-vec.workspace = true -tracing.workspace = true +thin-vec = "0.2.12" +tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs index bb9c8850093..8265fccabc9 100644 --- a/compiler/rustc_infer/src/infer/context.rs +++ b/compiler/rustc_infer/src/infer/context.rs @@ -1,4 +1,4 @@ -///! Definition of `InferCtxtLike` from the librarified type layer. +//! Definition of `InferCtxtLike` from the librarified type layer. use rustc_hir::def_id::DefId; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::relate::RelateResult; diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml index 40152e78f8a..473ac5e0cea 100644 --- a/compiler/rustc_interface/Cargo.toml +++ b/compiler/rustc_interface/Cargo.toml @@ -46,7 +46,7 @@ rustc_thread_pool = { path = "../rustc_thread_pool" } rustc_trait_selection = { path = "../rustc_trait_selection" } rustc_traits = { path = "../rustc_traits" } rustc_ty_utils = { path = "../rustc_ty_utils" } -tracing.workspace = true +tracing = "0.1" # tidy-alphabetical-end [dev-dependencies] diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 8f131f45bbd..4c820b8877b 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -13,7 +13,7 @@ use rustc_lint::LintStore; use rustc_middle::ty; use rustc_middle::ty::CurrentGcx; use rustc_middle::util::Providers; -use rustc_parse::new_parser_from_source_str; +use rustc_parse::new_parser_from_simple_source_str; use rustc_parse::parser::attr::AllowLeadingUnsafe; use rustc_query_impl::QueryCtxt; use rustc_query_system::query::print_query_stack; @@ -68,7 +68,7 @@ pub(crate) fn parse_cfg(dcx: DiagCtxtHandle<'_>, cfgs: Vec<String>) -> Cfg { }; } - match new_parser_from_source_str(&psess, filename, s.to_string()) { + match new_parser_from_simple_source_str(&psess, filename, s.to_string()) { Ok(mut parser) => match parser.parse_meta_item(AllowLeadingUnsafe::No) { Ok(meta_item) if parser.token == token::Eof => { if meta_item.path.segments.len() != 1 { @@ -166,7 +166,7 @@ pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec<String>) -> Ch error!("expected `cfg(name, values(\"value1\", \"value2\", ... \"valueN\"))`") }; - let mut parser = match new_parser_from_source_str(&psess, filename, s.to_string()) { + let mut parser = match new_parser_from_simple_source_str(&psess, filename, s.to_string()) { Ok(parser) => parser, Err(errs) => { errs.into_iter().for_each(|err| err.cancel()); diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index bc5ef04079e..ca8c10311fb 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -41,7 +41,7 @@ use rustc_span::{ Symbol, sym, }; use rustc_target::spec::PanicStrategy; -use rustc_trait_selection::traits; +use rustc_trait_selection::{solve, traits}; use tracing::{info, instrument}; use crate::interface::Compiler; @@ -895,6 +895,7 @@ pub static DEFAULT_QUERY_PROVIDERS: LazyLock<Providers> = LazyLock::new(|| { rustc_hir_typeck::provide(providers); ty::provide(providers); traits::provide(providers); + solve::provide(providers); rustc_passes::provide(providers); rustc_traits::provide(providers); rustc_ty_utils::provide(providers); diff --git a/compiler/rustc_lexer/Cargo.toml b/compiler/rustc_lexer/Cargo.toml index e0019fb1821..343b81bd171 100644 --- a/compiler/rustc_lexer/Cargo.toml +++ b/compiler/rustc_lexer/Cargo.toml @@ -14,7 +14,7 @@ Rust lexer used by rustc. No stability guarantees are provided. # Note that this crate purposefully does not depend on other rustc crates [dependencies] -memchr.workspace = true +memchr = "2.7.4" unicode-properties = { version = "0.1.0", default-features = false, features = ["emoji"] } unicode-xid = "0.2.0" diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs index d10b1920343..c29ab569f47 100644 --- a/compiler/rustc_lexer/src/lib.rs +++ b/compiler/rustc_lexer/src/lib.rs @@ -331,24 +331,37 @@ pub fn is_whitespace(c: char) -> bool { matches!( c, - // Usual ASCII suspects - '\u{0009}' // \t - | '\u{000A}' // \n + // End-of-line characters + | '\u{000A}' // line feed (\n) | '\u{000B}' // vertical tab | '\u{000C}' // form feed - | '\u{000D}' // \r - | '\u{0020}' // space - - // NEXT LINE from latin1 - | '\u{0085}' + | '\u{000D}' // carriage return (\r) + | '\u{0085}' // next line (from latin1) + | '\u{2028}' // LINE SEPARATOR + | '\u{2029}' // PARAGRAPH SEPARATOR - // Bidi markers + // `Default_Ignorable_Code_Point` characters | '\u{200E}' // LEFT-TO-RIGHT MARK | '\u{200F}' // RIGHT-TO-LEFT MARK - // Dedicated whitespace characters from Unicode - | '\u{2028}' // LINE SEPARATOR - | '\u{2029}' // PARAGRAPH SEPARATOR + // Horizontal space characters + | '\u{0009}' // tab (\t) + | '\u{0020}' // space + ) +} + +/// True if `c` is considered horizontal whitespace according to Rust language definition. +pub fn is_horizontal_whitespace(c: char) -> bool { + // This is Pattern_White_Space. + // + // Note that this set is stable (ie, it doesn't change with different + // Unicode versions), so it's ok to just hard-code the values. + + matches!( + c, + // Horizontal space characters + '\u{0009}' // tab (\t) + | '\u{0020}' // space ) } @@ -538,7 +551,7 @@ impl Cursor<'_> { debug_assert!(length_opening >= 3); // whitespace between the opening and the infostring. - self.eat_while(|ch| ch != '\n' && is_whitespace(ch)); + self.eat_while(|ch| ch != '\n' && is_horizontal_whitespace(ch)); // copied from `eat_identifier`, but allows `-` and `.` in infostring to allow something like // `---Cargo.toml` as a valid opener @@ -547,7 +560,7 @@ impl Cursor<'_> { self.eat_while(|c| is_id_continue(c) || c == '-' || c == '.'); } - self.eat_while(|ch| ch != '\n' && is_whitespace(ch)); + self.eat_while(|ch| ch != '\n' && is_horizontal_whitespace(ch)); let invalid_infostring = self.first() != '\n'; let mut found = false; @@ -588,7 +601,7 @@ impl Cursor<'_> { // on a standalone line. Might be wrong. while let Some(closing) = rest.find("---") { let preceding_chars_start = rest[..closing].rfind("\n").map_or(0, |i| i + 1); - if rest[preceding_chars_start..closing].chars().all(is_whitespace) { + if rest[preceding_chars_start..closing].chars().all(is_horizontal_whitespace) { // candidate found potential_closing = Some(closing); break; diff --git a/compiler/rustc_lint/Cargo.toml b/compiler/rustc_lint/Cargo.toml index 7900e4b9ab2..3a50aac50cb 100644 --- a/compiler/rustc_lint/Cargo.toml +++ b/compiler/rustc_lint/Cargo.toml @@ -5,6 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start +bitflags = "2.4.1" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } @@ -24,6 +25,6 @@ rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } -tracing.workspace = true +tracing = "0.1" unicode-security = "0.1.0" # tidy-alphabetical-end diff --git a/compiler/rustc_lint/src/foreign_modules.rs b/compiler/rustc_lint/src/foreign_modules.rs index 3267e70f1de..ad73e15e31f 100644 --- a/compiler/rustc_lint/src/foreign_modules.rs +++ b/compiler/rustc_lint/src/foreign_modules.rs @@ -136,7 +136,6 @@ impl ClashingExternDeclarations { ty::TypingEnv::non_body_analysis(tcx, this_fi.owner_id), existing_decl_ty, this_decl_ty, - types::CItemKind::Declaration, ) { let orig = name_of_extern_decl(tcx, existing_did); @@ -214,10 +213,9 @@ fn structurally_same_type<'tcx>( typing_env: ty::TypingEnv<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>, - ckind: types::CItemKind, ) -> bool { let mut seen_types = UnordSet::default(); - let result = structurally_same_type_impl(&mut seen_types, tcx, typing_env, a, b, ckind); + let result = structurally_same_type_impl(&mut seen_types, tcx, typing_env, a, b); if cfg!(debug_assertions) && result { // Sanity-check: must have same ABI, size and alignment. // `extern` blocks cannot be generic, so we'll always get a layout here. @@ -236,7 +234,6 @@ fn structurally_same_type_impl<'tcx>( typing_env: ty::TypingEnv<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>, - ckind: types::CItemKind, ) -> bool { debug!("structurally_same_type_impl(tcx, a = {:?}, b = {:?})", a, b); @@ -307,7 +304,6 @@ fn structurally_same_type_impl<'tcx>( typing_env, tcx.type_of(a_did).instantiate(tcx, a_gen_args), tcx.type_of(b_did).instantiate(tcx, b_gen_args), - ckind, ) }, ) @@ -315,25 +311,19 @@ fn structurally_same_type_impl<'tcx>( (ty::Array(a_ty, a_len), ty::Array(b_ty, b_len)) => { // For arrays, we also check the length. a_len == b_len - && structurally_same_type_impl( - seen_types, tcx, typing_env, *a_ty, *b_ty, ckind, - ) + && structurally_same_type_impl(seen_types, tcx, typing_env, *a_ty, *b_ty) } (ty::Slice(a_ty), ty::Slice(b_ty)) => { - structurally_same_type_impl(seen_types, tcx, typing_env, *a_ty, *b_ty, ckind) + structurally_same_type_impl(seen_types, tcx, typing_env, *a_ty, *b_ty) } (ty::RawPtr(a_ty, a_mutbl), ty::RawPtr(b_ty, b_mutbl)) => { a_mutbl == b_mutbl - && structurally_same_type_impl( - seen_types, tcx, typing_env, *a_ty, *b_ty, ckind, - ) + && structurally_same_type_impl(seen_types, tcx, typing_env, *a_ty, *b_ty) } (ty::Ref(_a_region, a_ty, a_mut), ty::Ref(_b_region, b_ty, b_mut)) => { // For structural sameness, we don't need the region to be same. a_mut == b_mut - && structurally_same_type_impl( - seen_types, tcx, typing_env, *a_ty, *b_ty, ckind, - ) + && structurally_same_type_impl(seen_types, tcx, typing_env, *a_ty, *b_ty) } (ty::FnDef(..), ty::FnDef(..)) => { let a_poly_sig = a.fn_sig(tcx); @@ -347,7 +337,7 @@ fn structurally_same_type_impl<'tcx>( (a_sig.abi, a_sig.safety, a_sig.c_variadic) == (b_sig.abi, b_sig.safety, b_sig.c_variadic) && a_sig.inputs().iter().eq_by(b_sig.inputs().iter(), |a, b| { - structurally_same_type_impl(seen_types, tcx, typing_env, *a, *b, ckind) + structurally_same_type_impl(seen_types, tcx, typing_env, *a, *b) }) && structurally_same_type_impl( seen_types, @@ -355,7 +345,6 @@ fn structurally_same_type_impl<'tcx>( typing_env, a_sig.output(), b_sig.output(), - ckind, ) } (ty::Tuple(..), ty::Tuple(..)) => { @@ -383,14 +372,14 @@ fn structurally_same_type_impl<'tcx>( // An Adt and a primitive or pointer type. This can be FFI-safe if non-null // enum layout optimisation is being applied. (ty::Adt(..) | ty::Pat(..), _) if is_primitive_or_pointer(b) => { - if let Some(a_inner) = types::repr_nullable_ptr(tcx, typing_env, a, ckind) { + if let Some(a_inner) = types::repr_nullable_ptr(tcx, typing_env, a) { a_inner == b } else { false } } (_, ty::Adt(..) | ty::Pat(..)) if is_primitive_or_pointer(a) => { - if let Some(b_inner) = types::repr_nullable_ptr(tcx, typing_env, b, ckind) { + if let Some(b_inner) = types::repr_nullable_ptr(tcx, typing_env, b) { b_inner == a } else { false diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index bdbac7fc4d1..9bb53fea54a 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -194,8 +194,7 @@ late_lint_methods!( DefaultCouldBeDerived: DefaultCouldBeDerived::default(), DerefIntoDynSupertrait: DerefIntoDynSupertrait, DropForgetUseless: DropForgetUseless, - ImproperCTypesDeclarations: ImproperCTypesDeclarations, - ImproperCTypesDefinitions: ImproperCTypesDefinitions, + ImproperCTypesLint: ImproperCTypesLint, InvalidFromUtf8: InvalidFromUtf8, VariantSizeDifferences: VariantSizeDifferences, PathStatements: PathStatements, diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index f8a692313f0..a72b802eb5d 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -1,35 +1,28 @@ use std::iter; -use std::ops::ControlFlow; -use rustc_abi::{BackendRepr, TagEncoding, VariantIdx, Variants, WrappingRange}; -use rustc_data_structures::fx::FxHashSet; -use rustc_errors::DiagMessage; -use rustc_hir::intravisit::VisitorExt; -use rustc_hir::{AmbigArg, Expr, ExprKind, HirId, LangItem}; +use rustc_abi::{BackendRepr, TagEncoding, Variants, WrappingRange}; +use rustc_hir::{Expr, ExprKind, HirId, LangItem}; use rustc_middle::bug; use rustc_middle::ty::layout::{LayoutOf, SizeSkeleton}; -use rustc_middle::ty::{ - self, Adt, AdtKind, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, - TypeVisitableExt, -}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass}; -use rustc_span::def_id::LocalDefId; use rustc_span::{Span, Symbol, sym}; use tracing::debug; use {rustc_ast as ast, rustc_hir as hir}; -mod improper_ctypes; +mod improper_ctypes; // these filed do the implementation for ImproperCTypesDefinitions,ImproperCTypesDeclarations +pub(crate) use improper_ctypes::ImproperCTypesLint; use crate::lints::{ AmbiguousWidePointerComparisons, AmbiguousWidePointerComparisonsAddrMetadataSuggestion, AmbiguousWidePointerComparisonsAddrSuggestion, AmbiguousWidePointerComparisonsCastSuggestion, AmbiguousWidePointerComparisonsExpectSuggestion, AtomicOrderingFence, AtomicOrderingLoad, - AtomicOrderingStore, ImproperCTypes, InvalidAtomicOrderingDiag, InvalidNanComparisons, + AtomicOrderingStore, InvalidAtomicOrderingDiag, InvalidNanComparisons, InvalidNanComparisonsSuggestion, UnpredictableFunctionPointerComparisons, - UnpredictableFunctionPointerComparisonsSuggestion, UnusedComparisons, UsesPowerAlignment, + UnpredictableFunctionPointerComparisonsSuggestion, UnusedComparisons, VariantSizeDifferencesDiag, }; -use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent}; +use crate::{LateContext, LateLintPass, LintContext}; mod literal; @@ -690,144 +683,6 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { } } -declare_lint! { - /// The `improper_ctypes` lint detects incorrect use of types in foreign - /// modules. - /// - /// ### Example - /// - /// ```rust - /// unsafe extern "C" { - /// static STATIC: String; - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// The compiler has several checks to verify that types used in `extern` - /// blocks are safe and follow certain rules to ensure proper - /// compatibility with the foreign interfaces. This lint is issued when it - /// detects a probable mistake in a definition. The lint usually should - /// provide a description of the issue, along with possibly a hint on how - /// to resolve it. - IMPROPER_CTYPES, - Warn, - "proper use of libc types in foreign modules" -} - -declare_lint_pass!(ImproperCTypesDeclarations => [IMPROPER_CTYPES]); - -declare_lint! { - /// The `improper_ctypes_definitions` lint detects incorrect use of - /// [`extern` function] definitions. - /// - /// [`extern` function]: https://doc.rust-lang.org/reference/items/functions.html#extern-function-qualifier - /// - /// ### Example - /// - /// ```rust - /// # #![allow(unused)] - /// pub extern "C" fn str_type(p: &str) { } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// There are many parameter and return types that may be specified in an - /// `extern` function that are not compatible with the given ABI. This - /// lint is an alert that these types should not be used. The lint usually - /// should provide a description of the issue, along with possibly a hint - /// on how to resolve it. - IMPROPER_CTYPES_DEFINITIONS, - Warn, - "proper use of libc types in foreign item definitions" -} - -declare_lint! { - /// The `uses_power_alignment` lint detects specific `repr(C)` - /// aggregates on AIX. - /// In its platform C ABI, AIX uses the "power" (as in PowerPC) alignment - /// rule (detailed in https://www.ibm.com/docs/en/xl-c-and-cpp-aix/16.1?topic=data-using-alignment-modes#alignment), - /// which can also be set for XLC by `#pragma align(power)` or - /// `-qalign=power`. Aggregates with a floating-point type as the - /// recursively first field (as in "at offset 0") modify the layout of - /// *subsequent* fields of the associated structs to use an alignment value - /// where the floating-point type is aligned on a 4-byte boundary. - /// - /// Effectively, subsequent floating-point fields act as-if they are `repr(packed(4))`. This - /// would be unsound to do in a `repr(C)` type without all the restrictions that come with - /// `repr(packed)`. Rust instead chooses a layout that maintains soundness of Rust code, at the - /// expense of incompatibility with C code. - /// - /// ### Example - /// - /// ```rust,ignore (fails on non-powerpc64-ibm-aix) - /// #[repr(C)] - /// pub struct Floats { - /// a: f64, - /// b: u8, - /// c: f64, - /// } - /// ``` - /// - /// This will produce: - /// - /// ```text - /// warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type - /// --> <source>:5:3 - /// | - /// 5 | c: f64, - /// | ^^^^^^ - /// | - /// = note: `#[warn(uses_power_alignment)]` on by default - /// ``` - /// - /// ### Explanation - /// - /// The power alignment rule specifies that the above struct has the - /// following alignment: - /// - offset_of!(Floats, a) == 0 - /// - offset_of!(Floats, b) == 8 - /// - offset_of!(Floats, c) == 12 - /// - /// However, Rust currently aligns `c` at `offset_of!(Floats, c) == 16`. - /// Using offset 12 would be unsound since `f64` generally must be 8-aligned on this target. - /// Thus, a warning is produced for the above struct. - USES_POWER_ALIGNMENT, - Warn, - "Structs do not follow the power alignment rule under repr(C)" -} - -declare_lint_pass!(ImproperCTypesDefinitions => [IMPROPER_CTYPES_DEFINITIONS, USES_POWER_ALIGNMENT]); - -#[derive(Clone, Copy)] -pub(crate) enum CItemKind { - Declaration, - Definition, -} - -struct ImproperCTypesVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - mode: CItemKind, -} - -/// Accumulator for recursive ffi type checking -struct CTypesVisitorState<'tcx> { - cache: FxHashSet<Ty<'tcx>>, - /// The original type being checked, before we recursed - /// to any other types it contains. - base_ty: Ty<'tcx>, -} - -enum FfiResult<'tcx> { - FfiSafe, - FfiPhantom(Ty<'tcx>), - FfiUnsafe { ty: Ty<'tcx>, reason: DiagMessage, help: Option<DiagMessage> }, -} - pub(crate) fn nonnull_optimization_guaranteed<'tcx>( tcx: TyCtxt<'tcx>, def: ty::AdtDef<'tcx>, @@ -855,14 +710,13 @@ fn ty_is_known_nonnull<'tcx>( tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, ty: Ty<'tcx>, - mode: CItemKind, ) -> bool { let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty); match ty.kind() { ty::FnPtr(..) => true, ty::Ref(..) => true, - ty::Adt(def, _) if def.is_box() && matches!(mode, CItemKind::Definition) => true, + ty::Adt(def, _) if def.is_box() => true, ty::Adt(def, args) if def.repr().transparent() && !def.is_union() => { let marked_non_null = nonnull_optimization_guaranteed(tcx, *def); @@ -878,10 +732,10 @@ fn ty_is_known_nonnull<'tcx>( def.variants() .iter() .filter_map(|variant| transparent_newtype_field(tcx, variant)) - .any(|field| ty_is_known_nonnull(tcx, typing_env, field.ty(tcx, args), mode)) + .any(|field| ty_is_known_nonnull(tcx, typing_env, field.ty(tcx, args))) } ty::Pat(base, pat) => { - ty_is_known_nonnull(tcx, typing_env, *base, mode) + ty_is_known_nonnull(tcx, typing_env, *base) || pat_ty_is_known_nonnull(tcx, typing_env, *pat) } _ => false, @@ -992,7 +846,6 @@ pub(crate) fn repr_nullable_ptr<'tcx>( tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, ty: Ty<'tcx>, - ckind: CItemKind, ) -> Option<Ty<'tcx>> { debug!("is_repr_nullable_ptr(tcx, ty = {:?})", ty); match ty.kind() { @@ -1017,7 +870,7 @@ pub(crate) fn repr_nullable_ptr<'tcx>( _ => return None, }; - if !ty_is_known_nonnull(tcx, typing_env, field_ty, ckind) { + if !ty_is_known_nonnull(tcx, typing_env, field_ty) { return None; } @@ -1076,710 +929,6 @@ fn get_nullable_type_from_pat<'tcx>( } } -impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { - /// Check if the type is array and emit an unsafe type lint. - fn check_for_array_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool { - if let ty::Array(..) = ty.kind() { - self.emit_ffi_unsafe_type_lint( - ty, - sp, - fluent::lint_improper_ctypes_array_reason, - Some(fluent::lint_improper_ctypes_array_help), - ); - true - } else { - false - } - } - - /// Checks if the given field's type is "ffi-safe". - fn check_field_type_for_ffi( - &self, - acc: &mut CTypesVisitorState<'tcx>, - field: &ty::FieldDef, - args: GenericArgsRef<'tcx>, - ) -> FfiResult<'tcx> { - let field_ty = field.ty(self.cx.tcx, args); - let field_ty = self - .cx - .tcx - .try_normalize_erasing_regions(self.cx.typing_env(), field_ty) - .unwrap_or(field_ty); - self.check_type_for_ffi(acc, field_ty) - } - - /// Checks if the given `VariantDef`'s field types are "ffi-safe". - fn check_variant_for_ffi( - &self, - acc: &mut CTypesVisitorState<'tcx>, - ty: Ty<'tcx>, - def: ty::AdtDef<'tcx>, - variant: &ty::VariantDef, - args: GenericArgsRef<'tcx>, - ) -> FfiResult<'tcx> { - use FfiResult::*; - let transparent_with_all_zst_fields = if def.repr().transparent() { - if let Some(field) = transparent_newtype_field(self.cx.tcx, variant) { - // Transparent newtypes have at most one non-ZST field which needs to be checked.. - match self.check_field_type_for_ffi(acc, field, args) { - FfiUnsafe { ty, .. } if ty.is_unit() => (), - r => return r, - } - - false - } else { - // ..or have only ZST fields, which is FFI-unsafe (unless those fields are all - // `PhantomData`). - true - } - } else { - false - }; - - // We can't completely trust `repr(C)` markings, so make sure the fields are actually safe. - let mut all_phantom = !variant.fields.is_empty(); - for field in &variant.fields { - all_phantom &= match self.check_field_type_for_ffi(acc, field, args) { - FfiSafe => false, - // `()` fields are FFI-safe! - FfiUnsafe { ty, .. } if ty.is_unit() => false, - FfiPhantom(..) => true, - r @ FfiUnsafe { .. } => return r, - } - } - - if all_phantom { - FfiPhantom(ty) - } else if transparent_with_all_zst_fields { - FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_struct_zst, help: None } - } else { - FfiSafe - } - } - - /// Checks if the given type is "ffi-safe" (has a stable, well-defined - /// representation which can be exported to C code). - fn check_type_for_ffi( - &self, - acc: &mut CTypesVisitorState<'tcx>, - ty: Ty<'tcx>, - ) -> FfiResult<'tcx> { - use FfiResult::*; - - let tcx = self.cx.tcx; - - // Protect against infinite recursion, for example - // `struct S(*mut S);`. - // FIXME: A recursion limit is necessary as well, for irregular - // recursive types. - if !acc.cache.insert(ty) { - return FfiSafe; - } - - match *ty.kind() { - ty::Adt(def, args) => { - if let Some(boxed) = ty.boxed_ty() - && matches!(self.mode, CItemKind::Definition) - { - if boxed.is_sized(tcx, self.cx.typing_env()) { - return FfiSafe; - } else { - return FfiUnsafe { - ty, - reason: fluent::lint_improper_ctypes_box, - help: None, - }; - } - } - if def.is_phantom_data() { - return FfiPhantom(ty); - } - match def.adt_kind() { - AdtKind::Struct | AdtKind::Union => { - if let Some(sym::cstring_type | sym::cstr_type) = - tcx.get_diagnostic_name(def.did()) - && !acc.base_ty.is_mutable_ptr() - { - return FfiUnsafe { - ty, - reason: fluent::lint_improper_ctypes_cstr_reason, - help: Some(fluent::lint_improper_ctypes_cstr_help), - }; - } - - if !def.repr().c() && !def.repr().transparent() { - return FfiUnsafe { - ty, - reason: if def.is_struct() { - fluent::lint_improper_ctypes_struct_layout_reason - } else { - fluent::lint_improper_ctypes_union_layout_reason - }, - help: if def.is_struct() { - Some(fluent::lint_improper_ctypes_struct_layout_help) - } else { - Some(fluent::lint_improper_ctypes_union_layout_help) - }, - }; - } - - if def.non_enum_variant().field_list_has_applicable_non_exhaustive() { - return FfiUnsafe { - ty, - reason: if def.is_struct() { - fluent::lint_improper_ctypes_struct_non_exhaustive - } else { - fluent::lint_improper_ctypes_union_non_exhaustive - }, - help: None, - }; - } - - if def.non_enum_variant().fields.is_empty() { - return FfiUnsafe { - ty, - reason: if def.is_struct() { - fluent::lint_improper_ctypes_struct_fieldless_reason - } else { - fluent::lint_improper_ctypes_union_fieldless_reason - }, - help: if def.is_struct() { - Some(fluent::lint_improper_ctypes_struct_fieldless_help) - } else { - Some(fluent::lint_improper_ctypes_union_fieldless_help) - }, - }; - } - - self.check_variant_for_ffi(acc, ty, def, def.non_enum_variant(), args) - } - AdtKind::Enum => { - if def.variants().is_empty() { - // Empty enums are okay... although sort of useless. - return FfiSafe; - } - // Check for a repr() attribute to specify the size of the - // discriminant. - if !def.repr().c() && !def.repr().transparent() && def.repr().int.is_none() - { - // Special-case types like `Option<extern fn()>` and `Result<extern fn(), ()>` - if let Some(ty) = - repr_nullable_ptr(self.cx.tcx, self.cx.typing_env(), ty, self.mode) - { - return self.check_type_for_ffi(acc, ty); - } - - return FfiUnsafe { - ty, - reason: fluent::lint_improper_ctypes_enum_repr_reason, - help: Some(fluent::lint_improper_ctypes_enum_repr_help), - }; - } - - use improper_ctypes::check_non_exhaustive_variant; - - let non_exhaustive = def.variant_list_has_applicable_non_exhaustive(); - // Check the contained variants. - let ret = def.variants().iter().try_for_each(|variant| { - check_non_exhaustive_variant(non_exhaustive, variant) - .map_break(|reason| FfiUnsafe { ty, reason, help: None })?; - - match self.check_variant_for_ffi(acc, ty, def, variant, args) { - FfiSafe => ControlFlow::Continue(()), - r => ControlFlow::Break(r), - } - }); - if let ControlFlow::Break(result) = ret { - return result; - } - - FfiSafe - } - } - } - - ty::Char => FfiUnsafe { - ty, - reason: fluent::lint_improper_ctypes_char_reason, - help: Some(fluent::lint_improper_ctypes_char_help), - }, - - // It's just extra invariants on the type that you need to uphold, - // but only the base type is relevant for being representable in FFI. - ty::Pat(base, ..) => self.check_type_for_ffi(acc, base), - - // Primitive types with a stable representation. - ty::Bool | ty::Int(..) | ty::Uint(..) | ty::Float(..) | ty::Never => FfiSafe, - - ty::Slice(_) => FfiUnsafe { - ty, - reason: fluent::lint_improper_ctypes_slice_reason, - help: Some(fluent::lint_improper_ctypes_slice_help), - }, - - ty::Dynamic(..) => { - FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_dyn, help: None } - } - - ty::Str => FfiUnsafe { - ty, - reason: fluent::lint_improper_ctypes_str_reason, - help: Some(fluent::lint_improper_ctypes_str_help), - }, - - ty::Tuple(..) => FfiUnsafe { - ty, - reason: fluent::lint_improper_ctypes_tuple_reason, - help: Some(fluent::lint_improper_ctypes_tuple_help), - }, - - ty::RawPtr(ty, _) | ty::Ref(_, ty, _) - if { - matches!(self.mode, CItemKind::Definition) - && ty.is_sized(self.cx.tcx, self.cx.typing_env()) - } => - { - FfiSafe - } - - ty::RawPtr(ty, _) - if match ty.kind() { - ty::Tuple(tuple) => tuple.is_empty(), - _ => false, - } => - { - FfiSafe - } - - ty::RawPtr(ty, _) | ty::Ref(_, ty, _) => self.check_type_for_ffi(acc, ty), - - ty::Array(inner_ty, _) => self.check_type_for_ffi(acc, inner_ty), - - ty::FnPtr(sig_tys, hdr) => { - let sig = sig_tys.with(hdr); - if sig.abi().is_rustic_abi() { - return FfiUnsafe { - ty, - reason: fluent::lint_improper_ctypes_fnptr_reason, - help: Some(fluent::lint_improper_ctypes_fnptr_help), - }; - } - - let sig = tcx.instantiate_bound_regions_with_erased(sig); - for arg in sig.inputs() { - match self.check_type_for_ffi(acc, *arg) { - FfiSafe => {} - r => return r, - } - } - - let ret_ty = sig.output(); - if ret_ty.is_unit() { - return FfiSafe; - } - - self.check_type_for_ffi(acc, ret_ty) - } - - ty::Foreign(..) => FfiSafe, - - // While opaque types are checked for earlier, if a projection in a struct field - // normalizes to an opaque type, then it will reach this branch. - ty::Alias(ty::Opaque, ..) => { - FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_opaque, help: None } - } - - // `extern "C" fn` functions can have type parameters, which may or may not be FFI-safe, - // so they are currently ignored for the purposes of this lint. - ty::Param(..) | ty::Alias(ty::Projection | ty::Inherent, ..) - if matches!(self.mode, CItemKind::Definition) => - { - FfiSafe - } - - ty::UnsafeBinder(_) => todo!("FIXME(unsafe_binder)"), - - ty::Param(..) - | ty::Alias(ty::Projection | ty::Inherent | ty::Free, ..) - | ty::Infer(..) - | ty::Bound(..) - | ty::Error(_) - | ty::Closure(..) - | ty::CoroutineClosure(..) - | ty::Coroutine(..) - | ty::CoroutineWitness(..) - | ty::Placeholder(..) - | ty::FnDef(..) => bug!("unexpected type in foreign function: {:?}", ty), - } - } - - fn emit_ffi_unsafe_type_lint( - &mut self, - ty: Ty<'tcx>, - sp: Span, - note: DiagMessage, - help: Option<DiagMessage>, - ) { - let lint = match self.mode { - CItemKind::Declaration => IMPROPER_CTYPES, - CItemKind::Definition => IMPROPER_CTYPES_DEFINITIONS, - }; - let desc = match self.mode { - CItemKind::Declaration => "block", - CItemKind::Definition => "fn", - }; - let span_note = if let ty::Adt(def, _) = ty.kind() - && let Some(sp) = self.cx.tcx.hir_span_if_local(def.did()) - { - Some(sp) - } else { - None - }; - self.cx.emit_span_lint( - lint, - sp, - ImproperCTypes { ty, desc, label: sp, help, note, span_note }, - ); - } - - fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool { - struct ProhibitOpaqueTypes; - impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for ProhibitOpaqueTypes { - type Result = ControlFlow<Ty<'tcx>>; - - fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { - if !ty.has_opaque_types() { - return ControlFlow::Continue(()); - } - - if let ty::Alias(ty::Opaque, ..) = ty.kind() { - ControlFlow::Break(ty) - } else { - ty.super_visit_with(self) - } - } - } - - if let Some(ty) = self - .cx - .tcx - .try_normalize_erasing_regions(self.cx.typing_env(), ty) - .unwrap_or(ty) - .visit_with(&mut ProhibitOpaqueTypes) - .break_value() - { - self.emit_ffi_unsafe_type_lint(ty, sp, fluent::lint_improper_ctypes_opaque, None); - true - } else { - false - } - } - - fn check_type_for_ffi_and_report_errors( - &mut self, - sp: Span, - ty: Ty<'tcx>, - is_static: bool, - is_return_type: bool, - ) { - if self.check_for_opaque_ty(sp, ty) { - // We've already emitted an error due to an opaque type. - return; - } - - let ty = self.cx.tcx.try_normalize_erasing_regions(self.cx.typing_env(), ty).unwrap_or(ty); - - // C doesn't really support passing arrays by value - the only way to pass an array by value - // is through a struct. So, first test that the top level isn't an array, and then - // recursively check the types inside. - if !is_static && self.check_for_array_ty(sp, ty) { - return; - } - - // Don't report FFI errors for unit return types. This check exists here, and not in - // the caller (where it would make more sense) so that normalization has definitely - // happened. - if is_return_type && ty.is_unit() { - return; - } - - let mut acc = CTypesVisitorState { cache: FxHashSet::default(), base_ty: ty }; - match self.check_type_for_ffi(&mut acc, ty) { - FfiResult::FfiSafe => {} - FfiResult::FfiPhantom(ty) => { - self.emit_ffi_unsafe_type_lint( - ty, - sp, - fluent::lint_improper_ctypes_only_phantomdata, - None, - ); - } - FfiResult::FfiUnsafe { ty, reason, help } => { - self.emit_ffi_unsafe_type_lint(ty, sp, reason, help); - } - } - } - - /// Check if a function's argument types and result type are "ffi-safe". - /// - /// For a external ABI function, argument types and the result type are walked to find fn-ptr - /// types that have external ABIs, as these still need checked. - fn check_fn(&mut self, def_id: LocalDefId, decl: &'tcx hir::FnDecl<'_>) { - let sig = self.cx.tcx.fn_sig(def_id).instantiate_identity(); - let sig = self.cx.tcx.instantiate_bound_regions_with_erased(sig); - - for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) { - for (fn_ptr_ty, span) in self.find_fn_ptr_ty_with_external_abi(input_hir, *input_ty) { - self.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, false, false); - } - } - - if let hir::FnRetTy::Return(ret_hir) = decl.output { - for (fn_ptr_ty, span) in self.find_fn_ptr_ty_with_external_abi(ret_hir, sig.output()) { - self.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, false, true); - } - } - } - - /// Check if a function's argument types and result type are "ffi-safe". - fn check_foreign_fn(&mut self, def_id: LocalDefId, decl: &'tcx hir::FnDecl<'_>) { - let sig = self.cx.tcx.fn_sig(def_id).instantiate_identity(); - let sig = self.cx.tcx.instantiate_bound_regions_with_erased(sig); - - for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) { - self.check_type_for_ffi_and_report_errors(input_hir.span, *input_ty, false, false); - } - - if let hir::FnRetTy::Return(ret_hir) = decl.output { - self.check_type_for_ffi_and_report_errors(ret_hir.span, sig.output(), false, true); - } - } - - fn check_foreign_static(&mut self, id: hir::OwnerId, span: Span) { - let ty = self.cx.tcx.type_of(id).instantiate_identity(); - self.check_type_for_ffi_and_report_errors(span, ty, true, false); - } - - /// Find any fn-ptr types with external ABIs in `ty`. - /// - /// For example, `Option<extern "C" fn()>` returns `extern "C" fn()` - fn find_fn_ptr_ty_with_external_abi( - &self, - hir_ty: &hir::Ty<'tcx>, - ty: Ty<'tcx>, - ) -> Vec<(Ty<'tcx>, Span)> { - struct FnPtrFinder<'tcx> { - spans: Vec<Span>, - tys: Vec<Ty<'tcx>>, - } - - impl<'tcx> hir::intravisit::Visitor<'_> for FnPtrFinder<'tcx> { - fn visit_ty(&mut self, ty: &'_ hir::Ty<'_, AmbigArg>) { - debug!(?ty); - if let hir::TyKind::FnPtr(hir::FnPtrTy { abi, .. }) = ty.kind - && !abi.is_rustic_abi() - { - self.spans.push(ty.span); - } - - hir::intravisit::walk_ty(self, ty) - } - } - - impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for FnPtrFinder<'tcx> { - type Result = (); - - fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { - if let ty::FnPtr(_, hdr) = ty.kind() - && !hdr.abi.is_rustic_abi() - { - self.tys.push(ty); - } - - ty.super_visit_with(self) - } - } - - let mut visitor = FnPtrFinder { spans: Vec::new(), tys: Vec::new() }; - ty.visit_with(&mut visitor); - visitor.visit_ty_unambig(hir_ty); - - iter::zip(visitor.tys.drain(..), visitor.spans.drain(..)).collect() - } -} - -impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDeclarations { - fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, it: &hir::ForeignItem<'tcx>) { - let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Declaration }; - let abi = cx.tcx.hir_get_foreign_abi(it.hir_id()); - - match it.kind { - hir::ForeignItemKind::Fn(sig, _, _) => { - if abi.is_rustic_abi() { - vis.check_fn(it.owner_id.def_id, sig.decl) - } else { - vis.check_foreign_fn(it.owner_id.def_id, sig.decl); - } - } - hir::ForeignItemKind::Static(ty, _, _) if !abi.is_rustic_abi() => { - vis.check_foreign_static(it.owner_id, ty.span); - } - hir::ForeignItemKind::Static(..) | hir::ForeignItemKind::Type => (), - } - } -} - -impl ImproperCTypesDefinitions { - fn check_ty_maybe_containing_foreign_fnptr<'tcx>( - &mut self, - cx: &LateContext<'tcx>, - hir_ty: &'tcx hir::Ty<'_>, - ty: Ty<'tcx>, - ) { - let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Definition }; - for (fn_ptr_ty, span) in vis.find_fn_ptr_ty_with_external_abi(hir_ty, ty) { - vis.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, true, false); - } - } - - fn check_arg_for_power_alignment<'tcx>( - &mut self, - cx: &LateContext<'tcx>, - ty: Ty<'tcx>, - ) -> bool { - assert!(cx.tcx.sess.target.os == "aix"); - // Structs (under repr(C)) follow the power alignment rule if: - // - the first field of the struct is a floating-point type that - // is greater than 4-bytes, or - // - the first field of the struct is an aggregate whose - // recursively first field is a floating-point type greater than - // 4 bytes. - if ty.is_floating_point() && ty.primitive_size(cx.tcx).bytes() > 4 { - return true; - } else if let Adt(adt_def, _) = ty.kind() - && adt_def.is_struct() - && adt_def.repr().c() - && !adt_def.repr().packed() - && adt_def.repr().align.is_none() - { - let struct_variant = adt_def.variant(VariantIdx::ZERO); - // Within a nested struct, all fields are examined to correctly - // report if any fields after the nested struct within the - // original struct are misaligned. - for struct_field in &struct_variant.fields { - let field_ty = cx.tcx.type_of(struct_field.did).instantiate_identity(); - if self.check_arg_for_power_alignment(cx, field_ty) { - return true; - } - } - } - return false; - } - - fn check_struct_for_power_alignment<'tcx>( - &mut self, - cx: &LateContext<'tcx>, - item: &'tcx hir::Item<'tcx>, - ) { - let adt_def = cx.tcx.adt_def(item.owner_id.to_def_id()); - // repr(C) structs also with packed or aligned representation - // should be ignored. - if adt_def.repr().c() - && !adt_def.repr().packed() - && adt_def.repr().align.is_none() - && cx.tcx.sess.target.os == "aix" - && !adt_def.all_fields().next().is_none() - { - let struct_variant_data = item.expect_struct().2; - for field_def in struct_variant_data.fields().iter().skip(1) { - // Struct fields (after the first field) are checked for the - // power alignment rule, as fields after the first are likely - // to be the fields that are misaligned. - let def_id = field_def.def_id; - let ty = cx.tcx.type_of(def_id).instantiate_identity(); - if self.check_arg_for_power_alignment(cx, ty) { - cx.emit_span_lint(USES_POWER_ALIGNMENT, field_def.span, UsesPowerAlignment); - } - } - } - } -} - -/// `ImproperCTypesDefinitions` checks items outside of foreign items (e.g. stuff that isn't in -/// `extern "C" { }` blocks): -/// -/// - `extern "<abi>" fn` definitions are checked in the same way as the -/// `ImproperCtypesDeclarations` visitor checks functions if `<abi>` is external (e.g. "C"). -/// - All other items which contain types (e.g. other functions, struct definitions, etc) are -/// checked for extern fn-ptrs with external ABIs. -impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { - match item.kind { - hir::ItemKind::Static(_, _, ty, _) - | hir::ItemKind::Const(_, _, ty, _) - | hir::ItemKind::TyAlias(_, _, ty) => { - self.check_ty_maybe_containing_foreign_fnptr( - cx, - ty, - cx.tcx.type_of(item.owner_id).instantiate_identity(), - ); - } - // See `check_fn`.. - hir::ItemKind::Fn { .. } => {} - // Structs are checked based on if they follow the power alignment - // rule (under repr(C)). - hir::ItemKind::Struct(..) => { - self.check_struct_for_power_alignment(cx, item); - } - // See `check_field_def`.. - hir::ItemKind::Union(..) | hir::ItemKind::Enum(..) => {} - // Doesn't define something that can contain a external type to be checked. - hir::ItemKind::Impl(..) - | hir::ItemKind::TraitAlias(..) - | hir::ItemKind::Trait(..) - | hir::ItemKind::GlobalAsm { .. } - | hir::ItemKind::ForeignMod { .. } - | hir::ItemKind::Mod(..) - | hir::ItemKind::Macro(..) - | hir::ItemKind::Use(..) - | hir::ItemKind::ExternCrate(..) => {} - } - } - - fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::FieldDef<'tcx>) { - self.check_ty_maybe_containing_foreign_fnptr( - cx, - field.ty, - cx.tcx.type_of(field.def_id).instantiate_identity(), - ); - } - - fn check_fn( - &mut self, - cx: &LateContext<'tcx>, - kind: hir::intravisit::FnKind<'tcx>, - decl: &'tcx hir::FnDecl<'_>, - _: &'tcx hir::Body<'_>, - _: Span, - id: LocalDefId, - ) { - use hir::intravisit::FnKind; - - let abi = match kind { - FnKind::ItemFn(_, _, header, ..) => header.abi, - FnKind::Method(_, sig, ..) => sig.header.abi, - _ => return, - }; - - let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Definition }; - if abi.is_rustic_abi() { - vis.check_fn(id, decl); - } else { - vis.check_foreign_fn(id, decl); - } - } -} - declare_lint_pass!(VariantSizeDifferences => [VARIANT_SIZE_DIFFERENCES]); impl<'tcx> LateLintPass<'tcx> for VariantSizeDifferences { diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index 13afa540afc..7ca57b0094e 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -1,10 +1,141 @@ +use std::iter; use std::ops::ControlFlow; +use bitflags::bitflags; +use rustc_abi::VariantIdx; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::DiagMessage; use rustc_hir::def::CtorKind; -use rustc_middle::ty; +use rustc_hir::intravisit::VisitorExt; +use rustc_hir::{self as hir, AmbigArg}; +use rustc_middle::bug; +use rustc_middle::ty::{ + self, Adt, AdtDef, AdtKind, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, + TypeVisitableExt, +}; +use rustc_session::{declare_lint, declare_lint_pass}; +use rustc_span::def_id::LocalDefId; +use rustc_span::{Span, sym}; +use tracing::debug; -use crate::fluent_generated as fluent; +use super::repr_nullable_ptr; +use crate::lints::{ImproperCTypes, UsesPowerAlignment}; +use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent}; + +declare_lint! { + /// The `improper_ctypes` lint detects incorrect use of types in foreign + /// modules. + /// + /// ### Example + /// + /// ```rust + /// unsafe extern "C" { + /// static STATIC: String; + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The compiler has several checks to verify that types used in `extern` + /// blocks are safe and follow certain rules to ensure proper + /// compatibility with the foreign interfaces. This lint is issued when it + /// detects a probable mistake in a definition. The lint usually should + /// provide a description of the issue, along with possibly a hint on how + /// to resolve it. + IMPROPER_CTYPES, + Warn, + "proper use of libc types in foreign modules" +} + +declare_lint! { + /// The `improper_ctypes_definitions` lint detects incorrect use of + /// [`extern` function] definitions. + /// + /// [`extern` function]: https://doc.rust-lang.org/reference/items/functions.html#extern-function-qualifier + /// + /// ### Example + /// + /// ```rust + /// # #![allow(unused)] + /// pub extern "C" fn str_type(p: &str) { } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// There are many parameter and return types that may be specified in an + /// `extern` function that are not compatible with the given ABI. This + /// lint is an alert that these types should not be used. The lint usually + /// should provide a description of the issue, along with possibly a hint + /// on how to resolve it. + IMPROPER_CTYPES_DEFINITIONS, + Warn, + "proper use of libc types in foreign item definitions" +} + +declare_lint! { + /// The `uses_power_alignment` lint detects specific `repr(C)` + /// aggregates on AIX. + /// In its platform C ABI, AIX uses the "power" (as in PowerPC) alignment + /// rule (detailed in https://www.ibm.com/docs/en/xl-c-and-cpp-aix/16.1?topic=data-using-alignment-modes#alignment), + /// which can also be set for XLC by `#pragma align(power)` or + /// `-qalign=power`. Aggregates with a floating-point type as the + /// recursively first field (as in "at offset 0") modify the layout of + /// *subsequent* fields of the associated structs to use an alignment value + /// where the floating-point type is aligned on a 4-byte boundary. + /// + /// Effectively, subsequent floating-point fields act as-if they are `repr(packed(4))`. This + /// would be unsound to do in a `repr(C)` type without all the restrictions that come with + /// `repr(packed)`. Rust instead chooses a layout that maintains soundness of Rust code, at the + /// expense of incompatibility with C code. + /// + /// ### Example + /// + /// ```rust,ignore (fails on non-powerpc64-ibm-aix) + /// #[repr(C)] + /// pub struct Floats { + /// a: f64, + /// b: u8, + /// c: f64, + /// } + /// ``` + /// + /// This will produce: + /// + /// ```text + /// warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type + /// --> <source>:5:3 + /// | + /// 5 | c: f64, + /// | ^^^^^^ + /// | + /// = note: `#[warn(uses_power_alignment)]` on by default + /// ``` + /// + /// ### Explanation + /// + /// The power alignment rule specifies that the above struct has the + /// following alignment: + /// - offset_of!(Floats, a) == 0 + /// - offset_of!(Floats, b) == 8 + /// - offset_of!(Floats, c) == 12 + /// + /// However, Rust currently aligns `c` at `offset_of!(Floats, c) == 16`. + /// Using offset 12 would be unsound since `f64` generally must be 8-aligned on this target. + /// Thus, a warning is produced for the above struct. + USES_POWER_ALIGNMENT, + Warn, + "Structs do not follow the power alignment rule under repr(C)" +} + +declare_lint_pass!(ImproperCTypesLint => [ + IMPROPER_CTYPES, + IMPROPER_CTYPES_DEFINITIONS, + USES_POWER_ALIGNMENT +]); /// Check a variant of a non-exhaustive enum for improper ctypes /// @@ -41,3 +172,884 @@ fn variant_has_complex_ctor(variant: &ty::VariantDef) -> bool { // CtorKind::Const means a "unit" ctor !matches!(variant.ctor_kind(), Some(CtorKind::Const)) } + +/// Per-struct-field function that checks if a struct definition follows +/// the Power alignment Rule (see the `check_struct_for_power_alignment` function). +fn check_arg_for_power_alignment<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + let tcx = cx.tcx; + assert!(tcx.sess.target.os == "aix"); + // Structs (under repr(C)) follow the power alignment rule if: + // - the first field of the struct is a floating-point type that + // is greater than 4-bytes, or + // - the first field of the struct is an aggregate whose + // recursively first field is a floating-point type greater than + // 4 bytes. + if ty.is_floating_point() && ty.primitive_size(tcx).bytes() > 4 { + return true; + } else if let Adt(adt_def, _) = ty.kind() + && adt_def.is_struct() + && adt_def.repr().c() + && !adt_def.repr().packed() + && adt_def.repr().align.is_none() + { + let struct_variant = adt_def.variant(VariantIdx::ZERO); + // Within a nested struct, all fields are examined to correctly + // report if any fields after the nested struct within the + // original struct are misaligned. + for struct_field in &struct_variant.fields { + let field_ty = tcx.type_of(struct_field.did).instantiate_identity(); + if check_arg_for_power_alignment(cx, field_ty) { + return true; + } + } + } + return false; +} + +/// Check a struct definition for respect of the Power alignment Rule (as in PowerPC), +/// which should be respected in the "aix" target OS. +/// To do so, we must follow one of the two following conditions: +/// - The first field of the struct must be floating-point type that +/// is greater than 4-bytes. +/// - The first field of the struct must be an aggregate whose +/// recursively first field is a floating-point type greater than +/// 4 bytes. +fn check_struct_for_power_alignment<'tcx>( + cx: &LateContext<'tcx>, + item: &'tcx hir::Item<'tcx>, + adt_def: AdtDef<'tcx>, +) { + let tcx = cx.tcx; + // repr(C) structs also with packed or aligned representation + // should be ignored. + if adt_def.repr().c() + && !adt_def.repr().packed() + && adt_def.repr().align.is_none() + && tcx.sess.target.os == "aix" + && !adt_def.all_fields().next().is_none() + { + let struct_variant_data = item.expect_struct().2; + for field_def in struct_variant_data.fields().iter().skip(1) { + // Struct fields (after the first field) are checked for the + // power alignment rule, as fields after the first are likely + // to be the fields that are misaligned. + let def_id = field_def.def_id; + let ty = tcx.type_of(def_id).instantiate_identity(); + if check_arg_for_power_alignment(cx, ty) { + cx.emit_span_lint(USES_POWER_ALIGNMENT, field_def.span, UsesPowerAlignment); + } + } + } +} + +#[derive(Clone, Copy)] +enum CItemKind { + Declaration, + Definition, +} + +enum FfiResult<'tcx> { + FfiSafe, + FfiPhantom(Ty<'tcx>), + FfiUnsafe { ty: Ty<'tcx>, reason: DiagMessage, help: Option<DiagMessage> }, +} + +/// The result when a type has been checked but perhaps not completely. `None` indicates that +/// FFI safety/unsafety has not yet been determined, `Some(res)` indicates that the safety/unsafety +/// in the `FfiResult` is final. +type PartialFfiResult<'tcx> = Option<FfiResult<'tcx>>; + +bitflags! { + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + struct VisitorState: u8 { + /// For use in (externally-linked) static variables. + const STATIC = 0b000001; + /// For use in functions in general. + const FUNC = 0b000010; + /// For variables in function returns (implicitly: not for static variables). + const FN_RETURN = 0b000100; + /// For variables in functions/variables which are defined in rust. + const DEFINED = 0b001000; + /// For times where we are only defining the type of something + /// (struct/enum/union definitions, FnPtrs). + const THEORETICAL = 0b010000; + } +} + +impl VisitorState { + // The values that can be set. + const STATIC_TY: Self = Self::STATIC; + const ARGUMENT_TY_IN_DEFINITION: Self = + Self::from_bits(Self::FUNC.bits() | Self::DEFINED.bits()).unwrap(); + const RETURN_TY_IN_DEFINITION: Self = + Self::from_bits(Self::FUNC.bits() | Self::FN_RETURN.bits() | Self::DEFINED.bits()).unwrap(); + const ARGUMENT_TY_IN_DECLARATION: Self = Self::FUNC; + const RETURN_TY_IN_DECLARATION: Self = + Self::from_bits(Self::FUNC.bits() | Self::FN_RETURN.bits()).unwrap(); + const ARGUMENT_TY_IN_FNPTR: Self = + Self::from_bits(Self::FUNC.bits() | Self::THEORETICAL.bits()).unwrap(); + const RETURN_TY_IN_FNPTR: Self = + Self::from_bits(Self::FUNC.bits() | Self::THEORETICAL.bits() | Self::FN_RETURN.bits()) + .unwrap(); + + /// Get the proper visitor state for a given function's arguments. + fn argument_from_fnmode(fn_mode: CItemKind) -> Self { + match fn_mode { + CItemKind::Definition => VisitorState::ARGUMENT_TY_IN_DEFINITION, + CItemKind::Declaration => VisitorState::ARGUMENT_TY_IN_DECLARATION, + } + } + + /// Get the proper visitor state for a given function's return type. + fn return_from_fnmode(fn_mode: CItemKind) -> Self { + match fn_mode { + CItemKind::Definition => VisitorState::RETURN_TY_IN_DEFINITION, + CItemKind::Declaration => VisitorState::RETURN_TY_IN_DECLARATION, + } + } + + /// Whether the type is used in a function. + fn is_in_function(self) -> bool { + let ret = self.contains(Self::FUNC); + if ret { + debug_assert!(!self.contains(Self::STATIC)); + } + ret + } + /// Whether the type is used (directly or not) in a function, in return position. + fn is_in_function_return(self) -> bool { + let ret = self.contains(Self::FN_RETURN); + if ret { + debug_assert!(self.is_in_function()); + } + ret + } + /// Whether the type is used (directly or not) in a defined function. + /// In other words, whether or not we allow non-FFI-safe types behind a C pointer, + /// to be treated as an opaque type on the other side of the FFI boundary. + fn is_in_defined_function(self) -> bool { + self.contains(Self::DEFINED) && self.is_in_function() + } + + /// Whether the type is used (directly or not) in a function pointer type. + /// Here, we also allow non-FFI-safe types behind a C pointer, + /// to be treated as an opaque type on the other side of the FFI boundary. + fn is_in_fnptr(self) -> bool { + self.contains(Self::THEORETICAL) && self.is_in_function() + } + + /// Whether we can expect type parameters and co in a given type. + fn can_expect_ty_params(self) -> bool { + // rust-defined functions, as well as FnPtrs + self.contains(Self::THEORETICAL) || self.is_in_defined_function() + } +} + +/// Visitor used to recursively traverse MIR types and evaluate FFI-safety. +/// It uses ``check_*`` methods as entrypoints to be called elsewhere, +/// and ``visit_*`` methods to recurse. +struct ImproperCTypesVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + /// To prevent problems with recursive types, + /// add a types-in-check cache. + cache: FxHashSet<Ty<'tcx>>, + /// The original type being checked, before we recursed + /// to any other types it contains. + base_ty: Ty<'tcx>, + base_fn_mode: CItemKind, +} + +impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { + fn new(cx: &'a LateContext<'tcx>, base_ty: Ty<'tcx>, base_fn_mode: CItemKind) -> Self { + Self { cx, base_ty, base_fn_mode, cache: FxHashSet::default() } + } + + /// Checks if the given field's type is "ffi-safe". + fn check_field_type_for_ffi( + &mut self, + state: VisitorState, + field: &ty::FieldDef, + args: GenericArgsRef<'tcx>, + ) -> FfiResult<'tcx> { + let field_ty = field.ty(self.cx.tcx, args); + let field_ty = self + .cx + .tcx + .try_normalize_erasing_regions(self.cx.typing_env(), field_ty) + .unwrap_or(field_ty); + self.visit_type(state, field_ty) + } + + /// Checks if the given `VariantDef`'s field types are "ffi-safe". + fn check_variant_for_ffi( + &mut self, + state: VisitorState, + ty: Ty<'tcx>, + def: ty::AdtDef<'tcx>, + variant: &ty::VariantDef, + args: GenericArgsRef<'tcx>, + ) -> FfiResult<'tcx> { + use FfiResult::*; + let transparent_with_all_zst_fields = if def.repr().transparent() { + if let Some(field) = super::transparent_newtype_field(self.cx.tcx, variant) { + // Transparent newtypes have at most one non-ZST field which needs to be checked.. + match self.check_field_type_for_ffi(state, field, args) { + FfiUnsafe { ty, .. } if ty.is_unit() => (), + r => return r, + } + + false + } else { + // ..or have only ZST fields, which is FFI-unsafe (unless those fields are all + // `PhantomData`). + true + } + } else { + false + }; + + // We can't completely trust `repr(C)` markings, so make sure the fields are actually safe. + let mut all_phantom = !variant.fields.is_empty(); + for field in &variant.fields { + all_phantom &= match self.check_field_type_for_ffi(state, field, args) { + FfiSafe => false, + // `()` fields are FFI-safe! + FfiUnsafe { ty, .. } if ty.is_unit() => false, + FfiPhantom(..) => true, + r @ FfiUnsafe { .. } => return r, + } + } + + if all_phantom { + FfiPhantom(ty) + } else if transparent_with_all_zst_fields { + FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_struct_zst, help: None } + } else { + FfiSafe + } + } + + /// Checks if the given type is "ffi-safe" (has a stable, well-defined + /// representation which can be exported to C code). + fn visit_type(&mut self, state: VisitorState, ty: Ty<'tcx>) -> FfiResult<'tcx> { + use FfiResult::*; + + let tcx = self.cx.tcx; + + // Protect against infinite recursion, for example + // `struct S(*mut S);`. + // FIXME: A recursion limit is necessary as well, for irregular + // recursive types. + if !self.cache.insert(ty) { + return FfiSafe; + } + + match *ty.kind() { + ty::Adt(def, args) => { + if let Some(boxed) = ty.boxed_ty() + && ( + // FIXME(ctypes): this logic is broken, but it still fits the current tests + state.is_in_defined_function() + || (state.is_in_fnptr() + && matches!(self.base_fn_mode, CItemKind::Definition)) + ) + { + if boxed.is_sized(tcx, self.cx.typing_env()) { + return FfiSafe; + } else { + return FfiUnsafe { + ty, + reason: fluent::lint_improper_ctypes_box, + help: None, + }; + } + } + if def.is_phantom_data() { + return FfiPhantom(ty); + } + match def.adt_kind() { + AdtKind::Struct | AdtKind::Union => { + if let Some(sym::cstring_type | sym::cstr_type) = + tcx.get_diagnostic_name(def.did()) + && !self.base_ty.is_mutable_ptr() + { + return FfiUnsafe { + ty, + reason: fluent::lint_improper_ctypes_cstr_reason, + help: Some(fluent::lint_improper_ctypes_cstr_help), + }; + } + + if !def.repr().c() && !def.repr().transparent() { + return FfiUnsafe { + ty, + reason: if def.is_struct() { + fluent::lint_improper_ctypes_struct_layout_reason + } else { + fluent::lint_improper_ctypes_union_layout_reason + }, + help: if def.is_struct() { + Some(fluent::lint_improper_ctypes_struct_layout_help) + } else { + Some(fluent::lint_improper_ctypes_union_layout_help) + }, + }; + } + + if def.non_enum_variant().field_list_has_applicable_non_exhaustive() { + return FfiUnsafe { + ty, + reason: if def.is_struct() { + fluent::lint_improper_ctypes_struct_non_exhaustive + } else { + fluent::lint_improper_ctypes_union_non_exhaustive + }, + help: None, + }; + } + + if def.non_enum_variant().fields.is_empty() { + return FfiUnsafe { + ty, + reason: if def.is_struct() { + fluent::lint_improper_ctypes_struct_fieldless_reason + } else { + fluent::lint_improper_ctypes_union_fieldless_reason + }, + help: if def.is_struct() { + Some(fluent::lint_improper_ctypes_struct_fieldless_help) + } else { + Some(fluent::lint_improper_ctypes_union_fieldless_help) + }, + }; + } + + self.check_variant_for_ffi(state, ty, def, def.non_enum_variant(), args) + } + AdtKind::Enum => { + if def.variants().is_empty() { + // Empty enums are okay... although sort of useless. + return FfiSafe; + } + // Check for a repr() attribute to specify the size of the + // discriminant. + if !def.repr().c() && !def.repr().transparent() && def.repr().int.is_none() + { + // Special-case types like `Option<extern fn()>` and `Result<extern fn(), ()>` + if let Some(ty) = + repr_nullable_ptr(self.cx.tcx, self.cx.typing_env(), ty) + { + return self.visit_type(state, ty); + } + + return FfiUnsafe { + ty, + reason: fluent::lint_improper_ctypes_enum_repr_reason, + help: Some(fluent::lint_improper_ctypes_enum_repr_help), + }; + } + + let non_exhaustive = def.variant_list_has_applicable_non_exhaustive(); + // Check the contained variants. + let ret = def.variants().iter().try_for_each(|variant| { + check_non_exhaustive_variant(non_exhaustive, variant) + .map_break(|reason| FfiUnsafe { ty, reason, help: None })?; + + match self.check_variant_for_ffi(state, ty, def, variant, args) { + FfiSafe => ControlFlow::Continue(()), + r => ControlFlow::Break(r), + } + }); + if let ControlFlow::Break(result) = ret { + return result; + } + + FfiSafe + } + } + } + + ty::Char => FfiUnsafe { + ty, + reason: fluent::lint_improper_ctypes_char_reason, + help: Some(fluent::lint_improper_ctypes_char_help), + }, + + // It's just extra invariants on the type that you need to uphold, + // but only the base type is relevant for being representable in FFI. + ty::Pat(base, ..) => self.visit_type(state, base), + + // Primitive types with a stable representation. + ty::Bool | ty::Int(..) | ty::Uint(..) | ty::Float(..) | ty::Never => FfiSafe, + + ty::Slice(_) => FfiUnsafe { + ty, + reason: fluent::lint_improper_ctypes_slice_reason, + help: Some(fluent::lint_improper_ctypes_slice_help), + }, + + ty::Dynamic(..) => { + FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_dyn, help: None } + } + + ty::Str => FfiUnsafe { + ty, + reason: fluent::lint_improper_ctypes_str_reason, + help: Some(fluent::lint_improper_ctypes_str_help), + }, + + ty::Tuple(..) => FfiUnsafe { + ty, + reason: fluent::lint_improper_ctypes_tuple_reason, + help: Some(fluent::lint_improper_ctypes_tuple_help), + }, + + ty::RawPtr(ty, _) | ty::Ref(_, ty, _) + if { + (state.is_in_defined_function() || state.is_in_fnptr()) + && ty.is_sized(self.cx.tcx, self.cx.typing_env()) + } => + { + FfiSafe + } + + ty::RawPtr(ty, _) + if match ty.kind() { + ty::Tuple(tuple) => tuple.is_empty(), + _ => false, + } => + { + FfiSafe + } + + ty::RawPtr(ty, _) | ty::Ref(_, ty, _) => self.visit_type(state, ty), + + ty::Array(inner_ty, _) => self.visit_type(state, inner_ty), + + ty::FnPtr(sig_tys, hdr) => { + let sig = sig_tys.with(hdr); + if sig.abi().is_rustic_abi() { + return FfiUnsafe { + ty, + reason: fluent::lint_improper_ctypes_fnptr_reason, + help: Some(fluent::lint_improper_ctypes_fnptr_help), + }; + } + + let sig = tcx.instantiate_bound_regions_with_erased(sig); + for arg in sig.inputs() { + match self.visit_type(VisitorState::ARGUMENT_TY_IN_FNPTR, *arg) { + FfiSafe => {} + r => return r, + } + } + + let ret_ty = sig.output(); + if ret_ty.is_unit() { + return FfiSafe; + } + + self.visit_type(VisitorState::RETURN_TY_IN_FNPTR, ret_ty) + } + + ty::Foreign(..) => FfiSafe, + + // While opaque types are checked for earlier, if a projection in a struct field + // normalizes to an opaque type, then it will reach this branch. + ty::Alias(ty::Opaque, ..) => { + FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_opaque, help: None } + } + + // `extern "C" fn` functions can have type parameters, which may or may not be FFI-safe, + // so they are currently ignored for the purposes of this lint. + ty::Param(..) | ty::Alias(ty::Projection | ty::Inherent, ..) + if state.can_expect_ty_params() => + { + FfiSafe + } + + ty::UnsafeBinder(_) => todo!("FIXME(unsafe_binder)"), + + ty::Param(..) + | ty::Alias(ty::Projection | ty::Inherent | ty::Free, ..) + | ty::Infer(..) + | ty::Bound(..) + | ty::Error(_) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(..) + | ty::CoroutineWitness(..) + | ty::Placeholder(..) + | ty::FnDef(..) => bug!("unexpected type in foreign function: {:?}", ty), + } + } + + fn visit_for_opaque_ty(&mut self, ty: Ty<'tcx>) -> PartialFfiResult<'tcx> { + struct ProhibitOpaqueTypes; + impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for ProhibitOpaqueTypes { + type Result = ControlFlow<Ty<'tcx>>; + + fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { + if !ty.has_opaque_types() { + return ControlFlow::Continue(()); + } + + if let ty::Alias(ty::Opaque, ..) = ty.kind() { + ControlFlow::Break(ty) + } else { + ty.super_visit_with(self) + } + } + } + + if let Some(ty) = self + .cx + .tcx + .try_normalize_erasing_regions(self.cx.typing_env(), ty) + .unwrap_or(ty) + .visit_with(&mut ProhibitOpaqueTypes) + .break_value() + { + Some(FfiResult::FfiUnsafe { + ty, + reason: fluent::lint_improper_ctypes_opaque, + help: None, + }) + } else { + None + } + } + + /// Check if the type is array and emit an unsafe type lint. + fn check_for_array_ty(&mut self, ty: Ty<'tcx>) -> PartialFfiResult<'tcx> { + if let ty::Array(..) = ty.kind() { + Some(FfiResult::FfiUnsafe { + ty, + reason: fluent::lint_improper_ctypes_array_reason, + help: Some(fluent::lint_improper_ctypes_array_help), + }) + } else { + None + } + } + + /// Determine the FFI-safety of a single (MIR) type, given the context of how it is used. + fn check_type(&mut self, state: VisitorState, ty: Ty<'tcx>) -> FfiResult<'tcx> { + if let Some(res) = self.visit_for_opaque_ty(ty) { + return res; + } + + let ty = self.cx.tcx.try_normalize_erasing_regions(self.cx.typing_env(), ty).unwrap_or(ty); + + // C doesn't really support passing arrays by value - the only way to pass an array by value + // is through a struct. So, first test that the top level isn't an array, and then + // recursively check the types inside. + if state.is_in_function() { + if let Some(res) = self.check_for_array_ty(ty) { + return res; + } + } + + // Don't report FFI errors for unit return types. This check exists here, and not in + // the caller (where it would make more sense) so that normalization has definitely + // happened. + if state.is_in_function_return() && ty.is_unit() { + return FfiResult::FfiSafe; + } + + self.visit_type(state, ty) + } +} + +impl<'tcx> ImproperCTypesLint { + /// Find any fn-ptr types with external ABIs in `ty`, and FFI-checks them. + /// For example, `Option<extern "C" fn()>` FFI-checks `extern "C" fn()`. + fn check_type_for_external_abi_fnptr( + &mut self, + cx: &LateContext<'tcx>, + state: VisitorState, + hir_ty: &hir::Ty<'tcx>, + ty: Ty<'tcx>, + fn_mode: CItemKind, + ) { + struct FnPtrFinder<'tcx> { + spans: Vec<Span>, + tys: Vec<Ty<'tcx>>, + } + + impl<'tcx> hir::intravisit::Visitor<'_> for FnPtrFinder<'tcx> { + fn visit_ty(&mut self, ty: &'_ hir::Ty<'_, AmbigArg>) { + debug!(?ty); + if let hir::TyKind::FnPtr(hir::FnPtrTy { abi, .. }) = ty.kind + && !abi.is_rustic_abi() + { + self.spans.push(ty.span); + } + + hir::intravisit::walk_ty(self, ty) + } + } + + impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for FnPtrFinder<'tcx> { + type Result = (); + + fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { + if let ty::FnPtr(_, hdr) = ty.kind() + && !hdr.abi.is_rustic_abi() + { + self.tys.push(ty); + } + + ty.super_visit_with(self) + } + } + + let mut visitor = FnPtrFinder { spans: Vec::new(), tys: Vec::new() }; + ty.visit_with(&mut visitor); + visitor.visit_ty_unambig(hir_ty); + + let all_types = iter::zip(visitor.tys.drain(..), visitor.spans.drain(..)); + for (fn_ptr_ty, span) in all_types { + let mut visitor = ImproperCTypesVisitor::new(cx, fn_ptr_ty, fn_mode); + // FIXME(ctypes): make a check_for_fnptr + let ffi_res = visitor.check_type(state, fn_ptr_ty); + + self.process_ffi_result(cx, span, ffi_res, fn_mode); + } + } + + /// Regardless of a function's need to be "ffi-safe", look for fn-ptr argument/return types + /// that need to be checked for ffi-safety. + fn check_fn_for_external_abi_fnptr( + &mut self, + cx: &LateContext<'tcx>, + fn_mode: CItemKind, + def_id: LocalDefId, + decl: &'tcx hir::FnDecl<'_>, + ) { + let sig = cx.tcx.fn_sig(def_id).instantiate_identity(); + let sig = cx.tcx.instantiate_bound_regions_with_erased(sig); + + for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) { + let state = VisitorState::argument_from_fnmode(fn_mode); + self.check_type_for_external_abi_fnptr(cx, state, input_hir, *input_ty, fn_mode); + } + + if let hir::FnRetTy::Return(ret_hir) = decl.output { + let state = VisitorState::return_from_fnmode(fn_mode); + self.check_type_for_external_abi_fnptr(cx, state, ret_hir, sig.output(), fn_mode); + } + } + + /// For a local definition of a #[repr(C)] struct/enum/union, check that it is indeed FFI-safe. + fn check_reprc_adt( + &mut self, + cx: &LateContext<'tcx>, + item: &'tcx hir::Item<'tcx>, + adt_def: AdtDef<'tcx>, + ) { + debug_assert!( + adt_def.repr().c() && !adt_def.repr().packed() && adt_def.repr().align.is_none() + ); + + // FIXME(ctypes): this following call is awkward. + // is there a way to perform its logic in MIR space rather than HIR space? + // (so that its logic can be absorbed into visitor.visit_struct_or_union) + check_struct_for_power_alignment(cx, item, adt_def); + } + + fn check_foreign_static(&mut self, cx: &LateContext<'tcx>, id: hir::OwnerId, span: Span) { + let ty = cx.tcx.type_of(id).instantiate_identity(); + let mut visitor = ImproperCTypesVisitor::new(cx, ty, CItemKind::Declaration); + let ffi_res = visitor.check_type(VisitorState::STATIC_TY, ty); + self.process_ffi_result(cx, span, ffi_res, CItemKind::Declaration); + } + + /// Check if a function's argument types and result type are "ffi-safe". + fn check_foreign_fn( + &mut self, + cx: &LateContext<'tcx>, + fn_mode: CItemKind, + def_id: LocalDefId, + decl: &'tcx hir::FnDecl<'_>, + ) { + let sig = cx.tcx.fn_sig(def_id).instantiate_identity(); + let sig = cx.tcx.instantiate_bound_regions_with_erased(sig); + + for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) { + let state = VisitorState::argument_from_fnmode(fn_mode); + let mut visitor = ImproperCTypesVisitor::new(cx, *input_ty, fn_mode); + let ffi_res = visitor.check_type(state, *input_ty); + self.process_ffi_result(cx, input_hir.span, ffi_res, fn_mode); + } + + if let hir::FnRetTy::Return(ret_hir) = decl.output { + let state = VisitorState::return_from_fnmode(fn_mode); + let mut visitor = ImproperCTypesVisitor::new(cx, sig.output(), fn_mode); + let ffi_res = visitor.check_type(state, sig.output()); + self.process_ffi_result(cx, ret_hir.span, ffi_res, fn_mode); + } + } + + fn process_ffi_result( + &self, + cx: &LateContext<'tcx>, + sp: Span, + res: FfiResult<'tcx>, + fn_mode: CItemKind, + ) { + match res { + FfiResult::FfiSafe => {} + FfiResult::FfiPhantom(ty) => { + self.emit_ffi_unsafe_type_lint( + cx, + ty, + sp, + fluent::lint_improper_ctypes_only_phantomdata, + None, + fn_mode, + ); + } + FfiResult::FfiUnsafe { ty, reason, help } => { + self.emit_ffi_unsafe_type_lint(cx, ty, sp, reason, help, fn_mode); + } + } + } + + fn emit_ffi_unsafe_type_lint( + &self, + cx: &LateContext<'tcx>, + ty: Ty<'tcx>, + sp: Span, + note: DiagMessage, + help: Option<DiagMessage>, + fn_mode: CItemKind, + ) { + let lint = match fn_mode { + CItemKind::Declaration => IMPROPER_CTYPES, + CItemKind::Definition => IMPROPER_CTYPES_DEFINITIONS, + }; + let desc = match fn_mode { + CItemKind::Declaration => "block", + CItemKind::Definition => "fn", + }; + let span_note = if let ty::Adt(def, _) = ty.kind() + && let Some(sp) = cx.tcx.hir_span_if_local(def.did()) + { + Some(sp) + } else { + None + }; + cx.emit_span_lint(lint, sp, ImproperCTypes { ty, desc, label: sp, help, note, span_note }); + } +} + +/// `ImproperCTypesDefinitions` checks items outside of foreign items (e.g. stuff that isn't in +/// `extern "C" { }` blocks): +/// +/// - `extern "<abi>" fn` definitions are checked in the same way as the +/// `ImproperCtypesDeclarations` visitor checks functions if `<abi>` is external (e.g. "C"). +/// - All other items which contain types (e.g. other functions, struct definitions, etc) are +/// checked for extern fn-ptrs with external ABIs. +impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint { + fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, it: &hir::ForeignItem<'tcx>) { + let abi = cx.tcx.hir_get_foreign_abi(it.hir_id()); + + match it.kind { + hir::ForeignItemKind::Fn(sig, _, _) => { + // fnptrs are a special case, they always need to be treated as + // "the element rendered unsafe" because their unsafety doesn't affect + // their surroundings, and their type is often declared inline + if !abi.is_rustic_abi() { + self.check_foreign_fn(cx, CItemKind::Declaration, it.owner_id.def_id, sig.decl); + } else { + self.check_fn_for_external_abi_fnptr( + cx, + CItemKind::Declaration, + it.owner_id.def_id, + sig.decl, + ); + } + } + hir::ForeignItemKind::Static(ty, _, _) if !abi.is_rustic_abi() => { + self.check_foreign_static(cx, it.owner_id, ty.span); + } + hir::ForeignItemKind::Static(..) | hir::ForeignItemKind::Type => (), + } + } + + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { + match item.kind { + hir::ItemKind::Static(_, _, ty, _) + | hir::ItemKind::Const(_, _, ty, _) + | hir::ItemKind::TyAlias(_, _, ty) => { + self.check_type_for_external_abi_fnptr( + cx, + VisitorState::STATIC_TY, + ty, + cx.tcx.type_of(item.owner_id).instantiate_identity(), + CItemKind::Definition, + ); + } + // See `check_fn` for declarations, `check_foreign_items` for definitions in extern blocks + hir::ItemKind::Fn { .. } => {} + hir::ItemKind::Struct(..) | hir::ItemKind::Union(..) | hir::ItemKind::Enum(..) => { + // looking for extern FnPtr:s is delegated to `check_field_def`. + let adt_def: AdtDef<'tcx> = cx.tcx.adt_def(item.owner_id.to_def_id()); + + if adt_def.repr().c() && !adt_def.repr().packed() && adt_def.repr().align.is_none() + { + self.check_reprc_adt(cx, item, adt_def); + } + } + + // Doesn't define something that can contain a external type to be checked. + hir::ItemKind::Impl(..) + | hir::ItemKind::TraitAlias(..) + | hir::ItemKind::Trait(..) + | hir::ItemKind::GlobalAsm { .. } + | hir::ItemKind::ForeignMod { .. } + | hir::ItemKind::Mod(..) + | hir::ItemKind::Macro(..) + | hir::ItemKind::Use(..) + | hir::ItemKind::ExternCrate(..) => {} + } + } + + fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::FieldDef<'tcx>) { + self.check_type_for_external_abi_fnptr( + cx, + VisitorState::STATIC_TY, + field.ty, + cx.tcx.type_of(field.def_id).instantiate_identity(), + CItemKind::Definition, + ); + } + + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + kind: hir::intravisit::FnKind<'tcx>, + decl: &'tcx hir::FnDecl<'_>, + _: &'tcx hir::Body<'_>, + _: Span, + id: LocalDefId, + ) { + use hir::intravisit::FnKind; + + let abi = match kind { + FnKind::ItemFn(_, _, header, ..) => header.abi, + FnKind::Method(_, sig, ..) => sig.header.abi, + _ => return, + }; + + // fnptrs are a special case, they always need to be treated as + // "the element rendered unsafe" because their unsafety doesn't affect + // their surroundings, and their type is often declared inline + if !abi.is_rustic_abi() { + self.check_foreign_fn(cx, CItemKind::Definition, id, decl); + } else { + self.check_fn_for_external_abi_fnptr(cx, CItemKind::Definition, id, decl); + } + } +} diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index ce92263babd..b79075ac09b 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -5141,3 +5141,57 @@ declare_lint! { "detects tail calls of functions marked with `#[track_caller]`", @feature_gate = explicit_tail_calls; } +declare_lint! { + /// The `inline_always_mismatching_target_features` lint will trigger when a + /// function with the `#[inline(always)]` and `#[target_feature(enable = "...")]` + /// attributes is called and cannot be inlined due to missing target features in the caller. + /// + /// ### Example + /// + /// ```rust,ignore (fails on x86_64) + /// #[inline(always)] + /// #[target_feature(enable = "fp16")] + /// unsafe fn callee() { + /// // operations using fp16 types + /// } + /// + /// // Caller does not enable the required target feature + /// fn caller() { + /// unsafe { callee(); } + /// } + /// + /// fn main() { + /// caller(); + /// } + /// ``` + /// + /// This will produce: + /// + /// ```text + /// warning: call to `#[inline(always)]`-annotated `callee` requires the same target features. Function will not have `alwaysinline` attribute applied + /// --> $DIR/builtin.rs:5192:14 + /// | + /// 10 | unsafe { callee(); } + /// | ^^^^^^^^ + /// | + /// note: `fp16` target feature enabled in `callee` here but missing from `caller` + /// --> $DIR/builtin.rs:5185:1 + /// | + /// 3 | #[target_feature(enable = "fp16")] + /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + /// 4 | unsafe fn callee() { + /// | ------------------ + /// = note: `#[warn(inline_always_mismatching_target_features)]` on by default + /// warning: 1 warning emitted + /// ``` + /// + /// ### Explanation + /// + /// Inlining a function with a target feature attribute into a caller that + /// lacks the corresponding target feature can lead to unsound behavior. + /// LLVM may select the wrong instructions or registers, or reorder + /// operations, potentially resulting in runtime errors. + pub INLINE_ALWAYS_MISMATCHING_TARGET_FEATURES, + Warn, + r#"detects when a function annotated with `#[inline(always)]` and `#[target_feature(enable = "..")]` is inlined into a caller without the required target feature"#, +} diff --git a/compiler/rustc_llvm/Cargo.toml b/compiler/rustc_llvm/Cargo.toml index e74de453be2..cd352ce3d0f 100644 --- a/compiler/rustc_llvm/Cargo.toml +++ b/compiler/rustc_llvm/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -libc.workspace = true +libc = "0.2.73" # tidy-alphabetical-end [build-dependencies] diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs index d01f79dcade..225ab8c846d 100644 --- a/compiler/rustc_llvm/build.rs +++ b/compiler/rustc_llvm/build.rs @@ -174,10 +174,10 @@ fn main() { // Prevent critical warnings when we're compiling from rust-lang/rust CI, // except on MSVC, as the compiler throws warnings that are only reported - // for this platform. See https://github.com/rust-lang/rust/pull/145031#issuecomment-3162677202 - // FIXME(llvm22): It looks like the specific problem code has been removed - // in https://github.com/llvm/llvm-project/commit/e8fc808bf8e78a3c80d1f8e293a92677b92366dd, - // retry msvc once we bump our LLVM version. + // for this platform. See https://github.com/rust-lang/rust/pull/145031#issuecomment-3162677202. + // Moreover, LLVM generally guarantees warning-freedom only when building with Clang, as other + // compilers have too many false positives. This is typically the case for MSVC, which throws + // many false-positive warnings. We keep it excluded, for these reasons. if std::env::var_os("CI").is_some() && !target.contains("msvc") { cfg.warnings_into_errors(true); } diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index dd492325814..3bb1533c2fe 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -1568,12 +1568,11 @@ extern "C" bool LLVMRustPrepareThinLTOImport(const LLVMRustThinLTOData *Data, return true; } -extern "C" LLVMRustThinLTOBuffer * -LLVMRustThinLTOBufferCreate(LLVMModuleRef M, bool is_thin, bool emit_summary) { +extern "C" LLVMRustThinLTOBuffer *LLVMRustThinLTOBufferCreate(LLVMModuleRef M, + bool is_thin) { auto Ret = std::make_unique<LLVMRustThinLTOBuffer>(); { auto OS = raw_string_ostream(Ret->data); - auto ThinLinkOS = raw_string_ostream(Ret->thin_link_data); { if (is_thin) { PassBuilder PB; @@ -1587,11 +1586,7 @@ LLVMRustThinLTOBufferCreate(LLVMModuleRef M, bool is_thin, bool emit_summary) { PB.registerLoopAnalyses(LAM); PB.crossRegisterProxies(LAM, FAM, CGAM, MAM); ModulePassManager MPM; - // We only pass ThinLinkOS to be filled in if we want the summary, - // because otherwise LLVM does extra work and may double-emit some - // errors or warnings. - MPM.addPass( - ThinLTOBitcodeWriterPass(OS, emit_summary ? &ThinLinkOS : nullptr)); + MPM.addPass(ThinLTOBitcodeWriterPass(OS, nullptr)); MPM.run(*unwrap(M), MAM); } else { WriteBitcodeToFile(*unwrap(M), OS); diff --git a/compiler/rustc_log/Cargo.toml b/compiler/rustc_log/Cargo.toml index e9d7b4723f8..2332ff9b323 100644 --- a/compiler/rustc_log/Cargo.toml +++ b/compiler/rustc_log/Cargo.toml @@ -5,10 +5,10 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -tracing-core = "=0.1.30" # FIXME(Nilstrieb) tracing has a deadlock: https://github.com/tokio-rs/tracing/issues/2635 +# tracing > 0.1.37 have huge binary size / instructions regression +tracing = "=0.1.37" tracing-subscriber = { version = "0.3.3", default-features = false, features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi"] } tracing-tree = "0.3.1" -tracing.workspace = true # tidy-alphabetical-end [features] diff --git a/compiler/rustc_log/src/lib.rs b/compiler/rustc_log/src/lib.rs index df648bbd489..26475eec1c1 100644 --- a/compiler/rustc_log/src/lib.rs +++ b/compiler/rustc_log/src/lib.rs @@ -38,7 +38,7 @@ use std::fmt::{self, Display}; use std::io::{self, IsTerminal}; use tracing::dispatcher::SetGlobalDefaultError; -use tracing_core::{Event, Subscriber}; +use tracing::{Event, Subscriber}; use tracing_subscriber::filter::{Directive, EnvFilter, LevelFilter}; use tracing_subscriber::fmt::FmtContext; use tracing_subscriber::fmt::format::{self, FormatEvent, FormatFields}; diff --git a/compiler/rustc_macros/Cargo.toml b/compiler/rustc_macros/Cargo.toml index 5add2691b88..f9d3b758359 100644 --- a/compiler/rustc_macros/Cargo.toml +++ b/compiler/rustc_macros/Cargo.toml @@ -8,8 +8,8 @@ proc-macro = true [dependencies] # tidy-alphabetical-start -proc-macro2.workspace = true -quote.workspace = true +proc-macro2 = "1" +quote = "1" syn = { version = "2.0.9", features = ["full"] } synstructure = "0.13.0" # tidy-alphabetical-end diff --git a/compiler/rustc_metadata/Cargo.toml b/compiler/rustc_metadata/Cargo.toml index a8f3dd18353..1b40d9f684e 100644 --- a/compiler/rustc_metadata/Cargo.toml +++ b/compiler/rustc_metadata/Cargo.toml @@ -5,9 +5,9 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -bitflags.workspace = true +bitflags = "2.4.1" libloading = "0.8.0" -odht.workspace = true +odht = { version = "0.3.1", features = ["nightly"] } rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_attr_parsing = { path = "../rustc_attr_parsing" } @@ -30,11 +30,11 @@ rustc_serialize = { path = "../rustc_serialize" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } -tempfile.workspace = true -tracing.workspace = true +tempfile = "3.7.1" +tracing = "0.1" # tidy-alphabetical-end [target.'cfg(target_os = "aix")'.dependencies] # tidy-alphabetical-start -libc.workspace = true +libc = "0.2" # tidy-alphabetical-end diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 5d776ea581d..9e23da88f5e 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -412,7 +412,7 @@ impl CStore { match (&left_name_val, &right_name_val) { (Some(l), Some(r)) => match l.1.opt.cmp(&r.1.opt) { cmp::Ordering::Equal => { - if l.0.tech_value != r.0.tech_value { + if !l.1.consistent(&tcx.sess.opts, Some(&r.1)) { report_diff( &l.0.prefix, &l.0.name, @@ -424,20 +424,28 @@ impl CStore { right_name_val = None; } cmp::Ordering::Greater => { - report_diff(&r.0.prefix, &r.0.name, None, Some(&r.1.value_name)); + if !r.1.consistent(&tcx.sess.opts, None) { + report_diff(&r.0.prefix, &r.0.name, None, Some(&r.1.value_name)); + } right_name_val = None; } cmp::Ordering::Less => { - report_diff(&l.0.prefix, &l.0.name, Some(&l.1.value_name), None); + if !l.1.consistent(&tcx.sess.opts, None) { + report_diff(&l.0.prefix, &l.0.name, Some(&l.1.value_name), None); + } left_name_val = None; } }, (Some(l), None) => { - report_diff(&l.0.prefix, &l.0.name, Some(&l.1.value_name), None); + if !l.1.consistent(&tcx.sess.opts, None) { + report_diff(&l.0.prefix, &l.0.name, Some(&l.1.value_name), None); + } left_name_val = None; } (None, Some(r)) => { - report_diff(&r.0.prefix, &r.0.name, None, Some(&r.1.value_name)); + if !r.1.consistent(&tcx.sess.opts, None) { + report_diff(&r.0.prefix, &r.0.name, None, Some(&r.1.value_name)); + } right_name_val = None; } (None, None) => break, diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index f0832405567..fbcce16cedc 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -5,12 +5,12 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -bitflags.workspace = true -either.workspace = true +bitflags = "2.4.1" +either = "1.5.0" gsgdt = "0.1.2" -polonius-engine.workspace = true +polonius-engine = "0.13.0" rustc_abi = { path = "../rustc_abi" } -rustc_apfloat.workspace = true +rustc_apfloat = "0.2.0" rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } rustc_ast_ir = { path = "../rustc_ast_ir" } @@ -34,8 +34,8 @@ rustc_target = { path = "../rustc_target" } rustc_thread_pool = { path = "../rustc_thread_pool" } rustc_type_ir = { path = "../rustc_type_ir" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } -thin-vec.workspace = true -tracing.workspace = true +thin-vec = "0.2.12" +tracing = "0.1" # tidy-alphabetical-end [features] diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index 4b6e38cd52d..52fbe19c9f2 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -80,6 +80,7 @@ macro_rules! arena_types { rustc_middle::infer::canonical::Canonical<'tcx, rustc_middle::infer::canonical::QueryResponse<'tcx, rustc_middle::ty::Ty<'tcx>> >, + [] inspect_probe: rustc_middle::traits::solve::inspect::Probe<rustc_middle::ty::TyCtxt<'tcx>>, [] effective_visibilities: rustc_middle::middle::privacy::EffectiveVisibilities, [] upvars_mentioned: rustc_data_structures::fx::FxIndexMap<rustc_hir::HirId, rustc_hir::Upvar>, [] dyn_compatibility_violations: rustc_middle::traits::DynCompatibilityViolation, diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index 866736f74a0..8b4503073b0 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -82,7 +82,7 @@ pub enum TargetFeatureKind { Forced, } -#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, TyEncodable, TyDecodable, HashStable)] pub struct TargetFeature { /// The name of the target feature (e.g. "avx") pub name: Symbol, diff --git a/compiler/rustc_middle/src/middle/region.rs b/compiler/rustc_middle/src/middle/region.rs index 857d041224f..5367e5edd49 100644 --- a/compiler/rustc_middle/src/middle/region.rs +++ b/compiler/rustc_middle/src/middle/region.rs @@ -299,4 +299,43 @@ impl ScopeTree { true } + + /// Returns the scope of non-lifetime-extended temporaries within a given scope, as well as + /// whether we've recorded a potential backwards-incompatible change to lint on. + /// Returns `None` when no enclosing temporary scope is found, such as for static items. + pub fn default_temporary_scope(&self, inner: Scope) -> (Option<Scope>, Option<Scope>) { + let mut id = inner; + let mut backwards_incompatible = None; + + while let Some(&p) = self.parent_map.get(&id) { + match p.data { + ScopeData::Destruction => { + debug!("temporary_scope({inner:?}) = {id:?} [enclosing]"); + return (Some(id), backwards_incompatible); + } + ScopeData::IfThenRescope | ScopeData::MatchGuard => { + debug!("temporary_scope({inner:?}) = {p:?} [enclosing]"); + return (Some(p), backwards_incompatible); + } + ScopeData::Node + | ScopeData::CallSite + | ScopeData::Arguments + | ScopeData::IfThen + | ScopeData::Remainder(_) => { + // If we haven't already passed through a backwards-incompatible node, + // then check if we are passing through one now and record it if so. + // This is for now only working for cases where a temporary lifetime is + // *shortened*. + if backwards_incompatible.is_none() { + backwards_incompatible = + self.backwards_incompatible_scope.get(&p.local_id).copied(); + } + id = p + } + } + } + + debug!("temporary_scope({inner:?}) = None"); + (None, backwards_incompatible) + } } diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index 164433aede2..5764a9c84ee 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -579,7 +579,7 @@ impl<'tcx> Display for Const<'tcx> { } /////////////////////////////////////////////////////////////////////////// -/// Const-related utilities +// Const-related utilities impl<'tcx> TyCtxt<'tcx> { pub fn span_as_caller_location(self, span: Span) -> ConstValue { diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index 6e52bc775ef..ec2a8e86077 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -408,14 +408,14 @@ impl<'tcx> Place<'tcx> { self.as_ref().project_deeper(more_projections, tcx) } - pub fn ty_from<D: ?Sized>( + pub fn ty_from<D>( local: Local, projection: &[PlaceElem<'tcx>], local_decls: &D, tcx: TyCtxt<'tcx>, ) -> PlaceTy<'tcx> where - D: HasLocalDecls<'tcx>, + D: ?Sized + HasLocalDecls<'tcx>, { PlaceTy::from_ty(local_decls.local_decls()[local].ty).multi_projection_ty(tcx, projection) } @@ -529,9 +529,9 @@ impl<'tcx> PlaceRef<'tcx> { Place { local: self.local, projection: tcx.mk_place_elems(new_projections) } } - pub fn ty<D: ?Sized>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> PlaceTy<'tcx> + pub fn ty<D>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> PlaceTy<'tcx> where - D: HasLocalDecls<'tcx>, + D: ?Sized + HasLocalDecls<'tcx>, { Place::ty_from(self.local, self.projection, local_decls, tcx) } @@ -630,9 +630,9 @@ impl<'tcx> Operand<'tcx> { if let ty::FnDef(def_id, args) = *const_ty.kind() { Some((def_id, args)) } else { None } } - pub fn ty<D: ?Sized>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> Ty<'tcx> + pub fn ty<D>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> Ty<'tcx> where - D: HasLocalDecls<'tcx>, + D: ?Sized + HasLocalDecls<'tcx>, { match self { &Operand::Copy(ref l) | &Operand::Move(ref l) => l.ty(local_decls, tcx).ty, @@ -640,9 +640,9 @@ impl<'tcx> Operand<'tcx> { } } - pub fn span<D: ?Sized>(&self, local_decls: &D) -> Span + pub fn span<D>(&self, local_decls: &D) -> Span where - D: HasLocalDecls<'tcx>, + D: ?Sized + HasLocalDecls<'tcx>, { match self { &Operand::Copy(ref l) | &Operand::Move(ref l) => { @@ -674,7 +674,7 @@ impl<'tcx> ConstOperand<'tcx> { } /////////////////////////////////////////////////////////////////////////// -/// Rvalues +// Rvalues pub enum RvalueInitializationState { Shallow, @@ -721,9 +721,9 @@ impl<'tcx> Rvalue<'tcx> { } } - pub fn ty<D: ?Sized>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> Ty<'tcx> + pub fn ty<D>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> Ty<'tcx> where - D: HasLocalDecls<'tcx>, + D: ?Sized + HasLocalDecls<'tcx>, { match *self { Rvalue::Use(ref operand) => operand.ty(local_decls, tcx), diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 904d78d69b6..b498b7b8912 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -1415,6 +1415,24 @@ impl PlaceContext { ) } + /// Returns `true` if this place context may be used to know the address of the given place. + #[inline] + pub fn may_observe_address(self) -> bool { + matches!( + self, + PlaceContext::NonMutatingUse( + NonMutatingUseContext::SharedBorrow + | NonMutatingUseContext::RawBorrow + | NonMutatingUseContext::FakeBorrow + ) | PlaceContext::MutatingUse( + MutatingUseContext::Drop + | MutatingUseContext::Borrow + | MutatingUseContext::RawBorrow + | MutatingUseContext::AsmOutput + ) + ) + } + /// Returns `true` if this place context represents a storage live or storage dead marker. #[inline] pub fn is_storage_marker(self) -> bool { diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index ea62461ebeb..bea2191c560 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -6,6 +6,7 @@ use rustc_span::ErrorGuaranteed; use crate::mir::interpret::EvalToValTreeResult; use crate::query::CyclePlaceholder; +use crate::traits::solve; use crate::ty::adjustment::CoerceUnsizedInfo; use crate::ty::{self, Ty, TyCtxt}; use crate::{mir, traits}; @@ -219,6 +220,10 @@ impl<T0, T1> EraseType for (&'_ T0, &'_ T1) { type Result = [u8; size_of::<(&'static (), &'static ())>()]; } +impl<T0> EraseType for (solve::QueryResult<'_>, &'_ T0) { + type Result = [u8; size_of::<(solve::QueryResult<'static>, &'static ())>()]; +} + impl<T0, T1> EraseType for (&'_ T0, &'_ [T1]) { type Result = [u8; size_of::<(&'static (), &'static [()])>()]; } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 8ce70f75c67..874cee54c7c 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -131,7 +131,7 @@ use crate::traits::query::{ }; use crate::traits::{ CodegenObligationError, DynCompatibilityViolation, EvaluationResult, ImplSource, - ObligationCause, OverflowError, WellFormedLoc, specialization_graph, + ObligationCause, OverflowError, WellFormedLoc, solve, specialization_graph, }; use crate::ty::fast_reject::SimplifiedType; use crate::ty::layout::ValidityRequirement; @@ -2563,6 +2563,14 @@ rustc_queries! { desc { "computing autoderef types for `{}`", goal.canonical.value.value } } + /// Used by `-Znext-solver` to compute proof trees. + query evaluate_root_goal_for_proof_tree_raw( + goal: solve::CanonicalInput<'tcx>, + ) -> (solve::QueryResult<'tcx>, &'tcx solve::inspect::Probe<TyCtxt<'tcx>>) { + no_hash + desc { "computing proof tree for `{}`", goal.canonical.value.goal.predicate } + } + /// Returns the Rust target features for the current target. These are not always the same as LLVM target features! query rust_target_features(_: CrateNum) -> &'tcx UnordMap<String, rustc_target::target_features::Stability> { arena_cache diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index 95759d1f31a..3f37595d0ee 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -216,10 +216,7 @@ impl<'tcx, E: TyEncoder<'tcx>> Encodable<E> for ty::ParamEnv<'tcx> { #[inline] fn decode_arena_allocable<'tcx, D: TyDecoder<'tcx>, T: ArenaAllocatable<'tcx> + Decodable<D>>( decoder: &mut D, -) -> &'tcx T -where - D: TyDecoder<'tcx>, -{ +) -> &'tcx T { decoder.interner().arena.alloc(Decodable::decode(decoder)) } @@ -230,10 +227,7 @@ fn decode_arena_allocable_slice< T: ArenaAllocatable<'tcx> + Decodable<D>, >( decoder: &mut D, -) -> &'tcx [T] -where - D: TyDecoder<'tcx>, -{ +) -> &'tcx [T] { decoder.interner().arena.alloc_from_iter(<Vec<T> as Decodable<D>>::decode(decoder)) } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 07cfc87250b..de6fa4c344e 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -72,9 +72,9 @@ use crate::query::plumbing::QuerySystem; use crate::query::{IntoQueryParam, LocalCrate, Providers, TyCtxtAt}; use crate::thir::Thir; use crate::traits; -use crate::traits::solve; use crate::traits::solve::{ - ExternalConstraints, ExternalConstraintsData, PredefinedOpaques, PredefinedOpaquesData, + self, CanonicalInput, ExternalConstraints, ExternalConstraintsData, PredefinedOpaques, + PredefinedOpaquesData, QueryResult, inspect, }; use crate::ty::predicate::ExistentialPredicateStableCmpExt as _; use crate::ty::{ @@ -756,6 +756,17 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.opaque_types_defined_by(defining_anchor).iter().chain(coroutines_defined_by), ) } + + type Probe = &'tcx inspect::Probe<TyCtxt<'tcx>>; + fn mk_probe(self, probe: inspect::Probe<Self>) -> &'tcx inspect::Probe<TyCtxt<'tcx>> { + self.arena.alloc(probe) + } + fn evaluate_root_goal_for_proof_tree_raw( + self, + canonical_goal: CanonicalInput<'tcx>, + ) -> (QueryResult<'tcx>, &'tcx inspect::Probe<TyCtxt<'tcx>>) { + self.evaluate_root_goal_for_proof_tree_raw(canonical_goal) + } } macro_rules! bidirectional_lang_item_map { diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs index c7b3b541549..b6b10e24585 100644 --- a/compiler/rustc_middle/src/ty/generics.rs +++ b/compiler/rustc_middle/src/ty/generics.rs @@ -13,7 +13,7 @@ use crate::ty::{EarlyBinder, GenericArgsRef}; pub enum GenericParamDefKind { Lifetime, Type { has_default: bool, synthetic: bool }, - Const { has_default: bool, synthetic: bool }, + Const { has_default: bool }, } impl GenericParamDefKind { diff --git a/compiler/rustc_middle/src/ty/rvalue_scopes.rs b/compiler/rustc_middle/src/ty/rvalue_scopes.rs index 7dfe2d28051..8b92e48ed1a 100644 --- a/compiler/rustc_middle/src/ty/rvalue_scopes.rs +++ b/compiler/rustc_middle/src/ty/rvalue_scopes.rs @@ -35,41 +35,8 @@ impl RvalueScopes { // if there's one. Static items, for instance, won't // have an enclosing scope, hence no scope will be // returned. - let mut id = Scope { local_id: expr_id, data: ScopeData::Node }; - let mut backwards_incompatible = None; - - while let Some(&p) = region_scope_tree.parent_map.get(&id) { - match p.data { - ScopeData::Destruction => { - debug!("temporary_scope({expr_id:?}) = {id:?} [enclosing]"); - return (Some(id), backwards_incompatible); - } - ScopeData::IfThenRescope | ScopeData::MatchGuard => { - debug!("temporary_scope({expr_id:?}) = {p:?} [enclosing]"); - return (Some(p), backwards_incompatible); - } - ScopeData::Node - | ScopeData::CallSite - | ScopeData::Arguments - | ScopeData::IfThen - | ScopeData::Remainder(_) => { - // If we haven't already passed through a backwards-incompatible node, - // then check if we are passing through one now and record it if so. - // This is for now only working for cases where a temporary lifetime is - // *shortened*. - if backwards_incompatible.is_none() { - backwards_incompatible = region_scope_tree - .backwards_incompatible_scope - .get(&p.local_id) - .copied(); - } - id = p - } - } - } - - debug!("temporary_scope({expr_id:?}) = None"); - (None, backwards_incompatible) + region_scope_tree + .default_temporary_scope(Scope { local_id: expr_id, data: ScopeData::Node }) } /// Make an association between a sub-expression and an extended lifetime diff --git a/compiler/rustc_mir_build/Cargo.toml b/compiler/rustc_mir_build/Cargo.toml index 440cb0bdbf3..f756f0a19ee 100644 --- a/compiler/rustc_mir_build/Cargo.toml +++ b/compiler/rustc_mir_build/Cargo.toml @@ -5,9 +5,9 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -itertools.workspace = true +itertools = "0.12" rustc_abi = { path = "../rustc_abi" } -rustc_apfloat.workspace = true +rustc_apfloat = "0.2.0" rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } @@ -23,5 +23,5 @@ rustc_pattern_analysis = { path = "../rustc_pattern_analysis" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_trait_selection = { path = "../rustc_trait_selection" } -tracing.workspace = true +tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_mir_dataflow/Cargo.toml b/compiler/rustc_mir_dataflow/Cargo.toml index 11713bb77f1..9621f9f20bd 100644 --- a/compiler/rustc_mir_dataflow/Cargo.toml +++ b/compiler/rustc_mir_dataflow/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -polonius-engine.workspace = true +polonius-engine = "0.13.0" regex = "1" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } @@ -18,5 +18,5 @@ rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } rustc_span = { path = "../rustc_span" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } -tracing.workspace = true +tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs index 6ec1b03a34e..5eba474a60c 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -92,7 +92,7 @@ impl<'tcx> Visitor<'tcx> for TransferFunction<'_> { } match DefUse::for_place(*place, context) { - Some(DefUse::Def) => { + DefUse::Def => { if let PlaceContext::MutatingUse( MutatingUseContext::Call | MutatingUseContext::AsmOutput, ) = context @@ -105,8 +105,8 @@ impl<'tcx> Visitor<'tcx> for TransferFunction<'_> { self.0.kill(place.local); } } - Some(DefUse::Use) => self.0.gen_(place.local), - None => {} + DefUse::Use => self.0.gen_(place.local), + DefUse::PartialWrite | DefUse::NonUse => {} } self.visit_projection(place.as_ref(), context, location); @@ -131,23 +131,29 @@ impl<'tcx> Visitor<'tcx> for YieldResumeEffect<'_> { } #[derive(Eq, PartialEq, Clone)] -enum DefUse { +pub enum DefUse { + /// Full write to the local. Def, + /// Read of any part of the local. Use, + /// Partial write to the local. + PartialWrite, + /// Non-use, like debuginfo. + NonUse, } impl DefUse { fn apply(state: &mut DenseBitSet<Local>, place: Place<'_>, context: PlaceContext) { match DefUse::for_place(place, context) { - Some(DefUse::Def) => state.kill(place.local), - Some(DefUse::Use) => state.gen_(place.local), - None => {} + DefUse::Def => state.kill(place.local), + DefUse::Use => state.gen_(place.local), + DefUse::PartialWrite | DefUse::NonUse => {} } } - fn for_place(place: Place<'_>, context: PlaceContext) -> Option<DefUse> { + pub fn for_place(place: Place<'_>, context: PlaceContext) -> DefUse { match context { - PlaceContext::NonUse(_) => None, + PlaceContext::NonUse(_) => DefUse::NonUse, PlaceContext::MutatingUse( MutatingUseContext::Call @@ -156,21 +162,20 @@ impl DefUse { | MutatingUseContext::Store | MutatingUseContext::Deinit, ) => { + // Treat derefs as a use of the base local. `*p = 4` is not a def of `p` but a use. if place.is_indirect() { - // Treat derefs as a use of the base local. `*p = 4` is not a def of `p` but a - // use. - Some(DefUse::Use) + DefUse::Use } else if place.projection.is_empty() { - Some(DefUse::Def) + DefUse::Def } else { - None + DefUse::PartialWrite } } // Setting the discriminant is not a use because it does no reading, but it is also not // a def because it does not overwrite the whole place PlaceContext::MutatingUse(MutatingUseContext::SetDiscriminant) => { - place.is_indirect().then_some(DefUse::Use) + if place.is_indirect() { DefUse::Use } else { DefUse::PartialWrite } } // All other contexts are uses... @@ -188,7 +193,7 @@ impl DefUse { | NonMutatingUseContext::PlaceMention | NonMutatingUseContext::FakeBorrow | NonMutatingUseContext::SharedBorrow, - ) => Some(DefUse::Use), + ) => DefUse::Use, PlaceContext::MutatingUse(MutatingUseContext::Projection) | PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) => { diff --git a/compiler/rustc_mir_dataflow/src/impls/mod.rs b/compiler/rustc_mir_dataflow/src/impls/mod.rs index 3f29b819a6d..6d573e1c00e 100644 --- a/compiler/rustc_mir_dataflow/src/impls/mod.rs +++ b/compiler/rustc_mir_dataflow/src/impls/mod.rs @@ -9,7 +9,8 @@ pub use self::initialized::{ MaybeUninitializedPlaces, MaybeUninitializedPlacesDomain, }; pub use self::liveness::{ - MaybeLiveLocals, MaybeTransitiveLiveLocals, TransferFunction as LivenessTransferFunction, + DefUse, MaybeLiveLocals, MaybeTransitiveLiveLocals, + TransferFunction as LivenessTransferFunction, }; pub use self::storage_liveness::{ MaybeRequiresStorage, MaybeStorageDead, MaybeStorageLive, always_storage_live_locals, diff --git a/compiler/rustc_mir_dataflow/src/points.rs b/compiler/rustc_mir_dataflow/src/points.rs index 70d1a34b5fb..e3d1e04a319 100644 --- a/compiler/rustc_mir_dataflow/src/points.rs +++ b/compiler/rustc_mir_dataflow/src/points.rs @@ -1,9 +1,5 @@ -use rustc_index::bit_set::DenseBitSet; -use rustc_index::interval::SparseIntervalMatrix; use rustc_index::{Idx, IndexVec}; -use rustc_middle::mir::{self, BasicBlock, Body, Location}; - -use crate::framework::{Analysis, Results, ResultsVisitor, visit_results}; +use rustc_middle::mir::{BasicBlock, Body, Location}; /// Maps between a `Location` and a `PointIndex` (and vice versa). pub struct DenseLocationMap { @@ -93,65 +89,3 @@ rustc_index::newtype_index! { #[debug_format = "PointIndex({})"] pub struct PointIndex {} } - -/// Add points depending on the result of the given dataflow analysis. -pub fn save_as_intervals<'tcx, N, A>( - elements: &DenseLocationMap, - body: &mir::Body<'tcx>, - mut analysis: A, - results: Results<A::Domain>, -) -> SparseIntervalMatrix<N, PointIndex> -where - N: Idx, - A: Analysis<'tcx, Domain = DenseBitSet<N>>, -{ - let values = SparseIntervalMatrix::new(elements.num_points()); - let mut visitor = Visitor { elements, values }; - visit_results( - body, - body.basic_blocks.reverse_postorder().iter().copied(), - &mut analysis, - &results, - &mut visitor, - ); - visitor.values -} - -struct Visitor<'a, N: Idx> { - elements: &'a DenseLocationMap, - values: SparseIntervalMatrix<N, PointIndex>, -} - -impl<'tcx, A, N> ResultsVisitor<'tcx, A> for Visitor<'_, N> -where - A: Analysis<'tcx, Domain = DenseBitSet<N>>, - N: Idx, -{ - fn visit_after_primary_statement_effect<'mir>( - &mut self, - _analysis: &mut A, - state: &A::Domain, - _statement: &'mir mir::Statement<'tcx>, - location: Location, - ) { - let point = self.elements.point_from_location(location); - // Use internal iterator manually as it is much more efficient. - state.iter().for_each(|node| { - self.values.insert(node, point); - }); - } - - fn visit_after_primary_terminator_effect<'mir>( - &mut self, - _analysis: &mut A, - state: &A::Domain, - _terminator: &'mir mir::Terminator<'tcx>, - location: Location, - ) { - let point = self.elements.point_from_location(location); - // Use internal iterator manually as it is much more efficient. - state.iter().for_each(|node| { - self.values.insert(node, point); - }); - } -} diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index 005e7973130..9a00831dc01 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -6,7 +6,7 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexSet, StdEntry}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_index::IndexVec; use rustc_index::bit_set::DenseBitSet; -use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor}; +use rustc_middle::mir::visit::{PlaceContext, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::{self, Ty, TyCtxt}; use tracing::debug; @@ -917,12 +917,7 @@ pub fn excluded_locals(body: &Body<'_>) -> DenseBitSet<Local> { impl<'tcx> Visitor<'tcx> for Collector { fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: Location) { - if (context.is_borrow() - || context.is_address_of() - || context.is_drop() - || context == PlaceContext::MutatingUse(MutatingUseContext::AsmOutput)) - && !place.is_indirect() - { + if context.may_observe_address() && !place.is_indirect() { // A pointer to a place could be used to access other places with the same local, // hence we have to exclude the local completely. self.result.insert(place.local); diff --git a/compiler/rustc_mir_transform/Cargo.toml b/compiler/rustc_mir_transform/Cargo.toml index 99ef67e2625..08c43a4648c 100644 --- a/compiler/rustc_mir_transform/Cargo.toml +++ b/compiler/rustc_mir_transform/Cargo.toml @@ -5,8 +5,8 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -either.workspace = true -itertools.workspace = true +either = "1" +itertools = "0.12" rustc_abi = { path = "../rustc_abi" } rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } @@ -26,5 +26,5 @@ rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } -tracing.workspace = true +tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_mir_transform/src/check_inline_always_target_features.rs b/compiler/rustc_mir_transform/src/check_inline_always_target_features.rs new file mode 100644 index 00000000000..abad28f0a8f --- /dev/null +++ b/compiler/rustc_mir_transform/src/check_inline_always_target_features.rs @@ -0,0 +1,88 @@ +use rustc_hir::attrs::InlineAttr; +use rustc_middle::middle::codegen_fn_attrs::{TargetFeature, TargetFeatureKind}; +use rustc_middle::mir::{Body, TerminatorKind}; +use rustc_middle::ty::{self, TyCtxt}; + +use crate::pass_manager::MirLint; + +pub(super) struct CheckInlineAlwaysTargetFeature; + +impl<'tcx> MirLint<'tcx> for CheckInlineAlwaysTargetFeature { + fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { + check_inline_always_target_features(tcx, body) + } +} + +/// `#[target_feature]`-annotated functions can be marked `#[inline]` and will only be inlined if +/// the target features match (as well as all of the other inlining heuristics). `#[inline(always)]` +/// will always inline regardless of matching target features, which can result in errors from LLVM. +/// However, it is desirable to be able to always annotate certain functions (e.g. SIMD intrinsics) +/// as `#[inline(always)]` but check the target features match in Rust to avoid the LLVM errors. +/// +/// We check the caller and callee target features to ensure that this can +/// be done or emit a lint. +#[inline] +fn check_inline_always_target_features<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { + let caller_def_id = body.source.def_id().expect_local(); + if !tcx.def_kind(caller_def_id).has_codegen_attrs() { + return; + } + + let caller_codegen_fn_attrs = tcx.codegen_fn_attrs(caller_def_id); + + for bb in body.basic_blocks.iter() { + let terminator = bb.terminator(); + match &terminator.kind { + TerminatorKind::Call { func, .. } | TerminatorKind::TailCall { func, .. } => { + let fn_ty = func.ty(body, tcx); + let ty::FnDef(callee_def_id, _) = *fn_ty.kind() else { + continue; + }; + + if !tcx.def_kind(callee_def_id).has_codegen_attrs() { + continue; + } + let callee_codegen_fn_attrs = tcx.codegen_fn_attrs(callee_def_id); + if callee_codegen_fn_attrs.inline != InlineAttr::Always + || callee_codegen_fn_attrs.target_features.is_empty() + { + continue; + } + + // Scan the users defined target features and ensure they + // match the caller. + if tcx.is_target_feature_call_safe( + &callee_codegen_fn_attrs.target_features, + &caller_codegen_fn_attrs + .target_features + .iter() + .cloned() + .chain(tcx.sess.target_features.iter().map(|feat| TargetFeature { + name: *feat, + kind: TargetFeatureKind::Implied, + })) + .collect::<Vec<_>>(), + ) { + continue; + } + + let callee_only: Vec<_> = callee_codegen_fn_attrs + .target_features + .iter() + .filter(|it| !caller_codegen_fn_attrs.target_features.contains(it)) + .filter(|it| !matches!(it.kind, TargetFeatureKind::Implied)) + .map(|it| it.name.as_str()) + .collect(); + + crate::errors::emit_inline_always_target_feature_diagnostic( + tcx, + terminator.source_info.span, + callee_def_id, + caller_def_id.into(), + &callee_only, + ); + } + _ => (), + } + } +} diff --git a/compiler/rustc_mir_transform/src/coverage/expansion.rs b/compiler/rustc_mir_transform/src/coverage/expansion.rs index 91e0528f52f..851bbaeed48 100644 --- a/compiler/rustc_mir_transform/src/coverage/expansion.rs +++ b/compiler/rustc_mir_transform/src/coverage/expansion.rs @@ -82,7 +82,7 @@ impl ExpnNode { Self { expn_id, - expn_kind: expn_data.kind.clone(), + expn_kind: expn_data.kind, call_site, call_site_expn_id, diff --git a/compiler/rustc_mir_transform/src/cross_crate_inline.rs b/compiler/rustc_mir_transform/src/cross_crate_inline.rs index b186c2bd775..df98c07f549 100644 --- a/compiler/rustc_mir_transform/src/cross_crate_inline.rs +++ b/compiler/rustc_mir_transform/src/cross_crate_inline.rs @@ -135,7 +135,16 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> { } } } - TerminatorKind::Call { unwind, .. } => { + TerminatorKind::Call { ref func, unwind, .. } => { + // We track calls because they make our function not a leaf (and in theory, the + // number of calls indicates how likely this function is to perturb other CGUs). + // But intrinsics don't have a body that gets assigned to a CGU, so they are + // ignored. + if let Some((fn_def_id, _)) = func.const_fn_def() + && self.tcx.has_attr(fn_def_id, sym::rustc_intrinsic) + { + return; + } self.calls += 1; if let UnwindAction::Cleanup(_) = unwind { self.landing_pads += 1; diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index cf7425251e8..9ba2d274691 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -59,6 +59,12 @@ //! The first two conditions are simple structural requirements on the `Assign` statements that can //! be trivially checked. The third requirement however is more difficult and costly to check. //! +//! ## Current implementation +//! +//! The current implementation relies on live range computation to check for conflicts. We only +//! allow to merge locals that have disjoint live ranges. The live range are defined with +//! half-statement granularity, so as to make all writes be live for at least a half statement. +//! //! ## Future Improvements //! //! There are a number of ways in which this pass could be improved in the future: @@ -117,9 +123,8 @@ //! - Layout optimizations for coroutines have been added to improve code generation for //! async/await, which are very similar in spirit to what this optimization does. //! -//! Also, rustc now has a simple NRVO pass (see `nrvo.rs`), which handles a subset of the cases that -//! this destination propagation pass handles, proving that similar optimizations can be performed -//! on MIR. +//! [The next approach][attempt 4] computes a conflict matrix between locals by forbidding merging +//! locals with competing writes or with one write while the other is live. //! //! ## Pre/Post Optimization //! @@ -130,20 +135,18 @@ //! [attempt 1]: https://github.com/rust-lang/rust/pull/47954 //! [attempt 2]: https://github.com/rust-lang/rust/pull/71003 //! [attempt 3]: https://github.com/rust-lang/rust/pull/72632 +//! [attempt 4]: https://github.com/rust-lang/rust/pull/96451 -use rustc_data_structures::fx::{FxIndexMap, IndexEntry, IndexOccupiedEntry}; +use rustc_data_structures::union_find::UnionFind; use rustc_index::bit_set::DenseBitSet; use rustc_index::interval::SparseIntervalMatrix; -use rustc_middle::bug; +use rustc_index::{IndexVec, newtype_index}; use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor}; -use rustc_middle::mir::{ - Body, HasLocalDecls, InlineAsmOperand, Local, LocalKind, Location, MirDumper, Operand, - PassWhere, Place, Rvalue, Statement, StatementKind, TerminatorKind, traversal, -}; +use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; -use rustc_mir_dataflow::Analysis; -use rustc_mir_dataflow::impls::MaybeLiveLocals; -use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex, save_as_intervals}; +use rustc_mir_dataflow::impls::{DefUse, MaybeLiveLocals}; +use rustc_mir_dataflow::points::DenseLocationMap; +use rustc_mir_dataflow::{Analysis, Results}; use tracing::{debug, trace}; pub(super) struct DestinationPropagation; @@ -161,84 +164,81 @@ impl<'tcx> crate::MirPass<'tcx> for DestinationPropagation { sess.mir_opt_level() >= 3 } + #[tracing::instrument(level = "trace", skip(self, tcx, body))] fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let def_id = body.source.def_id(); - let mut candidates = Candidates::default(); - let mut write_info = WriteInfo::default(); - trace!(func = ?tcx.def_path_str(def_id)); + trace!(?def_id); let borrowed = rustc_mir_dataflow::impls::borrowed_locals(body); + let candidates = Candidates::find(body, &borrowed); + trace!(?candidates); + if candidates.c.is_empty() { + return; + } + let live = MaybeLiveLocals.iterate_to_fixpoint(tcx, body, Some("MaybeLiveLocals-DestProp")); + let points = DenseLocationMap::new(body); - let mut live = save_as_intervals(&points, body, live.analysis, live.results); - - // In order to avoid having to collect data for every single pair of locals in the body, we - // do not allow doing more than one merge for places that are derived from the same local at - // once. To avoid missed opportunities, we instead iterate to a fixed point - we'll refer to - // each of these iterations as a "round." - // - // Reaching a fixed point could in theory take up to `min(l, s)` rounds - however, we do not - // expect to see MIR like that. To verify this, a test was run against `[rust-lang/regex]` - - // the average MIR body saw 1.32 full iterations of this loop. The most that was hit were 30 - // for a single function. Only 80/2801 (2.9%) of functions saw at least 5. - // - // [rust-lang/regex]: - // https://github.com/rust-lang/regex/tree/b5372864e2df6a2f5e543a556a62197f50ca3650 - let mut round_count = 0; - loop { - // PERF: Can we do something smarter than recalculating the candidates and liveness - // results? - candidates.reset_and_find(body, &borrowed); - trace!(?candidates); - dest_prop_mir_dump(tcx, body, &points, &live, round_count); - - FilterInformation::filter_liveness( - &mut candidates, - &points, - &live, - &mut write_info, - body, - ); - - // Because we only filter once per round, it is unsound to use a local for more than - // one merge operation within a single round of optimizations. We store here which ones - // we have already used. - let mut merged_locals: DenseBitSet<Local> = - DenseBitSet::new_empty(body.local_decls.len()); - - // This is the set of merges we will apply this round. It is a subset of the candidates. - let mut merges = FxIndexMap::default(); - - for (src, candidates) in candidates.c.iter() { - if merged_locals.contains(*src) { - continue; - } - let Some(dest) = candidates.iter().find(|dest| !merged_locals.contains(**dest)) - else { - continue; - }; - - // Replace `src` by `dest` everywhere. - merges.insert(*src, *dest); - merged_locals.insert(*src); - merged_locals.insert(*dest); - - // Update liveness information based on the merge we just performed. - // Every location where `src` was live, `dest` will be live. - live.union_rows(*src, *dest); + let mut relevant = RelevantLocals::compute(&candidates, body.local_decls.len()); + let mut live = save_as_intervals(&points, body, &relevant, live.results); + + dest_prop_mir_dump(tcx, body, &points, &live, &relevant); + + let mut merged_locals = DenseBitSet::new_empty(body.local_decls.len()); + + for (src, dst) in candidates.c.into_iter() { + trace!(?src, ?dst); + + let Some(mut src) = relevant.find(src) else { continue }; + let Some(mut dst) = relevant.find(dst) else { continue }; + if src == dst { + continue; } - trace!(merging = ?merges); - if merges.is_empty() { - break; + let Some(src_live_ranges) = live.row(src) else { continue }; + let Some(dst_live_ranges) = live.row(dst) else { continue }; + trace!(?src, ?src_live_ranges); + trace!(?dst, ?dst_live_ranges); + + if src_live_ranges.disjoint(dst_live_ranges) { + // We want to replace `src` by `dst`. + let mut orig_src = relevant.original[src]; + let mut orig_dst = relevant.original[dst]; + + // The return place and function arguments are required and cannot be renamed. + // This check cannot be made during candidate collection, as we may want to + // unify the same non-required local with several required locals. + match (is_local_required(orig_src, body), is_local_required(orig_dst, body)) { + // Renaming `src` is ok. + (false, _) => {} + // Renaming `src` is wrong, but renaming `dst` is ok. + (true, false) => { + std::mem::swap(&mut src, &mut dst); + std::mem::swap(&mut orig_src, &mut orig_dst); + } + // Neither local can be renamed, so skip this case. + (true, true) => continue, + } + + trace!(?src, ?dst, "merge"); + merged_locals.insert(orig_src); + merged_locals.insert(orig_dst); + + // Replace `src` by `dst`. + let head = relevant.union(src, dst); + live.union_rows(/* read */ src, /* write */ head); + live.union_rows(/* read */ dst, /* write */ head); } - round_count += 1; + } + trace!(?merged_locals); + trace!(?relevant.renames); - apply_merges(body, tcx, merges, merged_locals); + if merged_locals.is_empty() { + return; } - trace!(round_count); + apply_merges(body, tcx, relevant, merged_locals); } fn is_required(&self) -> bool { @@ -246,30 +246,6 @@ impl<'tcx> crate::MirPass<'tcx> for DestinationPropagation { } } -#[derive(Debug, Default)] -struct Candidates { - /// The set of candidates we are considering in this optimization. - /// - /// We will always merge the key into at most one of its values. - /// - /// Whether a place ends up in the key or the value does not correspond to whether it appears as - /// the lhs or rhs of any assignment. As a matter of fact, the places in here might never appear - /// in an assignment at all. This happens because if we see an assignment like this: - /// - /// ```ignore (syntax-highlighting-only) - /// _1.0 = _2.0 - /// ``` - /// - /// We will still report that we would like to merge `_1` and `_2` in an attempt to allow us to - /// remove that assignment. - c: FxIndexMap<Local, Vec<Local>>, - - /// A reverse index of the `c` set; if the `c` set contains `a => Place { local: b, proj }`, - /// then this contains `b => a`. - // PERF: Possibly these should be `SmallVec`s? - reverse: FxIndexMap<Local, Vec<Local>>, -} - ////////////////////////////////////////////////////////// // Merging // @@ -278,16 +254,16 @@ struct Candidates { fn apply_merges<'tcx>( body: &mut Body<'tcx>, tcx: TyCtxt<'tcx>, - merges: FxIndexMap<Local, Local>, + relevant: RelevantLocals, merged_locals: DenseBitSet<Local>, ) { - let mut merger = Merger { tcx, merges, merged_locals }; + let mut merger = Merger { tcx, relevant, merged_locals }; merger.visit_body_preserves_cfg(body); } struct Merger<'tcx> { tcx: TyCtxt<'tcx>, - merges: FxIndexMap<Local, Local>, + relevant: RelevantLocals, merged_locals: DenseBitSet<Local>, } @@ -297,8 +273,8 @@ impl<'tcx> MutVisitor<'tcx> for Merger<'tcx> { } fn visit_local(&mut self, local: &mut Local, _: PlaceContext, _location: Location) { - if let Some(dest) = self.merges.get(local) { - *local = *dest; + if let Some(relevant) = self.relevant.find(*local) { + *local = self.relevant.original[relevant]; } } @@ -336,414 +312,95 @@ impl<'tcx> MutVisitor<'tcx> for Merger<'tcx> { } ////////////////////////////////////////////////////////// -// Liveness filtering +// Relevant locals // -// This section enforces bullet point 2 +// Small utility to reduce size of the conflict matrix by only considering locals that appear in +// the candidates -struct FilterInformation<'a, 'tcx> { - body: &'a Body<'tcx>, - points: &'a DenseLocationMap, - live: &'a SparseIntervalMatrix<Local, PointIndex>, - candidates: &'a mut Candidates, - write_info: &'a mut WriteInfo, - at: Location, +newtype_index! { + /// Represent a subset of locals which appear in candidates. + struct RelevantLocal {} } -// We first implement some utility functions which we will expose removing candidates according to -// different needs. Throughout the liveness filtering, the `candidates` are only ever accessed -// through these methods, and not directly. -impl Candidates { - /// Collects the candidates for merging. - /// - /// This is responsible for enforcing the first and third bullet point. - fn reset_and_find<'tcx>(&mut self, body: &Body<'tcx>, borrowed: &DenseBitSet<Local>) { - self.c.clear(); - self.reverse.clear(); - let mut visitor = FindAssignments { body, candidates: &mut self.c, borrowed }; - visitor.visit_body(body); - // Deduplicate candidates. - for (_, cands) in self.c.iter_mut() { - cands.sort(); - cands.dedup(); - } - // Generate the reverse map. - for (src, cands) in self.c.iter() { - for dest in cands.iter().copied() { - self.reverse.entry(dest).or_default().push(*src); - } - } - } - - /// Just `Vec::retain`, but the condition is inverted and we add debugging output - fn vec_filter_candidates( - src: Local, - v: &mut Vec<Local>, - mut f: impl FnMut(Local) -> CandidateFilter, - at: Location, - ) { - v.retain(|dest| { - let remove = f(*dest); - if remove == CandidateFilter::Remove { - trace!("eliminating {:?} => {:?} due to conflict at {:?}", src, dest, at); - } - remove == CandidateFilter::Keep - }); - } - - /// `vec_filter_candidates` but for an `Entry` - fn entry_filter_candidates( - mut entry: IndexOccupiedEntry<'_, Local, Vec<Local>>, - p: Local, - f: impl FnMut(Local) -> CandidateFilter, - at: Location, - ) { - let candidates = entry.get_mut(); - Self::vec_filter_candidates(p, candidates, f, at); - if candidates.len() == 0 { - // FIXME(#120456) - is `swap_remove` correct? - entry.swap_remove(); - } - } - - /// For all candidates `(p, q)` or `(q, p)` removes the candidate if `f(q)` says to do so - fn filter_candidates_by( - &mut self, - p: Local, - mut f: impl FnMut(Local) -> CandidateFilter, - at: Location, - ) { - // Cover the cases where `p` appears as a `src` - if let IndexEntry::Occupied(entry) = self.c.entry(p) { - Self::entry_filter_candidates(entry, p, &mut f, at); - } - // And the cases where `p` appears as a `dest` - let Some(srcs) = self.reverse.get_mut(&p) else { - return; - }; - // We use `retain` here to remove the elements from the reverse set if we've removed the - // matching candidate in the forward set. - srcs.retain(|src| { - if f(*src) == CandidateFilter::Keep { - return true; - } - let IndexEntry::Occupied(entry) = self.c.entry(*src) else { - return false; - }; - Self::entry_filter_candidates( - entry, - *src, - |dest| { - if dest == p { CandidateFilter::Remove } else { CandidateFilter::Keep } - }, - at, - ); - false - }); - } +#[derive(Debug)] +struct RelevantLocals { + original: IndexVec<RelevantLocal, Local>, + shrink: IndexVec<Local, Option<RelevantLocal>>, + renames: UnionFind<RelevantLocal>, } -#[derive(Copy, Clone, PartialEq, Eq)] -enum CandidateFilter { - Keep, - Remove, -} +impl RelevantLocals { + #[tracing::instrument(level = "trace", skip(candidates, num_locals), ret)] + fn compute(candidates: &Candidates, num_locals: usize) -> RelevantLocals { + let mut original = IndexVec::with_capacity(candidates.c.len()); + let mut shrink = IndexVec::from_elem_n(None, num_locals); -impl<'a, 'tcx> FilterInformation<'a, 'tcx> { - /// Filters the set of candidates to remove those that conflict. - /// - /// The steps we take are exactly those that are outlined at the top of the file. For each - /// statement/terminator, we collect the set of locals that are written to in that - /// statement/terminator, and then we remove all pairs of candidates that contain one such local - /// and another one that is live. - /// - /// We need to be careful about the ordering of operations within each statement/terminator - /// here. Many statements might write and read from more than one place, and we need to consider - /// them all. The strategy for doing this is as follows: We first gather all the places that are - /// written to within the statement/terminator via `WriteInfo`. Then, we use the liveness - /// analysis from *before* the statement/terminator (in the control flow sense) to eliminate - /// candidates - this is because we want to conservatively treat a pair of locals that is both - /// read and written in the statement/terminator to be conflicting, and the liveness analysis - /// before the statement/terminator will correctly report locals that are read in the - /// statement/terminator to be live. We are additionally conservative by treating all written to - /// locals as also being read from. - fn filter_liveness( - candidates: &mut Candidates, - points: &DenseLocationMap, - live: &SparseIntervalMatrix<Local, PointIndex>, - write_info: &mut WriteInfo, - body: &Body<'tcx>, - ) { - let mut this = FilterInformation { - body, - points, - live, - candidates, - // We don't actually store anything at this scope, we just keep things here to be able - // to reuse the allocation. - write_info, - // Doesn't matter what we put here, will be overwritten before being used - at: Location::START, + // Mark a local as relevant and record it into the maps. + let mut declare = |local| { + shrink.get_or_insert_with(local, || original.push(local)); }; - this.internal_filter_liveness(); - } - fn internal_filter_liveness(&mut self) { - for (block, data) in traversal::preorder(self.body) { - self.at = Location { block, statement_index: data.statements.len() }; - self.write_info.for_terminator(&data.terminator().kind); - self.apply_conflicts(); - - for (i, statement) in data.statements.iter().enumerate().rev() { - self.at = Location { block, statement_index: i }; - self.write_info.for_statement(&statement.kind, self.body); - self.apply_conflicts(); - } + for &(src, dest) in candidates.c.iter() { + declare(src); + declare(dest) } - } - fn apply_conflicts(&mut self) { - let writes = &self.write_info.writes; - for p in writes { - let other_skip = self.write_info.skip_pair.and_then(|(a, b)| { - if a == *p { - Some(b) - } else if b == *p { - Some(a) - } else { - None - } - }); - let at = self.points.point_from_location(self.at); - self.candidates.filter_candidates_by( - *p, - |q| { - if Some(q) == other_skip { - return CandidateFilter::Keep; - } - // It is possible that a local may be live for less than the - // duration of a statement This happens in the case of function - // calls or inline asm. Because of this, we also mark locals as - // conflicting when both of them are written to in the same - // statement. - if self.live.contains(q, at) || writes.contains(&q) { - CandidateFilter::Remove - } else { - CandidateFilter::Keep - } - }, - self.at, - ); - } + let renames = UnionFind::new(original.len()); + RelevantLocals { original, shrink, renames } } -} -/// Describes where a statement/terminator writes to -#[derive(Default, Debug)] -struct WriteInfo { - writes: Vec<Local>, - /// If this pair of locals is a candidate pair, completely skip processing it during this - /// statement. All other candidates are unaffected. - skip_pair: Option<(Local, Local)>, -} - -impl WriteInfo { - fn for_statement<'tcx>(&mut self, statement: &StatementKind<'tcx>, body: &Body<'tcx>) { - self.reset(); - match statement { - StatementKind::Assign(box (lhs, rhs)) => { - self.add_place(*lhs); - match rhs { - Rvalue::Use(op) => { - self.add_operand(op); - self.consider_skipping_for_assign_use(*lhs, op, body); - } - Rvalue::Repeat(op, _) => { - self.add_operand(op); - } - Rvalue::Cast(_, op, _) - | Rvalue::UnaryOp(_, op) - | Rvalue::ShallowInitBox(op, _) => { - self.add_operand(op); - } - Rvalue::BinaryOp(_, ops) => { - for op in [&ops.0, &ops.1] { - self.add_operand(op); - } - } - Rvalue::Aggregate(_, ops) => { - for op in ops { - self.add_operand(op); - } - } - Rvalue::WrapUnsafeBinder(op, _) => { - self.add_operand(op); - } - Rvalue::ThreadLocalRef(_) - | Rvalue::NullaryOp(_, _) - | Rvalue::Ref(_, _, _) - | Rvalue::RawPtr(_, _) - | Rvalue::Len(_) - | Rvalue::Discriminant(_) - | Rvalue::CopyForDeref(_) => {} - } - } - // Retags are technically also reads, but reporting them as a write suffices - StatementKind::SetDiscriminant { place, .. } - | StatementKind::Deinit(place) - | StatementKind::Retag(_, place) => { - self.add_place(**place); - } - StatementKind::Intrinsic(_) - | StatementKind::ConstEvalCounter - | StatementKind::Nop - | StatementKind::Coverage(_) - | StatementKind::StorageLive(_) - | StatementKind::StorageDead(_) - | StatementKind::BackwardIncompatibleDropHint { .. } - | StatementKind::PlaceMention(_) => {} - StatementKind::FakeRead(_) | StatementKind::AscribeUserType(_, _) => { - bug!("{:?} not found in this MIR phase", statement) - } - } + fn find(&mut self, src: Local) -> Option<RelevantLocal> { + let src = self.shrink[src]?; + let src = self.renames.find(src); + Some(src) } - fn consider_skipping_for_assign_use<'tcx>( - &mut self, - lhs: Place<'tcx>, - rhs: &Operand<'tcx>, - body: &Body<'tcx>, - ) { - let Some(rhs) = rhs.place() else { return }; - if let Some(pair) = places_to_candidate_pair(lhs, rhs, body) { - self.skip_pair = Some(pair); - } - } - - fn for_terminator<'tcx>(&mut self, terminator: &TerminatorKind<'tcx>) { - self.reset(); - match terminator { - TerminatorKind::SwitchInt { discr: op, .. } - | TerminatorKind::Assert { cond: op, .. } => { - self.add_operand(op); - } - TerminatorKind::Call { destination, func, args, .. } => { - self.add_place(*destination); - self.add_operand(func); - for arg in args { - self.add_operand(&arg.node); - } - } - TerminatorKind::TailCall { func, args, .. } => { - self.add_operand(func); - for arg in args { - self.add_operand(&arg.node); - } - } - TerminatorKind::InlineAsm { operands, .. } => { - for asm_operand in operands { - match asm_operand { - InlineAsmOperand::In { value, .. } => { - self.add_operand(value); - } - InlineAsmOperand::Out { place, .. } => { - if let Some(place) = place { - self.add_place(*place); - } - } - // Note that the `late` field in `InOut` is about whether the registers used - // for these things overlap, and is of absolutely no interest to us. - InlineAsmOperand::InOut { in_value, out_place, .. } => { - if let Some(place) = out_place { - self.add_place(*place); - } - self.add_operand(in_value); - } - InlineAsmOperand::Const { .. } - | InlineAsmOperand::SymFn { .. } - | InlineAsmOperand::SymStatic { .. } - | InlineAsmOperand::Label { .. } => {} - } - } - } - TerminatorKind::Goto { .. } - | TerminatorKind::UnwindResume - | TerminatorKind::UnwindTerminate(_) - | TerminatorKind::Return - | TerminatorKind::Unreachable { .. } => (), - TerminatorKind::Drop { .. } => { - // `Drop`s create a `&mut` and so are not considered - } - TerminatorKind::Yield { .. } - | TerminatorKind::CoroutineDrop - | TerminatorKind::FalseEdge { .. } - | TerminatorKind::FalseUnwind { .. } => { - bug!("{:?} not found in this MIR phase", terminator) - } - } - } - - fn add_place(&mut self, place: Place<'_>) { - self.writes.push(place.local); - } - - fn add_operand<'tcx>(&mut self, op: &Operand<'tcx>) { - match op { - // FIXME(JakobDegen): In a previous version, the `Move` case was incorrectly treated as - // being a read only. This was unsound, however we cannot add a regression test because - // it is not possible to set this off with current MIR. Once we have that ability, a - // regression test should be added. - Operand::Move(p) => self.add_place(*p), - Operand::Copy(_) | Operand::Constant(_) => (), - } - } - - fn reset(&mut self) { - self.writes.clear(); - self.skip_pair = None; + fn union(&mut self, lhs: RelevantLocal, rhs: RelevantLocal) -> RelevantLocal { + let head = self.renames.unify(lhs, rhs); + // We need to ensure we keep the original local of the RHS, as it may be a required local. + self.original[head] = self.original[rhs]; + head } } ///////////////////////////////////////////////////// // Candidate accumulation -/// If the pair of places is being considered for merging, returns the candidate which would be -/// merged in order to accomplish this. -/// -/// The contract here is in one direction - there is a guarantee that merging the locals that are -/// outputted by this function would result in an assignment between the inputs becoming a -/// self-assignment. However, there is no guarantee that the returned pair is actually suitable for -/// merging - candidate collection must still check this independently. -/// -/// This output is unique for each unordered pair of input places. -fn places_to_candidate_pair<'tcx>( - a: Place<'tcx>, - b: Place<'tcx>, - body: &Body<'tcx>, -) -> Option<(Local, Local)> { - let (mut a, mut b) = if a.projection.len() == 0 && b.projection.len() == 0 { - (a.local, b.local) - } else { - return None; - }; +#[derive(Debug, Default)] +struct Candidates { + /// The set of candidates we are considering in this optimization. + /// + /// Whether a place ends up in the key or the value does not correspond to whether it appears as + /// the lhs or rhs of any assignment. As a matter of fact, the places in here might never appear + /// in an assignment at all. This happens because if we see an assignment like this: + /// + /// ```ignore (syntax-highlighting-only) + /// _1.0 = _2.0 + /// ``` + /// + /// We will still report that we would like to merge `_1` and `_2` in an attempt to allow us to + /// remove that assignment. + c: Vec<(Local, Local)>, +} - // By sorting, we make sure we're input order independent - if a > b { - std::mem::swap(&mut a, &mut b); - } +// We first implement some utility functions which we will expose removing candidates according to +// different needs. Throughout the liveness filtering, the `candidates` are only ever accessed +// through these methods, and not directly. +impl Candidates { + /// Collects the candidates for merging. + /// + /// This is responsible for enforcing the first and third bullet point. + fn find(body: &Body<'_>, borrowed: &DenseBitSet<Local>) -> Candidates { + let mut visitor = FindAssignments { body, candidates: Default::default(), borrowed }; + visitor.visit_body(body); - // We could now return `(a, b)`, but then we miss some candidates in the case where `a` can't be - // used as a `src`. - if is_local_required(a, body) { - std::mem::swap(&mut a, &mut b); + Candidates { c: visitor.candidates } } - // We could check `is_local_required` again here, but there's no need - after all, we make no - // promise that the candidate pair is actually valid - Some((a, b)) } struct FindAssignments<'a, 'tcx> { body: &'a Body<'tcx>, - candidates: &'a mut FxIndexMap<Local, Vec<Local>>, + candidates: Vec<(Local, Local)>, borrowed: &'a DenseBitSet<Local>, } @@ -753,11 +410,9 @@ impl<'tcx> Visitor<'tcx> for FindAssignments<'_, 'tcx> { lhs, Rvalue::CopyForDeref(rhs) | Rvalue::Use(Operand::Copy(rhs) | Operand::Move(rhs)), )) = &statement.kind + && let Some(src) = lhs.as_local() + && let Some(dest) = rhs.as_local() { - let Some((src, dest)) = places_to_candidate_pair(*lhs, *rhs, self.body) else { - return; - }; - // As described at the top of the file, we do not go near things that have // their address taken. if self.borrowed.contains(src) || self.borrowed.contains(dest) { @@ -774,13 +429,8 @@ impl<'tcx> Visitor<'tcx> for FindAssignments<'_, 'tcx> { return; } - // Also, we need to make sure that MIR actually allows the `src` to be removed - if is_local_required(src, self.body) { - return; - } - // We may insert duplicates here, but that's fine - self.candidates.entry(src).or_default().push(dest); + self.candidates.push((src, dest)); } } } @@ -803,22 +453,162 @@ fn dest_prop_mir_dump<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, points: &DenseLocationMap, - live: &SparseIntervalMatrix<Local, PointIndex>, - round: usize, + live: &SparseIntervalMatrix<RelevantLocal, TwoStepIndex>, + relevant: &RelevantLocals, ) { let locals_live_at = |location| { - let location = points.point_from_location(location); - live.rows().filter(|&r| live.contains(r, location)).collect::<Vec<_>>() + live.rows() + .filter(|&r| live.contains(r, location)) + .map(|rl| relevant.original[rl]) + .collect::<Vec<_>>() }; if let Some(dumper) = MirDumper::new(tcx, "DestinationPropagation-dataflow", body) { let extra_data = &|pass_where, w: &mut dyn std::io::Write| { if let PassWhere::BeforeLocation(loc) = pass_where { - writeln!(w, " // live: {:?}", locals_live_at(loc))?; + let location = TwoStepIndex::new(points, loc, Effect::Before); + let live = locals_live_at(location); + writeln!(w, " // before: {:?} => {:?}", location, live)?; + } + if let PassWhere::AfterLocation(loc) = pass_where { + let location = TwoStepIndex::new(points, loc, Effect::After); + let live = locals_live_at(location); + writeln!(w, " // after: {:?} => {:?}", location, live)?; } Ok(()) }; - dumper.set_disambiguator(&round).set_extra_data(extra_data).dump_mir(body) + dumper.set_extra_data(extra_data).dump_mir(body) } } + +#[derive(Copy, Clone, Debug)] +enum Effect { + Before, + After, +} + +rustc_index::newtype_index! { + /// A reversed `PointIndex` but with the lower bit encoding early/late inside the statement. + /// The reversed order allows to use the more efficient `IntervalSet::append` method while we + /// iterate on the statements in reverse order. + #[orderable] + #[debug_format = "TwoStepIndex({})"] + struct TwoStepIndex {} +} + +impl TwoStepIndex { + fn new(elements: &DenseLocationMap, location: Location, effect: Effect) -> TwoStepIndex { + let point = elements.point_from_location(location); + let effect = match effect { + Effect::Before => 0, + Effect::After => 1, + }; + let max_index = 2 * elements.num_points() as u32 - 1; + let index = 2 * point.as_u32() + (effect as u32); + // Reverse the indexing to use more efficient `IntervalSet::append`. + TwoStepIndex::from_u32(max_index - index) + } +} + +struct VisitPlacesWith<F>(F); + +impl<'tcx, F> Visitor<'tcx> for VisitPlacesWith<F> +where + F: FnMut(Place<'tcx>, PlaceContext), +{ + fn visit_local(&mut self, local: Local, ctxt: PlaceContext, _: Location) { + (self.0)(local.into(), ctxt); + } + + fn visit_place(&mut self, place: &Place<'tcx>, ctxt: PlaceContext, location: Location) { + (self.0)(*place, ctxt); + self.visit_projection(place.as_ref(), ctxt, location); + } +} + +/// Add points depending on the result of the given dataflow analysis. +fn save_as_intervals<'tcx>( + elements: &DenseLocationMap, + body: &Body<'tcx>, + relevant: &RelevantLocals, + results: Results<DenseBitSet<Local>>, +) -> SparseIntervalMatrix<RelevantLocal, TwoStepIndex> { + let mut values = SparseIntervalMatrix::new(2 * elements.num_points()); + let mut state = MaybeLiveLocals.bottom_value(body); + let reachable_blocks = traversal::reachable_as_bitset(body); + + let two_step_loc = |location, effect| TwoStepIndex::new(elements, location, effect); + let append_at = + |values: &mut SparseIntervalMatrix<_, _>, state: &DenseBitSet<Local>, twostep| { + for (relevant, &original) in relevant.original.iter_enumerated() { + if state.contains(original) { + values.append(relevant, twostep); + } + } + }; + + // Iterate blocks in decreasing order, to visit locations in decreasing order. This + // allows to use the more efficient `append` method to interval sets. + for block in body.basic_blocks.indices().rev() { + if !reachable_blocks.contains(block) { + continue; + } + + state.clone_from(&results[block]); + + let block_data = &body.basic_blocks[block]; + let loc = Location { block, statement_index: block_data.statements.len() }; + + let term = block_data.terminator(); + let mut twostep = two_step_loc(loc, Effect::After); + append_at(&mut values, &state, twostep); + // Ensure we have a non-zero live range even for dead stores. This is done by marking all + // the written-to locals as live in the second half of the statement. + // We also ensure that operands read by terminators conflict with writes by that terminator. + // For instance a function call may read args after having written to the destination. + VisitPlacesWith(|place, ctxt| match DefUse::for_place(place, ctxt) { + DefUse::Def | DefUse::Use | DefUse::PartialWrite => { + if let Some(relevant) = relevant.shrink[place.local] { + values.insert(relevant, twostep); + } + } + DefUse::NonUse => {} + }) + .visit_terminator(term, loc); + + twostep = TwoStepIndex::from_u32(twostep.as_u32() + 1); + debug_assert_eq!(twostep, two_step_loc(loc, Effect::Before)); + MaybeLiveLocals.apply_early_terminator_effect(&mut state, term, loc); + MaybeLiveLocals.apply_primary_terminator_effect(&mut state, term, loc); + append_at(&mut values, &state, twostep); + + for (statement_index, stmt) in block_data.statements.iter().enumerate().rev() { + let loc = Location { block, statement_index }; + twostep = TwoStepIndex::from_u32(twostep.as_u32() + 1); + debug_assert_eq!(twostep, two_step_loc(loc, Effect::After)); + append_at(&mut values, &state, twostep); + // Ensure we have a non-zero live range even for dead stores. This is done by marking + // all the written-to locals as live in the second half of the statement. + VisitPlacesWith(|place, ctxt| match DefUse::for_place(place, ctxt) { + DefUse::Def | DefUse::PartialWrite => { + if let Some(relevant) = relevant.shrink[place.local] { + values.insert(relevant, twostep); + } + } + DefUse::Use | DefUse::NonUse => {} + }) + .visit_statement(stmt, loc); + + twostep = TwoStepIndex::from_u32(twostep.as_u32() + 1); + debug_assert_eq!(twostep, two_step_loc(loc, Effect::Before)); + MaybeLiveLocals.apply_early_statement_effect(&mut state, stmt, loc); + MaybeLiveLocals.apply_primary_statement_effect(&mut state, stmt, loc); + // ... but reads from operands are marked as live here so they do not conflict with + // the all the writes we manually marked as live in the second half of the statement. + append_at(&mut values, &state, twostep); + } + } + + values +} diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs index ad9635aae33..775f5f9a7cf 100644 --- a/compiler/rustc_mir_transform/src/errors.rs +++ b/compiler/rustc_mir_transform/src/errors.rs @@ -2,6 +2,7 @@ use rustc_errors::codes::*; use rustc_errors::{Diag, LintDiagnostic}; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::mir::AssertKind; +use rustc_middle::query::Key; use rustc_middle::ty::TyCtxt; use rustc_session::lint::{self, Lint}; use rustc_span::def_id::DefId; @@ -9,6 +10,46 @@ use rustc_span::{Ident, Span, Symbol}; use crate::fluent_generated as fluent; +/// Emit diagnostic for calls to `#[inline(always)]`-annotated functions with a +/// `#[target_feature]` attribute where the caller enables a different set of target features. +pub(crate) fn emit_inline_always_target_feature_diagnostic<'a, 'tcx>( + tcx: TyCtxt<'tcx>, + call_span: Span, + callee_def_id: DefId, + caller_def_id: DefId, + callee_only: &[&'a str], +) { + let callee = tcx.def_path_str(callee_def_id); + let caller = tcx.def_path_str(caller_def_id); + + tcx.node_span_lint( + lint::builtin::INLINE_ALWAYS_MISMATCHING_TARGET_FEATURES, + tcx.local_def_id_to_hir_id(caller_def_id.as_local().unwrap()), + call_span, + |lint| { + lint.primary_message(format!( + "call to `#[inline(always)]`-annotated `{callee}` \ + requires the same target features to be inlined" + )); + lint.note("function will not be inlined"); + + lint.note(format!( + "the following target features are on `{callee}` but missing from `{caller}`: {}", + callee_only.join(", ") + )); + lint.span_note(callee_def_id.default_span(tcx), format!("`{callee}` is defined here")); + + let feats = callee_only.join(","); + lint.span_suggestion( + tcx.def_span(caller_def_id).shrink_to_lo(), + format!("add `#[target_feature]` attribute to `{caller}`"), + format!("#[target_feature(enable = \"{feats}\")]\n"), + lint::Applicability::MaybeIncorrect, + ); + }, + ); +} + #[derive(LintDiagnostic)] #[diag(mir_transform_unconditional_recursion)] #[help] diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 5a13394543b..f867c130efb 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -87,6 +87,7 @@ use std::borrow::Cow; use either::Either; +use itertools::Itertools as _; use rustc_abi::{self as abi, BackendRepr, FIRST_VARIANT, FieldIdx, Primitive, Size, VariantIdx}; use rustc_const_eval::const_eval::DummyMachine; use rustc_const_eval::interpret::{ @@ -895,18 +896,13 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { fn simplify_aggregate_to_copy( &mut self, - lhs: &Place<'tcx>, - rvalue: &mut Rvalue<'tcx>, - location: Location, - fields: &[VnIndex], + ty: Ty<'tcx>, variant_index: VariantIdx, + fields: &[VnIndex], ) -> Option<VnIndex> { - let Some(&first_field) = fields.first() else { - return None; - }; - let Value::Projection(copy_from_value, _) = *self.get(first_field) else { - return None; - }; + let Some(&first_field) = fields.first() else { return None }; + let Value::Projection(copy_from_value, _) = *self.get(first_field) else { return None }; + // All fields must correspond one-to-one and come from the same aggregate value. if fields.iter().enumerate().any(|(index, &v)| { if let Value::Projection(pointer, ProjectionElem::Field(from_index, _)) = *self.get(v) @@ -933,21 +929,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } } - // Allow introducing places with non-constant offsets, as those are still better than - // reconstructing an aggregate. - if self.ty(copy_from_local_value) == rvalue.ty(self.local_decls, self.tcx) - && let Some(place) = self.try_as_place(copy_from_local_value, location, true) - { - // Avoid creating `*a = copy (*b)`, as they might be aliases resulting in overlapping assignments. - // FIXME: This also avoids any kind of projection, not just derefs. We can add allowed projections. - if lhs.as_local().is_some() { - self.reused_locals.insert(place.local); - *rvalue = Rvalue::Use(Operand::Copy(place)); - } - return Some(copy_from_local_value); - } - - None + // Both must be variants of the same type. + if self.ty(copy_from_local_value) == ty { Some(copy_from_local_value) } else { None } } fn simplify_aggregate( @@ -1023,20 +1006,27 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } }; - if ty.is_array() && fields.len() > 4 { - let first = fields[0]; - if fields.iter().all(|&v| v == first) { - let len = ty::Const::from_target_usize(self.tcx, fields.len().try_into().unwrap()); - if let Some(op) = self.try_as_operand(first, location) { - *rvalue = Rvalue::Repeat(op, len); - } - return Some(self.insert(ty, Value::Repeat(first, len))); + if ty.is_array() + && fields.len() > 4 + && let Ok(&first) = fields.iter().all_equal_value() + { + let len = ty::Const::from_target_usize(self.tcx, fields.len().try_into().unwrap()); + if let Some(op) = self.try_as_operand(first, location) { + *rvalue = Rvalue::Repeat(op, len); } + return Some(self.insert(ty, Value::Repeat(first, len))); } - if let Some(value) = - self.simplify_aggregate_to_copy(lhs, rvalue, location, &fields, variant_index) - { + if let Some(value) = self.simplify_aggregate_to_copy(ty, variant_index, &fields) { + // Allow introducing places with non-constant offsets, as those are still better than + // reconstructing an aggregate. But avoid creating `*a = copy (*b)`, as they might be + // aliases resulting in overlapping assignments. + let allow_complex_projection = + lhs.projection[..].iter().all(PlaceElem::is_stable_offset); + if let Some(place) = self.try_as_place(value, location, allow_complex_projection) { + self.reused_locals.insert(place.local); + *rvalue = Rvalue::Use(Operand::Copy(place)); + } return Some(value); } diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 8f319e64916..1663dfa744f 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -117,6 +117,7 @@ declare_passes! { mod add_subtyping_projections : Subtyper; mod check_inline : CheckForceInline; mod check_call_recursion : CheckCallRecursion, CheckDropRecursion; + mod check_inline_always_target_features: CheckInlineAlwaysTargetFeature; mod check_alignment : CheckAlignment; mod check_enums : CheckEnums; mod check_const_item_mutation : CheckConstItemMutation; @@ -384,6 +385,9 @@ fn mir_built(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal<Body<'_>> { // MIR-level lints. &Lint(check_inline::CheckForceInline), &Lint(check_call_recursion::CheckCallRecursion), + // Check callee's target features match callers target features when + // using `#[inline(always)]` + &Lint(check_inline_always_target_features::CheckInlineAlwaysTargetFeature), &Lint(check_packed_ref::CheckPackedRef), &Lint(check_const_item_mutation::CheckConstItemMutation), &Lint(function_item_references::FunctionItemReferences), diff --git a/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs b/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs index 794984d2f3e..61c9bbe3123 100644 --- a/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs +++ b/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs @@ -2,6 +2,7 @@ use std::cell::RefCell; use std::collections::hash_map; use std::rc::Rc; +use itertools::Itertools as _; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::Subdiagnostic; @@ -339,9 +340,9 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body< // Suppose that all BIDs point into the same local, // we can remove the this local from the observed drops, // so that we can focus our diagnosis more on the others. - if candidates.iter().all(|&(_, place)| candidates[0].1.local == place.local) { + if let Ok(local) = candidates.iter().map(|&(_, place)| place.local).all_equal_value() { for path_idx in all_locals_dropped.iter() { - if move_data.move_paths[path_idx].place.local == candidates[0].1.local { + if move_data.move_paths[path_idx].place.local == local { to_exclude.insert(path_idx); } } diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index 468ef742dfb..75917d23883 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -34,6 +34,7 @@ //! The normal logic that a program with UB can be changed to do anything does not apply to //! pre-"runtime" MIR! +use itertools::Itertools as _; use rustc_index::{Idx, IndexSlice, IndexVec}; use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; @@ -288,20 +289,13 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { return false; }; - let first_succ = { - if let Some(first_succ) = terminator.successors().next() { - if terminator.successors().all(|s| s == first_succ) { - let count = terminator.successors().count(); - self.pred_count[first_succ] -= (count - 1) as u32; - first_succ - } else { - return false; - } - } else { - return false; - } + let Ok(first_succ) = terminator.successors().all_equal_value() else { + return false; }; + let count = terminator.successors().count(); + self.pred_count[first_succ] -= (count - 1) as u32; + debug!("simplifying branch {:?}", terminator); terminator.kind = TerminatorKind::Goto { target: first_succ }; true diff --git a/compiler/rustc_mir_transform/src/ssa.rs b/compiler/rustc_mir_transform/src/ssa.rs index cd9a7f4a39d..73c249a3c8c 100644 --- a/compiler/rustc_mir_transform/src/ssa.rs +++ b/compiler/rustc_mir_transform/src/ssa.rs @@ -225,6 +225,9 @@ impl SsaVisitor<'_, '_> { impl<'tcx> Visitor<'tcx> for SsaVisitor<'_, 'tcx> { fn visit_local(&mut self, local: Local, ctxt: PlaceContext, loc: Location) { + if ctxt.may_observe_address() { + self.borrowed_locals.insert(local); + } match ctxt { PlaceContext::MutatingUse(MutatingUseContext::Projection) | PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) => bug!(), @@ -237,7 +240,6 @@ impl<'tcx> Visitor<'tcx> for SsaVisitor<'_, 'tcx> { PlaceContext::NonMutatingUse( NonMutatingUseContext::SharedBorrow | NonMutatingUseContext::FakeBorrow, ) => { - self.borrowed_locals.insert(local); self.check_dominates(local, loc); self.direct_uses[local] += 1; } diff --git a/compiler/rustc_monomorphize/Cargo.toml b/compiler/rustc_monomorphize/Cargo.toml index 78266d3c6d8..09a55f0b5f8 100644 --- a/compiler/rustc_monomorphize/Cargo.toml +++ b/compiler/rustc_monomorphize/Cargo.toml @@ -16,6 +16,6 @@ rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } serde = "1" -serde_json.workspace = true -tracing.workspace = true +serde_json = "1" +tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_next_trait_solver/Cargo.toml b/compiler/rustc_next_trait_solver/Cargo.toml index 43db90c08f3..05bcabad02f 100644 --- a/compiler/rustc_next_trait_solver/Cargo.toml +++ b/compiler/rustc_next_trait_solver/Cargo.toml @@ -5,13 +5,13 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -derive-where.workspace = true +derive-where = "1.2.7" rustc_data_structures = { path = "../rustc_data_structures", optional = true } rustc_index = { path = "../rustc_index", default-features = false } rustc_macros = { path = "../rustc_macros", optional = true } rustc_type_ir = { path = "../rustc_type_ir", default-features = false } rustc_type_ir_macros = { path = "../rustc_type_ir_macros" } -tracing.workspace = true +tracing = "0.1" # tidy-alphabetical-end [features] diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs index 4644b145b18..6f9f4067384 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs @@ -56,22 +56,23 @@ where /// /// This expects `goal` and `opaque_types` to be eager resolved. pub(super) fn canonicalize_goal( - &self, + delegate: &D, goal: Goal<I, I::Predicate>, opaque_types: Vec<(ty::OpaqueTypeKey<I>, I::Ty)>, ) -> (Vec<I::GenericArg>, CanonicalInput<I, I::Predicate>) { let mut orig_values = Default::default(); let canonical = Canonicalizer::canonicalize_input( - self.delegate, + delegate, &mut orig_values, QueryInput { goal, - predefined_opaques_in_body: self + predefined_opaques_in_body: delegate .cx() .mk_predefined_opaques_in_body(PredefinedOpaquesData { opaque_types }), }, ); - let query_input = ty::CanonicalQueryInput { canonical, typing_mode: self.typing_mode() }; + let query_input = + ty::CanonicalQueryInput { canonical, typing_mode: delegate.typing_mode() }; (orig_values, query_input) } @@ -271,28 +272,23 @@ where /// - we apply the `external_constraints` returned by the query, returning /// the `normalization_nested_goals` pub(super) fn instantiate_and_apply_query_response( - &mut self, + delegate: &D, param_env: I::ParamEnv, original_values: &[I::GenericArg], response: CanonicalResponse<I>, + span: I::Span, ) -> (NestedNormalizationGoals<I>, Certainty) { let instantiation = Self::compute_query_response_instantiation_values( - self.delegate, + delegate, &original_values, &response, - self.origin_span, + span, ); let Response { var_values, external_constraints, certainty } = - self.delegate.instantiate_canonical(response, instantiation); + delegate.instantiate_canonical(response, instantiation); - Self::unify_query_var_values( - self.delegate, - param_env, - &original_values, - var_values, - self.origin_span, - ); + Self::unify_query_var_values(delegate, param_env, &original_values, var_values, span); let ExternalConstraintsData { region_constraints, @@ -300,8 +296,8 @@ where normalization_nested_goals, } = &*external_constraints; - self.register_region_constraints(region_constraints); - self.register_new_opaque_types(opaque_types); + Self::register_region_constraints(delegate, region_constraints, span); + Self::register_new_opaque_types(delegate, opaque_types, span); (normalization_nested_goals.clone(), certainty) } @@ -424,21 +420,26 @@ where } fn register_region_constraints( - &mut self, + delegate: &D, outlives: &[ty::OutlivesPredicate<I, I::GenericArg>], + span: I::Span, ) { for &ty::OutlivesPredicate(lhs, rhs) in outlives { match lhs.kind() { - ty::GenericArgKind::Lifetime(lhs) => self.register_region_outlives(lhs, rhs), - ty::GenericArgKind::Type(lhs) => self.register_ty_outlives(lhs, rhs), + ty::GenericArgKind::Lifetime(lhs) => delegate.sub_regions(rhs, lhs, span), + ty::GenericArgKind::Type(lhs) => delegate.register_ty_outlives(lhs, rhs, span), ty::GenericArgKind::Const(_) => panic!("const outlives: {lhs:?}: {rhs:?}"), } } } - fn register_new_opaque_types(&mut self, opaque_types: &[(ty::OpaqueTypeKey<I>, I::Ty)]) { + fn register_new_opaque_types( + delegate: &D, + opaque_types: &[(ty::OpaqueTypeKey<I>, I::Ty)], + span: I::Span, + ) { for &(key, ty) in opaque_types { - let prev = self.delegate.register_hidden_type_in_storage(key, ty, self.origin_span); + let prev = delegate.register_hidden_type_in_storage(key, ty, span); // We eagerly resolve inference variables when computing the query response. // This can cause previously distinct opaque type keys to now be structurally equal. // @@ -447,7 +448,7 @@ where // types here. However, doing so is difficult as it may result in nested goals and // any errors may make it harder to track the control flow for diagnostics. if let Some(prev) = prev { - self.delegate.add_duplicate_opaque_type(key, prev, self.origin_span); + delegate.add_duplicate_opaque_type(key, prev, span); } } } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 606fbcab4b3..99aa9bef5ac 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -20,13 +20,12 @@ use crate::coherence; use crate::delegate::SolverDelegate; use crate::placeholder::BoundVarReplacer; use crate::resolve::eager_resolve_vars; -use crate::solve::inspect::{self, ProofTreeBuilder}; use crate::solve::search_graph::SearchGraph; use crate::solve::ty::may_use_unstable_feature; use crate::solve::{ - CanonicalInput, Certainty, FIXPOINT_STEP_LIMIT, Goal, GoalEvaluation, GoalEvaluationKind, - GoalSource, GoalStalledOn, HasChanged, NestedNormalizationGoals, NoSolution, QueryInput, - QueryResult, + CanonicalInput, Certainty, FIXPOINT_STEP_LIMIT, Goal, GoalEvaluation, GoalSource, + GoalStalledOn, HasChanged, NestedNormalizationGoals, NoSolution, QueryInput, QueryResult, + inspect, }; pub(super) mod canonical; @@ -130,7 +129,7 @@ where // evaluation code. tainted: Result<(), NoSolution>, - pub(super) inspect: ProofTreeBuilder<D>, + pub(super) inspect: inspect::EvaluationStepBuilder<D>, } #[derive(PartialEq, Eq, Debug, Hash, Clone, Copy)] @@ -172,10 +171,7 @@ pub trait SolverDelegateEvalExt: SolverDelegate { goal: Goal<Self::Interner, <Self::Interner as Interner>::Predicate>, span: <Self::Interner as Interner>::Span, ) -> ( - Result< - (NestedNormalizationGoals<Self::Interner>, GoalEvaluation<Self::Interner>), - NoSolution, - >, + Result<NestedNormalizationGoals<Self::Interner>, NoSolution>, inspect::GoalEvaluation<Self::Interner>, ); } @@ -192,14 +188,9 @@ where span: I::Span, stalled_on: Option<GoalStalledOn<I>>, ) -> Result<GoalEvaluation<I>, NoSolution> { - EvalCtxt::enter_root( - self, - self.cx().recursion_limit(), - GenerateProofTree::No, - span, - |ecx| ecx.evaluate_goal(GoalEvaluationKind::Root, GoalSource::Misc, goal, stalled_on), - ) - .0 + EvalCtxt::enter_root(self, self.cx().recursion_limit(), span, |ecx| { + ecx.evaluate_goal(GoalSource::Misc, goal, stalled_on) + }) } fn root_goal_may_hold_with_depth( @@ -208,10 +199,9 @@ where goal: Goal<Self::Interner, <Self::Interner as Interner>::Predicate>, ) -> bool { self.probe(|| { - EvalCtxt::enter_root(self, root_depth, GenerateProofTree::No, I::Span::dummy(), |ecx| { - ecx.evaluate_goal(GoalEvaluationKind::Root, GoalSource::Misc, goal, None) + EvalCtxt::enter_root(self, root_depth, I::Span::dummy(), |ecx| { + ecx.evaluate_goal(GoalSource::Misc, goal, None) }) - .0 }) .is_ok() } @@ -221,18 +211,8 @@ where &self, goal: Goal<I, I::Predicate>, span: I::Span, - ) -> ( - Result<(NestedNormalizationGoals<I>, GoalEvaluation<I>), NoSolution>, - inspect::GoalEvaluation<I>, - ) { - let (result, proof_tree) = EvalCtxt::enter_root( - self, - self.cx().recursion_limit(), - GenerateProofTree::Yes, - span, - |ecx| ecx.evaluate_goal_raw(GoalEvaluationKind::Root, GoalSource::Misc, goal, None), - ); - (result, proof_tree.unwrap()) + ) -> (Result<NestedNormalizationGoals<I>, NoSolution>, inspect::GoalEvaluation<I>) { + evaluate_root_goal_for_proof_tree(self, goal, span) } } @@ -301,17 +281,16 @@ where pub(super) fn enter_root<R>( delegate: &D, root_depth: usize, - generate_proof_tree: GenerateProofTree, origin_span: I::Span, f: impl FnOnce(&mut EvalCtxt<'_, D>) -> R, - ) -> (R, Option<inspect::GoalEvaluation<I>>) { + ) -> R { let mut search_graph = SearchGraph::new(root_depth); let mut ecx = EvalCtxt { delegate, search_graph: &mut search_graph, nested_goals: Default::default(), - inspect: ProofTreeBuilder::new_maybe_root(generate_proof_tree), + inspect: inspect::EvaluationStepBuilder::new_noop(), // Only relevant when canonicalizing the response, // which we don't do within this evaluation context. @@ -324,15 +303,12 @@ where tainted: Ok(()), }; let result = f(&mut ecx); - - let proof_tree = ecx.inspect.finalize(); assert!( ecx.nested_goals.is_empty(), "root `EvalCtxt` should not have any goals added to it" ); - assert!(search_graph.is_empty()); - (result, proof_tree) + result } /// Creates a nested evaluation context that shares the same search graph as the @@ -346,11 +322,10 @@ where cx: I, search_graph: &'a mut SearchGraph<D>, canonical_input: CanonicalInput<I>, - canonical_goal_evaluation: &mut ProofTreeBuilder<D>, + proof_tree_builder: &mut inspect::ProofTreeBuilder<D>, f: impl FnOnce(&mut EvalCtxt<'_, D>, Goal<I, I::Predicate>) -> R, ) -> R { let (ref delegate, input, var_values) = D::build_with_canonical(cx, &canonical_input); - for &(key, ty) in &input.predefined_opaques_in_body.opaque_types { let prev = delegate.register_hidden_type_in_storage(key, ty, I::Span::dummy()); // It may be possible that two entries in the opaque type storage end up @@ -381,12 +356,12 @@ where nested_goals: Default::default(), origin_span: I::Span::dummy(), tainted: Ok(()), - inspect: canonical_goal_evaluation.new_goal_evaluation_step(var_values), + inspect: proof_tree_builder.new_evaluation_step(var_values), }; let result = f(&mut ecx, input.goal); ecx.inspect.probe_final_state(ecx.delegate, ecx.max_input_universe); - canonical_goal_evaluation.goal_evaluation_step(ecx.inspect); + proof_tree_builder.finish_evaluation_step(ecx.inspect); // When creating a query response we clone the opaque type constraints // instead of taking them. This would cause an ICE here, since we have @@ -406,13 +381,12 @@ where /// been constrained and the certainty of the result. fn evaluate_goal( &mut self, - goal_evaluation_kind: GoalEvaluationKind, source: GoalSource, goal: Goal<I, I::Predicate>, stalled_on: Option<GoalStalledOn<I>>, ) -> Result<GoalEvaluation<I>, NoSolution> { let (normalization_nested_goals, goal_evaluation) = - self.evaluate_goal_raw(goal_evaluation_kind, source, goal, stalled_on)?; + self.evaluate_goal_raw(source, goal, stalled_on)?; assert!(normalization_nested_goals.is_empty()); Ok(goal_evaluation) } @@ -426,7 +400,6 @@ where /// storage. pub(super) fn evaluate_goal_raw( &mut self, - goal_evaluation_kind: GoalEvaluationKind, source: GoalSource, goal: Goal<I, I::Predicate>, stalled_on: Option<GoalStalledOn<I>>, @@ -458,17 +431,14 @@ where let opaque_types = self.delegate.clone_opaque_types_lookup_table(); let (goal, opaque_types) = eager_resolve_vars(self.delegate, (goal, opaque_types)); - let (orig_values, canonical_goal) = self.canonicalize_goal(goal, opaque_types); - let mut goal_evaluation = - self.inspect.new_goal_evaluation(goal, &orig_values, goal_evaluation_kind); + let (orig_values, canonical_goal) = + Self::canonicalize_goal(self.delegate, goal, opaque_types); let canonical_result = self.search_graph.evaluate_goal( self.cx(), canonical_goal, self.step_kind_for_source(source), - &mut goal_evaluation, + &mut inspect::ProofTreeBuilder::new_noop(), ); - goal_evaluation.query_result(canonical_result); - self.inspect.goal_evaluation(goal_evaluation); let response = match canonical_result { Err(e) => return Err(e), Ok(response) => response, @@ -477,8 +447,13 @@ where let has_changed = if !has_only_region_constraints(response) { HasChanged::Yes } else { HasChanged::No }; - let (normalization_nested_goals, certainty) = - self.instantiate_and_apply_query_response(goal.param_env, &orig_values, response); + let (normalization_nested_goals, certainty) = Self::instantiate_and_apply_query_response( + self.delegate, + goal.param_env, + &orig_values, + response, + self.origin_span, + ); // FIXME: We previously had an assert here that checked that recomputing // a goal after applying its constraints did not change its response. @@ -676,12 +651,7 @@ where let ( NestedNormalizationGoals(nested_goals), GoalEvaluation { goal, certainty, stalled_on, has_changed: _ }, - ) = self.evaluate_goal_raw( - GoalEvaluationKind::Nested, - source, - unconstrained_goal, - stalled_on, - )?; + ) = self.evaluate_goal_raw(source, unconstrained_goal, stalled_on)?; // Add the nested goals from normalization to our own nested goals. trace!(?nested_goals); self.nested_goals.extend(nested_goals.into_iter().map(|(s, g)| (s, g, None))); @@ -734,7 +704,7 @@ where } } else { let GoalEvaluation { goal, certainty, has_changed, stalled_on } = - self.evaluate_goal(GoalEvaluationKind::Nested, source, goal, stalled_on)?; + self.evaluate_goal(source, goal, stalled_on)?; if has_changed == HasChanged::Yes { unchanged_certainty = None; } @@ -1297,3 +1267,62 @@ where if predicate.allow_normalization() { predicate.super_fold_with(self) } else { predicate } } } + +/// Do not call this directly, use the `tcx` query instead. +pub fn evaluate_root_goal_for_proof_tree_raw_provider< + D: SolverDelegate<Interner = I>, + I: Interner, +>( + cx: I, + canonical_goal: CanonicalInput<I>, +) -> (QueryResult<I>, I::Probe) { + let mut inspect = inspect::ProofTreeBuilder::new(); + let canonical_result = SearchGraph::<D>::evaluate_root_goal_for_proof_tree( + cx, + cx.recursion_limit(), + canonical_goal, + &mut inspect, + ); + let final_revision = inspect.unwrap(); + (canonical_result, cx.mk_probe(final_revision)) +} + +/// Evaluate a goal to build a proof tree. +/// +/// This is a copy of [EvalCtxt::evaluate_goal_raw] which avoids relying on the +/// [EvalCtxt] and uses a separate cache. +pub(super) fn evaluate_root_goal_for_proof_tree<D: SolverDelegate<Interner = I>, I: Interner>( + delegate: &D, + goal: Goal<I, I::Predicate>, + origin_span: I::Span, +) -> (Result<NestedNormalizationGoals<I>, NoSolution>, inspect::GoalEvaluation<I>) { + let opaque_types = delegate.clone_opaque_types_lookup_table(); + let (goal, opaque_types) = eager_resolve_vars(delegate, (goal, opaque_types)); + + let (orig_values, canonical_goal) = EvalCtxt::canonicalize_goal(delegate, goal, opaque_types); + + let (canonical_result, final_revision) = + delegate.cx().evaluate_root_goal_for_proof_tree_raw(canonical_goal); + + let proof_tree = inspect::GoalEvaluation { + uncanonicalized_goal: goal, + orig_values, + final_revision, + result: canonical_result, + }; + + let response = match canonical_result { + Err(e) => return (Err(e), proof_tree), + Ok(response) => response, + }; + + let (normalization_nested_goals, _certainty) = EvalCtxt::instantiate_and_apply_query_response( + delegate, + goal.param_env, + &proof_tree.orig_values, + response, + origin_span, + ); + + (Ok(normalization_nested_goals), proof_tree) +} diff --git a/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs b/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs index fc56b006d94..2675ed0d0da 100644 --- a/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs +++ b/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs @@ -12,96 +12,86 @@ use rustc_type_ir::{self as ty, Interner}; use crate::delegate::SolverDelegate; use crate::solve::eval_ctxt::canonical; -use crate::solve::{ - Certainty, GenerateProofTree, Goal, GoalEvaluationKind, GoalSource, QueryResult, inspect, -}; +use crate::solve::{Certainty, Goal, GoalSource, QueryResult, inspect}; -/// The core data structure when building proof trees. +/// We need to know whether to build a prove tree while evaluating. We +/// pass a `ProofTreeBuilder` with `state: Some(None)` into the search +/// graph which then causes the initial `EvalCtxt::compute_goal` to actually +/// build a proof tree which then gets written into the `state`. /// -/// In case the current evaluation does not generate a proof -/// tree, `state` is simply `None` and we avoid any work. -/// -/// The possible states of the solver are represented via -/// variants of [DebugSolver]. For any nested computation we call -/// `ProofTreeBuilder::new_nested_computation_kind` which -/// creates a new `ProofTreeBuilder` to temporarily replace the -/// current one. Once that nested computation is done, -/// `ProofTreeBuilder::nested_computation_kind` is called -/// to add the finished nested evaluation to the parent. -/// -/// We provide additional information to the current state -/// by calling methods such as `ProofTreeBuilder::probe_kind`. -/// -/// The actual structure closely mirrors the finished proof -/// trees. At the end of trait solving `ProofTreeBuilder::finalize` -/// is called to recursively convert the whole structure to a -/// finished proof tree. +/// Building the proof tree for a single evaluation step happens via the +/// [EvaluationStepBuilder] which is updated by the `EvalCtxt` when +/// appropriate. pub(crate) struct ProofTreeBuilder<D, I = <D as SolverDelegate>::Interner> where D: SolverDelegate<Interner = I>, I: Interner, { + state: Option<Box<Option<inspect::Probe<I>>>>, _infcx: PhantomData<D>, - state: Option<Box<DebugSolver<I>>>, } -/// The current state of the proof tree builder, at most places -/// in the code, only one or two variants are actually possible. -/// -/// We simply ICE in case that assumption is broken. -#[derive_where(Debug; I: Interner)] -enum DebugSolver<I: Interner> { - Root, - GoalEvaluation(WipGoalEvaluation<I>), - CanonicalGoalEvaluationStep(WipCanonicalGoalEvaluationStep<I>), -} +impl<D: SolverDelegate<Interner = I>, I: Interner> ProofTreeBuilder<D> { + pub(crate) fn new() -> ProofTreeBuilder<D> { + ProofTreeBuilder { state: Some(Box::new(None)), _infcx: PhantomData } + } -impl<I: Interner> From<WipGoalEvaluation<I>> for DebugSolver<I> { - fn from(g: WipGoalEvaluation<I>) -> DebugSolver<I> { - DebugSolver::GoalEvaluation(g) + pub(crate) fn new_noop() -> ProofTreeBuilder<D> { + ProofTreeBuilder { state: None, _infcx: PhantomData } } -} -impl<I: Interner> From<WipCanonicalGoalEvaluationStep<I>> for DebugSolver<I> { - fn from(g: WipCanonicalGoalEvaluationStep<I>) -> DebugSolver<I> { - DebugSolver::CanonicalGoalEvaluationStep(g) + pub(crate) fn is_noop(&self) -> bool { + self.state.is_none() } -} -#[derive_where(PartialEq, Debug; I: Interner)] -struct WipGoalEvaluation<I: Interner> { - pub uncanonicalized_goal: Goal<I, I::Predicate>, - pub orig_values: Vec<I::GenericArg>, - pub encountered_overflow: bool, - /// After we finished evaluating this is moved into `kind`. - pub final_revision: Option<WipCanonicalGoalEvaluationStep<I>>, - pub result: Option<QueryResult<I>>, -} + pub(crate) fn new_evaluation_step( + &mut self, + var_values: ty::CanonicalVarValues<I>, + ) -> EvaluationStepBuilder<D> { + if self.is_noop() { + EvaluationStepBuilder { state: None, _infcx: PhantomData } + } else { + EvaluationStepBuilder { + state: Some(Box::new(WipEvaluationStep { + var_values: var_values.var_values.to_vec(), + evaluation: WipProbe { + initial_num_var_values: var_values.len(), + steps: vec![], + kind: None, + final_state: None, + }, + probe_depth: 0, + })), + _infcx: PhantomData, + } + } + } -impl<I: Interner> Eq for WipGoalEvaluation<I> {} - -impl<I: Interner> WipGoalEvaluation<I> { - fn finalize(self) -> inspect::GoalEvaluation<I> { - inspect::GoalEvaluation { - uncanonicalized_goal: self.uncanonicalized_goal, - orig_values: self.orig_values, - kind: if self.encountered_overflow { - assert!(self.final_revision.is_none()); - inspect::GoalEvaluationKind::Overflow - } else { - let final_revision = self.final_revision.unwrap().finalize(); - inspect::GoalEvaluationKind::Evaluation { final_revision } - }, - result: self.result.unwrap(), + pub(crate) fn finish_evaluation_step( + &mut self, + goal_evaluation_step: EvaluationStepBuilder<D>, + ) { + if let Some(this) = self.state.as_deref_mut() { + *this = Some(goal_evaluation_step.state.unwrap().finalize()); } } + + pub(crate) fn unwrap(self) -> inspect::Probe<I> { + self.state.unwrap().unwrap() + } } -/// This only exists during proof tree building and does not have -/// a corresponding struct in `inspect`. We need this to track a -/// bunch of metadata about the current evaluation. -#[derive_where(PartialEq, Debug; I: Interner)] -struct WipCanonicalGoalEvaluationStep<I: Interner> { +pub(crate) struct EvaluationStepBuilder<D, I = <D as SolverDelegate>::Interner> +where + D: SolverDelegate<Interner = I>, + I: Interner, +{ + state: Option<Box<WipEvaluationStep<I>>>, + _infcx: PhantomData<D>, +} + +#[derive_where(PartialEq, Eq, Debug; I: Interner)] +struct WipEvaluationStep<I: Interner> { /// Unlike `EvalCtxt::var_values`, we append a new /// generic arg here whenever we create a new inference /// variable. @@ -113,9 +103,7 @@ struct WipCanonicalGoalEvaluationStep<I: Interner> { evaluation: WipProbe<I>, } -impl<I: Interner> Eq for WipCanonicalGoalEvaluationStep<I> {} - -impl<I: Interner> WipCanonicalGoalEvaluationStep<I> { +impl<I: Interner> WipEvaluationStep<I> { fn current_evaluation_scope(&mut self) -> &mut WipProbe<I> { let mut current = &mut self.evaluation; for _ in 0..self.probe_depth { @@ -181,169 +169,48 @@ impl<I: Interner> WipProbeStep<I> { } } -impl<D: SolverDelegate<Interner = I>, I: Interner> ProofTreeBuilder<D> { - fn new(state: impl Into<DebugSolver<I>>) -> ProofTreeBuilder<D> { - ProofTreeBuilder { state: Some(Box::new(state.into())), _infcx: PhantomData } - } - - fn opt_nested<T: Into<DebugSolver<I>>>(&self, state: impl FnOnce() -> Option<T>) -> Self { - ProofTreeBuilder { - state: self.state.as_ref().and_then(|_| Some(state()?.into())).map(Box::new), - _infcx: PhantomData, - } +impl<D: SolverDelegate<Interner = I>, I: Interner> EvaluationStepBuilder<D> { + pub(crate) fn new_noop() -> EvaluationStepBuilder<D> { + EvaluationStepBuilder { state: None, _infcx: PhantomData } } - fn nested<T: Into<DebugSolver<I>>>(&self, state: impl FnOnce() -> T) -> Self { - ProofTreeBuilder { - state: self.state.as_ref().map(|_| Box::new(state().into())), - _infcx: PhantomData, - } + pub(crate) fn is_noop(&self) -> bool { + self.state.is_none() } - fn as_mut(&mut self) -> Option<&mut DebugSolver<I>> { + fn as_mut(&mut self) -> Option<&mut WipEvaluationStep<I>> { self.state.as_deref_mut() } - pub(crate) fn take_and_enter_probe(&mut self) -> ProofTreeBuilder<D> { - let mut nested = ProofTreeBuilder { state: self.state.take(), _infcx: PhantomData }; + pub(crate) fn take_and_enter_probe(&mut self) -> EvaluationStepBuilder<D> { + let mut nested = EvaluationStepBuilder { state: self.state.take(), _infcx: PhantomData }; nested.enter_probe(); nested } - pub(crate) fn finalize(self) -> Option<inspect::GoalEvaluation<I>> { - match *self.state? { - DebugSolver::GoalEvaluation(wip_goal_evaluation) => { - Some(wip_goal_evaluation.finalize()) - } - root => unreachable!("unexpected proof tree builder root node: {:?}", root), - } - } - - pub(crate) fn new_maybe_root(generate_proof_tree: GenerateProofTree) -> ProofTreeBuilder<D> { - match generate_proof_tree { - GenerateProofTree::No => ProofTreeBuilder::new_noop(), - GenerateProofTree::Yes => ProofTreeBuilder::new_root(), - } - } - - fn new_root() -> ProofTreeBuilder<D> { - ProofTreeBuilder::new(DebugSolver::Root) - } - - fn new_noop() -> ProofTreeBuilder<D> { - ProofTreeBuilder { state: None, _infcx: PhantomData } - } - - pub(crate) fn is_noop(&self) -> bool { - self.state.is_none() - } - - pub(in crate::solve) fn new_goal_evaluation( - &mut self, - uncanonicalized_goal: Goal<I, I::Predicate>, - orig_values: &[I::GenericArg], - kind: GoalEvaluationKind, - ) -> ProofTreeBuilder<D> { - self.opt_nested(|| match kind { - GoalEvaluationKind::Root => Some(WipGoalEvaluation { - uncanonicalized_goal, - orig_values: orig_values.to_vec(), - encountered_overflow: false, - final_revision: None, - result: None, - }), - GoalEvaluationKind::Nested => None, - }) - } - - pub(crate) fn canonical_goal_evaluation_overflow(&mut self) { + pub(crate) fn add_var_value<T: Into<I::GenericArg>>(&mut self, arg: T) { if let Some(this) = self.as_mut() { - match this { - DebugSolver::GoalEvaluation(goal_evaluation) => { - goal_evaluation.encountered_overflow = true; - } - _ => unreachable!(), - }; + this.var_values.push(arg.into()); } } - pub(crate) fn goal_evaluation(&mut self, goal_evaluation: ProofTreeBuilder<D>) { + fn enter_probe(&mut self) { if let Some(this) = self.as_mut() { - match this { - DebugSolver::Root => *this = *goal_evaluation.state.unwrap(), - DebugSolver::CanonicalGoalEvaluationStep(_) => { - assert!(goal_evaluation.state.is_none()) - } - _ => unreachable!(), - } - } - } - - pub(crate) fn new_goal_evaluation_step( - &mut self, - var_values: ty::CanonicalVarValues<I>, - ) -> ProofTreeBuilder<D> { - self.nested(|| WipCanonicalGoalEvaluationStep { - var_values: var_values.var_values.to_vec(), - evaluation: WipProbe { - initial_num_var_values: var_values.len(), + let initial_num_var_values = this.var_values.len(); + this.current_evaluation_scope().steps.push(WipProbeStep::NestedProbe(WipProbe { + initial_num_var_values, steps: vec![], kind: None, final_state: None, - }, - probe_depth: 0, - }) - } - - pub(crate) fn goal_evaluation_step(&mut self, goal_evaluation_step: ProofTreeBuilder<D>) { - if let Some(this) = self.as_mut() { - match (this, *goal_evaluation_step.state.unwrap()) { - ( - DebugSolver::GoalEvaluation(goal_evaluation), - DebugSolver::CanonicalGoalEvaluationStep(goal_evaluation_step), - ) => { - goal_evaluation.final_revision = Some(goal_evaluation_step); - } - _ => unreachable!(), - } - } - } - - pub(crate) fn add_var_value<T: Into<I::GenericArg>>(&mut self, arg: T) { - match self.as_mut() { - None => {} - Some(DebugSolver::CanonicalGoalEvaluationStep(state)) => { - state.var_values.push(arg.into()); - } - Some(s) => panic!("tried to add var values to {s:?}"), - } - } - - fn enter_probe(&mut self) { - match self.as_mut() { - None => {} - Some(DebugSolver::CanonicalGoalEvaluationStep(state)) => { - let initial_num_var_values = state.var_values.len(); - state.current_evaluation_scope().steps.push(WipProbeStep::NestedProbe(WipProbe { - initial_num_var_values, - steps: vec![], - kind: None, - final_state: None, - })); - state.probe_depth += 1; - } - Some(s) => panic!("tried to start probe to {s:?}"), + })); + this.probe_depth += 1; } } pub(crate) fn probe_kind(&mut self, probe_kind: inspect::ProbeKind<I>) { - match self.as_mut() { - None => {} - Some(DebugSolver::CanonicalGoalEvaluationStep(state)) => { - let prev = state.current_evaluation_scope().kind.replace(probe_kind); - assert_eq!(prev, None); - } - _ => panic!(), + if let Some(this) = self.as_mut() { + let prev = this.current_evaluation_scope().kind.replace(probe_kind); + assert_eq!(prev, None); } } @@ -352,19 +219,11 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> ProofTreeBuilder<D> { delegate: &D, max_input_universe: ty::UniverseIndex, ) { - match self.as_mut() { - None => {} - Some(DebugSolver::CanonicalGoalEvaluationStep(state)) => { - let final_state = canonical::make_canonical_state( - delegate, - &state.var_values, - max_input_universe, - (), - ); - let prev = state.current_evaluation_scope().final_state.replace(final_state); - assert_eq!(prev, None); - } - _ => panic!(), + if let Some(this) = self.as_mut() { + let final_state = + canonical::make_canonical_state(delegate, &this.var_values, max_input_universe, ()); + let prev = this.current_evaluation_scope().final_state.replace(final_state); + assert_eq!(prev, None); } } @@ -375,18 +234,14 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> ProofTreeBuilder<D> { source: GoalSource, goal: Goal<I, I::Predicate>, ) { - match self.as_mut() { - None => {} - Some(DebugSolver::CanonicalGoalEvaluationStep(state)) => { - let goal = canonical::make_canonical_state( - delegate, - &state.var_values, - max_input_universe, - goal, - ); - state.current_evaluation_scope().steps.push(WipProbeStep::AddGoal(source, goal)) - } - _ => panic!(), + if let Some(this) = self.as_mut() { + let goal = canonical::make_canonical_state( + delegate, + &this.var_values, + max_input_universe, + goal, + ); + this.current_evaluation_scope().steps.push(WipProbeStep::AddGoal(source, goal)) } } @@ -396,47 +251,31 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> ProofTreeBuilder<D> { max_input_universe: ty::UniverseIndex, impl_args: I::GenericArgs, ) { - match self.as_mut() { - Some(DebugSolver::CanonicalGoalEvaluationStep(state)) => { - let impl_args = canonical::make_canonical_state( - delegate, - &state.var_values, - max_input_universe, - impl_args, - ); - state - .current_evaluation_scope() - .steps - .push(WipProbeStep::RecordImplArgs { impl_args }); - } - None => {} - _ => panic!(), + if let Some(this) = self.as_mut() { + let impl_args = canonical::make_canonical_state( + delegate, + &this.var_values, + max_input_universe, + impl_args, + ); + this.current_evaluation_scope().steps.push(WipProbeStep::RecordImplArgs { impl_args }); } } pub(crate) fn make_canonical_response(&mut self, shallow_certainty: Certainty) { - match self.as_mut() { - Some(DebugSolver::CanonicalGoalEvaluationStep(state)) => { - state - .current_evaluation_scope() - .steps - .push(WipProbeStep::MakeCanonicalResponse { shallow_certainty }); - } - None => {} - _ => panic!(), + if let Some(this) = self.as_mut() { + this.current_evaluation_scope() + .steps + .push(WipProbeStep::MakeCanonicalResponse { shallow_certainty }); } } - pub(crate) fn finish_probe(mut self) -> ProofTreeBuilder<D> { - match self.as_mut() { - None => {} - Some(DebugSolver::CanonicalGoalEvaluationStep(state)) => { - assert_ne!(state.probe_depth, 0); - let num_var_values = state.current_evaluation_scope().initial_num_var_values; - state.var_values.truncate(num_var_values); - state.probe_depth -= 1; - } - _ => panic!(), + pub(crate) fn finish_probe(mut self) -> EvaluationStepBuilder<D> { + if let Some(this) = self.as_mut() { + assert_ne!(this.probe_depth, 0); + let num_var_values = this.current_evaluation_scope().initial_num_var_values; + this.var_values.truncate(num_var_values); + this.probe_depth -= 1; } self @@ -444,21 +283,7 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> ProofTreeBuilder<D> { pub(crate) fn query_result(&mut self, result: QueryResult<I>) { if let Some(this) = self.as_mut() { - match this { - DebugSolver::GoalEvaluation(goal_evaluation) => { - assert_eq!(goal_evaluation.result.replace(result), None); - } - DebugSolver::CanonicalGoalEvaluationStep(evaluation_step) => { - assert_eq!( - evaluation_step - .evaluation - .kind - .replace(inspect::ProbeKind::Root { result }), - None - ); - } - _ => unreachable!(), - } + assert_eq!(this.evaluation.kind.replace(inspect::ProbeKind::Root { result }), None); } } } diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 710b59f662a..85f9d852d95 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -27,7 +27,10 @@ pub use rustc_type_ir::solve::*; use rustc_type_ir::{self as ty, Interner, TypingMode}; use tracing::instrument; -pub use self::eval_ctxt::{EvalCtxt, GenerateProofTree, SolverDelegateEvalExt}; +pub use self::eval_ctxt::{ + EvalCtxt, GenerateProofTree, SolverDelegateEvalExt, + evaluate_root_goal_for_proof_tree_raw_provider, +}; use crate::delegate::SolverDelegate; use crate::solve::assembly::Candidate; @@ -42,12 +45,6 @@ use crate::solve::assembly::Candidate; /// recursion limit again. However, this feels very unlikely. const FIXPOINT_STEP_LIMIT: usize = 8; -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -enum GoalEvaluationKind { - Root, - Nested, -} - /// Whether evaluating this goal ended up changing the /// inference state. #[derive(PartialEq, Eq, Debug, Hash, Clone, Copy)] diff --git a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs index 84f8eda4f8d..f0342e0523f 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -7,8 +7,9 @@ use rustc_type_ir::solve::{CanonicalInput, Certainty, NoSolution, QueryResult}; use rustc_type_ir::{Interner, TypingMode}; use crate::delegate::SolverDelegate; -use crate::solve::inspect::ProofTreeBuilder; -use crate::solve::{EvalCtxt, FIXPOINT_STEP_LIMIT, has_no_inference_or_external_constraints}; +use crate::solve::{ + EvalCtxt, FIXPOINT_STEP_LIMIT, has_no_inference_or_external_constraints, inspect, +}; /// This type is never constructed. We only use it to implement `search_graph::Delegate` /// for all types which impl `SolverDelegate` and doing it directly fails in coherence. @@ -34,7 +35,7 @@ where const FIXPOINT_STEP_LIMIT: usize = FIXPOINT_STEP_LIMIT; - type ProofTreeBuilder = ProofTreeBuilder<D>; + type ProofTreeBuilder = inspect::ProofTreeBuilder<D>; fn inspect_is_noop(inspect: &mut Self::ProofTreeBuilder) -> bool { inspect.is_noop() } @@ -81,12 +82,7 @@ where Self::initial_provisional_result(cx, kind, input) == result } - fn on_stack_overflow( - cx: I, - input: CanonicalInput<I>, - inspect: &mut ProofTreeBuilder<D>, - ) -> QueryResult<I> { - inspect.canonical_goal_evaluation_overflow(); + fn on_stack_overflow(cx: I, input: CanonicalInput<I>) -> QueryResult<I> { response_no_constraints(cx, input, Certainty::overflow(true)) } diff --git a/compiler/rustc_parse/Cargo.toml b/compiler/rustc_parse/Cargo.toml index 7cb4ae7ff5f..6d738a10371 100644 --- a/compiler/rustc_parse/Cargo.toml +++ b/compiler/rustc_parse/Cargo.toml @@ -5,8 +5,8 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -bitflags.workspace = true -rustc-literal-escaper.workspace = true +bitflags = "2.4.1" +rustc-literal-escaper = "0.0.5" rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_data_structures = { path = "../rustc_data_structures" } @@ -18,8 +18,8 @@ rustc_lexer = { path = "../rustc_lexer" } rustc_macros = { path = "../rustc_macros" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } -thin-vec.workspace = true -tracing.workspace = true +thin-vec = "0.2.12" +tracing = "0.1" unicode-normalization = "0.1.11" unicode-width = "0.2.0" # tidy-alphabetical-end diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 4ca2f57bd87..77dd313d9b8 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -58,7 +58,7 @@ parse_async_use_order_incorrect = the order of `use` and `async` is incorrect parse_at_dot_dot_in_struct_pattern = `@ ..` is not supported in struct patterns .suggestion = bind to each field separately or, if you don't need them, just remove `{$ident} @` -parse_at_in_struct_pattern = Unexpected `@` in struct pattern +parse_at_in_struct_pattern = unexpected `@` in struct pattern .note = struct patterns use `field: pattern` syntax to bind to fields .help = consider replacing `new_name @ field_name` with `field_name: new_name` if that is what you intended diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 9792240a548..f5f081efc49 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -6,7 +6,7 @@ use rustc_ast::util::unicode::{TEXT_FLOW_CONTROL_CHARS, contains_text_flow_contr use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, DiagCtxtHandle, StashKey}; use rustc_lexer::{ - Base, Cursor, DocStyle, FrontmatterAllowed, LiteralKind, RawStrError, is_whitespace, + Base, Cursor, DocStyle, FrontmatterAllowed, LiteralKind, RawStrError, is_horizontal_whitespace, }; use rustc_literal_escaper::{EscapeError, Mode, check_for_errors}; use rustc_session::lint::BuiltinLintDiag; @@ -44,19 +44,44 @@ pub(crate) struct UnmatchedDelim { pub candidate_span: Option<Span>, } +/// Which tokens should be stripped before lexing the tokens. +pub(crate) enum StripTokens { + /// Strip both shebang and frontmatter. + ShebangAndFrontmatter, + /// Strip the shebang but not frontmatter. + /// + /// That means that char sequences looking like frontmatter are simply + /// interpreted as regular Rust lexemes. + Shebang, + /// Strip nothing. + /// + /// In other words, char sequences looking like a shebang or frontmatter + /// are simply interpreted as regular Rust lexemes. + Nothing, +} + pub(crate) fn lex_token_trees<'psess, 'src>( psess: &'psess ParseSess, mut src: &'src str, mut start_pos: BytePos, override_span: Option<Span>, - frontmatter_allowed: FrontmatterAllowed, + strip_tokens: StripTokens, ) -> Result<TokenStream, Vec<Diag<'psess>>> { - // Skip `#!`, if present. - if let Some(shebang_len) = rustc_lexer::strip_shebang(src) { - src = &src[shebang_len..]; - start_pos = start_pos + BytePos::from_usize(shebang_len); + match strip_tokens { + StripTokens::Shebang | StripTokens::ShebangAndFrontmatter => { + if let Some(shebang_len) = rustc_lexer::strip_shebang(src) { + src = &src[shebang_len..]; + start_pos = start_pos + BytePos::from_usize(shebang_len); + } + } + StripTokens::Nothing => {} } + let frontmatter_allowed = match strip_tokens { + StripTokens::ShebangAndFrontmatter => FrontmatterAllowed::Yes, + StripTokens::Shebang | StripTokens::Nothing => FrontmatterAllowed::No, + }; + let cursor = Cursor::new(src, frontmatter_allowed); let mut lexer = Lexer { psess, @@ -597,7 +622,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { let last_line_start = within.rfind('\n').map_or(0, |i| i + 1); let last_line = &within[last_line_start..]; - let last_line_trimmed = last_line.trim_start_matches(is_whitespace); + let last_line_trimmed = last_line.trim_start_matches(is_horizontal_whitespace); let last_line_start_pos = frontmatter_opening_end_pos + BytePos(last_line_start as u32); let frontmatter_span = self.mk_sp(frontmatter_opening_pos, self.pos); @@ -640,7 +665,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { }); } - if !rest.trim_matches(is_whitespace).is_empty() { + if !rest.trim_matches(is_horizontal_whitespace).is_empty() { let span = self.mk_sp(last_line_start_pos, self.pos); self.dcx().emit_err(errors::FrontmatterExtraCharactersAfterClose { span }); } diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 197333d942d..d8792d7af4c 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -21,7 +21,6 @@ use rustc_ast::tokenstream::{DelimSpan, TokenStream}; use rustc_ast::{AttrItem, Attribute, MetaItemInner, token}; use rustc_ast_pretty::pprust; use rustc_errors::{Diag, EmissionGuarantee, FatalError, PResult, pluralize}; -use rustc_lexer::FrontmatterAllowed; use rustc_session::parse::ParseSess; use rustc_span::source_map::SourceMap; use rustc_span::{FileName, SourceFile, Span}; @@ -34,6 +33,8 @@ pub mod parser; use parser::Parser; use rustc_ast::token::Delimiter; +use crate::lexer::StripTokens; + pub mod lexer; mod errors; @@ -62,7 +63,20 @@ pub fn new_parser_from_source_str( source: String, ) -> Result<Parser<'_>, Vec<Diag<'_>>> { let source_file = psess.source_map().new_source_file(name, source); - new_parser_from_source_file(psess, source_file) + new_parser_from_source_file(psess, source_file, StripTokens::ShebangAndFrontmatter) +} + +/// Creates a new parser from a simple (no shebang, no frontmatter) source string. +/// +/// On failure, the errors must be consumed via `unwrap_or_emit_fatal`, `emit`, `cancel`, +/// etc., otherwise a panic will occur when they are dropped. +pub fn new_parser_from_simple_source_str( + psess: &ParseSess, + name: FileName, + source: String, +) -> Result<Parser<'_>, Vec<Diag<'_>>> { + let source_file = psess.source_map().new_source_file(name, source); + new_parser_from_source_file(psess, source_file, StripTokens::Nothing) } /// Creates a new parser from a filename. On failure, the errors must be consumed via @@ -96,7 +110,7 @@ pub fn new_parser_from_file<'a>( } err.emit(); }); - new_parser_from_source_file(psess, source_file) + new_parser_from_source_file(psess, source_file, StripTokens::ShebangAndFrontmatter) } pub fn utf8_error<E: EmissionGuarantee>( @@ -147,9 +161,10 @@ pub fn utf8_error<E: EmissionGuarantee>( fn new_parser_from_source_file( psess: &ParseSess, source_file: Arc<SourceFile>, + strip_tokens: StripTokens, ) -> Result<Parser<'_>, Vec<Diag<'_>>> { let end_pos = source_file.end_position(); - let stream = source_file_to_stream(psess, source_file, None, FrontmatterAllowed::Yes)?; + let stream = source_file_to_stream(psess, source_file, None, strip_tokens)?; let mut parser = Parser::new(psess, stream, None); if parser.token == token::Eof { parser.token.span = Span::new(end_pos, end_pos, parser.token.span.ctxt(), None); @@ -165,8 +180,8 @@ pub fn source_str_to_stream( ) -> Result<TokenStream, Vec<Diag<'_>>> { let source_file = psess.source_map().new_source_file(name, source); // used mainly for `proc_macro` and the likes, not for our parsing purposes, so don't parse - // frontmatters as frontmatters. - source_file_to_stream(psess, source_file, override_span, FrontmatterAllowed::No) + // frontmatters as frontmatters, but for compatibility reason still strip the shebang + source_file_to_stream(psess, source_file, override_span, StripTokens::Shebang) } /// Given a source file, produces a sequence of token trees. Returns any buffered errors from @@ -175,7 +190,7 @@ fn source_file_to_stream<'psess>( psess: &'psess ParseSess, source_file: Arc<SourceFile>, override_span: Option<Span>, - frontmatter_allowed: FrontmatterAllowed, + strip_tokens: StripTokens, ) -> Result<TokenStream, Vec<Diag<'psess>>> { let src = source_file.src.as_ref().unwrap_or_else(|| { psess.dcx().bug(format!( @@ -184,13 +199,7 @@ fn source_file_to_stream<'psess>( )); }); - lexer::lex_token_trees( - psess, - src.as_str(), - source_file.start_pos, - override_span, - frontmatter_allowed, - ) + lexer::lex_token_trees(psess, src.as_str(), source_file.start_pos, override_span, strip_tokens) } /// Runs the given subparser `f` on the tokens of the given `attr`'s item. diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 7de4f6efd0b..4c02547357e 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2742,7 +2742,7 @@ impl<'a> Parser<'a> { /// The specified `edition` in `let_chains_policy` should be that of the whole `if` construct, /// i.e. the same span we use to later decide whether the drop behaviour should be that of /// edition `..=2021` or that of `2024..`. - // Public because it is used in rustfmt forks such as https://github.com/tucant/rustfmt/blob/30c83df9e1db10007bdd16dafce8a86b404329b2/src/parse/macros/html.rs#L57 for custom if expressions. + // Public to use it for custom `if` expressions in rustfmt forks like https://github.com/tucant/rustfmt pub fn parse_expr_cond( &mut self, let_chains_policy: LetChainsPolicy, @@ -2910,7 +2910,8 @@ impl<'a> Parser<'a> { } } - fn parse_for_head(&mut self) -> PResult<'a, (Box<Pat>, Box<Expr>)> { + // Public to use it for custom `for` expressions in rustfmt forks like https://github.com/tucant/rustfmt + pub fn parse_for_head(&mut self) -> PResult<'a, (Box<Pat>, Box<Expr>)> { let begin_paren = if self.token == token::OpenParen { // Record whether we are about to parse `for (`. // This is used below for recovery in case of `for ( $stuff ) $block` diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 15598f32429..3bbca622975 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -22,6 +22,8 @@ use std::{fmt, mem, slice}; use attr_wrapper::{AttrWrapper, UsePreAttrPos}; pub use diagnostics::AttemptLocalParseRecovery; pub(crate) use expr::ForbiddenLetReason; +// Public to use it for custom `if` expressions in rustfmt forks like https://github.com/tucant/rustfmt +pub use expr::LetChainsPolicy; pub(crate) use item::{FnContext, FnParseMode}; pub use pat::{CommaRecoveryMode, RecoverColon, RecoverComma}; pub use path::PathStyle; diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 9754691a0b9..c4d30b3d328 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -1516,7 +1516,7 @@ impl<'a> Parser<'a> { || self.check_noexpect(&token::DotDotDot) || self.check_keyword(exp!(Underscore)) { - etc = PatFieldsRest::Rest; + etc = PatFieldsRest::Rest(self.token.span); let mut etc_sp = self.token.span; if first_etc_and_maybe_comma_span.is_none() { if let Some(comma_tok) = diff --git a/compiler/rustc_parse_format/Cargo.toml b/compiler/rustc_parse_format/Cargo.toml index 645e34b6e32..d178fcda1fb 100644 --- a/compiler/rustc_parse_format/Cargo.toml +++ b/compiler/rustc_parse_format/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -rustc-literal-escaper.workspace = true +rustc-literal-escaper = "0.0.5" rustc_lexer = { path = "../rustc_lexer" } # tidy-alphabetical-end diff --git a/compiler/rustc_passes/Cargo.toml b/compiler/rustc_passes/Cargo.toml index c74608a6146..ba81ef3103b 100644 --- a/compiler/rustc_passes/Cargo.toml +++ b/compiler/rustc_passes/Cargo.toml @@ -24,5 +24,5 @@ rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } -tracing.workspace = true +tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 498d9a405fa..afd08319738 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -687,6 +687,7 @@ passes_unused_var_maybe_capture_ref = unused variable: `{$name}` passes_unused_var_remove_field = unused variable: `{$name}` passes_unused_var_remove_field_suggestion = try removing the field +passes_unused_var_typo = you might have meant to pattern match on the similarly named {$kind} `{$item_name}` passes_unused_variable_args_in_macro = `{$name}` is captured in macro and introduced a unused variable diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 680e2a26d84..23dcabef1a1 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1367,6 +1367,22 @@ pub(crate) struct UnusedVarRemoveFieldSugg { #[note] pub(crate) struct UnusedVarAssignedOnly { pub name: String, + #[subdiagnostic] + pub typo: Option<PatternTypo>, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + passes_unused_var_typo, + style = "verbose", + applicability = "machine-applicable" +)] +pub(crate) struct PatternTypo { + #[suggestion_part(code = "{code}")] + pub span: Span, + pub code: String, + pub item_name: String, + pub kind: String, } #[derive(LintDiagnostic)] @@ -1434,6 +1450,8 @@ pub(crate) struct UnusedVariableTryPrefix { #[subdiagnostic] pub sugg: UnusedVariableSugg, pub name: String, + #[subdiagnostic] + pub typo: Option<PatternTypo>, } #[derive(Subdiagnostic)] diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 801a533c943..1b2ffb5b3db 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -95,8 +95,10 @@ use rustc_hir::{Expr, HirId, HirIdMap, HirIdSet, find_attr}; use rustc_index::IndexVec; use rustc_middle::query::Providers; use rustc_middle::span_bug; +use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, RootVariableMinCaptureList, Ty, TyCtxt}; use rustc_session::lint; +use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::{BytePos, Span, Symbol}; use tracing::{debug, instrument}; @@ -1583,7 +1585,7 @@ impl<'tcx> Liveness<'_, 'tcx> { }); let can_remove = match pat.kind { - hir::PatKind::Struct(_, fields, true) => { + hir::PatKind::Struct(_, fields, Some(_)) => { // if all fields are shorthand, remove the struct field, otherwise, mark with _ as prefix fields.iter().all(|f| f.is_shorthand) } @@ -1688,6 +1690,51 @@ impl<'tcx> Liveness<'_, 'tcx> { let is_assigned = if ln == self.exit_ln { false } else { self.assigned_on_exit(ln, var) }; + let mut typo = None; + for (hir_id, _, span) in &hir_ids_and_spans { + let ty = self.typeck_results.node_type(*hir_id); + if let ty::Adt(adt, _) = ty.peel_refs().kind() { + let name = Symbol::intern(&name); + let adt_def = self.ir.tcx.adt_def(adt.did()); + let variant_names: Vec<_> = adt_def + .variants() + .iter() + .filter(|v| matches!(v.ctor, Some((CtorKind::Const, _)))) + .map(|v| v.name) + .collect(); + if let Some(name) = find_best_match_for_name(&variant_names, name, None) + && let Some(variant) = adt_def.variants().iter().find(|v| { + v.name == name && matches!(v.ctor, Some((CtorKind::Const, _))) + }) + { + typo = Some(errors::PatternTypo { + span: *span, + code: with_no_trimmed_paths!(self.ir.tcx.def_path_str(variant.def_id)), + kind: self.ir.tcx.def_descr(variant.def_id).to_string(), + item_name: variant.name.to_string(), + }); + } + } + } + if typo.is_none() { + for (hir_id, _, span) in &hir_ids_and_spans { + let ty = self.typeck_results.node_type(*hir_id); + // Look for consts of the same type with similar names as well, not just unit + // structs and variants. + for def_id in self.ir.tcx.hir_body_owners() { + if let DefKind::Const = self.ir.tcx.def_kind(def_id) + && self.ir.tcx.type_of(def_id).instantiate_identity() == ty + { + typo = Some(errors::PatternTypo { + span: *span, + code: with_no_trimmed_paths!(self.ir.tcx.def_path_str(def_id)), + kind: "constant".to_string(), + item_name: self.ir.tcx.item_name(def_id).to_string(), + }); + } + } + } + } if is_assigned { self.ir.tcx.emit_node_span_lint( lint::builtin::UNUSED_VARIABLES, @@ -1696,7 +1743,7 @@ impl<'tcx> Liveness<'_, 'tcx> { .into_iter() .map(|(_, _, ident_span)| ident_span) .collect::<Vec<_>>(), - errors::UnusedVarAssignedOnly { name }, + errors::UnusedVarAssignedOnly { name, typo }, ) } else if can_remove { let spans = hir_ids_and_spans @@ -1788,6 +1835,7 @@ impl<'tcx> Liveness<'_, 'tcx> { name, sugg, string_interp: suggestions, + typo, }, ); } diff --git a/compiler/rustc_pattern_analysis/Cargo.toml b/compiler/rustc_pattern_analysis/Cargo.toml index e4909ab6d16..a59f7bbeb9e 100644 --- a/compiler/rustc_pattern_analysis/Cargo.toml +++ b/compiler/rustc_pattern_analysis/Cargo.toml @@ -5,9 +5,9 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -rustc-hash.workspace = true +rustc-hash = "2.0.0" rustc_abi = { path = "../rustc_abi", optional = true } -rustc_apfloat.workspace = true +rustc_apfloat = "0.2.0" rustc_arena = { path = "../rustc_arena", optional = true } rustc_data_structures = { path = "../rustc_data_structures", optional = true } rustc_errors = { path = "../rustc_errors", optional = true } @@ -19,7 +19,7 @@ rustc_middle = { path = "../rustc_middle", optional = true } rustc_session = { path = "../rustc_session", optional = true } rustc_span = { path = "../rustc_span", optional = true } smallvec = { version = "1.8.1", features = ["union"] } -tracing.workspace = true +tracing = "0.1" # tidy-alphabetical-end [dev-dependencies] diff --git a/compiler/rustc_privacy/Cargo.toml b/compiler/rustc_privacy/Cargo.toml index 7de58132e13..c8bfdb91304 100644 --- a/compiler/rustc_privacy/Cargo.toml +++ b/compiler/rustc_privacy/Cargo.toml @@ -15,5 +15,5 @@ rustc_middle = { path = "../rustc_middle" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_ty_utils = { path = "../rustc_ty_utils" } -tracing.workspace = true +tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_proc_macro/Cargo.toml b/compiler/rustc_proc_macro/Cargo.toml index 99e7fc7abc6..beb95aa3b52 100644 --- a/compiler/rustc_proc_macro/Cargo.toml +++ b/compiler/rustc_proc_macro/Cargo.toml @@ -16,7 +16,7 @@ doctest = false [dependencies] # tidy-alphabetical-start -rustc-literal-escaper.workspace = true +rustc-literal-escaper = "0.0.5" # tidy-alphabetical-end [features] diff --git a/compiler/rustc_public/Cargo.toml b/compiler/rustc_public/Cargo.toml index e67e4fe6739..70af30c1a5f 100644 --- a/compiler/rustc_public/Cargo.toml +++ b/compiler/rustc_public/Cargo.toml @@ -12,9 +12,9 @@ rustc_public_bridge = { path = "../rustc_public_bridge" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } -scoped-tls.workspace = true +scoped-tls = "1.0" serde = { version = "1.0.125", features = [ "derive" ] } -tracing.workspace = true +tracing = "0.1" # tidy-alphabetical-end [features] diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index 5a661072bc7..207038db40d 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -656,13 +656,13 @@ impl<'tcx> Stable<'tcx> for rustc_middle::ty::GenericParamDefKind { fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &CompilerCtxt<'_, BridgeTys>) -> Self::T { use crate::ty::GenericParamDefKind; - match self { + match *self { ty::GenericParamDefKind::Lifetime => GenericParamDefKind::Lifetime, ty::GenericParamDefKind::Type { has_default, synthetic } => { - GenericParamDefKind::Type { has_default: *has_default, synthetic: *synthetic } + GenericParamDefKind::Type { has_default, synthetic } } - ty::GenericParamDefKind::Const { has_default, synthetic: _ } => { - GenericParamDefKind::Const { has_default: *has_default } + ty::GenericParamDefKind::Const { has_default } => { + GenericParamDefKind::Const { has_default } } } } diff --git a/compiler/rustc_query_impl/Cargo.toml b/compiler/rustc_query_impl/Cargo.toml index 3d5cf0eb72d..e5cceacf15d 100644 --- a/compiler/rustc_query_impl/Cargo.toml +++ b/compiler/rustc_query_impl/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -measureme.workspace = true +measureme = "12.0.1" rustc_data_structures = { path = "../rustc_data_structures" } rustc_hashes = { path = "../rustc_hashes" } rustc_hir = { path = "../rustc_hir" } @@ -15,5 +15,5 @@ rustc_query_system = { path = "../rustc_query_system" } rustc_serialize = { path = "../rustc_serialize" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } -tracing.workspace = true +tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index 306d4dbc4b4..e499e08c82b 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -58,7 +58,7 @@ impl<'tcx, C: QueryCache, const ANON: bool, const DEPTH_LIMIT: bool, const FEEDA for DynamicConfig<'tcx, C, ANON, DEPTH_LIMIT, FEEDABLE> { fn clone(&self) -> Self { - DynamicConfig { dynamic: self.dynamic } + *self } } diff --git a/compiler/rustc_query_system/Cargo.toml b/compiler/rustc_query_system/Cargo.toml index 0df933bc81c..7480ba03474 100644 --- a/compiler/rustc_query_system/Cargo.toml +++ b/compiler/rustc_query_system/Cargo.toml @@ -21,7 +21,7 @@ rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_thread_pool = { path = "../rustc_thread_pool" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } -tracing.workspace = true +tracing = "0.1" # tidy-alphabetical-end [dependencies.hashbrown] diff --git a/compiler/rustc_query_system/messages.ftl b/compiler/rustc_query_system/messages.ftl index f48dc60afa0..f686608034c 100644 --- a/compiler/rustc_query_system/messages.ftl +++ b/compiler/rustc_query_system/messages.ftl @@ -18,8 +18,8 @@ query_system_cycle_usage = cycle used when {$usage} query_system_increment_compilation = internal compiler error: encountered incremental compilation error with {$dep_node} .help = This is a known issue with the compiler. Run {$run_cmd} to allow your project to compile -query_system_increment_compilation_note1 = Please follow the instructions below to create a bug report with the provided information -query_system_increment_compilation_note2 = See <https://github.com/rust-lang/rust/issues/84970> for more information +query_system_increment_compilation_note1 = please follow the instructions below to create a bug report with the provided information +query_system_increment_compilation_note2 = see <https://github.com/rust-lang/rust/issues/84970> for more information query_system_overflow_note = query depth increased by {$depth} when {$desc} diff --git a/compiler/rustc_resolve/Cargo.toml b/compiler/rustc_resolve/Cargo.toml index 4da4c0840df..eb98a6e85c0 100644 --- a/compiler/rustc_resolve/Cargo.toml +++ b/compiler/rustc_resolve/Cargo.toml @@ -5,9 +5,9 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -bitflags.workspace = true -indexmap.workspace = true -itertools.workspace = true +bitflags = "2.4.1" +indexmap = "2.4.0" +itertools = "0.12" pulldown-cmark = { version = "0.11", features = ["html"], default-features = false } rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } @@ -27,6 +27,6 @@ rustc_query_system = { path = "../rustc_query_system" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } -thin-vec.workspace = true -tracing.workspace = true +thin-vec = "0.2.12" +tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_resolve/messages.ftl b/compiler/rustc_resolve/messages.ftl index 47280a93677..0e566e20a12 100644 --- a/compiler/rustc_resolve/messages.ftl +++ b/compiler/rustc_resolve/messages.ftl @@ -470,6 +470,8 @@ resolve_variable_bound_with_different_mode = .label = bound in different ways .first_binding_span = first binding +resolve_variable_is_a_typo = you might have meant to use the similarly named previously used binding `{$typo}` + resolve_variable_is_not_bound_in_all_patterns = variable `{$name}` is not bound in all patterns diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 5dfc4292a38..689e713ef46 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1,3 +1,4 @@ +use itertools::Itertools as _; use rustc_ast::visit::{self, Visitor}; use rustc_ast::{ self as ast, CRATE_NODE_ID, Crate, ItemKind, ModKind, NodeId, Path, join_path_idents, @@ -661,8 +662,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ResolutionError::VariableNotBoundInPattern(binding_error, parent_scope) => { let BindingError { name, target, origin, could_be_path } = binding_error; - let target_sp = target.iter().copied().collect::<Vec<_>>(); - let origin_sp = origin.iter().copied().collect::<Vec<_>>(); + let mut target_sp = target.iter().map(|pat| pat.span).collect::<Vec<_>>(); + target_sp.sort(); + target_sp.dedup(); + let mut origin_sp = origin.iter().map(|(span, _)| *span).collect::<Vec<_>>(); + origin_sp.sort(); + origin_sp.dedup(); let msp = MultiSpan::from_spans(target_sp.clone()); let mut err = self @@ -671,8 +676,35 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { for sp in target_sp { err.subdiagnostic(errors::PatternDoesntBindName { span: sp, name }); } - for sp in origin_sp { - err.subdiagnostic(errors::VariableNotInAllPatterns { span: sp }); + for sp in &origin_sp { + err.subdiagnostic(errors::VariableNotInAllPatterns { span: *sp }); + } + let mut suggested_typo = false; + if !target.iter().all(|pat| matches!(pat.kind, ast::PatKind::Ident(..))) + && !origin.iter().all(|(_, pat)| matches!(pat.kind, ast::PatKind::Ident(..))) + { + // The check above is so that when we encounter `match foo { (a | b) => {} }`, + // we don't suggest `(a | a) => {}`, which would never be what the user wants. + let mut target_visitor = BindingVisitor::default(); + for pat in &target { + target_visitor.visit_pat(pat); + } + target_visitor.identifiers.sort(); + target_visitor.identifiers.dedup(); + let mut origin_visitor = BindingVisitor::default(); + for (_, pat) in &origin { + origin_visitor.visit_pat(pat); + } + origin_visitor.identifiers.sort(); + origin_visitor.identifiers.dedup(); + // Find if the binding could have been a typo + if let Some(typo) = + find_best_match_for_name(&target_visitor.identifiers, name.name, None) + && !origin_visitor.identifiers.contains(&typo) + { + err.subdiagnostic(errors::PatternBindingTypo { spans: origin_sp, typo }); + suggested_typo = true; + } } if could_be_path { let import_suggestions = self.lookup_import_candidates( @@ -693,10 +725,86 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }, ); - if import_suggestions.is_empty() { + if import_suggestions.is_empty() && !suggested_typo { + let kinds = [ + DefKind::Ctor(CtorOf::Variant, CtorKind::Const), + DefKind::Ctor(CtorOf::Struct, CtorKind::Const), + DefKind::Const, + DefKind::AssocConst, + ]; + let mut local_names = vec![]; + self.add_module_candidates( + parent_scope.module, + &mut local_names, + &|res| matches!(res, Res::Def(_, _)), + None, + ); + let local_names: FxHashSet<_> = local_names + .into_iter() + .filter_map(|s| match s.res { + Res::Def(_, def_id) => Some(def_id), + _ => None, + }) + .collect(); + + let mut local_suggestions = vec![]; + let mut suggestions = vec![]; + for kind in kinds { + if let Some(suggestion) = self.early_lookup_typo_candidate( + ScopeSet::All(Namespace::ValueNS), + &parent_scope, + name, + &|res: Res| match res { + Res::Def(k, _) => k == kind, + _ => false, + }, + ) && let Res::Def(kind, mut def_id) = suggestion.res + { + if let DefKind::Ctor(_, _) = kind { + def_id = self.tcx.parent(def_id); + } + let kind = kind.descr(def_id); + if local_names.contains(&def_id) { + // The item is available in the current scope. Very likely to + // be a typo. Don't use the full path. + local_suggestions.push(( + suggestion.candidate, + suggestion.candidate.to_string(), + kind, + )); + } else { + suggestions.push(( + suggestion.candidate, + self.def_path_str(def_id), + kind, + )); + } + } + } + let suggestions = if !local_suggestions.is_empty() { + // There is at least one item available in the current scope that is a + // likely typo. We only show those. + local_suggestions + } else { + suggestions + }; + for (name, sugg, kind) in suggestions { + err.span_suggestion_verbose( + span, + format!( + "you might have meant to use the similarly named {kind} `{name}`", + ), + sugg, + Applicability::MaybeIncorrect, + ); + suggested_typo = true; + } + } + if import_suggestions.is_empty() && !suggested_typo { let help_msg = format!( - "if you meant to match on a variant or a `const` item, consider \ - making the path in the pattern qualified: `path::to::ModOrType::{name}`", + "if you meant to match on a unit struct, unit variant or a `const` \ + item, consider making the path in the pattern qualified: \ + `path::to::ModOrType::{name}`", ); err.span_help(span, help_msg); } @@ -1016,6 +1124,39 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { .emit() } + fn def_path_str(&self, mut def_id: DefId) -> String { + // We can't use `def_path_str` in resolve. + let mut path = vec![def_id]; + while let Some(parent) = self.tcx.opt_parent(def_id) { + def_id = parent; + path.push(def_id); + if def_id.is_top_level_module() { + break; + } + } + // We will only suggest importing directly if it is accessible through that path. + path.into_iter() + .rev() + .map(|def_id| { + self.tcx + .opt_item_name(def_id) + .map(|name| { + match ( + def_id.is_top_level_module(), + def_id.is_local(), + self.tcx.sess.edition(), + ) { + (true, true, Edition::Edition2015) => String::new(), + (true, true, _) => kw::Crate.to_string(), + (true, false, _) | (false, _, _) => name.to_string(), + } + }) + .unwrap_or_else(|| "_".to_string()) + }) + .collect::<Vec<String>>() + .join("::") + } + pub(crate) fn add_scope_set_candidates( &mut self, suggestions: &mut Vec<TypoSuggestion>, @@ -1846,7 +1987,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let extern_prelude_ambiguity = || { self.extern_prelude.get(&Macros20NormalizedIdent::new(ident)).is_some_and(|entry| { entry.item_binding.map(|(b, _)| b) == Some(b1) - && entry.flag_binding.as_ref().and_then(|pb| pb.get().binding()) == Some(b2) + && entry.flag_binding.as_ref().and_then(|pb| pb.get().0.binding()) == Some(b2) }) }; let (b1, b2, misc1, misc2, swapped) = if b2.span.is_dummy() && !b1.span.is_dummy() { @@ -3329,16 +3470,11 @@ fn show_candidates( err.note(note.to_string()); } } else { - let (_, descr_first, _, _, _) = &inaccessible_path_strings[0]; - let descr = if inaccessible_path_strings + let descr = inaccessible_path_strings .iter() - .skip(1) - .all(|(_, descr, _, _, _)| descr == descr_first) - { - descr_first - } else { - "item" - }; + .map(|&(_, descr, _, _, _)| descr) + .all_equal_value() + .unwrap_or("item"); let plural_descr = if descr.ends_with('s') { format!("{descr}es") } else { format!("{descr}s") }; @@ -3396,7 +3532,7 @@ impl UsePlacementFinder { } } -impl<'tcx> visit::Visitor<'tcx> for UsePlacementFinder { +impl<'tcx> Visitor<'tcx> for UsePlacementFinder { fn visit_crate(&mut self, c: &Crate) { if self.target_module == CRATE_NODE_ID { let inject = c.spans.inject_use_span; @@ -3424,6 +3560,22 @@ impl<'tcx> visit::Visitor<'tcx> for UsePlacementFinder { } } +#[derive(Default)] +struct BindingVisitor { + identifiers: Vec<Symbol>, + spans: FxHashMap<Symbol, Vec<Span>>, +} + +impl<'tcx> Visitor<'tcx> for BindingVisitor { + fn visit_pat(&mut self, pat: &ast::Pat) { + if let ast::PatKind::Ident(_, ident, _) = pat.kind { + self.identifiers.push(ident.name); + self.spans.entry(ident.name).or_default().push(ident.span); + } + visit::walk_pat(self, pat); + } +} + fn search_for_any_use_in_items(items: &[Box<ast::Item>]) -> Option<Span> { for item in items { if let ItemKind::Use(..) = item.kind diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index 63d6fa23a14..72be94e58db 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -986,6 +986,18 @@ pub(crate) struct VariableNotInAllPatterns { pub(crate) span: Span, } +#[derive(Subdiagnostic)] +#[multipart_suggestion( + resolve_variable_is_a_typo, + applicability = "maybe-incorrect", + style = "verbose" +)] +pub(crate) struct PatternBindingTypo { + #[suggestion_part(code = "{typo}")] + pub(crate) spans: Vec<Span>, + pub(crate) typo: Symbol, +} + #[derive(Diagnostic)] #[diag(resolve_name_defined_multiple_time)] #[note] diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index dae42645bec..bc06a227571 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -422,6 +422,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // to detect potential ambiguities. let mut innermost_result: Option<(NameBinding<'_>, Flags)> = None; let mut determinacy = Determinacy::Determined; + let mut extern_prelude_item_binding = None; + let mut extern_prelude_flag_binding = None; // Shadowed bindings don't need to be marked as used or non-speculatively loaded. macro finalize_scope() { if innermost_result.is_none() { finalize } else { None } @@ -558,7 +560,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Scope::ExternPreludeItems => { // FIXME: use `finalize_scope` here. match this.reborrow().extern_prelude_get_item(ident, finalize.is_some()) { - Some(binding) => Ok((binding, Flags::empty())), + Some(binding) => { + extern_prelude_item_binding = Some(binding); + Ok((binding, Flags::empty())) + } None => Err(Determinacy::determined( this.graph_root.unexpanded_invocations.borrow().is_empty(), )), @@ -566,7 +571,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } Scope::ExternPreludeFlags => { match this.extern_prelude_get_flag(ident, finalize_scope!().is_some()) { - Some(binding) => Ok((binding, Flags::empty())), + Some(binding) => { + extern_prelude_flag_binding = Some(binding); + Ok((binding, Flags::empty())) + } None => Err(Determinacy::Determined), } } @@ -686,7 +694,16 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } else { None }; - if let Some(kind) = ambiguity_error_kind { + // Skip ambiguity errors for extern flag bindings "overridden" + // by extern item bindings. + // FIXME: Remove with lang team approval. + let issue_145575_hack = Some(binding) + == extern_prelude_flag_binding + && extern_prelude_item_binding.is_some() + && extern_prelude_item_binding != Some(innermost_binding); + if let Some(kind) = ambiguity_error_kind + && !issue_145575_hack + { let misc = |f: Flags| { if f.contains(Flags::MISC_SUGGEST_CRATE) { AmbiguityErrorMisc::SuggestCrate diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index d4f7fb276a9..521fef2b9d4 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -8,7 +8,6 @@ use std::assert_matches::debug_assert_matches; use std::borrow::Cow; -use std::collections::BTreeSet; use std::collections::hash_map::Entry; use std::mem::{replace, swap, take}; @@ -3682,31 +3681,30 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { // 2) Record any missing bindings or binding mode inconsistencies. for (map_outer, pat_outer) in not_never_pats.iter() { // Check against all arms except for the same pattern which is always self-consistent. - let inners = not_never_pats - .iter() - .filter(|(_, pat)| pat.id != pat_outer.id) - .flat_map(|(map, _)| map); - - for (&name, binding_inner) in inners { - match map_outer.get(&name) { - None => { - // The inner binding is missing in the outer. - let binding_error = - missing_vars.entry(name).or_insert_with(|| BindingError { - name, - origin: BTreeSet::new(), - target: BTreeSet::new(), - could_be_path: name.as_str().starts_with(char::is_uppercase), - }); - binding_error.origin.insert(binding_inner.span); - binding_error.target.insert(pat_outer.span); - } - Some(binding_outer) => { - if binding_outer.annotation != binding_inner.annotation { - // The binding modes in the outer and inner bindings differ. - inconsistent_vars - .entry(name) - .or_insert((binding_inner.span, binding_outer.span)); + let inners = not_never_pats.iter().filter(|(_, pat)| pat.id != pat_outer.id); + + for (map, pat) in inners { + for (&name, binding_inner) in map { + match map_outer.get(&name) { + None => { + // The inner binding is missing in the outer. + let binding_error = + missing_vars.entry(name).or_insert_with(|| BindingError { + name, + origin: Default::default(), + target: Default::default(), + could_be_path: name.as_str().starts_with(char::is_uppercase), + }); + binding_error.origin.push((binding_inner.span, (***pat).clone())); + binding_error.target.push((***pat_outer).clone()); + } + Some(binding_outer) => { + if binding_outer.annotation != binding_inner.annotation { + // The binding modes in the outer and inner bindings differ. + inconsistent_vars + .entry(name) + .or_insert((binding_inner.span, binding_outer.span)); + } } } } @@ -3719,7 +3717,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { v.could_be_path = false; } self.report_error( - *v.origin.iter().next().unwrap(), + v.origin.iter().next().unwrap().0, ResolutionError::VariableNotBoundInPattern(v, self.parent_scope), ); } @@ -3922,7 +3920,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { fn record_patterns_with_skipped_bindings(&mut self, pat: &Pat, rest: &ast::PatFieldsRest) { match rest { - ast::PatFieldsRest::Rest | ast::PatFieldsRest::Recovered(_) => { + ast::PatFieldsRest::Rest(_) | ast::PatFieldsRest::Recovered(_) => { // Record that the pattern doesn't introduce all the bindings it could. if let Some(partial_res) = self.r.partial_res_map.get(&pat.id) && let Some(res) = partial_res.full_res() diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 80b2095d8cc..9e3c0938836 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -3099,7 +3099,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { |err, _, span, message, suggestion, span_suggs| { err.multipart_suggestion_verbose( message, - std::iter::once((span, suggestion)).chain(span_suggs.clone()).collect(), + std::iter::once((span, suggestion)).chain(span_suggs).collect(), Applicability::MaybeIncorrect, ); true diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 9674c0356c2..8b185ce7ef2 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -230,8 +230,8 @@ enum Used { #[derive(Debug)] struct BindingError { name: Ident, - origin: BTreeSet<Span>, - target: BTreeSet<Span>, + origin: Vec<(Span, ast::Pat)>, + target: Vec<ast::Pat>, could_be_path: bool, } @@ -1031,7 +1031,7 @@ struct ExternPreludeEntry<'ra> { /// `flag_binding` is `None`, or when `extern crate` introducing `item_binding` used renaming. item_binding: Option<(NameBinding<'ra>, /* introduced by item */ bool)>, /// Binding from an `--extern` flag, lazily populated on first use. - flag_binding: Option<Cell<PendingBinding<'ra>>>, + flag_binding: Option<Cell<(PendingBinding<'ra>, /* finalized */ bool)>>, } impl ExternPreludeEntry<'_> { @@ -1042,7 +1042,7 @@ impl ExternPreludeEntry<'_> { fn flag() -> Self { ExternPreludeEntry { item_binding: None, - flag_binding: Some(Cell::new(PendingBinding::Pending)), + flag_binding: Some(Cell::new((PendingBinding::Pending, false))), } } } @@ -2245,14 +2245,16 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { fn extern_prelude_get_flag(&self, ident: Ident, finalize: bool) -> Option<NameBinding<'ra>> { let entry = self.extern_prelude.get(&Macros20NormalizedIdent::new(ident)); entry.and_then(|entry| entry.flag_binding.as_ref()).and_then(|flag_binding| { - let binding = match flag_binding.get() { + let (pending_binding, finalized) = flag_binding.get(); + let binding = match pending_binding { PendingBinding::Ready(binding) => { - if finalize { + if finalize && !finalized { self.cstore_mut().process_path_extern(self.tcx, ident.name, ident.span); } binding } PendingBinding::Pending => { + debug_assert!(!finalized); let crate_id = if finalize { self.cstore_mut().process_path_extern(self.tcx, ident.name, ident.span) } else { @@ -2264,7 +2266,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }) } }; - flag_binding.set(PendingBinding::Ready(binding)); + flag_binding.set((PendingBinding::Ready(binding), finalize || finalized)); binding.or_else(|| finalize.then_some(self.dummy_binding)) }) } diff --git a/compiler/rustc_sanitizers/Cargo.toml b/compiler/rustc_sanitizers/Cargo.toml index 3dbeaa435f3..9069d2c233d 100644 --- a/compiler/rustc_sanitizers/Cargo.toml +++ b/compiler/rustc_sanitizers/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -bitflags.workspace = true +bitflags = "2.5.0" rustc_abi = { path = "../rustc_abi" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_hir = { path = "../rustc_hir" } @@ -13,6 +13,6 @@ rustc_middle = { path = "../rustc_middle" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } -tracing.workspace = true +tracing = "0.1" twox-hash = "1.6.3" # tidy-alphabetical-end diff --git a/compiler/rustc_serialize/Cargo.toml b/compiler/rustc_serialize/Cargo.toml index 853f87ebed9..948242352e7 100644 --- a/compiler/rustc_serialize/Cargo.toml +++ b/compiler/rustc_serialize/Cargo.toml @@ -5,14 +5,14 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -indexmap.workspace = true +indexmap = "2.0.0" rustc_hashes = { path = "../rustc_hashes" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } -thin-vec.workspace = true +thin-vec = "0.2.12" # tidy-alphabetical-end [dev-dependencies] # tidy-alphabetical-start rustc_macros = { path = "../rustc_macros" } -tempfile.workspace = true +tempfile = "3.2" # tidy-alphabetical-end diff --git a/compiler/rustc_session/Cargo.toml b/compiler/rustc_session/Cargo.toml index 5870fb29ae8..60d56b1b808 100644 --- a/compiler/rustc_session/Cargo.toml +++ b/compiler/rustc_session/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -bitflags.workspace = true +bitflags = "2.4.1" getopts = "0.2" rand = "0.9.0" rustc_abi = { path = "../rustc_abi" } @@ -23,12 +23,14 @@ rustc_serialize = { path = "../rustc_serialize" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } termize = "0.2" -tracing.workspace = true +tracing = "0.1" # tidy-alphabetical-end [target.'cfg(unix)'.dependencies] +# FIXME: Remove this pin once this rustix issue is resolved +# https://github.com/bytecodealliance/rustix/issues/1496 # tidy-alphabetical-start -libc.workspace = true +libc = "=0.2.174" # tidy-alphabetical-end [target.'cfg(windows)'.dependencies.windows] diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 6d5be2d92cd..69facde6936 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -83,10 +83,77 @@ pub struct TargetModifier { pub value_name: String, } +mod target_modifier_consistency_check { + use super::*; + pub(super) fn sanitizer(l: &TargetModifier, r: Option<&TargetModifier>) -> bool { + let mut lparsed: SanitizerSet = Default::default(); + let lval = if l.value_name.is_empty() { None } else { Some(l.value_name.as_str()) }; + parse::parse_sanitizers(&mut lparsed, lval); + + let mut rparsed: SanitizerSet = Default::default(); + let rval = r.filter(|v| !v.value_name.is_empty()).map(|v| v.value_name.as_str()); + parse::parse_sanitizers(&mut rparsed, rval); + + // Some sanitizers need to be target modifiers, and some do not. + // For now, we should mark all sanitizers as target modifiers except for these: + // AddressSanitizer, LeakSanitizer + let tmod_sanitizers = SanitizerSet::MEMORY + | SanitizerSet::THREAD + | SanitizerSet::HWADDRESS + | SanitizerSet::CFI + | SanitizerSet::MEMTAG + | SanitizerSet::SHADOWCALLSTACK + | SanitizerSet::KCFI + | SanitizerSet::KERNELADDRESS + | SanitizerSet::SAFESTACK + | SanitizerSet::DATAFLOW; + + lparsed & tmod_sanitizers == rparsed & tmod_sanitizers + } + pub(super) fn sanitizer_cfi_normalize_integers( + opts: &Options, + l: &TargetModifier, + r: Option<&TargetModifier>, + ) -> bool { + // For kCFI, the helper flag -Zsanitizer-cfi-normalize-integers should also be a target modifier + if opts.unstable_opts.sanitizer.contains(SanitizerSet::KCFI) { + if let Some(r) = r { + return l.extend().tech_value == r.extend().tech_value; + } else { + return false; + } + } + true + } +} + impl TargetModifier { pub fn extend(&self) -> ExtendedTargetModifierInfo { self.opt.reparse(&self.value_name) } + // Custom consistency check for target modifiers (or default `l.tech_value == r.tech_value`) + // When other is None, consistency with default value is checked + pub fn consistent(&self, opts: &Options, other: Option<&TargetModifier>) -> bool { + assert!(other.is_none() || self.opt == other.unwrap().opt); + match self.opt { + OptionsTargetModifiers::UnstableOptions(unstable) => match unstable { + UnstableOptionsTargetModifiers::sanitizer => { + return target_modifier_consistency_check::sanitizer(self, other); + } + UnstableOptionsTargetModifiers::sanitizer_cfi_normalize_integers => { + return target_modifier_consistency_check::sanitizer_cfi_normalize_integers( + opts, self, other, + ); + } + _ => {} + }, + _ => {} + }; + match other { + Some(other) => self.extend().tech_value == other.extend().tech_value, + None => false, + } + } } fn tmod_push_impl( @@ -2504,13 +2571,13 @@ written to standard error output)"), retpoline_external_thunk: bool = (false, parse_bool, [TRACKED TARGET_MODIFIER], "enables retpoline-external-thunk, retpoline-indirect-branches and retpoline-indirect-calls \ target features (default: no)"), - sanitizer: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED], + sanitizer: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED TARGET_MODIFIER], "use a sanitizer"), sanitizer_cfi_canonical_jump_tables: Option<bool> = (Some(true), parse_opt_bool, [TRACKED], "enable canonical jump tables (default: yes)"), sanitizer_cfi_generalize_pointers: Option<bool> = (None, parse_opt_bool, [TRACKED], "enable generalizing pointer types (default: no)"), - sanitizer_cfi_normalize_integers: Option<bool> = (None, parse_opt_bool, [TRACKED], + sanitizer_cfi_normalize_integers: Option<bool> = (None, parse_opt_bool, [TRACKED TARGET_MODIFIER], "enable normalizing integer types (default: no)"), sanitizer_dataflow_abilist: Vec<String> = (Vec::new(), parse_comma_list, [TRACKED], "additional ABI list files that control how shadow parameters are passed (comma separated)"), diff --git a/compiler/rustc_span/Cargo.toml b/compiler/rustc_span/Cargo.toml index da6f2101d23..43a2d692577 100644 --- a/compiler/rustc_span/Cargo.toml +++ b/compiler/rustc_span/Cargo.toml @@ -6,8 +6,8 @@ edition = "2024" [dependencies] # tidy-alphabetical-start blake3 = "1.5.2" -derive-where.workspace = true -indexmap.workspace = true +derive-where = "1.2.7" +indexmap = { version = "2.0.0" } itoa = "1.0" md5 = { package = "md-5", version = "0.10.0" } rustc_arena = { path = "../rustc_arena" } @@ -16,9 +16,9 @@ rustc_hashes = { path = "../rustc_hashes" } rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } rustc_serialize = { path = "../rustc_serialize" } -scoped-tls.workspace = true +scoped-tls = "1.0" sha1 = "0.10.0" sha2 = "0.10.1" -tracing.workspace = true +tracing = "0.1" unicode-width = "0.2.0" # tidy-alphabetical-end diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 77260d07c99..e5108d8b7e9 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2162,6 +2162,7 @@ symbols! { target_family, target_feature, target_feature_11, + target_feature_inline_always, target_has_atomic, target_has_atomic_equal_alignment, target_has_atomic_load_store, @@ -2280,6 +2281,8 @@ symbols! { unboxed_closures, unchecked_add, unchecked_div, + unchecked_funnel_shl, + unchecked_funnel_shr, unchecked_mul, unchecked_rem, unchecked_shl, diff --git a/compiler/rustc_symbol_mangling/Cargo.toml b/compiler/rustc_symbol_mangling/Cargo.toml index 0ceac4b3e1b..0df9c7682bf 100644 --- a/compiler/rustc_symbol_mangling/Cargo.toml +++ b/compiler/rustc_symbol_mangling/Cargo.toml @@ -6,7 +6,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start punycode = "0.4.0" -rustc-demangle.workspace = true +rustc-demangle = "0.1.21" rustc_abi = { path = "../rustc_abi" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } @@ -15,5 +15,5 @@ rustc_hir = { path = "../rustc_hir" } rustc_middle = { path = "../rustc_middle" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } -tracing.workspace = true +tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 0cbd48ba08c..0655c2d5e81 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -82,9 +82,13 @@ pub(super) fn mangle<'tcx>( } pub fn mangle_internal_symbol<'tcx>(tcx: TyCtxt<'tcx>, item_name: &str) -> String { - if item_name == "rust_eh_personality" { + match item_name { // rust_eh_personality must not be renamed as LLVM hard-codes the name - return "rust_eh_personality".to_owned(); + "rust_eh_personality" => return item_name.to_owned(), + // Apple availability symbols need to not be mangled to be usable by + // C/Objective-C code. + "__isPlatformVersionAtLeast" | "__isOSVersionAtLeast" => return item_name.to_owned(), + _ => {} } let prefix = "_R"; diff --git a/compiler/rustc_target/Cargo.toml b/compiler/rustc_target/Cargo.toml index ed59ee2a575..7fabb227d9a 100644 --- a/compiler/rustc_target/Cargo.toml +++ b/compiler/rustc_target/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -bitflags.workspace = true +bitflags = "2.4.1" object = { version = "0.37.0", default-features = false, features = ["elf", "macho"] } rustc_abi = { path = "../rustc_abi" } rustc_data_structures = { path = "../rustc_data_structures" } @@ -16,8 +16,8 @@ rustc_serialize = { path = "../rustc_serialize" } rustc_span = { path = "../rustc_span" } serde = "1.0.219" serde_derive = "1.0.219" -serde_json.workspace = true +serde_json = "1.0.59" serde_path_to_error = "0.1.17" -tracing.workspace = true +tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_target/src/callconv/loongarch.rs b/compiler/rustc_target/src/callconv/loongarch.rs index d567ad401bb..9213d73e24e 100644 --- a/compiler/rustc_target/src/callconv/loongarch.rs +++ b/compiler/rustc_target/src/callconv/loongarch.rs @@ -8,16 +8,16 @@ use crate::spec::HasTargetSpec; #[derive(Copy, Clone)] enum RegPassKind { - Float(Reg), - Integer(Reg), + Float { offset_from_start: Size, ty: Reg }, + Integer { offset_from_start: Size, ty: Reg }, Unknown, } #[derive(Copy, Clone)] enum FloatConv { - FloatPair(Reg, Reg), + FloatPair { first_ty: Reg, second_ty_offset_from_start: Size, second_ty: Reg }, Float(Reg), - MixedPair(Reg, Reg), + MixedPair { first_ty: Reg, second_ty_offset_from_start: Size, second_ty: Reg }, } #[derive(Copy, Clone)] @@ -37,6 +37,7 @@ fn should_use_fp_conv_helper<'a, Ty, C>( flen: u64, field1_kind: &mut RegPassKind, field2_kind: &mut RegPassKind, + offset_from_start: Size, ) -> Result<(), CannotUseFpConv> where Ty: TyAbiInterface<'a, C> + Copy, @@ -49,16 +50,16 @@ where } match (*field1_kind, *field2_kind) { (RegPassKind::Unknown, _) => { - *field1_kind = RegPassKind::Integer(Reg { - kind: RegKind::Integer, - size: arg_layout.size, - }); + *field1_kind = RegPassKind::Integer { + offset_from_start, + ty: Reg { kind: RegKind::Integer, size: arg_layout.size }, + }; } - (RegPassKind::Float(_), RegPassKind::Unknown) => { - *field2_kind = RegPassKind::Integer(Reg { - kind: RegKind::Integer, - size: arg_layout.size, - }); + (RegPassKind::Float { .. }, RegPassKind::Unknown) => { + *field2_kind = RegPassKind::Integer { + offset_from_start, + ty: Reg { kind: RegKind::Integer, size: arg_layout.size }, + }; } _ => return Err(CannotUseFpConv), } @@ -69,12 +70,16 @@ where } match (*field1_kind, *field2_kind) { (RegPassKind::Unknown, _) => { - *field1_kind = - RegPassKind::Float(Reg { kind: RegKind::Float, size: arg_layout.size }); + *field1_kind = RegPassKind::Float { + offset_from_start, + ty: Reg { kind: RegKind::Float, size: arg_layout.size }, + }; } (_, RegPassKind::Unknown) => { - *field2_kind = - RegPassKind::Float(Reg { kind: RegKind::Float, size: arg_layout.size }); + *field2_kind = RegPassKind::Float { + offset_from_start, + ty: Reg { kind: RegKind::Float, size: arg_layout.size }, + }; } _ => return Err(CannotUseFpConv), } @@ -96,13 +101,14 @@ where flen, field1_kind, field2_kind, + offset_from_start, ); } return Err(CannotUseFpConv); } } FieldsShape::Array { count, .. } => { - for _ in 0..count { + for i in 0..count { let elem_layout = arg_layout.field(cx, 0); should_use_fp_conv_helper( cx, @@ -111,6 +117,7 @@ where flen, field1_kind, field2_kind, + offset_from_start + elem_layout.size * i, )?; } } @@ -121,7 +128,15 @@ where } for i in arg_layout.fields.index_by_increasing_offset() { let field = arg_layout.field(cx, i); - should_use_fp_conv_helper(cx, &field, xlen, flen, field1_kind, field2_kind)?; + should_use_fp_conv_helper( + cx, + &field, + xlen, + flen, + field1_kind, + field2_kind, + offset_from_start + arg_layout.fields.offset(i), + )?; } } }, @@ -140,14 +155,52 @@ where { let mut field1_kind = RegPassKind::Unknown; let mut field2_kind = RegPassKind::Unknown; - if should_use_fp_conv_helper(cx, arg, xlen, flen, &mut field1_kind, &mut field2_kind).is_err() { + if should_use_fp_conv_helper( + cx, + arg, + xlen, + flen, + &mut field1_kind, + &mut field2_kind, + Size::ZERO, + ) + .is_err() + { return None; } match (field1_kind, field2_kind) { - (RegPassKind::Integer(l), RegPassKind::Float(r)) => Some(FloatConv::MixedPair(l, r)), - (RegPassKind::Float(l), RegPassKind::Integer(r)) => Some(FloatConv::MixedPair(l, r)), - (RegPassKind::Float(l), RegPassKind::Float(r)) => Some(FloatConv::FloatPair(l, r)), - (RegPassKind::Float(f), RegPassKind::Unknown) => Some(FloatConv::Float(f)), + ( + RegPassKind::Integer { offset_from_start, .. } + | RegPassKind::Float { offset_from_start, .. }, + _, + ) if offset_from_start != Size::ZERO => { + panic!("type {:?} has a first field with non-zero offset {offset_from_start:?}", arg.ty) + } + ( + RegPassKind::Integer { ty: first_ty, .. }, + RegPassKind::Float { offset_from_start, ty: second_ty }, + ) => Some(FloatConv::MixedPair { + first_ty, + second_ty_offset_from_start: offset_from_start, + second_ty, + }), + ( + RegPassKind::Float { ty: first_ty, .. }, + RegPassKind::Integer { offset_from_start, ty: second_ty }, + ) => Some(FloatConv::MixedPair { + first_ty, + second_ty_offset_from_start: offset_from_start, + second_ty, + }), + ( + RegPassKind::Float { ty: first_ty, .. }, + RegPassKind::Float { offset_from_start, ty: second_ty }, + ) => Some(FloatConv::FloatPair { + first_ty, + second_ty_offset_from_start: offset_from_start, + second_ty, + }), + (RegPassKind::Float { ty, .. }, RegPassKind::Unknown) => Some(FloatConv::Float(ty)), _ => None, } } @@ -165,11 +218,19 @@ where FloatConv::Float(f) => { arg.cast_to(f); } - FloatConv::FloatPair(l, r) => { - arg.cast_to(CastTarget::pair(l, r)); + FloatConv::FloatPair { first_ty, second_ty_offset_from_start, second_ty } => { + arg.cast_to(CastTarget::offset_pair( + first_ty, + second_ty_offset_from_start, + second_ty, + )); } - FloatConv::MixedPair(l, r) => { - arg.cast_to(CastTarget::pair(l, r)); + FloatConv::MixedPair { first_ty, second_ty_offset_from_start, second_ty } => { + arg.cast_to(CastTarget::offset_pair( + first_ty, + second_ty_offset_from_start, + second_ty, + )); } } return false; @@ -233,15 +294,27 @@ fn classify_arg<'a, Ty, C>( arg.cast_to(f); return; } - Some(FloatConv::FloatPair(l, r)) if *avail_fprs >= 2 => { + Some(FloatConv::FloatPair { first_ty, second_ty_offset_from_start, second_ty }) + if *avail_fprs >= 2 => + { *avail_fprs -= 2; - arg.cast_to(CastTarget::pair(l, r)); + arg.cast_to(CastTarget::offset_pair( + first_ty, + second_ty_offset_from_start, + second_ty, + )); return; } - Some(FloatConv::MixedPair(l, r)) if *avail_fprs >= 1 && *avail_gprs >= 1 => { + Some(FloatConv::MixedPair { first_ty, second_ty_offset_from_start, second_ty }) + if *avail_fprs >= 1 && *avail_gprs >= 1 => + { *avail_gprs -= 1; *avail_fprs -= 1; - arg.cast_to(CastTarget::pair(l, r)); + arg.cast_to(CastTarget::offset_pair( + first_ty, + second_ty_offset_from_start, + second_ty, + )); return; } _ => (), diff --git a/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_msvc.rs index f1b6fa123de..cd55576ef81 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_msvc.rs @@ -15,7 +15,7 @@ pub(crate) fn target() -> Target { llvm_target: "aarch64-pc-windows-msvc".into(), metadata: TargetMetadata { description: Some("ARM64 Windows MSVC".into()), - tier: Some(2), + tier: Some(1), host_tools: Some(true), std: Some(true), }, diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_trusty.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_trusty.rs index 126f0251239..05783fde1ad 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_trusty.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_trusty.rs @@ -11,7 +11,7 @@ pub(crate) fn target() -> Target { description: Some("ARM64 Trusty".into()), tier: Some(3), host_tools: Some(false), - std: Some(false), + std: Some(true), }, pointer_width: 64, data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), diff --git a/compiler/rustc_target/src/spec/targets/arm64ec_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/arm64ec_pc_windows_msvc.rs index 8f93523909e..7292412a18d 100644 --- a/compiler/rustc_target/src/spec/targets/arm64ec_pc_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/targets/arm64ec_pc_windows_msvc.rs @@ -20,9 +20,9 @@ pub(crate) fn target() -> Target { llvm_target: "arm64ec-pc-windows-msvc".into(), metadata: TargetMetadata { description: Some("Arm64EC Windows MSVC".into()), - tier: Some(3), + tier: Some(2), host_tools: Some(false), - std: None, // ? + std: Some(true), }, pointer_width: 64, data_layout: diff --git a/compiler/rustc_target/src/spec/targets/armv7_unknown_trusty.rs b/compiler/rustc_target/src/spec/targets/armv7_unknown_trusty.rs index 31d492e83cd..2b0b0e1d117 100644 --- a/compiler/rustc_target/src/spec/targets/armv7_unknown_trusty.rs +++ b/compiler/rustc_target/src/spec/targets/armv7_unknown_trusty.rs @@ -13,7 +13,7 @@ pub(crate) fn target() -> Target { description: Some("Armv7-A Trusty".into()), tier: Some(3), host_tools: Some(false), - std: Some(false), + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none.rs index 91e3064aaed..b6a08958284 100644 --- a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none.rs +++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none.rs @@ -17,7 +17,7 @@ pub(crate) fn target() -> Target { arch: "loongarch64".into(), options: TargetOptions { cpu: "generic".into(), - features: "+f,+d".into(), + features: "+f,+d,-lsx".into(), linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), llvm_abiname: "lp64d".into(), diff --git a/compiler/rustc_target/src/spec/targets/nvptx64_nvidia_cuda.rs b/compiler/rustc_target/src/spec/targets/nvptx64_nvidia_cuda.rs index 598f0f19f0d..cada0dd640a 100644 --- a/compiler/rustc_target/src/spec/targets/nvptx64_nvidia_cuda.rs +++ b/compiler/rustc_target/src/spec/targets/nvptx64_nvidia_cuda.rs @@ -6,7 +6,7 @@ use crate::spec::{ pub(crate) fn target() -> Target { Target { arch: "nvptx64".into(), - data_layout: "e-p6:32:32-i64:64-i128:128-v16:16-v32:32-n16:32:64".into(), + data_layout: "e-p6:32:32-i64:64-i128:128-i256:256-v16:16-v32:32-n16:32:64".into(), llvm_target: "nvptx64-nvidia-cuda".into(), metadata: TargetMetadata { description: Some("--emit=asm generates PTX code that runs on NVIDIA GPUs".into()), diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_trusty.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_trusty.rs index c7b002bc9bb..dbcb5fd4e11 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_trusty.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_trusty.rs @@ -12,7 +12,7 @@ pub(crate) fn target() -> Target { description: Some("x86_64 Trusty".into()), tier: Some(3), host_tools: Some(false), - std: Some(false), + std: Some(true), }, pointer_width: 64, data_layout: diff --git a/compiler/rustc_thread_pool/Cargo.toml b/compiler/rustc_thread_pool/Cargo.toml index 8e8c6469512..c92984470b7 100644 --- a/compiler/rustc_thread_pool/Cargo.toml +++ b/compiler/rustc_thread_pool/Cargo.toml @@ -20,10 +20,10 @@ smallvec = "1.8.1" [dev-dependencies] rand = "0.9" rand_xorshift = "0.4" -scoped-tls.workspace = true +scoped-tls = "1.0" [target.'cfg(unix)'.dev-dependencies] -libc.workspace = true +libc = "0.2" [[test]] name = "stack_overflow_crash" diff --git a/compiler/rustc_trait_selection/Cargo.toml b/compiler/rustc_trait_selection/Cargo.toml index 6b79b98d1bf..1071105522d 100644 --- a/compiler/rustc_trait_selection/Cargo.toml +++ b/compiler/rustc_trait_selection/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -itertools.workspace = true +itertools = "0.12" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } @@ -21,6 +21,6 @@ rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_transmute = { path = "../rustc_transmute", features = ["rustc"] } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } -thin-vec.workspace = true -tracing.workspace = true +thin-vec = "0.2" +tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index 9447612d026..812e20e4338 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -318,7 +318,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let (expected, found) = if expected_str == found_str { (join_path_syms(&expected_abs), join_path_syms(&found_abs)) } else { - (expected_str.clone(), found_str.clone()) + (expected_str, found_str) }; // We've displayed "expected `a::b`, found `a::b`". We add context to diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 40a27c9aebe..f6dbbeb51ca 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -32,8 +32,8 @@ use rustc_middle::ty::print::{ }; use rustc_middle::ty::{ self, AdtKind, GenericArgs, InferTy, IsSuggestable, Ty, TyCtxt, TypeFoldable, TypeFolder, - TypeSuperFoldable, TypeVisitableExt, TypeckResults, Upcast, suggest_arbitrary_trait_bound, - suggest_constraining_type_param, + TypeSuperFoldable, TypeSuperVisitable, TypeVisitableExt, TypeVisitor, TypeckResults, Upcast, + suggest_arbitrary_trait_bound, suggest_constraining_type_param, }; use rustc_middle::{bug, span_bug}; use rustc_span::def_id::LocalDefId; @@ -263,6 +263,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { _ => (false, None), }; + let mut finder = ParamFinder { .. }; + finder.visit_binder(&trait_pred); + // FIXME: Add check for trait bound that is already present, particularly `?Sized` so we // don't suggest `T: Sized + ?Sized`. loop { @@ -411,6 +414,26 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } + hir::Node::TraitItem(hir::TraitItem { + generics, + kind: hir::TraitItemKind::Fn(..), + .. + }) + | hir::Node::ImplItem(hir::ImplItem { + generics, + trait_item_def_id: None, + kind: hir::ImplItemKind::Fn(..), + .. + }) if finder.can_suggest_bound(generics) => { + // Missing generic type parameter bound. + suggest_arbitrary_trait_bound( + self.tcx, + generics, + err, + trait_pred, + associated_ty, + ); + } hir::Node::Item(hir::Item { kind: hir::ItemKind::Struct(_, generics, _) @@ -423,7 +446,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { | hir::ItemKind::Const(_, generics, _, _) | hir::ItemKind::TraitAlias(_, generics, _), .. - }) if !param_ty => { + }) if finder.can_suggest_bound(generics) => { // Missing generic type parameter bound. if suggest_arbitrary_trait_bound( self.tcx, @@ -5068,8 +5091,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // Suggesting `T: ?Sized` is only valid in an ADT if `T` is only used in a // borrow. `struct S<'a, T: ?Sized>(&'a T);` is valid, `struct S<T: ?Sized>(T);` // is not. Look for invalid "bare" parameter uses, and suggest using indirection. - let mut visitor = - FindTypeParam { param: param.name.ident().name, invalid_spans: vec![], nested: false }; + let mut visitor = FindTypeParam { param: param.name.ident().name, .. }; visitor.visit_item(item); if visitor.invalid_spans.is_empty() { return false; @@ -5228,7 +5250,7 @@ fn hint_missing_borrow<'tcx>( /// Used to suggest replacing associated types with an explicit type in `where` clauses. #[derive(Debug)] pub struct SelfVisitor<'v> { - pub paths: Vec<&'v hir::Ty<'v>>, + pub paths: Vec<&'v hir::Ty<'v>> = Vec::new(), pub name: Option<Symbol>, } @@ -5599,7 +5621,7 @@ fn point_at_assoc_type_restriction<G: EmissionGuarantee>( ); // Search for the associated type `Self::{name}`, get // its type and suggest replacing the bound with it. - let mut visitor = SelfVisitor { paths: vec![], name: Some(name) }; + let mut visitor = SelfVisitor { name: Some(name), .. }; visitor.visit_trait_ref(trait_ref); for path in visitor.paths { err.span_suggestion_verbose( @@ -5610,7 +5632,7 @@ fn point_at_assoc_type_restriction<G: EmissionGuarantee>( ); } } else { - let mut visitor = SelfVisitor { paths: vec![], name: None }; + let mut visitor = SelfVisitor { name: None, .. }; visitor.visit_trait_ref(trait_ref); let span: MultiSpan = visitor.paths.iter().map(|p| p.span).collect::<Vec<Span>>().into(); @@ -5640,8 +5662,8 @@ fn get_deref_type_and_refs(mut ty: Ty<'_>) -> (Ty<'_>, Vec<hir::Mutability>) { /// `param: ?Sized` would be a valid constraint. struct FindTypeParam { param: rustc_span::Symbol, - invalid_spans: Vec<Span>, - nested: bool, + invalid_spans: Vec<Span> = Vec::new(), + nested: bool = false, } impl<'v> Visitor<'v> for FindTypeParam { @@ -5679,3 +5701,38 @@ impl<'v> Visitor<'v> for FindTypeParam { } } } + +/// Look for type parameters in predicates. We use this to identify whether a bound is suitable in +/// on a given item. +struct ParamFinder { + params: Vec<Symbol> = Vec::new(), +} + +impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ParamFinder { + fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result { + match t.kind() { + ty::Param(p) => self.params.push(p.name), + _ => {} + } + t.super_visit_with(self) + } +} + +impl ParamFinder { + /// Whether the `hir::Generics` of the current item can suggest the evaluated bound because its + /// references to type parameters are present in the generics. + fn can_suggest_bound(&self, generics: &hir::Generics<'_>) -> bool { + if self.params.is_empty() { + // There are no references to type parameters at all, so suggesting the bound + // would be reasonable. + return true; + } + generics.params.iter().any(|p| match p.name { + hir::ParamName::Plain(p_name) => { + // All of the parameters in the bound can be referenced in the current item. + self.params.iter().any(|p| *p == p_name.name || *p == kw::SelfUpper) + } + _ => true, + }) + } +} diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index e2b22f7bab7..fc0cf8f140a 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -19,6 +19,7 @@ #![feature(assert_matches)] #![feature(associated_type_defaults)] #![feature(box_patterns)] +#![feature(default_field_values)] #![feature(if_let_guard)] #![feature(iter_intersperse)] #![feature(iterator_try_reduce)] diff --git a/compiler/rustc_trait_selection/src/solve.rs b/compiler/rustc_trait_selection/src/solve.rs index f58961683a9..5d200c4d340 100644 --- a/compiler/rustc_trait_selection/src/solve.rs +++ b/compiler/rustc_trait_selection/src/solve.rs @@ -13,4 +13,20 @@ pub use normalize::{ deeply_normalize, deeply_normalize_with_skipped_universes, deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals, }; +use rustc_middle::query::Providers; +use rustc_middle::ty::TyCtxt; pub use select::InferCtxtSelectExt; + +fn evaluate_root_goal_for_proof_tree_raw<'tcx>( + tcx: TyCtxt<'tcx>, + canonical_input: CanonicalInput<TyCtxt<'tcx>>, +) -> (QueryResult<TyCtxt<'tcx>>, &'tcx inspect::Probe<TyCtxt<'tcx>>) { + evaluate_root_goal_for_proof_tree_raw_provider::<SolverDelegate<'tcx>, TyCtxt<'tcx>>( + tcx, + canonical_input, + ) +} + +pub fn provide(providers: &mut Providers) { + *providers = Providers { evaluate_root_goal_for_proof_tree_raw, ..*providers }; +} diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index 308486811e6..342d7121fc3 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -37,7 +37,7 @@ pub struct InspectGoal<'a, 'tcx> { orig_values: Vec<ty::GenericArg<'tcx>>, goal: Goal<'tcx, ty::Predicate<'tcx>>, result: Result<Certainty, NoSolution>, - evaluation_kind: inspect::GoalEvaluationKind<TyCtxt<'tcx>>, + final_revision: &'tcx inspect::Probe<TyCtxt<'tcx>>, normalizes_to_term_hack: Option<NormalizesToTermHack<'tcx>>, source: GoalSource, } @@ -249,7 +249,7 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> { // `InspectGoal::new` so that the goal has the right result (and maintains // the impression that we don't do this normalizes-to infer hack at all). let (nested, proof_tree) = infcx.evaluate_root_goal_for_proof_tree(goal, span); - let nested_goals_result = nested.and_then(|(nested, _)| { + let nested_goals_result = nested.and_then(|nested| { normalizes_to_term_hack.constrain_and( infcx, span, @@ -391,15 +391,8 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { pub fn candidates(&'a self) -> Vec<InspectCandidate<'a, 'tcx>> { let mut candidates = vec![]; - let last_eval_step = match &self.evaluation_kind { - // An annoying edge case in case the recursion limit is 0. - inspect::GoalEvaluationKind::Overflow => return vec![], - inspect::GoalEvaluationKind::Evaluation { final_revision } => final_revision, - }; - let mut nested_goals = vec![]; - self.candidates_recur(&mut candidates, &mut nested_goals, &last_eval_step); - + self.candidates_recur(&mut candidates, &mut nested_goals, &self.final_revision); candidates } @@ -426,7 +419,8 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { ) -> Self { let infcx = <&SolverDelegate<'tcx>>::from(infcx); - let inspect::GoalEvaluation { uncanonicalized_goal, orig_values, kind, result } = root; + let inspect::GoalEvaluation { uncanonicalized_goal, orig_values, final_revision, result } = + root; // If there's a normalizes-to goal, AND the evaluation result with the result of // constraining the normalizes-to RHS and computing the nested goals. let result = result.and_then(|ok| { @@ -441,7 +435,7 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { orig_values, goal: eager_resolve_vars(infcx, uncanonicalized_goal), result, - evaluation_kind: kind, + final_revision, normalizes_to_term_hack: term_hack_and_nested_certainty.map(|(n, _)| n), source, } diff --git a/compiler/rustc_traits/Cargo.toml b/compiler/rustc_traits/Cargo.toml index 24360a94cc7..9f40f4d5c23 100644 --- a/compiler/rustc_traits/Cargo.toml +++ b/compiler/rustc_traits/Cargo.toml @@ -10,5 +10,5 @@ rustc_infer = { path = "../rustc_infer" } rustc_middle = { path = "../rustc_middle" } rustc_span = { path = "../rustc_span" } rustc_trait_selection = { path = "../rustc_trait_selection" } -tracing.workspace = true +tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_transmute/Cargo.toml b/compiler/rustc_transmute/Cargo.toml index 2ac2b46cc58..e61717e5e9c 100644 --- a/compiler/rustc_transmute/Cargo.toml +++ b/compiler/rustc_transmute/Cargo.toml @@ -11,12 +11,12 @@ rustc_hir = { path = "../rustc_hir", optional = true } rustc_middle = { path = "../rustc_middle", optional = true } rustc_span = { path = "../rustc_span", optional = true } smallvec = "1.8.1" -tracing.workspace = true +tracing = "0.1" # tidy-alphabetical-end [dev-dependencies] # tidy-alphabetical-start -itertools.workspace = true +itertools = "0.12" # tidy-alphabetical-end [features] diff --git a/compiler/rustc_ty_utils/Cargo.toml b/compiler/rustc_ty_utils/Cargo.toml index 394cde5a323..ce08b300cc8 100644 --- a/compiler/rustc_ty_utils/Cargo.toml +++ b/compiler/rustc_ty_utils/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -itertools.workspace = true +itertools = "0.12" rustc_abi = { path = "../rustc_abi" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } @@ -20,5 +20,5 @@ rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } -tracing.workspace = true +tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_type_ir/Cargo.toml b/compiler/rustc_type_ir/Cargo.toml index 42860fa2d88..d55e9b3b1be 100644 --- a/compiler/rustc_type_ir/Cargo.toml +++ b/compiler/rustc_type_ir/Cargo.toml @@ -5,11 +5,11 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -bitflags.workspace = true -derive-where.workspace = true +bitflags = "2.4.1" +derive-where = "1.2.7" ena = "0.14.3" -indexmap.workspace = true -rustc-hash.workspace = true +indexmap = "2.0.0" +rustc-hash = "2.0.0" rustc_ast_ir = { path = "../rustc_ast_ir", default-features = false } rustc_data_structures = { path = "../rustc_data_structures", optional = true } rustc_error_messages = { path = "../rustc_error_messages", optional = true } @@ -19,8 +19,8 @@ rustc_serialize = { path = "../rustc_serialize", optional = true } rustc_span = { path = "../rustc_span", optional = true } rustc_type_ir_macros = { path = "../rustc_type_ir_macros" } smallvec = { version = "1.8.1", default-features = false, features = ["const_generics"] } -thin-vec.workspace = true -tracing.workspace = true +thin-vec = "0.2.12" +tracing = "0.1" # tidy-alphabetical-end [features] diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 03a8f5e74b4..3a83b1739ff 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -1,3 +1,4 @@ +use std::borrow::Borrow; use std::fmt::Debug; use std::hash::Hash; use std::ops::Deref; @@ -10,7 +11,9 @@ use crate::inherent::*; use crate::ir_print::IrPrint; use crate::lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem}; use crate::relate::Relate; -use crate::solve::{CanonicalInput, ExternalConstraintsData, PredefinedOpaquesData, QueryResult}; +use crate::solve::{ + CanonicalInput, ExternalConstraintsData, PredefinedOpaquesData, QueryResult, inspect, +}; use crate::visit::{Flags, TypeVisitable}; use crate::{self as ty, CanonicalParamEnvCacheEntry, search_graph}; @@ -394,6 +397,13 @@ pub trait Interner: self, defining_anchor: Self::LocalDefId, ) -> Self::LocalDefIds; + + type Probe: Debug + Hash + Eq + Borrow<inspect::Probe<Self>>; + fn mk_probe(self, probe: inspect::Probe<Self>) -> Self::Probe; + fn evaluate_root_goal_for_proof_tree_raw( + self, + canonical_goal: CanonicalInput<Self>, + ) -> (QueryResult<Self>, Self::Probe); } /// Imagine you have a function `F: FnOnce(&[T]) -> R`, plus an iterator `iter` diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index 348178a527f..dbbc0c217d7 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -92,11 +92,7 @@ pub trait Delegate: Sized { input: <Self::Cx as Cx>::Input, result: <Self::Cx as Cx>::Result, ) -> bool; - fn on_stack_overflow( - cx: Self::Cx, - input: <Self::Cx as Cx>::Input, - inspect: &mut Self::ProofTreeBuilder, - ) -> <Self::Cx as Cx>::Result; + fn on_stack_overflow(cx: Self::Cx, input: <Self::Cx as Cx>::Input) -> <Self::Cx as Cx>::Result; fn on_fixpoint_overflow( cx: Self::Cx, input: <Self::Cx as Cx>::Input, @@ -703,6 +699,31 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { } } + pub fn evaluate_root_goal_for_proof_tree( + cx: X, + root_depth: usize, + input: X::Input, + inspect: &mut D::ProofTreeBuilder, + ) -> X::Result { + let mut this = SearchGraph::<D>::new(root_depth); + let available_depth = AvailableDepth(root_depth); + let step_kind_from_parent = PathKind::Inductive; // is never used + this.stack.push(StackEntry { + input, + step_kind_from_parent, + available_depth, + provisional_result: None, + required_depth: 0, + heads: Default::default(), + encountered_overflow: false, + usages: None, + candidate_usages: None, + nested_goals: Default::default(), + }); + let evaluation_result = this.evaluate_goal_in_task(cx, input, inspect); + evaluation_result.result + } + /// Probably the most involved method of the whole solver. /// /// While goals get computed via `D::compute_goal`, this function handles @@ -718,7 +739,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { let Some(available_depth) = AvailableDepth::allowed_depth_for_nested::<D>(self.root_depth, &self.stack) else { - return self.handle_overflow(cx, input, inspect); + return self.handle_overflow(cx, input); }; // We check the provisional cache before checking the global cache. This simplifies @@ -833,12 +854,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { result } - fn handle_overflow( - &mut self, - cx: X, - input: X::Input, - inspect: &mut D::ProofTreeBuilder, - ) -> X::Result { + fn handle_overflow(&mut self, cx: X, input: X::Input) -> X::Result { if let Some(last) = self.stack.last_mut() { last.encountered_overflow = true; // If computing a goal `B` depends on another goal `A` and @@ -853,7 +869,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { } debug!("encountered stack overflow"); - D::on_stack_overflow(cx, input, inspect) + D::on_stack_overflow(cx, input) } /// When reevaluating a goal with a changed provisional result, all provisional cache entry diff --git a/compiler/rustc_type_ir/src/solve/inspect.rs b/compiler/rustc_type_ir/src/solve/inspect.rs index afb4043648f..3af2f8dbaf2 100644 --- a/compiler/rustc_type_ir/src/solve/inspect.rs +++ b/compiler/rustc_type_ir/src/solve/inspect.rs @@ -45,31 +45,18 @@ pub type CanonicalState<I, T> = Canonical<I, State<I, T>>; /// for the `CanonicalVarValues` of the canonicalized goal. /// We use this to map any [CanonicalState] from the local `InferCtxt` /// of the solver query to the `InferCtxt` of the caller. -#[derive_where(PartialEq, Hash; I: Interner)] +#[derive_where(PartialEq, Eq, Hash; I: Interner)] pub struct GoalEvaluation<I: Interner> { pub uncanonicalized_goal: Goal<I, I::Predicate>, pub orig_values: Vec<I::GenericArg>, - pub kind: GoalEvaluationKind<I>, + pub final_revision: I::Probe, pub result: QueryResult<I>, } -impl<I: Interner> Eq for GoalEvaluation<I> {} - -#[derive_where(PartialEq, Hash, Debug; I: Interner)] -pub enum GoalEvaluationKind<I: Interner> { - Overflow, - Evaluation { - /// This is always `ProbeKind::Root`. - final_revision: Probe<I>, - }, -} - -impl<I: Interner> Eq for GoalEvaluationKind<I> {} - /// A self-contained computation during trait solving. This either /// corresponds to a `EvalCtxt::probe(_X)` call or the root evaluation /// of a goal. -#[derive_where(PartialEq, Hash, Debug; I: Interner)] +#[derive_where(PartialEq, Eq, Hash, Debug; I: Interner)] pub struct Probe<I: Interner> { /// What happened inside of this probe in chronological order. pub steps: Vec<ProbeStep<I>>, @@ -77,9 +64,7 @@ pub struct Probe<I: Interner> { pub final_state: CanonicalState<I, ()>, } -impl<I: Interner> Eq for Probe<I> {} - -#[derive_where(PartialEq, Hash, Debug; I: Interner)] +#[derive_where(PartialEq, Eq, Hash, Debug; I: Interner)] pub enum ProbeStep<I: Interner> { /// We added a goal to the `EvalCtxt` which will get proven /// the next time `EvalCtxt::try_evaluate_added_goals` is called. @@ -98,12 +83,10 @@ pub enum ProbeStep<I: Interner> { MakeCanonicalResponse { shallow_certainty: Certainty }, } -impl<I: Interner> Eq for ProbeStep<I> {} - /// What kind of probe we're in. In case the probe represents a candidate, or /// the final result of the current goal - via [ProbeKind::Root] - we also /// store the [QueryResult]. -#[derive_where(Clone, Copy, PartialEq, Hash, Debug; I: Interner)] +#[derive_where(Clone, Copy, PartialEq, Eq, Hash, Debug; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic)] pub enum ProbeKind<I: Interner> { /// The root inference context while proving a goal. @@ -128,5 +111,3 @@ pub enum ProbeKind<I: Interner> { /// Checking that a rigid alias is well-formed. RigidAlias { result: QueryResult<I> }, } - -impl<I: Interner> Eq for ProbeKind<I> {} diff --git a/compiler/rustc_type_ir_macros/Cargo.toml b/compiler/rustc_type_ir_macros/Cargo.toml index 29a2cc89033..15a55575099 100644 --- a/compiler/rustc_type_ir_macros/Cargo.toml +++ b/compiler/rustc_type_ir_macros/Cargo.toml @@ -8,8 +8,8 @@ proc-macro = true [dependencies] # tidy-alphabetical-start -proc-macro2.workspace = true -quote.workspace = true +proc-macro2 = "1" +quote = "1" syn = { version = "2.0.9", features = ["full"] } synstructure = "0.13.0" # tidy-alphabetical-end diff --git a/library/Cargo.lock b/library/Cargo.lock index 8b860f69492..e601137e005 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -327,7 +327,8 @@ dependencies = [ "rustc-demangle", "std_detect", "unwind", - "wasi", + "wasi 0.11.1+wasi-snapshot-preview1", + "wasi 0.14.4+wasi-0.2.4", "windows-targets 0.0.0", ] @@ -400,6 +401,17 @@ dependencies = [ ] [[package]] +name = "wasi" +version = "0.14.4+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a5f4a424faf49c3c2c344f166f0662341d470ea185e939657aaff130f0ec4a" +dependencies = [ + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", + "wit-bindgen", +] + +[[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -475,3 +487,13 @@ name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814" +dependencies = [ + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", +] diff --git a/library/alloc/src/borrow.rs b/library/alloc/src/borrow.rs index 07f51b7614f..cb32896161e 100644 --- a/library/alloc/src/borrow.rs +++ b/library/alloc/src/borrow.rs @@ -17,9 +17,11 @@ use crate::fmt; use crate::string::String; #[stable(feature = "rust1", since = "1.0.0")] -impl<'a, B: ?Sized> Borrow<B> for Cow<'a, B> +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl<'a, B: ?Sized> const Borrow<B> for Cow<'a, B> where B: ToOwned, + B::Owned: [const] Borrow<B>, { fn borrow(&self) -> &B { &**self @@ -326,9 +328,10 @@ impl<B: ?Sized + ToOwned> Cow<'_, B> { } #[stable(feature = "rust1", since = "1.0.0")] -impl<B: ?Sized + ToOwned> Deref for Cow<'_, B> +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl<B: ?Sized + ToOwned> const Deref for Cow<'_, B> where - B::Owned: Borrow<B>, + B::Owned: [const] Borrow<B>, { type Target = B; @@ -439,7 +442,11 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl<T: ?Sized + ToOwned> AsRef<T> for Cow<'_, T> { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl<T: ?Sized + ToOwned> const AsRef<T> for Cow<'_, T> +where + T::Owned: [const] Borrow<T>, +{ fn as_ref(&self) -> &T { self } diff --git a/library/alloc/src/collections/mod.rs b/library/alloc/src/collections/mod.rs index fac4d1a65ab..212d7c8465b 100644 --- a/library/alloc/src/collections/mod.rs +++ b/library/alloc/src/collections/mod.rs @@ -128,8 +128,9 @@ pub use realalloc::collections::TryReserveErrorKind; reason = "Uncertain how much info should be exposed", issue = "48043" )] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] #[cfg(not(test))] -impl From<TryReserveErrorKind> for TryReserveError { +impl const From<TryReserveErrorKind> for TryReserveError { #[inline] fn from(kind: TryReserveErrorKind) -> Self { Self { kind } @@ -137,8 +138,9 @@ impl From<TryReserveErrorKind> for TryReserveError { } #[unstable(feature = "try_reserve_kind", reason = "new API", issue = "48043")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] #[cfg(not(test))] -impl From<LayoutError> for TryReserveErrorKind { +impl const From<LayoutError> for TryReserveErrorKind { /// Always evaluates to [`TryReserveErrorKind::CapacityOverflow`]. #[inline] fn from(_: LayoutError) -> Self { diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 711092ae8eb..cba1ce40f75 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -107,10 +107,10 @@ #![feature(char_max_len)] #![feature(clone_to_uninit)] #![feature(coerce_unsized)] +#![feature(const_convert)] #![feature(const_default)] #![feature(const_eval_select)] #![feature(const_heap)] -#![feature(const_trait_impl)] #![feature(core_intrinsics)] #![feature(deprecated_suggestion)] #![feature(deref_pure_trait)] @@ -168,6 +168,7 @@ #![feature(allow_internal_unstable)] #![feature(cfg_sanitize)] #![feature(const_precise_live_drops)] +#![feature(const_trait_impl)] #![feature(coroutine_trait)] #![feature(decl_macro)] #![feature(dropck_eyepatch)] diff --git a/library/alloc/src/raw_vec/mod.rs b/library/alloc/src/raw_vec/mod.rs index fd05f9ca464..b0027e964e4 100644 --- a/library/alloc/src/raw_vec/mod.rs +++ b/library/alloc/src/raw_vec/mod.rs @@ -468,10 +468,6 @@ impl<A: Allocator> RawVecInner<A> { return Ok(Self::new_in(alloc, elem_layout.alignment())); } - if let Err(err) = alloc_guard(layout.size()) { - return Err(err); - } - let result = match init { AllocInit::Uninitialized => alloc.allocate(layout), #[cfg(not(no_global_oom_handling))] @@ -662,7 +658,7 @@ impl<A: Allocator> RawVecInner<A> { let new_layout = layout_array(cap, elem_layout)?; let ptr = finish_grow(new_layout, self.current_memory(elem_layout), &mut self.alloc)?; - // SAFETY: finish_grow would have resulted in a capacity overflow if we tried to allocate more than `isize::MAX` items + // SAFETY: layout_array would have resulted in a capacity overflow if we tried to allocate more than `isize::MAX` items unsafe { self.set_ptr_and_cap(ptr, cap) }; Ok(()) @@ -684,7 +680,7 @@ impl<A: Allocator> RawVecInner<A> { let new_layout = layout_array(cap, elem_layout)?; let ptr = finish_grow(new_layout, self.current_memory(elem_layout), &mut self.alloc)?; - // SAFETY: finish_grow would have resulted in a capacity overflow if we tried to allocate more than `isize::MAX` items + // SAFETY: layout_array would have resulted in a capacity overflow if we tried to allocate more than `isize::MAX` items unsafe { self.set_ptr_and_cap(ptr, cap); } @@ -771,8 +767,6 @@ fn finish_grow<A>( where A: Allocator, { - alloc_guard(new_layout.size())?; - let memory = if let Some((ptr, old_layout)) = current_memory { debug_assert_eq!(old_layout.align(), new_layout.align()); unsafe { @@ -799,23 +793,6 @@ fn handle_error(e: TryReserveError) -> ! { } } -// We need to guarantee the following: -// * We don't ever allocate `> isize::MAX` byte-size objects. -// * We don't overflow `usize::MAX` and actually allocate too little. -// -// On 64-bit we just need to check for overflow since trying to allocate -// `> isize::MAX` bytes will surely fail. On 32-bit and 16-bit we need to add -// an extra guard for this in case we're running on a platform which can use -// all 4GB in user-space, e.g., PAE or x32. -#[inline] -fn alloc_guard(alloc_size: usize) -> Result<(), TryReserveError> { - if usize::BITS < 64 && alloc_size > isize::MAX as usize { - Err(CapacityOverflow.into()) - } else { - Ok(()) - } -} - #[inline] fn layout_array(cap: usize, elem_layout: Layout) -> Result<Layout, TryReserveError> { elem_layout.repeat(cap).map(|(layout, _pad)| layout).map_err(|_| CapacityOverflow.into()) diff --git a/library/alloc/src/str.rs b/library/alloc/src/str.rs index 22cdd8ecde0..e772ac25a95 100644 --- a/library/alloc/src/str.rs +++ b/library/alloc/src/str.rs @@ -418,9 +418,8 @@ impl str { } fn case_ignorable_then_cased<I: Iterator<Item = char>>(iter: I) -> bool { - use core::unicode::{Case_Ignorable, Cased}; - match iter.skip_while(|&c| Case_Ignorable(c)).next() { - Some(c) => Cased(c), + match iter.skip_while(|&c| c.is_case_ignorable()).next() { + Some(c) => c.is_cased(), None => false, } } diff --git a/library/alloctests/lib.rs b/library/alloctests/lib.rs index 97de7d6a701..0201c875221 100644 --- a/library/alloctests/lib.rs +++ b/library/alloctests/lib.rs @@ -49,6 +49,7 @@ // Language features: // tidy-alphabetical-start #![feature(cfg_sanitize)] +#![feature(const_trait_impl)] #![feature(dropck_eyepatch)] #![feature(lang_items)] #![feature(min_specialization)] diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index c4bb5ab7b21..d14419a23a1 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -48,6 +48,7 @@ pub use iter::IntoIter; /// assert_eq!(strings, ["Hello there!", "Hello there!"]); /// ``` #[inline] +#[must_use = "cloning is often expensive and is not expected to have side effects"] #[stable(feature = "array_repeat", since = "CURRENT_RUSTC_VERSION")] pub fn repeat<T: Clone, const N: usize>(val: T) -> [T; N] { from_trusted_iterator(repeat_n(val, N)) @@ -190,7 +191,7 @@ impl fmt::Display for TryFromSliceError { impl Error for TryFromSliceError {} #[stable(feature = "try_from_slice_error", since = "1.36.0")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const From<Infallible> for TryFromSliceError { fn from(x: Infallible) -> TryFromSliceError { match x {} @@ -198,7 +199,8 @@ impl const From<Infallible> for TryFromSliceError { } #[stable(feature = "rust1", since = "1.0.0")] -impl<T, const N: usize> AsRef<[T]> for [T; N] { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl<T, const N: usize> const AsRef<[T]> for [T; N] { #[inline] fn as_ref(&self) -> &[T] { &self[..] @@ -206,7 +208,8 @@ impl<T, const N: usize> AsRef<[T]> for [T; N] { } #[stable(feature = "rust1", since = "1.0.0")] -impl<T, const N: usize> AsMut<[T]> for [T; N] { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl<T, const N: usize> const AsMut<[T]> for [T; N] { #[inline] fn as_mut(&mut self) -> &mut [T] { &mut self[..] @@ -214,14 +217,16 @@ impl<T, const N: usize> AsMut<[T]> for [T; N] { } #[stable(feature = "array_borrow", since = "1.4.0")] -impl<T, const N: usize> Borrow<[T]> for [T; N] { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl<T, const N: usize> const Borrow<[T]> for [T; N] { fn borrow(&self) -> &[T] { self } } #[stable(feature = "array_borrow", since = "1.4.0")] -impl<T, const N: usize> BorrowMut<[T]> for [T; N] { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl<T, const N: usize> const BorrowMut<[T]> for [T; N] { fn borrow_mut(&mut self) -> &mut [T] { self } @@ -240,7 +245,8 @@ impl<T, const N: usize> BorrowMut<[T]> for [T; N] { /// assert_eq!(512, u16::from_le_bytes(bytes_tail)); /// ``` #[stable(feature = "try_from", since = "1.34.0")] -impl<T, const N: usize> TryFrom<&[T]> for [T; N] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl<T, const N: usize> const TryFrom<&[T]> for [T; N] where T: Copy, { @@ -265,7 +271,8 @@ where /// assert_eq!(512, u16::from_le_bytes(bytes_tail)); /// ``` #[stable(feature = "try_from_mut_slice_to_array", since = "1.59.0")] -impl<T, const N: usize> TryFrom<&mut [T]> for [T; N] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl<T, const N: usize> const TryFrom<&mut [T]> for [T; N] where T: Copy, { @@ -290,7 +297,8 @@ where /// assert_eq!(512, u16::from_le_bytes(*bytes_tail)); /// ``` #[stable(feature = "try_from", since = "1.34.0")] -impl<'a, T, const N: usize> TryFrom<&'a [T]> for &'a [T; N] { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl<'a, T, const N: usize> const TryFrom<&'a [T]> for &'a [T; N] { type Error = TryFromSliceError; #[inline] @@ -312,7 +320,8 @@ impl<'a, T, const N: usize> TryFrom<&'a [T]> for &'a [T; N] { /// assert_eq!(512, u16::from_le_bytes(*bytes_tail)); /// ``` #[stable(feature = "try_from", since = "1.34.0")] -impl<'a, T, const N: usize> TryFrom<&'a mut [T]> for &'a mut [T; N] { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl<'a, T, const N: usize> const TryFrom<&'a mut [T]> for &'a mut [T; N] { type Error = TryFromSliceError; #[inline] diff --git a/library/core/src/ascii/ascii_char.rs b/library/core/src/ascii/ascii_char.rs index 419e4694594..49d540314a2 100644 --- a/library/core/src/ascii/ascii_char.rs +++ b/library/core/src/ascii/ascii_char.rs @@ -1156,7 +1156,7 @@ macro_rules! into_int_impl { ($($ty:ty)*) => { $( #[unstable(feature = "ascii_char", issue = "110998")] - #[rustc_const_unstable(feature = "const_try", issue = "74935")] + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const From<AsciiChar> for $ty { #[inline] fn from(chr: AsciiChar) -> $ty { diff --git a/library/core/src/borrow.rs b/library/core/src/borrow.rs index da05f236d2f..78ba69fec14 100644 --- a/library/core/src/borrow.rs +++ b/library/core/src/borrow.rs @@ -154,7 +154,8 @@ /// [`String`]: ../../std/string/struct.String.html #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "Borrow"] -pub trait Borrow<Borrowed: ?Sized> { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +pub const trait Borrow<Borrowed: ?Sized> { /// Immutably borrows from an owned value. /// /// # Examples @@ -185,7 +186,8 @@ pub trait Borrow<Borrowed: ?Sized> { /// for more information on borrowing as another type. #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "BorrowMut"] -pub trait BorrowMut<Borrowed: ?Sized>: Borrow<Borrowed> { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +pub const trait BorrowMut<Borrowed: ?Sized>: Borrow<Borrowed> { /// Mutably borrows from an owned value. /// /// # Examples @@ -206,7 +208,8 @@ pub trait BorrowMut<Borrowed: ?Sized>: Borrow<Borrowed> { } #[stable(feature = "rust1", since = "1.0.0")] -impl<T: ?Sized> Borrow<T> for T { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl<T: ?Sized> const Borrow<T> for T { #[rustc_diagnostic_item = "noop_method_borrow"] fn borrow(&self) -> &T { self @@ -214,28 +217,32 @@ impl<T: ?Sized> Borrow<T> for T { } #[stable(feature = "rust1", since = "1.0.0")] -impl<T: ?Sized> BorrowMut<T> for T { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl<T: ?Sized> const BorrowMut<T> for T { fn borrow_mut(&mut self) -> &mut T { self } } #[stable(feature = "rust1", since = "1.0.0")] -impl<T: ?Sized> Borrow<T> for &T { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl<T: ?Sized> const Borrow<T> for &T { fn borrow(&self) -> &T { self } } #[stable(feature = "rust1", since = "1.0.0")] -impl<T: ?Sized> Borrow<T> for &mut T { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl<T: ?Sized> const Borrow<T> for &mut T { fn borrow(&self) -> &T { self } } #[stable(feature = "rust1", since = "1.0.0")] -impl<T: ?Sized> BorrowMut<T> for &mut T { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl<T: ?Sized> const BorrowMut<T> for &mut T { fn borrow_mut(&mut self) -> &mut T { self } diff --git a/library/core/src/bstr/mod.rs b/library/core/src/bstr/mod.rs index 13127d645a2..e13dc5cd44d 100644 --- a/library/core/src/bstr/mod.rs +++ b/library/core/src/bstr/mod.rs @@ -63,14 +63,16 @@ impl ByteStr { /// ``` #[inline] #[unstable(feature = "bstr", issue = "134915")] - pub fn new<B: ?Sized + AsRef<[u8]>>(bytes: &B) -> &Self { + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] + pub const fn new<B: ?Sized + [const] AsRef<[u8]>>(bytes: &B) -> &Self { ByteStr::from_bytes(bytes.as_ref()) } #[doc(hidden)] #[unstable(feature = "bstr_internals", issue = "none")] #[inline] - pub fn from_bytes(slice: &[u8]) -> &Self { + #[rustc_const_unstable(feature = "bstr_internals", issue = "none")] + pub const fn from_bytes(slice: &[u8]) -> &Self { // SAFETY: `ByteStr` is a transparent wrapper around `[u8]`, so we can turn a reference to // the wrapped type into a reference to the wrapper type. unsafe { &*(slice as *const [u8] as *const Self) } @@ -79,7 +81,8 @@ impl ByteStr { #[doc(hidden)] #[unstable(feature = "bstr_internals", issue = "none")] #[inline] - pub fn from_bytes_mut(slice: &mut [u8]) -> &mut Self { + #[rustc_const_unstable(feature = "bstr_internals", issue = "none")] + pub const fn from_bytes_mut(slice: &mut [u8]) -> &mut Self { // SAFETY: `ByteStr` is a transparent wrapper around `[u8]`, so we can turn a reference to // the wrapped type into a reference to the wrapper type. unsafe { &mut *(slice as *mut [u8] as *mut Self) } @@ -88,20 +91,23 @@ impl ByteStr { #[doc(hidden)] #[unstable(feature = "bstr_internals", issue = "none")] #[inline] - pub fn as_bytes(&self) -> &[u8] { + #[rustc_const_unstable(feature = "bstr_internals", issue = "none")] + pub const fn as_bytes(&self) -> &[u8] { &self.0 } #[doc(hidden)] #[unstable(feature = "bstr_internals", issue = "none")] #[inline] - pub fn as_bytes_mut(&mut self) -> &mut [u8] { + #[rustc_const_unstable(feature = "bstr_internals", issue = "none")] + pub const fn as_bytes_mut(&mut self) -> &mut [u8] { &mut self.0 } } #[unstable(feature = "bstr", issue = "134915")] -impl Deref for ByteStr { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl const Deref for ByteStr { type Target = [u8]; #[inline] @@ -111,7 +117,8 @@ impl Deref for ByteStr { } #[unstable(feature = "bstr", issue = "134915")] -impl DerefMut for ByteStr { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl const DerefMut for ByteStr { #[inline] fn deref_mut(&mut self) -> &mut [u8] { &mut self.0 @@ -185,7 +192,8 @@ impl fmt::Display for ByteStr { } #[unstable(feature = "bstr", issue = "134915")] -impl AsRef<[u8]> for ByteStr { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl const AsRef<[u8]> for ByteStr { #[inline] fn as_ref(&self) -> &[u8] { &self.0 @@ -193,7 +201,8 @@ impl AsRef<[u8]> for ByteStr { } #[unstable(feature = "bstr", issue = "134915")] -impl AsRef<ByteStr> for ByteStr { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl const AsRef<ByteStr> for ByteStr { #[inline] fn as_ref(&self) -> &ByteStr { self @@ -203,7 +212,8 @@ impl AsRef<ByteStr> for ByteStr { // `impl AsRef<ByteStr> for [u8]` omitted to avoid widespread inference failures #[unstable(feature = "bstr", issue = "134915")] -impl AsRef<ByteStr> for str { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl const AsRef<ByteStr> for str { #[inline] fn as_ref(&self) -> &ByteStr { ByteStr::new(self) @@ -211,7 +221,8 @@ impl AsRef<ByteStr> for str { } #[unstable(feature = "bstr", issue = "134915")] -impl AsMut<[u8]> for ByteStr { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl const AsMut<[u8]> for ByteStr { #[inline] fn as_mut(&mut self) -> &mut [u8] { &mut self.0 @@ -225,7 +236,8 @@ impl AsMut<[u8]> for ByteStr { // `impl Borrow<ByteStr> for str` omitted to avoid widespread inference failures #[unstable(feature = "bstr", issue = "134915")] -impl Borrow<[u8]> for ByteStr { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl const Borrow<[u8]> for ByteStr { #[inline] fn borrow(&self) -> &[u8] { &self.0 @@ -235,7 +247,8 @@ impl Borrow<[u8]> for ByteStr { // `impl BorrowMut<ByteStr> for [u8]` omitted to avoid widespread inference failures #[unstable(feature = "bstr", issue = "134915")] -impl BorrowMut<[u8]> for ByteStr { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl const BorrowMut<[u8]> for ByteStr { #[inline] fn borrow_mut(&mut self) -> &mut [u8] { &mut self.0 @@ -303,7 +316,8 @@ impl<'a> Default for &'a mut ByteStr { // } #[unstable(feature = "bstr", issue = "134915")] -impl<'a> TryFrom<&'a ByteStr> for &'a str { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl<'a> const TryFrom<&'a ByteStr> for &'a str { type Error = crate::str::Utf8Error; #[inline] @@ -313,7 +327,8 @@ impl<'a> TryFrom<&'a ByteStr> for &'a str { } #[unstable(feature = "bstr", issue = "134915")] -impl<'a> TryFrom<&'a mut ByteStr> for &'a mut str { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl<'a> const TryFrom<&'a mut ByteStr> for &'a mut str { type Error = crate::str::Utf8Error; #[inline] diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 59a6aa70620..9b53b75ebee 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -390,7 +390,8 @@ impl<T: Ord + Copy> Ord for Cell<T> { } #[stable(feature = "cell_from", since = "1.12.0")] -impl<T> From<T> for Cell<T> { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl<T> const From<T> for Cell<T> { /// Creates a new `Cell<T>` containing the given value. fn from(t: T) -> Cell<T> { Cell::new(t) @@ -1402,7 +1403,8 @@ impl<T: ?Sized + Ord> Ord for RefCell<T> { } #[stable(feature = "cell_from", since = "1.12.0")] -impl<T> From<T> for RefCell<T> { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl<T> const From<T> for RefCell<T> { /// Creates a new `RefCell<T>` containing the given value. fn from(t: T) -> RefCell<T> { RefCell::new(t) @@ -1483,7 +1485,7 @@ pub struct Ref<'b, T: ?Sized + 'b> { } #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_deref", issue = "88955")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl<T: ?Sized> const Deref for Ref<'_, T> { type Target = T; @@ -1967,7 +1969,7 @@ pub struct RefMut<'b, T: ?Sized + 'b> { } #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_deref", issue = "88955")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl<T: ?Sized> const Deref for RefMut<'_, T> { type Target = T; @@ -1979,7 +1981,7 @@ impl<T: ?Sized> const Deref for RefMut<'_, T> { } #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_deref", issue = "88955")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl<T: ?Sized> const DerefMut for RefMut<'_, T> { #[inline] fn deref_mut(&mut self) -> &mut T { @@ -2434,7 +2436,8 @@ impl<T: [const] Default> const Default for UnsafeCell<T> { } #[stable(feature = "cell_from", since = "1.12.0")] -impl<T> From<T> for UnsafeCell<T> { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl<T> const From<T> for UnsafeCell<T> { /// Creates a new `UnsafeCell<T>` containing the given value. fn from(t: T) -> UnsafeCell<T> { UnsafeCell::new(t) @@ -2539,7 +2542,8 @@ impl<T: [const] Default> const Default for SyncUnsafeCell<T> { } #[unstable(feature = "sync_unsafe_cell", issue = "95439")] -impl<T> From<T> for SyncUnsafeCell<T> { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl<T> const From<T> for SyncUnsafeCell<T> { /// Creates a new `SyncUnsafeCell<T>` containing the given value. fn from(t: T) -> SyncUnsafeCell<T> { SyncUnsafeCell::new(t) diff --git a/library/core/src/cell/once.rs b/library/core/src/cell/once.rs index c6c96571d33..833be059d75 100644 --- a/library/core/src/cell/once.rs +++ b/library/core/src/cell/once.rs @@ -395,7 +395,8 @@ impl<T: PartialEq> PartialEq for OnceCell<T> { impl<T: Eq> Eq for OnceCell<T> {} #[stable(feature = "once_cell", since = "1.70.0")] -impl<T> From<T> for OnceCell<T> { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl<T> const From<T> for OnceCell<T> { /// Creates a new `OnceCell<T>` which already contains the given `value`. #[inline] fn from(value: T) -> Self { diff --git a/library/core/src/char/convert.rs b/library/core/src/char/convert.rs index cf5a91bf8dd..6380f42d320 100644 --- a/library/core/src/char/convert.rs +++ b/library/core/src/char/convert.rs @@ -36,7 +36,7 @@ pub(super) const unsafe fn from_u32_unchecked(i: u32) -> char { } #[stable(feature = "char_convert", since = "1.13.0")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const From<char> for u32 { /// Converts a [`char`] into a [`u32`]. /// @@ -54,7 +54,7 @@ impl const From<char> for u32 { } #[stable(feature = "more_char_conversions", since = "1.51.0")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const From<char> for u64 { /// Converts a [`char`] into a [`u64`]. /// @@ -74,7 +74,7 @@ impl const From<char> for u64 { } #[stable(feature = "more_char_conversions", since = "1.51.0")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const From<char> for u128 { /// Converts a [`char`] into a [`u128`]. /// @@ -98,7 +98,8 @@ impl const From<char> for u128 { /// /// See [`impl From<u8> for char`](char#impl-From<u8>-for-char) for details on the encoding. #[stable(feature = "u8_from_char", since = "1.59.0")] -impl TryFrom<char> for u8 { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl const TryFrom<char> for u8 { type Error = TryFromCharError; /// Tries to convert a [`char`] into a [`u8`]. @@ -113,7 +114,11 @@ impl TryFrom<char> for u8 { /// ``` #[inline] fn try_from(c: char) -> Result<u8, Self::Error> { - u8::try_from(u32::from(c)).map_err(|_| TryFromCharError(())) + // FIXME(const-hack): this should use map_err instead + match u8::try_from(u32::from(c)) { + Ok(b) => Ok(b), + Err(_) => Err(TryFromCharError(())), + } } } @@ -122,7 +127,8 @@ impl TryFrom<char> for u8 { /// /// This corresponds to the UCS-2 encoding, as specified in ISO/IEC 10646:2003. #[stable(feature = "u16_from_char", since = "1.74.0")] -impl TryFrom<char> for u16 { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl const TryFrom<char> for u16 { type Error = TryFromCharError; /// Tries to convert a [`char`] into a [`u16`]. @@ -137,7 +143,11 @@ impl TryFrom<char> for u16 { /// ``` #[inline] fn try_from(c: char) -> Result<u16, Self::Error> { - u16::try_from(u32::from(c)).map_err(|_| TryFromCharError(())) + // FIXME(const-hack): this should use map_err instead + match u16::try_from(u32::from(c)) { + Ok(x) => Ok(x), + Err(_) => Err(TryFromCharError(())), + } } } @@ -160,7 +170,7 @@ impl TryFrom<char> for u16 { /// for a superset of Windows-1252 that fills the remaining blanks with corresponding /// C0 and C1 control codes. #[stable(feature = "char_convert", since = "1.13.0")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const From<u8> for char { /// Converts a [`u8`] into a [`char`]. /// @@ -246,7 +256,7 @@ const fn char_try_from_u32(i: u32) -> Result<char, CharTryFromError> { } #[stable(feature = "try_from", since = "1.34.0")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const TryFrom<u32> for char { type Error = CharTryFromError; diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index 3336d028e27..76f54db2870 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -969,7 +969,43 @@ impl char { #[must_use] #[inline] pub(crate) fn is_grapheme_extended(self) -> bool { - unicode::Grapheme_Extend(self) + !self.is_ascii() && unicode::Grapheme_Extend(self) + } + + /// Returns `true` if this `char` has the `Cased` property. + /// + /// `Cased` is described in Chapter 4 (Character Properties) of the [Unicode Standard] and + /// specified in the [Unicode Character Database][ucd] [`DerivedCoreProperties.txt`]. + /// + /// [Unicode Standard]: https://www.unicode.org/versions/latest/ + /// [ucd]: https://www.unicode.org/reports/tr44/ + /// [`DerivedCoreProperties.txt`]: https://www.unicode.org/Public/UCD/latest/ucd/DerivedCoreProperties.txt + #[must_use] + #[inline] + #[doc(hidden)] + #[unstable(feature = "char_internals", reason = "exposed only for libstd", issue = "none")] + pub fn is_cased(self) -> bool { + if self.is_ascii() { self.is_ascii_alphabetic() } else { unicode::Cased(self) } + } + + /// Returns `true` if this `char` has the `Case_Ignorable` property. + /// + /// `Case_Ignorable` is described in Chapter 4 (Character Properties) of the [Unicode Standard] and + /// specified in the [Unicode Character Database][ucd] [`DerivedCoreProperties.txt`]. + /// + /// [Unicode Standard]: https://www.unicode.org/versions/latest/ + /// [ucd]: https://www.unicode.org/reports/tr44/ + /// [`DerivedCoreProperties.txt`]: https://www.unicode.org/Public/UCD/latest/ucd/DerivedCoreProperties.txt + #[must_use] + #[inline] + #[doc(hidden)] + #[unstable(feature = "char_internals", reason = "exposed only for libstd", issue = "none")] + pub fn is_case_ignorable(self) -> bool { + if self.is_ascii() { + matches!(self, '\'' | '.' | ':' | '^' | '`') + } else { + unicode::Case_Ignorable(self) + } } /// Returns `true` if this `char` has one of the general categories for numbers. diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index 0add77b2bc8..7f2a40f753f 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -191,8 +191,7 @@ mod uninit; #[rustc_diagnostic_item = "Clone"] #[rustc_trivial_field_reads] #[rustc_const_unstable(feature = "const_clone", issue = "142757")] -#[const_trait] -pub trait Clone: Sized { +pub const trait Clone: Sized { /// Returns a duplicate of the value. /// /// Note that what "duplicate" means varies by type: diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index ab018fa2675..0812322f3fb 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -247,9 +247,8 @@ use crate::ops::ControlFlow; append_const_msg )] #[rustc_diagnostic_item = "PartialEq"] -#[const_trait] #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] -pub trait PartialEq<Rhs: PointeeSized = Self>: PointeeSized { +pub const trait PartialEq<Rhs: PointeeSized = Self>: PointeeSized { /// Tests for `self` and `other` values to be equal, and is used by `==`. #[must_use] #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/core/src/cmp/bytewise.rs b/library/core/src/cmp/bytewise.rs index a06a6e8b69a..2265fa7a353 100644 --- a/library/core/src/cmp/bytewise.rs +++ b/library/core/src/cmp/bytewise.rs @@ -17,7 +17,7 @@ use crate::num::NonZero; /// - Neither `Self` nor `Rhs` have provenance, so integer comparisons are correct. /// - `<Self as PartialEq<Rhs>>::{eq,ne}` are equivalent to comparing the bytes. #[rustc_specialization_trait] -#[const_trait] +#[const_trait] // FIXME(const_trait_impl): Migrate to `const unsafe trait` once #146122 is fixed. pub(crate) unsafe trait BytewiseEq<Rhs = Self>: [const] PartialEq<Rhs> + Sized { diff --git a/library/core/src/convert/mod.rs b/library/core/src/convert/mod.rs index a0a7b7928d1..a3cfd85974a 100644 --- a/library/core/src/convert/mod.rs +++ b/library/core/src/convert/mod.rs @@ -216,9 +216,8 @@ pub const fn identity<T>(x: T) -> T { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "AsRef"] -#[const_trait] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] -pub trait AsRef<T: PointeeSized>: PointeeSized { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +pub const trait AsRef<T: PointeeSized>: PointeeSized { /// Converts this type into a shared reference of the (usually inferred) input type. #[stable(feature = "rust1", since = "1.0.0")] fn as_ref(&self) -> &T; @@ -369,9 +368,8 @@ pub trait AsRef<T: PointeeSized>: PointeeSized { /// `&mut Vec<u8>`, for example, is the better choice (callers need to pass the correct type then). #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "AsMut"] -#[const_trait] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] -pub trait AsMut<T: PointeeSized>: PointeeSized { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +pub const trait AsMut<T: PointeeSized>: PointeeSized { /// Converts this type into a mutable reference of the (usually inferred) input type. #[stable(feature = "rust1", since = "1.0.0")] fn as_mut(&mut self) -> &mut T; @@ -449,9 +447,8 @@ pub trait AsMut<T: PointeeSized>: PointeeSized { #[rustc_diagnostic_item = "Into"] #[stable(feature = "rust1", since = "1.0.0")] #[doc(search_unbox)] -#[rustc_const_unstable(feature = "const_from", issue = "143773")] -#[const_trait] -pub trait Into<T>: Sized { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +pub const trait Into<T>: Sized { /// Converts this type into the (usually inferred) input type. #[must_use] #[stable(feature = "rust1", since = "1.0.0")] @@ -586,9 +583,8 @@ pub trait Into<T>: Sized { note = "to coerce a `{T}` into a `{Self}`, use `&*` as a prefix", ))] #[doc(search_unbox)] -#[rustc_const_unstable(feature = "const_from", issue = "143773")] -#[const_trait] -pub trait From<T>: Sized { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +pub const trait From<T>: Sized { /// Converts to this type from the input type. #[rustc_diagnostic_item = "from_fn"] #[must_use] @@ -615,9 +611,8 @@ pub trait From<T>: Sized { /// [`Into`], see there for details. #[rustc_diagnostic_item = "TryInto"] #[stable(feature = "try_from", since = "1.34.0")] -#[rustc_const_unstable(feature = "const_from", issue = "143773")] -#[const_trait] -pub trait TryInto<T>: Sized { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +pub const trait TryInto<T>: Sized { /// The type returned in the event of a conversion error. #[stable(feature = "try_from", since = "1.34.0")] type Error; @@ -695,9 +690,8 @@ pub trait TryInto<T>: Sized { /// [`try_from`]: TryFrom::try_from #[rustc_diagnostic_item = "TryFrom"] #[stable(feature = "try_from", since = "1.34.0")] -#[rustc_const_unstable(feature = "const_from", issue = "143773")] -#[const_trait] -pub trait TryFrom<T>: Sized { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +pub const trait TryFrom<T>: Sized { /// The type returned in the event of a conversion error. #[stable(feature = "try_from", since = "1.34.0")] type Error; @@ -714,7 +708,7 @@ pub trait TryFrom<T>: Sized { // As lifts over & #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl<T: PointeeSized, U: PointeeSized> const AsRef<U> for &T where T: [const] AsRef<U>, @@ -727,7 +721,7 @@ where // As lifts over &mut #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl<T: PointeeSized, U: PointeeSized> const AsRef<U> for &mut T where T: [const] AsRef<U>, @@ -748,7 +742,7 @@ where // AsMut lifts over &mut #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl<T: PointeeSized, U: PointeeSized> const AsMut<U> for &mut T where T: [const] AsMut<U>, @@ -769,7 +763,7 @@ where // From implies Into #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_from", issue = "143773")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl<T, U> const Into<U> for T where U: [const] From<T>, @@ -787,7 +781,7 @@ where // From (and thus Into) is reflexive #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_from", issue = "143773")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl<T> const From<T> for T { /// Returns the argument unchanged. #[inline(always)] @@ -804,7 +798,7 @@ impl<T> const From<T> for T { #[stable(feature = "convert_infallible", since = "1.34.0")] #[rustc_reservation_impl = "permitting this impl would forbid us from adding \ `impl<T> From<!> for T` later; see rust-lang/rust#64715 for details"] -#[rustc_const_unstable(feature = "const_from", issue = "143773")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl<T> const From<!> for T { fn from(t: !) -> T { t @@ -813,7 +807,7 @@ impl<T> const From<!> for T { // TryFrom implies TryInto #[stable(feature = "try_from", since = "1.34.0")] -#[rustc_const_unstable(feature = "const_from", issue = "143773")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl<T, U> const TryInto<U> for T where U: [const] TryFrom<T>, @@ -829,7 +823,7 @@ where // Infallible conversions are semantically equivalent to fallible conversions // with an uninhabited error type. #[stable(feature = "try_from", since = "1.34.0")] -#[rustc_const_unstable(feature = "const_from", issue = "143773")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl<T, U> const TryFrom<U> for T where U: [const] Into<T>, @@ -847,7 +841,7 @@ where //////////////////////////////////////////////////////////////////////////////// #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl<T> const AsRef<[T]> for [T] { #[inline(always)] fn as_ref(&self) -> &[T] { @@ -856,7 +850,7 @@ impl<T> const AsRef<[T]> for [T] { } #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl<T> const AsMut<[T]> for [T] { #[inline(always)] fn as_mut(&mut self) -> &mut [T] { @@ -865,7 +859,7 @@ impl<T> const AsMut<[T]> for [T] { } #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const AsRef<str> for str { #[inline(always)] fn as_ref(&self) -> &str { @@ -874,7 +868,7 @@ impl const AsRef<str> for str { } #[stable(feature = "as_mut_str_for_str", since = "1.51.0")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const AsMut<str> for str { #[inline(always)] fn as_mut(&mut self) -> &mut str { @@ -936,7 +930,7 @@ impl const AsMut<str> for str { pub enum Infallible {} #[stable(feature = "convert_infallible", since = "1.34.0")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_clone", issue = "142757")] impl const Clone for Infallible { fn clone(&self) -> Infallible { match *self {} @@ -986,7 +980,7 @@ impl Ord for Infallible { } #[stable(feature = "convert_infallible", since = "1.34.0")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const From<!> for Infallible { #[inline] fn from(x: !) -> Self { diff --git a/library/core/src/convert/num.rs b/library/core/src/convert/num.rs index affb4eb64d3..6ae588a4e04 100644 --- a/library/core/src/convert/num.rs +++ b/library/core/src/convert/num.rs @@ -69,7 +69,7 @@ macro_rules! impl_from { }; ($Small:ty => $Large:ty, #[$attr:meta], $doc:expr $(,)?) => { #[$attr] - #[rustc_const_unstable(feature = "const_try", issue = "74935")] + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const From<$Small> for $Large { // Rustdocs on the impl block show a "[+] show undocumented items" toggle. // Rustdocs on functions do not. @@ -201,7 +201,7 @@ macro_rules! impl_float_from_bool { )? ) => { #[stable(feature = "float_from_bool", since = "1.68.0")] - #[rustc_const_unstable(feature = "const_try", issue = "74935")] + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const From<bool> for $float { #[doc = concat!("Converts a [`bool`] to [`", stringify!($float),"`] losslessly.")] /// The resulting value is positive `0.0` for `false` and `1.0` for `true` values. @@ -252,7 +252,7 @@ impl_float_from_bool!( macro_rules! impl_try_from_unbounded { ($source:ty => $($target:ty),+) => {$( #[stable(feature = "try_from", since = "1.34.0")] - #[rustc_const_unstable(feature = "const_try", issue = "74935")] + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const TryFrom<$source> for $target { type Error = TryFromIntError; @@ -271,7 +271,7 @@ macro_rules! impl_try_from_unbounded { macro_rules! impl_try_from_lower_bounded { ($source:ty => $($target:ty),+) => {$( #[stable(feature = "try_from", since = "1.34.0")] - #[rustc_const_unstable(feature = "const_try", issue = "74935")] + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const TryFrom<$source> for $target { type Error = TryFromIntError; @@ -294,7 +294,7 @@ macro_rules! impl_try_from_lower_bounded { macro_rules! impl_try_from_upper_bounded { ($source:ty => $($target:ty),+) => {$( #[stable(feature = "try_from", since = "1.34.0")] - #[rustc_const_unstable(feature = "const_try", issue = "74935")] + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const TryFrom<$source> for $target { type Error = TryFromIntError; @@ -317,7 +317,7 @@ macro_rules! impl_try_from_upper_bounded { macro_rules! impl_try_from_both_bounded { ($source:ty => $($target:ty),+) => {$( #[stable(feature = "try_from", since = "1.34.0")] - #[rustc_const_unstable(feature = "const_try", issue = "74935")] + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const TryFrom<$source> for $target { type Error = TryFromIntError; @@ -456,7 +456,7 @@ use crate::num::NonZero; macro_rules! impl_nonzero_int_from_nonzero_int { ($Small:ty => $Large:ty) => { #[stable(feature = "nz_int_conv", since = "1.41.0")] - #[rustc_const_unstable(feature = "const_try", issue = "74935")] + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const From<NonZero<$Small>> for NonZero<$Large> { // Rustdocs on the impl block show a "[+] show undocumented items" toggle. // Rustdocs on functions do not. @@ -515,7 +515,8 @@ impl_nonzero_int_from_nonzero_int!(u64 => i128); macro_rules! impl_nonzero_int_try_from_int { ($Int:ty) => { #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] - impl TryFrom<$Int> for NonZero<$Int> { + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] + impl const TryFrom<$Int> for NonZero<$Int> { type Error = TryFromIntError; // Rustdocs on the impl block show a "[+] show undocumented items" toggle. @@ -547,7 +548,7 @@ impl_nonzero_int_try_from_int!(isize); macro_rules! impl_nonzero_int_try_from_nonzero_int { ($source:ty => $($target:ty),+) => {$( #[stable(feature = "nzint_try_from_nzint_conv", since = "1.49.0")] - #[rustc_const_unstable(feature = "const_try", issue = "74935")] + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const TryFrom<NonZero<$source>> for NonZero<$target> { type Error = TryFromIntError; diff --git a/library/core/src/default.rs b/library/core/src/default.rs index 897267968aa..cc9289307c1 100644 --- a/library/core/src/default.rs +++ b/library/core/src/default.rs @@ -103,9 +103,8 @@ use crate::ascii::Char as AsciiChar; /// ``` #[rustc_diagnostic_item = "Default"] #[stable(feature = "rust1", since = "1.0.0")] -#[const_trait] #[rustc_const_unstable(feature = "const_default", issue = "143894")] -pub trait Default: Sized { +pub const trait Default: Sized { /// Returns the "default value" for a type. /// /// Default values are often some kind of initial value, identity value, or anything else that diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index e6b599fafcf..d0b53e3a237 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -708,7 +708,8 @@ impl ops::Index<ops::RangeFrom<usize>> for CStr { } #[stable(feature = "cstring_asref", since = "1.7.0")] -impl AsRef<CStr> for CStr { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl const AsRef<CStr> for CStr { #[inline] fn as_ref(&self) -> &CStr { self diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs index 605ba42c541..253a7b7587e 100644 --- a/library/core/src/fmt/num.rs +++ b/library/core/src/fmt/num.rs @@ -3,7 +3,7 @@ use crate::fmt::NumBuffer; use crate::mem::MaybeUninit; use crate::num::fmt as numfmt; -use crate::{fmt, ptr, slice, str}; +use crate::{fmt, str}; /// Formatting of integers with a non-decimal radix. macro_rules! radix_integer { @@ -96,8 +96,8 @@ macro_rules! impl_Debug { }; } -// 2 digit decimal look up table -static DEC_DIGITS_LUT: &[u8; 200] = b"\ +// The string of all two-digit numbers in range 00..99 is used as a lookup table. +static DECIMAL_PAIRS: &[u8; 200] = b"\ 0001020304050607080910111213141516171819\ 2021222324252627282930313233343536373839\ 4041424344454647484950515253545556575859\ @@ -123,6 +123,9 @@ macro_rules! impl_Display { $( const _: () = { + assert!($Signed::MIN < 0, "need signed"); + assert!($Unsigned::MIN == 0, "need unsigned"); + assert!($Signed::BITS == $Unsigned::BITS, "need counterparts"); assert!($Signed::BITS <= $T::BITS, "need lossless conversion"); assert!($Unsigned::BITS <= $T::BITS, "need lossless conversion"); }; @@ -207,10 +210,10 @@ macro_rules! impl_Display { remain /= scale; let pair1 = (quad / 100) as usize; let pair2 = (quad % 100) as usize; - buf[offset + 0].write(DEC_DIGITS_LUT[pair1 * 2 + 0]); - buf[offset + 1].write(DEC_DIGITS_LUT[pair1 * 2 + 1]); - buf[offset + 2].write(DEC_DIGITS_LUT[pair2 * 2 + 0]); - buf[offset + 3].write(DEC_DIGITS_LUT[pair2 * 2 + 1]); + buf[offset + 0].write(DECIMAL_PAIRS[pair1 * 2 + 0]); + buf[offset + 1].write(DECIMAL_PAIRS[pair1 * 2 + 1]); + buf[offset + 2].write(DECIMAL_PAIRS[pair2 * 2 + 0]); + buf[offset + 3].write(DECIMAL_PAIRS[pair2 * 2 + 1]); } // Format per two digits from the lookup table. @@ -225,8 +228,8 @@ macro_rules! impl_Display { let pair = (remain % 100) as usize; remain /= 100; - buf[offset + 0].write(DEC_DIGITS_LUT[pair * 2 + 0]); - buf[offset + 1].write(DEC_DIGITS_LUT[pair * 2 + 1]); + buf[offset + 0].write(DECIMAL_PAIRS[pair * 2 + 0]); + buf[offset + 1].write(DECIMAL_PAIRS[pair * 2 + 1]); } // Format the last remaining digit, if any. @@ -242,7 +245,7 @@ macro_rules! impl_Display { // Either the compiler sees that remain < 10, or it prevents // a boundary check up next. let last = (remain & 15) as usize; - buf[offset].write(DEC_DIGITS_LUT[last * 2 + 1]); + buf[offset].write(DECIMAL_PAIRS[last * 2 + 1]); // not used: remain = 0; } @@ -335,7 +338,6 @@ macro_rules! impl_Display { } } - )* #[cfg(feature = "optimize_for_size")] @@ -374,178 +376,213 @@ macro_rules! impl_Display { macro_rules! impl_Exp { ($($Signed:ident, $Unsigned:ident),* ; as $T:ident into $fmt_fn:ident) => { + const _: () = assert!($T::MIN == 0, "need unsigned"); + fn $fmt_fn( - mut n: $T, + f: &mut fmt::Formatter<'_>, + n: $T, is_nonnegative: bool, - upper: bool, - f: &mut fmt::Formatter<'_> + letter_e: u8 ) -> fmt::Result { - let (mut n, mut exponent, trailing_zeros, added_precision) = { - let mut exponent = 0; - // count and remove trailing decimal zeroes - while n % 10 == 0 && n >= 10 { - n /= 10; - exponent += 1; - } - let (added_precision, subtracted_precision) = match f.precision() { - Some(fmt_prec) => { - // number of decimal digits minus 1 - let mut tmp = n; - let mut prec = 0; - while tmp >= 10 { - tmp /= 10; - prec += 1; - } - (fmt_prec.saturating_sub(prec), prec.saturating_sub(fmt_prec)) + debug_assert!(letter_e.is_ascii_alphabetic(), "single-byte character"); + + // Print the integer as a coefficient in range (-10, 10). + let mut exp = n.checked_ilog10().unwrap_or(0) as usize; + debug_assert!(n / (10 as $T).pow(exp as u32) < 10); + + // Precisison is counted as the number of digits in the fraction. + let mut coef_prec = exp; + // Keep the digits as an integer (paired with its coef_prec count). + let mut coef = n; + + // A Formatter may set the precision to a fixed number of decimals. + let more_prec = match f.precision() { + None => { + // Omit any and all trailing zeroes. + while coef_prec != 0 && coef % 10 == 0 { + coef /= 10; + coef_prec -= 1; } - None => (0, 0) - }; - for _ in 1..subtracted_precision { - n /= 10; - exponent += 1; - } - if subtracted_precision != 0 { - let rem = n % 10; - n /= 10; - exponent += 1; - // round up last digit, round to even on a tie - if rem > 5 || (rem == 5 && (n % 2 != 0 || subtracted_precision > 1 )) { - n += 1; - // if the digit is rounded to the next power - // instead adjust the exponent - if n.ilog10() > (n - 1).ilog10() { - n /= 10; - exponent += 1; - } + 0 + }, + + Some(fmt_prec) if fmt_prec >= coef_prec => { + // Count the number of additional zeroes needed. + fmt_prec - coef_prec + }, + + Some(fmt_prec) => { + // Count the number of digits to drop. + let less_prec = coef_prec - fmt_prec; + assert!(less_prec > 0); + // Scale down the coefficient/precision pair. For example, + // coef 123456 gets coef_prec 5 (to make 1.23456). To format + // the number with 2 decimals, i.e., fmt_prec 2, coef should + // be scaled by 10⁵⁻²=1000 to get coef 123 with coef_prec 2. + + // SAFETY: Any precision less than coef_prec will cause a + // power of ten below the coef value. + let scale = unsafe { + (10 as $T).checked_pow(less_prec as u32).unwrap_unchecked() + }; + let floor = coef / scale; + // Round half to even conform documentation. + let over = coef % scale; + let half = scale / 2; + let round_up = if over < half { + 0 + } else if over > half { + 1 + } else { + floor & 1 // round odd up to even + }; + // Adding one to a scale down of at least 10 won't overflow. + coef = floor + round_up; + coef_prec = fmt_prec; + + // The round_up may have caused the coefficient to reach 10 + // (which is not permitted). For example, anything in range + // [9.95, 10) becomes 10.0 when adjusted to precision 1. + if round_up != 0 && coef.checked_ilog10().unwrap_or(0) as usize > coef_prec { + debug_assert_eq!(coef, (10 as $T).pow(coef_prec as u32 + 1)); + coef /= 10; // drop one trailing zero + exp += 1; // one power of ten higher } - } - (n, exponent, exponent, added_precision) + 0 + }, }; - // Since `curr` always decreases by the number of digits copied, this means - // that `curr >= 0`. - let mut buf = [MaybeUninit::<u8>::uninit(); 40]; - let mut curr = buf.len(); //index for buf - let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf); - let lut_ptr = DEC_DIGITS_LUT.as_ptr(); - - // decode 2 chars at a time - while n >= 100 { - let d1 = ((n % 100) as usize) << 1; - curr -= 2; - // SAFETY: `d1 <= 198`, so we can copy from `lut_ptr[d1..d1 + 2]` since - // `DEC_DIGITS_LUT` has a length of 200. - unsafe { - ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr), 2); - } - n /= 100; - exponent += 2; - } - // n is <= 99, so at most 2 chars long - let mut n = n as isize; // possibly reduce 64bit math - // decode second-to-last character - if n >= 10 { - curr -= 1; - // SAFETY: Safe since `40 > curr >= 0` (see comment) - unsafe { - *buf_ptr.add(curr) = (n as u8 % 10_u8) + b'0'; + // Allocate a text buffer with lazy initialization. + const MAX_DEC_N: usize = $T::MAX.ilog10() as usize + 1; + const MAX_COEF_LEN: usize = MAX_DEC_N + ".".len(); + const MAX_TEXT_LEN: usize = MAX_COEF_LEN + "e99".len(); + let mut buf = [MaybeUninit::<u8>::uninit(); MAX_TEXT_LEN]; + + // Encode the coefficient in buf[..coef_len]. + let (lead_dec, coef_len) = if coef_prec == 0 && more_prec == 0 { + (coef, 1_usize) // single digit; no fraction + } else { + buf[1].write(b'.'); + let fraction_range = 2..(2 + coef_prec); + + // Consume the least-significant decimals from a working copy. + let mut remain = coef; + #[cfg(feature = "optimize_for_size")] { + for i in fraction_range.clone().rev() { + let digit = (remain % 10) as usize; + remain /= 10; + buf[i].write(b'0' + digit as u8); + } } - n /= 10; - exponent += 1; - } - // add decimal point iff >1 mantissa digit will be printed - if exponent != trailing_zeros || added_precision != 0 { - curr -= 1; - // SAFETY: Safe since `40 > curr >= 0` - unsafe { - *buf_ptr.add(curr) = b'.'; + #[cfg(not(feature = "optimize_for_size"))] { + // Write digits per two at a time with a lookup table. + for i in fraction_range.clone().skip(1).rev().step_by(2) { + let pair = (remain % 100) as usize; + remain /= 100; + buf[i - 1].write(DECIMAL_PAIRS[pair * 2 + 0]); + buf[i - 0].write(DECIMAL_PAIRS[pair * 2 + 1]); + } + // An odd number of digits leave one digit remaining. + if coef_prec & 1 != 0 { + let digit = (remain % 10) as usize; + remain /= 10; + buf[fraction_range.start].write(b'0' + digit as u8); + } } - } - // SAFETY: Safe since `40 > curr >= 0` - let buf_slice = unsafe { - // decode last character - curr -= 1; - *buf_ptr.add(curr) = (n as u8) + b'0'; - - let len = buf.len() - curr as usize; - slice::from_raw_parts(buf_ptr.add(curr), len) + (remain, fraction_range.end) }; - - // stores 'e' (or 'E') and the up to 2-digit exponent - let mut exp_buf = [MaybeUninit::<u8>::uninit(); 3]; - let exp_ptr = MaybeUninit::slice_as_mut_ptr(&mut exp_buf); - // SAFETY: In either case, `exp_buf` is written within bounds and `exp_ptr[..len]` - // is contained within `exp_buf` since `len <= 3`. - let exp_slice = unsafe { - *exp_ptr.add(0) = if upper { b'E' } else { b'e' }; - let len = if exponent < 10 { - *exp_ptr.add(1) = (exponent as u8) + b'0'; - 2 - } else { - let off = exponent << 1; - ptr::copy_nonoverlapping(lut_ptr.add(off), exp_ptr.add(1), 2); - 3 - }; - slice::from_raw_parts(exp_ptr, len) + debug_assert!(lead_dec < 10); + debug_assert!(lead_dec != 0 || coef == 0, "significant digits only"); + buf[0].write(b'0' + lead_dec as u8); + + // SAFETY: The number of decimals is limited, captured by MAX. + unsafe { core::hint::assert_unchecked(coef_len <= MAX_COEF_LEN) } + // Encode the scale factor in buf[coef_len..text_len]. + buf[coef_len].write(letter_e); + let text_len: usize = match exp { + ..10 => { + buf[coef_len + 1].write(b'0' + exp as u8); + coef_len + 2 + }, + 10..100 => { + #[cfg(feature = "optimize_for_size")] { + buf[coef_len + 1].write(b'0' + (exp / 10) as u8); + buf[coef_len + 2].write(b'0' + (exp % 10) as u8); + } + #[cfg(not(feature = "optimize_for_size"))] { + buf[coef_len + 1].write(DECIMAL_PAIRS[exp * 2 + 0]); + buf[coef_len + 2].write(DECIMAL_PAIRS[exp * 2 + 1]); + } + coef_len + 3 + }, + _ => { + const { assert!($T::MAX.ilog10() < 100) }; + // SAFETY: A `u256::MAX` would get exponent 77. + unsafe { core::hint::unreachable_unchecked() } + } }; - - let parts = &[ - numfmt::Part::Copy(buf_slice), - numfmt::Part::Zero(added_precision), - numfmt::Part::Copy(exp_slice), - ]; - let sign = if !is_nonnegative { - "-" - } else if f.sign_plus() { - "+" + // SAFETY: All bytes up until text_len have been set. + let text = unsafe { buf[..text_len].assume_init_ref() }; + + if more_prec == 0 { + // SAFETY: Text is set with ASCII exclusively: either a decimal, + // or a LETTER_E, or a dot. ASCII implies valid UTF-8. + let as_str = unsafe { str::from_utf8_unchecked(text) }; + f.pad_integral(is_nonnegative, "", as_str) } else { - "" - }; - let formatted = numfmt::Formatted { sign, parts }; - // SAFETY: `buf_slice` and `exp_slice` contain only ASCII characters. - unsafe { f.pad_formatted_parts(&formatted) } + let parts = &[ + numfmt::Part::Copy(&text[..coef_len]), + numfmt::Part::Zero(more_prec), + numfmt::Part::Copy(&text[coef_len..]), + ]; + let sign = if !is_nonnegative { + "-" + } else if f.sign_plus() { + "+" + } else { + "" + }; + // SAFETY: Text is set with ASCII exclusively: either a decimal, + // or a LETTER_E, or a dot. ASCII implies valid UTF-8. + unsafe { f.pad_formatted_parts(&numfmt::Formatted { sign, parts }) } + } } $( - #[stable(feature = "integer_exp_format", since = "1.42.0")] - impl fmt::LowerExp for $Signed { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let is_nonnegative = *self >= 0; - let n = if is_nonnegative { - *self as $T - } else { - self.unsigned_abs() as $T - }; - $fmt_fn(n, is_nonnegative, false, f) - } + const _: () = { + assert!($Signed::MIN < 0, "need signed"); + assert!($Unsigned::MIN == 0, "need unsigned"); + assert!($Signed::BITS == $Unsigned::BITS, "need counterparts"); + assert!($Signed::BITS <= $T::BITS, "need lossless conversion"); + assert!($Unsigned::BITS <= $T::BITS, "need lossless conversion"); + }; + #[stable(feature = "integer_exp_format", since = "1.42.0")] + impl fmt::LowerExp for $Signed { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + $fmt_fn(f, self.unsigned_abs() as $T, *self >= 0, b'e') } - #[stable(feature = "integer_exp_format", since = "1.42.0")] - impl fmt::LowerExp for $Unsigned { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - $fmt_fn(*self as $T, true, false, f) - } - })* - - $( - #[stable(feature = "integer_exp_format", since = "1.42.0")] - impl fmt::UpperExp for $Signed { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let is_nonnegative = *self >= 0; - let n = if is_nonnegative { - *self as $T - } else { - self.unsigned_abs() as $T - }; - $fmt_fn(n, is_nonnegative, true, f) - } + } + #[stable(feature = "integer_exp_format", since = "1.42.0")] + impl fmt::LowerExp for $Unsigned { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + $fmt_fn(f, *self as $T, true, b'e') } - #[stable(feature = "integer_exp_format", since = "1.42.0")] - impl fmt::UpperExp for $Unsigned { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - $fmt_fn(*self as $T, true, true, f) - } - })* + } + #[stable(feature = "integer_exp_format", since = "1.42.0")] + impl fmt::UpperExp for $Signed { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + $fmt_fn(f, self.unsigned_abs() as $T, *self >= 0, b'E') + } + } + #[stable(feature = "integer_exp_format", since = "1.42.0")] + impl fmt::UpperExp for $Unsigned { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + $fmt_fn(f, *self as $T, true, b'E') + } + } + )* + }; } @@ -658,10 +695,10 @@ impl u128 { remain /= 1_00_00; let pair1 = (quad / 100) as usize; let pair2 = (quad % 100) as usize; - buf[offset + 0].write(DEC_DIGITS_LUT[pair1 * 2 + 0]); - buf[offset + 1].write(DEC_DIGITS_LUT[pair1 * 2 + 1]); - buf[offset + 2].write(DEC_DIGITS_LUT[pair2 * 2 + 0]); - buf[offset + 3].write(DEC_DIGITS_LUT[pair2 * 2 + 1]); + buf[offset + 0].write(DECIMAL_PAIRS[pair1 * 2 + 0]); + buf[offset + 1].write(DECIMAL_PAIRS[pair1 * 2 + 1]); + buf[offset + 2].write(DECIMAL_PAIRS[pair2 * 2 + 0]); + buf[offset + 3].write(DECIMAL_PAIRS[pair2 * 2 + 1]); } // Format per two digits from the lookup table. @@ -676,8 +713,8 @@ impl u128 { let pair = (remain % 100) as usize; remain /= 100; - buf[offset + 0].write(DEC_DIGITS_LUT[pair * 2 + 0]); - buf[offset + 1].write(DEC_DIGITS_LUT[pair * 2 + 1]); + buf[offset + 0].write(DECIMAL_PAIRS[pair * 2 + 0]); + buf[offset + 1].write(DECIMAL_PAIRS[pair * 2 + 1]); } // Format the last remaining digit, if any. @@ -693,7 +730,7 @@ impl u128 { // Either the compiler sees that remain < 10, or it prevents // a boundary check up next. let last = (remain & 15) as usize; - buf[offset].write(DEC_DIGITS_LUT[last * 2 + 1]); + buf[offset].write(DECIMAL_PAIRS[last * 2 + 1]); // not used: remain = 0; } offset @@ -792,10 +829,10 @@ fn enc_16lsd<const OFFSET: usize>(buf: &mut [MaybeUninit<u8>], n: u64) { remain /= 1_00_00; let pair1 = (quad / 100) as usize; let pair2 = (quad % 100) as usize; - buf[quad_index * 4 + OFFSET + 0].write(DEC_DIGITS_LUT[pair1 * 2 + 0]); - buf[quad_index * 4 + OFFSET + 1].write(DEC_DIGITS_LUT[pair1 * 2 + 1]); - buf[quad_index * 4 + OFFSET + 2].write(DEC_DIGITS_LUT[pair2 * 2 + 0]); - buf[quad_index * 4 + OFFSET + 3].write(DEC_DIGITS_LUT[pair2 * 2 + 1]); + buf[quad_index * 4 + OFFSET + 0].write(DECIMAL_PAIRS[pair1 * 2 + 0]); + buf[quad_index * 4 + OFFSET + 1].write(DECIMAL_PAIRS[pair1 * 2 + 1]); + buf[quad_index * 4 + OFFSET + 2].write(DECIMAL_PAIRS[pair2 * 2 + 0]); + buf[quad_index * 4 + OFFSET + 3].write(DECIMAL_PAIRS[pair2 * 2 + 1]); } } diff --git a/library/core/src/intrinsics/fallback.rs b/library/core/src/intrinsics/fallback.rs index eec5c4d646d..932537f2581 100644 --- a/library/core/src/intrinsics/fallback.rs +++ b/library/core/src/intrinsics/fallback.rs @@ -7,9 +7,8 @@ )] #![allow(missing_docs)] -#[const_trait] #[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")] -pub trait CarryingMulAdd: Copy + 'static { +pub const trait CarryingMulAdd: Copy + 'static { type Unsigned: Copy + 'static; fn carrying_mul_add( self, @@ -111,9 +110,8 @@ impl const CarryingMulAdd for i128 { } } -#[const_trait] #[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")] -pub trait DisjointBitOr: Copy + 'static { +pub const trait DisjointBitOr: Copy + 'static { /// See [`super::disjoint_bitor`]; we just need the trait indirection to handle /// different types since calling intrinsics with generics doesn't work. unsafe fn disjoint_bitor(self, other: Self) -> Self; @@ -148,3 +146,75 @@ impl_disjoint_bitor! { u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, } + +#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")] +pub const trait FunnelShift: Copy + 'static { + /// See [`super::unchecked_funnel_shl`]; we just need the trait indirection to handle + /// different types since calling intrinsics with generics doesn't work. + unsafe fn unchecked_funnel_shl(self, rhs: Self, shift: u32) -> Self; + + /// See [`super::unchecked_funnel_shr`]; we just need the trait indirection to handle + /// different types since calling intrinsics with generics doesn't work. + unsafe fn unchecked_funnel_shr(self, rhs: Self, shift: u32) -> Self; +} + +macro_rules! impl_funnel_shifts { + ($($type:ident),*) => {$( + #[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")] + impl const FunnelShift for $type { + #[cfg_attr(miri, track_caller)] + #[inline] + unsafe fn unchecked_funnel_shl(self, rhs: Self, shift: u32) -> Self { + // This implementation is also used by Miri so we have to check the precondition. + // SAFETY: this is guaranteed by the caller + unsafe { super::assume(shift < $type::BITS) }; + if shift == 0 { + self + } else { + // SAFETY: + // - `shift < T::BITS`, which satisfies `unchecked_shl` + // - this also ensures that `T::BITS - shift < T::BITS` (shift = 0 is checked + // above), which satisfies `unchecked_shr` + // - because the types are unsigned, the combination are disjoint bits (this is + // not true if they're signed, since SHR will fill in the empty space with a + // sign bit, not zero) + unsafe { + super::disjoint_bitor( + super::unchecked_shl(self, shift), + super::unchecked_shr(rhs, $type::BITS - shift), + ) + } + } + } + + #[cfg_attr(miri, track_caller)] + #[inline] + unsafe fn unchecked_funnel_shr(self, rhs: Self, shift: u32) -> Self { + // This implementation is also used by Miri so we have to check the precondition. + // SAFETY: this is guaranteed by the caller + unsafe { super::assume(shift < $type::BITS) }; + if shift == 0 { + rhs + } else { + // SAFETY: + // - `shift < T::BITS`, which satisfies `unchecked_shr` + // - this also ensures that `T::BITS - shift < T::BITS` (shift = 0 is checked + // above), which satisfies `unchecked_shl` + // - because the types are unsigned, the combination are disjoint bits (this is + // not true if they're signed, since SHR will fill in the empty space with a + // sign bit, not zero) + unsafe { + super::disjoint_bitor( + super::unchecked_shl(self, $type::BITS - shift), + super::unchecked_shr(rhs, shift), + ) + } + } + } + } + )*}; +} + +impl_funnel_shifts! { + u8, u16, u32, u64, u128, usize +} diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 904aa52c784..bffffbc29c1 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2102,6 +2102,61 @@ pub const fn saturating_add<T: Copy>(a: T, b: T) -> T; #[rustc_intrinsic] pub const fn saturating_sub<T: Copy>(a: T, b: T) -> T; +/// Funnel Shift left. +/// +/// Concatenates `a` and `b` (with `a` in the most significant half), +/// creating an integer twice as wide. Then shift this integer left +/// by `shift`), and extract the most significant half. If `a` and `b` +/// are the same, this is equivalent to a rotate left operation. +/// +/// It is undefined behavior if `shift` is greater than or equal to the +/// bit size of `T`. +/// +/// Safe versions of this intrinsic are available on the integer primitives +/// via the `funnel_shl` method. For example, [`u32::funnel_shl`]. +#[rustc_intrinsic] +#[rustc_nounwind] +#[rustc_const_unstable(feature = "funnel_shifts", issue = "145686")] +#[unstable(feature = "funnel_shifts", issue = "145686")] +#[track_caller] +#[miri::intrinsic_fallback_is_spec] +pub const unsafe fn unchecked_funnel_shl<T: [const] fallback::FunnelShift>( + a: T, + b: T, + shift: u32, +) -> T { + // SAFETY: caller ensures that `shift` is in-range + unsafe { a.unchecked_funnel_shl(b, shift) } +} + +/// Funnel Shift right. +/// +/// Concatenates `a` and `b` (with `a` in the most significant half), +/// creating an integer twice as wide. Then shift this integer right +/// by `shift` (taken modulo the bit size of `T`), and extract the +/// least significant half. If `a` and `b` are the same, this is equivalent +/// to a rotate right operation. +/// +/// It is undefined behavior if `shift` is greater than or equal to the +/// bit size of `T`. +/// +/// Safer versions of this intrinsic are available on the integer primitives +/// via the `funnel_shr` method. For example, [`u32::funnel_shr`] +#[rustc_intrinsic] +#[rustc_nounwind] +#[rustc_const_unstable(feature = "funnel_shifts", issue = "145686")] +#[unstable(feature = "funnel_shifts", issue = "145686")] +#[track_caller] +#[miri::intrinsic_fallback_is_spec] +pub const unsafe fn unchecked_funnel_shr<T: [const] fallback::FunnelShift>( + a: T, + b: T, + shift: u32, +) -> T { + // SAFETY: caller ensures that `shift` is in-range + unsafe { a.unchecked_funnel_shr(b, shift) } +} + /// This is an implementation detail of [`crate::ptr::read`] and should /// not be used anywhere else. See its comments for why this exists. /// diff --git a/library/core/src/iter/adapters/chain.rs b/library/core/src/iter/adapters/chain.rs index 943b88e2330..3ebdf7b4727 100644 --- a/library/core/src/iter/adapters/chain.rs +++ b/library/core/src/iter/adapters/chain.rs @@ -321,6 +321,7 @@ impl<A: Default, B: Default> Default for Chain<A, B> { /// /// // take requires `Default` /// let _: Chain<_, _> = mem::take(&mut foo.0); + /// ``` fn default() -> Self { Chain::new(Default::default(), Default::default()) } diff --git a/library/core/src/iter/adapters/peekable.rs b/library/core/src/iter/adapters/peekable.rs index a6522659620..a55de75d56c 100644 --- a/library/core/src/iter/adapters/peekable.rs +++ b/library/core/src/iter/adapters/peekable.rs @@ -317,6 +317,108 @@ impl<I: Iterator> Peekable<I> { { self.next_if(|next| next == expected) } + + /// Consumes the next value of this iterator and applies a function `f` on it, + /// returning the result if the closure returns `Ok`. + /// + /// Otherwise if the closure returns `Err` the value is put back for the next iteration. + /// + /// The content of the `Err` variant is typically the original value of the closure, + /// but this is not required. If a different value is returned, + /// the next `peek()` or `next()` call will result in this new value. + /// This is similar to modifying the output of `peek_mut()`. + /// + /// If the closure panics, the next value will always be consumed and dropped + /// even if the panic is caught, because the closure never returned an `Err` value to put back. + /// + /// # Examples + /// + /// Parse the leading decimal number from an iterator of characters. + /// ``` + /// #![feature(peekable_next_if_map)] + /// let mut iter = "125 GOTO 10".chars().peekable(); + /// let mut line_num = 0_u32; + /// while let Some(digit) = iter.next_if_map(|c| c.to_digit(10).ok_or(c)) { + /// line_num = line_num * 10 + digit; + /// } + /// assert_eq!(line_num, 125); + /// assert_eq!(iter.collect::<String>(), " GOTO 10"); + /// ``` + /// + /// Matching custom types. + /// ``` + /// #![feature(peekable_next_if_map)] + /// + /// #[derive(Debug, PartialEq, Eq)] + /// enum Node { + /// Comment(String), + /// Red(String), + /// Green(String), + /// Blue(String), + /// } + /// + /// /// Combines all consecutive `Comment` nodes into a single one. + /// fn combine_comments(nodes: Vec<Node>) -> Vec<Node> { + /// let mut result = Vec::with_capacity(nodes.len()); + /// let mut iter = nodes.into_iter().peekable(); + /// let mut comment_text = None::<String>; + /// loop { + /// // Typically the closure in .next_if_map() matches on the input, + /// // extracts the desired pattern into an `Ok`, + /// // and puts the rest into an `Err`. + /// while let Some(text) = iter.next_if_map(|node| match node { + /// Node::Comment(text) => Ok(text), + /// other => Err(other), + /// }) { + /// comment_text.get_or_insert_default().push_str(&text); + /// } + /// + /// if let Some(text) = comment_text.take() { + /// result.push(Node::Comment(text)); + /// } + /// if let Some(node) = iter.next() { + /// result.push(node); + /// } else { + /// break; + /// } + /// } + /// result + /// } + ///# assert_eq!( // hiding the test to avoid cluttering the documentation. + ///# combine_comments(vec![ + ///# Node::Comment("The".to_owned()), + ///# Node::Comment("Quick".to_owned()), + ///# Node::Comment("Brown".to_owned()), + ///# Node::Red("Fox".to_owned()), + ///# Node::Green("Jumped".to_owned()), + ///# Node::Comment("Over".to_owned()), + ///# Node::Blue("The".to_owned()), + ///# Node::Comment("Lazy".to_owned()), + ///# Node::Comment("Dog".to_owned()), + ///# ]), + ///# vec![ + ///# Node::Comment("TheQuickBrown".to_owned()), + ///# Node::Red("Fox".to_owned()), + ///# Node::Green("Jumped".to_owned()), + ///# Node::Comment("Over".to_owned()), + ///# Node::Blue("The".to_owned()), + ///# Node::Comment("LazyDog".to_owned()), + ///# ], + ///# ) + /// ``` + #[unstable(feature = "peekable_next_if_map", issue = "143702")] + pub fn next_if_map<R>(&mut self, f: impl FnOnce(I::Item) -> Result<R, I::Item>) -> Option<R> { + let unpeek = if let Some(item) = self.next() { + match f(item) { + Ok(result) => return Some(result), + Err(item) => Some(item), + } + } else { + None + }; + self.peeked = Some(unpeek); + None + } } #[unstable(feature = "trusted_len", issue = "37572")] diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 82506379cac..c306011bdda 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -156,6 +156,7 @@ #![feature(f128)] #![feature(freeze_impls)] #![feature(fundamental)] +#![feature(funnel_shifts)] #![feature(if_let_guard)] #![feature(intra_doc_pointers)] #![feature(intrinsics)] diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 73aad27afef..d03d7a43469 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -1057,8 +1057,7 @@ marker_impls! { #[rustc_on_unimplemented(message = "can't drop `{Self}`", append_const_msg)] #[rustc_deny_explicit_impl] #[rustc_do_not_implement_via_object] -#[const_trait] -pub trait Destruct {} +pub const trait Destruct {} /// A marker for tuple types. /// diff --git a/library/core/src/mem/manually_drop.rs b/library/core/src/mem/manually_drop.rs index 02bb8179293..8868f05f1b9 100644 --- a/library/core/src/mem/manually_drop.rs +++ b/library/core/src/mem/manually_drop.rs @@ -258,7 +258,8 @@ impl<T: ?Sized> ManuallyDrop<T> { } #[stable(feature = "manually_drop", since = "1.20.0")] -impl<T: ?Sized> Deref for ManuallyDrop<T> { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl<T: ?Sized> const Deref for ManuallyDrop<T> { type Target = T; #[inline(always)] fn deref(&self) -> &T { @@ -267,7 +268,8 @@ impl<T: ?Sized> Deref for ManuallyDrop<T> { } #[stable(feature = "manually_drop", since = "1.20.0")] -impl<T: ?Sized> DerefMut for ManuallyDrop<T> { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl<T: ?Sized> const DerefMut for ManuallyDrop<T> { #[inline(always)] fn deref_mut(&mut self) -> &mut T { &mut self.value diff --git a/library/core/src/net/ip_addr.rs b/library/core/src/net/ip_addr.rs index 3bf113d017c..9779fb8fe4d 100644 --- a/library/core/src/net/ip_addr.rs +++ b/library/core/src/net/ip_addr.rs @@ -1087,7 +1087,7 @@ impl fmt::Debug for IpAddr { } #[stable(feature = "ip_from_ip", since = "1.16.0")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const From<Ipv4Addr> for IpAddr { /// Copies this address to a new `IpAddr::V4`. /// @@ -1110,7 +1110,7 @@ impl const From<Ipv4Addr> for IpAddr { } #[stable(feature = "ip_from_ip", since = "1.16.0")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const From<Ipv6Addr> for IpAddr { /// Copies this address to a new `IpAddr::V6`. /// @@ -1221,7 +1221,7 @@ impl Ord for Ipv4Addr { } #[stable(feature = "ip_u32", since = "1.1.0")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const From<Ipv4Addr> for u32 { /// Uses [`Ipv4Addr::to_bits`] to convert an IPv4 address to a host byte order `u32`. #[inline] @@ -1231,7 +1231,7 @@ impl const From<Ipv4Addr> for u32 { } #[stable(feature = "ip_u32", since = "1.1.0")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const From<u32> for Ipv4Addr { /// Uses [`Ipv4Addr::from_bits`] to convert a host byte order `u32` into an IPv4 address. #[inline] @@ -1241,7 +1241,7 @@ impl const From<u32> for Ipv4Addr { } #[stable(feature = "from_slice_v4", since = "1.9.0")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const From<[u8; 4]> for Ipv4Addr { /// Creates an `Ipv4Addr` from a four element byte array. /// @@ -1260,7 +1260,7 @@ impl const From<[u8; 4]> for Ipv4Addr { } #[stable(feature = "ip_from_slice", since = "1.17.0")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const From<[u8; 4]> for IpAddr { /// Creates an `IpAddr::V4` from a four element byte array. /// @@ -2215,7 +2215,7 @@ impl Ord for Ipv6Addr { } #[stable(feature = "i128", since = "1.26.0")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const From<Ipv6Addr> for u128 { /// Uses [`Ipv6Addr::to_bits`] to convert an IPv6 address to a host byte order `u128`. #[inline] @@ -2224,7 +2224,7 @@ impl const From<Ipv6Addr> for u128 { } } #[stable(feature = "i128", since = "1.26.0")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const From<u128> for Ipv6Addr { /// Uses [`Ipv6Addr::from_bits`] to convert a host byte order `u128` to an IPv6 address. #[inline] @@ -2234,7 +2234,7 @@ impl const From<u128> for Ipv6Addr { } #[stable(feature = "ipv6_from_octets", since = "1.9.0")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const From<[u8; 16]> for Ipv6Addr { /// Creates an `Ipv6Addr` from a sixteen element byte array. /// @@ -2262,7 +2262,7 @@ impl const From<[u8; 16]> for Ipv6Addr { } #[stable(feature = "ipv6_from_segments", since = "1.16.0")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const From<[u16; 8]> for Ipv6Addr { /// Creates an `Ipv6Addr` from an eight element 16-bit array. /// @@ -2291,7 +2291,7 @@ impl const From<[u16; 8]> for Ipv6Addr { } #[stable(feature = "ip_from_slice", since = "1.17.0")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const From<[u8; 16]> for IpAddr { /// Creates an `IpAddr::V6` from a sixteen element byte array. /// @@ -2319,7 +2319,7 @@ impl const From<[u8; 16]> for IpAddr { } #[stable(feature = "ip_from_slice", since = "1.17.0")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const From<[u16; 8]> for IpAddr { /// Creates an `IpAddr::V6` from an eight element 16-bit array. /// diff --git a/library/core/src/net/socket_addr.rs b/library/core/src/net/socket_addr.rs index df99e9b20c2..ccc53c156d3 100644 --- a/library/core/src/net/socket_addr.rs +++ b/library/core/src/net/socket_addr.rs @@ -592,7 +592,7 @@ impl SocketAddrV6 { } #[stable(feature = "ip_from_ip", since = "1.16.0")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const From<SocketAddrV4> for SocketAddr { /// Converts a [`SocketAddrV4`] into a [`SocketAddr::V4`]. #[inline] @@ -602,7 +602,7 @@ impl const From<SocketAddrV4> for SocketAddr { } #[stable(feature = "ip_from_ip", since = "1.16.0")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const From<SocketAddrV6> for SocketAddr { /// Converts a [`SocketAddrV6`] into a [`SocketAddr::V6`]. #[inline] @@ -612,7 +612,7 @@ impl const From<SocketAddrV6> for SocketAddr { } #[stable(feature = "addr_from_into_ip", since = "1.17.0")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl<I: [const] Into<IpAddr>> const From<(I, u16)> for SocketAddr { /// Converts a tuple struct (Into<[`IpAddr`]>, `u16`) into a [`SocketAddr`]. /// diff --git a/library/core/src/num/error.rs b/library/core/src/num/error.rs index faa52329827..8a353dc0fbe 100644 --- a/library/core/src/num/error.rs +++ b/library/core/src/num/error.rs @@ -20,7 +20,7 @@ impl fmt::Display for TryFromIntError { impl Error for TryFromIntError {} #[stable(feature = "try_from", since = "1.34.0")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const From<Infallible> for TryFromIntError { fn from(x: Infallible) -> TryFromIntError { match x {} @@ -28,7 +28,7 @@ impl const From<Infallible> for TryFromIntError { } #[unstable(feature = "never_type", issue = "35121")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const From<!> for TryFromIntError { #[inline] fn from(never: !) -> TryFromIntError { diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index 4282b1af9f2..66c892aadd0 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -832,7 +832,6 @@ impl f128 { #[unstable(feature = "f128", issue = "116909")] #[rustc_const_unstable(feature = "f128", issue = "116909")] pub const fn midpoint(self, other: f128) -> f128 { - const LO: f128 = f128::MIN_POSITIVE * 2.; const HI: f128 = f128::MAX / 2.; let (a, b) = (self, other); @@ -842,14 +841,7 @@ impl f128 { if abs_a <= HI && abs_b <= HI { // Overflow is impossible (a + b) / 2. - } else if abs_a < LO { - // Not safe to halve `a` (would underflow) - a + (b / 2.) - } else if abs_b < LO { - // Not safe to halve `b` (would underflow) - (a / 2.) + b } else { - // Safe to halve `a` and `b` (a / 2.) + (b / 2.) } } diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index 23a8661c14b..81220065e72 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -820,7 +820,6 @@ impl f16 { #[unstable(feature = "f16", issue = "116909")] #[rustc_const_unstable(feature = "f16", issue = "116909")] pub const fn midpoint(self, other: f16) -> f16 { - const LO: f16 = f16::MIN_POSITIVE * 2.; const HI: f16 = f16::MAX / 2.; let (a, b) = (self, other); @@ -830,14 +829,7 @@ impl f16 { if abs_a <= HI && abs_b <= HI { // Overflow is impossible (a + b) / 2. - } else if abs_a < LO { - // Not safe to halve `a` (would underflow) - a + (b / 2.) - } else if abs_b < LO { - // Not safe to halve `b` (would underflow) - (a / 2.) + b } else { - // Safe to halve `a` and `b` (a / 2.) + (b / 2.) } } diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index da08bfc5950..cefcf1d1fe2 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -1025,7 +1025,6 @@ impl f32 { ((self as f64 + other as f64) / 2.0) as f32 } _ => { - const LO: f32 = f32::MIN_POSITIVE * 2.; const HI: f32 = f32::MAX / 2.; let (a, b) = (self, other); @@ -1035,14 +1034,7 @@ impl f32 { if abs_a <= HI && abs_b <= HI { // Overflow is impossible (a + b) / 2. - } else if abs_a < LO { - // Not safe to halve `a` (would underflow) - a + (b / 2.) - } else if abs_b < LO { - // Not safe to halve `b` (would underflow) - (a / 2.) + b } else { - // Safe to halve `a` and `b` (a / 2.) + (b / 2.) } } @@ -1954,8 +1946,8 @@ pub mod math { /// let abs_difference_x = (f32::math::abs_sub(x, 1.0) - 2.0).abs(); /// let abs_difference_y = (f32::math::abs_sub(y, 1.0) - 0.0).abs(); /// - /// assert!(abs_difference_x <= f32::EPSILON); - /// assert!(abs_difference_y <= f32::EPSILON); + /// assert!(abs_difference_x <= 1e-6); + /// assert!(abs_difference_y <= 1e-6); /// ``` /// /// _This standalone function is for testing only. @@ -2000,7 +1992,7 @@ pub mod math { /// // x^(1/3) - 2 == 0 /// let abs_difference = (f32::math::cbrt(x) - 2.0).abs(); /// - /// assert!(abs_difference <= f32::EPSILON); + /// assert!(abs_difference <= 1e-6); /// ``` /// /// _This standalone function is for testing only. diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index dc0977cb147..9dd1141e703 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -1026,7 +1026,6 @@ impl f64 { #[stable(feature = "num_midpoint", since = "1.85.0")] #[rustc_const_stable(feature = "num_midpoint", since = "1.85.0")] pub const fn midpoint(self, other: f64) -> f64 { - const LO: f64 = f64::MIN_POSITIVE * 2.; const HI: f64 = f64::MAX / 2.; let (a, b) = (self, other); @@ -1036,14 +1035,7 @@ impl f64 { if abs_a <= HI && abs_b <= HI { // Overflow is impossible (a + b) / 2. - } else if abs_a < LO { - // Not safe to halve `a` (would underflow) - a + (b / 2.) - } else if abs_b < LO { - // Not safe to halve `b` (would underflow) - (a / 2.) + b } else { - // Safe to halve `a` and `b` (a / 2.) + (b / 2.) } } diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 25864db5fea..64a3dd3e8bc 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -1285,7 +1285,7 @@ macro_rules! int_impl { /// /// ```should_panic #[doc = concat!("let _ = ", stringify!($SelfT), "::MIN.strict_neg();")] - /// + /// ``` #[stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] #[must_use = "this returns the result of the operation, \ @@ -1413,6 +1413,66 @@ macro_rules! int_impl { } } + /// Exact shift left. Computes `self << rhs` as long as it can be reversed losslessly. + /// + /// Returns `None` if any bits that would be shifted out differ from the resulting sign bit + /// or if `rhs` >= + #[doc = concat!("`", stringify!($SelfT), "::BITS`.")] + /// Otherwise, returns `Some(self << rhs)`. + /// + /// # Examples + /// + /// ``` + /// #![feature(exact_bitshifts)] + /// + #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".exact_shl(4), Some(0x10));")] + #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".exact_shl(", stringify!($SelfT), "::BITS - 2), Some(1 << ", stringify!($SelfT), "::BITS - 2));")] + #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".exact_shl(", stringify!($SelfT), "::BITS - 1), None);")] + #[doc = concat!("assert_eq!((-0x2", stringify!($SelfT), ").exact_shl(", stringify!($SelfT), "::BITS - 2), Some(-0x2 << ", stringify!($SelfT), "::BITS - 2));")] + #[doc = concat!("assert_eq!((-0x2", stringify!($SelfT), ").exact_shl(", stringify!($SelfT), "::BITS - 1), None);")] + /// ``` + #[unstable(feature = "exact_bitshifts", issue = "144336")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn exact_shl(self, rhs: u32) -> Option<$SelfT> { + if rhs < self.leading_zeros() || rhs < self.leading_ones() { + // SAFETY: rhs is checked above + Some(unsafe { self.unchecked_shl(rhs) }) + } else { + None + } + } + + /// Unchecked exact shift left. Computes `self << rhs`, assuming the operation can be + /// losslessly reversed and `rhs` cannot be larger than + #[doc = concat!("`", stringify!($SelfT), "::BITS`.")] + /// + /// # Safety + /// + /// This results in undefined behavior when `rhs >= self.leading_zeros() && rhs >= + /// self.leading_ones()` i.e. when + #[doc = concat!("[`", stringify!($SelfT), "::exact_shl`]")] + /// would return `None`. + #[unstable(feature = "exact_bitshifts", issue = "144336")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const unsafe fn unchecked_exact_shl(self, rhs: u32) -> $SelfT { + assert_unsafe_precondition!( + check_language_ub, + concat!(stringify!($SelfT), "::unchecked_exact_shl cannot shift out non-zero bits"), + ( + zeros: u32 = self.leading_zeros(), + ones: u32 = self.leading_ones(), + rhs: u32 = rhs, + ) => rhs < zeros || rhs < ones, + ); + + // SAFETY: this is guaranteed to be safe by the caller + unsafe { self.unchecked_shl(rhs) } + } + /// Checked shift right. Computes `self >> rhs`, returning `None` if `rhs` is /// larger than or equal to the number of bits in `self`. /// @@ -1534,6 +1594,63 @@ macro_rules! int_impl { } } + /// Exact shift right. Computes `self >> rhs` as long as it can be reversed losslessly. + /// + /// Returns `None` if any non-zero bits would be shifted out or if `rhs` >= + #[doc = concat!("`", stringify!($SelfT), "::BITS`.")] + /// Otherwise, returns `Some(self >> rhs)`. + /// + /// # Examples + /// + /// ``` + /// #![feature(exact_bitshifts)] + /// + #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".exact_shr(4), Some(0x1));")] + #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".exact_shr(5), None);")] + /// ``` + #[unstable(feature = "exact_bitshifts", issue = "144336")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn exact_shr(self, rhs: u32) -> Option<$SelfT> { + if rhs <= self.trailing_zeros() && rhs < <$SelfT>::BITS { + // SAFETY: rhs is checked above + Some(unsafe { self.unchecked_shr(rhs) }) + } else { + None + } + } + + /// Unchecked exact shift right. Computes `self >> rhs`, assuming the operation can be + /// losslessly reversed and `rhs` cannot be larger than + #[doc = concat!("`", stringify!($SelfT), "::BITS`.")] + /// + /// # Safety + /// + /// This results in undefined behavior when `rhs > self.trailing_zeros() || rhs >= + #[doc = concat!(stringify!($SelfT), "::BITS`")] + /// i.e. when + #[doc = concat!("[`", stringify!($SelfT), "::exact_shr`]")] + /// would return `None`. + #[unstable(feature = "exact_bitshifts", issue = "144336")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const unsafe fn unchecked_exact_shr(self, rhs: u32) -> $SelfT { + assert_unsafe_precondition!( + check_language_ub, + concat!(stringify!($SelfT), "::unchecked_exact_shr cannot shift out non-zero bits"), + ( + zeros: u32 = self.trailing_zeros(), + bits: u32 = <$SelfT>::BITS, + rhs: u32 = rhs, + ) => rhs <= zeros && rhs < bits, + ); + + // SAFETY: this is guaranteed to be safe by the caller + unsafe { self.unchecked_shr(rhs) } + } + /// Checked absolute value. Computes `self.abs()`, returning `None` if /// `self == MIN`. /// diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index acfe38b7a37..54d5a63633c 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -454,6 +454,9 @@ impl u8 { rot = 2, rot_op = "0x82", rot_result = "0xa", + fsh_op = "0x36", + fshl_result = "0x8", + fshr_result = "0x8d", swap_op = "0x12", swapped = "0x12", reversed = "0x48", @@ -1088,6 +1091,9 @@ impl u16 { rot = 4, rot_op = "0xa003", rot_result = "0x3a", + fsh_op = "0x2de", + fshl_result = "0x30", + fshr_result = "0x302d", swap_op = "0x1234", swapped = "0x3412", reversed = "0x2c48", @@ -1135,6 +1141,9 @@ impl u32 { rot = 8, rot_op = "0x10000b3", rot_result = "0xb301", + fsh_op = "0x2fe78e45", + fshl_result = "0xb32f", + fshr_result = "0xb32fe78e", swap_op = "0x12345678", swapped = "0x78563412", reversed = "0x1e6a2c48", @@ -1158,6 +1167,9 @@ impl u64 { rot = 12, rot_op = "0xaa00000000006e1", rot_result = "0x6e10aa", + fsh_op = "0x2fe78e45983acd98", + fshl_result = "0x6e12fe", + fshr_result = "0x6e12fe78e45983ac", swap_op = "0x1234567890123456", swapped = "0x5634129078563412", reversed = "0x6a2c48091e6a2c48", @@ -1181,6 +1193,9 @@ impl u128 { rot = 16, rot_op = "0x13f40000000000000000000000004f76", rot_result = "0x4f7613f4", + fsh_op = "0x2fe78e45983acd98039000008736273", + fshl_result = "0x4f7602fe", + fshr_result = "0x4f7602fe78e45983acd9803900000873", swap_op = "0x12345678901234567890123456789012", swapped = "0x12907856341290785634129078563412", reversed = "0x48091e6a2c48091e6a2c48091e6a2c48", @@ -1207,6 +1222,9 @@ impl usize { rot = 4, rot_op = "0xa003", rot_result = "0x3a", + fsh_op = "0x2fe78e45983acd98039000008736273", + fshl_result = "0x4f7602fe", + fshr_result = "0x4f7602fe78e45983acd9803900000873", swap_op = "0x1234", swapped = "0x3412", reversed = "0x2c48", @@ -1231,6 +1249,9 @@ impl usize { rot = 8, rot_op = "0x10000b3", rot_result = "0xb301", + fsh_op = "0x2fe78e45", + fshl_result = "0xb32f", + fshr_result = "0xb32fe78e", swap_op = "0x12345678", swapped = "0x78563412", reversed = "0x1e6a2c48", @@ -1255,6 +1276,9 @@ impl usize { rot = 12, rot_op = "0xaa00000000006e1", rot_result = "0x6e10aa", + fsh_op = "0x2fe78e45983acd98", + fshl_result = "0x6e12fe", + fshr_result = "0x6e12fe78e45983ac", swap_op = "0x1234567890123456", swapped = "0x5634129078563412", reversed = "0x6a2c48091e6a2c48", @@ -1378,7 +1402,7 @@ const fn from_ascii_radix_panic(radix: u32) -> ! { macro_rules! from_str_int_impl { ($signedness:ident $($int_ty:ty)+) => {$( #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_try", issue = "74935")] + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const FromStr for $int_ty { type Err = ParseIntError; diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index e02d9260a16..0f38081c355 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -297,7 +297,7 @@ where } #[stable(feature = "from_nonzero", since = "1.31.0")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl<T> const From<NonZero<T>> for T where T: ZeroablePrimitive, diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index c1e656fdea2..bf72ec83197 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -14,6 +14,9 @@ macro_rules! uint_impl { rot = $rot:literal, rot_op = $rot_op:literal, rot_result = $rot_result:literal, + fsh_op = $fsh_op:literal, + fshl_result = $fshl_result:literal, + fshr_result = $fshr_result:literal, swap_op = $swap_op:literal, swapped = $swapped:literal, reversed = $reversed:literal, @@ -375,6 +378,76 @@ macro_rules! uint_impl { return intrinsics::rotate_right(self, n); } + /// Performs a left funnel shift (concatenates `self` with `rhs`, with `self` + /// making up the most significant half, then shifts the combined value left + /// by `n`, and most significant half is extracted to produce the result). + /// + /// Please note this isn't the same operation as the `<<` shifting operator or + /// [`rotate_left`](Self::rotate_left), although `a.funnel_shl(a, n)` is *equivalent* + /// to `a.rotate_left(n)`. + /// + /// # Panics + /// + /// If `n` is greater than or equal to the number of bits in `self` + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(funnel_shifts)] + #[doc = concat!("let a = ", $rot_op, stringify!($SelfT), ";")] + #[doc = concat!("let b = ", $fsh_op, stringify!($SelfT), ";")] + #[doc = concat!("let m = ", $fshl_result, ";")] + /// + #[doc = concat!("assert_eq!(a.funnel_shl(b, ", $rot, "), m);")] + /// ``` + #[rustc_const_unstable(feature = "funnel_shifts", issue = "145686")] + #[unstable(feature = "funnel_shifts", issue = "145686")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub const fn funnel_shl(self, rhs: Self, n: u32) -> Self { + assert!(n < Self::BITS, "attempt to funnel shift left with overflow"); + // SAFETY: just checked that `shift` is in-range + unsafe { intrinsics::unchecked_funnel_shl(self, rhs, n) } + } + + /// Performs a right funnel shift (concatenates `self` and `rhs`, with `self` + /// making up the most significant half, then shifts the combined value right + /// by `n`, and least significant half is extracted to produce the result). + /// + /// Please note this isn't the same operation as the `>>` shifting operator or + /// [`rotate_right`](Self::rotate_right), although `a.funnel_shr(a, n)` is *equivalent* + /// to `a.rotate_right(n)`. + /// + /// # Panics + /// + /// If `n` is greater than or equal to the number of bits in `self` + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(funnel_shifts)] + #[doc = concat!("let a = ", $rot_op, stringify!($SelfT), ";")] + #[doc = concat!("let b = ", $fsh_op, stringify!($SelfT), ";")] + #[doc = concat!("let m = ", $fshr_result, ";")] + /// + #[doc = concat!("assert_eq!(a.funnel_shr(b, ", $rot, "), m);")] + /// ``` + #[rustc_const_unstable(feature = "funnel_shifts", issue = "145686")] + #[unstable(feature = "funnel_shifts", issue = "145686")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub const fn funnel_shr(self, rhs: Self, n: u32) -> Self { + assert!(n < Self::BITS, "attempt to funnel shift right with overflow"); + // SAFETY: just checked that `shift` is in-range + unsafe { intrinsics::unchecked_funnel_shr(self, rhs, n) } + } + /// Reverses the byte order of the integer. /// /// # Examples @@ -1620,7 +1693,7 @@ macro_rules! uint_impl { /// /// ```should_panic #[doc = concat!("let _ = 1", stringify!($SelfT), ".strict_neg();")] - /// + /// ``` #[stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_stable(feature = "strict_overflow_ops", since = "CURRENT_RUSTC_VERSION")] #[must_use = "this returns the result of the operation, \ @@ -1748,6 +1821,63 @@ macro_rules! uint_impl { } } + /// Exact shift left. Computes `self << rhs` as long as it can be reversed losslessly. + /// + /// Returns `None` if any non-zero bits would be shifted out or if `rhs` >= + #[doc = concat!("`", stringify!($SelfT), "::BITS`.")] + /// Otherwise, returns `Some(self << rhs)`. + /// + /// # Examples + /// + /// ``` + /// #![feature(exact_bitshifts)] + /// + #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".exact_shl(4), Some(0x10));")] + #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".exact_shl(129), None);")] + /// ``` + #[unstable(feature = "exact_bitshifts", issue = "144336")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn exact_shl(self, rhs: u32) -> Option<$SelfT> { + if rhs <= self.leading_zeros() && rhs < <$SelfT>::BITS { + // SAFETY: rhs is checked above + Some(unsafe { self.unchecked_shl(rhs) }) + } else { + None + } + } + + /// Unchecked exact shift left. Computes `self << rhs`, assuming the operation can be + /// losslessly reversed `rhs` cannot be larger than + #[doc = concat!("`", stringify!($SelfT), "::BITS`.")] + /// + /// # Safety + /// + /// This results in undefined behavior when `rhs > self.leading_zeros() || rhs >= + #[doc = concat!(stringify!($SelfT), "::BITS`")] + /// i.e. when + #[doc = concat!("[`", stringify!($SelfT), "::exact_shl`]")] + /// would return `None`. + #[unstable(feature = "exact_bitshifts", issue = "144336")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const unsafe fn unchecked_exact_shl(self, rhs: u32) -> $SelfT { + assert_unsafe_precondition!( + check_language_ub, + concat!(stringify!($SelfT), "::exact_shl_unchecked cannot shift out non-zero bits"), + ( + zeros: u32 = self.leading_zeros(), + bits: u32 = <$SelfT>::BITS, + rhs: u32 = rhs, + ) => rhs <= zeros && rhs < bits, + ); + + // SAFETY: this is guaranteed to be safe by the caller + unsafe { self.unchecked_shl(rhs) } + } + /// Checked shift right. Computes `self >> rhs`, returning `None` /// if `rhs` is larger than or equal to the number of bits in `self`. /// @@ -1863,6 +1993,63 @@ macro_rules! uint_impl { } } + /// Exact shift right. Computes `self >> rhs` as long as it can be reversed losslessly. + /// + /// Returns `None` if any non-zero bits would be shifted out or if `rhs` >= + #[doc = concat!("`", stringify!($SelfT), "::BITS`.")] + /// Otherwise, returns `Some(self >> rhs)`. + /// + /// # Examples + /// + /// ``` + /// #![feature(exact_bitshifts)] + /// + #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".exact_shr(4), Some(0x1));")] + #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".exact_shr(5), None);")] + /// ``` + #[unstable(feature = "exact_bitshifts", issue = "144336")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn exact_shr(self, rhs: u32) -> Option<$SelfT> { + if rhs <= self.trailing_zeros() && rhs < <$SelfT>::BITS { + // SAFETY: rhs is checked above + Some(unsafe { self.unchecked_shr(rhs) }) + } else { + None + } + } + + /// Unchecked exact shift right. Computes `self >> rhs`, assuming the operation can be + /// losslessly reversed and `rhs` cannot be larger than + #[doc = concat!("`", stringify!($SelfT), "::BITS`.")] + /// + /// # Safety + /// + /// This results in undefined behavior when `rhs > self.trailing_zeros() || rhs >= + #[doc = concat!(stringify!($SelfT), "::BITS`")] + /// i.e. when + #[doc = concat!("[`", stringify!($SelfT), "::exact_shr`]")] + /// would return `None`. + #[unstable(feature = "exact_bitshifts", issue = "144336")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const unsafe fn unchecked_exact_shr(self, rhs: u32) -> $SelfT { + assert_unsafe_precondition!( + check_language_ub, + concat!(stringify!($SelfT), "::exact_shr_unchecked cannot shift out non-zero bits"), + ( + zeros: u32 = self.trailing_zeros(), + bits: u32 = <$SelfT>::BITS, + rhs: u32 = rhs, + ) => rhs <= zeros && rhs < bits, + ); + + // SAFETY: this is guaranteed to be safe by the caller + unsafe { self.unchecked_shr(rhs) } + } + /// Checked exponentiation. Computes `self.pow(exp)`, returning `None` if /// overflow occurred. /// diff --git a/library/core/src/ops/arith.rs b/library/core/src/ops/arith.rs index 16c719b0c39..6c6479c9984 100644 --- a/library/core/src/ops/arith.rs +++ b/library/core/src/ops/arith.rs @@ -74,8 +74,7 @@ append_const_msg )] #[doc(alias = "+")] -#[const_trait] -pub trait Add<Rhs = Self> { +pub const trait Add<Rhs = Self> { /// The resulting type after applying the `+` operator. #[stable(feature = "rust1", since = "1.0.0")] type Output; @@ -188,8 +187,7 @@ add_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f16 f32 f64 f128 append_const_msg )] #[doc(alias = "-")] -#[const_trait] -pub trait Sub<Rhs = Self> { +pub const trait Sub<Rhs = Self> { /// The resulting type after applying the `-` operator. #[stable(feature = "rust1", since = "1.0.0")] type Output; @@ -323,8 +321,7 @@ sub_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f16 f32 f64 f128 label = "no implementation for `{Self} * {Rhs}`" )] #[doc(alias = "*")] -#[const_trait] -pub trait Mul<Rhs = Self> { +pub const trait Mul<Rhs = Self> { /// The resulting type after applying the `*` operator. #[stable(feature = "rust1", since = "1.0.0")] type Output; @@ -462,8 +459,7 @@ mul_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f16 f32 f64 f128 label = "no implementation for `{Self} / {Rhs}`" )] #[doc(alias = "/")] -#[const_trait] -pub trait Div<Rhs = Self> { +pub const trait Div<Rhs = Self> { /// The resulting type after applying the `/` operator. #[stable(feature = "rust1", since = "1.0.0")] type Output; @@ -572,8 +568,7 @@ div_impl_float! { f16 f32 f64 f128 } label = "no implementation for `{Self} % {Rhs}`" )] #[doc(alias = "%")] -#[const_trait] -pub trait Rem<Rhs = Self> { +pub const trait Rem<Rhs = Self> { /// The resulting type after applying the `%` operator. #[stable(feature = "rust1", since = "1.0.0")] type Output; @@ -694,8 +689,7 @@ rem_impl_float! { f16 f32 f64 f128 } #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_ops", issue = "143802")] #[doc(alias = "-")] -#[const_trait] -pub trait Neg { +pub const trait Neg { /// The resulting type after applying the `-` operator. #[stable(feature = "rust1", since = "1.0.0")] type Output; @@ -772,8 +766,7 @@ neg_impl! { isize i8 i16 i32 i64 i128 f16 f32 f64 f128 } )] #[doc(alias = "+")] #[doc(alias = "+=")] -#[const_trait] -pub trait AddAssign<Rhs = Self> { +pub const trait AddAssign<Rhs = Self> { /// Performs the `+=` operation. /// /// # Example @@ -844,8 +837,7 @@ add_assign_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f16 f32 f )] #[doc(alias = "-")] #[doc(alias = "-=")] -#[const_trait] -pub trait SubAssign<Rhs = Self> { +pub const trait SubAssign<Rhs = Self> { /// Performs the `-=` operation. /// /// # Example @@ -907,8 +899,7 @@ sub_assign_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f16 f32 f )] #[doc(alias = "*")] #[doc(alias = "*=")] -#[const_trait] -pub trait MulAssign<Rhs = Self> { +pub const trait MulAssign<Rhs = Self> { /// Performs the `*=` operation. /// /// # Example @@ -970,8 +961,7 @@ mul_assign_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f16 f32 f )] #[doc(alias = "/")] #[doc(alias = "/=")] -#[const_trait] -pub trait DivAssign<Rhs = Self> { +pub const trait DivAssign<Rhs = Self> { /// Performs the `/=` operation. /// /// # Example @@ -1036,8 +1026,7 @@ div_assign_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f16 f32 f )] #[doc(alias = "%")] #[doc(alias = "%=")] -#[const_trait] -pub trait RemAssign<Rhs = Self> { +pub const trait RemAssign<Rhs = Self> { /// Performs the `%=` operation. /// /// # Example diff --git a/library/core/src/ops/bit.rs b/library/core/src/ops/bit.rs index 00196728219..0cd61b07373 100644 --- a/library/core/src/ops/bit.rs +++ b/library/core/src/ops/bit.rs @@ -32,8 +32,7 @@ #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_ops", issue = "143802")] #[doc(alias = "!")] -#[const_trait] -pub trait Not { +pub const trait Not { /// The resulting type after applying the `!` operator. #[stable(feature = "rust1", since = "1.0.0")] type Output; @@ -148,8 +147,7 @@ impl const Not for ! { message = "no implementation for `{Self} & {Rhs}`", label = "no implementation for `{Self} & {Rhs}`" )] -#[const_trait] -pub trait BitAnd<Rhs = Self> { +pub const trait BitAnd<Rhs = Self> { /// The resulting type after applying the `&` operator. #[stable(feature = "rust1", since = "1.0.0")] type Output; @@ -253,8 +251,7 @@ bitand_impl! { bool usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } message = "no implementation for `{Self} | {Rhs}`", label = "no implementation for `{Self} | {Rhs}`" )] -#[const_trait] -pub trait BitOr<Rhs = Self> { +pub const trait BitOr<Rhs = Self> { /// The resulting type after applying the `|` operator. #[stable(feature = "rust1", since = "1.0.0")] type Output; @@ -358,8 +355,7 @@ bitor_impl! { bool usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } message = "no implementation for `{Self} ^ {Rhs}`", label = "no implementation for `{Self} ^ {Rhs}`" )] -#[const_trait] -pub trait BitXor<Rhs = Self> { +pub const trait BitXor<Rhs = Self> { /// The resulting type after applying the `^` operator. #[stable(feature = "rust1", since = "1.0.0")] type Output; @@ -462,8 +458,7 @@ bitxor_impl! { bool usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } message = "no implementation for `{Self} << {Rhs}`", label = "no implementation for `{Self} << {Rhs}`" )] -#[const_trait] -pub trait Shl<Rhs = Self> { +pub const trait Shl<Rhs = Self> { /// The resulting type after applying the `<<` operator. #[stable(feature = "rust1", since = "1.0.0")] type Output; @@ -585,8 +580,7 @@ shl_impl_all! { u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize } message = "no implementation for `{Self} >> {Rhs}`", label = "no implementation for `{Self} >> {Rhs}`" )] -#[const_trait] -pub trait Shr<Rhs = Self> { +pub const trait Shr<Rhs = Self> { /// The resulting type after applying the `>>` operator. #[stable(feature = "rust1", since = "1.0.0")] type Output; @@ -717,8 +711,7 @@ shr_impl_all! { u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize } message = "no implementation for `{Self} &= {Rhs}`", label = "no implementation for `{Self} &= {Rhs}`" )] -#[const_trait] -pub trait BitAndAssign<Rhs = Self> { +pub const trait BitAndAssign<Rhs = Self> { /// Performs the `&=` operation. /// /// # Examples @@ -793,8 +786,7 @@ bitand_assign_impl! { bool usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } message = "no implementation for `{Self} |= {Rhs}`", label = "no implementation for `{Self} |= {Rhs}`" )] -#[const_trait] -pub trait BitOrAssign<Rhs = Self> { +pub const trait BitOrAssign<Rhs = Self> { /// Performs the `|=` operation. /// /// # Examples @@ -869,8 +861,7 @@ bitor_assign_impl! { bool usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } message = "no implementation for `{Self} ^= {Rhs}`", label = "no implementation for `{Self} ^= {Rhs}`" )] -#[const_trait] -pub trait BitXorAssign<Rhs = Self> { +pub const trait BitXorAssign<Rhs = Self> { /// Performs the `^=` operation. /// /// # Examples @@ -943,8 +934,7 @@ bitxor_assign_impl! { bool usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } message = "no implementation for `{Self} <<= {Rhs}`", label = "no implementation for `{Self} <<= {Rhs}`" )] -#[const_trait] -pub trait ShlAssign<Rhs = Self> { +pub const trait ShlAssign<Rhs = Self> { /// Performs the `<<=` operation. /// /// # Examples @@ -1030,8 +1020,7 @@ shl_assign_impl_all! { u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize } message = "no implementation for `{Self} >>= {Rhs}`", label = "no implementation for `{Self} >>= {Rhs}`" )] -#[const_trait] -pub trait ShrAssign<Rhs = Self> { +pub const trait ShrAssign<Rhs = Self> { /// Performs the `>>=` operation. /// /// # Examples diff --git a/library/core/src/ops/control_flow.rs b/library/core/src/ops/control_flow.rs index 7489a8bb6e7..73b74d53323 100644 --- a/library/core/src/ops/control_flow.rs +++ b/library/core/src/ops/control_flow.rs @@ -99,7 +99,8 @@ pub enum ControlFlow<B, C = ()> { } #[unstable(feature = "try_trait_v2", issue = "84277", old_name = "try_trait")] -impl<B, C> ops::Try for ControlFlow<B, C> { +#[rustc_const_unstable(feature = "const_try", issue = "74935")] +impl<B, C> const ops::Try for ControlFlow<B, C> { type Output = C; type Residual = ControlFlow<B, convert::Infallible>; @@ -118,9 +119,10 @@ impl<B, C> ops::Try for ControlFlow<B, C> { } #[unstable(feature = "try_trait_v2", issue = "84277", old_name = "try_trait")] +#[rustc_const_unstable(feature = "const_try", issue = "74935")] // Note: manually specifying the residual type instead of using the default to work around // https://github.com/rust-lang/rust/issues/99940 -impl<B, C> ops::FromResidual<ControlFlow<B, convert::Infallible>> for ControlFlow<B, C> { +impl<B, C> const ops::FromResidual<ControlFlow<B, convert::Infallible>> for ControlFlow<B, C> { #[inline] fn from_residual(residual: ControlFlow<B, convert::Infallible>) -> Self { match residual { diff --git a/library/core/src/ops/deref.rs b/library/core/src/ops/deref.rs index 5f68c1f55c2..305861ea7b6 100644 --- a/library/core/src/ops/deref.rs +++ b/library/core/src/ops/deref.rs @@ -135,9 +135,8 @@ use crate::marker::PointeeSized; #[doc(alias = "&*")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "Deref"] -#[const_trait] -#[rustc_const_unstable(feature = "const_deref", issue = "88955")] -pub trait Deref: PointeeSized { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +pub const trait Deref: PointeeSized { /// The resulting type after dereferencing. #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "deref_target"] @@ -152,7 +151,7 @@ pub trait Deref: PointeeSized { } #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_deref", issue = "88955")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl<T: ?Sized> const Deref for &T { type Target = T; @@ -166,7 +165,7 @@ impl<T: ?Sized> const Deref for &T { impl<T: ?Sized> !DerefMut for &T {} #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_deref", issue = "88955")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl<T: ?Sized> const Deref for &mut T { type Target = T; @@ -267,9 +266,8 @@ impl<T: ?Sized> const Deref for &mut T { #[lang = "deref_mut"] #[doc(alias = "*")] #[stable(feature = "rust1", since = "1.0.0")] -#[const_trait] -#[rustc_const_unstable(feature = "const_deref", issue = "88955")] -pub trait DerefMut: [const] Deref + PointeeSized { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +pub const trait DerefMut: [const] Deref + PointeeSized { /// Mutably dereferences the value. #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "deref_mut_method"] @@ -277,7 +275,7 @@ pub trait DerefMut: [const] Deref + PointeeSized { } #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_deref", issue = "88955")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl<T: ?Sized> const DerefMut for &mut T { fn deref_mut(&mut self) -> &mut T { self diff --git a/library/core/src/ops/drop.rs b/library/core/src/ops/drop.rs index bbef7023207..7125bf54701 100644 --- a/library/core/src/ops/drop.rs +++ b/library/core/src/ops/drop.rs @@ -203,9 +203,8 @@ /// [nomicon]: ../../nomicon/phantom-data.html#an-exception-the-special-case-of-the-standard-library-and-its-unstable-may_dangle #[lang = "drop"] #[stable(feature = "rust1", since = "1.0.0")] -#[const_trait] #[rustc_const_unstable(feature = "const_destruct", issue = "133214")] -pub trait Drop { +pub const trait Drop { /// Executes the destructor for this type. /// /// This method is called implicitly when the value goes out of scope, diff --git a/library/core/src/ops/function.rs b/library/core/src/ops/function.rs index ad46e52a475..479368ba8f8 100644 --- a/library/core/src/ops/function.rs +++ b/library/core/src/ops/function.rs @@ -72,9 +72,8 @@ use crate::marker::Tuple; )] #[fundamental] // so that regex can rely that `&str: !FnMut` #[must_use = "closures are lazy and do nothing unless called"] -#[const_trait] #[rustc_const_unstable(feature = "const_trait_impl", issue = "143874")] -pub trait Fn<Args: Tuple>: FnMut<Args> { +pub const trait Fn<Args: Tuple>: FnMut<Args> { /// Performs the call operation. #[unstable(feature = "fn_traits", issue = "29625")] extern "rust-call" fn call(&self, args: Args) -> Self::Output; @@ -160,9 +159,8 @@ pub trait Fn<Args: Tuple>: FnMut<Args> { )] #[fundamental] // so that regex can rely that `&str: !FnMut` #[must_use = "closures are lazy and do nothing unless called"] -#[const_trait] #[rustc_const_unstable(feature = "const_trait_impl", issue = "143874")] -pub trait FnMut<Args: Tuple>: FnOnce<Args> { +pub const trait FnMut<Args: Tuple>: FnOnce<Args> { /// Performs the call operation. #[unstable(feature = "fn_traits", issue = "29625")] extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output; @@ -240,9 +238,8 @@ pub trait FnMut<Args: Tuple>: FnOnce<Args> { )] #[fundamental] // so that regex can rely that `&str: !FnMut` #[must_use = "closures are lazy and do nothing unless called"] -#[const_trait] #[rustc_const_unstable(feature = "const_trait_impl", issue = "143874")] -pub trait FnOnce<Args: Tuple> { +pub const trait FnOnce<Args: Tuple> { /// The returned type after the call operator is used. #[lang = "fn_once_output"] #[stable(feature = "fn_once_output", since = "1.12.0")] diff --git a/library/core/src/ops/index.rs b/library/core/src/ops/index.rs index 1aed2fb4742..2c62a3930c2 100644 --- a/library/core/src/ops/index.rs +++ b/library/core/src/ops/index.rs @@ -55,9 +55,8 @@ #[doc(alias = "]")] #[doc(alias = "[")] #[doc(alias = "[]")] -#[const_trait] #[rustc_const_unstable(feature = "const_index", issue = "143775")] -pub trait Index<Idx: ?Sized> { +pub const trait Index<Idx: ?Sized> { /// The returned type after indexing. #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "IndexOutput"] @@ -168,8 +167,7 @@ see chapter in The Book <https://doc.rust-lang.org/book/ch08-02-strings.html#ind #[doc(alias = "]")] #[doc(alias = "[]")] #[rustc_const_unstable(feature = "const_index", issue = "143775")] -#[const_trait] -pub trait IndexMut<Idx: ?Sized>: [const] Index<Idx> { +pub const trait IndexMut<Idx: ?Sized>: [const] Index<Idx> { /// Performs the mutable indexing (`container[index]`) operation. /// /// # Panics diff --git a/library/core/src/ops/range.rs b/library/core/src/ops/range.rs index c0a27775694..58a9431bd84 100644 --- a/library/core/src/ops/range.rs +++ b/library/core/src/ops/range.rs @@ -836,6 +836,7 @@ pub trait RangeBounds<T: ?Sized> { /// assert!(!(0.0..1.0).contains(&f32::NAN)); /// assert!(!(0.0..f32::NAN).contains(&0.5)); /// assert!(!(f32::NAN..1.0).contains(&0.5)); + /// ``` #[inline] #[stable(feature = "range_contains", since = "1.35.0")] fn contains<U>(&self, item: &U) -> bool diff --git a/library/core/src/ops/try_trait.rs b/library/core/src/ops/try_trait.rs index 76bf438878f..e1f2ebcf4c2 100644 --- a/library/core/src/ops/try_trait.rs +++ b/library/core/src/ops/try_trait.rs @@ -128,9 +128,8 @@ use crate::ops::ControlFlow; )] #[doc(alias = "?")] #[lang = "Try"] -#[const_trait] #[rustc_const_unstable(feature = "const_try", issue = "74935")] -pub trait Try: [const] FromResidual { +pub const trait Try: [const] FromResidual { /// The type of the value produced by `?` when *not* short-circuiting. #[unstable(feature = "try_trait_v2", issue = "84277", old_name = "try_trait")] type Output; @@ -306,9 +305,8 @@ pub trait Try: [const] FromResidual { )] #[rustc_diagnostic_item = "FromResidual"] #[unstable(feature = "try_trait_v2", issue = "84277", old_name = "try_trait")] -#[const_trait] #[rustc_const_unstable(feature = "const_try", issue = "74935")] -pub trait FromResidual<R = <Self as Try>::Residual> { +pub const trait FromResidual<R = <Self as Try>::Residual> { /// Constructs the type from a compatible `Residual` type. /// /// This should be implemented consistently with the `branch` method such @@ -361,9 +359,8 @@ where /// and in the other direction, /// `<Result<Infallible, E> as Residual<T>>::TryType = Result<T, E>`. #[unstable(feature = "try_trait_v2_residual", issue = "91285")] -#[const_trait] #[rustc_const_unstable(feature = "const_try", issue = "74935")] -pub trait Residual<O> { +pub const trait Residual<O> { /// The "return" type of this meta-function. #[unstable(feature = "try_trait_v2_residual", issue = "91285")] type TryType: Try<Output = O, Residual = Self>; diff --git a/library/core/src/option.rs b/library/core/src/option.rs index fa09409b6da..198636c67d0 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -1379,11 +1379,12 @@ impl<T> Option<T> { /// ``` #[inline] #[stable(feature = "option_deref", since = "1.40.0")] - pub fn as_deref(&self) -> Option<&T::Target> + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] + pub const fn as_deref(&self) -> Option<&T::Target> where - T: Deref, + T: [const] Deref, { - self.as_ref().map(|t| t.deref()) + self.as_ref().map(Deref::deref) } /// Converts from `Option<T>` (or `&mut Option<T>`) to `Option<&mut T::Target>`. @@ -1402,11 +1403,12 @@ impl<T> Option<T> { /// ``` #[inline] #[stable(feature = "option_deref", since = "1.40.0")] - pub fn as_deref_mut(&mut self) -> Option<&mut T::Target> + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] + pub const fn as_deref_mut(&mut self) -> Option<&mut T::Target> where - T: DerefMut, + T: [const] DerefMut, { - self.as_mut().map(|t| t.deref_mut()) + self.as_mut().map(DerefMut::deref_mut) } ///////////////////////////////////////////////////////////////////////// @@ -1638,7 +1640,7 @@ impl<T> Option<T> { pub const fn or_else<F>(self, f: F) -> Option<T> where F: [const] FnOnce() -> Option<T> + [const] Destruct, - //FIXME(const_hack): this `T: ~const Destruct` is unnecessary, but even precise live drops can't tell + //FIXME(const_hack): this `T: [const] Destruct` is unnecessary, but even precise live drops can't tell // no value of type `T` gets dropped here T: [const] Destruct, { @@ -2180,10 +2182,10 @@ const fn expect_failed(msg: &str) -> ! { ///////////////////////////////////////////////////////////////////////////// #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_clone", issue = "142757")] impl<T> const Clone for Option<T> where - // FIXME(const_hack): the T: ~const Destruct should be inferred from the Self: ~const Destruct in clone_from. + // FIXME(const_hack): the T: [const] Destruct should be inferred from the Self: [const] Destruct in clone_from. // See https://github.com/rust-lang/rust/issues/144207 T: [const] Clone + [const] Destruct, { @@ -2269,7 +2271,7 @@ impl<'a, T> IntoIterator for &'a mut Option<T> { } #[stable(since = "1.12.0", feature = "option_from")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl<T> const From<T> for Option<T> { /// Moves `val` into a new [`Some`]. /// @@ -2286,7 +2288,7 @@ impl<T> const From<T> for Option<T> { } #[stable(feature = "option_ref_from_ref_option", since = "1.30.0")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl<'a, T> const From<&'a Option<T>> for Option<&'a T> { /// Converts from `&Option<T>` to `Option<&T>`. /// @@ -2314,7 +2316,7 @@ impl<'a, T> const From<&'a Option<T>> for Option<&'a T> { } #[stable(feature = "option_ref_from_ref_option", since = "1.30.0")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl<'a, T> const From<&'a mut Option<T>> for Option<&'a mut T> { /// Converts from `&mut Option<T>` to `Option<&mut T>` /// diff --git a/library/core/src/panic/unwind_safe.rs b/library/core/src/panic/unwind_safe.rs index a60f0799c0e..722af551038 100644 --- a/library/core/src/panic/unwind_safe.rs +++ b/library/core/src/panic/unwind_safe.rs @@ -248,7 +248,8 @@ impl RefUnwindSafe for crate::sync::atomic::AtomicBool {} impl<T> RefUnwindSafe for crate::sync::atomic::AtomicPtr<T> {} #[stable(feature = "catch_unwind", since = "1.9.0")] -impl<T> Deref for AssertUnwindSafe<T> { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl<T> const Deref for AssertUnwindSafe<T> { type Target = T; fn deref(&self) -> &T { @@ -257,7 +258,8 @@ impl<T> Deref for AssertUnwindSafe<T> { } #[stable(feature = "catch_unwind", since = "1.9.0")] -impl<T> DerefMut for AssertUnwindSafe<T> { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl<T> const DerefMut for AssertUnwindSafe<T> { fn deref_mut(&mut self) -> &mut T { &mut self.0 } diff --git a/library/core/src/pat.rs b/library/core/src/pat.rs index 91d015b1bc5..a13eea3fb58 100644 --- a/library/core/src/pat.rs +++ b/library/core/src/pat.rs @@ -18,12 +18,11 @@ macro_rules! pattern_type { /// used right now to simplify ast lowering of pattern type ranges. #[unstable(feature = "pattern_type_range_trait", issue = "123646")] #[rustc_const_unstable(feature = "pattern_type_range_trait", issue = "123646")] -#[const_trait] #[diagnostic::on_unimplemented( message = "`{Self}` is not a valid base type for range patterns", label = "only integer types and `char` are supported" )] -pub trait RangePattern { +pub const trait RangePattern { /// Trait version of the inherent `MIN` assoc const. #[lang = "RangeMin"] const MIN: Self; diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 14bf7ba9015..535830f2e74 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -1359,7 +1359,11 @@ impl<Ptr: Deref> Pin<Ptr> { /// ruled out by the contract of `Pin::new_unchecked`. #[stable(feature = "pin", since = "1.33.0")] #[inline(always)] - pub fn as_ref(&self) -> Pin<&Ptr::Target> { + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] + pub const fn as_ref(&self) -> Pin<&Ptr::Target> + where + Ptr: [const] Deref, + { // SAFETY: see documentation on this function unsafe { Pin::new_unchecked(&*self.pointer) } } @@ -1403,7 +1407,11 @@ impl<Ptr: DerefMut> Pin<Ptr> { /// ``` #[stable(feature = "pin", since = "1.33.0")] #[inline(always)] - pub fn as_mut(&mut self) -> Pin<&mut Ptr::Target> { + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] + pub const fn as_mut(&mut self) -> Pin<&mut Ptr::Target> + where + Ptr: [const] DerefMut, + { // SAFETY: see documentation on this function unsafe { Pin::new_unchecked(&mut *self.pointer) } } @@ -1418,7 +1426,11 @@ impl<Ptr: DerefMut> Pin<Ptr> { #[stable(feature = "pin_deref_mut", since = "1.84.0")] #[must_use = "`self` will be dropped if the result is not used"] #[inline(always)] - pub fn as_deref_mut(self: Pin<&mut Self>) -> Pin<&mut Ptr::Target> { + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] + pub const fn as_deref_mut(self: Pin<&mut Self>) -> Pin<&mut Ptr::Target> + where + Ptr: [const] DerefMut, + { // SAFETY: What we're asserting here is that going from // // Pin<&mut Pin<Ptr>> @@ -1669,7 +1681,8 @@ impl<T: ?Sized> Pin<&'static mut T> { } #[stable(feature = "pin", since = "1.33.0")] -impl<Ptr: Deref> Deref for Pin<Ptr> { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl<Ptr: [const] Deref> const Deref for Pin<Ptr> { type Target = Ptr::Target; fn deref(&self) -> &Ptr::Target { Pin::get_ref(Pin::as_ref(self)) @@ -1677,7 +1690,8 @@ impl<Ptr: Deref> Deref for Pin<Ptr> { } #[stable(feature = "pin", since = "1.33.0")] -impl<Ptr: DerefMut<Target: Unpin>> DerefMut for Pin<Ptr> { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl<Ptr: [const] DerefMut<Target: Unpin>> const DerefMut for Pin<Ptr> { fn deref_mut(&mut self) -> &mut Ptr::Target { Pin::get_mut(Pin::as_mut(self)) } diff --git a/library/core/src/pin/unsafe_pinned.rs b/library/core/src/pin/unsafe_pinned.rs index ede6e0d6106..ae03809b458 100644 --- a/library/core/src/pin/unsafe_pinned.rs +++ b/library/core/src/pin/unsafe_pinned.rs @@ -148,7 +148,8 @@ impl<T: Default> Default for UnsafePinned<T> { } #[unstable(feature = "unsafe_pinned", issue = "125735")] -impl<T> From<T> for UnsafePinned<T> { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl<T> const From<T> for UnsafePinned<T> { /// Creates a new `UnsafePinned<T>` containing the given value. fn from(value: T) -> Self { UnsafePinned::new(value) diff --git a/library/core/src/ptr/alignment.rs b/library/core/src/ptr/alignment.rs index 402634e49b3..bc7d3a1de71 100644 --- a/library/core/src/ptr/alignment.rs +++ b/library/core/src/ptr/alignment.rs @@ -171,7 +171,8 @@ impl fmt::Debug for Alignment { } #[unstable(feature = "ptr_alignment_type", issue = "102070")] -impl TryFrom<NonZero<usize>> for Alignment { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl const TryFrom<NonZero<usize>> for Alignment { type Error = num::TryFromIntError; #[inline] @@ -181,7 +182,8 @@ impl TryFrom<NonZero<usize>> for Alignment { } #[unstable(feature = "ptr_alignment_type", issue = "102070")] -impl TryFrom<usize> for Alignment { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl const TryFrom<usize> for Alignment { type Error = num::TryFromIntError; #[inline] @@ -191,7 +193,7 @@ impl TryFrom<usize> for Alignment { } #[unstable(feature = "ptr_alignment_type", issue = "102070")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const From<Alignment> for NonZero<usize> { #[inline] fn from(align: Alignment) -> NonZero<usize> { @@ -200,7 +202,7 @@ impl const From<Alignment> for NonZero<usize> { } #[unstable(feature = "ptr_alignment_type", issue = "102070")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const From<Alignment> for usize { #[inline] fn from(align: Alignment) -> usize { diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 3fe4b08d459..ce6eee4f911 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -106,6 +106,7 @@ impl<T: PointeeSized> *mut T { /// /// // This dereference is UB. The pointer only has provenance for `x` but points to `y`. /// println!("{:?}", unsafe { &*bad }); + /// ``` #[unstable(feature = "set_ptr_value", issue = "75091")] #[must_use = "returns a new pointer rather than modifying its argument"] #[inline] diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 117eb18826e..10f83120428 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -1711,7 +1711,8 @@ impl<T: PointeeSized> hash::Hash for NonNull<T> { } #[unstable(feature = "ptr_internals", issue = "none")] -impl<T: PointeeSized> From<Unique<T>> for NonNull<T> { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl<T: PointeeSized> const From<Unique<T>> for NonNull<T> { #[inline] fn from(unique: Unique<T>) -> Self { unique.as_non_null_ptr() @@ -1719,7 +1720,8 @@ impl<T: PointeeSized> From<Unique<T>> for NonNull<T> { } #[stable(feature = "nonnull", since = "1.25.0")] -impl<T: PointeeSized> From<&mut T> for NonNull<T> { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl<T: PointeeSized> const From<&mut T> for NonNull<T> { /// Converts a `&mut T` to a `NonNull<T>`. /// /// This conversion is safe and infallible since references cannot be null. @@ -1730,7 +1732,8 @@ impl<T: PointeeSized> From<&mut T> for NonNull<T> { } #[stable(feature = "nonnull", since = "1.25.0")] -impl<T: PointeeSized> From<&T> for NonNull<T> { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl<T: PointeeSized> const From<&T> for NonNull<T> { /// Converts a `&T` to a `NonNull<T>`. /// /// This conversion is safe and infallible since references cannot be null. diff --git a/library/core/src/ptr/unique.rs b/library/core/src/ptr/unique.rs index 4302c1b1e44..cdc8b6cc936 100644 --- a/library/core/src/ptr/unique.rs +++ b/library/core/src/ptr/unique.rs @@ -189,7 +189,8 @@ impl<T: PointeeSized> fmt::Pointer for Unique<T> { } #[unstable(feature = "ptr_internals", issue = "none")] -impl<T: PointeeSized> From<&mut T> for Unique<T> { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl<T: PointeeSized> const From<&mut T> for Unique<T> { /// Converts a `&mut T` to a `Unique<T>`. /// /// This conversion is infallible since references cannot be null. @@ -200,7 +201,8 @@ impl<T: PointeeSized> From<&mut T> for Unique<T> { } #[unstable(feature = "ptr_internals", issue = "none")] -impl<T: PointeeSized> From<NonNull<T>> for Unique<T> { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl<T: PointeeSized> const From<NonNull<T>> for Unique<T> { /// Converts a `NonNull<T>` to a `Unique<T>`. /// /// This conversion is infallible since `NonNull` cannot be null. diff --git a/library/core/src/range.rs b/library/core/src/range.rs index 7158fa0fcf0..332ae51d848 100644 --- a/library/core/src/range.rs +++ b/library/core/src/range.rs @@ -192,7 +192,7 @@ impl<T> IntoBounds<T> for Range<T> { } #[unstable(feature = "new_range_api", issue = "125687")] -#[rustc_const_unstable(feature = "const_index", issue = "143775")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl<T> const From<Range<T>> for legacy::Range<T> { #[inline] fn from(value: Range<T>) -> Self { @@ -201,7 +201,7 @@ impl<T> const From<Range<T>> for legacy::Range<T> { } #[unstable(feature = "new_range_api", issue = "125687")] -#[rustc_const_unstable(feature = "const_index", issue = "143775")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl<T> const From<legacy::Range<T>> for Range<T> { #[inline] fn from(value: legacy::Range<T>) -> Self { @@ -377,7 +377,7 @@ impl<T> IntoBounds<T> for RangeInclusive<T> { } #[unstable(feature = "new_range_api", issue = "125687")] -#[rustc_const_unstable(feature = "const_index", issue = "143775")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl<T> const From<RangeInclusive<T>> for legacy::RangeInclusive<T> { #[inline] fn from(value: RangeInclusive<T>) -> Self { @@ -385,7 +385,8 @@ impl<T> const From<RangeInclusive<T>> for legacy::RangeInclusive<T> { } } #[unstable(feature = "new_range_api", issue = "125687")] -impl<T> From<legacy::RangeInclusive<T>> for RangeInclusive<T> { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl<T> const From<legacy::RangeInclusive<T>> for RangeInclusive<T> { #[inline] fn from(value: legacy::RangeInclusive<T>) -> Self { assert!( diff --git a/library/core/src/result.rs b/library/core/src/result.rs index 6148bdb866a..7dffab9b316 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -1034,11 +1034,12 @@ impl<T, E> Result<T, E> { /// ``` #[inline] #[stable(feature = "inner_deref", since = "1.47.0")] - pub fn as_deref(&self) -> Result<&T::Target, &E> + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] + pub const fn as_deref(&self) -> Result<&T::Target, &E> where - T: Deref, + T: [const] Deref, { - self.as_ref().map(|t| t.deref()) + self.as_ref().map(Deref::deref) } /// Converts from `Result<T, E>` (or `&mut Result<T, E>`) to `Result<&mut <T as DerefMut>::Target, &mut E>`. @@ -1061,11 +1062,12 @@ impl<T, E> Result<T, E> { /// ``` #[inline] #[stable(feature = "inner_deref", since = "1.47.0")] - pub fn as_deref_mut(&mut self) -> Result<&mut T::Target, &mut E> + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] + pub const fn as_deref_mut(&mut self) -> Result<&mut T::Target, &mut E> where - T: DerefMut, + T: [const] DerefMut, { - self.as_mut().map(|t| t.deref_mut()) + self.as_mut().map(DerefMut::deref_mut) } ///////////////////////////////////////////////////////////////////////// @@ -1347,7 +1349,7 @@ impl<T, E> Result<T, E> { #[unstable(feature = "unwrap_infallible", reason = "newly added", issue = "61695")] #[inline] #[rustc_allow_const_fn_unstable(const_precise_live_drops)] - #[rustc_const_unstable(feature = "const_try", issue = "74935")] + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] pub const fn into_ok(self) -> T where E: [const] Into<!>, @@ -1384,7 +1386,7 @@ impl<T, E> Result<T, E> { #[unstable(feature = "unwrap_infallible", reason = "newly added", issue = "61695")] #[inline] #[rustc_allow_const_fn_unstable(const_precise_live_drops)] - #[rustc_const_unstable(feature = "const_try", issue = "74935")] + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] pub const fn into_err(self) -> E where T: [const] Into<!>, diff --git a/library/core/src/slice/cmp.rs b/library/core/src/slice/cmp.rs index 68bd12aa7bf..0d801306984 100644 --- a/library/core/src/slice/cmp.rs +++ b/library/core/src/slice/cmp.rs @@ -95,9 +95,8 @@ impl<T: PartialOrd> PartialOrd for [T] { #[doc(hidden)] // intermediate trait for specialization of slice's PartialEq -#[const_trait] #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] -trait SlicePartialEq<B> { +const trait SlicePartialEq<B> { fn equal(&self, other: &[B]) -> bool; fn not_equal(&self, other: &[B]) -> bool { diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index 98091e9fe83..8e1bc0bae70 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -151,7 +151,7 @@ mod private_slice_index { message = "the type `{T}` cannot be indexed by `{Self}`", label = "slice indices are of type `usize` or ranges of `usize`" )] -#[const_trait] +#[const_trait] // FIXME(const_trait_impl): Migrate to `const unsafe trait` once #146122 is fixed. #[rustc_const_unstable(feature = "const_index", issue = "143775")] pub unsafe trait SliceIndex<T: ?Sized>: private_slice_index::Sealed { /// The output type returned by methods. diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index f1db385e212..04fdaa8143e 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -3078,7 +3078,8 @@ impl str { } #[stable(feature = "rust1", since = "1.0.0")] -impl AsRef<[u8]> for str { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl const AsRef<[u8]> for str { #[inline] fn as_ref(&self) -> &[u8] { self.as_bytes() diff --git a/library/core/src/str/traits.rs b/library/core/src/str/traits.rs index dc88f35eca7..4f228edf78e 100644 --- a/library/core/src/str/traits.rs +++ b/library/core/src/str/traits.rs @@ -825,9 +825,8 @@ unsafe impl const SliceIndex<str> for ops::RangeToInclusive<usize> { /// assert!(Point::from_str("(1 2)").is_err()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] -#[const_trait] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] -pub trait FromStr: Sized { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +pub const trait FromStr: Sized { /// The associated error which can be returned from parsing. #[stable(feature = "rust1", since = "1.0.0")] type Err; diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 7bd68bcd0bc..1b4a54b1b7a 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -2516,7 +2516,7 @@ impl<T> AtomicPtr<T> { #[cfg(target_has_atomic_load_store = "8")] #[stable(feature = "atomic_bool_from", since = "1.24.0")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const From<bool> for AtomicBool { /// Converts a `bool` into an `AtomicBool`. /// @@ -2535,7 +2535,8 @@ impl const From<bool> for AtomicBool { #[cfg(target_has_atomic_load_store = "ptr")] #[stable(feature = "atomic_from", since = "1.23.0")] -impl<T> From<*mut T> for AtomicPtr<T> { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl<T> const From<*mut T> for AtomicPtr<T> { /// Converts a `*mut T` into an `AtomicPtr<T>`. #[inline] fn from(p: *mut T) -> Self { @@ -2614,7 +2615,7 @@ macro_rules! atomic_int { } #[$stable_from] - #[rustc_const_unstable(feature = "const_try", issue = "74935")] + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const From<$int_type> for $atomic_type { #[doc = concat!("Converts an `", stringify!($int_type), "` into an `", stringify!($atomic_type), "`.")] #[inline] diff --git a/library/core/src/sync/exclusive.rs b/library/core/src/sync/exclusive.rs index 340b0b79e40..cf086bf4f50 100644 --- a/library/core/src/sync/exclusive.rs +++ b/library/core/src/sync/exclusive.rs @@ -163,7 +163,8 @@ impl<T: ?Sized> Exclusive<T> { } #[unstable(feature = "exclusive_wrapper", issue = "98407")] -impl<T> From<T> for Exclusive<T> { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl<T> const From<T> for Exclusive<T> { #[inline] fn from(t: T) -> Self { Self::new(t) diff --git a/library/core/src/task/poll.rs b/library/core/src/task/poll.rs index 59ffe7ad49c..380abac0ae9 100644 --- a/library/core/src/task/poll.rs +++ b/library/core/src/task/poll.rs @@ -215,7 +215,8 @@ impl<T, E> Poll<Option<Result<T, E>>> { } #[stable(feature = "futures_api", since = "1.36.0")] -impl<T> From<T> for Poll<T> { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl<T> const From<T> for Poll<T> { /// Moves the value into a [`Poll::Ready`] to make a `Poll<T>`. /// /// # Example diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index bb7efe582f7..97eb9ec7dc5 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -901,7 +901,8 @@ impl Clone for LocalWaker { } #[unstable(feature = "local_waker", issue = "118959")] -impl AsRef<LocalWaker> for Waker { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl const AsRef<LocalWaker> for Waker { fn as_ref(&self) -> &LocalWaker { // SAFETY: LocalWaker is just Waker without thread safety unsafe { transmute(self) } diff --git a/library/core/src/tuple.rs b/library/core/src/tuple.rs index 23a0a6877df..2cdee1803a9 100644 --- a/library/core/src/tuple.rs +++ b/library/core/src/tuple.rs @@ -133,6 +133,7 @@ macro_rules! tuple_impls { maybe_tuple_doc! { $($T)+ @ #[stable(feature = "array_tuple_conv", since = "1.71.0")] + // can't do const From due to https://github.com/rust-lang/rust/issues/144280 impl<T> From<[T; ${count($T)}]> for ($(${ignore($T)} T,)+) { #[inline] #[allow(non_snake_case)] @@ -146,6 +147,7 @@ macro_rules! tuple_impls { maybe_tuple_doc! { $($T)+ @ #[stable(feature = "array_tuple_conv", since = "1.71.0")] + // can't do const From due to https://github.com/rust-lang/rust/issues/144280 impl<T> From<($(${ignore($T)} T,)+)> for [T; ${count($T)}] { #[inline] #[allow(non_snake_case)] diff --git a/library/core/src/unicode/unicode_data.rs b/library/core/src/unicode/unicode_data.rs index 55f64f1e96e..2f53de183f6 100644 --- a/library/core/src/unicode/unicode_data.rs +++ b/library/core/src/unicode/unicode_data.rs @@ -1,4 +1,15 @@ -///! This file is generated by `./x run src/tools/unicode-table-generator`; do not edit manually! +//! This file is generated by `./x run src/tools/unicode-table-generator`; do not edit manually! +// Alphabetic : 1723 bytes, 142707 codepoints in 755 ranges (U+0000AA - U+0323B0) using skiplist +// Case_Ignorable : 1043 bytes, 2744 codepoints in 447 ranges (U+0000A8 - U+0E01F0) using skiplist +// Cased : 403 bytes, 4526 codepoints in 157 ranges (U+0000AA - U+01F18A) using skiplist +// Grapheme_Extend : 887 bytes, 2193 codepoints in 375 ranges (U+000300 - U+0E01F0) using skiplist +// Lowercase : 933 bytes, 2543 codepoints in 674 ranges (U+0000AA - U+01E944) using bitset +// N : 455 bytes, 1901 codepoints in 143 ranges (U+0000B2 - U+01FBFA) using skiplist +// Uppercase : 797 bytes, 1952 codepoints in 655 ranges (U+0000C0 - U+01F18A) using bitset +// White_Space : 256 bytes, 19 codepoints in 8 ranges (U+000085 - U+003001) using cascading +// to_lower : 11484 bytes +// to_upper : 13432 bytes +// Total : 31413 bytes #[inline(always)] const fn bitset_search< @@ -136,93 +147,100 @@ pub mod alphabetic { use super::ShortOffsetRunHeader; static SHORT_OFFSET_RUNS: [ShortOffsetRunHeader; 53] = [ - ShortOffsetRunHeader::new(0, 706), ShortOffsetRunHeader::new(16, 4681), - ShortOffsetRunHeader::new(418, 5741), ShortOffsetRunHeader::new(456, 7958), - ShortOffsetRunHeader::new(556, 9398), ShortOffsetRunHeader::new(627, 11264), - ShortOffsetRunHeader::new(629, 12293), ShortOffsetRunHeader::new(667, 13312), - ShortOffsetRunHeader::new(691, 19904), ShortOffsetRunHeader::new(692, 42125), - ShortOffsetRunHeader::new(694, 42509), ShortOffsetRunHeader::new(698, 55204), - ShortOffsetRunHeader::new(788, 63744), ShortOffsetRunHeader::new(793, 64110), - ShortOffsetRunHeader::new(794, 64830), ShortOffsetRunHeader::new(816, 66176), - ShortOffsetRunHeader::new(857, 67383), ShortOffsetRunHeader::new(904, 73440), - ShortOffsetRunHeader::new(1221, 74650), ShortOffsetRunHeader::new(1232, 77712), - ShortOffsetRunHeader::new(1237, 78896), ShortOffsetRunHeader::new(1240, 82939), - ShortOffsetRunHeader::new(1244, 83527), ShortOffsetRunHeader::new(1246, 90368), - ShortOffsetRunHeader::new(1247, 92160), ShortOffsetRunHeader::new(1249, 92729), - ShortOffsetRunHeader::new(1250, 93504), ShortOffsetRunHeader::new(1265, 100344), - ShortOffsetRunHeader::new(1282, 101590), ShortOffsetRunHeader::new(1284, 110576), - ShortOffsetRunHeader::new(1287, 110883), ShortOffsetRunHeader::new(1294, 111356), - ShortOffsetRunHeader::new(1304, 113664), ShortOffsetRunHeader::new(1305, 119808), - ShortOffsetRunHeader::new(1315, 120486), ShortOffsetRunHeader::new(1352, 122624), - ShortOffsetRunHeader::new(1375, 123536), ShortOffsetRunHeader::new(1399, 124112), - ShortOffsetRunHeader::new(1403, 124896), ShortOffsetRunHeader::new(1409, 126464), - ShortOffsetRunHeader::new(1425, 127280), ShortOffsetRunHeader::new(1491, 131072), - ShortOffsetRunHeader::new(1497, 173792), ShortOffsetRunHeader::new(1498, 177978), - ShortOffsetRunHeader::new(1500, 183970), ShortOffsetRunHeader::new(1504, 191457), - ShortOffsetRunHeader::new(1506, 192094), ShortOffsetRunHeader::new(1508, 194560), - ShortOffsetRunHeader::new(1509, 195102), ShortOffsetRunHeader::new(1510, 196608), - ShortOffsetRunHeader::new(1511, 201547), ShortOffsetRunHeader::new(1512, 205744), - ShortOffsetRunHeader::new(1514, 1319856), + ShortOffsetRunHeader::new(0, 706), ShortOffsetRunHeader::new(12, 4681), + ShortOffsetRunHeader::new(414, 5741), ShortOffsetRunHeader::new(452, 7958), + ShortOffsetRunHeader::new(552, 9398), ShortOffsetRunHeader::new(623, 11264), + ShortOffsetRunHeader::new(625, 12293), ShortOffsetRunHeader::new(663, 13312), + ShortOffsetRunHeader::new(687, 19904), ShortOffsetRunHeader::new(688, 42125), + ShortOffsetRunHeader::new(690, 42509), ShortOffsetRunHeader::new(694, 55204), + ShortOffsetRunHeader::new(784, 63744), ShortOffsetRunHeader::new(789, 64110), + ShortOffsetRunHeader::new(790, 64830), ShortOffsetRunHeader::new(812, 66176), + ShortOffsetRunHeader::new(853, 67383), ShortOffsetRunHeader::new(900, 73440), + ShortOffsetRunHeader::new(1217, 74650), ShortOffsetRunHeader::new(1228, 77712), + ShortOffsetRunHeader::new(1233, 78896), ShortOffsetRunHeader::new(1236, 82939), + ShortOffsetRunHeader::new(1240, 83527), ShortOffsetRunHeader::new(1242, 90368), + ShortOffsetRunHeader::new(1243, 92160), ShortOffsetRunHeader::new(1245, 92729), + ShortOffsetRunHeader::new(1246, 93504), ShortOffsetRunHeader::new(1261, 100344), + ShortOffsetRunHeader::new(1278, 101590), ShortOffsetRunHeader::new(1280, 110576), + ShortOffsetRunHeader::new(1283, 110883), ShortOffsetRunHeader::new(1290, 111356), + ShortOffsetRunHeader::new(1300, 113664), ShortOffsetRunHeader::new(1301, 119808), + ShortOffsetRunHeader::new(1311, 120486), ShortOffsetRunHeader::new(1348, 122624), + ShortOffsetRunHeader::new(1371, 123536), ShortOffsetRunHeader::new(1395, 124112), + ShortOffsetRunHeader::new(1399, 124896), ShortOffsetRunHeader::new(1405, 126464), + ShortOffsetRunHeader::new(1421, 127280), ShortOffsetRunHeader::new(1487, 131072), + ShortOffsetRunHeader::new(1493, 173792), ShortOffsetRunHeader::new(1494, 177978), + ShortOffsetRunHeader::new(1496, 183970), ShortOffsetRunHeader::new(1500, 191457), + ShortOffsetRunHeader::new(1502, 192094), ShortOffsetRunHeader::new(1504, 194560), + ShortOffsetRunHeader::new(1505, 195102), ShortOffsetRunHeader::new(1506, 196608), + ShortOffsetRunHeader::new(1507, 201547), ShortOffsetRunHeader::new(1508, 205744), + ShortOffsetRunHeader::new(1510, 1319856), ]; - static OFFSETS: [u8; 1515] = [ - 65, 26, 6, 26, 47, 1, 10, 1, 4, 1, 5, 23, 1, 31, 1, 0, 4, 12, 14, 5, 7, 1, 1, 1, 86, 1, 29, - 18, 1, 2, 2, 4, 1, 1, 6, 1, 1, 3, 1, 1, 1, 20, 1, 83, 1, 139, 8, 166, 1, 38, 2, 1, 6, 41, - 39, 14, 1, 1, 1, 2, 1, 2, 1, 1, 8, 27, 4, 4, 29, 11, 5, 56, 1, 7, 14, 102, 1, 8, 4, 8, 4, 3, - 10, 3, 2, 1, 16, 48, 13, 101, 24, 33, 9, 2, 4, 1, 5, 24, 2, 19, 19, 25, 7, 11, 5, 24, 1, 6, - 8, 1, 8, 42, 10, 12, 3, 7, 6, 76, 1, 16, 1, 3, 4, 15, 13, 19, 1, 8, 2, 2, 2, 22, 1, 7, 1, 1, - 3, 4, 3, 8, 2, 2, 2, 2, 1, 1, 8, 1, 4, 2, 1, 5, 12, 2, 10, 1, 4, 3, 1, 6, 4, 2, 2, 22, 1, 7, - 1, 2, 1, 2, 1, 2, 4, 5, 4, 2, 2, 2, 4, 1, 7, 4, 1, 1, 17, 6, 11, 3, 1, 9, 1, 3, 1, 22, 1, 7, - 1, 2, 1, 5, 3, 9, 1, 3, 1, 2, 3, 1, 15, 4, 21, 4, 4, 3, 1, 8, 2, 2, 2, 22, 1, 7, 1, 2, 1, 5, - 3, 8, 2, 2, 2, 2, 9, 2, 4, 2, 1, 5, 13, 1, 16, 2, 1, 6, 3, 3, 1, 4, 3, 2, 1, 1, 1, 2, 3, 2, - 3, 3, 3, 12, 4, 5, 3, 3, 1, 3, 3, 1, 6, 1, 40, 13, 1, 3, 1, 23, 1, 16, 3, 8, 1, 3, 1, 3, 8, - 2, 1, 3, 2, 1, 2, 4, 28, 4, 1, 8, 1, 3, 1, 23, 1, 10, 1, 5, 3, 8, 1, 3, 1, 3, 8, 2, 6, 2, 1, - 4, 13, 3, 12, 13, 1, 3, 1, 41, 2, 8, 1, 3, 1, 3, 1, 1, 5, 4, 7, 5, 22, 6, 1, 3, 1, 18, 3, - 24, 1, 9, 1, 1, 2, 7, 8, 6, 1, 1, 1, 8, 18, 2, 13, 58, 5, 7, 6, 1, 51, 2, 1, 1, 1, 5, 1, 24, - 1, 1, 1, 19, 1, 3, 2, 5, 1, 1, 6, 1, 14, 4, 32, 1, 63, 8, 1, 36, 4, 19, 4, 16, 1, 36, 67, - 55, 1, 1, 2, 5, 16, 64, 10, 4, 2, 38, 1, 1, 5, 1, 2, 43, 1, 0, 1, 4, 2, 7, 1, 1, 1, 4, 2, - 41, 1, 4, 2, 33, 1, 4, 2, 7, 1, 1, 1, 4, 2, 15, 1, 57, 1, 4, 2, 67, 37, 16, 16, 86, 2, 6, 3, - 0, 2, 17, 1, 26, 5, 75, 3, 11, 7, 20, 11, 21, 12, 20, 12, 13, 1, 3, 1, 2, 12, 52, 2, 19, 14, - 1, 4, 1, 67, 89, 7, 43, 5, 70, 10, 31, 1, 12, 4, 9, 23, 30, 2, 5, 11, 44, 4, 26, 54, 28, 4, - 63, 2, 20, 50, 1, 23, 2, 11, 3, 49, 52, 1, 15, 1, 8, 51, 42, 2, 4, 10, 44, 1, 11, 14, 55, - 22, 3, 10, 36, 2, 11, 5, 43, 2, 3, 41, 4, 1, 6, 1, 2, 3, 1, 5, 192, 19, 34, 11, 0, 2, 6, 2, - 38, 2, 6, 2, 8, 1, 1, 1, 1, 1, 1, 1, 31, 2, 53, 1, 7, 1, 1, 3, 3, 1, 7, 3, 4, 2, 6, 4, 13, - 5, 3, 1, 7, 116, 1, 13, 1, 16, 13, 101, 1, 4, 1, 2, 10, 1, 1, 3, 5, 6, 1, 1, 1, 1, 1, 1, 4, - 1, 11, 2, 4, 5, 5, 4, 1, 17, 41, 0, 52, 0, 229, 6, 4, 3, 2, 12, 38, 1, 1, 5, 1, 2, 56, 7, 1, - 16, 23, 9, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 32, 47, 1, 0, 3, 25, 9, 7, 5, 2, - 5, 4, 86, 6, 3, 1, 90, 1, 4, 5, 43, 1, 94, 17, 32, 48, 16, 0, 0, 64, 0, 67, 46, 2, 0, 3, 16, - 10, 2, 20, 47, 5, 8, 3, 113, 39, 9, 2, 103, 2, 67, 2, 2, 1, 1, 1, 8, 21, 20, 1, 33, 24, 52, - 12, 68, 1, 1, 44, 6, 3, 1, 1, 3, 10, 33, 5, 35, 13, 29, 3, 51, 1, 12, 15, 1, 16, 16, 10, 5, - 1, 55, 9, 14, 18, 23, 3, 69, 1, 1, 1, 1, 24, 3, 2, 16, 2, 4, 11, 6, 2, 6, 2, 6, 9, 7, 1, 7, - 1, 43, 1, 14, 6, 123, 21, 0, 12, 23, 4, 49, 0, 0, 2, 106, 38, 7, 12, 5, 5, 12, 1, 13, 1, 5, - 1, 1, 1, 2, 1, 2, 1, 108, 33, 0, 18, 64, 2, 54, 40, 12, 116, 5, 1, 135, 36, 26, 6, 26, 11, - 89, 3, 6, 2, 6, 2, 6, 2, 3, 35, 12, 1, 26, 1, 19, 1, 2, 1, 15, 2, 14, 34, 123, 69, 53, 0, - 29, 3, 49, 47, 32, 13, 30, 5, 43, 5, 30, 2, 36, 4, 8, 1, 5, 42, 158, 18, 36, 4, 36, 4, 40, - 8, 52, 12, 11, 1, 15, 1, 7, 1, 2, 1, 11, 1, 15, 1, 7, 1, 2, 3, 52, 12, 0, 9, 22, 10, 8, 24, - 6, 1, 42, 1, 9, 69, 6, 2, 1, 1, 44, 1, 2, 3, 1, 2, 23, 10, 23, 9, 31, 65, 19, 1, 2, 10, 22, - 10, 26, 70, 56, 6, 2, 64, 4, 1, 2, 5, 8, 1, 3, 1, 29, 42, 29, 3, 29, 35, 8, 1, 28, 27, 54, - 10, 22, 10, 19, 13, 18, 110, 73, 55, 51, 13, 51, 13, 40, 34, 28, 3, 1, 5, 23, 250, 42, 1, 2, - 3, 2, 16, 3, 55, 1, 3, 29, 10, 1, 8, 22, 42, 18, 46, 21, 27, 23, 9, 70, 43, 5, 10, 57, 9, 1, - 13, 25, 23, 51, 17, 4, 8, 35, 3, 1, 9, 64, 1, 4, 9, 2, 10, 1, 1, 1, 35, 18, 1, 34, 2, 1, 6, - 4, 62, 7, 1, 1, 1, 4, 1, 15, 1, 10, 7, 57, 23, 4, 1, 8, 2, 2, 2, 22, 1, 7, 1, 2, 1, 5, 3, 8, - 2, 2, 2, 2, 3, 1, 6, 1, 5, 7, 28, 10, 1, 1, 2, 1, 1, 38, 1, 10, 1, 1, 2, 1, 1, 4, 1, 2, 3, - 1, 1, 1, 44, 66, 1, 3, 1, 4, 20, 3, 30, 66, 2, 2, 1, 1, 184, 54, 2, 7, 25, 6, 34, 63, 1, 1, - 3, 1, 59, 54, 2, 1, 71, 27, 2, 14, 21, 7, 185, 57, 103, 64, 31, 8, 2, 1, 2, 8, 1, 2, 1, 30, - 1, 2, 2, 2, 2, 4, 93, 8, 2, 46, 2, 6, 1, 1, 1, 2, 27, 51, 2, 10, 17, 72, 5, 1, 18, 73, 199, - 33, 31, 9, 1, 45, 1, 7, 1, 1, 49, 30, 2, 22, 1, 14, 73, 7, 1, 2, 1, 44, 3, 1, 1, 2, 1, 3, 1, - 1, 2, 2, 24, 6, 1, 2, 1, 37, 1, 2, 1, 4, 1, 1, 0, 23, 9, 17, 1, 41, 3, 3, 111, 1, 79, 0, - 102, 111, 17, 196, 0, 97, 15, 0, 17, 6, 25, 0, 5, 0, 0, 47, 0, 0, 7, 31, 17, 79, 17, 30, 18, - 48, 16, 4, 31, 21, 5, 19, 0, 45, 211, 64, 128, 75, 4, 57, 7, 17, 64, 2, 1, 1, 12, 2, 14, 0, - 8, 0, 41, 10, 0, 4, 1, 7, 1, 2, 1, 0, 15, 1, 29, 3, 2, 1, 14, 4, 8, 0, 0, 107, 5, 13, 3, 9, - 7, 10, 4, 1, 0, 85, 1, 71, 1, 2, 2, 1, 2, 2, 2, 4, 1, 12, 1, 1, 1, 7, 1, 65, 1, 4, 2, 8, 1, - 7, 1, 28, 1, 4, 1, 5, 1, 1, 3, 7, 1, 0, 2, 25, 1, 25, 1, 31, 1, 25, 1, 31, 1, 25, 1, 31, 1, - 25, 1, 31, 1, 25, 1, 8, 0, 31, 6, 6, 213, 7, 1, 17, 2, 7, 1, 2, 1, 5, 5, 62, 33, 1, 112, 45, - 10, 7, 16, 1, 0, 30, 18, 44, 0, 28, 228, 30, 2, 1, 0, 7, 1, 4, 1, 2, 1, 15, 1, 197, 59, 68, - 3, 1, 3, 1, 0, 4, 1, 27, 1, 2, 1, 1, 2, 1, 1, 10, 1, 4, 1, 1, 1, 1, 6, 1, 4, 1, 1, 1, 1, 1, - 1, 3, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 4, 1, 7, 1, 4, 1, 4, 1, 1, 1, - 10, 1, 17, 5, 3, 1, 5, 1, 17, 0, 26, 6, 26, 6, 26, 0, 0, 32, 0, 6, 222, 2, 0, 14, 0, 15, 0, - 0, 0, 0, 0, 5, 0, 0, + static OFFSETS: [u8; 1511] = [ + 170, 1, 10, 1, 4, 1, 5, 23, 1, 31, 1, 0, 4, 12, 14, 5, 7, 1, 1, 1, 86, 1, 29, 18, 1, 2, 2, + 4, 1, 1, 6, 1, 1, 3, 1, 1, 1, 20, 1, 83, 1, 139, 8, 166, 1, 38, 2, 1, 6, 41, 39, 14, 1, 1, + 1, 2, 1, 2, 1, 1, 8, 27, 4, 4, 29, 11, 5, 56, 1, 7, 14, 102, 1, 8, 4, 8, 4, 3, 10, 3, 2, 1, + 16, 48, 13, 101, 24, 33, 9, 2, 4, 1, 5, 24, 2, 19, 19, 25, 7, 11, 5, 24, 1, 6, 8, 1, 8, 42, + 10, 12, 3, 7, 6, 76, 1, 16, 1, 3, 4, 15, 13, 19, 1, 8, 2, 2, 2, 22, 1, 7, 1, 1, 3, 4, 3, 8, + 2, 2, 2, 2, 1, 1, 8, 1, 4, 2, 1, 5, 12, 2, 10, 1, 4, 3, 1, 6, 4, 2, 2, 22, 1, 7, 1, 2, 1, 2, + 1, 2, 4, 5, 4, 2, 2, 2, 4, 1, 7, 4, 1, 1, 17, 6, 11, 3, 1, 9, 1, 3, 1, 22, 1, 7, 1, 2, 1, 5, + 3, 9, 1, 3, 1, 2, 3, 1, 15, 4, 21, 4, 4, 3, 1, 8, 2, 2, 2, 22, 1, 7, 1, 2, 1, 5, 3, 8, 2, 2, + 2, 2, 9, 2, 4, 2, 1, 5, 13, 1, 16, 2, 1, 6, 3, 3, 1, 4, 3, 2, 1, 1, 1, 2, 3, 2, 3, 3, 3, 12, + 4, 5, 3, 3, 1, 3, 3, 1, 6, 1, 40, 13, 1, 3, 1, 23, 1, 16, 3, 8, 1, 3, 1, 3, 8, 2, 1, 3, 2, + 1, 2, 4, 28, 4, 1, 8, 1, 3, 1, 23, 1, 10, 1, 5, 3, 8, 1, 3, 1, 3, 8, 2, 6, 2, 1, 4, 13, 3, + 12, 13, 1, 3, 1, 41, 2, 8, 1, 3, 1, 3, 1, 1, 5, 4, 7, 5, 22, 6, 1, 3, 1, 18, 3, 24, 1, 9, 1, + 1, 2, 7, 8, 6, 1, 1, 1, 8, 18, 2, 13, 58, 5, 7, 6, 1, 51, 2, 1, 1, 1, 5, 1, 24, 1, 1, 1, 19, + 1, 3, 2, 5, 1, 1, 6, 1, 14, 4, 32, 1, 63, 8, 1, 36, 4, 19, 4, 16, 1, 36, 67, 55, 1, 1, 2, 5, + 16, 64, 10, 4, 2, 38, 1, 1, 5, 1, 2, 43, 1, 0, 1, 4, 2, 7, 1, 1, 1, 4, 2, 41, 1, 4, 2, 33, + 1, 4, 2, 7, 1, 1, 1, 4, 2, 15, 1, 57, 1, 4, 2, 67, 37, 16, 16, 86, 2, 6, 3, 0, 2, 17, 1, 26, + 5, 75, 3, 11, 7, 20, 11, 21, 12, 20, 12, 13, 1, 3, 1, 2, 12, 52, 2, 19, 14, 1, 4, 1, 67, 89, + 7, 43, 5, 70, 10, 31, 1, 12, 4, 9, 23, 30, 2, 5, 11, 44, 4, 26, 54, 28, 4, 63, 2, 20, 50, 1, + 23, 2, 11, 3, 49, 52, 1, 15, 1, 8, 51, 42, 2, 4, 10, 44, 1, 11, 14, 55, 22, 3, 10, 36, 2, + 11, 5, 43, 2, 3, 41, 4, 1, 6, 1, 2, 3, 1, 5, 192, 19, 34, 11, 0, 2, 6, 2, 38, 2, 6, 2, 8, 1, + 1, 1, 1, 1, 1, 1, 31, 2, 53, 1, 7, 1, 1, 3, 3, 1, 7, 3, 4, 2, 6, 4, 13, 5, 3, 1, 7, 116, 1, + 13, 1, 16, 13, 101, 1, 4, 1, 2, 10, 1, 1, 3, 5, 6, 1, 1, 1, 1, 1, 1, 4, 1, 11, 2, 4, 5, 5, + 4, 1, 17, 41, 0, 52, 0, 229, 6, 4, 3, 2, 12, 38, 1, 1, 5, 1, 2, 56, 7, 1, 16, 23, 9, 7, 1, + 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 32, 47, 1, 0, 3, 25, 9, 7, 5, 2, 5, 4, 86, 6, 3, + 1, 90, 1, 4, 5, 43, 1, 94, 17, 32, 48, 16, 0, 0, 64, 0, 67, 46, 2, 0, 3, 16, 10, 2, 20, 47, + 5, 8, 3, 113, 39, 9, 2, 103, 2, 67, 2, 2, 1, 1, 1, 8, 21, 20, 1, 33, 24, 52, 12, 68, 1, 1, + 44, 6, 3, 1, 1, 3, 10, 33, 5, 35, 13, 29, 3, 51, 1, 12, 15, 1, 16, 16, 10, 5, 1, 55, 9, 14, + 18, 23, 3, 69, 1, 1, 1, 1, 24, 3, 2, 16, 2, 4, 11, 6, 2, 6, 2, 6, 9, 7, 1, 7, 1, 43, 1, 14, + 6, 123, 21, 0, 12, 23, 4, 49, 0, 0, 2, 106, 38, 7, 12, 5, 5, 12, 1, 13, 1, 5, 1, 1, 1, 2, 1, + 2, 1, 108, 33, 0, 18, 64, 2, 54, 40, 12, 116, 5, 1, 135, 36, 26, 6, 26, 11, 89, 3, 6, 2, 6, + 2, 6, 2, 3, 35, 12, 1, 26, 1, 19, 1, 2, 1, 15, 2, 14, 34, 123, 69, 53, 0, 29, 3, 49, 47, 32, + 13, 30, 5, 43, 5, 30, 2, 36, 4, 8, 1, 5, 42, 158, 18, 36, 4, 36, 4, 40, 8, 52, 12, 11, 1, + 15, 1, 7, 1, 2, 1, 11, 1, 15, 1, 7, 1, 2, 3, 52, 12, 0, 9, 22, 10, 8, 24, 6, 1, 42, 1, 9, + 69, 6, 2, 1, 1, 44, 1, 2, 3, 1, 2, 23, 10, 23, 9, 31, 65, 19, 1, 2, 10, 22, 10, 26, 70, 56, + 6, 2, 64, 4, 1, 2, 5, 8, 1, 3, 1, 29, 42, 29, 3, 29, 35, 8, 1, 28, 27, 54, 10, 22, 10, 19, + 13, 18, 110, 73, 55, 51, 13, 51, 13, 40, 34, 28, 3, 1, 5, 23, 250, 42, 1, 2, 3, 2, 16, 3, + 55, 1, 3, 29, 10, 1, 8, 22, 42, 18, 46, 21, 27, 23, 9, 70, 43, 5, 10, 57, 9, 1, 13, 25, 23, + 51, 17, 4, 8, 35, 3, 1, 9, 64, 1, 4, 9, 2, 10, 1, 1, 1, 35, 18, 1, 34, 2, 1, 6, 4, 62, 7, 1, + 1, 1, 4, 1, 15, 1, 10, 7, 57, 23, 4, 1, 8, 2, 2, 2, 22, 1, 7, 1, 2, 1, 5, 3, 8, 2, 2, 2, 2, + 3, 1, 6, 1, 5, 7, 28, 10, 1, 1, 2, 1, 1, 38, 1, 10, 1, 1, 2, 1, 1, 4, 1, 2, 3, 1, 1, 1, 44, + 66, 1, 3, 1, 4, 20, 3, 30, 66, 2, 2, 1, 1, 184, 54, 2, 7, 25, 6, 34, 63, 1, 1, 3, 1, 59, 54, + 2, 1, 71, 27, 2, 14, 21, 7, 185, 57, 103, 64, 31, 8, 2, 1, 2, 8, 1, 2, 1, 30, 1, 2, 2, 2, 2, + 4, 93, 8, 2, 46, 2, 6, 1, 1, 1, 2, 27, 51, 2, 10, 17, 72, 5, 1, 18, 73, 199, 33, 31, 9, 1, + 45, 1, 7, 1, 1, 49, 30, 2, 22, 1, 14, 73, 7, 1, 2, 1, 44, 3, 1, 1, 2, 1, 3, 1, 1, 2, 2, 24, + 6, 1, 2, 1, 37, 1, 2, 1, 4, 1, 1, 0, 23, 9, 17, 1, 41, 3, 3, 111, 1, 79, 0, 102, 111, 17, + 196, 0, 97, 15, 0, 17, 6, 25, 0, 5, 0, 0, 47, 0, 0, 7, 31, 17, 79, 17, 30, 18, 48, 16, 4, + 31, 21, 5, 19, 0, 45, 211, 64, 128, 75, 4, 57, 7, 17, 64, 2, 1, 1, 12, 2, 14, 0, 8, 0, 41, + 10, 0, 4, 1, 7, 1, 2, 1, 0, 15, 1, 29, 3, 2, 1, 14, 4, 8, 0, 0, 107, 5, 13, 3, 9, 7, 10, 4, + 1, 0, 85, 1, 71, 1, 2, 2, 1, 2, 2, 2, 4, 1, 12, 1, 1, 1, 7, 1, 65, 1, 4, 2, 8, 1, 7, 1, 28, + 1, 4, 1, 5, 1, 1, 3, 7, 1, 0, 2, 25, 1, 25, 1, 31, 1, 25, 1, 31, 1, 25, 1, 31, 1, 25, 1, 31, + 1, 25, 1, 8, 0, 31, 6, 6, 213, 7, 1, 17, 2, 7, 1, 2, 1, 5, 5, 62, 33, 1, 112, 45, 10, 7, 16, + 1, 0, 30, 18, 44, 0, 28, 228, 30, 2, 1, 0, 7, 1, 4, 1, 2, 1, 15, 1, 197, 59, 68, 3, 1, 3, 1, + 0, 4, 1, 27, 1, 2, 1, 1, 2, 1, 1, 10, 1, 4, 1, 1, 1, 1, 6, 1, 4, 1, 1, 1, 1, 1, 1, 3, 1, 2, + 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 4, 1, 7, 1, 4, 1, 4, 1, 1, 1, 10, 1, 17, + 5, 3, 1, 5, 1, 17, 0, 26, 6, 26, 6, 26, 0, 0, 32, 0, 6, 222, 2, 0, 14, 0, 15, 0, 0, 0, 0, 0, + 5, 0, 0, ]; + #[inline] pub fn lookup(c: char) -> bool { + debug_assert!(!c.is_ascii()); + (c as u32) >= 0xaa && lookup_slow(c) + } + + #[inline(never)] + fn lookup_slow(c: char) -> bool { const { assert!(SHORT_OFFSET_RUNS.last().unwrap().0 > char::MAX as u32); let mut i = 0; @@ -242,62 +260,69 @@ pub mod case_ignorable { use super::ShortOffsetRunHeader; static SHORT_OFFSET_RUNS: [ShortOffsetRunHeader; 37] = [ - ShortOffsetRunHeader::new(0, 688), ShortOffsetRunHeader::new(21, 4957), - ShortOffsetRunHeader::new(273, 5906), ShortOffsetRunHeader::new(275, 8125), - ShortOffsetRunHeader::new(385, 11388), ShortOffsetRunHeader::new(419, 12293), - ShortOffsetRunHeader::new(431, 40981), ShortOffsetRunHeader::new(443, 42232), - ShortOffsetRunHeader::new(445, 42508), ShortOffsetRunHeader::new(447, 64286), - ShortOffsetRunHeader::new(543, 65024), ShortOffsetRunHeader::new(547, 66045), - ShortOffsetRunHeader::new(577, 67456), ShortOffsetRunHeader::new(583, 68097), - ShortOffsetRunHeader::new(589, 68900), ShortOffsetRunHeader::new(601, 69291), - ShortOffsetRunHeader::new(609, 71727), ShortOffsetRunHeader::new(733, 71995), - ShortOffsetRunHeader::new(737, 72752), ShortOffsetRunHeader::new(765, 73459), - ShortOffsetRunHeader::new(795, 78896), ShortOffsetRunHeader::new(807, 90398), - ShortOffsetRunHeader::new(811, 92912), ShortOffsetRunHeader::new(815, 93504), - ShortOffsetRunHeader::new(821, 94031), ShortOffsetRunHeader::new(825, 110576), - ShortOffsetRunHeader::new(833, 113821), ShortOffsetRunHeader::new(839, 118528), - ShortOffsetRunHeader::new(843, 119143), ShortOffsetRunHeader::new(847, 121344), - ShortOffsetRunHeader::new(857, 122880), ShortOffsetRunHeader::new(869, 123566), - ShortOffsetRunHeader::new(885, 124139), ShortOffsetRunHeader::new(889, 125136), - ShortOffsetRunHeader::new(893, 127995), ShortOffsetRunHeader::new(897, 917505), - ShortOffsetRunHeader::new(899, 2032112), + ShortOffsetRunHeader::new(0, 688), ShortOffsetRunHeader::new(11, 4957), + ShortOffsetRunHeader::new(263, 5906), ShortOffsetRunHeader::new(265, 8125), + ShortOffsetRunHeader::new(375, 11388), ShortOffsetRunHeader::new(409, 12293), + ShortOffsetRunHeader::new(421, 40981), ShortOffsetRunHeader::new(433, 42232), + ShortOffsetRunHeader::new(435, 42508), ShortOffsetRunHeader::new(437, 64286), + ShortOffsetRunHeader::new(533, 65024), ShortOffsetRunHeader::new(537, 66045), + ShortOffsetRunHeader::new(567, 67456), ShortOffsetRunHeader::new(573, 68097), + ShortOffsetRunHeader::new(579, 68900), ShortOffsetRunHeader::new(591, 69291), + ShortOffsetRunHeader::new(599, 71727), ShortOffsetRunHeader::new(723, 71995), + ShortOffsetRunHeader::new(727, 72752), ShortOffsetRunHeader::new(755, 73459), + ShortOffsetRunHeader::new(785, 78896), ShortOffsetRunHeader::new(797, 90398), + ShortOffsetRunHeader::new(801, 92912), ShortOffsetRunHeader::new(805, 93504), + ShortOffsetRunHeader::new(811, 94031), ShortOffsetRunHeader::new(815, 110576), + ShortOffsetRunHeader::new(823, 113821), ShortOffsetRunHeader::new(829, 118528), + ShortOffsetRunHeader::new(833, 119143), ShortOffsetRunHeader::new(837, 121344), + ShortOffsetRunHeader::new(847, 122880), ShortOffsetRunHeader::new(859, 123566), + ShortOffsetRunHeader::new(875, 124139), ShortOffsetRunHeader::new(879, 125136), + ShortOffsetRunHeader::new(883, 127995), ShortOffsetRunHeader::new(887, 917505), + ShortOffsetRunHeader::new(889, 2032112), ]; - static OFFSETS: [u8; 905] = [ - 39, 1, 6, 1, 11, 1, 35, 1, 1, 1, 71, 1, 4, 1, 1, 1, 4, 1, 2, 2, 0, 192, 4, 2, 4, 1, 9, 2, - 1, 1, 251, 7, 207, 1, 5, 1, 49, 45, 1, 1, 1, 2, 1, 2, 1, 1, 44, 1, 11, 6, 10, 11, 1, 1, 35, - 1, 10, 21, 16, 1, 101, 8, 1, 10, 1, 4, 33, 1, 1, 1, 30, 27, 91, 11, 58, 11, 4, 1, 2, 1, 24, - 24, 43, 3, 44, 1, 7, 2, 5, 9, 41, 58, 55, 1, 1, 1, 4, 8, 4, 1, 3, 7, 10, 2, 13, 1, 15, 1, - 58, 1, 4, 4, 8, 1, 20, 2, 26, 1, 2, 2, 57, 1, 4, 2, 4, 2, 2, 3, 3, 1, 30, 2, 3, 1, 11, 2, - 57, 1, 4, 5, 1, 2, 4, 1, 20, 2, 22, 6, 1, 1, 58, 1, 2, 1, 1, 4, 8, 1, 7, 2, 11, 2, 30, 1, - 61, 1, 12, 1, 50, 1, 3, 1, 55, 1, 1, 3, 5, 3, 1, 4, 7, 2, 11, 2, 29, 1, 58, 1, 2, 1, 6, 1, - 5, 2, 20, 2, 28, 2, 57, 2, 4, 4, 8, 1, 20, 2, 29, 1, 72, 1, 7, 3, 1, 1, 90, 1, 2, 7, 11, 9, - 98, 1, 2, 9, 9, 1, 1, 7, 73, 2, 27, 1, 1, 1, 1, 1, 55, 14, 1, 5, 1, 2, 5, 11, 1, 36, 9, 1, - 102, 4, 1, 6, 1, 2, 2, 2, 25, 2, 4, 3, 16, 4, 13, 1, 2, 2, 6, 1, 15, 1, 94, 1, 0, 3, 0, 3, - 29, 2, 30, 2, 30, 2, 64, 2, 1, 7, 8, 1, 2, 11, 3, 1, 5, 1, 45, 5, 51, 1, 65, 2, 34, 1, 118, - 3, 4, 2, 9, 1, 6, 3, 219, 2, 2, 1, 58, 1, 1, 7, 1, 1, 1, 1, 2, 8, 6, 10, 2, 1, 39, 1, 8, 31, - 49, 4, 48, 1, 1, 5, 1, 1, 5, 1, 40, 9, 12, 2, 32, 4, 2, 2, 1, 3, 56, 1, 1, 2, 3, 1, 1, 3, - 58, 8, 2, 2, 64, 6, 82, 3, 1, 13, 1, 7, 4, 1, 6, 1, 3, 2, 50, 63, 13, 1, 34, 101, 0, 1, 1, - 3, 11, 3, 13, 3, 13, 3, 13, 2, 12, 5, 8, 2, 10, 1, 2, 1, 2, 5, 49, 5, 1, 10, 1, 1, 13, 1, - 16, 13, 51, 33, 0, 2, 113, 3, 125, 1, 15, 1, 96, 32, 47, 1, 0, 1, 36, 4, 3, 5, 5, 1, 93, 6, - 93, 3, 0, 1, 0, 6, 0, 1, 98, 4, 1, 10, 1, 1, 28, 4, 80, 2, 14, 34, 78, 1, 23, 3, 103, 3, 3, - 2, 8, 1, 3, 1, 4, 1, 25, 2, 5, 1, 151, 2, 26, 18, 13, 1, 38, 8, 25, 11, 46, 3, 48, 1, 2, 4, - 2, 2, 17, 1, 21, 2, 66, 6, 2, 2, 2, 2, 12, 1, 8, 1, 35, 1, 11, 1, 51, 1, 1, 3, 2, 2, 5, 2, - 1, 1, 27, 1, 14, 2, 5, 2, 1, 1, 100, 5, 9, 3, 121, 1, 2, 1, 4, 1, 0, 1, 147, 17, 0, 16, 3, - 1, 12, 16, 34, 1, 2, 1, 169, 1, 7, 1, 6, 1, 11, 1, 35, 1, 1, 1, 47, 1, 45, 2, 67, 1, 21, 3, - 0, 1, 226, 1, 149, 5, 0, 6, 1, 42, 1, 9, 0, 3, 1, 2, 5, 4, 40, 3, 4, 1, 165, 2, 0, 4, 38, 1, - 26, 5, 1, 1, 0, 2, 79, 4, 70, 11, 49, 4, 123, 1, 54, 15, 41, 1, 2, 2, 10, 3, 49, 4, 2, 2, 2, - 1, 4, 1, 10, 1, 50, 3, 36, 5, 1, 8, 62, 1, 12, 2, 52, 9, 10, 4, 2, 1, 95, 3, 2, 1, 1, 2, 6, - 1, 2, 1, 157, 1, 3, 8, 21, 2, 57, 2, 3, 1, 37, 7, 3, 5, 70, 6, 13, 1, 1, 1, 1, 1, 14, 2, 85, - 8, 2, 3, 1, 1, 23, 1, 84, 6, 1, 1, 4, 2, 1, 2, 238, 4, 6, 2, 1, 2, 27, 2, 85, 8, 2, 1, 1, 2, - 106, 1, 1, 1, 2, 6, 1, 1, 101, 1, 1, 1, 2, 4, 1, 5, 0, 9, 1, 2, 0, 2, 1, 1, 4, 1, 144, 4, 2, - 2, 4, 1, 32, 10, 40, 6, 2, 4, 8, 1, 9, 6, 2, 3, 46, 13, 1, 2, 0, 7, 1, 6, 1, 1, 82, 22, 2, - 7, 1, 2, 1, 2, 122, 6, 3, 1, 1, 2, 1, 7, 1, 1, 72, 2, 3, 1, 1, 1, 0, 2, 11, 2, 52, 5, 5, 1, - 1, 1, 23, 1, 0, 17, 6, 15, 0, 12, 3, 3, 0, 5, 59, 7, 9, 4, 0, 3, 40, 2, 0, 1, 63, 17, 64, 2, - 1, 2, 0, 4, 1, 7, 1, 2, 0, 2, 1, 4, 0, 46, 2, 23, 0, 3, 9, 16, 2, 7, 30, 4, 148, 3, 0, 55, - 4, 50, 8, 1, 14, 1, 22, 5, 1, 15, 0, 7, 1, 17, 2, 7, 1, 2, 1, 5, 5, 62, 33, 1, 160, 14, 0, - 1, 61, 4, 0, 5, 254, 2, 0, 7, 109, 8, 0, 5, 0, 1, 30, 96, 128, 240, 0, + static OFFSETS: [u8; 895] = [ + 168, 1, 4, 1, 1, 1, 4, 1, 2, 2, 0, 192, 4, 2, 4, 1, 9, 2, 1, 1, 251, 7, 207, 1, 5, 1, 49, + 45, 1, 1, 1, 2, 1, 2, 1, 1, 44, 1, 11, 6, 10, 11, 1, 1, 35, 1, 10, 21, 16, 1, 101, 8, 1, 10, + 1, 4, 33, 1, 1, 1, 30, 27, 91, 11, 58, 11, 4, 1, 2, 1, 24, 24, 43, 3, 44, 1, 7, 2, 5, 9, 41, + 58, 55, 1, 1, 1, 4, 8, 4, 1, 3, 7, 10, 2, 13, 1, 15, 1, 58, 1, 4, 4, 8, 1, 20, 2, 26, 1, 2, + 2, 57, 1, 4, 2, 4, 2, 2, 3, 3, 1, 30, 2, 3, 1, 11, 2, 57, 1, 4, 5, 1, 2, 4, 1, 20, 2, 22, 6, + 1, 1, 58, 1, 2, 1, 1, 4, 8, 1, 7, 2, 11, 2, 30, 1, 61, 1, 12, 1, 50, 1, 3, 1, 55, 1, 1, 3, + 5, 3, 1, 4, 7, 2, 11, 2, 29, 1, 58, 1, 2, 1, 6, 1, 5, 2, 20, 2, 28, 2, 57, 2, 4, 4, 8, 1, + 20, 2, 29, 1, 72, 1, 7, 3, 1, 1, 90, 1, 2, 7, 11, 9, 98, 1, 2, 9, 9, 1, 1, 7, 73, 2, 27, 1, + 1, 1, 1, 1, 55, 14, 1, 5, 1, 2, 5, 11, 1, 36, 9, 1, 102, 4, 1, 6, 1, 2, 2, 2, 25, 2, 4, 3, + 16, 4, 13, 1, 2, 2, 6, 1, 15, 1, 94, 1, 0, 3, 0, 3, 29, 2, 30, 2, 30, 2, 64, 2, 1, 7, 8, 1, + 2, 11, 3, 1, 5, 1, 45, 5, 51, 1, 65, 2, 34, 1, 118, 3, 4, 2, 9, 1, 6, 3, 219, 2, 2, 1, 58, + 1, 1, 7, 1, 1, 1, 1, 2, 8, 6, 10, 2, 1, 39, 1, 8, 31, 49, 4, 48, 1, 1, 5, 1, 1, 5, 1, 40, 9, + 12, 2, 32, 4, 2, 2, 1, 3, 56, 1, 1, 2, 3, 1, 1, 3, 58, 8, 2, 2, 64, 6, 82, 3, 1, 13, 1, 7, + 4, 1, 6, 1, 3, 2, 50, 63, 13, 1, 34, 101, 0, 1, 1, 3, 11, 3, 13, 3, 13, 3, 13, 2, 12, 5, 8, + 2, 10, 1, 2, 1, 2, 5, 49, 5, 1, 10, 1, 1, 13, 1, 16, 13, 51, 33, 0, 2, 113, 3, 125, 1, 15, + 1, 96, 32, 47, 1, 0, 1, 36, 4, 3, 5, 5, 1, 93, 6, 93, 3, 0, 1, 0, 6, 0, 1, 98, 4, 1, 10, 1, + 1, 28, 4, 80, 2, 14, 34, 78, 1, 23, 3, 103, 3, 3, 2, 8, 1, 3, 1, 4, 1, 25, 2, 5, 1, 151, 2, + 26, 18, 13, 1, 38, 8, 25, 11, 46, 3, 48, 1, 2, 4, 2, 2, 17, 1, 21, 2, 66, 6, 2, 2, 2, 2, 12, + 1, 8, 1, 35, 1, 11, 1, 51, 1, 1, 3, 2, 2, 5, 2, 1, 1, 27, 1, 14, 2, 5, 2, 1, 1, 100, 5, 9, + 3, 121, 1, 2, 1, 4, 1, 0, 1, 147, 17, 0, 16, 3, 1, 12, 16, 34, 1, 2, 1, 169, 1, 7, 1, 6, 1, + 11, 1, 35, 1, 1, 1, 47, 1, 45, 2, 67, 1, 21, 3, 0, 1, 226, 1, 149, 5, 0, 6, 1, 42, 1, 9, 0, + 3, 1, 2, 5, 4, 40, 3, 4, 1, 165, 2, 0, 4, 38, 1, 26, 5, 1, 1, 0, 2, 79, 4, 70, 11, 49, 4, + 123, 1, 54, 15, 41, 1, 2, 2, 10, 3, 49, 4, 2, 2, 2, 1, 4, 1, 10, 1, 50, 3, 36, 5, 1, 8, 62, + 1, 12, 2, 52, 9, 10, 4, 2, 1, 95, 3, 2, 1, 1, 2, 6, 1, 2, 1, 157, 1, 3, 8, 21, 2, 57, 2, 3, + 1, 37, 7, 3, 5, 70, 6, 13, 1, 1, 1, 1, 1, 14, 2, 85, 8, 2, 3, 1, 1, 23, 1, 84, 6, 1, 1, 4, + 2, 1, 2, 238, 4, 6, 2, 1, 2, 27, 2, 85, 8, 2, 1, 1, 2, 106, 1, 1, 1, 2, 6, 1, 1, 101, 1, 1, + 1, 2, 4, 1, 5, 0, 9, 1, 2, 0, 2, 1, 1, 4, 1, 144, 4, 2, 2, 4, 1, 32, 10, 40, 6, 2, 4, 8, 1, + 9, 6, 2, 3, 46, 13, 1, 2, 0, 7, 1, 6, 1, 1, 82, 22, 2, 7, 1, 2, 1, 2, 122, 6, 3, 1, 1, 2, 1, + 7, 1, 1, 72, 2, 3, 1, 1, 1, 0, 2, 11, 2, 52, 5, 5, 1, 1, 1, 23, 1, 0, 17, 6, 15, 0, 12, 3, + 3, 0, 5, 59, 7, 9, 4, 0, 3, 40, 2, 0, 1, 63, 17, 64, 2, 1, 2, 0, 4, 1, 7, 1, 2, 0, 2, 1, 4, + 0, 46, 2, 23, 0, 3, 9, 16, 2, 7, 30, 4, 148, 3, 0, 55, 4, 50, 8, 1, 14, 1, 22, 5, 1, 15, 0, + 7, 1, 17, 2, 7, 1, 2, 1, 5, 5, 62, 33, 1, 160, 14, 0, 1, 61, 4, 0, 5, 254, 2, 0, 7, 109, 8, + 0, 5, 0, 1, 30, 96, 128, 240, 0, ]; + #[inline] pub fn lookup(c: char) -> bool { + debug_assert!(!c.is_ascii()); + (c as u32) >= 0xa8 && lookup_slow(c) + } + + #[inline(never)] + fn lookup_slow(c: char) -> bool { const { assert!(SHORT_OFFSET_RUNS.last().unwrap().0 > char::MAX as u32); let mut i = 0; @@ -317,33 +342,40 @@ pub mod cased { use super::ShortOffsetRunHeader; static SHORT_OFFSET_RUNS: [ShortOffsetRunHeader; 22] = [ - ShortOffsetRunHeader::new(0, 4256), ShortOffsetRunHeader::new(55, 5024), - ShortOffsetRunHeader::new(65, 7296), ShortOffsetRunHeader::new(69, 7958), - ShortOffsetRunHeader::new(78, 9398), ShortOffsetRunHeader::new(153, 11264), - ShortOffsetRunHeader::new(155, 42560), ShortOffsetRunHeader::new(167, 43824), - ShortOffsetRunHeader::new(187, 64256), ShortOffsetRunHeader::new(193, 65313), - ShortOffsetRunHeader::new(197, 66560), ShortOffsetRunHeader::new(201, 67456), - ShortOffsetRunHeader::new(223, 68736), ShortOffsetRunHeader::new(231, 71840), - ShortOffsetRunHeader::new(239, 93760), ShortOffsetRunHeader::new(241, 119808), - ShortOffsetRunHeader::new(243, 120486), ShortOffsetRunHeader::new(280, 122624), - ShortOffsetRunHeader::new(303, 122928), ShortOffsetRunHeader::new(309, 125184), - ShortOffsetRunHeader::new(311, 127280), ShortOffsetRunHeader::new(313, 1241482), + ShortOffsetRunHeader::new(0, 4256), ShortOffsetRunHeader::new(51, 5024), + ShortOffsetRunHeader::new(61, 7296), ShortOffsetRunHeader::new(65, 7958), + ShortOffsetRunHeader::new(74, 9398), ShortOffsetRunHeader::new(149, 11264), + ShortOffsetRunHeader::new(151, 42560), ShortOffsetRunHeader::new(163, 43824), + ShortOffsetRunHeader::new(183, 64256), ShortOffsetRunHeader::new(189, 65313), + ShortOffsetRunHeader::new(193, 66560), ShortOffsetRunHeader::new(197, 67456), + ShortOffsetRunHeader::new(219, 68736), ShortOffsetRunHeader::new(227, 71840), + ShortOffsetRunHeader::new(235, 93760), ShortOffsetRunHeader::new(237, 119808), + ShortOffsetRunHeader::new(239, 120486), ShortOffsetRunHeader::new(276, 122624), + ShortOffsetRunHeader::new(299, 122928), ShortOffsetRunHeader::new(305, 125184), + ShortOffsetRunHeader::new(307, 127280), ShortOffsetRunHeader::new(309, 1241482), ]; - static OFFSETS: [u8; 319] = [ - 65, 26, 6, 26, 47, 1, 10, 1, 4, 1, 5, 23, 1, 31, 1, 195, 1, 4, 4, 208, 1, 36, 7, 2, 30, 5, - 96, 1, 42, 4, 2, 2, 2, 4, 1, 1, 6, 1, 1, 3, 1, 1, 1, 20, 1, 83, 1, 139, 8, 166, 1, 38, 9, - 41, 0, 38, 1, 1, 5, 1, 2, 43, 1, 4, 0, 86, 2, 6, 0, 11, 5, 43, 2, 3, 64, 192, 64, 0, 2, 6, - 2, 38, 2, 6, 2, 8, 1, 1, 1, 1, 1, 1, 1, 31, 2, 53, 1, 7, 1, 1, 3, 3, 1, 7, 3, 4, 2, 6, 4, - 13, 5, 3, 1, 7, 116, 1, 13, 1, 16, 13, 101, 1, 4, 1, 2, 10, 1, 1, 3, 5, 6, 1, 1, 1, 1, 1, 1, - 4, 1, 6, 4, 1, 2, 4, 5, 5, 4, 1, 17, 32, 3, 2, 0, 52, 0, 229, 6, 4, 3, 2, 12, 38, 1, 1, 5, - 1, 0, 46, 18, 30, 132, 102, 3, 4, 1, 62, 2, 2, 1, 1, 1, 8, 21, 5, 1, 3, 0, 43, 1, 14, 6, 80, - 0, 7, 12, 5, 0, 26, 6, 26, 0, 80, 96, 36, 4, 36, 116, 11, 1, 15, 1, 7, 1, 2, 1, 11, 1, 15, - 1, 7, 1, 2, 0, 1, 2, 3, 1, 42, 1, 9, 0, 51, 13, 51, 93, 22, 10, 22, 0, 64, 0, 64, 0, 85, 1, - 71, 1, 2, 2, 1, 2, 2, 2, 4, 1, 12, 1, 1, 1, 7, 1, 65, 1, 4, 2, 8, 1, 7, 1, 28, 1, 4, 1, 5, - 1, 1, 3, 7, 1, 0, 2, 25, 1, 25, 1, 31, 1, 25, 1, 31, 1, 25, 1, 31, 1, 25, 1, 31, 1, 25, 1, - 8, 0, 10, 1, 20, 6, 6, 0, 62, 0, 68, 0, 26, 6, 26, 6, 26, 0, + static OFFSETS: [u8; 315] = [ + 170, 1, 10, 1, 4, 1, 5, 23, 1, 31, 1, 195, 1, 4, 4, 208, 1, 36, 7, 2, 30, 5, 96, 1, 42, 4, + 2, 2, 2, 4, 1, 1, 6, 1, 1, 3, 1, 1, 1, 20, 1, 83, 1, 139, 8, 166, 1, 38, 9, 41, 0, 38, 1, 1, + 5, 1, 2, 43, 1, 4, 0, 86, 2, 6, 0, 11, 5, 43, 2, 3, 64, 192, 64, 0, 2, 6, 2, 38, 2, 6, 2, 8, + 1, 1, 1, 1, 1, 1, 1, 31, 2, 53, 1, 7, 1, 1, 3, 3, 1, 7, 3, 4, 2, 6, 4, 13, 5, 3, 1, 7, 116, + 1, 13, 1, 16, 13, 101, 1, 4, 1, 2, 10, 1, 1, 3, 5, 6, 1, 1, 1, 1, 1, 1, 4, 1, 6, 4, 1, 2, 4, + 5, 5, 4, 1, 17, 32, 3, 2, 0, 52, 0, 229, 6, 4, 3, 2, 12, 38, 1, 1, 5, 1, 0, 46, 18, 30, 132, + 102, 3, 4, 1, 62, 2, 2, 1, 1, 1, 8, 21, 5, 1, 3, 0, 43, 1, 14, 6, 80, 0, 7, 12, 5, 0, 26, 6, + 26, 0, 80, 96, 36, 4, 36, 116, 11, 1, 15, 1, 7, 1, 2, 1, 11, 1, 15, 1, 7, 1, 2, 0, 1, 2, 3, + 1, 42, 1, 9, 0, 51, 13, 51, 93, 22, 10, 22, 0, 64, 0, 64, 0, 85, 1, 71, 1, 2, 2, 1, 2, 2, 2, + 4, 1, 12, 1, 1, 1, 7, 1, 65, 1, 4, 2, 8, 1, 7, 1, 28, 1, 4, 1, 5, 1, 1, 3, 7, 1, 0, 2, 25, + 1, 25, 1, 31, 1, 25, 1, 31, 1, 25, 1, 31, 1, 25, 1, 31, 1, 25, 1, 8, 0, 10, 1, 20, 6, 6, 0, + 62, 0, 68, 0, 26, 6, 26, 6, 26, 0, ]; + #[inline] pub fn lookup(c: char) -> bool { + debug_assert!(!c.is_ascii()); + (c as u32) >= 0xaa && lookup_slow(c) + } + + #[inline(never)] + fn lookup_slow(c: char) -> bool { const { assert!(SHORT_OFFSET_RUNS.last().unwrap().0 > char::MAX as u32); let mut i = 0; @@ -412,6 +444,7 @@ pub mod grapheme_extend { ]; #[inline] pub fn lookup(c: char) -> bool { + debug_assert!(!c.is_ascii()); (c as u32) >= 0x300 && lookup_slow(c) } @@ -434,7 +467,7 @@ pub mod grapheme_extend { #[rustfmt::skip] pub mod lowercase { static BITSET_CHUNKS_MAP: [u8; 123] = [ - 14, 17, 0, 0, 9, 0, 0, 12, 13, 10, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 12, 17, 0, 0, 9, 0, 0, 13, 14, 10, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1, 0, 15, 0, 8, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, @@ -446,37 +479,37 @@ pub mod lowercase { [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 14, 56, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 43, 0, 52, 48, 50, 33], - [0, 0, 0, 0, 10, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 9, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 3, 0, 16, 58, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27], [0, 0, 0, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 34, 17, 23, 53, 54, 49, 47, 7, 35, 42, 0, 28, 12, 31], [0, 0, 46, 0, 56, 56, 56, 0, 22, 22, 69, 22, 36, 25, 24, 37], [0, 5, 70, 0, 29, 15, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 66, 34, 17, 23, 53, 54, 49, 47, 8, 35, 42, 0, 28, 13, 31], - [11, 60, 0, 6, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 32, 0], + [10, 60, 0, 6, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 32, 0], [16, 26, 22, 38, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [16, 51, 2, 21, 68, 9, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [16, 51, 2, 21, 68, 8, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0], [16, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [65, 41, 55, 12, 77, 63, 18, 1, 7, 64, 76, 20, 73, 74, 4, 45], + [65, 41, 55, 11, 66, 63, 18, 13, 1, 64, 76, 20, 73, 74, 4, 45], ]; static BITSET_CANONICAL: [u64; 56] = [ 0b0000000000000000000000000000000000000000000000000000000000000000, - 0b1111111111111111110000000000000000000000000011111111111111111111, + 0b0000111111111111111111111111110000000000000000000000000011111111, 0b1010101010101010101010101010101010101010101010101010100000000010, 0b0000000000000111111111111111111111111111111111111111111111111111, 0b1111111111111111111111000000000000000000000000001111110111111111, 0b1000000000000010000000000000000000000000000000000000000000000000, 0b0000111111111111111111111111111111111111000000000000000000000000, - 0b0000111111111111111111111111110000000000000000000000000011111111, 0b1111111111111111111111111111111111111111111111111010101010000101, 0b1111111111111111111111111111111100000000000000000000000000000000, 0b1111111111111111111111111111110000000000000000000000000000000000, 0b1111111111111111111111110000000000000000000000000000000000000000, 0b1111111111111111111111000000000000000000000000001111111111101111, 0b1111111111111111111100000000000000000000000000010000000000000000, + 0b1111111111111111110000000000000000000000000011111111111111111111, 0b1111111111111111000000111111111111110111111111111111111111111111, 0b1111111111111111000000000000000000000000000000000100001111000000, 0b1111111111111111000000000000000000000000000000000000000000000000, @@ -520,13 +553,15 @@ pub mod lowercase { 0b1110011001010001001011010010101001001110001001000011000100101001, 0b1110101111000000000000000000000000001111111111111111111111111100, ]; - static BITSET_MAPPING: [(u8, u8); 22] = [ - (0, 64), (1, 188), (1, 186), (1, 183), (1, 176), (1, 109), (1, 124), (1, 126), (1, 66), - (1, 70), (1, 77), (2, 146), (2, 144), (2, 83), (3, 93), (3, 147), (3, 133), (4, 12), (4, 6), - (5, 187), (6, 78), (7, 132), + static BITSET_MAPPING: [(u8, u8); 21] = [ + (0, 64), (1, 184), (1, 182), (1, 179), (1, 172), (1, 161), (1, 146), (1, 144), (1, 140), + (1, 136), (1, 132), (2, 146), (2, 144), (2, 83), (3, 93), (3, 147), (3, 133), (4, 12), + (4, 6), (5, 187), (6, 78), ]; pub const fn lookup(c: char) -> bool { + debug_assert!(!c.is_ascii()); + (c as u32) >= 0xaa && super::bitset_search( c as u32, &BITSET_CHUNKS_MAP, @@ -542,43 +577,50 @@ pub mod n { use super::ShortOffsetRunHeader; static SHORT_OFFSET_RUNS: [ShortOffsetRunHeader; 42] = [ - ShortOffsetRunHeader::new(0, 1632), ShortOffsetRunHeader::new(9, 2406), - ShortOffsetRunHeader::new(15, 4160), ShortOffsetRunHeader::new(49, 4969), - ShortOffsetRunHeader::new(53, 5870), ShortOffsetRunHeader::new(55, 6470), - ShortOffsetRunHeader::new(63, 8304), ShortOffsetRunHeader::new(79, 9312), - ShortOffsetRunHeader::new(89, 10102), ShortOffsetRunHeader::new(93, 11517), - ShortOffsetRunHeader::new(95, 12295), ShortOffsetRunHeader::new(97, 12690), - ShortOffsetRunHeader::new(103, 42528), ShortOffsetRunHeader::new(115, 43056), - ShortOffsetRunHeader::new(119, 44016), ShortOffsetRunHeader::new(131, 65296), - ShortOffsetRunHeader::new(133, 65799), ShortOffsetRunHeader::new(135, 66273), - ShortOffsetRunHeader::new(141, 67672), ShortOffsetRunHeader::new(153, 68858), - ShortOffsetRunHeader::new(183, 69216), ShortOffsetRunHeader::new(189, 70736), - ShortOffsetRunHeader::new(209, 71248), ShortOffsetRunHeader::new(213, 71904), - ShortOffsetRunHeader::new(221, 72688), ShortOffsetRunHeader::new(225, 73552), - ShortOffsetRunHeader::new(233, 74752), ShortOffsetRunHeader::new(237, 90416), - ShortOffsetRunHeader::new(239, 92768), ShortOffsetRunHeader::new(241, 93552), - ShortOffsetRunHeader::new(249, 93824), ShortOffsetRunHeader::new(251, 118000), - ShortOffsetRunHeader::new(253, 119488), ShortOffsetRunHeader::new(255, 120782), - ShortOffsetRunHeader::new(261, 123200), ShortOffsetRunHeader::new(263, 123632), - ShortOffsetRunHeader::new(265, 124144), ShortOffsetRunHeader::new(267, 125127), - ShortOffsetRunHeader::new(271, 126065), ShortOffsetRunHeader::new(275, 127232), - ShortOffsetRunHeader::new(285, 130032), ShortOffsetRunHeader::new(287, 1244154), + ShortOffsetRunHeader::new(0, 1632), ShortOffsetRunHeader::new(7, 2406), + ShortOffsetRunHeader::new(13, 4160), ShortOffsetRunHeader::new(47, 4969), + ShortOffsetRunHeader::new(51, 5870), ShortOffsetRunHeader::new(53, 6470), + ShortOffsetRunHeader::new(61, 8304), ShortOffsetRunHeader::new(77, 9312), + ShortOffsetRunHeader::new(87, 10102), ShortOffsetRunHeader::new(91, 11517), + ShortOffsetRunHeader::new(93, 12295), ShortOffsetRunHeader::new(95, 12690), + ShortOffsetRunHeader::new(101, 42528), ShortOffsetRunHeader::new(113, 43056), + ShortOffsetRunHeader::new(117, 44016), ShortOffsetRunHeader::new(129, 65296), + ShortOffsetRunHeader::new(131, 65799), ShortOffsetRunHeader::new(133, 66273), + ShortOffsetRunHeader::new(139, 67672), ShortOffsetRunHeader::new(151, 68858), + ShortOffsetRunHeader::new(181, 69216), ShortOffsetRunHeader::new(187, 70736), + ShortOffsetRunHeader::new(207, 71248), ShortOffsetRunHeader::new(211, 71904), + ShortOffsetRunHeader::new(219, 72688), ShortOffsetRunHeader::new(223, 73552), + ShortOffsetRunHeader::new(231, 74752), ShortOffsetRunHeader::new(235, 90416), + ShortOffsetRunHeader::new(237, 92768), ShortOffsetRunHeader::new(239, 93552), + ShortOffsetRunHeader::new(247, 93824), ShortOffsetRunHeader::new(249, 118000), + ShortOffsetRunHeader::new(251, 119488), ShortOffsetRunHeader::new(253, 120782), + ShortOffsetRunHeader::new(259, 123200), ShortOffsetRunHeader::new(261, 123632), + ShortOffsetRunHeader::new(263, 124144), ShortOffsetRunHeader::new(265, 125127), + ShortOffsetRunHeader::new(269, 126065), ShortOffsetRunHeader::new(273, 127232), + ShortOffsetRunHeader::new(283, 130032), ShortOffsetRunHeader::new(285, 1244154), ]; - static OFFSETS: [u8; 289] = [ - 48, 10, 120, 2, 5, 1, 2, 3, 0, 10, 134, 10, 198, 10, 0, 10, 118, 10, 4, 6, 108, 10, 118, - 10, 118, 10, 2, 6, 110, 13, 115, 10, 8, 7, 103, 10, 104, 7, 7, 19, 109, 10, 96, 10, 118, 10, - 70, 20, 0, 10, 70, 10, 0, 20, 0, 3, 239, 10, 6, 10, 22, 10, 0, 10, 128, 11, 165, 10, 6, 10, - 182, 10, 86, 10, 134, 10, 6, 10, 0, 1, 3, 6, 6, 10, 198, 51, 2, 5, 0, 60, 78, 22, 0, 30, 0, - 1, 0, 1, 25, 9, 14, 3, 0, 4, 138, 10, 30, 8, 1, 15, 32, 10, 39, 15, 0, 10, 188, 10, 0, 6, - 154, 10, 38, 10, 198, 10, 22, 10, 86, 10, 0, 10, 0, 10, 0, 45, 12, 57, 17, 2, 0, 27, 36, 4, - 29, 1, 8, 1, 134, 5, 202, 10, 0, 8, 25, 7, 39, 9, 75, 5, 22, 6, 160, 2, 2, 16, 2, 46, 64, 9, - 52, 2, 30, 3, 75, 5, 104, 8, 24, 8, 41, 7, 0, 6, 48, 10, 6, 10, 0, 31, 158, 10, 42, 4, 112, - 7, 134, 30, 128, 10, 60, 10, 144, 10, 7, 20, 251, 10, 0, 10, 118, 10, 0, 10, 102, 10, 6, 20, - 76, 12, 0, 19, 93, 10, 0, 10, 86, 29, 227, 10, 70, 10, 0, 10, 102, 21, 0, 111, 0, 10, 0, 10, - 86, 10, 134, 10, 1, 7, 0, 10, 0, 23, 0, 10, 0, 20, 12, 20, 108, 25, 0, 50, 0, 10, 0, 10, 0, - 10, 247, 10, 0, 9, 128, 10, 0, 59, 1, 3, 1, 4, 76, 45, 1, 15, 0, 13, 0, 10, 0, + static OFFSETS: [u8; 287] = [ + 178, 2, 5, 1, 2, 3, 0, 10, 134, 10, 198, 10, 0, 10, 118, 10, 4, 6, 108, 10, 118, 10, 118, + 10, 2, 6, 110, 13, 115, 10, 8, 7, 103, 10, 104, 7, 7, 19, 109, 10, 96, 10, 118, 10, 70, 20, + 0, 10, 70, 10, 0, 20, 0, 3, 239, 10, 6, 10, 22, 10, 0, 10, 128, 11, 165, 10, 6, 10, 182, 10, + 86, 10, 134, 10, 6, 10, 0, 1, 3, 6, 6, 10, 198, 51, 2, 5, 0, 60, 78, 22, 0, 30, 0, 1, 0, 1, + 25, 9, 14, 3, 0, 4, 138, 10, 30, 8, 1, 15, 32, 10, 39, 15, 0, 10, 188, 10, 0, 6, 154, 10, + 38, 10, 198, 10, 22, 10, 86, 10, 0, 10, 0, 10, 0, 45, 12, 57, 17, 2, 0, 27, 36, 4, 29, 1, 8, + 1, 134, 5, 202, 10, 0, 8, 25, 7, 39, 9, 75, 5, 22, 6, 160, 2, 2, 16, 2, 46, 64, 9, 52, 2, + 30, 3, 75, 5, 104, 8, 24, 8, 41, 7, 0, 6, 48, 10, 6, 10, 0, 31, 158, 10, 42, 4, 112, 7, 134, + 30, 128, 10, 60, 10, 144, 10, 7, 20, 251, 10, 0, 10, 118, 10, 0, 10, 102, 10, 6, 20, 76, 12, + 0, 19, 93, 10, 0, 10, 86, 29, 227, 10, 70, 10, 0, 10, 102, 21, 0, 111, 0, 10, 0, 10, 86, 10, + 134, 10, 1, 7, 0, 10, 0, 23, 0, 10, 0, 20, 12, 20, 108, 25, 0, 50, 0, 10, 0, 10, 0, 10, 247, + 10, 0, 9, 128, 10, 0, 59, 1, 3, 1, 4, 76, 45, 1, 15, 0, 13, 0, 10, 0, ]; + #[inline] pub fn lookup(c: char) -> bool { + debug_assert!(!c.is_ascii()); + (c as u32) >= 0xb2 && lookup_slow(c) + } + + #[inline(never)] + fn lookup_slow(c: char) -> bool { const { assert!(SHORT_OFFSET_RUNS.last().unwrap().0 > char::MAX as u32); let mut i = 0; @@ -596,34 +638,34 @@ pub mod n { #[rustfmt::skip] pub mod uppercase { static BITSET_CHUNKS_MAP: [u8; 125] = [ - 12, 15, 6, 6, 0, 6, 6, 2, 4, 11, 6, 16, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 8, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 5, 6, 14, 6, 10, 6, 6, 1, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 6, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 13, 6, 6, - 6, 6, 9, 6, 3, + 3, 14, 6, 6, 0, 6, 6, 2, 5, 12, 6, 15, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 9, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 7, 6, 13, 6, 11, 6, 6, 1, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 8, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 16, 6, 6, + 6, 6, 10, 6, 4, ]; static BITSET_INDEX_CHUNKS: [[u8; 16]; 17] = [ - [44, 44, 5, 35, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 5, 1], + [44, 44, 5, 35, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 5, 0], [44, 44, 5, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], - [44, 44, 40, 44, 44, 44, 44, 44, 17, 17, 63, 17, 43, 29, 24, 23], + [44, 44, 40, 44, 44, 44, 44, 44, 17, 17, 62, 17, 43, 29, 24, 23], + [44, 44, 44, 32, 36, 21, 22, 15, 13, 34, 44, 44, 44, 11, 30, 39], [44, 44, 44, 44, 9, 8, 45, 44, 44, 44, 44, 44, 44, 44, 44, 44], - [44, 44, 44, 44, 37, 28, 67, 44, 44, 44, 44, 44, 44, 44, 44, 44], - [44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 0, 44, 44, 44], + [44, 44, 44, 44, 37, 28, 66, 44, 44, 44, 44, 44, 44, 44, 44, 44], [44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], - [44, 44, 44, 44, 44, 44, 44, 44, 44, 55, 44, 44, 44, 44, 44, 44], - [44, 44, 44, 44, 44, 44, 44, 44, 44, 62, 61, 44, 20, 14, 16, 4], - [44, 44, 44, 44, 56, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], - [44, 44, 59, 44, 44, 31, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], - [44, 44, 60, 46, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], - [44, 49, 44, 32, 36, 21, 22, 15, 13, 34, 44, 44, 44, 11, 30, 39], - [52, 54, 26, 50, 12, 7, 25, 51, 41, 53, 6, 3, 66, 65, 64, 68], - [57, 44, 9, 47, 44, 42, 33, 44, 44, 44, 44, 44, 44, 44, 44, 44], - [58, 19, 2, 18, 10, 48, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], - [58, 38, 17, 27, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], + [44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 57, 44, 44, 44], + [44, 44, 44, 44, 44, 44, 44, 44, 44, 49, 44, 44, 44, 44, 44, 44], + [44, 44, 44, 44, 44, 44, 44, 44, 44, 61, 60, 44, 20, 14, 16, 4], + [44, 44, 44, 44, 50, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], + [44, 44, 53, 44, 44, 31, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], + [44, 44, 54, 46, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], + [51, 44, 9, 47, 44, 42, 33, 44, 44, 44, 44, 44, 44, 44, 44, 44], + [52, 19, 2, 18, 10, 48, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], + [52, 38, 17, 27, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], + [58, 1, 26, 55, 12, 7, 25, 56, 41, 59, 6, 3, 65, 64, 63, 67], ]; static BITSET_CANONICAL: [u64; 44] = [ - 0b0000011111111111111111111111111000000000000000000000000000000000, 0b0000000000111111111111111111111111111111111111111111111111111111, + 0b1111111111111111111111110000000000000000000000000011111111111111, 0b0101010101010101010101010101010101010101010101010101010000000001, 0b0000011111111111111111111111110000000000000000000000000000000001, 0b0000000000100000000000000000000000010101010000010001101011110101, @@ -667,13 +709,15 @@ pub mod uppercase { 0b1111011111111111000000000000000000000000000000000000000000000000, 0b1111111100000000111111110000000000111111000000001111111100000000, ]; - static BITSET_MAPPING: [(u8, u8); 25] = [ - (0, 187), (0, 177), (0, 171), (0, 167), (0, 164), (0, 32), (0, 47), (0, 51), (0, 121), - (0, 117), (0, 109), (1, 150), (1, 148), (1, 142), (1, 134), (1, 131), (1, 64), (2, 164), - (2, 146), (2, 20), (3, 146), (3, 140), (3, 134), (4, 178), (4, 171), + static BITSET_MAPPING: [(u8, u8); 24] = [ + (0, 182), (0, 74), (0, 166), (0, 162), (0, 159), (0, 150), (0, 148), (0, 142), (0, 134), + (0, 131), (0, 64), (1, 66), (1, 70), (1, 83), (1, 12), (1, 8), (2, 164), (2, 146), (2, 20), + (3, 146), (3, 140), (3, 134), (4, 178), (4, 171), ]; pub const fn lookup(c: char) -> bool { + debug_assert!(!c.is_ascii()); + (c as u32) >= 0xc0 && super::bitset_search( c as u32, &BITSET_CHUNKS_MAP, @@ -687,8 +731,8 @@ pub mod uppercase { #[rustfmt::skip] pub mod white_space { static WHITESPACE_MAP: [u8; 256] = [ - 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -699,6 +743,7 @@ pub mod white_space { ]; #[inline] pub const fn lookup(c: char) -> bool { + debug_assert!(!c.is_ascii()); match c as u32 >> 8 { 0 => WHITESPACE_MAP[c as usize & 0xff] & 1 != 0, 22 => c as u32 == 0x1680, @@ -747,7 +792,7 @@ pub mod conversions { } } - static LOWERCASE_TABLE: &[(char, u32)] = &[ + static LOWERCASE_TABLE: &[(char, u32); 1434] = &[ ('\u{c0}', 224), ('\u{c1}', 225), ('\u{c2}', 226), ('\u{c3}', 227), ('\u{c4}', 228), ('\u{c5}', 229), ('\u{c6}', 230), ('\u{c7}', 231), ('\u{c8}', 232), ('\u{c9}', 233), ('\u{ca}', 234), ('\u{cb}', 235), ('\u{cc}', 236), ('\u{cd}', 237), ('\u{ce}', 238), @@ -1097,11 +1142,11 @@ pub mod conversions { ('\u{1e921}', 125251), ]; - static LOWERCASE_TABLE_MULTI: &[[char; 3]] = &[ + static LOWERCASE_TABLE_MULTI: &[[char; 3]; 1] = &[ ['i', '\u{307}', '\u{0}'], ]; - static UPPERCASE_TABLE: &[(char, u32)] = &[ + static UPPERCASE_TABLE: &[(char, u32); 1526] = &[ ('\u{b5}', 924), ('\u{df}', 4194304), ('\u{e0}', 192), ('\u{e1}', 193), ('\u{e2}', 194), ('\u{e3}', 195), ('\u{e4}', 196), ('\u{e5}', 197), ('\u{e6}', 198), ('\u{e7}', 199), ('\u{e8}', 200), ('\u{e9}', 201), ('\u{ea}', 202), ('\u{eb}', 203), ('\u{ec}', 204), @@ -1474,7 +1519,7 @@ pub mod conversions { ('\u{1e941}', 125215), ('\u{1e942}', 125216), ('\u{1e943}', 125217), ]; - static UPPERCASE_TABLE_MULTI: &[[char; 3]] = &[ + static UPPERCASE_TABLE_MULTI: &[[char; 3]; 102] = &[ ['S', 'S', '\u{0}'], ['\u{2bc}', 'N', '\u{0}'], ['J', '\u{30c}', '\u{0}'], ['\u{399}', '\u{308}', '\u{301}'], ['\u{3a5}', '\u{308}', '\u{301}'], ['\u{535}', '\u{552}', '\u{0}'], ['H', '\u{331}', '\u{0}'], ['T', '\u{308}', '\u{0}'], diff --git a/library/coretests/benches/fmt.rs b/library/coretests/benches/fmt.rs index f45b921b939..17549ab0f1c 100644 --- a/library/coretests/benches/fmt.rs +++ b/library/coretests/benches/fmt.rs @@ -342,3 +342,27 @@ fn write_i128_hex(bh: &mut Bencher) { black_box(&mut buf).clear(); }); } + +#[bench] +fn write_i64_exp(bh: &mut Bencher) { + let mut buf = String::with_capacity(1024); + bh.iter(|| { + write!(black_box(&mut buf), "{:e}", black_box(0_i64)).unwrap(); + write!(black_box(&mut buf), "{:e}", black_box(100_i64)).unwrap(); + write!(black_box(&mut buf), "{:e}", black_box(-100_i64)).unwrap(); + write!(black_box(&mut buf), "{:e}", black_box(1_i64 << 32)).unwrap(); + black_box(&mut buf).clear(); + }); +} + +#[bench] +fn write_i128_exp(bh: &mut Bencher) { + let mut buf = String::with_capacity(1024); + bh.iter(|| { + write!(black_box(&mut buf), "{:e}", black_box(0_i128)).unwrap(); + write!(black_box(&mut buf), "{:e}", black_box(100_i128)).unwrap(); + write!(black_box(&mut buf), "{:e}", black_box(-100_i128)).unwrap(); + write!(black_box(&mut buf), "{:e}", black_box(1_i128 << 64)).unwrap(); + black_box(&mut buf).clear(); + }); +} diff --git a/library/coretests/tests/floats/f128.rs b/library/coretests/tests/floats/f128.rs index c173d7f0ae0..4267fef50f8 100644 --- a/library/coretests/tests/floats/f128.rs +++ b/library/coretests/tests/floats/f128.rs @@ -1,7 +1,9 @@ // FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy #![cfg(target_has_reliable_f128)] -use super::{assert_approx_eq, assert_biteq}; +#[cfg(any(miri, target_has_reliable_f128_math))] +use super::assert_approx_eq; +use super::assert_biteq; // Note these tolerances make sense around zero, but not for more extreme exponents. @@ -75,25 +77,6 @@ fn test_float_bits_conv() { } #[test] -fn test_algebraic() { - let a: f128 = 123.0; - let b: f128 = 456.0; - - // Check that individual operations match their primitive counterparts. - // - // This is a check of current implementations and does NOT imply any form of - // guarantee about future behavior. The compiler reserves the right to make - // these operations inexact matches in the future. - let eps = if cfg!(miri) { 1e-6 } else { 0.0 }; - - assert_approx_eq!(a.algebraic_add(b), a + b, eps); - assert_approx_eq!(a.algebraic_sub(b), a - b, eps); - assert_approx_eq!(a.algebraic_mul(b), a * b, eps); - assert_approx_eq!(a.algebraic_div(b), a / b, eps); - assert_approx_eq!(a.algebraic_rem(b), a % b, eps); -} - -#[test] fn test_from() { assert_biteq!(f128::from(false), 0.0); assert_biteq!(f128::from(true), 1.0); diff --git a/library/coretests/tests/floats/f16.rs b/library/coretests/tests/floats/f16.rs index c12de7221ba..d2818a6d768 100644 --- a/library/coretests/tests/floats/f16.rs +++ b/library/coretests/tests/floats/f16.rs @@ -74,27 +74,6 @@ fn test_float_bits_conv() { } #[test] -fn test_algebraic() { - let a: f16 = 123.0; - let b: f16 = 456.0; - - // Check that individual operations match their primitive counterparts. - // - // This is a check of current implementations and does NOT imply any form of - // guarantee about future behavior. The compiler reserves the right to make - // these operations inexact matches in the future. - let eps_add = if cfg!(miri) { 1e1 } else { 0.0 }; - let eps_mul = if cfg!(miri) { 1e3 } else { 0.0 }; - let eps_div = if cfg!(miri) { 1e0 } else { 0.0 }; - - assert_approx_eq!(a.algebraic_add(b), a + b, eps_add); - assert_approx_eq!(a.algebraic_sub(b), a - b, eps_add); - assert_approx_eq!(a.algebraic_mul(b), a * b, eps_mul); - assert_approx_eq!(a.algebraic_div(b), a / b, eps_div); - assert_approx_eq!(a.algebraic_rem(b), a % b, eps_div); -} - -#[test] fn test_from() { assert_biteq!(f16::from(false), 0.0); assert_biteq!(f16::from(true), 1.0); diff --git a/library/coretests/tests/floats/f32.rs b/library/coretests/tests/floats/f32.rs index b79295f470d..7b25f354da4 100644 --- a/library/coretests/tests/floats/f32.rs +++ b/library/coretests/tests/floats/f32.rs @@ -1,6 +1,6 @@ use core::f32; -use super::{assert_approx_eq, assert_biteq}; +use super::assert_biteq; /// First pattern over the mantissa const NAN_MASK1: u32 = 0x002a_aaaa; @@ -47,24 +47,3 @@ fn test_float_bits_conv() { assert_eq!(f32::from_bits(masked_nan1).to_bits(), masked_nan1); assert_eq!(f32::from_bits(masked_nan2).to_bits(), masked_nan2); } - -#[test] -fn test_algebraic() { - let a: f32 = 123.0; - let b: f32 = 456.0; - - // Check that individual operations match their primitive counterparts. - // - // This is a check of current implementations and does NOT imply any form of - // guarantee about future behavior. The compiler reserves the right to make - // these operations inexact matches in the future. - let eps_add = if cfg!(miri) { 1e-3 } else { 0.0 }; - let eps_mul = if cfg!(miri) { 1e-1 } else { 0.0 }; - let eps_div = if cfg!(miri) { 1e-4 } else { 0.0 }; - - assert_approx_eq!(a.algebraic_add(b), a + b, eps_add); - assert_approx_eq!(a.algebraic_sub(b), a - b, eps_add); - assert_approx_eq!(a.algebraic_mul(b), a * b, eps_mul); - assert_approx_eq!(a.algebraic_div(b), a / b, eps_div); - assert_approx_eq!(a.algebraic_rem(b), a % b, eps_div); -} diff --git a/library/coretests/tests/floats/f64.rs b/library/coretests/tests/floats/f64.rs index a2540586328..099d85627a3 100644 --- a/library/coretests/tests/floats/f64.rs +++ b/library/coretests/tests/floats/f64.rs @@ -1,6 +1,6 @@ use core::f64; -use super::{assert_approx_eq, assert_biteq}; +use super::assert_biteq; /// First pattern over the mantissa const NAN_MASK1: u64 = 0x000a_aaaa_aaaa_aaaa; @@ -46,22 +46,3 @@ fn test_float_bits_conv() { assert_eq!(f64::from_bits(masked_nan1).to_bits(), masked_nan1); assert_eq!(f64::from_bits(masked_nan2).to_bits(), masked_nan2); } - -#[test] -fn test_algebraic() { - let a: f64 = 123.0; - let b: f64 = 456.0; - - // Check that individual operations match their primitive counterparts. - // - // This is a check of current implementations and does NOT imply any form of - // guarantee about future behavior. The compiler reserves the right to make - // these operations inexact matches in the future. - let eps = if cfg!(miri) { 1e-6 } else { 0.0 }; - - assert_approx_eq!(a.algebraic_add(b), a + b, eps); - assert_approx_eq!(a.algebraic_sub(b), a - b, eps); - assert_approx_eq!(a.algebraic_mul(b), a * b, eps); - assert_approx_eq!(a.algebraic_div(b), a / b, eps); - assert_approx_eq!(a.algebraic_rem(b), a % b, eps); -} diff --git a/library/coretests/tests/floats/mod.rs b/library/coretests/tests/floats/mod.rs index 5f59cb9cce3..c0439845a4a 100644 --- a/library/coretests/tests/floats/mod.rs +++ b/library/coretests/tests/floats/mod.rs @@ -27,6 +27,9 @@ trait TestableFloat: Sized { const NAN_MASK1: Self::Int; /// Second pattern over the mantissa const NAN_MASK2: Self::Int; + const EPS_ADD: Self; + const EPS_MUL: Self; + const EPS_DIV: Self; } impl TestableFloat for f16 { @@ -44,6 +47,9 @@ impl TestableFloat for f16 { const MAX_DOWN: Self = Self::from_bits(0x7bfe); const NAN_MASK1: Self::Int = 0x02aa; const NAN_MASK2: Self::Int = 0x0155; + const EPS_ADD: Self = if cfg!(miri) { 1e1 } else { 0.0 }; + const EPS_MUL: Self = if cfg!(miri) { 1e3 } else { 0.0 }; + const EPS_DIV: Self = if cfg!(miri) { 1e0 } else { 0.0 }; } impl TestableFloat for f32 { @@ -63,6 +69,9 @@ impl TestableFloat for f32 { const MAX_DOWN: Self = Self::from_bits(0x7f7f_fffe); const NAN_MASK1: Self::Int = 0x002a_aaaa; const NAN_MASK2: Self::Int = 0x0055_5555; + const EPS_ADD: Self = if cfg!(miri) { 1e-3 } else { 0.0 }; + const EPS_MUL: Self = if cfg!(miri) { 1e-1 } else { 0.0 }; + const EPS_DIV: Self = if cfg!(miri) { 1e-4 } else { 0.0 }; } impl TestableFloat for f64 { @@ -78,6 +87,9 @@ impl TestableFloat for f64 { const MAX_DOWN: Self = Self::from_bits(0x7fef_ffff_ffff_fffe); const NAN_MASK1: Self::Int = 0x000a_aaaa_aaaa_aaaa; const NAN_MASK2: Self::Int = 0x0005_5555_5555_5555; + const EPS_ADD: Self = if cfg!(miri) { 1e-6 } else { 0.0 }; + const EPS_MUL: Self = if cfg!(miri) { 1e-6 } else { 0.0 }; + const EPS_DIV: Self = if cfg!(miri) { 1e-6 } else { 0.0 }; } impl TestableFloat for f128 { @@ -93,6 +105,9 @@ impl TestableFloat for f128 { const MAX_DOWN: Self = Self::from_bits(0x7ffefffffffffffffffffffffffffffe); const NAN_MASK1: Self::Int = 0x0000aaaaaaaaaaaaaaaaaaaaaaaaaaaa; const NAN_MASK2: Self::Int = 0x00005555555555555555555555555555; + const EPS_ADD: Self = if cfg!(miri) { 1e-6 } else { 0.0 }; + const EPS_MUL: Self = if cfg!(miri) { 1e-6 } else { 0.0 }; + const EPS_DIV: Self = if cfg!(miri) { 1e-6 } else { 0.0 }; } /// Determine the tolerance for values of the argument type. @@ -1440,3 +1455,27 @@ float_test! { assert_biteq!(neg_inf.to_radians(), neg_inf); } } + +float_test! { + name: to_algebraic, + attrs: { + f16: #[cfg(target_has_reliable_f16)], + f128: #[cfg(target_has_reliable_f128)], + }, + test<Float> { + let a: Float = 123.0; + let b: Float = 456.0; + + // Check that individual operations match their primitive counterparts. + // + // This is a check of current implementations and does NOT imply any form of + // guarantee about future behavior. The compiler reserves the right to make + // these operations inexact matches in the future. + + assert_approx_eq!(a.algebraic_add(b), a + b, Float::EPS_ADD); + assert_approx_eq!(a.algebraic_sub(b), a - b, Float::EPS_ADD); + assert_approx_eq!(a.algebraic_mul(b), a * b, Float::EPS_MUL); + assert_approx_eq!(a.algebraic_div(b), a / b, Float::EPS_DIV); + assert_approx_eq!(a.algebraic_rem(b), a % b, Float::EPS_DIV); + } +} diff --git a/library/coretests/tests/iter/adapters/peekable.rs b/library/coretests/tests/iter/adapters/peekable.rs index 7f4341b8902..f0549e8d6c2 100644 --- a/library/coretests/tests/iter/adapters/peekable.rs +++ b/library/coretests/tests/iter/adapters/peekable.rs @@ -271,3 +271,89 @@ fn test_peekable_non_fused() { assert_eq!(iter.peek(), None); assert_eq!(iter.next_back(), None); } + +#[test] +fn test_peekable_next_if_map_mutation() { + fn collatz((mut num, mut len): (u64, u32)) -> Result<u32, (u64, u32)> { + let jump = num.trailing_zeros(); + num >>= jump; + len += jump; + if num == 1 { Ok(len) } else { Err((3 * num + 1, len + 1)) } + } + + let mut iter = once((3, 0)).peekable(); + assert_eq!(iter.peek(), Some(&(3, 0))); + assert_eq!(iter.next_if_map(collatz), None); + assert_eq!(iter.peek(), Some(&(10, 1))); + assert_eq!(iter.next_if_map(collatz), None); + assert_eq!(iter.peek(), Some(&(16, 3))); + assert_eq!(iter.next_if_map(collatz), Some(7)); + assert_eq!(iter.peek(), None); + assert_eq!(iter.next_if_map(collatz), None); +} + +#[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn test_peekable_next_if_map_panic() { + use core::cell::Cell; + use std::panic::{AssertUnwindSafe, catch_unwind}; + + struct BitsetOnDrop<'a> { + value: u32, + cell: &'a Cell<u32>, + } + impl<'a> Drop for BitsetOnDrop<'a> { + fn drop(&mut self) { + self.cell.update(|v| v | self.value); + } + } + + let cell = &Cell::new(0); + let mut it = [ + BitsetOnDrop { value: 1, cell }, + BitsetOnDrop { value: 2, cell }, + BitsetOnDrop { value: 4, cell }, + BitsetOnDrop { value: 8, cell }, + ] + .into_iter() + .peekable(); + + // sanity check, .peek() won't consume the value, .next() will transfer ownership. + let item = it.peek().unwrap(); + assert_eq!(item.value, 1); + assert_eq!(cell.get(), 0); + let item = it.next().unwrap(); + assert_eq!(item.value, 1); + assert_eq!(cell.get(), 0); + drop(item); + assert_eq!(cell.get(), 1); + + // next_if_map returning Ok should transfer the value out. + let item = it.next_if_map(Ok).unwrap(); + assert_eq!(item.value, 2); + assert_eq!(cell.get(), 1); + drop(item); + assert_eq!(cell.get(), 3); + + // next_if_map returning Err should not drop anything. + assert_eq!(it.next_if_map::<()>(Err), None); + assert_eq!(cell.get(), 3); + assert_eq!(it.peek().unwrap().value, 4); + assert_eq!(cell.get(), 3); + + // next_if_map panicking should consume and drop the item. + let result = catch_unwind({ + let mut it = AssertUnwindSafe(&mut it); + move || it.next_if_map::<()>(|_| panic!()) + }); + assert!(result.is_err()); + assert_eq!(cell.get(), 7); + assert_eq!(it.next().unwrap().value, 8); + assert_eq!(cell.get(), 15); + assert!(it.peek().is_none()); + + // next_if_map should *not* execute the closure if the iterator is exhausted. + assert!(it.next_if_map::<()>(|_| panic!()).is_none()); + assert!(it.peek().is_none()); + assert_eq!(cell.get(), 15); +} diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index bf0a3ae7870..b5658a9970f 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -15,10 +15,9 @@ #![feature(cfg_target_has_reliable_f16_f128)] #![feature(char_max_len)] #![feature(clone_to_uninit)] -#![feature(const_deref)] +#![feature(const_convert)] #![feature(const_destruct)] #![feature(const_eval_select)] -#![feature(const_from)] #![feature(const_ops)] #![feature(const_option_ops)] #![feature(const_ref_cell)] @@ -51,6 +50,7 @@ #![feature(fmt_internals)] #![feature(formatting_options)] #![feature(freeze)] +#![feature(funnel_shifts)] #![feature(future_join)] #![feature(generic_assert_internals)] #![feature(hasher_prefixfree_extras)] @@ -83,6 +83,7 @@ #![feature(numfmt)] #![feature(option_reduce)] #![feature(pattern)] +#![feature(peekable_next_if_map)] #![feature(pointer_is_aligned_to)] #![feature(portable_simd)] #![feature(ptr_metadata)] diff --git a/library/coretests/tests/num/uint_macros.rs b/library/coretests/tests/num/uint_macros.rs index c7d10ea4d88..63be8a45b5c 100644 --- a/library/coretests/tests/num/uint_macros.rs +++ b/library/coretests/tests/num/uint_macros.rs @@ -104,6 +104,19 @@ macro_rules! uint_module { assert_eq_const_safe!($T: C.rotate_left(128), C); } + fn test_funnel_shift() { + // Shifting by 0 should have no effect + assert_eq_const_safe!($T: <$T>::funnel_shl(A, B, 0), A); + assert_eq_const_safe!($T: <$T>::funnel_shr(A, B, 0), B); + + assert_eq_const_safe!($T: <$T>::funnel_shl(_0, _1, 4), 0b1111); + assert_eq_const_safe!($T: <$T>::funnel_shr(_0, _1, 4), _1 >> 4); + assert_eq_const_safe!($T: <$T>::funnel_shl(_1, _0, 4), _1 << 4); + + assert_eq_const_safe!($T: <$T>::funnel_shl(_1, _1, 4), <$T>::rotate_left(_1, 4)); + assert_eq_const_safe!($T: <$T>::funnel_shr(_1, _1, 4), <$T>::rotate_right(_1, 4)); + } + fn test_swap_bytes() { assert_eq_const_safe!($T: A.swap_bytes().swap_bytes(), A); assert_eq_const_safe!($T: B.swap_bytes().swap_bytes(), B); @@ -151,6 +164,29 @@ macro_rules! uint_module { } #[test] + #[should_panic = "attempt to funnel shift left with overflow"] + fn test_funnel_shl_overflow() { + let _ = <$T>::funnel_shl(A, B, $T::BITS); + } + + #[test] + #[should_panic = "attempt to funnel shift right with overflow"] + fn test_funnel_shr_overflow() { + let _ = <$T>::funnel_shr(A, B, $T::BITS); + } + + #[test] + fn test_funnel_shifts_runtime() { + for i in 0..$T::BITS - 1 { + assert_eq!(<$T>::funnel_shl(A, 0, i), A << i); + assert_eq!(<$T>::funnel_shl(A, A, i), A.rotate_left(i)); + + assert_eq!(<$T>::funnel_shr(0, A, i), A >> i); + assert_eq!(<$T>::funnel_shr(A, A, i), A.rotate_right(i)); + } + } + + #[test] fn test_isolate_highest_one() { const BITS: $T = <$T>::MAX; const MOST_SIG_ONE: $T = 1 << (<$T>::BITS - 1); diff --git a/library/portable-simd/crates/core_simd/src/simd/num/int.rs b/library/portable-simd/crates/core_simd/src/simd/num/int.rs index d25050c3e4b..e7253313f03 100644 --- a/library/portable-simd/crates/core_simd/src/simd/num/int.rs +++ b/library/portable-simd/crates/core_simd/src/simd/num/int.rs @@ -58,6 +58,7 @@ pub trait SimdInt: Copy + Sealed { /// let sat = x.saturating_sub(max); /// assert_eq!(unsat, Simd::from_array([1, MAX, MIN, 0])); /// assert_eq!(sat, Simd::from_array([MIN, MIN, MIN, 0])); + /// ``` fn saturating_sub(self, second: Self) -> Self; /// Lanewise absolute value, implemented in Rust. diff --git a/library/portable-simd/crates/core_simd/src/simd/num/uint.rs b/library/portable-simd/crates/core_simd/src/simd/num/uint.rs index 45d978068b6..e3ba8658bd8 100644 --- a/library/portable-simd/crates/core_simd/src/simd/num/uint.rs +++ b/library/portable-simd/crates/core_simd/src/simd/num/uint.rs @@ -55,6 +55,7 @@ pub trait SimdUint: Copy + Sealed { /// let sat = x.saturating_sub(max); /// assert_eq!(unsat, Simd::from_array([3, 2, 1, 0])); /// assert_eq!(sat, Simd::splat(0)); + /// ``` fn saturating_sub(self, second: Self) -> Self; /// Lanewise absolute difference. diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index eff2562f031..d28a7f0b460 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -80,6 +80,11 @@ wasi = { version = "0.11.0", features = [ 'rustc-dep-of-std', ], default-features = false } +[target.'cfg(all(target_os = "wasi", target_env = "p2"))'.dependencies] +wasip2 = { version = '0.14.4', features = [ + 'rustc-dep-of-std', +], default-features = false, package = 'wasi' } + [target.'cfg(target_os = "uefi")'.dependencies] r-efi = { version = "5.2.0", features = ['rustc-dep-of-std'] } r-efi-alloc = { version = "2.0.0", features = ['rustc-dep-of-std'] } diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index 1214490caad..a39565d2159 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -828,7 +828,8 @@ impl OsStr { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - pub fn new<S: AsRef<OsStr> + ?Sized>(s: &S) -> &OsStr { + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] + pub const fn new<S: [const] AsRef<OsStr> + ?Sized>(s: &S) -> &OsStr { s.as_ref() } @@ -876,14 +877,16 @@ impl OsStr { } #[inline] - fn from_inner(inner: &Slice) -> &OsStr { + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] + const fn from_inner(inner: &Slice) -> &OsStr { // SAFETY: OsStr is just a wrapper of Slice, // therefore converting &Slice to &OsStr is safe. unsafe { &*(inner as *const Slice as *const OsStr) } } #[inline] - fn from_inner_mut(inner: &mut Slice) -> &mut OsStr { + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] + const fn from_inner_mut(inner: &mut Slice) -> &mut OsStr { // SAFETY: OsStr is just a wrapper of Slice, // therefore converting &mut Slice to &mut OsStr is safe. // Any method that mutates OsStr must be careful not to @@ -1681,7 +1684,8 @@ impl ToOwned for OsStr { } #[stable(feature = "rust1", since = "1.0.0")] -impl AsRef<OsStr> for OsStr { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl const AsRef<OsStr> for OsStr { #[inline] fn as_ref(&self) -> &OsStr { self diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 5e51d5e5211..f8dfb0d6334 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -226,6 +226,7 @@ fn file_test_io_seek_and_write() { target_os = "freebsd", target_os = "linux", target_os = "netbsd", + target_os = "solaris", target_vendor = "apple", ))] fn file_lock_multiple_shared() { @@ -249,6 +250,7 @@ fn file_lock_multiple_shared() { target_os = "freebsd", target_os = "linux", target_os = "netbsd", + target_os = "solaris", target_vendor = "apple", ))] fn file_lock_blocking() { @@ -273,6 +275,7 @@ fn file_lock_blocking() { target_os = "freebsd", target_os = "linux", target_os = "netbsd", + target_os = "solaris", target_vendor = "apple", ))] fn file_lock_drop() { @@ -294,6 +297,7 @@ fn file_lock_drop() { target_os = "freebsd", target_os = "linux", target_os = "netbsd", + target_os = "solaris", target_vendor = "apple", ))] fn file_lock_dup() { @@ -492,6 +496,85 @@ fn file_test_io_read_write_at() { #[test] #[cfg(unix)] +fn test_read_buf_at() { + use crate::os::unix::fs::FileExt; + + let tmpdir = tmpdir(); + let filename = tmpdir.join("file_rt_io_file_test_read_buf_at.txt"); + { + let oo = OpenOptions::new().create_new(true).write(true).read(true).clone(); + let mut file = check!(oo.open(&filename)); + check!(file.write_all(b"0123456789")); + } + { + let mut file = check!(File::open(&filename)); + let mut buf: [MaybeUninit<u8>; 5] = [MaybeUninit::uninit(); 5]; + let mut buf = BorrowedBuf::from(buf.as_mut_slice()); + + // Fill entire buffer with potentially short reads + while buf.unfilled().capacity() > 0 { + let len = buf.len(); + check!(file.read_buf_at(buf.unfilled(), 2 + len as u64)); + assert!(!buf.filled().is_empty()); + assert!(b"23456".starts_with(buf.filled())); + assert_eq!(check!(file.stream_position()), 0); + } + assert_eq!(buf.filled(), b"23456"); + + // Already full + check!(file.read_buf_at(buf.unfilled(), 3)); + check!(file.read_buf_at(buf.unfilled(), 10)); + assert_eq!(buf.filled(), b"23456"); + assert_eq!(check!(file.stream_position()), 0); + + // Read past eof is noop + check!(file.read_buf_at(buf.clear().unfilled(), 10)); + assert_eq!(buf.filled(), b""); + check!(file.read_buf_at(buf.clear().unfilled(), 11)); + assert_eq!(buf.filled(), b""); + assert_eq!(check!(file.stream_position()), 0); + } + check!(fs::remove_file(&filename)); +} + +#[test] +#[cfg(unix)] +fn test_read_buf_exact_at() { + use crate::os::unix::fs::FileExt; + + let tmpdir = tmpdir(); + let filename = tmpdir.join("file_rt_io_file_test_read_buf_exact_at.txt"); + { + let oo = OpenOptions::new().create_new(true).write(true).read(true).clone(); + let mut file = check!(oo.open(&filename)); + check!(file.write_all(b"0123456789")); + } + { + let mut file = check!(File::open(&filename)); + let mut buf: [MaybeUninit<u8>; 5] = [MaybeUninit::uninit(); 5]; + let mut buf = BorrowedBuf::from(buf.as_mut_slice()); + + // Exact read + check!(file.read_buf_exact_at(buf.unfilled(), 2)); + assert_eq!(buf.filled(), b"23456"); + assert_eq!(check!(file.stream_position()), 0); + + // Already full + check!(file.read_buf_exact_at(buf.unfilled(), 3)); + check!(file.read_buf_exact_at(buf.unfilled(), 10)); + assert_eq!(buf.filled(), b"23456"); + assert_eq!(check!(file.stream_position()), 0); + + // Non-empty exact read past eof fails + let err = file.read_buf_exact_at(buf.clear().unfilled(), 6).unwrap_err(); + assert_eq!(err.kind(), ErrorKind::UnexpectedEof); + assert_eq!(check!(file.stream_position()), 0); + } + check!(fs::remove_file(&filename)); +} + +#[test] +#[cfg(unix)] fn set_get_unix_permissions() { use crate::os::unix::fs::PermissionsExt; @@ -567,6 +650,39 @@ fn file_test_io_seek_read_write() { } #[test] +#[cfg(windows)] +fn test_seek_read_buf() { + use crate::os::windows::fs::FileExt; + + let tmpdir = tmpdir(); + let filename = tmpdir.join("file_rt_io_file_test_seek_read_buf.txt"); + { + let oo = OpenOptions::new().create_new(true).write(true).read(true).clone(); + let mut file = check!(oo.open(&filename)); + check!(file.write_all(b"0123456789")); + } + { + let mut file = check!(File::open(&filename)); + let mut buf: [MaybeUninit<u8>; 1] = [MaybeUninit::uninit()]; + let mut buf = BorrowedBuf::from(buf.as_mut_slice()); + + // Seek read + check!(file.seek_read_buf(buf.unfilled(), 8)); + assert_eq!(buf.filled(), b"8"); + assert_eq!(check!(file.stream_position()), 9); + + // Empty seek read + check!(file.seek_read_buf(buf.unfilled(), 0)); + assert_eq!(buf.filled(), b"8"); + + // Seek read past eof + check!(file.seek_read_buf(buf.clear().unfilled(), 10)); + assert_eq!(buf.filled(), b""); + } + check!(fs::remove_file(&filename)); +} + +#[test] fn file_test_read_buf() { let tmpdir = tmpdir(); let filename = &tmpdir.join("test"); diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 3771699c59f..97db0d6ab75 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -290,11 +290,11 @@ #![feature(doc_masked)] #![feature(doc_notable_trait)] #![feature(dropck_eyepatch)] -#![feature(extended_varargs_abi_support)] #![feature(f16)] #![feature(f128)] #![feature(ffi_const)] #![feature(formatting_options)] +#![feature(funnel_shifts)] #![feature(hash_map_internals)] #![feature(hash_map_macro)] #![feature(if_let_guard)] @@ -333,6 +333,7 @@ #![feature(char_internals)] #![feature(clone_to_uninit)] #![feature(const_cmp)] +#![feature(const_convert)] #![feature(const_ops)] #![feature(const_option_ops)] #![feature(const_try)] @@ -353,6 +354,7 @@ #![feature(hasher_prefixfree_extras)] #![feature(hashmap_internals)] #![feature(hint_must_use)] +#![feature(int_from_ascii)] #![feature(ip)] #![feature(lazy_get)] #![feature(maybe_uninit_slice)] @@ -368,6 +370,7 @@ #![feature(slice_internals)] #![feature(slice_ptr_get)] #![feature(slice_range)] +#![feature(slice_split_once)] #![feature(std_internals)] #![feature(str_internals)] #![feature(sync_unsafe_cell)] diff --git a/library/std/src/num/f128.rs b/library/std/src/num/f128.rs index 64e604e35f7..b83692390b6 100644 --- a/library/std/src/num/f128.rs +++ b/library/std/src/num/f128.rs @@ -467,10 +467,10 @@ impl f128 { /// # #[cfg(not(miri))] /// # #[cfg(target_has_reliable_f128_math)] { /// - /// let f = std::f128::consts::FRAC_PI_2; + /// let f = std::f128::consts::FRAC_PI_4; /// /// // asin(sin(pi/2)) - /// let abs_difference = (f.sin().asin() - std::f128::consts::FRAC_PI_2).abs(); + /// let abs_difference = (f.sin().asin() - f).abs(); /// /// assert!(abs_difference <= f128::EPSILON); /// # } @@ -912,10 +912,10 @@ impl f128 { /// # #[cfg(not(miri))] /// # #[cfg(target_has_reliable_f128_math)] { /// - /// let e = std::f128::consts::E; - /// let f = e.tanh().atanh(); + /// let x = std::f128::consts::FRAC_PI_6; + /// let f = x.tanh().atanh(); /// - /// let abs_difference = (f - e).abs(); + /// let abs_difference = (f - x).abs(); /// /// assert!(abs_difference <= 1e-5); /// # } diff --git a/library/std/src/num/f16.rs b/library/std/src/num/f16.rs index 7bdefb05858..5599528717c 100644 --- a/library/std/src/num/f16.rs +++ b/library/std/src/num/f16.rs @@ -432,10 +432,10 @@ impl f16 { /// # #[cfg(not(miri))] /// # #[cfg(target_has_reliable_f16_math)] { /// - /// let f = std::f16::consts::FRAC_PI_2; + /// let f = std::f16::consts::FRAC_PI_4; /// /// // asin(sin(pi/2)) - /// let abs_difference = (f.sin().asin() - std::f16::consts::FRAC_PI_2).abs(); + /// let abs_difference = (f.sin().asin() - f).abs(); /// /// assert!(abs_difference <= f16::EPSILON); /// # } @@ -877,10 +877,10 @@ impl f16 { /// # #[cfg(not(miri))] /// # #[cfg(target_has_reliable_f16_math)] { /// - /// let e = std::f16::consts::E; - /// let f = e.tanh().atanh(); + /// let x = std::f16::consts::FRAC_PI_6; + /// let f = x.tanh().atanh(); /// - /// let abs_difference = (f - e).abs(); + /// let abs_difference = (f - x).abs(); /// /// assert!(abs_difference <= 0.01); /// # } diff --git a/library/std/src/num/f32.rs b/library/std/src/num/f32.rs index 5dee68ad909..0247080a8d6 100644 --- a/library/std/src/num/f32.rs +++ b/library/std/src/num/f32.rs @@ -582,8 +582,8 @@ impl f32 { /// let abs_difference_x = (x.abs_sub(1.0) - 2.0).abs(); /// let abs_difference_y = (y.abs_sub(1.0) - 0.0).abs(); /// - /// assert!(abs_difference_x <= f32::EPSILON); - /// assert!(abs_difference_y <= f32::EPSILON); + /// assert!(abs_difference_x <= 1e-6); + /// assert!(abs_difference_y <= 1e-6); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] @@ -621,7 +621,7 @@ impl f32 { /// // x^(1/3) - 2 == 0 /// let abs_difference = (x.cbrt() - 2.0).abs(); /// - /// assert!(abs_difference <= f32::EPSILON); + /// assert!(abs_difference <= 1e-6); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] @@ -652,7 +652,7 @@ impl f32 { /// // sqrt(x^2 + y^2) /// let abs_difference = (x.hypot(y) - (x.powi(2) + y.powi(2)).sqrt()).abs(); /// - /// assert!(abs_difference <= 1e-6); + /// assert!(abs_difference <= 1e-5); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] @@ -725,7 +725,7 @@ impl f32 { /// let x = std::f32::consts::FRAC_PI_4; /// let abs_difference = (x.tan() - 1.0).abs(); /// - /// assert!(abs_difference <= f32::EPSILON); + /// assert!(abs_difference <= 1e-6); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] @@ -749,12 +749,12 @@ impl f32 { /// # Examples /// /// ``` - /// let f = std::f32::consts::FRAC_PI_2; + /// let f = std::f32::consts::FRAC_PI_4; /// /// // asin(sin(pi/2)) - /// let abs_difference = (f.sin().asin() - std::f32::consts::FRAC_PI_2).abs(); + /// let abs_difference = (f.sin().asin() - f).abs(); /// - /// assert!(abs_difference <= 1e-3); + /// assert!(abs_difference <= 1e-6); /// ``` #[doc(alias = "arcsin")] #[rustc_allow_incoherent_impl] @@ -813,7 +813,7 @@ impl f32 { /// // atan(tan(1)) /// let abs_difference = (f.tan().atan() - 1.0).abs(); /// - /// assert!(abs_difference <= f32::EPSILON); + /// assert!(abs_difference <= 1e-6); /// ``` #[doc(alias = "arctan")] #[rustc_allow_incoherent_impl] @@ -854,8 +854,8 @@ impl f32 { /// let abs_difference_1 = (y1.atan2(x1) - (-std::f32::consts::FRAC_PI_4)).abs(); /// let abs_difference_2 = (y2.atan2(x2) - (3.0 * std::f32::consts::FRAC_PI_4)).abs(); /// - /// assert!(abs_difference_1 <= f32::EPSILON); - /// assert!(abs_difference_2 <= f32::EPSILON); + /// assert!(abs_difference_1 <= 1e-5); + /// assert!(abs_difference_2 <= 1e-5); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] @@ -884,8 +884,8 @@ impl f32 { /// let abs_difference_0 = (f.0 - x.sin()).abs(); /// let abs_difference_1 = (f.1 - x.cos()).abs(); /// - /// assert!(abs_difference_0 <= 1e-6); - /// assert!(abs_difference_1 <= 1e-6); + /// assert!(abs_difference_0 <= 1e-4); + /// assert!(abs_difference_1 <= 1e-4); /// ``` #[doc(alias = "sincos")] #[rustc_allow_incoherent_impl] @@ -982,7 +982,7 @@ impl f32 { /// let g = ((e * e) - 1.0) / (2.0 * e); /// let abs_difference = (f - g).abs(); /// - /// assert!(abs_difference <= f32::EPSILON); + /// assert!(abs_difference <= 1e-6); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] @@ -1012,7 +1012,7 @@ impl f32 { /// let abs_difference = (f - g).abs(); /// /// // Same result - /// assert!(abs_difference <= f32::EPSILON); + /// assert!(abs_difference <= 1e-6); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] @@ -1042,7 +1042,7 @@ impl f32 { /// let g = (1.0 - e.powi(-2)) / (1.0 + e.powi(-2)); /// let abs_difference = (f - g).abs(); /// - /// assert!(abs_difference <= f32::EPSILON); + /// assert!(abs_difference <= 1e-6); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] @@ -1067,7 +1067,7 @@ impl f32 { /// /// let abs_difference = (f - x).abs(); /// - /// assert!(abs_difference <= 1e-7); + /// assert!(abs_difference <= 1e-6); /// ``` #[doc(alias = "arcsinh")] #[rustc_allow_incoherent_impl] @@ -1120,10 +1120,10 @@ impl f32 { /// # Examples /// /// ``` - /// let e = std::f32::consts::E; - /// let f = e.tanh().atanh(); + /// let x = std::f32::consts::FRAC_PI_6; + /// let f = x.tanh().atanh(); /// - /// let abs_difference = (f - e).abs(); + /// let abs_difference = (f - x).abs(); /// /// assert!(abs_difference <= 1e-5); /// ``` @@ -1153,7 +1153,7 @@ impl f32 { /// /// let abs_difference = (x.gamma() - 24.0).abs(); /// - /// assert!(abs_difference <= f32::EPSILON); + /// assert!(abs_difference <= 1e-5); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] @@ -1248,7 +1248,7 @@ impl f32 { /// let one = x.erf() + x.erfc(); /// let abs_difference = (one - 1.0).abs(); /// - /// assert!(abs_difference <= f32::EPSILON); + /// assert!(abs_difference <= 1e-6); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] diff --git a/library/std/src/num/f64.rs b/library/std/src/num/f64.rs index 3ec80f68bdb..1cfd3909d96 100644 --- a/library/std/src/num/f64.rs +++ b/library/std/src/num/f64.rs @@ -749,12 +749,12 @@ impl f64 { /// # Examples /// /// ``` - /// let f = std::f64::consts::FRAC_PI_2; + /// let f = std::f64::consts::FRAC_PI_4; /// /// // asin(sin(pi/2)) - /// let abs_difference = (f.sin().asin() - std::f64::consts::FRAC_PI_2).abs(); + /// let abs_difference = (f.sin().asin() - f).abs(); /// - /// assert!(abs_difference < 1e-7); + /// assert!(abs_difference < 1e-14); /// ``` #[doc(alias = "arcsin")] #[rustc_allow_incoherent_impl] @@ -1120,10 +1120,10 @@ impl f64 { /// # Examples /// /// ``` - /// let e = std::f64::consts::E; - /// let f = e.tanh().atanh(); + /// let x = std::f64::consts::FRAC_PI_6; + /// let f = x.tanh().atanh(); /// - /// let abs_difference = (f - e).abs(); + /// let abs_difference = (f - x).abs(); /// /// assert!(abs_difference < 1.0e-10); /// ``` @@ -1153,7 +1153,7 @@ impl f64 { /// /// let abs_difference = (x.gamma() - 24.0).abs(); /// - /// assert!(abs_difference <= f64::EPSILON); + /// assert!(abs_difference <= 1e-10); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] @@ -1248,7 +1248,7 @@ impl f64 { /// let one = x.erf() + x.erfc(); /// let abs_difference = (one - 1.0).abs(); /// - /// assert!(abs_difference <= f64::EPSILON); + /// assert!(abs_difference <= 1e-10); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] diff --git a/library/std/src/os/cygwin/mod.rs b/library/std/src/os/cygwin/mod.rs index 7f6d6a645c8..a295a07caac 100644 --- a/library/std/src/os/cygwin/mod.rs +++ b/library/std/src/os/cygwin/mod.rs @@ -1,4 +1,5 @@ //! Cygwin-specific definitions #![stable(feature = "raw_ext", since = "1.1.0")] pub mod fs; +pub mod net; pub(crate) mod raw; diff --git a/library/std/src/os/cygwin/net.rs b/library/std/src/os/cygwin/net.rs new file mode 100644 index 00000000000..0cccddb85d0 --- /dev/null +++ b/library/std/src/os/cygwin/net.rs @@ -0,0 +1,17 @@ +//! Cygwin-specific networking functionality. +//! +//! There are some limitations of Unix domain sockets on Cygwin: +//! * The syscalls `accept` and `connect` need +//! [handshake](https://inbox.sourceware.org/cygwin/Z_UERXFI1g-1v3p2@calimero.vinschen.de/T/#t). +//! * Cannot bind to abstract addr. +//! * Unbounded unix socket has an abstract local addr. +//! * Doesn't support recvmsg with control data. + +#![stable(feature = "unix_socket_abstract", since = "1.70.0")] + +#[stable(feature = "unix_socket_abstract", since = "1.70.0")] +pub use crate::os::net::linux_ext::addr::SocketAddrExt; +#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] +pub use crate::os::net::linux_ext::socket::UnixSocketExt; +#[stable(feature = "tcp_quickack", since = "1.89.0")] +pub use crate::os::net::linux_ext::tcp::TcpStreamExt; diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index ab7734a7952..96d9bfae8ca 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -185,5 +185,5 @@ pub mod xous; #[cfg(any(unix, target_os = "hermit", target_os = "trusty", target_os = "wasi", doc))] pub mod fd; -#[cfg(any(target_os = "linux", target_os = "android", doc))] +#[cfg(any(target_os = "linux", target_os = "android", target_os = "cygwin", doc))] mod net; diff --git a/library/std/src/os/net/linux_ext/mod.rs b/library/std/src/os/net/linux_ext/mod.rs index 3c9afe35479..f3f3fdd258c 100644 --- a/library/std/src/os/net/linux_ext/mod.rs +++ b/library/std/src/os/net/linux_ext/mod.rs @@ -1,6 +1,6 @@ -//! Linux and Android-specific networking functionality. +//! Linux, Android and Cygwin-specific networking functionality. -#![doc(cfg(any(target_os = "linux", target_os = "android")))] +#![doc(cfg(any(target_os = "linux", target_os = "android", target_os = "cygwin")))] #[stable(feature = "unix_socket_abstract", since = "1.70.0")] pub(crate) mod addr; diff --git a/library/std/src/os/net/mod.rs b/library/std/src/os/net/mod.rs index b7046dd7c59..47e69b3a260 100644 --- a/library/std/src/os/net/mod.rs +++ b/library/std/src/os/net/mod.rs @@ -9,5 +9,5 @@ all(target_vendor = "fortanix", target_env = "sgx") ) )))] -#[cfg(any(target_os = "linux", target_os = "android", doc))] +#[cfg(any(target_os = "linux", target_os = "android", target_os = "cygwin", doc))] pub(super) mod linux_ext; diff --git a/library/std/src/os/unix/fs.rs b/library/std/src/os/unix/fs.rs index b776df3dde1..1d1a138b302 100644 --- a/library/std/src/os/unix/fs.rs +++ b/library/std/src/os/unix/fs.rs @@ -11,6 +11,7 @@ use super::platform::fs::MetadataExt as _; // Used for `File::read` on intra-doc links use crate::ffi::OsStr; use crate::fs::{self, OpenOptions, Permissions}; +use crate::io::BorrowedCursor; use crate::os::unix::io::{AsFd, AsRawFd}; use crate::path::Path; use crate::sealed::Sealed; @@ -130,6 +131,91 @@ pub trait FileExt { if !buf.is_empty() { Err(io::Error::READ_EXACT_EOF) } else { Ok(()) } } + /// Reads some bytes starting from a given offset into the buffer. + /// + /// This equivalent to the [`read_at`](FileExt::read_at) method, except that it is passed a + /// [`BorrowedCursor`] rather than `&mut [u8]` to allow use with uninitialized buffers. The new + /// data will be appended to any existing contents of `buf`. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(core_io_borrowed_buf)] + /// #![feature(read_buf_at)] + /// + /// use std::io; + /// use std::io::BorrowedBuf; + /// use std::fs::File; + /// use std::mem::MaybeUninit; + /// use std::os::unix::prelude::*; + /// + /// fn main() -> io::Result<()> { + /// let mut file = File::open("pi.txt")?; + /// + /// // Read some bytes starting from offset 2 + /// let mut buf: [MaybeUninit<u8>; 10] = [MaybeUninit::uninit(); 10]; + /// let mut buf = BorrowedBuf::from(buf.as_mut_slice()); + /// file.read_buf_at(buf.unfilled(), 2)?; + /// + /// assert!(buf.filled().starts_with(b"1")); + /// + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "read_buf_at", issue = "140771")] + fn read_buf_at(&self, buf: BorrowedCursor<'_>, offset: u64) -> io::Result<()> { + io::default_read_buf(|b| self.read_at(b, offset), buf) + } + + /// Reads the exact number of bytes required to fill the buffer from a given offset. + /// + /// This is equivalent to the [`read_exact_at`](FileExt::read_exact_at) method, except that it + /// is passed a [`BorrowedCursor`] rather than `&mut [u8]` to allow use with uninitialized + /// buffers. The new data will be appended to any existing contents of `buf`. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(core_io_borrowed_buf)] + /// #![feature(read_buf_at)] + /// + /// use std::io; + /// use std::io::BorrowedBuf; + /// use std::fs::File; + /// use std::mem::MaybeUninit; + /// use std::os::unix::prelude::*; + /// + /// fn main() -> io::Result<()> { + /// let mut file = File::open("pi.txt")?; + /// + /// // Read exactly 10 bytes starting from offset 2 + /// let mut buf: [MaybeUninit<u8>; 10] = [MaybeUninit::uninit(); 10]; + /// let mut buf = BorrowedBuf::from(buf.as_mut_slice()); + /// file.read_buf_exact_at(buf.unfilled(), 2)?; + /// + /// assert_eq!(buf.filled(), b"1415926535"); + /// + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "read_buf_at", issue = "140771")] + fn read_buf_exact_at(&self, mut buf: BorrowedCursor<'_>, mut offset: u64) -> io::Result<()> { + while buf.capacity() > 0 { + let prev_written = buf.written(); + match self.read_buf_at(buf.reborrow(), offset) { + Ok(()) => {} + Err(e) if e.is_interrupted() => {} + Err(e) => return Err(e), + } + let n = buf.written() - prev_written; + offset += n as u64; + if n == 0 { + return Err(io::Error::READ_EXACT_EOF); + } + } + Ok(()) + } + /// Writes a number of bytes starting from a given offset. /// /// Returns the number of bytes written. @@ -264,6 +350,9 @@ impl FileExt for fs::File { fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> { self.as_inner().read_at(buf, offset) } + fn read_buf_at(&self, buf: BorrowedCursor<'_>, offset: u64) -> io::Result<()> { + self.as_inner().read_buf_at(buf, offset) + } fn read_vectored_at(&self, bufs: &mut [io::IoSliceMut<'_>], offset: u64) -> io::Result<usize> { self.as_inner().read_vectored_at(bufs, offset) } diff --git a/library/std/src/os/unix/net/addr.rs b/library/std/src/os/unix/net/addr.rs index fd6fe72dd24..25b95014e08 100644 --- a/library/std/src/os/unix/net/addr.rs +++ b/library/std/src/os/unix/net/addr.rs @@ -1,6 +1,6 @@ use crate::bstr::ByteStr; use crate::ffi::OsStr; -#[cfg(any(doc, target_os = "android", target_os = "linux"))] +#[cfg(any(doc, target_os = "android", target_os = "linux", target_os = "cygwin"))] use crate::os::net::linux_ext; use crate::os::unix::ffi::OsStrExt; use crate::path::Path; @@ -241,7 +241,7 @@ impl SocketAddr { // macOS seems to return a len of 16 and a zeroed sun_path for unnamed addresses if len == 0 - || (cfg!(not(any(target_os = "linux", target_os = "android"))) + || (cfg!(not(any(target_os = "linux", target_os = "android", target_os = "cygwin"))) && self.addr.sun_path[0] == 0) { AddressKind::Unnamed @@ -256,8 +256,8 @@ impl SocketAddr { #[stable(feature = "unix_socket_abstract", since = "1.70.0")] impl Sealed for SocketAddr {} -#[doc(cfg(any(target_os = "android", target_os = "linux")))] -#[cfg(any(doc, target_os = "android", target_os = "linux"))] +#[doc(cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin")))] +#[cfg(any(doc, target_os = "android", target_os = "linux", target_os = "cygwin"))] #[stable(feature = "unix_socket_abstract", since = "1.70.0")] impl linux_ext::addr::SocketAddrExt for SocketAddr { fn as_abstract_name(&self) -> Option<&[u8]> { diff --git a/library/std/src/os/unix/net/ancillary.rs b/library/std/src/os/unix/net/ancillary.rs index 36967fc3f98..d0984bdfb99 100644 --- a/library/std/src/os/unix/net/ancillary.rs +++ b/library/std/src/os/unix/net/ancillary.rs @@ -16,7 +16,8 @@ use crate::sys::net::Socket; not(target_os = "linux"), not(target_os = "android"), not(target_os = "netbsd"), - not(target_os = "freebsd") + not(target_os = "freebsd"), + not(target_os = "cygwin"), ))] #[allow(non_camel_case_types)] mod libc { @@ -195,14 +196,15 @@ impl<'a, T> Iterator for AncillaryDataIter<'a, T> { not(target_os = "android"), not(target_os = "linux"), not(target_os = "netbsd"), - not(target_os = "freebsd") + not(target_os = "freebsd"), + not(target_os = "cygwin"), ))] #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] #[derive(Clone)] pub struct SocketCred(()); /// Unix credential. -#[cfg(any(target_os = "android", target_os = "linux",))] +#[cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin"))] #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] #[derive(Clone)] pub struct SocketCred(libc::ucred); @@ -217,8 +219,8 @@ pub struct SocketCred(libc::sockcred); #[derive(Clone)] pub struct SocketCred(libc::sockcred2); -#[doc(cfg(any(target_os = "android", target_os = "linux")))] -#[cfg(any(target_os = "android", target_os = "linux"))] +#[doc(cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin")))] +#[cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin"))] impl SocketCred { /// Creates a Unix credential struct. /// @@ -407,7 +409,8 @@ impl<'a> Iterator for ScmRights<'a> { not(target_os = "android"), not(target_os = "linux"), not(target_os = "netbsd"), - not(target_os = "freebsd") + not(target_os = "freebsd"), + not(target_os = "cygwin"), ))] #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] pub struct ScmCredentials<'a>(AncillaryDataIter<'a, ()>); @@ -415,7 +418,7 @@ pub struct ScmCredentials<'a>(AncillaryDataIter<'a, ()>); /// This control message contains unix credentials. /// /// The level is equal to `SOL_SOCKET` and the type is equal to `SCM_CREDENTIALS` or `SCM_CREDS`. -#[cfg(any(target_os = "android", target_os = "linux",))] +#[cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin"))] #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] pub struct ScmCredentials<'a>(AncillaryDataIter<'a, libc::ucred>); @@ -432,7 +435,8 @@ pub struct ScmCredentials<'a>(AncillaryDataIter<'a, libc::sockcred>); target_os = "android", target_os = "linux", target_os = "netbsd", - target_os = "freebsd" + target_os = "freebsd", + target_os = "cygwin", ))] #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] impl<'a> Iterator for ScmCredentials<'a> { @@ -460,7 +464,8 @@ pub enum AncillaryData<'a> { target_os = "android", target_os = "linux", target_os = "netbsd", - target_os = "freebsd" + target_os = "freebsd", + target_os = "cygwin", ))] ScmCredentials(ScmCredentials<'a>), } @@ -489,7 +494,8 @@ impl<'a> AncillaryData<'a> { target_os = "android", target_os = "linux", target_os = "netbsd", - target_os = "freebsd" + target_os = "freebsd", + target_os = "cygwin", ))] unsafe fn as_credentials(data: &'a [u8]) -> Self { let ancillary_data_iter = AncillaryDataIter::new(data); @@ -507,7 +513,7 @@ impl<'a> AncillaryData<'a> { match (*cmsg).cmsg_level { libc::SOL_SOCKET => match (*cmsg).cmsg_type { libc::SCM_RIGHTS => Ok(AncillaryData::as_rights(data)), - #[cfg(any(target_os = "android", target_os = "linux",))] + #[cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin"))] libc::SCM_CREDENTIALS => Ok(AncillaryData::as_credentials(data)), #[cfg(target_os = "freebsd")] libc::SCM_CREDS2 => Ok(AncillaryData::as_credentials(data)), @@ -729,7 +735,8 @@ impl<'a> SocketAncillary<'a> { target_os = "android", target_os = "linux", target_os = "netbsd", - target_os = "freebsd" + target_os = "freebsd", + target_os = "cygwin", ))] #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] pub fn add_creds(&mut self, creds: &[SocketCred]) -> bool { diff --git a/library/std/src/os/unix/net/datagram.rs b/library/std/src/os/unix/net/datagram.rs index 7735637c840..469bfbb0d83 100644 --- a/library/std/src/os/unix/net/datagram.rs +++ b/library/std/src/os/unix/net/datagram.rs @@ -14,9 +14,9 @@ use libc::MSG_NOSIGNAL; use super::{SocketAddr, sockaddr_un}; -#[cfg(any(doc, target_os = "android", target_os = "linux"))] +#[cfg(any(doc, target_os = "android", target_os = "linux", target_os = "cygwin"))] use super::{SocketAncillary, recv_vectored_with_ancillary_from, send_vectored_with_ancillary_to}; -#[cfg(any(doc, target_os = "android", target_os = "linux"))] +#[cfg(any(doc, target_os = "android", target_os = "linux", target_os = "cygwin"))] use crate::io::{IoSlice, IoSliceMut}; use crate::net::Shutdown; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; @@ -397,8 +397,14 @@ impl UnixDatagram { /// /// # Examples /// - #[cfg_attr(any(target_os = "android", target_os = "linux"), doc = "```no_run")] - #[cfg_attr(not(any(target_os = "android", target_os = "linux")), doc = "```ignore")] + #[cfg_attr( + any(target_os = "android", target_os = "linux", target_os = "cygwin"), + doc = "```no_run" + )] + #[cfg_attr( + not(any(target_os = "android", target_os = "linux", target_os = "cygwin")), + doc = "```ignore" + )] /// #![feature(unix_socket_ancillary_data)] /// use std::os::unix::net::{UnixDatagram, SocketAncillary, AncillaryData}; /// use std::io::IoSliceMut; @@ -428,7 +434,7 @@ impl UnixDatagram { /// Ok(()) /// } /// ``` - #[cfg(any(doc, target_os = "android", target_os = "linux"))] + #[cfg(any(doc, target_os = "android", target_os = "linux", target_os = "cygwin"))] #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] pub fn recv_vectored_with_ancillary_from( &self, @@ -447,8 +453,14 @@ impl UnixDatagram { /// /// # Examples /// - #[cfg_attr(any(target_os = "android", target_os = "linux"), doc = "```no_run")] - #[cfg_attr(not(any(target_os = "android", target_os = "linux")), doc = "```ignore")] + #[cfg_attr( + any(target_os = "android", target_os = "linux", target_os = "cygwin"), + doc = "```no_run" + )] + #[cfg_attr( + not(any(target_os = "android", target_os = "linux", target_os = "cygwin")), + doc = "```ignore" + )] /// #![feature(unix_socket_ancillary_data)] /// use std::os::unix::net::{UnixDatagram, SocketAncillary, AncillaryData}; /// use std::io::IoSliceMut; @@ -478,7 +490,7 @@ impl UnixDatagram { /// Ok(()) /// } /// ``` - #[cfg(any(doc, target_os = "android", target_os = "linux"))] + #[cfg(any(doc, target_os = "android", target_os = "linux", target_os = "cygwin"))] #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] pub fn recv_vectored_with_ancillary( &self, @@ -588,8 +600,14 @@ impl UnixDatagram { /// /// # Examples /// - #[cfg_attr(any(target_os = "android", target_os = "linux"), doc = "```no_run")] - #[cfg_attr(not(any(target_os = "android", target_os = "linux")), doc = "```ignore")] + #[cfg_attr( + any(target_os = "android", target_os = "linux", target_os = "cygwin"), + doc = "```no_run" + )] + #[cfg_attr( + not(any(target_os = "android", target_os = "linux", target_os = "cygwin")), + doc = "```ignore" + )] /// #![feature(unix_socket_ancillary_data)] /// use std::os::unix::net::{UnixDatagram, SocketAncillary}; /// use std::io::IoSlice; @@ -613,7 +631,7 @@ impl UnixDatagram { /// Ok(()) /// } /// ``` - #[cfg(any(doc, target_os = "android", target_os = "linux"))] + #[cfg(any(doc, target_os = "android", target_os = "linux", target_os = "cygwin"))] #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] pub fn send_vectored_with_ancillary_to<P: AsRef<Path>>( &self, @@ -630,8 +648,14 @@ impl UnixDatagram { /// /// # Examples /// - #[cfg_attr(any(target_os = "android", target_os = "linux"), doc = "```no_run")] - #[cfg_attr(not(any(target_os = "android", target_os = "linux")), doc = "```ignore")] + #[cfg_attr( + any(target_os = "android", target_os = "linux", target_os = "cygwin"), + doc = "```no_run" + )] + #[cfg_attr( + not(any(target_os = "android", target_os = "linux", target_os = "cygwin")), + doc = "```ignore" + )] /// #![feature(unix_socket_ancillary_data)] /// use std::os::unix::net::{UnixDatagram, SocketAncillary}; /// use std::io::IoSlice; @@ -655,7 +679,7 @@ impl UnixDatagram { /// Ok(()) /// } /// ``` - #[cfg(any(doc, target_os = "android", target_os = "linux"))] + #[cfg(any(doc, target_os = "android", target_os = "linux", target_os = "cygwin"))] #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] pub fn send_vectored_with_ancillary( &self, diff --git a/library/std/src/os/unix/net/mod.rs b/library/std/src/os/unix/net/mod.rs index 6cd62303a53..94523d7d1e4 100644 --- a/library/std/src/os/unix/net/mod.rs +++ b/library/std/src/os/unix/net/mod.rs @@ -4,8 +4,8 @@ #![stable(feature = "unix_socket", since = "1.10.0")] mod addr; -#[doc(cfg(any(target_os = "android", target_os = "linux")))] -#[cfg(any(doc, target_os = "android", target_os = "linux"))] +#[doc(cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin")))] +#[cfg(any(doc, target_os = "android", target_os = "linux", target_os = "cygwin"))] mod ancillary; mod datagram; mod listener; @@ -27,7 +27,7 @@ mod ucred; #[stable(feature = "unix_socket", since = "1.10.0")] pub use self::addr::*; -#[cfg(any(doc, target_os = "android", target_os = "linux"))] +#[cfg(any(doc, target_os = "android", target_os = "linux", target_os = "cygwin"))] #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] pub use self::ancillary::*; #[stable(feature = "unix_socket", since = "1.10.0")] diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs index 768fa77a5f8..ea4171a7d28 100644 --- a/library/std/src/os/unix/net/stream.rs +++ b/library/std/src/os/unix/net/stream.rs @@ -16,7 +16,7 @@ cfg_select! { } use super::{SocketAddr, sockaddr_un}; -#[cfg(any(doc, target_os = "android", target_os = "linux"))] +#[cfg(any(doc, target_os = "android", target_os = "linux", target_os = "cygwin"))] use super::{SocketAncillary, recv_vectored_with_ancillary_from, send_vectored_with_ancillary_to}; #[cfg(any( target_os = "android", @@ -508,8 +508,14 @@ impl UnixStream { /// /// # Examples /// - #[cfg_attr(any(target_os = "android", target_os = "linux"), doc = "```no_run")] - #[cfg_attr(not(any(target_os = "android", target_os = "linux")), doc = "```ignore")] + #[cfg_attr( + any(target_os = "android", target_os = "linux", target_os = "cygwin"), + doc = "```no_run" + )] + #[cfg_attr( + not(any(target_os = "android", target_os = "linux", target_os = "cygwin")), + doc = "```ignore" + )] /// #![feature(unix_socket_ancillary_data)] /// use std::os::unix::net::{UnixStream, SocketAncillary, AncillaryData}; /// use std::io::IoSliceMut; @@ -539,7 +545,7 @@ impl UnixStream { /// Ok(()) /// } /// ``` - #[cfg(any(doc, target_os = "android", target_os = "linux"))] + #[cfg(any(doc, target_os = "android", target_os = "linux", target_os = "cygwin"))] #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] pub fn recv_vectored_with_ancillary( &self, @@ -557,8 +563,14 @@ impl UnixStream { /// /// # Examples /// - #[cfg_attr(any(target_os = "android", target_os = "linux"), doc = "```no_run")] - #[cfg_attr(not(any(target_os = "android", target_os = "linux")), doc = "```ignore")] + #[cfg_attr( + any(target_os = "android", target_os = "linux", target_os = "cygwin"), + doc = "```no_run" + )] + #[cfg_attr( + not(any(target_os = "android", target_os = "linux", target_os = "cygwin")), + doc = "```ignore" + )] /// #![feature(unix_socket_ancillary_data)] /// use std::os::unix::net::{UnixStream, SocketAncillary}; /// use std::io::IoSlice; @@ -582,7 +594,7 @@ impl UnixStream { /// Ok(()) /// } /// ``` - #[cfg(any(doc, target_os = "android", target_os = "linux"))] + #[cfg(any(doc, target_os = "android", target_os = "linux", target_os = "cygwin"))] #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] pub fn send_vectored_with_ancillary( &self, diff --git a/library/std/src/os/unix/net/tests.rs b/library/std/src/os/unix/net/tests.rs index 9a88687b1df..4666b5e3c6c 100644 --- a/library/std/src/os/unix/net/tests.rs +++ b/library/std/src/os/unix/net/tests.rs @@ -3,6 +3,8 @@ use crate::io::prelude::*; use crate::io::{self, ErrorKind, IoSlice, IoSliceMut}; #[cfg(target_os = "android")] use crate::os::android::net::{SocketAddrExt, UnixSocketExt}; +#[cfg(target_os = "cygwin")] +use crate::os::cygwin::net::{SocketAddrExt, UnixSocketExt}; #[cfg(target_os = "linux")] use crate::os::linux::net::{SocketAddrExt, UnixSocketExt}; #[cfg(any(target_os = "android", target_os = "linux"))] @@ -170,6 +172,7 @@ fn long_path() { #[test] #[cfg(not(target_os = "nto"))] #[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets +#[cfg_attr(target_os = "cygwin", ignore)] // Cygwin connect needs handshake fn timeouts() { let dir = tmpdir(); let socket_path = dir.path().join("sock"); @@ -198,6 +201,7 @@ fn timeouts() { #[test] #[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets +#[cfg_attr(target_os = "cygwin", ignore)] // Cygwin connect needs handshake fn test_read_timeout() { let dir = tmpdir(); let socket_path = dir.path().join("sock"); @@ -218,6 +222,7 @@ fn test_read_timeout() { #[test] #[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets +#[cfg_attr(target_os = "cygwin", ignore)] // Cygwin connect needs handshake fn test_read_with_timeout() { let dir = tmpdir(); let socket_path = dir.path().join("sock"); @@ -246,6 +251,7 @@ fn test_read_with_timeout() { // when passed zero Durations #[test] #[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets +#[cfg_attr(target_os = "cygwin", ignore)] // Cygwin connect needs handshake fn test_unix_stream_timeout_zero_duration() { let dir = tmpdir(); let socket_path = dir.path().join("sock"); @@ -283,6 +289,7 @@ fn test_unix_datagram() { #[test] #[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets +#[cfg_attr(target_os = "cygwin", ignore)] // Cygwin autobinds an address fn test_unnamed_unix_datagram() { let dir = tmpdir(); let path1 = dir.path().join("sock1"); @@ -326,6 +333,7 @@ fn test_unix_datagram_connect_to_recv_addr() { #[test] #[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets +#[cfg_attr(target_os = "cygwin", ignore)] // Cygwin autobinds an address fn test_connect_unix_datagram() { let dir = tmpdir(); let path1 = dir.path().join("sock1"); @@ -425,8 +433,9 @@ fn abstract_namespace_not_allowed_connect() { assert!(UnixStream::connect("\0asdf").is_err()); } -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin"))] #[test] +#[cfg_attr(target_os = "cygwin", ignore)] // Cygwin cannot bind to abstract addr fn test_abstract_stream_connect() { let msg1 = b"hello"; let msg2 = b"world"; @@ -456,8 +465,9 @@ fn test_abstract_stream_connect() { thread.join().unwrap(); } -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin"))] #[test] +#[cfg_attr(target_os = "cygwin", ignore)] // Cygwin cannot bind to abstract addr fn test_abstract_stream_iter() { let addr = or_panic!(SocketAddr::from_abstract_name(b"hidden")); let listener = or_panic!(UnixListener::bind_addr(&addr)); @@ -478,8 +488,9 @@ fn test_abstract_stream_iter() { thread.join().unwrap(); } -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin"))] #[test] +#[cfg_attr(target_os = "cygwin", ignore)] // Cygwin cannot bind to abstract addr fn test_abstract_datagram_bind_send_to_addr() { let addr1 = or_panic!(SocketAddr::from_abstract_name(b"ns1")); let sock1 = or_panic!(UnixDatagram::bind_addr(&addr1)); @@ -499,8 +510,9 @@ fn test_abstract_datagram_bind_send_to_addr() { assert_eq!(addr.as_abstract_name().unwrap(), b"ns1"); } -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin"))] #[test] +#[cfg_attr(target_os = "cygwin", ignore)] // Cygwin cannot bind to abstract addr fn test_abstract_datagram_connect_addr() { let addr1 = or_panic!(SocketAddr::from_abstract_name(b"ns3")); let bsock1 = or_panic!(UnixDatagram::bind_addr(&addr1)); @@ -524,7 +536,7 @@ fn test_abstract_datagram_connect_addr() { or_panic!(bsock2.recv_from(&mut buf)); } -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin"))] #[test] fn test_abstract_name_too_long() { match SocketAddr::from_abstract_name( @@ -538,7 +550,7 @@ fn test_abstract_name_too_long() { } } -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin"))] #[test] fn test_abstract_no_pathname_and_not_unnamed() { let name = b"local"; @@ -669,9 +681,10 @@ fn test_send_vectored_fds_unix_stream() { } } -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin"))] #[test] #[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets +#[cfg_attr(target_os = "cygwin", ignore)] // Cygwin recvmsg doesn't support Unix sockets fn test_send_vectored_with_ancillary_to_unix_datagram() { fn getpid() -> libc::pid_t { unsafe { libc::getpid() } diff --git a/library/std/src/os/windows/fs.rs b/library/std/src/os/windows/fs.rs index ddb8dbd8fee..b445f368aeb 100644 --- a/library/std/src/os/windows/fs.rs +++ b/library/std/src/os/windows/fs.rs @@ -5,6 +5,7 @@ #![stable(feature = "rust1", since = "1.0.0")] use crate::fs::{self, Metadata, OpenOptions}; +use crate::io::BorrowedCursor; use crate::path::Path; use crate::sealed::Sealed; use crate::sys_common::{AsInner, AsInnerMut, IntoInner}; @@ -49,6 +50,44 @@ pub trait FileExt { #[stable(feature = "file_offset", since = "1.15.0")] fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>; + /// Seeks to a given position and reads some bytes into the buffer. + /// + /// This is equivalent to the [`seek_read`](FileExt::seek_read) method, except that it is passed + /// a [`BorrowedCursor`] rather than `&mut [u8]` to allow use with uninitialized buffers. The + /// new data will be appended to any existing contents of `buf`. + /// + /// Reading beyond the end of the file will always succeed without reading any bytes. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(core_io_borrowed_buf)] + /// #![feature(read_buf_at)] + /// + /// use std::io; + /// use std::io::BorrowedBuf; + /// use std::fs::File; + /// use std::mem::MaybeUninit; + /// use std::os::windows::prelude::*; + /// + /// fn main() -> io::Result<()> { + /// let mut file = File::open("pi.txt")?; + /// + /// // Read some bytes starting from offset 2 + /// let mut buf: [MaybeUninit<u8>; 10] = [MaybeUninit::uninit(); 10]; + /// let mut buf = BorrowedBuf::from(buf.as_mut_slice()); + /// file.seek_read_buf(buf.unfilled(), 2)?; + /// + /// assert!(buf.filled().starts_with(b"1")); + /// + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "read_buf_at", issue = "140771")] + fn seek_read_buf(&self, buf: BorrowedCursor<'_>, offset: u64) -> io::Result<()> { + io::default_read_buf(|b| self.seek_read(b, offset), buf) + } + /// Seeks to a given position and writes a number of bytes. /// /// Returns the number of bytes written. @@ -89,6 +128,10 @@ impl FileExt for fs::File { self.as_inner().read_at(buf, offset) } + fn seek_read_buf(&self, buf: BorrowedCursor<'_>, offset: u64) -> io::Result<()> { + self.as_inner().read_buf_at(buf, offset) + } + fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result<usize> { self.as_inner().write_at(buf, offset) } diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 87a3fc80dfa..8b7282c51d1 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -819,7 +819,7 @@ fn panic_with_hook( rtprintpanic!("aborting due to panic at {location}:\n{payload}\n"); } } - crate::sys::abort_internal(); + crate::process::abort(); } match *HOOK.read().unwrap_or_else(PoisonError::into_inner) { @@ -853,7 +853,7 @@ fn panic_with_hook( // through a nounwind function (e.g. extern "C") then we cannot continue // unwinding and have to abort immediately. rtprintpanic!("thread caused non-unwinding panic. aborting.\n"); - crate::sys::abort_internal(); + crate::process::abort(); } rust_panic(payload) diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 470d300d2d9..19663e4a9df 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -1575,8 +1575,6 @@ impl PathBuf { /// # Examples /// /// ``` - /// #![feature(path_add_extension)] - /// /// use std::path::{Path, PathBuf}; /// /// let mut p = PathBuf::from("/feel/the"); @@ -1596,7 +1594,7 @@ impl PathBuf { /// p.add_extension(""); /// assert_eq!(Path::new("/feel/the.formatted.dark"), p.as_path()); /// ``` - #[unstable(feature = "path_add_extension", issue = "127292")] + #[stable(feature = "path_add_extension", since = "CURRENT_RUSTC_VERSION")] pub fn add_extension<S: AsRef<OsStr>>(&mut self, extension: S) -> bool { self._add_extension(extension.as_ref()) } @@ -2109,7 +2107,7 @@ impl PartialEq for PathBuf { impl cmp::PartialEq<str> for PathBuf { #[inline] fn eq(&self, other: &str) -> bool { - &*self == other + Path::eq(self, other) } } @@ -2264,11 +2262,13 @@ impl Path { /// assert_eq!(from_string, from_path); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn new<S: AsRef<OsStr> + ?Sized>(s: &S) -> &Path { + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] + pub const fn new<S: [const] AsRef<OsStr> + ?Sized>(s: &S) -> &Path { unsafe { &*(s.as_ref() as *const OsStr as *const Path) } } - fn from_inner_mut(inner: &mut OsStr) -> &mut Path { + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] + const fn from_inner_mut(inner: &mut OsStr) -> &mut Path { // SAFETY: Path is just a wrapper around OsStr, // therefore converting &mut OsStr to &mut Path is safe. unsafe { &mut *(inner as *mut OsStr as *mut Path) } @@ -2878,8 +2878,6 @@ impl Path { /// # Examples /// /// ``` - /// #![feature(path_add_extension)] - /// /// use std::path::{Path, PathBuf}; /// /// let path = Path::new("foo.rs"); @@ -2890,7 +2888,7 @@ impl Path { /// assert_eq!(path.with_added_extension("xz"), PathBuf::from("foo.tar.gz.xz")); /// assert_eq!(path.with_added_extension("").with_added_extension("txt"), PathBuf::from("foo.tar.gz.txt")); /// ``` - #[unstable(feature = "path_add_extension", issue = "127292")] + #[stable(feature = "path_add_extension", since = "CURRENT_RUSTC_VERSION")] pub fn with_added_extension<S: AsRef<OsStr>>(&self, extension: S) -> PathBuf { let mut new_path = self.to_path_buf(); new_path.add_extension(extension); @@ -3337,7 +3335,8 @@ unsafe impl CloneToUninit for Path { } #[stable(feature = "rust1", since = "1.0.0")] -impl AsRef<OsStr> for Path { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl const AsRef<OsStr> for Path { #[inline] fn as_ref(&self) -> &OsStr { &self.inner @@ -3507,7 +3506,8 @@ impl Ord for Path { } #[stable(feature = "rust1", since = "1.0.0")] -impl AsRef<Path> for Path { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl const AsRef<Path> for Path { #[inline] fn as_ref(&self) -> &Path { self @@ -3515,7 +3515,8 @@ impl AsRef<Path> for Path { } #[stable(feature = "rust1", since = "1.0.0")] -impl AsRef<Path> for OsStr { +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl const AsRef<Path> for OsStr { #[inline] fn as_ref(&self) -> &Path { Path::new(self) diff --git a/library/std/src/process.rs b/library/std/src/process.rs index 373584d0117..48265de90c4 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -2495,6 +2495,7 @@ pub fn exit(code: i32) -> ! { #[stable(feature = "process_abort", since = "1.17.0")] #[cold] #[cfg_attr(not(test), rustc_diagnostic_item = "process_abort")] +#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn abort() -> ! { crate::sys::abort_internal(); } diff --git a/library/std/src/sys/args/mod.rs b/library/std/src/sys/args/mod.rs index c9627322276..e11e8e5430f 100644 --- a/library/std/src/sys/args/mod.rs +++ b/library/std/src/sys/args/mod.rs @@ -32,9 +32,13 @@ cfg_select! { mod uefi; pub use uefi::*; } - target_os = "wasi" => { - mod wasi; - pub use wasi::*; + all(target_os = "wasi", target_env = "p1") => { + mod wasip1; + pub use wasip1::*; + } + all(target_os = "wasi", target_env = "p2") => { + mod wasip2; + pub use wasip2::*; } target_os = "xous" => { mod xous; diff --git a/library/std/src/sys/args/wasi.rs b/library/std/src/sys/args/wasip1.rs index 72063a87dc9..72063a87dc9 100644 --- a/library/std/src/sys/args/wasi.rs +++ b/library/std/src/sys/args/wasip1.rs diff --git a/library/std/src/sys/args/wasip2.rs b/library/std/src/sys/args/wasip2.rs new file mode 100644 index 00000000000..a57e4b97786 --- /dev/null +++ b/library/std/src/sys/args/wasip2.rs @@ -0,0 +1,6 @@ +pub use super::common::Args; + +/// Returns the command line arguments +pub fn args() -> Args { + Args::new(wasip2::cli::environment::get_arguments().into_iter().map(|arg| arg.into()).collect()) +} diff --git a/library/std/src/sys/fd/unix.rs b/library/std/src/sys/fd/unix.rs index a12f692e754..2b2dfe48e89 100644 --- a/library/std/src/sys/fd/unix.rs +++ b/library/std/src/sys/fd/unix.rs @@ -18,6 +18,21 @@ use libc::off_t as off64_t; ))] use libc::off64_t; +cfg_select! { + any( + all(target_os = "linux", not(target_env = "musl")), + target_os = "android", + target_os = "hurd", + ) => { + // Prefer explicit pread64 for 64-bit offset independently of libc + // #[cfg(gnu_file_offset_bits64)]. + use libc::pread64; + } + _ => { + use libc::pread as pread64; + } +} + use crate::cmp; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read}; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; @@ -146,42 +161,47 @@ impl FileDesc { (&mut me).read_to_end(buf) } - #[cfg_attr(target_os = "vxworks", allow(unused_unsafe))] pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> { - #[cfg(not(any( - all(target_os = "linux", not(target_env = "musl")), - target_os = "android", - target_os = "hurd" - )))] - use libc::pread as pread64; - #[cfg(any( - all(target_os = "linux", not(target_env = "musl")), - target_os = "android", - target_os = "hurd" - ))] - use libc::pread64; - - unsafe { - cvt(pread64( + cvt(unsafe { + pread64( self.as_raw_fd(), buf.as_mut_ptr() as *mut libc::c_void, cmp::min(buf.len(), READ_LIMIT), - offset as off64_t, - )) - .map(|n| n as usize) - } + offset as off64_t, // EINVAL if offset + count overflows + ) + }) + .map(|n| n as usize) } pub fn read_buf(&self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { + // SAFETY: `cursor.as_mut()` starts with `cursor.capacity()` writable bytes let ret = cvt(unsafe { libc::read( self.as_raw_fd(), - cursor.as_mut().as_mut_ptr() as *mut libc::c_void, + cursor.as_mut().as_mut_ptr().cast::<libc::c_void>(), + cmp::min(cursor.capacity(), READ_LIMIT), + ) + })?; + + // SAFETY: `ret` bytes were written to the initialized portion of the buffer + unsafe { + cursor.advance_unchecked(ret as usize); + } + Ok(()) + } + + pub fn read_buf_at(&self, mut cursor: BorrowedCursor<'_>, offset: u64) -> io::Result<()> { + // SAFETY: `cursor.as_mut()` starts with `cursor.capacity()` writable bytes + let ret = cvt(unsafe { + pread64( + self.as_raw_fd(), + cursor.as_mut().as_mut_ptr().cast::<libc::c_void>(), cmp::min(cursor.capacity(), READ_LIMIT), + offset as off64_t, // EINVAL if offset + count overflows ) })?; - // Safety: `ret` bytes were written to the initialized portion of the buffer + // SAFETY: `ret` bytes were written to the initialized portion of the buffer unsafe { cursor.advance_unchecked(ret as usize); } @@ -369,7 +389,6 @@ impl FileDesc { ))) } - #[cfg_attr(target_os = "vxworks", allow(unused_unsafe))] pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> { #[cfg(not(any( all(target_os = "linux", not(target_env = "musl")), diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index a89c3bbacfb..dfd6ce56a76 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -1293,6 +1293,15 @@ impl File { return Ok(()); } + #[cfg(target_os = "solaris")] + pub fn lock(&self) -> io::Result<()> { + let mut flock: libc::flock = unsafe { mem::zeroed() }; + flock.l_type = libc::F_WRLCK as libc::c_short; + flock.l_whence = libc::SEEK_SET as libc::c_short; + cvt(unsafe { libc::fcntl(self.as_raw_fd(), libc::F_SETLKW, &flock) })?; + Ok(()) + } + #[cfg(not(any( target_os = "freebsd", target_os = "fuchsia", @@ -1300,6 +1309,7 @@ impl File { target_os = "netbsd", target_os = "openbsd", target_os = "cygwin", + target_os = "solaris", target_vendor = "apple", )))] pub fn lock(&self) -> io::Result<()> { @@ -1320,6 +1330,15 @@ impl File { return Ok(()); } + #[cfg(target_os = "solaris")] + pub fn lock_shared(&self) -> io::Result<()> { + let mut flock: libc::flock = unsafe { mem::zeroed() }; + flock.l_type = libc::F_RDLCK as libc::c_short; + flock.l_whence = libc::SEEK_SET as libc::c_short; + cvt(unsafe { libc::fcntl(self.as_raw_fd(), libc::F_SETLKW, &flock) })?; + Ok(()) + } + #[cfg(not(any( target_os = "freebsd", target_os = "fuchsia", @@ -1327,6 +1346,7 @@ impl File { target_os = "netbsd", target_os = "openbsd", target_os = "cygwin", + target_os = "solaris", target_vendor = "apple", )))] pub fn lock_shared(&self) -> io::Result<()> { @@ -1355,6 +1375,23 @@ impl File { } } + #[cfg(target_os = "solaris")] + pub fn try_lock(&self) -> Result<(), TryLockError> { + let mut flock: libc::flock = unsafe { mem::zeroed() }; + flock.l_type = libc::F_WRLCK as libc::c_short; + flock.l_whence = libc::SEEK_SET as libc::c_short; + let result = cvt(unsafe { libc::fcntl(self.as_raw_fd(), libc::F_SETLK, &flock) }); + if let Err(err) = result { + if err.kind() == io::ErrorKind::WouldBlock { + Err(TryLockError::WouldBlock) + } else { + Err(TryLockError::Error(err)) + } + } else { + Ok(()) + } + } + #[cfg(not(any( target_os = "freebsd", target_os = "fuchsia", @@ -1362,6 +1399,7 @@ impl File { target_os = "netbsd", target_os = "openbsd", target_os = "cygwin", + target_os = "solaris", target_vendor = "apple", )))] pub fn try_lock(&self) -> Result<(), TryLockError> { @@ -1393,6 +1431,23 @@ impl File { } } + #[cfg(target_os = "solaris")] + pub fn try_lock_shared(&self) -> Result<(), TryLockError> { + let mut flock: libc::flock = unsafe { mem::zeroed() }; + flock.l_type = libc::F_RDLCK as libc::c_short; + flock.l_whence = libc::SEEK_SET as libc::c_short; + let result = cvt(unsafe { libc::fcntl(self.as_raw_fd(), libc::F_SETLK, &flock) }); + if let Err(err) = result { + if err.kind() == io::ErrorKind::WouldBlock { + Err(TryLockError::WouldBlock) + } else { + Err(TryLockError::Error(err)) + } + } else { + Ok(()) + } + } + #[cfg(not(any( target_os = "freebsd", target_os = "fuchsia", @@ -1400,6 +1455,7 @@ impl File { target_os = "netbsd", target_os = "openbsd", target_os = "cygwin", + target_os = "solaris", target_vendor = "apple", )))] pub fn try_lock_shared(&self) -> Result<(), TryLockError> { @@ -1423,6 +1479,15 @@ impl File { return Ok(()); } + #[cfg(target_os = "solaris")] + pub fn unlock(&self) -> io::Result<()> { + let mut flock: libc::flock = unsafe { mem::zeroed() }; + flock.l_type = libc::F_UNLCK as libc::c_short; + flock.l_whence = libc::SEEK_SET as libc::c_short; + cvt(unsafe { libc::fcntl(self.as_raw_fd(), libc::F_SETLKW, &flock) })?; + Ok(()) + } + #[cfg(not(any( target_os = "freebsd", target_os = "fuchsia", @@ -1430,6 +1495,7 @@ impl File { target_os = "netbsd", target_os = "openbsd", target_os = "cygwin", + target_os = "solaris", target_vendor = "apple", )))] pub fn unlock(&self) -> io::Result<()> { @@ -1463,6 +1529,10 @@ impl File { self.0.read_buf(cursor) } + pub fn read_buf_at(&self, cursor: BorrowedCursor<'_>, offset: u64) -> io::Result<()> { + self.0.read_buf_at(cursor, offset) + } + pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> { self.0.read_vectored_at(bufs, offset) } diff --git a/library/std/src/sys/fs/windows.rs b/library/std/src/sys/fs/windows.rs index bac278f7c8f..ccfe410627f 100644 --- a/library/std/src/sys/fs/windows.rs +++ b/library/std/src/sys/fs/windows.rs @@ -605,6 +605,10 @@ impl File { self.handle.read_buf(cursor) } + pub fn read_buf_at(&self, cursor: BorrowedCursor<'_>, offset: u64) -> io::Result<()> { + self.handle.read_buf_at(cursor, offset) + } + pub fn write(&self, buf: &[u8]) -> io::Result<usize> { self.handle.write(buf) } diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index 6324c1a232a..8c115015580 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -26,6 +26,7 @@ pub mod io; pub mod net; pub mod os_str; pub mod path; +pub mod platform_version; pub mod process; pub mod random; pub mod stdio; diff --git a/library/std/src/sys/net/connection/socket/unix.rs b/library/std/src/sys/net/connection/socket/unix.rs index 8b5970d1494..8216f8d2fd5 100644 --- a/library/std/src/sys/net/connection/socket/unix.rs +++ b/library/std/src/sys/net/connection/socket/unix.rs @@ -361,7 +361,7 @@ impl Socket { self.recv_from_with_flags(buf, 0) } - #[cfg(any(target_os = "android", target_os = "linux"))] + #[cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin"))] pub fn recv_msg(&self, msg: &mut libc::msghdr) -> io::Result<usize> { let n = cvt(unsafe { libc::recvmsg(self.as_raw_fd(), msg, libc::MSG_CMSG_CLOEXEC) })?; Ok(n as usize) @@ -384,7 +384,7 @@ impl Socket { self.0.is_write_vectored() } - #[cfg(any(target_os = "android", target_os = "linux"))] + #[cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin"))] pub fn send_msg(&self, msg: &mut libc::msghdr) -> io::Result<usize> { let n = cvt(unsafe { libc::sendmsg(self.as_raw_fd(), msg, 0) })?; Ok(n as usize) @@ -472,12 +472,12 @@ impl Socket { Ok(raw != 0) } - #[cfg(any(target_os = "android", target_os = "linux",))] + #[cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin"))] pub fn set_quickack(&self, quickack: bool) -> io::Result<()> { setsockopt(self, libc::IPPROTO_TCP, libc::TCP_QUICKACK, quickack as c_int) } - #[cfg(any(target_os = "android", target_os = "linux",))] + #[cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin"))] pub fn quickack(&self) -> io::Result<bool> { let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_QUICKACK)?; Ok(raw != 0) @@ -541,12 +541,12 @@ impl Socket { Ok(raw != 0) } - #[cfg(any(target_os = "android", target_os = "linux",))] + #[cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin"))] pub fn set_passcred(&self, passcred: bool) -> io::Result<()> { setsockopt(self, libc::SOL_SOCKET, libc::SO_PASSCRED, passcred as libc::c_int) } - #[cfg(any(target_os = "android", target_os = "linux",))] + #[cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin"))] pub fn passcred(&self) -> io::Result<bool> { let passcred: libc::c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_PASSCRED)?; Ok(passcred != 0) diff --git a/library/std/src/sys/pal/mod.rs b/library/std/src/sys/pal/mod.rs index 9376f5249f1..513121c6d30 100644 --- a/library/std/src/sys/pal/mod.rs +++ b/library/std/src/sys/pal/mod.rs @@ -49,9 +49,9 @@ cfg_select! { mod wasip2; pub use self::wasip2::*; } - target_os = "wasi" => { - mod wasi; - pub use self::wasi::*; + all(target_os = "wasi", target_env = "p1") => { + mod wasip1; + pub use self::wasip1::*; } target_family = "wasm" => { mod wasm; diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index 400128acf12..ac5c823a1bf 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -364,6 +364,7 @@ pub fn cvt_nz(error: libc::c_int) -> crate::io::Result<()> { // multithreaded C program. It is much less severe for Rust, because Rust // stdlib doesn't use libc stdio buffering. In a typical Rust program, which // does not use C stdio, even a buggy libc::abort() is, in fact, safe. +#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn abort_internal() -> ! { unsafe { libc::abort() } } diff --git a/library/std/src/sys/pal/wasi/helpers.rs b/library/std/src/sys/pal/wasip1/helpers.rs index 404747f0dc7..404747f0dc7 100644 --- a/library/std/src/sys/pal/wasi/helpers.rs +++ b/library/std/src/sys/pal/wasip1/helpers.rs diff --git a/library/std/src/sys/pal/wasi/mod.rs b/library/std/src/sys/pal/wasip1/mod.rs index 61dd1c3f98b..61dd1c3f98b 100644 --- a/library/std/src/sys/pal/wasi/mod.rs +++ b/library/std/src/sys/pal/wasip1/mod.rs diff --git a/library/std/src/sys/pal/wasi/os.rs b/library/std/src/sys/pal/wasip1/os.rs index 151ba254ec4..151ba254ec4 100644 --- a/library/std/src/sys/pal/wasi/os.rs +++ b/library/std/src/sys/pal/wasip1/os.rs diff --git a/library/std/src/sys/pal/wasi/thread.rs b/library/std/src/sys/pal/wasip1/thread.rs index e062b49bd7a..e062b49bd7a 100644 --- a/library/std/src/sys/pal/wasi/thread.rs +++ b/library/std/src/sys/pal/wasip1/thread.rs diff --git a/library/std/src/sys/pal/wasi/time.rs b/library/std/src/sys/pal/wasip1/time.rs index 892661b312b..892661b312b 100644 --- a/library/std/src/sys/pal/wasi/time.rs +++ b/library/std/src/sys/pal/wasip1/time.rs diff --git a/library/std/src/sys/pal/wasip2/mod.rs b/library/std/src/sys/pal/wasip2/mod.rs index 47fe3221c90..5f3fb6d6ddf 100644 --- a/library/std/src/sys/pal/wasip2/mod.rs +++ b/library/std/src/sys/pal/wasip2/mod.rs @@ -10,13 +10,11 @@ #[path = "../wasm/atomics/futex.rs"] pub mod futex; -#[path = "../wasi/os.rs"] +#[path = "../wasip1/os.rs"] pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -#[path = "../wasi/thread.rs"] pub mod thread; -#[path = "../wasi/time.rs"] pub mod time; #[path = "../unsupported/common.rs"] @@ -26,7 +24,7 @@ mod common; pub use common::*; -#[path = "../wasi/helpers.rs"] +#[path = "../wasip1/helpers.rs"] mod helpers; // The following exports are listed individually to work around Rust's glob diff --git a/library/std/src/sys/pal/wasip2/thread.rs b/library/std/src/sys/pal/wasip2/thread.rs new file mode 100644 index 00000000000..ad52918f15a --- /dev/null +++ b/library/std/src/sys/pal/wasip2/thread.rs @@ -0,0 +1,73 @@ +use crate::ffi::CStr; +use crate::io; +use crate::num::NonZero; +use crate::time::{Duration, Instant}; + +pub struct Thread(!); + +pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024; + +impl Thread { + pub unsafe fn new( + _stack: usize, + _name: Option<&str>, + _p: Box<dyn FnOnce()>, + ) -> io::Result<Thread> { + // Note that unlike WASIp1 even if the wasm `atomics` feature is enabled + // there is no support for threads, not even experimentally, not even in + // wasi-libc. Thus this is unconditionally unsupported. + crate::sys::unsupported() + } + + pub fn yield_now() { + // no API for this in WASIp2, but there's also no threads, so that's + // sort of expected. + } + + pub fn set_name(_name: &CStr) { + // nope + } + + pub fn sleep(dur: Duration) { + // Sleep in increments of `u64::MAX` nanoseconds until the `dur` is + // entirely drained. + let mut remaining = dur.as_nanos(); + while remaining > 0 { + let amt = u64::try_from(remaining).unwrap_or(u64::MAX); + wasip2::clocks::monotonic_clock::subscribe_duration(amt).block(); + remaining -= u128::from(amt); + } + } + + pub fn sleep_until(deadline: Instant) { + match u64::try_from(deadline.into_inner().as_duration().as_nanos()) { + // If the point in time we're sleeping to fits within a 64-bit + // number of nanoseconds then directly use `subscribe_instant`. + Ok(deadline) => { + wasip2::clocks::monotonic_clock::subscribe_instant(deadline).block(); + } + // ... otherwise we're sleeping for 500+ years relative to the + // "start" of what the system is using as a clock so speed/accuracy + // is not so much of a concern. Use `sleep` instead. + Err(_) => { + let now = Instant::now(); + + if let Some(delay) = deadline.checked_duration_since(now) { + Self::sleep(delay); + } + } + } + } + + pub fn join(self) { + self.0 + } +} + +pub(crate) fn current_os_id() -> Option<u64> { + None +} + +pub fn available_parallelism() -> io::Result<NonZero<usize>> { + crate::sys::unsupported() +} diff --git a/library/std/src/sys/pal/wasip2/time.rs b/library/std/src/sys/pal/wasip2/time.rs new file mode 100644 index 00000000000..f1f6839774b --- /dev/null +++ b/library/std/src/sys/pal/wasip2/time.rs @@ -0,0 +1,69 @@ +use crate::time::Duration; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub struct Instant(Duration); + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub struct SystemTime(Duration); + +pub const UNIX_EPOCH: SystemTime = SystemTime(Duration::from_secs(0)); + +impl Instant { + pub fn now() -> Instant { + Instant(Duration::from_nanos(wasip2::clocks::monotonic_clock::now())) + } + + pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> { + self.0.checked_sub(other.0) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> { + Some(Instant(self.0.checked_add(*other)?)) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> { + Some(Instant(self.0.checked_sub(*other)?)) + } + + pub(super) fn as_duration(&self) -> &Duration { + &self.0 + } +} + +impl SystemTime { + pub fn now() -> SystemTime { + let now = wasip2::clocks::wall_clock::now(); + SystemTime(Duration::new(now.seconds, now.nanoseconds)) + } + + #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] + pub const fn from_wasi_timestamp(ts: wasi::Timestamp) -> SystemTime { + SystemTime(Duration::from_nanos(ts)) + } + + #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] + pub const fn to_wasi_timestamp(&self) -> Option<wasi::Timestamp> { + // FIXME: const TryInto + let ns = self.0.as_nanos(); + if ns <= u64::MAX as u128 { Some(ns as u64) } else { None } + } + + #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] + pub const fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> { + // FIXME: ok_or_else with const closures + match self.0.checked_sub(other.0) { + Some(duration) => Ok(duration), + None => Err(other.0 - self.0), + } + } + + #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] + pub const fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> { + Some(SystemTime(self.0.checked_add(*other)?)) + } + + #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] + pub const fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> { + Some(SystemTime(self.0.checked_sub(*other)?)) + } +} diff --git a/library/std/src/sys/pal/windows/handle.rs b/library/std/src/sys/pal/windows/handle.rs index 82a880faf5f..76c8aa939d3 100644 --- a/library/std/src/sys/pal/windows/handle.rs +++ b/library/std/src/sys/pal/windows/handle.rs @@ -136,6 +136,19 @@ impl Handle { } } + pub fn read_buf_at(&self, mut cursor: BorrowedCursor<'_>, offset: u64) -> io::Result<()> { + // SAFETY: `cursor.as_mut()` starts with `cursor.capacity()` writable bytes + let read = unsafe { + self.synchronous_read(cursor.as_mut().as_mut_ptr(), cursor.capacity(), Some(offset)) + }?; + + // SAFETY: `read` bytes were written to the initialized portion of the buffer + unsafe { + cursor.advance_unchecked(read); + } + Ok(()) + } + pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> { let mut me = self; diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index 10ad4541bed..3b6a86cbc8f 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -356,6 +356,7 @@ pub fn abort_internal() -> ! { } #[cfg(miri)] +#[track_caller] // even without panics, this helps for Miri backtraces pub fn abort_internal() -> ! { crate::intrinsics::abort(); } diff --git a/library/std/src/sys/platform_version/darwin/core_foundation.rs b/library/std/src/sys/platform_version/darwin/core_foundation.rs new file mode 100644 index 00000000000..1e0d15fcf66 --- /dev/null +++ b/library/std/src/sys/platform_version/darwin/core_foundation.rs @@ -0,0 +1,180 @@ +//! Minimal utilities for interfacing with a dynamically loaded CoreFoundation. +#![allow(non_snake_case, non_upper_case_globals)] +use super::root_relative; +use crate::ffi::{CStr, c_char, c_void}; +use crate::ptr::null_mut; +use crate::sys::common::small_c_string::run_path_with_cstr; + +// MacTypes.h +pub(super) type Boolean = u8; +// CoreFoundation/CFBase.h +pub(super) type CFTypeID = usize; +pub(super) type CFOptionFlags = usize; +pub(super) type CFIndex = isize; +pub(super) type CFTypeRef = *mut c_void; +pub(super) type CFAllocatorRef = CFTypeRef; +pub(super) const kCFAllocatorDefault: CFAllocatorRef = null_mut(); +// CoreFoundation/CFError.h +pub(super) type CFErrorRef = CFTypeRef; +// CoreFoundation/CFData.h +pub(super) type CFDataRef = CFTypeRef; +// CoreFoundation/CFPropertyList.h +pub(super) const kCFPropertyListImmutable: CFOptionFlags = 0; +pub(super) type CFPropertyListFormat = CFIndex; +pub(super) type CFPropertyListRef = CFTypeRef; +// CoreFoundation/CFString.h +pub(super) type CFStringRef = CFTypeRef; +pub(super) type CFStringEncoding = u32; +pub(super) const kCFStringEncodingUTF8: CFStringEncoding = 0x08000100; +// CoreFoundation/CFDictionary.h +pub(super) type CFDictionaryRef = CFTypeRef; + +/// An open handle to the dynamically loaded CoreFoundation framework. +/// +/// This is `dlopen`ed, and later `dlclose`d. This is done to try to avoid +/// "leaking" the CoreFoundation symbols to the rest of the user's binary if +/// they decided to not link CoreFoundation themselves. +/// +/// It is also faster to look up symbols directly via this handle than with +/// `RTLD_DEFAULT`. +pub(super) struct CFHandle(*mut c_void); + +macro_rules! dlsym_fn { + ( + unsafe fn $name:ident($($param:ident: $param_ty:ty),* $(,)?) $(-> $ret:ty)?; + ) => { + pub(super) unsafe fn $name(&self, $($param: $param_ty),*) $(-> $ret)? { + let ptr = unsafe { + libc::dlsym( + self.0, + concat!(stringify!($name), '\0').as_bytes().as_ptr().cast(), + ) + }; + if ptr.is_null() { + let err = unsafe { CStr::from_ptr(libc::dlerror()) }; + panic!("could not find function {}: {err:?}", stringify!($name)); + } + + // SAFETY: Just checked that the symbol isn't NULL, and macro invoker verifies that + // the signature is correct. + let fnptr = unsafe { + crate::mem::transmute::< + *mut c_void, + unsafe extern "C" fn($($param_ty),*) $(-> $ret)?, + >(ptr) + }; + + // SAFETY: Upheld by caller. + unsafe { fnptr($($param),*) } + } + }; +} + +impl CFHandle { + /// Link to the CoreFoundation dylib, and look up symbols from that. + pub(super) fn new() -> Self { + // We explicitly use non-versioned path here, to allow this to work on older iOS devices. + let cf_path = + root_relative("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation"); + + let handle = run_path_with_cstr(&cf_path, &|path| unsafe { + Ok(libc::dlopen(path.as_ptr(), libc::RTLD_LAZY | libc::RTLD_LOCAL)) + }) + .expect("failed allocating string"); + + if handle.is_null() { + let err = unsafe { CStr::from_ptr(libc::dlerror()) }; + panic!("could not open CoreFoundation.framework: {err:?}"); + } + + Self(handle) + } + + pub(super) fn kCFAllocatorNull(&self) -> CFAllocatorRef { + // Available: in all CF versions. + let static_ptr = unsafe { libc::dlsym(self.0, c"kCFAllocatorNull".as_ptr()) }; + if static_ptr.is_null() { + let err = unsafe { CStr::from_ptr(libc::dlerror()) }; + panic!("could not find kCFAllocatorNull: {err:?}"); + } + unsafe { *static_ptr.cast() } + } + + // CoreFoundation/CFBase.h + dlsym_fn!( + // Available: in all CF versions. + unsafe fn CFRelease(cf: CFTypeRef); + ); + dlsym_fn!( + // Available: in all CF versions. + unsafe fn CFGetTypeID(cf: CFTypeRef) -> CFTypeID; + ); + + // CoreFoundation/CFData.h + dlsym_fn!( + // Available: in all CF versions. + unsafe fn CFDataCreateWithBytesNoCopy( + allocator: CFAllocatorRef, + bytes: *const u8, + length: CFIndex, + bytes_deallocator: CFAllocatorRef, + ) -> CFDataRef; + ); + + // CoreFoundation/CFPropertyList.h + dlsym_fn!( + // Available: since macOS 10.6. + unsafe fn CFPropertyListCreateWithData( + allocator: CFAllocatorRef, + data: CFDataRef, + options: CFOptionFlags, + format: *mut CFPropertyListFormat, + error: *mut CFErrorRef, + ) -> CFPropertyListRef; + ); + + // CoreFoundation/CFString.h + dlsym_fn!( + // Available: in all CF versions. + unsafe fn CFStringGetTypeID() -> CFTypeID; + ); + dlsym_fn!( + // Available: in all CF versions. + unsafe fn CFStringCreateWithCStringNoCopy( + alloc: CFAllocatorRef, + c_str: *const c_char, + encoding: CFStringEncoding, + contents_deallocator: CFAllocatorRef, + ) -> CFStringRef; + ); + dlsym_fn!( + // Available: in all CF versions. + unsafe fn CFStringGetCString( + the_string: CFStringRef, + buffer: *mut c_char, + buffer_size: CFIndex, + encoding: CFStringEncoding, + ) -> Boolean; + ); + + // CoreFoundation/CFDictionary.h + dlsym_fn!( + // Available: in all CF versions. + unsafe fn CFDictionaryGetTypeID() -> CFTypeID; + ); + dlsym_fn!( + // Available: in all CF versions. + unsafe fn CFDictionaryGetValue( + the_dict: CFDictionaryRef, + key: *const c_void, + ) -> *const c_void; + ); +} + +impl Drop for CFHandle { + fn drop(&mut self) { + // Ignore errors when closing. This is also what `libloading` does: + // https://docs.rs/libloading/0.8.6/src/libloading/os/unix/mod.rs.html#374 + let _ = unsafe { libc::dlclose(self.0) }; + } +} diff --git a/library/std/src/sys/platform_version/darwin/mod.rs b/library/std/src/sys/platform_version/darwin/mod.rs new file mode 100644 index 00000000000..06b97fcdef4 --- /dev/null +++ b/library/std/src/sys/platform_version/darwin/mod.rs @@ -0,0 +1,351 @@ +use self::core_foundation::{ + CFDictionaryRef, CFHandle, CFIndex, CFStringRef, CFTypeRef, kCFAllocatorDefault, + kCFPropertyListImmutable, kCFStringEncodingUTF8, +}; +use crate::borrow::Cow; +use crate::bstr::ByteStr; +use crate::ffi::{CStr, c_char}; +use crate::num::{NonZero, ParseIntError}; +use crate::path::{Path, PathBuf}; +use crate::ptr::null_mut; +use crate::sync::atomic::{AtomicU32, Ordering}; +use crate::{env, fs}; + +mod core_foundation; +mod public_extern; +#[cfg(test)] +mod tests; + +/// The version of the operating system. +/// +/// We use a packed u32 here to allow for fast comparisons and to match Mach-O's `LC_BUILD_VERSION`. +type OSVersion = u32; + +/// Combine parts of a version into an [`OSVersion`]. +/// +/// The size of the parts are inherently limited by Mach-O's `LC_BUILD_VERSION`. +#[inline] +const fn pack_os_version(major: u16, minor: u8, patch: u8) -> OSVersion { + let (major, minor, patch) = (major as u32, minor as u32, patch as u32); + (major << 16) | (minor << 8) | patch +} + +/// [`pack_os_version`], but takes `i32` and saturates. +/// +/// Instead of using e.g. `major as u16`, which truncates. +#[inline] +fn pack_i32_os_version(major: i32, minor: i32, patch: i32) -> OSVersion { + let major: u16 = major.try_into().unwrap_or(u16::MAX); + let minor: u8 = minor.try_into().unwrap_or(u8::MAX); + let patch: u8 = patch.try_into().unwrap_or(u8::MAX); + pack_os_version(major, minor, patch) +} + +/// Get the current OS version, packed according to [`pack_os_version`]. +/// +/// # Semantics +/// +/// The reported version on macOS might be 10.16 if the SDK version of the binary is less than 11.0. +/// This is a workaround that Apple implemented to handle applications that assumed that macOS +/// versions would always start with "10", see: +/// <https://github.com/apple-oss-distributions/xnu/blob/xnu-11215.81.4/libsyscall/wrappers/system-version-compat.c> +/// +/// It _is_ possible to get the real version regardless of the SDK version of the binary, this is +/// what Zig does: +/// <https://github.com/ziglang/zig/blob/0.13.0/lib/std/zig/system/darwin/macos.zig> +/// +/// We choose to not do that, and instead follow Apple's behaviour here, and return 10.16 when +/// compiled with an older SDK; the user should instead upgrade their tooling. +/// +/// NOTE: `rustc` currently doesn't set the right SDK version when linking with ld64, so this will +/// have the wrong behaviour with `-Clinker=ld` on x86_64. But that's a `rustc` bug: +/// <https://github.com/rust-lang/rust/issues/129432> +#[inline] +fn current_version() -> OSVersion { + // Cache the lookup for performance. + // + // 0.0.0 is never going to be a valid version ("vtool" reports "n/a" on 0 versions), so we use + // that as our sentinel value. + static CURRENT_VERSION: AtomicU32 = AtomicU32::new(0); + + // We use relaxed atomics instead of e.g. a `Once`, it doesn't matter if multiple threads end up + // racing to read or write the version, `lookup_version` should be idempotent and always return + // the same value. + // + // `compiler-rt` uses `dispatch_once`, but that's overkill for the reasons above. + let version = CURRENT_VERSION.load(Ordering::Relaxed); + if version == 0 { + let version = lookup_version().get(); + CURRENT_VERSION.store(version, Ordering::Relaxed); + version + } else { + version + } +} + +/// Look up the os version. +/// +/// # Aborts +/// +/// Aborts if reading or parsing the version fails (or if the system was out of memory). +/// +/// We deliberately choose to abort, as having this silently return an invalid OS version would be +/// impossible for a user to debug. +// The lookup is costly and should be on the cold path because of the cache in `current_version`. +#[cold] +// Micro-optimization: We use `extern "C"` to abort on panic, allowing `current_version` (inlined) +// to be free of unwind handling. Aborting is required for `__isPlatformVersionAtLeast` anyhow. +extern "C" fn lookup_version() -> NonZero<OSVersion> { + // Try to read from `sysctl` first (faster), but if that fails, fall back to reading the + // property list (this is roughly what `_availability_version_check` does internally). + let version = version_from_sysctl().unwrap_or_else(version_from_plist); + + // Use `NonZero` to try to make it clearer to the optimizer that this will never return 0. + NonZero::new(version).expect("version cannot be 0.0.0") +} + +/// Read the version from `kern.osproductversion` or `kern.iossupportversion`. +/// +/// This is faster than `version_from_plist`, since it doesn't need to invoke `dlsym`. +fn version_from_sysctl() -> Option<OSVersion> { + // This won't work in the simulator, as `kern.osproductversion` returns the host macOS version, + // and `kern.iossupportversion` returns the host macOS' iOSSupportVersion (while you can run + // simulators with many different iOS versions). + if cfg!(target_abi = "sim") { + // Fall back to `version_from_plist` on these targets. + return None; + } + + let sysctl_version = |name: &CStr| { + let mut buf: [u8; 32] = [0; 32]; + let mut size = buf.len(); + let ptr = buf.as_mut_ptr().cast(); + let ret = unsafe { libc::sysctlbyname(name.as_ptr(), ptr, &mut size, null_mut(), 0) }; + if ret != 0 { + // This sysctl is not available. + return None; + } + let buf = &buf[..(size - 1)]; + + if buf.is_empty() { + // The buffer may be empty when using `kern.iossupportversion` on an actual iOS device, + // or on visionOS when running under "Designed for iPad". + // + // In that case, fall back to `kern.osproductversion`. + return None; + } + + Some(parse_os_version(buf).unwrap_or_else(|err| { + panic!("failed parsing version from sysctl ({}): {err}", ByteStr::new(buf)) + })) + }; + + // When `target_os = "ios"`, we may be in many different states: + // - Native iOS device. + // - iOS Simulator. + // - Mac Catalyst. + // - Mac + "Designed for iPad". + // - Native visionOS device + "Designed for iPad". + // - visionOS simulator + "Designed for iPad". + // + // Of these, only native, Mac Catalyst and simulators can be differentiated at compile-time + // (with `target_abi = ""`, `target_abi = "macabi"` and `target_abi = "sim"` respectively). + // + // That is, "Designed for iPad" will act as iOS at compile-time, but the `ProductVersion` will + // still be the host macOS or visionOS version. + // + // Furthermore, we can't even reliably differentiate between these at runtime, since + // `dyld_get_active_platform` isn't publicly available. + // + // Fortunately, we won't need to know any of that; we can simply attempt to get the + // `iOSSupportVersion` (which may be set on native iOS too, but then it will be set to the host + // iOS version), and if that fails, fall back to the `ProductVersion`. + if cfg!(target_os = "ios") { + // https://github.com/apple-oss-distributions/xnu/blob/xnu-11215.81.4/bsd/kern/kern_sysctl.c#L2077-L2100 + if let Some(ios_support_version) = sysctl_version(c"kern.iossupportversion") { + return Some(ios_support_version); + } + + // On Mac Catalyst, if we failed looking up `iOSSupportVersion`, we don't want to + // accidentally fall back to `ProductVersion`. + if cfg!(target_abi = "macabi") { + return None; + } + } + + // Introduced in macOS 10.13.4. + // https://github.com/apple-oss-distributions/xnu/blob/xnu-11215.81.4/bsd/kern/kern_sysctl.c#L2015-L2051 + sysctl_version(c"kern.osproductversion") +} + +/// Look up the current OS version(s) from `/System/Library/CoreServices/SystemVersion.plist`. +/// +/// More specifically, from the `ProductVersion` and `iOSSupportVersion` keys, and from +/// `$IPHONE_SIMULATOR_ROOT/System/Library/CoreServices/SystemVersion.plist` on the simulator. +/// +/// This file was introduced in macOS 10.3, which is well below the minimum supported version by +/// `rustc`, which is (at the time of writing) macOS 10.12. +/// +/// # Implementation +/// +/// We do roughly the same thing in here as `compiler-rt`, and dynamically look up CoreFoundation +/// utilities for parsing PLists (to avoid having to re-implement that in here, as pulling in a full +/// PList parser into `std` seems costly). +/// +/// If this is found to be undesirable, we _could_ possibly hack it by parsing the PList manually +/// (it seems to use the plain-text "xml1" encoding/format in all versions), but that seems brittle. +fn version_from_plist() -> OSVersion { + // Read `SystemVersion.plist`. Always present on Apple platforms, reading it cannot fail. + let path = root_relative("/System/Library/CoreServices/SystemVersion.plist"); + let plist_buffer = fs::read(&path).unwrap_or_else(|e| panic!("failed reading {path:?}: {e}")); + let cf_handle = CFHandle::new(); + parse_version_from_plist(&cf_handle, &plist_buffer) +} + +/// Parse OS version from the given PList. +/// +/// Split out from [`version_from_plist`] to allow for testing. +fn parse_version_from_plist(cf_handle: &CFHandle, plist_buffer: &[u8]) -> OSVersion { + let plist_data = unsafe { + cf_handle.CFDataCreateWithBytesNoCopy( + kCFAllocatorDefault, + plist_buffer.as_ptr(), + plist_buffer.len() as CFIndex, + cf_handle.kCFAllocatorNull(), + ) + }; + assert!(!plist_data.is_null(), "failed creating CFData"); + let _plist_data_release = Deferred(|| unsafe { cf_handle.CFRelease(plist_data) }); + + let plist = unsafe { + cf_handle.CFPropertyListCreateWithData( + kCFAllocatorDefault, + plist_data, + kCFPropertyListImmutable, + null_mut(), // Don't care about the format of the PList. + null_mut(), // Don't care about the error data. + ) + }; + assert!(!plist.is_null(), "failed reading PList in SystemVersion.plist"); + let _plist_release = Deferred(|| unsafe { cf_handle.CFRelease(plist) }); + + assert_eq!( + unsafe { cf_handle.CFGetTypeID(plist) }, + unsafe { cf_handle.CFDictionaryGetTypeID() }, + "SystemVersion.plist did not contain a dictionary at the top level" + ); + let plist: CFDictionaryRef = plist.cast(); + + // Same logic as in `version_from_sysctl`. + if cfg!(target_os = "ios") { + if let Some(ios_support_version) = + unsafe { string_version_key(cf_handle, plist, c"iOSSupportVersion") } + { + return ios_support_version; + } + + // Force Mac Catalyst to use iOSSupportVersion (do not fall back to ProductVersion). + if cfg!(target_abi = "macabi") { + panic!("expected iOSSupportVersion in SystemVersion.plist"); + } + } + + // On all other platforms, we can find the OS version by simply looking at `ProductVersion`. + unsafe { string_version_key(cf_handle, plist, c"ProductVersion") } + .expect("expected ProductVersion in SystemVersion.plist") +} + +/// Look up a string key in a CFDictionary, and convert it to an [`OSVersion`]. +unsafe fn string_version_key( + cf_handle: &CFHandle, + plist: CFDictionaryRef, + lookup_key: &CStr, +) -> Option<OSVersion> { + let cf_lookup_key = unsafe { + cf_handle.CFStringCreateWithCStringNoCopy( + kCFAllocatorDefault, + lookup_key.as_ptr(), + kCFStringEncodingUTF8, + cf_handle.kCFAllocatorNull(), + ) + }; + assert!(!cf_lookup_key.is_null(), "failed creating CFString"); + let _lookup_key_release = Deferred(|| unsafe { cf_handle.CFRelease(cf_lookup_key) }); + + let value: CFTypeRef = + unsafe { cf_handle.CFDictionaryGetValue(plist, cf_lookup_key) }.cast_mut(); + // `CFDictionaryGetValue` is a "getter", so we should not release, + // the value is held alive internally by the CFDictionary, see: + // https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmPractical.html#//apple_ref/doc/uid/TP40004447-SW12 + if value.is_null() { + return None; + } + + assert_eq!( + unsafe { cf_handle.CFGetTypeID(value) }, + unsafe { cf_handle.CFStringGetTypeID() }, + "key in SystemVersion.plist must be a string" + ); + let value: CFStringRef = value.cast(); + + let mut version_str = [0u8; 32]; + let ret = unsafe { + cf_handle.CFStringGetCString( + value, + version_str.as_mut_ptr().cast::<c_char>(), + version_str.len() as CFIndex, + kCFStringEncodingUTF8, + ) + }; + assert_ne!(ret, 0, "failed getting string from CFString"); + + let version_str = + CStr::from_bytes_until_nul(&version_str).expect("failed converting CFString to CStr"); + + Some(parse_os_version(version_str.to_bytes()).unwrap_or_else(|err| { + panic!( + "failed parsing version from PList ({}): {err}", + ByteStr::new(version_str.to_bytes()) + ) + })) +} + +/// Parse an OS version from a bytestring like b"10.1" or b"14.3.7". +fn parse_os_version(version: &[u8]) -> Result<OSVersion, ParseIntError> { + if let Some((major, minor)) = version.split_once(|&b| b == b'.') { + let major = u16::from_ascii(major)?; + if let Some((minor, patch)) = minor.split_once(|&b| b == b'.') { + let minor = u8::from_ascii(minor)?; + let patch = u8::from_ascii(patch)?; + Ok(pack_os_version(major, minor, patch)) + } else { + let minor = u8::from_ascii(minor)?; + Ok(pack_os_version(major, minor, 0)) + } + } else { + let major = u16::from_ascii(version)?; + Ok(pack_os_version(major, 0, 0)) + } +} + +/// Get a path relative to the root directory in which all files for the current env are located. +fn root_relative(path: &str) -> Cow<'_, Path> { + if cfg!(target_abi = "sim") { + let mut root = PathBuf::from(env::var_os("IPHONE_SIMULATOR_ROOT").expect( + "environment variable `IPHONE_SIMULATOR_ROOT` must be set when executing under simulator", + )); + // Convert absolute path to relative path, to make the `.push` work as expected. + root.push(Path::new(path).strip_prefix("/").unwrap()); + root.into() + } else { + Path::new(path).into() + } +} + +struct Deferred<F: FnMut()>(F); + +impl<F: FnMut()> Drop for Deferred<F> { + fn drop(&mut self) { + (self.0)(); + } +} diff --git a/library/std/src/sys/platform_version/darwin/public_extern.rs b/library/std/src/sys/platform_version/darwin/public_extern.rs new file mode 100644 index 00000000000..967cdb4920f --- /dev/null +++ b/library/std/src/sys/platform_version/darwin/public_extern.rs @@ -0,0 +1,151 @@ +//! # Runtime version checking ABI for other compilers. +//! +//! The symbols in this file are useful for us to expose to allow linking code written in the +//! following languages when using their version checking functionality: +//! - Clang's `__builtin_available` macro. +//! - Objective-C's `@available`. +//! - Swift's `#available`, +//! +//! Without Rust exposing these symbols, the user would encounter a linker error when linking to +//! C/Objective-C/Swift libraries using these features. +//! +//! The presence of these symbols is mostly considered a quality-of-implementation detail, and +//! should not be relied upon to be available. The intended effect is that linking with code built +//! with Clang's `__builtin_available` (or similar) will continue to work. For example, we may +//! decide to remove `__isOSVersionAtLeast` if support for Clang 11 (Xcode 11) is dropped. +//! +//! ## Background +//! +//! The original discussion of this feature can be found at: +//! - <https://lists.llvm.org/pipermail/cfe-dev/2016-July/049851.html> +//! - <https://reviews.llvm.org/D27827> +//! - <https://reviews.llvm.org/D30136> +//! +//! And the upstream implementation of these can be found in `compiler-rt`: +//! <https://github.com/llvm/llvm-project/blob/llvmorg-20.1.0/compiler-rt/lib/builtins/os_version_check.c> +//! +//! Ideally, these symbols should probably have been a part of Apple's `libSystem.dylib`, both +//! because their implementation is quite complex, using allocation, environment variables, file +//! access and dynamic library loading (and emitting all of this into every binary). +//! +//! The reason why Apple chose to not do that originally is lost to the sands of time, but a good +//! reason would be that implementing it as part of `compiler-rt` allowed them to back-deploy this +//! to older OSes immediately. +//! +//! In Rust's case, while we may provide a feature similar to `@available` in the future, we will +//! probably do so as a macro exposed by `std` (and not as a compiler builtin). So implementing this +//! in `std` makes sense, since then we can implement it using `std` utilities, and we can avoid +//! having `compiler-builtins` depend on `libSystem.dylib`. +//! +//! This does mean that users that attempt to link C/Objective-C/Swift code _and_ use `#![no_std]` +//! in all their crates may get a linker error because these symbols are missing. Using `no_std` is +//! quite uncommon on Apple systems though, so it's probably fine to not support this use-case. +//! +//! The workaround would be to link `libclang_rt.osx.a` or otherwise use Clang's `compiler-rt`. +//! +//! See also discussion in <https://github.com/rust-lang/compiler-builtins/pull/794>. +//! +//! ## Implementation details +//! +//! NOTE: Since macOS 10.15, `libSystem.dylib` _has_ actually provided the undocumented +//! `_availability_version_check` via `libxpc` for doing the version lookup (zippered, which is why +//! it requires a platform parameter to differentiate between macOS and Mac Catalyst), though its +//! usage may be a bit dangerous, see: +//! - <https://reviews.llvm.org/D150397> +//! - <https://github.com/llvm/llvm-project/issues/64227> +//! +//! Besides, we'd need to implement the version lookup via PList to support older versions anyhow, +//! so we might as well use that everywhere (since it can also be optimized more after inlining). + +#![allow(non_snake_case)] + +use super::{current_version, pack_i32_os_version}; + +/// Whether the current platform's OS version is higher than or equal to the given version. +/// +/// The first argument is the _base_ Mach-O platform (i.e. `PLATFORM_MACOS`, `PLATFORM_IOS`, etc., +/// but not `PLATFORM_IOSSIMULATOR` or `PLATFORM_MACCATALYST`) of the invoking binary. +/// +/// Arguments are specified statically by Clang. Inlining with LTO should allow the versions to be +/// combined into a single `u32`, which should make comparisons faster, and should make the +/// `BASE_TARGET_PLATFORM` check a no-op. +// +// SAFETY: The signature is the same as what Clang expects, and we export weakly to allow linking +// both this and `libclang_rt.*.a`, similar to how `compiler-builtins` does it: +// https://github.com/rust-lang/compiler-builtins/blob/0.1.113/src/macros.rs#L494 +// +// NOTE: This symbol has a workaround in the compiler's symbol mangling to avoid mangling it, while +// still not exposing it from non-cdylib (like `#[no_mangle]` would). +#[rustc_std_internal_symbol] +// extern "C" is correct, Clang assumes the function cannot unwind: +// https://github.com/llvm/llvm-project/blob/llvmorg-20.1.0/clang/lib/CodeGen/CGObjC.cpp#L3980 +// +// If an error happens in this, we instead abort the process. +pub(super) extern "C" fn __isPlatformVersionAtLeast( + platform: i32, + major: i32, + minor: i32, + subminor: i32, +) -> i32 { + let version = pack_i32_os_version(major, minor, subminor); + + // Mac Catalyst is a technology that allows macOS to run in a different "mode" that closely + // resembles iOS (and has iOS libraries like UIKit available). + // + // (Apple has added a "Designed for iPad" mode later on that allows running iOS apps + // natively, but we don't need to think too much about those, since they link to + // iOS-specific system binaries as well). + // + // To support Mac Catalyst, Apple added the concept of a "zippered" binary, which is a single + // binary that can be run on both macOS and Mac Catalyst (has two `LC_BUILD_VERSION` Mach-O + // commands, one set to `PLATFORM_MACOS` and one to `PLATFORM_MACCATALYST`). + // + // Most system libraries are zippered, which allows re-use across macOS and Mac Catalyst. + // This includes the `libclang_rt.osx.a` shipped with Xcode! This means that `compiler-rt` + // can't statically know whether it's compiled for macOS or Mac Catalyst, and thus this new + // API (which replaces `__isOSVersionAtLeast`) is needed. + // + // In short: + // normal binary calls normal compiler-rt --> `__isOSVersionAtLeast` was enough + // normal binary calls zippered compiler-rt --> `__isPlatformVersionAtLeast` required + // zippered binary calls zippered compiler-rt --> `__isPlatformOrVariantPlatformVersionAtLeast` called + + // FIXME(madsmtm): `rustc` doesn't support zippered binaries yet, see rust-lang/rust#131216. + // But once it does, we need the pre-compiled `std` shipped with rustup to be zippered, and thus + // we also need to handle the `platform` difference here: + // + // if cfg!(target_os = "macos") && platform == 2 /* PLATFORM_IOS */ && cfg!(zippered) { + // return (version.to_u32() <= current_ios_version()) as i32; + // } + // + // `__isPlatformOrVariantPlatformVersionAtLeast` would also need to be implemented. + + // The base Mach-O platform for the current target. + const BASE_TARGET_PLATFORM: i32 = if cfg!(target_os = "macos") { + 1 // PLATFORM_MACOS + } else if cfg!(target_os = "ios") { + 2 // PLATFORM_IOS + } else if cfg!(target_os = "tvos") { + 3 // PLATFORM_TVOS + } else if cfg!(target_os = "watchos") { + 4 // PLATFORM_WATCHOS + } else if cfg!(target_os = "visionos") { + 11 // PLATFORM_VISIONOS + } else { + 0 // PLATFORM_UNKNOWN + }; + debug_assert_eq!( + platform, BASE_TARGET_PLATFORM, + "invalid platform provided to __isPlatformVersionAtLeast", + ); + + (version <= current_version()) as i32 +} + +/// Old entry point for availability. Used when compiling with older Clang versions. +// SAFETY: Same as for `__isPlatformVersionAtLeast`. +#[rustc_std_internal_symbol] +pub(super) extern "C" fn __isOSVersionAtLeast(major: i32, minor: i32, subminor: i32) -> i32 { + let version = pack_i32_os_version(major, minor, subminor); + (version <= current_version()) as i32 +} diff --git a/library/std/src/sys/platform_version/darwin/tests.rs b/library/std/src/sys/platform_version/darwin/tests.rs new file mode 100644 index 00000000000..76dc4482c98 --- /dev/null +++ b/library/std/src/sys/platform_version/darwin/tests.rs @@ -0,0 +1,379 @@ +use super::public_extern::*; +use super::*; +use crate::process::Command; + +#[test] +fn test_general_available() { + // Lowest version always available. + assert_eq!(__isOSVersionAtLeast(0, 0, 0), 1); + // This high version never available. + assert_eq!(__isOSVersionAtLeast(9999, 99, 99), 0); +} + +#[test] +fn test_saturating() { + // Higher version than supported by OSVersion -> make sure we saturate. + assert_eq!(__isOSVersionAtLeast(0x10000, 0, 0), 0); +} + +#[test] +#[cfg_attr(not(target_os = "macos"), ignore = "`sw_vers` is only available on host macOS")] +fn compare_against_sw_vers() { + let sw_vers = Command::new("sw_vers").arg("-productVersion").output().unwrap().stdout; + let sw_vers = String::from_utf8(sw_vers).unwrap(); + let mut sw_vers = sw_vers.trim().split('.'); + + let major: i32 = sw_vers.next().unwrap().parse().unwrap(); + let minor: i32 = sw_vers.next().unwrap_or("0").parse().unwrap(); + let subminor: i32 = sw_vers.next().unwrap_or("0").parse().unwrap(); + assert_eq!(sw_vers.count(), 0); + + // Current version is available + assert_eq!(__isOSVersionAtLeast(major, minor, subminor), 1); + + // One lower is available + assert_eq!(__isOSVersionAtLeast(major, minor, subminor.saturating_sub(1)), 1); + assert_eq!(__isOSVersionAtLeast(major, minor.saturating_sub(1), subminor), 1); + assert_eq!(__isOSVersionAtLeast(major.saturating_sub(1), minor, subminor), 1); + + // One higher isn't available + assert_eq!(__isOSVersionAtLeast(major, minor, subminor + 1), 0); + assert_eq!(__isOSVersionAtLeast(major, minor + 1, subminor), 0); + assert_eq!(__isOSVersionAtLeast(major + 1, minor, subminor), 0); + + // Test directly against the lookup + assert_eq!(lookup_version().get(), pack_os_version(major as _, minor as _, subminor as _)); +} + +#[test] +fn sysctl_same_as_in_plist() { + if let Some(version) = version_from_sysctl() { + assert_eq!(version, version_from_plist()); + } +} + +#[test] +fn lookup_idempotent() { + let version = lookup_version(); + for _ in 0..10 { + assert_eq!(version, lookup_version()); + } +} + +/// Test parsing a bunch of different PLists found in the wild, to ensure that +/// if we decide to parse it without CoreFoundation in the future, that it +/// would continue to work, even on older platforms. +#[test] +fn parse_plist() { + #[track_caller] + fn check( + (major, minor, patch): (u16, u8, u8), + ios_version: Option<(u16, u8, u8)>, + plist: &str, + ) { + let expected = if cfg!(target_os = "ios") { + if let Some((ios_major, ios_minor, ios_patch)) = ios_version { + pack_os_version(ios_major, ios_minor, ios_patch) + } else if cfg!(target_abi = "macabi") { + // Skip checking iOS version on Mac Catalyst. + return; + } else { + // iOS version will be parsed from ProductVersion + pack_os_version(major, minor, patch) + } + } else { + pack_os_version(major, minor, patch) + }; + let cf_handle = CFHandle::new(); + assert_eq!(expected, parse_version_from_plist(&cf_handle, plist.as_bytes())); + } + + // macOS 10.3.0 + let plist = r#"<?xml version="1.0" encoding="UTF-8"?> + <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> + <plist version="1.0"> + <dict> + <key>ProductBuildVersion</key> + <string>7B85</string> + <key>ProductCopyright</key> + <string>Apple Computer, Inc. 1983-2003</string> + <key>ProductName</key> + <string>Mac OS X</string> + <key>ProductUserVisibleVersion</key> + <string>10.3</string> + <key>ProductVersion</key> + <string>10.3</string> + </dict> + </plist> + "#; + check((10, 3, 0), None, plist); + + // macOS 10.7.5 + let plist = r#"<?xml version="1.0" encoding="UTF-8"?> + <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> + <plist version="1.0"> + <dict> + <key>ProductBuildVersion</key> + <string>11G63</string> + <key>ProductCopyright</key> + <string>1983-2012 Apple Inc.</string> + <key>ProductName</key> + <string>Mac OS X</string> + <key>ProductUserVisibleVersion</key> + <string>10.7.5</string> + <key>ProductVersion</key> + <string>10.7.5</string> + </dict> + </plist> + "#; + check((10, 7, 5), None, plist); + + // macOS 14.7.4 + let plist = r#"<?xml version="1.0" encoding="UTF-8"?> + <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> + <plist version="1.0"> + <dict> + <key>BuildID</key> + <string>6A558D8A-E2EA-11EF-A1D3-6222CAA672A8</string> + <key>ProductBuildVersion</key> + <string>23H420</string> + <key>ProductCopyright</key> + <string>1983-2025 Apple Inc.</string> + <key>ProductName</key> + <string>macOS</string> + <key>ProductUserVisibleVersion</key> + <string>14.7.4</string> + <key>ProductVersion</key> + <string>14.7.4</string> + <key>iOSSupportVersion</key> + <string>17.7</string> + </dict> + </plist> + "#; + check((14, 7, 4), Some((17, 7, 0)), plist); + + // SystemVersionCompat.plist on macOS 14.7.4 + let plist = r#"<?xml version="1.0" encoding="UTF-8"?> + <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> + <plist version="1.0"> + <dict> + <key>BuildID</key> + <string>6A558D8A-E2EA-11EF-A1D3-6222CAA672A8</string> + <key>ProductBuildVersion</key> + <string>23H420</string> + <key>ProductCopyright</key> + <string>1983-2025 Apple Inc.</string> + <key>ProductName</key> + <string>Mac OS X</string> + <key>ProductUserVisibleVersion</key> + <string>10.16</string> + <key>ProductVersion</key> + <string>10.16</string> + <key>iOSSupportVersion</key> + <string>17.7</string> + </dict> + </plist> + "#; + check((10, 16, 0), Some((17, 7, 0)), plist); + + // macOS 15.4 Beta 24E5238a + let plist = r#"<?xml version="1.0" encoding="UTF-8"?> + <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> + <plist version="1.0"> + <dict> + <key>BuildID</key> + <string>67A50F62-00DA-11F0-BDB6-F99BB8310D2A</string> + <key>ProductBuildVersion</key> + <string>24E5238a</string> + <key>ProductCopyright</key> + <string>1983-2025 Apple Inc.</string> + <key>ProductName</key> + <string>macOS</string> + <key>ProductUserVisibleVersion</key> + <string>15.4</string> + <key>ProductVersion</key> + <string>15.4</string> + <key>iOSSupportVersion</key> + <string>18.4</string> + </dict> + </plist> + "#; + check((15, 4, 0), Some((18, 4, 0)), plist); + + // iOS Simulator 17.5 + let plist = r#"<?xml version="1.0" encoding="UTF-8"?> + <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> + <plist version="1.0"> + <dict> + <key>BuildID</key> + <string>210B8A2C-09C3-11EF-9DB8-273A64AEFA1C</string> + <key>ProductBuildVersion</key> + <string>21F79</string> + <key>ProductCopyright</key> + <string>1983-2024 Apple Inc.</string> + <key>ProductName</key> + <string>iPhone OS</string> + <key>ProductVersion</key> + <string>17.5</string> + </dict> + </plist> + "#; + check((17, 5, 0), None, plist); + + // visionOS Simulator 2.3 + let plist = r#"<?xml version="1.0" encoding="UTF-8"?> + <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> + <plist version="1.0"> + <dict> + <key>BuildID</key> + <string>57CEFDE6-D079-11EF-837C-8B8C7961D0AC</string> + <key>ProductBuildVersion</key> + <string>22N895</string> + <key>ProductCopyright</key> + <string>1983-2025 Apple Inc.</string> + <key>ProductName</key> + <string>xrOS</string> + <key>ProductVersion</key> + <string>2.3</string> + <key>SystemImageID</key> + <string>D332C7F1-08DF-4DD9-8122-94EF39A1FB92</string> + <key>iOSSupportVersion</key> + <string>18.3</string> + </dict> + </plist> + "#; + check((2, 3, 0), Some((18, 3, 0)), plist); + + // tvOS Simulator 18.2 + let plist = r#"<?xml version="1.0" encoding="UTF-8"?> + <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> + <plist version="1.0"> + <dict> + <key>BuildID</key> + <string>617587B0-B059-11EF-BE70-4380EDE44645</string> + <key>ProductBuildVersion</key> + <string>22K154</string> + <key>ProductCopyright</key> + <string>1983-2024 Apple Inc.</string> + <key>ProductName</key> + <string>Apple TVOS</string> + <key>ProductVersion</key> + <string>18.2</string> + <key>SystemImageID</key> + <string>8BB5A425-33F0-4821-9F93-40E7ED92F4E0</string> + </dict> + </plist> + "#; + check((18, 2, 0), None, plist); + + // watchOS Simulator 11.2 + let plist = r#"<?xml version="1.0" encoding="UTF-8"?> + <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> + <plist version="1.0"> + <dict> + <key>BuildID</key> + <string>BAAE2D54-B122-11EF-BF78-C6C6836B724A</string> + <key>ProductBuildVersion</key> + <string>22S99</string> + <key>ProductCopyright</key> + <string>1983-2024 Apple Inc.</string> + <key>ProductName</key> + <string>Watch OS</string> + <key>ProductVersion</key> + <string>11.2</string> + <key>SystemImageID</key> + <string>79F773E2-2041-43B4-98EE-FAE52402AE95</string> + </dict> + </plist> + "#; + check((11, 2, 0), None, plist); + + // iOS 9.3.6 + let plist = r#"<?xml version="1.0" encoding="UTF-8"?> + <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> + <plist version="1.0"> + <dict> + <key>ProductBuildVersion</key> + <string>13G37</string> + <key>ProductCopyright</key> + <string>1983-2019 Apple Inc.</string> + <key>ProductName</key> + <string>iPhone OS</string> + <key>ProductVersion</key> + <string>9.3.6</string> + </dict> + </plist> + "#; + check((9, 3, 6), None, plist); +} + +#[test] +#[should_panic = "SystemVersion.plist did not contain a dictionary at the top level"] +fn invalid_plist() { + let cf_handle = CFHandle::new(); + let _ = parse_version_from_plist(&cf_handle, b"INVALID"); +} + +#[test] +#[cfg_attr( + target_abi = "macabi", + should_panic = "expected iOSSupportVersion in SystemVersion.plist" +)] +#[cfg_attr( + not(target_abi = "macabi"), + should_panic = "expected ProductVersion in SystemVersion.plist" +)] +fn empty_plist() { + let plist = r#"<?xml version="1.0" encoding="UTF-8"?> + <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> + <plist version="1.0"> + <dict> + </dict> + </plist> + "#; + let cf_handle = CFHandle::new(); + let _ = parse_version_from_plist(&cf_handle, plist.as_bytes()); +} + +#[test] +fn parse_version() { + #[track_caller] + fn check(major: u16, minor: u8, patch: u8, version: &str) { + assert_eq!( + pack_os_version(major, minor, patch), + parse_os_version(version.as_bytes()).unwrap() + ) + } + + check(0, 0, 0, "0"); + check(0, 0, 0, "0.0.0"); + check(1, 0, 0, "1"); + check(1, 2, 0, "1.2"); + check(1, 2, 3, "1.2.3"); + check(9999, 99, 99, "9999.99.99"); + + // Check leading zeroes + check(10, 0, 0, "010"); + check(10, 20, 0, "010.020"); + check(10, 20, 30, "010.020.030"); + check(10000, 100, 100, "000010000.00100.00100"); + + // Too many parts + assert!(parse_os_version(b"1.2.3.4").is_err()); + + // Empty + assert!(parse_os_version(b"").is_err()); + + // Invalid digit + assert!(parse_os_version(b"A.B").is_err()); + + // Missing digits + assert!(parse_os_version(b".").is_err()); + assert!(parse_os_version(b".1").is_err()); + assert!(parse_os_version(b"1.").is_err()); + + // Too large + assert!(parse_os_version(b"100000").is_err()); + assert!(parse_os_version(b"1.1000").is_err()); + assert!(parse_os_version(b"1.1.1000").is_err()); +} diff --git a/library/std/src/sys/platform_version/mod.rs b/library/std/src/sys/platform_version/mod.rs new file mode 100644 index 00000000000..88896c97ea3 --- /dev/null +++ b/library/std/src/sys/platform_version/mod.rs @@ -0,0 +1,13 @@ +//! Runtime lookup of operating system / platform version. +//! +//! Related to [RFC 3750](https://github.com/rust-lang/rfcs/pull/3750), which +//! does version detection at compile-time. +//! +//! See also the `os_info` crate. + +#[cfg(target_vendor = "apple")] +mod darwin; + +// In the future, we could expand this module with: +// - `RtlGetVersion` on Windows. +// - `__system_property_get` on Android. diff --git a/library/std/src/sys/random/mod.rs b/library/std/src/sys/random/mod.rs index a7fbae86099..1e0eec07b50 100644 --- a/library/std/src/sys/random/mod.rs +++ b/library/std/src/sys/random/mod.rs @@ -86,9 +86,13 @@ cfg_select! { mod vxworks; pub use vxworks::fill_bytes; } - target_os = "wasi" => { - mod wasi; - pub use wasi::fill_bytes; + all(target_os = "wasi", target_env = "p1") => { + mod wasip1; + pub use wasip1::fill_bytes; + } + all(target_os = "wasi", target_env = "p2") => { + mod wasip2; + pub use wasip2::{fill_bytes, hashmap_random_keys}; } target_os = "zkvm" => { mod zkvm; @@ -110,6 +114,7 @@ cfg_select! { target_os = "linux", target_os = "android", all(target_family = "wasm", target_os = "unknown"), + all(target_os = "wasi", target_env = "p2"), target_os = "xous", )))] pub fn hashmap_random_keys() -> (u64, u64) { diff --git a/library/std/src/sys/random/wasi.rs b/library/std/src/sys/random/wasip1.rs index d41da3751fc..d41da3751fc 100644 --- a/library/std/src/sys/random/wasi.rs +++ b/library/std/src/sys/random/wasip1.rs diff --git a/library/std/src/sys/random/wasip2.rs b/library/std/src/sys/random/wasip2.rs new file mode 100644 index 00000000000..a67c8a6428d --- /dev/null +++ b/library/std/src/sys/random/wasip2.rs @@ -0,0 +1,9 @@ +pub fn fill_bytes(bytes: &mut [u8]) { + bytes.copy_from_slice(&wasip2::random::random::get_random_bytes( + u64::try_from(bytes.len()).unwrap(), + )); +} + +pub fn hashmap_random_keys() -> (u64, u64) { + wasip2::random::insecure_seed::insecure_seed() +} diff --git a/library/std/src/sys/stdio/mod.rs b/library/std/src/sys/stdio/mod.rs index 314f226f07b..7436e4d9de4 100644 --- a/library/std/src/sys/stdio/mod.rs +++ b/library/std/src/sys/stdio/mod.rs @@ -29,9 +29,13 @@ cfg_select! { mod uefi; pub use uefi::*; } - target_os = "wasi" => { - mod wasi; - pub use wasi::*; + all(target_os = "wasi", target_env = "p1") => { + mod wasip1; + pub use wasip1::*; + } + all(target_os = "wasi", target_env = "p2") => { + mod wasip2; + pub use wasip2::*; } target_os = "xous" => { mod xous; diff --git a/library/std/src/sys/stdio/wasi.rs b/library/std/src/sys/stdio/wasip1.rs index b70efd026f9..b70efd026f9 100644 --- a/library/std/src/sys/stdio/wasi.rs +++ b/library/std/src/sys/stdio/wasip1.rs diff --git a/library/std/src/sys/stdio/wasip2.rs b/library/std/src/sys/stdio/wasip2.rs new file mode 100644 index 00000000000..1fcb49a083d --- /dev/null +++ b/library/std/src/sys/stdio/wasip2.rs @@ -0,0 +1,120 @@ +use wasip2::cli; +use wasip2::io::streams::{Error, InputStream, OutputStream, StreamError}; + +use crate::io::{self, BorrowedBuf, BorrowedCursor}; + +pub struct Stdin(Option<InputStream>); +pub struct Stdout(Option<OutputStream>); +pub struct Stderr(Option<OutputStream>); + +fn error_to_io(err: Error) -> io::Error { + // There exists a function in `wasi:filesystem` to optionally acquire an + // error code from an error, but the streams in use in this module are + // exclusively used with stdio meaning that a filesystem error is not + // possible here. + // + // In lieu of an error code, which WASIp2 does not specify, this instead + // carries along the `to_debug_string` implementation that the host + // supplies. If this becomes too expensive in the future this could also + // become `io::Error::from_raw_os_error(libc::EIO)` or similar. + io::Error::new(io::ErrorKind::Other, err.to_debug_string()) +} + +impl Stdin { + pub const fn new() -> Stdin { + Stdin(None) + } + + fn stream(&mut self) -> &InputStream { + self.0.get_or_insert_with(cli::stdin::get_stdin) + } +} + +impl io::Read for Stdin { + fn read(&mut self, data: &mut [u8]) -> io::Result<usize> { + let mut buf = BorrowedBuf::from(data); + self.read_buf(buf.unfilled())?; + Ok(buf.len()) + } + + fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> io::Result<()> { + match self.stream().blocking_read(u64::try_from(buf.capacity()).unwrap()) { + Ok(result) => { + buf.append(&result); + Ok(()) + } + Err(StreamError::Closed) => Ok(()), + Err(StreamError::LastOperationFailed(e)) => Err(error_to_io(e)), + } + } +} + +impl Stdout { + pub const fn new() -> Stdout { + Stdout(None) + } + + fn stream(&mut self) -> &OutputStream { + self.0.get_or_insert_with(cli::stdout::get_stdout) + } +} + +fn write(stream: &OutputStream, buf: &[u8]) -> io::Result<usize> { + // WASIp2's `blocking_write_and_flush` function is defined as accepting no + // more than 4096 bytes. Larger writes can be issued by manually using + // `check_write`, `write`, and `blocking_flush`, but for now just go ahead + // and use `blocking_write_and_flush` and report a short write and let a + // higher level loop over the result. + const MAX: usize = 4096; + let buf = &buf[..buf.len().min(MAX)]; + match stream.blocking_write_and_flush(buf) { + Ok(()) => Ok(buf.len()), + Err(StreamError::Closed) => Ok(0), + Err(StreamError::LastOperationFailed(e)) => Err(error_to_io(e)), + } +} + +impl io::Write for Stdout { + fn write(&mut self, data: &[u8]) -> io::Result<usize> { + write(self.stream(), data) + } + + fn flush(&mut self) -> io::Result<()> { + // Note that `OutputStream` has a `flush` function but for stdio all + // writes are accompanied with a flush which means that this flush + // doesn't need to do anything. + Ok(()) + } +} + +impl Stderr { + pub const fn new() -> Stderr { + Stderr(None) + } + + fn stream(&mut self) -> &OutputStream { + self.0.get_or_insert_with(cli::stderr::get_stderr) + } +} + +impl io::Write for Stderr { + fn write(&mut self, data: &[u8]) -> io::Result<usize> { + write(self.stream(), data) + } + + fn flush(&mut self) -> io::Result<()> { + // See `Stdout::flush` for why this is a noop. + Ok(()) + } +} + +pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; + +pub fn is_ebadf(_err: &io::Error) -> bool { + // WASIp2 stdio streams are always available so ebadf never shows up. + false +} + +pub fn panic_output() -> Option<impl io::Write> { + Some(Stderr::new()) +} diff --git a/library/std/tests/floats/f32.rs b/library/std/tests/floats/f32.rs index c61a8ec4d20..c29d803b25e 100644 --- a/library/std/tests/floats/f32.rs +++ b/library/std/tests/floats/f32.rs @@ -79,8 +79,8 @@ fn test_log() { let nan: f32 = f32::NAN; let inf: f32 = f32::INFINITY; let neg_inf: f32 = f32::NEG_INFINITY; - assert_approx_eq!(10.0f32.log(10.0), 1.0, APPROX_DELTA); - assert_approx_eq!(2.3f32.log(3.5), 0.664858, APPROX_DELTA); + assert_approx_eq!(10.0f32.log(10.0), 1.0); + assert_approx_eq!(2.3f32.log(3.5), 0.664858); assert_approx_eq!(1.0f32.exp().log(1.0f32.exp()), 1.0, APPROX_DELTA); assert!(1.0f32.log(1.0).is_nan()); assert!(1.0f32.log(-13.9).is_nan()); @@ -140,10 +140,10 @@ fn test_asinh() { assert_approx_eq!(2.0f32.asinh(), 1.443635475178810342493276740273105f32); assert_approx_eq!((-2.0f32).asinh(), -1.443635475178810342493276740273105f32); // regression test for the catastrophic cancellation fixed in 72486 - assert_approx_eq!((-3000.0f32).asinh(), -8.699514775987968673236893537700647f32); + assert_approx_eq!((-3000.0f32).asinh(), -8.699514775987968673236893537700647f32, APPROX_DELTA); // test for low accuracy from issue 104548 - assert_approx_eq!(60.0f32, 60.0f32.sinh().asinh()); + assert_approx_eq!(60.0f32, 60.0f32.sinh().asinh(), APPROX_DELTA); // mul needed for approximate comparison to be meaningful assert_approx_eq!(1.0f32, 1e-15f32.sinh().asinh() * 1e15f32); } @@ -196,8 +196,8 @@ fn test_gamma() { assert_approx_eq!(1.0f32.gamma(), 1.0f32); assert_approx_eq!(2.0f32.gamma(), 1.0f32); assert_approx_eq!(3.0f32.gamma(), 2.0f32); - assert_approx_eq!(4.0f32.gamma(), 6.0f32); - assert_approx_eq!(5.0f32.gamma(), 24.0f32); + assert_approx_eq!(4.0f32.gamma(), 6.0f32, APPROX_DELTA); + assert_approx_eq!(5.0f32.gamma(), 24.0f32, APPROX_DELTA); assert_approx_eq!(0.5f32.gamma(), consts::PI.sqrt()); assert_approx_eq!((-0.5f32).gamma(), -2.0 * consts::PI.sqrt()); assert_eq!(0.0f32.gamma(), f32::INFINITY); @@ -218,7 +218,7 @@ fn test_ln_gamma() { assert_eq!(2.0f32.ln_gamma().1, 1); assert_approx_eq!(3.0f32.ln_gamma().0, 2.0f32.ln()); assert_eq!(3.0f32.ln_gamma().1, 1); - assert_approx_eq!((-0.5f32).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln()); + assert_approx_eq!((-0.5f32).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln(), APPROX_DELTA); assert_eq!((-0.5f32).ln_gamma().1, -1); } diff --git a/library/std/tests/path.rs b/library/std/tests/path.rs index e1576a0d423..fa76c50597b 100644 --- a/library/std/tests/path.rs +++ b/library/std/tests/path.rs @@ -1,4 +1,4 @@ -#![feature(clone_to_uninit, path_add_extension, maybe_uninit_slice, normalize_lexically)] +#![feature(clone_to_uninit, maybe_uninit_slice, normalize_lexically)] use std::clone::CloneToUninit; use std::ffi::OsStr; @@ -2526,3 +2526,9 @@ fn normalize_lexically() { check_err(r"\\?\UNC\server\share\a\..\.."); } } + +#[test] +/// See issue#146183 +fn compare_path_to_str() { + assert!(&PathBuf::from("x") == "x"); +} diff --git a/package-lock.json b/package-lock.json index 44e8b65c28d..def0cfa86a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,7 +5,7 @@ "packages": { "": { "dependencies": { - "browser-ui-test": "^0.21.3", + "browser-ui-test": "^0.22.0", "es-check": "^6.2.1", "eslint": "^8.57.1", "eslint-js": "github:eslint/js", @@ -37,8 +37,6 @@ }, "node_modules/@caporal/core": { "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@caporal/core/-/core-2.0.7.tgz", - "integrity": "sha512-OvKBEidoXUGT28RP3USXFdLgiR5kGCHfRXR1uBQznyxBHaWjGcpH+G1chRqyIVT82pQoJiauOZRIGlrpyAbRYQ==", "license": "MIT", "dependencies": { "@types/glob": "^7.1.1", @@ -61,8 +59,6 @@ }, "node_modules/@colors/colors": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", - "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", "license": "MIT", "engines": { "node": ">=0.1.90" @@ -70,8 +66,6 @@ }, "node_modules/@dabh/diagnostics": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", - "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", "license": "MIT", "dependencies": { "colorspace": "1.1.x", @@ -81,8 +75,6 @@ }, "node_modules/@eslint-community/eslint-utils": { "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", - "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", "license": "MIT", "dependencies": { "eslint-visitor-keys": "^3.4.3" @@ -99,8 +91,6 @@ }, "node_modules/@eslint-community/regexpp": { "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" @@ -108,8 +98,6 @@ }, "node_modules/@eslint/eslintrc": { "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "license": "MIT", "dependencies": { "ajv": "^6.12.4", @@ -131,8 +119,6 @@ }, "node_modules/@eslint/js": { "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", - "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "license": "MIT", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -140,9 +126,6 @@ }, "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", - "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", - "deprecated": "Use @eslint/config-array instead", "license": "Apache-2.0", "dependencies": { "@humanwhocodes/object-schema": "^2.0.3", @@ -155,8 +138,6 @@ }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "license": "Apache-2.0", "engines": { "node": ">=12.22" @@ -168,15 +149,10 @@ }, "node_modules/@humanwhocodes/object-schema": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "deprecated": "Use @eslint/object-schema instead", "license": "BSD-3-Clause" }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", @@ -188,8 +164,6 @@ }, "node_modules/@nodelib/fs.stat": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "license": "MIT", "engines": { "node": ">= 8" @@ -197,8 +171,6 @@ }, "node_modules/@nodelib/fs.walk": { "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", @@ -238,8 +210,6 @@ }, "node_modules/@types/glob": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", "license": "MIT", "dependencies": { "@types/minimatch": "*", @@ -248,32 +218,22 @@ }, "node_modules/@types/lodash": { "version": "4.17.20", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==", "license": "MIT" }, "node_modules/@types/minimatch": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", - "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", "license": "MIT" }, "node_modules/@types/node": { "version": "13.9.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-13.9.3.tgz", - "integrity": "sha512-01s+ac4qerwd6RHD+mVbOEsraDHSgUaefQlEdBbUolnQFjKwCr7luvAlEwW1RFojh67u0z4OUTjPn9LEl4zIkA==", "license": "MIT" }, "node_modules/@types/table": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@types/table/-/table-5.0.0.tgz", - "integrity": "sha512-fQLtGLZXor264zUPWI95WNDsZ3QV43/c0lJpR/h1hhLJumXRmHNsrvBfEzW2YMhb0EWCsn4U6h82IgwsajAuTA==", "license": "MIT" }, "node_modules/@types/tabtab": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/tabtab/-/tabtab-3.0.4.tgz", - "integrity": "sha512-gmh8JsmIYPGRqk8Xb4dmulV37TpLwg0Quo3GJ0LgEcl4v0O92F14PGebBd7LHv9GBEw2KbmBSrvU0/NzIy5AoA==", "license": "MIT", "dependencies": { "@types/node": "*" @@ -281,14 +241,10 @@ }, "node_modules/@types/triple-beam": { "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", - "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", "license": "MIT" }, "node_modules/@types/wrap-ansi": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz", - "integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==", "license": "MIT" }, "node_modules/@types/yauzl": { @@ -303,14 +259,10 @@ }, "node_modules/@ungap/structured-clone": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "license": "ISC" }, "node_modules/acorn": { "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -321,8 +273,6 @@ }, "node_modules/acorn-jsx": { "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" @@ -339,8 +289,6 @@ }, "node_modules/ajv": { "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", @@ -355,8 +303,6 @@ }, "node_modules/ansi-escapes": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", "license": "MIT", "engines": { "node": ">=4" @@ -364,8 +310,6 @@ }, "node_modules/ansi-regex": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "license": "MIT", "engines": { "node": ">=8" @@ -373,8 +317,6 @@ }, "node_modules/ansi-styles": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -388,8 +330,6 @@ }, "node_modules/argparse": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "license": "Python-2.0" }, "node_modules/ast-types": { @@ -406,8 +346,6 @@ }, "node_modules/astral-regex": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", "license": "MIT", "engines": { "node": ">=4" @@ -415,8 +353,6 @@ }, "node_modules/async": { "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", "license": "MIT" }, "node_modules/b4a": { @@ -427,21 +363,19 @@ }, "node_modules/balanced-match": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "license": "MIT" }, "node_modules/bare-events": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.6.0.tgz", - "integrity": "sha512-EKZ5BTXYExaNqi3I3f9RtEsaI/xBSGjE0XZCZilPzFAV/goswFHuPd9jEZlPIZ/iNZJwDSao9qRiScySz7MbQg==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.6.1.tgz", + "integrity": "sha512-AuTJkq9XmE6Vk0FJVNq5QxETrSA/vKHarWVBG5l/JbdCL1prJemiyJqUS0jrlXO0MftuPq4m3YVYhoNc5+aE/g==", "license": "Apache-2.0", "optional": true }, "node_modules/bare-fs": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.1.6.tgz", - "integrity": "sha512-25RsLF33BqooOEFNdMcEhMpJy8EoR88zSMrnOQOaM3USnOK2VmaJ1uaQEwPA6AQjrv1lXChScosN6CzbwbO9OQ==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.2.3.tgz", + "integrity": "sha512-1aGs5pRVLToMQ79elP+7cc0u0s/wXAzfBv/7hDloT7WFggLqECCas5qqPky7WHCFdsBH5WDq6sD4fAoz5sJbtA==", "license": "Apache-2.0", "optional": true, "dependencies": { @@ -462,9 +396,9 @@ } }, "node_modules/bare-os": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.6.1.tgz", - "integrity": "sha512-uaIjxokhFidJP+bmmvKSgiMzj2sV5GPHaZVAIktcxcpCyBFFWO+YlikVAdhmUo2vYFvFhOXIAlldqV29L8126g==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.6.2.tgz", + "integrity": "sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A==", "license": "Apache-2.0", "optional": true, "engines": { @@ -482,9 +416,9 @@ } }, "node_modules/bare-stream": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.6.5.tgz", - "integrity": "sha512-jSmxKJNJmHySi6hC42zlZnq00rga4jjxcgNZjY9N5WlOe/iOoGRtdwGsHzQv2RlH2KOYMwGUXhf2zXd32BA9RA==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.7.0.tgz", + "integrity": "sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A==", "license": "Apache-2.0", "optional": true, "dependencies": { @@ -534,8 +468,6 @@ }, "node_modules/brace-expansion": { "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -544,8 +476,6 @@ }, "node_modules/braces": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -555,9 +485,9 @@ } }, "node_modules/browser-ui-test": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/browser-ui-test/-/browser-ui-test-0.21.1.tgz", - "integrity": "sha512-b3a9mhALAmbP+GifoN/c295RPyuyfIUFMz0DtlBHbeDW5PYQTMHZZJtm7R2UyP6JiIQSkR+7227sS3maMGMUTg==", + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/browser-ui-test/-/browser-ui-test-0.22.0.tgz", + "integrity": "sha512-p/C02TMybTDKsAjpGOdnyNC0Q25KDae/fKMnvHaqcJ0tXRqNKwndW2Ltq7HTmin5xqg8GGOmysEgWTZkXu6pfA==", "license": "MIT", "dependencies": { "css-unit-converter": "^1.1.2", @@ -604,8 +534,6 @@ }, "node_modules/callsites": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "license": "MIT", "engines": { "node": ">=6" @@ -613,8 +541,6 @@ }, "node_modules/chalk": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -626,8 +552,6 @@ }, "node_modules/chardet": { "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "license": "MIT" }, "node_modules/chromium-bidi": { @@ -646,8 +570,6 @@ }, "node_modules/cli-cursor": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", "license": "MIT", "dependencies": { "restore-cursor": "^2.0.0" @@ -658,8 +580,6 @@ }, "node_modules/cli-width": { "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", - "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", "license": "ISC" }, "node_modules/cliui": { @@ -724,8 +644,6 @@ }, "node_modules/color": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", - "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", "license": "MIT", "dependencies": { "color-convert": "^1.9.3", @@ -734,8 +652,6 @@ }, "node_modules/color-convert": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -746,14 +662,10 @@ }, "node_modules/color-name": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, "node_modules/color-string": { "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", "license": "MIT", "dependencies": { "color-name": "^1.0.0", @@ -762,8 +674,6 @@ }, "node_modules/color/node_modules/color-convert": { "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "license": "MIT", "dependencies": { "color-name": "1.1.3" @@ -771,14 +681,10 @@ }, "node_modules/color/node_modules/color-name": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "license": "MIT" }, "node_modules/colorspace": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", - "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", "license": "MIT", "dependencies": { "color": "^3.1.3", @@ -787,8 +693,6 @@ }, "node_modules/concat-map": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "license": "MIT" }, "node_modules/cosmiconfig": { @@ -819,8 +723,6 @@ }, "node_modules/cross-spawn": { "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -848,8 +750,6 @@ }, "node_modules/debug": { "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -865,8 +765,6 @@ }, "node_modules/deep-is": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "license": "MIT" }, "node_modules/degenerator": { @@ -891,8 +789,6 @@ }, "node_modules/doctrine": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "license": "Apache-2.0", "dependencies": { "esutils": "^2.0.2" @@ -903,14 +799,10 @@ }, "node_modules/emoji-regex": { "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", "license": "MIT" }, "node_modules/enabled": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", "license": "MIT" }, "node_modules/end-of-stream": { @@ -942,8 +834,6 @@ }, "node_modules/es-check": { "version": "6.2.1", - "resolved": "https://registry.npmjs.org/es-check/-/es-check-6.2.1.tgz", - "integrity": "sha512-IPiRXUlwSTd2yMklIf9yEGe6GK5wCS8Sz1aTNHm1QSiYzI4aiq19giYbLi95tb+e0JJVKmcU0iQXQWW60a8V9A==", "license": "MIT", "dependencies": { "@caporal/core": "^2.0.2", @@ -959,8 +849,6 @@ }, "node_modules/es6-promisify": { "version": "6.1.1", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-6.1.1.tgz", - "integrity": "sha512-HBL8I3mIki5C1Cc9QjKUenHtnG0A5/xA8Q/AllRcfiwl2CZFXGK7ddBiCoRwAix4i2KxcQfjtIVcrVbB3vbmwg==", "license": "MIT" }, "node_modules/escalade": { @@ -974,8 +862,6 @@ }, "node_modules/escape-string-regexp": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "license": "MIT", "engines": { "node": ">=10" @@ -1007,9 +893,6 @@ }, "node_modules/eslint": { "version": "8.57.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", - "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", - "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", @@ -1063,15 +946,13 @@ }, "node_modules/eslint-js": { "version": "1.0.0", - "resolved": "git+ssh://git@github.com/eslint/js.git#5bd12e3772d361af2c4bfad774ea7bfd9caae6bc", + "resolved": "git+ssh://git@github.com/eslint/js.git#9e5b4fabf073b915abc56d6c14cc24177036d43e", "workspaces": [ "packages/*" ] }, "node_modules/eslint-scope": { "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", @@ -1086,8 +967,6 @@ }, "node_modules/eslint-visitor-keys": { "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "license": "Apache-2.0", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1098,8 +977,6 @@ }, "node_modules/eslint/node_modules/chalk": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -1114,8 +991,6 @@ }, "node_modules/espree": { "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "license": "BSD-2-Clause", "dependencies": { "acorn": "^8.9.0", @@ -1144,8 +1019,6 @@ }, "node_modules/esquery": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" @@ -1156,8 +1029,6 @@ }, "node_modules/esrecurse": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" @@ -1168,8 +1039,6 @@ }, "node_modules/estraverse": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "license": "BSD-2-Clause", "engines": { "node": ">=4.0" @@ -1177,8 +1046,6 @@ }, "node_modules/esutils": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" @@ -1186,8 +1053,6 @@ }, "node_modules/external-editor": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", "license": "MIT", "dependencies": { "chardet": "^0.7.0", @@ -1220,8 +1085,6 @@ }, "node_modules/fast-deep-equal": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "license": "MIT" }, "node_modules/fast-fifo": { @@ -1232,8 +1095,6 @@ }, "node_modules/fast-glob": { "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -1248,8 +1109,6 @@ }, "node_modules/fast-glob/node_modules/glob-parent": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "license": "ISC", "dependencies": { "is-glob": "^4.0.1" @@ -1260,20 +1119,14 @@ }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "license": "MIT" }, "node_modules/fastq": { "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", "license": "ISC", "dependencies": { "reusify": "^1.0.4" @@ -1290,14 +1143,10 @@ }, "node_modules/fecha": { "version": "4.2.3", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", - "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", "license": "MIT" }, "node_modules/figures": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", "license": "MIT", "dependencies": { "escape-string-regexp": "^1.0.5" @@ -1308,8 +1157,6 @@ }, "node_modules/figures/node_modules/escape-string-regexp": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "license": "MIT", "engines": { "node": ">=0.8.0" @@ -1317,8 +1164,6 @@ }, "node_modules/file-entry-cache": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "license": "MIT", "dependencies": { "flat-cache": "^3.0.4" @@ -1329,8 +1174,6 @@ }, "node_modules/fill-range": { "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -1341,8 +1184,6 @@ }, "node_modules/find-up": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "license": "MIT", "dependencies": { "locate-path": "^6.0.0", @@ -1357,8 +1198,6 @@ }, "node_modules/flat-cache": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "license": "MIT", "dependencies": { "flatted": "^3.2.9", @@ -1371,20 +1210,14 @@ }, "node_modules/flatted": { "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", "license": "ISC" }, "node_modules/fn.name": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", "license": "MIT" }, "node_modules/fs.realpath": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "license": "ISC" }, "node_modules/get-caller-file": { @@ -1427,9 +1260,6 @@ }, "node_modules/glob": { "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", @@ -1448,8 +1278,6 @@ }, "node_modules/glob-parent": { "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "license": "ISC", "dependencies": { "is-glob": "^4.0.3" @@ -1460,8 +1288,6 @@ }, "node_modules/globals": { "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "license": "MIT", "dependencies": { "type-fest": "^0.20.2" @@ -1475,14 +1301,10 @@ }, "node_modules/graphemer": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "license": "MIT" }, "node_modules/has-flag": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "license": "MIT", "engines": { "node": ">=8" @@ -1516,8 +1338,6 @@ }, "node_modules/iconv-lite": { "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" @@ -1548,8 +1368,6 @@ }, "node_modules/ignore": { "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "license": "MIT", "engines": { "node": ">= 4" @@ -1557,8 +1375,6 @@ }, "node_modules/import-fresh": { "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "license": "MIT", "dependencies": { "parent-module": "^1.0.0", @@ -1573,8 +1389,6 @@ }, "node_modules/imurmurhash": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "license": "MIT", "engines": { "node": ">=0.8.19" @@ -1582,9 +1396,6 @@ }, "node_modules/inflight": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "license": "ISC", "dependencies": { "once": "^1.3.0", @@ -1593,14 +1404,10 @@ }, "node_modules/inherits": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, "node_modules/inquirer": { "version": "6.5.2", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz", - "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==", "license": "MIT", "dependencies": { "ansi-escapes": "^3.2.0", @@ -1623,8 +1430,6 @@ }, "node_modules/inquirer/node_modules/ansi-regex": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", "license": "MIT", "engines": { "node": ">=6" @@ -1632,8 +1437,6 @@ }, "node_modules/inquirer/node_modules/ansi-styles": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "license": "MIT", "dependencies": { "color-convert": "^1.9.0" @@ -1644,8 +1447,6 @@ }, "node_modules/inquirer/node_modules/chalk": { "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "license": "MIT", "dependencies": { "ansi-styles": "^3.2.1", @@ -1658,8 +1459,6 @@ }, "node_modules/inquirer/node_modules/color-convert": { "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "license": "MIT", "dependencies": { "color-name": "1.1.3" @@ -1667,14 +1466,10 @@ }, "node_modules/inquirer/node_modules/color-name": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "license": "MIT" }, "node_modules/inquirer/node_modules/escape-string-regexp": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "license": "MIT", "engines": { "node": ">=0.8.0" @@ -1682,8 +1477,6 @@ }, "node_modules/inquirer/node_modules/has-flag": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "license": "MIT", "engines": { "node": ">=4" @@ -1691,8 +1484,6 @@ }, "node_modules/inquirer/node_modules/string-width": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "license": "MIT", "dependencies": { "is-fullwidth-code-point": "^2.0.0", @@ -1704,8 +1495,6 @@ }, "node_modules/inquirer/node_modules/string-width/node_modules/ansi-regex": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", "license": "MIT", "engines": { "node": ">=4" @@ -1713,8 +1502,6 @@ }, "node_modules/inquirer/node_modules/string-width/node_modules/strip-ansi": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", "license": "MIT", "dependencies": { "ansi-regex": "^3.0.0" @@ -1725,8 +1512,6 @@ }, "node_modules/inquirer/node_modules/strip-ansi": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "license": "MIT", "dependencies": { "ansi-regex": "^4.1.0" @@ -1737,8 +1522,6 @@ }, "node_modules/inquirer/node_modules/supports-color": { "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "license": "MIT", "dependencies": { "has-flag": "^3.0.0" @@ -1748,14 +1531,10 @@ } }, "node_modules/ip-address": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", - "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", + "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", "license": "MIT", - "dependencies": { - "jsbn": "1.1.0", - "sprintf-js": "^1.1.3" - }, "engines": { "node": ">= 12" } @@ -1768,8 +1547,6 @@ }, "node_modules/is-extglob": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -1777,8 +1554,6 @@ }, "node_modules/is-fullwidth-code-point": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", "license": "MIT", "engines": { "node": ">=4" @@ -1786,8 +1561,6 @@ }, "node_modules/is-glob": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -1798,8 +1571,6 @@ }, "node_modules/is-number": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "license": "MIT", "engines": { "node": ">=0.12.0" @@ -1807,8 +1578,6 @@ }, "node_modules/is-path-inside": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "license": "MIT", "engines": { "node": ">=8" @@ -1816,8 +1585,6 @@ }, "node_modules/is-stream": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "license": "MIT", "engines": { "node": ">=8" @@ -1828,8 +1595,6 @@ }, "node_modules/isexe": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "license": "ISC" }, "node_modules/js-tokens": { @@ -1840,8 +1605,6 @@ }, "node_modules/js-yaml": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -1850,16 +1613,8 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/jsbn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", - "license": "MIT" - }, "node_modules/json-buffer": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "license": "MIT" }, "node_modules/json-parse-even-better-errors": { @@ -1870,20 +1625,14 @@ }, "node_modules/json-schema-traverse": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "license": "MIT" }, "node_modules/keyv": { "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "license": "MIT", "dependencies": { "json-buffer": "3.0.1" @@ -1891,14 +1640,10 @@ }, "node_modules/kuler": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", "license": "MIT" }, "node_modules/levn": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1", @@ -1916,8 +1661,6 @@ }, "node_modules/locate-path": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "license": "MIT", "dependencies": { "p-locate": "^5.0.0" @@ -1931,20 +1674,14 @@ }, "node_modules/lodash": { "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "license": "MIT" }, "node_modules/lodash.merge": { "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "license": "MIT" }, "node_modules/logform": { "version": "2.7.0", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", - "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", "license": "MIT", "dependencies": { "@colors/colors": "1.6.0", @@ -1969,8 +1706,6 @@ }, "node_modules/merge2": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "license": "MIT", "engines": { "node": ">= 8" @@ -1978,8 +1713,6 @@ }, "node_modules/micromatch": { "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "license": "MIT", "dependencies": { "braces": "^3.0.3", @@ -1991,8 +1724,6 @@ }, "node_modules/mimic-fn": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", "license": "MIT", "engines": { "node": ">=4" @@ -2000,8 +1731,6 @@ }, "node_modules/minimatch": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -2012,8 +1741,6 @@ }, "node_modules/minimist": { "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2027,8 +1754,6 @@ }, "node_modules/mkdirp": { "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "license": "MIT", "dependencies": { "minimist": "^1.2.6" @@ -2039,20 +1764,14 @@ }, "node_modules/ms": { "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, "node_modules/mute-stream": { "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ==", "license": "ISC" }, "node_modules/natural-compare": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "license": "MIT" }, "node_modules/netmask": { @@ -2066,8 +1785,6 @@ }, "node_modules/once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "license": "ISC", "dependencies": { "wrappy": "1" @@ -2075,8 +1792,6 @@ }, "node_modules/one-time": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", - "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", "license": "MIT", "dependencies": { "fn.name": "1.x.x" @@ -2084,8 +1799,6 @@ }, "node_modules/onetime": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", "license": "MIT", "dependencies": { "mimic-fn": "^1.0.0" @@ -2096,8 +1809,6 @@ }, "node_modules/optionator": { "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "license": "MIT", "dependencies": { "deep-is": "^0.1.3", @@ -2113,8 +1824,6 @@ }, "node_modules/os-tmpdir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -2122,8 +1831,6 @@ }, "node_modules/p-limit": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" @@ -2137,8 +1844,6 @@ }, "node_modules/p-locate": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "license": "MIT", "dependencies": { "p-limit": "^3.0.2" @@ -2184,8 +1889,6 @@ }, "node_modules/parent-module": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "license": "MIT", "dependencies": { "callsites": "^3.0.0" @@ -2214,8 +1917,6 @@ }, "node_modules/path-exists": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "license": "MIT", "engines": { "node": ">=8" @@ -2223,8 +1924,6 @@ }, "node_modules/path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -2232,8 +1931,6 @@ }, "node_modules/path-key": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "license": "MIT", "engines": { "node": ">=8" @@ -2253,8 +1950,6 @@ }, "node_modules/picomatch": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "license": "MIT", "engines": { "node": ">=8.6" @@ -2274,8 +1969,6 @@ }, "node_modules/prelude-ls": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "license": "MIT", "engines": { "node": ">= 0.8.0" @@ -2327,8 +2020,6 @@ }, "node_modules/punycode": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "license": "MIT", "engines": { "node": ">=6" @@ -2338,6 +2029,7 @@ "version": "22.15.0", "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-22.15.0.tgz", "integrity": "sha512-XjCY1SiSEi1T7iSYuxS82ft85kwDJUS7wj1Z0eGVXKdtr5g4xnVcbjwxhq5xBnpK/E7x1VZZoJDxpjAOasHT4Q==", + "deprecated": "< 24.10.2 is no longer supported", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -2371,8 +2063,6 @@ }, "node_modules/queue-microtask": { "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "funding": [ { "type": "github", @@ -2391,8 +2081,6 @@ }, "node_modules/readable-stream": { "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "license": "MIT", "dependencies": { "inherits": "^2.0.3", @@ -2423,8 +2111,6 @@ }, "node_modules/resolve-from": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "license": "MIT", "engines": { "node": ">=4" @@ -2432,8 +2118,6 @@ }, "node_modules/restore-cursor": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", "license": "MIT", "dependencies": { "onetime": "^2.0.0", @@ -2445,8 +2129,6 @@ }, "node_modules/reusify": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "license": "MIT", "engines": { "iojs": ">=1.0.0", @@ -2455,9 +2137,6 @@ }, "node_modules/rimraf": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", "license": "ISC", "dependencies": { "glob": "^7.1.3" @@ -2471,8 +2150,6 @@ }, "node_modules/run-async": { "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", "license": "MIT", "engines": { "node": ">=0.12.0" @@ -2480,8 +2157,6 @@ }, "node_modules/run-parallel": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "funding": [ { "type": "github", @@ -2503,8 +2178,6 @@ }, "node_modules/rxjs": { "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", "license": "Apache-2.0", "dependencies": { "tslib": "^1.9.0" @@ -2515,14 +2188,10 @@ }, "node_modules/rxjs/node_modules/tslib": { "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "license": "0BSD" }, "node_modules/safe-buffer": { "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "funding": [ { "type": "github", @@ -2541,8 +2210,6 @@ }, "node_modules/safe-stable-stringify": { "version": "2.5.0", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", - "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", "license": "MIT", "engines": { "node": ">=10" @@ -2550,8 +2217,6 @@ }, "node_modules/safer-buffer": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, "node_modules/semver": { @@ -2568,8 +2233,6 @@ }, "node_modules/shebang-command": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -2580,8 +2243,6 @@ }, "node_modules/shebang-regex": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "license": "MIT", "engines": { "node": ">=8" @@ -2589,14 +2250,10 @@ }, "node_modules/signal-exit": { "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "license": "ISC" }, "node_modules/simple-swizzle": { "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", "license": "MIT", "dependencies": { "is-arrayish": "^0.3.1" @@ -2604,14 +2261,10 @@ }, "node_modules/simple-swizzle/node_modules/is-arrayish": { "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", "license": "MIT" }, "node_modules/slice-ansi": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", - "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", "license": "MIT", "dependencies": { "ansi-styles": "^3.2.0", @@ -2624,8 +2277,6 @@ }, "node_modules/slice-ansi/node_modules/ansi-styles": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "license": "MIT", "dependencies": { "color-convert": "^1.9.0" @@ -2636,8 +2287,6 @@ }, "node_modules/slice-ansi/node_modules/color-convert": { "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "license": "MIT", "dependencies": { "color-name": "1.1.3" @@ -2645,8 +2294,6 @@ }, "node_modules/slice-ansi/node_modules/color-name": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "license": "MIT" }, "node_modules/smart-buffer": { @@ -2660,12 +2307,12 @@ } }, "node_modules/socks": { - "version": "2.8.6", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.6.tgz", - "integrity": "sha512-pe4Y2yzru68lXCb38aAqRf5gvN8YdjP1lok5o0J7BOHljkyCGKVz7H3vpVIXKD27rj2giOJ7DwVyk/GWrPHDWA==", + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", "license": "MIT", "dependencies": { - "ip-address": "^9.0.5", + "ip-address": "^10.0.1", "smart-buffer": "^4.2.0" }, "engines": { @@ -2697,16 +2344,8 @@ "node": ">=0.10.0" } }, - "node_modules/sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", - "license": "BSD-3-Clause" - }, "node_modules/stack-trace": { "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", "license": "MIT", "engines": { "node": "*" @@ -2727,8 +2366,6 @@ }, "node_modules/string_decoder": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "license": "MIT", "dependencies": { "safe-buffer": "~5.2.0" @@ -2736,8 +2373,6 @@ }, "node_modules/string-width": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", "license": "MIT", "dependencies": { "emoji-regex": "^7.0.1", @@ -2750,8 +2385,6 @@ }, "node_modules/string-width/node_modules/ansi-regex": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", "license": "MIT", "engines": { "node": ">=6" @@ -2759,8 +2392,6 @@ }, "node_modules/string-width/node_modules/strip-ansi": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "license": "MIT", "dependencies": { "ansi-regex": "^4.1.0" @@ -2771,8 +2402,6 @@ }, "node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -2783,8 +2412,6 @@ }, "node_modules/strip-json-comments": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "license": "MIT", "engines": { "node": ">=8" @@ -2795,8 +2422,6 @@ }, "node_modules/supports-color": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -2807,8 +2432,6 @@ }, "node_modules/table": { "version": "5.4.6", - "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", - "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", "license": "BSD-3-Clause", "dependencies": { "ajv": "^6.10.2", @@ -2822,8 +2445,6 @@ }, "node_modules/tabtab": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/tabtab/-/tabtab-3.0.2.tgz", - "integrity": "sha512-jANKmUe0sIQc/zTALTBy186PoM/k6aPrh3A7p6AaAfF6WPSbTx1JYeGIGH162btpH+mmVEXln+UxwViZHO2Jhg==", "license": "MIT", "dependencies": { "debug": "^4.0.1", @@ -2870,26 +2491,18 @@ }, "node_modules/text-hex": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", "license": "MIT" }, "node_modules/text-table": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "license": "MIT" }, "node_modules/through": { "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "license": "MIT" }, "node_modules/tmp": { "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "license": "MIT", "dependencies": { "os-tmpdir": "~1.0.2" @@ -2900,8 +2513,6 @@ }, "node_modules/to-regex-range": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -2912,8 +2523,6 @@ }, "node_modules/triple-beam": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", - "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", "license": "MIT", "engines": { "node": ">= 14.0.0" @@ -2927,8 +2536,6 @@ }, "node_modules/type-check": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1" @@ -2939,8 +2546,6 @@ }, "node_modules/type-fest": { "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" @@ -2951,8 +2556,6 @@ }, "node_modules/typescript": { "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -2974,8 +2577,6 @@ }, "node_modules/untildify": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-3.0.3.tgz", - "integrity": "sha512-iSk/J8efr8uPT/Z4eSUywnqyrQU7DSdMfdqK4iWEaUVVmcP5JcnpRqmVMwcwcnmI1ATFNgC5V90u09tBynNFKA==", "license": "MIT", "engines": { "node": ">=4" @@ -2983,8 +2584,6 @@ }, "node_modules/uri-js": { "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" @@ -2998,14 +2597,10 @@ }, "node_modules/util-deprecate": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, "node_modules/which": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -3019,8 +2614,6 @@ }, "node_modules/winston": { "version": "3.17.0", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz", - "integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==", "license": "MIT", "dependencies": { "@colors/colors": "^1.6.0", @@ -3041,8 +2634,6 @@ }, "node_modules/winston-transport": { "version": "4.9.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", - "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", "license": "MIT", "dependencies": { "logform": "^2.7.0", @@ -3055,8 +2646,6 @@ }, "node_modules/word-wrap": { "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -3064,8 +2653,6 @@ }, "node_modules/wrap-ansi": { "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -3078,14 +2665,10 @@ }, "node_modules/wrap-ansi/node_modules/emoji-regex": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "license": "MIT" }, "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "license": "MIT", "engines": { "node": ">=8" @@ -3093,8 +2676,6 @@ }, "node_modules/wrap-ansi/node_modules/string-width": { "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -3107,8 +2688,6 @@ }, "node_modules/wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" }, "node_modules/ws": { @@ -3209,8 +2788,6 @@ }, "node_modules/yocto-queue": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "license": "MIT", "engines": { "node": ">=10" diff --git a/package.json b/package.json index 04f4c501d1f..976d6303634 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "dependencies": { - "browser-ui-test": "^0.21.3", + "browser-ui-test": "^0.22.0", "es-check": "^6.2.1", "eslint": "^8.57.1", "eslint-js": "github:eslint/js", diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index 1535d087033..fe832652d25 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -269,14 +269,14 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "filetime" -version = "0.2.25" +version = "0.2.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" +checksum = "bc0505cd1b6fa6580283f6bdf70a73fcf4aba1184038c90902b92b3dd0df63ed" dependencies = [ "cfg-if", "libc", "libredox", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -322,11 +322,11 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "home" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -364,12 +364,12 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "junction" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72bbdfd737a243da3dfc1f99ee8d6e166480f17ab4ac84d7c34aacd73fc7bd16" +checksum = "c52f6e1bf39a7894f618c9d378904a11dbd7e10fe3ec20d1173600e79b1408d8" dependencies = [ "scopeguard", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] @@ -434,6 +434,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] +name = "normpath" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8911957c4b1549ac0dc74e30db9c8b0e66ddcd6d7acc33098f4c63a64a6d7ed" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] name = "ntapi" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -497,12 +506,13 @@ checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "opener" -version = "0.5.2" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "293c15678e37254c15bd2f092314abb4e51d7fdde05c2021279c12631b54f005" +checksum = "771b9704f8cd8b424ec747a320b30b47517a6966ba2c7da90047c16f4a962223" dependencies = [ "bstr", - "winapi", + "normpath", + "windows-sys 0.59.0", ] [[package]] @@ -757,15 +767,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.19.0" +version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488960f40a3fd53d72c2a29a58722561dee8afdd175bd88e3db4677d7b2ba600" +checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e" dependencies = [ "fastrand", "getrandom", "once_cell", "rustix", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -929,11 +939,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -1045,20 +1055,20 @@ dependencies = [ [[package]] name = "windows-sys" -version = "0.52.0" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] name = "windows-sys" -version = "0.59.0" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets", + "windows-targets 0.53.2", ] [[package]] @@ -1067,14 +1077,30 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", ] [[package]] @@ -1084,48 +1110,96 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + +[[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + +[[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + +[[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + +[[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + +[[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + +[[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + +[[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] name = "wit-bindgen-rt" version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index c7175584465..9a76a7dda2a 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -42,7 +42,7 @@ home = "0.5" ignore = "0.4" libc = "0.2" object = { version = "0.36.3", default-features = false, features = ["archive", "coff", "read_core", "std", "unaligned"] } -opener = "0.5" +opener = "0.8" semver = "1.0" serde = "1.0" # Directly use serde_derive rather than through the derive feature of serde to allow building both @@ -67,7 +67,7 @@ tracing-subscriber = { version = "0.3", optional = true, features = ["env-filter tempfile = { version = "3.15.0", optional = true } [target.'cfg(windows)'.dependencies.junction] -version = "1.0.0" +version = "1.3.0" [target.'cfg(windows)'.dependencies.windows] version = "0.61" diff --git a/src/bootstrap/mk/Makefile.in b/src/bootstrap/mk/Makefile.in index 82c05092dfa..03caa764ccf 100644 --- a/src/bootstrap/mk/Makefile.in +++ b/src/bootstrap/mk/Makefile.in @@ -76,7 +76,7 @@ check-aux: library/std \ $(BOOTSTRAP_ARGS) \ -- \ - --skip fs:: --skip net:: --skip process:: --skip sys::fd:: --skip sys::pal:: + --skip fs:: --skip net:: --skip process:: --skip sys:: # Also test some very target-specific modules on other targets # (making sure to cover an i686 target as well). $(Q)MIRIFLAGS="-Zmiri-disable-isolation" BOOTSTRAP_SKIP_TARGET_SANITY=1 \ diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index a604e7c0585..043457f64e5 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -389,7 +389,7 @@ impl Step for Rustc { /// Represents a compiler that can check something. /// -/// If the compiler was created for `Mode::ToolRustc` or `Mode::Codegen`, it will also contain +/// If the compiler was created for `Mode::ToolRustcPrivate` or `Mode::Codegen`, it will also contain /// .rmeta artifacts from rustc that was already checked using `build_compiler`. /// /// All steps that use this struct in a "general way" (i.e. they don't know exactly what kind of @@ -469,7 +469,7 @@ pub fn prepare_compiler_for_check( build_compiler } } - Mode::ToolRustc | Mode::Codegen => { + Mode::ToolRustcPrivate | Mode::Codegen => { // Check Rustc to produce the required rmeta artifacts for rustc_private, and then // return the build compiler that was used to check rustc. // We do not need to check examples/tests/etc. of Rustc for rustc_private, so we pass @@ -767,19 +767,22 @@ fn run_tool_check_step( tool_check_step!(Rustdoc { path: "src/tools/rustdoc", alt_path: "src/librustdoc", - mode: |_builder| Mode::ToolRustc + mode: |_builder| Mode::ToolRustcPrivate }); // Clippy, miri and Rustfmt are hybrids. They are external tools, but use a git subtree instead // of a submodule. Since the SourceType only drives the deny-warnings // behavior, treat it as in-tree so that any new warnings in clippy will be // rejected. -tool_check_step!(Clippy { path: "src/tools/clippy", mode: |_builder| Mode::ToolRustc }); -tool_check_step!(Miri { path: "src/tools/miri", mode: |_builder| Mode::ToolRustc }); -tool_check_step!(CargoMiri { path: "src/tools/miri/cargo-miri", mode: |_builder| Mode::ToolRustc }); -tool_check_step!(Rustfmt { path: "src/tools/rustfmt", mode: |_builder| Mode::ToolRustc }); +tool_check_step!(Clippy { path: "src/tools/clippy", mode: |_builder| Mode::ToolRustcPrivate }); +tool_check_step!(Miri { path: "src/tools/miri", mode: |_builder| Mode::ToolRustcPrivate }); +tool_check_step!(CargoMiri { + path: "src/tools/miri/cargo-miri", + mode: |_builder| Mode::ToolRustcPrivate +}); +tool_check_step!(Rustfmt { path: "src/tools/rustfmt", mode: |_builder| Mode::ToolRustcPrivate }); tool_check_step!(RustAnalyzer { path: "src/tools/rust-analyzer", - mode: |_builder| Mode::ToolRustc, + mode: |_builder| Mode::ToolRustcPrivate, allow_features: tool::RustAnalyzer::ALLOW_FEATURES, enable_features: ["in-rust-tree"], }); @@ -836,3 +839,9 @@ tool_check_step!(Linkchecker { mode: |_builder| Mode::ToolBootstrap, default: false }); + +tool_check_step!(BumpStage0 { + path: "src/tools/bump-stage0", + mode: |_builder| Mode::ToolBootstrap, + default: false +}); diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs index 05f8b240291..2083c675e1f 100644 --- a/src/bootstrap/src/core/build_steps/clippy.rs +++ b/src/bootstrap/src/core/build_steps/clippy.rs @@ -366,8 +366,13 @@ impl Step for CodegenGcc { ); self.build_compiler.configure_cargo(&mut cargo); - let _guard = - builder.msg(Kind::Clippy, "rustc_codegen_gcc", Mode::ToolRustc, build_compiler, target); + let _guard = builder.msg( + Kind::Clippy, + "rustc_codegen_gcc", + Mode::ToolRustcPrivate, + build_compiler, + target, + ); let stamp = BuildStamp::new(&builder.cargo_out(build_compiler, Mode::Codegen, target)) .with_prefix("rustc_codegen_gcc-check"); @@ -478,8 +483,8 @@ lint_any!( Bootstrap, "src/bootstrap", "bootstrap", Mode::ToolTarget; BuildHelper, "src/build_helper", "build_helper", Mode::ToolTarget; BuildManifest, "src/tools/build-manifest", "build-manifest", Mode::ToolTarget; - CargoMiri, "src/tools/miri/cargo-miri", "cargo-miri", Mode::ToolRustc; - Clippy, "src/tools/clippy", "clippy", Mode::ToolRustc; + CargoMiri, "src/tools/miri/cargo-miri", "cargo-miri", Mode::ToolRustcPrivate; + Clippy, "src/tools/clippy", "clippy", Mode::ToolRustcPrivate; CollectLicenseMetadata, "src/tools/collect-license-metadata", "collect-license-metadata", Mode::ToolTarget; Compiletest, "src/tools/compiletest", "compiletest", Mode::ToolTarget; CoverageDump, "src/tools/coverage-dump", "coverage-dump", Mode::ToolTarget; @@ -487,14 +492,14 @@ lint_any!( Jsondoclint, "src/tools/jsondoclint", "jsondoclint", Mode::ToolTarget; LintDocs, "src/tools/lint-docs", "lint-docs", Mode::ToolTarget; LlvmBitcodeLinker, "src/tools/llvm-bitcode-linker", "llvm-bitcode-linker", Mode::ToolTarget; - Miri, "src/tools/miri", "miri", Mode::ToolRustc; + Miri, "src/tools/miri", "miri", Mode::ToolRustcPrivate; MiroptTestTools, "src/tools/miropt-test-tools", "miropt-test-tools", Mode::ToolTarget; OptDist, "src/tools/opt-dist", "opt-dist", Mode::ToolTarget; RemoteTestClient, "src/tools/remote-test-client", "remote-test-client", Mode::ToolTarget; RemoteTestServer, "src/tools/remote-test-server", "remote-test-server", Mode::ToolTarget; - RustAnalyzer, "src/tools/rust-analyzer", "rust-analyzer", Mode::ToolRustc; - Rustdoc, "src/librustdoc", "clippy", Mode::ToolRustc; - Rustfmt, "src/tools/rustfmt", "rustfmt", Mode::ToolRustc; + RustAnalyzer, "src/tools/rust-analyzer", "rust-analyzer", Mode::ToolRustcPrivate; + Rustdoc, "src/librustdoc", "clippy", Mode::ToolRustcPrivate; + Rustfmt, "src/tools/rustfmt", "rustfmt", Mode::ToolRustcPrivate; RustInstaller, "src/tools/rust-installer", "rust-installer", Mode::ToolTarget; Tidy, "src/tools/tidy", "tidy", Mode::ToolTarget; TestFloatParse, "src/tools/test-float-parse", "test-float-parse", Mode::ToolStd; diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index f113dd7683d..820dda5a652 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -823,6 +823,18 @@ pub struct RustcDev { target: TargetSelection, } +impl RustcDev { + pub fn new(builder: &Builder<'_>, target: TargetSelection) -> Self { + Self { + // We currently always ship a stage 2 rustc-dev component, so we build it with the + // stage 1 compiler. This might change in the future. + // The precise stage used here is important, so we hard-code it. + build_compiler: builder.compiler(1, builder.config.host_target), + target, + } + } +} + impl Step for RustcDev { type Output = Option<GeneratedTarball>; const DEFAULT: bool = true; @@ -833,13 +845,7 @@ impl Step for RustcDev { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(RustcDev { - // We currently always ship a stage 2 rustc-dev component, so we build it with the - // stage 1 compiler. This might change in the future. - // The precise stage used here is important, so we hard-code it. - build_compiler: run.builder.compiler(1, run.builder.config.host_target), - target: run.target, - }); + run.builder.ensure(RustcDev::new(run.builder, run.target)); } fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> { diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 0789eefa894..7865b685659 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -575,6 +575,31 @@ impl Step for SharedAssets { FileType::Regular, ); + builder.copy_link( + &builder + .src + .join("src") + .join("librustdoc") + .join("html") + .join("static") + .join("images") + .join("favicon.svg"), + &out.join("favicon.svg"), + FileType::Regular, + ); + builder.copy_link( + &builder + .src + .join("src") + .join("librustdoc") + .join("html") + .join("static") + .join("images") + .join("favicon-32x32.png"), + &out.join("favicon-32x32.png"), + FileType::Regular, + ); + SharedAssetsPaths { version_info } } } @@ -965,9 +990,11 @@ macro_rules! tool_doc { ( $tool: ident, $path: literal, - $(rustc_private_tool = $rustc_private_tool:literal, )? - $(is_library = $is_library:expr,)? - $(crates = $crates:expr)? + mode = $mode:expr + $(, is_library = $is_library:expr )? + $(, crates = $crates:expr )? + // Subset of nightly features that are allowed to be used when documenting + $(, allow_features: $allow_features:expr )? ) => { #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct $tool { @@ -988,20 +1015,29 @@ macro_rules! tool_doc { fn make_run(run: RunConfig<'_>) { let target = run.target; - let (build_compiler, mode) = if true $(&& $rustc_private_tool)? { - // Rustdoc needs the rustc sysroot available to build. - let compilers = RustcPrivateCompilers::new(run.builder, run.builder.top_stage, target); - - // Build rustc docs so that we generate relative links. - run.builder.ensure(Rustc::from_build_compiler(run.builder, compilers.build_compiler(), target)); - - (compilers.build_compiler(), Mode::ToolRustc) - } else { - // bootstrap/host tools have to be documented with the stage 0 compiler - (prepare_doc_compiler(run.builder, run.builder.host_target, 1), Mode::ToolBootstrap) + let build_compiler = match $mode { + Mode::ToolRustcPrivate => { + // Rustdoc needs the rustc sysroot available to build. + let compilers = RustcPrivateCompilers::new(run.builder, run.builder.top_stage, target); + + // Build rustc docs so that we generate relative links. + run.builder.ensure(Rustc::from_build_compiler(run.builder, compilers.build_compiler(), target)); + compilers.build_compiler() + } + Mode::ToolBootstrap => { + // bootstrap/host tools should be documented with the stage 0 compiler + prepare_doc_compiler(run.builder, run.builder.host_target, 1) + } + Mode::ToolTarget => { + // target tools should be documented with the in-tree compiler + prepare_doc_compiler(run.builder, run.builder.host_target, run.builder.top_stage) + } + _ => { + panic!("Unexpected tool mode for documenting: {:?}", $mode); + } }; - run.builder.ensure($tool { build_compiler, mode, target }); + run.builder.ensure($tool { build_compiler, mode: $mode, target }); } /// Generates documentation for a tool. @@ -1032,6 +1068,15 @@ macro_rules! tool_doc { source_type, &[], ); + let allow_features = { + let mut _value = ""; + $( _value = $allow_features; )? + _value + }; + + if !allow_features.is_empty() { + cargo.allow_features(allow_features); + } cargo.arg("-Zskip-rustdoc-fingerprint"); // Only include compiler crates, no dependencies of those, such as `libc`. @@ -1087,18 +1132,33 @@ macro_rules! tool_doc { tool_doc!( BuildHelper, "src/build_helper", - rustc_private_tool = false, + mode = Mode::ToolBootstrap, is_library = true, crates = ["build_helper"] ); -tool_doc!(Rustdoc, "src/tools/rustdoc", crates = ["rustdoc", "rustdoc-json-types"]); -tool_doc!(Rustfmt, "src/tools/rustfmt", crates = ["rustfmt-nightly", "rustfmt-config_proc_macro"]); -tool_doc!(Clippy, "src/tools/clippy", crates = ["clippy_config", "clippy_utils"]); -tool_doc!(Miri, "src/tools/miri", crates = ["miri"]); +tool_doc!( + Rustdoc, + "src/tools/rustdoc", + mode = Mode::ToolRustcPrivate, + crates = ["rustdoc", "rustdoc-json-types"] +); +tool_doc!( + Rustfmt, + "src/tools/rustfmt", + mode = Mode::ToolRustcPrivate, + crates = ["rustfmt-nightly", "rustfmt-config_proc_macro"] +); +tool_doc!( + Clippy, + "src/tools/clippy", + mode = Mode::ToolRustcPrivate, + crates = ["clippy_config", "clippy_utils"] +); +tool_doc!(Miri, "src/tools/miri", mode = Mode::ToolRustcPrivate, crates = ["miri"]); tool_doc!( Cargo, "src/tools/cargo", - rustc_private_tool = false, + mode = Mode::ToolTarget, crates = [ "cargo", "cargo-credential", @@ -1110,27 +1170,30 @@ tool_doc!( "crates-io", "mdman", "rustfix", - ] + ], + // Required because of the im-rc dependency of Cargo, which automatically opts into the + // "specialization" feature in its build script when it detects a nightly toolchain. + allow_features: "specialization" ); -tool_doc!(Tidy, "src/tools/tidy", rustc_private_tool = false, crates = ["tidy"]); +tool_doc!(Tidy, "src/tools/tidy", mode = Mode::ToolBootstrap, crates = ["tidy"]); tool_doc!( Bootstrap, "src/bootstrap", - rustc_private_tool = false, + mode = Mode::ToolBootstrap, is_library = true, crates = ["bootstrap"] ); tool_doc!( RunMakeSupport, "src/tools/run-make-support", - rustc_private_tool = false, + mode = Mode::ToolBootstrap, is_library = true, crates = ["run_make_support"] ); tool_doc!( Compiletest, "src/tools/compiletest", - rustc_private_tool = false, + mode = Mode::ToolBootstrap, is_library = true, crates = ["compiletest"] ); diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index d47c1495838..83ed7430c39 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -205,6 +205,7 @@ pub(crate) fn is_ci_llvm_available_for_target( // tier 1 ("aarch64-unknown-linux-gnu", false), ("aarch64-apple-darwin", false), + ("aarch64-pc-windows-msvc", false), ("i686-pc-windows-gnu", false), ("i686-pc-windows-msvc", false), ("i686-unknown-linux-gnu", false), @@ -213,7 +214,6 @@ pub(crate) fn is_ci_llvm_available_for_target( ("x86_64-pc-windows-gnu", true), ("x86_64-pc-windows-msvc", true), // tier 2 with host tools - ("aarch64-pc-windows-msvc", false), ("aarch64-unknown-linux-musl", false), ("arm-unknown-linux-gnueabi", false), ("arm-unknown-linux-gnueabihf", false), diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs index d9de6b7ef96..9f7248b80f7 100644 --- a/src/bootstrap/src/core/build_steps/run.rs +++ b/src/bootstrap/src/core/build_steps/run.rs @@ -161,7 +161,7 @@ impl Step for Miri { let mut miri = tool::prepare_tool_cargo( builder, compilers.build_compiler(), - Mode::ToolRustc, + Mode::ToolRustcPrivate, host, Kind::Run, "src/tools/miri", @@ -487,7 +487,7 @@ impl Step for Rustfmt { let mut rustfmt = tool::prepare_tool_cargo( builder, rustfmt_build.build_compiler, - Mode::ToolRustc, + Mode::ToolRustcPrivate, host, Kind::Run, "src/tools/rustfmt", diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 95be5360b0b..4f839bdf7b8 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -96,7 +96,7 @@ impl Step for CrateBootstrap { ); let crate_name = path.rsplit_once('/').unwrap().1; - run_cargo_test(cargo, &[], &[], crate_name, bootstrap_host, builder, Mode::ToolBootstrap); + run_cargo_test(cargo, &[], &[], crate_name, bootstrap_host, builder); } fn metadata(&self) -> Option<StepMetadata> { @@ -153,15 +153,7 @@ You can skip linkcheck with --skip src/tools/linkchecker" SourceType::InTree, &[], ); - run_cargo_test( - cargo, - &[], - &[], - "linkchecker self tests", - bootstrap_host, - builder, - Mode::ToolBootstrap, - ); + run_cargo_test(cargo, &[], &[], "linkchecker self tests", bootstrap_host, builder); if builder.doc_tests == DocTests::No { return; @@ -174,7 +166,7 @@ You can skip linkcheck with --skip src/tools/linkchecker" let linkchecker = builder.tool_cmd(Tool::Linkchecker); // Run the linkchecker. - let _guard = builder.msg(Kind::Test, "Linkcheck", None, compiler, bootstrap_host); + let _guard = builder.msg_test("Linkcheck", bootstrap_host, 1); let _time = helpers::timeit(builder); linkchecker.delay_failure().arg(builder.out.join(host).join("doc")).run(builder); } @@ -463,7 +455,7 @@ impl Step for RustAnalyzer { let mut cargo = tool::prepare_tool_cargo( builder, self.compilers.build_compiler(), - Mode::ToolRustc, + Mode::ToolRustcPrivate, host, Kind::Test, crate_path, @@ -482,7 +474,7 @@ impl Step for RustAnalyzer { cargo.env("SKIP_SLOW_TESTS", "1"); cargo.add_rustc_lib_path(builder); - run_cargo_test(cargo, &[], &[], "rust-analyzer", host, builder, Mode::ToolRustc); + run_cargo_test(cargo, &[], &[], "rust-analyzer", host, builder); } fn metadata(&self) -> Option<StepMetadata> { @@ -526,7 +518,7 @@ impl Step for Rustfmt { let mut cargo = tool::prepare_tool_cargo( builder, build_compiler, - Mode::ToolRustc, + Mode::ToolRustcPrivate, target, Kind::Test, "src/tools/rustfmt", @@ -540,7 +532,7 @@ impl Step for Rustfmt { cargo.add_rustc_lib_path(builder); - run_cargo_test(cargo, &[], &[], "rustfmt", target, builder, Mode::ToolRustc); + run_cargo_test(cargo, &[], &[], "rustfmt", target, builder); } fn metadata(&self) -> Option<StepMetadata> { @@ -579,7 +571,8 @@ impl Miri { cargo.env("MIRI_SYSROOT", &miri_sysroot); let mut cargo = BootstrapCommand::from(cargo); - let _guard = builder.msg(Kind::Build, "miri sysroot", Mode::ToolRustc, compiler, target); + let _guard = + builder.msg(Kind::Build, "miri sysroot", Mode::ToolRustcPrivate, compiler, target); cargo.run(builder); // # Determine where Miri put its sysroot. @@ -656,7 +649,7 @@ impl Step for Miri { let mut cargo = tool::prepare_tool_cargo( builder, miri.build_compiler, - Mode::ToolRustc, + Mode::ToolRustcPrivate, host, Kind::Test, "src/tools/miri", @@ -679,8 +672,7 @@ impl Step for Miri { cargo.env("MIRI_TEST_TARGET", target.rustc_target_arg()); { - let _guard = - builder.msg(Kind::Test, "miri", Mode::ToolRustc, miri.build_compiler, target); + let _guard = builder.msg_test("miri", target, target_compiler.stage); let _time = helpers::timeit(builder); cargo.run(builder); } @@ -696,13 +688,8 @@ impl Step for Miri { cargo.args(["tests/pass", "tests/panic"]); { - let _guard = builder.msg( - Kind::Test, - "miri (mir-opt-level 4)", - Mode::ToolRustc, - miri.build_compiler, - target, - ); + let _guard = + builder.msg_test("miri (mir-opt-level 4)", target, target_compiler.stage); let _time = helpers::timeit(builder); cargo.run(builder); } @@ -772,8 +759,7 @@ impl Step for CargoMiri { // Finally, run everything. let mut cargo = BootstrapCommand::from(cargo); { - let _guard = - builder.msg(Kind::Test, "cargo-miri", Mode::ToolRustc, (host, stage), target); + let _guard = builder.msg_test("cargo-miri", target, stage); let _time = helpers::timeit(builder); cargo.run(builder); } @@ -833,7 +819,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the cargo.env("TEST_RUSTC", builder.rustc(compiler)); cargo.allow_features(COMPILETEST_ALLOW_FEATURES); - run_cargo_test(cargo, &[], &[], "compiletest self test", host, builder, Mode::ToolStd); + run_cargo_test(cargo, &[], &[], "compiletest self test", host, builder); } } @@ -876,7 +862,7 @@ impl Step for Clippy { let mut cargo = tool::prepare_tool_cargo( builder, build_compiler, - Mode::ToolRustc, + Mode::ToolRustcPrivate, target, Kind::Test, "src/tools/clippy", @@ -887,7 +873,7 @@ impl Step for Clippy { cargo.env("RUSTC_TEST_SUITE", builder.rustc(build_compiler)); cargo.env("RUSTC_LIB_PATH", builder.rustc_libdir(build_compiler)); let host_libs = - builder.stage_out(build_compiler, Mode::ToolRustc).join(builder.cargo_dir()); + builder.stage_out(build_compiler, Mode::ToolRustcPrivate).join(builder.cargo_dir()); cargo.env("HOST_LIBS", host_libs); // Build the standard library that the tests can use. @@ -916,7 +902,7 @@ impl Step for Clippy { cargo.add_rustc_lib_path(builder); let cargo = prepare_cargo_test(cargo, &[], &[], target, builder); - let _guard = builder.msg(Kind::Test, "clippy", Mode::ToolRustc, build_compiler, target); + let _guard = builder.msg_test("clippy", target, target_compiler.stage); // Clippy reports errors if it blessed the outputs if cargo.allow_failure().run(builder) { @@ -1046,8 +1032,7 @@ impl Step for RustdocJSStd { self.target, DocumentationFormat::Html, )); - let _guard = - builder.msg(Kind::Test, "rustdoc-js-std", None, self.build_compiler, self.target); + let _guard = builder.msg_test("rustdoc-js-std", self.target, self.build_compiler.stage); command.run(builder); } @@ -1079,7 +1064,7 @@ impl Step for RustdocJSNotStd { fn run(self, builder: &Builder<'_>) { builder.ensure(Compiletest { - compiler: self.compiler, + test_compiler: self.compiler, target: self.target, mode: "rustdoc-js", suite: "rustdoc-js", @@ -1200,7 +1185,7 @@ impl Step for RustdocGUI { } let _time = helpers::timeit(builder); - let _guard = builder.msg_test("rustdoc-gui", (self.target, self.test_compiler.stage)); + let _guard = builder.msg_test("rustdoc-gui", self.target, self.test_compiler.stage); try_run_tests(builder, &mut cmd, true); } @@ -1359,15 +1344,7 @@ impl Step for CrateRunMakeSupport { &[], ); cargo.allow_features("test"); - run_cargo_test( - cargo, - &[], - &[], - "run-make-support self test", - host, - builder, - Mode::ToolBootstrap, - ); + run_cargo_test(cargo, &[], &[], "run-make-support self test", host, builder); } } @@ -1404,15 +1381,7 @@ impl Step for CrateBuildHelper { &[], ); cargo.allow_features("test"); - run_cargo_test( - cargo, - &[], - &[], - "build_helper self test", - host, - builder, - Mode::ToolBootstrap, - ); + run_cargo_test(cargo, &[], &[], "build_helper self test", host, builder); } } @@ -1437,8 +1406,8 @@ macro_rules! test { $( #[$attr] )* #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct $name { - pub compiler: Compiler, - pub target: TargetSelection, + test_compiler: Compiler, + target: TargetSelection, } impl Step for $name { @@ -1456,14 +1425,14 @@ macro_rules! test { } fn make_run(run: RunConfig<'_>) { - let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple()); + let test_compiler = run.builder.compiler(run.builder.top_stage, run.build_triple()); - run.builder.ensure($name { compiler, target: run.target }); + run.builder.ensure($name { test_compiler, target: run.target }); } fn run(self, builder: &Builder<'_>) { builder.ensure(Compiletest { - compiler: self.compiler, + test_compiler: self.test_compiler, target: self.target, mode: $mode, suite: $suite, @@ -1476,12 +1445,6 @@ macro_rules! test { }), }) } - - fn metadata(&self) -> Option<StepMetadata> { - Some( - StepMetadata::test(stringify!($name), self.target) - ) - } } }; } @@ -1559,6 +1522,12 @@ test!(Pretty { }); test!(RunMake { path: "tests/run-make", mode: "run-make", suite: "run-make", default: true }); +test!(RunMakeCargo { + path: "tests/run-make-cargo", + mode: "run-make", + suite: "run-make-cargo", + default: true +}); test!(AssemblyLlvm { path: "tests/assembly-llvm", @@ -1650,7 +1619,7 @@ impl Step for Coverage { // Like other compiletest suite test steps, delegate to an internal // compiletest task to actually run the tests. builder.ensure(Compiletest { - compiler, + test_compiler: compiler, target, mode, suite: Self::SUITE, @@ -1691,7 +1660,7 @@ impl Step for MirOpt { fn run(self, builder: &Builder<'_>) { let run = |target| { builder.ensure(Compiletest { - compiler: self.compiler, + test_compiler: self.compiler, target, mode: "mir-opt", suite: "mir-opt", @@ -1726,9 +1695,15 @@ impl Step for MirOpt { } } +/// Executes the `compiletest` tool to run a suite of tests. +/// +/// Compiles all tests with `test_compiler` for `target` with the specified +/// compiletest `mode` and `suite` arguments. For example `mode` can be +/// "mir-opt" and `suite` can be something like "debuginfo". #[derive(Debug, Clone, PartialEq, Eq, Hash)] struct Compiletest { - compiler: Compiler, + /// The compiler that we're testing. + test_compiler: Compiler, target: TargetSelection, mode: &'static str, suite: &'static str, @@ -1743,11 +1718,6 @@ impl Step for Compiletest { run.never() } - /// Executes the `compiletest` tool to run a suite of tests. - /// - /// Compiles all tests with `compiler` for `target` with the specified - /// compiletest `mode` and `suite` arguments. For example `mode` can be - /// "run-pass" or `suite` can be something like `debuginfo`. fn run(self, builder: &Builder<'_>) { if builder.doc_tests == DocTests::Only { return; @@ -1762,7 +1732,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the crate::exit!(1); } - let mut compiler = self.compiler; + let mut test_compiler = self.test_compiler; let target = self.target; let mode = self.mode; let suite = self.suite; @@ -1782,53 +1752,55 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the // NOTE: Only stage 1 is special cased because we need the rustc_private artifacts to match the // running compiler in stage 2 when plugins run. let query_compiler; - let (stage, stage_id) = if suite == "ui-fulldeps" && compiler.stage == 1 { + let (stage, stage_id) = if suite == "ui-fulldeps" && test_compiler.stage == 1 { // Even when using the stage 0 compiler, we also need to provide the stage 1 compiler // so that compiletest can query it for target information. - query_compiler = Some(compiler); + query_compiler = Some(test_compiler); // At stage 0 (stage - 1) we are using the stage0 compiler. Using `self.target` can lead // finding an incorrect compiler path on cross-targets, as the stage 0 is always equal to // `build.build` in the configuration. let build = builder.build.host_target; - compiler = builder.compiler(compiler.stage - 1, build); - let test_stage = compiler.stage + 1; + test_compiler = builder.compiler(test_compiler.stage - 1, build); + let test_stage = test_compiler.stage + 1; (test_stage, format!("stage{test_stage}-{build}")) } else { query_compiler = None; - let stage = compiler.stage; + let stage = test_compiler.stage; (stage, format!("stage{stage}-{target}")) }; if suite.ends_with("fulldeps") { - builder.ensure(compile::Rustc::new(compiler, target)); + builder.ensure(compile::Rustc::new(test_compiler, target)); } if suite == "debuginfo" { builder.ensure(dist::DebuggerScripts { - sysroot: builder.sysroot(compiler).to_path_buf(), + sysroot: builder.sysroot(test_compiler).to_path_buf(), target, }); } - if suite == "run-make" { + if mode == "run-make" { builder.tool_exe(Tool::RunMakeSupport); } // ensure that `libproc_macro` is available on the host. if suite == "mir-opt" { - builder.ensure(compile::Std::new(compiler, compiler.host).is_for_mir_opt_tests(true)); + builder.ensure( + compile::Std::new(test_compiler, test_compiler.host).is_for_mir_opt_tests(true), + ); } else { - builder.std(compiler, compiler.host); + builder.std(test_compiler, test_compiler.host); } let mut cmd = builder.tool_cmd(Tool::Compiletest); if suite == "mir-opt" { - builder.ensure(compile::Std::new(compiler, target).is_for_mir_opt_tests(true)); + builder.ensure(compile::Std::new(test_compiler, target).is_for_mir_opt_tests(true)); } else { - builder.std(compiler, target); + builder.std(test_compiler, target); } - builder.ensure(RemoteCopyLibs { build_compiler: compiler, target }); + builder.ensure(RemoteCopyLibs { build_compiler: test_compiler, target }); // compiletest currently has... a lot of arguments, so let's just pass all // of them! @@ -1836,9 +1808,9 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the cmd.arg("--stage").arg(stage.to_string()); cmd.arg("--stage-id").arg(stage_id); - cmd.arg("--compile-lib-path").arg(builder.rustc_libdir(compiler)); - cmd.arg("--run-lib-path").arg(builder.sysroot_target_libdir(compiler, target)); - cmd.arg("--rustc-path").arg(builder.rustc(compiler)); + cmd.arg("--compile-lib-path").arg(builder.rustc_libdir(test_compiler)); + cmd.arg("--run-lib-path").arg(builder.sysroot_target_libdir(test_compiler, target)); + cmd.arg("--rustc-path").arg(builder.rustc(test_compiler)); if let Some(query_compiler) = query_compiler { cmd.arg("--query-rustc-path").arg(builder.rustc(query_compiler)); } @@ -1850,20 +1822,41 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the let is_rustdoc = suite == "rustdoc-ui" || suite == "rustdoc-js"; + // There are (potentially) 2 `cargo`s to consider: + // + // - A "bootstrap" cargo, which is the same cargo used to build bootstrap itself, and is + // used to build the `run-make` test recipes and the `run-make-support` test library. All + // of these may not use unstable rustc/cargo features. + // - An in-tree cargo, which should be considered as under test. The `run-make-cargo` test + // suite is intended to support the use case of testing the "toolchain" (that is, at the + // minimum the interaction between in-tree cargo + rustc) together. + // + // For build time and iteration purposes, we partition `run-make` tests which needs an + // in-tree cargo (a smaller subset) versus `run-make` tests that do not into two test + // suites, `run-make` and `run-make-cargo`. That way, contributors who do not need to run + // the `run-make` tests that need in-tree cargo do not need to spend time building in-tree + // cargo. if mode == "run-make" { - let cargo_path = if builder.top_stage == 0 { - // If we're using `--stage 0`, we should provide the bootstrap cargo. - builder.initial_cargo.clone() - } else { - builder.ensure(tool::Cargo::from_build_compiler(compiler, compiler.host)).tool_path - }; - - cmd.arg("--cargo-path").arg(cargo_path); - // We need to pass the compiler that was used to compile run-make-support, // because we have to use the same compiler to compile rmake.rs recipes. - let stage0_rustc_path = builder.compiler(0, compiler.host); + let stage0_rustc_path = builder.compiler(0, test_compiler.host); cmd.arg("--stage0-rustc-path").arg(builder.rustc(stage0_rustc_path)); + + if suite == "run-make-cargo" { + let cargo_path = if test_compiler.stage == 0 { + // If we're using `--stage 0`, we should provide the bootstrap cargo. + builder.initial_cargo.clone() + } else { + builder + .ensure(tool::Cargo::from_build_compiler( + builder.compiler(test_compiler.stage - 1, test_compiler.host), + test_compiler.host, + )) + .tool_path + }; + + cmd.arg("--cargo-path").arg(cargo_path); + } } // Avoid depending on rustdoc when we don't need it. @@ -1874,12 +1867,12 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the || mode == "rustdoc-json" || suite == "coverage-run-rustdoc" { - cmd.arg("--rustdoc-path").arg(builder.rustdoc_for_compiler(compiler)); + cmd.arg("--rustdoc-path").arg(builder.rustdoc_for_compiler(test_compiler)); } if mode == "rustdoc-json" { // Use the stage0 compiler for jsondocck - let json_compiler = compiler.with_stage(0); + let json_compiler = builder.compiler(0, builder.host_target); cmd.arg("--jsondocck-path") .arg(builder.ensure(tool::JsonDocCk { compiler: json_compiler, target }).tool_path); cmd.arg("--jsondoclint-path").arg( @@ -1899,14 +1892,16 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the // directory immediately under the root build directory, and the test-suite-specific build // directory. cmd.arg("--build-root").arg(&builder.out); - cmd.arg("--build-test-suite-root").arg(testdir(builder, compiler.host).join(suite)); + cmd.arg("--build-test-suite-root").arg(testdir(builder, test_compiler.host).join(suite)); // When top stage is 0, that means that we're testing an externally provided compiler. // In that case we need to use its specific sysroot for tests to pass. + // Note: DO NOT check if test_compiler.stage is 0, because the test compiler can be stage 0 + // even if the top stage is 1 (when we run the ui-fulldeps suite). let sysroot = if builder.top_stage == 0 { builder.initial_sysroot.clone() } else { - builder.sysroot(compiler) + builder.sysroot(test_compiler) }; cmd.arg("--sysroot-base").arg(sysroot); @@ -1914,11 +1909,15 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the cmd.arg("--suite").arg(suite); cmd.arg("--mode").arg(mode); cmd.arg("--target").arg(target.rustc_target_arg()); - cmd.arg("--host").arg(&*compiler.host.triple); + cmd.arg("--host").arg(&*test_compiler.host.triple); cmd.arg("--llvm-filecheck").arg(builder.llvm_filecheck(builder.config.host_target)); if let Some(codegen_backend) = builder.config.cmd.test_codegen_backend() { - if !builder.config.enabled_codegen_backends(compiler.host).contains(codegen_backend) { + if !builder + .config + .enabled_codegen_backends(test_compiler.host) + .contains(codegen_backend) + { eprintln!( "\ ERROR: No configured backend named `{name}` @@ -1937,7 +1936,7 @@ HELP: You can add it into `bootstrap.toml` in `rust.codegen-backends = [{name:?} // Tells compiletest which codegen backend to use. // It is used to e.g. ignore tests that don't support that codegen backend. cmd.arg("--default-codegen-backend") - .arg(builder.config.default_codegen_backend(compiler.host).name()); + .arg(builder.config.default_codegen_backend(test_compiler.host).name()); } if builder.build.config.llvm_enzyme { @@ -2017,7 +2016,7 @@ HELP: You can add it into `bootstrap.toml` in `rust.codegen-backends = [{name:?} if let Some(linker) = builder.linker(target) { cmd.arg("--target-linker").arg(linker); } - if let Some(linker) = builder.linker(compiler.host) { + if let Some(linker) = builder.linker(test_compiler.host) { cmd.arg("--host-linker").arg(linker); } } @@ -2028,16 +2027,18 @@ HELP: You can add it into `bootstrap.toml` in `rust.codegen-backends = [{name:?} } let mut hostflags = flags.clone(); - hostflags.extend(linker_flags(builder, compiler.host, LldThreads::No)); + hostflags.extend(linker_flags(builder, test_compiler.host, LldThreads::No)); let mut targetflags = flags; // Provide `rust_test_helpers` for both host and target. if suite == "ui" || suite == "incremental" { - builder.ensure(TestHelpers { target: compiler.host }); + builder.ensure(TestHelpers { target: test_compiler.host }); builder.ensure(TestHelpers { target }); - hostflags - .push(format!("-Lnative={}", builder.test_helpers_out(compiler.host).display())); + hostflags.push(format!( + "-Lnative={}", + builder.test_helpers_out(test_compiler.host).display() + )); targetflags.push(format!("-Lnative={}", builder.test_helpers_out(target).display())); } @@ -2122,7 +2123,7 @@ HELP: You can add it into `bootstrap.toml` in `rust.codegen-backends = [{name:?} let mut llvm_components_passed = false; let mut copts_passed = false; - if builder.config.llvm_enabled(compiler.host) { + if builder.config.llvm_enabled(test_compiler.host) { let llvm::LlvmResult { host_llvm_config, .. } = builder.ensure(llvm::Llvm { target: builder.config.host_target }); if !builder.config.dry_run() { @@ -2322,19 +2323,16 @@ HELP: You can add it into `bootstrap.toml` in `rust.codegen-backends = [{name:?} mode: mode.into(), compare_mode: None, target: self.target.triple.to_string(), - host: self.compiler.host.triple.to_string(), - stage: self.compiler.stage, + host: self.test_compiler.host.triple.to_string(), + stage: self.test_compiler.stage, }, builder, ); - let _group = builder.msg( - Kind::Test, - format!("compiletest suite={suite} mode={mode}"), - // FIXME: compiletest sometimes behaves as ToolStd, we could expose that difference here - Mode::ToolBootstrap, - compiler, + let _group = builder.msg_test( + format!("with compiletest suite={suite} mode={mode}"), target, + test_compiler.stage, ); try_run_tests(builder, &mut cmd, false); @@ -2348,20 +2346,27 @@ HELP: You can add it into `bootstrap.toml` in `rust.codegen-backends = [{name:?} mode: mode.into(), compare_mode: Some(compare_mode.into()), target: self.target.triple.to_string(), - host: self.compiler.host.triple.to_string(), - stage: self.compiler.stage, + host: self.test_compiler.host.triple.to_string(), + stage: self.test_compiler.stage, }, builder, ); builder.info(&format!( "Check compiletest suite={} mode={} compare_mode={} ({} -> {})", - suite, mode, compare_mode, &compiler.host, target + suite, mode, compare_mode, &test_compiler.host, target )); let _time = helpers::timeit(builder); try_run_tests(builder, &mut cmd, false); } } + + fn metadata(&self) -> Option<StepMetadata> { + Some( + StepMetadata::test(&format!("compiletest-{}", self.suite), self.target) + .stage(self.test_compiler.stage), + ) + } } /// Runs the documentation tests for a book in `src/doc` using the `rustdoc` of `test_compiler`. @@ -2429,7 +2434,7 @@ impl BookTest { let libs = if !self.dependencies.is_empty() { let mut lib_paths = vec![]; for dep in self.dependencies { - let mode = Mode::ToolRustc; + let mode = Mode::ToolRustcPrivate; let target = builder.config.host_target; let cargo = tool::prepare_tool_cargo( builder, @@ -2471,12 +2476,10 @@ impl BookTest { } builder.add_rust_test_threads(&mut rustbook_cmd); - let _guard = builder.msg( - Kind::Test, + let _guard = builder.msg_test( format_args!("mdbook {}", self.path.display()), - None, - test_compiler, test_compiler.host, + test_compiler.stage, ); let _time = helpers::timeit(builder); let toolstate = if rustbook_cmd.delay_failure().run(builder) { @@ -2494,12 +2497,10 @@ impl BookTest { builder.std(test_compiler, host); - let _guard = builder.msg( - Kind::Test, + let _guard = builder.msg_test( format!("book {}", self.name), - None, - (test_compiler.host, test_compiler.stage - 1), - host, + test_compiler.host, + test_compiler.stage, ); // Do a breadth-first traversal of the `src/doc` directory and just run @@ -2642,13 +2643,7 @@ impl Step for ErrorIndex { let mut tool = tool::ErrorIndex::command(builder, self.compilers); tool.arg("markdown").arg(&output); - let guard = builder.msg( - Kind::Test, - "error-index", - None, - self.compilers.build_compiler(), - target_compiler.host, - ); + let guard = builder.msg_test("error-index", target_compiler.host, target_compiler.stage); let _time = helpers::timeit(builder); tool.run_capture(builder); drop(guard); @@ -2744,14 +2739,12 @@ fn run_cargo_test<'a>( description: impl Into<Option<&'a str>>, target: TargetSelection, builder: &Builder<'_>, - mode: impl Into<Option<Mode>>, ) -> bool { - let mode = mode.into(); let compiler = cargo.compiler(); let mut cargo = prepare_cargo_test(cargo, libtest_args, crates, target, builder); let _time = helpers::timeit(builder); let _group = - description.into().and_then(|what| builder.msg(Kind::Test, what, mode, compiler, target)); + description.into().and_then(|what| builder.msg_test(what, target, compiler.stage + 1)); #[cfg(feature = "build-metrics")] builder.metrics.begin_test_suite( @@ -2978,15 +2971,7 @@ impl Step for Crate { crates.push("alloctests".to_owned()); } - run_cargo_test( - cargo, - &[], - &crates, - &*crate_description(&self.crates), - target, - builder, - mode, - ); + run_cargo_test(cargo, &[], &crates, &*crate_description(&self.crates), target, builder); } } @@ -3034,7 +3019,7 @@ impl Step for CrateRustdoc { let mut cargo = tool::prepare_tool_cargo( builder, compiler, - Mode::ToolRustc, + Mode::ToolRustcPrivate, target, builder.kind, "src/tools/rustdoc", @@ -3080,15 +3065,7 @@ impl Step for CrateRustdoc { dylib_path.insert(0, PathBuf::from(&*libdir)); cargo.env(dylib_path_var(), env::join_paths(&dylib_path).unwrap()); - run_cargo_test( - cargo, - &[], - &["rustdoc:0.0.0".to_string()], - "rustdoc", - target, - builder, - Mode::ToolRustc, - ); + run_cargo_test(cargo, &[], &["rustdoc:0.0.0".to_string()], "rustdoc", target, builder); } } @@ -3147,7 +3124,6 @@ impl Step for CrateRustdocJsonTypes { "rustdoc-json-types", target, builder, - Mode::ToolTarget, ); } } @@ -3229,6 +3205,7 @@ impl Step for Distcheck { /// check steps from those sources. /// - Check that selected dist components (`rust-src` only at the moment) at least have expected /// directory shape and crate manifests that cargo can generate a lockfile from. + /// - Check that we can run `cargo metadata` on the workspace in the `rustc-dev` component /// /// FIXME(#136822): dist components are under-tested. fn run(self, builder: &Builder<'_>) { @@ -3236,64 +3213,91 @@ impl Step for Distcheck { // local source code, built artifacts or configuration by accident let root_dir = std::env::temp_dir().join("distcheck"); - // Check that we can build some basic things from the plain source tarball - builder.info("Distcheck plain source tarball"); - let plain_src_tarball = builder.ensure(dist::PlainSourceTarball); - let plain_src_dir = root_dir.join("distcheck-plain-src"); - builder.clear_dir(&plain_src_dir); - - let configure_args: Vec<String> = std::env::var("DISTCHECK_CONFIGURE_ARGS") - .map(|args| args.split(" ").map(|s| s.to_string()).collect::<Vec<String>>()) - .unwrap_or_default(); - - // FIXME: unpack the source tarballs into a directory outside the source checkout, to - // ensure that it cannot access any local state - // Also ensure that it doesn't use download-ci-llvm - command("tar") - .arg("-xf") - .arg(plain_src_tarball.tarball()) - .arg("--strip-components=1") - .current_dir(&plain_src_dir) - .run(builder); - command("./configure") - .arg("--set") - .arg("rust.omit-git-hash=false") - .args(&configure_args) - .arg("--enable-vendor") - .current_dir(&plain_src_dir) - .run(builder); - command(helpers::make(&builder.config.host_target.triple)) - .arg("check") - // Do not run the build as if we were in CI, otherwise git would be assumed to be - // present, but we build from a tarball here - .env("GITHUB_ACTIONS", "0") - .current_dir(&plain_src_dir) - .run(builder); - - // Now make sure that rust-src has all of libstd's dependencies - builder.info("Distcheck rust-src"); - let src_tarball = builder.ensure(dist::Src); - let src_dir = root_dir.join("distcheck-src"); - builder.clear_dir(&src_dir); - - command("tar") - .arg("-xf") - .arg(src_tarball.tarball()) - .arg("--strip-components=1") - .current_dir(&src_dir) - .run(builder); - - let toml = src_dir.join("rust-src/lib/rustlib/src/rust/library/std/Cargo.toml"); - command(&builder.initial_cargo) - // Will read the libstd Cargo.toml - // which uses the unstable `public-dependency` feature. - .env("RUSTC_BOOTSTRAP", "1") - .arg("generate-lockfile") - .arg("--manifest-path") - .arg(&toml) - .current_dir(&src_dir) - .run(builder); - } + distcheck_plain_source_tarball(builder, &root_dir.join("distcheck-rustc-src")); + distcheck_rust_src(builder, &root_dir.join("distcheck-rust-src")); + distcheck_rustc_dev(builder, &root_dir.join("distcheck-rustc-dev")); + } +} + +/// Check that we can build some basic things from the plain source tarball +fn distcheck_plain_source_tarball(builder: &Builder<'_>, plain_src_dir: &Path) { + builder.info("Distcheck plain source tarball"); + let plain_src_tarball = builder.ensure(dist::PlainSourceTarball); + builder.clear_dir(plain_src_dir); + + let configure_args: Vec<String> = std::env::var("DISTCHECK_CONFIGURE_ARGS") + .map(|args| args.split(" ").map(|s| s.to_string()).collect::<Vec<String>>()) + .unwrap_or_default(); + + command("tar") + .arg("-xf") + .arg(plain_src_tarball.tarball()) + .arg("--strip-components=1") + .current_dir(plain_src_dir) + .run(builder); + command("./configure") + .arg("--set") + .arg("rust.omit-git-hash=false") + .args(&configure_args) + .arg("--enable-vendor") + .current_dir(plain_src_dir) + .run(builder); + command(helpers::make(&builder.config.host_target.triple)) + .arg("check") + // Do not run the build as if we were in CI, otherwise git would be assumed to be + // present, but we build from a tarball here + .env("GITHUB_ACTIONS", "0") + .current_dir(plain_src_dir) + .run(builder); +} + +/// Check that rust-src has all of libstd's dependencies +fn distcheck_rust_src(builder: &Builder<'_>, src_dir: &Path) { + builder.info("Distcheck rust-src"); + let src_tarball = builder.ensure(dist::Src); + builder.clear_dir(src_dir); + + command("tar") + .arg("-xf") + .arg(src_tarball.tarball()) + .arg("--strip-components=1") + .current_dir(src_dir) + .run(builder); + + let toml = src_dir.join("rust-src/lib/rustlib/src/rust/library/std/Cargo.toml"); + command(&builder.initial_cargo) + // Will read the libstd Cargo.toml + // which uses the unstable `public-dependency` feature. + .env("RUSTC_BOOTSTRAP", "1") + .arg("generate-lockfile") + .arg("--manifest-path") + .arg(&toml) + .current_dir(src_dir) + .run(builder); +} + +/// Check that rustc-dev's compiler crate source code can be loaded with `cargo metadata` +fn distcheck_rustc_dev(builder: &Builder<'_>, dir: &Path) { + builder.info("Distcheck rustc-dev"); + let tarball = builder.ensure(dist::RustcDev::new(builder, builder.host_target)).unwrap(); + builder.clear_dir(dir); + + command("tar") + .arg("-xf") + .arg(tarball.tarball()) + .arg("--strip-components=1") + .current_dir(dir) + .run(builder); + + command(&builder.initial_cargo) + .arg("metadata") + .arg("--manifest-path") + .arg("rustc-dev/lib/rustlib/rustc-src/rust/compiler/rustc/Cargo.toml") + .env("RUSTC_BOOTSTRAP", "1") + // We might not have a globally available `rustc` binary on CI + .env("RUSTC", &builder.initial_rustc) + .current_dir(dir) + .run(builder); } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -3308,8 +3312,6 @@ impl Step for Bootstrap { fn run(self, builder: &Builder<'_>) { let host = builder.config.host_target; let build_compiler = builder.compiler(0, host); - let _guard = - builder.msg(Kind::Test, "bootstrap", Mode::ToolBootstrap, build_compiler, host); // Some tests require cargo submodule to be present. builder.build.require_submodule("src/tools/cargo", None); @@ -3346,9 +3348,7 @@ impl Step for Bootstrap { .env("INSTA_WORKSPACE_ROOT", &builder.src) .env("RUSTC_BOOTSTRAP", "1"); - // bootstrap tests are racy on directory creation so just run them one at a time. - // Since there's not many this shouldn't be a problem. - run_cargo_test(cargo, &["--test-threads=1"], &[], None, host, builder, Mode::ToolBootstrap); + run_cargo_test(cargo, &[], &[], None, host, builder); } fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -3408,7 +3408,11 @@ impl Step for TierCheck { cargo.arg("--verbose"); } - let _guard = builder.msg_test("platform support check", self.test_compiler); + let _guard = builder.msg_test( + "platform support check", + self.test_compiler.host, + self.test_compiler.stage, + ); BootstrapCommand::from(cargo).delay_failure().run(builder); } @@ -3487,9 +3491,8 @@ impl Step for RustInstaller { &[], ); - let _guard = - builder.msg(Kind::Test, "rust-installer", None, build_compiler, bootstrap_host); - run_cargo_test(cargo, &[], &[], None, bootstrap_host, builder, Mode::ToolBootstrap); + let _guard = builder.msg_test("rust-installer", bootstrap_host, 1); + run_cargo_test(cargo, &[], &[], None, bootstrap_host, builder); // We currently don't support running the test.sh script outside linux(?) environments. // Eventually this should likely migrate to #[test]s in rust-installer proper rather than a @@ -3660,7 +3663,11 @@ impl Step for CodegenCranelift { // Avoid incremental cache issues when changing rustc cargo.env("CARGO_BUILD_INCREMENTAL", "false"); - let _guard = builder.msg_test("rustc_codegen_cranelift", target_compiler); + let _guard = builder.msg_test( + "rustc_codegen_cranelift", + target_compiler.host, + target_compiler.stage, + ); // FIXME handle vendoring for source tarballs before removing the --skip-test below let download_dir = builder.out.join("cg_clif_download"); @@ -3755,7 +3762,11 @@ impl Step for CodegenGCC { .extra_rust_args(&["-Csymbol-mangling-version=v0", "-Cpanic=abort"]), ); - let _guard = builder.msg_test("rustc_codegen_gcc", compilers.build_compiler()); + let _guard = builder.msg_test( + "rustc_codegen_gcc", + compilers.target(), + compilers.target_compiler().stage, + ); let mut cargo = builder::Cargo::new( builder, @@ -3860,7 +3871,7 @@ impl Step for TestFloatParse { ); cargo_test.allow_features(TEST_FLOAT_PARSE_ALLOW_FEATURES); - run_cargo_test(cargo_test, &[], &[], "test-float-parse", target, builder, Mode::ToolStd); + run_cargo_test(cargo_test, &[], &[], "test-float-parse", target, builder); // Run the actual parse tests. let mut cargo_run = tool::prepare_tool_cargo( diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 65c4c499086..6870bf3eddc 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -82,7 +82,7 @@ impl Step for ToolBuild { let path = self.path; match self.mode { - Mode::ToolRustc => { + Mode::ToolRustcPrivate => { // FIXME: remove this, it's only needed for download-rustc... if !self.build_compiler.is_forced_compiler() && builder.download_rustc() { builder.std(self.build_compiler, self.build_compiler.host); @@ -121,9 +121,11 @@ impl Step for ToolBuild { cargo.env("RUSTC_WRAPPER", ccache); } - // Rustc tools (miri, clippy, cargo, rustfmt, rust-analyzer) + // RustcPrivate tools (miri, clippy, rustfmt, rust-analyzer) and cargo // could use the additional optimizations. - if self.mode == Mode::ToolRustc && is_lto_stage(&self.build_compiler) { + if is_lto_stage(&self.build_compiler) + && (self.mode == Mode::ToolRustcPrivate || self.path == "src/tools/cargo") + { let lto = match builder.config.rust_lto { RustcLto::Off => Some("off"), RustcLto::Thin => Some("thin"), @@ -607,7 +609,7 @@ impl Step for ErrorIndex { build_compiler: self.compilers.build_compiler, target: self.compilers.target(), tool: "error_index_generator", - mode: Mode::ToolRustc, + mode: Mode::ToolRustcPrivate, path: "src/tools/error_index_generator", source_type: SourceType::InTree, extra_features: Vec::new(), @@ -671,7 +673,7 @@ impl Step for RemoteTestServer { /// Represents `Rustdoc` that either comes from the external stage0 sysroot or that is built /// locally. /// Rustdoc is special, because it both essentially corresponds to a `Compiler` (that can be -/// externally provided), but also to a `ToolRustc` tool. +/// externally provided), but also to a `ToolRustcPrivate` tool. #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Rustdoc { /// If the stage of `target_compiler` is `0`, then rustdoc is externally provided. @@ -759,7 +761,7 @@ impl Step for Rustdoc { // the wrong rustdoc being executed. To avoid the conflicting rustdocs, we name the "tool" // rustdoc a different name. tool: "rustdoc_tool_binary", - mode: Mode::ToolRustc, + mode: Mode::ToolRustcPrivate, path: "src/tools/rustdoc", source_type: SourceType::InTree, extra_features, @@ -1048,7 +1050,7 @@ impl Step for RustAnalyzer { build_compiler, target, tool: "rust-analyzer", - mode: Mode::ToolRustc, + mode: Mode::ToolRustcPrivate, path: "src/tools/rust-analyzer", extra_features: vec!["in-rust-tree".to_owned()], source_type: SourceType::InTree, @@ -1105,7 +1107,7 @@ impl Step for RustAnalyzerProcMacroSrv { build_compiler: self.compilers.build_compiler, target: self.compilers.target(), tool: "rust-analyzer-proc-macro-srv", - mode: Mode::ToolRustc, + mode: Mode::ToolRustcPrivate, path: "src/tools/rust-analyzer/crates/proc-macro-srv-cli", extra_features: vec!["in-rust-tree".to_owned()], source_type: SourceType::InTree, @@ -1352,7 +1354,7 @@ impl RustcPrivateCompilers { } } -/// Creates a step that builds an extended `Mode::ToolRustc` tool +/// Creates a step that builds an extended `Mode::ToolRustcPrivate` tool /// and installs it into the sysroot of a corresponding compiler. macro_rules! tool_rustc_extended { ( @@ -1466,7 +1468,7 @@ fn build_extended_rustc_tool( build_compiler, target, tool: tool_name, - mode: Mode::ToolRustc, + mode: Mode::ToolRustcPrivate, path, extra_features, source_type: SourceType::InTree, diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index cdf6fe573e5..924bb4adb42 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -533,7 +533,7 @@ impl Builder<'_> { if cmd_kind == Kind::Doc { let my_out = match mode { // This is the intended out directory for compiler documentation. - Mode::Rustc | Mode::ToolRustc | Mode::ToolBootstrap => { + Mode::Rustc | Mode::ToolRustcPrivate | Mode::ToolBootstrap | Mode::ToolTarget => { self.compiler_doc_out(target) } Mode::Std => { @@ -583,7 +583,7 @@ impl Builder<'_> { // We synthetically interpret a stage0 compiler used to build tools as a // "raw" compiler in that it's the exact snapshot we download. For things like - // ToolRustc, we would have to use the artificial stage0-sysroot compiler instead. + // ToolRustcPrivate, we would have to use the artificial stage0-sysroot compiler instead. let use_snapshot = mode == Mode::ToolBootstrap || (mode == Mode::ToolTarget && build_compiler_stage == 0); assert!(!use_snapshot || build_compiler_stage == 0 || self.local_rebuild); @@ -643,7 +643,8 @@ impl Builder<'_> { // sysroot. Passing this cfg enables raw-dylib support instead, which makes the native // library unnecessary. This can be removed when windows-rs enables raw-dylib // unconditionally. - if let Mode::Rustc | Mode::ToolRustc | Mode::ToolBootstrap | Mode::ToolTarget = mode { + if let Mode::Rustc | Mode::ToolRustcPrivate | Mode::ToolBootstrap | Mode::ToolTarget = mode + { rustflags.arg("--cfg=windows_raw_dylib"); } @@ -657,7 +658,7 @@ impl Builder<'_> { // - rust-analyzer, due to the rowan crate // so we exclude an entire category of steps here due to lack of fine-grained control over // rustflags. - if self.config.rust_randomize_layout && mode != Mode::ToolRustc { + if self.config.rust_randomize_layout && mode != Mode::ToolRustcPrivate { rustflags.arg("-Zrandomize-layout"); } @@ -717,7 +718,7 @@ impl Builder<'_> { match mode { Mode::Std | Mode::ToolBootstrap | Mode::ToolStd | Mode::ToolTarget => {} - Mode::Rustc | Mode::Codegen | Mode::ToolRustc => { + Mode::Rustc | Mode::Codegen | Mode::ToolRustcPrivate => { // Build proc macros both for the host and the target unless proc-macros are not // supported by the target. if target != compiler.host && cmd_kind != Kind::Check { @@ -778,7 +779,7 @@ impl Builder<'_> { "binary-dep-depinfo,proc_macro_span,proc_macro_span_shrink,proc_macro_diagnostic" .to_string() } - Mode::Std | Mode::Rustc | Mode::Codegen | Mode::ToolRustc => String::new(), + Mode::Std | Mode::Rustc | Mode::Codegen | Mode::ToolRustcPrivate => String::new(), }; cargo.arg("-j").arg(self.jobs().to_string()); @@ -825,7 +826,7 @@ impl Builder<'_> { // rustc step and one that we just built. This isn't always a // problem, somehow -- not really clear why -- but we know that this // fixes things. - Mode::ToolRustc => metadata.push_str("tool-rustc"), + Mode::ToolRustcPrivate => metadata.push_str("tool-rustc"), // Same for codegen backends. Mode::Codegen => metadata.push_str("codegen"), _ => {} @@ -877,8 +878,11 @@ impl Builder<'_> { .env("RUSTC_LIBDIR", libdir) .env("RUSTDOC", self.bootstrap_out.join("rustdoc")) .env("RUSTDOC_REAL", rustdoc_path) - .env("RUSTC_ERROR_METADATA_DST", self.extended_error_dir()) - .env("RUSTC_BREAK_ON_ICE", "1"); + .env("RUSTC_ERROR_METADATA_DST", self.extended_error_dir()); + + if self.config.rust_break_on_ice { + cargo.env("RUSTC_BREAK_ON_ICE", "1"); + } // Set RUSTC_WRAPPER to the bootstrap shim, which switches between beta and in-tree // sysroot depending on whether we're building build scripts. @@ -917,7 +921,7 @@ impl Builder<'_> { let debuginfo_level = match mode { Mode::Rustc | Mode::Codegen => self.config.rust_debuginfo_level_rustc, Mode::Std => self.config.rust_debuginfo_level_std, - Mode::ToolBootstrap | Mode::ToolStd | Mode::ToolRustc | Mode::ToolTarget => { + Mode::ToolBootstrap | Mode::ToolStd | Mode::ToolRustcPrivate | Mode::ToolTarget => { self.config.rust_debuginfo_level_tools } }; @@ -930,7 +934,7 @@ impl Builder<'_> { match mode { Mode::Std => self.config.std_debug_assertions, Mode::Rustc | Mode::Codegen => self.config.rustc_debug_assertions, - Mode::ToolBootstrap | Mode::ToolStd | Mode::ToolRustc | Mode::ToolTarget => { + Mode::ToolBootstrap | Mode::ToolStd | Mode::ToolRustcPrivate | Mode::ToolTarget => { self.config.tools_debug_assertions } } @@ -1005,7 +1009,7 @@ impl Builder<'_> { } Mode::Std | Mode::ToolBootstrap - | Mode::ToolRustc + | Mode::ToolRustcPrivate | Mode::ToolStd | Mode::ToolTarget => { if let Some(ref map_to) = @@ -1078,7 +1082,7 @@ impl Builder<'_> { // requirement, but the `-L` library path is not propagated across // separate Cargo projects. We can add LLVM's library path to the // rustc args as a workaround. - if (mode == Mode::ToolRustc || mode == Mode::Codegen) + if (mode == Mode::ToolRustcPrivate || mode == Mode::Codegen) && let Some(llvm_config) = self.llvm_config(target) { let llvm_libdir = diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 627085df812..75c8ee36528 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -444,6 +444,7 @@ const PATH_REMAP: &[(&str, &[&str])] = &[ "tests/mir-opt", "tests/pretty", "tests/run-make", + "tests/run-make-cargo", "tests/rustdoc", "tests/rustdoc-gui", "tests/rustdoc-js", @@ -1061,6 +1062,7 @@ impl<'a> Builder<'a> { check::FeaturesStatusDump, check::CoverageDump, check::Linkchecker, + check::BumpStage0, // This has special staging logic, it may run on stage 1 while others run on stage 0. // It takes quite some time to build stage 1, so put this at the end. // @@ -1127,8 +1129,8 @@ impl<'a> Builder<'a> { test::RustInstaller, test::TestFloatParse, test::CollectLicenseMetadata, - // Run run-make last, since these won't pass without make on Windows test::RunMake, + test::RunMakeCargo, ), Kind::Miri => describe!(test::Crate), Kind::Bench => describe!(test::Crate, test::CrateLibrustc), diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 43e67756e74..89a0ab7711e 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -10,8 +10,8 @@ use crate::core::build_steps::doc::DocumentationFormat; use crate::core::config::Config; use crate::utils::cache::ExecutedStep; use crate::utils::helpers::get_host_target; -use crate::utils::tests::ConfigBuilder; use crate::utils::tests::git::{GitCtx, git_test}; +use crate::utils::tests::{ConfigBuilder, TestCtx}; static TEST_TRIPLE_1: &str = "i686-unknown-haiku"; static TEST_TRIPLE_2: &str = "i686-unknown-hurd-gnu"; @@ -22,38 +22,13 @@ fn configure(cmd: &str, host: &[&str], target: &[&str]) -> Config { } fn configure_with_args(cmd: &[&str], host: &[&str], target: &[&str]) -> Config { - let cmd = cmd.iter().copied().map(String::from).collect::<Vec<_>>(); - let mut config = Config::parse(Flags::parse(&cmd)); - // don't save toolstates - config.save_toolstates = None; - config.set_dry_run(DryRun::SelfCheck); - - // Ignore most submodules, since we don't need them for a dry run, and the - // tests run much faster without them. - // - // The src/doc/book submodule is needed because TheBook step tries to - // access files even during a dry-run (may want to consider just skipping - // that in a dry run). - let submodule_build = Build::new(Config { - // don't include LLVM, so CI doesn't require ninja/cmake to be installed - rust_codegen_backends: vec![], - ..Config::parse(Flags::parse(&["check".to_owned()])) - }); - submodule_build.require_submodule("src/doc/book", None); - config.submodules = Some(false); - - config.ninja_in_file = false; - // try to avoid spurious failures in dist where we create/delete each others file - // HACK: rather than pull in `tempdir`, use the one that cargo has conveniently created for us - let dir = Path::new(env!("OUT_DIR")) - .join("tmp-rustbuild-tests") - .join(&thread::current().name().unwrap_or("unknown").replace(":", "-")); - t!(fs::create_dir_all(&dir)); - config.out = dir; - config.host_target = TargetSelection::from_user(TEST_TRIPLE_1); - config.hosts = host.iter().map(|s| TargetSelection::from_user(s)).collect(); - config.targets = target.iter().map(|s| TargetSelection::from_user(s)).collect(); - config + TestCtx::new() + .config(cmd[0]) + .args(&cmd[1..]) + .hosts(host) + .targets(target) + .args(&["--build", TEST_TRIPLE_1]) + .create_config() } fn first<A, B>(v: Vec<(A, B)>) -> Vec<A> { @@ -547,8 +522,8 @@ mod snapshot { use crate::core::build_steps::{compile, dist, doc, test, tool}; use crate::core::builder::tests::{ - RenderConfig, TEST_TRIPLE_1, TEST_TRIPLE_2, TEST_TRIPLE_3, configure, configure_with_args, - first, host_target, render_steps, run_build, + RenderConfig, TEST_TRIPLE_1, TEST_TRIPLE_2, TEST_TRIPLE_3, configure, first, host_target, + render_steps, run_build, }; use crate::core::builder::{Builder, Kind, StepDescription, StepMetadata}; use crate::core::config::TargetSelection; @@ -1156,6 +1131,59 @@ mod snapshot { } #[test] + fn dist_compiler_docs() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("dist") + .path("rustc-docs") + .args(&["--set", "build.compiler-docs=true"]) + .render_steps(), @r" + [build] rustc 0 <host> -> UnstableBookGen 1 <host> + [build] rustc 0 <host> -> Rustbook 1 <host> + [doc] unstable-book (book) <host> + [build] llvm <host> + [build] rustc 0 <host> -> rustc 1 <host> + [build] rustc 1 <host> -> std 1 <host> + [doc] book (book) <host> + [doc] book/first-edition (book) <host> + [doc] book/second-edition (book) <host> + [doc] book/2018-edition (book) <host> + [build] rustdoc 1 <host> + [doc] rustc 1 <host> -> standalone 2 <host> + [doc] rustc 1 <host> -> std 1 <host> crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] + [doc] rustc 1 <host> -> rustc 2 <host> + [build] rustc 1 <host> -> rustc 2 <host> + [doc] rustc 1 <host> -> Rustdoc 2 <host> + [doc] rustc 1 <host> -> Rustfmt 2 <host> + [build] rustc 1 <host> -> error-index 2 <host> + [doc] rustc 1 <host> -> error-index 2 <host> + [doc] nomicon (book) <host> + [doc] rustc 1 <host> -> reference (book) 2 <host> + [doc] rustdoc (book) <host> + [doc] rust-by-example (book) <host> + [build] rustc 0 <host> -> LintDocs 1 <host> + [doc] rustc (book) <host> + [doc] rustc 1 <host> -> Cargo 2 <host> + [doc] cargo (book) <host> + [doc] rustc 1 <host> -> Clippy 2 <host> + [doc] clippy (book) <host> + [doc] rustc 1 <host> -> Miri 2 <host> + [doc] embedded-book (book) <host> + [doc] edition-guide (book) <host> + [doc] style-guide (book) <host> + [build] rustdoc 0 <host> + [doc] rustc 0 <host> -> Tidy 1 <host> + [doc] rustc 0 <host> -> Bootstrap 1 <host> + [doc] rustc 1 <host> -> releases 2 <host> + [doc] rustc 0 <host> -> RunMakeSupport 1 <host> + [doc] rustc 0 <host> -> BuildHelper 1 <host> + [doc] rustc 0 <host> -> Compiletest 1 <host> + [build] rustc 0 <host> -> RustInstaller 1 <host> + " + ); + } + + #[test] fn dist_extended() { let ctx = TestCtx::new(); insta::assert_snapshot!( @@ -2062,20 +2090,23 @@ mod snapshot { [build] rustc 0 <host> -> rustc 1 <host> [build] rustc 1 <host> -> std 1 <host> [build] rustc 0 <host> -> Compiletest 1 <host> - [test] Ui <host> - [test] Crashes <host> + [test] compiletest-ui 1 <host> + [test] compiletest-crashes 1 <host> [build] rustc 0 <host> -> CoverageDump 1 <host> + [test] compiletest-coverage 1 <host> + [test] compiletest-coverage 1 <host> [build] rustc 1 <host> -> std 1 <host> - [test] CodegenLlvm <host> - [test] CodegenUnits <host> - [test] AssemblyLlvm <host> - [test] Incremental <host> - [test] Debuginfo <host> - [test] UiFullDeps <host> + [test] compiletest-mir-opt 1 <host> + [test] compiletest-codegen-llvm 1 <host> + [test] compiletest-codegen-units 1 <host> + [test] compiletest-assembly-llvm 1 <host> + [test] compiletest-incremental 1 <host> + [test] compiletest-debuginfo 1 <host> + [test] compiletest-ui-fulldeps 1 <host> [build] rustdoc 1 <host> - [test] Rustdoc <host> - [test] CoverageRunRustdoc <host> - [test] Pretty <host> + [test] compiletest-rustdoc 1 <host> + [test] compiletest-coverage-run-rustdoc 1 <host> + [test] compiletest-pretty 1 <host> [build] rustc 1 <host> -> std 1 <host> [build] rustc 0 <host> -> std 0 <host> [test] rustc 0 <host> -> CrateLibrustc 1 <host> @@ -2113,16 +2144,105 @@ mod snapshot { [test] rustc 0 <host> -> rust-analyzer 1 <host> [build] rustc 0 <host> -> RustdocTheme 1 <host> [test] rustdoc-theme 1 <host> - [test] RustdocUi <host> + [test] compiletest-rustdoc-ui 1 <host> [build] rustc 0 <host> -> JsonDocCk 1 <host> [build] rustc 0 <host> -> JsonDocLint 1 <host> - [test] RustdocJson <host> + [test] compiletest-rustdoc-json 1 <host> [doc] rustc 0 <host> -> rustc 1 <host> [build] rustc 0 <host> -> HtmlChecker 1 <host> [test] html-check <host> [build] rustc 0 <host> -> RunMakeSupport 1 <host> - [build] rustc 1 <host> -> cargo 2 <host> - [test] RunMake <host> + [test] compiletest-run-make 1 <host> + [build] rustc 0 <host> -> cargo 1 <host> + [test] compiletest-run-make-cargo 1 <host> + "); + } + + #[test] + fn test_compiletest_suites_stage1() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("test") + .args(&["ui", "ui-fulldeps", "run-make", "rustdoc", "rustdoc-gui", "incremental"]) + .render_steps(), @r" + [build] llvm <host> + [build] rustc 0 <host> -> rustc 1 <host> + [build] rustc 1 <host> -> std 1 <host> + [build] rustc 0 <host> -> Compiletest 1 <host> + [test] compiletest-ui 1 <host> + [test] compiletest-ui-fulldeps 1 <host> + [build] rustc 0 <host> -> RunMakeSupport 1 <host> + [build] rustdoc 1 <host> + [test] compiletest-run-make 1 <host> + [test] compiletest-rustdoc 1 <host> + [build] rustc 0 <host> -> RustdocGUITest 1 <host> + [test] rustdoc-gui 1 <host> + [test] compiletest-incremental 1 <host> + [build] rustc 1 <host> -> rustc 2 <host> + "); + } + + #[test] + fn test_compiletest_suites_stage2() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("test") + .args(&["ui", "ui-fulldeps", "run-make", "rustdoc", "rustdoc-gui", "incremental"]) + .stage(2) + .render_steps(), @r" + [build] llvm <host> + [build] rustc 0 <host> -> rustc 1 <host> + [build] rustc 1 <host> -> std 1 <host> + [build] rustc 1 <host> -> rustc 2 <host> + [build] rustc 2 <host> -> std 2 <host> + [build] rustc 0 <host> -> Compiletest 1 <host> + [test] compiletest-ui 2 <host> + [build] rustc 2 <host> -> rustc 3 <host> + [test] compiletest-ui-fulldeps 2 <host> + [build] rustc 0 <host> -> RunMakeSupport 1 <host> + [build] rustdoc 2 <host> + [test] compiletest-run-make 2 <host> + [test] compiletest-rustdoc 2 <host> + [build] rustc 0 <host> -> RustdocGUITest 1 <host> + [test] rustdoc-gui 2 <host> + [test] compiletest-incremental 2 <host> + [build] rustdoc 1 <host> + "); + } + + #[test] + fn test_compiletest_suites_stage2_cross() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("test") + .hosts(&[TEST_TRIPLE_1]) + .targets(&[TEST_TRIPLE_1]) + .args(&["ui", "ui-fulldeps", "run-make", "rustdoc", "rustdoc-gui", "incremental"]) + .stage(2) + .render_steps(), @r" + [build] llvm <host> + [build] rustc 0 <host> -> rustc 1 <host> + [build] rustc 1 <host> -> std 1 <host> + [build] rustc 1 <host> -> rustc 2 <host> + [build] rustc 2 <host> -> std 2 <host> + [build] rustc 0 <host> -> Compiletest 1 <host> + [build] rustc 1 <host> -> std 1 <target1> + [build] rustc 2 <host> -> std 2 <target1> + [test] compiletest-ui 2 <target1> + [build] llvm <target1> + [build] rustc 2 <host> -> rustc 3 <target1> + [test] compiletest-ui-fulldeps 2 <target1> + [build] rustc 0 <host> -> RunMakeSupport 1 <host> + [build] rustdoc 2 <host> + [test] compiletest-run-make 2 <target1> + [test] compiletest-rustdoc 2 <target1> + [build] rustc 0 <host> -> RustdocGUITest 1 <host> + [test] rustdoc-gui 2 <target1> + [test] compiletest-incremental 2 <target1> + [build] rustc 1 <host> -> rustc 2 <target1> + [build] rustdoc 1 <host> + [build] rustc 2 <target1> -> std 2 <target1> + [build] rustdoc 2 <target1> "); } @@ -2142,21 +2262,24 @@ mod snapshot { [build] rustc 1 <host> -> rustc 2 <host> [build] rustc 2 <host> -> std 2 <host> [build] rustc 0 <host> -> Compiletest 1 <host> - [test] Ui <host> - [test] Crashes <host> + [test] compiletest-ui 2 <host> + [test] compiletest-crashes 2 <host> [build] rustc 0 <host> -> CoverageDump 1 <host> + [test] compiletest-coverage 2 <host> + [test] compiletest-coverage 2 <host> [build] rustc 2 <host> -> std 2 <host> - [test] CodegenLlvm <host> - [test] CodegenUnits <host> - [test] AssemblyLlvm <host> - [test] Incremental <host> - [test] Debuginfo <host> + [test] compiletest-mir-opt 2 <host> + [test] compiletest-codegen-llvm 2 <host> + [test] compiletest-codegen-units 2 <host> + [test] compiletest-assembly-llvm 2 <host> + [test] compiletest-incremental 2 <host> + [test] compiletest-debuginfo 2 <host> [build] rustc 2 <host> -> rustc 3 <host> - [test] UiFullDeps <host> + [test] compiletest-ui-fulldeps 2 <host> [build] rustdoc 2 <host> - [test] Rustdoc <host> - [test] CoverageRunRustdoc <host> - [test] Pretty <host> + [test] compiletest-rustdoc 2 <host> + [test] compiletest-coverage-run-rustdoc 2 <host> + [test] compiletest-pretty 2 <host> [build] rustc 2 <host> -> std 2 <host> [build] rustc 1 <host> -> std 1 <host> [build] rustdoc 1 <host> @@ -2196,16 +2319,17 @@ mod snapshot { [test] rustc 1 <host> -> lint-docs 2 <host> [build] rustc 0 <host> -> RustdocTheme 1 <host> [test] rustdoc-theme 2 <host> - [test] RustdocUi <host> + [test] compiletest-rustdoc-ui 2 <host> [build] rustc 0 <host> -> JsonDocCk 1 <host> [build] rustc 0 <host> -> JsonDocLint 1 <host> - [test] RustdocJson <host> + [test] compiletest-rustdoc-json 2 <host> [doc] rustc 1 <host> -> rustc 2 <host> [build] rustc 0 <host> -> HtmlChecker 1 <host> [test] html-check <host> [build] rustc 0 <host> -> RunMakeSupport 1 <host> - [build] rustc 2 <host> -> cargo 3 <host> - [test] RunMake <host> + [test] compiletest-run-make 2 <host> + [build] rustc 1 <host> -> cargo 2 <host> + [test] compiletest-run-make-cargo 2 <host> "); } @@ -2249,7 +2373,7 @@ mod snapshot { let steps = ctx.config("test").args(&["--skip", "src/tools/tidy"]).get_steps(); let host = TargetSelection::from_user(&host_target()); - steps.assert_contains(StepMetadata::test("RustdocUi", host)); + steps.assert_contains(StepMetadata::test("compiletest-rustdoc-ui", host).stage(1)); steps.assert_not_contains(test::Tidy); } @@ -2339,6 +2463,43 @@ mod snapshot { "); } + // Differential snapshots for `./x test run-make` run `./x test run-make-cargo`: only + // `run-make-cargo` should build an in-tree cargo, running `./x test run-make` should not. + #[test] + fn test_run_make_no_cargo() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("test") + .path("run-make") + .render_steps(), @r" + [build] llvm <host> + [build] rustc 0 <host> -> rustc 1 <host> + [build] rustc 0 <host> -> RunMakeSupport 1 <host> + [build] rustc 1 <host> -> std 1 <host> + [build] rustc 0 <host> -> Compiletest 1 <host> + [build] rustdoc 1 <host> + [test] compiletest-run-make 1 <host> + "); + } + + #[test] + fn test_run_make_cargo_builds_cargo() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("test") + .path("run-make-cargo") + .render_steps(), @r" + [build] llvm <host> + [build] rustc 0 <host> -> rustc 1 <host> + [build] rustc 0 <host> -> RunMakeSupport 1 <host> + [build] rustc 1 <host> -> std 1 <host> + [build] rustc 0 <host> -> Compiletest 1 <host> + [build] rustc 0 <host> -> cargo 1 <host> + [build] rustdoc 1 <host> + [test] compiletest-run-make-cargo 1 <host> + "); + } + #[test] fn doc_all() { let ctx = TestCtx::new(); @@ -2391,6 +2552,33 @@ mod snapshot { } #[test] + fn doc_cargo_stage_1() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("doc") + .path("cargo") + .render_steps(), @r" + [build] rustdoc 0 <host> + [doc] rustc 0 <host> -> Cargo 1 <host> + "); + } + #[test] + fn doc_cargo_stage_2() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("doc") + .path("cargo") + .stage(2) + .render_steps(), @r" + [build] llvm <host> + [build] rustc 0 <host> -> rustc 1 <host> + [build] rustc 1 <host> -> std 1 <host> + [build] rustdoc 1 <host> + [doc] rustc 1 <host> -> Cargo 2 <host> + "); + } + + #[test] fn doc_core() { let ctx = TestCtx::new(); insta::assert_snapshot!( diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index d12cc962187..678a9b63952 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -221,6 +221,7 @@ pub struct Config { pub rust_lto: RustcLto, pub rust_validate_mir_opts: Option<u32>, pub rust_std_features: BTreeSet<String>, + pub rust_break_on_ice: bool, pub llvm_profile_use: Option<String>, pub llvm_profile_generate: bool, pub llvm_libunwind_default: Option<LlvmLibunwind>, @@ -550,6 +551,7 @@ impl Config { strip: rust_strip, lld_mode: rust_lld_mode, std_features: rust_std_features, + break_on_ice: rust_break_on_ice, } = toml.rust.unwrap_or_default(); let Llvm { @@ -1269,6 +1271,7 @@ impl Config { reproducible_artifacts: flags_reproducible_artifact, reuse: build_reuse.map(PathBuf::from), rust_analyzer_info, + rust_break_on_ice: rust_break_on_ice.unwrap_or(true), rust_codegen_backends: rust_codegen_backends .map(|backends| parse_codegen_backends(backends, "rust")) .unwrap_or(vec![CodegenBackendKind::Llvm]), diff --git a/src/bootstrap/src/core/config/toml/rust.rs b/src/bootstrap/src/core/config/toml/rust.rs index c54df456d52..4832a1d37b7 100644 --- a/src/bootstrap/src/core/config/toml/rust.rs +++ b/src/bootstrap/src/core/config/toml/rust.rs @@ -65,6 +65,7 @@ define_config! { lto: Option<String> = "lto", validate_mir_opts: Option<u32> = "validate-mir-opts", std_features: Option<BTreeSet<String>> = "std-features", + break_on_ice: Option<bool> = "break-on-ice", } } @@ -355,6 +356,7 @@ pub fn check_incompatible_options_for_ci_rustc( download_rustc: _, validate_mir_opts: _, frame_pointers: _, + break_on_ice: _, } = ci_rust_config; // There are two kinds of checks for CI rustc incompatible options: diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index 099ec488397..04cf63f1c6d 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -397,6 +397,16 @@ $ pacman -R cmake && pacman -S mingw-w64-x86_64-cmake ); } } + + // For testing `wasm32-wasip2`-and-beyond it's required to have + // `wasm-component-ld`. This is enabled by default via `tool_enabled` + // but if it's disabled then double-check it's present on the system. + if target.contains("wasip") + && !target.contains("wasip1") + && !build.tool_enabled("wasm-component-ld") + { + cmd_finder.must_have("wasm-component-ld"); + } } if let Some(ref s) = build.config.ccache { diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 9a882eae08e..a2aeed20948 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -85,12 +85,12 @@ const LLD_FILE_NAMES: &[&str] = &["ld.lld", "ld64.lld", "lld-link", "wasm-ld"]; const EXTRA_CHECK_CFGS: &[(Option<Mode>, &str, Option<&[&'static str]>)] = &[ (Some(Mode::Rustc), "bootstrap", None), (Some(Mode::Codegen), "bootstrap", None), - (Some(Mode::ToolRustc), "bootstrap", None), + (Some(Mode::ToolRustcPrivate), "bootstrap", None), (Some(Mode::ToolStd), "bootstrap", None), (Some(Mode::Rustc), "llvm_enzyme", None), (Some(Mode::Codegen), "llvm_enzyme", None), - (Some(Mode::ToolRustc), "llvm_enzyme", None), - (Some(Mode::ToolRustc), "rust_analyzer", None), + (Some(Mode::ToolRustcPrivate), "llvm_enzyme", None), + (Some(Mode::ToolRustcPrivate), "rust_analyzer", None), (Some(Mode::ToolStd), "rust_analyzer", None), // Any library specific cfgs like `target_os`, `target_arch` should be put in // priority the `[lints.rust.unexpected_cfgs.check-cfg]` table @@ -334,17 +334,18 @@ pub enum Mode { /// compiletest which needs libtest. ToolStd, - /// Build a tool which uses the locally built rustc and the target std, + /// Build a tool which uses the `rustc_private` mechanism, and thus + /// the locally built rustc rlib artifacts, /// placing the output in the "stageN-tools" directory. This is used for - /// anything that needs a fully functional rustc, such as rustdoc, clippy, - /// cargo, rustfmt, miri, etc. - ToolRustc, + /// everything that links to rustc as a library, such as rustdoc, clippy, + /// rustfmt, miri, etc. + ToolRustcPrivate, } impl Mode { pub fn is_tool(&self) -> bool { match self { - Mode::ToolBootstrap | Mode::ToolRustc | Mode::ToolStd | Mode::ToolTarget => true, + Mode::ToolBootstrap | Mode::ToolRustcPrivate | Mode::ToolStd | Mode::ToolTarget => true, Mode::Std | Mode::Codegen | Mode::Rustc => false, } } @@ -353,7 +354,7 @@ impl Mode { match self { Mode::Std | Mode::Codegen => true, Mode::ToolBootstrap - | Mode::ToolRustc + | Mode::ToolRustcPrivate | Mode::ToolStd | Mode::ToolTarget | Mode::Rustc => false, @@ -426,22 +427,22 @@ forward! { download_rustc() -> bool, } -/// A mostly temporary helper struct before we can migrate everything in bootstrap to use -/// the concept of a build compiler. -struct HostAndStage { - host: TargetSelection, +/// An alternative way of specifying what target and stage is involved in some bootstrap activity. +/// Ideally using a `Compiler` directly should be preferred. +struct TargetAndStage { + target: TargetSelection, stage: u32, } -impl From<(TargetSelection, u32)> for HostAndStage { - fn from((host, stage): (TargetSelection, u32)) -> Self { - Self { host, stage } +impl From<(TargetSelection, u32)> for TargetAndStage { + fn from((target, stage): (TargetSelection, u32)) -> Self { + Self { target, stage } } } -impl From<Compiler> for HostAndStage { +impl From<Compiler> for TargetAndStage { fn from(compiler: Compiler) -> Self { - Self { host: compiler.host, stage: compiler.stage } + Self { target: compiler.host, stage: compiler.stage } } } @@ -924,7 +925,7 @@ impl Build { Mode::Rustc => (Some(build_compiler.stage + 1), "rustc"), Mode::Codegen => (Some(build_compiler.stage + 1), "codegen"), Mode::ToolBootstrap => bootstrap_tool(), - Mode::ToolStd | Mode::ToolRustc => (Some(build_compiler.stage + 1), "tools"), + Mode::ToolStd | Mode::ToolRustcPrivate => (Some(build_compiler.stage + 1), "tools"), Mode::ToolTarget => { // If we're not cross-compiling (the common case), share the target directory with // bootstrap tools to reuse the build cache. @@ -1109,11 +1110,12 @@ impl Build { /// Return a `Group` guard for a [`Step`] that: /// - Performs `action` + /// - If the action is `Kind::Test`, use [`Build::msg_test`] instead. /// - On `what` /// - Where `what` possibly corresponds to a `mode` - /// - `action` is performed using the given build compiler (`host_and_stage`). - /// - Since some steps do not use the concept of a build compiler yet, it is also possible - /// to pass the host and stage explicitly. + /// - `action` is performed with/on the given compiler (`target_and_stage`). + /// - Since for some steps it is not possible to pass a single compiler here, it is also + /// possible to pass the host and stage explicitly. /// - With a given `target`. /// /// [`Step`]: crate::core::builder::Step @@ -1124,13 +1126,19 @@ impl Build { action: impl Into<Kind>, what: impl Display, mode: impl Into<Option<Mode>>, - host_and_stage: impl Into<HostAndStage>, + target_and_stage: impl Into<TargetAndStage>, target: impl Into<Option<TargetSelection>>, ) -> Option<gha::Group> { - let host_and_stage = host_and_stage.into(); + let target_and_stage = target_and_stage.into(); + let action = action.into(); + assert!( + action != Kind::Test, + "Please use `Build::msg_test` instead of `Build::msg(Kind::Test)`" + ); + let actual_stage = match mode.into() { // Std has the same stage as the compiler that builds it - Some(Mode::Std) => host_and_stage.stage, + Some(Mode::Std) => target_and_stage.stage, // Other things have stage corresponding to their build compiler + 1 Some( Mode::Rustc @@ -1138,20 +1146,20 @@ impl Build { | Mode::ToolBootstrap | Mode::ToolTarget | Mode::ToolStd - | Mode::ToolRustc, + | Mode::ToolRustcPrivate, ) - | None => host_and_stage.stage + 1, + | None => target_and_stage.stage + 1, }; - let action = action.into().description(); + let action = action.description(); let what = what.to_string(); let msg = |fmt| { let space = if !what.is_empty() { " " } else { "" }; format!("{action} stage{actual_stage} {what}{space}{fmt}") }; let msg = if let Some(target) = target.into() { - let build_stage = host_and_stage.stage; - let host = host_and_stage.host; + let build_stage = target_and_stage.stage; + let host = target_and_stage.target; if host == target { msg(format_args!("(stage{build_stage} -> stage{actual_stage}, {target})")) } else { @@ -1163,10 +1171,9 @@ impl Build { self.group(&msg) } - /// Return a `Group` guard for a [`Step`] that tests `what` with the given `stage` and `target` - /// (determined by `host_and_stage`). - /// Use this instead of [`Build::msg`] when there is no clear `build_compiler` to be - /// determined. + /// Return a `Group` guard for a [`Step`] that tests `what` with the given `stage` and `target`. + /// Use this instead of [`Build::msg`] for test steps, because for them it is not always clear + /// what exactly is a build compiler. /// /// [`Step`]: crate::core::builder::Step #[must_use = "Groups should not be dropped until the Step finishes running"] @@ -1174,11 +1181,11 @@ impl Build { fn msg_test( &self, what: impl Display, - host_and_stage: impl Into<HostAndStage>, + target: TargetSelection, + stage: u32, ) -> Option<gha::Group> { - let HostAndStage { host, stage } = host_and_stage.into(); let action = Kind::Test.description(); - let msg = format!("{action} stage{stage} {what} ({host})"); + let msg = format!("{action} stage{stage} {what} ({target})"); self.group(&msg) } @@ -2109,11 +2116,6 @@ impl Compiler { self.forced_compiler = forced_compiler; } - pub fn with_stage(mut self, stage: u32) -> Compiler { - self.stage = stage; - self - } - /// Returns `true` if this is a snapshot compiler for `build`'s configuration pub fn is_snapshot(&self, build: &Build) -> bool { self.stage == 0 && self.host == build.host_target diff --git a/src/bootstrap/src/utils/cc_detect/tests.rs b/src/bootstrap/src/utils/cc_detect/tests.rs index bed03c18aaa..a6233e6b61c 100644 --- a/src/bootstrap/src/utils/cc_detect/tests.rs +++ b/src/bootstrap/src/utils/cc_detect/tests.rs @@ -3,7 +3,8 @@ use std::{env, iter}; use super::*; use crate::core::config::{Target, TargetSelection}; -use crate::{Build, Config, Flags}; +use crate::utils::tests::TestCtx; +use crate::{Build, Config, Flags, t}; #[test] fn test_ndk_compiler_c() { @@ -68,7 +69,8 @@ fn test_language_clang() { #[test] fn test_new_cc_build() { - let build = Build::new(Config { ..Config::parse(Flags::parse(&["build".to_owned()])) }); + let config = TestCtx::new().config("build").create_config(); + let build = Build::new(config); let target = TargetSelection::from_user("x86_64-unknown-linux-gnu"); let cfg = new_cc_build(&build, target.clone()); let compiler = cfg.get_compiler(); @@ -77,7 +79,8 @@ fn test_new_cc_build() { #[test] fn test_default_compiler_wasi() { - let mut build = Build::new(Config { ..Config::parse(Flags::parse(&["build".to_owned()])) }); + let config = TestCtx::new().config("build").create_config(); + let mut build = Build::new(config); let target = TargetSelection::from_user("wasm32-wasi"); let wasi_sdk = PathBuf::from("/wasi-sdk"); build.wasi_sdk_path = Some(wasi_sdk.clone()); @@ -98,7 +101,8 @@ fn test_default_compiler_wasi() { #[test] fn test_default_compiler_fallback() { - let build = Build::new(Config { ..Config::parse(Flags::parse(&["build".to_owned()])) }); + let config = TestCtx::new().config("build").create_config(); + let build = Build::new(config); let target = TargetSelection::from_user("x86_64-unknown-linux-gnu"); let mut cfg = cc::Build::new(); let result = default_compiler(&mut cfg, Language::C, target, &build); @@ -107,7 +111,8 @@ fn test_default_compiler_fallback() { #[test] fn test_find_target_with_config() { - let mut build = Build::new(Config { ..Config::parse(Flags::parse(&["build".to_owned()])) }); + let config = TestCtx::new().config("build").create_config(); + let mut build = Build::new(config); let target = TargetSelection::from_user("x86_64-unknown-linux-gnu"); let mut target_config = Target::default(); target_config.cc = Some(PathBuf::from("dummy-cc")); @@ -128,7 +133,8 @@ fn test_find_target_with_config() { #[test] fn test_find_target_without_config() { - let mut build = Build::new(Config { ..Config::parse(Flags::parse(&["build".to_owned()])) }); + let config = TestCtx::new().config("build").create_config(); + let mut build = Build::new(config); let target = TargetSelection::from_user("x86_64-unknown-linux-gnu"); build.config.target_config.clear(); fill_target_compiler(&mut build, target.clone()); @@ -141,7 +147,8 @@ fn test_find_target_without_config() { #[test] fn test_find() { - let mut build = Build::new(Config { ..Config::parse(Flags::parse(&["build".to_owned()])) }); + let config = TestCtx::new().config("build").create_config(); + let mut build = Build::new(config); let target1 = TargetSelection::from_user("x86_64-unknown-linux-gnu"); let target2 = TargetSelection::from_user("x86_64-unknown-openbsd"); build.targets.push(target1.clone()); diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 2cc2fb486fa..01309072927 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -536,4 +536,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Warning, summary: "It is no longer possible to `x test` with stage 0, except for running compiletest and opting into `build.compiletest-allow-stage0`.", }, + ChangeInfo { + change_id: 145976, + severity: ChangeSeverity::Info, + summary: "Added a new option `rust.break-on-ice` to control if internal compiler errors cause a debug break on Windows.", + }, ]; diff --git a/src/ci/docker/host-aarch64/aarch64-gnu-debug/Dockerfile b/src/ci/docker/host-aarch64/aarch64-gnu-debug/Dockerfile index cf030f6830e..71de8f917fa 100644 --- a/src/ci/docker/host-aarch64/aarch64-gnu-debug/Dockerfile +++ b/src/ci/docker/host-aarch64/aarch64-gnu-debug/Dockerfile @@ -54,4 +54,4 @@ ENV RUST_CONFIGURE_ARGS \ ENV SCRIPT \ python3 ../x.py --stage 2 build && \ - python3 ../x.py --stage 2 test tests/run-make + python3 ../x.py --stage 2 test tests/run-make tests/run-make-cargo diff --git a/src/ci/docker/host-x86_64/dist-various-1/Dockerfile b/src/ci/docker/host-x86_64/dist-various-1/Dockerfile index 4d5980027ca..e59012ff6af 100644 --- a/src/ci/docker/host-x86_64/dist-various-1/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-various-1/Dockerfile @@ -158,7 +158,7 @@ ENV RUST_CONFIGURE_ARGS \ --disable-docs ENV SCRIPT \ - python3 ../x.py --stage 2 test --host='' --target $RUN_MAKE_TARGETS tests/run-make && \ + python3 ../x.py --stage 2 test --host='' --target $RUN_MAKE_TARGETS tests/run-make tests/run-make-cargo && \ python3 ../x.py dist --host='' --target $TARGETS # sccache diff --git a/src/ci/docker/host-x86_64/pr-check-2/Dockerfile b/src/ci/docker/host-x86_64/pr-check-2/Dockerfile index 8073b8efb46..d6470e4deb8 100644 --- a/src/ci/docker/host-x86_64/pr-check-2/Dockerfile +++ b/src/ci/docker/host-x86_64/pr-check-2/Dockerfile @@ -28,6 +28,7 @@ RUN sh /scripts/sccache.sh ENV SCRIPT \ python3 ../x.py check && \ + python3 ../x.py check src/tools/bump-stage0 && \ python3 ../x.py clippy ci --stage 2 && \ python3 ../x.py test --stage 1 core alloc std test proc_macro && \ python3 ../x.py test --stage 1 src/tools/compiletest && \ diff --git a/src/ci/docker/host-x86_64/test-various/Dockerfile b/src/ci/docker/host-x86_64/test-various/Dockerfile index 6ff529c9e71..e1c882d5b08 100644 --- a/src/ci/docker/host-x86_64/test-various/Dockerfile +++ b/src/ci/docker/host-x86_64/test-various/Dockerfile @@ -60,9 +60,10 @@ RUN curl -L https://github.com/bytecodealliance/wasmtime/releases/download/v19.0 tar -xJ ENV PATH "$PATH:/wasmtime-v19.0.0-x86_64-linux" -ENV WASM_WASIP_TARGET=wasm32-wasip1 +ENV WASM_WASIP_TARGET=wasm32-wasip1 ENV WASM_WASIP_SCRIPT python3 /checkout/x.py --stage 2 test --host='' --target $WASM_WASIP_TARGET \ tests/run-make \ + tests/run-make-cargo \ tests/ui \ tests/mir-opt \ tests/codegen-units \ @@ -73,6 +74,7 @@ ENV WASM_WASIP_SCRIPT python3 /checkout/x.py --stage 2 test --host='' --target $ ENV NVPTX_TARGETS=nvptx64-nvidia-cuda ENV NVPTX_SCRIPT python3 /checkout/x.py --stage 2 test --host='' --target $NVPTX_TARGETS \ tests/run-make \ + tests/run-make-cargo \ tests/assembly-llvm ENV MUSL_TARGETS=x86_64-unknown-linux-musl \ @@ -88,8 +90,8 @@ ENV UEFI_TARGETS=aarch64-unknown-uefi,i686-unknown-uefi,x86_64-unknown-uefi \ CC_x86_64_unknown_uefi=clang-11 \ CXX_x86_64_unknown_uefi=clang++-11 ENV UEFI_SCRIPT python3 /checkout/x.py --stage 2 build --host='' --target $UEFI_TARGETS && \ - python3 /checkout/x.py --stage 2 test tests/run-make/uefi-qemu/rmake.rs --target aarch64-unknown-uefi && \ - python3 /checkout/x.py --stage 2 test tests/run-make/uefi-qemu/rmake.rs --target i686-unknown-uefi && \ - python3 /checkout/x.py --stage 2 test tests/run-make/uefi-qemu/rmake.rs --target x86_64-unknown-uefi + python3 /checkout/x.py --stage 2 test tests/run-make-cargo/uefi-qemu/rmake.rs --target aarch64-unknown-uefi && \ + python3 /checkout/x.py --stage 2 test tests/run-make-cargo/uefi-qemu/rmake.rs --target i686-unknown-uefi && \ + python3 /checkout/x.py --stage 2 test tests/run-make-cargo/uefi-qemu/rmake.rs --target x86_64-unknown-uefi ENV SCRIPT $WASM_WASIP_SCRIPT && $NVPTX_SCRIPT && $MUSL_SCRIPT && $UEFI_SCRIPT diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile index 5052d86f0ac..7e6a59aaf89 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile @@ -56,4 +56,4 @@ ENV RUST_CONFIGURE_ARGS \ ENV SCRIPT \ python3 ../x.py --stage 2 build && \ python3 ../x.py --stage 2 test tests/ui && \ - python3 ../x.py --stage 2 test tests/run-make + python3 ../x.py --stage 2 test tests/run-make tests/run-make-cargo diff --git a/src/ci/docker/scripts/rfl-build.sh b/src/ci/docker/scripts/rfl-build.sh index 70ff5d0d2c7..7e0fb5794f4 100755 --- a/src/ci/docker/scripts/rfl-build.sh +++ b/src/ci/docker/scripts/rfl-build.sh @@ -3,7 +3,8 @@ set -euo pipefail # https://github.com/rust-lang/rust/pull/144443 -LINUX_VERSION=7770d51bce622b13195b2d3c85407282fc9c27e5 +# https://github.com/rust-lang/rust/pull/145928 +LINUX_VERSION=8851e27d2cb947ea8bbbe8e812068f7bf5cbd00b # Build rustc, rustdoc, cargo, clippy-driver and rustfmt ../x.py build --stage 2 library rustdoc clippy rustfmt diff --git a/src/doc/favicon.inc b/src/doc/favicon.inc index 9c330685209..f09498cc095 100644 --- a/src/doc/favicon.inc +++ b/src/doc/favicon.inc @@ -1 +1,2 @@ -<link rel="icon" href="https://www.rust-lang.org/favicon.ico"> +<link rel="alternate icon" type="image/png" href="favicon-32x32.png"> +<link rel="icon" type="image/svg+xml" href="favicon.svg"> diff --git a/src/doc/redirect.inc b/src/doc/redirect.inc index 2fb44be0145..1b7d3744b1f 100644 --- a/src/doc/redirect.inc +++ b/src/doc/redirect.inc @@ -1,2 +1,3 @@ <meta name="robots" content="noindex,follow"> -<link rel="icon" href="https://www.rust-lang.org/favicon.ico"> +<link rel="alternate icon" type="image/png" href="../favicon-32x32.png"> +<link rel="icon" type="image/svg+xml" href="../favicon.svg"> diff --git a/src/doc/rustc-dev-guide/src/building/bootstrapping/writing-tools-in-bootstrap.md b/src/doc/rustc-dev-guide/src/building/bootstrapping/writing-tools-in-bootstrap.md index 41d0cf8d9fb..c3660e24b15 100644 --- a/src/doc/rustc-dev-guide/src/building/bootstrapping/writing-tools-in-bootstrap.md +++ b/src/doc/rustc-dev-guide/src/building/bootstrapping/writing-tools-in-bootstrap.md @@ -11,11 +11,8 @@ There are three types of tools you can write in bootstrap: Use this for tools that rely on the locally built std. The output goes into the "stageN-tools" directory. This mode is rarely used, mainly for `compiletest` which requires `libtest`. -- **`Mode::ToolRustc`** - Use this for tools that depend on both the locally built `rustc` and the target `std`. This is more complex than - the other modes because the tool must be built with the same compiler used for `rustc` and placed in the "stageN-tools" - directory. When you choose `Mode::ToolRustc`, `ToolBuild` implementation takes care of this automatically. - If you need to use the builder’s compiler for something specific, you can get it from `ToolBuildResult`, which is +- **`Mode::ToolRustcPrivate`** + Use this for tools that use the `rustc_private` mechanism, and thus depend on the locally built `rustc` and its rlib artifacts. This is more complex than the other modes because the tool must be built with the same compiler used for `rustc` and placed in the "stageN-tools" directory. When you choose `Mode::ToolRustcPrivate`, `ToolBuild` implementation takes care of this automatically. If you need to use the builder’s compiler for something specific, you can get it from `ToolBuildResult`, which is returned by the tool's [`Step`]. Regardless of the tool type you must return `ToolBuildResult` from the tool’s [`Step`] implementation and use `ToolBuild` inside it. diff --git a/src/doc/rustc-dev-guide/src/tests/adding.md b/src/doc/rustc-dev-guide/src/tests/adding.md index e5c26bef11d..46b8a1e4cf4 100644 --- a/src/doc/rustc-dev-guide/src/tests/adding.md +++ b/src/doc/rustc-dev-guide/src/tests/adding.md @@ -29,6 +29,8 @@ guidelines: suites. - Need to inspect the resulting binary in some way? Or if all the other test suites are too limited for your purposes? Then use `run-make`. + - Use `run-make-cargo` if you need to exercise in-tree `cargo` in conjunction + with in-tree `rustc`. - Check out the [compiletest] chapter for more specialized test suites. After deciding on which kind of test to add, see [best diff --git a/src/doc/rustc-dev-guide/src/tests/best-practices.md b/src/doc/rustc-dev-guide/src/tests/best-practices.md index be00207e3fb..efc626035b7 100644 --- a/src/doc/rustc-dev-guide/src/tests/best-practices.md +++ b/src/doc/rustc-dev-guide/src/tests/best-practices.md @@ -83,10 +83,10 @@ related tests. - E.g. for an implementation of RFC 2093 specifically, we can group a collection of tests under `tests/ui/rfc-2093-infer-outlives/`. For the directory name, include what the RFC is about. -- For the [`run-make`] test suite, each `rmake.rs` must be contained within an - immediate subdirectory under `tests/run-make/`. Further nesting is not - presently supported. Avoid including issue number in the directory name too, - include that info in a comment inside `rmake.rs`. +- For the [`run-make`]/`run-make-support` test suites, each `rmake.rs` must + be contained within an immediate subdirectory under `tests/run-make/` or + `tests/run-make-cargo/` respectively. Further nesting is not presently + supported. Avoid using _only_ an issue number for the test name as well. ## Test descriptions diff --git a/src/doc/rustc-dev-guide/src/tests/compiletest.md b/src/doc/rustc-dev-guide/src/tests/compiletest.md index 4980ed845d6..a4a729935fa 100644 --- a/src/doc/rustc-dev-guide/src/tests/compiletest.md +++ b/src/doc/rustc-dev-guide/src/tests/compiletest.md @@ -397,13 +397,19 @@ your test, causing separate files to be generated for 32bit and 64bit systems. ### `run-make` tests -The tests in [`tests/run-make`] are general-purpose tests using Rust *recipes*, -which are small programs (`rmake.rs`) allowing arbitrary Rust code such as -`rustc` invocations, and is supported by a [`run_make_support`] library. Using -Rust recipes provide the ultimate in flexibility. +The tests in [`tests/run-make`] and [`tests/run-make-cargo`] are general-purpose +tests using Rust *recipes*, which are small programs (`rmake.rs`) allowing +arbitrary Rust code such as `rustc` invocations, and is supported by a +[`run_make_support`] library. Using Rust recipes provide the ultimate in +flexibility. `run-make` tests should be used if no other test suites better suit your needs. +The `run-make-cargo` test suite additionally builds an in-tree `cargo` to support +use cases that require testing in-tree `cargo` in conjunction with in-tree `rustc`. +The `run-make` test suite does not have access to in-tree `cargo` (so it can be the +faster-to-iterate test suite). + #### Using Rust recipes Each test should be in a separate directory with a `rmake.rs` Rust program, @@ -476,6 +482,7 @@ Then add a corresponding entry to `"rust-analyzer.linkedProjects"` ``` [`tests/run-make`]: https://github.com/rust-lang/rust/tree/master/tests/run-make +[`tests/run-make-cargo`]: https://github.com/rust-lang/rust/tree/master/tests/run-make-cargo [`run_make_support`]: https://github.com/rust-lang/rust/tree/master/src/tools/run-make-support ### Coverage tests diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md index fbbeb7e97d3..4ff6b7cb10f 100644 --- a/src/doc/rustc-dev-guide/src/tests/directives.md +++ b/src/doc/rustc-dev-guide/src/tests/directives.md @@ -52,14 +52,14 @@ not be exhaustive. Directives can generally be found by browsing the See [Building auxiliary crates](compiletest.html#building-auxiliary-crates) -| Directive | Explanation | Supported test suites | Possible values | -|-----------------------|-------------------------------------------------------------------------------------------------------|-----------------------|-----------------------------------------------| -| `aux-bin` | Build a aux binary, made available in `auxiliary/bin` relative to test directory | All except `run-make` | Path to auxiliary `.rs` file | -| `aux-build` | Build a separate crate from the named source file | All except `run-make` | Path to auxiliary `.rs` file | -| `aux-crate` | Like `aux-build` but makes available as extern prelude | All except `run-make` | `<extern_prelude_name>=<path/to/aux/file.rs>` | -| `aux-codegen-backend` | Similar to `aux-build` but pass the compiled dylib to `-Zcodegen-backend` when building the main file | `ui-fulldeps` | Path to codegen backend file | -| `proc-macro` | Similar to `aux-build`, but for aux forces host and don't use `-Cprefer-dynamic`[^pm]. | All except `run-make` | Path to auxiliary proc-macro `.rs` file | -| `build-aux-docs` | Build docs for auxiliaries as well. Note that this only works with `aux-build`, not `aux-crate`. | All except `run-make` | N/A | +| Directive | Explanation | Supported test suites | Possible values | +|-----------------------|-------------------------------------------------------------------------------------------------------|----------------------------------------|-----------------------------------------------| +| `aux-bin` | Build a aux binary, made available in `auxiliary/bin` relative to test directory | All except `run-make`/`run-make-cargo` | Path to auxiliary `.rs` file | +| `aux-build` | Build a separate crate from the named source file | All except `run-make`/`run-make-cargo` | Path to auxiliary `.rs` file | +| `aux-crate` | Like `aux-build` but makes available as extern prelude | All except `run-make`/`run-make-cargo` | `<extern_prelude_name>=<path/to/aux/file.rs>` | +| `aux-codegen-backend` | Similar to `aux-build` but pass the compiled dylib to `-Zcodegen-backend` when building the main file | `ui-fulldeps` | Path to codegen backend file | +| `proc-macro` | Similar to `aux-build`, but for aux forces host and don't use `-Cprefer-dynamic`[^pm]. | All except `run-make`/`run-make-cargo` | Path to auxiliary proc-macro `.rs` file | +| `build-aux-docs` | Build docs for auxiliaries as well. Note that this only works with `aux-build`, not `aux-crate`. | All except `run-make`/`run-make-cargo` | N/A | [^pm]: please see the [Auxiliary proc-macro section](compiletest.html#auxiliary-proc-macro) in the compiletest chapter for specifics. @@ -243,18 +243,18 @@ ignoring debuggers. ### Affecting how tests are built -| Directive | Explanation | Supported test suites | Possible values | -|---------------------|----------------------------------------------------------------------------------------------|---------------------------|--------------------------------------------------------------------------------------------| -| `compile-flags` | Flags passed to `rustc` when building the test or aux file | All except for `run-make` | Any valid `rustc` flags, e.g. `-Awarnings -Dfoo`. Cannot be `-Cincremental` or `--edition` | -| `edition` | The edition used to build the test | All except for `run-make` | Any valid `--edition` value | -| `rustc-env` | Env var to set when running `rustc` | All except for `run-make` | `<KEY>=<VALUE>` | -| `unset-rustc-env` | Env var to unset when running `rustc` | All except for `run-make` | Any env var name | -| `incremental` | Proper incremental support for tests outside of incremental test suite | `ui`, `crashes` | N/A | -| `no-prefer-dynamic` | Don't use `-C prefer-dynamic`, don't build as a dylib via a `--crate-type=dylib` preset flag | `ui`, `crashes` | N/A | +| Directive | Explanation | Supported test suites | Possible values | +|---------------------|----------------------------------------------------------------------------------------------|--------------------------------------------|--------------------------------------------------------------------------------------------| +| `compile-flags` | Flags passed to `rustc` when building the test or aux file | All except for `run-make`/`run-make-cargo` | Any valid `rustc` flags, e.g. `-Awarnings -Dfoo`. Cannot be `-Cincremental` or `--edition` | +| `edition` | The edition used to build the test | All except for `run-make`/`run-make-cargo` | Any valid `--edition` value | +| `rustc-env` | Env var to set when running `rustc` | All except for `run-make`/`run-make-cargo` | `<KEY>=<VALUE>` | +| `unset-rustc-env` | Env var to unset when running `rustc` | All except for `run-make`/`run-make-cargo` | Any env var name | +| `incremental` | Proper incremental support for tests outside of incremental test suite | `ui`, `crashes` | N/A | +| `no-prefer-dynamic` | Don't use `-C prefer-dynamic`, don't build as a dylib via a `--crate-type=dylib` preset flag | `ui`, `crashes` | N/A | <div class="warning"> -Tests (outside of `run-make`) that want to use incremental tests not in the +Tests (outside of `run-make`/`run-make-cargo`) that want to use incremental tests not in the incremental test-suite must not pass `-C incremental` via `compile-flags`, and must instead use the `//@ incremental` directive. @@ -264,9 +264,9 @@ Consider writing the test as a proper incremental test instead. ### Rustdoc -| Directive | Explanation | Supported test suites | Possible values | -|-------------|--------------------------------------------------------------|------------------------------------------|---------------------------| -| `doc-flags` | Flags passed to `rustdoc` when building the test or aux file | `rustdoc`, `rustdoc-js`, `rustdoc-json` | Any valid `rustdoc` flags | +| Directive | Explanation | Supported test suites | Possible values | +|-------------|--------------------------------------------------------------|-----------------------------------------|---------------------------| +| `doc-flags` | Flags passed to `rustdoc` when building the test or aux file | `rustdoc`, `rustdoc-js`, `rustdoc-json` | Any valid `rustdoc` flags | <!-- **FIXME(rustdoc)**: what does `check-test-line-numbers-match` do? diff --git a/src/doc/rustc-dev-guide/src/tests/misc.md b/src/doc/rustc-dev-guide/src/tests/misc.md index 39f88174879..cc8f501224f 100644 --- a/src/doc/rustc-dev-guide/src/tests/misc.md +++ b/src/doc/rustc-dev-guide/src/tests/misc.md @@ -24,8 +24,8 @@ In `ui` tests and other test suites that support `//@ rustc-env`, you can specif //@ rustc-env:RUSTC_BOOTSTRAP=-1 ``` -For `run-make` tests, `//@ rustc-env` is not supported. You can do something -like the following for individual `rustc` invocations. +For `run-make`/`run-make-cargo` tests, `//@ rustc-env` is not supported. You can do +something like the following for individual `rustc` invocations. ```rust,ignore use run_make_support::rustc; diff --git a/src/doc/rustc/src/command-line-arguments/print-options.md b/src/doc/rustc/src/command-line-arguments/print-options.md index fed19d6b667..f37b27d88c3 100644 --- a/src/doc/rustc/src/command-line-arguments/print-options.md +++ b/src/doc/rustc/src/command-line-arguments/print-options.md @@ -184,7 +184,7 @@ Example: ```bash $ rustc --print native-static-libs --crate-type staticlib a.rs -note: Link against the following native artifacts when linking against this static library. The order and any duplication can be significant on some platforms. +note: link against the following native artifacts when linking against this static library. The order and any duplication can be significant on some platforms. note: native-static-libs: -lgcc_s -lutil [REDACTED] -lpthread -lm -ldl -lc ``` diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index dfe193e6e8a..6c5b48d8c8f 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -33,6 +33,7 @@ All tier 1 targets with host tools support the full standard library. target | notes -------|------- [`aarch64-apple-darwin`](platform-support/apple-darwin.md) | ARM64 macOS (11.0+, Big Sur+) +[`aarch64-pc-windows-msvc`](platform-support/windows-msvc.md) | ARM64 Windows MSVC `aarch64-unknown-linux-gnu` | ARM64 Linux (kernel 4.1+, glibc 2.17+) [`i686-pc-windows-msvc`](platform-support/windows-msvc.md) | 32-bit MSVC (Windows 10+, Windows Server 2016+, Pentium 4) [^x86_32-floats-return-ABI] [^win32-msvc-alignment] `i686-unknown-linux-gnu` | 32-bit Linux (kernel 3.2+, glibc 2.17+, Pentium 4) [^x86_32-floats-return-ABI] @@ -88,7 +89,6 @@ so Rustup may install the documentation for a similar tier 1 target instead. target | notes -------|------- [`aarch64-pc-windows-gnullvm`](platform-support/windows-gnullvm.md) | ARM64 MinGW (Windows 10+), LLVM ABI -[`aarch64-pc-windows-msvc`](platform-support/windows-msvc.md) | ARM64 Windows MSVC [`aarch64-unknown-linux-musl`](platform-support/aarch64-unknown-linux-musl.md) | ARM64 Linux with musl 1.2.3 [`aarch64-unknown-linux-ohos`](platform-support/openharmony.md) | ARM64 OpenHarmony `arm-unknown-linux-gnueabi` | Armv6 Linux (kernel 3.2+, glibc 2.17) diff --git a/src/doc/rustc/src/platform-support/vxworks.md b/src/doc/rustc/src/platform-support/vxworks.md index 3fccee80722..091c757a2ee 100644 --- a/src/doc/rustc/src/platform-support/vxworks.md +++ b/src/doc/rustc/src/platform-support/vxworks.md @@ -20,6 +20,7 @@ Target triplets available: ## Target maintainers [@biabbas](https://github.com/biabbas) +[@hax0kartik](https://github.com/hax0kartik) ## Requirements diff --git a/src/doc/rustc/src/platform-support/windows-msvc.md b/src/doc/rustc/src/platform-support/windows-msvc.md index 71dc4ddc2e6..826c75b79c5 100644 --- a/src/doc/rustc/src/platform-support/windows-msvc.md +++ b/src/doc/rustc/src/platform-support/windows-msvc.md @@ -4,13 +4,10 @@ Windows MSVC targets. **Tier 1 with host tools:** +- `aarch64-pc-windows-msvc`: Windows on ARM64. - `i686-pc-windows-msvc`: Windows on 32-bit x86. - `x86_64-pc-windows-msvc`: Windows on 64-bit x86. -**Tier 2 with host tools:** - -- `aarch64-pc-windows-msvc`: Windows on ARM64. - ## Target maintainers [@ChrisDenton](https://github.com/ChrisDenton) diff --git a/src/doc/style-guide/src/README.md b/src/doc/style-guide/src/README.md index f42b9cb5978..c3788c97ae6 100644 --- a/src/doc/style-guide/src/README.md +++ b/src/doc/style-guide/src/README.md @@ -112,6 +112,14 @@ fn bar() {} fn baz() {} ``` +### Trailing whitespace + +Do not include trailing whitespace on the end of any line. This includes blank +lines, comment lines, code lines, and string literals. + +Note that avoiding trailing whitespace in string literals requires care to +preserve the value of the literal. + ### Sorting In various cases, the default Rust style specifies to sort things. If not @@ -225,8 +233,8 @@ newline after the opening sigil, and a newline before the closing sigil. Prefer to put a comment on its own line. Where a comment follows code, put a single space before it. Where a block comment appears inline, use surrounding -whitespace as if it were an identifier or keyword. Do not include trailing -whitespace after a comment or at the end of any line in a multi-line comment. +whitespace as if it were an identifier or keyword. + Examples: ```rust diff --git a/src/doc/unstable-book/src/compiler-flags/sanitizer.md b/src/doc/unstable-book/src/compiler-flags/sanitizer.md index 2f9d4d22e5a..493256de99d 100644 --- a/src/doc/unstable-book/src/compiler-flags/sanitizer.md +++ b/src/doc/unstable-book/src/compiler-flags/sanitizer.md @@ -244,18 +244,16 @@ See the [Clang ControlFlowIntegrity documentation][clang-cfi] for more details. ## Example 1: Redirecting control flow using an indirect branch/call to an invalid destination -```rust,ignore (making doc tests pass cross-platform is hard) -use std::arch::naked_asm; -use std::mem; - +```rust fn add_one(x: i32) -> i32 { x + 1 } #[unsafe(naked)] -pub extern "C" fn add_two(x: i32) { +# #[cfg(all(target_os = "linux", target_arch = "x86_64"))] +pub extern "sysv64" fn add_two(x: i32) { // x + 2 preceded by a landing pad/nop block - naked_asm!( + std::arch::naked_asm!( " nop nop @@ -281,16 +279,18 @@ fn main() { println!("The answer is: {}", answer); - println!("With CFI enabled, you should not see the next answer"); - let f: fn(i32) -> i32 = unsafe { - // Offset 0 is a valid branch/call destination (i.e., the function entry - // point), but offsets 1-8 within the landing pad/nop block are invalid - // branch/call destinations (i.e., within the body of the function). - mem::transmute::<*const u8, fn(i32) -> i32>((add_two as *const u8).offset(5)) - }; - let next_answer = do_twice(f, 5); - - println!("The next answer is: {}", next_answer); +# #[cfg(all(target_os = "linux", target_arch = "x86_64"))] { + println!("With CFI enabled, you should not see the next answer"); + let f: fn(i32) -> i32 = unsafe { + // Offset 0 is a valid branch/call destination (i.e., the function entry + // point), but offsets 1-8 within the landing pad/nop block are invalid + // branch/call destinations (i.e., within the body of the function). + std::mem::transmute::<*const u8, fn(i32) -> i32>((add_two as *const u8).offset(5)) + }; + let next_answer = do_twice(f, 5); + + println!("The next answer is: {}", next_answer); +# } } ``` Fig. 1. Redirecting control flow using an indirect branch/call to an invalid diff --git a/src/doc/unstable-book/src/language-features/extended-varargs-abi-support.md b/src/doc/unstable-book/src/language-features/extended-varargs-abi-support.md deleted file mode 100644 index b20c30ec8f1..00000000000 --- a/src/doc/unstable-book/src/language-features/extended-varargs-abi-support.md +++ /dev/null @@ -1,10 +0,0 @@ -# `extended_varargs_abi_support` - -The tracking issue for this feature is: [#100189] - -[#100189]: https://github.com/rust-lang/rust/issues/100189 - ------------------------- - -This feature adds the possibility of using `sysv64`, `win64` or `efiapi` calling -conventions on functions with varargs. diff --git a/src/etc/htmldocck.py b/src/etc/htmldocck.py index 8d7f7341c2e..46a3a1602ac 100755 --- a/src/etc/htmldocck.py +++ b/src/etc/htmldocck.py @@ -247,7 +247,7 @@ class CachedFiles(object): paths = list(Path(self.root).glob(path)) if len(paths) != 1: raise FailedCheck("glob path does not resolve to one file") - path = str(paths[0]) + return str(paths[0]) return os.path.join(self.root, path) def get_file(self, path): diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index 2985971a053..f37a8d85361 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -12,20 +12,20 @@ path = "lib.rs" arrayvec = { version = "0.7", default-features = false } askama = { version = "0.14", default-features = false, features = ["alloc", "config", "derive"] } base64 = "0.21.7" -indexmap.workspace = true -itertools.workspace = true +indexmap = "2" +itertools = "0.12" minifier = { version = "0.3.5", default-features = false } pulldown-cmark-escape = { version = "0.11.0", features = ["simd"] } regex = "1" rustdoc-json-types = { path = "../rustdoc-json-types" } serde = { version = "1.0", features = ["derive"] } -serde_json.workspace = true +serde_json = "1.0" smallvec = "1.8.1" -stringdex = { version = "0.0.1-alpha4" } -tempfile.workspace = true +stringdex = { version = "0.0.1-alpha9" } +tempfile = "3" threadpool = "1.8.1" +tracing = "0.1" tracing-tree = "0.3.0" -tracing.workspace = true unicode-segmentation = "1.9" # tidy-alphabetical-end diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 4ff94cc6f3b..93932936a2e 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -557,7 +557,7 @@ fn clean_generic_param_def( }, ) } - ty::GenericParamDefKind::Const { has_default, synthetic } => ( + ty::GenericParamDefKind::Const { has_default } => ( def.name, GenericParamDefKind::Const { ty: Box::new(clean_middle_ty( @@ -580,7 +580,6 @@ fn clean_generic_param_def( } else { None }, - synthetic, }, ), }; @@ -636,14 +635,13 @@ fn clean_generic_param<'tcx>( }, ) } - hir::GenericParamKind::Const { ty, default, synthetic } => ( + hir::GenericParamKind::Const { ty, default } => ( param.name.ident().name, GenericParamDefKind::Const { ty: Box::new(clean_ty(ty, cx)), default: default.map(|ct| { Box::new(lower_const_arg_for_rustdoc(cx.tcx, ct, FeedConstTy::No).to_string()) }), - synthetic, }, ), }; diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index fcff15650ce..dcd67cb7ebc 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1396,7 +1396,7 @@ pub(crate) enum GenericParamDefKind { Lifetime { outlives: ThinVec<Lifetime> }, Type { bounds: ThinVec<GenericBound>, default: Option<Box<Type>>, synthetic: bool }, // Option<Box<String>> makes this type smaller than `Option<String>` would. - Const { ty: Box<Type>, default: Option<Box<String>>, synthetic: bool }, + Const { ty: Box<Type>, default: Option<Box<String>> }, } impl GenericParamDefKind { diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 450ac04b40d..d7f6fa347be 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -454,15 +454,22 @@ impl Options { return None; } - let mut emit = Vec::new(); + let mut emit = FxIndexMap::<_, EmitType>::default(); for list in matches.opt_strs("emit") { for kind in list.split(',') { match kind.parse() { - Ok(kind) => emit.push(kind), + Ok(kind) => { + // De-duplicate emit types and the last wins. + // Only one instance for each type is allowed + // regardless the actual data it carries. + // This matches rustc's `--emit` behavior. + emit.insert(std::mem::discriminant(&kind), kind); + } Err(()) => dcx.fatal(format!("unrecognized emission type: {kind}")), } } } + let emit = emit.into_values().collect::<Vec<_>>(); let show_coverage = matches.opt_present("show-coverage"); let output_format_s = matches.opt_str("output-format"); @@ -814,7 +821,8 @@ impl Options { let scrape_examples_options = ScrapeExamplesOptions::new(matches, dcx); let with_examples = matches.opt_strs("with-examples"); - let call_locations = crate::scrape_examples::load_call_locations(with_examples, dcx); + let call_locations = + crate::scrape_examples::load_call_locations(with_examples, dcx, &mut loaded_paths); let doctest_build_args = matches.opt_strs("doctest-build-arg"); let unstable_features = diff --git a/src/librustdoc/formats/item_type.rs b/src/librustdoc/formats/item_type.rs index e94ef517309..ab40c01cb36 100644 --- a/src/librustdoc/formats/item_type.rs +++ b/src/librustdoc/formats/item_type.rs @@ -8,6 +8,9 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer, de}; use crate::clean; +macro_rules! item_type { + ($($variant:ident = $number:literal,)+) => { + /// Item type. Corresponds to `clean::ItemEnum` variants. /// /// The search index uses item types encoded as smaller numbers which equal to @@ -29,6 +32,44 @@ use crate::clean; #[derive(Copy, PartialEq, Eq, Hash, Clone, Debug, PartialOrd, Ord)] #[repr(u8)] pub(crate) enum ItemType { + $($variant = $number,)+ +} + +impl Serialize for ItemType { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + (*self as u8).serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for ItemType { + fn deserialize<D>(deserializer: D) -> Result<ItemType, D::Error> + where + D: Deserializer<'de>, + { + struct ItemTypeVisitor; + impl<'de> de::Visitor<'de> for ItemTypeVisitor { + type Value = ItemType; + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "an integer between 0 and 27") + } + fn visit_u64<E: de::Error>(self, v: u64) -> Result<ItemType, E> { + Ok(match v { + $($number => ItemType::$variant,)+ + _ => return Err(E::missing_field("unknown number for `ItemType` enum")), + }) + } + } + deserializer.deserialize_any(ItemTypeVisitor) + } +} + + } +} + +item_type! { Keyword = 0, Primitive = 1, Module = 2, @@ -60,61 +101,6 @@ pub(crate) enum ItemType { Attribute = 27, } -impl Serialize for ItemType { - fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> - where - S: Serializer, - { - (*self as u8).serialize(serializer) - } -} - -impl<'de> Deserialize<'de> for ItemType { - fn deserialize<D>(deserializer: D) -> Result<ItemType, D::Error> - where - D: Deserializer<'de>, - { - struct ItemTypeVisitor; - impl<'de> de::Visitor<'de> for ItemTypeVisitor { - type Value = ItemType; - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(formatter, "an integer between 0 and 25") - } - fn visit_u64<E: de::Error>(self, v: u64) -> Result<ItemType, E> { - Ok(match v { - 0 => ItemType::Keyword, - 1 => ItemType::Primitive, - 2 => ItemType::Module, - 3 => ItemType::ExternCrate, - 4 => ItemType::Import, - 5 => ItemType::Struct, - 6 => ItemType::Enum, - 7 => ItemType::Function, - 8 => ItemType::TypeAlias, - 9 => ItemType::Static, - 10 => ItemType::Trait, - 11 => ItemType::Impl, - 12 => ItemType::TyMethod, - 13 => ItemType::Method, - 14 => ItemType::StructField, - 15 => ItemType::Variant, - 16 => ItemType::Macro, - 17 => ItemType::AssocType, - 18 => ItemType::Constant, - 19 => ItemType::AssocConst, - 20 => ItemType::Union, - 21 => ItemType::ForeignType, - 23 => ItemType::ProcAttribute, - 24 => ItemType::ProcDerive, - 25 => ItemType::TraitAlias, - _ => return Err(E::missing_field("unknown number")), - }) - } - } - deserializer.deserialize_any(ItemTypeVisitor) - } -} - impl<'a> From<&'a clean::Item> for ItemType { fn from(item: &'a clean::Item) -> ItemType { let kind = match &item.kind { diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 6db90c9bf2a..b4ef47d1e26 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -2812,24 +2812,46 @@ fn render_call_locations<W: fmt::Write>( let needs_expansion = line_max - line_min > NUM_VISIBLE_LINES; let locations_encoded = serde_json::to_string(&line_ranges).unwrap(); - // Look for the example file in the source map if it exists, otherwise return a dummy span - let file_span = (|| { - let source_map = tcx.sess.source_map(); - let crate_src = tcx.sess.local_crate_source_file()?.into_local_path()?; + let source_map = tcx.sess.source_map(); + let files = source_map.files(); + let local = tcx.sess.local_crate_source_file().unwrap(); + + let get_file_start_pos = || { + let crate_src = local.clone().into_local_path()?; let abs_crate_src = crate_src.canonicalize().ok()?; let crate_root = abs_crate_src.parent()?.parent()?; let rel_path = path.strip_prefix(crate_root).ok()?; - let files = source_map.files(); - let file = files.iter().find(|file| match &file.name { - FileName::Real(RealFileName::LocalPath(other_path)) => rel_path == other_path, - _ => false, - })?; - Some(rustc_span::Span::with_root_ctxt( - file.start_pos + BytePos(byte_min), - file.start_pos + BytePos(byte_max), - )) - })() - .unwrap_or(DUMMY_SP); + files + .iter() + .find(|file| match &file.name { + FileName::Real(RealFileName::LocalPath(other_path)) => rel_path == other_path, + _ => false, + }) + .map(|file| file.start_pos) + }; + + // Look for the example file in the source map if it exists, otherwise + // return a span to the local crate's source file + let Some(file_span) = get_file_start_pos() + .or_else(|| { + files + .iter() + .find(|file| match &file.name { + FileName::Real(file_name) => file_name == &local, + _ => false, + }) + .map(|file| file.start_pos) + }) + .map(|start_pos| { + rustc_span::Span::with_root_ctxt( + start_pos + BytePos(byte_min), + start_pos + BytePos(byte_max), + ) + }) + else { + // if the fallback span can't be built, don't render the code for this example + return false; + }; let mut decoration_info = FxIndexMap::default(); decoration_info.insert("highlight focus", vec![byte_ranges.remove(0)]); diff --git a/src/librustdoc/html/static/js/rustdoc.d.ts b/src/librustdoc/html/static/js/rustdoc.d.ts index 74f646008eb..938ccc7d2c3 100644 --- a/src/librustdoc/html/static/js/rustdoc.d.ts +++ b/src/librustdoc/html/static/js/rustdoc.d.ts @@ -289,7 +289,7 @@ declare namespace rustdoc { exactModulePath: string, entry: EntryData?, path: PathData?, - type: FunctionData?, + functionData: FunctionData?, deprecated: boolean, parent: { path: PathData, name: string}?, } diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index fa812a2b67b..b01b596da68 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -1212,7 +1212,7 @@ class DocSearch { * will never fulfill. */ async buildIndex() { - const nn = this.database.getIndex("normalizedName"); + const nn = this.database.getData("normalizedName"); if (!nn) { return; } @@ -1802,14 +1802,15 @@ class DocSearch { /** * @param {number} id + * @param {boolean} loadFunctionData * @returns {Promise<rustdoc.Row?>} */ - async getRow(id) { - const [name_, entry, path, type] = await Promise.all([ + async getRow(id, loadFunctionData) { + const [name_, entry, path, functionData] = await Promise.all([ this.getName(id), this.getEntryData(id), this.getPathData(id), - this.getFunctionData(id), + loadFunctionData ? this.getFunctionData(id) : null, ]); if (!entry && !path) { return null; @@ -1853,7 +1854,7 @@ class DocSearch { `${exactModulePathData.exactModulePath}::${exactModuleName}`), entry, path, - type, + functionData, deprecated: entry ? entry.deprecated : false, parent: parentName !== null && parentPath !== null ? { name: parentName, path: parentPath } : @@ -2563,11 +2564,11 @@ class DocSearch { name: item.parent.name, ty: item.parent.path.ty, } : undefined, - type: item.type && item.type.functionSignature ? - item.type.functionSignature : + type: item.functionData && item.functionData.functionSignature ? + item.functionData.functionSignature : undefined, - paramNames: item.type && item.type.paramNames ? - item.type.paramNames : + paramNames: item.functionData && item.functionData.paramNames ? + item.functionData.paramNames : undefined, dist: result.dist, path_dist: result.path_dist, @@ -2642,7 +2643,7 @@ class DocSearch { /** * @type {rustdoc.Row?} */ - const item = await this.getRow(result.id); + const item = await this.getRow(result.id, typeInfo !== null); if (!item) { continue; } @@ -3721,7 +3722,7 @@ class DocSearch { * @returns {AsyncGenerator<rustdoc.ResultObject>} */ async function*(currentCrate) { - const index = this.database.getIndex("normalizedName"); + const index = this.database.getData("normalizedName"); if (!index) { return; } @@ -3749,7 +3750,7 @@ class DocSearch { is_alias: true, elems: [], // only used in type-based queries returned: [], // only used in type-based queries - original: await this.getRow(alias), + original: await this.getRow(alias, false), }; }; /** @@ -3804,7 +3805,7 @@ class DocSearch { * @returns {Promise<rustdoc.PlainResultObject?>} */ const handleNameSearch = async id => { - const row = await this.getRow(id); + const row = await this.getRow(id, false); if (!row || !row.entry) { return null; } @@ -3850,8 +3851,7 @@ class DocSearch { }; if (elem.normalizedPathLast === "") { // faster full-table scan for this specific case. - const nameData = this.database.getData("name"); - const l = nameData ? nameData.length : 0; + const l = index.length; for (let id = 0; id < l; ++id) { if (!idDuplicates.has(id)) { idDuplicates.add(id); @@ -3953,7 +3953,7 @@ class DocSearch { * @returns {AsyncGenerator<rustdoc.ResultObject>} */ async function*(inputs, output, typeInfo, currentCrate) { - const index = this.database.getIndex("normalizedName"); + const index = this.database.getData("normalizedName"); if (!index) { return; } diff --git a/src/librustdoc/html/static/js/stringdex.d.ts b/src/librustdoc/html/static/js/stringdex.d.ts index cf9a8b6b564..2eb1fdf95d8 100644 --- a/src/librustdoc/html/static/js/stringdex.d.ts +++ b/src/librustdoc/html/static/js/stringdex.d.ts @@ -5,18 +5,9 @@ declare namespace stringdex { * The client interface to Stringdex. */ interface Database { - getIndex(colname: string): SearchTree|undefined; getData(colname: string): DataColumn|undefined; } /** - * A search index file. - */ - interface SearchTree { - trie(): Trie; - search(name: Uint8Array|string): Promise<Trie?>; - searchLev(name: Uint8Array|string): AsyncGenerator<Trie>; - } - /** * A compressed node in the search tree. * * This object logically addresses two interleaved trees: @@ -29,9 +20,7 @@ declare namespace stringdex { matches(): RoaringBitmap; substringMatches(): AsyncGenerator<RoaringBitmap>; prefixMatches(): AsyncGenerator<RoaringBitmap>; - keys(): Uint8Array; keysExcludeSuffixOnly(): Uint8Array; - children(): [number, Promise<Trie>][]; childrenExcludeSuffixOnly(): [number, Promise<Trie>][]; child(id: number): Promise<Trie>?; } @@ -41,6 +30,8 @@ declare namespace stringdex { interface DataColumn { isEmpty(id: number): boolean; at(id: number): Promise<Uint8Array|undefined>; + search(name: Uint8Array|string): Promise<Trie?>; + searchLev(name: Uint8Array|string): AsyncGenerator<Trie>; length: number, } /** diff --git a/src/librustdoc/html/static/js/stringdex.js b/src/librustdoc/html/static/js/stringdex.js index cb956d926db..b7f605a1035 100644 --- a/src/librustdoc/html/static/js/stringdex.js +++ b/src/librustdoc/html/static/js/stringdex.js @@ -1,3 +1,4 @@ +// ignore-tidy-filelength /** * @import * as stringdex from "./stringdex.d.ts" */ @@ -486,6 +487,63 @@ class RoaringBitmap { return mid === -1 ? false : this.containers[mid].contains(value); } /** + * @param {number} keyvalue + * @returns {RoaringBitmap} + */ + remove(keyvalue) { + const key = keyvalue >> 16; + const value = keyvalue & 0xFFFF; + const mid = this.getContainerId(key); + if (mid === -1) { + return this; + } + const container = this.containers[mid]; + if (!container.contains(value)) { + return this; + } + const newCardinality = (this.keysAndCardinalities[(mid * 4) + 2] | + (this.keysAndCardinalities[(mid * 4) + 3] << 8)); + const l = this.containers.length; + const m = l - (newCardinality === 0 ? 1 : 0); + const result = new RoaringBitmap(null, 0); + result.keysAndCardinalities = new Uint8Array(m * 4); + let j = 0; + for (let i = 0; i < l; i += 1) { + if (i === mid) { + if (newCardinality !== 0) { + result.keysAndCardinalities[(j * 4) + 0] = key; + result.keysAndCardinalities[(j * 4) + 1] = key >> 8; + const card = newCardinality - 1; + result.keysAndCardinalities[(j * 4) + 2] = card; + result.keysAndCardinalities[(j * 4) + 3] = card >> 8; + const newContainer = new RoaringBitmapArray( + newCardinality, + new Uint8Array(newCardinality * 2), + ); + let newContainerSlot = 0; + for (const containerValue of container.values()) { + if (containerValue !== value) { + newContainer.array[newContainerSlot] = value & 0xFF; + newContainerSlot += 1; + newContainer.array[newContainerSlot] = value >> 8; + newContainerSlot += 1; + } + } + result.containers.push(newContainer); + j += 1; + } + } else { + result.keysAndCardinalities[(j * 4) + 0] = this.keysAndCardinalities[(i * 4) + 0]; + result.keysAndCardinalities[(j * 4) + 1] = this.keysAndCardinalities[(i * 4) + 1]; + result.keysAndCardinalities[(j * 4) + 2] = this.keysAndCardinalities[(i * 4) + 2]; + result.keysAndCardinalities[(j * 4) + 3] = this.keysAndCardinalities[(i * 4) + 3]; + result.containers.push(this.containers[i]); + j += 1; + } + } + return result; + } + /** * @param {number} key * @returns {number} */ @@ -878,6 +936,46 @@ function bitCount(n) { /*eslint-enable */ /** + * https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore%E2%80%93Horspool_algorithm + */ +class Uint8ArraySearchPattern { + /** @param {Uint8Array} needle */ + constructor(needle) { + this.needle = needle; + this.skipTable = []; + const m = needle.length; + for (let i = 0; i < 256; i += 1) { + this.skipTable.push(m); + } + for (let i = 0; i < m - 1; i += 1) { + this.skipTable[needle[i]] = m - 1 - i; + } + } + /** + * @param {Uint8Array} haystack + * @returns {boolean} + */ + matches(haystack) { + const needle = this.needle; + const skipTable = this.skipTable; + const m = needle.length; + const n = haystack.length; + + let skip = 0; + search: while (n - skip >= m) { + for (let i = m - 1; i >= 0; i -= 1) { + if (haystack[skip + i] !== needle[i]) { + skip += skipTable[haystack[skip + m - 1]]; + continue search; + } + } + return true; + } + return false; + } +} + +/** * @param {stringdex.Hooks} hooks * @returns {Promise<stringdex.Database>} */ @@ -887,12 +985,6 @@ function loadDatabase(hooks) { rr_: function(data) { const dataObj = JSON.parse(data); for (const colName of Object.keys(dataObj)) { - if (Object.hasOwn(dataObj[colName], "I")) { - registry.searchTreeRoots.set( - colName, - makeSearchTreeFromBase64(dataObj[colName].I)[1], - ); - } if (Object.hasOwn(dataObj[colName], "N")) { const counts = []; const countsstring = dataObj[colName]["N"]; @@ -915,6 +1007,9 @@ function loadDatabase(hooks) { makeUint8ArrayFromBase64(dataObj[colName]["H"]), new RoaringBitmap(makeUint8ArrayFromBase64(dataObj[colName]["E"]), 0), colName, + Object.hasOwn(dataObj[colName], "I") ? + makeSearchTreeFromBase64(dataObj[colName].I)[1] : + null, )); } } @@ -978,7 +1073,7 @@ function loadDatabase(hooks) { * searchTreePromises: HashTable<Promise<SearchTree>>; * dataColumnLoadPromiseCallbacks: HashTable<function(any, Uint8Array[]?): any>; * dataColumns: Map<string, DataColumn>; - * dataColumnsBuckets: Map<string, HashTable<Promise<Uint8Array[]>>>; + * dataColumnsBuckets: HashTable<Promise<Uint8Array[]>>; * searchTreeLoadByNodeID: function(Uint8Array): Promise<SearchTree>; * searchTreeRootCallback?: function(any, Database?): any; * dataLoadByNameAndHash: function(string, Uint8Array): Promise<Uint8Array[]>; @@ -990,7 +1085,7 @@ function loadDatabase(hooks) { searchTreePromises: new HashTable(), dataColumnLoadPromiseCallbacks: new HashTable(), dataColumns: new Map(), - dataColumnsBuckets: new Map(), + dataColumnsBuckets: new HashTable(), searchTreeLoadByNodeID: function(nodeid) { const existingPromise = registry.searchTreePromises.get(nodeid); if (existingPromise) { @@ -1022,7 +1117,7 @@ function loadDatabase(hooks) { const data = (nodeid[0] & 0x20) !== 0 ? Uint8Array.of(((nodeid[0] & 0x0f) << 4) | (nodeid[1] >> 4)) : EMPTY_UINT8; - newPromise = Promise.resolve(new SearchTree( + newPromise = Promise.resolve(new PrefixSearchTree( EMPTY_SEARCH_TREE_BRANCHES, EMPTY_SEARCH_TREE_BRANCHES, data, @@ -1058,12 +1153,7 @@ function loadDatabase(hooks) { return newPromise; }, dataLoadByNameAndHash: function(name, hash) { - let dataColumnBuckets = registry.dataColumnsBuckets.get(name); - if (dataColumnBuckets === undefined) { - dataColumnBuckets = new HashTable(); - registry.dataColumnsBuckets.set(name, dataColumnBuckets); - } - const existingBucket = dataColumnBuckets.get(hash); + const existingBucket = registry.dataColumnsBuckets.get(hash); if (existingBucket) { return existingBucket; } @@ -1091,16 +1181,17 @@ function loadDatabase(hooks) { hooks.loadDataByNameAndHash(name, hashHex); } }); - dataColumnBuckets.set(hash, newBucket); + registry.dataColumnsBuckets.set(hash, newBucket); return newBucket; }, }; /** * The set of child subtrees. + * @template ST * @type {{ * nodeids: Uint8Array, - * subtrees: Array<Promise<SearchTree>|null>, + * subtrees: Array<Promise<ST>|null>, * }} */ class SearchTreeBranches { @@ -1128,7 +1219,7 @@ function loadDatabase(hooks) { ); } // https://github.com/microsoft/TypeScript/issues/17227 - /** @returns {Generator<[number, Promise<SearchTree>|null]>} */ + /** @returns {Generator<[number, Promise<ST>|null]>} */ entries() { throw new Error(); } @@ -1157,10 +1248,12 @@ function loadDatabase(hooks) { /** * A sorted array of search tree branches. * + * @template ST + * @extends SearchTreeBranches<ST> * @type {{ * keys: Uint8Array, * nodeids: Uint8Array, - * subtrees: Array<Promise<SearchTree>|null>, + * subtrees: Array<Promise<ST>|null>, * }} */ class SearchTreeBranchesArray extends SearchTreeBranches { @@ -1179,7 +1272,7 @@ function loadDatabase(hooks) { i += 1; } } - /** @returns {Generator<[number, Promise<SearchTree>|null]>} */ + /** @returns {Generator<[number, Promise<ST>|null]>} */ * entries() { let i = 0; const l = this.keys.length; @@ -1246,12 +1339,16 @@ function loadDatabase(hooks) { } /** + * @template ST * @param {number[]} alphabitmap_chars * @param {number} width - * @return {(typeof SearchTreeBranches)&{"ALPHABITMAP_CHARS": number[], "width": number}} + * @return {(typeof SearchTreeBranches<ST>)&{"ALPHABITMAP_CHARS": number[], "width": number}} */ function makeSearchTreeBranchesAlphaBitmapClass(alphabitmap_chars, width) { const bitwidth = width * 8; + /** + * @extends SearchTreeBranches<ST> + */ const cls = class SearchTreeBranchesAlphaBitmap extends SearchTreeBranches { /** * @param {number} bitmap @@ -1265,7 +1362,7 @@ function loadDatabase(hooks) { this.bitmap = bitmap; this.nodeids = nodeids; } - /** @returns {Generator<[number, Promise<SearchTree>|null]>} */ + /** @returns {Generator<[number, Promise<ST>|null]>} */ * entries() { let i = 0; let j = 0; @@ -1295,15 +1392,7 @@ function loadDatabase(hooks) { * @returns {number} */ getKey(branch_index) { - //return this.getKeys()[branch_index]; - let alpha_index = 0; - while (branch_index >= 0) { - if (this.bitmap & (1 << alpha_index)) { - branch_index -= 1; - } - alpha_index += 1; - } - return alphabitmap_chars[alpha_index]; + return this.getKeys()[branch_index]; } /** * @returns {Uint8Array} @@ -1326,20 +1415,35 @@ function loadDatabase(hooks) { return cls; } + /** + * @template ST + * @type {(typeof SearchTreeBranches<any>)&{"ALPHABITMAP_CHARS": number[], "width": number}} + */ const SearchTreeBranchesShortAlphaBitmap = makeSearchTreeBranchesAlphaBitmapClass(SHORT_ALPHABITMAP_CHARS, 3); + /** + * @template ST + * @type {(typeof SearchTreeBranches<any>)&{"ALPHABITMAP_CHARS": number[], "width": number}} + */ const SearchTreeBranchesLongAlphaBitmap = makeSearchTreeBranchesAlphaBitmapClass(LONG_ALPHABITMAP_CHARS, 4); /** - * A [suffix tree], used for name-based search. + * @typedef {PrefixSearchTree|SuffixSearchTree} SearchTree + * @typedef {PrefixTrie|SuffixTrie} Trie + */ + + /** + * An interleaved [prefix] and [suffix tree], + * used for name-based search. * - * This data structure is used to drive substring matches, + * This data structure is used to drive prefix matches, * such as matching the query "link" to `LinkedList`, * and Lev-distance matches, such as matching the * query "hahsmap" to `HashMap`. * + * [prefix tree]: https://en.wikipedia.org/wiki/Prefix_tree * [suffix tree]: https://en.wikipedia.org/wiki/Suffix_tree * * branches @@ -1358,17 +1462,17 @@ function loadDatabase(hooks) { * will include these. * * @type {{ - * might_have_prefix_branches: SearchTreeBranches, - * branches: SearchTreeBranches, + * might_have_prefix_branches: SearchTreeBranches<SearchTree>, + * branches: SearchTreeBranches<SearchTree>, * data: Uint8Array, * leaves_suffix: RoaringBitmap, * leaves_whole: RoaringBitmap, * }} */ - class SearchTree { + class PrefixSearchTree { /** - * @param {SearchTreeBranches} branches - * @param {SearchTreeBranches} might_have_prefix_branches + * @param {SearchTreeBranches<SearchTree>} branches + * @param {SearchTreeBranches<SearchTree>} might_have_prefix_branches * @param {Uint8Array} data * @param {RoaringBitmap} leaves_whole * @param {RoaringBitmap} leaves_suffix @@ -1392,25 +1496,31 @@ function loadDatabase(hooks) { * A Trie pointer refers to a single node in a logical decompressed search tree * (the real search tree is compressed). * - * @return {Trie} + * @param {DataColumn} dataColumn + * @param {Uint8ArraySearchPattern} searchPattern + * @return {PrefixTrie} */ - trie() { - return new Trie(this, 0); + trie(dataColumn, searchPattern) { + return new PrefixTrie(this, 0, dataColumn, searchPattern); } /** * Return the trie representing `name` * @param {Uint8Array|string} name + * @param {DataColumn} dataColumn * @returns {Promise<Trie?>} */ - async search(name) { + async search(name, dataColumn) { if (typeof name === "string") { const utf8encoder = new TextEncoder(); name = utf8encoder.encode(name); } - let trie = this.trie(); + const searchPattern = new Uint8ArraySearchPattern(name); + /** @type {Trie} */ + let trie = this.trie(dataColumn, searchPattern); for (const datum of name) { // code point definitely exists + /** @type {Promise<Trie>?} */ const newTrie = trie.child(datum); if (newTrie) { trie = await newTrie; @@ -1423,26 +1533,28 @@ function loadDatabase(hooks) { /** * @param {Uint8Array|string} name + * @param {DataColumn} dataColumn * @returns {AsyncGenerator<Trie>} */ - async* searchLev(name) { + async* searchLev(name, dataColumn) { if (typeof name === "string") { const utf8encoder = new TextEncoder(); name = utf8encoder.encode(name); } const w = name.length; if (w < 3) { - const trie = await this.search(name); + const trie = await this.search(name, dataColumn); if (trie !== null) { yield trie; } return; } + const searchPattern = new Uint8ArraySearchPattern(name); const levParams = w >= 6 ? new Lev2TParametricDescription(w) : new Lev1TParametricDescription(w); /** @type {Array<[Promise<Trie>, number]>} */ - const stack = [[Promise.resolve(this.trie()), 0]]; + const stack = [[Promise.resolve(this.trie(dataColumn, searchPattern)), 0]]; const n = levParams.n; while (stack.length !== 0) { // It's not empty @@ -1475,20 +1587,29 @@ function loadDatabase(hooks) { } } } + + /** @returns {RoaringBitmap} */ + getCurrentLeaves() { + return this.leaves_whole.union(this.leaves_suffix); + } } /** * A representation of a set of strings in the search index, * as a subset of the entire tree. */ - class Trie { + class PrefixTrie { /** - * @param {SearchTree} tree + * @param {PrefixSearchTree} tree * @param {number} offset + * @param {DataColumn} dataColumn + * @param {Uint8ArraySearchPattern} searchPattern */ - constructor(tree, offset) { + constructor(tree, offset, dataColumn, searchPattern) { this.tree = tree; this.offset = offset; + this.dataColumn = dataColumn; + this.searchPattern = searchPattern; } /** @@ -1514,7 +1635,28 @@ function loadDatabase(hooks) { const current_layer = layer; layer = []; for await (const tree of current_layer) { - yield tree.leaves_whole.union(tree.leaves_suffix); + /** @type {number[]?} */ + let rejected = null; + let leaves = tree.getCurrentLeaves(); + for (const leaf of leaves.entries()) { + const haystack = await this.dataColumn.at(leaf); + if (haystack === undefined || !this.searchPattern.matches(haystack)) { + if (!rejected) { + rejected = []; + } + rejected.push(leaf); + } + } + if (rejected) { + if (leaves.cardinality() !== rejected.length) { + for (const rej of rejected) { + leaves = leaves.remove(rej); + } + yield leaves; + } + } else { + yield leaves; + } } /** @type {HashTable<[number, SearchTree][]>} */ const subnodes = new HashTable(); @@ -1548,12 +1690,14 @@ function loadDatabase(hooks) { const res = registry.searchTreeLoadByNodeID(newnode); for (const [byte, node] of subnode_list) { const branches = node.branches; - const might_have_prefix_branches = node.might_have_prefix_branches; const i = branches.getIndex(byte); branches.subtrees[i] = res; - const mhpI = might_have_prefix_branches.getIndex(byte); - if (mhpI !== -1) { - might_have_prefix_branches.subtrees[mhpI] = res; + if (node instanceof PrefixSearchTree) { + const might_have_prefix_branches = node.might_have_prefix_branches; + const mhpI = might_have_prefix_branches.getIndex(byte); + if (mhpI !== -1) { + might_have_prefix_branches.subtrees[mhpI] = res; + } } } layer.push(res); @@ -1581,6 +1725,9 @@ function loadDatabase(hooks) { // if we want to do them in order) for (const {node, len} of current_layer) { const tree = await node; + if (!(tree instanceof PrefixSearchTree)) { + continue; + } const length = len + tree.data.length; if (minLength === null || length < minLength) { minLength = length; @@ -1637,10 +1784,13 @@ function loadDatabase(hooks) { } } // if we still have more subtrees to walk, then keep going - /** @type {HashTable<{byte: number, tree: SearchTree, len: number}[]>} */ + /** @type {HashTable<{byte: number, tree: PrefixSearchTree, len: number}[]>} */ const subnodes = new HashTable(); for await (const {node, len} of current_layer) { const tree = await node; + if (!(tree instanceof PrefixSearchTree)) { + continue; + } const length = len + tree.data.length; const mhp_branches = tree.might_have_prefix_branches; const l = mhp_branches.subtrees.length; @@ -1663,8 +1813,6 @@ function loadDatabase(hooks) { subnode_list.push({byte, tree, len}); } } - } else { - throw new Error(`malformed tree; index ${i} does not exist`); } } } @@ -1728,14 +1876,21 @@ function loadDatabase(hooks) { this.tree.might_have_prefix_branches.subtrees[mhpI] = node; } } - nodes.push([k, node.then(node => node.trie())]); + nodes.push([k, node.then(node => { + return node.trie(this.dataColumn, this.searchPattern); + })]); i += 1; } return nodes; } else { /** @type {number} */ const codePoint = data[this.offset]; - const trie = new Trie(this.tree, this.offset + 1); + const trie = new PrefixTrie( + this.tree, + this.offset + 1, + this.dataColumn, + this.searchPattern, + ); return [[codePoint, Promise.resolve(trie)]]; } } @@ -1777,14 +1932,21 @@ function loadDatabase(hooks) { this.tree.might_have_prefix_branches.subtrees[i] = node; this.tree.branches.subtrees[this.tree.branches.getIndex(k)] = node; } - nodes.push([k, node.then(node => node.trie())]); + nodes.push([k, node.then(node => { + return node.trie(this.dataColumn, this.searchPattern); + })]); i += 1; } return nodes; } else { /** @type {number} */ const codePoint = data[this.offset]; - const trie = new Trie(this.tree, this.offset + 1); + const trie = new PrefixTrie( + this.tree, + this.offset + 1, + this.dataColumn, + this.searchPattern, + ); return [[codePoint, Promise.resolve(trie)]]; } } @@ -1811,10 +1973,275 @@ function loadDatabase(hooks) { this.tree.might_have_prefix_branches.subtrees[mhpI] = branch; } } - return branch.then(branch => branch.trie()); + return branch.then(branch => branch.trie(this.dataColumn, this.searchPattern)); } } else if (this.tree.data[this.offset] === byte) { - return Promise.resolve(new Trie(this.tree, this.offset + 1)); + return Promise.resolve(new PrefixTrie( + this.tree, + this.offset + 1, + this.dataColumn, + this.searchPattern, + )); + } + return null; + } + } + /** + * A [suffix tree], used for name-based search. + * + * This data structure is used to drive substring matches, + * such as matching the query "inked" to `LinkedList`. + * + * [suffix tree]: https://en.wikipedia.org/wiki/Suffix_tree + * + * Suffix trees do not actually carry the intermediate data + * between branches, so, in order to validate the results, + * they must go through a filtering step at the end. + * Suffix trees also cannot match lev and exact matches, + * so those just return empty sets. + * + * branches + * : A sorted-array map of subtrees. + * + * dataLen + * : The length of the substring used by this node. + * + * leaves_suffix + * : The IDs of every entry that matches. Levenshtein matches + * won't include these. + * + * @type {{ + * branches: SearchTreeBranches<SearchTree>, + * dataLen: number, + * leaves_suffix: RoaringBitmap, + * }} + */ + class SuffixSearchTree { + /** + * @param {SearchTreeBranches<SearchTree>} branches + * @param {number} dataLen + * @param {RoaringBitmap} leaves_suffix + */ + constructor( + branches, + dataLen, + leaves_suffix, + ) { + this.branches = branches; + this.dataLen = dataLen; + this.leaves_suffix = leaves_suffix; + } + /** + * Returns the Trie for the root node. + * + * A Trie pointer refers to a single node in a logical decompressed search tree + * (the real search tree is compressed). + * + * @param {DataColumn} dataColumn + * @param {Uint8ArraySearchPattern} searchPattern + * @return {Trie} + */ + trie(dataColumn, searchPattern) { + return new SuffixTrie(this, 0, dataColumn, searchPattern); + } + + /** + * Return the trie representing `name` + * @param {Uint8Array|string} name + * @param {DataColumn} dataColumn + * @returns {Promise<Trie?>} + */ + async search(name, dataColumn) { + if (typeof name === "string") { + const utf8encoder = new TextEncoder(); + name = utf8encoder.encode(name); + } + const searchPattern = new Uint8ArraySearchPattern(name); + let trie = this.trie(dataColumn, searchPattern); + for (const datum of name) { + // code point definitely exists + const newTrie = trie.child(datum); + if (newTrie) { + trie = await newTrie; + } else { + return null; + } + } + return trie; + } + + /** + * @param {Uint8Array|string} _name + * @param {DataColumn} _dataColumn + * @returns {AsyncGenerator<Trie>} + */ + async* searchLev(_name, _dataColumn) { + // this function only returns whole-string matches, + // which pure-suffix nodes don't have, so is + // intentionally blank + } + + /** @returns {RoaringBitmap} */ + getCurrentLeaves() { + return this.leaves_suffix; + } + } + + /** + * A representation of a set of strings in the search index, + * as a subset of the entire tree (suffix-only). + */ + class SuffixTrie { + /** + * @param {SuffixSearchTree} tree + * @param {number} offset + * @param {DataColumn} dataColumn + * @param {Uint8ArraySearchPattern} searchPattern + */ + constructor(tree, offset, dataColumn, searchPattern) { + this.tree = tree; + this.offset = offset; + this.dataColumn = dataColumn; + this.searchPattern = searchPattern; + } + + /** + * All exact matches for the string represented by this node. + * Since pure-suffix nodes have no exactly-matching children, + * this function returns the empty bitmap. + * @returns {RoaringBitmap} + */ + matches() { + return EMPTY_BITMAP; + } + + /** + * All matches for strings that contain the string represented by this node. + * @returns {AsyncGenerator<RoaringBitmap>} + */ + async* substringMatches() { + /** @type {Promise<SearchTree>[]} */ + let layer = [Promise.resolve(this.tree)]; + while (layer.length) { + const current_layer = layer; + layer = []; + for await (const tree of current_layer) { + /** @type {number[]?} */ + let rejected = null; + let leaves = tree.getCurrentLeaves(); + for (const leaf of leaves.entries()) { + const haystack = await this.dataColumn.at(leaf); + if (haystack === undefined || !this.searchPattern.matches(haystack)) { + if (!rejected) { + rejected = []; + } + rejected.push(leaf); + } + } + if (rejected) { + if (leaves.cardinality() !== rejected.length) { + for (const rej of rejected) { + leaves = leaves.remove(rej); + } + yield leaves; + } + } else { + yield leaves; + } + } + /** @type {HashTable<[number, SearchTree][]>} */ + const subnodes = new HashTable(); + for await (const node of current_layer) { + const branches = node.branches; + const l = branches.subtrees.length; + for (let i = 0; i < l; ++i) { + const subtree = branches.subtrees[i]; + if (subtree) { + layer.push(subtree); + } else if (subtree === null) { + const newnode = branches.getNodeID(i); + if (!newnode) { + throw new Error(`malformed tree; no node for index ${i}`); + } else { + let subnode_list = subnodes.get(newnode); + if (!subnode_list) { + subnode_list = [[i, node]]; + subnodes.set(newnode, subnode_list); + } else { + subnode_list.push([i, node]); + } + } + } else { + throw new Error(`malformed tree; index ${i} does not exist`); + } + } + } + for (const [newnode, subnode_list] of subnodes.entries()) { + const res = registry.searchTreeLoadByNodeID(newnode); + for (const [i, node] of subnode_list) { + const branches = node.branches; + branches.subtrees[i] = res; + } + layer.push(res); + } + } + } + + /** + * All matches for strings that start with the string represented by this node. + * Since this is a pure-suffix node, there aren't any. + * @returns {AsyncGenerator<RoaringBitmap>} + */ + async* prefixMatches() { + // this function only returns prefix matches, + // which pure-suffix nodes don't have, so is + // intentionally blank + } + + /** + * Returns all keys that are children of this node. + * @returns {Uint8Array} + */ + keysExcludeSuffixOnly() { + return EMPTY_UINT8; + } + + /** + * Returns all nodes that are direct children of this node. + * @returns {[number, Promise<Trie>][]} + */ + childrenExcludeSuffixOnly() { + return []; + } + + /** + * Returns a single node that is a direct child of this node. + * @param {number} byte + * @returns {Promise<Trie>?} + */ + child(byte) { + if (this.offset === this.tree.dataLen) { + const i = this.tree.branches.getIndex(byte); + if (i !== -1) { + /** @type {Promise<SearchTree>?} */ + let branch = this.tree.branches.subtrees[i]; + if (branch === null) { + const newnode = this.tree.branches.getNodeID(i); + if (!newnode) { + throw new Error(`malformed tree; no node for key ${byte}`); + } + branch = registry.searchTreeLoadByNodeID(newnode); + this.tree.branches.subtrees[i] = branch; + } + return branch.then(branch => branch.trie(this.dataColumn, this.searchPattern)); + } + } else { + return Promise.resolve(new SuffixTrie( + this.tree, + this.offset + 1, + this.dataColumn, + this.searchPattern, + )); } return null; } @@ -1827,8 +2254,10 @@ function loadDatabase(hooks) { * @param {Uint8Array} hashes * @param {RoaringBitmap} emptyset * @param {string} name + * @param {SearchTree?} searchTree */ - constructor(counts, hashes, emptyset, name) { + constructor(counts, hashes, emptyset, name, searchTree) { + this.searchTree = searchTree; this.hashes = hashes; this.emptyset = emptyset; this.name = name; @@ -1883,7 +2312,7 @@ function loadDatabase(hooks) { const {hash, end} = this.buckets[idx]; let data = this.buckets[idx].data; if (data === null) { - const dataSansEmptyset = await registry.dataLoadByNameAndHash( + const dataSansEmptysetOrig = await registry.dataLoadByNameAndHash( this.name, hash, ); @@ -1893,6 +2322,7 @@ function loadDatabase(hooks) { if (data !== null) { return (await data)[id - start]; } + const dataSansEmptyset = [...dataSansEmptysetOrig]; /** @type {(Uint8Array[])|null} */ let dataWithEmptyset = null; let pos = start; @@ -1924,6 +2354,24 @@ function loadDatabase(hooks) { } } } + /** + * Search by exact substring + * @param {Uint8Array|string} name + * @returns {Promise<Trie?>} + */ + async search(name) { + return await (this.searchTree ? this.searchTree.search(name, this) : null); + } + /** + * Search by whole, inexact match + * @param {Uint8Array|string} name + * @returns {AsyncGenerator<Trie>} + */ + async *searchLev(name) { + if (this.searchTree) { + yield* this.searchTree.searchLev(name, this); + } + } } class Database { @@ -2015,7 +2463,7 @@ function loadDatabase(hooks) { const data_history = []; let canonical = EMPTY_UINT8; /** @type {SearchTree} */ - let tree = new SearchTree( + let tree = new PrefixSearchTree( EMPTY_SEARCH_TREE_BRANCHES, EMPTY_SEARCH_TREE_BRANCHES, EMPTY_UINT8, @@ -2029,8 +2477,8 @@ function loadDatabase(hooks) { * @returns {{ * "cpbranches": Uint8Array, * "csbranches": Uint8Array, - * "might_have_prefix_branches": SearchTreeBranches, - * "branches": SearchTreeBranches, + * "might_have_prefix_branches": SearchTreeBranches<SearchTree>, + * "branches": SearchTreeBranches<SearchTree>, * "cpnodes": Uint8Array, * "csnodes": Uint8Array, * "consumed_len_bytes": number, @@ -2051,6 +2499,12 @@ function loadDatabase(hooks) { const start_point = i; let cplen; let cslen; + /** + * @type {( + * typeof SearchTreeBranches<SearchTree> & + * {"ALPHABITMAP_CHARS": number[], "width": number} + * )?} + */ let alphabitmap = null; if (is_pure_suffixes_only_node) { cplen = 0; @@ -2299,6 +2753,8 @@ function loadDatabase(hooks) { if (is_data_compressed) { data = data_history[data_history.length - dlen - 1]; dlen = data.length; + } else if (is_pure_suffixes_only_node) { + data = EMPTY_UINT8; } else { data = dlen === 0 ? EMPTY_UINT8 : @@ -2319,80 +2775,109 @@ function loadDatabase(hooks) { let whole; let suffix; if (is_pure_suffixes_only_node) { - whole = EMPTY_BITMAP; suffix = input[i] === 0 ? EMPTY_BITMAP1 : new RoaringBitmap(input, i); i += suffix.consumed_len_bytes; - } else if (input[i] === 0xff) { - whole = EMPTY_BITMAP; - suffix = EMPTY_BITMAP1; - i += 1; + tree = new SuffixSearchTree( + branches, + dlen, + suffix, + ); + const clen = ( + 3 + // lengths of children and data + csnodes.length + + csbranches.length + + suffix.consumed_len_bytes + ); + if (canonical.length < clen) { + canonical = new Uint8Array(clen); + } + let ci = 0; + canonical[ci] = 1; + ci += 1; + canonical[ci] = dlen; + ci += 1; + canonical[ci] = input[coffset]; // suffix child count + ci += 1; + canonical.set(csnodes, ci); + ci += csnodes.length; + canonical.set(csbranches, ci); + ci += csbranches.length; + const leavesOffset = i - suffix.consumed_len_bytes; + for (let j = leavesOffset; j < i; j += 1) { + canonical[ci + j - leavesOffset] = input[j]; + } + siphashOfBytes(canonical.subarray(0, clen), 0, 0, 0, 0, hash); } else { - whole = input[i] === 0 ? - EMPTY_BITMAP1 : - new RoaringBitmap(input, i); - i += whole.consumed_len_bytes; - suffix = input[i] === 0 ? - EMPTY_BITMAP1 : - new RoaringBitmap(input, i); - i += suffix.consumed_len_bytes; - } - tree = new SearchTree( - branches, - might_have_prefix_branches, - data, - whole, - suffix, - ); - const clen = ( - (is_pure_suffixes_only_node ? 3 : 4) + // lengths of children and data - dlen + - cpnodes.length + csnodes.length + - cpbranches.length + csbranches.length + - whole.consumed_len_bytes + - suffix.consumed_len_bytes - ); - if (canonical.length < clen) { - canonical = new Uint8Array(clen); - } - let ci = 0; - canonical[ci] = is_pure_suffixes_only_node ? 1 : 0; - ci += 1; - canonical[ci] = dlen; - ci += 1; - canonical.set(data, ci); - ci += dlen; - canonical[ci] = input[coffset]; - ci += 1; - if (!is_pure_suffixes_only_node) { - canonical[ci] = input[coffset + 1]; + if (input[i] === 0xff) { + whole = EMPTY_BITMAP; + suffix = EMPTY_BITMAP1; + i += 1; + } else { + whole = input[i] === 0 ? + EMPTY_BITMAP1 : + new RoaringBitmap(input, i); + i += whole.consumed_len_bytes; + suffix = input[i] === 0 ? + EMPTY_BITMAP1 : + new RoaringBitmap(input, i); + i += suffix.consumed_len_bytes; + } + tree = new PrefixSearchTree( + branches, + might_have_prefix_branches, + data, + whole, + suffix, + ); + const clen = ( + 4 + // lengths of children and data + dlen + + cpnodes.length + csnodes.length + + cpbranches.length + csbranches.length + + whole.consumed_len_bytes + + suffix.consumed_len_bytes + ); + if (canonical.length < clen) { + canonical = new Uint8Array(clen); + } + let ci = 0; + canonical[ci] = 0; ci += 1; + canonical[ci] = dlen; + ci += 1; + canonical.set(data, ci); + ci += data.length; + canonical[ci] = input[coffset]; // prefix child count + ci += 1; + canonical[ci] = input[coffset + 1]; // suffix child count + ci += 1; + canonical.set(cpnodes, ci); + ci += cpnodes.length; + canonical.set(csnodes, ci); + ci += csnodes.length; + canonical.set(cpbranches, ci); + ci += cpbranches.length; + canonical.set(csbranches, ci); + ci += csbranches.length; + const leavesOffset = i - whole.consumed_len_bytes - suffix.consumed_len_bytes; + for (let j = leavesOffset; j < i; j += 1) { + canonical[ci + j - leavesOffset] = input[j]; + } + siphashOfBytes(canonical.subarray(0, clen), 0, 0, 0, 0, hash); } - canonical.set(cpnodes, ci); - ci += cpnodes.length; - canonical.set(csnodes, ci); - ci += csnodes.length; - canonical.set(cpbranches, ci); - ci += cpbranches.length; - canonical.set(csbranches, ci); - ci += csbranches.length; - const leavesOffset = i - whole.consumed_len_bytes - suffix.consumed_len_bytes; - for (let j = leavesOffset; j < i; j += 1) { - canonical[ci + j - leavesOffset] = input[j]; - } - siphashOfBytes(canonical.subarray(0, clen), 0, 0, 0, 0, hash); hash[2] &= 0x7f; } else { // uncompressed node const dlen = input [i + 1]; i += 2; - if (dlen === 0) { + if (dlen === 0 || is_pure_suffixes_only_node) { data = EMPTY_UINT8; } else { data = new Uint8Array(input.buffer, i + input.byteOffset, dlen); + i += dlen; } - i += dlen; const { consumed_len_bytes: branches_consumed_len_bytes, branches, @@ -2427,13 +2912,19 @@ function loadDatabase(hooks) { i - start, ), 0, 0, 0, 0, hash); hash[2] &= 0x7f; - tree = new SearchTree( - branches, - might_have_prefix_branches, - data, - whole, - suffix, - ); + tree = is_pure_suffixes_only_node ? + new SuffixSearchTree( + branches, + dlen, + suffix, + ) : + new PrefixSearchTree( + branches, + might_have_prefix_branches, + data, + whole, + suffix, + ); } hash_history.push({hash: truncatedHash.slice(), used: false}); if (data.length !== 0) { @@ -2454,20 +2945,22 @@ function loadDatabase(hooks) { } j += 1; } - const tree_mhp_branch_nodeids = tree.might_have_prefix_branches.nodeids; - const tree_mhp_branch_subtrees = tree.might_have_prefix_branches.subtrees; - j = 0; - lb = tree.might_have_prefix_branches.subtrees.length; - while (j < lb) { - // node id with a 1 in its most significant bit is inlined, and, so - // it won't be in the stash - if ((tree_mhp_branch_nodeids[j * 6] & 0x80) === 0) { - const subtree = stash.getWithOffsetKey(tree_mhp_branch_nodeids, j * 6); - if (subtree !== undefined) { - tree_mhp_branch_subtrees[j] = Promise.resolve(subtree); + if (tree instanceof PrefixSearchTree) { + const tree_mhp_branch_nodeids = tree.might_have_prefix_branches.nodeids; + const tree_mhp_branch_subtrees = tree.might_have_prefix_branches.subtrees; + j = 0; + lb = tree.might_have_prefix_branches.subtrees.length; + while (j < lb) { + // node id with a 1 in its most significant bit is inlined, and, so + // it won't be in the stash + if ((tree_mhp_branch_nodeids[j * 6] & 0x80) === 0) { + const subtree = stash.getWithOffsetKey(tree_mhp_branch_nodeids, j * 6); + if (subtree !== undefined) { + tree_mhp_branch_subtrees[j] = Promise.resolve(subtree); + } } + j += 1; } - j += 1; } if (i !== l) { stash.set(truncatedHash, tree); diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index f0520716228..6fe94f9d291 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -466,7 +466,7 @@ impl FromClean<clean::GenericParamDefKind> for GenericParamDefKind { default: default.into_json(renderer), is_synthetic: *synthetic, }, - Const { ty, default, synthetic: _ } => GenericParamDefKind::Const { + Const { ty, default } => GenericParamDefKind::Const { type_: ty.into_json(renderer), default: default.as_ref().map(|x| x.as_ref().clone()), }, diff --git a/src/librustdoc/scrape_examples.rs b/src/librustdoc/scrape_examples.rs index 16034c11827..471e966e2c2 100644 --- a/src/librustdoc/scrape_examples.rs +++ b/src/librustdoc/scrape_examples.rs @@ -333,14 +333,11 @@ pub(crate) fn run( pub(crate) fn load_call_locations( with_examples: Vec<String>, dcx: DiagCtxtHandle<'_>, + loaded_paths: &mut Vec<PathBuf>, ) -> AllCallLocations { let mut all_calls: AllCallLocations = FxIndexMap::default(); for path in with_examples { - // FIXME: Figure out why this line is causing this feature to crash in specific contexts. - // Full issue backlog is available here: <https://github.com/rust-lang/rust/pull/144600>. - // - // Can be checked with `tests/run-make/rustdoc-scrape-examples-paths`. - // loaded_paths.push(path.clone().into()); + loaded_paths.push(path.clone().into()); let bytes = match fs::read(&path) { Ok(bytes) => bytes, Err(e) => dcx.fatal(format!("failed to load examples: {e}")), diff --git a/src/stage0 b/src/stage0 index 73bf5ba4b78..a705cd1c760 100644 --- a/src/stage0 +++ b/src/stage0 @@ -15,7 +15,7 @@ nightly_branch=master compiler_date=2025-08-05 compiler_version=beta -rustfmt_date=2025-08-06 +rustfmt_date=2025-09-05 rustfmt_version=nightly dist/2025-08-05/rustc-beta-aarch64-apple-darwin.tar.gz=b9d8f74da46aeadb6c650a4ccfc3c2de08e229e4211a198fa2914103f09f579d @@ -396,123 +396,123 @@ dist/2025-08-05/clippy-beta-x86_64-unknown-linux-musl.tar.gz=79fd42cffac98024308 dist/2025-08-05/clippy-beta-x86_64-unknown-linux-musl.tar.xz=a3a616554ed25630df9f8cef37a5d36573e6f722b1f6b4220ff4885e2d3a60de dist/2025-08-05/clippy-beta-x86_64-unknown-netbsd.tar.gz=ad1849cb72ccfd52ba17a34d90f65226726e5044f7ffbe975c74f23643d87d98 dist/2025-08-05/clippy-beta-x86_64-unknown-netbsd.tar.xz=608001728598164e234f176d0c6cfe7317dde27fb4cbb8ad1c2452289e83bf30 -dist/2025-08-06/rustfmt-nightly-aarch64-apple-darwin.tar.gz=b33b3bd26dd4518802b325e7c151ea73fa6844bc710ac2d2c8fb55a27c5d7e2a -dist/2025-08-06/rustfmt-nightly-aarch64-apple-darwin.tar.xz=c900e816a94a0ddd7701c64051ba0e0244c5799e77189e26df109649b10f636f -dist/2025-08-06/rustfmt-nightly-aarch64-pc-windows-gnullvm.tar.gz=aa7c344a52c69ee9b9e68007da806f5741688ce3917c377b6563043703e7d867 -dist/2025-08-06/rustfmt-nightly-aarch64-pc-windows-gnullvm.tar.xz=d76d7c6ca6cd5286bee12cabec166a3d8848b28a484a105b56bd5687674bc4c6 -dist/2025-08-06/rustfmt-nightly-aarch64-pc-windows-msvc.tar.gz=ea4d7c94a12485958b15e7a2782f9b8a09e065c021acd2894e7a905cadfa7489 -dist/2025-08-06/rustfmt-nightly-aarch64-pc-windows-msvc.tar.xz=4a85be8477cf4b23971fedf3b06b404cf2231f8fe941630e3e73dc902454b96e -dist/2025-08-06/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.gz=385625349ab2e79887e2630fc861dcdd039190af36748df27500f7ea45a515f9 -dist/2025-08-06/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.xz=50cec6b681ae63ebcd60ce6cb6d328f49939c3cac1aa6b71ce76a5a3902cb52c -dist/2025-08-06/rustfmt-nightly-aarch64-unknown-linux-musl.tar.gz=09a141bf2e457e91abf688c8084654e5e0b932418e351da2c0daf6cf89b77e56 -dist/2025-08-06/rustfmt-nightly-aarch64-unknown-linux-musl.tar.xz=7aa6d3bd020e903c842725bb3ec9dba1881f2e9cd748953c23c658ef93d49dce -dist/2025-08-06/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.gz=c00a4b1ad60af59609e3bc61f042f9c4152de26687dcd120da173d61264b88ab -dist/2025-08-06/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.xz=fcca5576b2f0bdab5bf7dd7ecb6ea956285aa3c129a1ea9facaf517f09f20c2d -dist/2025-08-06/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.gz=434d9bff2e5693db416701730af1a71767715e98d674cf7273b32f76128ccab1 -dist/2025-08-06/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.xz=af48289fe813a45eb78373d80f01f060ebd2ce6337763d1ee8ea6f5080cb4eb1 -dist/2025-08-06/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.gz=68b2704b80c8f0706dc4bdba4db3b620afecbe086a7d858749d84d56f130979b -dist/2025-08-06/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.xz=930b287659104dbf4fe759d450a4126b7a0f855fbe8fd7d3ab11aadb18ceccfa -dist/2025-08-06/rustfmt-nightly-i686-pc-windows-gnu.tar.gz=7580ce2af926de7e07ad2313ae6397a624b10532556f7f9b9cee8ecd5addc697 -dist/2025-08-06/rustfmt-nightly-i686-pc-windows-gnu.tar.xz=ed19716bdb95eb04e64299c04725c7a0b92c66121856ab891bb67a797c8b1087 -dist/2025-08-06/rustfmt-nightly-i686-pc-windows-msvc.tar.gz=1d4077c7b42aabe2b80a19f3c96b457901f81667d62a5be9df617543245f0d8c -dist/2025-08-06/rustfmt-nightly-i686-pc-windows-msvc.tar.xz=e0203ce7cea0c2125d8d39d92c5137b2397115c4d99f42d0b23746d0270c78d2 -dist/2025-08-06/rustfmt-nightly-i686-unknown-linux-gnu.tar.gz=0a836d9d2fc5824aeb4afd5eba709c0e3c6d4403ca8815356a0ac8735cbc95dc -dist/2025-08-06/rustfmt-nightly-i686-unknown-linux-gnu.tar.xz=f656cb9adff0aa53f47f15a937556e9b1a9418f33dff39350e48ab8c02da987a -dist/2025-08-06/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.gz=c44a884292ffeba027d3d029d6d53ccaa2bb85787e825c9cc1dca464046a7855 -dist/2025-08-06/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.xz=6992586e7dc3b95ddaa630cb61f58ec88b5d7c488aed8476be25a92d0fb4a3dd -dist/2025-08-06/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.gz=6e053aee8964a26385292dc2e9b671995045bb736447059e536b1e7f09c79685 -dist/2025-08-06/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.xz=611d609f23f75a2b7fe304737abaf017fcf4594d9d11cbe014e91a90ee5ab27f -dist/2025-08-06/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.gz=932f888a1a604358ad40d62bc0ca9a484f47715e8a5306fe77ae610ada4671ce -dist/2025-08-06/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.xz=4eae3ed539b4ff8b39dae4ea4089081fa49807d31640129eec29b47030e8101e -dist/2025-08-06/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.gz=13c65d2427c063961c19b20aa4f25bb11b3189084e2f0a798a846b95c630735b -dist/2025-08-06/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.xz=2f8e8ec9a4c0187174a0d8d3c2addf75e68a2d60920ae100f37efb5e57a45033 -dist/2025-08-06/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.gz=e11b21d9aa403a1202c299390210d11d403501f6f55cc7ec24d6118461545825 -dist/2025-08-06/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.xz=ece7d348df665d6ff6d061e911c6bb4c9d4f161e63e888d3a66f17b533843886 -dist/2025-08-06/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.gz=d87c2370e2346587d71994ba31a0455932760507fcce680ab39322619179c429 -dist/2025-08-06/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.xz=030355ceaa2697d8b2d2de41b56cc4ef50124aa81b2f226cf0d56a1e011e0fa6 -dist/2025-08-06/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.gz=d725272b6b92059afd637a3edfc6dc2b22013bf4e70a0ed1f7c5aad3c6a71fca -dist/2025-08-06/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.xz=f0c3e7de4e92f696585bd6a85c30fab337548e4393c3ec5b52832f3533acb063 -dist/2025-08-06/rustfmt-nightly-s390x-unknown-linux-gnu.tar.gz=79f6a3429c2ee29e1350bfce20f7f5ca1e5ec46720194cf3f34977fe446c79cd -dist/2025-08-06/rustfmt-nightly-s390x-unknown-linux-gnu.tar.xz=777568cc6df1ce1b96f291027a5024109c189e83e2e2c046a3248e3a1cbedcc6 -dist/2025-08-06/rustfmt-nightly-sparcv9-sun-solaris.tar.gz=2528c1e6256bd9e73174b435802702bbe66eaaa4cff3748d3e5b7580ca7bc789 -dist/2025-08-06/rustfmt-nightly-sparcv9-sun-solaris.tar.xz=2f62d01498b57395e03ddfffc4e0b5cdf78a7fb6f5308440b8eee24637874752 -dist/2025-08-06/rustfmt-nightly-x86_64-apple-darwin.tar.gz=a9b2f612748bc7a88eced4462999c67eeba0ea46f4a73977513c34c09286b0a3 -dist/2025-08-06/rustfmt-nightly-x86_64-apple-darwin.tar.xz=a8137526bc41ab13a624aa5c9895abcc250f38776aeb690a7939baab1093f388 -dist/2025-08-06/rustfmt-nightly-x86_64-pc-solaris.tar.gz=18015624ba6cc1892d944d6f3c82e0b22fc5ff85ac074d25ad80fb4af12f0172 -dist/2025-08-06/rustfmt-nightly-x86_64-pc-solaris.tar.xz=bc0f5d324b4b807b0a378aa0e132a429c57c4773c6d9d30e9215ba835de1a0a5 -dist/2025-08-06/rustfmt-nightly-x86_64-pc-windows-gnu.tar.gz=de159bd0f81d54906d8020b3da80ab3319292907c3f0c9c68654f8e6ed900135 -dist/2025-08-06/rustfmt-nightly-x86_64-pc-windows-gnu.tar.xz=6dd48c3a64fcc0f2f99e4cc643cf82f1a7efab1921918fd37953c64125982606 -dist/2025-08-06/rustfmt-nightly-x86_64-pc-windows-gnullvm.tar.gz=8ba8c7ce8715fb378758ae8bd6c1538e538c2dfe86771db43b7903f18a384ad3 -dist/2025-08-06/rustfmt-nightly-x86_64-pc-windows-gnullvm.tar.xz=4259f65663aea76e1397c677bc286588cd4b6f8694647c738efc55e5616bc084 -dist/2025-08-06/rustfmt-nightly-x86_64-pc-windows-msvc.tar.gz=bfd3e1434e5b816d9d338e6c56203f58b5c1a89499ca03099d4d86b3c665f0d1 -dist/2025-08-06/rustfmt-nightly-x86_64-pc-windows-msvc.tar.xz=6260a7487d7d3759aea9e6691ac77ee38948a538f8b973459f444f98912753e0 -dist/2025-08-06/rustfmt-nightly-x86_64-unknown-freebsd.tar.gz=c10b36a6ac99d8e1de73c3ae0ca74ef10600baf7a3963f6885282b97f703f992 -dist/2025-08-06/rustfmt-nightly-x86_64-unknown-freebsd.tar.xz=b8de069c788f4a6c195d7a429ed656a45ea2a9a5775138de46d79c1c3591e70e -dist/2025-08-06/rustfmt-nightly-x86_64-unknown-illumos.tar.gz=b0f0d9331d213015493e080f8cd06e6ed955693981241acd692a92dd545df66f -dist/2025-08-06/rustfmt-nightly-x86_64-unknown-illumos.tar.xz=91ed6b2792622c68a5cc3698650860373305a39162b2b643b1c219cab6debbba -dist/2025-08-06/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.gz=b1920a45d2532f41956b2c1112cf8c590243b1417bc48f3922056e1d62a27a1d -dist/2025-08-06/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.xz=1825ed54530deb070d6f894cc12ca157be57a37e6d7ccc6007a51674eae42082 -dist/2025-08-06/rustfmt-nightly-x86_64-unknown-linux-musl.tar.gz=ac01818b1cc9130b4dcdad9db8137382655d21df9d6e7edc5cd0f84c71bbe787 -dist/2025-08-06/rustfmt-nightly-x86_64-unknown-linux-musl.tar.xz=21ee5aeb048e5337b3231b4f3ca8a6bfacc3a1cc23800052093d360ca22b3b78 -dist/2025-08-06/rustfmt-nightly-x86_64-unknown-netbsd.tar.gz=020995d3e24b4e72b0166662fd2e6969f397baa728999bbd09bce76e5381e0b5 -dist/2025-08-06/rustfmt-nightly-x86_64-unknown-netbsd.tar.xz=4a09cad2bd24ae6a15c83fb9098f66208219d3ab14c9fd46a3ce7a3c8efe6119 -dist/2025-08-06/rustc-nightly-aarch64-apple-darwin.tar.gz=25e5461350634cbd4b38dce96bc6ba6a9fbdb87caed51f26dfa058105bbccb4a -dist/2025-08-06/rustc-nightly-aarch64-apple-darwin.tar.xz=e8774ab1e12ba4d3d088dfb0c6a929b3fb6866db035eb10a60b203a6af59f490 -dist/2025-08-06/rustc-nightly-aarch64-pc-windows-gnullvm.tar.gz=0094366fa4b8f33c7ec5bb75fa5116044c76f3b7bf4a96b5a48b31dbf6f080eb -dist/2025-08-06/rustc-nightly-aarch64-pc-windows-gnullvm.tar.xz=6686cbf9182560d9b691ac56b3e89ed76c4fe6340dd036820a1a126e2ff41689 -dist/2025-08-06/rustc-nightly-aarch64-pc-windows-msvc.tar.gz=d3e23b1b5ac10b0cc406eb5397da7fff30aa4e558832379debf4410551ebdc4d -dist/2025-08-06/rustc-nightly-aarch64-pc-windows-msvc.tar.xz=fd2771191a84469c6b1fcef7e90388f4699ef087e082c9838ca2bb2d1d50b399 -dist/2025-08-06/rustc-nightly-aarch64-unknown-linux-gnu.tar.gz=d4848e6a71e9cb6b87e46843f61da911ae81266f5c3aca10f85120d069d304bb -dist/2025-08-06/rustc-nightly-aarch64-unknown-linux-gnu.tar.xz=b1e9c97f2b1c06ebf1dc00cf5fa23191a86e3ef234ebaeeced2bd41b3e59b656 -dist/2025-08-06/rustc-nightly-aarch64-unknown-linux-musl.tar.gz=4d68bc5ecf31127f67d1bb2359e9ca475ea7a0c396da29b00781929f1f00ba6c -dist/2025-08-06/rustc-nightly-aarch64-unknown-linux-musl.tar.xz=8588747d7ce6c5a9b68ba6805b63449695e60d2e9ea7f3f0ad5f4e5a04d9062c -dist/2025-08-06/rustc-nightly-arm-unknown-linux-gnueabi.tar.gz=4ebb3bd5e06737d2b61f249a674cda32a2a58e871039c37d49c2d6c8d2ecd6d9 -dist/2025-08-06/rustc-nightly-arm-unknown-linux-gnueabi.tar.xz=5740de96cfbdcc5261e0df10e9c7ec63e28bd553f9691a8e26e6fe9f76cbbee3 -dist/2025-08-06/rustc-nightly-arm-unknown-linux-gnueabihf.tar.gz=a7f5f4a9b5c0b6d2cf2f86feac062018e044cfbd05c9cc8243e2ff44d8196745 -dist/2025-08-06/rustc-nightly-arm-unknown-linux-gnueabihf.tar.xz=a1f54aab1b4a5ebc09d9b57c892072dfd8d5280fe7a0f3dd3f643ffc72992d34 -dist/2025-08-06/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.gz=cac4ed0ff044997a35471f6c291bb165b48cdcf3f0a0a4df24d7c91cbe121362 -dist/2025-08-06/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.xz=47e6083275169ef75dde6daae2bb9ca599ea03ce56d31945e9e0b42d09ebf364 -dist/2025-08-06/rustc-nightly-i686-pc-windows-gnu.tar.gz=95c87a447d33748fbfa1e9136dfb290814a7dfb1a28efc45f14f0c7830ec49bb -dist/2025-08-06/rustc-nightly-i686-pc-windows-gnu.tar.xz=52929264f954609165aca5d925d6a1f0e41f78647b45edcd8a7c8c3f872c4bd7 -dist/2025-08-06/rustc-nightly-i686-pc-windows-msvc.tar.gz=f8db463c7c5aeb7fda6d0aa2e4d460d42a7fca5ec8b9b4d8a97e4f988c72a1bd -dist/2025-08-06/rustc-nightly-i686-pc-windows-msvc.tar.xz=b33e23dfc673dda3eaa81d3c2d690e68cbc95bfb09864c8b2307029b0509c4f0 -dist/2025-08-06/rustc-nightly-i686-unknown-linux-gnu.tar.gz=f4f243206778a3997e3ab63170182a6e210cedd47c2a17358146e3e2cb892912 -dist/2025-08-06/rustc-nightly-i686-unknown-linux-gnu.tar.xz=194cda3450befacd2da23bccf836995f09a85e7e9c6d4ba5613cbb3c1768815f -dist/2025-08-06/rustc-nightly-loongarch64-unknown-linux-gnu.tar.gz=f189ae4ab931c61eef04b5748b696070b998699315629a3b95d4f0b4cdae9ff9 -dist/2025-08-06/rustc-nightly-loongarch64-unknown-linux-gnu.tar.xz=23c82bf0d9f5532d94a24cfaeec66fc5f02e1b28c854e5689babd41ebcfc0ad2 -dist/2025-08-06/rustc-nightly-loongarch64-unknown-linux-musl.tar.gz=3e76a7201dd24f9d79589445509f0c80e6902864731f71d1da3f5343a75aa5bc -dist/2025-08-06/rustc-nightly-loongarch64-unknown-linux-musl.tar.xz=3a62df3e5497fee4e56bc428db422990c7df924f72dacb6897b3783560a967c8 -dist/2025-08-06/rustc-nightly-powerpc-unknown-linux-gnu.tar.gz=cc5c126bb1ad662dc78a3b4b039e6cd32445b45b163886180fda199cbfdae9df -dist/2025-08-06/rustc-nightly-powerpc-unknown-linux-gnu.tar.xz=1c9ecfef304fb901ec6f2cfd4e170d1f02511bb2a84de664718c20c7d154e313 -dist/2025-08-06/rustc-nightly-powerpc64-unknown-linux-gnu.tar.gz=76b6c1025d9dc045bfdfb5e0ca8e9cde674d0c40bb67f600ecb0e765b9f71f03 -dist/2025-08-06/rustc-nightly-powerpc64-unknown-linux-gnu.tar.xz=bf08f2bbd2197919acfcdf8db8130882ec8f77cb41371729072e7e94cc54997b -dist/2025-08-06/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.gz=7e5e5816a18c3b22a5a5cd0814206c3cb53c7fa2ce3d36c9c045e1fac87b0747 -dist/2025-08-06/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.xz=6470b0bbe1a82064de13bd9540acbd2368421e8b000cfd8af05886a48cf11d2c -dist/2025-08-06/rustc-nightly-powerpc64le-unknown-linux-musl.tar.gz=1a2cfbf02e55995793bdd9cf3809ace31a3d9900667ec6d5e71c6c63af26f578 -dist/2025-08-06/rustc-nightly-powerpc64le-unknown-linux-musl.tar.xz=7ea6594b669fa77481ff8ec4e8cf447c11e021a412584e1e60f0baed0976316e -dist/2025-08-06/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.gz=5cbd5821ad528d90f191a17d019b71ac2beea413b00ceec78aef9433832b2469 -dist/2025-08-06/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.xz=86002592eb275d582ec8656d903ddd2247a0fcfdb2c6d90f337735a574debb9a -dist/2025-08-06/rustc-nightly-s390x-unknown-linux-gnu.tar.gz=1c420af2bb6af11b2a718ff02d96794e9916a7f538d622b418f39ecd451e1fd6 -dist/2025-08-06/rustc-nightly-s390x-unknown-linux-gnu.tar.xz=1c33f80fbc3284d8499ec03bddb5bd9ebbe7e0da1aac6675d8442a3443440c3c -dist/2025-08-06/rustc-nightly-sparcv9-sun-solaris.tar.gz=366a9097aaf91fd8b2853b2f94d82b8baf162dae5ef1b514072b4da19fec87a5 -dist/2025-08-06/rustc-nightly-sparcv9-sun-solaris.tar.xz=eca0aaa16e5e7006b19e56330157545d0fb56a5e590c92e041afbc8205c35ab0 -dist/2025-08-06/rustc-nightly-x86_64-apple-darwin.tar.gz=f2665645f498cc57e0b6cb052715afd264b7bb3184be58d50f528562a1610111 -dist/2025-08-06/rustc-nightly-x86_64-apple-darwin.tar.xz=2d5dba8ed862c80035ef742d9a45c1e122228219b4e1da8fd8b920eb589bbe71 -dist/2025-08-06/rustc-nightly-x86_64-pc-solaris.tar.gz=6970602ef52246883c2df83795cca94356a1e978265facab3dd3f838707d31d3 -dist/2025-08-06/rustc-nightly-x86_64-pc-solaris.tar.xz=92e7a314326015586054cdd5044c3123f41972432b2167f0dd551ee9380d66ca -dist/2025-08-06/rustc-nightly-x86_64-pc-windows-gnu.tar.gz=5405ac07a580ba16b0f95ed0fc56a24318e8aab7abfe04c2ed3c87f9d6f1edcb -dist/2025-08-06/rustc-nightly-x86_64-pc-windows-gnu.tar.xz=10d189c68491e15b6dd34b7f47bf8c72ccb8ab7260c4d815cb51046fd24ad76c -dist/2025-08-06/rustc-nightly-x86_64-pc-windows-gnullvm.tar.gz=812a29f858412b9c256184e8e2b3d65d0c9912d7f4157a9ba640c8231caa5160 -dist/2025-08-06/rustc-nightly-x86_64-pc-windows-gnullvm.tar.xz=aa22d4547d85669a17c8a475baf533614d8d0547b1e386dc11bde66f215a874c -dist/2025-08-06/rustc-nightly-x86_64-pc-windows-msvc.tar.gz=5e60c66f60a4358c0f8b2fea5577f71f4c056c7b2d0afefd9289f782004fbc63 -dist/2025-08-06/rustc-nightly-x86_64-pc-windows-msvc.tar.xz=6d975c907e1def685c14fe6e460001af0362824f771585f9b1ccc2cc1ea71fac -dist/2025-08-06/rustc-nightly-x86_64-unknown-freebsd.tar.gz=465206e95e2c6a7731ca55db9e576f1f78e9d356ba81c6687752793101c80b92 -dist/2025-08-06/rustc-nightly-x86_64-unknown-freebsd.tar.xz=13cd73704262a891ea825201e6583c171f7a920c1be5372b4cccf5cbe3d294fc -dist/2025-08-06/rustc-nightly-x86_64-unknown-illumos.tar.gz=a7bf182bf19812d41e103a8d89e6c3457c1c9d0ddd800f59fdc3d94df587e3c3 -dist/2025-08-06/rustc-nightly-x86_64-unknown-illumos.tar.xz=43da78e7dca1415ecd756ef13fd08a8358b8deedf406d18801f36e38843a704f -dist/2025-08-06/rustc-nightly-x86_64-unknown-linux-gnu.tar.gz=ef6381475d22aff666429754e25855e97a4dc39e8d508c02a0e353df58afcaba -dist/2025-08-06/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz=238910ad25562d4f7fa6c83d92f9cad5ec3817191907958fa646251c9bcdb690 -dist/2025-08-06/rustc-nightly-x86_64-unknown-linux-musl.tar.gz=df2464c2f2c9233f2009ac73924e95b7dd395c8575874a3391fe2f3dfcfda327 -dist/2025-08-06/rustc-nightly-x86_64-unknown-linux-musl.tar.xz=99c5a9d7f0e7f9e3acde96bfd1d23a11100be81a3651e8267e6e0ffca6bc553d -dist/2025-08-06/rustc-nightly-x86_64-unknown-netbsd.tar.gz=705dd37d72d99694234af13a561dd9995a0e4c2bfd888aa451b666f49a7083a7 -dist/2025-08-06/rustc-nightly-x86_64-unknown-netbsd.tar.xz=0dc9551d63131336cd97b7bfca984970fc8e5c366b39e04a179673f7859f4c1e +dist/2025-09-05/rustfmt-nightly-aarch64-apple-darwin.tar.gz=6fd7eece7221e76c2596e0472e7311fdced87e9fab26d2a4673a3242fe779bd3 +dist/2025-09-05/rustfmt-nightly-aarch64-apple-darwin.tar.xz=1a662a55931f58be9ac05841360305f277f8b1e36f99bd0a2ee4d2cc92b7ad14 +dist/2025-09-05/rustfmt-nightly-aarch64-pc-windows-gnullvm.tar.gz=d1c3c52cf61522697d726b32ed28d7b8b4cfadf30ec57f242e8c7f9f8e09f692 +dist/2025-09-05/rustfmt-nightly-aarch64-pc-windows-gnullvm.tar.xz=2cc7cbbfa06803a2fe422ed3266f6eb519360b403c83f92259cc1b83f5ddca45 +dist/2025-09-05/rustfmt-nightly-aarch64-pc-windows-msvc.tar.gz=61f525d050d1ff4a29cc7240912d84c9c091f25195b58411af9ef176175a3200 +dist/2025-09-05/rustfmt-nightly-aarch64-pc-windows-msvc.tar.xz=504b8ace2ab7ac13be143d95ed74d94940e8706ef9f53b7778da215840337e20 +dist/2025-09-05/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.gz=ff48bd98d109310638822f5813042583900e2b70edd45fccd871c7c03dd1c2e6 +dist/2025-09-05/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.xz=b3de2bba7858e76cdafd326f3072e4c5b534ca9b679ea62caeffb07722e9a3c9 +dist/2025-09-05/rustfmt-nightly-aarch64-unknown-linux-musl.tar.gz=8ca4caedc50f09995dad7bc6e001cc863c524e28c45c186022ded589f3728709 +dist/2025-09-05/rustfmt-nightly-aarch64-unknown-linux-musl.tar.xz=8f2cfb052f9697052d89bb729d17d74583af3fa0be98f18a3c44ea518a8f4855 +dist/2025-09-05/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.gz=b12ac2a38b379bf0de4a92f29ca40e1955c45273e798edd1a72bd40253de70f1 +dist/2025-09-05/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.xz=87fb7185aa46f3810e09479dc8fafb66d13b41f4f40e9c4e866ea77fa47b1bb6 +dist/2025-09-05/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.gz=be1e8377f3d10f4f8a07ae06ab9f649f9d2fc9cfc395abaa5f0ad10a95c4fe7a +dist/2025-09-05/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.xz=60646799fdacdafec9e0ed81357b5db70f85bb1241fe775e71b8ad3feb686116 +dist/2025-09-05/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.gz=ed8cade9b846efb5ac121aa70ac188fbd2e61fa9402fe68c80b2cbd3ee32ccbd +dist/2025-09-05/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.xz=0d0bfbd9cd4123e0404fe476a6f16eec6e435ce28d328dc0dd0aad257b295d64 +dist/2025-09-05/rustfmt-nightly-i686-pc-windows-gnu.tar.gz=bd411db34707c36d5b60f14bba776b0b89b69d4266007a3b591c467a17ef053c +dist/2025-09-05/rustfmt-nightly-i686-pc-windows-gnu.tar.xz=f0f2a6a81177ae6d06ff9b8f4a5bdf3bc8b26710aaf0f09258b32f7f710722c0 +dist/2025-09-05/rustfmt-nightly-i686-pc-windows-msvc.tar.gz=b49130da444e01fe4ef997b649aada8978b8dcca60dd38cf9e7400a7c7569e1b +dist/2025-09-05/rustfmt-nightly-i686-pc-windows-msvc.tar.xz=0195cdddc74cba2bf17eaa1d53edb1a2bc0e34cdf13c7b25a34ad9729a2635b8 +dist/2025-09-05/rustfmt-nightly-i686-unknown-linux-gnu.tar.gz=1141897495ddca10fd6d9476a624b6a340fc2bfc619148e183bcf355a0078b18 +dist/2025-09-05/rustfmt-nightly-i686-unknown-linux-gnu.tar.xz=81b31bc8b3d431120005c3c8eeff3ed194dd18e56220c175c3250855cbdcddbe +dist/2025-09-05/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.gz=10a27070239e7dfcf701669c8d93ecb2d310b9fde768639a2bf5be62cd13528d +dist/2025-09-05/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.xz=0ac4a529f4f62a94d5ae4cc8a4a52f0e7d57296ac0884258dcc5478e6b0b1785 +dist/2025-09-05/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.gz=9d0ed6778fc4f0601be1e317438cf95c414fcab6d3207c635babb4f3a6faf2b0 +dist/2025-09-05/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.xz=e40b607faf2f37c9d654cc0b60c47ea155893a3b62236cd66728f68665becb18 +dist/2025-09-05/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.gz=46c029ebbfa35972b0b9e366d17c41ff8e278e01ce12634d5e3146cbf6d1a32e +dist/2025-09-05/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.xz=8d1462fd09b04a94bfb1c1841c522430b644961995bf0e19e7c8fa150628e7c7 +dist/2025-09-05/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.gz=5827684ccb4d38956e77858ddeadeaff2d92905c67093207bed0f38202268151 +dist/2025-09-05/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.xz=975c7d7beb5b66caed7d507aaec092fdf33de2308f4dc7de46fe74e5e15b5352 +dist/2025-09-05/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.gz=8829677ab0f898c98badf22dad61094cf53c6d599b2cc76142d3d792a44f3770 +dist/2025-09-05/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.xz=3d961bead4010f8a488a065ac8a4153e3c747dfcd7d5f7eeba1cad00767a7ac5 +dist/2025-09-05/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.gz=0138c30ebe74e8ee838d9eef31c7882812bb52d2304f2de7c23c47dedd6a5032 +dist/2025-09-05/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.xz=bdb4b7b3c89a30c79f51b5fa33a2a29fc8313f8193bc43ee611e8ce7d80382d2 +dist/2025-09-05/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.gz=8e440dd400bf3eb4a144318459e111069a64bb309a5a51eeb0f0383dc48ee55f +dist/2025-09-05/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.xz=f623e1d7d38d94965d7653fdf4a272630b2b6dec81662fbbe5d2573f2f3f3b8f +dist/2025-09-05/rustfmt-nightly-s390x-unknown-linux-gnu.tar.gz=f46a8278352d5a981c6746b876fe19df3093090a866d20d24cd5cb081136b1c0 +dist/2025-09-05/rustfmt-nightly-s390x-unknown-linux-gnu.tar.xz=fdf8b44c6f110a33ad7f969e651ad396c880a85545aadfee8a24ca3cbed35974 +dist/2025-09-05/rustfmt-nightly-sparcv9-sun-solaris.tar.gz=59d778dea354867d64c809b40443ca0762c685dd0e5361971daab04aa7c5a5ad +dist/2025-09-05/rustfmt-nightly-sparcv9-sun-solaris.tar.xz=18628b2888d77281fc9b2ac5636ce4ec444ab0e47bbe0e8a08238f90040c20a3 +dist/2025-09-05/rustfmt-nightly-x86_64-apple-darwin.tar.gz=169d9f2ee4a0c726040f4940370d1162502aa6568a0a442c92cad3fbc7bd95c2 +dist/2025-09-05/rustfmt-nightly-x86_64-apple-darwin.tar.xz=7f955cfa1ab07819f31cd904b0a79c67cae70090aabc7dafffdc1f3f67463248 +dist/2025-09-05/rustfmt-nightly-x86_64-pc-solaris.tar.gz=d2cc32d6be1d0f1a8654400f0418d16e789b62db3fbc0cca0d0d492615bcf6e2 +dist/2025-09-05/rustfmt-nightly-x86_64-pc-solaris.tar.xz=3dbc29c923a6a2809a8ef561d2ad375211b09dcb107bceabbf510ab0d7b471f0 +dist/2025-09-05/rustfmt-nightly-x86_64-pc-windows-gnu.tar.gz=d9b4ca2abf1062e888b31f31f517ccc6b451bd2bfdae915ec3984bc88a8be91a +dist/2025-09-05/rustfmt-nightly-x86_64-pc-windows-gnu.tar.xz=00c6d92b6e82ae58e682e72c974f2dcc865820567ba44ed008e4759dfdbdca87 +dist/2025-09-05/rustfmt-nightly-x86_64-pc-windows-gnullvm.tar.gz=e75424d0aece8d548b2c9d896291288615d08ff2a37f1c4844a70839c612e633 +dist/2025-09-05/rustfmt-nightly-x86_64-pc-windows-gnullvm.tar.xz=cce9578d9b35bd8192a26e2dc7d7f7e7d5b9f08c7d73b3f4dde08208b448b063 +dist/2025-09-05/rustfmt-nightly-x86_64-pc-windows-msvc.tar.gz=ac4282e06b0972033f974c63d2c6cbf194d4e66a1c811f443d3fa0b886868825 +dist/2025-09-05/rustfmt-nightly-x86_64-pc-windows-msvc.tar.xz=a2465b31855861d0e0eea072bb366480acf2bafdd7fdfdab79c809d8bbf8d8e4 +dist/2025-09-05/rustfmt-nightly-x86_64-unknown-freebsd.tar.gz=30c22a97066a5711f207c1919a1d74a328540da0d9d6f85a0cc044174049c927 +dist/2025-09-05/rustfmt-nightly-x86_64-unknown-freebsd.tar.xz=40987da0b665940f9c406cfbb4889dc101d73846730b0bdfa1382d051297ad08 +dist/2025-09-05/rustfmt-nightly-x86_64-unknown-illumos.tar.gz=443ba10092122fbba9854abb4aa9d2e71d8e5e65b99fae6dd572909bf50f28c5 +dist/2025-09-05/rustfmt-nightly-x86_64-unknown-illumos.tar.xz=10285942b9140efc9897965cb3a4204145e774bd1b0c2690d45d8b04498fb917 +dist/2025-09-05/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.gz=b09af4fe367df416bce622654d9e3dfb610c564f5e6d14b200d949340595673c +dist/2025-09-05/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.xz=1f9a585a017cee5a05190377cab105603f039fbefd9b4934278dc555dfca91b0 +dist/2025-09-05/rustfmt-nightly-x86_64-unknown-linux-musl.tar.gz=24f911745fcc9f120e44acccb98f54dc6406e492915410aae17efdd00e2752c3 +dist/2025-09-05/rustfmt-nightly-x86_64-unknown-linux-musl.tar.xz=48eb58ba1d58701dbff341e19fb6d446ed937cc410207b35297babafa29dd889 +dist/2025-09-05/rustfmt-nightly-x86_64-unknown-netbsd.tar.gz=c2d1cfdcd8a08bde3f9a635eaf2d6d2fbd82e4cabbbd459e3c47e64bf1581f11 +dist/2025-09-05/rustfmt-nightly-x86_64-unknown-netbsd.tar.xz=ed1775b4fd3d7d1203f8749f70328ea4ade802fa0a76c4d7ab3e2d63ca1535af +dist/2025-09-05/rustc-nightly-aarch64-apple-darwin.tar.gz=4b64c4148e5cdd6585a4200125cc394c6a998da3686ef758f37742ec2d33d573 +dist/2025-09-05/rustc-nightly-aarch64-apple-darwin.tar.xz=fd45182f9db059bdc17f2c465dbaae22589eb8e8d2d88776437a34ddca3b7153 +dist/2025-09-05/rustc-nightly-aarch64-pc-windows-gnullvm.tar.gz=c14f2d7f391492d150f2f2f91af413266649cbdbdb042fc8b660b3cb141b80c7 +dist/2025-09-05/rustc-nightly-aarch64-pc-windows-gnullvm.tar.xz=d72ea1c571fe2376ef05cfd15fb3207ee2d27c3c851f3391dfbb082c06a5bdd0 +dist/2025-09-05/rustc-nightly-aarch64-pc-windows-msvc.tar.gz=3a485d8fd8d58fdfbc1216e51414aa4d90f0c7285a99abea0fa5935d2337251b +dist/2025-09-05/rustc-nightly-aarch64-pc-windows-msvc.tar.xz=7317060a29eecd2e28d47f0ff8780150059756b07e2bc9137c3877be8af593b7 +dist/2025-09-05/rustc-nightly-aarch64-unknown-linux-gnu.tar.gz=58a53ae147de1beb0a53629898bf7c51097351271be3713a2e2344b86a4080a4 +dist/2025-09-05/rustc-nightly-aarch64-unknown-linux-gnu.tar.xz=f79d2730843dbea9e9588fd1c1b0d854441969d8f93f76021f06af2efe22b845 +dist/2025-09-05/rustc-nightly-aarch64-unknown-linux-musl.tar.gz=eddf23e28b8067021e80883faf2eb1d6d3005a6e8419a1232b84236bea286f78 +dist/2025-09-05/rustc-nightly-aarch64-unknown-linux-musl.tar.xz=4f0af1a66050f5e2d9d48b696349b9ccd420bdcfdb88b251a6655cc22a11949b +dist/2025-09-05/rustc-nightly-arm-unknown-linux-gnueabi.tar.gz=fd2f0446e3c993d746e8a5f72ccebd8b0a49172316ac1d1c58bad10596becbf3 +dist/2025-09-05/rustc-nightly-arm-unknown-linux-gnueabi.tar.xz=0b06e7ba47621819b4e59e048e5d336b3d6978e906c7363f06bbc804e49f1046 +dist/2025-09-05/rustc-nightly-arm-unknown-linux-gnueabihf.tar.gz=97d7c34b53628f28e6636fae738a18d0f1f4c60a9febddfb7145e6b91fcf3fdc +dist/2025-09-05/rustc-nightly-arm-unknown-linux-gnueabihf.tar.xz=323025215bf851024a7eb6566ad7dc5719832d81e8d46e70adaece98adc5644b +dist/2025-09-05/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.gz=3017e03222d030448ffe2805624143540197bd6d3b3e93f9f73469ace25ae4be +dist/2025-09-05/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.xz=4f6e86b185fb54f7a0b7d09a0faae7daac722351354f6abebd388efb3037dfa0 +dist/2025-09-05/rustc-nightly-i686-pc-windows-gnu.tar.gz=292c3770b96cde97072d70c58234488f955ed5582b7c3044c6de66891e73d639 +dist/2025-09-05/rustc-nightly-i686-pc-windows-gnu.tar.xz=6855d3fd9040fb4da554fd732eaf8a1c723921c35bb8a8efb31c78af69b2e4ec +dist/2025-09-05/rustc-nightly-i686-pc-windows-msvc.tar.gz=64a86a2971ed9934bbb6aaa0afc2a7f747da463afb55b51a7c5fdbdaa294e437 +dist/2025-09-05/rustc-nightly-i686-pc-windows-msvc.tar.xz=c98f1fc3e077b8c8eb3e526c416a6551c08c62e58be57b4a4c7d59670bc435f9 +dist/2025-09-05/rustc-nightly-i686-unknown-linux-gnu.tar.gz=5481c97d788899f896473380dde0877808489bc4a0ed6d98265558631fa67a57 +dist/2025-09-05/rustc-nightly-i686-unknown-linux-gnu.tar.xz=afadaae945c0b9a8b50dbdee28791e0c03c880cb22d3c20996eeb7fab94df0bf +dist/2025-09-05/rustc-nightly-loongarch64-unknown-linux-gnu.tar.gz=18b276a2464b6c5a817d72384f25442c7619cac05b2d8d0af212c0dad96ccfc6 +dist/2025-09-05/rustc-nightly-loongarch64-unknown-linux-gnu.tar.xz=b74323cd2dbee966eebe8e63e24fae026ecd900a025e9167cca0341e50333cc3 +dist/2025-09-05/rustc-nightly-loongarch64-unknown-linux-musl.tar.gz=916dc5144107d9173479b320b55b0a95d2d42156c69fbdb0f21377d825fe0892 +dist/2025-09-05/rustc-nightly-loongarch64-unknown-linux-musl.tar.xz=3a83da72aa4a6553ecd957af35a05274528dc79f87d24d8470c20b8b4478b05b +dist/2025-09-05/rustc-nightly-powerpc-unknown-linux-gnu.tar.gz=59e031b6b79a1f11406c0640e1a357f2941967ea8c034a94148d60928038e58e +dist/2025-09-05/rustc-nightly-powerpc-unknown-linux-gnu.tar.xz=53f0e33cf2651d5dc8931ec5af8f04994188d8f9a10a5c61bd72cc822df34501 +dist/2025-09-05/rustc-nightly-powerpc64-unknown-linux-gnu.tar.gz=0eec56e3b725d918cb21e494a803b2e38eb6d744f64f1a82481a4c704eb7c1f0 +dist/2025-09-05/rustc-nightly-powerpc64-unknown-linux-gnu.tar.xz=5efc97abb235f349c6cc952b4a1e4dae7d56d70b0f8b8da2a1060b85f9b734fd +dist/2025-09-05/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.gz=cb45bbcdf8841b1ac184a0aacc909f153c830e8051260e09ca4e32c1f048e2fb +dist/2025-09-05/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.xz=1c9ddddb90d45612e4fd190fb71e527b523df13146343dde200580fb2b638794 +dist/2025-09-05/rustc-nightly-powerpc64le-unknown-linux-musl.tar.gz=3a9bdd4d14e8f121d3051944ee83a901b5ca4c0921f2d01e34596fb7450b49e3 +dist/2025-09-05/rustc-nightly-powerpc64le-unknown-linux-musl.tar.xz=732b9abb8a80191884fe1ff1d4d923cc1b74c21b81e6327bc5979ae526337400 +dist/2025-09-05/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.gz=1826e74200fe286478e1659ab88ea86b43efa7b023074d00dbc814d80bebc883 +dist/2025-09-05/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.xz=c30b52d0f474fd6193bb1b3e147fb79fa8cc31e5db38444f023d84d1c2d93d12 +dist/2025-09-05/rustc-nightly-s390x-unknown-linux-gnu.tar.gz=c330067621ed25d8383f27e494346eca4d7d4866e48f331f2ec897ff1c386e56 +dist/2025-09-05/rustc-nightly-s390x-unknown-linux-gnu.tar.xz=cb4097ea582a83a94cab80ff2f36b6f7141c140d75c30b1d261a1ddd4ea45bd4 +dist/2025-09-05/rustc-nightly-sparcv9-sun-solaris.tar.gz=0af19e764f10333017a3ab66020b82c7185ad648d1230b68f10977e0affb937f +dist/2025-09-05/rustc-nightly-sparcv9-sun-solaris.tar.xz=a22cfb55cdd122dd99bf3566eabd781bb2ecded90c71a41fd33b1e0588bcc39c +dist/2025-09-05/rustc-nightly-x86_64-apple-darwin.tar.gz=163a07b91e36e85c6c41368598793667414cdc6cadc980866811234539f3aec3 +dist/2025-09-05/rustc-nightly-x86_64-apple-darwin.tar.xz=76711800685b39b3b75945395682062c40efe3195f9979784bf318837e21768a +dist/2025-09-05/rustc-nightly-x86_64-pc-solaris.tar.gz=69142a6c04703c3c8309c6fdf66b25831bf9efa3ee70cc93da4b5b75f901b29a +dist/2025-09-05/rustc-nightly-x86_64-pc-solaris.tar.xz=7e535f4aa136284e4bdfd4d56891caac6844dab91e1b711fa3914a5974dfcb60 +dist/2025-09-05/rustc-nightly-x86_64-pc-windows-gnu.tar.gz=ff0ea563126ff28df297258001d9fac4dbd85788b5d27a0f5d6be75306f21139 +dist/2025-09-05/rustc-nightly-x86_64-pc-windows-gnu.tar.xz=6b66adcaa9a5332979591464242423897f59276e9e2cbeb9ab038a72e72c3a5c +dist/2025-09-05/rustc-nightly-x86_64-pc-windows-gnullvm.tar.gz=64cac5e377bc115a8f8176719575903e695337941db43cfb35546d65c0723130 +dist/2025-09-05/rustc-nightly-x86_64-pc-windows-gnullvm.tar.xz=11e2765e4b3e2296ea05ecf735cf7b5f7beb08d76d12449cfae67f88bab02819 +dist/2025-09-05/rustc-nightly-x86_64-pc-windows-msvc.tar.gz=c7d15ae7cd5af774ae70e63fec3ba8723b85fa4c4640917b3960907eedb30b39 +dist/2025-09-05/rustc-nightly-x86_64-pc-windows-msvc.tar.xz=55036567af270cdac046fb4306e515787ca6ef5073617311fac79fb87ffe9366 +dist/2025-09-05/rustc-nightly-x86_64-unknown-freebsd.tar.gz=2d24470d2bb4c4d2605c15f1b2654cc45805384babb73b1960e8aea0b8cc227d +dist/2025-09-05/rustc-nightly-x86_64-unknown-freebsd.tar.xz=fa41782cb2e22aba30d1619db1f478c0305944ceb4de1d1001f221c5c68c104e +dist/2025-09-05/rustc-nightly-x86_64-unknown-illumos.tar.gz=d0f9785f76c59f3a67a499cfff4a5639f3ae05cbc76750b867faaa60b7d67f78 +dist/2025-09-05/rustc-nightly-x86_64-unknown-illumos.tar.xz=925e473c6e31d8811879a805358cfd2e5ab8f4de836c270d02dc8403771bed3a +dist/2025-09-05/rustc-nightly-x86_64-unknown-linux-gnu.tar.gz=ebac845114b89dfe7d0efc0cfe8820902faad617ed21eb2a701d73cf7b544a85 +dist/2025-09-05/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz=2512a5462e3f46c95ed4aba4228282a357b3e0694e9db117a857196448fe7bcc +dist/2025-09-05/rustc-nightly-x86_64-unknown-linux-musl.tar.gz=b4a5d364b84464e9a92140fff50349d4942b8d970b64150a4bc6d720cc6c4a4e +dist/2025-09-05/rustc-nightly-x86_64-unknown-linux-musl.tar.xz=6c57c2edc305737530f8551d789ee79ff952f42a0d52d6f8d7d7f1ea2027cfca +dist/2025-09-05/rustc-nightly-x86_64-unknown-netbsd.tar.gz=f3192ded327875d5a27fb50c690e3fce36669e8f71c47de304dc21edad573ff9 +dist/2025-09-05/rustc-nightly-x86_64-unknown-netbsd.tar.xz=11359e4731866f6a788e5699867e45befdf1ad49ef35c0aba22af76d3f327cc3 diff --git a/src/tools/build-manifest/Cargo.toml b/src/tools/build-manifest/Cargo.toml index 05d5f21c12c..efa99f181b3 100644 --- a/src/tools/build-manifest/Cargo.toml +++ b/src/tools/build-manifest/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] toml = "0.7" serde = { version = "1.0", features = ["derive"] } -serde_json.workspace = true +serde_json = "1.0" anyhow = "1.0.32" flate2 = "1.0.26" xz2 = "0.1.7" diff --git a/src/tools/bump-stage0/Cargo.toml b/src/tools/bump-stage0/Cargo.toml index b7f3625da91..79097f2c189 100644 --- a/src/tools/bump-stage0/Cargo.toml +++ b/src/tools/bump-stage0/Cargo.toml @@ -11,4 +11,4 @@ build_helper = { path = "../../build_helper" } curl = "0.4.38" indexmap = { version = "2.0.0", features = ["serde"] } serde = { version = "1.0.125", features = ["derive"] } -toml = "0.7" +toml = "0.8.23" diff --git a/src/tools/bump-stage0/src/main.rs b/src/tools/bump-stage0/src/main.rs index 680437cce4f..faed748785f 100644 --- a/src/tools/bump-stage0/src/main.rs +++ b/src/tools/bump-stage0/src/main.rs @@ -185,7 +185,11 @@ fn fetch_manifest( format!("{}/dist/channel-rust-{}.toml", config.dist_server, channel) }; - Ok(toml::from_slice(&http_get(&url)?)?) + // FIXME: on newer `toml` (>= `0.9.*`), use `toml::from_slice`. For now, we use the most recent + // `toml` available in-tree which is `0.8.*`, so we have to do an additional dance here. + let response = http_get(&url)?; + let response = String::from_utf8(response)?; + Ok(toml::from_str(&response)?) } fn http_get(url: &str) -> Result<Vec<u8>, Error> { diff --git a/src/tools/cargo b/src/tools/cargo -Subproject a6c58d43051d01d83f55a3e61ef5f5b2b0dd6bd +Subproject 761c4658d0079d607e6d33cf0c060e61a617cad diff --git a/src/tools/clippy/.cargo/config.toml b/src/tools/clippy/.cargo/config.toml index d9c635df5dc..a09bf95e87b 100644 --- a/src/tools/clippy/.cargo/config.toml +++ b/src/tools/clippy/.cargo/config.toml @@ -23,3 +23,10 @@ split-debuginfo = "unpacked" rustflags = ["--remap-path-prefix", "=clippy_dev"] [profile.dev.package.lintcheck] rustflags = ["--remap-path-prefix", "=lintcheck"] + +# quine-mc_cluskey makes up a significant part of the runtime in dogfood +# due to the number of conditions in the clippy_lints crate +# and enabling optimizations for that specific dependency helps a bit +# without increasing total build times. +[profile.dev.package.quine-mc_cluskey] +opt-level = 3 diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index bc60b1c57f7..eb2a76a8183 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -30,6 +30,8 @@ Current stable, released 2025-08-07 * Removed superseded lints: `transmute_float_to_int`, `transmute_int_to_char`, `transmute_int_to_float`, `transmute_num_to_bytes` (now in rustc) [#14703](https://github.com/rust-lang/rust-clippy/pull/14703) +* Move [`uninlined_format_args`] to `pedantic` (from `style`, now allow-by-default) + [#15287](https://github.com/rust-lang/rust-clippy/pull/15287) ### Enhancements @@ -74,6 +76,9 @@ Current stable, released 2025-08-07 [#14719](https://github.com/rust-lang/rust-clippy/pull/14719) * [`unnecessary_to_owned`] fixed FP when map key is a reference [#14834](https://github.com/rust-lang/rust-clippy/pull/14834) +* [`swap_with_temporary`]: fix false positive leading to different semantics + being suggested, and use the right number of dereferences in suggestion + [#15172](https://github.com/rust-lang/rust-clippy/pull/15172) ### ICE Fixes @@ -6707,6 +6712,7 @@ Released 2018-09-13 [`check-inconsistent-struct-field-initializers`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-inconsistent-struct-field-initializers [`check-private-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-private-items [`cognitive-complexity-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#cognitive-complexity-threshold +[`const-literal-digits-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#const-literal-digits-threshold [`disallowed-macros`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-macros [`disallowed-methods`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-methods [`disallowed-names`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-names diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index 05590ff7b1c..c2d080cd96a 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -485,6 +485,16 @@ The maximum cognitive complexity a function can have * [`cognitive_complexity`](https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity) +## `const-literal-digits-threshold` +The minimum digits a const float literal must have to supress the `excessive_precicion` lint + +**Default Value:** `30` + +--- +**Affected lints:** +* [`excessive_precision`](https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision) + + ## `disallowed-macros` The list of disallowed macros, written as fully qualified paths. @@ -555,7 +565,7 @@ default configuration of Clippy. By default, any configuration will replace the * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`. * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list. -**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "MHz", "GHz", "THz", "AccessKit", "CoAP", "CoreFoundation", "CoreGraphics", "CoreText", "DevOps", "Direct2D", "Direct3D", "DirectWrite", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "PowerPC", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenAL", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "OpenType", "WebGL", "WebGL2", "WebGPU", "WebRTC", "WebSocket", "WebTransport", "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD", "NixOS", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` +**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "MHz", "GHz", "THz", "AccessKit", "CoAP", "CoreFoundation", "CoreGraphics", "CoreText", "DevOps", "Direct2D", "Direct3D", "DirectWrite", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "InfiniBand", "RoCE", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "PowerPC", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenAL", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "OpenType", "WebGL", "WebGL2", "WebGPU", "WebRTC", "WebSocket", "WebTransport", "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD", "NixOS", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` --- **Affected lints:** @@ -873,7 +883,6 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`needless_borrow`](https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow) * [`non_std_lazy_statics`](https://rust-lang.github.io/rust-clippy/master/index.html#non_std_lazy_statics) * [`option_as_ref_deref`](https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref) -* [`option_map_unwrap_or`](https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or) * [`ptr_as_ptr`](https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr) * [`question_mark`](https://rust-lang.github.io/rust-clippy/master/index.html#question_mark) * [`redundant_field_names`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names) @@ -881,7 +890,6 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`repeat_vec_with_capacity`](https://rust-lang.github.io/rust-clippy/master/index.html#repeat_vec_with_capacity) * [`same_item_push`](https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push) * [`seek_from_current`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_from_current) -* [`seek_rewind`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_rewind) * [`to_digit_is_some`](https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some) * [`transmute_ptr_to_ref`](https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref) * [`tuple_array_conversions`](https://rust-lang.github.io/rust-clippy/master/index.html#tuple_array_conversions) diff --git a/src/tools/clippy/clippy.toml b/src/tools/clippy/clippy.toml index 77573105d86..d9bcfd17606 100644 --- a/src/tools/clippy/clippy.toml +++ b/src/tools/clippy/clippy.toml @@ -6,12 +6,12 @@ lint-commented-code = true [[disallowed-methods]] path = "rustc_lint::context::LintContext::lint" -reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead" +reason = "this function does not add a link to our documentation; please use the `clippy_utils::diagnostics::span_lint*` functions instead" [[disallowed-methods]] path = "rustc_lint::context::LintContext::span_lint" -reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead" +reason = "this function does not add a link to our documentation; please use the `clippy_utils::diagnostics::span_lint*` functions instead" [[disallowed-methods]] path = "rustc_middle::ty::context::TyCtxt::node_span_lint" -reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead" +reason = "this function does not add a link to our documentation; please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead" diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs index 2ad3f2efcdd..2f28f6175ad 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -33,6 +33,7 @@ const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[ "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", + "InfiniBand", "RoCE", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "PowerPC", "WebAssembly", "NaN", "NaNs", @@ -569,6 +570,9 @@ define_Conf! { /// The maximum cognitive complexity a function can have #[lints(cognitive_complexity)] cognitive_complexity_threshold: u64 = 25, + /// The minimum digits a const float literal must have to supress the `excessive_precicion` lint + #[lints(excessive_precision)] + const_literal_digits_threshold: usize = 30, /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY. /// /// Use the Cognitive Complexity lint instead. @@ -775,7 +779,6 @@ define_Conf! { needless_borrow, non_std_lazy_statics, option_as_ref_deref, - option_map_unwrap_or, ptr_as_ptr, question_mark, redundant_field_names, @@ -783,7 +786,6 @@ define_Conf! { repeat_vec_with_capacity, same_item_push, seek_from_current, - seek_rewind, to_digit_is_some, transmute_ptr_to_ref, tuple_array_conversions, diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index b9e6de79cc0..70184ee2ca5 100644 --- a/src/tools/clippy/clippy_lints/Cargo.toml +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -27,6 +27,10 @@ url = "2.2" [dev-dependencies] walkdir = "2.3" +[lints.rust.unexpected_cfgs] +level = "warn" +check-cfg = ['cfg(bootstrap)'] + [package.metadata.rust-analyzer] # This crate uses #[feature(rustc_private)] rustc_private = true diff --git a/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs b/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs index 6f2a6a36a38..08253b0c499 100644 --- a/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs +++ b/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs @@ -3,10 +3,10 @@ use clippy_utils::macros::{PanicExpn, find_assert_args, root_macro_call_first_no use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{has_debug_impl, is_copy, is_type_diagnostic_item}; use clippy_utils::usage::local_used_after_expr; -use clippy_utils::{is_expr_final_block_expr, path_res, sym}; +use clippy_utils::{path_res, sym}; use rustc_errors::Applicability; use rustc_hir::def::Res; -use rustc_hir::{Expr, ExprKind}; +use rustc_hir::{Expr, ExprKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::declare_lint_pass; @@ -77,17 +77,20 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates { _ => return, }; span_lint_and_then(cx, ASSERTIONS_ON_RESULT_STATES, macro_call.span, message, |diag| { - let semicolon = if is_expr_final_block_expr(cx.tcx, e) { ";" } else { "" }; let mut app = Applicability::MachineApplicable; - diag.span_suggestion( - macro_call.span, - "replace with", - format!( - "{}.{replacement}(){semicolon}", - snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0 - ), - app, - ); + let recv = snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0; + + // `assert!` doesn't return anything, but `Result::unwrap(_err)` does, so we might need to add a + // semicolon to the suggestion to avoid leaking the type + let sugg = match cx.tcx.parent_hir_node(e.hir_id) { + // trailing expr of a block + Node::Block(..) => format!("{recv}.{replacement}();"), + // already has a trailing semicolon + Node::Stmt(..) => format!("{recv}.{replacement}()"), + // this is the last-resort option, because it's rather verbose + _ => format!("{{ {recv}.{replacement}(); }}"), + }; + diag.span_suggestion(macro_call.span, "replace with", sugg, app); }); } } diff --git a/src/tools/clippy/clippy_lints/src/async_yields_async.rs b/src/tools/clippy/clippy_lints/src/async_yields_async.rs index 013819b0da8..1a10db291cd 100644 --- a/src/tools/clippy/clippy_lints/src/async_yields_async.rs +++ b/src/tools/clippy/clippy_lints/src/async_yields_async.rs @@ -1,8 +1,12 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::source::snippet; +use clippy_utils::is_expr_async_block; +use clippy_utils::source::walk_span_to_context; +use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; use rustc_errors::Applicability; -use rustc_hir::{Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, QPath}; +use rustc_hir::{ + Block, Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, QPath, +}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -87,31 +91,37 @@ impl<'tcx> LateLintPass<'tcx> for AsyncYieldsAsync { let expr_ty = typeck_results.expr_ty(body_expr); if implements_trait(cx, expr_ty, future_trait_def_id, &[]) { - let return_expr_span = match &body_expr.kind { - // XXXkhuey there has to be a better way. - ExprKind::Block(block, _) => block.expr.map(|e| e.span), - ExprKind::Path(QPath::Resolved(_, path)) => Some(path.span), - _ => None, + let (return_expr, return_expr_span) = match &body_expr.kind { + ExprKind::Block(Block { expr: Some(e), .. }, _) => (*e, e.span), + ExprKind::Path(QPath::Resolved(_, path)) => (body_expr, path.span), + _ => return, }; - if let Some(return_expr_span) = return_expr_span { - span_lint_hir_and_then( - cx, - ASYNC_YIELDS_ASYNC, - body_expr.hir_id, - return_expr_span, - "an async construct yields a type which is itself awaitable", - |db| { - db.span_label(body_expr.span, "outer async construct"); - db.span_label(return_expr_span, "awaitable value not awaited"); - db.span_suggestion( - return_expr_span, - "consider awaiting this value", - format!("{}.await", snippet(cx, return_expr_span, "..")), - Applicability::MaybeIncorrect, - ); - }, - ); + + let return_expr_span = walk_span_to_context(return_expr_span, expr.span.ctxt()).unwrap_or(return_expr_span); + let mut applicability = Applicability::MaybeIncorrect; + let mut return_expr_snip = + Sugg::hir_with_context(cx, return_expr, expr.span.ctxt(), "..", &mut applicability); + if !is_expr_async_block(return_expr) { + return_expr_snip = return_expr_snip.maybe_paren(); } + + span_lint_hir_and_then( + cx, + ASYNC_YIELDS_ASYNC, + body_expr.hir_id, + return_expr_span, + "an async construct yields a type which is itself awaitable", + |db| { + db.span_label(body_expr.span, "outer async construct"); + db.span_label(return_expr_span, "awaitable value not awaited"); + db.span_suggestion( + return_expr_span, + "consider awaiting this value", + format!("{return_expr_snip}.await"), + applicability, + ); + }, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/bool_comparison.rs b/src/tools/clippy/clippy_lints/src/bool_comparison.rs new file mode 100644 index 00000000000..722095909a6 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/bool_comparison.rs @@ -0,0 +1,179 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::sugg::Sugg; +use clippy_utils::{is_expn_of, peel_blocks, sym}; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; +use rustc_span::Span; +use rustc_span::source_map::Spanned; + +declare_clippy_lint! { + /// ### What it does + /// Checks for expressions of the form `x == true`, + /// `x != true` and order comparisons such as `x < true` (or vice versa) and + /// suggest using the variable directly. + /// + /// ### Why is this bad? + /// Unnecessary code. + /// + /// ### Example + /// ```rust,ignore + /// if x == true {} + /// if y == false {} + /// ``` + /// use `x` directly: + /// ```rust,ignore + /// if x {} + /// if !y {} + /// ``` + #[clippy::version = "pre 1.29.0"] + pub BOOL_COMPARISON, + complexity, + "comparing a variable to a boolean, e.g., `if x == true` or `if x != true`" +} + +declare_lint_pass!(BoolComparison => [BOOL_COMPARISON]); + +impl<'tcx> LateLintPass<'tcx> for BoolComparison { + fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { + if e.span.from_expansion() { + return; + } + + if let ExprKind::Binary(Spanned { node, .. }, left_side, right_side) = e.kind + && is_expn_of(left_side.span, sym::cfg).is_none() + && is_expn_of(right_side.span, sym::cfg).is_none() + && cx.typeck_results().expr_ty(left_side).is_bool() + && cx.typeck_results().expr_ty(right_side).is_bool() + { + let ignore_case = None::<(fn(_) -> _, &str)>; + let ignore_no_literal = None::<(fn(_, _) -> _, &str)>; + match node { + BinOpKind::Eq => { + let true_case = Some((|h| h, "equality checks against true are unnecessary")); + let false_case = Some(( + |h: Sugg<'tcx>| !h, + "equality checks against false can be replaced by a negation", + )); + check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal); + }, + BinOpKind::Ne => { + let true_case = Some(( + |h: Sugg<'tcx>| !h, + "inequality checks against true can be replaced by a negation", + )); + let false_case = Some((|h| h, "inequality checks against false are unnecessary")); + check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal); + }, + BinOpKind::Lt => check_comparison( + cx, + e, + ignore_case, + Some((|h| h, "greater than checks against false are unnecessary")), + Some(( + |h: Sugg<'tcx>| !h, + "less than comparison against true can be replaced by a negation", + )), + ignore_case, + Some(( + |l: Sugg<'tcx>, r: Sugg<'tcx>| (!l).bit_and(&r), + "order comparisons between booleans can be simplified", + )), + ), + BinOpKind::Gt => check_comparison( + cx, + e, + Some(( + |h: Sugg<'tcx>| !h, + "less than comparison against true can be replaced by a negation", + )), + ignore_case, + ignore_case, + Some((|h| h, "greater than checks against false are unnecessary")), + Some(( + |l: Sugg<'tcx>, r: Sugg<'tcx>| l.bit_and(&(!r)), + "order comparisons between booleans can be simplified", + )), + ), + _ => (), + } + } + } +} + +fn check_comparison<'a, 'tcx>( + cx: &LateContext<'tcx>, + e: &'tcx Expr<'_>, + left_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>, + left_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>, + right_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>, + right_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>, + no_literal: Option<(impl FnOnce(Sugg<'a>, Sugg<'a>) -> Sugg<'a>, &'static str)>, +) { + if let ExprKind::Binary(_, left_side, right_side) = e.kind { + let mut applicability = Applicability::MachineApplicable; + // Eliminate parentheses in `e` by using the lo pos of lhs and hi pos of rhs, + // calling `source_callsite` make sure macros are handled correctly, see issue #9907 + let binop_span = left_side.span.source_callsite().to(right_side.span.source_callsite()); + + match (fetch_bool_expr(left_side), fetch_bool_expr(right_side)) { + (Some(true), None) => left_true.map_or((), |(h, m)| { + suggest_bool_comparison(cx, binop_span, right_side, applicability, m, h); + }), + (None, Some(true)) => right_true.map_or((), |(h, m)| { + suggest_bool_comparison(cx, binop_span, left_side, applicability, m, h); + }), + (Some(false), None) => left_false.map_or((), |(h, m)| { + suggest_bool_comparison(cx, binop_span, right_side, applicability, m, h); + }), + (None, Some(false)) => right_false.map_or((), |(h, m)| { + suggest_bool_comparison(cx, binop_span, left_side, applicability, m, h); + }), + (None, None) => no_literal.map_or((), |(h, m)| { + let left_side = Sugg::hir_with_context(cx, left_side, binop_span.ctxt(), "..", &mut applicability); + let right_side = Sugg::hir_with_context(cx, right_side, binop_span.ctxt(), "..", &mut applicability); + span_lint_and_sugg( + cx, + BOOL_COMPARISON, + binop_span, + m, + "try", + h(left_side, right_side).into_string(), + applicability, + ); + }), + _ => (), + } + } +} + +fn suggest_bool_comparison<'a, 'tcx>( + cx: &LateContext<'tcx>, + span: Span, + expr: &Expr<'_>, + mut app: Applicability, + message: &'static str, + conv_hint: impl FnOnce(Sugg<'a>) -> Sugg<'a>, +) { + let hint = Sugg::hir_with_context(cx, expr, span.ctxt(), "..", &mut app); + span_lint_and_sugg( + cx, + BOOL_COMPARISON, + span, + message, + "try", + conv_hint(hint).into_string(), + app, + ); +} + +fn fetch_bool_expr(expr: &Expr<'_>) -> Option<bool> { + if let ExprKind::Lit(lit_ptr) = peel_blocks(expr).kind + && let LitKind::Bool(value) = lit_ptr.node + { + return Some(value); + } + None +} diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs index a1543cabd2f..d78da9396fa 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -8,17 +8,12 @@ use rustc_middle::ty::{self, Ty}; use super::CAST_PTR_ALIGNMENT; -pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { - if let ExprKind::Cast(cast_expr, cast_to) = expr.kind { - if is_hir_ty_cfg_dependant(cx, cast_to) { - return; - } - let (cast_from, cast_to) = ( - cx.typeck_results().expr_ty(cast_expr), - cx.typeck_results().expr_ty(expr), - ); - lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); - } else if let ExprKind::MethodCall(method_path, self_arg, [], _) = &expr.kind +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, cast_from: Ty<'tcx>, cast_to: Ty<'tcx>) { + lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); +} + +pub(super) fn check_cast_method(cx: &LateContext<'_>, expr: &Expr<'_>) { + if let ExprKind::MethodCall(method_path, self_arg, [], _) = &expr.kind && method_path.ident.name == sym::cast && let Some(generic_args) = method_path.args && let [GenericArg::Type(cast_to)] = generic_args.args @@ -74,14 +69,13 @@ fn is_used_as_unaligned(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { ExprKind::Call(func, [arg, ..]) if arg.hir_id == e.hir_id => { if let ExprKind::Path(path) = &func.kind && let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id() + && let Some(name) = cx.tcx.get_diagnostic_name(def_id) && matches!( - cx.tcx.get_diagnostic_name(def_id), - Some( - sym::ptr_write_unaligned - | sym::ptr_read_unaligned - | sym::intrinsics_unaligned_volatile_load - | sym::intrinsics_unaligned_volatile_store - ) + name, + sym::ptr_write_unaligned + | sym::ptr_read_unaligned + | sym::intrinsics_unaligned_volatile_load + | sym::intrinsics_unaligned_volatile_store ) { true diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs b/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs index 46b0c88d3fe..bce7b4c69cc 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs @@ -1,10 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_context; +use clippy_utils::{get_parent_expr, is_no_std_crate}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::{self, Ty}; use rustc_span::sym; @@ -42,13 +44,52 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, let mut applicability = Applicability::MachineApplicable; let ptr = snippet_with_context(cx, ptr_arg.span, ctxt, "ptr", &mut applicability).0; let len = snippet_with_context(cx, len_arg.span, ctxt, "len", &mut applicability).0; + let krate = if is_no_std_crate(cx) { "core" } else { "std" }; span_lint_and_sugg( cx, CAST_SLICE_FROM_RAW_PARTS, span, format!("casting the result of `{func}` to {cast_to}"), "replace with", - format!("core::ptr::slice_{func}({ptr}, {len})"), + format!("{krate}::ptr::slice_{func}({ptr}, {len})"), + applicability, + ); + } +} + +/// Checks for implicit cast from slice reference to raw slice pointer. +pub(super) fn check_implicit_cast(cx: &LateContext<'_>, expr: &Expr<'_>) { + if let ExprKind::Call(fun, [ptr_arg, len_arg]) = expr.peel_blocks().kind + && let ExprKind::Path(ref qpath) = fun.kind + && let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id() + && let Some(rpk) = raw_parts_kind(cx, fun_def_id) + && !matches!(get_parent_expr(cx, expr).map(|e| e.kind), Some(ExprKind::Cast(..))) + && let [deref, borrow] = cx.typeck_results().expr_adjustments(expr) + && matches!(deref.kind, Adjust::Deref(..)) + && let Adjustment { + kind: Adjust::Borrow(AutoBorrow::RawPtr(..)), + target, + } = borrow + && let ty::RawPtr(pointee_ty, _) = target.kind() + && pointee_ty.is_slice() + && !expr.span.from_expansion() + { + let func = match rpk { + RawPartsKind::Immutable => "from_raw_parts", + RawPartsKind::Mutable => "from_raw_parts_mut", + }; + let mut applicability = Applicability::MachineApplicable; + let ctxt = expr.span.ctxt(); + let ptr = snippet_with_context(cx, ptr_arg.span, ctxt, "ptr", &mut applicability).0; + let len = snippet_with_context(cx, len_arg.span, ctxt, "len", &mut applicability).0; + let krate = if is_no_std_crate(cx) { "core" } else { "std" }; + span_lint_and_sugg( + cx, + CAST_SLICE_FROM_RAW_PARTS, + expr.span, + format!("implicitly casting the result of `{func}` to `{target}`"), + "replace_with", + format!("{krate}::ptr::slice_{func}({ptr}, {len})"), applicability, ); } diff --git a/src/tools/clippy/clippy_lints/src/casts/mod.rs b/src/tools/clippy/clippy_lints/src/casts/mod.rs index e25df9dd249..d2e62ee56e4 100644 --- a/src/tools/clippy/clippy_lints/src/casts/mod.rs +++ b/src/tools/clippy/clippy_lints/src/casts/mod.rs @@ -873,7 +873,9 @@ impl<'tcx> LateLintPass<'tcx> for Casts { } char_lit_as_u8::check(cx, expr, cast_from_expr, cast_to); cast_slice_from_raw_parts::check(cx, expr, cast_from_expr, cast_to, self.msrv); + cast_ptr_alignment::check(cx, expr, cast_from, cast_to); ptr_cast_constness::check(cx, expr, cast_from_expr, cast_from, cast_to, self.msrv); + ptr_as_ptr::check(cx, expr, cast_from_expr, cast_from, cast_to_hir, cast_to, self.msrv); as_ptr_cast_mut::check(cx, expr, cast_from_expr, cast_to); fn_to_numeric_cast_any::check(cx, expr, cast_from_expr, cast_from, cast_to); confusing_method_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to); @@ -911,8 +913,10 @@ impl<'tcx> LateLintPass<'tcx> for Casts { if self.msrv.meets(cx, msrvs::RAW_REF_OP) { borrow_as_ptr::check_implicit_cast(cx, expr); } - cast_ptr_alignment::check(cx, expr); - ptr_as_ptr::check(cx, expr, self.msrv); + if self.msrv.meets(cx, msrvs::PTR_SLICE_RAW_PARTS) { + cast_slice_from_raw_parts::check_implicit_cast(cx, expr); + } + cast_ptr_alignment::check_cast_method(cx, expr); cast_slice_different_sizes::check(cx, expr, self.msrv); ptr_cast_constness::check_null_ptr_cast_method(cx, expr); } diff --git a/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs index 89075409098..d012380cb76 100644 --- a/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs @@ -4,9 +4,9 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, Mutability, QPath, TyKind}; +use rustc_hir::{self as hir, Expr, ExprKind, QPath, TyKind}; use rustc_lint::LateContext; -use rustc_middle::ty; +use rustc_middle::ty::{self, Ty}; use rustc_span::{Span, sym}; use super::PTR_AS_PTR; @@ -26,13 +26,18 @@ impl OmitFollowedCastReason<'_> { } } -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Msrv) { - if let ExprKind::Cast(cast_expr, cast_to_hir_ty) = expr.kind - && let (cast_from, cast_to) = (cx.typeck_results().expr_ty(cast_expr), cx.typeck_results().expr_ty(expr)) - && let ty::RawPtr(_, from_mutbl) = cast_from.kind() +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &Expr<'tcx>, + cast_from_expr: &Expr<'_>, + cast_from: Ty<'_>, + cast_to_hir: &hir::Ty<'_>, + cast_to: Ty<'tcx>, + msrv: Msrv, +) { + if let ty::RawPtr(_, from_mutbl) = cast_from.kind() && let ty::RawPtr(to_pointee_ty, to_mutbl) = cast_to.kind() - && matches!((from_mutbl, to_mutbl), - (Mutability::Not, Mutability::Not) | (Mutability::Mut, Mutability::Mut)) + && from_mutbl == to_mutbl // The `U` in `pointer::cast` have to be `Sized` // as explained here: https://github.com/rust-lang/rust/issues/60602. && to_pointee_ty.is_sized(cx.tcx, cx.typing_env()) @@ -40,7 +45,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Msrv) && !is_from_proc_macro(cx, expr) { let mut app = Applicability::MachineApplicable; - let turbofish = match &cast_to_hir_ty.kind { + let turbofish = match &cast_to_hir.kind { TyKind::Infer(()) => String::new(), TyKind::Ptr(mut_ty) => { if matches!(mut_ty.ty.kind, TyKind::Infer(())) { @@ -58,16 +63,14 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Msrv) // following `cast` does not compile because it fails to infer what type is expected // as type argument to `std::ptr::ptr_null` or `std::ptr::ptr_null_mut`, so // we omit following `cast`: - let omit_cast = if let ExprKind::Call(func, []) = cast_expr.kind + let omit_cast = if let ExprKind::Call(func, []) = cast_from_expr.kind && let ExprKind::Path(ref qpath @ QPath::Resolved(None, path)) = func.kind && let Some(method_defid) = path.res.opt_def_id() { - if cx.tcx.is_diagnostic_item(sym::ptr_null, method_defid) { - OmitFollowedCastReason::Null(qpath) - } else if cx.tcx.is_diagnostic_item(sym::ptr_null_mut, method_defid) { - OmitFollowedCastReason::NullMut(qpath) - } else { - OmitFollowedCastReason::None + match cx.tcx.get_diagnostic_name(method_defid) { + Some(sym::ptr_null) => OmitFollowedCastReason::Null(qpath), + Some(sym::ptr_null_mut) => OmitFollowedCastReason::NullMut(qpath), + _ => OmitFollowedCastReason::None, } } else { OmitFollowedCastReason::None @@ -78,7 +81,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Msrv) let method = snippet_with_applicability(cx, qpath_span_without_turbofish(method), "..", &mut app); ("try call directly", format!("{method}{turbofish}()")) } else { - let cast_expr_sugg = Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app); + let cast_expr_sugg = Sugg::hir_with_context(cx, cast_from_expr, expr.span.ctxt(), "_", &mut app); ( "try `pointer::cast`, a safer alternative", diff --git a/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs b/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs index c0c0a47f855..c0f13f5e578 100644 --- a/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs +++ b/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::{std_or_core, sym}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, Mutability, QPath}; +use rustc_hir::{self as hir, Expr, ExprKind, QPath}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty, TypeVisitableExt}; @@ -12,26 +13,23 @@ use super::PTR_CAST_CONSTNESS; pub(super) fn check<'tcx>( cx: &LateContext<'_>, expr: &Expr<'_>, - cast_expr: &Expr<'_>, + cast_from_expr: &Expr<'_>, cast_from: Ty<'tcx>, cast_to: Ty<'tcx>, msrv: Msrv, ) { if let ty::RawPtr(from_ty, from_mutbl) = cast_from.kind() && let ty::RawPtr(to_ty, to_mutbl) = cast_to.kind() - && matches!( - (from_mutbl, to_mutbl), - (Mutability::Not, Mutability::Mut) | (Mutability::Mut, Mutability::Not) - ) + && from_mutbl != to_mutbl && from_ty == to_ty && !from_ty.has_erased_regions() { - if let ExprKind::Call(func, []) = cast_expr.kind + if let ExprKind::Call(func, []) = cast_from_expr.kind && let ExprKind::Path(QPath::Resolved(None, path)) = func.kind && let Some(defid) = path.res.opt_def_id() && let Some(prefix) = std_or_core(cx) && let mut app = Applicability::MachineApplicable - && let sugg = format!("{}", Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app)) + && let sugg = snippet_with_applicability(cx, cast_from_expr.span, "_", &mut app) && let Some((_, after_lt)) = sugg.split_once("::<") && let Some((source, target, target_func)) = match cx.tcx.get_diagnostic_name(defid) { Some(sym::ptr_null) => Some(("const", "mutable", "null_mut")), @@ -53,11 +51,17 @@ pub(super) fn check<'tcx>( if msrv.meets(cx, msrvs::POINTER_CAST_CONSTNESS) { let mut app = Applicability::MachineApplicable; - let sugg = Sugg::hir_with_context(cx, cast_expr, expr.span.ctxt(), "_", &mut app); - let constness = match *to_mutbl { - Mutability::Not => "const", - Mutability::Mut => "mut", + let sugg = if let ExprKind::Cast(nested_from, nested_hir_ty) = cast_from_expr.kind + && let hir::TyKind::Ptr(ptr_ty) = nested_hir_ty.kind + && let hir::TyKind::Infer(()) = ptr_ty.ty.kind + { + // `(foo as *const _).cast_mut()` fails method name resolution + // avoid this by `as`-ing the full type + Sugg::hir_with_context(cx, nested_from, expr.span.ctxt(), "_", &mut app).as_ty(cast_from) + } else { + Sugg::hir_with_context(cx, cast_from_expr, expr.span.ctxt(), "_", &mut app) }; + let constness = to_mutbl.ptr_str(); span_lint_and_sugg( cx, @@ -73,8 +77,8 @@ pub(super) fn check<'tcx>( } pub(super) fn check_null_ptr_cast_method(cx: &LateContext<'_>, expr: &Expr<'_>) { - if let ExprKind::MethodCall(method, cast_expr, [], _) = expr.kind - && let ExprKind::Call(func, []) = cast_expr.kind + if let ExprKind::MethodCall(method, cast_from_expr, [], _) = expr.kind + && let ExprKind::Call(func, []) = cast_from_expr.kind && let ExprKind::Path(QPath::Resolved(None, path)) = func.kind && let Some(defid) = path.res.opt_def_id() && let method = match (cx.tcx.get_diagnostic_name(defid), method.ident.name) { @@ -84,7 +88,7 @@ pub(super) fn check_null_ptr_cast_method(cx: &LateContext<'_>, expr: &Expr<'_>) } && let Some(prefix) = std_or_core(cx) && let mut app = Applicability::MachineApplicable - && let sugg = format!("{}", Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app)) + && let sugg = snippet_with_applicability(cx, cast_from_expr.span, "_", &mut app) && let Some((_, after_lt)) = sugg.split_once("::<") { span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs index 8f95c63a854..7646aa48b77 100644 --- a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs +++ b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs @@ -23,8 +23,8 @@ declare_clippy_lint! { /// /// ### Known problems /// The true Cognitive Complexity of a method is not something we can - /// calculate using modern technology. This lint has been left in the - /// `nursery` so as to not mislead users into using this lint as a + /// calculate using modern technology. This lint has been left in + /// `restriction` so as to not mislead users into using this lint as a /// measurement tool. /// /// For more detailed information, see [rust-clippy#3793](https://github.com/rust-lang/rust-clippy/issues/3793) diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index e67e8d9070f..d0c7443a4a4 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -35,6 +35,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::await_holding_invalid::AWAIT_HOLDING_REFCELL_REF_INFO, crate::blocks_in_conditions::BLOCKS_IN_CONDITIONS_INFO, crate::bool_assert_comparison::BOOL_ASSERT_COMPARISON_INFO, + crate::bool_comparison::BOOL_COMPARISON_INFO, crate::bool_to_int_with_if::BOOL_TO_INT_WITH_IF_INFO, crate::booleans::NONMINIMAL_BOOL_INFO, crate::booleans::OVERLY_COMPLEX_BOOL_EXPR_INFO, @@ -538,7 +539,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::mutex_atomic::MUTEX_ATOMIC_INFO, crate::mutex_atomic::MUTEX_INTEGER_INFO, crate::needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE_INFO, - crate::needless_bool::BOOL_COMPARISON_INFO, crate::needless_bool::NEEDLESS_BOOL_INFO, crate::needless_bool::NEEDLESS_BOOL_ASSIGN_INFO, crate::needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE_INFO, diff --git a/src/tools/clippy/clippy_lints/src/derivable_impls.rs b/src/tools/clippy/clippy_lints/src/derivable_impls.rs index 7580d6cab66..06c2393e0a3 100644 --- a/src/tools/clippy/clippy_lints/src/derivable_impls.rs +++ b/src/tools/clippy/clippy_lints/src/derivable_impls.rs @@ -10,7 +10,7 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, PointerCoercion}; -use rustc_middle::ty::{self, AdtDef, GenericArgsRef, Ty, TypeckResults}; +use rustc_middle::ty::{self, AdtDef, GenericArgsRef, Ty, TypeckResults, VariantDef}; use rustc_session::impl_lint_pass; use rustc_span::sym; @@ -85,6 +85,13 @@ fn contains_trait_object(ty: Ty<'_>) -> bool { } } +fn determine_derive_macro(cx: &LateContext<'_>, is_const: bool) -> Option<&'static str> { + (!is_const) + .then_some("derive") + .or_else(|| cx.tcx.features().enabled(sym::derive_const).then_some("derive_const")) +} + +#[expect(clippy::too_many_arguments)] fn check_struct<'tcx>( cx: &LateContext<'tcx>, item: &'tcx Item<'_>, @@ -93,6 +100,7 @@ fn check_struct<'tcx>( adt_def: AdtDef<'_>, ty_args: GenericArgsRef<'_>, typeck_results: &'tcx TypeckResults<'tcx>, + is_const: bool, ) { if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind && let Some(PathSegment { args, .. }) = p.segments.last() @@ -125,14 +133,18 @@ fn check_struct<'tcx>( ExprKind::Tup(fields) => fields.iter().all(is_default_without_adjusts), ExprKind::Call(callee, args) if is_path_self(callee) => args.iter().all(is_default_without_adjusts), ExprKind::Struct(_, fields, _) => fields.iter().all(|ef| is_default_without_adjusts(ef.expr)), - _ => false, + _ => return, }; - if should_emit { + if should_emit && let Some(derive_snippet) = determine_derive_macro(cx, is_const) { let struct_span = cx.tcx.def_span(adt_def.did()); + let indent_enum = indent_of(cx, struct_span).unwrap_or(0); let suggestions = vec![ (item.span, String::new()), // Remove the manual implementation - (struct_span.shrink_to_lo(), "#[derive(Default)]\n".to_string()), // Add the derive attribute + ( + struct_span.shrink_to_lo(), + format!("#[{derive_snippet}(Default)]\n{}", " ".repeat(indent_enum)), + ), // Add the derive attribute ]; span_lint_and_then(cx, DERIVABLE_IMPLS, item.span, "this `impl` can be derived", |diag| { @@ -145,11 +157,41 @@ fn check_struct<'tcx>( } } -fn check_enum<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>, func_expr: &Expr<'_>, adt_def: AdtDef<'_>) { - if let ExprKind::Path(QPath::Resolved(None, p)) = &peel_blocks(func_expr).kind - && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = p.res - && let variant_id = cx.tcx.parent(id) - && let Some(variant_def) = adt_def.variants().iter().find(|v| v.def_id == variant_id) +fn extract_enum_variant<'tcx>( + cx: &LateContext<'tcx>, + func_expr: &'tcx Expr<'tcx>, + adt_def: AdtDef<'tcx>, +) -> Option<&'tcx VariantDef> { + match &peel_blocks(func_expr).kind { + ExprKind::Path(QPath::Resolved(None, p)) + if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = p.res + && let variant_id = cx.tcx.parent(id) + && let Some(variant_def) = adt_def.variants().iter().find(|v| v.def_id == variant_id) => + { + Some(variant_def) + }, + ExprKind::Path(QPath::TypeRelative(ty, segment)) + if let TyKind::Path(QPath::Resolved(None, p)) = &ty.kind + && let Res::SelfTyAlias { + is_trait_impl: true, .. + } = p.res + && let variant_ident = segment.ident + && let Some(variant_def) = adt_def.variants().iter().find(|v| v.ident(cx.tcx) == variant_ident) => + { + Some(variant_def) + }, + _ => None, + } +} + +fn check_enum<'tcx>( + cx: &LateContext<'tcx>, + item: &'tcx Item<'tcx>, + func_expr: &'tcx Expr<'tcx>, + adt_def: AdtDef<'tcx>, + is_const: bool, +) { + if let Some(variant_def) = extract_enum_variant(cx, func_expr, adt_def) && variant_def.fields.is_empty() && !variant_def.is_field_list_non_exhaustive() { @@ -158,11 +200,15 @@ fn check_enum<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>, func_expr: &Ex let variant_span = cx.tcx.def_span(variant_def.def_id); let indent_variant = indent_of(cx, variant_span).unwrap_or(0); + let Some(derive_snippet) = determine_derive_macro(cx, is_const) else { + return; + }; + let suggestions = vec![ (item.span, String::new()), // Remove the manual implementation ( enum_span.shrink_to_lo(), - format!("#[derive(Default)]\n{}", " ".repeat(indent_enum)), + format!("#[{derive_snippet}(Default)]\n{}", " ".repeat(indent_enum)), ), // Add the derive attribute ( variant_span.shrink_to_lo(), @@ -201,10 +247,20 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls { && !attrs.iter().any(|attr| attr.doc_str().is_some()) && cx.tcx.hir_attrs(impl_item_hir).is_empty() { + let is_const = of_trait.constness == hir::Constness::Const; if adt_def.is_struct() { - check_struct(cx, item, self_ty, func_expr, adt_def, args, cx.tcx.typeck_body(*b)); + check_struct( + cx, + item, + self_ty, + func_expr, + adt_def, + args, + cx.tcx.typeck_body(*b), + is_const, + ); } else if adt_def.is_enum() && self.msrv.meets(cx, msrvs::DEFAULT_ENUM_ATTRIBUTE) { - check_enum(cx, item, func_expr, adt_def); + check_enum(cx, item, func_expr, adt_def, is_const); } } } diff --git a/src/tools/clippy/clippy_lints/src/entry.rs b/src/tools/clippy/clippy_lints/src/entry.rs index 182cb4e46d2..a5ec6777b43 100644 --- a/src/tools/clippy/clippy_lints/src/entry.rs +++ b/src/tools/clippy/clippy_lints/src/entry.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::{reindent_multiline, snippet_indent, snippet_with_applicability, snippet_with_context}; use clippy_utils::ty::is_copy; use clippy_utils::visitors::for_each_expr; @@ -194,7 +194,17 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { if let Some(sugg) = sugg { span_lint_and_sugg(cx, MAP_ENTRY, expr.span, lint_msg, "try", sugg, app); } else { - span_lint(cx, MAP_ENTRY, expr.span, lint_msg); + span_lint_and_help( + cx, + MAP_ENTRY, + expr.span, + lint_msg, + None, + format!( + "consider using the `Entry` API: https://doc.rust-lang.org/std/collections/struct.{}.html#entry-api", + map_ty.name() + ), + ); } } } @@ -254,35 +264,28 @@ fn try_parse_contains<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Optio _ => None, }); - match expr.kind { - ExprKind::MethodCall( - _, + if let ExprKind::MethodCall(_, map, [arg], _) = expr.kind + && let Expr { + kind: ExprKind::AddrOf(_, _, key), + span: key_span, + .. + } = arg + && key_span.eq_ctxt(expr.span) + { + let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?; + let expr = ContainsExpr { + negated, map, - [ - Expr { - kind: ExprKind::AddrOf(_, _, key), - span: key_span, - .. - }, - ], - _, - ) if key_span.eq_ctxt(expr.span) => { - let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?; - let expr = ContainsExpr { - negated, - map, - key, - call_ctxt: expr.span.ctxt(), - }; - if cx.tcx.is_diagnostic_item(sym::btreemap_contains_key, id) { - Some((MapType::BTree, expr)) - } else if cx.tcx.is_diagnostic_item(sym::hashmap_contains_key, id) { - Some((MapType::Hash, expr)) - } else { - None - } - }, - _ => None, + key, + call_ctxt: expr.span.ctxt(), + }; + match cx.tcx.get_diagnostic_name(id) { + Some(sym::btreemap_contains_key) => Some((MapType::BTree, expr)), + Some(sym::hashmap_contains_key) => Some((MapType::Hash, expr)), + _ => None, + } + } else { + None } } @@ -311,7 +314,9 @@ struct InsertExpr<'tcx> { fn try_parse_insert<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<InsertExpr<'tcx>> { if let ExprKind::MethodCall(_, map, [key, value], _) = expr.kind { let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?; - if cx.tcx.is_diagnostic_item(sym::btreemap_insert, id) || cx.tcx.is_diagnostic_item(sym::hashmap_insert, id) { + if let Some(insert) = cx.tcx.get_diagnostic_name(id) + && matches!(insert, sym::btreemap_insert | sym::hashmap_insert) + { Some(InsertExpr { map, key, value }) } else { None diff --git a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs index 72f5eaf8a4b..c3fc09343db 100644 --- a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs +++ b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs @@ -53,7 +53,7 @@ fn unary_pattern(pat: &Pat<'_>) -> bool { | PatKind::Never | PatKind::Or(_) | PatKind::Err(_) => false, - PatKind::Struct(_, a, etc) => !etc && a.iter().all(|x| unary_pattern(x.pat)), + PatKind::Struct(_, a, etc) => etc.is_none() && a.iter().all(|x| unary_pattern(x.pat)), PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => etc.as_opt_usize().is_none() && array_rec(a), PatKind::Ref(x, _) | PatKind::Box(x) | PatKind::Deref(x) | PatKind::Guard(x, _) => unary_pattern(x), PatKind::Expr(_) => true, diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs index 0eefc2f6109..2da1c2bad11 100644 --- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs +++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs @@ -12,6 +12,7 @@ use rustc_hir::attrs::AttributeKind; use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, GenericArgs, Param, PatKind, QPath, Safety, TyKind, find_attr}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::{ self, Binder, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, Ty, TypeVisitableExt, TypeckResults, }; @@ -148,10 +149,9 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx { return; } - let callee_ty_adjusted = typeck - .expr_adjustments(callee) - .last() - .map_or(callee_ty, |a| a.target.peel_refs()); + + let callee_ty_adjustments = typeck.expr_adjustments(callee); + let callee_ty_adjusted = callee_ty_adjustments.last().map_or(callee_ty, |a| a.target); let sig = match callee_ty_adjusted.kind() { ty::FnDef(def, _) => { @@ -230,7 +230,20 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx }, _ => (), } + } else if let n_refs = + callee_ty_adjustments + .iter() + .rev() + .fold(0, |acc, adjustment| match adjustment.kind { + Adjust::Deref(Some(_)) => acc + 1, + Adjust::Deref(_) if acc > 0 => acc + 1, + _ => acc, + }) + && n_refs > 0 + { + snippet = format!("{}{snippet}", "*".repeat(n_refs)); } + let replace_with = match callee_ty_adjusted.kind() { ty::FnDef(def, _) => cx.tcx.def_descr(*def), _ => "function", diff --git a/src/tools/clippy/clippy_lints/src/float_literal.rs b/src/tools/clippy/clippy_lints/src/float_literal.rs index ccaf38aee4d..6178addfff1 100644 --- a/src/tools/clippy/clippy_lints/src/float_literal.rs +++ b/src/tools/clippy/clippy_lints/src/float_literal.rs @@ -1,11 +1,12 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::numeric_literal; +use clippy_utils::{ExprUseNode, expr_use_ctxt, numeric_literal}; use rustc_ast::ast::{LitFloatType, LitKind}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, FloatTy}; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; use std::fmt; declare_clippy_lint! { @@ -13,6 +14,8 @@ declare_clippy_lint! { /// Checks for float literals with a precision greater /// than that supported by the underlying type. /// + /// The lint is suppressed for literals with over `const_literal_digits_threshold` digits. + /// /// ### Why is this bad? /// Rust will truncate the literal silently. /// @@ -58,7 +61,21 @@ declare_clippy_lint! { "lossy whole number float literals" } -declare_lint_pass!(FloatLiteral => [EXCESSIVE_PRECISION, LOSSY_FLOAT_LITERAL]); +pub struct FloatLiteral { + const_literal_digits_threshold: usize, +} + +impl_lint_pass!(FloatLiteral => [ + EXCESSIVE_PRECISION, LOSSY_FLOAT_LITERAL +]); + +impl FloatLiteral { + pub fn new(conf: &'static Conf) -> Self { + Self { + const_literal_digits_threshold: conf.const_literal_digits_threshold, + } + } +} impl<'tcx> LateLintPass<'tcx> for FloatLiteral { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { @@ -126,13 +143,25 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral { }, ); } - } else if digits > max as usize && count_digits(&float_str) < count_digits(sym_str) { + } else if digits > max as usize && count_digits(&float_str) < digits { + if digits >= self.const_literal_digits_threshold + && matches!(expr_use_ctxt(cx, expr).use_node(cx), ExprUseNode::ConstStatic(_)) + { + // If a big enough number of digits is specified and it's a constant + // we assume the user is definining a constant, and excessive precision is ok + return; + } span_lint_and_then( cx, EXCESSIVE_PRECISION, expr.span, "float has excessive precision", |diag| { + if digits >= self.const_literal_digits_threshold + && let Some(let_stmt) = maybe_let_stmt(cx, expr) + { + diag.span_note(let_stmt.span, "consider making it a `const` item"); + } diag.span_suggestion_verbose( expr.span, "consider changing the type or truncating it to", @@ -196,3 +225,11 @@ impl FloatFormat { } } } + +fn maybe_let_stmt<'a>(cx: &LateContext<'a>, expr: &hir::Expr<'_>) -> Option<&'a hir::LetStmt<'a>> { + let parent = cx.tcx.parent_hir_node(expr.hir_id); + match parent { + hir::Node::LetStmt(let_stmt) => Some(let_stmt), + _ => None, + } +} diff --git a/src/tools/clippy/clippy_lints/src/functions/mod.rs b/src/tools/clippy/clippy_lints/src/functions/mod.rs index ca5ea901814..5a40af42194 100644 --- a/src/tools/clippy/clippy_lints/src/functions/mod.rs +++ b/src/tools/clippy/clippy_lints/src/functions/mod.rs @@ -535,7 +535,7 @@ impl<'tcx> LateLintPass<'tcx> for Functions { def_id: LocalDefId, ) { let hir_id = cx.tcx.local_def_id_to_hir_id(def_id); - too_many_arguments::check_fn(cx, kind, decl, span, hir_id, self.too_many_arguments_threshold); + too_many_arguments::check_fn(cx, kind, decl, hir_id, def_id, self.too_many_arguments_threshold); too_many_lines::check_fn(cx, kind, body, span, def_id, self.too_many_lines_threshold); not_unsafe_ptr_arg_deref::check_fn(cx, kind, decl, body, def_id); misnamed_getters::check_fn(cx, kind, decl, body, span); diff --git a/src/tools/clippy/clippy_lints/src/functions/too_many_arguments.rs b/src/tools/clippy/clippy_lints/src/functions/too_many_arguments.rs index 48d050aa36a..6c3c3d354ec 100644 --- a/src/tools/clippy/clippy_lints/src/functions/too_many_arguments.rs +++ b/src/tools/clippy/clippy_lints/src/functions/too_many_arguments.rs @@ -1,5 +1,7 @@ use rustc_abi::ExternAbi; -use rustc_hir::{self as hir, intravisit}; +use rustc_hir as hir; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::intravisit::FnKind; use rustc_lint::LateContext; use rustc_span::Span; @@ -10,39 +12,18 @@ use super::TOO_MANY_ARGUMENTS; pub(super) fn check_fn( cx: &LateContext<'_>, - kind: intravisit::FnKind<'_>, + kind: FnKind<'_>, decl: &hir::FnDecl<'_>, - span: Span, hir_id: hir::HirId, + def_id: LocalDefId, too_many_arguments_threshold: u64, ) { // don't warn for implementations, it's not their fault - if !is_trait_impl_item(cx, hir_id) { + if !is_trait_impl_item(cx, hir_id) // don't lint extern functions decls, it's not their fault either - match kind { - intravisit::FnKind::Method( - _, - &hir::FnSig { - header: hir::FnHeader { - abi: ExternAbi::Rust, .. - }, - .. - }, - ) - | intravisit::FnKind::ItemFn( - _, - _, - hir::FnHeader { - abi: ExternAbi::Rust, .. - }, - ) => check_arg_number( - cx, - decl, - span.with_hi(decl.output.span().hi()), - too_many_arguments_threshold, - ), - _ => {}, - } + && kind.header().is_some_and(|header| header.abi == ExternAbi::Rust) + { + check_arg_number(cx, decl, cx.tcx.def_span(def_id), too_many_arguments_threshold); } } diff --git a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs index 7158f9419c1..b50d91f1014 100644 --- a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs +++ b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs @@ -2,16 +2,16 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::snippet_with_context; +use clippy_utils::source::{snippet_with_applicability, snippet_with_context, walk_span_to_context}; use clippy_utils::sugg::Sugg; use clippy_utils::{ contains_return, expr_adjustment_requires_coercion, higher, is_else_clause, is_in_const_context, is_res_lang_ctor, - path_res, peel_blocks, + path_res, peel_blocks, sym, }; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; declare_clippy_lint! { @@ -71,21 +71,21 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { && let ExprKind::Block(then_block, _) = then.kind && let Some(then_expr) = then_block.expr && let ExprKind::Call(then_call, [then_arg]) = then_expr.kind - && let ctxt = expr.span.ctxt() - && then_expr.span.ctxt() == ctxt + && !expr.span.from_expansion() + && !then_expr.span.from_expansion() && is_res_lang_ctor(cx, path_res(cx, then_call), OptionSome) && is_res_lang_ctor(cx, path_res(cx, peel_blocks(els)), OptionNone) && !is_else_clause(cx.tcx, expr) && !is_in_const_context(cx) - && !expr.span.in_external_macro(cx.sess().source_map()) && self.msrv.meets(cx, msrvs::BOOL_THEN) && !contains_return(then_block.stmts) { let method_name = if switch_to_eager_eval(cx, expr) && self.msrv.meets(cx, msrvs::BOOL_THEN_SOME) { - "then_some" + sym::then_some } else { - "then" + sym::then }; + let ctxt = expr.span.ctxt(); span_lint_and_then( cx, @@ -98,16 +98,18 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { } let mut app = Applicability::MachineApplicable; - let cond_snip = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "[condition]", &mut app) + let cond_snip = Sugg::hir_with_context(cx, cond, ctxt, "[condition]", &mut app) .maybe_paren() .to_string(); let arg_snip = snippet_with_context(cx, then_arg.span, ctxt, "[body]", &mut app).0; - let method_body = if let Some(first_stmt) = then_block.stmts.first() { - let (block_snippet, _) = - snippet_with_context(cx, first_stmt.span.until(then_arg.span), ctxt, "..", &mut app); - let closure = if method_name == "then" { "|| " } else { "" }; - format!("{closure} {{ {block_snippet}; {arg_snip} }}") - } else if method_name == "then" { + let method_body = if let Some(first_stmt) = then_block.stmts.first() + && let Some(first_stmt_span) = walk_span_to_context(first_stmt.span, ctxt) + { + let block_snippet = + snippet_with_applicability(cx, first_stmt_span.until(then_expr.span), "..", &mut app); + let closure = if method_name == sym::then { "|| " } else { "" }; + format!("{closure} {{ {} {arg_snip} }}", block_snippet.trim_end()) + } else if method_name == sym::then { (std::borrow::Cow::Borrowed("|| ") + arg_snip).into_owned() } else { arg_snip.into_owned() diff --git a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs index 91f65d0b79c..13117f60abd 100644 --- a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs +++ b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_context; use clippy_utils::sugg::Sugg; -use clippy_utils::ty; +use clippy_utils::{is_path_diagnostic_item, ty}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -107,8 +107,7 @@ impl LateLintPass<'_> for InstantSubtraction { fn is_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool { if let ExprKind::Call(fn_expr, []) = expr_block.kind - && let Some(fn_id) = clippy_utils::path_def_id(cx, fn_expr) - && cx.tcx.is_diagnostic_item(sym::instant_now, fn_id) + && is_path_diagnostic_item(cx, fn_expr, sym::instant_now) { true } else { diff --git a/src/tools/clippy/clippy_lints/src/large_include_file.rs b/src/tools/clippy/clippy_lints/src/large_include_file.rs index 8707612fbdd..48ce1afc6e6 100644 --- a/src/tools/clippy/clippy_lints/src/large_include_file.rs +++ b/src/tools/clippy/clippy_lints/src/large_include_file.rs @@ -64,8 +64,8 @@ impl LateLintPass<'_> for LargeIncludeFile { } && len as u64 > self.max_file_size && let Some(macro_call) = root_macro_call_first_node(cx, expr) - && (cx.tcx.is_diagnostic_item(sym::include_bytes_macro, macro_call.def_id) - || cx.tcx.is_diagnostic_item(sym::include_str_macro, macro_call.def_id)) + && let Some(macro_name) = cx.tcx.get_diagnostic_name(macro_call.def_id) + && matches!(macro_name, sym::include_bytes_macro | sym::include_str_macro) { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then( diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs index 57deb011f2b..f44a5fdf715 100644 --- a/src/tools/clippy/clippy_lints/src/len_zero.rs +++ b/src/tools/clippy/clippy_lints/src/len_zero.rs @@ -355,12 +355,15 @@ fn parse_len_output<'tcx>(cx: &LateContext<'tcx>, sig: FnSig<'tcx>) -> Option<Le return Some(LenOutput::Integral); } - if let Res::Def(_, def_id) = res { - if cx.tcx.is_diagnostic_item(sym::Option, def_id) && is_first_generic_integral(segment) { - return Some(LenOutput::Option(def_id)); - } else if cx.tcx.is_diagnostic_item(sym::Result, def_id) && is_first_generic_integral(segment) { - return Some(LenOutput::Result(def_id)); + if let Res::Def(_, def_id) = res + && let Some(res) = match cx.tcx.get_diagnostic_name(def_id) { + Some(sym::Option) => Some(LenOutput::Option(def_id)), + Some(sym::Result) => Some(LenOutput::Result(def_id)), + _ => None, } + && is_first_generic_integral(segment) + { + return Some(res); } return None; @@ -368,11 +371,10 @@ fn parse_len_output<'tcx>(cx: &LateContext<'tcx>, sig: FnSig<'tcx>) -> Option<Le match *sig.output().kind() { ty::Int(_) | ty::Uint(_) => Some(LenOutput::Integral), - ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) => { - subs.type_at(0).is_integral().then(|| LenOutput::Option(adt.did())) - }, - ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) => { - subs.type_at(0).is_integral().then(|| LenOutput::Result(adt.did())) + ty::Adt(adt, subs) if subs.type_at(0).is_integral() => match cx.tcx.get_diagnostic_name(adt.did()) { + Some(sym::Option) => Some(LenOutput::Option(adt.did())), + Some(sym::Result) => Some(LenOutput::Result(adt.did())), + _ => None, }, _ => None, } diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index c8594cf35e2..a89cf3fdc1e 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -84,6 +84,7 @@ mod attrs; mod await_holding_invalid; mod blocks_in_conditions; mod bool_assert_comparison; +mod bool_comparison; mod bool_to_int_with_if; mod booleans; mod borrow_deref_ref; @@ -474,10 +475,10 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(move |_| Box::new(types::Types::new(conf))); store.register_late_pass(move |_| Box::new(booleans::NonminimalBool::new(conf))); store.register_late_pass(|_| Box::new(enum_clike::UnportableVariant)); - store.register_late_pass(|_| Box::new(float_literal::FloatLiteral)); + store.register_late_pass(move |_| Box::new(float_literal::FloatLiteral::new(conf))); store.register_late_pass(|_| Box::new(ptr::Ptr)); store.register_late_pass(|_| Box::new(needless_bool::NeedlessBool)); - store.register_late_pass(|_| Box::new(needless_bool::BoolComparison)); + store.register_late_pass(|_| Box::new(bool_comparison::BoolComparison)); store.register_late_pass(|_| Box::new(needless_for_each::NeedlessForEach)); store.register_late_pass(|_| Box::new(misc::LintPass)); store.register_late_pass(|_| Box::new(eta_reduction::EtaReduction)); @@ -655,7 +656,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(conf))); store.register_late_pass(|_| Box::<macro_use::MacroUseImports>::default()); store.register_late_pass(|_| Box::new(pattern_type_mismatch::PatternTypeMismatch)); - store.register_late_pass(|_| Box::new(unwrap_in_result::UnwrapInResult)); + store.register_late_pass(|_| Box::<unwrap_in_result::UnwrapInResult>::default()); store.register_late_pass(|_| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned)); store.register_late_pass(|_| Box::new(async_yields_async::AsyncYieldsAsync)); let attrs = attr_storage.clone(); diff --git a/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs b/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs index a71e6963f8c..74c0b178018 100644 --- a/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs @@ -4,7 +4,8 @@ use hir::intravisit::{Visitor, walk_expr}; use rustc_ast::Label; use rustc_errors::Applicability; use rustc_hir::{ - self as hir, Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, Expr, ExprKind, FnRetTy, FnSig, Node, TyKind, + self as hir, Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, FnRetTy, + FnSig, Node, TyKind, }; use rustc_lint::{LateContext, LintContext}; use rustc_span::sym; @@ -73,7 +74,11 @@ fn is_inside_unawaited_async_block(cx: &LateContext<'_>, expr: &Expr<'_>) -> boo if let Node::Expr(Expr { kind: ExprKind::Closure(Closure { - kind: ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)), + kind: + ClosureKind::Coroutine(CoroutineKind::Desugared( + CoroutineDesugaring::Async, + CoroutineSource::Block | CoroutineSource::Closure, + )), .. }), .. diff --git a/src/tools/clippy/clippy_lints/src/loops/missing_spin_loop.rs b/src/tools/clippy/clippy_lints/src/loops/missing_spin_loop.rs index a9944d64ce2..8a2d0036203 100644 --- a/src/tools/clippy/clippy_lints/src/loops/missing_spin_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/missing_spin_loop.rs @@ -1,10 +1,10 @@ use super::MISSING_SPIN_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::std_or_core; +use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind}; use rustc_lint::LateContext; -use rustc_middle::ty; use rustc_span::sym; fn unpack_cond<'tcx>(cond: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { @@ -39,8 +39,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, body: &' ) = body.kind && let ExprKind::MethodCall(method, callee, ..) = unpack_cond(cond).kind && [sym::load, sym::compare_exchange, sym::compare_exchange_weak].contains(&method.ident.name) - && let ty::Adt(def, _args) = cx.typeck_results().expr_ty(callee).kind() - && cx.tcx.is_diagnostic_item(sym::AtomicBool, def.did()) + && let callee_ty = cx.typeck_results().expr_ty(callee) + && is_type_diagnostic_item(cx, callee_ty, sym::AtomicBool) && let Some(std_or_core) = std_or_core(cx) { span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs index 7bb684d65bb..11edb929d70 100644 --- a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::ty::has_iter_method; use clippy_utils::visitors::is_local_used; -use clippy_utils::{SpanlessEq, contains_name, higher, is_integer_const, sugg}; +use clippy_utils::{SpanlessEq, contains_name, higher, is_integer_const, peel_hir_expr_while, sugg}; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_errors::Applicability; @@ -253,12 +253,38 @@ struct VarVisitor<'a, 'tcx> { impl<'tcx> VarVisitor<'_, 'tcx> { fn check(&mut self, idx: &'tcx Expr<'_>, seqexpr: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) -> bool { - let index_used_directly = matches!(idx.kind, ExprKind::Path(_)); + let mut used_cnt = 0; + // It is `true` if all indices are direct + let mut index_used_directly = true; + + // Handle initial index + if is_local_used(self.cx, idx, self.var) { + used_cnt += 1; + index_used_directly &= matches!(idx.kind, ExprKind::Path(_)); + } + // Handle nested indices + let seqexpr = peel_hir_expr_while(seqexpr, |e| { + if let ExprKind::Index(e, idx, _) = e.kind { + if is_local_used(self.cx, idx, self.var) { + used_cnt += 1; + index_used_directly &= matches!(idx.kind, ExprKind::Path(_)); + } + Some(e) + } else { + None + } + }); + + match used_cnt { + 0 => return true, + n if n > 1 => self.nonindex = true, // Optimize code like `a[i][i]` + _ => {}, + } + if let ExprKind::Path(ref seqpath) = seqexpr.kind // the indexed container is referenced by a name && let QPath::Resolved(None, seqvar) = *seqpath && seqvar.segments.len() == 1 - && is_local_used(self.cx, idx, self.var) { if self.prefer_mutable { self.indexed_mut.insert(seqvar.segments[0].ident.name); @@ -312,7 +338,6 @@ impl<'tcx> VarVisitor<'_, 'tcx> { impl<'tcx> Visitor<'tcx> for VarVisitor<'_, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { if let ExprKind::MethodCall(meth, args_0, [args_1, ..], _) = &expr.kind - // a range index op && let Some(trait_id) = self .cx .typeck_results() diff --git a/src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs b/src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs index 51e21aa9734..13b93d2c009 100644 --- a/src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs +++ b/src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs @@ -1,12 +1,12 @@ use super::UNUSED_ENUMERATE_INDEX; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; +use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{pat_is_wild, sugg}; use rustc_errors::Applicability; use rustc_hir::def::DefKind; use rustc_hir::{Expr, ExprKind, Pat, PatKind}; use rustc_lint::LateContext; -use rustc_middle::ty; use rustc_span::sym; /// Checks for the `UNUSED_ENUMERATE_INDEX` lint. @@ -17,8 +17,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>, arg: &Expr<'_ && let ExprKind::MethodCall(_method, self_arg, [], _) = arg.kind && let ty = cx.typeck_results().expr_ty(arg) && pat_is_wild(cx, &index.kind, body) - && let ty::Adt(base, _) = *ty.kind() - && cx.tcx.is_diagnostic_item(sym::Enumerate, base.did()) + && is_type_diagnostic_item(cx, ty, sym::Enumerate) && let Some((DefKind::AssocFn, call_id)) = cx.typeck_results().type_dependent_def(arg.hir_id) && cx.tcx.is_diagnostic_item(sym::enumerate_method, call_id) { diff --git a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs index ac8c88f0205..2eebb2430fd 100644 --- a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs +++ b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs @@ -97,11 +97,12 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { return; } - if let Some(macro_call) = matching_root_macro_call(cx, expr.span, sym::matches_macro) { - if let ExprKind::Match(recv, [arm, ..], _) = expr.kind { - let range = check_pat(&arm.pat.kind); - check_is_ascii(cx, macro_call.span, recv, &range, None); - } + let (arg, span, range) = if let Some(macro_call) = matching_root_macro_call(cx, expr.span, sym::matches_macro) + && let ExprKind::Match(recv, [arm, ..], _) = expr.kind + { + let recv = peel_ref_operators(cx, recv); + let range = check_pat(&arm.pat.kind); + (recv, macro_call.span, range) } else if let ExprKind::MethodCall(path, receiver, [arg], ..) = expr.kind && path.ident.name == sym::contains && let Some(higher::Range { @@ -112,10 +113,14 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { && !matches!(cx.typeck_results().expr_ty(arg).peel_refs().kind(), ty::Param(_)) { let arg = peel_ref_operators(cx, arg); - let ty_sugg = get_ty_sugg(cx, arg); let range = check_expr_range(start, end); - check_is_ascii(cx, expr.span, arg, &range, ty_sugg); - } + (arg, expr.span, range) + } else { + return; + }; + + let ty_sugg = get_ty_sugg(cx, arg); + check_is_ascii(cx, span, arg, &range, ty_sugg); } } @@ -146,9 +151,8 @@ fn check_is_ascii( CharRange::HexDigit => "is_ascii_hexdigit", CharRange::Otherwise | CharRange::LowerHexLetter | CharRange::UpperHexLetter => return, }; - let default_snip = ".."; let mut app = Applicability::MachineApplicable; - let recv = Sugg::hir_with_context(cx, recv, span.ctxt(), default_snip, &mut app).maybe_paren(); + let recv = Sugg::hir_with_context(cx, recv, span.ctxt(), "_", &mut app).maybe_paren(); let mut suggestion = vec![(span, format!("{recv}.{sugg}()"))]; if let Some((ty_span, ty)) = ty_sugg { suggestion.push((ty_span, format!("{recv}: {ty}"))); @@ -182,7 +186,7 @@ fn check_pat(pat_kind: &PatKind<'_>) -> CharRange { CharRange::Otherwise } }, - PatKind::Range(Some(start), Some(end), kind) if *kind == RangeEnd::Included => check_range(start, end), + PatKind::Range(Some(start), Some(end), RangeEnd::Included) => check_range(start, end), _ => CharRange::Otherwise, } } diff --git a/src/tools/clippy/clippy_lints/src/manual_let_else.rs b/src/tools/clippy/clippy_lints/src/manual_let_else.rs index 5a7967bbf94..2705ef20b79 100644 --- a/src/tools/clippy/clippy_lints/src/manual_let_else.rs +++ b/src/tools/clippy/clippy_lints/src/manual_let_else.rs @@ -287,7 +287,7 @@ fn replace_in_pattern( } return or_pat; }, - PatKind::Struct(path, fields, has_dot_dot) => { + PatKind::Struct(path, fields, dot_dot) => { let fields = fields .iter() .map(|fld| { @@ -311,7 +311,7 @@ fn replace_in_pattern( .collect::<Vec<_>>(); let fields_string = fields.join(", "); - let dot_dot_str = if has_dot_dot { " .." } else { "" }; + let dot_dot_str = if dot_dot.is_some() { " .." } else { "" }; let (sn_pth, _) = snippet_with_context(cx, path.span(), span.ctxt(), "", app); return format!("{sn_pth} {{ {fields_string}{dot_dot_str} }}"); }, diff --git a/src/tools/clippy/clippy_lints/src/manual_retain.rs b/src/tools/clippy/clippy_lints/src/manual_retain.rs index 98e8b1f5cf9..7fb88763e64 100644 --- a/src/tools/clippy/clippy_lints/src/manual_retain.rs +++ b/src/tools/clippy/clippy_lints/src/manual_retain.rs @@ -123,8 +123,8 @@ fn check_iter( ) { if let hir::ExprKind::MethodCall(_, filter_expr, [], _) = &target_expr.kind && let Some(copied_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id) - && (cx.tcx.is_diagnostic_item(sym::iter_copied, copied_def_id) - || cx.tcx.is_diagnostic_item(sym::iter_cloned, copied_def_id)) + && let Some(copied_name) = cx.tcx.get_diagnostic_name(copied_def_id) + && matches!(copied_name, sym::iter_copied | sym::iter_cloned) && let hir::ExprKind::MethodCall(_, iter_expr, [_], _) = &filter_expr.kind && let Some(filter_def_id) = cx.typeck_results().type_dependent_def_id(filter_expr.hir_id) && cx.tcx.is_diagnostic_item(sym::iter_filter, filter_def_id) @@ -243,9 +243,9 @@ fn make_sugg( } fn match_acceptable_sym(cx: &LateContext<'_>, collect_def_id: DefId) -> bool { - ACCEPTABLE_METHODS - .iter() - .any(|&method| cx.tcx.is_diagnostic_item(method, collect_def_id)) + cx.tcx + .get_diagnostic_name(collect_def_id) + .is_some_and(|collect_name| ACCEPTABLE_METHODS.contains(&collect_name)) } fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: Msrv) -> bool { diff --git a/src/tools/clippy/clippy_lints/src/manual_strip.rs b/src/tools/clippy/clippy_lints/src/manual_strip.rs index 6bf43a1c6d4..07cce4046ca 100644 --- a/src/tools/clippy/clippy_lints/src/manual_strip.rs +++ b/src/tools/clippy/clippy_lints/src/manual_strip.rs @@ -75,12 +75,10 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { && let ExprKind::Path(target_path) = &target_arg.kind && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(cond.hir_id) { - let strip_kind = if cx.tcx.is_diagnostic_item(sym::str_starts_with, method_def_id) { - StripKind::Prefix - } else if cx.tcx.is_diagnostic_item(sym::str_ends_with, method_def_id) { - StripKind::Suffix - } else { - return; + let strip_kind = match cx.tcx.get_diagnostic_name(method_def_id) { + Some(sym::str_starts_with) => StripKind::Prefix, + Some(sym::str_ends_with) => StripKind::Suffix, + _ => return, }; let target_res = cx.qpath_res(target_path, target_arg.hir_id); if target_res == Res::Err { diff --git a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs index af6a1b07a49..39e5289c62a 100644 --- a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs +++ b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs @@ -116,8 +116,10 @@ fn is_unit_expression(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { /// The expression inside a closure may or may not have surrounding braces and /// semicolons, which causes problems when generating a suggestion. Given an /// expression that evaluates to '()' or '!', recursively remove useless braces -/// and semi-colons until is suitable for including in the suggestion template -fn reduce_unit_expression(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Span> { +/// and semi-colons until is suitable for including in the suggestion template. +/// The `bool` is `true` when the resulting `span` needs to be enclosed in an +/// `unsafe` block. +fn reduce_unit_expression(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<(Span, bool)> { if !is_unit_expression(cx, expr) { return None; } @@ -125,22 +127,24 @@ fn reduce_unit_expression(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option< match expr.kind { hir::ExprKind::Call(_, _) | hir::ExprKind::MethodCall(..) => { // Calls can't be reduced any more - Some(expr.span) + Some((expr.span, false)) }, hir::ExprKind::Block(block, _) => { + let is_unsafe = matches!(block.rules, hir::BlockCheckMode::UnsafeBlock(_)); match (block.stmts, block.expr.as_ref()) { ([], Some(inner_expr)) => { // If block only contains an expression, // reduce `{ X }` to `X` reduce_unit_expression(cx, inner_expr) + .map(|(span, inner_is_unsafe)| (span, inner_is_unsafe || is_unsafe)) }, ([inner_stmt], None) => { // If block only contains statements, // reduce `{ X; }` to `X` or `X;` match inner_stmt.kind { - hir::StmtKind::Let(local) => Some(local.span), - hir::StmtKind::Expr(e) => Some(e.span), - hir::StmtKind::Semi(..) => Some(inner_stmt.span), + hir::StmtKind::Let(local) => Some((local.span, is_unsafe)), + hir::StmtKind::Expr(e) => Some((e.span, is_unsafe)), + hir::StmtKind::Semi(..) => Some((inner_stmt.span, is_unsafe)), hir::StmtKind::Item(..) => None, } }, @@ -228,10 +232,11 @@ fn lint_map_unit_fn( let msg = suggestion_msg("closure", map_type); span_lint_and_then(cx, lint, expr.span, msg, |diag| { - if let Some(reduced_expr_span) = reduce_unit_expression(cx, closure_expr) { + if let Some((reduced_expr_span, is_unsafe)) = reduce_unit_expression(cx, closure_expr) { let mut applicability = Applicability::MachineApplicable; + let (prefix_is_unsafe, suffix_is_unsafe) = if is_unsafe { ("unsafe { ", " }") } else { ("", "") }; let suggestion = format!( - "if let {0}({1}) = {2} {{ {3} }}", + "if let {0}({1}) = {2} {{ {prefix_is_unsafe}{3}{suffix_is_unsafe} }}", variant, snippet_with_applicability(cx, binding.pat.span, "_", &mut applicability), snippet_with_applicability(cx, var_arg.span, "_", &mut applicability), diff --git a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs index 5b50efad3e4..aaf559fc443 100644 --- a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs @@ -4,20 +4,22 @@ use clippy_utils::msrvs::Msrv; use clippy_utils::source::snippet; use clippy_utils::visitors::is_local_used; use clippy_utils::{ - SpanlessEq, is_res_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators, + SpanlessEq, get_ref_operators, is_res_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, + peel_ref_operators, }; +use rustc_ast::BorrowKind; use rustc_errors::MultiSpan; use rustc_hir::LangItem::OptionNone; -use rustc_hir::{Arm, Expr, HirId, Pat, PatExpr, PatExprKind, PatKind}; +use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatExpr, PatExprKind, PatKind}; use rustc_lint::LateContext; use rustc_span::Span; use super::{COLLAPSIBLE_MATCH, pat_contains_disallowed_or}; -pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], msrv: Msrv) { +pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>], msrv: Msrv) { if let Some(els_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) { for arm in arms { - check_arm(cx, true, arm.pat, arm.body, arm.guard, Some(els_arm.body), msrv); + check_arm(cx, true, arm.pat, expr, arm.body, arm.guard, Some(els_arm.body), msrv); } } } @@ -27,15 +29,18 @@ pub(super) fn check_if_let<'tcx>( pat: &'tcx Pat<'_>, body: &'tcx Expr<'_>, else_expr: Option<&'tcx Expr<'_>>, + let_expr: &'tcx Expr<'_>, msrv: Msrv, ) { - check_arm(cx, false, pat, body, None, else_expr, msrv); + check_arm(cx, false, pat, let_expr, body, None, else_expr, msrv); } +#[allow(clippy::too_many_arguments)] fn check_arm<'tcx>( cx: &LateContext<'tcx>, outer_is_match: bool, outer_pat: &'tcx Pat<'tcx>, + outer_cond: &'tcx Expr<'tcx>, outer_then_body: &'tcx Expr<'tcx>, outer_guard: Option<&'tcx Expr<'tcx>>, outer_else_body: Option<&'tcx Expr<'tcx>>, @@ -82,6 +87,9 @@ fn check_arm<'tcx>( }, IfLetOrMatch::Match(_, arms, ..) => !arms.iter().any(|arm| is_local_used(cx, arm, binding_id)), } + // Check if the inner expression contains any borrows/dereferences + && let ref_types = get_ref_operators(cx, inner_scrutinee) + && let Some(method) = build_ref_method_chain(ref_types) { let msg = format!( "this `{}` can be collapsed into the outer `{}`", @@ -103,6 +111,10 @@ fn check_arm<'tcx>( let mut help_span = MultiSpan::from_spans(vec![binding_span, inner_then_pat.span]); help_span.push_span_label(binding_span, "replace this binding"); help_span.push_span_label(inner_then_pat.span, format!("with this pattern{replace_msg}")); + if !method.is_empty() { + let outer_cond_msg = format!("use: `{}{}`", snippet(cx, outer_cond.span, ".."), method); + help_span.push_span_label(outer_cond.span, outer_cond_msg); + } diag.span_help( help_span, "the outer pattern can be modified to include the inner pattern", @@ -148,3 +160,30 @@ fn find_pat_binding_and_is_innermost_parent_pat_struct(pat: &Pat<'_>, hir_id: Hi }); (span, is_innermost_parent_pat_struct) } + +/// Builds a chain of reference-manipulation method calls (e.g., `.as_ref()`, `.as_mut()`, +/// `.copied()`) based on reference operators +fn build_ref_method_chain(expr: Vec<&Expr<'_>>) -> Option<String> { + let mut req_method_calls = String::new(); + + for ref_operator in expr { + match ref_operator.kind { + ExprKind::AddrOf(BorrowKind::Raw, _, _) => { + return None; + }, + ExprKind::AddrOf(_, m, _) if m.is_mut() => { + req_method_calls.push_str(".as_mut()"); + }, + ExprKind::AddrOf(_, _, _) => { + req_method_calls.push_str(".as_ref()"); + }, + // Deref operator is the only operator that this function should have received + ExprKind::Unary(_, _) => { + req_method_calls.push_str(".copied()"); + }, + _ => (), + } + } + + Some(req_method_calls) +} diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs index c128fc40b73..6f49c552411 100644 --- a/src/tools/clippy/clippy_lints/src/matches/mod.rs +++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs @@ -1073,7 +1073,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { significant_drop_in_scrutinee::check_match(cx, expr, ex, arms, source); } - collapsible_match::check_match(cx, arms, self.msrv); + collapsible_match::check_match(cx, ex, arms, self.msrv); if !from_expansion { // These don't depend on a relationship between multiple arms match_wild_err_arm::check(cx, ex, arms); @@ -1137,7 +1137,14 @@ impl<'tcx> LateLintPass<'tcx> for Matches { match_ref_pats::check(cx, ex, arms.iter().map(|el| el.pat), expr); } } else if let Some(if_let) = higher::IfLet::hir(cx, expr) { - collapsible_match::check_if_let(cx, if_let.let_pat, if_let.if_then, if_let.if_else, self.msrv); + collapsible_match::check_if_let( + cx, + if_let.let_pat, + if_let.if_then, + if_let.if_else, + if_let.let_expr, + self.msrv, + ); significant_drop_in_scrutinee::check_if_let(cx, expr, if_let.let_expr, if_let.if_then, if_let.if_else); if !from_expansion { if let Some(else_expr) = if_let.if_else { diff --git a/src/tools/clippy/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs b/src/tools/clippy/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs index 2154cd5b24a..ae09c2e87d6 100644 --- a/src/tools/clippy/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs +++ b/src/tools/clippy/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs @@ -7,7 +7,7 @@ use super::REST_PAT_IN_FULLY_BOUND_STRUCTS; pub(crate) fn check(cx: &LateContext<'_>, pat: &Pat<'_>) { if !pat.span.from_expansion() - && let PatKind::Struct(QPath::Resolved(_, path), fields, true) = pat.kind + && let PatKind::Struct(QPath::Resolved(_, path), fields, Some(_)) = pat.kind && let Some(def_id) = path.res.opt_def_id() && let ty = cx.tcx.type_of(def_id).instantiate_identity() && let ty::Adt(def, _) = ty.kind() diff --git a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs index 027dd7ce053..81fecc87256 100644 --- a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs +++ b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -226,11 +226,12 @@ impl<'a, 'tcx> SigDropChecker<'a, 'tcx> { } } -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Default)] enum SigDropHolder { /// No values with significant drop present in this expression. /// /// Expressions that we've emitted lints do not count. + #[default] None, /// Some field in this expression references to values with significant drop. /// @@ -244,12 +245,6 @@ enum SigDropHolder { Moved, } -impl Default for SigDropHolder { - fn default() -> Self { - Self::None - } -} - struct SigDropHelper<'a, 'tcx> { cx: &'a LateContext<'tcx>, parent_expr: Option<&'tcx Expr<'tcx>>, diff --git a/src/tools/clippy/clippy_lints/src/matches/try_err.rs b/src/tools/clippy/clippy_lints/src/matches/try_err.rs index ff7769af1df..af90cb5e673 100644 --- a/src/tools/clippy/clippy_lints/src/matches/try_err.rs +++ b/src/tools/clippy/clippy_lints/src/matches/try_err.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::option_arg_ty; use clippy_utils::{get_parent_expr, is_res_lang_ctor, path_res}; use rustc_errors::Applicability; use rustc_hir::LangItem::ResultErr; @@ -28,25 +28,15 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine && is_res_lang_ctor(cx, path_res(cx, err_fun), ResultErr) && let Some(return_ty) = find_return_type(cx, &expr.kind) { - let prefix; - let suffix; - let err_ty; - - if let Some(ty) = result_error_type(cx, return_ty) { - prefix = "Err("; - suffix = ")"; - err_ty = ty; + let (prefix, suffix, err_ty) = if let Some(ty) = result_error_type(cx, return_ty) { + ("Err(", ")", ty) } else if let Some(ty) = poll_result_error_type(cx, return_ty) { - prefix = "Poll::Ready(Err("; - suffix = "))"; - err_ty = ty; + ("Poll::Ready(Err(", "))", ty) } else if let Some(ty) = poll_option_result_error_type(cx, return_ty) { - prefix = "Poll::Ready(Some(Err("; - suffix = ")))"; - err_ty = ty; + ("Poll::Ready(Some(Err(", ")))", ty) } else { return; - } + }; span_lint_and_then( cx, @@ -88,8 +78,8 @@ fn find_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> O /// Extracts the error type from Result<T, E>. fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> { - if let ty::Adt(_, subst) = ty.kind() - && is_type_diagnostic_item(cx, ty, sym::Result) + if let ty::Adt(def, subst) = ty.kind() + && cx.tcx.is_diagnostic_item(sym::Result, def.did()) { Some(subst.type_at(1)) } else { @@ -101,11 +91,9 @@ fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'t fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> { if let ty::Adt(def, subst) = ty.kind() && cx.tcx.lang_items().get(LangItem::Poll) == Some(def.did()) - && let ready_ty = subst.type_at(0) - && let ty::Adt(ready_def, ready_subst) = ready_ty.kind() - && cx.tcx.is_diagnostic_item(sym::Result, ready_def.did()) { - Some(ready_subst.type_at(1)) + let ready_ty = subst.type_at(0); + result_error_type(cx, ready_ty) } else { None } @@ -116,13 +104,9 @@ fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> if let ty::Adt(def, subst) = ty.kind() && cx.tcx.lang_items().get(LangItem::Poll) == Some(def.did()) && let ready_ty = subst.type_at(0) - && let ty::Adt(ready_def, ready_subst) = ready_ty.kind() - && cx.tcx.is_diagnostic_item(sym::Option, ready_def.did()) - && let some_ty = ready_subst.type_at(0) - && let ty::Adt(some_def, some_subst) = some_ty.kind() - && cx.tcx.is_diagnostic_item(sym::Result, some_def.did()) + && let Some(some_ty) = option_arg_ty(cx, ready_ty) { - Some(some_subst.type_at(1)) + result_error_type(cx, some_ty) } else { None } diff --git a/src/tools/clippy/clippy_lints/src/mem_replace.rs b/src/tools/clippy/clippy_lints/src/mem_replace.rs index 28efd2038b3..e39916f733d 100644 --- a/src/tools/clippy/clippy_lints/src/mem_replace.rs +++ b/src/tools/clippy/clippy_lints/src/mem_replace.rs @@ -215,7 +215,8 @@ fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<' && let ExprKind::Path(ref repl_func_qpath) = repl_func.kind && let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id() { - if cx.tcx.is_diagnostic_item(sym::mem_uninitialized, repl_def_id) { + let repl_name = cx.tcx.get_diagnostic_name(repl_def_id); + if repl_name == Some(sym::mem_uninitialized) { let Some(top_crate) = std_or_core(cx) else { return }; let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( @@ -230,9 +231,7 @@ fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<' ), applicability, ); - } else if cx.tcx.is_diagnostic_item(sym::mem_zeroed, repl_def_id) - && !cx.typeck_results().expr_ty(src).is_primitive() - { + } else if repl_name == Some(sym::mem_zeroed) && !cx.typeck_results().expr_ty(src).is_primitive() { span_lint_and_help( cx, MEM_REPLACE_WITH_UNINIT, diff --git a/src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs b/src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs index 96e2de0dc1c..65583c6a981 100644 --- a/src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs @@ -24,9 +24,10 @@ pub(super) fn check( && let Some(name) = cx.tcx.get_diagnostic_name(adt.did()) { let caller_type = match name { - sym::Rc => "Rc", - sym::Arc => "Arc", - sym::RcWeak | sym::ArcWeak => "Weak", + sym::Rc => "std::rc::Rc", + sym::Arc => "std::sync::Arc", + sym::RcWeak => "std::rc::Weak", + sym::ArcWeak => "std::sync::Weak", _ => return, }; span_lint_and_then( diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs index 5b8457bdd16..2da0f8341b1 100644 --- a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs @@ -233,18 +233,16 @@ impl<'tcx> OffendingFilterExpr<'tcx> { // the latter only calls `effect` once let side_effect_expr_span = receiver.can_have_side_effects().then_some(receiver.span); - if cx.tcx.is_diagnostic_item(sym::Option, recv_ty.did()) && path.ident.name == sym::is_some { - Some(Self::IsSome { + match (cx.tcx.get_diagnostic_name(recv_ty.did()), path.ident.name) { + (Some(sym::Option), sym::is_some) => Some(Self::IsSome { receiver, side_effect_expr_span, - }) - } else if cx.tcx.is_diagnostic_item(sym::Result, recv_ty.did()) && path.ident.name == sym::is_ok { - Some(Self::IsOk { + }), + (Some(sym::Result), sym::is_ok) => Some(Self::IsOk { receiver, side_effect_expr_span, - }) - } else { - None + }), + _ => None, } } else if matching_root_macro_call(cx, expr.span, sym::matches_macro).is_some() // we know for a fact that the wildcard pattern is the second arm diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs b/src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs index 9a62b719a8f..fa8f9d640ee 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs @@ -24,32 +24,29 @@ fn get_iterator_length<'tcx>(cx: &LateContext<'tcx>, iter: &'tcx Expr<'tcx>) -> let ty::Adt(adt, substs) = cx.typeck_results().expr_ty(iter).kind() else { return None; }; - let did = adt.did(); - if cx.tcx.is_diagnostic_item(sym::ArrayIntoIter, did) { - // For array::IntoIter<T, const N: usize>, the length is the second generic - // parameter. - substs.const_at(1).try_to_target_usize(cx.tcx).map(u128::from) - } else if cx.tcx.is_diagnostic_item(sym::SliceIter, did) - && let ExprKind::MethodCall(_, recv, ..) = iter.kind - { - if let ty::Array(_, len) = cx.typeck_results().expr_ty(recv).peel_refs().kind() { - // For slice::Iter<'_, T>, the receiver might be an array literal: [1,2,3].iter().skip(..) - len.try_to_target_usize(cx.tcx).map(u128::from) - } else if let Some(args) = VecArgs::hir(cx, expr_or_init(cx, recv)) { - match args { - VecArgs::Vec(vec) => vec.len().try_into().ok(), - VecArgs::Repeat(_, len) => expr_as_u128(cx, len), + match cx.tcx.get_diagnostic_name(adt.did()) { + Some(sym::ArrayIntoIter) => { + // For array::IntoIter<T, const N: usize>, the length is the second generic + // parameter. + substs.const_at(1).try_to_target_usize(cx.tcx).map(u128::from) + }, + Some(sym::SliceIter) if let ExprKind::MethodCall(_, recv, ..) = iter.kind => { + if let ty::Array(_, len) = cx.typeck_results().expr_ty(recv).peel_refs().kind() { + // For slice::Iter<'_, T>, the receiver might be an array literal: [1,2,3].iter().skip(..) + len.try_to_target_usize(cx.tcx).map(u128::from) + } else if let Some(args) = VecArgs::hir(cx, expr_or_init(cx, recv)) { + match args { + VecArgs::Vec(vec) => vec.len().try_into().ok(), + VecArgs::Repeat(_, len) => expr_as_u128(cx, len), + } + } else { + None } - } else { - None - } - } else if cx.tcx.is_diagnostic_item(sym::IterEmpty, did) { - Some(0) - } else if cx.tcx.is_diagnostic_item(sym::IterOnce, did) { - Some(1) - } else { - None + }, + Some(sym::IterEmpty) => Some(0), + Some(sym::IterOnce) => Some(1), + _ => None, } } diff --git a/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs b/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs index f7bb8c1d696..750f933330a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs @@ -50,10 +50,10 @@ fn try_get_caller_ty_name_and_method_name( } } else { if let ty::Adt(adt, _) = cx.typeck_results().expr_ty(caller_expr).kind() { - if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) { - return Some(("Option", "and_then")); - } else if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) { - return Some(("Result", "and_then")); + match cx.tcx.get_diagnostic_name(adt.did()) { + Some(sym::Option) => return Some(("Option", "and_then")), + Some(sym::Result) => return Some(("Result", "and_then")), + _ => {}, } } None diff --git a/src/tools/clippy/clippy_lints/src/methods/map_identity.rs b/src/tools/clippy/clippy_lints/src/methods/map_identity.rs index 98def66ca14..a98cfff8bfb 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_identity.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_identity.rs @@ -1,14 +1,16 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_expr_untyped_identity_function, is_trait_method, path_to_local}; -use rustc_ast::BindingMode; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::{is_copy, is_type_diagnostic_item}; +use clippy_utils::{is_expr_untyped_identity_function, is_mutable, is_trait_method, path_to_local_with_projections}; use rustc_errors::Applicability; -use rustc_hir::{self as hir, Node, PatKind}; -use rustc_lint::LateContext; +use rustc_hir::{self as hir, ExprKind, Node, PatKind}; +use rustc_lint::{LateContext, LintContext}; use rustc_span::{Span, Symbol, sym}; use super::MAP_IDENTITY; +const MSG: &str = "unnecessary map of the identity function"; + pub(super) fn check( cx: &LateContext<'_>, expr: &hir::Expr<'_>, @@ -23,26 +25,70 @@ pub(super) fn check( || is_type_diagnostic_item(cx, caller_ty, sym::Result) || is_type_diagnostic_item(cx, caller_ty, sym::Option)) && is_expr_untyped_identity_function(cx, map_arg) - && let Some(sugg_span) = expr.span.trim_start(caller.span) + && let Some(call_span) = expr.span.trim_start(caller.span) { - // If the result of `.map(identity)` is used as a mutable reference, - // the caller must not be an immutable binding. - if cx.typeck_results().expr_ty_adjusted(expr).is_mutable_ptr() - && let Some(hir_id) = path_to_local(caller) - && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) - && !matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..)) - { - return; - } + let main_sugg = (call_span, String::new()); + let mut app = if is_copy(cx, caller_ty) { + // there is technically a behavioral change here for `Copy` iterators, where + // `iter.map(|x| x).next()` would mutate a temporary copy of the iterator and + // changing it to `iter.next()` mutates iter directly + Applicability::Unspecified + } else { + Applicability::MachineApplicable + }; + + let needs_to_be_mutable = cx.typeck_results().expr_ty_adjusted(expr).is_mutable_ptr(); + if needs_to_be_mutable && !is_mutable(cx, caller) { + if let Some(hir_id) = path_to_local_with_projections(caller) + && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) + && let PatKind::Binding(_, _, ident, _) = pat.kind + { + // We can reach the binding -- suggest making it mutable + let suggs = vec![main_sugg, (ident.span.shrink_to_lo(), String::from("mut "))]; + + let ident = snippet_with_applicability(cx.sess(), ident.span, "_", &mut app); - span_lint_and_sugg( - cx, - MAP_IDENTITY, - sugg_span, - "unnecessary map of the identity function", - format!("remove the call to `{name}`"), - String::new(), - Applicability::MachineApplicable, - ); + span_lint_and_then(cx, MAP_IDENTITY, call_span, MSG, |diag| { + diag.multipart_suggestion( + format!("remove the call to `{name}`, and make `{ident}` mutable"), + suggs, + app, + ); + }); + } else { + // If we can't make the binding mutable, prevent the suggestion from being automatically applied, + // and add a complementary help message. + app = Applicability::Unspecified; + + let method_requiring_mut = if let Node::Expr(expr) = cx.tcx.parent_hir_node(expr.hir_id) + && let ExprKind::MethodCall(method, ..) = expr.kind + { + Some(method.ident) + } else { + None + }; + + span_lint_and_then(cx, MAP_IDENTITY, call_span, MSG, |diag| { + diag.span_suggestion(main_sugg.0, format!("remove the call to `{name}`"), main_sugg.1, app); + + let note = if let Some(method_requiring_mut) = method_requiring_mut { + format!("this must be made mutable to use `{method_requiring_mut}`") + } else { + "this must be made mutable".to_string() + }; + diag.span_note(caller.span, note); + }); + } + } else { + span_lint_and_sugg( + cx, + MAP_IDENTITY, + main_sugg.0, + MSG, + format!("remove the call to `{name}`"), + main_sugg.1, + app, + ); + } } } diff --git a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs index 63ee922acfa..906ead16fd0 100644 --- a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs @@ -38,17 +38,13 @@ pub(super) fn check( ]; let is_deref = match map_arg.kind { - hir::ExprKind::Path(ref expr_qpath) => { - cx.qpath_res(expr_qpath, map_arg.hir_id) - .opt_def_id() - .is_some_and(|fun_def_id| { - cx.tcx.is_diagnostic_item(sym::deref_method, fun_def_id) - || cx.tcx.is_diagnostic_item(sym::deref_mut_method, fun_def_id) - || deref_aliases - .iter() - .any(|&sym| cx.tcx.is_diagnostic_item(sym, fun_def_id)) - }) - }, + hir::ExprKind::Path(ref expr_qpath) => cx + .qpath_res(expr_qpath, map_arg.hir_id) + .opt_def_id() + .and_then(|fun_def_id| cx.tcx.get_diagnostic_name(fun_def_id)) + .is_some_and(|fun_name| { + matches!(fun_name, sym::deref_method | sym::deref_mut_method) || deref_aliases.contains(&fun_name) + }), hir::ExprKind::Closure(&hir::Closure { body, .. }) => { let closure_body = cx.tcx.hir_body(body); let closure_expr = peel_blocks(closure_body.value); @@ -63,13 +59,11 @@ pub(super) fn check( .map(|x| &x.kind) .collect::<Box<[_]>>() && let [ty::adjustment::Adjust::Deref(None), ty::adjustment::Adjust::Borrow(_)] = *adj + && let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap() + && let Some(method_name) = cx.tcx.get_diagnostic_name(method_did) { - let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap(); - cx.tcx.is_diagnostic_item(sym::deref_method, method_did) - || cx.tcx.is_diagnostic_item(sym::deref_mut_method, method_did) - || deref_aliases - .iter() - .any(|&sym| cx.tcx.is_diagnostic_item(sym, method_did)) + matches!(method_name, sym::deref_method | sym::deref_mut_method) + || deref_aliases.contains(&method_name) } else { false } diff --git a/src/tools/clippy/clippy_lints/src/methods/or_then_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/or_then_unwrap.rs index 3e64e15dc86..1a760ea733d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/or_then_unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/methods/or_then_unwrap.rs @@ -62,7 +62,7 @@ fn get_content_if_ctor_matches(cx: &LateContext<'_>, expr: &Expr<'_>, item: Lang if let ExprKind::Call(some_expr, [arg]) = expr.kind && is_res_lang_ctor(cx, path_res(cx, some_expr), item) { - Some(arg.span) + Some(arg.span.source_callsite()) } else { None } diff --git a/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs b/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs index 407f2e80aff..6738bbf0a12 100644 --- a/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs +++ b/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs @@ -31,8 +31,8 @@ fn parse_fails_on_trailing_newline(ty: Ty<'_>) -> bool { } pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) { - if let Some(recv_adt) = cx.typeck_results().expr_ty(recv).ty_adt_def() - && cx.tcx.is_diagnostic_item(sym::Stdin, recv_adt.did()) + let recv_ty = cx.typeck_results().expr_ty(recv); + if is_type_diagnostic_item(cx, recv_ty, sym::Stdin) && let ExprKind::Path(QPath::Resolved(_, path)) = arg.peel_borrows().kind && let Res::Local(local_id) = path.res { diff --git a/src/tools/clippy/clippy_lints/src/methods/single_char_add_str.rs b/src/tools/clippy/clippy_lints/src/methods/single_char_add_str.rs index ccdf5529d53..ef3d7acdc01 100644 --- a/src/tools/clippy/clippy_lints/src/methods/single_char_add_str.rs +++ b/src/tools/clippy/clippy_lints/src/methods/single_char_add_str.rs @@ -5,10 +5,10 @@ use rustc_span::sym; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { - if cx.tcx.is_diagnostic_item(sym::string_push_str, fn_def_id) { - single_char_push_string::check(cx, expr, receiver, args); - } else if cx.tcx.is_diagnostic_item(sym::string_insert_str, fn_def_id) { - single_char_insert_string::check(cx, expr, receiver, args); + match cx.tcx.get_diagnostic_name(fn_def_id) { + Some(sym::string_push_str) => single_char_push_string::check(cx, expr, receiver, args), + Some(sym::string_insert_str) => single_char_insert_string::check(cx, expr, receiver, args), + _ => {}, } } } diff --git a/src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs index ce7aefed01f..ffc237e3c24 100644 --- a/src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_diag_trait_item; use clippy_utils::source::snippet_with_context; +use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty::print::with_forced_trimmed_paths; -use rustc_middle::ty::{self}; use rustc_span::sym; use super::SUSPICIOUS_TO_OWNED; @@ -14,8 +14,7 @@ pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) - if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) && is_diag_trait_item(cx, method_def_id, sym::ToOwned) && let input_type = cx.typeck_results().expr_ty(expr) - && let ty::Adt(adt, _) = cx.typeck_results().expr_ty(expr).kind() - && cx.tcx.is_diagnostic_item(sym::Cow, adt.did()) + && is_type_diagnostic_item(cx, input_type, sym::Cow) { let mut app = Applicability::MaybeIncorrect; let recv_snip = snippet_with_context(cx, recv.span, expr.span.ctxt(), "..", &mut app).0; diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_min_or_max.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_min_or_max.rs index 413881d5ec9..b87d81b7102 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_min_or_max.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_min_or_max.rs @@ -22,7 +22,8 @@ pub(super) fn check<'tcx>( let typeck_results = cx.typeck_results(); let ecx = ConstEvalCtxt::with_env(cx.tcx, cx.typing_env(), typeck_results); if let Some(id) = typeck_results.type_dependent_def_id(expr.hir_id) - && (cx.tcx.is_diagnostic_item(sym::cmp_ord_min, id) || cx.tcx.is_diagnostic_item(sym::cmp_ord_max, id)) + && let Some(fn_name) = cx.tcx.get_diagnostic_name(id) + && matches!(fn_name, sym::cmp_ord_min | sym::cmp_ord_max) { if let Some((left, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(recv) && let Some((right, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(arg) diff --git a/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs b/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs index af466fe091c..af4ade3cc0f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::{SpanRangeExt, snippet}; +use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{expr_or_init, is_trait_method, pat_is_wild}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, FnDecl, PatKind, TyKind}; use rustc_lint::LateContext; -use rustc_middle::ty::AdtDef; use rustc_span::{Span, sym}; use crate::loops::UNUSED_ENUMERATE_INDEX; @@ -39,9 +39,8 @@ use crate::loops::UNUSED_ENUMERATE_INDEX; /// * `closure_arg`: The argument to the map function call containing the closure/function to apply pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, closure_arg: &Expr<'_>) { let recv_ty = cx.typeck_results().expr_ty(recv); - if let Some(recv_ty_defid) = recv_ty.ty_adt_def().map(AdtDef::did) - // If we call a method on a `std::iter::Enumerate` instance - && cx.tcx.is_diagnostic_item(sym::Enumerate, recv_ty_defid) + // If we call a method on a `std::iter::Enumerate` instance + if is_type_diagnostic_item(cx, recv_ty, sym::Enumerate) // If we are calling a method of the `Iterator` trait && is_trait_method(cx, call_expr, sym::Iterator) // And the map argument is a closure diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs index d02952eb487..28555a61090 100644 --- a/src/tools/clippy/clippy_lints/src/missing_inline.rs +++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::{span_lint, span_lint_hir}; use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, Attribute, find_attr}; @@ -64,14 +64,20 @@ declare_clippy_lint! { "detects missing `#[inline]` attribute for public callables (functions, trait methods, methods...)" } -fn check_missing_inline_attrs(cx: &LateContext<'_>, attrs: &[Attribute], sp: Span, desc: &'static str) { +fn check_missing_inline_attrs( + cx: &LateContext<'_>, + attrs: &[Attribute], + sp: Span, + desc: &'static str, + hir_id: Option<hir::HirId>, +) { if !find_attr!(attrs, AttributeKind::Inline(..)) { - span_lint( - cx, - MISSING_INLINE_IN_PUBLIC_ITEMS, - sp, - format!("missing `#[inline]` for {desc}"), - ); + let msg = format!("missing `#[inline]` for {desc}"); + if let Some(hir_id) = hir_id { + span_lint_hir(cx, MISSING_INLINE_IN_PUBLIC_ITEMS, hir_id, sp, msg); + } else { + span_lint(cx, MISSING_INLINE_IN_PUBLIC_ITEMS, sp, msg); + } } } @@ -103,17 +109,9 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { let desc = "a function"; let attrs = cx.tcx.hir_attrs(it.hir_id()); - check_missing_inline_attrs(cx, attrs, it.span, desc); + check_missing_inline_attrs(cx, attrs, it.span, desc, None); }, - hir::ItemKind::Trait( - ref _constness, - ref _is_auto, - ref _unsafe, - _ident, - _generics, - _bounds, - trait_items, - ) => { + hir::ItemKind::Trait(.., trait_items) => { // note: we need to check if the trait is exported so we can't use // `LateLintPass::check_trait_item` here. for &tit in trait_items { @@ -127,7 +125,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { let desc = "a default trait method"; let item = cx.tcx.hir_trait_item(tit); let attrs = cx.tcx.hir_attrs(item.hir_id()); - check_missing_inline_attrs(cx, attrs, item.span, desc); + check_missing_inline_attrs(cx, attrs, item.span, desc, Some(tit.hir_id())); } }, } @@ -182,7 +180,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { } let attrs = cx.tcx.hir_attrs(impl_item.hir_id()); - check_missing_inline_attrs(cx, attrs, impl_item.span, desc); + check_missing_inline_attrs(cx, attrs, impl_item.span, desc, None); } } diff --git a/src/tools/clippy/clippy_lints/src/mut_reference.rs b/src/tools/clippy/clippy_lints/src/mut_reference.rs index 31f51b45754..ec93ef97cfa 100644 --- a/src/tools/clippy/clippy_lints/src/mut_reference.rs +++ b/src/tools/clippy/clippy_lints/src/mut_reference.rs @@ -1,4 +1,6 @@ -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::sugg::Sugg; +use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; @@ -83,13 +85,18 @@ fn check_arguments<'tcx>( let parameters = type_definition.fn_sig(cx.tcx).skip_binder().inputs(); for (argument, parameter) in iter::zip(arguments, parameters) { if let ty::Ref(_, _, Mutability::Not) | ty::RawPtr(_, Mutability::Not) = parameter.kind() - && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _) = argument.kind + && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, arg) = argument.kind { - span_lint( + let mut applicability = Applicability::MachineApplicable; + let sugg = Sugg::hir_with_applicability(cx, arg, "_", &mut applicability).addr(); + span_lint_and_sugg( cx, UNNECESSARY_MUT_PASSED, argument.span, format!("the {fn_kind} `{name}` doesn't need a mutable reference"), + "remove this `mut`", + sugg.to_string(), + applicability, ); } } diff --git a/src/tools/clippy/clippy_lints/src/needless_bool.rs b/src/tools/clippy/clippy_lints/src/needless_bool.rs index 6ae26156bc4..854e927aa2f 100644 --- a/src/tools/clippy/clippy_lints/src/needless_bool.rs +++ b/src/tools/clippy/clippy_lints/src/needless_bool.rs @@ -2,16 +2,14 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::{ - SpanlessEq, get_parent_expr, higher, is_block_like, is_else_clause, is_expn_of, is_parent_stmt, - is_receiver_of_method_call, peel_blocks, peel_blocks_with_stmt, span_contains_comment, sym, + SpanlessEq, get_parent_expr, higher, is_block_like, is_else_clause, is_parent_stmt, is_receiver_of_method_call, + peel_blocks, peel_blocks_with_stmt, span_contains_comment, }; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::Span; -use rustc_span::source_map::Spanned; declare_clippy_lint! { /// ### What it does @@ -52,31 +50,6 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for expressions of the form `x == true`, - /// `x != true` and order comparisons such as `x < true` (or vice versa) and - /// suggest using the variable directly. - /// - /// ### Why is this bad? - /// Unnecessary code. - /// - /// ### Example - /// ```rust,ignore - /// if x == true {} - /// if y == false {} - /// ``` - /// use `x` directly: - /// ```rust,ignore - /// if x {} - /// if !y {} - /// ``` - #[clippy::version = "pre 1.29.0"] - pub BOOL_COMPARISON, - complexity, - "comparing a variable to a boolean, e.g., `if x == true` or `if x != true`" -} - -declare_clippy_lint! { - /// ### What it does /// Checks for expressions of the form `if c { x = true } else { x = false }` /// (or vice versa) and suggest assigning the variable directly from the /// condition. @@ -224,201 +197,6 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool { } } -declare_lint_pass!(BoolComparison => [BOOL_COMPARISON]); - -impl<'tcx> LateLintPass<'tcx> for BoolComparison { - fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if e.span.from_expansion() { - return; - } - - if let ExprKind::Binary(Spanned { node, .. }, ..) = e.kind { - let ignore_case = None::<(fn(_) -> _, &str)>; - let ignore_no_literal = None::<(fn(_, _) -> _, &str)>; - match node { - BinOpKind::Eq => { - let true_case = Some((|h| h, "equality checks against true are unnecessary")); - let false_case = Some(( - |h: Sugg<'tcx>| !h, - "equality checks against false can be replaced by a negation", - )); - check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal); - }, - BinOpKind::Ne => { - let true_case = Some(( - |h: Sugg<'tcx>| !h, - "inequality checks against true can be replaced by a negation", - )); - let false_case = Some((|h| h, "inequality checks against false are unnecessary")); - check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal); - }, - BinOpKind::Lt => check_comparison( - cx, - e, - ignore_case, - Some((|h| h, "greater than checks against false are unnecessary")), - Some(( - |h: Sugg<'tcx>| !h, - "less than comparison against true can be replaced by a negation", - )), - ignore_case, - Some(( - |l: Sugg<'tcx>, r: Sugg<'tcx>| (!l).bit_and(&r), - "order comparisons between booleans can be simplified", - )), - ), - BinOpKind::Gt => check_comparison( - cx, - e, - Some(( - |h: Sugg<'tcx>| !h, - "less than comparison against true can be replaced by a negation", - )), - ignore_case, - ignore_case, - Some((|h| h, "greater than checks against false are unnecessary")), - Some(( - |l: Sugg<'tcx>, r: Sugg<'tcx>| l.bit_and(&(!r)), - "order comparisons between booleans can be simplified", - )), - ), - _ => (), - } - } - } -} - -struct ExpressionInfoWithSpan { - one_side_is_unary_not: bool, - left_span: Span, - right_span: Span, -} - -fn is_unary_not(e: &Expr<'_>) -> (bool, Span) { - if let ExprKind::Unary(UnOp::Not, operand) = e.kind { - return (true, operand.span); - } - (false, e.span) -} - -fn one_side_is_unary_not<'tcx>(left_side: &'tcx Expr<'_>, right_side: &'tcx Expr<'_>) -> ExpressionInfoWithSpan { - let left = is_unary_not(left_side); - let right = is_unary_not(right_side); - - ExpressionInfoWithSpan { - one_side_is_unary_not: left.0 != right.0, - left_span: left.1, - right_span: right.1, - } -} - -fn check_comparison<'a, 'tcx>( - cx: &LateContext<'tcx>, - e: &'tcx Expr<'_>, - left_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>, - left_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>, - right_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>, - right_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>, - no_literal: Option<(impl FnOnce(Sugg<'a>, Sugg<'a>) -> Sugg<'a>, &'static str)>, -) { - if let ExprKind::Binary(op, left_side, right_side) = e.kind { - let (l_ty, r_ty) = ( - cx.typeck_results().expr_ty(left_side), - cx.typeck_results().expr_ty(right_side), - ); - if is_expn_of(left_side.span, sym::cfg).is_some() || is_expn_of(right_side.span, sym::cfg).is_some() { - return; - } - if l_ty.is_bool() && r_ty.is_bool() { - let mut applicability = Applicability::MachineApplicable; - // Eliminate parentheses in `e` by using the lo pos of lhs and hi pos of rhs, - // calling `source_callsite` make sure macros are handled correctly, see issue #9907 - let binop_span = left_side - .span - .source_callsite() - .with_hi(right_side.span.source_callsite().hi()); - - if op.node == BinOpKind::Eq { - let expression_info = one_side_is_unary_not(left_side, right_side); - if expression_info.one_side_is_unary_not { - span_lint_and_sugg( - cx, - BOOL_COMPARISON, - binop_span, - "this comparison might be written more concisely", - "try simplifying it as shown", - format!( - "{} != {}", - snippet_with_applicability( - cx, - expression_info.left_span.source_callsite(), - "..", - &mut applicability - ), - snippet_with_applicability( - cx, - expression_info.right_span.source_callsite(), - "..", - &mut applicability - ) - ), - applicability, - ); - } - } - - match (fetch_bool_expr(left_side), fetch_bool_expr(right_side)) { - (Some(true), None) => left_true.map_or((), |(h, m)| { - suggest_bool_comparison(cx, binop_span, right_side, applicability, m, h); - }), - (None, Some(true)) => right_true.map_or((), |(h, m)| { - suggest_bool_comparison(cx, binop_span, left_side, applicability, m, h); - }), - (Some(false), None) => left_false.map_or((), |(h, m)| { - suggest_bool_comparison(cx, binop_span, right_side, applicability, m, h); - }), - (None, Some(false)) => right_false.map_or((), |(h, m)| { - suggest_bool_comparison(cx, binop_span, left_side, applicability, m, h); - }), - (None, None) => no_literal.map_or((), |(h, m)| { - let left_side = Sugg::hir_with_applicability(cx, left_side, "..", &mut applicability); - let right_side = Sugg::hir_with_applicability(cx, right_side, "..", &mut applicability); - span_lint_and_sugg( - cx, - BOOL_COMPARISON, - binop_span, - m, - "try simplifying it as shown", - h(left_side, right_side).into_string(), - applicability, - ); - }), - _ => (), - } - } - } -} - -fn suggest_bool_comparison<'a, 'tcx>( - cx: &LateContext<'tcx>, - span: Span, - expr: &Expr<'_>, - mut app: Applicability, - message: &'static str, - conv_hint: impl FnOnce(Sugg<'a>) -> Sugg<'a>, -) { - let hint = Sugg::hir_with_context(cx, expr, span.ctxt(), "..", &mut app); - span_lint_and_sugg( - cx, - BOOL_COMPARISON, - span, - message, - "try simplifying it as shown", - conv_hint(hint).into_string(), - app, - ); -} - enum Expression { Bool(bool), RetBool(bool), diff --git a/src/tools/clippy/clippy_lints/src/needless_for_each.rs b/src/tools/clippy/clippy_lints/src/needless_for_each.rs index a67545e419c..3a6ccc2bca9 100644 --- a/src/tools/clippy/clippy_lints/src/needless_for_each.rs +++ b/src/tools/clippy/clippy_lints/src/needless_for_each.rs @@ -1,6 +1,6 @@ use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_expr}; -use rustc_hir::{Block, BlockCheckMode, Closure, Expr, ExprKind, Stmt, StmtKind}; +use rustc_hir::{Block, BlockCheckMode, Closure, Expr, ExprKind, Stmt, StmtKind, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::Span; @@ -70,12 +70,24 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach { && has_iter_method(cx, cx.typeck_results().expr_ty(iter_recv)).is_some() // Skip the lint if the body is not block because this is simpler than `for` loop. // e.g. `v.iter().for_each(f)` is simpler and clearer than using `for` loop. - && let ExprKind::Closure(&Closure { body, .. }) = for_each_arg.kind + && let ExprKind::Closure(&Closure { body, fn_decl, .. }) = for_each_arg.kind && let body = cx.tcx.hir_body(body) // Skip the lint if the body is not safe, so as not to suggest `for … in … unsafe {}` // and suggesting `for … in … { unsafe { } }` is a little ugly. && !matches!(body.value.kind, ExprKind::Block(Block { rules: BlockCheckMode::UnsafeBlock(_), .. }, ..)) { + let mut applicability = Applicability::MachineApplicable; + + // If any closure parameter has an explicit type specified, applying the lint would necessarily + // remove that specification, possibly breaking type inference + if fn_decl + .inputs + .iter() + .any(|input| matches!(input.kind, TyKind::Infer(..))) + { + applicability = Applicability::MaybeIncorrect; + } + let mut ret_collector = RetCollector::default(); ret_collector.visit_expr(body.value); @@ -84,18 +96,16 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach { return; } - let (mut applicability, ret_suggs) = if ret_collector.spans.is_empty() { - (Applicability::MachineApplicable, None) + let ret_suggs = if ret_collector.spans.is_empty() { + None } else { - ( - Applicability::MaybeIncorrect, - Some( - ret_collector - .spans - .into_iter() - .map(|span| (span, "continue".to_string())) - .collect(), - ), + applicability = Applicability::MaybeIncorrect; + Some( + ret_collector + .spans + .into_iter() + .map(|span| (span, "continue".to_string())) + .collect(), ) }; diff --git a/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs b/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs index 04b09276966..ba67dc62abb 100644 --- a/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs +++ b/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs @@ -134,7 +134,8 @@ impl LateLintPass<'_> for NonCanonicalImpls { return; } - if cx.tcx.is_diagnostic_item(sym::Clone, trait_impl.def_id) + let trait_name = cx.tcx.get_diagnostic_name(trait_impl.def_id); + if trait_name == Some(sym::Clone) && let Some(copy_def_id) = cx.tcx.get_diagnostic_item(sym::Copy) && implements_trait(cx, trait_impl.self_ty(), copy_def_id, &[]) { @@ -170,12 +171,8 @@ impl LateLintPass<'_> for NonCanonicalImpls { String::new(), Applicability::MaybeIncorrect, ); - - return; } - } - - if cx.tcx.is_diagnostic_item(sym::PartialOrd, trait_impl.def_id) + } else if trait_name == Some(sym::PartialOrd) && impl_item.ident.name == sym::partial_cmp && let Some(ord_def_id) = cx.tcx.get_diagnostic_item(sym::Ord) && implements_trait(cx, trait_impl.self_ty(), ord_def_id, &[]) diff --git a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs index 23a1622f30f..cb934466bd8 100644 --- a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs +++ b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs @@ -43,13 +43,11 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { match &expr.kind { ExprKind::MethodCall(path, func, [param], _) => { if let Some(adt) = cx.typeck_results().expr_ty(func).peel_refs().ty_adt_def() - && ((path.ident.name == sym::mode - && matches!( - cx.tcx.get_diagnostic_name(adt.did()), - Some(sym::FsOpenOptions | sym::DirBuilder) - )) - || (path.ident.name == sym::set_mode - && cx.tcx.is_diagnostic_item(sym::FsPermissions, adt.did()))) + && matches!( + (cx.tcx.get_diagnostic_name(adt.did()), path.ident.name), + (Some(sym::FsOpenOptions | sym::DirBuilder), sym::mode) + | (Some(sym::FsPermissions), sym::set_mode) + ) && let ExprKind::Lit(_) = param.kind && param.span.eq_ctxt(expr.span) && param diff --git a/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs b/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs index 7317c62df7f..2d303e40bd1 100644 --- a/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs +++ b/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::Msrv; +use clippy_utils::qualify_min_const_fn::is_stable_const_fn; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::implements_trait; use clippy_utils::visitors::for_each_expr_without_closures; @@ -20,7 +21,7 @@ pub(super) fn check<'tcx>( expr: &'tcx hir::Expr<'_>, assignee: &'tcx hir::Expr<'_>, e: &'tcx hir::Expr<'_>, - _msrv: Msrv, + msrv: Msrv, ) { if let hir::ExprKind::Binary(op, l, r) = &e.kind { let lint = |assignee: &hir::Expr<'_>, rhs: &hir::Expr<'_>| { @@ -43,10 +44,28 @@ pub(super) fn check<'tcx>( } } - // Skip if the trait is not stable in const contexts - // FIXME: reintroduce a better check after this is merged back into Clippy + // Skip if the trait or the implementation is not stable in const contexts if is_in_const_context(cx) { - return; + if cx + .tcx + .associated_item_def_ids(trait_id) + .first() + .is_none_or(|binop_id| !is_stable_const_fn(cx, *binop_id, msrv)) + { + return; + } + + let impls = cx.tcx.non_blanket_impls_for_ty(trait_id, rty).collect::<Vec<_>>(); + if impls.is_empty() + || impls.into_iter().any(|impl_id| { + cx.tcx + .associated_item_def_ids(impl_id) + .first() + .is_none_or(|fn_id| !is_stable_const_fn(cx, *fn_id, msrv)) + }) + { + return; + } } span_lint_and_then( diff --git a/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs b/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs index 22ec4fe60fb..604f8f5da0b 100644 --- a/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs +++ b/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs @@ -47,14 +47,10 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) (arg, arg.span) }, ExprKind::Call(path, [arg]) - if path_def_id(cx, path).is_some_and(|did| { - if cx.tcx.is_diagnostic_item(sym::from_str_method, did) { - true - } else if cx.tcx.is_diagnostic_item(sym::from_fn, did) { - !is_copy(cx, typeck.expr_ty(expr)) - } else { - false - } + if path_def_id(cx, path).is_some_and(|did| match cx.tcx.get_diagnostic_name(did) { + Some(sym::from_str_method) => true, + Some(sym::from_fn) => !is_copy(cx, typeck.expr_ty(expr)), + _ => false, }) => { (arg, arg.span) diff --git a/src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs b/src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs index 047a5a0159c..17fa8017c97 100644 --- a/src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs +++ b/src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs @@ -34,10 +34,11 @@ pub(crate) fn check<'tcx>( val_r, ) = lhs.kind - // right hand side matches either f32::EPSILON or f64::EPSILON + // right hand side matches _::EPSILON && let ExprKind::Path(ref epsilon_path) = rhs.kind && let Res::Def(DefKind::AssocConst, def_id) = cx.qpath_res(epsilon_path, rhs.hir_id) - && ([sym::f32_epsilon, sym::f64_epsilon].into_iter().any(|sym| cx.tcx.is_diagnostic_item(sym, def_id))) + && let Some(sym) = cx.tcx.get_diagnostic_name(def_id) + && matches!(sym, sym::f16_epsilon | sym::f32_epsilon | sym::f64_epsilon | sym::f128_epsilon) // values of the subtractions on the left hand side are of the type float && let t_val_l = cx.typeck_results().expr_ty(val_l) diff --git a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs index 449d3da7639..43db0085f2e 100644 --- a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs +++ b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint; -use clippy_utils::is_in_test; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; +use clippy_utils::{is_in_test, is_inside_always_const_context}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; @@ -99,7 +99,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let Some(macro_call) = root_macro_call_first_node(cx, expr) { if is_panic(cx, macro_call.def_id) { - if cx.tcx.hir_is_inside_const_context(expr.hir_id) + if is_inside_always_const_context(cx.tcx, expr.hir_id) || self.allow_panic_in_tests && is_in_test(cx.tcx, expr.hir_id) { return; @@ -140,7 +140,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { && let Res::Def(DefKind::Fn, def_id) = expr_path.res && cx.tcx.is_diagnostic_item(sym::panic_any, def_id) { - if cx.tcx.hir_is_inside_const_context(expr.hir_id) + if is_inside_always_const_context(cx.tcx, expr.hir_id) || self.allow_panic_in_tests && is_in_test(cx.tcx, expr.hir_id) { return; diff --git a/src/tools/clippy/clippy_lints/src/redundant_clone.rs b/src/tools/clippy/clippy_lints/src/redundant_clone.rs index e57b8cc2d84..1d58cdd26d8 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_clone.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_clone.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::fn_has_unsatisfiable_preds; use clippy_utils::mir::{LocalUsage, PossibleBorrowerMap, visit_local_usage}; use clippy_utils::source::SpanRangeExt; -use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, is_type_lang_item, walk_ptrs_ty_depth}; +use clippy_utils::ty::{has_drop, is_copy, is_type_lang_item, walk_ptrs_ty_depth}; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl, LangItem, def_id}; @@ -96,14 +96,13 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { let (fn_def_id, arg, arg_ty, clone_ret) = unwrap_or_continue!(is_call_with_ref_arg(cx, mir, &terminator.kind)); + let fn_name = cx.tcx.get_diagnostic_name(fn_def_id); + let from_borrow = cx.tcx.lang_items().get(LangItem::CloneFn) == Some(fn_def_id) - || cx.tcx.is_diagnostic_item(sym::to_owned_method, fn_def_id) - || (cx.tcx.is_diagnostic_item(sym::to_string_method, fn_def_id) - && is_type_lang_item(cx, arg_ty, LangItem::String)); + || fn_name == Some(sym::to_owned_method) + || (fn_name == Some(sym::to_string_method) && is_type_lang_item(cx, arg_ty, LangItem::String)); - let from_deref = !from_borrow - && (cx.tcx.is_diagnostic_item(sym::path_to_pathbuf, fn_def_id) - || cx.tcx.is_diagnostic_item(sym::os_str_to_os_string, fn_def_id)); + let from_deref = !from_borrow && matches!(fn_name, Some(sym::path_to_pathbuf | sym::os_str_to_os_string)); if !from_borrow && !from_deref { continue; @@ -148,8 +147,9 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { is_call_with_ref_arg(cx, mir, &pred_terminator.kind) && res == cloned && cx.tcx.is_diagnostic_item(sym::deref_method, pred_fn_def_id) - && (is_type_diagnostic_item(cx, pred_arg_ty, sym::PathBuf) - || is_type_diagnostic_item(cx, pred_arg_ty, sym::OsString)) + && let ty::Adt(pred_arg_def, _) = pred_arg_ty.kind() + && let Some(pred_arg_name) = cx.tcx.get_diagnostic_name(pred_arg_def.did()) + && matches!(pred_arg_name, sym::PathBuf | sym::OsString) { (pred_arg, res) } else { diff --git a/src/tools/clippy/clippy_lints/src/semicolon_block.rs b/src/tools/clippy/clippy_lints/src/semicolon_block.rs index db91c57b181..1dea8f17c34 100644 --- a/src/tools/clippy/clippy_lints/src/semicolon_block.rs +++ b/src/tools/clippy/clippy_lints/src/semicolon_block.rs @@ -155,6 +155,11 @@ impl LateLintPass<'_> for SemicolonBlock { kind: ExprKind::Block(block, _), .. }) if !block.span.from_expansion() => { + let attrs = cx.tcx.hir_attrs(stmt.hir_id); + if !attrs.is_empty() && !cx.tcx.features().stmt_expr_attributes() { + return; + } + if let Some(tail) = block.expr { self.semicolon_inside_block(cx, block, tail, stmt.span); } diff --git a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs index e9534bc63a6..8c4a50041e6 100644 --- a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs +++ b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs @@ -126,6 +126,8 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports { && !is_from_proc_macro(cx, &first_segment.ident) && !matches!(def_kind, DefKind::Macro(_)) && let Some(last_segment) = path.segments.last() + && let Res::Def(DefKind::Mod, crate_def_id) = first_segment.res + && crate_def_id.is_crate_root() { let (lint, used_mod, replace_with) = match first_segment.ident.name { sym::std => match cx.tcx.crate_name(def_id.krate) { diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs index 490e6c974ae..57d5900b045 100644 --- a/src/tools/clippy/clippy_lints/src/strings.rs +++ b/src/tools/clippy/clippy_lints/src/strings.rs @@ -457,7 +457,8 @@ impl<'tcx> LateLintPass<'tcx> for TrimSplitWhitespace { } fn is_one_of_trim_diagnostic_items(cx: &LateContext<'_>, trim_def_id: DefId) -> bool { - cx.tcx.is_diagnostic_item(sym::str_trim, trim_def_id) - || cx.tcx.is_diagnostic_item(sym::str_trim_start, trim_def_id) - || cx.tcx.is_diagnostic_item(sym::str_trim_end, trim_def_id) + matches!( + cx.tcx.get_diagnostic_name(trim_def_id), + Some(sym::str_trim | sym::str_trim_start | sym::str_trim_end) + ) } diff --git a/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs b/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs index 08f36a2ed5d..543f3c45e14 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs @@ -1,8 +1,12 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use std::borrow::Cow; + +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::{HasSession, SpanRangeExt as _}; use rustc_errors::Applicability; -use rustc_hir::{GenericArg, HirId, LetStmt, Node, Path, TyKind}; +use rustc_hir::{Expr, GenericArg, HirId, LetStmt, Node, Path, TyKind}; use rustc_lint::LateContext; -use rustc_middle::ty::Ty; +use rustc_middle::ty::{self, Ty}; +use rustc_span::Span; use crate::transmute::MISSING_TRANSMUTE_ANNOTATIONS; @@ -38,6 +42,7 @@ fn is_function_block(cx: &LateContext<'_>, expr_hir_id: HirId) -> bool { pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, path: &Path<'tcx>, + arg: &Expr<'tcx>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>, expr_hir_id: HirId, @@ -68,14 +73,48 @@ pub(super) fn check<'tcx>( } else if is_function_block(cx, expr_hir_id) { return false; } - span_lint_and_sugg( + let span = last.ident.span.with_hi(path.span.hi()); + span_lint_and_then( cx, MISSING_TRANSMUTE_ANNOTATIONS, - last.ident.span.with_hi(path.span.hi()), + span, "transmute used without annotations", - "consider adding missing annotations", - format!("{}::<{from_ty}, {to_ty}>", last.ident), - Applicability::MaybeIncorrect, + |diag| { + let from_ty_no_name = ty_cannot_be_named(from_ty); + let to_ty_no_name = ty_cannot_be_named(to_ty); + if from_ty_no_name || to_ty_no_name { + let to_name = match (from_ty_no_name, to_ty_no_name) { + (true, false) => maybe_name_by_expr(cx, arg.span, "the origin type"), + (false, true) => "the destination type".into(), + _ => "the source and destination types".into(), + }; + diag.help(format!( + "consider giving {to_name} a name, and adding missing type annotations" + )); + } else { + diag.span_suggestion( + span, + "consider adding missing annotations", + format!("{}::<{from_ty}, {to_ty}>", last.ident), + Applicability::MaybeIncorrect, + ); + } + }, ); true } + +fn ty_cannot_be_named(ty: Ty<'_>) -> bool { + matches!( + ty.kind(), + ty::Alias(ty::AliasTyKind::Opaque | ty::AliasTyKind::Inherent, _) + ) +} + +fn maybe_name_by_expr<'a>(sess: &impl HasSession, span: Span, default: &'a str) -> Cow<'a, str> { + span.with_source_text(sess, |name| { + (name.len() + 9 < default.len()).then_some(format!("`{name}`'s type").into()) + }) + .flatten() + .unwrap_or(default.into()) +} diff --git a/src/tools/clippy/clippy_lints/src/transmute/mod.rs b/src/tools/clippy/clippy_lints/src/transmute/mod.rs index 1c7bb4314dd..5fda388259a 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/mod.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/mod.rs @@ -520,7 +520,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { | transmuting_null::check(cx, e, arg, to_ty) | transmute_null_to_fn::check(cx, e, arg, to_ty) | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, self.msrv) - | missing_transmute_annotations::check(cx, path, from_ty, to_ty, e.hir_id) + | missing_transmute_annotations::check(cx, path, arg, from_ty, to_ty, e.hir_id) | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg, self.msrv) | transmute_int_to_bool::check(cx, e, from_ty, to_ty, arg) diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs index f27aaa2fa77..2257aa1b73c 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs @@ -1,6 +1,6 @@ use super::TRANSMUTE_INT_TO_NON_ZERO; -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::sugg; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::sugg::Sugg; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -16,35 +16,24 @@ pub(super) fn check<'tcx>( to_ty: Ty<'tcx>, arg: &'tcx Expr<'_>, ) -> bool { - let tcx = cx.tcx; - - let (ty::Int(_) | ty::Uint(_), ty::Adt(adt, substs)) = (&from_ty.kind(), to_ty.kind()) else { - return false; - }; - - if !tcx.is_diagnostic_item(sym::NonZero, adt.did()) { - return false; + if let ty::Int(_) | ty::Uint(_) = from_ty.kind() + && let ty::Adt(adt, substs) = to_ty.kind() + && cx.tcx.is_diagnostic_item(sym::NonZero, adt.did()) + && let int_ty = substs.type_at(0) + && from_ty == int_ty + { + let arg = Sugg::hir(cx, arg, ".."); + span_lint_and_sugg( + cx, + TRANSMUTE_INT_TO_NON_ZERO, + e.span, + format!("transmute from a `{from_ty}` to a `{}<{int_ty}>`", sym::NonZero), + "consider using", + format!("{}::{}({arg})", sym::NonZero, sym::new_unchecked), + Applicability::Unspecified, + ); + true + } else { + false } - - let int_ty = substs.type_at(0); - if from_ty != int_ty { - return false; - } - - span_lint_and_then( - cx, - TRANSMUTE_INT_TO_NON_ZERO, - e.span, - format!("transmute from a `{from_ty}` to a `{}<{int_ty}>`", sym::NonZero), - |diag| { - let arg = sugg::Sugg::hir(cx, arg, ".."); - diag.span_suggestion( - e.span, - "consider using", - format!("{}::{}({arg})", sym::NonZero, sym::new_unchecked), - Applicability::Unspecified, - ); - }, - ); - true } diff --git a/src/tools/clippy/clippy_lints/src/types/rc_buffer.rs b/src/tools/clippy/clippy_lints/src/types/rc_buffer.rs index d691f1878b1..c4fd0fbf87a 100644 --- a/src/tools/clippy/clippy_lints/src/types/rc_buffer.rs +++ b/src/tools/clippy/clippy_lints/src/types/rc_buffer.rs @@ -11,7 +11,8 @@ use super::RC_BUFFER; pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { let app = Applicability::Unspecified; - if cx.tcx.is_diagnostic_item(sym::Rc, def_id) { + let name = cx.tcx.get_diagnostic_name(def_id); + if name == Some(sym::Rc) { if let Some(alternate) = match_buffer_type(cx, qpath) { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then( @@ -56,7 +57,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ ); return true; } - } else if cx.tcx.is_diagnostic_item(sym::Arc, def_id) { + } else if name == Some(sym::Arc) { if let Some(alternate) = match_buffer_type(cx, qpath) { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then( diff --git a/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs b/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs index de3456a8ba5..0ba51daf027 100644 --- a/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs +++ b/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs @@ -13,14 +13,11 @@ use super::{REDUNDANT_ALLOCATION, utils}; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'tcx>, qpath: &QPath<'tcx>, def_id: DefId) -> bool { let mut applicability = Applicability::MaybeIncorrect; - let outer_sym = if Some(def_id) == cx.tcx.lang_items().owned_box() { - "Box" - } else if cx.tcx.is_diagnostic_item(sym::Rc, def_id) { - "Rc" - } else if cx.tcx.is_diagnostic_item(sym::Arc, def_id) { - "Arc" - } else { - return false; + let outer_sym = match cx.tcx.get_diagnostic_name(def_id) { + _ if Some(def_id) == cx.tcx.lang_items().owned_box() => "Box", + Some(sym::Rc) => "Rc", + Some(sym::Arc) => "Arc", + _ => return false, }; if let Some(span) = utils::match_borrows_parameter(cx, qpath) { diff --git a/src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs b/src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs index 48b532968cb..38716519e23 100644 --- a/src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node}; -use clippy_utils::sym; +use clippy_utils::{is_unit_expr, sym}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; @@ -16,10 +16,13 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { sym::assert_ne_macro | sym::debug_assert_ne_macro => "fail", _ => return, }; - let Some((left, _, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { + let Some((lhs, rhs, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { return; }; - if !cx.typeck_results().expr_ty(left).is_unit() { + if is_unit_expr(lhs) || is_unit_expr(rhs) { + return; + } + if !cx.typeck_results().expr_ty(lhs).is_unit() { return; } span_lint( diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs b/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs index 7d996775a58..28f4884fa31 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs @@ -41,7 +41,8 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyStrings { && let ty::Ref(_, inner_str, _) = cx.typeck_results().expr_ty_adjusted(expr).kind() && inner_str.is_str() { - if cx.tcx.is_diagnostic_item(sym::string_new, fun_def_id) { + let fun_name = cx.tcx.get_diagnostic_name(fun_def_id); + if fun_name == Some(sym::string_new) { span_lint_and_sugg( cx, UNNECESSARY_OWNED_EMPTY_STRINGS, @@ -51,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyStrings { "\"\"".to_owned(), Applicability::MachineApplicable, ); - } else if cx.tcx.is_diagnostic_item(sym::from_fn, fun_def_id) + } else if fun_name == Some(sym::from_fn) && let [arg] = args && let ExprKind::Lit(spanned) = &arg.kind && let LitKind::Str(symbol, _) = spanned.node diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs index 54a7efc090a..849c0b438a5 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs @@ -107,12 +107,10 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { // Get the wrapper and inner types, if can't, abort. let (return_type_label, lang_item, inner_type) = if let ty::Adt(adt_def, subst) = return_ty(cx, hir_id.expect_owner()).kind() { - if cx.tcx.is_diagnostic_item(sym::Option, adt_def.did()) { - ("Option", OptionSome, subst.type_at(0)) - } else if cx.tcx.is_diagnostic_item(sym::Result, adt_def.did()) { - ("Result", ResultOk, subst.type_at(0)) - } else { - return; + match cx.tcx.get_diagnostic_name(adt_def.did()) { + Some(sym::Option) => ("Option", OptionSome, subst.type_at(0)), + Some(sym::Result) => ("Result", ResultOk, subst.type_at(0)), + _ => return, } } else { return; diff --git a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs index 8b278d98a30..f3410c98973 100644 --- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs @@ -326,7 +326,11 @@ fn extend_with_struct_pat( if idx_1 == idx { // In the case of `k`, we merely require identical field names // so that we will transform into `ident_k: p1_k | p2_k`. - let pos = fps2.iter().position(|fp2| eq_id(fp1.ident, fp2.ident)); + let pos = fps2.iter().position(|fp2| { + // Avoid `Foo { bar } | Foo { bar }` => `Foo { bar | bar }` + !(fp1.is_shorthand && fp2.is_shorthand) + && eq_id(fp1.ident, fp2.ident) + }); pos_in_2.set(pos); pos.is_some() } else { diff --git a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs index f3cd3f1bb28..af3ad4566c4 100644 --- a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs +++ b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs @@ -89,7 +89,9 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { { // We don't want to lint inside io::Read or io::Write implementations, as the author has more // information about their trait implementation than our lint, see https://github.com/rust-lang/rust-clippy/issues/4836 - if cx.tcx.is_diagnostic_item(sym::IoRead, trait_id) || cx.tcx.is_diagnostic_item(sym::IoWrite, trait_id) { + if let Some(trait_name) = cx.tcx.get_diagnostic_name(trait_id) + && matches!(trait_name, sym::IoRead | sym::IoWrite) + { return; } diff --git a/src/tools/clippy/clippy_lints/src/unused_unit.rs b/src/tools/clippy/clippy_lints/src/unused_unit.rs index 3811f0fe6b5..d503bd3379b 100644 --- a/src/tools/clippy/clippy_lints/src/unused_unit.rs +++ b/src/tools/clippy/clippy_lints/src/unused_unit.rs @@ -6,13 +6,13 @@ use rustc_errors::Applicability; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::FnKind; use rustc_hir::{ - AssocItemConstraintKind, Body, Expr, ExprKind, FnDecl, FnRetTy, GenericArgsParentheses, Node, PolyTraitRef, Term, - Ty, TyKind, + AssocItemConstraintKind, Body, Expr, ExprKind, FnDecl, FnRetTy, GenericArgsParentheses, PolyTraitRef, Term, Ty, + TyKind, }; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::edition::Edition; -use rustc_span::{BytePos, Span, sym}; +use rustc_span::{BytePos, Pos as _, Span, sym}; declare_clippy_lint! { /// ### What it does @@ -49,19 +49,22 @@ impl<'tcx> LateLintPass<'tcx> for UnusedUnit { decl: &'tcx FnDecl<'tcx>, body: &'tcx Body<'tcx>, span: Span, - def_id: LocalDefId, + _def_id: LocalDefId, ) { if let FnRetTy::Return(hir_ty) = decl.output && is_unit_ty(hir_ty) && !hir_ty.span.from_expansion() && get_def(span) == get_def(hir_ty.span) { - // implicit types in closure signatures are forbidden when `for<...>` is present - if let FnKind::Closure = kind - && let Node::Expr(expr) = cx.tcx.hir_node_by_def_id(def_id) - && let ExprKind::Closure(closure) = expr.kind - && !closure.bound_generic_params.is_empty() - { + // The explicit `-> ()` in the closure signature might be necessary for multiple reasons: + // - Implicit types in closure signatures are forbidden when `for<...>` is present + // - If the closure body ends with a function call, and that function's return type is generic, the + // `-> ()` could be required for it to be inferred + // + // There could be more reasons to have it, and, in general, we shouldn't discourage the users from + // writing more type annotations than strictly necessary, because it can help readability and + // maintainability + if let FnKind::Closure = kind { return; } @@ -97,16 +100,13 @@ impl<'tcx> LateLintPass<'tcx> for UnusedUnit { } fn check_poly_trait_ref(&mut self, cx: &LateContext<'tcx>, poly: &'tcx PolyTraitRef<'tcx>) { - let segments = &poly.trait_ref.path.segments; - - if segments.len() == 1 - && matches!(segments[0].ident.name, sym::Fn | sym::FnMut | sym::FnOnce) - && let Some(args) = segments[0].args + if let [segment] = &poly.trait_ref.path.segments + && matches!(segment.ident.name, sym::Fn | sym::FnMut | sym::FnOnce) + && let Some(args) = segment.args && args.parenthesized == GenericArgsParentheses::ParenSugar - && let constraints = &args.constraints - && constraints.len() == 1 - && constraints[0].ident.name == sym::Output - && let AssocItemConstraintKind::Equality { term: Term::Ty(hir_ty) } = constraints[0].kind + && let [constraint] = &args.constraints + && constraint.ident.name == sym::Output + && let AssocItemConstraintKind::Equality { term: Term::Ty(hir_ty) } = constraint.kind && args.span_ext.hi() != poly.span.hi() && !hir_ty.span.from_expansion() && args.span_ext.hi() != hir_ty.span.hi() @@ -160,17 +160,15 @@ fn get_def(span: Span) -> Option<Span> { fn lint_unneeded_unit_return(cx: &LateContext<'_>, ty_span: Span, span: Span) { let (ret_span, appl) = - span.with_hi(ty_span.hi()) - .get_source_text(cx) - .map_or((ty_span, Applicability::MaybeIncorrect), |src| { - position_before_rarrow(&src).map_or((ty_span, Applicability::MaybeIncorrect), |rpos| { - ( - #[expect(clippy::cast_possible_truncation)] - ty_span.with_lo(BytePos(span.lo().0 + rpos as u32)), - Applicability::MachineApplicable, - ) - }) - }); + if let Some(Some(rpos)) = span.with_hi(ty_span.hi()).with_source_text(cx, position_before_rarrow) { + ( + ty_span.with_lo(span.lo() + BytePos::from_usize(rpos)), + Applicability::MachineApplicable, + ) + } else { + (ty_span, Applicability::MaybeIncorrect) + }; + span_lint_and_sugg( cx, UNUSED_UNIT, diff --git a/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs b/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs index 7bec212a23c..f26647fa348 100644 --- a/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs +++ b/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs @@ -1,13 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::visitors::for_each_expr; -use clippy_utils::{method_chain_args, return_ty}; -use core::ops::ControlFlow; -use rustc_hir as hir; -use rustc_hir::ImplItemKind; +use clippy_utils::{return_ty, sym}; +use rustc_hir::{ + Body, BodyOwnerKind, Expr, ExprKind, FnSig, ImplItem, ImplItemKind, Item, ItemKind, OwnerId, PathSegment, QPath, +}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; -use rustc_span::{Span, sym}; +use rustc_middle::ty::Ty; +use rustc_session::impl_lint_pass; +use rustc_span::{Ident, Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -57,62 +56,165 @@ declare_clippy_lint! { "functions of type `Result<..>` or `Option`<...> that contain `expect()` or `unwrap()`" } -declare_lint_pass!(UnwrapInResult=> [UNWRAP_IN_RESULT]); +impl_lint_pass!(UnwrapInResult=> [UNWRAP_IN_RESULT]); -impl<'tcx> LateLintPass<'tcx> for UnwrapInResult { - fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { - if let ImplItemKind::Fn(ref _signature, _) = impl_item.kind - // first check if it's a method or function - // checking if its return type is `result` or `option` - && (is_type_diagnostic_item(cx, return_ty(cx, impl_item.owner_id), sym::Result) - || is_type_diagnostic_item(cx, return_ty(cx, impl_item.owner_id), sym::Option)) - { - lint_impl_body(cx, impl_item.span, impl_item); +#[derive(Clone, Copy, Eq, PartialEq)] +enum OptionOrResult { + Option, + Result, +} + +impl OptionOrResult { + fn with_article(self) -> &'static str { + match self { + Self::Option => "an `Option`", + Self::Result => "a `Result`", } } } -fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tcx hir::ImplItem<'_>) { - if let ImplItemKind::Fn(_, body_id) = impl_item.kind { - let body = cx.tcx.hir_body(body_id); - let typeck = cx.tcx.typeck(impl_item.owner_id.def_id); - let mut result = Vec::new(); - let _: Option<!> = for_each_expr(cx, body.value, |e| { - // check for `expect` - if let Some(arglists) = method_chain_args(e, &[sym::expect]) { - let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs(); - if is_type_diagnostic_item(cx, receiver_ty, sym::Option) - || is_type_diagnostic_item(cx, receiver_ty, sym::Result) - { - result.push(e.span); - } - } - - // check for `unwrap` - if let Some(arglists) = method_chain_args(e, &[sym::unwrap]) { - let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs(); - if is_type_diagnostic_item(cx, receiver_ty, sym::Option) - || is_type_diagnostic_item(cx, receiver_ty, sym::Result) - { - result.push(e.span); - } - } - - ControlFlow::Continue(()) +struct OptionOrResultFn { + kind: OptionOrResult, + return_ty_span: Option<Span>, +} + +#[derive(Default)] +pub struct UnwrapInResult { + fn_stack: Vec<Option<OptionOrResultFn>>, + current_fn: Option<OptionOrResultFn>, +} + +impl UnwrapInResult { + fn enter_item(&mut self, cx: &LateContext<'_>, fn_def_id: OwnerId, sig: &FnSig<'_>) { + self.fn_stack.push(self.current_fn.take()); + self.current_fn = is_option_or_result(cx, return_ty(cx, fn_def_id)).map(|kind| OptionOrResultFn { + kind, + return_ty_span: Some(sig.decl.output.span()), }); + } - // if we've found one, lint - if !result.is_empty() { + fn leave_item(&mut self) { + self.current_fn = self.fn_stack.pop().unwrap(); + } +} + +impl<'tcx> LateLintPass<'tcx> for UnwrapInResult { + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) { + if let ImplItemKind::Fn(sig, _) = &impl_item.kind { + self.enter_item(cx, impl_item.owner_id, sig); + } + } + + fn check_impl_item_post(&mut self, _: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'tcx>) { + if let ImplItemKind::Fn(..) = impl_item.kind { + self.leave_item(); + } + } + + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if let ItemKind::Fn { + has_body: true, sig, .. + } = &item.kind + { + self.enter_item(cx, item.owner_id, sig); + } + } + + fn check_item_post(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if let ItemKind::Fn { has_body: true, .. } = item.kind { + self.leave_item(); + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { + if expr.span.from_expansion() { + return; + } + + if let Some(OptionOrResultFn { + kind, + ref mut return_ty_span, + }) = self.current_fn + && let Some((oor, fn_name)) = is_unwrap_or_expect_call(cx, expr) + && oor == kind + { span_lint_and_then( cx, UNWRAP_IN_RESULT, - impl_span, - "used unwrap or expect in a function that returns result or option", - move |diag| { - diag.help("unwrap and expect should not be used in a function that returns result or option"); - diag.span_note(result, "potential non-recoverable error(s)"); + expr.span, + format!("`{fn_name}` used in a function that returns {}", kind.with_article()), + |diag| { + // Issue the note and help only once per function + if let Some(span) = return_ty_span.take() { + diag.span_note(span, "in this function signature"); + let complement = if kind == OptionOrResult::Result { + " or calling the `.map_err()` method" + } else { + "" + }; + diag.help(format!("consider using the `?` operator{complement}")); + } }, ); } } + + fn check_body(&mut self, cx: &LateContext<'tcx>, body: &Body<'tcx>) { + let body_def_id = cx.tcx.hir_body_owner_def_id(body.id()); + if !matches!(cx.tcx.hir_body_owner_kind(body_def_id), BodyOwnerKind::Fn) { + // When entering a body which is not a function, mask the potential surrounding + // function to not apply the lint. + self.fn_stack.push(self.current_fn.take()); + } + } + + fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &Body<'tcx>) { + let body_def_id = cx.tcx.hir_body_owner_def_id(body.id()); + if !matches!(cx.tcx.hir_body_owner_kind(body_def_id), BodyOwnerKind::Fn) { + // Unmask the potential surrounding function. + self.current_fn = self.fn_stack.pop().unwrap(); + } + } +} + +fn is_option_or_result(cx: &LateContext<'_>, ty: Ty<'_>) -> Option<OptionOrResult> { + match ty.ty_adt_def().and_then(|def| cx.tcx.get_diagnostic_name(def.did())) { + Some(sym::Option) => Some(OptionOrResult::Option), + Some(sym::Result) => Some(OptionOrResult::Result), + _ => None, + } +} + +fn is_unwrap_or_expect_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<(OptionOrResult, Symbol)> { + if let ExprKind::Call(func, _) = expr.kind + && let ExprKind::Path(QPath::TypeRelative( + hir_ty, + PathSegment { + ident: + Ident { + name: name @ (sym::unwrap | sym::expect), + .. + }, + .. + }, + )) = func.kind + { + is_option_or_result(cx, cx.typeck_results().node_type(hir_ty.hir_id)).map(|oor| (oor, *name)) + } else if let ExprKind::MethodCall( + PathSegment { + ident: Ident { + name: name @ (sym::unwrap | sym::expect), + .. + }, + .. + }, + recv, + _, + _, + ) = expr.kind + { + is_option_or_result(cx, cx.typeck_results().expr_ty_adjusted(recv)).map(|oor| (oor, *name)) + } else { + None + } } diff --git a/src/tools/clippy/clippy_lints/src/useless_conversion.rs b/src/tools/clippy/clippy_lints/src/useless_conversion.rs index e5b20c0e0a1..70ae982a445 100644 --- a/src/tools/clippy/clippy_lints/src/useless_conversion.rs +++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs @@ -386,12 +386,13 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { ExprKind::Call(path, [arg]) => { if let ExprKind::Path(ref qpath) = path.kind - && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() && !is_ty_alias(qpath) + && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() + && let Some(name) = cx.tcx.get_diagnostic_name(def_id) { let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(arg); - if cx.tcx.is_diagnostic_item(sym::try_from_fn, def_id) + if name == sym::try_from_fn && is_type_diagnostic_item(cx, a, sym::Result) && let ty::Adt(_, args) = a.kind() && let Some(a_type) = args.types().next() @@ -406,9 +407,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { None, hint, ); - } - - if cx.tcx.is_diagnostic_item(sym::from_fn, def_id) && same_type_and_consts(a, b) { + } else if name == sym::from_fn && same_type_and_consts(a, b) { let mut app = Applicability::MachineApplicable; let sugg = Sugg::hir_with_context(cx, arg, e.span.ctxt(), "<expr>", &mut app).maybe_paren(); let sugg_msg = format!("consider removing `{}()`", snippet(cx, path.span, "From::from")); diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index 2113cb92137..ece29362a39 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -754,7 +754,8 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { self.ident(name); sub.if_some(|p| self.pat(p)); }, - PatKind::Struct(ref qpath, fields, ignore) => { + PatKind::Struct(ref qpath, fields, etc) => { + let ignore = etc.is_some(); bind!(self, qpath, fields); kind!("Struct(ref {qpath}, {fields}, {ignore})"); self.qpath(qpath, pat); diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs index 7b6a25123e8..52b30ddce12 100644 --- a/src/tools/clippy/clippy_lints/src/vec.rs +++ b/src/tools/clippy/clippy_lints/src/vec.rs @@ -1,4 +1,6 @@ use std::collections::BTreeMap; +use std::collections::btree_map::Entry; +use std::mem; use std::ops::ControlFlow; use clippy_config::Conf; @@ -20,15 +22,36 @@ use rustc_span::{DesugaringKind, Span}; pub struct UselessVec { too_large_for_stack: u64, msrv: Msrv, - span_to_lint_map: BTreeMap<Span, Option<(HirId, SuggestedType, String, Applicability)>>, + /// Maps from a `vec![]` source callsite invocation span to the "state" (i.e., whether we can + /// emit a warning there or not). + /// + /// The purpose of this is to buffer lints up until `check_crate_post` so that we can cancel a + /// lint while visiting, because a `vec![]` invocation span can appear multiple times when + /// it is passed as a macro argument, once in a context that doesn't require a `Vec<_>` and + /// another time that does. Consider: + /// ``` + /// macro_rules! m { + /// ($v:expr) => { + /// let a = $v; + /// $v.push(3); + /// } + /// } + /// m!(vec![1, 2]); + /// ``` + /// The macro invocation expands to two `vec![1, 2]` invocations. If we eagerly suggest changing + /// the first `vec![1, 2]` (which is shared with the other expn) to an array which indeed would + /// work, we get a false positive warning on the `$v.push(3)` which really requires `$v` to + /// be a vector. + span_to_state: BTreeMap<Span, VecState>, allow_in_test: bool, } + impl UselessVec { pub fn new(conf: &'static Conf) -> Self { Self { too_large_for_stack: conf.too_large_for_stack, msrv: conf.msrv, - span_to_lint_map: BTreeMap::new(), + span_to_state: BTreeMap::new(), allow_in_test: conf.allow_useless_vec_in_tests, } } @@ -62,17 +85,28 @@ declare_clippy_lint! { impl_lint_pass!(UselessVec => [USELESS_VEC]); -impl<'tcx> LateLintPass<'tcx> for UselessVec { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let Some(vec_args) = higher::VecArgs::hir(cx, expr.peel_borrows()) else { - return; - }; - if self.allow_in_test && is_in_test(cx.tcx, expr.hir_id) { - return; - } - // the parent callsite of this `vec!` expression, or span to the borrowed one such as `&vec!` - let callsite = expr.span.parent_callsite().unwrap_or(expr.span); +/// The "state" of a `vec![]` invocation, indicating whether it can or cannot be changed. +enum VecState { + Change { + suggest_ty: SuggestedType, + vec_snippet: String, + expr_hir_id: HirId, + }, + NoChange, +} + +enum VecToArray { + /// Expression does not need to be a `Vec<_>` and its type can be changed to an array (or + /// slice). + Possible, + /// Expression must be a `Vec<_>`. Type cannot change. + Impossible, +} +impl UselessVec { + /// Checks if the surrounding environment requires this expression to actually be of type + /// `Vec<_>`, or if it can be changed to `&[]`/`[]` without causing type errors. + fn expr_usage_requires_vec(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) -> VecToArray { match cx.tcx.parent_hir_node(expr.hir_id) { // search for `let foo = vec![_]` expressions where all uses of `foo` // adjust to slices or call a method that exist on slices (e.g. len) @@ -100,110 +134,126 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { .is_continue(); if only_slice_uses { - self.check_vec_macro(cx, &vec_args, callsite, expr.hir_id, SuggestedType::Array); + VecToArray::Possible } else { - self.span_to_lint_map.insert(callsite, None); + VecToArray::Impossible } }, // if the local pattern has a specified type, do not lint. Node::LetStmt(LetStmt { ty: Some(_), .. }) if higher::VecArgs::hir(cx, expr).is_some() => { - self.span_to_lint_map.insert(callsite, None); + VecToArray::Impossible }, // search for `for _ in vec![...]` Node::Expr(Expr { span, .. }) if span.is_desugaring(DesugaringKind::ForLoop) && self.msrv.meets(cx, msrvs::ARRAY_INTO_ITERATOR) => { - let suggest_slice = suggest_type(expr); - self.check_vec_macro(cx, &vec_args, callsite, expr.hir_id, suggest_slice); + VecToArray::Possible }, // search for `&vec![_]` or `vec![_]` expressions where the adjusted type is `&[_]` _ => { - let suggest_slice = suggest_type(expr); - if adjusts_to_slice(cx, expr) { - self.check_vec_macro(cx, &vec_args, callsite, expr.hir_id, suggest_slice); + VecToArray::Possible } else { - self.span_to_lint_map.insert(callsite, None); + VecToArray::Impossible } }, } } +} + +impl<'tcx> LateLintPass<'tcx> for UselessVec { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let Some(vec_args) = higher::VecArgs::hir(cx, expr.peel_borrows()) + // The `vec![]` or `&vec![]` invocation span. + && let vec_span = expr.span.parent_callsite().unwrap_or(expr.span) + && !vec_span.from_expansion() + { + if self.allow_in_test && is_in_test(cx.tcx, expr.hir_id) { + return; + } + + match self.expr_usage_requires_vec(cx, expr) { + VecToArray::Possible => { + let suggest_ty = suggest_type(expr); + + // Size and `Copy` checks don't depend on the enclosing usage of the expression + // and don't need to be inserted into the state map. + let vec_snippet = match vec_args { + higher::VecArgs::Repeat(expr, len) => { + if is_copy(cx, cx.typeck_results().expr_ty(expr)) + && let Some(Constant::Int(length)) = ConstEvalCtxt::new(cx).eval(len) + && let Ok(length) = u64::try_from(length) + && size_of(cx, expr) + .checked_mul(length) + .is_some_and(|size| size <= self.too_large_for_stack) + { + suggest_ty.snippet( + cx, + Some(expr.span.source_callsite()), + Some(len.span.source_callsite()), + ) + } else { + return; + } + }, + higher::VecArgs::Vec(args) => { + if let Ok(length) = u64::try_from(args.len()) + && size_of(cx, expr) + .checked_mul(length) + .is_some_and(|size| size <= self.too_large_for_stack) + { + suggest_ty.snippet( + cx, + args.first().zip(args.last()).map(|(first, last)| { + first.span.source_callsite().to(last.span.source_callsite()) + }), + None, + ) + } else { + return; + } + }, + }; + + if let Entry::Vacant(entry) = self.span_to_state.entry(vec_span) { + entry.insert(VecState::Change { + suggest_ty, + vec_snippet, + expr_hir_id: expr.hir_id, + }); + } + }, + VecToArray::Impossible => { + self.span_to_state.insert(vec_span, VecState::NoChange); + }, + } + } + } fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { - for (span, lint_opt) in &self.span_to_lint_map { - if let Some((hir_id, suggest_slice, snippet, applicability)) = lint_opt { - let help_msg = format!("you can use {} directly", suggest_slice.desc()); - span_lint_hir_and_then(cx, USELESS_VEC, *hir_id, *span, "useless use of `vec!`", |diag| { - // If the `vec!` macro contains comment, better not make the suggestion machine - // applicable as it would remove them. - let applicability = if *applicability != Applicability::Unspecified - && let source_map = cx.tcx.sess.source_map() - && span_contains_comment(source_map, *span) - { + for (span, state) in mem::take(&mut self.span_to_state) { + if let VecState::Change { + suggest_ty, + vec_snippet, + expr_hir_id, + } = state + { + span_lint_hir_and_then(cx, USELESS_VEC, expr_hir_id, span, "useless use of `vec!`", |diag| { + let help_msg = format!("you can use {} directly", suggest_ty.desc()); + // If the `vec!` macro contains comment, better not make the suggestion machine applicable as it + // would remove them. + let applicability = if span_contains_comment(cx.tcx.sess.source_map(), span) { Applicability::Unspecified } else { - *applicability + Applicability::MachineApplicable }; - diag.span_suggestion(*span, help_msg, snippet, applicability); + diag.span_suggestion(span, help_msg, vec_snippet, applicability); }); } } } } -impl UselessVec { - fn check_vec_macro<'tcx>( - &mut self, - cx: &LateContext<'tcx>, - vec_args: &higher::VecArgs<'tcx>, - span: Span, - hir_id: HirId, - suggest_slice: SuggestedType, - ) { - if span.from_expansion() { - return; - } - - let snippet = match *vec_args { - higher::VecArgs::Repeat(elem, len) => { - if let Some(Constant::Int(len_constant)) = ConstEvalCtxt::new(cx).eval(len) { - // vec![ty; N] works when ty is Clone, [ty; N] requires it to be Copy also - if !is_copy(cx, cx.typeck_results().expr_ty(elem)) { - return; - } - - #[expect(clippy::cast_possible_truncation)] - if len_constant as u64 * size_of(cx, elem) > self.too_large_for_stack { - return; - } - - suggest_slice.snippet(cx, Some(elem.span), Some(len.span)) - } else { - return; - } - }, - higher::VecArgs::Vec(args) => { - let args_span = if let Some(last) = args.iter().last() { - if args.len() as u64 * size_of(cx, last) > self.too_large_for_stack { - return; - } - Some(args[0].span.source_callsite().to(last.span.source_callsite())) - } else { - None - }; - suggest_slice.snippet(cx, args_span, None) - }, - }; - - self.span_to_lint_map.entry(span).or_insert(Some(( - hir_id, - suggest_slice, - snippet, - Applicability::MachineApplicable, - ))); - } -} - #[derive(Copy, Clone)] pub(crate) enum SuggestedType { /// Suggest using a slice `&[..]` / `&mut [..]` @@ -221,11 +271,17 @@ impl SuggestedType { } fn snippet(self, cx: &LateContext<'_>, args_span: Option<Span>, len_span: Option<Span>) -> String { + // Invariant of the lint as implemented: all spans are from the root context (and as a result, + // always trivially crate-local). + assert!(args_span.is_none_or(|s| !s.from_expansion())); + assert!(len_span.is_none_or(|s| !s.from_expansion())); + let maybe_args = args_span - .and_then(|sp| sp.get_source_text(cx)) + .map(|sp| sp.get_source_text(cx).expect("spans are always crate-local")) .map_or(String::new(), |x| x.to_owned()); let maybe_len = len_span - .and_then(|sp| sp.get_source_text(cx).map(|s| format!("; {s}"))) + .map(|sp| sp.get_source_text(cx).expect("spans are always crate-local")) + .map(|st| format!("; {st}")) .unwrap_or_default(); match self { diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs index a15e4e42e71..c55c5ec2f51 100644 --- a/src/tools/clippy/clippy_lints/src/write.rs +++ b/src/tools/clippy/clippy_lints/src/write.rs @@ -537,7 +537,7 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) { sug_span = Some(sug_span.unwrap_or(arg.expr.span).to(arg.expr.span)); - if let Some((_, index)) = positional_arg_piece_span(piece) { + if let Some((_, index)) = format_arg_piece_span(piece) { replaced_position.push(index); } @@ -569,16 +569,11 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) { } } -/// Extract Span and its index from the given `piece`, if it's positional argument. -fn positional_arg_piece_span(piece: &FormatArgsPiece) -> Option<(Span, usize)> { +/// Extract Span and its index from the given `piece` +fn format_arg_piece_span(piece: &FormatArgsPiece) -> Option<(Span, usize)> { match piece { FormatArgsPiece::Placeholder(FormatPlaceholder { - argument: - FormatArgPosition { - index: Ok(index), - kind: FormatArgPositionKind::Number, - .. - }, + argument: FormatArgPosition { index: Ok(index), .. }, span: Some(span), .. }) => Some((*span, *index)), diff --git a/src/tools/clippy/clippy_lints/src/zombie_processes.rs b/src/tools/clippy/clippy_lints/src/zombie_processes.rs index 6ab94a52210..a934d2094e0 100644 --- a/src/tools/clippy/clippy_lints/src/zombie_processes.rs +++ b/src/tools/clippy/clippy_lints/src/zombie_processes.rs @@ -1,5 +1,6 @@ use ControlFlow::{Break, Continue}; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{fn_def_id, get_enclosing_block, path_to_local_id}; use rustc_ast::Mutability; use rustc_ast::visit::visit_opt; @@ -58,8 +59,8 @@ declare_lint_pass!(ZombieProcesses => [ZOMBIE_PROCESSES]); impl<'tcx> LateLintPass<'tcx> for ZombieProcesses { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let ExprKind::Call(..) | ExprKind::MethodCall(..) = expr.kind - && let Some(child_adt) = cx.typeck_results().expr_ty(expr).ty_adt_def() - && cx.tcx.is_diagnostic_item(sym::Child, child_adt.did()) + && let child_ty = cx.typeck_results().expr_ty(expr) + && is_type_diagnostic_item(cx, child_ty, sym::Child) { match cx.tcx.parent_hir_node(expr.hir_id) { Node::LetStmt(local) @@ -177,8 +178,8 @@ impl<'tcx> Visitor<'tcx> for WaitFinder<'_, 'tcx> { Node::Expr(expr) if let ExprKind::AddrOf(_, Mutability::Not, _) = expr.kind => {}, Node::Expr(expr) if let Some(fn_did) = fn_def_id(self.cx, expr) - && (self.cx.tcx.is_diagnostic_item(sym::child_id, fn_did) - || self.cx.tcx.is_diagnostic_item(sym::child_kill, fn_did)) => {}, + && let Some(fn_name) = self.cx.tcx.get_diagnostic_name(fn_did) + && matches!(fn_name, sym::child_id | sym::child_kill) => {}, // Conservatively assume that all other kinds of nodes call `.wait()` somehow. _ => return Break(MaybeWait(ex.span)), @@ -351,9 +352,14 @@ fn check<'tcx>(cx: &LateContext<'tcx>, spawn_expr: &'tcx Expr<'tcx>, cause: Caus /// Checks if the given expression exits the process. fn is_exit_expression(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - fn_def_id(cx, expr).is_some_and(|fn_did| { - cx.tcx.is_diagnostic_item(sym::process_exit, fn_did) || cx.tcx.is_diagnostic_item(sym::process_abort, fn_did) - }) + if let Some(fn_did) = fn_def_id(cx, expr) + && let Some(fn_name) = cx.tcx.get_diagnostic_name(fn_did) + && matches!(fn_name, sym::process_exit | sym::process_abort) + { + true + } else { + false + } } #[derive(Debug)] diff --git a/src/tools/clippy/clippy_utils/README.md b/src/tools/clippy/clippy_utils/README.md index 2dfe28953d0..e01f563c49e 100644 --- a/src/tools/clippy/clippy_utils/README.md +++ b/src/tools/clippy/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: <!-- begin autogenerated nightly --> ``` -nightly-2025-08-22 +nightly-2025-09-04 ``` <!-- end autogenerated nightly --> diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs index 40c00568a3b..ad69e6eb184 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -21,7 +21,7 @@ pub fn is_useless_with_eq_exprs(kind: BinOpKind) -> bool { } /// Checks if each element in the first slice is contained within the latter as per `eq_fn`. -pub fn unordered_over<X>(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool { +pub fn unordered_over<X, Y>(left: &[X], right: &[Y], mut eq_fn: impl FnMut(&X, &Y) -> bool) -> bool { left.len() == right.len() && left.iter().all(|l| right.iter().any(|r| eq_fn(l, r))) } diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs index 4e0b00df950..bda28a663fb 100644 --- a/src/tools/clippy/clippy_utils/src/higher.rs +++ b/src/tools/clippy/clippy_utils/src/higher.rs @@ -287,23 +287,22 @@ impl<'a> VecArgs<'a> { && let ExprKind::Path(ref qpath) = fun.kind && is_expn_of(fun.span, sym::vec).is_some() && let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id() + && let Some(name) = cx.tcx.get_diagnostic_name(fun_def_id) { - return if cx.tcx.is_diagnostic_item(sym::vec_from_elem, fun_def_id) && args.len() == 2 { - // `vec![elem; size]` case - Some(VecArgs::Repeat(&args[0], &args[1])) - } else if cx.tcx.is_diagnostic_item(sym::slice_into_vec, fun_def_id) && args.len() == 1 { - // `vec![a, b, c]` case - if let ExprKind::Call(_, [arg]) = &args[0].kind - && let ExprKind::Array(args) = arg.kind + return match (name, args) { + (sym::vec_from_elem, [elem, size]) => { + // `vec![elem; size]` case + Some(VecArgs::Repeat(elem, size)) + }, + (sym::slice_into_vec, [slice]) + if let ExprKind::Call(_, [arg]) = slice.kind + && let ExprKind::Array(args) = arg.kind => { + // `vec![a, b, c]` case Some(VecArgs::Vec(args)) - } else { - None - } - } else if cx.tcx.is_diagnostic_item(sym::vec_new, fun_def_id) && args.is_empty() { - Some(VecArgs::Vec(&[])) - } else { - None + }, + (sym::vec_new, []) => Some(VecArgs::Vec(&[])), + _ => None, }; } diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 8160443f413..b79e15cd717 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -757,7 +757,7 @@ pub fn both_some_and<X, Y>(l: Option<X>, r: Option<Y>, mut pred: impl FnMut(X, Y } /// Checks if two slices are equal as per `eq_fn`. -pub fn over<X>(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool { +pub fn over<X, Y>(left: &[X], right: &[Y], mut eq_fn: impl FnMut(&X, &Y) -> bool) -> bool { left.len() == right.len() && left.iter().zip(right).all(|(x, y)| eq_fn(x, y)) } diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 8533fa85541..14b64eb4d54 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -102,9 +102,9 @@ use rustc_hir::hir_id::{HirIdMap, HirIdSet}; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr}; use rustc_hir::{ self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, CoroutineDesugaring, - CoroutineKind, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs, HirId, Impl, - ImplItem, ImplItemKind, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode, - Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem, + CoroutineKind, CoroutineSource, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs, + HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, + OwnerNode, Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp, def, find_attr, }; use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize}; @@ -126,6 +126,7 @@ use rustc_span::{InnerSpan, Span}; use source::{SpanRangeExt, walk_span_to_context}; use visitors::{Visitable, for_each_unconsumed_temporary}; +use crate::ast_utils::unordered_over; use crate::consts::{ConstEvalCtxt, Constant, mir_to_const}; use crate::higher::Range; use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type}; @@ -308,6 +309,7 @@ pub fn is_lang_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: LangItem) -> cx.tcx.lang_items().get(item) == Some(did) } +/// Checks if `expr` is an empty block or an empty tuple. pub fn is_unit_expr(expr: &Expr<'_>) -> bool { matches!( expr.kind, @@ -640,9 +642,8 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath< && let Some(impl_did) = cx.tcx.impl_of_assoc(def_id) && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def() { - return std_types_symbols.iter().any(|&symbol| { - cx.tcx.is_diagnostic_item(symbol, adt.did()) || Some(adt.did()) == cx.tcx.lang_items().string() - }); + return Some(adt.did()) == cx.tcx.lang_items().string() + || (cx.tcx.get_diagnostic_name(adt.did())).is_some_and(|adt_name| std_types_symbols.contains(&adt_name)); } false } @@ -1992,7 +1993,7 @@ pub fn is_expr_identity_of_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr< (PatKind::Tuple(pats, dotdot), ExprKind::Tup(tup)) if dotdot.as_opt_usize().is_none() && pats.len() == tup.len() => { - zip(pats, tup).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr, by_hir)) + over(pats, tup, |pat, expr| is_expr_identity_of_pat(cx, pat, expr, by_hir)) }, (PatKind::Slice(before, None, after), ExprKind::Array(arr)) if before.len() + after.len() == arr.len() => { zip(before.iter().chain(after), arr).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr, by_hir)) @@ -2004,23 +2005,21 @@ pub fn is_expr_identity_of_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr< if let ExprKind::Path(ident) = &ident.kind && qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id) // check fields - && zip(field_pats, fields).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr,by_hir)) + && over(field_pats, fields, |pat, expr| is_expr_identity_of_pat(cx, pat, expr,by_hir)) { true } else { false } }, - (PatKind::Struct(pat_ident, field_pats, false), ExprKind::Struct(ident, fields, hir::StructTailExpr::None)) + (PatKind::Struct(pat_ident, field_pats, None), ExprKind::Struct(ident, fields, hir::StructTailExpr::None)) if field_pats.len() == fields.len() => { // check ident qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id) // check fields - && field_pats.iter().all(|field_pat| { - fields.iter().any(|field| { - field_pat.ident == field.ident && is_expr_identity_of_pat(cx, field_pat.pat, field.expr, by_hir) - }) + && unordered_over(field_pats, fields, |field_pat, field| { + field_pat.ident == field.ident && is_expr_identity_of_pat(cx, field_pat.pat, field.expr, by_hir) }) }, _ => false, @@ -2405,6 +2404,24 @@ pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir> expr } +/// Returns a `Vec` of `Expr`s containing `AddrOf` operators (`&`) or deref operators (`*`) of a +/// given expression. +pub fn get_ref_operators<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>) -> Vec<&'hir Expr<'hir>> { + let mut operators = Vec::new(); + peel_hir_expr_while(expr, |expr| match expr.kind { + ExprKind::AddrOf(_, _, e) => { + operators.push(expr); + Some(e) + }, + ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => { + operators.push(expr); + Some(e) + }, + _ => None, + }); + operators +} + pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind && let Res::Def(_, def_id) = path.res @@ -3633,3 +3650,17 @@ pub fn expr_adjustment_requires_coercion(cx: &LateContext<'_>, expr: &Expr<'_>) ) }) } + +/// Checks if the expression is an async block (i.e., `async { ... }`). +pub fn is_expr_async_block(expr: &Expr<'_>) -> bool { + matches!( + expr.kind, + ExprKind::Closure(Closure { + kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared( + CoroutineDesugaring::Async, + CoroutineSource::Block + )), + .. + }) + ) +} diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs index fafc1d07e51..8e302f9d2ad 100644 --- a/src/tools/clippy/clippy_utils/src/ty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs @@ -387,10 +387,7 @@ pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool { /// Checks if the type is a reference equals to a diagnostic item pub fn is_type_ref_to_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool { match ty.kind() { - ty::Ref(_, ref_ty, _) => match ref_ty.kind() { - ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did()), - _ => false, - }, + ty::Ref(_, ref_ty, _) => is_type_diagnostic_item(cx, *ref_ty, diag_item), _ => false, } } @@ -1378,9 +1375,7 @@ pub fn has_non_owning_mutable_access<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<' /// Check if `ty` is slice-like, i.e., `&[T]`, `[T; N]`, or `Vec<T>`. pub fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - ty.is_slice() - || ty.is_array() - || matches!(ty.kind(), ty::Adt(adt_def, _) if cx.tcx.is_diagnostic_item(sym::Vec, adt_def.did())) + ty.is_slice() || ty.is_array() || is_type_diagnostic_item(cx, ty, sym::Vec) } pub fn get_field_idx_by_name(ty: Ty<'_>, name: Symbol) -> Option<usize> { diff --git a/src/tools/clippy/clippy_utils/src/usage.rs b/src/tools/clippy/clippy_utils/src/usage.rs index 76d43feee12..6eccbcdb122 100644 --- a/src/tools/clippy/clippy_utils/src/usage.rs +++ b/src/tools/clippy/clippy_utils/src/usage.rs @@ -144,11 +144,9 @@ impl<'tcx> Visitor<'tcx> for BindingUsageFinder<'_, 'tcx> { /// Checks if the given expression is a macro call to `todo!()` or `unimplemented!()`. pub fn is_todo_unimplemented_macro(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - root_macro_call_first_node(cx, expr).is_some_and(|macro_call| { - [sym::todo_macro, sym::unimplemented_macro] - .iter() - .any(|&sym| cx.tcx.is_diagnostic_item(sym, macro_call.def_id)) - }) + root_macro_call_first_node(cx, expr) + .and_then(|macro_call| cx.tcx.get_diagnostic_name(macro_call.def_id)) + .is_some_and(|macro_name| matches!(macro_name, sym::todo_macro | sym::unimplemented_macro)) } /// Checks if the given expression is a stub, i.e., a `todo!()` or `unimplemented!()` expression, diff --git a/src/tools/clippy/rust-toolchain.toml b/src/tools/clippy/rust-toolchain.toml index 5497e77e8ad..ec2f24a0a6d 100644 --- a/src/tools/clippy/rust-toolchain.toml +++ b/src/tools/clippy/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-08-22" +channel = "nightly-2025-09-04" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs index c4076cbaa77..6bddcbfd94c 100644 --- a/src/tools/clippy/src/driver.rs +++ b/src/tools/clippy/src/driver.rs @@ -330,7 +330,8 @@ pub fn main() { // Do not run Clippy for Cargo's info queries so that invalid CLIPPY_ARGS are not cached // https://github.com/rust-lang/cargo/issues/14385 - let info_query = has_arg(&orig_args, "-vV") || has_arg(&orig_args, "--print"); + let info_query = has_arg(&orig_args, "-vV") + || arg_value(&orig_args, "--print", |val| val != "crate-root-lint-levels").is_some(); let clippy_enabled = !cap_lints_allow && relevant_package && !info_query; if clippy_enabled { diff --git a/src/tools/clippy/tests/config-consistency.rs b/src/tools/clippy/tests/config-consistency.rs new file mode 100644 index 00000000000..9e7ca26c7d4 --- /dev/null +++ b/src/tools/clippy/tests/config-consistency.rs @@ -0,0 +1,30 @@ +#![feature(rustc_private)] + +// This test checks that all lints defined in `clippy_config::conf` in `#[lints]` +// attributes exist as Clippy lints. +// +// This test is a no-op if run as part of the compiler test suite +// and will always succeed. + +use std::collections::HashSet; + +#[test] +fn config_consistency() { + if option_env!("RUSTC_TEST_SUITE").is_some() { + return; + } + + let lint_names: HashSet<String> = clippy_lints::declared_lints::LINTS + .iter() + .map(|lint_info| lint_info.lint.name.strip_prefix("clippy::").unwrap().to_lowercase()) + .collect(); + for conf in clippy_config::get_configuration_metadata() { + for lint in conf.lints { + assert!( + lint_names.contains(*lint), + "Configuration option {} references lint `{lint}` which does not exist", + conf.name + ); + } + } +} diff --git a/src/tools/clippy/tests/no-profile-in-cargo-toml.rs b/src/tools/clippy/tests/no-profile-in-cargo-toml.rs new file mode 100644 index 00000000000..2ad9bfb75de --- /dev/null +++ b/src/tools/clippy/tests/no-profile-in-cargo-toml.rs @@ -0,0 +1,34 @@ +// Check that we do not have `profile.*` sections in our `Cargo.toml` files, +// as this causes warnings when run from the compiler repository which includes +// Clippy in a workspace. +// +// Those sections can be put into `.cargo/config.toml` which will be read +// when commands are issued from the top-level Clippy directory, outside of +// a workspace. + +use std::fs::File; +use std::io::{self, BufRead as _}; +use walkdir::WalkDir; + +#[test] +fn no_profile_in_cargo_toml() { + // This check could parse `Cargo.toml` using a TOML deserializer, but in practice + // profile sections would be added at the beginning of a line as `[profile.*]`, so + // keep it fast and simple. + for entry in WalkDir::new(".") + .into_iter() + .filter_map(Result::ok) + .filter(|e| e.file_name().to_str() == Some("Cargo.toml")) + { + for line in io::BufReader::new(File::open(entry.path()).unwrap()) + .lines() + .map(Result::unwrap) + { + if line.starts_with("[profile.") { + eprintln!("Profile section `{line}` found in file `{}`.", entry.path().display()); + eprintln!("Use `.cargo/config.toml` for profiles specific to the standalone Clippy repository."); + panic!("Profile section found in `Cargo.toml`"); + } + } + } +} diff --git a/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr b/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr index f03a745963e..e9d53c64dd9 100644 --- a/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr +++ b/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr @@ -4,7 +4,7 @@ error: use of a disallowed method `rustc_lint::context::LintContext::span_lint` LL | cx.span_lint(lint, span, |lint| { | ^^^^^^^^^ | - = note: this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead + = note: this function does not add a link to our documentation; please use the `clippy_utils::diagnostics::span_lint*` functions instead note: the lint level is defined here --> tests/ui-internal/disallow_span_lint.rs:2:9 | @@ -17,7 +17,7 @@ error: use of a disallowed method `rustc_middle::ty::context::TyCtxt::node_span_ LL | tcx.node_span_lint(lint, hir_id, span, |lint| { | ^^^^^^^^^^^^^^ | - = note: this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead + = note: this function does not add a link to our documentation; please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui-toml/excessive_precision/clippy.toml b/src/tools/clippy/tests/ui-toml/excessive_precision/clippy.toml new file mode 100644 index 00000000000..c7fc230dcb3 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/excessive_precision/clippy.toml @@ -0,0 +1 @@ +const-literal-digits-threshold = 20 diff --git a/src/tools/clippy/tests/ui-toml/excessive_precision/excessive_precision.fixed b/src/tools/clippy/tests/ui-toml/excessive_precision/excessive_precision.fixed new file mode 100644 index 00000000000..577bbff2957 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/excessive_precision/excessive_precision.fixed @@ -0,0 +1,38 @@ +#![warn(clippy::excessive_precision)] +#![allow( + dead_code, + overflowing_literals, + unused_variables, + clippy::print_literal, + clippy::useless_vec +)] + +fn main() { + // Overly specified constants + let _: f32 = 1.012_345_7; + //~^ excessive_precision + let _: f64 = 1.012_345_678_901_234_6; + //~^ excessive_precision + const _: f32 = 1.012345678901234567890; + const _: f64 = 1.012345678901234567890; + + static STATIC1: f32 = 1.012345678901234567890; + static STATIC2: f64 = 1.012345678901234567890; + + static mut STATIC_MUT1: f32 = 1.012345678901234567890; + static mut STATIC_MUT2: f64 = 1.012345678901234567890; +} + +trait ExcessivelyPreciseTrait { + // Overly specified constants + const GOOD1: f32 = 1.012345678901234567890; + const GOOD2: f64 = 1.012345678901234567890; +} + +struct ExcessivelyPreciseStruct; + +impl ExcessivelyPreciseStruct { + // Overly specified constants + const GOOD1: f32 = 1.012345678901234567890; + const GOOD2: f64 = 1.012345678901234567890; +} diff --git a/src/tools/clippy/tests/ui-toml/excessive_precision/excessive_precision.rs b/src/tools/clippy/tests/ui-toml/excessive_precision/excessive_precision.rs new file mode 100644 index 00000000000..121448ed540 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/excessive_precision/excessive_precision.rs @@ -0,0 +1,38 @@ +#![warn(clippy::excessive_precision)] +#![allow( + dead_code, + overflowing_literals, + unused_variables, + clippy::print_literal, + clippy::useless_vec +)] + +fn main() { + // Overly specified constants + let _: f32 = 1.012345678901234567890; + //~^ excessive_precision + let _: f64 = 1.012345678901234567890; + //~^ excessive_precision + const _: f32 = 1.012345678901234567890; + const _: f64 = 1.012345678901234567890; + + static STATIC1: f32 = 1.012345678901234567890; + static STATIC2: f64 = 1.012345678901234567890; + + static mut STATIC_MUT1: f32 = 1.012345678901234567890; + static mut STATIC_MUT2: f64 = 1.012345678901234567890; +} + +trait ExcessivelyPreciseTrait { + // Overly specified constants + const GOOD1: f32 = 1.012345678901234567890; + const GOOD2: f64 = 1.012345678901234567890; +} + +struct ExcessivelyPreciseStruct; + +impl ExcessivelyPreciseStruct { + // Overly specified constants + const GOOD1: f32 = 1.012345678901234567890; + const GOOD2: f64 = 1.012345678901234567890; +} diff --git a/src/tools/clippy/tests/ui-toml/excessive_precision/excessive_precision.stderr b/src/tools/clippy/tests/ui-toml/excessive_precision/excessive_precision.stderr new file mode 100644 index 00000000000..65d33eddef1 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/excessive_precision/excessive_precision.stderr @@ -0,0 +1,38 @@ +error: float has excessive precision + --> tests/ui-toml/excessive_precision/excessive_precision.rs:12:18 + | +LL | let _: f32 = 1.012345678901234567890; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +note: consider making it a `const` item + --> tests/ui-toml/excessive_precision/excessive_precision.rs:12:5 + | +LL | let _: f32 = 1.012345678901234567890; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: `-D clippy::excessive-precision` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::excessive_precision)]` +help: consider changing the type or truncating it to + | +LL - let _: f32 = 1.012345678901234567890; +LL + let _: f32 = 1.012_345_7; + | + +error: float has excessive precision + --> tests/ui-toml/excessive_precision/excessive_precision.rs:14:18 + | +LL | let _: f64 = 1.012345678901234567890; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +note: consider making it a `const` item + --> tests/ui-toml/excessive_precision/excessive_precision.rs:14:5 + | +LL | let _: f64 = 1.012345678901234567890; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider changing the type or truncating it to + | +LL - let _: f64 = 1.012345678901234567890; +LL + let _: f64 = 1.012_345_678_901_234_6; + | + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 6ee77ebd8ec..20aeb4bb849 100644 --- a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -35,6 +35,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect check-inconsistent-struct-field-initializers check-private-items cognitive-complexity-threshold + const-literal-digits-threshold disallowed-macros disallowed-methods disallowed-names @@ -129,6 +130,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect check-inconsistent-struct-field-initializers check-private-items cognitive-complexity-threshold + const-literal-digits-threshold disallowed-macros disallowed-methods disallowed-names @@ -223,6 +225,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni check-inconsistent-struct-field-initializers check-private-items cognitive-complexity-threshold + const-literal-digits-threshold disallowed-macros disallowed-methods disallowed-names diff --git a/src/tools/clippy/tests/ui/assertions_on_result_states.fixed b/src/tools/clippy/tests/ui/assertions_on_result_states.fixed index 09c1a8d0ed1..b2b7318c113 100644 --- a/src/tools/clippy/tests/ui/assertions_on_result_states.fixed +++ b/src/tools/clippy/tests/ui/assertions_on_result_states.fixed @@ -82,9 +82,18 @@ fn main() { assert!(r.is_err()); } -#[allow(dead_code)] fn issue9450() { let res: Result<i32, i32> = Ok(1); res.unwrap_err(); //~^ assertions_on_result_states } + +fn issue9916(res: Result<u32, u32>) { + let a = 0; + match a { + 0 => {}, + 1 => { res.unwrap(); }, + //~^ assertions_on_result_states + _ => todo!(), + } +} diff --git a/src/tools/clippy/tests/ui/assertions_on_result_states.rs b/src/tools/clippy/tests/ui/assertions_on_result_states.rs index c63c2502b53..33f1485326b 100644 --- a/src/tools/clippy/tests/ui/assertions_on_result_states.rs +++ b/src/tools/clippy/tests/ui/assertions_on_result_states.rs @@ -82,9 +82,18 @@ fn main() { assert!(r.is_err()); } -#[allow(dead_code)] fn issue9450() { let res: Result<i32, i32> = Ok(1); assert!(res.is_err()) //~^ assertions_on_result_states } + +fn issue9916(res: Result<u32, u32>) { + let a = 0; + match a { + 0 => {}, + 1 => assert!(res.is_ok()), + //~^ assertions_on_result_states + _ => todo!(), + } +} diff --git a/src/tools/clippy/tests/ui/assertions_on_result_states.stderr b/src/tools/clippy/tests/ui/assertions_on_result_states.stderr index 3bf811588c6..826f049555c 100644 --- a/src/tools/clippy/tests/ui/assertions_on_result_states.stderr +++ b/src/tools/clippy/tests/ui/assertions_on_result_states.stderr @@ -38,10 +38,16 @@ LL | assert!(r.is_err()); | ^^^^^^^^^^^^^^^^^^^ help: replace with: `r.unwrap_err()` error: called `assert!` with `Result::is_err` - --> tests/ui/assertions_on_result_states.rs:88:5 + --> tests/ui/assertions_on_result_states.rs:87:5 | LL | assert!(res.is_err()) | ^^^^^^^^^^^^^^^^^^^^^ help: replace with: `res.unwrap_err();` -error: aborting due to 7 previous errors +error: called `assert!` with `Result::is_ok` + --> tests/ui/assertions_on_result_states.rs:95:14 + | +LL | 1 => assert!(res.is_ok()), + | ^^^^^^^^^^^^^^^^^^^^ help: replace with: `{ res.unwrap(); }` + +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/assign_ops.fixed b/src/tools/clippy/tests/ui/assign_ops.fixed index 3754b9dfe74..2046d089d6a 100644 --- a/src/tools/clippy/tests/ui/assign_ops.fixed +++ b/src/tools/clippy/tests/ui/assign_ops.fixed @@ -1,8 +1,9 @@ #![allow(clippy::useless_vec)] #![warn(clippy::assign_op_pattern)] -#![feature(const_trait_impl, const_ops)] +#![feature(const_ops)] +#![feature(const_trait_impl)] -use core::num::Wrapping; +use std::num::Wrapping; use std::ops::{Mul, MulAssign}; fn main() { @@ -82,6 +83,18 @@ mod issue14871 { pub trait Number: Copy + Add<Self, Output = Self> + AddAssign { const ZERO: Self; const ONE: Self; + + fn non_constant(value: usize) -> Self { + let mut res = Self::ZERO; + let mut count = 0; + while count < value { + res += Self::ONE; + //~^ assign_op_pattern + count += 1; + //~^ assign_op_pattern + } + res + } } #[rustfmt::skip] // rustfmt doesn't understand the order of pub const on traits (yet) @@ -91,7 +104,7 @@ mod issue14871 { impl<T> const NumberConstants for T where - T: Number + [const] core::ops::Add, + T: Number + [const] std::ops::Add, { fn constant(value: usize) -> Self { let mut res = Self::ZERO; @@ -99,8 +112,28 @@ mod issue14871 { while count < value { res = res + Self::ONE; count += 1; + //~^ assign_op_pattern } res } } + + pub struct S; + + impl const std::ops::Add for S { + type Output = S; + fn add(self, _rhs: S) -> S { + S + } + } + + impl const std::ops::AddAssign for S { + fn add_assign(&mut self, rhs: S) {} + } + + const fn do_add() { + let mut s = S; + s += S; + //~^ assign_op_pattern + } } diff --git a/src/tools/clippy/tests/ui/assign_ops.rs b/src/tools/clippy/tests/ui/assign_ops.rs index 0b878d4f490..f83a40f5547 100644 --- a/src/tools/clippy/tests/ui/assign_ops.rs +++ b/src/tools/clippy/tests/ui/assign_ops.rs @@ -1,8 +1,9 @@ #![allow(clippy::useless_vec)] #![warn(clippy::assign_op_pattern)] -#![feature(const_trait_impl, const_ops)] +#![feature(const_ops)] +#![feature(const_trait_impl)] -use core::num::Wrapping; +use std::num::Wrapping; use std::ops::{Mul, MulAssign}; fn main() { @@ -82,6 +83,18 @@ mod issue14871 { pub trait Number: Copy + Add<Self, Output = Self> + AddAssign { const ZERO: Self; const ONE: Self; + + fn non_constant(value: usize) -> Self { + let mut res = Self::ZERO; + let mut count = 0; + while count < value { + res = res + Self::ONE; + //~^ assign_op_pattern + count = count + 1; + //~^ assign_op_pattern + } + res + } } #[rustfmt::skip] // rustfmt doesn't understand the order of pub const on traits (yet) @@ -91,16 +104,36 @@ mod issue14871 { impl<T> const NumberConstants for T where - T: Number + [const] core::ops::Add, + T: Number + [const] std::ops::Add, { fn constant(value: usize) -> Self { let mut res = Self::ZERO; let mut count = 0; while count < value { res = res + Self::ONE; - count += 1; + count = count + 1; + //~^ assign_op_pattern } res } } + + pub struct S; + + impl const std::ops::Add for S { + type Output = S; + fn add(self, _rhs: S) -> S { + S + } + } + + impl const std::ops::AddAssign for S { + fn add_assign(&mut self, rhs: S) {} + } + + const fn do_add() { + let mut s = S; + s = s + S; + //~^ assign_op_pattern + } } diff --git a/src/tools/clippy/tests/ui/assign_ops.stderr b/src/tools/clippy/tests/ui/assign_ops.stderr index c5e698b3ee1..a4fca04893c 100644 --- a/src/tools/clippy/tests/ui/assign_ops.stderr +++ b/src/tools/clippy/tests/ui/assign_ops.stderr @@ -1,5 +1,5 @@ error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:10:5 + --> tests/ui/assign_ops.rs:11:5 | LL | a = a + 1; | ^^^^^^^^^ help: replace it with: `a += 1` @@ -8,70 +8,94 @@ LL | a = a + 1; = help: to override `-D warnings` add `#[allow(clippy::assign_op_pattern)]` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:12:5 + --> tests/ui/assign_ops.rs:13:5 | LL | a = 1 + a; | ^^^^^^^^^ help: replace it with: `a += 1` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:14:5 + --> tests/ui/assign_ops.rs:15:5 | LL | a = a - 1; | ^^^^^^^^^ help: replace it with: `a -= 1` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:16:5 + --> tests/ui/assign_ops.rs:17:5 | LL | a = a * 99; | ^^^^^^^^^^ help: replace it with: `a *= 99` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:18:5 + --> tests/ui/assign_ops.rs:19:5 | LL | a = 42 * a; | ^^^^^^^^^^ help: replace it with: `a *= 42` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:20:5 + --> tests/ui/assign_ops.rs:21:5 | LL | a = a / 2; | ^^^^^^^^^ help: replace it with: `a /= 2` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:22:5 + --> tests/ui/assign_ops.rs:23:5 | LL | a = a % 5; | ^^^^^^^^^ help: replace it with: `a %= 5` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:24:5 + --> tests/ui/assign_ops.rs:25:5 | LL | a = a & 1; | ^^^^^^^^^ help: replace it with: `a &= 1` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:31:5 + --> tests/ui/assign_ops.rs:32:5 | LL | s = s + "bla"; | ^^^^^^^^^^^^^ help: replace it with: `s += "bla"` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:36:5 + --> tests/ui/assign_ops.rs:37:5 | LL | a = a + Wrapping(1u32); | ^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a += Wrapping(1u32)` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:39:5 + --> tests/ui/assign_ops.rs:40:5 | LL | v[0] = v[0] + v[1]; | ^^^^^^^^^^^^^^^^^^ help: replace it with: `v[0] += v[1]` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:52:5 + --> tests/ui/assign_ops.rs:53:5 | LL | buf = buf + cows.clone(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `buf += cows.clone()` -error: aborting due to 12 previous errors +error: manual implementation of an assign operation + --> tests/ui/assign_ops.rs:91:17 + | +LL | res = res + Self::ONE; + | ^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `res += Self::ONE` + +error: manual implementation of an assign operation + --> tests/ui/assign_ops.rs:93:17 + | +LL | count = count + 1; + | ^^^^^^^^^^^^^^^^^ help: replace it with: `count += 1` + +error: manual implementation of an assign operation + --> tests/ui/assign_ops.rs:114:17 + | +LL | count = count + 1; + | ^^^^^^^^^^^^^^^^^ help: replace it with: `count += 1` + +error: manual implementation of an assign operation + --> tests/ui/assign_ops.rs:136:9 + | +LL | s = s + S; + | ^^^^^^^^^ help: replace it with: `s += S` + +error: aborting due to 16 previous errors diff --git a/src/tools/clippy/tests/ui/async_yields_async.fixed b/src/tools/clippy/tests/ui/async_yields_async.fixed index 93c573d3086..bd1b31f74ee 100644 --- a/src/tools/clippy/tests/ui/async_yields_async.fixed +++ b/src/tools/clippy/tests/ui/async_yields_async.fixed @@ -80,3 +80,42 @@ fn check_expect_suppression() { } }; } + +#[allow(clippy::let_underscore_future)] +fn issue15552() { + async fn bar(i: i32) {} + + macro_rules! call_bar { + () => { + async { bar(5).await } + }; + ($e:expr) => { + bar($e) + }; + } + let x = async { call_bar!(5).await }; + //~^ async_yields_async + let y = async { call_bar!().await }; + //~^ async_yields_async + //~| async_yields_async + + use std::future::{Future, Ready}; + use std::ops::Add; + use std::pin::Pin; + use std::task::{Context, Poll}; + struct CustomFutureType; + impl Add for CustomFutureType { + type Output = Self; + fn add(self, other: Self) -> Self { + self + } + } + impl Future for CustomFutureType { + type Output = (); + fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> { + Poll::Ready(()) + } + } + let _ = async { (CustomFutureType + CustomFutureType).await }; + //~^ async_yields_async +} diff --git a/src/tools/clippy/tests/ui/async_yields_async.rs b/src/tools/clippy/tests/ui/async_yields_async.rs index 166d522e1c0..605d2734157 100644 --- a/src/tools/clippy/tests/ui/async_yields_async.rs +++ b/src/tools/clippy/tests/ui/async_yields_async.rs @@ -80,3 +80,42 @@ fn check_expect_suppression() { } }; } + +#[allow(clippy::let_underscore_future)] +fn issue15552() { + async fn bar(i: i32) {} + + macro_rules! call_bar { + () => { + async { bar(5) } + }; + ($e:expr) => { + bar($e) + }; + } + let x = async { call_bar!(5) }; + //~^ async_yields_async + let y = async { call_bar!() }; + //~^ async_yields_async + //~| async_yields_async + + use std::future::{Future, Ready}; + use std::ops::Add; + use std::pin::Pin; + use std::task::{Context, Poll}; + struct CustomFutureType; + impl Add for CustomFutureType { + type Output = Self; + fn add(self, other: Self) -> Self { + self + } + } + impl Future for CustomFutureType { + type Output = (); + fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> { + Poll::Ready(()) + } + } + let _ = async { CustomFutureType + CustomFutureType }; + //~^ async_yields_async +} diff --git a/src/tools/clippy/tests/ui/async_yields_async.stderr b/src/tools/clippy/tests/ui/async_yields_async.stderr index 17a35076fa3..3041cf65789 100644 --- a/src/tools/clippy/tests/ui/async_yields_async.stderr +++ b/src/tools/clippy/tests/ui/async_yields_async.stderr @@ -89,5 +89,50 @@ LL | | CustomFutureType LL | | }; | |_____- outer async construct -error: aborting due to 6 previous errors +error: an async construct yields a type which is itself awaitable + --> tests/ui/async_yields_async.rs:96:21 + | +LL | let x = async { call_bar!(5) }; + | --^^^^^^^^^^^^-- + | | | + | | awaitable value not awaited + | | help: consider awaiting this value: `call_bar!(5).await` + | outer async construct + +error: an async construct yields a type which is itself awaitable + --> tests/ui/async_yields_async.rs:98:21 + | +LL | let y = async { call_bar!() }; + | --^^^^^^^^^^^-- + | | | + | | awaitable value not awaited + | | help: consider awaiting this value: `call_bar!().await` + | outer async construct + +error: an async construct yields a type which is itself awaitable + --> tests/ui/async_yields_async.rs:90:21 + | +LL | async { bar(5) } + | --^^^^^^-- + | | | + | | awaitable value not awaited + | | help: consider awaiting this value: `bar(5).await` + | outer async construct +... +LL | let y = async { call_bar!() }; + | ----------- in this macro invocation + | + = note: this error originates in the macro `call_bar` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: an async construct yields a type which is itself awaitable + --> tests/ui/async_yields_async.rs:119:21 + | +LL | let _ = async { CustomFutureType + CustomFutureType }; + | --^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-- + | | | + | | awaitable value not awaited + | | help: consider awaiting this value: `(CustomFutureType + CustomFutureType).await` + | outer async construct + +error: aborting due to 10 previous errors diff --git a/src/tools/clippy/tests/ui/bool_assert_comparison.stderr b/src/tools/clippy/tests/ui/bool_assert_comparison.stderr index f823f08f31d..72aa6303a20 100644 --- a/src/tools/clippy/tests/ui/bool_assert_comparison.stderr +++ b/src/tools/clippy/tests/ui/bool_assert_comparison.stderr @@ -272,10 +272,8 @@ LL | assert_eq!(a!(), true); | help: replace it with `assert!(..)` | -LL | true -... -LL | -LL ~ assert!(a!()); +LL - assert_eq!(a!(), true); +LL + assert!(a!()); | error: used `assert_eq!` with a literal bool @@ -286,10 +284,8 @@ LL | assert_eq!(true, b!()); | help: replace it with `assert!(..)` | -LL | true -... -LL | -LL ~ assert!(b!()); +LL - assert_eq!(true, b!()); +LL + assert!(b!()); | error: used `debug_assert_eq!` with a literal bool diff --git a/src/tools/clippy/tests/ui/bool_comparison.fixed b/src/tools/clippy/tests/ui/bool_comparison.fixed index 166abbe549c..b0b60104c0b 100644 --- a/src/tools/clippy/tests/ui/bool_comparison.fixed +++ b/src/tools/clippy/tests/ui/bool_comparison.fixed @@ -1,97 +1,41 @@ #![allow(non_local_definitions, clippy::needless_if)] #![warn(clippy::bool_comparison)] -#![allow(clippy::non_canonical_partial_ord_impl, clippy::nonminimal_bool)] +#![allow(clippy::non_canonical_partial_ord_impl)] fn main() { let x = true; - if x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if !x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if !x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if !x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if !x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if !x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if !x { - //~^ bool_comparison - "yes" - } else { - "no" - }; + let _ = if x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if !x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if !x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if !x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if !x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if !x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if !x { "yes" } else { "no" }; + //~^ bool_comparison + let y = true; - if !x & y { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if x & !y { - //~^ bool_comparison - "yes" - } else { - "no" - }; + let _ = if !x & y { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if x & !y { "yes" } else { "no" }; + //~^ bool_comparison } -#[allow(dead_code)] fn issue3703() { struct Foo; impl PartialEq<bool> for Foo { @@ -127,26 +71,6 @@ fn issue3703() { if false < Foo {} } -#[allow(dead_code)] -fn issue4983() { - let a = true; - let b = false; - - if a != b {}; - //~^ bool_comparison - if a != b {}; - //~^ bool_comparison - if a == b {}; - if !a == !b {}; - - if b != a {}; - //~^ bool_comparison - if b != a {}; - //~^ bool_comparison - if b == a {}; - if !b == !a {}; -} - macro_rules! m { ($func:ident) => { $func() @@ -157,7 +81,6 @@ fn func() -> bool { true } -#[allow(dead_code)] fn issue3973() { // ok, don't lint on `cfg` invocation if false == cfg!(feature = "debugging") {} @@ -196,6 +119,34 @@ fn issue9907() { //~^ bool_comparison // This is not part of the issue, but an unexpected found when fixing the issue, // the provided span was inside of macro rather than the macro callsite. - let _ = ((1 < 2) != m!(func)) as usize; + let _ = ((1 < 2) & !m!(func)) as usize; //~^ bool_comparison } + +#[allow(clippy::nonminimal_bool)] +fn issue15367() { + let a = true; + let b = false; + + // these cases are handled by `nonminimal_bool`, so don't double-lint + if a == !b {}; + if !a == b {}; + if b == !a {}; + if !b == a {}; +} + +fn issue15497() { + fn func() -> bool { + true + } + + fn foo(x: bool) -> bool { + x & !m!(func) + //~^ bool_comparison + } + + fn bar(x: bool) -> bool { + !x & m!(func) + //~^ bool_comparison + } +} diff --git a/src/tools/clippy/tests/ui/bool_comparison.rs b/src/tools/clippy/tests/ui/bool_comparison.rs index 7265c2b4c5a..1b1108d6ce5 100644 --- a/src/tools/clippy/tests/ui/bool_comparison.rs +++ b/src/tools/clippy/tests/ui/bool_comparison.rs @@ -1,97 +1,41 @@ #![allow(non_local_definitions, clippy::needless_if)] #![warn(clippy::bool_comparison)] -#![allow(clippy::non_canonical_partial_ord_impl, clippy::nonminimal_bool)] +#![allow(clippy::non_canonical_partial_ord_impl)] fn main() { let x = true; - if x == true { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if x == false { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if true == x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if false == x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if x != true { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if x != false { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if true != x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if false != x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if x < true { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if false < x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if x > false { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if true > x { - //~^ bool_comparison - "yes" - } else { - "no" - }; + let _ = if x == true { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if x == false { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if true == x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if false == x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if x != true { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if x != false { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if true != x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if false != x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if x < true { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if false < x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if x > false { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if true > x { "yes" } else { "no" }; + //~^ bool_comparison + let y = true; - if x < y { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if x > y { - //~^ bool_comparison - "yes" - } else { - "no" - }; + let _ = if x < y { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if x > y { "yes" } else { "no" }; + //~^ bool_comparison } -#[allow(dead_code)] fn issue3703() { struct Foo; impl PartialEq<bool> for Foo { @@ -127,26 +71,6 @@ fn issue3703() { if false < Foo {} } -#[allow(dead_code)] -fn issue4983() { - let a = true; - let b = false; - - if a == !b {}; - //~^ bool_comparison - if !a == b {}; - //~^ bool_comparison - if a == b {}; - if !a == !b {}; - - if b == !a {}; - //~^ bool_comparison - if !b == a {}; - //~^ bool_comparison - if b == a {}; - if !b == !a {}; -} - macro_rules! m { ($func:ident) => { $func() @@ -157,7 +81,6 @@ fn func() -> bool { true } -#[allow(dead_code)] fn issue3973() { // ok, don't lint on `cfg` invocation if false == cfg!(feature = "debugging") {} @@ -196,6 +119,34 @@ fn issue9907() { //~^ bool_comparison // This is not part of the issue, but an unexpected found when fixing the issue, // the provided span was inside of macro rather than the macro callsite. - let _ = ((1 < 2) == !m!(func)) as usize; + let _ = ((1 < 2) > m!(func)) as usize; //~^ bool_comparison } + +#[allow(clippy::nonminimal_bool)] +fn issue15367() { + let a = true; + let b = false; + + // these cases are handled by `nonminimal_bool`, so don't double-lint + if a == !b {}; + if !a == b {}; + if b == !a {}; + if !b == a {}; +} + +fn issue15497() { + fn func() -> bool { + true + } + + fn foo(x: bool) -> bool { + x > m!(func) + //~^ bool_comparison + } + + fn bar(x: bool) -> bool { + x < m!(func) + //~^ bool_comparison + } +} diff --git a/src/tools/clippy/tests/ui/bool_comparison.stderr b/src/tools/clippy/tests/ui/bool_comparison.stderr index ddbb9ff78ed..98881a2d20f 100644 --- a/src/tools/clippy/tests/ui/bool_comparison.stderr +++ b/src/tools/clippy/tests/ui/bool_comparison.stderr @@ -1,155 +1,143 @@ error: equality checks against true are unnecessary - --> tests/ui/bool_comparison.rs:7:8 + --> tests/ui/bool_comparison.rs:7:16 | -LL | if x == true { - | ^^^^^^^^^ help: try simplifying it as shown: `x` +LL | let _ = if x == true { "yes" } else { "no" }; + | ^^^^^^^^^ help: try: `x` | = note: `-D clippy::bool-comparison` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::bool_comparison)]` error: equality checks against false can be replaced by a negation - --> tests/ui/bool_comparison.rs:13:8 + --> tests/ui/bool_comparison.rs:9:16 | -LL | if x == false { - | ^^^^^^^^^^ help: try simplifying it as shown: `!x` +LL | let _ = if x == false { "yes" } else { "no" }; + | ^^^^^^^^^^ help: try: `!x` error: equality checks against true are unnecessary - --> tests/ui/bool_comparison.rs:19:8 + --> tests/ui/bool_comparison.rs:11:16 | -LL | if true == x { - | ^^^^^^^^^ help: try simplifying it as shown: `x` +LL | let _ = if true == x { "yes" } else { "no" }; + | ^^^^^^^^^ help: try: `x` error: equality checks against false can be replaced by a negation - --> tests/ui/bool_comparison.rs:25:8 + --> tests/ui/bool_comparison.rs:13:16 | -LL | if false == x { - | ^^^^^^^^^^ help: try simplifying it as shown: `!x` +LL | let _ = if false == x { "yes" } else { "no" }; + | ^^^^^^^^^^ help: try: `!x` error: inequality checks against true can be replaced by a negation - --> tests/ui/bool_comparison.rs:31:8 + --> tests/ui/bool_comparison.rs:15:16 | -LL | if x != true { - | ^^^^^^^^^ help: try simplifying it as shown: `!x` +LL | let _ = if x != true { "yes" } else { "no" }; + | ^^^^^^^^^ help: try: `!x` error: inequality checks against false are unnecessary - --> tests/ui/bool_comparison.rs:37:8 + --> tests/ui/bool_comparison.rs:17:16 | -LL | if x != false { - | ^^^^^^^^^^ help: try simplifying it as shown: `x` +LL | let _ = if x != false { "yes" } else { "no" }; + | ^^^^^^^^^^ help: try: `x` error: inequality checks against true can be replaced by a negation - --> tests/ui/bool_comparison.rs:43:8 + --> tests/ui/bool_comparison.rs:19:16 | -LL | if true != x { - | ^^^^^^^^^ help: try simplifying it as shown: `!x` +LL | let _ = if true != x { "yes" } else { "no" }; + | ^^^^^^^^^ help: try: `!x` error: inequality checks against false are unnecessary - --> tests/ui/bool_comparison.rs:49:8 + --> tests/ui/bool_comparison.rs:21:16 | -LL | if false != x { - | ^^^^^^^^^^ help: try simplifying it as shown: `x` +LL | let _ = if false != x { "yes" } else { "no" }; + | ^^^^^^^^^^ help: try: `x` error: less than comparison against true can be replaced by a negation - --> tests/ui/bool_comparison.rs:55:8 + --> tests/ui/bool_comparison.rs:23:16 | -LL | if x < true { - | ^^^^^^^^ help: try simplifying it as shown: `!x` +LL | let _ = if x < true { "yes" } else { "no" }; + | ^^^^^^^^ help: try: `!x` error: greater than checks against false are unnecessary - --> tests/ui/bool_comparison.rs:61:8 + --> tests/ui/bool_comparison.rs:25:16 | -LL | if false < x { - | ^^^^^^^^^ help: try simplifying it as shown: `x` +LL | let _ = if false < x { "yes" } else { "no" }; + | ^^^^^^^^^ help: try: `x` error: greater than checks against false are unnecessary - --> tests/ui/bool_comparison.rs:67:8 + --> tests/ui/bool_comparison.rs:27:16 | -LL | if x > false { - | ^^^^^^^^^ help: try simplifying it as shown: `x` +LL | let _ = if x > false { "yes" } else { "no" }; + | ^^^^^^^^^ help: try: `x` error: less than comparison against true can be replaced by a negation - --> tests/ui/bool_comparison.rs:73:8 + --> tests/ui/bool_comparison.rs:29:16 | -LL | if true > x { - | ^^^^^^^^ help: try simplifying it as shown: `!x` +LL | let _ = if true > x { "yes" } else { "no" }; + | ^^^^^^^^ help: try: `!x` error: order comparisons between booleans can be simplified - --> tests/ui/bool_comparison.rs:80:8 + --> tests/ui/bool_comparison.rs:33:16 | -LL | if x < y { - | ^^^^^ help: try simplifying it as shown: `!x & y` +LL | let _ = if x < y { "yes" } else { "no" }; + | ^^^^^ help: try: `!x & y` error: order comparisons between booleans can be simplified - --> tests/ui/bool_comparison.rs:86:8 + --> tests/ui/bool_comparison.rs:35:16 | -LL | if x > y { - | ^^^^^ help: try simplifying it as shown: `x & !y` - -error: this comparison might be written more concisely - --> tests/ui/bool_comparison.rs:135:8 - | -LL | if a == !b {}; - | ^^^^^^^ help: try simplifying it as shown: `a != b` - -error: this comparison might be written more concisely - --> tests/ui/bool_comparison.rs:137:8 - | -LL | if !a == b {}; - | ^^^^^^^ help: try simplifying it as shown: `a != b` - -error: this comparison might be written more concisely - --> tests/ui/bool_comparison.rs:142:8 - | -LL | if b == !a {}; - | ^^^^^^^ help: try simplifying it as shown: `b != a` - -error: this comparison might be written more concisely - --> tests/ui/bool_comparison.rs:144:8 - | -LL | if !b == a {}; - | ^^^^^^^ help: try simplifying it as shown: `b != a` +LL | let _ = if x > y { "yes" } else { "no" }; + | ^^^^^ help: try: `x & !y` error: equality checks against false can be replaced by a negation - --> tests/ui/bool_comparison.rs:169:8 + --> tests/ui/bool_comparison.rs:92:8 | LL | if false == m!(func) {} - | ^^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `!m!(func)` + | ^^^^^^^^^^^^^^^^^ help: try: `!m!(func)` error: equality checks against false can be replaced by a negation - --> tests/ui/bool_comparison.rs:171:8 + --> tests/ui/bool_comparison.rs:94:8 | LL | if m!(func) == false {} - | ^^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `!m!(func)` + | ^^^^^^^^^^^^^^^^^ help: try: `!m!(func)` error: equality checks against true are unnecessary - --> tests/ui/bool_comparison.rs:173:8 + --> tests/ui/bool_comparison.rs:96:8 | LL | if true == m!(func) {} - | ^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `m!(func)` + | ^^^^^^^^^^^^^^^^ help: try: `m!(func)` error: equality checks against true are unnecessary - --> tests/ui/bool_comparison.rs:175:8 + --> tests/ui/bool_comparison.rs:98:8 | LL | if m!(func) == true {} - | ^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `m!(func)` + | ^^^^^^^^^^^^^^^^ help: try: `m!(func)` error: equality checks against false can be replaced by a negation - --> tests/ui/bool_comparison.rs:193:14 + --> tests/ui/bool_comparison.rs:116:14 | LL | let _ = ((1 < 2) == false) as usize; - | ^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `1 >= 2` + | ^^^^^^^^^^^^^^^^ help: try: `1 >= 2` error: equality checks against false can be replaced by a negation - --> tests/ui/bool_comparison.rs:195:14 + --> tests/ui/bool_comparison.rs:118:14 | LL | let _ = (false == m!(func)) as usize; - | ^^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `!m!(func)` + | ^^^^^^^^^^^^^^^^^ help: try: `!m!(func)` -error: this comparison might be written more concisely - --> tests/ui/bool_comparison.rs:199:14 +error: order comparisons between booleans can be simplified + --> tests/ui/bool_comparison.rs:122:14 + | +LL | let _ = ((1 < 2) > m!(func)) as usize; + | ^^^^^^^^^^^^^^^^^^ help: try: `(1 < 2) & !m!(func)` + +error: order comparisons between booleans can be simplified + --> tests/ui/bool_comparison.rs:144:9 + | +LL | x > m!(func) + | ^^^^^^^^^^^^ help: try: `x & !m!(func)` + +error: order comparisons between booleans can be simplified + --> tests/ui/bool_comparison.rs:149:9 | -LL | let _ = ((1 < 2) == !m!(func)) as usize; - | ^^^^^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `(1 < 2) != m!(func)` +LL | x < m!(func) + | ^^^^^^^^^^^^ help: try: `!x & m!(func)` -error: aborting due to 25 previous errors +error: aborting due to 23 previous errors diff --git a/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.fixed b/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.fixed index bddcb0ebf64..3c1cf884595 100644 --- a/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.fixed +++ b/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.fixed @@ -1,30 +1,53 @@ #![warn(clippy::cast_slice_from_raw_parts)] -#[allow(unused_imports, unused_unsafe)] +const fn require_raw_slice_ptr<T>(_: *const [T]) {} + fn main() { let mut vec = vec![0u8; 1]; let ptr: *const u8 = vec.as_ptr(); let mptr = vec.as_mut_ptr(); - let _: *const [u8] = unsafe { core::ptr::slice_from_raw_parts(ptr, 1) }; + let _: *const [u8] = unsafe { std::ptr::slice_from_raw_parts(ptr, 1) }; //~^ cast_slice_from_raw_parts - let _: *const [u8] = unsafe { core::ptr::slice_from_raw_parts_mut(mptr, 1) }; + let _: *const [u8] = unsafe { std::ptr::slice_from_raw_parts_mut(mptr, 1) }; //~^ cast_slice_from_raw_parts - let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1); + let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, 1); //~^ cast_slice_from_raw_parts { use core::slice; - let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1); + let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, 1); //~^ cast_slice_from_raw_parts use slice as one; - let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1); + let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, 1); //~^ cast_slice_from_raw_parts } { use std::slice; - let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1); + let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, 1); //~^ cast_slice_from_raw_parts use slice as one; - let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1); + let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, 1); //~^ cast_slice_from_raw_parts } + + // implicit cast + { + let _: *const [u8] = unsafe { std::ptr::slice_from_raw_parts(ptr, 1) }; + //~^ cast_slice_from_raw_parts + let _: *mut [u8] = unsafe { std::ptr::slice_from_raw_parts_mut(mptr, 1) }; + //~^ cast_slice_from_raw_parts + require_raw_slice_ptr(unsafe { std::ptr::slice_from_raw_parts(ptr, 1) }); + //~^ cast_slice_from_raw_parts + } + + // implicit cast in const context + const { + const PTR: *const u8 = std::ptr::null(); + const MPTR: *mut u8 = std::ptr::null_mut(); + let _: *const [u8] = unsafe { std::ptr::slice_from_raw_parts(PTR, 1) }; + //~^ cast_slice_from_raw_parts + let _: *mut [u8] = unsafe { std::ptr::slice_from_raw_parts_mut(MPTR, 1) }; + //~^ cast_slice_from_raw_parts + require_raw_slice_ptr(unsafe { std::ptr::slice_from_raw_parts(PTR, 1) }); + //~^ cast_slice_from_raw_parts + }; } diff --git a/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.rs b/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.rs index 0a1eb276d5e..8f57b1f9619 100644 --- a/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.rs +++ b/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.rs @@ -1,6 +1,7 @@ #![warn(clippy::cast_slice_from_raw_parts)] -#[allow(unused_imports, unused_unsafe)] +const fn require_raw_slice_ptr<T>(_: *const [T]) {} + fn main() { let mut vec = vec![0u8; 1]; let ptr: *const u8 = vec.as_ptr(); @@ -27,4 +28,26 @@ fn main() { let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8]; //~^ cast_slice_from_raw_parts } + + // implicit cast + { + let _: *const [u8] = unsafe { std::slice::from_raw_parts(ptr, 1) }; + //~^ cast_slice_from_raw_parts + let _: *mut [u8] = unsafe { std::slice::from_raw_parts_mut(mptr, 1) }; + //~^ cast_slice_from_raw_parts + require_raw_slice_ptr(unsafe { std::slice::from_raw_parts(ptr, 1) }); + //~^ cast_slice_from_raw_parts + } + + // implicit cast in const context + const { + const PTR: *const u8 = std::ptr::null(); + const MPTR: *mut u8 = std::ptr::null_mut(); + let _: *const [u8] = unsafe { std::slice::from_raw_parts(PTR, 1) }; + //~^ cast_slice_from_raw_parts + let _: *mut [u8] = unsafe { std::slice::from_raw_parts_mut(MPTR, 1) }; + //~^ cast_slice_from_raw_parts + require_raw_slice_ptr(unsafe { std::slice::from_raw_parts(PTR, 1) }); + //~^ cast_slice_from_raw_parts + }; } diff --git a/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.stderr b/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.stderr index 60794a988db..328dbafbafe 100644 --- a/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.stderr +++ b/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.stderr @@ -1,47 +1,83 @@ error: casting the result of `from_raw_parts` to *const [u8] - --> tests/ui/cast_raw_slice_pointer_cast.rs:8:35 + --> tests/ui/cast_raw_slice_pointer_cast.rs:9:35 | LL | let _: *const [u8] = unsafe { std::slice::from_raw_parts(ptr, 1) as *const [u8] }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::ptr::slice_from_raw_parts(ptr, 1)` | = note: `-D clippy::cast-slice-from-raw-parts` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::cast_slice_from_raw_parts)]` error: casting the result of `from_raw_parts_mut` to *mut [u8] - --> tests/ui/cast_raw_slice_pointer_cast.rs:10:35 + --> tests/ui/cast_raw_slice_pointer_cast.rs:11:35 | LL | let _: *const [u8] = unsafe { std::slice::from_raw_parts_mut(mptr, 1) as *mut [u8] }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts_mut(mptr, 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::ptr::slice_from_raw_parts_mut(mptr, 1)` error: casting the result of `from_raw_parts` to *const [u8] - --> tests/ui/cast_raw_slice_pointer_cast.rs:12:26 + --> tests/ui/cast_raw_slice_pointer_cast.rs:13:26 | LL | let _: *const [u8] = unsafe { std::slice::from_raw_parts(ptr, 1) } as *const [u8]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::ptr::slice_from_raw_parts(ptr, 1)` error: casting the result of `from_raw_parts` to *const [u8] - --> tests/ui/cast_raw_slice_pointer_cast.rs:16:30 + --> tests/ui/cast_raw_slice_pointer_cast.rs:17:30 | LL | let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::ptr::slice_from_raw_parts(ptr, 1)` error: casting the result of `from_raw_parts` to *const [u8] - --> tests/ui/cast_raw_slice_pointer_cast.rs:19:30 + --> tests/ui/cast_raw_slice_pointer_cast.rs:20:30 | LL | let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::ptr::slice_from_raw_parts(ptr, 1)` error: casting the result of `from_raw_parts` to *const [u8] - --> tests/ui/cast_raw_slice_pointer_cast.rs:24:30 + --> tests/ui/cast_raw_slice_pointer_cast.rs:25:30 | LL | let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::ptr::slice_from_raw_parts(ptr, 1)` error: casting the result of `from_raw_parts` to *const [u8] - --> tests/ui/cast_raw_slice_pointer_cast.rs:27:30 + --> tests/ui/cast_raw_slice_pointer_cast.rs:28:30 | LL | let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::ptr::slice_from_raw_parts(ptr, 1)` -error: aborting due to 7 previous errors +error: implicitly casting the result of `from_raw_parts` to `*const [u8]` + --> tests/ui/cast_raw_slice_pointer_cast.rs:34:39 + | +LL | let _: *const [u8] = unsafe { std::slice::from_raw_parts(ptr, 1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `std::ptr::slice_from_raw_parts(ptr, 1)` + +error: implicitly casting the result of `from_raw_parts_mut` to `*mut [u8]` + --> tests/ui/cast_raw_slice_pointer_cast.rs:36:37 + | +LL | let _: *mut [u8] = unsafe { std::slice::from_raw_parts_mut(mptr, 1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `std::ptr::slice_from_raw_parts_mut(mptr, 1)` + +error: implicitly casting the result of `from_raw_parts` to `*const [u8]` + --> tests/ui/cast_raw_slice_pointer_cast.rs:38:40 + | +LL | require_raw_slice_ptr(unsafe { std::slice::from_raw_parts(ptr, 1) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `std::ptr::slice_from_raw_parts(ptr, 1)` + +error: implicitly casting the result of `from_raw_parts` to `*const [u8]` + --> tests/ui/cast_raw_slice_pointer_cast.rs:46:39 + | +LL | let _: *const [u8] = unsafe { std::slice::from_raw_parts(PTR, 1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `std::ptr::slice_from_raw_parts(PTR, 1)` + +error: implicitly casting the result of `from_raw_parts_mut` to `*mut [u8]` + --> tests/ui/cast_raw_slice_pointer_cast.rs:48:37 + | +LL | let _: *mut [u8] = unsafe { std::slice::from_raw_parts_mut(MPTR, 1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `std::ptr::slice_from_raw_parts_mut(MPTR, 1)` + +error: implicitly casting the result of `from_raw_parts` to `*const [u8]` + --> tests/ui/cast_raw_slice_pointer_cast.rs:50:40 + | +LL | require_raw_slice_ptr(unsafe { std::slice::from_raw_parts(PTR, 1) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `std::ptr::slice_from_raw_parts(PTR, 1)` + +error: aborting due to 13 previous errors diff --git a/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast_no_std.fixed b/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast_no_std.fixed new file mode 100644 index 00000000000..f71fb8d863c --- /dev/null +++ b/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast_no_std.fixed @@ -0,0 +1,55 @@ +#![warn(clippy::cast_slice_from_raw_parts)] +#![no_std] +#![crate_type = "lib"] + +const fn require_raw_slice_ptr<T>(_: *const [T]) {} + +fn main() { + let mut arr = [0u8; 1]; + let ptr: *const u8 = arr.as_ptr(); + let mptr = arr.as_mut_ptr(); + let _: *const [u8] = unsafe { core::ptr::slice_from_raw_parts(ptr, 1) }; + //~^ cast_slice_from_raw_parts + let _: *const [u8] = unsafe { core::ptr::slice_from_raw_parts_mut(mptr, 1) }; + //~^ cast_slice_from_raw_parts + let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1); + //~^ cast_slice_from_raw_parts + { + use core::slice; + let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1); + //~^ cast_slice_from_raw_parts + use slice as one; + let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1); + //~^ cast_slice_from_raw_parts + } + { + use core::slice; + let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1); + //~^ cast_slice_from_raw_parts + use slice as one; + let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1); + //~^ cast_slice_from_raw_parts + } + + // implicit cast + { + let _: *const [u8] = unsafe { core::ptr::slice_from_raw_parts(ptr, 1) }; + //~^ cast_slice_from_raw_parts + let _: *mut [u8] = unsafe { core::ptr::slice_from_raw_parts_mut(mptr, 1) }; + //~^ cast_slice_from_raw_parts + require_raw_slice_ptr(unsafe { core::ptr::slice_from_raw_parts(ptr, 1) }); + //~^ cast_slice_from_raw_parts + } + + // implicit cast in const context + const { + const PTR: *const u8 = core::ptr::null(); + const MPTR: *mut u8 = core::ptr::null_mut(); + let _: *const [u8] = unsafe { core::ptr::slice_from_raw_parts(PTR, 1) }; + //~^ cast_slice_from_raw_parts + let _: *mut [u8] = unsafe { core::ptr::slice_from_raw_parts_mut(MPTR, 1) }; + //~^ cast_slice_from_raw_parts + require_raw_slice_ptr(unsafe { core::ptr::slice_from_raw_parts(PTR, 1) }); + //~^ cast_slice_from_raw_parts + }; +} diff --git a/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast_no_std.rs b/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast_no_std.rs new file mode 100644 index 00000000000..743e44c97dc --- /dev/null +++ b/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast_no_std.rs @@ -0,0 +1,55 @@ +#![warn(clippy::cast_slice_from_raw_parts)] +#![no_std] +#![crate_type = "lib"] + +const fn require_raw_slice_ptr<T>(_: *const [T]) {} + +fn main() { + let mut arr = [0u8; 1]; + let ptr: *const u8 = arr.as_ptr(); + let mptr = arr.as_mut_ptr(); + let _: *const [u8] = unsafe { core::slice::from_raw_parts(ptr, 1) as *const [u8] }; + //~^ cast_slice_from_raw_parts + let _: *const [u8] = unsafe { core::slice::from_raw_parts_mut(mptr, 1) as *mut [u8] }; + //~^ cast_slice_from_raw_parts + let _: *const [u8] = unsafe { core::slice::from_raw_parts(ptr, 1) } as *const [u8]; + //~^ cast_slice_from_raw_parts + { + use core::slice; + let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8]; + //~^ cast_slice_from_raw_parts + use slice as one; + let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8]; + //~^ cast_slice_from_raw_parts + } + { + use core::slice; + let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8]; + //~^ cast_slice_from_raw_parts + use slice as one; + let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8]; + //~^ cast_slice_from_raw_parts + } + + // implicit cast + { + let _: *const [u8] = unsafe { core::slice::from_raw_parts(ptr, 1) }; + //~^ cast_slice_from_raw_parts + let _: *mut [u8] = unsafe { core::slice::from_raw_parts_mut(mptr, 1) }; + //~^ cast_slice_from_raw_parts + require_raw_slice_ptr(unsafe { core::slice::from_raw_parts(ptr, 1) }); + //~^ cast_slice_from_raw_parts + } + + // implicit cast in const context + const { + const PTR: *const u8 = core::ptr::null(); + const MPTR: *mut u8 = core::ptr::null_mut(); + let _: *const [u8] = unsafe { core::slice::from_raw_parts(PTR, 1) }; + //~^ cast_slice_from_raw_parts + let _: *mut [u8] = unsafe { core::slice::from_raw_parts_mut(MPTR, 1) }; + //~^ cast_slice_from_raw_parts + require_raw_slice_ptr(unsafe { core::slice::from_raw_parts(PTR, 1) }); + //~^ cast_slice_from_raw_parts + }; +} diff --git a/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast_no_std.stderr b/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast_no_std.stderr new file mode 100644 index 00000000000..5488fbcfa1c --- /dev/null +++ b/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast_no_std.stderr @@ -0,0 +1,83 @@ +error: casting the result of `from_raw_parts` to *const [u8] + --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:11:35 + | +LL | let _: *const [u8] = unsafe { core::slice::from_raw_parts(ptr, 1) as *const [u8] }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + | + = note: `-D clippy::cast-slice-from-raw-parts` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::cast_slice_from_raw_parts)]` + +error: casting the result of `from_raw_parts_mut` to *mut [u8] + --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:13:35 + | +LL | let _: *const [u8] = unsafe { core::slice::from_raw_parts_mut(mptr, 1) as *mut [u8] }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts_mut(mptr, 1)` + +error: casting the result of `from_raw_parts` to *const [u8] + --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:15:26 + | +LL | let _: *const [u8] = unsafe { core::slice::from_raw_parts(ptr, 1) } as *const [u8]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + +error: casting the result of `from_raw_parts` to *const [u8] + --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:19:30 + | +LL | let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + +error: casting the result of `from_raw_parts` to *const [u8] + --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:22:30 + | +LL | let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + +error: casting the result of `from_raw_parts` to *const [u8] + --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:27:30 + | +LL | let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + +error: casting the result of `from_raw_parts` to *const [u8] + --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:30:30 + | +LL | let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + +error: implicitly casting the result of `from_raw_parts` to `*const [u8]` + --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:36:39 + | +LL | let _: *const [u8] = unsafe { core::slice::from_raw_parts(ptr, 1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `core::ptr::slice_from_raw_parts(ptr, 1)` + +error: implicitly casting the result of `from_raw_parts_mut` to `*mut [u8]` + --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:38:37 + | +LL | let _: *mut [u8] = unsafe { core::slice::from_raw_parts_mut(mptr, 1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `core::ptr::slice_from_raw_parts_mut(mptr, 1)` + +error: implicitly casting the result of `from_raw_parts` to `*const [u8]` + --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:40:40 + | +LL | require_raw_slice_ptr(unsafe { core::slice::from_raw_parts(ptr, 1) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `core::ptr::slice_from_raw_parts(ptr, 1)` + +error: implicitly casting the result of `from_raw_parts` to `*const [u8]` + --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:48:39 + | +LL | let _: *const [u8] = unsafe { core::slice::from_raw_parts(PTR, 1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `core::ptr::slice_from_raw_parts(PTR, 1)` + +error: implicitly casting the result of `from_raw_parts_mut` to `*mut [u8]` + --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:50:37 + | +LL | let _: *mut [u8] = unsafe { core::slice::from_raw_parts_mut(MPTR, 1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `core::ptr::slice_from_raw_parts_mut(MPTR, 1)` + +error: implicitly casting the result of `from_raw_parts` to `*const [u8]` + --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:52:40 + | +LL | require_raw_slice_ptr(unsafe { core::slice::from_raw_parts(PTR, 1) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `core::ptr::slice_from_raw_parts(PTR, 1)` + +error: aborting due to 13 previous errors + diff --git a/src/tools/clippy/tests/ui/collapsible_match.rs b/src/tools/clippy/tests/ui/collapsible_match.rs index 71b82040ff6..8931a3aa09c 100644 --- a/src/tools/clippy/tests/ui/collapsible_match.rs +++ b/src/tools/clippy/tests/ui/collapsible_match.rs @@ -316,6 +316,47 @@ fn lint_emitted_at_right_node(opt: Option<Result<u64, String>>) { }; } +pub fn issue_14155() { + let mut arr = ["a", "b", "c"]; + if let Some(last) = arr.last() { + match *last { + //~^ collapsible_match + "a" | "b" => { + unimplemented!() + }, + _ => (), + } + } + + if let Some(last) = arr.last() { + match &last { + //~^ collapsible_match + &&"a" | &&"b" => { + unimplemented!() + }, + _ => (), + } + } + + if let Some(mut last) = arr.last_mut() { + match &mut last { + //~^ collapsible_match + &mut &mut "a" | &mut &mut "b" => { + unimplemented!() + }, + _ => (), + } + } + + const NULL_PTR: *const &'static str = std::ptr::null(); + if let Some(last) = arr.last() { + match &raw const *last { + NULL_PTR => unimplemented!(), + _ => (), + } + } +} + fn make<T>() -> T { unimplemented!() } diff --git a/src/tools/clippy/tests/ui/collapsible_match.stderr b/src/tools/clippy/tests/ui/collapsible_match.stderr index c290d84ec29..14b1c1b187e 100644 --- a/src/tools/clippy/tests/ui/collapsible_match.stderr +++ b/src/tools/clippy/tests/ui/collapsible_match.stderr @@ -250,5 +250,74 @@ LL | if let Issue9647::A { a: Some(a), .. } = x { LL | if let Some(u) = a { | ^^^^^^^ with this pattern -error: aborting due to 13 previous errors +error: this `match` can be collapsed into the outer `if let` + --> tests/ui/collapsible_match.rs:322:9 + | +LL | / match *last { +LL | | +LL | | "a" | "b" => { +LL | | unimplemented!() +LL | | }, +LL | | _ => (), +LL | | } + | |_________^ + | +help: the outer pattern can be modified to include the inner pattern + --> tests/ui/collapsible_match.rs:321:17 + | +LL | if let Some(last) = arr.last() { + | ^^^^ ---------- use: `arr.last().copied()` + | | + | replace this binding +... +LL | "a" | "b" => { + | ^^^^^^^^^ with this pattern + +error: this `match` can be collapsed into the outer `if let` + --> tests/ui/collapsible_match.rs:332:9 + | +LL | / match &last { +LL | | +LL | | &&"a" | &&"b" => { +LL | | unimplemented!() +LL | | }, +LL | | _ => (), +LL | | } + | |_________^ + | +help: the outer pattern can be modified to include the inner pattern + --> tests/ui/collapsible_match.rs:331:17 + | +LL | if let Some(last) = arr.last() { + | ^^^^ ---------- use: `arr.last().as_ref()` + | | + | replace this binding +... +LL | &&"a" | &&"b" => { + | ^^^^^^^^^^^^^ with this pattern + +error: this `match` can be collapsed into the outer `if let` + --> tests/ui/collapsible_match.rs:342:9 + | +LL | / match &mut last { +LL | | +LL | | &mut &mut "a" | &mut &mut "b" => { +LL | | unimplemented!() +LL | | }, +LL | | _ => (), +LL | | } + | |_________^ + | +help: the outer pattern can be modified to include the inner pattern + --> tests/ui/collapsible_match.rs:341:17 + | +LL | if let Some(mut last) = arr.last_mut() { + | ^^^^^^^^ -------------- use: `arr.last_mut().as_mut()` + | | + | replace this binding +... +LL | &mut &mut "a" | &mut &mut "b" => { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ with this pattern + +error: aborting due to 16 previous errors diff --git a/src/tools/clippy/tests/ui/collapsible_match2.stderr b/src/tools/clippy/tests/ui/collapsible_match2.stderr index 7b273063752..25970670999 100644 --- a/src/tools/clippy/tests/ui/collapsible_match2.stderr +++ b/src/tools/clippy/tests/ui/collapsible_match2.stderr @@ -77,6 +77,8 @@ LL | | }, help: the outer pattern can be modified to include the inner pattern --> tests/ui/collapsible_match2.rs:54:14 | +LL | match Some(&[1]) { + | ---------- use: `Some(&[1]).copied()` LL | Some(s) => match *s { | ^ replace this binding LL | diff --git a/src/tools/clippy/tests/ui/derivable_impls.fixed b/src/tools/clippy/tests/ui/derivable_impls.fixed index 65bfded3883..9f9e4e253c3 100644 --- a/src/tools/clippy/tests/ui/derivable_impls.fixed +++ b/src/tools/clippy/tests/ui/derivable_impls.fixed @@ -1,4 +1,6 @@ #![allow(dead_code)] +#![feature(const_trait_impl)] +#![feature(const_default)] use std::collections::HashMap; @@ -326,4 +328,40 @@ mod issue11368 { } } +mod issue15493 { + #[derive(Copy, Clone)] + #[repr(transparent)] + struct Foo(u64); + + impl const Default for Foo { + fn default() -> Self { + Self(0) + } + } + + #[derive(Copy, Clone)] + enum Bar { + A, + B, + } + + impl const Default for Bar { + fn default() -> Self { + Bar::A + } + } +} + +mod issue15536 { + #[derive(Copy, Clone)] + #[derive(Default)] + enum Bar { + #[default] + A, + B, + } + + +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/derivable_impls.rs b/src/tools/clippy/tests/ui/derivable_impls.rs index 4826c5497b4..74a793b9a70 100644 --- a/src/tools/clippy/tests/ui/derivable_impls.rs +++ b/src/tools/clippy/tests/ui/derivable_impls.rs @@ -1,4 +1,6 @@ #![allow(dead_code)] +#![feature(const_trait_impl)] +#![feature(const_default)] use std::collections::HashMap; @@ -396,4 +398,43 @@ mod issue11368 { } } +mod issue15493 { + #[derive(Copy, Clone)] + #[repr(transparent)] + struct Foo(u64); + + impl const Default for Foo { + fn default() -> Self { + Self(0) + } + } + + #[derive(Copy, Clone)] + enum Bar { + A, + B, + } + + impl const Default for Bar { + fn default() -> Self { + Bar::A + } + } +} + +mod issue15536 { + #[derive(Copy, Clone)] + enum Bar { + A, + B, + } + + impl Default for Bar { + //~^ derivable_impls + fn default() -> Self { + Self::A + } + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/derivable_impls.stderr b/src/tools/clippy/tests/ui/derivable_impls.stderr index 0f73ad55a85..cd46414cb4a 100644 --- a/src/tools/clippy/tests/ui/derivable_impls.stderr +++ b/src/tools/clippy/tests/ui/derivable_impls.stderr @@ -1,5 +1,5 @@ error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:20:1 + --> tests/ui/derivable_impls.rs:22:1 | LL | / impl std::default::Default for FooDefault<'_> { LL | | @@ -18,7 +18,7 @@ LL ~ struct FooDefault<'a> { | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:42:1 + --> tests/ui/derivable_impls.rs:44:1 | LL | / impl std::default::Default for TupleDefault { LL | | @@ -35,7 +35,7 @@ LL ~ struct TupleDefault(bool, i32, u64); | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:95:1 + --> tests/ui/derivable_impls.rs:97:1 | LL | / impl Default for StrDefault<'_> { LL | | @@ -52,7 +52,7 @@ LL ~ struct StrDefault<'a>(&'a str); | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:122:1 + --> tests/ui/derivable_impls.rs:124:1 | LL | / impl Default for Y { LL | | @@ -69,7 +69,7 @@ LL ~ struct Y(u32); | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:162:1 + --> tests/ui/derivable_impls.rs:164:1 | LL | / impl Default for WithoutSelfCurly { LL | | @@ -86,7 +86,7 @@ LL ~ struct WithoutSelfCurly { | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:171:1 + --> tests/ui/derivable_impls.rs:173:1 | LL | / impl Default for WithoutSelfParan { LL | | @@ -103,7 +103,7 @@ LL ~ struct WithoutSelfParan(bool); | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:194:1 + --> tests/ui/derivable_impls.rs:196:1 | LL | / impl Default for DirectDefaultDefaultCall { LL | | @@ -119,7 +119,7 @@ LL ~ pub struct DirectDefaultDefaultCall { | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:206:1 + --> tests/ui/derivable_impls.rs:208:1 | LL | / impl Default for EquivalentToDefaultDefaultCallVec { LL | | @@ -135,7 +135,7 @@ LL ~ pub struct EquivalentToDefaultDefaultCallVec { | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:234:1 + --> tests/ui/derivable_impls.rs:236:1 | LL | / impl Default for EquivalentToDefaultDefaultCallLocal { LL | | @@ -151,7 +151,7 @@ LL ~ pub struct EquivalentToDefaultDefaultCallLocal { | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:274:1 + --> tests/ui/derivable_impls.rs:276:1 | LL | / impl Default for RepeatDefault1 { LL | | @@ -168,7 +168,7 @@ LL ~ pub struct RepeatDefault1 { | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:309:1 + --> tests/ui/derivable_impls.rs:311:1 | LL | / impl Default for SimpleEnum { LL | | @@ -187,5 +187,28 @@ LL ~ #[default] LL ~ Bar, | -error: aborting due to 11 previous errors +error: this `impl` can be derived + --> tests/ui/derivable_impls.rs:432:5 + | +LL | / impl Default for Bar { +LL | | +LL | | fn default() -> Self { +LL | | Self::A +LL | | } +LL | | } + | |_____^ + | +help: replace the manual implementation with a derive attribute and mark the default variant + | +LL ~ #[derive(Default)] +LL ~ enum Bar { +LL ~ #[default] +LL ~ A, +LL | B, +LL | } +LL | +LL ~ + | + +error: aborting due to 12 previous errors diff --git a/src/tools/clippy/tests/ui/derivable_impls_derive_const.fixed b/src/tools/clippy/tests/ui/derivable_impls_derive_const.fixed new file mode 100644 index 00000000000..f0d8d2d2409 --- /dev/null +++ b/src/tools/clippy/tests/ui/derivable_impls_derive_const.fixed @@ -0,0 +1,25 @@ +#![allow(dead_code)] +#![feature(const_trait_impl)] +#![feature(const_default)] +#![feature(derive_const)] + +mod issue15493 { + #[derive(Copy, Clone)] + #[repr(transparent)] + #[derive_const(Default)] + struct Foo(u64); + + + + #[derive(Copy, Clone)] + #[derive_const(Default)] + enum Bar { + #[default] + A, + B, + } + + +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/derivable_impls_derive_const.rs b/src/tools/clippy/tests/ui/derivable_impls_derive_const.rs new file mode 100644 index 00000000000..7d70db1c097 --- /dev/null +++ b/src/tools/clippy/tests/ui/derivable_impls_derive_const.rs @@ -0,0 +1,32 @@ +#![allow(dead_code)] +#![feature(const_trait_impl)] +#![feature(const_default)] +#![feature(derive_const)] + +mod issue15493 { + #[derive(Copy, Clone)] + #[repr(transparent)] + struct Foo(u64); + + impl const Default for Foo { + //~^ derivable_impls + fn default() -> Self { + Self(0) + } + } + + #[derive(Copy, Clone)] + enum Bar { + A, + B, + } + + impl const Default for Bar { + //~^ derivable_impls + fn default() -> Self { + Bar::A + } + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/derivable_impls_derive_const.stderr b/src/tools/clippy/tests/ui/derivable_impls_derive_const.stderr new file mode 100644 index 00000000000..196bac185dd --- /dev/null +++ b/src/tools/clippy/tests/ui/derivable_impls_derive_const.stderr @@ -0,0 +1,46 @@ +error: this `impl` can be derived + --> tests/ui/derivable_impls_derive_const.rs:11:5 + | +LL | / impl const Default for Foo { +LL | | +LL | | fn default() -> Self { +LL | | Self(0) +LL | | } +LL | | } + | |_____^ + | + = note: `-D clippy::derivable-impls` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::derivable_impls)]` +help: replace the manual implementation with a derive attribute + | +LL ~ #[derive_const(Default)] +LL ~ struct Foo(u64); +LL | +LL ~ + | + +error: this `impl` can be derived + --> tests/ui/derivable_impls_derive_const.rs:24:5 + | +LL | / impl const Default for Bar { +LL | | +LL | | fn default() -> Self { +LL | | Bar::A +LL | | } +LL | | } + | |_____^ + | +help: replace the manual implementation with a derive attribute and mark the default variant + | +LL ~ #[derive_const(Default)] +LL ~ enum Bar { +LL ~ #[default] +LL ~ A, +LL | B, +LL | } +LL | +LL ~ + | + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed index 423a73734da..46695dc929a 100644 --- a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed +++ b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed @@ -73,6 +73,7 @@ fn test_units() { /// GPLv2 GPLv3 /// GitHub GitLab /// IPv4 IPv6 +/// InfiniBand RoCE /// ClojureScript CoffeeScript JavaScript PostScript PureScript TypeScript /// PowerPC WebAssembly /// NaN NaNs diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.rs b/src/tools/clippy/tests/ui/doc/doc-fixable.rs index 8deffb4210e..4082fa5b56f 100644 --- a/src/tools/clippy/tests/ui/doc/doc-fixable.rs +++ b/src/tools/clippy/tests/ui/doc/doc-fixable.rs @@ -73,6 +73,7 @@ fn test_units() { /// GPLv2 GPLv3 /// GitHub GitLab /// IPv4 IPv6 +/// InfiniBand RoCE /// ClojureScript CoffeeScript JavaScript PostScript PureScript TypeScript /// PowerPC WebAssembly /// NaN NaNs diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.stderr b/src/tools/clippy/tests/ui/doc/doc-fixable.stderr index 98c26e6bec2..2a94a8f3165 100644 --- a/src/tools/clippy/tests/ui/doc/doc-fixable.stderr +++ b/src/tools/clippy/tests/ui/doc/doc-fixable.stderr @@ -145,7 +145,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:90:5 + --> tests/ui/doc/doc-fixable.rs:91:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -157,7 +157,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:108:5 + --> tests/ui/doc/doc-fixable.rs:109:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -169,7 +169,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:117:8 + --> tests/ui/doc/doc-fixable.rs:118:8 | LL | /// ## CamelCaseThing | ^^^^^^^^^^^^^^ @@ -181,7 +181,7 @@ LL + /// ## `CamelCaseThing` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:121:7 + --> tests/ui/doc/doc-fixable.rs:122:7 | LL | /// # CamelCaseThing | ^^^^^^^^^^^^^^ @@ -193,7 +193,7 @@ LL + /// # `CamelCaseThing` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:124:22 + --> tests/ui/doc/doc-fixable.rs:125:22 | LL | /// Not a title #897 CamelCaseThing | ^^^^^^^^^^^^^^ @@ -205,7 +205,7 @@ LL + /// Not a title #897 `CamelCaseThing` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:126:5 + --> tests/ui/doc/doc-fixable.rs:127:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -217,7 +217,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:134:5 + --> tests/ui/doc/doc-fixable.rs:135:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -229,7 +229,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:148:5 + --> tests/ui/doc/doc-fixable.rs:149:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -241,7 +241,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:160:43 + --> tests/ui/doc/doc-fixable.rs:161:43 | LL | /** E.g., serialization of an empty list: FooBar | ^^^^^^ @@ -253,7 +253,7 @@ LL + /** E.g., serialization of an empty list: `FooBar` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:165:5 + --> tests/ui/doc/doc-fixable.rs:166:5 | LL | And BarQuz too. | ^^^^^^ @@ -265,7 +265,7 @@ LL + And `BarQuz` too. | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:166:1 + --> tests/ui/doc/doc-fixable.rs:167:1 | LL | be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -277,7 +277,7 @@ LL + `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:174:43 + --> tests/ui/doc/doc-fixable.rs:175:43 | LL | /** E.g., serialization of an empty list: FooBar | ^^^^^^ @@ -289,7 +289,7 @@ LL + /** E.g., serialization of an empty list: `FooBar` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:179:5 + --> tests/ui/doc/doc-fixable.rs:180:5 | LL | And BarQuz too. | ^^^^^^ @@ -301,7 +301,7 @@ LL + And `BarQuz` too. | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:180:1 + --> tests/ui/doc/doc-fixable.rs:181:1 | LL | be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -313,7 +313,7 @@ LL + `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:194:5 + --> tests/ui/doc/doc-fixable.rs:195:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -325,7 +325,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:214:22 + --> tests/ui/doc/doc-fixable.rs:215:22 | LL | /// An iterator over mycrate::Collection's values. | ^^^^^^^^^^^^^^^^^^^ @@ -337,7 +337,7 @@ LL + /// An iterator over `mycrate::Collection`'s values. | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:239:34 + --> tests/ui/doc/doc-fixable.rs:240:34 | LL | /// Foo \[bar\] \[baz\] \[qux\]. DocMarkdownLint | ^^^^^^^^^^^^^^^ @@ -349,7 +349,7 @@ LL + /// Foo \[bar\] \[baz\] \[qux\]. `DocMarkdownLint` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:263:22 + --> tests/ui/doc/doc-fixable.rs:264:22 | LL | /// There is no try (do() or do_not()). | ^^^^ @@ -361,7 +361,7 @@ LL + /// There is no try (`do()` or do_not()). | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:263:30 + --> tests/ui/doc/doc-fixable.rs:264:30 | LL | /// There is no try (do() or do_not()). | ^^^^^^^^ @@ -373,7 +373,7 @@ LL + /// There is no try (do() or `do_not()`). | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:268:5 + --> tests/ui/doc/doc-fixable.rs:269:5 | LL | /// ABes | ^^^^ @@ -385,7 +385,7 @@ LL + /// `ABes` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:275:9 + --> tests/ui/doc/doc-fixable.rs:276:9 | LL | /// foo() | ^^^^^ @@ -397,7 +397,7 @@ LL + /// `foo()` | error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> tests/ui/doc/doc-fixable.rs:280:5 + --> tests/ui/doc/doc-fixable.rs:281:5 | LL | /// https://github.com/rust-lang/rust-clippy/pull/12836 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `<https://github.com/rust-lang/rust-clippy/pull/12836>` diff --git a/src/tools/clippy/tests/ui/entry_unfixable.stderr b/src/tools/clippy/tests/ui/entry_unfixable.stderr index 0197d2ab4cf..f92f472512f 100644 --- a/src/tools/clippy/tests/ui/entry_unfixable.stderr +++ b/src/tools/clippy/tests/ui/entry_unfixable.stderr @@ -10,6 +10,7 @@ LL | | false LL | | } | |_____________^ | + = help: consider using the `Entry` API: https://doc.rust-lang.org/std/collections/struct.HashMap.html#entry-api = note: `-D clippy::map-entry` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::map_entry)]` @@ -24,6 +25,8 @@ LL | | } else { LL | | hm.insert(key, true); LL | | } | |_____^ + | + = help: consider using the `Entry` API: https://doc.rust-lang.org/std/collections/struct.HashMap.html#entry-api error: usage of `contains_key` followed by `insert` on a `HashMap` --> tests/ui/entry_unfixable.rs:80:13 @@ -36,6 +39,8 @@ LL | | let interner = INTERNER.lock().unwrap(); LL | | return Err(interner.resolve(name).unwrap().to_owned()); LL | | } | |_____________^ + | + = help: consider using the `Entry` API: https://doc.rust-lang.org/std/collections/struct.HashMap.html#entry-api error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/eta.fixed b/src/tools/clippy/tests/ui/eta.fixed index 3d2b41b8fb8..6944a979c05 100644 --- a/src/tools/clippy/tests/ui/eta.fixed +++ b/src/tools/clippy/tests/ui/eta.fixed @@ -565,6 +565,51 @@ fn issue_14789() { ); } +fn issue_15072() { + use std::ops::Deref; + + struct Foo; + impl Deref for Foo { + type Target = fn() -> &'static str; + + fn deref(&self) -> &Self::Target { + fn hello() -> &'static str { + "Hello, world!" + } + &(hello as fn() -> &'static str) + } + } + + fn accepts_fn(f: impl Fn() -> &'static str) { + println!("{}", f()); + } + + fn some_fn() -> &'static str { + todo!() + } + + let f = &Foo; + accepts_fn(**f); + //~^ redundant_closure + + let g = &some_fn; + accepts_fn(g); + //~^ redundant_closure + + struct Bar(Foo); + impl Deref for Bar { + type Target = Foo; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + let b = &Bar(Foo); + accepts_fn(***b); + //~^ redundant_closure +} + fn issue8817() { fn f(_: u32) -> u32 { todo!() diff --git a/src/tools/clippy/tests/ui/eta.rs b/src/tools/clippy/tests/ui/eta.rs index 79d1103410d..5bcc1cb26fd 100644 --- a/src/tools/clippy/tests/ui/eta.rs +++ b/src/tools/clippy/tests/ui/eta.rs @@ -565,6 +565,51 @@ fn issue_14789() { ); } +fn issue_15072() { + use std::ops::Deref; + + struct Foo; + impl Deref for Foo { + type Target = fn() -> &'static str; + + fn deref(&self) -> &Self::Target { + fn hello() -> &'static str { + "Hello, world!" + } + &(hello as fn() -> &'static str) + } + } + + fn accepts_fn(f: impl Fn() -> &'static str) { + println!("{}", f()); + } + + fn some_fn() -> &'static str { + todo!() + } + + let f = &Foo; + accepts_fn(|| f()); + //~^ redundant_closure + + let g = &some_fn; + accepts_fn(|| g()); + //~^ redundant_closure + + struct Bar(Foo); + impl Deref for Bar { + type Target = Foo; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + let b = &Bar(Foo); + accepts_fn(|| b()); + //~^ redundant_closure +} + fn issue8817() { fn f(_: u32) -> u32 { todo!() diff --git a/src/tools/clippy/tests/ui/eta.stderr b/src/tools/clippy/tests/ui/eta.stderr index aa32ed1a38e..0b401cdea98 100644 --- a/src/tools/clippy/tests/ui/eta.stderr +++ b/src/tools/clippy/tests/ui/eta.stderr @@ -215,28 +215,46 @@ LL | let _field = bind.or_else(|| get_default()).unwrap(); | ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `get_default` error: redundant closure - --> tests/ui/eta.rs:588:14 + --> tests/ui/eta.rs:592:16 + | +LL | accepts_fn(|| f()); + | ^^^^^^ help: replace the closure with the function itself: `**f` + +error: redundant closure + --> tests/ui/eta.rs:596:16 + | +LL | accepts_fn(|| g()); + | ^^^^^^ help: replace the closure with the function itself: `g` + +error: redundant closure + --> tests/ui/eta.rs:609:16 + | +LL | accepts_fn(|| b()); + | ^^^^^^ help: replace the closure with the function itself: `***b` + +error: redundant closure + --> tests/ui/eta.rs:633:14 | LL | .map(|n| MyError::A(n)) | ^^^^^^^^^^^^^^^^^ help: replace the closure with the tuple variant itself: `MyError::A` error: redundant closure - --> tests/ui/eta.rs:585:14 + --> tests/ui/eta.rs:630:14 | LL | .map(|n| S(n)) | ^^^^^^^^ help: replace the closure with the tuple struct itself: `S` error: redundant closure - --> tests/ui/eta.rs:582:14 + --> tests/ui/eta.rs:627:14 | LL | .map(|n| g(n)) | ^^^^^^^^ help: replace the closure with the function itself: `g` error: redundant closure - --> tests/ui/eta.rs:579:14 + --> tests/ui/eta.rs:624:14 | LL | .map(|n| f(n)) | ^^^^^^^^ help: replace the closure with the function itself: `f` -error: aborting due to 39 previous errors +error: aborting due to 42 previous errors diff --git a/src/tools/clippy/tests/ui/excessive_precision.fixed b/src/tools/clippy/tests/ui/excessive_precision.fixed index 8a8c2e1939c..8158d4b332a 100644 --- a/src/tools/clippy/tests/ui/excessive_precision.fixed +++ b/src/tools/clippy/tests/ui/excessive_precision.fixed @@ -4,9 +4,16 @@ overflowing_literals, unused_variables, clippy::print_literal, - clippy::useless_vec + clippy::useless_vec, + clippy::approx_constant )] +macro_rules! make_pi { + ($i:ident : $t:ty) => { + const $i: $t = 3.14159265358979323846264338327950288419716939937510582097494459230781640628; + }; +} + fn main() { // Consts const GOOD32: f32 = 0.123_456; @@ -101,4 +108,40 @@ fn main() { const _: f64 = 3.0; //~^ excessive_precision const _: f64 = 3.0000000000000000; + + // Overly specified constants + let _: f32 = 1.012_345_7; + //~^ excessive_precision + let _: f64 = 1.012_345_678_901_234_6; + //~^ excessive_precision + const _: f32 = 1.01234567890123456789012345678901234567890; + const _: f64 = 1.01234567890123456789012345678901234567890; + + static STATIC1: f32 = 1.01234567890123456789012345678901234567890; + static STATIC2: f64 = 1.01234567890123456789012345678901234567890; + + static mut STATIC_MUT1: f32 = 1.01234567890123456789012345678901234567890; + static mut STATIC_MUT2: f64 = 1.01234567890123456789012345678901234567890; + + // From issue #13855 + let gamma = 0.577_215_664_901_532_9; + //~^ excessive_precision + const GAMMA: f64 = 0.5772156649015328606065120900824024310421; + + make_pi!(P32: f32); + make_pi!(P64: f64); +} + +trait ExcessivelyPreciseTrait { + // Overly specified constants + const GOOD1: f32 = 1.01234567890123456789012345678901234567890; + const GOOD2: f64 = 1.01234567890123456789012345678901234567890; +} + +struct ExcessivelyPreciseStruct; + +impl ExcessivelyPreciseStruct { + // Overly specified constants + const GOOD1: f32 = 1.01234567890123456789012345678901234567890; + const GOOD2: f64 = 1.01234567890123456789012345678901234567890; } diff --git a/src/tools/clippy/tests/ui/excessive_precision.rs b/src/tools/clippy/tests/ui/excessive_precision.rs index 5dcf55cb927..7ee6247ee5a 100644 --- a/src/tools/clippy/tests/ui/excessive_precision.rs +++ b/src/tools/clippy/tests/ui/excessive_precision.rs @@ -4,9 +4,16 @@ overflowing_literals, unused_variables, clippy::print_literal, - clippy::useless_vec + clippy::useless_vec, + clippy::approx_constant )] +macro_rules! make_pi { + ($i:ident : $t:ty) => { + const $i: $t = 3.14159265358979323846264338327950288419716939937510582097494459230781640628; + }; +} + fn main() { // Consts const GOOD32: f32 = 0.123_456; @@ -101,4 +108,40 @@ fn main() { const _: f64 = 3.0000000000000000e+00; //~^ excessive_precision const _: f64 = 3.0000000000000000; + + // Overly specified constants + let _: f32 = 1.01234567890123456789012345678901234567890; + //~^ excessive_precision + let _: f64 = 1.01234567890123456789012345678901234567890; + //~^ excessive_precision + const _: f32 = 1.01234567890123456789012345678901234567890; + const _: f64 = 1.01234567890123456789012345678901234567890; + + static STATIC1: f32 = 1.01234567890123456789012345678901234567890; + static STATIC2: f64 = 1.01234567890123456789012345678901234567890; + + static mut STATIC_MUT1: f32 = 1.01234567890123456789012345678901234567890; + static mut STATIC_MUT2: f64 = 1.01234567890123456789012345678901234567890; + + // From issue #13855 + let gamma = 0.5772156649015328606065120900824024310421; + //~^ excessive_precision + const GAMMA: f64 = 0.5772156649015328606065120900824024310421; + + make_pi!(P32: f32); + make_pi!(P64: f64); +} + +trait ExcessivelyPreciseTrait { + // Overly specified constants + const GOOD1: f32 = 1.01234567890123456789012345678901234567890; + const GOOD2: f64 = 1.01234567890123456789012345678901234567890; +} + +struct ExcessivelyPreciseStruct; + +impl ExcessivelyPreciseStruct { + // Overly specified constants + const GOOD1: f32 = 1.01234567890123456789012345678901234567890; + const GOOD2: f64 = 1.01234567890123456789012345678901234567890; } diff --git a/src/tools/clippy/tests/ui/excessive_precision.stderr b/src/tools/clippy/tests/ui/excessive_precision.stderr index f5eeadf0c8c..40806d67487 100644 --- a/src/tools/clippy/tests/ui/excessive_precision.stderr +++ b/src/tools/clippy/tests/ui/excessive_precision.stderr @@ -1,5 +1,5 @@ error: float has excessive precision - --> tests/ui/excessive_precision.rs:20:26 + --> tests/ui/excessive_precision.rs:27:26 | LL | const BAD32_1: f32 = 0.123_456_789_f32; | ^^^^^^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL + const BAD32_1: f32 = 0.123_456_79_f32; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:22:26 + --> tests/ui/excessive_precision.rs:29:26 | LL | const BAD32_2: f32 = 0.123_456_789; | ^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL + const BAD32_2: f32 = 0.123_456_79; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:24:26 + --> tests/ui/excessive_precision.rs:31:26 | LL | const BAD32_3: f32 = 0.100_000_000_000_1; | ^^^^^^^^^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL + const BAD32_3: f32 = 0.1; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:26:29 + --> tests/ui/excessive_precision.rs:33:29 | LL | const BAD32_EDGE: f32 = 1.000_000_9; | ^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL + const BAD32_EDGE: f32 = 1.000_001; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:31:26 + --> tests/ui/excessive_precision.rs:38:26 | LL | const BAD64_3: f64 = 0.100_000_000_000_000_000_1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -61,7 +61,7 @@ LL + const BAD64_3: f64 = 0.1; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:35:22 + --> tests/ui/excessive_precision.rs:42:22 | LL | println!("{:?}", 8.888_888_888_888_888_888_888); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL + println!("{:?}", 8.888_888_888_888_89); | error: float has excessive precision - --> tests/ui/excessive_precision.rs:47:22 + --> tests/ui/excessive_precision.rs:54:22 | LL | let bad32: f32 = 1.123_456_789; | ^^^^^^^^^^^^^ @@ -85,7 +85,7 @@ LL + let bad32: f32 = 1.123_456_8; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:49:26 + --> tests/ui/excessive_precision.rs:56:26 | LL | let bad32_suf: f32 = 1.123_456_789_f32; | ^^^^^^^^^^^^^^^^^ @@ -97,7 +97,7 @@ LL + let bad32_suf: f32 = 1.123_456_8_f32; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:51:21 + --> tests/ui/excessive_precision.rs:58:21 | LL | let bad32_inf = 1.123_456_789_f32; | ^^^^^^^^^^^^^^^^^ @@ -109,7 +109,7 @@ LL + let bad32_inf = 1.123_456_8_f32; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:62:36 + --> tests/ui/excessive_precision.rs:69:36 | LL | let bad_vec32: Vec<f32> = vec![0.123_456_789]; | ^^^^^^^^^^^^^ @@ -121,7 +121,7 @@ LL + let bad_vec32: Vec<f32> = vec![0.123_456_79]; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:64:36 + --> tests/ui/excessive_precision.rs:71:36 | LL | let bad_vec64: Vec<f64> = vec![0.123_456_789_123_456_789]; | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -133,7 +133,7 @@ LL + let bad_vec64: Vec<f64> = vec![0.123_456_789_123_456_78]; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:69:24 + --> tests/ui/excessive_precision.rs:76:24 | LL | let bad_e32: f32 = 1.123_456_788_888e-10; | ^^^^^^^^^^^^^^^^^^^^^ @@ -145,7 +145,7 @@ LL + let bad_e32: f32 = 1.123_456_8e-10; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:73:27 + --> tests/ui/excessive_precision.rs:80:27 | LL | let bad_bige32: f32 = 1.123_456_788_888E-10; | ^^^^^^^^^^^^^^^^^^^^^ @@ -157,7 +157,7 @@ LL + let bad_bige32: f32 = 1.123_456_8E-10; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:86:13 + --> tests/ui/excessive_precision.rs:93:13 | LL | let _ = 2.225_073_858_507_201_1e-308_f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -169,7 +169,7 @@ LL + let _ = 2.225_073_858_507_201e-308_f64; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:90:13 + --> tests/ui/excessive_precision.rs:97:13 | LL | let _ = 1.000_000_000_000_001e-324_f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -181,7 +181,7 @@ LL + let _ = 0_f64; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:101:20 + --> tests/ui/excessive_precision.rs:108:20 | LL | const _: f64 = 3.0000000000000000e+00; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -192,5 +192,56 @@ LL - const _: f64 = 3.0000000000000000e+00; LL + const _: f64 = 3.0; | -error: aborting due to 16 previous errors +error: float has excessive precision + --> tests/ui/excessive_precision.rs:113:18 + | +LL | let _: f32 = 1.01234567890123456789012345678901234567890; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: consider making it a `const` item + --> tests/ui/excessive_precision.rs:113:5 + | +LL | let _: f32 = 1.01234567890123456789012345678901234567890; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider changing the type or truncating it to + | +LL - let _: f32 = 1.01234567890123456789012345678901234567890; +LL + let _: f32 = 1.012_345_7; + | + +error: float has excessive precision + --> tests/ui/excessive_precision.rs:115:18 + | +LL | let _: f64 = 1.01234567890123456789012345678901234567890; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: consider making it a `const` item + --> tests/ui/excessive_precision.rs:115:5 + | +LL | let _: f64 = 1.01234567890123456789012345678901234567890; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider changing the type or truncating it to + | +LL - let _: f64 = 1.01234567890123456789012345678901234567890; +LL + let _: f64 = 1.012_345_678_901_234_6; + | + +error: float has excessive precision + --> tests/ui/excessive_precision.rs:127:17 + | +LL | let gamma = 0.5772156649015328606065120900824024310421; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: consider making it a `const` item + --> tests/ui/excessive_precision.rs:127:5 + | +LL | let gamma = 0.5772156649015328606065120900824024310421; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider changing the type or truncating it to + | +LL - let gamma = 0.5772156649015328606065120900824024310421; +LL + let gamma = 0.577_215_664_901_532_9; + | + +error: aborting due to 19 previous errors diff --git a/src/tools/clippy/tests/ui/float_equality_without_abs.rs b/src/tools/clippy/tests/ui/float_equality_without_abs.rs index a1548db6710..e1dd7902683 100644 --- a/src/tools/clippy/tests/ui/float_equality_without_abs.rs +++ b/src/tools/clippy/tests/ui/float_equality_without_abs.rs @@ -1,8 +1,8 @@ +#![feature(f128)] +#![feature(f16)] #![warn(clippy::float_equality_without_abs)] //@no-rustfix: suggestions cause type ambiguity -// FIXME(f16_f128): add tests for these types when abs is available - pub fn is_roughly_equal(a: f32, b: f32) -> bool { (a - b) < f32::EPSILON //~^ float_equality_without_abs @@ -44,10 +44,20 @@ pub fn main() { let _ = f32::EPSILON > 1.0 - 2.0; //~^ float_equality_without_abs + let _ = (a as f16 - b as f16) < f16::EPSILON; + //~^ float_equality_without_abs + + let _ = (a as f128 - b as f128) < f128::EPSILON; + //~^ float_equality_without_abs + // those are correct + let _ = (a as f16 - b as f16).abs() < f16::EPSILON; let _ = (a - b).abs() < f32::EPSILON; let _ = (a as f64 - b as f64).abs() < f64::EPSILON; + let _ = (a as f128 - b as f128).abs() < f128::EPSILON; + let _ = f16::EPSILON > (a as f16 - b as f16).abs(); let _ = f32::EPSILON > (a - b).abs(); let _ = f64::EPSILON > (a as f64 - b as f64).abs(); + let _ = f128::EPSILON > (a as f128 - b as f128).abs(); } diff --git a/src/tools/clippy/tests/ui/float_equality_without_abs.stderr b/src/tools/clippy/tests/ui/float_equality_without_abs.stderr index d4c89ce72ba..55a150dead5 100644 --- a/src/tools/clippy/tests/ui/float_equality_without_abs.stderr +++ b/src/tools/clippy/tests/ui/float_equality_without_abs.stderr @@ -89,5 +89,21 @@ LL | let _ = f32::EPSILON > 1.0 - 2.0; | | | help: add `.abs()`: `(1.0 - 2.0).abs()` -error: aborting due to 11 previous errors +error: float equality check without `.abs()` + --> tests/ui/float_equality_without_abs.rs:47:13 + | +LL | let _ = (a as f16 - b as f16) < f16::EPSILON; + | ---------------------^^^^^^^^^^^^^^^ + | | + | help: add `.abs()`: `(a as f16 - b as f16).abs()` + +error: float equality check without `.abs()` + --> tests/ui/float_equality_without_abs.rs:50:13 + | +LL | let _ = (a as f128 - b as f128) < f128::EPSILON; + | -----------------------^^^^^^^^^^^^^^^^ + | | + | help: add `.abs()`: `(a as f128 - b as f128).abs()` + +error: aborting due to 13 previous errors diff --git a/src/tools/clippy/tests/ui/if_then_some_else_none.fixed b/src/tools/clippy/tests/ui/if_then_some_else_none.fixed index d14a805b666..0fd130609ae 100644 --- a/src/tools/clippy/tests/ui/if_then_some_else_none.fixed +++ b/src/tools/clippy/tests/ui/if_then_some_else_none.fixed @@ -165,3 +165,44 @@ mod issue15257 { do_something((i % 2 == 0).then_some(closure_fn)); } } + +fn issue15005() { + struct Counter { + count: u32, + } + + impl Counter { + fn new() -> Counter { + Counter { count: 0 } + } + } + + impl Iterator for Counter { + type Item = u32; + + fn next(&mut self) -> Option<Self::Item> { + //~v if_then_some_else_none + (self.count < 5).then(|| { self.count += 1; self.count }) + } + } +} + +fn statements_from_macro() { + macro_rules! mac { + () => { + println!("foo"); + println!("bar"); + }; + } + //~v if_then_some_else_none + let _ = true.then(|| { mac!(); 42 }); +} + +fn dont_lint_inside_macros() { + macro_rules! mac { + ($cond:expr, $res:expr) => { + if $cond { Some($res) } else { None } + }; + } + let _: Option<u32> = mac!(true, 42); +} diff --git a/src/tools/clippy/tests/ui/if_then_some_else_none.rs b/src/tools/clippy/tests/ui/if_then_some_else_none.rs index bb0072f3157..640828aa9bf 100644 --- a/src/tools/clippy/tests/ui/if_then_some_else_none.rs +++ b/src/tools/clippy/tests/ui/if_then_some_else_none.rs @@ -211,3 +211,54 @@ mod issue15257 { }); } } + +fn issue15005() { + struct Counter { + count: u32, + } + + impl Counter { + fn new() -> Counter { + Counter { count: 0 } + } + } + + impl Iterator for Counter { + type Item = u32; + + fn next(&mut self) -> Option<Self::Item> { + //~v if_then_some_else_none + if self.count < 5 { + self.count += 1; + Some(self.count) + } else { + None + } + } + } +} + +fn statements_from_macro() { + macro_rules! mac { + () => { + println!("foo"); + println!("bar"); + }; + } + //~v if_then_some_else_none + let _ = if true { + mac!(); + Some(42) + } else { + None + }; +} + +fn dont_lint_inside_macros() { + macro_rules! mac { + ($cond:expr, $res:expr) => { + if $cond { Some($res) } else { None } + }; + } + let _: Option<u32> = mac!(true, 42); +} diff --git a/src/tools/clippy/tests/ui/if_then_some_else_none.stderr b/src/tools/clippy/tests/ui/if_then_some_else_none.stderr index c2e624a0a73..58651a05594 100644 --- a/src/tools/clippy/tests/ui/if_then_some_else_none.stderr +++ b/src/tools/clippy/tests/ui/if_then_some_else_none.stderr @@ -116,5 +116,28 @@ LL | | None LL | | }); | |_________^ help: try: `(i % 2 == 0).then_some(closure_fn)` -error: aborting due to 11 previous errors +error: this could be simplified with `bool::then` + --> tests/ui/if_then_some_else_none.rs:231:13 + | +LL | / if self.count < 5 { +LL | | self.count += 1; +LL | | Some(self.count) +LL | | } else { +LL | | None +LL | | } + | |_____________^ help: try: `(self.count < 5).then(|| { self.count += 1; self.count })` + +error: this could be simplified with `bool::then` + --> tests/ui/if_then_some_else_none.rs:249:13 + | +LL | let _ = if true { + | _____________^ +LL | | mac!(); +LL | | Some(42) +LL | | } else { +LL | | None +LL | | }; + | |_____^ help: try: `true.then(|| { mac!(); 42 })` + +error: aborting due to 13 previous errors diff --git a/src/tools/clippy/tests/ui/infinite_loops.rs b/src/tools/clippy/tests/ui/infinite_loops.rs index 9b8c3933197..7d01a7fb61f 100644 --- a/src/tools/clippy/tests/ui/infinite_loops.rs +++ b/src/tools/clippy/tests/ui/infinite_loops.rs @@ -521,4 +521,19 @@ mod tokio_spawn_test { } } +mod issue15541 { + async fn good() -> ! { + loop { + std::future::pending().await + } + } + + async fn bad() { + //~v infinite_loop + loop { + std::future::pending().await + } + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/infinite_loops.stderr b/src/tools/clippy/tests/ui/infinite_loops.stderr index 4c6b6f725f1..319f1e5012b 100644 --- a/src/tools/clippy/tests/ui/infinite_loops.stderr +++ b/src/tools/clippy/tests/ui/infinite_loops.stderr @@ -333,5 +333,15 @@ LL | | } | = help: if this is not intended, try adding a `break` or `return` condition in the loop -error: aborting due to 23 previous errors +error: infinite loop detected + --> tests/ui/infinite_loops.rs:533:9 + | +LL | / loop { +LL | | std::future::pending().await +LL | | } + | |_________^ + | + = help: if this is not intended, try adding a `break` or `return` condition in the loop + +error: aborting due to 24 previous errors diff --git a/src/tools/clippy/tests/ui/manual_is_ascii_check.fixed b/src/tools/clippy/tests/ui/manual_is_ascii_check.fixed index 7b0d1906834..406336dbd09 100644 --- a/src/tools/clippy/tests/ui/manual_is_ascii_check.fixed +++ b/src/tools/clippy/tests/ui/manual_is_ascii_check.fixed @@ -108,6 +108,8 @@ fn generics() { //~^ manual_is_ascii_check take_while(|c: char| c.is_ascii_uppercase()); //~^ manual_is_ascii_check + take_while(|c: char| c.is_ascii_uppercase()); + //~^ manual_is_ascii_check } fn adds_type_reference() { @@ -115,4 +117,6 @@ fn adds_type_reference() { //~^ manual_is_ascii_check let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c: &&mut char| c.is_ascii_digit()).collect(); //~^ manual_is_ascii_check + let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c: &&mut char| c.is_ascii_digit()).collect(); + //~^ manual_is_ascii_check } diff --git a/src/tools/clippy/tests/ui/manual_is_ascii_check.rs b/src/tools/clippy/tests/ui/manual_is_ascii_check.rs index e4f7fe9f583..a624497e75e 100644 --- a/src/tools/clippy/tests/ui/manual_is_ascii_check.rs +++ b/src/tools/clippy/tests/ui/manual_is_ascii_check.rs @@ -108,6 +108,8 @@ fn generics() { //~^ manual_is_ascii_check take_while(|c: char| ('A'..='Z').contains(&c)); //~^ manual_is_ascii_check + take_while(|c| matches!(c, 'A'..='Z')); + //~^ manual_is_ascii_check } fn adds_type_reference() { @@ -115,4 +117,6 @@ fn adds_type_reference() { //~^ manual_is_ascii_check let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c| ('0'..='9').contains(c)).collect(); //~^ manual_is_ascii_check + let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c| matches!(c, '0'..='9')).collect(); + //~^ manual_is_ascii_check } diff --git a/src/tools/clippy/tests/ui/manual_is_ascii_check.stderr b/src/tools/clippy/tests/ui/manual_is_ascii_check.stderr index 9fd7f457b42..cb2548ea731 100644 --- a/src/tools/clippy/tests/ui/manual_is_ascii_check.stderr +++ b/src/tools/clippy/tests/ui/manual_is_ascii_check.stderr @@ -176,7 +176,19 @@ LL | take_while(|c: char| ('A'..='Z').contains(&c)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `c.is_ascii_uppercase()` error: manual check for common ascii range - --> tests/ui/manual_is_ascii_check.rs:114:63 + --> tests/ui/manual_is_ascii_check.rs:111:20 + | +LL | take_while(|c| matches!(c, 'A'..='Z')); + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL - take_while(|c| matches!(c, 'A'..='Z')); +LL + take_while(|c: char| c.is_ascii_uppercase()); + | + +error: manual check for common ascii range + --> tests/ui/manual_is_ascii_check.rs:116:63 | LL | let digits: Vec<&char> = ['1', 'A'].iter().take_while(|c| ('0'..='9').contains(c)).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -188,7 +200,7 @@ LL + let digits: Vec<&char> = ['1', 'A'].iter().take_while(|c: &&char| c.is_ | error: manual check for common ascii range - --> tests/ui/manual_is_ascii_check.rs:116:71 + --> tests/ui/manual_is_ascii_check.rs:118:71 | LL | let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c| ('0'..='9').contains(c)).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -199,5 +211,17 @@ LL - let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c| ('0'. LL + let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c: &&mut char| c.is_ascii_digit()).collect(); | -error: aborting due to 29 previous errors +error: manual check for common ascii range + --> tests/ui/manual_is_ascii_check.rs:120:71 + | +LL | let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c| matches!(c, '0'..='9')).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL - let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c| matches!(c, '0'..='9')).collect(); +LL + let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c: &&mut char| c.is_ascii_digit()).collect(); + | + +error: aborting due to 31 previous errors diff --git a/src/tools/clippy/tests/ui/map_identity.fixed b/src/tools/clippy/tests/ui/map_identity.fixed index 6c971ba6338..a10aae8cc12 100644 --- a/src/tools/clippy/tests/ui/map_identity.fixed +++ b/src/tools/clippy/tests/ui/map_identity.fixed @@ -1,3 +1,4 @@ +//@require-annotations-for-level: ERROR #![warn(clippy::map_identity)] #![allow(clippy::needless_return, clippy::disallowed_names)] @@ -72,20 +73,33 @@ fn issue11764() { } fn issue13904() { - // don't lint: `it.next()` would not be legal as `it` is immutable - let it = [1, 2, 3].into_iter(); - let _ = it.map(|x| x).next(); + // lint, but there's a catch: + // when we remove the `.map()`, `it.next()` would require `it` to be mutable + // therefore, include that in the suggestion as well + let mut it = [1, 2, 3].into_iter(); + let _ = it.next(); + //~^ map_identity + //~| HELP: remove the call to `map`, and make `it` mutable + + // lint + let mut index = [1, 2, 3].into_iter(); + let mut subindex = (index.by_ref().take(3), 42); + let _ = subindex.0.next(); + //~^ map_identity + //~| HELP: remove the call to `map`, and make `subindex` mutable // lint #[allow(unused_mut)] let mut it = [1, 2, 3].into_iter(); let _ = it.next(); //~^ map_identity + //~| HELP: remove the call to `map` // lint let it = [1, 2, 3].into_iter(); let _ = { it }.next(); //~^ map_identity + //~| HELP: remove the call to `map` } // same as `issue11764`, but for arrays diff --git a/src/tools/clippy/tests/ui/map_identity.rs b/src/tools/clippy/tests/ui/map_identity.rs index 59dcfcda3b6..fc6e018f924 100644 --- a/src/tools/clippy/tests/ui/map_identity.rs +++ b/src/tools/clippy/tests/ui/map_identity.rs @@ -1,3 +1,4 @@ +//@require-annotations-for-level: ERROR #![warn(clippy::map_identity)] #![allow(clippy::needless_return, clippy::disallowed_names)] @@ -78,20 +79,33 @@ fn issue11764() { } fn issue13904() { - // don't lint: `it.next()` would not be legal as `it` is immutable + // lint, but there's a catch: + // when we remove the `.map()`, `it.next()` would require `it` to be mutable + // therefore, include that in the suggestion as well let it = [1, 2, 3].into_iter(); let _ = it.map(|x| x).next(); + //~^ map_identity + //~| HELP: remove the call to `map`, and make `it` mutable + + // lint + let mut index = [1, 2, 3].into_iter(); + let subindex = (index.by_ref().take(3), 42); + let _ = subindex.0.map(|n| n).next(); + //~^ map_identity + //~| HELP: remove the call to `map`, and make `subindex` mutable // lint #[allow(unused_mut)] let mut it = [1, 2, 3].into_iter(); let _ = it.map(|x| x).next(); //~^ map_identity + //~| HELP: remove the call to `map` // lint let it = [1, 2, 3].into_iter(); let _ = { it }.map(|x| x).next(); //~^ map_identity + //~| HELP: remove the call to `map` } // same as `issue11764`, but for arrays diff --git a/src/tools/clippy/tests/ui/map_identity.stderr b/src/tools/clippy/tests/ui/map_identity.stderr index a50c0d6b87b..8d19d62cdc6 100644 --- a/src/tools/clippy/tests/ui/map_identity.stderr +++ b/src/tools/clippy/tests/ui/map_identity.stderr @@ -1,5 +1,5 @@ error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:7:47 + --> tests/ui/map_identity.rs:8:47 | LL | let _: Vec<_> = x.iter().map(not_identity).map(|x| return x).collect(); | ^^^^^^^^^^^^^^^^^^ help: remove the call to `map` @@ -8,25 +8,25 @@ LL | let _: Vec<_> = x.iter().map(not_identity).map(|x| return x).collect(); = help: to override `-D warnings` add `#[allow(clippy::map_identity)]` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:9:57 + --> tests/ui/map_identity.rs:10:57 | LL | let _: Vec<_> = x.iter().map(std::convert::identity).map(|y| y).collect(); | ^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:9:29 + --> tests/ui/map_identity.rs:10:29 | LL | let _: Vec<_> = x.iter().map(std::convert::identity).map(|y| y).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:12:32 + --> tests/ui/map_identity.rs:13:32 | LL | let _: Option<u8> = Some(3).map(|x| x); | ^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:14:36 + --> tests/ui/map_identity.rs:15:36 | LL | let _: Result<i8, f32> = Ok(-3).map(|x| { | ____________________________________^ @@ -36,19 +36,19 @@ LL | | }); | |______^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:25:36 + --> tests/ui/map_identity.rs:26:36 | LL | let _: Result<u32, u32> = Ok(1).map_err(|a| a); | ^^^^^^^^^^^^^^^ help: remove the call to `map_err` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:36:22 + --> tests/ui/map_identity.rs:37:22 | LL | let _ = x.clone().map(|(x, y)| (x, y)); | ^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:38:22 + --> tests/ui/map_identity.rs:39:22 | LL | let _ = x.clone().map(|(x, y)| { | ______________________^ @@ -58,76 +58,100 @@ LL | | }); | |______^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:42:22 + --> tests/ui/map_identity.rs:43:22 | LL | let _ = x.clone().map(|(x, y)| return (x, y)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:46:22 + --> tests/ui/map_identity.rs:47:22 | LL | let _ = y.clone().map(|(x, y, (z, (w,)))| (x, y, (z, (w,)))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:76:30 + --> tests/ui/map_identity.rs:77:30 | LL | let _ = x.iter().copied().map(|(x, y)| (x, y)); | ^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:88:15 + --> tests/ui/map_identity.rs:86:15 + | +LL | let _ = it.map(|x| x).next(); + | ^^^^^^^^^^^ + | +help: remove the call to `map`, and make `it` mutable + | +LL ~ let mut it = [1, 2, 3].into_iter(); +LL ~ let _ = it.next(); + | + +error: unnecessary map of the identity function + --> tests/ui/map_identity.rs:93:23 + | +LL | let _ = subindex.0.map(|n| n).next(); + | ^^^^^^^^^^^ + | +help: remove the call to `map`, and make `subindex` mutable + | +LL ~ let mut subindex = (index.by_ref().take(3), 42); +LL ~ let _ = subindex.0.next(); + | + +error: unnecessary map of the identity function + --> tests/ui/map_identity.rs:100:15 | LL | let _ = it.map(|x| x).next(); | ^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:93:19 + --> tests/ui/map_identity.rs:106:19 | LL | let _ = { it }.map(|x| x).next(); | ^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:105:30 + --> tests/ui/map_identity.rs:119:30 | LL | let _ = x.iter().copied().map(|[x, y]| [x, y]); | ^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:131:26 + --> tests/ui/map_identity.rs:145:26 | LL | let _ = x.into_iter().map(|Foo { foo, bar }| Foo { foo, bar }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:135:26 + --> tests/ui/map_identity.rs:149:26 | LL | let _ = x.into_iter().map(|Foo { foo, bar }| foo::Foo { foo, bar }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:143:26 + --> tests/ui/map_identity.rs:157:26 | LL | let _ = x.into_iter().map(|Foo { foo, bar }| Foo { foo: foo, bar: bar }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:147:26 + --> tests/ui/map_identity.rs:161:26 | LL | let _ = x.into_iter().map(|Foo { foo, bar }| Foo { bar, foo }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:157:26 + --> tests/ui/map_identity.rs:171:26 | LL | let _ = x.into_iter().map(|Foo2(foo, bar)| Foo2(foo, bar)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:161:26 + --> tests/ui/map_identity.rs:175:26 | LL | let _ = x.into_iter().map(|Foo2(foo, bar)| foo::Foo2(foo, bar)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` -error: aborting due to 20 previous errors +error: aborting due to 22 previous errors diff --git a/src/tools/clippy/tests/ui/missing_inline.rs b/src/tools/clippy/tests/ui/missing_inline.rs index 223c7447975..8e937d60951 100644 --- a/src/tools/clippy/tests/ui/missing_inline.rs +++ b/src/tools/clippy/tests/ui/missing_inline.rs @@ -97,3 +97,10 @@ pub mod issue15301 { println!("Just called a Rust function from Rust!"); } } + +pub mod issue15491 { + pub trait Foo { + #[allow(clippy::missing_inline_in_public_items)] + fn foo(&self) {} + } +} diff --git a/src/tools/clippy/tests/ui/missing_transmute_annotations_unfixable.rs b/src/tools/clippy/tests/ui/missing_transmute_annotations_unfixable.rs new file mode 100644 index 00000000000..08ba3b791ee --- /dev/null +++ b/src/tools/clippy/tests/ui/missing_transmute_annotations_unfixable.rs @@ -0,0 +1,29 @@ +//@no-rustfix + +fn issue14984() { + async fn e() {} + async fn x() -> u32 { + 0 + } + async fn y() -> f32 { + 0.0 + }; + let mut yy = unsafe { std::ptr::read(&y()) }; + yy = unsafe { std::mem::transmute(std::ptr::read(&x())) }; + //~^ missing_transmute_annotations + + let mut zz = 0u8; + zz = unsafe { std::mem::transmute(std::ptr::read(&x())) }; + //~^ missing_transmute_annotations + + yy = unsafe { std::mem::transmute(zz) }; + //~^ missing_transmute_annotations + + fn a() -> impl Sized { + 0u32 + } + + let mut b: f32 = 0.0; + b = unsafe { std::mem::transmute(a()) }; + //~^ missing_transmute_annotations +} diff --git a/src/tools/clippy/tests/ui/missing_transmute_annotations_unfixable.stderr b/src/tools/clippy/tests/ui/missing_transmute_annotations_unfixable.stderr new file mode 100644 index 00000000000..83efdce13f7 --- /dev/null +++ b/src/tools/clippy/tests/ui/missing_transmute_annotations_unfixable.stderr @@ -0,0 +1,36 @@ +error: transmute used without annotations + --> tests/ui/missing_transmute_annotations_unfixable.rs:12:29 + | +LL | yy = unsafe { std::mem::transmute(std::ptr::read(&x())) }; + | ^^^^^^^^^ + | + = help: consider giving the source and destination types a name, and adding missing type annotations + = note: `-D clippy::missing-transmute-annotations` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::missing_transmute_annotations)]` + +error: transmute used without annotations + --> tests/ui/missing_transmute_annotations_unfixable.rs:16:29 + | +LL | zz = unsafe { std::mem::transmute(std::ptr::read(&x())) }; + | ^^^^^^^^^ + | + = help: consider giving the origin type a name, and adding missing type annotations + +error: transmute used without annotations + --> tests/ui/missing_transmute_annotations_unfixable.rs:19:29 + | +LL | yy = unsafe { std::mem::transmute(zz) }; + | ^^^^^^^^^ + | + = help: consider giving the destination type a name, and adding missing type annotations + +error: transmute used without annotations + --> tests/ui/missing_transmute_annotations_unfixable.rs:27:28 + | +LL | b = unsafe { std::mem::transmute(a()) }; + | ^^^^^^^^^ + | + = help: consider giving `a()`'s type a name, and adding missing type annotations + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/mut_reference.fixed b/src/tools/clippy/tests/ui/mut_reference.fixed new file mode 100644 index 00000000000..03d854099e6 --- /dev/null +++ b/src/tools/clippy/tests/ui/mut_reference.fixed @@ -0,0 +1,170 @@ +#![allow(clippy::mut_mut)] + +fn takes_ref(a: &i32) {} +fn takes_refmut(a: &mut i32) {} +fn takes_ref_ref(a: &&i32) {} +fn takes_refmut_ref(a: &mut &i32) {} +fn takes_ref_refmut(a: &&mut i32) {} +fn takes_refmut_refmut(a: &mut &mut i32) {} +fn takes_raw_const(a: *const i32) {} +fn takes_raw_mut(a: *mut i32) {} + +mod issue11268 { + macro_rules! x { + (1 $f:expr) => { + $f(&mut 1); + }; + (2 $f:expr) => { + $f(&mut &1) + }; + (3 $f:expr) => { + $f(&mut &mut 1) + }; + (4 $f:expr) => { + let mut a = 1; + $f(&raw mut a) + }; + } + + fn f() { + x!(1 super::takes_ref); + x!(1 super::takes_refmut); + x!(2 super::takes_refmut_ref); + x!(3 super::takes_ref_refmut); + x!(3 super::takes_refmut_refmut); + x!(4 super::takes_raw_const); + x!(4 super::takes_raw_mut); + } +} + +struct MyStruct; + +impl MyStruct { + fn takes_ref(&self, a: &i32) {} + fn takes_refmut(&self, a: &mut i32) {} + fn takes_ref_ref(&self, a: &&i32) {} + fn takes_refmut_ref(&self, a: &mut &i32) {} + fn takes_ref_refmut(&self, a: &&mut i32) {} + fn takes_refmut_refmut(&self, a: &mut &mut i32) {} + fn takes_raw_const(&self, a: *const i32) {} + fn takes_raw_mut(&self, a: *mut i32) {} +} + +#[warn(clippy::unnecessary_mut_passed)] +fn main() { + // Functions + takes_ref(&42); + //~^ unnecessary_mut_passed + takes_ref_ref(&&42); + //~^ unnecessary_mut_passed + takes_ref_refmut(&&mut 42); + //~^ unnecessary_mut_passed + takes_raw_const(&42); + //~^ unnecessary_mut_passed + + let as_ptr: fn(&i32) = takes_ref; + as_ptr(&42); + //~^ unnecessary_mut_passed + let as_ptr: fn(&&i32) = takes_ref_ref; + as_ptr(&&42); + //~^ unnecessary_mut_passed + let as_ptr: fn(&&mut i32) = takes_ref_refmut; + as_ptr(&&mut 42); + //~^ unnecessary_mut_passed + let as_ptr: fn(*const i32) = takes_raw_const; + as_ptr(&42); + //~^ unnecessary_mut_passed + + // Methods + let my_struct = MyStruct; + my_struct.takes_ref(&42); + //~^ unnecessary_mut_passed + my_struct.takes_ref_ref(&&42); + //~^ unnecessary_mut_passed + my_struct.takes_ref_refmut(&&mut 42); + //~^ unnecessary_mut_passed + my_struct.takes_raw_const(&42); + //~^ unnecessary_mut_passed + + // No error + + // Functions + takes_ref(&42); + let as_ptr: fn(&i32) = takes_ref; + as_ptr(&42); + + takes_refmut(&mut 42); + let as_ptr: fn(&mut i32) = takes_refmut; + as_ptr(&mut 42); + + takes_ref_ref(&&42); + let as_ptr: fn(&&i32) = takes_ref_ref; + as_ptr(&&42); + + takes_refmut_ref(&mut &42); + let as_ptr: fn(&mut &i32) = takes_refmut_ref; + as_ptr(&mut &42); + + takes_ref_refmut(&&mut 42); + let as_ptr: fn(&&mut i32) = takes_ref_refmut; + as_ptr(&&mut 42); + + takes_refmut_refmut(&mut &mut 42); + let as_ptr: fn(&mut &mut i32) = takes_refmut_refmut; + as_ptr(&mut &mut 42); + + takes_raw_const(&42); + let as_ptr: fn(*const i32) = takes_raw_const; + as_ptr(&42); + + takes_raw_mut(&mut 42); + let as_ptr: fn(*mut i32) = takes_raw_mut; + as_ptr(&mut 42); + + let a = &mut 42; + let b = &mut &42; + let c = &mut &mut 42; + takes_ref(a); + takes_ref_ref(b); + takes_ref_refmut(c); + takes_raw_const(a); + + // Methods + my_struct.takes_ref(&42); + my_struct.takes_refmut(&mut 42); + my_struct.takes_ref_ref(&&42); + my_struct.takes_refmut_ref(&mut &42); + my_struct.takes_ref_refmut(&&mut 42); + my_struct.takes_refmut_refmut(&mut &mut 42); + my_struct.takes_raw_const(&42); + my_struct.takes_raw_mut(&mut 42); + my_struct.takes_ref(a); + my_struct.takes_ref_ref(b); + my_struct.takes_ref_refmut(c); + my_struct.takes_raw_const(a); + my_struct.takes_raw_mut(a); +} + +// not supported currently +fn raw_ptrs(my_struct: MyStruct) { + let mut n = 42; + + takes_raw_const(&raw mut n); + + let as_ptr: fn(*const i32) = takes_raw_const; + as_ptr(&raw mut n); + + my_struct.takes_raw_const(&raw mut n); + + // No error + + takes_raw_const(&raw const n); + takes_raw_mut(&raw mut n); + + let a = &raw mut n; + takes_raw_const(a); + + my_struct.takes_raw_const(&raw const n); + my_struct.takes_raw_mut(&raw mut n); + my_struct.takes_raw_const(a); +} diff --git a/src/tools/clippy/tests/ui/mut_reference.rs b/src/tools/clippy/tests/ui/mut_reference.rs index f664c373cdc..80e3f506927 100644 --- a/src/tools/clippy/tests/ui/mut_reference.rs +++ b/src/tools/clippy/tests/ui/mut_reference.rs @@ -1,60 +1,170 @@ -#![allow(unused_variables, dead_code)] -//@no-rustfix -fn takes_an_immutable_reference(a: &i32) {} -fn takes_a_mutable_reference(a: &mut i32) {} +#![allow(clippy::mut_mut)] + +fn takes_ref(a: &i32) {} +fn takes_refmut(a: &mut i32) {} +fn takes_ref_ref(a: &&i32) {} +fn takes_refmut_ref(a: &mut &i32) {} +fn takes_ref_refmut(a: &&mut i32) {} +fn takes_refmut_refmut(a: &mut &mut i32) {} +fn takes_raw_const(a: *const i32) {} +fn takes_raw_mut(a: *mut i32) {} mod issue11268 { macro_rules! x { - ($f:expr) => { + (1 $f:expr) => { $f(&mut 1); }; + (2 $f:expr) => { + $f(&mut &1) + }; + (3 $f:expr) => { + $f(&mut &mut 1) + }; + (4 $f:expr) => { + let mut a = 1; + $f(&raw mut a) + }; } fn f() { - x!(super::takes_an_immutable_reference); - x!(super::takes_a_mutable_reference); + x!(1 super::takes_ref); + x!(1 super::takes_refmut); + x!(2 super::takes_refmut_ref); + x!(3 super::takes_ref_refmut); + x!(3 super::takes_refmut_refmut); + x!(4 super::takes_raw_const); + x!(4 super::takes_raw_mut); } } struct MyStruct; impl MyStruct { - fn takes_an_immutable_reference(&self, a: &i32) {} - - fn takes_a_mutable_reference(&self, a: &mut i32) {} + fn takes_ref(&self, a: &i32) {} + fn takes_refmut(&self, a: &mut i32) {} + fn takes_ref_ref(&self, a: &&i32) {} + fn takes_refmut_ref(&self, a: &mut &i32) {} + fn takes_ref_refmut(&self, a: &&mut i32) {} + fn takes_refmut_refmut(&self, a: &mut &mut i32) {} + fn takes_raw_const(&self, a: *const i32) {} + fn takes_raw_mut(&self, a: *mut i32) {} } #[warn(clippy::unnecessary_mut_passed)] fn main() { // Functions - takes_an_immutable_reference(&mut 42); + takes_ref(&mut 42); + //~^ unnecessary_mut_passed + takes_ref_ref(&mut &42); + //~^ unnecessary_mut_passed + takes_ref_refmut(&mut &mut 42); + //~^ unnecessary_mut_passed + takes_raw_const(&mut 42); //~^ unnecessary_mut_passed - let as_ptr: fn(&i32) = takes_an_immutable_reference; + let as_ptr: fn(&i32) = takes_ref; + as_ptr(&mut 42); + //~^ unnecessary_mut_passed + let as_ptr: fn(&&i32) = takes_ref_ref; + as_ptr(&mut &42); + //~^ unnecessary_mut_passed + let as_ptr: fn(&&mut i32) = takes_ref_refmut; + as_ptr(&mut &mut 42); + //~^ unnecessary_mut_passed + let as_ptr: fn(*const i32) = takes_raw_const; as_ptr(&mut 42); //~^ unnecessary_mut_passed // Methods let my_struct = MyStruct; - my_struct.takes_an_immutable_reference(&mut 42); + my_struct.takes_ref(&mut 42); + //~^ unnecessary_mut_passed + my_struct.takes_ref_ref(&mut &42); + //~^ unnecessary_mut_passed + my_struct.takes_ref_refmut(&mut &mut 42); + //~^ unnecessary_mut_passed + my_struct.takes_raw_const(&mut 42); //~^ unnecessary_mut_passed // No error // Functions - takes_an_immutable_reference(&42); - let as_ptr: fn(&i32) = takes_an_immutable_reference; + takes_ref(&42); + let as_ptr: fn(&i32) = takes_ref; as_ptr(&42); - takes_a_mutable_reference(&mut 42); - let as_ptr: fn(&mut i32) = takes_a_mutable_reference; + takes_refmut(&mut 42); + let as_ptr: fn(&mut i32) = takes_refmut; + as_ptr(&mut 42); + + takes_ref_ref(&&42); + let as_ptr: fn(&&i32) = takes_ref_ref; + as_ptr(&&42); + + takes_refmut_ref(&mut &42); + let as_ptr: fn(&mut &i32) = takes_refmut_ref; + as_ptr(&mut &42); + + takes_ref_refmut(&&mut 42); + let as_ptr: fn(&&mut i32) = takes_ref_refmut; + as_ptr(&&mut 42); + + takes_refmut_refmut(&mut &mut 42); + let as_ptr: fn(&mut &mut i32) = takes_refmut_refmut; + as_ptr(&mut &mut 42); + + takes_raw_const(&42); + let as_ptr: fn(*const i32) = takes_raw_const; + as_ptr(&42); + + takes_raw_mut(&mut 42); + let as_ptr: fn(*mut i32) = takes_raw_mut; as_ptr(&mut 42); let a = &mut 42; - takes_an_immutable_reference(a); + let b = &mut &42; + let c = &mut &mut 42; + takes_ref(a); + takes_ref_ref(b); + takes_ref_refmut(c); + takes_raw_const(a); // Methods - my_struct.takes_an_immutable_reference(&42); - my_struct.takes_a_mutable_reference(&mut 42); - my_struct.takes_an_immutable_reference(a); + my_struct.takes_ref(&42); + my_struct.takes_refmut(&mut 42); + my_struct.takes_ref_ref(&&42); + my_struct.takes_refmut_ref(&mut &42); + my_struct.takes_ref_refmut(&&mut 42); + my_struct.takes_refmut_refmut(&mut &mut 42); + my_struct.takes_raw_const(&42); + my_struct.takes_raw_mut(&mut 42); + my_struct.takes_ref(a); + my_struct.takes_ref_ref(b); + my_struct.takes_ref_refmut(c); + my_struct.takes_raw_const(a); + my_struct.takes_raw_mut(a); +} + +// not supported currently +fn raw_ptrs(my_struct: MyStruct) { + let mut n = 42; + + takes_raw_const(&raw mut n); + + let as_ptr: fn(*const i32) = takes_raw_const; + as_ptr(&raw mut n); + + my_struct.takes_raw_const(&raw mut n); + + // No error + + takes_raw_const(&raw const n); + takes_raw_mut(&raw mut n); + + let a = &raw mut n; + takes_raw_const(a); + + my_struct.takes_raw_const(&raw const n); + my_struct.takes_raw_mut(&raw mut n); + my_struct.takes_raw_const(a); } diff --git a/src/tools/clippy/tests/ui/mut_reference.stderr b/src/tools/clippy/tests/ui/mut_reference.stderr index 474221329c2..5ecfaa37416 100644 --- a/src/tools/clippy/tests/ui/mut_reference.stderr +++ b/src/tools/clippy/tests/ui/mut_reference.stderr @@ -1,23 +1,77 @@ -error: the function `takes_an_immutable_reference` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:30:34 +error: the function `takes_ref` doesn't need a mutable reference + --> tests/ui/mut_reference.rs:56:15 | -LL | takes_an_immutable_reference(&mut 42); - | ^^^^^^^ +LL | takes_ref(&mut 42); + | ^^^^^^^ help: remove this `mut`: `&42` | = note: `-D clippy::unnecessary-mut-passed` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_mut_passed)]` +error: the function `takes_ref_ref` doesn't need a mutable reference + --> tests/ui/mut_reference.rs:58:19 + | +LL | takes_ref_ref(&mut &42); + | ^^^^^^^^ help: remove this `mut`: `&&42` + +error: the function `takes_ref_refmut` doesn't need a mutable reference + --> tests/ui/mut_reference.rs:60:22 + | +LL | takes_ref_refmut(&mut &mut 42); + | ^^^^^^^^^^^^ help: remove this `mut`: `&&mut 42` + +error: the function `takes_raw_const` doesn't need a mutable reference + --> tests/ui/mut_reference.rs:62:21 + | +LL | takes_raw_const(&mut 42); + | ^^^^^^^ help: remove this `mut`: `&42` + +error: the function `as_ptr` doesn't need a mutable reference + --> tests/ui/mut_reference.rs:66:12 + | +LL | as_ptr(&mut 42); + | ^^^^^^^ help: remove this `mut`: `&42` + +error: the function `as_ptr` doesn't need a mutable reference + --> tests/ui/mut_reference.rs:69:12 + | +LL | as_ptr(&mut &42); + | ^^^^^^^^ help: remove this `mut`: `&&42` + +error: the function `as_ptr` doesn't need a mutable reference + --> tests/ui/mut_reference.rs:72:12 + | +LL | as_ptr(&mut &mut 42); + | ^^^^^^^^^^^^ help: remove this `mut`: `&&mut 42` + error: the function `as_ptr` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:34:12 + --> tests/ui/mut_reference.rs:75:12 | LL | as_ptr(&mut 42); - | ^^^^^^^ + | ^^^^^^^ help: remove this `mut`: `&42` + +error: the method `takes_ref` doesn't need a mutable reference + --> tests/ui/mut_reference.rs:80:25 + | +LL | my_struct.takes_ref(&mut 42); + | ^^^^^^^ help: remove this `mut`: `&42` + +error: the method `takes_ref_ref` doesn't need a mutable reference + --> tests/ui/mut_reference.rs:82:29 + | +LL | my_struct.takes_ref_ref(&mut &42); + | ^^^^^^^^ help: remove this `mut`: `&&42` + +error: the method `takes_ref_refmut` doesn't need a mutable reference + --> tests/ui/mut_reference.rs:84:32 + | +LL | my_struct.takes_ref_refmut(&mut &mut 42); + | ^^^^^^^^^^^^ help: remove this `mut`: `&&mut 42` -error: the method `takes_an_immutable_reference` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:39:44 +error: the method `takes_raw_const` doesn't need a mutable reference + --> tests/ui/mut_reference.rs:86:31 | -LL | my_struct.takes_an_immutable_reference(&mut 42); - | ^^^^^^^ +LL | my_struct.takes_raw_const(&mut 42); + | ^^^^^^^ help: remove this `mut`: `&42` -error: aborting due to 3 previous errors +error: aborting due to 12 previous errors diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.stderr b/src/tools/clippy/tests/ui/needless_bool/fixable.stderr index 3f117ee5a50..9404d07ba0e 100644 --- a/src/tools/clippy/tests/ui/needless_bool/fixable.stderr +++ b/src/tools/clippy/tests/ui/needless_bool/fixable.stderr @@ -135,7 +135,7 @@ error: equality checks against true are unnecessary --> tests/ui/needless_bool/fixable.rs:157:8 | LL | if x == true {}; - | ^^^^^^^^^ help: try simplifying it as shown: `x` + | ^^^^^^^^^ help: try: `x` | = note: `-D clippy::bool-comparison` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::bool_comparison)]` @@ -144,19 +144,19 @@ error: equality checks against false can be replaced by a negation --> tests/ui/needless_bool/fixable.rs:162:8 | LL | if x == false {}; - | ^^^^^^^^^^ help: try simplifying it as shown: `!x` + | ^^^^^^^^^^ help: try: `!x` error: equality checks against true are unnecessary --> tests/ui/needless_bool/fixable.rs:173:8 | LL | if x == true {}; - | ^^^^^^^^^ help: try simplifying it as shown: `x` + | ^^^^^^^^^ help: try: `x` error: equality checks against false can be replaced by a negation --> tests/ui/needless_bool/fixable.rs:175:8 | LL | if x == false {}; - | ^^^^^^^^^^ help: try simplifying it as shown: `!x` + | ^^^^^^^^^^ help: try: `!x` error: this if-then-else expression returns a bool literal --> tests/ui/needless_bool/fixable.rs:185:12 diff --git a/src/tools/clippy/tests/ui/needless_for_each_unfixable.rs b/src/tools/clippy/tests/ui/needless_for_each_unfixable.rs index 159cbe49828..24ce5786b91 100644 --- a/src/tools/clippy/tests/ui/needless_for_each_unfixable.rs +++ b/src/tools/clippy/tests/ui/needless_for_each_unfixable.rs @@ -1,6 +1,6 @@ //@no-rustfix: overlapping suggestions #![warn(clippy::needless_for_each)] -#![allow(clippy::needless_return, clippy::uninlined_format_args)] +#![allow(clippy::needless_return)] fn main() { let v: Vec<i32> = Vec::new(); @@ -11,7 +11,22 @@ fn main() { if *v == 10 { return; } else { - println!("{}", v); + println!("{v}"); } }); } + +fn issue9912() { + let mut i = 0; + // Changing this to a `for` loop would break type inference + [].iter().for_each(move |_: &i32| { + //~^ needless_for_each + i += 1; + }); + + // Changing this would actually be okay, but we still suggest `MaybeIncorrect`ly + [1i32].iter().for_each(move |_: &i32| { + //~^ needless_for_each + i += 1; + }); +} diff --git a/src/tools/clippy/tests/ui/needless_for_each_unfixable.stderr b/src/tools/clippy/tests/ui/needless_for_each_unfixable.stderr index 3a3a240c5a3..6bc56db68a0 100644 --- a/src/tools/clippy/tests/ui/needless_for_each_unfixable.stderr +++ b/src/tools/clippy/tests/ui/needless_for_each_unfixable.stderr @@ -19,7 +19,7 @@ LL + LL + if *v == 10 { LL + return; LL + } else { -LL + println!("{}", v); +LL + println!("{v}"); LL + } LL + } | @@ -29,5 +29,39 @@ LL - return; LL + continue; | -error: aborting due to 1 previous error +error: needless use of `for_each` + --> tests/ui/needless_for_each_unfixable.rs:22:5 + | +LL | / [].iter().for_each(move |_: &i32| { +LL | | +LL | | i += 1; +LL | | }); + | |_______^ + | +help: try + | +LL ~ for _ in [].iter() { +LL + +LL + i += 1; +LL + } + | + +error: needless use of `for_each` + --> tests/ui/needless_for_each_unfixable.rs:28:5 + | +LL | / [1i32].iter().for_each(move |_: &i32| { +LL | | +LL | | i += 1; +LL | | }); + | |_______^ + | +help: try + | +LL ~ for _ in [1i32].iter() { +LL + +LL + i += 1; +LL + } + | + +error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/needless_range_loop.rs b/src/tools/clippy/tests/ui/needless_range_loop.rs index 70cf9fa7369..ea4591d8b71 100644 --- a/src/tools/clippy/tests/ui/needless_range_loop.rs +++ b/src/tools/clippy/tests/ui/needless_range_loop.rs @@ -210,3 +210,35 @@ fn needless_loop() { black_box([1, 2, 3, 4, 5, 6, 7, 8][i]); } } + +fn issue_15068() { + let a = vec![vec![0u8; MAX_LEN]; MAX_LEN]; + let b = vec![0u8; MAX_LEN]; + + for i in 0..MAX_LEN { + // no error + let _ = a[0][i]; + let _ = b[i]; + } + + for i in 0..MAX_LEN { + // no error + let _ = a[i][0]; + let _ = b[i]; + } + + for i in 0..MAX_LEN { + // no error + let _ = a[i][b[i] as usize]; + } + + for i in 0..MAX_LEN { + //~^ needless_range_loop + let _ = a[i][i]; + } + + for i in 0..MAX_LEN { + //~^ needless_range_loop + let _ = a[0][i]; + } +} diff --git a/src/tools/clippy/tests/ui/needless_range_loop.stderr b/src/tools/clippy/tests/ui/needless_range_loop.stderr index 23c085f9d3b..33a519d8a80 100644 --- a/src/tools/clippy/tests/ui/needless_range_loop.stderr +++ b/src/tools/clippy/tests/ui/needless_range_loop.stderr @@ -168,5 +168,29 @@ LL - for i in 0..vec.len() { LL + for (i, <item>) in vec.iter_mut().enumerate() { | -error: aborting due to 14 previous errors +error: the loop variable `i` is used to index `a` + --> tests/ui/needless_range_loop.rs:235:14 + | +LL | for i in 0..MAX_LEN { + | ^^^^^^^^^^ + | +help: consider using an iterator and enumerate() + | +LL - for i in 0..MAX_LEN { +LL + for (i, <item>) in a.iter().enumerate().take(MAX_LEN) { + | + +error: the loop variable `i` is only used to index `a` + --> tests/ui/needless_range_loop.rs:240:14 + | +LL | for i in 0..MAX_LEN { + | ^^^^^^^^^^ + | +help: consider using an iterator + | +LL - for i in 0..MAX_LEN { +LL + for <item> in a.iter().take(MAX_LEN) { + | + +error: aborting due to 16 previous errors diff --git a/src/tools/clippy/tests/ui/nonminimal_bool.rs b/src/tools/clippy/tests/ui/nonminimal_bool.rs index cacce9a7d1c..f03f74dfafe 100644 --- a/src/tools/clippy/tests/ui/nonminimal_bool.rs +++ b/src/tools/clippy/tests/ui/nonminimal_bool.rs @@ -182,14 +182,12 @@ fn issue_5794() { if !b == true {} //~^ nonminimal_bool //~| bool_comparison - //~| bool_comparison if !b != true {} //~^ nonminimal_bool //~| bool_comparison if true == !b {} //~^ nonminimal_bool //~| bool_comparison - //~| bool_comparison if true != !b {} //~^ nonminimal_bool //~| bool_comparison diff --git a/src/tools/clippy/tests/ui/nonminimal_bool.stderr b/src/tools/clippy/tests/ui/nonminimal_bool.stderr index c20412974b2..6a20b9216da 100644 --- a/src/tools/clippy/tests/ui/nonminimal_bool.stderr +++ b/src/tools/clippy/tests/ui/nonminimal_bool.stderr @@ -154,98 +154,86 @@ error: this boolean expression can be simplified LL | if !b == true {} | ^^^^^^^^^^ help: try: `b != true` -error: this comparison might be written more concisely +error: equality checks against true are unnecessary --> tests/ui/nonminimal_bool.rs:182:8 | LL | if !b == true {} - | ^^^^^^^^^^ help: try simplifying it as shown: `b != true` + | ^^^^^^^^^^ help: try: `!b` | = note: `-D clippy::bool-comparison` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::bool_comparison)]` -error: equality checks against true are unnecessary - --> tests/ui/nonminimal_bool.rs:182:8 - | -LL | if !b == true {} - | ^^^^^^^^^^ help: try simplifying it as shown: `!b` - error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:186:8 + --> tests/ui/nonminimal_bool.rs:185:8 | LL | if !b != true {} | ^^^^^^^^^^ help: try: `b == true` error: inequality checks against true can be replaced by a negation - --> tests/ui/nonminimal_bool.rs:186:8 + --> tests/ui/nonminimal_bool.rs:185:8 | LL | if !b != true {} - | ^^^^^^^^^^ help: try simplifying it as shown: `b` + | ^^^^^^^^^^ help: try: `b` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:189:8 + --> tests/ui/nonminimal_bool.rs:188:8 | LL | if true == !b {} | ^^^^^^^^^^ help: try: `true != b` -error: this comparison might be written more concisely - --> tests/ui/nonminimal_bool.rs:189:8 - | -LL | if true == !b {} - | ^^^^^^^^^^ help: try simplifying it as shown: `true != b` - error: equality checks against true are unnecessary - --> tests/ui/nonminimal_bool.rs:189:8 + --> tests/ui/nonminimal_bool.rs:188:8 | LL | if true == !b {} - | ^^^^^^^^^^ help: try simplifying it as shown: `!b` + | ^^^^^^^^^^ help: try: `!b` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:193:8 + --> tests/ui/nonminimal_bool.rs:191:8 | LL | if true != !b {} | ^^^^^^^^^^ help: try: `true == b` error: inequality checks against true can be replaced by a negation - --> tests/ui/nonminimal_bool.rs:193:8 + --> tests/ui/nonminimal_bool.rs:191:8 | LL | if true != !b {} - | ^^^^^^^^^^ help: try simplifying it as shown: `b` + | ^^^^^^^^^^ help: try: `b` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:196:8 + --> tests/ui/nonminimal_bool.rs:194:8 | LL | if !b == !c {} | ^^^^^^^^ help: try: `b == c` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:198:8 + --> tests/ui/nonminimal_bool.rs:196:8 | LL | if !b != !c {} | ^^^^^^^^ help: try: `b != c` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:214:8 + --> tests/ui/nonminimal_bool.rs:212:8 | LL | if !(a < 2.0 && !b) { | ^^^^^^^^^^^^^^^^ help: try: `a >= 2.0 || b` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:233:12 + --> tests/ui/nonminimal_bool.rs:231:12 | LL | if !(matches!(ty, TyKind::Ref(_, _, _)) && !is_mutable(&expr)) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `!matches!(ty, TyKind::Ref(_, _, _)) || is_mutable(&expr)` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:253:8 + --> tests/ui/nonminimal_bool.rs:251:8 | LL | if !S != true {} | ^^^^^^^^^^ help: try: `S == true` error: inequality checks against true can be replaced by a negation - --> tests/ui/nonminimal_bool.rs:253:8 + --> tests/ui/nonminimal_bool.rs:251:8 | LL | if !S != true {} - | ^^^^^^^^^^ help: try simplifying it as shown: `!!S` + | ^^^^^^^^^^ help: try: `!!S` -error: aborting due to 33 previous errors +error: aborting due to 31 previous errors diff --git a/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.fixed b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.fixed index 55c1b8f110c..340be7c7e93 100644 --- a/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.fixed +++ b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.fixed @@ -102,4 +102,13 @@ fn option_map_unit_fn() { //~^ option_map_unit_fn } +fn issue15568() { + unsafe fn f(_: u32) {} + let x = Some(3); + if let Some(x) = x { unsafe { f(x) } } + //~^ option_map_unit_fn + if let Some(x) = x { unsafe { f(x) } } + //~^ option_map_unit_fn +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.rs b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.rs index 5ed47e4c60b..d902c87379b 100644 --- a/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.rs +++ b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.rs @@ -102,4 +102,13 @@ fn option_map_unit_fn() { //~^ option_map_unit_fn } +fn issue15568() { + unsafe fn f(_: u32) {} + let x = Some(3); + x.map(|x| unsafe { f(x) }); + //~^ option_map_unit_fn + x.map(|x| unsafe { { f(x) } }); + //~^ option_map_unit_fn +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.stderr b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.stderr index 3f7abae34ee..2405aa9a7cc 100644 --- a/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.stderr +++ b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.stderr @@ -153,5 +153,21 @@ LL | option().map(|value| println!("{:?}", value)); | | | help: try: `if let Some(value) = option() { println!("{:?}", value) }` -error: aborting due to 19 previous errors +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` + --> tests/ui/option_map_unit_fn_fixable.rs:108:5 + | +LL | x.map(|x| unsafe { f(x) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try: `if let Some(x) = x { unsafe { f(x) } }` + +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` + --> tests/ui/option_map_unit_fn_fixable.rs:110:5 + | +LL | x.map(|x| unsafe { { f(x) } }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try: `if let Some(x) = x { unsafe { f(x) } }` + +error: aborting due to 21 previous errors diff --git a/src/tools/clippy/tests/ui/or_then_unwrap.fixed b/src/tools/clippy/tests/ui/or_then_unwrap.fixed index ba9beef57af..9660b82fe7d 100644 --- a/src/tools/clippy/tests/ui/or_then_unwrap.fixed +++ b/src/tools/clippy/tests/ui/or_then_unwrap.fixed @@ -28,6 +28,12 @@ fn main() { // //~^^ or_then_unwrap + // Call with macro should preserve the macro call rather than expand it + let option: Option<Vec<&str>> = None; + let _ = option.unwrap_or(vec!["fallback"]); // should trigger lint + // + //~^^ or_then_unwrap + // as part of a method chain let option: Option<&str> = None; let _ = option.map(|v| v).unwrap_or("fallback").to_string().chars(); // should trigger lint diff --git a/src/tools/clippy/tests/ui/or_then_unwrap.rs b/src/tools/clippy/tests/ui/or_then_unwrap.rs index fac90249a24..c3873352116 100644 --- a/src/tools/clippy/tests/ui/or_then_unwrap.rs +++ b/src/tools/clippy/tests/ui/or_then_unwrap.rs @@ -28,6 +28,12 @@ fn main() { // //~^^ or_then_unwrap + // Call with macro should preserve the macro call rather than expand it + let option: Option<Vec<&str>> = None; + let _ = option.or(Some(vec!["fallback"])).unwrap(); // should trigger lint + // + //~^^ or_then_unwrap + // as part of a method chain let option: Option<&str> = None; let _ = option.map(|v| v).or(Some("fallback")).unwrap().to_string().chars(); // should trigger lint diff --git a/src/tools/clippy/tests/ui/or_then_unwrap.stderr b/src/tools/clippy/tests/ui/or_then_unwrap.stderr index 1160498c605..3e66b15edbd 100644 --- a/src/tools/clippy/tests/ui/or_then_unwrap.stderr +++ b/src/tools/clippy/tests/ui/or_then_unwrap.stderr @@ -14,10 +14,16 @@ LL | let _ = result.or::<&str>(Ok("fallback")).unwrap(); // should trigger l | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or("fallback")` error: found `.or(Some(…)).unwrap()` - --> tests/ui/or_then_unwrap.rs:33:31 + --> tests/ui/or_then_unwrap.rs:33:20 + | +LL | let _ = option.or(Some(vec!["fallback"])).unwrap(); // should trigger lint + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or(vec!["fallback"])` + +error: found `.or(Some(…)).unwrap()` + --> tests/ui/or_then_unwrap.rs:39:31 | LL | let _ = option.map(|v| v).or(Some("fallback")).unwrap().to_string().chars(); // should trigger lint | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or("fallback")` -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/panicking_macros.rs b/src/tools/clippy/tests/ui/panicking_macros.rs index 65854d7eb4b..b044be7d54a 100644 --- a/src/tools/clippy/tests/ui/panicking_macros.rs +++ b/src/tools/clippy/tests/ui/panicking_macros.rs @@ -31,6 +31,20 @@ fn panic() { let b = a + 2; } +const fn panic_const() { + let a = 2; + panic!(); + //~^ panic + + panic!("message"); + //~^ panic + + panic!("{} {}", "panic with", "multiple arguments"); + //~^ panic + + let b = a + 2; +} + fn todo() { let a = 2; todo!(); @@ -114,6 +128,7 @@ fn debug_assert_msg() { fn main() { panic(); + panic_const(); todo(); unimplemented(); unreachable(); diff --git a/src/tools/clippy/tests/ui/panicking_macros.stderr b/src/tools/clippy/tests/ui/panicking_macros.stderr index 03e459e4ec6..7b767ae0f65 100644 --- a/src/tools/clippy/tests/ui/panicking_macros.stderr +++ b/src/tools/clippy/tests/ui/panicking_macros.stderr @@ -19,9 +19,27 @@ error: `panic` should not be present in production code LL | panic!("{} {}", "panic with", "multiple arguments"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: `todo` should not be present in production code +error: `panic` should not be present in production code --> tests/ui/panicking_macros.rs:36:5 | +LL | panic!(); + | ^^^^^^^^ + +error: `panic` should not be present in production code + --> tests/ui/panicking_macros.rs:39:5 + | +LL | panic!("message"); + | ^^^^^^^^^^^^^^^^^ + +error: `panic` should not be present in production code + --> tests/ui/panicking_macros.rs:42:5 + | +LL | panic!("{} {}", "panic with", "multiple arguments"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `todo` should not be present in production code + --> tests/ui/panicking_macros.rs:50:5 + | LL | todo!(); | ^^^^^^^ | @@ -29,19 +47,19 @@ LL | todo!(); = help: to override `-D warnings` add `#[allow(clippy::todo)]` error: `todo` should not be present in production code - --> tests/ui/panicking_macros.rs:39:5 + --> tests/ui/panicking_macros.rs:53:5 | LL | todo!("message"); | ^^^^^^^^^^^^^^^^ error: `todo` should not be present in production code - --> tests/ui/panicking_macros.rs:42:5 + --> tests/ui/panicking_macros.rs:56:5 | LL | todo!("{} {}", "panic with", "multiple arguments"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `unimplemented` should not be present in production code - --> tests/ui/panicking_macros.rs:50:5 + --> tests/ui/panicking_macros.rs:64:5 | LL | unimplemented!(); | ^^^^^^^^^^^^^^^^ @@ -50,19 +68,19 @@ LL | unimplemented!(); = help: to override `-D warnings` add `#[allow(clippy::unimplemented)]` error: `unimplemented` should not be present in production code - --> tests/ui/panicking_macros.rs:53:5 + --> tests/ui/panicking_macros.rs:67:5 | LL | unimplemented!("message"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: `unimplemented` should not be present in production code - --> tests/ui/panicking_macros.rs:56:5 + --> tests/ui/panicking_macros.rs:70:5 | LL | unimplemented!("{} {}", "panic with", "multiple arguments"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: usage of the `unreachable!` macro - --> tests/ui/panicking_macros.rs:64:5 + --> tests/ui/panicking_macros.rs:78:5 | LL | unreachable!(); | ^^^^^^^^^^^^^^ @@ -71,40 +89,40 @@ LL | unreachable!(); = help: to override `-D warnings` add `#[allow(clippy::unreachable)]` error: usage of the `unreachable!` macro - --> tests/ui/panicking_macros.rs:67:5 + --> tests/ui/panicking_macros.rs:81:5 | LL | unreachable!("message"); | ^^^^^^^^^^^^^^^^^^^^^^^ error: usage of the `unreachable!` macro - --> tests/ui/panicking_macros.rs:70:5 + --> tests/ui/panicking_macros.rs:84:5 | LL | unreachable!("{} {}", "panic with", "multiple arguments"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `panic` should not be present in production code - --> tests/ui/panicking_macros.rs:78:5 + --> tests/ui/panicking_macros.rs:92:5 | LL | panic!(); | ^^^^^^^^ error: `todo` should not be present in production code - --> tests/ui/panicking_macros.rs:81:5 + --> tests/ui/panicking_macros.rs:95:5 | LL | todo!(); | ^^^^^^^ error: `unimplemented` should not be present in production code - --> tests/ui/panicking_macros.rs:84:5 + --> tests/ui/panicking_macros.rs:98:5 | LL | unimplemented!(); | ^^^^^^^^^^^^^^^^ error: usage of the `unreachable!` macro - --> tests/ui/panicking_macros.rs:87:5 + --> tests/ui/panicking_macros.rs:101:5 | LL | unreachable!(); | ^^^^^^^^^^^^^^ -error: aborting due to 16 previous errors +error: aborting due to 19 previous errors diff --git a/src/tools/clippy/tests/ui/print_literal.fixed b/src/tools/clippy/tests/ui/print_literal.fixed index ebfe19c700e..26139f9b671 100644 --- a/src/tools/clippy/tests/ui/print_literal.fixed +++ b/src/tools/clippy/tests/ui/print_literal.fixed @@ -105,3 +105,16 @@ fn issue_14930() { println!("Hello x is {0:2$.1$}", 0.01, 2, 3); //~^ print_literal } + +fn issue_15576() { + println!("Hello x is {1:.*}", 5, 0.01); + //~^ print_literal + + println!("Hello x is {:.p$}", 0.01, p = 5); + //~^ print_literal + + println!( + "Hello name: x is {1:.*} (which {1} with {0} places)", 5, 0.01 + ); + //~^^ print_literal +} diff --git a/src/tools/clippy/tests/ui/print_literal.rs b/src/tools/clippy/tests/ui/print_literal.rs index 8f3d9be0698..7c4cf028e84 100644 --- a/src/tools/clippy/tests/ui/print_literal.rs +++ b/src/tools/clippy/tests/ui/print_literal.rs @@ -106,3 +106,17 @@ fn issue_14930() { println!("Hello {0} is {1:3$.2$}", "x", 0.01, 2, 3); //~^ print_literal } + +fn issue_15576() { + println!("Hello {} is {2:.*}", "x", 5, 0.01); + //~^ print_literal + + println!("Hello {} is {:.p$}", "x", 0.01, p = 5); + //~^ print_literal + + println!( + "Hello {}: {2} is {3:.*} (which {3} with {1} places)", + "name", 5, "x", 0.01 + ); + //~^^ print_literal +} diff --git a/src/tools/clippy/tests/ui/print_literal.stderr b/src/tools/clippy/tests/ui/print_literal.stderr index 1c378017d15..c136f52800f 100644 --- a/src/tools/clippy/tests/ui/print_literal.stderr +++ b/src/tools/clippy/tests/ui/print_literal.stderr @@ -277,5 +277,41 @@ LL - println!("Hello {0} is {1:3$.2$}", "x", 0.01, 2, 3); LL + println!("Hello x is {0:2$.1$}", 0.01, 2, 3); | -error: aborting due to 22 previous errors +error: literal with an empty format string + --> tests/ui/print_literal.rs:111:36 + | +LL | println!("Hello {} is {2:.*}", "x", 5, 0.01); + | ^^^ + | +help: try + | +LL - println!("Hello {} is {2:.*}", "x", 5, 0.01); +LL + println!("Hello x is {1:.*}", 5, 0.01); + | + +error: literal with an empty format string + --> tests/ui/print_literal.rs:114:36 + | +LL | println!("Hello {} is {:.p$}", "x", 0.01, p = 5); + | ^^^ + | +help: try + | +LL - println!("Hello {} is {:.p$}", "x", 0.01, p = 5); +LL + println!("Hello x is {:.p$}", 0.01, p = 5); + | + +error: literal with an empty format string + --> tests/ui/print_literal.rs:119:9 + | +LL | "name", 5, "x", 0.01 + | ^^^^^^^^^^^^^^ + | +help: try + | +LL - "Hello {}: {2} is {3:.*} (which {3} with {1} places)", +LL + "Hello name: x is {1:.*} (which {1} with {0} places)", 5, 0.01 + | + +error: aborting due to 25 previous errors diff --git a/src/tools/clippy/tests/ui/ptr_as_ptr.fixed b/src/tools/clippy/tests/ui/ptr_as_ptr.fixed index 78e1ceb480a..4457878b6fa 100644 --- a/src/tools/clippy/tests/ui/ptr_as_ptr.fixed +++ b/src/tools/clippy/tests/ui/ptr_as_ptr.fixed @@ -229,3 +229,9 @@ fn issue15283() { //~^ ptr_as_ptr } } + +fn issue15232() { + let mut b = Box::new(0i32); + let _ptr = std::ptr::addr_of_mut!(*b).cast::<()>(); + //~^ ptr_as_ptr +} diff --git a/src/tools/clippy/tests/ui/ptr_as_ptr.rs b/src/tools/clippy/tests/ui/ptr_as_ptr.rs index 70732cf0a6c..9124fc912d2 100644 --- a/src/tools/clippy/tests/ui/ptr_as_ptr.rs +++ b/src/tools/clippy/tests/ui/ptr_as_ptr.rs @@ -229,3 +229,9 @@ fn issue15283() { //~^ ptr_as_ptr } } + +fn issue15232() { + let mut b = Box::new(0i32); + let _ptr = std::ptr::addr_of_mut!(*b) as *mut (); + //~^ ptr_as_ptr +} diff --git a/src/tools/clippy/tests/ui/ptr_as_ptr.stderr b/src/tools/clippy/tests/ui/ptr_as_ptr.stderr index c0a2a4b6d20..af21c1e35f5 100644 --- a/src/tools/clippy/tests/ui/ptr_as_ptr.stderr +++ b/src/tools/clippy/tests/ui/ptr_as_ptr.stderr @@ -199,5 +199,11 @@ error: `as` casting between raw pointers without changing their constness LL | let _: fn() = std::mem::transmute(std::ptr::null::<()>() as *const u8); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null::<u8>()` -error: aborting due to 33 previous errors +error: `as` casting between raw pointers without changing their constness + --> tests/ui/ptr_as_ptr.rs:235:16 + | +LL | let _ptr = std::ptr::addr_of_mut!(*b) as *mut (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `std::ptr::addr_of_mut!(*b).cast::<()>()` + +error: aborting due to 34 previous errors diff --git a/src/tools/clippy/tests/ui/ptr_cast_constness.fixed b/src/tools/clippy/tests/ui/ptr_cast_constness.fixed index 79bfae1f7eb..cf57de53d9f 100644 --- a/src/tools/clippy/tests/ui/ptr_cast_constness.fixed +++ b/src/tools/clippy/tests/ui/ptr_cast_constness.fixed @@ -106,3 +106,9 @@ fn issue14621() { let _ = std::ptr::addr_of_mut!(local).cast_const(); //~^ ptr_cast_constness } + +fn issue11317() { + let r = &0_u32; + let _ptr: *mut u32 = (r as *const u32).cast_mut(); + //~^ ptr_cast_constness +} diff --git a/src/tools/clippy/tests/ui/ptr_cast_constness.rs b/src/tools/clippy/tests/ui/ptr_cast_constness.rs index f6590dabd5b..ea53a0fa8c5 100644 --- a/src/tools/clippy/tests/ui/ptr_cast_constness.rs +++ b/src/tools/clippy/tests/ui/ptr_cast_constness.rs @@ -106,3 +106,9 @@ fn issue14621() { let _ = std::ptr::addr_of_mut!(local) as *const _; //~^ ptr_cast_constness } + +fn issue11317() { + let r = &0_u32; + let _ptr: *mut u32 = r as *const _ as *mut _; + //~^ ptr_cast_constness +} diff --git a/src/tools/clippy/tests/ui/ptr_cast_constness.stderr b/src/tools/clippy/tests/ui/ptr_cast_constness.stderr index 0b1644168ff..4adb5cc5ad7 100644 --- a/src/tools/clippy/tests/ui/ptr_cast_constness.stderr +++ b/src/tools/clippy/tests/ui/ptr_cast_constness.stderr @@ -89,5 +89,11 @@ error: `as` casting between raw pointers while changing only its constness LL | let _ = std::ptr::addr_of_mut!(local) as *const _; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_const`, a safer alternative: `std::ptr::addr_of_mut!(local).cast_const()` -error: aborting due to 14 previous errors +error: `as` casting between raw pointers while changing only its constness + --> tests/ui/ptr_cast_constness.rs:112:26 + | +LL | let _ptr: *mut u32 = r as *const _ as *mut _; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `(r as *const u32).cast_mut()` + +error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui/semicolon_inside_block.fixed b/src/tools/clippy/tests/ui/semicolon_inside_block.fixed index 7eb53e733ad..7308e78aae2 100644 --- a/src/tools/clippy/tests/ui/semicolon_inside_block.fixed +++ b/src/tools/clippy/tests/ui/semicolon_inside_block.fixed @@ -86,3 +86,8 @@ fn main() { unit_fn_block() } + +pub fn issue15388() { + #[rustfmt::skip] + {0; 0}; +} diff --git a/src/tools/clippy/tests/ui/semicolon_inside_block.rs b/src/tools/clippy/tests/ui/semicolon_inside_block.rs index 9fa5b117194..467bf4d779f 100644 --- a/src/tools/clippy/tests/ui/semicolon_inside_block.rs +++ b/src/tools/clippy/tests/ui/semicolon_inside_block.rs @@ -86,3 +86,8 @@ fn main() { unit_fn_block() } + +pub fn issue15388() { + #[rustfmt::skip] + {0; 0}; +} diff --git a/src/tools/clippy/tests/ui/semicolon_inside_block_stmt_expr_attrs.fixed b/src/tools/clippy/tests/ui/semicolon_inside_block_stmt_expr_attrs.fixed new file mode 100644 index 00000000000..5b93a91da00 --- /dev/null +++ b/src/tools/clippy/tests/ui/semicolon_inside_block_stmt_expr_attrs.fixed @@ -0,0 +1,11 @@ +// Test when the feature `stmt_expr_attributes` is enabled + +#![feature(stmt_expr_attributes)] +#![allow(clippy::no_effect)] +#![warn(clippy::semicolon_inside_block)] + +pub fn issue15388() { + #[rustfmt::skip] + {0; 0;} + //~^ semicolon_inside_block +} diff --git a/src/tools/clippy/tests/ui/semicolon_inside_block_stmt_expr_attrs.rs b/src/tools/clippy/tests/ui/semicolon_inside_block_stmt_expr_attrs.rs new file mode 100644 index 00000000000..aa2c0cb5029 --- /dev/null +++ b/src/tools/clippy/tests/ui/semicolon_inside_block_stmt_expr_attrs.rs @@ -0,0 +1,11 @@ +// Test when the feature `stmt_expr_attributes` is enabled + +#![feature(stmt_expr_attributes)] +#![allow(clippy::no_effect)] +#![warn(clippy::semicolon_inside_block)] + +pub fn issue15388() { + #[rustfmt::skip] + {0; 0}; + //~^ semicolon_inside_block +} diff --git a/src/tools/clippy/tests/ui/semicolon_inside_block_stmt_expr_attrs.stderr b/src/tools/clippy/tests/ui/semicolon_inside_block_stmt_expr_attrs.stderr new file mode 100644 index 00000000000..5bb91915a5f --- /dev/null +++ b/src/tools/clippy/tests/ui/semicolon_inside_block_stmt_expr_attrs.stderr @@ -0,0 +1,16 @@ +error: consider moving the `;` inside the block for consistent formatting + --> tests/ui/semicolon_inside_block_stmt_expr_attrs.rs:9:5 + | +LL | {0; 0}; + | ^^^^^^^ + | + = note: `-D clippy::semicolon-inside-block` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::semicolon_inside_block)]` +help: put the `;` here + | +LL - {0; 0}; +LL + {0; 0;} + | + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.fixed b/src/tools/clippy/tests/ui/std_instead_of_core.fixed index 603ab0accb0..c27cec55824 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core.fixed +++ b/src/tools/clippy/tests/ui/std_instead_of_core.fixed @@ -89,3 +89,10 @@ fn msrv_1_76(_: std::net::IpAddr) {} #[clippy::msrv = "1.77"] fn msrv_1_77(_: core::net::IpAddr) {} //~^ std_instead_of_core + +#[warn(clippy::alloc_instead_of_core)] +fn issue15579() { + use std::alloc; + + let layout = alloc::Layout::new::<u8>(); +} diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.rs b/src/tools/clippy/tests/ui/std_instead_of_core.rs index b6d4abad9f8..7d53f7fc307 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core.rs +++ b/src/tools/clippy/tests/ui/std_instead_of_core.rs @@ -89,3 +89,10 @@ fn msrv_1_76(_: std::net::IpAddr) {} #[clippy::msrv = "1.77"] fn msrv_1_77(_: std::net::IpAddr) {} //~^ std_instead_of_core + +#[warn(clippy::alloc_instead_of_core)] +fn issue15579() { + use std::alloc; + + let layout = alloc::Layout::new::<u8>(); +} diff --git a/src/tools/clippy/tests/ui/unit_cmp.rs b/src/tools/clippy/tests/ui/unit_cmp.rs index 93f5b87c3d2..0a0fe3a1990 100644 --- a/src/tools/clippy/tests/ui/unit_cmp.rs +++ b/src/tools/clippy/tests/ui/unit_cmp.rs @@ -68,3 +68,20 @@ fn main() { } ); } + +fn issue15559() { + fn foo() {} + assert_eq!( + //~^ unit_cmp + { + 1; + }, + foo() + ); + assert_eq!(foo(), foo()); + //~^ unit_cmp + + // don't lint on explicitly written unit expr + assert_eq!(foo(), ()); + assert_ne!((), ContainsUnit(()).0); +} diff --git a/src/tools/clippy/tests/ui/unit_cmp.stderr b/src/tools/clippy/tests/ui/unit_cmp.stderr index 8f26749fd86..21aa9dce151 100644 --- a/src/tools/clippy/tests/ui/unit_cmp.stderr +++ b/src/tools/clippy/tests/ui/unit_cmp.stderr @@ -71,5 +71,23 @@ LL | | true; LL | | ); | |_____^ -error: aborting due to 6 previous errors +error: `assert_eq` of unit values detected. This will always succeed + --> tests/ui/unit_cmp.rs:74:5 + | +LL | / assert_eq!( +LL | | +LL | | { +LL | | 1; +LL | | }, +LL | | foo() +LL | | ); + | |_____^ + +error: `assert_eq` of unit values detected. This will always succeed + --> tests/ui/unit_cmp.rs:81:5 + | +LL | assert_eq!(foo(), foo()); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_clone.stderr b/src/tools/clippy/tests/ui/unnecessary_clone.stderr index a4fdf09cd5d..17518e123d1 100644 --- a/src/tools/clippy/tests/ui/unnecessary_clone.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_clone.stderr @@ -2,7 +2,7 @@ error: using `.clone()` on a ref-counted pointer --> tests/ui/unnecessary_clone.rs:23:5 | LL | rc.clone(); - | ^^^^^^^^^^ help: try: `Rc::<bool>::clone(&rc)` + | ^^^^^^^^^^ help: try: `std::rc::Rc::<bool>::clone(&rc)` | = note: `-D clippy::clone-on-ref-ptr` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::clone_on_ref_ptr)]` @@ -11,25 +11,25 @@ error: using `.clone()` on a ref-counted pointer --> tests/ui/unnecessary_clone.rs:28:5 | LL | arc.clone(); - | ^^^^^^^^^^^ help: try: `Arc::<bool>::clone(&arc)` + | ^^^^^^^^^^^ help: try: `std::sync::Arc::<bool>::clone(&arc)` error: using `.clone()` on a ref-counted pointer --> tests/ui/unnecessary_clone.rs:33:5 | LL | rcweak.clone(); - | ^^^^^^^^^^^^^^ help: try: `Weak::<bool>::clone(&rcweak)` + | ^^^^^^^^^^^^^^ help: try: `std::rc::Weak::<bool>::clone(&rcweak)` error: using `.clone()` on a ref-counted pointer --> tests/ui/unnecessary_clone.rs:38:5 | LL | arc_weak.clone(); - | ^^^^^^^^^^^^^^^^ help: try: `Weak::<bool>::clone(&arc_weak)` + | ^^^^^^^^^^^^^^^^ help: try: `std::sync::Weak::<bool>::clone(&arc_weak)` error: using `.clone()` on a ref-counted pointer --> tests/ui/unnecessary_clone.rs:44:33 | LL | let _: Arc<dyn SomeTrait> = x.clone(); - | ^^^^^^^^^ help: try: `Arc::<SomeImpl>::clone(&x)` + | ^^^^^^^^^ help: try: `std::sync::Arc::<SomeImpl>::clone(&x)` error: using `clone` on type `T` which implements the `Copy` trait --> tests/ui/unnecessary_clone.rs:49:5 @@ -56,7 +56,7 @@ error: using `.clone()` on a ref-counted pointer --> tests/ui/unnecessary_clone.rs:108:14 | LL | Some(try_opt!(Some(rc)).clone()) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Rc::<u8>::clone(&try_opt!(Some(rc)))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::rc::Rc::<u8>::clone(&try_opt!(Some(rc)))` error: aborting due to 9 previous errors diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns.fixed b/src/tools/clippy/tests/ui/unnested_or_patterns.fixed index 2081772d06b..339d4a95084 100644 --- a/src/tools/clippy/tests/ui/unnested_or_patterns.fixed +++ b/src/tools/clippy/tests/ui/unnested_or_patterns.fixed @@ -9,6 +9,11 @@ )] #![allow(unreachable_patterns, irrefutable_let_patterns, unused)] +struct S { + x: u8, + y: u8, +} + fn main() { // Should be ignored by this lint, as nesting requires more characters. if let &0 | &2 = &0 {} @@ -45,10 +50,6 @@ fn main() { //~^ unnested_or_patterns if let TS(x, ..) | TS(x, 1 | 2) = TS(0, 0) {} //~^ unnested_or_patterns - struct S { - x: u8, - y: u8, - } if let S { x: 0 | 1, y } = (S { x: 0, y: 1 }) {} //~^ unnested_or_patterns if let S { x: 0, y, .. } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} @@ -77,3 +78,19 @@ mod issue9952 { fn or_in_param((x | x | x): i32) {} //~^ unnested_or_patterns } + +fn issue15219() { + struct Foo { + x: u8, + } + + // the original repro + if let Foo { x } | Foo { x } = (Foo { x: 0 }) {} + + // also works with more fields + if let S { x, y } | S { x, y } = (S { x: 0, y: 0 }) {} + + // `y` not triggering the lint doesn't stop the `x` from getting flagged + if let S { y, x: 0 | 1 } = (S { x: 0, y: 1 }) {} + //~^ unnested_or_patterns +} diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns.rs b/src/tools/clippy/tests/ui/unnested_or_patterns.rs index 6bf8fce3661..f5c99183b0c 100644 --- a/src/tools/clippy/tests/ui/unnested_or_patterns.rs +++ b/src/tools/clippy/tests/ui/unnested_or_patterns.rs @@ -9,6 +9,11 @@ )] #![allow(unreachable_patterns, irrefutable_let_patterns, unused)] +struct S { + x: u8, + y: u8, +} + fn main() { // Should be ignored by this lint, as nesting requires more characters. if let &0 | &2 = &0 {} @@ -45,10 +50,6 @@ fn main() { //~^ unnested_or_patterns if let TS(x, ..) | TS(x, 1) | TS(x, 2) = TS(0, 0) {} //~^ unnested_or_patterns - struct S { - x: u8, - y: u8, - } if let S { x: 0, y } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} //~^ unnested_or_patterns if let S { x: 0, y, .. } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} @@ -77,3 +78,19 @@ mod issue9952 { fn or_in_param((x | (x | x)): i32) {} //~^ unnested_or_patterns } + +fn issue15219() { + struct Foo { + x: u8, + } + + // the original repro + if let Foo { x } | Foo { x } = (Foo { x: 0 }) {} + + // also works with more fields + if let S { x, y } | S { x, y } = (S { x: 0, y: 0 }) {} + + // `y` not triggering the lint doesn't stop the `x` from getting flagged + if let S { y, x: 0 } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} + //~^ unnested_or_patterns +} diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns.stderr b/src/tools/clippy/tests/ui/unnested_or_patterns.stderr index c805dc992b1..d2b617c322c 100644 --- a/src/tools/clippy/tests/ui/unnested_or_patterns.stderr +++ b/src/tools/clippy/tests/ui/unnested_or_patterns.stderr @@ -1,5 +1,5 @@ error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:16:12 + --> tests/ui/unnested_or_patterns.rs:21:12 | LL | if let box 0 | box 2 = Box::new(0) {} | ^^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL + if let box (0 | 2) = Box::new(0) {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:18:12 + --> tests/ui/unnested_or_patterns.rs:23:12 | LL | if let box ((0 | 1)) | box (2 | 3) | box 4 = Box::new(0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL + if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:21:12 + --> tests/ui/unnested_or_patterns.rs:26:12 | LL | if let Some(1) | C0 | Some(2) = None {} | ^^^^^^^^^^^^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL + if let Some(1 | 2) | C0 = None {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:23:12 + --> tests/ui/unnested_or_patterns.rs:28:12 | LL | if let &mut 0 | &mut 2 = &mut 0 {} | ^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL + if let &mut (0 | 2) = &mut 0 {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:25:12 + --> tests/ui/unnested_or_patterns.rs:30:12 | LL | if let x @ 0 | x @ 2 = 0 {} | ^^^^^^^^^^^^^ @@ -61,7 +61,7 @@ LL + if let x @ (0 | 2) = 0 {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:27:12 + --> tests/ui/unnested_or_patterns.rs:32:12 | LL | if let (0, 1) | (0, 2) | (0, 3) = (0, 0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL + if let (0, 1 | 2 | 3) = (0, 0) {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:29:12 + --> tests/ui/unnested_or_patterns.rs:34:12 | LL | if let (1, 0) | (2, 0) | (3, 0) = (0, 0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -85,7 +85,7 @@ LL + if let (1 | 2 | 3, 0) = (0, 0) {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:31:12 + --> tests/ui/unnested_or_patterns.rs:36:12 | LL | if let (x, ..) | (x, 1) | (x, 2) = (0, 1) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -97,7 +97,7 @@ LL + if let (x, ..) | (x, 1 | 2) = (0, 1) {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:33:12 + --> tests/ui/unnested_or_patterns.rs:38:12 | LL | if let [0] | [1] = [0] {} | ^^^^^^^^^ @@ -109,7 +109,7 @@ LL + if let [0 | 1] = [0] {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:35:12 + --> tests/ui/unnested_or_patterns.rs:40:12 | LL | if let [x, 0] | [x, 1] = [0, 1] {} | ^^^^^^^^^^^^^^^ @@ -121,7 +121,7 @@ LL + if let [x, 0 | 1] = [0, 1] {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:37:12 + --> tests/ui/unnested_or_patterns.rs:42:12 | LL | if let [x, 0] | [x, 1] | [x, 2] = [0, 1] {} | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -133,7 +133,7 @@ LL + if let [x, 0 | 1 | 2] = [0, 1] {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:39:12 + --> tests/ui/unnested_or_patterns.rs:44:12 | LL | if let [x, ..] | [x, 1] | [x, 2] = [0, 1] {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -145,7 +145,7 @@ LL + if let [x, ..] | [x, 1 | 2] = [0, 1] {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:42:12 + --> tests/ui/unnested_or_patterns.rs:47:12 | LL | if let TS(0, x) | TS(1, x) = TS(0, 0) {} | ^^^^^^^^^^^^^^^^^^^ @@ -157,7 +157,7 @@ LL + if let TS(0 | 1, x) = TS(0, 0) {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:44:12 + --> tests/ui/unnested_or_patterns.rs:49:12 | LL | if let TS(1, 0) | TS(2, 0) | TS(3, 0) = TS(0, 0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -169,7 +169,7 @@ LL + if let TS(1 | 2 | 3, 0) = TS(0, 0) {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:46:12 + --> tests/ui/unnested_or_patterns.rs:51:12 | LL | if let TS(x, ..) | TS(x, 1) | TS(x, 2) = TS(0, 0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -181,7 +181,7 @@ LL + if let TS(x, ..) | TS(x, 1 | 2) = TS(0, 0) {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:52:12 + --> tests/ui/unnested_or_patterns.rs:53:12 | LL | if let S { x: 0, y } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -193,7 +193,7 @@ LL + if let S { x: 0 | 1, y } = (S { x: 0, y: 1 }) {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:64:12 + --> tests/ui/unnested_or_patterns.rs:65:12 | LL | if let [1] | [53] = [0] {} | ^^^^^^^^^^ @@ -205,7 +205,7 @@ LL + if let [1 | 53] = [0] {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:70:13 + --> tests/ui/unnested_or_patterns.rs:71:13 | LL | let (0 | (1 | _)) = 0; | ^^^^^^^^^^^^^ @@ -217,7 +217,7 @@ LL + let (0 | 1 | _) = 0; | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:73:16 + --> tests/ui/unnested_or_patterns.rs:74:16 | LL | if let (0 | (1 | _)) = 0 {} | ^^^^^^^^^^^^^ @@ -229,7 +229,7 @@ LL + if let (0 | 1 | _) = 0 {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:77:20 + --> tests/ui/unnested_or_patterns.rs:78:20 | LL | fn or_in_param((x | (x | x)): i32) {} | ^^^^^^^^^^^^^ @@ -240,5 +240,17 @@ LL - fn or_in_param((x | (x | x)): i32) {} LL + fn or_in_param((x | x | x): i32) {} | -error: aborting due to 20 previous errors +error: unnested or-patterns + --> tests/ui/unnested_or_patterns.rs:94:12 + | +LL | if let S { y, x: 0 } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: nest the patterns + | +LL - if let S { y, x: 0 } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} +LL + if let S { y, x: 0 | 1 } = (S { x: 0, y: 1 }) {} + | + +error: aborting due to 21 previous errors diff --git a/src/tools/clippy/tests/ui/unused_unit.edition2021.fixed b/src/tools/clippy/tests/ui/unused_unit.edition2021.fixed index def8ef86e3c..8e12bd2c8c7 100644 --- a/src/tools/clippy/tests/ui/unused_unit.edition2021.fixed +++ b/src/tools/clippy/tests/ui/unused_unit.edition2021.fixed @@ -127,14 +127,10 @@ mod issue14577 { trait Unit {} impl Unit for () {} - fn run<R: Unit>(f: impl FnOnce() -> R) { - f(); - } - #[allow(dependency_on_unit_never_type_fallback)] fn bar() { - run(|| { todo!() }); //~[edition2021]^ unused_unit + panic!() } struct UnitStruct; @@ -150,3 +146,24 @@ mod pr14962 { type UnusedParensButNoUnit = Box<dyn (Fn())>; } + +mod issue15035 { + + trait Convert<T> { + fn from(value: T) -> Self; + } + + impl Convert<u64> for () { + fn from(_value: u64) -> Self {} + } + + fn handle<T: Convert<u64>>(value: u64) -> T { + Convert::from(value) + } + + pub fn f() -> Option<bool> { + let result: Result<bool, u64> = Err(42); + // the `-> ()` is required for the inference of `handle`'s return type + result.map_err(|err| -> () { handle(err) }).ok() + } +} diff --git a/src/tools/clippy/tests/ui/unused_unit.edition2021.stderr b/src/tools/clippy/tests/ui/unused_unit.edition2021.stderr index 13cc20d4d7a..9ad3c2df915 100644 --- a/src/tools/clippy/tests/ui/unused_unit.edition2021.stderr +++ b/src/tools/clippy/tests/ui/unused_unit.edition2021.stderr @@ -119,10 +119,10 @@ LL | fn test3()-> (){} | ^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> tests/ui/unused_unit.rs:136:15 + --> tests/ui/unused_unit.rs:131:13 | -LL | run(|| -> () { todo!() }); - | ^^^^^^ help: remove the `-> ()` +LL | fn bar() -> () { + | ^^^^^^ help: remove the `-> ()` error: aborting due to 20 previous errors diff --git a/src/tools/clippy/tests/ui/unused_unit.edition2024.fixed b/src/tools/clippy/tests/ui/unused_unit.edition2024.fixed index f908b958b19..688d2fe9afa 100644 --- a/src/tools/clippy/tests/ui/unused_unit.edition2024.fixed +++ b/src/tools/clippy/tests/ui/unused_unit.edition2024.fixed @@ -127,14 +127,10 @@ mod issue14577 { trait Unit {} impl Unit for () {} - fn run<R: Unit>(f: impl FnOnce() -> R) { - f(); - } - #[allow(dependency_on_unit_never_type_fallback)] - fn bar() { - run(|| -> () { todo!() }); + fn bar() -> () { //~[edition2021]^ unused_unit + panic!() } struct UnitStruct; @@ -150,3 +146,24 @@ mod pr14962 { type UnusedParensButNoUnit = Box<dyn (Fn())>; } + +mod issue15035 { + + trait Convert<T> { + fn from(value: T) -> Self; + } + + impl Convert<u64> for () { + fn from(_value: u64) -> Self {} + } + + fn handle<T: Convert<u64>>(value: u64) -> T { + Convert::from(value) + } + + pub fn f() -> Option<bool> { + let result: Result<bool, u64> = Err(42); + // the `-> ()` is required for the inference of `handle`'s return type + result.map_err(|err| -> () { handle(err) }).ok() + } +} diff --git a/src/tools/clippy/tests/ui/unused_unit.rs b/src/tools/clippy/tests/ui/unused_unit.rs index 7298ec40cc2..31e980f2655 100644 --- a/src/tools/clippy/tests/ui/unused_unit.rs +++ b/src/tools/clippy/tests/ui/unused_unit.rs @@ -127,14 +127,10 @@ mod issue14577 { trait Unit {} impl Unit for () {} - fn run<R: Unit>(f: impl FnOnce() -> R) { - f(); - } - #[allow(dependency_on_unit_never_type_fallback)] - fn bar() { - run(|| -> () { todo!() }); + fn bar() -> () { //~[edition2021]^ unused_unit + panic!() } struct UnitStruct; @@ -150,3 +146,24 @@ mod pr14962 { type UnusedParensButNoUnit = Box<dyn (Fn())>; } + +mod issue15035 { + + trait Convert<T> { + fn from(value: T) -> Self; + } + + impl Convert<u64> for () { + fn from(_value: u64) -> Self {} + } + + fn handle<T: Convert<u64>>(value: u64) -> T { + Convert::from(value) + } + + pub fn f() -> Option<bool> { + let result: Result<bool, u64> = Err(42); + // the `-> ()` is required for the inference of `handle`'s return type + result.map_err(|err| -> () { handle(err) }).ok() + } +} diff --git a/src/tools/clippy/tests/ui/unwrap_in_result.rs b/src/tools/clippy/tests/ui/unwrap_in_result.rs index 4e872c67b42..70c28fe54f3 100644 --- a/src/tools/clippy/tests/ui/unwrap_in_result.rs +++ b/src/tools/clippy/tests/ui/unwrap_in_result.rs @@ -1,4 +1,5 @@ #![warn(clippy::unwrap_in_result)] +#![allow(clippy::ok_expect)] struct A; @@ -20,10 +21,9 @@ impl A { // should be detected fn bad_divisible_by_3(i_str: String) -> Result<bool, String> { - //~^ unwrap_in_result - // checks whether a string represents a number divisible by 3 let i = i_str.parse::<i32>().unwrap(); + //~^ unwrap_in_result if i % 3 == 0 { Ok(true) } else { @@ -32,9 +32,8 @@ impl A { } fn example_option_expect(i_str: String) -> Option<bool> { + let i = i_str.parse::<i32>().ok().expect("not a number"); //~^ unwrap_in_result - - let i = i_str.parse::<i32>().expect("not a number"); if i % 3 == 0 { return Some(true); } @@ -42,13 +41,66 @@ impl A { } fn in_closure(a: Option<bool>) -> Option<bool> { - //~^ unwrap_in_result + // No lint inside a closure let c = || a.unwrap(); - Some(c()) + + // But lint outside + let a = c().then_some(true); + let _ = a.unwrap(); + //~^ unwrap_in_result + + None } + + const fn in_const_inside_fn() -> bool { + const A: bool = { + const fn inner(b: Option<bool>) -> Option<bool> { + Some(b.unwrap()) + //~^ unwrap_in_result + } + + // No lint inside `const` + inner(Some(true)).unwrap() + }; + A + } + + fn in_static_inside_fn() -> bool { + static A: bool = { + const fn inner(b: Option<bool>) -> Option<bool> { + Some(b.unwrap()) + //~^ unwrap_in_result + } + + // No lint inside `static` + inner(Some(true)).unwrap() + }; + A + } +} + +macro_rules! mac { + () => { + Option::unwrap(Some(3)) + }; +} + +fn type_relative_unwrap() -> Option<()> { + _ = Option::unwrap(Some(3)); + //~^ unwrap_in_result + + // Do not lint macro output + _ = mac!(); + + None } -fn main() { - A::bad_divisible_by_3("3".to_string()); - A::good_divisible_by_3("3".to_string()); +fn main() -> Result<(), ()> { + A::bad_divisible_by_3("3".to_string()).unwrap(); + //~^ unwrap_in_result + A::good_divisible_by_3("3".to_string()).unwrap(); + //~^ unwrap_in_result + Result::unwrap(A::good_divisible_by_3("3".to_string())); + //~^ unwrap_in_result + Ok(()) } diff --git a/src/tools/clippy/tests/ui/unwrap_in_result.stderr b/src/tools/clippy/tests/ui/unwrap_in_result.stderr index 5e3eab813e0..804b44246dc 100644 --- a/src/tools/clippy/tests/ui/unwrap_in_result.stderr +++ b/src/tools/clippy/tests/ui/unwrap_in_result.stderr @@ -1,55 +1,107 @@ -error: used unwrap or expect in a function that returns result or option - --> tests/ui/unwrap_in_result.rs:22:5 - | -LL | / fn bad_divisible_by_3(i_str: String) -> Result<bool, String> { -... | -LL | | } - | |_____^ - | - = help: unwrap and expect should not be used in a function that returns result or option -note: potential non-recoverable error(s) - --> tests/ui/unwrap_in_result.rs:26:17 +error: `unwrap` used in a function that returns a `Result` + --> tests/ui/unwrap_in_result.rs:25:17 | LL | let i = i_str.parse::<i32>().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: in this function signature + --> tests/ui/unwrap_in_result.rs:23:45 + | +LL | fn bad_divisible_by_3(i_str: String) -> Result<bool, String> { + | ^^^^^^^^^^^^^^^^^^^^ + = help: consider using the `?` operator or calling the `.map_err()` method = note: `-D clippy::unwrap-in-result` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unwrap_in_result)]` -error: used unwrap or expect in a function that returns result or option - --> tests/ui/unwrap_in_result.rs:34:5 +error: `expect` used in a function that returns an `Option` + --> tests/ui/unwrap_in_result.rs:35:17 + | +LL | let i = i_str.parse::<i32>().ok().expect("not a number"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: in this function signature + --> tests/ui/unwrap_in_result.rs:34:48 + | +LL | fn example_option_expect(i_str: String) -> Option<bool> { + | ^^^^^^^^^^^^ + = help: consider using the `?` operator + +error: `unwrap` used in a function that returns an `Option` + --> tests/ui/unwrap_in_result.rs:49:17 + | +LL | let _ = a.unwrap(); + | ^^^^^^^^^^ + | +note: in this function signature + --> tests/ui/unwrap_in_result.rs:43:39 + | +LL | fn in_closure(a: Option<bool>) -> Option<bool> { + | ^^^^^^^^^^^^ + = help: consider using the `?` operator + +error: `unwrap` used in a function that returns an `Option` + --> tests/ui/unwrap_in_result.rs:58:22 | -LL | / fn example_option_expect(i_str: String) -> Option<bool> { -LL | | -LL | | -LL | | let i = i_str.parse::<i32>().expect("not a number"); -... | -LL | | None -LL | | } - | |_____^ +LL | Some(b.unwrap()) + | ^^^^^^^^^^ | - = help: unwrap and expect should not be used in a function that returns result or option -note: potential non-recoverable error(s) - --> tests/ui/unwrap_in_result.rs:37:17 +note: in this function signature + --> tests/ui/unwrap_in_result.rs:57:48 | -LL | let i = i_str.parse::<i32>().expect("not a number"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const fn inner(b: Option<bool>) -> Option<bool> { + | ^^^^^^^^^^^^ + = help: consider using the `?` operator -error: used unwrap or expect in a function that returns result or option - --> tests/ui/unwrap_in_result.rs:44:5 +error: `unwrap` used in a function that returns an `Option` + --> tests/ui/unwrap_in_result.rs:71:22 | -LL | / fn in_closure(a: Option<bool>) -> Option<bool> { -LL | | -LL | | let c = || a.unwrap(); -LL | | Some(c()) -LL | | } - | |_____^ +LL | Some(b.unwrap()) + | ^^^^^^^^^^ | - = help: unwrap and expect should not be used in a function that returns result or option -note: potential non-recoverable error(s) - --> tests/ui/unwrap_in_result.rs:46:20 +note: in this function signature + --> tests/ui/unwrap_in_result.rs:70:48 + | +LL | const fn inner(b: Option<bool>) -> Option<bool> { + | ^^^^^^^^^^^^ + = help: consider using the `?` operator + +error: `unwrap` used in a function that returns an `Option` + --> tests/ui/unwrap_in_result.rs:89:9 + | +LL | _ = Option::unwrap(Some(3)); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +note: in this function signature + --> tests/ui/unwrap_in_result.rs:88:30 + | +LL | fn type_relative_unwrap() -> Option<()> { + | ^^^^^^^^^^ + = help: consider using the `?` operator + +error: `unwrap` used in a function that returns a `Result` + --> tests/ui/unwrap_in_result.rs:99:5 + | +LL | A::bad_divisible_by_3("3".to_string()).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: in this function signature + --> tests/ui/unwrap_in_result.rs:98:14 + | +LL | fn main() -> Result<(), ()> { + | ^^^^^^^^^^^^^^ + = help: consider using the `?` operator or calling the `.map_err()` method + +error: `unwrap` used in a function that returns a `Result` + --> tests/ui/unwrap_in_result.rs:101:5 + | +LL | A::good_divisible_by_3("3".to_string()).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `unwrap` used in a function that returns a `Result` + --> tests/ui/unwrap_in_result.rs:103:5 | -LL | let c = || a.unwrap(); - | ^^^^^^^^^^ +LL | Result::unwrap(A::good_divisible_by_3("3".to_string())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 3 previous errors +error: aborting due to 9 previous errors diff --git a/src/tools/clippy/tests/ui/vec.fixed b/src/tools/clippy/tests/ui/vec.fixed index f360a8afadf..55742459c92 100644 --- a/src/tools/clippy/tests/ui/vec.fixed +++ b/src/tools/clippy/tests/ui/vec.fixed @@ -242,3 +242,12 @@ fn issue_12101() { for a in &[1, 2] {} //~^ useless_vec } + +fn issue_14531() { + // The lint used to suggest using an array rather than a reference to a slice. + + fn requires_ref_slice(v: &[()]) {} + let v = &[]; + //~^ useless_vec + requires_ref_slice(v); +} diff --git a/src/tools/clippy/tests/ui/vec.rs b/src/tools/clippy/tests/ui/vec.rs index a779d33557c..fbf7131323c 100644 --- a/src/tools/clippy/tests/ui/vec.rs +++ b/src/tools/clippy/tests/ui/vec.rs @@ -242,3 +242,12 @@ fn issue_12101() { for a in &(vec![1, 2]) {} //~^ useless_vec } + +fn issue_14531() { + // The lint used to suggest using an array rather than a reference to a slice. + + fn requires_ref_slice(v: &[()]) {} + let v = &vec![]; + //~^ useless_vec + requires_ref_slice(v); +} diff --git a/src/tools/clippy/tests/ui/vec.stderr b/src/tools/clippy/tests/ui/vec.stderr index 806d6617200..d16c8a8944a 100644 --- a/src/tools/clippy/tests/ui/vec.stderr +++ b/src/tools/clippy/tests/ui/vec.stderr @@ -127,5 +127,11 @@ error: useless use of `vec!` LL | for a in &(vec![1, 2]) {} | ^^^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]` -error: aborting due to 21 previous errors +error: useless use of `vec!` + --> tests/ui/vec.rs:250:13 + | +LL | let v = &vec![]; + | ^^^^^^^ help: you can use a slice directly: `&[]` + +error: aborting due to 22 previous errors diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml index 805baf2af6d..7b19f8658c0 100644 --- a/src/tools/clippy/triagebot.toml +++ b/src/tools/clippy/triagebot.toml @@ -49,11 +49,18 @@ new_pr = true # These labels are set when there are unresolved concerns, removed otherwise labels = ["S-waiting-on-concerns"] +# Show differences when a PR is rebased +[range-diff] + +# Amend a review to include a link to what was changed since the review +[review-changes-since] + [assign] contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md" users_on_vacation = [ "matthiaskrgr", "Manishearth", + "flip1995", ] [assign.owners] diff --git a/src/tools/collect-license-metadata/Cargo.toml b/src/tools/collect-license-metadata/Cargo.toml index 7f2e57ced05..edf9e5c5393 100644 --- a/src/tools/collect-license-metadata/Cargo.toml +++ b/src/tools/collect-license-metadata/Cargo.toml @@ -8,5 +8,5 @@ license = "MIT OR Apache-2.0" [dependencies] anyhow = "1.0.65" serde = { version = "1.0.147", features = ["derive"] } -serde_json.workspace = true +serde_json = "1.0.85" spdx-rs = "0.5.1" diff --git a/src/tools/compiletest/Cargo.toml b/src/tools/compiletest/Cargo.toml index fb71275b03c..cdada5a2230 100644 --- a/src/tools/compiletest/Cargo.toml +++ b/src/tools/compiletest/Cargo.toml @@ -20,22 +20,22 @@ diff = "0.1.10" getopts = "0.2" glob = "0.3.0" home = "0.5.5" -indexmap.workspace = true +indexmap = "2.0.0" miropt-test-tools = { path = "../miropt-test-tools" } rayon = "1.10.0" regex = "1.0" rustfix = "0.8.1" semver = { version = "1.0.23", features = ["serde"] } serde = { version = "1.0", features = ["derive"] } -serde_json.workspace = true +serde_json = "1.0" +tracing = "0.1" tracing-subscriber = { version = "0.3.3", default-features = false, features = ["ansi", "env-filter", "fmt", "parking_lot", "smallvec"] } -tracing.workspace = true unified-diff = "0.2.1" walkdir = "2" # tidy-alphabetical-end [target.'cfg(unix)'.dependencies] -libc.workspace = true +libc = "0.2" [target.'cfg(windows)'.dependencies] miow = "0.6" diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 89b1b4f84b6..143ccdcb9e5 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -67,6 +67,7 @@ string_enum! { MirOpt => "mir-opt", Pretty => "pretty", RunMake => "run-make", + RunMakeCargo => "run-make-cargo", Rustdoc => "rustdoc", RustdocGui => "rustdoc-gui", RustdocJs => "rustdoc-js", @@ -269,9 +270,6 @@ pub struct Config { /// between e.g. beta `cargo` vs in-tree `cargo`. /// /// FIXME: maybe rename this to reflect that this is a *staged* host cargo. - /// - /// FIXME(#134109): split `run-make` into two test suites, a test suite *with* staged cargo, and - /// another test suite *without*. pub cargo_path: Option<Utf8PathBuf>, /// Path to the stage 0 `rustc` used to build `run-make` recipes. This must not be confused with @@ -667,6 +665,10 @@ pub struct Config { /// to avoid `!nocapture` double-negatives. pub nocapture: bool, + /// True if the experimental new output-capture implementation should be + /// used, avoiding the need for `#![feature(internal_output_capture)]`. + pub new_output_capture: bool, + /// Needed both to construct [`build_helper::git::GitConfig`]. pub nightly_branch: String, pub git_merge_commit_email: String, @@ -784,6 +786,7 @@ impl Config { builtin_cfg_names: Default::default(), supported_crate_types: Default::default(), nocapture: Default::default(), + new_output_capture: Default::default(), nightly_branch: Default::default(), git_merge_commit_email: Default::default(), profiler_runtime: Default::default(), diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index f2ad049d526..857953072c4 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -1012,7 +1012,7 @@ impl Config { if let Some(raw) = self.parse_name_value_directive(line, "revisions", testfile, line_number) { if self.mode == TestMode::RunMake { - panic!("`run-make` tests do not support revisions: {}", testfile); + panic!("`run-make` mode tests do not support revisions: {}", testfile); } let mut duplicates: HashSet<_> = existing.iter().cloned().collect(); diff --git a/src/tools/compiletest/src/executor.rs b/src/tools/compiletest/src/executor.rs index 5519ef1af1f..b0dc24798b6 100644 --- a/src/tools/compiletest/src/executor.rs +++ b/src/tools/compiletest/src/executor.rs @@ -13,6 +13,7 @@ use std::sync::{Arc, Mutex, mpsc}; use std::{env, hint, io, mem, panic, thread}; use crate::common::{Config, TestPaths}; +use crate::output_capture::{self, ConsoleOut}; use crate::panic_hook; mod deadline; @@ -120,28 +121,28 @@ fn run_test_inner( runnable_test: RunnableTest, completion_sender: mpsc::Sender<TestCompletion>, ) { - let is_capture = !runnable_test.config.nocapture; + let capture = CaptureKind::for_config(&runnable_test.config); // Install a panic-capture buffer for use by the custom panic hook. - if is_capture { + if capture.should_set_panic_hook() { panic_hook::set_capture_buf(Default::default()); } - let capture_buf = is_capture.then(|| Arc::new(Mutex::new(vec![]))); - if let Some(capture_buf) = &capture_buf { - io::set_output_capture(Some(Arc::clone(capture_buf))); + if let CaptureKind::Old { ref buf } = capture { + io::set_output_capture(Some(Arc::clone(buf))); } - let panic_payload = panic::catch_unwind(move || runnable_test.run()).err(); + let stdout = capture.stdout(); + let stderr = capture.stderr(); + + let panic_payload = panic::catch_unwind(move || runnable_test.run(stdout, stderr)).err(); if let Some(panic_buf) = panic_hook::take_capture_buf() { let panic_buf = panic_buf.lock().unwrap_or_else(|e| e.into_inner()); - // For now, forward any captured panic message to (captured) stderr. - // FIXME(Zalathar): Once we have our own output-capture buffer for - // non-panic output, append the panic message to that buffer instead. - eprint!("{panic_buf}"); + // Forward any captured panic message to (captured) stderr. + write!(stderr, "{panic_buf}"); } - if is_capture { + if matches!(capture, CaptureKind::Old { .. }) { io::set_output_capture(None); } @@ -152,11 +153,70 @@ fn run_test_inner( TestOutcome::Failed { message: Some("test did not panic as expected") } } }; - let stdout = capture_buf.map(|mutex| mutex.lock().unwrap_or_else(|e| e.into_inner()).to_vec()); + let stdout = capture.into_inner(); completion_sender.send(TestCompletion { id, outcome, stdout }).unwrap(); } +enum CaptureKind { + /// Do not capture test-runner output, for `--no-capture`. + /// + /// (This does not affect `rustc` and other subprocesses spawned by test + /// runners, whose output is always captured.) + None, + + /// Use the old output-capture implementation, which relies on the unstable + /// library feature `#![feature(internal_output_capture)]`. + Old { buf: Arc<Mutex<Vec<u8>>> }, + + /// Use the new output-capture implementation, which only uses stable Rust. + New { buf: output_capture::CaptureBuf }, +} + +impl CaptureKind { + fn for_config(config: &Config) -> Self { + if config.nocapture { + Self::None + } else if config.new_output_capture { + Self::New { buf: output_capture::CaptureBuf::new() } + } else { + // Create a capure buffer for `io::set_output_capture`. + Self::Old { buf: Default::default() } + } + } + + fn should_set_panic_hook(&self) -> bool { + match self { + Self::None => false, + Self::Old { .. } => true, + Self::New { .. } => true, + } + } + + fn stdout(&self) -> &dyn ConsoleOut { + self.capture_buf_or(&output_capture::Stdout) + } + + fn stderr(&self) -> &dyn ConsoleOut { + self.capture_buf_or(&output_capture::Stderr) + } + + fn capture_buf_or<'a>(&'a self, fallback: &'a dyn ConsoleOut) -> &'a dyn ConsoleOut { + match self { + Self::None | Self::Old { .. } => fallback, + Self::New { buf } => buf, + } + } + + fn into_inner(self) -> Option<Vec<u8>> { + match self { + Self::None => None, + Self::Old { buf } => Some(buf.lock().unwrap_or_else(|e| e.into_inner()).to_vec()), + Self::New { buf } => Some(buf.into_inner().into()), + } + } +} + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] struct TestId(usize); @@ -174,10 +234,12 @@ impl RunnableTest { Self { config, testpaths, revision } } - fn run(&self) { + fn run(&self, stdout: &dyn ConsoleOut, stderr: &dyn ConsoleOut) { __rust_begin_short_backtrace(|| { crate::runtest::run( Arc::clone(&self.config), + stdout, + stderr, &self.testpaths, self.revision.as_deref(), ); diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index fa84691a46f..f647f96a9bf 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -15,6 +15,7 @@ pub mod directives; pub mod errors; mod executor; mod json; +mod output_capture; mod panic_hook; mod raise_fd_limit; mod read2; @@ -177,6 +178,12 @@ pub fn parse_config(args: Vec<String>) -> Config { // FIXME: Temporarily retained so we can point users to `--no-capture` .optflag("", "nocapture", "") .optflag("", "no-capture", "don't capture stdout/stderr of tests") + .optopt( + "N", + "new-output-capture", + "enables or disables the new output-capture implementation", + "off|on", + ) .optflag("", "profiler-runtime", "is the profiler runtime enabled for this target") .optflag("h", "help", "show this message") .reqopt("", "channel", "current Rust channel", "CHANNEL") @@ -461,6 +468,14 @@ pub fn parse_config(args: Vec<String>) -> Config { supported_crate_types: OnceLock::new(), nocapture: matches.opt_present("no-capture"), + new_output_capture: { + let value = matches + .opt_str("new-output-capture") + .or_else(|| env::var("COMPILETEST_NEW_OUTPUT_CAPTURE").ok()) + .unwrap_or_else(|| "off".to_owned()); + parse_bool_option(&value) + .unwrap_or_else(|| panic!("unknown `--new-output-capture` value `{value}` given")) + }, nightly_branch: matches.opt_str("nightly-branch").unwrap(), git_merge_commit_email: matches.opt_str("git-merge-commit-email").unwrap(), @@ -476,6 +491,19 @@ pub fn parse_config(args: Vec<String>) -> Config { } } +/// Parses the same set of boolean values accepted by rustc command-line arguments. +/// +/// Accepting all of these values is more complicated than just picking one +/// pair, but has the advantage that contributors who are used to rustc +/// shouldn't have to think about which values are legal. +fn parse_bool_option(value: &str) -> Option<bool> { + match value { + "off" | "no" | "n" | "false" => Some(false), + "on" | "yes" | "y" | "true" => Some(true), + _ => None, + } +} + pub fn opt_str(maybestr: &Option<String>) -> &str { match *maybestr { None => "(none)", diff --git a/src/tools/compiletest/src/output_capture.rs b/src/tools/compiletest/src/output_capture.rs new file mode 100644 index 00000000000..de1aea11ade --- /dev/null +++ b/src/tools/compiletest/src/output_capture.rs @@ -0,0 +1,52 @@ +use std::fmt; +use std::panic::RefUnwindSafe; +use std::sync::Mutex; + +pub trait ConsoleOut: fmt::Debug + RefUnwindSafe { + fn write_fmt(&self, args: fmt::Arguments<'_>); +} + +#[derive(Debug)] +pub(crate) struct Stdout; + +impl ConsoleOut for Stdout { + fn write_fmt(&self, args: fmt::Arguments<'_>) { + print!("{args}"); + } +} + +#[derive(Debug)] +pub(crate) struct Stderr; + +impl ConsoleOut for Stderr { + fn write_fmt(&self, args: fmt::Arguments<'_>) { + eprint!("{args}"); + } +} + +pub(crate) struct CaptureBuf { + inner: Mutex<String>, +} + +impl CaptureBuf { + pub(crate) fn new() -> Self { + Self { inner: Mutex::new(String::new()) } + } + + pub(crate) fn into_inner(self) -> String { + self.inner.into_inner().unwrap_or_else(|e| e.into_inner()) + } +} + +impl fmt::Debug for CaptureBuf { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("CaptureBuf").finish_non_exhaustive() + } +} + +impl ConsoleOut for CaptureBuf { + fn write_fmt(&self, args: fmt::Arguments<'_>) { + let mut s = self.inner.lock().unwrap_or_else(|e| e.into_inner()); + <String as fmt::Write>::write_fmt(&mut s, args).unwrap(); + } +} diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 867624cc8fa..89fb8eb4357 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -23,6 +23,7 @@ use crate::common::{ }; use crate::directives::TestProps; use crate::errors::{Error, ErrorKind, load_errors}; +use crate::output_capture::ConsoleOut; use crate::read2::{Truncated, read2_abbreviated}; use crate::runtest::compute_diff::{DiffLine, make_diff, write_diff, write_filtered_diff}; use crate::util::{Utf8PathBufExt, add_dylib_path, static_regex}; @@ -108,7 +109,13 @@ fn dylib_name(name: &str) -> String { format!("{}{name}.{}", std::env::consts::DLL_PREFIX, std::env::consts::DLL_EXTENSION) } -pub fn run(config: Arc<Config>, testpaths: &TestPaths, revision: Option<&str>) { +pub fn run( + config: Arc<Config>, + stdout: &dyn ConsoleOut, + stderr: &dyn ConsoleOut, + testpaths: &TestPaths, + revision: Option<&str>, +) { match &*config.target { "arm-linux-androideabi" | "armv7-linux-androideabi" @@ -131,7 +138,7 @@ pub fn run(config: Arc<Config>, testpaths: &TestPaths, revision: Option<&str>) { if config.verbose { // We're going to be dumping a lot of info. Start on a new line. - print!("\n\n"); + write!(stdout, "\n\n"); } debug!("running {}", testpaths.file); let mut props = TestProps::from_file(&testpaths.file, revision, &config); @@ -143,7 +150,7 @@ pub fn run(config: Arc<Config>, testpaths: &TestPaths, revision: Option<&str>) { props.incremental_dir = Some(incremental_dir(&config, testpaths, revision)); } - let cx = TestCx { config: &config, props: &props, testpaths, revision }; + let cx = TestCx { config: &config, stdout, stderr, props: &props, testpaths, revision }; if let Err(e) = create_dir_all(&cx.output_base_dir()) { panic!("failed to create output base directory {}: {e}", cx.output_base_dir()); @@ -162,6 +169,8 @@ pub fn run(config: Arc<Config>, testpaths: &TestPaths, revision: Option<&str>) { revision_props.incremental_dir = props.incremental_dir.clone(); let rev_cx = TestCx { config: &config, + stdout, + stderr, props: &revision_props, testpaths, revision: Some(revision), @@ -212,6 +221,8 @@ pub fn compute_stamp_hash(config: &Config) -> String { #[derive(Copy, Clone, Debug)] struct TestCx<'test> { config: &'test Config, + stdout: &'test dyn ConsoleOut, + stderr: &'test dyn ConsoleOut, props: &'test TestProps, testpaths: &'test TestPaths, revision: Option<&'test str>, @@ -603,7 +614,8 @@ impl<'test> TestCx<'test> { ); } else { for pattern in missing_patterns { - println!( + writeln!( + self.stdout, "\n{prefix}: error pattern '{pattern}' not found!", prefix = self.error_prefix() ); @@ -783,7 +795,8 @@ impl<'test> TestCx<'test> { }; format!("{file_name}:{line_num}{opt_col_num}") }; - let print_error = |e| println!("{}: {}: {}", line_str(e), e.kind, e.msg.cyan()); + let print_error = + |e| writeln!(self.stdout, "{}: {}: {}", line_str(e), e.kind, e.msg.cyan()); let push_suggestion = |suggestions: &mut Vec<_>, e: &Error, kind, line, msg, color, rank| { let mut ret = String::new(); @@ -811,7 +824,7 @@ impl<'test> TestCx<'test> { if let Some(&(_, top_rank)) = suggestions.first() { for (suggestion, rank) in suggestions { if rank == top_rank { - println!(" {} {suggestion}", prefix.color(color)); + writeln!(self.stdout, " {} {suggestion}", prefix.color(color)); } } } @@ -824,7 +837,8 @@ impl<'test> TestCx<'test> { // - only known line - meh, but suggested // - others are not worth suggesting if !unexpected.is_empty() { - println!( + writeln!( + self.stdout, "\n{prefix}: {n} diagnostics reported in JSON output but not expected in test file", prefix = self.error_prefix(), n = unexpected.len(), @@ -858,7 +872,8 @@ impl<'test> TestCx<'test> { } } if !not_found.is_empty() { - println!( + writeln!( + self.stdout, "\n{prefix}: {n} diagnostics expected in test file but not reported in JSON output", prefix = self.error_prefix(), n = not_found.len(), @@ -978,6 +993,8 @@ impl<'test> TestCx<'test> { self.props.from_aux_file(&aux_testpaths.file, self.revision, self.config); let aux_cx = TestCx { config: self.config, + stdout: self.stdout, + stderr: self.stderr, props: &props_for_aux, testpaths: &aux_testpaths, revision: self.revision, @@ -1343,6 +1360,8 @@ impl<'test> TestCx<'test> { let aux_output = TargetLocation::ThisDirectory(aux_dir.clone()); let aux_cx = TestCx { config: self.config, + stdout: self.stdout, + stderr: self.stderr, props: &aux_props, testpaths: &aux_testpaths, revision: self.revision, @@ -1948,11 +1967,11 @@ impl<'test> TestCx<'test> { } else { path.file_name().unwrap().into() }; - println!("------{proc_name} stdout------------------------------"); - println!("{}", out); - println!("------{proc_name} stderr------------------------------"); - println!("{}", err); - println!("------------------------------------------"); + writeln!(self.stdout, "------{proc_name} stdout------------------------------"); + writeln!(self.stdout, "{}", out); + writeln!(self.stdout, "------{proc_name} stderr------------------------------"); + writeln!(self.stdout, "{}", err); + writeln!(self.stdout, "------------------------------------------"); } fn dump_output_file(&self, out: &str, extension: &str) { @@ -2014,7 +2033,7 @@ impl<'test> TestCx<'test> { debug!("{message}"); if self.config.verbose { // Note: `./x test ... --verbose --no-capture` is needed to see this print. - println!("{message}"); + writeln!(self.stdout, "{message}"); } } @@ -2030,7 +2049,7 @@ impl<'test> TestCx<'test> { #[track_caller] fn fatal(&self, err: &str) -> ! { - println!("\n{prefix}: {err}", prefix = self.error_prefix()); + writeln!(self.stdout, "\n{prefix}: {err}", prefix = self.error_prefix()); error!("fatal error, panic: {:?}", err); panic!("fatal error"); } @@ -2048,15 +2067,15 @@ impl<'test> TestCx<'test> { proc_res: &ProcRes, callback_before_unwind: impl FnOnce(), ) -> ! { - println!("\n{prefix}: {err}", prefix = self.error_prefix()); + writeln!(self.stdout, "\n{prefix}: {err}", prefix = self.error_prefix()); // Some callers want to print additional notes after the main error message. if let Some(note) = extra_note { - println!("{note}"); + writeln!(self.stdout, "{note}"); } // Print the details and output of the subprocess that caused this test to fail. - println!("{}", proc_res.format_info()); + writeln!(self.stdout, "{}", proc_res.format_info()); // Some callers want print more context or show a custom diff before the unwind occurs. callback_before_unwind(); @@ -2126,7 +2145,7 @@ impl<'test> TestCx<'test> { if !self.config.has_html_tidy { return; } - println!("info: generating a diff against nightly rustdoc"); + writeln!(self.stdout, "info: generating a diff against nightly rustdoc"); let suffix = self.safe_revision().map_or("nightly".into(), |path| path.to_owned() + "-nightly"); @@ -2162,7 +2181,7 @@ impl<'test> TestCx<'test> { let proc_res = new_rustdoc.document(&compare_dir, &new_rustdoc.testpaths); if !proc_res.status.success() { - eprintln!("failed to run nightly rustdoc"); + writeln!(self.stderr, "failed to run nightly rustdoc"); return; } @@ -2207,6 +2226,7 @@ impl<'test> TestCx<'test> { let diff_filename = format!("build/tmp/rustdoc-compare-{}.diff", std::process::id()); if !write_filtered_diff( + self, &diff_filename, out_dir, &compare_dir, @@ -2227,7 +2247,7 @@ impl<'test> TestCx<'test> { if let Some(pager) = pager { let pager = pager.trim(); if self.config.verbose { - eprintln!("using pager {}", pager); + writeln!(self.stderr, "using pager {}", pager); } let output = Command::new(pager) // disable paging; we want this to be non-interactive @@ -2238,8 +2258,8 @@ impl<'test> TestCx<'test> { .output() .unwrap(); assert!(output.status.success()); - println!("{}", String::from_utf8_lossy(&output.stdout)); - eprintln!("{}", String::from_utf8_lossy(&output.stderr)); + writeln!(self.stdout, "{}", String::from_utf8_lossy(&output.stdout)); + writeln!(self.stderr, "{}", String::from_utf8_lossy(&output.stderr)); } else { warning!("no pager configured, falling back to unified diff"); help!( @@ -2254,7 +2274,7 @@ impl<'test> TestCx<'test> { match diff.read_until(b'\n', &mut line) { Ok(0) => break, Ok(_) => {} - Err(e) => eprintln!("ERROR: {:?}", e), + Err(e) => writeln!(self.stderr, "ERROR: {:?}", e), } match String::from_utf8(line.clone()) { Ok(line) => { @@ -2802,11 +2822,11 @@ impl<'test> TestCx<'test> { if let Err(err) = fs::write(&actual_path, &actual) { self.fatal(&format!("failed to write {stream} to `{actual_path}`: {err}",)); } - println!("Saved the actual {stream} to `{actual_path}`"); + writeln!(self.stdout, "Saved the actual {stream} to `{actual_path}`"); if !self.config.bless { if expected.is_empty() { - println!("normalized {}:\n{}\n", stream, actual); + writeln!(self.stdout, "normalized {}:\n{}\n", stream, actual); } else { self.show_diff( stream, @@ -2830,14 +2850,15 @@ impl<'test> TestCx<'test> { if let Err(err) = fs::write(&expected_path, &actual) { self.fatal(&format!("failed to write {stream} to `{expected_path}`: {err}")); } - println!( + writeln!( + self.stdout, "Blessing the {stream} of `{test_name}` as `{expected_path}`", test_name = self.testpaths.file ); } } - println!("\nThe actual {stream} differed from the expected {stream}"); + writeln!(self.stdout, "\nThe actual {stream} differed from the expected {stream}"); if self.config.bless { CompareOutcome::Blessed } else { CompareOutcome::Differed } } @@ -2852,7 +2873,7 @@ impl<'test> TestCx<'test> { actual: &str, actual_unnormalized: &str, ) { - eprintln!("diff of {stream}:\n"); + writeln!(self.stderr, "diff of {stream}:\n"); if let Some(diff_command) = self.config.diff_command.as_deref() { let mut args = diff_command.split_whitespace(); let name = args.next().unwrap(); @@ -2864,11 +2885,11 @@ impl<'test> TestCx<'test> { } Ok(output) => { let output = String::from_utf8_lossy(&output.stdout); - eprint!("{output}"); + write!(self.stderr, "{output}"); } } } else { - eprint!("{}", write_diff(expected, actual, 3)); + write!(self.stderr, "{}", write_diff(expected, actual, 3)); } // NOTE: argument order is important, we need `actual` to be on the left so the line number match up when we compare it to `actual_unnormalized` below. @@ -2906,9 +2927,16 @@ impl<'test> TestCx<'test> { && !mismatches_unnormalized.is_empty() && !mismatches_normalized.is_empty() { - eprintln!("Note: some mismatched output was normalized before being compared"); + writeln!( + self.stderr, + "Note: some mismatched output was normalized before being compared" + ); // FIXME: respect diff_command - eprint!("{}", write_diff(&mismatches_unnormalized, &mismatches_normalized, 0)); + write!( + self.stderr, + "{}", + write_diff(&mismatches_unnormalized, &mismatches_normalized, 0) + ); } } @@ -2986,7 +3014,7 @@ impl<'test> TestCx<'test> { fs::create_dir_all(&incremental_dir).unwrap(); if self.config.verbose { - println!("init_incremental_test: incremental_dir={incremental_dir}"); + writeln!(self.stdout, "init_incremental_test: incremental_dir={incremental_dir}"); } } } diff --git a/src/tools/compiletest/src/runtest/codegen_units.rs b/src/tools/compiletest/src/runtest/codegen_units.rs index 44ddcb1d288..16c251c3c9e 100644 --- a/src/tools/compiletest/src/runtest/codegen_units.rs +++ b/src/tools/compiletest/src/runtest/codegen_units.rs @@ -62,13 +62,13 @@ impl TestCx<'_> { if !missing.is_empty() { missing.sort(); - println!("\nThese items should have been contained but were not:\n"); + writeln!(self.stdout, "\nThese items should have been contained but were not:\n"); for item in &missing { - println!("{}", item); + writeln!(self.stdout, "{}", item); } - println!("\n"); + writeln!(self.stdout, "\n"); } if !unexpected.is_empty() { @@ -78,24 +78,32 @@ impl TestCx<'_> { sorted }; - println!("\nThese items were contained but should not have been:\n"); + writeln!(self.stdout, "\nThese items were contained but should not have been:\n"); for item in sorted { - println!("{}", item); + writeln!(self.stdout, "{}", item); } - println!("\n"); + writeln!(self.stdout, "\n"); } if !wrong_cgus.is_empty() { wrong_cgus.sort_by_key(|pair| pair.0.name.clone()); - println!("\nThe following items were assigned to wrong codegen units:\n"); + writeln!(self.stdout, "\nThe following items were assigned to wrong codegen units:\n"); for &(ref expected_item, ref actual_item) in &wrong_cgus { - println!("{}", expected_item.name); - println!(" expected: {}", codegen_units_to_str(&expected_item.codegen_units)); - println!(" actual: {}", codegen_units_to_str(&actual_item.codegen_units)); - println!(); + writeln!(self.stdout, "{}", expected_item.name); + writeln!( + self.stdout, + " expected: {}", + codegen_units_to_str(&expected_item.codegen_units) + ); + writeln!( + self.stdout, + " actual: {}", + codegen_units_to_str(&actual_item.codegen_units) + ); + writeln!(self.stdout); } } diff --git a/src/tools/compiletest/src/runtest/compute_diff.rs b/src/tools/compiletest/src/runtest/compute_diff.rs index 509e7e11703..3363127b3ea 100644 --- a/src/tools/compiletest/src/runtest/compute_diff.rs +++ b/src/tools/compiletest/src/runtest/compute_diff.rs @@ -3,6 +3,8 @@ use std::fs::{File, FileType}; use camino::Utf8Path; +use crate::runtest::TestCx; + #[derive(Debug, PartialEq)] pub enum DiffLine { Context(String), @@ -112,6 +114,7 @@ pub(crate) fn write_diff(expected: &str, actual: &str, context_size: usize) -> S /// /// Returns whether any data was actually written. pub(crate) fn write_filtered_diff<Filter>( + cx: &TestCx<'_>, diff_filename: &str, out_dir: &Utf8Path, compare_dir: &Utf8Path, @@ -147,11 +150,11 @@ where } if !wrote_data { - println!("note: diff is identical to nightly rustdoc"); + writeln!(cx.stdout, "note: diff is identical to nightly rustdoc"); assert!(diff_output.metadata().unwrap().len() == 0); return false; } else if verbose { - eprintln!("printing diff:"); + writeln!(cx.stderr, "printing diff:"); let mut buf = Vec::new(); diff_output.read_to_end(&mut buf).unwrap(); std::io::stderr().lock().write_all(&mut buf).unwrap(); diff --git a/src/tools/compiletest/src/runtest/crashes.rs b/src/tools/compiletest/src/runtest/crashes.rs index da1e74b4a56..0aae7eaa39c 100644 --- a/src/tools/compiletest/src/runtest/crashes.rs +++ b/src/tools/compiletest/src/runtest/crashes.rs @@ -6,10 +6,10 @@ impl TestCx<'_> { let proc_res = self.compile_test(WillExecute::No, self.should_emit_metadata(pm)); if std::env::var("COMPILETEST_VERBOSE_CRASHES").is_ok() { - eprintln!("{}", proc_res.status); - eprintln!("{}", proc_res.stdout); - eprintln!("{}", proc_res.stderr); - eprintln!("{}", proc_res.cmdline); + writeln!(self.stderr, "{}", proc_res.status); + writeln!(self.stderr, "{}", proc_res.stdout); + writeln!(self.stderr, "{}", proc_res.stderr); + writeln!(self.stderr, "{}", proc_res.cmdline); } // if a test does not crash, consider it an error diff --git a/src/tools/compiletest/src/runtest/debuginfo.rs b/src/tools/compiletest/src/runtest/debuginfo.rs index 88d022b8bba..071c0863b7e 100644 --- a/src/tools/compiletest/src/runtest/debuginfo.rs +++ b/src/tools/compiletest/src/runtest/debuginfo.rs @@ -245,7 +245,7 @@ impl TestCx<'_> { cmdline, }; if adb.kill().is_err() { - println!("Adb process is already finished."); + writeln!(self.stdout, "Adb process is already finished."); } } else { let rust_pp_module_abs_path = self.config.src_root.join("src").join("etc"); @@ -256,7 +256,11 @@ impl TestCx<'_> { match self.config.gdb_version { Some(version) => { - println!("NOTE: compiletest thinks it is using GDB version {}", version); + writeln!( + self.stdout, + "NOTE: compiletest thinks it is using GDB version {}", + version + ); if !self.props.disable_gdb_pretty_printers && version > extract_gdb_version("7.4").unwrap() @@ -278,7 +282,8 @@ impl TestCx<'_> { } } _ => { - println!( + writeln!( + self.stdout, "NOTE: compiletest does not know which version of \ GDB it is using" ); @@ -376,10 +381,15 @@ impl TestCx<'_> { match self.config.lldb_version { Some(ref version) => { - println!("NOTE: compiletest thinks it is using LLDB version {}", version); + writeln!( + self.stdout, + "NOTE: compiletest thinks it is using LLDB version {}", + version + ); } _ => { - println!( + writeln!( + self.stdout, "NOTE: compiletest does not know which version of \ LLDB it is using" ); diff --git a/src/tools/compiletest/src/runtest/incremental.rs b/src/tools/compiletest/src/runtest/incremental.rs index 90cff6bab4d..44eb80300c3 100644 --- a/src/tools/compiletest/src/runtest/incremental.rs +++ b/src/tools/compiletest/src/runtest/incremental.rs @@ -30,7 +30,7 @@ impl TestCx<'_> { assert!(incremental_dir.exists(), "init_incremental_test failed to create incremental dir"); if self.config.verbose { - print!("revision={:?} props={:#?}", revision, self.props); + write!(self.stdout, "revision={:?} props={:#?}", revision, self.props); } if revision.starts_with("cpass") { diff --git a/src/tools/compiletest/src/runtest/mir_opt.rs b/src/tools/compiletest/src/runtest/mir_opt.rs index 55043bf4bc2..94487926383 100644 --- a/src/tools/compiletest/src/runtest/mir_opt.rs +++ b/src/tools/compiletest/src/runtest/mir_opt.rs @@ -80,7 +80,7 @@ impl TestCx<'_> { } let expected_string = fs::read_to_string(&expected_file).unwrap(); if dumped_string != expected_string { - print!("{}", write_diff(&expected_string, &dumped_string, 3)); + write!(self.stdout, "{}", write_diff(&expected_string, &dumped_string, 3)); panic!( "Actual MIR output differs from expected MIR output {}", expected_file.display() diff --git a/src/tools/compiletest/src/runtest/run_make.rs b/src/tools/compiletest/src/runtest/run_make.rs index 8a0e45cf8ca..738f504d5c1 100644 --- a/src/tools/compiletest/src/runtest/run_make.rs +++ b/src/tools/compiletest/src/runtest/run_make.rs @@ -5,11 +5,12 @@ use build_helper::fs::{ignore_not_found, recursive_remove}; use camino::{Utf8Path, Utf8PathBuf}; use super::{ProcRes, TestCx, disable_error_reporting}; +use crate::common::TestSuite; use crate::util::{copy_dir_all, dylib_env_var}; impl TestCx<'_> { pub(super) fn run_rmake_test(&self) { - // For `run-make` V2, we need to perform 2 steps to build and run a `run-make` V2 recipe + // For `run-make`, we need to perform 2 steps to build and run a `run-make` recipe // (`rmake.rs`) to run the actual tests. The support library is already built as a tool rust // library and is available under // `build/$HOST/bootstrap-tools/$TARGET/release/librun_make_support.rlib`. @@ -189,8 +190,12 @@ impl TestCx<'_> { // through a specific CI runner). .env("LLVM_COMPONENTS", &self.config.llvm_components); - if let Some(ref cargo) = self.config.cargo_path { - cmd.env("CARGO", cargo); + // Only `run-make-cargo` test suite gets an in-tree `cargo`, not `run-make`. + if self.config.suite == TestSuite::RunMakeCargo { + cmd.env( + "CARGO", + self.config.cargo_path.as_ref().expect("cargo must be built and made available"), + ); } if let Some(ref rustdoc) = self.config.rustdoc_path { diff --git a/src/tools/compiletest/src/runtest/rustdoc_json.rs b/src/tools/compiletest/src/runtest/rustdoc_json.rs index 083398f9274..b8da6e2ac52 100644 --- a/src/tools/compiletest/src/runtest/rustdoc_json.rs +++ b/src/tools/compiletest/src/runtest/rustdoc_json.rs @@ -30,8 +30,8 @@ impl TestCx<'_> { if !res.status.success() { self.fatal_proc_rec_general("jsondocck failed!", None, &res, || { - println!("Rustdoc Output:"); - println!("{}", proc_res.format_info()); + writeln!(self.stdout, "Rustdoc Output:"); + writeln!(self.stdout, "{}", proc_res.format_info()); }) } diff --git a/src/tools/compiletest/src/runtest/ui.rs b/src/tools/compiletest/src/runtest/ui.rs index 40b0ee0a399..d683a325c86 100644 --- a/src/tools/compiletest/src/runtest/ui.rs +++ b/src/tools/compiletest/src/runtest/ui.rs @@ -115,10 +115,14 @@ impl TestCx<'_> { } if errors > 0 { - println!("To update references, rerun the tests and pass the `--bless` flag"); + writeln!( + self.stdout, + "To update references, rerun the tests and pass the `--bless` flag" + ); let relative_path_to_file = self.testpaths.relative_dir.join(self.testpaths.file.file_name().unwrap()); - println!( + writeln!( + self.stdout, "To only update this specific test, also pass `--test-args {}`", relative_path_to_file, ); diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs index 1f16a672a98..558e9a58697 100644 --- a/src/tools/compiletest/src/util.rs +++ b/src/tools/compiletest/src/util.rs @@ -45,7 +45,7 @@ impl Utf8PathBufExt for Utf8PathBuf { /// The name of the environment variable that holds dynamic library locations. pub fn dylib_env_var() -> &'static str { - if cfg!(windows) { + if cfg!(any(windows, target_os = "cygwin")) { "PATH" } else if cfg!(target_vendor = "apple") { "DYLD_LIBRARY_PATH" diff --git a/src/tools/coverage-dump/Cargo.toml b/src/tools/coverage-dump/Cargo.toml index e491804c257..36a66f16030 100644 --- a/src/tools/coverage-dump/Cargo.toml +++ b/src/tools/coverage-dump/Cargo.toml @@ -7,9 +7,9 @@ edition = "2021" [dependencies] anyhow = "1.0.71" -itertools.workspace = true +itertools = "0.12" leb128 = "0.2.5" md5 = { package = "md-5" , version = "0.10.5" } miniz_oxide = "0.8.8" regex = "1.8.4" -rustc-demangle.workspace = true +rustc-demangle = "0.1.23" diff --git a/src/tools/features-status-dump/Cargo.toml b/src/tools/features-status-dump/Cargo.toml index d72555da486..b2976f14a01 100644 --- a/src/tools/features-status-dump/Cargo.toml +++ b/src/tools/features-status-dump/Cargo.toml @@ -8,5 +8,5 @@ edition = "2021" anyhow = { version = "1" } clap = { version = "4", features = ["derive"] } serde = { version = "1.0.125", features = [ "derive" ] } -serde_json.workspace = true +serde_json = "1.0.59" tidy = { path = "../tidy", features = ["build-metrics"] } diff --git a/src/tools/generate-copyright/Cargo.toml b/src/tools/generate-copyright/Cargo.toml index 5edf1f3d88b..bcb3165de45 100644 --- a/src/tools/generate-copyright/Cargo.toml +++ b/src/tools/generate-copyright/Cargo.toml @@ -11,5 +11,5 @@ anyhow = "1.0.65" askama = "0.14.0" cargo_metadata = "0.21" serde = { version = "1.0.147", features = ["derive"] } -serde_json.workspace = true +serde_json = "1.0.85" thiserror = "1" diff --git a/src/tools/jsondocck/Cargo.toml b/src/tools/jsondocck/Cargo.toml index 92fde363882..80fc26cbe66 100644 --- a/src/tools/jsondocck/Cargo.toml +++ b/src/tools/jsondocck/Cargo.toml @@ -8,5 +8,5 @@ jsonpath-rust = "1.0.0" getopts = "0.2" regex = "1.4" shlex = "1.0" -serde_json.workspace = true +serde_json = "1.0" fs-err = "2.5.0" diff --git a/src/tools/jsondoclint/Cargo.toml b/src/tools/jsondoclint/Cargo.toml index 44beaf2ddfd..cc8ecefd530 100644 --- a/src/tools/jsondoclint/Cargo.toml +++ b/src/tools/jsondoclint/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" anyhow = "1.0.62" clap = { version = "4.0.15", features = ["derive"] } fs-err = "2.8.1" -rustc-hash.workspace = true +rustc-hash = "2.0.0" rustdoc-json-types = { version = "0.1.0", path = "../../rustdoc-json-types" } serde = { version = "1.0", features = ["derive"] } -serde_json.workspace = true +serde_json = "1.0.85" diff --git a/src/tools/lint-docs/Cargo.toml b/src/tools/lint-docs/Cargo.toml index acafe17cb0c..6e1ab84ed18 100644 --- a/src/tools/lint-docs/Cargo.toml +++ b/src/tools/lint-docs/Cargo.toml @@ -8,6 +8,6 @@ description = "A script to extract the lint documentation for the rustc book." [dependencies] rustc-literal-escaper = "0.0.5" -serde_json.workspace = true -tempfile.workspace = true +serde_json = "1.0.57" +tempfile = "3.1.0" walkdir = "2.3.1" diff --git a/src/tools/llvm-bitcode-linker/Cargo.toml b/src/tools/llvm-bitcode-linker/Cargo.toml index f78f8b618d3..a9210b562f3 100644 --- a/src/tools/llvm-bitcode-linker/Cargo.toml +++ b/src/tools/llvm-bitcode-linker/Cargo.toml @@ -8,7 +8,7 @@ publish = false [dependencies] anyhow = "1.0" -tracing.workspace = true -tracing-subscriber = { version = "0.3.0", features = ["std"] } +tracing = "0.1" +tracing-subscriber = {version = "0.3.0", features = ["std"] } clap = { version = "4.3", features = ["derive"] } thiserror = "1.0.24" diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml index 7d79c384f85..c0fed96d4e6 100644 --- a/src/tools/miri/.github/workflows/ci.yml +++ b/src/tools/miri/.github/workflows/ci.yml @@ -41,6 +41,11 @@ jobs: multiarch: s390x gcc_cross: s390x-linux-gnu qemu: true + - host_target: powerpc64le-unknown-linux-gnu + os: ubuntu-latest + multiarch: ppc64el + gcc_cross: powerpc64le-linux-gnu + qemu: true - host_target: aarch64-apple-darwin os: macos-latest - host_target: i686-pc-windows-msvc diff --git a/src/tools/miri/CONTRIBUTING.md b/src/tools/miri/CONTRIBUTING.md index 7d78fdddbad..073ad267476 100644 --- a/src/tools/miri/CONTRIBUTING.md +++ b/src/tools/miri/CONTRIBUTING.md @@ -255,6 +255,12 @@ when installing the Miri toolchain. Alternatively, set the `RUSTUP_TOOLCHAIN` en [`etc/rust_analyzer_helix.toml`]: https://github.com/rust-lang/miri/blob/master/etc/rust_analyzer_helix.toml +### Zed + +Copy [`etc/rust_analyzer_zed.json`] to `.zed/settings.json` in the project root directory. + +[`etc/rust_analyzer_zed.json`]: https://github.com/rust-lang/miri/blob/master/etc/rust_analyzer_zed.json + ### Advanced configuration If you are building Miri with a locally built rustc, set diff --git a/src/tools/miri/Cargo.lock b/src/tools/miri/Cargo.lock index b46f0f83420..4df17c83c7e 100644 --- a/src/tools/miri/Cargo.lock +++ b/src/tools/miri/Cargo.lock @@ -1569,9 +1569,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.19" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" dependencies = [ "sharded-slab", "thread_local", diff --git a/src/tools/miri/Cargo.toml b/src/tools/miri/Cargo.toml index 99111092d39..924dfed2bca 100644 --- a/src/tools/miri/Cargo.toml +++ b/src/tools/miri/Cargo.toml @@ -39,7 +39,7 @@ features = ['unprefixed_malloc_on_supported_platforms'] [target.'cfg(unix)'.dependencies] libc = "0.2" # native-lib dependencies -libffi = { version = "4.0.0", optional = true } +libffi = { version = "4.1.1", optional = true } libloading = { version = "0.8", optional = true } serde = { version = "1.0.219", features = ["derive"], optional = true } diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index 7ccd27d7b83..517aa343a6d 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -319,8 +319,14 @@ environment variable. We first document the most relevant and most commonly used Can be used without a value; in that case the range defaults to `0..64`. * `-Zmiri-many-seeds-keep-going` tells Miri to really try all the seeds in the given range, even if a failing seed has already been found. This is useful to determine which fraction of seeds fails. +* `-Zmiri-max-extra-rounding-error` tells Miri to always apply the maximum error to float operations + that do not have a guaranteed precision. The sign of the error is still non-deterministic. * `-Zmiri-no-extra-rounding-error` stops Miri from adding extra rounding errors to float operations that do not have a guaranteed precision. +* `-Zmiri-no-short-fd-operations` stops Miri from artificially forcing `read`/`write` operations + to only process a part of their buffer. Note that whenever Miri uses host operations to + implement `read`/`write` (e.g. for file-backed file descriptors), the host system can still + introduce short reads/writes. * `-Zmiri-num-cpus` states the number of available CPUs to be reported by miri. By default, the number of available CPUs is `1`. Note that this flag does not affect how miri handles threads in any way. diff --git a/src/tools/miri/cargo-miri/Cargo.lock b/src/tools/miri/cargo-miri/Cargo.lock index b3f5dafab64..ea9c04a3cb5 100644 --- a/src/tools/miri/cargo-miri/Cargo.lock +++ b/src/tools/miri/cargo-miri/Cargo.lock @@ -429,9 +429,9 @@ dependencies = [ [[package]] name = "rustc-build-sysroot" -version = "0.5.9" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb13874a0e55baf4ac3d49d38206aecb31a55b75d6c4d04fd850b53942c8cc8" +checksum = "dd41ead66a69880951b2f7df3139db401d44451b4da123344d27eaa791b89c95" dependencies = [ "anyhow", "rustc_version", diff --git a/src/tools/miri/cargo-miri/Cargo.toml b/src/tools/miri/cargo-miri/Cargo.toml index 77cb1df8e74..64b56ea114e 100644 --- a/src/tools/miri/cargo-miri/Cargo.toml +++ b/src/tools/miri/cargo-miri/Cargo.toml @@ -18,7 +18,7 @@ directories = "6" rustc_version = "0.4" serde_json = "1.0.40" cargo_metadata = "0.21" -rustc-build-sysroot = "0.5.8" +rustc-build-sysroot = "0.5.10" # Enable some feature flags that dev-dependencies need but dependencies # do not. This makes `./miri install` after `./miri build` faster. diff --git a/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs index efb9053f69a..0716f4add9d 100644 --- a/src/tools/miri/cargo-miri/src/phases.rs +++ b/src/tools/miri/cargo-miri/src/phases.rs @@ -65,16 +65,6 @@ fn forward_patched_extern_arg(args: &mut impl Iterator<Item = String>, cmd: &mut } pub fn phase_cargo_miri(mut args: impl Iterator<Item = String>) { - // Check for version and help flags even when invoked as `cargo-miri`. - if has_arg_flag("--help") || has_arg_flag("-h") { - show_help(); - return; - } - if has_arg_flag("--version") || has_arg_flag("-V") { - show_version(); - return; - } - // Require a subcommand before any flags. // We cannot know which of those flags take arguments and which do not, // so we cannot detect subcommands later. @@ -85,11 +75,36 @@ pub fn phase_cargo_miri(mut args: impl Iterator<Item = String>) { "setup" => MiriCommand::Setup, "test" | "t" | "run" | "r" | "nextest" => MiriCommand::Forward(subcommand), "clean" => MiriCommand::Clean, - _ => + _ => { + // Check for version and help flags. + if has_arg_flag("--help") || has_arg_flag("-h") { + show_help(); + return; + } + if has_arg_flag("--version") || has_arg_flag("-V") { + show_version(); + return; + } show_error!( "`cargo miri` supports the following subcommands: `run`, `test`, `nextest`, `clean`, and `setup`." - ), + ) + } }; + if has_arg_flag("--help") || has_arg_flag("-h") { + match subcommand { + MiriCommand::Forward(verb) => { + println!("`cargo miri {verb}` supports the same flags as `cargo {verb}`:\n"); + let mut cmd = cargo(); + cmd.arg(verb); + cmd.arg("--help"); + exec(cmd); + } + _ => { + show_help(); + return; + } + } + } let verbose = num_arg_flag("-v") + num_arg_flag("--verbose"); let quiet = has_arg_flag("-q") || has_arg_flag("--quiet"); diff --git a/src/tools/miri/doc/img/perfetto_aggregate_statistics.png b/src/tools/miri/doc/img/perfetto_aggregate_statistics.png new file mode 100644 index 00000000000..d4fd3826f47 --- /dev/null +++ b/src/tools/miri/doc/img/perfetto_aggregate_statistics.png Binary files differdiff --git a/src/tools/miri/doc/img/perfetto_aggregate_statistics_sql.png b/src/tools/miri/doc/img/perfetto_aggregate_statistics_sql.png new file mode 100644 index 00000000000..bda92d3885a --- /dev/null +++ b/src/tools/miri/doc/img/perfetto_aggregate_statistics_sql.png Binary files differdiff --git a/src/tools/miri/doc/img/perfetto_span.png b/src/tools/miri/doc/img/perfetto_span.png new file mode 100644 index 00000000000..1a7184f22ae --- /dev/null +++ b/src/tools/miri/doc/img/perfetto_span.png Binary files differdiff --git a/src/tools/miri/doc/img/perfetto_subname_statistics.png b/src/tools/miri/doc/img/perfetto_subname_statistics.png new file mode 100644 index 00000000000..8c86b07e925 --- /dev/null +++ b/src/tools/miri/doc/img/perfetto_subname_statistics.png Binary files differdiff --git a/src/tools/miri/doc/img/perfetto_timeline.png b/src/tools/miri/doc/img/perfetto_timeline.png new file mode 100644 index 00000000000..49f8a1fac1d --- /dev/null +++ b/src/tools/miri/doc/img/perfetto_timeline.png Binary files differdiff --git a/src/tools/miri/doc/img/perfetto_visualize_argument_values.png b/src/tools/miri/doc/img/perfetto_visualize_argument_values.png new file mode 100644 index 00000000000..1dcbacaf9cb --- /dev/null +++ b/src/tools/miri/doc/img/perfetto_visualize_argument_values.png Binary files differdiff --git a/src/tools/miri/doc/img/perfetto_visualize_argument_values_misbehaving.png b/src/tools/miri/doc/img/perfetto_visualize_argument_values_misbehaving.png new file mode 100644 index 00000000000..beeba8a4a3a --- /dev/null +++ b/src/tools/miri/doc/img/perfetto_visualize_argument_values_misbehaving.png Binary files differdiff --git a/src/tools/miri/doc/img/perfetto_visualize_argument_values_sql.png b/src/tools/miri/doc/img/perfetto_visualize_argument_values_sql.png new file mode 100644 index 00000000000..c7b163b0a57 --- /dev/null +++ b/src/tools/miri/doc/img/perfetto_visualize_argument_values_sql.png Binary files differdiff --git a/src/tools/miri/doc/tracing.md b/src/tools/miri/doc/tracing.md new file mode 100644 index 00000000000..d7114af947d --- /dev/null +++ b/src/tools/miri/doc/tracing.md @@ -0,0 +1,292 @@ +# Documentation for the tracing infrastructure in Miri + +Miri can be traced to understand how much time is spent in its various components (e.g. borrow tracker, data race checker, etc.). When tracing is enabled, running Miri will create a `.json` trace file that can be opened and analyzed in [Perfetto](https://ui.perfetto.dev/). For any questions regarding this documentation you may contact [Stypox](https://rust-lang.zulipchat.com/#narrow/dm/627563-Stypox) on Zulip. + +## Obtaining a trace file + +### From the Miri codebase + +All of the tracing functionality in Miri is gated by the `"tracing"` feature flag to ensure it does not create any overhead when unneeded. To compile Miri with this feature enabled, you can pass `--features=tracing` to `./miri`. Then, to make running Miri actually produce a trace file, you also need to set the `MIRI_TRACING` environment variable. For example: + +```sh +MIRI_TRACING=1 ./miri run --features=tracing ./tests/pass/hello.rs +``` + +### From the rustc codebase + +If you are building Miri from within the rustc tree, you need to enable the `"tracing"` feature by adding this line to `bootstrap.toml`: + +```toml +build.tool.miri.features = ["tracing"] +``` + +And then you could run the following: + +```sh +MIRI_TRACING=1 ./x.py run miri --stage 1 --args ./src/tools/miri/tests/pass/hello.rs +``` + +### The trace file + +After running Miri with tracing enabled you will get a `.json` trace file that contains a list of all events and spans that occurred throughout the execution. The file follows [this format](https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview). + +## Analyzing a trace file + +To analyze traces you can use [Perfetto UI](https://ui.perfetto.dev/), a trace analyzer made by Google that was originally a part of the Chrome browser. Just open Perfetto and drag and drop the `.json` file there. Official documentation for the controls in the UI can be found [here](https://perfetto.dev/docs/visualization/perfetto-ui). + +### The timeline + +You will see the boxes "Global Legacy Events" and "Process 1" on the left of the workspace: after clicking on either of them their timeline will expand and you will be able to zoom in and look at individual spans (and events). + +- "Process 1" contains tracing spans for the various components of Miri, all in a single timeline line (e.g. borrow tracker, data race checker, etc.) +- "Global Legacy Events" contains auxiliary spans on two separate lines that allow understanding what code is being executed at any point in time: + - "frame": what is the current stack frame in the interpreted program + - "step": what statement/terminator in the MIR of the interpreted program is being executed + +Spans are represented as colored boxes in the timeline, while instantaneous events are represented by tiny arrows. (Events exist because rustc and Miri also use the `tracing` crate for debug logging, and those logs turn into events in the trace.) + + + +### Span/event data + +You can click on a span or an event to get more information about it, including some arguments that were passed when the span/event was entered/fired. In the following screenshot you can see the details of a "layouting" span that was generated by the following line in Miri's code: + +```rust +let _trace = enter_trace_span!(M, layouting::fn_abi_of_instance, ?instance, ?extra_args); +``` + + + +### SQL tables + +Perfetto supports querying the span/event database using SQL queries (see the [docs](https://perfetto.dev/docs/analysis/perfetto-sql-syntax)). Just type `:` in the search bar at the top to enter SQL mode, and then you will be able to enter SQL queries there. The relevant SQL tables are: +- `slices`: contains all spans and events; events can be distinguished from spans since their `dur` is 0. Relevant columns are: + - `id`: a unique primary-key ID for the span (assigned by Perfetto, not present in the trace file) + - `ts` and `dur`: the beginning and duration of the span, in nanoseconds + - `name`: the name of the span + - `parent_id`: the parent span ID, or null if there is no parent (assigned by Perfetto based on the timing at which spans occur, i.e. two nested spans must be one the child of the other) + - `arg_set_id`: a foreign key into the table of arguments (1-to-N) +- `args`: contains all of the arguments of the various events/spans. Relevant columns are: + - `arg_set_id`: the key used to join the slices and args tables + - `key`: the name of the argument prepended with "args." + - `display_value`: the value of the argument + +Some useful queries are provided in the following sections. + +### Enhancing the timeline + +On the "Process 1" timeline line there are some spans with the same name, that are actually generated from different places in Miri's code. In those cases the span name indicates the component that was invoked (e.g. the data race checker), but not the specific function that was run. To inspect the specific function, we store a "subname" in an argument with the same name as the span, which unfortunately can be seen only after clicking on the span. + +To make it quicker to look at subnames, you can add a new timeline line that specifically shows the subnames for spans with a specific name. To do so: +1. select any span with the name you care about (call this name `$NAME`) +2. click on the dropdown highlighted in blue next on the argument with name `$NAME` (or `args.$NAME`) +3. click on "Visualize argument values" +4. a new timeline line will appear with only spans originally named `$NAME`, but now with the subname displayed instead + +The following screenshot shows the 4 steps for spans named "data_race": + + + +### Visualizing which "frame" or "step" is being executed + +Unfortunately the instructions in [Enhancing the timeline](#enhancing-the-timeline) only work well with spans under "Process 1", but misbehave with spans under "Global Legacy Events" (see the screenshot below). This might be a bug in Perfetto, but nevertheless a workaround is available: + +1. click on the search bar at the top and write `:` to enter SQL mode +2. copy-paste the following SQL, replace "SPAN_NAME" at the end with either "frame" or "step" (i.e. one of the two span names under "Global Legacy Events"), and press Enter to execute it: + ```sql + select slices.id, ts, dur, track_id, category, args.string_value as name, depth, stack_id, parent_stack_id, parent_id, slices.arg_set_id, thread_ts, thread_instruction_count, thread_instruction_delta, cat, slice_id + from slices inner join args using (arg_set_id) + where args.key = "args." || name and name = "SPAN_NAME" + ``` +3. at the top-right of the box at the bottom, click on "Show debug track" +4. press on "Show" in the popup that just appeared +5. a new debug track will appear with the names of steps or frames + +What the SQL does is to select only spans with the name "SPAN_NAME" and keep all of the span fields untouched, except for the name which is replaced with the subname. As explained in [Enhancing the timeline](#enhancing-the-timeline), remember that the subname is stored in an argument with the same name as the span. + + + +<img src="./img/perfetto_visualize_argument_values_misbehaving.png" width="300px"> + +### Compute aggregate statistics + +The simplest way to get aggregate statistics about a time range is to: + +1. select a time range by drag-clicking along a trace line +2. click on the "Current Selection" tab at the bottom if it's not already open +3. see various tables/visualizations of how much time is spent in each span by clicking on "Slices", "Pivot Table" or "Slice Flamegraph" + +Note that the numbers shown in the "Slices" and "Pivot Table" tabs also include nested spans, so they cannot be used to compute statistics such as "X% of time is spent in spans named Y" because two spans named Y might be nested and their duration would be counted twice. For such statistics use the method in [Compute aggregate statistics (enhanced)](#compute-aggregate-statistics-enhanced). + + + +### Compute aggregate statistics (enhanced) + +The following (long but not complicated) query can be used to find out how much time is spent in spans (grouped by their name). Only spans without a parent are considered towards the computations (see `where parent_id is null`): so for example if `validate_operand` in turn calls `layouting` (which generates a nested/child span), only the `validate_operand` statistics are increased. This query also excludes auxiliary spans (see `name != "frame" and name != "step"`). + +Note that this query does not allow selecting a time range, but that can be done by adding a condition, e.g. `ts + dur > MIN_T and ts < MAX_T` would match only spans that intersect the range `(MIN_T, MAX_T)`. Remember that the time unit is nanoseconds. + +```sql +select "TOTAL PROGRAM DURATION" as name, count(*), max(ts + dur) as "sum(dur)", 100.0 as "%", null as "min(dur)", null as "max(dur)", null as "avg(dur)", null as "stddev(dur)" +from slices + +union select "TOTAL OVER ALL SPANS (excluding events)" as name, count(*), sum(dur), cast(cast(sum(dur) as float) / (select max(ts + dur) from slices) * 1000 as int) / 10.0 as "%", min(dur), max(dur), cast(avg(dur) as int) as "avg(dur)", cast(sqrt(avg(dur*dur)-avg(dur)*avg(dur)) as int) as "stddev(dur)" +from slices +where parent_id is null and name != "frame" and name != "step" and dur > 0 + +union select name, count(*), sum(dur), cast(cast(sum(dur) as float) / (select max(ts + dur) from slices) * 1000 as int) / 10.0 as "%", min(dur), max(dur), cast(avg(dur) as int) as "avg(dur)", cast(sqrt(avg(dur*dur)-avg(dur)*avg(dur)) as int) as "stddev(dur)" +from slices +where parent_id is null and name != "frame" and name != "step" +group by name +order by sum(dur) desc, count(*) desc +``` + +This is the kind of table you would get out: + + + +### Statistics about subnames of a span + +Use the following SQL to see statistics about the subnames of spans with the same name (replace "SPAN_NAME" with the name of the span you want to see subname statistics of): + +```sql +select args.string_value as name, count(*), sum(dur), min(dur), max(dur), cast(avg(dur) as int) as "avg(dur)", cast(sqrt(avg(dur*dur)-avg(dur)*avg(dur)) as int) as "stddev(dur)" +from slices inner join args using (arg_set_id) +where args.key = "args." || name and name = "SPAN_NAME" +group by args.string_value +order by count(*) desc +``` + +For example, this is the table of how much time is spent in each borrow tracker function: + + + +### Finding long periods of time without any tracing + +The following SQL finds the longest periods of time where time is being spent, with the ability to click on IDs in the table of results to quickly reach the corresponding place. This can be useful to spot things that use up a significant amount of time but that are not yet covered by tracing calls. + +```sql +with ordered as ( + select s1.*, row_number() over (order by s1.ts) as rn + from slices as s1 + where s1.parent_id is null and s1.dur > 0 and s1.name != "frame" and s1.name != "step" +) +select a.ts+a.dur as ts, b.ts-a.ts-a.dur as dur, a.id, a.track_id, a.category, a.depth, a.stack_id, a.parent_stack_id, a.parent_id, a.arg_set_id, a.thread_ts, a.thread_instruction_count, a.thread_instruction_delta, a.cat, a.slice_id, "empty" as name +from ordered as a inner join ordered as b on a.rn=b.rn-1 +order by b.ts-a.ts-a.dur desc +``` + +### Saving Perfetto's state as a preset + +Unfortunately Perfetto does not seem to support saving the UI state as a preset that can be used to repeat the same analysis on multiple traces. You have to click through the various menus or run the various SQL queries every time to setup the UI as you want. + +## Adding new tracing calls to the code + +### The "tracing" feature + +Miri is highly interconnected with `rustc_const_eval`, and therefore collecting proper trace data about Miri also involves adding some tracing calls within `rustc_const_eval`'s codebase. As explained in [Obtaining a trace file](#obtaining-a-trace-file), tracing calls are disabled (and optimized out) when Miri's "tracing" feature is not enabled. However, while it is possible to check for the feature from Miri's codebase, it's not possible to do so from `rustc_const_eval` (since it's a separate crate, and it's even in a precompiled `.rlib` in case of out-of-tree builds). + +The solution to make it possible to check whether tracing is enabled at compile time even in `rustc_const_eval` was to add a function with this signature to the `Machine` trait: +```rust +fn enter_trace_span(span: impl FnOnce() -> tracing::Span) -> impl EnteredTraceSpan +``` + +where `EnteredTraceSpan` is just a marker trait implemented by `()` and `tracing::span::EnteredSpan`. This function returns `()` by default (without calling the `span` closure), except in `MiriMachine` where if tracing is enabled it will return `span().entered()`. + +The code in `rustc_const_eval` calls this function when it wants to do tracing, and the compiler will (hopefully) optimize out tracing calls when tracing is disabled. + +### The `enter_trace_span!()` macro + +To add tracing to a section of code in Miri or in `rustc_const_eval`, you can use the `enter_trace_span!()` macro, which takes care of the details explained in [The "tracing" feature](#the-tracing-feature). + +The `enter_trace_span!()` macro accepts the same syntax as `tracing::span!()` ([documentation](https://docs.rs/tracing/latest/tracing/#using-the-macros)) except for a few customizations, and returns an already entered trace span. The returned value is a drop guard that will exit the span when dropped, so **make sure to give it a proper scope** by storing it in a variable like this: + +```rust +let _trace = enter_trace_span!("My span"); +``` + +When calling this macro from `rustc_const_eval` you need to pass a type implementing the `Machine` trait as the first argument (since it will be used to call `Machine::enter_trace_span()`). This is usually available in various parts of `rustc_const_eval` under the name `M`, since most of `rustc_const_eval`'s code is `Machine`-agnostic. + +```rust +let _trace = enter_trace_span!("My span"); // from Miri +let _trace = enter_trace_span!(M, "My span"); // from rustc_const_eval +``` + +You can make sense of the syntaxes explained below also by looking at this Perfetto screenshot from [Span/event data](#spanevent-data). + + + +### Syntax accepted by `tracing::span!()` + +The full documentation for the `tracing::span!()` syntax can be found [here](https://docs.rs/tracing/latest/tracing/#using-the-macros) under "Using the Macros". A few possibly confusing syntaxes are listed here: +```rust +// logs a span named "hello" with a field named "arg" of value 42 (works only because +// 42 implements the tracing::Value trait, otherwise use one of the options below) +let _trace = enter_trace_span!(M, "hello", arg = 42); +// logs a field called "my_display_var" using the Display implementation +let _trace = enter_trace_span!(M, "hello", %my_display_var); +// logs a field called "my_debug_var" using the Debug implementation +let _trace = enter_trace_span!(M, "hello", ?my_debug_var); +``` + +### `NAME::SUBNAME` syntax + +In addition to the syntax accepted by `tracing::span!()`, the `enter_trace_span!()` macro optionally allows passing the span name (i.e. the first macro argument) in the form `NAME::SUBNAME` (without quotes) to indicate that the span has name "NAME" (usually the name of the component) and has an additional more specific name "SUBNAME" (usually the function name). The latter is passed to the tracing crate as a span field with the name "NAME". This allows not being distracted by subnames when looking at the trace in Perfetto, but when deeper introspection is needed within a component, it's still possible to view the subnames directly with a few steps (see [Enhancing the timeline](#enhancing-the-timeline)). +```rust +// for example, the first will expand to the second +let _trace = enter_trace_span!(M, borrow_tracker::on_stack_pop); +let _trace = enter_trace_span!(M, "borrow_tracker", borrow_tracker = "on_stack_pop"); +``` + +### `tracing_separate_thread` parameter + +Miri saves traces using the the `tracing_chrome` `tracing::Layer` so that they can be visualized in Perfetto. To instruct `tracing_chrome` to put some spans on a separate trace thread/line than other spans when viewed in Perfetto, you can pass `tracing_separate_thread = tracing::field::Empty` to the tracing macros. This is useful to separate out spans which just indicate the current step or program frame being processed by the interpreter. As explained in [The timeline](#the-timeline), those spans end up under the "Global Legacy Events" track. You should use a value of `tracing::field::Empty` so that other tracing layers (e.g. the logger) will ignore the `tracing_separate_thread` field. For example: +```rust +let _trace = enter_trace_span!(M, step::eval_statement, tracing_separate_thread = tracing::field::Empty); +``` + +### Executing something else when tracing is disabled + +The `EnteredTraceSpan` trait contains a `or_if_tracing_disabled()` function that you can use to e.g. log a line as an alternative to the tracing span for when tracing is disabled. For example: +```rust +let _trace = enter_trace_span!(M, step::eval_statement) + .or_if_tracing_disabled(|| tracing::info!("eval_statement")); +``` + +## Implementation details + +Here we explain how tracing is implemented internally. + +The events and spans generated throughout the codebase are collected by [the `tracing` crate](https://crates.io/crates/tracing), which then dispatches them to the code that writes to the trace file, but also to the logger if logging is enabled. + +### Choice of tracing library + +The crate that was chosen for collecting traces is [tracing](https://crates.io/crates/tracing), since: +- it is very well maintained +- it supports various different trace formats through plug-and-play `Layer`s (in Miri we are using `tracing_chrome` to export traces for perfetto, see [The `tracing_chrome` layer](#the-tracing_chrome-layer)) +- spans and events are collected with not just their name, but also file, line, module, and any number of custom arguments +- it was already used in Miri and rustc as a logging framework + +One major drawback of the tracing crate is, however, its big overhead. Entering and exiting a span takes on the order of 100ns, and many of Miri's spans are shorter than that, so their measurements are completely off and the program execution increases significantly. E.g. at the point of writing this documentation, enabling tracing makes Miri 5x slower. Note that this used to be even worse, see [Time measurements](#time-measurements). + +### The `tracing_chrome` layer + +Miri uses [tracing-chrome](https://github.com/thoren-d/tracing-chrome) as the `Layer` that collects spans and events from the tracing crate and saves them to a file that can be opened in Perfetto. Although the crate [is published](https://crates.io/crates/tracing-chrome) on crates.io, it was not possible to depend on it from Miri, because it would bring in a separate compilation of the `tracing` crate. This is because Miri does not directly depend on `tracing`, and instead uses rustc's version through rustc-private, and apparently cargo can't realize that the same library is being built again when rustc-private is involved. + +So the solution was to copy-paste [the only file](https://github.com/thoren-d/tracing-chrome/blob/develop/src/lib.rs) in tracing-chrome into Miri. Nevertheless, this gave the possibility to make some changes to tracing-chrome, which you can read about in documentation at the top of [the file](https://github.com/rust-lang/miri/blob/master/src/bin/log/tracing_chrome.rs) that was copied to Miri. + +### Time measurements + +tracing-chrome originally used `std::time::Instant` to measure time, however on some x86/x86_64 Linux systems it might be unbearably slow since the underlying system call (`clock_gettime`) would take ≈1.3µs. Read more [here](https://btorpey.github.io/blog/2014/02/18/clock-sources-in-linux/) about how the Linux kernel chooses the clock source. + +Therefore, on x86/x86_64 Linux systems with a CPU that has an invariant TSC counter, we read from that instead to measure time, which takes only ≈13ns. There are unfortunately a lot of caveats to this approach though, as explained [in the code](https://github.com/rust-lang/miri/blob/master/src/bin/log/tracing_chrome_instant.rs) and [in the PR](https://github.com/rust-lang/miri/pull/4524). The most impactful one is that: every thread spawned in Miri that wants to trace something (which requires measuring time) needs to pin itself to a single CPU core (using `sched_setaffinity`). + +## Other useful stuff + +### Making a flamegraph + +After compiling Miri, you can run the following command to make a flamegraph using Linux' `perf`. It can be useful to spot functions that use up a significant amount of time but that are not yet covered by tracing calls. + +```sh +perf record --call-graph dwarf -F 999 ./miri/target/debug/miri --edition 2021 --sysroot ~/.cache/miri ./tests/pass/hashmap.rs && perf script | inferno-collapse-perf | inferno-flamegraph > flamegraph.svg +``` diff --git a/src/tools/miri/etc/rust_analyzer_zed.json b/src/tools/miri/etc/rust_analyzer_zed.json new file mode 100644 index 00000000000..839914c8b68 --- /dev/null +++ b/src/tools/miri/etc/rust_analyzer_zed.json @@ -0,0 +1,41 @@ +{ + "lsp": { + "rust-analyzer": { + "initialization_options": { + "rustc": { + "source": "discover" + }, + "linkedProjects": [ + "./Cargo.toml", + "./cargo-miri/Cargo.toml", + "./genmc-sys/Cargo.toml", + "./miri-script/Cargo.toml" + ], + "check": { + "invocationStrategy": "once", + "overrideCommand": [ + "./miri", + "clippy", // make this `check` when working with a locally built rustc + "--message-format=json" + ] + }, + "cargo": { + "extraEnv": { + "MIRI_AUTO_OPS": "no", + "MIRI_IN_RA": "1" + }, + // Contrary to what the name suggests, this also affects proc macros. + "buildScripts": { + "invocationStrategy": "once", + "overrideCommand": [ + "./miri", + "check", + "--no-default-features", + "--message-format=json" + ] + } + } + } + } + } +} diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 59adc572eaa..d44488399f8 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -f605b57042ffeb320d7ae44490113a827139b766 +51ff895062ba60a7cba53f57af928c3fb7b0f2f4 diff --git a/src/tools/miri/src/bin/log/mod.rs b/src/tools/miri/src/bin/log/mod.rs index f3b2fdb5348..22f74dd46b5 100644 --- a/src/tools/miri/src/bin/log/mod.rs +++ b/src/tools/miri/src/bin/log/mod.rs @@ -1,2 +1,3 @@ pub mod setup; mod tracing_chrome; +mod tracing_chrome_instant; diff --git a/src/tools/miri/src/bin/log/tracing_chrome.rs b/src/tools/miri/src/bin/log/tracing_chrome.rs index 3379816550c..310887a13a5 100644 --- a/src/tools/miri/src/bin/log/tracing_chrome.rs +++ b/src/tools/miri/src/bin/log/tracing_chrome.rs @@ -7,12 +7,15 @@ //! (`git log -- path/to/tracing_chrome.rs`), but in summary: //! - the file attributes were changed and `extern crate` was added at the top //! - if a tracing span has a field called "tracing_separate_thread", it will be given a separate -//! span ID even in [TraceStyle::Threaded] mode, to make it appear on a separate line when viewing -//! the trace in <https://ui.perfetto.dev>. This is the syntax to trigger this behavior: +//! span ID even in [TraceStyle::Threaded] mode, to make it appear on a separate line when viewing +//! the trace in <https://ui.perfetto.dev>. This is the syntax to trigger this behavior: //! ```rust //! tracing::info_span!("my_span", tracing_separate_thread = tracing::field::Empty, /* ... */) //! ``` -//! - use i64 instead of u64 for the "id" in [ChromeLayer::get_root_id] to be compatible with Perfetto +//! - use i64 instead of u64 for the "id" in [ChromeLayer::get_root_id] to be compatible with +//! Perfetto +//! - use [ChromeLayer::with_elapsed_micros_subtracting_tracing] to make time measurements faster on +//! Linux x86/x86_64 and to subtract time spent tracing from the timestamps in the trace file //! //! Depending on the tracing-chrome crate from crates.io is unfortunately not possible, since it //! depends on `tracing_core` which conflicts with rustc_private's `tracing_core` (meaning it would @@ -50,9 +53,22 @@ use std::{ thread::JoinHandle, }; +use crate::log::tracing_chrome_instant::TracingChromeInstant; + +/// Contains thread-local data for threads that send tracing spans or events. +struct ThreadData { + /// A unique ID for this thread, will populate "tid" field in the output trace file. + tid: usize, + /// A clone of [ChromeLayer::out] to avoid the expensive operation of accessing a mutex + /// every time. This is used to send [Message]s to the thread that saves trace data to file. + out: Sender<Message>, + /// The instant in time this thread was started. All events happening on this thread will be + /// saved to the trace file with a timestamp (the "ts" field) measured relative to this instant. + start: TracingChromeInstant, +} + thread_local! { - static OUT: RefCell<Option<Sender<Message>>> = const { RefCell::new(None) }; - static TID: RefCell<Option<usize>> = const { RefCell::new(None) }; + static THREAD_DATA: RefCell<Option<ThreadData>> = const { RefCell::new(None) }; } type NameFn<S> = Box<dyn Fn(&EventOrSpan<'_, '_, S>) -> String + Send + Sync>; @@ -64,7 +80,6 @@ where S: Subscriber + for<'span> LookupSpan<'span> + Send + Sync, { out: Arc<Mutex<Sender<Message>>>, - start: std::time::Instant, max_tid: AtomicUsize, include_args: bool, include_locations: bool, @@ -323,7 +338,6 @@ where { fn new(mut builder: ChromeLayerBuilder<S>) -> (ChromeLayer<S>, FlushGuard) { let (tx, rx) = mpsc::channel(); - OUT.with(|val| val.replace(Some(tx.clone()))); let out_writer = builder .out_writer @@ -443,7 +457,6 @@ where }; let layer = ChromeLayer { out: Arc::new(Mutex::new(tx)), - start: std::time::Instant::now(), max_tid: AtomicUsize::new(0), name_fn: builder.name_fn.take(), cat_fn: builder.cat_fn.take(), @@ -456,22 +469,7 @@ where (layer, guard) } - fn get_tid(&self) -> (usize, bool) { - TID.with(|value| { - let tid = *value.borrow(); - match tid { - Some(tid) => (tid, false), - None => { - let tid = self.max_tid.fetch_add(1, Ordering::SeqCst); - value.replace(Some(tid)); - (tid, true) - } - } - }) - } - - fn get_callsite(&self, data: EventOrSpan<S>) -> Callsite { - let (tid, new_thread) = self.get_tid(); + fn get_callsite(&self, data: EventOrSpan<S>, tid: usize) -> Callsite { let name = self.name_fn.as_ref().map(|name_fn| name_fn(&data)); let target = self.cat_fn.as_ref().map(|cat_fn| cat_fn(&data)); let meta = match data { @@ -502,14 +500,6 @@ where (None, None) }; - if new_thread { - let name = match std::thread::current().name() { - Some(name) => name.to_owned(), - None => tid.to_string(), - }; - self.send_message(Message::NewThread(tid, name)); - } - Callsite { tid, name, @@ -548,31 +538,55 @@ where } } - fn enter_span(&self, span: SpanRef<S>, ts: f64) { - let callsite = self.get_callsite(EventOrSpan::Span(&span)); + fn enter_span(&self, span: SpanRef<S>, ts: f64, tid: usize, out: &Sender<Message>) { + let callsite = self.get_callsite(EventOrSpan::Span(&span), tid); let root_id = self.get_root_id(span); - self.send_message(Message::Enter(ts, callsite, root_id)); + let _ignored = out.send(Message::Enter(ts, callsite, root_id)); } - fn exit_span(&self, span: SpanRef<S>, ts: f64) { - let callsite = self.get_callsite(EventOrSpan::Span(&span)); + fn exit_span(&self, span: SpanRef<S>, ts: f64, tid: usize, out: &Sender<Message>) { + let callsite = self.get_callsite(EventOrSpan::Span(&span), tid); let root_id = self.get_root_id(span); - self.send_message(Message::Exit(ts, callsite, root_id)); + let _ignored = out.send(Message::Exit(ts, callsite, root_id)); } - fn get_ts(&self) -> f64 { - self.start.elapsed().as_nanos() as f64 / 1000.0 - } + /// Helper function that measures how much time is spent while executing `f` and accounts for it + /// in subsequent calls, with the aim to reduce biases in the data collected by `tracing_chrome` + /// by subtracting the time spent inside tracing functions from the timeline. This makes it so + /// that the time spent inside the `tracing_chrome` functions does not impact the timestamps + /// inside the trace file (i.e. `ts`), even if such functions are slow (e.g. because they need + /// to format arguments on the same thread those arguments are collected on, otherwise memory + /// safety would be broken). + /// + /// `f` is called with the microseconds elapsed since the current thread was started (**not** + /// since the program start!), with the current thread ID (i.e. `tid`), and with a [Sender] that + /// can be used to send a [Message] to the thread that collects [Message]s and saves them to the + /// trace file. + #[inline(always)] + fn with_elapsed_micros_subtracting_tracing(&self, f: impl Fn(f64, usize, &Sender<Message>)) { + THREAD_DATA.with(|value| { + let mut thread_data = value.borrow_mut(); + let (ThreadData { tid, out, start }, new_thread) = match thread_data.as_mut() { + Some(thread_data) => (thread_data, false), + None => { + let tid = self.max_tid.fetch_add(1, Ordering::SeqCst); + let out = self.out.lock().unwrap().clone(); + let start = TracingChromeInstant::setup_for_thread_and_start(tid); + *thread_data = Some(ThreadData { tid, out, start }); + (thread_data.as_mut().unwrap(), true) + } + }; - fn send_message(&self, message: Message) { - OUT.with(move |val| { - if val.borrow().is_some() { - let _ignored = val.borrow().as_ref().unwrap().send(message); - } else { - let out = self.out.lock().unwrap().clone(); - let _ignored = out.send(message); - val.replace(Some(out)); - } + start.with_elapsed_micros_subtracting_tracing(|ts| { + if new_thread { + let name = match std::thread::current().name() { + Some(name) => name.to_owned(), + None => tid.to_string(), + }; + let _ignored = out.send(Message::NewThread(*tid, name)); + } + f(ts, *tid, out); + }); }); } } @@ -586,52 +600,58 @@ where return; } - let ts = self.get_ts(); - self.enter_span(ctx.span(id).expect("Span not found."), ts); + self.with_elapsed_micros_subtracting_tracing(|ts, tid, out| { + self.enter_span(ctx.span(id).expect("Span not found."), ts, tid, out); + }); } fn on_record(&self, id: &span::Id, values: &span::Record<'_>, ctx: Context<'_, S>) { if self.include_args { - let span = ctx.span(id).unwrap(); - let mut exts = span.extensions_mut(); + self.with_elapsed_micros_subtracting_tracing(|_, _, _| { + let span = ctx.span(id).unwrap(); + let mut exts = span.extensions_mut(); - let args = exts.get_mut::<ArgsWrapper>(); + let args = exts.get_mut::<ArgsWrapper>(); - if let Some(args) = args { - let args = Arc::make_mut(&mut args.args); - values.record(&mut JsonVisitor { object: args }); - } + if let Some(args) = args { + let args = Arc::make_mut(&mut args.args); + values.record(&mut JsonVisitor { object: args }); + } + }); } } fn on_event(&self, event: &Event<'_>, _ctx: Context<'_, S>) { - let ts = self.get_ts(); - let callsite = self.get_callsite(EventOrSpan::Event(event)); - self.send_message(Message::Event(ts, callsite)); + self.with_elapsed_micros_subtracting_tracing(|ts, tid, out| { + let callsite = self.get_callsite(EventOrSpan::Event(event), tid); + let _ignored = out.send(Message::Event(ts, callsite)); + }); } fn on_exit(&self, id: &span::Id, ctx: Context<'_, S>) { if let TraceStyle::Async = self.trace_style { return; } - let ts = self.get_ts(); - self.exit_span(ctx.span(id).expect("Span not found."), ts); + self.with_elapsed_micros_subtracting_tracing(|ts, tid, out| { + self.exit_span(ctx.span(id).expect("Span not found."), ts, tid, out); + }); } fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) { - if self.include_args { - let mut args = Object::new(); - attrs.record(&mut JsonVisitor { object: &mut args }); - ctx.span(id).unwrap().extensions_mut().insert(ArgsWrapper { - args: Arc::new(args), - }); - } - if let TraceStyle::Threaded = self.trace_style { - return; - } + self.with_elapsed_micros_subtracting_tracing(|ts, tid, out| { + if self.include_args { + let mut args = Object::new(); + attrs.record(&mut JsonVisitor { object: &mut args }); + ctx.span(id).unwrap().extensions_mut().insert(ArgsWrapper { + args: Arc::new(args), + }); + } + if let TraceStyle::Threaded = self.trace_style { + return; + } - let ts = self.get_ts(); - self.enter_span(ctx.span(id).expect("Span not found."), ts); + self.enter_span(ctx.span(id).expect("Span not found."), ts, tid, out); + }); } fn on_close(&self, id: span::Id, ctx: Context<'_, S>) { @@ -639,8 +659,9 @@ where return; } - let ts = self.get_ts(); - self.exit_span(ctx.span(&id).expect("Span not found."), ts); + self.with_elapsed_micros_subtracting_tracing(|ts, tid, out| { + self.exit_span(ctx.span(&id).expect("Span not found."), ts, tid, out); + }); } } diff --git a/src/tools/miri/src/bin/log/tracing_chrome_instant.rs b/src/tools/miri/src/bin/log/tracing_chrome_instant.rs new file mode 100644 index 00000000000..f400bc20a7b --- /dev/null +++ b/src/tools/miri/src/bin/log/tracing_chrome_instant.rs @@ -0,0 +1,183 @@ +//! Code in this class was in part inspired by +//! <https://github.com/tikv/minstant/blob/27c9ec5ec90b5b67113a748a4defee0d2519518c/src/tsc_now.rs>. +//! A useful resource is also +//! <https://www.pingcap.com/blog/how-we-trace-a-kv-database-with-less-than-5-percent-performance-impact/>, +//! although this file does not implement TSC synchronization but insteads pins threads to CPUs, +//! since the former is not reliable (i.e. it might lead to non-monotonic time measurements). +//! Another useful resource for future improvements might be measureme's time measurement utils: +//! <https://github.com/rust-lang/measureme/blob/master/measureme/src/counters.rs>. +//! Documentation about how the Linux kernel chooses a clock source can be found here: +//! <https://btorpey.github.io/blog/2014/02/18/clock-sources-in-linux/>. +#![cfg(feature = "tracing")] + +/// This alternative `TracingChromeInstant` implementation was made entirely to suit the needs of +/// [crate::log::tracing_chrome], and shouldn't be used for anything else. It featues two functions: +/// - [TracingChromeInstant::setup_for_thread_and_start], which sets up the current thread to do +/// proper time tracking and returns a point in time to use as "t=0", and +/// - [TracingChromeInstant::with_elapsed_micros_subtracting_tracing], which allows +/// obtaining how much time elapsed since [TracingChromeInstant::setup_for_thread_and_start] was +/// called while accounting for (and subtracting) the time spent inside tracing-related functions. +/// +/// This measures time using [std::time::Instant], except for x86/x86_64 Linux machines, where +/// [std::time::Instant] is too slow (~1.5us) and thus `rdtsc` is used instead (~5ns). +pub enum TracingChromeInstant { + WallTime { + /// The time at which this instant was created, shifted forward to account + /// for time spent in tracing functions as explained in + /// [TracingChromeInstant::with_elapsed_micros_subtracting_tracing]'s comments. + start_instant: std::time::Instant, + }, + #[cfg(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64")))] + Tsc { + /// The value in the TSC counter when this instant was created, shifted forward to account + /// for time spent in tracing functions as explained in + /// [TracingChromeInstant::with_elapsed_micros_subtracting_tracing]'s comments. + start_tsc: u64, + /// The period of the TSC counter in microseconds. + tsc_to_microseconds: f64, + }, +} + +impl TracingChromeInstant { + /// Can be thought of as the same as [std::time::Instant::now()], but also does some setup to + /// make TSC stable in case TSC is available. This is supposed to be called (at most) once per + /// thread since the thread setup takes a few milliseconds. + /// + /// WARNING: If TSC is available, `incremental_thread_id` is used to pick to which CPU to pin + /// the current thread. Thread IDs should be assigned contiguously starting from 0. Be aware + /// that the current thread will be restricted to one CPU for the rest of the execution! + pub fn setup_for_thread_and_start(incremental_thread_id: usize) -> TracingChromeInstant { + #[cfg(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64")))] + if *tsc::IS_TSC_AVAILABLE.get_or_init(tsc::is_tsc_available) { + // We need to lock this thread to a specific CPU, because CPUs' TSC timers might be out + // of sync. + tsc::set_cpu_affinity(incremental_thread_id); + + // Can only use tsc_to_microseconds() and rdtsc() after having set the CPU affinity! + // We compute tsc_to_microseconds anew for every new thread just in case some CPU core + // has a different TSC frequency. + let tsc_to_microseconds = tsc::tsc_to_microseconds(); + let start_tsc = tsc::rdtsc(); + return TracingChromeInstant::Tsc { start_tsc, tsc_to_microseconds }; + } + + let _ = incremental_thread_id; // otherwise we get a warning when the TSC branch is disabled + TracingChromeInstant::WallTime { start_instant: std::time::Instant::now() } + } + + /// Calls `f` with the time elapsed in microseconds since this [TracingChromeInstant] was built + /// by [TracingChromeInstant::setup_for_thread_and_start], while subtracting all time previously + /// spent executing other `f`s passed to this function. This behavior allows subtracting time + /// spent in functions that log tracing data (which `f` is supposed to be) from the tracing time + /// measurements. + /// + /// Note: microseconds are used as the time unit since that's what Chrome trace files should + /// contain, see the definition of the "ts" field in + /// <https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview>. + #[inline(always)] + pub fn with_elapsed_micros_subtracting_tracing(&mut self, f: impl Fn(f64)) { + match self { + TracingChromeInstant::WallTime { start_instant } => { + // Obtain the current time (before executing `f`). + let instant_before_f = std::time::Instant::now(); + + // Using the current time (`instant_before_f`) and the `start_instant` stored in + // `self`, calculate the elapsed time (in microseconds) since this instant was + // instantiated, accounting for any time that was previously spent executing `f`. + // The "accounting" part is not computed in this line, but is rather done by + // shifting forward the `start_instant` down below. + let ts = (instant_before_f - *start_instant).as_nanos() as f64 / 1000.0; + + // Run the function (supposedly a function internal to the tracing infrastructure). + f(ts); + + // Measure how much time was spent executing `f` and shift `start_instant` forward + // by that amount. This "removes" that time from the trace. + *start_instant += std::time::Instant::now() - instant_before_f; + } + + #[cfg(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64")))] + TracingChromeInstant::Tsc { start_tsc, tsc_to_microseconds } => { + // the comments above also apply here, since it's the same logic + let tsc_before_f = tsc::rdtsc(); + let ts = ((tsc_before_f - *start_tsc) as f64) * (*tsc_to_microseconds); + f(ts); + *start_tsc += tsc::rdtsc() - tsc_before_f; + } + } + } +} + +#[cfg(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64")))] +mod tsc { + + pub static IS_TSC_AVAILABLE: std::sync::OnceLock<bool> = std::sync::OnceLock::new(); + + /// Reads the timestamp-counter register. Will give monotonic answers only when called from the + /// same thread, because the TSC of different CPUs might be out of sync. + #[inline(always)] + pub(super) fn rdtsc() -> u64 { + #[cfg(target_arch = "x86")] + use core::arch::x86::_rdtsc; + #[cfg(target_arch = "x86_64")] + use core::arch::x86_64::_rdtsc; + + unsafe { _rdtsc() } + } + + /// Estimates the frequency of the TSC counter by waiting 10ms in a busy loop and + /// looking at how much the TSC increased in the meantime. + pub(super) fn tsc_to_microseconds() -> f64 { + const BUSY_WAIT: std::time::Duration = std::time::Duration::from_millis(10); + let tsc_start = rdtsc(); + let instant_start = std::time::Instant::now(); + while instant_start.elapsed() < BUSY_WAIT { + // `thread::sleep()` is not very precise at waking up the program at the right time, + // so use a busy loop instead. + core::hint::spin_loop(); + } + let tsc_end = rdtsc(); + (BUSY_WAIT.as_nanos() as f64) / 1000.0 / ((tsc_end - tsc_start) as f64) + } + + /// Checks whether the TSC counter is available and runs at a constant rate independently + /// of CPU frequency even across different power states of the CPU (i.e. checks for the + /// `invariant_tsc` CPUID flag). + pub(super) fn is_tsc_available() -> bool { + #[cfg(target_arch = "x86")] + use core::arch::x86::__cpuid; + #[cfg(target_arch = "x86_64")] + use core::arch::x86_64::__cpuid; + + // implemented like https://docs.rs/raw-cpuid/latest/src/raw_cpuid/extended.rs.html#965-967 + const LEAF: u32 = 0x80000007; // this is the leaf for "advanced power management info" + let cpuid = unsafe { __cpuid(LEAF) }; + (cpuid.edx & (1 << 8)) != 0 // EDX bit 8 indicates invariant TSC + } + + /// Forces the current thread to run on a single CPU, which ensures the TSC counter is monotonic + /// (since TSCs of different CPUs might be out-of-sync). `incremental_thread_id` is used to pick + /// to which CPU to pin the current thread, and should be an incremental number that starts from + /// 0. + pub(super) fn set_cpu_affinity(incremental_thread_id: usize) { + let cpu_id = match std::thread::available_parallelism() { + Ok(available_parallelism) => incremental_thread_id % available_parallelism, + _ => panic!("Could not determine CPU count to properly set CPU affinity"), + }; + + let mut set = unsafe { std::mem::zeroed::<libc::cpu_set_t>() }; + unsafe { libc::CPU_SET(cpu_id, &mut set) }; + + // Set the current thread's core affinity. + if unsafe { + libc::sched_setaffinity( + 0, // Defaults to current thread + size_of::<libc::cpu_set_t>(), + &set as *const _, + ) + } != 0 + { + panic!("Could not set CPU affinity") + } + } +} diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index ae1b25f8857..9b0bee72aef 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -556,7 +556,11 @@ fn main() { } else if arg == "-Zmiri-deterministic-floats" { miri_config.float_nondet = false; } else if arg == "-Zmiri-no-extra-rounding-error" { - miri_config.float_rounding_error = false; + miri_config.float_rounding_error = miri::FloatRoundingErrorMode::None; + } else if arg == "-Zmiri-max-extra-rounding-error" { + miri_config.float_rounding_error = miri::FloatRoundingErrorMode::Max; + } else if arg == "-Zmiri-no-short-fd-operations" { + miri_config.short_fd_operations = false; } else if arg == "-Zmiri-strict-provenance" { miri_config.provenance_mode = ProvenanceMode::Strict; } else if arg == "-Zmiri-permissive-provenance" { diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs index ad2a67160f4..bed65440dc9 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs @@ -125,81 +125,64 @@ pub struct NewPermission { /// Whether a read access should be performed on the non-frozen /// part on a retag. nonfreeze_access: bool, + /// Permission for memory outside the range. + outside_perm: Permission, /// Whether this pointer is part of the arguments of a function call. /// `protector` is `Some(_)` for all pointers marked `noalias`. protector: Option<ProtectorKind>, } impl<'tcx> NewPermission { - /// Determine NewPermission of the reference from the type of the pointee. - fn from_ref_ty( + /// Determine NewPermission of the reference/Box from the type of the pointee. + /// + /// A `ref_mutability` of `None` indicates a `Box` type. + fn new( pointee: Ty<'tcx>, - mutability: Mutability, - kind: RetagKind, + ref_mutability: Option<Mutability>, + retag_kind: RetagKind, cx: &crate::MiriInterpCx<'tcx>, ) -> Option<Self> { let ty_is_unpin = pointee.is_unpin(*cx.tcx, cx.typing_env()); - let is_protected = kind == RetagKind::FnEntry; - let protector = is_protected.then_some(ProtectorKind::StrongProtector); - - Some(match mutability { - Mutability::Mut if ty_is_unpin => - NewPermission { - freeze_perm: Permission::new_reserved( - /* ty_is_freeze */ true, - is_protected, - ), - freeze_access: true, - nonfreeze_perm: Permission::new_reserved( - /* ty_is_freeze */ false, - is_protected, - ), - // If we have a mutable reference, then the non-frozen part will - // have state `ReservedIM` or `Reserved`, which can have an initial read access - // performed on it because you cannot have multiple mutable borrows. - nonfreeze_access: true, - protector, - }, - Mutability::Not => - NewPermission { - freeze_perm: Permission::new_frozen(), - freeze_access: true, - nonfreeze_perm: Permission::new_cell(), - // If it is a shared reference, then the non-frozen - // part will have state `Cell`, which should not have an initial access, - // as this can cause data races when using thread-safe data types like - // `Mutex<T>`. - nonfreeze_access: false, - protector, - }, - _ => return None, - }) - } + let ty_is_freeze = pointee.is_freeze(*cx.tcx, cx.typing_env()); + let is_protected = retag_kind == RetagKind::FnEntry; - /// Compute permission for `Box`-like type (`Box` always, and also `Unique` if enabled). - /// These pointers allow deallocation so need a different kind of protector not handled - /// by `from_ref_ty`. - fn from_unique_ty( - ty: Ty<'tcx>, - kind: RetagKind, - cx: &crate::MiriInterpCx<'tcx>, - ) -> Option<Self> { - let pointee = ty.builtin_deref(true).unwrap(); - pointee.is_unpin(*cx.tcx, cx.typing_env()).then_some(()).map(|()| { - // Regular `Unpin` box, give it `noalias` but only a weak protector - // because it is valid to deallocate it within the function. - let is_protected = kind == RetagKind::FnEntry; - let protector = is_protected.then_some(ProtectorKind::WeakProtector); - NewPermission { - freeze_perm: Permission::new_reserved(/* ty_is_freeze */ true, is_protected), - freeze_access: true, - nonfreeze_perm: Permission::new_reserved( - /* ty_is_freeze */ false, - is_protected, - ), - nonfreeze_access: true, - protector, - } + if matches!(ref_mutability, Some(Mutability::Mut) | None if !ty_is_unpin) { + // Mutable reference / Box to pinning type: retagging is a NOP. + // FIXME: with `UnsafePinned`, this should do proper per-byte tracking. + return None; + } + + let freeze_perm = match ref_mutability { + // Shared references are frozen. + Some(Mutability::Not) => Permission::new_frozen(), + // Mutable references and Boxes are reserved. + _ => Permission::new_reserved_frz(), + }; + let nonfreeze_perm = match ref_mutability { + // Shared references are "transparent". + Some(Mutability::Not) => Permission::new_cell(), + // *Protected* mutable references and boxes are reserved without regarding for interior mutability. + _ if is_protected => Permission::new_reserved_frz(), + // Unprotected mutable references and boxes start in `ReservedIm`. + _ => Permission::new_reserved_im(), + }; + + // Everything except for `Cell` gets an initial access. + let initial_access = |perm: &Permission| !perm.is_cell(); + + Some(NewPermission { + freeze_perm, + freeze_access: initial_access(&freeze_perm), + nonfreeze_perm, + nonfreeze_access: initial_access(&nonfreeze_perm), + outside_perm: if ty_is_freeze { freeze_perm } else { nonfreeze_perm }, + protector: is_protected.then_some(if ref_mutability.is_some() { + // Strong protector for references + ProtectorKind::StrongProtector + } else { + // Weak protector for boxes + ProtectorKind::WeakProtector + }), }) } } @@ -313,30 +296,20 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let span = this.machine.current_span(); - // Store initial permissions and their corresponding range. - let mut perms_map: DedupRangeMap<LocationState> = DedupRangeMap::new( - ptr_size, - LocationState::new_accessed(Permission::new_disabled(), IdempotentForeignAccess::None), // this will be overwritten - ); - // Keep track of whether the node has any part that allows for interior mutability. - // FIXME: This misses `PhantomData<UnsafeCell<T>>` which could be considered a marker - // for requesting interior mutability. - let mut has_unsafe_cell = false; - // When adding a new node, the SIFA of its parents needs to be updated, potentially across // the entire memory range. For the parts that are being accessed below, the access itself - // trivially takes care of that. However, we have to do some more work to also deal with - // the parts that are not being accessed. Specifically what we do is that we - // call `update_last_accessed_after_retag` on the SIFA of the permission set for the part of - // memory outside `perm_map` -- so that part is definitely taken care of. The remaining concern - // is the part of memory that is in the range of `perms_map`, but not accessed below. - // There we have two cases: - // * If we do have an `UnsafeCell` (`has_unsafe_cell` becomes true), then the non-accessed part - // uses `nonfreeze_perm`, so the `nonfreeze_perm` initialized parts are also fine. We enforce - // the `freeze_perm` parts to be accessed, and thus everything is taken care of. - // * If there is no `UnsafeCell`, then `freeze_perm` is used everywhere (both inside and outside the initial range), - // and we update everything to have the `freeze_perm`'s SIFA, so there are no issues. (And this assert below is not - // actually needed in this case). + // trivially takes care of that. However, we have to do some more work to also deal with the + // parts that are not being accessed. Specifically what we do is that we call + // `update_last_accessed_after_retag` on the SIFA of the permission set for the part of + // memory outside `perm_map` -- so that part is definitely taken care of. The remaining + // concern is the part of memory that is in the range of `perms_map`, but not accessed + // below. There we have two cases: + // * If the type is `!Freeze`, then the non-accessed part uses `nonfreeze_perm`, so the + // `nonfreeze_perm` initialized parts are also fine. We enforce the `freeze_perm` parts to + // be accessed via the assert below, and thus everything is taken care of. + // * If the type is `Freeze`, then `freeze_perm` is used everywhere (both inside and outside + // the initial range), and we update everything to have the `freeze_perm`'s SIFA, so there + // are no issues. (And this assert below is not actually needed in this case). assert!(new_perm.freeze_access); let protected = new_perm.protector.is_some(); @@ -350,66 +323,48 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { .get_tree_borrows_params() .precise_interior_mut; - let default_perm = if !precise_interior_mut { - // NOTE: Using `ty_is_freeze` doesn't give the same result as going through the range - // and computing `has_unsafe_cell`. This is because of zero-sized `UnsafeCell`, for which - // `has_unsafe_cell` is false, but `!ty_is_freeze` is true. - let ty_is_freeze = place.layout.ty.is_freeze(*this.tcx, this.typing_env()); - let (perm, access) = if ty_is_freeze { + // Compute initial "inside" permissions. + let loc_state = |frozen: bool| -> LocationState { + let (perm, access) = if frozen { (new_perm.freeze_perm, new_perm.freeze_access) } else { (new_perm.nonfreeze_perm, new_perm.nonfreeze_access) }; let sifa = perm.strongest_idempotent_foreign_access(protected); - let new_loc = if access { + if access { LocationState::new_accessed(perm, sifa) } else { LocationState::new_non_accessed(perm, sifa) - }; - - for (_loc_range, loc) in perms_map.iter_mut_all() { - *loc = new_loc; } - - perm + }; + let perms_map = if !precise_interior_mut { + // For `!Freeze` types, just pretend the entire thing is an `UnsafeCell`. + let ty_is_freeze = place.layout.ty.is_freeze(*this.tcx, this.typing_env()); + let state = loc_state(ty_is_freeze); + DedupRangeMap::new(ptr_size, state) } else { + // The initial state will be overwritten by the visitor below. + let mut perms_map: DedupRangeMap<LocationState> = DedupRangeMap::new( + ptr_size, + LocationState::new_accessed( + Permission::new_disabled(), + IdempotentForeignAccess::None, + ), + ); this.visit_freeze_sensitive(place, ptr_size, |range, frozen| { - has_unsafe_cell = has_unsafe_cell || !frozen; - - // We are only ever `Frozen` inside the frozen bits. - let (perm, access) = if frozen { - (new_perm.freeze_perm, new_perm.freeze_access) - } else { - (new_perm.nonfreeze_perm, new_perm.nonfreeze_access) - }; - let sifa = perm.strongest_idempotent_foreign_access(protected); - // NOTE: Currently, `access` is false if and only if `perm` is Cell, so this `if` - // doesn't not change whether any code is UB or not. We could just always use - // `new_accessed` and everything would stay the same. But that seems conceptually - // odd, so we keep the initial "accessed" bit of the `LocationState` in sync with whether - // a read access is performed below. - let new_loc = if access { - LocationState::new_accessed(perm, sifa) - } else { - LocationState::new_non_accessed(perm, sifa) - }; - - // Store initial permissions. + let state = loc_state(frozen); for (_loc_range, loc) in perms_map.iter_mut(range.start, range.size) { - *loc = new_loc; + *loc = state; } - interp_ok(()) })?; - - // Allow lazily writing to surrounding data if we found an `UnsafeCell`. - if has_unsafe_cell { new_perm.nonfreeze_perm } else { new_perm.freeze_perm } + perms_map }; let alloc_extra = this.get_alloc_extra(alloc_id)?; let mut tree_borrows = alloc_extra.borrow_tracker_tb().borrow_mut(); - for (perm_range, perm) in perms_map.iter_mut_all() { + for (perm_range, perm) in perms_map.iter_all() { if perm.is_accessed() { // Some reborrows incur a read access to the parent. // Adjust range to be relative to allocation start (rather than to `place`). @@ -447,7 +402,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { orig_tag, new_tag, perms_map, - default_perm, + new_perm.outside_perm, protected, span, )?; @@ -514,7 +469,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); let new_perm = match val.layout.ty.kind() { &ty::Ref(_, pointee, mutability) => - NewPermission::from_ref_ty(pointee, mutability, kind, this), + NewPermission::new(pointee, Some(mutability), kind, this), _ => None, }; if let Some(new_perm) = new_perm { @@ -571,8 +526,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn visit_box(&mut self, box_ty: Ty<'tcx>, place: &PlaceTy<'tcx>) -> InterpResult<'tcx> { // Only boxes for the global allocator get any special treatment. if box_ty.is_box_global(*self.ecx.tcx) { + let pointee = place.layout.ty.builtin_deref(true).unwrap(); let new_perm = - NewPermission::from_unique_ty(place.layout.ty, self.kind, self.ecx); + NewPermission::new(pointee, /* not a ref */ None, self.kind, self.ecx); self.retag_ptr_inplace(place, new_perm)?; } interp_ok(()) @@ -591,7 +547,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match place.layout.ty.kind() { &ty::Ref(_, pointee, mutability) => { let new_perm = - NewPermission::from_ref_ty(pointee, mutability, self.kind, self.ecx); + NewPermission::new(pointee, Some(mutability), self.kind, self.ecx); self.retag_ptr_inplace(place, new_perm)?; } ty::RawPtr(_, _) => { @@ -643,14 +599,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // never be ReservedIM, the value of the `ty_is_freeze` // argument doesn't matter // (`ty_is_freeze || true` in `new_reserved` will always be `true`). - freeze_perm: Permission::new_reserved( - /* ty_is_freeze */ true, /* protected */ true, - ), + freeze_perm: Permission::new_reserved_frz(), freeze_access: true, - nonfreeze_perm: Permission::new_reserved( - /* ty_is_freeze */ false, /* protected */ true, - ), + nonfreeze_perm: Permission::new_reserved_frz(), nonfreeze_access: true, + outside_perm: Permission::new_reserved_frz(), protector: Some(ProtectorKind::StrongProtector), }; this.tb_retag_place(place, new_perm) diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs index 38863ca0734..390435e58d1 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs @@ -272,28 +272,15 @@ impl Permission { /// Default initial permission of a reborrowed mutable reference that is either /// protected or not interior mutable. - fn new_reserved_frz() -> Self { + pub fn new_reserved_frz() -> Self { Self { inner: ReservedFrz { conflicted: false } } } /// Default initial permission of an unprotected interior mutable reference. - fn new_reserved_im() -> Self { + pub fn new_reserved_im() -> Self { Self { inner: ReservedIM } } - /// Wrapper around `new_reserved_frz` and `new_reserved_im` that decides - /// which to call based on the interior mutability and the retag kind (whether there - /// is a protector is relevant because being protected takes priority over being - /// interior mutable) - pub fn new_reserved(ty_is_freeze: bool, protected: bool) -> Self { - // As demonstrated by `tests/fail/tree_borrows/reservedim_spurious_write.rs`, - // interior mutability and protectors interact poorly. - // To eliminate the case of Protected Reserved IM we override interior mutability - // in the case of a protected reference: protected references are always considered - // "freeze" in their reservation phase. - if ty_is_freeze || protected { Self::new_reserved_frz() } else { Self::new_reserved_im() } - } - /// Default initial permission of a reborrowed shared reference. pub fn new_frozen() -> Self { Self { inner: Frozen } diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs index bb3fc2d80b3..d9b3696e4f8 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs @@ -610,7 +610,7 @@ mod spurious_read { }, y: LocStateProt { state: LocationState::new_non_accessed( - Permission::new_reserved(/* freeze */ true, /* protected */ true), + Permission::new_reserved_frz(), IdempotentForeignAccess::default(), ), prot: true, diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs index 4c531a8d1f5..caed98f04f3 100644 --- a/src/tools/miri/src/eval.rs +++ b/src/tools/miri/src/eval.rs @@ -32,65 +32,6 @@ pub enum MiriEntryFnType { /// will hang the program. const MAIN_THREAD_YIELDS_AT_SHUTDOWN: u32 = 256; -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum AlignmentCheck { - /// Do not check alignment. - None, - /// Check alignment "symbolically", i.e., using only the requested alignment for an allocation and not its real base address. - Symbolic, - /// Check alignment on the actual physical integer address. - Int, -} - -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum RejectOpWith { - /// Isolated op is rejected with an abort of the machine. - Abort, - - /// If not Abort, miri returns an error for an isolated op. - /// Following options determine if user should be warned about such error. - /// Do not print warning about rejected isolated op. - NoWarning, - - /// Print a warning about rejected isolated op, with backtrace. - Warning, - - /// Print a warning about rejected isolated op, without backtrace. - WarningWithoutBacktrace, -} - -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum IsolatedOp { - /// Reject an op requiring communication with the host. By - /// default, miri rejects the op with an abort. If not, it returns - /// an error code, and prints a warning about it. Warning levels - /// are controlled by `RejectOpWith` enum. - Reject(RejectOpWith), - - /// Execute op requiring communication with the host, i.e. disable isolation. - Allow, -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum BacktraceStyle { - /// Prints a terser backtrace which ideally only contains relevant information. - Short, - /// Prints a backtrace with all possible information. - Full, - /// Prints only the frame that the error occurs in. - Off, -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum ValidationMode { - /// Do not perform any kind of validation. - No, - /// Validate the interior of the value, but not things behind references. - Shallow, - /// Fully recursively validate references. - Deep, -} - /// Configuration needed to spawn a Miri instance. #[derive(Clone)] pub struct MiriConfig { @@ -171,7 +112,9 @@ pub struct MiriConfig { /// Whether floating-point operations can behave non-deterministically. pub float_nondet: bool, /// Whether floating-point operations can have a non-deterministic rounding error. - pub float_rounding_error: bool, + pub float_rounding_error: FloatRoundingErrorMode, + /// Whether Miri artifically introduces short reads/writes on file descriptors. + pub short_fd_operations: bool, } impl Default for MiriConfig { @@ -213,7 +156,8 @@ impl Default for MiriConfig { fixed_scheduling: false, force_intrinsic_fallback: false, float_nondet: true, - float_rounding_error: true, + float_rounding_error: FloatRoundingErrorMode::Random, + short_fd_operations: true, } } } diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs index b5e81460773..4628c30e2df 100644 --- a/src/tools/miri/src/intrinsics/mod.rs +++ b/src/tools/miri/src/intrinsics/mod.rs @@ -3,20 +3,16 @@ mod atomic; mod simd; -use std::ops::Neg; - use rand::Rng; use rustc_abi::Size; -use rustc_apfloat::ieee::{IeeeFloat, Semantics}; use rustc_apfloat::{self, Float, Round}; use rustc_middle::mir; -use rustc_middle::ty::{self, FloatTy, ScalarInt}; +use rustc_middle::ty::{self, FloatTy}; use rustc_span::{Symbol, sym}; use self::atomic::EvalContextExt as _; use self::helpers::{ToHost, ToSoft}; use self::simd::EvalContextExt as _; -use crate::math::{IeeeExt, apply_random_float_error_ulp}; use crate::*; /// Check that the number of args is what we expect. @@ -209,7 +205,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let [f] = check_intrinsic_arg_count(args)?; let f = this.read_scalar(f)?.to_f32()?; - let res = fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| { + let res = math::fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| { // Using host floats (but it's fine, these operations do not have // guaranteed precision). let host = f.to_host(); @@ -227,15 +223,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Apply a relative error of 4ULP to introduce some non-determinism // simulating imprecise implementations and optimizations. - let res = apply_random_float_error_ulp( + let res = math::apply_random_float_error_ulp( this, res, - 2, // log2(4) + 4, ); // Clamp the result to the guaranteed range of this function according to the C standard, // if any. - clamp_float_value(intrinsic_name, res) + math::clamp_float_value(intrinsic_name, res) }); let res = this.adjust_nan(res, &[f]); this.write_scalar(res, dest)?; @@ -253,7 +249,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let [f] = check_intrinsic_arg_count(args)?; let f = this.read_scalar(f)?.to_f64()?; - let res = fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| { + let res = math::fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| { // Using host floats (but it's fine, these operations do not have // guaranteed precision). let host = f.to_host(); @@ -271,15 +267,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Apply a relative error of 4ULP to introduce some non-determinism // simulating imprecise implementations and optimizations. - let res = apply_random_float_error_ulp( + let res = math::apply_random_float_error_ulp( this, res, - 2, // log2(4) + 4, ); // Clamp the result to the guaranteed range of this function according to the C standard, // if any. - clamp_float_value(intrinsic_name, res) + math::clamp_float_value(intrinsic_name, res) }); let res = this.adjust_nan(res, &[f]); this.write_scalar(res, dest)?; @@ -330,16 +326,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let f1 = this.read_scalar(f1)?.to_f32()?; let f2 = this.read_scalar(f2)?.to_f32()?; - let res = fixed_float_value(this, intrinsic_name, &[f1, f2]).unwrap_or_else(|| { - // Using host floats (but it's fine, this operation does not have guaranteed precision). - let res = f1.to_host().powf(f2.to_host()).to_soft(); + let res = + math::fixed_float_value(this, intrinsic_name, &[f1, f2]).unwrap_or_else(|| { + // Using host floats (but it's fine, this operation does not have guaranteed precision). + let res = f1.to_host().powf(f2.to_host()).to_soft(); - // Apply a relative error of 4ULP to introduce some non-determinism - // simulating imprecise implementations and optimizations. - apply_random_float_error_ulp( - this, res, 2, // log2(4) - ) - }); + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + math::apply_random_float_error_ulp(this, res, 4) + }); let res = this.adjust_nan(res, &[f1, f2]); this.write_scalar(res, dest)?; } @@ -348,16 +343,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let f1 = this.read_scalar(f1)?.to_f64()?; let f2 = this.read_scalar(f2)?.to_f64()?; - let res = fixed_float_value(this, intrinsic_name, &[f1, f2]).unwrap_or_else(|| { - // Using host floats (but it's fine, this operation does not have guaranteed precision). - let res = f1.to_host().powf(f2.to_host()).to_soft(); + let res = + math::fixed_float_value(this, intrinsic_name, &[f1, f2]).unwrap_or_else(|| { + // Using host floats (but it's fine, this operation does not have guaranteed precision). + let res = f1.to_host().powf(f2.to_host()).to_soft(); - // Apply a relative error of 4ULP to introduce some non-determinism - // simulating imprecise implementations and optimizations. - apply_random_float_error_ulp( - this, res, 2, // log2(4) - ) - }); + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + math::apply_random_float_error_ulp(this, res, 4) + }); let res = this.adjust_nan(res, &[f1, f2]); this.write_scalar(res, dest)?; } @@ -367,15 +361,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let f = this.read_scalar(f)?.to_f32()?; let i = this.read_scalar(i)?.to_i32()?; - let res = fixed_powi_float_value(this, f, i).unwrap_or_else(|| { + let res = math::fixed_powi_value(this, f, i).unwrap_or_else(|| { // Using host floats (but it's fine, this operation does not have guaranteed precision). let res = f.to_host().powi(i).to_soft(); // Apply a relative error of 4ULP to introduce some non-determinism // simulating imprecise implementations and optimizations. - apply_random_float_error_ulp( - this, res, 2, // log2(4) - ) + math::apply_random_float_error_ulp(this, res, 4) }); let res = this.adjust_nan(res, &[f]); this.write_scalar(res, dest)?; @@ -385,15 +377,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let f = this.read_scalar(f)?.to_f64()?; let i = this.read_scalar(i)?.to_i32()?; - let res = fixed_powi_float_value(this, f, i).unwrap_or_else(|| { + let res = math::fixed_powi_value(this, f, i).unwrap_or_else(|| { // Using host floats (but it's fine, this operation does not have guaranteed precision). let res = f.to_host().powi(i).to_soft(); // Apply a relative error of 4ULP to introduce some non-determinism // simulating imprecise implementations and optimizations. - apply_random_float_error_ulp( - this, res, 2, // log2(4) - ) + math::apply_random_float_error_ulp(this, res, 4) }); let res = this.adjust_nan(res, &[f]); this.write_scalar(res, dest)?; @@ -448,7 +438,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } // Apply a relative error of 4ULP to simulate non-deterministic precision loss // due to optimizations. - let res = apply_random_float_error_to_imm(this, res, 2 /* log2(4) */)?; + let res = math::apply_random_float_error_to_imm(this, res, 4)?; this.write_immediate(*res, dest)?; } @@ -485,133 +475,3 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { interp_ok(EmulateItemResult::NeedsReturn) } } - -/// Applies a random ULP floating point error to `val` and returns the new value. -/// So if you want an X ULP error, `ulp_exponent` should be log2(X). -/// -/// Will fail if `val` is not a floating point number. -fn apply_random_float_error_to_imm<'tcx>( - ecx: &mut MiriInterpCx<'tcx>, - val: ImmTy<'tcx>, - ulp_exponent: u32, -) -> InterpResult<'tcx, ImmTy<'tcx>> { - let scalar = val.to_scalar_int()?; - let res: ScalarInt = match val.layout.ty.kind() { - ty::Float(FloatTy::F16) => - apply_random_float_error_ulp(ecx, scalar.to_f16(), ulp_exponent).into(), - ty::Float(FloatTy::F32) => - apply_random_float_error_ulp(ecx, scalar.to_f32(), ulp_exponent).into(), - ty::Float(FloatTy::F64) => - apply_random_float_error_ulp(ecx, scalar.to_f64(), ulp_exponent).into(), - ty::Float(FloatTy::F128) => - apply_random_float_error_ulp(ecx, scalar.to_f128(), ulp_exponent).into(), - _ => bug!("intrinsic called with non-float input type"), - }; - - interp_ok(ImmTy::from_scalar_int(res, val.layout)) -} - -/// For the intrinsics: -/// - sinf32, sinf64 -/// - cosf32, cosf64 -/// - expf32, expf64, exp2f32, exp2f64 -/// - logf32, logf64, log2f32, log2f64, log10f32, log10f64 -/// - powf32, powf64 -/// -/// # Return -/// -/// Returns `Some(output)` if the `intrinsic` results in a defined fixed `output` specified in the C standard -/// (specifically, C23 annex F.10) when given `args` as arguments. Outputs that are unaffected by a relative error -/// (such as INF and zero) are not handled here, they are assumed to be handled by the underlying -/// implementation. Returns `None` if no specific value is guaranteed. -/// -/// # Note -/// -/// For `powf*` operations of the form: -/// -/// - `(SNaN)^(±0)` -/// - `1^(SNaN)` -/// -/// The result is implementation-defined: -/// - musl returns for both `1.0` -/// - glibc returns for both `NaN` -/// -/// This discrepancy exists because SNaN handling is not consistently defined across platforms, -/// and the C standard leaves behavior for SNaNs unspecified. -/// -/// Miri chooses to adhere to both implementations and returns either one of them non-deterministically. -fn fixed_float_value<S: Semantics>( - ecx: &mut MiriInterpCx<'_>, - intrinsic_name: &str, - args: &[IeeeFloat<S>], -) -> Option<IeeeFloat<S>> { - let one = IeeeFloat::<S>::one(); - Some(match (intrinsic_name, args) { - // cos(+- 0) = 1 - ("cosf32" | "cosf64", [input]) if input.is_zero() => one, - - // e^0 = 1 - ("expf32" | "expf64" | "exp2f32" | "exp2f64", [input]) if input.is_zero() => one, - - // (-1)^(±INF) = 1 - ("powf32" | "powf64", [base, exp]) if *base == -one && exp.is_infinite() => one, - - // 1^y = 1 for any y, even a NaN - ("powf32" | "powf64", [base, exp]) if *base == one => { - let rng = ecx.machine.rng.get_mut(); - // SNaN exponents get special treatment: they might return 1, or a NaN. - let return_nan = exp.is_signaling() && ecx.machine.float_nondet && rng.random(); - // Handle both the musl and glibc cases non-deterministically. - if return_nan { ecx.generate_nan(args) } else { one } - } - - // x^(±0) = 1 for any x, even a NaN - ("powf32" | "powf64", [base, exp]) if exp.is_zero() => { - let rng = ecx.machine.rng.get_mut(); - // SNaN bases get special treatment: they might return 1, or a NaN. - let return_nan = base.is_signaling() && ecx.machine.float_nondet && rng.random(); - // Handle both the musl and glibc cases non-deterministically. - if return_nan { ecx.generate_nan(args) } else { one } - } - - // There are a lot of cases for fixed outputs according to the C Standard, but these are - // mainly INF or zero which are not affected by the applied error. - _ => return None, - }) -} - -/// Returns `Some(output)` if `powi` (called `pown` in C) results in a fixed value specified in the -/// C standard (specifically, C23 annex F.10.4.6) when doing `base^exp`. Otherwise, returns `None`. -fn fixed_powi_float_value<S: Semantics>( - ecx: &mut MiriInterpCx<'_>, - base: IeeeFloat<S>, - exp: i32, -) -> Option<IeeeFloat<S>> { - Some(match exp { - 0 => { - let one = IeeeFloat::<S>::one(); - let rng = ecx.machine.rng.get_mut(); - let return_nan = ecx.machine.float_nondet && rng.random() && base.is_signaling(); - // For SNaN treatment, we are consistent with `powf`above. - // (We wouldn't have two, unlike powf all implementations seem to agree for powi, - // but for now we are maximally conservative.) - if return_nan { ecx.generate_nan(&[base]) } else { one } - } - - _ => return None, - }) -} - -/// Given an floating-point operation and a floating-point value, clamps the result to the output -/// range of the given operation. -fn clamp_float_value<S: Semantics>(intrinsic_name: &str, val: IeeeFloat<S>) -> IeeeFloat<S> { - match intrinsic_name { - // sin and cos: [-1, 1] - "sinf32" | "cosf32" | "sinf64" | "cosf64" => - val.clamp(IeeeFloat::<S>::one().neg(), IeeeFloat::<S>::one()), - // exp: [0, +INF] - "expf32" | "exp2f32" | "expf64" | "exp2f64" => - IeeeFloat::<S>::maximum(val, IeeeFloat::<S>::ZERO), - _ => val, - } -} diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 5ed6d6b346c..0856411b8e8 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -138,15 +138,14 @@ pub use crate::data_structures::mono_hash_map::MonoHashMap; pub use crate::diagnostics::{ EvalContextExt as _, NonHaltingDiagnostic, TerminationInfo, report_error, }; -pub use crate::eval::{ - AlignmentCheck, BacktraceStyle, IsolatedOp, MiriConfig, MiriEntryFnType, RejectOpWith, - ValidationMode, create_ecx, eval_entry, -}; +pub use crate::eval::{MiriConfig, MiriEntryFnType, create_ecx, eval_entry}; pub use crate::helpers::{AccessKind, EvalContextExt as _, ToU64 as _, ToUsize as _}; pub use crate::intrinsics::EvalContextExt as _; pub use crate::machine::{ - AllocExtra, DynMachineCallback, FrameExtra, MachineCallback, MemoryKind, MiriInterpCx, - MiriInterpCxExt, MiriMachine, MiriMemoryKind, PrimitiveLayouts, Provenance, ProvenanceExtra, + AlignmentCheck, AllocExtra, BacktraceStyle, DynMachineCallback, FloatRoundingErrorMode, + FrameExtra, IsolatedOp, MachineCallback, MemoryKind, MiriInterpCx, MiriInterpCxExt, + MiriMachine, MiriMemoryKind, PrimitiveLayouts, Provenance, ProvenanceExtra, RejectOpWith, + ValidationMode, }; pub use crate::operator::EvalContextExt as _; pub use crate::provenance_gc::{EvalContextExt as _, LiveAllocs, VisitProvenance, VisitWith}; diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 0b2ce900414..e4540fb9bc5 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -49,6 +49,75 @@ pub const SIGRTMAX: i32 = 42; /// base address for each evaluation would produce unbounded memory usage. const ADDRS_PER_ANON_GLOBAL: usize = 32; +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum AlignmentCheck { + /// Do not check alignment. + None, + /// Check alignment "symbolically", i.e., using only the requested alignment for an allocation and not its real base address. + Symbolic, + /// Check alignment on the actual physical integer address. + Int, +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum RejectOpWith { + /// Isolated op is rejected with an abort of the machine. + Abort, + + /// If not Abort, miri returns an error for an isolated op. + /// Following options determine if user should be warned about such error. + /// Do not print warning about rejected isolated op. + NoWarning, + + /// Print a warning about rejected isolated op, with backtrace. + Warning, + + /// Print a warning about rejected isolated op, without backtrace. + WarningWithoutBacktrace, +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum IsolatedOp { + /// Reject an op requiring communication with the host. By + /// default, miri rejects the op with an abort. If not, it returns + /// an error code, and prints a warning about it. Warning levels + /// are controlled by `RejectOpWith` enum. + Reject(RejectOpWith), + + /// Execute op requiring communication with the host, i.e. disable isolation. + Allow, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum BacktraceStyle { + /// Prints a terser backtrace which ideally only contains relevant information. + Short, + /// Prints a backtrace with all possible information. + Full, + /// Prints only the frame that the error occurs in. + Off, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum ValidationMode { + /// Do not perform any kind of validation. + No, + /// Validate the interior of the value, but not things behind references. + Shallow, + /// Fully recursively validate references. + Deep, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum FloatRoundingErrorMode { + /// Apply a random error (the default). + Random, + /// Don't apply any error. + None, + /// Always apply the maximum error (with a random sign). + Max, +} + /// Extra data stored with each stack frame pub struct FrameExtra<'tcx> { /// Extra data for the Borrow Tracker. @@ -599,7 +668,10 @@ pub struct MiriMachine<'tcx> { /// Whether floating-point operations can behave non-deterministically. pub float_nondet: bool, /// Whether floating-point operations can have a non-deterministic rounding error. - pub float_rounding_error: bool, + pub float_rounding_error: FloatRoundingErrorMode, + + /// Whether Miri artifically introduces short reads/writes on file descriptors. + pub short_fd_operations: bool, } impl<'tcx> MiriMachine<'tcx> { @@ -761,6 +833,7 @@ impl<'tcx> MiriMachine<'tcx> { force_intrinsic_fallback: config.force_intrinsic_fallback, float_nondet: config.float_nondet, float_rounding_error: config.float_rounding_error, + short_fd_operations: config.short_fd_operations, } } @@ -937,6 +1010,7 @@ impl VisitProvenance for MiriMachine<'_> { force_intrinsic_fallback: _, float_nondet: _, float_rounding_error: _, + short_fd_operations: _, } = self; threads.visit_provenance(visit); @@ -1077,7 +1151,8 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { .target_features .iter() .filter(|&feature| { - feature.kind != TargetFeatureKind::Implied && !ecx.tcx.sess.target_features.contains(&feature.name) + feature.kind != TargetFeatureKind::Implied + && !ecx.tcx.sess.target_features.contains(&feature.name) }) .fold(String::new(), |mut s, feature| { if !s.is_empty() { @@ -1208,7 +1283,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { ecx: &mut InterpCx<'tcx, Self>, val: ImmTy<'tcx>, ) -> InterpResult<'tcx, ImmTy<'tcx>> { - crate::math::apply_random_float_error_to_imm(ecx, val, 2 /* log2(4) */) + crate::math::apply_random_float_error_to_imm(ecx, val, 4) } #[inline(always)] diff --git a/src/tools/miri/src/math.rs b/src/tools/miri/src/math.rs index e9e5a1070c9..7d2f1c08368 100644 --- a/src/tools/miri/src/math.rs +++ b/src/tools/miri/src/math.rs @@ -1,78 +1,317 @@ +use std::ops::Neg; +use std::{f32, f64}; + use rand::Rng as _; use rustc_apfloat::Float as _; -use rustc_apfloat::ieee::IeeeFloat; +use rustc_apfloat::ieee::{DoubleS, IeeeFloat, Semantics, SingleS}; use rustc_middle::ty::{self, FloatTy, ScalarInt}; use crate::*; /// Disturbes a floating-point result by a relative error in the range (-2^scale, 2^scale). -/// -/// For a 2^N ULP error, you can use an `err_scale` of `-(F::PRECISION - 1 - N)`. -/// In other words, a 1 ULP (absolute) error is the same as a `2^-(F::PRECISION-1)` relative error. -/// (Subtracting 1 compensates for the integer bit.) pub(crate) fn apply_random_float_error<F: rustc_apfloat::Float>( ecx: &mut crate::MiriInterpCx<'_>, val: F, err_scale: i32, ) -> F { - if !ecx.machine.float_nondet || !ecx.machine.float_rounding_error { + if !ecx.machine.float_nondet + || matches!(ecx.machine.float_rounding_error, FloatRoundingErrorMode::None) + // relative errors don't do anything to zeros... avoid messing up the sign + || val.is_zero() + // The logic below makes no sense if the input is already non-finite. + || !val.is_finite() + { return val; } - let rng = ecx.machine.rng.get_mut(); + // Generate a random integer in the range [0, 2^PREC). // (When read as binary, the position of the first `1` determines the exponent, // and the remaining bits fill the mantissa. `PREC` is one plus the size of the mantissa, // so this all works out.) - let r = F::from_u128(rng.random_range(0..(1 << F::PRECISION))).value; + let r = F::from_u128(match ecx.machine.float_rounding_error { + FloatRoundingErrorMode::Random => rng.random_range(0..(1 << F::PRECISION)), + FloatRoundingErrorMode::Max => (1 << F::PRECISION) - 1, // force max error + FloatRoundingErrorMode::None => unreachable!(), + }) + .value; // Multiply this with 2^(scale - PREC). The result is between 0 and // 2^PREC * 2^(scale - PREC) = 2^scale. let err = r.scalbn(err_scale.strict_sub(F::PRECISION.try_into().unwrap())); // give it a random sign let err = if rng.random() { -err } else { err }; - // multiple the value with (1+err) - (val * (F::from_u128(1).value + err).value).value + // Compute `val*(1+err)`, distributed out as `val + val*err` to avoid the imprecise addition + // error being amplified by multiplication. + (val + (val * err).value).value } -/// [`apply_random_float_error`] gives instructions to apply a 2^N ULP error. -/// This function implements these instructions such that applying a 2^N ULP error is less error prone. -/// So for a 2^N ULP error, you would pass N as the `ulp_exponent` argument. +/// Applies an error of `[-N, +N]` ULP to the given value. pub(crate) fn apply_random_float_error_ulp<F: rustc_apfloat::Float>( ecx: &mut crate::MiriInterpCx<'_>, val: F, - ulp_exponent: u32, + max_error: u32, ) -> F { - let n = i32::try_from(ulp_exponent) - .expect("`err_scale_for_ulp`: exponent is too large to create an error scale"); - // we know this fits - let prec = i32::try_from(F::PRECISION).unwrap(); - let err_scale = -(prec - n - 1); - apply_random_float_error(ecx, val, err_scale) + // We could try to be clever and reuse `apply_random_float_error`, but that is hard to get right + // (see <https://github.com/rust-lang/miri/pull/4558#discussion_r2316838085> for why) so we + // implement the logic directly instead. + if !ecx.machine.float_nondet + || matches!(ecx.machine.float_rounding_error, FloatRoundingErrorMode::None) + // FIXME: also disturb zeros? That requires a lot more cases in `fixed_float_value` + // and might make the std test suite quite unhappy. + || val.is_zero() + // The logic below makes no sense if the input is already non-finite. + || !val.is_finite() + { + return val; + } + let rng = ecx.machine.rng.get_mut(); + + let max_error = i64::from(max_error); + let error = match ecx.machine.float_rounding_error { + FloatRoundingErrorMode::Random => rng.random_range(-max_error..=max_error), + FloatRoundingErrorMode::Max => + if rng.random() { + max_error + } else { + -max_error + }, + FloatRoundingErrorMode::None => unreachable!(), + }; + // If upwards ULP and downwards ULP differ, we take the average. + let ulp = (((val.next_up().value - val).value + (val - val.next_down().value).value).value + / F::from_u128(2).value) + .value; + // Shift the value by N times the ULP + (val + (ulp * F::from_i128(error.into()).value).value).value } -/// Applies a random 16ULP floating point error to `val` and returns the new value. +/// Applies an error of `[-N, +N]` ULP to the given value. /// Will fail if `val` is not a floating point number. pub(crate) fn apply_random_float_error_to_imm<'tcx>( ecx: &mut MiriInterpCx<'tcx>, val: ImmTy<'tcx>, - ulp_exponent: u32, + max_error: u32, ) -> InterpResult<'tcx, ImmTy<'tcx>> { let scalar = val.to_scalar_int()?; let res: ScalarInt = match val.layout.ty.kind() { ty::Float(FloatTy::F16) => - apply_random_float_error_ulp(ecx, scalar.to_f16(), ulp_exponent).into(), + apply_random_float_error_ulp(ecx, scalar.to_f16(), max_error).into(), ty::Float(FloatTy::F32) => - apply_random_float_error_ulp(ecx, scalar.to_f32(), ulp_exponent).into(), + apply_random_float_error_ulp(ecx, scalar.to_f32(), max_error).into(), ty::Float(FloatTy::F64) => - apply_random_float_error_ulp(ecx, scalar.to_f64(), ulp_exponent).into(), + apply_random_float_error_ulp(ecx, scalar.to_f64(), max_error).into(), ty::Float(FloatTy::F128) => - apply_random_float_error_ulp(ecx, scalar.to_f128(), ulp_exponent).into(), + apply_random_float_error_ulp(ecx, scalar.to_f128(), max_error).into(), _ => bug!("intrinsic called with non-float input type"), }; interp_ok(ImmTy::from_scalar_int(res, val.layout)) } +/// Given a floating-point operation and a floating-point value, clamps the result to the output +/// range of the given operation according to the C standard, if any. +pub(crate) fn clamp_float_value<S: Semantics>( + intrinsic_name: &str, + val: IeeeFloat<S>, +) -> IeeeFloat<S> +where + IeeeFloat<S>: IeeeExt, +{ + let zero = IeeeFloat::<S>::ZERO; + let one = IeeeFloat::<S>::one(); + let two = IeeeFloat::<S>::two(); + let pi = IeeeFloat::<S>::pi(); + let pi_over_2 = (pi / two).value; + + match intrinsic_name { + // sin, cos, tanh: [-1, 1] + #[rustfmt::skip] + | "sinf32" + | "sinf64" + | "cosf32" + | "cosf64" + | "tanhf" + | "tanh" + => val.clamp(one.neg(), one), + + // exp: [0, +INF) + "expf32" | "exp2f32" | "expf64" | "exp2f64" => val.maximum(zero), + + // cosh: [1, +INF) + "coshf" | "cosh" => val.maximum(one), + + // acos: [0, π] + "acosf" | "acos" => val.clamp(zero, pi), + + // asin: [-π, +π] + "asinf" | "asin" => val.clamp(pi.neg(), pi), + + // atan: (-π/2, +π/2) + "atanf" | "atan" => val.clamp(pi_over_2.neg(), pi_over_2), + + // erfc: (-1, 1) + "erff" | "erf" => val.clamp(one.neg(), one), + + // erfc: (0, 2) + "erfcf" | "erfc" => val.clamp(zero, two), + + // atan2(y, x): arctan(y/x) in [−π, +π] + "atan2f" | "atan2" => val.clamp(pi.neg(), pi), + + _ => val, + } +} + +/// For the intrinsics: +/// - sinf32, sinf64, sinhf, sinh +/// - cosf32, cosf64, coshf, cosh +/// - tanhf, tanh, atanf, atan, atan2f, atan2 +/// - expf32, expf64, exp2f32, exp2f64 +/// - logf32, logf64, log2f32, log2f64, log10f32, log10f64 +/// - powf32, powf64 +/// - erff, erf, erfcf, erfc +/// - hypotf, hypot +/// +/// # Return +/// +/// Returns `Some(output)` if the `intrinsic` results in a defined fixed `output` specified in the C standard +/// (specifically, C23 annex F.10) when given `args` as arguments. Outputs that are unaffected by a relative error +/// (such as INF and zero) are not handled here, they are assumed to be handled by the underlying +/// implementation. Returns `None` if no specific value is guaranteed. +/// +/// # Note +/// +/// For `powf*` operations of the form: +/// +/// - `(SNaN)^(±0)` +/// - `1^(SNaN)` +/// +/// The result is implementation-defined: +/// - musl returns for both `1.0` +/// - glibc returns for both `NaN` +/// +/// This discrepancy exists because SNaN handling is not consistently defined across platforms, +/// and the C standard leaves behavior for SNaNs unspecified. +/// +/// Miri chooses to adhere to both implementations and returns either one of them non-deterministically. +pub(crate) fn fixed_float_value<S: Semantics>( + ecx: &mut MiriInterpCx<'_>, + intrinsic_name: &str, + args: &[IeeeFloat<S>], +) -> Option<IeeeFloat<S>> +where + IeeeFloat<S>: IeeeExt, +{ + let this = ecx.eval_context_mut(); + let one = IeeeFloat::<S>::one(); + let two = IeeeFloat::<S>::two(); + let three = IeeeFloat::<S>::three(); + let pi = IeeeFloat::<S>::pi(); + let pi_over_2 = (pi / two).value; + let pi_over_4 = (pi_over_2 / two).value; + + Some(match (intrinsic_name, args) { + // cos(±0) and cosh(±0)= 1 + ("cosf32" | "cosf64" | "coshf" | "cosh", [input]) if input.is_zero() => one, + + // e^0 = 1 + ("expf32" | "expf64" | "exp2f32" | "exp2f64", [input]) if input.is_zero() => one, + + // tanh(±INF) = ±1 + ("tanhf" | "tanh", [input]) if input.is_infinite() => one.copy_sign(*input), + + // atan(±INF) = ±π/2 + ("atanf" | "atan", [input]) if input.is_infinite() => pi_over_2.copy_sign(*input), + + // erf(±INF) = ±1 + ("erff" | "erf", [input]) if input.is_infinite() => one.copy_sign(*input), + + // erfc(-INF) = 2 + ("erfcf" | "erfc", [input]) if input.is_neg_infinity() => (one + one).value, + + // hypot(x, ±0) = abs(x), if x is not a NaN. + ("_hypotf" | "hypotf" | "_hypot" | "hypot", [x, y]) if !x.is_nan() && y.is_zero() => + x.abs(), + + // atan2(±0,−0) = ±π. + // atan2(±0, y) = ±π for y < 0. + // Must check for non NaN because `y.is_negative()` also applies to NaN. + ("atan2f" | "atan2", [x, y]) if (x.is_zero() && (y.is_negative() && !y.is_nan())) => + pi.copy_sign(*x), + + // atan2(±x,−∞) = ±π for finite x > 0. + ("atan2f" | "atan2", [x, y]) + if (!x.is_zero() && !x.is_infinite()) && y.is_neg_infinity() => + pi.copy_sign(*x), + + // atan2(x, ±0) = −π/2 for x < 0. + // atan2(x, ±0) = π/2 for x > 0. + ("atan2f" | "atan2", [x, y]) if !x.is_zero() && y.is_zero() => pi_over_2.copy_sign(*x), + + //atan2(±∞, −∞) = ±3π/4 + ("atan2f" | "atan2", [x, y]) if x.is_infinite() && y.is_neg_infinity() => + (pi_over_4 * three).value.copy_sign(*x), + + //atan2(±∞, +∞) = ±π/4 + ("atan2f" | "atan2", [x, y]) if x.is_infinite() && y.is_pos_infinity() => + pi_over_4.copy_sign(*x), + + // atan2(±∞, y) returns ±π/2 for finite y. + ("atan2f" | "atan2", [x, y]) if x.is_infinite() && (!y.is_infinite() && !y.is_nan()) => + pi_over_2.copy_sign(*x), + + // (-1)^(±INF) = 1 + ("powf32" | "powf64", [base, exp]) if *base == -one && exp.is_infinite() => one, + + // 1^y = 1 for any y, even a NaN + ("powf32" | "powf64", [base, exp]) if *base == one => { + let rng = this.machine.rng.get_mut(); + // SNaN exponents get special treatment: they might return 1, or a NaN. + let return_nan = exp.is_signaling() && this.machine.float_nondet && rng.random(); + // Handle both the musl and glibc cases non-deterministically. + if return_nan { this.generate_nan(args) } else { one } + } + + // x^(±0) = 1 for any x, even a NaN + ("powf32" | "powf64", [base, exp]) if exp.is_zero() => { + let rng = this.machine.rng.get_mut(); + // SNaN bases get special treatment: they might return 1, or a NaN. + let return_nan = base.is_signaling() && this.machine.float_nondet && rng.random(); + // Handle both the musl and glibc cases non-deterministically. + if return_nan { this.generate_nan(args) } else { one } + } + + // There are a lot of cases for fixed outputs according to the C Standard, but these are + // mainly INF or zero which are not affected by the applied error. + _ => return None, + }) +} + +/// Returns `Some(output)` if `powi` (called `pown` in C) results in a fixed value specified in the +/// C standard (specifically, C23 annex F.10.4.6) when doing `base^exp`. Otherwise, returns `None`. +pub(crate) fn fixed_powi_value<S: Semantics>( + ecx: &mut MiriInterpCx<'_>, + base: IeeeFloat<S>, + exp: i32, +) -> Option<IeeeFloat<S>> +where + IeeeFloat<S>: IeeeExt, +{ + match exp { + 0 => { + let one = IeeeFloat::<S>::one(); + let rng = ecx.machine.rng.get_mut(); + let return_nan = ecx.machine.float_nondet && rng.random() && base.is_signaling(); + // For SNaN treatment, we are consistent with `powf`above. + // (We wouldn't have two, unlike powf all implementations seem to agree for powi, + // but for now we are maximally conservative.) + Some(if return_nan { ecx.generate_nan(&[base]) } else { one }) + } + + _ => return None, + } +} + pub(crate) fn sqrt<S: rustc_apfloat::ieee::Semantics>(x: IeeeFloat<S>) -> IeeeFloat<S> { match x.category() { // preserve zero sign @@ -155,19 +394,47 @@ pub(crate) fn sqrt<S: rustc_apfloat::ieee::Semantics>(x: IeeeFloat<S>) -> IeeeFl } } -/// Extend functionality of rustc_apfloat softfloats +/// Extend functionality of `rustc_apfloat` softfloats for IEEE float types. pub trait IeeeExt: rustc_apfloat::Float { + // Some values we use: + #[inline] fn one() -> Self { Self::from_u128(1).value } #[inline] + fn two() -> Self { + Self::from_u128(2).value + } + + #[inline] + fn three() -> Self { + Self::from_u128(3).value + } + + fn pi() -> Self; + + #[inline] fn clamp(self, min: Self, max: Self) -> Self { self.maximum(min).minimum(max) } } -impl<S: rustc_apfloat::ieee::Semantics> IeeeExt for IeeeFloat<S> {} + +macro_rules! impl_ieee_pi { + ($float_ty:ident, $semantic:ty) => { + impl IeeeExt for IeeeFloat<$semantic> { + #[inline] + fn pi() -> Self { + // We take the value from the standard library as the most reasonable source for an exact π here. + Self::from_bits($float_ty::consts::PI.to_bits().into()) + } + } + }; +} + +impl_ieee_pi!(f32, SingleS); +impl_ieee_pi!(f64, DoubleS); #[cfg(test)] mod tests { diff --git a/src/tools/miri/src/shims/files.rs b/src/tools/miri/src/shims/files.rs index 0d4642c6ad0..8c29cb040b5 100644 --- a/src/tools/miri/src/shims/files.rs +++ b/src/tools/miri/src/shims/files.rs @@ -168,8 +168,9 @@ pub trait FileDescription: std::fmt::Debug + FileDescriptionExt { } /// Determines whether this FD non-deterministically has its reads and writes shortened. - fn nondet_short_accesses(&self) -> bool { - true + fn short_fd_operations(&self) -> bool { + // We only enable this for FD kinds where we think short accesses gain useful test coverage. + false } /// Seeks to the given offset (which can be relative to the beginning, end, or current position). @@ -395,6 +396,13 @@ impl FileDescription for FileHandle { communicate_allowed && self.file.is_terminal() } + fn short_fd_operations(&self) -> bool { + // While short accesses on file-backed FDs are very rare (at least for sufficiently small + // accesses), they can realistically happen when a signal interrupts the syscall. + // FIXME: we should return `false` if this is a named pipe... + true + } + fn as_unix<'tcx>(&self, ecx: &MiriInterpCx<'tcx>) -> &dyn UnixFileDescription { assert!( ecx.target_os_is_unix(), diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index a700644b95d..187423472ab 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -18,6 +18,7 @@ use rustc_target::callconv::FnAbi; use self::helpers::{ToHost, ToSoft}; use super::alloc::EvalContextExt as _; use super::backtrace::EvalContextExt as _; +use crate::helpers::EvalContextExt as _; use crate::*; /// Type of dynamic symbols (for `dlsym` et al) @@ -826,33 +827,36 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { => { let [f] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; let f = this.read_scalar(f)?.to_f32()?; - // Using host floats (but it's fine, these operations do not have guaranteed precision). - let f_host = f.to_host(); - let res = match link_name.as_str() { - "cbrtf" => f_host.cbrt(), - "coshf" => f_host.cosh(), - "sinhf" => f_host.sinh(), - "tanf" => f_host.tan(), - "tanhf" => f_host.tanh(), - "acosf" => f_host.acos(), - "asinf" => f_host.asin(), - "atanf" => f_host.atan(), - "log1pf" => f_host.ln_1p(), - "expm1f" => f_host.exp_m1(), - "tgammaf" => f_host.gamma(), - "erff" => f_host.erf(), - "erfcf" => f_host.erfc(), - _ => bug!(), - }; - let res = res.to_soft(); - // Apply a relative error of 16ULP to introduce some non-determinism - // simulating imprecise implementations and optimizations. - // FIXME: temporarily disabled as it breaks std tests. - // let res = math::apply_random_float_error_ulp( - // this, - // res, - // 4, // log2(16) - // ); + + let res = math::fixed_float_value(this, link_name.as_str(), &[f]).unwrap_or_else(|| { + // Using host floats (but it's fine, these operations do not have + // guaranteed precision). + let f_host = f.to_host(); + let res = match link_name.as_str() { + "cbrtf" => f_host.cbrt(), + "coshf" => f_host.cosh(), + "sinhf" => f_host.sinh(), + "tanf" => f_host.tan(), + "tanhf" => f_host.tanh(), + "acosf" => f_host.acos(), + "asinf" => f_host.asin(), + "atanf" => f_host.atan(), + "log1pf" => f_host.ln_1p(), + "expm1f" => f_host.exp_m1(), + "tgammaf" => f_host.gamma(), + "erff" => f_host.erf(), + "erfcf" => f_host.erfc(), + _ => bug!(), + }; + let res = res.to_soft(); + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + let res = math::apply_random_float_error_ulp(this, res, 4); + + // Clamp the result to the guaranteed range of this function according to the C standard, + // if any. + math::clamp_float_value(link_name.as_str(), res) + }); let res = this.adjust_nan(res, &[f]); this.write_scalar(res, dest)?; } @@ -865,24 +869,27 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { let [f1, f2] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; let f1 = this.read_scalar(f1)?.to_f32()?; let f2 = this.read_scalar(f2)?.to_f32()?; - // underscore case for windows, here and below - // (see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/floating-point-primitives?view=vs-2019) - // Using host floats (but it's fine, these operations do not have guaranteed precision). - let res = match link_name.as_str() { - "_hypotf" | "hypotf" => f1.to_host().hypot(f2.to_host()).to_soft(), - "atan2f" => f1.to_host().atan2(f2.to_host()).to_soft(), - #[allow(deprecated)] - "fdimf" => f1.to_host().abs_sub(f2.to_host()).to_soft(), - _ => bug!(), - }; - // Apply a relative error of 16ULP to introduce some non-determinism - // simulating imprecise implementations and optimizations. - // FIXME: temporarily disabled as it breaks std tests. - // let res = math::apply_random_float_error_ulp( - // this, - // res, - // 4, // log2(16) - // ); + + let res = math::fixed_float_value(this, link_name.as_str(), &[f1, f2]) + .unwrap_or_else(|| { + let res = match link_name.as_str() { + // underscore case for windows, here and below + // (see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/floating-point-primitives?view=vs-2019) + // Using host floats (but it's fine, these operations do not have guaranteed precision). + "_hypotf" | "hypotf" => f1.to_host().hypot(f2.to_host()).to_soft(), + "atan2f" => f1.to_host().atan2(f2.to_host()).to_soft(), + #[allow(deprecated)] + "fdimf" => f1.to_host().abs_sub(f2.to_host()).to_soft(), + _ => bug!(), + }; + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + let res = math::apply_random_float_error_ulp(this, res, 4); + + // Clamp the result to the guaranteed range of this function according to the C standard, + // if any. + math::clamp_float_value(link_name.as_str(), res) + }); let res = this.adjust_nan(res, &[f1, f2]); this.write_scalar(res, dest)?; } @@ -903,33 +910,36 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { => { let [f] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; let f = this.read_scalar(f)?.to_f64()?; - // Using host floats (but it's fine, these operations do not have guaranteed precision). - let f_host = f.to_host(); - let res = match link_name.as_str() { - "cbrt" => f_host.cbrt(), - "cosh" => f_host.cosh(), - "sinh" => f_host.sinh(), - "tan" => f_host.tan(), - "tanh" => f_host.tanh(), - "acos" => f_host.acos(), - "asin" => f_host.asin(), - "atan" => f_host.atan(), - "log1p" => f_host.ln_1p(), - "expm1" => f_host.exp_m1(), - "tgamma" => f_host.gamma(), - "erf" => f_host.erf(), - "erfc" => f_host.erfc(), - _ => bug!(), - }; - let res = res.to_soft(); - // Apply a relative error of 16ULP to introduce some non-determinism - // simulating imprecise implementations and optimizations. - // FIXME: temporarily disabled as it breaks std tests. - // let res = math::apply_random_float_error_ulp( - // this, - // res.to_soft(), - // 4, // log2(16) - // ); + + let res = math::fixed_float_value(this, link_name.as_str(), &[f]).unwrap_or_else(|| { + // Using host floats (but it's fine, these operations do not have + // guaranteed precision). + let f_host = f.to_host(); + let res = match link_name.as_str() { + "cbrt" => f_host.cbrt(), + "cosh" => f_host.cosh(), + "sinh" => f_host.sinh(), + "tan" => f_host.tan(), + "tanh" => f_host.tanh(), + "acos" => f_host.acos(), + "asin" => f_host.asin(), + "atan" => f_host.atan(), + "log1p" => f_host.ln_1p(), + "expm1" => f_host.exp_m1(), + "tgamma" => f_host.gamma(), + "erf" => f_host.erf(), + "erfc" => f_host.erfc(), + _ => bug!(), + }; + let res = res.to_soft(); + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + let res = math::apply_random_float_error_ulp(this, res, 4); + + // Clamp the result to the guaranteed range of this function according to the C standard, + // if any. + math::clamp_float_value(link_name.as_str(), res) + }); let res = this.adjust_nan(res, &[f]); this.write_scalar(res, dest)?; } @@ -942,24 +952,26 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { let [f1, f2] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; let f1 = this.read_scalar(f1)?.to_f64()?; let f2 = this.read_scalar(f2)?.to_f64()?; - // underscore case for windows, here and below - // (see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/floating-point-primitives?view=vs-2019) - // Using host floats (but it's fine, these operations do not have guaranteed precision). - let res = match link_name.as_str() { - "_hypot" | "hypot" => f1.to_host().hypot(f2.to_host()).to_soft(), - "atan2" => f1.to_host().atan2(f2.to_host()).to_soft(), - #[allow(deprecated)] - "fdim" => f1.to_host().abs_sub(f2.to_host()).to_soft(), - _ => bug!(), - }; - // Apply a relative error of 16ULP to introduce some non-determinism - // simulating imprecise implementations and optimizations. - // FIXME: temporarily disabled as it breaks std tests. - // let res = math::apply_random_float_error_ulp( - // this, - // res, - // 4, // log2(16) - // ); + + let res = math::fixed_float_value(this, link_name.as_str(), &[f1, f2]).unwrap_or_else(|| { + let res = match link_name.as_str() { + // underscore case for windows, here and below + // (see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/floating-point-primitives?view=vs-2019) + // Using host floats (but it's fine, these operations do not have guaranteed precision). + "_hypot" | "hypot" => f1.to_host().hypot(f2.to_host()).to_soft(), + "atan2" => f1.to_host().atan2(f2.to_host()).to_soft(), + #[allow(deprecated)] + "fdim" => f1.to_host().abs_sub(f2.to_host()).to_soft(), + _ => bug!(), + }; + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + let res = math::apply_random_float_error_ulp(this, res, 4); + + // Clamp the result to the guaranteed range of this function according to the C standard, + // if any. + math::clamp_float_value(link_name.as_str(), res) + }); let res = this.adjust_nan(res, &[f1, f2]); this.write_scalar(res, dest)?; } @@ -985,11 +997,14 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // Using host floats (but it's fine, these operations do not have guaranteed precision). let (res, sign) = x.to_host().ln_gamma(); this.write_int(sign, &signp)?; + let res = res.to_soft(); - // Apply a relative error of 16ULP to introduce some non-determinism + // Apply a relative error of 4ULP to introduce some non-determinism // simulating imprecise implementations and optimizations. - // FIXME: temporarily disabled as it breaks std tests. - // let res = math::apply_random_float_error_ulp(this, res, 4 /* log2(16) */); + let res = math::apply_random_float_error_ulp(this, res, 4); + // Clamp the result to the guaranteed range of this function according to the C standard, + // if any. + let res = math::clamp_float_value(link_name.as_str(), res); let res = this.adjust_nan(res, &[x]); this.write_scalar(res, dest)?; } @@ -1001,11 +1016,14 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // Using host floats (but it's fine, these operations do not have guaranteed precision). let (res, sign) = x.to_host().ln_gamma(); this.write_int(sign, &signp)?; + let res = res.to_soft(); - // Apply a relative error of 16ULP to introduce some non-determinism + // Apply a relative error of 4ULP to introduce some non-determinism // simulating imprecise implementations and optimizations. - // FIXME: temporarily disabled as it breaks std tests. - // let res = math::apply_random_float_error_ulp(this, res, 4 /* log2(16) */); + let res = math::apply_random_float_error_ulp(this, res, 4); + // Clamp the result to the guaranteed range of this function according to the C standard, + // if any. + let res = math::clamp_float_value(link_name.as_str(), res); let res = this.adjust_nan(res, &[x]); this.write_scalar(res, dest)?; } diff --git a/src/tools/miri/src/shims/native_lib/ffi.rs b/src/tools/miri/src/shims/native_lib/ffi.rs new file mode 100644 index 00000000000..0badf22bb76 --- /dev/null +++ b/src/tools/miri/src/shims/native_lib/ffi.rs @@ -0,0 +1,46 @@ +//! Support code for dealing with libffi. + +use libffi::low::CodePtr; +use libffi::middle::{Arg as ArgPtr, Cif, Type as FfiType}; + +/// Perform the actual FFI call. +/// +/// # Safety +/// +/// The safety invariants of the foreign function being called must be upheld (if any). +pub unsafe fn call<R: libffi::high::CType>(fun: CodePtr, args: &mut [OwnedArg]) -> R { + let arg_ptrs: Vec<_> = args.iter().map(|arg| arg.ptr()).collect(); + let cif = Cif::new(args.iter_mut().map(|arg| arg.ty.take().unwrap()), R::reify().into_middle()); + // SAFETY: Caller upholds that the function is safe to call, and since we + // were passed a slice reference we know the `OwnedArg`s won't have been + // dropped by this point. + unsafe { cif.call(fun, &arg_ptrs) } +} + +/// An argument for an FFI call. +#[derive(Debug, Clone)] +pub struct OwnedArg { + /// The type descriptor for this argument. + ty: Option<FfiType>, + /// Corresponding bytes for the value. + bytes: Box<[u8]>, +} + +impl OwnedArg { + /// Instantiates an argument from a type descriptor and bytes. + pub fn new(ty: FfiType, bytes: Box<[u8]>) -> Self { + Self { ty: Some(ty), bytes } + } + + /// Creates a libffi argument pointer pointing to this argument's bytes. + /// NB: Since `libffi::middle::Arg` ignores the lifetime of the reference + /// it's derived from, it is up to the caller to ensure the `OwnedArg` is + /// not dropped before unsafely calling `libffi::middle::Cif::call()`! + fn ptr(&self) -> ArgPtr { + // FIXME: Using `&self.bytes[0]` to reference the whole array is + // definitely unsound under SB, but we're waiting on + // https://github.com/libffi-rs/libffi-rs/commit/112a37b3b6ffb35bd75241fbcc580de40ba74a73 + // to land in a release so that we don't need to do this. + ArgPtr::new(&self.bytes[0]) + } +} diff --git a/src/tools/miri/src/shims/native_lib/mod.rs b/src/tools/miri/src/shims/native_lib/mod.rs index 74b9b704fea..da8f785e373 100644 --- a/src/tools/miri/src/shims/native_lib/mod.rs +++ b/src/tools/miri/src/shims/native_lib/mod.rs @@ -2,14 +2,15 @@ use std::ops::Deref; -use libffi::high::call as ffi; use libffi::low::CodePtr; -use rustc_abi::{BackendRepr, HasDataLayout, Size}; -use rustc_middle::mir::interpret::Pointer; -use rustc_middle::ty::{self as ty, IntTy, UintTy}; +use libffi::middle::Type as FfiType; +use rustc_abi::{HasDataLayout, Size}; +use rustc_middle::ty::{self as ty, IntTy, Ty, UintTy}; use rustc_span::Symbol; use serde::{Deserialize, Serialize}; +mod ffi; + #[cfg_attr( not(all( target_os = "linux", @@ -20,6 +21,7 @@ use serde::{Deserialize, Serialize}; )] pub mod trace; +use self::ffi::OwnedArg; use crate::*; /// The final results of an FFI trace, containing every relevant event detected @@ -70,12 +72,12 @@ impl AccessRange { impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {} trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { /// Call native host function and return the output as an immediate. - fn call_native_with_args<'a>( + fn call_native_with_args( &mut self, link_name: Symbol, dest: &MPlaceTy<'tcx>, - ptr: CodePtr, - libffi_args: Vec<libffi::high::Arg<'a>>, + fun: CodePtr, + libffi_args: &mut [OwnedArg], ) -> InterpResult<'tcx, (crate::ImmTy<'tcx>, Option<MemEvents>)> { let this = self.eval_context_mut(); #[cfg(target_os = "linux")] @@ -93,55 +95,55 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // Unsafe because of the call to native code. // Because this is calling a C function it is not necessarily sound, // but there is no way around this and we've checked as much as we can. - let x = unsafe { ffi::call::<i8>(ptr, libffi_args.as_slice()) }; + let x = unsafe { ffi::call::<i8>(fun, libffi_args) }; Scalar::from_i8(x) } ty::Int(IntTy::I16) => { - let x = unsafe { ffi::call::<i16>(ptr, libffi_args.as_slice()) }; + let x = unsafe { ffi::call::<i16>(fun, libffi_args) }; Scalar::from_i16(x) } ty::Int(IntTy::I32) => { - let x = unsafe { ffi::call::<i32>(ptr, libffi_args.as_slice()) }; + let x = unsafe { ffi::call::<i32>(fun, libffi_args) }; Scalar::from_i32(x) } ty::Int(IntTy::I64) => { - let x = unsafe { ffi::call::<i64>(ptr, libffi_args.as_slice()) }; + let x = unsafe { ffi::call::<i64>(fun, libffi_args) }; Scalar::from_i64(x) } ty::Int(IntTy::Isize) => { - let x = unsafe { ffi::call::<isize>(ptr, libffi_args.as_slice()) }; + let x = unsafe { ffi::call::<isize>(fun, libffi_args) }; Scalar::from_target_isize(x.try_into().unwrap(), this) } // uints ty::Uint(UintTy::U8) => { - let x = unsafe { ffi::call::<u8>(ptr, libffi_args.as_slice()) }; + let x = unsafe { ffi::call::<u8>(fun, libffi_args) }; Scalar::from_u8(x) } ty::Uint(UintTy::U16) => { - let x = unsafe { ffi::call::<u16>(ptr, libffi_args.as_slice()) }; + let x = unsafe { ffi::call::<u16>(fun, libffi_args) }; Scalar::from_u16(x) } ty::Uint(UintTy::U32) => { - let x = unsafe { ffi::call::<u32>(ptr, libffi_args.as_slice()) }; + let x = unsafe { ffi::call::<u32>(fun, libffi_args) }; Scalar::from_u32(x) } ty::Uint(UintTy::U64) => { - let x = unsafe { ffi::call::<u64>(ptr, libffi_args.as_slice()) }; + let x = unsafe { ffi::call::<u64>(fun, libffi_args) }; Scalar::from_u64(x) } ty::Uint(UintTy::Usize) => { - let x = unsafe { ffi::call::<usize>(ptr, libffi_args.as_slice()) }; + let x = unsafe { ffi::call::<usize>(fun, libffi_args) }; Scalar::from_target_usize(x.try_into().unwrap(), this) } // Functions with no declared return type (i.e., the default return) // have the output_type `Tuple([])`. ty::Tuple(t_list) if (*t_list).deref().is_empty() => { - unsafe { ffi::call::<()>(ptr, libffi_args.as_slice()) }; + unsafe { ffi::call::<()>(fun, libffi_args) }; return interp_ok(ImmTy::uninit(dest.layout)); } ty::RawPtr(..) => { - let x = unsafe { ffi::call::<*const ()>(ptr, libffi_args.as_slice()) }; - let ptr = Pointer::new(Provenance::Wildcard, Size::from_bytes(x.addr())); + let x = unsafe { ffi::call::<*const ()>(fun, libffi_args) }; + let ptr = StrictPointer::new(Provenance::Wildcard, Size::from_bytes(x.addr())); Scalar::from_pointer(ptr, this) } _ => @@ -267,6 +269,150 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { interp_ok(()) } + + /// Extract the value from the result of reading an operand from the machine + /// and convert it to a `OwnedArg`. + fn op_to_ffi_arg(&self, v: &OpTy<'tcx>, tracing: bool) -> InterpResult<'tcx, OwnedArg> { + let this = self.eval_context_ref(); + + // This should go first so that we emit unsupported before doing a bunch + // of extra work for types that aren't supported yet. + let ty = this.ty_to_ffitype(v.layout.ty)?; + + // Helper to print a warning when a pointer is shared with the native code. + let expose = |prov: Provenance| -> InterpResult<'tcx> { + // The first time this happens, print a warning. + if !this.machine.native_call_mem_warned.replace(true) { + // Newly set, so first time we get here. + this.emit_diagnostic(NonHaltingDiagnostic::NativeCallSharedMem { tracing }); + } + + this.expose_provenance(prov)?; + interp_ok(()) + }; + + // Compute the byte-level representation of the argument. If there's a pointer in there, we + // expose it inside the AM. Later in `visit_reachable_allocs`, the "meta"-level provenance + // for accessing the pointee gets exposed; this is crucial to justify the C code effectively + // casting the integer in `byte` to a pointer and using that. + let bytes = match v.as_mplace_or_imm() { + either::Either::Left(mplace) => { + // Get the alloc id corresponding to this mplace, alongside + // a pointer that's offset to point to this particular + // mplace (not one at the base addr of the allocation). + let sz = mplace.layout.size.bytes_usize(); + if sz == 0 { + throw_unsup_format!("attempting to pass a ZST over FFI"); + } + let (id, ofs, _) = this.ptr_get_alloc_id(mplace.ptr(), sz.try_into().unwrap())?; + let ofs = ofs.bytes_usize(); + let range = ofs..ofs.strict_add(sz); + // Expose all provenances in the allocation within the byte range of the struct, if + // any. These pointers are being directly passed to native code by-value. + let alloc = this.get_alloc_raw(id)?; + for prov in alloc.provenance().get_range(this, range.clone().into()) { + expose(prov)?; + } + // Read the bytes that make up this argument. We cannot use the normal getter as + // those would fail if any part of the argument is uninitialized. Native code + // is kind of outside the interpreter, after all... + Box::from(alloc.inspect_with_uninit_and_ptr_outside_interpreter(range)) + } + either::Either::Right(imm) => { + let mut bytes: Box<[u8]> = vec![0; imm.layout.size.bytes_usize()].into(); + + // A little helper to write scalars to our byte array. + let mut write_scalar = |this: &MiriInterpCx<'tcx>, sc: Scalar, pos: usize| { + // If a scalar is a pointer, then expose its provenance. + if let interpret::Scalar::Ptr(p, _) = sc { + expose(p.provenance)?; + } + write_target_uint( + this.data_layout().endian, + &mut bytes[pos..][..sc.size().bytes_usize()], + sc.to_scalar_int()?.to_bits_unchecked(), + ) + .unwrap(); + interp_ok(()) + }; + + // Write the scalar into the `bytes` buffer. + match *imm { + Immediate::Scalar(sc) => write_scalar(this, sc, 0)?, + Immediate::ScalarPair(sc_first, sc_second) => { + // The first scalar has an offset of zero; compute the offset of the 2nd. + let ofs_second = { + let rustc_abi::BackendRepr::ScalarPair(a, b) = imm.layout.backend_repr + else { + span_bug!( + this.cur_span(), + "op_to_ffi_arg: invalid scalar pair layout: {:#?}", + imm.layout + ) + }; + a.size(this).align_to(b.align(this).abi).bytes_usize() + }; + + write_scalar(this, sc_first, 0)?; + write_scalar(this, sc_second, ofs_second)?; + } + Immediate::Uninit => { + // Nothing to write. + } + } + + bytes + } + }; + interp_ok(OwnedArg::new(ty, bytes)) + } + + /// Parses an ADT to construct the matching libffi type. + fn adt_to_ffitype( + &self, + orig_ty: Ty<'_>, + adt_def: ty::AdtDef<'tcx>, + args: &'tcx ty::List<ty::GenericArg<'tcx>>, + ) -> InterpResult<'tcx, FfiType> { + // TODO: Certain non-C reprs should be okay also. + if !adt_def.repr().c() { + throw_unsup_format!("passing a non-#[repr(C)] struct over FFI: {orig_ty}") + } + // TODO: unions, etc. + if !adt_def.is_struct() { + throw_unsup_format!( + "unsupported argument type for native call: {orig_ty} is an enum or union" + ); + } + + let this = self.eval_context_ref(); + let mut fields = vec![]; + for field in &adt_def.non_enum_variant().fields { + fields.push(this.ty_to_ffitype(field.ty(*this.tcx, args))?); + } + + interp_ok(FfiType::structure(fields)) + } + + /// Gets the matching libffi type for a given Ty. + fn ty_to_ffitype(&self, ty: Ty<'tcx>) -> InterpResult<'tcx, FfiType> { + interp_ok(match ty.kind() { + ty::Int(IntTy::I8) => FfiType::i8(), + ty::Int(IntTy::I16) => FfiType::i16(), + ty::Int(IntTy::I32) => FfiType::i32(), + ty::Int(IntTy::I64) => FfiType::i64(), + ty::Int(IntTy::Isize) => FfiType::isize(), + // the uints + ty::Uint(UintTy::U8) => FfiType::u8(), + ty::Uint(UintTy::U16) => FfiType::u16(), + ty::Uint(UintTy::U32) => FfiType::u32(), + ty::Uint(UintTy::U64) => FfiType::u64(), + ty::Uint(UintTy::Usize) => FfiType::usize(), + ty::RawPtr(..) => FfiType::pointer(), + ty::Adt(adt_def, args) => self.adt_to_ffitype(ty, *adt_def, args)?, + _ => throw_unsup_format!("unsupported argument type for native call: {}", ty), + }) + } } impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} @@ -295,36 +441,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Do we have ptrace? let tracing = trace::Supervisor::is_enabled(); - // Get the function arguments, and convert them to `libffi`-compatible form. - let mut libffi_args = Vec::<CArg>::with_capacity(args.len()); + // Get the function arguments, copy them, and prepare the type descriptions. + let mut libffi_args = Vec::<OwnedArg>::with_capacity(args.len()); for arg in args.iter() { - if !matches!(arg.layout.backend_repr, BackendRepr::Scalar(_)) { - throw_unsup_format!("only scalar argument types are supported for native calls") - } - let imm = this.read_immediate(arg)?; - libffi_args.push(imm_to_carg(&imm, this)?); - // If we are passing a pointer, expose its provenance. Below, all exposed memory - // (previously exposed and new exposed) will then be properly prepared. - if matches!(arg.layout.ty.kind(), ty::RawPtr(..)) { - let ptr = imm.to_scalar().to_pointer(this)?; - let Some(prov) = ptr.provenance else { - // Pointer without provenance may not access any memory anyway, skip. - continue; - }; - // The first time this happens, print a warning. - if !this.machine.native_call_mem_warned.replace(true) { - // Newly set, so first time we get here. - this.emit_diagnostic(NonHaltingDiagnostic::NativeCallSharedMem { tracing }); - } - - this.expose_provenance(prov)?; - } + libffi_args.push(this.op_to_ffi_arg(arg, tracing)?); } - // Convert arguments to `libffi::high::Arg` type. - let libffi_args = libffi_args - .iter() - .map(|arg| arg.arg_downcast()) - .collect::<Vec<libffi::high::Arg<'_>>>(); // Prepare all exposed memory (both previously exposed, and just newly exposed since a // pointer was passed as argument). Uninitialised memory is left as-is, but any data @@ -343,8 +464,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { std::hint::black_box(alloc.get_bytes_unchecked_raw().expose_provenance()); if !tracing { - // Expose all provenances in this allocation, since the native code can do $whatever. - // Can be skipped when tracing; in that case we'll expose just the actually-read parts later. + // Expose all provenances in this allocation, since the native code can do + // $whatever. Can be skipped when tracing; in that case we'll expose just the + // actually-read parts later. for prov in alloc.provenance().provenances() { this.expose_provenance(prov)?; } @@ -354,7 +476,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if info.mutbl.is_mut() { let (alloc, cx) = this.get_alloc_raw_mut(alloc_id)?; // These writes could initialize everything and wreck havoc with the pointers. - // We can skip that when tracing; in that case we'll later do that only for the memory that got actually written. + // We can skip that when tracing; in that case we'll later do that only for the + // memory that got actually written. if !tracing { alloc.process_native_write(&cx.tcx, None); } @@ -367,7 +490,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Call the function and store output, depending on return type in the function signature. let (ret, maybe_memevents) = - this.call_native_with_args(link_name, dest, code_ptr, libffi_args)?; + this.call_native_with_args(link_name, dest, code_ptr, &mut libffi_args)?; if tracing { this.tracing_apply_accesses(maybe_memevents.unwrap())?; @@ -377,83 +500,3 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { interp_ok(true) } } - -#[derive(Debug, Clone)] -/// Enum of supported arguments to external C functions. -// We introduce this enum instead of just calling `ffi::arg` and storing a list -// of `libffi::high::Arg` directly, because the `libffi::high::Arg` just wraps a reference -// to the value it represents: https://docs.rs/libffi/latest/libffi/high/call/struct.Arg.html -// and we need to store a copy of the value, and pass a reference to this copy to C instead. -enum CArg { - /// 8-bit signed integer. - Int8(i8), - /// 16-bit signed integer. - Int16(i16), - /// 32-bit signed integer. - Int32(i32), - /// 64-bit signed integer. - Int64(i64), - /// isize. - ISize(isize), - /// 8-bit unsigned integer. - UInt8(u8), - /// 16-bit unsigned integer. - UInt16(u16), - /// 32-bit unsigned integer. - UInt32(u32), - /// 64-bit unsigned integer. - UInt64(u64), - /// usize. - USize(usize), - /// Raw pointer, stored as C's `void*`. - RawPtr(*mut std::ffi::c_void), -} - -impl<'a> CArg { - /// Convert a `CArg` to a `libffi` argument type. - fn arg_downcast(&'a self) -> libffi::high::Arg<'a> { - match self { - CArg::Int8(i) => ffi::arg(i), - CArg::Int16(i) => ffi::arg(i), - CArg::Int32(i) => ffi::arg(i), - CArg::Int64(i) => ffi::arg(i), - CArg::ISize(i) => ffi::arg(i), - CArg::UInt8(i) => ffi::arg(i), - CArg::UInt16(i) => ffi::arg(i), - CArg::UInt32(i) => ffi::arg(i), - CArg::UInt64(i) => ffi::arg(i), - CArg::USize(i) => ffi::arg(i), - CArg::RawPtr(i) => ffi::arg(i), - } - } -} - -/// Extract the scalar value from the result of reading a scalar from the machine, -/// and convert it to a `CArg`. -fn imm_to_carg<'tcx>(v: &ImmTy<'tcx>, cx: &impl HasDataLayout) -> InterpResult<'tcx, CArg> { - interp_ok(match v.layout.ty.kind() { - // If the primitive provided can be converted to a type matching the type pattern - // then create a `CArg` of this primitive value with the corresponding `CArg` constructor. - // the ints - ty::Int(IntTy::I8) => CArg::Int8(v.to_scalar().to_i8()?), - ty::Int(IntTy::I16) => CArg::Int16(v.to_scalar().to_i16()?), - ty::Int(IntTy::I32) => CArg::Int32(v.to_scalar().to_i32()?), - ty::Int(IntTy::I64) => CArg::Int64(v.to_scalar().to_i64()?), - ty::Int(IntTy::Isize) => - CArg::ISize(v.to_scalar().to_target_isize(cx)?.try_into().unwrap()), - // the uints - ty::Uint(UintTy::U8) => CArg::UInt8(v.to_scalar().to_u8()?), - ty::Uint(UintTy::U16) => CArg::UInt16(v.to_scalar().to_u16()?), - ty::Uint(UintTy::U32) => CArg::UInt32(v.to_scalar().to_u32()?), - ty::Uint(UintTy::U64) => CArg::UInt64(v.to_scalar().to_u64()?), - ty::Uint(UintTy::Usize) => - CArg::USize(v.to_scalar().to_target_usize(cx)?.try_into().unwrap()), - ty::RawPtr(..) => { - let s = v.to_scalar().to_pointer(cx)?.addr(); - // This relies on the `expose_provenance` in the `visit_reachable_allocs` callback - // above. - CArg::RawPtr(std::ptr::with_exposed_provenance_mut(s.bytes_usize())) - } - _ => throw_unsup_format!("unsupported argument type for native call: {}", v.layout.ty), - }) -} diff --git a/src/tools/miri/src/shims/native_lib/trace/child.rs b/src/tools/miri/src/shims/native_lib/trace/child.rs index b998ba822dd..95b0617a026 100644 --- a/src/tools/miri/src/shims/native_lib/trace/child.rs +++ b/src/tools/miri/src/shims/native_lib/trace/child.rs @@ -90,14 +90,6 @@ impl Supervisor { // Unwinding might be messed up due to partly protected memory, so let's abort if something // breaks inside here. let res = std::panic::abort_unwind(|| { - // SAFETY: We do not access machine memory past this point until the - // supervisor is ready to allow it. - // FIXME: this is sketchy, as technically the memory is still in the Rust Abstract Machine, - // and the compiler would be allowed to reorder accesses below this block... - unsafe { - Self::protect_pages(alloc.pages(), mman::ProtFlags::PROT_NONE).unwrap(); - } - // Send over the info. // NB: if we do not wait to receive a blank confirmation response, it is // possible that the supervisor is alerted of the SIGSTOP *before* it has @@ -110,16 +102,14 @@ impl Supervisor { // count. signal::raise(signal::SIGSTOP).unwrap(); - let res = f(); + // SAFETY: We have coordinated with the supervisor to ensure that this memory will keep + // working as normal, just with extra tracing. So even if the compiler moves memory + // accesses down to after the `mprotect`, they won't actually segfault. + unsafe { + Self::protect_pages(alloc.pages(), mman::ProtFlags::PROT_NONE).unwrap(); + } - // We can't use IPC channels here to signal that FFI mode has ended, - // since they might allocate memory which could get us stuck in a SIGTRAP - // with no easy way out! While this could be worked around, it is much - // simpler and more robust to simply use the signals which are left for - // arbitrary usage. Since this will block until we are continued by the - // supervisor, we can assume past this point that everything is back to - // normal. - signal::raise(signal::SIGUSR1).unwrap(); + let res = f(); // SAFETY: We set memory back to normal, so this is safe. unsafe { @@ -130,6 +120,12 @@ impl Supervisor { .unwrap(); } + // Signal the supervisor that we are done. Will block until the supervisor continues us. + // This will also shut down the segfault handler, so it's important that all memory is + // reset back to normal above. There must not be a window in time where accessing the + // pages we protected above actually causes the program to abort. + signal::raise(signal::SIGUSR1).unwrap(); + res }); diff --git a/src/tools/miri/src/shims/native_lib/trace/parent.rs b/src/tools/miri/src/shims/native_lib/trace/parent.rs index 83f6c7a13fc..3ae98259ab3 100644 --- a/src/tools/miri/src/shims/native_lib/trace/parent.rs +++ b/src/tools/miri/src/shims/native_lib/trace/parent.rs @@ -18,6 +18,11 @@ const ARCH_WORD_SIZE: usize = 4; #[cfg(target_arch = "x86_64")] const ARCH_WORD_SIZE: usize = 8; +// x86 max instruction length is 15 bytes: +// https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html +// See vol. 3B section 24.25. +const ARCH_MAX_INSTR_SIZE: usize = 15; + /// The address of the page set to be edited, initialised to a sentinel null /// pointer. static PAGE_ADDR: AtomicPtr<u8> = AtomicPtr::new(std::ptr::null_mut()); @@ -132,10 +137,10 @@ impl Iterator for ChildListener { return Some(ExecEvent::Syscall(pid)); }, // Child with the given pid was stopped by the given signal. - // It's somewhat dubious when this is returned instead of - // WaitStatus::Stopped, but for our purposes they are the - // same thing. - wait::WaitStatus::PtraceEvent(pid, signal, _) => + // It's somewhat unclear when which of these two is returned; + // we just treat them the same. + wait::WaitStatus::Stopped(pid, signal) + | wait::WaitStatus::PtraceEvent(pid, signal, _) => if self.attached { // This is our end-of-FFI signal! if signal == signal::SIGUSR1 { @@ -148,19 +153,6 @@ impl Iterator for ChildListener { // Just pass along the signal. ptrace::cont(pid, signal).unwrap(); }, - // Child was stopped at the given signal. Same logic as for - // WaitStatus::PtraceEvent. - wait::WaitStatus::Stopped(pid, signal) => - if self.attached { - if signal == signal::SIGUSR1 { - self.attached = false; - return Some(ExecEvent::End); - } else { - return Some(ExecEvent::Status(pid, signal)); - } - } else { - ptrace::cont(pid, signal).unwrap(); - }, _ => (), }, // This case should only trigger when all children died. @@ -250,7 +242,7 @@ pub fn sv_loop( // We can't trust simply calling `Pid::this()` in the child process to give the right // PID for us, so we get it this way. curr_pid = wait_for_signal(None, signal::SIGSTOP, InitialCont::No).unwrap(); - + // Continue until next syscall. ptrace::syscall(curr_pid, None).unwrap(); } // Child wants to end tracing. @@ -289,8 +281,7 @@ pub fn sv_loop( } } }, - // Child entered a syscall; we wait for exits inside of this, so it - // should never trigger on return from a syscall we care about. + // Child entered or exited a syscall. For now we ignore this and just continue. ExecEvent::Syscall(pid) => { ptrace::syscall(pid, None).unwrap(); } @@ -344,8 +335,8 @@ fn wait_for_signal( return Err(ExecEnd(Some(code))); } wait::WaitStatus::Signaled(_, _, _) => return Err(ExecEnd(None)), - wait::WaitStatus::Stopped(pid, signal) => (signal, pid), - wait::WaitStatus::PtraceEvent(pid, signal, _) => (signal, pid), + wait::WaitStatus::Stopped(pid, signal) + | wait::WaitStatus::PtraceEvent(pid, signal, _) => (signal, pid), // This covers PtraceSyscall and variants that are impossible with // the flags set (e.g. WaitStatus::StillAlive). _ => { @@ -486,7 +477,27 @@ fn handle_segfault( let stack_ptr = ch_stack.strict_add(CALLBACK_STACK_SIZE / 2); let regs_bak = ptrace::getregs(pid).unwrap(); let mut new_regs = regs_bak; - let ip_prestep = regs_bak.ip(); + + // Read at least one instruction from the ip. It's possible that the instruction + // that triggered the segfault was short and at the end of the mapped text area, + // so some of these reads may fail; in that case, just write empty bytes. If all + // reads failed, the disassembler will report an error. + let instr = (0..(ARCH_MAX_INSTR_SIZE.div_ceil(ARCH_WORD_SIZE))) + .flat_map(|ofs| { + // This reads one word of memory; we divided by `ARCH_WORD_SIZE` above to compensate for that. + ptrace::read( + pid, + std::ptr::without_provenance_mut( + regs_bak.ip().strict_add(ARCH_WORD_SIZE.strict_mul(ofs)), + ), + ) + .unwrap_or_default() + .to_ne_bytes() + }) + .collect::<Vec<_>>(); + + // Now figure out the size + type of access and log it down. + capstone_disassemble(&instr, addr, cs, acc_events).expect("Failed to disassemble instruction"); // Move the instr ptr into the deprotection code. #[expect(clippy::as_conversions)] @@ -526,33 +537,8 @@ fn handle_segfault( ptrace::write(pid, std::ptr::with_exposed_provenance_mut(a), 0).unwrap(); } - // Save registers and grab the bytes that were executed. This would - // be really nasty if it was a jump or similar but those thankfully - // won't do memory accesses and so can't trigger this! let regs_bak = ptrace::getregs(pid).unwrap(); new_regs = regs_bak; - let ip_poststep = regs_bak.ip(); - - // Ensure that we've actually gone forwards. - assert!(ip_poststep > ip_prestep); - // But not by too much. 64 bytes should be "big enough" on ~any architecture. - assert!(ip_prestep.strict_add(64) > ip_poststep); - - // We need to do reads/writes in word-sized chunks. - let diff = (ip_poststep.strict_sub(ip_prestep)).div_ceil(ARCH_WORD_SIZE); - let instr = (ip_prestep..ip_prestep.strict_add(diff)).fold(vec![], |mut ret, ip| { - // This only needs to be a valid pointer in the child process, not ours. - ret.append( - &mut ptrace::read(pid, std::ptr::without_provenance_mut(ip)) - .unwrap() - .to_ne_bytes() - .to_vec(), - ); - ret - }); - - // Now figure out the size + type of access and log it down. - capstone_disassemble(&instr, addr, cs, acc_events).expect("Failed to disassemble instruction"); // Reprotect everything and continue. #[expect(clippy::as_conversions)] diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs index e226a55d8b1..95e26ef5d5d 100644 --- a/src/tools/miri/src/shims/unix/fd.rs +++ b/src/tools/miri/src/shims/unix/fd.rs @@ -264,14 +264,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return this.set_last_error_and_return(LibcError("EBADF"), dest); }; + // Handle the zero-sized case. The man page says: + // > If count is zero, read() may detect the errors described below. In the absence of any + // > errors, or if read() does not check for errors, a read() with a count of 0 returns zero + // > and has no other effects. + if count == 0 { + this.write_null(dest)?; + return interp_ok(()); + } // Non-deterministically decide to further reduce the count, simulating a partial read (but - // never to 0, that has different behavior). - let count = - if fd.nondet_short_accesses() && count >= 2 && this.machine.rng.get_mut().random() { - count / 2 - } else { - count - }; + // never to 0, that would indicate EOF). + let count = if this.machine.short_fd_operations + && fd.short_fd_operations() + && count >= 2 + && this.machine.rng.get_mut().random() + { + count / 2 // since `count` is at least 2, the result is still at least 1 + } else { + count + }; trace!("read: FD mapped to {fd:?}"); // We want to read at most `count` bytes. We are sure that `count` is not negative @@ -338,14 +349,29 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return this.set_last_error_and_return(LibcError("EBADF"), dest); }; - // Non-deterministically decide to further reduce the count, simulating a partial write (but - // never to 0, that has different behavior). - let count = - if fd.nondet_short_accesses() && count >= 2 && this.machine.rng.get_mut().random() { - count / 2 - } else { - count - }; + // Handle the zero-sized case. The man page says: + // > If count is zero and fd refers to a regular file, then write() may return a failure + // > status if one of the errors below is detected. If no errors are detected, or error + // > detection is not performed, 0 is returned without causing any other effect. If count + // > is zero and fd refers to a file other than a regular file, the results are not + // > specified. + if count == 0 { + // For now let's not open the can of worms of what exactly "not specified" could mean... + this.write_null(dest)?; + return interp_ok(()); + } + // Non-deterministically decide to further reduce the count, simulating a partial write. + // We avoid reducing the write size to 0: the docs seem to be entirely fine with that, + // but the standard library is not (https://github.com/rust-lang/rust/issues/145959). + let count = if this.machine.short_fd_operations + && fd.short_fd_operations() + && count >= 2 + && this.machine.rng.get_mut().random() + { + count / 2 + } else { + count + }; let finish = { let dest = dest.clone(); diff --git a/src/tools/miri/src/shims/unix/linux_like/eventfd.rs b/src/tools/miri/src/shims/unix/linux_like/eventfd.rs index 2d35ef064db..ee7deb8d383 100644 --- a/src/tools/miri/src/shims/unix/linux_like/eventfd.rs +++ b/src/tools/miri/src/shims/unix/linux_like/eventfd.rs @@ -37,11 +37,6 @@ impl FileDescription for EventFd { "event" } - fn nondet_short_accesses(&self) -> bool { - // We always read and write exactly one `u64`. - false - } - fn close<'tcx>( self, _communicate_allowed: bool, diff --git a/src/tools/miri/src/shims/unix/unnamed_socket.rs b/src/tools/miri/src/shims/unix/unnamed_socket.rs index 817ddd7954d..7eb82851033 100644 --- a/src/tools/miri/src/shims/unix/unnamed_socket.rs +++ b/src/tools/miri/src/shims/unix/unnamed_socket.rs @@ -123,6 +123,14 @@ impl FileDescription for AnonSocket { anonsocket_write(self, ptr, len, ecx, finish) } + fn short_fd_operations(&self) -> bool { + // Pipes guarantee that sufficiently small accesses are not broken apart: + // <https://pubs.opengroup.org/onlinepubs/9799919799/functions/write.html#tag_17_699_08>. + // For now, we don't bother checking for the size, and just entirely disable + // short accesses on pipes. + matches!(self.fd_type, AnonSocketType::Socketpair) + } + fn as_unix<'tcx>(&self, _ecx: &MiriInterpCx<'tcx>) -> &dyn UnixFileDescription { self } diff --git a/src/tools/miri/src/shims/windows/handle.rs b/src/tools/miri/src/shims/windows/handle.rs index 8a965ea316d..92d6321bed1 100644 --- a/src/tools/miri/src/shims/windows/handle.rs +++ b/src/tools/miri/src/shims/windows/handle.rs @@ -289,9 +289,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } if this.ptr_is_null(target_handle_ptr)? { - throw_unsup_format!( - "`DuplicateHandle` `lpTargetHandle` parameter is null, which is unsupported" - ); + throw_machine_stop!(TerminationInfo::Abort( + "`DuplicateHandle` `lpTargetHandle` parameter must not be null, as otherwise the \ + newly created handle is leaked" + .to_string() + )); } if options != this.eval_windows("c", "DUPLICATE_SAME_ACCESS") { diff --git a/src/tools/miri/tests/deps/Cargo.lock b/src/tools/miri/tests/deps/Cargo.lock index 4b783ebdc4e..65ca4215c60 100644 --- a/src/tools/miri/tests/deps/Cargo.lock +++ b/src/tools/miri/tests/deps/Cargo.lock @@ -296,9 +296,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "socket2" diff --git a/src/tools/miri/tests/fail/alloc/alloc_error_handler.rs b/src/tools/miri/tests/fail/alloc/alloc_error_handler.rs index 4a87411d755..35b8e20e56b 100644 --- a/src/tools/miri/tests/fail/alloc/alloc_error_handler.rs +++ b/src/tools/miri/tests/fail/alloc/alloc_error_handler.rs @@ -1,6 +1,4 @@ //@error-in-other-file: aborted -//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()" -//@normalize-stderr-test: "\| +\^+" -> "| ^" #![feature(allocator_api)] use std::alloc::*; diff --git a/src/tools/miri/tests/fail/alloc/alloc_error_handler.stderr b/src/tools/miri/tests/fail/alloc/alloc_error_handler.stderr index d67dabd6935..bbf5d14a98a 100644 --- a/src/tools/miri/tests/fail/alloc/alloc_error_handler.stderr +++ b/src/tools/miri/tests/fail/alloc/alloc_error_handler.stderr @@ -1,13 +1,11 @@ memory allocation of 4 bytes failed error: abnormal termination: the program aborted execution - --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC + --> RUSTLIB/std/src/alloc.rs:LL:CC | -LL | ABORT() - | ^ abnormal termination occurred here +LL | crate::process::abort() + | ^^^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC - = note: inside `std::process::abort` at RUSTLIB/std/src/process.rs:LL:CC = note: inside `std::alloc::rust_oom` at RUSTLIB/std/src/alloc.rs:LL:CC = note: inside `std::alloc::_::__rg_oom` at RUSTLIB/std/src/alloc.rs:LL:CC = note: inside `std::alloc::handle_alloc_error::rt_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC @@ -16,7 +14,7 @@ note: inside `main` --> tests/fail/alloc/alloc_error_handler.rs:LL:CC | LL | handle_alloc_error(Layout::for_value(&0)); - | ^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr index a1571173a53..95f79aae6c1 100644 --- a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr +++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr @@ -9,13 +9,12 @@ panic in a function that cannot unwind stack backtrace: thread caused non-unwinding panic. aborting. error: abnormal termination: the program aborted execution - --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC + --> RUSTLIB/std/src/panicking.rs:LL:CC | -LL | ABORT() - | ^ abnormal termination occurred here +LL | crate::process::abort(); + | ^^^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC = note: inside `std::panicking::panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside `std::sys::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys/backtrace.rs:LL:CC @@ -33,7 +32,7 @@ note: inside `main` --> tests/fail/function_calls/exported_symbol_bad_unwind2.rs:LL:CC | LL | unsafe { nounwind() } - | ^ + | ^^^^^^^^^^ note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr index a1571173a53..95f79aae6c1 100644 --- a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr +++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr @@ -9,13 +9,12 @@ panic in a function that cannot unwind stack backtrace: thread caused non-unwinding panic. aborting. error: abnormal termination: the program aborted execution - --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC + --> RUSTLIB/std/src/panicking.rs:LL:CC | -LL | ABORT() - | ^ abnormal termination occurred here +LL | crate::process::abort(); + | ^^^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC = note: inside `std::panicking::panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside `std::sys::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys/backtrace.rs:LL:CC @@ -33,7 +32,7 @@ note: inside `main` --> tests/fail/function_calls/exported_symbol_bad_unwind2.rs:LL:CC | LL | unsafe { nounwind() } - | ^ + | ^^^^^^^^^^ note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.extern_block.stderr b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.extern_block.stderr index e755b262474..eaca0d3f012 100644 --- a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.extern_block.stderr +++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.extern_block.stderr @@ -7,7 +7,7 @@ error: Undefined Behavior: unwinding past a stack frame that does not allow unwi --> tests/fail/function_calls/exported_symbol_bad_unwind2.rs:LL:CC | LL | unsafe { nounwind() } - | ^ Undefined Behavior occurred here + | ^^^^^^^^^^ Undefined Behavior occurred here | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.rs b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.rs index 9d993786d57..aa5a185f110 100644 --- a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.rs +++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.rs @@ -1,6 +1,4 @@ //@revisions: extern_block definition both -//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()" -//@normalize-stderr-test: "\| +\^+" -> "| ^" //@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" //@normalize-stderr-test: "\n +at [^\n]+" -> "" //@[definition,both]error-in-other-file: aborted execution diff --git a/src/tools/miri/tests/fail/intrinsics/funnel_shl.rs b/src/tools/miri/tests/fail/intrinsics/funnel_shl.rs new file mode 100644 index 00000000000..40b61f65ebe --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/funnel_shl.rs @@ -0,0 +1,7 @@ +#![feature(core_intrinsics, funnel_shifts)] + +fn main() { + unsafe { + std::intrinsics::unchecked_funnel_shl(1_u32, 2, 32); //~ ERROR: Undefined Behavior + } +} diff --git a/src/tools/miri/tests/fail/intrinsics/funnel_shl.stderr b/src/tools/miri/tests/fail/intrinsics/funnel_shl.stderr new file mode 100644 index 00000000000..fc828021b13 --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/funnel_shl.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: `assume` called with `false` + --> tests/fail/intrinsics/funnel_shl.rs:LL:CC + | +LL | std::intrinsics::unchecked_funnel_shl(1_u32, 2, 32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at tests/fail/intrinsics/funnel_shl.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/intrinsics/funnel_shr.rs b/src/tools/miri/tests/fail/intrinsics/funnel_shr.rs new file mode 100644 index 00000000000..95822656397 --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/funnel_shr.rs @@ -0,0 +1,7 @@ +#![feature(core_intrinsics, funnel_shifts)] + +fn main() { + unsafe { + std::intrinsics::unchecked_funnel_shr(1_u32, 2, 32); //~ ERROR: Undefined Behavior + } +} diff --git a/src/tools/miri/tests/fail/intrinsics/funnel_shr.stderr b/src/tools/miri/tests/fail/intrinsics/funnel_shr.stderr new file mode 100644 index 00000000000..7361df1b4c7 --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/funnel_shr.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: `assume` called with `false` + --> tests/fail/intrinsics/funnel_shr.rs:LL:CC + | +LL | std::intrinsics::unchecked_funnel_shr(1_u32, 2, 32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at tests/fail/intrinsics/funnel_shr.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/panic/abort_unwind.rs b/src/tools/miri/tests/fail/panic/abort_unwind.rs index bd819362da4..775cbf62229 100644 --- a/src/tools/miri/tests/fail/panic/abort_unwind.rs +++ b/src/tools/miri/tests/fail/panic/abort_unwind.rs @@ -1,6 +1,4 @@ //@error-in-other-file: the program aborted execution -//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()" -//@normalize-stderr-test: "\| +\^+" -> "| ^" //@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" //@normalize-stderr-test: "\n +at [^\n]+" -> "" diff --git a/src/tools/miri/tests/fail/panic/abort_unwind.stderr b/src/tools/miri/tests/fail/panic/abort_unwind.stderr index 287f7d5432a..23dbc2fb8f3 100644 --- a/src/tools/miri/tests/fail/panic/abort_unwind.stderr +++ b/src/tools/miri/tests/fail/panic/abort_unwind.stderr @@ -9,13 +9,12 @@ panic in a function that cannot unwind stack backtrace: thread caused non-unwinding panic. aborting. error: abnormal termination: the program aborted execution - --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC + --> RUSTLIB/std/src/panicking.rs:LL:CC | -LL | ABORT() - | ^ abnormal termination occurred here +LL | crate::process::abort(); + | ^^^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC = note: inside `std::panicking::panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside `std::sys::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys/backtrace.rs:LL:CC @@ -27,7 +26,7 @@ note: inside `main` --> tests/fail/panic/abort_unwind.rs:LL:CC | LL | std::panic::abort_unwind(|| panic!("PANIC!!!")); - | ^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/panic/double_panic.rs b/src/tools/miri/tests/fail/panic/double_panic.rs index 4d8f4cb6fb7..88421e84529 100644 --- a/src/tools/miri/tests/fail/panic/double_panic.rs +++ b/src/tools/miri/tests/fail/panic/double_panic.rs @@ -1,5 +1,3 @@ -//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()" -//@normalize-stderr-test: "\| +\^+" -> "| ^" //@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" //@normalize-stderr-test: "\n +at [^\n]+" -> "" //@error-in-other-file: aborted execution diff --git a/src/tools/miri/tests/fail/panic/double_panic.stderr b/src/tools/miri/tests/fail/panic/double_panic.stderr index b76ece7f1e5..edbc0d8fc57 100644 --- a/src/tools/miri/tests/fail/panic/double_panic.stderr +++ b/src/tools/miri/tests/fail/panic/double_panic.stderr @@ -12,13 +12,12 @@ thread 'main' ($TID) panicked at RUSTLIB/core/src/panicking.rs:LL:CC: panic in a destructor during cleanup thread caused non-unwinding panic. aborting. error: abnormal termination: the program aborted execution - --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC + --> RUSTLIB/std/src/panicking.rs:LL:CC | -LL | ABORT() - | ^ abnormal termination occurred here +LL | crate::process::abort(); + | ^^^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC = note: inside `std::panicking::panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside `std::sys::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys/backtrace.rs:LL:CC diff --git a/src/tools/miri/tests/fail/panic/panic_abort1.rs b/src/tools/miri/tests/fail/panic/panic_abort1.rs index 06cb673778a..511b8bddf97 100644 --- a/src/tools/miri/tests/fail/panic/panic_abort1.rs +++ b/src/tools/miri/tests/fail/panic/panic_abort1.rs @@ -1,6 +1,4 @@ //@error-in-other-file: the program aborted execution -//@normalize-stderr-test: "\| +\^+" -> "| ^" -//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()" //@compile-flags: -C panic=abort fn main() { diff --git a/src/tools/miri/tests/fail/panic/panic_abort1.stderr b/src/tools/miri/tests/fail/panic/panic_abort1.stderr index c469d24287f..c389a9bc075 100644 --- a/src/tools/miri/tests/fail/panic/panic_abort1.stderr +++ b/src/tools/miri/tests/fail/panic/panic_abort1.stderr @@ -4,14 +4,12 @@ panicking from libstd note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect error: abnormal termination: the program aborted execution - --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC + --> RUSTLIB/std/src/rt.rs:LL:CC | -LL | ABORT() - | ^ abnormal termination occurred here +LL | crate::process::abort(); + | ^^^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC - = note: inside `std::process::abort` at RUSTLIB/std/src/process.rs:LL:CC = note: inside `std::rt::__rust_abort` at RUSTLIB/std/src/rt.rs:LL:CC = note: inside `panic_abort::__rust_start_panic` at RUSTLIB/panic_abort/src/lib.rs:LL:CC = note: inside `std::panicking::rust_panic` at RUSTLIB/std/src/panicking.rs:LL:CC @@ -23,7 +21,7 @@ note: inside `main` --> tests/fail/panic/panic_abort1.rs:LL:CC | LL | std::panic!("panicking from libstd"); - | ^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/panic/panic_abort2.rs b/src/tools/miri/tests/fail/panic/panic_abort2.rs index c011b3ee7eb..e6c1a130ac9 100644 --- a/src/tools/miri/tests/fail/panic/panic_abort2.rs +++ b/src/tools/miri/tests/fail/panic/panic_abort2.rs @@ -1,6 +1,4 @@ //@error-in-other-file: the program aborted execution -//@normalize-stderr-test: "\| +\^+" -> "| ^" -//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()" //@compile-flags: -C panic=abort fn main() { diff --git a/src/tools/miri/tests/fail/panic/panic_abort2.stderr b/src/tools/miri/tests/fail/panic/panic_abort2.stderr index bc7918f5f86..5fe2245cbe0 100644 --- a/src/tools/miri/tests/fail/panic/panic_abort2.stderr +++ b/src/tools/miri/tests/fail/panic/panic_abort2.stderr @@ -4,14 +4,12 @@ thread 'main' ($TID) panicked at tests/fail/panic/panic_abort2.rs:LL:CC: note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect error: abnormal termination: the program aborted execution - --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC + --> RUSTLIB/std/src/rt.rs:LL:CC | -LL | ABORT() - | ^ abnormal termination occurred here +LL | crate::process::abort(); + | ^^^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC - = note: inside `std::process::abort` at RUSTLIB/std/src/process.rs:LL:CC = note: inside `std::rt::__rust_abort` at RUSTLIB/std/src/rt.rs:LL:CC = note: inside `panic_abort::__rust_start_panic` at RUSTLIB/panic_abort/src/lib.rs:LL:CC = note: inside `std::panicking::rust_panic` at RUSTLIB/std/src/panicking.rs:LL:CC @@ -23,7 +21,7 @@ note: inside `main` --> tests/fail/panic/panic_abort2.rs:LL:CC | LL | std::panic!("{}-panicking from libstd", 42); - | ^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/panic/panic_abort3.rs b/src/tools/miri/tests/fail/panic/panic_abort3.rs index 911dc4a44ab..28a28c923fd 100644 --- a/src/tools/miri/tests/fail/panic/panic_abort3.rs +++ b/src/tools/miri/tests/fail/panic/panic_abort3.rs @@ -1,6 +1,4 @@ //@error-in-other-file: the program aborted execution -//@normalize-stderr-test: "\| +\^+" -> "| ^" -//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()" //@compile-flags: -C panic=abort fn main() { diff --git a/src/tools/miri/tests/fail/panic/panic_abort3.stderr b/src/tools/miri/tests/fail/panic/panic_abort3.stderr index 553bfa61635..cac24ca41c7 100644 --- a/src/tools/miri/tests/fail/panic/panic_abort3.stderr +++ b/src/tools/miri/tests/fail/panic/panic_abort3.stderr @@ -4,14 +4,12 @@ panicking from libcore note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect error: abnormal termination: the program aborted execution - --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC + --> RUSTLIB/std/src/rt.rs:LL:CC | -LL | ABORT() - | ^ abnormal termination occurred here +LL | crate::process::abort(); + | ^^^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC - = note: inside `std::process::abort` at RUSTLIB/std/src/process.rs:LL:CC = note: inside `std::rt::__rust_abort` at RUSTLIB/std/src/rt.rs:LL:CC = note: inside `panic_abort::__rust_start_panic` at RUSTLIB/panic_abort/src/lib.rs:LL:CC = note: inside `std::panicking::rust_panic` at RUSTLIB/std/src/panicking.rs:LL:CC @@ -23,7 +21,7 @@ note: inside `main` --> tests/fail/panic/panic_abort3.rs:LL:CC | LL | core::panic!("panicking from libcore"); - | ^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/panic/panic_abort4.rs b/src/tools/miri/tests/fail/panic/panic_abort4.rs index 696fdff7422..248064d14d5 100644 --- a/src/tools/miri/tests/fail/panic/panic_abort4.rs +++ b/src/tools/miri/tests/fail/panic/panic_abort4.rs @@ -1,6 +1,4 @@ //@error-in-other-file: the program aborted execution -//@normalize-stderr-test: "\| +\^+" -> "| ^" -//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()" //@compile-flags: -C panic=abort fn main() { diff --git a/src/tools/miri/tests/fail/panic/panic_abort4.stderr b/src/tools/miri/tests/fail/panic/panic_abort4.stderr index 07ecab6661e..21195729ae8 100644 --- a/src/tools/miri/tests/fail/panic/panic_abort4.stderr +++ b/src/tools/miri/tests/fail/panic/panic_abort4.stderr @@ -4,14 +4,12 @@ thread 'main' ($TID) panicked at tests/fail/panic/panic_abort4.rs:LL:CC: note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect error: abnormal termination: the program aborted execution - --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC + --> RUSTLIB/std/src/rt.rs:LL:CC | -LL | ABORT() - | ^ abnormal termination occurred here +LL | crate::process::abort(); + | ^^^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC - = note: inside `std::process::abort` at RUSTLIB/std/src/process.rs:LL:CC = note: inside `std::rt::__rust_abort` at RUSTLIB/std/src/rt.rs:LL:CC = note: inside `panic_abort::__rust_start_panic` at RUSTLIB/panic_abort/src/lib.rs:LL:CC = note: inside `std::panicking::rust_panic` at RUSTLIB/std/src/panicking.rs:LL:CC @@ -23,7 +21,7 @@ note: inside `main` --> tests/fail/panic/panic_abort4.rs:LL:CC | LL | core::panic!("{}-panicking from libcore", 42); - | ^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.rs b/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.rs index 6119e8604b4..0f7cf189f33 100644 --- a/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.rs +++ b/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.rs @@ -1,7 +1,5 @@ //! This is a regression test for <https://github.com/rust-lang/miri/issues/4188>: The precondition //! check in `ptr::swap_nonoverlapping` was incorrectly disabled in Miri. -//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()" -//@normalize-stderr-test: "\| +\^+" -> "| ^" //@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" //@normalize-stderr-test: "\n +at [^\n]+" -> "" //@error-in-other-file: aborted execution diff --git a/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.stderr b/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.stderr index 70f5d498da3..c5f6e62b869 100644 --- a/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.stderr +++ b/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.stderr @@ -7,13 +7,12 @@ note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect thread caused non-unwinding panic. aborting. error: abnormal termination: the program aborted execution - --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC + --> RUSTLIB/std/src/panicking.rs:LL:CC | -LL | ABORT() - | ^ abnormal termination occurred here +LL | crate::process::abort(); + | ^^^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC = note: inside `std::panicking::panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside `std::sys::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys/backtrace.rs:LL:CC @@ -22,7 +21,7 @@ note: inside `main` --> tests/fail/ptr_swap_nonoverlapping.rs:LL:CC | LL | std::ptr::swap_nonoverlapping(ptr, ptr, 1); - | ^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/terminate-terminator.rs b/src/tools/miri/tests/fail/terminate-terminator.rs index 31ae829a2de..421cddca160 100644 --- a/src/tools/miri/tests/fail/terminate-terminator.rs +++ b/src/tools/miri/tests/fail/terminate-terminator.rs @@ -1,6 +1,4 @@ //@compile-flags: -Zmir-opt-level=3 -Zinline-mir-hint-threshold=1000 -//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()" -//@normalize-stderr-test: "\| +\^+" -> "| ^" //@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" //@normalize-stderr-test: "\n +at [^\n]+" -> "" //@error-in-other-file: aborted execution diff --git a/src/tools/miri/tests/fail/terminate-terminator.stderr b/src/tools/miri/tests/fail/terminate-terminator.stderr index 228e2d3de1f..8ae649a392b 100644 --- a/src/tools/miri/tests/fail/terminate-terminator.stderr +++ b/src/tools/miri/tests/fail/terminate-terminator.stderr @@ -11,13 +11,12 @@ panic in a function that cannot unwind stack backtrace: thread caused non-unwinding panic. aborting. error: abnormal termination: the program aborted execution - --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC + --> RUSTLIB/std/src/panicking.rs:LL:CC | -LL | ABORT() - | ^ abnormal termination occurred here +LL | crate::process::abort(); + | ^^^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC = note: inside `std::panicking::panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside `std::sys::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys/backtrace.rs:LL:CC @@ -36,12 +35,12 @@ note: inside `panic_abort` --> tests/fail/terminate-terminator.rs:LL:CC | LL | has_cleanup(); - | ^ + | ^^^^^^^^^^^^^ note: inside `main` --> tests/fail/terminate-terminator.rs:LL:CC | LL | panic_abort(); - | ^ + | ^^^^^^^^^^^^^ note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/tree_borrows/frozen-lazy-write-to-surrounding.rs b/src/tools/miri/tests/fail/tree_borrows/frozen-lazy-write-to-surrounding.rs new file mode 100644 index 00000000000..7d51050f32b --- /dev/null +++ b/src/tools/miri/tests/fail/tree_borrows/frozen-lazy-write-to-surrounding.rs @@ -0,0 +1,9 @@ +//@compile-flags: -Zmiri-tree-borrows + +fn main() { + // Since the "inside" part is `!Freeze`, the permission to mutate is gone. + let pair = ((), 1); + let x = &pair.0; + let ptr = (&raw const *x).cast::<i32>().cast_mut(); + unsafe { ptr.write(0) }; //~ERROR: /write access .* forbidden/ +} diff --git a/src/tools/miri/tests/fail/tree_borrows/frozen-lazy-write-to-surrounding.stderr b/src/tools/miri/tests/fail/tree_borrows/frozen-lazy-write-to-surrounding.stderr new file mode 100644 index 00000000000..e9800468c57 --- /dev/null +++ b/src/tools/miri/tests/fail/tree_borrows/frozen-lazy-write-to-surrounding.stderr @@ -0,0 +1,21 @@ +error: Undefined Behavior: write access through <TAG> at ALLOC[0x0] is forbidden + --> tests/fail/tree_borrows/frozen-lazy-write-to-surrounding.rs:LL:CC + | +LL | unsafe { ptr.write(0) }; + | ^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information + = help: the accessed tag <TAG> has state Frozen which forbids this child write access +help: the accessed tag <TAG> was created here, in the initial state Frozen + --> tests/fail/tree_borrows/frozen-lazy-write-to-surrounding.rs:LL:CC + | +LL | let x = &pair.0; + | ^^^^^^^ + = note: BACKTRACE (of the first span): + = note: inside `main` at tests/fail/tree_borrows/frozen-lazy-write-to-surrounding.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/unwind-action-terminate.rs b/src/tools/miri/tests/fail/unwind-action-terminate.rs index f0fbcfd8867..e1c8909fffa 100644 --- a/src/tools/miri/tests/fail/unwind-action-terminate.rs +++ b/src/tools/miri/tests/fail/unwind-action-terminate.rs @@ -1,6 +1,4 @@ //@error-in-other-file: aborted execution -//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()" -//@normalize-stderr-test: "\| +\^+" -> "| ^" //@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" //@normalize-stderr-test: "\n +at [^\n]+" -> "" extern "C" fn panic_abort() { diff --git a/src/tools/miri/tests/fail/unwind-action-terminate.stderr b/src/tools/miri/tests/fail/unwind-action-terminate.stderr index 1c0f8cb7ec6..cf41c88ce37 100644 --- a/src/tools/miri/tests/fail/unwind-action-terminate.stderr +++ b/src/tools/miri/tests/fail/unwind-action-terminate.stderr @@ -9,13 +9,12 @@ panic in a function that cannot unwind stack backtrace: thread caused non-unwinding panic. aborting. error: abnormal termination: the program aborted execution - --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC + --> RUSTLIB/std/src/panicking.rs:LL:CC | -LL | ABORT() - | ^ abnormal termination occurred here +LL | crate::process::abort(); + | ^^^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC = note: inside `std::panicking::panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside `std::sys::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys/backtrace.rs:LL:CC @@ -33,7 +32,7 @@ note: inside `main` --> tests/fail/unwind-action-terminate.rs:LL:CC | LL | panic_abort(); - | ^ + | ^^^^^^^^^^^^^ note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/native-lib/aggregate_arguments.c b/src/tools/miri/tests/native-lib/aggregate_arguments.c new file mode 100644 index 00000000000..8ad687f2aec --- /dev/null +++ b/src/tools/miri/tests/native-lib/aggregate_arguments.c @@ -0,0 +1,52 @@ +#include <stdint.h> + +// See comments in build_native_lib() +#define EXPORT __attribute__((visibility("default"))) + +/* Test: fail/pass_struct_expose_only_range */ + +typedef struct HasPointer { + uint8_t *ptr; +} HasPointer; + +EXPORT uint8_t access_struct_ptr(const HasPointer s) { + return *s.ptr; +} + +/* Test: test_pass_struct */ + +typedef struct PassMe { + int32_t value; + int64_t other_value; +} PassMe; + +EXPORT int64_t pass_struct(const PassMe pass_me) { + return pass_me.value + pass_me.other_value; +} + +/* Test: test_pass_struct_complex */ + +typedef struct Part1 { + uint16_t high; + uint16_t low; +} Part1; + +typedef struct Part2 { + uint32_t bits; +} Part2; + +typedef struct ComplexStruct { + Part1 part_1; + Part2 part_2; + uint32_t part_3; +} ComplexStruct; + +EXPORT int32_t pass_struct_complex(const ComplexStruct complex, uint16_t high, uint16_t low, uint32_t bits) { + if (complex.part_1.high == high && complex.part_1.low == low + && complex.part_2.bits == bits + && complex.part_3 == bits) + return 0; + else { + return 1; + } +} diff --git a/src/tools/miri/tests/native-lib/fail/pass_struct_expose_only_range.rs b/src/tools/miri/tests/native-lib/fail/pass_struct_expose_only_range.rs new file mode 100644 index 00000000000..a2b43031a29 --- /dev/null +++ b/src/tools/miri/tests/native-lib/fail/pass_struct_expose_only_range.rs @@ -0,0 +1,23 @@ +//@compile-flags: -Zmiri-permissive-provenance + +#[repr(C)] +#[derive(Copy, Clone)] +struct HasPointer { + ptr: *const u8, +} + +extern "C" { + fn access_struct_ptr(s: HasPointer) -> u8; +} + +fn main() { + let structs = vec![HasPointer { ptr: &0 }, HasPointer { ptr: &1 }]; + unsafe { + let r = access_struct_ptr(structs[1]); + assert_eq!(r, 1); + // There are two pointers stored in the allocation backing `structs`; ensure + // we only exposed the one that was actually passed to C. + let _val = *std::ptr::with_exposed_provenance::<u8>(structs[1].ptr.addr()); // fine, ptr got sent to C + let _val = *std::ptr::with_exposed_provenance::<u8>(structs[0].ptr.addr()); //~ ERROR: memory access failed + }; +} diff --git a/src/tools/miri/tests/native-lib/fail/pass_struct_expose_only_range.stderr b/src/tools/miri/tests/native-lib/fail/pass_struct_expose_only_range.stderr new file mode 100644 index 00000000000..a8f85001c23 --- /dev/null +++ b/src/tools/miri/tests/native-lib/fail/pass_struct_expose_only_range.stderr @@ -0,0 +1,28 @@ +warning: sharing memory with a native function called via FFI + --> tests/native-lib/fail/pass_struct_expose_only_range.rs:LL:CC + | +LL | let r = access_struct_ptr(structs[1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ sharing memory with a native function + | + = help: when memory is shared with a native function call, Miri stops tracking initialization and provenance for that memory + = help: in particular, Miri assumes that the native call initializes all memory it has access to + = help: Miri also assumes that any part of this memory may be a pointer that is permitted to point to arbitrary exposed memory + = help: what this means is that Miri will easily miss Undefined Behavior related to incorrect usage of this shared memory, so you should not take a clean Miri run as a signal that your FFI code is UB-free + = note: BACKTRACE: + = note: inside `main` at tests/native-lib/fail/pass_struct_expose_only_range.rs:LL:CC + +error: Undefined Behavior: memory access failed: attempting to access 1 byte, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) + --> tests/native-lib/fail/pass_struct_expose_only_range.rs:LL:CC + | +LL | let _val = *std::ptr::with_exposed_provenance::<u8>(structs[0].ptr.addr()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at tests/native-lib/fail/pass_struct_expose_only_range.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error; 1 warning emitted + diff --git a/src/tools/miri/tests/native-lib/fail/struct_not_extern_c.rs b/src/tools/miri/tests/native-lib/fail/struct_not_extern_c.rs new file mode 100644 index 00000000000..cf8315e0fd9 --- /dev/null +++ b/src/tools/miri/tests/native-lib/fail/struct_not_extern_c.rs @@ -0,0 +1,19 @@ +// Only works on Unix targets +//@ignore-target: windows wasm +//@only-on-host + +#![allow(improper_ctypes)] + +pub struct PassMe { + pub value: i32, + pub other_value: i64, +} + +extern "C" { + fn pass_struct(s: PassMe) -> i64; +} + +fn main() { + let pass_me = PassMe { value: 42, other_value: 1337 }; + unsafe { pass_struct(pass_me) }; //~ ERROR: unsupported operation: passing a non-#[repr(C)] struct over FFI +} diff --git a/src/tools/miri/tests/native-lib/fail/struct_not_extern_c.stderr b/src/tools/miri/tests/native-lib/fail/struct_not_extern_c.stderr new file mode 100644 index 00000000000..90e59a31da4 --- /dev/null +++ b/src/tools/miri/tests/native-lib/fail/struct_not_extern_c.stderr @@ -0,0 +1,14 @@ +error: unsupported operation: passing a non-#[repr(C)] struct over FFI: PassMe + --> tests/native-lib/fail/struct_not_extern_c.rs:LL:CC + | +LL | unsafe { pass_struct(pass_me) }; + | ^^^^^^^^^^^^^^^^^^^^ unsupported operation occurred here + | + = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support + = note: BACKTRACE: + = note: inside `main` at tests/native-lib/fail/struct_not_extern_c.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/native-lib/fail/uninit_struct.rs b/src/tools/miri/tests/native-lib/fail/uninit_struct.rs new file mode 100644 index 00000000000..cf61c7f3915 --- /dev/null +++ b/src/tools/miri/tests/native-lib/fail/uninit_struct.rs @@ -0,0 +1,27 @@ +#[repr(C)] +#[derive(Copy, Clone)] +struct ComplexStruct { + part_1: Part1, + part_2: Part2, + part_3: u32, +} +#[repr(C)] +#[derive(Copy, Clone)] +struct Part1 { + high: u16, + low: u16, +} +#[repr(C)] +#[derive(Copy, Clone)] +struct Part2 { + bits: u32, +} + +extern "C" { + fn pass_struct_complex(s: ComplexStruct, high: u16, low: u16, bits: u32) -> i32; +} + +fn main() { + let arg = std::mem::MaybeUninit::<ComplexStruct>::uninit(); + unsafe { pass_struct_complex(*arg.as_ptr(), 0, 0, 0) }; //~ ERROR: Undefined Behavior: constructing invalid value +} diff --git a/src/tools/miri/tests/native-lib/fail/uninit_struct.stderr b/src/tools/miri/tests/native-lib/fail/uninit_struct.stderr new file mode 100644 index 00000000000..0fe6ad9c77b --- /dev/null +++ b/src/tools/miri/tests/native-lib/fail/uninit_struct.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: constructing invalid value at .part_1.high: encountered uninitialized memory, but expected an integer + --> tests/native-lib/fail/uninit_struct.rs:LL:CC + | +LL | unsafe { pass_struct_complex(*arg.as_ptr(), 0, 0, 0) }; + | ^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at tests/native-lib/fail/uninit_struct.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/native-lib/pass/aggregate_arguments.rs b/src/tools/miri/tests/native-lib/pass/aggregate_arguments.rs new file mode 100644 index 00000000000..55acb240612 --- /dev/null +++ b/src/tools/miri/tests/native-lib/pass/aggregate_arguments.rs @@ -0,0 +1,52 @@ +fn main() { + test_pass_struct(); + test_pass_struct_complex(); +} + +/// Test passing a basic struct as an argument. +fn test_pass_struct() { + // Exactly two fields, so that we hit the ScalarPair case. + #[repr(C)] + struct PassMe { + value: i32, + other_value: i64, + } + + extern "C" { + fn pass_struct(s: PassMe) -> i64; + } + + let pass_me = PassMe { value: 42, other_value: 1337 }; + assert_eq!(unsafe { pass_struct(pass_me) }, 42 + 1337); +} + +/// Test passing a more complex struct as an argument. +fn test_pass_struct_complex() { + #[repr(C)] + struct ComplexStruct { + part_1: Part1, + part_2: Part2, + part_3: u32, + } + #[repr(C)] + struct Part1 { + high: u16, + low: u16, + } + #[repr(C)] + struct Part2 { + bits: u32, + } + + extern "C" { + fn pass_struct_complex(s: ComplexStruct, high: u16, low: u16, bits: u32) -> i32; + } + + let high = 0xabcd; + let low = 0xef01; + let bits = 0xabcdef01; + + let complex = + ComplexStruct { part_1: Part1 { high, low }, part_2: Part2 { bits }, part_3: bits }; + assert_eq!(unsafe { pass_struct_complex(complex, high, low, bits) }, 0); +} diff --git a/src/tools/miri/tests/native-lib/pass/ptr_read_access.rs b/src/tools/miri/tests/native-lib/pass/ptr_read_access.rs index 4f3c37f00c1..bab73f7cf17 100644 --- a/src/tools/miri/tests/native-lib/pass/ptr_read_access.rs +++ b/src/tools/miri/tests/native-lib/pass/ptr_read_access.rs @@ -1,6 +1,7 @@ //@revisions: trace notrace //@[trace] only-target: x86_64-unknown-linux-gnu i686-unknown-linux-gnu //@[trace] compile-flags: -Zmiri-native-lib-enable-tracing +//@compile-flags: -Zmiri-permissive-provenance fn main() { test_access_pointer(); diff --git a/src/tools/miri/tests/pass-dep/shims/windows-fs.rs b/src/tools/miri/tests/pass-dep/shims/windows-fs.rs index 4ca19046b67..7b756603d92 100644 --- a/src/tools/miri/tests/pass-dep/shims/windows-fs.rs +++ b/src/tools/miri/tests/pass-dep/shims/windows-fs.rs @@ -2,20 +2,20 @@ //@compile-flags: -Zmiri-disable-isolation #![allow(nonstandard_style)] -use std::io::{ErrorKind, Read, Write}; +use std::io::{ErrorKind, Read, Seek, SeekFrom, Write}; use std::os::windows::ffi::OsStrExt; -use std::os::windows::io::AsRawHandle; +use std::os::windows::io::{AsRawHandle, FromRawHandle}; use std::path::Path; -use std::{fs, ptr}; +use std::{fs, mem, ptr}; #[path = "../../utils/mod.rs"] mod utils; use windows_sys::Wdk::Storage::FileSystem::{NtReadFile, NtWriteFile}; use windows_sys::Win32::Foundation::{ - CloseHandle, ERROR_ACCESS_DENIED, ERROR_ALREADY_EXISTS, ERROR_IO_DEVICE, GENERIC_READ, - GENERIC_WRITE, GetLastError, RtlNtStatusToDosError, STATUS_ACCESS_DENIED, - STATUS_IO_DEVICE_ERROR, STATUS_SUCCESS, SetLastError, + CloseHandle, DUPLICATE_SAME_ACCESS, DuplicateHandle, ERROR_ACCESS_DENIED, ERROR_ALREADY_EXISTS, + ERROR_IO_DEVICE, FALSE, GENERIC_READ, GENERIC_WRITE, GetLastError, RtlNtStatusToDosError, + STATUS_ACCESS_DENIED, STATUS_IO_DEVICE_ERROR, STATUS_SUCCESS, SetLastError, }; use windows_sys::Win32::Storage::FileSystem::{ BY_HANDLE_FILE_INFORMATION, CREATE_ALWAYS, CREATE_NEW, CreateFileW, DeleteFileW, @@ -24,6 +24,7 @@ use windows_sys::Win32::Storage::FileSystem::{ FILE_SHARE_WRITE, GetFileInformationByHandle, OPEN_ALWAYS, OPEN_EXISTING, SetFilePointerEx, }; use windows_sys::Win32::System::IO::IO_STATUS_BLOCK; +use windows_sys::Win32::System::Threading::GetCurrentProcess; fn main() { unsafe { @@ -36,6 +37,7 @@ fn main() { test_ntstatus_to_dos(); test_file_read_write(); test_file_seek(); + test_dup_handle(); } } @@ -273,6 +275,39 @@ unsafe fn test_file_read_write() { assert_eq!(GetLastError(), 1234); } +unsafe fn test_dup_handle() { + let temp = utils::tmp().join("test_dup.txt"); + + let mut file1 = fs::File::options().read(true).write(true).create(true).open(&temp).unwrap(); + + file1.write_all(b"Hello, World!\n").unwrap(); + file1.seek(SeekFrom::Start(0)).unwrap(); + + let first_handle = file1.as_raw_handle(); + + let cur_proc = GetCurrentProcess(); + let mut second_handle = mem::zeroed(); + let res = DuplicateHandle( + cur_proc, + first_handle, + cur_proc, + &mut second_handle, + 0, + FALSE, + DUPLICATE_SAME_ACCESS, + ); + assert!(res != 0); + + let mut buf1 = [0; 5]; + file1.read(&mut buf1).unwrap(); + assert_eq!(&buf1, b"Hello"); + + let mut file2 = fs::File::from_raw_handle(second_handle); + let mut buf2 = [0; 5]; + file2.read(&mut buf2).unwrap(); + assert_eq!(&buf2, b", Wor"); +} + unsafe fn test_file_seek() { let temp = utils::tmp().join("test_file_seek.txt"); let mut file = fs::File::options().create(true).write(true).read(true).open(&temp).unwrap(); diff --git a/src/tools/miri/tests/pass/both_borrows/basic_aliasing_model.rs b/src/tools/miri/tests/pass/both_borrows/basic_aliasing_model.rs index 6a625e597df..82976326a8d 100644 --- a/src/tools/miri/tests/pass/both_borrows/basic_aliasing_model.rs +++ b/src/tools/miri/tests/pass/both_borrows/basic_aliasing_model.rs @@ -23,7 +23,8 @@ fn main() { not_unpin_not_protected(); write_does_not_invalidate_all_aliases(); box_into_raw_allows_interior_mutable_alias(); - cell_inside_struct() + cell_inside_struct(); + zst(); } // Make sure that reading from an `&mut` does, like reborrowing to `&`, @@ -287,3 +288,22 @@ fn cell_inside_struct() { // Writing to `field1`, which is reserved, should also be allowed. (*a).field1 = 88; } + +/// ZST reborrows on various kinds of dangling pointers are valid. +fn zst() { + unsafe { + // Integer pointer. + let ptr = ptr::without_provenance_mut::<()>(15); + let _ref = &mut *ptr; + + // Out-of-bounds pointer. + let mut b = Box::new(0u8); + let ptr = (&raw mut *b).wrapping_add(15) as *mut (); + let _ref = &mut *ptr; + + // Deallocated pointer. + let ptr = &raw mut *b as *mut (); + drop(b); + let _ref = &mut *ptr; + } +} diff --git a/src/tools/miri/tests/pass/concurrency/sync.rs b/src/tools/miri/tests/pass/concurrency/sync.rs index a92359758da..142ac9cc8ca 100644 --- a/src/tools/miri/tests/pass/concurrency/sync.rs +++ b/src/tools/miri/tests/pass/concurrency/sync.rs @@ -9,7 +9,8 @@ use std::time::{Duration, Instant}; // We are expecting to sleep for 10ms. How long of a sleep we are accepting? // Even with 1000ms we still see this test fail on macOS runners. -const MAX_SLEEP_TIME_MS: u64 = 2000; +// On a aarch64-pc-windows-msvc runner, we saw 2.7s! +const MAX_SLEEP_TIME_MS: u64 = 4000; // Check if Rust barriers are working. diff --git a/src/tools/miri/tests/pass/float.rs b/src/tools/miri/tests/pass/float.rs index fe7316c6680..9f1b3f612b2 100644 --- a/src/tools/miri/tests/pass/float.rs +++ b/src/tools/miri/tests/pass/float.rs @@ -39,9 +39,8 @@ macro_rules! assert_approx_eq { }}; ($a:expr, $b: expr) => { - // accept up to 12ULP (4ULP for host floats and 4ULP for miri artificial error and 4 for any additional effects - // due to having multiple error sources. - assert_approx_eq!($a, $b, 12); + // accept up to 8ULP (4ULP for host floats and 4ULP for miri artificial error). + assert_approx_eq!($a, $b, 8); }; } @@ -176,6 +175,7 @@ fn assert_eq_msg<T: PartialEq + Debug>(x: T, y: T, msg: impl Display) { } /// Check that floats have bitwise equality +#[track_caller] fn assert_biteq<F: Float>(a: F, b: F, msg: impl Display) { let ab = a.to_bits(); let bb = b.to_bits(); @@ -189,6 +189,7 @@ fn assert_biteq<F: Float>(a: F, b: F, msg: impl Display) { } /// Check that two floats have equality +#[track_caller] fn assert_feq<F: Float>(a: F, b: F, msg: impl Display) { let ab = a.to_bits(); let bb = b.to_bits(); @@ -1051,6 +1052,10 @@ pub fn libm() { assert_eq!(42f64.powf(0.0), 1.0); assert_eq!(f32::INFINITY.powf(0.0), 1.0); assert_eq!(f64::INFINITY.powf(0.0), 1.0); + assert_eq!(f32::NEG_INFINITY.powi(3), f32::NEG_INFINITY); + assert_eq!(f32::NEG_INFINITY.powi(2), f32::INFINITY); + assert_eq!(f64::INFINITY.powi(3), f64::INFINITY); + assert_eq!(f64::INFINITY.powi(2), f64::INFINITY); // f*::NAN is a quiet NAN and should return 1 as well. assert_eq!(f32::NAN.powi(0), 1.0); @@ -1088,6 +1093,8 @@ pub fn libm() { assert_approx_eq!(1f32.exp_m1(), f32::consts::E - 1.0); assert_approx_eq!(1f64.exp_m1(), f64::consts::E - 1.0); + assert_approx_eq!(f32::NEG_INFINITY.exp_m1(), -1.0); + assert_approx_eq!(f64::NEG_INFINITY.exp_m1(), -1.0); assert_approx_eq!(10f32.exp2(), 1024f32); assert_approx_eq!(50f64.exp2(), 1125899906842624f64); @@ -1123,6 +1130,7 @@ pub fn libm() { assert_eq!(ldexp(0.65f64, 3i32), 5.2f64); assert_eq!(ldexp(1.42, 0xFFFF), f64::INFINITY); assert_eq!(ldexp(1.42, -0xFFFF), 0f64); + assert_eq!(ldexp(42.0, 0), 42.0); // Trigonometric functions. @@ -1131,8 +1139,13 @@ pub fn libm() { assert_approx_eq!((f64::consts::PI / 2f64).sin(), 1f64); assert_approx_eq!(f32::consts::FRAC_PI_6.sin(), 0.5); assert_approx_eq!(f64::consts::FRAC_PI_6.sin(), 0.5); - assert_approx_eq!(f32::consts::FRAC_PI_4.sin().asin(), f32::consts::FRAC_PI_4); - assert_approx_eq!(f64::consts::FRAC_PI_4.sin().asin(), f64::consts::FRAC_PI_4); + // Increase error tolerance to 16ULP because of the extra operation. + assert_approx_eq!(f32::consts::FRAC_PI_4.sin().asin(), f32::consts::FRAC_PI_4, 16); + assert_approx_eq!(f64::consts::FRAC_PI_4.sin().asin(), f64::consts::FRAC_PI_4, 16); + assert_biteq(0.0f32.asin(), 0.0f32, "asin(+0) = +0"); + assert_biteq((-0.0f32).asin(), -0.0, "asin(-0) = -0"); + assert_biteq(0.0f64.asin(), 0.0, "asin(+0) = +0"); + assert_biteq((-0.0f64).asin(), -0.0, "asin(-0) = -0"); assert_approx_eq!(1.0f32.sinh(), 1.1752012f32); assert_approx_eq!(1.0f64.sinh(), 1.1752011936438014f64); @@ -1159,11 +1172,18 @@ pub fn libm() { assert_approx_eq!((f64::consts::PI * 2f64).cos(), 1f64); assert_approx_eq!(f32::consts::FRAC_PI_3.cos(), 0.5); assert_approx_eq!(f64::consts::FRAC_PI_3.cos(), 0.5); - assert_approx_eq!(f32::consts::FRAC_PI_4.cos().acos(), f32::consts::FRAC_PI_4); - assert_approx_eq!(f64::consts::FRAC_PI_4.cos().acos(), f64::consts::FRAC_PI_4); + // Increase error tolerance to 16ULP because of the extra operation. + assert_approx_eq!(f32::consts::FRAC_PI_4.cos().acos(), f32::consts::FRAC_PI_4, 16); + assert_approx_eq!(f64::consts::FRAC_PI_4.cos().acos(), f64::consts::FRAC_PI_4, 16); + assert_biteq(1.0f32.acos(), 0.0, "acos(1) = 0"); + assert_biteq(1.0f64.acos(), 0.0, "acos(1) = 0"); - assert_approx_eq!(1.0f32.cosh(), 1.54308f32); + assert_approx_eq!(1.0f32.cosh(), 1.5430806f32); assert_approx_eq!(1.0f64.cosh(), 1.5430806348152437f64); + assert_eq!(0.0f32.cosh(), 1.0); + assert_eq!(0.0f64.cosh(), 1.0); + assert_eq!((-0.0f32).cosh(), 1.0); + assert_eq!((-0.0f64).cosh(), 1.0); assert_approx_eq!(2.0f32.acosh(), 1.31695789692481670862504634730796844f32); assert_approx_eq!(3.0f64.acosh(), 1.76274717403908605046521864995958461f64); @@ -1173,6 +1193,47 @@ pub fn libm() { assert_approx_eq!(1.0_f64, 1.0_f64.tan().atan()); assert_approx_eq!(1.0f32.atan2(2.0f32), 0.46364761f32); assert_approx_eq!(1.0f32.atan2(2.0f32), 0.46364761f32); + // C standard defines a bunch of fixed outputs for atan2 + macro_rules! fixed_atan2_cases{ + ($float_type:ident) => {{ + use std::$float_type::consts::{PI, FRAC_PI_2, FRAC_PI_4}; + use $float_type::{INFINITY, NEG_INFINITY}; + + // atan2(±0,−0) = ±π. + assert_eq!($float_type::atan2(0.0, -0.0), PI, "atan2(0,−0) = π"); + assert_eq!($float_type::atan2(-0.0, -0.0), -PI, "atan2(-0,−0) = -π"); + + // atan2(±0, y) = ±π for y < 0. + assert_eq!($float_type::atan2(0.0, -1.0), PI, "atan2(0, y) = π for y < 0."); + assert_eq!($float_type::atan2(-0.0, -1.0), -PI, "atan2(-0, y) = -π for y < 0."); + + // atan2(x, ±0) = −π/2 for x < 0. + assert_eq!($float_type::atan2(-1.0, 0.0), -FRAC_PI_2, "atan2(x, 0) = −π/2 for x < 0"); + assert_eq!($float_type::atan2(-1.0, -0.0), -FRAC_PI_2, "atan2(x, -0) = −π/2 for x < 0"); + + // atan2(x, ±0) = π/2 for x > 0. + assert_eq!($float_type::atan2(1.0, 0.0), FRAC_PI_2, "atan2(x, 0) = π/2 for x > 0."); + assert_eq!($float_type::atan2(1.0, -0.0), FRAC_PI_2, "atan2(x, -0) = π/2 for x > 0."); + + // atan2(±x,−∞) = ±π for finite x > 0. + assert_eq!($float_type::atan2(1.0, NEG_INFINITY), PI, "atan2(x, −∞) = π for finite x > 0"); + assert_eq!($float_type::atan2(-1.0, NEG_INFINITY), -PI, "atan2(-x, −∞) = -π for finite x > 0"); + + // atan2(±∞, y) returns ±π/2 for finite y. + assert_eq!($float_type::atan2(INFINITY, 1.0), FRAC_PI_2, "atan2(+∞, y) returns π/2 for finite y"); + assert_eq!($float_type::atan2(NEG_INFINITY, 1.0), -FRAC_PI_2, "atan2(-∞, y) returns -π/2 for finite y"); + + // atan2(±∞, −∞) = ±3π/4 + assert_eq!($float_type::atan2(INFINITY, NEG_INFINITY), 3.0 * FRAC_PI_4, "atan2(+∞, −∞) = 3π/4"); + assert_eq!($float_type::atan2(NEG_INFINITY, NEG_INFINITY), -3.0 * FRAC_PI_4, "atan2(-∞, −∞) = -3π/4"); + + // atan2(±∞, +∞) = ±π/4 + assert_eq!($float_type::atan2(INFINITY, INFINITY), FRAC_PI_4, "atan2(+∞, +∞) = π/4"); + assert_eq!($float_type::atan2(NEG_INFINITY, INFINITY), -FRAC_PI_4, "atan2(-∞, +∞) = -π/4"); + }} + } + fixed_atan2_cases!(f32); + fixed_atan2_cases!(f64); assert_approx_eq!( 1.0f32.tanh(), @@ -1182,6 +1243,11 @@ pub fn libm() { 1.0f64.tanh(), (1.0 - f64::consts::E.powi(-2)) / (1.0 + f64::consts::E.powi(-2)) ); + assert_eq!(f32::INFINITY.tanh(), 1.0); + assert_eq!(f32::NEG_INFINITY.tanh(), -1.0); + assert_eq!(f64::INFINITY.tanh(), 1.0); + assert_eq!(f64::NEG_INFINITY.tanh(), -1.0); + assert_approx_eq!(0.5f32.atanh(), 0.54930614433405484569762261846126285f32); assert_approx_eq!(0.5f64.atanh(), 0.54930614433405484569762261846126285f64); @@ -1202,8 +1268,14 @@ pub fn libm() { assert_approx_eq!(1.0f32.erf(), 0.84270079294971486934122063508260926f32); assert_approx_eq!(1.0f64.erf(), 0.84270079294971486934122063508260926f64); + assert_eq!(f32::INFINITY.erf(), 1.0); + assert_eq!(f64::INFINITY.erf(), 1.0); assert_approx_eq!(1.0f32.erfc(), 0.15729920705028513065877936491739074f32); assert_approx_eq!(1.0f64.erfc(), 0.15729920705028513065877936491739074f64); + assert_eq!(f32::NEG_INFINITY.erfc(), 2.0); + assert_eq!(f64::NEG_INFINITY.erfc(), 2.0); + assert_eq!(f32::INFINITY.erfc(), 0.0); + assert_eq!(f64::INFINITY.erfc(), 0.0); } fn test_fast() { @@ -1413,7 +1485,6 @@ fn test_non_determinism() { } pub fn test_operations_f32(a: f32, b: f32) { test_operations_f!(a, b); - // FIXME: some are temporarily disabled as it breaks std tests. ensure_nondet(|| a.powf(b)); ensure_nondet(|| a.powi(2)); ensure_nondet(|| a.log(b)); @@ -1422,35 +1493,34 @@ fn test_non_determinism() { ensure_nondet(|| f32::consts::E.ln()); ensure_nondet(|| 10f32.log10()); ensure_nondet(|| 8f32.log2()); - // ensure_nondet(|| 1f32.ln_1p()); - // ensure_nondet(|| 27.0f32.cbrt()); - // ensure_nondet(|| 3.0f32.hypot(4.0f32)); + ensure_nondet(|| 1f32.ln_1p()); + ensure_nondet(|| 27.0f32.cbrt()); + ensure_nondet(|| 3.0f32.hypot(4.0f32)); ensure_nondet(|| 1f32.sin()); ensure_nondet(|| 1f32.cos()); // On i686-pc-windows-msvc , these functions are implemented by calling the `f64` version, // which means the little rounding errors Miri introduces are discarded by the cast down to // `f32`. Just skip the test for them. - // if !cfg!(all(target_os = "windows", target_env = "msvc", target_arch = "x86")) { - // ensure_nondet(|| 1.0f32.tan()); - // ensure_nondet(|| 1.0f32.asin()); - // ensure_nondet(|| 5.0f32.acos()); - // ensure_nondet(|| 1.0f32.atan()); - // ensure_nondet(|| 1.0f32.atan2(2.0f32)); - // ensure_nondet(|| 1.0f32.sinh()); - // ensure_nondet(|| 1.0f32.cosh()); - // ensure_nondet(|| 1.0f32.tanh()); - // } - // ensure_nondet(|| 1.0f32.asinh()); - // ensure_nondet(|| 2.0f32.acosh()); - // ensure_nondet(|| 0.5f32.atanh()); - // ensure_nondet(|| 5.0f32.gamma()); - // ensure_nondet(|| 5.0f32.ln_gamma()); - // ensure_nondet(|| 5.0f32.erf()); - // ensure_nondet(|| 5.0f32.erfc()); + if !cfg!(all(target_os = "windows", target_env = "msvc", target_arch = "x86")) { + ensure_nondet(|| 1.0f32.tan()); + ensure_nondet(|| 1.0f32.asin()); + ensure_nondet(|| 5.0f32.acos()); + ensure_nondet(|| 1.0f32.atan()); + ensure_nondet(|| 1.0f32.atan2(2.0f32)); + ensure_nondet(|| 1.0f32.sinh()); + ensure_nondet(|| 1.0f32.cosh()); + ensure_nondet(|| 1.0f32.tanh()); + } + ensure_nondet(|| 1.0f32.asinh()); + ensure_nondet(|| 2.0f32.acosh()); + ensure_nondet(|| 0.5f32.atanh()); + ensure_nondet(|| 5.0f32.gamma()); + ensure_nondet(|| 5.0f32.ln_gamma()); + ensure_nondet(|| 5.0f32.erf()); + ensure_nondet(|| 5.0f32.erfc()); } pub fn test_operations_f64(a: f64, b: f64) { test_operations_f!(a, b); - // FIXME: some are temporarily disabled as it breaks std tests. ensure_nondet(|| a.powf(b)); ensure_nondet(|| a.powi(2)); ensure_nondet(|| a.log(b)); @@ -1459,26 +1529,26 @@ fn test_non_determinism() { ensure_nondet(|| 3f64.ln()); ensure_nondet(|| f64::consts::E.log10()); ensure_nondet(|| f64::consts::E.log2()); - // ensure_nondet(|| 1f64.ln_1p()); - // ensure_nondet(|| 27.0f64.cbrt()); - // ensure_nondet(|| 3.0f64.hypot(4.0f64)); + ensure_nondet(|| 1f64.ln_1p()); + ensure_nondet(|| 27.0f64.cbrt()); + ensure_nondet(|| 3.0f64.hypot(4.0f64)); ensure_nondet(|| 1f64.sin()); ensure_nondet(|| 1f64.cos()); - // ensure_nondet(|| 1.0f64.tan()); - // ensure_nondet(|| 1.0f64.asin()); - // ensure_nondet(|| 5.0f64.acos()); - // ensure_nondet(|| 1.0f64.atan()); - // ensure_nondet(|| 1.0f64.atan2(2.0f64)); - // ensure_nondet(|| 1.0f64.sinh()); - // ensure_nondet(|| 1.0f64.cosh()); - // ensure_nondet(|| 1.0f64.tanh()); - // ensure_nondet(|| 1.0f64.asinh()); - // ensure_nondet(|| 3.0f64.acosh()); - // ensure_nondet(|| 0.5f64.atanh()); - // ensure_nondet(|| 5.0f64.gamma()); - // ensure_nondet(|| 5.0f64.ln_gamma()); - // ensure_nondet(|| 5.0f64.erf()); - // ensure_nondet(|| 5.0f64.erfc()); + ensure_nondet(|| 1.0f64.tan()); + ensure_nondet(|| 1.0f64.asin()); + ensure_nondet(|| 5.0f64.acos()); + ensure_nondet(|| 1.0f64.atan()); + ensure_nondet(|| 1.0f64.atan2(2.0f64)); + ensure_nondet(|| 1.0f64.sinh()); + ensure_nondet(|| 1.0f64.cosh()); + ensure_nondet(|| 1.0f64.tanh()); + ensure_nondet(|| 1.0f64.asinh()); + ensure_nondet(|| 3.0f64.acosh()); + ensure_nondet(|| 0.5f64.atanh()); + ensure_nondet(|| 5.0f64.gamma()); + ensure_nondet(|| 5.0f64.ln_gamma()); + ensure_nondet(|| 5.0f64.erf()); + ensure_nondet(|| 5.0f64.erfc()); } pub fn test_operations_f128(a: f128, b: f128) { test_operations_f!(a, b); diff --git a/src/tools/miri/tests/pass/float_extra_rounding_error.rs b/src/tools/miri/tests/pass/float_extra_rounding_error.rs new file mode 100644 index 00000000000..24d7cf2ccee --- /dev/null +++ b/src/tools/miri/tests/pass/float_extra_rounding_error.rs @@ -0,0 +1,31 @@ +//! Check that the flags to control the extra rounding error work. +//@revisions: random max none +//@[max]compile-flags: -Zmiri-max-extra-rounding-error +//@[none]compile-flags: -Zmiri-no-extra-rounding-error +#![feature(cfg_select)] + +use std::collections::HashSet; +use std::hint::black_box; + +fn main() { + let expected = cfg_select! { + random => 9, // -4 ..= +4 ULP error + max => 2, + none => 1, + }; + // Call `sin(0.5)` a bunch of times and see how many different values we get. + let mut values = HashSet::new(); + for _ in 0..(expected * 16) { + let val = black_box(0.5f64).sin(); + values.insert(val.to_bits()); + } + assert_eq!(values.len(), expected); + + if !cfg!(none) { + // Ensure the smallest and biggest value are 8 ULP apart. + // We can just subtract the raw bit representations for this. + let min = *values.iter().min().unwrap(); + let max = *values.iter().max().unwrap(); + assert_eq!(min.abs_diff(max), 8); + } +} diff --git a/src/tools/miri/tests/pass/intrinsics/integer.rs b/src/tools/miri/tests/pass/intrinsics/integer.rs index 13e7bd8e1b9..8727b6d3c87 100644 --- a/src/tools/miri/tests/pass/intrinsics/integer.rs +++ b/src/tools/miri/tests/pass/intrinsics/integer.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 // SPDX-FileCopyrightText: The Rust Project Developers (see https://thanks.rust-lang.org) -#![feature(core_intrinsics)] +#![feature(core_intrinsics, funnel_shifts)] use std::intrinsics::*; pub fn main() { @@ -143,5 +143,11 @@ pub fn main() { assert_eq!(unchecked_mul(6u8, 7), 42); assert_eq!(unchecked_mul(13, -5), -65); + + assert_eq!(unchecked_funnel_shl(1_u32, 2, 5), 32); + assert_eq!(unchecked_funnel_shl(1_u32, 2, 31), 0x80000001); + + assert_eq!(unchecked_funnel_shr(1_u32, 2, 5), 0x08000000); + assert_eq!(unchecked_funnel_shr(1_u32, 2, 31), 2); } } diff --git a/src/tools/miri/tests/pass/shims/fs.rs b/src/tools/miri/tests/pass/shims/fs.rs index e7f11c54704..022dcc5dcba 100644 --- a/src/tools/miri/tests/pass/shims/fs.rs +++ b/src/tools/miri/tests/pass/shims/fs.rs @@ -72,7 +72,9 @@ fn test_file() { // Writing to a file opened for reading should error (and not stop interpretation). std does not // categorize the error so we don't check for details. - file.write(&[]).unwrap_err(); + file.write(&[0]).unwrap_err(); + // However, writing 0 bytes can succeed or fail. + let _ignore = file.write(&[]); // Removing file should succeed. remove_file(&path).unwrap(); diff --git a/src/tools/miri/tests/pass/shims/x86/rounding-error.rs b/src/tools/miri/tests/pass/shims/x86/rounding-error.rs new file mode 100644 index 00000000000..bf56111b2e4 --- /dev/null +++ b/src/tools/miri/tests/pass/shims/x86/rounding-error.rs @@ -0,0 +1,37 @@ +// We're testing x86 target specific features +//@only-target: x86_64 i686 + +//! rsqrt and rcp SSE/AVX operations are approximate. We use that as license to treat them as +//! non-deterministic. Ensure that we do indeed see random results within the expected error bounds. + +#[cfg(target_arch = "x86")] +use std::arch::x86::*; +#[cfg(target_arch = "x86_64")] +use std::arch::x86_64::*; +use std::collections::HashSet; + +fn main() { + let mut vals = HashSet::new(); + for _ in 0..50 { + unsafe { + // Compute the inverse square root of 4.0, four times. + let a = _mm_setr_ps(4.0, 4.0, 4.0, 4.0); + let exact = 0.5; + let r = _mm_rsqrt_ps(a); + let r: [f32; 4] = std::mem::transmute(r); + // Check the results. + for r in r { + vals.insert(r.to_bits()); + // Ensure the relative error is less than 2^-12. + let rel_error = (r - exact) / exact; + let log_error = rel_error.abs().log2(); + assert!( + rel_error == 0.0 || log_error < -12.0, + "got an error of {rel_error} = 2^{log_error}" + ); + } + } + } + // Ensure we saw a bunch of different results. + assert!(vals.len() >= 50); +} diff --git a/src/tools/miri/tests/pass/tree_borrows/cell-lazy-write-to-surrounding.rs b/src/tools/miri/tests/pass/tree_borrows/cell-lazy-write-to-surrounding.rs index abe08f2cd22..7352784ac7a 100644 --- a/src/tools/miri/tests/pass/tree_borrows/cell-lazy-write-to-surrounding.rs +++ b/src/tools/miri/tests/pass/tree_borrows/cell-lazy-write-to-surrounding.rs @@ -14,9 +14,11 @@ fn main() { foo(&arr[0]); let pair = (Cell::new(1), 1); - // TODO: Ideally, this would result in UB since the second element - // in `pair` is Frozen. We would need some way to express a - // "shared reference with permission to access surrounding - // interior mutable data". foo(&pair.0); + + // As long as the "inside" part is `!Freeze`, the permission to mutate the "outside" is preserved. + let pair = (Cell::new(()), 1); + let x = &pair.0; + let ptr = (&raw const *x).cast::<i32>().cast_mut(); + unsafe { ptr.write(0) }; } diff --git a/src/tools/miri/tests/ui.rs b/src/tools/miri/tests/ui.rs index f021d5194cd..b7286d9a367 100644 --- a/src/tools/miri/tests/ui.rs +++ b/src/tools/miri/tests/ui.rs @@ -60,6 +60,7 @@ fn build_native_lib(target: &str) -> PathBuf { native_lib_path.to_str().unwrap(), // FIXME: Automate gathering of all relevant C source files in the directory. "tests/native-lib/scalar_arguments.c", + "tests/native-lib/aggregate_arguments.c", "tests/native-lib/ptr_read_access.c", "tests/native-lib/ptr_write_access.c", // Ensure we notice serious problems in the C code. diff --git a/src/tools/miri/tests/utils/libc.rs b/src/tools/miri/tests/utils/libc.rs index 1a3cd067c04..4757a5a268c 100644 --- a/src/tools/miri/tests/utils/libc.rs +++ b/src/tools/miri/tests/utils/libc.rs @@ -34,10 +34,7 @@ pub unsafe fn write_all( if res < 0 { return res; } - if res == 0 { - // EOF? - break; - } + // Apparently a return value of 0 is just a short write, nothing special (unlike reads). written_so_far += res as libc::size_t; } return written_so_far as libc::ssize_t; diff --git a/src/tools/opt-dist/Cargo.toml b/src/tools/opt-dist/Cargo.toml index b2833a9d7f1..f4051ae67d7 100644 --- a/src/tools/opt-dist/Cargo.toml +++ b/src/tools/opt-dist/Cargo.toml @@ -15,9 +15,9 @@ fs_extra = "1" camino = "1" tar = "0.4" xz = { version = "0.1", package = "xz2" } -serde_json.workspace = true +serde_json = "1" glob = "0.3" -tempfile.workspace = true +tempfile = "3.5" derive_builder = "0.20" clap = { version = "4", features = ["derive"] } tabled = { version = "0.15", default-features = false, features = ["std"] } diff --git a/src/tools/run-make-support/Cargo.toml b/src/tools/run-make-support/Cargo.toml index 86ac4b9d7b4..250e0f65a9f 100644 --- a/src/tools/run-make-support/Cargo.toml +++ b/src/tools/run-make-support/Cargo.toml @@ -12,10 +12,10 @@ edition = "2024" # tidy-alphabetical-start bstr = "1.12" gimli = "0.32" -libc.workspace = true +libc = "0.2" object = "0.37" regex = "1.11" -serde_json.workspace = true +serde_json = "1.0" similar = "2.7" wasmparser = { version = "0.236", default-features = false, features = ["std", "features", "validate"] } # tidy-alphabetical-end diff --git a/src/tools/run-make-support/src/external_deps/cargo.rs b/src/tools/run-make-support/src/external_deps/cargo.rs index 8da9f002c41..3f2d0ce14e3 100644 --- a/src/tools/run-make-support/src/external_deps/cargo.rs +++ b/src/tools/run-make-support/src/external_deps/cargo.rs @@ -1,11 +1,17 @@ use crate::command::Command; -use crate::env_var; use crate::util::set_host_compiler_dylib_path; -/// Returns a command that can be used to invoke cargo. The cargo is provided by compiletest -/// through the `CARGO` env var. +/// Returns a command that can be used to invoke in-tree cargo. The cargo is provided by compiletest +/// through the `CARGO` env var, and is **only** available for the `run-make-cargo` test suite. pub fn cargo() -> Command { - let mut cmd = Command::new(env_var("CARGO")); + let cargo_path = std::env::var("CARGO").unwrap_or_else(|e| { + panic!( + "in-tree `cargo` should be available for `run-make-cargo` test suite, but not \ + `run-make` test suite: {e}" + ) + }); + + let mut cmd = Command::new(cargo_path); set_host_compiler_dylib_path(&mut cmd); cmd } diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index 191e205f257..fef75401d94 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -1,7 +1,8 @@ //! `run-make-support` is a support library for run-make tests. It provides command wrappers and //! convenience utility functions to help test writers reduce duplication. The support library -//! notably is built via cargo: this means that if your test wants some non-trivial utility, such -//! as `object` or `wasmparser`, they can be re-exported and be made available through this library. +//! notably is built via bootstrap cargo: this means that if your test wants some non-trivial +//! utility, such as `object` or `wasmparser`, they can be re-exported and be made available through +//! this library. #![warn(unreachable_pub)] diff --git a/src/tools/rustdoc-gui-test/src/main.rs b/src/tools/rustdoc-gui-test/src/main.rs index 42feae8c208..5062c2597f1 100644 --- a/src/tools/rustdoc-gui-test/src/main.rs +++ b/src/tools/rustdoc-gui-test/src/main.rs @@ -71,22 +71,28 @@ fn main() -> Result<(), ()> { let mut command = Command::new(&config.nodejs); command - .arg(config.rust_src.join("src/tools/rustdoc-gui/tester.js")) + .arg(local_node_modules.join(".bin/browser-ui-test")) .arg("--jobs") .arg(&config.jobs) - .arg("--doc-folder") + .arg("--variable") + .arg("DOC_PATH") .arg(config.out_dir.join("doc")) - .arg("--tests-folder") - .arg(config.rust_src.join("tests/rustdoc-gui")); + .arg("--allow-file-access-from-files") + .arg("--display-format") + .arg("compact"); if local_node_modules.exists() { - // Link the local node_modules if exists. + // Link the local node_modules if it exists. // This is useful when we run rustdoc-gui-test from outside of the source root. command.env("NODE_PATH", local_node_modules); } - for file in &config.goml_files { - command.arg("--file").arg(file); + if config.goml_files.is_empty() { + command.arg("--test-folder").arg(config.rust_src.join("tests/rustdoc-gui")); + } else { + for file in &config.goml_files { + command.arg("--test-file").arg(config.rust_src.join("tests/rustdoc-gui").join(file)); + } } command.args(&config.test_args); diff --git a/src/tools/rustdoc-gui/.eslintrc.js b/src/tools/rustdoc-gui/.eslintrc.js deleted file mode 100644 index 3eccbc74cb9..00000000000 --- a/src/tools/rustdoc-gui/.eslintrc.js +++ /dev/null @@ -1,96 +0,0 @@ -module.exports = { - "env": { - "browser": true, - "node": true, - "es6": true - }, - "extends": "eslint:recommended", - "parserOptions": { - "ecmaVersion": 2019, - "sourceType": "module" - }, - "rules": { - "linebreak-style": [ - "error", - "unix" - ], - "semi": [ - "error", - "always" - ], - "quotes": [ - "error", - "double" - ], - "linebreak-style": [ - "error", - "unix" - ], - "no-trailing-spaces": "error", - "no-var": ["error"], - "prefer-const": ["error"], - "prefer-arrow-callback": ["error"], - "brace-style": [ - "error", - "1tbs", - { "allowSingleLine": false } - ], - "keyword-spacing": [ - "error", - { "before": true, "after": true } - ], - "arrow-spacing": [ - "error", - { "before": true, "after": true } - ], - "key-spacing": [ - "error", - { "beforeColon": false, "afterColon": true, "mode": "strict" } - ], - "func-call-spacing": ["error", "never"], - "space-infix-ops": "error", - "space-before-function-paren": ["error", "never"], - "space-before-blocks": "error", - "comma-dangle": ["error", "always-multiline"], - "comma-style": ["error", "last"], - "max-len": ["error", { "code": 100, "tabWidth": 4 }], - "eol-last": ["error", "always"], - "arrow-parens": ["error", "as-needed"], - "no-unused-vars": [ - "error", - { - "argsIgnorePattern": "^_", - "varsIgnorePattern": "^_" - } - ], - "eqeqeq": "error", - "no-const-assign": "error", - "no-debugger": "error", - "no-dupe-args": "error", - "no-dupe-else-if": "error", - "no-dupe-keys": "error", - "no-duplicate-case": "error", - "no-ex-assign": "error", - "no-fallthrough": "error", - "no-invalid-regexp": "error", - "no-import-assign": "error", - "no-self-compare": "error", - "no-template-curly-in-string": "error", - "block-scoped-var": "error", - "guard-for-in": "error", - "no-alert": "error", - "no-confusing-arrow": "error", - "no-div-regex": "error", - "no-floating-decimal": "error", - "no-implicit-globals": "error", - "no-implied-eval": "error", - "no-label-var": "error", - "no-lonely-if": "error", - "no-mixed-operators": "error", - "no-multi-assign": "error", - "no-return-assign": "error", - "no-script-url": "error", - "no-sequences": "error", - "no-div-regex": "error", - } -}; diff --git a/src/tools/rustdoc-gui/tester.js b/src/tools/rustdoc-gui/tester.js deleted file mode 100644 index 9dc32f335a8..00000000000 --- a/src/tools/rustdoc-gui/tester.js +++ /dev/null @@ -1,309 +0,0 @@ -// This package needs to be install: -// -// ``` -// npm install browser-ui-test -// ``` - -const fs = require("fs"); -const path = require("path"); -const os = require("os"); -const {Options, runTest} = require("browser-ui-test"); - -// If a test fails or errors, we will retry it two more times in case it was a flaky failure. -const NB_RETRY = 3; - -function showHelp() { - console.log("rustdoc-js options:"); - console.log(" --doc-folder [PATH] : location of the generated doc folder"); - console.log(" --file [PATH] : file to run (can be repeated)"); - console.log(" --debug : show extra information about script run"); - console.log(" --show-text : render font in pages"); - console.log(" --no-headless : disable headless mode"); - console.log(" --help : show this message then quit"); - console.log(" --tests-folder [PATH] : location of the .GOML tests folder"); - console.log(" --jobs [NUMBER] : number of threads to run tests on"); - console.log(" --executable-path [PATH] : path of the browser's executable to be used"); -} - -function isNumeric(s) { - return /^\d+$/.test(s); -} - -function parseOptions(args) { - const opts = { - "doc_folder": "", - "tests_folder": "", - "files": [], - "debug": false, - "show_text": false, - "no_headless": false, - "jobs": -1, - "executable_path": null, - }; - const correspondences = { - "--doc-folder": "doc_folder", - "--tests-folder": "tests_folder", - "--debug": "debug", - "--show-text": "show_text", - "--no-headless": "no_headless", - "--executable-path": "executable_path", - }; - - for (let i = 0; i < args.length; ++i) { - const arg = args[i]; - if (arg === "--doc-folder" - || arg === "--tests-folder" - || arg === "--file" - || arg === "--jobs" - || arg === "--executable-path") { - i += 1; - if (i >= args.length) { - console.log("Missing argument after `" + arg + "` option."); - return null; - } - const arg_value = args[i]; - if (arg === "--jobs") { - if (!isNumeric(arg_value)) { - console.log( - "`--jobs` option expects a positive number, found `" + arg_value + "`"); - return null; - } - opts["jobs"] = parseInt(arg_value); - } else if (arg !== "--file") { - opts[correspondences[arg]] = arg_value; - } else { - opts["files"].push(arg_value); - } - } else if (arg === "--help") { - showHelp(); - process.exit(0); - } else if (correspondences[arg]) { - opts[correspondences[arg]] = true; - } else { - console.log("Unknown option `" + arg + "`."); - console.log("Use `--help` to see the list of options"); - return null; - } - } - if (opts["tests_folder"].length < 1) { - console.log("Missing `--tests-folder` option."); - } else if (opts["doc_folder"].length < 1) { - console.log("Missing `--doc-folder` option."); - } else { - return opts; - } - return null; -} - -/// Print single char status information without \n -function char_printer(n_tests) { - const max_per_line = 10; - let current = 0; - return { - successful: function() { - current += 1; - if (current % max_per_line === 0) { - process.stdout.write(`. (${current}/${n_tests})${os.EOL}`); - } else { - process.stdout.write("."); - } - }, - erroneous: function() { - current += 1; - if (current % max_per_line === 0) { - process.stderr.write(`F (${current}/${n_tests})${os.EOL}`); - } else { - process.stderr.write("F"); - } - }, - finish: function() { - if (current % max_per_line === 0) { - // Don't output if we are already at a matching line end - console.log(""); - } else { - const spaces = " ".repeat(max_per_line - (current % max_per_line)); - process.stdout.write(`${spaces} (${current}/${n_tests})${os.EOL}${os.EOL}`); - } - }, - }; -} - -// Sort array by .file_name property -function by_filename(a, b) { - return a.file_name - b.file_name; -} - -async function runTests(opts, framework_options, files, results, status_bar, showTestFailures) { - const tests_queue = []; - - for (const testPath of files) { - const callback = runTest(testPath, {"options": framework_options}) - .then(out => { - const [output, nb_failures] = out; - results[nb_failures === 0 ? "successful" : "failed"].push({ - file_name: testPath, - output: output, - }); - if (nb_failures === 0) { - status_bar.successful(); - } else if (showTestFailures) { - status_bar.erroneous(); - } - }) - .catch(err => { - results.errored.push({ - file_name: testPath, - output: err, - }); - if (showTestFailures) { - status_bar.erroneous(); - } - }) - .finally(() => { - // We now remove the promise from the tests_queue. - tests_queue.splice(tests_queue.indexOf(callback), 1); - }); - tests_queue.push(callback); - if (opts["jobs"] > 0 && tests_queue.length >= opts["jobs"]) { - await Promise.race(tests_queue); - } - } - if (tests_queue.length > 0) { - await Promise.all(tests_queue); - } -} - -function createEmptyResults() { - return { - successful: [], - failed: [], - errored: [], - }; -} - -async function main(argv) { - const opts = parseOptions(argv.slice(2)); - if (opts === null) { - process.exit(1); - } - - // Print successful tests too - let debug = false; - // Run tests in sequentially - let headless = true; - const framework_options = new Options(); - try { - // This is more convenient that setting fields one by one. - const args = [ - "--variable", "DOC_PATH", opts["doc_folder"].split("\\").join("/"), - "--allow-file-access-from-files", - ]; - if (opts["debug"]) { - debug = true; - args.push("--debug"); - } - if (opts["show_text"]) { - args.push("--show-text"); - } - if (opts["no_headless"]) { - args.push("--no-headless"); - headless = false; - } - if (opts["executable_path"] !== null) { - args.push("--executable-path"); - args.push(opts["executable_path"]); - } - framework_options.parseArguments(args); - } catch (error) { - console.error(`invalid argument: ${error}`); - process.exit(1); - } - - let files; - if (opts["files"].length === 0) { - files = fs.readdirSync(opts["tests_folder"]); - } else { - files = opts["files"]; - } - files = files.filter(file => path.extname(file) === ".goml"); - if (files.length === 0) { - console.error("rustdoc-gui: No test selected"); - process.exit(2); - } - files.forEach((file_name, index) => { - files[index] = path.join(opts["tests_folder"], file_name); - }); - files.sort(); - - if (!headless) { - opts["jobs"] = 1; - console.log("`--no-headless` option is active, disabling concurrency for running tests."); - } - - if (opts["jobs"] < 1) { - const len = files.length; - console.log( - `Running ${len} rustdoc-gui (UNBOUNDED concurrency; use "-j#" for a limit) ...`, - ); - process.setMaxListeners(files.length + 1); - } else if (headless) { - console.log(`Running ${files.length} rustdoc-gui (${opts["jobs"]} concurrently) ...`); - process.setMaxListeners(opts["jobs"] + 1); - } else { - console.log(`Running ${files.length} rustdoc-gui ...`); - } - - const originalFilesLen = files.length; - const results = createEmptyResults(); - const status_bar = char_printer(files.length); - - let new_results; - for (let it = 0; it < NB_RETRY && files.length > 0; ++it) { - new_results = createEmptyResults(); - await runTests(opts, framework_options, files, new_results, status_bar, it + 1 >= NB_RETRY); - Array.prototype.push.apply(results.successful, new_results.successful); - // We generate the new list of files with the previously failing tests. - files = Array.prototype.concat(new_results.failed, new_results.errored).map( - f => f["file_name"]); - if (files.length > originalFilesLen / 2) { - // If we have too many failing tests, it's very likely not flaky failures anymore so - // no need to retry. - break; - } - } - - status_bar.finish(); - - Array.prototype.push.apply(results.failed, new_results.failed); - Array.prototype.push.apply(results.errored, new_results.errored); - - if (debug) { - results.successful.sort(by_filename); - results.successful.forEach(r => { - console.log(r.output); - }); - } - - if (results.failed.length > 0) { - console.log(""); - results.failed.sort(by_filename); - results.failed.forEach(r => { - console.log(r.file_name, r.output); - }); - } - if (results.errored.length > 0) { - console.log(os.EOL); - // print run errors on the bottom so developers see them better - results.errored.sort(by_filename); - results.errored.forEach(r => { - console.error(r.file_name, r.output); - }); - } - - if (results.failed.length > 0 || results.errored.length > 0) { - process.exit(1); - } - process.exit(0); -} - -main(process.argv); diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs index 6555679c394..75e468b3525 100644 --- a/src/tools/rustfmt/src/items.rs +++ b/src/tools/rustfmt/src/items.rs @@ -1175,8 +1175,8 @@ pub(crate) fn format_trait( let mut result = String::with_capacity(128); let header = format!( "{}{}{}{}trait ", - format_constness(constness), format_visibility(context, &item.vis), + format_constness(constness), format_safety(safety), format_auto(is_auto), ); diff --git a/src/tools/rustfmt/src/patterns.rs b/src/tools/rustfmt/src/patterns.rs index d212ecf392a..848bd0766e7 100644 --- a/src/tools/rustfmt/src/patterns.rs +++ b/src/tools/rustfmt/src/patterns.rs @@ -303,7 +303,7 @@ impl Rewrite for Pat { qself, path, fields, - rest == ast::PatFieldsRest::Rest, + matches!(rest, ast::PatFieldsRest::Rest(_)), self.span, context, shape, diff --git a/src/tools/tidy/Cargo.toml b/src/tools/tidy/Cargo.toml index f43733665ed..c1f27de7ed4 100644 --- a/src/tools/tidy/Cargo.toml +++ b/src/tools/tidy/Cargo.toml @@ -14,7 +14,7 @@ ignore = "0.4.18" semver = "1.0" serde = { version = "1.0.125", features = ["derive"], optional = true } termcolor = "1.1.3" -rustc-hash.workspace = true +rustc-hash = "2.0.0" fluent-syntax = "0.12" similar = "2.5.0" toml = "0.7.8" diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 6974ede624a..fee48bea144 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -80,7 +80,7 @@ pub(crate) const WORKSPACES: &[(&str, ExceptionList, Option<(&[&str], &[&str])>, ("src/tools/rustbook", EXCEPTIONS_RUSTBOOK, None, &["src/doc/book", "src/doc/reference"]), ("src/tools/rustc-perf", EXCEPTIONS_RUSTC_PERF, None, &["src/tools/rustc-perf"]), ("src/tools/test-float-parse", EXCEPTIONS, None, &[]), - ("tests/run-make/uefi-qemu/uefi_qemu_test", EXCEPTIONS_UEFI_QEMU_TEST, None, &[]), + ("tests/run-make-cargo/uefi-qemu/uefi_qemu_test", EXCEPTIONS_UEFI_QEMU_TEST, None, &[]), // tidy-alphabetical-end ]; @@ -490,6 +490,7 @@ const PERMITTED_STDLIB_DEPENDENCIES: &[&str] = &[ "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", + "wit-bindgen", // tidy-alphabetical-end ]; @@ -798,7 +799,10 @@ fn check_runtime_no_duplicate_dependencies(metadata: &Metadata, bad: &mut bool) continue; } - if !seen_pkgs.insert(&*pkg.name) { + // Skip the `wasi` crate here which the standard library explicitly + // depends on two version of (one for the `wasm32-wasip1` target and + // another for the `wasm32-wasip2` target). + if pkg.name.to_string() != "wasi" && !seen_pkgs.insert(&*pkg.name) { tidy_error!( bad, "duplicate package `{}` is not allowed for the standard library", diff --git a/src/tools/tidy/src/extra_checks/mod.rs b/src/tools/tidy/src/extra_checks/mod.rs index 31169ec5967..321ef65117e 100644 --- a/src/tools/tidy/src/extra_checks/mod.rs +++ b/src/tools/tidy/src/extra_checks/mod.rs @@ -303,7 +303,7 @@ fn check_impl( } if js_lint { - rustdoc_js::lint(outdir, librustdoc_path, tools_path)?; + rustdoc_js::lint(outdir, librustdoc_path, tools_path, bless)?; rustdoc_js::es_check(outdir, librustdoc_path)?; } @@ -720,21 +720,19 @@ impl ExtraCheckArg { if !self.auto { return true; } - let ext = match self.lang { - ExtraCheckLang::Py => ".py", - ExtraCheckLang::Cpp => ".cpp", - ExtraCheckLang::Shell => ".sh", - ExtraCheckLang::Js => ".js", + let exts: &[&str] = match self.lang { + ExtraCheckLang::Py => &[".py"], + ExtraCheckLang::Cpp => &[".cpp"], + ExtraCheckLang::Shell => &[".sh"], + ExtraCheckLang::Js => &[".js", ".ts"], ExtraCheckLang::Spellcheck => { - for dir in SPELLCHECK_DIRS { - if Path::new(filepath).starts_with(dir) { - return true; - } + if SPELLCHECK_DIRS.iter().any(|dir| Path::new(filepath).starts_with(dir)) { + return true; } - return false; + &[] } }; - filepath.ends_with(ext) + exts.iter().any(|ext| filepath.ends_with(ext)) } fn has_supported_kind(&self) -> bool { diff --git a/src/tools/tidy/src/extra_checks/rustdoc_js.rs b/src/tools/tidy/src/extra_checks/rustdoc_js.rs index 7708b128e23..a6c66b8be80 100644 --- a/src/tools/tidy/src/extra_checks/rustdoc_js.rs +++ b/src/tools/tidy/src/extra_checks/rustdoc_js.rs @@ -40,13 +40,18 @@ fn rustdoc_js_files(librustdoc_path: &Path) -> Vec<PathBuf> { return files; } -fn run_eslint(outdir: &Path, args: &[PathBuf], config_folder: PathBuf) -> Result<(), super::Error> { - let mut child = spawn_cmd( - Command::new(node_module_bin(outdir, "eslint")) - .arg("-c") - .arg(config_folder.join(".eslintrc.js")) - .args(args), - )?; +fn run_eslint( + outdir: &Path, + args: &[PathBuf], + config_folder: PathBuf, + bless: bool, +) -> Result<(), super::Error> { + let mut cmd = Command::new(node_module_bin(outdir, "eslint")); + if bless { + cmd.arg("--fix"); + } + cmd.arg("-c").arg(config_folder.join(".eslintrc.js")).args(args); + let mut child = spawn_cmd(&mut cmd)?; match child.wait() { Ok(exit_status) => { if exit_status.success() { @@ -62,16 +67,17 @@ pub(super) fn lint( outdir: &Path, librustdoc_path: &Path, tools_path: &Path, + bless: bool, ) -> Result<(), super::Error> { let files_to_check = rustdoc_js_files(librustdoc_path); println!("Running eslint on rustdoc JS files"); - run_eslint(outdir, &files_to_check, librustdoc_path.join("html/static"))?; + run_eslint(outdir, &files_to_check, librustdoc_path.join("html/static"), bless)?; - run_eslint(outdir, &[tools_path.join("rustdoc-js/tester.js")], tools_path.join("rustdoc-js"))?; run_eslint( outdir, - &[tools_path.join("rustdoc-gui/tester.js")], - tools_path.join("rustdoc-gui"), + &[tools_path.join("rustdoc-js/tester.js")], + tools_path.join("rustdoc-js"), + bless, )?; Ok(()) } diff --git a/src/tools/tidy/src/triagebot.rs b/src/tools/tidy/src/triagebot.rs index 305a0b4d264..6f25ed616fa 100644 --- a/src/tools/tidy/src/triagebot.rs +++ b/src/tools/tidy/src/triagebot.rs @@ -19,7 +19,12 @@ pub fn check(path: &Path, bad: &mut bool) { // Check [mentions."*"] sections, i.e. [mentions."compiler/rustc_const_eval/src/"] if let Some(Value::Table(mentions)) = config.get("mentions") { - for path_str in mentions.keys() { + for (entry_key, entry_val) in mentions.iter() { + // If the type is set to something other than "filename", then this is not a path. + if entry_val.get("type").is_some_and(|t| t.as_str().unwrap_or_default() != "filename") { + continue; + } + let path_str = entry_key; // Remove quotes from the path let clean_path = path_str.trim_matches('"'); let full_path = path.join(clean_path); diff --git a/src/tools/unicode-table-generator/src/cascading_map.rs b/src/tools/unicode-table-generator/src/cascading_map.rs index 78a7bba3208..56e6401908d 100644 --- a/src/tools/unicode-table-generator/src/cascading_map.rs +++ b/src/tools/unicode-table-generator/src/cascading_map.rs @@ -64,6 +64,7 @@ impl RawEmitter { writeln!(&mut self.file, "#[inline]").unwrap(); writeln!(&mut self.file, "pub const fn lookup(c: char) -> bool {{").unwrap(); + writeln!(&mut self.file, " debug_assert!(!c.is_ascii());").unwrap(); writeln!(&mut self.file, " match c as u32 >> 8 {{").unwrap(); for arm in arms { writeln!(&mut self.file, " {arm},").unwrap(); diff --git a/src/tools/unicode-table-generator/src/case_mapping.rs b/src/tools/unicode-table-generator/src/case_mapping.rs index 9c6454492e7..49aef3ec33e 100644 --- a/src/tools/unicode-table-generator/src/case_mapping.rs +++ b/src/tools/unicode-table-generator/src/case_mapping.rs @@ -6,24 +6,26 @@ use crate::{UnicodeData, fmt_list}; const INDEX_MASK: u32 = 1 << 22; -pub(crate) fn generate_case_mapping(data: &UnicodeData) -> String { +pub(crate) fn generate_case_mapping(data: &UnicodeData) -> (String, [usize; 2]) { let mut file = String::new(); write!(file, "const INDEX_MASK: u32 = 0x{INDEX_MASK:x};").unwrap(); file.push_str("\n\n"); file.push_str(HEADER.trim_start()); file.push('\n'); - file.push_str(&generate_tables("LOWER", &data.to_lower)); + let (lower_tables, lower_size) = generate_tables("LOWER", &data.to_lower); + file.push_str(&lower_tables); file.push_str("\n\n"); - file.push_str(&generate_tables("UPPER", &data.to_upper)); - file + let (upper_tables, upper_size) = generate_tables("UPPER", &data.to_upper); + file.push_str(&upper_tables); + (file, [lower_size, upper_size]) } -fn generate_tables(case: &str, data: &BTreeMap<u32, (u32, u32, u32)>) -> String { +fn generate_tables(case: &str, data: &BTreeMap<u32, [u32; 3]>) -> (String, usize) { let mut mappings = Vec::with_capacity(data.len()); let mut multis = Vec::new(); - for (&key, &(a, b, c)) in data.iter() { + for (&key, &[a, b, c]) in data.iter() { let key = char::from_u32(key).unwrap(); if key.is_ascii() { @@ -46,16 +48,31 @@ fn generate_tables(case: &str, data: &BTreeMap<u32, (u32, u32, u32)>) -> String } let mut tables = String::new(); - - write!(tables, "static {}CASE_TABLE: &[(char, u32)] = &[{}];", case, fmt_list(mappings)) - .unwrap(); + let mut size = 0; + + size += size_of_val(mappings.as_slice()); + write!( + tables, + "static {}CASE_TABLE: &[(char, u32); {}] = &[{}];", + case, + mappings.len(), + fmt_list(mappings), + ) + .unwrap(); tables.push_str("\n\n"); - write!(tables, "static {}CASE_TABLE_MULTI: &[[char; 3]] = &[{}];", case, fmt_list(multis)) - .unwrap(); - - tables + size += size_of_val(multis.as_slice()); + write!( + tables, + "static {}CASE_TABLE_MULTI: &[[char; 3]; {}] = &[{}];", + case, + multis.len(), + fmt_list(multis), + ) + .unwrap(); + + (tables, size) } struct CharEscape(char); diff --git a/src/tools/unicode-table-generator/src/main.rs b/src/tools/unicode-table-generator/src/main.rs index 38e5e8bbdb9..ded9205ffc4 100644 --- a/src/tools/unicode-table-generator/src/main.rs +++ b/src/tools/unicode-table-generator/src/main.rs @@ -72,6 +72,8 @@ //! or not. use std::collections::{BTreeMap, HashMap}; +use std::fmt; +use std::fmt::Write; use std::ops::Range; use ucd_parse::Codepoints; @@ -97,11 +99,11 @@ static PROPERTIES: &[&str] = &[ struct UnicodeData { ranges: Vec<(&'static str, Vec<Range<u32>>)>, - to_upper: BTreeMap<u32, (u32, u32, u32)>, - to_lower: BTreeMap<u32, (u32, u32, u32)>, + to_upper: BTreeMap<u32, [u32; 3]>, + to_lower: BTreeMap<u32, [u32; 3]>, } -fn to_mapping(origin: u32, codepoints: Vec<ucd_parse::Codepoint>) -> Option<(u32, u32, u32)> { +fn to_mapping(origin: u32, codepoints: Vec<ucd_parse::Codepoint>) -> Option<[u32; 3]> { let mut a = None; let mut b = None; let mut c = None; @@ -122,7 +124,7 @@ fn to_mapping(origin: u32, codepoints: Vec<ucd_parse::Codepoint>) -> Option<(u32 } } - Some((a.unwrap(), b.unwrap_or(0), c.unwrap_or(0))) + Some([a.unwrap(), b.unwrap_or(0), c.unwrap_or(0)]) } static UNICODE_DIRECTORY: &str = "unicode-downloads"; @@ -162,12 +164,12 @@ fn load_data() -> UnicodeData { if let Some(mapped) = row.simple_lowercase_mapping && mapped != row.codepoint { - to_lower.insert(row.codepoint.value(), (mapped.value(), 0, 0)); + to_lower.insert(row.codepoint.value(), [mapped.value(), 0, 0]); } if let Some(mapped) = row.simple_uppercase_mapping && mapped != row.codepoint { - to_upper.insert(row.codepoint.value(), (mapped.value(), 0, 0)); + to_upper.insert(row.codepoint.value(), [mapped.value(), 0, 0]); } } @@ -186,33 +188,20 @@ fn load_data() -> UnicodeData { } } - let mut properties: HashMap<&'static str, Vec<Range<u32>>> = properties + let mut properties: Vec<(&'static str, Vec<Range<u32>>)> = properties .into_iter() - .map(|(k, v)| { - ( - k, - v.into_iter() - .flat_map(|codepoints| match codepoints { - Codepoints::Single(c) => c - .scalar() - .map(|ch| ch as u32..ch as u32 + 1) - .into_iter() - .collect::<Vec<_>>(), - Codepoints::Range(c) => c - .into_iter() - .flat_map(|c| c.scalar().map(|ch| ch as u32..ch as u32 + 1)) - .collect::<Vec<_>>(), - }) - .collect::<Vec<Range<u32>>>(), - ) + .map(|(prop, codepoints)| { + let codepoints = codepoints + .into_iter() + .flatten() + .flat_map(|cp| cp.scalar()) + .filter(|c| !c.is_ascii()) + .map(u32::from) + .collect::<Vec<_>>(); + (prop, ranges_from_set(&codepoints)) }) .collect(); - for ranges in properties.values_mut() { - merge_ranges(ranges); - } - - let mut properties = properties.into_iter().collect::<Vec<_>>(); properties.sort_by_key(|p| p.0); UnicodeData { ranges: properties, to_lower, to_upper } } @@ -235,9 +224,14 @@ fn main() { let ranges_by_property = &unicode_data.ranges; if let Some(path) = test_path { - std::fs::write(&path, generate_tests(&write_location, ranges_by_property)).unwrap(); + std::fs::write(&path, generate_tests(&unicode_data).unwrap()).unwrap(); } + let mut table_file = String::new(); + table_file.push_str( + "//! This file is generated by `./x run src/tools/unicode-table-generator`; do not edit manually!\n", + ); + let mut total_bytes = 0; let mut modules = Vec::new(); for (property, ranges) in ranges_by_property { @@ -251,8 +245,8 @@ fn main() { } modules.push((property.to_lowercase().to_string(), emitter.file)); - println!( - "{:15}: {} bytes, {} codepoints in {} ranges ({} - {}) using {}", + table_file.push_str(&format!( + "// {:16}: {:5} bytes, {:6} codepoints in {:3} ranges (U+{:06X} - U+{:06X}) using {}\n", property, emitter.bytes_used, datapoints, @@ -260,15 +254,15 @@ fn main() { ranges.first().unwrap().start, ranges.last().unwrap().end, emitter.desc, - ); + )); total_bytes += emitter.bytes_used; } - - let mut table_file = String::new(); - - table_file.push_str( - "///! This file is generated by `./x run src/tools/unicode-table-generator`; do not edit manually!\n", - ); + let (conversions, sizes) = case_mapping::generate_case_mapping(&unicode_data); + for (name, size) in ["to_lower", "to_upper"].iter().zip(sizes) { + table_file.push_str(&format!("// {:16}: {:5} bytes\n", name, size)); + total_bytes += size; + } + table_file.push_str(&format!("// {:16}: {:5} bytes\n", "Total", total_bytes)); // Include the range search function table_file.push('\n'); @@ -279,7 +273,7 @@ fn main() { table_file.push('\n'); - modules.push((String::from("conversions"), case_mapping::generate_case_mapping(&unicode_data))); + modules.push((String::from("conversions"), conversions)); for (name, contents) in modules { table_file.push_str("#[rustfmt::skip]\n"); @@ -295,8 +289,6 @@ fn main() { } std::fs::write(&write_location, format!("{}\n", table_file.trim_end())).unwrap(); - - println!("Total table sizes: {total_bytes} bytes"); } fn version() -> String { @@ -336,110 +328,96 @@ fn fmt_list<V: std::fmt::Debug>(values: impl IntoIterator<Item = V>) -> String { out } -fn generate_tests(data_path: &str, ranges: &[(&str, Vec<Range<u32>>)]) -> String { +fn generate_tests(data: &UnicodeData) -> Result<String, fmt::Error> { let mut s = String::new(); - s.push_str("#![allow(incomplete_features, unused)]\n"); - s.push_str("#![feature(const_generics)]\n\n"); - s.push_str("\n#[allow(unused)]\nuse std::hint;\n"); - s.push_str(&format!("#[path = \"{data_path}\"]\n")); - s.push_str("mod unicode_data;\n\n"); - - s.push_str("\nfn main() {\n"); - - for (property, ranges) in ranges { - s.push_str(&format!(r#" println!("Testing {property}");"#)); - s.push('\n'); - s.push_str(&format!(" {}_true();\n", property.to_lowercase())); - s.push_str(&format!(" {}_false();\n", property.to_lowercase())); - let mut is_true = Vec::new(); - let mut is_false = Vec::new(); - for ch_num in 0..(std::char::MAX as u32) { - if std::char::from_u32(ch_num).is_none() { - continue; - } - if ranges.iter().any(|r| r.contains(&ch_num)) { - is_true.push(ch_num); - } else { - is_false.push(ch_num); - } - } - - s.push_str(&format!(" fn {}_true() {{\n", property.to_lowercase())); - generate_asserts(&mut s, property, &is_true, true); - s.push_str(" }\n\n"); - s.push_str(&format!(" fn {}_false() {{\n", property.to_lowercase())); - generate_asserts(&mut s, property, &is_false, false); - s.push_str(" }\n\n"); + writeln!(s, "#![feature(core_intrinsics)]")?; + writeln!(s, "#![allow(internal_features, dead_code)]")?; + writeln!(s, "// ignore-tidy-filelength")?; + writeln!(s, "use std::intrinsics;")?; + writeln!(s, "mod unicode_data;")?; + writeln!(s, "fn main() {{")?; + for (property, ranges) in &data.ranges { + let prop = property.to_lowercase(); + writeln!(s, r#" println!("Testing {prop}");"#)?; + writeln!(s, " {prop}_true();")?; + writeln!(s, " {prop}_false();")?; + let (is_true, is_false): (Vec<_>, Vec<_>) = (char::MIN..=char::MAX) + .filter(|c| !c.is_ascii()) + .map(u32::from) + .partition(|c| ranges.iter().any(|r| r.contains(c))); + + writeln!(s, " fn {prop}_true() {{")?; + generate_asserts(&mut s, &prop, &is_true, true)?; + writeln!(s, " }}")?; + + writeln!(s, " fn {prop}_false() {{")?; + generate_asserts(&mut s, &prop, &is_false, false)?; + writeln!(s, " }}")?; } - s.push('}'); - s -} - -fn generate_asserts(s: &mut String, property: &str, points: &[u32], truthy: bool) { - for range in ranges_from_set(points) { - if range.end == range.start + 1 { - s.push_str(&format!( - " assert!({}unicode_data::{}::lookup({:?}), \"{}\");\n", - if truthy { "" } else { "!" }, - property.to_lowercase(), - std::char::from_u32(range.start).unwrap(), - range.start, - )); - } else { - s.push_str(&format!(" for chn in {range:?}u32 {{\n")); - s.push_str(&format!( - " assert!({}unicode_data::{}::lookup(std::char::from_u32(chn).unwrap()), \"{{:?}}\", chn);\n", - if truthy { "" } else { "!" }, - property.to_lowercase(), - )); - s.push_str(" }\n"); + for (name, conversion) in ["to_lower", "to_upper"].iter().zip([&data.to_lower, &data.to_upper]) + { + writeln!(s, r#" println!("Testing {name}");"#)?; + for (c, mapping) in conversion { + let c = char::from_u32(*c).unwrap(); + let mapping = mapping.map(|c| char::from_u32(c).unwrap()); + writeln!( + s, + r#" assert_eq!(unicode_data::conversions::{name}({c:?}), {mapping:?});"# + )?; + } + let unmapped: Vec<_> = (char::MIN..=char::MAX) + .filter(|c| !c.is_ascii()) + .map(u32::from) + .filter(|c| !conversion.contains_key(c)) + .collect(); + let unmapped_ranges = ranges_from_set(&unmapped); + for range in unmapped_ranges { + let start = char::from_u32(range.start).unwrap(); + let end = char::from_u32(range.end - 1).unwrap(); + writeln!(s, " for c in {start:?}..={end:?} {{")?; + writeln!( + s, + r#" assert_eq!(unicode_data::conversions::{name}(c), [c, '\0', '\0']);"# + )?; + + writeln!(s, " }}")?; } } -} -fn ranges_from_set(set: &[u32]) -> Vec<Range<u32>> { - let mut ranges = set.iter().map(|e| (*e)..(*e + 1)).collect::<Vec<Range<u32>>>(); - merge_ranges(&mut ranges); - ranges + writeln!(s, "}}")?; + Ok(s) } -fn merge_ranges(ranges: &mut Vec<Range<u32>>) { - loop { - let mut new_ranges = Vec::new(); - let mut idx_iter = 0..(ranges.len() - 1); - let mut should_insert_last = true; - while let Some(idx) = idx_iter.next() { - let cur = ranges[idx].clone(); - let next = ranges[idx + 1].clone(); - if cur.end == next.start { - if idx_iter.next().is_none() { - // We're merging the last element - should_insert_last = false; - } - new_ranges.push(cur.start..next.end); - } else { - // We're *not* merging the last element - should_insert_last = true; - new_ranges.push(cur); +fn generate_asserts( + s: &mut String, + prop: &str, + points: &[u32], + truthy: bool, +) -> Result<(), fmt::Error> { + let truthy = if truthy { "" } else { "!" }; + for range in ranges_from_set(points) { + let start = char::from_u32(range.start).unwrap(); + let end = char::from_u32(range.end - 1).unwrap(); + match range.len() { + 1 => writeln!(s, " assert!({truthy}unicode_data::{prop}::lookup({start:?}));")?, + _ => { + writeln!(s, " for c in {start:?}..={end:?} {{")?; + writeln!(s, " assert!({truthy}unicode_data::{prop}::lookup(c));")?; + writeln!(s, " }}")?; } } - if should_insert_last { - new_ranges.push(ranges.last().unwrap().clone()); - } - if new_ranges.len() == ranges.len() { - *ranges = new_ranges; - break; - } else { - *ranges = new_ranges; - } } + Ok(()) +} - let mut last_end = None; - for range in ranges { - if let Some(last) = last_end { - assert!(range.start > last, "{range:?}"); - } - last_end = Some(range.end); - } +/// Group the elements of `set` into contigous ranges +fn ranges_from_set(set: &[u32]) -> Vec<Range<u32>> { + set.chunk_by(|a, b| a + 1 == *b) + .map(|chunk| { + let start = *chunk.first().unwrap(); + let end = *chunk.last().unwrap(); + start..(end + 1) + }) + .collect() } diff --git a/src/tools/unicode-table-generator/src/raw_emitter.rs b/src/tools/unicode-table-generator/src/raw_emitter.rs index 03ed9499e26..297965615c1 100644 --- a/src/tools/unicode-table-generator/src/raw_emitter.rs +++ b/src/tools/unicode-table-generator/src/raw_emitter.rs @@ -98,6 +98,7 @@ impl RawEmitter { self.blank_line(); writeln!(&mut self.file, "pub const fn lookup(c: char) -> bool {{").unwrap(); + writeln!(&mut self.file, " debug_assert!(!c.is_ascii());").unwrap(); if first_code_point > 0x7f { writeln!(&mut self.file, " (c as u32) >= {first_code_point:#04x} &&").unwrap(); } diff --git a/src/tools/unicode-table-generator/src/skiplist.rs b/src/tools/unicode-table-generator/src/skiplist.rs index 34c9802e122..660a8f342f7 100644 --- a/src/tools/unicode-table-generator/src/skiplist.rs +++ b/src/tools/unicode-table-generator/src/skiplist.rs @@ -99,6 +99,7 @@ impl RawEmitter { if first_code_point > 0x7f { writeln!(&mut self.file, "#[inline]").unwrap(); writeln!(&mut self.file, "pub fn lookup(c: char) -> bool {{").unwrap(); + writeln!(&mut self.file, " debug_assert!(!c.is_ascii());").unwrap(); writeln!(&mut self.file, " (c as u32) >= {first_code_point:#04x} && lookup_slow(c)") .unwrap(); writeln!(&mut self.file, "}}").unwrap(); @@ -107,6 +108,7 @@ impl RawEmitter { writeln!(&mut self.file, "fn lookup_slow(c: char) -> bool {{").unwrap(); } else { writeln!(&mut self.file, "pub fn lookup(c: char) -> bool {{").unwrap(); + writeln!(&mut self.file, " debug_assert!(!c.is_ascii());").unwrap(); } writeln!(&mut self.file, " const {{").unwrap(); writeln!( diff --git a/tests/assembly-llvm/breakpoint.rs b/tests/assembly-llvm/breakpoint.rs index e0cc2d1eebb..d119b68e899 100644 --- a/tests/assembly-llvm/breakpoint.rs +++ b/tests/assembly-llvm/breakpoint.rs @@ -9,6 +9,7 @@ // CHECK-LABEL: use_bp // aarch64: brk #0xf000 // x86_64: int3 +#[inline(never)] pub fn use_bp() { core::arch::breakpoint(); } diff --git a/tests/assembly-llvm/loongarch-float-struct-abi.rs b/tests/assembly-llvm/loongarch-float-struct-abi.rs new file mode 100644 index 00000000000..4991004fc05 --- /dev/null +++ b/tests/assembly-llvm/loongarch-float-struct-abi.rs @@ -0,0 +1,134 @@ +//@ add-core-stubs +//@ assembly-output: emit-asm +//@ compile-flags: -Copt-level=3 --target loongarch64-unknown-linux-gnu +//@ needs-llvm-components: loongarch + +#![feature(no_core, lang_items)] +#![no_std] +#![no_core] +#![crate_type = "lib"] + +extern crate minicore; +use minicore::*; + +#[repr(C, align(64))] +struct Aligned(f64); + +#[repr(C)] +struct Padded(u8, Aligned); + +#[repr(C, packed)] +struct Packed(u8, f32); + +impl Copy for Aligned {} +impl Copy for Padded {} +impl Copy for Packed {} + +extern "C" { + fn take_padded(x: Padded); + fn get_padded() -> Padded; + fn take_packed(x: Packed); + fn get_packed() -> Packed; +} + +// CHECK-LABEL: pass_padded +#[unsafe(no_mangle)] +extern "C" fn pass_padded(out: &mut Padded, x: Padded) { + // CHECK: st.b $a1, $a0, 0 + // CHECK-NEXT: fst.d $fa0, $a0, 64 + // CHECK-NEXT: ret + *out = x; +} + +// CHECK-LABEL: ret_padded +#[unsafe(no_mangle)] +extern "C" fn ret_padded(x: &Padded) -> Padded { + // CHECK: fld.d $fa0, $a0, 64 + // CHECK-NEXT: ld.b $a0, $a0, 0 + // CHECK-NEXT: ret + *x +} + +#[unsafe(no_mangle)] +extern "C" fn call_padded(x: &Padded) { + // CHECK: fld.d $fa0, $a0, 64 + // CHECK-NEXT: ld.b $a0, $a0, 0 + // CHECK-NEXT: pcaddu18i $t8, %call36(take_padded) + // CHECK-NEXT: jr $t8 + unsafe { + take_padded(*x); + } +} + +#[unsafe(no_mangle)] +extern "C" fn receive_padded(out: &mut Padded) { + // CHECK: addi.d $sp, $sp, -16 + // CHECK-NEXT: .cfi_def_cfa_offset 16 + // CHECK-NEXT: st.d $ra, $sp, [[#%d,RA_SPILL:]] + // CHECK-NEXT: st.d [[TEMP:.*]], $sp, [[#%d,TEMP_SPILL:]] + // CHECK-NEXT: .cfi_offset 1, [[#%d,RA_SPILL - 16]] + // CHECK-NEXT: .cfi_offset [[#%d,TEMP_NUM:]], [[#%d,TEMP_SPILL - 16]] + // CHECK-NEXT: move [[TEMP]], $a0 + // CHECK-NEXT: pcaddu18i $ra, %call36(get_padded) + // CHECK-NEXT: jirl $ra, $ra, 0 + // CHECK-NEXT: st.b $a0, [[TEMP]], 0 + // CHECK-NEXT: fst.d $fa0, [[TEMP]], 64 + // CHECK-NEXT: ld.d [[TEMP]], $sp, [[#%d,TEMP_SPILL]] + // CHECK-NEXT: ld.d $ra, $sp, [[#%d,RA_SPILL]] + // CHECK: addi.d $sp, $sp, 16 + // CHECK: ret + unsafe { + *out = get_padded(); + } +} + +// CHECK-LABEL: pass_packed +#[unsafe(no_mangle)] +extern "C" fn pass_packed(out: &mut Packed, x: Packed) { + // CHECK: st.b $a1, $a0, 0 + // CHECK-NEXT: fst.s $fa0, $a0, 1 + // CHECK-NEXT: ret + *out = x; +} + +// CHECK-LABEL: ret_packed +#[unsafe(no_mangle)] +extern "C" fn ret_packed(x: &Packed) -> Packed { + // CHECK: fld.s $fa0, $a0, 1 + // CHECK-NEXT: ld.b $a0, $a0, 0 + // CHECK-NEXT: ret + *x +} + +#[unsafe(no_mangle)] +extern "C" fn call_packed(x: &Packed) { + // CHECK: fld.s $fa0, $a0, 1 + // CHECK-NEXT: ld.b $a0, $a0, 0 + // CHECK-NEXT: pcaddu18i $t8, %call36(take_packed) + // CHECK-NEXT: jr $t8 + unsafe { + take_packed(*x); + } +} + +#[unsafe(no_mangle)] +extern "C" fn receive_packed(out: &mut Packed) { + // CHECK: addi.d $sp, $sp, -16 + // CHECK-NEXT: .cfi_def_cfa_offset 16 + // CHECK-NEXT: st.d $ra, $sp, [[#%d,RA_SPILL:]] + // CHECK-NEXT: st.d [[TEMP:.*]], $sp, [[#%d,TEMP_SPILL:]] + // CHECK-NEXT: .cfi_offset 1, [[#%d,RA_SPILL - 16]] + // CHECK-NEXT: .cfi_offset [[#%d,TEMP_NUM:]], [[#%d,TEMP_SPILL - 16]] + // CHECK-NEXT: move [[TEMP]], $a0 + // CHECK-NEXT: pcaddu18i $ra, %call36(get_packed) + // CHECK-NEXT: jirl $ra, $ra, 0 + // CHECK-NEXT: st.b $a0, [[TEMP]], 0 + // CHECK-NEXT: fst.s $fa0, [[TEMP]], 1 + // CHECK-NEXT: ld.d [[TEMP]], $sp, [[#%d,TEMP_SPILL]] + // CHECK-NEXT: ld.d $ra, $sp, [[#%d,RA_SPILL]] + // CHECK: addi.d $sp, $sp, 16 + // CHECK: ret + unsafe { + *out = get_packed(); + } +} diff --git a/tests/assembly-llvm/simd/reduce-fadd-unordered.rs b/tests/assembly-llvm/simd/reduce-fadd-unordered.rs index e872826f6ef..fdd03639da0 100644 --- a/tests/assembly-llvm/simd/reduce-fadd-unordered.rs +++ b/tests/assembly-llvm/simd/reduce-fadd-unordered.rs @@ -16,6 +16,7 @@ use std::simd::*; // It would emit about an extra fadd, depending on the architecture. // CHECK-LABEL: reduce_fadd_negative_zero +#[inline(never)] pub unsafe fn reduce_fadd_negative_zero(v: f32x4) -> f32 { // x86_64: addps // x86_64-NEXT: movshdup diff --git a/tests/auxiliary/minicore.rs b/tests/auxiliary/minicore.rs index da880100a10..4f4c653cb46 100644 --- a/tests/auxiliary/minicore.rs +++ b/tests/auxiliary/minicore.rs @@ -179,7 +179,14 @@ impl Add<isize> for isize { #[lang = "sync"] trait Sync {} -impl Sync for u8 {} +impl_marker_trait!( + Sync => [ + char, bool, + isize, i8, i16, i32, i64, i128, + usize, u8, u16, u32, u64, u128, + f16, f32, f64, f128, + ] +); #[lang = "drop_in_place"] fn drop_in_place<T>(_: *mut T) {} diff --git a/tests/codegen-llvm/amdgpu-addrspacecast.rs b/tests/codegen-llvm/amdgpu-addrspacecast.rs index 7fe630a7efa..829133de00d 100644 --- a/tests/codegen-llvm/amdgpu-addrspacecast.rs +++ b/tests/codegen-llvm/amdgpu-addrspacecast.rs @@ -16,3 +16,12 @@ pub fn ref_of_local(f: fn(&i32)) { let i = 0; f(&i); } + +// CHECK-LABEL: @ref_of_global +// CHECK: addrspacecast (ptr addrspace(1) @I to ptr) +#[no_mangle] +pub fn ref_of_global(f: fn(&i32)) { + #[no_mangle] + static I: i32 = 0; + f(&I); +} diff --git a/tests/codegen-llvm/cast-target-abi.rs b/tests/codegen-llvm/cast-target-abi.rs index 28d3ad5f61c..090de00df93 100644 --- a/tests/codegen-llvm/cast-target-abi.rs +++ b/tests/codegen-llvm/cast-target-abi.rs @@ -171,7 +171,15 @@ pub extern "C" fn receives_doubledouble(x: DoubleDouble) { // CHECK: [[RUST_ALLOCA:%.+]] = alloca [16 x i8], align [[RUST_ALIGN:8]] - // CHECK: store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] + // aarch64: store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] + // loongarch64: [[ABI_VALUE_0:%.+]] = extractvalue [[ABI_TYPE]] [[ABI_VALUE:%.+]], 0 + // loongarch64: [[ABI_VALUE_1:%.+]] = extractvalue [[ABI_TYPE]] [[ABI_VALUE:%.+]], 1 + // loongarch64: store double [[ABI_VALUE_0]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] + // loongarch64: [[ABI_ALLOCA_1:%.+]] = getelementptr inbounds i8, ptr [[ABI_ALLOCA]], i64 8 + // loongarch64: store double [[ABI_VALUE_1]], ptr [[ABI_ALLOCA_1]], align [[ABI_ALIGN]] + // powerpc64: store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] + // sparc64: store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] + // x86_64: store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] // CHECK: call void @llvm.memcpy.{{.+}}(ptr align [[RUST_ALIGN]] [[RUST_ALLOCA]], ptr align [[ABI_ALIGN]] [[ABI_ALLOCA]], i64 16, i1 false) } @@ -190,7 +198,11 @@ pub extern "C" fn returns_doubledouble() -> DoubleDouble { // x86_64: [[ABI_ALLOCA:%.+]] = alloca [16 x i8], align [[ABI_ALIGN:8]] // aarch64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:\[2 x double\]]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] - // loongarch64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:{ double, double }]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] + // loongarch64: [[ABI_VALUE_0:%.+]] = load double, ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] + // loongarch64: [[ABI_ALLOCA_1:%.+]] = getelementptr inbounds i8, ptr [[ABI_ALLOCA]], i64 8 + // loongarch64: [[ABI_VALUE_1:%.+]] = load double, ptr [[ABI_ALLOCA_1]], align [[ABI_ALIGN]] + // loongarch64: [[ABI_VALUE_2:%.+]] = insertvalue [[ABI_TYPE:{ double, double }]] poison, double [[ABI_VALUE_0]], 0 + // loongarch64: [[ABI_VALUE:%.+]] = insertvalue { double, double } [[ABI_VALUE_2]], double [[ABI_VALUE_1]], 1 // sparc64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:{ double, double }]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] // x86_64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:{ double, double }]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] @@ -269,7 +281,11 @@ pub extern "C" fn receives_doublefloat(x: DoubleFloat) { // x86_64: [[RUST_ALLOCA:%.+]] = alloca [16 x i8], align [[RUST_ALIGN:8]] // aarch64: store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] - // loongarch64: store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] + // loongarch64: [[ABI_VALUE_0:%.+]] = extractvalue { double, float } [[ABI_VALUE]], 0 + // loongarch64: [[ABI_VALUE_1:%.+]] = extractvalue { double, float } [[ABI_VALUE]], 1 + // loongarch64: store double [[ABI_VALUE_0]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] + // loongarch64: [[ABI_ALLOCA_1:%.+]] = getelementptr inbounds i8, ptr [[ABI_ALLOCA]], i64 8 + // loongarch64: store float [[ABI_VALUE_1]], ptr [[ABI_ALLOCA_1]], align [[ABI_ALIGN]] // powerpc64: store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] // x86_64: store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] @@ -297,7 +313,11 @@ pub extern "C" fn returns_doublefloat() -> DoubleFloat { // x86_64: [[ABI_ALLOCA:%.+]] = alloca [16 x i8], align [[ABI_ALIGN:8]] // aarch64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:\[2 x i64\]]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] - // loongarch64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:{ double, float }]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] + // loongarch64: [[ABI_VALUE_0:%.+]] = load double, ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] + // loongarch64: [[ABI_ALLOCA_1:%.+]] = getelementptr inbounds i8, ptr [[ABI_ALLOCA]], i64 8 + // loongarch64: [[ABI_VALUE_1:%.+]] = load float, ptr [[ABI_ALLOCA_1]], align [[ABI_ALIGN]] + // loongarch64: [[ABI_VALUE_2:%.+]] = insertvalue [[ABI_TYPE:{ double, float }]] poison, double [[ABI_VALUE_0]], 0 + // loongarch64: [[ABI_VALUE:%.+]] = insertvalue { double, float } [[ABI_VALUE_2]], float [[ABI_VALUE_1]], 1 // x86_64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:{ double, double }]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] // aarch64: ret [[ABI_TYPE]] [[ABI_VALUE]] @@ -429,7 +449,11 @@ pub fn call_doubledouble() { // CHECK: call void @llvm.memcpy.{{.+}}(ptr align [[ABI_ALIGN]] [[ABI_ALLOCA]], ptr align [[RUST_ALIGN]] [[RUST_ALLOCA]], i64 16, i1 false) // aarch64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:\[2 x double\]]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] - // loongarch64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:{ double, double }]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] + // loongarch64: [[ABI_VALUE_0:%.+]] = load double, ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] + // loongarch64: [[ABI_ALLOCA_1:%.+]] = getelementptr inbounds i8, ptr [[ABI_ALLOCA]], i64 8 + // loongarch64: [[ABI_VALUE_1:%.+]] = load double, ptr [[ABI_ALLOCA_1]], align [[ABI_ALIGN]] + // loongarch64: [[ABI_VALUE_2:%.+]] = insertvalue [[ABI_TYPE:{ double, double }]] poison, double [[ABI_VALUE_0]], 0 + // loongarch64: [[ABI_VALUE:%.+]] = insertvalue { double, double } [[ABI_VALUE_2]], double [[ABI_VALUE_1]], 1 // powerpc64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:\[2 x i64\]]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] // sparc64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:{ double, double }]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] // x86_64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:{ double, double }]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] @@ -465,7 +489,11 @@ pub fn return_doubledouble() -> DoubleDouble { // x86_64: [[ABI_VALUE:%.+]] = call [[ABI_TYPE:{ double, double }]] @returns_doubledouble() // aarch64: store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] - // loongarch64: store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] + // loongarch64: [[ABI_VALUE_0:%.+]] = extractvalue { double, double } [[ABI_VALUE]], 0 + // loongarch64: [[ABI_VALUE_1:%.+]] = extractvalue { double, double } [[ABI_VALUE]], 1 + // loongarch64: store double [[ABI_VALUE_0]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] + // loongarch64: [[ABI_ALLOCA_1:%.+]] = getelementptr inbounds i8, ptr [[ABI_ALLOCA]], i64 8 + // loongarch64: store double [[ABI_VALUE_1]], ptr [[ABI_ALLOCA_1]], align [[ABI_ALIGN]] // sparc64: store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] // x86_64: store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] @@ -500,7 +528,11 @@ pub fn call_doublefloat() { // x86_64: call void @llvm.memcpy.{{.+}}(ptr align [[ABI_ALIGN]] [[ABI_ALLOCA]], ptr align [[RUST_ALIGN]] [[RUST_ALLOCA]], i64 16, i1 false) // aarch64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:\[2 x i64\]]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] - // loongarch64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:{ double, float }]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] + // loongarch64: [[ABI_VALUE_0:%.+]] = load double, ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] + // loongarch64: [[ABI_ALLOCA_1:%.+]] = getelementptr inbounds i8, ptr [[ABI_ALLOCA]], i64 8 + // loongarch64: [[ABI_VALUE_1:%.+]] = load float, ptr [[ABI_ALLOCA_1]], align [[ABI_ALIGN]] + // loongarch64: [[ABI_VALUE_2:%.+]] = insertvalue [[ABI_TYPE:{ double, float }]] poison, double [[ABI_VALUE_0]], 0 + // loongarch64: [[ABI_VALUE:%.+]] = insertvalue { double, float } [[ABI_VALUE_2]], float [[ABI_VALUE_1]], 1 // powerpc64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:\[2 x i64\]]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] // x86_64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:{ double, double }]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] @@ -540,7 +572,11 @@ pub fn return_doublefloat() -> DoubleFloat { // x86_64: [[ABI_VALUE:%.+]] = call [[ABI_TYPE:{ double, double }]] @returns_doublefloat() // aarch64: store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] - // loongarch64: store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] + // loongarch64: [[ABI_VALUE_0:%.+]] = extractvalue { double, float } [[ABI_VALUE]], 0 + // loongarch64: [[ABI_VALUE_1:%.+]] = extractvalue { double, float } [[ABI_VALUE]], 1 + // loongarch64: store double [[ABI_VALUE_0]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] + // loongarch64: [[ABI_ALLOCA_1:%.+]] = getelementptr inbounds i8, ptr [[ABI_ALLOCA]], i64 8 + // loongarch64: store float [[ABI_VALUE_1]], ptr [[ABI_ALLOCA_1]], align [[ABI_ALIGN]] // x86_64: store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] // aarch64: call void @llvm.memcpy.{{.+}}(ptr align [[RUST_ALIGN]] [[RUST_ALLOCA]], ptr align [[ABI_ALIGN]] [[ABI_ALLOCA]], i64 16, i1 false) diff --git a/tests/codegen-llvm/cffi/c-variadic-ffi.rs b/tests/codegen-llvm/cffi/c-variadic-ffi.rs index 3e99c9fb84e..1dee477e9ed 100644 --- a/tests/codegen-llvm/cffi/c-variadic-ffi.rs +++ b/tests/codegen-llvm/cffi/c-variadic-ffi.rs @@ -14,7 +14,7 @@ //@[arm32] needs-llvm-components: arm #![crate_type = "lib"] #![feature(no_core)] -#![feature(extended_varargs_abi_support, extern_system_varargs)] +#![feature(extern_system_varargs)] #![no_core] extern crate minicore; diff --git a/tests/codegen-llvm/cross-crate-inlining/auxiliary/leaf.rs b/tests/codegen-llvm/cross-crate-inlining/auxiliary/leaf.rs index d059a3d0a73..7b5679c3f4d 100644 --- a/tests/codegen-llvm/cross-crate-inlining/auxiliary/leaf.rs +++ b/tests/codegen-llvm/cross-crate-inlining/auxiliary/leaf.rs @@ -18,3 +18,8 @@ pub fn stem_fn() -> String { fn inner() -> String { String::from("test") } + +// This function's optimized MIR contains a call, but it is to an intrinsic. +pub fn leaf_with_intrinsic(a: &[u64; 2], b: &[u64; 2]) -> bool { + a == b +} diff --git a/tests/codegen-llvm/cross-crate-inlining/leaf-inlining.rs b/tests/codegen-llvm/cross-crate-inlining/leaf-inlining.rs index 37132312ca9..5e7912791ad 100644 --- a/tests/codegen-llvm/cross-crate-inlining/leaf-inlining.rs +++ b/tests/codegen-llvm/cross-crate-inlining/leaf-inlining.rs @@ -18,3 +18,10 @@ pub fn stem_outer() -> String { // CHECK: call {{.*}}stem_fn leaf::stem_fn() } + +// Check that we inline functions that call intrinsics +#[no_mangle] +pub fn leaf_with_intrinsic_outer(a: &[u64; 2], b: &[u64; 2]) -> bool { + // CHECK-NOT: call {{.*}}leaf_with_intrinsic + leaf::leaf_with_intrinsic(a, b) +} diff --git a/tests/codegen-llvm/default-visibility.rs b/tests/codegen-llvm/default-visibility.rs index 88ff9fee254..28238e5ef12 100644 --- a/tests/codegen-llvm/default-visibility.rs +++ b/tests/codegen-llvm/default-visibility.rs @@ -32,6 +32,7 @@ pub static tested_symbol: [u8; 6] = *b"foobar"; // INTERPOSABLE: @{{.*}}default_visibility{{.*}}tested_symbol{{.*}} = constant // DEFAULT: @{{.*}}default_visibility{{.*}}tested_symbol{{.*}} = constant +#[inline(never)] pub fn do_memcmp(left: &[u8], right: &[u8]) -> i32 { left.cmp(right) as i32 } diff --git a/tests/codegen-llvm/gpu-kernel-abi.rs b/tests/codegen-llvm/gpu-kernel-abi.rs index 8ac376d9338..d5a357ef655 100644 --- a/tests/codegen-llvm/gpu-kernel-abi.rs +++ b/tests/codegen-llvm/gpu-kernel-abi.rs @@ -1,7 +1,9 @@ // Checks that the gpu-kernel calling convention correctly translates to LLVM calling conventions. //@ add-core-stubs -//@ revisions: nvptx +//@ revisions: amdgpu nvptx +//@ [amdgpu] compile-flags: --crate-type=rlib --target=amdgcn-amd-amdhsa -Ctarget-cpu=gfx900 +//@ [amdgpu] needs-llvm-components: amdgpu //@ [nvptx] compile-flags: --crate-type=rlib --target=nvptx64-nvidia-cuda //@ [nvptx] needs-llvm-components: nvptx #![feature(no_core, lang_items, abi_gpu_kernel)] @@ -10,6 +12,7 @@ extern crate minicore; use minicore::*; +// amdgpu: define amdgpu_kernel void @fun(i32 // nvptx: define ptx_kernel void @fun(i32 #[no_mangle] pub extern "gpu-kernel" fn fun(_: i32) {} diff --git a/tests/codegen-llvm/issues/issue-118306.rs b/tests/codegen-llvm/issues/issue-118306.rs index f12dc7cdfe2..934a7687b60 100644 --- a/tests/codegen-llvm/issues/issue-118306.rs +++ b/tests/codegen-llvm/issues/issue-118306.rs @@ -11,7 +11,7 @@ pub fn branchy(input: u64) -> u64 { // CHECK-LABEL: @branchy( // CHECK-NEXT: start: // CHECK-NEXT: [[_2:%.*]] = and i64 [[INPUT:%.*]], 3 - // CHECK-NEXT: [[SWITCH_GEP:%.*]] = getelementptr inbounds{{( nuw)?}} [4 x i64], ptr @switch.table.branchy, i64 0, i64 [[_2]] + // CHECK-NEXT: [[SWITCH_GEP:%.*]] = getelementptr inbounds{{( nuw)?}} {{\[4 x i64\]|i64}}, ptr @switch.table.branchy{{(, i64 0)?}}, i64 [[_2]] // CHECK-NEXT: [[SWITCH_LOAD:%.*]] = load i64, ptr [[SWITCH_GEP]] // CHECK-NEXT: ret i64 [[SWITCH_LOAD]] match input % 4 { diff --git a/tests/codegen-llvm/loongarch-abi/cast-local-large-enough.rs b/tests/codegen-llvm/loongarch-abi/cast-local-large-enough.rs new file mode 100644 index 00000000000..e5a0e4cd3a2 --- /dev/null +++ b/tests/codegen-llvm/loongarch-abi/cast-local-large-enough.rs @@ -0,0 +1,44 @@ +//@ add-core-stubs +//@ compile-flags: -Copt-level=0 -Cdebuginfo=0 --target loongarch64-unknown-linux-gnu +//@ needs-llvm-components: loongarch + +#![feature(no_core, lang_items)] +#![no_std] +#![no_core] +#![crate_type = "lib"] + +extern crate minicore; +use minicore::*; + +#[repr(C, align(64))] +struct Aligned(f64); + +#[repr(C, align(64))] +struct AlignedPair(f32, f64); + +impl Copy for Aligned {} +impl Copy for AlignedPair {} + +// CHECK-LABEL: define double @read_aligned +#[unsafe(no_mangle)] +pub extern "C" fn read_aligned(x: &Aligned) -> Aligned { + // CHECK: %[[TEMP:.*]] = alloca [64 x i8], align 64 + // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 64 %[[TEMP]], ptr align 64 %[[PTR:.*]], i64 64, i1 false) + // CHECK-NEXT: %[[RES:.*]] = load double, ptr %[[TEMP]], align 64 + // CHECK-NEXT: ret double %[[RES]] + *x +} + +// CHECK-LABEL: define { float, double } @read_aligned_pair +#[unsafe(no_mangle)] +pub extern "C" fn read_aligned_pair(x: &AlignedPair) -> AlignedPair { + // CHECK: %[[TEMP:.*]] = alloca [64 x i8], align 64 + // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 64 %[[TEMP]], ptr align 64 %[[PTR:.*]], i64 64, i1 false) + // CHECK-NEXT: %[[FIRST:.*]] = load float, ptr %[[TEMP]], align 64 + // CHECK-NEXT: %[[SECOND_PTR:.*]] = getelementptr inbounds i8, ptr %[[TEMP]], i64 8 + // CHECK-NEXT: %[[SECOND:.*]] = load double, ptr %[[SECOND_PTR]], align 8 + // CHECK-NEXT: %[[RES1:.*]] = insertvalue { float, double } poison, float %[[FIRST]], 0 + // CHECK-NEXT: %[[RES2:.*]] = insertvalue { float, double } %[[RES1]], double %[[SECOND]], 1 + // CHECK-NEXT: ret { float, double } %[[RES2]] + *x +} diff --git a/tests/codegen-llvm/sanitizer/address-sanitizer-globals-tracking.rs b/tests/codegen-llvm/sanitizer/address-sanitizer-globals-tracking.rs index 642bf5e7576..ada525b6c80 100644 --- a/tests/codegen-llvm/sanitizer/address-sanitizer-globals-tracking.rs +++ b/tests/codegen-llvm/sanitizer/address-sanitizer-globals-tracking.rs @@ -19,7 +19,7 @@ //@ only-linux // //@ revisions:ASAN ASAN-FAT-LTO -//@ compile-flags: -Zsanitizer=address -Ctarget-feature=-crt-static +//@ compile-flags: -Zsanitizer=address -Ctarget-feature=-crt-static -C unsafe-allow-abi-mismatch=sanitizer // [ASAN] no extra compile-flags //@[ASAN-FAT-LTO] compile-flags: -Cprefer-dynamic=false -Clto=fat diff --git a/tests/codegen-llvm/sanitizer/cfi/add-canonical-jump-tables-flag.rs b/tests/codegen-llvm/sanitizer/cfi/add-canonical-jump-tables-flag.rs index 22577e2a3c4..77857ca4ccb 100644 --- a/tests/codegen-llvm/sanitizer/cfi/add-canonical-jump-tables-flag.rs +++ b/tests/codegen-llvm/sanitizer/cfi/add-canonical-jump-tables-flag.rs @@ -1,7 +1,7 @@ // Verifies that "CFI Canonical Jump Tables" module flag is added. // //@ needs-sanitizer-cfi -//@ compile-flags: -Clto -Ctarget-feature=-crt-static -Zsanitizer=cfi +//@ compile-flags: -Clto -Ctarget-feature=-crt-static -Zsanitizer=cfi -C unsafe-allow-abi-mismatch=sanitizer #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/cfi/add-cfi-normalize-integers-flag.rs b/tests/codegen-llvm/sanitizer/cfi/add-cfi-normalize-integers-flag.rs index a54a6d84a80..6cf9a72b748 100644 --- a/tests/codegen-llvm/sanitizer/cfi/add-cfi-normalize-integers-flag.rs +++ b/tests/codegen-llvm/sanitizer/cfi/add-cfi-normalize-integers-flag.rs @@ -1,7 +1,7 @@ // Verifies that "cfi-normalize-integers" module flag is added. // //@ needs-sanitizer-cfi -//@ compile-flags: -Clto -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-normalize-integers +//@ compile-flags: -Clto -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-normalize-integers -C unsafe-allow-abi-mismatch=sanitizer,sanitizer-cfi-normalize-integers #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/cfi/add-enable-split-lto-unit-flag.rs b/tests/codegen-llvm/sanitizer/cfi/add-enable-split-lto-unit-flag.rs index 283b8f26102..0bfdbfba5d2 100644 --- a/tests/codegen-llvm/sanitizer/cfi/add-enable-split-lto-unit-flag.rs +++ b/tests/codegen-llvm/sanitizer/cfi/add-enable-split-lto-unit-flag.rs @@ -1,7 +1,7 @@ // Verifies that "EnableSplitLTOUnit" module flag is added. // //@ needs-sanitizer-cfi -//@ compile-flags: -Clto -Ctarget-feature=-crt-static -Zsanitizer=cfi +//@ compile-flags: -Clto -Ctarget-feature=-crt-static -Zsanitizer=cfi -C unsafe-allow-abi-mismatch=sanitizer #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/cfi/dbg-location-on-cfi-blocks.rs b/tests/codegen-llvm/sanitizer/cfi/dbg-location-on-cfi-blocks.rs index df65960dfe0..2a18e30e2b0 100644 --- a/tests/codegen-llvm/sanitizer/cfi/dbg-location-on-cfi-blocks.rs +++ b/tests/codegen-llvm/sanitizer/cfi/dbg-location-on-cfi-blocks.rs @@ -1,7 +1,7 @@ // Verifies that the parent block's debug information are assigned to the inserted cfi block. // //@ needs-sanitizer-cfi -//@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static -Cdebuginfo=1 +//@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static -Cdebuginfo=1 -C unsafe-allow-abi-mismatch=sanitizer #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-attr-sanitize-off.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-attr-sanitize-off.rs index 651afb33228..c49438f4318 100644 --- a/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-attr-sanitize-off.rs +++ b/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-attr-sanitize-off.rs @@ -1,7 +1,7 @@ // Verifies that pointer type membership tests for indirect calls are omitted. // //@ needs-sanitizer-cfi -//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0 +//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0 -C unsafe-allow-abi-mismatch=sanitizer #![crate_type = "lib"] #![feature(sanitize)] diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-checks.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-checks.rs index ebc66a015df..9cad88f6518 100644 --- a/tests/codegen-llvm/sanitizer/cfi/emit-type-checks.rs +++ b/tests/codegen-llvm/sanitizer/cfi/emit-type-checks.rs @@ -1,7 +1,7 @@ // Verifies that pointer type membership tests for indirect calls are emitted. // //@ needs-sanitizer-cfi -//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0 +//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0 -C unsafe-allow-abi-mismatch=sanitizer #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-attr-cfi-encoding.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-attr-cfi-encoding.rs index 9bc2e42db0f..cd9088f58af 100644 --- a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-attr-cfi-encoding.rs +++ b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-attr-cfi-encoding.rs @@ -1,7 +1,7 @@ // Verifies that user-defined CFI encoding for types are emitted. // //@ needs-sanitizer-cfi -//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0 +//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0 -C unsafe-allow-abi-mismatch=sanitizer #![crate_type = "lib"] #![feature(cfi_encoding, extern_types)] diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-const-generics.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-const-generics.rs index 9048c6a1f18..cf26c17af1e 100644 --- a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-const-generics.rs +++ b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-const-generics.rs @@ -2,7 +2,7 @@ // for const generics. // //@ needs-sanitizer-cfi -//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0 +//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0 -C unsafe-allow-abi-mismatch=sanitizer #![crate_type = "lib"] #![feature(type_alias_impl_trait)] diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-drop-in-place.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-drop-in-place.rs index 8fec275fd06..279350d20c5 100644 --- a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-drop-in-place.rs +++ b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-drop-in-place.rs @@ -5,7 +5,7 @@ // future. // //@ needs-sanitizer-cfi -//@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static +//@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static -C unsafe-allow-abi-mismatch=sanitizer #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-function-types.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-function-types.rs index 7e60aafff68..047b532e994 100644 --- a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-function-types.rs +++ b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-function-types.rs @@ -2,7 +2,7 @@ // for function types. // //@ needs-sanitizer-cfi -//@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static +//@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static -C unsafe-allow-abi-mismatch=sanitizer #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-lifetimes.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-lifetimes.rs index 36d2e8c9f25..92b2ab32ea0 100644 --- a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-lifetimes.rs +++ b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-lifetimes.rs @@ -2,7 +2,7 @@ // for lifetimes/regions. // //@ needs-sanitizer-cfi -//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0 +//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0 -C unsafe-allow-abi-mismatch=sanitizer #![crate_type = "lib"] #![feature(type_alias_impl_trait)] diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-method-secondary-typeid.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-method-secondary-typeid.rs index 9d611777ff0..5de39dc85c1 100644 --- a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-method-secondary-typeid.rs +++ b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-method-secondary-typeid.rs @@ -2,7 +2,7 @@ // self so they can be used as function pointers. // //@ needs-sanitizer-cfi -//@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static +//@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static -C unsafe-allow-abi-mismatch=sanitizer #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-paths.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-paths.rs index a8ba8db1be3..f5846713bd5 100644 --- a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-paths.rs +++ b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-paths.rs @@ -2,7 +2,7 @@ // for paths. // //@ needs-sanitizer-cfi -//@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static +//@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static -C unsafe-allow-abi-mismatch=sanitizer #![crate_type = "lib"] #![feature(type_alias_impl_trait)] diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-pointer-types.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-pointer-types.rs index d37bb740f55..ad4fe11d087 100644 --- a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-pointer-types.rs +++ b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-pointer-types.rs @@ -2,7 +2,7 @@ // for pointer types. // //@ needs-sanitizer-cfi -//@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static +//@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static -C unsafe-allow-abi-mismatch=sanitizer #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-primitive-types.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-primitive-types.rs index 7d9e4d05872..93845d05195 100644 --- a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-primitive-types.rs +++ b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-primitive-types.rs @@ -2,7 +2,7 @@ // for primitive types. // //@ needs-sanitizer-cfi -//@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static +//@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static -C unsafe-allow-abi-mismatch=sanitizer #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-repr-transparent-types.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-repr-transparent-types.rs index 0f97c70f3f9..025aa902658 100644 --- a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-repr-transparent-types.rs +++ b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-repr-transparent-types.rs @@ -2,7 +2,7 @@ // for repr transparent types. // //@ needs-sanitizer-cfi -//@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static +//@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static -C unsafe-allow-abi-mismatch=sanitizer #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-sequence-types.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-sequence-types.rs index bdee3f47a83..76c8150b778 100644 --- a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-sequence-types.rs +++ b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-sequence-types.rs @@ -2,7 +2,7 @@ // for sequence types. // //@ needs-sanitizer-cfi -//@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static +//@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static -C unsafe-allow-abi-mismatch=sanitizer #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-trait-types.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-trait-types.rs index 55e816178f8..4fafdd2f040 100644 --- a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-trait-types.rs +++ b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-trait-types.rs @@ -2,7 +2,7 @@ // for trait types. // //@ needs-sanitizer-cfi -//@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static +//@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static -C unsafe-allow-abi-mismatch=sanitizer #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-user-defined-types.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-user-defined-types.rs index c1f3ca61afe..91351096ca2 100644 --- a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-user-defined-types.rs +++ b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-user-defined-types.rs @@ -2,7 +2,7 @@ // for user-defined types. // //@ needs-sanitizer-cfi -//@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static +//@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static -C unsafe-allow-abi-mismatch=sanitizer #![crate_type = "lib"] #![feature(extern_types)] diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-generalized.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-generalized.rs index 32637b64b3e..22d518cca74 100644 --- a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-generalized.rs +++ b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-generalized.rs @@ -1,7 +1,7 @@ // Verifies that generalized type metadata for functions are emitted. // //@ needs-sanitizer-cfi -//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-generalize-pointers +//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-generalize-pointers -C unsafe-allow-abi-mismatch=sanitizer #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-normalized-generalized.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-normalized-generalized.rs index 51121b0aef1..7639ce7b104 100644 --- a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-normalized-generalized.rs +++ b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-normalized-generalized.rs @@ -1,7 +1,7 @@ // Verifies that normalized and generalized type metadata for functions are emitted. // //@ needs-sanitizer-cfi -//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-normalize-integers -Zsanitizer-cfi-generalize-pointers +//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-normalize-integers -Zsanitizer-cfi-generalize-pointers -C unsafe-allow-abi-mismatch=sanitizer,sanitizer-cfi-normalize-integers #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-normalized.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-normalized.rs index 1cfdd23006e..acd72b0ca3c 100644 --- a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-normalized.rs +++ b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-normalized.rs @@ -1,7 +1,7 @@ // Verifies that normalized type metadata for functions are emitted. // //@ needs-sanitizer-cfi -//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-normalize-integers +//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-normalize-integers -C unsafe-allow-abi-mismatch=sanitizer,sanitizer-cfi-normalize-integers #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi.rs index 56ab1ce4b35..fa5cd471466 100644 --- a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi.rs +++ b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi.rs @@ -1,7 +1,7 @@ // Verifies that type metadata for functions are emitted. // //@ needs-sanitizer-cfi -//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi +//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -C unsafe-allow-abi-mismatch=sanitizer #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-trait-objects.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-trait-objects.rs index 0e57ce322d1..82873e935b2 100644 --- a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-trait-objects.rs +++ b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-trait-objects.rs @@ -1,7 +1,7 @@ // Verifies that type metadata identifiers for trait objects are emitted correctly. // //@ needs-sanitizer-cfi -//@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Ctarget-feature=-crt-static -Zsanitizer=cfi +//@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Ctarget-feature=-crt-static -Zsanitizer=cfi -C unsafe-allow-abi-mismatch=sanitizer #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/cfi/external_weak_symbols.rs b/tests/codegen-llvm/sanitizer/cfi/external_weak_symbols.rs index b3cb6dfdd37..893b0167693 100644 --- a/tests/codegen-llvm/sanitizer/cfi/external_weak_symbols.rs +++ b/tests/codegen-llvm/sanitizer/cfi/external_weak_symbols.rs @@ -2,7 +2,7 @@ // emitted correctly. // //@ needs-sanitizer-cfi -//@ compile-flags: -Clinker-plugin-lto -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static +//@ compile-flags: -Clinker-plugin-lto -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static -C unsafe-allow-abi-mismatch=sanitizer #![crate_type = "bin"] #![feature(linkage)] diff --git a/tests/codegen-llvm/sanitizer/cfi/generalize-pointers.rs b/tests/codegen-llvm/sanitizer/cfi/generalize-pointers.rs index 57004da6f8e..caa2f258f8f 100644 --- a/tests/codegen-llvm/sanitizer/cfi/generalize-pointers.rs +++ b/tests/codegen-llvm/sanitizer/cfi/generalize-pointers.rs @@ -1,7 +1,7 @@ // Verifies that pointer types are generalized. // //@ needs-sanitizer-cfi -//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-generalize-pointers -Copt-level=0 +//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-generalize-pointers -Copt-level=0 -C unsafe-allow-abi-mismatch=sanitizer #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/cfi/normalize-integers.rs b/tests/codegen-llvm/sanitizer/cfi/normalize-integers.rs index 770ee4e64e0..16f76adafb8 100644 --- a/tests/codegen-llvm/sanitizer/cfi/normalize-integers.rs +++ b/tests/codegen-llvm/sanitizer/cfi/normalize-integers.rs @@ -1,7 +1,7 @@ // Verifies that integer types are normalized. // //@ needs-sanitizer-cfi -//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-normalize-integers -Copt-level=0 +//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-normalize-integers -Copt-level=0 -C unsafe-allow-abi-mismatch=sanitizer,sanitizer-cfi-normalize-integers #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/dataflow-instrument-functions.rs b/tests/codegen-llvm/sanitizer/dataflow-instrument-functions.rs index a2d0d63cc17..cd8c2c48ea4 100644 --- a/tests/codegen-llvm/sanitizer/dataflow-instrument-functions.rs +++ b/tests/codegen-llvm/sanitizer/dataflow-instrument-functions.rs @@ -1,7 +1,7 @@ // Verifies that functions are instrumented. // //@ needs-sanitizer-dataflow -//@ compile-flags: -Copt-level=0 -Zsanitizer=dataflow +//@ compile-flags: -Copt-level=0 -Zsanitizer=dataflow -C unsafe-allow-abi-mismatch=sanitizer #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/memory-track-origins.rs b/tests/codegen-llvm/sanitizer/memory-track-origins.rs index 5eb5b356b05..a72e523c4e1 100644 --- a/tests/codegen-llvm/sanitizer/memory-track-origins.rs +++ b/tests/codegen-llvm/sanitizer/memory-track-origins.rs @@ -4,7 +4,7 @@ //@ needs-sanitizer-memory //@ revisions:MSAN-0 MSAN-1 MSAN-2 MSAN-1-LTO MSAN-2-LTO // -//@ compile-flags: -Zsanitizer=memory -Ctarget-feature=-crt-static +//@ compile-flags: -Zsanitizer=memory -Ctarget-feature=-crt-static -C unsafe-allow-abi-mismatch=sanitizer // [MSAN-0] no extra compile-flags //@[MSAN-1] compile-flags: -Zsanitizer-memory-track-origins=1 //@[MSAN-2] compile-flags: -Zsanitizer-memory-track-origins diff --git a/tests/codegen-llvm/sanitizer/memtag-attr-check.rs b/tests/codegen-llvm/sanitizer/memtag-attr-check.rs index ffe3a2322a2..fc430f3a570 100644 --- a/tests/codegen-llvm/sanitizer/memtag-attr-check.rs +++ b/tests/codegen-llvm/sanitizer/memtag-attr-check.rs @@ -2,7 +2,7 @@ // applied when enabling the memtag sanitizer. // //@ needs-sanitizer-memtag -//@ compile-flags: -Zsanitizer=memtag -Ctarget-feature=+mte -Copt-level=0 +//@ compile-flags: -C unsafe-allow-abi-mismatch=sanitizer -Zsanitizer=memtag -Ctarget-feature=+mte -Copt-level=0 #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/safestack-attr-check.rs b/tests/codegen-llvm/sanitizer/safestack-attr-check.rs index 050a60333af..414dd89a580 100644 --- a/tests/codegen-llvm/sanitizer/safestack-attr-check.rs +++ b/tests/codegen-llvm/sanitizer/safestack-attr-check.rs @@ -1,7 +1,7 @@ // This tests that the safestack attribute is applied when enabling the safe-stack sanitizer. // //@ needs-sanitizer-safestack -//@ compile-flags: -Zsanitizer=safestack -Copt-level=0 +//@ compile-flags: -Zsanitizer=safestack -Copt-level=0 -C unsafe-allow-abi-mismatch=sanitizer #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/sanitize-off-inlining.rs b/tests/codegen-llvm/sanitizer/sanitize-off-inlining.rs index 69771827c3a..0f43e6b8393 100644 --- a/tests/codegen-llvm/sanitizer/sanitize-off-inlining.rs +++ b/tests/codegen-llvm/sanitizer/sanitize-off-inlining.rs @@ -1,9 +1,9 @@ // Verifies that sanitize(xyz = "off") attribute prevents inlining when // given sanitizer is enabled, but has no effect on inlining otherwise. -// //@ needs-sanitizer-address //@ needs-sanitizer-leak //@ revisions: ASAN LSAN +//@ compile-flags: -C unsafe-allow-abi-mismatch=sanitizer //@ compile-flags: -Copt-level=3 -Zmir-opt-level=4 -Ctarget-feature=-crt-static //@[ASAN] compile-flags: -Zsanitizer=address //@[LSAN] compile-flags: -Zsanitizer=leak diff --git a/tests/codegen-llvm/sanitizer/sanitize-off-kasan-asan.rs b/tests/codegen-llvm/sanitizer/sanitize-off-kasan-asan.rs index 94945f2b2e6..61ad0ba7d90 100644 --- a/tests/codegen-llvm/sanitizer/sanitize-off-kasan-asan.rs +++ b/tests/codegen-llvm/sanitizer/sanitize-off-kasan-asan.rs @@ -2,7 +2,7 @@ // the address sanitizer. // //@ needs-sanitizer-address -//@ compile-flags: -Zsanitizer=address -Ctarget-feature=-crt-static -Copt-level=0 +//@ compile-flags: -Zsanitizer=address -Ctarget-feature=-crt-static -Copt-level=0 -C unsafe-allow-abi-mismatch=sanitizer #![crate_type = "lib"] #![feature(sanitize)] diff --git a/tests/codegen-llvm/sanitizer/sanitizer-recover.rs b/tests/codegen-llvm/sanitizer/sanitizer-recover.rs index 6b659320481..b8a24e31c30 100644 --- a/tests/codegen-llvm/sanitizer/sanitizer-recover.rs +++ b/tests/codegen-llvm/sanitizer/sanitizer-recover.rs @@ -5,7 +5,7 @@ //@ needs-sanitizer-memory //@ revisions:ASAN ASAN-RECOVER MSAN MSAN-RECOVER MSAN-RECOVER-LTO //@ no-prefer-dynamic -// +//@ compile-flags: -C unsafe-allow-abi-mismatch=sanitizer //@ compile-flags: -Ctarget-feature=-crt-static //@[ASAN] compile-flags: -Zsanitizer=address -Copt-level=0 //@[ASAN-RECOVER] compile-flags: -Zsanitizer=address -Zsanitizer-recover=address -Copt-level=0 diff --git a/tests/crashes/140479.rs b/tests/crashes/140479.rs deleted file mode 100644 index ed3ca887546..00000000000 --- a/tests/crashes/140479.rs +++ /dev/null @@ -1,5 +0,0 @@ -//@ known-bug: #140479 -macro_rules! a { ( $( { $ [ $b:c ] } )) => ( $(${ concat(d, $b)} ))} -fn e() { - a!({}) -} diff --git a/tests/mir-opt/dest-prop/cycle.main.DestinationPropagation.panic-abort.diff b/tests/mir-opt/dest-prop/cycle.main.DestinationPropagation.panic-abort.diff index 5d8aaedae37..a2f10be31a9 100644 --- a/tests/mir-opt/dest-prop/cycle.main.DestinationPropagation.panic-abort.diff +++ b/tests/mir-opt/dest-prop/cycle.main.DestinationPropagation.panic-abort.diff @@ -8,25 +8,23 @@ let _5: (); let mut _6: i32; scope 1 { -- debug x => _1; -+ debug x => _6; + debug x => _1; let _2: i32; scope 2 { - debug y => _2; -+ debug y => _6; ++ debug y => _1; let _3: i32; scope 3 { - debug z => _3; -+ debug z => _6; ++ debug z => _1; } } } bb0: { - StorageLive(_1); -- _1 = val() -> [return: bb1, unwind unreachable]; + nop; -+ _6 = val() -> [return: bb1, unwind unreachable]; + _1 = val() -> [return: bb1, unwind unreachable]; } bb1: { @@ -49,9 +47,10 @@ StorageLive(_5); - StorageLive(_6); - _6 = copy _1; +- _5 = std::mem::drop::<i32>(move _6) -> [return: bb2, unwind unreachable]; + nop; + nop; - _5 = std::mem::drop::<i32>(move _6) -> [return: bb2, unwind unreachable]; ++ _5 = std::mem::drop::<i32>(move _1) -> [return: bb2, unwind unreachable]; } bb2: { diff --git a/tests/mir-opt/dest-prop/cycle.main.DestinationPropagation.panic-unwind.diff b/tests/mir-opt/dest-prop/cycle.main.DestinationPropagation.panic-unwind.diff index 05c9bcc1d73..a08488615b1 100644 --- a/tests/mir-opt/dest-prop/cycle.main.DestinationPropagation.panic-unwind.diff +++ b/tests/mir-opt/dest-prop/cycle.main.DestinationPropagation.panic-unwind.diff @@ -8,25 +8,23 @@ let _5: (); let mut _6: i32; scope 1 { -- debug x => _1; -+ debug x => _6; + debug x => _1; let _2: i32; scope 2 { - debug y => _2; -+ debug y => _6; ++ debug y => _1; let _3: i32; scope 3 { - debug z => _3; -+ debug z => _6; ++ debug z => _1; } } } bb0: { - StorageLive(_1); -- _1 = val() -> [return: bb1, unwind continue]; + nop; -+ _6 = val() -> [return: bb1, unwind continue]; + _1 = val() -> [return: bb1, unwind continue]; } bb1: { @@ -49,9 +47,10 @@ StorageLive(_5); - StorageLive(_6); - _6 = copy _1; +- _5 = std::mem::drop::<i32>(move _6) -> [return: bb2, unwind continue]; + nop; + nop; - _5 = std::mem::drop::<i32>(move _6) -> [return: bb2, unwind continue]; ++ _5 = std::mem::drop::<i32>(move _1) -> [return: bb2, unwind continue]; } bb2: { diff --git a/tests/mir-opt/dest-prop/dead_stores_79191.f.DestinationPropagation.after.panic-abort.mir b/tests/mir-opt/dest-prop/dead_stores_79191.f.DestinationPropagation.after.panic-abort.mir index eb4209731c6..15061da8120 100644 --- a/tests/mir-opt/dest-prop/dead_stores_79191.f.DestinationPropagation.after.panic-abort.mir +++ b/tests/mir-opt/dest-prop/dead_stores_79191.f.DestinationPropagation.after.panic-abort.mir @@ -7,16 +7,16 @@ fn f(_1: usize) -> usize { let mut _3: usize; let mut _4: usize; scope 1 { - debug b => _3; + debug b => _2; } bb0: { nop; - _3 = copy _1; + _2 = copy _1; _1 = const 5_usize; nop; nop; - _1 = move _3; + _1 = move _2; nop; nop; nop; diff --git a/tests/mir-opt/dest-prop/dead_stores_79191.f.DestinationPropagation.after.panic-unwind.mir b/tests/mir-opt/dest-prop/dead_stores_79191.f.DestinationPropagation.after.panic-unwind.mir index fe9a7376a58..ddfe4dc5b3e 100644 --- a/tests/mir-opt/dest-prop/dead_stores_79191.f.DestinationPropagation.after.panic-unwind.mir +++ b/tests/mir-opt/dest-prop/dead_stores_79191.f.DestinationPropagation.after.panic-unwind.mir @@ -7,16 +7,16 @@ fn f(_1: usize) -> usize { let mut _3: usize; let mut _4: usize; scope 1 { - debug b => _3; + debug b => _2; } bb0: { nop; - _3 = copy _1; + _2 = copy _1; _1 = const 5_usize; nop; nop; - _1 = move _3; + _1 = move _2; nop; nop; nop; diff --git a/tests/mir-opt/dest-prop/dead_stores_better.f.DestinationPropagation.after.panic-abort.mir b/tests/mir-opt/dest-prop/dead_stores_better.f.DestinationPropagation.after.panic-abort.mir index eb4209731c6..15061da8120 100644 --- a/tests/mir-opt/dest-prop/dead_stores_better.f.DestinationPropagation.after.panic-abort.mir +++ b/tests/mir-opt/dest-prop/dead_stores_better.f.DestinationPropagation.after.panic-abort.mir @@ -7,16 +7,16 @@ fn f(_1: usize) -> usize { let mut _3: usize; let mut _4: usize; scope 1 { - debug b => _3; + debug b => _2; } bb0: { nop; - _3 = copy _1; + _2 = copy _1; _1 = const 5_usize; nop; nop; - _1 = move _3; + _1 = move _2; nop; nop; nop; diff --git a/tests/mir-opt/dest-prop/dead_stores_better.f.DestinationPropagation.after.panic-unwind.mir b/tests/mir-opt/dest-prop/dead_stores_better.f.DestinationPropagation.after.panic-unwind.mir index fe9a7376a58..ddfe4dc5b3e 100644 --- a/tests/mir-opt/dest-prop/dead_stores_better.f.DestinationPropagation.after.panic-unwind.mir +++ b/tests/mir-opt/dest-prop/dead_stores_better.f.DestinationPropagation.after.panic-unwind.mir @@ -7,16 +7,16 @@ fn f(_1: usize) -> usize { let mut _3: usize; let mut _4: usize; scope 1 { - debug b => _3; + debug b => _2; } bb0: { nop; - _3 = copy _1; + _2 = copy _1; _1 = const 5_usize; nop; nop; - _1 = move _3; + _1 = move _2; nop; nop; nop; diff --git a/tests/mir-opt/gvn_overlapping.fields.GVN.diff b/tests/mir-opt/gvn_overlapping.fields.GVN.diff new file mode 100644 index 00000000000..0548f4e42e4 --- /dev/null +++ b/tests/mir-opt/gvn_overlapping.fields.GVN.diff @@ -0,0 +1,14 @@ +- // MIR for `fields` before GVN ++ // MIR for `fields` after GVN + + fn fields(_1: (Adt, Adt)) -> () { + let mut _0: (); + let mut _2: u32; + + bb0: { + _2 = copy (((_1.0: Adt) as variant#1).0: u32); + (_1.1: Adt) = Adt::Some(copy _2); + return; + } + } + diff --git a/tests/mir-opt/gvn_overlapping.rs b/tests/mir-opt/gvn_overlapping.rs index 99113445e68..f148a843561 100644 --- a/tests/mir-opt/gvn_overlapping.rs +++ b/tests/mir-opt/gvn_overlapping.rs @@ -2,11 +2,10 @@ #![feature(custom_mir, core_intrinsics)] -// Check that we do not create overlapping assignments. - use std::intrinsics::mir::*; // EMIT_MIR gvn_overlapping.overlapping.GVN.diff +/// Check that we do not create overlapping assignments. #[custom_mir(dialect = "runtime")] fn overlapping(_17: Adt) { // CHECK-LABEL: fn overlapping( @@ -26,6 +25,45 @@ fn overlapping(_17: Adt) { } } +// EMIT_MIR gvn_overlapping.stable_projection.GVN.diff +/// Check that we allow dereferences in the RHS if the LHS is a stable projection. +#[custom_mir(dialect = "runtime")] +fn stable_projection(_1: (Adt,)) { + // CHECK-LABEL: fn stable_projection( + // CHECK: let mut _2: *mut Adt; + // CHECK: let mut _4: &Adt; + // CHECK: (_1.0: Adt) = copy (*_4); + mir! { + let _2: *mut Adt; + let _3: u32; + let _4: &Adt; + { + _2 = core::ptr::addr_of_mut!(_1.0); + _4 = &(*_2); + _3 = Field(Variant((*_4), 1), 0); + _1.0 = Adt::Some(_3); + Return() + } + } +} + +// EMIT_MIR gvn_overlapping.fields.GVN.diff +/// Check that we do not create assignments between different fields of the same local. +#[custom_mir(dialect = "runtime")] +fn fields(_1: (Adt, Adt)) { + // CHECK-LABEL: fn fields( + // CHECK: _2 = copy (((_1.0: Adt) as variant#1).0: u32); + // CHECK-NEXT: (_1.1: Adt) = Adt::Some(copy _2); + mir! { + let _2: u32; + { + _2 = Field(Variant(_1.0, 1), 0); + _1.1 = Adt::Some(_2); + Return() + } + } +} + fn main() { overlapping(Adt::Some(0)); } diff --git a/tests/mir-opt/gvn_overlapping.stable_projection.GVN.diff b/tests/mir-opt/gvn_overlapping.stable_projection.GVN.diff new file mode 100644 index 00000000000..08835456591 --- /dev/null +++ b/tests/mir-opt/gvn_overlapping.stable_projection.GVN.diff @@ -0,0 +1,19 @@ +- // MIR for `stable_projection` before GVN ++ // MIR for `stable_projection` after GVN + + fn stable_projection(_1: (Adt,)) -> () { + let mut _0: (); + let mut _2: *mut Adt; + let mut _3: u32; + let mut _4: &Adt; + + bb0: { + _2 = &raw mut (_1.0: Adt); + _4 = &(*_2); + _3 = copy (((*_4) as variant#1).0: u32); +- (_1.0: Adt) = Adt::Some(copy _3); ++ (_1.0: Adt) = copy (*_4); + return; + } + } + diff --git a/tests/mir-opt/nrvo_miscompile_111005.rs b/tests/mir-opt/nrvo_miscompile_111005.rs index 03008fa8191..131f7b8f6f9 100644 --- a/tests/mir-opt/nrvo_miscompile_111005.rs +++ b/tests/mir-opt/nrvo_miscompile_111005.rs @@ -1,4 +1,3 @@ -// skip-filecheck // This is a miscompilation, #111005 to track //@ test-mir-pass: RenameReturnPlace @@ -10,6 +9,11 @@ use core::intrinsics::mir::*; // EMIT_MIR nrvo_miscompile_111005.wrong.RenameReturnPlace.diff #[custom_mir(dialect = "runtime", phase = "initial")] pub fn wrong(arg: char) -> char { + // CHECK-LABEL: fn wrong( + // CHECK: _0 = copy _1; + // FIXME: This is wrong: + // CHECK-NEXT: _0 = const 'b'; + // CHECK-NEXT: return; mir! { { let temp = arg; diff --git a/tests/mir-opt/pre-codegen/clone_as_copy.enum_clone_as_copy.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/clone_as_copy.enum_clone_as_copy.PreCodegen.after.mir index 9f88e1961ec..e67f362ee04 100644 --- a/tests/mir-opt/pre-codegen/clone_as_copy.enum_clone_as_copy.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/clone_as_copy.enum_clone_as_copy.PreCodegen.after.mir @@ -6,8 +6,8 @@ fn enum_clone_as_copy(_1: &Enum1) -> Enum1 { scope 1 (inlined <Enum1 as Clone>::clone) { debug self => _1; let mut _2: isize; - let mut _3: &AllCopy; - let mut _4: &NestCopy; + let _3: &AllCopy; + let _4: &NestCopy; scope 2 { debug __self_0 => _3; scope 6 (inlined <AllCopy as Clone>::clone) { diff --git a/tests/run-make/apple-slow-tls/rmake.rs b/tests/run-make-cargo/apple-slow-tls/rmake.rs index 231e0b1668e..231e0b1668e 100644 --- a/tests/run-make/apple-slow-tls/rmake.rs +++ b/tests/run-make-cargo/apple-slow-tls/rmake.rs diff --git a/tests/run-make/apple-slow-tls/tls_test/Cargo.toml b/tests/run-make-cargo/apple-slow-tls/tls_test/Cargo.toml index c8e5a228eef..c8e5a228eef 100644 --- a/tests/run-make/apple-slow-tls/tls_test/Cargo.toml +++ b/tests/run-make-cargo/apple-slow-tls/tls_test/Cargo.toml diff --git a/tests/run-make/apple-slow-tls/tls_test/src/main.rs b/tests/run-make-cargo/apple-slow-tls/tls_test/src/main.rs index d48d22bb3d1..d48d22bb3d1 100644 --- a/tests/run-make/apple-slow-tls/tls_test/src/main.rs +++ b/tests/run-make-cargo/apple-slow-tls/tls_test/src/main.rs diff --git a/tests/run-make/compiler-builtins/Cargo.toml b/tests/run-make-cargo/compiler-builtins/Cargo.toml index 869210c4a68..869210c4a68 100644 --- a/tests/run-make/compiler-builtins/Cargo.toml +++ b/tests/run-make-cargo/compiler-builtins/Cargo.toml diff --git a/tests/run-make/compiler-builtins/lib.rs b/tests/run-make-cargo/compiler-builtins/lib.rs index 0c9ac1ac8e4..0c9ac1ac8e4 100644 --- a/tests/run-make/compiler-builtins/lib.rs +++ b/tests/run-make-cargo/compiler-builtins/lib.rs diff --git a/tests/run-make/compiler-builtins/rmake.rs b/tests/run-make-cargo/compiler-builtins/rmake.rs index 10093db2258..10093db2258 100644 --- a/tests/run-make/compiler-builtins/rmake.rs +++ b/tests/run-make-cargo/compiler-builtins/rmake.rs diff --git a/tests/run-make/rustc-crates-on-stable/rmake.rs b/tests/run-make-cargo/rustc-crates-on-stable/rmake.rs index cbc1f24b8c1..cbc1f24b8c1 100644 --- a/tests/run-make/rustc-crates-on-stable/rmake.rs +++ b/tests/run-make-cargo/rustc-crates-on-stable/rmake.rs diff --git a/tests/run-make/rustdoc-scrape-examples-paths/foo/Cargo.toml b/tests/run-make-cargo/rustdoc-scrape-examples-paths/foo/Cargo.toml index 6962028375b..6962028375b 100644 --- a/tests/run-make/rustdoc-scrape-examples-paths/foo/Cargo.toml +++ b/tests/run-make-cargo/rustdoc-scrape-examples-paths/foo/Cargo.toml diff --git a/tests/run-make/rustdoc-scrape-examples-paths/foo/examples/complex.rs b/tests/run-make-cargo/rustdoc-scrape-examples-paths/foo/examples/complex.rs index 1cda7e098f4..1cda7e098f4 100644 --- a/tests/run-make/rustdoc-scrape-examples-paths/foo/examples/complex.rs +++ b/tests/run-make-cargo/rustdoc-scrape-examples-paths/foo/examples/complex.rs diff --git a/tests/run-make/rustdoc-scrape-examples-paths/foo/examples/tester.rs b/tests/run-make-cargo/rustdoc-scrape-examples-paths/foo/examples/tester.rs index fbd1906ec09..fbd1906ec09 100644 --- a/tests/run-make/rustdoc-scrape-examples-paths/foo/examples/tester.rs +++ b/tests/run-make-cargo/rustdoc-scrape-examples-paths/foo/examples/tester.rs diff --git a/tests/run-make/rustdoc-scrape-examples-paths/foo/src/lib.rs b/tests/run-make-cargo/rustdoc-scrape-examples-paths/foo/src/lib.rs index a9bb0272c78..a9bb0272c78 100644 --- a/tests/run-make/rustdoc-scrape-examples-paths/foo/src/lib.rs +++ b/tests/run-make-cargo/rustdoc-scrape-examples-paths/foo/src/lib.rs diff --git a/tests/run-make/rustdoc-scrape-examples-paths/rmake.rs b/tests/run-make-cargo/rustdoc-scrape-examples-paths/rmake.rs index 6784e438762..6784e438762 100644 --- a/tests/run-make/rustdoc-scrape-examples-paths/rmake.rs +++ b/tests/run-make-cargo/rustdoc-scrape-examples-paths/rmake.rs diff --git a/tests/run-make/thumb-none-cortex-m/rmake.rs b/tests/run-make-cargo/thumb-none-cortex-m/rmake.rs index 27afef874da..6158b9c36da 100644 --- a/tests/run-make/thumb-none-cortex-m/rmake.rs +++ b/tests/run-make-cargo/thumb-none-cortex-m/rmake.rs @@ -4,7 +4,7 @@ //! //! How to run this //! $ ./x.py clean -//! $ ./x.py test --target thumbv6m-none-eabi,thumbv7m-none-eabi tests/run-make +//! $ ./x.py test --target thumbv6m-none-eabi,thumbv7m-none-eabi tests/run-make-cargo //! //! Supported targets: //! - thumbv6m-none-eabi (Bare Cortex-M0, M0+, M1) diff --git a/tests/run-make/thumb-none-qemu/example/.cargo/config.toml b/tests/run-make-cargo/thumb-none-qemu/example/.cargo/config.toml index 7152e81b502..7152e81b502 100644 --- a/tests/run-make/thumb-none-qemu/example/.cargo/config.toml +++ b/tests/run-make-cargo/thumb-none-qemu/example/.cargo/config.toml diff --git a/tests/run-make/thumb-none-qemu/example/Cargo.lock b/tests/run-make-cargo/thumb-none-qemu/example/Cargo.lock index 687b962a8b6..687b962a8b6 100644 --- a/tests/run-make/thumb-none-qemu/example/Cargo.lock +++ b/tests/run-make-cargo/thumb-none-qemu/example/Cargo.lock diff --git a/tests/run-make/thumb-none-qemu/example/Cargo.toml b/tests/run-make-cargo/thumb-none-qemu/example/Cargo.toml index 63eb5f90aec..63eb5f90aec 100644 --- a/tests/run-make/thumb-none-qemu/example/Cargo.toml +++ b/tests/run-make-cargo/thumb-none-qemu/example/Cargo.toml diff --git a/tests/run-make/thumb-none-qemu/example/memory.x b/tests/run-make-cargo/thumb-none-qemu/example/memory.x index dc7ad967a42..dc7ad967a42 100644 --- a/tests/run-make/thumb-none-qemu/example/memory.x +++ b/tests/run-make-cargo/thumb-none-qemu/example/memory.x diff --git a/tests/run-make/thumb-none-qemu/example/src/main.rs b/tests/run-make-cargo/thumb-none-qemu/example/src/main.rs index 4b8162c4120..4b8162c4120 100644 --- a/tests/run-make/thumb-none-qemu/example/src/main.rs +++ b/tests/run-make-cargo/thumb-none-qemu/example/src/main.rs diff --git a/tests/run-make/thumb-none-qemu/rmake.rs b/tests/run-make-cargo/thumb-none-qemu/rmake.rs index 9d4b426f4a1..9d4b426f4a1 100644 --- a/tests/run-make/thumb-none-qemu/rmake.rs +++ b/tests/run-make-cargo/thumb-none-qemu/rmake.rs diff --git a/tests/run-make/uefi-qemu/rmake.rs b/tests/run-make-cargo/uefi-qemu/rmake.rs index 55d42fba5e7..55d42fba5e7 100644 --- a/tests/run-make/uefi-qemu/rmake.rs +++ b/tests/run-make-cargo/uefi-qemu/rmake.rs diff --git a/tests/run-make/uefi-qemu/uefi_qemu_test/Cargo.lock b/tests/run-make-cargo/uefi-qemu/uefi_qemu_test/Cargo.lock index 8b6a664ad93..8b6a664ad93 100644 --- a/tests/run-make/uefi-qemu/uefi_qemu_test/Cargo.lock +++ b/tests/run-make-cargo/uefi-qemu/uefi_qemu_test/Cargo.lock diff --git a/tests/run-make/uefi-qemu/uefi_qemu_test/Cargo.toml b/tests/run-make-cargo/uefi-qemu/uefi_qemu_test/Cargo.toml index 1a8d0d94368..1a8d0d94368 100644 --- a/tests/run-make/uefi-qemu/uefi_qemu_test/Cargo.toml +++ b/tests/run-make-cargo/uefi-qemu/uefi_qemu_test/Cargo.toml diff --git a/tests/run-make/uefi-qemu/uefi_qemu_test/src/main.rs b/tests/run-make-cargo/uefi-qemu/uefi_qemu_test/src/main.rs index f8e1212a9f6..f8e1212a9f6 100644 --- a/tests/run-make/uefi-qemu/uefi_qemu_test/src/main.rs +++ b/tests/run-make-cargo/uefi-qemu/uefi_qemu_test/src/main.rs diff --git a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/cc_plus_one_asm.checks b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/cc_plus_one_asm.checks index d2e53bee08f..d2e53bee08f 100644 --- a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/cc_plus_one_asm.checks +++ b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/cc_plus_one_asm.checks diff --git a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/cc_plus_one_c.checks b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/cc_plus_one_c.checks index b93b33afb3f..b93b33afb3f 100644 --- a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/cc_plus_one_c.checks +++ b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/cc_plus_one_c.checks diff --git a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/cc_plus_one_c_asm.checks b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/cc_plus_one_c_asm.checks index 0a3d1dced42..0a3d1dced42 100644 --- a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/cc_plus_one_c_asm.checks +++ b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/cc_plus_one_c_asm.checks diff --git a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/cc_plus_one_cxx.checks b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/cc_plus_one_cxx.checks index f96f152c02f..f96f152c02f 100644 --- a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/cc_plus_one_cxx.checks +++ b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/cc_plus_one_cxx.checks diff --git a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/cc_plus_one_cxx_asm.checks b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/cc_plus_one_cxx_asm.checks index 0126cd8ee64..0126cd8ee64 100644 --- a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/cc_plus_one_cxx_asm.checks +++ b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/cc_plus_one_cxx_asm.checks diff --git a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_asm.checks b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_asm.checks index 61567548848..61567548848 100644 --- a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_asm.checks +++ b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_asm.checks diff --git a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_c.checks b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_c.checks index f551356b2ff..f551356b2ff 100644 --- a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_c.checks +++ b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_c.checks diff --git a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_c_asm.checks b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_c_asm.checks index 17312b9126a..17312b9126a 100644 --- a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_c_asm.checks +++ b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_c_asm.checks diff --git a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_c_global_asm.checks b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_c_global_asm.checks index 4b66cc5bc83..4b66cc5bc83 100644 --- a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_c_global_asm.checks +++ b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_c_global_asm.checks diff --git a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_cxx.checks b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_cxx.checks index 0f403e0203c..0f403e0203c 100644 --- a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_cxx.checks +++ b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_cxx.checks diff --git a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_cxx_asm.checks b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_cxx_asm.checks index 222e4ef7983..222e4ef7983 100644 --- a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_cxx_asm.checks +++ b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_cxx_asm.checks diff --git a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_cxx_global_asm.checks b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_cxx_global_asm.checks index 1d8fc96ad50..1d8fc96ad50 100644 --- a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_cxx_global_asm.checks +++ b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_cxx_global_asm.checks diff --git a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/Cargo.toml b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/enclave/Cargo.toml index 3a97c37e951..3a97c37e951 100644 --- a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/Cargo.toml +++ b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/enclave/Cargo.toml diff --git a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/build.rs b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/enclave/build.rs index f1fb5803948..f1fb5803948 100644 --- a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/build.rs +++ b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/enclave/build.rs diff --git a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/foo.c b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/enclave/foo.c index dd76d4f303a..dd76d4f303a 100644 --- a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/foo.c +++ b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/enclave/foo.c diff --git a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/foo_asm.s b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/enclave/foo_asm.s index 6d56214e87e..6d56214e87e 100644 --- a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/foo_asm.s +++ b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/enclave/foo_asm.s diff --git a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/foo_cxx.cpp b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/enclave/foo_cxx.cpp index ac6f64ac413..ac6f64ac413 100644 --- a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/foo_cxx.cpp +++ b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/enclave/foo_cxx.cpp diff --git a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/libcmake_foo/CMakeLists.txt b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/enclave/libcmake_foo/CMakeLists.txt index 27cdf2ecf82..27cdf2ecf82 100644 --- a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/libcmake_foo/CMakeLists.txt +++ b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/enclave/libcmake_foo/CMakeLists.txt diff --git a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/libcmake_foo/src/foo.c b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/enclave/libcmake_foo/src/foo.c index c3b731a2d50..c3b731a2d50 100644 --- a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/libcmake_foo/src/foo.c +++ b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/enclave/libcmake_foo/src/foo.c diff --git a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/libcmake_foo/src/foo_asm.s b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/enclave/libcmake_foo/src/foo_asm.s index 64b6b430eea..64b6b430eea 100644 --- a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/libcmake_foo/src/foo_asm.s +++ b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/enclave/libcmake_foo/src/foo_asm.s diff --git a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/libcmake_foo/src/foo_cxx.cpp b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/enclave/libcmake_foo/src/foo_cxx.cpp index 824e2afebcc..824e2afebcc 100644 --- a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/libcmake_foo/src/foo_cxx.cpp +++ b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/enclave/libcmake_foo/src/foo_cxx.cpp diff --git a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/src/main.rs b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/enclave/src/main.rs index cde38aacf7f..cde38aacf7f 100644 --- a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/src/main.rs +++ b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/enclave/src/main.rs diff --git a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/jumpto.checks b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/jumpto.checks index 885bf461bf3..885bf461bf3 100644 --- a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/jumpto.checks +++ b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/jumpto.checks diff --git a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/print.with_frame_pointers.checks b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/print.with_frame_pointers.checks index 485e20e3111..485e20e3111 100644 --- a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/print.with_frame_pointers.checks +++ b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/print.with_frame_pointers.checks diff --git a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/print.without_frame_pointers.checks b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/print.without_frame_pointers.checks index e02fe094889..e02fe094889 100644 --- a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/print.without_frame_pointers.checks +++ b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/print.without_frame_pointers.checks diff --git a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/rmake.rs b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/rmake.rs index 89754cdaf90..89754cdaf90 100644 --- a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/rmake.rs +++ b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/rmake.rs diff --git a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/rust_plus_one_global_asm.checks b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/rust_plus_one_global_asm.checks index fe6777537fb..fe6777537fb 100644 --- a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/rust_plus_one_global_asm.checks +++ b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/rust_plus_one_global_asm.checks diff --git a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/unw_getcontext.checks b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/unw_getcontext.checks index b46cf758384..b46cf758384 100644 --- a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/unw_getcontext.checks +++ b/tests/run-make-cargo/x86_64-fortanix-unknown-sgx-lvi/unw_getcontext.checks diff --git a/tests/run-make/apple-c-available-links/foo.c b/tests/run-make/apple-c-available-links/foo.c new file mode 100644 index 00000000000..eff99a8b12a --- /dev/null +++ b/tests/run-make/apple-c-available-links/foo.c @@ -0,0 +1,22 @@ +int foo(void) { + // Act as if using some API that's a lot newer than the deployment target. + // + // This forces Clang to insert a call to __isPlatformVersionAtLeast, + // and linking will fail if that is not present. + if (__builtin_available( + macos 1000.0, + ios 1000.0, + tvos 1000.0, + watchos 1000.0, + // CI runs below Xcode 15, where `visionos` wasn't a valid key in + // `__builtin_available`. +#ifdef TARGET_OS_VISION + visionos 1000.0, +#endif + * + )) { + return 1; + } else { + return 0; + } +} diff --git a/tests/run-make/apple-c-available-links/main.rs b/tests/run-make/apple-c-available-links/main.rs new file mode 100644 index 00000000000..4ffada43c1b --- /dev/null +++ b/tests/run-make/apple-c-available-links/main.rs @@ -0,0 +1,7 @@ +unsafe extern "C" { + safe fn foo() -> core::ffi::c_int; +} + +fn main() { + assert_eq!(foo(), 0); +} diff --git a/tests/run-make/apple-c-available-links/rmake.rs b/tests/run-make/apple-c-available-links/rmake.rs new file mode 100644 index 00000000000..44a5ee94d57 --- /dev/null +++ b/tests/run-make/apple-c-available-links/rmake.rs @@ -0,0 +1,14 @@ +//! Test that using `__builtin_available` in C (`@available` in Objective-C) +//! successfully links (because `std` provides the required symbols). + +//@ only-apple __builtin_available is (mostly) specific to Apple platforms. + +use run_make_support::{cc, rustc, target}; + +fn main() { + // Invoke the C compiler to generate an object file. + cc().arg("-c").input("foo.c").output("foo.o").run(); + + // Link the object file together with a Rust program. + rustc().target(target()).input("main.rs").link_arg("foo.o").run(); +} diff --git a/tests/run-make/multiline-args-value/cfg-frontmatter.stderr b/tests/run-make/multiline-args-value/cfg-frontmatter.stderr new file mode 100644 index 00000000000..9b06f84be48 --- /dev/null +++ b/tests/run-make/multiline-args-value/cfg-frontmatter.stderr @@ -0,0 +1,4 @@ +error: invalid `--cfg` argument: `--- + --- + key` (expected `key` or `key="value"`) + diff --git a/tests/run-make/multiline-args-value/cfg-shebang.stderr b/tests/run-make/multiline-args-value/cfg-shebang.stderr new file mode 100644 index 00000000000..09baf3b5123 --- /dev/null +++ b/tests/run-make/multiline-args-value/cfg-shebang.stderr @@ -0,0 +1,3 @@ +error: invalid `--cfg` argument: `#!/usr/bin/shebang + key` (expected `key` or `key="value"`) + diff --git a/tests/run-make/multiline-args-value/check-cfg-frontmatter.stderr b/tests/run-make/multiline-args-value/check-cfg-frontmatter.stderr new file mode 100644 index 00000000000..4a499cc0a1e --- /dev/null +++ b/tests/run-make/multiline-args-value/check-cfg-frontmatter.stderr @@ -0,0 +1,7 @@ +error: invalid `--check-cfg` argument: `--- + --- + cfg(key)` + | + = note: expected `cfg(name, values("value1", "value2", ... "valueN"))` + = note: visit <https://doc.rust-lang.org/nightly/rustc/check-cfg.html> for more details + diff --git a/tests/run-make/multiline-args-value/check-cfg-shebang.stderr b/tests/run-make/multiline-args-value/check-cfg-shebang.stderr new file mode 100644 index 00000000000..5bf18dc2b14 --- /dev/null +++ b/tests/run-make/multiline-args-value/check-cfg-shebang.stderr @@ -0,0 +1,6 @@ +error: invalid `--check-cfg` argument: `#!/usr/bin/shebang + cfg(key)` + | + = note: expected `cfg(name, values("value1", "value2", ... "valueN"))` + = note: visit <https://doc.rust-lang.org/nightly/rustc/check-cfg.html> for more details + diff --git a/tests/run-make/multiline-args-value/rmake.rs b/tests/run-make/multiline-args-value/rmake.rs new file mode 100644 index 00000000000..3964cbbc1e6 --- /dev/null +++ b/tests/run-make/multiline-args-value/rmake.rs @@ -0,0 +1,51 @@ +use run_make_support::{cwd, diff, rustc}; + +fn test_and_compare(test_name: &str, flag: &str, val: &str) { + let mut cmd = rustc(); + + let output = cmd.input("").arg("--crate-type=lib").arg(flag).arg(val).run_fail(); + + assert_eq!(output.stdout_utf8(), ""); + diff() + .expected_file(format!("{test_name}.stderr")) + .actual_text("stderr", output.stderr_utf8()) + .run(); +} + +fn main() { + // Verify that frontmatter isn't allowed in `--cfg` arguments. + // https://github.com/rust-lang/rust/issues/146130 + test_and_compare( + "cfg-frontmatter", + "--cfg", + r#"--- +--- +key"#, + ); + + // Verify that frontmatter isn't allowed in `--check-cfg` arguments. + // https://github.com/rust-lang/rust/issues/146130 + test_and_compare( + "check-cfg-frontmatter", + "--check-cfg", + r#"--- +--- +cfg(key)"#, + ); + + // Verify that shebang isn't allowed in `--cfg` arguments. + test_and_compare( + "cfg-shebang", + "--cfg", + r#"#!/usr/bin/shebang +key"#, + ); + + // Verify that shebang isn't allowed in `--check-cfg` arguments. + test_and_compare( + "check-cfg-shebang", + "--check-cfg", + r#"#!/usr/bin/shebang +cfg(key)"#, + ); +} diff --git a/tests/run-make/rustdoc-dep-info/rmake.rs b/tests/run-make/rustdoc-dep-info/rmake.rs index 625f81fd428..166e8d5702f 100644 --- a/tests/run-make/rustdoc-dep-info/rmake.rs +++ b/tests/run-make/rustdoc-dep-info/rmake.rs @@ -33,4 +33,16 @@ fn main() { // Now we check that we can provide a file name to the `dep-info` argument. rustdoc().input("lib.rs").arg("-Zunstable-options").emit("dep-info=bla.d").run(); assert!(path("bla.d").exists()); + + // The last emit-type wins. The same behavior as rustc. + rustdoc() + .input("lib.rs") + .arg("-Zunstable-options") + .emit("dep-info=precedence1.d") + .emit("dep-info=precedence2.d") + .emit("dep-info=precedence3.d") + .run(); + assert!(!path("precedence1.d").exists()); + assert!(!path("precedence2.d").exists()); + assert!(path("precedence3.d").exists()); } diff --git a/tests/run-make/rustdoc-search-load-itemtype/bar.rs b/tests/run-make/rustdoc-search-load-itemtype/bar.rs new file mode 100644 index 00000000000..0416b1b75b5 --- /dev/null +++ b/tests/run-make/rustdoc-search-load-itemtype/bar.rs @@ -0,0 +1,27 @@ +#![crate_type = "proc-macro"] + +extern crate proc_macro; +use proc_macro::*; + +//@ has bar/macro.a_procmacro.html +//@ hasraw search.index/name/*.js a_procmacro +#[proc_macro] +pub fn a_procmacro(_: TokenStream) -> TokenStream { + unimplemented!() +} + +//@ has bar/attr.a_procattribute.html +//@ hasraw search.index/name/*.js a_procattribute +#[proc_macro_attribute] +pub fn a_procattribute(_: TokenStream, _: TokenStream) -> TokenStream { + unimplemented!() +} + +//@ has bar/derive.AProcDerive.html +//@ !has bar/derive.a_procderive.html +//@ hasraw search.index/name/*.js AProcDerive +//@ !hasraw search.index/name/*.js a_procderive +#[proc_macro_derive(AProcDerive)] +pub fn a_procderive(_: TokenStream) -> TokenStream { + unimplemented!() +} diff --git a/tests/run-make/rustdoc-search-load-itemtype/baz.rs b/tests/run-make/rustdoc-search-load-itemtype/baz.rs new file mode 100644 index 00000000000..4d1f5430fc6 --- /dev/null +++ b/tests/run-make/rustdoc-search-load-itemtype/baz.rs @@ -0,0 +1,3 @@ +//@ has baz/struct.Baz.html +//@ hasraw search.index/name/*.js Baz +pub struct Baz; diff --git a/tests/run-make/rustdoc-search-load-itemtype/foo.rs b/tests/run-make/rustdoc-search-load-itemtype/foo.rs new file mode 100644 index 00000000000..93b372d10cb --- /dev/null +++ b/tests/run-make/rustdoc-search-load-itemtype/foo.rs @@ -0,0 +1,119 @@ +#![feature(extern_types, rustc_attrs, rustdoc_internals, trait_alias)] +#![allow(internal_features)] +#![no_std] + +//@ has foo/keyword.while.html +//@ hasraw search.index/name/*.js while +//@ !hasraw search.index/name/*.js w_keyword +#[doc(keyword = "while")] +mod w_keyword {} + +//@ has foo/primitive.u32.html +//@ hasraw search.index/name/*.js u32 +//@ !hasraw search.index/name/*.js u_primitive +#[rustc_doc_primitive = "u32"] +mod u_primitive {} + +//@ has foo/x_mod/index.html +//@ hasraw search.index/name/*.js x_mod +pub mod x_mod {} + +//@ hasraw foo/index.html y_crate +//@ hasraw search.index/name/*.js y_crate +#[doc(no_inline)] +pub extern crate core as y_crate; + +//@ hasraw foo/index.html z_import +//@ hasraw search.index/name/*.js z_import +#[doc(no_inline)] +pub use core::option as z_import; + +//@ has foo/struct.AStruct.html +//@ hasraw search.index/name/*.js AStruct +pub struct AStruct { + //@ hasraw foo/struct.AStruct.html a_structfield + //@ hasraw search.index/name/*.js a_structfield + pub a_structfield: i32, +} + +//@ has foo/enum.AEnum.html +//@ hasraw search.index/name/*.js AEnum +pub enum AEnum { + //@ hasraw foo/enum.AEnum.html AVariant + //@ hasraw search.index/name/*.js AVariant + AVariant, +} + +//@ has foo/fn.a_fn.html +//@ hasraw search.index/name/*.js a_fn +pub fn a_fn() {} + +//@ has foo/type.AType.html +//@ hasraw search.index/name/*.js AType +pub type AType = AStruct; + +//@ has foo/static.a_static.html +//@ hasraw search.index/name/*.js a_static +pub static a_static: i32 = 1; + +//@ has foo/trait.ATrait.html +//@ hasraw search.index/name/*.js ATrait +pub trait ATrait { + //@ hasraw foo/trait.ATrait.html a_tymethod + //@ hasraw search.index/name/*.js a_tymethod + fn a_tymethod(); + //@ hasraw foo/trait.ATrait.html AAssocType + //@ hasraw search.index/name/*.js AAssocType + type AAssocType; + //@ hasraw foo/trait.ATrait.html AAssocConst + //@ hasraw search.index/name/*.js AAssocConst + const AAssocConst: bool; +} + +// skip ItemType::Impl, since impls are anonymous +// and have no search entry + +impl AStruct { + //@ hasraw foo/struct.AStruct.html a_method + //@ hasraw search.index/name/*.js a_method + pub fn a_method() {} +} + +//@ has foo/macro.a_macro.html +//@ hasraw search.index/name/*.js a_macro +#[macro_export] +macro_rules! a_macro { + () => {}; +} + +//@ has foo/constant.A_CONSTANT.html +//@ hasraw search.index/name/*.js A_CONSTANT +pub const A_CONSTANT: i32 = 1; + +//@ has foo/union.AUnion.html +//@ hasraw search.index/name/*.js AUnion +pub union AUnion { + //@ hasraw foo/union.AUnion.html a_unionfield + //@ hasraw search.index/name/*.js a_unionfield + pub a_unionfield: i32, +} + +extern "C" { + //@ has foo/foreigntype.AForeignType.html + //@ hasraw search.index/name/*.js AForeignType + pub type AForeignType; +} + +// procattribute and procderive are defined in +// bar.rs, because they only work with proc_macro +// crate type. + +//@ has foo/traitalias.ATraitAlias.html +//@ hasraw search.index/name/*.js ATraitAlias +pub trait ATraitAlias = ATrait; + +//@ has foo/attribute.doc.html +//@ hasraw search.index/name/*.js doc +//@ !hasraw search.index/name/*.js aa_mod +#[doc(attribute = "doc")] +mod aa_mod {} diff --git a/tests/run-make/rustdoc-search-load-itemtype/rmake.rs b/tests/run-make/rustdoc-search-load-itemtype/rmake.rs new file mode 100644 index 00000000000..803eb8a61da --- /dev/null +++ b/tests/run-make/rustdoc-search-load-itemtype/rmake.rs @@ -0,0 +1,21 @@ +//@ ignore-cross-compile +//@ needs-crate-type: proc-macro + +// Test that rustdoc can deserialize a search index with every itemtype. +// https://github.com/rust-lang/rust/pull/146117 + +use std::path::Path; + +use run_make_support::{htmldocck, rfs, rustdoc, source_root}; + +fn main() { + let out_dir = Path::new("rustdoc-search-load-itemtype"); + + rfs::create_dir_all(&out_dir); + rustdoc().out_dir(&out_dir).input("foo.rs").run(); + rustdoc().out_dir(&out_dir).input("bar.rs").arg("--crate-type=proc-macro").run(); + rustdoc().out_dir(&out_dir).input("baz.rs").run(); + htmldocck().arg(out_dir).arg("foo.rs").run(); + htmldocck().arg(out_dir).arg("bar.rs").run(); + htmldocck().arg(out_dir).arg("baz.rs").run(); +} diff --git a/tests/run-make/sanitizer-cdylib-link/rmake.rs b/tests/run-make/sanitizer-cdylib-link/rmake.rs index f9d7fd98789..8ff58594d10 100644 --- a/tests/run-make/sanitizer-cdylib-link/rmake.rs +++ b/tests/run-make/sanitizer-cdylib-link/rmake.rs @@ -8,6 +8,8 @@ //@ needs-sanitizer-support //@ needs-sanitizer-address +//@ compile-flags: -C unsafe-allow-abi-mismatch=sanitizer + use run_make_support::{run_fail, rustc}; fn main() { diff --git a/tests/run-make/sanitizer-dylib-link/rmake.rs b/tests/run-make/sanitizer-dylib-link/rmake.rs index b43420adc72..bae3240a236 100644 --- a/tests/run-make/sanitizer-dylib-link/rmake.rs +++ b/tests/run-make/sanitizer-dylib-link/rmake.rs @@ -7,6 +7,8 @@ //@ needs-sanitizer-support //@ needs-sanitizer-address +//@ compile-flags: -C unsafe-allow-abi-mismatch=sanitizer + use run_make_support::{run_fail, rustc}; fn main() { diff --git a/tests/run-make/sanitizer-staticlib-link/rmake.rs b/tests/run-make/sanitizer-staticlib-link/rmake.rs index e38d15a8a3c..dda92626189 100644 --- a/tests/run-make/sanitizer-staticlib-link/rmake.rs +++ b/tests/run-make/sanitizer-staticlib-link/rmake.rs @@ -11,6 +11,8 @@ //@ needs-sanitizer-support //@ needs-sanitizer-address +//@ compile-flags: -C unsafe-allow-abi-mismatch=sanitizer + use run_make_support::{cc, extra_c_flags, extra_cxx_flags, run_fail, rustc, static_lib_name}; fn main() { diff --git a/tests/run-make/separate-link-fail/rmake.rs b/tests/run-make/separate-link-fail/rmake.rs index b5d5300de68..ba24f777395 100644 --- a/tests/run-make/separate-link-fail/rmake.rs +++ b/tests/run-make/separate-link-fail/rmake.rs @@ -11,5 +11,5 @@ fn main() { .arg("-Zlink-only") .input("foo.rs") .run_fail() - .assert_stderr_contains("The input does not look like a .rlink file"); + .assert_stderr_contains("the input does not look like a .rlink file"); } diff --git a/tests/rustdoc-gui/scrape-examples-ice-links.goml b/tests/rustdoc-gui/scrape-examples-ice-links.goml new file mode 100644 index 00000000000..0a159621935 --- /dev/null +++ b/tests/rustdoc-gui/scrape-examples-ice-links.goml @@ -0,0 +1,8 @@ +// Check that the line number column has the correct layout. +go-to: "file://" + |DOC_PATH| + "/scrape_ice/struct.ObscurelyNamedType1.html" +wait-for: ".scraped-example-title" +assert-attribute: (".scraped-example-title a", {"href": "../src/bar/bar.rs.html#2"}) +click: ".scraped-example-title a" +wait-for-property: ("h1", {"innerText": "bar/\nbar.rs"}) +// Ensure that the `--html-after-content` option was correctly handled. +assert: "#outer-html" diff --git a/tests/rustdoc-gui/sidebar-source-code.goml b/tests/rustdoc-gui/sidebar-source-code.goml index 3f6914a89d6..a3c19d8c6d0 100644 --- a/tests/rustdoc-gui/sidebar-source-code.goml +++ b/tests/rustdoc-gui/sidebar-source-code.goml @@ -71,7 +71,7 @@ assert: "//*[@class='dir-entry' and @open]/*[normalize-space()='sub_mod']" // Only "another_folder" should be "open" in "lib2". assert: "//*[@class='dir-entry' and not(@open)]/*[normalize-space()='another_mod']" // All other trees should be collapsed. -assert-count: ("//*[@id='src-sidebar']/details[not(normalize-space()='lib2') and not(@open)]", 12) +assert-count: ("//*[@id='src-sidebar']/details[not(normalize-space()='lib2') and not(@open)]", 13) // We now switch to mobile mode. set-window-size: (600, 600) diff --git a/tests/rustdoc-gui/src/scrape_examples_ice/Cargo.lock b/tests/rustdoc-gui/src/scrape_examples_ice/Cargo.lock new file mode 100644 index 00000000000..03410a0da12 --- /dev/null +++ b/tests/rustdoc-gui/src/scrape_examples_ice/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "scrape_ice" +version = "0.1.0" diff --git a/tests/rustdoc-gui/src/scrape_examples_ice/Cargo.toml b/tests/rustdoc-gui/src/scrape_examples_ice/Cargo.toml new file mode 100644 index 00000000000..076ff38ad23 --- /dev/null +++ b/tests/rustdoc-gui/src/scrape_examples_ice/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "scrape_ice" +version = "0.1.0" +edition = "2024" + +[[example]] +name = "bar" +path = "examples/bar.rs" +doc-scrape-examples = true diff --git a/tests/rustdoc-gui/src/scrape_examples_ice/examples/bar.rs b/tests/rustdoc-gui/src/scrape_examples_ice/examples/bar.rs new file mode 100644 index 00000000000..9f05f9e46e8 --- /dev/null +++ b/tests/rustdoc-gui/src/scrape_examples_ice/examples/bar.rs @@ -0,0 +1,3 @@ +fn main() { + let mut bar = scrape_ice::ObscurelyNamedType1::new(); +} diff --git a/tests/rustdoc-gui/src/scrape_examples_ice/extra.html b/tests/rustdoc-gui/src/scrape_examples_ice/extra.html new file mode 100644 index 00000000000..e7a420154a6 --- /dev/null +++ b/tests/rustdoc-gui/src/scrape_examples_ice/extra.html @@ -0,0 +1 @@ +<span id="outer-html"></span> diff --git a/tests/rustdoc-gui/src/scrape_examples_ice/src/lib.rs b/tests/rustdoc-gui/src/scrape_examples_ice/src/lib.rs new file mode 100644 index 00000000000..7d910c6de7d --- /dev/null +++ b/tests/rustdoc-gui/src/scrape_examples_ice/src/lib.rs @@ -0,0 +1,9 @@ +//@ run-flags:-Zrustdoc-scrape-examples +//@ compile-flags: --html-after-content extra.html +pub struct ObscurelyNamedType1; + +impl ObscurelyNamedType1 { + pub fn new() -> Self { + ObscurelyNamedType1 + } +} diff --git a/tests/rustdoc/sanitizer-option.rs b/tests/rustdoc/sanitizer-option.rs index 2adf1be51fd..7b0038138f0 100644 --- a/tests/rustdoc/sanitizer-option.rs +++ b/tests/rustdoc/sanitizer-option.rs @@ -1,6 +1,6 @@ //@ needs-sanitizer-support //@ needs-sanitizer-address -//@ compile-flags: --test -Z sanitizer=address +//@ compile-flags: --test -Z sanitizer=address -C unsafe-allow-abi-mismatch=sanitizer // // #43031: Verify that rustdoc passes `-Z` options to rustc. Use an extern // function that is provided by the sanitizer runtime, if flag is not passed diff --git a/tests/ui/.gitattributes b/tests/ui/.gitattributes index 9ea3d3fb0e1..4923ac100e1 100644 --- a/tests/ui/.gitattributes +++ b/tests/ui/.gitattributes @@ -3,4 +3,5 @@ json-bom-plus-crlf.rs -text json-bom-plus-crlf-multifile.rs -text json-bom-plus-crlf-multifile-aux.rs -text trailing-carriage-return-in-string.rs -text +frontmatter-crlf.rs -text *.bin -text diff --git a/tests/ui/abi/unsupported-varargs-fnptr.rs b/tests/ui/abi/unsupported-varargs-fnptr.rs index 1d23916d039..ac3913b3ef8 100644 --- a/tests/ui/abi/unsupported-varargs-fnptr.rs +++ b/tests/ui/abi/unsupported-varargs-fnptr.rs @@ -6,8 +6,6 @@ // We have to use this flag to force ABI computation of an invalid ABI //@ compile-flags: -Clink-dead-code -#![feature(extended_varargs_abi_support)] - // sometimes fn ptrs with varargs make layout and ABI computation ICE // as found in https://github.com/rust-lang/rust/issues/142107 diff --git a/tests/ui/abi/unsupported-varargs-fnptr.stderr b/tests/ui/abi/unsupported-varargs-fnptr.stderr index 238f2b31330..c05c55df82e 100644 --- a/tests/ui/abi/unsupported-varargs-fnptr.stderr +++ b/tests/ui/abi/unsupported-varargs-fnptr.stderr @@ -1,5 +1,5 @@ error[E0570]: "aapcs" is not a supported ABI for the current target - --> $DIR/unsupported-varargs-fnptr.rs:14:20 + --> $DIR/unsupported-varargs-fnptr.rs:12:20 | LL | fn aapcs(f: extern "aapcs" fn(usize, ...)) { | ^^^^^^^ diff --git a/tests/ui/asm/global-asm-isnt-really-a-mir-body.rs b/tests/ui/asm/global-asm-isnt-really-a-mir-body.rs index b7636d116ec..aef25d057d4 100644 --- a/tests/ui/asm/global-asm-isnt-really-a-mir-body.rs +++ b/tests/ui/asm/global-asm-isnt-really-a-mir-body.rs @@ -1,5 +1,7 @@ //@ revisions: emit_mir instrument cfi +//@ compile-flags: -C unsafe-allow-abi-mismatch=sanitizer + // Make sure we don't try to emit MIR for it. //@[emit_mir] compile-flags: --emit=mir diff --git a/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.rs b/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.rs deleted file mode 100644 index 1e0576b2186..00000000000 --- a/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.rs +++ /dev/null @@ -1,16 +0,0 @@ -//@ only-x86_64 - -fn efiapi(f: extern "efiapi" fn(usize, ...)) { - //~^ ERROR: unstable - f(22, 44); -} -fn sysv(f: extern "sysv64" fn(usize, ...)) { - //~^ ERROR: unstable - f(22, 44); -} -fn win(f: extern "win64" fn(usize, ...)) { - //~^ ERROR: unstable - f(22, 44); -} - -fn main() {} diff --git a/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.stderr b/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.stderr deleted file mode 100644 index 7ef54b639ad..00000000000 --- a/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.stderr +++ /dev/null @@ -1,33 +0,0 @@ -error[E0658]: C-variadic functions with the "efiapi" calling convention are unstable - --> $DIR/feature-gate-extended_varargs_abi_support.rs:3:14 - | -LL | fn efiapi(f: extern "efiapi" fn(usize, ...)) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #100189 <https://github.com/rust-lang/rust/issues/100189> for more information - = help: add `#![feature(extended_varargs_abi_support)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: C-variadic functions with the "sysv64" calling convention are unstable - --> $DIR/feature-gate-extended_varargs_abi_support.rs:7:12 - | -LL | fn sysv(f: extern "sysv64" fn(usize, ...)) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #100189 <https://github.com/rust-lang/rust/issues/100189> for more information - = help: add `#![feature(extended_varargs_abi_support)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: C-variadic functions with the "win64" calling convention are unstable - --> $DIR/feature-gate-extended_varargs_abi_support.rs:11:11 - | -LL | fn win(f: extern "win64" fn(usize, ...)) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #100189 <https://github.com/rust-lang/rust/issues/100189> for more information - = help: add `#![feature(extended_varargs_abi_support)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/c-variadic/issue-86053-1.rs b/tests/ui/c-variadic/issue-86053-1.rs index 8eeb99a4cc1..537d0263adf 100644 --- a/tests/ui/c-variadic/issue-86053-1.rs +++ b/tests/ui/c-variadic/issue-86053-1.rs @@ -13,6 +13,6 @@ fn ordering4 < 'a , 'b > ( a : , self , self , self , //~| ERROR unexpected `self` parameter in function //~| ERROR unexpected `self` parameter in function //~| ERROR `...` must be the last argument of a C-variadic function - //~| ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg + //~| ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention //~| ERROR cannot find type `F` in this scope } diff --git a/tests/ui/c-variadic/issue-86053-1.stderr b/tests/ui/c-variadic/issue-86053-1.stderr index 3bf8b7ba5d8..675dfd335c4 100644 --- a/tests/ui/c-variadic/issue-86053-1.stderr +++ b/tests/ui/c-variadic/issue-86053-1.stderr @@ -46,7 +46,7 @@ error: `...` must be the last argument of a C-variadic function LL | self , ... , self , self , ... ) where F : FnOnce ( & 'a & 'b usize ) { | ^^^ -error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg +error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention --> $DIR/issue-86053-1.rs:11:12 | LL | self , ... , self , self , ... ) where F : FnOnce ( & 'a & 'b usize ) { diff --git a/tests/ui/c-variadic/same-program-multiple-abis-arm.rs b/tests/ui/c-variadic/same-program-multiple-abis-arm.rs index 1b0bdecabfb..1445bbb47bb 100644 --- a/tests/ui/c-variadic/same-program-multiple-abis-arm.rs +++ b/tests/ui/c-variadic/same-program-multiple-abis-arm.rs @@ -1,4 +1,3 @@ -#![feature(extended_varargs_abi_support)] //@ run-pass //@ only-arm //@ ignore-thumb (this test uses arm assembly) diff --git a/tests/ui/c-variadic/same-program-multiple-abis-x86_64.rs b/tests/ui/c-variadic/same-program-multiple-abis-x86_64.rs index b21accb999e..aff8ab613c4 100644 --- a/tests/ui/c-variadic/same-program-multiple-abis-x86_64.rs +++ b/tests/ui/c-variadic/same-program-multiple-abis-x86_64.rs @@ -1,4 +1,3 @@ -#![feature(extended_varargs_abi_support)] //@ run-pass //@ only-x86_64 diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs index 800dd580af2..ad4fca8252d 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs @@ -71,6 +71,6 @@ extern "cmse-nonsecure-entry" fn wrapped_trait_object( } extern "cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) { - //~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg + //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention //~| ERROR requires `va_list` lang_item } diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr index f0190671b5a..7aeb6969feb 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr @@ -1,4 +1,4 @@ -error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg +error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention --> $DIR/generics.rs:73:53 | LL | extern "cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) { diff --git a/tests/ui/codegen/empty-static-libs-issue-108825.rs b/tests/ui/codegen/empty-static-libs-issue-108825.rs index 4c644be0954..4f8e4420f72 100644 --- a/tests/ui/codegen/empty-static-libs-issue-108825.rs +++ b/tests/ui/codegen/empty-static-libs-issue-108825.rs @@ -15,4 +15,4 @@ fn panic(_info: &core::panic::PanicInfo) -> ! { } //~? NOTE native-static-libs: -//~? NOTE Link against the following native artifacts when linking against this static library +//~? NOTE link against the following native artifacts when linking against this static library diff --git a/tests/ui/const-generics/failing_goal_with_repeat_expr_anon_const.stderr b/tests/ui/const-generics/failing_goal_with_repeat_expr_anon_const.stderr index 12de4f1dc30..cf5b55a0ce2 100644 --- a/tests/ui/const-generics/failing_goal_with_repeat_expr_anon_const.stderr +++ b/tests/ui/const-generics/failing_goal_with_repeat_expr_anon_const.stderr @@ -21,10 +21,6 @@ LL | [0u8; std::mem::size_of::<Self::A>()] == Self::P; | ^^ no implementation for `[u8; std::mem::size_of::<Self::A>()] == <Self as T>::A` | = help: the trait `PartialEq<<Self as T>::A>` is not implemented for `[u8; std::mem::size_of::<Self::A>()]` -help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement - | -LL | pub trait T where [u8; std::mem::size_of::<Self::A>()]: PartialEq<<Self as T>::A> { - | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ error: aborting due to 3 previous errors diff --git a/tests/ui/const-generics/min_const_generics/invalid-patterns.32bit.stderr b/tests/ui/const-generics/min_const_generics/invalid-patterns.32bit.stderr index 0c57eddbe93..cc6a813b747 100644 --- a/tests/ui/const-generics/min_const_generics/invalid-patterns.32bit.stderr +++ b/tests/ui/const-generics/min_const_generics/invalid-patterns.32bit.stderr @@ -14,7 +14,7 @@ error[E0080]: constructing invalid value: encountered 0x42, but expected a boole LL | get_flag::<{ unsafe { bool_raw.boolean } }, 'z'>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 1, align: 1) { 42 │ B } @@ -25,7 +25,7 @@ error[E0080]: constructing invalid value: encountered 0x42, but expected a boole LL | get_flag::<{ unsafe { bool_raw.boolean } }, { unsafe { char_raw.character } }>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 1, align: 1) { 42 │ B } diff --git a/tests/ui/const-generics/min_const_generics/invalid-patterns.64bit.stderr b/tests/ui/const-generics/min_const_generics/invalid-patterns.64bit.stderr index 0c57eddbe93..cc6a813b747 100644 --- a/tests/ui/const-generics/min_const_generics/invalid-patterns.64bit.stderr +++ b/tests/ui/const-generics/min_const_generics/invalid-patterns.64bit.stderr @@ -14,7 +14,7 @@ error[E0080]: constructing invalid value: encountered 0x42, but expected a boole LL | get_flag::<{ unsafe { bool_raw.boolean } }, 'z'>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 1, align: 1) { 42 │ B } @@ -25,7 +25,7 @@ error[E0080]: constructing invalid value: encountered 0x42, but expected a boole LL | get_flag::<{ unsafe { bool_raw.boolean } }, { unsafe { char_raw.character } }>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 1, align: 1) { 42 │ B } diff --git a/tests/ui/const-ptr/forbidden_slices.stderr b/tests/ui/const-ptr/forbidden_slices.stderr index 25d6f0461a9..70ae00af23f 100644 --- a/tests/ui/const-ptr/forbidden_slices.stderr +++ b/tests/ui/const-ptr/forbidden_slices.stderr @@ -4,7 +4,7 @@ error[E0080]: constructing invalid value: encountered a null reference LL | pub static S0: &[u32] = unsafe { from_raw_parts(ptr::null(), 0) }; | ^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -15,7 +15,7 @@ error[E0080]: constructing invalid value: encountered a null reference LL | pub static S1: &[()] = unsafe { from_raw_parts(ptr::null(), 0) }; | ^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -26,7 +26,7 @@ error[E0080]: constructing invalid value: encountered a dangling reference (goin LL | pub static S2: &[u32] = unsafe { from_raw_parts(&D0, 2) }; | ^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -37,7 +37,7 @@ error[E0080]: constructing invalid value at .<deref>[0]: encountered uninitializ LL | pub static S4: &[u8] = unsafe { from_raw_parts((&D1) as *const _ as _, 1) }; | ^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -50,7 +50,7 @@ LL | pub static S5: &[u8] = unsafe { from_raw_parts((&D3) as *const _ as _, size | = help: this code performed an operation that depends on the underlying bytes representing a pointer = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -61,7 +61,7 @@ error[E0080]: constructing invalid value at .<deref>[0]: encountered 0x11, but e LL | pub static S6: &[bool] = unsafe { from_raw_parts((&D0) as *const _ as _, 4) }; | ^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -72,7 +72,7 @@ error[E0080]: constructing invalid value at .<deref>[1]: encountered uninitializ LL | pub static S7: &[u16] = unsafe { | ^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -83,7 +83,7 @@ error[E0080]: constructing invalid value: encountered a dangling reference (goin LL | pub static S8: &[u64] = unsafe { | ^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -94,7 +94,7 @@ error[E0080]: constructing invalid value: encountered a null reference LL | pub static R0: &[u32] = unsafe { from_ptr_range(ptr::null()..ptr::null()) }; | ^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -117,7 +117,7 @@ error[E0080]: constructing invalid value at .<deref>[0]: encountered uninitializ LL | pub static R4: &[u8] = unsafe { | ^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -130,7 +130,7 @@ LL | pub static R5: &[u8] = unsafe { | = help: this code performed an operation that depends on the underlying bytes representing a pointer = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -141,7 +141,7 @@ error[E0080]: constructing invalid value at .<deref>[0]: encountered 0x11, but e LL | pub static R6: &[bool] = unsafe { | ^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -152,7 +152,7 @@ error[E0080]: constructing invalid value: encountered an unaligned reference (re LL | pub static R7: &[u16] = unsafe { | ^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } diff --git a/tests/ui/consts/const-eval/heap/alloc_intrinsic_uninit.32bit.stderr b/tests/ui/consts/const-eval/heap/alloc_intrinsic_uninit.32bit.stderr index a8c7ee93971..239bca51fc9 100644 --- a/tests/ui/consts/const-eval/heap/alloc_intrinsic_uninit.32bit.stderr +++ b/tests/ui/consts/const-eval/heap/alloc_intrinsic_uninit.32bit.stderr @@ -4,7 +4,7 @@ error[E0080]: constructing invalid value at .<deref>: encountered uninitialized LL | const BAR: &i32 = unsafe { | ^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { ╾ALLOC0<imm>╼ │ ╾──╼ } diff --git a/tests/ui/consts/const-eval/heap/alloc_intrinsic_uninit.64bit.stderr b/tests/ui/consts/const-eval/heap/alloc_intrinsic_uninit.64bit.stderr index 47e1c22cc2c..3c6d66ac5b6 100644 --- a/tests/ui/consts/const-eval/heap/alloc_intrinsic_uninit.64bit.stderr +++ b/tests/ui/consts/const-eval/heap/alloc_intrinsic_uninit.64bit.stderr @@ -4,7 +4,7 @@ error[E0080]: constructing invalid value at .<deref>: encountered uninitialized LL | const BAR: &i32 = unsafe { | ^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { ╾ALLOC0<imm>╼ │ ╾──────╼ } diff --git a/tests/ui/consts/const-eval/heap/dealloc_intrinsic_dangling.stderr b/tests/ui/consts/const-eval/heap/dealloc_intrinsic_dangling.stderr index d4039e1952c..152eeababc5 100644 --- a/tests/ui/consts/const-eval/heap/dealloc_intrinsic_dangling.stderr +++ b/tests/ui/consts/const-eval/heap/dealloc_intrinsic_dangling.stderr @@ -4,7 +4,7 @@ error[E0080]: constructing invalid value: encountered a dangling reference (use- LL | const _X: &'static u8 = unsafe { | ^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } diff --git a/tests/ui/consts/const-eval/raw-bytes.32bit.stderr b/tests/ui/consts/const-eval/raw-bytes.32bit.stderr index 36183e28921..2861f82ec53 100644 --- a/tests/ui/consts/const-eval/raw-bytes.32bit.stderr +++ b/tests/ui/consts/const-eval/raw-bytes.32bit.stderr @@ -4,7 +4,7 @@ error[E0080]: constructing invalid value at .<enum-tag>: encountered 0x00000001, LL | const BAD_ENUM: Enum = unsafe { mem::transmute(1usize) }; | ^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { 01 00 00 00 │ .... } @@ -15,7 +15,7 @@ error[E0080]: constructing invalid value at .<enum-tag>: encountered 0x00000000, LL | const BAD_ENUM2: Enum2 = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { 00 00 00 00 │ .... } @@ -26,7 +26,7 @@ error[E0080]: constructing invalid value at .<enum-tag>: encountered an uninhabi LL | const BAD_UNINHABITED_VARIANT1: UninhDiscriminant = unsafe { mem::transmute(1u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 1, align: 1) { 01 │ . } @@ -37,7 +37,7 @@ error[E0080]: constructing invalid value at .<enum-tag>: encountered an uninhabi LL | const BAD_UNINHABITED_VARIANT2: UninhDiscriminant = unsafe { mem::transmute(3u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 1, align: 1) { 03 │ . } @@ -48,7 +48,7 @@ error[E0080]: constructing invalid value at .<enum-variant(Some)>.0.1: encounter LL | const BAD_OPTION_CHAR: Option<(char, char)> = Some(('x', unsafe { mem::transmute(!0u32) })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { 78 00 00 00 ff ff ff ff │ x....... } @@ -59,7 +59,7 @@ error[E0080]: constructing invalid value: encountered 0, but expected something LL | const NULL_PTR: NonNull<u8> = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { 00 00 00 00 │ .... } @@ -70,7 +70,7 @@ error[E0080]: constructing invalid value at .0: encountered 0, but expected some LL | const NULL_U8: NonZero<u8> = unsafe { mem::transmute(0u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 1, align: 1) { 00 │ . } @@ -81,7 +81,7 @@ error[E0080]: constructing invalid value at .0: encountered 0, but expected some LL | const NULL_USIZE: NonZero<usize> = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { 00 00 00 00 │ .... } @@ -92,7 +92,7 @@ error[E0080]: constructing invalid value: encountered 42, but expected something LL | const BAD_RANGE1: RestrictedRange1 = unsafe { RestrictedRange1(42) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { 2a 00 00 00 │ *... } @@ -103,7 +103,7 @@ error[E0080]: constructing invalid value: encountered 20, but expected something LL | const BAD_RANGE2: RestrictedRange2 = unsafe { RestrictedRange2(20) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { 14 00 00 00 │ .... } @@ -114,7 +114,7 @@ error[E0080]: constructing invalid value: encountered 0, but expected something LL | const NULL_FAT_PTR: NonNull<dyn Send> = unsafe { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { 00 00 00 00 ╾ALLOC_ID╼ │ ....╾──╼ } @@ -125,7 +125,7 @@ error[E0080]: constructing invalid value: encountered an unaligned reference (re LL | const UNALIGNED: &u16 = unsafe { mem::transmute(&[0u8; 4]) }; | ^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { ╾ALLOC_ID╼ │ ╾──╼ } @@ -136,7 +136,7 @@ error[E0080]: constructing invalid value: encountered an unaligned box (required LL | const UNALIGNED_BOX: Box<u16> = unsafe { mem::transmute(&[0u8; 4]) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { ╾ALLOC_ID╼ │ ╾──╼ } @@ -147,7 +147,7 @@ error[E0080]: constructing invalid value: encountered a null reference LL | const NULL: &u16 = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { 00 00 00 00 │ .... } @@ -158,7 +158,7 @@ error[E0080]: constructing invalid value: encountered a null box LL | const NULL_BOX: Box<u16> = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { 00 00 00 00 │ .... } @@ -169,7 +169,7 @@ error[E0080]: constructing invalid value: encountered a dangling reference (0x53 LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { 39 05 00 00 │ 9... } @@ -180,7 +180,7 @@ error[E0080]: constructing invalid value: encountered a dangling box (0x539[noal LL | const USIZE_AS_BOX: Box<u8> = unsafe { mem::transmute(1337usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { 39 05 00 00 │ 9... } @@ -191,7 +191,7 @@ error[E0080]: constructing invalid value: encountered null pointer, but expected LL | const NULL_FN_PTR: fn() = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { 00 00 00 00 │ .... } @@ -202,7 +202,7 @@ error[E0080]: constructing invalid value: encountered 0xd[noalloc], but expected LL | const DANGLING_FN_PTR: fn() = unsafe { mem::transmute(13usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { 0d 00 00 00 │ .... } @@ -213,7 +213,7 @@ error[E0080]: constructing invalid value: encountered ALLOC3<imm>, but expected LL | const DATA_FN_PTR: fn() = unsafe { mem::transmute(&13) }; | ^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { ╾ALLOC_ID╼ │ ╾──╼ } @@ -224,7 +224,7 @@ error[E0080]: constructing invalid value: encountered a reference pointing to un LL | const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { 01 00 00 00 │ .... } @@ -235,7 +235,7 @@ error[E0080]: constructing invalid value: encountered a dangling reference (goin LL | const STR_TOO_LONG: &str = unsafe { mem::transmute((&42u8, 999usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { ╾ALLOC_ID╼ e7 03 00 00 │ ╾──╼.... } @@ -246,7 +246,7 @@ error[E0080]: constructing invalid value at .0: encountered invalid reference me LL | const NESTED_STR_MUCH_TOO_LONG: (&str,) = (unsafe { mem::transmute((&42, usize::MAX)) },); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { ╾ALLOC_ID╼ ff ff ff ff │ ╾──╼.... } @@ -257,7 +257,7 @@ error[E0080]: constructing invalid value: encountered invalid reference metadata LL | const MY_STR_MUCH_TOO_LONG: &MyStr = unsafe { mem::transmute((&42u8, usize::MAX)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { ╾ALLOC_ID╼ ff ff ff ff │ ╾──╼.... } @@ -268,7 +268,7 @@ error[E0080]: constructing invalid value at .<deref>: encountered uninitialized LL | const STR_NO_INIT: &str = unsafe { mem::transmute::<&[_], _>(&[MaybeUninit::<u8> { uninit: () }]) }; | ^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { ╾ALLOC_ID╼ 01 00 00 00 │ ╾──╼.... } @@ -279,7 +279,7 @@ error[E0080]: constructing invalid value at .<deref>.0: encountered uninitialize LL | const MYSTR_NO_INIT: &MyStr = unsafe { mem::transmute::<&[_], _>(&[MaybeUninit::<u8> { uninit: () }]) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { ╾ALLOC_ID╼ 01 00 00 00 │ ╾──╼.... } @@ -292,7 +292,7 @@ LL | const MYSTR_NO_INIT_ISSUE83182: &MyStr = unsafe { mem::transmute::<&[_], _> | = help: this code performed an operation that depends on the underlying bytes representing a pointer = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { ╾ALLOC_ID╼ 01 00 00 00 │ ╾──╼.... } @@ -303,7 +303,7 @@ error[E0080]: constructing invalid value: encountered a dangling reference (goin LL | const SLICE_TOO_LONG: &[u8] = unsafe { mem::transmute((&42u8, 999usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { ╾ALLOC_ID╼ e7 03 00 00 │ ╾──╼.... } @@ -314,7 +314,7 @@ error[E0080]: constructing invalid value: encountered invalid reference metadata LL | const SLICE_TOO_LONG_OVERFLOW: &[u32] = unsafe { mem::transmute((&42u32, isize::MAX)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { ╾ALLOC_ID╼ ff ff ff 7f │ ╾──╼.... } @@ -325,7 +325,7 @@ error[E0080]: constructing invalid value: encountered a dangling box (going beyo LL | const SLICE_TOO_LONG_BOX: Box<[u8]> = unsafe { mem::transmute((&42u8, 999usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { ╾ALLOC_ID╼ e7 03 00 00 │ ╾──╼.... } @@ -336,7 +336,7 @@ error[E0080]: constructing invalid value at .<deref>[0]: encountered 0x03, but e LL | const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { mem::transmute(3u8) }]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { ╾ALLOC_ID╼ │ ╾──╼ } @@ -353,7 +353,7 @@ error[E0080]: constructing invalid value at .<deref>.0: encountered 0x03, but ex LL | const MYSLICE_PREFIX_BAD: &MySliceBool = &MySlice(unsafe { mem::transmute(3u8) }, [false]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { ╾ALLOC_ID╼ │ ╾──╼ } @@ -370,7 +370,7 @@ error[E0080]: constructing invalid value at .<deref>.1[0]: encountered 0x03, but LL | const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { mem::transmute(3u8) }]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { ╾ALLOC_ID╼ │ ╾──╼ } @@ -387,7 +387,7 @@ error[E0080]: constructing invalid value at .0: encountered ALLOC17<imm>, but ex LL | const TRAIT_OBJ_SHORT_VTABLE_1: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u8))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { ╾ALLOC_ID╼ ╾ALLOC_ID╼ │ ╾──╼╾──╼ } @@ -398,7 +398,7 @@ error[E0080]: constructing invalid value at .0: encountered ALLOC19<imm>, but ex LL | const TRAIT_OBJ_SHORT_VTABLE_2: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u64))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { ╾ALLOC_ID╼ ╾ALLOC_ID╼ │ ╾──╼╾──╼ } @@ -409,7 +409,7 @@ error[E0080]: constructing invalid value at .0: encountered 0x4[noalloc], but ex LL | const TRAIT_OBJ_INT_VTABLE: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, 4usize))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { ╾ALLOC_ID╼ 04 00 00 00 │ ╾──╼.... } @@ -420,7 +420,7 @@ error[E0080]: constructing invalid value at .0: encountered ALLOC22<imm>, but ex LL | const TRAIT_OBJ_BAD_DROP_FN_NOT_FN_PTR: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &[&42u8; 8]))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { ╾ALLOC_ID╼ ╾ALLOC_ID╼ │ ╾──╼╾──╼ } @@ -431,7 +431,7 @@ error[E0080]: constructing invalid value at .<deref>.<dyn-downcast>: encountered LL | const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_, &bool>(&3u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { ╾ALLOC_ID╼ ╾ALLOC_ID╼ │ ╾──╼╾──╼ } @@ -442,7 +442,7 @@ error[E0080]: constructing invalid value: encountered null pointer, but expected LL | const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { mem::transmute((&92u8, 0usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { ╾ALLOC_ID╼ 00 00 00 00 │ ╾──╼.... } @@ -453,7 +453,7 @@ error[E0080]: constructing invalid value: encountered ALLOC27<imm>, but expected LL | const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { ╾ALLOC_ID╼ ╾ALLOC_ID╼ │ ╾──╼╾──╼ } @@ -464,7 +464,7 @@ error[E0080]: constructing invalid value: encountered a reference pointing to un LL | const _: &[!; 1] = unsafe { &*(1_usize as *const [!; 1]) }; | ^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { 01 00 00 00 │ .... } @@ -475,7 +475,7 @@ error[E0080]: constructing invalid value at .<deref>[0]: encountered a value of LL | const _: &[!] = unsafe { &*(1_usize as *const [!; 1]) }; | ^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { 01 00 00 00 01 00 00 00 │ ........ } @@ -486,7 +486,7 @@ error[E0080]: constructing invalid value at .<deref>[0]: encountered a value of LL | const _: &[!] = unsafe { &*(1_usize as *const [!; 42]) }; | ^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { 01 00 00 00 2a 00 00 00 │ ....*... } @@ -497,7 +497,7 @@ error[E0080]: constructing invalid value at .<deref>[0]: encountered uninitializ LL | pub static S4: &[u8] = unsafe { from_raw_parts((&D1) as *const _ as _, 1) }; | ^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { ╾ALLOC_ID╼ 01 00 00 00 │ ╾──╼.... } @@ -510,7 +510,7 @@ LL | pub static S5: &[u8] = unsafe { from_raw_parts((&D3) as *const _ as _, mem: | = help: this code performed an operation that depends on the underlying bytes representing a pointer = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { ╾ALLOC_ID╼ 04 00 00 00 │ ╾──╼.... } @@ -521,7 +521,7 @@ error[E0080]: constructing invalid value at .<deref>[0]: encountered 0x11, but e LL | pub static S6: &[bool] = unsafe { from_raw_parts((&D0) as *const _ as _, 4) }; | ^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { ╾ALLOC_ID╼ 04 00 00 00 │ ╾──╼.... } @@ -532,7 +532,7 @@ error[E0080]: constructing invalid value at .<deref>[1]: encountered uninitializ LL | pub static S7: &[u16] = unsafe { | ^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { ╾ALLOC_ID+0x2╼ 04 00 00 00 │ ╾──╼.... } @@ -543,7 +543,7 @@ error[E0080]: constructing invalid value at .<deref>[0]: encountered uninitializ LL | pub static R4: &[u8] = unsafe { | ^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { ╾ALLOC_ID╼ 01 00 00 00 │ ╾──╼.... } @@ -556,7 +556,7 @@ LL | pub static R5: &[u8] = unsafe { | = help: this code performed an operation that depends on the underlying bytes representing a pointer = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { ╾ALLOC_ID╼ 04 00 00 00 │ ╾──╼.... } @@ -567,7 +567,7 @@ error[E0080]: constructing invalid value at .<deref>[0]: encountered 0x11, but e LL | pub static R6: &[bool] = unsafe { | ^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { ╾ALLOC_ID╼ 04 00 00 00 │ ╾──╼.... } diff --git a/tests/ui/consts/const-eval/raw-bytes.64bit.stderr b/tests/ui/consts/const-eval/raw-bytes.64bit.stderr index c53326534fd..8e6dc66a40e 100644 --- a/tests/ui/consts/const-eval/raw-bytes.64bit.stderr +++ b/tests/ui/consts/const-eval/raw-bytes.64bit.stderr @@ -4,7 +4,7 @@ error[E0080]: constructing invalid value at .<enum-tag>: encountered 0x000000000 LL | const BAD_ENUM: Enum = unsafe { mem::transmute(1usize) }; | ^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { 01 00 00 00 00 00 00 00 │ ........ } @@ -15,7 +15,7 @@ error[E0080]: constructing invalid value at .<enum-tag>: encountered 0x000000000 LL | const BAD_ENUM2: Enum2 = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { 00 00 00 00 00 00 00 00 │ ........ } @@ -26,7 +26,7 @@ error[E0080]: constructing invalid value at .<enum-tag>: encountered an uninhabi LL | const BAD_UNINHABITED_VARIANT1: UninhDiscriminant = unsafe { mem::transmute(1u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 1, align: 1) { 01 │ . } @@ -37,7 +37,7 @@ error[E0080]: constructing invalid value at .<enum-tag>: encountered an uninhabi LL | const BAD_UNINHABITED_VARIANT2: UninhDiscriminant = unsafe { mem::transmute(3u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 1, align: 1) { 03 │ . } @@ -48,7 +48,7 @@ error[E0080]: constructing invalid value at .<enum-variant(Some)>.0.1: encounter LL | const BAD_OPTION_CHAR: Option<(char, char)> = Some(('x', unsafe { mem::transmute(!0u32) })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { 78 00 00 00 ff ff ff ff │ x....... } @@ -59,7 +59,7 @@ error[E0080]: constructing invalid value: encountered 0, but expected something LL | const NULL_PTR: NonNull<u8> = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { 00 00 00 00 00 00 00 00 │ ........ } @@ -70,7 +70,7 @@ error[E0080]: constructing invalid value at .0: encountered 0, but expected some LL | const NULL_U8: NonZero<u8> = unsafe { mem::transmute(0u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 1, align: 1) { 00 │ . } @@ -81,7 +81,7 @@ error[E0080]: constructing invalid value at .0: encountered 0, but expected some LL | const NULL_USIZE: NonZero<usize> = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { 00 00 00 00 00 00 00 00 │ ........ } @@ -92,7 +92,7 @@ error[E0080]: constructing invalid value: encountered 42, but expected something LL | const BAD_RANGE1: RestrictedRange1 = unsafe { RestrictedRange1(42) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { 2a 00 00 00 │ *... } @@ -103,7 +103,7 @@ error[E0080]: constructing invalid value: encountered 20, but expected something LL | const BAD_RANGE2: RestrictedRange2 = unsafe { RestrictedRange2(20) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { 14 00 00 00 │ .... } @@ -114,7 +114,7 @@ error[E0080]: constructing invalid value: encountered 0, but expected something LL | const NULL_FAT_PTR: NonNull<dyn Send> = unsafe { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { 00 00 00 00 00 00 00 00 ╾ALLOC_ID╼ │ ........╾──────╼ } @@ -125,7 +125,7 @@ error[E0080]: constructing invalid value: encountered an unaligned reference (re LL | const UNALIGNED: &u16 = unsafe { mem::transmute(&[0u8; 4]) }; | ^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { ╾ALLOC_ID╼ │ ╾──────╼ } @@ -136,7 +136,7 @@ error[E0080]: constructing invalid value: encountered an unaligned box (required LL | const UNALIGNED_BOX: Box<u16> = unsafe { mem::transmute(&[0u8; 4]) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { ╾ALLOC_ID╼ │ ╾──────╼ } @@ -147,7 +147,7 @@ error[E0080]: constructing invalid value: encountered a null reference LL | const NULL: &u16 = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { 00 00 00 00 00 00 00 00 │ ........ } @@ -158,7 +158,7 @@ error[E0080]: constructing invalid value: encountered a null box LL | const NULL_BOX: Box<u16> = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { 00 00 00 00 00 00 00 00 │ ........ } @@ -169,7 +169,7 @@ error[E0080]: constructing invalid value: encountered a dangling reference (0x53 LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { 39 05 00 00 00 00 00 00 │ 9....... } @@ -180,7 +180,7 @@ error[E0080]: constructing invalid value: encountered a dangling box (0x539[noal LL | const USIZE_AS_BOX: Box<u8> = unsafe { mem::transmute(1337usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { 39 05 00 00 00 00 00 00 │ 9....... } @@ -191,7 +191,7 @@ error[E0080]: constructing invalid value: encountered null pointer, but expected LL | const NULL_FN_PTR: fn() = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { 00 00 00 00 00 00 00 00 │ ........ } @@ -202,7 +202,7 @@ error[E0080]: constructing invalid value: encountered 0xd[noalloc], but expected LL | const DANGLING_FN_PTR: fn() = unsafe { mem::transmute(13usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { 0d 00 00 00 00 00 00 00 │ ........ } @@ -213,7 +213,7 @@ error[E0080]: constructing invalid value: encountered ALLOC3<imm>, but expected LL | const DATA_FN_PTR: fn() = unsafe { mem::transmute(&13) }; | ^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { ╾ALLOC_ID╼ │ ╾──────╼ } @@ -224,7 +224,7 @@ error[E0080]: constructing invalid value: encountered a reference pointing to un LL | const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { 01 00 00 00 00 00 00 00 │ ........ } @@ -235,7 +235,7 @@ error[E0080]: constructing invalid value: encountered a dangling reference (goin LL | const STR_TOO_LONG: &str = unsafe { mem::transmute((&42u8, 999usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { ╾ALLOC_ID╼ e7 03 00 00 00 00 00 00 │ ╾──────╼........ } @@ -246,7 +246,7 @@ error[E0080]: constructing invalid value at .0: encountered invalid reference me LL | const NESTED_STR_MUCH_TOO_LONG: (&str,) = (unsafe { mem::transmute((&42, usize::MAX)) },); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { ╾ALLOC_ID╼ ff ff ff ff ff ff ff ff │ ╾──────╼........ } @@ -257,7 +257,7 @@ error[E0080]: constructing invalid value: encountered invalid reference metadata LL | const MY_STR_MUCH_TOO_LONG: &MyStr = unsafe { mem::transmute((&42u8, usize::MAX)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { ╾ALLOC_ID╼ ff ff ff ff ff ff ff ff │ ╾──────╼........ } @@ -268,7 +268,7 @@ error[E0080]: constructing invalid value at .<deref>: encountered uninitialized LL | const STR_NO_INIT: &str = unsafe { mem::transmute::<&[_], _>(&[MaybeUninit::<u8> { uninit: () }]) }; | ^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { ╾ALLOC_ID╼ 01 00 00 00 00 00 00 00 │ ╾──────╼........ } @@ -279,7 +279,7 @@ error[E0080]: constructing invalid value at .<deref>.0: encountered uninitialize LL | const MYSTR_NO_INIT: &MyStr = unsafe { mem::transmute::<&[_], _>(&[MaybeUninit::<u8> { uninit: () }]) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { ╾ALLOC_ID╼ 01 00 00 00 00 00 00 00 │ ╾──────╼........ } @@ -292,7 +292,7 @@ LL | const MYSTR_NO_INIT_ISSUE83182: &MyStr = unsafe { mem::transmute::<&[_], _> | = help: this code performed an operation that depends on the underlying bytes representing a pointer = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { ╾ALLOC_ID╼ 01 00 00 00 00 00 00 00 │ ╾──────╼........ } @@ -303,7 +303,7 @@ error[E0080]: constructing invalid value: encountered a dangling reference (goin LL | const SLICE_TOO_LONG: &[u8] = unsafe { mem::transmute((&42u8, 999usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { ╾ALLOC_ID╼ e7 03 00 00 00 00 00 00 │ ╾──────╼........ } @@ -314,7 +314,7 @@ error[E0080]: constructing invalid value: encountered invalid reference metadata LL | const SLICE_TOO_LONG_OVERFLOW: &[u32] = unsafe { mem::transmute((&42u32, isize::MAX)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { ╾ALLOC_ID╼ ff ff ff ff ff ff ff 7f │ ╾──────╼........ } @@ -325,7 +325,7 @@ error[E0080]: constructing invalid value: encountered a dangling box (going beyo LL | const SLICE_TOO_LONG_BOX: Box<[u8]> = unsafe { mem::transmute((&42u8, 999usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { ╾ALLOC_ID╼ e7 03 00 00 00 00 00 00 │ ╾──────╼........ } @@ -336,7 +336,7 @@ error[E0080]: constructing invalid value at .<deref>[0]: encountered 0x03, but e LL | const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { mem::transmute(3u8) }]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { ╾ALLOC_ID╼ │ ╾──────╼ } @@ -353,7 +353,7 @@ error[E0080]: constructing invalid value at .<deref>.0: encountered 0x03, but ex LL | const MYSLICE_PREFIX_BAD: &MySliceBool = &MySlice(unsafe { mem::transmute(3u8) }, [false]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { ╾ALLOC_ID╼ │ ╾──────╼ } @@ -370,7 +370,7 @@ error[E0080]: constructing invalid value at .<deref>.1[0]: encountered 0x03, but LL | const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { mem::transmute(3u8) }]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { ╾ALLOC_ID╼ │ ╾──────╼ } @@ -387,7 +387,7 @@ error[E0080]: constructing invalid value at .0: encountered ALLOC17<imm>, but ex LL | const TRAIT_OBJ_SHORT_VTABLE_1: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u8))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { ╾ALLOC_ID╼ ╾ALLOC_ID╼ │ ╾──────╼╾──────╼ } @@ -398,7 +398,7 @@ error[E0080]: constructing invalid value at .0: encountered ALLOC19<imm>, but ex LL | const TRAIT_OBJ_SHORT_VTABLE_2: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u64))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { ╾ALLOC_ID╼ ╾ALLOC_ID╼ │ ╾──────╼╾──────╼ } @@ -409,7 +409,7 @@ error[E0080]: constructing invalid value at .0: encountered 0x4[noalloc], but ex LL | const TRAIT_OBJ_INT_VTABLE: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, 4usize))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { ╾ALLOC_ID╼ 04 00 00 00 00 00 00 00 │ ╾──────╼........ } @@ -420,7 +420,7 @@ error[E0080]: constructing invalid value at .0: encountered ALLOC22<imm>, but ex LL | const TRAIT_OBJ_BAD_DROP_FN_NOT_FN_PTR: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &[&42u8; 8]))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { ╾ALLOC_ID╼ ╾ALLOC_ID╼ │ ╾──────╼╾──────╼ } @@ -431,7 +431,7 @@ error[E0080]: constructing invalid value at .<deref>.<dyn-downcast>: encountered LL | const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_, &bool>(&3u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { ╾ALLOC_ID╼ ╾ALLOC_ID╼ │ ╾──────╼╾──────╼ } @@ -442,7 +442,7 @@ error[E0080]: constructing invalid value: encountered null pointer, but expected LL | const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { mem::transmute((&92u8, 0usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { ╾ALLOC_ID╼ 00 00 00 00 00 00 00 00 │ ╾──────╼........ } @@ -453,7 +453,7 @@ error[E0080]: constructing invalid value: encountered ALLOC27<imm>, but expected LL | const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { ╾ALLOC_ID╼ ╾ALLOC_ID╼ │ ╾──────╼╾──────╼ } @@ -464,7 +464,7 @@ error[E0080]: constructing invalid value: encountered a reference pointing to un LL | const _: &[!; 1] = unsafe { &*(1_usize as *const [!; 1]) }; | ^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { 01 00 00 00 00 00 00 00 │ ........ } @@ -475,7 +475,7 @@ error[E0080]: constructing invalid value at .<deref>[0]: encountered a value of LL | const _: &[!] = unsafe { &*(1_usize as *const [!; 1]) }; | ^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { 01 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 │ ................ } @@ -486,7 +486,7 @@ error[E0080]: constructing invalid value at .<deref>[0]: encountered a value of LL | const _: &[!] = unsafe { &*(1_usize as *const [!; 42]) }; | ^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { 01 00 00 00 00 00 00 00 2a 00 00 00 00 00 00 00 │ ........*....... } @@ -497,7 +497,7 @@ error[E0080]: constructing invalid value at .<deref>[0]: encountered uninitializ LL | pub static S4: &[u8] = unsafe { from_raw_parts((&D1) as *const _ as _, 1) }; | ^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { ╾ALLOC_ID╼ 01 00 00 00 00 00 00 00 │ ╾──────╼........ } @@ -510,7 +510,7 @@ LL | pub static S5: &[u8] = unsafe { from_raw_parts((&D3) as *const _ as _, mem: | = help: this code performed an operation that depends on the underlying bytes representing a pointer = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { ╾ALLOC_ID╼ 08 00 00 00 00 00 00 00 │ ╾──────╼........ } @@ -521,7 +521,7 @@ error[E0080]: constructing invalid value at .<deref>[0]: encountered 0x11, but e LL | pub static S6: &[bool] = unsafe { from_raw_parts((&D0) as *const _ as _, 4) }; | ^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { ╾ALLOC_ID╼ 04 00 00 00 00 00 00 00 │ ╾──────╼........ } @@ -532,7 +532,7 @@ error[E0080]: constructing invalid value at .<deref>[1]: encountered uninitializ LL | pub static S7: &[u16] = unsafe { | ^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { ╾ALLOC_ID+0x2╼ 04 00 00 00 00 00 00 00 │ ╾──────╼........ } @@ -543,7 +543,7 @@ error[E0080]: constructing invalid value at .<deref>[0]: encountered uninitializ LL | pub static R4: &[u8] = unsafe { | ^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { ╾ALLOC_ID╼ 01 00 00 00 00 00 00 00 │ ╾──────╼........ } @@ -556,7 +556,7 @@ LL | pub static R5: &[u8] = unsafe { | = help: this code performed an operation that depends on the underlying bytes representing a pointer = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { ╾ALLOC_ID╼ 08 00 00 00 00 00 00 00 │ ╾──────╼........ } @@ -567,7 +567,7 @@ error[E0080]: constructing invalid value at .<deref>[0]: encountered 0x11, but e LL | pub static R6: &[bool] = unsafe { | ^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { ╾ALLOC_ID╼ 04 00 00 00 00 00 00 00 │ ╾──────╼........ } diff --git a/tests/ui/consts/const-eval/transmute-const.stderr b/tests/ui/consts/const-eval/transmute-const.stderr index ed3b3df70dd..53665c176a7 100644 --- a/tests/ui/consts/const-eval/transmute-const.stderr +++ b/tests/ui/consts/const-eval/transmute-const.stderr @@ -4,7 +4,7 @@ error[E0080]: constructing invalid value: encountered 0x03, but expected a boole LL | static FOO: bool = unsafe { mem::transmute(3u8) }; | ^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 1, align: 1) { 03 │ . } diff --git a/tests/ui/consts/const-eval/ub-enum.stderr b/tests/ui/consts/const-eval/ub-enum.stderr index 5cbd6176c92..1efd9383229 100644 --- a/tests/ui/consts/const-eval/ub-enum.stderr +++ b/tests/ui/consts/const-eval/ub-enum.stderr @@ -4,7 +4,7 @@ error[E0080]: constructing invalid value at .<enum-tag>: encountered 0x01, but e LL | const BAD_ENUM: Enum = unsafe { mem::transmute(1usize) }; | ^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -33,7 +33,7 @@ error[E0080]: constructing invalid value at .<enum-tag>: encountered 0x0, but ex LL | const BAD_ENUM2: Enum2 = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -81,7 +81,7 @@ error[E0080]: constructing invalid value at .<enum-tag>: encountered an uninhabi LL | const BAD_UNINHABITED_VARIANT1: UninhDiscriminant = unsafe { mem::transmute(1u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -92,7 +92,7 @@ error[E0080]: constructing invalid value at .<enum-tag>: encountered an uninhabi LL | const BAD_UNINHABITED_VARIANT2: UninhDiscriminant = unsafe { mem::transmute(3u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -103,7 +103,7 @@ error[E0080]: constructing invalid value at .<enum-variant(Some)>.0.1: encounter LL | const BAD_OPTION_CHAR: Option<(char, char)> = Some(('x', unsafe { mem::transmute(!0u32) })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } diff --git a/tests/ui/consts/const-eval/ub-incorrect-vtable.32bit.stderr b/tests/ui/consts/const-eval/ub-incorrect-vtable.32bit.stderr index 86d6f8c52bc..1e4d425d78e 100644 --- a/tests/ui/consts/const-eval/ub-incorrect-vtable.32bit.stderr +++ b/tests/ui/consts/const-eval/ub-incorrect-vtable.32bit.stderr @@ -4,7 +4,7 @@ error[E0080]: constructing invalid value: encountered ALLOC1<imm>, but expected LL | const INVALID_VTABLE_ALIGNMENT: &dyn Trait = | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { ╾ALLOC0<imm>╼ ╾ALLOC1<imm>╼ │ ╾──╼╾──╼ } @@ -15,7 +15,7 @@ error[E0080]: constructing invalid value: encountered ALLOC3<imm>, but expected LL | const INVALID_VTABLE_SIZE: &dyn Trait = | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { ╾ALLOC2<imm>╼ ╾ALLOC3<imm>╼ │ ╾──╼╾──╼ } @@ -26,7 +26,7 @@ error[E0080]: constructing invalid value at .0: encountered ALLOC5<imm>, but exp LL | const INVALID_VTABLE_ALIGNMENT_UB: W<&dyn Trait> = | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { ╾ALLOC4<imm>╼ ╾ALLOC5<imm>╼ │ ╾──╼╾──╼ } @@ -37,7 +37,7 @@ error[E0080]: constructing invalid value at .0: encountered ALLOC7<imm>, but exp LL | const INVALID_VTABLE_SIZE_UB: W<&dyn Trait> = | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { ╾ALLOC6<imm>╼ ╾ALLOC7<imm>╼ │ ╾──╼╾──╼ } @@ -48,7 +48,7 @@ error[E0080]: constructing invalid value at .0: encountered ALLOC9<imm>, but exp LL | const INVALID_VTABLE_UB: W<&dyn Trait> = | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { ╾ALLOC8<imm>╼ ╾ALLOC9<imm>╼ │ ╾──╼╾──╼ } @@ -59,7 +59,7 @@ error[E0080]: constructing invalid value at .1: encountered a dangling reference LL | const G: Wide = unsafe { Transmute { t: FOO }.u }; | ^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { ╾ALLOC10<imm>╼ ╾ALLOC11╼ │ ╾──╼╾──╼ } diff --git a/tests/ui/consts/const-eval/ub-incorrect-vtable.64bit.stderr b/tests/ui/consts/const-eval/ub-incorrect-vtable.64bit.stderr index a9518216dbd..a068991f324 100644 --- a/tests/ui/consts/const-eval/ub-incorrect-vtable.64bit.stderr +++ b/tests/ui/consts/const-eval/ub-incorrect-vtable.64bit.stderr @@ -4,7 +4,7 @@ error[E0080]: constructing invalid value: encountered ALLOC1<imm>, but expected LL | const INVALID_VTABLE_ALIGNMENT: &dyn Trait = | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { ╾ALLOC0<imm>╼ ╾ALLOC1<imm>╼ │ ╾──────╼╾──────╼ } @@ -15,7 +15,7 @@ error[E0080]: constructing invalid value: encountered ALLOC3<imm>, but expected LL | const INVALID_VTABLE_SIZE: &dyn Trait = | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { ╾ALLOC2<imm>╼ ╾ALLOC3<imm>╼ │ ╾──────╼╾──────╼ } @@ -26,7 +26,7 @@ error[E0080]: constructing invalid value at .0: encountered ALLOC5<imm>, but exp LL | const INVALID_VTABLE_ALIGNMENT_UB: W<&dyn Trait> = | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { ╾ALLOC4<imm>╼ ╾ALLOC5<imm>╼ │ ╾──────╼╾──────╼ } @@ -37,7 +37,7 @@ error[E0080]: constructing invalid value at .0: encountered ALLOC7<imm>, but exp LL | const INVALID_VTABLE_SIZE_UB: W<&dyn Trait> = | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { ╾ALLOC6<imm>╼ ╾ALLOC7<imm>╼ │ ╾──────╼╾──────╼ } @@ -48,7 +48,7 @@ error[E0080]: constructing invalid value at .0: encountered ALLOC9<imm>, but exp LL | const INVALID_VTABLE_UB: W<&dyn Trait> = | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { ╾ALLOC8<imm>╼ ╾ALLOC9<imm>╼ │ ╾──────╼╾──────╼ } @@ -59,7 +59,7 @@ error[E0080]: constructing invalid value at .1: encountered a dangling reference LL | const G: Wide = unsafe { Transmute { t: FOO }.u }; | ^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { ╾ALLOC10<imm>╼ ╾ALLOC11╼ │ ╾──────╼╾──────╼ } diff --git a/tests/ui/consts/const-eval/ub-int-array.stderr b/tests/ui/consts/const-eval/ub-int-array.stderr index 10eb7c46c92..065bfd2c304 100644 --- a/tests/ui/consts/const-eval/ub-int-array.stderr +++ b/tests/ui/consts/const-eval/ub-int-array.stderr @@ -4,7 +4,7 @@ error[E0080]: constructing invalid value at [0]: encountered uninitialized memor LL | const UNINIT_INT_0: [u32; 3] = unsafe { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 12, align: 4) { __ __ __ __ 11 11 11 11 22 22 22 22 │ ░░░░...."""" } @@ -15,7 +15,7 @@ error[E0080]: constructing invalid value at [1]: encountered uninitialized memor LL | const UNINIT_INT_1: [u32; 3] = unsafe { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 12, align: 4) { 00 00 00 00 01 __ 01 01 02 02 __ 02 │ .....░....░. } @@ -26,7 +26,7 @@ error[E0080]: constructing invalid value at [2]: encountered uninitialized memor LL | const UNINIT_INT_2: [u32; 3] = unsafe { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 12, align: 4) { 00 00 00 00 01 01 01 01 02 02 02 __ │ ...........░ } diff --git a/tests/ui/consts/const-eval/ub-nonnull.stderr b/tests/ui/consts/const-eval/ub-nonnull.stderr index 19ae66cf3c6..91c82efbc5e 100644 --- a/tests/ui/consts/const-eval/ub-nonnull.stderr +++ b/tests/ui/consts/const-eval/ub-nonnull.stderr @@ -4,7 +4,7 @@ error[E0080]: constructing invalid value: encountered 0, but expected something LL | const NULL_PTR: NonNull<u8> = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -21,7 +21,7 @@ error[E0080]: constructing invalid value at .0: encountered 0, but expected some LL | const NULL_U8: NonZero<u8> = unsafe { mem::transmute(0u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -32,7 +32,7 @@ error[E0080]: constructing invalid value at .0: encountered 0, but expected some LL | const NULL_USIZE: NonZero<usize> = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -53,7 +53,7 @@ error[E0080]: constructing invalid value: encountered 42, but expected something LL | const BAD_RANGE1: RestrictedRange1 = unsafe { RestrictedRange1(42) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -64,7 +64,7 @@ error[E0080]: constructing invalid value: encountered 20, but expected something LL | const BAD_RANGE2: RestrictedRange2 = unsafe { RestrictedRange2(20) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -75,7 +75,7 @@ error[E0080]: constructing invalid value: encountered 0, but expected something LL | const NULL_FAT_PTR: NonNull<dyn Send> = unsafe { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } diff --git a/tests/ui/consts/const-eval/ub-ref-ptr.stderr b/tests/ui/consts/const-eval/ub-ref-ptr.stderr index 451ebb6eba1..c45f66c2925 100644 --- a/tests/ui/consts/const-eval/ub-ref-ptr.stderr +++ b/tests/ui/consts/const-eval/ub-ref-ptr.stderr @@ -4,7 +4,7 @@ error[E0080]: constructing invalid value: encountered an unaligned reference (re LL | const UNALIGNED: &u16 = unsafe { mem::transmute(&[0u8; 4]) }; | ^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -15,7 +15,7 @@ error[E0080]: constructing invalid value: encountered an unaligned box (required LL | const UNALIGNED_BOX: Box<u16> = unsafe { mem::transmute(&[0u8; 4]) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -26,7 +26,7 @@ error[E0080]: constructing invalid value: encountered a null reference LL | const NULL: &u16 = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -37,7 +37,7 @@ error[E0080]: constructing invalid value: encountered a null box LL | const NULL_BOX: Box<u16> = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -87,7 +87,7 @@ error[E0080]: constructing invalid value: encountered a dangling reference (0x53 LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -98,7 +98,7 @@ error[E0080]: constructing invalid value: encountered a dangling box (0x539[noal LL | const USIZE_AS_BOX: Box<u8> = unsafe { mem::transmute(1337usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -119,7 +119,7 @@ error[E0080]: constructing invalid value: encountered null pointer, but expected LL | const NULL_FN_PTR: fn() = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -140,7 +140,7 @@ error[E0080]: constructing invalid value: encountered 0xd[noalloc], but expected LL | const DANGLING_FN_PTR: fn() = unsafe { mem::transmute(13usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -151,7 +151,7 @@ error[E0080]: constructing invalid value: encountered ALLOC2<imm>, but expected LL | const DATA_FN_PTR: fn() = unsafe { mem::transmute(&13) }; | ^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } diff --git a/tests/ui/consts/const-eval/ub-uninhabit.stderr b/tests/ui/consts/const-eval/ub-uninhabit.stderr index b0f475fe938..aca0b13bb90 100644 --- a/tests/ui/consts/const-eval/ub-uninhabit.stderr +++ b/tests/ui/consts/const-eval/ub-uninhabit.stderr @@ -10,7 +10,7 @@ error[E0080]: constructing invalid value: encountered a reference pointing to un LL | const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } diff --git a/tests/ui/consts/const-eval/ub-upvars.32bit.stderr b/tests/ui/consts/const-eval/ub-upvars.32bit.stderr index ecd1c768c28..b434970b677 100644 --- a/tests/ui/consts/const-eval/ub-upvars.32bit.stderr +++ b/tests/ui/consts/const-eval/ub-upvars.32bit.stderr @@ -4,7 +4,7 @@ error[E0080]: constructing invalid value at .<deref>.<dyn-downcast>.<captured-va LL | const BAD_UPVAR: &dyn FnOnce() = &{ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { ╾ALLOC0<imm>╼ ╾ALLOC1╼ │ ╾──╼╾──╼ } diff --git a/tests/ui/consts/const-eval/ub-upvars.64bit.stderr b/tests/ui/consts/const-eval/ub-upvars.64bit.stderr index 108dfe6b27b..de1e1f8a2be 100644 --- a/tests/ui/consts/const-eval/ub-upvars.64bit.stderr +++ b/tests/ui/consts/const-eval/ub-upvars.64bit.stderr @@ -4,7 +4,7 @@ error[E0080]: constructing invalid value at .<deref>.<dyn-downcast>.<captured-va LL | const BAD_UPVAR: &dyn FnOnce() = &{ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { ╾ALLOC0<imm>╼ ╾ALLOC1╼ │ ╾──────╼╾──────╼ } diff --git a/tests/ui/consts/const-eval/ub-wide-ptr.stderr b/tests/ui/consts/const-eval/ub-wide-ptr.stderr index ab15ba826a5..c505e5cc8a2 100644 --- a/tests/ui/consts/const-eval/ub-wide-ptr.stderr +++ b/tests/ui/consts/const-eval/ub-wide-ptr.stderr @@ -4,7 +4,7 @@ error[E0080]: constructing invalid value: encountered a dangling reference (goin LL | const STR_TOO_LONG: &str = unsafe { mem::transmute((&42u8, 999usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -15,7 +15,7 @@ error[E0080]: constructing invalid value at .0: encountered invalid reference me LL | const NESTED_STR_MUCH_TOO_LONG: (&str,) = (unsafe { mem::transmute((&42, usize::MAX)) },); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -44,7 +44,7 @@ error[E0080]: constructing invalid value: encountered invalid reference metadata LL | const MY_STR_MUCH_TOO_LONG: &MyStr = unsafe { mem::transmute((&42u8, usize::MAX)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -55,7 +55,7 @@ error[E0080]: constructing invalid value at .<deref>: encountered uninitialized LL | const STR_NO_INIT: &str = unsafe { mem::transmute::<&[_], _>(&[MaybeUninit::<u8> { uninit: () }]) }; | ^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -66,7 +66,7 @@ error[E0080]: constructing invalid value at .<deref>.0: encountered uninitialize LL | const MYSTR_NO_INIT: &MyStr = unsafe { mem::transmute::<&[_], _>(&[MaybeUninit::<u8> { uninit: () }]) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -87,7 +87,7 @@ error[E0080]: constructing invalid value: encountered a dangling reference (goin LL | const SLICE_TOO_LONG: &[u8] = unsafe { mem::transmute((&42u8, 999usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -98,7 +98,7 @@ error[E0080]: constructing invalid value: encountered invalid reference metadata LL | const SLICE_TOO_LONG_OVERFLOW: &[u32] = unsafe { mem::transmute((&42u32, isize::MAX)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -118,7 +118,7 @@ error[E0080]: constructing invalid value: encountered a dangling box (going beyo LL | const SLICE_TOO_LONG_BOX: Box<[u8]> = unsafe { mem::transmute((&42u8, 999usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -138,7 +138,7 @@ error[E0080]: constructing invalid value at .<deref>[0]: encountered 0x03, but e LL | const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { mem::transmute(3u8) }]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -155,7 +155,7 @@ error[E0080]: constructing invalid value at .<deref>.0: encountered 0x03, but ex LL | const MYSLICE_PREFIX_BAD: &MySliceBool = &MySlice(unsafe { mem::transmute(3u8) }, [false]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -172,7 +172,7 @@ error[E0080]: constructing invalid value at .<deref>.1[0]: encountered 0x03, but LL | const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { mem::transmute(3u8) }]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -199,7 +199,7 @@ error[E0080]: constructing invalid value at .0: encountered ALLOC12<imm>, but ex LL | const TRAIT_OBJ_SHORT_VTABLE_1: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u8))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -210,7 +210,7 @@ error[E0080]: constructing invalid value at .0: encountered ALLOC14<imm>, but ex LL | const TRAIT_OBJ_SHORT_VTABLE_2: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u64))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -221,7 +221,7 @@ error[E0080]: constructing invalid value at .0: encountered 0x4[noalloc], but ex LL | const TRAIT_OBJ_INT_VTABLE: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, 4usize))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -232,7 +232,7 @@ error[E0080]: constructing invalid value: encountered ALLOC17<imm>, but expected LL | const TRAIT_OBJ_UNALIGNED_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, &[0u8; 128])) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -243,7 +243,7 @@ error[E0080]: constructing invalid value: encountered ALLOC19<imm>, but expected LL | const TRAIT_OBJ_BAD_DROP_FN_NULL: &dyn Trait = unsafe { mem::transmute((&92u8, &[0usize; 8])) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -254,7 +254,7 @@ error[E0080]: constructing invalid value: encountered ALLOC21<imm>, but expected LL | const TRAIT_OBJ_BAD_DROP_FN_INT: &dyn Trait = unsafe { mem::transmute((&92u8, &[1usize; 8])) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -265,7 +265,7 @@ error[E0080]: constructing invalid value at .0: encountered ALLOC23<imm>, but ex LL | const TRAIT_OBJ_BAD_DROP_FN_NOT_FN_PTR: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &[&42u8; 8]))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -276,7 +276,7 @@ error[E0080]: constructing invalid value at .<deref>.<dyn-downcast>: encountered LL | const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_, &bool>(&3u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -287,7 +287,7 @@ error[E0080]: constructing invalid value: encountered null pointer, but expected LL | const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { mem::transmute((&92u8, 0usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -298,7 +298,7 @@ error[E0080]: constructing invalid value: encountered ALLOC28<imm>, but expected LL | const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -309,7 +309,7 @@ error[E0080]: constructing invalid value: encountered null pointer, but expected LL | static mut RAW_TRAIT_OBJ_VTABLE_NULL_THROUGH_REF: *const dyn Trait = unsafe { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -320,7 +320,7 @@ error[E0080]: constructing invalid value: encountered ALLOC31<imm>, but expected LL | static mut RAW_TRAIT_OBJ_VTABLE_INVALID_THROUGH_REF: *const dyn Trait = unsafe { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } diff --git a/tests/ui/consts/const-eval/union-ub.32bit.stderr b/tests/ui/consts/const-eval/union-ub.32bit.stderr index 757bcea91c3..fb2311b9921 100644 --- a/tests/ui/consts/const-eval/union-ub.32bit.stderr +++ b/tests/ui/consts/const-eval/union-ub.32bit.stderr @@ -4,7 +4,7 @@ error[E0080]: constructing invalid value: encountered 0x2a, but expected a boole LL | const BAD_BOOL: bool = unsafe { DummyUnion { u8: 42 }.bool }; | ^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 1, align: 1) { 2a │ * } diff --git a/tests/ui/consts/const-eval/union-ub.64bit.stderr b/tests/ui/consts/const-eval/union-ub.64bit.stderr index 757bcea91c3..fb2311b9921 100644 --- a/tests/ui/consts/const-eval/union-ub.64bit.stderr +++ b/tests/ui/consts/const-eval/union-ub.64bit.stderr @@ -4,7 +4,7 @@ error[E0080]: constructing invalid value: encountered 0x2a, but expected a boole LL | const BAD_BOOL: bool = unsafe { DummyUnion { u8: 42 }.bool }; | ^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 1, align: 1) { 2a │ * } diff --git a/tests/ui/consts/const-mut-refs/mut_ref_in_final.stderr b/tests/ui/consts/const-mut-refs/mut_ref_in_final.stderr index 16dee44d800..08656776468 100644 --- a/tests/ui/consts/const-mut-refs/mut_ref_in_final.stderr +++ b/tests/ui/consts/const-mut-refs/mut_ref_in_final.stderr @@ -34,7 +34,7 @@ error[E0080]: constructing invalid value: encountered mutable reference or box p LL | const IMMUT_MUT_REF: &mut u16 = unsafe { mem::transmute(&13) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -45,7 +45,7 @@ error[E0080]: constructing invalid value: encountered mutable reference or box p LL | static IMMUT_MUT_REF_STATIC: &mut u16 = unsafe { mem::transmute(&13) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } diff --git a/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.stderr b/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.stderr index 302e342bce6..96263998ad4 100644 --- a/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.stderr +++ b/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.stderr @@ -4,7 +4,7 @@ error[E0080]: constructing invalid value at .<enum-variant(Some)>.0: encountered LL | const MUT: Option<&mut i32> = helper(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -15,7 +15,7 @@ error[E0080]: constructing invalid value at .<enum-variant(Some)>.0: encountered LL | const INT2PTR: Option<&mut i32> = helper_int2ptr(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -26,7 +26,7 @@ error[E0080]: constructing invalid value at .<enum-variant(Some)>.0: encountered LL | static INT2PTR_STATIC: Option<&mut i32> = helper_int2ptr(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -37,7 +37,7 @@ error[E0080]: constructing invalid value at .<enum-variant(Some)>.0: encountered LL | const DANGLING: Option<&mut i32> = helper_dangling(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -48,7 +48,7 @@ error[E0080]: constructing invalid value at .<enum-variant(Some)>.0: encountered LL | static DANGLING_STATIC: Option<&mut i32> = helper_dangling(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } diff --git a/tests/ui/consts/const_refs_to_static_fail_invalid.stderr b/tests/ui/consts/const_refs_to_static_fail_invalid.stderr index 8be8b4bc50f..f9088c318a6 100644 --- a/tests/ui/consts/const_refs_to_static_fail_invalid.stderr +++ b/tests/ui/consts/const_refs_to_static_fail_invalid.stderr @@ -4,7 +4,7 @@ error[E0080]: constructing invalid value at .<deref>: encountered 0x0a, but expe LL | const C: &bool = unsafe { std::mem::transmute(&S) }; | ^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } diff --git a/tests/ui/consts/dangling-alloc-id-ice.stderr b/tests/ui/consts/dangling-alloc-id-ice.stderr index 65a46b62dae..4b034c81a61 100644 --- a/tests/ui/consts/dangling-alloc-id-ice.stderr +++ b/tests/ui/consts/dangling-alloc-id-ice.stderr @@ -4,7 +4,7 @@ error[E0080]: constructing invalid value: encountered a dangling reference (use- LL | const FOO: &() = { | ^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } diff --git a/tests/ui/consts/dangling-zst-ice-issue-126393.stderr b/tests/ui/consts/dangling-zst-ice-issue-126393.stderr index 2f600e494c4..248db694d52 100644 --- a/tests/ui/consts/dangling-zst-ice-issue-126393.stderr +++ b/tests/ui/consts/dangling-zst-ice-issue-126393.stderr @@ -4,7 +4,7 @@ error[E0080]: constructing invalid value: encountered a dangling reference (use- LL | pub static MAGIC_FFI_REF: &'static Wrapper = unsafe { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } diff --git a/tests/ui/consts/interior-mut-const-via-union.32bit.stderr b/tests/ui/consts/interior-mut-const-via-union.32bit.stderr index 47bb2e5e879..17b32383912 100644 --- a/tests/ui/consts/interior-mut-const-via-union.32bit.stderr +++ b/tests/ui/consts/interior-mut-const-via-union.32bit.stderr @@ -4,7 +4,7 @@ error[E0080]: constructing invalid value at .<deref>.y.<enum-variant(B)>.0: enco LL | fn main() { | ^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { ╾ALLOC0╼ │ ╾──╼ } diff --git a/tests/ui/consts/interior-mut-const-via-union.64bit.stderr b/tests/ui/consts/interior-mut-const-via-union.64bit.stderr index b4c9a4bd47e..c4f78e7bf9e 100644 --- a/tests/ui/consts/interior-mut-const-via-union.64bit.stderr +++ b/tests/ui/consts/interior-mut-const-via-union.64bit.stderr @@ -4,7 +4,7 @@ error[E0080]: constructing invalid value at .<deref>.y.<enum-variant(B)>.0: enco LL | fn main() { | ^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { ╾ALLOC0╼ │ ╾──────╼ } diff --git a/tests/ui/consts/issue-17718-const-bad-values.stderr b/tests/ui/consts/issue-17718-const-bad-values.stderr index 68d1a72b71e..2c54200c8a2 100644 --- a/tests/ui/consts/issue-17718-const-bad-values.stderr +++ b/tests/ui/consts/issue-17718-const-bad-values.stderr @@ -14,7 +14,7 @@ error[E0080]: constructing invalid value: encountered mutable reference in `cons LL | const C2: &'static mut i32 = unsafe { &mut S }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $PTR, align: $PTR) { HEX_DUMP } diff --git a/tests/ui/consts/issue-63952.32bit.stderr b/tests/ui/consts/issue-63952.32bit.stderr index e5340788167..cf97ed6e487 100644 --- a/tests/ui/consts/issue-63952.32bit.stderr +++ b/tests/ui/consts/issue-63952.32bit.stderr @@ -4,7 +4,7 @@ error[E0080]: constructing invalid value: encountered invalid reference metadata LL | const SLICE_WAY_TOO_LONG: &[u8] = unsafe { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { ╾ALLOC0<imm>╼ ff ff ff ff │ ╾──╼.... } diff --git a/tests/ui/consts/issue-63952.64bit.stderr b/tests/ui/consts/issue-63952.64bit.stderr index 27e74833fc5..4cea967314c 100644 --- a/tests/ui/consts/issue-63952.64bit.stderr +++ b/tests/ui/consts/issue-63952.64bit.stderr @@ -4,7 +4,7 @@ error[E0080]: constructing invalid value: encountered invalid reference metadata LL | const SLICE_WAY_TOO_LONG: &[u8] = unsafe { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { ╾ALLOC0<imm>╼ ff ff ff ff ff ff ff ff │ ╾──────╼........ } diff --git a/tests/ui/consts/issue-79690.64bit.stderr b/tests/ui/consts/issue-79690.64bit.stderr index 7488f7b7752..2653ff22f12 100644 --- a/tests/ui/consts/issue-79690.64bit.stderr +++ b/tests/ui/consts/issue-79690.64bit.stderr @@ -4,7 +4,7 @@ error[E0080]: constructing invalid value at .1: encountered a dangling reference LL | const G: Fat = unsafe { Transmute { t: FOO }.u }; | ^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { ╾ALLOC0<imm>╼ ╾ALLOC1╼ │ ╾──────╼╾──────╼ } diff --git a/tests/ui/consts/miri_unleashed/mutable_references.stderr b/tests/ui/consts/miri_unleashed/mutable_references.stderr index 137efde44b3..b35076b669e 100644 --- a/tests/ui/consts/miri_unleashed/mutable_references.stderr +++ b/tests/ui/consts/miri_unleashed/mutable_references.stderr @@ -4,7 +4,7 @@ error[E0080]: constructing invalid value at .<deref>: encountered mutable refere LL | static FOO: &&mut u32 = &&mut 42; | ^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -15,7 +15,7 @@ error[E0080]: constructing invalid value: encountered mutable reference or box p LL | static OH_YES: &mut i32 = &mut 42; | ^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -38,7 +38,7 @@ error[E0080]: constructing invalid value: encountered mutable reference or box p LL | const BLUNT: &mut i32 = &mut 42; | ^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -49,7 +49,7 @@ error[E0080]: constructing invalid value: encountered mutable reference in `cons LL | const SUBTLE: &mut i32 = unsafe { | ^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -60,7 +60,7 @@ error[E0080]: constructing invalid value at .x.<deref>: encountered `UnsafeCell` LL | static MEH: Meh = Meh { x: &UnsafeCell::new(42) }; | ^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -71,7 +71,7 @@ error[E0080]: constructing invalid value at .x.<deref>: encountered `UnsafeCell` LL | const MUH: Meh = Meh { | ^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -82,7 +82,7 @@ error[E0080]: constructing invalid value at .<deref>.<dyn-downcast>.x: encounter LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) }; | ^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -93,7 +93,7 @@ error[E0080]: constructing invalid value: encountered mutable reference or box p LL | static mut MUT_TO_READONLY: &mut i32 = unsafe { &mut *(&READONLY as *const _ as *mut _) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } diff --git a/tests/ui/consts/miri_unleashed/static-no-inner-mut.32bit.stderr b/tests/ui/consts/miri_unleashed/static-no-inner-mut.32bit.stderr index 1ef20689985..d7c668a9812 100644 --- a/tests/ui/consts/miri_unleashed/static-no-inner-mut.32bit.stderr +++ b/tests/ui/consts/miri_unleashed/static-no-inner-mut.32bit.stderr @@ -4,7 +4,7 @@ error[E0080]: constructing invalid value at .<deref>.v: encountered `UnsafeCell` LL | static REF: &AtomicI32 = &AtomicI32::new(42); | ^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { ╾ALLOC0╼ │ ╾──╼ } @@ -15,7 +15,7 @@ error[E0080]: constructing invalid value: encountered mutable reference or box p LL | static REFMUT: &mut i32 = &mut 0; | ^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { ╾ALLOC1╼ │ ╾──╼ } @@ -26,7 +26,7 @@ error[E0080]: constructing invalid value at .<deref>.v: encountered `UnsafeCell` LL | static REF2: &AtomicI32 = {let x = AtomicI32::new(42); &{x}}; | ^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { ╾ALLOC2╼ │ ╾──╼ } @@ -37,7 +37,7 @@ error[E0080]: constructing invalid value: encountered mutable reference or box p LL | static REFMUT2: &mut i32 = {let mut x = 0; &mut {x}}; | ^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { ╾ALLOC3╼ │ ╾──╼ } diff --git a/tests/ui/consts/miri_unleashed/static-no-inner-mut.64bit.stderr b/tests/ui/consts/miri_unleashed/static-no-inner-mut.64bit.stderr index 06f78e679b1..f3bb49900b5 100644 --- a/tests/ui/consts/miri_unleashed/static-no-inner-mut.64bit.stderr +++ b/tests/ui/consts/miri_unleashed/static-no-inner-mut.64bit.stderr @@ -4,7 +4,7 @@ error[E0080]: constructing invalid value at .<deref>.v: encountered `UnsafeCell` LL | static REF: &AtomicI32 = &AtomicI32::new(42); | ^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { ╾ALLOC0╼ │ ╾──────╼ } @@ -15,7 +15,7 @@ error[E0080]: constructing invalid value: encountered mutable reference or box p LL | static REFMUT: &mut i32 = &mut 0; | ^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { ╾ALLOC1╼ │ ╾──────╼ } @@ -26,7 +26,7 @@ error[E0080]: constructing invalid value at .<deref>.v: encountered `UnsafeCell` LL | static REF2: &AtomicI32 = {let x = AtomicI32::new(42); &{x}}; | ^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { ╾ALLOC2╼ │ ╾──────╼ } @@ -37,7 +37,7 @@ error[E0080]: constructing invalid value: encountered mutable reference or box p LL | static REFMUT2: &mut i32 = {let mut x = 0; &mut {x}}; | ^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { ╾ALLOC3╼ │ ╾──────╼ } diff --git a/tests/ui/consts/validate_never_arrays.stderr b/tests/ui/consts/validate_never_arrays.stderr index 0f503df4060..3c405e8d3cd 100644 --- a/tests/ui/consts/validate_never_arrays.stderr +++ b/tests/ui/consts/validate_never_arrays.stderr @@ -4,7 +4,7 @@ error[E0080]: constructing invalid value: encountered a reference pointing to un LL | const _: &[!; 1] = unsafe { &*(1_usize as *const [!; 1]) }; | ^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -15,7 +15,7 @@ error[E0080]: constructing invalid value at .<deref>[0]: encountered a value of LL | const _: &[!] = unsafe { &*(1_usize as *const [!; 1]) }; | ^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } @@ -26,7 +26,7 @@ error[E0080]: constructing invalid value at .<deref>[0]: encountered a value of LL | const _: &[!] = unsafe { &*(1_usize as *const [!; 42]) }; | ^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } diff --git a/tests/ui/crate-loading/invalid-rlib.rs b/tests/ui/crate-loading/invalid-rlib.rs index 6b463526244..24293f88b1c 100644 --- a/tests/ui/crate-loading/invalid-rlib.rs +++ b/tests/ui/crate-loading/invalid-rlib.rs @@ -6,6 +6,3 @@ #![no_std] use ::foo; //~ ERROR invalid metadata files for crate `foo` //~| NOTE failed to mmap file -//~^^ ERROR invalid metadata files for crate `foo` -//~| NOTE failed to mmap file -//~| NOTE duplicate diagnostic diff --git a/tests/ui/crate-loading/invalid-rlib.stderr b/tests/ui/crate-loading/invalid-rlib.stderr index 63bb1b95cbb..ad3ab729a5d 100644 --- a/tests/ui/crate-loading/invalid-rlib.stderr +++ b/tests/ui/crate-loading/invalid-rlib.stderr @@ -6,15 +6,6 @@ LL | use ::foo; | = note: failed to mmap file 'auxiliary/libfoo.rlib' -error[E0786]: found invalid metadata files for crate `foo` - --> $DIR/invalid-rlib.rs:7:7 - | -LL | use ::foo; - | ^^^ - | - = note: failed to mmap file 'auxiliary/libfoo.rlib' - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0786`. diff --git a/tests/ui/drop/if-let-super-let.rs b/tests/ui/drop/if-let-super-let.rs new file mode 100644 index 00000000000..c6543e6d3dc --- /dev/null +++ b/tests/ui/drop/if-let-super-let.rs @@ -0,0 +1,112 @@ +//! Test for #145328: ensure the lifetime of a `super let` binding within an `if let` scrutinee is +//! at most the scope of the `if` condition's temporaries. Additionally, test `pin!` since it's +//! implemented in terms of `super let` and exposes this behavior. +//@ run-pass +//@ revisions: e2021 e2024 +//@ [e2021] edition: 2021 +//@ [e2024] edition: 2024 + +#![feature(if_let_guard)] +#![feature(super_let)] +#![expect(irrefutable_let_patterns)] + +use std::cell::RefCell; +use std::pin::pin; + +fn main() { + // The `super let` bindings here should have the same scope as `if let` temporaries. + // In Rust 2021, this means it lives past the end of the `if` expression. + // In Rust 2024, this means it lives to the end of the `if`'s success block. + assert_drop_order(0..=2, |o| { + #[cfg(e2021)] + ( + if let _ = { super let _x = o.log(2); } { o.push(0) }, + o.push(1), + ); + #[cfg(e2024)] + ( + if let _ = { super let _x = o.log(1); } { o.push(0) }, + o.push(2), + ); + }); + assert_drop_order(0..=2, |o| { + #[cfg(e2021)] + ( + if let true = { super let _x = o.log(2); false } {} else { o.push(0) }, + o.push(1), + ); + #[cfg(e2024)] + ( + if let true = { super let _x = o.log(0); false } {} else { o.push(1) }, + o.push(2), + ); + }); + + // `pin!` should behave likewise. + assert_drop_order(0..=2, |o| { + #[cfg(e2021)] (if let _ = pin!(o.log(2)) { o.push(0) }, o.push(1)); + #[cfg(e2024)] (if let _ = pin!(o.log(1)) { o.push(0) }, o.push(2)); + }); + assert_drop_order(0..=2, |o| { + #[cfg(e2021)] + ( + if let None = Some(pin!(o.log(2))) {} else { o.push(0) }, + o.push(1), + ); + #[cfg(e2024)] + ( + if let None = Some(pin!(o.log(0))) {} else { o.push(1) }, + o.push(2), + ); + }); + + // `super let` bindings' scope should also be consistent with `if let` temporaries in guards. + // Here, that means the `super let` binding in the second guard condition operand should be + // dropped before the first operand's temporary. This is consistent across Editions. + assert_drop_order(0..=1, |o| { + match () { + _ if let _ = o.log(1) + && let _ = { super let _x = o.log(0); } => {} + _ => unreachable!(), + } + }); + assert_drop_order(0..=1, |o| { + match () { + _ if let _ = o.log(1) + && let _ = pin!(o.log(0)) => {} + _ => unreachable!(), + } + }); +} + +// # Test scaffolding... + +struct DropOrder(RefCell<Vec<u64>>); +struct LogDrop<'o>(&'o DropOrder, u64); + +impl DropOrder { + fn log(&self, n: u64) -> LogDrop<'_> { + LogDrop(self, n) + } + fn push(&self, n: u64) { + self.0.borrow_mut().push(n); + } +} + +impl<'o> Drop for LogDrop<'o> { + fn drop(&mut self) { + self.0.push(self.1); + } +} + +#[track_caller] +fn assert_drop_order( + ex: impl IntoIterator<Item = u64>, + f: impl Fn(&DropOrder), +) { + let order = DropOrder(RefCell::new(Vec::new())); + f(&order); + let order = order.0.into_inner(); + let expected: Vec<u64> = ex.into_iter().collect(); + assert_eq!(order, expected); +} diff --git a/tests/ui/feature-gates/feature-gate-c_variadic.rs b/tests/ui/feature-gates/feature-gate-c_variadic.rs index f189f02a26d..45c68842093 100644 --- a/tests/ui/feature-gates/feature-gate-c_variadic.rs +++ b/tests/ui/feature-gates/feature-gate-c_variadic.rs @@ -2,3 +2,9 @@ pub unsafe extern "C" fn test(_: i32, ap: ...) { } //~^ ERROR C-variadic functions are unstable + +trait Trait { + unsafe extern "C" fn trait_test(_: i32, ap: ...) { } + //~^ ERROR C-variadic functions are unstable + //~| ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +} diff --git a/tests/ui/feature-gates/feature-gate-c_variadic.stderr b/tests/ui/feature-gates/feature-gate-c_variadic.stderr index 1b6a8c92af5..e30a2f1ede3 100644 --- a/tests/ui/feature-gates/feature-gate-c_variadic.stderr +++ b/tests/ui/feature-gates/feature-gate-c_variadic.stderr @@ -1,3 +1,9 @@ +error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention + --> $DIR/feature-gate-c_variadic.rs:7:45 + | +LL | unsafe extern "C" fn trait_test(_: i32, ap: ...) { } + | ^^^^^^^ + error[E0658]: C-variadic functions are unstable --> $DIR/feature-gate-c_variadic.rs:3:1 | @@ -8,6 +14,16 @@ LL | pub unsafe extern "C" fn test(_: i32, ap: ...) { } = help: add `#![feature(c_variadic)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 1 previous error +error[E0658]: C-variadic functions are unstable + --> $DIR/feature-gate-c_variadic.rs:7:5 + | +LL | unsafe extern "C" fn trait_test(_: i32, ap: ...) { } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #44930 <https://github.com/rust-lang/rust/issues/44930> for more information + = help: add `#![feature(c_variadic)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-target-feature-inline-always.rs b/tests/ui/feature-gates/feature-gate-target-feature-inline-always.rs new file mode 100644 index 00000000000..181f9a21000 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-target-feature-inline-always.rs @@ -0,0 +1,9 @@ +//@ only-aarch64 +#[inline(always)] +//~^ ERROR cannot use `#[inline(always)]` with `#[target_feature]` +#[target_feature(enable="fp16")] +fn test() { + +} + +fn main() { } diff --git a/tests/ui/feature-gates/feature-gate-target-feature-inline-always.stderr b/tests/ui/feature-gates/feature-gate-target-feature-inline-always.stderr new file mode 100644 index 00000000000..de54844bc29 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-target-feature-inline-always.stderr @@ -0,0 +1,13 @@ +error[E0658]: cannot use `#[inline(always)]` with `#[target_feature]` + --> $DIR/feature-gate-target-feature-inline-always.rs:2:1 + | +LL | #[inline(always)] + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #145574 <https://github.com/rust-lang/rust/issues/145574> for more information + = help: add `#![feature(target_feature_inline_always)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/fn/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.fixed b/tests/ui/fn/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.fixed index b58c3a6720d..14d5de0261d 100644 --- a/tests/ui/fn/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.fixed +++ b/tests/ui/fn/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.fixed @@ -1,5 +1,5 @@ //@ run-rustfix -#![deny(unused_assignments, unused_variables)] +#![deny(unused_assignments)] #![allow(unused_mut)] struct Object; @@ -8,15 +8,15 @@ fn change_object(object: &mut Object) { //~ HELP you might have meant to mutate *object = object2; //~ ERROR mismatched types } -fn change_object2(object: &mut Object) { //~ ERROR variable `object` is assigned to, but never used - //~^ HELP you might have meant to mutate +fn change_object2(object: &mut Object) { + //~^ HELP you might have meant to mutate let object2 = Object; *object = object2; //~^ ERROR `object2` does not live long enough //~| ERROR value assigned to `object` is never read } -fn change_object3(object: &mut Object) { //~ ERROR variable `object` is assigned to, but never used +fn change_object3(object: &mut Object) { //~^ HELP you might have meant to mutate let mut object2 = Object; //~ HELP consider changing this to be mutable *object = object2; diff --git a/tests/ui/fn/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs b/tests/ui/fn/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs index 1fd222e0db1..5805afb3369 100644 --- a/tests/ui/fn/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs +++ b/tests/ui/fn/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs @@ -1,5 +1,5 @@ //@ run-rustfix -#![deny(unused_assignments, unused_variables)] +#![deny(unused_assignments)] #![allow(unused_mut)] struct Object; @@ -8,15 +8,15 @@ fn change_object(mut object: &Object) { //~ HELP you might have meant to mutate object = object2; //~ ERROR mismatched types } -fn change_object2(mut object: &Object) { //~ ERROR variable `object` is assigned to, but never used - //~^ HELP you might have meant to mutate +fn change_object2(mut object: &Object) { + //~^ HELP you might have meant to mutate let object2 = Object; object = &object2; //~^ ERROR `object2` does not live long enough //~| ERROR value assigned to `object` is never read } -fn change_object3(mut object: &mut Object) { //~ ERROR variable `object` is assigned to, but never used +fn change_object3(mut object: &mut Object) { //~^ HELP you might have meant to mutate let object2 = Object; //~ HELP consider changing this to be mutable object = &mut object2; diff --git a/tests/ui/fn/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.stderr b/tests/ui/fn/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.stderr index 0330853d922..c2c7378f07a 100644 --- a/tests/ui/fn/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.stderr +++ b/tests/ui/fn/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.stderr @@ -23,7 +23,7 @@ LL | object = &object2; note: the lint level is defined here --> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:2:9 | -LL | #![deny(unused_assignments, unused_variables)] +LL | #![deny(unused_assignments)] | ^^^^^^^^^^^^^^^^^^ help: you might have meant to mutate the pointed at value being passed in, instead of changing the reference in the local binding | @@ -33,19 +33,6 @@ LL | let object2 = Object; LL ~ *object = object2; | -error: variable `object` is assigned to, but never used - --> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:11:23 - | -LL | fn change_object2(mut object: &Object) { - | ^^^^^^ - | - = note: consider using `_object` instead -note: the lint level is defined here - --> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:2:29 - | -LL | #![deny(unused_assignments, unused_variables)] - | ^^^^^^^^^^^^^^^^ - error[E0597]: `object2` does not live long enough --> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:14:13 | @@ -77,14 +64,6 @@ LL | let object2 = Object; LL ~ *object = object2; | -error: variable `object` is assigned to, but never used - --> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:19:23 - | -LL | fn change_object3(mut object: &mut Object) { - | ^^^^^^ - | - = note: consider using `_object` instead - error[E0596]: cannot borrow `object2` as mutable, as it is not declared as mutable --> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:22:14 | @@ -96,7 +75,7 @@ help: consider changing this to be mutable LL | let mut object2 = Object; | +++ -error: aborting due to 7 previous errors +error: aborting due to 5 previous errors Some errors have detailed explanations: E0308, E0596, E0597. For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/frontmatter/frontmatter-contains-whitespace.rs b/tests/ui/frontmatter/frontmatter-contains-whitespace.rs new file mode 100644 index 00000000000..11df822f793 --- /dev/null +++ b/tests/ui/frontmatter/frontmatter-contains-whitespace.rs @@ -0,0 +1,22 @@ +#!/usr/bin/env -S cargo -Zscript +---cargo +# Beware editing: it has numerous whitespace characters which are important. +# It contains one ranges from the 'PATTERN_WHITE_SPACE' property outlined in +# https://unicode.org/Public/UNIDATA/PropList.txt +# +# The characters in the first expression of the assertion can be generated +# from: "4\u{0C}+\n\t\r7\t*\u{20}2\u{85}/\u{200E}3\u{200F}*\u{2028}2\u{2029}" +package.description = """ +4+ + +7 * 2 /3* 2 +""" +--- + +//@ check-pass + +// Ensure the frontmatter can contain any whitespace + +#![feature(frontmatter)] + +fn main() {} diff --git a/tests/ui/frontmatter/frontmatter-crlf.rs b/tests/ui/frontmatter/frontmatter-crlf.rs new file mode 100644 index 00000000000..b46cc9ddbb0 --- /dev/null +++ b/tests/ui/frontmatter/frontmatter-crlf.rs @@ -0,0 +1,14 @@ +#!/usr/bin/env -S cargo -Zscript +--- +[dependencies] +clap = "4" +--- + +//@ check-pass +// ignore-tidy-cr + +// crlf line endings should be accepted + +#![feature(frontmatter)] + +fn main() {} diff --git a/tests/ui/frontmatter/frontmatter-whitespace-3.rs b/tests/ui/frontmatter/frontmatter-whitespace-3.rs index 95e0981e2ae..6580514fba2 100644 --- a/tests/ui/frontmatter/frontmatter-whitespace-3.rs +++ b/tests/ui/frontmatter/frontmatter-whitespace-3.rs @@ -1,7 +1,7 @@ ----cargo ---- +---cargo +--- // please note the whitespace characters after the first four lines. // This ensures that we accept whitespaces before the frontmatter, after @@ -10,6 +10,7 @@ //@ check-pass // ignore-tidy-end-whitespace // ignore-tidy-leading-newlines +// ignore-tidy-tab #![feature(frontmatter)] diff --git a/tests/ui/frontmatter/frontmatter-whitespace-4.rs b/tests/ui/frontmatter/frontmatter-whitespace-4.rs index 3bda3227838..1e7c5556647 100644 --- a/tests/ui/frontmatter/frontmatter-whitespace-4.rs +++ b/tests/ui/frontmatter/frontmatter-whitespace-4.rs @@ -1,7 +1,8 @@ ---- cargo +--- cargo --- //@ check-pass +// ignore-tidy-tab // A frontmatter infostring can have leading whitespace. #![feature(frontmatter)] diff --git a/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.current.stderr b/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.current.stderr new file mode 100644 index 00000000000..ccbe2d3593c --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.current.stderr @@ -0,0 +1,29 @@ +error[E0277]: the trait bound `(): ReturnsSend` is not satisfied + --> $DIR/ice-issue-146191.rs:6:52 + | +LL | fn create_complex_future() -> impl Future<Output = impl ReturnsSend> { + | ^^^^^^^^^^^^^^^^ the trait `ReturnsSend` is not implemented for `()` + | +help: this trait has no implementations, consider adding one + --> $DIR/ice-issue-146191.rs:14:1 + | +LL | trait ReturnsSend {} + | ^^^^^^^^^^^^^^^^^ +note: required by a bound in an opaque type + --> $DIR/ice-issue-146191.rs:6:57 + | +LL | fn create_complex_future() -> impl Future<Output = impl ReturnsSend> { + | ^^^^^^^^^^^ + +error[E0733]: recursion in an async block requires boxing + --> $DIR/ice-issue-146191.rs:8:5 + | +LL | async { create_complex_future().await } + | ^^^^^ ----------------------------- recursive call here + | + = note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0277, E0733. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.next.stderr b/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.next.stderr new file mode 100644 index 00000000000..e8b551c65fc --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.next.stderr @@ -0,0 +1,17 @@ +error[E0282]: type annotations needed + --> $DIR/ice-issue-146191.rs:8:5 + | +LL | async { create_complex_future().await } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type + +error[E0282]: type annotations needed + --> $DIR/ice-issue-146191.rs:8:5 + | +LL | async { create_complex_future().await } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.rs b/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.rs new file mode 100644 index 00000000000..356f7d01eb9 --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.rs @@ -0,0 +1,15 @@ +//@ edition: 2024 +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) + +fn create_complex_future() -> impl Future<Output = impl ReturnsSend> { + //[current]~^ ERROR the trait bound `(): ReturnsSend` is not satisfied + async { create_complex_future().await } + //[current]~^ ERROR recursion in an async block requires + //[next]~^^ ERROR type annotations needed + //[next]~| ERROR type annotations needed +} + +trait ReturnsSend {} +fn main() {} diff --git a/tests/ui/imports/overwritten-extern-flag-ambig.rs b/tests/ui/imports/overwritten-extern-flag-ambig.rs new file mode 100644 index 00000000000..80f394d34de --- /dev/null +++ b/tests/ui/imports/overwritten-extern-flag-ambig.rs @@ -0,0 +1,13 @@ +// Test for issue #145575. + +//@ check-pass +//@ edition: 2018 + +extern crate core as std; + +mod inner { + use crate::*; + use std::str; // OK for now +} + +fn main() {} diff --git a/tests/ui/linkage-attr/raw-dylib/windows/invalid-dlltool.rs b/tests/ui/linkage-attr/raw-dylib/windows/invalid-dlltool.rs index 0c78d799bad..f5f92db3b50 100644 --- a/tests/ui/linkage-attr/raw-dylib/windows/invalid-dlltool.rs +++ b/tests/ui/linkage-attr/raw-dylib/windows/invalid-dlltool.rs @@ -11,4 +11,4 @@ pub fn lib_main() { unsafe { f(42); } } -//~? ERROR Error calling dlltool 'does_not_exist.exe': program not found +//~? ERROR error calling dlltool 'does_not_exist.exe': program not found diff --git a/tests/ui/linkage-attr/raw-dylib/windows/invalid-dlltool.stderr b/tests/ui/linkage-attr/raw-dylib/windows/invalid-dlltool.stderr index 4bbad9b30a7..311d8c61589 100644 --- a/tests/ui/linkage-attr/raw-dylib/windows/invalid-dlltool.stderr +++ b/tests/ui/linkage-attr/raw-dylib/windows/invalid-dlltool.stderr @@ -1,4 +1,4 @@ -error: Error calling dlltool 'does_not_exist.exe': program not found +error: error calling dlltool 'does_not_exist.exe': program not found error: aborting due to 1 previous error diff --git a/tests/ui/linking/mixed-allocator-shim.rs b/tests/ui/linking/mixed-allocator-shim.rs new file mode 100644 index 00000000000..e4f20a11ebb --- /dev/null +++ b/tests/ui/linking/mixed-allocator-shim.rs @@ -0,0 +1,16 @@ +//@ build-pass +//@ compile-flags: --crate-type staticlib,dylib -Zstaticlib-prefer-dynamic +//@ no-prefer-dynamic +//@ needs-crate-type: dylib + +// Test that compiling for multiple crate types in a single compilation with +// mismatching allocator shim requirements doesn't result in the allocator shim +// missing entirely. +// In this particular test the dylib crate type will statically link libstd and +// thus need an allocator shim, while the staticlib crate type will dynamically +// link libstd and thus not need an allocator shim. +// The -Zstaticlib-prefer-dynamic flag could be avoided by doing it the other +// way around, but testing that the staticlib correctly has the allocator shim +// in that case would require a run-make test instead. + +pub fn foo() {} diff --git a/tests/ui/lint/improper_ctypes/allow-phantomdata-in-ffi.rs b/tests/ui/lint/improper-ctypes/allow-phantomdata-in-ffi.rs index a90159d2b58..a90159d2b58 100644 --- a/tests/ui/lint/improper_ctypes/allow-phantomdata-in-ffi.rs +++ b/tests/ui/lint/improper-ctypes/allow-phantomdata-in-ffi.rs diff --git a/tests/ui/lint/lint-ctypes-113436-1.rs b/tests/ui/lint/improper-ctypes/lint-113436-1.rs index 1ca59c6868d..1ca59c6868d 100644 --- a/tests/ui/lint/lint-ctypes-113436-1.rs +++ b/tests/ui/lint/improper-ctypes/lint-113436-1.rs diff --git a/tests/ui/lint/lint-ctypes-113436-1.stderr b/tests/ui/lint/improper-ctypes/lint-113436-1.stderr index 7b63043f057..f01dc3b6e0d 100644 --- a/tests/ui/lint/lint-ctypes-113436-1.stderr +++ b/tests/ui/lint/improper-ctypes/lint-113436-1.stderr @@ -1,5 +1,5 @@ error: `extern` fn uses type `NotSafe`, which is not FFI-safe - --> $DIR/lint-ctypes-113436-1.rs:22:22 + --> $DIR/lint-113436-1.rs:22:22 | LL | extern "C" fn bar(x: Bar) -> Bar { | ^^^ not FFI-safe @@ -7,18 +7,18 @@ LL | extern "C" fn bar(x: Bar) -> Bar { = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct = note: this struct has unspecified layout note: the type is defined here - --> $DIR/lint-ctypes-113436-1.rs:13:1 + --> $DIR/lint-113436-1.rs:13:1 | LL | struct NotSafe(u32); | ^^^^^^^^^^^^^^ note: the lint level is defined here - --> $DIR/lint-ctypes-113436-1.rs:1:9 + --> $DIR/lint-113436-1.rs:1:9 | LL | #![deny(improper_ctypes_definitions)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `extern` fn uses type `NotSafe`, which is not FFI-safe - --> $DIR/lint-ctypes-113436-1.rs:22:30 + --> $DIR/lint-113436-1.rs:22:30 | LL | extern "C" fn bar(x: Bar) -> Bar { | ^^^ not FFI-safe @@ -26,7 +26,7 @@ LL | extern "C" fn bar(x: Bar) -> Bar { = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct = note: this struct has unspecified layout note: the type is defined here - --> $DIR/lint-ctypes-113436-1.rs:13:1 + --> $DIR/lint-113436-1.rs:13:1 | LL | struct NotSafe(u32); | ^^^^^^^^^^^^^^ diff --git a/tests/ui/lint/lint-ctypes-73249-2.rs b/tests/ui/lint/improper-ctypes/lint-73249-2.rs index 31af0e3d381..31af0e3d381 100644 --- a/tests/ui/lint/lint-ctypes-73249-2.rs +++ b/tests/ui/lint/improper-ctypes/lint-73249-2.rs diff --git a/tests/ui/lint/lint-ctypes-73249-2.stderr b/tests/ui/lint/improper-ctypes/lint-73249-2.stderr index 2d0dfe94f09..d6c1cec2bd6 100644 --- a/tests/ui/lint/lint-ctypes-73249-2.stderr +++ b/tests/ui/lint/improper-ctypes/lint-73249-2.stderr @@ -1,12 +1,12 @@ error: `extern` block uses type `Qux`, which is not FFI-safe - --> $DIR/lint-ctypes-73249-2.rs:27:21 + --> $DIR/lint-73249-2.rs:27:21 | LL | fn lint_me() -> A<()>; | ^^^^^ not FFI-safe | = note: opaque types have no C equivalent note: the lint level is defined here - --> $DIR/lint-ctypes-73249-2.rs:2:9 + --> $DIR/lint-73249-2.rs:2:9 | LL | #![deny(improper_ctypes)] | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/lint/lint-ctypes-73249-3.rs b/tests/ui/lint/improper-ctypes/lint-73249-3.rs index 8bdf536bf77..8bdf536bf77 100644 --- a/tests/ui/lint/lint-ctypes-73249-3.rs +++ b/tests/ui/lint/improper-ctypes/lint-73249-3.rs diff --git a/tests/ui/lint/lint-ctypes-73249-3.stderr b/tests/ui/lint/improper-ctypes/lint-73249-3.stderr index e1a313a2906..ebc9eb5eb82 100644 --- a/tests/ui/lint/lint-ctypes-73249-3.stderr +++ b/tests/ui/lint/improper-ctypes/lint-73249-3.stderr @@ -1,12 +1,12 @@ error: `extern` block uses type `Qux`, which is not FFI-safe - --> $DIR/lint-ctypes-73249-3.rs:21:25 + --> $DIR/lint-73249-3.rs:21:25 | LL | pub fn lint_me() -> A; | ^ not FFI-safe | = note: opaque types have no C equivalent note: the lint level is defined here - --> $DIR/lint-ctypes-73249-3.rs:2:9 + --> $DIR/lint-73249-3.rs:2:9 | LL | #![deny(improper_ctypes)] | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/lint/lint-ctypes-73249-5.rs b/tests/ui/lint/improper-ctypes/lint-73249-5.rs index cc6da59950d..cc6da59950d 100644 --- a/tests/ui/lint/lint-ctypes-73249-5.rs +++ b/tests/ui/lint/improper-ctypes/lint-73249-5.rs diff --git a/tests/ui/lint/lint-ctypes-73249-5.stderr b/tests/ui/lint/improper-ctypes/lint-73249-5.stderr index c4fa955de05..484927f57fe 100644 --- a/tests/ui/lint/lint-ctypes-73249-5.stderr +++ b/tests/ui/lint/improper-ctypes/lint-73249-5.stderr @@ -1,12 +1,12 @@ error: `extern` block uses type `Qux`, which is not FFI-safe - --> $DIR/lint-ctypes-73249-5.rs:21:25 + --> $DIR/lint-73249-5.rs:21:25 | LL | pub fn lint_me() -> A; | ^ not FFI-safe | = note: opaque types have no C equivalent note: the lint level is defined here - --> $DIR/lint-ctypes-73249-5.rs:2:9 + --> $DIR/lint-73249-5.rs:2:9 | LL | #![deny(improper_ctypes)] | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/lint/lint-ctypes-73251-1.rs b/tests/ui/lint/improper-ctypes/lint-73251-1.rs index 07ae05be69f..07ae05be69f 100644 --- a/tests/ui/lint/lint-ctypes-73251-1.rs +++ b/tests/ui/lint/improper-ctypes/lint-73251-1.rs diff --git a/tests/ui/lint/lint-ctypes-73251-1.stderr b/tests/ui/lint/improper-ctypes/lint-73251-1.stderr index 675a9de51cd..749722f0e22 100644 --- a/tests/ui/lint/lint-ctypes-73251-1.stderr +++ b/tests/ui/lint/improper-ctypes/lint-73251-1.stderr @@ -1,12 +1,12 @@ error: `extern` block uses type `Qux`, which is not FFI-safe - --> $DIR/lint-ctypes-73251-1.rs:24:21 + --> $DIR/lint-73251-1.rs:24:21 | LL | fn lint_me() -> <u32 as Foo>::Assoc; | ^^^^^^^^^^^^^^^^^^^ not FFI-safe | = note: opaque types have no C equivalent note: the lint level is defined here - --> $DIR/lint-ctypes-73251-1.rs:2:9 + --> $DIR/lint-73251-1.rs:2:9 | LL | #![deny(improper_ctypes)] | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/lint/lint-ctypes-73251-2.rs b/tests/ui/lint/improper-ctypes/lint-73251-2.rs index c47118672e0..c47118672e0 100644 --- a/tests/ui/lint/lint-ctypes-73251-2.rs +++ b/tests/ui/lint/improper-ctypes/lint-73251-2.rs diff --git a/tests/ui/lint/lint-ctypes-73251-2.stderr b/tests/ui/lint/improper-ctypes/lint-73251-2.stderr index 634950b29ed..3770b7d789f 100644 --- a/tests/ui/lint/lint-ctypes-73251-2.stderr +++ b/tests/ui/lint/improper-ctypes/lint-73251-2.stderr @@ -1,12 +1,12 @@ error: `extern` block uses type `AliasA`, which is not FFI-safe - --> $DIR/lint-ctypes-73251-2.rs:38:21 + --> $DIR/lint-73251-2.rs:38:21 | LL | fn lint_me() -> <AliasB as TraitB>::Assoc; | ^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe | = note: opaque types have no C equivalent note: the lint level is defined here - --> $DIR/lint-ctypes-73251-2.rs:2:9 + --> $DIR/lint-73251-2.rs:2:9 | LL | #![deny(improper_ctypes)] | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/lint/lint-ctypes-94223.rs b/tests/ui/lint/improper-ctypes/lint-94223.rs index ac24f61b0ac..ac24f61b0ac 100644 --- a/tests/ui/lint/lint-ctypes-94223.rs +++ b/tests/ui/lint/improper-ctypes/lint-94223.rs diff --git a/tests/ui/lint/lint-ctypes-94223.stderr b/tests/ui/lint/improper-ctypes/lint-94223.stderr index bd127cf6004..008debf8f01 100644 --- a/tests/ui/lint/lint-ctypes-94223.stderr +++ b/tests/ui/lint/improper-ctypes/lint-94223.stderr @@ -1,5 +1,5 @@ error: `extern` fn uses type `[u8]`, which is not FFI-safe - --> $DIR/lint-ctypes-94223.rs:4:15 + --> $DIR/lint-94223.rs:4:15 | LL | pub fn bad(f: extern "C" fn([u8])) {} | ^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -7,13 +7,13 @@ LL | pub fn bad(f: extern "C" fn([u8])) {} = help: consider using a raw pointer instead = note: slices have no C equivalent note: the lint level is defined here - --> $DIR/lint-ctypes-94223.rs:2:9 + --> $DIR/lint-94223.rs:2:9 | LL | #![deny(improper_ctypes_definitions)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `extern` fn uses type `[u8]`, which is not FFI-safe - --> $DIR/lint-ctypes-94223.rs:7:28 + --> $DIR/lint-94223.rs:7:28 | LL | pub fn bad_twice(f: Result<extern "C" fn([u8]), extern "C" fn([u8])>) {} | ^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -22,7 +22,7 @@ LL | pub fn bad_twice(f: Result<extern "C" fn([u8]), extern "C" fn([u8])>) {} = note: slices have no C equivalent error: `extern` fn uses type `[u8]`, which is not FFI-safe - --> $DIR/lint-ctypes-94223.rs:7:49 + --> $DIR/lint-94223.rs:7:49 | LL | pub fn bad_twice(f: Result<extern "C" fn([u8]), extern "C" fn([u8])>) {} | ^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -31,7 +31,7 @@ LL | pub fn bad_twice(f: Result<extern "C" fn([u8]), extern "C" fn([u8])>) {} = note: slices have no C equivalent error: `extern` fn uses type `[u8]`, which is not FFI-safe - --> $DIR/lint-ctypes-94223.rs:11:18 + --> $DIR/lint-94223.rs:11:18 | LL | struct BadStruct(extern "C" fn([u8])); | ^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -40,7 +40,7 @@ LL | struct BadStruct(extern "C" fn([u8])); = note: slices have no C equivalent error: `extern` fn uses type `[u8]`, which is not FFI-safe - --> $DIR/lint-ctypes-94223.rs:15:7 + --> $DIR/lint-94223.rs:15:7 | LL | A(extern "C" fn([u8])), | ^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -49,7 +49,7 @@ LL | A(extern "C" fn([u8])), = note: slices have no C equivalent error: `extern` fn uses type `[u8]`, which is not FFI-safe - --> $DIR/lint-ctypes-94223.rs:20:7 + --> $DIR/lint-94223.rs:20:7 | LL | A(extern "C" fn([u8])), | ^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -58,7 +58,7 @@ LL | A(extern "C" fn([u8])), = note: slices have no C equivalent error: `extern` fn uses type `[u8]`, which is not FFI-safe - --> $DIR/lint-ctypes-94223.rs:24:12 + --> $DIR/lint-94223.rs:24:12 | LL | type Foo = extern "C" fn([u8]); | ^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -67,7 +67,7 @@ LL | type Foo = extern "C" fn([u8]); = note: slices have no C equivalent error: `extern` fn uses type `Option<&<T as FooTrait>::FooType>`, which is not FFI-safe - --> $DIR/lint-ctypes-94223.rs:31:20 + --> $DIR/lint-94223.rs:31:20 | LL | pub type Foo2<T> = extern "C" fn(Option<&<T as FooTrait>::FooType>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -76,7 +76,7 @@ LL | pub type Foo2<T> = extern "C" fn(Option<&<T as FooTrait>::FooType>); = note: enum has no representation hint error: `extern` fn uses type `FfiUnsafe`, which is not FFI-safe - --> $DIR/lint-ctypes-94223.rs:41:17 + --> $DIR/lint-94223.rs:41:17 | LL | pub static BAD: extern "C" fn(FfiUnsafe) = f; | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -84,13 +84,13 @@ LL | pub static BAD: extern "C" fn(FfiUnsafe) = f; = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct = note: this struct has unspecified layout note: the type is defined here - --> $DIR/lint-ctypes-94223.rs:34:1 + --> $DIR/lint-94223.rs:34:1 | LL | pub struct FfiUnsafe; | ^^^^^^^^^^^^^^^^^^^^ error: `extern` fn uses type `FfiUnsafe`, which is not FFI-safe - --> $DIR/lint-ctypes-94223.rs:44:30 + --> $DIR/lint-94223.rs:44:30 | LL | pub static BAD_TWICE: Result<extern "C" fn(FfiUnsafe), extern "C" fn(FfiUnsafe)> = Ok(f); | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -98,13 +98,13 @@ LL | pub static BAD_TWICE: Result<extern "C" fn(FfiUnsafe), extern "C" fn(FfiUns = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct = note: this struct has unspecified layout note: the type is defined here - --> $DIR/lint-ctypes-94223.rs:34:1 + --> $DIR/lint-94223.rs:34:1 | LL | pub struct FfiUnsafe; | ^^^^^^^^^^^^^^^^^^^^ error: `extern` fn uses type `FfiUnsafe`, which is not FFI-safe - --> $DIR/lint-ctypes-94223.rs:44:56 + --> $DIR/lint-94223.rs:44:56 | LL | pub static BAD_TWICE: Result<extern "C" fn(FfiUnsafe), extern "C" fn(FfiUnsafe)> = Ok(f); | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -112,13 +112,13 @@ LL | pub static BAD_TWICE: Result<extern "C" fn(FfiUnsafe), extern "C" fn(FfiUns = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct = note: this struct has unspecified layout note: the type is defined here - --> $DIR/lint-ctypes-94223.rs:34:1 + --> $DIR/lint-94223.rs:34:1 | LL | pub struct FfiUnsafe; | ^^^^^^^^^^^^^^^^^^^^ error: `extern` fn uses type `FfiUnsafe`, which is not FFI-safe - --> $DIR/lint-ctypes-94223.rs:48:22 + --> $DIR/lint-94223.rs:48:22 | LL | pub const BAD_CONST: extern "C" fn(FfiUnsafe) = f; | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -126,7 +126,7 @@ LL | pub const BAD_CONST: extern "C" fn(FfiUnsafe) = f; = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct = note: this struct has unspecified layout note: the type is defined here - --> $DIR/lint-ctypes-94223.rs:34:1 + --> $DIR/lint-94223.rs:34:1 | LL | pub struct FfiUnsafe; | ^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/lint/lint-ctypes-cstr.rs b/tests/ui/lint/improper-ctypes/lint-cstr.rs index b04decd0bca..b04decd0bca 100644 --- a/tests/ui/lint/lint-ctypes-cstr.rs +++ b/tests/ui/lint/improper-ctypes/lint-cstr.rs diff --git a/tests/ui/lint/lint-ctypes-cstr.stderr b/tests/ui/lint/improper-ctypes/lint-cstr.stderr index 8957758d577..da263065843 100644 --- a/tests/ui/lint/lint-ctypes-cstr.stderr +++ b/tests/ui/lint/improper-ctypes/lint-cstr.stderr @@ -1,5 +1,5 @@ error: `extern` block uses type `CStr`, which is not FFI-safe - --> $DIR/lint-ctypes-cstr.rs:7:21 + --> $DIR/lint-cstr.rs:7:21 | LL | fn take_cstr(s: CStr); | ^^^^ not FFI-safe @@ -7,13 +7,13 @@ LL | fn take_cstr(s: CStr); = help: consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()` = note: `CStr`/`CString` do not have a guaranteed layout note: the lint level is defined here - --> $DIR/lint-ctypes-cstr.rs:2:9 + --> $DIR/lint-cstr.rs:2:9 | LL | #![deny(improper_ctypes, improper_ctypes_definitions)] | ^^^^^^^^^^^^^^^ error: `extern` block uses type `CStr`, which is not FFI-safe - --> $DIR/lint-ctypes-cstr.rs:10:25 + --> $DIR/lint-cstr.rs:10:25 | LL | fn take_cstr_ref(s: &CStr); | ^^^^^ not FFI-safe @@ -22,7 +22,7 @@ LL | fn take_cstr_ref(s: &CStr); = note: `CStr`/`CString` do not have a guaranteed layout error: `extern` block uses type `CString`, which is not FFI-safe - --> $DIR/lint-ctypes-cstr.rs:13:24 + --> $DIR/lint-cstr.rs:13:24 | LL | fn take_cstring(s: CString); | ^^^^^^^ not FFI-safe @@ -31,7 +31,7 @@ LL | fn take_cstring(s: CString); = note: `CStr`/`CString` do not have a guaranteed layout error: `extern` block uses type `CString`, which is not FFI-safe - --> $DIR/lint-ctypes-cstr.rs:16:28 + --> $DIR/lint-cstr.rs:16:28 | LL | fn take_cstring_ref(s: &CString); | ^^^^^^^^ not FFI-safe @@ -40,7 +40,7 @@ LL | fn take_cstring_ref(s: &CString); = note: `CStr`/`CString` do not have a guaranteed layout error: `extern` block uses type `CString`, which is not FFI-safe - --> $DIR/lint-ctypes-cstr.rs:20:43 + --> $DIR/lint-cstr.rs:20:43 | LL | fn no_special_help_for_mut_cstring(s: *mut CString); | ^^^^^^^^^^^^ not FFI-safe @@ -49,7 +49,7 @@ LL | fn no_special_help_for_mut_cstring(s: *mut CString); = note: this struct has unspecified layout error: `extern` block uses type `CString`, which is not FFI-safe - --> $DIR/lint-ctypes-cstr.rs:24:47 + --> $DIR/lint-cstr.rs:24:47 | LL | fn no_special_help_for_mut_cstring_ref(s: &mut CString); | ^^^^^^^^^^^^ not FFI-safe @@ -58,7 +58,7 @@ LL | fn no_special_help_for_mut_cstring_ref(s: &mut CString); = note: this struct has unspecified layout error: `extern` fn uses type `CStr`, which is not FFI-safe - --> $DIR/lint-ctypes-cstr.rs:29:37 + --> $DIR/lint-cstr.rs:29:37 | LL | extern "C" fn rust_take_cstr_ref(s: &CStr) {} | ^^^^^ not FFI-safe @@ -66,13 +66,13 @@ LL | extern "C" fn rust_take_cstr_ref(s: &CStr) {} = help: consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()` = note: `CStr`/`CString` do not have a guaranteed layout note: the lint level is defined here - --> $DIR/lint-ctypes-cstr.rs:2:26 + --> $DIR/lint-cstr.rs:2:26 | LL | #![deny(improper_ctypes, improper_ctypes_definitions)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `extern` fn uses type `CString`, which is not FFI-safe - --> $DIR/lint-ctypes-cstr.rs:32:36 + --> $DIR/lint-cstr.rs:32:36 | LL | extern "C" fn rust_take_cstring(s: CString) {} | ^^^^^^^ not FFI-safe diff --git a/tests/ui/lint/lint-ctypes.rs b/tests/ui/lint/improper-ctypes/lint-ctypes.rs index 47586c826ab..7dc06079fa3 100644 --- a/tests/ui/lint/lint-ctypes.rs +++ b/tests/ui/lint/improper-ctypes/lint-ctypes.rs @@ -52,7 +52,6 @@ extern "C" { pub fn str_type(p: &str); //~ ERROR: uses type `str` pub fn box_type(p: Box<u32>); //~ ERROR uses type `Box<u32>` pub fn opt_box_type(p: Option<Box<u32>>); - //~^ ERROR uses type `Option<Box<u32>>` pub fn char_type(p: char); //~ ERROR uses type `char` pub fn trait_type(p: &dyn Bar); //~ ERROR uses type `dyn Bar` pub fn tuple_type(p: (i32, i32)); //~ ERROR uses type `(i32, i32)` diff --git a/tests/ui/lint/lint-ctypes.stderr b/tests/ui/lint/improper-ctypes/lint-ctypes.stderr index 3fb36647d4f..6f8b951c53d 100644 --- a/tests/ui/lint/lint-ctypes.stderr +++ b/tests/ui/lint/improper-ctypes/lint-ctypes.stderr @@ -67,17 +67,8 @@ LL | pub fn box_type(p: Box<u32>); = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct = note: this struct has unspecified layout -error: `extern` block uses type `Option<Box<u32>>`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:54:28 - | -LL | pub fn opt_box_type(p: Option<Box<u32>>); - | ^^^^^^^^^^^^^^^^ not FFI-safe - | - = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum - = note: enum has no representation hint - error: `extern` block uses type `char`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:56:25 + --> $DIR/lint-ctypes.rs:55:25 | LL | pub fn char_type(p: char); | ^^^^ not FFI-safe @@ -86,7 +77,7 @@ LL | pub fn char_type(p: char); = note: the `char` type has no C equivalent error: `extern` block uses type `dyn Bar`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:57:26 + --> $DIR/lint-ctypes.rs:56:26 | LL | pub fn trait_type(p: &dyn Bar); | ^^^^^^^^ not FFI-safe @@ -94,7 +85,7 @@ LL | pub fn trait_type(p: &dyn Bar); = note: trait objects have no C equivalent error: `extern` block uses type `(i32, i32)`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:58:26 + --> $DIR/lint-ctypes.rs:57:26 | LL | pub fn tuple_type(p: (i32, i32)); | ^^^^^^^^^^ not FFI-safe @@ -103,7 +94,7 @@ LL | pub fn tuple_type(p: (i32, i32)); = note: tuples have unspecified layout error: `extern` block uses type `(i32, i32)`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:59:27 + --> $DIR/lint-ctypes.rs:58:27 | LL | pub fn tuple_type2(p: I32Pair); | ^^^^^^^ not FFI-safe @@ -112,7 +103,7 @@ LL | pub fn tuple_type2(p: I32Pair); = note: tuples have unspecified layout error: `extern` block uses type `ZeroSize`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:60:25 + --> $DIR/lint-ctypes.rs:59:25 | LL | pub fn zero_size(p: ZeroSize); | ^^^^^^^^ not FFI-safe @@ -126,7 +117,7 @@ LL | pub struct ZeroSize; | ^^^^^^^^^^^^^^^^^^^ error: `extern` block uses type `ZeroSizeWithPhantomData`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:61:33 + --> $DIR/lint-ctypes.rs:60:33 | LL | pub fn zero_size_phantom(p: ZeroSizeWithPhantomData); | ^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -139,7 +130,7 @@ LL | pub struct ZeroSizeWithPhantomData(::std::marker::PhantomData<i32>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `extern` block uses type `PhantomData<bool>`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:64:12 + --> $DIR/lint-ctypes.rs:63:12 | LL | -> ::std::marker::PhantomData<bool>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -147,7 +138,7 @@ LL | -> ::std::marker::PhantomData<bool>; = note: composed only of `PhantomData` error: `extern` block uses type `fn()`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:65:23 + --> $DIR/lint-ctypes.rs:64:23 | LL | pub fn fn_type(p: RustFn); | ^^^^^^ not FFI-safe @@ -156,7 +147,7 @@ LL | pub fn fn_type(p: RustFn); = note: this function pointer has Rust-specific calling convention error: `extern` block uses type `fn()`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:66:24 + --> $DIR/lint-ctypes.rs:65:24 | LL | pub fn fn_type2(p: fn()); | ^^^^ not FFI-safe @@ -165,7 +156,7 @@ LL | pub fn fn_type2(p: fn()); = note: this function pointer has Rust-specific calling convention error: `extern` block uses type `Box<u32>`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:67:28 + --> $DIR/lint-ctypes.rs:66:28 | LL | pub fn fn_contained(p: RustBadRet); | ^^^^^^^^^^ not FFI-safe @@ -174,7 +165,7 @@ LL | pub fn fn_contained(p: RustBadRet); = note: this struct has unspecified layout error: `extern` block uses type `str`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:68:31 + --> $DIR/lint-ctypes.rs:67:31 | LL | pub fn transparent_str(p: TransparentStr); | ^^^^^^^^^^^^^^ not FFI-safe @@ -183,7 +174,7 @@ LL | pub fn transparent_str(p: TransparentStr); = note: string slices have no C equivalent error: `extern` block uses type `Box<u32>`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:69:30 + --> $DIR/lint-ctypes.rs:68:30 | LL | pub fn transparent_fn(p: TransparentBadFn); | ^^^^^^^^^^^^^^^^ not FFI-safe @@ -192,7 +183,7 @@ LL | pub fn transparent_fn(p: TransparentBadFn); = note: this struct has unspecified layout error: `extern` block uses type `[u8; 8]`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:70:27 + --> $DIR/lint-ctypes.rs:69:27 | LL | pub fn raw_array(arr: [u8; 8]); | ^^^^^^^ not FFI-safe @@ -201,7 +192,7 @@ LL | pub fn raw_array(arr: [u8; 8]); = note: passing raw arrays by value is not FFI-safe error: `extern` block uses type `Option<UnsafeCell<extern "C" fn()>>`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:72:26 + --> $DIR/lint-ctypes.rs:71:26 | LL | pub fn no_niche_a(a: Option<UnsafeCell<extern "C" fn()>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -210,7 +201,7 @@ LL | pub fn no_niche_a(a: Option<UnsafeCell<extern "C" fn()>>); = note: enum has no representation hint error: `extern` block uses type `Option<UnsafeCell<&i32>>`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:74:26 + --> $DIR/lint-ctypes.rs:73:26 | LL | pub fn no_niche_b(b: Option<UnsafeCell<&i32>>); | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -218,5 +209,5 @@ LL | pub fn no_niche_b(b: Option<UnsafeCell<&i32>>); = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum = note: enum has no representation hint -error: aborting due to 22 previous errors +error: aborting due to 21 previous errors diff --git a/tests/ui/lint/lint-ctypes-enum.rs b/tests/ui/lint/improper-ctypes/lint-enum.rs index f900f998d06..f900f998d06 100644 --- a/tests/ui/lint/lint-ctypes-enum.rs +++ b/tests/ui/lint/improper-ctypes/lint-enum.rs diff --git a/tests/ui/lint/lint-ctypes-enum.stderr b/tests/ui/lint/improper-ctypes/lint-enum.stderr index 40d22723309..35d1dcb87fd 100644 --- a/tests/ui/lint/lint-ctypes-enum.stderr +++ b/tests/ui/lint/improper-ctypes/lint-enum.stderr @@ -1,5 +1,5 @@ error: `extern` block uses type `U`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:82:14 + --> $DIR/lint-enum.rs:82:14 | LL | fn uf(x: U); | ^ not FFI-safe @@ -7,18 +7,18 @@ LL | fn uf(x: U); = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum = note: enum has no representation hint note: the type is defined here - --> $DIR/lint-ctypes-enum.rs:9:1 + --> $DIR/lint-enum.rs:9:1 | LL | enum U { | ^^^^^^ note: the lint level is defined here - --> $DIR/lint-ctypes-enum.rs:2:9 + --> $DIR/lint-enum.rs:2:9 | LL | #![deny(improper_ctypes)] | ^^^^^^^^^^^^^^^ error: `extern` block uses type `B`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:83:14 + --> $DIR/lint-enum.rs:83:14 | LL | fn bf(x: B); | ^ not FFI-safe @@ -26,13 +26,13 @@ LL | fn bf(x: B); = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum = note: enum has no representation hint note: the type is defined here - --> $DIR/lint-ctypes-enum.rs:12:1 + --> $DIR/lint-enum.rs:12:1 | LL | enum B { | ^^^^^^ error: `extern` block uses type `T`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:84:14 + --> $DIR/lint-enum.rs:84:14 | LL | fn tf(x: T); | ^ not FFI-safe @@ -40,13 +40,13 @@ LL | fn tf(x: T); = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum = note: enum has no representation hint note: the type is defined here - --> $DIR/lint-ctypes-enum.rs:16:1 + --> $DIR/lint-enum.rs:16:1 | LL | enum T { | ^^^^^^ error: `extern` block uses type `Option<TransparentUnion<NonZero<u8>>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:108:36 + --> $DIR/lint-enum.rs:108:36 | LL | fn option_transparent_union(x: Option<TransparentUnion<num::NonZero<u8>>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -55,7 +55,7 @@ LL | fn option_transparent_union(x: Option<TransparentUnion<num::NonZero<u8> = note: enum has no representation hint error: `extern` block uses type `Option<Rust<NonZero<u8>>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:110:28 + --> $DIR/lint-enum.rs:110:28 | LL | fn option_repr_rust(x: Option<Rust<num::NonZero<u8>>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -64,7 +64,7 @@ LL | fn option_repr_rust(x: Option<Rust<num::NonZero<u8>>>); = note: enum has no representation hint error: `extern` block uses type `Option<u8>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:111:21 + --> $DIR/lint-enum.rs:111:21 | LL | fn option_u8(x: Option<u8>); | ^^^^^^^^^^ not FFI-safe @@ -73,7 +73,7 @@ LL | fn option_u8(x: Option<u8>); = note: enum has no representation hint error: `extern` block uses type `Result<TransparentUnion<NonZero<u8>>, ()>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:131:38 + --> $DIR/lint-enum.rs:131:38 | LL | fn result_transparent_union_t(x: Result<TransparentUnion<num::NonZero<u8>>, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -82,7 +82,7 @@ LL | fn result_transparent_union_t(x: Result<TransparentUnion<num::NonZero<u = note: enum has no representation hint error: `extern` block uses type `Result<Rust<NonZero<u8>>, ()>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:133:30 + --> $DIR/lint-enum.rs:133:30 | LL | fn result_repr_rust_t(x: Result<Rust<num::NonZero<u8>>, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -91,7 +91,7 @@ LL | fn result_repr_rust_t(x: Result<Rust<num::NonZero<u8>>, ()>); = note: enum has no representation hint error: `extern` block uses type `Result<NonZero<u8>, U>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:137:51 + --> $DIR/lint-enum.rs:137:51 | LL | fn result_1zst_exhaustive_single_variant_t(x: Result<num::NonZero<u8>, U>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -100,7 +100,7 @@ LL | fn result_1zst_exhaustive_single_variant_t(x: Result<num::NonZero<u8>, = note: enum has no representation hint error: `extern` block uses type `Result<NonZero<u8>, B>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:139:53 + --> $DIR/lint-enum.rs:139:53 | LL | fn result_1zst_exhaustive_multiple_variant_t(x: Result<num::NonZero<u8>, B>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -109,7 +109,7 @@ LL | fn result_1zst_exhaustive_multiple_variant_t(x: Result<num::NonZero<u8> = note: enum has no representation hint error: `extern` block uses type `Result<NonZero<u8>, NonExhaustive>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:141:51 + --> $DIR/lint-enum.rs:141:51 | LL | fn result_1zst_non_exhaustive_no_variant_t(x: Result<num::NonZero<u8>, NonExhaustive>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -118,7 +118,7 @@ LL | fn result_1zst_non_exhaustive_no_variant_t(x: Result<num::NonZero<u8>, = note: enum has no representation hint error: `extern` block uses type `Result<NonZero<u8>, Field>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:144:49 + --> $DIR/lint-enum.rs:144:49 | LL | fn result_1zst_exhaustive_single_field_t(x: Result<num::NonZero<u8>, Field>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -127,7 +127,7 @@ LL | fn result_1zst_exhaustive_single_field_t(x: Result<num::NonZero<u8>, Fi = note: enum has no representation hint error: `extern` block uses type `Result<Result<(), NonZero<u8>>, ()>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:146:30 + --> $DIR/lint-enum.rs:146:30 | LL | fn result_cascading_t(x: Result<Result<(), num::NonZero<u8>>, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -136,7 +136,7 @@ LL | fn result_cascading_t(x: Result<Result<(), num::NonZero<u8>>, ()>); = note: enum has no representation hint error: `extern` block uses type `Result<(), TransparentUnion<NonZero<u8>>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:167:38 + --> $DIR/lint-enum.rs:167:38 | LL | fn result_transparent_union_e(x: Result<(), TransparentUnion<num::NonZero<u8>>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -145,7 +145,7 @@ LL | fn result_transparent_union_e(x: Result<(), TransparentUnion<num::NonZe = note: enum has no representation hint error: `extern` block uses type `Result<(), Rust<NonZero<u8>>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:169:30 + --> $DIR/lint-enum.rs:169:30 | LL | fn result_repr_rust_e(x: Result<(), Rust<num::NonZero<u8>>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -154,7 +154,7 @@ LL | fn result_repr_rust_e(x: Result<(), Rust<num::NonZero<u8>>>); = note: enum has no representation hint error: `extern` block uses type `Result<U, NonZero<u8>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:173:51 + --> $DIR/lint-enum.rs:173:51 | LL | fn result_1zst_exhaustive_single_variant_e(x: Result<U, num::NonZero<u8>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -163,7 +163,7 @@ LL | fn result_1zst_exhaustive_single_variant_e(x: Result<U, num::NonZero<u8 = note: enum has no representation hint error: `extern` block uses type `Result<B, NonZero<u8>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:175:53 + --> $DIR/lint-enum.rs:175:53 | LL | fn result_1zst_exhaustive_multiple_variant_e(x: Result<B, num::NonZero<u8>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -172,7 +172,7 @@ LL | fn result_1zst_exhaustive_multiple_variant_e(x: Result<B, num::NonZero< = note: enum has no representation hint error: `extern` block uses type `Result<NonExhaustive, NonZero<u8>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:177:51 + --> $DIR/lint-enum.rs:177:51 | LL | fn result_1zst_non_exhaustive_no_variant_e(x: Result<NonExhaustive, num::NonZero<u8>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -181,7 +181,7 @@ LL | fn result_1zst_non_exhaustive_no_variant_e(x: Result<NonExhaustive, num = note: enum has no representation hint error: `extern` block uses type `Result<Field, NonZero<u8>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:180:49 + --> $DIR/lint-enum.rs:180:49 | LL | fn result_1zst_exhaustive_single_field_e(x: Result<Field, num::NonZero<u8>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -190,7 +190,7 @@ LL | fn result_1zst_exhaustive_single_field_e(x: Result<Field, num::NonZero< = note: enum has no representation hint error: `extern` block uses type `Result<(), Result<(), NonZero<u8>>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:182:30 + --> $DIR/lint-enum.rs:182:30 | LL | fn result_cascading_e(x: Result<(), Result<(), num::NonZero<u8>>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -199,7 +199,7 @@ LL | fn result_cascading_e(x: Result<(), Result<(), num::NonZero<u8>>>); = note: enum has no representation hint error: `extern` block uses type `Result<(), ()>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:184:27 + --> $DIR/lint-enum.rs:184:27 | LL | fn result_unit_t_e(x: Result<(), ()>); | ^^^^^^^^^^^^^^ not FFI-safe diff --git a/tests/ui/lint/lint-ctypes-fn.rs b/tests/ui/lint/improper-ctypes/lint-fn.rs index 0b84098e390..0b84098e390 100644 --- a/tests/ui/lint/lint-ctypes-fn.rs +++ b/tests/ui/lint/improper-ctypes/lint-fn.rs diff --git a/tests/ui/lint/lint-ctypes-fn.stderr b/tests/ui/lint/improper-ctypes/lint-fn.stderr index a19c04a63b5..34e3bd021b9 100644 --- a/tests/ui/lint/lint-ctypes-fn.stderr +++ b/tests/ui/lint/improper-ctypes/lint-fn.stderr @@ -1,5 +1,5 @@ error: `extern` fn uses type `[u32]`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:70:33 + --> $DIR/lint-fn.rs:70:33 | LL | pub extern "C" fn slice_type(p: &[u32]) { } | ^^^^^^ not FFI-safe @@ -7,13 +7,13 @@ LL | pub extern "C" fn slice_type(p: &[u32]) { } = help: consider using a raw pointer instead = note: slices have no C equivalent note: the lint level is defined here - --> $DIR/lint-ctypes-fn.rs:2:9 + --> $DIR/lint-fn.rs:2:9 | LL | #![deny(improper_ctypes_definitions)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `extern` fn uses type `str`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:73:31 + --> $DIR/lint-fn.rs:73:31 | LL | pub extern "C" fn str_type(p: &str) { } | ^^^^ not FFI-safe @@ -22,7 +22,7 @@ LL | pub extern "C" fn str_type(p: &str) { } = note: string slices have no C equivalent error: `extern` fn uses type `Box<[u8]>`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:80:34 + --> $DIR/lint-fn.rs:80:34 | LL | pub extern "C" fn boxed_slice(p: Box<[u8]>) { } | ^^^^^^^^^ not FFI-safe @@ -30,7 +30,7 @@ LL | pub extern "C" fn boxed_slice(p: Box<[u8]>) { } = note: box cannot be represented as a single pointer error: `extern` fn uses type `Box<str>`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:83:35 + --> $DIR/lint-fn.rs:83:35 | LL | pub extern "C" fn boxed_string(p: Box<str>) { } | ^^^^^^^^ not FFI-safe @@ -38,7 +38,7 @@ LL | pub extern "C" fn boxed_string(p: Box<str>) { } = note: box cannot be represented as a single pointer error: `extern` fn uses type `Box<dyn Trait>`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:86:34 + --> $DIR/lint-fn.rs:86:34 | LL | pub extern "C" fn boxed_trait(p: Box<dyn Trait>) { } | ^^^^^^^^^^^^^^ not FFI-safe @@ -46,7 +46,7 @@ LL | pub extern "C" fn boxed_trait(p: Box<dyn Trait>) { } = note: box cannot be represented as a single pointer error: `extern` fn uses type `char`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:89:32 + --> $DIR/lint-fn.rs:89:32 | LL | pub extern "C" fn char_type(p: char) { } | ^^^^ not FFI-safe @@ -55,7 +55,7 @@ LL | pub extern "C" fn char_type(p: char) { } = note: the `char` type has no C equivalent error: `extern` fn uses type `(i32, i32)`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:92:33 + --> $DIR/lint-fn.rs:92:33 | LL | pub extern "C" fn tuple_type(p: (i32, i32)) { } | ^^^^^^^^^^ not FFI-safe @@ -64,7 +64,7 @@ LL | pub extern "C" fn tuple_type(p: (i32, i32)) { } = note: tuples have unspecified layout error: `extern` fn uses type `(i32, i32)`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:95:34 + --> $DIR/lint-fn.rs:95:34 | LL | pub extern "C" fn tuple_type2(p: I32Pair) { } | ^^^^^^^ not FFI-safe @@ -73,7 +73,7 @@ LL | pub extern "C" fn tuple_type2(p: I32Pair) { } = note: tuples have unspecified layout error: `extern` fn uses type `ZeroSize`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:98:32 + --> $DIR/lint-fn.rs:98:32 | LL | pub extern "C" fn zero_size(p: ZeroSize) { } | ^^^^^^^^ not FFI-safe @@ -81,26 +81,26 @@ LL | pub extern "C" fn zero_size(p: ZeroSize) { } = help: consider adding a member to this struct = note: this struct has no fields note: the type is defined here - --> $DIR/lint-ctypes-fn.rs:25:1 + --> $DIR/lint-fn.rs:25:1 | LL | pub struct ZeroSize; | ^^^^^^^^^^^^^^^^^^^ error: `extern` fn uses type `ZeroSizeWithPhantomData`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:101:40 + --> $DIR/lint-fn.rs:101:40 | LL | pub extern "C" fn zero_size_phantom(p: ZeroSizeWithPhantomData) { } | ^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe | = note: composed only of `PhantomData` note: the type is defined here - --> $DIR/lint-ctypes-fn.rs:60:1 + --> $DIR/lint-fn.rs:60:1 | LL | pub struct ZeroSizeWithPhantomData(PhantomData<i32>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `extern` fn uses type `PhantomData<bool>`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:104:51 + --> $DIR/lint-fn.rs:104:51 | LL | pub extern "C" fn zero_size_phantom_toplevel() -> PhantomData<bool> { | ^^^^^^^^^^^^^^^^^ not FFI-safe @@ -108,7 +108,7 @@ LL | pub extern "C" fn zero_size_phantom_toplevel() -> PhantomData<bool> { = note: composed only of `PhantomData` error: `extern` fn uses type `fn()`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:109:30 + --> $DIR/lint-fn.rs:109:30 | LL | pub extern "C" fn fn_type(p: RustFn) { } | ^^^^^^ not FFI-safe @@ -117,7 +117,7 @@ LL | pub extern "C" fn fn_type(p: RustFn) { } = note: this function pointer has Rust-specific calling convention error: `extern` fn uses type `fn()`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:112:31 + --> $DIR/lint-fn.rs:112:31 | LL | pub extern "C" fn fn_type2(p: fn()) { } | ^^^^ not FFI-safe @@ -126,7 +126,7 @@ LL | pub extern "C" fn fn_type2(p: fn()) { } = note: this function pointer has Rust-specific calling convention error: `extern` fn uses type `str`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:117:38 + --> $DIR/lint-fn.rs:117:38 | LL | pub extern "C" fn transparent_str(p: TransparentStr) { } | ^^^^^^^^^^^^^^ not FFI-safe @@ -135,7 +135,7 @@ LL | pub extern "C" fn transparent_str(p: TransparentStr) { } = note: string slices have no C equivalent error: `extern` fn uses type `PhantomData<bool>`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:169:43 + --> $DIR/lint-fn.rs:169:43 | LL | pub extern "C" fn unused_generic2<T>() -> PhantomData<bool> { | ^^^^^^^^^^^^^^^^^ not FFI-safe @@ -143,7 +143,7 @@ LL | pub extern "C" fn unused_generic2<T>() -> PhantomData<bool> { = note: composed only of `PhantomData` error: `extern` fn uses type `Vec<T>`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:182:39 + --> $DIR/lint-fn.rs:182:39 | LL | pub extern "C" fn used_generic4<T>(x: Vec<T>) { } | ^^^^^^ not FFI-safe @@ -152,7 +152,7 @@ LL | pub extern "C" fn used_generic4<T>(x: Vec<T>) { } = note: this struct has unspecified layout error: `extern` fn uses type `Vec<T>`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:185:41 + --> $DIR/lint-fn.rs:185:41 | LL | pub extern "C" fn used_generic5<T>() -> Vec<T> { | ^^^^^^ not FFI-safe diff --git a/tests/ui/lint/lint-ctypes-non-recursion-limit.rs b/tests/ui/lint/improper-ctypes/lint-non-recursion-limit.rs index 61e95dc5a46..61e95dc5a46 100644 --- a/tests/ui/lint/lint-ctypes-non-recursion-limit.rs +++ b/tests/ui/lint/improper-ctypes/lint-non-recursion-limit.rs diff --git a/tests/ui/lint/lint-ctypes-option-nonnull-unsized.rs b/tests/ui/lint/improper-ctypes/lint-option-nonnull-unsized.rs index ca08eb23a57..ca08eb23a57 100644 --- a/tests/ui/lint/lint-ctypes-option-nonnull-unsized.rs +++ b/tests/ui/lint/improper-ctypes/lint-option-nonnull-unsized.rs diff --git a/tests/ui/lint/lint-ctypes-option-nonnull-unsized.stderr b/tests/ui/lint/improper-ctypes/lint-option-nonnull-unsized.stderr index 74630469416..b17fb6bd614 100644 --- a/tests/ui/lint/lint-ctypes-option-nonnull-unsized.stderr +++ b/tests/ui/lint/improper-ctypes/lint-option-nonnull-unsized.stderr @@ -1,5 +1,5 @@ error: `extern` fn uses type `Option<&T>`, which is not FFI-safe - --> $DIR/lint-ctypes-option-nonnull-unsized.rs:3:45 + --> $DIR/lint-option-nonnull-unsized.rs:3:45 | LL | extern "C" fn foo<T: ?Sized + 'static>() -> Option<&'static T> { | ^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -7,7 +7,7 @@ LL | extern "C" fn foo<T: ?Sized + 'static>() -> Option<&'static T> { = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum = note: enum has no representation hint note: the lint level is defined here - --> $DIR/lint-ctypes-option-nonnull-unsized.rs:1:9 + --> $DIR/lint-option-nonnull-unsized.rs:1:9 | LL | #![deny(improper_ctypes_definitions)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/lint/lint-ctypes-113436.rs b/tests/ui/lint/improper-ctypes/mustpass-113436.rs index d5acdc45f92..d5acdc45f92 100644 --- a/tests/ui/lint/lint-ctypes-113436.rs +++ b/tests/ui/lint/improper-ctypes/mustpass-113436.rs diff --git a/tests/ui/lint/lint-ctypes-113900.rs b/tests/ui/lint/improper-ctypes/mustpass-113900.rs index 3dd196a4094..3dd196a4094 100644 --- a/tests/ui/lint/lint-ctypes-113900.rs +++ b/tests/ui/lint/improper-ctypes/mustpass-113900.rs diff --git a/tests/ui/lint/improper_ctypes_definitions_ice_134060.rs b/tests/ui/lint/improper-ctypes/mustpass-134060.rs index b30be996736..b30be996736 100644 --- a/tests/ui/lint/improper_ctypes_definitions_ice_134060.rs +++ b/tests/ui/lint/improper-ctypes/mustpass-134060.rs diff --git a/tests/ui/lint/improper_ctypes_definitions_ice_134060.stderr b/tests/ui/lint/improper-ctypes/mustpass-134060.stderr index f6ac9a92cd5..791b2f73709 100644 --- a/tests/ui/lint/improper_ctypes_definitions_ice_134060.stderr +++ b/tests/ui/lint/improper-ctypes/mustpass-134060.stderr @@ -1,5 +1,5 @@ warning: `extern` fn uses type `()`, which is not FFI-safe - --> $DIR/improper_ctypes_definitions_ice_134060.rs:11:34 + --> $DIR/mustpass-134060.rs:11:34 | LL | extern "C" fn foo_(&self, _: ()) -> i64 { | ^^ not FFI-safe diff --git a/tests/ui/lint/lint-ctypes-66202.rs b/tests/ui/lint/improper-ctypes/mustpass-66202.rs index e4cfa54c8d8..e4cfa54c8d8 100644 --- a/tests/ui/lint/lint-ctypes-66202.rs +++ b/tests/ui/lint/improper-ctypes/mustpass-66202.rs diff --git a/tests/ui/lint/lint-ctypes-73249-1.rs b/tests/ui/lint/improper-ctypes/mustpass-73249-1.rs index 0ca91ef294f..0ca91ef294f 100644 --- a/tests/ui/lint/lint-ctypes-73249-1.rs +++ b/tests/ui/lint/improper-ctypes/mustpass-73249-1.rs diff --git a/tests/ui/lint/lint-ctypes-73249-4.rs b/tests/ui/lint/improper-ctypes/mustpass-73249-4.rs index 37099c1313a..37099c1313a 100644 --- a/tests/ui/lint/lint-ctypes-73249-4.rs +++ b/tests/ui/lint/improper-ctypes/mustpass-73249-4.rs diff --git a/tests/ui/lint/lint-ctypes-73249.rs b/tests/ui/lint/improper-ctypes/mustpass-73249.rs index c5f2318ef0a..c5f2318ef0a 100644 --- a/tests/ui/lint/lint-ctypes-73249.rs +++ b/tests/ui/lint/improper-ctypes/mustpass-73249.rs diff --git a/tests/ui/lint/lint-ctypes-73251.rs b/tests/ui/lint/improper-ctypes/mustpass-73251.rs index 15c1dfcaabf..15c1dfcaabf 100644 --- a/tests/ui/lint/lint-ctypes-73251.rs +++ b/tests/ui/lint/improper-ctypes/mustpass-73251.rs diff --git a/tests/ui/lint/lint-ctypes-73747.rs b/tests/ui/lint/improper-ctypes/mustpass-73747.rs index a2562e3b421..a2562e3b421 100644 --- a/tests/ui/lint/lint-ctypes-73747.rs +++ b/tests/ui/lint/improper-ctypes/mustpass-73747.rs diff --git a/tests/ui/lint/improper_ctypes/repr-rust-is-undefined.rs b/tests/ui/lint/improper-ctypes/repr-rust-is-undefined.rs index 379c4132404..379c4132404 100644 --- a/tests/ui/lint/improper_ctypes/repr-rust-is-undefined.rs +++ b/tests/ui/lint/improper-ctypes/repr-rust-is-undefined.rs diff --git a/tests/ui/lint/improper_ctypes/repr-rust-is-undefined.stderr b/tests/ui/lint/improper-ctypes/repr-rust-is-undefined.stderr index 5f0465bcf00..5f0465bcf00 100644 --- a/tests/ui/lint/improper_ctypes/repr-rust-is-undefined.stderr +++ b/tests/ui/lint/improper-ctypes/repr-rust-is-undefined.stderr diff --git a/tests/ui/lint/non-snake-case/lint-uppercase-variables.stderr b/tests/ui/lint/non-snake-case/lint-uppercase-variables.stderr index b0c56003957..e5f2e65fd91 100644 --- a/tests/ui/lint/non-snake-case/lint-uppercase-variables.stderr +++ b/tests/ui/lint/non-snake-case/lint-uppercase-variables.stderr @@ -16,7 +16,7 @@ warning: unused variable: `Foo` --> $DIR/lint-uppercase-variables.rs:22:9 | LL | Foo => {} - | ^^^ help: if this is intentional, prefix it with an underscore: `_Foo` + | ^^^ | note: the lint level is defined here --> $DIR/lint-uppercase-variables.rs:1:9 @@ -24,12 +24,29 @@ note: the lint level is defined here LL | #![warn(unused)] | ^^^^^^ = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` +help: if this is intentional, prefix it with an underscore + | +LL | _Foo => {} + | + +help: you might have meant to pattern match on the similarly named variant `Foo` + | +LL | foo::Foo::Foo => {} + | ++++++++++ warning: unused variable: `Foo` --> $DIR/lint-uppercase-variables.rs:28:9 | LL | let Foo = foo::Foo::Foo; - | ^^^ help: if this is intentional, prefix it with an underscore: `_Foo` + | ^^^ + | +help: if this is intentional, prefix it with an underscore + | +LL | let _Foo = foo::Foo::Foo; + | + +help: you might have meant to pattern match on the similarly named variant `Foo` + | +LL | let foo::Foo::Foo = foo::Foo::Foo; + | ++++++++++ error[E0170]: pattern binding `Foo` is named the same as one of the variants of the type `foo::Foo` --> $DIR/lint-uppercase-variables.rs:33:17 @@ -41,7 +58,16 @@ warning: unused variable: `Foo` --> $DIR/lint-uppercase-variables.rs:33:17 | LL | fn in_param(Foo: foo::Foo) {} - | ^^^ help: if this is intentional, prefix it with an underscore: `_Foo` + | ^^^ + | +help: if this is intentional, prefix it with an underscore + | +LL | fn in_param(_Foo: foo::Foo) {} + | + +help: you might have meant to pattern match on the similarly named variant `Foo` + | +LL | fn in_param(foo::Foo::Foo: foo::Foo) {} + | ++++++++++ error: structure field `X` should have a snake case name --> $DIR/lint-uppercase-variables.rs:10:5 diff --git a/tests/ui/lto/issue-100772.rs b/tests/ui/lto/issue-100772.rs index 29ec5b9bf96..9468e20894a 100644 --- a/tests/ui/lto/issue-100772.rs +++ b/tests/ui/lto/issue-100772.rs @@ -1,6 +1,6 @@ //@ build-pass //@ needs-sanitizer-cfi -//@ compile-flags: -Ccodegen-units=1 -Clto -Ctarget-feature=-crt-static -Zsanitizer=cfi +//@ compile-flags: -Ccodegen-units=1 -Clto -Ctarget-feature=-crt-static -Zsanitizer=cfi -C unsafe-allow-abi-mismatch=sanitizer //@ no-prefer-dynamic //@ only-x86_64-unknown-linux-gnu diff --git a/tests/ui/lto/lto-global-allocator.rs b/tests/ui/lto/lto-global-allocator.rs new file mode 100644 index 00000000000..03f11709c90 --- /dev/null +++ b/tests/ui/lto/lto-global-allocator.rs @@ -0,0 +1,19 @@ +//@ compile-flags: --crate-type cdylib -C lto +//@ build-pass +//@ no-prefer-dynamic +//@ needs-crate-type: cdylib + +use std::alloc::{GlobalAlloc, Layout}; + +struct MyAllocator; + +unsafe impl GlobalAlloc for MyAllocator { + unsafe fn alloc(&self, _layout: Layout) -> *mut u8 { + todo!() + } + + unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {} +} + +#[global_allocator] +static GLOBAL: MyAllocator = MyAllocator; diff --git a/tests/ui/macros/macro-metavar-expr-concat/in-repetition.rs b/tests/ui/macros/macro-metavar-expr-concat/in-repetition.rs new file mode 100644 index 00000000000..d2bd31b06d6 --- /dev/null +++ b/tests/ui/macros/macro-metavar-expr-concat/in-repetition.rs @@ -0,0 +1,21 @@ +// issue: <https://github.com/rust-lang/rust/issues/140479> +// Ensure a proper compiler error, instead of an ICE occurs. +// FIXME(macro_metavar_expr_concat): this error message could be improved +#![feature(macro_metavar_expr_concat)] + +macro_rules! InRepetition { + ( + $( + $($arg:ident),+ + )+ + ) => { + $( + $( + ${concat(_, $arg)} //~ ERROR nested repetitions with `${concat(...)}` metavariable expressions are not yet supported + )* + )* + }; +} +InRepetition!(other); + +fn main() {} diff --git a/tests/ui/macros/macro-metavar-expr-concat/in-repetition.stderr b/tests/ui/macros/macro-metavar-expr-concat/in-repetition.stderr new file mode 100644 index 00000000000..ec39ca799e1 --- /dev/null +++ b/tests/ui/macros/macro-metavar-expr-concat/in-repetition.stderr @@ -0,0 +1,8 @@ +error: nested repetitions with `${concat(...)}` metavariable expressions are not yet supported + --> $DIR/in-repetition.rs:14:30 + | +LL | ${concat(_, $arg)} + | ^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/mir/issue-83499-input-output-iteration-ice.rs b/tests/ui/mir/issue-83499-input-output-iteration-ice.rs index 78e5c961802..9277994d9b3 100644 --- a/tests/ui/mir/issue-83499-input-output-iteration-ice.rs +++ b/tests/ui/mir/issue-83499-input-output-iteration-ice.rs @@ -5,6 +5,6 @@ fn main() {} fn foo(_: Bar, ...) -> impl {} -//~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg +//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention //~| ERROR cannot find type `Bar` in this scope //~| ERROR at least one trait must be specified diff --git a/tests/ui/mir/issue-83499-input-output-iteration-ice.stderr b/tests/ui/mir/issue-83499-input-output-iteration-ice.stderr index 80a8a94aea4..4a1aa49eb6e 100644 --- a/tests/ui/mir/issue-83499-input-output-iteration-ice.stderr +++ b/tests/ui/mir/issue-83499-input-output-iteration-ice.stderr @@ -1,4 +1,4 @@ -error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg +error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention --> $DIR/issue-83499-input-output-iteration-ice.rs:7:16 | LL | fn foo(_: Bar, ...) -> impl {} diff --git a/tests/ui/or-patterns/binding-typo-2.rs b/tests/ui/or-patterns/binding-typo-2.rs new file mode 100644 index 00000000000..275b68a22e2 --- /dev/null +++ b/tests/ui/or-patterns/binding-typo-2.rs @@ -0,0 +1,122 @@ +// Issue #51976 +#![deny(unused_variables)] //~ NOTE: the lint level is defined here +enum Lol { + Foo, + Bar, +} +const Bat: () = (); +const Battery: () = (); +struct Bay; + +fn foo(x: (Lol, Lol)) { + use Lol::*; + match &x { + (Foo, Bar) | (Ban, Foo) => {} + //~^ ERROR: variable `Ban` is not bound in all patterns + //~| HELP: you might have meant to use the similarly named previously used binding `Bar` + //~| NOTE: pattern doesn't bind `Ban` + //~| NOTE: variable not in all patterns + //~| ERROR: variable `Ban` is assigned to, but never used + //~| NOTE: consider using `_Ban` instead + //~| HELP: you might have meant to pattern match on the similarly named + _ => {} + } + match &x { + (Foo, _) | (Ban, Foo) => {} + //~^ ERROR: variable `Ban` is not bound in all patterns + //~| HELP: you might have meant to use the similarly named unit variant `Bar` + //~| NOTE: pattern doesn't bind `Ban` + //~| NOTE: variable not in all patterns + //~| ERROR: variable `Ban` is assigned to, but never used + //~| NOTE: consider using `_Ban` instead + //~| HELP: you might have meant to pattern match on the similarly named + _ => {} + } + match Some(42) { + Some(_) => {} + Non => {} + //~^ ERROR: unused variable: `Non` + //~| HELP: if this is intentional, prefix it with an underscore + //~| HELP: you might have meant to pattern match on the similarly named + } + match Some(42) { + Some(_) => {} + Non | None => {} + //~^ ERROR: unused variable: `Non` + //~| HELP: if this is intentional, prefix it with an underscore + //~| ERROR: variable `Non` is not bound in all patterns [E0408] + //~| NOTE: pattern doesn't bind `Non` + //~| NOTE: variable not in all patterns + //~| HELP: you might have meant to use the similarly named unit variant `None` + //~| HELP: you might have meant to pattern match on the similarly named + } + match Some(42) { + Non | Some(_) => {} + //~^ ERROR: unused variable: `Non` + //~| HELP: if this is intentional, prefix it with an underscore + //~| ERROR: variable `Non` is not bound in all patterns [E0408] + //~| NOTE: pattern doesn't bind `Non` + //~| NOTE: variable not in all patterns + //~| HELP: you might have meant to use the similarly named unit variant `None` + //~| HELP: you might have meant to pattern match on the similarly named + } +} +fn bar(x: (Lol, Lol)) { + use Lol::*; + use ::Bat; + use ::Bay; + match &x { + (Foo, _) | (Ban, Foo) => {} + //~^ ERROR: variable `Ban` is not bound in all patterns + //~| HELP: you might have meant to use the similarly named unit variant `Bar` + //~| HELP: you might have meant to use the similarly named unit struct `Bay` + //~| HELP: you might have meant to use the similarly named constant `Bat` + //~| NOTE: pattern doesn't bind `Ban` + //~| NOTE: variable not in all patterns + //~| ERROR: variable `Ban` is assigned to, but never used + //~| NOTE: consider using `_Ban` instead + //~| HELP: you might have meant to pattern match on the similarly named + _ => {} + } +} +fn baz(x: (Lol, Lol)) { + use Lol::*; + use Bat; + match &x { + (Foo, _) | (Ban, Foo) => {} + //~^ ERROR: variable `Ban` is not bound in all patterns + //~| HELP: you might have meant to use the similarly named unit variant `Bar` + //~| HELP: you might have meant to use the similarly named constant `Bat` + //~| NOTE: pattern doesn't bind `Ban` + //~| NOTE: variable not in all patterns + //~| ERROR: variable `Ban` is assigned to, but never used + //~| NOTE: consider using `_Ban` instead + //~| HELP: you might have meant to pattern match on the similarly named + _ => {} + } + match &x { + (Ban, _) => {} + //~^ ERROR: unused variable: `Ban` + //~| HELP: if this is intentional, prefix it with an underscore + //~| HELP: you might have meant to pattern match on the similarly named + } + match Bay { + Ban => {} + //~^ ERROR: unused variable: `Ban` + //~| HELP: if this is intentional, prefix it with an underscore + //~| HELP: you might have meant to pattern match on the similarly named + } + match () { + Batery => {} + //~^ ERROR: unused variable: `Batery` + //~| HELP: if this is intentional, prefix it with an underscore + //~| HELP: you might have meant to pattern match on the similarly named constant + } +} + +fn main() { + use Lol::*; + foo((Foo, Bar)); + bar((Foo, Bar)); + baz((Foo, Bar)); +} diff --git a/tests/ui/or-patterns/binding-typo-2.stderr b/tests/ui/or-patterns/binding-typo-2.stderr new file mode 100644 index 00000000000..81ea6e6b1bc --- /dev/null +++ b/tests/ui/or-patterns/binding-typo-2.stderr @@ -0,0 +1,254 @@ +error[E0408]: variable `Ban` is not bound in all patterns + --> $DIR/binding-typo-2.rs:14:9 + | +LL | (Foo, Bar) | (Ban, Foo) => {} + | ^^^^^^^^^^ --- variable not in all patterns + | | + | pattern doesn't bind `Ban` + | +help: you might have meant to use the similarly named previously used binding `Bar` + | +LL - (Foo, Bar) | (Ban, Foo) => {} +LL + (Foo, Bar) | (Bar, Foo) => {} + | + +error[E0408]: variable `Ban` is not bound in all patterns + --> $DIR/binding-typo-2.rs:25:9 + | +LL | (Foo, _) | (Ban, Foo) => {} + | ^^^^^^^^ --- variable not in all patterns + | | + | pattern doesn't bind `Ban` + | +help: you might have meant to use the similarly named unit variant `Bar` + | +LL - (Foo, _) | (Ban, Foo) => {} +LL + (Foo, _) | (Bar, Foo) => {} + | + +error[E0408]: variable `Non` is not bound in all patterns + --> $DIR/binding-typo-2.rs:44:15 + | +LL | Non | None => {} + | --- ^^^^ pattern doesn't bind `Non` + | | + | variable not in all patterns + | +help: you might have meant to use the similarly named unit variant `None` + | +LL - Non | None => {} +LL + core::option::Option::None | None => {} + | + +error[E0408]: variable `Non` is not bound in all patterns + --> $DIR/binding-typo-2.rs:54:15 + | +LL | Non | Some(_) => {} + | --- ^^^^^^^ pattern doesn't bind `Non` + | | + | variable not in all patterns + | +help: you might have meant to use the similarly named unit variant `None` + | +LL - Non | Some(_) => {} +LL + core::option::Option::None | Some(_) => {} + | + +error[E0408]: variable `Ban` is not bound in all patterns + --> $DIR/binding-typo-2.rs:69:9 + | +LL | (Foo, _) | (Ban, Foo) => {} + | ^^^^^^^^ --- variable not in all patterns + | | + | pattern doesn't bind `Ban` + | +help: you might have meant to use the similarly named unit variant `Bar` + | +LL - (Foo, _) | (Ban, Foo) => {} +LL + (Foo, _) | (Bar, Foo) => {} + | +help: you might have meant to use the similarly named unit struct `Bay` + | +LL - (Foo, _) | (Ban, Foo) => {} +LL + (Foo, _) | (Bay, Foo) => {} + | +help: you might have meant to use the similarly named constant `Bat` + | +LL - (Foo, _) | (Ban, Foo) => {} +LL + (Foo, _) | (Bat, Foo) => {} + | + +error[E0408]: variable `Ban` is not bound in all patterns + --> $DIR/binding-typo-2.rs:86:9 + | +LL | (Foo, _) | (Ban, Foo) => {} + | ^^^^^^^^ --- variable not in all patterns + | | + | pattern doesn't bind `Ban` + | +help: you might have meant to use the similarly named unit variant `Bar` + | +LL - (Foo, _) | (Ban, Foo) => {} +LL + (Foo, _) | (Bar, Foo) => {} + | +help: you might have meant to use the similarly named constant `Bat` + | +LL - (Foo, _) | (Ban, Foo) => {} +LL + (Foo, _) | (Bat, Foo) => {} + | + +error: variable `Ban` is assigned to, but never used + --> $DIR/binding-typo-2.rs:14:23 + | +LL | (Foo, Bar) | (Ban, Foo) => {} + | ^^^ + | + = note: consider using `_Ban` instead +note: the lint level is defined here + --> $DIR/binding-typo-2.rs:2:9 + | +LL | #![deny(unused_variables)] + | ^^^^^^^^^^^^^^^^ +help: you might have meant to pattern match on the similarly named variant `Bar` + | +LL - (Foo, Bar) | (Ban, Foo) => {} +LL + (Foo, Bar) | (Lol::Bar, Foo) => {} + | + +error: variable `Ban` is assigned to, but never used + --> $DIR/binding-typo-2.rs:25:21 + | +LL | (Foo, _) | (Ban, Foo) => {} + | ^^^ + | + = note: consider using `_Ban` instead +help: you might have meant to pattern match on the similarly named variant `Bar` + | +LL - (Foo, _) | (Ban, Foo) => {} +LL + (Foo, _) | (Lol::Bar, Foo) => {} + | + +error: unused variable: `Non` + --> $DIR/binding-typo-2.rs:37:9 + | +LL | Non => {} + | ^^^ + | +help: if this is intentional, prefix it with an underscore + | +LL | _Non => {} + | + +help: you might have meant to pattern match on the similarly named variant `None` + | +LL - Non => {} +LL + std::prelude::v1::None => {} + | + +error: unused variable: `Non` + --> $DIR/binding-typo-2.rs:44:9 + | +LL | Non | None => {} + | ^^^ + | +help: if this is intentional, prefix it with an underscore + | +LL | _Non | None => {} + | + +help: you might have meant to pattern match on the similarly named variant `None` + | +LL - Non | None => {} +LL + std::prelude::v1::None | None => {} + | + +error: unused variable: `Non` + --> $DIR/binding-typo-2.rs:54:9 + | +LL | Non | Some(_) => {} + | ^^^ + | +help: if this is intentional, prefix it with an underscore + | +LL | _Non | Some(_) => {} + | + +help: you might have meant to pattern match on the similarly named variant `None` + | +LL - Non | Some(_) => {} +LL + std::prelude::v1::None | Some(_) => {} + | + +error: variable `Ban` is assigned to, but never used + --> $DIR/binding-typo-2.rs:69:21 + | +LL | (Foo, _) | (Ban, Foo) => {} + | ^^^ + | + = note: consider using `_Ban` instead +help: you might have meant to pattern match on the similarly named variant `Bar` + | +LL - (Foo, _) | (Ban, Foo) => {} +LL + (Foo, _) | (Lol::Bar, Foo) => {} + | + +error: variable `Ban` is assigned to, but never used + --> $DIR/binding-typo-2.rs:86:21 + | +LL | (Foo, _) | (Ban, Foo) => {} + | ^^^ + | + = note: consider using `_Ban` instead +help: you might have meant to pattern match on the similarly named variant `Bar` + | +LL - (Foo, _) | (Ban, Foo) => {} +LL + (Foo, _) | (Lol::Bar, Foo) => {} + | + +error: unused variable: `Ban` + --> $DIR/binding-typo-2.rs:98:10 + | +LL | (Ban, _) => {} + | ^^^ + | +help: if this is intentional, prefix it with an underscore + | +LL | (_Ban, _) => {} + | + +help: you might have meant to pattern match on the similarly named variant `Bar` + | +LL - (Ban, _) => {} +LL + (Lol::Bar, _) => {} + | + +error: unused variable: `Ban` + --> $DIR/binding-typo-2.rs:104:9 + | +LL | Ban => {} + | ^^^ + | +help: if this is intentional, prefix it with an underscore + | +LL | _Ban => {} + | + +help: you might have meant to pattern match on the similarly named struct `Bay` + | +LL - Ban => {} +LL + Bay => {} + | + +error: unused variable: `Batery` + --> $DIR/binding-typo-2.rs:110:9 + | +LL | Batery => {} + | ^^^^^^ + | +help: if this is intentional, prefix it with an underscore + | +LL | _Batery => {} + | + +help: you might have meant to pattern match on the similarly named constant `Battery` + | +LL | Battery => {} + | + + +error: aborting due to 16 previous errors + +For more information about this error, try `rustc --explain E0408`. diff --git a/tests/ui/or-patterns/binding-typo.fixed b/tests/ui/or-patterns/binding-typo.fixed new file mode 100644 index 00000000000..f209ad644db --- /dev/null +++ b/tests/ui/or-patterns/binding-typo.fixed @@ -0,0 +1,32 @@ +// Issue #51976 +//@ run-rustfix +#![allow(unused_variables)] // allowed so we don't get overlapping suggestions +enum Lol { + Foo, + Bar, +} + +fn foo(x: (Lol, Lol)) { + use Lol::*; + match &x { + (Foo, Bar) | (Bar, Foo) => {} + //~^ ERROR: variable `Ban` is not bound in all patterns + //~| HELP: you might have meant to use the similarly named previously used binding `Bar` + //~| NOTE: pattern doesn't bind `Ban` + //~| NOTE: variable not in all patterns + _ => {} + } + match &x { + (Foo, _) | (Bar, Foo) => {} + //~^ ERROR: variable `Ban` is not bound in all patterns + //~| HELP: you might have meant to use the similarly named unit variant `Bar` + //~| NOTE: pattern doesn't bind `Ban` + //~| NOTE: variable not in all patterns + _ => {} + } +} + +fn main() { + use Lol::*; + foo((Foo, Bar)); +} diff --git a/tests/ui/or-patterns/binding-typo.rs b/tests/ui/or-patterns/binding-typo.rs new file mode 100644 index 00000000000..6be9b801a0d --- /dev/null +++ b/tests/ui/or-patterns/binding-typo.rs @@ -0,0 +1,32 @@ +// Issue #51976 +//@ run-rustfix +#![allow(unused_variables)] // allowed so we don't get overlapping suggestions +enum Lol { + Foo, + Bar, +} + +fn foo(x: (Lol, Lol)) { + use Lol::*; + match &x { + (Foo, Bar) | (Ban, Foo) => {} + //~^ ERROR: variable `Ban` is not bound in all patterns + //~| HELP: you might have meant to use the similarly named previously used binding `Bar` + //~| NOTE: pattern doesn't bind `Ban` + //~| NOTE: variable not in all patterns + _ => {} + } + match &x { + (Foo, _) | (Ban, Foo) => {} + //~^ ERROR: variable `Ban` is not bound in all patterns + //~| HELP: you might have meant to use the similarly named unit variant `Bar` + //~| NOTE: pattern doesn't bind `Ban` + //~| NOTE: variable not in all patterns + _ => {} + } +} + +fn main() { + use Lol::*; + foo((Foo, Bar)); +} diff --git a/tests/ui/or-patterns/binding-typo.stderr b/tests/ui/or-patterns/binding-typo.stderr new file mode 100644 index 00000000000..fb6d5f71209 --- /dev/null +++ b/tests/ui/or-patterns/binding-typo.stderr @@ -0,0 +1,31 @@ +error[E0408]: variable `Ban` is not bound in all patterns + --> $DIR/binding-typo.rs:12:9 + | +LL | (Foo, Bar) | (Ban, Foo) => {} + | ^^^^^^^^^^ --- variable not in all patterns + | | + | pattern doesn't bind `Ban` + | +help: you might have meant to use the similarly named previously used binding `Bar` + | +LL - (Foo, Bar) | (Ban, Foo) => {} +LL + (Foo, Bar) | (Bar, Foo) => {} + | + +error[E0408]: variable `Ban` is not bound in all patterns + --> $DIR/binding-typo.rs:20:9 + | +LL | (Foo, _) | (Ban, Foo) => {} + | ^^^^^^^^ --- variable not in all patterns + | | + | pattern doesn't bind `Ban` + | +help: you might have meant to use the similarly named unit variant `Bar` + | +LL - (Foo, _) | (Ban, Foo) => {} +LL + (Foo, _) | (Bar, Foo) => {} + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0408`. diff --git a/tests/ui/or-patterns/missing-bindings.stderr b/tests/ui/or-patterns/missing-bindings.stderr index 6288cc58913..636f7016647 100644 --- a/tests/ui/or-patterns/missing-bindings.stderr +++ b/tests/ui/or-patterns/missing-bindings.stderr @@ -86,6 +86,12 @@ LL | let (A(A(a, b) | B(c), d) | B(e)) = Y; | ^^^^^^^ - variable not in all patterns | | | pattern doesn't bind `c` + | +help: you might have meant to use the similarly named previously used binding `a` + | +LL - let (A(A(a, b) | B(c), d) | B(e)) = Y; +LL + let (A(A(a, b) | B(a), d) | B(e)) = Y; + | error[E0408]: variable `a` is not bound in all patterns --> $DIR/missing-bindings.rs:47:22 @@ -94,6 +100,12 @@ LL | let (A(A(a, b) | B(c), d) | B(e)) = Y; | - ^^^^ pattern doesn't bind `a` | | | variable not in all patterns + | +help: you might have meant to use the similarly named previously used binding `c` + | +LL - let (A(A(a, b) | B(c), d) | B(e)) = Y; +LL + let (A(A(c, b) | B(c), d) | B(e)) = Y; + | error[E0408]: variable `b` is not bound in all patterns --> $DIR/missing-bindings.rs:47:22 @@ -102,6 +114,12 @@ LL | let (A(A(a, b) | B(c), d) | B(e)) = Y; | - ^^^^ pattern doesn't bind `b` | | | variable not in all patterns + | +help: you might have meant to use the similarly named previously used binding `c` + | +LL - let (A(A(a, b) | B(c), d) | B(e)) = Y; +LL + let (A(A(a, c) | B(c), d) | B(e)) = Y; + | error[E0408]: variable `e` is not bound in all patterns --> $DIR/missing-bindings.rs:47:10 @@ -110,6 +128,12 @@ LL | let (A(A(a, b) | B(c), d) | B(e)) = Y; | ^^^^^^^^^^^^^^^^^^^^ - variable not in all patterns | | | pattern doesn't bind `e` + | +help: you might have meant to use the similarly named previously used binding `c` + | +LL - let (A(A(a, b) | B(c), d) | B(e)) = Y; +LL + let (A(A(a, b) | B(c), d) | B(c)) = Y; + | error[E0408]: variable `a` is not bound in all patterns --> $DIR/missing-bindings.rs:47:33 @@ -118,6 +142,12 @@ LL | let (A(A(a, b) | B(c), d) | B(e)) = Y; | - ^^^^ pattern doesn't bind `a` | | | variable not in all patterns + | +help: you might have meant to use the similarly named previously used binding `e` + | +LL - let (A(A(a, b) | B(c), d) | B(e)) = Y; +LL + let (A(A(e, b) | B(c), d) | B(e)) = Y; + | error[E0408]: variable `b` is not bound in all patterns --> $DIR/missing-bindings.rs:47:33 @@ -126,6 +156,12 @@ LL | let (A(A(a, b) | B(c), d) | B(e)) = Y; | - ^^^^ pattern doesn't bind `b` | | | variable not in all patterns + | +help: you might have meant to use the similarly named previously used binding `e` + | +LL - let (A(A(a, b) | B(c), d) | B(e)) = Y; +LL + let (A(A(a, e) | B(c), d) | B(e)) = Y; + | error[E0408]: variable `c` is not bound in all patterns --> $DIR/missing-bindings.rs:47:33 @@ -134,6 +170,12 @@ LL | let (A(A(a, b) | B(c), d) | B(e)) = Y; | - ^^^^ pattern doesn't bind `c` | | | variable not in all patterns + | +help: you might have meant to use the similarly named previously used binding `e` + | +LL - let (A(A(a, b) | B(c), d) | B(e)) = Y; +LL + let (A(A(a, b) | B(e), d) | B(e)) = Y; + | error[E0408]: variable `d` is not bound in all patterns --> $DIR/missing-bindings.rs:47:33 @@ -142,6 +184,12 @@ LL | let (A(A(a, b) | B(c), d) | B(e)) = Y; | - ^^^^ pattern doesn't bind `d` | | | variable not in all patterns + | +help: you might have meant to use the similarly named previously used binding `e` + | +LL - let (A(A(a, b) | B(c), d) | B(e)) = Y; +LL + let (A(A(a, b) | B(c), e) | B(e)) = Y; + | error[E0408]: variable `a` is not bound in all patterns --> $DIR/missing-bindings.rs:63:29 @@ -158,6 +206,12 @@ LL | A(_, a) | | ^^^^^^^ pattern doesn't bind `b` LL | B(b), | - variable not in all patterns + | +help: you might have meant to use the similarly named previously used binding `a` + | +LL - B(b), +LL + B(a), + | error[E0408]: variable `a` is not bound in all patterns --> $DIR/missing-bindings.rs:71:21 @@ -166,6 +220,12 @@ LL | A(_, a) | | - variable not in all patterns LL | B(b), | ^^^^ pattern doesn't bind `a` + | +help: you might have meant to use the similarly named previously used binding `b` + | +LL - A(_, a) | +LL + A(_, b) | + | error[E0408]: variable `a` is not bound in all patterns --> $DIR/missing-bindings.rs:74:17 @@ -202,6 +262,12 @@ LL | B(b), ... LL | V3(c), | ^^^^^ pattern doesn't bind `b` + | +help: you might have meant to use the similarly named previously used binding `c` + | +LL - B(b), +LL + B(c), + | error[E0408]: variable `c` is not bound in all patterns --> $DIR/missing-bindings.rs:59:13 @@ -223,6 +289,12 @@ LL | | ) | | |_____________^ pattern doesn't bind `c` LL | V3(c), | - variable not in all patterns + | +help: you might have meant to use the similarly named previously used binding `a` + | +LL - V3(c), +LL + V3(a), + | error[E0408]: variable `a` is not bound in all patterns --> $DIR/missing-bindings.rs:78:13 @@ -235,6 +307,15 @@ LL | A(_, a) | ... LL | V3(c), | ^^^^^ pattern doesn't bind `a` + | +help: you might have meant to use the similarly named previously used binding `c` + | +LL ~ B(Ok(a) | Err(c)) +LL | ) | +LL | V2( +LL | A( +LL ~ A(_, c) | + | error[E0170]: pattern binding `beta` is named the same as one of the variants of the type `check_handling_of_paths::bar::foo` --> $DIR/missing-bindings.rs:19:18 diff --git a/tests/ui/parser/expr-as-stmt-2.rs b/tests/ui/parser/expr-as-stmt-2.rs index 3a18bdc3b73..4b1f62d8056 100644 --- a/tests/ui/parser/expr-as-stmt-2.rs +++ b/tests/ui/parser/expr-as-stmt-2.rs @@ -7,4 +7,25 @@ fn foo(a: Option<u32>, b: Option<u32>) -> bool { if let Some(y) = a { true } else { false } } -fn main() {} +fn bar() -> bool { + false +} + +fn main() { + if true { true } else { false } && true; + //~^ ERROR mismatched types + //~| ERROR mismatched types + if true { true } else { false } && if true { true } else { false }; + //~^ ERROR mismatched types + //~| ERROR mismatched types + if true { true } else { false } if true { true } else { false }; + //~^ ERROR mismatched types + //~| ERROR mismatched types + if true { bar() } else { bar() } && if true { bar() } else { bar() }; + //~^ ERROR mismatched types + //~| ERROR mismatched types + if true { bar() } else { bar() } if true { bar() } else { bar() }; + //~^ ERROR mismatched types + //~| ERROR mismatched types + let _ = if true { true } else { false } && true; // ok +} diff --git a/tests/ui/parser/expr-as-stmt-2.stderr b/tests/ui/parser/expr-as-stmt-2.stderr index 2b6314c38ce..f9838ee0299 100644 --- a/tests/ui/parser/expr-as-stmt-2.stderr +++ b/tests/ui/parser/expr-as-stmt-2.stderr @@ -7,6 +7,10 @@ LL | if let Some(x) = a { true } else { false } | | expected `()`, found `bool` | expected this to be `()` | +help: parentheses are required to parse this as an expression + | +LL | (if let Some(x) = a { true } else { false }) + | + + help: you might have meant to return this value | LL | if let Some(x) = a { return true; } else { false } @@ -21,6 +25,10 @@ LL | if let Some(x) = a { true } else { false } | | expected `()`, found `bool` | expected this to be `()` | +help: parentheses are required to parse this as an expression + | +LL | (if let Some(x) = a { true } else { false }) + | + + help: you might have meant to return this value | LL | if let Some(x) = a { true } else { return false; } @@ -41,6 +49,152 @@ help: parentheses are required to parse this as an expression LL | (if let Some(x) = a { true } else { false }) | + + -error: aborting due to 3 previous errors +error[E0308]: mismatched types + --> $DIR/expr-as-stmt-2.rs:15:15 + | +LL | if true { true } else { false } && true; + | ----------^^^^----------------- + | | | + | | expected `()`, found `bool` + | expected this to be `()` + | +help: parentheses are required to parse this as an expression + | +LL | (if true { true } else { false }) && true; + | + + + +error[E0308]: mismatched types + --> $DIR/expr-as-stmt-2.rs:15:29 + | +LL | if true { true } else { false } && true; + | ------------------------^^^^^-- + | | | + | | expected `()`, found `bool` + | expected this to be `()` + | +help: parentheses are required to parse this as an expression + | +LL | (if true { true } else { false }) && true; + | + + + +error[E0308]: mismatched types + --> $DIR/expr-as-stmt-2.rs:18:15 + | +LL | if true { true } else { false } && if true { true } else { false }; + | ----------^^^^----------------- + | | | + | | expected `()`, found `bool` + | expected this to be `()` + | +help: parentheses are required to parse this as an expression + | +LL | (if true { true } else { false }) && if true { true } else { false }; + | + + + +error[E0308]: mismatched types + --> $DIR/expr-as-stmt-2.rs:18:29 + | +LL | if true { true } else { false } && if true { true } else { false }; + | ------------------------^^^^^-- + | | | + | | expected `()`, found `bool` + | expected this to be `()` + | +help: parentheses are required to parse this as an expression + | +LL | (if true { true } else { false }) && if true { true } else { false }; + | + + + +error[E0308]: mismatched types + --> $DIR/expr-as-stmt-2.rs:21:15 + | +LL | if true { true } else { false } if true { true } else { false }; + | ----------^^^^----------------- + | | | + | | expected `()`, found `bool` + | expected this to be `()` + +error[E0308]: mismatched types + --> $DIR/expr-as-stmt-2.rs:21:29 + | +LL | if true { true } else { false } if true { true } else { false }; + | ------------------------^^^^^-- + | | | + | | expected `()`, found `bool` + | expected this to be `()` + +error[E0308]: mismatched types + --> $DIR/expr-as-stmt-2.rs:24:15 + | +LL | if true { bar() } else { bar() } && if true { bar() } else { bar() }; + | ----------^^^^^----------------- + | | | + | | expected `()`, found `bool` + | expected this to be `()` + | +help: parentheses are required to parse this as an expression + | +LL | (if true { bar() } else { bar() }) && if true { bar() } else { bar() }; + | + + +help: consider using a semicolon here + | +LL | if true { bar() } else { bar() }; && if true { bar() } else { bar() }; + | + + +error[E0308]: mismatched types + --> $DIR/expr-as-stmt-2.rs:24:30 + | +LL | if true { bar() } else { bar() } && if true { bar() } else { bar() }; + | -------------------------^^^^^-- + | | | + | | expected `()`, found `bool` + | expected this to be `()` + | +help: parentheses are required to parse this as an expression + | +LL | (if true { bar() } else { bar() }) && if true { bar() } else { bar() }; + | + + +help: consider using a semicolon here + | +LL | if true { bar() } else { bar() }; && if true { bar() } else { bar() }; + | + + +error[E0308]: mismatched types + --> $DIR/expr-as-stmt-2.rs:27:15 + | +LL | if true { bar() } else { bar() } if true { bar() } else { bar() }; + | ----------^^^^^----------------- + | | | + | | expected `()`, found `bool` + | expected this to be `()` + | +help: consider using a semicolon here + | +LL | if true { bar(); } else { bar() } if true { bar() } else { bar() }; + | + +help: consider using a semicolon here + | +LL | if true { bar() } else { bar() }; if true { bar() } else { bar() }; + | + + +error[E0308]: mismatched types + --> $DIR/expr-as-stmt-2.rs:27:30 + | +LL | if true { bar() } else { bar() } if true { bar() } else { bar() }; + | -------------------------^^^^^-- + | | | + | | expected `()`, found `bool` + | expected this to be `()` + | +help: consider using a semicolon here + | +LL | if true { bar() } else { bar(); } if true { bar() } else { bar() }; + | + +help: consider using a semicolon here + | +LL | if true { bar() } else { bar() }; if true { bar() } else { bar() }; + | + + +error: aborting due to 13 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/parser/expr-as-stmt.fixed b/tests/ui/parser/expr-as-stmt.fixed index 0a4d62a4a0c..bfae55047ed 100644 --- a/tests/ui/parser/expr-as-stmt.fixed +++ b/tests/ui/parser/expr-as-stmt.fixed @@ -66,7 +66,7 @@ fn asteroids() -> impl FnOnce() -> bool { // https://github.com/rust-lang/rust/issues/105179 fn r#match() -> i32 { - (match () { () => 1 }) + match () { () => 1 } //~ ERROR expected expression, found `+` + ((match () { () => 1 })) + match () { () => 1 } //~ ERROR expected expression, found `+` //~^ ERROR mismatched types } @@ -76,4 +76,13 @@ fn r#unsafe() -> i32 { //~^ ERROR mismatched types } +// https://github.com/rust-lang/rust/issues/88727 +fn matches() -> bool { + (match () { _ => true }) && match () { _ => true }; //~ ERROR mismatched types + (match () { _ => true }) && match () { _ => true }; //~ ERROR mismatched types + //~^ ERROR expected `;`, found keyword `match` + (match () { _ => true }) && true; //~ ERROR mismatched types + ((match () { _ => true })) && true //~ ERROR mismatched types + //~^ ERROR mismatched types +} fn main() {} diff --git a/tests/ui/parser/expr-as-stmt.rs b/tests/ui/parser/expr-as-stmt.rs index 99c85e65baa..94917432cb0 100644 --- a/tests/ui/parser/expr-as-stmt.rs +++ b/tests/ui/parser/expr-as-stmt.rs @@ -76,4 +76,13 @@ fn r#unsafe() -> i32 { //~^ ERROR mismatched types } +// https://github.com/rust-lang/rust/issues/88727 +fn matches() -> bool { + match () { _ => true } && match () { _ => true }; //~ ERROR mismatched types + match () { _ => true } && match () { _ => true } //~ ERROR mismatched types + //~^ ERROR expected `;`, found keyword `match` + match () { _ => true } && true; //~ ERROR mismatched types + match () { _ => true } && true //~ ERROR mismatched types + //~^ ERROR mismatched types +} fn main() {} diff --git a/tests/ui/parser/expr-as-stmt.stderr b/tests/ui/parser/expr-as-stmt.stderr index 577c3455a71..4681129baba 100644 --- a/tests/ui/parser/expr-as-stmt.stderr +++ b/tests/ui/parser/expr-as-stmt.stderr @@ -77,6 +77,15 @@ help: parentheses are required to parse this as an expression LL | (unsafe { 1 }) + unsafe { 1 } | + + +error: expected `;`, found keyword `match` + --> $DIR/expr-as-stmt.rs:82:53 + | +LL | match () { _ => true } && match () { _ => true } + | ^ help: add `;` here +LL | +LL | match () { _ => true } && true; + | ----- unexpected token + error[E0308]: mismatched types --> $DIR/expr-as-stmt.rs:64:7 | @@ -227,9 +236,12 @@ error[E0308]: mismatched types --> $DIR/expr-as-stmt.rs:69:5 | LL | match () { () => 1 } + match () { () => 1 } - | ^^^^^^^^^^^^^^^^^^^^- help: consider using a semicolon here - | | - | expected `()`, found integer + | ^^^^^^^^^^^^^^^^^^^^ expected `()`, found integer + | +help: parentheses are required to parse this as an expression + | +LL | (match () { () => 1 }) + match () { () => 1 } + | + + error[E0308]: mismatched types --> $DIR/expr-as-stmt.rs:75:14 @@ -242,7 +254,65 @@ help: you might have meant to return this value LL | unsafe { return 1; } + unsafe { 1 } | ++++++ + -error: aborting due to 22 previous errors +error[E0308]: mismatched types + --> $DIR/expr-as-stmt.rs:81:5 + | +LL | match () { _ => true } && match () { _ => true }; + | ^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found `bool` + | +help: parentheses are required to parse this as an expression + | +LL | (match () { _ => true }) && match () { _ => true }; + | + + + +error[E0308]: mismatched types + --> $DIR/expr-as-stmt.rs:82:5 + | +LL | match () { _ => true } && match () { _ => true } + | ^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found `bool` + | +help: parentheses are required to parse this as an expression + | +LL | (match () { _ => true }) && match () { _ => true } + | + + + +error[E0308]: mismatched types + --> $DIR/expr-as-stmt.rs:84:5 + | +LL | match () { _ => true } && true; + | ^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found `bool` + | +help: parentheses are required to parse this as an expression + | +LL | (match () { _ => true }) && true; + | + + + +error[E0308]: mismatched types + --> $DIR/expr-as-stmt.rs:85:5 + | +LL | match () { _ => true } && true + | ^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found `bool` + | +help: parentheses are required to parse this as an expression + | +LL | (match () { _ => true }) && true + | + + + +error[E0308]: mismatched types + --> $DIR/expr-as-stmt.rs:85:28 + | +LL | fn matches() -> bool { + | ---- expected `bool` because of return type +... +LL | match () { _ => true } && true + | ^^^^^^^ expected `bool`, found `&&bool` + | +help: parentheses are required to parse this as an expression + | +LL | (match () { _ => true }) && true + | + + + +error: aborting due to 28 previous errors Some errors have detailed explanations: E0308, E0600, E0614. For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs index 1cd6d13d56b..e7a0248cffa 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs @@ -4,29 +4,29 @@ fn main() {} fn f1_1(x: isize, ...) {} -//~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg +//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention fn f1_2(...) {} -//~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg +//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention extern "C" fn f2_1(x: isize, ...) {} -//~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg +//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention extern "C" fn f2_2(...) {} -//~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg +//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention extern "C" fn f2_3(..., x: isize) {} -//~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg +//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention //~| ERROR `...` must be the last argument of a C-variadic function extern "C" fn f3_1(x: isize, ...) {} -//~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg +//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention extern "C" fn f3_2(...) {} -//~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg +//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention extern "C" fn f3_3(..., x: isize) {} -//~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg +//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention //~| ERROR `...` must be the last argument of a C-variadic function const unsafe extern "C" fn f4_1(x: isize, ...) {} @@ -35,12 +35,12 @@ const unsafe extern "C" fn f4_1(x: isize, ...) {} const extern "C" fn f4_2(x: isize, ...) {} //~^ ERROR functions cannot be both `const` and C-variadic -//~| ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg +//~| ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention //~| ERROR destructor of `VaListImpl<'_>` cannot be evaluated at compile-time const extern "C" fn f4_3(..., x: isize, ...) {} //~^ ERROR functions cannot be both `const` and C-variadic -//~| ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg +//~| ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention //~| ERROR `...` must be the last argument of a C-variadic function extern "C" { @@ -52,34 +52,34 @@ struct X; impl X { fn i_f1(x: isize, ...) {} - //~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg + //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention fn i_f2(...) {} - //~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg + //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention fn i_f3(..., x: isize, ...) {} - //~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg + //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention //~| ERROR `...` must be the last argument of a C-variadic function fn i_f4(..., x: isize, ...) {} - //~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg + //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention //~| ERROR `...` must be the last argument of a C-variadic function const fn i_f5(x: isize, ...) {} - //~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg + //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention //~| ERROR functions cannot be both `const` and C-variadic //~| ERROR destructor of `VaListImpl<'_>` cannot be evaluated at compile-time } trait T { fn t_f1(x: isize, ...) {} - //~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg + //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention fn t_f2(x: isize, ...); - //~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg + //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention fn t_f3(...) {} - //~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg + //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention fn t_f4(...); - //~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg + //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention fn t_f5(..., x: isize) {} - //~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg + //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention //~| ERROR `...` must be the last argument of a C-variadic function fn t_f6(..., x: isize); - //~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg + //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention //~| ERROR `...` must be the last argument of a C-variadic function } diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr index b740cef0200..5379045967a 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr @@ -1,22 +1,22 @@ -error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg +error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention --> $DIR/variadic-ffi-semantic-restrictions.rs:6:19 | LL | fn f1_1(x: isize, ...) {} | ^^^ -error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg +error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention --> $DIR/variadic-ffi-semantic-restrictions.rs:9:9 | LL | fn f1_2(...) {} | ^^^ -error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg +error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention --> $DIR/variadic-ffi-semantic-restrictions.rs:12:30 | LL | extern "C" fn f2_1(x: isize, ...) {} | ^^^ -error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg +error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention --> $DIR/variadic-ffi-semantic-restrictions.rs:15:20 | LL | extern "C" fn f2_2(...) {} @@ -28,19 +28,19 @@ error: `...` must be the last argument of a C-variadic function LL | extern "C" fn f2_3(..., x: isize) {} | ^^^ -error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg +error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention --> $DIR/variadic-ffi-semantic-restrictions.rs:18:20 | LL | extern "C" fn f2_3(..., x: isize) {} | ^^^ -error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg +error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention --> $DIR/variadic-ffi-semantic-restrictions.rs:22:30 | LL | extern "C" fn f3_1(x: isize, ...) {} | ^^^ -error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg +error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention --> $DIR/variadic-ffi-semantic-restrictions.rs:25:20 | LL | extern "C" fn f3_2(...) {} @@ -52,7 +52,7 @@ error: `...` must be the last argument of a C-variadic function LL | extern "C" fn f3_3(..., x: isize) {} | ^^^ -error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg +error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention --> $DIR/variadic-ffi-semantic-restrictions.rs:28:20 | LL | extern "C" fn f3_3(..., x: isize) {} @@ -70,7 +70,7 @@ error: functions cannot be both `const` and C-variadic LL | const extern "C" fn f4_2(x: isize, ...) {} | ^^^^^ `const` because of this ^^^ C-variadic because of this -error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg +error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention --> $DIR/variadic-ffi-semantic-restrictions.rs:36:36 | LL | const extern "C" fn f4_2(x: isize, ...) {} @@ -91,7 +91,7 @@ LL | const extern "C" fn f4_3(..., x: isize, ...) {} | | C-variadic because of this | `const` because of this -error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg +error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention --> $DIR/variadic-ffi-semantic-restrictions.rs:41:26 | LL | const extern "C" fn f4_3(..., x: isize, ...) {} @@ -103,13 +103,13 @@ error: `...` must be the last argument of a C-variadic function LL | fn e_f2(..., x: isize); | ^^^ -error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg +error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention --> $DIR/variadic-ffi-semantic-restrictions.rs:54:23 | LL | fn i_f1(x: isize, ...) {} | ^^^ -error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg +error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention --> $DIR/variadic-ffi-semantic-restrictions.rs:56:13 | LL | fn i_f2(...) {} @@ -121,7 +121,7 @@ error: `...` must be the last argument of a C-variadic function LL | fn i_f3(..., x: isize, ...) {} | ^^^ -error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg +error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention --> $DIR/variadic-ffi-semantic-restrictions.rs:58:13 | LL | fn i_f3(..., x: isize, ...) {} @@ -133,7 +133,7 @@ error: `...` must be the last argument of a C-variadic function LL | fn i_f4(..., x: isize, ...) {} | ^^^ -error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg +error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention --> $DIR/variadic-ffi-semantic-restrictions.rs:61:13 | LL | fn i_f4(..., x: isize, ...) {} @@ -147,31 +147,31 @@ LL | const fn i_f5(x: isize, ...) {} | | | `const` because of this -error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg +error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention --> $DIR/variadic-ffi-semantic-restrictions.rs:64:29 | LL | const fn i_f5(x: isize, ...) {} | ^^^ -error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg +error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention --> $DIR/variadic-ffi-semantic-restrictions.rs:71:23 | LL | fn t_f1(x: isize, ...) {} | ^^^ -error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg +error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention --> $DIR/variadic-ffi-semantic-restrictions.rs:73:23 | LL | fn t_f2(x: isize, ...); | ^^^ -error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg +error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention --> $DIR/variadic-ffi-semantic-restrictions.rs:75:13 | LL | fn t_f3(...) {} | ^^^ -error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg +error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention --> $DIR/variadic-ffi-semantic-restrictions.rs:77:13 | LL | fn t_f4(...); @@ -183,7 +183,7 @@ error: `...` must be the last argument of a C-variadic function LL | fn t_f5(..., x: isize) {} | ^^^ -error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg +error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention --> $DIR/variadic-ffi-semantic-restrictions.rs:79:13 | LL | fn t_f5(..., x: isize) {} @@ -195,7 +195,7 @@ error: `...` must be the last argument of a C-variadic function LL | fn t_f6(..., x: isize); | ^^^ -error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg +error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention --> $DIR/variadic-ffi-semantic-restrictions.rs:82:13 | LL | fn t_f6(..., x: isize); diff --git a/tests/ui/pattern/at-in-struct-patterns.rs b/tests/ui/pattern/at-in-struct-patterns.rs index e8fad61f317..b0557220d05 100644 --- a/tests/ui/pattern/at-in-struct-patterns.rs +++ b/tests/ui/pattern/at-in-struct-patterns.rs @@ -5,7 +5,7 @@ struct Foo { fn main() { let foo = Foo { field1: 1, field2: 2 }; - let Foo { var @ field1, .. } = foo; //~ ERROR Unexpected `@` in struct pattern + let Foo { var @ field1, .. } = foo; //~ ERROR unexpected `@` in struct pattern dbg!(var); //~ ERROR cannot find value `var` in this scope let Foo { field1: _, bar @ .. } = foo; //~ ERROR `@ ..` is not supported in struct patterns let Foo { bar @ .. } = foo; //~ ERROR `@ ..` is not supported in struct patterns diff --git a/tests/ui/pattern/at-in-struct-patterns.stderr b/tests/ui/pattern/at-in-struct-patterns.stderr index ff75edfe681..20832897ca5 100644 --- a/tests/ui/pattern/at-in-struct-patterns.stderr +++ b/tests/ui/pattern/at-in-struct-patterns.stderr @@ -1,4 +1,4 @@ -error: Unexpected `@` in struct pattern +error: unexpected `@` in struct pattern --> $DIR/at-in-struct-patterns.rs:8:15 | LL | let Foo { var @ field1, .. } = foo; diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr index d76e60478a1..a5d9fd2b1a6 100644 --- a/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr +++ b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr @@ -5,6 +5,12 @@ LL | ((Ok(x) if y) | (Err(y) if x),) => x && y, | ^^^^^^^^^^^^ - variable not in all patterns | | | pattern doesn't bind `y` + | +help: you might have meant to use the similarly named previously used binding `x` + | +LL - ((Ok(x) if y) | (Err(y) if x),) => x && y, +LL + ((Ok(x) if y) | (Err(x) if x),) => x && y, + | error[E0408]: variable `x` is not bound in all patterns --> $DIR/name-resolution.rs:37:25 @@ -13,6 +19,12 @@ LL | ((Ok(x) if y) | (Err(y) if x),) => x && y, | - ^^^^^^^^^^^^^ pattern doesn't bind `x` | | | variable not in all patterns + | +help: you might have meant to use the similarly named previously used binding `y` + | +LL - ((Ok(x) if y) | (Err(y) if x),) => x && y, +LL + ((Ok(y) if y) | (Err(y) if x),) => x && y, + | error[E0408]: variable `x` is not bound in all patterns --> $DIR/name-resolution.rs:63:28 diff --git a/tests/ui/privacy/suggest-box-new.rs b/tests/ui/privacy/suggest-box-new.rs index 7125285fc77..ff585387020 100644 --- a/tests/ui/privacy/suggest-box-new.rs +++ b/tests/ui/privacy/suggest-box-new.rs @@ -16,4 +16,7 @@ fn main() { let _ = std::collections::HashMap {}; //~^ ERROR cannot construct `HashMap<_, _, _>` with struct literal syntax due to private fields let _ = Box {}; //~ ERROR cannot construct `Box<_, _>` with struct literal syntax due to private fields + + // test that we properly instantiate the parameter of `Box::<T>::new` with an inference variable + let _ = Box::<i32> {}; //~ ERROR cannot construct `Box<i32>` with struct literal syntax due to private fields } diff --git a/tests/ui/privacy/suggest-box-new.stderr b/tests/ui/privacy/suggest-box-new.stderr index 2b48e9046bf..37b2989dcc1 100644 --- a/tests/ui/privacy/suggest-box-new.stderr +++ b/tests/ui/privacy/suggest-box-new.stderr @@ -118,13 +118,41 @@ LL + let _ = Box::new_zeroed(); LL - let _ = Box {}; LL + let _ = Box::new_in(_, _); | - = and 12 other candidates + = and 13 other candidates help: consider using the `Default` trait | LL - let _ = Box {}; LL + let _ = <Box as std::default::Default>::default(); | -error: aborting due to 4 previous errors +error: cannot construct `Box<i32>` with struct literal syntax due to private fields + --> $DIR/suggest-box-new.rs:21:13 + | +LL | let _ = Box::<i32> {}; + | ^^^^^^^^^^ + | + = note: private fields `0` and `1` that were not provided +help: you might have meant to use an associated function to build this type + | +LL - let _ = Box::<i32> {}; +LL + let _ = Box::<i32>::new(_); + | +LL - let _ = Box::<i32> {}; +LL + let _ = Box::<i32>::new_in(_, _); + | +LL - let _ = Box::<i32> {}; +LL + let _ = Box::<i32>::into_inner(_); + | +LL - let _ = Box::<i32> {}; +LL + let _ = Box::<i32>::write(_, _); + | + = and 4 other candidates +help: consider using the `Default` trait + | +LL - let _ = Box::<i32> {}; +LL + let _ = <Box::<i32> as std::default::Default>::default(); + | + +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0423`. diff --git a/tests/ui/privacy/suggest-new-projection-ice.rs b/tests/ui/privacy/suggest-new-projection-ice.rs new file mode 100644 index 00000000000..41ac27508e8 --- /dev/null +++ b/tests/ui/privacy/suggest-new-projection-ice.rs @@ -0,0 +1,19 @@ +//! Regression test for <https://github.com/rust-lang/rust/issues/146174>. +//! Ensure that we don't ICE when an associated function returns an associated type. + +mod m { + pub trait Project { + type Assoc; + } + pub struct Foo { + _priv: (), + } + impl Foo { + fn new<T: Project>() -> T::Assoc { + todo!() + } + } +} +fn main() { + let _ = m::Foo {}; //~ ERROR: cannot construct `Foo` +} diff --git a/tests/ui/privacy/suggest-new-projection-ice.stderr b/tests/ui/privacy/suggest-new-projection-ice.stderr new file mode 100644 index 00000000000..4700772096b --- /dev/null +++ b/tests/ui/privacy/suggest-new-projection-ice.stderr @@ -0,0 +1,10 @@ +error: cannot construct `Foo` with struct literal syntax due to private fields + --> $DIR/suggest-new-projection-ice.rs:18:13 + | +LL | let _ = m::Foo {}; + | ^^^^^^ + | + = note: private field `_priv` that was not provided + +error: aborting due to 1 previous error + diff --git a/tests/ui/resolve/resolve-inconsistent-names.rs b/tests/ui/resolve/resolve-inconsistent-names.rs index 9a40b20346c..96504720e83 100644 --- a/tests/ui/resolve/resolve-inconsistent-names.rs +++ b/tests/ui/resolve/resolve-inconsistent-names.rs @@ -10,8 +10,8 @@ pub mod m { fn main() { let y = 1; match y { - a | b => {} //~ ERROR variable `a` is not bound in all patterns - //~| ERROR variable `b` is not bound in all patterns + a | b => {} //~ ERROR variable `a` is not bound in all patterns + //~| ERROR variable `b` is not bound in all patterns } let x = (E::A, E::B); diff --git a/tests/ui/resolve/resolve-inconsistent-names.stderr b/tests/ui/resolve/resolve-inconsistent-names.stderr index 5fac622eef2..d2333150961 100644 --- a/tests/ui/resolve/resolve-inconsistent-names.stderr +++ b/tests/ui/resolve/resolve-inconsistent-names.stderr @@ -1,18 +1,18 @@ error[E0408]: variable `b` is not bound in all patterns - --> $DIR/resolve-inconsistent-names.rs:13:8 + --> $DIR/resolve-inconsistent-names.rs:13:9 | -LL | a | b => {} - | ^ - variable not in all patterns - | | - | pattern doesn't bind `b` +LL | a | b => {} + | ^ - variable not in all patterns + | | + | pattern doesn't bind `b` error[E0408]: variable `a` is not bound in all patterns - --> $DIR/resolve-inconsistent-names.rs:13:12 + --> $DIR/resolve-inconsistent-names.rs:13:13 | -LL | a | b => {} - | - ^ pattern doesn't bind `a` - | | - | variable not in all patterns +LL | a | b => {} + | - ^ pattern doesn't bind `a` + | | + | variable not in all patterns error[E0408]: variable `c` is not bound in all patterns --> $DIR/resolve-inconsistent-names.rs:19:9 diff --git a/tests/ui/sanitizer/address.rs b/tests/ui/sanitizer/address.rs index 704d84764c1..1688f46c2d5 100644 --- a/tests/ui/sanitizer/address.rs +++ b/tests/ui/sanitizer/address.rs @@ -2,7 +2,7 @@ //@ needs-sanitizer-address //@ ignore-cross-compile // -//@ compile-flags: -Z sanitizer=address -O -g +//@ compile-flags: -Z sanitizer=address -O -g -C unsafe-allow-abi-mismatch=sanitizer // //@ run-fail-or-crash //@ error-pattern: AddressSanitizer: stack-buffer-overflow diff --git a/tests/ui/sanitizer/asan_odr_windows.rs b/tests/ui/sanitizer/asan_odr_windows.rs index 28c24716761..b638d6eb969 100644 --- a/tests/ui/sanitizer/asan_odr_windows.rs +++ b/tests/ui/sanitizer/asan_odr_windows.rs @@ -2,7 +2,7 @@ //! See <https://github.com/rust-lang/rust/issues/124390>. //@ run-pass -//@ compile-flags:-Zsanitizer=address +//@ compile-flags:-Zsanitizer=address -C unsafe-allow-abi-mismatch=sanitizer //@ aux-build: asan_odr_win-2.rs //@ only-windows-msvc //@ needs-sanitizer-support diff --git a/tests/ui/sanitizer/badfree.rs b/tests/ui/sanitizer/badfree.rs index 6b3aea7239c..b1b02649dcc 100644 --- a/tests/ui/sanitizer/badfree.rs +++ b/tests/ui/sanitizer/badfree.rs @@ -2,7 +2,7 @@ //@ needs-sanitizer-address //@ ignore-cross-compile // -//@ compile-flags: -Z sanitizer=address -O +//@ compile-flags: -Z sanitizer=address -O -C unsafe-allow-abi-mismatch=sanitizer // //@ run-fail-or-crash //@ regex-error-pattern: AddressSanitizer: (SEGV|attempting free on address which was not malloc) diff --git a/tests/ui/sanitizer/cfi/assoc-ty-lifetime-issue-123053.rs b/tests/ui/sanitizer/cfi/assoc-ty-lifetime-issue-123053.rs index fad57198dfb..5de5e99f6d0 100644 --- a/tests/ui/sanitizer/cfi/assoc-ty-lifetime-issue-123053.rs +++ b/tests/ui/sanitizer/cfi/assoc-ty-lifetime-issue-123053.rs @@ -2,7 +2,7 @@ // trait object type to fail, causing an ICE. // //@ needs-sanitizer-cfi -//@ compile-flags: -Ccodegen-units=1 -Clto -Ctarget-feature=-crt-static -Zsanitizer=cfi +//@ compile-flags: -Ccodegen-units=1 -Clto -Ctarget-feature=-crt-static -Zsanitizer=cfi -C unsafe-allow-abi-mismatch=sanitizer //@ edition: 2021 //@ no-prefer-dynamic //@ only-x86_64-unknown-linux-gnu diff --git a/tests/ui/sanitizer/cfi/async-closures.rs b/tests/ui/sanitizer/cfi/async-closures.rs index 9b099263000..621a0882c91 100644 --- a/tests/ui/sanitizer/cfi/async-closures.rs +++ b/tests/ui/sanitizer/cfi/async-closures.rs @@ -7,7 +7,7 @@ //@ ignore-backends: gcc //@ [cfi] needs-sanitizer-cfi //@ [kcfi] needs-sanitizer-kcfi -//@ compile-flags: -C target-feature=-crt-static +//@ compile-flags: -C target-feature=-crt-static -C unsafe-allow-abi-mismatch=sanitizer //@ [cfi] compile-flags: -C codegen-units=1 -C lto -C prefer-dynamic=off -C opt-level=0 //@ [cfi] compile-flags: -Z sanitizer=cfi //@ [kcfi] compile-flags: -Z sanitizer=kcfi diff --git a/tests/ui/sanitizer/cfi/can-reveal-opaques.rs b/tests/ui/sanitizer/cfi/can-reveal-opaques.rs index a881c6b92b2..310ce04c552 100644 --- a/tests/ui/sanitizer/cfi/can-reveal-opaques.rs +++ b/tests/ui/sanitizer/cfi/can-reveal-opaques.rs @@ -1,5 +1,5 @@ //@ needs-sanitizer-cfi -//@ compile-flags: -Ccodegen-units=1 -Clto -Ctarget-feature=-crt-static -Zsanitizer=cfi +//@ compile-flags: -Ccodegen-units=1 -Clto -Ctarget-feature=-crt-static -Zsanitizer=cfi -C unsafe-allow-abi-mismatch=sanitizer //@ no-prefer-dynamic //@ only-x86_64-unknown-linux-gnu //@ ignore-backends: gcc diff --git a/tests/ui/sanitizer/cfi/closures.rs b/tests/ui/sanitizer/cfi/closures.rs index fc9718faa28..7493dba4928 100644 --- a/tests/ui/sanitizer/cfi/closures.rs +++ b/tests/ui/sanitizer/cfi/closures.rs @@ -6,7 +6,7 @@ //@ ignore-backends: gcc //@ [cfi] needs-sanitizer-cfi //@ [kcfi] needs-sanitizer-kcfi -//@ compile-flags: -C target-feature=-crt-static +//@ compile-flags: -C target-feature=-crt-static -C unsafe-allow-abi-mismatch=sanitizer //@ [cfi] compile-flags: -C codegen-units=1 -C lto -C prefer-dynamic=off -C opt-level=0 //@ [cfi] compile-flags: -Z sanitizer=cfi //@ [kcfi] compile-flags: -Z sanitizer=kcfi diff --git a/tests/ui/sanitizer/cfi/complex-receiver.rs b/tests/ui/sanitizer/cfi/complex-receiver.rs index aac44c33227..adacc0d6c5d 100644 --- a/tests/ui/sanitizer/cfi/complex-receiver.rs +++ b/tests/ui/sanitizer/cfi/complex-receiver.rs @@ -8,7 +8,7 @@ //@ ignore-backends: gcc //@ [cfi] needs-sanitizer-cfi //@ [kcfi] needs-sanitizer-kcfi -//@ compile-flags: -C target-feature=-crt-static +//@ compile-flags: -C target-feature=-crt-static -C unsafe-allow-abi-mismatch=sanitizer //@ [cfi] compile-flags: -C codegen-units=1 -C lto -C prefer-dynamic=off -C opt-level=0 //@ [cfi] compile-flags: -Z sanitizer=cfi //@ [kcfi] compile-flags: -Z sanitizer=kcfi diff --git a/tests/ui/sanitizer/cfi/coroutine.rs b/tests/ui/sanitizer/cfi/coroutine.rs index 084a521f597..d85615b597d 100644 --- a/tests/ui/sanitizer/cfi/coroutine.rs +++ b/tests/ui/sanitizer/cfi/coroutine.rs @@ -8,6 +8,7 @@ //@ [cfi] needs-sanitizer-cfi //@ [kcfi] needs-sanitizer-kcfi //@ compile-flags: -C target-feature=-crt-static +//@ compile-flags: -C unsafe-allow-abi-mismatch=sanitizer //@ [cfi] compile-flags: -C codegen-units=1 -C lto -C prefer-dynamic=off -C opt-level=0 //@ [cfi] compile-flags: -Z sanitizer=cfi //@ [kcfi] compile-flags: -Z sanitizer=kcfi diff --git a/tests/ui/sanitizer/cfi/drop-in-place.rs b/tests/ui/sanitizer/cfi/drop-in-place.rs index 160338a5b54..fe59d546312 100644 --- a/tests/ui/sanitizer/cfi/drop-in-place.rs +++ b/tests/ui/sanitizer/cfi/drop-in-place.rs @@ -5,6 +5,7 @@ //@ ignore-backends: gcc //@ needs-sanitizer-cfi //@ compile-flags: -Clto -Copt-level=0 -Cprefer-dynamic=off -Ctarget-feature=-crt-static -Zsanitizer=cfi +//@ compile-flags: -C unsafe-allow-abi-mismatch=sanitizer //@ run-pass struct EmptyDrop; diff --git a/tests/ui/sanitizer/cfi/drop-no-principal.rs b/tests/ui/sanitizer/cfi/drop-no-principal.rs index e3a46fe9d75..4fb905eb51d 100644 --- a/tests/ui/sanitizer/cfi/drop-no-principal.rs +++ b/tests/ui/sanitizer/cfi/drop-no-principal.rs @@ -4,7 +4,7 @@ // FIXME(#122848) Remove only-linux once OSX CFI binaries works //@ only-linux //@ ignore-backends: gcc -//@ compile-flags: --crate-type=bin -Cprefer-dynamic=off -Clto -Zsanitizer=cfi +//@ compile-flags: --crate-type=bin -Cprefer-dynamic=off -Clto -Zsanitizer=cfi -C unsafe-allow-abi-mismatch=sanitizer //@ compile-flags: -C target-feature=-crt-static -C codegen-units=1 -C opt-level=0 // FIXME(#118761) Should be run-pass once the labels on drop are compatible. // This test is being landed ahead of that to test that the compiler doesn't ICE while labeling the diff --git a/tests/ui/sanitizer/cfi/fn-ptr.rs b/tests/ui/sanitizer/cfi/fn-ptr.rs index d3209c62ddf..bdb8c7ceb32 100644 --- a/tests/ui/sanitizer/cfi/fn-ptr.rs +++ b/tests/ui/sanitizer/cfi/fn-ptr.rs @@ -7,6 +7,7 @@ //@ [cfi] needs-sanitizer-cfi //@ [kcfi] needs-sanitizer-kcfi //@ compile-flags: -C target-feature=-crt-static +//@ compile-flags: -C unsafe-allow-abi-mismatch=sanitizer //@ [cfi] compile-flags: -C opt-level=0 -C codegen-units=1 -C lto //@ [cfi] compile-flags: -C prefer-dynamic=off //@ [cfi] compile-flags: -Z sanitizer=cfi diff --git a/tests/ui/sanitizer/cfi/generalize-pointers-attr-cfg.rs b/tests/ui/sanitizer/cfi/generalize-pointers-attr-cfg.rs index d46002c69fd..44cdcb250e7 100644 --- a/tests/ui/sanitizer/cfi/generalize-pointers-attr-cfg.rs +++ b/tests/ui/sanitizer/cfi/generalize-pointers-attr-cfg.rs @@ -4,6 +4,7 @@ //@ needs-sanitizer-cfi //@ check-pass //@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-generalize-pointers +//@ compile-flags: -C unsafe-allow-abi-mismatch=sanitizer #![feature(cfg_sanitizer_cfi)] diff --git a/tests/ui/sanitizer/cfi/no_builtins.rs b/tests/ui/sanitizer/cfi/no_builtins.rs deleted file mode 100644 index 949057689ab..00000000000 --- a/tests/ui/sanitizer/cfi/no_builtins.rs +++ /dev/null @@ -1,22 +0,0 @@ -// Verifies that `#![no_builtins]` crates can be built with linker-plugin-lto and CFI. -// See Issue #142284 -// -//@ needs-sanitizer-cfi -//@ compile-flags: -Clinker-plugin-lto -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static -//@ compile-flags: --crate-type rlib -//@ build-pass - -#![no_builtins] -#![no_std] - -pub static FUNC: fn() = initializer; - -pub fn initializer() { - call(fma_with_fma); -} - -pub fn call(fn_ptr: fn()) { - fn_ptr(); -} - -pub fn fma_with_fma() {} diff --git a/tests/ui/sanitizer/cfi/normalize-integers-attr-cfg.rs b/tests/ui/sanitizer/cfi/normalize-integers-attr-cfg.rs index 24c2c2c13da..ce4e31eb69b 100644 --- a/tests/ui/sanitizer/cfi/normalize-integers-attr-cfg.rs +++ b/tests/ui/sanitizer/cfi/normalize-integers-attr-cfg.rs @@ -3,7 +3,7 @@ // //@ needs-sanitizer-cfi //@ check-pass -//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-normalize-integers +//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-normalize-integers -C unsafe-allow-abi-mismatch=sanitizer,sanitizer-cfi-normalize-integers #![feature(cfg_sanitizer_cfi)] diff --git a/tests/ui/sanitizer/cfi/self-ref.rs b/tests/ui/sanitizer/cfi/self-ref.rs index b93d49296b6..827610a2610 100644 --- a/tests/ui/sanitizer/cfi/self-ref.rs +++ b/tests/ui/sanitizer/cfi/self-ref.rs @@ -6,7 +6,7 @@ //@ ignore-backends: gcc //@ [cfi] needs-sanitizer-cfi //@ [kcfi] needs-sanitizer-kcfi -//@ compile-flags: -C target-feature=-crt-static +//@ compile-flags: -C target-feature=-crt-static -C unsafe-allow-abi-mismatch=sanitizer //@ [cfi] compile-flags: -C codegen-units=1 -C lto -C prefer-dynamic=off -C opt-level=0 //@ [cfi] compile-flags: -Z sanitizer=cfi //@ [kcfi] compile-flags: -Z sanitizer=kcfi diff --git a/tests/ui/sanitizer/cfi/sized-associated-ty.rs b/tests/ui/sanitizer/cfi/sized-associated-ty.rs index eefc3e2e9db..da8c385c6fc 100644 --- a/tests/ui/sanitizer/cfi/sized-associated-ty.rs +++ b/tests/ui/sanitizer/cfi/sized-associated-ty.rs @@ -7,7 +7,7 @@ //@ ignore-backends: gcc //@ [cfi] needs-sanitizer-cfi //@ [kcfi] needs-sanitizer-kcfi -//@ compile-flags: -C target-feature=-crt-static +//@ compile-flags: -C target-feature=-crt-static -C unsafe-allow-abi-mismatch=sanitizer //@ [cfi] compile-flags: -C codegen-units=1 -C lto -C prefer-dynamic=off -C opt-level=0 //@ [cfi] compile-flags: -Z sanitizer=cfi //@ [kcfi] compile-flags: -Z sanitizer=kcfi diff --git a/tests/ui/sanitizer/cfi/supertraits.rs b/tests/ui/sanitizer/cfi/supertraits.rs index f5a984b9583..b2782dff5d5 100644 --- a/tests/ui/sanitizer/cfi/supertraits.rs +++ b/tests/ui/sanitizer/cfi/supertraits.rs @@ -6,7 +6,7 @@ //@ ignore-backends: gcc //@ [cfi] needs-sanitizer-cfi //@ [kcfi] needs-sanitizer-kcfi -//@ compile-flags: -C target-feature=-crt-static +//@ compile-flags: -C target-feature=-crt-static -C unsafe-allow-abi-mismatch=sanitizer //@ [cfi] compile-flags: -C codegen-units=1 -C lto -C prefer-dynamic=off -C opt-level=0 //@ [cfi] compile-flags: -Z sanitizer=cfi //@ [kcfi] compile-flags: -Z sanitizer=kcfi diff --git a/tests/ui/sanitizer/cfi/transparent-has-regions.rs b/tests/ui/sanitizer/cfi/transparent-has-regions.rs index b70e1ea1791..b82850133c1 100644 --- a/tests/ui/sanitizer/cfi/transparent-has-regions.rs +++ b/tests/ui/sanitizer/cfi/transparent-has-regions.rs @@ -1,5 +1,5 @@ //@ needs-sanitizer-cfi -//@ compile-flags: -Ccodegen-units=1 -Clto -Ctarget-feature=-crt-static -Zsanitizer=cfi +//@ compile-flags: -Ccodegen-units=1 -Clto -Ctarget-feature=-crt-static -Zsanitizer=cfi -C unsafe-allow-abi-mismatch=sanitizer //@ no-prefer-dynamic //@ only-x86_64-unknown-linux-gnu //@ build-pass diff --git a/tests/ui/sanitizer/cfi/virtual-auto.rs b/tests/ui/sanitizer/cfi/virtual-auto.rs index e6f2ebd4b2c..d3a715c079a 100644 --- a/tests/ui/sanitizer/cfi/virtual-auto.rs +++ b/tests/ui/sanitizer/cfi/virtual-auto.rs @@ -6,7 +6,7 @@ //@ ignore-backends: gcc //@ [cfi] needs-sanitizer-cfi //@ [kcfi] needs-sanitizer-kcfi -//@ compile-flags: -C target-feature=-crt-static +//@ compile-flags: -C target-feature=-crt-static -C unsafe-allow-abi-mismatch=sanitizer //@ [cfi] compile-flags: -C codegen-units=1 -C lto -C prefer-dynamic=off -C opt-level=0 //@ [cfi] compile-flags: -Z sanitizer=cfi //@ [kcfi] compile-flags: -Z sanitizer=kcfi diff --git a/tests/ui/sanitizer/dataflow.rs b/tests/ui/sanitizer/dataflow.rs index 658a9e48086..d99a1a6ac24 100644 --- a/tests/ui/sanitizer/dataflow.rs +++ b/tests/ui/sanitizer/dataflow.rs @@ -4,7 +4,7 @@ //@ needs-sanitizer-support //@ needs-sanitizer-dataflow //@ run-pass -//@ compile-flags: -Zsanitizer=dataflow -Zsanitizer-dataflow-abilist={{src-base}}/sanitizer/dataflow-abilist.txt +//@ compile-flags: -Zsanitizer=dataflow -Zsanitizer-dataflow-abilist={{src-base}}/sanitizer/dataflow-abilist.txt -C unsafe-allow-abi-mismatch=sanitizer use std::mem::size_of; use std::os::raw::{c_int, c_long, c_void}; diff --git a/tests/ui/sanitizer/hwaddress.rs b/tests/ui/sanitizer/hwaddress.rs index e5939eb734b..05fcab17506 100644 --- a/tests/ui/sanitizer/hwaddress.rs +++ b/tests/ui/sanitizer/hwaddress.rs @@ -5,7 +5,7 @@ //@ ignore-aarch64-unknown-linux-gnu // // FIXME(#83989): codegen-units=1 triggers linker errors on aarch64-gnu -//@ compile-flags: -Z sanitizer=hwaddress -O -g -C codegen-units=16 +//@ compile-flags: -Z sanitizer=hwaddress -O -g -C codegen-units=16 -C unsafe-allow-abi-mismatch=sanitizer // //@ run-fail //@ error-pattern: HWAddressSanitizer: tag-mismatch diff --git a/tests/ui/sanitizer/issue-111184-cfi-coroutine-witness.rs b/tests/ui/sanitizer/issue-111184-cfi-coroutine-witness.rs index 7d0c73c2841..be81c7bd0ca 100644 --- a/tests/ui/sanitizer/issue-111184-cfi-coroutine-witness.rs +++ b/tests/ui/sanitizer/issue-111184-cfi-coroutine-witness.rs @@ -2,7 +2,7 @@ // encode_ty and caused the compiler to ICE. // //@ needs-sanitizer-cfi -//@ compile-flags: -Ccodegen-units=1 -Clto -Ctarget-feature=-crt-static -Zsanitizer=cfi +//@ compile-flags: -Ccodegen-units=1 -Clto -Ctarget-feature=-crt-static -Zsanitizer=cfi -C unsafe-allow-abi-mismatch=sanitizer //@ edition: 2021 //@ no-prefer-dynamic //@ only-x86_64-unknown-linux-gnu diff --git a/tests/ui/sanitizer/issue-114275-cfi-const-expr-in-arry-len.rs b/tests/ui/sanitizer/issue-114275-cfi-const-expr-in-arry-len.rs index b1b7487fa2a..f7af2842ad6 100644 --- a/tests/ui/sanitizer/issue-114275-cfi-const-expr-in-arry-len.rs +++ b/tests/ui/sanitizer/issue-114275-cfi-const-expr-in-arry-len.rs @@ -2,7 +2,7 @@ // was expecting array type lengths to be evaluated, this was causing an ICE. // //@ build-pass -//@ compile-flags: -Ccodegen-units=1 -Clto -Zsanitizer=cfi -Ctarget-feature=-crt-static +//@ compile-flags: -Ccodegen-units=1 -Clto -Zsanitizer=cfi -Ctarget-feature=-crt-static -C unsafe-allow-abi-mismatch=sanitizer //@ needs-sanitizer-cfi #![crate_type = "lib"] diff --git a/tests/ui/sanitizer/issue-72154-address-lifetime-markers.rs b/tests/ui/sanitizer/issue-72154-address-lifetime-markers.rs index aa0c19db9a1..edeb1b0bf94 100644 --- a/tests/ui/sanitizer/issue-72154-address-lifetime-markers.rs +++ b/tests/ui/sanitizer/issue-72154-address-lifetime-markers.rs @@ -7,7 +7,7 @@ //@ needs-sanitizer-address //@ ignore-cross-compile // -//@ compile-flags: -Copt-level=0 -Zsanitizer=address +//@ compile-flags: -Copt-level=0 -Zsanitizer=address -C unsafe-allow-abi-mismatch=sanitizer //@ run-pass pub struct Wrap { diff --git a/tests/ui/sanitizer/kcfi-mangling.rs b/tests/ui/sanitizer/kcfi-mangling.rs index ba36dc184ec..371f34ba72a 100644 --- a/tests/ui/sanitizer/kcfi-mangling.rs +++ b/tests/ui/sanitizer/kcfi-mangling.rs @@ -2,7 +2,7 @@ //@ needs-sanitizer-kcfi //@ no-prefer-dynamic -//@ compile-flags: -C panic=abort -Zsanitizer=kcfi -C symbol-mangling-version=v0 +//@ compile-flags: -C panic=abort -Zsanitizer=kcfi -C symbol-mangling-version=v0 -C unsafe-allow-abi-mismatch=sanitizer //@ build-pass //@ ignore-backends: gcc diff --git a/tests/ui/sanitizer/leak.rs b/tests/ui/sanitizer/leak.rs index 65915ec24b7..4ce3f7ab1b9 100644 --- a/tests/ui/sanitizer/leak.rs +++ b/tests/ui/sanitizer/leak.rs @@ -1,7 +1,7 @@ //@ needs-sanitizer-support //@ needs-sanitizer-leak // -//@ compile-flags: -Z sanitizer=leak -O +//@ compile-flags: -Z sanitizer=leak -O -C unsafe-allow-abi-mismatch=sanitizer // //@ run-fail //@ error-pattern: LeakSanitizer: detected memory leaks diff --git a/tests/ui/sanitizer/memory-eager.rs b/tests/ui/sanitizer/memory-eager.rs index 709299f87d4..9498336abb7 100644 --- a/tests/ui/sanitizer/memory-eager.rs +++ b/tests/ui/sanitizer/memory-eager.rs @@ -1,6 +1,8 @@ //@ needs-sanitizer-support //@ needs-sanitizer-memory // +//@ compile-flags: -C unsafe-allow-abi-mismatch=sanitizer +// //@ revisions: unoptimized optimized // //@ [optimized]compile-flags: -Z sanitizer=memory -Zsanitizer-memory-track-origins -O diff --git a/tests/ui/sanitizer/memory-passing.rs b/tests/ui/sanitizer/memory-passing.rs index 96a4cd909c7..6ac41b178fe 100644 --- a/tests/ui/sanitizer/memory-passing.rs +++ b/tests/ui/sanitizer/memory-passing.rs @@ -1,6 +1,8 @@ //@ needs-sanitizer-support //@ needs-sanitizer-memory // +//@ compile-flags: -C unsafe-allow-abi-mismatch=sanitizer +// //@ revisions: unoptimized optimized // //@ [optimized]compile-flags: -Z sanitizer=memory -Zsanitizer-memory-track-origins -O diff --git a/tests/ui/sanitizer/memory.rs b/tests/ui/sanitizer/memory.rs index a91fefe4d16..1566637acd2 100644 --- a/tests/ui/sanitizer/memory.rs +++ b/tests/ui/sanitizer/memory.rs @@ -1,6 +1,8 @@ //@ needs-sanitizer-support //@ needs-sanitizer-memory // +//@ compile-flags: -C unsafe-allow-abi-mismatch=sanitizer +// //@ revisions: unoptimized optimized // //@ [optimized]compile-flags: -Z sanitizer=memory -Zsanitizer-memory-track-origins -O diff --git a/tests/ui/sanitizer/new-llvm-pass-manager-thin-lto.rs b/tests/ui/sanitizer/new-llvm-pass-manager-thin-lto.rs index c1a2c2f26ac..b6656847427 100644 --- a/tests/ui/sanitizer/new-llvm-pass-manager-thin-lto.rs +++ b/tests/ui/sanitizer/new-llvm-pass-manager-thin-lto.rs @@ -6,6 +6,8 @@ //@ needs-sanitizer-address //@ ignore-cross-compile // +//@ compile-flags: -C unsafe-allow-abi-mismatch=sanitizer +// //@ no-prefer-dynamic //@ revisions: opt0 opt1 //@ compile-flags: -Zsanitizer=address -Clto=thin diff --git a/tests/ui/sanitizer/thread.rs b/tests/ui/sanitizer/thread.rs index 9073124d1bd..8f72b1b41a8 100644 --- a/tests/ui/sanitizer/thread.rs +++ b/tests/ui/sanitizer/thread.rs @@ -13,7 +13,7 @@ //@ needs-sanitizer-support //@ needs-sanitizer-thread // -//@ compile-flags: -Z sanitizer=thread -O +//@ compile-flags: -Z sanitizer=thread -O -C unsafe-allow-abi-mismatch=sanitizer // //@ run-fail-or-crash //@ error-pattern: WARNING: ThreadSanitizer: data race diff --git a/tests/ui/sanitizer/use-after-scope.rs b/tests/ui/sanitizer/use-after-scope.rs index 106dc6466d6..1c477d0be14 100644 --- a/tests/ui/sanitizer/use-after-scope.rs +++ b/tests/ui/sanitizer/use-after-scope.rs @@ -2,7 +2,7 @@ //@ needs-sanitizer-address //@ ignore-cross-compile // -//@ compile-flags: -Zsanitizer=address +//@ compile-flags: -Zsanitizer=address -C unsafe-allow-abi-mismatch=sanitizer //@ run-fail-or-crash //@ error-pattern: ERROR: AddressSanitizer: stack-use-after-scope diff --git a/tests/ui/span/issue-39698.stderr b/tests/ui/span/issue-39698.stderr index 73fcc5c8477..eb18969c3c0 100644 --- a/tests/ui/span/issue-39698.stderr +++ b/tests/ui/span/issue-39698.stderr @@ -7,6 +7,12 @@ LL | T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?} | | | pattern doesn't bind `b` | | variable not in all patterns | pattern doesn't bind `b` + | +help: you might have meant to use the similarly named previously used binding `c` + | +LL - T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?}", a); } +LL + T::T1(a, d) | T::T2(d, c) | T::T3(c) | T::T4(a) => { println!("{:?}", a); } + | error[E0408]: variable `c` is not bound in all patterns --> $DIR/issue-39698.rs:10:9 @@ -17,6 +23,12 @@ LL | T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?} | | | variable not in all patterns | | pattern doesn't bind `c` | pattern doesn't bind `c` + | +help: you might have meant to use the similarly named previously used binding `d` + | +LL - T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?}", a); } +LL + T::T1(a, d) | T::T2(d, b) | T::T3(d) | T::T4(a) => { println!("{:?}", a); } + | error[E0408]: variable `a` is not bound in all patterns --> $DIR/issue-39698.rs:10:23 @@ -27,6 +39,12 @@ LL | T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?} | | | pattern doesn't bind `a` | | pattern doesn't bind `a` | variable not in all patterns + | +help: you might have meant to use the similarly named previously used binding `c` + | +LL - T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?}", a); } +LL + T::T1(c, d) | T::T2(d, b) | T::T3(c) | T::T4(c) => { println!("{:?}", a); } + | error[E0408]: variable `d` is not bound in all patterns --> $DIR/issue-39698.rs:10:37 @@ -37,6 +55,12 @@ LL | T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?} | | | pattern doesn't bind `d` | | variable not in all patterns | variable not in all patterns + | +help: you might have meant to use the similarly named previously used binding `c` + | +LL - T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?}", a); } +LL + T::T1(a, c) | T::T2(c, b) | T::T3(c) | T::T4(a) => { println!("{:?}", a); } + | error[E0381]: used binding `a` is possibly-uninitialized --> $DIR/issue-39698.rs:10:79 diff --git a/tests/ui/statics/mutable_memory_validation.stderr b/tests/ui/statics/mutable_memory_validation.stderr index df36287cc69..1d6ba195fa2 100644 --- a/tests/ui/statics/mutable_memory_validation.stderr +++ b/tests/ui/statics/mutable_memory_validation.stderr @@ -4,7 +4,7 @@ error[E0080]: constructing invalid value at .x.<deref>: encountered `UnsafeCell` LL | const MUH: Meh = Meh { x: unsafe { &mut *(&READONLY as *const _ as *mut _) } }; | ^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { HEX_DUMP } diff --git a/tests/ui/stats/input-stats.stderr b/tests/ui/stats/input-stats.stderr index 72a9820bb64..4a73a4747ad 100644 --- a/tests/ui/stats/input-stats.stderr +++ b/tests/ui/stats/input-stats.stderr @@ -23,10 +23,10 @@ ast-stats - Path 72 (NN.N%) 1 ast-stats - Struct 72 (NN.N%) 1 ast-stats - Lit 144 (NN.N%) 2 ast-stats - Block 216 (NN.N%) 3 -ast-stats Pat 504 (NN.N%) 7 72 -ast-stats - Struct 72 (NN.N%) 1 -ast-stats - Wild 72 (NN.N%) 1 -ast-stats - Ident 360 (NN.N%) 5 +ast-stats Pat 560 (NN.N%) 7 80 +ast-stats - Struct 80 (NN.N%) 1 +ast-stats - Wild 80 (NN.N%) 1 +ast-stats - Ident 400 (NN.N%) 5 ast-stats GenericParam 480 (NN.N%) 5 96 ast-stats GenericBound 352 (NN.N%) 4 88 ast-stats - Trait 352 (NN.N%) 4 @@ -57,7 +57,7 @@ ast-stats GenericArgs 40 (NN.N%) 1 40 ast-stats - AngleBracketed 40 (NN.N%) 1 ast-stats Crate 40 (NN.N%) 1 40 ast-stats ---------------------------------------------------------------- -ast-stats Total 7_472 129 +ast-stats Total 7_528 129 ast-stats ================================================================ hir-stats ================================================================ hir-stats HIR STATS: input_stats @@ -85,11 +85,11 @@ hir-stats - Ptr 48 (NN.N%) 1 hir-stats - Ref 48 (NN.N%) 1 hir-stats - Path 624 (NN.N%) 13 hir-stats Generics 560 (NN.N%) 10 56 +hir-stats Pat 400 (NN.N%) 5 80 +hir-stats - Struct 80 (NN.N%) 1 +hir-stats - Wild 80 (NN.N%) 1 +hir-stats - Binding 240 (NN.N%) 3 hir-stats GenericParam 400 (NN.N%) 5 80 -hir-stats Pat 360 (NN.N%) 5 72 -hir-stats - Struct 72 (NN.N%) 1 -hir-stats - Wild 72 (NN.N%) 1 -hir-stats - Binding 216 (NN.N%) 3 hir-stats Block 288 (NN.N%) 6 48 hir-stats GenericBound 256 (NN.N%) 4 64 hir-stats - Trait 256 (NN.N%) 4 @@ -119,5 +119,5 @@ hir-stats TraitItemId 8 (NN.N%) 2 4 hir-stats ImplItemId 8 (NN.N%) 2 4 hir-stats ForeignItemId 4 (NN.N%) 1 4 hir-stats ---------------------------------------------------------------- -hir-stats Total 8_584 173 +hir-stats Total 8_624 173 hir-stats ================================================================ diff --git a/tests/ui/suggestions/multi-suggestion.ascii.stderr b/tests/ui/suggestions/multi-suggestion.ascii.stderr index 1744162e6ce..9c8867a1771 100644 --- a/tests/ui/suggestions/multi-suggestion.ascii.stderr +++ b/tests/ui/suggestions/multi-suggestion.ascii.stderr @@ -118,7 +118,7 @@ LL + let _ = Box::new_zeroed(); LL - let _ = Box {}; LL + let _ = Box::new_in(_, _); | - = and 12 other candidates + = and 13 other candidates help: consider using the `Default` trait | LL - let _ = Box {}; diff --git a/tests/ui/suggestions/multi-suggestion.unicode.stderr b/tests/ui/suggestions/multi-suggestion.unicode.stderr index 4835c263f19..4fdab51493e 100644 --- a/tests/ui/suggestions/multi-suggestion.unicode.stderr +++ b/tests/ui/suggestions/multi-suggestion.unicode.stderr @@ -118,7 +118,7 @@ LL + let _ = Box::new_zeroed(); LL - let _ = Box {}; LL + let _ = Box::new_in(_, _); │ - ╰ and 12 other candidates + ╰ and 13 other candidates help: consider using the `Default` trait ╭╴ LL - let _ = Box {}; diff --git a/tests/ui/target-feature/inline-always.aarch64.stderr b/tests/ui/target-feature/inline-always.aarch64.stderr new file mode 100644 index 00000000000..a9ffb425c52 --- /dev/null +++ b/tests/ui/target-feature/inline-always.aarch64.stderr @@ -0,0 +1,60 @@ +warning: call to `#[inline(always)]`-annotated `target_feature_identity` requires the same target features to be inlined + --> $DIR/inline-always.rs:19:5 + | +LL | target_feature_identity(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: function will not be inlined + = note: the following target features are on `target_feature_identity` but missing from `call_no_target_features`: neon, fp16 +note: `target_feature_identity` is defined here + --> $DIR/inline-always.rs:16:1 + | +LL | pub unsafe fn target_feature_identity() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: `#[warn(inline_always_mismatching_target_features)]` on by default +help: add `#[target_feature]` attribute to `call_no_target_features` + | +LL + #[target_feature(enable = "neon,fp16")] +LL | unsafe fn call_no_target_features() { + | + +warning: call to `#[inline(always)]`-annotated `multiple_target_features` requires the same target features to be inlined + --> $DIR/inline-always.rs:22:5 + | +LL | multiple_target_features(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: function will not be inlined + = note: the following target features are on `multiple_target_features` but missing from `call_no_target_features`: fp16, sve, rdm +note: `multiple_target_features` is defined here + --> $DIR/inline-always.rs:52:1 + | +LL | fn multiple_target_features() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: add `#[target_feature]` attribute to `call_no_target_features` + | +LL + #[target_feature(enable = "fp16,sve,rdm")] +LL | unsafe fn call_no_target_features() { + | + +warning: call to `#[inline(always)]`-annotated `multiple_target_features` requires the same target features to be inlined + --> $DIR/inline-always.rs:28:5 + | +LL | multiple_target_features(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: function will not be inlined + = note: the following target features are on `multiple_target_features` but missing from `call_to_first_set`: rdm +note: `multiple_target_features` is defined here + --> $DIR/inline-always.rs:52:1 + | +LL | fn multiple_target_features() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: add `#[target_feature]` attribute to `call_to_first_set` + | +LL + #[target_feature(enable = "rdm")] +LL | unsafe fn call_to_first_set() { + | + +warning: 3 warnings emitted + diff --git a/tests/ui/target-feature/inline-always.rs b/tests/ui/target-feature/inline-always.rs new file mode 100644 index 00000000000..dbf46537caa --- /dev/null +++ b/tests/ui/target-feature/inline-always.rs @@ -0,0 +1,54 @@ +//@ add-core-stubs +//@ build-pass +//@ compile-flags: --crate-type=lib +//@ revisions: aarch64 +//@[aarch64] compile-flags: --target aarch64-unknown-linux-gnu +//@[aarch64] needs-llvm-components: aarch64 + +#![feature(no_core, target_feature_inline_always)] +#![no_core] + +extern crate minicore; +use minicore::*; + +#[inline(always)] +#[target_feature(enable = "neon,fp16")] +pub unsafe fn target_feature_identity() {} + +unsafe fn call_no_target_features() { + target_feature_identity(); + //~^ WARNING call to `#[inline(always)]`-annotated `target_feature_identity` requires the same target features to be inlined [inline_always_mismatching_target_features] + global_feature_enabled(); + multiple_target_features(); + //~^ WARNING call to `#[inline(always)]`-annotated `multiple_target_features` requires the same target features to be inlined [inline_always_mismatching_target_features] +} + +#[target_feature(enable = "fp16,sve")] +unsafe fn call_to_first_set() { + multiple_target_features(); + //~^ WARNING call to `#[inline(always)]`-annotated `multiple_target_features` requires the same target features to be inlined [inline_always_mismatching_target_features] +} + +/* You can't have "fhm" without "fp16" */ +#[target_feature(enable = "fhm")] +unsafe fn mismatching_features() { + target_feature_identity() +} + +#[target_feature(enable = "fp16")] +unsafe fn matching_target_features() { + target_feature_identity() +} + +#[inline(always)] +#[target_feature(enable = "neon")] +unsafe fn global_feature_enabled() { + +} + +#[inline(always)] +#[target_feature(enable = "fp16,sve")] +#[target_feature(enable="rdm")] +fn multiple_target_features() { + + } diff --git a/tests/ui/target-feature/invalid-attribute.rs b/tests/ui/target-feature/invalid-attribute.rs index b34a48aba26..a958700231a 100644 --- a/tests/ui/target-feature/invalid-attribute.rs +++ b/tests/ui/target-feature/invalid-attribute.rs @@ -61,6 +61,8 @@ trait Baz {} #[inline(always)] //~^ ERROR: cannot use `#[inline(always)]` +//~| NOTE: see issue #145574 <https://github.com/rust-lang/rust/issues/145574> for more information +//~| NOTE: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date #[target_feature(enable = "sse2")] unsafe fn test() {} diff --git a/tests/ui/target-feature/invalid-attribute.stderr b/tests/ui/target-feature/invalid-attribute.stderr index 7b75367b48c..d85bccce441 100644 --- a/tests/ui/target-feature/invalid-attribute.stderr +++ b/tests/ui/target-feature/invalid-attribute.stderr @@ -106,7 +106,7 @@ LL | #[target_feature(enable = "sse2")] = help: `#[target_feature]` can only be applied to functions error: `#[target_feature]` attribute cannot be used on statics - --> $DIR/invalid-attribute.rs:67:1 + --> $DIR/invalid-attribute.rs:69:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -114,7 +114,7 @@ LL | #[target_feature(enable = "sse2")] = help: `#[target_feature]` can only be applied to functions error: `#[target_feature]` attribute cannot be used on trait impl blocks - --> $DIR/invalid-attribute.rs:71:1 + --> $DIR/invalid-attribute.rs:73:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -122,7 +122,7 @@ LL | #[target_feature(enable = "sse2")] = help: `#[target_feature]` can only be applied to functions error: `#[target_feature]` attribute cannot be used on inherent impl blocks - --> $DIR/invalid-attribute.rs:77:1 + --> $DIR/invalid-attribute.rs:79:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -130,7 +130,7 @@ LL | #[target_feature(enable = "sse2")] = help: `#[target_feature]` can only be applied to functions error: `#[target_feature]` attribute cannot be used on expressions - --> $DIR/invalid-attribute.rs:98:5 + --> $DIR/invalid-attribute.rs:100:5 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -138,18 +138,22 @@ LL | #[target_feature(enable = "sse2")] = help: `#[target_feature]` can only be applied to functions error: `#[target_feature]` attribute cannot be used on closures - --> $DIR/invalid-attribute.rs:104:5 + --> $DIR/invalid-attribute.rs:106:5 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: `#[target_feature]` can be applied to methods and functions -error: cannot use `#[inline(always)]` with `#[target_feature]` +error[E0658]: cannot use `#[inline(always)]` with `#[target_feature]` --> $DIR/invalid-attribute.rs:62:1 | LL | #[inline(always)] | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #145574 <https://github.com/rust-lang/rust/issues/145574> for more information + = help: add `#![feature(target_feature_inline_always)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: the feature named `foo` is not valid for this target --> $DIR/invalid-attribute.rs:20:18 @@ -158,7 +162,7 @@ LL | #[target_feature(enable = "foo")] | ^^^^^^^^^^^^^^ `foo` is not valid for this target error[E0046]: not all trait items implemented, missing: `foo` - --> $DIR/invalid-attribute.rs:73:1 + --> $DIR/invalid-attribute.rs:75:1 | LL | impl Quux for u8 {} | ^^^^^^^^^^^^^^^^ missing `foo` in implementation @@ -167,7 +171,7 @@ LL | fn foo(); | --------- `foo` from trait error: `#[target_feature(..)]` cannot be applied to safe trait method - --> $DIR/invalid-attribute.rs:87:5 + --> $DIR/invalid-attribute.rs:89:5 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot be applied to safe trait method @@ -176,13 +180,13 @@ LL | fn foo() {} | -------- not an `unsafe` function error[E0053]: method `foo` has an incompatible type for trait - --> $DIR/invalid-attribute.rs:90:5 + --> $DIR/invalid-attribute.rs:92:5 | LL | fn foo() {} | ^^^^^^^^ expected safe fn, found unsafe fn | note: type in trait - --> $DIR/invalid-attribute.rs:82:5 + --> $DIR/invalid-attribute.rs:84:5 | LL | fn foo(); | ^^^^^^^^^ @@ -190,7 +194,7 @@ LL | fn foo(); found signature `#[target_features] fn()` error: the feature named `+sse2` is not valid for this target - --> $DIR/invalid-attribute.rs:109:18 + --> $DIR/invalid-attribute.rs:111:18 | LL | #[target_feature(enable = "+sse2")] | ^^^^^^^^^^^^^^^^ `+sse2` is not valid for this target @@ -199,5 +203,5 @@ LL | #[target_feature(enable = "+sse2")] error: aborting due to 24 previous errors -Some errors have detailed explanations: E0046, E0053, E0539. +Some errors have detailed explanations: E0046, E0053, E0539, E0658. For more information about an error, try `rustc --explain E0046`. diff --git a/tests/ui/target_modifiers/auxiliary/kcfi-normalize-ints.rs b/tests/ui/target_modifiers/auxiliary/kcfi-normalize-ints.rs new file mode 100644 index 00000000000..f97005a1450 --- /dev/null +++ b/tests/ui/target_modifiers/auxiliary/kcfi-normalize-ints.rs @@ -0,0 +1,7 @@ +//@ no-prefer-dynamic +//@ needs-sanitizer-kcfi +//@ compile-flags: -C panic=abort -Zsanitizer=kcfi -Zsanitizer-cfi-normalize-integers + +#![feature(no_core)] +#![crate_type = "rlib"] +#![no_core] diff --git a/tests/ui/target_modifiers/auxiliary/no-sanitizers.rs b/tests/ui/target_modifiers/auxiliary/no-sanitizers.rs new file mode 100644 index 00000000000..1d47ff6826d --- /dev/null +++ b/tests/ui/target_modifiers/auxiliary/no-sanitizers.rs @@ -0,0 +1,6 @@ +//@ no-prefer-dynamic +//@ compile-flags: -C panic=abort + +#![feature(no_core)] +#![crate_type = "rlib"] +#![no_core] diff --git a/tests/ui/target_modifiers/auxiliary/safestack-and-kcfi.rs b/tests/ui/target_modifiers/auxiliary/safestack-and-kcfi.rs new file mode 100644 index 00000000000..029744d2a1f --- /dev/null +++ b/tests/ui/target_modifiers/auxiliary/safestack-and-kcfi.rs @@ -0,0 +1,10 @@ +//@ no-prefer-dynamic + +//@ needs-sanitizer-kcfi +//@ needs-sanitizer-safestack + +//@ compile-flags: -C panic=abort -Zsanitizer=safestack,kcfi + +#![feature(no_core)] +#![crate_type = "rlib"] +#![no_core] diff --git a/tests/ui/target_modifiers/sanitizer-kcfi-normalize-ints.rs b/tests/ui/target_modifiers/sanitizer-kcfi-normalize-ints.rs new file mode 100644 index 00000000000..cb9f701349a --- /dev/null +++ b/tests/ui/target_modifiers/sanitizer-kcfi-normalize-ints.rs @@ -0,0 +1,18 @@ +// For kCFI, the helper flag -Zsanitizer-cfi-normalize-integers should also be a target modifier. + +//@ needs-sanitizer-kcfi +//@ aux-build:kcfi-normalize-ints.rs +//@ compile-flags: -Cpanic=abort + +//@ revisions: ok wrong_flag wrong_sanitizer +//@[ok] compile-flags: -Zsanitizer=kcfi -Zsanitizer-cfi-normalize-integers +//@[wrong_flag] compile-flags: -Zsanitizer=kcfi +//@[ok] check-pass + +#![feature(no_core)] +//[wrong_flag]~^ ERROR mixing `-Zsanitizer-cfi-normalize-integers` will cause an ABI mismatch in crate `sanitizer_kcfi_normalize_ints` +//[wrong_sanitizer]~^^ ERROR mixing `-Zsanitizer` will cause an ABI mismatch in crate `sanitizer_kcfi_normalize_ints` +#![crate_type = "rlib"] +#![no_core] + +extern crate kcfi_normalize_ints; diff --git a/tests/ui/target_modifiers/sanitizer-kcfi-normalize-ints.wrong_flag.stderr b/tests/ui/target_modifiers/sanitizer-kcfi-normalize-ints.wrong_flag.stderr new file mode 100644 index 00000000000..1db79b025e9 --- /dev/null +++ b/tests/ui/target_modifiers/sanitizer-kcfi-normalize-ints.wrong_flag.stderr @@ -0,0 +1,13 @@ +error: mixing `-Zsanitizer-cfi-normalize-integers` will cause an ABI mismatch in crate `sanitizer_kcfi_normalize_ints` + --> $DIR/sanitizer-kcfi-normalize-ints.rs:12:1 + | +LL | #![feature(no_core)] + | ^ + | + = help: the `-Zsanitizer-cfi-normalize-integers` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely + = note: unset `-Zsanitizer-cfi-normalize-integers` in this crate is incompatible with `-Zsanitizer-cfi-normalize-integers=` in dependency `kcfi_normalize_ints` + = help: set `-Zsanitizer-cfi-normalize-integers=` in this crate or unset `-Zsanitizer-cfi-normalize-integers` in `kcfi_normalize_ints` + = help: if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch=sanitizer-cfi-normalize-integers` to silence this error + +error: aborting due to 1 previous error + diff --git a/tests/ui/target_modifiers/sanitizer-kcfi-normalize-ints.wrong_sanitizer.stderr b/tests/ui/target_modifiers/sanitizer-kcfi-normalize-ints.wrong_sanitizer.stderr new file mode 100644 index 00000000000..5b949c87350 --- /dev/null +++ b/tests/ui/target_modifiers/sanitizer-kcfi-normalize-ints.wrong_sanitizer.stderr @@ -0,0 +1,13 @@ +error: mixing `-Zsanitizer` will cause an ABI mismatch in crate `sanitizer_kcfi_normalize_ints` + --> $DIR/sanitizer-kcfi-normalize-ints.rs:12:1 + | +LL | #![feature(no_core)] + | ^ + | + = help: the `-Zsanitizer` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely + = note: unset `-Zsanitizer` in this crate is incompatible with `-Zsanitizer=kcfi` in dependency `kcfi_normalize_ints` + = help: set `-Zsanitizer=kcfi` in this crate or unset `-Zsanitizer` in `kcfi_normalize_ints` + = help: if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch=sanitizer` to silence this error + +error: aborting due to 1 previous error + diff --git a/tests/ui/target_modifiers/sanitizers-good-for-inconsistency.rs b/tests/ui/target_modifiers/sanitizers-good-for-inconsistency.rs new file mode 100644 index 00000000000..abda7be9e40 --- /dev/null +++ b/tests/ui/target_modifiers/sanitizers-good-for-inconsistency.rs @@ -0,0 +1,19 @@ +// AddressSanitizer, LeakSanitizer are good to be inconsistent (they are not a target modifiers) + +//@ revisions: wrong_address_san wrong_leak_san + +//@[wrong_address_san] needs-sanitizer-address +//@[wrong_leak_san] needs-sanitizer-leak + +//@ aux-build:no-sanitizers.rs +//@ compile-flags: -Cpanic=abort -C target-feature=-crt-static + +//@[wrong_address_san] compile-flags: -Zsanitizer=address +//@[wrong_leak_san] compile-flags: -Zsanitizer=leak +//@ check-pass + +#![feature(no_core)] +#![crate_type = "rlib"] +#![no_core] + +extern crate no_sanitizers; diff --git a/tests/ui/target_modifiers/sanitizers-safestack-and-kcfi.missed_both.stderr b/tests/ui/target_modifiers/sanitizers-safestack-and-kcfi.missed_both.stderr new file mode 100644 index 00000000000..440a91c7707 --- /dev/null +++ b/tests/ui/target_modifiers/sanitizers-safestack-and-kcfi.missed_both.stderr @@ -0,0 +1,13 @@ +error: mixing `-Zsanitizer` will cause an ABI mismatch in crate `sanitizers_safestack_and_kcfi` + --> $DIR/sanitizers-safestack-and-kcfi.rs:16:1 + | +LL | #![feature(no_core)] + | ^ + | + = help: the `-Zsanitizer` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely + = note: unset `-Zsanitizer` in this crate is incompatible with `-Zsanitizer=safestack,kcfi` in dependency `safestack_and_kcfi` + = help: set `-Zsanitizer=safestack,kcfi` in this crate or unset `-Zsanitizer` in `safestack_and_kcfi` + = help: if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch=sanitizer` to silence this error + +error: aborting due to 1 previous error + diff --git a/tests/ui/target_modifiers/sanitizers-safestack-and-kcfi.missed_kcfi.stderr b/tests/ui/target_modifiers/sanitizers-safestack-and-kcfi.missed_kcfi.stderr new file mode 100644 index 00000000000..6cdd7facc37 --- /dev/null +++ b/tests/ui/target_modifiers/sanitizers-safestack-and-kcfi.missed_kcfi.stderr @@ -0,0 +1,13 @@ +error: mixing `-Zsanitizer` will cause an ABI mismatch in crate `sanitizers_safestack_and_kcfi` + --> $DIR/sanitizers-safestack-and-kcfi.rs:16:1 + | +LL | #![feature(no_core)] + | ^ + | + = help: the `-Zsanitizer` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely + = note: `-Zsanitizer=safestack` in this crate is incompatible with `-Zsanitizer=safestack,kcfi` in dependency `safestack_and_kcfi` + = help: set `-Zsanitizer=safestack,kcfi` in this crate or `-Zsanitizer=safestack` in `safestack_and_kcfi` + = help: if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch=sanitizer` to silence this error + +error: aborting due to 1 previous error + diff --git a/tests/ui/target_modifiers/sanitizers-safestack-and-kcfi.missed_safestack.stderr b/tests/ui/target_modifiers/sanitizers-safestack-and-kcfi.missed_safestack.stderr new file mode 100644 index 00000000000..ecfbcace39f --- /dev/null +++ b/tests/ui/target_modifiers/sanitizers-safestack-and-kcfi.missed_safestack.stderr @@ -0,0 +1,13 @@ +error: mixing `-Zsanitizer` will cause an ABI mismatch in crate `sanitizers_safestack_and_kcfi` + --> $DIR/sanitizers-safestack-and-kcfi.rs:16:1 + | +LL | #![feature(no_core)] + | ^ + | + = help: the `-Zsanitizer` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely + = note: `-Zsanitizer=kcfi` in this crate is incompatible with `-Zsanitizer=safestack,kcfi` in dependency `safestack_and_kcfi` + = help: set `-Zsanitizer=safestack,kcfi` in this crate or `-Zsanitizer=kcfi` in `safestack_and_kcfi` + = help: if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch=sanitizer` to silence this error + +error: aborting due to 1 previous error + diff --git a/tests/ui/target_modifiers/sanitizers-safestack-and-kcfi.rs b/tests/ui/target_modifiers/sanitizers-safestack-and-kcfi.rs new file mode 100644 index 00000000000..6c3ceb7e910 --- /dev/null +++ b/tests/ui/target_modifiers/sanitizers-safestack-and-kcfi.rs @@ -0,0 +1,23 @@ +//@ needs-sanitizer-kcfi +//@ needs-sanitizer-safestack + +//@ aux-build:safestack-and-kcfi.rs +//@ compile-flags: -Cpanic=abort + +//@ revisions: good good_reverted missed_safestack missed_kcfi missed_both +//@[good] compile-flags: -Zsanitizer=safestack,kcfi +//@[good_reverted] compile-flags: -Zsanitizer=kcfi,safestack +//@[missed_safestack] compile-flags: -Zsanitizer=kcfi +//@[missed_kcfi] compile-flags: -Zsanitizer=safestack +// [missed_both] no additional compile-flags: +//@[good] check-pass +//@[good_reverted] check-pass + +#![feature(no_core)] +//[missed_safestack]~^ ERROR mixing `-Zsanitizer` will cause an ABI mismatch in crate `sanitizers_safestack_and_kcfi` +//[missed_kcfi]~^^ ERROR mixing `-Zsanitizer` will cause an ABI mismatch in crate `sanitizers_safestack_and_kcfi` +//[missed_both]~^^^ ERROR mixing `-Zsanitizer` will cause an ABI mismatch in crate `sanitizers_safestack_and_kcfi` +#![crate_type = "rlib"] +#![no_core] + +extern crate safestack_and_kcfi; diff --git a/tests/ui/traits/bound/suggest-complex-bound-on-method.fixed b/tests/ui/traits/bound/suggest-complex-bound-on-method.fixed new file mode 100644 index 00000000000..719835af0a9 --- /dev/null +++ b/tests/ui/traits/bound/suggest-complex-bound-on-method.fixed @@ -0,0 +1,23 @@ +//@ run-rustfix +#![allow(dead_code)] +struct Application; +// https://github.com/rust-lang/rust/issues/144734 +trait Trait { + type Error: std::error::Error; + + fn run(&self) -> Result<(), Self::Error>; +} + +#[derive(Debug)] +enum ApplicationError { + Quit, +} + +impl Application { + fn thing<T: Trait>(&self, t: T) -> Result<(), ApplicationError> where ApplicationError: From<<T as Trait>::Error> { + t.run()?; //~ ERROR E0277 + Ok(()) + } +} + +fn main() {} diff --git a/tests/ui/traits/bound/suggest-complex-bound-on-method.rs b/tests/ui/traits/bound/suggest-complex-bound-on-method.rs new file mode 100644 index 00000000000..39cec571242 --- /dev/null +++ b/tests/ui/traits/bound/suggest-complex-bound-on-method.rs @@ -0,0 +1,23 @@ +//@ run-rustfix +#![allow(dead_code)] +struct Application; +// https://github.com/rust-lang/rust/issues/144734 +trait Trait { + type Error: std::error::Error; + + fn run(&self) -> Result<(), Self::Error>; +} + +#[derive(Debug)] +enum ApplicationError { + Quit, +} + +impl Application { + fn thing<T: Trait>(&self, t: T) -> Result<(), ApplicationError> { + t.run()?; //~ ERROR E0277 + Ok(()) + } +} + +fn main() {} diff --git a/tests/ui/traits/bound/suggest-complex-bound-on-method.stderr b/tests/ui/traits/bound/suggest-complex-bound-on-method.stderr new file mode 100644 index 00000000000..ac45f049541 --- /dev/null +++ b/tests/ui/traits/bound/suggest-complex-bound-on-method.stderr @@ -0,0 +1,22 @@ +error[E0277]: `?` couldn't convert the error to `ApplicationError` + --> $DIR/suggest-complex-bound-on-method.rs:18:16 + | +LL | t.run()?; + | -----^ the trait `From<<T as Trait>::Error>` is not implemented for `ApplicationError` + | | + | this can't be annotated with `?` because it has type `Result<_, <T as Trait>::Error>` + | +note: `ApplicationError` needs to implement `From<<T as Trait>::Error>` + --> $DIR/suggest-complex-bound-on-method.rs:12:1 + | +LL | enum ApplicationError { + | ^^^^^^^^^^^^^^^^^^^^^ + = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait +help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement + | +LL | fn thing<T: Trait>(&self, t: T) -> Result<(), ApplicationError> where ApplicationError: From<<T as Trait>::Error> { + | +++++++++++++++++++++++++++++++++++++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/const-traits/constructor-const-fn.rs b/tests/ui/traits/const-traits/constructor-const-fn.rs index 4a7bf531df2..af40ad94d1c 100644 --- a/tests/ui/traits/const-traits/constructor-const-fn.rs +++ b/tests/ui/traits/const-traits/constructor-const-fn.rs @@ -1,7 +1,7 @@ //@ check-pass //@ compile-flags: -Znext-solver #![feature(const_trait_impl)] -const fn impls_fn<F: ~const Fn(u32) -> Foo>(_: &F) {} +const fn impls_fn<F: [const] Fn(u32) -> Foo>(_: &F) {} struct Foo(u32); diff --git a/tests/ui/traits/const-traits/enforce-deref-on-adjust.rs b/tests/ui/traits/const-traits/enforce-deref-on-adjust.rs index d5240b7e18d..cba207f0953 100644 --- a/tests/ui/traits/const-traits/enforce-deref-on-adjust.rs +++ b/tests/ui/traits/const-traits/enforce-deref-on-adjust.rs @@ -1,6 +1,6 @@ //@ check-pass -#![feature(const_deref)] +#![feature(const_convert)] #![feature(const_trait_impl)] use std::ops::Deref; diff --git a/tests/ui/traits/const-traits/non-const-op-in-closure-in-const.rs b/tests/ui/traits/const-traits/non-const-op-in-closure-in-const.rs index d5f80acc15b..50c985096d1 100644 --- a/tests/ui/traits/const-traits/non-const-op-in-closure-in-const.rs +++ b/tests/ui/traits/const-traits/non-const-op-in-closure-in-const.rs @@ -1,4 +1,4 @@ -#![feature(const_trait_impl, const_from)] +#![feature(const_trait_impl, const_convert)] //@ check-pass diff --git a/tests/ui/traits/const-traits/reservation-impl-ice.rs b/tests/ui/traits/const-traits/reservation-impl-ice.rs index efaea1cc6b2..3fbfe8c950d 100644 --- a/tests/ui/traits/const-traits/reservation-impl-ice.rs +++ b/tests/ui/traits/const-traits/reservation-impl-ice.rs @@ -1,7 +1,7 @@ //@ compile-flags: -Znext-solver -#![feature(const_from, never_type, const_trait_impl)] +#![feature(const_convert, never_type, const_trait_impl)] -const fn impls_from<T: ~const From<!>>() {} +const fn impls_from<T: [const] From<!>>() {} const fn foo() { // This previously ICE'd when encountering the reservation impl diff --git a/tests/ui/traits/const-traits/reservation-impl-ice.stderr b/tests/ui/traits/const-traits/reservation-impl-ice.stderr index a3fdcbac69e..d30c014d63f 100644 --- a/tests/ui/traits/const-traits/reservation-impl-ice.stderr +++ b/tests/ui/traits/const-traits/reservation-impl-ice.stderr @@ -17,8 +17,8 @@ LL | impls_from::<()>(); note: required by a bound in `impls_from` --> $DIR/reservation-impl-ice.rs:4:24 | -LL | const fn impls_from<T: ~const From<!>>() {} - | ^^^^^^^^^^^^^^ required by this bound in `impls_from` +LL | const fn impls_from<T: [const] From<!>>() {} + | ^^^^^^^^^^^^^^^ required by this bound in `impls_from` error: aborting due to 1 previous error diff --git a/tests/ui/type/pattern_types/validity.stderr b/tests/ui/type/pattern_types/validity.stderr index b545cd75ddb..e19915a58a3 100644 --- a/tests/ui/type/pattern_types/validity.stderr +++ b/tests/ui/type/pattern_types/validity.stderr @@ -4,7 +4,7 @@ error[E0080]: constructing invalid value: encountered 0, but expected something LL | const BAD: pattern_type!(u32 is 1..) = unsafe { std::mem::transmute(0) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { HEX_DUMP } @@ -34,7 +34,7 @@ error[E0080]: constructing invalid value at .0: encountered 0, but expected some LL | const BAD_AGGREGATE: (pattern_type!(u32 is 1..), u32) = (unsafe { std::mem::transmute(0) }, 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { HEX_DUMP } @@ -45,7 +45,7 @@ error[E0080]: constructing invalid value at .0.0: encountered 0, but expected so LL | const BAD_FOO: Foo = Foo(Bar(unsafe { std::mem::transmute(0) })); | ^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { HEX_DUMP } @@ -66,7 +66,7 @@ error[E0080]: constructing invalid value: encountered 97, but expected something LL | const CHAR_OOB_PAT: pattern_type!(char is 'A'..'Z') = unsafe { std::mem::transmute('a') }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { HEX_DUMP } @@ -77,7 +77,7 @@ error[E0080]: constructing invalid value: encountered 0xffffffff, but expected a LL | const CHAR_OOB: pattern_type!(char is 'A'..'Z') = unsafe { std::mem::transmute(u32::MAX) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { HEX_DUMP } diff --git a/tests/ui/unstable-feature-bound/unstable_feature_bound_incompatible_stability.rs b/tests/ui/unstable-feature-bound/unstable_feature_bound_incompatible_stability.rs index 1a9652c1023..5a683884238 100644 --- a/tests/ui/unstable-feature-bound/unstable_feature_bound_incompatible_stability.rs +++ b/tests/ui/unstable-feature-bound/unstable_feature_bound_incompatible_stability.rs @@ -9,6 +9,6 @@ #[stable(feature = "a", since = "1.1.1")] #[unstable_feature_bound(feat_bar)] fn bar() {} -//~^ ERROR Item annotated with `#[unstable_feature_bound]` should not be stable +//~^ ERROR item annotated with `#[unstable_feature_bound]` should not be stable fn main() {} diff --git a/tests/ui/unstable-feature-bound/unstable_feature_bound_incompatible_stability.stderr b/tests/ui/unstable-feature-bound/unstable_feature_bound_incompatible_stability.stderr index 9cb6a181bef..9f07e63e454 100644 --- a/tests/ui/unstable-feature-bound/unstable_feature_bound_incompatible_stability.stderr +++ b/tests/ui/unstable-feature-bound/unstable_feature_bound_incompatible_stability.stderr @@ -1,4 +1,4 @@ -error: Item annotated with `#[unstable_feature_bound]` should not be stable +error: item annotated with `#[unstable_feature_bound]` should not be stable --> $DIR/unstable_feature_bound_incompatible_stability.rs:11:1 | LL | fn bar() {} diff --git a/triagebot.toml b/triagebot.toml index 3b7b216c0d2..bf9bfee4306 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -235,7 +235,6 @@ trigger_files = [ "src/etc/htmldocck.py", "src/tools/jsondocck", "src/tools/jsondoclint", - "src/tools/rustdoc-gui", "src/tools/rustdoc-js", "src/tools/rustdoc-themes", @@ -336,6 +335,7 @@ trigger_files = [ "tests/mir-opt", "tests/pretty", "tests/run-make", + "tests/run-make-cargo", "tests/ui", "tests/ui-fulldeps", ] @@ -380,6 +380,7 @@ trigger_files = [ [autolabel."O-apple"] trigger_files = [ "library/std/src/os/darwin", + "library/std/src/sys/platform_version/darwin", "library/std/src/sys/sync/thread_parking/darwin.rs", "compiler/rustc_target/src/spec/base/apple", ] @@ -450,7 +451,8 @@ trigger_files = [ [autolabel."O-wasi"] trigger_files = [ - "library/std/src/sys/pal/wasi", + "library/std/src/sys/pal/wasip1", + "library/std/src/sys/pal/wasip2", "library/std/src/os/wasi" ] @@ -591,6 +593,7 @@ trigger_files = [ [autolabel."A-run-make"] trigger_files = [ "tests/run-make", + "tests/run-make-cargo", "src/tools/run-make-support" ] @@ -1305,6 +1308,21 @@ cc = ["@m-ou-se"] [mentions."compiler/rustc_ast_lowering/src/format.rs"] cc = ["@m-ou-se"] +[mentions."#[miri::intrinsic_fallback_is_spec]"] +type = "content" +message = """ +`#[miri::intrinsic_fallback_is_spec]` must only be used if the function actively checks for all UB cases, +and explores the possible non-determinism of the intrinsic. +""" +cc = ["@rust-lang/miri"] +[mentions."#[rustc_allow_const_fn_unstable"] +type = "content" +message = """ +`#[rustc_allow_const_fn_unstable]` needs careful audit to avoid accidentally exposing unstable +implementation details on stable. +""" +cc = ["@rust-lang/wg-const-eval"] + # ------------------------------------------------------------------------------ # PR assignments @@ -1526,6 +1544,7 @@ dep-bumps = [ "/src/rustdoc-json-types" = ["rustdoc"] "/src/stage0" = ["bootstrap"] "/tests/run-make" = ["@jieyouxu"] +"/tests/run-make-cargo" = ["@jieyouxu"] "/tests/rustdoc" = ["rustdoc"] "/tests/rustdoc-gui" = ["rustdoc"] "/tests/rustdoc-js-std" = ["rustdoc"] |
