diff --git a/compiler/rustc_borrowck/messages.ftl b/compiler/rustc_borrowck/messages.ftl index ada20e5c614f..33b80c4b03d6 100644 --- a/compiler/rustc_borrowck/messages.ftl +++ b/compiler/rustc_borrowck/messages.ftl @@ -162,13 +162,6 @@ borrowck_opaque_type_lifetime_mismatch = .prev_lifetime_label = lifetime `{$prev}` previously used here .note = if all non-lifetime generic parameters are the same, but the lifetime parameters differ, it is not possible to differentiate the opaque types -borrowck_opaque_type_non_generic_param = - expected generic {$kind} parameter, found `{$ty}` - .label = {STREQ($ty, "'static") -> - [true] cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type - *[other] this generic parameter must be used with a generic {$kind} parameter - } - borrowck_partial_var_move_by_use_in_closure = variable {$is_partial -> [true] partially moved diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index ca8b9fb4e9d8..2f15c2bc4644 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -1,22 +1,16 @@ use rustc_data_structures::fx::FxIndexMap; -use rustc_errors::ErrorGuaranteed; -use rustc_hir::OpaqueTyOrigin; -use rustc_hir::def_id::LocalDefId; -use rustc_infer::infer::outlives::env::OutlivesEnvironment; -use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, TyCtxtInferExt as _}; +use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin}; use rustc_macros::extension; use rustc_middle::ty::{ - self, GenericArgKind, GenericArgs, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, - TypeVisitableExt, TypingMode, fold_regions, + self, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, fold_regions, }; use rustc_span::Span; -use rustc_trait_selection::regions::OutlivesEnvironmentBuildExt; -use rustc_trait_selection::traits::ObligationCtxt; +use rustc_trait_selection::opaque_types::check_opaque_type_parameter_valid; use tracing::{debug, instrument}; use super::RegionInferenceContext; use crate::opaque_types::ConcreteOpaqueTypes; -use crate::session_diagnostics::{LifetimeMismatchOpaqueParam, NonGenericOpaqueTypeParam}; +use crate::session_diagnostics::LifetimeMismatchOpaqueParam; use crate::universal_regions::RegionClassification; impl<'tcx> RegionInferenceContext<'tcx> { @@ -289,156 +283,3 @@ impl<'tcx> InferCtxt<'tcx> { definition_ty } } - -/// Opaque type parameter validity check as documented in the [rustc-dev-guide chapter]. -/// -/// [rustc-dev-guide chapter]: -/// https://rustc-dev-guide.rust-lang.org/opaque-types-region-infer-restrictions.html -fn check_opaque_type_parameter_valid<'tcx>( - infcx: &InferCtxt<'tcx>, - opaque_type_key: OpaqueTypeKey<'tcx>, - span: Span, -) -> Result<(), ErrorGuaranteed> { - let tcx = infcx.tcx; - let opaque_generics = tcx.generics_of(opaque_type_key.def_id); - let opaque_env = LazyOpaqueTyEnv::new(tcx, opaque_type_key.def_id); - let mut seen_params: FxIndexMap<_, Vec<_>> = FxIndexMap::default(); - - for (i, arg) in opaque_type_key.iter_captured_args(tcx) { - let arg_is_param = match arg.unpack() { - GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)), - GenericArgKind::Lifetime(lt) => { - matches!(*lt, ty::ReEarlyParam(_) | ty::ReLateParam(_)) - || (lt.is_static() && opaque_env.param_equal_static(i)) - } - GenericArgKind::Const(ct) => matches!(ct.kind(), ty::ConstKind::Param(_)), - }; - - if arg_is_param { - // Register if the same lifetime appears multiple times in the generic args. - // There is an exception when the opaque type *requires* the lifetimes to be equal. - // See [rustc-dev-guide chapter] § "An exception to uniqueness rule". - let seen_where = seen_params.entry(arg).or_default(); - if !seen_where.first().is_some_and(|&prev_i| opaque_env.params_equal(i, prev_i)) { - seen_where.push(i); - } - } else { - // Prevent `fn foo() -> Foo` from being defining. - let opaque_param = opaque_generics.param_at(i, tcx); - let kind = opaque_param.kind.descr(); - - opaque_env.param_is_error(i)?; - - return Err(infcx.dcx().emit_err(NonGenericOpaqueTypeParam { - ty: arg, - kind, - span, - param_span: tcx.def_span(opaque_param.def_id), - })); - } - } - - for (_, indices) in seen_params { - if indices.len() > 1 { - let descr = opaque_generics.param_at(indices[0], tcx).kind.descr(); - let spans: Vec<_> = indices - .into_iter() - .map(|i| tcx.def_span(opaque_generics.param_at(i, tcx).def_id)) - .collect(); - #[allow(rustc::diagnostic_outside_of_impl)] - #[allow(rustc::untranslatable_diagnostic)] - return Err(infcx - .dcx() - .struct_span_err(span, "non-defining opaque type use in defining scope") - .with_span_note(spans, format!("{descr} used multiple times")) - .emit()); - } - } - - Ok(()) -} - -/// Computes if an opaque type requires a lifetime parameter to be equal to -/// another one or to the `'static` lifetime. -/// These requirements are derived from the explicit and implied bounds. -struct LazyOpaqueTyEnv<'tcx> { - tcx: TyCtxt<'tcx>, - def_id: LocalDefId, - - /// Equal parameters will have the same name. Computed Lazily. - /// Example: - /// `type Opaque<'a: 'static, 'b: 'c, 'c: 'b> = impl Sized;` - /// Identity args: `['a, 'b, 'c]` - /// Canonical args: `['static, 'b, 'b]` - canonical_args: std::cell::OnceCell>, -} - -impl<'tcx> LazyOpaqueTyEnv<'tcx> { - fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self { - Self { tcx, def_id, canonical_args: std::cell::OnceCell::new() } - } - - fn param_equal_static(&self, param_index: usize) -> bool { - self.get_canonical_args()[param_index].expect_region().is_static() - } - - fn params_equal(&self, param1: usize, param2: usize) -> bool { - let canonical_args = self.get_canonical_args(); - canonical_args[param1] == canonical_args[param2] - } - - fn param_is_error(&self, param_index: usize) -> Result<(), ErrorGuaranteed> { - self.get_canonical_args()[param_index].error_reported() - } - - fn get_canonical_args(&self) -> ty::GenericArgsRef<'tcx> { - if let Some(&canonical_args) = self.canonical_args.get() { - return canonical_args; - } - - let &Self { tcx, def_id, .. } = self; - let origin = tcx.local_opaque_ty_origin(def_id); - let parent = match origin { - OpaqueTyOrigin::FnReturn { parent, .. } - | OpaqueTyOrigin::AsyncFn { parent, .. } - | OpaqueTyOrigin::TyAlias { parent, .. } => parent, - }; - let param_env = tcx.param_env(parent); - let args = GenericArgs::identity_for_item(tcx, parent).extend_to( - tcx, - def_id.to_def_id(), - |param, _| { - tcx.map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local()).into() - }, - ); - - // FIXME(#132279): It feels wrong to use `non_body_analysis` here given that we're - // in a body here. - let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); - let ocx = ObligationCtxt::new(&infcx); - - let wf_tys = ocx.assumed_wf_types(param_env, parent).unwrap_or_else(|_| { - tcx.dcx().span_delayed_bug(tcx.def_span(def_id), "error getting implied bounds"); - Default::default() - }); - let outlives_env = OutlivesEnvironment::new(&infcx, parent, param_env, wf_tys); - - let mut seen = vec![tcx.lifetimes.re_static]; - let canonical_args = fold_regions(tcx, args, |r1, _| { - if r1.is_error() { - r1 - } else if let Some(&r2) = seen.iter().find(|&&r2| { - let free_regions = outlives_env.free_region_map(); - free_regions.sub_free_regions(tcx, r1, r2) - && free_regions.sub_free_regions(tcx, r2, r1) - }) { - r2 - } else { - seen.push(r1); - r1 - } - }); - self.canonical_args.set(canonical_args).unwrap(); - canonical_args - } -} diff --git a/compiler/rustc_borrowck/src/session_diagnostics.rs b/compiler/rustc_borrowck/src/session_diagnostics.rs index 4be5d0dbf428..5143b2fa2059 100644 --- a/compiler/rustc_borrowck/src/session_diagnostics.rs +++ b/compiler/rustc_borrowck/src/session_diagnostics.rs @@ -294,17 +294,6 @@ pub(crate) struct MoveBorrow<'a> { pub borrow_span: Span, } -#[derive(Diagnostic)] -#[diag(borrowck_opaque_type_non_generic_param, code = E0792)] -pub(crate) struct NonGenericOpaqueTypeParam<'a, 'tcx> { - pub ty: GenericArg<'tcx>, - pub kind: &'a str, - #[primary_span] - pub span: Span, - #[label] - pub param_span: Span, -} - #[derive(Diagnostic)] #[diag(borrowck_opaque_type_lifetime_mismatch)] pub(crate) struct LifetimeMismatchOpaqueParam<'tcx> { diff --git a/compiler/rustc_trait_selection/messages.ftl b/compiler/rustc_trait_selection/messages.ftl index 4db9d9915b13..05bbb42fb7c6 100644 --- a/compiler/rustc_trait_selection/messages.ftl +++ b/compiler/rustc_trait_selection/messages.ftl @@ -264,8 +264,15 @@ trait_selection_oc_no_diverge = `else` clause of `let...else` does not diverge trait_selection_oc_no_else = `if` may be missing an `else` clause trait_selection_oc_try_compat = `?` operator has incompatible types trait_selection_oc_type_compat = type not compatible with trait + trait_selection_opaque_captures_lifetime = hidden type for `{$opaque_ty}` captures lifetime that does not appear in bounds .label = opaque type defined here +trait_selection_opaque_type_non_generic_param = + expected generic {$kind} parameter, found `{$ty}` + .label = {STREQ($ty, "'static") -> + [true] cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type + *[other] this generic parameter must be used with a generic {$kind} parameter + } trait_selection_outlives_bound = lifetime of the source pointer does not outlive lifetime bound of the object type trait_selection_outlives_content = lifetime of reference outlives lifetime of borrowed content... diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index b30390a9330e..9f7bfe5101ab 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -12,7 +12,7 @@ use rustc_hir::intravisit::{Visitor, VisitorExt, walk_ty}; use rustc_hir::{self as hir, AmbigArg, FnRetTy, GenericParamKind, IsAnonInPath, Node}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::ty::print::{PrintTraitRefExt as _, TraitRefPrintOnlyTraitPath}; -use rustc_middle::ty::{self, Binder, ClosureKind, FnSig, Region, Ty, TyCtxt}; +use rustc_middle::ty::{self, Binder, ClosureKind, FnSig, GenericArg, Region, Ty, TyCtxt}; use rustc_span::{BytePos, Ident, Span, Symbol, kw}; use crate::error_reporting::infer::ObligationCauseAsDiagArg; @@ -1922,3 +1922,14 @@ impl Subdiagnostic for AddPreciseCapturingForOvercapture { } } } + +#[derive(Diagnostic)] +#[diag(trait_selection_opaque_type_non_generic_param, code = E0792)] +pub(crate) struct NonGenericOpaqueTypeParam<'a, 'tcx> { + pub ty: GenericArg<'tcx>, + pub kind: &'a str, + #[primary_span] + pub span: Span, + #[label] + pub param_span: Span, +} diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index b18fb0fb8fd3..93c118053045 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -36,6 +36,7 @@ pub mod error_reporting; pub mod errors; pub mod infer; +pub mod opaque_types; pub mod regions; pub mod solve; pub mod traits; diff --git a/compiler/rustc_trait_selection/src/opaque_types.rs b/compiler/rustc_trait_selection/src/opaque_types.rs new file mode 100644 index 000000000000..5af912fee549 --- /dev/null +++ b/compiler/rustc_trait_selection/src/opaque_types.rs @@ -0,0 +1,165 @@ +use rustc_data_structures::fx::FxIndexMap; +use rustc_hir::OpaqueTyOrigin; +use rustc_hir::def_id::LocalDefId; +use rustc_infer::infer::outlives::env::OutlivesEnvironment; +use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; +use rustc_middle::ty::{ + self, GenericArgKind, GenericArgs, OpaqueTypeKey, TyCtxt, TypeVisitableExt, + TypingMode, fold_regions, +}; +use rustc_span::{ErrorGuaranteed, Span}; + +use crate::errors::NonGenericOpaqueTypeParam; +use crate::regions::OutlivesEnvironmentBuildExt; +use crate::traits::ObligationCtxt; + +/// Opaque type parameter validity check as documented in the [rustc-dev-guide chapter]. +/// +/// [rustc-dev-guide chapter]: +/// https://rustc-dev-guide.rust-lang.org/opaque-types-region-infer-restrictions.html +pub fn check_opaque_type_parameter_valid<'tcx>( + infcx: &InferCtxt<'tcx>, + opaque_type_key: OpaqueTypeKey<'tcx>, + span: Span, +) -> Result<(), ErrorGuaranteed> { + let tcx = infcx.tcx; + let opaque_generics = tcx.generics_of(opaque_type_key.def_id); + let opaque_env = LazyOpaqueTyEnv::new(tcx, opaque_type_key.def_id); + let mut seen_params: FxIndexMap<_, Vec<_>> = FxIndexMap::default(); + + for (i, arg) in opaque_type_key.iter_captured_args(tcx) { + let arg_is_param = match arg.unpack() { + GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)), + GenericArgKind::Lifetime(lt) => { + matches!(*lt, ty::ReEarlyParam(_) | ty::ReLateParam(_)) + || (lt.is_static() && opaque_env.param_equal_static(i)) + } + GenericArgKind::Const(ct) => matches!(ct.kind(), ty::ConstKind::Param(_)), + }; + + if arg_is_param { + // Register if the same lifetime appears multiple times in the generic args. + // There is an exception when the opaque type *requires* the lifetimes to be equal. + // See [rustc-dev-guide chapter] § "An exception to uniqueness rule". + let seen_where = seen_params.entry(arg).or_default(); + if !seen_where.first().is_some_and(|&prev_i| opaque_env.params_equal(i, prev_i)) { + seen_where.push(i); + } + } else { + // Prevent `fn foo() -> Foo` from being defining. + let opaque_param = opaque_generics.param_at(i, tcx); + let kind = opaque_param.kind.descr(); + + opaque_env.param_is_error(i)?; + + return Err(infcx.dcx().emit_err(NonGenericOpaqueTypeParam { + ty: arg, + kind, + span, + param_span: tcx.def_span(opaque_param.def_id), + })); + } + } + + for (_, indices) in seen_params { + if indices.len() > 1 { + let descr = opaque_generics.param_at(indices[0], tcx).kind.descr(); + let spans: Vec<_> = indices + .into_iter() + .map(|i| tcx.def_span(opaque_generics.param_at(i, tcx).def_id)) + .collect(); + return Err(infcx + .dcx() + .struct_span_err(span, "non-defining opaque type use in defining scope") + .with_span_note(spans, format!("{descr} used multiple times")) + .emit()); + } + } + + Ok(()) +} + +/// Computes if an opaque type requires a lifetime parameter to be equal to +/// another one or to the `'static` lifetime. +/// These requirements are derived from the explicit and implied bounds. +struct LazyOpaqueTyEnv<'tcx> { + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, + + /// Equal parameters will have the same name. Computed Lazily. + /// Example: + /// `type Opaque<'a: 'static, 'b: 'c, 'c: 'b> = impl Sized;` + /// Identity args: `['a, 'b, 'c]` + /// Canonical args: `['static, 'b, 'b]` + canonical_args: std::cell::OnceCell>, +} + +impl<'tcx> LazyOpaqueTyEnv<'tcx> { + fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self { + Self { tcx, def_id, canonical_args: std::cell::OnceCell::new() } + } + + fn param_equal_static(&self, param_index: usize) -> bool { + self.get_canonical_args()[param_index].expect_region().is_static() + } + + fn params_equal(&self, param1: usize, param2: usize) -> bool { + let canonical_args = self.get_canonical_args(); + canonical_args[param1] == canonical_args[param2] + } + + fn param_is_error(&self, param_index: usize) -> Result<(), ErrorGuaranteed> { + self.get_canonical_args()[param_index].error_reported() + } + + fn get_canonical_args(&self) -> ty::GenericArgsRef<'tcx> { + if let Some(&canonical_args) = self.canonical_args.get() { + return canonical_args; + } + + let &Self { tcx, def_id, .. } = self; + let origin = tcx.local_opaque_ty_origin(def_id); + let parent = match origin { + OpaqueTyOrigin::FnReturn { parent, .. } + | OpaqueTyOrigin::AsyncFn { parent, .. } + | OpaqueTyOrigin::TyAlias { parent, .. } => parent, + }; + let param_env = tcx.param_env(parent); + let args = GenericArgs::identity_for_item(tcx, parent).extend_to( + tcx, + def_id.to_def_id(), + |param, _| { + tcx.map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local()).into() + }, + ); + + // FIXME(#132279): It feels wrong to use `non_body_analysis` here given that we're + // in a body here. + let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); + let ocx = ObligationCtxt::new(&infcx); + + let wf_tys = ocx.assumed_wf_types(param_env, parent).unwrap_or_else(|_| { + tcx.dcx().span_delayed_bug(tcx.def_span(def_id), "error getting implied bounds"); + Default::default() + }); + let outlives_env = OutlivesEnvironment::new(&infcx, parent, param_env, wf_tys); + + let mut seen = vec![tcx.lifetimes.re_static]; + let canonical_args = fold_regions(tcx, args, |r1, _| { + if r1.is_error() { + r1 + } else if let Some(&r2) = seen.iter().find(|&&r2| { + let free_regions = outlives_env.free_region_map(); + free_regions.sub_free_regions(tcx, r1, r2) + && free_regions.sub_free_regions(tcx, r2, r1) + }) { + r2 + } else { + seen.push(r1); + r1 + } + }); + self.canonical_args.set(canonical_args).unwrap(); + canonical_args + } +}