From 1819b4fa2339fcf416c8f3856f19190cab8c54ff Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Sat, 26 Oct 2024 17:46:22 -0700 Subject: [PATCH 1/7] rustdoc: make doctest span tweak a 2024 edition change Fixes #132203 This is a compatibility hack, because I think the new behavior is better. When an A `include_str!` B, and B `include_str!` C, the path to C should be resolved relative to B, not A. That's how `include!` itself works, so that's how `include_str!` with should work. --- src/librustdoc/doctest/rust.rs | 14 ++++++++++- .../doctest/auxiliary/extern_macros_2024.rs | 10 ++++++++ .../doctest/auxiliary/relative-dir-empty-file | 0 .../doctest/auxiliary/relative-dir.md | 3 +++ .../doctest/doctest-output-include-fail.rs | 3 ++- .../doctest/doctest-output.edition2015.stdout | 8 +++++++ .../doctest/doctest-output.edition2024.stdout | 8 +++++++ tests/rustdoc-ui/doctest/doctest-output.rs | 10 +++++--- .../rustdoc-ui/doctest/doctest-output.stdout | 8 ------- ...th-include-bytes-132203.edition2015.stdout | 24 +++++++++++++++++++ ...th-include-bytes-132203.edition2024.stdout | 6 +++++ .../relative-path-include-bytes-132203.rs | 17 +++++++++++++ 12 files changed, 98 insertions(+), 13 deletions(-) create mode 100644 tests/rustdoc-ui/doctest/auxiliary/extern_macros_2024.rs create mode 100644 tests/rustdoc-ui/doctest/auxiliary/relative-dir-empty-file create mode 100644 tests/rustdoc-ui/doctest/auxiliary/relative-dir.md create mode 100644 tests/rustdoc-ui/doctest/doctest-output.edition2015.stdout create mode 100644 tests/rustdoc-ui/doctest/doctest-output.edition2024.stdout delete mode 100644 tests/rustdoc-ui/doctest/doctest-output.stdout create mode 100644 tests/rustdoc-ui/doctest/relative-path-include-bytes-132203.edition2015.stdout create mode 100644 tests/rustdoc-ui/doctest/relative-path-include-bytes-132203.edition2024.stdout create mode 100644 tests/rustdoc-ui/doctest/relative-path-include-bytes-132203.rs diff --git a/src/librustdoc/doctest/rust.rs b/src/librustdoc/doctest/rust.rs index a9ab02e29cd95..27d0be133fea8 100644 --- a/src/librustdoc/doctest/rust.rs +++ b/src/librustdoc/doctest/rust.rs @@ -113,7 +113,19 @@ impl<'tcx> HirCollector<'tcx> { let attrs = Attributes::from_ast(ast_attrs); if let Some(doc) = attrs.opt_doc_value() { let span = span_of_fragments(&attrs.doc_strings).unwrap_or(sp); - self.collector.position = span; + self.collector.position = if span.edition().at_least_rust_2024() { + span + } else { + // this span affects filesystem path resolution, + // so we need to keep it the same as it was previously + ast_attrs + .iter() + .find(|attr| attr.doc_str().is_some()) + .map(|attr| { + attr.span.ctxt().outer_expn().expansion_cause().unwrap_or(attr.span) + }) + .unwrap_or(DUMMY_SP) + }; markdown::find_testable_code( &doc, &mut self.collector, diff --git a/tests/rustdoc-ui/doctest/auxiliary/extern_macros_2024.rs b/tests/rustdoc-ui/doctest/auxiliary/extern_macros_2024.rs new file mode 100644 index 0000000000000..354427000bf71 --- /dev/null +++ b/tests/rustdoc-ui/doctest/auxiliary/extern_macros_2024.rs @@ -0,0 +1,10 @@ +//@ edition:2024 +//@ compile-flags:-Z unstable-options +#![crate_name="extern_macros"] +#[macro_export] +macro_rules! attrs_on_struct { + ( $( #[$attr:meta] )* ) => { + $( #[$attr] )* + pub struct ExpandedStruct; + } +} diff --git a/tests/rustdoc-ui/doctest/auxiliary/relative-dir-empty-file b/tests/rustdoc-ui/doctest/auxiliary/relative-dir-empty-file new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/tests/rustdoc-ui/doctest/auxiliary/relative-dir.md b/tests/rustdoc-ui/doctest/auxiliary/relative-dir.md new file mode 100644 index 0000000000000..90097ce82fb0a --- /dev/null +++ b/tests/rustdoc-ui/doctest/auxiliary/relative-dir.md @@ -0,0 +1,3 @@ +```rust +let x = include_bytes!("relative-dir-empty-file"); +``` diff --git a/tests/rustdoc-ui/doctest/doctest-output-include-fail.rs b/tests/rustdoc-ui/doctest/doctest-output-include-fail.rs index 4fc0674a0c98b..bae61992eb212 100644 --- a/tests/rustdoc-ui/doctest/doctest-output-include-fail.rs +++ b/tests/rustdoc-ui/doctest/doctest-output-include-fail.rs @@ -1,4 +1,5 @@ -//@ compile-flags:--test --test-args=--test-threads=1 +//@ edition:2024 +//@ compile-flags:--test --test-args=--test-threads=1 -Z unstable-options //@ normalize-stdout-test: "tests/rustdoc-ui/doctest" -> "$$DIR" //@ normalize-stdout-test: "finished in \d+\.\d+s" -> "finished in $$TIME" //@ failure-status: 101 diff --git a/tests/rustdoc-ui/doctest/doctest-output.edition2015.stdout b/tests/rustdoc-ui/doctest/doctest-output.edition2015.stdout new file mode 100644 index 0000000000000..0e2e30390ad93 --- /dev/null +++ b/tests/rustdoc-ui/doctest/doctest-output.edition2015.stdout @@ -0,0 +1,8 @@ + +running 3 tests +test $DIR/doctest-output.rs - (line 12) ... ok +test $DIR/doctest-output.rs - ExpandedStruct (line 28) ... ok +test $DIR/doctest-output.rs - foo::bar (line 22) ... ok + +test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-ui/doctest/doctest-output.edition2024.stdout b/tests/rustdoc-ui/doctest/doctest-output.edition2024.stdout new file mode 100644 index 0000000000000..0e2e30390ad93 --- /dev/null +++ b/tests/rustdoc-ui/doctest/doctest-output.edition2024.stdout @@ -0,0 +1,8 @@ + +running 3 tests +test $DIR/doctest-output.rs - (line 12) ... ok +test $DIR/doctest-output.rs - ExpandedStruct (line 28) ... ok +test $DIR/doctest-output.rs - foo::bar (line 22) ... ok + +test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-ui/doctest/doctest-output.rs b/tests/rustdoc-ui/doctest/doctest-output.rs index 72394a4bed311..0e5ccf0b090bb 100644 --- a/tests/rustdoc-ui/doctest/doctest-output.rs +++ b/tests/rustdoc-ui/doctest/doctest-output.rs @@ -1,6 +1,10 @@ -//@ edition:2018 -//@ aux-build:extern_macros.rs -//@ compile-flags:--test --test-args=--test-threads=1 +//@ revisions: edition2015 edition2024 +//@[edition2015]edition:2015 +//@[edition2015]aux-build:extern_macros.rs +//@[edition2015]compile-flags:--test --test-args=--test-threads=1 +//@[edition2024]edition:2015 +//@[edition2024]aux-build:extern_macros.rs +//@[edition2024]compile-flags:--test --test-args=--test-threads=1 -Z unstable-options //@ normalize-stdout-test: "tests/rustdoc-ui/doctest" -> "$$DIR" //@ normalize-stdout-test: "finished in \d+\.\d+s" -> "finished in $$TIME" //@ check-pass diff --git a/tests/rustdoc-ui/doctest/doctest-output.stdout b/tests/rustdoc-ui/doctest/doctest-output.stdout deleted file mode 100644 index c3b1570c43ec1..0000000000000 --- a/tests/rustdoc-ui/doctest/doctest-output.stdout +++ /dev/null @@ -1,8 +0,0 @@ - -running 3 tests -test $DIR/doctest-output.rs - (line 8) ... ok -test $DIR/doctest-output.rs - ExpandedStruct (line 25) ... ok -test $DIR/doctest-output.rs - foo::bar (line 18) ... ok - -test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME - diff --git a/tests/rustdoc-ui/doctest/relative-path-include-bytes-132203.edition2015.stdout b/tests/rustdoc-ui/doctest/relative-path-include-bytes-132203.edition2015.stdout new file mode 100644 index 0000000000000..535d681750190 --- /dev/null +++ b/tests/rustdoc-ui/doctest/relative-path-include-bytes-132203.edition2015.stdout @@ -0,0 +1,24 @@ + +running 1 test +test $DIR/relative-path-include-bytes-132203.rs - (line 17) ... FAILED + +failures: + +---- $DIR/relative-path-include-bytes-132203.rs - (line 17) stdout ---- +error: couldn't read `$DIR/relative-dir-empty-file`: No such file or directory (os error 2) + --> $DIR/relative-path-include-bytes-132203.rs:18:9 + | +LL | let x = include_bytes!("relative-dir-empty-file"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `include_bytes` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error + +Couldn't compile the test. + +failures: + $DIR/relative-path-include-bytes-132203.rs - (line 17) + +test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-ui/doctest/relative-path-include-bytes-132203.edition2024.stdout b/tests/rustdoc-ui/doctest/relative-path-include-bytes-132203.edition2024.stdout new file mode 100644 index 0000000000000..e4c657030819f --- /dev/null +++ b/tests/rustdoc-ui/doctest/relative-path-include-bytes-132203.edition2024.stdout @@ -0,0 +1,6 @@ + +running 1 test +test $DIR/auxiliary/relative-dir.md - (line 1) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-ui/doctest/relative-path-include-bytes-132203.rs b/tests/rustdoc-ui/doctest/relative-path-include-bytes-132203.rs new file mode 100644 index 0000000000000..6ec41a3c05283 --- /dev/null +++ b/tests/rustdoc-ui/doctest/relative-path-include-bytes-132203.rs @@ -0,0 +1,17 @@ +//@ revisions: edition2015 edition2024 +//@[edition2015]edition:2015 +//@[edition2015]check-fail +//@[edition2015]failure-status: 101 +//@[edition2015]compile-flags:--test --test-args=--test-threads=1 +//@[edition2024]edition:2024 +//@[edition2024]check-pass +//@[edition2024]compile-flags:--test --test-args=--test-threads=1 -Z unstable-options +//@ normalize-stdout-test: "tests/rustdoc-ui/doctest" -> "$$DIR" +//@ normalize-stdout-test: "finished in \d+\.\d+s" -> "finished in $$TIME" + +// https://github.com/rust-lang/rust/issues/132203 +// This version, because it's edition2024, passes thanks to the new +// relative path. The edition2015 version fails, because paths are +// resolved relative to the rs file instead of relative to the md file. + +#![doc=include_str!("auxiliary/relative-dir.md")] From 5cf6b8bdee5d93f3e26a4a682cf517f8c6482120 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Mon, 28 Oct 2024 18:22:33 +0300 Subject: [PATCH 2/7] force-recompile library changes on download-rustc="if-unchanged" This makes download-rustc="if-unchanged" more functional and useful for library developers. Signed-off-by: onur-ozkan --- src/bootstrap/src/core/build_steps/compile.rs | 1 - src/bootstrap/src/core/config/config.rs | 37 +++++++++++-------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index e13d4ccc61824..99ddf58104ce8 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -153,7 +153,6 @@ impl Step for Std { // NOTE: the beta compiler may generate different artifacts than the downloaded compiler, so // its artifacts can't be reused. && compiler.stage != 0 - // This check is specific to testing std itself; see `test::Std` for more details. && !self.force_recompile { let sysroot = builder.ensure(Sysroot { compiler, force_recompile: false }); diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 139ca7eb52e7b..8115aea033d93 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -2767,25 +2767,32 @@ impl Config { } }; - let files_to_track = - &["compiler", "library", "src/version", "src/stage0", "src/ci/channel"]; + let mut files_to_track = vec!["compiler", "src/version", "src/stage0", "src/ci/channel"]; + + // In CI, disable ci-rustc if there are changes in the library tree. But for non-CI, ignore + // these changes to speed up the build process for library developers. This provides consistent + // functionality for library developers between `download-rustc=true` and `download-rustc="if-unchanged"` + // options. + if CiEnv::is_ci() { + files_to_track.push("library"); + } // Look for a version to compare to based on the current commit. // Only commits merged by bors will have CI artifacts. - let commit = match self.last_modified_commit(files_to_track, "download-rustc", if_unchanged) - { - Some(commit) => commit, - None => { - if if_unchanged { - return None; + let commit = + match self.last_modified_commit(&files_to_track, "download-rustc", if_unchanged) { + Some(commit) => commit, + None => { + if if_unchanged { + return None; + } + println!("ERROR: could not find commit hash for downloading rustc"); + println!("HELP: maybe your repository history is too shallow?"); + println!("HELP: consider disabling `download-rustc`"); + println!("HELP: or fetch enough history to include one upstream commit"); + crate::exit!(1); } - println!("ERROR: could not find commit hash for downloading rustc"); - println!("HELP: maybe your repository history is too shallow?"); - println!("HELP: consider disabling `download-rustc`"); - println!("HELP: or fetch enough history to include one upstream commit"); - crate::exit!(1); - } - }; + }; if CiEnv::is_ci() && { let head_sha = From 7e064e791d747ef6509c7a5cfe9823104e51f30f Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Mon, 28 Oct 2024 18:59:37 +0300 Subject: [PATCH 3/7] update download-rustc doc in config.example.toml Signed-off-by: onur-ozkan --- config.example.toml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/config.example.toml b/config.example.toml index d5b904ebf3d59..9072a83551a7e 100644 --- a/config.example.toml +++ b/config.example.toml @@ -497,11 +497,12 @@ #debug = false # Whether to download the stage 1 and 2 compilers from CI. -# This is mostly useful for tools; if you have changes to `compiler/` or `library/` they will be ignored. +# This is useful if you are working on tools, doc-comments, or library (you will be able to build +# the standard library without needing to build the compiler). # -# Set this to "if-unchanged" to only download if the compiler and standard library have not been modified. -# Set this to `true` to download unconditionally. This is useful if you are working on tools, doc-comments, -# or library (you will be able to build the standard library without needing to build the compiler). +# Set this to "if-unchanged" to only download if the compiler (and library if running on CI) have +# not been modified. +# Set this to `true` to download unconditionally. #download-rustc = false # Number of codegen units to use for each compiler invocation. A value of 0 From 802f3a78a6eea323617964a168a612d1b0826bd6 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 29 Oct 2024 23:42:59 +0000 Subject: [PATCH 4/7] Merge HostPolarity and BoundConstness --- compiler/rustc_hir_analysis/src/bounds.rs | 4 +- .../src/check/compare_impl_item.rs | 10 ++-- .../rustc_hir_analysis/src/check/wfcheck.rs | 2 +- .../src/collect/predicates_of.rs | 12 ++-- .../src/hir_ty_lowering/mod.rs | 9 ++- compiler/rustc_hir_typeck/src/callee.rs | 4 +- compiler/rustc_middle/src/ty/context.rs | 6 +- compiler/rustc_middle/src/ty/flags.rs | 2 +- compiler/rustc_middle/src/ty/print/pretty.rs | 8 +-- .../src/solve/effect_goals.rs | 11 ++-- compiler/rustc_privacy/src/lib.rs | 2 +- .../traits/fulfillment_errors.rs | 13 ++--- .../src/traits/effects.rs | 5 +- compiler/rustc_ty_utils/src/ty.rs | 2 +- compiler/rustc_type_ir/src/elaborate.rs | 2 +- compiler/rustc_type_ir/src/predicate.rs | 55 ++++++------------- 16 files changed, 66 insertions(+), 81 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/bounds.rs b/compiler/rustc_hir_analysis/src/bounds.rs index 09ddc6ca9de1d..9b02651a8bdf5 100644 --- a/compiler/rustc_hir_analysis/src/bounds.rs +++ b/compiler/rustc_hir_analysis/src/bounds.rs @@ -84,11 +84,11 @@ impl<'tcx> Bounds<'tcx> { &mut self, tcx: TyCtxt<'tcx>, bound_trait_ref: ty::PolyTraitRef<'tcx>, - host: ty::HostPolarity, + constness: ty::BoundConstness, span: Span, ) { if tcx.is_const_trait(bound_trait_ref.def_id()) { - self.clauses.push((bound_trait_ref.to_host_effect_clause(tcx, host), span)); + self.clauses.push((bound_trait_ref.to_host_effect_clause(tcx, constness), span)); } else { tcx.dcx().span_delayed_bug(span, "tried to lower {host:?} bound for non-const trait"); } diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index db2c44fd29df4..796999c65243d 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -218,7 +218,7 @@ fn compare_method_predicate_entailment<'tcx>( tcx.const_conditions(trait_m.def_id).instantiate_own(tcx, trait_to_impl_args), ) .map(|(trait_ref, _)| { - trait_ref.to_host_effect_clause(tcx, ty::HostPolarity::Maybe) + trait_ref.to_host_effect_clause(tcx, ty::BoundConstness::Maybe) }), ); } @@ -272,7 +272,7 @@ fn compare_method_predicate_entailment<'tcx>( tcx, cause, param_env, - const_condition.to_host_effect_clause(tcx, ty::HostPolarity::Maybe), + const_condition.to_host_effect_clause(tcx, ty::BoundConstness::Maybe), )); } } @@ -1942,7 +1942,7 @@ fn compare_type_predicate_entailment<'tcx>( tcx.const_conditions(trait_ty.def_id).instantiate_own(tcx, trait_to_impl_args), ) .map(|(trait_ref, _)| { - trait_ref.to_host_effect_clause(tcx, ty::HostPolarity::Maybe) + trait_ref.to_host_effect_clause(tcx, ty::BoundConstness::Maybe) }), ); } @@ -1985,7 +1985,7 @@ fn compare_type_predicate_entailment<'tcx>( tcx, cause, param_env, - const_condition.to_host_effect_clause(tcx, ty::HostPolarity::Maybe), + const_condition.to_host_effect_clause(tcx, ty::BoundConstness::Maybe), )); } } @@ -2091,7 +2091,7 @@ pub(super) fn check_type_bounds<'tcx>( tcx, mk_cause(span), param_env, - c.to_host_effect_clause(tcx, ty::HostPolarity::Maybe), + c.to_host_effect_clause(tcx, ty::BoundConstness::Maybe), ) }), ); diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 12ed7b89f68be..b9e18d784ce94 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1389,7 +1389,7 @@ fn check_impl<'tcx>( ObligationCauseCode::WellFormed(None), ), wfcx.param_env, - bound.to_host_effect_clause(tcx, ty::HostPolarity::Maybe), + bound.to_host_effect_clause(tcx, ty::BoundConstness::Maybe), )) } } diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index 644ff0c667c6b..2a0751ba09100 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -716,7 +716,7 @@ pub(super) fn assert_only_contains_predicates_from<'tcx>( match clause.kind().skip_binder() { ty::ClauseKind::HostEffect(ty::HostEffectPredicate { trait_ref: _, - host: ty::HostPolarity::Maybe, + constness: ty::BoundConstness::Maybe, }) => {} _ => { bug!( @@ -732,8 +732,8 @@ pub(super) fn assert_only_contains_predicates_from<'tcx>( match clause.kind().skip_binder() { ty::ClauseKind::HostEffect(pred) => { assert_eq!( - pred.host, - ty::HostPolarity::Maybe, + pred.constness, + ty::BoundConstness::Maybe, "expected `~const` predicate when computing `{filter:?}` \ implied bounds: {clause:?}", ); @@ -943,7 +943,7 @@ pub(super) fn const_conditions<'tcx>( bounds.push_const_bound( tcx, ty::Binder::dummy(ty::TraitRef::identity(tcx, def_id.to_def_id())), - ty::HostPolarity::Maybe, + ty::BoundConstness::Maybe, DUMMY_SP, ); @@ -963,7 +963,7 @@ pub(super) fn const_conditions<'tcx>( clause.kind().map_bound(|clause| match clause { ty::ClauseKind::HostEffect(ty::HostEffectPredicate { trait_ref, - host: ty::HostPolarity::Maybe, + constness: ty::BoundConstness::Maybe, }) => trait_ref, _ => bug!("converted {clause:?}"), }), @@ -1001,7 +1001,7 @@ pub(super) fn implied_const_bounds<'tcx>( clause.kind().map_bound(|clause| match clause { ty::ClauseKind::HostEffect(ty::HostEffectPredicate { trait_ref, - host: ty::HostPolarity::Maybe, + constness: ty::BoundConstness::Maybe, }) => trait_ref, _ => bug!("converted {clause:?}"), }), diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 2d0c3ec28c372..74bd0174cf567 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -713,7 +713,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { bounds.push_const_bound( tcx, poly_trait_ref, - ty::HostPolarity::Const, + ty::BoundConstness::Const, span, ); } @@ -736,7 +736,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst => match constness { hir::BoundConstness::Maybe(span) => { if polarity == ty::PredicatePolarity::Positive { - bounds.push_const_bound(tcx, poly_trait_ref, ty::HostPolarity::Maybe, span); + bounds.push_const_bound( + tcx, + poly_trait_ref, + ty::BoundConstness::Maybe, + span, + ); } } hir::BoundConstness::Always(_) | hir::BoundConstness::Never => {} diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 9cf1ea3fcb881..93d50bf3d7b3b 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -853,9 +853,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let host = match self.tcx.hir().body_const_context(self.body_id) { Some(hir::ConstContext::Const { .. } | hir::ConstContext::Static(_)) => { - ty::HostPolarity::Const + ty::BoundConstness::Const } - Some(hir::ConstContext::ConstFn) => ty::HostPolarity::Maybe, + Some(hir::ConstContext::ConstFn) => ty::BoundConstness::Maybe, None => return, }; diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 5cbbc80ebfbb6..a3c0908ce5b24 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -76,8 +76,8 @@ use crate::traits::solve::{ }; use crate::ty::predicate::ExistentialPredicateStableCmpExt as _; use crate::ty::{ - self, AdtDef, AdtDefData, AdtKind, Binder, Clause, Clauses, Const, GenericArg, GenericArgs, - GenericArgsRef, GenericParamDefKind, HostPolarity, ImplPolarity, List, ListWithCachedTypeInfo, + self, AdtDef, AdtDefData, AdtKind, Binder, BoundConstness, Clause, Clauses, Const, GenericArg, + GenericArgs, GenericArgsRef, GenericParamDefKind, ImplPolarity, List, ListWithCachedTypeInfo, ParamConst, ParamTy, Pattern, PatternKind, PolyExistentialPredicate, PolyFnSig, Predicate, PredicateKind, PredicatePolarity, Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyVid, Visibility, @@ -2205,7 +2205,7 @@ macro_rules! nop_slice_lift { nop_slice_lift! {ty::ValTree<'a> => ty::ValTree<'tcx>} TrivialLiftImpls! { - ImplPolarity, PredicatePolarity, Promoted, HostPolarity, + ImplPolarity, PredicatePolarity, Promoted, BoundConstness, } macro_rules! sty_debug_print { diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs index 704a197aa49d0..04d03187541ce 100644 --- a/compiler/rustc_middle/src/ty/flags.rs +++ b/compiler/rustc_middle/src/ty/flags.rs @@ -267,7 +267,7 @@ impl FlagComputation { } ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(ty::HostEffectPredicate { trait_ref, - host: _, + constness: _, })) => { self.add_args(trait_ref.args); } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 0248aad53e248..2480cee3dc4b6 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1959,7 +1959,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { ty::BoundConstness::Const => { p!("const "); } - ty::BoundConstness::ConstIfConst => { + ty::BoundConstness::Maybe => { p!("~const "); } } @@ -3076,9 +3076,9 @@ define_print! { } ty::HostEffectPredicate<'tcx> { - let constness = match self.host { - ty::HostPolarity::Const => { "const" } - ty::HostPolarity::Maybe => { "~const" } + let constness = match self.constness { + ty::BoundConstness::Const => { "const" } + ty::BoundConstness::Maybe => { "~const" } }; p!(print(self.trait_ref.self_ty()), ": {constness} "); p!(print(self.trait_ref.print_trait_sugared())) diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index 8d57ad8f2551b..5bfc156ed94fe 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -44,7 +44,7 @@ where ) -> Result, NoSolution> { if let Some(host_clause) = assumption.as_host_effect_clause() { if host_clause.def_id() == goal.predicate.def_id() - && host_clause.host().satisfies(goal.predicate.host) + && host_clause.constness().satisfies(goal.predicate.constness) { if !DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify( goal.predicate.trait_ref.args, @@ -91,7 +91,7 @@ where cx, cx.implied_const_bounds(alias_ty.def_id) .iter_instantiated(cx, alias_ty.args) - .map(|trait_ref| trait_ref.to_host_effect_clause(cx, goal.predicate.host)), + .map(|trait_ref| trait_ref.to_host_effect_clause(cx, goal.predicate.constness)), ) { candidates.extend(Self::probe_and_match_goal_against_assumption( ecx, @@ -107,7 +107,7 @@ where .map(|trait_ref| { goal.with( cx, - trait_ref.to_host_effect_clause(cx, goal.predicate.host), + trait_ref.to_host_effect_clause(cx, goal.predicate.constness), ) }), ); @@ -163,7 +163,10 @@ where .const_conditions(impl_def_id) .iter_instantiated(cx, impl_args) .map(|bound_trait_ref| { - goal.with(cx, bound_trait_ref.to_host_effect_clause(cx, goal.predicate.host)) + goal.with( + cx, + bound_trait_ref.to_host_effect_clause(cx, goal.predicate.constness), + ) }); ecx.add_goals(GoalSource::ImplWhereBound, const_conditions); diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 05954143aee00..1a5c29afdc939 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -139,7 +139,7 @@ where } ty::ClauseKind::HostEffect(pred) => { try_visit!(self.visit_trait(pred.trait_ref)); - pred.host.visit_with(self) + pred.constness.visit_with(self) } ty::ClauseKind::Projection(ty::ProjectionPredicate { projection_term: projection_ty, 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 6014ed555b64d..612e92ea7843d 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 @@ -545,10 +545,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { polarity: ty::PredicatePolarity::Positive, }), None, - Some(match predicate.host { - ty::HostPolarity::Maybe => ty::BoundConstness::ConstIfConst, - ty::HostPolarity::Const => ty::BoundConstness::Const, - }), + Some(predicate.constness), None, String::new(), ); @@ -2238,18 +2235,16 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { (None, _) => Some(cannot_do_this), // suggested using default post message ( - Some(ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst), + Some(ty::BoundConstness::Const | ty::BoundConstness::Maybe), Some(AppendConstMessage::Default), ) => Some(format!("{cannot_do_this} in const contexts")), // overridden post message ( - Some(ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst), + Some(ty::BoundConstness::Const | ty::BoundConstness::Maybe), Some(AppendConstMessage::Custom(custom_msg, _)), ) => Some(format!("{cannot_do_this}{custom_msg}")), // fallback to generic message - (Some(ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst), None) => { - None - } + (Some(ty::BoundConstness::Const | ty::BoundConstness::Maybe), None) => None, } }) .unwrap_or_else(|| { diff --git a/compiler/rustc_trait_selection/src/traits/effects.rs b/compiler/rustc_trait_selection/src/traits/effects.rs index 60b3357810a65..cb36f1a62dbfd 100644 --- a/compiler/rustc_trait_selection/src/traits/effects.rs +++ b/compiler/rustc_trait_selection/src/traits/effects.rs @@ -47,7 +47,7 @@ fn match_candidate<'tcx>( obligation: &HostEffectObligation<'tcx>, candidate: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>, ) -> Result>, NoSolution> { - if !candidate.skip_binder().host.satisfies(obligation.predicate.host) { + if !candidate.skip_binder().constness.satisfies(obligation.predicate.constness) { return Err(NoSolution); } @@ -135,7 +135,8 @@ fn evaluate_host_effect_from_selection_candiate<'tcx>( .map(|(trait_ref, _)| { obligation.with( tcx, - trait_ref.to_host_effect_clause(tcx, obligation.predicate.host), + trait_ref + .to_host_effect_clause(tcx, obligation.predicate.constness), ) }), ); diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index aa499995bcb9d..731d42fc006ca 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -155,7 +155,7 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> { if tcx.is_conditionally_const(def_id) { predicates.extend( tcx.const_conditions(def_id).instantiate_identity(tcx).into_iter().map( - |(trait_ref, _)| trait_ref.to_host_effect_clause(tcx, ty::HostPolarity::Maybe), + |(trait_ref, _)| trait_ref.to_host_effect_clause(tcx, ty::BoundConstness::Maybe), ), ); } diff --git a/compiler/rustc_type_ir/src/elaborate.rs b/compiler/rustc_type_ir/src/elaborate.rs index 72d392ecd7bdc..3fbce7886edcb 100644 --- a/compiler/rustc_type_ir/src/elaborate.rs +++ b/compiler/rustc_type_ir/src/elaborate.rs @@ -160,7 +160,7 @@ impl> Elaborator { cx.implied_const_bounds(data.def_id()).iter_identity().map(|trait_ref| { elaboratable.child( trait_ref - .to_host_effect_clause(cx, data.host) + .to_host_effect_clause(cx, data.constness) .instantiate_supertrait(cx, bound_clause.rebind(data.trait_ref)), ) }), diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index c316455034873..0cade1d688553 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -112,9 +112,9 @@ impl ty::Binder> { self.skip_binder().def_id } - pub fn to_host_effect_clause(self, cx: I, host: HostPolarity) -> I::Clause { + pub fn to_host_effect_clause(self, cx: I, constness: BoundConstness) -> I::Clause { self.map_bound(|trait_ref| { - ty::ClauseKind::HostEffect(HostEffectPredicate { trait_ref, host }) + ty::ClauseKind::HostEffect(HostEffectPredicate { trait_ref, constness }) }) .upcast(cx) } @@ -757,7 +757,7 @@ impl fmt::Debug for NormalizesTo { #[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] pub struct HostEffectPredicate { pub trait_ref: ty::TraitRef, - pub host: HostPolarity, + pub constness: BoundConstness, } impl HostEffectPredicate { @@ -785,28 +785,8 @@ impl ty::Binder> { } #[inline] - pub fn host(self) -> HostPolarity { - self.skip_binder().host - } -} - -#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] -#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] -pub enum HostPolarity { - /// May be called in const environments if the callee is const. - Maybe, - /// Always allowed to be called in const environments. - Const, -} - -impl HostPolarity { - pub fn satisfies(self, goal: HostPolarity) -> bool { - match (self, goal) { - (HostPolarity::Const, HostPolarity::Const | HostPolarity::Maybe) => true, - (HostPolarity::Maybe, HostPolarity::Maybe) => true, - (HostPolarity::Maybe, HostPolarity::Const) => false, - } + pub fn constness(self) -> BoundConstness { + self.skip_binder().constness } } @@ -831,8 +811,8 @@ pub struct CoercePredicate { pub b: I::Ty, } -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "nightly", derive(HashStable_NoContext, TyEncodable, TyDecodable))] +#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] pub enum BoundConstness { /// `Type: const Trait` /// @@ -841,14 +821,22 @@ pub enum BoundConstness { /// `Type: ~const Trait` /// /// Requires resolving to const only when we are in a const context. - ConstIfConst, + Maybe, } impl BoundConstness { + pub fn satisfies(self, goal: BoundConstness) -> bool { + match (self, goal) { + (BoundConstness::Const, BoundConstness::Const | BoundConstness::Maybe) => true, + (BoundConstness::Maybe, BoundConstness::Maybe) => true, + (BoundConstness::Maybe, BoundConstness::Const) => false, + } + } + pub fn as_str(self) -> &'static str { match self { Self::Const => "const", - Self::ConstIfConst => "~const", + Self::Maybe => "~const", } } } @@ -857,14 +845,7 @@ impl fmt::Display for BoundConstness { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Const => f.write_str("const"), - Self::ConstIfConst => f.write_str("~const"), + Self::Maybe => f.write_str("~const"), } } } - -impl Lift for BoundConstness { - type Lifted = BoundConstness; - fn lift_to_interner(self, _: I) -> Option { - Some(self) - } -} From 08d769b30675a6cfb2998752f73d2f146a3bfb8d Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 30 Oct 2024 14:47:25 +1100 Subject: [PATCH 5/7] Remove `ValueAnalysis` and `ValueAnalysisWrapper`. They represent a lot of abstraction and indirection, but they're only used for `ConstAnalysis`, and apparently won't be used for any other analyses in the future. This commit inlines and removes them, which makes `ConstAnalysis` easier to read and understand. --- .../rustc_mir_dataflow/src/value_analysis.rs | 416 +----------------- .../src/dataflow_const_prop.rs | 333 +++++++++++--- 2 files changed, 278 insertions(+), 471 deletions(-) diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index f7d4a082779b7..26bfbe0e5158d 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -1,38 +1,3 @@ -//! This module provides a framework on top of the normal MIR dataflow framework to simplify the -//! implementation of analyses that track information about the values stored in certain places. -//! We are using the term "place" here to refer to a `mir::Place` (a place expression) instead of -//! an `interpret::Place` (a memory location). -//! -//! The default methods of [`ValueAnalysis`] (prefixed with `super_` instead of `handle_`) -//! provide some behavior that should be valid for all abstract domains that are based only on the -//! value stored in a certain place. On top of these default rules, an implementation should -//! override some of the `handle_` methods. For an example, see `ConstAnalysis`. -//! -//! An implementation must also provide a [`Map`]. Before the analysis begins, all places that -//! should be tracked during the analysis must be registered. During the analysis, no new places -//! can be registered. The [`State`] can be queried to retrieve the abstract value stored for a -//! certain place by passing the map. -//! -//! This framework is currently experimental. Originally, it supported shared references and enum -//! variants. However, it was discovered that both of these were unsound, and especially references -//! had subtle but serious issues. In the future, they could be added back in, but we should clarify -//! the rules for optimizations that rely on the aliasing model first. -//! -//! -//! # Notes -//! -//! - The bottom state denotes uninitialized memory. Because we are only doing a sound approximation -//! of the actual execution, we can also use this state for places where access would be UB. -//! -//! - The assignment logic in `State::insert_place_idx` assumes that the places are non-overlapping, -//! or identical. Note that this refers to place expressions, not memory locations. -//! -//! - Currently, places that have their reference taken cannot be tracked. Although this would be -//! possible, it has to rely on some aliasing model, which we are not ready to commit to yet. -//! Because of that, we can assume that the only way to change the value behind a tracked place is -//! by direct assignment. - -use std::assert_matches::assert_matches; use std::fmt::{Debug, Formatter}; use std::ops::Range; @@ -42,359 +7,14 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexSet, StdEntry}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_index::IndexVec; use rustc_index::bit_set::BitSet; -use rustc_middle::bug; use rustc_middle::mir::tcx::PlaceTy; use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::{self, Ty, TyCtxt}; use tracing::debug; -use crate::fmt::DebugWithContext; +use crate::JoinSemiLattice; use crate::lattice::{HasBottom, HasTop}; -use crate::{Analysis, JoinSemiLattice, SwitchIntEdgeEffects}; - -pub trait ValueAnalysis<'tcx> { - /// For each place of interest, the analysis tracks a value of the given type. - type Value: Clone + JoinSemiLattice + HasBottom + HasTop + Debug; - - const NAME: &'static str; - - fn map(&self) -> &Map<'tcx>; - - fn handle_statement(&self, statement: &Statement<'tcx>, state: &mut State) { - self.super_statement(statement, state) - } - - fn super_statement(&self, statement: &Statement<'tcx>, state: &mut State) { - match &statement.kind { - StatementKind::Assign(box (place, rvalue)) => { - self.handle_assign(*place, rvalue, state); - } - StatementKind::SetDiscriminant { box place, variant_index } => { - self.handle_set_discriminant(*place, *variant_index, state); - } - StatementKind::Intrinsic(box intrinsic) => { - self.handle_intrinsic(intrinsic, state); - } - StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => { - // StorageLive leaves the local in an uninitialized state. - // StorageDead makes it UB to access the local afterwards. - state.flood_with(Place::from(*local).as_ref(), self.map(), Self::Value::BOTTOM); - } - StatementKind::Deinit(box place) => { - // Deinit makes the place uninitialized. - state.flood_with(place.as_ref(), self.map(), Self::Value::BOTTOM); - } - StatementKind::Retag(..) => { - // We don't track references. - } - StatementKind::ConstEvalCounter - | StatementKind::Nop - | StatementKind::FakeRead(..) - | StatementKind::PlaceMention(..) - | StatementKind::Coverage(..) - | StatementKind::AscribeUserType(..) => (), - } - } - - fn handle_set_discriminant( - &self, - place: Place<'tcx>, - variant_index: VariantIdx, - state: &mut State, - ) { - self.super_set_discriminant(place, variant_index, state) - } - - fn super_set_discriminant( - &self, - place: Place<'tcx>, - _variant_index: VariantIdx, - state: &mut State, - ) { - state.flood_discr(place.as_ref(), self.map()); - } - - fn handle_intrinsic( - &self, - intrinsic: &NonDivergingIntrinsic<'tcx>, - state: &mut State, - ) { - self.super_intrinsic(intrinsic, state); - } - - fn super_intrinsic( - &self, - intrinsic: &NonDivergingIntrinsic<'tcx>, - _state: &mut State, - ) { - match intrinsic { - NonDivergingIntrinsic::Assume(..) => { - // Could use this, but ignoring it is sound. - } - NonDivergingIntrinsic::CopyNonOverlapping(CopyNonOverlapping { - dst: _, - src: _, - count: _, - }) => { - // This statement represents `*dst = *src`, `count` times. - } - } - } - - fn handle_assign( - &self, - target: Place<'tcx>, - rvalue: &Rvalue<'tcx>, - state: &mut State, - ) { - self.super_assign(target, rvalue, state) - } - - fn super_assign( - &self, - target: Place<'tcx>, - rvalue: &Rvalue<'tcx>, - state: &mut State, - ) { - let result = self.handle_rvalue(rvalue, state); - state.assign(target.as_ref(), result, self.map()); - } - - fn handle_rvalue( - &self, - rvalue: &Rvalue<'tcx>, - state: &mut State, - ) -> ValueOrPlace { - self.super_rvalue(rvalue, state) - } - - fn super_rvalue( - &self, - rvalue: &Rvalue<'tcx>, - state: &mut State, - ) -> ValueOrPlace { - match rvalue { - Rvalue::Use(operand) => self.handle_operand(operand, state), - Rvalue::CopyForDeref(place) => self.handle_operand(&Operand::Copy(*place), state), - Rvalue::Ref(..) | Rvalue::RawPtr(..) => { - // We don't track such places. - ValueOrPlace::TOP - } - Rvalue::Repeat(..) - | Rvalue::ThreadLocalRef(..) - | Rvalue::Len(..) - | Rvalue::Cast(..) - | Rvalue::BinaryOp(..) - | Rvalue::NullaryOp(..) - | Rvalue::UnaryOp(..) - | Rvalue::Discriminant(..) - | Rvalue::Aggregate(..) - | Rvalue::ShallowInitBox(..) => { - // No modification is possible through these r-values. - ValueOrPlace::TOP - } - } - } - - fn handle_operand( - &self, - operand: &Operand<'tcx>, - state: &mut State, - ) -> ValueOrPlace { - self.super_operand(operand, state) - } - - fn super_operand( - &self, - operand: &Operand<'tcx>, - state: &mut State, - ) -> ValueOrPlace { - match operand { - Operand::Constant(box constant) => { - ValueOrPlace::Value(self.handle_constant(constant, state)) - } - Operand::Copy(place) | Operand::Move(place) => { - // On move, we would ideally flood the place with bottom. But with the current - // framework this is not possible (similar to `InterpCx::eval_operand`). - self.map() - .find(place.as_ref()) - .map(ValueOrPlace::Place) - .unwrap_or(ValueOrPlace::TOP) - } - } - } - - fn handle_constant( - &self, - constant: &ConstOperand<'tcx>, - state: &mut State, - ) -> Self::Value { - self.super_constant(constant, state) - } - - fn super_constant( - &self, - _constant: &ConstOperand<'tcx>, - _state: &mut State, - ) -> Self::Value { - Self::Value::TOP - } - - /// The effect of a successful function call return should not be - /// applied here, see [`Analysis::apply_terminator_effect`]. - fn handle_terminator<'mir>( - &self, - terminator: &'mir Terminator<'tcx>, - state: &mut State, - ) -> TerminatorEdges<'mir, 'tcx> { - self.super_terminator(terminator, state) - } - - fn super_terminator<'mir>( - &self, - terminator: &'mir Terminator<'tcx>, - state: &mut State, - ) -> TerminatorEdges<'mir, 'tcx> { - match &terminator.kind { - TerminatorKind::Call { .. } | TerminatorKind::InlineAsm { .. } => { - // Effect is applied by `handle_call_return`. - } - TerminatorKind::Drop { place, .. } => { - state.flood_with(place.as_ref(), self.map(), Self::Value::BOTTOM); - } - TerminatorKind::Yield { .. } => { - // They would have an effect, but are not allowed in this phase. - bug!("encountered disallowed terminator"); - } - TerminatorKind::SwitchInt { discr, targets } => { - return self.handle_switch_int(discr, targets, state); - } - TerminatorKind::TailCall { .. } => { - // FIXME(explicit_tail_calls): determine if we need to do something here (probably not) - } - TerminatorKind::Goto { .. } - | TerminatorKind::UnwindResume - | TerminatorKind::UnwindTerminate(_) - | TerminatorKind::Return - | TerminatorKind::Unreachable - | TerminatorKind::Assert { .. } - | TerminatorKind::CoroutineDrop - | TerminatorKind::FalseEdge { .. } - | TerminatorKind::FalseUnwind { .. } => { - // These terminators have no effect on the analysis. - } - } - terminator.edges() - } - - fn handle_call_return( - &self, - return_places: CallReturnPlaces<'_, 'tcx>, - state: &mut State, - ) { - self.super_call_return(return_places, state) - } - - fn super_call_return( - &self, - return_places: CallReturnPlaces<'_, 'tcx>, - state: &mut State, - ) { - return_places.for_each(|place| { - state.flood(place.as_ref(), self.map()); - }) - } - - fn handle_switch_int<'mir>( - &self, - discr: &'mir Operand<'tcx>, - targets: &'mir SwitchTargets, - state: &mut State, - ) -> TerminatorEdges<'mir, 'tcx> { - self.super_switch_int(discr, targets, state) - } - - fn super_switch_int<'mir>( - &self, - discr: &'mir Operand<'tcx>, - targets: &'mir SwitchTargets, - _state: &mut State, - ) -> TerminatorEdges<'mir, 'tcx> { - TerminatorEdges::SwitchInt { discr, targets } - } - - fn wrap(self) -> ValueAnalysisWrapper - where - Self: Sized, - { - ValueAnalysisWrapper(self) - } -} - -pub struct ValueAnalysisWrapper(pub T); - -impl<'tcx, T: ValueAnalysis<'tcx>> Analysis<'tcx> for ValueAnalysisWrapper { - type Domain = State; - - const NAME: &'static str = T::NAME; - - fn bottom_value(&self, _body: &Body<'tcx>) -> Self::Domain { - State::Unreachable - } - - fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) { - // The initial state maps all tracked places of argument projections to ⊤ and the rest to ⊥. - assert_matches!(state, State::Unreachable); - *state = State::new_reachable(); - for arg in body.args_iter() { - state.flood(PlaceRef { local: arg, projection: &[] }, self.0.map()); - } - } - - fn apply_statement_effect( - &mut self, - state: &mut Self::Domain, - statement: &Statement<'tcx>, - _location: Location, - ) { - if state.is_reachable() { - self.0.handle_statement(statement, state); - } - } - - fn apply_terminator_effect<'mir>( - &mut self, - state: &mut Self::Domain, - terminator: &'mir Terminator<'tcx>, - _location: Location, - ) -> TerminatorEdges<'mir, 'tcx> { - if state.is_reachable() { - self.0.handle_terminator(terminator, state) - } else { - TerminatorEdges::None - } - } - - fn apply_call_return_effect( - &mut self, - state: &mut Self::Domain, - _block: BasicBlock, - return_places: CallReturnPlaces<'_, 'tcx>, - ) { - if state.is_reachable() { - self.0.handle_call_return(return_places, state) - } - } - - fn apply_switch_int_edge_effects( - &mut self, - _block: BasicBlock, - _discr: &Operand<'tcx>, - _apply_edge_effects: &mut impl SwitchIntEdgeEffects, - ) { - } -} rustc_index::newtype_index!( /// This index uniquely identifies a place. @@ -464,7 +84,7 @@ impl JoinSemiLattice for StateData { } } -/// The dataflow state for an instance of [`ValueAnalysis`]. +/// Dataflow state. /// /// Every instance specifies a lattice that represents the possible values of a single tracked /// place. If we call this lattice `V` and set of tracked places `P`, then a [`State`] is an @@ -514,7 +134,7 @@ impl State { } } - fn is_reachable(&self) -> bool { + pub fn is_reachable(&self) -> bool { matches!(self, State::Reachable(_)) } @@ -1317,34 +937,6 @@ pub fn excluded_locals(body: &Body<'_>) -> BitSet { collector.result } -/// This is used to visualize the dataflow analysis. -impl<'tcx, T> DebugWithContext> for State -where - T: ValueAnalysis<'tcx>, - T::Value: Debug, -{ - fn fmt_with(&self, ctxt: &ValueAnalysisWrapper, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - State::Reachable(values) => debug_with_context(values, None, ctxt.0.map(), f), - State::Unreachable => write!(f, "unreachable"), - } - } - - fn fmt_diff_with( - &self, - old: &Self, - ctxt: &ValueAnalysisWrapper, - f: &mut Formatter<'_>, - ) -> std::fmt::Result { - match (self, old) { - (State::Reachable(this), State::Reachable(old)) => { - debug_with_context(this, Some(old), ctxt.0.map(), f) - } - _ => Ok(()), // Consider printing something here. - } - } -} - fn debug_with_context_rec( place: PlaceIndex, place_str: &str, @@ -1391,7 +983,7 @@ fn debug_with_context_rec( Ok(()) } -fn debug_with_context( +pub fn debug_with_context( new: &StateData, old: Option<&StateData>, map: &Map<'_>, diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 07263460733cf..b07d01eeaede1 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -2,6 +2,9 @@ //! //! Currently, this pass only propagates scalar values. +use std::assert_matches::assert_matches; +use std::fmt::Formatter; + use rustc_const_eval::const_eval::{DummyMachine, throw_machine_stop_str}; use rustc_const_eval::interpret::{ ImmTy, Immediate, InterpCx, OpTy, PlaceTy, Projectable, interp_ok, @@ -14,9 +17,10 @@ use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::layout::{HasParamEnv, LayoutOf}; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_mir_dataflow::lattice::FlatSet; +use rustc_mir_dataflow::fmt::DebugWithContext; +use rustc_mir_dataflow::lattice::{FlatSet, HasBottom}; use rustc_mir_dataflow::value_analysis::{ - Map, PlaceIndex, State, TrackElem, ValueAnalysis, ValueAnalysisWrapper, ValueOrPlace, + Map, PlaceIndex, State, TrackElem, ValueOrPlace, debug_with_context, }; use rustc_mir_dataflow::{Analysis, Results, ResultsVisitor}; use rustc_span::DUMMY_SP; @@ -58,8 +62,8 @@ impl<'tcx> crate::MirPass<'tcx> for DataflowConstProp { // Perform the actual dataflow analysis. let analysis = ConstAnalysis::new(tcx, body, map); - let mut results = debug_span!("analyze") - .in_scope(|| analysis.wrap().iterate_to_fixpoint(tcx, body, None)); + let mut results = + debug_span!("analyze").in_scope(|| analysis.iterate_to_fixpoint(tcx, body, None)); // Collect results and patch the body afterwards. let mut visitor = Collector::new(tcx, &body.local_decls); @@ -69,6 +73,10 @@ impl<'tcx> crate::MirPass<'tcx> for DataflowConstProp { } } +// Note: Currently, places that have their reference taken cannot be tracked. Although this would +// be possible, it has to rely on some aliasing model, which we are not ready to commit to yet. +// Because of that, we can assume that the only way to change the value behind a tracked place is +// by direct assignment. struct ConstAnalysis<'a, 'tcx> { map: Map<'tcx>, tcx: TyCtxt<'tcx>, @@ -77,20 +85,198 @@ struct ConstAnalysis<'a, 'tcx> { param_env: ty::ParamEnv<'tcx>, } -impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { - type Value = FlatSet; +impl<'tcx> Analysis<'tcx> for ConstAnalysis<'_, 'tcx> { + type Domain = State>; const NAME: &'static str = "ConstAnalysis"; - fn map(&self) -> &Map<'tcx> { - &self.map + // The bottom state denotes uninitialized memory. Because we are only doing a sound + // approximation of the actual execution, we can also use this state for places where access + // would be UB. + fn bottom_value(&self, _body: &Body<'tcx>) -> Self::Domain { + State::Unreachable + } + + fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) { + // The initial state maps all tracked places of argument projections to ⊤ and the rest to ⊥. + assert_matches!(state, State::Unreachable); + *state = State::new_reachable(); + for arg in body.args_iter() { + state.flood(PlaceRef { local: arg, projection: &[] }, &self.map); + } + } + + fn apply_statement_effect( + &mut self, + state: &mut Self::Domain, + statement: &Statement<'tcx>, + _location: Location, + ) { + if state.is_reachable() { + self.handle_statement(statement, state); + } + } + + fn apply_terminator_effect<'mir>( + &mut self, + state: &mut Self::Domain, + terminator: &'mir Terminator<'tcx>, + _location: Location, + ) -> TerminatorEdges<'mir, 'tcx> { + if state.is_reachable() { + self.handle_terminator(terminator, state) + } else { + TerminatorEdges::None + } + } + + fn apply_call_return_effect( + &mut self, + state: &mut Self::Domain, + _block: BasicBlock, + return_places: CallReturnPlaces<'_, 'tcx>, + ) { + if state.is_reachable() { + self.handle_call_return(return_places, state) + } + } +} + +impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { + fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, map: Map<'tcx>) -> Self { + let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id()); + Self { + map, + tcx, + local_decls: &body.local_decls, + ecx: InterpCx::new(tcx, DUMMY_SP, param_env, DummyMachine), + param_env, + } + } + + fn handle_statement(&self, statement: &Statement<'tcx>, state: &mut State>) { + match &statement.kind { + StatementKind::Assign(box (place, rvalue)) => { + self.handle_assign(*place, rvalue, state); + } + StatementKind::SetDiscriminant { box place, variant_index } => { + self.handle_set_discriminant(*place, *variant_index, state); + } + StatementKind::Intrinsic(box intrinsic) => { + self.handle_intrinsic(intrinsic); + } + StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => { + // StorageLive leaves the local in an uninitialized state. + // StorageDead makes it UB to access the local afterwards. + state.flood_with( + Place::from(*local).as_ref(), + &self.map, + FlatSet::::BOTTOM, + ); + } + StatementKind::Deinit(box place) => { + // Deinit makes the place uninitialized. + state.flood_with(place.as_ref(), &self.map, FlatSet::::BOTTOM); + } + StatementKind::Retag(..) => { + // We don't track references. + } + StatementKind::ConstEvalCounter + | StatementKind::Nop + | StatementKind::FakeRead(..) + | StatementKind::PlaceMention(..) + | StatementKind::Coverage(..) + | StatementKind::AscribeUserType(..) => (), + } + } + + fn handle_intrinsic(&self, intrinsic: &NonDivergingIntrinsic<'tcx>) { + match intrinsic { + NonDivergingIntrinsic::Assume(..) => { + // Could use this, but ignoring it is sound. + } + NonDivergingIntrinsic::CopyNonOverlapping(CopyNonOverlapping { + dst: _, + src: _, + count: _, + }) => { + // This statement represents `*dst = *src`, `count` times. + } + } + } + + fn handle_operand( + &self, + operand: &Operand<'tcx>, + state: &mut State>, + ) -> ValueOrPlace> { + match operand { + Operand::Constant(box constant) => { + ValueOrPlace::Value(self.handle_constant(constant, state)) + } + Operand::Copy(place) | Operand::Move(place) => { + // On move, we would ideally flood the place with bottom. But with the current + // framework this is not possible (similar to `InterpCx::eval_operand`). + self.map.find(place.as_ref()).map(ValueOrPlace::Place).unwrap_or(ValueOrPlace::TOP) + } + } + } + + /// The effect of a successful function call return should not be + /// applied here, see [`Analysis::apply_terminator_effect`]. + fn handle_terminator<'mir>( + &self, + terminator: &'mir Terminator<'tcx>, + state: &mut State>, + ) -> TerminatorEdges<'mir, 'tcx> { + match &terminator.kind { + TerminatorKind::Call { .. } | TerminatorKind::InlineAsm { .. } => { + // Effect is applied by `handle_call_return`. + } + TerminatorKind::Drop { place, .. } => { + state.flood_with(place.as_ref(), &self.map, FlatSet::::BOTTOM); + } + TerminatorKind::Yield { .. } => { + // They would have an effect, but are not allowed in this phase. + bug!("encountered disallowed terminator"); + } + TerminatorKind::SwitchInt { discr, targets } => { + return self.handle_switch_int(discr, targets, state); + } + TerminatorKind::TailCall { .. } => { + // FIXME(explicit_tail_calls): determine if we need to do something here (probably + // not) + } + TerminatorKind::Goto { .. } + | TerminatorKind::UnwindResume + | TerminatorKind::UnwindTerminate(_) + | TerminatorKind::Return + | TerminatorKind::Unreachable + | TerminatorKind::Assert { .. } + | TerminatorKind::CoroutineDrop + | TerminatorKind::FalseEdge { .. } + | TerminatorKind::FalseUnwind { .. } => { + // These terminators have no effect on the analysis. + } + } + terminator.edges() + } + + fn handle_call_return( + &self, + return_places: CallReturnPlaces<'_, 'tcx>, + state: &mut State>, + ) { + return_places.for_each(|place| { + state.flood(place.as_ref(), &self.map); + }) } fn handle_set_discriminant( &self, place: Place<'tcx>, variant_index: VariantIdx, - state: &mut State, + state: &mut State>, ) { state.flood_discr(place.as_ref(), &self.map); if self.map.find_discr(place.as_ref()).is_some() { @@ -109,17 +295,17 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { &self, target: Place<'tcx>, rvalue: &Rvalue<'tcx>, - state: &mut State, + state: &mut State>, ) { match rvalue { Rvalue::Use(operand) => { - state.flood(target.as_ref(), self.map()); + state.flood(target.as_ref(), &self.map); if let Some(target) = self.map.find(target.as_ref()) { self.assign_operand(state, target, operand); } } Rvalue::CopyForDeref(rhs) => { - state.flood(target.as_ref(), self.map()); + state.flood(target.as_ref(), &self.map); if let Some(target) = self.map.find(target.as_ref()) { self.assign_operand(state, target, &Operand::Copy(*rhs)); } @@ -127,9 +313,9 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { Rvalue::Aggregate(kind, operands) => { // If we assign `target = Enum::Variant#0(operand)`, // we must make sure that all `target as Variant#i` are `Top`. - state.flood(target.as_ref(), self.map()); + state.flood(target.as_ref(), &self.map); - let Some(target_idx) = self.map().find(target.as_ref()) else { return }; + let Some(target_idx) = self.map.find(target.as_ref()) else { return }; let (variant_target, variant_index) = match **kind { AggregateKind::Tuple | AggregateKind::Closure(..) => (Some(target_idx), None), @@ -148,14 +334,14 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { if let Some(variant_target_idx) = variant_target { for (field_index, operand) in operands.iter_enumerated() { if let Some(field) = - self.map().apply(variant_target_idx, TrackElem::Field(field_index)) + self.map.apply(variant_target_idx, TrackElem::Field(field_index)) { self.assign_operand(state, field, operand); } } } if let Some(variant_index) = variant_index - && let Some(discr_idx) = self.map().apply(target_idx, TrackElem::Discriminant) + && let Some(discr_idx) = self.map.apply(target_idx, TrackElem::Discriminant) { // We are assigning the discriminant as part of an aggregate. // This discriminant can only alias a variant field's value if the operand @@ -170,23 +356,23 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { } Rvalue::BinaryOp(op, box (left, right)) if op.is_overflowing() => { // Flood everything now, so we can use `insert_value_idx` directly later. - state.flood(target.as_ref(), self.map()); + state.flood(target.as_ref(), &self.map); - let Some(target) = self.map().find(target.as_ref()) else { return }; + let Some(target) = self.map.find(target.as_ref()) else { return }; - let value_target = self.map().apply(target, TrackElem::Field(0_u32.into())); - let overflow_target = self.map().apply(target, TrackElem::Field(1_u32.into())); + let value_target = self.map.apply(target, TrackElem::Field(0_u32.into())); + let overflow_target = self.map.apply(target, TrackElem::Field(1_u32.into())); if value_target.is_some() || overflow_target.is_some() { let (val, overflow) = self.binary_op(state, *op, left, right); if let Some(value_target) = value_target { // We have flooded `target` earlier. - state.insert_value_idx(value_target, val, self.map()); + state.insert_value_idx(value_target, val, &self.map); } if let Some(overflow_target) = overflow_target { // We have flooded `target` earlier. - state.insert_value_idx(overflow_target, overflow, self.map()); + state.insert_value_idx(overflow_target, overflow, &self.map); } } } @@ -196,27 +382,30 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { _, ) => { let pointer = self.handle_operand(operand, state); - state.assign(target.as_ref(), pointer, self.map()); + state.assign(target.as_ref(), pointer, &self.map); - if let Some(target_len) = self.map().find_len(target.as_ref()) + if let Some(target_len) = self.map.find_len(target.as_ref()) && let operand_ty = operand.ty(self.local_decls, self.tcx) && let Some(operand_ty) = operand_ty.builtin_deref(true) && let ty::Array(_, len) = operand_ty.kind() && let Some(len) = Const::Ty(self.tcx.types.usize, *len) .try_eval_scalar_int(self.tcx, self.param_env) { - state.insert_value_idx(target_len, FlatSet::Elem(len.into()), self.map()); + state.insert_value_idx(target_len, FlatSet::Elem(len.into()), &self.map); } } - _ => self.super_assign(target, rvalue, state), + _ => { + let result = self.handle_rvalue(rvalue, state); + state.assign(target.as_ref(), result, &self.map); + } } } fn handle_rvalue( &self, rvalue: &Rvalue<'tcx>, - state: &mut State, - ) -> ValueOrPlace { + state: &mut State>, + ) -> ValueOrPlace> { let val = match rvalue { Rvalue::Len(place) => { let place_ty = place.ty(self.local_decls, self.tcx); @@ -225,7 +414,7 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { .try_eval_scalar(self.tcx, self.param_env) .map_or(FlatSet::Top, FlatSet::Elem) } else if let [ProjectionElem::Deref] = place.projection[..] { - state.get_len(place.local.into(), self.map()) + state.get_len(place.local.into(), &self.map) } else { FlatSet::Top } @@ -296,8 +485,24 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { }; FlatSet::Elem(Scalar::from_target_usize(val, &self.tcx)) } - Rvalue::Discriminant(place) => state.get_discr(place.as_ref(), self.map()), - _ => return self.super_rvalue(rvalue, state), + Rvalue::Discriminant(place) => state.get_discr(place.as_ref(), &self.map), + Rvalue::Use(operand) => return self.handle_operand(operand, state), + Rvalue::CopyForDeref(place) => { + return self.handle_operand(&Operand::Copy(*place), state); + } + Rvalue::Ref(..) | Rvalue::RawPtr(..) => { + // We don't track such places. + return ValueOrPlace::TOP; + } + Rvalue::Repeat(..) + | Rvalue::ThreadLocalRef(..) + | Rvalue::Cast(..) + | Rvalue::BinaryOp(..) + | Rvalue::Aggregate(..) + | Rvalue::ShallowInitBox(..) => { + // No modification is possible through these r-values. + return ValueOrPlace::TOP; + } }; ValueOrPlace::Value(val) } @@ -305,8 +510,8 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { fn handle_constant( &self, constant: &ConstOperand<'tcx>, - _state: &mut State, - ) -> Self::Value { + _state: &mut State>, + ) -> FlatSet { constant .const_ .try_eval_scalar(self.tcx, self.param_env) @@ -317,11 +522,11 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { &self, discr: &'mir Operand<'tcx>, targets: &'mir SwitchTargets, - state: &mut State, + state: &mut State>, ) -> TerminatorEdges<'mir, 'tcx> { let value = match self.handle_operand(discr, state) { ValueOrPlace::Value(value) => value, - ValueOrPlace::Place(place) => state.get_idx(place, self.map()), + ValueOrPlace::Place(place) => state.get_idx(place, &self.map), }; match value { // We are branching on uninitialized data, this is UB, treat it as unreachable. @@ -334,19 +539,6 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { FlatSet::Top => TerminatorEdges::SwitchInt { discr, targets }, } } -} - -impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { - fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, map: Map<'tcx>) -> Self { - let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id()); - Self { - map, - tcx, - local_decls: &body.local_decls, - ecx: InterpCx::new(tcx, DUMMY_SP, param_env, DummyMachine), - param_env, - } - } /// The caller must have flooded `place`. fn assign_operand( @@ -537,6 +729,30 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { } } +/// This is used to visualize the dataflow analysis. +impl<'tcx> DebugWithContext> for State> { + fn fmt_with(&self, ctxt: &ConstAnalysis<'_, 'tcx>, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + State::Reachable(values) => debug_with_context(values, None, &ctxt.map, f), + State::Unreachable => write!(f, "unreachable"), + } + } + + fn fmt_diff_with( + &self, + old: &Self, + ctxt: &ConstAnalysis<'_, 'tcx>, + f: &mut Formatter<'_>, + ) -> std::fmt::Result { + match (self, old) { + (State::Reachable(this), State::Reachable(old)) => { + debug_with_context(this, Some(old), &ctxt.map, f) + } + _ => Ok(()), // Consider printing something here. + } + } +} + pub(crate) struct Patch<'tcx> { tcx: TyCtxt<'tcx>, @@ -725,8 +941,7 @@ fn try_write_constant<'tcx>( interp_ok(()) } -impl<'mir, 'tcx> - ResultsVisitor<'mir, 'tcx, Results<'tcx, ValueAnalysisWrapper>>> +impl<'mir, 'tcx> ResultsVisitor<'mir, 'tcx, Results<'tcx, ConstAnalysis<'_, 'tcx>>> for Collector<'_, 'tcx> { type Domain = State>; @@ -734,7 +949,7 @@ impl<'mir, 'tcx> #[instrument(level = "trace", skip(self, results, statement))] fn visit_statement_before_primary_effect( &mut self, - results: &mut Results<'tcx, ValueAnalysisWrapper>>, + results: &mut Results<'tcx, ConstAnalysis<'_, 'tcx>>, state: &Self::Domain, statement: &'mir Statement<'tcx>, location: Location, @@ -744,8 +959,8 @@ impl<'mir, 'tcx> OperandCollector { state, visitor: self, - ecx: &mut results.analysis.0.ecx, - map: &results.analysis.0.map, + ecx: &mut results.analysis.ecx, + map: &results.analysis.map, } .visit_rvalue(rvalue, location); } @@ -756,7 +971,7 @@ impl<'mir, 'tcx> #[instrument(level = "trace", skip(self, results, statement))] fn visit_statement_after_primary_effect( &mut self, - results: &mut Results<'tcx, ValueAnalysisWrapper>>, + results: &mut Results<'tcx, ConstAnalysis<'_, 'tcx>>, state: &Self::Domain, statement: &'mir Statement<'tcx>, location: Location, @@ -767,10 +982,10 @@ impl<'mir, 'tcx> } StatementKind::Assign(box (place, _)) => { if let Some(value) = self.try_make_constant( - &mut results.analysis.0.ecx, + &mut results.analysis.ecx, place, state, - &results.analysis.0.map, + &results.analysis.map, ) { self.patch.assignments.insert(location, value); } @@ -781,7 +996,7 @@ impl<'mir, 'tcx> fn visit_terminator_before_primary_effect( &mut self, - results: &mut Results<'tcx, ValueAnalysisWrapper>>, + results: &mut Results<'tcx, ConstAnalysis<'_, 'tcx>>, state: &Self::Domain, terminator: &'mir Terminator<'tcx>, location: Location, @@ -789,8 +1004,8 @@ impl<'mir, 'tcx> OperandCollector { state, visitor: self, - ecx: &mut results.analysis.0.ecx, - map: &results.analysis.0.map, + ecx: &mut results.analysis.ecx, + map: &results.analysis.map, } .visit_terminator(terminator, location); } From edef7924679605d2ed6d32ad673261444f7f6b5b Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 30 Oct 2024 16:07:26 +1100 Subject: [PATCH 6/7] Reduce some visibilities. --- compiler/rustc_mir_transform/src/dataflow_const_prop.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index b07d01eeaede1..b7032bc1da888 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -753,16 +753,16 @@ impl<'tcx> DebugWithContext> for State> } } -pub(crate) struct Patch<'tcx> { +struct Patch<'tcx> { tcx: TyCtxt<'tcx>, /// For a given MIR location, this stores the values of the operands used by that location. In /// particular, this is before the effect, such that the operands of `_1 = _1 + _2` are /// properly captured. (This may become UB soon, but it is currently emitted even by safe code.) - pub(crate) before_effect: FxHashMap<(Location, Place<'tcx>), Const<'tcx>>, + before_effect: FxHashMap<(Location, Place<'tcx>), Const<'tcx>>, /// Stores the assigned values for assignments where the Rvalue is constant. - pub(crate) assignments: FxHashMap>, + assignments: FxHashMap>, } impl<'tcx> Patch<'tcx> { From ac7de1a0d925af4504f9fb100be071e2fd346d13 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Tue, 29 Oct 2024 23:21:54 -0700 Subject: [PATCH 7/7] Ignore Windows due to its differing path syntax --- .../relative-path-include-bytes-132203.edition2015.stdout | 8 ++++---- .../doctest/relative-path-include-bytes-132203.rs | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/rustdoc-ui/doctest/relative-path-include-bytes-132203.edition2015.stdout b/tests/rustdoc-ui/doctest/relative-path-include-bytes-132203.edition2015.stdout index 535d681750190..ca6e77502640e 100644 --- a/tests/rustdoc-ui/doctest/relative-path-include-bytes-132203.edition2015.stdout +++ b/tests/rustdoc-ui/doctest/relative-path-include-bytes-132203.edition2015.stdout @@ -1,12 +1,12 @@ running 1 test -test $DIR/relative-path-include-bytes-132203.rs - (line 17) ... FAILED +test $DIR/relative-path-include-bytes-132203.rs - (line 18) ... FAILED failures: ----- $DIR/relative-path-include-bytes-132203.rs - (line 17) stdout ---- +---- $DIR/relative-path-include-bytes-132203.rs - (line 18) stdout ---- error: couldn't read `$DIR/relative-dir-empty-file`: No such file or directory (os error 2) - --> $DIR/relative-path-include-bytes-132203.rs:18:9 + --> $DIR/relative-path-include-bytes-132203.rs:19:9 | LL | let x = include_bytes!("relative-dir-empty-file"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -18,7 +18,7 @@ error: aborting due to 1 previous error Couldn't compile the test. failures: - $DIR/relative-path-include-bytes-132203.rs - (line 17) + $DIR/relative-path-include-bytes-132203.rs - (line 18) test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME diff --git a/tests/rustdoc-ui/doctest/relative-path-include-bytes-132203.rs b/tests/rustdoc-ui/doctest/relative-path-include-bytes-132203.rs index 6ec41a3c05283..b393d126306aa 100644 --- a/tests/rustdoc-ui/doctest/relative-path-include-bytes-132203.rs +++ b/tests/rustdoc-ui/doctest/relative-path-include-bytes-132203.rs @@ -1,3 +1,4 @@ +//@ ignore-windows //@ revisions: edition2015 edition2024 //@[edition2015]edition:2015 //@[edition2015]check-fail