diff options
| author | bors <bors@rust-lang.org> | 2024-04-16 11:22:35 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2024-04-16 11:22:35 +0000 |
| commit | 4e1f5d90bca45207605a88e39b1f76abcdb85d2f (patch) | |
| tree | c927952718135627a6c5e5bc68886e81b9f38f60 /compiler/rustc_hir_analysis | |
| parent | ad18fe08de03fbb459c05475bddee22707b4f0ec (diff) | |
| parent | 8ddd2805de6ad8200868befc6ae703243fac3a46 (diff) | |
| download | rust-4e1f5d90bca45207605a88e39b1f76abcdb85d2f.tar.gz rust-4e1f5d90bca45207605a88e39b1f76abcdb85d2f.zip | |
Auto merge of #123468 - compiler-errors:precise-capturing, r=oli-obk
Implement syntax for `impl Trait` to specify its captures explicitly (`feature(precise_capturing)`) Implements `impl use<'a, 'b, T, U> Sized` syntax that allows users to explicitly list the captured parameters for an opaque, rather than inferring it from the opaque's bounds (or capturing *all* lifetimes under 2024-edition capture rules). This allows us to exclude some implicit captures, so this syntax may be used as a migration strategy for changes due to #117587. We represent this list of captured params as `PreciseCapturingArg` in AST and HIR, resolving them between `rustc_resolve` and `resolve_bound_vars`. Later on, we validate that the opaques only capture the parameters in this list. We artificially limit the feature to *require* mentioning all type and const parameters, since we don't currently have support for non-lifetime bivariant generics. This can be relaxed in the future. We also may need to limit this to require naming *all* lifetime parameters for RPITIT, since GATs have no variance. I have to investigate this. This can also be relaxed in the future. r? `@oli-obk` Tracking issue: - https://github.com/rust-lang/rust/issues/123432
Diffstat (limited to 'compiler/rustc_hir_analysis')
| -rw-r--r-- | compiler/rustc_hir_analysis/messages.ftl | 19 | ||||
| -rw-r--r-- | compiler/rustc_hir_analysis/src/check/check.rs | 134 | ||||
| -rw-r--r-- | compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs | 44 | ||||
| -rw-r--r-- | compiler/rustc_hir_analysis/src/errors.rs | 3 | ||||
| -rw-r--r-- | compiler/rustc_hir_analysis/src/errors/precise_captures.rs | 63 |
5 files changed, 261 insertions, 2 deletions
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 86b8b6d6b2b..0ff78ebff99 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -37,6 +37,8 @@ hir_analysis_auto_deref_reached_recursion_limit = reached the recursion limit wh .label = deref recursion limit reached .help = consider increasing the recursion limit by adding a `#![recursion_limit = "{$suggested_limit}"]` attribute to your crate (`{$crate_name}`) +hir_analysis_bad_precise_capture = expected {$kind} parameter in `use<...>` precise captures list, found {$found} + hir_analysis_cannot_capture_late_bound_const = cannot capture late-bound const parameter in {$what} .label = parameter defined here @@ -111,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 @@ -214,6 +219,13 @@ 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 + hir_analysis_lifetimes_or_bounds_mismatch_on_trait = lifetime parameters or bounds on {$item_kind} `{$ident}` do not match the trait declaration .label = lifetimes do not match {$item_kind} in trait @@ -339,6 +351,10 @@ hir_analysis_param_in_ty_of_assoc_const_binding = *[normal] the {$param_def_kind} `{$param_name}` is defined here } +hir_analysis_param_not_captured = `impl Trait` must mention all {$kind} parameters in scope + .label = {$kind} parameter is implicitly captured by this `impl Trait` + .note = currently, all {$kind} parameters are required to be mentioned in the precise captures list + hir_analysis_paren_sugar_attribute = the `#[rustc_paren_sugar]` attribute is a temporary means of controlling which traits can use parenthetical notation .help = add `#![feature(unboxed_closures)]` to the crate attributes to use it @@ -355,6 +371,9 @@ hir_analysis_pattern_type_wild_pat = "wildcard patterns are not permitted for pa hir_analysis_placeholder_not_allowed_item_signatures = the placeholder `_` is not allowed within types on item signatures for {$kind} .label = not allowed in type signatures +hir_analysis_precise_capture_self_alias = `Self` can't be captured in `use<...>` precise captures list, since it is an alias + .label = `Self` is not a generic argument, but an alias to the type of the {$what} + hir_analysis_redundant_lifetime_args = unnecessary lifetime parameter `{$victim}` .note = you can use the `{$candidate}` lifetime directly, in place of `{$victim}` diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 216b89fd4f1..8c85d13650b 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1,10 +1,10 @@ use crate::check::intrinsicck::InlineAsmCtxt; -use crate::errors::LinkageType; 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::{UnordMap, UnordSet}; use rustc_errors::{codes::*, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind}; @@ -12,6 +12,7 @@ use rustc_hir::Node; use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt}; use rustc_infer::traits::{Obligation, TraitEngineExt as _}; use rustc_lint_defs::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS; +use rustc_middle::middle::resolve_bound_vars::ResolvedArg; use rustc_middle::middle::stability::EvalResult; use rustc_middle::traits::ObligationCauseCode; use rustc_middle::ty::fold::BottomUpFolder; @@ -474,6 +475,133 @@ fn sanity_check_found_hidden_type<'tcx>( } } +/// Check that the opaque's precise captures list is valid (if present). +/// We check this for regular `impl Trait`s and also RPITITs, even though the latter +/// are technically GATs. +/// +/// This function is responsible for: +/// 1. Checking that all type/const params are mention in the captures list. +/// 2. Checking that all lifetimes that are implicitly captured are mentioned. +/// 3. Asserting that all parameters mentioned in the captures list are invariant. +fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDefId) { + let hir::OpaqueTy { precise_capturing_args, .. } = + *tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty(); + let Some(precise_capturing_args) = precise_capturing_args else { + // No precise capturing args; nothing to validate + return; + }; + + 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 { + 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); + } + (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", + ); + } + } + } + + let variances = tcx.variances_of(opaque_def_id); + let mut def_id = Some(opaque_def_id.to_def_id()); + while let Some(generics) = def_id { + let generics = tcx.generics_of(generics); + def_id = generics.parent; + + for param in &generics.params { + if expected_captures.contains(¶m.def_id) { + assert_eq!( + variances[param.index as usize], + ty::Invariant, + "precise captured param should be invariant" + ); + continue; + } + + match param.kind { + ty::GenericParamDefKind::Lifetime => { + // Check if the lifetime param was captured but isn't named in the precise captures list. + if variances[param.index as usize] == ty::Invariant { + let param_span = + if let ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. }) + | ty::ReLateParam(ty::LateParamRegion { + bound_region: ty::BoundRegionKind::BrNamed(def_id, _), + .. + }) = *tcx + .map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local()) + { + Some(tcx.def_span(def_id)) + } else { + None + }; + // FIXME(precise_capturing): Structured suggestion for this would be useful + tcx.dcx().emit_err(errors::LifetimeNotCaptured { + use_span: tcx.def_span(param.def_id), + param_span, + opaque_span: tcx.def_span(opaque_def_id), + }); + continue; + } + } + ty::GenericParamDefKind::Type { .. } => { + // FIXME(precise_capturing): Structured suggestion for this would be useful + tcx.dcx().emit_err(errors::ParamNotCaptured { + param_span: tcx.def_span(param.def_id), + opaque_span: tcx.def_span(opaque_def_id), + kind: "type", + }); + } + ty::GenericParamDefKind::Const { .. } => { + // FIXME(precise_capturing): Structured suggestion for this would be useful + tcx.dcx().emit_err(errors::ParamNotCaptured { + param_span: tcx.def_span(param.def_id), + opaque_span: tcx.def_span(opaque_def_id), + kind: "const", + }); + } + } + } + } +} + fn is_enum_of_nonnullable_ptr<'tcx>( tcx: TyCtxt<'tcx>, adt_def: AdtDef<'tcx>, @@ -499,7 +627,7 @@ fn check_static_linkage(tcx: TyCtxt<'_>, def_id: LocalDefId) { ty::Adt(adt_def, args) => !is_enum_of_nonnullable_ptr(tcx, *adt_def, *args), _ => true, } { - tcx.dcx().emit_err(LinkageType { span: tcx.def_span(def_id) }); + tcx.dcx().emit_err(errors::LinkageType { span: tcx.def_span(def_id) }); } } } @@ -566,6 +694,8 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) { check_union(tcx, def_id); } DefKind::OpaqueTy => { + check_opaque_precise_captures(tcx, def_id); + let origin = tcx.opaque_type_origin(def_id); if let hir::OpaqueTyOrigin::FnReturn(fn_def_id) | hir::OpaqueTyOrigin::AsyncFn(fn_def_id) = origin diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 3d16f1420d9..78c2665fd1f 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -557,6 +557,50 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { } } + fn visit_precise_capturing_arg( + &mut self, + arg: &'tcx hir::PreciseCapturingArg<'tcx>, + ) -> Self::Result { + match *arg { + hir::PreciseCapturingArg::Lifetime(lt) => match lt.res { + LifetimeName::Param(def_id) => { + self.resolve_lifetime_ref(def_id, lt); + } + LifetimeName::Error => {} + LifetimeName::ImplicitObjectLifetimeDefault + | LifetimeName::Infer + | LifetimeName::Static => { + self.tcx.dcx().emit_err(errors::BadPreciseCapture { + span: lt.ident.span, + kind: "lifetime", + found: format!("`{}`", lt.ident.name), + }); + } + }, + hir::PreciseCapturingArg::Param(param) => match param.res { + Res::Def(DefKind::TyParam | DefKind::ConstParam, def_id) + | Res::SelfTyParam { trait_: def_id } => { + self.resolve_type_ref(def_id.expect_local(), param.hir_id); + } + Res::Err => {} + Res::SelfTyAlias { alias_to, .. } => { + self.tcx.dcx().emit_err(errors::PreciseCaptureSelfAlias { + span: param.ident.span, + self_span: self.tcx.def_span(alias_to), + what: self.tcx.def_descr(alias_to), + }); + } + res => { + self.tcx.dcx().emit_err(errors::BadPreciseCapture { + span: param.ident.span, + kind: "type or const", + found: res.descr().to_string(), + }); + } + }, + } + } + fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'tcx>) { match item.kind { hir::ForeignItemKind::Fn(_, _, generics) => { diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index d129614e0e1..867ee772a30 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -10,6 +10,9 @@ use rustc_span::{symbol::Ident, Span, Symbol}; mod pattern_types; pub use pattern_types::*; +mod precise_captures; +pub(crate) use precise_captures::*; + #[derive(Diagnostic)] #[diag(hir_analysis_ambiguous_assoc_item)] pub struct AmbiguousAssocItem<'a> { diff --git a/compiler/rustc_hir_analysis/src/errors/precise_captures.rs b/compiler/rustc_hir_analysis/src/errors/precise_captures.rs new file mode 100644 index 00000000000..520bf1d9f40 --- /dev/null +++ b/compiler/rustc_hir_analysis/src/errors/precise_captures.rs @@ -0,0 +1,63 @@ +use rustc_macros::Diagnostic; +use rustc_span::{Span, Symbol}; + +#[derive(Diagnostic)] +#[diag(hir_analysis_param_not_captured)] +#[note] +pub struct ParamNotCaptured { + #[primary_span] + pub param_span: Span, + #[label] + pub opaque_span: Span, + pub kind: &'static str, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_lifetime_not_captured)] +pub struct LifetimeNotCaptured { + #[primary_span] + pub use_span: Span, + #[label(hir_analysis_param_label)] + pub param_span: Option<Span>, + #[label] + pub opaque_span: Span, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_bad_precise_capture)] +pub struct BadPreciseCapture { + #[primary_span] + pub span: Span, + pub kind: &'static str, + pub found: String, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_precise_capture_self_alias)] +pub struct PreciseCaptureSelfAlias { + #[primary_span] + pub span: Span, + #[label] + pub self_span: Span, + pub what: &'static str, +} + +#[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, +} |
