rustc_trait_selection/
opaque_types.rs

1use rustc_data_structures::fx::FxIndexMap;
2use rustc_hir::OpaqueTyOrigin;
3use rustc_hir::def_id::LocalDefId;
4use rustc_infer::infer::outlives::env::OutlivesEnvironment;
5use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
6use rustc_middle::ty::{
7    self, DefiningScopeKind, GenericArgKind, GenericArgs, OpaqueTypeKey, TyCtxt, TypeVisitableExt,
8    TypingMode, fold_regions,
9};
10use rustc_span::{ErrorGuaranteed, Span};
11
12use crate::errors::NonGenericOpaqueTypeParam;
13use crate::regions::OutlivesEnvironmentBuildExt;
14use crate::traits::ObligationCtxt;
15
16/// Opaque type parameter validity check as documented in the [rustc-dev-guide chapter].
17///
18/// [rustc-dev-guide chapter]:
19/// https://rustc-dev-guide.rust-lang.org/opaque-types-region-infer-restrictions.html
20pub fn check_opaque_type_parameter_valid<'tcx>(
21    infcx: &InferCtxt<'tcx>,
22    opaque_type_key: OpaqueTypeKey<'tcx>,
23    span: Span,
24    defining_scope_kind: DefiningScopeKind,
25) -> Result<(), ErrorGuaranteed> {
26    let tcx = infcx.tcx;
27    let opaque_generics = tcx.generics_of(opaque_type_key.def_id);
28    let opaque_env = LazyOpaqueTyEnv::new(tcx, opaque_type_key.def_id);
29    let mut seen_params: FxIndexMap<_, Vec<_>> = FxIndexMap::default();
30
31    // Avoid duplicate errors in case the opaque has already been malformed in
32    // HIR typeck.
33    if let DefiningScopeKind::MirBorrowck = defining_scope_kind {
34        if let Err(guar) = infcx
35            .tcx
36            .type_of_opaque_hir_typeck(opaque_type_key.def_id)
37            .instantiate_identity()
38            .error_reported()
39        {
40            return Err(guar);
41        }
42    }
43
44    for (i, arg) in opaque_type_key.iter_captured_args(tcx) {
45        let arg_is_param = match arg.unpack() {
46            GenericArgKind::Lifetime(lt) => match defining_scope_kind {
47                DefiningScopeKind::HirTypeck => continue,
48                DefiningScopeKind::MirBorrowck => {
49                    matches!(lt.kind(), ty::ReEarlyParam(_) | ty::ReLateParam(_))
50                        || (lt.is_static() && opaque_env.param_equal_static(i))
51                }
52            },
53            GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)),
54            GenericArgKind::Const(ct) => matches!(ct.kind(), ty::ConstKind::Param(_)),
55        };
56
57        if arg_is_param {
58            // Register if the same lifetime appears multiple times in the generic args.
59            // There is an exception when the opaque type *requires* the lifetimes to be equal.
60            // See [rustc-dev-guide chapter] § "An exception to uniqueness rule".
61            let seen_where = seen_params.entry(arg).or_default();
62            if !seen_where.first().is_some_and(|&prev_i| opaque_env.params_equal(i, prev_i)) {
63                seen_where.push(i);
64            }
65        } else {
66            // Prevent `fn foo() -> Foo<u32>` from being defining.
67            let opaque_param = opaque_generics.param_at(i, tcx);
68            let kind = opaque_param.kind.descr();
69
70            opaque_env.param_is_error(i)?;
71
72            return Err(infcx.dcx().emit_err(NonGenericOpaqueTypeParam {
73                ty: arg,
74                kind,
75                span,
76                param_span: tcx.def_span(opaque_param.def_id),
77            }));
78        }
79    }
80
81    for (_, indices) in seen_params {
82        if indices.len() > 1 {
83            let descr = opaque_generics.param_at(indices[0], tcx).kind.descr();
84            let spans: Vec<_> = indices
85                .into_iter()
86                .map(|i| tcx.def_span(opaque_generics.param_at(i, tcx).def_id))
87                .collect();
88            return Err(infcx
89                .dcx()
90                .struct_span_err(span, "non-defining opaque type use in defining scope")
91                .with_span_note(spans, format!("{descr} used multiple times"))
92                .emit());
93        }
94    }
95
96    Ok(())
97}
98
99/// Computes if an opaque type requires a lifetime parameter to be equal to
100/// another one or to the `'static` lifetime.
101/// These requirements are derived from the explicit and implied bounds.
102struct LazyOpaqueTyEnv<'tcx> {
103    tcx: TyCtxt<'tcx>,
104    def_id: LocalDefId,
105
106    /// Equal parameters will have the same name. Computed Lazily.
107    /// Example:
108    ///     `type Opaque<'a: 'static, 'b: 'c, 'c: 'b> = impl Sized;`
109    ///     Identity args: `['a, 'b, 'c]`
110    ///     Canonical args: `['static, 'b, 'b]`
111    canonical_args: std::cell::OnceCell<ty::GenericArgsRef<'tcx>>,
112}
113
114impl<'tcx> LazyOpaqueTyEnv<'tcx> {
115    fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self {
116        Self { tcx, def_id, canonical_args: std::cell::OnceCell::new() }
117    }
118
119    fn param_equal_static(&self, param_index: usize) -> bool {
120        self.get_canonical_args()[param_index].expect_region().is_static()
121    }
122
123    fn params_equal(&self, param1: usize, param2: usize) -> bool {
124        let canonical_args = self.get_canonical_args();
125        canonical_args[param1] == canonical_args[param2]
126    }
127
128    fn param_is_error(&self, param_index: usize) -> Result<(), ErrorGuaranteed> {
129        self.get_canonical_args()[param_index].error_reported()
130    }
131
132    fn get_canonical_args(&self) -> ty::GenericArgsRef<'tcx> {
133        if let Some(&canonical_args) = self.canonical_args.get() {
134            return canonical_args;
135        }
136
137        let &Self { tcx, def_id, .. } = self;
138        let origin = tcx.local_opaque_ty_origin(def_id);
139        let parent = match origin {
140            OpaqueTyOrigin::FnReturn { parent, .. }
141            | OpaqueTyOrigin::AsyncFn { parent, .. }
142            | OpaqueTyOrigin::TyAlias { parent, .. } => parent,
143        };
144        let param_env = tcx.param_env(parent);
145        let args = GenericArgs::identity_for_item(tcx, parent).extend_to(
146            tcx,
147            def_id.to_def_id(),
148            |param, _| {
149                tcx.map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local()).into()
150            },
151        );
152
153        // FIXME(#132279): It feels wrong to use `non_body_analysis` here given that we're
154        // in a body here.
155        let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());
156        let ocx = ObligationCtxt::new(&infcx);
157
158        let wf_tys = ocx.assumed_wf_types(param_env, parent).unwrap_or_else(|_| {
159            tcx.dcx().span_delayed_bug(tcx.def_span(def_id), "error getting implied bounds");
160            Default::default()
161        });
162        let outlives_env = OutlivesEnvironment::new(&infcx, parent, param_env, wf_tys);
163
164        let mut seen = vec![tcx.lifetimes.re_static];
165        let canonical_args = fold_regions(tcx, args, |r1, _| {
166            if r1.is_error() {
167                r1
168            } else if let Some(&r2) = seen.iter().find(|&&r2| {
169                let free_regions = outlives_env.free_region_map();
170                free_regions.sub_free_regions(tcx, r1, r2)
171                    && free_regions.sub_free_regions(tcx, r2, r1)
172            }) {
173                r2
174            } else {
175                seen.push(r1);
176                r1
177            }
178        });
179        self.canonical_args.set(canonical_args).unwrap();
180        canonical_args
181    }
182}