From c293af9b57a3eae2969d49f629b67bb3d88c1bf3 Mon Sep 17 00:00:00 2001
From: Peter Jaszkowiak
Date: Wed, 19 Feb 2025 21:06:48 -0700
Subject: [PATCH 01/15] add `IntoBounds::intersect` and `RangeBounds::is_empty`
---
library/core/src/ops/range.rs | 140 +++++++++++++++++-
.../method-suggestion-no-duplication.stderr | 3 +-
2 files changed, 137 insertions(+), 6 deletions(-)
diff --git a/library/core/src/ops/range.rs b/library/core/src/ops/range.rs
index 5580faefacc0d..e0c442e529215 100644
--- a/library/core/src/ops/range.rs
+++ b/library/core/src/ops/range.rs
@@ -771,13 +771,11 @@ pub trait RangeBounds {
/// # Examples
///
/// ```
- /// # fn main() {
/// use std::ops::Bound::*;
/// use std::ops::RangeBounds;
///
/// assert_eq!((..10).start_bound(), Unbounded);
/// assert_eq!((3..10).start_bound(), Included(&3));
- /// # }
/// ```
#[stable(feature = "collections_range", since = "1.28.0")]
fn start_bound(&self) -> Bound<&T>;
@@ -789,13 +787,11 @@ pub trait RangeBounds {
/// # Examples
///
/// ```
- /// # fn main() {
/// use std::ops::Bound::*;
/// use std::ops::RangeBounds;
///
/// assert_eq!((3..).end_bound(), Unbounded);
/// assert_eq!((3..10).end_bound(), Excluded(&10));
- /// # }
/// ```
#[stable(feature = "collections_range", since = "1.28.0")]
fn end_bound(&self) -> Bound<&T>;
@@ -829,6 +825,71 @@ pub trait RangeBounds {
Unbounded => true,
})
}
+
+ /// Returns `true` if the range contains no items.
+ /// One-sided ranges (`RangeFrom`, etc) always return `true`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(range_bounds_is_empty)]
+ /// use std::ops::RangeBounds;
+ ///
+ /// assert!(!(3..).is_empty());
+ /// assert!(!(..2).is_empty());
+ /// assert!(!RangeBounds::is_empty(&(3..5)));
+ /// assert!( RangeBounds::is_empty(&(3..3)));
+ /// assert!( RangeBounds::is_empty(&(3..2)));
+ /// ```
+ ///
+ /// The range is empty if either side is incomparable:
+ ///
+ /// ```
+ /// #![feature(range_bounds_is_empty)]
+ /// use std::ops::RangeBounds;
+ ///
+ /// assert!(!RangeBounds::is_empty(&(3.0..5.0)));
+ /// assert!( RangeBounds::is_empty(&(3.0..f32::NAN)));
+ /// assert!( RangeBounds::is_empty(&(f32::NAN..5.0)));
+ /// ```
+ ///
+ /// But never empty is either side is unbounded:
+ ///
+ /// ```
+ /// #![feature(range_bounds_is_empty)]
+ /// use std::ops::RangeBounds;
+ ///
+ /// assert!(!(..0).is_empty());
+ /// assert!(!(i32::MAX..).is_empty());
+ /// assert!(!RangeBounds::::is_empty(&(..)));
+ /// ```
+ ///
+ /// `(Excluded(a), Excluded(b))` is only empty if `a >= b`:
+ ///
+ /// ```
+ /// #![feature(range_bounds_is_empty)]
+ /// use std::ops::Bound::*;
+ /// use std::ops::RangeBounds;
+ ///
+ /// assert!(!(Excluded(1), Excluded(3)).is_empty());
+ /// assert!(!(Excluded(1), Excluded(2)).is_empty());
+ /// assert!( (Excluded(1), Excluded(1)).is_empty());
+ /// assert!( (Excluded(2), Excluded(1)).is_empty());
+ /// assert!( (Excluded(3), Excluded(1)).is_empty());
+ /// ```
+ #[unstable(feature = "range_bounds_is_empty", issue = "137300")]
+ fn is_empty(&self) -> bool
+ where
+ T: PartialOrd,
+ {
+ !match (self.start_bound(), self.end_bound()) {
+ (Unbounded, _) | (_, Unbounded) => true,
+ (Included(start), Excluded(end))
+ | (Excluded(start), Included(end))
+ | (Excluded(start), Excluded(end)) => start < end,
+ (Included(start), Included(end)) => start <= end,
+ }
+ }
}
/// Used to convert a range into start and end bounds, consuming the
@@ -845,7 +906,6 @@ pub trait IntoBounds: RangeBounds {
///
/// ```
/// #![feature(range_into_bounds)]
- ///
/// use std::ops::Bound::*;
/// use std::ops::IntoBounds;
///
@@ -853,6 +913,76 @@ pub trait IntoBounds: RangeBounds {
/// assert_eq!((..=7).into_bounds(), (Unbounded, Included(7)));
/// ```
fn into_bounds(self) -> (Bound, Bound);
+
+ /// Compute the intersection of `self` and `other`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(range_into_bounds)]
+ /// use std::ops::Bound::*;
+ /// use std::ops::IntoBounds;
+ ///
+ /// assert_eq!((3..).intersect(..5), (Included(3), Excluded(5)));
+ /// assert_eq!((-12..387).intersect(0..256), (Included(0), Excluded(256)));
+ /// assert_eq!((1..5).intersect(..), (Included(1), Excluded(5)));
+ /// assert_eq!((1..=9).intersect(0..10), (Included(1), Included(9)));
+ /// assert_eq!((7..=13).intersect(8..13), (Included(8), Excluded(13)));
+ /// ```
+ ///
+ /// Combine with `is_empty` to determine if two ranges overlap.
+ ///
+ /// ```
+ /// #![feature(range_into_bounds)]
+ /// #![feature(range_bounds_is_empty)]
+ /// use std::ops::{RangeBounds, IntoBounds};
+ ///
+ /// assert!(!(3..).intersect(..5).is_empty());
+ /// assert!(!(-12..387).intersect(0..256).is_empty());
+ /// assert!((1..5).intersect(6..).is_empty());
+ /// ```
+ fn intersect(self, other: R) -> (Bound, Bound)
+ where
+ Self: Sized,
+ T: Ord,
+ R: Sized + IntoBounds,
+ {
+ let (self_start, self_end) = IntoBounds::into_bounds(self);
+ let (other_start, other_end) = IntoBounds::into_bounds(other);
+
+ let start = match (self_start, other_start) {
+ (Included(a), Included(b)) => Included(Ord::max(a, b)),
+ (Excluded(a), Excluded(b)) => Excluded(Ord::max(a, b)),
+ (Unbounded, Unbounded) => Unbounded,
+
+ (x, Unbounded) | (Unbounded, x) => x,
+
+ (Included(i), Excluded(e)) | (Excluded(e), Included(i)) => {
+ if i > e {
+ Included(i)
+ } else {
+ Excluded(e)
+ }
+ }
+ };
+ let end = match (self_end, other_end) {
+ (Included(a), Included(b)) => Included(Ord::min(a, b)),
+ (Excluded(a), Excluded(b)) => Excluded(Ord::min(a, b)),
+ (Unbounded, Unbounded) => Unbounded,
+
+ (x, Unbounded) | (Unbounded, x) => x,
+
+ (Included(i), Excluded(e)) | (Excluded(e), Included(i)) => {
+ if i < e {
+ Included(i)
+ } else {
+ Excluded(e)
+ }
+ }
+ };
+
+ (start, end)
+ }
}
use self::Bound::{Excluded, Included, Unbounded};
diff --git a/tests/ui/impl-trait/method-suggestion-no-duplication.stderr b/tests/ui/impl-trait/method-suggestion-no-duplication.stderr
index c401269da83cc..6bc57f89467d8 100644
--- a/tests/ui/impl-trait/method-suggestion-no-duplication.stderr
+++ b/tests/ui/impl-trait/method-suggestion-no-duplication.stderr
@@ -8,8 +8,9 @@ LL | foo(|s| s.is_empty());
| ^^^^^^^^ method not found in `Foo`
|
= help: items from traits can only be used if the trait is implemented and in scope
- = note: the following trait defines an item `is_empty`, perhaps you need to implement it:
+ = note: the following traits define an item `is_empty`, perhaps you need to implement one of them:
candidate #1: `ExactSizeIterator`
+ candidate #2: `RangeBounds`
error: aborting due to 1 previous error
From 5d6a6e70e34e9b39e1bccb1c48e7b7c5264c838a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?=
<39484203+jieyouxu@users.noreply.github.com>
Date: Tue, 4 Feb 2025 21:54:15 +0800
Subject: [PATCH 02/15] compiletest: disambiguate between root build dir vs
test suite specific build dir
- Introduce and use `--build-{root,test-suite-root}` over
`--build-base`.
- A few minor cleanups.
---
src/tools/compiletest/src/common.rs | 20 +++++++++++-------
src/tools/compiletest/src/header.rs | 10 +++++----
src/tools/compiletest/src/header/tests.rs | 3 ++-
src/tools/compiletest/src/lib.rs | 21 ++++++++++++++-----
src/tools/compiletest/src/runtest.rs | 16 +++++++-------
src/tools/compiletest/src/runtest/run_make.rs | 17 ++++++---------
src/tools/compiletest/src/runtest/ui.rs | 2 +-
7 files changed, 51 insertions(+), 38 deletions(-)
diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index 98ab7adf5a726..b302c6a49f58e 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -220,8 +220,10 @@ pub struct Config {
/// The directory containing the test suite sources. Must be a subdirectory of `src_root`.
pub src_test_suite_root: PathBuf,
- /// The directory where programs should be built
- pub build_base: PathBuf,
+ /// Root build directory (e.g. `build/`).
+ pub build_root: PathBuf,
+ /// Test suite specific build directory (e.g. `build/host/test/ui/`).
+ pub build_test_suite_root: PathBuf,
/// The directory containing the compiler sysroot
pub sysroot_base: PathBuf,
@@ -347,7 +349,7 @@ pub struct Config {
/// If true, this will generate a coverage file with UI test files that run `MachineApplicable`
/// diagnostics but are missing `run-rustfix` annotations. The generated coverage file is
- /// created in `//rustfix_missing_coverage.txt`
+ /// created in `/rustfix_missing_coverage.txt`
pub rustfix_coverage: bool,
/// whether to run `tidy` (html-tidy) when a rustdoc test fails
@@ -812,12 +814,16 @@ pub const UI_STDERR_16: &str = "16bit.stderr";
pub const UI_COVERAGE: &str = "coverage";
pub const UI_COVERAGE_MAP: &str = "cov-map";
-/// Absolute path to the directory where all output for all tests in the given
-/// `relative_dir` group should reside. Example:
-/// /path/to/build/host-tuple/test/ui/relative/
+/// Absolute path to the directory where all output for all tests in the given `relative_dir` group
+/// should reside. Example:
+///
+/// ```text
+/// /path/to/build/host-tuple/test/ui/relative/
+/// ```
+///
/// This is created early when tests are collected to avoid race conditions.
pub fn output_relative_path(config: &Config, relative_dir: &Path) -> PathBuf {
- config.build_base.join(relative_dir)
+ config.build_test_suite_root.join(relative_dir)
}
/// Generates a unique name for the test, such as `testname.revision.mode`.
diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs
index 3bdf37a1f294a..53ee901b8bc48 100644
--- a/src/tools/compiletest/src/header.rs
+++ b/src/tools/compiletest/src/header.rs
@@ -1070,10 +1070,11 @@ impl Config {
}
}
+// FIXME(jieyouxu): fix some of these variable names to more accurately reflect what they do.
fn expand_variables(mut value: String, config: &Config) -> String {
const CWD: &str = "{{cwd}}";
const SRC_BASE: &str = "{{src-base}}";
- const BUILD_BASE: &str = "{{build-base}}";
+ const TEST_SUITE_BUILD_BASE: &str = "{{build-base}}";
const RUST_SRC_BASE: &str = "{{rust-src-base}}";
const SYSROOT_BASE: &str = "{{sysroot-base}}";
const TARGET_LINKER: &str = "{{target-linker}}";
@@ -1088,12 +1089,13 @@ fn expand_variables(mut value: String, config: &Config) -> String {
value = value.replace(SRC_BASE, &config.src_test_suite_root.to_str().unwrap());
}
- if value.contains(BUILD_BASE) {
- value = value.replace(BUILD_BASE, &config.build_base.to_string_lossy());
+ if value.contains(TEST_SUITE_BUILD_BASE) {
+ value =
+ value.replace(TEST_SUITE_BUILD_BASE, &config.build_test_suite_root.to_str().unwrap());
}
if value.contains(SYSROOT_BASE) {
- value = value.replace(SYSROOT_BASE, &config.sysroot_base.to_string_lossy());
+ value = value.replace(SYSROOT_BASE, &config.sysroot_base.to_str().unwrap());
}
if value.contains(TARGET_LINKER) {
diff --git a/src/tools/compiletest/src/header/tests.rs b/src/tools/compiletest/src/header/tests.rs
index c9536b7a19b79..e47ea001e7e6a 100644
--- a/src/tools/compiletest/src/header/tests.rs
+++ b/src/tools/compiletest/src/header/tests.rs
@@ -155,7 +155,8 @@ impl ConfigBuilder {
"--jsondocck-path=",
"--src-root=",
"--src-test-suite-root=",
- "--build-base=",
+ "--build-root=",
+ "--build-test-suite-root=",
"--sysroot-base=",
"--cc=c",
"--cxx=c++",
diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs
index 979821701385e..9dff7047bc4ab 100644
--- a/src/tools/compiletest/src/lib.rs
+++ b/src/tools/compiletest/src/lib.rs
@@ -63,7 +63,8 @@ pub fn parse_config(args: Vec) -> Config {
.optopt("", "llvm-filecheck", "path to LLVM's FileCheck binary", "DIR")
.reqopt("", "src-root", "directory containing sources", "PATH")
.reqopt("", "src-test-suite-root", "directory containing test suite sources", "PATH")
- .reqopt("", "build-base", "directory to deposit test outputs", "PATH")
+ .reqopt("", "build-root", "path to root build directory", "PATH")
+ .reqopt("", "build-test-suite-root", "path to test suite specific build directory", "PATH")
.reqopt("", "sysroot-base", "directory containing the compiler sysroot", "PATH")
.reqopt("", "stage", "stage number under test", "N")
.reqopt("", "stage-id", "the target-stage identifier", "stageN-TARGET")
@@ -157,7 +158,7 @@ pub fn parse_config(args: Vec) -> Config {
"",
"rustfix-coverage",
"enable this to generate a Rustfix coverage file, which is saved in \
- `.//rustfix_missing_coverage.txt`",
+ `.//rustfix_missing_coverage.txt`",
)
.optflag("", "force-rerun", "rerun tests even if the inputs are unchanged")
.optflag("", "only-modified", "only run tests that result been modified")
@@ -309,6 +310,10 @@ pub fn parse_config(args: Vec) -> Config {
src_test_suite_root.display()
);
+ let build_root = opt_path(matches, "build-root");
+ let build_test_suite_root = opt_path(matches, "build-test-suite-root");
+ assert!(build_test_suite_root.starts_with(&build_root));
+
Config {
bless: matches.opt_present("bless"),
compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")),
@@ -327,7 +332,9 @@ pub fn parse_config(args: Vec) -> Config {
src_root,
src_test_suite_root,
- build_base: opt_path(matches, "build-base"),
+ build_root,
+ build_test_suite_root,
+
sysroot_base: opt_path(matches, "sysroot-base"),
stage,
@@ -438,7 +445,11 @@ pub fn log_config(config: &Config) {
logv(c, format!("src_root: {}", config.src_root.display()));
logv(c, format!("src_test_suite_root: {}", config.src_test_suite_root.display()));
- logv(c, format!("build_base: {:?}", config.build_base.display()));
+ logv(c, format!("build_root: {}", config.build_root.display()));
+ logv(c, format!("build_test_suite_root: {}", config.build_test_suite_root.display()));
+
+ logv(c, format!("sysroot_base: {}", config.sysroot_base.display()));
+
logv(c, format!("stage: {}", config.stage));
logv(c, format!("stage_id: {}", config.stage_id));
logv(c, format!("mode: {}", config.mode));
@@ -488,7 +499,7 @@ pub fn run_tests(config: Arc) {
// we first make sure that the coverage file does not exist.
// It will be created later on.
if config.rustfix_coverage {
- let mut coverage_file_path = config.build_base.clone();
+ let mut coverage_file_path = config.build_test_suite_root.clone();
coverage_file_path.push("rustfix_missing_coverage.txt");
if coverage_file_path.exists() {
if let Err(e) = fs::remove_file(&coverage_file_path) {
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 536e19bc4933e..f36d43b211fff 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -535,7 +535,9 @@ impl<'test> TestCx<'test> {
.arg(&out_dir)
.arg(&format!("--target={}", target))
.arg("-L")
- .arg(&self.config.build_base)
+ // FIXME(jieyouxu): this search path seems questionable. Is this intended for
+ // `rust_test_helpers` in ui tests?
+ .arg(&self.config.build_test_suite_root)
.arg("-L")
.arg(aux_dir)
.arg("-A")
@@ -1366,7 +1368,7 @@ impl<'test> TestCx<'test> {
// Note: avoid adding a subdirectory of an already filtered directory here, otherwise the
// same slice of text will be double counted and the truncation might not happen.
add_path(&self.config.src_test_suite_root);
- add_path(&self.config.build_base);
+ add_path(&self.config.build_test_suite_root);
read2_abbreviated(child, &filter_paths_from_len).expect("failed to read output")
}
@@ -1421,7 +1423,7 @@ impl<'test> TestCx<'test> {
}
fn get_mir_dump_dir(&self) -> PathBuf {
- let mut mir_dump_dir = PathBuf::from(self.config.build_base.as_path());
+ let mut mir_dump_dir = self.config.build_test_suite_root.clone();
debug!("input_file: {:?}", self.testpaths.file);
mir_dump_dir.push(&self.testpaths.relative_dir);
mir_dump_dir.push(self.testpaths.file.file_stem().unwrap());
@@ -2410,14 +2412,10 @@ impl<'test> TestCx<'test> {
let rust_src_dir = rust_src_dir.read_link().unwrap_or(rust_src_dir.to_path_buf());
normalize_path(&rust_src_dir.join("library"), "$SRC_DIR_REAL");
- // Paths into the build directory
- let test_build_dir = &self.config.build_base;
- let parent_build_dir = test_build_dir.parent().unwrap().parent().unwrap().parent().unwrap();
-
// eg. /home/user/rust/build/x86_64-unknown-linux-gnu/test/ui
- normalize_path(test_build_dir, "$TEST_BUILD_DIR");
+ normalize_path(&self.config.build_test_suite_root, "$TEST_BUILD_DIR");
// eg. /home/user/rust/build
- normalize_path(parent_build_dir, "$BUILD_DIR");
+ normalize_path(&self.config.build_root, "$BUILD_DIR");
if json {
// escaped newlines in json strings should be readable
diff --git a/src/tools/compiletest/src/runtest/run_make.rs b/src/tools/compiletest/src/runtest/run_make.rs
index 74e6af36ea1dd..9bb3993223e21 100644
--- a/src/tools/compiletest/src/runtest/run_make.rs
+++ b/src/tools/compiletest/src/runtest/run_make.rs
@@ -179,12 +179,7 @@ impl TestCx<'_> {
// library.
// 2. We need to run the recipe binary.
- // `self.config.build_base` is actually the build base folder + "test" + test suite name, it
- // looks like `build//test/run-make`. But we want `build//`. Note
- // that the `build` directory does not need to be called `build`, nor does it need to be
- // under `src_root`, so we must compute it based off of `self.config.build_base`.
- let build_root =
- self.config.build_base.parent().and_then(Path::parent).unwrap().to_path_buf();
+ let host_build_root = self.config.build_root.join(&self.config.host);
// We construct the following directory tree for each rmake.rs test:
// ```
@@ -242,10 +237,10 @@ impl TestCx<'_> {
let stage_number = self.config.stage;
- let stage_tools_bin = build_root.join(format!("stage{stage_number}-tools-bin"));
+ let stage_tools_bin = host_build_root.join(format!("stage{stage_number}-tools-bin"));
let support_lib_path = stage_tools_bin.join("librun_make_support.rlib");
- let stage_tools = build_root.join(format!("stage{stage_number}-tools"));
+ let stage_tools = host_build_root.join(format!("stage{stage_number}-tools"));
let support_lib_deps = stage_tools.join(&self.config.host).join("release").join("deps");
let support_lib_deps_deps = stage_tools.join("release").join("deps");
@@ -311,7 +306,7 @@ impl TestCx<'_> {
// to work correctly.
//
// See for more background.
- let stage0_sysroot = build_root.join("stage0-sysroot");
+ let stage0_sysroot = host_build_root.join("stage0-sysroot");
if std::env::var_os("COMPILETEST_FORCE_STAGE0").is_some() {
rustc.arg("--sysroot").arg(&stage0_sysroot);
}
@@ -326,7 +321,7 @@ impl TestCx<'_> {
// provided through env vars.
// Compute stage-specific standard library paths.
- let stage_std_path = build_root.join(format!("stage{stage_number}")).join("lib");
+ let stage_std_path = host_build_root.join(format!("stage{stage_number}")).join("lib");
// Compute dynamic library search paths for recipes.
let recipe_dylib_search_paths = {
@@ -372,7 +367,7 @@ impl TestCx<'_> {
// Provide path to sources root.
.env("SOURCE_ROOT", &self.config.src_root)
// Path to the host build directory.
- .env("BUILD_ROOT", &build_root)
+ .env("BUILD_ROOT", &host_build_root)
// Provide path to stage-corresponding rustc.
.env("RUSTC", &self.config.rustc_path)
// Provide the directory to libraries that are needed to run the *compiler*. This is not
diff --git a/src/tools/compiletest/src/runtest/ui.rs b/src/tools/compiletest/src/runtest/ui.rs
index 0c6d46188e6f4..3329e10745f8d 100644
--- a/src/tools/compiletest/src/runtest/ui.rs
+++ b/src/tools/compiletest/src/runtest/ui.rs
@@ -66,7 +66,7 @@ impl TestCx<'_> {
&& !self.props.run_rustfix
&& !self.props.rustfix_only_machine_applicable
{
- let mut coverage_file_path = self.config.build_base.clone();
+ let mut coverage_file_path = self.config.build_test_suite_root.clone();
coverage_file_path.push("rustfix_missing_coverage.txt");
debug!("coverage_file_path: {}", coverage_file_path.display());
From 5238337d522351eaa71659c3865b6bd7cf9341c6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?=
<39484203+jieyouxu@users.noreply.github.com>
Date: Tue, 4 Feb 2025 21:28:32 +0800
Subject: [PATCH 03/15] bootstrap: remove `--build-base` and use `--build-root`
and `--build-test-suite-root` instead
---
src/bootstrap/src/core/build_steps/test.rs | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs
index 604f9e73b4581..cdb1ae70ab613 100644
--- a/src/bootstrap/src/core/build_steps/test.rs
+++ b/src/bootstrap/src/core/build_steps/test.rs
@@ -1774,7 +1774,11 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
cmd.arg("--src-root").arg(&builder.src);
cmd.arg("--src-test-suite-root").arg(builder.src.join("tests").join(suite));
- cmd.arg("--build-base").arg(testdir(builder, compiler.host).join(suite));
+ // N.B. it's important to distinguish between the *root* build directory, the *host* build
+ // directory immediately under the root build directory, and the test-suite-specific build
+ // directory.
+ cmd.arg("--build-root").arg(&builder.out);
+ cmd.arg("--build-test-suite-root").arg(testdir(builder, compiler.host).join(suite));
// When top stage is 0, that means that we're testing an externally provided compiler.
// In that case we need to use its specific sysroot for tests to pass.
From 7d2e4e4b6966ae4646e4f87800dafe6cc1b839c2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?=
<39484203+jieyouxu@users.noreply.github.com>
Date: Tue, 4 Feb 2025 21:30:36 +0800
Subject: [PATCH 04/15] bootstrap: remove redundant `to_path_buf()`
---
src/bootstrap/src/core/build_steps/test.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs
index cdb1ae70ab613..2053e9c97d740 100644
--- a/src/bootstrap/src/core/build_steps/test.rs
+++ b/src/bootstrap/src/core/build_steps/test.rs
@@ -1785,7 +1785,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
let sysroot = if builder.top_stage == 0 {
builder.initial_sysroot.clone()
} else {
- builder.sysroot(compiler).to_path_buf()
+ builder.sysroot(compiler)
};
cmd.arg("--sysroot-base").arg(sysroot);
From 7041eedb1a00640805d9e4dd3cd1efd142655652 Mon Sep 17 00:00:00 2001
From: Oli Scherer
Date: Wed, 12 Feb 2025 10:37:49 +0000
Subject: [PATCH 05/15] Prefer a two value enum over bool
---
compiler/rustc_hir/src/hir.rs | 2 +-
.../rustc_hir_analysis/src/hir_ty_lowering/mod.rs | 5 -----
compiler/rustc_lint/src/types.rs | 11 ++++-------
compiler/rustc_middle/src/ty/pattern.rs | 8 +++-----
compiler/rustc_middle/src/ty/structural_impls.rs | 1 +
compiler/rustc_smir/src/rustc_internal/internal.rs | 3 ++-
compiler/rustc_smir/src/rustc_smir/convert/ty.rs | 2 +-
compiler/rustc_symbol_mangling/src/v0.rs | 5 ++++-
compiler/rustc_ty_utils/src/layout.rs | 5 +++--
9 files changed, 19 insertions(+), 23 deletions(-)
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index f0eaec55dbdd3..63b1ae5c88651 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -1600,7 +1600,7 @@ pub struct PatField<'hir> {
pub span: Span,
}
-#[derive(Copy, Clone, PartialEq, Debug, HashStable_Generic)]
+#[derive(Copy, Clone, PartialEq, Debug, HashStable_Generic, Hash, Eq, Encodable, Decodable)]
pub enum RangeEnd {
Included,
Excluded,
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 750770178eef9..6f87707f479de 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
@@ -2468,11 +2468,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let start = start.map(|expr| self.lower_const_arg(expr, FeedConstTy::No));
let end = end.map(|expr| self.lower_const_arg(expr, FeedConstTy::No));
- let include_end = match include_end {
- hir::RangeEnd::Included => true,
- hir::RangeEnd::Excluded => false,
- };
-
let pat = tcx.mk_pat(ty::PatternKind::Range { start, end, include_end });
Ty::new_pat(tcx, ty, pat)
}
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index cb83d405cc3ed..0740ee7e82348 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -5,7 +5,7 @@ use rustc_abi::{BackendRepr, ExternAbi, TagEncoding, VariantIdx, Variants, Wrapp
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::DiagMessage;
use rustc_hir::intravisit::VisitorExt;
-use rustc_hir::{AmbigArg, Expr, ExprKind, HirId, LangItem};
+use rustc_hir::{AmbigArg, Expr, ExprKind, HirId, LangItem, RangeEnd};
use rustc_middle::bug;
use rustc_middle::ty::layout::{LayoutOf, SizeSkeleton};
use rustc_middle::ty::{
@@ -893,12 +893,9 @@ fn ty_is_known_nonnull<'tcx>(
let end =
end.try_to_value()?.try_to_bits(tcx, typing_env)?;
- if include_end {
- // This also works for negative numbers, as we just need
- // to ensure we aren't wrapping over zero.
- start > 0 && end >= start
- } else {
- start > 0 && end > start
+ match include_end {
+ RangeEnd::Included => start > 0 && end >= start,
+ RangeEnd::Excluded => start > 0 && end > start,
}
}
_ => false,
diff --git a/compiler/rustc_middle/src/ty/pattern.rs b/compiler/rustc_middle/src/ty/pattern.rs
index e604aedd05e32..48ae5b1e657de 100644
--- a/compiler/rustc_middle/src/ty/pattern.rs
+++ b/compiler/rustc_middle/src/ty/pattern.rs
@@ -1,6 +1,7 @@
use std::fmt;
use rustc_data_structures::intern::Interned;
+use rustc_hir::RangeEnd;
use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
use crate::ty;
@@ -30,10 +31,7 @@ impl<'tcx> fmt::Debug for PatternKind<'tcx> {
if let Some(start) = start {
write!(f, "{start}")?;
}
- write!(f, "..")?;
- if include_end {
- write!(f, "=")?;
- }
+ write!(f, "{include_end}")?;
if let Some(end) = end {
write!(f, "{end}")?;
}
@@ -46,5 +44,5 @@ impl<'tcx> fmt::Debug for PatternKind<'tcx> {
#[derive(Clone, PartialEq, Eq, Hash)]
#[derive(HashStable, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable)]
pub enum PatternKind<'tcx> {
- Range { start: Option>, end: Option>, include_end: bool },
+ Range { start: Option>, end: Option>, include_end: RangeEnd },
}
diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs
index db9e9fbc643b2..6c62c04f42e4c 100644
--- a/compiler/rustc_middle/src/ty/structural_impls.rs
+++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -284,6 +284,7 @@ TrivialTypeTraversalImpls! {
rustc_hir::def_id::LocalDefId,
rustc_hir::HirId,
rustc_hir::MatchSource,
+ rustc_hir::RangeEnd,
rustc_span::Ident,
rustc_span::Span,
rustc_span::Symbol,
diff --git a/compiler/rustc_smir/src/rustc_internal/internal.rs b/compiler/rustc_smir/src/rustc_internal/internal.rs
index 50cf605ba2a82..093ce5f56488b 100644
--- a/compiler/rustc_smir/src/rustc_internal/internal.rs
+++ b/compiler/rustc_smir/src/rustc_internal/internal.rs
@@ -5,6 +5,7 @@
// Prefer importing stable_mir over internal rustc constructs to make this file more readable.
+use rustc_hir::RangeEnd;
use rustc_middle::ty::{self as rustc_ty, Const as InternalConst, Ty as InternalTy, TyCtxt};
use rustc_span::Symbol;
use stable_mir::abi::Layout;
@@ -91,7 +92,7 @@ impl RustcInternal for Pattern {
Pattern::Range { start, end, include_end } => rustc_ty::PatternKind::Range {
start: start.as_ref().map(|c| c.internal(tables, tcx)),
end: end.as_ref().map(|c| c.internal(tables, tcx)),
- include_end: *include_end,
+ include_end: if *include_end { RangeEnd::Included } else { RangeEnd::Excluded },
},
})
}
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
index a0faf20c79a67..9c06d08d61b5a 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
@@ -408,7 +408,7 @@ impl<'tcx> Stable<'tcx> for ty::Pattern<'tcx> {
ty::PatternKind::Range { start, end, include_end } => stable_mir::ty::Pattern::Range {
start: start.stable(tables),
end: end.stable(tables),
- include_end,
+ include_end: matches!(include_end, rustc_hir::RangeEnd::Included),
},
}
}
diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs
index 4fafd1ac3509a..66b6af6bf51d4 100644
--- a/compiler/rustc_symbol_mangling/src/v0.rs
+++ b/compiler/rustc_symbol_mangling/src/v0.rs
@@ -417,7 +417,10 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
let consts = [
start.unwrap_or(self.tcx.consts.unit),
end.unwrap_or(self.tcx.consts.unit),
- ty::Const::from_bool(self.tcx, include_end),
+ ty::Const::from_bool(
+ self.tcx,
+ matches!(include_end, rustc_hir::RangeEnd::Included),
+ ),
];
// HACK: Represent as tuple until we have something better.
// HACK: constants are used in arrays, even if the types don't match.
diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs
index 2ab6b8e17c4cc..0e5a299956154 100644
--- a/compiler/rustc_ty_utils/src/layout.rs
+++ b/compiler/rustc_ty_utils/src/layout.rs
@@ -220,8 +220,9 @@ fn layout_of_uncached<'tcx>(
let mut end = extract_const_value(cx, ty, end)?
.try_to_bits(tcx, cx.typing_env)
.ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
- if !include_end {
- end = end.wrapping_sub(1);
+ match include_end {
+ rustc_hir::RangeEnd::Included => {}
+ rustc_hir::RangeEnd::Excluded => end = end.wrapping_sub(1),
}
scalar.valid_range_mut().end = end;
}
From 0ea02cdcbc0780fbba4d93fdbf9b36b339ea4518 Mon Sep 17 00:00:00 2001
From: Oli Scherer
Date: Wed, 5 Feb 2025 15:22:10 +0000
Subject: [PATCH 06/15] Avoid having to handle an `Option` in the type system
---
.../src/hir_ty_lowering/mod.rs | 89 ++++++++++++++++---
.../src/variance/constraints.rs | 8 +-
compiler/rustc_lint/src/types.rs | 24 ++---
compiler/rustc_middle/src/ty/flags.rs | 8 +-
compiler/rustc_middle/src/ty/pattern.rs | 11 +--
compiler/rustc_middle/src/ty/relate.rs | 23 +++--
compiler/rustc_middle/src/ty/walk.rs | 4 +-
.../rustc_smir/src/rustc_internal/internal.rs | 4 +-
.../rustc_smir/src/rustc_smir/convert/ty.rs | 5 +-
compiler/rustc_symbol_mangling/src/v0.rs | 4 +-
.../rustc_trait_selection/src/traits/wf.rs | 8 +-
compiler/rustc_ty_utils/src/layout.rs | 25 +++---
.../pattern_types.main.PreCodegen.after.mir | 4 +-
tests/mir-opt/pattern_types.rs | 4 +-
tests/ui/lint/clashing-extern-fn.stderr | 4 +-
tests/ui/type/pattern_types/nested.stderr | 18 ++--
.../type/pattern_types/range_patterns.stderr | 4 +-
.../range_patterns_trait_impls2.stderr | 2 +-
.../range_patterns_unusable.stderr | 2 +-
.../range_patterns_unusable_math.rs | 2 +-
.../range_patterns_unusable_math.stderr | 4 +-
21 files changed, 147 insertions(+), 110 deletions(-)
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 6f87707f479de..efafac19defe0 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
@@ -21,8 +21,9 @@ pub mod generics;
mod lint;
use std::assert_matches::assert_matches;
-use std::slice;
+use std::{char, slice};
+use rustc_abi::Size;
use rustc_ast::TraitObjectSyntax;
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
use rustc_errors::codes::*;
@@ -31,7 +32,7 @@ use rustc_errors::{
};
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Namespace, Res};
use rustc_hir::def_id::{DefId, LocalDefId};
-use rustc_hir::{self as hir, AnonConst, GenericArg, GenericArgs, HirId};
+use rustc_hir::{self as hir, AnonConst, ConstArg, GenericArg, GenericArgs, HirId};
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
use rustc_infer::traits::ObligationCause;
use rustc_middle::middle::stability::AllowUnstable;
@@ -2453,20 +2454,22 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let ty = self.lower_ty(ty);
let pat_ty = match pat.kind {
hir::TyPatKind::Range(start, end, include_end) => {
- let ty = match ty.kind() {
- ty::Int(_) | ty::Uint(_) | ty::Char => ty,
- _ => Ty::new_error(
- tcx,
- self.dcx().emit_err(InvalidBaseType {
+ let (ty, start, end) = match ty.kind() {
+ ty::Int(_) | ty::Uint(_) | ty::Char => {
+ let (start, end) = self.lower_ty_pat_range(ty, start, end);
+ (ty, start, end)
+ }
+ _ => {
+ let guar = self.dcx().emit_err(InvalidBaseType {
ty,
pat: "range",
ty_span,
pat_span: pat.span,
- }),
- ),
+ });
+ let errc = ty::Const::new_error(tcx, guar);
+ (Ty::new_error(tcx, guar), errc, errc)
+ }
};
- let start = start.map(|expr| self.lower_const_arg(expr, FeedConstTy::No));
- let end = end.map(|expr| self.lower_const_arg(expr, FeedConstTy::No));
let pat = tcx.mk_pat(ty::PatternKind::Range { start, end, include_end });
Ty::new_pat(tcx, ty, pat)
@@ -2483,6 +2486,70 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
result_ty
}
+ fn lower_ty_pat_range(
+ &self,
+ base: Ty<'tcx>,
+ start: Option<&ConstArg<'tcx>>,
+ end: Option<&ConstArg<'tcx>>,
+ ) -> (ty::Const<'tcx>, ty::Const<'tcx>) {
+ let tcx = self.tcx();
+ let size = match base.kind() {
+ ty::Int(i) => {
+ i.bit_width().map_or(tcx.data_layout.pointer_size, |bits| Size::from_bits(bits))
+ }
+ ty::Uint(ui) => {
+ ui.bit_width().map_or(tcx.data_layout.pointer_size, |bits| Size::from_bits(bits))
+ }
+ ty::Char => Size::from_bytes(4),
+ _ => unreachable!(),
+ };
+ let start =
+ start.map(|expr| self.lower_const_arg(expr, FeedConstTy::No)).unwrap_or_else(|| {
+ match base.kind() {
+ ty::Char | ty::Uint(_) => ty::Const::new_value(
+ tcx,
+ ty::ValTree::from_scalar_int(ty::ScalarInt::null(size)),
+ base,
+ ),
+ ty::Int(_) => ty::Const::new_value(
+ tcx,
+ ty::ValTree::from_scalar_int(
+ ty::ScalarInt::truncate_from_int(size.signed_int_min(), size).0,
+ ),
+ base,
+ ),
+ _ => unreachable!(),
+ }
+ });
+ let end = end.map(|expr| self.lower_const_arg(expr, FeedConstTy::No)).unwrap_or_else(
+ || match base.kind() {
+ ty::Char => ty::Const::new_value(
+ tcx,
+ ty::ValTree::from_scalar_int(
+ ty::ScalarInt::truncate_from_uint(char::MAX, size).0,
+ ),
+ base,
+ ),
+ ty::Uint(_) => ty::Const::new_value(
+ tcx,
+ ty::ValTree::from_scalar_int(
+ ty::ScalarInt::truncate_from_uint(size.unsigned_int_max(), size).0,
+ ),
+ base,
+ ),
+ ty::Int(_) => ty::Const::new_value(
+ tcx,
+ ty::ValTree::from_scalar_int(
+ ty::ScalarInt::truncate_from_int(size.signed_int_max(), size).0,
+ ),
+ base,
+ ),
+ _ => unreachable!(),
+ },
+ );
+ (start, end)
+ }
+
/// Lower an opaque type (i.e., an existential impl-Trait type) from the HIR.
#[instrument(level = "debug", skip(self), ret)]
fn lower_opaque_ty(&self, def_id: LocalDefId, in_trait: bool) -> Ty<'tcx> {
diff --git a/compiler/rustc_hir_analysis/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs
index e954d2b9ea48e..4b336769cfb80 100644
--- a/compiler/rustc_hir_analysis/src/variance/constraints.rs
+++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs
@@ -253,12 +253,8 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
ty::Pat(typ, pat) => {
match *pat {
ty::PatternKind::Range { start, end, include_end: _ } => {
- if let Some(start) = start {
- self.add_constraints_from_const(current, start, variance);
- }
- if let Some(end) = end {
- self.add_constraints_from_const(current, end, variance);
- }
+ self.add_constraints_from_const(current, start, variance);
+ self.add_constraints_from_const(current, end, variance);
}
}
self.add_constraints_from_ty(current, typ, variance);
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index 0740ee7e82348..73fe8f1057885 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -883,22 +883,14 @@ fn ty_is_known_nonnull<'tcx>(
try {
match **pat {
ty::PatternKind::Range { start, end, include_end } => {
- match (start, end) {
- (Some(start), None) => {
- start.try_to_value()?.try_to_bits(tcx, typing_env)? > 0
- }
- (Some(start), Some(end)) => {
- let start =
- start.try_to_value()?.try_to_bits(tcx, typing_env)?;
- let end =
- end.try_to_value()?.try_to_bits(tcx, typing_env)?;
-
- match include_end {
- RangeEnd::Included => start > 0 && end >= start,
- RangeEnd::Excluded => start > 0 && end > start,
- }
- }
- _ => false,
+ let start = start.try_to_value()?.try_to_bits(tcx, typing_env)?;
+ let end = end.try_to_value()?.try_to_bits(tcx, typing_env)?;
+
+ match include_end {
+ // This also works for negative numbers, as we just need
+ // to ensure we aren't wrapping over zero.
+ RangeEnd::Included => start > 0 && end >= start,
+ RangeEnd::Excluded => start > 0 && end > start,
}
}
}
diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs
index ec0498b168c01..c08d78e25b8d1 100644
--- a/compiler/rustc_middle/src/ty/flags.rs
+++ b/compiler/rustc_middle/src/ty/flags.rs
@@ -221,12 +221,8 @@ impl FlagComputation {
self.add_ty(ty);
match *pat {
ty::PatternKind::Range { start, end, include_end: _ } => {
- if let Some(start) = start {
- self.add_const(start)
- }
- if let Some(end) = end {
- self.add_const(end)
- }
+ self.add_const(start);
+ self.add_const(end);
}
}
}
diff --git a/compiler/rustc_middle/src/ty/pattern.rs b/compiler/rustc_middle/src/ty/pattern.rs
index 48ae5b1e657de..a1dfd2e8d34f0 100644
--- a/compiler/rustc_middle/src/ty/pattern.rs
+++ b/compiler/rustc_middle/src/ty/pattern.rs
@@ -28,14 +28,7 @@ impl<'tcx> fmt::Debug for PatternKind<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
PatternKind::Range { start, end, include_end } => {
- if let Some(start) = start {
- write!(f, "{start}")?;
- }
- write!(f, "{include_end}")?;
- if let Some(end) = end {
- write!(f, "{end}")?;
- }
- Ok(())
+ write!(f, "{start}{include_end}{end}")
}
}
}
@@ -44,5 +37,5 @@ impl<'tcx> fmt::Debug for PatternKind<'tcx> {
#[derive(Clone, PartialEq, Eq, Hash)]
#[derive(HashStable, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable)]
pub enum PatternKind<'tcx> {
- Range { start: Option>, end: Option>, include_end: RangeEnd },
+ Range { start: ty::Const<'tcx>, end: ty::Const<'tcx>, include_end: RangeEnd },
}
diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs
index 839c1c346a47b..6d5cc42065f22 100644
--- a/compiler/rustc_middle/src/ty/relate.rs
+++ b/compiler/rustc_middle/src/ty/relate.rs
@@ -54,19 +54,18 @@ impl<'tcx> Relate> for ty::Pattern<'tcx> {
&ty::PatternKind::Range { start: start_a, end: end_a, include_end: inc_a },
&ty::PatternKind::Range { start: start_b, end: end_b, include_end: inc_b },
) => {
- // FIXME(pattern_types): make equal patterns equal (`0..=` is the same as `..=`).
- let mut relate_opt_const = |a, b| match (a, b) {
- (None, None) => Ok(None),
- (Some(a), Some(b)) => relation.relate(a, b).map(Some),
- // FIXME(pattern_types): report a better error
- _ => Err(TypeError::Mismatch),
- };
- let start = relate_opt_const(start_a, start_b)?;
- let end = relate_opt_const(end_a, end_b)?;
- if inc_a != inc_b {
- todo!()
+ let start = relation.relate(start_a, start_b)?;
+ // FIXME(pattern_types): make equal patterns equal (`0..5` is the same as `0..=6`).
+ let end = relation.relate(end_a, end_b)?;
+ if inc_a == inc_b {
+ Ok(relation.cx().mk_pat(ty::PatternKind::Range {
+ start,
+ end,
+ include_end: inc_a,
+ }))
+ } else {
+ Err(TypeError::Mismatch)
}
- Ok(relation.cx().mk_pat(ty::PatternKind::Range { start, end, include_end: inc_a }))
}
}
}
diff --git a/compiler/rustc_middle/src/ty/walk.rs b/compiler/rustc_middle/src/ty/walk.rs
index 3e8a3d1a28923..b31b7cac41ef2 100644
--- a/compiler/rustc_middle/src/ty/walk.rs
+++ b/compiler/rustc_middle/src/ty/walk.rs
@@ -138,8 +138,8 @@ fn push_inner<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent: GenericArg<'tcx>)
ty::Pat(ty, pat) => {
match *pat {
ty::PatternKind::Range { start, end, include_end: _ } => {
- stack.extend(end.map(Into::into));
- stack.extend(start.map(Into::into));
+ stack.push(end.into());
+ stack.push(start.into());
}
}
stack.push(ty.into());
diff --git a/compiler/rustc_smir/src/rustc_internal/internal.rs b/compiler/rustc_smir/src/rustc_internal/internal.rs
index 093ce5f56488b..a8c4cd660fd98 100644
--- a/compiler/rustc_smir/src/rustc_internal/internal.rs
+++ b/compiler/rustc_smir/src/rustc_internal/internal.rs
@@ -90,8 +90,8 @@ impl RustcInternal for Pattern {
fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
tcx.mk_pat(match self {
Pattern::Range { start, end, include_end } => rustc_ty::PatternKind::Range {
- start: start.as_ref().map(|c| c.internal(tables, tcx)),
- end: end.as_ref().map(|c| c.internal(tables, tcx)),
+ start: start.as_ref().unwrap().internal(tables, tcx),
+ end: end.as_ref().unwrap().internal(tables, tcx),
include_end: if *include_end { RangeEnd::Included } else { RangeEnd::Excluded },
},
})
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
index 9c06d08d61b5a..f6a6a3c1737fb 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
@@ -406,8 +406,9 @@ impl<'tcx> Stable<'tcx> for ty::Pattern<'tcx> {
fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
match **self {
ty::PatternKind::Range { start, end, include_end } => stable_mir::ty::Pattern::Range {
- start: start.stable(tables),
- end: end.stable(tables),
+ // FIXME(SMIR): update data structures to not have an Option here anymore
+ start: Some(start.stable(tables)),
+ end: Some(end.stable(tables)),
include_end: matches!(include_end, rustc_hir::RangeEnd::Included),
},
}
diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs
index 66b6af6bf51d4..754c74c4f009f 100644
--- a/compiler/rustc_symbol_mangling/src/v0.rs
+++ b/compiler/rustc_symbol_mangling/src/v0.rs
@@ -415,8 +415,8 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
ty::Pat(ty, pat) => match *pat {
ty::PatternKind::Range { start, end, include_end } => {
let consts = [
- start.unwrap_or(self.tcx.consts.unit),
- end.unwrap_or(self.tcx.consts.unit),
+ start,
+ end,
ty::Const::from_bool(
self.tcx,
matches!(include_end, rustc_hir::RangeEnd::Included),
diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs
index 18906a6a8ce0f..0fece96a3eb41 100644
--- a/compiler/rustc_trait_selection/src/traits/wf.rs
+++ b/compiler/rustc_trait_selection/src/traits/wf.rs
@@ -738,12 +738,8 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> {
}
}
};
- if let Some(start) = start {
- check(start)
- }
- if let Some(end) = end {
- check(end)
- }
+ check(start);
+ check(end);
}
}
}
diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs
index 0e5a299956154..781b86ae92362 100644
--- a/compiler/rustc_ty_utils/src/layout.rs
+++ b/compiler/rustc_ty_utils/src/layout.rs
@@ -211,21 +211,18 @@ fn layout_of_uncached<'tcx>(
if let BackendRepr::Scalar(scalar) | BackendRepr::ScalarPair(scalar, _) =
&mut layout.backend_repr
{
- if let Some(start) = start {
- scalar.valid_range_mut().start = extract_const_value(cx, ty, start)?
- .try_to_bits(tcx, cx.typing_env)
- .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
- }
- if let Some(end) = end {
- let mut end = extract_const_value(cx, ty, end)?
- .try_to_bits(tcx, cx.typing_env)
- .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
- match include_end {
- rustc_hir::RangeEnd::Included => {}
- rustc_hir::RangeEnd::Excluded => end = end.wrapping_sub(1),
- }
- scalar.valid_range_mut().end = end;
+ scalar.valid_range_mut().start = extract_const_value(cx, ty, start)?
+ .try_to_bits(tcx, cx.typing_env)
+ .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
+
+ let mut end = extract_const_value(cx, ty, end)?
+ .try_to_bits(tcx, cx.typing_env)
+ .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
+ match include_end {
+ rustc_hir::RangeEnd::Included => {}
+ rustc_hir::RangeEnd::Excluded => end = end.wrapping_sub(1),
}
+ scalar.valid_range_mut().end = end;
let niche = Niche {
offset: Size::ZERO,
diff --git a/tests/mir-opt/pattern_types.main.PreCodegen.after.mir b/tests/mir-opt/pattern_types.main.PreCodegen.after.mir
index 5ff90de961506..8214ff61d75e9 100644
--- a/tests/mir-opt/pattern_types.main.PreCodegen.after.mir
+++ b/tests/mir-opt/pattern_types.main.PreCodegen.after.mir
@@ -3,9 +3,9 @@
fn main() -> () {
let mut _0: ();
scope 1 {
- debug x => const 2_u32 is 1..=;
+ debug x => const 2_u32 is 1..=u32::MAX;
scope 2 {
- debug y => const {transmute(0x00000000): (u32) is 1..=};
+ debug y => const {transmute(0x00000000): (u32) is 1..=u32::MAX};
}
}
diff --git a/tests/mir-opt/pattern_types.rs b/tests/mir-opt/pattern_types.rs
index 0369ccf9a9d56..6d53919934528 100644
--- a/tests/mir-opt/pattern_types.rs
+++ b/tests/mir-opt/pattern_types.rs
@@ -5,8 +5,8 @@ use std::pat::pattern_type;
// EMIT_MIR pattern_types.main.PreCodegen.after.mir
fn main() {
- // CHECK: debug x => const 2_u32 is 1..=
+ // CHECK: debug x => const 2_u32 is 1..=u32::MAX
let x: pattern_type!(u32 is 1..) = unsafe { std::mem::transmute(2) };
- // CHECK: debug y => const {transmute(0x00000000): (u32) is 1..=}
+ // CHECK: debug y => const {transmute(0x00000000): (u32) is 1..=u32::MAX}
let y: pattern_type!(u32 is 1..) = unsafe { std::mem::transmute(0) };
}
diff --git a/tests/ui/lint/clashing-extern-fn.stderr b/tests/ui/lint/clashing-extern-fn.stderr
index 118b18b224c05..85ca701a3b64d 100644
--- a/tests/ui/lint/clashing-extern-fn.stderr
+++ b/tests/ui/lint/clashing-extern-fn.stderr
@@ -17,7 +17,7 @@ LL | fn hidden_niche_unsafe_cell() -> Option`, which is not FFI-safe
+warning: `extern` block uses type `Option<(usize) is 0..=usize::MAX>`, which is not FFI-safe
--> $DIR/clashing-extern-fn.rs:502:54
|
LL | fn pt_non_zero_usize_opt_full_range() -> Option;
@@ -276,7 +276,7 @@ LL | fn pt_non_null_ptr() -> pattern_type!(usize is 1..);
LL | fn pt_non_null_ptr() -> *const ();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
|
- = note: expected `unsafe extern "C" fn() -> (usize) is 1..=`
+ = note: expected `unsafe extern "C" fn() -> (usize) is 1..=usize::MAX`
found `unsafe extern "C" fn() -> *const ()`
warning: 24 warnings emitted
diff --git a/tests/ui/type/pattern_types/nested.stderr b/tests/ui/type/pattern_types/nested.stderr
index b753b0a9c9baa..a606fd013c5ea 100644
--- a/tests/ui/type/pattern_types/nested.stderr
+++ b/tests/ui/type/pattern_types/nested.stderr
@@ -1,4 +1,4 @@
-error: `(u32) is 1..=` is not a valid base type for range patterns
+error: `(u32) is 1..=u32::MAX` is not a valid base type for range patterns
--> $DIR/nested.rs:10:34
|
LL | const BAD_NESTING: pattern_type!(pattern_type!(u32 is 1..) is 0..) = todo!();
@@ -10,7 +10,7 @@ note: range patterns only support integers
LL | const BAD_NESTING: pattern_type!(pattern_type!(u32 is 1..) is 0..) = todo!();
| ^^^
-error: `(i32) is 1..=` is not a valid base type for range patterns
+error: `(i32) is 1..=i32::MAX` is not a valid base type for range patterns
--> $DIR/nested.rs:15:35
|
LL | const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = todo!();
@@ -22,7 +22,7 @@ note: range patterns only support integers
LL | const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = todo!();
| ^^^^^
-error: `(i32) is 1..=` is not a valid base type for range patterns
+error: `(i32) is 1..=i32::MAX` is not a valid base type for range patterns
--> $DIR/nested.rs:19:35
|
LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!();
@@ -62,27 +62,27 @@ error[E0308]: mismatched types
--> $DIR/nested.rs:10:63
|
LL | const BAD_NESTING: pattern_type!(pattern_type!(u32 is 1..) is 0..) = todo!();
- | ^ expected `(u32) is 1..=`, found integer
+ | ^ expected `(u32) is 1..=u32::MAX`, found integer
|
- = note: expected pattern type `(u32) is 1..=`
+ = note: expected pattern type `(u32) is 1..=u32::MAX`
found type `{integer}`
error[E0308]: mismatched types
--> $DIR/nested.rs:15:67
|
LL | const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = todo!();
- | ^^ expected `(i32) is 1..=`, found integer
+ | ^^ expected `(i32) is 1..=i32::MAX`, found integer
|
- = note: expected pattern type `(i32) is 1..=`
+ = note: expected pattern type `(i32) is 1..=i32::MAX`
found type `{integer}`
error[E0308]: mismatched types
--> $DIR/nested.rs:19:66
|
LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!();
- | ^ expected `(i32) is 1..=`, found integer
+ | ^ expected `(i32) is 1..=i32::MAX`, found integer
|
- = note: expected pattern type `(i32) is 1..=`
+ = note: expected pattern type `(i32) is 1..=i32::MAX`
found type `{integer}`
error[E0308]: mismatched types
diff --git a/tests/ui/type/pattern_types/range_patterns.stderr b/tests/ui/type/pattern_types/range_patterns.stderr
index 690592ba0b8da..43ae7e870f002 100644
--- a/tests/ui/type/pattern_types/range_patterns.stderr
+++ b/tests/ui/type/pattern_types/range_patterns.stderr
@@ -44,7 +44,7 @@ error: layout_of(NonZero) = Layout {
LL | type X = std::num::NonZeroU32;
| ^^^^^^
-error: layout_of((u32) is 1..=) = Layout {
+error: layout_of((u32) is 1..=u32::MAX) = Layout {
size: Size(4 bytes),
align: AbiAndPrefAlign {
abi: Align(4 bytes),
@@ -83,7 +83,7 @@ error: layout_of((u32) is 1..=) = Layout {
LL | type Y = pattern_type!(u32 is 1..);
| ^^^^^^
-error: layout_of(Option<(u32) is 1..=>) = Layout {
+error: layout_of(Option<(u32) is 1..=u32::MAX>) = Layout {
size: Size(4 bytes),
align: AbiAndPrefAlign {
abi: Align(4 bytes),
diff --git a/tests/ui/type/pattern_types/range_patterns_trait_impls2.stderr b/tests/ui/type/pattern_types/range_patterns_trait_impls2.stderr
index d5c539b6c52e6..565d8f18cdbb6 100644
--- a/tests/ui/type/pattern_types/range_patterns_trait_impls2.stderr
+++ b/tests/ui/type/pattern_types/range_patterns_trait_impls2.stderr
@@ -4,7 +4,7 @@ error[E0117]: only traits defined in the current crate can be implemented for ar
LL | impl Eq for Y {}
| ^^^^^^^^^^^^-
| |
- | `(u32) is 1..=` is not defined in the current crate
+ | `(u32) is 1..=u32::MAX` is not defined in the current crate
|
= note: impl doesn't have any local type before any uncovered type parameters
= note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules
diff --git a/tests/ui/type/pattern_types/range_patterns_unusable.stderr b/tests/ui/type/pattern_types/range_patterns_unusable.stderr
index 8377d417452bb..a9558852eb29e 100644
--- a/tests/ui/type/pattern_types/range_patterns_unusable.stderr
+++ b/tests/ui/type/pattern_types/range_patterns_unusable.stderr
@@ -4,7 +4,7 @@ error[E0512]: cannot transmute between types of different sizes, or dependently-
LL | let _: Option = unsafe { std::mem::transmute(z) };
| ^^^^^^^^^^^^^^^^^^^
|
- = note: source type: `Option<(u32) is 1..=>` (32 bits)
+ = note: source type: `Option<(u32) is 1..=u32::MAX>` (32 bits)
= note: target type: `Option` (64 bits)
error: aborting due to 1 previous error
diff --git a/tests/ui/type/pattern_types/range_patterns_unusable_math.rs b/tests/ui/type/pattern_types/range_patterns_unusable_math.rs
index ece4009e1e7d8..54ad6096c577d 100644
--- a/tests/ui/type/pattern_types/range_patterns_unusable_math.rs
+++ b/tests/ui/type/pattern_types/range_patterns_unusable_math.rs
@@ -11,5 +11,5 @@ type Z = Option;
fn main() {
let x: Y = unsafe { std::mem::transmute(42_u32) };
- let x = x + 1_u32; //~ ERROR cannot add `u32` to `(u32) is 1..=`
+ let x = x + 1_u32; //~ ERROR cannot add `u32` to `(u32) is 1..=u32::MAX`
}
diff --git a/tests/ui/type/pattern_types/range_patterns_unusable_math.stderr b/tests/ui/type/pattern_types/range_patterns_unusable_math.stderr
index 373615e3714e5..68ddd63cd5306 100644
--- a/tests/ui/type/pattern_types/range_patterns_unusable_math.stderr
+++ b/tests/ui/type/pattern_types/range_patterns_unusable_math.stderr
@@ -1,10 +1,10 @@
-error[E0369]: cannot add `u32` to `(u32) is 1..=`
+error[E0369]: cannot add `u32` to `(u32) is 1..=u32::MAX`
--> $DIR/range_patterns_unusable_math.rs:14:15
|
LL | let x = x + 1_u32;
| - ^ ----- u32
| |
- | (u32) is 1..=
+ | (u32) is 1..=u32::MAX
error: aborting due to 1 previous error
From ff7c93ff8ea36ed5f99597ce257374c7cc5dd3e0 Mon Sep 17 00:00:00 2001
From: Oli Scherer
Date: Wed, 5 Feb 2025 15:22:10 +0000
Subject: [PATCH 07/15] Remove the `Option` part of range ends in the HIR
---
compiler/rustc_ast_lowering/src/lib.rs | 4 +-
compiler/rustc_ast_lowering/src/pat.rs | 122 +++++++++-
compiler/rustc_hir/src/hir.rs | 2 +-
compiler/rustc_hir/src/intravisit.rs | 6 +-
compiler/rustc_hir/src/lang_items.rs | 3 +
compiler/rustc_hir_analysis/messages.ftl | 3 -
compiler/rustc_hir_analysis/src/errors.rs | 2 -
.../src/errors/pattern_types.rs | 14 --
.../src/hir_ty_lowering/mod.rs | 90 +-------
.../src/variance/constraints.rs | 2 +-
compiler/rustc_hir_pretty/src/lib.rs | 15 +-
compiler/rustc_lint/src/types.rs | 13 +-
compiler/rustc_middle/src/ty/flags.rs | 2 +-
compiler/rustc_middle/src/ty/pattern.rs | 7 +-
compiler/rustc_middle/src/ty/relate.rs | 15 +-
compiler/rustc_middle/src/ty/walk.rs | 2 +-
.../rustc_smir/src/rustc_internal/internal.rs | 4 +-
.../rustc_smir/src/rustc_smir/convert/ty.rs | 4 +-
compiler/rustc_span/src/hygiene.rs | 3 +
compiler/rustc_span/src/symbol.rs | 4 +
compiler/rustc_symbol_mangling/src/v0.rs | 11 +-
.../rustc_trait_selection/src/traits/wf.rs | 2 +-
compiler/rustc_ty_utils/src/layout.rs | 9 +-
library/core/src/pat.rs | 57 +++++
.../clippy/clippy_utils/src/hir_utils.rs | 11 +-
tests/ui/lint/clashing-extern-fn.stderr | 2 +-
tests/ui/type/pattern_types/nested.rs | 2 +
tests/ui/type/pattern_types/nested.stderr | 214 ++++++++++++------
.../range_patterns_trait_impls2.stderr | 2 +-
tests/ui/type/pattern_types/reverse_range.rs | 13 +-
.../type/pattern_types/reverse_range.stderr | 16 +-
tests/ui/type/pattern_types/validity.rs | 2 +-
32 files changed, 392 insertions(+), 266 deletions(-)
delete mode 100644 compiler/rustc_hir_analysis/src/errors/pattern_types.rs
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 1c69937eed07a..135d56c89d48a 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -136,6 +136,7 @@ struct LoweringContext<'a, 'hir> {
allow_try_trait: Arc<[Symbol]>,
allow_gen_future: Arc<[Symbol]>,
+ allow_pattern_type: Arc<[Symbol]>,
allow_async_iterator: Arc<[Symbol]>,
allow_for_await: Arc<[Symbol]>,
allow_async_fn_traits: Arc<[Symbol]>,
@@ -176,6 +177,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
impl_trait_defs: Vec::new(),
impl_trait_bounds: Vec::new(),
allow_try_trait: [sym::try_trait_v2, sym::yeet_desugar_details].into(),
+ allow_pattern_type: [sym::pattern_types, sym::pattern_type_range_trait].into(),
allow_gen_future: if tcx.features().async_fn_track_caller() {
[sym::gen_future, sym::closure_track_caller].into()
} else {
@@ -1357,7 +1359,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}
}
TyKind::Pat(ty, pat) => {
- hir::TyKind::Pat(self.lower_ty(ty, itctx), self.lower_ty_pat(pat))
+ hir::TyKind::Pat(self.lower_ty(ty, itctx), self.lower_ty_pat(pat, ty.span))
}
TyKind::MacCall(_) => {
span_bug!(t.span, "`TyKind::MacCall` should have been expanded by now")
diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs
index 2dcfe7c745da5..728981dea5f83 100644
--- a/compiler/rustc_ast_lowering/src/pat.rs
+++ b/compiler/rustc_ast_lowering/src/pat.rs
@@ -3,11 +3,11 @@ use std::sync::Arc;
use rustc_ast::ptr::P;
use rustc_ast::*;
use rustc_data_structures::stack::ensure_sufficient_stack;
-use rustc_hir as hir;
-use rustc_hir::def::Res;
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::{self as hir, LangItem};
use rustc_middle::span_bug;
use rustc_span::source_map::{Spanned, respan};
-use rustc_span::{Ident, Span};
+use rustc_span::{DesugaringKind, Ident, Span, kw};
use super::errors::{
ArbitraryExpressionInPattern, ExtraDoubleDot, MisplacedDoubleDot, SubTupleBinding,
@@ -430,22 +430,124 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
self.arena.alloc(hir::PatExpr { hir_id: self.lower_node_id(expr.id), span, kind })
}
- pub(crate) fn lower_ty_pat(&mut self, pattern: &TyPat) -> &'hir hir::TyPat<'hir> {
- self.arena.alloc(self.lower_ty_pat_mut(pattern))
+ pub(crate) fn lower_ty_pat(
+ &mut self,
+ pattern: &TyPat,
+ base_type: Span,
+ ) -> &'hir hir::TyPat<'hir> {
+ self.arena.alloc(self.lower_ty_pat_mut(pattern, base_type))
}
- fn lower_ty_pat_mut(&mut self, pattern: &TyPat) -> hir::TyPat<'hir> {
+ fn lower_ty_pat_mut(&mut self, pattern: &TyPat, base_type: Span) -> hir::TyPat<'hir> {
// loop here to avoid recursion
let pat_hir_id = self.lower_node_id(pattern.id);
let node = match &pattern.kind {
- TyPatKind::Range(e1, e2, Spanned { node: end, .. }) => hir::TyPatKind::Range(
- e1.as_deref().map(|e| self.lower_anon_const_to_const_arg(e)),
- e2.as_deref().map(|e| self.lower_anon_const_to_const_arg(e)),
- self.lower_range_end(end, e2.is_some()),
+ TyPatKind::Range(e1, e2, Spanned { node: end, span }) => hir::TyPatKind::Range(
+ e1.as_deref().map(|e| self.lower_anon_const_to_const_arg(e)).unwrap_or_else(|| {
+ self.lower_ty_pat_range_end(
+ hir::LangItem::RangeMin,
+ span.shrink_to_lo(),
+ base_type,
+ )
+ }),
+ e2.as_deref()
+ .map(|e| match end {
+ RangeEnd::Included(..) => self.lower_anon_const_to_const_arg(e),
+ RangeEnd::Excluded => self.lower_excluded_range_end(e),
+ })
+ .unwrap_or_else(|| {
+ self.lower_ty_pat_range_end(
+ hir::LangItem::RangeMax,
+ span.shrink_to_hi(),
+ base_type,
+ )
+ }),
),
TyPatKind::Err(guar) => hir::TyPatKind::Err(*guar),
};
hir::TyPat { hir_id: pat_hir_id, kind: node, span: self.lower_span(pattern.span) }
}
+
+ /// Lowers the range end of an exclusive range (`2..5`) to an inclusive range 2..=(5 - 1).
+ /// This way the type system doesn't have to handle the distinction between inclusive/exclusive ranges.
+ fn lower_excluded_range_end(&mut self, e: &AnonConst) -> &'hir hir::ConstArg<'hir> {
+ let span = self.lower_span(e.value.span);
+ let unstable_span = self.mark_span_with_reason(
+ DesugaringKind::PatTyRange,
+ span,
+ Some(Arc::clone(&self.allow_pattern_type)),
+ );
+ let anon_const = self.with_new_scopes(span, |this| {
+ let def_id = this.local_def_id(e.id);
+ let hir_id = this.lower_node_id(e.id);
+ let body = this.lower_body(|this| {
+ // Need to use a custom function as we can't just subtract `1` from a `char`.
+ let kind = hir::ExprKind::Path(this.make_lang_item_qpath(
+ hir::LangItem::RangeSub,
+ unstable_span,
+ None,
+ ));
+ let fn_def = this.arena.alloc(hir::Expr { hir_id: this.next_id(), kind, span });
+ let args = this.arena.alloc([this.lower_expr_mut(&e.value)]);
+ (
+ &[],
+ hir::Expr {
+ hir_id: this.next_id(),
+ kind: hir::ExprKind::Call(fn_def, args),
+ span,
+ },
+ )
+ });
+ hir::AnonConst { def_id, hir_id, body, span }
+ });
+ self.arena.alloc(hir::ConstArg {
+ hir_id: self.next_id(),
+ kind: hir::ConstArgKind::Anon(self.arena.alloc(anon_const)),
+ })
+ }
+
+ /// When a range has no end specified (`1..` or `1..=`) or no start specified (`..5` or `..=5`),
+ /// we instead use a constant of the MAX/MIN of the type.
+ /// This way the type system does not have to handle the lack of a start/end.
+ fn lower_ty_pat_range_end(
+ &mut self,
+ lang_item: LangItem,
+ span: Span,
+ base_type: Span,
+ ) -> &'hir hir::ConstArg<'hir> {
+ let parent_def_id = self.current_hir_id_owner.def_id;
+ let node_id = self.next_node_id();
+
+ // Add a definition for the in-band const def.
+ // We're generating a range end that didn't exist in the AST,
+ // so the def collector didn't create the def ahead of time. That's why we have to do
+ // it here.
+ let def_id = self.create_def(parent_def_id, node_id, kw::Empty, DefKind::AnonConst, span);
+ let hir_id = self.lower_node_id(node_id);
+
+ let unstable_span = self.mark_span_with_reason(
+ DesugaringKind::PatTyRange,
+ self.lower_span(span),
+ Some(Arc::clone(&self.allow_pattern_type)),
+ );
+ let span = self.lower_span(base_type);
+
+ let path_expr = hir::Expr {
+ hir_id: self.next_id(),
+ kind: hir::ExprKind::Path(self.make_lang_item_qpath(lang_item, unstable_span, None)),
+ span,
+ };
+
+ let ct = self.with_new_scopes(span, |this| {
+ self.arena.alloc(hir::AnonConst {
+ def_id,
+ hir_id,
+ body: this.lower_body(|_this| (&[], path_expr)),
+ span,
+ })
+ });
+ let hir_id = self.next_id();
+ self.arena.alloc(hir::ConstArg { kind: hir::ConstArgKind::Anon(ct), hir_id })
+ }
}
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 63b1ae5c88651..ba96fd87973a1 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -1668,7 +1668,7 @@ pub enum PatExprKind<'hir> {
#[derive(Debug, Clone, Copy, HashStable_Generic)]
pub enum TyPatKind<'hir> {
/// A range pattern (e.g., `1..=2` or `1..2`).
- Range(Option<&'hir ConstArg<'hir>>, Option<&'hir ConstArg<'hir>>, RangeEnd),
+ Range(&'hir ConstArg<'hir>, &'hir ConstArg<'hir>),
/// A placeholder for a pattern that wasn't well formed in some way.
Err(ErrorGuaranteed),
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index d5fa7ec366b28..db08538b90a83 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -708,9 +708,9 @@ pub fn walk_arm<'v, V: Visitor<'v>>(visitor: &mut V, arm: &'v Arm<'v>) -> V::Res
pub fn walk_ty_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v TyPat<'v>) -> V::Result {
try_visit!(visitor.visit_id(pattern.hir_id));
match pattern.kind {
- TyPatKind::Range(lower_bound, upper_bound, _) => {
- visit_opt!(visitor, visit_const_arg_unambig, lower_bound);
- visit_opt!(visitor, visit_const_arg_unambig, upper_bound);
+ TyPatKind::Range(lower_bound, upper_bound) => {
+ try_visit!(visitor.visit_const_arg_unambig(lower_bound));
+ try_visit!(visitor.visit_const_arg_unambig(upper_bound));
}
TyPatKind::Err(_) => (),
}
diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs
index f5626937ec456..c3b14a4e06c79 100644
--- a/compiler/rustc_hir/src/lang_items.rs
+++ b/compiler/rustc_hir/src/lang_items.rs
@@ -418,6 +418,9 @@ language_item_table! {
Range, sym::Range, range_struct, Target::Struct, GenericRequirement::None;
RangeToInclusive, sym::RangeToInclusive, range_to_inclusive_struct, Target::Struct, GenericRequirement::None;
RangeTo, sym::RangeTo, range_to_struct, Target::Struct, GenericRequirement::None;
+ RangeMax, sym::RangeMax, range_max, Target::AssocConst, GenericRequirement::Exact(0);
+ RangeMin, sym::RangeMin, range_min, Target::AssocConst, GenericRequirement::Exact(0);
+ RangeSub, sym::RangeSub, range_sub, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::Exact(0);
// `new_range` types that are `Copy + IntoIterator`
RangeFromCopy, sym::RangeFromCopy, range_from_copy_struct, Target::Struct, GenericRequirement::None;
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index 3f75cce009225..d16aba197fe59 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -244,9 +244,6 @@ hir_analysis_inherent_ty_outside_relevant = cannot define inherent `impl` for a
.help = consider moving this inherent impl into the crate defining the type if possible
.span_help = alternatively add `#[rustc_allow_incoherent_impl]` to the relevant impl items
-hir_analysis_invalid_base_type = `{$ty}` is not a valid base type for range patterns
- .note = range patterns only support integers
-
hir_analysis_invalid_generic_receiver_ty = invalid generic `self` parameter type: `{$receiver_ty}`
.note = type of `self` must not be a method generic parameter type
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index 852533ff5c954..f3e1e89812ca7 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -11,8 +11,6 @@ use rustc_middle::ty::Ty;
use rustc_span::{Ident, Span, Symbol};
use crate::fluent_generated as fluent;
-mod pattern_types;
-pub(crate) use pattern_types::*;
pub(crate) mod wrong_number_of_generic_args;
mod precise_captures;
diff --git a/compiler/rustc_hir_analysis/src/errors/pattern_types.rs b/compiler/rustc_hir_analysis/src/errors/pattern_types.rs
deleted file mode 100644
index ec7b3aaa1c14c..0000000000000
--- a/compiler/rustc_hir_analysis/src/errors/pattern_types.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-use rustc_macros::Diagnostic;
-use rustc_middle::ty::Ty;
-use rustc_span::Span;
-
-#[derive(Diagnostic)]
-#[diag(hir_analysis_invalid_base_type)]
-pub(crate) struct InvalidBaseType<'tcx> {
- pub ty: Ty<'tcx>,
- #[primary_span]
- pub ty_span: Span,
- pub pat: &'static str,
- #[note]
- pub pat_span: Span,
-}
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 efafac19defe0..5daf7f18cfd96 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
@@ -21,9 +21,8 @@ pub mod generics;
mod lint;
use std::assert_matches::assert_matches;
-use std::{char, slice};
+use std::slice;
-use rustc_abi::Size;
use rustc_ast::TraitObjectSyntax;
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
use rustc_errors::codes::*;
@@ -32,7 +31,7 @@ use rustc_errors::{
};
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Namespace, Res};
use rustc_hir::def_id::{DefId, LocalDefId};
-use rustc_hir::{self as hir, AnonConst, ConstArg, GenericArg, GenericArgs, HirId};
+use rustc_hir::{self as hir, AnonConst, GenericArg, GenericArgs, HirId};
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
use rustc_infer::traits::ObligationCause;
use rustc_middle::middle::stability::AllowUnstable;
@@ -54,9 +53,7 @@ use rustc_type_ir::Upcast;
use tracing::{debug, instrument};
use crate::check::check_abi_fn_ptr;
-use crate::errors::{
- AmbiguousLifetimeBound, BadReturnTypeNotation, InvalidBaseType, NoVariantNamed,
-};
+use crate::errors::{AmbiguousLifetimeBound, BadReturnTypeNotation, NoVariantNamed};
use crate::hir_ty_lowering::errors::{GenericsArgsErrExtend, prohibit_assoc_item_constraint};
use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args};
use crate::middle::resolve_bound_vars as rbv;
@@ -2453,25 +2450,26 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let ty_span = ty.span;
let ty = self.lower_ty(ty);
let pat_ty = match pat.kind {
- hir::TyPatKind::Range(start, end, include_end) => {
+ hir::TyPatKind::Range(start, end) => {
let (ty, start, end) = match ty.kind() {
+ // Keep this list of types in sync with the list of types that
+ // the `RangePattern` trait is implemented for.
ty::Int(_) | ty::Uint(_) | ty::Char => {
- let (start, end) = self.lower_ty_pat_range(ty, start, end);
+ let start = self.lower_const_arg(start, FeedConstTy::No);
+ let end = self.lower_const_arg(end, FeedConstTy::No);
(ty, start, end)
}
_ => {
- let guar = self.dcx().emit_err(InvalidBaseType {
- ty,
- pat: "range",
+ let guar = self.dcx().span_delayed_bug(
ty_span,
- pat_span: pat.span,
- });
+ "invalid base type for range pattern",
+ );
let errc = ty::Const::new_error(tcx, guar);
(Ty::new_error(tcx, guar), errc, errc)
}
};
- let pat = tcx.mk_pat(ty::PatternKind::Range { start, end, include_end });
+ let pat = tcx.mk_pat(ty::PatternKind::Range { start, end });
Ty::new_pat(tcx, ty, pat)
}
hir::TyPatKind::Err(e) => Ty::new_error(tcx, e),
@@ -2486,70 +2484,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
result_ty
}
- fn lower_ty_pat_range(
- &self,
- base: Ty<'tcx>,
- start: Option<&ConstArg<'tcx>>,
- end: Option<&ConstArg<'tcx>>,
- ) -> (ty::Const<'tcx>, ty::Const<'tcx>) {
- let tcx = self.tcx();
- let size = match base.kind() {
- ty::Int(i) => {
- i.bit_width().map_or(tcx.data_layout.pointer_size, |bits| Size::from_bits(bits))
- }
- ty::Uint(ui) => {
- ui.bit_width().map_or(tcx.data_layout.pointer_size, |bits| Size::from_bits(bits))
- }
- ty::Char => Size::from_bytes(4),
- _ => unreachable!(),
- };
- let start =
- start.map(|expr| self.lower_const_arg(expr, FeedConstTy::No)).unwrap_or_else(|| {
- match base.kind() {
- ty::Char | ty::Uint(_) => ty::Const::new_value(
- tcx,
- ty::ValTree::from_scalar_int(ty::ScalarInt::null(size)),
- base,
- ),
- ty::Int(_) => ty::Const::new_value(
- tcx,
- ty::ValTree::from_scalar_int(
- ty::ScalarInt::truncate_from_int(size.signed_int_min(), size).0,
- ),
- base,
- ),
- _ => unreachable!(),
- }
- });
- let end = end.map(|expr| self.lower_const_arg(expr, FeedConstTy::No)).unwrap_or_else(
- || match base.kind() {
- ty::Char => ty::Const::new_value(
- tcx,
- ty::ValTree::from_scalar_int(
- ty::ScalarInt::truncate_from_uint(char::MAX, size).0,
- ),
- base,
- ),
- ty::Uint(_) => ty::Const::new_value(
- tcx,
- ty::ValTree::from_scalar_int(
- ty::ScalarInt::truncate_from_uint(size.unsigned_int_max(), size).0,
- ),
- base,
- ),
- ty::Int(_) => ty::Const::new_value(
- tcx,
- ty::ValTree::from_scalar_int(
- ty::ScalarInt::truncate_from_int(size.signed_int_max(), size).0,
- ),
- base,
- ),
- _ => unreachable!(),
- },
- );
- (start, end)
- }
-
/// Lower an opaque type (i.e., an existential impl-Trait type) from the HIR.
#[instrument(level = "debug", skip(self), ret)]
fn lower_opaque_ty(&self, def_id: LocalDefId, in_trait: bool) -> Ty<'tcx> {
diff --git a/compiler/rustc_hir_analysis/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs
index 4b336769cfb80..8475903c68fde 100644
--- a/compiler/rustc_hir_analysis/src/variance/constraints.rs
+++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs
@@ -252,7 +252,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
ty::Pat(typ, pat) => {
match *pat {
- ty::PatternKind::Range { start, end, include_end: _ } => {
+ ty::PatternKind::Range { start, end } => {
self.add_constraints_from_const(current, start, variance);
self.add_constraints_from_const(current, end, variance);
}
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index 5c7426d76b31c..bc8e4ffb69820 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -1869,17 +1869,10 @@ impl<'a> State<'a> {
// Pat isn't normalized, but the beauty of it
// is that it doesn't matter
match pat.kind {
- TyPatKind::Range(begin, end, end_kind) => {
- if let Some(expr) = begin {
- self.print_const_arg(expr);
- }
- match end_kind {
- RangeEnd::Included => self.word("..."),
- RangeEnd::Excluded => self.word(".."),
- }
- if let Some(expr) = end {
- self.print_const_arg(expr);
- }
+ TyPatKind::Range(begin, end) => {
+ self.print_const_arg(begin);
+ self.word("..=");
+ self.print_const_arg(end);
}
TyPatKind::Err(_) => {
self.popen();
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index 73fe8f1057885..14710e5545202 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -5,7 +5,7 @@ use rustc_abi::{BackendRepr, ExternAbi, TagEncoding, VariantIdx, Variants, Wrapp
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::DiagMessage;
use rustc_hir::intravisit::VisitorExt;
-use rustc_hir::{AmbigArg, Expr, ExprKind, HirId, LangItem, RangeEnd};
+use rustc_hir::{AmbigArg, Expr, ExprKind, HirId, LangItem};
use rustc_middle::bug;
use rustc_middle::ty::layout::{LayoutOf, SizeSkeleton};
use rustc_middle::ty::{
@@ -882,16 +882,13 @@ fn ty_is_known_nonnull<'tcx>(
|| Option::unwrap_or_default(
try {
match **pat {
- ty::PatternKind::Range { start, end, include_end } => {
+ ty::PatternKind::Range { start, end } => {
let start = start.try_to_value()?.try_to_bits(tcx, typing_env)?;
let end = end.try_to_value()?.try_to_bits(tcx, typing_env)?;
- match include_end {
- // This also works for negative numbers, as we just need
- // to ensure we aren't wrapping over zero.
- RangeEnd::Included => start > 0 && end >= start,
- RangeEnd::Excluded => start > 0 && end > start,
- }
+ // This also works for negative numbers, as we just need
+ // to ensure we aren't wrapping over zero.
+ start > 0 && end >= start
}
}
},
diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs
index c08d78e25b8d1..0b8f0e8cd41d9 100644
--- a/compiler/rustc_middle/src/ty/flags.rs
+++ b/compiler/rustc_middle/src/ty/flags.rs
@@ -220,7 +220,7 @@ impl FlagComputation {
&ty::Pat(ty, pat) => {
self.add_ty(ty);
match *pat {
- ty::PatternKind::Range { start, end, include_end: _ } => {
+ ty::PatternKind::Range { start, end } => {
self.add_const(start);
self.add_const(end);
}
diff --git a/compiler/rustc_middle/src/ty/pattern.rs b/compiler/rustc_middle/src/ty/pattern.rs
index a1dfd2e8d34f0..89dadb36ad649 100644
--- a/compiler/rustc_middle/src/ty/pattern.rs
+++ b/compiler/rustc_middle/src/ty/pattern.rs
@@ -1,7 +1,6 @@
use std::fmt;
use rustc_data_structures::intern::Interned;
-use rustc_hir::RangeEnd;
use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
use crate::ty;
@@ -27,8 +26,8 @@ impl<'tcx> fmt::Debug for Pattern<'tcx> {
impl<'tcx> fmt::Debug for PatternKind<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
- PatternKind::Range { start, end, include_end } => {
- write!(f, "{start}{include_end}{end}")
+ PatternKind::Range { start, end } => {
+ write!(f, "{start}..={end}")
}
}
}
@@ -37,5 +36,5 @@ impl<'tcx> fmt::Debug for PatternKind<'tcx> {
#[derive(Clone, PartialEq, Eq, Hash)]
#[derive(HashStable, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable)]
pub enum PatternKind<'tcx> {
- Range { start: ty::Const<'tcx>, end: ty::Const<'tcx>, include_end: RangeEnd },
+ Range { start: ty::Const<'tcx>, end: ty::Const<'tcx> },
}
diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs
index 6d5cc42065f22..b1dfcb80bde57 100644
--- a/compiler/rustc_middle/src/ty/relate.rs
+++ b/compiler/rustc_middle/src/ty/relate.rs
@@ -51,21 +51,12 @@ impl<'tcx> Relate> for ty::Pattern<'tcx> {
) -> RelateResult<'tcx, Self> {
match (&*a, &*b) {
(
- &ty::PatternKind::Range { start: start_a, end: end_a, include_end: inc_a },
- &ty::PatternKind::Range { start: start_b, end: end_b, include_end: inc_b },
+ &ty::PatternKind::Range { start: start_a, end: end_a },
+ &ty::PatternKind::Range { start: start_b, end: end_b },
) => {
let start = relation.relate(start_a, start_b)?;
- // FIXME(pattern_types): make equal patterns equal (`0..5` is the same as `0..=6`).
let end = relation.relate(end_a, end_b)?;
- if inc_a == inc_b {
- Ok(relation.cx().mk_pat(ty::PatternKind::Range {
- start,
- end,
- include_end: inc_a,
- }))
- } else {
- Err(TypeError::Mismatch)
- }
+ Ok(relation.cx().mk_pat(ty::PatternKind::Range { start, end }))
}
}
}
diff --git a/compiler/rustc_middle/src/ty/walk.rs b/compiler/rustc_middle/src/ty/walk.rs
index b31b7cac41ef2..a23316ae6fc88 100644
--- a/compiler/rustc_middle/src/ty/walk.rs
+++ b/compiler/rustc_middle/src/ty/walk.rs
@@ -137,7 +137,7 @@ fn push_inner<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent: GenericArg<'tcx>)
ty::Pat(ty, pat) => {
match *pat {
- ty::PatternKind::Range { start, end, include_end: _ } => {
+ ty::PatternKind::Range { start, end } => {
stack.push(end.into());
stack.push(start.into());
}
diff --git a/compiler/rustc_smir/src/rustc_internal/internal.rs b/compiler/rustc_smir/src/rustc_internal/internal.rs
index a8c4cd660fd98..bb2b2dea2f37f 100644
--- a/compiler/rustc_smir/src/rustc_internal/internal.rs
+++ b/compiler/rustc_smir/src/rustc_internal/internal.rs
@@ -5,7 +5,6 @@
// Prefer importing stable_mir over internal rustc constructs to make this file more readable.
-use rustc_hir::RangeEnd;
use rustc_middle::ty::{self as rustc_ty, Const as InternalConst, Ty as InternalTy, TyCtxt};
use rustc_span::Symbol;
use stable_mir::abi::Layout;
@@ -89,10 +88,9 @@ impl RustcInternal for Pattern {
type T<'tcx> = rustc_ty::Pattern<'tcx>;
fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
tcx.mk_pat(match self {
- Pattern::Range { start, end, include_end } => rustc_ty::PatternKind::Range {
+ Pattern::Range { start, end, include_end: _ } => rustc_ty::PatternKind::Range {
start: start.as_ref().unwrap().internal(tables, tcx),
end: end.as_ref().unwrap().internal(tables, tcx),
- include_end: if *include_end { RangeEnd::Included } else { RangeEnd::Excluded },
},
})
}
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
index f6a6a3c1737fb..aa0eac628dd0f 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
@@ -405,11 +405,11 @@ impl<'tcx> Stable<'tcx> for ty::Pattern<'tcx> {
fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
match **self {
- ty::PatternKind::Range { start, end, include_end } => stable_mir::ty::Pattern::Range {
+ ty::PatternKind::Range { start, end } => stable_mir::ty::Pattern::Range {
// FIXME(SMIR): update data structures to not have an Option here anymore
start: Some(start.stable(tables)),
end: Some(end.stable(tables)),
- include_end: matches!(include_end, rustc_hir::RangeEnd::Included),
+ include_end: true,
},
}
}
diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs
index 84e89ff4b7dab..64982b858c644 100644
--- a/compiler/rustc_span/src/hygiene.rs
+++ b/compiler/rustc_span/src/hygiene.rs
@@ -1173,6 +1173,8 @@ pub enum DesugaringKind {
BoundModifier,
/// Calls to contract checks (`#[requires]` to precond, `#[ensures]` to postcond)
Contract,
+ /// A pattern type range start/end
+ PatTyRange,
}
impl DesugaringKind {
@@ -1190,6 +1192,7 @@ impl DesugaringKind {
DesugaringKind::WhileLoop => "`while` loop",
DesugaringKind::BoundModifier => "trait bound modifier",
DesugaringKind::Contract => "contract check",
+ DesugaringKind::PatTyRange => "pattern type",
}
}
}
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 501c9039f2de9..43ebd41571772 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -308,6 +308,9 @@ symbols! {
RangeFull,
RangeInclusive,
RangeInclusiveCopy,
+ RangeMax,
+ RangeMin,
+ RangeSub,
RangeTo,
RangeToInclusive,
Rc,
@@ -1521,6 +1524,7 @@ symbols! {
pattern_complexity_limit,
pattern_parentheses,
pattern_type,
+ pattern_type_range_trait,
pattern_types,
permissions_from_mode,
phantom_data,
diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs
index 754c74c4f009f..7756a4f1c3a15 100644
--- a/compiler/rustc_symbol_mangling/src/v0.rs
+++ b/compiler/rustc_symbol_mangling/src/v0.rs
@@ -413,15 +413,8 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
}
ty::Pat(ty, pat) => match *pat {
- ty::PatternKind::Range { start, end, include_end } => {
- let consts = [
- start,
- end,
- ty::Const::from_bool(
- self.tcx,
- matches!(include_end, rustc_hir::RangeEnd::Included),
- ),
- ];
+ ty::PatternKind::Range { start, end } => {
+ let consts = [start, end];
// HACK: Represent as tuple until we have something better.
// HACK: constants are used in arrays, even if the types don't match.
self.push("T");
diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs
index 0fece96a3eb41..54b6c22b2d821 100644
--- a/compiler/rustc_trait_selection/src/traits/wf.rs
+++ b/compiler/rustc_trait_selection/src/traits/wf.rs
@@ -708,7 +708,7 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> {
ty::Pat(subty, pat) => {
self.require_sized(subty, ObligationCauseCode::Misc);
match *pat {
- ty::PatternKind::Range { start, end, include_end: _ } => {
+ ty::PatternKind::Range { start, end } => {
let mut check = |c| {
let cause = self.cause(ObligationCauseCode::Misc);
self.out.push(traits::Obligation::with_depth(
diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs
index 781b86ae92362..55af8ceeab920 100644
--- a/compiler/rustc_ty_utils/src/layout.rs
+++ b/compiler/rustc_ty_utils/src/layout.rs
@@ -207,7 +207,7 @@ fn layout_of_uncached<'tcx>(
let layout = cx.layout_of(ty)?.layout;
let mut layout = LayoutData::clone(&layout.0);
match *pat {
- ty::PatternKind::Range { start, end, include_end } => {
+ ty::PatternKind::Range { start, end } => {
if let BackendRepr::Scalar(scalar) | BackendRepr::ScalarPair(scalar, _) =
&mut layout.backend_repr
{
@@ -215,14 +215,9 @@ fn layout_of_uncached<'tcx>(
.try_to_bits(tcx, cx.typing_env)
.ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
- let mut end = extract_const_value(cx, ty, end)?
+ scalar.valid_range_mut().end = extract_const_value(cx, ty, end)?
.try_to_bits(tcx, cx.typing_env)
.ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
- match include_end {
- rustc_hir::RangeEnd::Included => {}
- rustc_hir::RangeEnd::Excluded => end = end.wrapping_sub(1),
- }
- scalar.valid_range_mut().end = end;
let niche = Niche {
offset: Size::ZERO,
diff --git a/library/core/src/pat.rs b/library/core/src/pat.rs
index 752e79c2dacee..5122d55f5163a 100644
--- a/library/core/src/pat.rs
+++ b/library/core/src/pat.rs
@@ -12,3 +12,60 @@ macro_rules! pattern_type {
/* compiler built-in */
};
}
+
+/// A trait implemented for integer types and `char`.
+/// Useful in the future for generic pattern types, but
+/// used right now to simplify ast lowering of pattern type ranges.
+#[unstable(feature = "pattern_type_range_trait", issue = "123646")]
+#[rustc_const_unstable(feature = "pattern_type_range_trait", issue = "123646")]
+#[const_trait]
+#[diagnostic::on_unimplemented(
+ message = "`{Self}` is not a valid base type for range patterns",
+ label = "only integer types and `char` are supported"
+)]
+pub trait RangePattern {
+ /// Trait version of the inherent `MIN` assoc const.
+ #[cfg_attr(not(bootstrap), lang = "RangeMin")]
+ const MIN: Self;
+
+ /// Trait version of the inherent `MIN` assoc const.
+ #[cfg_attr(not(bootstrap), lang = "RangeMax")]
+ const MAX: Self;
+
+ /// A compile-time helper to subtract 1 for exclusive ranges.
+ #[cfg_attr(not(bootstrap), lang = "RangeSub")]
+ #[track_caller]
+ fn sub_one(self) -> Self;
+}
+
+macro_rules! impl_range_pat {
+ ($($ty:ty,)*) => {
+ $(
+ impl const RangePattern for $ty {
+ const MIN: $ty = <$ty>::MIN;
+ const MAX: $ty = <$ty>::MAX;
+ fn sub_one(self) -> Self {
+ self - 1
+ }
+ }
+ )*
+ }
+}
+
+impl_range_pat! {
+ i8, i16, i32, i64, i128, isize,
+ u8, u16, u32, u64, u128, usize,
+}
+
+impl const RangePattern for char {
+ const MIN: Self = char::MIN;
+
+ const MAX: Self = char::MAX;
+
+ fn sub_one(self) -> Self {
+ match char::from_u32(self as u32 - 1) {
+ None => panic!("exclusive range to start of valid chars"),
+ Some(val) => val,
+ }
+ }
+}
diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs
index 0ac675345ae0c..cde201196c57a 100644
--- a/src/tools/clippy/clippy_utils/src/hir_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs
@@ -1108,14 +1108,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
pub fn hash_ty_pat(&mut self, pat: &TyPat<'_>) {
std::mem::discriminant(&pat.kind).hash(&mut self.s);
match pat.kind {
- TyPatKind::Range(s, e, i) => {
- if let Some(s) = s {
- self.hash_const_arg(s);
- }
- if let Some(e) = e {
- self.hash_const_arg(e);
- }
- std::mem::discriminant(&i).hash(&mut self.s);
+ TyPatKind::Range(s, e) => {
+ self.hash_const_arg(s);
+ self.hash_const_arg(e);
},
TyPatKind::Err(_) => {},
}
diff --git a/tests/ui/lint/clashing-extern-fn.stderr b/tests/ui/lint/clashing-extern-fn.stderr
index 85ca701a3b64d..4e9f6ea3f78c7 100644
--- a/tests/ui/lint/clashing-extern-fn.stderr
+++ b/tests/ui/lint/clashing-extern-fn.stderr
@@ -276,7 +276,7 @@ LL | fn pt_non_null_ptr() -> pattern_type!(usize is 1..);
LL | fn pt_non_null_ptr() -> *const ();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
|
- = note: expected `unsafe extern "C" fn() -> (usize) is 1..=usize::MAX`
+ = note: expected `unsafe extern "C" fn() -> (usize) is 1..=`
found `unsafe extern "C" fn() -> *const ()`
warning: 24 warnings emitted
diff --git a/tests/ui/type/pattern_types/nested.rs b/tests/ui/type/pattern_types/nested.rs
index 9ca9c7923dedd..0d8cd22190e55 100644
--- a/tests/ui/type/pattern_types/nested.rs
+++ b/tests/ui/type/pattern_types/nested.rs
@@ -18,10 +18,12 @@ const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = todo!();
const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!();
//~^ ERROR: not a valid base type for range patterns
+//~| ERROR: not a valid base type for range patterns
//~| ERROR: mismatched types
const BAD_NESTING4: pattern_type!(() is ..0) = todo!();
//~^ ERROR: not a valid base type for range patterns
+//~| ERROR: not a valid base type for range patterns
//~| ERROR: mismatched types
const BAD_NESTING5: pattern_type!(f32 is 1.0 .. 2.0) = todo!();
diff --git a/tests/ui/type/pattern_types/nested.stderr b/tests/ui/type/pattern_types/nested.stderr
index a606fd013c5ea..04aacabc136e7 100644
--- a/tests/ui/type/pattern_types/nested.stderr
+++ b/tests/ui/type/pattern_types/nested.stderr
@@ -1,96 +1,184 @@
-error: `(u32) is 1..=u32::MAX` is not a valid base type for range patterns
- --> $DIR/nested.rs:10:34
+error[E0308]: mismatched types
+ --> $DIR/nested.rs:10:63
|
LL | const BAD_NESTING: pattern_type!(pattern_type!(u32 is 1..) is 0..) = todo!();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^ expected `(u32) is 1..=u32::MAX`, found integer
|
-note: range patterns only support integers
- --> $DIR/nested.rs:10:63
+ = note: expected pattern type `(u32) is 1..=u32::MAX`
+ found type `{integer}`
+
+error[E0277]: `(u32) is 1..=u32::MAX` is not a valid base type for range patterns
+ --> $DIR/nested.rs:10:34
|
LL | const BAD_NESTING: pattern_type!(pattern_type!(u32 is 1..) is 0..) = todo!();
- | ^^^
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ only integer types and `char` are supported
+ |
+ = help: the trait `core::pat::RangePattern` is not implemented for `(u32) is 1..=u32::MAX`
+ = help: the following other types implement trait `core::pat::RangePattern`:
+ char
+ i128
+ i16
+ i32
+ i64
+ i8
+ isize
+ u128
+ and 5 others
-error: `(i32) is 1..=i32::MAX` is not a valid base type for range patterns
+error[E0277]: `(i32) is 1..=i32::MAX` is not a valid base type for range patterns
--> $DIR/nested.rs:15:35
|
LL | const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = todo!();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^
- |
-note: range patterns only support integers
- --> $DIR/nested.rs:15:64
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ only integer types and `char` are supported
+ |
+ = help: the trait `core::pat::RangePattern` is not implemented for `(i32) is 1..=i32::MAX`
+ = help: the following other types implement trait `core::pat::RangePattern`:
+ char
+ i128
+ i16
+ i32
+ i64
+ i8
+ isize
+ u128
+ and 5 others
+
+error[E0308]: mismatched types
+ --> $DIR/nested.rs:15:67
|
LL | const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = todo!();
- | ^^^^^
+ | ^^ expected `(i32) is 1..=i32::MAX`, found integer
+ |
+ = note: expected pattern type `(i32) is 1..=i32::MAX`
+ found type `{integer}`
-error: `(i32) is 1..=i32::MAX` is not a valid base type for range patterns
+error[E0277]: `(i32) is 1..=i32::MAX` is not a valid base type for range patterns
--> $DIR/nested.rs:19:35
|
LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^
- |
-note: range patterns only support integers
- --> $DIR/nested.rs:19:64
- |
-LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!();
- | ^^^
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ only integer types and `char` are supported
+ |
+ = help: the trait `core::pat::RangePattern` is not implemented for `(i32) is 1..=i32::MAX`
+ = help: the following other types implement trait `core::pat::RangePattern`:
+ char
+ i128
+ i16
+ i32
+ i64
+ i8
+ isize
+ u128
+ and 5 others
-error: `()` is not a valid base type for range patterns
- --> $DIR/nested.rs:23:35
+error[E0308]: mismatched types
+ --> $DIR/nested.rs:19:66
|
-LL | const BAD_NESTING4: pattern_type!(() is ..0) = todo!();
- | ^^
+LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!();
+ | ^
+ | |
+ | expected `(i32) is 1..=i32::MAX`, found integer
+ | arguments to this function are incorrect
|
-note: range patterns only support integers
- --> $DIR/nested.rs:23:41
+ = note: expected pattern type `(i32) is 1..=i32::MAX`
+ found type `{integer}`
+help: the return type of this call is `{integer}` due to the type of the argument passed
+ --> $DIR/nested.rs:19:66
|
-LL | const BAD_NESTING4: pattern_type!(() is ..0) = todo!();
- | ^^^
+LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!();
+ | ^ this argument influences the return type of `RangeSub`
+note: method defined here
+ --> $SRC_DIR/core/src/pat.rs:LL:COL
-error: `f32` is not a valid base type for range patterns
- --> $DIR/nested.rs:27:35
- |
-LL | const BAD_NESTING5: pattern_type!(f32 is 1.0 .. 2.0) = todo!();
- | ^^^
- |
-note: range patterns only support integers
- --> $DIR/nested.rs:27:42
+error[E0277]: `(i32) is 1..=i32::MAX` is not a valid base type for range patterns
+ --> $DIR/nested.rs:19:66
|
-LL | const BAD_NESTING5: pattern_type!(f32 is 1.0 .. 2.0) = todo!();
- | ^^^^^^^^^^
+LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!();
+ | ^ only integer types and `char` are supported
+ |
+ = help: the trait `core::pat::RangePattern` is not implemented for `(i32) is 1..=i32::MAX`
+ = help: the following other types implement trait `core::pat::RangePattern`:
+ char
+ i128
+ i16
+ i32
+ i64
+ i8
+ isize
+ u128
+ and 5 others
-error[E0308]: mismatched types
- --> $DIR/nested.rs:10:63
+error[E0277]: `()` is not a valid base type for range patterns
+ --> $DIR/nested.rs:24:35
|
-LL | const BAD_NESTING: pattern_type!(pattern_type!(u32 is 1..) is 0..) = todo!();
- | ^ expected `(u32) is 1..=u32::MAX`, found integer
- |
- = note: expected pattern type `(u32) is 1..=u32::MAX`
- found type `{integer}`
+LL | const BAD_NESTING4: pattern_type!(() is ..0) = todo!();
+ | ^^ only integer types and `char` are supported
+ |
+ = help: the trait `core::pat::RangePattern` is not implemented for `()`
+ = help: the following other types implement trait `core::pat::RangePattern`:
+ char
+ i128
+ i16
+ i32
+ i64
+ i8
+ isize
+ u128
+ and 5 others
error[E0308]: mismatched types
- --> $DIR/nested.rs:15:67
- |
-LL | const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = todo!();
- | ^^ expected `(i32) is 1..=i32::MAX`, found integer
+ --> $DIR/nested.rs:24:43
|
- = note: expected pattern type `(i32) is 1..=i32::MAX`
- found type `{integer}`
-
-error[E0308]: mismatched types
- --> $DIR/nested.rs:19:66
+LL | const BAD_NESTING4: pattern_type!(() is ..0) = todo!();
+ | ^
+ | |
+ | expected `()`, found integer
+ | arguments to this function are incorrect
|
-LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!();
- | ^ expected `(i32) is 1..=i32::MAX`, found integer
+help: the return type of this call is `{integer}` due to the type of the argument passed
+ --> $DIR/nested.rs:24:43
|
- = note: expected pattern type `(i32) is 1..=i32::MAX`
- found type `{integer}`
+LL | const BAD_NESTING4: pattern_type!(() is ..0) = todo!();
+ | ^ this argument influences the return type of `RangeSub`
+note: method defined here
+ --> $SRC_DIR/core/src/pat.rs:LL:COL
-error[E0308]: mismatched types
- --> $DIR/nested.rs:23:43
+error[E0277]: `()` is not a valid base type for range patterns
+ --> $DIR/nested.rs:24:43
|
LL | const BAD_NESTING4: pattern_type!(() is ..0) = todo!();
- | ^ expected `()`, found integer
+ | ^ only integer types and `char` are supported
+ |
+ = help: the trait `core::pat::RangePattern` is not implemented for `()`
+ = help: the following other types implement trait `core::pat::RangePattern`:
+ char
+ i128
+ i16
+ i32
+ i64
+ i8
+ isize
+ u128
+ and 5 others
+
+error[E0277]: `f32` is not a valid base type for range patterns
+ --> $DIR/nested.rs:29:49
+ |
+LL | const BAD_NESTING5: pattern_type!(f32 is 1.0 .. 2.0) = todo!();
+ | ^^^ only integer types and `char` are supported
+ |
+ = help: the trait `core::pat::RangePattern` is not implemented for `f32`
+ = help: the following other types implement trait `core::pat::RangePattern`:
+ i128
+ i16
+ i32
+ i64
+ i8
+ isize
+ u128
+ u16
+ and 4 others
-error: aborting due to 9 previous errors
+error: aborting due to 11 previous errors
-For more information about this error, try `rustc --explain E0308`.
+Some errors have detailed explanations: E0277, E0308.
+For more information about an error, try `rustc --explain E0277`.
diff --git a/tests/ui/type/pattern_types/range_patterns_trait_impls2.stderr b/tests/ui/type/pattern_types/range_patterns_trait_impls2.stderr
index 565d8f18cdbb6..d5c539b6c52e6 100644
--- a/tests/ui/type/pattern_types/range_patterns_trait_impls2.stderr
+++ b/tests/ui/type/pattern_types/range_patterns_trait_impls2.stderr
@@ -4,7 +4,7 @@ error[E0117]: only traits defined in the current crate can be implemented for ar
LL | impl Eq for Y {}
| ^^^^^^^^^^^^-
| |
- | `(u32) is 1..=u32::MAX` is not defined in the current crate
+ | `(u32) is 1..=` is not defined in the current crate
|
= note: impl doesn't have any local type before any uncovered type parameters
= note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules
diff --git a/tests/ui/type/pattern_types/reverse_range.rs b/tests/ui/type/pattern_types/reverse_range.rs
index 6a245615f1ac0..4471624d66440 100644
--- a/tests/ui/type/pattern_types/reverse_range.rs
+++ b/tests/ui/type/pattern_types/reverse_range.rs
@@ -1,14 +1,11 @@
//! Check that the range start must be smaller than the range end
-//@ known-bug: unknown
-//@ failure-status: 101
-//@ normalize-stderr: "note: .*\n\n" -> ""
-//@ normalize-stderr: "thread 'rustc' panicked.*\n" -> ""
-//@ normalize-stderr: "(error: internal compiler error: [^:]+):\d+:\d+: " -> "$1:LL:CC: "
-//@ rustc-env:RUST_BACKTRACE=0
-
-#![feature(pattern_types)]
+#![feature(pattern_types, const_trait_impl, pattern_type_range_trait)]
#![feature(pattern_type_macro)]
use std::pat::pattern_type;
const NONE: pattern_type!(u8 is 1..0) = unsafe { std::mem::transmute(3_u8) };
+//~^ NOTE: attempt to compute `0_u8 - 1_u8`, which would overflow
+//~| ERROR: evaluation of constant value failed
+
+fn main() {}
diff --git a/tests/ui/type/pattern_types/reverse_range.stderr b/tests/ui/type/pattern_types/reverse_range.stderr
index b714ca7d9ab96..8bdd40297870a 100644
--- a/tests/ui/type/pattern_types/reverse_range.stderr
+++ b/tests/ui/type/pattern_types/reverse_range.stderr
@@ -1,17 +1,9 @@
-error[E0601]: `main` function not found in crate `reverse_range`
- --> $DIR/reverse_range.rs:14:78
+error[E0080]: evaluation of constant value failed
+ --> $DIR/reverse_range.rs:7:36
|
LL | const NONE: pattern_type!(u8 is 1..0) = unsafe { std::mem::transmute(3_u8) };
- | ^ consider adding a `main` function to `$DIR/reverse_range.rs`
+ | ^ attempt to compute `0_u8 - 1_u8`, which would overflow
-
-assertion failed: end <= max_value
-error: the compiler unexpectedly panicked. this is a bug.
-
-query stack during panic:
-#0 [eval_to_allocation_raw] const-evaluating + checking `NONE`
-#1 [eval_to_const_value_raw] simplifying constant for the type system `NONE`
-... and 1 other queries... use `env RUST_BACKTRACE=1` to see the full query stack
error: aborting due to 1 previous error
-For more information about this error, try `rustc --explain E0601`.
+For more information about this error, try `rustc --explain E0080`.
diff --git a/tests/ui/type/pattern_types/validity.rs b/tests/ui/type/pattern_types/validity.rs
index 77a4e72f6755a..5a6a688e1b305 100644
--- a/tests/ui/type/pattern_types/validity.rs
+++ b/tests/ui/type/pattern_types/validity.rs
@@ -1,6 +1,6 @@
//! Check that pattern types have their validity checked
-#![feature(pattern_types)]
+#![feature(pattern_types, const_trait_impl, pattern_type_range_trait)]
#![feature(pattern_type_macro)]
use std::pat::pattern_type;
From 73b7e5f9cb5a68a6cfa6e899b839e59a1df5a369 Mon Sep 17 00:00:00 2001
From: Oli Scherer
Date: Wed, 5 Feb 2025 16:21:54 +0000
Subject: [PATCH 08/15] Hide the end of ranges in pretty printing if it's also
the maximum of the type
---
compiler/rustc_middle/src/ty/pattern.rs | 24 +++++++++++++++-
tests/codegen/pattern_type_symbols.rs | 4 +--
.../pattern_types.main.PreCodegen.after.mir | 4 +--
tests/mir-opt/pattern_types.rs | 4 +--
tests/ui/lint/clashing-extern-fn.stderr | 2 +-
tests/ui/type/pattern_types/nested.stderr | 28 +++++++++----------
.../type/pattern_types/range_patterns.stderr | 4 +--
.../range_patterns_unusable.stderr | 2 +-
.../range_patterns_unusable_math.rs | 2 +-
.../range_patterns_unusable_math.stderr | 4 +--
10 files changed, 50 insertions(+), 28 deletions(-)
diff --git a/compiler/rustc_middle/src/ty/pattern.rs b/compiler/rustc_middle/src/ty/pattern.rs
index 89dadb36ad649..4cad1ab209916 100644
--- a/compiler/rustc_middle/src/ty/pattern.rs
+++ b/compiler/rustc_middle/src/ty/pattern.rs
@@ -27,7 +27,29 @@ impl<'tcx> fmt::Debug for PatternKind<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
PatternKind::Range { start, end } => {
- write!(f, "{start}..={end}")
+ write!(f, "{start}")?;
+
+ if let Some(c) = end.try_to_value() {
+ let end = c.valtree.unwrap_leaf();
+ let size = end.size();
+ let max = match c.ty.kind() {
+ ty::Int(_) => {
+ Some(ty::ScalarInt::truncate_from_int(size.signed_int_max(), size))
+ }
+ ty::Uint(_) => {
+ Some(ty::ScalarInt::truncate_from_uint(size.unsigned_int_max(), size))
+ }
+ ty::Char => Some(ty::ScalarInt::truncate_from_uint(char::MAX, size)),
+ _ => None,
+ };
+ if let Some((max, _)) = max
+ && end == max
+ {
+ return write!(f, "..");
+ }
+ }
+
+ write!(f, "..={end}")
}
}
}
diff --git a/tests/codegen/pattern_type_symbols.rs b/tests/codegen/pattern_type_symbols.rs
index b504a3508f94c..e86a9ef27de1b 100644
--- a/tests/codegen/pattern_type_symbols.rs
+++ b/tests/codegen/pattern_type_symbols.rs
@@ -16,7 +16,7 @@ pub fn bar() {
// CHECK: call pattern_type_symbols::foo::
// CHECK: call void @_RINvC[[CRATE_IDENT:[a-zA-Z0-9]{12}]]_20pattern_type_symbols3foomEB2_
foo::();
- // CHECK: call pattern_type_symbols::foo::<(u32, [(); 0], [(); 999999999], [(); true])>
- // CHECK: call void @_RINvC[[CRATE_IDENT]]_20pattern_type_symbols3fooTmAum0_Aum3b9ac9ff_Aub1_EEB2_
+ // CHECK: call pattern_type_symbols::foo::<(u32, [(); 0], [(); 999999999])>
+ // CHECK: call void @_RINvC[[CRATE_IDENT]]_20pattern_type_symbols3fooTmAum0_Aum3b9ac9ff_EEB2_
foo::();
}
diff --git a/tests/mir-opt/pattern_types.main.PreCodegen.after.mir b/tests/mir-opt/pattern_types.main.PreCodegen.after.mir
index 8214ff61d75e9..cc01f51973c84 100644
--- a/tests/mir-opt/pattern_types.main.PreCodegen.after.mir
+++ b/tests/mir-opt/pattern_types.main.PreCodegen.after.mir
@@ -3,9 +3,9 @@
fn main() -> () {
let mut _0: ();
scope 1 {
- debug x => const 2_u32 is 1..=u32::MAX;
+ debug x => const 2_u32 is 1..;
scope 2 {
- debug y => const {transmute(0x00000000): (u32) is 1..=u32::MAX};
+ debug y => const {transmute(0x00000000): (u32) is 1..};
}
}
diff --git a/tests/mir-opt/pattern_types.rs b/tests/mir-opt/pattern_types.rs
index 6d53919934528..d5847b95f7328 100644
--- a/tests/mir-opt/pattern_types.rs
+++ b/tests/mir-opt/pattern_types.rs
@@ -5,8 +5,8 @@ use std::pat::pattern_type;
// EMIT_MIR pattern_types.main.PreCodegen.after.mir
fn main() {
- // CHECK: debug x => const 2_u32 is 1..=u32::MAX
+ // CHECK: debug x => const 2_u32 is 1..
let x: pattern_type!(u32 is 1..) = unsafe { std::mem::transmute(2) };
- // CHECK: debug y => const {transmute(0x00000000): (u32) is 1..=u32::MAX}
+ // CHECK: debug y => const {transmute(0x00000000): (u32) is 1..}
let y: pattern_type!(u32 is 1..) = unsafe { std::mem::transmute(0) };
}
diff --git a/tests/ui/lint/clashing-extern-fn.stderr b/tests/ui/lint/clashing-extern-fn.stderr
index 4e9f6ea3f78c7..0c27547a6ed8f 100644
--- a/tests/ui/lint/clashing-extern-fn.stderr
+++ b/tests/ui/lint/clashing-extern-fn.stderr
@@ -17,7 +17,7 @@ LL | fn hidden_niche_unsafe_cell() -> Option`, which is not FFI-safe
+warning: `extern` block uses type `Option<(usize) is 0..>`, which is not FFI-safe
--> $DIR/clashing-extern-fn.rs:502:54
|
LL | fn pt_non_zero_usize_opt_full_range() -> Option;
diff --git a/tests/ui/type/pattern_types/nested.stderr b/tests/ui/type/pattern_types/nested.stderr
index 04aacabc136e7..f79d12bc3f376 100644
--- a/tests/ui/type/pattern_types/nested.stderr
+++ b/tests/ui/type/pattern_types/nested.stderr
@@ -2,18 +2,18 @@ error[E0308]: mismatched types
--> $DIR/nested.rs:10:63
|
LL | const BAD_NESTING: pattern_type!(pattern_type!(u32 is 1..) is 0..) = todo!();
- | ^ expected `(u32) is 1..=u32::MAX`, found integer
+ | ^ expected `(u32) is 1..`, found integer
|
- = note: expected pattern type `(u32) is 1..=u32::MAX`
+ = note: expected pattern type `(u32) is 1..`
found type `{integer}`
-error[E0277]: `(u32) is 1..=u32::MAX` is not a valid base type for range patterns
+error[E0277]: `(u32) is 1..` is not a valid base type for range patterns
--> $DIR/nested.rs:10:34
|
LL | const BAD_NESTING: pattern_type!(pattern_type!(u32 is 1..) is 0..) = todo!();
| ^^^^^^^^^^^^^^^^^^^^^^^^^ only integer types and `char` are supported
|
- = help: the trait `core::pat::RangePattern` is not implemented for `(u32) is 1..=u32::MAX`
+ = help: the trait `core::pat::RangePattern` is not implemented for `(u32) is 1..`
= help: the following other types implement trait `core::pat::RangePattern`:
char
i128
@@ -25,13 +25,13 @@ LL | const BAD_NESTING: pattern_type!(pattern_type!(u32 is 1..) is 0..) = todo!(
u128
and 5 others
-error[E0277]: `(i32) is 1..=i32::MAX` is not a valid base type for range patterns
+error[E0277]: `(i32) is 1..` is not a valid base type for range patterns
--> $DIR/nested.rs:15:35
|
LL | const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = todo!();
| ^^^^^^^^^^^^^^^^^^^^^^^^^ only integer types and `char` are supported
|
- = help: the trait `core::pat::RangePattern` is not implemented for `(i32) is 1..=i32::MAX`
+ = help: the trait `core::pat::RangePattern` is not implemented for `(i32) is 1..`
= help: the following other types implement trait `core::pat::RangePattern`:
char
i128
@@ -47,18 +47,18 @@ error[E0308]: mismatched types
--> $DIR/nested.rs:15:67
|
LL | const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = todo!();
- | ^^ expected `(i32) is 1..=i32::MAX`, found integer
+ | ^^ expected `(i32) is 1..`, found integer
|
- = note: expected pattern type `(i32) is 1..=i32::MAX`
+ = note: expected pattern type `(i32) is 1..`
found type `{integer}`
-error[E0277]: `(i32) is 1..=i32::MAX` is not a valid base type for range patterns
+error[E0277]: `(i32) is 1..` is not a valid base type for range patterns
--> $DIR/nested.rs:19:35
|
LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!();
| ^^^^^^^^^^^^^^^^^^^^^^^^^ only integer types and `char` are supported
|
- = help: the trait `core::pat::RangePattern` is not implemented for `(i32) is 1..=i32::MAX`
+ = help: the trait `core::pat::RangePattern` is not implemented for `(i32) is 1..`
= help: the following other types implement trait `core::pat::RangePattern`:
char
i128
@@ -76,10 +76,10 @@ error[E0308]: mismatched types
LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!();
| ^
| |
- | expected `(i32) is 1..=i32::MAX`, found integer
+ | expected `(i32) is 1..`, found integer
| arguments to this function are incorrect
|
- = note: expected pattern type `(i32) is 1..=i32::MAX`
+ = note: expected pattern type `(i32) is 1..`
found type `{integer}`
help: the return type of this call is `{integer}` due to the type of the argument passed
--> $DIR/nested.rs:19:66
@@ -89,13 +89,13 @@ LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!
note: method defined here
--> $SRC_DIR/core/src/pat.rs:LL:COL
-error[E0277]: `(i32) is 1..=i32::MAX` is not a valid base type for range patterns
+error[E0277]: `(i32) is 1..` is not a valid base type for range patterns
--> $DIR/nested.rs:19:66
|
LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!();
| ^ only integer types and `char` are supported
|
- = help: the trait `core::pat::RangePattern` is not implemented for `(i32) is 1..=i32::MAX`
+ = help: the trait `core::pat::RangePattern` is not implemented for `(i32) is 1..`
= help: the following other types implement trait `core::pat::RangePattern`:
char
i128
diff --git a/tests/ui/type/pattern_types/range_patterns.stderr b/tests/ui/type/pattern_types/range_patterns.stderr
index 43ae7e870f002..512181c22d535 100644
--- a/tests/ui/type/pattern_types/range_patterns.stderr
+++ b/tests/ui/type/pattern_types/range_patterns.stderr
@@ -44,7 +44,7 @@ error: layout_of(NonZero) = Layout {
LL | type X = std::num::NonZeroU32;
| ^^^^^^
-error: layout_of((u32) is 1..=u32::MAX) = Layout {
+error: layout_of((u32) is 1..) = Layout {
size: Size(4 bytes),
align: AbiAndPrefAlign {
abi: Align(4 bytes),
@@ -83,7 +83,7 @@ error: layout_of((u32) is 1..=u32::MAX) = Layout {
LL | type Y = pattern_type!(u32 is 1..);
| ^^^^^^
-error: layout_of(Option<(u32) is 1..=u32::MAX>) = Layout {
+error: layout_of(Option<(u32) is 1..>) = Layout {
size: Size(4 bytes),
align: AbiAndPrefAlign {
abi: Align(4 bytes),
diff --git a/tests/ui/type/pattern_types/range_patterns_unusable.stderr b/tests/ui/type/pattern_types/range_patterns_unusable.stderr
index a9558852eb29e..7daa41d708179 100644
--- a/tests/ui/type/pattern_types/range_patterns_unusable.stderr
+++ b/tests/ui/type/pattern_types/range_patterns_unusable.stderr
@@ -4,7 +4,7 @@ error[E0512]: cannot transmute between types of different sizes, or dependently-
LL | let _: Option = unsafe { std::mem::transmute(z) };
| ^^^^^^^^^^^^^^^^^^^
|
- = note: source type: `Option<(u32) is 1..=u32::MAX>` (32 bits)
+ = note: source type: `Option<(u32) is 1..>` (32 bits)
= note: target type: `Option` (64 bits)
error: aborting due to 1 previous error
diff --git a/tests/ui/type/pattern_types/range_patterns_unusable_math.rs b/tests/ui/type/pattern_types/range_patterns_unusable_math.rs
index 54ad6096c577d..6125063699be9 100644
--- a/tests/ui/type/pattern_types/range_patterns_unusable_math.rs
+++ b/tests/ui/type/pattern_types/range_patterns_unusable_math.rs
@@ -11,5 +11,5 @@ type Z = Option;
fn main() {
let x: Y = unsafe { std::mem::transmute(42_u32) };
- let x = x + 1_u32; //~ ERROR cannot add `u32` to `(u32) is 1..=u32::MAX`
+ let x = x + 1_u32; //~ ERROR cannot add `u32` to `(u32) is 1..`
}
diff --git a/tests/ui/type/pattern_types/range_patterns_unusable_math.stderr b/tests/ui/type/pattern_types/range_patterns_unusable_math.stderr
index 68ddd63cd5306..a64f1db31766f 100644
--- a/tests/ui/type/pattern_types/range_patterns_unusable_math.stderr
+++ b/tests/ui/type/pattern_types/range_patterns_unusable_math.stderr
@@ -1,10 +1,10 @@
-error[E0369]: cannot add `u32` to `(u32) is 1..=u32::MAX`
+error[E0369]: cannot add `u32` to `(u32) is 1..`
--> $DIR/range_patterns_unusable_math.rs:14:15
|
LL | let x = x + 1_u32;
| - ^ ----- u32
| |
- | (u32) is 1..=u32::MAX
+ | (u32) is 1..
error: aborting due to 1 previous error
From 3477297c374f2b12a5846b8b522b1e69ea08cc5e Mon Sep 17 00:00:00 2001
From: bjorn3 <17426603+bjorn3@users.noreply.github.com>
Date: Fri, 24 Jan 2025 15:40:41 +0000
Subject: [PATCH 09/15] Fix UB in ThinVec::flat_map_in_place
thin_vec.as_ptr() goes through the Deref impl of ThinVec, which will
not allow access to any memory as we did call set_len(0) first.
---
.../src/flat_map_in_place.rs | 42 +++++++++++--------
1 file changed, 25 insertions(+), 17 deletions(-)
diff --git a/compiler/rustc_data_structures/src/flat_map_in_place.rs b/compiler/rustc_data_structures/src/flat_map_in_place.rs
index e66b00b755760..6d718059f9c81 100644
--- a/compiler/rustc_data_structures/src/flat_map_in_place.rs
+++ b/compiler/rustc_data_structures/src/flat_map_in_place.rs
@@ -1,4 +1,4 @@
-use std::ptr;
+use std::{mem, ptr};
use smallvec::{Array, SmallVec};
use thin_vec::ThinVec;
@@ -13,39 +13,44 @@ pub trait FlatMapInPlace: Sized {
// The implementation of this method is syntactically identical for all the
// different vector types.
macro_rules! flat_map_in_place {
- () => {
+ ($vec:ident $( where T: $bound:path)?) => {
fn flat_map_in_place(&mut self, mut f: F)
where
F: FnMut(T) -> I,
I: IntoIterator- ,
{
+ struct LeakGuard<'a, T $(: $bound)?>(&'a mut $vec);
+
+ impl<'a, T $(: $bound)?> Drop for LeakGuard<'a, T> {
+ fn drop(&mut self) {
+ unsafe {
+ self.0.set_len(0); // make sure we just leak elements in case of panic
+ }
+ }
+ }
+
+ let this = LeakGuard(self);
+
let mut read_i = 0;
let mut write_i = 0;
unsafe {
- let mut old_len = self.len();
- self.set_len(0); // make sure we just leak elements in case of panic
-
- while read_i < old_len {
+ while read_i < this.0.len() {
// move the read_i'th item out of the vector and map it
// to an iterator
- let e = ptr::read(self.as_ptr().add(read_i));
+ let e = ptr::read(this.0.as_ptr().add(read_i));
let iter = f(e).into_iter();
read_i += 1;
for e in iter {
if write_i < read_i {
- ptr::write(self.as_mut_ptr().add(write_i), e);
+ ptr::write(this.0.as_mut_ptr().add(write_i), e);
write_i += 1;
} else {
// If this is reached we ran out of space
// in the middle of the vector.
// However, the vector is in a valid state here,
// so we just do a somewhat inefficient insert.
- self.set_len(old_len);
- self.insert(write_i, e);
-
- old_len = self.len();
- self.set_len(0);
+ this.0.insert(write_i, e);
read_i += 1;
write_i += 1;
@@ -54,20 +59,23 @@ macro_rules! flat_map_in_place {
}
// write_i tracks the number of actually written new items.
- self.set_len(write_i);
+ this.0.set_len(write_i);
+
+ // The ThinVec is in a sane state again. Prevent the LeakGuard from leaking the data.
+ mem::forget(this);
}
}
};
}
impl FlatMapInPlace for Vec {
- flat_map_in_place!();
+ flat_map_in_place!(Vec);
}
impl> FlatMapInPlace for SmallVec {
- flat_map_in_place!();
+ flat_map_in_place!(SmallVec where T: Array);
}
impl FlatMapInPlace for ThinVec {
- flat_map_in_place!();
+ flat_map_in_place!(ThinVec);
}
From 864cca80b02e9daa2c41440f1bf9787083c9f149 Mon Sep 17 00:00:00 2001
From: Michael Goulet
Date: Sun, 23 Feb 2025 03:34:33 +0000
Subject: [PATCH 10/15] Print out destructor
---
Cargo.lock | 1 +
compiler/rustc_lint/Cargo.toml | 1 +
compiler/rustc_lint/messages.ftl | 5 +
compiler/rustc_lint/src/if_let_rescope.rs | 77 ++++++--
compiler/rustc_middle/src/ty/mod.rs | 1 +
.../src/ty/significant_drop_order.rs | 172 +++++++++++++++++
.../src/lint_tail_expr_drop_order.rs | 174 +-----------------
.../drop/drop-order-comparisons.e2021.stderr | 90 +++++++++
...nt-if-let-rescope-gated.edition2021.stderr | 9 +
.../lint-if-let-rescope-with-macro.stderr | 9 +
tests/ui/drop/lint-if-let-rescope.stderr | 72 ++++++++
11 files changed, 425 insertions(+), 186 deletions(-)
create mode 100644 compiler/rustc_middle/src/ty/significant_drop_order.rs
diff --git a/Cargo.lock b/Cargo.lock
index fb4cf235c6f49..2227f5f3e9934 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3916,6 +3916,7 @@ dependencies = [
"rustc_target",
"rustc_trait_selection",
"rustc_type_ir",
+ "smallvec",
"tracing",
"unicode-security",
]
diff --git a/compiler/rustc_lint/Cargo.toml b/compiler/rustc_lint/Cargo.toml
index 24e7b40c8a2d3..d6014f5006ad0 100644
--- a/compiler/rustc_lint/Cargo.toml
+++ b/compiler/rustc_lint/Cargo.toml
@@ -24,6 +24,7 @@ rustc_span = { path = "../rustc_span" }
rustc_target = { path = "../rustc_target" }
rustc_trait_selection = { path = "../rustc_trait_selection" }
rustc_type_ir = { path = "../rustc_type_ir" }
+smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
tracing = "0.1"
unicode-security = "0.1.0"
# tidy-alphabetical-end
diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl
index 679367634276d..d51865810b9a8 100644
--- a/compiler/rustc_lint/messages.ftl
+++ b/compiler/rustc_lint/messages.ftl
@@ -333,6 +333,11 @@ lint_identifier_uncommon_codepoints = identifier contains {$codepoints_len ->
*[other] {" "}{$identifier_type}
} Unicode general security profile
+lint_if_let_dtor = {$dtor_kind ->
+ [dyn] value may invoke a custom destructor because it contains a trait object
+ *[concrete] value invokes this custom destructor
+ }
+
lint_if_let_rescope = `if let` assigns a shorter lifetime since Edition 2024
.label = this value has a significant drop implementation which may observe a major change in drop order and requires your discretion
.help = the value is now dropped here in Edition 2024
diff --git a/compiler/rustc_lint/src/if_let_rescope.rs b/compiler/rustc_lint/src/if_let_rescope.rs
index 23f037f3692ab..39ea8d8e3246c 100644
--- a/compiler/rustc_lint/src/if_let_rescope.rs
+++ b/compiler/rustc_lint/src/if_let_rescope.rs
@@ -7,13 +7,17 @@ use rustc_errors::{
Applicability, Diag, EmissionGuarantee, SubdiagMessageOp, Subdiagnostic, SuggestionStyle,
};
use rustc_hir::{self as hir, HirIdSet};
-use rustc_macros::LintDiagnostic;
-use rustc_middle::ty::TyCtxt;
+use rustc_macros::{LintDiagnostic, Subdiagnostic};
use rustc_middle::ty::adjustment::Adjust;
+use rustc_middle::ty::significant_drop_order::{
+ extract_component_with_significant_dtor, ty_dtor_span,
+};
+use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_session::lint::{FutureIncompatibilityReason, LintId};
use rustc_session::{declare_lint, impl_lint_pass};
-use rustc_span::Span;
use rustc_span::edition::Edition;
+use rustc_span::{DUMMY_SP, Span};
+use smallvec::SmallVec;
use crate::{LateContext, LateLintPass};
@@ -130,6 +134,7 @@ impl IfLetRescope {
hir::ExprKind::If(_cond, _conseq, Some(alt)) => alt.span.shrink_to_hi(),
_ => return,
};
+ let mut seen_dyn = false;
let mut add_bracket_to_match_head = match_head_needs_bracket(tcx, expr);
let mut significant_droppers = vec![];
let mut lifetime_ends = vec![];
@@ -137,6 +142,7 @@ impl IfLetRescope {
let mut alt_heads = vec![];
let mut match_heads = vec![];
let mut consequent_heads = vec![];
+ let mut destructors = vec![];
let mut first_if_to_lint = None;
let mut first_if_to_rewrite = false;
let mut empty_alt = false;
@@ -160,11 +166,25 @@ impl IfLetRescope {
let before_conseq = conseq.span.shrink_to_lo();
let lifetime_end = source_map.end_point(conseq.span);
- if let ControlFlow::Break(significant_dropper) =
+ if let ControlFlow::Break((drop_span, drop_tys)) =
(FindSignificantDropper { cx }).check_if_let_scrutinee(init)
{
+ destructors.extend(drop_tys.into_iter().filter_map(|ty| {
+ if let Some(span) = ty_dtor_span(tcx, ty) {
+ Some(DestructorLabel { span, dtor_kind: "concrete" })
+ } else if matches!(ty.kind(), ty::Dynamic(..)) {
+ if seen_dyn {
+ None
+ } else {
+ seen_dyn = true;
+ Some(DestructorLabel { span: DUMMY_SP, dtor_kind: "dyn" })
+ }
+ } else {
+ None
+ }
+ }));
first_if_to_lint = first_if_to_lint.or_else(|| Some((span, expr.hir_id)));
- significant_droppers.push(significant_dropper);
+ significant_droppers.push(drop_span);
lifetime_ends.push(lifetime_end);
if ty_ascription.is_some()
|| !expr.span.can_be_used_for_suggestions()
@@ -227,6 +247,7 @@ impl IfLetRescope {
hir_id,
span,
IfLetRescopeLint {
+ destructors,
significant_droppers,
lifetime_ends,
rewrite: first_if_to_rewrite.then_some(IfLetRescopeRewrite {
@@ -288,6 +309,8 @@ impl<'tcx> LateLintPass<'tcx> for IfLetRescope {
#[derive(LintDiagnostic)]
#[diag(lint_if_let_rescope)]
struct IfLetRescopeLint {
+ #[subdiagnostic]
+ destructors: Vec,
#[label]
significant_droppers: Vec,
#[help]
@@ -347,6 +370,14 @@ impl Subdiagnostic for IfLetRescopeRewrite {
}
}
+#[derive(Subdiagnostic)]
+#[note(lint_if_let_dtor)]
+struct DestructorLabel {
+ #[primary_span]
+ span: Span,
+ dtor_kind: &'static str,
+}
+
struct AltHead(Span);
struct ConsequentRewrite {
@@ -374,7 +405,10 @@ impl<'tcx> FindSignificantDropper<'_, 'tcx> {
/// of the scrutinee itself, and also recurses into the expression to find any ref
/// exprs (or autoref) which would promote temporaries that would be scoped to the
/// end of this `if`.
- fn check_if_let_scrutinee(&mut self, init: &'tcx hir::Expr<'tcx>) -> ControlFlow {
+ fn check_if_let_scrutinee(
+ &mut self,
+ init: &'tcx hir::Expr<'tcx>,
+ ) -> ControlFlow<(Span, SmallVec<[Ty<'tcx>; 4]>)> {
self.check_promoted_temp_with_drop(init)?;
self.visit_expr(init)
}
@@ -385,28 +419,35 @@ impl<'tcx> FindSignificantDropper<'_, 'tcx> {
/// An expression is a promoted temporary if it has an addr taken (i.e. `&expr` or autoref)
/// or is the scrutinee of the `if let`, *and* the expression is not a place
/// expr, and it has a significant drop.
- fn check_promoted_temp_with_drop(&self, expr: &'tcx hir::Expr<'tcx>) -> ControlFlow {
- if !expr.is_place_expr(|base| {
+ fn check_promoted_temp_with_drop(
+ &self,
+ expr: &'tcx hir::Expr<'tcx>,
+ ) -> ControlFlow<(Span, SmallVec<[Ty<'tcx>; 4]>)> {
+ if expr.is_place_expr(|base| {
self.cx
.typeck_results()
.adjustments()
.get(base.hir_id)
.is_some_and(|x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_))))
- }) && self
- .cx
- .typeck_results()
- .expr_ty(expr)
- .has_significant_drop(self.cx.tcx, self.cx.typing_env())
- {
- ControlFlow::Break(expr.span)
- } else {
- ControlFlow::Continue(())
+ }) {
+ return ControlFlow::Continue(());
}
+
+ let drop_tys = extract_component_with_significant_dtor(
+ self.cx.tcx,
+ self.cx.typing_env(),
+ self.cx.typeck_results().expr_ty(expr),
+ );
+ if drop_tys.is_empty() {
+ return ControlFlow::Continue(());
+ }
+
+ ControlFlow::Break((expr.span, drop_tys))
}
}
impl<'tcx> Visitor<'tcx> for FindSignificantDropper<'_, 'tcx> {
- type Result = ControlFlow;
+ type Result = ControlFlow<(Span, SmallVec<[Ty<'tcx>; 4]>)>;
fn visit_block(&mut self, b: &'tcx hir::Block<'tcx>) -> Self::Result {
// Blocks introduce temporary terminating scope for all of its
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index eb70a35d3708c..d926d6cc02a5f 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -122,6 +122,7 @@ pub mod normalize_erasing_regions;
pub mod pattern;
pub mod print;
pub mod relate;
+pub mod significant_drop_order;
pub mod trait_def;
pub mod util;
pub mod visit;
diff --git a/compiler/rustc_middle/src/ty/significant_drop_order.rs b/compiler/rustc_middle/src/ty/significant_drop_order.rs
new file mode 100644
index 0000000000000..7f0d82d89fede
--- /dev/null
+++ b/compiler/rustc_middle/src/ty/significant_drop_order.rs
@@ -0,0 +1,172 @@
+use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::unord::UnordSet;
+use rustc_hir::def_id::DefId;
+use rustc_span::Span;
+use smallvec::{SmallVec, smallvec};
+use tracing::{debug, instrument};
+
+use crate::ty::{self, Ty, TyCtxt};
+
+/// An additional filter to exclude well-known types from the ecosystem
+/// because their drops are trivial.
+/// This returns additional types to check if the drops are delegated to those.
+/// A typical example is `hashbrown::HashMap`, whose drop is delegated to `K` and `V`.
+fn true_significant_drop_ty<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ ty: Ty<'tcx>,
+) -> Option; 2]>> {
+ if let ty::Adt(def, args) = ty.kind() {
+ let mut did = def.did();
+ let mut name_rev = vec![];
+ loop {
+ let key = tcx.def_key(did);
+
+ match key.disambiguated_data.data {
+ rustc_hir::definitions::DefPathData::CrateRoot => {
+ name_rev.push(tcx.crate_name(did.krate))
+ }
+ rustc_hir::definitions::DefPathData::TypeNs(symbol) => name_rev.push(symbol),
+ _ => return None,
+ }
+ if let Some(parent) = key.parent {
+ did = DefId { krate: did.krate, index: parent };
+ } else {
+ break;
+ }
+ }
+ let name_str: Vec<_> = name_rev.iter().rev().map(|x| x.as_str()).collect();
+ debug!(?name_str);
+ match name_str[..] {
+ // These are the types from Rust core ecosystem
+ ["syn" | "proc_macro2", ..]
+ | ["core" | "std", "task", "LocalWaker" | "Waker"]
+ | ["core" | "std", "task", "wake", "LocalWaker" | "Waker"] => Some(smallvec![]),
+ // These are important types from Rust ecosystem
+ ["tracing", "instrument", "Instrumented"] | ["bytes", "Bytes"] => Some(smallvec![]),
+ ["hashbrown", "raw", "RawTable" | "RawIntoIter"] => {
+ if let [ty, ..] = &***args
+ && let Some(ty) = ty.as_type()
+ {
+ Some(smallvec![ty])
+ } else {
+ None
+ }
+ }
+ ["hashbrown", "raw", "RawDrain"] => {
+ if let [_, ty, ..] = &***args
+ && let Some(ty) = ty.as_type()
+ {
+ Some(smallvec![ty])
+ } else {
+ None
+ }
+ }
+ _ => None,
+ }
+ } else {
+ None
+ }
+}
+
+/// Returns the list of types with a "potentially sigificant" that may be dropped
+/// by dropping a value of type `ty`.
+#[instrument(level = "trace", skip(tcx, typing_env))]
+pub fn extract_component_raw<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ typing_env: ty::TypingEnv<'tcx>,
+ ty: Ty<'tcx>,
+ ty_seen: &mut UnordSet>,
+) -> SmallVec<[Ty<'tcx>; 4]> {
+ // Droppiness does not depend on regions, so let us erase them.
+ let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty);
+
+ let tys = tcx.list_significant_drop_tys(typing_env.as_query_input(ty));
+ debug!(?ty, "components");
+ let mut out_tys = smallvec![];
+ for ty in tys {
+ if let Some(tys) = true_significant_drop_ty(tcx, ty) {
+ // Some types can be further opened up because the drop is simply delegated
+ for ty in tys {
+ if ty_seen.insert(ty) {
+ out_tys.extend(extract_component_raw(tcx, typing_env, ty, ty_seen));
+ }
+ }
+ } else {
+ if ty_seen.insert(ty) {
+ out_tys.push(ty);
+ }
+ }
+ }
+ out_tys
+}
+
+#[instrument(level = "trace", skip(tcx, typing_env))]
+pub fn extract_component_with_significant_dtor<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ typing_env: ty::TypingEnv<'tcx>,
+ ty: Ty<'tcx>,
+) -> SmallVec<[Ty<'tcx>; 4]> {
+ let mut tys = extract_component_raw(tcx, typing_env, ty, &mut Default::default());
+ let mut deduplicate = FxHashSet::default();
+ tys.retain(|oty| deduplicate.insert(*oty));
+ tys.into_iter().collect()
+}
+
+/// Extract the span of the custom destructor of a type
+/// especially the span of the `impl Drop` header or its entire block
+/// when we are working with current local crate.
+#[instrument(level = "trace", skip(tcx))]
+pub fn ty_dtor_span<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option {
+ match ty.kind() {
+ ty::Bool
+ | ty::Char
+ | ty::Int(_)
+ | ty::Uint(_)
+ | ty::Float(_)
+ | ty::Error(_)
+ | ty::Str
+ | ty::Never
+ | ty::RawPtr(_, _)
+ | ty::Ref(_, _, _)
+ | ty::FnPtr(_, _)
+ | ty::Tuple(_)
+ | ty::Dynamic(_, _, _)
+ | ty::Alias(_, _)
+ | ty::Bound(_, _)
+ | ty::Pat(_, _)
+ | ty::Placeholder(_)
+ | ty::Infer(_)
+ | ty::Slice(_)
+ | ty::Array(_, _)
+ | ty::UnsafeBinder(_) => None,
+
+ ty::Adt(adt_def, _) => {
+ let did = adt_def.did();
+ let try_local_did_span = |did: DefId| {
+ if let Some(local) = did.as_local() {
+ tcx.source_span(local)
+ } else {
+ tcx.def_span(did)
+ }
+ };
+ let dtor = if let Some(dtor) = tcx.adt_destructor(did) {
+ dtor.did
+ } else if let Some(dtor) = tcx.adt_async_destructor(did) {
+ dtor.future
+ } else {
+ return Some(try_local_did_span(did));
+ };
+ let def_key = tcx.def_key(dtor);
+ let Some(parent_index) = def_key.parent else { return Some(try_local_did_span(dtor)) };
+ let parent_did = DefId { index: parent_index, krate: dtor.krate };
+ Some(try_local_did_span(parent_did))
+ }
+ ty::Coroutine(did, _)
+ | ty::CoroutineWitness(did, _)
+ | ty::CoroutineClosure(did, _)
+ | ty::Closure(did, _)
+ | ty::FnDef(did, _)
+ | ty::Foreign(did) => Some(tcx.def_span(did)),
+ ty::Param(_) => None,
+ }
+}
diff --git a/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs b/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs
index e3260e45bc524..7d77fffa83fd1 100644
--- a/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs
+++ b/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs
@@ -6,7 +6,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::unord::{UnordMap, UnordSet};
use rustc_errors::Subdiagnostic;
use rustc_hir::CRATE_HIR_ID;
-use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::def_id::LocalDefId;
use rustc_index::bit_set::MixedBitSet;
use rustc_index::{IndexSlice, IndexVec};
use rustc_macros::{LintDiagnostic, Subdiagnostic};
@@ -15,7 +15,10 @@ use rustc_middle::mir::{
self, BasicBlock, Body, ClearCrossCrate, Local, Location, Place, StatementKind, TerminatorKind,
dump_mir,
};
-use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::significant_drop_order::{
+ extract_component_with_significant_dtor, ty_dtor_span,
+};
+use rustc_middle::ty::{self, TyCtxt};
use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
use rustc_mir_dataflow::move_paths::{LookupResult, MoveData, MovePathIndex};
use rustc_mir_dataflow::{Analysis, MaybeReachable, ResultsCursor};
@@ -23,8 +26,7 @@ use rustc_session::lint::builtin::TAIL_EXPR_DROP_ORDER;
use rustc_session::lint::{self};
use rustc_span::{DUMMY_SP, Span, Symbol};
use rustc_type_ir::data_structures::IndexMap;
-use smallvec::{SmallVec, smallvec};
-use tracing::{debug, instrument};
+use tracing::debug;
fn place_has_common_prefix<'tcx>(left: &Place<'tcx>, right: &Place<'tcx>) -> bool {
left.local == right.local
@@ -155,170 +157,6 @@ impl<'a, 'mir, 'tcx> DropsReachable<'a, 'mir, 'tcx> {
}
}
-/// An additional filter to exclude well-known types from the ecosystem
-/// because their drops are trivial.
-/// This returns additional types to check if the drops are delegated to those.
-/// A typical example is `hashbrown::HashMap`, whose drop is delegated to `K` and `V`.
-fn true_significant_drop_ty<'tcx>(
- tcx: TyCtxt<'tcx>,
- ty: Ty<'tcx>,
-) -> Option; 2]>> {
- if let ty::Adt(def, args) = ty.kind() {
- let mut did = def.did();
- let mut name_rev = vec![];
- loop {
- let key = tcx.def_key(did);
-
- match key.disambiguated_data.data {
- rustc_hir::definitions::DefPathData::CrateRoot => {
- name_rev.push(tcx.crate_name(did.krate))
- }
- rustc_hir::definitions::DefPathData::TypeNs(symbol) => name_rev.push(symbol),
- _ => return None,
- }
- if let Some(parent) = key.parent {
- did = DefId { krate: did.krate, index: parent };
- } else {
- break;
- }
- }
- let name_str: Vec<_> = name_rev.iter().rev().map(|x| x.as_str()).collect();
- debug!(?name_str);
- match name_str[..] {
- // These are the types from Rust core ecosystem
- ["syn" | "proc_macro2", ..]
- | ["core" | "std", "task", "LocalWaker" | "Waker"]
- | ["core" | "std", "task", "wake", "LocalWaker" | "Waker"] => Some(smallvec![]),
- // These are important types from Rust ecosystem
- ["tracing", "instrument", "Instrumented"] | ["bytes", "Bytes"] => Some(smallvec![]),
- ["hashbrown", "raw", "RawTable" | "RawIntoIter"] => {
- if let [ty, ..] = &***args
- && let Some(ty) = ty.as_type()
- {
- Some(smallvec![ty])
- } else {
- None
- }
- }
- ["hashbrown", "raw", "RawDrain"] => {
- if let [_, ty, ..] = &***args
- && let Some(ty) = ty.as_type()
- {
- Some(smallvec![ty])
- } else {
- None
- }
- }
- _ => None,
- }
- } else {
- None
- }
-}
-
-/// Returns the list of types with a "potentially sigificant" that may be dropped
-/// by dropping a value of type `ty`.
-#[instrument(level = "debug", skip(tcx, typing_env))]
-fn extract_component_raw<'tcx>(
- tcx: TyCtxt<'tcx>,
- typing_env: ty::TypingEnv<'tcx>,
- ty: Ty<'tcx>,
- ty_seen: &mut UnordSet>,
-) -> SmallVec<[Ty<'tcx>; 4]> {
- // Droppiness does not depend on regions, so let us erase them.
- let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty);
-
- let tys = tcx.list_significant_drop_tys(typing_env.as_query_input(ty));
- debug!(?ty, "components");
- let mut out_tys = smallvec![];
- for ty in tys {
- if let Some(tys) = true_significant_drop_ty(tcx, ty) {
- // Some types can be further opened up because the drop is simply delegated
- for ty in tys {
- if ty_seen.insert(ty) {
- out_tys.extend(extract_component_raw(tcx, typing_env, ty, ty_seen));
- }
- }
- } else {
- if ty_seen.insert(ty) {
- out_tys.push(ty);
- }
- }
- }
- out_tys
-}
-
-#[instrument(level = "debug", skip(tcx, typing_env))]
-fn extract_component_with_significant_dtor<'tcx>(
- tcx: TyCtxt<'tcx>,
- typing_env: ty::TypingEnv<'tcx>,
- ty: Ty<'tcx>,
-) -> SmallVec<[Ty<'tcx>; 4]> {
- let mut tys = extract_component_raw(tcx, typing_env, ty, &mut Default::default());
- let mut deduplicate = FxHashSet::default();
- tys.retain(|oty| deduplicate.insert(*oty));
- tys.into_iter().collect()
-}
-
-/// Extract the span of the custom destructor of a type
-/// especially the span of the `impl Drop` header or its entire block
-/// when we are working with current local crate.
-#[instrument(level = "debug", skip(tcx))]
-fn ty_dtor_span<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option {
- match ty.kind() {
- ty::Bool
- | ty::Char
- | ty::Int(_)
- | ty::Uint(_)
- | ty::Float(_)
- | ty::Error(_)
- | ty::Str
- | ty::Never
- | ty::RawPtr(_, _)
- | ty::Ref(_, _, _)
- | ty::FnPtr(_, _)
- | ty::Tuple(_)
- | ty::Dynamic(_, _, _)
- | ty::Alias(_, _)
- | ty::Bound(_, _)
- | ty::Pat(_, _)
- | ty::Placeholder(_)
- | ty::Infer(_)
- | ty::Slice(_)
- | ty::Array(_, _)
- | ty::UnsafeBinder(_) => None,
-
- ty::Adt(adt_def, _) => {
- let did = adt_def.did();
- let try_local_did_span = |did: DefId| {
- if let Some(local) = did.as_local() {
- tcx.source_span(local)
- } else {
- tcx.def_span(did)
- }
- };
- let dtor = if let Some(dtor) = tcx.adt_destructor(did) {
- dtor.did
- } else if let Some(dtor) = tcx.adt_async_destructor(did) {
- dtor.future
- } else {
- return Some(try_local_did_span(did));
- };
- let def_key = tcx.def_key(dtor);
- let Some(parent_index) = def_key.parent else { return Some(try_local_did_span(dtor)) };
- let parent_did = DefId { index: parent_index, krate: dtor.krate };
- Some(try_local_did_span(parent_did))
- }
- ty::Coroutine(did, _)
- | ty::CoroutineWitness(did, _)
- | ty::CoroutineClosure(did, _)
- | ty::Closure(did, _)
- | ty::FnDef(did, _)
- | ty::Foreign(did) => Some(tcx.def_span(did)),
- ty::Param(_) => None,
- }
-}
-
/// Check if a moved place at `idx` is a part of a BID.
/// The use of this check is that we will consider drops on these
/// as a drop of the overall BID and, thus, we can exclude it from the diagnosis.
diff --git a/tests/ui/drop/drop-order-comparisons.e2021.stderr b/tests/ui/drop/drop-order-comparisons.e2021.stderr
index 158d18f68826b..601b0a38412f0 100644
--- a/tests/ui/drop/drop-order-comparisons.e2021.stderr
+++ b/tests/ui/drop/drop-order-comparisons.e2021.stderr
@@ -242,6 +242,15 @@ LL | _ = (if let Ok(_) = e.ok(4).as_ref() {
|
= warning: this changes meaning in Rust 2024
= note: for more information, see
+note: value invokes this custom destructor
+ --> $DIR/drop-order-comparisons.rs:571:1
+ |
+LL | / impl<'b> Drop for LogDrop<'b> {
+LL | | fn drop(&mut self) {
+LL | | self.0.mark(self.1);
+LL | | }
+LL | | }
+ | |_^
help: the value is now dropped here in Edition 2024
--> $DIR/drop-order-comparisons.rs:127:5
|
@@ -267,6 +276,15 @@ LL | _ = (if let Ok(_) = e.err(4).as_ref() {} else {
|
= warning: this changes meaning in Rust 2024
= note: for more information, see
+note: value invokes this custom destructor
+ --> $DIR/drop-order-comparisons.rs:571:1
+ |
+LL | / impl<'b> Drop for LogDrop<'b> {
+LL | | fn drop(&mut self) {
+LL | | self.0.mark(self.1);
+LL | | }
+LL | | }
+ | |_^
help: the value is now dropped here in Edition 2024
--> $DIR/drop-order-comparisons.rs:145:44
|
@@ -291,6 +309,15 @@ LL | if let Ok(_) = e.err(4).as_ref() {} else {
|
= warning: this changes meaning in Rust 2024
= note: for more information, see
+note: value invokes this custom destructor
+ --> $DIR/drop-order-comparisons.rs:571:1
+ |
+LL | / impl<'b> Drop for LogDrop<'b> {
+LL | | fn drop(&mut self) {
+LL | | self.0.mark(self.1);
+LL | | }
+LL | | }
+ | |_^
help: the value is now dropped here in Edition 2024
--> $DIR/drop-order-comparisons.rs:247:43
|
@@ -315,6 +342,15 @@ LL | if let true = e.err(9).is_ok() {} else {
|
= warning: this changes meaning in Rust 2024
= note: for more information, see
+note: value invokes this custom destructor
+ --> $DIR/drop-order-comparisons.rs:571:1
+ |
+LL | / impl<'b> Drop for LogDrop<'b> {
+LL | | fn drop(&mut self) {
+LL | | self.0.mark(self.1);
+LL | | }
+LL | | }
+ | |_^
help: the value is now dropped here in Edition 2024
--> $DIR/drop-order-comparisons.rs:352:41
|
@@ -339,6 +375,15 @@ LL | if let Ok(_v) = e.err(8) {} else {
|
= warning: this changes meaning in Rust 2024
= note: for more information, see
+note: value invokes this custom destructor
+ --> $DIR/drop-order-comparisons.rs:571:1
+ |
+LL | / impl<'b> Drop for LogDrop<'b> {
+LL | | fn drop(&mut self) {
+LL | | self.0.mark(self.1);
+LL | | }
+LL | | }
+ | |_^
help: the value is now dropped here in Edition 2024
--> $DIR/drop-order-comparisons.rs:355:35
|
@@ -363,6 +408,15 @@ LL | if let Ok(_) = e.err(7) {} else {
|
= warning: this changes meaning in Rust 2024
= note: for more information, see
+note: value invokes this custom destructor
+ --> $DIR/drop-order-comparisons.rs:571:1
+ |
+LL | / impl<'b> Drop for LogDrop<'b> {
+LL | | fn drop(&mut self) {
+LL | | self.0.mark(self.1);
+LL | | }
+LL | | }
+ | |_^
help: the value is now dropped here in Edition 2024
--> $DIR/drop-order-comparisons.rs:358:34
|
@@ -387,6 +441,15 @@ LL | if let Ok(_) = e.err(6).as_ref() {} else {
|
= warning: this changes meaning in Rust 2024
= note: for more information, see
+note: value invokes this custom destructor
+ --> $DIR/drop-order-comparisons.rs:571:1
+ |
+LL | / impl<'b> Drop for LogDrop<'b> {
+LL | | fn drop(&mut self) {
+LL | | self.0.mark(self.1);
+LL | | }
+LL | | }
+ | |_^
help: the value is now dropped here in Edition 2024
--> $DIR/drop-order-comparisons.rs:361:43
|
@@ -411,6 +474,15 @@ LL | if let Ok(_v) = e.err(5) {} else {
|
= warning: this changes meaning in Rust 2024
= note: for more information, see
+note: value invokes this custom destructor
+ --> $DIR/drop-order-comparisons.rs:571:1
+ |
+LL | / impl<'b> Drop for LogDrop<'b> {
+LL | | fn drop(&mut self) {
+LL | | self.0.mark(self.1);
+LL | | }
+LL | | }
+ | |_^
help: the value is now dropped here in Edition 2024
--> $DIR/drop-order-comparisons.rs:365:35
|
@@ -435,6 +507,15 @@ LL | if let Ok(_) = e.err(4) {} else {
|
= warning: this changes meaning in Rust 2024
= note: for more information, see
+note: value invokes this custom destructor
+ --> $DIR/drop-order-comparisons.rs:571:1
+ |
+LL | / impl<'b> Drop for LogDrop<'b> {
+LL | | fn drop(&mut self) {
+LL | | self.0.mark(self.1);
+LL | | }
+LL | | }
+ | |_^
help: the value is now dropped here in Edition 2024
--> $DIR/drop-order-comparisons.rs:368:34
|
@@ -459,6 +540,15 @@ LL | if let Ok(_) = e.err(4).as_ref() {} else {
|
= warning: this changes meaning in Rust 2024
= note: for more information, see
+note: value invokes this custom destructor
+ --> $DIR/drop-order-comparisons.rs:571:1
+ |
+LL | / impl<'b> Drop for LogDrop<'b> {
+LL | | fn drop(&mut self) {
+LL | | self.0.mark(self.1);
+LL | | }
+LL | | }
+ | |_^
help: the value is now dropped here in Edition 2024
--> $DIR/drop-order-comparisons.rs:404:43
|
diff --git a/tests/ui/drop/lint-if-let-rescope-gated.edition2021.stderr b/tests/ui/drop/lint-if-let-rescope-gated.edition2021.stderr
index 546a5fe0fd045..070ba1c6a4cfa 100644
--- a/tests/ui/drop/lint-if-let-rescope-gated.edition2021.stderr
+++ b/tests/ui/drop/lint-if-let-rescope-gated.edition2021.stderr
@@ -8,6 +8,15 @@ LL | if let Some(_value) = Droppy.get() {
|
= warning: this changes meaning in Rust 2024
= note: for more information, see
+note: value invokes this custom destructor
+ --> $DIR/lint-if-let-rescope-gated.rs:14:1
+ |
+LL | / impl Drop for Droppy {
+LL | | fn drop(&mut self) {
+LL | | println!("dropped");
+LL | | }
+LL | | }
+ | |_^
help: the value is now dropped here in Edition 2024
--> $DIR/lint-if-let-rescope-gated.rs:30:5
|
diff --git a/tests/ui/drop/lint-if-let-rescope-with-macro.stderr b/tests/ui/drop/lint-if-let-rescope-with-macro.stderr
index 029d5c7492936..f1ca0ba57de4f 100644
--- a/tests/ui/drop/lint-if-let-rescope-with-macro.stderr
+++ b/tests/ui/drop/lint-if-let-rescope-with-macro.stderr
@@ -15,6 +15,15 @@ LL | | };
|
= warning: this changes meaning in Rust 2024
= note: for more information, see
+note: value invokes this custom destructor
+ --> $DIR/lint-if-let-rescope-with-macro.rs:22:1
+ |
+LL | / impl Drop for Droppy {
+LL | | fn drop(&mut self) {
+LL | | println!("dropped");
+LL | | }
+LL | | }
+ | |_^
help: the value is now dropped here in Edition 2024
--> $DIR/lint-if-let-rescope-with-macro.rs:12:38
|
diff --git a/tests/ui/drop/lint-if-let-rescope.stderr b/tests/ui/drop/lint-if-let-rescope.stderr
index b17239976cc0d..e95ec8fcea7fe 100644
--- a/tests/ui/drop/lint-if-let-rescope.stderr
+++ b/tests/ui/drop/lint-if-let-rescope.stderr
@@ -8,6 +8,15 @@ LL | if let Some(_value) = droppy().get() {
|
= warning: this changes meaning in Rust 2024
= note: for more information, see
+note: value invokes this custom destructor
+ --> $DIR/lint-if-let-rescope.rs:11:1
+ |
+LL | / impl Drop for Droppy {
+LL | | fn drop(&mut self) {
+LL | | println!("dropped");
+LL | | }
+LL | | }
+ | |_^
help: the value is now dropped here in Edition 2024
--> $DIR/lint-if-let-rescope.rs:32:5
|
@@ -43,6 +52,24 @@ LL | } else if let Some(_value) = droppy().get() {
|
= warning: this changes meaning in Rust 2024
= note: for more information, see
+note: value invokes this custom destructor
+ --> $DIR/lint-if-let-rescope.rs:11:1
+ |
+LL | / impl Drop for Droppy {
+LL | | fn drop(&mut self) {
+LL | | println!("dropped");
+LL | | }
+LL | | }
+ | |_^
+note: value invokes this custom destructor
+ --> $DIR/lint-if-let-rescope.rs:11:1
+ |
+LL | / impl Drop for Droppy {
+LL | | fn drop(&mut self) {
+LL | | println!("dropped");
+LL | | }
+LL | | }
+ | |_^
help: the value is now dropped here in Edition 2024
--> $DIR/lint-if-let-rescope.rs:42:5
|
@@ -75,6 +102,15 @@ LL | } else if let Some(_value) = droppy().get() {
|
= warning: this changes meaning in Rust 2024
= note: for more information, see
+note: value invokes this custom destructor
+ --> $DIR/lint-if-let-rescope.rs:11:1
+ |
+LL | / impl Drop for Droppy {
+LL | | fn drop(&mut self) {
+LL | | println!("dropped");
+LL | | }
+LL | | }
+ | |_^
help: the value is now dropped here in Edition 2024
--> $DIR/lint-if-let-rescope.rs:54:5
|
@@ -101,6 +137,15 @@ LL | if let Some(1) = { if let Some(_value) = Droppy.get() { Some(1) } else
|
= warning: this changes meaning in Rust 2024
= note: for more information, see
+note: value invokes this custom destructor
+ --> $DIR/lint-if-let-rescope.rs:11:1
+ |
+LL | / impl Drop for Droppy {
+LL | | fn drop(&mut self) {
+LL | | println!("dropped");
+LL | | }
+LL | | }
+ | |_^
help: the value is now dropped here in Edition 2024
--> $DIR/lint-if-let-rescope.rs:58:69
|
@@ -122,6 +167,15 @@ LL | if (if let Some(_value) = droppy().get() { true } else { false }) {
|
= warning: this changes meaning in Rust 2024
= note: for more information, see
+note: value invokes this custom destructor
+ --> $DIR/lint-if-let-rescope.rs:11:1
+ |
+LL | / impl Drop for Droppy {
+LL | | fn drop(&mut self) {
+LL | | println!("dropped");
+LL | | }
+LL | | }
+ | |_^
help: the value is now dropped here in Edition 2024
--> $DIR/lint-if-let-rescope.rs:72:53
|
@@ -143,6 +197,15 @@ LL | } else if (((if let Some(_value) = droppy().get() { true } else { false
|
= warning: this changes meaning in Rust 2024
= note: for more information, see
+note: value invokes this custom destructor
+ --> $DIR/lint-if-let-rescope.rs:11:1
+ |
+LL | / impl Drop for Droppy {
+LL | | fn drop(&mut self) {
+LL | | println!("dropped");
+LL | | }
+LL | | }
+ | |_^
help: the value is now dropped here in Edition 2024
--> $DIR/lint-if-let-rescope.rs:78:62
|
@@ -164,6 +227,15 @@ LL | while (if let Some(_value) = droppy().get() { false } else { true }) {
|
= warning: this changes meaning in Rust 2024
= note: for more information, see
+note: value invokes this custom destructor
+ --> $DIR/lint-if-let-rescope.rs:11:1
+ |
+LL | / impl Drop for Droppy {
+LL | | fn drop(&mut self) {
+LL | | println!("dropped");
+LL | | }
+LL | | }
+ | |_^
help: the value is now dropped here in Edition 2024
--> $DIR/lint-if-let-rescope.rs:90:57
|
From a8364f3b2ad63c19037bdf060e49ad9ca07e16b6 Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote
Date: Thu, 19 Dec 2024 16:24:06 +1100
Subject: [PATCH 11/15] In `AssocOp::AssignOp`, use `BinOpKind` instead of
`BinOpToken`
`AssocOp::AssignOp` contains a `BinOpToken`. `ExprKind::AssignOp`
contains a `BinOpKind`. Given that `AssocOp` is basically a cut-down
version of `ExprKind`, it makes sense to make `AssocOp` more like
`ExprKind`. Especially given that `AssocOp` and `BinOpKind` use semantic
operation names (e.g. `Mul`, `Div`), but `BinOpToken` uses syntactic
names (e.g. `Star`, `Slash`).
This results in more concise code, and removes the need for various
conversions. (Note that the removed functions `hirbinop2assignop` and
`astbinop2assignop` are semantically identical, because `hir::BinOp` is
just a synonum for `ast::BinOp`!)
The only downside to this is that it allows the possibility of some
nonsensical combinations, such as `AssocOp::AssignOp(BinOpKind::Lt)`.
But `ExprKind::AssignOp` already has that problem. The problem can be
fixed for both types in the future with some effort, by introducing an
`AssignOpKind` type.
---
compiler/rustc_ast/src/util/parser.rs | 25 ++++++----
compiler/rustc_parse/src/parser/expr.rs | 16 +------
src/tools/clippy/clippy_utils/src/lib.rs | 1 -
src/tools/clippy/clippy_utils/src/sugg.rs | 58 ++---------------------
4 files changed, 23 insertions(+), 77 deletions(-)
diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs
index 8f2b7a23c01c8..69df9ff93b4f6 100644
--- a/compiler/rustc_ast/src/util/parser.rs
+++ b/compiler/rustc_ast/src/util/parser.rs
@@ -46,8 +46,8 @@ pub enum AssocOp {
GreaterEqual,
/// `=`
Assign,
- /// `?=` where ? is one of the BinOpToken
- AssignOp(BinOpToken),
+ /// `?=` where ? is one of the assignable BinOps
+ AssignOp(BinOpKind),
/// `as`
As,
/// `..` range
@@ -71,18 +71,27 @@ impl AssocOp {
pub fn from_token(t: &Token) -> Option {
use AssocOp::*;
match t.kind {
- token::BinOpEq(k) => Some(AssignOp(k)),
token::Eq => Some(Assign),
+ token::BinOpEq(BinOpToken::Plus) => Some(AssignOp(BinOpKind::Add)),
+ token::BinOpEq(BinOpToken::Minus) => Some(AssignOp(BinOpKind::Sub)),
+ token::BinOpEq(BinOpToken::Star) => Some(AssignOp(BinOpKind::Mul)),
+ token::BinOpEq(BinOpToken::Slash) => Some(AssignOp(BinOpKind::Div)),
+ token::BinOpEq(BinOpToken::Percent) => Some(AssignOp(BinOpKind::Rem)),
+ token::BinOpEq(BinOpToken::Caret) => Some(AssignOp(BinOpKind::BitXor)),
+ token::BinOpEq(BinOpToken::And) => Some(AssignOp(BinOpKind::BitAnd)),
+ token::BinOpEq(BinOpToken::Or) => Some(AssignOp(BinOpKind::BitOr)),
+ token::BinOpEq(BinOpToken::Shl) => Some(AssignOp(BinOpKind::Shl)),
+ token::BinOpEq(BinOpToken::Shr) => Some(AssignOp(BinOpKind::Shr)),
+ token::BinOp(BinOpToken::Plus) => Some(Add),
+ token::BinOp(BinOpToken::Minus) => Some(Subtract),
token::BinOp(BinOpToken::Star) => Some(Multiply),
token::BinOp(BinOpToken::Slash) => Some(Divide),
token::BinOp(BinOpToken::Percent) => Some(Modulus),
- token::BinOp(BinOpToken::Plus) => Some(Add),
- token::BinOp(BinOpToken::Minus) => Some(Subtract),
- token::BinOp(BinOpToken::Shl) => Some(ShiftLeft),
- token::BinOp(BinOpToken::Shr) => Some(ShiftRight),
- token::BinOp(BinOpToken::And) => Some(BitAnd),
token::BinOp(BinOpToken::Caret) => Some(BitXor),
+ token::BinOp(BinOpToken::And) => Some(BitAnd),
token::BinOp(BinOpToken::Or) => Some(BitOr),
+ token::BinOp(BinOpToken::Shl) => Some(ShiftLeft),
+ token::BinOp(BinOpToken::Shr) => Some(ShiftRight),
token::Lt => Some(Less),
token::Le => Some(LessEqual),
token::Ge => Some(GreaterEqual),
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index b2e58c9428064..935e13cedef82 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -313,19 +313,7 @@ impl<'a> Parser<'a> {
self.mk_expr(span, binary)
}
AssocOp::Assign => self.mk_expr(span, ExprKind::Assign(lhs, rhs, cur_op_span)),
- AssocOp::AssignOp(k) => {
- let aop = match k {
- token::Plus => BinOpKind::Add,
- token::Minus => BinOpKind::Sub,
- token::Star => BinOpKind::Mul,
- token::Slash => BinOpKind::Div,
- token::Percent => BinOpKind::Rem,
- token::Caret => BinOpKind::BitXor,
- token::And => BinOpKind::BitAnd,
- token::Or => BinOpKind::BitOr,
- token::Shl => BinOpKind::Shl,
- token::Shr => BinOpKind::Shr,
- };
+ AssocOp::AssignOp(aop) => {
let aopexpr = self.mk_assign_op(source_map::respan(cur_op_span, aop), lhs, rhs);
self.mk_expr(span, aopexpr)
}
@@ -395,7 +383,7 @@ impl<'a> Parser<'a> {
AssocOp::ShiftRight
| AssocOp::Greater
| AssocOp::GreaterEqual
- | AssocOp::AssignOp(token::BinOpToken::Shr),
+ | AssocOp::AssignOp(BinOpKind::Shr),
),
_,
) if self.restrictions.contains(Restrictions::CONST_EXPR) => {
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index 7fc25e3617d07..f7266ef4d4ebb 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -31,7 +31,6 @@
// (Currently there is no way to opt into sysroot crates without `extern crate`.)
extern crate rustc_abi;
extern crate rustc_ast;
-extern crate rustc_ast_pretty;
extern crate rustc_attr_parsing;
extern crate rustc_const_eval;
extern crate rustc_data_structures;
diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs
index d5e0e2e3436e2..ff5bcba5adb8f 100644
--- a/src/tools/clippy/clippy_utils/src/sugg.rs
+++ b/src/tools/clippy/clippy_utils/src/sugg.rs
@@ -5,8 +5,7 @@ use crate::source::{snippet, snippet_opt, snippet_with_applicability, snippet_wi
use crate::ty::expr_sig;
use crate::{get_parent_expr_for_hir, higher};
use rustc_ast::util::parser::AssocOp;
-use rustc_ast::{ast, token};
-use rustc_ast_pretty::pprust::token_kind_to_string;
+use rustc_ast::ast;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::{Closure, ExprKind, HirId, MutTy, TyKind};
@@ -158,7 +157,7 @@ impl<'a> Sugg<'a> {
Sugg::BinOp(AssocOp::Assign, get_snippet(lhs.span), get_snippet(rhs.span))
},
ExprKind::AssignOp(op, lhs, rhs) => {
- Sugg::BinOp(hirbinop2assignop(op), get_snippet(lhs.span), get_snippet(rhs.span))
+ Sugg::BinOp(AssocOp::AssignOp(op.node), get_snippet(lhs.span), get_snippet(rhs.span))
},
ExprKind::Binary(op, lhs, rhs) => Sugg::BinOp(
AssocOp::from_ast_binop(op.node),
@@ -245,7 +244,7 @@ impl<'a> Sugg<'a> {
snippet(rhs.span),
),
ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => Sugg::BinOp(
- astbinop2assignop(op),
+ AssocOp::AssignOp(op.node),
snippet(lhs.span),
snippet(rhs.span),
),
@@ -389,7 +388,7 @@ fn binop_to_string(op: AssocOp, lhs: &str, rhs: &str) -> String {
},
AssocOp::Assign => format!("{lhs} = {rhs}"),
AssocOp::AssignOp(op) => {
- format!("{lhs} {}= {rhs}", token_kind_to_string(&token::BinOp(op)))
+ format!("{lhs} {}= {rhs}", op.as_str())
},
AssocOp::As => format!("{lhs} as {rhs}"),
AssocOp::DotDot => format!("{lhs}..{rhs}"),
@@ -619,55 +618,6 @@ fn associativity(op: AssocOp) -> Associativity {
}
}
-/// Converts a `hir::BinOp` to the corresponding assigning binary operator.
-fn hirbinop2assignop(op: hir::BinOp) -> AssocOp {
- use rustc_ast::token::BinOpToken::{And, Caret, Minus, Or, Percent, Plus, Shl, Shr, Slash, Star};
-
- AssocOp::AssignOp(match op.node {
- hir::BinOpKind::Add => Plus,
- hir::BinOpKind::BitAnd => And,
- hir::BinOpKind::BitOr => Or,
- hir::BinOpKind::BitXor => Caret,
- hir::BinOpKind::Div => Slash,
- hir::BinOpKind::Mul => Star,
- hir::BinOpKind::Rem => Percent,
- hir::BinOpKind::Shl => Shl,
- hir::BinOpKind::Shr => Shr,
- hir::BinOpKind::Sub => Minus,
-
- hir::BinOpKind::And
- | hir::BinOpKind::Eq
- | hir::BinOpKind::Ge
- | hir::BinOpKind::Gt
- | hir::BinOpKind::Le
- | hir::BinOpKind::Lt
- | hir::BinOpKind::Ne
- | hir::BinOpKind::Or => panic!("This operator does not exist"),
- })
-}
-
-/// Converts an `ast::BinOp` to the corresponding assigning binary operator.
-fn astbinop2assignop(op: ast::BinOp) -> AssocOp {
- use rustc_ast::ast::BinOpKind::{
- Add, And, BitAnd, BitOr, BitXor, Div, Eq, Ge, Gt, Le, Lt, Mul, Ne, Or, Rem, Shl, Shr, Sub,
- };
- use rustc_ast::token::BinOpToken;
-
- AssocOp::AssignOp(match op.node {
- Add => BinOpToken::Plus,
- BitAnd => BinOpToken::And,
- BitOr => BinOpToken::Or,
- BitXor => BinOpToken::Caret,
- Div => BinOpToken::Slash,
- Mul => BinOpToken::Star,
- Rem => BinOpToken::Percent,
- Shl => BinOpToken::Shl,
- Shr => BinOpToken::Shr,
- Sub => BinOpToken::Minus,
- And | Eq | Ge | Gt | Le | Lt | Ne | Or => panic!("This operator does not exist"),
- })
-}
-
/// Returns the indentation before `span` if there are nothing but `[ \t]`
/// before it on its line.
fn indentation(cx: &T, span: Span) -> Option {
From ceafbad81fcf71423d4dc7a90fe962bc5895b108 Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote
Date: Thu, 19 Dec 2024 18:24:07 +1100
Subject: [PATCH 12/15] Introduce `AssocOp::Binary`.
It mirrors `ExprKind::Binary`, and contains a `BinOpKind`. This makes
`AssocOp` more like `ExprKind`. Note that the variants removed from
`AssocOp` are all named differently to `BinOpToken`, e.g. `Multiply`
instead of `Mul`, so that's an inconsistency removed.
The commit adds `precedence` and `fixity` methods to `BinOpKind`, and
calls them from the corresponding methods in `AssocOp`. This avoids the
need to create an `AssocOp` from a `BinOpKind` in a bunch of places, and
`AssocOp::from_ast_binop` is removed.
`AssocOp::to_ast_binop` is also no longer needed.
Overall things are shorter and nicer.
---
compiler/rustc_ast/src/ast.rs | 35 +++-
compiler/rustc_ast/src/util/parser.rs | 182 ++++--------------
.../rustc_ast_pretty/src/pprust/state/expr.rs | 7 +-
compiler/rustc_hir/src/hir.rs | 4 +-
compiler/rustc_hir_pretty/src/lib.rs | 7 +-
compiler/rustc_middle/src/ty/print/pretty.rs | 5 +-
.../rustc_parse/src/parser/diagnostics.rs | 36 ++--
compiler/rustc_parse/src/parser/expr.rs | 61 ++----
.../operators/float_equality_without_abs.rs | 2 +-
src/tools/clippy/clippy_utils/src/sugg.rs | 69 +++----
10 files changed, 152 insertions(+), 256 deletions(-)
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 29c1d34a125a4..65dd0a34aecad 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -39,7 +39,7 @@ pub use crate::format::*;
use crate::ptr::P;
use crate::token::{self, CommentKind, Delimiter};
use crate::tokenstream::{DelimSpan, LazyAttrTokenStream, TokenStream};
-use crate::util::parser::{AssocOp, ExprPrecedence};
+use crate::util::parser::{ExprPrecedence, Fixity};
/// A "Label" is an identifier of some point in sources,
/// e.g. in the following code:
@@ -937,8 +937,37 @@ impl BinOpKind {
matches!(self, BinOpKind::And | BinOpKind::Or)
}
+ pub fn precedence(&self) -> ExprPrecedence {
+ use BinOpKind::*;
+ match *self {
+ Mul | Div | Rem => ExprPrecedence::Product,
+ Add | Sub => ExprPrecedence::Sum,
+ Shl | Shr => ExprPrecedence::Shift,
+ BitAnd => ExprPrecedence::BitAnd,
+ BitXor => ExprPrecedence::BitXor,
+ BitOr => ExprPrecedence::BitOr,
+ Lt | Gt | Le | Ge | Eq | Ne => ExprPrecedence::Compare,
+ And => ExprPrecedence::LAnd,
+ Or => ExprPrecedence::LOr,
+ }
+ }
+
+ pub fn fixity(&self) -> Fixity {
+ use BinOpKind::*;
+ match self {
+ Eq | Ne | Lt | Le | Gt | Ge => Fixity::None,
+ Add | Sub | Mul | Div | Rem | And | Or | BitXor | BitAnd | BitOr | Shl | Shr => {
+ Fixity::Left
+ }
+ }
+ }
+
pub fn is_comparison(self) -> bool {
- crate::util::parser::AssocOp::from_ast_binop(self).is_comparison()
+ use BinOpKind::*;
+ match self {
+ Eq | Ne | Lt | Le | Gt | Ge => true,
+ Add | Sub | Mul | Div | Rem | And | Or | BitXor | BitAnd | BitOr | Shl | Shr => false,
+ }
}
/// Returns `true` if the binary operator takes its arguments by value.
@@ -1332,7 +1361,7 @@ impl Expr {
ExprKind::Range(..) => ExprPrecedence::Range,
// Binop-like expr kinds, handled by `AssocOp`.
- ExprKind::Binary(op, ..) => AssocOp::from_ast_binop(op.node).precedence(),
+ ExprKind::Binary(op, ..) => op.node.precedence(),
ExprKind::Cast(..) => ExprPrecedence::Cast,
ExprKind::Assign(..) |
diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs
index 69df9ff93b4f6..e7c26aa30928a 100644
--- a/compiler/rustc_ast/src/util/parser.rs
+++ b/compiler/rustc_ast/src/util/parser.rs
@@ -3,51 +3,15 @@ use rustc_span::kw;
use crate::ast::{self, BinOpKind};
use crate::token::{self, BinOpToken, Token};
-/// Associative operator with precedence.
-///
-/// This is the enum which specifies operator precedence and fixity to the parser.
+/// Associative operator.
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum AssocOp {
- /// `+`
- Add,
- /// `-`
- Subtract,
- /// `*`
- Multiply,
- /// `/`
- Divide,
- /// `%`
- Modulus,
- /// `&&`
- LAnd,
- /// `||`
- LOr,
- /// `^`
- BitXor,
- /// `&`
- BitAnd,
- /// `|`
- BitOr,
- /// `<<`
- ShiftLeft,
- /// `>>`
- ShiftRight,
- /// `==`
- Equal,
- /// `<`
- Less,
- /// `<=`
- LessEqual,
- /// `!=`
- NotEqual,
- /// `>`
- Greater,
- /// `>=`
- GreaterEqual,
- /// `=`
- Assign,
+ /// A binary op.
+ Binary(BinOpKind),
/// `?=` where ? is one of the assignable BinOps
AssignOp(BinOpKind),
+ /// `=`
+ Assign,
/// `as`
As,
/// `..` range
@@ -67,11 +31,21 @@ pub enum Fixity {
}
impl AssocOp {
- /// Creates a new AssocOP from a token
+ /// Creates a new AssocOp from a token.
pub fn from_token(t: &Token) -> Option {
use AssocOp::*;
match t.kind {
token::Eq => Some(Assign),
+ token::BinOp(BinOpToken::Plus) => Some(Binary(BinOpKind::Add)),
+ token::BinOp(BinOpToken::Minus) => Some(Binary(BinOpKind::Sub)),
+ token::BinOp(BinOpToken::Star) => Some(Binary(BinOpKind::Mul)),
+ token::BinOp(BinOpToken::Slash) => Some(Binary(BinOpKind::Div)),
+ token::BinOp(BinOpToken::Percent) => Some(Binary(BinOpKind::Rem)),
+ token::BinOp(BinOpToken::Caret) => Some(Binary(BinOpKind::BitXor)),
+ token::BinOp(BinOpToken::And) => Some(Binary(BinOpKind::BitAnd)),
+ token::BinOp(BinOpToken::Or) => Some(Binary(BinOpKind::BitOr)),
+ token::BinOp(BinOpToken::Shl) => Some(Binary(BinOpKind::Shl)),
+ token::BinOp(BinOpToken::Shr) => Some(Binary(BinOpKind::Shr)),
token::BinOpEq(BinOpToken::Plus) => Some(AssignOp(BinOpKind::Add)),
token::BinOpEq(BinOpToken::Minus) => Some(AssignOp(BinOpKind::Sub)),
token::BinOpEq(BinOpToken::Star) => Some(AssignOp(BinOpKind::Mul)),
@@ -82,74 +56,31 @@ impl AssocOp {
token::BinOpEq(BinOpToken::Or) => Some(AssignOp(BinOpKind::BitOr)),
token::BinOpEq(BinOpToken::Shl) => Some(AssignOp(BinOpKind::Shl)),
token::BinOpEq(BinOpToken::Shr) => Some(AssignOp(BinOpKind::Shr)),
- token::BinOp(BinOpToken::Plus) => Some(Add),
- token::BinOp(BinOpToken::Minus) => Some(Subtract),
- token::BinOp(BinOpToken::Star) => Some(Multiply),
- token::BinOp(BinOpToken::Slash) => Some(Divide),
- token::BinOp(BinOpToken::Percent) => Some(Modulus),
- token::BinOp(BinOpToken::Caret) => Some(BitXor),
- token::BinOp(BinOpToken::And) => Some(BitAnd),
- token::BinOp(BinOpToken::Or) => Some(BitOr),
- token::BinOp(BinOpToken::Shl) => Some(ShiftLeft),
- token::BinOp(BinOpToken::Shr) => Some(ShiftRight),
- token::Lt => Some(Less),
- token::Le => Some(LessEqual),
- token::Ge => Some(GreaterEqual),
- token::Gt => Some(Greater),
- token::EqEq => Some(Equal),
- token::Ne => Some(NotEqual),
- token::AndAnd => Some(LAnd),
- token::OrOr => Some(LOr),
+ token::Lt => Some(Binary(BinOpKind::Lt)),
+ token::Le => Some(Binary(BinOpKind::Le)),
+ token::Ge => Some(Binary(BinOpKind::Ge)),
+ token::Gt => Some(Binary(BinOpKind::Gt)),
+ token::EqEq => Some(Binary(BinOpKind::Eq)),
+ token::Ne => Some(Binary(BinOpKind::Ne)),
+ token::AndAnd => Some(Binary(BinOpKind::And)),
+ token::OrOr => Some(Binary(BinOpKind::Or)),
token::DotDot => Some(DotDot),
token::DotDotEq => Some(DotDotEq),
// DotDotDot is no longer supported, but we need some way to display the error
token::DotDotDot => Some(DotDotEq),
// `<-` should probably be `< -`
- token::LArrow => Some(Less),
+ token::LArrow => Some(Binary(BinOpKind::Lt)),
_ if t.is_keyword(kw::As) => Some(As),
_ => None,
}
}
- /// Creates a new AssocOp from ast::BinOpKind.
- pub fn from_ast_binop(op: BinOpKind) -> Self {
- use AssocOp::*;
- match op {
- BinOpKind::Lt => Less,
- BinOpKind::Gt => Greater,
- BinOpKind::Le => LessEqual,
- BinOpKind::Ge => GreaterEqual,
- BinOpKind::Eq => Equal,
- BinOpKind::Ne => NotEqual,
- BinOpKind::Mul => Multiply,
- BinOpKind::Div => Divide,
- BinOpKind::Rem => Modulus,
- BinOpKind::Add => Add,
- BinOpKind::Sub => Subtract,
- BinOpKind::Shl => ShiftLeft,
- BinOpKind::Shr => ShiftRight,
- BinOpKind::BitAnd => BitAnd,
- BinOpKind::BitXor => BitXor,
- BinOpKind::BitOr => BitOr,
- BinOpKind::And => LAnd,
- BinOpKind::Or => LOr,
- }
- }
-
/// Gets the precedence of this operator
pub fn precedence(&self) -> ExprPrecedence {
use AssocOp::*;
match *self {
As => ExprPrecedence::Cast,
- Multiply | Divide | Modulus => ExprPrecedence::Product,
- Add | Subtract => ExprPrecedence::Sum,
- ShiftLeft | ShiftRight => ExprPrecedence::Shift,
- BitAnd => ExprPrecedence::BitAnd,
- BitXor => ExprPrecedence::BitXor,
- BitOr => ExprPrecedence::BitOr,
- Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => ExprPrecedence::Compare,
- LAnd => ExprPrecedence::LAnd,
- LOr => ExprPrecedence::LOr,
+ Binary(bin_op) => bin_op.precedence(),
DotDot | DotDotEq => ExprPrecedence::Range,
Assign | AssignOp(_) => ExprPrecedence::Assign,
}
@@ -161,22 +92,17 @@ impl AssocOp {
// NOTE: it is a bug to have an operators that has same precedence but different fixities!
match *self {
Assign | AssignOp(_) => Fixity::Right,
- As | Multiply | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd
- | BitXor | BitOr | LAnd | LOr => Fixity::Left,
- Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | DotDot | DotDotEq => {
- Fixity::None
- }
+ Binary(binop) => binop.fixity(),
+ As => Fixity::Left,
+ DotDot | DotDotEq => Fixity::None,
}
}
pub fn is_comparison(&self) -> bool {
use AssocOp::*;
match *self {
- Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => true,
- Assign | AssignOp(_) | As | Multiply | Divide | Modulus | Add | Subtract
- | ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd | LOr | DotDot | DotDotEq => {
- false
- }
+ Binary(binop) => binop.is_comparison(),
+ Assign | AssignOp(_) | As | DotDot | DotDotEq => false,
}
}
@@ -184,34 +110,7 @@ impl AssocOp {
use AssocOp::*;
match *self {
Assign | AssignOp(_) => true,
- Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | As | Multiply
- | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd | BitXor
- | BitOr | LAnd | LOr | DotDot | DotDotEq => false,
- }
- }
-
- pub fn to_ast_binop(&self) -> Option {
- use AssocOp::*;
- match *self {
- Less => Some(BinOpKind::Lt),
- Greater => Some(BinOpKind::Gt),
- LessEqual => Some(BinOpKind::Le),
- GreaterEqual => Some(BinOpKind::Ge),
- Equal => Some(BinOpKind::Eq),
- NotEqual => Some(BinOpKind::Ne),
- Multiply => Some(BinOpKind::Mul),
- Divide => Some(BinOpKind::Div),
- Modulus => Some(BinOpKind::Rem),
- Add => Some(BinOpKind::Add),
- Subtract => Some(BinOpKind::Sub),
- ShiftLeft => Some(BinOpKind::Shl),
- ShiftRight => Some(BinOpKind::Shr),
- BitAnd => Some(BinOpKind::BitAnd),
- BitXor => Some(BinOpKind::BitXor),
- BitOr => Some(BinOpKind::BitOr),
- LAnd => Some(BinOpKind::And),
- LOr => Some(BinOpKind::Or),
- Assign | AssignOp(_) | As | DotDot | DotDotEq => None,
+ As | Binary(_) | DotDot | DotDotEq => false,
}
}
@@ -221,16 +120,19 @@ impl AssocOp {
/// parentheses while having a high degree of confidence on the correctness of the suggestion.
pub fn can_continue_expr_unambiguously(&self) -> bool {
use AssocOp::*;
+ use BinOpKind::*;
matches!(
self,
- BitXor | // `{ 42 } ^ 3`
Assign | // `{ 42 } = { 42 }`
- Divide | // `{ 42 } / 42`
- Modulus | // `{ 42 } % 2`
- ShiftRight | // `{ 42 } >> 2`
- LessEqual | // `{ 42 } <= 3`
- Greater | // `{ 42 } > 3`
- GreaterEqual | // `{ 42 } >= 3`
+ Binary(
+ BitXor | // `{ 42 } ^ 3`
+ Div | // `{ 42 } / 42`
+ Rem | // `{ 42 } % 2`
+ Shr | // `{ 42 } >> 2`
+ Le | // `{ 42 } <= 3`
+ Gt | // `{ 42 } > 3`
+ Ge // `{ 42 } >= 3`
+ ) |
AssignOp(_) | // `{ 42 } +=`
// Equal | // `{ 42 } == { 42 }` Accepting these here would regress incorrect
// NotEqual | // `{ 42 } != { 42 } struct literals parser recovery.
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
index 4b1374ceef31e..496323a35b8d9 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
@@ -5,7 +5,7 @@ use itertools::{Itertools, Position};
use rustc_ast::ptr::P;
use rustc_ast::util::classify;
use rustc_ast::util::literal::escape_byte_str_symbol;
-use rustc_ast::util::parser::{self, AssocOp, ExprPrecedence, Fixity};
+use rustc_ast::util::parser::{self, ExprPrecedence, Fixity};
use rustc_ast::{
self as ast, BlockCheckMode, FormatAlignment, FormatArgPosition, FormatArgsPiece, FormatCount,
FormatDebugHex, FormatSign, FormatTrait, token,
@@ -279,12 +279,11 @@ impl<'a> State<'a> {
rhs: &ast::Expr,
fixup: FixupContext,
) {
- let assoc_op = AssocOp::from_ast_binop(op.node);
- let binop_prec = assoc_op.precedence();
+ let binop_prec = op.node.precedence();
let left_prec = lhs.precedence();
let right_prec = rhs.precedence();
- let (mut left_needs_paren, right_needs_paren) = match assoc_op.fixity() {
+ let (mut left_needs_paren, right_needs_paren) = match op.node.fixity() {
Fixity::Left => (left_prec < binop_prec, right_prec <= binop_prec),
Fixity::Right => (left_prec <= binop_prec, right_prec < binop_prec),
Fixity::None => (left_prec <= binop_prec, right_prec <= binop_prec),
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index f0eaec55dbdd3..3c75bc588a138 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -4,7 +4,7 @@ use std::fmt;
use rustc_abi::ExternAbi;
use rustc_ast::attr::AttributeExt;
use rustc_ast::token::CommentKind;
-use rustc_ast::util::parser::{AssocOp, ExprPrecedence};
+use rustc_ast::util::parser::ExprPrecedence;
use rustc_ast::{
self as ast, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, IntTy, Label, LitIntType,
LitKind, TraitObjectSyntax, UintTy, UnsafeBinderCastKind,
@@ -2124,7 +2124,7 @@ impl Expr<'_> {
| ExprKind::Become(..) => ExprPrecedence::Jump,
// Binop-like expr kinds, handled by `AssocOp`.
- ExprKind::Binary(op, ..) => AssocOp::from_ast_binop(op.node).precedence(),
+ ExprKind::Binary(op, ..) => op.node.precedence(),
ExprKind::Cast(..) => ExprPrecedence::Cast,
ExprKind::Assign(..) |
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index 5c7426d76b31c..b3377b487694b 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -10,7 +10,7 @@ use std::cell::Cell;
use std::vec;
use rustc_abi::ExternAbi;
-use rustc_ast::util::parser::{self, AssocOp, ExprPrecedence, Fixity};
+use rustc_ast::util::parser::{self, ExprPrecedence, Fixity};
use rustc_ast::{AttrStyle, DUMMY_NODE_ID, DelimArgs};
use rustc_ast_pretty::pp::Breaks::{Consistent, Inconsistent};
use rustc_ast_pretty::pp::{self, Breaks};
@@ -1296,12 +1296,11 @@ impl<'a> State<'a> {
}
fn print_expr_binary(&mut self, op: hir::BinOp, lhs: &hir::Expr<'_>, rhs: &hir::Expr<'_>) {
- let assoc_op = AssocOp::from_ast_binop(op.node);
- let binop_prec = assoc_op.precedence();
+ let binop_prec = op.node.precedence();
let left_prec = lhs.precedence();
let right_prec = rhs.precedence();
- let (mut left_needs_paren, right_needs_paren) = match assoc_op.fixity() {
+ let (mut left_needs_paren, right_needs_paren) = match op.node.fixity() {
Fixity::Left => (left_prec < binop_prec, right_prec <= binop_prec),
Fixity::Right => (left_prec <= binop_prec, right_prec < binop_prec),
Fixity::None => (left_prec <= binop_prec, right_prec <= binop_prec),
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index ed0839f47e696..4a4e4a8c33d8b 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -1516,10 +1516,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
ty::ExprKind::Binop(op) => {
let (_, _, c1, c2) = expr.binop_args();
- let precedence = |binop: crate::mir::BinOp| {
- use rustc_ast::util::parser::AssocOp;
- AssocOp::from_ast_binop(binop.to_hir_binop()).precedence()
- };
+ let precedence = |binop: crate::mir::BinOp| binop.to_hir_binop().precedence();
let op_precedence = precedence(op);
let formatted_op = op.to_hir_binop().as_str();
let (lhs_parenthesized, rhs_parenthesized) = match (c1.kind(), c2.kind()) {
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index 67abc2d539401..b35a57ae49299 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -1350,13 +1350,13 @@ impl<'a> Parser<'a> {
}
return match (op.node, &outer_op.node) {
// `x == y == z`
- (BinOpKind::Eq, AssocOp::Equal) |
+ (BinOpKind::Eq, AssocOp::Binary(BinOpKind::Eq)) |
// `x < y < z` and friends.
- (BinOpKind::Lt, AssocOp::Less | AssocOp::LessEqual) |
- (BinOpKind::Le, AssocOp::LessEqual | AssocOp::Less) |
+ (BinOpKind::Lt, AssocOp::Binary(BinOpKind::Lt | BinOpKind::Le)) |
+ (BinOpKind::Le, AssocOp::Binary(BinOpKind::Lt | BinOpKind::Le)) |
// `x > y > z` and friends.
- (BinOpKind::Gt, AssocOp::Greater | AssocOp::GreaterEqual) |
- (BinOpKind::Ge, AssocOp::GreaterEqual | AssocOp::Greater) => {
+ (BinOpKind::Gt, AssocOp::Binary(BinOpKind::Gt | BinOpKind::Ge)) |
+ (BinOpKind::Ge, AssocOp::Binary(BinOpKind::Gt | BinOpKind::Ge)) => {
let expr_to_str = |e: &Expr| {
self.span_to_snippet(e.span)
.unwrap_or_else(|_| pprust::expr_to_string(e))
@@ -1368,7 +1368,10 @@ impl<'a> Parser<'a> {
false // Keep the current parse behavior, where the AST is `(x < y) < z`.
}
// `x == y < z`
- (BinOpKind::Eq, AssocOp::Less | AssocOp::LessEqual | AssocOp::Greater | AssocOp::GreaterEqual) => {
+ (
+ BinOpKind::Eq,
+ AssocOp::Binary(BinOpKind::Lt | BinOpKind::Le | BinOpKind::Gt | BinOpKind::Ge)
+ ) => {
// Consume `z`/outer-op-rhs.
let snapshot = self.create_snapshot_for_diagnostic();
match self.parse_expr() {
@@ -1389,7 +1392,10 @@ impl<'a> Parser<'a> {
}
}
// `x > y == z`
- (BinOpKind::Lt | BinOpKind::Le | BinOpKind::Gt | BinOpKind::Ge, AssocOp::Equal) => {
+ (
+ BinOpKind::Lt | BinOpKind::Le | BinOpKind::Gt | BinOpKind::Ge,
+ AssocOp::Binary(BinOpKind::Eq)
+ ) => {
let snapshot = self.create_snapshot_for_diagnostic();
// At this point it is always valid to enclose the lhs in parentheses, no
// further checks are necessary.
@@ -1457,10 +1463,10 @@ impl<'a> Parser<'a> {
// Include `<` to provide this recommendation even in a case like
// `Foo>>`
- if op.node == BinOpKind::Lt && outer_op.node == AssocOp::Less
- || outer_op.node == AssocOp::Greater
+ if op.node == BinOpKind::Lt && outer_op.node == AssocOp::Binary(BinOpKind::Lt)
+ || outer_op.node == AssocOp::Binary(BinOpKind::Gt)
{
- if outer_op.node == AssocOp::Less {
+ if outer_op.node == AssocOp::Binary(BinOpKind::Lt) {
let snapshot = self.create_snapshot_for_diagnostic();
self.bump();
// So far we have parsed `foo Parser<'a> {
) -> PResult<'a, GenericArg> {
let is_op_or_dot = AssocOp::from_token(&self.token)
.and_then(|op| {
- if let AssocOp::Greater
- | AssocOp::Less
- | AssocOp::ShiftRight
- | AssocOp::GreaterEqual
+ if let AssocOp::Binary(
+ BinOpKind::Gt
+ | BinOpKind::Lt
+ | BinOpKind::Shr
+ | BinOpKind::Ge
+ )
// Don't recover from `foo::`, because this could be an attempt to
// assign a value to a defaulted generic parameter.
| AssocOp::Assign
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 935e13cedef82..00a5f2fa28fbc 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -188,17 +188,12 @@ impl<'a> Parser<'a> {
}
// Look for JS' `===` and `!==` and recover
- if (op.node == AssocOp::Equal || op.node == AssocOp::NotEqual)
+ if let AssocOp::Binary(bop @ BinOpKind::Eq | bop @ BinOpKind::Ne) = op.node
&& self.token == token::Eq
&& self.prev_token.span.hi() == self.token.span.lo()
{
let sp = op.span.to(self.token.span);
- let sugg = match op.node {
- AssocOp::Equal => "==",
- AssocOp::NotEqual => "!=",
- _ => unreachable!(),
- }
- .into();
+ let sugg = bop.as_str().into();
let invalid = format!("{sugg}=");
self.dcx().emit_err(errors::InvalidComparisonOperator {
span: sp,
@@ -213,7 +208,7 @@ impl<'a> Parser<'a> {
}
// Look for PHP's `<>` and recover
- if op.node == AssocOp::Less
+ if op.node == AssocOp::Binary(BinOpKind::Lt)
&& self.token == token::Gt
&& self.prev_token.span.hi() == self.token.span.lo()
{
@@ -231,7 +226,7 @@ impl<'a> Parser<'a> {
}
// Look for C++'s `<=>` and recover
- if op.node == AssocOp::LessEqual
+ if op.node == AssocOp::Binary(BinOpKind::Le)
&& self.token == token::Gt
&& self.prev_token.span.hi() == self.token.span.lo()
{
@@ -290,25 +285,7 @@ impl<'a> Parser<'a> {
let span = self.mk_expr_sp(&lhs, lhs_span, rhs.span);
lhs = match op {
- AssocOp::Add
- | AssocOp::Subtract
- | AssocOp::Multiply
- | AssocOp::Divide
- | AssocOp::Modulus
- | AssocOp::LAnd
- | AssocOp::LOr
- | AssocOp::BitXor
- | AssocOp::BitAnd
- | AssocOp::BitOr
- | AssocOp::ShiftLeft
- | AssocOp::ShiftRight
- | AssocOp::Equal
- | AssocOp::Less
- | AssocOp::LessEqual
- | AssocOp::NotEqual
- | AssocOp::Greater
- | AssocOp::GreaterEqual => {
- let ast_op = op.to_ast_binop().unwrap();
+ AssocOp::Binary(ast_op) => {
let binary = self.mk_binary(source_map::respan(cur_op_span, ast_op), lhs, rhs);
self.mk_expr(span, binary)
}
@@ -335,13 +312,14 @@ impl<'a> Parser<'a> {
// An exhaustive check is done in the following block, but these are checked first
// because they *are* ambiguous but also reasonable looking incorrect syntax, so we
// want to keep their span info to improve diagnostics in these cases in a later stage.
- (true, Some(AssocOp::Multiply)) | // `{ 42 } *foo = bar;` or `{ 42 } * 3`
- (true, Some(AssocOp::Subtract)) | // `{ 42 } -5`
- (true, Some(AssocOp::Add)) | // `{ 42 } + 42` (unary plus)
- (true, Some(AssocOp::LAnd)) | // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }`
- (true, Some(AssocOp::LOr)) | // `{ 42 } || 42` ("logical or" or closure)
- (true, Some(AssocOp::BitOr)) // `{ 42 } | 42` or `{ 42 } |x| 42`
- => {
+ (true, Some(AssocOp::Binary(
+ BinOpKind::Mul | // `{ 42 } *foo = bar;` or `{ 42 } * 3`
+ BinOpKind::Sub | // `{ 42 } -5`
+ BinOpKind::Add | // `{ 42 } + 42` (unary plus)
+ BinOpKind::And | // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }`
+ BinOpKind::Or | // `{ 42 } || 42` ("logical or" or closure)
+ BinOpKind::BitOr // `{ 42 } | 42` or `{ 42 } |x| 42`
+ ))) => {
// These cases are ambiguous and can't be identified in the parser alone.
//
// Bitwise AND is left out because guessing intent is hard. We can make
@@ -380,21 +358,20 @@ impl<'a> Parser<'a> {
// When parsing const expressions, stop parsing when encountering `>`.
(
Some(
- AssocOp::ShiftRight
- | AssocOp::Greater
- | AssocOp::GreaterEqual
+ AssocOp::Binary(BinOpKind::Shr | BinOpKind::Gt | BinOpKind::Ge)
| AssocOp::AssignOp(BinOpKind::Shr),
),
_,
) if self.restrictions.contains(Restrictions::CONST_EXPR) => {
return None;
}
- // When recovering patterns as expressions, stop parsing when encountering an assignment `=`, an alternative `|`, or a range `..`.
+ // When recovering patterns as expressions, stop parsing when encountering an
+ // assignment `=`, an alternative `|`, or a range `..`.
(
Some(
AssocOp::Assign
| AssocOp::AssignOp(_)
- | AssocOp::BitOr
+ | AssocOp::Binary(BinOpKind::BitOr)
| AssocOp::DotDot
| AssocOp::DotDotEq,
),
@@ -411,7 +388,7 @@ impl<'a> Parser<'a> {
incorrect: "and".into(),
sub: errors::InvalidLogicalOperatorSub::Conjunction(self.token.span),
});
- (AssocOp::LAnd, span)
+ (AssocOp::Binary(BinOpKind::And), span)
}
(None, Some((Ident { name: sym::or, span }, IdentIsRaw::No))) if self.may_recover() => {
self.dcx().emit_err(errors::InvalidLogicalOperator {
@@ -419,7 +396,7 @@ impl<'a> Parser<'a> {
incorrect: "or".into(),
sub: errors::InvalidLogicalOperatorSub::Disjunction(self.token.span),
});
- (AssocOp::LOr, span)
+ (AssocOp::Binary(BinOpKind::Or), span)
}
_ => return None,
};
diff --git a/src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs b/src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs
index 34f7dbea84e49..74e0a6333db0f 100644
--- a/src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs
@@ -50,7 +50,7 @@ pub(crate) fn check<'tcx>(
// format the suggestion
let suggestion = format!(
"{}.abs()",
- sugg::make_assoc(AssocOp::Subtract, &sug_l, &sug_r).maybe_par()
+ sugg::make_assoc(AssocOp::Binary(BinOpKind::Sub), &sug_l, &sug_r).maybe_par()
);
// spans the lint
span_lint_and_then(
diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs
index ff5bcba5adb8f..6e16778b343de 100644
--- a/src/tools/clippy/clippy_utils/src/sugg.rs
+++ b/src/tools/clippy/clippy_utils/src/sugg.rs
@@ -160,7 +160,7 @@ impl<'a> Sugg<'a> {
Sugg::BinOp(AssocOp::AssignOp(op.node), get_snippet(lhs.span), get_snippet(rhs.span))
},
ExprKind::Binary(op, lhs, rhs) => Sugg::BinOp(
- AssocOp::from_ast_binop(op.node),
+ AssocOp::Binary(op.node),
get_snippet(lhs.span),
get_snippet(rhs.span),
),
@@ -249,7 +249,7 @@ impl<'a> Sugg<'a> {
snippet(rhs.span),
),
ast::ExprKind::Binary(op, ref lhs, ref rhs) => Sugg::BinOp(
- AssocOp::from_ast_binop(op.node),
+ AssocOp::Binary(op.node),
snippet(lhs.span),
snippet(rhs.span),
),
@@ -366,30 +366,9 @@ impl<'a> Sugg<'a> {
/// Generates a string from the operator and both sides.
fn binop_to_string(op: AssocOp, lhs: &str, rhs: &str) -> String {
match op {
- AssocOp::Add
- | AssocOp::Subtract
- | AssocOp::Multiply
- | AssocOp::Divide
- | AssocOp::Modulus
- | AssocOp::LAnd
- | AssocOp::LOr
- | AssocOp::BitXor
- | AssocOp::BitAnd
- | AssocOp::BitOr
- | AssocOp::ShiftLeft
- | AssocOp::ShiftRight
- | AssocOp::Equal
- | AssocOp::Less
- | AssocOp::LessEqual
- | AssocOp::NotEqual
- | AssocOp::Greater
- | AssocOp::GreaterEqual => {
- format!("{lhs} {} {rhs}", op.to_ast_binop().expect("Those are AST ops").as_str())
- },
+ AssocOp::Binary(op) => format!("{lhs} {} {rhs}", op.as_str()),
AssocOp::Assign => format!("{lhs} = {rhs}"),
- AssocOp::AssignOp(op) => {
- format!("{lhs} {}= {rhs}", op.as_str())
- },
+ AssocOp::AssignOp(op) => format!("{lhs} {}= {rhs}", op.as_str()),
AssocOp::As => format!("{lhs} as {rhs}"),
AssocOp::DotDot => format!("{lhs}..{rhs}"),
AssocOp::DotDotEq => format!("{lhs}..={rhs}"),
@@ -476,16 +455,17 @@ impl Neg for Sugg<'_> {
impl<'a> Not for Sugg<'a> {
type Output = Sugg<'a>;
fn not(self) -> Sugg<'a> {
- use AssocOp::{Equal, Greater, GreaterEqual, Less, LessEqual, NotEqual};
+ use AssocOp::Binary;
+ use ast::BinOpKind::{Eq, Gt, Ge, Lt, Le, Ne};
if let Sugg::BinOp(op, lhs, rhs) = self {
let to_op = match op {
- Equal => NotEqual,
- NotEqual => Equal,
- Less => GreaterEqual,
- GreaterEqual => Less,
- Greater => LessEqual,
- LessEqual => Greater,
+ Binary(Eq) => Binary(Ne),
+ Binary(Ne) => Binary(Eq),
+ Binary(Lt) => Binary(Ge),
+ Binary(Ge) => Binary(Lt),
+ Binary(Gt) => Binary(Le),
+ Binary(Le) => Binary(Gt),
_ => return make_unop("!", Sugg::BinOp(op, lhs, rhs)),
};
Sugg::BinOp(to_op, lhs, rhs)
@@ -537,7 +517,7 @@ pub fn make_unop(op: &str, expr: Sugg<'_>) -> Sugg<'static> {
pub fn make_assoc(op: AssocOp, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static> {
/// Returns `true` if the operator is a shift operator `<<` or `>>`.
fn is_shift(op: AssocOp) -> bool {
- matches!(op, AssocOp::ShiftLeft | AssocOp::ShiftRight)
+ matches!(op, AssocOp::Binary(ast::BinOpKind::Shl | ast::BinOpKind::Shr))
}
/// Returns `true` if the operator is an arithmetic operator
@@ -545,7 +525,13 @@ pub fn make_assoc(op: AssocOp, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static>
fn is_arith(op: AssocOp) -> bool {
matches!(
op,
- AssocOp::Add | AssocOp::Subtract | AssocOp::Multiply | AssocOp::Divide | AssocOp::Modulus
+ AssocOp::Binary(
+ ast::BinOpKind::Add
+ | ast::BinOpKind::Sub
+ | ast::BinOpKind::Mul
+ | ast::BinOpKind::Div
+ | ast::BinOpKind::Rem
+ )
)
}
@@ -577,9 +563,9 @@ pub fn make_assoc(op: AssocOp, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static>
Sugg::BinOp(op, lhs.into(), rhs.into())
}
-/// Convenience wrapper around `make_assoc` and `AssocOp::from_ast_binop`.
+/// Convenience wrapper around `make_assoc` and `AssocOp::Binary`.
pub fn make_binop(op: ast::BinOpKind, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static> {
- make_assoc(AssocOp::from_ast_binop(op), lhs, rhs)
+ make_assoc(AssocOp::Binary(op), lhs, rhs)
}
#[derive(PartialEq, Eq, Clone, Copy)]
@@ -604,16 +590,15 @@ enum Associativity {
/// associative.
#[must_use]
fn associativity(op: AssocOp) -> Associativity {
- use rustc_ast::util::parser::AssocOp::{
- Add, As, Assign, AssignOp, BitAnd, BitOr, BitXor, Divide, DotDot, DotDotEq, Equal, Greater, GreaterEqual, LAnd,
- LOr, Less, LessEqual, Modulus, Multiply, NotEqual, ShiftLeft, ShiftRight, Subtract,
+ use rustc_ast::util::parser::AssocOp::{As, Assign, AssignOp, Binary, DotDot, DotDotEq};
+ use ast::BinOpKind::{
+ Add, BitAnd, BitOr, BitXor, Div, Eq, Gt, Ge, And, Or, Lt, Le, Rem, Mul, Ne, Shl, Shr, Sub,
};
match op {
Assign | AssignOp(_) => Associativity::Right,
- Add | BitAnd | BitOr | BitXor | LAnd | LOr | Multiply | As => Associativity::Both,
- Divide | Equal | Greater | GreaterEqual | Less | LessEqual | Modulus | NotEqual | ShiftLeft | ShiftRight
- | Subtract => Associativity::Left,
+ Binary(Add | BitAnd | BitOr | BitXor | And | Or | Mul) | As => Associativity::Both,
+ Binary(Div | Eq | Gt | Ge | Lt | Le | Rem | Ne | Shl | Shr | Sub) => Associativity::Left,
DotDot | DotDotEq => Associativity::None,
}
}
From fc8e87b27418704316047eb15ff40382d8d847bc Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote
Date: Thu, 19 Dec 2024 21:42:46 +1100
Subject: [PATCH 13/15] Replace `AssocOp::DotDot{,Eq}` with `AssocOp::Range`.
It makes `AssocOp` more similar to `ExprKind` and makes things a little
simpler. And the semantic names make more sense here than the syntactic
names.
---
compiler/rustc_ast/src/ast.rs | 9 +++++++
compiler/rustc_ast/src/util/parser.rs | 21 +++++++---------
compiler/rustc_parse/src/parser/expr.rs | 15 +++++-------
src/tools/clippy/clippy_utils/src/sugg.rs | 30 ++++++-----------------
4 files changed, 32 insertions(+), 43 deletions(-)
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 65dd0a34aecad..5c44fda226241 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -1453,6 +1453,15 @@ pub enum RangeLimits {
Closed,
}
+impl RangeLimits {
+ pub fn as_str(&self) -> &'static str {
+ match self {
+ RangeLimits::HalfOpen => "..",
+ RangeLimits::Closed => "..=",
+ }
+ }
+}
+
/// A method call (e.g. `x.foo::(a, b, c)`).
#[derive(Clone, Encodable, Decodable, Debug)]
pub struct MethodCall {
diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs
index e7c26aa30928a..c610f933074d6 100644
--- a/compiler/rustc_ast/src/util/parser.rs
+++ b/compiler/rustc_ast/src/util/parser.rs
@@ -1,6 +1,6 @@
use rustc_span::kw;
-use crate::ast::{self, BinOpKind};
+use crate::ast::{self, BinOpKind, RangeLimits};
use crate::token::{self, BinOpToken, Token};
/// Associative operator.
@@ -14,10 +14,8 @@ pub enum AssocOp {
Assign,
/// `as`
As,
- /// `..` range
- DotDot,
- /// `..=` range
- DotDotEq,
+ /// `..` or `..=` range
+ Range(RangeLimits),
}
#[derive(PartialEq, Debug)]
@@ -64,10 +62,9 @@ impl AssocOp {
token::Ne => Some(Binary(BinOpKind::Ne)),
token::AndAnd => Some(Binary(BinOpKind::And)),
token::OrOr => Some(Binary(BinOpKind::Or)),
- token::DotDot => Some(DotDot),
- token::DotDotEq => Some(DotDotEq),
+ token::DotDot => Some(Range(RangeLimits::HalfOpen)),
// DotDotDot is no longer supported, but we need some way to display the error
- token::DotDotDot => Some(DotDotEq),
+ token::DotDotEq | token::DotDotDot => Some(Range(RangeLimits::Closed)),
// `<-` should probably be `< -`
token::LArrow => Some(Binary(BinOpKind::Lt)),
_ if t.is_keyword(kw::As) => Some(As),
@@ -81,7 +78,7 @@ impl AssocOp {
match *self {
As => ExprPrecedence::Cast,
Binary(bin_op) => bin_op.precedence(),
- DotDot | DotDotEq => ExprPrecedence::Range,
+ Range(_) => ExprPrecedence::Range,
Assign | AssignOp(_) => ExprPrecedence::Assign,
}
}
@@ -94,7 +91,7 @@ impl AssocOp {
Assign | AssignOp(_) => Fixity::Right,
Binary(binop) => binop.fixity(),
As => Fixity::Left,
- DotDot | DotDotEq => Fixity::None,
+ Range(_) => Fixity::None,
}
}
@@ -102,7 +99,7 @@ impl AssocOp {
use AssocOp::*;
match *self {
Binary(binop) => binop.is_comparison(),
- Assign | AssignOp(_) | As | DotDot | DotDotEq => false,
+ Assign | AssignOp(_) | As | Range(_) => false,
}
}
@@ -110,7 +107,7 @@ impl AssocOp {
use AssocOp::*;
match *self {
Assign | AssignOp(_) => true,
- As | Binary(_) | DotDot | DotDotEq => false,
+ As | Binary(_) | Range(_) => false,
}
}
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 00a5f2fa28fbc..50ce6fa1a906c 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -171,7 +171,7 @@ impl<'a> Parser<'a> {
break;
}
// Check for deprecated `...` syntax
- if self.token == token::DotDotDot && op.node == AssocOp::DotDotEq {
+ if self.token == token::DotDotDot && op.node == AssocOp::Range(RangeLimits::Closed) {
self.err_dotdotdot_syntax(self.token.span);
}
@@ -267,10 +267,10 @@ impl<'a> Parser<'a> {
if op == AssocOp::As {
lhs = self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Cast)?;
continue;
- } else if op == AssocOp::DotDot || op == AssocOp::DotDotEq {
+ } else if let AssocOp::Range(limits) = op {
// If we didn't have to handle `x..`/`x..=`, it would be pretty easy to
// generalise it to the Fixity::None code.
- lhs = self.parse_expr_range(prec, lhs, op, cur_op_span)?;
+ lhs = self.parse_expr_range(prec, lhs, limits, cur_op_span)?;
break;
}
@@ -294,7 +294,7 @@ impl<'a> Parser<'a> {
let aopexpr = self.mk_assign_op(source_map::respan(cur_op_span, aop), lhs, rhs);
self.mk_expr(span, aopexpr)
}
- AssocOp::As | AssocOp::DotDot | AssocOp::DotDotEq => {
+ AssocOp::As | AssocOp::Range(_) => {
self.dcx().span_bug(span, "AssocOp should have been handled by special case")
}
};
@@ -372,8 +372,7 @@ impl<'a> Parser<'a> {
AssocOp::Assign
| AssocOp::AssignOp(_)
| AssocOp::Binary(BinOpKind::BitOr)
- | AssocOp::DotDot
- | AssocOp::DotDotEq,
+ | AssocOp::Range(_),
),
_,
) if self.restrictions.contains(Restrictions::IS_PAT) => {
@@ -414,7 +413,7 @@ impl<'a> Parser<'a> {
&mut self,
prec: ExprPrecedence,
lhs: P,
- op: AssocOp,
+ limits: RangeLimits,
cur_op_span: Span,
) -> PResult<'a, P> {
let rhs = if self.is_at_start_of_range_notation_rhs() {
@@ -430,8 +429,6 @@ impl<'a> Parser<'a> {
};
let rhs_span = rhs.as_ref().map_or(cur_op_span, |x| x.span);
let span = self.mk_expr_sp(&lhs, lhs.span, rhs_span);
- let limits =
- if op == AssocOp::DotDot { RangeLimits::HalfOpen } else { RangeLimits::Closed };
let range = self.mk_range(Some(lhs), rhs, limits);
Ok(self.mk_expr(span, range))
}
diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs
index 6e16778b343de..fe5ae54fe50b8 100644
--- a/src/tools/clippy/clippy_utils/src/sugg.rs
+++ b/src/tools/clippy/clippy_utils/src/sugg.rs
@@ -113,10 +113,7 @@ impl<'a> Sugg<'a> {
/// function variants of `Sugg`, since these use different snippet functions.
fn hir_from_snippet(expr: &hir::Expr<'_>, mut get_snippet: impl FnMut(Span) -> Cow<'a, str>) -> Self {
if let Some(range) = higher::Range::hir(expr) {
- let op = match range.limits {
- ast::RangeLimits::HalfOpen => AssocOp::DotDot,
- ast::RangeLimits::Closed => AssocOp::DotDotEq,
- };
+ let op = AssocOp::Range(range.limits);
let start = range.start.map_or("".into(), |expr| get_snippet(expr.span));
let end = range.end.map_or("".into(), |expr| get_snippet(expr.span));
@@ -178,8 +175,6 @@ impl<'a> Sugg<'a> {
ctxt: SyntaxContext,
app: &mut Applicability,
) -> Self {
- use rustc_ast::ast::RangeLimits;
-
let mut snippet = |span: Span| snippet_with_context(cx, span, ctxt, default, app).0;
match expr.kind {
@@ -228,13 +223,8 @@ impl<'a> Sugg<'a> {
| ast::ExprKind::Err(_)
| ast::ExprKind::Dummy
| ast::ExprKind::UnsafeBinderCast(..) => Sugg::NonParen(snippet(expr.span)),
- ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::HalfOpen) => Sugg::BinOp(
- AssocOp::DotDot,
- lhs.as_ref().map_or("".into(), |lhs| snippet(lhs.span)),
- rhs.as_ref().map_or("".into(), |rhs| snippet(rhs.span)),
- ),
- ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::Closed) => Sugg::BinOp(
- AssocOp::DotDotEq,
+ ast::ExprKind::Range(ref lhs, ref rhs, limits) => Sugg::BinOp(
+ AssocOp::Range(limits),
lhs.as_ref().map_or("".into(), |lhs| snippet(lhs.span)),
rhs.as_ref().map_or("".into(), |rhs| snippet(rhs.span)),
),
@@ -326,11 +316,8 @@ impl<'a> Sugg<'a> {
/// Convenience method to create the `..` or `...`
/// suggestion.
- pub fn range(self, end: &Self, limit: ast::RangeLimits) -> Sugg<'static> {
- match limit {
- ast::RangeLimits::HalfOpen => make_assoc(AssocOp::DotDot, &self, end),
- ast::RangeLimits::Closed => make_assoc(AssocOp::DotDotEq, &self, end),
- }
+ pub fn range(self, end: &Self, limits: ast::RangeLimits) -> Sugg<'static> {
+ make_assoc(AssocOp::Range(limits), &self, end)
}
/// Adds parentheses to any expression that might need them. Suitable to the
@@ -370,8 +357,7 @@ fn binop_to_string(op: AssocOp, lhs: &str, rhs: &str) -> String {
AssocOp::Assign => format!("{lhs} = {rhs}"),
AssocOp::AssignOp(op) => format!("{lhs} {}= {rhs}", op.as_str()),
AssocOp::As => format!("{lhs} as {rhs}"),
- AssocOp::DotDot => format!("{lhs}..{rhs}"),
- AssocOp::DotDotEq => format!("{lhs}..={rhs}"),
+ AssocOp::Range(limits) => format!("{lhs}{}{rhs}", limits.as_str()),
}
}
@@ -590,7 +576,7 @@ enum Associativity {
/// associative.
#[must_use]
fn associativity(op: AssocOp) -> Associativity {
- use rustc_ast::util::parser::AssocOp::{As, Assign, AssignOp, Binary, DotDot, DotDotEq};
+ use rustc_ast::util::parser::AssocOp::{As, Assign, AssignOp, Binary, Range};
use ast::BinOpKind::{
Add, BitAnd, BitOr, BitXor, Div, Eq, Gt, Ge, And, Or, Lt, Le, Rem, Mul, Ne, Shl, Shr, Sub,
};
@@ -599,7 +585,7 @@ fn associativity(op: AssocOp) -> Associativity {
Assign | AssignOp(_) => Associativity::Right,
Binary(Add | BitAnd | BitOr | BitXor | And | Or | Mul) | As => Associativity::Both,
Binary(Div | Eq | Gt | Ge | Lt | Le | Rem | Ne | Shl | Shr | Sub) => Associativity::Left,
- DotDot | DotDotEq => Associativity::None,
+ Range(_) => Associativity::None,
}
}
From 2ac46f651789950b6bafabcd5621080c9ff649f7 Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote
Date: Thu, 19 Dec 2024 21:46:44 +1100
Subject: [PATCH 14/15] Rename `AssocOp::As` as `AssocOp::Cast`.
To match `ExprKind::Cast`, and because a semantic name makes more sense
here than a syntactic name.
---
compiler/rustc_ast/src/util/parser.rs | 14 +++++++-------
compiler/rustc_parse/src/parser/expr.rs | 4 ++--
src/tools/clippy/clippy_utils/src/sugg.rs | 14 +++++++-------
3 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs
index c610f933074d6..69454967eed62 100644
--- a/compiler/rustc_ast/src/util/parser.rs
+++ b/compiler/rustc_ast/src/util/parser.rs
@@ -13,7 +13,7 @@ pub enum AssocOp {
/// `=`
Assign,
/// `as`
- As,
+ Cast,
/// `..` or `..=` range
Range(RangeLimits),
}
@@ -67,7 +67,7 @@ impl AssocOp {
token::DotDotEq | token::DotDotDot => Some(Range(RangeLimits::Closed)),
// `<-` should probably be `< -`
token::LArrow => Some(Binary(BinOpKind::Lt)),
- _ if t.is_keyword(kw::As) => Some(As),
+ _ if t.is_keyword(kw::As) => Some(Cast),
_ => None,
}
}
@@ -76,7 +76,7 @@ impl AssocOp {
pub fn precedence(&self) -> ExprPrecedence {
use AssocOp::*;
match *self {
- As => ExprPrecedence::Cast,
+ Cast => ExprPrecedence::Cast,
Binary(bin_op) => bin_op.precedence(),
Range(_) => ExprPrecedence::Range,
Assign | AssignOp(_) => ExprPrecedence::Assign,
@@ -90,7 +90,7 @@ impl AssocOp {
match *self {
Assign | AssignOp(_) => Fixity::Right,
Binary(binop) => binop.fixity(),
- As => Fixity::Left,
+ Cast => Fixity::Left,
Range(_) => Fixity::None,
}
}
@@ -99,7 +99,7 @@ impl AssocOp {
use AssocOp::*;
match *self {
Binary(binop) => binop.is_comparison(),
- Assign | AssignOp(_) | As | Range(_) => false,
+ Assign | AssignOp(_) | Cast | Range(_) => false,
}
}
@@ -107,7 +107,7 @@ impl AssocOp {
use AssocOp::*;
match *self {
Assign | AssignOp(_) => true,
- As | Binary(_) | Range(_) => false,
+ Cast | Binary(_) | Range(_) => false,
}
}
@@ -133,7 +133,7 @@ impl AssocOp {
AssignOp(_) | // `{ 42 } +=`
// Equal | // `{ 42 } == { 42 }` Accepting these here would regress incorrect
// NotEqual | // `{ 42 } != { 42 } struct literals parser recovery.
- As // `{ 42 } as usize`
+ Cast // `{ 42 } as usize`
)
}
}
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 50ce6fa1a906c..c934c1e36d528 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -264,7 +264,7 @@ impl<'a> Parser<'a> {
let op = op.node;
// Special cases:
- if op == AssocOp::As {
+ if op == AssocOp::Cast {
lhs = self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Cast)?;
continue;
} else if let AssocOp::Range(limits) = op {
@@ -294,7 +294,7 @@ impl<'a> Parser<'a> {
let aopexpr = self.mk_assign_op(source_map::respan(cur_op_span, aop), lhs, rhs);
self.mk_expr(span, aopexpr)
}
- AssocOp::As | AssocOp::Range(_) => {
+ AssocOp::Cast | AssocOp::Range(_) => {
self.dcx().span_bug(span, "AssocOp should have been handled by special case")
}
};
diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs
index fe5ae54fe50b8..4a9ab17d4a609 100644
--- a/src/tools/clippy/clippy_utils/src/sugg.rs
+++ b/src/tools/clippy/clippy_utils/src/sugg.rs
@@ -163,7 +163,7 @@ impl<'a> Sugg<'a> {
),
ExprKind::Cast(lhs, ty) |
//FIXME(chenyukang), remove this after type ascription is removed from AST
- ExprKind::Type(lhs, ty) => Sugg::BinOp(AssocOp::As, get_snippet(lhs.span), get_snippet(ty.span)),
+ ExprKind::Type(lhs, ty) => Sugg::BinOp(AssocOp::Cast, get_snippet(lhs.span), get_snippet(ty.span)),
}
}
@@ -246,7 +246,7 @@ impl<'a> Sugg<'a> {
ast::ExprKind::Cast(ref lhs, ref ty) |
//FIXME(chenyukang), remove this after type ascription is removed from AST
ast::ExprKind::Type(ref lhs, ref ty) => Sugg::BinOp(
- AssocOp::As,
+ AssocOp::Cast,
snippet(lhs.span),
snippet(ty.span),
),
@@ -265,7 +265,7 @@ impl<'a> Sugg<'a> {
/// Convenience method to create the ` as ` suggestion.
pub fn as_ty(self, rhs: R) -> Sugg<'static> {
- make_assoc(AssocOp::As, &self, &Sugg::NonParen(rhs.to_string().into()))
+ make_assoc(AssocOp::Cast, &self, &Sugg::NonParen(rhs.to_string().into()))
}
/// Convenience method to create the `&` suggestion.
@@ -356,7 +356,7 @@ fn binop_to_string(op: AssocOp, lhs: &str, rhs: &str) -> String {
AssocOp::Binary(op) => format!("{lhs} {} {rhs}", op.as_str()),
AssocOp::Assign => format!("{lhs} = {rhs}"),
AssocOp::AssignOp(op) => format!("{lhs} {}= {rhs}", op.as_str()),
- AssocOp::As => format!("{lhs} as {rhs}"),
+ AssocOp::Cast => format!("{lhs} as {rhs}"),
AssocOp::Range(limits) => format!("{lhs}{}{rhs}", limits.as_str()),
}
}
@@ -432,7 +432,7 @@ impl Neg for Sugg<'_> {
type Output = Sugg<'static>;
fn neg(self) -> Sugg<'static> {
match &self {
- Self::BinOp(AssocOp::As, ..) => Sugg::MaybeParen(format!("-({self})").into()),
+ Self::BinOp(AssocOp::Cast, ..) => Sugg::MaybeParen(format!("-({self})").into()),
_ => make_unop("-", self),
}
}
@@ -576,14 +576,14 @@ enum Associativity {
/// associative.
#[must_use]
fn associativity(op: AssocOp) -> Associativity {
- use rustc_ast::util::parser::AssocOp::{As, Assign, AssignOp, Binary, Range};
+ use rustc_ast::util::parser::AssocOp::{Assign, AssignOp, Binary, Cast, Range};
use ast::BinOpKind::{
Add, BitAnd, BitOr, BitXor, Div, Eq, Gt, Ge, And, Or, Lt, Le, Rem, Mul, Ne, Shl, Shr, Sub,
};
match op {
Assign | AssignOp(_) => Associativity::Right,
- Binary(Add | BitAnd | BitOr | BitXor | And | Or | Mul) | As => Associativity::Both,
+ Binary(Add | BitAnd | BitOr | BitXor | And | Or | Mul) | Cast => Associativity::Both,
Binary(Div | Eq | Gt | Ge | Lt | Le | Rem | Ne | Shl | Shr | Sub) => Associativity::Left,
Range(_) => Associativity::None,
}
From ef66cbb27bc2a911edf4541e1c56102dc03e42d9 Mon Sep 17 00:00:00 2001
From: Deadbeef
Date: Fri, 7 Feb 2025 21:58:08 +0800
Subject: [PATCH 15/15] require trait impls to have matching const stabilities
as the traits
---
.../rustc_const_eval/src/check_consts/mod.rs | 5 ++
compiler/rustc_passes/messages.ftl | 6 ++
compiler/rustc_passes/src/errors.rs | 39 +++++++++++
compiler/rustc_passes/src/stability.rs | 60 ++++++++++++----
library/core/src/ops/arith.rs | 1 +
library/core/src/ops/deref.rs | 3 +
tests/ui/traits/const-traits/staged-api.rs | 32 +++++++++
.../ui/traits/const-traits/staged-api.stderr | 69 ++++++++++++++++++-
8 files changed, 200 insertions(+), 15 deletions(-)
diff --git a/compiler/rustc_const_eval/src/check_consts/mod.rs b/compiler/rustc_const_eval/src/check_consts/mod.rs
index 659d4a304563e..607cb2e497d8d 100644
--- a/compiler/rustc_const_eval/src/check_consts/mod.rs
+++ b/compiler/rustc_const_eval/src/check_consts/mod.rs
@@ -94,6 +94,11 @@ pub fn rustc_allow_const_fn_unstable(
/// world into two functions: those that are safe to expose on stable (and hence may not use
/// unstable features, not even recursively), and those that are not.
pub fn is_fn_or_trait_safe_to_expose_on_stable(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
+ // A default body in a `#[const_trait]` is const-stable when the trait is const-stable.
+ if tcx.is_const_default_method(def_id) {
+ return is_fn_or_trait_safe_to_expose_on_stable(tcx, tcx.parent(def_id));
+ }
+
match tcx.lookup_const_stability(def_id) {
None => {
// In a `staged_api` crate, we do enforce recursive const stability for all unmarked
diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl
index bc43580a7f00d..b65430c348027 100644
--- a/compiler/rustc_passes/messages.ftl
+++ b/compiler/rustc_passes/messages.ftl
@@ -725,6 +725,12 @@ passes_target_feature_on_statement =
.warn = {-passes_previously_accepted}
.label = {passes_should_be_applied_to_fn.label}
+passes_trait_impl_const_stability_mismatch = const stability on the impl does not match the const stability on the trait
+passes_trait_impl_const_stability_mismatch_impl_stable = this impl is (implicitly) stable...
+passes_trait_impl_const_stability_mismatch_impl_unstable = this impl is unstable...
+passes_trait_impl_const_stability_mismatch_trait_stable = ...but the trait is stable
+passes_trait_impl_const_stability_mismatch_trait_unstable = ...but the trait is unstable
+
passes_trait_impl_const_stable =
trait implementations cannot be const stable yet
.note = see issue #67792 for more information
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index 51b5861ee0ae6..9bb9b2353dc86 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -1552,6 +1552,45 @@ pub(crate) struct TraitImplConstStable {
pub span: Span,
}
+#[derive(Diagnostic)]
+#[diag(passes_trait_impl_const_stability_mismatch)]
+pub(crate) struct TraitImplConstStabilityMismatch {
+ #[primary_span]
+ pub span: Span,
+ #[subdiagnostic]
+ pub impl_stability: ImplConstStability,
+ #[subdiagnostic]
+ pub trait_stability: TraitConstStability,
+}
+
+#[derive(Subdiagnostic)]
+pub(crate) enum TraitConstStability {
+ #[note(passes_trait_impl_const_stability_mismatch_trait_stable)]
+ Stable {
+ #[primary_span]
+ span: Span,
+ },
+ #[note(passes_trait_impl_const_stability_mismatch_trait_unstable)]
+ Unstable {
+ #[primary_span]
+ span: Span,
+ },
+}
+
+#[derive(Subdiagnostic)]
+pub(crate) enum ImplConstStability {
+ #[note(passes_trait_impl_const_stability_mismatch_impl_stable)]
+ Stable {
+ #[primary_span]
+ span: Span,
+ },
+ #[note(passes_trait_impl_const_stability_mismatch_impl_unstable)]
+ Unstable {
+ #[primary_span]
+ span: Span,
+ },
+}
+
#[derive(Diagnostic)]
#[diag(passes_unknown_feature, code = E0635)]
pub(crate) struct UnknownFeature {
diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs
index 8a4bdf3875c4b..0fcf6a80ec4de 100644
--- a/compiler/rustc_passes/src/stability.rs
+++ b/compiler/rustc_passes/src/stability.rs
@@ -313,7 +313,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
.map(|(stab, _span)| ConstStability::from_partial(stab, const_stability_indirect));
// If this is a const fn but not annotated with stability markers, see if we can inherit regular stability.
- if fn_sig.is_some_and(|s| s.header.is_const()) && const_stab.is_none() &&
+ if fn_sig.is_some_and(|s| s.header.is_const()) && const_stab.is_none() &&
// We only ever inherit unstable features.
let Some(inherit_regular_stab) =
final_stab.filter(|s| s.is_unstable())
@@ -826,24 +826,56 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
}
}
- // `#![feature(const_trait_impl)]` is unstable, so any impl declared stable
- // needs to have an error emitted.
if features.const_trait_impl()
- && self.tcx.is_const_trait_impl(item.owner_id.to_def_id())
- && const_stab.is_some_and(|stab| stab.is_const_stable())
+ && let hir::Constness::Const = constness
{
- self.tcx.dcx().emit_err(errors::TraitImplConstStable { span: item.span });
+ let stable_or_implied_stable = match const_stab {
+ None => true,
+ Some(stab) if stab.is_const_stable() => {
+ // `#![feature(const_trait_impl)]` is unstable, so any impl declared stable
+ // needs to have an error emitted.
+ // Note: Remove this error once `const_trait_impl` is stabilized
+ self.tcx
+ .dcx()
+ .emit_err(errors::TraitImplConstStable { span: item.span });
+ true
+ }
+ Some(_) => false,
+ };
+
+ if let Some(trait_id) = t.trait_def_id()
+ && let Some(const_stab) = self.tcx.lookup_const_stability(trait_id)
+ {
+ // the const stability of a trait impl must match the const stability on the trait.
+ if const_stab.is_const_stable() != stable_or_implied_stable {
+ let trait_span = self.tcx.def_ident_span(trait_id).unwrap();
+
+ let impl_stability = if stable_or_implied_stable {
+ errors::ImplConstStability::Stable { span: item.span }
+ } else {
+ errors::ImplConstStability::Unstable { span: item.span }
+ };
+ let trait_stability = if const_stab.is_const_stable() {
+ errors::TraitConstStability::Stable { span: trait_span }
+ } else {
+ errors::TraitConstStability::Unstable { span: trait_span }
+ };
+
+ self.tcx.dcx().emit_err(errors::TraitImplConstStabilityMismatch {
+ span: item.span,
+ impl_stability,
+ trait_stability,
+ });
+ }
+ }
}
}
- match constness {
- rustc_hir::Constness::Const => {
- if let Some(def_id) = t.trait_def_id() {
- // FIXME(const_trait_impl): Improve the span here.
- self.tcx.check_const_stability(def_id, t.path.span, t.path.span);
- }
- }
- rustc_hir::Constness::NotConst => {}
+ if let hir::Constness::Const = constness
+ && let Some(def_id) = t.trait_def_id()
+ {
+ // FIXME(const_trait_impl): Improve the span here.
+ self.tcx.check_const_stability(def_id, t.path.span, t.path.span);
}
for impl_item_ref in *items {
diff --git a/library/core/src/ops/arith.rs b/library/core/src/ops/arith.rs
index fe7ff2d9ede6a..54d79beca95ab 100644
--- a/library/core/src/ops/arith.rs
+++ b/library/core/src/ops/arith.rs
@@ -96,6 +96,7 @@ pub trait Add {
macro_rules! add_impl {
($($t:ty)*) => ($(
#[stable(feature = "rust1", since = "1.0.0")]
+ #[rustc_const_unstable(feature = "const_ops", issue = "90080")]
impl const Add for $t {
type Output = $t;
diff --git a/library/core/src/ops/deref.rs b/library/core/src/ops/deref.rs
index 11490ea2bfcb4..e74f5443ac2d8 100644
--- a/library/core/src/ops/deref.rs
+++ b/library/core/src/ops/deref.rs
@@ -150,6 +150,7 @@ pub trait Deref {
}
#[stable(feature = "rust1", since = "1.0.0")]
+#[rustc_const_unstable(feature = "const_deref", issue = "88955")]
impl const Deref for &T {
type Target = T;
@@ -163,6 +164,7 @@ impl const Deref for &T {
impl !DerefMut for &T {}
#[stable(feature = "rust1", since = "1.0.0")]
+#[rustc_const_unstable(feature = "const_deref", issue = "88955")]
impl const Deref for &mut T {
type Target = T;
@@ -273,6 +275,7 @@ pub trait DerefMut: ~const Deref {
}
#[stable(feature = "rust1", since = "1.0.0")]
+#[rustc_const_unstable(feature = "const_deref", issue = "88955")]
impl const DerefMut for &mut T {
fn deref_mut(&mut self) -> &mut T {
*self
diff --git a/tests/ui/traits/const-traits/staged-api.rs b/tests/ui/traits/const-traits/staged-api.rs
index 9a030dafd6bc4..8dd7226fc298e 100644
--- a/tests/ui/traits/const-traits/staged-api.rs
+++ b/tests/ui/traits/const-traits/staged-api.rs
@@ -85,4 +85,36 @@ const fn implicitly_stable_const_context() {
//~^ ERROR cannot use `#[feature(const_trait_impl)]`
}
+// check that const stability of impls and traits must match
+#[const_trait]
+#[rustc_const_unstable(feature = "beef", issue = "none")]
+trait U {}
+
+#[const_trait]
+#[rustc_const_stable(since = "0.0.0", feature = "beef2")]
+trait S {}
+
+// implied stable
+impl const U for u8 {}
+//~^ const stability on the impl does not match the const stability on the trait
+
+#[rustc_const_stable(since = "0.0.0", feature = "beef2")]
+impl const U for u16 {}
+//~^ const stability on the impl does not match the const stability on the trait
+//~| trait implementations cannot be const stable yet
+
+#[rustc_const_unstable(feature = "beef", issue = "none")]
+impl const U for u32 {}
+
+// implied stable
+impl const S for u8 {}
+
+#[rustc_const_stable(since = "0.0.0", feature = "beef2")]
+impl const S for u16 {}
+//~^ trait implementations cannot be const stable yet
+
+#[rustc_const_unstable(feature = "beef", issue = "none")]
+impl const S for u32 {}
+//~^ const stability on the impl does not match the const stability on the trait
+
fn main() {}
diff --git a/tests/ui/traits/const-traits/staged-api.stderr b/tests/ui/traits/const-traits/staged-api.stderr
index a7a7a1ee7215b..cdf577287eec5 100644
--- a/tests/ui/traits/const-traits/staged-api.stderr
+++ b/tests/ui/traits/const-traits/staged-api.stderr
@@ -1,3 +1,70 @@
+error: const stability on the impl does not match the const stability on the trait
+ --> $DIR/staged-api.rs:98:1
+ |
+LL | impl const U for u8 {}
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: this impl is (implicitly) stable...
+ --> $DIR/staged-api.rs:98:1
+ |
+LL | impl const U for u8 {}
+ | ^^^^^^^^^^^^^^^^^^^^^^
+note: ...but the trait is unstable
+ --> $DIR/staged-api.rs:91:7
+ |
+LL | trait U {}
+ | ^
+
+error: trait implementations cannot be const stable yet
+ --> $DIR/staged-api.rs:102:1
+ |
+LL | impl const U for u16 {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: see issue #67792 for more information
+
+error: const stability on the impl does not match the const stability on the trait
+ --> $DIR/staged-api.rs:102:1
+ |
+LL | impl const U for u16 {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: this impl is (implicitly) stable...
+ --> $DIR/staged-api.rs:102:1
+ |
+LL | impl const U for u16 {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+note: ...but the trait is unstable
+ --> $DIR/staged-api.rs:91:7
+ |
+LL | trait U {}
+ | ^
+
+error: trait implementations cannot be const stable yet
+ --> $DIR/staged-api.rs:113:1
+ |
+LL | impl const S for u16 {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: see issue #67792 for more information
+
+error: const stability on the impl does not match the const stability on the trait
+ --> $DIR/staged-api.rs:117:1
+ |
+LL | impl const S for u32 {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: this impl is unstable...
+ --> $DIR/staged-api.rs:117:1
+ |
+LL | impl const S for u32 {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+note: ...but the trait is stable
+ --> $DIR/staged-api.rs:95:7
+ |
+LL | trait S {}
+ | ^
+
error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]`
--> $DIR/staged-api.rs:38:5
|
@@ -323,5 +390,5 @@ LL + #[rustc_allow_const_fn_unstable(const_trait_impl)]
LL | const fn implicitly_stable_const_context() {
|
-error: aborting due to 19 previous errors
+error: aborting due to 24 previous errors