From ce8961039e244b1e4e0fa02fc10d59a22abc9ea3 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 4 Apr 2024 15:06:30 -0400 Subject: [PATCH] Some ordering and duplication checks --- compiler/rustc_hir_analysis/messages.ftl | 6 ++ .../rustc_hir_analysis/src/check/check.rs | 57 ++++++++++++++----- .../src/errors/precise_captures.rs | 22 ++++++- .../feature-gate-precise-capturing.rs | 4 ++ .../feature-gate-precise-capturing.stderr | 13 +++++ .../impl-trait/precise-capturing/ordering.rs | 16 ++++++ .../precise-capturing/ordering.stderr | 37 ++++++++++++ 7 files changed, 140 insertions(+), 15 deletions(-) create mode 100644 tests/ui/feature-gates/feature-gate-precise-capturing.rs create mode 100644 tests/ui/feature-gates/feature-gate-precise-capturing.stderr create mode 100644 tests/ui/impl-trait/precise-capturing/ordering.rs create mode 100644 tests/ui/impl-trait/precise-capturing/ordering.stderr diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 81cf3067a1e..4ac2965bd5f 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -113,6 +113,9 @@ hir_analysis_drop_impl_on_wrong_item = hir_analysis_drop_impl_reservation = reservation `Drop` impls are not supported +hir_analysis_duplicate_precise_capture = cannot capture parameter `{$name}` twice + .label = parameter captured again here + hir_analysis_empty_specialization = specialization impl does not specialize any associated items .note = impl is a specialization of this impl @@ -216,6 +219,9 @@ hir_analysis_late_bound_lifetime_in_apit = `impl Trait` can only mention lifetim hir_analysis_late_bound_type_in_apit = `impl Trait` can only mention type parameters from an fn or impl .label = type parameter declared here +hir_analysis_lifetime_must_be_first = lifetime parameter `{$name}` must be listed before non-lifetime parameters + .label = move the lifetime before this parameter + hir_analysis_lifetime_not_captured = `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list .label = lifetime captured due to being mentioned in the bounds of the `impl Trait` .param_label = this lifetime parameter is captured diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 592a8648f14..1e8cd50ca0d 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -4,7 +4,7 @@ use super::compare_impl_item::check_type_bounds; use super::compare_impl_item::{compare_impl_method, compare_impl_ty}; use super::*; use rustc_attr as attr; -use rustc_data_structures::unord::UnordSet; +use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::{codes::*, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind}; @@ -484,22 +484,51 @@ fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDe }; let mut expected_captures = UnordSet::default(); + let mut seen_params = UnordMap::default(); + let mut prev_non_lifetime_param = None; for arg in precise_capturing_args { - match *arg { - hir::PreciseCapturingArg::Lifetime(&hir::Lifetime { hir_id, .. }) - | hir::PreciseCapturingArg::Param(hir::PreciseCapturingNonLifetimeArg { - hir_id, .. - }) => match tcx.named_bound_var(hir_id) { - Some(ResolvedArg::EarlyBound(def_id)) => { - expected_captures.insert(def_id); + let (hir_id, ident) = match *arg { + hir::PreciseCapturingArg::Param(hir::PreciseCapturingNonLifetimeArg { + hir_id, + ident, + .. + }) => { + if prev_non_lifetime_param.is_none() { + prev_non_lifetime_param = Some(ident); } - _ => { - tcx.dcx().span_delayed_bug( - tcx.hir().span(hir_id), - "parameter should have been resolved", - ); + (hir_id, ident) + } + hir::PreciseCapturingArg::Lifetime(&hir::Lifetime { hir_id, ident, .. }) => { + if let Some(prev_non_lifetime_param) = prev_non_lifetime_param { + tcx.dcx().emit_err(errors::LifetimesMustBeFirst { + lifetime_span: ident.span, + name: ident.name, + other_span: prev_non_lifetime_param.span, + }); } - }, + (hir_id, ident) + } + }; + + let ident = ident.normalize_to_macros_2_0(); + if let Some(span) = seen_params.insert(ident, ident.span) { + tcx.dcx().emit_err(errors::DuplicatePreciseCapture { + name: ident.name, + first_span: span, + second_span: ident.span, + }); + } + + match tcx.named_bound_var(hir_id) { + Some(ResolvedArg::EarlyBound(def_id)) => { + expected_captures.insert(def_id); + } + _ => { + tcx.dcx().span_delayed_bug( + tcx.hir().span(hir_id), + "parameter should have been resolved", + ); + } } } diff --git a/compiler/rustc_hir_analysis/src/errors/precise_captures.rs b/compiler/rustc_hir_analysis/src/errors/precise_captures.rs index 3b22437abb2..e2eb9c72bf2 100644 --- a/compiler/rustc_hir_analysis/src/errors/precise_captures.rs +++ b/compiler/rustc_hir_analysis/src/errors/precise_captures.rs @@ -1,5 +1,5 @@ use rustc_macros::Diagnostic; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; #[derive(Diagnostic)] #[diag(hir_analysis_param_not_captured)] @@ -31,3 +31,23 @@ pub struct BadPreciseCapture { pub kind: &'static str, pub found: String, } + +#[derive(Diagnostic)] +#[diag(hir_analysis_duplicate_precise_capture)] +pub struct DuplicatePreciseCapture { + #[primary_span] + pub first_span: Span, + pub name: Symbol, + #[label] + pub second_span: Span, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_lifetime_must_be_first)] +pub struct LifetimesMustBeFirst { + #[primary_span] + pub lifetime_span: Span, + pub name: Symbol, + #[label] + pub other_span: Span, +} diff --git a/tests/ui/feature-gates/feature-gate-precise-capturing.rs b/tests/ui/feature-gates/feature-gate-precise-capturing.rs new file mode 100644 index 00000000000..0c3b4977623 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-precise-capturing.rs @@ -0,0 +1,4 @@ +fn hello() -> impl use<> Sized {} +//~^ ERROR precise captures on `impl Trait` are experimental + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-precise-capturing.stderr b/tests/ui/feature-gates/feature-gate-precise-capturing.stderr new file mode 100644 index 00000000000..102b39148f9 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-precise-capturing.stderr @@ -0,0 +1,13 @@ +error[E0658]: precise captures on `impl Trait` are experimental + --> $DIR/feature-gate-precise-capturing.rs:1:20 + | +LL | fn hello() -> impl use<> Sized {} + | ^^^ + | + = note: see issue #123432 for more information + = help: add `#![feature(precise_capturing)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/impl-trait/precise-capturing/ordering.rs b/tests/ui/impl-trait/precise-capturing/ordering.rs new file mode 100644 index 00000000000..2bace798c57 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/ordering.rs @@ -0,0 +1,16 @@ +#![feature(precise_capturing)] +//~^ WARN the feature `precise_capturing` is incomplete + +fn lt<'a>() -> impl use<'a, 'a> Sized {} +//~^ ERROR cannot capture parameter `'a` twice + +fn ty() -> impl use Sized {} +//~^ ERROR cannot capture parameter `T` twice + +fn ct() -> impl use Sized {} +//~^ ERROR cannot capture parameter `N` twice + +fn ordering<'a, T>() -> impl use Sized {} +//~^ ERROR lifetime parameter `'a` must be listed before non-lifetime parameters + +fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/ordering.stderr b/tests/ui/impl-trait/precise-capturing/ordering.stderr new file mode 100644 index 00000000000..3f545108df5 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/ordering.stderr @@ -0,0 +1,37 @@ +warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/ordering.rs:1:12 + | +LL | #![feature(precise_capturing)] + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #123432 for more information + = note: `#[warn(incomplete_features)]` on by default + +error: cannot capture parameter `'a` twice + --> $DIR/ordering.rs:4:25 + | +LL | fn lt<'a>() -> impl use<'a, 'a> Sized {} + | ^^ -- parameter captured again here + +error: cannot capture parameter `T` twice + --> $DIR/ordering.rs:7:24 + | +LL | fn ty() -> impl use Sized {} + | ^ - parameter captured again here + +error: cannot capture parameter `N` twice + --> $DIR/ordering.rs:10:37 + | +LL | fn ct() -> impl use Sized {} + | ^ - parameter captured again here + +error: lifetime parameter `'a` must be listed before non-lifetime parameters + --> $DIR/ordering.rs:13:37 + | +LL | fn ordering<'a, T>() -> impl use Sized {} + | - ^^ + | | + | move the lifetime before this parameter + +error: aborting due to 4 previous errors; 1 warning emitted +