diff options
| author | Ralf Jung <post@ralfj.de> | 2025-08-18 07:19:57 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-08-18 07:19:57 +0000 |
| commit | 909d3297af98c4a3af35c1bb910b4306452d9599 (patch) | |
| tree | eb7c072ff377c4e688afdcc6a772c8a683cf1367 | |
| parent | fb8ac986b178ed977c3334ad27198b787e1e6140 (diff) | |
| parent | 5a3871d7b2521f9c6edbdcbc24d381d6fdef0f6d (diff) | |
| download | rust-909d3297af98c4a3af35c1bb910b4306452d9599.tar.gz rust-909d3297af98c4a3af35c1bb910b4306452d9599.zip | |
Merge pull request #4528 from rust-lang/rustup-2025-08-18
Automatic Rustup
281 files changed, 4760 insertions, 3028 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index df5dda76eb7..8266c03eaa7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -223,6 +223,11 @@ jobs: cd src/ci/citool CARGO_INCREMENTAL=0 CARGO_TARGET_DIR=../../../build/citool cargo build + - name: wait for Windows disk cleanup to finish + if: ${{ matrix.free_disk && startsWith(matrix.os, 'windows-') }} + run: | + python3 src/ci/scripts/free-disk-space-windows-wait.py + - name: run the build run: | set +e diff --git a/Cargo.lock b/Cargo.lock index 8c980616e83..85df0dda818 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -80,9 +80,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.19" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" +checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" dependencies = [ "anstyle", "anstyle-parse", @@ -119,18 +119,18 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "anstyle-svg" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a43964079ef399480603125d5afae2b219aceffb77478956e25f17b9bc3435c" +checksum = "dc03a770ef506fe1396c0e476120ac0e6523cf14b74218dd5f18cd6833326fa9" dependencies = [ "anstyle", "anstyle-lossy", @@ -141,13 +141,13 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "3.0.9" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -353,9 +353,9 @@ checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" [[package]] name = "camino" -version = "1.1.10" +version = "1.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0da45bc31171d8d6960122e222a67740df867c1dd53b4d51caa297084c185cab" +checksum = "5d07aa9a93b00c76f71bc35d598bed923f6d4f3a9ca5c24b7737ae1a292841c0" dependencies = [ "serde", ] @@ -518,9 +518,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.42" +version = "4.5.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed87a9d530bb41a67537289bafcac159cb3ee28460e0a4571123d2a778a6a882" +checksum = "50fd97c9dc2399518aa331917ac6f274280ec5eb34e555dd291899745c48ec6f" dependencies = [ "clap_builder", "clap_derive", @@ -538,9 +538,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.42" +version = "4.5.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64f4f3f3c77c94aff3c7e9aac9a2ca1974a5adf392a8bb751e827d6d127ab966" +checksum = "c35b5830294e1fa0462034af85cc95225a4cb07092c088c55bda3147cfcd8f65" dependencies = [ "anstream", "anstyle", @@ -937,9 +937,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.161" +version = "1.0.166" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3523cc02ad831111491dd64b27ad999f1ae189986728e477604e61b81f828df" +checksum = "b5287274dfdf7e7eaa3d97d460eb2a94922539e6af214bda423f292105011ee2" dependencies = [ "cc", "cxxbridge-cmd", @@ -951,9 +951,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.161" +version = "1.0.166" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212b754247a6f07b10fa626628c157593f0abf640a3dd04cce2760eca970f909" +checksum = "65f3ce027a744135db10a1ebffa0863dab685aeef48f40a02c201f5e70c667d3" dependencies = [ "cc", "codespan-reporting", @@ -966,9 +966,9 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "1.0.161" +version = "1.0.166" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f426a20413ec2e742520ba6837c9324b55ffac24ead47491a6e29f933c5b135a" +checksum = "a07dc23f2eea4774297f4c9a17ae4065fecb63127da556e6c9fadb0216d93595" dependencies = [ "clap", "codespan-reporting", @@ -980,15 +980,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.161" +version = "1.0.166" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258b6069020b4e5da6415df94a50ee4f586a6c38b037a180e940a43d06a070d" +checksum = "f7a4dbad6171f763c4066c83dcd27546b6e93c5c5ae2229f9813bda7233f571d" [[package]] name = "cxxbridge-macro" -version = "1.0.161" +version = "1.0.166" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8dec184b52be5008d6eaf7e62fc1802caf1ad1227d11b3b7df2c409c7ffc3f4" +checksum = "a9be4b527950fc42db06163705e78e73eedc8fd723708e942afe3572a9a2c366" dependencies = [ "indexmap", "proc-macro2", @@ -1055,9 +1055,9 @@ version = "0.1.91" [[package]] name = "derive-where" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "510c292c8cf384b1a340b816a9a6cf2599eb8f566a44949024af88418000c50b" +checksum = "ef941ded77d15ca19b40374869ac6000af1c9f2a4c0f3d4c70926287e6364a8f" dependencies = [ "proc-macro2", "quote", @@ -1567,9 +1567,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.4" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", @@ -1688,7 +1688,7 @@ dependencies = [ "potential_utf", "yoke 0.8.0", "zerofrom", - "zerovec 0.11.2", + "zerovec 0.11.4", ] [[package]] @@ -1721,7 +1721,7 @@ dependencies = [ "litemap 0.8.0", "tinystr 0.8.1", "writeable 0.6.1", - "zerovec 0.11.2", + "zerovec 0.11.4", ] [[package]] @@ -1769,7 +1769,7 @@ dependencies = [ "icu_properties", "icu_provider 2.0.0", "smallvec", - "zerovec 0.11.2", + "zerovec 0.11.4", ] [[package]] @@ -1791,7 +1791,7 @@ dependencies = [ "icu_provider 2.0.0", "potential_utf", "zerotrie", - "zerovec 0.11.2", + "zerovec 0.11.4", ] [[package]] @@ -1831,7 +1831,7 @@ dependencies = [ "yoke 0.8.0", "zerofrom", "zerotrie", - "zerovec 0.11.2", + "zerovec 0.11.4", ] [[package]] @@ -1909,9 +1909,9 @@ dependencies = [ [[package]] name = "indenter" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" +checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" [[package]] name = "indexmap" @@ -2971,7 +2971,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" dependencies = [ - "zerovec 0.11.2", + "zerovec 0.11.4", ] [[package]] @@ -3283,7 +3283,7 @@ dependencies = [ "regex", "serde_json", "similar", - "wasmparser 0.219.2", + "wasmparser 0.236.0", ] [[package]] @@ -3623,6 +3623,7 @@ dependencies = [ "rustc_hir", "rustc_incremental", "rustc_index", + "rustc_lint_defs", "rustc_macros", "rustc_metadata", "rustc_middle", @@ -4309,7 +4310,6 @@ name = "rustc_monomorphize" version = "0.0.0" dependencies = [ "rustc_abi", - "rustc_ast", "rustc_data_structures", "rustc_errors", "rustc_fluent_macro", @@ -4318,7 +4318,6 @@ dependencies = [ "rustc_middle", "rustc_session", "rustc_span", - "rustc_symbol_mangling", "rustc_target", "serde", "serde_json", @@ -4920,9 +4919,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ruzstd" @@ -4980,9 +4979,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "scratch" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f6280af86e5f559536da57a45ebc84948833b3bee313a7dd25232e09c878a52" +checksum = "d68f2ec51b097e4c1a75b681a8bec621909b5e91f15bb7b840c4f2f7b01148b2" [[package]] name = "self_cell" @@ -5508,7 +5507,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" dependencies = [ "displaydoc", - "zerovec 0.11.2", + "zerovec 0.11.4", ] [[package]] @@ -6854,9 +6853,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.2" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" dependencies = [ "yoke 0.8.0", "zerofrom", diff --git a/bootstrap.example.toml b/bootstrap.example.toml index 89da6eeb531..4c18d5f8675 100644 --- a/bootstrap.example.toml +++ b/bootstrap.example.toml @@ -9,7 +9,7 @@ # a custom configuration file can also be specified with `--config` to the build # system. # -# Note that the following are equivelent, for more details see <https://toml.io/en/v1.0.0>. +# Note that the following are equivalent, for more details see <https://toml.io/en/v1.0.0>. # # build.verbose = 1 # @@ -345,9 +345,9 @@ # want to use vendoring. See https://forge.rust-lang.org/infra/other-installation-methods.html#source-code. #build.vendor = if "is a tarball source" && "vendor" dir exists && ".cargo/config.toml" file exists { true } else { false } -# Typically the build system will build the Rust compiler twice. The second -# compiler, however, will simply use its own libraries to link against. If you -# would rather to perform a full bootstrap, compiling the compiler three times, +# If you build the compiler more than twice (stage3+) or the standard library more than once +# (stage 2+), the third compiler and second library will get uplifted from stage2 and stage1, +# respectively. If you would like to disable this uplifting, and rather perform a full bootstrap, # then you can set this option to true. # # This is only useful for verifying that rustc generates reproducible builds. @@ -482,7 +482,7 @@ # Use `--extra-checks=''` to temporarily disable all extra checks. # # Automatically enabled in the "tools" profile. -# Set to the empty string to force disable (recommeded for hdd systems). +# Set to the empty string to force disable (recommended for hdd systems). #build.tidy-extra-checks = "" # Indicates whether ccache is used when building certain artifacts (e.g. LLVM). diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 340a1a239c5..73cbcdd30ce 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -17,7 +17,7 @@ ast_passes_abi_must_not_have_parameters_or_return_type= ast_passes_abi_must_not_have_return_type= invalid signature for `extern {$abi}` function - .note = functions with the "custom" ABI cannot have a return type + .note = functions with the {$abi} ABI cannot have a return type .help = remove the return type ast_passes_assoc_const_without_body = @@ -32,6 +32,13 @@ ast_passes_assoc_type_without_body = associated type in `impl` without body .suggestion = provide a definition for the type +ast_passes_async_fn_in_const_trait_or_trait_impl = + async functions are not allowed in `const` {$in_impl -> + [true] trait impls + *[false] traits + } + .label = associated functions of `const` cannot be declared `async` + ast_passes_at_least_one_trait = at least one trait must be specified ast_passes_auto_generic = auto traits cannot have generic parameters diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index f62b8d1d576..0c72f319007 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -293,6 +293,21 @@ impl<'a> AstValidator<'a> { }); } + fn check_async_fn_in_const_trait_or_impl(&self, sig: &FnSig, parent: &TraitOrTraitImpl) { + let Some(const_keyword) = parent.constness() else { return }; + + let Some(CoroutineKind::Async { span: async_keyword, .. }) = sig.header.coroutine_kind + else { + return; + }; + + self.dcx().emit_err(errors::AsyncFnInConstTraitOrTraitImpl { + async_keyword, + in_impl: matches!(parent, TraitOrTraitImpl::TraitImpl { .. }), + const_keyword, + }); + } + fn check_fn_decl(&self, fn_decl: &FnDecl, self_semantic: SelfSemantic) { self.check_decl_num_args(fn_decl); self.check_decl_cvariadic_pos(fn_decl); @@ -390,7 +405,13 @@ impl<'a> AstValidator<'a> { if let InterruptKind::X86 = interrupt_kind { // "x86-interrupt" is special because it does have arguments. // FIXME(workingjubilee): properly lint on acceptable input types. - if let FnRetTy::Ty(ref ret_ty) = sig.decl.output { + if let FnRetTy::Ty(ref ret_ty) = sig.decl.output + && match &ret_ty.kind { + TyKind::Never => false, + TyKind::Tup(tup) if tup.is_empty() => false, + _ => true, + } + { self.dcx().emit_err(errors::AbiMustNotHaveReturnType { span: ret_ty.span, abi, @@ -449,7 +470,13 @@ impl<'a> AstValidator<'a> { fn reject_params_or_return(&self, abi: ExternAbi, ident: &Ident, sig: &FnSig) { let mut spans: Vec<_> = sig.decl.inputs.iter().map(|p| p.span).collect(); - if let FnRetTy::Ty(ref ret_ty) = sig.decl.output { + if let FnRetTy::Ty(ref ret_ty) = sig.decl.output + && match &ret_ty.kind { + TyKind::Never => false, + TyKind::Tup(tup) if tup.is_empty() => false, + _ => true, + } + { spans.push(ret_ty.span); } @@ -1566,6 +1593,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.visibility_not_permitted(&item.vis, errors::VisibilityNotPermittedNote::TraitImpl); if let AssocItemKind::Fn(box Fn { sig, .. }) = &item.kind { self.check_trait_fn_not_const(sig.header.constness, parent); + self.check_async_fn_in_const_trait_or_impl(sig, parent); } } diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index 1cb2493afe8..5ecc0d21411 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -63,6 +63,16 @@ pub(crate) struct TraitFnConst { } #[derive(Diagnostic)] +#[diag(ast_passes_async_fn_in_const_trait_or_trait_impl)] +pub(crate) struct AsyncFnInConstTraitOrTraitImpl { + #[primary_span] + pub async_keyword: Span, + pub in_impl: bool, + #[label] + pub const_keyword: Span, +} + +#[derive(Diagnostic)] #[diag(ast_passes_forbidden_bound)] pub(crate) struct ForbiddenBound { #[primary_span] diff --git a/compiler/rustc_borrowck/src/handle_placeholders.rs b/compiler/rustc_borrowck/src/handle_placeholders.rs index 34599ac55b8..1614c112ab5 100644 --- a/compiler/rustc_borrowck/src/handle_placeholders.rs +++ b/compiler/rustc_borrowck/src/handle_placeholders.rs @@ -103,6 +103,10 @@ impl RegionTracker { self.max_nameable_universe } + pub(crate) fn max_placeholder_universe_reached(self) -> UniverseIndex { + self.max_placeholder_universe_reached + } + fn merge_min_max_seen(&mut self, other: &Self) { self.max_placeholder_universe_reached = std::cmp::max( self.max_placeholder_universe_reached, diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index b0c31ac9601..c76c5c17431 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -713,7 +713,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // If the member region lives in a higher universe, we currently choose // the most conservative option by leaving it unchanged. - if !self.max_nameable_universe(scc).is_root() { + if !self.max_placeholder_universe_reached(scc).is_root() { return; } @@ -1376,6 +1376,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.scc_annotations[scc].max_nameable_universe() } + pub(crate) fn max_placeholder_universe_reached( + &self, + scc: ConstraintSccIndex, + ) -> UniverseIndex { + self.scc_annotations[scc].max_placeholder_universe_reached() + } + /// Checks the final value for the free region `fr` to see if it /// grew too large. In particular, examine what `end(X)` points /// wound up in `fr`'s final value; for each `end(X)` where `X != diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index eb3c40cc593..358c0d3db46 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -222,6 +222,15 @@ builtin_macros_format_unused_args = multiple unused formatting arguments builtin_macros_format_use_positional = consider using a positional formatting argument instead +builtin_macros_derive_from_wrong_target = `#[derive(From)]` used on {$kind} + +builtin_macros_derive_from_wrong_field_count = `#[derive(From)]` used on a struct with {$multiple_fields -> + [true] multiple fields + *[false] no fields +} + +builtin_macros_derive_from_usage_note = `#[derive(From)]` can only be used on structs with exactly one field + builtin_macros_multiple_default_attrs = multiple `#[default]` attributes .note = only one `#[default]` attribute is needed .label = `#[default]` used here diff --git a/compiler/rustc_builtin_macros/src/assert.rs b/compiler/rustc_builtin_macros/src/assert.rs index 855da5caa31..013258a1b4e 100644 --- a/compiler/rustc_builtin_macros/src/assert.rs +++ b/compiler/rustc_builtin_macros/src/assert.rs @@ -1,8 +1,8 @@ mod context; -use rustc_ast::token::Delimiter; +use rustc_ast::token::{self, Delimiter}; use rustc_ast::tokenstream::{DelimSpan, TokenStream}; -use rustc_ast::{DelimArgs, Expr, ExprKind, MacCall, Path, PathSegment, UnOp, token}; +use rustc_ast::{DelimArgs, Expr, ExprKind, MacCall, Path, PathSegment}; use rustc_ast_pretty::pprust; use rustc_errors::PResult; use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult}; @@ -29,7 +29,7 @@ pub(crate) fn expand_assert<'cx>( // `core::panic` and `std::panic` are different macros, so we use call-site // context to pick up whichever is currently in scope. - let call_site_span = cx.with_call_site_ctxt(span); + let call_site_span = cx.with_call_site_ctxt(cond_expr.span); let panic_path = || { if use_panic_2021(span) { @@ -63,7 +63,7 @@ pub(crate) fn expand_assert<'cx>( }), })), ); - expr_if_not(cx, call_site_span, cond_expr, then, None) + assert_cond_check(cx, call_site_span, cond_expr, then) } // If `generic_assert` is enabled, generates rich captured outputs // @@ -88,26 +88,33 @@ pub(crate) fn expand_assert<'cx>( )), )], ); - expr_if_not(cx, call_site_span, cond_expr, then, None) + assert_cond_check(cx, call_site_span, cond_expr, then) }; ExpandResult::Ready(MacEager::expr(expr)) } +/// `assert!($cond_expr, $custom_message)` struct Assert { cond_expr: Box<Expr>, custom_message: Option<TokenStream>, } -// if !{ ... } { ... } else { ... } -fn expr_if_not( - cx: &ExtCtxt<'_>, - span: Span, - cond: Box<Expr>, - then: Box<Expr>, - els: Option<Box<Expr>>, -) -> Box<Expr> { - cx.expr_if(span, cx.expr(span, ExprKind::Unary(UnOp::Not, cond)), then, els) +/// `match <cond> { true => {} _ => <then> }` +fn assert_cond_check(cx: &ExtCtxt<'_>, span: Span, cond: Box<Expr>, then: Box<Expr>) -> Box<Expr> { + // Instead of expanding to `if !<cond> { <then> }`, we expand to + // `match <cond> { true => {} _ => <then> }`. + // This allows us to always complain about mismatched types instead of "cannot apply unary + // operator `!` to type `X`" when passing an invalid `<cond>`, while also allowing `<cond>` to + // be `&true`. + let els = cx.expr_block(cx.block(span, thin_vec![])); + let mut arms = thin_vec![]; + arms.push(cx.arm(span, cx.pat_lit(span, cx.expr_bool(span, true)), els)); + arms.push(cx.arm(span, cx.pat_wild(span), then)); + + // We wrap the `match` in a statement to limit the length of any borrows introduced in the + // condition. + cx.expr_block(cx.block(span, [cx.stmt_expr(cx.expr_match(span, cond, arms))].into())) } fn parse_assert<'a>(cx: &ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PResult<'a, Assert> { diff --git a/compiler/rustc_builtin_macros/src/autodiff.rs b/compiler/rustc_builtin_macros/src/autodiff.rs index a662840eda5..48d0795af5e 100644 --- a/compiler/rustc_builtin_macros/src/autodiff.rs +++ b/compiler/rustc_builtin_macros/src/autodiff.rs @@ -15,11 +15,12 @@ mod llvm_enzyme { use rustc_ast::tokenstream::*; use rustc_ast::visit::AssocCtxt::*; use rustc_ast::{ - self as ast, AssocItemKind, BindingMode, ExprKind, FnRetTy, FnSig, Generics, ItemKind, - MetaItemInner, PatKind, QSelf, TyKind, Visibility, + self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AssocItemKind, BindingMode, + FnRetTy, FnSig, GenericArg, GenericArgs, GenericParamKind, Generics, ItemKind, + MetaItemInner, PatKind, Path, PathSegment, TyKind, Visibility, }; use rustc_expand::base::{Annotatable, ExtCtxt}; - use rustc_span::{Ident, Span, Symbol, kw, sym}; + use rustc_span::{Ident, Span, Symbol, sym}; use thin_vec::{ThinVec, thin_vec}; use tracing::{debug, trace}; @@ -179,11 +180,8 @@ mod llvm_enzyme { } /// We expand the autodiff macro to generate a new placeholder function which passes - /// type-checking and can be called by users. The function body of the placeholder function will - /// later be replaced on LLVM-IR level, so the design of the body is less important and for now - /// should just prevent early inlining and optimizations which alter the function signature. - /// The exact signature of the generated function depends on the configuration provided by the - /// user, but here is an example: + /// type-checking and can be called by users. The exact signature of the generated function + /// depends on the configuration provided by the user, but here is an example: /// /// ``` /// #[autodiff(cos_box, Reverse, Duplicated, Active)] @@ -194,19 +192,12 @@ mod llvm_enzyme { /// which becomes expanded to: /// ``` /// #[rustc_autodiff] - /// #[inline(never)] /// fn sin(x: &Box<f32>) -> f32 { /// f32::sin(**x) /// } /// #[rustc_autodiff(Reverse, Duplicated, Active)] - /// #[inline(never)] /// fn cos_box(x: &Box<f32>, dx: &mut Box<f32>, dret: f32) -> f32 { - /// unsafe { - /// asm!("NOP"); - /// }; - /// ::core::hint::black_box(sin(x)); - /// ::core::hint::black_box((dx, dret)); - /// ::core::hint::black_box(sin(x)) + /// std::intrinsics::autodiff(sin::<>, cos_box::<>, (x, dx, dret)) /// } /// ``` /// FIXME(ZuseZ4): Once autodiff is enabled by default, make this a doc comment which is checked @@ -227,16 +218,24 @@ mod llvm_enzyme { // first get information about the annotable item: visibility, signature, name and generic // parameters. // these will be used to generate the differentiated version of the function - let Some((vis, sig, primal, generics)) = (match &item { - Annotatable::Item(iitem) => extract_item_info(iitem), + let Some((vis, sig, primal, generics, impl_of_trait)) = (match &item { + Annotatable::Item(iitem) => { + extract_item_info(iitem).map(|(v, s, p, g)| (v, s, p, g, false)) + } Annotatable::Stmt(stmt) => match &stmt.kind { - ast::StmtKind::Item(iitem) => extract_item_info(iitem), + ast::StmtKind::Item(iitem) => { + extract_item_info(iitem).map(|(v, s, p, g)| (v, s, p, g, false)) + } _ => None, }, - Annotatable::AssocItem(assoc_item, Impl { .. }) => match &assoc_item.kind { - ast::AssocItemKind::Fn(box ast::Fn { sig, ident, generics, .. }) => { - Some((assoc_item.vis.clone(), sig.clone(), ident.clone(), generics.clone())) - } + Annotatable::AssocItem(assoc_item, Impl { of_trait }) => match &assoc_item.kind { + ast::AssocItemKind::Fn(box ast::Fn { sig, ident, generics, .. }) => Some(( + assoc_item.vis.clone(), + sig.clone(), + ident.clone(), + generics.clone(), + *of_trait, + )), _ => None, }, _ => None, @@ -254,7 +253,6 @@ mod llvm_enzyme { }; let has_ret = has_ret(&sig.decl.output); - let sig_span = ecx.with_call_site_ctxt(sig.span); // create TokenStream from vec elemtents: // meta_item doesn't have a .tokens field @@ -323,19 +321,23 @@ mod llvm_enzyme { } let span = ecx.with_def_site_ctxt(expand_span); - let n_active: u32 = x - .input_activity - .iter() - .filter(|a| **a == DiffActivity::Active || **a == DiffActivity::ActiveOnly) - .count() as u32; - let (d_sig, new_args, idents, errored) = gen_enzyme_decl(ecx, &sig, &x, span); - let d_body = gen_enzyme_body( - ecx, &x, n_active, &sig, &d_sig, primal, &new_args, span, sig_span, idents, errored, - &generics, + let d_sig = gen_enzyme_decl(ecx, &sig, &x, span); + + let d_body = ecx.block( + span, + thin_vec![call_autodiff( + ecx, + primal, + first_ident(&meta_item_vec[0]), + span, + &d_sig, + &generics, + impl_of_trait, + )], ); // The first element of it is the name of the function to be generated - let asdf = Box::new(ast::Fn { + let d_fn = Box::new(ast::Fn { defaultness: ast::Defaultness::Final, sig: d_sig, ident: first_ident(&meta_item_vec[0]), @@ -368,7 +370,7 @@ mod llvm_enzyme { let new_id = ecx.sess.psess.attr_id_generator.mk_attr_id(); let inline_never = outer_normal_attr(&inline_never_attr, new_id, span); - // We're avoid duplicating the attributes `#[rustc_autodiff]` and `#[inline(never)]`. + // We're avoid duplicating the attribute `#[rustc_autodiff]`. fn same_attribute(attr: &ast::AttrKind, item: &ast::AttrKind) -> bool { match (attr, item) { (ast::AttrKind::Normal(a), ast::AttrKind::Normal(b)) => { @@ -381,14 +383,16 @@ mod llvm_enzyme { } } + let mut has_inline_never = false; + // Don't add it multiple times: let orig_annotatable: Annotatable = match item { Annotatable::Item(ref mut iitem) => { if !iitem.attrs.iter().any(|a| same_attribute(&a.kind, &attr.kind)) { iitem.attrs.push(attr); } - if !iitem.attrs.iter().any(|a| same_attribute(&a.kind, &inline_never.kind)) { - iitem.attrs.push(inline_never.clone()); + if iitem.attrs.iter().any(|a| same_attribute(&a.kind, &inline_never.kind)) { + has_inline_never = true; } Annotatable::Item(iitem.clone()) } @@ -396,8 +400,8 @@ mod llvm_enzyme { if !assoc_item.attrs.iter().any(|a| same_attribute(&a.kind, &attr.kind)) { assoc_item.attrs.push(attr); } - if !assoc_item.attrs.iter().any(|a| same_attribute(&a.kind, &inline_never.kind)) { - assoc_item.attrs.push(inline_never.clone()); + if assoc_item.attrs.iter().any(|a| same_attribute(&a.kind, &inline_never.kind)) { + has_inline_never = true; } Annotatable::AssocItem(assoc_item.clone(), i) } @@ -407,9 +411,8 @@ mod llvm_enzyme { if !iitem.attrs.iter().any(|a| same_attribute(&a.kind, &attr.kind)) { iitem.attrs.push(attr); } - if !iitem.attrs.iter().any(|a| same_attribute(&a.kind, &inline_never.kind)) - { - iitem.attrs.push(inline_never.clone()); + if iitem.attrs.iter().any(|a| same_attribute(&a.kind, &inline_never.kind)) { + has_inline_never = true; } } _ => unreachable!("stmt kind checked previously"), @@ -428,12 +431,21 @@ mod llvm_enzyme { tokens: ts, }); + let new_id = ecx.sess.psess.attr_id_generator.mk_attr_id(); let d_attr = outer_normal_attr(&rustc_ad_attr, new_id, span); + + // If the source function has the `#[inline(never)]` attribute, we'll also add it to the diff function + let mut d_attrs = thin_vec![d_attr]; + + if has_inline_never { + d_attrs.push(inline_never); + } + let d_annotatable = match &item { Annotatable::AssocItem(_, _) => { - let assoc_item: AssocItemKind = ast::AssocItemKind::Fn(asdf); + let assoc_item: AssocItemKind = ast::AssocItemKind::Fn(d_fn); let d_fn = Box::new(ast::AssocItem { - attrs: thin_vec![d_attr, inline_never], + attrs: d_attrs, id: ast::DUMMY_NODE_ID, span, vis, @@ -443,13 +455,13 @@ mod llvm_enzyme { Annotatable::AssocItem(d_fn, Impl { of_trait: false }) } Annotatable::Item(_) => { - let mut d_fn = ecx.item(span, thin_vec![d_attr, inline_never], ItemKind::Fn(asdf)); + let mut d_fn = ecx.item(span, d_attrs, ItemKind::Fn(d_fn)); d_fn.vis = vis; Annotatable::Item(d_fn) } Annotatable::Stmt(_) => { - let mut d_fn = ecx.item(span, thin_vec![d_attr, inline_never], ItemKind::Fn(asdf)); + let mut d_fn = ecx.item(span, d_attrs, ItemKind::Fn(d_fn)); d_fn.vis = vis; Annotatable::Stmt(Box::new(ast::Stmt { @@ -484,282 +496,95 @@ mod llvm_enzyme { ty } - // Will generate a body of the type: + // Generate `autodiff` intrinsic call // ``` - // { - // unsafe { - // asm!("NOP"); - // } - // ::core::hint::black_box(primal(args)); - // ::core::hint::black_box((args, ret)); - // <This part remains to be done by following function> - // } + // std::intrinsics::autodiff(source, diff, (args)) // ``` - fn init_body_helper( + fn call_autodiff( ecx: &ExtCtxt<'_>, - span: Span, primal: Ident, - new_names: &[String], - sig_span: Span, - new_decl_span: Span, - idents: &[Ident], - errored: bool, + diff: Ident, + span: Span, + d_sig: &FnSig, generics: &Generics, - ) -> (Box<ast::Block>, Box<ast::Expr>, Box<ast::Expr>, Box<ast::Expr>) { - let blackbox_path = ecx.std_path(&[sym::hint, sym::black_box]); - let noop = ast::InlineAsm { - asm_macro: ast::AsmMacro::Asm, - template: vec![ast::InlineAsmTemplatePiece::String("NOP".into())], - template_strs: Box::new([]), - operands: vec![], - clobber_abis: vec![], - options: ast::InlineAsmOptions::PURE | ast::InlineAsmOptions::NOMEM, - line_spans: vec![], - }; - let noop_expr = ecx.expr_asm(span, Box::new(noop)); - let unsf = ast::BlockCheckMode::Unsafe(ast::UnsafeSource::CompilerGenerated); - let unsf_block = ast::Block { - stmts: thin_vec![ecx.stmt_semi(noop_expr)], - id: ast::DUMMY_NODE_ID, - tokens: None, - rules: unsf, + is_impl: bool, + ) -> rustc_ast::Stmt { + let primal_path_expr = gen_turbofish_expr(ecx, primal, generics, span, is_impl); + let diff_path_expr = gen_turbofish_expr(ecx, diff, generics, span, is_impl); + + let tuple_expr = ecx.expr_tuple( span, - }; - let unsf_expr = ecx.expr_block(Box::new(unsf_block)); - let blackbox_call_expr = ecx.expr_path(ecx.path(span, blackbox_path)); - let primal_call = gen_primal_call(ecx, span, primal, idents, generics); - let black_box_primal_call = ecx.expr_call( - new_decl_span, - blackbox_call_expr.clone(), - thin_vec![primal_call.clone()], + d_sig + .decl + .inputs + .iter() + .map(|arg| match arg.pat.kind { + PatKind::Ident(_, ident, _) => ecx.expr_path(ecx.path_ident(span, ident)), + _ => todo!(), + }) + .collect::<ThinVec<_>>() + .into(), ); - let tup_args = new_names - .iter() - .map(|arg| ecx.expr_path(ecx.path_ident(span, Ident::from_str(arg)))) - .collect(); - let black_box_remaining_args = ecx.expr_call( - sig_span, - blackbox_call_expr.clone(), - thin_vec![ecx.expr_tuple(sig_span, tup_args)], + let enzyme_path_idents = ecx.std_path(&[sym::intrinsics, sym::autodiff]); + let enzyme_path = ecx.path(span, enzyme_path_idents); + let call_expr = ecx.expr_call( + span, + ecx.expr_path(enzyme_path), + vec![primal_path_expr, diff_path_expr, tuple_expr].into(), ); - let mut body = ecx.block(span, ThinVec::new()); - body.stmts.push(ecx.stmt_semi(unsf_expr)); - - // This uses primal args which won't be available if we errored before - if !errored { - body.stmts.push(ecx.stmt_semi(black_box_primal_call.clone())); - } - body.stmts.push(ecx.stmt_semi(black_box_remaining_args)); - - (body, primal_call, black_box_primal_call, blackbox_call_expr) + ecx.stmt_expr(call_expr) } - /// We only want this function to type-check, since we will replace the body - /// later on llvm level. Using `loop {}` does not cover all return types anymore, - /// so instead we manually build something that should pass the type checker. - /// We also add a inline_asm line, as one more barrier for rustc to prevent inlining - /// or const propagation. inline_asm will also triggers an Enzyme crash if due to another - /// bug would ever try to accidentally differentiate this placeholder function body. - /// Finally, we also add back_box usages of all input arguments, to prevent rustc - /// from optimizing any arguments away. - fn gen_enzyme_body( + // Generate turbofish expression from fn name and generics + // Given `foo` and `<A, B, C>` params, gen `foo::<A, B, C>` + // We use this expression when passing primal and diff function to the autodiff intrinsic + fn gen_turbofish_expr( ecx: &ExtCtxt<'_>, - x: &AutoDiffAttrs, - n_active: u32, - sig: &ast::FnSig, - d_sig: &ast::FnSig, - primal: Ident, - new_names: &[String], - span: Span, - sig_span: Span, - idents: Vec<Ident>, - errored: bool, + ident: Ident, generics: &Generics, - ) -> Box<ast::Block> { - let new_decl_span = d_sig.span; - - // Just adding some default inline-asm and black_box usages to prevent early inlining - // and optimizations which alter the function signature. - // - // The bb_primal_call is the black_box call of the primal function. We keep it around, - // since it has the convenient property of returning the type of the primal function, - // Remember, we only care to match types here. - // No matter which return we pick, we always wrap it into a std::hint::black_box call, - // to prevent rustc from propagating it into the caller. - let (mut body, primal_call, bb_primal_call, bb_call_expr) = init_body_helper( - ecx, - span, - primal, - new_names, - sig_span, - new_decl_span, - &idents, - errored, - generics, - ); - - if !has_ret(&d_sig.decl.output) { - // there is no return type that we have to match, () works fine. - return body; - } - - // Everything from here onwards just tries to fulfil the return type. Fun! - - // having an active-only return means we'll drop the original return type. - // So that can be treated identical to not having one in the first place. - let primal_ret = has_ret(&sig.decl.output) && !x.has_active_only_ret(); - - if primal_ret && n_active == 0 && x.mode.is_rev() { - // We only have the primal ret. - body.stmts.push(ecx.stmt_expr(bb_primal_call)); - return body; - } - - if !primal_ret && n_active == 1 { - // Again no tuple return, so return default float val. - let ty = match d_sig.decl.output { - FnRetTy::Ty(ref ty) => ty.clone(), - FnRetTy::Default(span) => { - panic!("Did not expect Default ret ty: {:?}", span); + span: Span, + is_impl: bool, + ) -> Box<ast::Expr> { + let generic_args = generics + .params + .iter() + .filter_map(|p| match &p.kind { + GenericParamKind::Type { .. } => { + let path = ast::Path::from_ident(p.ident); + let ty = ecx.ty_path(path); + Some(AngleBracketedArg::Arg(GenericArg::Type(ty))) } - }; - let arg = ty.kind.is_simple_path().unwrap(); - let tmp = ecx.def_site_path(&[arg, kw::Default]); - let default_call_expr = ecx.expr_path(ecx.path(span, tmp)); - let default_call_expr = ecx.expr_call(new_decl_span, default_call_expr, thin_vec![]); - body.stmts.push(ecx.stmt_expr(default_call_expr)); - return body; - } - - let mut exprs: Box<ast::Expr> = primal_call; - let d_ret_ty = match d_sig.decl.output { - FnRetTy::Ty(ref ty) => ty.clone(), - FnRetTy::Default(span) => { - panic!("Did not expect Default ret ty: {:?}", span); - } - }; - if x.mode.is_fwd() { - // Fwd mode is easy. If the return activity is Const, we support arbitrary types. - // Otherwise, we only support a scalar, a pair of scalars, or an array of scalars. - // We checked that (on a best-effort base) in the preceding gen_enzyme_decl function. - // In all three cases, we can return `std::hint::black_box(<T>::default())`. - if x.ret_activity == DiffActivity::Const { - // Here we call the primal function, since our dummy function has the same return - // type due to the Const return activity. - exprs = ecx.expr_call(new_decl_span, bb_call_expr, thin_vec![exprs]); - } else { - let q = QSelf { ty: d_ret_ty, path_span: span, position: 0 }; - let y = ExprKind::Path( - Some(Box::new(q)), - ecx.path_ident(span, Ident::with_dummy_span(kw::Default)), - ); - let default_call_expr = ecx.expr(span, y); - let default_call_expr = - ecx.expr_call(new_decl_span, default_call_expr, thin_vec![]); - exprs = ecx.expr_call(new_decl_span, bb_call_expr, thin_vec![default_call_expr]); - } - } else if x.mode.is_rev() { - if x.width == 1 { - // We either have `-> ArbitraryType` or `-> (ArbitraryType, repeated_float_scalars)`. - match d_ret_ty.kind { - TyKind::Tup(ref args) => { - // We have a tuple return type. We need to create a tuple of the same size - // and fill it with default values. - let mut exprs2 = thin_vec![exprs]; - for arg in args.iter().skip(1) { - let arg = arg.kind.is_simple_path().unwrap(); - let tmp = ecx.def_site_path(&[arg, kw::Default]); - let default_call_expr = ecx.expr_path(ecx.path(span, tmp)); - let default_call_expr = - ecx.expr_call(new_decl_span, default_call_expr, thin_vec![]); - exprs2.push(default_call_expr); - } - exprs = ecx.expr_tuple(new_decl_span, exprs2); - } - _ => { - // Interestingly, even the `-> ArbitraryType` case - // ends up getting matched and handled correctly above, - // so we don't have to handle any other case for now. - panic!("Unsupported return type: {:?}", d_ret_ty); - } + GenericParamKind::Const { .. } => { + let expr = ecx.expr_path(ast::Path::from_ident(p.ident)); + let anon_const = AnonConst { id: ast::DUMMY_NODE_ID, value: expr }; + Some(AngleBracketedArg::Arg(GenericArg::Const(anon_const))) } - } - exprs = ecx.expr_call(new_decl_span, bb_call_expr, thin_vec![exprs]); - } else { - unreachable!("Unsupported mode: {:?}", x.mode); - } + GenericParamKind::Lifetime { .. } => None, + }) + .collect::<ThinVec<_>>(); - body.stmts.push(ecx.stmt_expr(exprs)); - - body - } + let args: AngleBracketedArgs = AngleBracketedArgs { span, args: generic_args }; - fn gen_primal_call( - ecx: &ExtCtxt<'_>, - span: Span, - primal: Ident, - idents: &[Ident], - generics: &Generics, - ) -> Box<ast::Expr> { - let has_self = idents.len() > 0 && idents[0].name == kw::SelfLower; + let segment = PathSegment { + ident, + id: ast::DUMMY_NODE_ID, + args: Some(Box::new(GenericArgs::AngleBracketed(args))), + }; - if has_self { - let args: ThinVec<_> = - idents[1..].iter().map(|arg| ecx.expr_path(ecx.path_ident(span, *arg))).collect(); - let self_expr = ecx.expr_self(span); - ecx.expr_method_call(span, self_expr, primal, args) + let segments = if is_impl { + thin_vec![ + PathSegment { ident: Ident::from_str("Self"), id: ast::DUMMY_NODE_ID, args: None }, + segment, + ] } else { - let args: ThinVec<_> = - idents.iter().map(|arg| ecx.expr_path(ecx.path_ident(span, *arg))).collect(); - let mut primal_path = ecx.path_ident(span, primal); - - let is_generic = !generics.params.is_empty(); - - match (is_generic, primal_path.segments.last_mut()) { - (true, Some(function_path)) => { - let primal_generic_types = generics - .params - .iter() - .filter(|param| matches!(param.kind, ast::GenericParamKind::Type { .. })); - - let generated_generic_types = primal_generic_types - .map(|type_param| { - let generic_param = TyKind::Path( - None, - ast::Path { - span, - segments: thin_vec![ast::PathSegment { - ident: type_param.ident, - args: None, - id: ast::DUMMY_NODE_ID, - }], - tokens: None, - }, - ); - - ast::AngleBracketedArg::Arg(ast::GenericArg::Type(Box::new(ast::Ty { - id: type_param.id, - span, - kind: generic_param, - tokens: None, - }))) - }) - .collect(); - - function_path.args = - Some(Box::new(ast::GenericArgs::AngleBracketed(ast::AngleBracketedArgs { - span, - args: generated_generic_types, - }))); - } - _ => {} - } + thin_vec![segment] + }; - let primal_call_expr = ecx.expr_path(primal_path); - ecx.expr_call(span, primal_call_expr, args) - } + let path = Path { span, segments, tokens: None }; + + ecx.expr_path(path) } // Generate the new function declaration. Const arguments are kept as is. Duplicated arguments must @@ -778,7 +603,7 @@ mod llvm_enzyme { sig: &ast::FnSig, x: &AutoDiffAttrs, span: Span, - ) -> (ast::FnSig, Vec<String>, Vec<Ident>, bool) { + ) -> ast::FnSig { let dcx = ecx.sess.dcx(); let has_ret = has_ret(&sig.decl.output); let sig_args = sig.decl.inputs.len() + if has_ret { 1 } else { 0 }; @@ -790,7 +615,7 @@ mod llvm_enzyme { found: num_activities, }); // This is not the right signature, but we can continue parsing. - return (sig.clone(), vec![], vec![], true); + return sig.clone(); } assert!(sig.decl.inputs.len() == x.input_activity.len()); assert!(has_ret == x.has_ret_activity()); @@ -833,7 +658,7 @@ mod llvm_enzyme { if errors { // This is not the right signature, but we can continue parsing. - return (sig.clone(), new_inputs, idents, true); + return sig.clone(); } let unsafe_activities = x @@ -1047,7 +872,7 @@ mod llvm_enzyme { } let d_sig = FnSig { header: d_header, decl: d_decl, span }; trace!("Generated signature: {:?}", d_sig); - (d_sig, new_inputs, idents, false) + d_sig } } diff --git a/compiler/rustc_builtin_macros/src/deriving/from.rs b/compiler/rustc_builtin_macros/src/deriving/from.rs new file mode 100644 index 00000000000..ef0e6ca324a --- /dev/null +++ b/compiler/rustc_builtin_macros/src/deriving/from.rs @@ -0,0 +1,132 @@ +use rustc_ast as ast; +use rustc_ast::{ItemKind, VariantData}; +use rustc_errors::MultiSpan; +use rustc_expand::base::{Annotatable, DummyResult, ExtCtxt}; +use rustc_span::{Ident, Span, kw, sym}; +use thin_vec::thin_vec; + +use crate::deriving::generic::ty::{Bounds, Path, PathKind, Ty}; +use crate::deriving::generic::{ + BlockOrExpr, FieldlessVariantsStrategy, MethodDef, SubstructureFields, TraitDef, + combine_substructure, +}; +use crate::deriving::pathvec_std; +use crate::errors; + +/// Generate an implementation of the `From` trait, provided that `item` +/// is a struct or a tuple struct with exactly one field. +pub(crate) fn expand_deriving_from( + cx: &ExtCtxt<'_>, + span: Span, + mitem: &ast::MetaItem, + annotatable: &Annotatable, + push: &mut dyn FnMut(Annotatable), + is_const: bool, +) { + let Annotatable::Item(item) = &annotatable else { + cx.dcx().bug("derive(From) used on something else than an item"); + }; + + // #[derive(From)] is currently usable only on structs with exactly one field. + let field = if let ItemKind::Struct(_, _, data) = &item.kind + && let [field] = data.fields() + { + Some(field.clone()) + } else { + None + }; + + let from_type = match &field { + Some(field) => Ty::AstTy(field.ty.clone()), + // We don't have a type to put into From<...> if we don't have a single field, so just put + // unit there. + None => Ty::Unit, + }; + let path = + Path::new_(pathvec_std!(convert::From), vec![Box::new(from_type.clone())], PathKind::Std); + + // Generate code like this: + // + // struct S(u32); + // #[automatically_derived] + // impl ::core::convert::From<u32> for S { + // #[inline] + // fn from(value: u32) -> S { + // Self(value) + // } + // } + let from_trait_def = TraitDef { + span, + path, + skip_path_as_bound: true, + needs_copy_as_bound_if_packed: false, + additional_bounds: Vec::new(), + supports_unions: false, + methods: vec![MethodDef { + name: sym::from, + generics: Bounds { bounds: vec![] }, + explicit_self: false, + nonself_args: vec![(from_type, sym::value)], + ret_ty: Ty::Self_, + attributes: thin_vec![cx.attr_word(sym::inline, span)], + fieldless_variants_strategy: FieldlessVariantsStrategy::Default, + combine_substructure: combine_substructure(Box::new(|cx, span, substructure| { + let Some(field) = &field else { + let item_span = item.kind.ident().map(|ident| ident.span).unwrap_or(item.span); + let err_span = MultiSpan::from_spans(vec![span, item_span]); + let error = match &item.kind { + ItemKind::Struct(_, _, data) => { + cx.dcx().emit_err(errors::DeriveFromWrongFieldCount { + span: err_span, + multiple_fields: data.fields().len() > 1, + }) + } + ItemKind::Enum(_, _, _) | ItemKind::Union(_, _, _) => { + cx.dcx().emit_err(errors::DeriveFromWrongTarget { + span: err_span, + kind: &format!("{} {}", item.kind.article(), item.kind.descr()), + }) + } + _ => cx.dcx().bug("Invalid derive(From) ADT input"), + }; + + return BlockOrExpr::new_expr(DummyResult::raw_expr(span, Some(error))); + }; + + let self_kw = Ident::new(kw::SelfUpper, span); + let expr: Box<ast::Expr> = match substructure.fields { + SubstructureFields::StaticStruct(variant, _) => match variant { + // Self { + // field: value + // } + VariantData::Struct { .. } => cx.expr_struct_ident( + span, + self_kw, + thin_vec![cx.field_imm( + span, + field.ident.unwrap(), + cx.expr_ident(span, Ident::new(sym::value, span)) + )], + ), + // Self(value) + VariantData::Tuple(_, _) => cx.expr_call_ident( + span, + self_kw, + thin_vec![cx.expr_ident(span, Ident::new(sym::value, span))], + ), + variant => { + cx.dcx().bug(format!("Invalid derive(From) ADT variant: {variant:?}")); + } + }, + _ => cx.dcx().bug("Invalid derive(From) ADT input"), + }; + BlockOrExpr::new_expr(expr) + })), + }], + associated_types: Vec::new(), + is_const, + is_staged_api_crate: cx.ecfg.features.staged_api(), + }; + + from_trait_def.expand(cx, mitem, annotatable, push); +} diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs b/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs index 00e70b21cf4..1458553d492 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs @@ -2,7 +2,7 @@ //! when specifying impls to be derived. pub(crate) use Ty::*; -use rustc_ast::{self as ast, Expr, GenericArg, GenericParamKind, Generics, SelfKind}; +use rustc_ast::{self as ast, Expr, GenericArg, GenericParamKind, Generics, SelfKind, TyKind}; use rustc_expand::base::ExtCtxt; use rustc_span::source_map::respan; use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw}; @@ -65,7 +65,7 @@ impl Path { } } -/// A type. Supports pointers, Self, and literals. +/// A type. Supports pointers, Self, literals, unit or an arbitrary AST path. #[derive(Clone)] pub(crate) enum Ty { Self_, @@ -76,6 +76,8 @@ pub(crate) enum Ty { Path(Path), /// For () return types. Unit, + /// An arbitrary type. + AstTy(Box<ast::Ty>), } pub(crate) fn self_ref() -> Ty { @@ -101,6 +103,7 @@ impl Ty { let ty = ast::TyKind::Tup(ThinVec::new()); cx.ty(span, ty) } + AstTy(ty) => ty.clone(), } } @@ -132,6 +135,10 @@ impl Ty { cx.path_all(span, false, vec![self_ty], params) } Path(p) => p.to_path(cx, span, self_ty, generics), + AstTy(ty) => match &ty.kind { + TyKind::Path(_, path) => path.clone(), + _ => cx.dcx().span_bug(span, "non-path in a path in generic `derive`"), + }, Ref(..) => cx.dcx().span_bug(span, "ref in a path in generic `derive`"), Unit => cx.dcx().span_bug(span, "unit in a path in generic `derive`"), } diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs index 1edc2965def..cee6952fa34 100644 --- a/compiler/rustc_builtin_macros/src/deriving/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs @@ -23,6 +23,7 @@ pub(crate) mod clone; pub(crate) mod coerce_pointee; pub(crate) mod debug; pub(crate) mod default; +pub(crate) mod from; pub(crate) mod hash; #[path = "cmp/eq.rs"] diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index bb520db75b9..54e8f750337 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -447,6 +447,24 @@ pub(crate) struct DefaultHasArg { } #[derive(Diagnostic)] +#[diag(builtin_macros_derive_from_wrong_target)] +#[note(builtin_macros_derive_from_usage_note)] +pub(crate) struct DeriveFromWrongTarget<'a> { + #[primary_span] + pub(crate) span: MultiSpan, + pub(crate) kind: &'a str, +} + +#[derive(Diagnostic)] +#[diag(builtin_macros_derive_from_wrong_field_count)] +#[note(builtin_macros_derive_from_usage_note)] +pub(crate) struct DeriveFromWrongFieldCount { + #[primary_span] + pub(crate) span: MultiSpan, + pub(crate) multiple_fields: bool, +} + +#[derive(Diagnostic)] #[diag(builtin_macros_derive_macro_call)] pub(crate) struct DeriveMacroCall { #[primary_span] diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 7bc448a9acb..86a4927f390 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -139,6 +139,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { PartialEq: partial_eq::expand_deriving_partial_eq, PartialOrd: partial_ord::expand_deriving_partial_ord, CoercePointee: coerce_pointee::expand_deriving_coerce_pointee, + From: from::expand_deriving_from, } let client = rustc_proc_macro::bridge::client::Client::expand1(rustc_proc_macro::quote); diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index b11f11d38e3..4025aba82da 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -93,7 +93,6 @@ use gccjit::{CType, Context, OptimizationLevel}; #[cfg(feature = "master")] use gccjit::{TargetInfo, Version}; use rustc_ast::expand::allocator::AllocatorKind; -use rustc_ast::expand::autodiff_attrs::AutoDiffItem; use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule}; use rustc_codegen_ssa::back::write::{ CodegenContext, FatLtoInput, ModuleConfig, TargetMachineFactoryFn, @@ -363,12 +362,7 @@ impl WriteBackendMethods for GccCodegenBackend { _exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], modules: Vec<FatLtoInput<Self>>, - diff_functions: Vec<AutoDiffItem>, ) -> Result<ModuleCodegen<Self::Module>, FatalError> { - if !diff_functions.is_empty() { - unimplemented!(); - } - back::lto::run_fat(cgcx, each_linked_rlib_for_lto, modules) } diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs index 2b5090ed6db..23610aa856c 100644 --- a/compiler/rustc_codegen_llvm/src/allocator.rs +++ b/compiler/rustc_codegen_llvm/src/allocator.rs @@ -8,11 +8,12 @@ use rustc_middle::bug; use rustc_middle::ty::TyCtxt; use rustc_session::config::{DebugInfo, OomStrategy}; use rustc_symbol_mangling::mangle_internal_symbol; +use smallvec::SmallVec; use crate::builder::SBuilder; use crate::declare::declare_simple_fn; use crate::llvm::{self, False, True, Type, Value}; -use crate::{SimpleCx, attributes, debuginfo}; +use crate::{SimpleCx, attributes, debuginfo, llvm_util}; pub(crate) unsafe fn codegen( tcx: TyCtxt<'_>, @@ -147,6 +148,20 @@ fn create_wrapper_function( llvm::Visibility::from_generic(tcx.sess.default_visibility()), ty, ); + + let mut attrs = SmallVec::<[_; 2]>::new(); + + let target_cpu = llvm_util::target_cpu(tcx.sess); + let target_cpu_attr = llvm::CreateAttrStringValue(cx.llcx, "target-cpu", target_cpu); + + let tune_cpu_attr = llvm_util::tune_cpu(tcx.sess) + .map(|tune_cpu| llvm::CreateAttrStringValue(cx.llcx, "tune-cpu", tune_cpu)); + + attrs.push(target_cpu_attr); + attrs.extend(tune_cpu_attr); + + attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &attrs); + let no_return = if no_return { // -> ! DIFlagNoReturn let no_return = llvm::AttributeKind::NoReturn.create_attr(cx.llcx); diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index c548f467583..a6daacd95ef 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -28,22 +28,6 @@ pub(crate) fn apply_to_callsite(callsite: &Value, idx: AttributePlace, attrs: &[ } } -pub(crate) fn has_attr(llfn: &Value, idx: AttributePlace, attr: AttributeKind) -> bool { - llvm::HasAttributeAtIndex(llfn, idx, attr) -} - -pub(crate) fn has_string_attr(llfn: &Value, name: &str) -> bool { - llvm::HasStringAttribute(llfn, name) -} - -pub(crate) fn remove_from_llfn(llfn: &Value, place: AttributePlace, kind: AttributeKind) { - llvm::RemoveRustEnumAttributeAtIndex(llfn, place, kind); -} - -pub(crate) fn remove_string_attr_from_llfn(llfn: &Value, name: &str) { - llvm::RemoveStringAttrFromFn(llfn, name); -} - /// Get LLVM attribute for the provided inline heuristic. #[inline] fn inline_attr<'ll>(cx: &CodegenCx<'ll, '_>, inline: InlineAttr) -> Option<&'ll Attribute> { diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index c269f11e931..853d0295238 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -24,9 +24,8 @@ use crate::back::write::{ self, CodegenDiagnosticsStage, DiagnosticHandlers, bitcode_section_name, save_temp_bitcode, }; use crate::errors::{LlvmError, LtoBitcodeFromRlib}; -use crate::llvm::AttributePlace::Function; use crate::llvm::{self, build_string}; -use crate::{LlvmCodegenBackend, ModuleLlvm, SimpleCx, attributes}; +use crate::{LlvmCodegenBackend, ModuleLlvm, SimpleCx}; /// We keep track of the computed LTO cache keys from the previous /// session to determine which CGUs we can reuse. @@ -593,31 +592,6 @@ pub(crate) fn run_pass_manager( } if cfg!(llvm_enzyme) && enable_ad && !thin { - let cx = - SimpleCx::new(module.module_llvm.llmod(), &module.module_llvm.llcx, cgcx.pointer_size); - - for function in cx.get_functions() { - let enzyme_marker = "enzyme_marker"; - if attributes::has_string_attr(function, enzyme_marker) { - // Sanity check: Ensure 'noinline' is present before replacing it. - assert!( - attributes::has_attr(function, Function, llvm::AttributeKind::NoInline), - "Expected __enzyme function to have 'noinline' before adding 'alwaysinline'" - ); - - attributes::remove_from_llfn(function, Function, llvm::AttributeKind::NoInline); - attributes::remove_string_attr_from_llfn(function, enzyme_marker); - - assert!( - !attributes::has_string_attr(function, enzyme_marker), - "Expected function to not have 'enzyme_marker'" - ); - - let always_inline = llvm::AttributeKind::AlwaysInline.create_attr(cx.llcx); - attributes::apply_to_llfn(function, Function, &[always_inline]); - } - } - let opt_stage = llvm::OptStage::FatLTO; let stage = write::AutodiffStage::PostAD; if !config.autodiff.contains(&config::AutoDiff::NoPostopt) { diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 85a06f457eb..62998003ca1 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -862,7 +862,7 @@ pub(crate) fn codegen( .generic_activity_with_arg("LLVM_module_codegen_embed_bitcode", &*module.name); let thin_bc = module.thin_lto_buffer.as_deref().expect("cannot find embedded bitcode"); - embed_bitcode(cgcx, llcx, llmod, &config.bc_cmdline, &thin_bc); + embed_bitcode(cgcx, llcx, llmod, &thin_bc); } } @@ -1058,7 +1058,6 @@ fn embed_bitcode( cgcx: &CodegenContext<LlvmCodegenBackend>, llcx: &llvm::Context, llmod: &llvm::Module, - cmdline: &str, bitcode: &[u8], ) { // We're adding custom sections to the output object file, but we definitely @@ -1074,7 +1073,9 @@ fn embed_bitcode( // * Mach-O - this is for macOS. Inspecting the source code for the native // linker here shows that the `.llvmbc` and `.llvmcmd` sections are // automatically skipped by the linker. In that case there's nothing extra - // that we need to do here. + // that we need to do here. We do need to make sure that the + // `__LLVM,__cmdline` section exists even though it is empty as otherwise + // ld64 rejects the object file. // // * Wasm - the native LLD linker is hard-coded to skip `.llvmbc` and // `.llvmcmd` sections, so there's nothing extra we need to do. @@ -1111,7 +1112,7 @@ fn embed_bitcode( llvm::set_linkage(llglobal, llvm::Linkage::PrivateLinkage); llvm::LLVMSetGlobalConstant(llglobal, llvm::True); - let llconst = common::bytes_in_context(llcx, cmdline.as_bytes()); + let llconst = common::bytes_in_context(llcx, &[]); let llglobal = llvm::add_global(llmod, common::val_ty(llconst), c"rustc.embedded.cmdline"); llvm::set_initializer(llglobal, llconst); let section = if cgcx.target_is_like_darwin { @@ -1128,7 +1129,7 @@ fn embed_bitcode( let section_flags = if cgcx.is_pe_coff { "n" } else { "e" }; let asm = create_section_with_flags_asm(".llvmbc", section_flags, bitcode); llvm::append_module_inline_asm(llmod, &asm); - let asm = create_section_with_flags_asm(".llvmcmd", section_flags, cmdline.as_bytes()); + let asm = create_section_with_flags_asm(".llvmcmd", section_flags, &[]); llvm::append_module_inline_asm(llmod, &asm); } } diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 917d07e3c61..cb98df59c1b 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -1696,7 +1696,11 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { return; } - self.call_intrinsic(intrinsic, &[self.val_ty(ptr)], &[self.cx.const_u64(size), ptr]); + if crate::llvm_util::get_version() >= (22, 0, 0) { + self.call_intrinsic(intrinsic, &[self.val_ty(ptr)], &[ptr]); + } else { + self.call_intrinsic(intrinsic, &[self.val_ty(ptr)], &[self.cx.const_u64(size), ptr]); + } } } impl<'a, 'll, CX: Borrow<SCx<'ll>>> GenericBuilder<'a, 'll, CX> { diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs index 829b3c513c2..e2df3265f6f 100644 --- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs +++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs @@ -1,40 +1,92 @@ use std::ptr; -use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, AutoDiffItem, DiffActivity, DiffMode}; -use rustc_codegen_ssa::ModuleCodegen; +use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode}; use rustc_codegen_ssa::common::TypeKind; -use rustc_codegen_ssa::traits::BaseTypeCodegenMethods; -use rustc_errors::FatalError; -use rustc_middle::bug; -use tracing::{debug, trace}; +use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, BuilderMethods}; +use rustc_middle::ty::{PseudoCanonicalInput, Ty, TyCtxt, TypingEnv}; +use rustc_middle::{bug, ty}; +use tracing::debug; -use crate::back::write::llvm_err; -use crate::builder::{SBuilder, UNNAMED}; +use crate::builder::{Builder, PlaceRef, UNNAMED}; use crate::context::SimpleCx; use crate::declare::declare_simple_fn; -use crate::errors::{AutoDiffWithoutEnable, LlvmError}; -use crate::llvm::AttributePlace::Function; -use crate::llvm::{Metadata, True}; +use crate::llvm; +use crate::llvm::{Metadata, True, Type}; use crate::value::Value; -use crate::{CodegenContext, LlvmCodegenBackend, ModuleLlvm, attributes, llvm}; -fn get_params(fnc: &Value) -> Vec<&Value> { - let param_num = llvm::LLVMCountParams(fnc) as usize; - let mut fnc_args: Vec<&Value> = vec![]; - fnc_args.reserve(param_num); - unsafe { - llvm::LLVMGetParams(fnc, fnc_args.as_mut_ptr()); - fnc_args.set_len(param_num); +pub(crate) fn adjust_activity_to_abi<'tcx>( + tcx: TyCtxt<'tcx>, + fn_ty: Ty<'tcx>, + da: &mut Vec<DiffActivity>, +) { + if !matches!(fn_ty.kind(), ty::FnDef(..)) { + bug!("expected fn def for autodiff, got {:?}", fn_ty); } - fnc_args -} -fn has_sret(fnc: &Value) -> bool { - let num_args = llvm::LLVMCountParams(fnc) as usize; - if num_args == 0 { - false - } else { - unsafe { llvm::LLVMRustHasAttributeAtIndex(fnc, 0, llvm::AttributeKind::StructRet) } + // We don't actually pass the types back into the type system. + // All we do is decide how to handle the arguments. + let sig = fn_ty.fn_sig(tcx).skip_binder(); + + let mut new_activities = vec![]; + let mut new_positions = vec![]; + for (i, ty) in sig.inputs().iter().enumerate() { + if let Some(inner_ty) = ty.builtin_deref(true) { + if inner_ty.is_slice() { + // Now we need to figure out the size of each slice element in memory to allow + // safety checks and usability improvements in the backend. + let sty = match inner_ty.builtin_index() { + Some(sty) => sty, + None => { + panic!("slice element type unknown"); + } + }; + let pci = PseudoCanonicalInput { + typing_env: TypingEnv::fully_monomorphized(), + value: sty, + }; + + let layout = tcx.layout_of(pci); + let elem_size = match layout { + Ok(layout) => layout.size, + Err(_) => { + bug!("autodiff failed to compute slice element size"); + } + }; + let elem_size: u32 = elem_size.bytes() as u32; + + // We know that the length will be passed as extra arg. + if !da.is_empty() { + // We are looking at a slice. The length of that slice will become an + // extra integer on llvm level. Integers are always const. + // However, if the slice get's duplicated, we want to know to later check the + // size. So we mark the new size argument as FakeActivitySize. + // There is one FakeActivitySize per slice, so for convenience we store the + // slice element size in bytes in it. We will use the size in the backend. + let activity = match da[i] { + DiffActivity::DualOnly + | DiffActivity::Dual + | DiffActivity::Dualv + | DiffActivity::DuplicatedOnly + | DiffActivity::Duplicated => { + DiffActivity::FakeActivitySize(Some(elem_size)) + } + DiffActivity::Const => DiffActivity::Const, + _ => bug!("unexpected activity for ptr/ref"), + }; + new_activities.push(activity); + new_positions.push(i + 1); + } + + continue; + } + } + } + // now add the extra activities coming from slices + // Reverse order to not invalidate the indices + for _ in 0..new_activities.len() { + let pos = new_positions.pop().unwrap(); + let activity = new_activities.pop().unwrap(); + da.insert(pos, activity); } } @@ -48,14 +100,13 @@ fn has_sret(fnc: &Value) -> bool { // need to match those. // FIXME(ZuseZ4): This logic is a bit more complicated than it should be, can we simplify it // using iterators and peek()? -fn match_args_from_caller_to_enzyme<'ll>( +fn match_args_from_caller_to_enzyme<'ll, 'tcx>( cx: &SimpleCx<'ll>, - builder: &SBuilder<'ll, 'll>, + builder: &mut Builder<'_, 'll, 'tcx>, width: u32, args: &mut Vec<&'ll llvm::Value>, inputs: &[DiffActivity], outer_args: &[&'ll llvm::Value], - has_sret: bool, ) { debug!("matching autodiff arguments"); // We now handle the issue that Rust level arguments not always match the llvm-ir level @@ -67,14 +118,6 @@ fn match_args_from_caller_to_enzyme<'ll>( let mut outer_pos: usize = 0; let mut activity_pos = 0; - if has_sret { - // Then the first outer arg is the sret pointer. Enzyme doesn't know about sret, so the - // inner function will still return something. We increase our outer_pos by one, - // and once we're done with all other args we will take the return of the inner call and - // update the sret pointer with it - outer_pos = 1; - } - let enzyme_const = cx.create_metadata(b"enzyme_const"); let enzyme_out = cx.create_metadata(b"enzyme_out"); let enzyme_dup = cx.create_metadata(b"enzyme_dup"); @@ -193,92 +236,6 @@ fn match_args_from_caller_to_enzyme<'ll>( } } -// On LLVM-IR, we can luckily declare __enzyme_ functions without specifying the input -// arguments. We do however need to declare them with their correct return type. -// We already figured the correct return type out in our frontend, when generating the outer_fn, -// so we can now just go ahead and use that. This is not always trivial, e.g. because sret. -// Beyond sret, this article describes our challenges nicely: -// <https://yorickpeterse.com/articles/the-mess-that-is-handling-structure-arguments-and-returns-in-llvm/> -// I.e. (i32, f32) will get merged into i64, but we don't handle that yet. -fn compute_enzyme_fn_ty<'ll>( - cx: &SimpleCx<'ll>, - attrs: &AutoDiffAttrs, - fn_to_diff: &'ll Value, - outer_fn: &'ll Value, -) -> &'ll llvm::Type { - let fn_ty = cx.get_type_of_global(outer_fn); - let mut ret_ty = cx.get_return_type(fn_ty); - - let has_sret = has_sret(outer_fn); - - if has_sret { - // Now we don't just forward the return type, so we have to figure it out based on the - // primal return type, in combination with the autodiff settings. - let fn_ty = cx.get_type_of_global(fn_to_diff); - let inner_ret_ty = cx.get_return_type(fn_ty); - - let void_ty = unsafe { llvm::LLVMVoidTypeInContext(cx.llcx) }; - if inner_ret_ty == void_ty { - // This indicates that even the inner function has an sret. - // Right now I only look for an sret in the outer function. - // This *probably* needs some extra handling, but I never ran - // into such a case. So I'll wait for user reports to have a test case. - bug!("sret in inner function"); - } - - if attrs.width == 1 { - // Enzyme returns a struct of style: - // `{ original_ret(if requested), float, float, ... }` - let mut struct_elements = vec![]; - if attrs.has_primal_ret() { - struct_elements.push(inner_ret_ty); - } - // Next, we push the list of active floats, since they will be lowered to `enzyme_out`, - // and therefore part of the return struct. - let param_tys = cx.func_params_types(fn_ty); - for (act, param_ty) in attrs.input_activity.iter().zip(param_tys) { - if matches!(act, DiffActivity::Active) { - // Now find the float type at position i based on the fn_ty, - // to know what (f16/f32/f64/...) to add to the struct. - struct_elements.push(param_ty); - } - } - ret_ty = cx.type_struct(&struct_elements, false); - } else { - // First we check if we also have to deal with the primal return. - match attrs.mode { - DiffMode::Forward => match attrs.ret_activity { - DiffActivity::Dual => { - let arr_ty = - unsafe { llvm::LLVMArrayType2(inner_ret_ty, attrs.width as u64 + 1) }; - ret_ty = arr_ty; - } - DiffActivity::DualOnly => { - let arr_ty = - unsafe { llvm::LLVMArrayType2(inner_ret_ty, attrs.width as u64) }; - ret_ty = arr_ty; - } - DiffActivity::Const => { - todo!("Not sure, do we need to do something here?"); - } - _ => { - bug!("unreachable"); - } - }, - DiffMode::Reverse => { - todo!("Handle sret for reverse mode"); - } - _ => { - bug!("unreachable"); - } - } - } - } - - // LLVM can figure out the input types on it's own, so we take a shortcut here. - unsafe { llvm::LLVMFunctionType(ret_ty, ptr::null(), 0, True) } -} - /// When differentiating `fn_to_diff`, take a `outer_fn` and generate another /// function with expected naming and calling conventions[^1] which will be /// discovered by the enzyme LLVM pass and its body populated with the differentiated @@ -288,11 +245,15 @@ fn compute_enzyme_fn_ty<'ll>( /// [^1]: <https://enzyme.mit.edu/getting_started/CallingConvention/> // FIXME(ZuseZ4): `outer_fn` should include upstream safety checks to // cover some assumptions of enzyme/autodiff, which could lead to UB otherwise. -fn generate_enzyme_call<'ll>( +pub(crate) fn generate_enzyme_call<'ll, 'tcx>( + builder: &mut Builder<'_, 'll, 'tcx>, cx: &SimpleCx<'ll>, fn_to_diff: &'ll Value, - outer_fn: &'ll Value, + outer_name: &str, + ret_ty: &'ll Type, + fn_args: &[&'ll Value], attrs: AutoDiffAttrs, + dest: PlaceRef<'tcx, &'ll Value>, ) { // We have to pick the name depending on whether we want forward or reverse mode autodiff. let mut ad_name: String = match attrs.mode { @@ -302,11 +263,9 @@ fn generate_enzyme_call<'ll>( } .to_string(); - // add outer_fn name to ad_name to make it unique, in case users apply autodiff to multiple + // add outer_name to ad_name to make it unique, in case users apply autodiff to multiple // functions. Unwrap will only panic, if LLVM gave us an invalid string. - let name = llvm::get_value_name(outer_fn); - let outer_fn_name = std::str::from_utf8(&name).unwrap(); - ad_name.push_str(outer_fn_name); + ad_name.push_str(outer_name); // Let us assume the user wrote the following function square: // @@ -316,14 +275,8 @@ fn generate_enzyme_call<'ll>( // %0 = fmul double %x, %x // ret double %0 // } - // ``` - // - // The user now applies autodiff to the function square, in which case fn_to_diff will be `square`. - // Our macro generates the following placeholder code (slightly simplified): // - // ```llvm // define double @dsquare(double %x) { - // ; placeholder code // return 0.0; // } // ``` @@ -340,175 +293,44 @@ fn generate_enzyme_call<'ll>( // ret double %0 // } // ``` - unsafe { - let enzyme_ty = compute_enzyme_fn_ty(cx, &attrs, fn_to_diff, outer_fn); - - // FIXME(ZuseZ4): the CC/Addr/Vis values are best effort guesses, we should look at tests and - // think a bit more about what should go here. - let cc = llvm::LLVMGetFunctionCallConv(outer_fn); - let ad_fn = declare_simple_fn( - cx, - &ad_name, - llvm::CallConv::try_from(cc).expect("invalid callconv"), - llvm::UnnamedAddr::No, - llvm::Visibility::Default, - enzyme_ty, - ); - - // Otherwise LLVM might inline our temporary code before the enzyme pass has a chance to - // do it's work. - let attr = llvm::AttributeKind::NoInline.create_attr(cx.llcx); - attributes::apply_to_llfn(ad_fn, Function, &[attr]); - - // We add a made-up attribute just such that we can recognize it after AD to update - // (no)-inline attributes. We'll then also remove this attribute. - let enzyme_marker_attr = llvm::CreateAttrString(cx.llcx, "enzyme_marker"); - attributes::apply_to_llfn(outer_fn, Function, &[enzyme_marker_attr]); - - // first, remove all calls from fnc - let entry = llvm::LLVMGetFirstBasicBlock(outer_fn); - let br = llvm::LLVMRustGetTerminator(entry); - llvm::LLVMRustEraseInstFromParent(br); - - let last_inst = llvm::LLVMRustGetLastInstruction(entry).unwrap(); - let mut builder = SBuilder::build(cx, entry); - - let num_args = llvm::LLVMCountParams(&fn_to_diff); - let mut args = Vec::with_capacity(num_args as usize + 1); - args.push(fn_to_diff); - - let enzyme_primal_ret = cx.create_metadata(b"enzyme_primal_return"); - if matches!(attrs.ret_activity, DiffActivity::Dual | DiffActivity::Active) { - args.push(cx.get_metadata_value(enzyme_primal_ret)); - } - if attrs.width > 1 { - let enzyme_width = cx.create_metadata(b"enzyme_width"); - args.push(cx.get_metadata_value(enzyme_width)); - args.push(cx.get_const_int(cx.type_i64(), attrs.width as u64)); - } - - let has_sret = has_sret(outer_fn); - let outer_args: Vec<&llvm::Value> = get_params(outer_fn); - match_args_from_caller_to_enzyme( - &cx, - &builder, - attrs.width, - &mut args, - &attrs.input_activity, - &outer_args, - has_sret, - ); - - let call = builder.call(enzyme_ty, ad_fn, &args, None); - - // This part is a bit iffy. LLVM requires that a call to an inlineable function has some - // metadata attached to it, but we just created this code oota. Given that the - // differentiated function already has partly confusing metadata, and given that this - // affects nothing but the auttodiff IR, we take a shortcut and just steal metadata from the - // dummy code which we inserted at a higher level. - // FIXME(ZuseZ4): Work with Enzyme core devs to clarify what debug metadata issues we have, - // and how to best improve it for enzyme core and rust-enzyme. - let md_ty = cx.get_md_kind_id("dbg"); - if llvm::LLVMRustHasMetadata(last_inst, md_ty) { - let md = llvm::LLVMRustDIGetInstMetadata(last_inst) - .expect("failed to get instruction metadata"); - let md_todiff = cx.get_metadata_value(md); - llvm::LLVMSetMetadata(call, md_ty, md_todiff); - } else { - // We don't panic, since depending on whether we are in debug or release mode, we might - // have no debug info to copy, which would then be ok. - trace!("no dbg info"); - } - - // Now that we copied the metadata, get rid of dummy code. - llvm::LLVMRustEraseInstUntilInclusive(entry, last_inst); - - if cx.val_ty(call) == cx.type_void() || has_sret { - if has_sret { - // This is what we already have in our outer_fn (shortened): - // define void @_foo(ptr <..> sret([32 x i8]) initializes((0, 32)) %0, <...>) { - // %7 = call [4 x double] (...) @__enzyme_fwddiff_foo(ptr @square, metadata !"enzyme_width", i64 4, <...>) - // <Here we are, we want to add the following two lines> - // store [4 x double] %7, ptr %0, align 8 - // ret void - // } - - // now store the result of the enzyme call into the sret pointer. - let sret_ptr = outer_args[0]; - let call_ty = cx.val_ty(call); - if attrs.width == 1 { - assert_eq!(cx.type_kind(call_ty), TypeKind::Struct); - } else { - assert_eq!(cx.type_kind(call_ty), TypeKind::Array); - } - llvm::LLVMBuildStore(&builder.llbuilder, call, sret_ptr); - } - builder.ret_void(); - } else { - builder.ret(call); - } - - // Let's crash in case that we messed something up above and generated invalid IR. - llvm::LLVMRustVerifyFunction( - outer_fn, - llvm::LLVMRustVerifierFailureAction::LLVMAbortProcessAction, - ); - } -} - -pub(crate) fn differentiate<'ll>( - module: &'ll ModuleCodegen<ModuleLlvm>, - cgcx: &CodegenContext<LlvmCodegenBackend>, - diff_items: Vec<AutoDiffItem>, -) -> Result<(), FatalError> { - for item in &diff_items { - trace!("{}", item); + let enzyme_ty = unsafe { llvm::LLVMFunctionType(ret_ty, ptr::null(), 0, True) }; + + // FIXME(ZuseZ4): the CC/Addr/Vis values are best effort guesses, we should look at tests and + // think a bit more about what should go here. + let cc = unsafe { llvm::LLVMGetFunctionCallConv(fn_to_diff) }; + let ad_fn = declare_simple_fn( + cx, + &ad_name, + llvm::CallConv::try_from(cc).expect("invalid callconv"), + llvm::UnnamedAddr::No, + llvm::Visibility::Default, + enzyme_ty, + ); + + let num_args = llvm::LLVMCountParams(&fn_to_diff); + let mut args = Vec::with_capacity(num_args as usize + 1); + args.push(fn_to_diff); + + let enzyme_primal_ret = cx.create_metadata(b"enzyme_primal_return"); + if matches!(attrs.ret_activity, DiffActivity::Dual | DiffActivity::Active) { + args.push(cx.get_metadata_value(enzyme_primal_ret)); } - - let diag_handler = cgcx.create_dcx(); - - let cx = SimpleCx::new(module.module_llvm.llmod(), module.module_llvm.llcx, cgcx.pointer_size); - - // First of all, did the user try to use autodiff without using the -Zautodiff=Enable flag? - if !diff_items.is_empty() - && !cgcx.opts.unstable_opts.autodiff.contains(&rustc_session::config::AutoDiff::Enable) - { - return Err(diag_handler.handle().emit_almost_fatal(AutoDiffWithoutEnable)); - } - - // Here we replace the placeholder code with the actual autodiff code, which calls Enzyme. - for item in diff_items.iter() { - let name = item.source.clone(); - let fn_def: Option<&llvm::Value> = cx.get_function(&name); - let Some(fn_def) = fn_def else { - return Err(llvm_err( - diag_handler.handle(), - LlvmError::PrepareAutoDiff { - src: item.source.clone(), - target: item.target.clone(), - error: "could not find source function".to_owned(), - }, - )); - }; - debug!(?item.target); - let fn_target: Option<&llvm::Value> = cx.get_function(&item.target); - let Some(fn_target) = fn_target else { - return Err(llvm_err( - diag_handler.handle(), - LlvmError::PrepareAutoDiff { - src: item.source.clone(), - target: item.target.clone(), - error: "could not find target function".to_owned(), - }, - )); - }; - - generate_enzyme_call(&cx, fn_def, fn_target, item.attrs.clone()); + if attrs.width > 1 { + let enzyme_width = cx.create_metadata(b"enzyme_width"); + args.push(cx.get_metadata_value(enzyme_width)); + args.push(cx.get_const_int(cx.type_i64(), attrs.width as u64)); } - // FIXME(ZuseZ4): support SanitizeHWAddress and prevent illegal/unsupported opts + match_args_from_caller_to_enzyme( + &cx, + builder, + attrs.width, + &mut args, + &attrs.input_activity, + fn_args, + ); - trace!("done with differentiate()"); + let call = builder.call(enzyme_ty, None, None, ad_fn, &args, None, None); - Ok(()) + builder.store_to_place(call, dest.val); } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 27ae729a531..b0f3494ea68 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -8,7 +8,6 @@ use std::str; use rustc_abi::{HasDataLayout, Size, TargetDataLayout, VariantIdx}; use rustc_codegen_ssa::back::versioned_llvm_target; use rustc_codegen_ssa::base::{wants_msvc_seh, wants_wasm_eh}; -use rustc_codegen_ssa::common::TypeKind; use rustc_codegen_ssa::errors as ssa_errors; use rustc_codegen_ssa::traits::*; use rustc_data_structures::base_n::{ALPHANUMERIC_ONLY, ToBaseN}; @@ -660,10 +659,6 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { } } impl<'ll> SimpleCx<'ll> { - pub(crate) fn get_return_type(&self, ty: &'ll Type) -> &'ll Type { - assert_eq!(self.type_kind(ty), TypeKind::Function); - unsafe { llvm::LLVMGetReturnType(ty) } - } pub(crate) fn get_type_of_global(&self, val: &'ll Value) -> &'ll Type { unsafe { llvm::LLVMGlobalGetValueType(val) } } @@ -727,16 +722,6 @@ impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> { llvm::LLVMMDStringInContext2(self.llcx(), name.as_ptr() as *const c_char, name.len()) } } - - pub(crate) fn get_functions(&self) -> Vec<&'ll Value> { - let mut functions = vec![]; - let mut func = unsafe { llvm::LLVMGetFirstFunction(self.llmod()) }; - while let Some(f) = func { - functions.push(f); - func = unsafe { llvm::LLVMGetNextFunction(f) } - } - functions - } } impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 7b27e496986..4935f8d7dff 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -3,24 +3,29 @@ use std::cmp::Ordering; use rustc_abi::{Align, BackendRepr, ExternAbi, Float, HasDataLayout, Primitive, Size}; use rustc_codegen_ssa::base::{compare_simd_types, wants_msvc_seh, wants_wasm_eh}; +use rustc_codegen_ssa::codegen_attrs::autodiff_attrs; use rustc_codegen_ssa::common::{IntPredicate, TypeKind}; use rustc_codegen_ssa::errors::{ExpectedPointerMutability, InvalidMonomorphization}; use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; use rustc_codegen_ssa::mir::place::{PlaceRef, PlaceValue}; use rustc_codegen_ssa::traits::*; -use rustc_hir as hir; +use rustc_hir::def_id::LOCAL_CRATE; +use rustc_hir::{self as hir}; use rustc_middle::mir::BinOp; use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, HasTypingEnv, LayoutOf}; -use rustc_middle::ty::{self, GenericArgsRef, Ty}; +use rustc_middle::ty::{self, GenericArgsRef, Instance, Ty, TyCtxt, TypingEnv}; use rustc_middle::{bug, span_bug}; use rustc_span::{Span, Symbol, sym}; -use rustc_symbol_mangling::mangle_internal_symbol; +use rustc_symbol_mangling::{mangle_internal_symbol, symbol_name_for_instance_in_crate}; +use rustc_target::callconv::PassMode; use rustc_target::spec::PanicStrategy; use tracing::debug; use crate::abi::FnAbiLlvmExt; use crate::builder::Builder; +use crate::builder::autodiff::{adjust_activity_to_abi, generate_enzyme_call}; use crate::context::CodegenCx; +use crate::errors::AutoDiffWithoutEnable; use crate::llvm::{self, Metadata}; use crate::type_::Type; use crate::type_of::LayoutLlvmExt; @@ -189,6 +194,10 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { &[ptr, args[1].immediate()], ) } + sym::autodiff => { + codegen_autodiff(self, tcx, instance, args, result); + return Ok(()); + } sym::is_val_statically_known => { if let OperandValue::Immediate(imm) = args[0].val { self.call_intrinsic( @@ -1113,6 +1122,143 @@ fn get_rust_try_fn<'a, 'll, 'tcx>( rust_try } +fn codegen_autodiff<'ll, 'tcx>( + bx: &mut Builder<'_, 'll, 'tcx>, + tcx: TyCtxt<'tcx>, + instance: ty::Instance<'tcx>, + args: &[OperandRef<'tcx, &'ll Value>], + result: PlaceRef<'tcx, &'ll Value>, +) { + if !tcx.sess.opts.unstable_opts.autodiff.contains(&rustc_session::config::AutoDiff::Enable) { + let _ = tcx.dcx().emit_almost_fatal(AutoDiffWithoutEnable); + } + + let fn_args = instance.args; + let callee_ty = instance.ty(tcx, bx.typing_env()); + + let sig = callee_ty.fn_sig(tcx).skip_binder(); + + let ret_ty = sig.output(); + let llret_ty = bx.layout_of(ret_ty).llvm_type(bx); + + // Get source, diff, and attrs + let (source_id, source_args) = match fn_args.into_type_list(tcx)[0].kind() { + ty::FnDef(def_id, source_params) => (def_id, source_params), + _ => bug!("invalid autodiff intrinsic args"), + }; + + let fn_source = match Instance::try_resolve(tcx, bx.cx.typing_env(), *source_id, source_args) { + Ok(Some(instance)) => instance, + Ok(None) => bug!( + "could not resolve ({:?}, {:?}) to a specific autodiff instance", + source_id, + source_args + ), + Err(_) => { + // An error has already been emitted + return; + } + }; + + let source_symbol = symbol_name_for_instance_in_crate(tcx, fn_source.clone(), LOCAL_CRATE); + let Some(fn_to_diff) = bx.cx.get_function(&source_symbol) else { + bug!("could not find source function") + }; + + let (diff_id, diff_args) = match fn_args.into_type_list(tcx)[1].kind() { + ty::FnDef(def_id, diff_args) => (def_id, diff_args), + _ => bug!("invalid args"), + }; + + let fn_diff = match Instance::try_resolve(tcx, bx.cx.typing_env(), *diff_id, diff_args) { + Ok(Some(instance)) => instance, + Ok(None) => bug!( + "could not resolve ({:?}, {:?}) to a specific autodiff instance", + diff_id, + diff_args + ), + Err(_) => { + // An error has already been emitted + return; + } + }; + + let val_arr = get_args_from_tuple(bx, args[2], fn_diff); + let diff_symbol = symbol_name_for_instance_in_crate(tcx, fn_diff.clone(), LOCAL_CRATE); + + let Some(mut diff_attrs) = autodiff_attrs(tcx, fn_diff.def_id()) else { + bug!("could not find autodiff attrs") + }; + + adjust_activity_to_abi( + tcx, + fn_source.ty(tcx, TypingEnv::fully_monomorphized()), + &mut diff_attrs.input_activity, + ); + + // Build body + generate_enzyme_call( + bx, + bx.cx, + fn_to_diff, + &diff_symbol, + llret_ty, + &val_arr, + diff_attrs.clone(), + result, + ); +} + +fn get_args_from_tuple<'ll, 'tcx>( + bx: &mut Builder<'_, 'll, 'tcx>, + tuple_op: OperandRef<'tcx, &'ll Value>, + fn_instance: Instance<'tcx>, +) -> Vec<&'ll Value> { + let cx = bx.cx; + let fn_abi = cx.fn_abi_of_instance(fn_instance, ty::List::empty()); + + match tuple_op.val { + OperandValue::Immediate(val) => vec![val], + OperandValue::Pair(v1, v2) => vec![v1, v2], + OperandValue::Ref(ptr) => { + let tuple_place = PlaceRef { val: ptr, layout: tuple_op.layout }; + + let mut result = Vec::with_capacity(fn_abi.args.len()); + let mut tuple_index = 0; + + for arg in &fn_abi.args { + match arg.mode { + PassMode::Ignore => {} + PassMode::Direct(_) | PassMode::Cast { .. } => { + let field = tuple_place.project_field(bx, tuple_index); + let llvm_ty = field.layout.llvm_type(bx.cx); + let val = bx.load(llvm_ty, field.val.llval, field.val.align); + result.push(val); + tuple_index += 1; + } + PassMode::Pair(_, _) => { + let field = tuple_place.project_field(bx, tuple_index); + let llvm_ty = field.layout.llvm_type(bx.cx); + let pair_val = bx.load(llvm_ty, field.val.llval, field.val.align); + result.push(bx.extract_value(pair_val, 0)); + result.push(bx.extract_value(pair_val, 1)); + tuple_index += 1; + } + PassMode::Indirect { .. } => { + let field = tuple_place.project_field(bx, tuple_index); + result.push(field.val.llval); + tuple_index += 1; + } + } + } + + result + } + + OperandValue::ZeroSized => vec![], + } +} + fn generic_simd_intrinsic<'ll, 'tcx>( bx: &mut Builder<'_, 'll, 'tcx>, name: Symbol, diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index ca84b6de8b1..79e80db6f55 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -30,7 +30,6 @@ use context::SimpleCx; use errors::ParseTargetMachineConfig; use llvm_util::target_config; use rustc_ast::expand::allocator::AllocatorKind; -use rustc_ast::expand::autodiff_attrs::AutoDiffItem; use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule}; use rustc_codegen_ssa::back::write::{ CodegenContext, FatLtoInput, ModuleConfig, TargetMachineFactoryConfig, TargetMachineFactoryFn, @@ -173,15 +172,10 @@ impl WriteBackendMethods for LlvmCodegenBackend { exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], modules: Vec<FatLtoInput<Self>>, - diff_fncs: Vec<AutoDiffItem>, ) -> Result<ModuleCodegen<Self::Module>, FatalError> { let mut module = back::lto::run_fat(cgcx, exported_symbols_for_lto, each_linked_rlib_for_lto, modules)?; - if !diff_fncs.is_empty() { - builder::autodiff::differentiate(&module, cgcx, diff_fncs)?; - } - let dcx = cgcx.create_dcx(); let dcx = dcx.handle(); back::lto::run_pass_manager(cgcx, dcx, &mut module, false)?; diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index 154ba4fd690..0ea0af0c9af 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -42,32 +42,6 @@ pub(crate) fn AddFunctionAttributes<'ll>( } } -pub(crate) fn HasAttributeAtIndex<'ll>( - llfn: &'ll Value, - idx: AttributePlace, - kind: AttributeKind, -) -> bool { - unsafe { LLVMRustHasAttributeAtIndex(llfn, idx.as_uint(), kind) } -} - -pub(crate) fn HasStringAttribute<'ll>(llfn: &'ll Value, name: &str) -> bool { - unsafe { LLVMRustHasFnAttribute(llfn, name.as_c_char_ptr(), name.len()) } -} - -pub(crate) fn RemoveStringAttrFromFn<'ll>(llfn: &'ll Value, name: &str) { - unsafe { LLVMRustRemoveFnAttribute(llfn, name.as_c_char_ptr(), name.len()) } -} - -pub(crate) fn RemoveRustEnumAttributeAtIndex( - llfn: &Value, - place: AttributePlace, - kind: AttributeKind, -) { - unsafe { - LLVMRustRemoveEnumAttributeAtIndex(llfn, place.as_uint(), kind); - } -} - pub(crate) fn AddCallSiteAttributes<'ll>( callsite: &'ll Value, idx: AttributePlace, diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 30956fd1855..6a8971de746 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -26,6 +26,7 @@ rustc_hashes = { path = "../rustc_hashes" } rustc_hir = { path = "../rustc_hir" } rustc_incremental = { path = "../rustc_incremental" } rustc_index = { path = "../rustc_index" } +rustc_lint_defs = { path = "../rustc_lint_defs" } rustc_macros = { path = "../rustc_macros" } rustc_metadata = { path = "../rustc_metadata" } rustc_middle = { path = "../rustc_middle" } diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index aa29afb7f5b..26d089a1171 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -7,7 +7,6 @@ use std::{fs, io, mem, str, thread}; use rustc_abi::Size; use rustc_ast::attr; -use rustc_ast::expand::autodiff_attrs::AutoDiffItem; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::jobserver::{self, Acquired}; use rustc_data_structures::memmap::Mmap; @@ -38,7 +37,7 @@ use tracing::debug; use super::link::{self, ensure_removed}; use super::lto::{self, SerializedModule}; use crate::back::lto::check_lto_allowed; -use crate::errors::{AutodiffWithoutLto, ErrorCreatingRemarkDir}; +use crate::errors::ErrorCreatingRemarkDir; use crate::traits::*; use crate::{ CachedModuleCodegen, CodegenResults, CompiledModule, CrateInfo, ModuleCodegen, ModuleKind, @@ -76,12 +75,9 @@ pub struct ModuleConfig { /// Names of additional optimization passes to run. pub passes: Vec<String>, /// Some(level) to optimize at a certain level, or None to run - /// absolutely no optimizations (used for the metadata module). + /// absolutely no optimizations (used for the allocator module). pub opt_level: Option<config::OptLevel>, - /// Some(level) to optimize binary size, or None to not affect program size. - pub opt_size: Option<config::OptLevel>, - pub pgo_gen: SwitchWithOptPath, pub pgo_use: Option<PathBuf>, pub pgo_sample_use: Option<PathBuf>, @@ -102,7 +98,6 @@ pub struct ModuleConfig { pub emit_obj: EmitObj, pub emit_thin_lto: bool, pub emit_thin_lto_summary: bool, - pub bc_cmdline: String, // Miscellaneous flags. These are mostly copied from command-line // options. @@ -110,7 +105,6 @@ pub struct ModuleConfig { pub lint_llvm_ir: bool, pub no_prepopulate_passes: bool, pub no_builtins: bool, - pub time_module: bool, pub vectorize_loop: bool, pub vectorize_slp: bool, pub merge_functions: bool, @@ -171,7 +165,6 @@ impl ModuleConfig { passes: if_regular!(sess.opts.cg.passes.clone(), vec![]), opt_level: opt_level_and_size, - opt_size: opt_level_and_size, pgo_gen: if_regular!( sess.opts.cg.profile_generate.clone(), @@ -221,17 +214,12 @@ impl ModuleConfig { sess.opts.output_types.contains_key(&OutputType::ThinLinkBitcode), false ), - bc_cmdline: sess.target.bitcode_llvm_cmdline.to_string(), verify_llvm_ir: sess.verify_llvm_ir(), lint_llvm_ir: sess.opts.unstable_opts.lint_llvm_ir, no_prepopulate_passes: sess.opts.cg.no_prepopulate_passes, no_builtins: no_builtins || sess.target.no_builtins, - // Exclude metadata and allocator modules from time_passes output, - // since they throw off the "LLVM passes" measurement. - time_module: if_regular!(true, false), - // Copy what clang does by turning on loop vectorization at O2 and // slp vectorization at O3. vectorize_loop: !sess.opts.cg.no_vectorize_loops @@ -454,7 +442,6 @@ pub(crate) fn start_async_codegen<B: ExtraBackendMethods>( backend: B, tcx: TyCtxt<'_>, target_cpu: String, - autodiff_items: &[AutoDiffItem], ) -> OngoingCodegen<B> { let (coordinator_send, coordinator_receive) = channel(); @@ -473,7 +460,6 @@ pub(crate) fn start_async_codegen<B: ExtraBackendMethods>( backend.clone(), tcx, &crate_info, - autodiff_items, shared_emitter, codegen_worker_send, coordinator_receive, @@ -728,7 +714,6 @@ pub(crate) enum WorkItem<B: WriteBackendMethods> { each_linked_rlib_for_lto: Vec<PathBuf>, needs_fat_lto: Vec<FatLtoInput<B>>, import_only_modules: Vec<(SerializedModule<B::ModuleBuffer>, WorkProduct)>, - autodiff: Vec<AutoDiffItem>, }, /// Performs thin-LTO on the given module. ThinLto(lto::ThinModule<B>), @@ -1001,7 +986,6 @@ 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)>, - autodiff: Vec<AutoDiffItem>, module_config: &ModuleConfig, ) -> Result<WorkItemResult<B>, FatalError> { for (module, wp) in import_only_modules { @@ -1013,7 +997,6 @@ fn execute_fat_lto_work_item<B: ExtraBackendMethods>( exported_symbols_for_lto, each_linked_rlib_for_lto, needs_fat_lto, - autodiff, )?; let module = B::codegen(cgcx, module, module_config)?; Ok(WorkItemResult::Finished(module)) @@ -1105,7 +1088,6 @@ fn start_executing_work<B: ExtraBackendMethods>( backend: B, tcx: TyCtxt<'_>, crate_info: &CrateInfo, - autodiff_items: &[AutoDiffItem], shared_emitter: SharedEmitter, codegen_worker_send: Sender<CguMessage>, coordinator_receive: Receiver<Message<B>>, @@ -1115,7 +1097,6 @@ fn start_executing_work<B: ExtraBackendMethods>( ) -> thread::JoinHandle<Result<CompiledModules, ()>> { let coordinator_send = tx_to_llvm_workers; let sess = tcx.sess; - let autodiff_items = autodiff_items.to_vec(); let mut each_linked_rlib_for_lto = Vec::new(); let mut each_linked_rlib_file_for_lto = Vec::new(); @@ -1448,7 +1429,6 @@ fn start_executing_work<B: ExtraBackendMethods>( each_linked_rlib_for_lto: each_linked_rlib_file_for_lto, needs_fat_lto, import_only_modules, - autodiff: autodiff_items.clone(), }, 0, )); @@ -1456,11 +1436,6 @@ fn start_executing_work<B: ExtraBackendMethods>( helper.request_token(); } } else { - if !autodiff_items.is_empty() { - let dcx = cgcx.create_dcx(); - dcx.handle().emit_fatal(AutodiffWithoutLto {}); - } - for (work, cost) in generate_thin_lto_work( &cgcx, &exported_symbols_for_lto, @@ -1740,7 +1715,7 @@ fn spawn_work<'a, B: ExtraBackendMethods>( llvm_start_time: &mut Option<VerboseTimingGuard<'a>>, work: WorkItem<B>, ) { - if cgcx.config(work.module_kind()).time_module && llvm_start_time.is_none() { + if llvm_start_time.is_none() { *llvm_start_time = Some(cgcx.prof.verbose_generic_activity("LLVM_passes")); } @@ -1795,7 +1770,6 @@ fn spawn_work<'a, B: ExtraBackendMethods>( each_linked_rlib_for_lto, needs_fat_lto, import_only_modules, - autodiff, } => { let _timer = cgcx .prof @@ -1806,7 +1780,6 @@ fn spawn_work<'a, B: ExtraBackendMethods>( &each_linked_rlib_for_lto, needs_fat_lto, import_only_modules, - autodiff, module_config, ) } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index b4556ced0b3..67cd1f4cd41 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -647,7 +647,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); ongoing_codegen.codegen_finished(tcx); @@ -665,8 +665,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>( // Run the monomorphization collector and partition the collected items into // codegen units. - let MonoItemPartitions { codegen_units, autodiff_items, .. } = - tcx.collect_and_partition_mono_items(()); + let MonoItemPartitions { codegen_units, .. } = tcx.collect_and_partition_mono_items(()); // Force all codegen_unit queries so they are already either red or green // when compile_codegen_unit accesses them. We are not able to re-execute @@ -679,34 +678,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>( } } - let ongoing_codegen = start_async_codegen(backend.clone(), tcx, target_cpu, autodiff_items); - - // Codegen an allocator shim, if necessary. - 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, - ); - } + let ongoing_codegen = start_async_codegen(backend.clone(), tcx, target_cpu); // For better throughput during parallel processing by LLVM, we used to sort // CGUs largest to smallest. This would lead to better thread utilization @@ -823,6 +795,35 @@ 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 7fa37198387..46fb323edca 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -176,14 +176,6 @@ fn process_builtin_attrs( let mut interesting_spans = InterestingAttributeDiagnosticSpans::default(); let rust_target_features = tcx.rust_target_features(LOCAL_CRATE); - // If our rustc version supports autodiff/enzyme, then we call our handler - // to check for any `#[rustc_autodiff(...)]` attributes. - // FIXME(jdonszelmann): merge with loop below - if cfg!(llvm_enzyme) { - let ad = autodiff_attrs(tcx, did.into()); - codegen_fn_attrs.autodiff_item = ad; - } - for attr in attrs.iter() { if let hir::Attribute::Parsed(p) = attr { match p { @@ -609,7 +601,7 @@ fn inherited_align<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Option<Align> { /// placeholder functions. We wrote the rustc_autodiff attributes ourself, so this should never /// panic, unless we introduced a bug when parsing the autodiff macro. //FIXME(jdonszelmann): put in the main loop. No need to have two..... :/ Let's do that when we make autodiff parsed. -fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> { +pub fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> { let attrs = tcx.get_attrs(id, sym::rustc_autodiff); let attrs = attrs.filter(|attr| attr.has_name(sym::rustc_autodiff)).collect::<Vec<_>>(); diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index e96590441fa..c3dc3e42b83 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -5,6 +5,7 @@ use rustc_ast as ast; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_data_structures::packed::Pu128; use rustc_hir::lang_items::LangItem; +use rustc_lint_defs::builtin::TAIL_CALL_TRACK_CALLER; use rustc_middle::mir::{self, AssertKind, InlineAsmMacro, SwitchTargets, UnwindTerminateReason}; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, ValidityRequirement}; use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; @@ -35,7 +36,7 @@ enum MergingSucc { True, } -/// Indicates to the call terminator codegen whether a cal +/// Indicates to the call terminator codegen whether a call /// is a normal call or an explicit tail call. #[derive(Debug, PartialEq)] enum CallKind { @@ -906,7 +907,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { fn_span, ); - let instance = match instance.def { + match instance.def { // We don't need AsyncDropGlueCtorShim here because it is not `noop func`, // it is `func returning noop future` ty::InstanceKind::DropGlue(_, None) => { @@ -995,14 +996,35 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { intrinsic.name, ); } - instance + (Some(instance), None) } } } - _ => instance, - }; - (Some(instance), None) + _ if kind == CallKind::Tail + && instance.def.requires_caller_location(bx.tcx()) => + { + if let Some(hir_id) = + terminator.source_info.scope.lint_root(&self.mir.source_scopes) + { + let msg = "tail calling a function marked with `#[track_caller]` has no special effect"; + bx.tcx().node_lint(TAIL_CALL_TRACK_CALLER, hir_id, |d| { + _ = d.primary_message(msg).span(fn_span) + }); + } + + let instance = ty::Instance::resolve_for_fn_ptr( + bx.tcx(), + bx.typing_env(), + def_id, + generic_args, + ) + .unwrap(); + + (None, Some(bx.get_fn_addr(instance))) + } + _ => (Some(instance), None), + } } ty::FnPtr(..) => (None, Some(callee.immediate())), _ => bug!("{} is not callable", callee.layout.ty), diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index f391c198e1a..c29ad90735b 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -1,6 +1,5 @@ use std::path::PathBuf; -use rustc_ast::expand::autodiff_attrs::AutoDiffItem; use rustc_errors::{DiagCtxtHandle, FatalError}; use rustc_middle::dep_graph::WorkProduct; @@ -23,7 +22,6 @@ pub trait WriteBackendMethods: Clone + 'static { exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], modules: Vec<FatLtoInput<Self>>, - diff_fncs: Vec<AutoDiffItem>, ) -> Result<ModuleCodegen<Self::Module>, FatalError>; /// Performs thin LTO by performing necessary global analysis and returning two /// lists, one of the modules that need optimization and another for modules that diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index 2985eafb63a..60518dafbf2 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -57,7 +57,7 @@ const_eval_const_context = {$kind -> } const_eval_const_heap_ptr_in_final = encountered `const_allocate` pointer in final value that was not made global - .note = use `const_make_global` to make allocated pointers immutable before returning + .note = use `const_make_global` to turn allocated pointers into immutable globals before returning const_eval_const_make_global_ptr_already_made_global = attempting to call `const_make_global` twice on the same allocation {$alloc} @@ -231,6 +231,9 @@ const_eval_mutable_borrow_escaping = const_eval_mutable_ptr_in_final = encountered mutable pointer in final value of {const_eval_intern_kind} +const_eval_partial_pointer_in_final = encountered partial pointer in final value of {const_eval_intern_kind} + .note = while pointers can be broken apart into individual bytes during const-evaluation, only complete pointers (with all their bytes in the right order) are supported in the final value + const_eval_nested_static_in_thread_local = #[thread_local] does not support implicit nested statics, please create explicit static items and refer to them instead const_eval_non_const_await = @@ -299,10 +302,8 @@ const_eval_panic = evaluation panicked: {$msg} const_eval_panic_non_str = argument to `panic!()` in a const context must have type `&str` -const_eval_partial_pointer_copy = - unable to copy parts of a pointer from memory at {$ptr} -const_eval_partial_pointer_overwrite = - unable to overwrite parts of a pointer in memory at {$ptr} +const_eval_partial_pointer_read = + unable to read parts of a pointer from memory at {$ptr} const_eval_pointer_arithmetic_overflow = overflowing pointer arithmetic: the total offset in bytes does not fit in an `isize` diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 8e2c138282a..ca707b50d50 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -827,7 +827,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // At this point, we are calling a function, `callee`, whose `DefId` is known... - // `begin_panic` and `#[rustc_const_panic_str]` functions accept generic + // `begin_panic` and `panic_display` functions accept generic // types other than str. Check to enforce that only str can be used in // const-eval. @@ -841,8 +841,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { return; } - // const-eval of `#[rustc_const_panic_str]` functions assumes the argument is `&&str` - if tcx.has_attr(callee, sym::rustc_const_panic_str) { + // const-eval of `panic_display` assumes the argument is `&&str` + if tcx.is_lang_item(callee, LangItem::PanicDisplay) { match args[0].node.ty(&self.ccx.body.local_decls, tcx).kind() { ty::Ref(_, ty, _) if matches!(ty.kind(), ty::Ref(_, ty, _) if ty.is_str()) => {} diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 5835660e1c3..4da2663319c 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -117,6 +117,13 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>( ecx.tcx.dcx().emit_err(errors::ConstHeapPtrInFinal { span: ecx.tcx.span }), ))); } + Err(InternError::PartialPointer) => { + throw_inval!(AlreadyReported(ReportedErrorInfo::non_const_eval_error( + ecx.tcx + .dcx() + .emit_err(errors::PartialPtrInFinal { span: ecx.tcx.span, kind: intern_kind }), + ))); + } } interp_ok(R::make_result(ret, ecx)) diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index a18ae79f318..da954cf4ed7 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -237,7 +237,7 @@ impl<'tcx> CompileTimeInterpCx<'tcx> { ) -> InterpResult<'tcx, Option<ty::Instance<'tcx>>> { let def_id = instance.def_id(); - if self.tcx.has_attr(def_id, sym::rustc_const_panic_str) + if self.tcx.is_lang_item(def_id, LangItem::PanicDisplay) || self.tcx.is_lang_item(def_id, LangItem::BeginPanic) { let args = self.copy_fn_args(args); diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index a4148cb145f..2d412ee5ec2 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -52,6 +52,15 @@ pub(crate) struct ConstHeapPtrInFinal { } #[derive(Diagnostic)] +#[diag(const_eval_partial_pointer_in_final)] +#[note] +pub(crate) struct PartialPtrInFinal { + #[primary_span] + pub span: Span, + pub kind: InternKind, +} + +#[derive(Diagnostic)] #[diag(const_eval_unstable_in_stable_exposed)] pub(crate) struct UnstableInStableExposed { pub gate: String, @@ -836,8 +845,7 @@ impl ReportErrorExt for UnsupportedOpInfo { UnsupportedOpInfo::Unsupported(s) => s.clone().into(), UnsupportedOpInfo::ExternTypeField => const_eval_extern_type_field, UnsupportedOpInfo::UnsizedLocal => const_eval_unsized_local, - UnsupportedOpInfo::OverwritePartialPointer(_) => const_eval_partial_pointer_overwrite, - UnsupportedOpInfo::ReadPartialPointer(_) => const_eval_partial_pointer_copy, + UnsupportedOpInfo::ReadPartialPointer(_) => const_eval_partial_pointer_read, UnsupportedOpInfo::ReadPointerAsInt(_) => const_eval_read_pointer_as_int, UnsupportedOpInfo::ThreadLocalStatic(_) => const_eval_thread_local_static, UnsupportedOpInfo::ExternStatic(_) => const_eval_extern_static, @@ -848,7 +856,7 @@ impl ReportErrorExt for UnsupportedOpInfo { use UnsupportedOpInfo::*; use crate::fluent_generated::*; - if let ReadPointerAsInt(_) | OverwritePartialPointer(_) | ReadPartialPointer(_) = self { + if let ReadPointerAsInt(_) | ReadPartialPointer(_) = self { diag.help(const_eval_ptr_as_bytes_1); diag.help(const_eval_ptr_as_bytes_2); } @@ -860,7 +868,7 @@ impl ReportErrorExt for UnsupportedOpInfo { | UnsupportedOpInfo::ExternTypeField | Unsupported(_) | ReadPointerAsInt(_) => {} - OverwritePartialPointer(ptr) | ReadPartialPointer(ptr) => { + ReadPartialPointer(ptr) => { diag.arg("ptr", ptr); } ThreadLocalStatic(did) | ExternStatic(did) => rustc_middle::ty::tls::with(|tcx| { diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs index bb59b9f5418..5d510a98352 100644 --- a/compiler/rustc_const_eval/src/interpret/intern.rs +++ b/compiler/rustc_const_eval/src/interpret/intern.rs @@ -19,9 +19,12 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_hir as hir; use rustc_hir::definitions::{DefPathData, DisambiguatorState}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; -use rustc_middle::mir::interpret::{ConstAllocation, CtfeProvenance, InterpResult}; +use rustc_middle::mir::interpret::{ + AllocBytes, ConstAllocation, CtfeProvenance, InterpResult, Provenance, +}; use rustc_middle::query::TyCtxtAt; use rustc_middle::span_bug; +use rustc_middle::ty::TyCtxt; use rustc_middle::ty::layout::TyAndLayout; use rustc_span::def_id::LocalDefId; use tracing::{instrument, trace}; @@ -52,39 +55,30 @@ impl HasStaticRootDefId for const_eval::CompileTimeMachine<'_> { } } -/// Intern an allocation. Returns `Err` if the allocation does not exist in the local memory. -/// -/// `mutability` can be used to force immutable interning: if it is `Mutability::Not`, the -/// allocation is interned immutably; if it is `Mutability::Mut`, then the allocation *must be* -/// already mutable (as a sanity check). -/// -/// Returns an iterator over all relocations referred to by this allocation. -fn intern_shallow<'tcx, M: CompileTimeMachine<'tcx>>( - ecx: &mut InterpCx<'tcx, M>, - alloc_id: AllocId, +fn prepare_alloc<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>( + tcx: TyCtxt<'tcx>, + kind: MemoryKind<const_eval::MemoryKind>, + alloc: &mut Allocation<Prov, Extra, Bytes>, mutability: Mutability, - disambiguator: Option<&mut DisambiguatorState>, -) -> Result<impl Iterator<Item = CtfeProvenance> + 'tcx, InternError> { - trace!("intern_shallow {:?}", alloc_id); - // remove allocation - // FIXME(#120456) - is `swap_remove` correct? - let Some((kind, mut alloc)) = ecx.memory.alloc_map.swap_remove(&alloc_id) else { - return Err(InternError::DanglingPointer); - }; - +) -> Result<(), InternError> { match kind { MemoryKind::Machine(const_eval::MemoryKind::Heap { was_made_global }) => { if !was_made_global { // Attempting to intern a `const_allocate`d pointer that was not made global via - // `const_make_global`. We want to error here, but we have to first put the - // allocation back into the `alloc_map` to keep things in a consistent state. - ecx.memory.alloc_map.insert(alloc_id, (kind, alloc)); + // `const_make_global`. + tcx.dcx().delayed_bug("non-global heap allocation in const value"); return Err(InternError::ConstAllocNotGlobal); } } MemoryKind::Stack | MemoryKind::CallerLocation => {} } + if !alloc.provenance_merge_bytes(&tcx) { + // Per-byte provenance is not supported by backends, so we cannot accept it here. + tcx.dcx().delayed_bug("partial pointer in const value"); + return Err(InternError::PartialPointer); + } + // Set allocation mutability as appropriate. This is used by LLVM to put things into // read-only memory, and also by Miri when evaluating other globals that // access this one. @@ -97,6 +91,36 @@ fn intern_shallow<'tcx, M: CompileTimeMachine<'tcx>>( assert_eq!(alloc.mutability, Mutability::Mut); } } + Ok(()) +} + +/// Intern an allocation. Returns `Err` if the allocation does not exist in the local memory. +/// +/// `mutability` can be used to force immutable interning: if it is `Mutability::Not`, the +/// allocation is interned immutably; if it is `Mutability::Mut`, then the allocation *must be* +/// already mutable (as a sanity check). +/// +/// Returns an iterator over all relocations referred to by this allocation. +fn intern_shallow<'tcx, M: CompileTimeMachine<'tcx>>( + ecx: &mut InterpCx<'tcx, M>, + alloc_id: AllocId, + mutability: Mutability, + disambiguator: Option<&mut DisambiguatorState>, +) -> Result<impl Iterator<Item = CtfeProvenance> + 'tcx, InternError> { + trace!("intern_shallow {:?}", alloc_id); + // remove allocation + // FIXME(#120456) - is `swap_remove` correct? + let Some((kind, mut alloc)) = ecx.memory.alloc_map.swap_remove(&alloc_id) else { + return Err(InternError::DanglingPointer); + }; + + if let Err(err) = prepare_alloc(*ecx.tcx, kind, &mut alloc, mutability) { + // We want to error here, but we have to first put the + // allocation back into the `alloc_map` to keep things in a consistent state. + ecx.memory.alloc_map.insert(alloc_id, (kind, alloc)); + return Err(err); + } + // link the alloc id to the actual allocation let alloc = ecx.tcx.mk_const_alloc(alloc); if let Some(static_id) = ecx.machine.static_def_id() { @@ -166,6 +190,7 @@ pub enum InternError { BadMutablePointer, DanglingPointer, ConstAllocNotGlobal, + PartialPointer, } /// Intern `ret` and everything it references. @@ -221,13 +246,11 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx>>( let mut todo: Vec<_> = if is_static { // Do not steal the root allocation, we need it later to create the return value of `eval_static_initializer`. // But still change its mutability to match the requested one. - let alloc = ecx.memory.alloc_map.get_mut(&base_alloc_id).unwrap(); - alloc.1.mutability = base_mutability; - alloc.1.provenance().ptrs().iter().map(|&(_, prov)| prov).collect() + let (kind, alloc) = ecx.memory.alloc_map.get_mut(&base_alloc_id).unwrap(); + prepare_alloc(*ecx.tcx, *kind, alloc, base_mutability)?; + alloc.provenance().ptrs().iter().map(|&(_, prov)| prov).collect() } else { - intern_shallow(ecx, base_alloc_id, base_mutability, Some(&mut disambiguator)) - .unwrap() - .collect() + intern_shallow(ecx, base_alloc_id, base_mutability, Some(&mut disambiguator))?.collect() }; // We need to distinguish "has just been interned" from "was already in `tcx`", // so we track this in a separate set. @@ -235,7 +258,6 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx>>( // Whether we encountered a bad mutable pointer. // We want to first report "dangling" and then "mutable", so we need to delay reporting these // errors. - let mut result = Ok(()); let mut found_bad_mutable_ptr = false; // Keep interning as long as there are things to intern. @@ -310,20 +332,15 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx>>( // okay with losing some potential for immutability here. This can anyway only affect // `static mut`. just_interned.insert(alloc_id); - match intern_shallow(ecx, alloc_id, inner_mutability, Some(&mut disambiguator)) { - Ok(nested) => todo.extend(nested), - Err(err) => { - ecx.tcx.dcx().delayed_bug("error during const interning"); - result = Err(err); - } - } + let next = intern_shallow(ecx, alloc_id, inner_mutability, Some(&mut disambiguator))?; + todo.extend(next); } - if found_bad_mutable_ptr && result.is_ok() { + if found_bad_mutable_ptr { // We found a mutable pointer inside a const where inner allocations should be immutable, // and there was no other error. This should usually never happen! However, this can happen // in unleash-miri mode, so report it as a normal error then. if ecx.tcx.sess.opts.unstable_opts.unleash_the_miri_inside_of_you { - result = Err(InternError::BadMutablePointer); + return Err(InternError::BadMutablePointer); } else { span_bug!( ecx.tcx.span, @@ -331,7 +348,7 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx>>( ); } } - result + Ok(()) } /// Intern `ret`. This function assumes that `ret` references no other allocation. diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 23b40894f16..2c1e5087e1c 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -1314,29 +1314,20 @@ impl<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes> } /// Mark the given sub-range (relative to this allocation reference) as uninitialized. - pub fn write_uninit(&mut self, range: AllocRange) -> InterpResult<'tcx> { + pub fn write_uninit(&mut self, range: AllocRange) { let range = self.range.subrange(range); - self.alloc - .write_uninit(&self.tcx, range) - .map_err(|e| e.to_interp_error(self.alloc_id)) - .into() + self.alloc.write_uninit(&self.tcx, range); } /// Mark the entire referenced range as uninitialized - pub fn write_uninit_full(&mut self) -> InterpResult<'tcx> { - self.alloc - .write_uninit(&self.tcx, self.range) - .map_err(|e| e.to_interp_error(self.alloc_id)) - .into() + pub fn write_uninit_full(&mut self) { + self.alloc.write_uninit(&self.tcx, self.range); } /// Remove all provenance in the reference range. - pub fn clear_provenance(&mut self) -> InterpResult<'tcx> { - self.alloc - .clear_provenance(&self.tcx, self.range) - .map_err(|e| e.to_interp_error(self.alloc_id)) - .into() + pub fn clear_provenance(&mut self) { + self.alloc.clear_provenance(&self.tcx, self.range); } } @@ -1427,11 +1418,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Side-step AllocRef and directly access the underlying bytes more efficiently. // (We are staying inside the bounds here and all bytes do get overwritten so all is good.) - let alloc_id = alloc_ref.alloc_id; - let bytes = alloc_ref - .alloc - .get_bytes_unchecked_for_overwrite(&alloc_ref.tcx, alloc_ref.range) - .map_err(move |e| e.to_interp_error(alloc_id))?; + let bytes = + alloc_ref.alloc.get_bytes_unchecked_for_overwrite(&alloc_ref.tcx, alloc_ref.range); // `zip` would stop when the first iterator ends; we want to definitely // cover all of `bytes`. for dest in bytes { @@ -1513,10 +1501,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // `get_bytes_mut` will clear the provenance, which is correct, // since we don't want to keep any provenance at the target. // This will also error if copying partial provenance is not supported. - let provenance = src_alloc - .provenance() - .prepare_copy(src_range, dest_offset, num_copies, self) - .map_err(|e| e.to_interp_error(src_alloc_id))?; + let provenance = + src_alloc.provenance().prepare_copy(src_range, dest_offset, num_copies, self); // Prepare a copy of the initialization mask. let init = src_alloc.init_mask().prepare_copy(src_range); @@ -1534,10 +1520,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { dest_range, )?; // Yes we do overwrite all bytes in `dest_bytes`. - let dest_bytes = dest_alloc - .get_bytes_unchecked_for_overwrite_ptr(&tcx, dest_range) - .map_err(|e| e.to_interp_error(dest_alloc_id))? - .as_mut_ptr(); + let dest_bytes = + dest_alloc.get_bytes_unchecked_for_overwrite_ptr(&tcx, dest_range).as_mut_ptr(); if init.no_bytes_init() { // Fast path: If all bytes are `uninit` then there is nothing to copy. The target range @@ -1546,9 +1530,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // This also avoids writing to the target bytes so that the backing allocation is never // touched if the bytes stay uninitialized for the whole interpreter execution. On contemporary // operating system this can avoid physically allocating the page. - dest_alloc - .write_uninit(&tcx, dest_range) - .map_err(|e| e.to_interp_error(dest_alloc_id))?; + dest_alloc.write_uninit(&tcx, dest_range); // `write_uninit` also resets the provenance, so we are done. return interp_ok(()); } diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 6ff50dc700f..3255ffa54aa 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -705,7 +705,7 @@ where match value { Immediate::Scalar(scalar) => { - alloc.write_scalar(alloc_range(Size::ZERO, scalar.size()), scalar) + alloc.write_scalar(alloc_range(Size::ZERO, scalar.size()), scalar)?; } Immediate::ScalarPair(a_val, b_val) => { let BackendRepr::ScalarPair(a, b) = layout.backend_repr else { @@ -725,10 +725,10 @@ where alloc.write_scalar(alloc_range(Size::ZERO, a_val.size()), a_val)?; alloc.write_scalar(alloc_range(b_offset, b_val.size()), b_val)?; // We don't have to reset padding here, `write_immediate` will anyway do a validation run. - interp_ok(()) } Immediate::Uninit => alloc.write_uninit_full(), } + interp_ok(()) } pub fn write_uninit( @@ -748,7 +748,7 @@ where // Zero-sized access return interp_ok(()); }; - alloc.write_uninit_full()?; + alloc.write_uninit_full(); } } interp_ok(()) @@ -772,7 +772,7 @@ where // Zero-sized access return interp_ok(()); }; - alloc.clear_provenance()?; + alloc.clear_provenance(); } } interp_ok(()) diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index ab0c0665d51..5e8bee65706 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -949,7 +949,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { let padding_size = offset - padding_cleared_until; let range = alloc_range(padding_start, padding_size); trace!("reset_padding on {}: resetting padding range {range:?}", mplace.layout.ty); - alloc.write_uninit(range)?; + alloc.write_uninit(range); } padding_cleared_until = offset + size; } @@ -1239,7 +1239,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, if self.reset_provenance_and_padding { // We can't share this with above as above, we might be looking at read-only memory. let mut alloc = self.ecx.get_ptr_alloc_mut(mplace.ptr(), size)?.expect("we already excluded size 0"); - alloc.clear_provenance()?; + alloc.clear_provenance(); // Also, mark this as containing data, not padding. self.add_data_range(mplace.ptr(), size); } diff --git a/compiler/rustc_const_eval/src/util/type_name.rs b/compiler/rustc_const_eval/src/util/type_name.rs index 9d6674873b1..13cc607135a 100644 --- a/compiler/rustc_const_eval/src/util/type_name.rs +++ b/compiler/rustc_const_eval/src/util/type_name.rs @@ -5,7 +5,7 @@ use rustc_hir::def_id::CrateNum; use rustc_hir::definitions::DisambiguatedDefPathData; use rustc_middle::bug; use rustc_middle::ty::print::{PrettyPrinter, PrintError, Printer}; -use rustc_middle::ty::{self, GenericArg, GenericArgKind, Ty, TyCtxt}; +use rustc_middle::ty::{self, GenericArg, Ty, TyCtxt}; struct TypeNamePrinter<'tcx> { tcx: TyCtxt<'tcx>, @@ -18,9 +18,10 @@ impl<'tcx> Printer<'tcx> for TypeNamePrinter<'tcx> { } fn print_region(&mut self, _region: ty::Region<'_>) -> Result<(), PrintError> { - // This is reachable (via `pretty_print_dyn_existential`) even though - // `<Self As PrettyPrinter>::should_print_region` returns false. See #144994. - Ok(()) + // FIXME: most regions have been erased by the time this code runs. + // Just printing `'_` is a bit hacky but gives mostly good results, and + // doing better is difficult. See `should_print_optional_region`. + write!(self, "'_") } fn print_type(&mut self, ty: Ty<'tcx>) -> Result<(), PrintError> { @@ -125,10 +126,8 @@ impl<'tcx> Printer<'tcx> for TypeNamePrinter<'tcx> { args: &[GenericArg<'tcx>], ) -> Result<(), PrintError> { print_prefix(self)?; - let args = - args.iter().cloned().filter(|arg| !matches!(arg.kind(), GenericArgKind::Lifetime(_))); - if args.clone().next().is_some() { - self.generic_delimiters(|cx| cx.comma_sep(args)) + if !args.is_empty() { + self.generic_delimiters(|cx| cx.comma_sep(args.iter().copied())) } else { Ok(()) } @@ -136,8 +135,15 @@ impl<'tcx> Printer<'tcx> for TypeNamePrinter<'tcx> { } impl<'tcx> PrettyPrinter<'tcx> for TypeNamePrinter<'tcx> { - fn should_print_region(&self, _region: ty::Region<'_>) -> bool { - false + fn should_print_optional_region(&self, _region: ty::Region<'_>) -> bool { + // Bound regions are always printed (as `'_`), which gives some idea that they are special, + // even though the `for` is omitted by the pretty printer. + // E.g. `for<'a, 'b> fn(&'a u32, &'b u32)` is printed as "fn(&'_ u32, &'_ u32)". + match _region.kind() { + ty::ReErased => false, + ty::ReBound(..) => true, + _ => unreachable!(), + } } fn generic_delimiters( diff --git a/compiler/rustc_error_codes/src/error_codes/E0793.md b/compiler/rustc_error_codes/src/error_codes/E0793.md index ccd1b43bd19..2722dac104b 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0793.md +++ b/compiler/rustc_error_codes/src/error_codes/E0793.md @@ -1,4 +1,9 @@ -An unaligned reference to a field of a [packed] struct got created. +An unaligned reference to a field of a [packed] `struct` or `union` was created. + +The `#[repr(packed)]` attribute removes padding between fields, which can +cause fields to be stored at unaligned memory addresses. Creating references +to such fields violates Rust's memory safety guarantees and can lead to +undefined behavior in optimized code. Erroneous code example: @@ -45,9 +50,36 @@ unsafe { // For formatting, we can create a copy to avoid the direct reference. let copy = foo.field1; println!("{}", copy); + // Creating a copy can be written in a single line with curly braces. // (This is equivalent to the two lines above.) println!("{}", { foo.field1 }); + + // A reference to a field that will always be sufficiently aligned is safe: + println!("{}", foo.field2); +} +``` + +### Unions + +Although creating a reference to a `union` field is `unsafe`, this error +will still be triggered if the referenced field is not sufficiently +aligned. Use `addr_of!` and raw pointers in the same way as for struct fields. + +```compile_fail,E0793 +#[repr(packed)] +pub union Foo { + field1: u64, + field2: u8, +} + +unsafe { + let foo = Foo { field1: 0 }; + // Accessing the field directly is fine. + let val = foo.field1; + + // A reference to a packed union field causes an error. + let val = &foo.field1; // ERROR } ``` diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index ab6b8f92802..6fbedaf5b10 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -1159,10 +1159,6 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ EncodeCrossCrate::Yes, "`#[rustc_do_not_const_check]` skips const-check for this function's body", ), rustc_attr!( - rustc_const_panic_str, Normal, template!(Word), WarnFollowing, - EncodeCrossCrate::Yes, "`#[rustc_const_panic_str]` ensures the argument to this function is &&str during const-check", - ), - rustc_attr!( rustc_const_stable_indirect, Normal, template!(Word), WarnFollowing, diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 87ecc7b41e2..07f928b8c88 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -470,6 +470,8 @@ declare_features! ( (unstable, deprecated_suggestion, "1.61.0", Some(94785)), /// Allows deref patterns. (incomplete, deref_patterns, "1.79.0", Some(87121)), + /// Allows deriving the From trait on single-field structs. + (unstable, derive_from, "CURRENT_RUSTC_VERSION", Some(144889)), /// Tells rustdoc to automatically generate `#[doc(cfg(...))]`. (unstable, doc_auto_cfg, "1.58.0", Some(43781)), /// Allows `#[doc(cfg(...))]`. diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 75c04b23ed6..905b84a8cbe 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -286,6 +286,7 @@ language_item_table! { Panic, sym::panic, panic_fn, Target::Fn, GenericRequirement::Exact(0); PanicNounwind, sym::panic_nounwind, panic_nounwind, Target::Fn, GenericRequirement::Exact(0); PanicFmt, sym::panic_fmt, panic_fmt, Target::Fn, GenericRequirement::None; + PanicDisplay, sym::panic_display, panic_display, Target::Fn, GenericRequirement::None; ConstPanicFmt, sym::const_panic_fmt, const_panic_fmt, Target::Fn, GenericRequirement::None; PanicBoundsCheck, sym::panic_bounds_check, panic_bounds_check_fn, Target::Fn, GenericRequirement::Exact(0); PanicMisalignedPointerDereference, sym::panic_misaligned_pointer_dereference, panic_misaligned_pointer_dereference_fn, Target::Fn, GenericRequirement::Exact(0); diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 4441dd6ebd6..f50aed0b3c2 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -135,6 +135,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::round_ties_even_f32 | sym::round_ties_even_f64 | sym::round_ties_even_f128 + | sym::autodiff | sym::const_eval_select => hir::Safety::Safe, _ => hir::Safety::Unsafe, }; @@ -198,6 +199,7 @@ pub(crate) fn check_intrinsic_type( let safety = intrinsic_operation_unsafety(tcx, intrinsic_id); let n_lts = 0; let (n_tps, n_cts, inputs, output) = match intrinsic_name { + sym::autodiff => (4, 0, vec![param(0), param(1), param(2)], param(3)), sym::abort => (0, 0, vec![], tcx.types.never), sym::unreachable => (0, 0, vec![], tcx.types.never), sym::breakpoint => (0, 0, vec![], tcx.types.unit), diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs index 0043f0c7117..b38639ed8c6 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs @@ -390,45 +390,13 @@ fn check_predicates<'tcx>( let mut res = Ok(()); for (clause, span) in impl1_predicates { - if !impl2_predicates.iter().any(|pred2| trait_predicates_eq(clause.as_predicate(), *pred2)) - { + if !impl2_predicates.iter().any(|&pred2| clause.as_predicate() == pred2) { res = res.and(check_specialization_on(tcx, clause, span)) } } res } -/// Checks if some predicate on the specializing impl (`predicate1`) is the same -/// as some predicate on the base impl (`predicate2`). -/// -/// This basically just checks syntactic equivalence, but is a little more -/// forgiving since we want to equate `T: Tr` with `T: [const] Tr` so this can work: -/// -/// ```ignore (illustrative) -/// #[rustc_specialization_trait] -/// trait Specialize { } -/// -/// impl<T: Bound> Tr for T { } -/// impl<T: [const] Bound + Specialize> const Tr for T { } -/// ``` -/// -/// However, we *don't* want to allow the reverse, i.e., when the bound on the -/// specializing impl is not as const as the bound on the base impl: -/// -/// ```ignore (illustrative) -/// impl<T: [const] Bound> const Tr for T { } -/// impl<T: Bound + Specialize> const Tr for T { } // should be T: [const] Bound -/// ``` -/// -/// So we make that check in this function and try to raise a helpful error message. -fn trait_predicates_eq<'tcx>( - predicate1: ty::Predicate<'tcx>, - predicate2: ty::Predicate<'tcx>, -) -> bool { - // FIXME(const_trait_impl) - predicate1 == predicate2 -} - #[instrument(level = "debug", skip(tcx))] fn check_specialization_on<'tcx>( tcx: TyCtxt<'tcx>, diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 48bb45de53e..7afc555b598 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -910,7 +910,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // const stability checking here too, I guess. if self.tcx.is_conditionally_const(callee_did) { let q = self.tcx.const_conditions(callee_did); - // FIXME(const_trait_impl): Use this span with a better cause code. for (idx, (cond, pred_span)) in q.instantiate(self.tcx, callee_args).into_iter().enumerate() { diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index d9163d94710..cb159a0b914 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -756,22 +756,22 @@ impl<'tcx> LateContext<'tcx> { } fn print_region(&mut self, _region: ty::Region<'_>) -> Result<(), PrintError> { - unreachable!(); // because `path_generic_args` ignores the `GenericArgs` + unreachable!(); // because `print_path_with_generic_args` ignores the `GenericArgs` } fn print_type(&mut self, _ty: Ty<'tcx>) -> Result<(), PrintError> { - unreachable!(); // because `path_generic_args` ignores the `GenericArgs` + unreachable!(); // because `print_path_with_generic_args` ignores the `GenericArgs` } fn print_dyn_existential( &mut self, _predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, ) -> Result<(), PrintError> { - unreachable!(); // because `path_generic_args` ignores the `GenericArgs` + unreachable!(); // because `print_path_with_generic_args` ignores the `GenericArgs` } fn print_const(&mut self, _ct: ty::Const<'tcx>) -> Result<(), PrintError> { - unreachable!(); // because `path_generic_args` ignores the `GenericArgs` + unreachable!(); // because `print_path_with_generic_args` ignores the `GenericArgs` } fn print_crate_name(&mut self, cnum: CrateNum) -> Result<(), PrintError> { diff --git a/compiler/rustc_lint/src/drop_forget_useless.rs b/compiler/rustc_lint/src/drop_forget_useless.rs index 7f098893f7d..c2d137986ce 100644 --- a/compiler/rustc_lint/src/drop_forget_useless.rs +++ b/compiler/rustc_lint/src/drop_forget_useless.rs @@ -151,7 +151,7 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetUseless { && let Node::Stmt(stmt) = node && let StmtKind::Semi(e) = stmt.kind && e.hir_id == expr.hir_id - && let Some(arg_span) = arg.span.find_ancestor_inside(expr.span) + && let Some(arg_span) = arg.span.find_ancestor_inside_same_ctxt(expr.span) { UseLetUnderscoreIgnoreSuggestion::Suggestion { start_span: expr.span.shrink_to_lo().until(arg_span), diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index bee80335f74..a0083a7df3d 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -5105,3 +5105,36 @@ declare_lint! { report_in_deps: true, }; } + +declare_lint! { + /// The `tail_call_track_caller` lint detects usage of `become` attempting to tail call + /// a function marked with `#[track_caller]`. + /// + /// ### Example + /// + /// ```rust + /// #![feature(explicit_tail_calls)] + /// #![expect(incomplete_features)] + /// + /// #[track_caller] + /// fn f() {} + /// + /// fn g() { + /// become f(); + /// } + /// + /// g(); + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Due to implementation details of tail calls and `#[track_caller]` attribute, calls to + /// functions marked with `#[track_caller]` cannot become tail calls. As such using `become` + /// is no different than a normal call (except for changes in drop order). + pub TAIL_CALL_TRACK_CALLER, + Warn, + "detects tail calls of functions marked with `#[track_caller]`", + @feature_gate = explicit_tail_calls; +} diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index 2852c4cbd34..7d2fc0995aa 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -1,7 +1,6 @@ use std::borrow::Cow; use rustc_abi::Align; -use rustc_ast::expand::autodiff_attrs::AutoDiffAttrs; use rustc_hir::attrs::{InlineAttr, InstructionSetAttr, Linkage, OptimizeAttr}; use rustc_hir::def_id::DefId; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; @@ -75,8 +74,6 @@ pub struct CodegenFnAttrs { /// The `#[patchable_function_entry(...)]` attribute. Indicates how many nops should be around /// the function entry. pub patchable_function_entry: Option<PatchableFunctionEntry>, - /// For the `#[autodiff]` macros. - pub autodiff_item: Option<AutoDiffAttrs>, } #[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)] @@ -182,7 +179,6 @@ impl CodegenFnAttrs { instruction_set: None, alignment: None, patchable_function_entry: None, - autodiff_item: None, } } diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index 96131d47a17..164433aede2 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -448,6 +448,11 @@ impl<'tcx> Const<'tcx> { Self::Val(val, ty) } + #[inline] + pub fn from_ty_value(tcx: TyCtxt<'tcx>, val: ty::Value<'tcx>) -> Self { + Self::Ty(val.ty, ty::Const::new_value(tcx, val.valtree, val.ty)) + } + pub fn from_bits( tcx: TyCtxt<'tcx>, bits: u128, diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 27ead514531..2ea92a39d48 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -306,8 +306,6 @@ pub enum AllocError { ScalarSizeMismatch(ScalarSizeMismatch), /// Encountered a pointer where we needed raw bytes. ReadPointerAsInt(Option<BadBytesAccess>), - /// Partially overwriting a pointer. - OverwritePartialPointer(Size), /// Partially copying a pointer. ReadPartialPointer(Size), /// Using uninitialized data where it is not allowed. @@ -331,9 +329,6 @@ impl AllocError { ReadPointerAsInt(info) => InterpErrorKind::Unsupported( UnsupportedOpInfo::ReadPointerAsInt(info.map(|b| (alloc_id, b))), ), - OverwritePartialPointer(offset) => InterpErrorKind::Unsupported( - UnsupportedOpInfo::OverwritePartialPointer(Pointer::new(alloc_id, offset)), - ), ReadPartialPointer(offset) => InterpErrorKind::Unsupported( UnsupportedOpInfo::ReadPartialPointer(Pointer::new(alloc_id, offset)), ), @@ -633,11 +628,11 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes> &mut self, cx: &impl HasDataLayout, range: AllocRange, - ) -> AllocResult<&mut [u8]> { + ) -> &mut [u8] { self.mark_init(range, true); - self.provenance.clear(range, cx)?; + self.provenance.clear(range, cx); - Ok(&mut self.bytes[range.start.bytes_usize()..range.end().bytes_usize()]) + &mut self.bytes[range.start.bytes_usize()..range.end().bytes_usize()] } /// A raw pointer variant of `get_bytes_unchecked_for_overwrite` that avoids invalidating existing immutable aliases @@ -646,15 +641,15 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes> &mut self, cx: &impl HasDataLayout, range: AllocRange, - ) -> AllocResult<*mut [u8]> { + ) -> *mut [u8] { self.mark_init(range, true); - self.provenance.clear(range, cx)?; + self.provenance.clear(range, cx); assert!(range.end().bytes_usize() <= self.bytes.len()); // need to do our own bounds-check // Crucially, we go via `AllocBytes::as_mut_ptr`, not `AllocBytes::deref_mut`. let begin_ptr = self.bytes.as_mut_ptr().wrapping_add(range.start.bytes_usize()); let len = range.end().bytes_usize() - range.start.bytes_usize(); - Ok(ptr::slice_from_raw_parts_mut(begin_ptr, len)) + ptr::slice_from_raw_parts_mut(begin_ptr, len) } /// This gives direct mutable access to the entire buffer, just exposing their internal state @@ -723,26 +718,45 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes> let ptr = Pointer::new(prov, Size::from_bytes(bits)); return Ok(Scalar::from_pointer(ptr, cx)); } - - // If we can work on pointers byte-wise, join the byte-wise provenances. - if Prov::OFFSET_IS_ADDR { - let mut prov = self.provenance.get(range.start, cx); + // The other easy case is total absence of provenance. + if self.provenance.range_empty(range, cx) { + return Ok(Scalar::from_uint(bits, range.size)); + } + // If we get here, we have to check per-byte provenance, and join them together. + let prov = 'prov: { + // Initialize with first fragment. Must have index 0. + let Some((mut joint_prov, 0)) = self.provenance.get_byte(range.start, cx) else { + break 'prov None; + }; + // Update with the remaining fragments. for offset in Size::from_bytes(1)..range.size { - let this_prov = self.provenance.get(range.start + offset, cx); - prov = Prov::join(prov, this_prov); + // Ensure there is provenance here and it has the right index. + let Some((frag_prov, frag_idx)) = + self.provenance.get_byte(range.start + offset, cx) + else { + break 'prov None; + }; + // Wildcard provenance is allowed to come with any index (this is needed + // for Miri's native-lib mode to work). + if u64::from(frag_idx) != offset.bytes() && Some(frag_prov) != Prov::WILDCARD { + break 'prov None; + } + // Merge this byte's provenance with the previous ones. + joint_prov = match Prov::join(joint_prov, frag_prov) { + Some(prov) => prov, + None => break 'prov None, + }; } - // Now use this provenance. - let ptr = Pointer::new(prov, Size::from_bytes(bits)); - return Ok(Scalar::from_maybe_pointer(ptr, cx)); - } else { - // Without OFFSET_IS_ADDR, the only remaining case we can handle is total absence of - // provenance. - if self.provenance.range_empty(range, cx) { - return Ok(Scalar::from_uint(bits, range.size)); - } - // Else we have mixed provenance, that doesn't work. + break 'prov Some(joint_prov); + }; + if prov.is_none() && !Prov::OFFSET_IS_ADDR { + // There are some bytes with provenance here but overall the provenance does not add up. + // We need `OFFSET_IS_ADDR` to fall back to no-provenance here; without that option, we must error. return Err(AllocError::ReadPartialPointer(range.start)); } + // We can use this provenance. + let ptr = Pointer::new(prov, Size::from_bytes(bits)); + return Ok(Scalar::from_maybe_pointer(ptr, cx)); } else { // We are *not* reading a pointer. // If we can just ignore provenance or there is none, that's easy. @@ -782,7 +796,7 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes> let endian = cx.data_layout().endian; // Yes we do overwrite all the bytes in `dst`. - let dst = self.get_bytes_unchecked_for_overwrite(cx, range)?; + let dst = self.get_bytes_unchecked_for_overwrite(cx, range); write_target_uint(endian, dst, bytes).unwrap(); // See if we have to also store some provenance. @@ -795,10 +809,9 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes> } /// Write "uninit" to the given memory range. - pub fn write_uninit(&mut self, cx: &impl HasDataLayout, range: AllocRange) -> AllocResult { + pub fn write_uninit(&mut self, cx: &impl HasDataLayout, range: AllocRange) { self.mark_init(range, false); - self.provenance.clear(range, cx)?; - Ok(()) + self.provenance.clear(range, cx); } /// Mark all bytes in the given range as initialised and reset the provenance @@ -817,9 +830,12 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes> } /// Remove all provenance in the given memory range. - pub fn clear_provenance(&mut self, cx: &impl HasDataLayout, range: AllocRange) -> AllocResult { - self.provenance.clear(range, cx)?; - return Ok(()); + pub fn clear_provenance(&mut self, cx: &impl HasDataLayout, range: AllocRange) { + self.provenance.clear(range, cx); + } + + pub fn provenance_merge_bytes(&mut self, cx: &impl HasDataLayout) -> bool { + self.provenance.merge_bytes(cx) } /// Applies a previously prepared provenance copy. diff --git a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs index 119d4be64e6..ea8596ea286 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs @@ -10,7 +10,7 @@ use rustc_macros::HashStable; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use tracing::trace; -use super::{AllocError, AllocRange, AllocResult, CtfeProvenance, Provenance, alloc_range}; +use super::{AllocRange, CtfeProvenance, Provenance, alloc_range}; /// Stores the provenance information of pointers stored in memory. #[derive(Clone, PartialEq, Eq, Hash, Debug)] @@ -19,25 +19,25 @@ pub struct ProvenanceMap<Prov = CtfeProvenance> { /// `Provenance` in this map applies from the given offset for an entire pointer-size worth of /// bytes. Two entries in this map are always at least a pointer size apart. ptrs: SortedMap<Size, Prov>, - /// Provenance in this map only applies to the given single byte. - /// This map is disjoint from the previous. It will always be empty when - /// `Prov::OFFSET_IS_ADDR` is false. - bytes: Option<Box<SortedMap<Size, Prov>>>, + /// This stores byte-sized provenance fragments. + /// The `u8` indicates the position of this byte inside its original pointer. + /// If the bytes are re-assembled in their original order, the pointer can be used again. + /// Wildcard provenance is allowed to have index 0 everywhere. + bytes: Option<Box<SortedMap<Size, (Prov, u8)>>>, } // These impls are generic over `Prov` since `CtfeProvenance` is only decodable/encodable // for some particular `D`/`S`. impl<D: Decoder, Prov: Provenance + Decodable<D>> Decodable<D> for ProvenanceMap<Prov> { fn decode(d: &mut D) -> Self { - assert!(!Prov::OFFSET_IS_ADDR); // only `CtfeProvenance` is ever serialized + // `bytes` is not in the serialized format Self { ptrs: Decodable::decode(d), bytes: None } } } impl<S: Encoder, Prov: Provenance + Encodable<S>> Encodable<S> for ProvenanceMap<Prov> { fn encode(&self, s: &mut S) { let Self { ptrs, bytes } = self; - assert!(!Prov::OFFSET_IS_ADDR); // only `CtfeProvenance` is ever serialized - debug_assert!(bytes.is_none()); // without `OFFSET_IS_ADDR`, this is always empty + assert!(bytes.is_none()); // interning refuses allocations with pointer fragments ptrs.encode(s) } } @@ -58,10 +58,10 @@ impl ProvenanceMap { /// Give access to the ptr-sized provenances (which can also be thought of as relocations, and /// indeed that is how codegen treats them). /// - /// Only exposed with `CtfeProvenance` provenance, since it panics if there is bytewise provenance. + /// Only use on interned allocations, as other allocations may have per-byte provenance! #[inline] pub fn ptrs(&self) -> &SortedMap<Size, CtfeProvenance> { - debug_assert!(self.bytes.is_none()); // `CtfeProvenance::OFFSET_IS_ADDR` is false so this cannot fail + assert!(self.bytes.is_none(), "`ptrs()` called on non-interned allocation"); &self.ptrs } } @@ -88,12 +88,12 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { } /// `pm.range_ptrs_is_empty(r, cx)` == `pm.range_ptrs_get(r, cx).is_empty()`, but is faster. - pub(super) fn range_ptrs_is_empty(&self, range: AllocRange, cx: &impl HasDataLayout) -> bool { + fn range_ptrs_is_empty(&self, range: AllocRange, cx: &impl HasDataLayout) -> bool { self.ptrs.range_is_empty(Self::adjusted_range_ptrs(range, cx)) } /// Returns all byte-wise provenance in the given range. - fn range_bytes_get(&self, range: AllocRange) -> &[(Size, Prov)] { + fn range_bytes_get(&self, range: AllocRange) -> &[(Size, (Prov, u8))] { if let Some(bytes) = self.bytes.as_ref() { bytes.range(range.start..range.end()) } else { @@ -107,19 +107,47 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { } /// Get the provenance of a single byte. - pub fn get(&self, offset: Size, cx: &impl HasDataLayout) -> Option<Prov> { + pub fn get_byte(&self, offset: Size, cx: &impl HasDataLayout) -> Option<(Prov, u8)> { let prov = self.range_ptrs_get(alloc_range(offset, Size::from_bytes(1)), cx); debug_assert!(prov.len() <= 1); if let Some(entry) = prov.first() { // If it overlaps with this byte, it is on this byte. debug_assert!(self.bytes.as_ref().is_none_or(|b| !b.contains_key(&offset))); - Some(entry.1) + Some((entry.1, (offset - entry.0).bytes() as u8)) } else { // Look up per-byte provenance. self.bytes.as_ref().and_then(|b| b.get(&offset).copied()) } } + /// Attempt to merge per-byte provenance back into ptr chunks, if the right fragments + /// sit next to each other. Return `false` is that is not possible due to partial pointers. + pub fn merge_bytes(&mut self, cx: &impl HasDataLayout) -> bool { + let Some(bytes) = self.bytes.as_deref_mut() else { + return true; + }; + let ptr_size = cx.data_layout().pointer_size(); + while let Some((offset, (prov, _))) = bytes.iter().next().copied() { + // Check if this fragment starts a pointer. + let range = offset..offset + ptr_size; + let frags = bytes.range(range.clone()); + if frags.len() != ptr_size.bytes_usize() { + return false; + } + for (idx, (_offset, (frag_prov, frag_idx))) in frags.iter().copied().enumerate() { + if frag_prov != prov || frag_idx != idx as u8 { + return false; + } + } + // Looks like a pointer! Move it over to the ptr provenance map. + bytes.remove_range(range); + self.ptrs.insert(offset, prov); + } + // We managed to convert everything into whole pointers. + self.bytes = None; + true + } + /// Check if there is ptr-sized provenance at the given index. /// Does not mean anything for bytewise provenance! But can be useful as an optimization. pub fn get_ptr(&self, offset: Size) -> Option<Prov> { @@ -137,7 +165,7 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { /// Yields all the provenances stored in this map. pub fn provenances(&self) -> impl Iterator<Item = Prov> { - let bytes = self.bytes.iter().flat_map(|b| b.values()); + let bytes = self.bytes.iter().flat_map(|b| b.values().map(|(p, _i)| p)); self.ptrs.values().chain(bytes).copied() } @@ -148,16 +176,12 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { /// Removes all provenance inside the given range. /// If there is provenance overlapping with the edges, might result in an error. - pub fn clear(&mut self, range: AllocRange, cx: &impl HasDataLayout) -> AllocResult { + pub fn clear(&mut self, range: AllocRange, cx: &impl HasDataLayout) { let start = range.start; let end = range.end(); // Clear the bytewise part -- this is easy. - if Prov::OFFSET_IS_ADDR { - if let Some(bytes) = self.bytes.as_mut() { - bytes.remove_range(start..end); - } - } else { - debug_assert!(self.bytes.is_none()); + if let Some(bytes) = self.bytes.as_mut() { + bytes.remove_range(start..end); } let pointer_size = cx.data_layout().pointer_size(); @@ -168,7 +192,7 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { // Find all provenance overlapping the given range. if self.range_ptrs_is_empty(range, cx) { // No provenance in this range, we are done. This is the common case. - return Ok(()); + return; } // This redoes some of the work of `range_get_ptrs_is_empty`, but this path is much @@ -179,28 +203,20 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { // We need to handle clearing the provenance from parts of a pointer. if first < start { - if !Prov::OFFSET_IS_ADDR { - // We can't split up the provenance into less than a pointer. - return Err(AllocError::OverwritePartialPointer(first)); - } // Insert the remaining part in the bytewise provenance. let prov = self.ptrs[&first]; let bytes = self.bytes.get_or_insert_with(Box::default); for offset in first..start { - bytes.insert(offset, prov); + bytes.insert(offset, (prov, (offset - first).bytes() as u8)); } } if last > end { let begin_of_last = last - pointer_size; - if !Prov::OFFSET_IS_ADDR { - // We can't split up the provenance into less than a pointer. - return Err(AllocError::OverwritePartialPointer(begin_of_last)); - } // Insert the remaining part in the bytewise provenance. let prov = self.ptrs[&begin_of_last]; let bytes = self.bytes.get_or_insert_with(Box::default); for offset in end..last { - bytes.insert(offset, prov); + bytes.insert(offset, (prov, (offset - begin_of_last).bytes() as u8)); } } @@ -208,8 +224,6 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { // Since provenance do not overlap, we know that removing until `last` (exclusive) is fine, // i.e., this will not remove any other provenance just after the ones we care about. self.ptrs.remove_range(first..last); - - Ok(()) } /// Overwrites all provenance in the given range with wildcard provenance. @@ -218,10 +232,6 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { /// /// Provided for usage in Miri and panics otherwise. pub fn write_wildcards(&mut self, cx: &impl HasDataLayout, range: AllocRange) { - assert!( - Prov::OFFSET_IS_ADDR, - "writing wildcard provenance is not supported when `OFFSET_IS_ADDR` is false" - ); let wildcard = Prov::WILDCARD.unwrap(); let bytes = self.bytes.get_or_insert_with(Box::default); @@ -229,21 +239,22 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { // Remove pointer provenances that overlap with the range, then readd the edge ones bytewise. let ptr_range = Self::adjusted_range_ptrs(range, cx); let ptrs = self.ptrs.range(ptr_range.clone()); - if let Some((offset, prov)) = ptrs.first() { - for byte_ofs in *offset..range.start { - bytes.insert(byte_ofs, *prov); + if let Some((offset, prov)) = ptrs.first().copied() { + for byte_ofs in offset..range.start { + bytes.insert(byte_ofs, (prov, (byte_ofs - offset).bytes() as u8)); } } - if let Some((offset, prov)) = ptrs.last() { - for byte_ofs in range.end()..*offset + cx.data_layout().pointer_size() { - bytes.insert(byte_ofs, *prov); + if let Some((offset, prov)) = ptrs.last().copied() { + for byte_ofs in range.end()..offset + cx.data_layout().pointer_size() { + bytes.insert(byte_ofs, (prov, (byte_ofs - offset).bytes() as u8)); } } self.ptrs.remove_range(ptr_range); // Overwrite bytewise provenance. for offset in range.start..range.end() { - bytes.insert(offset, wildcard); + // The fragment index does not matter for wildcard provenance. + bytes.insert(offset, (wildcard, 0)); } } } @@ -253,7 +264,7 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { /// Offsets are already adjusted to the destination allocation. pub struct ProvenanceCopy<Prov> { dest_ptrs: Option<Box<[(Size, Prov)]>>, - dest_bytes: Option<Box<[(Size, Prov)]>>, + dest_bytes: Option<Box<[(Size, (Prov, u8))]>>, } impl<Prov: Provenance> ProvenanceMap<Prov> { @@ -263,7 +274,7 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { dest: Size, count: u64, cx: &impl HasDataLayout, - ) -> AllocResult<ProvenanceCopy<Prov>> { + ) -> ProvenanceCopy<Prov> { let shift_offset = move |idx, offset| { // compute offset for current repetition let dest_offset = dest + src.size * idx; // `Size` operations @@ -301,24 +312,16 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { let mut dest_bytes_box = None; let begin_overlap = self.range_ptrs_get(alloc_range(src.start, Size::ZERO), cx).first(); let end_overlap = self.range_ptrs_get(alloc_range(src.end(), Size::ZERO), cx).first(); - if !Prov::OFFSET_IS_ADDR { - // There can't be any bytewise provenance, and we cannot split up the begin/end overlap. - if let Some(entry) = begin_overlap { - return Err(AllocError::ReadPartialPointer(entry.0)); - } - if let Some(entry) = end_overlap { - return Err(AllocError::ReadPartialPointer(entry.0)); - } - debug_assert!(self.bytes.is_none()); - } else { - let mut bytes = Vec::new(); + // We only need to go here if there is some overlap or some bytewise provenance. + if begin_overlap.is_some() || end_overlap.is_some() || self.bytes.is_some() { + let mut bytes: Vec<(Size, (Prov, u8))> = Vec::new(); // First, if there is a part of a pointer at the start, add that. if let Some(entry) = begin_overlap { trace!("start overlapping entry: {entry:?}"); // For really small copies, make sure we don't run off the end of the `src` range. let entry_end = cmp::min(entry.0 + ptr_size, src.end()); for offset in src.start..entry_end { - bytes.push((offset, entry.1)); + bytes.push((offset, (entry.1, (offset - entry.0).bytes() as u8))); } } else { trace!("no start overlapping entry"); @@ -334,8 +337,9 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { let entry_start = cmp::max(entry.0, src.start); for offset in entry_start..src.end() { if bytes.last().is_none_or(|bytes_entry| bytes_entry.0 < offset) { - // The last entry, if it exists, has a lower offset than us. - bytes.push((offset, entry.1)); + // The last entry, if it exists, has a lower offset than us, so we + // can add it at the end and remain sorted. + bytes.push((offset, (entry.1, (offset - entry.0).bytes() as u8))); } else { // There already is an entry for this offset in there! This can happen when the // start and end range checks actually end up hitting the same pointer, so we @@ -358,7 +362,7 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { dest_bytes_box = Some(dest_bytes.into_boxed_slice()); } - Ok(ProvenanceCopy { dest_ptrs: dest_ptrs_box, dest_bytes: dest_bytes_box }) + ProvenanceCopy { dest_ptrs: dest_ptrs_box, dest_bytes: dest_bytes_box } } /// Applies a provenance copy. @@ -368,14 +372,10 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { if let Some(dest_ptrs) = copy.dest_ptrs { self.ptrs.insert_presorted(dest_ptrs.into()); } - if Prov::OFFSET_IS_ADDR { - if let Some(dest_bytes) = copy.dest_bytes - && !dest_bytes.is_empty() - { - self.bytes.get_or_insert_with(Box::default).insert_presorted(dest_bytes.into()); - } - } else { - debug_assert!(copy.dest_bytes.is_none()); + if let Some(dest_bytes) = copy.dest_bytes + && !dest_bytes.is_empty() + { + self.bytes.get_or_insert_with(Box::default).insert_presorted(dest_bytes.into()); } } } diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 77427a12d11..7c72a8ec243 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -582,9 +582,6 @@ pub enum UnsupportedOpInfo { // // The variants below are only reachable from CTFE/const prop, miri will never emit them. // - /// Overwriting parts of a pointer; without knowing absolute addresses, the resulting state - /// cannot be represented by the CTFE interpreter. - OverwritePartialPointer(Pointer<AllocId>), /// Attempting to read or copy parts of a pointer to somewhere else; without knowing absolute /// addresses, the resulting state cannot be represented by the CTFE interpreter. ReadPartialPointer(Pointer<AllocId>), diff --git a/compiler/rustc_middle/src/mir/interpret/pointer.rs b/compiler/rustc_middle/src/mir/interpret/pointer.rs index e25ff7651f6..c581b2ebf09 100644 --- a/compiler/rustc_middle/src/mir/interpret/pointer.rs +++ b/compiler/rustc_middle/src/mir/interpret/pointer.rs @@ -56,7 +56,7 @@ impl<T: HasDataLayout> PointerArithmetic for T {} /// mostly opaque; the `Machine` trait extends it with some more operations that also have access to /// some global state. /// The `Debug` rendering is used to display bare provenance, and for the default impl of `fmt`. -pub trait Provenance: Copy + fmt::Debug + 'static { +pub trait Provenance: Copy + PartialEq + fmt::Debug + 'static { /// Says whether the `offset` field of `Pointer`s with this provenance is the actual physical address. /// - If `false`, the offset *must* be relative. This means the bytes representing a pointer are /// different from what the Abstract Machine prescribes, so the interpreter must prevent any @@ -79,7 +79,7 @@ pub trait Provenance: Copy + fmt::Debug + 'static { fn get_alloc_id(self) -> Option<AllocId>; /// Defines the 'join' of provenance: what happens when doing a pointer load and different bytes have different provenance. - fn join(left: Option<Self>, right: Option<Self>) -> Option<Self>; + fn join(left: Self, right: Self) -> Option<Self>; } /// The type of provenance in the compile-time interpreter. @@ -192,8 +192,8 @@ impl Provenance for CtfeProvenance { Some(self.alloc_id()) } - fn join(_left: Option<Self>, _right: Option<Self>) -> Option<Self> { - panic!("merging provenance is not supported when `OFFSET_IS_ADDR` is false") + fn join(left: Self, right: Self) -> Option<Self> { + if left == right { Some(left) } else { None } } } @@ -224,8 +224,8 @@ impl Provenance for AllocId { Some(self) } - fn join(_left: Option<Self>, _right: Option<Self>) -> Option<Self> { - panic!("merging provenance is not supported when `OFFSET_IS_ADDR` is false") + fn join(_left: Self, _right: Self) -> Option<Self> { + unreachable!() } } diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 440771b3d68..0e6f797b1e4 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -2,7 +2,6 @@ use std::borrow::Cow; use std::fmt; use std::hash::Hash; -use rustc_ast::expand::autodiff_attrs::AutoDiffItem; use rustc_data_structures::base_n::{BaseNString, CASE_INSENSITIVE, ToBaseN}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxIndexMap; @@ -336,7 +335,6 @@ impl ToStableHashKey<StableHashingContext<'_>> for MonoItem<'_> { pub struct MonoItemPartitions<'tcx> { pub codegen_units: &'tcx [CodegenUnit<'tcx>], pub all_mono_items: &'tcx DefIdSet, - pub autodiff_items: &'tcx [AutoDiffItem], } #[derive(Debug, HashStable)] diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index d4d925d2057..f2d5b13cbc8 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1789,7 +1789,7 @@ pub fn write_allocation_bytes<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>( ascii.push('╼'); i += ptr_size; } - } else if let Some(prov) = alloc.provenance().get(i, &tcx) { + } else if let Some((prov, idx)) = alloc.provenance().get_byte(i, &tcx) { // Memory with provenance must be defined assert!( alloc.init_mask().is_range_initialized(alloc_range(i, Size::from_bytes(1))).is_ok() @@ -1799,7 +1799,7 @@ pub fn write_allocation_bytes<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>( // Format is similar to "oversized" above. let j = i.bytes_usize(); let c = alloc.inspect_with_uninit_and_ptr_outside_interpreter(j..j + 1)[0]; - write!(w, "╾{c:02x}{prov:#?} (1 ptr byte)╼")?; + write!(w, "╾{c:02x}{prov:#?} (ptr fragment {idx})╼")?; i += Size::from_bytes(1); } else if alloc .init_mask() diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 3dd6d2c8928..f1d19800a78 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -832,17 +832,17 @@ pub enum PatKind<'tcx> { }, /// One of the following: - /// * `&str` (represented as a valtree), which will be handled as a string pattern and thus + /// * `&str`, which will be handled as a string pattern and thus /// exhaustiveness checking will detect if you use the same string twice in different /// patterns. - /// * integer, bool, char or float (represented as a valtree), which will be handled by + /// * integer, bool, char or float, which will be handled by /// exhaustiveness to cover exactly its own value, similar to `&str`, but these values are /// much simpler. /// * raw pointers derived from integers, other raw pointers will have already resulted in an // error. /// * `String`, if `string_deref_patterns` is enabled. Constant { - value: mir::Const<'tcx>, + value: ty::Value<'tcx>, }, /// Pattern obtained by converting a constant (inline or named) to its pattern @@ -935,7 +935,7 @@ impl<'tcx> PatRange<'tcx> { let lo_is_min = match self.lo { PatRangeBoundary::NegInfinity => true, PatRangeBoundary::Finite(value) => { - let lo = value.try_to_bits(size).unwrap() ^ bias; + let lo = value.try_to_scalar_int().unwrap().to_bits(size) ^ bias; lo <= min } PatRangeBoundary::PosInfinity => false, @@ -944,7 +944,7 @@ impl<'tcx> PatRange<'tcx> { let hi_is_max = match self.hi { PatRangeBoundary::NegInfinity => false, PatRangeBoundary::Finite(value) => { - let hi = value.try_to_bits(size).unwrap() ^ bias; + let hi = value.try_to_scalar_int().unwrap().to_bits(size) ^ bias; hi > max || hi == max && self.end == RangeEnd::Included } PatRangeBoundary::PosInfinity => true, @@ -957,22 +957,17 @@ impl<'tcx> PatRange<'tcx> { } #[inline] - pub fn contains( - &self, - value: mir::Const<'tcx>, - tcx: TyCtxt<'tcx>, - typing_env: ty::TypingEnv<'tcx>, - ) -> Option<bool> { + pub fn contains(&self, value: ty::Value<'tcx>, tcx: TyCtxt<'tcx>) -> Option<bool> { use Ordering::*; - debug_assert_eq!(self.ty, value.ty()); + debug_assert_eq!(value.ty, self.ty); let ty = self.ty; - let value = PatRangeBoundary::Finite(value); + let value = PatRangeBoundary::Finite(value.valtree); // For performance, it's important to only do the second comparison if necessary. Some( - match self.lo.compare_with(value, ty, tcx, typing_env)? { + match self.lo.compare_with(value, ty, tcx)? { Less | Equal => true, Greater => false, - } && match value.compare_with(self.hi, ty, tcx, typing_env)? { + } && match value.compare_with(self.hi, ty, tcx)? { Less => true, Equal => self.end == RangeEnd::Included, Greater => false, @@ -981,21 +976,16 @@ impl<'tcx> PatRange<'tcx> { } #[inline] - pub fn overlaps( - &self, - other: &Self, - tcx: TyCtxt<'tcx>, - typing_env: ty::TypingEnv<'tcx>, - ) -> Option<bool> { + pub fn overlaps(&self, other: &Self, tcx: TyCtxt<'tcx>) -> Option<bool> { use Ordering::*; debug_assert_eq!(self.ty, other.ty); // For performance, it's important to only do the second comparison if necessary. Some( - match other.lo.compare_with(self.hi, self.ty, tcx, typing_env)? { + match other.lo.compare_with(self.hi, self.ty, tcx)? { Less => true, Equal => self.end == RangeEnd::Included, Greater => false, - } && match self.lo.compare_with(other.hi, self.ty, tcx, typing_env)? { + } && match self.lo.compare_with(other.hi, self.ty, tcx)? { Less => true, Equal => other.end == RangeEnd::Included, Greater => false, @@ -1006,11 +996,13 @@ impl<'tcx> PatRange<'tcx> { impl<'tcx> fmt::Display for PatRange<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let PatRangeBoundary::Finite(value) = &self.lo { + if let &PatRangeBoundary::Finite(valtree) = &self.lo { + let value = ty::Value { ty: self.ty, valtree }; write!(f, "{value}")?; } - if let PatRangeBoundary::Finite(value) = &self.hi { + if let &PatRangeBoundary::Finite(valtree) = &self.hi { write!(f, "{}", self.end)?; + let value = ty::Value { ty: self.ty, valtree }; write!(f, "{value}")?; } else { // `0..` is parsed as an inclusive range, we must display it correctly. @@ -1024,7 +1016,8 @@ impl<'tcx> fmt::Display for PatRange<'tcx> { /// If present, the const must be of a numeric type. #[derive(Copy, Clone, Debug, PartialEq, HashStable, TypeVisitable)] pub enum PatRangeBoundary<'tcx> { - Finite(mir::Const<'tcx>), + /// The type of this valtree is stored in the surrounding `PatRange`. + Finite(ty::ValTree<'tcx>), NegInfinity, PosInfinity, } @@ -1035,20 +1028,15 @@ impl<'tcx> PatRangeBoundary<'tcx> { matches!(self, Self::Finite(..)) } #[inline] - pub fn as_finite(self) -> Option<mir::Const<'tcx>> { + pub fn as_finite(self) -> Option<ty::ValTree<'tcx>> { match self { Self::Finite(value) => Some(value), Self::NegInfinity | Self::PosInfinity => None, } } - pub fn eval_bits( - self, - ty: Ty<'tcx>, - tcx: TyCtxt<'tcx>, - typing_env: ty::TypingEnv<'tcx>, - ) -> u128 { + pub fn to_bits(self, ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> u128 { match self { - Self::Finite(value) => value.eval_bits(tcx, typing_env), + Self::Finite(value) => value.try_to_scalar_int().unwrap().to_bits_unchecked(), Self::NegInfinity => { // Unwrap is ok because the type is known to be numeric. ty.numeric_min_and_max_as_bits(tcx).unwrap().0 @@ -1060,14 +1048,8 @@ impl<'tcx> PatRangeBoundary<'tcx> { } } - #[instrument(skip(tcx, typing_env), level = "debug", ret)] - pub fn compare_with( - self, - other: Self, - ty: Ty<'tcx>, - tcx: TyCtxt<'tcx>, - typing_env: ty::TypingEnv<'tcx>, - ) -> Option<Ordering> { + #[instrument(skip(tcx), level = "debug", ret)] + pub fn compare_with(self, other: Self, ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Option<Ordering> { use PatRangeBoundary::*; match (self, other) { // When comparing with infinities, we must remember that `0u8..` and `0u8..=255` @@ -1095,8 +1077,8 @@ impl<'tcx> PatRangeBoundary<'tcx> { _ => {} } - let a = self.eval_bits(ty, tcx, typing_env); - let b = other.eval_bits(ty, tcx, typing_env); + let a = self.to_bits(ty, tcx); + let b = other.to_bits(ty, tcx); match ty.kind() { ty::Float(ty::FloatTy::F16) => { diff --git a/compiler/rustc_middle/src/ty/consts/valtree.rs b/compiler/rustc_middle/src/ty/consts/valtree.rs index d95006dcf4a..a14e47d7082 100644 --- a/compiler/rustc_middle/src/ty/consts/valtree.rs +++ b/compiler/rustc_middle/src/ty/consts/valtree.rs @@ -2,10 +2,12 @@ use std::fmt; use std::ops::Deref; use rustc_data_structures::intern::Interned; +use rustc_hir::def::Namespace; use rustc_macros::{HashStable, Lift, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use super::ScalarInt; use crate::mir::interpret::{ErrorHandled, Scalar}; +use crate::ty::print::{FmtPrinter, PrettyPrinter}; use crate::ty::{self, Ty, TyCtxt}; /// This datastructure is used to represent the value of constants used in the type system. @@ -133,6 +135,8 @@ pub type ConstToValTreeResult<'tcx> = Result<Result<ValTree<'tcx>, Ty<'tcx>>, Er /// A type-level constant value. /// /// Represents a typed, fully evaluated constant. +/// Note that this is also used by pattern elaboration to represent values which cannot occur in types, +/// such as raw pointers and floats. #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] #[derive(HashStable, TyEncodable, TyDecodable, TypeFoldable, TypeVisitable, Lift)] pub struct Value<'tcx> { @@ -203,3 +207,14 @@ impl<'tcx> rustc_type_ir::inherent::ValueConst<TyCtxt<'tcx>> for Value<'tcx> { self.valtree } } + +impl<'tcx> fmt::Display for Value<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ty::tls::with(move |tcx| { + let cv = tcx.lift(*self).unwrap(); + let mut p = FmtPrinter::new(tcx, Namespace::ValueNS); + p.pretty_print_const_valtree(cv, /*print_ty*/ true)?; + f.write_str(&p.into_buffer()) + }) + } +} diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 0a976f3a0ac..74caee7336a 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -337,10 +337,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { false } - /// Returns `true` if the region should be printed in - /// optional positions, e.g., `&'a T` or `dyn Tr + 'b`. - /// This is typically the case for all non-`'_` regions. - fn should_print_region(&self, region: ty::Region<'tcx>) -> bool; + /// Returns `true` if the region should be printed in optional positions, + /// e.g., `&'a T` or `dyn Tr + 'b`. (Regions like the one in `Cow<'static, T>` + /// will always be printed.) + fn should_print_optional_region(&self, region: ty::Region<'tcx>) -> bool; fn reset_type_limit(&mut self) {} @@ -717,7 +717,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } ty::Ref(r, ty, mutbl) => { write!(self, "&")?; - if self.should_print_region(r) { + if self.should_print_optional_region(r) { r.print(self)?; write!(self, " ")?; } @@ -785,7 +785,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { }, ty::Adt(def, args) => self.print_def_path(def.did(), args)?, ty::Dynamic(data, r, repr) => { - let print_r = self.should_print_region(r); + let print_r = self.should_print_optional_region(r); if print_r { write!(self, "(")?; } @@ -2494,7 +2494,7 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> { !self.type_length_limit.value_within_limit(self.printed_type_count) } - fn should_print_region(&self, region: ty::Region<'tcx>) -> bool { + fn should_print_optional_region(&self, region: ty::Region<'tcx>) -> bool { let highlight = self.region_highlight_mode; if highlight.region_highlighted(region).is_some() { return true; diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 0e2aff6f9bd..89ef46b1ae5 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -12,7 +12,6 @@ use rustc_hir::def_id::LocalDefId; use rustc_span::source_map::Spanned; use rustc_type_ir::{ConstKind, TypeFolder, VisitorResult, try_visit}; -use super::print::PrettyPrinter; use super::{GenericArg, GenericArgKind, Pattern, Region}; use crate::mir::PlaceElem; use crate::ty::print::{FmtPrinter, Printer, with_no_trimmed_paths}; @@ -168,15 +167,11 @@ impl<'tcx> fmt::Debug for ty::Const<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // If this is a value, we spend some effort to make it look nice. if let ConstKind::Value(cv) = self.kind() { - return ty::tls::with(move |tcx| { - let cv = tcx.lift(cv).unwrap(); - let mut p = FmtPrinter::new(tcx, Namespace::ValueNS); - p.pretty_print_const_valtree(cv, /*print_ty*/ true)?; - f.write_str(&p.into_buffer()) - }); + write!(f, "{}", cv) + } else { + // Fall back to something verbose. + write!(f, "{:?}", self.kind()) } - // Fall back to something verbose. - write!(f, "{:?}", self.kind()) } } diff --git a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs index 9825b947fe0..41d3aefcbe6 100644 --- a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs +++ b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs @@ -160,7 +160,7 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { }); } }; - values.push(value.eval_bits(self.tcx, self.typing_env)); + values.push(value.valtree.unwrap_leaf().to_bits_unchecked()); targets.push(self.parse_block(arm.body)?); } diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 94ae5dadd8a..6ee9674bb08 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -16,7 +16,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::{BindingMode, ByRef, LetStmt, LocalSource, Node}; use rustc_middle::bug; use rustc_middle::middle::region; -use rustc_middle::mir::{self, *}; +use rustc_middle::mir::*; use rustc_middle::thir::{self, *}; use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, ValTree, ValTreeKind}; use rustc_pattern_analysis::constructor::RangeEnd; @@ -1245,7 +1245,7 @@ struct Ascription<'tcx> { #[derive(Debug, Clone)] enum TestCase<'tcx> { Variant { adt_def: ty::AdtDef<'tcx>, variant_index: VariantIdx }, - Constant { value: mir::Const<'tcx> }, + Constant { value: ty::Value<'tcx> }, Range(Arc<PatRange<'tcx>>), Slice { len: usize, variable_length: bool }, Deref { temp: Place<'tcx>, mutability: Mutability }, @@ -1316,13 +1316,13 @@ enum TestKind<'tcx> { If, /// Test for equality with value, possibly after an unsizing coercion to - /// `ty`, + /// `cast_ty`, Eq { - value: Const<'tcx>, + value: ty::Value<'tcx>, // Integer types are handled by `SwitchInt`, and constants with ADT // types and `&[T]` types are converted back into patterns, so this can - // only be `&str`, `f32` or `f64`. - ty: Ty<'tcx>, + // only be `&str` or floats. + cast_ty: Ty<'tcx>, }, /// Test whether the value falls within an inclusive or exclusive range. @@ -1357,8 +1357,8 @@ pub(crate) struct Test<'tcx> { enum TestBranch<'tcx> { /// Success branch, used for tests with two possible outcomes. Success, - /// Branch corresponding to this constant. - Constant(Const<'tcx>, u128), + /// Branch corresponding to this constant. Must be a scalar. + Constant(ty::Value<'tcx>), /// Branch corresponding to this variant. Variant(VariantIdx), /// Failure branch for tests with two possible outcomes, and "otherwise" branch for other tests. @@ -1366,8 +1366,8 @@ enum TestBranch<'tcx> { } impl<'tcx> TestBranch<'tcx> { - fn as_constant(&self) -> Option<&Const<'tcx>> { - if let Self::Constant(v, _) = self { Some(v) } else { None } + fn as_constant(&self) -> Option<ty::Value<'tcx>> { + if let Self::Constant(v) = self { Some(*v) } else { None } } } diff --git a/compiler/rustc_mir_build/src/builder/matches/test.rs b/compiler/rustc_mir_build/src/builder/matches/test.rs index a4609a6053e..d03794fe2d5 100644 --- a/compiler/rustc_mir_build/src/builder/matches/test.rs +++ b/compiler/rustc_mir_build/src/builder/matches/test.rs @@ -35,7 +35,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { TestCase::Constant { .. } if match_pair.pattern_ty.is_bool() => TestKind::If, TestCase::Constant { .. } if is_switch_ty(match_pair.pattern_ty) => TestKind::SwitchInt, - TestCase::Constant { value } => TestKind::Eq { value, ty: match_pair.pattern_ty }, + TestCase::Constant { value } => TestKind::Eq { value, cast_ty: match_pair.pattern_ty }, TestCase::Range(ref range) => { assert_eq!(range.ty, match_pair.pattern_ty); @@ -112,7 +112,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let otherwise_block = target_block(TestBranch::Failure); let switch_targets = SwitchTargets::new( target_blocks.iter().filter_map(|(&branch, &block)| { - if let TestBranch::Constant(_, bits) = branch { + if let TestBranch::Constant(value) = branch { + let bits = value.valtree.unwrap_leaf().to_bits_unchecked(); Some((bits, block)) } else { None @@ -135,17 +136,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.cfg.terminate(block, self.source_info(match_start_span), terminator); } - TestKind::Eq { value, mut ty } => { + TestKind::Eq { value, mut cast_ty } => { let tcx = self.tcx; let success_block = target_block(TestBranch::Success); let fail_block = target_block(TestBranch::Failure); - let mut expect_ty = value.ty(); - let mut expect = self.literal_operand(test.span, value); + let mut expect_ty = value.ty; + let mut expect = self.literal_operand(test.span, Const::from_ty_value(tcx, value)); let mut place = place; let mut block = block; - match ty.kind() { + match cast_ty.kind() { ty::Str => { // String literal patterns may have type `str` if `deref_patterns` is // enabled, in order to allow `deref!("..."): String`. In this case, `value` @@ -167,7 +168,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { Rvalue::Ref(re_erased, BorrowKind::Shared, place), ); place = ref_place; - ty = ref_str_ty; + cast_ty = ref_str_ty; } ty::Adt(def, _) if tcx.is_lang_item(def.did(), LangItem::String) => { if !tcx.features().string_deref_patterns() { @@ -186,7 +187,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { eq_block, place, Mutability::Not, - ty, + cast_ty, ref_str, test.span, ); @@ -195,10 +196,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Similarly, the normal test code should be generated for the `&str`, instead of the `String`. block = eq_block; place = ref_str; - ty = ref_str_ty; + cast_ty = ref_str_ty; } &ty::Pat(base, _) => { - assert_eq!(ty, value.ty()); + assert_eq!(cast_ty, value.ty); assert!(base.is_trivially_pure_clone_copy()); let transmuted_place = self.temp(base, test.span); @@ -219,14 +220,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { place = transmuted_place; expect = Operand::Copy(transmuted_expect); - ty = base; + cast_ty = base; expect_ty = base; } _ => {} } - assert_eq!(expect_ty, ty); - if !ty.is_scalar() { + assert_eq!(expect_ty, cast_ty); + if !cast_ty.is_scalar() { // Use `PartialEq::eq` instead of `BinOp::Eq` // (the binop can only handle primitives) // Make sure that we do *not* call any user-defined code here. @@ -234,10 +235,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // comparison defined in `core`. // (Interestingly this means that exhaustiveness analysis relies, for soundness, // on the `PartialEq` impl for `str` to b correct!) - match *ty.kind() { + match *cast_ty.kind() { ty::Ref(_, deref_ty, _) if deref_ty == self.tcx.types.str_ => {} _ => { - span_bug!(source_info.span, "invalid type for non-scalar compare: {ty}") + span_bug!( + source_info.span, + "invalid type for non-scalar compare: {cast_ty}" + ) } }; self.string_compare( @@ -276,7 +280,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }; if let Some(lo) = range.lo.as_finite() { - let lo = self.literal_operand(test.span, lo); + let lo = ty::Value { ty: range.ty, valtree: lo }; + let lo = self.literal_operand(test.span, Const::from_ty_value(self.tcx, lo)); self.compare( block, intermediate_block, @@ -289,7 +294,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }; if let Some(hi) = range.hi.as_finite() { - let hi = self.literal_operand(test.span, hi); + let hi = ty::Value { ty: range.ty, valtree: hi }; + let hi = self.literal_operand(test.span, Const::from_ty_value(self.tcx, hi)); let op = match range.end { RangeEnd::Included => BinOp::Le, RangeEnd::Excluded => BinOp::Lt, @@ -555,10 +561,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // not to add such values here. let is_covering_range = |test_case: &TestCase<'tcx>| { test_case.as_range().is_some_and(|range| { - matches!( - range.contains(value, self.tcx, self.typing_env()), - None | Some(true) - ) + matches!(range.contains(value, self.tcx), None | Some(true)) }) }; let is_conflicting_candidate = |candidate: &&mut Candidate<'tcx>| { @@ -575,8 +578,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { None } else { fully_matched = true; - let bits = value.eval_bits(self.tcx, self.typing_env()); - Some(TestBranch::Constant(value, bits)) + Some(TestBranch::Constant(value)) } } (TestKind::SwitchInt, TestCase::Range(range)) => { @@ -585,12 +587,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // the values being tested. (This restricts what values can be // added to the test by subsequent candidates.) fully_matched = false; - let not_contained = - sorted_candidates.keys().filter_map(|br| br.as_constant()).copied().all( - |val| { - matches!(range.contains(val, self.tcx, self.typing_env()), Some(false)) - }, - ); + let not_contained = sorted_candidates + .keys() + .filter_map(|br| br.as_constant()) + .all(|val| matches!(range.contains(val, self.tcx), Some(false))); not_contained.then(|| { // No switch values are contained in the pattern range, @@ -601,7 +601,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { (TestKind::If, TestCase::Constant { value }) => { fully_matched = true; - let value = value.try_eval_bool(self.tcx, self.typing_env()).unwrap_or_else(|| { + let value = value.try_to_bool().unwrap_or_else(|| { span_bug!(test.span, "expected boolean value but got {value:?}") }); Some(if value { TestBranch::Success } else { TestBranch::Failure }) @@ -681,16 +681,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fully_matched = false; // If the testing range does not overlap with pattern range, // the pattern can be matched only if this test fails. - if !test.overlaps(pat, self.tcx, self.typing_env())? { - Some(TestBranch::Failure) - } else { - None - } + if !test.overlaps(pat, self.tcx)? { Some(TestBranch::Failure) } else { None } } } (TestKind::Range(range), &TestCase::Constant { value }) => { fully_matched = false; - if !range.contains(value, self.tcx, self.typing_env())? { + if !range.contains(value, self.tcx)? { // `value` is not contained in the testing range, // so `value` can be matched only if this test fails. Some(TestBranch::Failure) diff --git a/compiler/rustc_mir_build/src/check_tail_calls.rs b/compiler/rustc_mir_build/src/check_tail_calls.rs index 3ecccb422c4..d4b6da2c14b 100644 --- a/compiler/rustc_mir_build/src/check_tail_calls.rs +++ b/compiler/rustc_mir_build/src/check_tail_calls.rs @@ -3,6 +3,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::Applicability; use rustc_hir::LangItem; use rustc_hir::def::DefKind; +use rustc_hir::def_id::CRATE_DEF_ID; use rustc_middle::span_bug; use rustc_middle::thir::visit::{self, Visitor}; use rustc_middle::thir::{BodyTy, Expr, ExprId, ExprKind, Thir}; @@ -136,7 +137,15 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> { if caller_sig.inputs_and_output != callee_sig.inputs_and_output { if caller_sig.inputs() != callee_sig.inputs() { - self.report_arguments_mismatch(expr.span, caller_sig, callee_sig); + self.report_arguments_mismatch( + expr.span, + self.tcx.liberate_late_bound_regions( + CRATE_DEF_ID.to_def_id(), + self.caller_ty.fn_sig(self.tcx), + ), + self.tcx + .liberate_late_bound_regions(CRATE_DEF_ID.to_def_id(), ty.fn_sig(self.tcx)), + ); } // FIXME(explicit_tail_calls): this currently fails for cases where opaques are used. diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index a501cdf88c2..d46c4678bcf 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -11,11 +11,11 @@ use rustc_index::Idx; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::Obligation; use rustc_middle::mir::interpret::ErrorHandled; +use rustc_middle::span_bug; use rustc_middle::thir::{FieldPat, Pat, PatKind}; use rustc_middle::ty::{ self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitableExt, TypeVisitor, ValTree, }; -use rustc_middle::{mir, span_bug}; use rustc_span::def_id::DefId; use rustc_span::{DUMMY_SP, Span}; use rustc_trait_selection::traits::ObligationCause; @@ -288,16 +288,12 @@ impl<'tcx> ConstToPat<'tcx> { // when lowering to MIR in `Builder::perform_test`, treat the constant as a `&str`. // This works because `str` and `&str` have the same valtree representation. let ref_str_ty = Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, ty); - PatKind::Constant { - value: mir::Const::Ty(ref_str_ty, ty::Const::new_value(tcx, cv, ref_str_ty)), - } + PatKind::Constant { value: ty::Value { ty: ref_str_ty, valtree: cv } } } ty::Ref(_, pointee_ty, ..) => match *pointee_ty.kind() { // `&str` is represented as a valtree, let's keep using this // optimization for now. - ty::Str => PatKind::Constant { - value: mir::Const::Ty(ty, ty::Const::new_value(tcx, cv, ty)), - }, + ty::Str => PatKind::Constant { value: ty::Value { ty, valtree: cv } }, // All other references are converted into deref patterns and then recursively // convert the dereferenced constant to a pattern that is the sub-pattern of the // deref pattern. @@ -326,15 +322,13 @@ impl<'tcx> ConstToPat<'tcx> { // Also see <https://github.com/rust-lang/rfcs/pull/3535>. return self.mk_err(tcx.dcx().create_err(NaNPattern { span }), ty); } else { - PatKind::Constant { - value: mir::Const::Ty(ty, ty::Const::new_value(tcx, cv, ty)), - } + PatKind::Constant { value: ty::Value { ty, valtree: cv } } } } ty::Pat(..) | ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::RawPtr(..) => { // The raw pointers we see here have been "vetted" by valtree construction to be // just integers, so we simply allow them. - PatKind::Constant { value: mir::Const::Ty(ty, ty::Const::new_value(tcx, cv, ty)) } + PatKind::Constant { value: ty::Value { ty, valtree: cv } } } ty::FnPtr(..) => { unreachable!( diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index a44afed5492..166e64a5fcb 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -161,8 +161,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { format!("found bad range pattern endpoint `{expr:?}` outside of error recovery"); return Err(self.tcx.dcx().span_delayed_bug(expr.span, msg)); }; - - Ok(Some(PatRangeBoundary::Finite(value))) + Ok(Some(PatRangeBoundary::Finite(value.valtree))) } /// Overflowing literals are linted against in a late pass. This is mostly fine, except when we @@ -235,7 +234,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let lo = lower_endpoint(lo_expr)?.unwrap_or(PatRangeBoundary::NegInfinity); let hi = lower_endpoint(hi_expr)?.unwrap_or(PatRangeBoundary::PosInfinity); - let cmp = lo.compare_with(hi, ty, self.tcx, self.typing_env); + let cmp = lo.compare_with(hi, ty, self.tcx); let mut kind = PatKind::Range(Arc::new(PatRange { lo, hi, end, ty })); match (end, cmp) { // `x..y` where `x < y`. @@ -244,7 +243,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { (RangeEnd::Included, Some(Ordering::Less)) => {} // `x..=y` where `x == y` and `x` and `y` are finite. (RangeEnd::Included, Some(Ordering::Equal)) if lo.is_finite() && hi.is_finite() => { - kind = PatKind::Constant { value: lo.as_finite().unwrap() }; + let value = ty::Value { ty, valtree: lo.as_finite().unwrap() }; + kind = PatKind::Constant { value }; } // `..=x` where `x == ty::MIN`. (RangeEnd::Included, Some(Ordering::Equal)) if !lo.is_finite() => {} diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index 5efc4be2de2..b7e8d6fea40 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -763,7 +763,7 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { } PatKind::Constant { value } => { print_indented!(self, "Constant {", depth_lvl + 1); - print_indented!(self, format!("value: {:?}", value), depth_lvl + 2); + print_indented!(self, format!("value: {}", value), depth_lvl + 2); print_indented!(self, "}", depth_lvl + 1); } PatKind::ExpandedConstant { def_id, subpattern } => { diff --git a/compiler/rustc_mir_transform/src/coverage/mappings.rs b/compiler/rustc_mir_transform/src/coverage/mappings.rs index 46fe7c40826..8dbe564f517 100644 --- a/compiler/rustc_mir_transform/src/coverage/mappings.rs +++ b/compiler/rustc_mir_transform/src/coverage/mappings.rs @@ -1,51 +1,36 @@ use rustc_index::IndexVec; -use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, CoverageInfoHi, CoverageKind}; +use rustc_middle::mir::coverage::{ + BlockMarkerId, BranchSpan, CoverageInfoHi, CoverageKind, Mapping, MappingKind, +}; use rustc_middle::mir::{self, BasicBlock, StatementKind}; use rustc_middle::ty::TyCtxt; -use rustc_span::Span; -use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph}; +use crate::coverage::graph::CoverageGraph; use crate::coverage::hir_info::ExtractedHirInfo; use crate::coverage::spans::extract_refined_covspans; use crate::coverage::unexpand::unexpand_into_body_span; -/// Associates an ordinary executable code span with its corresponding BCB. -#[derive(Debug)] -pub(super) struct CodeMapping { - pub(super) span: Span, - pub(super) bcb: BasicCoverageBlock, -} - -#[derive(Debug)] -pub(super) struct BranchPair { - pub(super) span: Span, - pub(super) true_bcb: BasicCoverageBlock, - pub(super) false_bcb: BasicCoverageBlock, -} - #[derive(Default)] -pub(super) struct ExtractedMappings { - pub(super) code_mappings: Vec<CodeMapping>, - pub(super) branch_pairs: Vec<BranchPair>, +pub(crate) struct ExtractedMappings { + pub(crate) mappings: Vec<Mapping>, } -/// Extracts coverage-relevant spans from MIR, and associates them with -/// their corresponding BCBs. -pub(super) fn extract_all_mapping_info_from_mir<'tcx>( +/// Extracts coverage-relevant spans from MIR, and uses them to create +/// coverage mapping data for inclusion in MIR. +pub(crate) fn extract_mappings_from_mir<'tcx>( tcx: TyCtxt<'tcx>, mir_body: &mir::Body<'tcx>, hir_info: &ExtractedHirInfo, graph: &CoverageGraph, ) -> ExtractedMappings { - let mut code_mappings = vec![]; - let mut branch_pairs = vec![]; + let mut mappings = vec![]; // Extract ordinary code mappings from MIR statement/terminator spans. - extract_refined_covspans(tcx, mir_body, hir_info, graph, &mut code_mappings); + extract_refined_covspans(tcx, mir_body, hir_info, graph, &mut mappings); - branch_pairs.extend(extract_branch_pairs(mir_body, hir_info, graph)); + extract_branch_mappings(mir_body, hir_info, graph, &mut mappings); - ExtractedMappings { code_mappings, branch_pairs } + ExtractedMappings { mappings } } fn resolve_block_markers( @@ -69,19 +54,18 @@ fn resolve_block_markers( block_markers } -pub(super) fn extract_branch_pairs( +pub(super) fn extract_branch_mappings( mir_body: &mir::Body<'_>, hir_info: &ExtractedHirInfo, graph: &CoverageGraph, -) -> Vec<BranchPair> { - let Some(coverage_info_hi) = mir_body.coverage_info_hi.as_deref() else { return vec![] }; + mappings: &mut Vec<Mapping>, +) { + let Some(coverage_info_hi) = mir_body.coverage_info_hi.as_deref() else { return }; let block_markers = resolve_block_markers(coverage_info_hi, mir_body); - coverage_info_hi - .branch_spans - .iter() - .filter_map(|&BranchSpan { span: raw_span, true_marker, false_marker }| { + mappings.extend(coverage_info_hi.branch_spans.iter().filter_map( + |&BranchSpan { span: raw_span, true_marker, false_marker }| try { // For now, ignore any branch span that was introduced by // expansion. This makes things like assert macros less noisy. if !raw_span.ctxt().outer_expn_data().is_root() { @@ -94,7 +78,7 @@ pub(super) fn extract_branch_pairs( let true_bcb = bcb_from_marker(true_marker)?; let false_bcb = bcb_from_marker(false_marker)?; - Some(BranchPair { span, true_bcb, false_bcb }) - }) - .collect::<Vec<_>>() + Mapping { span, kind: MappingKind::Branch { true_bcb, false_bcb } } + }, + )); } diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index 47a87a2e94d..c5fef299244 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -1,4 +1,4 @@ -use rustc_middle::mir::coverage::{CoverageKind, FunctionCoverageInfo, Mapping, MappingKind}; +use rustc_middle::mir::coverage::{CoverageKind, FunctionCoverageInfo}; use rustc_middle::mir::{self, BasicBlock, Statement, StatementKind, TerminatorKind}; use rustc_middle::ty::TyCtxt; use tracing::{debug, debug_span, trace}; @@ -71,10 +71,8 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir: //////////////////////////////////////////////////// // Extract coverage spans and other mapping info from MIR. - let extracted_mappings = - mappings::extract_all_mapping_info_from_mir(tcx, mir_body, &hir_info, &graph); - - let mappings = create_mappings(&extracted_mappings); + let ExtractedMappings { mappings } = + mappings::extract_mappings_from_mir(tcx, mir_body, &hir_info, &graph); if mappings.is_empty() { // No spans could be converted into valid mappings, so skip this function. debug!("no spans could be converted into valid mappings; skipping"); @@ -100,34 +98,6 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir: })); } -/// For each coverage span extracted from MIR, create a corresponding mapping. -/// -/// FIXME(Zalathar): This used to be where BCBs in the extracted mappings were -/// resolved to a `CovTerm`. But that is now handled elsewhere, so this -/// function can potentially be simplified even further. -fn create_mappings(extracted_mappings: &ExtractedMappings) -> Vec<Mapping> { - // Fully destructure the mappings struct to make sure we don't miss any kinds. - let ExtractedMappings { code_mappings, branch_pairs } = extracted_mappings; - let mut mappings = Vec::new(); - - mappings.extend(code_mappings.iter().map( - // Ordinary code mappings are the simplest kind. - |&mappings::CodeMapping { span, bcb }| { - let kind = MappingKind::Code { bcb }; - Mapping { kind, span } - }, - )); - - mappings.extend(branch_pairs.iter().map( - |&mappings::BranchPair { span, true_bcb, false_bcb }| { - let kind = MappingKind::Branch { true_bcb, false_bcb }; - Mapping { kind, span } - }, - )); - - mappings -} - /// Inject any necessary coverage statements into MIR, so that they influence codegen. fn inject_coverage_statements<'tcx>(mir_body: &mut mir::Body<'tcx>, graph: &CoverageGraph) { for (bcb, data) in graph.iter_enumerated() { diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index ae9459dee84..d1b04c8f587 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -1,6 +1,6 @@ use rustc_data_structures::fx::FxHashSet; use rustc_middle::mir; -use rustc_middle::mir::coverage::START_BCB; +use rustc_middle::mir::coverage::{Mapping, MappingKind, START_BCB}; use rustc_middle::ty::TyCtxt; use rustc_span::source_map::SourceMap; use rustc_span::{BytePos, DesugaringKind, ExpnKind, MacroKind, Span}; @@ -9,7 +9,7 @@ use tracing::instrument; use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph}; use crate::coverage::hir_info::ExtractedHirInfo; use crate::coverage::spans::from_mir::{Hole, RawSpanFromMir, SpanFromMir}; -use crate::coverage::{mappings, unexpand}; +use crate::coverage::unexpand; mod from_mir; @@ -18,7 +18,7 @@ pub(super) fn extract_refined_covspans<'tcx>( mir_body: &mir::Body<'tcx>, hir_info: &ExtractedHirInfo, graph: &CoverageGraph, - code_mappings: &mut Vec<mappings::CodeMapping>, + mappings: &mut Vec<Mapping>, ) { if hir_info.is_async_fn { // An async function desugars into a function that returns a future, @@ -26,7 +26,7 @@ pub(super) fn extract_refined_covspans<'tcx>( // outer function will be unhelpful, so just keep the signature span // and ignore all of the spans in the MIR body. if let Some(span) = hir_info.fn_sig_span { - code_mappings.push(mappings::CodeMapping { span, bcb: START_BCB }); + mappings.push(Mapping { span, kind: MappingKind::Code { bcb: START_BCB } }) } return; } @@ -111,9 +111,9 @@ pub(super) fn extract_refined_covspans<'tcx>( // Merge covspans that can be merged. covspans.dedup_by(|b, a| a.merge_if_eligible(b)); - code_mappings.extend(covspans.into_iter().map(|Covspan { span, bcb }| { + mappings.extend(covspans.into_iter().map(|Covspan { span, bcb }| { // Each span produced by the refiner represents an ordinary code region. - mappings::CodeMapping { span, bcb } + Mapping { span, kind: MappingKind::Code { bcb } } })); } diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs index 7a8d3ba1ff1..5b3ddcc777b 100644 --- a/compiler/rustc_mir_transform/src/pass_manager.rs +++ b/compiler/rustc_mir_transform/src/pass_manager.rs @@ -41,19 +41,40 @@ fn to_profiler_name(type_name: &'static str) -> &'static str { }) } -// const wrapper for `if let Some((_, tail)) = name.rsplit_once(':') { tail } else { name }` -const fn c_name(name: &'static str) -> &'static str { +// A function that simplifies a pass's type_name. E.g. `Baz`, `Baz<'_>`, +// `foo::bar::Baz`, and `foo::bar::Baz<'a, 'b>` all become `Baz`. +// +// It's `const` for perf reasons: it's called a lot, and doing the string +// operations at runtime causes a non-trivial slowdown. If +// `split_once`/`rsplit_once` become `const` its body could be simplified to +// this: +// ```ignore (fragment) +// let name = if let Some((_, tail)) = name.rsplit_once(':') { tail } else { name }; +// let name = if let Some((head, _)) = name.split_once('<') { head } else { name }; +// name +// ``` +const fn simplify_pass_type_name(name: &'static str) -> &'static str { // FIXME(const-hack) Simplify the implementation once more `str` methods get const-stable. - // and inline into call site + + // Work backwards from the end. If a ':' is hit, strip it and everything before it. let bytes = name.as_bytes(); let mut i = bytes.len(); while i > 0 && bytes[i - 1] != b':' { - i = i - 1; + i -= 1; } let (_, bytes) = bytes.split_at(i); + + // Work forwards from the start of what's left. If a '<' is hit, strip it and everything after + // it. + let mut i = 0; + while i < bytes.len() && bytes[i] != b'<' { + i += 1; + } + let (bytes, _) = bytes.split_at(i); + match std::str::from_utf8(bytes) { Ok(name) => name, - Err(_) => name, + Err(_) => panic!(), } } @@ -62,12 +83,7 @@ const fn c_name(name: &'static str) -> &'static str { /// loop that goes over each available MIR and applies `run_pass`. pub(super) trait MirPass<'tcx> { fn name(&self) -> &'static str { - // FIXME(const-hack) Simplify the implementation once more `str` methods get const-stable. - // See copypaste in `MirLint` - const { - let name = std::any::type_name::<Self>(); - c_name(name) - } + const { simplify_pass_type_name(std::any::type_name::<Self>()) } } fn profiler_name(&self) -> &'static str { @@ -101,12 +117,7 @@ pub(super) trait MirPass<'tcx> { /// disabled (via the `Lint` adapter). pub(super) trait MirLint<'tcx> { fn name(&self) -> &'static str { - // FIXME(const-hack) Simplify the implementation once more `str` methods get const-stable. - // See copypaste in `MirPass` - const { - let name = std::any::type_name::<Self>(); - c_name(name) - } + const { simplify_pass_type_name(std::any::type_name::<Self>()) } } fn is_enabled(&self, _sess: &Session) -> bool { diff --git a/compiler/rustc_monomorphize/Cargo.toml b/compiler/rustc_monomorphize/Cargo.toml index 0ed5b4fc0d0..09a55f0b5f8 100644 --- a/compiler/rustc_monomorphize/Cargo.toml +++ b/compiler/rustc_monomorphize/Cargo.toml @@ -6,7 +6,6 @@ edition = "2024" [dependencies] # tidy-alphabetical-start rustc_abi = { path = "../rustc_abi" } -rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } @@ -15,7 +14,6 @@ rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } -rustc_symbol_mangling = { path = "../rustc_symbol_mangling" } rustc_target = { path = "../rustc_target" } serde = "1" serde_json = "1" diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 26ca8518434..6a836442c32 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -205,6 +205,8 @@ //! this is not implemented however: a mono item will be produced //! regardless of whether it is actually needed or not. +mod autodiff; + use std::cell::OnceCell; use rustc_data_structures::fx::FxIndexMap; @@ -235,6 +237,7 @@ use rustc_span::source_map::{Spanned, dummy_spanned, respan}; use rustc_span::{DUMMY_SP, Span}; use tracing::{debug, instrument, trace}; +use crate::collector::autodiff::collect_autodiff_fn; use crate::errors::{ self, EncounteredErrorWhileInstantiating, EncounteredErrorWhileInstantiatingGlobalAsm, NoOptimizedMir, RecursionLimit, @@ -786,7 +789,35 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { // *Before* monomorphizing, record that we already handled this mention. self.used_mentioned_items.insert(MentionedItem::Fn(callee_ty)); let callee_ty = self.monomorphize(callee_ty); - visit_fn_use(self.tcx, callee_ty, true, source, &mut self.used_items) + + // HACK(explicit_tail_calls): collect tail calls to `#[track_caller]` functions as indirect, + // because we later call them as such, to prevent issues with ABI incompatibility. + // Ideally we'd replace such tail calls with normal call + return, but this requires + // post-mono MIR optimizations, which we don't yet have. + let force_indirect_call = + if matches!(terminator.kind, mir::TerminatorKind::TailCall { .. }) + && let &ty::FnDef(def_id, args) = callee_ty.kind() + && let instance = ty::Instance::expect_resolve( + self.tcx, + ty::TypingEnv::fully_monomorphized(), + def_id, + args, + source, + ) + && instance.def.requires_caller_location(self.tcx) + { + true + } else { + false + }; + + visit_fn_use( + self.tcx, + callee_ty, + !force_indirect_call, + source, + &mut self.used_items, + ) } mir::TerminatorKind::Drop { ref place, .. } => { let ty = place.ty(self.body, self.tcx).ty; @@ -911,6 +942,8 @@ fn visit_instance_use<'tcx>( return; } if let Some(intrinsic) = tcx.intrinsic(instance.def_id()) { + collect_autodiff_fn(tcx, instance, intrinsic, output); + if let Some(_requirement) = ValidityRequirement::from_intrinsic(intrinsic.name) { // The intrinsics assert_inhabited, assert_zero_valid, and assert_mem_uninitialized_valid will // be lowered in codegen to nothing or a call to panic_nounwind. So if we encounter any diff --git a/compiler/rustc_monomorphize/src/collector/autodiff.rs b/compiler/rustc_monomorphize/src/collector/autodiff.rs new file mode 100644 index 00000000000..13868cca944 --- /dev/null +++ b/compiler/rustc_monomorphize/src/collector/autodiff.rs @@ -0,0 +1,48 @@ +use rustc_middle::bug; +use rustc_middle::ty::{self, GenericArg, IntrinsicDef, TyCtxt}; + +use crate::collector::{MonoItems, create_fn_mono_item}; + +// Here, we force both primal and diff function to be collected in +// mono so this does not interfere in `autodiff` intrinsics +// codegen process. If they are unused, LLVM will remove them when +// compiling with O3. +pub(crate) fn collect_autodiff_fn<'tcx>( + tcx: TyCtxt<'tcx>, + instance: ty::Instance<'tcx>, + intrinsic: IntrinsicDef, + output: &mut MonoItems<'tcx>, +) { + if intrinsic.name != rustc_span::sym::autodiff { + return; + }; + + collect_autodiff_fn_from_arg(instance.args[0], tcx, output); +} + +fn collect_autodiff_fn_from_arg<'tcx>( + arg: GenericArg<'tcx>, + tcx: TyCtxt<'tcx>, + output: &mut MonoItems<'tcx>, +) { + let (instance, span) = match arg.kind() { + ty::GenericArgKind::Type(ty) => match ty.kind() { + ty::FnDef(def_id, substs) => { + let span = tcx.def_span(def_id); + let instance = ty::Instance::expect_resolve( + tcx, + ty::TypingEnv::non_body_analysis(tcx, def_id), + *def_id, + substs, + span, + ); + + (instance, span) + } + _ => bug!("expected autodiff function"), + }, + _ => bug!("expected type when matching autodiff arg"), + }; + + output.push(create_fn_mono_item(tcx, instance, span)); +} diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 628ea2b63de..d784d3540c4 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -92,8 +92,6 @@ //! source-level module, functions from the same module will be available for //! inlining, even when they are not marked `#[inline]`. -mod autodiff; - use std::cmp; use std::collections::hash_map::Entry; use std::fs::{self, File}; @@ -251,17 +249,7 @@ where always_export_generics, ); - // We can't differentiate a function that got inlined. - let autodiff_active = cfg!(llvm_enzyme) - && matches!(mono_item, MonoItem::Fn(_)) - && cx - .tcx - .codegen_fn_attrs(mono_item.def_id()) - .autodiff_item - .as_ref() - .is_some_and(|ad| ad.is_active()); - - if !autodiff_active && visibility == Visibility::Hidden && can_be_internalized { + if visibility == Visibility::Hidden && can_be_internalized { internalization_candidates.insert(mono_item); } let size_estimate = mono_item.size_estimate(cx.tcx); @@ -1157,27 +1145,15 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> MonoItemPartitio } } - #[cfg(not(llvm_enzyme))] - let autodiff_mono_items: Vec<_> = vec![]; - #[cfg(llvm_enzyme)] - let mut autodiff_mono_items: Vec<_> = vec![]; let mono_items: DefIdSet = items .iter() .filter_map(|mono_item| match *mono_item { - MonoItem::Fn(ref instance) => { - #[cfg(llvm_enzyme)] - autodiff_mono_items.push((mono_item, instance)); - Some(instance.def_id()) - } + MonoItem::Fn(ref instance) => Some(instance.def_id()), MonoItem::Static(def_id) => Some(def_id), _ => None, }) .collect(); - let autodiff_items = - autodiff::find_autodiff_source_functions(tcx, &usage_map, autodiff_mono_items); - let autodiff_items = tcx.arena.alloc_from_iter(autodiff_items); - // Output monomorphization stats per def_id if let SwitchWithOptPath::Enabled(ref path) = tcx.sess.opts.unstable_opts.dump_mono_stats && let Err(err) = @@ -1235,11 +1211,7 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> MonoItemPartitio } } - MonoItemPartitions { - all_mono_items: tcx.arena.alloc(mono_items), - codegen_units, - autodiff_items, - } + MonoItemPartitions { all_mono_items: tcx.arena.alloc(mono_items), codegen_units } } /// Outputs stats about instantiation counts and estimated size, per `MonoItem`'s diff --git a/compiler/rustc_monomorphize/src/partitioning/autodiff.rs b/compiler/rustc_monomorphize/src/partitioning/autodiff.rs deleted file mode 100644 index 22d593b80b8..00000000000 --- a/compiler/rustc_monomorphize/src/partitioning/autodiff.rs +++ /dev/null @@ -1,143 +0,0 @@ -use rustc_ast::expand::autodiff_attrs::{AutoDiffItem, DiffActivity}; -use rustc_hir::def_id::LOCAL_CRATE; -use rustc_middle::bug; -use rustc_middle::mir::mono::MonoItem; -use rustc_middle::ty::{self, Instance, PseudoCanonicalInput, Ty, TyCtxt, TypingEnv}; -use rustc_symbol_mangling::symbol_name_for_instance_in_crate; -use tracing::{debug, trace}; - -use crate::partitioning::UsageMap; - -fn adjust_activity_to_abi<'tcx>(tcx: TyCtxt<'tcx>, fn_ty: Ty<'tcx>, da: &mut Vec<DiffActivity>) { - if !matches!(fn_ty.kind(), ty::FnDef(..)) { - bug!("expected fn def for autodiff, got {:?}", fn_ty); - } - - // We don't actually pass the types back into the type system. - // All we do is decide how to handle the arguments. - let sig = fn_ty.fn_sig(tcx).skip_binder(); - - let mut new_activities = vec![]; - let mut new_positions = vec![]; - for (i, ty) in sig.inputs().iter().enumerate() { - if let Some(inner_ty) = ty.builtin_deref(true) { - if inner_ty.is_slice() { - // Now we need to figure out the size of each slice element in memory to allow - // safety checks and usability improvements in the backend. - let sty = match inner_ty.builtin_index() { - Some(sty) => sty, - None => { - panic!("slice element type unknown"); - } - }; - let pci = PseudoCanonicalInput { - typing_env: TypingEnv::fully_monomorphized(), - value: sty, - }; - - let layout = tcx.layout_of(pci); - let elem_size = match layout { - Ok(layout) => layout.size, - Err(_) => { - bug!("autodiff failed to compute slice element size"); - } - }; - let elem_size: u32 = elem_size.bytes() as u32; - - // We know that the length will be passed as extra arg. - if !da.is_empty() { - // We are looking at a slice. The length of that slice will become an - // extra integer on llvm level. Integers are always const. - // However, if the slice get's duplicated, we want to know to later check the - // size. So we mark the new size argument as FakeActivitySize. - // There is one FakeActivitySize per slice, so for convenience we store the - // slice element size in bytes in it. We will use the size in the backend. - let activity = match da[i] { - DiffActivity::DualOnly - | DiffActivity::Dual - | DiffActivity::Dualv - | DiffActivity::DuplicatedOnly - | DiffActivity::Duplicated => { - DiffActivity::FakeActivitySize(Some(elem_size)) - } - DiffActivity::Const => DiffActivity::Const, - _ => bug!("unexpected activity for ptr/ref"), - }; - new_activities.push(activity); - new_positions.push(i + 1); - } - - continue; - } - } - } - // now add the extra activities coming from slices - // Reverse order to not invalidate the indices - for _ in 0..new_activities.len() { - let pos = new_positions.pop().unwrap(); - let activity = new_activities.pop().unwrap(); - da.insert(pos, activity); - } -} - -pub(crate) fn find_autodiff_source_functions<'tcx>( - tcx: TyCtxt<'tcx>, - usage_map: &UsageMap<'tcx>, - autodiff_mono_items: Vec<(&MonoItem<'tcx>, &Instance<'tcx>)>, -) -> Vec<AutoDiffItem> { - let mut autodiff_items: Vec<AutoDiffItem> = vec![]; - for (item, instance) in autodiff_mono_items { - let target_id = instance.def_id(); - let cg_fn_attr = &tcx.codegen_fn_attrs(target_id).autodiff_item; - let Some(target_attrs) = cg_fn_attr else { - continue; - }; - let mut input_activities: Vec<DiffActivity> = target_attrs.input_activity.clone(); - if target_attrs.is_source() { - trace!("source found: {:?}", target_id); - } - if !target_attrs.apply_autodiff() { - continue; - } - - let target_symbol = symbol_name_for_instance_in_crate(tcx, instance.clone(), LOCAL_CRATE); - - let source = - usage_map.used_map.get(&item).unwrap().into_iter().find_map(|item| match *item { - MonoItem::Fn(ref instance_s) => { - let source_id = instance_s.def_id(); - if let Some(ad) = &tcx.codegen_fn_attrs(source_id).autodiff_item - && ad.is_active() - { - return Some(instance_s); - } - None - } - _ => None, - }); - let inst = match source { - Some(source) => source, - None => continue, - }; - - debug!("source_id: {:?}", inst.def_id()); - let fn_ty = inst.ty(tcx, ty::TypingEnv::fully_monomorphized()); - assert!(fn_ty.is_fn()); - adjust_activity_to_abi(tcx, fn_ty, &mut input_activities); - let symb = symbol_name_for_instance_in_crate(tcx, inst.clone(), LOCAL_CRATE); - - let mut new_target_attrs = target_attrs.clone(); - new_target_attrs.input_activity = input_activities; - let itm = new_target_attrs.into_item(symb, target_symbol); - autodiff_items.push(itm); - } - - if !autodiff_items.is_empty() { - trace!("AUTODIFF ITEMS EXIST"); - for item in &mut *autodiff_items { - trace!("{}", &item); - } - } - - autodiff_items -} diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index 5e08c3a03d8..a4a8317912a 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -8,6 +8,7 @@ use std::ops::ControlFlow; use derive_where::derive_where; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::TraitSolverLangItem; +use rustc_type_ir::search_graph::CandidateHeadUsages; use rustc_type_ir::solve::SizedTraitKind; use rustc_type_ir::{ self as ty, Interner, TypeFlags, TypeFoldable, TypeSuperVisitable, TypeVisitable, @@ -33,10 +34,11 @@ enum AliasBoundKind { /// /// It consists of both the `source`, which describes how that goal would be proven, /// and the `result` when using the given `source`. -#[derive_where(Clone, Debug; I: Interner)] +#[derive_where(Debug; I: Interner)] pub(super) struct Candidate<I: Interner> { pub(super) source: CandidateSource<I>, pub(super) result: CanonicalResponse<I>, + pub(super) head_usages: CandidateHeadUsages, } /// Methods used to assemble candidates for either trait or projection goals. @@ -116,8 +118,11 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal<I, Self>, assumption: I::Clause, - ) -> Result<Candidate<I>, NoSolution> { - Self::fast_reject_assumption(ecx, goal, assumption)?; + ) -> Result<Candidate<I>, CandidateHeadUsages> { + match Self::fast_reject_assumption(ecx, goal, assumption) { + Ok(()) => {} + Err(NoSolution) => return Err(CandidateHeadUsages::default()), + } // Dealing with `ParamEnv` candidates is a bit of a mess as we need to lazily // check whether the candidate is global while considering normalization. @@ -126,18 +131,23 @@ where // in `probe` even if the candidate does not apply before we get there. We handle this // by using a `Cell` here. We only ever write into it inside of `match_assumption`. let source = Cell::new(CandidateSource::ParamEnv(ParamEnvSource::Global)); - ecx.probe(|result: &QueryResult<I>| inspect::ProbeKind::TraitCandidate { - source: source.get(), - result: *result, - }) - .enter(|ecx| { - Self::match_assumption(ecx, goal, assumption, |ecx| { - ecx.try_evaluate_added_goals()?; - source.set(ecx.characterize_param_env_assumption(goal.param_env, assumption)?); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + let (result, head_usages) = ecx + .probe(|result: &QueryResult<I>| inspect::ProbeKind::TraitCandidate { + source: source.get(), + result: *result, }) - }) - .map(|result| Candidate { source: source.get(), result }) + .enter_single_candidate(|ecx| { + Self::match_assumption(ecx, goal, assumption, |ecx| { + ecx.try_evaluate_added_goals()?; + source.set(ecx.characterize_param_env_assumption(goal.param_env, assumption)?); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + }); + + match result { + Ok(result) => Ok(Candidate { source: source.get(), result, head_usages }), + Err(NoSolution) => Err(head_usages), + } } /// Try equating an assumption predicate against a goal's predicate. If it @@ -355,6 +365,19 @@ pub(super) enum AssembleCandidatesFrom { EnvAndBounds, } +/// This is currently used to track the [CandidateHeadUsages] of all failed `ParamEnv` +/// candidates. This is then used to ignore their head usages in case there's another +/// always applicable `ParamEnv` candidate. Look at how `param_env_head_usages` is +/// used in the code for more details. +/// +/// We could easily extend this to also ignore head usages of other ignored candidates. +/// However, we currently don't have any tests where this matters and the complexity of +/// doing so does not feel worth it for now. +#[derive(Debug)] +pub(super) struct FailedCandidateInfo { + pub param_env_head_usages: CandidateHeadUsages, +} + impl<D, I> EvalCtxt<'_, D> where D: SolverDelegate<Interner = I>, @@ -364,16 +387,20 @@ where &mut self, goal: Goal<I, G>, assemble_from: AssembleCandidatesFrom, - ) -> Vec<Candidate<I>> { + ) -> (Vec<Candidate<I>>, FailedCandidateInfo) { + let mut candidates = vec![]; + let mut failed_candidate_info = + FailedCandidateInfo { param_env_head_usages: CandidateHeadUsages::default() }; let Ok(normalized_self_ty) = self.structurally_normalize_ty(goal.param_env, goal.predicate.self_ty()) else { - return vec![]; + return (candidates, failed_candidate_info); }; if normalized_self_ty.is_ty_var() { debug!("self type has been normalized to infer"); - return self.forced_ambiguity(MaybeCause::Ambiguity).into_iter().collect(); + candidates.extend(self.forced_ambiguity(MaybeCause::Ambiguity)); + return (candidates, failed_candidate_info); } let goal: Goal<I, G> = goal @@ -382,16 +409,15 @@ where // normalizing the self type as well, since type variables are not uniquified. let goal = self.resolve_vars_if_possible(goal); - let mut candidates = vec![]; - if let TypingMode::Coherence = self.typing_mode() && let Ok(candidate) = self.consider_coherence_unknowable_candidate(goal) { - return vec![candidate]; + candidates.push(candidate); + return (candidates, failed_candidate_info); } self.assemble_alias_bound_candidates(goal, &mut candidates); - self.assemble_param_env_candidates(goal, &mut candidates); + self.assemble_param_env_candidates(goal, &mut candidates, &mut failed_candidate_info); match assemble_from { AssembleCandidatesFrom::All => { @@ -423,7 +449,7 @@ where AssembleCandidatesFrom::EnvAndBounds => {} } - candidates + (candidates, failed_candidate_info) } pub(super) fn forced_ambiguity( @@ -584,9 +610,15 @@ where &mut self, goal: Goal<I, G>, candidates: &mut Vec<Candidate<I>>, + failed_candidate_info: &mut FailedCandidateInfo, ) { for assumption in goal.param_env.caller_bounds().iter() { - candidates.extend(G::probe_and_consider_param_env_candidate(self, goal, assumption)); + match G::probe_and_consider_param_env_candidate(self, goal, assumption) { + Ok(candidate) => candidates.push(candidate), + Err(head_usages) => { + failed_candidate_info.param_env_head_usages.merge_usages(head_usages) + } + } } } @@ -661,7 +693,11 @@ where if let Ok(result) = self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) { - candidates.push(Candidate { source: CandidateSource::AliasBound, result }); + candidates.push(Candidate { + source: CandidateSource::AliasBound, + result, + head_usages: CandidateHeadUsages::default(), + }); } return; } @@ -959,7 +995,7 @@ where // Even when a trait bound has been proven using a where-bound, we // still need to consider alias-bounds for normalization, see // `tests/ui/next-solver/alias-bound-shadowed-by-env.rs`. - let mut candidates: Vec<_> = self + let (mut candidates, _) = self .assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::EnvAndBounds); // We still need to prefer where-bounds over alias-bounds however. @@ -972,23 +1008,20 @@ where return inject_normalize_to_rigid_candidate(self); } - if let Some(response) = self.try_merge_candidates(&candidates) { + if let Some((response, _)) = self.try_merge_candidates(&candidates) { Ok(response) } else { self.flounder(&candidates) } } TraitGoalProvenVia::Misc => { - let mut candidates = + let (mut candidates, _) = self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All); // Prefer "orphaned" param-env normalization predicates, which are used // (for example, and ideally only) when proving item bounds for an impl. - let candidates_from_env: Vec<_> = candidates - .extract_if(.., |c| matches!(c.source, CandidateSource::ParamEnv(_))) - .collect(); - if let Some(response) = self.try_merge_candidates(&candidates_from_env) { - return Ok(response); + if candidates.iter().any(|c| matches!(c.source, CandidateSource::ParamEnv(_))) { + candidates.retain(|c| matches!(c.source, CandidateSource::ParamEnv(_))); } // We drop specialized impls to allow normalization via a final impl here. In case @@ -997,7 +1030,7 @@ where // means we can just ignore inference constraints and don't have to special-case // constraining the normalized-to `term`. self.filter_specialized_impls(AllowInferenceConstraints::Yes, &mut candidates); - if let Some(response) = self.try_merge_candidates(&candidates) { + if let Some((response, _)) = self.try_merge_candidates(&candidates) { Ok(response) } else { self.flounder(&candidates) 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 8671cc7c3d3..a4738306181 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 @@ -8,7 +8,7 @@ use rustc_type_ir::fast_reject::DeepRejectCtxt; use rustc_type_ir::inherent::*; use rustc_type_ir::relate::Relate; use rustc_type_ir::relate::solver_relating::RelateExt; -use rustc_type_ir::search_graph::PathKind; +use rustc_type_ir::search_graph::{CandidateHeadUsages, PathKind}; use rustc_type_ir::{ self as ty, CanonicalVarValues, InferCtxtLike, Interner, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, @@ -399,6 +399,10 @@ where result } + pub(super) fn ignore_candidate_head_usages(&mut self, usages: CandidateHeadUsages) { + self.search_graph.ignore_candidate_head_usages(usages); + } + /// Recursively evaluates `goal`, returning whether any inference vars have /// been constrained and the certainty of the result. fn evaluate_goal( diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs index ed0cedc4077..e5658ba32ff 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs @@ -1,5 +1,6 @@ use std::marker::PhantomData; +use rustc_type_ir::search_graph::CandidateHeadUsages; use rustc_type_ir::{InferCtxtLike, Interner}; use tracing::instrument; @@ -25,6 +26,20 @@ where D: SolverDelegate<Interner = I>, I: Interner, { + pub(in crate::solve) fn enter_single_candidate( + self, + f: impl FnOnce(&mut EvalCtxt<'_, D>) -> T, + ) -> (T, CandidateHeadUsages) { + self.ecx.search_graph.enter_single_candidate(); + let mut candidate_usages = CandidateHeadUsages::default(); + let result = self.enter(|ecx| { + let result = f(ecx); + candidate_usages = ecx.search_graph.finish_single_candidate(); + result + }); + (result, candidate_usages) + } + pub(in crate::solve) fn enter(self, f: impl FnOnce(&mut EvalCtxt<'_, D>) -> T) -> T { let ProbeCtxt { ecx: outer, probe_kind, _result } = self; @@ -78,7 +93,8 @@ where self, f: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>, ) -> Result<Candidate<I>, NoSolution> { - self.cx.enter(|ecx| f(ecx)).map(|result| Candidate { source: self.source, result }) + let (result, head_usages) = self.cx.enter_single_candidate(f); + result.map(|result| Candidate { source: self.source, result, head_usages }) } } diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 2feebe270a6..c2745c878dc 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -236,6 +236,12 @@ where } } +#[derive(Debug)] +enum MergeCandidateInfo { + AlwaysApplicable(usize), + EqualResponse, +} + impl<D, I> EvalCtxt<'_, D> where D: SolverDelegate<Interner = I>, @@ -248,23 +254,25 @@ where fn try_merge_candidates( &mut self, candidates: &[Candidate<I>], - ) -> Option<CanonicalResponse<I>> { + ) -> Option<(CanonicalResponse<I>, MergeCandidateInfo)> { if candidates.is_empty() { return None; } + let always_applicable = candidates.iter().enumerate().find(|(_, candidate)| { + candidate.result.value.certainty == Certainty::Yes + && has_no_inference_or_external_constraints(candidate.result) + }); + if let Some((i, c)) = always_applicable { + return Some((c.result, MergeCandidateInfo::AlwaysApplicable(i))); + } + let one: CanonicalResponse<I> = candidates[0].result; if candidates[1..].iter().all(|candidate| candidate.result == one) { - return Some(one); + return Some((one, MergeCandidateInfo::EqualResponse)); } - candidates - .iter() - .find(|candidate| { - candidate.result.value.certainty == Certainty::Yes - && has_no_inference_or_external_constraints(candidate.result) - }) - .map(|candidate| candidate.result) + None } fn bail_with_ambiguity(&mut self, candidates: &[Candidate<I>]) -> CanonicalResponse<I> { diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 60bae738e61..04ede365a21 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -13,11 +13,13 @@ use tracing::{debug, instrument, trace}; use crate::delegate::SolverDelegate; use crate::solve::assembly::structural_traits::{self, AsyncCallableRelevantTypes}; -use crate::solve::assembly::{self, AllowInferenceConstraints, AssembleCandidatesFrom, Candidate}; +use crate::solve::assembly::{ + self, AllowInferenceConstraints, AssembleCandidatesFrom, Candidate, FailedCandidateInfo, +}; use crate::solve::inspect::ProbeKind; use crate::solve::{ BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause, - NoSolution, ParamEnvSource, QueryResult, has_only_region_constraints, + MergeCandidateInfo, NoSolution, ParamEnvSource, QueryResult, has_only_region_constraints, }; impl<D, I> assembly::GoalKind<D> for TraitPredicate<I> @@ -1344,9 +1346,10 @@ where pub(super) fn merge_trait_candidates( &mut self, mut candidates: Vec<Candidate<I>>, + failed_candidate_info: FailedCandidateInfo, ) -> Result<(CanonicalResponse<I>, Option<TraitGoalProvenVia>), NoSolution> { if let TypingMode::Coherence = self.typing_mode() { - return if let Some(response) = self.try_merge_candidates(&candidates) { + return if let Some((response, _)) = self.try_merge_candidates(&candidates) { Ok((response, Some(TraitGoalProvenVia::Misc))) } else { self.flounder(&candidates).map(|r| (r, None)) @@ -1376,10 +1379,41 @@ where let where_bounds: Vec<_> = candidates .extract_if(.., |c| matches!(c.source, CandidateSource::ParamEnv(_))) .collect(); - return if let Some(response) = self.try_merge_candidates(&where_bounds) { - Ok((response, Some(TraitGoalProvenVia::ParamEnv))) + if let Some((response, info)) = self.try_merge_candidates(&where_bounds) { + match info { + // If there's an always applicable candidate, the result of all + // other candidates does not matter. This means we can ignore + // them when checking whether we've reached a fixpoint. + // + // We always prefer the first always applicable candidate, even if a + // later candidate is also always applicable and would result in fewer + // reruns. We could slightly improve this by e.g. searching for another + // always applicable candidate which doesn't depend on any cycle heads. + // + // NOTE: This is optimization is observable in case there is an always + // applicable global candidate and another non-global candidate which only + // applies because of a provisional result. I can't even think of a test + // case where this would occur and even then, this would not be unsound. + // Supporting this makes the code more involved, so I am just going to + // ignore this for now. + MergeCandidateInfo::AlwaysApplicable(i) => { + for (j, c) in where_bounds.into_iter().enumerate() { + if i != j { + self.ignore_candidate_head_usages(c.head_usages) + } + } + // If a where-bound does not apply, we don't actually get a + // candidate for it. We manually track the head usages + // of all failed `ParamEnv` candidates instead. + self.ignore_candidate_head_usages( + failed_candidate_info.param_env_head_usages, + ); + } + MergeCandidateInfo::EqualResponse => {} + } + return Ok((response, Some(TraitGoalProvenVia::ParamEnv))); } else { - Ok((self.bail_with_ambiguity(&where_bounds), None)) + return Ok((self.bail_with_ambiguity(&where_bounds), None)); }; } @@ -1387,7 +1421,7 @@ where let alias_bounds: Vec<_> = candidates .extract_if(.., |c| matches!(c.source, CandidateSource::AliasBound)) .collect(); - return if let Some(response) = self.try_merge_candidates(&alias_bounds) { + return if let Some((response, _)) = self.try_merge_candidates(&alias_bounds) { Ok((response, Some(TraitGoalProvenVia::AliasBound))) } else { Ok((self.bail_with_ambiguity(&alias_bounds), None)) @@ -1412,7 +1446,7 @@ where TraitGoalProvenVia::Misc }; - if let Some(response) = self.try_merge_candidates(&candidates) { + if let Some((response, _)) = self.try_merge_candidates(&candidates) { Ok((response, Some(proven_via))) } else { self.flounder(&candidates).map(|r| (r, None)) @@ -1424,8 +1458,9 @@ where &mut self, goal: Goal<I, TraitPredicate<I>>, ) -> Result<(CanonicalResponse<I>, Option<TraitGoalProvenVia>), NoSolution> { - let candidates = self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All); - self.merge_trait_candidates(candidates) + let (candidates, failed_candidate_info) = + self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All); + self.merge_trait_candidates(candidates, failed_candidate_info) } fn try_stall_coroutine(&mut self, self_ty: I::Ty) -> Option<Result<Candidate<I>, NoSolution>> { diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index 41d3889c448..7f6afeba28c 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -11,6 +11,7 @@ use tracing::debug; use super::{ AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, PathStyle, Trailing, UsePreAttrPos, }; +use crate::parser::FnContext; use crate::{errors, exp, fluent_generated as fluent}; // Public for rustfmt usage @@ -200,7 +201,7 @@ impl<'a> Parser<'a> { AttrWrapper::empty(), true, false, - FnParseMode { req_name: |_| true, req_body: true }, + FnParseMode { req_name: |_| true, context: FnContext::Free, req_body: true }, ForceCollect::No, ) { Ok(Some(item)) => { diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index a32cd33a260..220e4ac18fc 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -44,6 +44,7 @@ use crate::errors::{ UnexpectedConstParamDeclaration, UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead, WrapType, }; +use crate::parser::FnContext; use crate::parser::attr::InnerAttrPolicy; use crate::{exp, fluent_generated as fluent}; @@ -2246,6 +2247,7 @@ impl<'a> Parser<'a> { pat: Box<ast::Pat>, require_name: bool, first_param: bool, + fn_parse_mode: &crate::parser::item::FnParseMode, ) -> Option<Ident> { // If we find a pattern followed by an identifier, it could be an (incorrect) // C-style parameter declaration. @@ -2268,7 +2270,14 @@ impl<'a> Parser<'a> { || self.token == token::Lt || self.token == token::CloseParen) { - let rfc_note = "anonymous parameters are removed in the 2018 edition (see RFC 1685)"; + let maybe_emit_anon_params_note = |this: &mut Self, err: &mut Diag<'_>| { + let ed = this.token.span.with_neighbor(this.prev_token.span).edition(); + if matches!(fn_parse_mode.context, crate::parser::item::FnContext::Trait) + && (fn_parse_mode.req_name)(ed) + { + err.note("anonymous parameters are removed in the 2018 edition (see RFC 1685)"); + } + }; let (ident, self_sugg, param_sugg, type_sugg, self_span, param_span, type_span) = match pat.kind { @@ -2305,7 +2314,7 @@ impl<'a> Parser<'a> { "_: ".to_string(), Applicability::MachineApplicable, ); - err.note(rfc_note); + maybe_emit_anon_params_note(self, err); } return None; @@ -2313,7 +2322,13 @@ impl<'a> Parser<'a> { }; // `fn foo(a, b) {}`, `fn foo(a<x>, b<y>) {}` or `fn foo(usize, usize) {}` - if first_param { + if first_param + // Only when the fn is a method, we emit this suggestion. + && matches!( + fn_parse_mode.context, + FnContext::Trait | FnContext::Impl + ) + { err.span_suggestion_verbose( self_span, "if this is a `self` type, give it a parameter name", @@ -2337,7 +2352,7 @@ impl<'a> Parser<'a> { type_sugg, Applicability::MachineApplicable, ); - err.note(rfc_note); + maybe_emit_anon_params_note(self, err); // Don't attempt to recover by using the `X` in `X<Y>` as the parameter name. return if self.token == token::Lt { None } else { Some(ident) }; diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 607adaf0829..ca89eb1e2cf 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -116,7 +116,8 @@ impl<'a> Parser<'a> { impl<'a> Parser<'a> { pub fn parse_item(&mut self, force_collect: ForceCollect) -> PResult<'a, Option<Box<Item>>> { - let fn_parse_mode = FnParseMode { req_name: |_| true, req_body: true }; + let fn_parse_mode = + FnParseMode { req_name: |_| true, context: FnContext::Free, req_body: true }; self.parse_item_(fn_parse_mode, force_collect).map(|i| i.map(Box::new)) } @@ -975,7 +976,8 @@ impl<'a> Parser<'a> { &mut self, force_collect: ForceCollect, ) -> PResult<'a, Option<Option<Box<AssocItem>>>> { - let fn_parse_mode = FnParseMode { req_name: |_| true, req_body: true }; + let fn_parse_mode = + FnParseMode { req_name: |_| true, context: FnContext::Impl, req_body: true }; self.parse_assoc_item(fn_parse_mode, force_collect) } @@ -983,8 +985,11 @@ impl<'a> Parser<'a> { &mut self, force_collect: ForceCollect, ) -> PResult<'a, Option<Option<Box<AssocItem>>>> { - let fn_parse_mode = - FnParseMode { req_name: |edition| edition >= Edition::Edition2018, req_body: false }; + let fn_parse_mode = FnParseMode { + req_name: |edition| edition >= Edition::Edition2018, + context: FnContext::Trait, + req_body: false, + }; self.parse_assoc_item(fn_parse_mode, force_collect) } @@ -1261,7 +1266,8 @@ impl<'a> Parser<'a> { &mut self, force_collect: ForceCollect, ) -> PResult<'a, Option<Option<Box<ForeignItem>>>> { - let fn_parse_mode = FnParseMode { req_name: |_| true, req_body: false }; + let fn_parse_mode = + FnParseMode { req_name: |_| true, context: FnContext::Free, req_body: false }; Ok(self.parse_item_(fn_parse_mode, force_collect)?.map( |Item { attrs, id, span, vis, kind, tokens }| { let kind = match ForeignItemKind::try_from(kind) { @@ -2135,7 +2141,8 @@ impl<'a> Parser<'a> { let inherited_vis = Visibility { span: DUMMY_SP, kind: VisibilityKind::Inherited, tokens: None }; // We use `parse_fn` to get a span for the function - let fn_parse_mode = FnParseMode { req_name: |_| true, req_body: true }; + let fn_parse_mode = + FnParseMode { req_name: |_| true, context: FnContext::Free, req_body: true }; match self.parse_fn( &mut AttrVec::new(), fn_parse_mode, @@ -2403,6 +2410,9 @@ pub(crate) struct FnParseMode { /// * The span is from Edition 2015. In particular, you can get a /// 2015 span inside a 2021 crate using macros. pub(super) req_name: ReqName, + /// The context in which this function is parsed, used for diagnostics. + /// This indicates the fn is a free function or method and so on. + pub(super) context: FnContext, /// If this flag is set to `true`, then plain, semicolon-terminated function /// prototypes are not allowed here. /// @@ -2424,6 +2434,18 @@ pub(crate) struct FnParseMode { pub(super) req_body: bool, } +/// The context in which a function is parsed. +/// FIXME(estebank, xizheyin): Use more variants. +#[derive(Clone, Copy, PartialEq, Eq)] +pub(crate) enum FnContext { + /// Free context. + Free, + /// A Trait context. + Trait, + /// An Impl block. + Impl, +} + /// Parsing of functions and methods. impl<'a> Parser<'a> { /// Parse a function starting from the front matter (`const ...`) to the body `{ ... }` or `;`. @@ -2439,11 +2461,8 @@ impl<'a> Parser<'a> { let header = self.parse_fn_front_matter(vis, case, FrontMatterParsingMode::Function)?; // `const ... fn` let ident = self.parse_ident()?; // `foo` let mut generics = self.parse_generics()?; // `<'a, T, ...>` - let decl = match self.parse_fn_decl( - fn_parse_mode.req_name, - AllowPlus::Yes, - RecoverReturnSign::Yes, - ) { + let decl = match self.parse_fn_decl(&fn_parse_mode, AllowPlus::Yes, RecoverReturnSign::Yes) + { Ok(decl) => decl, Err(old_err) => { // If we see `for Ty ...` then user probably meant `impl` item. @@ -2961,18 +2980,21 @@ impl<'a> Parser<'a> { /// Parses the parameter list and result type of a function declaration. pub(super) fn parse_fn_decl( &mut self, - req_name: ReqName, + fn_parse_mode: &FnParseMode, ret_allow_plus: AllowPlus, recover_return_sign: RecoverReturnSign, ) -> PResult<'a, Box<FnDecl>> { Ok(Box::new(FnDecl { - inputs: self.parse_fn_params(req_name)?, + inputs: self.parse_fn_params(fn_parse_mode)?, output: self.parse_ret_ty(ret_allow_plus, RecoverQPath::Yes, recover_return_sign)?, })) } /// Parses the parameter list of a function, including the `(` and `)` delimiters. - pub(super) fn parse_fn_params(&mut self, req_name: ReqName) -> PResult<'a, ThinVec<Param>> { + pub(super) fn parse_fn_params( + &mut self, + fn_parse_mode: &FnParseMode, + ) -> PResult<'a, ThinVec<Param>> { let mut first_param = true; // Parse the arguments, starting out with `self` being allowed... if self.token != TokenKind::OpenParen @@ -2988,7 +3010,7 @@ impl<'a> Parser<'a> { let (mut params, _) = self.parse_paren_comma_seq(|p| { p.recover_vcs_conflict_marker(); let snapshot = p.create_snapshot_for_diagnostic(); - let param = p.parse_param_general(req_name, first_param, true).or_else(|e| { + let param = p.parse_param_general(fn_parse_mode, first_param, true).or_else(|e| { let guar = e.emit(); // When parsing a param failed, we should check to make the span of the param // not contain '(' before it. @@ -3019,7 +3041,7 @@ impl<'a> Parser<'a> { /// - `recover_arg_parse` is used to recover from a failed argument parse. pub(super) fn parse_param_general( &mut self, - req_name: ReqName, + fn_parse_mode: &FnParseMode, first_param: bool, recover_arg_parse: bool, ) -> PResult<'a, Param> { @@ -3035,16 +3057,22 @@ impl<'a> Parser<'a> { let is_name_required = match this.token.kind { token::DotDotDot => false, - _ => req_name(this.token.span.with_neighbor(this.prev_token.span).edition()), + _ => (fn_parse_mode.req_name)( + this.token.span.with_neighbor(this.prev_token.span).edition(), + ), }; let (pat, ty) = if is_name_required || this.is_named_param() { debug!("parse_param_general parse_pat (is_name_required:{})", is_name_required); let (pat, colon) = this.parse_fn_param_pat_colon()?; if !colon { let mut err = this.unexpected().unwrap_err(); - return if let Some(ident) = - this.parameter_without_type(&mut err, pat, is_name_required, first_param) - { + return if let Some(ident) = this.parameter_without_type( + &mut err, + pat, + is_name_required, + first_param, + fn_parse_mode, + ) { let guar = err.emit(); Ok((dummy_arg(ident, guar), Trailing::No, UsePreAttrPos::No)) } else { diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 0a8a0203013..41ed1f95a01 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -22,7 +22,7 @@ use std::{fmt, mem, slice}; use attr_wrapper::{AttrWrapper, UsePreAttrPos}; pub use diagnostics::AttemptLocalParseRecovery; pub(crate) use expr::ForbiddenLetReason; -pub(crate) use item::FnParseMode; +pub(crate) use item::{FnContext, FnParseMode}; pub use pat::{CommaRecoveryMode, RecoverColon, RecoverComma}; use path::PathStyle; use rustc_ast::token::{ diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index a6ec3ea4245..37fc723cd89 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -20,7 +20,9 @@ use crate::errors::{ PathFoundAttributeInParams, PathFoundCVariadicParams, PathSingleColon, PathTripleColon, }; use crate::exp; -use crate::parser::{CommaRecoveryMode, ExprKind, RecoverColon, RecoverComma}; +use crate::parser::{ + CommaRecoveryMode, ExprKind, FnContext, FnParseMode, RecoverColon, RecoverComma, +}; /// Specifies how to parse a path. #[derive(Copy, Clone, PartialEq)] @@ -399,7 +401,13 @@ impl<'a> Parser<'a> { let dcx = self.dcx(); let parse_params_result = self.parse_paren_comma_seq(|p| { - let param = p.parse_param_general(|_| false, false, false); + // Inside parenthesized type arguments, we want types only, not names. + let mode = FnParseMode { + context: FnContext::Free, + req_name: |_| false, + req_body: false, + }; + let param = p.parse_param_general(&mode, false, false); param.map(move |param| { if !matches!(param.pat.kind, PatKind::Missing) { dcx.emit_err(FnPathFoundNamedParams { diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 7aacb674253..b4943ff7de6 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -19,8 +19,8 @@ use super::diagnostics::AttemptLocalParseRecovery; use super::pat::{PatternLocation, RecoverComma}; use super::path::PathStyle; use super::{ - AttrWrapper, BlockMode, FnParseMode, ForceCollect, Parser, Restrictions, SemiColonMode, - Trailing, UsePreAttrPos, + AttrWrapper, BlockMode, FnContext, FnParseMode, ForceCollect, Parser, Restrictions, + SemiColonMode, Trailing, UsePreAttrPos, }; use crate::errors::{self, MalformedLoopLabel}; use crate::exp; @@ -153,7 +153,7 @@ impl<'a> Parser<'a> { attrs.clone(), // FIXME: unwanted clone of attrs false, true, - FnParseMode { req_name: |_| true, req_body: true }, + FnParseMode { req_name: |_| true, context: FnContext::Free, req_body: true }, force_collect, )? { self.mk_stmt(lo.to(item.span), StmtKind::Item(Box::new(item))) diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 0d479731e73..290f0a440af 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -19,6 +19,7 @@ use crate::errors::{ NestedCVariadicType, ReturnTypesUseThinArrow, }; use crate::parser::item::FrontMatterParsingMode; +use crate::parser::{FnContext, FnParseMode}; use crate::{exp, maybe_recover_from_interpolated_ty_qpath}; /// Signals whether parsing a type should allow `+`. @@ -769,7 +770,12 @@ impl<'a> Parser<'a> { if self.may_recover() && self.token == TokenKind::Lt { self.recover_fn_ptr_with_generics(lo, &mut params, param_insertion_point)?; } - let decl = self.parse_fn_decl(|_| false, AllowPlus::No, recover_return_sign)?; + let mode = crate::parser::item::FnParseMode { + req_name: |_| false, + context: FnContext::Free, + req_body: false, + }; + let decl = self.parse_fn_decl(&mode, AllowPlus::No, recover_return_sign)?; let decl_span = span_start.to(self.prev_token.span); Ok(TyKind::FnPtr(Box::new(FnPtrTy { @@ -1314,7 +1320,8 @@ impl<'a> Parser<'a> { self.bump(); let args_lo = self.token.span; let snapshot = self.create_snapshot_for_diagnostic(); - match self.parse_fn_decl(|_| false, AllowPlus::No, RecoverReturnSign::OnlyFatArrow) { + let mode = FnParseMode { req_name: |_| false, context: FnContext::Free, req_body: false }; + match self.parse_fn_decl(&mode, AllowPlus::No, RecoverReturnSign::OnlyFatArrow) { Ok(decl) => { self.dcx().emit_err(ExpectedFnPathFoundFnKeyword { fn_token_span }); Some(ast::Path { @@ -1400,8 +1407,9 @@ impl<'a> Parser<'a> { // Parse `(T, U) -> R`. let inputs_lo = self.token.span; + let mode = FnParseMode { req_name: |_| false, context: FnContext::Free, req_body: false }; let inputs: ThinVec<_> = - self.parse_fn_params(|_| false)?.into_iter().map(|input| input.ty).collect(); + self.parse_fn_params(&mode)?.into_iter().map(|input| input.ty).collect(); let inputs_span = inputs_lo.to(self.prev_token.span); let output = self.parse_ret_ty(AllowPlus::No, RecoverQPath::No, RecoverReturnSign::No)?; let args = ast::ParenthesizedArgs { diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 0c1b0d622f2..c9bf4fe4449 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -8,7 +8,6 @@ use rustc_hir::HirId; use rustc_hir::def_id::DefId; use rustc_index::{Idx, IndexVec}; use rustc_middle::middle::stability::EvalResult; -use rustc_middle::mir::{self, Const}; use rustc_middle::thir::{self, Pat, PatKind, PatRange, PatRangeBoundary}; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{ @@ -430,7 +429,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { match bdy { PatRangeBoundary::NegInfinity => MaybeInfiniteInt::NegInfinity, PatRangeBoundary::Finite(value) => { - let bits = value.eval_bits(self.tcx, self.typing_env); + let bits = value.try_to_scalar_int().unwrap().to_bits_unchecked(); match *ty.kind() { ty::Int(ity) => { let size = Integer::from_int_ty(&self.tcx, ity).size().bits(); @@ -520,75 +519,54 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { PatKind::Constant { value } => { match ty.kind() { ty::Bool => { - ctor = match value.try_eval_bool(cx.tcx, cx.typing_env) { - Some(b) => Bool(b), - None => Opaque(OpaqueId::new()), - }; + ctor = Bool(value.try_to_bool().unwrap()); fields = vec![]; arity = 0; } ty::Char | ty::Int(_) | ty::Uint(_) => { - ctor = match value.try_eval_bits(cx.tcx, cx.typing_env) { - Some(bits) => { - let x = match *ty.kind() { - ty::Int(ity) => { - let size = Integer::from_int_ty(&cx.tcx, ity).size().bits(); - MaybeInfiniteInt::new_finite_int(bits, size) - } - _ => MaybeInfiniteInt::new_finite_uint(bits), - }; - IntRange(IntRange::from_singleton(x)) - } - None => Opaque(OpaqueId::new()), + ctor = { + let bits = value.valtree.unwrap_leaf().to_bits_unchecked(); + let x = match *ty.kind() { + ty::Int(ity) => { + let size = Integer::from_int_ty(&cx.tcx, ity).size().bits(); + MaybeInfiniteInt::new_finite_int(bits, size) + } + _ => MaybeInfiniteInt::new_finite_uint(bits), + }; + IntRange(IntRange::from_singleton(x)) }; fields = vec![]; arity = 0; } ty::Float(ty::FloatTy::F16) => { - ctor = match value.try_eval_bits(cx.tcx, cx.typing_env) { - Some(bits) => { - use rustc_apfloat::Float; - let value = rustc_apfloat::ieee::Half::from_bits(bits); - F16Range(value, value, RangeEnd::Included) - } - None => Opaque(OpaqueId::new()), - }; + use rustc_apfloat::Float; + let bits = value.valtree.unwrap_leaf().to_u16(); + let value = rustc_apfloat::ieee::Half::from_bits(bits.into()); + ctor = F16Range(value, value, RangeEnd::Included); fields = vec![]; arity = 0; } ty::Float(ty::FloatTy::F32) => { - ctor = match value.try_eval_bits(cx.tcx, cx.typing_env) { - Some(bits) => { - use rustc_apfloat::Float; - let value = rustc_apfloat::ieee::Single::from_bits(bits); - F32Range(value, value, RangeEnd::Included) - } - None => Opaque(OpaqueId::new()), - }; + use rustc_apfloat::Float; + let bits = value.valtree.unwrap_leaf().to_u32(); + let value = rustc_apfloat::ieee::Single::from_bits(bits.into()); + ctor = F32Range(value, value, RangeEnd::Included); fields = vec![]; arity = 0; } ty::Float(ty::FloatTy::F64) => { - ctor = match value.try_eval_bits(cx.tcx, cx.typing_env) { - Some(bits) => { - use rustc_apfloat::Float; - let value = rustc_apfloat::ieee::Double::from_bits(bits); - F64Range(value, value, RangeEnd::Included) - } - None => Opaque(OpaqueId::new()), - }; + use rustc_apfloat::Float; + let bits = value.valtree.unwrap_leaf().to_u64(); + let value = rustc_apfloat::ieee::Double::from_bits(bits.into()); + ctor = F64Range(value, value, RangeEnd::Included); fields = vec![]; arity = 0; } ty::Float(ty::FloatTy::F128) => { - ctor = match value.try_eval_bits(cx.tcx, cx.typing_env) { - Some(bits) => { - use rustc_apfloat::Float; - let value = rustc_apfloat::ieee::Quad::from_bits(bits); - F128Range(value, value, RangeEnd::Included) - } - None => Opaque(OpaqueId::new()), - }; + use rustc_apfloat::Float; + let bits = value.valtree.unwrap_leaf().to_u128(); + let value = rustc_apfloat::ieee::Quad::from_bits(bits); + ctor = F128Range(value, value, RangeEnd::Included); fields = vec![]; arity = 0; } @@ -630,8 +608,12 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { } ty::Float(fty) => { use rustc_apfloat::Float; - let lo = lo.as_finite().map(|c| c.eval_bits(cx.tcx, cx.typing_env)); - let hi = hi.as_finite().map(|c| c.eval_bits(cx.tcx, cx.typing_env)); + let lo = lo + .as_finite() + .map(|c| c.try_to_scalar_int().unwrap().to_bits_unchecked()); + let hi = hi + .as_finite() + .map(|c| c.try_to_scalar_int().unwrap().to_bits_unchecked()); match fty { ty::FloatTy::F16 => { use rustc_apfloat::ieee::Half; @@ -739,8 +721,8 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { }; match ScalarInt::try_from_uint(bits, size) { Some(scalar) => { - let value = mir::Const::from_scalar(tcx, scalar.into(), ty.inner()); - PatRangeBoundary::Finite(value) + let valtree = ty::ValTree::from_scalar_int(tcx, scalar); + PatRangeBoundary::Finite(valtree) } // The value doesn't fit. Since `x >= 0` and 0 always encodes the minimum value // for a type, the problem isn't that the value is too small. So it must be too @@ -760,7 +742,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { "_".to_string() } else if range.is_singleton() { let lo = cx.hoist_pat_range_bdy(range.lo, ty); - let value = lo.as_finite().unwrap(); + let value = ty::Value { ty: ty.inner(), valtree: lo.as_finite().unwrap() }; value.to_string() } else { // We convert to an inclusive range for diagnostics. @@ -772,7 +754,9 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { // fictitious values after `{u,i}size::MAX` (see [`IntRange::split`] for why we do // this). We show this to the user as `usize::MAX..` which is slightly incorrect but // probably clear enough. - lo = PatRangeBoundary::Finite(ty.numeric_max_val(cx.tcx).unwrap()); + let max = ty.numeric_max_val(cx.tcx).unwrap(); + let max = ty::ValTree::from_scalar_int(cx.tcx, max.try_to_scalar_int().unwrap()); + lo = PatRangeBoundary::Finite(max); } let hi = if let Some(hi) = range.hi.minus_one() { hi @@ -907,7 +891,7 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> { type Ty = RevealedTy<'tcx>; type Error = ErrorGuaranteed; type VariantIdx = VariantIdx; - type StrLit = Const<'tcx>; + type StrLit = ty::Value<'tcx>; type ArmData = HirId; type PatData = &'p Pat<'tcx>; diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 580fa4f5b2c..1eb4e1199e6 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -493,9 +493,6 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { }); } } - // We don't add prelude imports to the globs since they only affect lexical scopes, - // which are not relevant to import resolution. - ImportKind::Glob { is_prelude: true, .. } => {} ImportKind::Glob { .. } => current_module.globs.borrow_mut().push(import), _ => unreachable!(), } @@ -658,13 +655,19 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { self.add_import(module_path, kind, use_tree.span, item, root_span, item.id, vis); } ast::UseTreeKind::Glob => { - let kind = ImportKind::Glob { - is_prelude: ast::attr::contains_name(&item.attrs, sym::prelude_import), - max_vis: Cell::new(None), - id, - }; - - self.add_import(prefix, kind, use_tree.span, item, root_span, item.id, vis); + if !ast::attr::contains_name(&item.attrs, sym::prelude_import) { + let kind = ImportKind::Glob { max_vis: Cell::new(None), id }; + self.add_import(prefix, kind, use_tree.span, item, root_span, item.id, vis); + } else { + // Resolve the prelude import early. + let path_res = + self.r.cm().maybe_resolve_path(&prefix, None, &self.parent_scope, None); + if let PathResult::Module(ModuleOrUniformRoot::Module(module)) = path_res { + self.r.prelude = Some(module); + } else { + self.r.dcx().span_err(use_tree.span, "cannot resolve a prelude import"); + } + } } ast::UseTreeKind::Nested { ref items, .. } => { // Ensure there is at most one `self` in the list diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index c5fcbdfb42f..a437f86e377 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -2189,9 +2189,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let ast::ExprKind::Struct(struct_expr) = &expr.kind else { return }; // We don't have to handle type-relative paths because they're forbidden in ADT // expressions, but that would change with `#[feature(more_qualified_paths)]`. - let Some(Res::Def(_, def_id)) = - self.partial_res_map[&struct_expr.path.segments.iter().last().unwrap().id].full_res() - else { + let Some(segment) = struct_expr.path.segments.last() else { return }; + let Some(partial_res) = self.partial_res_map.get(&segment.id) else { return }; + let Some(Res::Def(_, def_id)) = partial_res.full_res() else { return; }; let Some(default_fields) = self.field_defaults(def_id) else { return }; diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index dc01c94af57..c1b3aff4e69 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -318,7 +318,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let normalized_ident = Ident { span: normalized_span, ..ident }; // Walk backwards up the ribs in scope. - let mut module = self.graph_root; for (i, rib) in ribs.iter().enumerate().rev() { debug!("walk rib\n{:?}", rib.bindings); // Use the rib kind to determine whether we are resolving parameters @@ -334,51 +333,47 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { *original_rib_ident_def, ribs, ))); + } else if let RibKind::Block(Some(module)) = rib.kind + && let Ok(binding) = self.cm().resolve_ident_in_module_unadjusted( + ModuleOrUniformRoot::Module(module), + ident, + ns, + parent_scope, + Shadowing::Unrestricted, + finalize.map(|finalize| Finalize { used: Used::Scope, ..finalize }), + ignore_binding, + None, + ) + { + // The ident resolves to an item in a block. + return Some(LexicalScopeBinding::Item(binding)); + } else if let RibKind::Module(module) = rib.kind { + // Encountered a module item, abandon ribs and look into that module and preludes. + return self + .cm() + .early_resolve_ident_in_lexical_scope( + orig_ident, + ScopeSet::Late(ns, module, finalize.map(|finalize| finalize.node_id)), + parent_scope, + finalize, + finalize.is_some(), + ignore_binding, + None, + ) + .ok() + .map(LexicalScopeBinding::Item); } - module = match rib.kind { - RibKind::Module(module) => module, - RibKind::MacroDefinition(def) if def == self.macro_def(ident.span.ctxt()) => { - // If an invocation of this macro created `ident`, give up on `ident` - // and switch to `ident`'s source from the macro definition. - ident.span.remove_mark(); - continue; - } - _ => continue, - }; - - match module.kind { - ModuleKind::Block => {} // We can see through blocks - _ => break, - } - - let item = self.cm().resolve_ident_in_module_unadjusted( - ModuleOrUniformRoot::Module(module), - ident, - ns, - parent_scope, - Shadowing::Unrestricted, - finalize.map(|finalize| Finalize { used: Used::Scope, ..finalize }), - ignore_binding, - None, - ); - if let Ok(binding) = item { - // The ident resolves to an item. - return Some(LexicalScopeBinding::Item(binding)); + if let RibKind::MacroDefinition(def) = rib.kind + && def == self.macro_def(ident.span.ctxt()) + { + // If an invocation of this macro created `ident`, give up on `ident` + // and switch to `ident`'s source from the macro definition. + ident.span.remove_mark(); } } - self.cm() - .early_resolve_ident_in_lexical_scope( - orig_ident, - ScopeSet::Late(ns, module, finalize.map(|finalize| finalize.node_id)), - parent_scope, - finalize, - finalize.is_some(), - ignore_binding, - None, - ) - .ok() - .map(LexicalScopeBinding::Item) + + unreachable!() } /// Resolve an identifier in lexical scope. @@ -1171,6 +1166,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { for rib in ribs { match rib.kind { RibKind::Normal + | RibKind::Block(..) | RibKind::FnOrCoroutine | RibKind::Module(..) | RibKind::MacroDefinition(..) @@ -1263,6 +1259,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { for rib in ribs { let (has_generic_params, def_kind) = match rib.kind { RibKind::Normal + | RibKind::Block(..) | RibKind::FnOrCoroutine | RibKind::Module(..) | RibKind::MacroDefinition(..) @@ -1356,6 +1353,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { for rib in ribs { let (has_generic_params, def_kind) = match rib.kind { RibKind::Normal + | RibKind::Block(..) | RibKind::FnOrCoroutine | RibKind::Module(..) | RibKind::MacroDefinition(..) diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 403d440bee7..7c93fdb88ee 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -87,7 +87,6 @@ pub(crate) enum ImportKind<'ra> { id: NodeId, }, Glob { - is_prelude: bool, // The visibility of the greatest re-export. // n.b. `max_vis` is only used in `finalize_import` to check for re-export errors. max_vis: Cell<Option<Visibility>>, @@ -125,12 +124,9 @@ impl<'ra> std::fmt::Debug for ImportKind<'ra> { .field("nested", nested) .field("id", id) .finish(), - Glob { is_prelude, max_vis, id } => f - .debug_struct("Glob") - .field("is_prelude", is_prelude) - .field("max_vis", max_vis) - .field("id", id) - .finish(), + Glob { max_vis, id } => { + f.debug_struct("Glob").field("max_vis", max_vis).field("id", id).finish() + } ExternCrate { source, target, id } => f .debug_struct("ExternCrate") .field("source", source) @@ -1073,7 +1069,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ImportKind::Single { source, target, ref bindings, type_ns_only, id, .. } => { (source, target, bindings, type_ns_only, id) } - ImportKind::Glob { is_prelude, ref max_vis, id } => { + ImportKind::Glob { ref max_vis, id } => { if import.module_path.len() <= 1 { // HACK(eddyb) `lint_if_path_starts_with_module` needs at least // 2 segments, so the `resolve_path` above won't trigger it. @@ -1096,8 +1092,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { module: None, }); } - if !is_prelude - && let Some(max_vis) = max_vis.get() + if let Some(max_vis) = max_vis.get() && !max_vis.is_at_least(import.vis, self.tcx) { let def_id = self.local_def_id(id); @@ -1485,7 +1480,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { fn resolve_glob_import(&mut self, import: Import<'ra>) { // This function is only called for glob imports. - let ImportKind::Glob { id, is_prelude, .. } = import.kind else { unreachable!() }; + let ImportKind::Glob { id, .. } = import.kind else { unreachable!() }; let ModuleOrUniformRoot::Module(module) = import.imported_module.get().unwrap() else { self.dcx().emit_err(CannotGlobImportAllCrates { span: import.span }); @@ -1504,9 +1499,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if module == import.parent_scope.module { return; - } else if is_prelude { - self.prelude = Some(module); - return; } // Add to module's glob_importers diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 1e4ab57a316..5200f9340e1 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -192,6 +192,13 @@ pub(crate) enum RibKind<'ra> { /// No restriction needs to be applied. Normal, + /// We passed through an `ast::Block`. + /// Behaves like `Normal`, but also partially like `Module` if the block contains items. + /// `Block(None)` must be always processed in the same way as `Block(Some(module))` + /// with empty `module`. The module can be `None` only because creation of some definitely + /// empty modules is skipped as an optimization. + Block(Option<Module<'ra>>), + /// We passed through an impl or trait and are now in one of its /// methods or associated types. Allow references to ty params that impl or trait /// binds. Disallow any other upvars (including other ty params that are @@ -210,7 +217,7 @@ pub(crate) enum RibKind<'ra> { /// All other constants aren't allowed to use generic params at all. ConstantItem(ConstantHasGenerics, Option<(Ident, ConstantItemKind)>), - /// We passed through a module. + /// We passed through a module item. Module(Module<'ra>), /// We passed through a `macro_rules!` statement @@ -242,6 +249,7 @@ impl RibKind<'_> { pub(crate) fn contains_params(&self) -> bool { match self { RibKind::Normal + | RibKind::Block(..) | RibKind::FnOrCoroutine | RibKind::ConstantItem(..) | RibKind::Module(_) @@ -258,15 +266,8 @@ impl RibKind<'_> { fn is_label_barrier(self) -> bool { match self { RibKind::Normal | RibKind::MacroDefinition(..) => false, - - RibKind::AssocItem - | RibKind::FnOrCoroutine - | RibKind::Item(..) - | RibKind::ConstantItem(..) - | RibKind::Module(..) - | RibKind::ForwardGenericParamBan(_) - | RibKind::ConstParamTy - | RibKind::InlineAsmSym => true, + RibKind::FnOrCoroutine | RibKind::ConstantItem(..) => true, + kind => bug!("unexpected rib kind: {kind:?}"), } } } @@ -1527,19 +1528,6 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { ret } - fn with_mod_rib<T>(&mut self, id: NodeId, f: impl FnOnce(&mut Self) -> T) -> T { - let module = self.r.expect_module(self.r.local_def_id(id).to_def_id()); - // Move down in the graph. - let orig_module = replace(&mut self.parent_scope.module, module); - self.with_rib(ValueNS, RibKind::Module(module), |this| { - this.with_rib(TypeNS, RibKind::Module(module), |this| { - let ret = f(this); - this.parent_scope.module = orig_module; - ret - }) - }) - } - fn visit_generic_params(&mut self, params: &'ast [GenericParam], add_self_upper: bool) { // For type parameter defaults, we have to ban access // to following type parameters, as the GenericArgs can only @@ -2677,20 +2665,25 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } ItemKind::Mod(..) => { - self.with_mod_rib(item.id, |this| { - if mod_inner_docs { - this.resolve_doc_links(&item.attrs, MaybeExported::Ok(item.id)); - } - let old_macro_rules = this.parent_scope.macro_rules; - visit::walk_item(this, item); - // Maintain macro_rules scopes in the same way as during early resolution - // for diagnostics and doc links. - if item.attrs.iter().all(|attr| { - !attr.has_name(sym::macro_use) && !attr.has_name(sym::macro_escape) - }) { - this.parent_scope.macro_rules = old_macro_rules; - } + let module = self.r.expect_module(self.r.local_def_id(item.id).to_def_id()); + let orig_module = replace(&mut self.parent_scope.module, module); + self.with_rib(ValueNS, RibKind::Module(module), |this| { + this.with_rib(TypeNS, RibKind::Module(module), |this| { + if mod_inner_docs { + this.resolve_doc_links(&item.attrs, MaybeExported::Ok(item.id)); + } + let old_macro_rules = this.parent_scope.macro_rules; + visit::walk_item(this, item); + // Maintain macro_rules scopes in the same way as during early resolution + // for diagnostics and doc links. + if item.attrs.iter().all(|attr| { + !attr.has_name(sym::macro_use) && !attr.has_name(sym::macro_escape) + }) { + this.parent_scope.macro_rules = old_macro_rules; + } + }) }); + self.parent_scope.module = orig_module; } ItemKind::Static(box ast::StaticItem { @@ -2821,9 +2814,9 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { // We also can't shadow bindings from associated parent items. for ns in [ValueNS, TypeNS] { for parent_rib in self.ribs[ns].iter().rev() { - // Break at mod level, to account for nested items which are + // Break at module or block level, to account for nested items which are // allowed to shadow generic param names. - if matches!(parent_rib.kind, RibKind::Module(..)) { + if matches!(parent_rib.kind, RibKind::Module(..) | RibKind::Block(..)) { break; } @@ -4652,16 +4645,16 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { debug!("(resolving block) entering block"); // Move down in the graph, if there's an anonymous module rooted here. let orig_module = self.parent_scope.module; - let anonymous_module = self.r.block_map.get(&block.id).cloned(); // clones a reference + let anonymous_module = self.r.block_map.get(&block.id).copied(); let mut num_macro_definition_ribs = 0; if let Some(anonymous_module) = anonymous_module { debug!("(resolving block) found anonymous module, moving down"); - self.ribs[ValueNS].push(Rib::new(RibKind::Module(anonymous_module))); - self.ribs[TypeNS].push(Rib::new(RibKind::Module(anonymous_module))); + self.ribs[ValueNS].push(Rib::new(RibKind::Block(Some(anonymous_module)))); + self.ribs[TypeNS].push(Rib::new(RibKind::Block(Some(anonymous_module)))); self.parent_scope.module = anonymous_module; } else { - self.ribs[ValueNS].push(Rib::new(RibKind::Normal)); + self.ribs[ValueNS].push(Rib::new(RibKind::Block(None))); } // Descend into the block. diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 6a753b38035..9b201e40d13 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -849,9 +849,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } // Try to find in last block rib - if let Some(rib) = &self.last_block_rib - && let RibKind::Normal = rib.kind - { + if let Some(rib) = &self.last_block_rib { for (ident, &res) in &rib.bindings { if let Res::Local(_) = res && path.len() == 1 @@ -900,7 +898,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { if path.len() == 1 { for rib in self.ribs[ns].iter().rev() { let item = path[0].ident; - if let RibKind::Module(module) = rib.kind + if let RibKind::Module(module) | RibKind::Block(Some(module)) = rib.kind && let Some(did) = find_doc_alias_name(self.r, module, item.name) { return Some((did, item)); @@ -2458,9 +2456,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } } - if let RibKind::Module(module) = rib.kind - && let ModuleKind::Block = module.kind - { + if let RibKind::Block(Some(module)) = rib.kind { self.r.add_module_candidates(module, &mut names, &filter_fn, Some(ctxt)); } else if let RibKind::Module(module) = rib.kind { // Encountered a module item, abandon ribs and look into that module and preludes. diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index c665c85d1fe..9793d8091e2 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -16,10 +16,11 @@ use std::{cmp, fmt, fs, iter}; use externs::{ExternOpt, split_extern_opt}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; -use rustc_data_structures::stable_hasher::{StableOrd, ToStableHashKey}; +use rustc_data_structures::stable_hasher::{StableHasher, StableOrd, ToStableHashKey}; use rustc_errors::emitter::HumanReadableErrorType; use rustc_errors::{ColorConfig, DiagArgValue, DiagCtxtFlags, IntoDiagArg}; use rustc_feature::UnstableFeatures; +use rustc_hashes::Hash64; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::edition::{DEFAULT_EDITION, EDITION_NAME_LIST, Edition, LATEST_STABLE_EDITION}; use rustc_span::source_map::FilePathMapping; @@ -1195,7 +1196,25 @@ pub struct OutputFilenames { pub const RLINK_EXT: &str = "rlink"; pub const RUST_CGU_EXT: &str = "rcgu"; pub const DWARF_OBJECT_EXT: &str = "dwo"; +pub const MAX_FILENAME_LENGTH: usize = 143; // ecryptfs limits filenames to 143 bytes see #49914 +/// Ensure the filename is not too long, as some filesystems have a limit. +/// If the filename is too long, hash part of it and append the hash to the filename. +/// This is a workaround for long crate names generating overly long filenames. +fn maybe_strip_file_name(mut path: PathBuf) -> PathBuf { + if path.file_name().map_or(0, |name| name.len()) > MAX_FILENAME_LENGTH { + let filename = path.file_name().unwrap().to_string_lossy(); + let hash_len = 64 / 4; // Hash64 is 64 bits encoded in hex + let stripped_len = filename.len() - MAX_FILENAME_LENGTH + hash_len; + + let mut hasher = StableHasher::new(); + filename[..stripped_len].hash(&mut hasher); + let hash = hasher.finish::<Hash64>(); + + path.set_file_name(format!("{:x}-{}", hash, &filename[stripped_len..])); + } + path +} impl OutputFilenames { pub fn new( out_directory: PathBuf, @@ -1288,7 +1307,7 @@ impl OutputFilenames { } let temps_directory = self.temps_directory.as_ref().unwrap_or(&self.out_directory); - self.with_directory_and_extension(temps_directory, &extension) + maybe_strip_file_name(self.with_directory_and_extension(temps_directory, &extension)) } pub fn temp_path_for_diagnostic(&self, ext: &str) -> PathBuf { @@ -2828,16 +2847,27 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M // This is the location used by the `rustc-dev` `rustup` component. real_source_base_dir("lib/rustlib/rustc-src/rust", "compiler/rustc/src/main.rs"); - let mut search_paths = vec![]; - for s in &matches.opt_strs("L") { - search_paths.push(SearchPath::from_cli_opt( - sysroot.path(), - &target_triple, - early_dcx, - s, - unstable_opts.unstable_options, - )); - } + // We eagerly scan all files in each passed -L path. If the same directory is passed multiple + // times, and the directory contains a lot of files, this can take a lot of time. + // So we remove -L paths that were passed multiple times, and keep only the first occurrence. + // We still have to keep the original order of the -L arguments. + let search_paths: Vec<SearchPath> = { + let mut seen_search_paths = FxHashSet::default(); + let search_path_matches: Vec<String> = matches.opt_strs("L"); + search_path_matches + .iter() + .filter(|p| seen_search_paths.insert(*p)) + .map(|path| { + SearchPath::from_cli_opt( + sysroot.path(), + &target_triple, + early_dcx, + &path, + unstable_opts.unstable_options, + ) + }) + .collect() + }; let working_dir = std::env::current_dir().unwrap_or_else(|e| { early_dcx.early_fatal(format!("Current directory is invalid: {e}")); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 416ce27367e..f4a6d0f5891 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -392,6 +392,7 @@ symbols! { __D, __H, __S, + __T, __awaitee, __try_var, _t, @@ -542,6 +543,7 @@ symbols! { audit_that, augmented_assignments, auto_traits, + autodiff, autodiff_forward, autodiff_reverse, automatically_derived, @@ -745,6 +747,7 @@ symbols! { contracts_ensures, contracts_internals, contracts_requires, + convert, convert_identity, copy, copy_closures, @@ -846,6 +849,7 @@ symbols! { derive_const, derive_const_issue: "118304", derive_default_enum, + derive_from, derive_smart_pointer, destruct, destructuring_assignment, @@ -1589,6 +1593,7 @@ symbols! { panic_const_shl_overflow, panic_const_shr_overflow, panic_const_sub_overflow, + panic_display, panic_fmt, panic_handler, panic_impl, @@ -1834,7 +1839,6 @@ symbols! { rustc_coherence_is_core, rustc_coinductive, rustc_confusables, - rustc_const_panic_str, rustc_const_stable, rustc_const_stable_indirect, rustc_const_unstable, @@ -2330,6 +2334,7 @@ symbols! { va_start, val, validity, + value, values, var, variant_count, diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index 025fa299826..4d9741f1d11 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -235,7 +235,8 @@ impl<'tcx> Printer<'tcx> for LegacySymbolMangler<'tcx> { fn print_region(&mut self, _region: ty::Region<'_>) -> Result<(), PrintError> { // This might be reachable (via `pretty_print_dyn_existential`) even though - // `<Self As PrettyPrinter>::should_print_region` returns false. See #144994. + // `<Self As PrettyPrinter>::should_print_optional_region` returns false and + // `print_path_with_generic_args` filters out lifetimes. See #144994. Ok(()) } @@ -389,7 +390,6 @@ impl<'tcx> Printer<'tcx> for LegacySymbolMangler<'tcx> { let args = args.iter().cloned().filter(|arg| !matches!(arg.kind(), GenericArgKind::Lifetime(_))); - if args.clone().next().is_some() { self.generic_delimiters(|cx| cx.comma_sep(args)) } else { @@ -459,7 +459,7 @@ impl<'tcx> Printer<'tcx> for LegacySymbolMangler<'tcx> { } impl<'tcx> PrettyPrinter<'tcx> for LegacySymbolMangler<'tcx> { - fn should_print_region(&self, _region: ty::Region<'_>) -> bool { + fn should_print_optional_region(&self, _region: ty::Region<'_>) -> bool { false } diff --git a/compiler/rustc_target/src/spec/json.rs b/compiler/rustc_target/src/spec/json.rs index 6c44b7ff52f..f56a65d9c0c 100644 --- a/compiler/rustc_target/src/spec/json.rs +++ b/compiler/rustc_target/src/spec/json.rs @@ -168,7 +168,6 @@ impl Target { forward!(main_needs_argc_argv); forward!(has_thread_local); forward!(obj_is_bitcode); - forward!(bitcode_llvm_cmdline); forward_opt!(max_atomic_width); forward_opt!(min_atomic_width); forward!(atomic_cas); @@ -361,7 +360,6 @@ impl ToJson for Target { target_option_val!(main_needs_argc_argv); target_option_val!(has_thread_local); target_option_val!(obj_is_bitcode); - target_option_val!(bitcode_llvm_cmdline); target_option_val!(min_atomic_width); target_option_val!(max_atomic_width); target_option_val!(atomic_cas); @@ -555,7 +553,6 @@ struct TargetSpecJson { main_needs_argc_argv: Option<bool>, has_thread_local: Option<bool>, obj_is_bitcode: Option<bool>, - bitcode_llvm_cmdline: Option<StaticCow<str>>, max_atomic_width: Option<u64>, min_atomic_width: Option<u64>, atomic_cas: Option<bool>, diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index ee408c76006..2aa8ab5d317 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -2624,8 +2624,6 @@ pub struct TargetOptions { /// If we give emcc .o files that are actually .bc files it /// will 'just work'. pub obj_is_bitcode: bool, - /// Content of the LLVM cmdline section associated with embedded bitcode. - pub bitcode_llvm_cmdline: StaticCow<str>, /// Don't use this field; instead use the `.min_atomic_width()` method. pub min_atomic_width: Option<u64>, @@ -2989,7 +2987,6 @@ impl Default for TargetOptions { allow_asm: true, has_thread_local: false, obj_is_bitcode: false, - bitcode_llvm_cmdline: "".into(), min_atomic_width: None, max_atomic_width: None, atomic_cas: true, 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 20e425bfad1..9447612d026 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -235,22 +235,22 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } fn print_region(&mut self, _region: ty::Region<'_>) -> Result<(), PrintError> { - unreachable!(); // because `path_generic_args` ignores the `GenericArgs` + unreachable!(); // because `print_path_with_generic_args` ignores the `GenericArgs` } fn print_type(&mut self, _ty: Ty<'tcx>) -> Result<(), PrintError> { - unreachable!(); // because `path_generic_args` ignores the `GenericArgs` + unreachable!(); // because `print_path_with_generic_args` ignores the `GenericArgs` } fn print_dyn_existential( &mut self, _predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, ) -> Result<(), PrintError> { - unreachable!(); // because `path_generic_args` ignores the `GenericArgs` + unreachable!(); // because `print_path_with_generic_args` ignores the `GenericArgs` } fn print_const(&mut self, _ct: ty::Const<'tcx>) -> Result<(), PrintError> { - unreachable!(); // because `path_generic_args` ignores the `GenericArgs` + unreachable!(); // because `print_path_with_generic_args` ignores the `GenericArgs` } fn print_crate_name(&mut self, cnum: CrateNum) -> Result<(), PrintError> { @@ -1619,8 +1619,18 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { { let e = self.tcx.erase_regions(e); let f = self.tcx.erase_regions(f); - let expected = with_forced_trimmed_paths!(e.sort_string(self.tcx)); - let found = with_forced_trimmed_paths!(f.sort_string(self.tcx)); + let mut expected = with_forced_trimmed_paths!(e.sort_string(self.tcx)); + let mut found = with_forced_trimmed_paths!(f.sort_string(self.tcx)); + if let ObligationCauseCode::Pattern { span, .. } = cause.code() + && let Some(span) = span + && !span.from_expansion() + && cause.span.from_expansion() + { + // When the type error comes from a macro like `assert!()`, and we are pointing at + // code the user wrote the cause and effect are reversed as the expected value is + // what the macro expanded to. + (found, expected) = (expected, found); + } if expected == found { label_or_note(span, terr.to_string(self.tcx)); } else { @@ -2143,7 +2153,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ) -> Option<(DiagStyledString, DiagStyledString)> { match values { ValuePairs::Regions(exp_found) => self.expected_found_str(exp_found), - ValuePairs::Terms(exp_found) => self.expected_found_str_term(exp_found, long_ty_path), + ValuePairs::Terms(exp_found) => { + self.expected_found_str_term(cause, exp_found, long_ty_path) + } ValuePairs::Aliases(exp_found) => self.expected_found_str(exp_found), ValuePairs::ExistentialTraitRef(exp_found) => self.expected_found_str(exp_found), ValuePairs::ExistentialProjection(exp_found) => self.expected_found_str(exp_found), @@ -2182,6 +2194,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { fn expected_found_str_term( &self, + cause: &ObligationCause<'tcx>, exp_found: ty::error::ExpectedFound<ty::Term<'tcx>>, long_ty_path: &mut Option<PathBuf>, ) -> Option<(DiagStyledString, DiagStyledString)> { @@ -2189,8 +2202,27 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if exp_found.references_error() { return None; } + let (mut expected, mut found) = (exp_found.expected, exp_found.found); + + if let ObligationCauseCode::Pattern { span, .. } = cause.code() + && let Some(span) = span + && !span.from_expansion() + && cause.span.from_expansion() + { + // When the type error comes from a macro like `assert!()`, and we are pointing at + // code the user wrote, the cause and effect are reversed as the expected value is + // what the macro expanded to. So if the user provided a `Type` when the macro is + // written in such a way that a `bool` was expected, we want to print: + // = note: expected `bool` + // found `Type`" + // but as far as the compiler is concerned, after expansion what was expected was `Type` + // = note: expected `Type` + // found `bool`" + // so we reverse them here to match user expectation. + (expected, found) = (found, expected); + } - Some(match (exp_found.expected.kind(), exp_found.found.kind()) { + Some(match (expected.kind(), found.kind()) { (ty::TermKind::Ty(expected), ty::TermKind::Ty(found)) => { let (mut exp, mut fnd) = self.cmp(expected, found); // Use the terminal width as the basis to determine when to compress the printed diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 62859329de3..214a6e86d39 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -37,7 +37,6 @@ use super::on_unimplemented::{AppendConstMessage, OnUnimplementedNote}; use super::suggestions::get_explanation_based_on_obligation; use super::{ ArgKind, CandidateSimilarity, FindExprBySpan, GetSafeTransmuteErrorAndReason, ImplCandidate, - UnsatisfiedConst, }; use crate::error_reporting::TypeErrCtxt; use crate::error_reporting::infer::TyCategory; @@ -374,13 +373,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - let UnsatisfiedConst(unsatisfied_const) = self - .maybe_add_note_for_unsatisfied_const( - leaf_trait_predicate, - &mut err, - span, - ); - if let Some((msg, span)) = type_def { err.span_label(span, msg); } @@ -506,7 +498,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { span, is_fn_trait, suggested, - unsatisfied_const, ); // Changing mutability doesn't make a difference to whether we have @@ -2716,7 +2707,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { span: Span, is_fn_trait: bool, suggested: bool, - unsatisfied_const: bool, ) { let body_def_id = obligation.cause.body_id; let span = if let ObligationCauseCode::BinOp { rhs_span: Some(rhs_span), .. } = @@ -2763,10 +2753,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { self.tcx.def_span(trait_def_id), crate::fluent_generated::trait_selection_trait_has_no_impls, ); - } else if !suggested - && !unsatisfied_const - && trait_predicate.polarity() == ty::PredicatePolarity::Positive - { + } else if !suggested && trait_predicate.polarity() == ty::PredicatePolarity::Positive { // Can't show anything else useful, try to find similar impls. let impl_candidates = self.find_similar_impl_candidates(trait_predicate); if !self.report_similar_impl_candidates( @@ -2878,17 +2865,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - fn maybe_add_note_for_unsatisfied_const( - &self, - _trait_predicate: ty::PolyTraitPredicate<'tcx>, - _err: &mut Diag<'_>, - _span: Span, - ) -> UnsatisfiedConst { - let unsatisfied_const = UnsatisfiedConst(false); - // FIXME(const_trait_impl) - unsatisfied_const - } - fn report_closure_error( &self, obligation: &PredicateObligation<'tcx>, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs index 1d8b934cef3..c8500b2d9d4 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs @@ -51,8 +51,6 @@ enum GetSafeTransmuteErrorAndReason { Error { err_msg: String, safe_transmute_explanation: Option<String> }, } -struct UnsatisfiedConst(pub bool); - /// Crude way of getting back an `Expr` from a `Span`. pub struct FindExprBySpan<'hir> { pub span: Span, diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index 24094eeea8d..348178a527f 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -170,27 +170,76 @@ impl PathKind { /// This is used to avoid rerunning a cycle if there's /// just a single usage kind and the final result matches /// its provisional result. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum UsageKind { - Single(PathKind), - Mixed, +/// +/// While it tracks the amount of usages using `u32`, we only ever +/// care whether there are any. We only count them to be able to ignore +/// usages from irrelevant candidates while evaluating a goal. +/// +/// This cares about how nested goals relied on a cycle head. It does +/// not care about how frequently the nested goal relied on it. +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] +struct HeadUsages { + inductive: u32, + unknown: u32, + coinductive: u32, + forced_ambiguity: u32, } -impl From<PathKind> for UsageKind { - fn from(path: PathKind) -> UsageKind { - UsageKind::Single(path) + +impl HeadUsages { + fn add_usage(&mut self, path: PathKind) { + match path { + PathKind::Inductive => self.inductive += 1, + PathKind::Unknown => self.unknown += 1, + PathKind::Coinductive => self.coinductive += 1, + PathKind::ForcedAmbiguity => self.forced_ambiguity += 1, + } + } + + /// This adds the usages which occurred while computing a nested goal. + /// + /// We don't actually care about how frequently the nested goal relied + /// on its cycle heads, only whether it did. + fn add_usages_from_nested(&mut self, usages: HeadUsages) { + let HeadUsages { inductive, unknown, coinductive, forced_ambiguity } = usages; + self.inductive += if inductive == 0 { 0 } else { 1 }; + self.unknown += if unknown == 0 { 0 } else { 1 }; + self.coinductive += if coinductive == 0 { 0 } else { 1 }; + self.forced_ambiguity += if forced_ambiguity == 0 { 0 } else { 1 }; + } + + fn ignore_usages(&mut self, usages: HeadUsages) { + let HeadUsages { inductive, unknown, coinductive, forced_ambiguity } = usages; + self.inductive = self.inductive.checked_sub(inductive).unwrap(); + self.unknown = self.unknown.checked_sub(unknown).unwrap(); + self.coinductive = self.coinductive.checked_sub(coinductive).unwrap(); + self.forced_ambiguity = self.forced_ambiguity.checked_sub(forced_ambiguity).unwrap(); + } + + fn is_empty(self) -> bool { + let HeadUsages { inductive, unknown, coinductive, forced_ambiguity } = self; + inductive == 0 && unknown == 0 && coinductive == 0 && forced_ambiguity == 0 } } -impl UsageKind { - #[must_use] - fn merge(self, other: impl Into<Self>) -> Self { - match (self, other.into()) { - (UsageKind::Mixed, _) | (_, UsageKind::Mixed) => UsageKind::Mixed, - (UsageKind::Single(lhs), UsageKind::Single(rhs)) => { - if lhs == rhs { - UsageKind::Single(lhs) - } else { - UsageKind::Mixed + +#[derive(Debug, Default)] +pub struct CandidateHeadUsages { + usages: Option<Box<HashMap<StackDepth, HeadUsages>>>, +} +impl CandidateHeadUsages { + pub fn merge_usages(&mut self, other: CandidateHeadUsages) { + if let Some(other_usages) = other.usages { + if let Some(ref mut self_usages) = self.usages { + #[allow(rustc::potential_query_instability)] + for (head_index, head) in other_usages.into_iter() { + let HeadUsages { inductive, unknown, coinductive, forced_ambiguity } = head; + let self_usages = self_usages.entry(head_index).or_default(); + self_usages.inductive += inductive; + self_usages.unknown += unknown; + self_usages.coinductive += coinductive; + self_usages.forced_ambiguity += forced_ambiguity; } + } else { + self.usages = Some(other_usages); } } } @@ -234,7 +283,12 @@ impl AvailableDepth { #[derive(Clone, Copy, Debug)] struct CycleHead { paths_to_head: PathsToNested, - usage_kind: UsageKind, + /// If the `usages` are empty, the result of that head does not matter + /// for the current goal. However, we still don't completely drop this + /// cycle head as whether or not it exists impacts which queries we + /// access, so ignoring it would cause incremental compilation verification + /// failures or hide query cycles. + usages: HeadUsages, } /// All cycle heads a given goal depends on, ordered by their stack depth. @@ -251,15 +305,19 @@ impl CycleHeads { self.heads.is_empty() } - fn highest_cycle_head(&self) -> StackDepth { - self.opt_highest_cycle_head().unwrap() + fn highest_cycle_head(&self) -> (StackDepth, CycleHead) { + self.heads.last_key_value().map(|(k, v)| (*k, *v)).unwrap() } - fn opt_highest_cycle_head(&self) -> Option<StackDepth> { + fn highest_cycle_head_index(&self) -> StackDepth { + self.opt_highest_cycle_head_index().unwrap() + } + + fn opt_highest_cycle_head_index(&self) -> Option<StackDepth> { self.heads.last_key_value().map(|(k, _)| *k) } - fn opt_lowest_cycle_head(&self) -> Option<StackDepth> { + fn opt_lowest_cycle_head_index(&self) -> Option<StackDepth> { self.heads.first_key_value().map(|(k, _)| *k) } @@ -272,20 +330,24 @@ impl CycleHeads { &mut self, head_index: StackDepth, path_from_entry: impl Into<PathsToNested> + Copy, - usage_kind: UsageKind, + usages: HeadUsages, ) { match self.heads.entry(head_index) { btree_map::Entry::Vacant(entry) => { - entry.insert(CycleHead { paths_to_head: path_from_entry.into(), usage_kind }); + entry.insert(CycleHead { paths_to_head: path_from_entry.into(), usages }); } btree_map::Entry::Occupied(entry) => { let head = entry.into_mut(); head.paths_to_head |= path_from_entry.into(); - head.usage_kind = head.usage_kind.merge(usage_kind); + head.usages.add_usages_from_nested(usages); } } } + fn ignore_usages(&mut self, head_index: StackDepth, usages: HeadUsages) { + self.heads.get_mut(&head_index).unwrap().usages.ignore_usages(usages) + } + fn iter(&self) -> impl Iterator<Item = (StackDepth, CycleHead)> + '_ { self.heads.iter().map(|(k, v)| (*k, *v)) } @@ -546,17 +608,22 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { parent.encountered_overflow |= encountered_overflow; for (head_index, head) in heads { + if let Some(candidate_usages) = &mut parent.candidate_usages { + candidate_usages + .usages + .get_or_insert_default() + .entry(head_index) + .or_default() + .add_usages_from_nested(head.usages); + } match head_index.cmp(&parent_index) { Ordering::Less => parent.heads.insert( head_index, head.paths_to_head.extend_with(step_kind_from_parent), - head.usage_kind, + head.usages, ), Ordering::Equal => { - let usage_kind = parent - .has_been_used - .map_or(head.usage_kind, |prev| prev.merge(head.usage_kind)); - parent.has_been_used = Some(usage_kind); + parent.usages.get_or_insert_default().add_usages_from_nested(head.usages); } Ordering::Greater => unreachable!(), } @@ -613,6 +680,29 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { stack.cycle_step_kinds(head).fold(step_kind_to_head, |curr, step| curr.extend(step)) } + pub fn enter_single_candidate(&mut self) { + let prev = self.stack.last_mut().unwrap().candidate_usages.replace(Default::default()); + debug_assert!(prev.is_none(), "existing candidate_usages: {prev:?}"); + } + + pub fn finish_single_candidate(&mut self) -> CandidateHeadUsages { + self.stack.last_mut().unwrap().candidate_usages.take().unwrap() + } + + pub fn ignore_candidate_head_usages(&mut self, usages: CandidateHeadUsages) { + if let Some(usages) = usages.usages { + let (entry_index, entry) = self.stack.last_mut_with_index().unwrap(); + #[allow(rustc::potential_query_instability)] + for (head_index, usages) in usages.into_iter() { + if head_index == entry_index { + entry.usages.unwrap().ignore_usages(usages); + } else { + entry.heads.ignore_usages(head_index, usages); + } + } + } + } + /// Probably the most involved method of the whole solver. /// /// While goals get computed via `D::compute_goal`, this function handles @@ -681,7 +771,8 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { required_depth: 0, heads: Default::default(), encountered_overflow: false, - has_been_used: None, + usages: None, + candidate_usages: None, nested_goals: Default::default(), }); @@ -729,7 +820,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { let path_from_head = Self::cycle_path_kind( &self.stack, step_kind_from_parent, - heads.highest_cycle_head(), + heads.highest_cycle_head_index(), ); let provisional_cache_entry = ProvisionalCacheEntry { encountered_overflow, heads, path_from_head, result }; @@ -767,11 +858,17 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { /// When reevaluating a goal with a changed provisional result, all provisional cache entry /// which depend on this goal get invalidated. - fn clear_dependent_provisional_results(&mut self) { - let head = self.stack.next_index(); + /// + /// Note that we keep provisional cache entries which accessed this goal as a cycle head, but + /// don't depend on its value. + fn clear_dependent_provisional_results_for_rerun(&mut self) { + let rerun_index = self.stack.next_index(); #[allow(rustc::potential_query_instability)] self.provisional_cache.retain(|_, entries| { - entries.retain(|entry| entry.heads.highest_cycle_head() != head); + entries.retain(|entry| { + let (head_index, head) = entry.heads.highest_cycle_head(); + head_index != rerun_index || head.usages.is_empty() + }); !entries.is_empty() }); } @@ -808,50 +905,65 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { path_from_head, result, } = entry; - let popped_head = if heads.highest_cycle_head() == popped_head_index { + let popped_head = if heads.highest_cycle_head_index() == popped_head_index { heads.remove_highest_cycle_head() } else { return true; }; // We're rebasing an entry `e` over a head `p`. This head - // has a number of own heads `h` it depends on. We need to - // make sure that the path kind of all paths `hph` remain the - // same after rebasing. + // has a number of own heads `h` it depends on. // - // After rebasing the cycles `hph` will go through `e`. We need to make - // sure that forall possible paths `hep`, `heph` is equal to `hph.` - let ep = popped_head.paths_to_head; - for (head_index, head) in stack_entry.heads.iter() { - let ph = head.paths_to_head; - let hp = Self::cycle_path_kind( - &self.stack, - stack_entry.step_kind_from_parent, - head_index, - ); - - // We first validate that all cycles while computing `p` would stay - // the same if we were to recompute it as a nested goal of `e`. - let he = hp.extend(*path_from_head); - for ph in ph.iter_paths() { - let hph = hp.extend(ph); - for ep in ep.iter_paths() { - let hep = ep.extend(he); - let heph = hep.extend(ph); - if hph != heph { - return false; + // This causes our provisional result to depend on the heads + // of `p` to avoid moving any goal which uses this cache entry to + // the global cache. + if popped_head.usages.is_empty() { + // The result of `e` does not depend on the value of `p`. This we can + // keep using the result of this provisional cache entry even if evaluating + // `p` as a nested goal of `e` would have a different result. + for (head_index, _) in stack_entry.heads.iter() { + heads.insert(head_index, PathsToNested::EMPTY, HeadUsages::default()); + } + } else { + // The entry `e` actually depends on the value of `p`. We need + // to make sure that the value of `p` wouldn't change even if we + // were to reevaluate it as a nested goal of `e` instead. For this + // we check that the path kind of all paths `hph` remain the + // same after rebasing. + // + // After rebasing the cycles `hph` will go through `e`. We need to make + // sure that forall possible paths `hep`, `heph` is equal to `hph.` + let ep = popped_head.paths_to_head; + for (head_index, head) in stack_entry.heads.iter() { + let ph = head.paths_to_head; + let hp = Self::cycle_path_kind( + &self.stack, + stack_entry.step_kind_from_parent, + head_index, + ); + // We first validate that all cycles while computing `p` would stay + // the same if we were to recompute it as a nested goal of `e`. + let he = hp.extend(*path_from_head); + for ph in ph.iter_paths() { + let hph = hp.extend(ph); + for ep in ep.iter_paths() { + let hep = ep.extend(he); + let heph = hep.extend(ph); + if hph != heph { + return false; + } } } - } - // If so, all paths reached while computing `p` have to get added - // the heads of `e` to make sure that rebasing `e` again also considers - // them. - let eph = ep.extend_with_paths(ph); - heads.insert(head_index, eph, head.usage_kind); + // If so, all paths reached while computing `p` have to get added + // the heads of `e` to make sure that rebasing `e` again also considers + // them. + let eph = ep.extend_with_paths(ph); + heads.insert(head_index, eph, head.usages); + } } - let Some(head) = heads.opt_highest_cycle_head() else { + let Some(head_index) = heads.opt_highest_cycle_head_index() else { return false; }; @@ -860,7 +972,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { *path_from_head = path_from_head.extend(Self::cycle_path_kind( &self.stack, stack_entry.step_kind_from_parent, - head, + head_index, )); // Mutate the result of the provisional cache entry in case we did // not reach a fixpoint. @@ -884,7 +996,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { for &ProvisionalCacheEntry { encountered_overflow, ref heads, path_from_head, result } in entries { - let head = heads.highest_cycle_head(); + let head_index = heads.highest_cycle_head_index(); if encountered_overflow { // This check is overly strict and very subtle. We need to make sure that if // a global cache entry depends on some goal without adding it to its @@ -896,14 +1008,17 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { // current goal is already part of the same cycle. This check could be // improved but seems to be good enough for now. let last = self.stack.last().unwrap(); - if last.heads.opt_lowest_cycle_head().is_none_or(|lowest| lowest > head) { + if last.heads.opt_lowest_cycle_head_index().is_none_or(|lowest| lowest > head_index) + { continue; } } // A provisional cache entry is only valid if the current path from its // highest cycle head to the goal is the same. - if path_from_head == Self::cycle_path_kind(&self.stack, step_kind_from_parent, head) { + if path_from_head + == Self::cycle_path_kind(&self.stack, step_kind_from_parent, head_index) + { Self::update_parent_goal( &mut self.stack, step_kind_from_parent, @@ -912,7 +1027,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { encountered_overflow, UpdateParentGoalCtxt::ProvisionalCacheHit, ); - debug!(?head, ?path_from_head, "provisional cache hit"); + debug!(?head_index, ?path_from_head, "provisional cache hit"); return Some(result); } } @@ -970,8 +1085,9 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { // // We check if any of the paths taken while computing the global goal // would end up with an applicable provisional cache entry. - let head = heads.highest_cycle_head(); - let head_to_curr = Self::cycle_path_kind(&self.stack, step_kind_from_parent, head); + let head_index = heads.highest_cycle_head_index(); + let head_to_curr = + Self::cycle_path_kind(&self.stack, step_kind_from_parent, head_index); let full_paths = path_from_global_entry.extend_with(head_to_curr); if full_paths.contains(head_to_provisional.into()) { debug!( @@ -1053,10 +1169,9 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { // in case we're in the first fixpoint iteration for this goal. let path_kind = Self::cycle_path_kind(&self.stack, step_kind_from_parent, head_index); debug!(?path_kind, "encountered cycle with depth {head_index:?}"); - let head = CycleHead { - paths_to_head: step_kind_from_parent.into(), - usage_kind: UsageKind::Single(path_kind), - }; + let mut usages = HeadUsages::default(); + usages.add_usage(path_kind); + let head = CycleHead { paths_to_head: step_kind_from_parent.into(), usages }; Self::update_parent_goal( &mut self.stack, step_kind_from_parent, @@ -1080,15 +1195,31 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { &mut self, cx: X, stack_entry: &StackEntry<X>, - usage_kind: UsageKind, + usages: HeadUsages, result: X::Result, ) -> bool { - if let Some(prev) = stack_entry.provisional_result { - prev == result - } else if let UsageKind::Single(kind) = usage_kind { - D::is_initial_provisional_result(cx, kind, stack_entry.input, result) + let provisional_result = stack_entry.provisional_result; + if usages.is_empty() { + true + } else if let Some(provisional_result) = provisional_result { + provisional_result == result } else { - false + let check = |k| D::is_initial_provisional_result(cx, k, stack_entry.input, result); + match usages { + HeadUsages { inductive: _, unknown: 0, coinductive: 0, forced_ambiguity: 0 } => { + check(PathKind::Inductive) + } + HeadUsages { inductive: 0, unknown: _, coinductive: 0, forced_ambiguity: 0 } => { + check(PathKind::Unknown) + } + HeadUsages { inductive: 0, unknown: 0, coinductive: _, forced_ambiguity: 0 } => { + check(PathKind::Coinductive) + } + HeadUsages { inductive: 0, unknown: 0, coinductive: 0, forced_ambiguity: _ } => { + check(PathKind::ForcedAmbiguity) + } + _ => false, + } } } @@ -1118,7 +1249,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { // If the current goal is not the root of a cycle, we are done. // // There are no provisional cache entries which depend on this goal. - let Some(usage_kind) = stack_entry.has_been_used else { + let Some(usages) = stack_entry.usages else { return EvaluationResult::finalize(stack_entry, encountered_overflow, result); }; @@ -1133,7 +1264,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { // is equal to the provisional result of the previous iteration, or because // this was only the root of either coinductive or inductive cycles, and the // final result is equal to the initial response for that case. - if self.reached_fixpoint(cx, &stack_entry, usage_kind, result) { + if self.reached_fixpoint(cx, &stack_entry, usages, result) { self.rebase_provisional_cache_entries(&stack_entry, |_, result| result); return EvaluationResult::finalize(stack_entry, encountered_overflow, result); } @@ -1171,7 +1302,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { // Clear all provisional cache entries which depend on a previous provisional // result of this goal and rerun. - self.clear_dependent_provisional_results(); + self.clear_dependent_provisional_results_for_rerun(); debug!(?result, "fixpoint changed provisional results"); self.stack.push(StackEntry { @@ -1190,7 +1321,8 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { // similar to the previous iterations when reevaluating, it's better // for caching if the reevaluation also starts out with `false`. encountered_overflow: false, - has_been_used: None, + usages: None, + candidate_usages: None, }); } } diff --git a/compiler/rustc_type_ir/src/search_graph/stack.rs b/compiler/rustc_type_ir/src/search_graph/stack.rs index ea99dc6e7fd..3fd8e2bd16e 100644 --- a/compiler/rustc_type_ir/src/search_graph/stack.rs +++ b/compiler/rustc_type_ir/src/search_graph/stack.rs @@ -3,7 +3,9 @@ use std::ops::Index; use derive_where::derive_where; use rustc_index::IndexVec; -use crate::search_graph::{AvailableDepth, Cx, CycleHeads, NestedGoals, PathKind, UsageKind}; +use crate::search_graph::{ + AvailableDepth, CandidateHeadUsages, Cx, CycleHeads, HeadUsages, NestedGoals, PathKind, +}; rustc_index::newtype_index! { #[orderable] @@ -26,13 +28,13 @@ pub(super) struct StackEntry<X: Cx> { /// The available depth of a given goal, immutable. pub available_depth: AvailableDepth, + /// The maximum depth required while evaluating this goal. + pub required_depth: usize, + /// Starts out as `None` and gets set when rerunning this /// goal in case we encounter a cycle. pub provisional_result: Option<X::Result>, - /// The maximum depth required while evaluating this goal. - pub required_depth: usize, - /// All cycle heads this goal depends on. Lazily updated and only /// up-to date for the top of the stack. pub heads: CycleHeads, @@ -40,9 +42,16 @@ pub(super) struct StackEntry<X: Cx> { /// Whether evaluating this goal encountered overflow. Lazily updated. pub encountered_overflow: bool, - /// Whether this goal has been used as the root of a cycle. This gets - /// eagerly updated when encountering a cycle. - pub has_been_used: Option<UsageKind>, + /// Whether and how this goal has been used as the root of a cycle. Lazily updated. + pub usages: Option<HeadUsages>, + + /// We want to be able to ignore head usages if they happen inside of candidates + /// which don't impact the result of a goal. This enables us to avoid rerunning goals + /// and is also used when rebasing provisional cache entries. + /// + /// To implement this, we track all usages while evaluating a candidate. If this candidate + /// then ends up ignored, we manually remove its usages from `usages` and `heads`. + pub candidate_usages: Option<CandidateHeadUsages>, /// The nested goals of this goal, see the doc comment of the type. pub nested_goals: NestedGoals<X>, diff --git a/library/Cargo.lock b/library/Cargo.lock index 09228825086..4b06f14c7ee 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -120,9 +120,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.4" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "rustc-std-workspace-alloc", "rustc-std-workspace-core", diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 3db37f1d16f..fa12d379c8c 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -1672,7 +1672,7 @@ unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for Box<T, A> { #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] impl<T: Default> Default for Box<T> { - /// Creates a `Box<T>`, with the `Default` value for T. + /// Creates a `Box<T>`, with the `Default` value for `T`. #[inline] fn default() -> Self { let mut x: Box<mem::MaybeUninit<T>> = Box::new_uninit(); @@ -1695,6 +1695,7 @@ impl<T: Default> Default for Box<T> { #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] impl<T> Default for Box<[T]> { + /// Creates an empty `[T]` inside a `Box`. #[inline] fn default() -> Self { let ptr: Unique<[T]> = Unique::<[T; 0]>::dangling(); @@ -1717,6 +1718,19 @@ impl Default for Box<str> { } #[cfg(not(no_global_oom_handling))] +#[stable(feature = "pin_default_impls", since = "CURRENT_RUSTC_VERSION")] +impl<T> Default for Pin<Box<T>> +where + T: ?Sized, + Box<T>: Default, +{ + #[inline] + fn default() -> Self { + Box::into_pin(Box::<T>::default()) + } +} + +#[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] impl<T: Clone, A: Allocator + Clone> Clone for Box<T, A> { /// Returns a new box with a `clone()` of this box's contents. diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 5018ff4ad71..529b583cdd2 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -2357,7 +2357,7 @@ impl<T: Default> Default for Rc<T> { /// assert_eq!(*x, 0); /// ``` #[inline] - fn default() -> Rc<T> { + fn default() -> Self { unsafe { Self::from_inner( Box::leak(Box::write( @@ -2373,7 +2373,7 @@ impl<T: Default> Default for Rc<T> { #[cfg(not(no_global_oom_handling))] #[stable(feature = "more_rc_default_impls", since = "1.80.0")] impl Default for Rc<str> { - /// Creates an empty str inside an Rc + /// Creates an empty `str` inside an `Rc`. /// /// This may or may not share an allocation with other Rcs on the same thread. #[inline] @@ -2387,7 +2387,7 @@ impl Default for Rc<str> { #[cfg(not(no_global_oom_handling))] #[stable(feature = "more_rc_default_impls", since = "1.80.0")] impl<T> Default for Rc<[T]> { - /// Creates an empty `[T]` inside an Rc + /// Creates an empty `[T]` inside an `Rc`. /// /// This may or may not share an allocation with other Rcs on the same thread. #[inline] @@ -2397,6 +2397,19 @@ impl<T> Default for Rc<[T]> { } } +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "pin_default_impls", since = "CURRENT_RUSTC_VERSION")] +impl<T> Default for Pin<Rc<T>> +where + T: ?Sized, + Rc<T>: Default, +{ + #[inline] + fn default() -> Self { + unsafe { Pin::new_unchecked(Rc::<T>::default()) } + } +} + #[stable(feature = "rust1", since = "1.0.0")] trait RcEqIdent<T: ?Sized + PartialEq, A: Allocator> { fn eq(&self, other: &Rc<T, A>) -> bool; diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index b8925f4544f..29caa7bc539 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -3654,6 +3654,19 @@ impl<T> Default for Arc<[T]> { } } +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "pin_default_impls", since = "CURRENT_RUSTC_VERSION")] +impl<T> Default for Pin<Arc<T>> +where + T: ?Sized, + Arc<T>: Default, +{ + #[inline] + fn default() -> Self { + unsafe { Pin::new_unchecked(Arc::<T>::default()) } + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl<T: ?Sized + Hash, A: Allocator> Hash for Arc<T, A> { fn hash<H: Hasher>(&self, state: &mut H) { diff --git a/library/core/src/any.rs b/library/core/src/any.rs index ceb9748e7fe..e7d9763d46e 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -835,9 +835,9 @@ impl fmt::Debug for TypeId { /// /// The returned string must not be considered to be a unique identifier of a /// type as multiple types may map to the same type name. Similarly, there is no -/// guarantee that all parts of a type will appear in the returned string: for -/// example, lifetime specifiers are currently not included. In addition, the -/// output may change between versions of the compiler. +/// guarantee that all parts of a type will appear in the returned string. In +/// addition, the output may change between versions of the compiler. For +/// example, lifetime specifiers were omitted in some earlier versions. /// /// The current implementation uses the same infrastructure as compiler /// diagnostics and debuginfo, but this is not guaranteed. diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index c639d50cc3d..59a6aa70620 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -698,14 +698,14 @@ impl<T, const N: usize> Cell<[T; N]> { /// # Examples /// /// ``` - /// #![feature(as_array_of_cells)] /// use std::cell::Cell; /// /// let mut array: [i32; 3] = [1, 2, 3]; /// let cell_array: &Cell<[i32; 3]> = Cell::from_mut(&mut array); /// let array_cell: &[Cell<i32>; 3] = cell_array.as_array_of_cells(); /// ``` - #[unstable(feature = "as_array_of_cells", issue = "88248")] + #[stable(feature = "as_array_of_cells", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "as_array_of_cells", since = "CURRENT_RUSTC_VERSION")] pub const fn as_array_of_cells(&self) -> &[Cell<T>; N] { // SAFETY: `Cell<T>` has the same memory layout as `T`. unsafe { &*(self as *const Cell<[T; N]> as *const [Cell<T>; N]) } @@ -1573,6 +1573,47 @@ impl<'b, T: ?Sized> Ref<'b, T> { } } + /// Tries to makes a new `Ref` for a component of the borrowed data. + /// On failure, the original guard is returned alongside with the error + /// returned by the closure. + /// + /// The `RefCell` is already immutably borrowed, so this cannot fail. + /// + /// This is an associated function that needs to be used as + /// `Ref::try_map(...)`. A method would interfere with methods of the same + /// name on the contents of a `RefCell` used through `Deref`. + /// + /// # Examples + /// + /// ``` + /// #![feature(refcell_try_map)] + /// use std::cell::{RefCell, Ref}; + /// use std::str::{from_utf8, Utf8Error}; + /// + /// let c = RefCell::new(vec![0xF0, 0x9F, 0xA6 ,0x80]); + /// let b1: Ref<'_, Vec<u8>> = c.borrow(); + /// let b2: Result<Ref<'_, str>, _> = Ref::try_map(b1, |v| from_utf8(v)); + /// assert_eq!(&*b2.unwrap(), "🦀"); + /// + /// let c = RefCell::new(vec![0xF0, 0x9F, 0xA6]); + /// let b1: Ref<'_, Vec<u8>> = c.borrow(); + /// let b2: Result<_, (Ref<'_, Vec<u8>>, Utf8Error)> = Ref::try_map(b1, |v| from_utf8(v)); + /// let (b3, e) = b2.unwrap_err(); + /// assert_eq!(*b3, vec![0xF0, 0x9F, 0xA6]); + /// assert_eq!(e.valid_up_to(), 0); + /// ``` + #[unstable(feature = "refcell_try_map", issue = "143801")] + #[inline] + pub fn try_map<U: ?Sized, E>( + orig: Ref<'b, T>, + f: impl FnOnce(&T) -> Result<&U, E>, + ) -> Result<Ref<'b, U>, (Self, E)> { + match f(&*orig) { + Ok(value) => Ok(Ref { value: NonNull::from(value), borrow: orig.borrow }), + Err(e) => Err((orig, e)), + } + } + /// Splits a `Ref` into multiple `Ref`s for different components of the /// borrowed data. /// @@ -1734,6 +1775,58 @@ impl<'b, T: ?Sized> RefMut<'b, T> { } } + /// Tries to makes a new `RefMut` for a component of the borrowed data. + /// On failure, the original guard is returned alongside with the error + /// returned by the closure. + /// + /// The `RefCell` is already mutably borrowed, so this cannot fail. + /// + /// This is an associated function that needs to be used as + /// `RefMut::try_map(...)`. A method would interfere with methods of the same + /// name on the contents of a `RefCell` used through `Deref`. + /// + /// # Examples + /// + /// ``` + /// #![feature(refcell_try_map)] + /// use std::cell::{RefCell, RefMut}; + /// use std::str::{from_utf8_mut, Utf8Error}; + /// + /// let c = RefCell::new(vec![0x68, 0x65, 0x6C, 0x6C, 0x6F]); + /// { + /// let b1: RefMut<'_, Vec<u8>> = c.borrow_mut(); + /// let b2: Result<RefMut<'_, str>, _> = RefMut::try_map(b1, |v| from_utf8_mut(v)); + /// let mut b2 = b2.unwrap(); + /// assert_eq!(&*b2, "hello"); + /// b2.make_ascii_uppercase(); + /// } + /// assert_eq!(*c.borrow(), "HELLO".as_bytes()); + /// + /// let c = RefCell::new(vec![0xFF]); + /// let b1: RefMut<'_, Vec<u8>> = c.borrow_mut(); + /// let b2: Result<_, (RefMut<'_, Vec<u8>>, Utf8Error)> = RefMut::try_map(b1, |v| from_utf8_mut(v)); + /// let (b3, e) = b2.unwrap_err(); + /// assert_eq!(*b3, vec![0xFF]); + /// assert_eq!(e.valid_up_to(), 0); + /// ``` + #[unstable(feature = "refcell_try_map", issue = "143801")] + #[inline] + pub fn try_map<U: ?Sized, E>( + mut orig: RefMut<'b, T>, + f: impl FnOnce(&mut T) -> Result<&mut U, E>, + ) -> Result<RefMut<'b, U>, (Self, E)> { + // SAFETY: function holds onto an exclusive reference for the duration + // of its call through `orig`, and the pointer is only de-referenced + // inside of the function call never allowing the exclusive reference to + // escape. + match f(&mut *orig) { + Ok(value) => { + Ok(RefMut { value: NonNull::from(value), borrow: orig.borrow, marker: PhantomData }) + } + Err(e) => Err((orig, e)), + } + } + /// Splits a `RefMut` into multiple `RefMut`s for different components of the /// borrowed data. /// diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 7228ad0ed6d..dd838d494bc 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -3157,6 +3157,44 @@ pub const unsafe fn copysignf64(x: f64, y: f64) -> f64; #[rustc_intrinsic] pub const unsafe fn copysignf128(x: f128, y: f128) -> f128; +/// Generates the LLVM body for the automatic differentiation of `f` using Enzyme, +/// with `df` as the derivative function and `args` as its arguments. +/// +/// Used internally as the body of `df` when expanding the `#[autodiff_forward]` +/// and `#[autodiff_reverse]` attribute macros. +/// +/// Type Parameters: +/// - `F`: The original function to differentiate. Must be a function item. +/// - `G`: The derivative function. Must be a function item. +/// - `T`: A tuple of arguments passed to `df`. +/// - `R`: The return type of the derivative function. +/// +/// This shows where the `autodiff` intrinsic is used during macro expansion: +/// +/// ```rust,ignore (macro example) +/// #[autodiff_forward(df1, Dual, Const, Dual)] +/// pub fn f1(x: &[f64], y: f64) -> f64 { +/// unimplemented!() +/// } +/// ``` +/// +/// expands to: +/// +/// ```rust,ignore (macro example) +/// #[rustc_autodiff] +/// #[inline(never)] +/// pub fn f1(x: &[f64], y: f64) -> f64 { +/// ::core::panicking::panic("not implemented") +/// } +/// #[rustc_autodiff(Forward, 1, Dual, Const, Dual)] +/// pub fn df1(x: &[f64], bx_0: &[f64], y: f64) -> (f64, f64) { +/// ::core::intrinsics::autodiff(f1::<>, df1::<>, (x, bx_0, y)) +/// } +/// ``` +#[rustc_nounwind] +#[rustc_intrinsic] +pub const fn autodiff<F, G, T: crate::marker::Tuple, R>(f: F, df: G, args: T) -> R; + /// Inform Miri that a given pointer definitely has a certain alignment. #[cfg(miri)] #[rustc_allow_const_fn_unstable(const_eval_select)] diff --git a/library/core/src/iter/adapters/chain.rs b/library/core/src/iter/adapters/chain.rs index dad3d79acb1..943b88e2330 100644 --- a/library/core/src/iter/adapters/chain.rs +++ b/library/core/src/iter/adapters/chain.rs @@ -45,8 +45,6 @@ impl<A, B> Chain<A, B> { /// # Examples /// /// ``` -/// #![feature(iter_chain)] -/// /// use std::iter::chain; /// /// let a = [1, 2, 3]; @@ -62,7 +60,7 @@ impl<A, B> Chain<A, B> { /// assert_eq!(iter.next(), Some(6)); /// assert_eq!(iter.next(), None); /// ``` -#[unstable(feature = "iter_chain", reason = "recently added", issue = "125964")] +#[stable(feature = "iter_chain", since = "CURRENT_RUSTC_VERSION")] pub fn chain<A, B>(a: A, b: B) -> Chain<A::IntoIter, B::IntoIter> where A: IntoIterator, diff --git a/library/core/src/iter/adapters/mod.rs b/library/core/src/iter/adapters/mod.rs index 2a0ef0189d1..6c6de0a4e5c 100644 --- a/library/core/src/iter/adapters/mod.rs +++ b/library/core/src/iter/adapters/mod.rs @@ -32,7 +32,7 @@ mod zip; pub use self::array_chunks::ArrayChunks; #[unstable(feature = "std_internals", issue = "none")] pub use self::by_ref_sized::ByRefSized; -#[unstable(feature = "iter_chain", reason = "recently added", issue = "125964")] +#[stable(feature = "iter_chain", since = "CURRENT_RUSTC_VERSION")] pub use self::chain::chain; #[stable(feature = "iter_cloned", since = "1.1.0")] pub use self::cloned::Cloned; diff --git a/library/core/src/iter/mod.rs b/library/core/src/iter/mod.rs index 56ca1305b60..bc07324f520 100644 --- a/library/core/src/iter/mod.rs +++ b/library/core/src/iter/mod.rs @@ -404,7 +404,7 @@ pub use self::adapters::StepBy; pub use self::adapters::TrustedRandomAccess; #[unstable(feature = "trusted_random_access", issue = "none")] pub use self::adapters::TrustedRandomAccessNoCoerce; -#[unstable(feature = "iter_chain", reason = "recently added", issue = "125964")] +#[stable(feature = "iter_chain", since = "CURRENT_RUSTC_VERSION")] pub use self::adapters::chain; pub(crate) use self::adapters::try_process; #[stable(feature = "iter_zip", since = "1.59.0")] diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index a6825b4be68..6396b8524f2 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -208,6 +208,10 @@ #[allow(unused_extern_crates)] extern crate self as core; +/* The core prelude, not as all-encompassing as the std prelude */ +// The compiler expects the prelude definition to be defined before it's use statement. +pub mod prelude; + #[prelude_import] #[allow(unused)] use prelude::rust_2024::*; @@ -293,10 +297,6 @@ pub mod f64; #[macro_use] pub mod num; -/* The core prelude, not as all-encompassing as the std prelude */ - -pub mod prelude; - /* Core modules for ownership management */ pub mod hint; diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index db8b527d593..ccf41dfb01d 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -1495,6 +1495,7 @@ pub(crate) mod builtin { /// (or explicitly returns `-> ()`). Otherwise, it must be set to one of the allowed activities. #[unstable(feature = "autodiff", issue = "124509")] #[allow_internal_unstable(rustc_attrs)] + #[allow_internal_unstable(core_intrinsics)] #[rustc_builtin_macro] pub macro autodiff_forward($item:item) { /* compiler built-in */ @@ -1513,6 +1514,7 @@ pub(crate) mod builtin { /// (or explicitly returns `-> ()`). Otherwise, it must be set to one of the allowed activities. #[unstable(feature = "autodiff", issue = "124509")] #[allow_internal_unstable(rustc_attrs)] + #[allow_internal_unstable(core_intrinsics)] #[rustc_builtin_macro] pub macro autodiff_reverse($item:item) { /* compiler built-in */ @@ -1768,4 +1770,15 @@ pub(crate) mod builtin { pub macro deref($pat:pat) { builtin # deref($pat) } + + /// Derive macro generating an impl of the trait `From`. + /// Currently, it can only be used on single-field structs. + // Note that the macro is in a different module than the `From` trait, + // to avoid triggering an unstable feature being used if someone imports + // `std::convert::From`. + #[rustc_builtin_macro] + #[unstable(feature = "derive_from", issue = "144889")] + pub macro From($item: item) { + /* compiler built-in */ + } } diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 9284a566208..186c6f32cff 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -1094,23 +1094,17 @@ macro_rules! uint_impl { self / rhs } - /// Checked integer division without remainder. Computes `self / rhs`. - /// - /// # Panics - /// - /// This function will panic if `rhs == 0` or `self % rhs != 0`. + /// Checked integer division without remainder. Computes `self / rhs`, + /// returning `None` if `rhs == 0` or if `self % rhs != 0`. /// /// # Examples /// /// ``` /// #![feature(exact_div)] - #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".exact_div(2), 32);")] - #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".exact_div(32), 2);")] - /// ``` - /// - /// ```should_panic - /// #![feature(exact_div)] - #[doc = concat!("let _ = 65", stringify!($SelfT), ".exact_div(2);")] + #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".checked_exact_div(2), Some(32));")] + #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".checked_exact_div(32), Some(2));")] + #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".checked_exact_div(0), None);")] + #[doc = concat!("assert_eq!(65", stringify!($SelfT), ".checked_exact_div(2), None);")] /// ``` #[unstable( feature = "exact_div", diff --git a/library/core/src/panic.rs b/library/core/src/panic.rs index 5fa340a6147..7a42b3d0fc7 100644 --- a/library/core/src/panic.rs +++ b/library/core/src/panic.rs @@ -48,7 +48,6 @@ pub macro panic_2015 { #[allow_internal_unstable(panic_internals, const_format_args)] #[rustc_diagnostic_item = "core_panic_2021_macro"] #[rustc_macro_transparency = "semitransparent"] -#[cfg(feature = "panic_immediate_abort")] pub macro panic_2021 { () => ( $crate::panicking::panic("explicit panic") @@ -65,50 +64,6 @@ pub macro panic_2021 { } #[doc(hidden)] -#[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")] -#[allow_internal_unstable( - panic_internals, - core_intrinsics, - const_dispatch, - const_eval_select, - const_format_args, - rustc_attrs -)] -#[rustc_diagnostic_item = "core_panic_2021_macro"] -#[rustc_macro_transparency = "semitransparent"] -#[cfg(not(feature = "panic_immediate_abort"))] -pub macro panic_2021 { - () => ({ - // Create a function so that the argument for `track_caller` - // can be moved inside if possible. - #[cold] - #[track_caller] - #[inline(never)] - const fn panic_cold_explicit() -> ! { - $crate::panicking::panic_explicit() - } - panic_cold_explicit(); - }), - // Special-case the single-argument case for const_panic. - ("{}", $arg:expr $(,)?) => ({ - #[cold] - #[track_caller] - #[inline(never)] - #[rustc_const_panic_str] // enforce a &&str argument in const-check and hook this by const-eval - #[rustc_do_not_const_check] // hooked by const-eval - const fn panic_cold_display<T: $crate::fmt::Display>(arg: &T) -> ! { - $crate::panicking::panic_display(arg) - } - panic_cold_display(&$arg); - }), - ($($t:tt)+) => ({ - // Semicolon to prevent temporaries inside the formatting machinery from - // being considered alive in the caller after the panic_fmt call. - $crate::panicking::panic_fmt($crate::const_format_args!($($t)+)); - }), -} - -#[doc(hidden)] #[unstable(feature = "edition_panic", issue = "none", reason = "use unreachable!() instead")] #[allow_internal_unstable(panic_internals)] #[rustc_diagnostic_item = "unreachable_2015_macro"] diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index 812bc5e6145..804a12ee477 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -233,14 +233,6 @@ pub fn panic_nounwind_nobacktrace(expr: &'static str) -> ! { panic_nounwind_fmt(fmt::Arguments::new_const(&[expr]), /* force_no_backtrace */ true); } -#[track_caller] -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] -#[cfg_attr(feature = "panic_immediate_abort", inline)] -#[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable -pub const fn panic_explicit() -> ! { - panic_display(&"explicit panic"); -} - #[inline] #[track_caller] #[rustc_diagnostic_item = "unreachable_display"] // needed for `non-fmt-panics` lint @@ -260,9 +252,8 @@ pub const fn panic_str_2015(expr: &str) -> ! { #[inline] #[track_caller] +#[lang = "panic_display"] // needed for const-evaluated panics #[rustc_do_not_const_check] // hooked by const-eval -// enforce a &&str argument in const-check and hook this by const-eval -#[rustc_const_panic_str] #[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable pub const fn panic_display<T: fmt::Display>(x: &T) -> ! { panic_fmt(format_args!("{}", *x)); diff --git a/library/core/src/prelude/v1.rs b/library/core/src/prelude/v1.rs index a4be66b90ca..d8d82afb0e6 100644 --- a/library/core/src/prelude/v1.rs +++ b/library/core/src/prelude/v1.rs @@ -117,3 +117,10 @@ pub use crate::macros::builtin::deref; reason = "`type_alias_impl_trait` has open design concerns" )] pub use crate::macros::builtin::define_opaque; + +#[unstable( + feature = "derive_from", + issue = "144889", + reason = "`derive(From)` is unstable" +)] +pub use crate::macros::builtin::From; diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index b2607e45324..f5c490ca7ce 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -974,7 +974,7 @@ pub const fn dangling_mut<T>() -> *mut T { #[must_use] #[inline(always)] #[stable(feature = "exposed_provenance", since = "1.84.0")] -#[rustc_const_unstable(feature = "const_exposed_provenance", issue = "144538")] +#[rustc_const_stable(feature = "const_exposed_provenance", since = "CURRENT_RUSTC_VERSION")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[allow(fuzzy_provenance_casts)] // this *is* the explicit provenance API one should use instead pub const fn with_exposed_provenance<T>(addr: usize) -> *const T { @@ -1015,7 +1015,7 @@ pub const fn with_exposed_provenance<T>(addr: usize) -> *const T { #[must_use] #[inline(always)] #[stable(feature = "exposed_provenance", since = "1.84.0")] -#[rustc_const_unstable(feature = "const_exposed_provenance", issue = "144538")] +#[rustc_const_stable(feature = "const_exposed_provenance", since = "CURRENT_RUSTC_VERSION")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[allow(fuzzy_provenance_casts)] // this *is* the explicit provenance API one should use instead pub const fn with_exposed_provenance_mut<T>(addr: usize) -> *mut T { @@ -1347,40 +1347,6 @@ pub const unsafe fn swap<T>(x: *mut T, y: *mut T) { /// assert_eq!(x, [7, 8, 3, 4]); /// assert_eq!(y, [1, 2, 9]); /// ``` -/// -/// # Const evaluation limitations -/// -/// If this function is invoked during const-evaluation, the current implementation has a small (and -/// rarely relevant) limitation: if `count` is at least 2 and the data pointed to by `x` or `y` -/// contains a pointer that crosses the boundary of two `T`-sized chunks of memory, the function may -/// fail to evaluate (similar to a panic during const-evaluation). This behavior may change in the -/// future. -/// -/// The limitation is illustrated by the following example: -/// -/// ``` -/// use std::mem::size_of; -/// use std::ptr; -/// -/// const { unsafe { -/// const PTR_SIZE: usize = size_of::<*const i32>(); -/// let mut data1 = [0u8; PTR_SIZE]; -/// let mut data2 = [0u8; PTR_SIZE]; -/// // Store a pointer in `data1`. -/// data1.as_mut_ptr().cast::<*const i32>().write_unaligned(&42); -/// // Swap the contents of `data1` and `data2` by swapping `PTR_SIZE` many `u8`-sized chunks. -/// // This call will fail, because the pointer in `data1` crosses the boundary -/// // between several of the 1-byte chunks that are being swapped here. -/// //ptr::swap_nonoverlapping(data1.as_mut_ptr(), data2.as_mut_ptr(), PTR_SIZE); -/// // Swap the contents of `data1` and `data2` by swapping a single chunk of size -/// // `[u8; PTR_SIZE]`. That works, as there is no pointer crossing the boundary between -/// // two chunks. -/// ptr::swap_nonoverlapping(&mut data1, &mut data2, 1); -/// // Read the pointer from `data2` and dereference it. -/// let ptr = data2.as_ptr().cast::<*const i32>().read_unaligned(); -/// assert!(*ptr == 42); -/// } } -/// ``` #[inline] #[stable(feature = "swap_nonoverlapping", since = "1.27.0")] #[rustc_const_stable(feature = "const_swap_nonoverlapping", since = "1.88.0")] @@ -1409,9 +1375,7 @@ pub const unsafe fn swap_nonoverlapping<T>(x: *mut T, y: *mut T, count: usize) { const_eval_select!( @capture[T] { x: *mut T, y: *mut T, count: usize }: if const { - // At compile-time we want to always copy this in chunks of `T`, to ensure that if there - // are pointers inside `T` we will copy them in one go rather than trying to copy a part - // of a pointer (which would not work). + // At compile-time we don't need all the special code below. // SAFETY: Same preconditions as this function unsafe { swap_nonoverlapping_const(x, y, count) } } else { diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 0c4d49f3c99..ecda8a7fec6 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -61,7 +61,6 @@ #![feature(isolate_most_least_significant_one)] #![feature(iter_advance_by)] #![feature(iter_array_chunks)] -#![feature(iter_chain)] #![feature(iter_collect_into)] #![feature(iter_intersperse)] #![feature(iter_is_partitioned)] diff --git a/library/coretests/tests/ptr.rs b/library/coretests/tests/ptr.rs index 197a14423b5..c13fb96a67f 100644 --- a/library/coretests/tests/ptr.rs +++ b/library/coretests/tests/ptr.rs @@ -936,22 +936,18 @@ fn test_const_swap_ptr() { assert!(*s1.0.ptr == 666); assert!(*s2.0.ptr == 1); - // Swap them back, again as an array. + // Swap them back, byte-for-byte unsafe { ptr::swap_nonoverlapping( - ptr::from_mut(&mut s1).cast::<T>(), - ptr::from_mut(&mut s2).cast::<T>(), - 1, + ptr::from_mut(&mut s1).cast::<u8>(), + ptr::from_mut(&mut s2).cast::<u8>(), + size_of::<A>(), ); } // Make sure they still work. assert!(*s1.0.ptr == 1); assert!(*s2.0.ptr == 666); - - // This is where we'd swap again using a `u8` type and a `count` of `size_of::<T>()` if it - // were not for the limitation of `swap_nonoverlapping` around pointers crossing multiple - // elements. }; } diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 07b38c65898..f111fcb4a47 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -428,11 +428,15 @@ // #![default_lib_allocator] +// The Rust prelude +// The compiler expects the prelude definition to be defined before it's use statement. +pub mod prelude; + // Explicitly import the prelude. The compiler uses this same unstable attribute // to import the prelude implicitly when building crates that depend on std. #[prelude_import] #[allow(unused)] -use prelude::rust_2021::*; +use prelude::rust_2024::*; // Access to Bencher, etc. #[cfg(test)] @@ -483,9 +487,6 @@ mod macros; #[macro_use] pub mod rt; -// The Rust prelude -pub mod prelude; - #[stable(feature = "rust1", since = "1.0.0")] pub use core::any; #[stable(feature = "core_array", since = "1.35.0")] diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index 36e53e7cadc..24b65c11fd2 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -77,7 +77,18 @@ impl Thread { let page_size = os::page_size(); let stack_size = (stack_size + page_size - 1) & (-(page_size as isize - 1) as usize - 1); - assert_eq!(libc::pthread_attr_setstacksize(attr.as_mut_ptr(), stack_size), 0); + + // Some libc implementations, e.g. musl, place an upper bound + // on the stack size, in which case we can only gracefully return + // an error here. + if libc::pthread_attr_setstacksize(attr.as_mut_ptr(), stack_size) != 0 { + assert_eq!(libc::pthread_attr_destroy(attr.as_mut_ptr()), 0); + drop(Box::from_raw(data)); + return Err(io::const_error!( + io::ErrorKind::InvalidInput, + "invalid stack size" + )); + } } }; } diff --git a/library/std/src/sys/pal/windows/c/bindings.txt b/library/std/src/sys/pal/windows/c/bindings.txt index c8e4dca4781..abc1c19827f 100644 --- a/library/std/src/sys/pal/windows/c/bindings.txt +++ b/library/std/src/sys/pal/windows/c/bindings.txt @@ -2158,6 +2158,7 @@ GetCurrentDirectoryW GetCurrentProcess GetCurrentProcessId GetCurrentThread +GetCurrentThreadId GetEnvironmentStringsW GetEnvironmentVariableW GetExitCodeProcess @@ -2185,7 +2186,6 @@ GetSystemInfo GetSystemTimeAsFileTime GetSystemTimePreciseAsFileTime GetTempPathW -GetThreadId GetUserProfileDirectoryW GetWindowsDirectoryW HANDLE diff --git a/library/std/src/sys/pal/windows/c/windows_sys.rs b/library/std/src/sys/pal/windows/c/windows_sys.rs index 45a273d241a..989a1246650 100644 --- a/library/std/src/sys/pal/windows/c/windows_sys.rs +++ b/library/std/src/sys/pal/windows/c/windows_sys.rs @@ -38,6 +38,7 @@ windows_targets::link!("kernel32.dll" "system" fn GetCurrentDirectoryW(nbufferle windows_targets::link!("kernel32.dll" "system" fn GetCurrentProcess() -> HANDLE); windows_targets::link!("kernel32.dll" "system" fn GetCurrentProcessId() -> u32); windows_targets::link!("kernel32.dll" "system" fn GetCurrentThread() -> HANDLE); +windows_targets::link!("kernel32.dll" "system" fn GetCurrentThreadId() -> u32); windows_targets::link!("kernel32.dll" "system" fn GetEnvironmentStringsW() -> PWSTR); windows_targets::link!("kernel32.dll" "system" fn GetEnvironmentVariableW(lpname : PCWSTR, lpbuffer : PWSTR, nsize : u32) -> u32); windows_targets::link!("kernel32.dll" "system" fn GetExitCodeProcess(hprocess : HANDLE, lpexitcode : *mut u32) -> BOOL); @@ -61,7 +62,6 @@ windows_targets::link!("kernel32.dll" "system" fn GetSystemInfo(lpsysteminfo : * windows_targets::link!("kernel32.dll" "system" fn GetSystemTimeAsFileTime(lpsystemtimeasfiletime : *mut FILETIME)); windows_targets::link!("kernel32.dll" "system" fn GetSystemTimePreciseAsFileTime(lpsystemtimeasfiletime : *mut FILETIME)); windows_targets::link!("kernel32.dll" "system" fn GetTempPathW(nbufferlength : u32, lpbuffer : PWSTR) -> u32); -windows_targets::link!("kernel32.dll" "system" fn GetThreadId(thread : HANDLE) -> u32); windows_targets::link!("userenv.dll" "system" fn GetUserProfileDirectoryW(htoken : HANDLE, lpprofiledir : PWSTR, lpcchsize : *mut u32) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn GetWindowsDirectoryW(lpbuffer : PWSTR, usize : u32) -> u32); windows_targets::link!("kernel32.dll" "system" fn InitOnceBeginInitialize(lpinitonce : *mut INIT_ONCE, dwflags : u32, fpending : *mut BOOL, lpcontext : *mut *mut core::ffi::c_void) -> BOOL); diff --git a/library/std/src/sys/pal/windows/thread.rs b/library/std/src/sys/pal/windows/thread.rs index c708da5af12..b0e38220a2d 100644 --- a/library/std/src/sys/pal/windows/thread.rs +++ b/library/std/src/sys/pal/windows/thread.rs @@ -129,7 +129,7 @@ impl Thread { pub(crate) fn current_os_id() -> Option<u64> { // SAFETY: FFI call with no preconditions. - let id: u32 = unsafe { c::GetThreadId(c::GetCurrentThread()) }; + let id: u32 = unsafe { c::GetCurrentThreadId() }; // A return value of 0 indicates failed lookup. if id == 0 { None } else { Some(id.into()) } diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index 537f4b6184f..be29e77e572 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -48,7 +48,6 @@ dependencies = [ "clap", "clap_complete", "cmake", - "fd-lock", "home", "ignore", "insta", @@ -269,17 +268,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] -name = "fd-lock" -version = "4.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78" -dependencies = [ - "cfg-if", - "rustix", - "windows-sys 0.59.0", -] - -[[package]] name = "filetime" version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -759,13 +747,12 @@ dependencies = [ [[package]] name = "tar" -version = "0.4.43" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c65998313f8e17d0d553d28f91a0df93e4dbbbf770279c7bc21ca0f09ea1a1f6" +checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" dependencies = [ "filetime", "libc", - "xattr", ] [[package]] @@ -1148,16 +1135,6 @@ dependencies = [ ] [[package]] -name = "xattr" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d65cbf2f12c15564212d48f4e3dfb87923d25d611f2aed18f4cb23f0413d89e" -dependencies = [ - "libc", - "rustix", -] - -[[package]] name = "xz2" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index bdf0b42255e..cd5a60187e5 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -38,7 +38,6 @@ cmake = "=0.1.54" build_helper = { path = "../build_helper" } clap = { version = "4.4", default-features = false, features = ["std", "usage", "help", "derive", "error-context"] } clap_complete = "4.4" -fd-lock = "4.0" home = "0.5" ignore = "0.4" libc = "0.2" @@ -51,7 +50,7 @@ serde = "1.0" serde_derive = "1.0" serde_json = "1.0" sha2 = "0.10" -tar = "0.4" +tar = { version = "0.4.44", default-features = false } termcolor = "1.4" toml = "0.5" walkdir = "2.4" diff --git a/src/bootstrap/src/bin/main.rs b/src/bootstrap/src/bin/main.rs index 8b2d67266a7..93c7faf4f01 100644 --- a/src/bootstrap/src/bin/main.rs +++ b/src/bootstrap/src/bin/main.rs @@ -5,8 +5,8 @@ //! parent directory, and otherwise documentation can be found throughout the `build` //! directory in each respective module. -use std::fs::{self, OpenOptions}; -use std::io::{self, BufRead, BufReader, IsTerminal, Write}; +use std::fs::{self, OpenOptions, TryLockError}; +use std::io::{self, BufRead, BufReader, IsTerminal, Read, Write}; use std::path::Path; use std::str::FromStr; use std::time::Instant; @@ -39,38 +39,34 @@ fn main() { let config = Config::parse(flags); let mut build_lock; - let _build_lock_guard; if !config.bypass_bootstrap_lock { // Display PID of process holding the lock // PID will be stored in a lock file let lock_path = config.out.join("lock"); - let pid = fs::read_to_string(&lock_path); - - build_lock = fd_lock::RwLock::new(t!(fs::OpenOptions::new() + build_lock = t!(fs::OpenOptions::new() + .read(true) .write(true) - .truncate(true) .create(true) - .open(&lock_path))); - _build_lock_guard = match build_lock.try_write() { - Ok(mut lock) => { - t!(lock.write(process::id().to_string().as_ref())); - lock + .truncate(false) + .open(&lock_path)); + t!(build_lock.try_lock().or_else(|e| { + if let TryLockError::Error(e) = e { + return Err(e); } - err => { - drop(err); - // #135972: We can reach this point when the lock has been taken, - // but the locker has not yet written its PID to the file - if let Some(pid) = pid.ok().filter(|pid| !pid.is_empty()) { - println!("WARNING: build directory locked by process {pid}, waiting for lock"); - } else { - println!("WARNING: build directory locked, waiting for lock"); - } - let mut lock = t!(build_lock.write()); - t!(lock.write(process::id().to_string().as_ref())); - lock + let mut pid = String::new(); + t!(build_lock.read_to_string(&mut pid)); + // #135972: We can reach this point when the lock has been taken, + // but the locker has not yet written its PID to the file + if !pid.is_empty() { + println!("WARNING: build directory locked by process {pid}, waiting for lock"); + } else { + println!("WARNING: build directory locked, waiting for lock"); } - }; + build_lock.lock() + })); + t!(build_lock.set_len(0)); + t!(build_lock.write_all(process::id().to_string().as_bytes())); } // check_version warnings are not printed during setup, or during CI diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 1e08e8547dc..4a110b733e1 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -314,41 +314,31 @@ pub fn prepare_compiler_for_check( } } -/// Checks a single codegen backend. +/// Check the Cranelift codegen backend. #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct CodegenBackend { - pub build_compiler: Compiler, - pub target: TargetSelection, - pub backend: CodegenBackendKind, +pub struct CraneliftCodegenBackend { + build_compiler: Compiler, + target: TargetSelection, } -impl Step for CodegenBackend { +impl Step for CraneliftCodegenBackend { type Output = (); + const IS_HOST: bool = true; const DEFAULT: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.paths(&["compiler/rustc_codegen_cranelift", "compiler/rustc_codegen_gcc"]) + run.alias("rustc_codegen_cranelift").alias("cg_clif") } fn make_run(run: RunConfig<'_>) { - // FIXME: only check the backend(s) that were actually selected in run.paths let build_compiler = prepare_compiler_for_check(run.builder, run.target, Mode::Codegen); - for backend in [CodegenBackendKind::Cranelift, CodegenBackendKind::Gcc] { - run.builder.ensure(CodegenBackend { build_compiler, target: run.target, backend }); - } + run.builder.ensure(CraneliftCodegenBackend { build_compiler, target: run.target }); } fn run(self, builder: &Builder<'_>) { - // FIXME: remove once https://github.com/rust-lang/rust/issues/112393 is resolved - if builder.build.config.vendor && self.backend.is_gcc() { - println!("Skipping checking of `rustc_codegen_gcc` with vendoring enabled."); - return; - } - let build_compiler = self.build_compiler; let target = self.target; - let backend = self.backend; let mut cargo = builder::Cargo::new( builder, @@ -361,31 +351,104 @@ impl Step for CodegenBackend { cargo .arg("--manifest-path") - .arg(builder.src.join(format!("compiler/{}/Cargo.toml", backend.crate_name()))); + .arg(builder.src.join("compiler/rustc_codegen_cranelift/Cargo.toml")); rustc_cargo_env(builder, &mut cargo, target); let _guard = builder.msg( Kind::Check, - backend.crate_name(), + "rustc_codegen_cranelift", Mode::Codegen, self.build_compiler, target, ); - let stamp = build_stamp::codegen_backend_stamp(builder, build_compiler, target, &backend) - .with_prefix("check"); + let stamp = build_stamp::codegen_backend_stamp( + builder, + build_compiler, + target, + &CodegenBackendKind::Cranelift, + ) + .with_prefix("check"); run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); } fn metadata(&self) -> Option<StepMetadata> { Some( - StepMetadata::check(&self.backend.crate_name(), self.target) + StepMetadata::check("rustc_codegen_cranelift", self.target) .built_by(self.build_compiler), ) } } +/// Check the GCC codegen backend. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct GccCodegenBackend { + build_compiler: Compiler, + target: TargetSelection, +} + +impl Step for GccCodegenBackend { + type Output = (); + + const IS_HOST: bool = true; + const DEFAULT: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.alias("rustc_codegen_gcc").alias("cg_gcc") + } + + fn make_run(run: RunConfig<'_>) { + let build_compiler = prepare_compiler_for_check(run.builder, run.target, Mode::Codegen); + run.builder.ensure(GccCodegenBackend { build_compiler, target: run.target }); + } + + fn run(self, builder: &Builder<'_>) { + // FIXME: remove once https://github.com/rust-lang/rust/issues/112393 is resolved + if builder.build.config.vendor { + println!("Skipping checking of `rustc_codegen_gcc` with vendoring enabled."); + return; + } + + let build_compiler = self.build_compiler; + let target = self.target; + + let mut cargo = builder::Cargo::new( + builder, + build_compiler, + Mode::Codegen, + SourceType::InTree, + target, + builder.kind, + ); + + cargo.arg("--manifest-path").arg(builder.src.join("compiler/rustc_codegen_gcc/Cargo.toml")); + rustc_cargo_env(builder, &mut cargo, target); + + let _guard = builder.msg( + Kind::Check, + "rustc_codegen_gcc", + Mode::Codegen, + self.build_compiler, + target, + ); + + let stamp = build_stamp::codegen_backend_stamp( + builder, + build_compiler, + target, + &CodegenBackendKind::Gcc, + ) + .with_prefix("check"); + + run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); + } + + fn metadata(&self) -> Option<StepMetadata> { + Some(StepMetadata::check("rustc_codegen_gcc", self.target).built_by(self.build_compiler)) + } +} + macro_rules! tool_check_step { ( $name:ident { diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index d860cafa1c0..997a152a31f 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -37,11 +37,12 @@ use crate::{ debug, trace, }; -/// Build a standard library for the given `target` using the given `compiler`. +/// Build a standard library for the given `target` using the given `build_compiler`. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Std { pub target: TargetSelection, - pub compiler: Compiler, + /// Compiler that builds the standard library. + pub build_compiler: Compiler, /// Whether to build only a subset of crates in the standard library. /// /// This shouldn't be used from other steps; see the comment on [`Rustc`]. @@ -54,10 +55,10 @@ pub struct Std { } impl Std { - pub fn new(compiler: Compiler, target: TargetSelection) -> Self { + pub fn new(build_compiler: Compiler, target: TargetSelection) -> Self { Self { target, - compiler, + build_compiler, crates: Default::default(), force_recompile: false, extra_rust_args: &[], @@ -120,7 +121,7 @@ impl Step for Std { trace!(force_recompile); run.builder.ensure(Std { - compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()), + build_compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()), target: run.target, crates, force_recompile, @@ -138,8 +139,8 @@ impl Step for Std { let target = self.target; // We already have std ready to be used for stage 0. - if self.compiler.stage == 0 { - let compiler = self.compiler; + if self.build_compiler.stage == 0 { + let compiler = self.build_compiler; builder.ensure(StdLink::from_std(self, compiler)); return; @@ -148,9 +149,10 @@ impl Step for Std { let build_compiler = if builder.download_rustc() && self.force_recompile { // When there are changes in the library tree with CI-rustc, we want to build // the stageN library and that requires using stageN-1 compiler. - builder.compiler(self.compiler.stage.saturating_sub(1), builder.config.host_target) + builder + .compiler(self.build_compiler.stage.saturating_sub(1), builder.config.host_target) } else { - self.compiler + self.build_compiler }; // When using `download-rustc`, we already have artifacts for the host available. Don't @@ -187,51 +189,50 @@ impl Step for Std { let mut target_deps = builder.ensure(StartupObjects { compiler: build_compiler, target }); - let compiler_to_use = - builder.compiler_for(build_compiler.stage, build_compiler.host, target); - trace!(?compiler_to_use); - - if compiler_to_use != build_compiler - // Never uplift std unless we have compiled stage 1; if stage 1 is compiled, - // uplift it from there. - // - // FIXME: improve `fn compiler_for` to avoid adding stage condition here. - && build_compiler.stage > 1 + // Stage of the stdlib that we're building + let stage = build_compiler.stage; + + // If we're building a stage2+ libstd, full bootstrap is + // disabled and we have a stage1 libstd already compiled for the given target, + // then simply uplift a previously built stage1 library. + if build_compiler.stage > 1 + && !builder.config.full_bootstrap + // This estimates if a stage1 libstd exists for the given target. If we're not + // cross-compiling, it should definitely exist by the time we're building a stage2 + // libstd. + // Or if we are cross-compiling, and we are building a cross-compiled rustc, then that + // rustc needs to link to a cross-compiled libstd, so again we should have a stage1 + // libstd for the given target prepared. + // Even if we guess wrong in the cross-compiled case, the worst that should happen is + // that we build a fresh stage1 libstd below, and then we immediately uplift it, so we + // don't pay the libstd build cost twice. + && (target == builder.host_target || builder.config.hosts.contains(&target)) { - trace!( - ?compiler_to_use, - ?build_compiler, - "build_compiler != compiler_to_use, uplifting library" - ); + let build_compiler_for_std_to_uplift = builder.compiler(1, builder.host_target); + builder.std(build_compiler_for_std_to_uplift, target); - builder.std(compiler_to_use, target); - let msg = if compiler_to_use.host == target { + let msg = if build_compiler_for_std_to_uplift.host == target { format!( - "Uplifting library (stage{} -> stage{})", - compiler_to_use.stage, build_compiler.stage + "Uplifting library (stage{} -> stage{stage})", + build_compiler_for_std_to_uplift.stage ) } else { format!( - "Uplifting library (stage{}:{} -> stage{}:{})", - compiler_to_use.stage, compiler_to_use.host, build_compiler.stage, target + "Uplifting library (stage{}:{} -> stage{stage}:{target})", + build_compiler_for_std_to_uplift.stage, build_compiler_for_std_to_uplift.host, ) }; + builder.info(&msg); // Even if we're not building std this stage, the new sysroot must // still contain the third party objects needed by various targets. self.copy_extra_objects(builder, &build_compiler, target); - builder.ensure(StdLink::from_std(self, compiler_to_use)); + builder.ensure(StdLink::from_std(self, build_compiler_for_std_to_uplift)); return; } - trace!( - ?compiler_to_use, - ?build_compiler, - "compiler == compiler_to_use, handling not-cross-compile scenario" - ); - target_deps.extend(self.copy_extra_objects(builder, &build_compiler, target)); // We build a sysroot for mir-opt tests using the same trick that Miri does: A check build @@ -299,7 +300,7 @@ impl Step for Std { } fn metadata(&self) -> Option<StepMetadata> { - Some(StepMetadata::build("std", self.target).built_by(self.compiler)) + Some(StepMetadata::build("std", self.target).built_by(self.build_compiler)) } } @@ -665,6 +666,14 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, cargo: &mut Car cargo.rustdocflag("-Zcrate-attr=warn(rust_2018_idioms)"); } +/// Link all libstd rlibs/dylibs into a sysroot of `target_compiler`. +/// +/// Links those artifacts generated by `compiler` to the `stage` compiler's +/// sysroot for the specified `host` and `target`. +/// +/// Note that this assumes that `compiler` has already generated the libstd +/// libraries for `target`, and this method will find them in the relevant +/// output directory. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct StdLink { pub compiler: Compiler, @@ -680,7 +689,7 @@ impl StdLink { pub fn from_std(std: Std, host_compiler: Compiler) -> Self { Self { compiler: host_compiler, - target_compiler: std.compiler, + target_compiler: std.build_compiler, target: std.target, crates: std.crates, force_recompile: std.force_recompile, @@ -951,14 +960,8 @@ impl Rustc { } impl Step for Rustc { - /// We return the stage of the "actual" compiler (not the uplifted one). - /// - /// By "actual" we refer to the uplifting logic where we may not compile the requested stage; - /// instead, we uplift it from the previous stages. Which can lead to bootstrap failures in - /// specific situations where we request stage X from other steps. However we may end up - /// uplifting it from stage Y, causing the other stage to fail when attempting to link with - /// stage X which was never actually built. - type Output = u32; + type Output = (); + const IS_HOST: bool = true; const DEFAULT: bool = false; @@ -997,7 +1000,7 @@ impl Step for Rustc { /// This will build the compiler for a particular stage of the build using /// the `build_compiler` targeting the `target` architecture. The artifacts /// created will also be linked into the sysroot directory. - fn run(self, builder: &Builder<'_>) -> u32 { + fn run(self, builder: &Builder<'_>) { let build_compiler = self.build_compiler; let target = self.target; @@ -1013,7 +1016,7 @@ impl Step for Rustc { &sysroot, builder.config.ci_rustc_dev_contents(), ); - return build_compiler.stage; + return; } // Build a standard library for `target` using the `build_compiler`. @@ -1027,31 +1030,33 @@ impl Step for Rustc { builder.info("WARNING: Use `--keep-stage-std` if you want to rebuild the compiler when it changes"); builder.ensure(RustcLink::from_rustc(self, build_compiler)); - return build_compiler.stage; + return; } - let compiler_to_use = - builder.compiler_for(build_compiler.stage, build_compiler.host, target); - if compiler_to_use != build_compiler { - builder.ensure(Rustc::new(compiler_to_use, target)); - let msg = if compiler_to_use.host == target { - format!( - "Uplifting rustc (stage{} -> stage{})", - compiler_to_use.stage, - build_compiler.stage + 1 - ) + // The stage of the compiler that we're building + let stage = build_compiler.stage + 1; + + // If we are building a stage3+ compiler, and full bootstrap is disabled, and we have a + // previous rustc available, we will uplift a compiler from a previous stage. + if build_compiler.stage >= 2 + && !builder.config.full_bootstrap + && (target == builder.host_target || builder.hosts.contains(&target)) + { + // If we're cross-compiling, the earliest rustc that we could have is stage 2. + // If we're not cross-compiling, then we should have rustc stage 1. + let stage_to_uplift = if target == builder.host_target { 1 } else { 2 }; + let rustc_to_uplift = builder.compiler(stage_to_uplift, target); + let msg = if rustc_to_uplift.host == target { + format!("Uplifting rustc (stage{} -> stage{stage})", rustc_to_uplift.stage,) } else { format!( - "Uplifting rustc (stage{}:{} -> stage{}:{})", - compiler_to_use.stage, - compiler_to_use.host, - build_compiler.stage + 1, - target + "Uplifting rustc (stage{}:{} -> stage{stage}:{target})", + rustc_to_uplift.stage, rustc_to_uplift.host, ) }; builder.info(&msg); - builder.ensure(RustcLink::from_rustc(self, compiler_to_use)); - return compiler_to_use.stage; + builder.ensure(RustcLink::from_rustc(self, rustc_to_uplift)); + return; } // Build a standard library for the current host target using the `build_compiler`. @@ -1128,8 +1133,6 @@ impl Step for Rustc { self, builder.compiler(build_compiler.stage, builder.config.host_target), )); - - build_compiler.stage } fn metadata(&self) -> Option<StepMetadata> { @@ -1909,12 +1912,18 @@ impl Step for Sysroot { } } +/// Prepare a compiler sysroot. +/// +/// The sysroot may contain various things useful for running the compiler, like linkers and +/// linker wrappers (LLD, LLVM bitcode linker, etc.). +/// +/// This will assemble a compiler in `build/$target/stage$stage`. #[derive(Debug, PartialOrd, Ord, Clone, PartialEq, Eq, Hash)] pub struct Assemble { /// The compiler which we will produce in this step. Assemble itself will /// take care of ensuring that the necessary prerequisites to do so exist, - /// that is, this target can be a stage2 compiler and Assemble will build - /// previous stages for you. + /// that is, this can be e.g. a stage2 compiler and Assemble will build + /// the previous stages for you. pub target_compiler: Compiler, } @@ -1932,11 +1941,6 @@ impl Step for Assemble { }); } - /// Prepare a new compiler from the artifacts in `stage` - /// - /// This will assemble a compiler in `build/$host/stage$stage`. The compiler - /// must have been previously produced by the `stage - 1` builder.build - /// compiler. fn run(self, builder: &Builder<'_>) -> Compiler { let target_compiler = self.target_compiler; @@ -2065,7 +2069,7 @@ impl Step for Assemble { target_compiler.stage - 1, builder.config.host_target, ); - let mut build_compiler = + let build_compiler = builder.compiler(target_compiler.stage - 1, builder.config.host_target); // Build enzyme @@ -2089,24 +2093,13 @@ impl Step for Assemble { } // Build the libraries for this compiler to link to (i.e., the libraries - // it uses at runtime). NOTE: Crates the target compiler compiles don't - // link to these. (FIXME: Is that correct? It seems to be correct most - // of the time but I think we do link to these for stage2/bin compilers - // when not performing a full bootstrap). + // it uses at runtime). debug!( ?build_compiler, "target_compiler.host" = ?target_compiler.host, "building compiler libraries to link to" ); - let actual_stage = builder.ensure(Rustc::new(build_compiler, target_compiler.host)); - // Current build_compiler.stage might be uplifted instead of being built; so update it - // to not fail while linking the artifacts. - debug!( - "(old) build_compiler.stage" = build_compiler.stage, - "(adjusted) build_compiler.stage" = actual_stage, - "temporarily adjusting `build_compiler.stage` to account for uplifted libraries" - ); - build_compiler.stage = actual_stage; + builder.ensure(Rustc::new(build_compiler, target_compiler.host)); let stage = target_compiler.stage; let host = target_compiler.host; @@ -2494,9 +2487,6 @@ pub fn stream_cargo( ) -> bool { let mut cmd = cargo.into_cmd(); - #[cfg(feature = "tracing")] - let _run_span = crate::utils::tracing::trace_cmd(&cmd); - // Instruct Cargo to give us json messages on stdout, critically leaving // stderr as piped so we can get those pretty colors. let mut message_format = if builder.config.json_output { diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 64c2cdd2ec7..414f4464d1e 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -916,6 +916,12 @@ fn copy_src_dirs( exclude_dirs: &[&str], dst_dir: &Path, ) { + // Iterating, filtering and copying a large number of directories can be quite slow. + // Avoid doing it in dry run (and thus also tests). + if builder.config.dry_run() { + return; + } + fn filter_fn(exclude_dirs: &[&str], dir: &str, path: &Path) -> bool { let spath = match path.to_str() { Some(path) => path, diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 6226c81a3fd..043cb1c2666 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -1042,7 +1042,8 @@ impl<'a> Builder<'a> { Kind::Check | Kind::Fix => describe!( check::Rustc, check::Rustdoc, - check::CodegenBackend, + check::CraneliftCodegenBackend, + check::GccCodegenBackend, check::Clippy, check::Miri, check::CargoMiri, @@ -1839,9 +1840,14 @@ pub fn pretty_step_name<S: Step>() -> String { /// Renders `step` using its `Debug` implementation and extract the field arguments out of it. fn step_debug_args<S: Step>(step: &S) -> String { let step_dbg_repr = format!("{step:?}"); - let brace_start = step_dbg_repr.find('{').unwrap_or(0); - let brace_end = step_dbg_repr.rfind('}').unwrap_or(step_dbg_repr.len()); - step_dbg_repr[brace_start + 1..brace_end - 1].trim().to_string() + + // Some steps do not have any arguments, so they do not have the braces + match (step_dbg_repr.find('{'), step_dbg_repr.rfind('}')) { + (Some(brace_start), Some(brace_end)) => { + step_dbg_repr[brace_start + 1..brace_end - 1].trim().to_string() + } + _ => String::new(), + } } fn pretty_print_step<S: Step>(step: &S) -> String { diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 9ba57542549..a9398a654e9 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1514,12 +1514,7 @@ mod snapshot { insta::assert_snapshot!( ctx.config("check") .path("compiler") - .render_steps(), @r" - [check] rustc 0 <host> -> rustc 1 <host> (73 crates) - [check] rustc 0 <host> -> rustc 1 <host> - [check] rustc 0 <host> -> rustc_codegen_cranelift 1 <host> - [check] rustc 0 <host> -> rustc_codegen_gcc 1 <host> - "); + .render_steps(), @"[check] rustc 0 <host> -> rustc 1 <host> (73 crates)"); } #[test] @@ -1545,12 +1540,7 @@ mod snapshot { ctx.config("check") .path("compiler") .stage(1) - .render_steps(), @r" - [check] rustc 0 <host> -> rustc 1 <host> (73 crates) - [check] rustc 0 <host> -> rustc 1 <host> - [check] rustc 0 <host> -> rustc_codegen_cranelift 1 <host> - [check] rustc 0 <host> -> rustc_codegen_gcc 1 <host> - "); + .render_steps(), @"[check] rustc 0 <host> -> rustc 1 <host> (73 crates)"); } #[test] @@ -1565,9 +1555,6 @@ mod snapshot { [build] rustc 0 <host> -> rustc 1 <host> [build] rustc 1 <host> -> std 1 <host> [check] rustc 1 <host> -> rustc 2 <host> (73 crates) - [check] rustc 1 <host> -> rustc 2 <host> - [check] rustc 1 <host> -> rustc_codegen_cranelift 2 <host> - [check] rustc 1 <host> -> rustc_codegen_gcc 2 <host> "); } @@ -1679,12 +1666,7 @@ mod snapshot { ctx.config("check") .paths(&["library", "compiler"]) .args(&args) - .render_steps(), @r" - [check] rustc 0 <host> -> rustc 1 <host> (73 crates) - [check] rustc 0 <host> -> rustc 1 <host> - [check] rustc 0 <host> -> rustc_codegen_cranelift 1 <host> - [check] rustc 0 <host> -> rustc_codegen_gcc 1 <host> - "); + .render_steps(), @"[check] rustc 0 <host> -> rustc 1 <host> (73 crates)"); } #[test] @@ -1768,7 +1750,6 @@ mod snapshot { .render_steps(), @r" [check] rustc 0 <host> -> rustc 1 <host> [check] rustc 0 <host> -> rustc_codegen_cranelift 1 <host> - [check] rustc 0 <host> -> rustc_codegen_gcc 1 <host> "); } @@ -2068,130 +2049,6 @@ mod snapshot { [doc] rustc 1 <host> -> reference (book) 2 <host> "); } - - #[test] - fn clippy_ci() { - let ctx = TestCtx::new(); - insta::assert_snapshot!( - ctx.config("clippy") - .path("ci") - .stage(2) - .render_steps(), @r" - [build] llvm <host> - [build] rustc 0 <host> -> rustc 1 <host> - [build] rustc 1 <host> -> std 1 <host> - [build] rustc 0 <host> -> clippy-driver 1 <host> - [build] rustc 0 <host> -> cargo-clippy 1 <host> - [clippy] rustc 1 <host> -> bootstrap 2 <host> - [clippy] rustc 1 <host> -> std 1 <host> - [clippy] rustc 1 <host> -> rustc 2 <host> - [check] rustc 1 <host> -> rustc 2 <host> - [clippy] rustc 1 <host> -> rustc_codegen_gcc 2 <host> - "); - } - - #[test] - fn clippy_compiler_stage1() { - let ctx = TestCtx::new(); - insta::assert_snapshot!( - ctx.config("clippy") - .path("compiler") - .render_steps(), @r" - [build] llvm <host> - [clippy] rustc 0 <host> -> rustc 1 <host> - "); - } - - #[test] - fn clippy_compiler_stage2() { - let ctx = TestCtx::new(); - insta::assert_snapshot!( - ctx.config("clippy") - .path("compiler") - .stage(2) - .render_steps(), @r" - [build] llvm <host> - [build] rustc 0 <host> -> rustc 1 <host> - [build] rustc 1 <host> -> std 1 <host> - [build] rustc 0 <host> -> clippy-driver 1 <host> - [build] rustc 0 <host> -> cargo-clippy 1 <host> - [clippy] rustc 1 <host> -> rustc 2 <host> - "); - } - - #[test] - fn clippy_std_stage1() { - let ctx = TestCtx::new(); - insta::assert_snapshot!( - ctx.config("clippy") - .path("std") - .render_steps(), @r" - [build] llvm <host> - [build] rustc 0 <host> -> rustc 1 <host> - [build] rustc 0 <host> -> clippy-driver 1 <host> - [build] rustc 0 <host> -> cargo-clippy 1 <host> - [clippy] rustc 1 <host> -> std 1 <host> - "); - } - - #[test] - fn clippy_std_stage2() { - let ctx = TestCtx::new(); - insta::assert_snapshot!( - ctx.config("clippy") - .path("std") - .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 1 <host> -> clippy-driver 2 <host> - [build] rustc 1 <host> -> cargo-clippy 2 <host> - [clippy] rustc 2 <host> -> std 2 <host> - "); - } - - #[test] - fn clippy_miri_stage1() { - let ctx = TestCtx::new(); - insta::assert_snapshot!( - ctx.config("clippy") - .path("miri") - .stage(1) - .render_steps(), @r" - [build] llvm <host> - [check] rustc 0 <host> -> rustc 1 <host> - [clippy] rustc 0 <host> -> miri 1 <host> - "); - } - - #[test] - fn clippy_miri_stage2() { - let ctx = TestCtx::new(); - insta::assert_snapshot!( - ctx.config("clippy") - .path("miri") - .stage(2) - .render_steps(), @r" - [build] llvm <host> - [build] rustc 0 <host> -> rustc 1 <host> - [build] rustc 1 <host> -> std 1 <host> - [check] rustc 1 <host> -> rustc 2 <host> - [build] rustc 0 <host> -> clippy-driver 1 <host> - [build] rustc 0 <host> -> cargo-clippy 1 <host> - [clippy] rustc 1 <host> -> miri 2 <host> - "); - } - - #[test] - fn clippy_bootstrap() { - let ctx = TestCtx::new(); - insta::assert_snapshot!( - ctx.config("clippy") - .path("bootstrap") - .render_steps(), @"[clippy] rustc 0 <host> -> bootstrap 1 <host>"); - } } struct ExecutedSteps { diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index b454a8ddefb..b2f9960a449 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -506,4 +506,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Warning, summary: "It is no longer possible to `x clippy` with stage 0. All clippy commands have to be on stage 1+.", }, + ChangeInfo { + change_id: 145379, + severity: ChangeSeverity::Info, + summary: "Build/check now supports forwarding `--timings` flag to cargo.", + }, ]; diff --git a/src/ci/scripts/free-disk-space-windows-start.py b/src/ci/scripts/free-disk-space-windows-start.py new file mode 100644 index 00000000000..fbaad722bff --- /dev/null +++ b/src/ci/scripts/free-disk-space-windows-start.py @@ -0,0 +1,72 @@ +""" +Start freeing disk space on Windows in the background by launching +the PowerShell cleanup script, and recording the PID in a file, +so later steps can wait for completion. +""" + +import subprocess +from pathlib import Path +from free_disk_space_windows_util import get_pid_file, get_log_file, run_main + + +def get_cleanup_script() -> Path: + script_dir = Path(__file__).resolve().parent + cleanup_script = script_dir / "free-disk-space-windows.ps1" + if not cleanup_script.exists(): + raise Exception(f"Cleanup script '{cleanup_script}' not found") + return cleanup_script + + +def write_pid(pid: int): + pid_file = get_pid_file() + if pid_file.exists(): + raise Exception(f"Pid file '{pid_file}' already exists") + pid_file.write_text(str(pid)) + print(f"wrote pid {pid} in file {pid_file}") + + +def launch_cleanup_process(): + cleanup_script = get_cleanup_script() + log_file_path = get_log_file() + # Launch the PowerShell cleanup in the background and redirect logs. + try: + with open(log_file_path, "w", encoding="utf-8") as log_file: + proc = subprocess.Popen( + [ + "pwsh", + # Suppress PowerShell startup banner/logo for cleaner logs. + "-NoLogo", + # Don't load user/system profiles. Ensures a clean, predictable environment. + "-NoProfile", + # Disable interactive prompts. Required for CI to avoid hangs. + "-NonInteractive", + # Execute the specified script file (next argument). + "-File", + str(cleanup_script), + ], + # Write child stdout to the log file. + stdout=log_file, + # Merge stderr into stdout for a single, ordered log stream. + stderr=subprocess.STDOUT, + ) + print( + f"Started free-disk-space cleanup in background. " + f"pid={proc.pid}; log_file={log_file_path}" + ) + return proc + except FileNotFoundError as e: + raise Exception("pwsh not found on PATH; cannot start disk cleanup.") from e + + +def main() -> int: + proc = launch_cleanup_process() + + # Write pid of the process to a file, so that later steps can read it and wait + # until the process completes. + write_pid(proc.pid) + + return 0 + + +if __name__ == "__main__": + run_main(main) diff --git a/src/ci/scripts/free-disk-space-windows-wait.py b/src/ci/scripts/free-disk-space-windows-wait.py new file mode 100644 index 00000000000..b8612bb71c2 --- /dev/null +++ b/src/ci/scripts/free-disk-space-windows-wait.py @@ -0,0 +1,77 @@ +""" +Wait for the background Windows disk cleanup process. +""" + +import ctypes +import time +from free_disk_space_windows_util import get_pid_file, get_log_file, run_main + + +def is_process_running(pid: int) -> bool: + PROCESS_QUERY_LIMITED_INFORMATION = 0x1000 + processHandle = ctypes.windll.kernel32.OpenProcess( + PROCESS_QUERY_LIMITED_INFORMATION, 0, pid + ) + if processHandle == 0: + # The process is not running. + # If you don't have the sufficient rights to check if a process is running, + # zero is also returned. But in GitHub Actions we have these rights. + return False + else: + ctypes.windll.kernel32.CloseHandle(processHandle) + return True + + +def print_logs(): + """Print the logs from the cleanup script.""" + log_file = get_log_file() + if log_file.exists(): + print("free-disk-space logs:") + # Print entire log; replace undecodable bytes to avoid exceptions. + try: + with open(log_file, "r", encoding="utf-8", errors="replace") as f: + print(f.read()) + except Exception as e: + raise Exception(f"Failed to read log file '{log_file}'") from e + else: + print(f"::warning::Log file '{log_file}' not found") + + +def read_pid_from_file() -> int: + """Read the PID from the pid file.""" + + pid_file = get_pid_file() + if not pid_file.exists(): + raise Exception( + f"No background free-disk-space process to wait for: pid file {pid_file} not found" + ) + + pid_file_content = pid_file.read_text().strip() + + # Delete the file if it exists + pid_file.unlink(missing_ok=True) + + try: + # Read the first line and convert to int. + pid = int(pid_file_content.splitlines()[0]) + return pid + except Exception as e: + raise Exception( + f"Error while parsing the pid file with content '{pid_file_content!r}'" + ) from e + + +def main() -> int: + pid = read_pid_from_file() + + # Poll until process exits + while is_process_running(pid): + time.sleep(3) + + print_logs() + + return 0 + + +if __name__ == "__main__": + run_main(main) diff --git a/src/ci/scripts/free-disk-space.sh b/src/ci/scripts/free-disk-space.sh index 062ad801cd8..9264fe4de6d 100755 --- a/src/ci/scripts/free-disk-space.sh +++ b/src/ci/scripts/free-disk-space.sh @@ -4,7 +4,7 @@ set -euo pipefail script_dir=$(dirname "$0") if [[ "${RUNNER_OS:-}" == "Windows" ]]; then - pwsh $script_dir/free-disk-space-windows.ps1 + python3 "$script_dir/free-disk-space-windows-start.py" else $script_dir/free-disk-space-linux.sh fi diff --git a/src/ci/scripts/free_disk_space_windows_util.py b/src/ci/scripts/free_disk_space_windows_util.py new file mode 100644 index 00000000000..488187864c2 --- /dev/null +++ b/src/ci/scripts/free_disk_space_windows_util.py @@ -0,0 +1,29 @@ +""" +Utilities for Windows disk space cleanup scripts. +""" + +import os +from pathlib import Path +import sys + + +def get_temp_dir() -> Path: + """Get the temporary directory set by GitHub Actions.""" + return Path(os.environ.get("RUNNER_TEMP")) + + +def get_pid_file() -> Path: + return get_temp_dir() / "free-disk-space.pid" + + +def get_log_file() -> Path: + return get_temp_dir() / "free-disk-space.log" + + +def run_main(main_fn): + exit_code = 1 + try: + exit_code = main_fn() + except Exception as e: + print(f"::error::{e}") + sys.exit(exit_code) diff --git a/src/doc/rustc-dev-guide/src/SUMMARY.md b/src/doc/rustc-dev-guide/src/SUMMARY.md index 9ded467d5cd..025a078ae5b 100644 --- a/src/doc/rustc-dev-guide/src/SUMMARY.md +++ b/src/doc/rustc-dev-guide/src/SUMMARY.md @@ -107,7 +107,6 @@ - [Installation](./autodiff/installation.md) - [How to debug](./autodiff/debugging.md) - [Autodiff flags](./autodiff/flags.md) - - [Current limitations](./autodiff/limitations.md) # Source Code Representation diff --git a/src/doc/rustc-dev-guide/src/autodiff/limitations.md b/src/doc/rustc-dev-guide/src/autodiff/limitations.md deleted file mode 100644 index 90afbd51f3f..00000000000 --- a/src/doc/rustc-dev-guide/src/autodiff/limitations.md +++ /dev/null @@ -1,27 +0,0 @@ -# Current limitations - -## Safety and Soundness - -Enzyme currently assumes that the user passes shadow arguments (`dx`, `dy`, ...) of appropriate size. Under Reverse Mode, we additionally assume that shadow arguments are mutable. In Reverse Mode we adjust the outermost pointer or reference to be mutable. Therefore `&f32` will receive the shadow type `&mut f32`. However, we do not check length for other types than slices (e.g. enums, Vec). We also do not enforce mutability of inner references, but will warn if we recognize them. We do intend to add additional checks over time. - -## ABI adjustments - -In some cases, a function parameter might get lowered in a way that we currently don't handle correctly, leading to a compile time type mismatch in the `rustc_codegen_llvm` backend. Here are some [examples](https://github.com/EnzymeAD/rust/issues/105). - -## Compile Times - -Enzyme will often achieve excellent runtime performance, but might increase your compile time by a large factor. For Rust, we already have made significant improvements and have a list of further improvements planed - please reach out if you have time to help here. - -### Type Analysis - -Most of the times, Type Analysis (TA) is the reason of large (>5x) compile time increases when using Enzyme. This poster explains why we need to run Type Analysis in the bottom left part: [Poster Link](https://c.wsmoses.com/posters/Enzyme-llvmdev.pdf). - -We intend to increase the number of locations where we pass down Type information based on Rust types, which in turn will reduce the number of locations where Enzyme has to run Type Analysis, which will help compile times. - -### Duplicated Optimizations - -The key reason for Enzyme offering often excellent performance is that Enzyme differentiates already optimized LLVM-IR. However, we also (have to) run LLVM's optimization pipeline after differentiating, to make sure that the code which Enzyme generates is optimized properly. As a result you should have excellent runtime performance (please fill an issue if not), but at a compile time cost for running optimizations twice. - -### Fat-LTO - -The usage of `#[autodiff(...)]` currently requires compiling your project with Fat-LTO. We technically only need LTO if the function being differentiated calls functions in other compilation units. Therefore, other solutions are possible, but this is the most simple one to get started. diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 73ce62cdcde..95bd31729de 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -16,7 +16,7 @@ use std::{fmt, panic, str}; pub(crate) use make::{BuildDocTestBuilder, DocTestBuilder}; pub(crate) use markdown::test as test_markdown; -use rustc_data_structures::fx::{FxHashMap, FxHasher, FxIndexMap, FxIndexSet}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxHasher, FxIndexMap, FxIndexSet}; use rustc_errors::emitter::HumanReadableErrorType; use rustc_errors::{ColorConfig, DiagCtxtHandle}; use rustc_hir as hir; @@ -689,6 +689,10 @@ fn run_test( "--extern=doctest_bundle_{edition}=", edition = doctest.edition )); + + // Deduplicate passed -L directory paths, since usually all dependencies will be in the + // same directory (e.g. target/debug/deps from Cargo). + let mut seen_search_dirs = FxHashSet::default(); for extern_str in &rustdoc_options.extern_strs { if let Some((_cratename, path)) = extern_str.split_once('=') { // Direct dependencies of the tests themselves are @@ -698,7 +702,9 @@ fn run_test( .parent() .filter(|x| x.components().count() > 0) .unwrap_or(Path::new(".")); - runner_compiler.arg("-L").arg(dir); + if seen_search_dirs.insert(dir) { + runner_compiler.arg("-L").arg(dir); + } } } let output_bundle_file = doctest diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 918b292466d..80399cf3842 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -602,7 +602,6 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It search_type, aliases, deprecation, - is_unstable: item.stability(tcx).map(|x| x.is_unstable()).unwrap_or(false), }; cache.search_index.push(index_item); } diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 95259847075..a46253237db 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -139,7 +139,6 @@ pub(crate) struct IndexItem { pub(crate) search_type: Option<IndexItemFunctionType>, pub(crate) aliases: Box<[Symbol]>, pub(crate) deprecation: Option<Deprecation>, - pub(crate) is_unstable: bool, } /// A type used for the search index. diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 93c9df8c049..e2f86b8a854 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -93,7 +93,6 @@ pub(crate) fn build_index( ), aliases: item.attrs.get_doc_aliases(), deprecation: item.deprecation(tcx), - is_unstable: item.stability(tcx).is_some_and(|x| x.is_unstable()), }); } } @@ -656,7 +655,6 @@ pub(crate) fn build_index( let mut parents_backref_queue = VecDeque::new(); let mut functions = String::with_capacity(self.items.len()); let mut deprecated = Vec::with_capacity(self.items.len()); - let mut unstable = Vec::with_capacity(self.items.len()); let mut type_backref_queue = VecDeque::new(); @@ -713,9 +711,6 @@ pub(crate) fn build_index( // bitmasks always use 1-indexing for items, with 0 as the crate itself deprecated.push(u32::try_from(index + 1).unwrap()); } - if item.is_unstable { - unstable.push(u32::try_from(index + 1).unwrap()); - } } for (index, path) in &revert_extra_paths { @@ -754,7 +749,6 @@ pub(crate) fn build_index( crate_data.serialize_field("r", &re_exports)?; crate_data.serialize_field("b", &self.associated_item_disambiguators)?; crate_data.serialize_field("c", &bitmap_to_string(&deprecated))?; - crate_data.serialize_field("u", &bitmap_to_string(&unstable))?; crate_data.serialize_field("e", &bitmap_to_string(&self.empty_desc))?; crate_data.serialize_field("P", ¶m_names)?; if has_aliases { diff --git a/src/librustdoc/html/static/js/rustdoc.d.ts b/src/librustdoc/html/static/js/rustdoc.d.ts index b082b65ab57..3d30a7adb98 100644 --- a/src/librustdoc/html/static/js/rustdoc.d.ts +++ b/src/librustdoc/html/static/js/rustdoc.d.ts @@ -129,7 +129,7 @@ declare namespace rustdoc { /** * A single parsed "atom" in a search query. For example, - * + * * std::fmt::Formatter, Write -> Result<()> * ┏━━━━━━━━━━━━━━━━━━ ┌──── ┏━━━━━┅┅┅┅┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐ * ┃ │ ┗ QueryElement { ┊ @@ -449,8 +449,6 @@ declare namespace rustdoc { * of `p`) but is used for modules items like free functions. * * `c` is an array of item indices that are deprecated. - * - * `u` is an array of item indices that are unstable. */ type RawSearchIndexCrate = { doc: string, @@ -465,7 +463,6 @@ declare namespace rustdoc { p: Array<[number, string] | [number, string, number] | [number, string, number, number] | [number, string, number, number, string]>, b: Array<[number, String]>, c: string, - u: string, r: Array<[number, number]>, P: Array<[number, string]>, }; diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 0011544d16e..505652c0f4a 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -1465,11 +1465,6 @@ class DocSearch { */ this.searchIndexEmptyDesc = new Map(); /** - * @type {Map<String, RoaringBitmap>} - */ - this.searchIndexUnstable = new Map(); - - /** * @type {Uint32Array} */ this.functionTypeFingerprint = new Uint32Array(0); @@ -2057,12 +2052,9 @@ class DocSearch { }; const descShardList = [descShard]; - // Deprecated and unstable items and items with no description + // Deprecated items and items with no description this.searchIndexDeprecated.set(crate, new RoaringBitmap(crateCorpus.c)); this.searchIndexEmptyDesc.set(crate, new RoaringBitmap(crateCorpus.e)); - if (crateCorpus.u !== undefined && crateCorpus.u !== null) { - this.searchIndexUnstable.set(crate, new RoaringBitmap(crateCorpus.u)); - } let descIndex = 0; /** @@ -3334,25 +3326,6 @@ class DocSearch { return a - b; } - // sort unstable items later - // FIXME: there is some doubt if this is the most effecient way to implement this. - // alternative options include: - // * put is_unstable on each item when the index is built. - // increases memory usage but avoids a hashmap lookup. - // * put is_unstable on each item before sorting. - // better worst case performance but worse average case performance. - a = Number( - // @ts-expect-error - this.searchIndexUnstable.get(aaa.item.crate).contains(aaa.item.bitIndex), - ); - b = Number( - // @ts-expect-error - this.searchIndexUnstable.get(bbb.item.crate).contains(bbb.item.bitIndex), - ); - if (a !== b) { - return a - b; - } - // sort by crate (current crate comes first) a = Number(aaa.item.crate !== preferredCrate); b = Number(bbb.item.crate !== preferredCrate); diff --git a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs index cf0c85990b1..788a04357b1 100644 --- a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs +++ b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs @@ -11,7 +11,7 @@ use rustc_ast::{BinOpKind, LitKind, RangeLimits}; use rustc_data_structures::packed::Pu128; use rustc_data_structures::unhash::UnindexMap; use rustc_errors::{Applicability, Diag}; -use rustc_hir::{Block, Body, Expr, ExprKind, UnOp}; +use rustc_hir::{Body, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::source_map::Spanned; @@ -135,12 +135,12 @@ fn assert_len_expr<'hir>( cx: &LateContext<'_>, expr: &'hir Expr<'hir>, ) -> Option<(LengthComparison, usize, &'hir Expr<'hir>)> { - let (cmp, asserted_len, slice_len) = if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr) - && let ExprKind::Unary(UnOp::Not, condition) = &cond.kind - && let ExprKind::Binary(bin_op, left, right) = &condition.kind + let (cmp, asserted_len, slice_len) = if let Some( + higher::IfLetOrMatch::Match(cond, [_, then], _) + ) = higher::IfLetOrMatch::parse(cx, expr) + && let ExprKind::Binary(bin_op, left, right) = &cond.kind // check if `then` block has a never type expression - && let ExprKind::Block(Block { expr: Some(then_expr), .. }, _) = then.kind - && cx.typeck_results().expr_ty(then_expr).is_never() + && cx.typeck_results().expr_ty(then.body).is_never() { len_comparison(bin_op.node, left, right)? } else if let Some((macro_call, bin_op)) = first_node_macro_backtrace(cx, expr).find_map(|macro_call| { diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs index 60473a26493..7cd5a16f5b4 100644 --- a/src/tools/clippy/clippy_utils/src/macros.rs +++ b/src/tools/clippy/clippy_utils/src/macros.rs @@ -250,18 +250,13 @@ impl<'a> PanicExpn<'a> { }; let name = path.segments.last().unwrap().ident.name; - // This has no argument - if name == sym::panic_cold_explicit { - return Some(Self::Empty); - } - let [arg, rest @ ..] = args else { return None; }; let result = match name { sym::panic if arg.span.eq_ctxt(expr.span) => Self::Empty, sym::panic | sym::panic_str => Self::Str(arg), - sym::panic_display | sym::panic_cold_display => { + sym::panic_display => { let ExprKind::AddrOf(_, _, e) = &arg.kind else { return None; }; diff --git a/src/tools/clippy/clippy_utils/src/sym.rs b/src/tools/clippy/clippy_utils/src/sym.rs index ce7cc9348fb..278101ac27f 100644 --- a/src/tools/clippy/clippy_utils/src/sym.rs +++ b/src/tools/clippy/clippy_utils/src/sym.rs @@ -241,9 +241,6 @@ generate! { or_insert, or_insert_with, outer_expn, - panic_cold_display, - panic_cold_explicit, - panic_display, panic_str, parse, partition, diff --git a/src/tools/clippy/tests/ui/const_is_empty.rs b/src/tools/clippy/tests/ui/const_is_empty.rs index 8bb4f0e5d97..63c6342a323 100644 --- a/src/tools/clippy/tests/ui/const_is_empty.rs +++ b/src/tools/clippy/tests/ui/const_is_empty.rs @@ -196,6 +196,7 @@ fn issue_13106() { const { assert!(EMPTY_STR.is_empty()); + //~^ const_is_empty } const { diff --git a/src/tools/clippy/tests/ui/const_is_empty.stderr b/src/tools/clippy/tests/ui/const_is_empty.stderr index 2ba189058e8..9a42518698e 100644 --- a/src/tools/clippy/tests/ui/const_is_empty.stderr +++ b/src/tools/clippy/tests/ui/const_is_empty.stderr @@ -158,10 +158,16 @@ LL | let _ = val.is_empty(); | ^^^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:202:9 + --> tests/ui/const_is_empty.rs:198:17 + | +LL | assert!(EMPTY_STR.is_empty()); + | ^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:203:9 | LL | EMPTY_STR.is_empty(); | ^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 27 previous errors +error: aborting due to 28 previous errors diff --git a/src/tools/clippy/tests/ui/incompatible_msrv.rs b/src/tools/clippy/tests/ui/incompatible_msrv.rs index f7f21e1850d..882f909e30c 100644 --- a/src/tools/clippy/tests/ui/incompatible_msrv.rs +++ b/src/tools/clippy/tests/ui/incompatible_msrv.rs @@ -1,6 +1,6 @@ #![warn(clippy::incompatible_msrv)] #![feature(custom_inner_attributes)] -#![allow(stable_features)] +#![allow(stable_features, clippy::diverging_sub_expression)] #![feature(strict_provenance)] // For use in test #![clippy::msrv = "1.3.0"] diff --git a/src/tools/compiletest/src/runtest/ui.rs b/src/tools/compiletest/src/runtest/ui.rs index 0507c2600ae..40b0ee0a399 100644 --- a/src/tools/compiletest/src/runtest/ui.rs +++ b/src/tools/compiletest/src/runtest/ui.rs @@ -10,6 +10,7 @@ use super::{ TestCx, TestOutput, Truncated, UI_FIXED, WillExecute, }; use crate::json; +use crate::runtest::ProcRes; impl TestCx<'_> { pub(super) fn run_ui_test(&self) { @@ -127,6 +128,9 @@ impl TestCx<'_> { ); } + // If the test is executed, capture its ProcRes separately so that + // pattern/forbid checks can report the *runtime* stdout/stderr when they fail. + let mut run_proc_res: Option<ProcRes> = None; let output_to_check = if let WillExecute::Yes = should_run { let proc_res = self.exec_compiled_test(); let run_output_errors = if self.props.check_run_results { @@ -189,7 +193,10 @@ impl TestCx<'_> { unreachable!("run_ui_test() must not be called if the test should not run"); } - self.get_output(&proc_res) + let output = self.get_output(&proc_res); + // Move the proc_res into our option after we've extracted output. + run_proc_res = Some(proc_res); + output } else { self.get_output(&proc_res) }; @@ -200,9 +207,14 @@ impl TestCx<'_> { explicit, self.config.compare_mode, proc_res.status, self.props.error_patterns ); + // Compiler diagnostics (expected errors) are always tied to the compile-time ProcRes. self.check_expected_errors(&proc_res); - self.check_all_error_patterns(&output_to_check, &proc_res); - self.check_forbid_output(&output_to_check, &proc_res); + + // For runtime pattern/forbid checks prefer the executed program's ProcRes if available + // so that missing pattern failures include the program's stdout/stderr. + let pattern_proc_res = run_proc_res.as_ref().unwrap_or(&proc_res); + self.check_all_error_patterns(&output_to_check, pattern_proc_res); + self.check_forbid_output(&output_to_check, pattern_proc_res); if self.props.run_rustfix && self.config.compare_mode.is_none() { // And finally, compile the fixed code and make sure it both diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 82cdbed7a1d..a399b5cd77e 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -3507a749b365aae4eefa96ab700a9315d3280ee7 +425a9c0a0e365c0b8c6cfd00c2ded83a73bed9a0 diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 757244c11a6..f4b66af574c 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -280,16 +280,16 @@ impl interpret::Provenance for Provenance { Ok(()) } - fn join(left: Option<Self>, right: Option<Self>) -> Option<Self> { + fn join(left: Self, right: Self) -> Option<Self> { match (left, right) { // If both are the *same* concrete tag, that is the result. ( - Some(Provenance::Concrete { alloc_id: left_alloc, tag: left_tag }), - Some(Provenance::Concrete { alloc_id: right_alloc, tag: right_tag }), - ) if left_alloc == right_alloc && left_tag == right_tag => left, + Provenance::Concrete { alloc_id: left_alloc, tag: left_tag }, + Provenance::Concrete { alloc_id: right_alloc, tag: right_tag }, + ) if left_alloc == right_alloc && left_tag == right_tag => Some(left), // If one side is a wildcard, the best possible outcome is that it is equal to the other // one, and we use that. - (Some(Provenance::Wildcard), o) | (o, Some(Provenance::Wildcard)) => o, + (Provenance::Wildcard, o) | (o, Provenance::Wildcard) => Some(o), // Otherwise, fall back to `None`. _ => None, } diff --git a/src/tools/miri/src/shims/native_lib/mod.rs b/src/tools/miri/src/shims/native_lib/mod.rs index 2827ed997a7..e2a0bdbd9b4 100644 --- a/src/tools/miri/src/shims/native_lib/mod.rs +++ b/src/tools/miri/src/shims/native_lib/mod.rs @@ -246,7 +246,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { let p_map = alloc.provenance(); for idx in overlap { // If a provenance was read by the foreign code, expose it. - if let Some(prov) = p_map.get(Size::from_bytes(idx), this) { + if let Some((prov, _idx)) = p_map.get_byte(Size::from_bytes(idx), this) + { this.expose_provenance(prov)?; } } diff --git a/src/tools/miri/tests/fail/provenance/mix-ptrs1.rs b/src/tools/miri/tests/fail/provenance/mix-ptrs1.rs new file mode 100644 index 00000000000..0b7266a4da6 --- /dev/null +++ b/src/tools/miri/tests/fail/provenance/mix-ptrs1.rs @@ -0,0 +1,21 @@ +use std::{mem, ptr}; + +const PTR_SIZE: usize = mem::size_of::<&i32>(); + +fn main() { + unsafe { + let ptr = &0 as *const i32; + let arr = [ptr; 2]; + // We want to do a scalar read of a pointer at offset PTR_SIZE/2 into this array. But we + // cannot use a packed struct or `read_unaligned`, as those use the memcpy code path in + // Miri. So instead we shift the entire array by a bit and then the actual read we want to + // do is perfectly aligned. + let mut target_arr = [ptr::null::<i32>(); 3]; + let target = target_arr.as_mut_ptr().cast::<u8>(); + target.add(PTR_SIZE / 2).cast::<[*const i32; 2]>().write_unaligned(arr); + // Now target_arr[1] is a mix of the two `ptr` we had stored in `arr`. + // They all have the same provenance, but not in the right order, so we reject this. + let strange_ptr = target_arr[1]; + assert_eq!(*strange_ptr.with_addr(ptr.addr()), 0); //~ERROR: no provenance + } +} diff --git a/src/tools/miri/tests/fail/provenance/mix-ptrs1.stderr b/src/tools/miri/tests/fail/provenance/mix-ptrs1.stderr new file mode 100644 index 00000000000..88d75135a76 --- /dev/null +++ b/src/tools/miri/tests/fail/provenance/mix-ptrs1.stderr @@ -0,0 +1,16 @@ +error: Undefined Behavior: pointer not dereferenceable: pointer must be dereferenceable for 4 bytes, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) + --> tests/fail/provenance/mix-ptrs1.rs:LL:CC + | +LL | assert_eq!(*strange_ptr.with_addr(ptr.addr()), 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 RUSTLIB/core/src/macros/mod.rs:LL:CC + = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +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/provenance/mix-ptrs2.rs b/src/tools/miri/tests/fail/provenance/mix-ptrs2.rs new file mode 100644 index 00000000000..9ef40c194ab --- /dev/null +++ b/src/tools/miri/tests/fail/provenance/mix-ptrs2.rs @@ -0,0 +1,37 @@ +use std::mem; + +const PTR_SIZE: usize = mem::size_of::<&i32>(); + +/// Overwrite one byte of a pointer, then restore it. +fn main() { + unsafe fn ptr_bytes<'x>(ptr: &'x mut *const i32) -> &'x mut [mem::MaybeUninit<u8>; PTR_SIZE] { + mem::transmute(ptr) + } + + // Returns a value with the same provenance as `x` but 0 for the integer value. + // `x` must be initialized. + unsafe fn zero_with_provenance(x: mem::MaybeUninit<u8>) -> mem::MaybeUninit<u8> { + let ptr = [x; PTR_SIZE]; + let ptr: *const i32 = mem::transmute(ptr); + let mut ptr = ptr.with_addr(0); + ptr_bytes(&mut ptr)[0] + } + + unsafe { + let ptr = &42; + let mut ptr = ptr as *const i32; + // Get a bytewise view of the pointer. + let ptr_bytes = ptr_bytes(&mut ptr); + + // The highest bytes must be 0 for this to work. + let hi = if cfg!(target_endian = "little") { ptr_bytes.len() - 1 } else { 0 }; + assert_eq!(*ptr_bytes[hi].as_ptr().cast::<u8>(), 0); + // Overwrite provenance on the last byte. + ptr_bytes[hi] = mem::MaybeUninit::new(0); + // Restore it from the another byte. + ptr_bytes[hi] = zero_with_provenance(ptr_bytes[1]); + + // Now ptr is almost good, except the provenance fragment indices do not work out... + assert_eq!(*ptr, 42); //~ERROR: no provenance + } +} diff --git a/src/tools/miri/tests/fail/provenance/mix-ptrs2.stderr b/src/tools/miri/tests/fail/provenance/mix-ptrs2.stderr new file mode 100644 index 00000000000..636378952ba --- /dev/null +++ b/src/tools/miri/tests/fail/provenance/mix-ptrs2.stderr @@ -0,0 +1,16 @@ +error: Undefined Behavior: pointer not dereferenceable: pointer must be dereferenceable for 4 bytes, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) + --> tests/fail/provenance/mix-ptrs2.rs:LL:CC + | +LL | assert_eq!(*ptr, 42); + | ^^^^^^^^^^^^^^^^^^^^ 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 RUSTLIB/core/src/macros/mod.rs:LL:CC + = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +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/uninit/uninit_alloc_diagnostic_with_provenance.rs b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.rs index 1cf70d9d9a3..78a5098d21a 100644 --- a/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.rs +++ b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.rs @@ -9,11 +9,10 @@ use std::alloc::{Layout, alloc, dealloc}; use std::mem::{self, MaybeUninit}; use std::slice::from_raw_parts; -fn byte_with_provenance<T>(val: u8, prov: *const T) -> MaybeUninit<u8> { - let ptr = prov.with_addr(val as usize); +fn byte_with_provenance<T>(val: u8, prov: *const T, frag_idx: usize) -> MaybeUninit<u8> { + let ptr = prov.with_addr(usize::from_ne_bytes([val; _])); let bytes: [MaybeUninit<u8>; mem::size_of::<*const ()>()] = unsafe { mem::transmute(ptr) }; - let lsb = if cfg!(target_endian = "little") { 0 } else { bytes.len() - 1 }; - bytes[lsb] + bytes[frag_idx] } fn main() { @@ -21,10 +20,10 @@ fn main() { unsafe { let ptr = alloc(layout); let ptr_raw = ptr.cast::<MaybeUninit<u8>>(); - *ptr_raw.add(0) = byte_with_provenance(0x42, &42u8); + *ptr_raw.add(0) = byte_with_provenance(0x42, &42u8, 0); *ptr.add(1) = 0x12; *ptr.add(2) = 0x13; - *ptr_raw.add(3) = byte_with_provenance(0x43, &0u8); + *ptr_raw.add(3) = byte_with_provenance(0x43, &0u8, 1); let slice1 = from_raw_parts(ptr, 8); let slice2 = from_raw_parts(ptr.add(8), 8); drop(slice1.cmp(slice2)); diff --git a/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.stderr b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.stderr index d3253317fae..305d8d4fbed 100644 --- a/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.stderr +++ b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.stderr @@ -17,7 +17,7 @@ LL | drop(slice1.cmp(slice2)); Uninitialized memory occurred at ALLOC[0x4..0x8], in this allocation: ALLOC (Rust heap, size: 16, align: 8) { - ╾42[ALLOC]<TAG> (1 ptr byte)╼ 12 13 ╾43[ALLOC]<TAG> (1 ptr byte)╼ __ __ __ __ __ __ __ __ __ __ __ __ │ ━..━░░░░░░░░░░░░ + ╾42[ALLOC]<TAG> (ptr fragment 0)╼ 12 13 ╾43[ALLOC]<TAG> (ptr fragment 1)╼ __ __ __ __ __ __ __ __ __ __ __ __ │ ━..━░░░░░░░░░░░░ } ALLOC (global (static or const), size: 1, align: 1) { 2a │ * diff --git a/src/tools/miri/tests/native-lib/fail/tracing/partial_init.stderr b/src/tools/miri/tests/native-lib/fail/tracing/partial_init.stderr index 74a599ede5c..1c0ad2f0dc9 100644 --- a/src/tools/miri/tests/native-lib/fail/tracing/partial_init.stderr +++ b/src/tools/miri/tests/native-lib/fail/tracing/partial_init.stderr @@ -35,7 +35,7 @@ LL | partial_init(); Uninitialized memory occurred at ALLOC[0x2..0x3], in this allocation: ALLOC (stack variable, size: 3, align: 1) { - ╾00[wildcard] (1 ptr byte)╼ ╾00[wildcard] (1 ptr byte)╼ __ │ ━━░ + ╾00[wildcard] (ptr fragment 0)╼ ╾00[wildcard] (ptr fragment 0)╼ __ │ ━━░ } note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/pass/provenance.rs b/src/tools/miri/tests/pass/provenance.rs index 8c52b703190..46f5bd73d55 100644 --- a/src/tools/miri/tests/pass/provenance.rs +++ b/src/tools/miri/tests/pass/provenance.rs @@ -6,7 +6,6 @@ const PTR_SIZE: usize = mem::size_of::<&i32>(); fn main() { basic(); - partial_overwrite_then_restore(); bytewise_ptr_methods(); bytewise_custom_memcpy(); bytewise_custom_memcpy_chunked(); @@ -29,40 +28,6 @@ fn basic() { assert_eq!(unsafe { *ptr_back }, 42); } -/// Overwrite one byte of a pointer, then restore it. -fn partial_overwrite_then_restore() { - unsafe fn ptr_bytes<'x>(ptr: &'x mut *const i32) -> &'x mut [mem::MaybeUninit<u8>; PTR_SIZE] { - mem::transmute(ptr) - } - - // Returns a value with the same provenance as `x` but 0 for the integer value. - // `x` must be initialized. - unsafe fn zero_with_provenance(x: mem::MaybeUninit<u8>) -> mem::MaybeUninit<u8> { - let ptr = [x; PTR_SIZE]; - let ptr: *const i32 = mem::transmute(ptr); - let mut ptr = ptr.with_addr(0); - ptr_bytes(&mut ptr)[0] - } - - unsafe { - let ptr = &42; - let mut ptr = ptr as *const i32; - // Get a bytewise view of the pointer. - let ptr_bytes = ptr_bytes(&mut ptr); - - // The highest bytes must be 0 for this to work. - let hi = if cfg!(target_endian = "little") { ptr_bytes.len() - 1 } else { 0 }; - assert_eq!(*ptr_bytes[hi].as_ptr().cast::<u8>(), 0); - // Overwrite provenance on the last byte. - ptr_bytes[hi] = mem::MaybeUninit::new(0); - // Restore it from the another byte. - ptr_bytes[hi] = zero_with_provenance(ptr_bytes[1]); - - // Now ptr should be good again. - assert_eq!(*ptr, 42); - } -} - fn bytewise_ptr_methods() { let mut ptr1 = &1; let mut ptr2 = &2; diff --git a/src/tools/miri/tests/pass/transmute_ptr.rs b/src/tools/miri/tests/pass/transmute_ptr.rs index 0944781c6de..0a53b778294 100644 --- a/src/tools/miri/tests/pass/transmute_ptr.rs +++ b/src/tools/miri/tests/pass/transmute_ptr.rs @@ -1,6 +1,6 @@ //@revisions: stack tree //@[tree]compile-flags: -Zmiri-tree-borrows -use std::{mem, ptr}; +use std::mem; fn t1() { // If we are careful, we can exploit data layout... @@ -27,27 +27,8 @@ fn ptr_integer_array() { let _x: [u8; PTR_SIZE] = unsafe { mem::transmute(&0) }; } -fn ptr_in_two_halves() { - unsafe { - let ptr = &0 as *const i32; - let arr = [ptr; 2]; - // We want to do a scalar read of a pointer at offset PTR_SIZE/2 into this array. But we - // cannot use a packed struct or `read_unaligned`, as those use the memcpy code path in - // Miri. So instead we shift the entire array by a bit and then the actual read we want to - // do is perfectly aligned. - let mut target_arr = [ptr::null::<i32>(); 3]; - let target = target_arr.as_mut_ptr().cast::<u8>(); - target.add(PTR_SIZE / 2).cast::<[*const i32; 2]>().write_unaligned(arr); - // Now target_arr[1] is a mix of the two `ptr` we had stored in `arr`. - let strange_ptr = target_arr[1]; - // Check that the provenance works out. - assert_eq!(*strange_ptr.with_addr(ptr.addr()), 0); - } -} - fn main() { t1(); t2(); ptr_integer_array(); - ptr_in_two_halves(); } diff --git a/src/tools/run-make-support/Cargo.toml b/src/tools/run-make-support/Cargo.toml index a4e7534137d..250e0f65a9f 100644 --- a/src/tools/run-make-support/Cargo.toml +++ b/src/tools/run-make-support/Cargo.toml @@ -17,7 +17,7 @@ object = "0.37" regex = "1.11" serde_json = "1.0" similar = "2.7" -wasmparser = { version = "0.219", default-features = false, features = ["std"] } +wasmparser = { version = "0.236", default-features = false, features = ["std", "features", "validate"] } # tidy-alphabetical-end # Shared with bootstrap and compiletest diff --git a/src/tools/rustbook/Cargo.lock b/src/tools/rustbook/Cargo.lock index d0eeab22062..e42f266391e 100644 --- a/src/tools/rustbook/Cargo.lock +++ b/src/tools/rustbook/Cargo.lock @@ -47,9 +47,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.19" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" +checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" dependencies = [ "anstyle", "anstyle-parse", @@ -77,22 +77,22 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.9" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -156,9 +156,9 @@ checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "cc" -version = "1.2.31" +version = "1.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2" +checksum = "2352e5597e9c544d5e6d9c95190d5d27738ade584fa8db0a16e130e5c2b5296e" dependencies = [ "shlex", ] @@ -185,9 +185,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.42" +version = "4.5.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed87a9d530bb41a67537289bafcac159cb3ee28460e0a4571123d2a778a6a882" +checksum = "50fd97c9dc2399518aa331917ac6f274280ec5eb34e555dd291899745c48ec6f" dependencies = [ "clap_builder", "clap_derive", @@ -195,9 +195,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.42" +version = "4.5.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64f4f3f3c77c94aff3c7e9aac9a2ca1974a5adf392a8bb751e827d6d127ab966" +checksum = "c35b5830294e1fa0462034af85cc95225a4cb07092c088c55bda3147cfcd8f65" dependencies = [ "anstream", "anstyle", @@ -208,9 +208,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.55" +version = "4.5.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5abde44486daf70c5be8b8f8f1b66c49f86236edf6fa2abadb4d961c4c6229a" +checksum = "67e4efcbb5da11a92e8a609233aa1e8a7d91e38de0be865f016d14700d45a7fd" dependencies = [ "clap", ] @@ -564,9 +564,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.4" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" [[package]] name = "heck" @@ -1406,9 +1406,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" @@ -2183,9 +2183,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.2" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" dependencies = [ "yoke", "zerofrom", diff --git a/tests/codegen-llvm/align-byval-alignment-mismatch.rs b/tests/codegen-llvm/align-byval-alignment-mismatch.rs index c69fc2de9d2..8eaf2751ed7 100644 --- a/tests/codegen-llvm/align-byval-alignment-mismatch.rs +++ b/tests/codegen-llvm/align-byval-alignment-mismatch.rs @@ -55,10 +55,10 @@ extern "C" { pub unsafe fn rust_to_c_increases_alignment(x: Align1) { // i686-linux: start: // i686-linux-NEXT: [[ALLOCA:%[0-9a-z]+]] = alloca [48 x i8], align 4 - // i686-linux-NEXT: call void @llvm.lifetime.start.p0(i64 48, ptr {{.*}}[[ALLOCA]]) + // i686-linux-NEXT: call void @llvm.lifetime.start.p0({{(i64 48, )?}}ptr {{.*}}[[ALLOCA]]) // i686-linux-NEXT: call void @llvm.memcpy.{{.+}}(ptr {{.*}}align 4 {{.*}}[[ALLOCA]], ptr {{.*}}align 1 {{.*}}%x // i686-linux-NEXT: call void @extern_c_align1({{.+}} [[ALLOCA]]) - // i686-linux-NEXT: call void @llvm.lifetime.end.p0(i64 48, ptr {{.*}}[[ALLOCA]]) + // i686-linux-NEXT: call void @llvm.lifetime.end.p0({{(i64 48, )?}}ptr {{.*}}[[ALLOCA]]) // x86_64-linux: start: // x86_64-linux-NEXT: call void @extern_c_align1 diff --git a/tests/codegen-llvm/autodiffv2.rs b/tests/codegen-llvm/autodiff/autodiffv2.rs index a40d19d3be3..85aed6a183b 100644 --- a/tests/codegen-llvm/autodiffv2.rs +++ b/tests/codegen-llvm/autodiff/autodiffv2.rs @@ -26,12 +26,13 @@ #![feature(autodiff)] -use std::autodiff::autodiff; +use std::autodiff::autodiff_forward; +// CHECK: ; #[no_mangle] //#[autodiff(d_square1, Forward, Dual, Dual)] -#[autodiff(d_square2, Forward, 4, Dualv, Dualv)] -#[autodiff(d_square3, Forward, 4, Dual, Dual)] +#[autodiff_forward(d_square2, 4, Dualv, Dualv)] +#[autodiff_forward(d_square3, 4, Dual, Dual)] fn square(x: &[f32], y: &mut [f32]) { assert!(x.len() >= 4); assert!(y.len() >= 5); diff --git a/tests/codegen-llvm/autodiff/batched.rs b/tests/codegen-llvm/autodiff/batched.rs index d27aed50e6c..306a6ed9d1f 100644 --- a/tests/codegen-llvm/autodiff/batched.rs +++ b/tests/codegen-llvm/autodiff/batched.rs @@ -17,11 +17,12 @@ use std::autodiff::autodiff_forward; #[autodiff_forward(d_square2, 4, Dual, DualOnly)] #[autodiff_forward(d_square1, 4, Dual, Dual)] #[no_mangle] +#[inline(never)] fn square(x: &f32) -> f32 { x * x } -// d_sqaure2 +// d_square2 // CHECK: define internal fastcc [4 x float] @fwddiffe4square(float %x.0.val, [4 x ptr] %"x'") // CHECK-NEXT: start: // CHECK-NEXT: %0 = extractvalue [4 x ptr] %"x'", 0 @@ -32,24 +33,20 @@ fn square(x: &f32) -> f32 { // CHECK-NEXT: %"_2'ipl2" = load float, ptr %2, align 4 // CHECK-NEXT: %3 = extractvalue [4 x ptr] %"x'", 3 // CHECK-NEXT: %"_2'ipl3" = load float, ptr %3, align 4 -// CHECK-NEXT: %4 = insertelement <4 x float> poison, float %"_2'ipl", i64 0 -// CHECK-NEXT: %5 = insertelement <4 x float> %4, float %"_2'ipl1", i64 1 -// CHECK-NEXT: %6 = insertelement <4 x float> %5, float %"_2'ipl2", i64 2 -// CHECK-NEXT: %7 = insertelement <4 x float> %6, float %"_2'ipl3", i64 3 -// CHECK-NEXT: %8 = fadd fast <4 x float> %7, %7 -// CHECK-NEXT: %9 = insertelement <4 x float> poison, float %x.0.val, i64 0 -// CHECK-NEXT: %10 = shufflevector <4 x float> %9, <4 x float> poison, <4 x i32> zeroinitializer -// CHECK-NEXT: %11 = fmul fast <4 x float> %8, %10 -// CHECK-NEXT: %12 = extractelement <4 x float> %11, i64 0 -// CHECK-NEXT: %13 = insertvalue [4 x float] undef, float %12, 0 -// CHECK-NEXT: %14 = extractelement <4 x float> %11, i64 1 -// CHECK-NEXT: %15 = insertvalue [4 x float] %13, float %14, 1 -// CHECK-NEXT: %16 = extractelement <4 x float> %11, i64 2 -// CHECK-NEXT: %17 = insertvalue [4 x float] %15, float %16, 2 -// CHECK-NEXT: %18 = extractelement <4 x float> %11, i64 3 -// CHECK-NEXT: %19 = insertvalue [4 x float] %17, float %18, 3 -// CHECK-NEXT: ret [4 x float] %19 -// CHECK-NEXT: } +// CHECK-NEXT: %4 = fmul float %"_2'ipl", 2.000000e+00 +// CHECK-NEXT: %5 = fmul fast float %4, %x.0.val +// CHECK-NEXT: %6 = insertvalue [4 x float] undef, float %5, 0 +// CHECK-NEXT: %7 = fmul float %"_2'ipl1", 2.000000e+00 +// CHECK-NEXT: %8 = fmul fast float %7, %x.0.val +// CHECK-NEXT: %9 = insertvalue [4 x float] %6, float %8, 1 +// CHECK-NEXT: %10 = fmul float %"_2'ipl2", 2.000000e+00 +// CHECK-NEXT: %11 = fmul fast float %10, %x.0.val +// CHECK-NEXT: %12 = insertvalue [4 x float] %9, float %11, 2 +// CHECK-NEXT: %13 = fmul float %"_2'ipl3", 2.000000e+00 +// CHECK-NEXT: %14 = fmul fast float %13, %x.0.val +// CHECK-NEXT: %15 = insertvalue [4 x float] %12, float %14, 3 +// CHECK-NEXT: ret [4 x float] %15 +// CHECK-NEXT: } // d_square3, the extra float is the original return value (x * x) // CHECK: define internal fastcc { float, [4 x float] } @fwddiffe4square.1(float %x.0.val, [4 x ptr] %"x'") @@ -63,26 +60,22 @@ fn square(x: &f32) -> f32 { // CHECK-NEXT: %3 = extractvalue [4 x ptr] %"x'", 3 // CHECK-NEXT: %"_2'ipl3" = load float, ptr %3, align 4 // CHECK-NEXT: %_0 = fmul float %x.0.val, %x.0.val -// CHECK-NEXT: %4 = insertelement <4 x float> poison, float %"_2'ipl", i64 0 -// CHECK-NEXT: %5 = insertelement <4 x float> %4, float %"_2'ipl1", i64 1 -// CHECK-NEXT: %6 = insertelement <4 x float> %5, float %"_2'ipl2", i64 2 -// CHECK-NEXT: %7 = insertelement <4 x float> %6, float %"_2'ipl3", i64 3 -// CHECK-NEXT: %8 = fadd fast <4 x float> %7, %7 -// CHECK-NEXT: %9 = insertelement <4 x float> poison, float %x.0.val, i64 0 -// CHECK-NEXT: %10 = shufflevector <4 x float> %9, <4 x float> poison, <4 x i32> zeroinitializer -// CHECK-NEXT: %11 = fmul fast <4 x float> %8, %10 -// CHECK-NEXT: %12 = extractelement <4 x float> %11, i64 0 -// CHECK-NEXT: %13 = insertvalue [4 x float] undef, float %12, 0 -// CHECK-NEXT: %14 = extractelement <4 x float> %11, i64 1 -// CHECK-NEXT: %15 = insertvalue [4 x float] %13, float %14, 1 -// CHECK-NEXT: %16 = extractelement <4 x float> %11, i64 2 -// CHECK-NEXT: %17 = insertvalue [4 x float] %15, float %16, 2 -// CHECK-NEXT: %18 = extractelement <4 x float> %11, i64 3 -// CHECK-NEXT: %19 = insertvalue [4 x float] %17, float %18, 3 -// CHECK-NEXT: %20 = insertvalue { float, [4 x float] } undef, float %_0, 0 -// CHECK-NEXT: %21 = insertvalue { float, [4 x float] } %20, [4 x float] %19, 1 -// CHECK-NEXT: ret { float, [4 x float] } %21 -// CHECK-NEXT: } +// CHECK-NEXT: %4 = fmul float %"_2'ipl", 2.000000e+00 +// CHECK-NEXT: %5 = fmul fast float %4, %x.0.val +// CHECK-NEXT: %6 = insertvalue [4 x float] undef, float %5, 0 +// CHECK-NEXT: %7 = fmul float %"_2'ipl1", 2.000000e+00 +// CHECK-NEXT: %8 = fmul fast float %7, %x.0.val +// CHECK-NEXT: %9 = insertvalue [4 x float] %6, float %8, 1 +// CHECK-NEXT: %10 = fmul float %"_2'ipl2", 2.000000e+00 +// CHECK-NEXT: %11 = fmul fast float %10, %x.0.val +// CHECK-NEXT: %12 = insertvalue [4 x float] %9, float %11, 2 +// CHECK-NEXT: %13 = fmul float %"_2'ipl3", 2.000000e+00 +// CHECK-NEXT: %14 = fmul fast float %13, %x.0.val +// CHECK-NEXT: %15 = insertvalue [4 x float] %12, float %14, 3 +// CHECK-NEXT: %16 = insertvalue { float, [4 x float] } undef, float %_0, 0 +// CHECK-NEXT: %17 = insertvalue { float, [4 x float] } %16, [4 x float] %15, 1 +// CHECK-NEXT: ret { float, [4 x float] } %17 +// CHECK-NEXT: } fn main() { let x = std::hint::black_box(3.0); diff --git a/tests/codegen-llvm/autodiff/generic.rs b/tests/codegen-llvm/autodiff/generic.rs index 2f674079be0..6f56460a2b6 100644 --- a/tests/codegen-llvm/autodiff/generic.rs +++ b/tests/codegen-llvm/autodiff/generic.rs @@ -6,27 +6,28 @@ use std::autodiff::autodiff_reverse; #[autodiff_reverse(d_square, Duplicated, Active)] +#[inline(never)] fn square<T: std::ops::Mul<Output = T> + Copy>(x: &T) -> T { *x * *x } -// Ensure that `d_square::<f64>` code is generated even if `square::<f64>` was never called +// Ensure that `d_square::<f32>` code is generated // // CHECK: ; generic::square -// CHECK-NEXT: ; Function Attrs: -// CHECK-NEXT: define internal {{.*}} double +// CHECK-NEXT: ; Function Attrs: {{.*}} +// CHECK-NEXT: define internal {{.*}} float // CHECK-NEXT: start: // CHECK-NOT: ret -// CHECK: fmul double +// CHECK: fmul float -// Ensure that `d_square::<f32>` code is generated +// Ensure that `d_square::<f64>` code is generated even if `square::<f64>` was never called // // CHECK: ; generic::square -// CHECK-NEXT: ; Function Attrs: {{.*}} -// CHECK-NEXT: define internal {{.*}} float +// CHECK-NEXT: ; Function Attrs: +// CHECK-NEXT: define internal {{.*}} double // CHECK-NEXT: start: // CHECK-NOT: ret -// CHECK: fmul float +// CHECK: fmul double fn main() { let xf32: f32 = std::hint::black_box(3.0); diff --git a/tests/codegen-llvm/autodiff/identical_fnc.rs b/tests/codegen-llvm/autodiff/identical_fnc.rs index 1c25b3d09ab..6066f8cb34f 100644 --- a/tests/codegen-llvm/autodiff/identical_fnc.rs +++ b/tests/codegen-llvm/autodiff/identical_fnc.rs @@ -14,25 +14,27 @@ use std::autodiff::autodiff_reverse; #[autodiff_reverse(d_square, Duplicated, Active)] +#[inline(never)] fn square(x: &f64) -> f64 { x * x } #[autodiff_reverse(d_square2, Duplicated, Active)] +#[inline(never)] fn square2(x: &f64) -> f64 { x * x } // CHECK:; identical_fnc::main // CHECK-NEXT:; Function Attrs: -// CHECK-NEXT:define internal void @_ZN13identical_fnc4main17hf4dbc69c8d2f9130E() +// CHECK-NEXT:define internal void @_ZN13identical_fnc4main17h6009e4f751bf9407E() // CHECK-NEXT:start: // CHECK-NOT:br // CHECK-NOT:ret // CHECK:; call identical_fnc::d_square -// CHECK-NEXT: call fastcc void @_ZN13identical_fnc8d_square17h4c364207a2f8e06dE(double %x.val, ptr noalias noundef nonnull align 8 dereferenceable(8) %dx1) -// CHECK-NEXT:; call identical_fnc::d_square -// CHECK-NEXT: call fastcc void @_ZN13identical_fnc8d_square17h4c364207a2f8e06dE(double %x.val, ptr noalias noundef nonnull align 8 dereferenceable(8) %dx2) +// CHECK-NEXT:call fastcc void @_ZN13identical_fnc8d_square17hcb5768e95528c35fE(double %x.val, ptr noalias noundef align 8 dereferenceable(8) %dx1) +// CHECK:; call identical_fnc::d_square +// CHECK-NEXT:call fastcc void @_ZN13identical_fnc8d_square17hcb5768e95528c35fE(double %x.val, ptr noalias noundef align 8 dereferenceable(8) %dx2) fn main() { let x = std::hint::black_box(3.0); diff --git a/tests/codegen-llvm/autodiff/inline.rs b/tests/codegen-llvm/autodiff/inline.rs deleted file mode 100644 index 65bed170207..00000000000 --- a/tests/codegen-llvm/autodiff/inline.rs +++ /dev/null @@ -1,23 +0,0 @@ -//@ compile-flags: -Zautodiff=Enable -C opt-level=3 -Clto=fat -Zautodiff=NoPostopt -//@ no-prefer-dynamic -//@ needs-enzyme - -#![feature(autodiff)] - -use std::autodiff::autodiff_reverse; - -#[autodiff_reverse(d_square, Duplicated, Active)] -fn square(x: &f64) -> f64 { - x * x -} - -// CHECK: ; inline::d_square -// CHECK-NEXT: ; Function Attrs: alwaysinline -// CHECK-NOT: noinline -// CHECK-NEXT: define internal fastcc void @_ZN6inline8d_square17h021c74e92c259cdeE -fn main() { - let x = std::hint::black_box(3.0); - let mut dx1 = std::hint::black_box(1.0); - let _ = d_square(&x, &mut dx1, 1.0); - assert_eq!(dx1, 6.0); -} diff --git a/tests/codegen-llvm/autodiff/scalar.rs b/tests/codegen-llvm/autodiff/scalar.rs index 096b4209e84..55b989f920d 100644 --- a/tests/codegen-llvm/autodiff/scalar.rs +++ b/tests/codegen-llvm/autodiff/scalar.rs @@ -7,11 +7,12 @@ use std::autodiff::autodiff_reverse; #[autodiff_reverse(d_square, Duplicated, Active)] #[no_mangle] +#[inline(never)] fn square(x: &f64) -> f64 { x * x } -// CHECK:define internal fastcc double @diffesquare(double %x.0.val, ptr nocapture nonnull align 8 %"x'" +// CHECK:define internal fastcc double @diffesquare(double %x.0.val, ptr nonnull align 8 captures(none) %"x'") // CHECK-NEXT:invertstart: // CHECK-NEXT: %_0 = fmul double %x.0.val, %x.0.val // CHECK-NEXT: %0 = fadd fast double %x.0.val, %x.0.val diff --git a/tests/codegen-llvm/autodiff/sret.rs b/tests/codegen-llvm/autodiff/sret.rs index d2fa85e3e37..dbc253ce894 100644 --- a/tests/codegen-llvm/autodiff/sret.rs +++ b/tests/codegen-llvm/autodiff/sret.rs @@ -13,30 +13,30 @@ use std::autodiff::autodiff_reverse; #[no_mangle] #[autodiff_reverse(df, Active, Active, Active)] +#[inline(never)] fn primal(x: f32, y: f32) -> f64 { (x * x * y) as f64 } -// CHECK:define internal fastcc void @_ZN4sret2df17h93be4316dd8ea006E(ptr dead_on_unwind noalias nocapture noundef nonnull writable writeonly align 8 dereferenceable(16) initializes((0, 16)) %_0, float noundef %x, float noundef %y) -// CHECK-NEXT:start: -// CHECK-NEXT: %0 = tail call fastcc { double, float, float } @diffeprimal(float %x, float %y) -// CHECK-NEXT: %.elt = extractvalue { double, float, float } %0, 0 -// CHECK-NEXT: store double %.elt, ptr %_0, align 8 -// CHECK-NEXT: %_0.repack1 = getelementptr inbounds nuw i8, ptr %_0, i64 8 -// CHECK-NEXT: %.elt2 = extractvalue { double, float, float } %0, 1 -// CHECK-NEXT: store float %.elt2, ptr %_0.repack1, align 8 -// CHECK-NEXT: %_0.repack3 = getelementptr inbounds nuw i8, ptr %_0, i64 12 -// CHECK-NEXT: %.elt4 = extractvalue { double, float, float } %0, 2 -// CHECK-NEXT: store float %.elt4, ptr %_0.repack3, align 4 -// CHECK-NEXT: ret void -// CHECK-NEXT:} +// CHECK: define internal fastcc { double, float, float } @diffeprimal(float noundef %x, float noundef %y) +// CHECK-NEXT: invertstart: +// CHECK-NEXT: %_4 = fmul float %x, %x +// CHECK-NEXT: %_3 = fmul float %_4, %y +// CHECK-NEXT: %_0 = fpext float %_3 to double +// CHECK-NEXT: %0 = fadd fast float %y, %y +// CHECK-NEXT: %1 = fmul fast float %0, %x +// CHECK-NEXT: %2 = insertvalue { double, float, float } undef, double %_0, 0 +// CHECK-NEXT: %3 = insertvalue { double, float, float } %2, float %1, 1 +// CHECK-NEXT: %4 = insertvalue { double, float, float } %3, float %_4, 2 +// CHECK-NEXT: ret { double, float, float } %4 +// CHECK-NEXT: } fn main() { let x = std::hint::black_box(3.0); let y = std::hint::black_box(2.5); let scalar = std::hint::black_box(1.0); let (r1, r2, r3) = df(x, y, scalar); - // 3*3*1.5 = 22.5 + // 3*3*2.5 = 22.5 assert_eq!(r1, 22.5); // 2*x*y = 2*3*2.5 = 15.0 assert_eq!(r2, 15.0); diff --git a/tests/codegen-llvm/autodiff/trait.rs b/tests/codegen-llvm/autodiff/trait.rs new file mode 100644 index 00000000000..701f3a9e843 --- /dev/null +++ b/tests/codegen-llvm/autodiff/trait.rs @@ -0,0 +1,30 @@ +//@ compile-flags: -Zautodiff=Enable -Zautodiff=NoPostopt -C opt-level=3 -Clto=fat +//@ no-prefer-dynamic +//@ needs-enzyme + +// Just check it does not crash for now +// CHECK: ; +#![feature(autodiff)] + +use std::autodiff::autodiff_reverse; + +struct Foo { + a: f64, +} + +trait MyTrait { + fn f(&self, x: f64) -> f64; + fn df(&self, x: f64, seed: f64) -> (f64, f64); +} + +impl MyTrait for Foo { + #[autodiff_reverse(df, Const, Active, Active)] + fn f(&self, x: f64) -> f64 { + self.a * 0.25 * (x * x - 1.0 - 2.0 * x.ln()) + } +} + +fn main() { + let foo = Foo { a: 3.0f64 }; + dbg!(foo.df(1.0, 1.0)); +} diff --git a/tests/codegen-llvm/call-tmps-lifetime.rs b/tests/codegen-llvm/call-tmps-lifetime.rs index 7b7b6e17bdd..0d7657ed758 100644 --- a/tests/codegen-llvm/call-tmps-lifetime.rs +++ b/tests/codegen-llvm/call-tmps-lifetime.rs @@ -16,14 +16,14 @@ use minicore::*; // CHECK-NEXT: start: // CHECK-NEXT: [[B:%.*]] = alloca // CHECK-NEXT: [[A:%.*]] = alloca -// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4096, ptr [[A]]) +// CHECK-NEXT: call void @llvm.lifetime.start.p0({{(i64 4096, )?}}ptr [[A]]) // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[A]], ptr align 4 {{.*}}, i32 4096, i1 false) // CHECK-NEXT: call void %h(ptr {{.*}} [[A]]) -// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 4096, ptr [[A]]) -// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4096, ptr [[B]]) +// CHECK-NEXT: call void @llvm.lifetime.end.p0({{(i64 4096, )?}}ptr [[A]]) +// CHECK-NEXT: call void @llvm.lifetime.start.p0({{(i64 4096, )?}}ptr [[B]]) // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[B]], ptr align 4 {{.*}}, i32 4096, i1 false) // CHECK-NEXT: call void %h(ptr {{.*}} [[B]]) -// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 4096, ptr [[B]]) +// CHECK-NEXT: call void @llvm.lifetime.end.p0({{(i64 4096, )?}}ptr [[B]]) #[no_mangle] pub fn const_indirect(h: extern "C" fn([u32; 1024])) { const C: [u32; 1024] = [0; 1024]; @@ -42,12 +42,12 @@ pub struct Str { // CHECK-LABEL: define void @immediate_indirect(ptr {{.*}}%s.0, i32 {{.*}}%s.1, ptr {{.*}}%g) // CHECK-NEXT: start: // CHECK-NEXT: [[A:%.*]] = alloca -// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr [[A]]) +// CHECK-NEXT: call void @llvm.lifetime.start.p0({{(i64 8, )?}}ptr [[A]]) // CHECK-NEXT: store ptr %s.0, ptr [[A]] // CHECK-NEXT: [[B:%.]] = getelementptr inbounds i8, ptr [[A]], i32 4 // CHECK-NEXT: store i32 %s.1, ptr [[B]] // CHECK-NEXT: call void %g(ptr {{.*}} [[A]]) -// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[A]]) +// CHECK-NEXT: call void @llvm.lifetime.end.p0({{(i64 8, )?}}ptr [[A]]) #[no_mangle] pub fn immediate_indirect(s: Str, g: extern "C" fn(Str)) { g(s); @@ -58,10 +58,10 @@ pub fn immediate_indirect(s: Str, g: extern "C" fn(Str)) { // CHECK-LABEL: define void @align_indirect(ptr{{.*}} align 1{{.*}} %a, ptr{{.*}} %fun) // CHECK-NEXT: start: // CHECK-NEXT: [[A:%.*]] = alloca [1024 x i8], align 4 -// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 1024, ptr [[A]]) +// CHECK-NEXT: call void @llvm.lifetime.start.p0({{(i64 1024, )?}}ptr [[A]]) // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[A]], ptr align 1 %a, i32 1024, i1 false) // CHECK-NEXT: call void %fun(ptr {{.*}} [[A]]) -// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 1024, ptr [[A]]) +// CHECK-NEXT: call void @llvm.lifetime.end.p0({{(i64 1024, )?}}ptr [[A]]) #[no_mangle] pub fn align_indirect(a: [u8; 1024], fun: extern "C" fn([u8; 1024])) { fun(a); diff --git a/tests/codegen-llvm/intrinsics/transmute-simd.rs b/tests/codegen-llvm/intrinsics/transmute-simd.rs index e34b27e1333..92af16945ab 100644 --- a/tests/codegen-llvm/intrinsics/transmute-simd.rs +++ b/tests/codegen-llvm/intrinsics/transmute-simd.rs @@ -97,10 +97,10 @@ pub extern "C" fn float_ptr_same_lanes(v: f64x2) -> PtrX2 { // CHECK-NOT: alloca // CHECK: %[[TEMP:.+]] = alloca [16 x i8] // CHECK-NOT: alloca - // CHECK: call void @llvm.lifetime.start.p0(i64 16, ptr %[[TEMP]]) + // CHECK: call void @llvm.lifetime.start.p0({{(i64 16, )?}}ptr %[[TEMP]]) // CHECK: store <2 x double> %v, ptr %[[TEMP]] // CHECK: %[[RET:.+]] = load <2 x ptr>, ptr %[[TEMP]] - // CHECK: call void @llvm.lifetime.end.p0(i64 16, ptr %[[TEMP]]) + // CHECK: call void @llvm.lifetime.end.p0({{(i64 16, )?}}ptr %[[TEMP]]) // CHECK: ret <2 x ptr> %[[RET]] unsafe { transmute(v) } } @@ -111,10 +111,10 @@ pub extern "C" fn ptr_float_same_lanes(v: PtrX2) -> f64x2 { // CHECK-NOT: alloca // CHECK: %[[TEMP:.+]] = alloca [16 x i8] // CHECK-NOT: alloca - // CHECK: call void @llvm.lifetime.start.p0(i64 16, ptr %[[TEMP]]) + // CHECK: call void @llvm.lifetime.start.p0({{(i64 16, )?}}ptr %[[TEMP]]) // CHECK: store <2 x ptr> %v, ptr %[[TEMP]] // CHECK: %[[RET:.+]] = load <2 x double>, ptr %[[TEMP]] - // CHECK: call void @llvm.lifetime.end.p0(i64 16, ptr %[[TEMP]]) + // CHECK: call void @llvm.lifetime.end.p0({{(i64 16, )?}}ptr %[[TEMP]]) // CHECK: ret <2 x double> %[[RET]] unsafe { transmute(v) } } @@ -125,10 +125,10 @@ pub extern "C" fn int_ptr_same_lanes(v: i64x2) -> PtrX2 { // CHECK-NOT: alloca // CHECK: %[[TEMP:.+]] = alloca [16 x i8] // CHECK-NOT: alloca - // CHECK: call void @llvm.lifetime.start.p0(i64 16, ptr %[[TEMP]]) + // CHECK: call void @llvm.lifetime.start.p0({{(i64 16, )?}}ptr %[[TEMP]]) // CHECK: store <2 x i64> %v, ptr %[[TEMP]] // CHECK: %[[RET:.+]] = load <2 x ptr>, ptr %[[TEMP]] - // CHECK: call void @llvm.lifetime.end.p0(i64 16, ptr %[[TEMP]]) + // CHECK: call void @llvm.lifetime.end.p0({{(i64 16, )?}}ptr %[[TEMP]]) // CHECK: ret <2 x ptr> %[[RET]] unsafe { transmute(v) } } @@ -139,10 +139,10 @@ pub extern "C" fn ptr_int_same_lanes(v: PtrX2) -> i64x2 { // CHECK-NOT: alloca // CHECK: %[[TEMP:.+]] = alloca [16 x i8] // CHECK-NOT: alloca - // CHECK: call void @llvm.lifetime.start.p0(i64 16, ptr %[[TEMP]]) + // CHECK: call void @llvm.lifetime.start.p0({{(i64 16, )?}}ptr %[[TEMP]]) // CHECK: store <2 x ptr> %v, ptr %[[TEMP]] // CHECK: %[[RET:.+]] = load <2 x i64>, ptr %[[TEMP]] - // CHECK: call void @llvm.lifetime.end.p0(i64 16, ptr %[[TEMP]]) + // CHECK: call void @llvm.lifetime.end.p0({{(i64 16, )?}}ptr %[[TEMP]]) // CHECK: ret <2 x i64> %[[RET]] unsafe { transmute(v) } } @@ -153,10 +153,10 @@ pub extern "C" fn float_ptr_widen(v: f32x4) -> PtrX2 { // CHECK-NOT: alloca // CHECK: %[[TEMP:.+]] = alloca [16 x i8] // CHECK-NOT: alloca - // CHECK: call void @llvm.lifetime.start.p0(i64 16, ptr %[[TEMP]]) + // CHECK: call void @llvm.lifetime.start.p0({{(i64 16, )?}}ptr %[[TEMP]]) // CHECK: store <4 x float> %v, ptr %[[TEMP]] // CHECK: %[[RET:.+]] = load <2 x ptr>, ptr %[[TEMP]] - // CHECK: call void @llvm.lifetime.end.p0(i64 16, ptr %[[TEMP]]) + // CHECK: call void @llvm.lifetime.end.p0({{(i64 16, )?}}ptr %[[TEMP]]) // CHECK: ret <2 x ptr> %[[RET]] unsafe { transmute(v) } } @@ -167,10 +167,10 @@ pub extern "C" fn int_ptr_widen(v: i32x4) -> PtrX2 { // CHECK-NOT: alloca // CHECK: %[[TEMP:.+]] = alloca [16 x i8] // CHECK-NOT: alloca - // CHECK: call void @llvm.lifetime.start.p0(i64 16, ptr %[[TEMP]]) + // CHECK: call void @llvm.lifetime.start.p0({{(i64 16, )?}}ptr %[[TEMP]]) // CHECK: store <4 x i32> %v, ptr %[[TEMP]] // CHECK: %[[RET:.+]] = load <2 x ptr>, ptr %[[TEMP]] - // CHECK: call void @llvm.lifetime.end.p0(i64 16, ptr %[[TEMP]]) + // CHECK: call void @llvm.lifetime.end.p0({{(i64 16, )?}}ptr %[[TEMP]]) // CHECK: ret <2 x ptr> %[[RET]] unsafe { transmute(v) } } diff --git a/tests/codegen-llvm/intrinsics/transmute.rs b/tests/codegen-llvm/intrinsics/transmute.rs index 91cff38773d..e327c100d7d 100644 --- a/tests/codegen-llvm/intrinsics/transmute.rs +++ b/tests/codegen-llvm/intrinsics/transmute.rs @@ -192,12 +192,12 @@ pub unsafe fn check_byte_from_bool(x: bool) -> u8 { #[no_mangle] pub unsafe fn check_to_pair(x: u64) -> Option<i32> { // CHECK: %[[TEMP:.+]] = alloca [8 x i8], align 8 - // CHECK: call void @llvm.lifetime.start.p0(i64 8, ptr %[[TEMP]]) + // CHECK: call void @llvm.lifetime.start.p0({{(i64 8, )?}}ptr %[[TEMP]]) // CHECK: store i64 %x, ptr %[[TEMP]], align 8 // CHECK: %[[PAIR0:.+]] = load i32, ptr %[[TEMP]], align 8 // CHECK: %[[PAIR1P:.+]] = getelementptr inbounds i8, ptr %[[TEMP]], i64 4 // CHECK: %[[PAIR1:.+]] = load i32, ptr %[[PAIR1P]], align 4 - // CHECK: call void @llvm.lifetime.end.p0(i64 8, ptr %[[TEMP]]) + // CHECK: call void @llvm.lifetime.end.p0({{(i64 8, )?}}ptr %[[TEMP]]) // CHECK: insertvalue {{.+}}, i32 %[[PAIR0]], 0 // CHECK: insertvalue {{.+}}, i32 %[[PAIR1]], 1 transmute(x) @@ -207,12 +207,12 @@ pub unsafe fn check_to_pair(x: u64) -> Option<i32> { #[no_mangle] pub unsafe fn check_from_pair(x: Option<i32>) -> u64 { // CHECK: %[[TEMP:.+]] = alloca [8 x i8], align 8 - // CHECK: call void @llvm.lifetime.start.p0(i64 8, ptr %[[TEMP]]) + // CHECK: call void @llvm.lifetime.start.p0({{(i64 8, )?}}ptr %[[TEMP]]) // CHECK: store i32 %x.0, ptr %[[TEMP]], align 8 // CHECK: %[[PAIR1P:.+]] = getelementptr inbounds i8, ptr %[[TEMP]], i64 4 // CHECK: store i32 %x.1, ptr %[[PAIR1P]], align 4 // CHECK: %[[R:.+]] = load i64, ptr %[[TEMP]], align 8 - // CHECK: call void @llvm.lifetime.end.p0(i64 8, ptr %[[TEMP]]) + // CHECK: call void @llvm.lifetime.end.p0({{(i64 8, )?}}ptr %[[TEMP]]) // CHECK: ret i64 %[[R]] transmute(x) } diff --git a/tests/codegen-llvm/issues/issue-105386-ub-in-debuginfo.rs b/tests/codegen-llvm/issues/issue-105386-ub-in-debuginfo.rs index 848aa910b58..95e70bf5678 100644 --- a/tests/codegen-llvm/issues/issue-105386-ub-in-debuginfo.rs +++ b/tests/codegen-llvm/issues/issue-105386-ub-in-debuginfo.rs @@ -19,6 +19,6 @@ pub fn outer_function(x: S, y: S) -> usize { // CHECK: [[spill:%.*]] = alloca // CHECK-NOT: [[ptr_tmp:%.*]] = getelementptr inbounds i8, ptr [[spill]] // CHECK-NOT: [[load:%.*]] = load ptr, ptr -// CHECK: call void @llvm.lifetime.start{{.*}}({{.*}}, ptr [[spill]]) +// CHECK: call void @llvm.lifetime.start{{.*}}({{(.*, )?}}ptr [[spill]]) // CHECK: [[inner:%.*]] = getelementptr inbounds i8, ptr [[spill]] // CHECK: call void @llvm.memcpy{{.*}}(ptr {{align .*}} [[inner]], ptr {{align .*}} %x diff --git a/tests/codegen-llvm/lifetime_start_end.rs b/tests/codegen-llvm/lifetime_start_end.rs index 0639e7640aa..2df5acf54df 100644 --- a/tests/codegen-llvm/lifetime_start_end.rs +++ b/tests/codegen-llvm/lifetime_start_end.rs @@ -8,27 +8,27 @@ pub fn test() { let a = 0u8; &a; // keep variable in an alloca - // CHECK: call void @llvm.lifetime.start{{.*}}(i{{[0-9 ]+}}, ptr %a) + // CHECK: call void @llvm.lifetime.start{{.*}}({{(i[0-9 ]+, )?}}ptr %a) { let b = &Some(a); &b; // keep variable in an alloca - // CHECK: call void @llvm.lifetime.start{{.*}}(i{{[0-9 ]+}}, {{.*}}) + // CHECK: call void @llvm.lifetime.start{{.*}}({{(i[0-9 ]+, )?}}{{.*}}) - // CHECK: call void @llvm.lifetime.start{{.*}}(i{{[0-9 ]+}}, {{.*}}) + // CHECK: call void @llvm.lifetime.start{{.*}}({{(i[0-9 ]+, )?}}{{.*}}) - // CHECK: call void @llvm.lifetime.end{{.*}}(i{{[0-9 ]+}}, {{.*}}) + // CHECK: call void @llvm.lifetime.end{{.*}}({{(i[0-9 ]+, )?}}{{.*}}) - // CHECK: call void @llvm.lifetime.end{{.*}}(i{{[0-9 ]+}}, {{.*}}) + // CHECK: call void @llvm.lifetime.end{{.*}}({{(i[0-9 ]+, )?}}{{.*}}) } let c = 1u8; &c; // keep variable in an alloca - // CHECK: call void @llvm.lifetime.start{{.*}}(i{{[0-9 ]+}}, ptr %c) + // CHECK: call void @llvm.lifetime.start{{.*}}({{(i[0-9 ]+, )?}}ptr %c) - // CHECK: call void @llvm.lifetime.end{{.*}}(i{{[0-9 ]+}}, ptr %c) + // CHECK: call void @llvm.lifetime.end{{.*}}({{(i[0-9 ]+, )?}}ptr %c) - // CHECK: call void @llvm.lifetime.end{{.*}}(i{{[0-9 ]+}}, ptr %a) + // CHECK: call void @llvm.lifetime.end{{.*}}({{(i[0-9 ]+, )?}}ptr %a) } diff --git a/tests/codegen-llvm/uninhabited-transparent-return-abi.rs b/tests/codegen-llvm/uninhabited-transparent-return-abi.rs index face1577c3f..83d9a7a32ab 100644 --- a/tests/codegen-llvm/uninhabited-transparent-return-abi.rs +++ b/tests/codegen-llvm/uninhabited-transparent-return-abi.rs @@ -23,7 +23,7 @@ extern "Rust" { #[no_mangle] pub fn test_uninhabited_ret_by_ref() { // CHECK: %_1 = alloca [24 x i8], align {{8|4}} - // CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %_1) + // CHECK-NEXT: call void @llvm.lifetime.start.p0({{(i64 24, )?}}ptr nonnull %_1) // CHECK-NEXT: call void @opaque({{.*}} sret([24 x i8]) {{.*}} %_1) #2 // CHECK-NEXT: unreachable unsafe { @@ -35,7 +35,7 @@ pub fn test_uninhabited_ret_by_ref() { #[no_mangle] pub fn test_uninhabited_ret_by_ref_with_arg(rsi: u32) { // CHECK: %_2 = alloca [24 x i8], align {{8|4}} - // CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %_2) + // CHECK-NEXT: call void @llvm.lifetime.start.p0({{(i64 24, )?}}ptr nonnull %_2) // CHECK-NEXT: call void @opaque_with_arg({{.*}} sret([24 x i8]) {{.*}} %_2, i32 noundef %rsi) #2 // CHECK-NEXT: unreachable unsafe { diff --git a/tests/crashes/117629.rs b/tests/crashes/117629.rs deleted file mode 100644 index f63365395c6..00000000000 --- a/tests/crashes/117629.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@ known-bug: #117629 -//@ edition:2021 - -#![feature(const_trait_impl)] - -const trait Tr { - async fn ft1() {} -} - -fn main() {} diff --git a/tests/pretty/autodiff/autodiff_forward.pp b/tests/pretty/autodiff/autodiff_forward.pp index a2525abc832..6eddb5669c7 100644 --- a/tests/pretty/autodiff/autodiff_forward.pp +++ b/tests/pretty/autodiff/autodiff_forward.pp @@ -3,10 +3,10 @@ //@ needs-enzyme #![feature(autodiff)] -#[prelude_import] -use ::std::prelude::rust_2015::*; #[macro_use] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; //@ pretty-mode:expanded //@ pretty-compare-only //@ pp-exact:autodiff_forward.pp @@ -16,7 +16,6 @@ extern crate std; use std::autodiff::{autodiff_forward, autodiff_reverse}; #[rustc_autodiff] -#[inline(never)] pub fn f1(x: &[f64], y: f64) -> f64 { @@ -36,163 +35,96 @@ pub fn f1(x: &[f64], y: f64) -> f64 { ::core::panicking::panic("not implemented") } #[rustc_autodiff(Forward, 1, Dual, Const, Dual)] -#[inline(never)] pub fn df1(x: &[f64], bx_0: &[f64], y: f64) -> (f64, f64) { - unsafe { asm!("NOP", options(pure, nomem)); }; - ::core::hint::black_box(f1(x, y)); - ::core::hint::black_box((bx_0,)); - ::core::hint::black_box(<(f64, f64)>::default()) + ::core::intrinsics::autodiff(f1::<>, df1::<>, (x, bx_0, y)) } #[rustc_autodiff] -#[inline(never)] pub fn f2(x: &[f64], y: f64) -> f64 { ::core::panicking::panic("not implemented") } #[rustc_autodiff(Forward, 1, Dual, Const, Const)] -#[inline(never)] pub fn df2(x: &[f64], bx_0: &[f64], y: f64) -> f64 { - unsafe { asm!("NOP", options(pure, nomem)); }; - ::core::hint::black_box(f2(x, y)); - ::core::hint::black_box((bx_0,)); - ::core::hint::black_box(f2(x, y)) + ::core::intrinsics::autodiff(f2::<>, df2::<>, (x, bx_0, y)) } #[rustc_autodiff] -#[inline(never)] pub fn f3(x: &[f64], y: f64) -> f64 { ::core::panicking::panic("not implemented") } #[rustc_autodiff(Forward, 1, Dual, Const, Const)] -#[inline(never)] pub fn df3(x: &[f64], bx_0: &[f64], y: f64) -> f64 { - unsafe { asm!("NOP", options(pure, nomem)); }; - ::core::hint::black_box(f3(x, y)); - ::core::hint::black_box((bx_0,)); - ::core::hint::black_box(f3(x, y)) + ::core::intrinsics::autodiff(f3::<>, df3::<>, (x, bx_0, y)) } #[rustc_autodiff] -#[inline(never)] pub fn f4() {} #[rustc_autodiff(Forward, 1, None)] -#[inline(never)] -pub fn df4() -> () { - unsafe { asm!("NOP", options(pure, nomem)); }; - ::core::hint::black_box(f4()); - ::core::hint::black_box(()); -} +pub fn df4() -> () { ::core::intrinsics::autodiff(f4::<>, df4::<>, ()) } #[rustc_autodiff] -#[inline(never)] pub fn f5(x: &[f64], y: f64) -> f64 { ::core::panicking::panic("not implemented") } #[rustc_autodiff(Forward, 1, Const, Dual, Const)] -#[inline(never)] pub fn df5_y(x: &[f64], y: f64, by_0: f64) -> f64 { - unsafe { asm!("NOP", options(pure, nomem)); }; - ::core::hint::black_box(f5(x, y)); - ::core::hint::black_box((by_0,)); - ::core::hint::black_box(f5(x, y)) + ::core::intrinsics::autodiff(f5::<>, df5_y::<>, (x, y, by_0)) } #[rustc_autodiff(Forward, 1, Dual, Const, Const)] -#[inline(never)] pub fn df5_x(x: &[f64], bx_0: &[f64], y: f64) -> f64 { - unsafe { asm!("NOP", options(pure, nomem)); }; - ::core::hint::black_box(f5(x, y)); - ::core::hint::black_box((bx_0,)); - ::core::hint::black_box(f5(x, y)) + ::core::intrinsics::autodiff(f5::<>, df5_x::<>, (x, bx_0, y)) } #[rustc_autodiff(Reverse, 1, Duplicated, Const, Active)] -#[inline(never)] pub fn df5_rev(x: &[f64], dx_0: &mut [f64], y: f64, dret: f64) -> f64 { - unsafe { asm!("NOP", options(pure, nomem)); }; - ::core::hint::black_box(f5(x, y)); - ::core::hint::black_box((dx_0, dret)); - ::core::hint::black_box(f5(x, y)) + ::core::intrinsics::autodiff(f5::<>, df5_rev::<>, (x, dx_0, y, dret)) } struct DoesNotImplDefault; #[rustc_autodiff] -#[inline(never)] pub fn f6() -> DoesNotImplDefault { ::core::panicking::panic("not implemented") } #[rustc_autodiff(Forward, 1, Const)] -#[inline(never)] pub fn df6() -> DoesNotImplDefault { - unsafe { asm!("NOP", options(pure, nomem)); }; - ::core::hint::black_box(f6()); - ::core::hint::black_box(()); - ::core::hint::black_box(f6()) + ::core::intrinsics::autodiff(f6::<>, df6::<>, ()) } #[rustc_autodiff] -#[inline(never)] pub fn f7(x: f32) -> () {} #[rustc_autodiff(Forward, 1, Const, None)] -#[inline(never)] pub fn df7(x: f32) -> () { - unsafe { asm!("NOP", options(pure, nomem)); }; - ::core::hint::black_box(f7(x)); - ::core::hint::black_box(()); + ::core::intrinsics::autodiff(f7::<>, df7::<>, (x,)) } #[no_mangle] #[rustc_autodiff] -#[inline(never)] fn f8(x: &f32) -> f32 { ::core::panicking::panic("not implemented") } #[rustc_autodiff(Forward, 4, Dual, Dual)] -#[inline(never)] fn f8_3(x: &f32, bx_0: &f32, bx_1: &f32, bx_2: &f32, bx_3: &f32) -> [f32; 5usize] { - unsafe { asm!("NOP", options(pure, nomem)); }; - ::core::hint::black_box(f8(x)); - ::core::hint::black_box((bx_0, bx_1, bx_2, bx_3)); - ::core::hint::black_box(<[f32; 5usize]>::default()) + ::core::intrinsics::autodiff(f8::<>, f8_3::<>, + (x, bx_0, bx_1, bx_2, bx_3)) } #[rustc_autodiff(Forward, 4, Dual, DualOnly)] -#[inline(never)] fn f8_2(x: &f32, bx_0: &f32, bx_1: &f32, bx_2: &f32, bx_3: &f32) -> [f32; 4usize] { - unsafe { asm!("NOP", options(pure, nomem)); }; - ::core::hint::black_box(f8(x)); - ::core::hint::black_box((bx_0, bx_1, bx_2, bx_3)); - ::core::hint::black_box(<[f32; 4usize]>::default()) + ::core::intrinsics::autodiff(f8::<>, f8_2::<>, + (x, bx_0, bx_1, bx_2, bx_3)) } #[rustc_autodiff(Forward, 1, Dual, DualOnly)] -#[inline(never)] fn f8_1(x: &f32, bx_0: &f32) -> f32 { - unsafe { asm!("NOP", options(pure, nomem)); }; - ::core::hint::black_box(f8(x)); - ::core::hint::black_box((bx_0,)); - ::core::hint::black_box(<f32>::default()) + ::core::intrinsics::autodiff(f8::<>, f8_1::<>, (x, bx_0)) } pub fn f9() { #[rustc_autodiff] - #[inline(never)] fn inner(x: f32) -> f32 { x * x } #[rustc_autodiff(Forward, 1, Dual, Dual)] - #[inline(never)] fn d_inner_2(x: f32, bx_0: f32) -> (f32, f32) { - unsafe { asm!("NOP", options(pure, nomem)); }; - ::core::hint::black_box(inner(x)); - ::core::hint::black_box((bx_0,)); - ::core::hint::black_box(<(f32, f32)>::default()) + ::core::intrinsics::autodiff(inner::<>, d_inner_2::<>, (x, bx_0)) } #[rustc_autodiff(Forward, 1, Dual, DualOnly)] - #[inline(never)] fn d_inner_1(x: f32, bx_0: f32) -> f32 { - unsafe { asm!("NOP", options(pure, nomem)); }; - ::core::hint::black_box(inner(x)); - ::core::hint::black_box((bx_0,)); - ::core::hint::black_box(<f32>::default()) + ::core::intrinsics::autodiff(inner::<>, d_inner_1::<>, (x, bx_0)) } } #[rustc_autodiff] -#[inline(never)] pub fn f10<T: std::ops::Mul<Output = T> + Copy>(x: &T) -> T { *x * *x } #[rustc_autodiff(Reverse, 1, Duplicated, Active)] -#[inline(never)] pub fn d_square<T: std::ops::Mul<Output = T> + Copy>(x: &T, dx_0: &mut T, dret: T) -> T { - unsafe { asm!("NOP", options(pure, nomem)); }; - ::core::hint::black_box(f10::<T>(x)); - ::core::hint::black_box((dx_0, dret)); - ::core::hint::black_box(f10::<T>(x)) + ::core::intrinsics::autodiff(f10::<T>, d_square::<T>, (x, dx_0, dret)) } fn main() {} diff --git a/tests/pretty/autodiff/autodiff_reverse.pp b/tests/pretty/autodiff/autodiff_reverse.pp index e67c3443dde..8f598b865c7 100644 --- a/tests/pretty/autodiff/autodiff_reverse.pp +++ b/tests/pretty/autodiff/autodiff_reverse.pp @@ -3,10 +3,10 @@ //@ needs-enzyme #![feature(autodiff)] -#[prelude_import] -use ::std::prelude::rust_2015::*; #[macro_use] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; //@ pretty-mode:expanded //@ pretty-compare-only //@ pp-exact:autodiff_reverse.pp @@ -16,7 +16,6 @@ extern crate std; use std::autodiff::autodiff_reverse; #[rustc_autodiff] -#[inline(never)] pub fn f1(x: &[f64], y: f64) -> f64 { // Not the most interesting derivative, but who are we to judge @@ -29,58 +28,33 @@ pub fn f1(x: &[f64], y: f64) -> f64 { ::core::panicking::panic("not implemented") } #[rustc_autodiff(Reverse, 1, Duplicated, Const, Active)] -#[inline(never)] pub fn df1(x: &[f64], dx_0: &mut [f64], y: f64, dret: f64) -> f64 { - unsafe { asm!("NOP", options(pure, nomem)); }; - ::core::hint::black_box(f1(x, y)); - ::core::hint::black_box((dx_0, dret)); - ::core::hint::black_box(f1(x, y)) + ::core::intrinsics::autodiff(f1::<>, df1::<>, (x, dx_0, y, dret)) } #[rustc_autodiff] -#[inline(never)] pub fn f2() {} #[rustc_autodiff(Reverse, 1, None)] -#[inline(never)] -pub fn df2() { - unsafe { asm!("NOP", options(pure, nomem)); }; - ::core::hint::black_box(f2()); - ::core::hint::black_box(()); -} +pub fn df2() { ::core::intrinsics::autodiff(f2::<>, df2::<>, ()) } #[rustc_autodiff] -#[inline(never)] pub fn f3(x: &[f64], y: f64) -> f64 { ::core::panicking::panic("not implemented") } #[rustc_autodiff(Reverse, 1, Duplicated, Const, Active)] -#[inline(never)] pub fn df3(x: &[f64], dx_0: &mut [f64], y: f64, dret: f64) -> f64 { - unsafe { asm!("NOP", options(pure, nomem)); }; - ::core::hint::black_box(f3(x, y)); - ::core::hint::black_box((dx_0, dret)); - ::core::hint::black_box(f3(x, y)) + ::core::intrinsics::autodiff(f3::<>, df3::<>, (x, dx_0, y, dret)) } enum Foo { Reverse, } use Foo::Reverse; #[rustc_autodiff] -#[inline(never)] pub fn f4(x: f32) { ::core::panicking::panic("not implemented") } #[rustc_autodiff(Reverse, 1, Const, None)] -#[inline(never)] -pub fn df4(x: f32) { - unsafe { asm!("NOP", options(pure, nomem)); }; - ::core::hint::black_box(f4(x)); - ::core::hint::black_box(()); -} +pub fn df4(x: f32) { ::core::intrinsics::autodiff(f4::<>, df4::<>, (x,)) } #[rustc_autodiff] -#[inline(never)] pub fn f5(x: *const f32, y: &f32) { ::core::panicking::panic("not implemented") } #[rustc_autodiff(Reverse, 1, DuplicatedOnly, Duplicated, None)] -#[inline(never)] pub unsafe fn df5(x: *const f32, dx_0: *mut f32, y: &f32, dy_0: &mut f32) { - unsafe { asm!("NOP", options(pure, nomem)); }; - ::core::hint::black_box(f5(x, y)); - ::core::hint::black_box((dx_0, dy_0)); + ::core::intrinsics::autodiff(f5::<>, df5::<>, (x, dx_0, y, dy_0)) } fn main() {} diff --git a/tests/pretty/autodiff/autodiff_reverse.rs b/tests/pretty/autodiff/autodiff_reverse.rs index d37e5e3eb4c..c50b81d7780 100644 --- a/tests/pretty/autodiff/autodiff_reverse.rs +++ b/tests/pretty/autodiff/autodiff_reverse.rs @@ -23,7 +23,9 @@ pub fn f3(x: &[f64], y: f64) -> f64 { unimplemented!() } -enum Foo { Reverse } +enum Foo { + Reverse, +} use Foo::Reverse; // What happens if we already have Reverse in type (enum variant decl) and value (enum variant // constructor) namespace? > It's expected to work normally. diff --git a/tests/pretty/autodiff/inherent_impl.pp b/tests/pretty/autodiff/inherent_impl.pp index d18061b2dbd..36a9222640a 100644 --- a/tests/pretty/autodiff/inherent_impl.pp +++ b/tests/pretty/autodiff/inherent_impl.pp @@ -3,10 +3,10 @@ //@ needs-enzyme #![feature(autodiff)] -#[prelude_import] -use ::std::prelude::rust_2015::*; #[macro_use] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; //@ pretty-mode:expanded //@ pretty-compare-only //@ pp-exact:inherent_impl.pp @@ -26,16 +26,12 @@ trait MyTrait { impl MyTrait for Foo { #[rustc_autodiff] - #[inline(never)] fn f(&self, x: f64) -> f64 { self.a * 0.25 * (x * x - 1.0 - 2.0 * x.ln()) } #[rustc_autodiff(Reverse, 1, Const, Active, Active)] - #[inline(never)] fn df(&self, x: f64, dret: f64) -> (f64, f64) { - unsafe { asm!("NOP", options(pure, nomem)); }; - ::core::hint::black_box(self.f(x)); - ::core::hint::black_box((dret,)); - ::core::hint::black_box((self.f(x), f64::default())) + ::core::intrinsics::autodiff(Self::f::<>, Self::df::<>, + (self, x, dret)) } } diff --git a/tests/run-make/lto-long-filenames/main.rs b/tests/run-make/lto-long-filenames/main.rs new file mode 100644 index 00000000000..daedff5c05f --- /dev/null +++ b/tests/run-make/lto-long-filenames/main.rs @@ -0,0 +1,7 @@ +// This file has very long lines, but there is no way to avoid it as we are testing +// long crate names. so: +// ignore-tidy-linelength + +extern crate generated_large_large_large_large_large_large_large_large_large_large_large_large_large_large_large_large_large_crate_name; + +fn main() {} diff --git a/tests/run-make/lto-long-filenames/rmake.rs b/tests/run-make/lto-long-filenames/rmake.rs new file mode 100644 index 00000000000..9e0ba63e9f5 --- /dev/null +++ b/tests/run-make/lto-long-filenames/rmake.rs @@ -0,0 +1,32 @@ +// This file has very long lines, but there is no way to avoid it as we are testing +// long crate names. so: +// ignore-tidy-linelength + +// A variant of the smoke test to check that link time optimization +// (LTO) is accepted by the compiler, and that +// passing its various flags still results in successful compilation, even for very long crate names. +// See https://github.com/rust-lang/rust/issues/49914 + +//@ ignore-cross-compile + +use std::fs; + +use run_make_support::{rfs, rustc}; + +// This test make sure we don't get such following error: +// error: could not write output to generated_large_large_large_large_large_large_large_large_large_large_large_large_large_large_large_large_large_crate_name.generated_large_large_large_large_large_large_large_large_large_large_large_large_large_large_large_large_large_crate_name.9384edb61bfd127c-cgu.0.rcgu.o: File name too long +// as reported in issue #49914 +fn main() { + let lto_flags = ["-Clto", "-Clto=yes", "-Clto=off", "-Clto=thin", "-Clto=fat"]; + let aux_file = "generated_large_large_large_large_large_large_large_large_large_large_large_large_large_large_large_large_large_crate_name.rs"; + // The auxiliary file is used to test long crate names. + // The file name is intentionally long to test the handling of long filenames. + // We don't commit it to avoid issues with Windows paths which have known limitations for the full path length. + // Posix usually only have a limit for the length of the file name. + rfs::write(aux_file, "#![crate_type = \"rlib\"]\n"); + + for flag in lto_flags { + rustc().input(aux_file).arg(flag).run(); + rustc().input("main.rs").arg(flag).run(); + } +} diff --git a/tests/run-make/wasm-unexpected-features/foo.rs b/tests/run-make/wasm-unexpected-features/foo.rs new file mode 100644 index 00000000000..5c7aa2d6190 --- /dev/null +++ b/tests/run-make/wasm-unexpected-features/foo.rs @@ -0,0 +1,43 @@ +#![no_core] +#![crate_type = "cdylib"] +#![feature(no_core, lang_items, allocator_internals, rustc_attrs)] +#![needs_allocator] +#![allow(internal_features)] + +#[rustc_std_internal_symbol] +unsafe fn __rust_alloc(_size: usize, _align: usize) -> *mut u8 { + 0 as *mut u8 +} + +unsafe extern "Rust" { + #[rustc_std_internal_symbol] + fn __rust_alloc_error_handler(size: usize, align: usize) -> !; +} + +#[used] +static mut BUF: [u8; 1024] = [0; 1024]; + +#[unsafe(no_mangle)] +extern "C" fn init() { + unsafe { + __rust_alloc_error_handler(0, 0); + } +} + +mod minicore { + #[lang = "pointee_sized"] + pub trait PointeeSized {} + + #[lang = "meta_sized"] + pub trait MetaSized: PointeeSized {} + + #[lang = "sized"] + pub trait Sized: MetaSized {} + + #[lang = "copy"] + pub trait Copy {} + impl Copy for u8 {} + + #[lang = "drop_in_place"] + fn drop_in_place<T>(_: *mut T) {} +} diff --git a/tests/run-make/wasm-unexpected-features/rmake.rs b/tests/run-make/wasm-unexpected-features/rmake.rs new file mode 100644 index 00000000000..01eff54e823 --- /dev/null +++ b/tests/run-make/wasm-unexpected-features/rmake.rs @@ -0,0 +1,26 @@ +//@ only-wasm32-wasip1 + +use std::path::Path; + +use run_make_support::{rfs, rustc, wasmparser}; + +fn main() { + rustc() + .input("foo.rs") + .target("wasm32-wasip1") + .target_cpu("mvp") + .opt_level("z") + .lto("fat") + .linker_plugin_lto("on") + .link_arg("--import-memory") + .run(); + verify_features(Path::new("foo.wasm")); +} + +fn verify_features(path: &Path) { + eprintln!("verify {path:?}"); + let file = rfs::read(&path); + + let mut validator = wasmparser::Validator::new_with_features(wasmparser::WasmFeatures::MVP); + validator.validate_all(&file).unwrap(); +} diff --git a/tests/rustdoc-js-std/core-transmute.js b/tests/rustdoc-js-std/core-transmute.js index b15f398902c..8c9910a32d7 100644 --- a/tests/rustdoc-js-std/core-transmute.js +++ b/tests/rustdoc-js-std/core-transmute.js @@ -3,9 +3,9 @@ const EXPECTED = [ { 'query': 'generic:T -> generic:U', 'others': [ - { 'path': 'core::mem', 'name': 'transmute' }, { 'path': 'core::intrinsics::simd', 'name': 'simd_as' }, { 'path': 'core::intrinsics::simd', 'name': 'simd_cast' }, + { 'path': 'core::mem', 'name': 'transmute' }, ], }, ]; diff --git a/tests/rustdoc-js-std/transmute-fail.js b/tests/rustdoc-js-std/transmute-fail.js index 459e8dc3f00..ddfb2761948 100644 --- a/tests/rustdoc-js-std/transmute-fail.js +++ b/tests/rustdoc-js-std/transmute-fail.js @@ -6,9 +6,9 @@ const EXPECTED = [ // should-fail tag and the search query below: 'query': 'generic:T -> generic:T', 'others': [ - { 'path': 'std::mem', 'name': 'transmute' }, { 'path': 'std::intrinsics::simd', 'name': 'simd_as' }, { 'path': 'std::intrinsics::simd', 'name': 'simd_cast' }, + { 'path': 'std::mem', 'name': 'transmute' }, ], }, ]; diff --git a/tests/rustdoc-js-std/transmute.js b/tests/rustdoc-js-std/transmute.js index a85b02e2994..f52e0ab1436 100644 --- a/tests/rustdoc-js-std/transmute.js +++ b/tests/rustdoc-js-std/transmute.js @@ -5,9 +5,9 @@ const EXPECTED = [ // should-fail tag and the search query below: 'query': 'generic:T -> generic:U', 'others': [ - { 'path': 'std::mem', 'name': 'transmute' }, { 'path': 'std::intrinsics::simd', 'name': 'simd_as' }, { 'path': 'std::intrinsics::simd', 'name': 'simd_cast' }, + { 'path': 'std::mem', 'name': 'transmute' }, ], }, ]; diff --git a/tests/rustdoc-js/sort-stability.js b/tests/rustdoc-js/sort-stability.js deleted file mode 100644 index 8c095619a08..00000000000 --- a/tests/rustdoc-js/sort-stability.js +++ /dev/null @@ -1,9 +0,0 @@ -const EXPECTED = [ - { - 'query': 'foo', - 'others': [ - {"path": "sort_stability::old", "name": "foo"}, - {"path": "sort_stability::new", "name": "foo"}, - ], - }, -]; diff --git a/tests/rustdoc-js/sort-stability.rs b/tests/rustdoc-js/sort-stability.rs deleted file mode 100644 index 68662bb3aab..00000000000 --- a/tests/rustdoc-js/sort-stability.rs +++ /dev/null @@ -1,16 +0,0 @@ -#![feature(staged_api)] -#![stable(feature = "foo_lib", since = "1.0.0")] - -#[stable(feature = "old_foo", since = "1.0.1")] -pub mod old { - /// Old, stable foo - #[stable(feature = "old_foo", since = "1.0.1")] - pub fn foo() {} -} - -#[unstable(feature = "new_foo", issue = "none")] -pub mod new { - /// New, unstable foo - #[unstable(feature = "new_foo", issue = "none")] - pub fn foo() {} -} diff --git a/tests/ui/abi/interrupt-invalid-signature.avr.stderr b/tests/ui/abi/interrupt-invalid-signature.avr.stderr new file mode 100644 index 00000000000..91f5ca2022e --- /dev/null +++ b/tests/ui/abi/interrupt-invalid-signature.avr.stderr @@ -0,0 +1,28 @@ +error: invalid signature for `extern "avr-interrupt"` function + --> $DIR/interrupt-invalid-signature.rs:44:35 + | +LL | extern "avr-interrupt" fn avr_arg(_byte: u8) {} + | ^^^^^^^^^ + | + = note: functions with the "avr-interrupt" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "avr-interrupt" fn avr_arg(_byte: u8) {} +LL + extern "avr-interrupt" fn avr_arg() {} + | + +error: invalid signature for `extern "avr-interrupt"` function + --> $DIR/interrupt-invalid-signature.rs:59:40 + | +LL | extern "avr-interrupt" fn avr_ret() -> u8 { + | ^^ + | + = note: functions with the "avr-interrupt" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "avr-interrupt" fn avr_ret() -> u8 { +LL + extern "avr-interrupt" fn avr_ret() { + | + +error: aborting due to 2 previous errors + diff --git a/tests/ui/abi/interrupt-invalid-signature.i686.stderr b/tests/ui/abi/interrupt-invalid-signature.i686.stderr new file mode 100644 index 00000000000..86f2e097c37 --- /dev/null +++ b/tests/ui/abi/interrupt-invalid-signature.i686.stderr @@ -0,0 +1,15 @@ +error: invalid signature for `extern "x86-interrupt"` function + --> $DIR/interrupt-invalid-signature.rs:83:40 + | +LL | extern "x86-interrupt" fn x86_ret() -> u8 { + | ^^ + | + = note: functions with the "x86-interrupt" ABI cannot have a return type +help: remove the return type + --> $DIR/interrupt-invalid-signature.rs:83:40 + | +LL | extern "x86-interrupt" fn x86_ret() -> u8 { + | ^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/abi/interrupt-invalid-signature.msp430.stderr b/tests/ui/abi/interrupt-invalid-signature.msp430.stderr new file mode 100644 index 00000000000..38479f93de5 --- /dev/null +++ b/tests/ui/abi/interrupt-invalid-signature.msp430.stderr @@ -0,0 +1,28 @@ +error: invalid signature for `extern "msp430-interrupt"` function + --> $DIR/interrupt-invalid-signature.rs:40:41 + | +LL | extern "msp430-interrupt" fn msp430_arg(_byte: u8) {} + | ^^^^^^^^^ + | + = note: functions with the "msp430-interrupt" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "msp430-interrupt" fn msp430_arg(_byte: u8) {} +LL + extern "msp430-interrupt" fn msp430_arg() {} + | + +error: invalid signature for `extern "msp430-interrupt"` function + --> $DIR/interrupt-invalid-signature.rs:65:46 + | +LL | extern "msp430-interrupt" fn msp430_ret() -> u8 { + | ^^ + | + = note: functions with the "msp430-interrupt" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "msp430-interrupt" fn msp430_ret() -> u8 { +LL + extern "msp430-interrupt" fn msp430_ret() { + | + +error: aborting due to 2 previous errors + diff --git a/tests/ui/abi/interrupt-invalid-signature.riscv32.stderr b/tests/ui/abi/interrupt-invalid-signature.riscv32.stderr new file mode 100644 index 00000000000..d632b17ab83 --- /dev/null +++ b/tests/ui/abi/interrupt-invalid-signature.riscv32.stderr @@ -0,0 +1,54 @@ +error: invalid signature for `extern "riscv-interrupt-m"` function + --> $DIR/interrupt-invalid-signature.rs:48:43 + | +LL | extern "riscv-interrupt-m" fn riscv_m_arg(_byte: u8) {} + | ^^^^^^^^^ + | + = note: functions with the "riscv-interrupt-m" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "riscv-interrupt-m" fn riscv_m_arg(_byte: u8) {} +LL + extern "riscv-interrupt-m" fn riscv_m_arg() {} + | + +error: invalid signature for `extern "riscv-interrupt-s"` function + --> $DIR/interrupt-invalid-signature.rs:52:43 + | +LL | extern "riscv-interrupt-s" fn riscv_s_arg(_byte: u8) {} + | ^^^^^^^^^ + | + = note: functions with the "riscv-interrupt-s" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "riscv-interrupt-s" fn riscv_s_arg(_byte: u8) {} +LL + extern "riscv-interrupt-s" fn riscv_s_arg() {} + | + +error: invalid signature for `extern "riscv-interrupt-m"` function + --> $DIR/interrupt-invalid-signature.rs:71:48 + | +LL | extern "riscv-interrupt-m" fn riscv_m_ret() -> u8 { + | ^^ + | + = note: functions with the "riscv-interrupt-m" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "riscv-interrupt-m" fn riscv_m_ret() -> u8 { +LL + extern "riscv-interrupt-m" fn riscv_m_ret() { + | + +error: invalid signature for `extern "riscv-interrupt-s"` function + --> $DIR/interrupt-invalid-signature.rs:77:48 + | +LL | extern "riscv-interrupt-s" fn riscv_s_ret() -> u8 { + | ^^ + | + = note: functions with the "riscv-interrupt-s" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "riscv-interrupt-s" fn riscv_s_ret() -> u8 { +LL + extern "riscv-interrupt-s" fn riscv_s_ret() { + | + +error: aborting due to 4 previous errors + diff --git a/tests/ui/abi/interrupt-invalid-signature.riscv64.stderr b/tests/ui/abi/interrupt-invalid-signature.riscv64.stderr new file mode 100644 index 00000000000..d632b17ab83 --- /dev/null +++ b/tests/ui/abi/interrupt-invalid-signature.riscv64.stderr @@ -0,0 +1,54 @@ +error: invalid signature for `extern "riscv-interrupt-m"` function + --> $DIR/interrupt-invalid-signature.rs:48:43 + | +LL | extern "riscv-interrupt-m" fn riscv_m_arg(_byte: u8) {} + | ^^^^^^^^^ + | + = note: functions with the "riscv-interrupt-m" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "riscv-interrupt-m" fn riscv_m_arg(_byte: u8) {} +LL + extern "riscv-interrupt-m" fn riscv_m_arg() {} + | + +error: invalid signature for `extern "riscv-interrupt-s"` function + --> $DIR/interrupt-invalid-signature.rs:52:43 + | +LL | extern "riscv-interrupt-s" fn riscv_s_arg(_byte: u8) {} + | ^^^^^^^^^ + | + = note: functions with the "riscv-interrupt-s" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "riscv-interrupt-s" fn riscv_s_arg(_byte: u8) {} +LL + extern "riscv-interrupt-s" fn riscv_s_arg() {} + | + +error: invalid signature for `extern "riscv-interrupt-m"` function + --> $DIR/interrupt-invalid-signature.rs:71:48 + | +LL | extern "riscv-interrupt-m" fn riscv_m_ret() -> u8 { + | ^^ + | + = note: functions with the "riscv-interrupt-m" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "riscv-interrupt-m" fn riscv_m_ret() -> u8 { +LL + extern "riscv-interrupt-m" fn riscv_m_ret() { + | + +error: invalid signature for `extern "riscv-interrupt-s"` function + --> $DIR/interrupt-invalid-signature.rs:77:48 + | +LL | extern "riscv-interrupt-s" fn riscv_s_ret() -> u8 { + | ^^ + | + = note: functions with the "riscv-interrupt-s" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "riscv-interrupt-s" fn riscv_s_ret() -> u8 { +LL + extern "riscv-interrupt-s" fn riscv_s_ret() { + | + +error: aborting due to 4 previous errors + diff --git a/tests/ui/abi/interrupt-invalid-signature.rs b/tests/ui/abi/interrupt-invalid-signature.rs new file mode 100644 index 00000000000..e389285b069 --- /dev/null +++ b/tests/ui/abi/interrupt-invalid-signature.rs @@ -0,0 +1,110 @@ +/*! Tests interrupt ABIs have a constricted signature + +Most interrupt ABIs share a similar restriction in terms of not allowing most signatures. +Specifically, they generally cannot have arguments or return types. +So we test that they error in essentially all of the same places. +A notable and interesting exception is x86. + +This test uses `cfg` because it is not testing whether these ABIs work on the platform. +*/ +//@ add-core-stubs +//@ revisions: x64 i686 riscv32 riscv64 avr msp430 +// +//@ [x64] needs-llvm-components: x86 +//@ [x64] compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=rlib +//@ [i686] needs-llvm-components: x86 +//@ [i686] compile-flags: --target=i686-unknown-linux-gnu --crate-type=rlib +//@ [riscv32] needs-llvm-components: riscv +//@ [riscv32] compile-flags: --target=riscv32i-unknown-none-elf --crate-type=rlib +//@ [riscv64] needs-llvm-components: riscv +//@ [riscv64] compile-flags: --target=riscv64gc-unknown-none-elf --crate-type=rlib +//@ [avr] needs-llvm-components: avr +//@ [avr] compile-flags: --target=avr-none -C target-cpu=atmega328p --crate-type=rlib +//@ [msp430] needs-llvm-components: msp430 +//@ [msp430] compile-flags: --target=msp430-none-elf --crate-type=rlib +#![no_core] +#![feature( + no_core, + abi_msp430_interrupt, + abi_avr_interrupt, + abi_x86_interrupt, + abi_riscv_interrupt +)] + +extern crate minicore; +use minicore::*; + +/* most extern "interrupt" definitions should not accept args */ + +#[cfg(msp430)] +extern "msp430-interrupt" fn msp430_arg(_byte: u8) {} +//[msp430]~^ ERROR invalid signature + +#[cfg(avr)] +extern "avr-interrupt" fn avr_arg(_byte: u8) {} +//[avr]~^ ERROR invalid signature + +#[cfg(any(riscv32,riscv64))] +extern "riscv-interrupt-m" fn riscv_m_arg(_byte: u8) {} +//[riscv32,riscv64]~^ ERROR invalid signature + +#[cfg(any(riscv32,riscv64))] +extern "riscv-interrupt-s" fn riscv_s_arg(_byte: u8) {} +//[riscv32,riscv64]~^ ERROR invalid signature + + +/* all extern "interrupt" definitions should not return non-1ZST values */ + +#[cfg(avr)] +extern "avr-interrupt" fn avr_ret() -> u8 { + //[avr]~^ ERROR invalid signature + 1 +} + +#[cfg(msp430)] +extern "msp430-interrupt" fn msp430_ret() -> u8 { + //[msp430]~^ ERROR invalid signature + 1 +} + +#[cfg(any(riscv32,riscv64))] +extern "riscv-interrupt-m" fn riscv_m_ret() -> u8 { + //[riscv32,riscv64]~^ ERROR invalid signature + 1 +} + +#[cfg(any(riscv32,riscv64))] +extern "riscv-interrupt-s" fn riscv_s_ret() -> u8 { + //[riscv32,riscv64]~^ ERROR invalid signature + 1 +} + +#[cfg(any(x64,i686))] +extern "x86-interrupt" fn x86_ret() -> u8 { + //[x64,i686]~^ ERROR invalid signature + 1 +} + + + +/* extern "interrupt" fnptrs with invalid signatures */ + +#[cfg(avr)] +fn avr_ptr(_f: extern "avr-interrupt" fn(u8) -> u8) { +} + +#[cfg(msp430)] +fn msp430_ptr(_f: extern "msp430-interrupt" fn(u8) -> u8) { +} + +#[cfg(any(riscv32,riscv64))] +fn riscv_m_ptr(_f: extern "riscv-interrupt-m" fn(u8) -> u8) { +} + +#[cfg(any(riscv32,riscv64))] +fn riscv_s_ptr(_f: extern "riscv-interrupt-s" fn(u8) -> u8) { +} + +#[cfg(any(x64,i686))] +fn x86_ptr(_f: extern "x86-interrupt" fn() -> u8) { +} diff --git a/tests/ui/abi/interrupt-invalid-signature.x64.stderr b/tests/ui/abi/interrupt-invalid-signature.x64.stderr new file mode 100644 index 00000000000..86f2e097c37 --- /dev/null +++ b/tests/ui/abi/interrupt-invalid-signature.x64.stderr @@ -0,0 +1,15 @@ +error: invalid signature for `extern "x86-interrupt"` function + --> $DIR/interrupt-invalid-signature.rs:83:40 + | +LL | extern "x86-interrupt" fn x86_ret() -> u8 { + | ^^ + | + = note: functions with the "x86-interrupt" ABI cannot have a return type +help: remove the return type + --> $DIR/interrupt-invalid-signature.rs:83:40 + | +LL | extern "x86-interrupt" fn x86_ret() -> u8 { + | ^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/abi/interrupt-returns-never-or-unit.rs b/tests/ui/abi/interrupt-returns-never-or-unit.rs new file mode 100644 index 00000000000..8e224229a0b --- /dev/null +++ b/tests/ui/abi/interrupt-returns-never-or-unit.rs @@ -0,0 +1,110 @@ +/*! Tests interrupt ABIs can return ! + +Most interrupt ABIs share a similar restriction in terms of not allowing most signatures, +but it makes sense to allow them to return ! because they could indeed be divergent. + +This test uses `cfg` because it is not testing whether these ABIs work on the platform. +*/ +//@ add-core-stubs +//@ revisions: x64 i686 riscv32 riscv64 avr msp430 +//@ build-pass +// +//@ [x64] needs-llvm-components: x86 +//@ [x64] compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=rlib +//@ [i686] needs-llvm-components: x86 +//@ [i686] compile-flags: --target=i686-unknown-linux-gnu --crate-type=rlib +//@ [riscv32] needs-llvm-components: riscv +//@ [riscv32] compile-flags: --target=riscv32i-unknown-none-elf --crate-type=rlib +//@ [riscv64] needs-llvm-components: riscv +//@ [riscv64] compile-flags: --target=riscv64gc-unknown-none-elf --crate-type=rlib +//@ [avr] needs-llvm-components: avr +//@ [avr] compile-flags: --target=avr-none -C target-cpu=atmega328p --crate-type=rlib +//@ [msp430] needs-llvm-components: msp430 +//@ [msp430] compile-flags: --target=msp430-none-elf --crate-type=rlib +#![no_core] +#![feature( + no_core, + abi_msp430_interrupt, + abi_avr_interrupt, + abi_x86_interrupt, + abi_riscv_interrupt +)] + +extern crate minicore; +use minicore::*; + +/* interrupts can return never */ + +#[cfg(avr)] +extern "avr-interrupt" fn avr_ret_never() -> ! { + loop {} +} + +#[cfg(msp430)] +extern "msp430-interrupt" fn msp430_ret_never() -> ! { + loop {} +} + +#[cfg(any(riscv32,riscv64))] +extern "riscv-interrupt-m" fn riscv_m_ret_never() -> ! { + loop {} +} + +#[cfg(any(riscv32,riscv64))] +extern "riscv-interrupt-s" fn riscv_s_ret_never() -> ! { + loop {} +} + +#[cfg(any(x64,i686))] +extern "x86-interrupt" fn x86_ret_never() -> ! { + loop {} +} + +/* interrupts can return explicit () */ + +#[cfg(avr)] +extern "avr-interrupt" fn avr_ret_unit() -> () { + () +} + +#[cfg(msp430)] +extern "msp430-interrupt" fn msp430_ret_unit() -> () { + () +} + +#[cfg(any(riscv32,riscv64))] +extern "riscv-interrupt-m" fn riscv_m_ret_unit() -> () { + () +} + +#[cfg(any(riscv32,riscv64))] +extern "riscv-interrupt-s" fn riscv_s_ret_unit() -> () { + () +} + +#[cfg(any(x64,i686))] +extern "x86-interrupt" fn x86_ret_unit() -> () { + () +} + +/* extern "interrupt" fnptrs can return ! too */ + +#[cfg(avr)] +fn avr_ptr(_f: extern "avr-interrupt" fn() -> !) { +} + +#[cfg(msp430)] +fn msp430_ptr(_f: extern "msp430-interrupt" fn() -> !) { +} + +#[cfg(any(riscv32,riscv64))] +fn riscv_m_ptr(_f: extern "riscv-interrupt-m" fn() -> !) { +} + +#[cfg(any(riscv32,riscv64))] +fn riscv_s_ptr(_f: extern "riscv-interrupt-s" fn() -> !) { +} + +#[cfg(any(x64,i686))] +fn x86_ptr(_f: extern "x86-interrupt" fn() -> !) { +} diff --git a/tests/ui/autodiff/macro_hygiene.rs b/tests/ui/autodiff/macro_hygiene.rs new file mode 100644 index 00000000000..dec58254b99 --- /dev/null +++ b/tests/ui/autodiff/macro_hygiene.rs @@ -0,0 +1,22 @@ +//@ compile-flags: -Zautodiff=Enable -C opt-level=3 -Clto=fat +//@ no-prefer-dynamic +//@ needs-enzyme +//@ check-pass + +// In the past, we just checked for correct macro hygiene information. + +#![feature(autodiff)] + +macro_rules! demo { + () => { + #[std::autodiff::autodiff_reverse(fd, Active, Active)] + fn f(x: f64) -> f64 { + x * x + } + }; +} +demo!(); + +fn main() { + dbg!(f(2.0f64)); +} diff --git a/tests/ui/codemap_tests/issue-28308.rs b/tests/ui/codemap_tests/issue-28308.rs index 81493f8c453..b0e04d0f1f6 100644 --- a/tests/ui/codemap_tests/issue-28308.rs +++ b/tests/ui/codemap_tests/issue-28308.rs @@ -1,4 +1,16 @@ fn main() { - assert!("foo"); - //~^ ERROR cannot apply unary operator `!` + assert!("foo"); //~ ERROR mismatched types + //~^ NOTE expected `bool`, found `str` + //~| NOTE in this expansion of assert! + let x = Some(&1); + assert!(x); //~ ERROR mismatched types + //~^ NOTE expected `bool`, found `Option<&{integer}>` + //~| NOTE expected enum `bool` + //~| NOTE in this expansion of assert! + //~| NOTE in this expansion of assert! + assert!(x, ""); //~ ERROR mismatched types + //~^ NOTE expected `bool`, found `Option<&{integer}>` + //~| NOTE expected enum `bool` + //~| NOTE in this expansion of assert! + //~| NOTE in this expansion of assert! } diff --git a/tests/ui/codemap_tests/issue-28308.stderr b/tests/ui/codemap_tests/issue-28308.stderr index 7bc9e05dfc0..e84ceb44aac 100644 --- a/tests/ui/codemap_tests/issue-28308.stderr +++ b/tests/ui/codemap_tests/issue-28308.stderr @@ -1,9 +1,27 @@ -error[E0600]: cannot apply unary operator `!` to type `&'static str` - --> $DIR/issue-28308.rs:2:5 +error[E0308]: mismatched types + --> $DIR/issue-28308.rs:2:13 | LL | assert!("foo"); - | ^^^^^^^^^^^^^^ cannot apply unary operator `!` + | ^^^^^ expected `bool`, found `str` -error: aborting due to 1 previous error +error[E0308]: mismatched types + --> $DIR/issue-28308.rs:6:13 + | +LL | assert!(x); + | ^ expected `bool`, found `Option<&{integer}>` + | + = note: expected enum `bool` + found type `Option<&{integer}>` + +error[E0308]: mismatched types + --> $DIR/issue-28308.rs:11:13 + | +LL | assert!(x, ""); + | ^ expected `bool`, found `Option<&{integer}>` + | + = note: expected enum `bool` + found type `Option<&{integer}>` + +error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0600`. +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/consts/const-eval/heap/ptr_not_made_global.stderr b/tests/ui/consts/const-eval/heap/ptr_not_made_global.stderr index cb2bb1e8cd8..e5b108df298 100644 --- a/tests/ui/consts/const-eval/heap/ptr_not_made_global.stderr +++ b/tests/ui/consts/const-eval/heap/ptr_not_made_global.stderr @@ -4,7 +4,7 @@ error: encountered `const_allocate` pointer in final value that was not made glo LL | const FOO: &i32 = foo(); | ^^^^^^^^^^^^^^^ | - = note: use `const_make_global` to make allocated pointers immutable before returning + = note: use `const_make_global` to turn allocated pointers into immutable globals before returning error: encountered `const_allocate` pointer in final value that was not made global --> $DIR/ptr_not_made_global.rs:12:1 @@ -12,7 +12,7 @@ error: encountered `const_allocate` pointer in final value that was not made glo LL | const FOO_RAW: *const i32 = foo(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: use `const_make_global` to make allocated pointers immutable before returning + = note: use `const_make_global` to turn allocated pointers into immutable globals before returning error: aborting due to 2 previous errors diff --git a/tests/ui/consts/const-eval/heap/ptr_not_made_global_mut.stderr b/tests/ui/consts/const-eval/heap/ptr_not_made_global_mut.stderr index 2445ce633d6..2d1993f96d3 100644 --- a/tests/ui/consts/const-eval/heap/ptr_not_made_global_mut.stderr +++ b/tests/ui/consts/const-eval/heap/ptr_not_made_global_mut.stderr @@ -4,7 +4,7 @@ error: encountered `const_allocate` pointer in final value that was not made glo LL | const BAR: *mut i32 = unsafe { intrinsics::const_allocate(4, 4) as *mut i32 }; | ^^^^^^^^^^^^^^^^^^^ | - = note: use `const_make_global` to make allocated pointers immutable before returning + = note: use `const_make_global` to turn allocated pointers into immutable globals before returning error: aborting due to 1 previous error diff --git a/tests/ui/consts/const-eval/partial_ptr_overwrite.rs b/tests/ui/consts/const-eval/partial_ptr_overwrite.rs deleted file mode 100644 index bd97bec0f71..00000000000 --- a/tests/ui/consts/const-eval/partial_ptr_overwrite.rs +++ /dev/null @@ -1,12 +0,0 @@ -// Test for the behavior described in <https://github.com/rust-lang/rust/issues/87184>. - -const PARTIAL_OVERWRITE: () = { - let mut p = &42; - unsafe { - let ptr: *mut _ = &mut p; - *(ptr as *mut u8) = 123; //~ ERROR unable to overwrite parts of a pointer - } - let x = *p; -}; - -fn main() {} diff --git a/tests/ui/consts/const-eval/partial_ptr_overwrite.stderr b/tests/ui/consts/const-eval/partial_ptr_overwrite.stderr deleted file mode 100644 index 6ef1cfd35c8..00000000000 --- a/tests/ui/consts/const-eval/partial_ptr_overwrite.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0080]: unable to overwrite parts of a pointer in memory at ALLOC0 - --> $DIR/partial_ptr_overwrite.rs:7:9 - | -LL | *(ptr as *mut u8) = 123; - | ^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `PARTIAL_OVERWRITE` failed here - | - = 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 - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const-eval/ptr_fragments.rs b/tests/ui/consts/const-eval/ptr_fragments.rs new file mode 100644 index 00000000000..04dcbe55590 --- /dev/null +++ b/tests/ui/consts/const-eval/ptr_fragments.rs @@ -0,0 +1,63 @@ +//! Test that various operations involving pointer fragments work as expected. +//@ run-pass + +use std::mem::{self, MaybeUninit, transmute}; +use std::ptr; + +type Byte = MaybeUninit<u8>; + +const unsafe fn memcpy(dst: *mut Byte, src: *const Byte, n: usize) { + let mut i = 0; + while i < n { + *dst.add(i) = *src.add(i); + i += 1; + } +} + +const _MEMCPY: () = unsafe { + let ptr = &42; + let mut ptr2 = ptr::null::<i32>(); + memcpy(&mut ptr2 as *mut _ as *mut _, &ptr as *const _ as *const _, mem::size_of::<&i32>()); + assert!(*ptr2 == 42); +}; +const _MEMCPY_OFFSET: () = unsafe { + // Same as above, but the pointer has a non-zero offset so not all the data bytes are the same. + let ptr = &(42, 18); + let ptr = &ptr.1; + let mut ptr2 = ptr::null::<i32>(); + memcpy(&mut ptr2 as *mut _ as *mut _, &ptr as *const _ as *const _, mem::size_of::<&i32>()); + assert!(*ptr2 == 18); +}; + +const MEMCPY_RET: MaybeUninit<*const i32> = unsafe { + let ptr = &42; + let mut ptr2 = MaybeUninit::new(ptr::null::<i32>()); + memcpy(&mut ptr2 as *mut _ as *mut _, &ptr as *const _ as *const _, mem::size_of::<&i32>()); + // Return in a MaybeUninit so it does not get treated as a scalar. + ptr2 +}; + +#[allow(dead_code)] +fn reassemble_ptr_fragments_in_static() { + static DATA: i32 = 1i32; + + #[cfg(target_pointer_width = "64")] + struct Thing { + x: MaybeUninit<u32>, + y: MaybeUninit<u32>, + } + #[cfg(target_pointer_width = "32")] + struct Thing { + x: MaybeUninit<u16>, + y: MaybeUninit<u16>, + } + + static X: Thing = unsafe { + let Thing { x, y } = transmute(&raw const DATA); + Thing { x, y } + }; +} + +fn main() { + assert_eq!(unsafe { MEMCPY_RET.assume_init().read() }, 42); +} diff --git a/tests/ui/consts/const-eval/ptr_fragments_in_final.rs b/tests/ui/consts/const-eval/ptr_fragments_in_final.rs new file mode 100644 index 00000000000..e2f3f51b086 --- /dev/null +++ b/tests/ui/consts/const-eval/ptr_fragments_in_final.rs @@ -0,0 +1,25 @@ +//! Test that we properly error when there is a pointer fragment in the final value. + +use std::{mem::{self, MaybeUninit}, ptr}; + +type Byte = MaybeUninit<u8>; + +const unsafe fn memcpy(dst: *mut Byte, src: *const Byte, n: usize) { + let mut i = 0; + while i < n { + dst.add(i).write(src.add(i).read()); + i += 1; + } +} + +const MEMCPY_RET: MaybeUninit<*const i32> = unsafe { //~ERROR: partial pointer in final value + let ptr = &42; + let mut ptr2 = MaybeUninit::new(ptr::null::<i32>()); + memcpy(&mut ptr2 as *mut _ as *mut _, &ptr as *const _ as *const _, mem::size_of::<&i32>() / 2); + // Return in a MaybeUninit so it does not get treated as a scalar. + ptr2 +}; + +fn main() { + assert_eq!(unsafe { MEMCPY_RET.assume_init().read() }, 42); +} diff --git a/tests/ui/consts/const-eval/ptr_fragments_in_final.stderr b/tests/ui/consts/const-eval/ptr_fragments_in_final.stderr new file mode 100644 index 00000000000..628bf2566e5 --- /dev/null +++ b/tests/ui/consts/const-eval/ptr_fragments_in_final.stderr @@ -0,0 +1,10 @@ +error: encountered partial pointer in final value of constant + --> $DIR/ptr_fragments_in_final.rs:15:1 + | +LL | const MEMCPY_RET: MaybeUninit<*const i32> = unsafe { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: while pointers can be broken apart into individual bytes during const-evaluation, only complete pointers (with all their bytes in the right order) are supported in the final value + +error: aborting due to 1 previous error + diff --git a/tests/ui/consts/const-eval/read_partial_ptr.rs b/tests/ui/consts/const-eval/read_partial_ptr.rs new file mode 100644 index 00000000000..bccef9c0bc6 --- /dev/null +++ b/tests/ui/consts/const-eval/read_partial_ptr.rs @@ -0,0 +1,49 @@ +//! Ensure we error when trying to load from a pointer whose provenance has been messed with. + +const PARTIAL_OVERWRITE: () = { + let mut p = &42; + // Overwrite one byte with a no-provenance value. + unsafe { + let ptr: *mut _ = &mut p; + *(ptr as *mut u8) = 123; + } + let x = *p; //~ ERROR: unable to read parts of a pointer +}; + +const PTR_BYTES_SWAP: () = { + let mut p = &42; + // Swap the first two bytes. + unsafe { + let ptr = &mut p as *mut _ as *mut std::mem::MaybeUninit<u8>; + let byte0 = ptr.read(); + let byte1 = ptr.add(1).read(); + ptr.write(byte1); + ptr.add(1).write(byte0); + } + let x = *p; //~ ERROR: unable to read parts of a pointer +}; + +const PTR_BYTES_REPEAT: () = { + let mut p = &42; + // Duplicate the first byte over the second. + unsafe { + let ptr = &mut p as *mut _ as *mut std::mem::MaybeUninit<u8>; + let byte0 = ptr.read(); + ptr.add(1).write(byte0); + } + let x = *p; //~ ERROR: unable to read parts of a pointer +}; + +const PTR_BYTES_MIX: () = { + let mut p = &42; + let q = &43; + // Overwrite the first byte of p with the first byte of q. + unsafe { + let ptr = &mut p as *mut _ as *mut std::mem::MaybeUninit<u8>; + let qtr = &q as *const _ as *const std::mem::MaybeUninit<u8>; + ptr.write(qtr.read()); + } + let x = *p; //~ ERROR: unable to read parts of a pointer +}; + +fn main() {} diff --git a/tests/ui/consts/const-eval/read_partial_ptr.stderr b/tests/ui/consts/const-eval/read_partial_ptr.stderr new file mode 100644 index 00000000000..196606c77a3 --- /dev/null +++ b/tests/ui/consts/const-eval/read_partial_ptr.stderr @@ -0,0 +1,39 @@ +error[E0080]: unable to read parts of a pointer from memory at ALLOC0 + --> $DIR/read_partial_ptr.rs:10:13 + | +LL | let x = *p; + | ^^ evaluation of `PARTIAL_OVERWRITE` failed here + | + = 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 + +error[E0080]: unable to read parts of a pointer from memory at ALLOC1 + --> $DIR/read_partial_ptr.rs:23:13 + | +LL | let x = *p; + | ^^ evaluation of `PTR_BYTES_SWAP` failed here + | + = 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 + +error[E0080]: unable to read parts of a pointer from memory at ALLOC2 + --> $DIR/read_partial_ptr.rs:34:13 + | +LL | let x = *p; + | ^^ evaluation of `PTR_BYTES_REPEAT` failed here + | + = 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 + +error[E0080]: unable to read parts of a pointer from memory at ALLOC3 + --> $DIR/read_partial_ptr.rs:46:13 + | +LL | let x = *p; + | ^^ evaluation of `PTR_BYTES_MIX` failed here + | + = 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 + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/control-flow/assert.stderr b/tests/ui/consts/control-flow/assert.stderr index 026097a6ba0..deaad6abbcc 100644 --- a/tests/ui/consts/control-flow/assert.stderr +++ b/tests/ui/consts/control-flow/assert.stderr @@ -1,8 +1,8 @@ error[E0080]: evaluation panicked: assertion failed: false - --> $DIR/assert.rs:5:15 + --> $DIR/assert.rs:5:23 | LL | const _: () = assert!(false); - | ^^^^^^^^^^^^^^ evaluation of `_` failed here + | ^^^^^ evaluation of `_` failed here error: aborting due to 1 previous error diff --git a/tests/ui/consts/missing_span_in_backtrace.rs b/tests/ui/consts/missing_span_in_backtrace.rs index 4f3f9aa6ada..09a11c75fd7 100644 --- a/tests/ui/consts/missing_span_in_backtrace.rs +++ b/tests/ui/consts/missing_span_in_backtrace.rs @@ -1,3 +1,4 @@ +//! Check what happens when the error occurs inside a std function that we can't print the span of. //@ ignore-backends: gcc //@ compile-flags: -Z ui-testing=no @@ -7,15 +8,15 @@ use std::{ }; const X: () = { - let mut ptr1 = &1; - let mut ptr2 = &2; + let mut x1 = 1; + let mut x2 = 2; // Swap them, bytewise. unsafe { - ptr::swap_nonoverlapping( //~ ERROR unable to copy parts of a pointer - &mut ptr1 as *mut _ as *mut MaybeUninit<u8>, - &mut ptr2 as *mut _ as *mut MaybeUninit<u8>, - mem::size_of::<&i32>(), + ptr::swap_nonoverlapping( //~ ERROR beyond the end of the allocation + &mut x1 as *mut _ as *mut MaybeUninit<u8>, + &mut x2 as *mut _ as *mut MaybeUninit<u8>, + 10, ); } }; diff --git a/tests/ui/consts/missing_span_in_backtrace.stderr b/tests/ui/consts/missing_span_in_backtrace.stderr index 0ac1e281107..55dde635828 100644 --- a/tests/ui/consts/missing_span_in_backtrace.stderr +++ b/tests/ui/consts/missing_span_in_backtrace.stderr @@ -1,15 +1,13 @@ -error[E0080]: unable to copy parts of a pointer from memory at ALLOC0 - --> $DIR/missing_span_in_backtrace.rs:15:9 +error[E0080]: memory access failed: attempting to access 1 byte, but got ALLOC0+0x4 which is at or beyond the end of the allocation of size 4 bytes + --> $DIR/missing_span_in_backtrace.rs:16:9 | -15 | / ptr::swap_nonoverlapping( -16 | | &mut ptr1 as *mut _ as *mut MaybeUninit<u8>, -17 | | &mut ptr2 as *mut _ as *mut MaybeUninit<u8>, -18 | | mem::size_of::<&i32>(), -19 | | ); +16 | / ptr::swap_nonoverlapping( +17 | | &mut x1 as *mut _ as *mut MaybeUninit<u8>, +18 | | &mut x2 as *mut _ as *mut MaybeUninit<u8>, +19 | | 10, +20 | | ); | |_________^ evaluation of `X` failed inside this call | - = 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: inside `swap_nonoverlapping::compiletime::<MaybeUninit<u8>>` --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL note: inside `std::ptr::swap_nonoverlapping_const::<MaybeUninit<u8>>` diff --git a/tests/ui/deriving/deriving-all-codegen.rs b/tests/ui/deriving/deriving-all-codegen.rs index e2b6804fbd1..00a269ccb5c 100644 --- a/tests/ui/deriving/deriving-all-codegen.rs +++ b/tests/ui/deriving/deriving-all-codegen.rs @@ -16,6 +16,7 @@ #![crate_type = "lib"] #![allow(dead_code)] #![allow(deprecated)] +#![feature(derive_from)] // Empty struct. #[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)] @@ -38,6 +39,14 @@ struct PackedPoint { y: u32, } +#[derive(Clone, Copy, Debug, Default, From, Hash, PartialEq, Eq, PartialOrd, Ord)] +struct TupleSingleField(u32); + +#[derive(Clone, Copy, Debug, Default, From, Hash, PartialEq, Eq, PartialOrd, Ord)] +struct SingleField { + foo: bool, +} + // A large struct. Note: because this derives `Copy`, it gets the simple // `clone` implemention that just does `*self`. #[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)] @@ -86,7 +95,7 @@ struct PackedManualCopy(u32); impl Copy for PackedManualCopy {} // A struct with an unsized field. Some derives are not usable in this case. -#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Debug, From, Hash, PartialEq, Eq, PartialOrd, Ord)] struct Unsized([u32]); trait Trait { diff --git a/tests/ui/deriving/deriving-all-codegen.stdout b/tests/ui/deriving/deriving-all-codegen.stdout index 0e4bfa30257..78b93f39b9e 100644 --- a/tests/ui/deriving/deriving-all-codegen.stdout +++ b/tests/ui/deriving/deriving-all-codegen.stdout @@ -17,6 +17,7 @@ #![crate_type = "lib"] #![allow(dead_code)] #![allow(deprecated)] +#![feature(derive_from)] #[macro_use] extern crate std; #[prelude_import] @@ -249,6 +250,148 @@ impl ::core::cmp::Ord for PackedPoint { } } +struct TupleSingleField(u32); +#[automatically_derived] +impl ::core::clone::Clone for TupleSingleField { + #[inline] + fn clone(&self) -> TupleSingleField { + let _: ::core::clone::AssertParamIsClone<u32>; + *self + } +} +#[automatically_derived] +impl ::core::marker::Copy for TupleSingleField { } +#[automatically_derived] +impl ::core::fmt::Debug for TupleSingleField { + #[inline] + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_tuple_field1_finish(f, + "TupleSingleField", &&self.0) + } +} +#[automatically_derived] +impl ::core::default::Default for TupleSingleField { + #[inline] + fn default() -> TupleSingleField { + TupleSingleField(::core::default::Default::default()) + } +} +#[automatically_derived] +impl ::core::convert::From<u32> for TupleSingleField { + #[inline] + fn from(value: u32) -> TupleSingleField { Self(value) } +} +#[automatically_derived] +impl ::core::hash::Hash for TupleSingleField { + #[inline] + fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + ::core::hash::Hash::hash(&self.0, state) + } +} +#[automatically_derived] +impl ::core::marker::StructuralPartialEq for TupleSingleField { } +#[automatically_derived] +impl ::core::cmp::PartialEq for TupleSingleField { + #[inline] + fn eq(&self, other: &TupleSingleField) -> bool { self.0 == other.0 } +} +#[automatically_derived] +impl ::core::cmp::Eq for TupleSingleField { + #[inline] + #[doc(hidden)] + #[coverage(off)] + fn assert_receiver_is_total_eq(&self) -> () { + let _: ::core::cmp::AssertParamIsEq<u32>; + } +} +#[automatically_derived] +impl ::core::cmp::PartialOrd for TupleSingleField { + #[inline] + fn partial_cmp(&self, other: &TupleSingleField) + -> ::core::option::Option<::core::cmp::Ordering> { + ::core::cmp::PartialOrd::partial_cmp(&self.0, &other.0) + } +} +#[automatically_derived] +impl ::core::cmp::Ord for TupleSingleField { + #[inline] + fn cmp(&self, other: &TupleSingleField) -> ::core::cmp::Ordering { + ::core::cmp::Ord::cmp(&self.0, &other.0) + } +} + +struct SingleField { + foo: bool, +} +#[automatically_derived] +impl ::core::clone::Clone for SingleField { + #[inline] + fn clone(&self) -> SingleField { + let _: ::core::clone::AssertParamIsClone<bool>; + *self + } +} +#[automatically_derived] +impl ::core::marker::Copy for SingleField { } +#[automatically_derived] +impl ::core::fmt::Debug for SingleField { + #[inline] + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field1_finish(f, "SingleField", + "foo", &&self.foo) + } +} +#[automatically_derived] +impl ::core::default::Default for SingleField { + #[inline] + fn default() -> SingleField { + SingleField { foo: ::core::default::Default::default() } + } +} +#[automatically_derived] +impl ::core::convert::From<bool> for SingleField { + #[inline] + fn from(value: bool) -> SingleField { Self { foo: value } } +} +#[automatically_derived] +impl ::core::hash::Hash for SingleField { + #[inline] + fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + ::core::hash::Hash::hash(&self.foo, state) + } +} +#[automatically_derived] +impl ::core::marker::StructuralPartialEq for SingleField { } +#[automatically_derived] +impl ::core::cmp::PartialEq for SingleField { + #[inline] + fn eq(&self, other: &SingleField) -> bool { self.foo == other.foo } +} +#[automatically_derived] +impl ::core::cmp::Eq for SingleField { + #[inline] + #[doc(hidden)] + #[coverage(off)] + fn assert_receiver_is_total_eq(&self) -> () { + let _: ::core::cmp::AssertParamIsEq<bool>; + } +} +#[automatically_derived] +impl ::core::cmp::PartialOrd for SingleField { + #[inline] + fn partial_cmp(&self, other: &SingleField) + -> ::core::option::Option<::core::cmp::Ordering> { + ::core::cmp::PartialOrd::partial_cmp(&self.foo, &other.foo) + } +} +#[automatically_derived] +impl ::core::cmp::Ord for SingleField { + #[inline] + fn cmp(&self, other: &SingleField) -> ::core::cmp::Ordering { + ::core::cmp::Ord::cmp(&self.foo, &other.foo) + } +} + // A large struct. Note: because this derives `Copy`, it gets the simple // `clone` implemention that just does `*self`. struct Big { @@ -572,6 +715,11 @@ impl ::core::fmt::Debug for Unsized { } } #[automatically_derived] +impl ::core::convert::From<[u32]> for Unsized { + #[inline] + fn from(value: [u32]) -> Unsized { Self(value) } +} +#[automatically_derived] impl ::core::hash::Hash for Unsized { #[inline] fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { diff --git a/tests/ui/deriving/deriving-from-wrong-target.rs b/tests/ui/deriving/deriving-from-wrong-target.rs new file mode 100644 index 00000000000..57e009cae69 --- /dev/null +++ b/tests/ui/deriving/deriving-from-wrong-target.rs @@ -0,0 +1,38 @@ +//@ edition: 2021 +//@ check-fail + +#![feature(derive_from)] +#![allow(dead_code)] + +#[derive(From)] +//~^ ERROR `#[derive(From)]` used on a struct with no fields +struct S1; + +#[derive(From)] +//~^ ERROR `#[derive(From)]` used on a struct with no fields +struct S2 {} + +#[derive(From)] +//~^ ERROR `#[derive(From)]` used on a struct with multiple fields +struct S3(u32, bool); + +#[derive(From)] +//~^ ERROR `#[derive(From)]` used on a struct with multiple fields +struct S4 { + a: u32, + b: bool, +} + +#[derive(From)] +//~^ ERROR `#[derive(From)]` used on an enum +enum E1 {} + +#[derive(From)] +//~^ ERROR the size for values of type `T` cannot be known at compilation time [E0277] +//~| ERROR the size for values of type `T` cannot be known at compilation time [E0277] +struct SUnsizedField<T: ?Sized> { + last: T, + //~^ ERROR the size for values of type `T` cannot be known at compilation time [E0277] +} + +fn main() {} diff --git a/tests/ui/deriving/deriving-from-wrong-target.stderr b/tests/ui/deriving/deriving-from-wrong-target.stderr new file mode 100644 index 00000000000..13593c95973 --- /dev/null +++ b/tests/ui/deriving/deriving-from-wrong-target.stderr @@ -0,0 +1,115 @@ +error: `#[derive(From)]` used on a struct with no fields + --> $DIR/deriving-from-wrong-target.rs:7:10 + | +LL | #[derive(From)] + | ^^^^ +LL | +LL | struct S1; + | ^^ + | + = note: `#[derive(From)]` can only be used on structs with exactly one field + +error: `#[derive(From)]` used on a struct with no fields + --> $DIR/deriving-from-wrong-target.rs:11:10 + | +LL | #[derive(From)] + | ^^^^ +LL | +LL | struct S2 {} + | ^^ + | + = note: `#[derive(From)]` can only be used on structs with exactly one field + +error: `#[derive(From)]` used on a struct with multiple fields + --> $DIR/deriving-from-wrong-target.rs:15:10 + | +LL | #[derive(From)] + | ^^^^ +LL | +LL | struct S3(u32, bool); + | ^^ + | + = note: `#[derive(From)]` can only be used on structs with exactly one field + +error: `#[derive(From)]` used on a struct with multiple fields + --> $DIR/deriving-from-wrong-target.rs:19:10 + | +LL | #[derive(From)] + | ^^^^ +LL | +LL | struct S4 { + | ^^ + | + = note: `#[derive(From)]` can only be used on structs with exactly one field + +error: `#[derive(From)]` used on an enum + --> $DIR/deriving-from-wrong-target.rs:26:10 + | +LL | #[derive(From)] + | ^^^^ +LL | +LL | enum E1 {} + | ^^ + | + = note: `#[derive(From)]` can only be used on structs with exactly one field + +error[E0277]: the size for values of type `T` cannot be known at compilation time + --> $DIR/deriving-from-wrong-target.rs:30:10 + | +LL | #[derive(From)] + | ^^^^ doesn't have a size known at compile-time +... +LL | struct SUnsizedField<T: ?Sized> { + | - this type parameter needs to be `Sized` + | +note: required by an implicit `Sized` bound in `From` + --> $SRC_DIR/core/src/convert/mod.rs:LL:COL +help: consider removing the `?Sized` bound to make the type parameter `Sized` + | +LL - struct SUnsizedField<T: ?Sized> { +LL + struct SUnsizedField<T> { + | + +error[E0277]: the size for values of type `T` cannot be known at compilation time + --> $DIR/deriving-from-wrong-target.rs:30:10 + | +LL | #[derive(From)] + | ^^^^ doesn't have a size known at compile-time +... +LL | struct SUnsizedField<T: ?Sized> { + | - this type parameter needs to be `Sized` + | +note: required because it appears within the type `SUnsizedField<T>` + --> $DIR/deriving-from-wrong-target.rs:33:8 + | +LL | struct SUnsizedField<T: ?Sized> { + | ^^^^^^^^^^^^^ + = note: the return type of a function must have a statically known size +help: consider removing the `?Sized` bound to make the type parameter `Sized` + | +LL - struct SUnsizedField<T: ?Sized> { +LL + struct SUnsizedField<T> { + | + +error[E0277]: the size for values of type `T` cannot be known at compilation time + --> $DIR/deriving-from-wrong-target.rs:34:11 + | +LL | struct SUnsizedField<T: ?Sized> { + | - this type parameter needs to be `Sized` +LL | last: T, + | ^ doesn't have a size known at compile-time + | + = help: unsized fn params are gated as an unstable feature +help: consider removing the `?Sized` bound to make the type parameter `Sized` + | +LL - struct SUnsizedField<T: ?Sized> { +LL + struct SUnsizedField<T> { + | +help: function arguments must have a statically known size, borrowed types always have a known size + | +LL | last: &T, + | + + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/deriving/deriving-from.rs b/tests/ui/deriving/deriving-from.rs new file mode 100644 index 00000000000..ff4c5b4c426 --- /dev/null +++ b/tests/ui/deriving/deriving-from.rs @@ -0,0 +1,58 @@ +//@ edition: 2021 +//@ run-pass + +#![feature(derive_from)] + +#[derive(From)] +struct TupleSimple(u32); + +#[derive(From)] +struct TupleNonPathType([u32; 4]); + +#[derive(From)] +struct TupleWithRef<'a, T>(&'a T); + +#[derive(From)] +struct TupleSWithBound<T: std::fmt::Debug>(T); + +#[derive(From)] +struct RawIdentifier { + r#use: u32, +} + +#[derive(From)] +struct Field { + foo: bool, +} + +#[derive(From)] +struct Const<const C: usize> { + foo: [u32; C], +} + +fn main() { + let a = 42u32; + let b: [u32; 4] = [0, 1, 2, 3]; + let c = true; + + let s1: TupleSimple = a.into(); + assert_eq!(s1.0, a); + + let s2: TupleNonPathType = b.into(); + assert_eq!(s2.0, b); + + let s3: TupleWithRef<u32> = (&a).into(); + assert_eq!(s3.0, &a); + + let s4: TupleSWithBound<u32> = a.into(); + assert_eq!(s4.0, a); + + let s5: RawIdentifier = a.into(); + assert_eq!(s5.r#use, a); + + let s6: Field = c.into(); + assert_eq!(s6.foo, c); + + let s7: Const<4> = b.into(); + assert_eq!(s7.foo, b); +} diff --git a/tests/ui/explicit-tail-calls/callee_is_track_caller.rs b/tests/ui/explicit-tail-calls/callee_is_track_caller.rs index bcb93fda8c8..b85b335844b 100644 --- a/tests/ui/explicit-tail-calls/callee_is_track_caller.rs +++ b/tests/ui/explicit-tail-calls/callee_is_track_caller.rs @@ -1,10 +1,11 @@ -//@ check-pass -// FIXME(explicit_tail_calls): make this run-pass, once tail calls are properly implemented +//@ run-pass +//@ ignore-pass #![expect(incomplete_features)] #![feature(explicit_tail_calls)] fn a(x: u32) -> u32 { become b(x); + //~^ warning: tail calling a function marked with `#[track_caller]` has no special effect } #[track_caller] diff --git a/tests/ui/explicit-tail-calls/callee_is_track_caller.stderr b/tests/ui/explicit-tail-calls/callee_is_track_caller.stderr new file mode 100644 index 00000000000..e1a251d156f --- /dev/null +++ b/tests/ui/explicit-tail-calls/callee_is_track_caller.stderr @@ -0,0 +1,10 @@ +warning: tail calling a function marked with `#[track_caller]` has no special effect + --> $DIR/callee_is_track_caller.rs:7:12 + | +LL | become b(x); + | ^^^^ + | + = note: `#[warn(tail_call_track_caller)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/explicit-tail-calls/callee_is_track_caller_polymorphic.rs b/tests/ui/explicit-tail-calls/callee_is_track_caller_polymorphic.rs new file mode 100644 index 00000000000..33384de83eb --- /dev/null +++ b/tests/ui/explicit-tail-calls/callee_is_track_caller_polymorphic.rs @@ -0,0 +1,20 @@ +//@ run-pass +//@ ignore-pass +#![expect(incomplete_features)] +#![feature(explicit_tail_calls)] + +fn c<T: Trait>() { + become T::f(); + //~^ warning: tail calling a function marked with `#[track_caller]` has no special effect +} + +trait Trait { + #[track_caller] + fn f() {} +} + +impl Trait for () {} + +fn main() { + c::<()>(); +} diff --git a/tests/ui/explicit-tail-calls/callee_is_track_caller_polymorphic.stderr b/tests/ui/explicit-tail-calls/callee_is_track_caller_polymorphic.stderr new file mode 100644 index 00000000000..5a1c40509ad --- /dev/null +++ b/tests/ui/explicit-tail-calls/callee_is_track_caller_polymorphic.stderr @@ -0,0 +1,10 @@ +warning: tail calling a function marked with `#[track_caller]` has no special effect + --> $DIR/callee_is_track_caller_polymorphic.rs:7:12 + | +LL | become T::f(); + | ^^^^^^ + | + = note: `#[warn(tail_call_track_caller)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/explicit-tail-calls/caller-lifetime-presence.rs b/tests/ui/explicit-tail-calls/caller-lifetime-presence.rs new file mode 100644 index 00000000000..2382bc9dd1e --- /dev/null +++ b/tests/ui/explicit-tail-calls/caller-lifetime-presence.rs @@ -0,0 +1,51 @@ +//! Regression test for: https://github.com/rust-lang/rust/issues/144957 +//! +//! This test ensures that lifetime information is included in diagnostics. +//! +//! Specifically, it checks that the `become` call produces an error with lifetimes shown +//! in both caller and callee signatures. +//! +//! If the test fails: +//! - Lifetimes may be missing (fix the diagnostic), or +//! - The message format changed (update the test). + +#![feature(explicit_tail_calls)] +#![allow(incomplete_features)] + +fn foo<'a>(_: fn(&'a ())) { + become bar(dummy); + //~^ ERROR mismatched signatures + //~| NOTE `become` requires caller and callee to have matching signatures + //~| NOTE caller signature: `fn(fn(&'a ()))` + //~| NOTE callee signature: `fn(for<'a> fn(&'a ()))` +} + +fn bar(_: fn(&())) {} + +fn dummy(_: &()) {} + +fn foo_(_: fn(&())) { + become bar1(dummy2); + //~^ ERROR mismatched signatures + //~| NOTE `become` requires caller and callee to have matching signatures + //~| NOTE caller signature: `fn(for<'a> fn(&'a ()))` + //~| NOTE callee signature: `fn(fn(&'a ()))` +} + +fn bar1<'a>(_: fn(&'a ())) {} + +fn dummy2(_: &()) {} + +fn foo__(_: fn(&'static ())) { + become bar(dummy3); + //~^ ERROR mismatched signatures + //~| NOTE `become` requires caller and callee to have matching signatures + //~| NOTE caller signature: `fn(fn(&'static ()))` + //~| NOTE callee signature: `fn(for<'a> fn(&'a ()))` +} + +fn bar2(_: fn(&())) {} + +fn dummy3(_: &()) {} + +fn main() {} diff --git a/tests/ui/explicit-tail-calls/caller-lifetime-presence.stderr b/tests/ui/explicit-tail-calls/caller-lifetime-presence.stderr new file mode 100644 index 00000000000..2fb981d9682 --- /dev/null +++ b/tests/ui/explicit-tail-calls/caller-lifetime-presence.stderr @@ -0,0 +1,32 @@ +error: mismatched signatures + --> $DIR/caller-lifetime-presence.rs:16:5 + | +LL | become bar(dummy); + | ^^^^^^^^^^^^^^^^^ + | + = note: `become` requires caller and callee to have matching signatures + = note: caller signature: `fn(fn(&'a ()))` + = note: callee signature: `fn(for<'a> fn(&'a ()))` + +error: mismatched signatures + --> $DIR/caller-lifetime-presence.rs:28:5 + | +LL | become bar1(dummy2); + | ^^^^^^^^^^^^^^^^^^^ + | + = note: `become` requires caller and callee to have matching signatures + = note: caller signature: `fn(for<'a> fn(&'a ()))` + = note: callee signature: `fn(fn(&'a ()))` + +error: mismatched signatures + --> $DIR/caller-lifetime-presence.rs:40:5 + | +LL | become bar(dummy3); + | ^^^^^^^^^^^^^^^^^^ + | + = note: `become` requires caller and callee to have matching signatures + = note: caller signature: `fn(fn(&'static ()))` + = note: callee signature: `fn(for<'a> fn(&'a ()))` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/extern-flag/empty-extern-arg.rs b/tests/ui/extern-flag/empty-extern-arg.rs index 2dee721ed03..10cc3be7113 100644 --- a/tests/ui/extern-flag/empty-extern-arg.rs +++ b/tests/ui/extern-flag/empty-extern-arg.rs @@ -1,4 +1,5 @@ //~ ERROR extern location for std does not exist +//~^ ERROR cannot resolve a prelude import //@ compile-flags: --extern std= //@ needs-unwind since it affects the error output //@ ignore-emscripten missing eh_catch_typeinfo lang item diff --git a/tests/ui/extern-flag/empty-extern-arg.stderr b/tests/ui/extern-flag/empty-extern-arg.stderr index 79efcc5d8b0..3e0a0d6cd5f 100644 --- a/tests/ui/extern-flag/empty-extern-arg.stderr +++ b/tests/ui/extern-flag/empty-extern-arg.stderr @@ -1,5 +1,7 @@ error: extern location for std does not exist: +error: cannot resolve a prelude import + error: `#[panic_handler]` function required, but not found error: unwinding panics are not supported without std @@ -7,5 +9,5 @@ error: unwinding panics are not supported without std = help: using nightly cargo, use -Zbuild-std with panic="abort" to avoid unwinding = note: since the core library is usually precompiled with panic="unwind", rebuilding your crate with panic="abort" may not be enough to fix the problem -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/feature-gates/feature-gate-derive-from.rs b/tests/ui/feature-gates/feature-gate-derive-from.rs new file mode 100644 index 00000000000..12440356ddf --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-derive-from.rs @@ -0,0 +1,6 @@ +//@ edition: 2021 + +#[derive(From)] //~ ERROR use of unstable library feature `derive_from` +struct Foo(u32); + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-derive-from.stderr b/tests/ui/feature-gates/feature-gate-derive-from.stderr new file mode 100644 index 00000000000..d58dcdd7541 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-derive-from.stderr @@ -0,0 +1,13 @@ +error[E0658]: use of unstable library feature `derive_from` + --> $DIR/feature-gate-derive-from.rs:3:10 + | +LL | #[derive(From)] + | ^^^^ + | + = note: see issue #144889 <https://github.com/rust-lang/rust/issues/144889> for more information + = help: add `#![feature(derive_from)]` 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/generics/post_monomorphization_error_backtrace.stderr b/tests/ui/generics/post_monomorphization_error_backtrace.stderr index 6953414f0c2..92c7df73638 100644 --- a/tests/ui/generics/post_monomorphization_error_backtrace.stderr +++ b/tests/ui/generics/post_monomorphization_error_backtrace.stderr @@ -1,8 +1,8 @@ error[E0080]: evaluation panicked: assertion failed: std::mem::size_of::<T>() == 0 - --> $DIR/post_monomorphization_error_backtrace.rs:6:23 + --> $DIR/post_monomorphization_error_backtrace.rs:6:31 | LL | const V: () = assert!(std::mem::size_of::<T>() == 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `assert_zst::F::<u32>::V` failed here + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `assert_zst::F::<u32>::V` failed here note: erroneous constant encountered --> $DIR/post_monomorphization_error_backtrace.rs:14:5 @@ -17,10 +17,10 @@ LL | assert_zst::<U>() | ^^^^^^^^^^^^^^^^^ error[E0080]: evaluation panicked: assertion failed: std::mem::size_of::<T>() == 0 - --> $DIR/post_monomorphization_error_backtrace.rs:6:23 + --> $DIR/post_monomorphization_error_backtrace.rs:6:31 | LL | const V: () = assert!(std::mem::size_of::<T>() == 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `assert_zst::F::<i32>::V` failed here + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `assert_zst::F::<i32>::V` failed here note: erroneous constant encountered --> $DIR/post_monomorphization_error_backtrace.rs:14:5 diff --git a/tests/ui/inline-const/const-expr-generic-err.stderr b/tests/ui/inline-const/const-expr-generic-err.stderr index 26039ba6d44..e053e88db17 100644 --- a/tests/ui/inline-const/const-expr-generic-err.stderr +++ b/tests/ui/inline-const/const-expr-generic-err.stderr @@ -1,8 +1,8 @@ error[E0080]: evaluation panicked: assertion failed: std::mem::size_of::<T>() == 0 - --> $DIR/const-expr-generic-err.rs:4:13 + --> $DIR/const-expr-generic-err.rs:4:21 | LL | const { assert!(std::mem::size_of::<T>() == 0); } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `foo::<i32>::{constant#0}` failed here + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `foo::<i32>::{constant#0}` failed here note: erroneous constant encountered --> $DIR/const-expr-generic-err.rs:4:5 diff --git a/tests/ui/lint/dropping_copy_types-macros.fixed b/tests/ui/lint/dropping_copy_types-macros.fixed index a8ceedadc80..2a0c3988157 100644 --- a/tests/ui/lint/dropping_copy_types-macros.fixed +++ b/tests/ui/lint/dropping_copy_types-macros.fixed @@ -9,4 +9,7 @@ fn main() { let mut msg = String::new(); let _ = writeln!(&mut msg, "test"); //~^ ERROR calls to `std::mem::drop` + + let _ = format_args!("a"); + //~^ ERROR calls to `std::mem::drop` } diff --git a/tests/ui/lint/dropping_copy_types-macros.rs b/tests/ui/lint/dropping_copy_types-macros.rs index b249b0c868f..2272e0e8560 100644 --- a/tests/ui/lint/dropping_copy_types-macros.rs +++ b/tests/ui/lint/dropping_copy_types-macros.rs @@ -9,4 +9,7 @@ fn main() { let mut msg = String::new(); drop(writeln!(&mut msg, "test")); //~^ ERROR calls to `std::mem::drop` + + drop(format_args!("a")); + //~^ ERROR calls to `std::mem::drop` } diff --git a/tests/ui/lint/dropping_copy_types-macros.stderr b/tests/ui/lint/dropping_copy_types-macros.stderr index 117e9f4fe09..5048f6e3f18 100644 --- a/tests/ui/lint/dropping_copy_types-macros.stderr +++ b/tests/ui/lint/dropping_copy_types-macros.stderr @@ -17,5 +17,19 @@ LL - drop(writeln!(&mut msg, "test")); LL + let _ = writeln!(&mut msg, "test"); | -error: aborting due to 1 previous error +error: calls to `std::mem::drop` with a value that implements `Copy` does nothing + --> $DIR/dropping_copy_types-macros.rs:13:5 + | +LL | drop(format_args!("a")); + | ^^^^^-----------------^ + | | + | argument has type `Arguments<'_>` + | +help: use `let _ = ...` to ignore the expression or result + | +LL - drop(format_args!("a")); +LL + let _ = format_args!("a"); + | + +error: aborting due to 2 previous errors diff --git a/tests/ui/macros/assert-macro-lifetimes.rs b/tests/ui/macros/assert-macro-lifetimes.rs new file mode 100644 index 00000000000..cc259283204 --- /dev/null +++ b/tests/ui/macros/assert-macro-lifetimes.rs @@ -0,0 +1,8 @@ +//@ check-pass +#[derive(PartialEq, Eq, Hash)] +struct S; +fn main() { + let foo = std::rc::Rc::new(std::cell::RefCell::new(std::collections::HashMap::<S, S>::new())); + // Ensure that the lifetimes of the borrow do not leak past the end of `main`. + assert!(matches!(foo.borrow().get(&S).unwrap(), S)) +} diff --git a/tests/ui/mir/alignment/packed.rs b/tests/ui/mir/alignment/packed.rs index cf908365e1a..aa79880a21a 100644 --- a/tests/ui/mir/alignment/packed.rs +++ b/tests/ui/mir/alignment/packed.rs @@ -12,7 +12,7 @@ fn main() { // Test that we can use addr_of! to get the address of a packed member which according to its // type is not aligned, but because it is a projection from a packed type is a valid place. let ptr0 = std::ptr::addr_of!(memory[0].tail); - let ptr1 = std::ptr::addr_of!(memory[0].tail); + let ptr1 = std::ptr::addr_of!(memory[1].tail); // Even if ptr0 happens to be aligned by chance, ptr1 is not. assert!(!ptr0.is_aligned() || !ptr1.is_aligned()); diff --git a/tests/ui/nll/member-constraints/non-root-universe-existential-1.rs b/tests/ui/nll/member-constraints/non-root-universe-existential-1.rs new file mode 100644 index 00000000000..39dbfebce10 --- /dev/null +++ b/tests/ui/nll/member-constraints/non-root-universe-existential-1.rs @@ -0,0 +1,29 @@ +//@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +trait Proj<'a> { + type Assoc; +} + +impl<'a, 'b, F: FnOnce() -> &'b ()> Proj<'a> for F { + type Assoc = (); +} + +fn is_proj<F: for<'a> Proj<'a>>(f: F) {} + +fn define<'a>() -> impl Sized + use<'a> { + // This defines the RPIT to `&'unconstrained_b ()`, an inference + // variable which is in a higher universe as gets created inside + // of the binder of `F: for<'a> Proj<'a>`. This previously caused + // us to not apply member constraints. + // + // This was unnecessary. It is totally acceptable for member regions + // to be able to name placeholders from higher universes, as long as + // they don't actually do so. + is_proj(define::<'a>); + &() +} + +fn main() {} diff --git a/tests/ui/nll/member-constraints/non-root-universe-existential-2.rs b/tests/ui/nll/member-constraints/non-root-universe-existential-2.rs new file mode 100644 index 00000000000..c5ddde0d7d8 --- /dev/null +++ b/tests/ui/nll/member-constraints/non-root-universe-existential-2.rs @@ -0,0 +1,31 @@ +//@ check-pass + +// Unlike `non-root-universe-existential-1.rs` this previously +// compiled as it simply didn't define the hidden type of +// `impl Iterator` when projecting through it. We will do so +// with the new solver. Further minimizing this is challenging. + +struct Type(Vec<Type>); +enum TypeTreeValueIter<'a, T> { + Once(T), + Ref(&'a ()), +} + +impl<'a, T> Iterator for TypeTreeValueIter<'a, T> { + type Item = T; + + fn next(&mut self) -> Option<Self::Item> { + loop {} + } +} + +fn item<I: Iterator<Item: Iterator>>(x: I) -> <I::Item as Iterator>::Item { + loop {} +} + +fn get_type_tree_values<'a>(ty: &'a Type) -> impl Iterator<Item = &'a Type> { + let _: &'a Type = item(std::iter::once(ty).map(get_type_tree_values)); + TypeTreeValueIter::<'a, &'a Type>::Once(ty) +} + +fn main() {} diff --git a/tests/ui/parser/inverted-parameters.rs b/tests/ui/parser/inverted-parameters.rs index bc2f53f0be1..5b2827b6fee 100644 --- a/tests/ui/parser/inverted-parameters.rs +++ b/tests/ui/parser/inverted-parameters.rs @@ -23,7 +23,6 @@ fn pattern((i32, i32) (a, b)) {} fn fizz(i32) {} //~^ ERROR expected one of `:`, `@` //~| HELP if this is a parameter name, give it a type -//~| HELP if this is a `self` type, give it a parameter name //~| HELP if this is a type, explicitly ignore the parameter name fn missing_colon(quux S) {} diff --git a/tests/ui/parser/inverted-parameters.stderr b/tests/ui/parser/inverted-parameters.stderr index 7b969032d0f..93b95a75608 100644 --- a/tests/ui/parser/inverted-parameters.stderr +++ b/tests/ui/parser/inverted-parameters.stderr @@ -34,11 +34,6 @@ error: expected one of `:`, `@`, or `|`, found `)` LL | fn fizz(i32) {} | ^ expected one of `:`, `@`, or `|` | - = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) -help: if this is a `self` type, give it a parameter name - | -LL | fn fizz(self: i32) {} - | +++++ help: if this is a parameter name, give it a type | LL | fn fizz(i32: TypeName) {} @@ -49,7 +44,7 @@ LL | fn fizz(_: i32) {} | ++ error: expected one of `:`, `@`, or `|`, found `S` - --> $DIR/inverted-parameters.rs:29:23 + --> $DIR/inverted-parameters.rs:28:23 | LL | fn missing_colon(quux S) {} | -----^ diff --git a/tests/ui/parser/lifetime-in-pattern.stderr b/tests/ui/parser/lifetime-in-pattern.stderr index ffda28b202b..0d01120a057 100644 --- a/tests/ui/parser/lifetime-in-pattern.stderr +++ b/tests/ui/parser/lifetime-in-pattern.stderr @@ -16,11 +16,6 @@ error: expected one of `:`, `@`, or `|`, found `)` LL | fn test(&'a str) { | ^ expected one of `:`, `@`, or `|` | - = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) -help: if this is a `self` type, give it a parameter name - | -LL | fn test(self: &'a str) { - | +++++ help: if this is a parameter name, give it a type | LL - fn test(&'a str) { diff --git a/tests/ui/parser/omitted-arg-in-item-fn.stderr b/tests/ui/parser/omitted-arg-in-item-fn.stderr index 6f2a9f64c94..d6bc5d9c68f 100644 --- a/tests/ui/parser/omitted-arg-in-item-fn.stderr +++ b/tests/ui/parser/omitted-arg-in-item-fn.stderr @@ -4,11 +4,6 @@ error: expected one of `:`, `@`, or `|`, found `)` LL | fn foo(x) { | ^ expected one of `:`, `@`, or `|` | - = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) -help: if this is a `self` type, give it a parameter name - | -LL | fn foo(self: x) { - | +++++ help: if this is a parameter name, give it a type | LL | fn foo(x: TypeName) { diff --git a/tests/ui/parser/pat-lt-bracket-2.stderr b/tests/ui/parser/pat-lt-bracket-2.stderr index 5fe97b2ef4c..9e056bd63ec 100644 --- a/tests/ui/parser/pat-lt-bracket-2.stderr +++ b/tests/ui/parser/pat-lt-bracket-2.stderr @@ -4,11 +4,6 @@ error: expected one of `:`, `@`, or `|`, found `<` LL | fn a(B<) {} | ^ expected one of `:`, `@`, or `|` | - = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) -help: if this is a `self` type, give it a parameter name - | -LL | fn a(self: B<) {} - | +++++ help: if this is a type, explicitly ignore the parameter name | LL | fn a(_: B<) {} diff --git a/tests/ui/parser/suggest-self-in-bare-function.rs b/tests/ui/parser/suggest-self-in-bare-function.rs new file mode 100644 index 00000000000..e7f2a12bcd0 --- /dev/null +++ b/tests/ui/parser/suggest-self-in-bare-function.rs @@ -0,0 +1,25 @@ +// We should not suggest `self` in bare functions. +// And a note for RFC 1685 should not be shown. +// See #144968 + +//@ edition:2018 + +fn is_even(value) -> bool { //~ ERROR expected one of `:`, `@`, or `|`, found `)` + value % 2 == 0 +} + +struct S; + +impl S { + fn is_even(value) -> bool { //~ ERROR expected one of `:`, `@`, or `|`, found `)` + value % 2 == 0 + } +} + +trait T { + fn is_even(value) -> bool { //~ ERROR expected one of `:`, `@`, or `|`, found `)` + value % 2 == 0 + } +} + +fn main() {} diff --git a/tests/ui/parser/suggest-self-in-bare-function.stderr b/tests/ui/parser/suggest-self-in-bare-function.stderr new file mode 100644 index 00000000000..40cbe5575fd --- /dev/null +++ b/tests/ui/parser/suggest-self-in-bare-function.stderr @@ -0,0 +1,56 @@ +error: expected one of `:`, `@`, or `|`, found `)` + --> $DIR/suggest-self-in-bare-function.rs:7:17 + | +LL | fn is_even(value) -> bool { + | ^ expected one of `:`, `@`, or `|` + | +help: if this is a parameter name, give it a type + | +LL | fn is_even(value: TypeName) -> bool { + | ++++++++++ +help: if this is a type, explicitly ignore the parameter name + | +LL | fn is_even(_: value) -> bool { + | ++ + +error: expected one of `:`, `@`, or `|`, found `)` + --> $DIR/suggest-self-in-bare-function.rs:14:21 + | +LL | fn is_even(value) -> bool { + | ^ expected one of `:`, `@`, or `|` + | +help: if this is a `self` type, give it a parameter name + | +LL | fn is_even(self: value) -> bool { + | +++++ +help: if this is a parameter name, give it a type + | +LL | fn is_even(value: TypeName) -> bool { + | ++++++++++ +help: if this is a type, explicitly ignore the parameter name + | +LL | fn is_even(_: value) -> bool { + | ++ + +error: expected one of `:`, `@`, or `|`, found `)` + --> $DIR/suggest-self-in-bare-function.rs:20:21 + | +LL | fn is_even(value) -> bool { + | ^ expected one of `:`, `@`, or `|` + | + = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) +help: if this is a `self` type, give it a parameter name + | +LL | fn is_even(self: value) -> bool { + | +++++ +help: if this is a parameter name, give it a type + | +LL | fn is_even(value: TypeName) -> bool { + | ++++++++++ +help: if this is a type, explicitly ignore the parameter name + | +LL | fn is_even(_: value) -> bool { + | ++ + +error: aborting due to 3 previous errors + diff --git a/tests/ui/proc-macro/derive-helper-legacy-spurious.rs b/tests/ui/proc-macro/derive-helper-legacy-spurious.rs index 8e902f30419..0b55e775f38 100644 --- a/tests/ui/proc-macro/derive-helper-legacy-spurious.rs +++ b/tests/ui/proc-macro/derive-helper-legacy-spurious.rs @@ -6,7 +6,7 @@ extern crate test_macros; #[derive(Empty)] -#[empty_helper] //~ ERROR cannot find attribute `empty_helper` in this scope +#[empty_helper] struct Foo {} fn main() {} diff --git a/tests/ui/proc-macro/derive-helper-legacy-spurious.stderr b/tests/ui/proc-macro/derive-helper-legacy-spurious.stderr index b34713b8ca6..4cd89904ef3 100644 --- a/tests/ui/proc-macro/derive-helper-legacy-spurious.stderr +++ b/tests/ui/proc-macro/derive-helper-legacy-spurious.stderr @@ -4,11 +4,5 @@ error: cannot find attribute `dummy` in this scope LL | #![dummy] | ^^^^^ -error: cannot find attribute `empty_helper` in this scope - --> $DIR/derive-helper-legacy-spurious.rs:9:3 - | -LL | #[empty_helper] - | ^^^^^^^^^^^^ - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error diff --git a/tests/ui/resolve/inner-attr-prelude-macro.rs b/tests/ui/resolve/inner-attr-prelude-macro.rs new file mode 100644 index 00000000000..1047c908ad1 --- /dev/null +++ b/tests/ui/resolve/inner-attr-prelude-macro.rs @@ -0,0 +1,11 @@ +//@ check-pass +//! This test checks that macro names resolved from the libstd prelude +//! still work even if there's a crate-level custom inner attribute. + +#![feature(custom_inner_attributes)] + +#![rustfmt::skip] + +fn main() { + let _ = todo!(); +} diff --git a/tests/ui/resolve/issue-104700-inner_scope.rs b/tests/ui/resolve/issue-104700-inner_scope.rs index e8f28c113e3..fb77ab2626b 100644 --- a/tests/ui/resolve/issue-104700-inner_scope.rs +++ b/tests/ui/resolve/issue-104700-inner_scope.rs @@ -7,5 +7,12 @@ fn main() { if bar == 2 { //~ ERROR cannot find value println!("yes"); } + { + let baz = 3; + struct S; + } + if baz == 3 { //~ ERROR cannot find value + println!("yes"); + } test_func(1); //~ ERROR cannot find function } diff --git a/tests/ui/resolve/issue-104700-inner_scope.stderr b/tests/ui/resolve/issue-104700-inner_scope.stderr index 051b234fc72..b579f8e76d9 100644 --- a/tests/ui/resolve/issue-104700-inner_scope.stderr +++ b/tests/ui/resolve/issue-104700-inner_scope.stderr @@ -10,12 +10,24 @@ help: the binding `bar` is available in a different scope in the same function LL | let bar = 2; | ^^^ +error[E0425]: cannot find value `baz` in this scope + --> $DIR/issue-104700-inner_scope.rs:14:8 + | +LL | if baz == 3 { + | ^^^ + | +help: the binding `baz` is available in a different scope in the same function + --> $DIR/issue-104700-inner_scope.rs:11:13 + | +LL | let baz = 3; + | ^^^ + error[E0425]: cannot find function `test_func` in this scope - --> $DIR/issue-104700-inner_scope.rs:10:5 + --> $DIR/issue-104700-inner_scope.rs:17:5 | LL | test_func(1); | ^^^^^^^^^ not found in this scope -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/rfcs/rfc-1789-as-cell/from-mut.rs b/tests/ui/rfcs/rfc-1789-as-cell/from-mut.rs index d3b441fbe88..700f875a4f6 100644 --- a/tests/ui/rfcs/rfc-1789-as-cell/from-mut.rs +++ b/tests/ui/rfcs/rfc-1789-as-cell/from-mut.rs @@ -1,7 +1,5 @@ //@ run-pass -#![feature(as_array_of_cells)] - use std::cell::Cell; fn main() { diff --git a/tests/ui/simd/const-err-trumps-simd-err.stderr b/tests/ui/simd/const-err-trumps-simd-err.stderr index 93d1fce637f..6d25a28c92c 100644 --- a/tests/ui/simd/const-err-trumps-simd-err.stderr +++ b/tests/ui/simd/const-err-trumps-simd-err.stderr @@ -1,8 +1,8 @@ error[E0080]: evaluation panicked: assertion failed: LANE < 4 - --> $DIR/const-err-trumps-simd-err.rs:17:13 + --> $DIR/const-err-trumps-simd-err.rs:17:21 | LL | const { assert!(LANE < 4); } // the error should be here... - | ^^^^^^^^^^^^^^^^^ evaluation of `get_elem::<4>::{constant#0}` failed here + | ^^^^^^^^ evaluation of `get_elem::<4>::{constant#0}` failed here note: erroneous constant encountered --> $DIR/const-err-trumps-simd-err.rs:17:5 diff --git a/tests/ui/span/issue-34264.stderr b/tests/ui/span/issue-34264.stderr index c8046a1bddf..cc41d3048df 100644 --- a/tests/ui/span/issue-34264.stderr +++ b/tests/ui/span/issue-34264.stderr @@ -4,11 +4,6 @@ error: expected one of `:`, `@`, or `|`, found `<` LL | fn foo(Option<i32>, String) {} | ^ expected one of `:`, `@`, or `|` | - = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) -help: if this is a `self` type, give it a parameter name - | -LL | fn foo(self: Option<i32>, String) {} - | +++++ help: if this is a type, explicitly ignore the parameter name | LL | fn foo(_: Option<i32>, String) {} @@ -20,7 +15,6 @@ error: expected one of `:`, `@`, or `|`, found `)` LL | fn foo(Option<i32>, String) {} | ^ expected one of `:`, `@`, or `|` | - = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) help: if this is a parameter name, give it a type | LL | fn foo(Option<i32>, String: TypeName) {} @@ -36,11 +30,6 @@ error: expected one of `:`, `@`, or `|`, found `,` LL | fn bar(x, y: usize) {} | ^ expected one of `:`, `@`, or `|` | - = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) -help: if this is a `self` type, give it a parameter name - | -LL | fn bar(self: x, y: usize) {} - | +++++ help: if this is a parameter name, give it a type | LL | fn bar(x: TypeName, y: usize) {} diff --git a/tests/ui/structs/default-field-values/non-exhaustive-ctor-not-found.rs b/tests/ui/structs/default-field-values/non-exhaustive-ctor-not-found.rs new file mode 100644 index 00000000000..4c5926a91ea --- /dev/null +++ b/tests/ui/structs/default-field-values/non-exhaustive-ctor-not-found.rs @@ -0,0 +1,8 @@ +// Regression test for https://github.com/rust-lang/rust/issues/145367 +mod m { + struct Priv2; +} +fn main() { + WithUse { one: m::Priv2 } //~ ERROR: cannot find struct, variant or union type `WithUse` in this scope + //~^ ERROR: unit struct `Priv2` is private +} diff --git a/tests/ui/structs/default-field-values/non-exhaustive-ctor-not-found.stderr b/tests/ui/structs/default-field-values/non-exhaustive-ctor-not-found.stderr new file mode 100644 index 00000000000..1f0c80092b7 --- /dev/null +++ b/tests/ui/structs/default-field-values/non-exhaustive-ctor-not-found.stderr @@ -0,0 +1,22 @@ +error[E0422]: cannot find struct, variant or union type `WithUse` in this scope + --> $DIR/non-exhaustive-ctor-not-found.rs:6:5 + | +LL | WithUse { one: m::Priv2 } + | ^^^^^^^ not found in this scope + +error[E0603]: unit struct `Priv2` is private + --> $DIR/non-exhaustive-ctor-not-found.rs:6:23 + | +LL | WithUse { one: m::Priv2 } + | ^^^^^ private unit struct + | +note: the unit struct `Priv2` is defined here + --> $DIR/non-exhaustive-ctor-not-found.rs:3:5 + | +LL | struct Priv2; + | ^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0422, E0603. +For more information about an error, try `rustc --explain E0422`. diff --git a/tests/ui/suggestions/issue-64252-self-type.stderr b/tests/ui/suggestions/issue-64252-self-type.stderr index c3418dab0e8..320d09b589b 100644 --- a/tests/ui/suggestions/issue-64252-self-type.stderr +++ b/tests/ui/suggestions/issue-64252-self-type.stderr @@ -4,11 +4,6 @@ error: expected one of `:`, `@`, or `|`, found `<` LL | pub fn foo(Box<Self>) { } | ^ expected one of `:`, `@`, or `|` | - = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) -help: if this is a `self` type, give it a parameter name - | -LL | pub fn foo(self: Box<Self>) { } - | +++++ help: if this is a type, explicitly ignore the parameter name | LL | pub fn foo(_: Box<Self>) { } @@ -20,7 +15,6 @@ error: expected one of `:`, `@`, or `|`, found `<` LL | fn bar(Box<Self>) { } | ^ expected one of `:`, `@`, or `|` | - = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) help: if this is a `self` type, give it a parameter name | LL | fn bar(self: Box<Self>) { } diff --git a/tests/ui/thir-print/thir-tree-loop-match.stdout b/tests/ui/thir-print/thir-tree-loop-match.stdout index 5c4c50cb156..f2b63782c59 100644 --- a/tests/ui/thir-print/thir-tree-loop-match.stdout +++ b/tests/ui/thir-print/thir-tree-loop-match.stdout @@ -121,7 +121,7 @@ body: span: $DIR/thir-tree-loop-match.rs:12:17: 12:21 (#0) kind: PatKind { Constant { - value: Ty(bool, true) + value: true } } } @@ -219,7 +219,7 @@ body: span: $DIR/thir-tree-loop-match.rs:16:17: 16:22 (#0) kind: PatKind { Constant { - value: Ty(bool, false) + value: false } } } diff --git a/tests/ui/traits/const-traits/const-trait-async-assoc-fn.rs b/tests/ui/traits/const-traits/const-trait-async-assoc-fn.rs new file mode 100644 index 00000000000..00fdccc2ac8 --- /dev/null +++ b/tests/ui/traits/const-traits/const-trait-async-assoc-fn.rs @@ -0,0 +1,18 @@ +//@ edition: 2021 +#![feature(const_trait_impl)] + +const trait Tr { + async fn ft1() {} +//~^ ERROR async functions are not allowed in `const` traits +} + +const trait Tr2 { + fn f() -> impl std::future::Future<Output = ()>; +} + +impl const Tr2 for () { + async fn f() {} +//~^ ERROR async functions are not allowed in `const` trait impls +} + +fn main() {} diff --git a/tests/ui/traits/const-traits/const-trait-async-assoc-fn.stderr b/tests/ui/traits/const-traits/const-trait-async-assoc-fn.stderr new file mode 100644 index 00000000000..09ba0969dc9 --- /dev/null +++ b/tests/ui/traits/const-traits/const-trait-async-assoc-fn.stderr @@ -0,0 +1,18 @@ +error: async functions are not allowed in `const` traits + --> $DIR/const-trait-async-assoc-fn.rs:5:5 + | +LL | const trait Tr { + | ----- associated functions of `const` cannot be declared `async` +LL | async fn ft1() {} + | ^^^^^ + +error: async functions are not allowed in `const` trait impls + --> $DIR/const-trait-async-assoc-fn.rs:14:5 + | +LL | impl const Tr2 for () { + | ----- associated functions of `const` cannot be declared `async` +LL | async fn f() {} + | ^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/traits/next-solver/cycles/many-where-clauses-with-aliases-hang.rs b/tests/ui/traits/next-solver/cycles/many-where-clauses-with-aliases-hang.rs new file mode 100644 index 00000000000..36fa8b85f7a --- /dev/null +++ b/tests/ui/traits/next-solver/cycles/many-where-clauses-with-aliases-hang.rs @@ -0,0 +1,43 @@ +//@ compile-flags: -Znext-solver +//@ check-pass + +// A regression test for trait-system-refactor-initiative#210. +// +// Trying to prove `T: Trait<...>` ends up trying to apply all the where-clauses, +// most of which require normalizing some `Alias<T, ...>`. This then requires us +// to prove `T: Trait<...>` again. +// +// This results in a lot of solver cycles whose initial result differs from their +// final result. Reevaluating all of them results in exponential blowup and hangs. +// +// With #144991 we now don't reevaluate cycle heads if their provisional value +// didn't actually impact the final result, avoiding these reruns and allowing us +// to compile this in less than a second. + +struct A; +struct B; +struct C; + +type Alias<T, U> = <T as Trait<U>>::Assoc; +trait Trait<T> { + type Assoc; +} + +fn foo<T>() +where + T: Trait<A> + Trait<B> + Trait<C>, + T: Trait<Alias<T, A>>, + T: Trait<Alias<T, B>>, + T: Trait<Alias<T, C>>, + T: Trait<Alias<T, Alias<T, A>>>, + T: Trait<Alias<T, Alias<T, B>>>, + T: Trait<Alias<T, Alias<T, C>>>, + T: Trait<Alias<T, Alias<T, Alias<T, A>>>>, + T: Trait<Alias<T, Alias<T, Alias<T, B>>>>, + T: Trait<Alias<T, Alias<T, Alias<T, C>>>>, + T: Trait<Alias<T, Alias<T, Alias<T, Alias<T, A>>>>>, + T: Trait<Alias<T, Alias<T, Alias<T, Alias<T, B>>>>>, + T: Trait<Alias<T, Alias<T, Alias<T, Alias<T, C>>>>>, +{ +} +fn main() {} diff --git a/tests/ui/transmutability/uninhabited.stderr b/tests/ui/transmutability/uninhabited.stderr index 4757daec997..9f289852809 100644 --- a/tests/ui/transmutability/uninhabited.stderr +++ b/tests/ui/transmutability/uninhabited.stderr @@ -41,10 +41,10 @@ LL | | }> | |__________^ required by this bound in `is_maybe_transmutable` error[E0080]: evaluation panicked: assertion failed: false - --> $DIR/uninhabited.rs:41:9 + --> $DIR/uninhabited.rs:41:17 | LL | assert!(false); - | ^^^^^^^^^^^^^^ evaluation of `yawning_void_struct::_` failed here + | ^^^^^ evaluation of `yawning_void_struct::_` failed here error[E0277]: `()` cannot be safely transmuted into `yawning_void_enum::Void` --> $DIR/uninhabited.rs:71:41 @@ -68,10 +68,10 @@ LL | | }> | |__________^ required by this bound in `is_maybe_transmutable` error[E0080]: evaluation panicked: assertion failed: false - --> $DIR/uninhabited.rs:63:9 + --> $DIR/uninhabited.rs:63:17 | LL | assert!(false); - | ^^^^^^^^^^^^^^ evaluation of `yawning_void_enum::_` failed here + | ^^^^^ evaluation of `yawning_void_enum::_` failed here error[E0277]: `u128` cannot be safely transmuted into `DistantVoid` --> $DIR/uninhabited.rs:92:43 @@ -95,10 +95,10 @@ LL | | }> | |__________^ required by this bound in `is_maybe_transmutable` error[E0080]: evaluation panicked: assertion failed: false - --> $DIR/uninhabited.rs:87:9 + --> $DIR/uninhabited.rs:87:17 | LL | assert!(false); - | ^^^^^^^^^^^^^^ evaluation of `distant_void::_` failed here + | ^^^^^ evaluation of `distant_void::_` failed here error[E0277]: `Src` cannot be safely transmuted into `issue_126267::Error` --> $DIR/uninhabited.rs:108:42 diff --git a/tests/ui/type/issue-102598.stderr b/tests/ui/type/issue-102598.stderr index a232395cedb..5623a7aa80d 100644 --- a/tests/ui/type/issue-102598.stderr +++ b/tests/ui/type/issue-102598.stderr @@ -15,7 +15,6 @@ error: expected one of `:`, `@`, or `|`, found `)` LL | fn foo<'a>(_: impl 'a Sized) {} | ^ expected one of `:`, `@`, or `|` | - = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) help: if this is a parameter name, give it a type | LL | fn foo<'a>(_: impl 'a Sized: TypeName) {} diff --git a/tests/ui/type/type-name-basic.rs b/tests/ui/type/type-name-basic.rs index 6a9f772e542..e1310e1f365 100644 --- a/tests/ui/type/type-name-basic.rs +++ b/tests/ui/type/type-name-basic.rs @@ -62,28 +62,32 @@ pub fn main() { t!(Vec<Vec<u32>>, "alloc::vec::Vec<alloc::vec::Vec<u32>>"); t!(Foo<usize>, "type_name_basic::Foo<usize>"); - t!(Bar<'static>, "type_name_basic::Bar"); - t!(Baz<'static, u32>, "type_name_basic::Baz<u32>"); + t!(Bar<'static>, "type_name_basic::Bar<'_>"); + t!(Baz<'static, u32>, "type_name_basic::Baz<'_, u32>"); - // FIXME: lifetime omission means these all print badly. - t!(dyn TrL<'static>, "dyn type_name_basic::TrL<>"); - t!(dyn TrLA<'static, A = u32>, "dyn type_name_basic::TrLA<, A = u32>"); + t!(dyn TrL<'static>, "dyn type_name_basic::TrL<'_>"); + t!(dyn TrLA<'static, A = u32>, "dyn type_name_basic::TrLA<'_, A = u32>"); t!( dyn TrLT<'static, Cow<'static, ()>>, - "dyn type_name_basic::TrLT<, alloc::borrow::Cow<()>>" + "dyn type_name_basic::TrLT<'_, alloc::borrow::Cow<'_, ()>>" ); t!( dyn TrLTA<'static, u32, A = Cow<'static, ()>>, - "dyn type_name_basic::TrLTA<, u32, A = alloc::borrow::Cow<()>>" + "dyn type_name_basic::TrLTA<'_, u32, A = alloc::borrow::Cow<'_, ()>>" ); t!(fn(i32) -> i32, "fn(i32) -> i32"); - t!(dyn for<'a> Fn(&'a u32), "dyn core::ops::function::Fn(&u32)"); + t!(fn(&'static u32), "fn(&u32)"); + + // FIXME: these are sub-optimal, ideally the `for<...>` would be printed. + t!(for<'a> fn(&'a u32), "fn(&'_ u32)"); + t!(for<'a, 'b> fn(&'a u32, &'b u32), "fn(&'_ u32, &'_ u32)"); + t!(for<'a> fn(for<'b> fn(&'a u32, &'b u32)), "fn(fn(&'_ u32, &'_ u32))"); struct S<'a, T>(&'a T); impl<'a, T: Clone> S<'a, T> { fn test() { - t!(Cow<'a, T>, "alloc::borrow::Cow<u32>"); + t!(Cow<'a, T>, "alloc::borrow::Cow<'_, u32>"); } } S::<u32>::test(); diff --git a/tests/ui/unpretty/exhaustive.expanded.stdout b/tests/ui/unpretty/exhaustive.expanded.stdout index 6b08f3e1cd7..0327ad5f92b 100644 --- a/tests/ui/unpretty/exhaustive.expanded.stdout +++ b/tests/ui/unpretty/exhaustive.expanded.stdout @@ -34,9 +34,6 @@ extern crate std; #[prelude_import] use std::prelude::rust_2024::*; -#[prelude_import] -use self::prelude::*; - mod prelude { pub use std::prelude::rust_2024::*; @@ -47,6 +44,9 @@ mod prelude { } } +#[prelude_import] +use self::prelude::*; + mod attributes { //! inner single-line doc comment /*! diff --git a/tests/ui/unpretty/exhaustive.hir.stdout b/tests/ui/unpretty/exhaustive.hir.stdout index e9823c9575b..68356a33c9e 100644 --- a/tests/ui/unpretty/exhaustive.hir.stdout +++ b/tests/ui/unpretty/exhaustive.hir.stdout @@ -33,9 +33,6 @@ extern crate std; #[prelude_import] use std::prelude::rust_2024::*; -#[prelude_import] -use self::prelude::*; - mod prelude { use std::prelude::rust_2024::*; @@ -48,6 +45,9 @@ mod prelude { } } +#[prelude_import] +use self::prelude::*; + /// inner single-line doc comment /** * inner multi-line doc comment diff --git a/tests/ui/unpretty/exhaustive.rs b/tests/ui/unpretty/exhaustive.rs index 5292ddad4f6..b19d4f9fe2c 100644 --- a/tests/ui/unpretty/exhaustive.rs +++ b/tests/ui/unpretty/exhaustive.rs @@ -29,9 +29,6 @@ #![feature(yeet_expr)] #![allow(incomplete_features)] -#[prelude_import] -use self::prelude::*; - mod prelude { pub use std::prelude::rust_2024::*; @@ -42,6 +39,9 @@ mod prelude { } } +#[prelude_import] +use self::prelude::*; + mod attributes { //! inner single-line doc comment /*! diff --git a/triagebot.toml b/triagebot.toml index 6f6e95c5b50..2f31a30019b 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -283,7 +283,6 @@ trigger_files = [ "src/tools/enzyme", "src/doc/unstable-book/src/compiler-flags/autodiff.md", "compiler/rustc_ast/src/expand/autodiff_attrs.rs", - "compiler/rustc_monomorphize/src/partitioning/autodiff.rs", "compiler/rustc_codegen_llvm/src/builder/autodiff.rs", "compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs", ] @@ -628,6 +627,16 @@ trigger_files = [ "src/ci", ] +[autolabel."T-rust-analyzer"] +trigger_files = [ + "src/tools/rust-analyzer", +] + +[autolabel."T-rustfmt"] +trigger_files = [ + "src/tools/rustfmt", +] + # ------------------------------------------------------------------------------ # Prioritization and team nominations # ------------------------------------------------------------------------------ @@ -1285,8 +1294,6 @@ cc = ["@ZuseZ4"] cc = ["@ZuseZ4"] [mentions."compiler/rustc_builtin_macros/src/autodiff.rs"] cc = ["@ZuseZ4"] -[mentions."compiler/rustc_monomorphize/src/partitioning/autodiff.rs"] -cc = ["@ZuseZ4"] [mentions."compiler/rustc_codegen_llvm/src/builder/autodiff.rs"] cc = ["@ZuseZ4"] [mentions."compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs"] @@ -1589,3 +1596,8 @@ days-threshold = 28 # Documentation at: https://forge.rust-lang.org/triagebot/concern.html [concern] labels = ["S-waiting-on-concerns"] + +# Enable comments linking to triagebot range-diff when a PR is rebased +# onto a different base commit +# Documentation at: https://forge.rust-lang.org/triagebot/range-diff.html +[range-diff] |
