]> git.proxmox.com Git - rustc.git/blobdiff - compiler/rustc_borrowck/src/region_infer/opaque_types.rs
New upstream version 1.57.0+dfsg1
[rustc.git] / compiler / rustc_borrowck / src / region_infer / opaque_types.rs
diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
new file mode 100644 (file)
index 0000000..4eb7be5
--- /dev/null
@@ -0,0 +1,239 @@
+use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::vec_map::VecMap;
+use rustc_hir::OpaqueTyOrigin;
+use rustc_infer::infer::opaque_types::OpaqueTypeDecl;
+use rustc_infer::infer::InferCtxt;
+use rustc_middle::ty::subst::GenericArgKind;
+use rustc_middle::ty::{self, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable};
+use rustc_span::Span;
+use rustc_trait_selection::opaque_types::InferCtxtExt;
+
+use super::RegionInferenceContext;
+
+impl<'tcx> RegionInferenceContext<'tcx> {
+    /// Resolve any opaque types that were encountered while borrow checking
+    /// this item. This is then used to get the type in the `type_of` query.
+    ///
+    /// For example consider `fn f<'a>(x: &'a i32) -> impl Sized + 'a { x }`.
+    /// This is lowered to give HIR something like
+    ///
+    /// type f<'a>::_Return<'_a> = impl Sized + '_a;
+    /// fn f<'a>(x: &'a i32) -> f<'static>::_Return<'a> { x }
+    ///
+    /// When checking the return type record the type from the return and the
+    /// type used in the return value. In this case they might be `_Return<'1>`
+    /// and `&'2 i32` respectively.
+    ///
+    /// Once we to this method, we have completed region inference and want to
+    /// call `infer_opaque_definition_from_instantiation` to get the inferred
+    /// type of `_Return<'_a>`. `infer_opaque_definition_from_instantiation`
+    /// compares lifetimes directly, so we need to map the inference variables
+    /// back to concrete lifetimes: `'static`, `ReEarlyBound` or `ReFree`.
+    ///
+    /// First we map all the lifetimes in the concrete type to an equal
+    /// universal region that occurs in the concrete type's substs, in this case
+    /// this would result in `&'1 i32`. We only consider regions in the substs
+    /// in case there is an equal region that does not. For example, this should
+    /// be allowed:
+    /// `fn f<'a: 'b, 'b: 'a>(x: *mut &'b i32) -> impl Sized + 'a { x }`
+    ///
+    /// Then we map the regions in both the type and the subst to their
+    /// `external_name` giving `concrete_type = &'a i32`,
+    /// `substs = ['static, 'a]`. This will then allow
+    /// `infer_opaque_definition_from_instantiation` to determine that
+    /// `_Return<'_a> = &'_a i32`.
+    ///
+    /// There's a slight complication around closures. Given
+    /// `fn f<'a: 'a>() { || {} }` the closure's type is something like
+    /// `f::<'a>::{{closure}}`. The region parameter from f is essentially
+    /// ignored by type checking so ends up being inferred to an empty region.
+    /// Calling `universal_upper_bound` for such a region gives `fr_fn_body`,
+    /// which has no `external_name` in which case we use `'empty` as the
+    /// region to pass to `infer_opaque_definition_from_instantiation`.
+    #[instrument(level = "debug", skip(self, infcx))]
+    pub(crate) fn infer_opaque_types(
+        &self,
+        infcx: &InferCtxt<'_, 'tcx>,
+        opaque_ty_decls: VecMap<OpaqueTypeKey<'tcx>, OpaqueTypeDecl<'tcx>>,
+        span: Span,
+    ) -> VecMap<OpaqueTypeKey<'tcx>, Ty<'tcx>> {
+        opaque_ty_decls
+            .into_iter()
+            .filter_map(|(opaque_type_key, decl)| {
+                let substs = opaque_type_key.substs;
+                let concrete_type = decl.concrete_ty;
+                debug!(?concrete_type, ?substs);
+
+                let mut subst_regions = vec![self.universal_regions.fr_static];
+                let universal_substs = infcx.tcx.fold_regions(substs, &mut false, |region, _| {
+                    let vid = self.universal_regions.to_region_vid(region);
+                    subst_regions.push(vid);
+                    self.definitions[vid].external_name.unwrap_or_else(|| {
+                        infcx
+                            .tcx
+                            .sess
+                            .delay_span_bug(span, "opaque type with non-universal region substs");
+                        infcx.tcx.lifetimes.re_static
+                    })
+                });
+
+                subst_regions.sort();
+                subst_regions.dedup();
+
+                let universal_concrete_type =
+                    infcx.tcx.fold_regions(concrete_type, &mut false, |region, _| match *region {
+                        ty::ReVar(vid) => subst_regions
+                            .iter()
+                            .find(|ur_vid| self.eval_equal(vid, **ur_vid))
+                            .and_then(|ur_vid| self.definitions[*ur_vid].external_name)
+                            .unwrap_or(infcx.tcx.lifetimes.re_root_empty),
+                        _ => region,
+                    });
+
+                debug!(?universal_concrete_type, ?universal_substs);
+
+                let opaque_type_key =
+                    OpaqueTypeKey { def_id: opaque_type_key.def_id, substs: universal_substs };
+                let remapped_type = infcx.infer_opaque_definition_from_instantiation(
+                    opaque_type_key,
+                    universal_concrete_type,
+                    span,
+                );
+
+                check_opaque_type_parameter_valid(
+                    infcx.tcx,
+                    opaque_type_key,
+                    OpaqueTypeDecl { concrete_ty: remapped_type, ..decl },
+                )
+                .then_some((opaque_type_key, remapped_type))
+            })
+            .collect()
+    }
+
+    /// Map the regions in the type to named regions. This is similar to what
+    /// `infer_opaque_types` does, but can infer any universal region, not only
+    /// ones from the substs for the opaque type. It also doesn't double check
+    /// that the regions produced are in fact equal to the named region they are
+    /// replaced with. This is fine because this function is only to improve the
+    /// region names in error messages.
+    pub(crate) fn name_regions<T>(&self, tcx: TyCtxt<'tcx>, ty: T) -> T
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        tcx.fold_regions(ty, &mut false, |region, _| match *region {
+            ty::ReVar(vid) => {
+                // Find something that we can name
+                let upper_bound = self.approx_universal_upper_bound(vid);
+                let upper_bound = &self.definitions[upper_bound];
+                match upper_bound.external_name {
+                    Some(reg) => reg,
+                    None => {
+                        // Nothing exact found, so we pick the first one that we find.
+                        let scc = self.constraint_sccs.scc(vid);
+                        for vid in self.rev_scc_graph.as_ref().unwrap().upper_bounds(scc) {
+                            match self.definitions[vid].external_name {
+                                None => {}
+                                Some(&ty::ReStatic) => {}
+                                Some(region) => return region,
+                            }
+                        }
+                        region
+                    }
+                }
+            }
+            _ => region,
+        })
+    }
+}
+
+fn check_opaque_type_parameter_valid(
+    tcx: TyCtxt<'_>,
+    opaque_type_key: OpaqueTypeKey<'_>,
+    decl: OpaqueTypeDecl<'_>,
+) -> bool {
+    match decl.origin {
+        // No need to check return position impl trait (RPIT)
+        // because for type and const parameters they are correct
+        // by construction: we convert
+        //
+        // fn foo<P0..Pn>() -> impl Trait
+        //
+        // into
+        //
+        // type Foo<P0...Pn>
+        // fn foo<P0..Pn>() -> Foo<P0...Pn>.
+        //
+        // For lifetime parameters we convert
+        //
+        // fn foo<'l0..'ln>() -> impl Trait<'l0..'lm>
+        //
+        // into
+        //
+        // type foo::<'p0..'pn>::Foo<'q0..'qm>
+        // fn foo<l0..'ln>() -> foo::<'static..'static>::Foo<'l0..'lm>.
+        //
+        // which would error here on all of the `'static` args.
+        OpaqueTyOrigin::FnReturn | OpaqueTyOrigin::AsyncFn => return true,
+        // Check these
+        OpaqueTyOrigin::TyAlias => {}
+    }
+    let span = decl.definition_span;
+    let opaque_generics = tcx.generics_of(opaque_type_key.def_id);
+    let mut seen_params: FxHashMap<_, Vec<_>> = FxHashMap::default();
+    for (i, arg) in opaque_type_key.substs.iter().enumerate() {
+        let arg_is_param = match arg.unpack() {
+            GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)),
+            GenericArgKind::Lifetime(ty::ReStatic) => {
+                tcx.sess
+                    .struct_span_err(span, "non-defining opaque type use in defining scope")
+                    .span_label(
+                        tcx.def_span(opaque_generics.param_at(i, tcx).def_id),
+                        "cannot use static lifetime; use a bound lifetime \
+                                    instead or remove the lifetime parameter from the \
+                                    opaque type",
+                    )
+                    .emit();
+                return false;
+            }
+            GenericArgKind::Lifetime(lt) => {
+                matches!(lt, ty::ReEarlyBound(_) | ty::ReFree(_))
+            }
+            GenericArgKind::Const(ct) => matches!(ct.val, ty::ConstKind::Param(_)),
+        };
+
+        if arg_is_param {
+            seen_params.entry(arg).or_default().push(i);
+        } else {
+            // Prevent `fn foo() -> Foo<u32>` from being defining.
+            let opaque_param = opaque_generics.param_at(i, tcx);
+            tcx.sess
+                .struct_span_err(span, "non-defining opaque type use in defining scope")
+                .span_note(
+                    tcx.def_span(opaque_param.def_id),
+                    &format!(
+                        "used non-generic {} `{}` for generic parameter",
+                        opaque_param.kind.descr(),
+                        arg,
+                    ),
+                )
+                .emit();
+            return false;
+        }
+    }
+
+    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();
+            tcx.sess
+                .struct_span_err(span, "non-defining opaque type use in defining scope")
+                .span_note(spans, &format!("{} used multiple times", descr))
+                .emit();
+            return false;
+        }
+    }
+    true
+}