]> git.lizzy.rs Git - rust.git/blobdiff - crates/hir-ty/src/lower.rs
Auto merge of #13223 - lowr:fix/hir-proj-normalization, r=flodiebold
[rust.git] / crates / hir-ty / src / lower.rs
index 239f66bcb7e798f813a0e2d9206f78897a1d0c95..532544fee595c51eee1680627a70cbfe327a7a01 100644 (file)
@@ -1,12 +1,12 @@
 //! Methods for lowering the HIR to types. There are two main cases here:
 //!
 //!  - Lowering a type reference like `&usize` or `Option<foo::bar::Baz>` to a
-//!    type: The entry point for this is `Ty::from_hir`.
-//!  - Building the type for an item: This happens through the `type_for_def` query.
+//!    type: The entry point for this is `TyLoweringContext::lower_ty`.
+//!  - Building the type for an item: This happens through the `ty` query.
 //!
 //! This usually involves resolving names, collecting generic arguments etc.
 use std::{
-    cell::{Cell, RefCell},
+    cell::{Cell, RefCell, RefMut},
     iter,
     sync::Arc,
 };
@@ -47,7 +47,7 @@
     consteval::{intern_const_scalar, path_to_const, unknown_const, unknown_const_as_generic},
     db::HirDatabase,
     make_binders,
-    mapping::ToChalk,
+    mapping::{from_chalk_trait_id, ToChalk},
     static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx,
     utils::Generics,
     utils::{all_super_trait_refs, associated_type_by_name_including_super_traits, generics},
@@ -238,18 +238,7 @@ pub fn lower_ty_ext(&self, type_ref: &TypeRef) -> (Ty, Option<TypeNs>) {
                 })
                 .intern(Interner)
             }
-            TypeRef::DynTrait(bounds) => {
-                let self_ty =
-                    TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(Interner);
-                let bounds = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
-                    QuantifiedWhereClauses::from_iter(
-                        Interner,
-                        bounds.iter().flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false)),
-                    )
-                });
-                let bounds = crate::make_single_type_binders(bounds);
-                TyKind::Dyn(DynTy { bounds, lifetime: static_lifetime() }).intern(Interner)
-            }
+            TypeRef::DynTrait(bounds) => self.lower_dyn_trait(bounds),
             TypeRef::ImplTrait(bounds) => {
                 match self.impl_trait_mode {
                     ImplTraitLoweringMode::Opaque => {
@@ -341,26 +330,29 @@ pub fn lower_ty_ext(&self, type_ref: &TypeRef) -> (Ty, Option<TypeNs>) {
                 }
             }
             TypeRef::Macro(macro_call) => {
-                let (expander, recursion_start) = {
-                    let mut expander = self.expander.borrow_mut();
-                    if expander.is_some() {
-                        (Some(expander), false)
-                    } else {
-                        *expander = Some(Expander::new(
-                            self.db.upcast(),
-                            macro_call.file_id,
-                            self.resolver.module(),
-                        ));
-                        (Some(expander), true)
+                let (mut expander, recursion_start) = {
+                    match RefMut::filter_map(self.expander.borrow_mut(), Option::as_mut) {
+                        // There already is an expander here, this means we are already recursing
+                        Ok(expander) => (expander, false),
+                        // No expander was created yet, so we are at the start of the expansion recursion
+                        // and therefore have to create an expander.
+                        Err(expander) => (
+                            RefMut::map(expander, |it| {
+                                it.insert(Expander::new(
+                                    self.db.upcast(),
+                                    macro_call.file_id,
+                                    self.resolver.module(),
+                                ))
+                            }),
+                            true,
+                        ),
                     }
                 };
-                let ty = if let Some(mut expander) = expander {
-                    let expander_mut = expander.as_mut().unwrap();
+                let ty = {
                     let macro_call = macro_call.to_node(self.db.upcast());
-                    match expander_mut.enter_expand::<ast::Type>(self.db.upcast(), macro_call) {
+                    match expander.enter_expand::<ast::Type>(self.db.upcast(), macro_call) {
                         Ok(ExpandResult { value: Some((mark, expanded)), .. }) => {
-                            let ctx =
-                                LowerCtx::new(self.db.upcast(), expander_mut.current_file_id());
+                            let ctx = LowerCtx::new(self.db.upcast(), expander.current_file_id());
                             let type_ref = TypeRef::from_ast(&ctx, expanded);
 
                             drop(expander);
@@ -373,11 +365,14 @@ pub fn lower_ty_ext(&self, type_ref: &TypeRef) -> (Ty, Option<TypeNs>) {
                                 .exit(self.db.upcast(), mark);
                             Some(ty)
                         }
-                        _ => None,
+                        _ => {
+                            drop(expander);
+                            None
+                        }
                     }
-                } else {
-                    None
                 };
+
+                // drop the expander, resetting it to pre-recursion state
                 if recursion_start {
                     *self.expander.borrow_mut() = None;
                 }
@@ -468,29 +463,10 @@ pub(crate) fn lower_partly_resolved_path(
                         }
                     }
                     0 => {
-                        let self_ty = Some(
-                            TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0))
-                                .intern(Interner),
-                        );
-                        let trait_ref = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
-                            ctx.lower_trait_ref_from_resolved_path(
-                                trait_,
-                                resolved_segment,
-                                self_ty,
-                            )
-                        });
-                        let dyn_ty = DynTy {
-                            bounds: crate::make_single_type_binders(
-                                QuantifiedWhereClauses::from_iter(
-                                    Interner,
-                                    Some(crate::wrap_empty_binders(WhereClause::Implemented(
-                                        trait_ref,
-                                    ))),
-                                ),
-                            ),
-                            lifetime: static_lifetime(),
-                        };
-                        TyKind::Dyn(dyn_ty).intern(Interner)
+                        // Trait object type without dyn; this should be handled in upstream. See
+                        // `lower_path()`.
+                        stdx::never!("unexpected fully resolved trait path");
+                        TyKind::Error.intern(Interner)
                     }
                     _ => {
                         // FIXME report error (ambiguous associated type)
@@ -509,7 +485,14 @@ pub(crate) fn lower_partly_resolved_path(
                         TyKind::Placeholder(to_placeholder_idx(self.db, param_id.into()))
                     }
                     ParamLoweringMode::Variable => {
-                        let idx = generics.param_idx(param_id.into()).expect("matching generics");
+                        let idx = match generics.param_idx(param_id.into()) {
+                            None => {
+                                never!("no matching generics");
+                                return (TyKind::Error.intern(Interner), None);
+                            }
+                            Some(idx) => idx,
+                        };
+
                         TyKind::BoundVar(BoundVar::new(self.in_binders, idx))
                     }
                 }
@@ -555,11 +538,20 @@ pub(crate) fn lower_path(&self, path: &Path) -> (Ty, Option<TypeNs>) {
             let (ty, res) = self.lower_ty_ext(type_ref);
             return self.lower_ty_relative_path(ty, res, path.segments());
         }
+
         let (resolution, remaining_index) =
             match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) {
                 Some(it) => it,
                 None => return (TyKind::Error.intern(Interner), None),
             };
+
+        if matches!(resolution, TypeNs::TraitId(_)) && remaining_index.is_none() {
+            // trait object type without dyn
+            let bound = TypeBound::Path(path.clone(), TraitBoundModifier::None);
+            let ty = self.lower_dyn_trait(&[Interned::new(bound)]);
+            return (ty, None);
+        }
+
         let (resolved_segment, remaining_segments) = match remaining_index {
             None => (
                 path.segments().last().expect("resolved path has at least one element"),
@@ -987,6 +979,78 @@ fn assoc_type_bindings_from_type_bound(
             })
     }
 
+    fn lower_dyn_trait(&self, bounds: &[Interned<TypeBound>]) -> Ty {
+        let self_ty = TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(Interner);
+        // INVARIANT: The principal trait bound must come first. Others may be in any order but
+        // should be in the same order for the same set but possibly different order of bounds in
+        // the input.
+        // This invariant is used by `TyExt::dyn_trait()` and chalk.
+        let bounds = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
+            let mut bounds: Vec<_> = bounds
+                .iter()
+                .flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false))
+                .collect();
+
+            let mut multiple_regular_traits = false;
+            let mut multiple_same_projection = false;
+            bounds.sort_unstable_by(|lhs, rhs| {
+                use std::cmp::Ordering;
+                match (lhs.skip_binders(), rhs.skip_binders()) {
+                    (WhereClause::Implemented(lhs), WhereClause::Implemented(rhs)) => {
+                        let lhs_id = lhs.trait_id;
+                        let lhs_is_auto = ctx.db.trait_data(from_chalk_trait_id(lhs_id)).is_auto;
+                        let rhs_id = rhs.trait_id;
+                        let rhs_is_auto = ctx.db.trait_data(from_chalk_trait_id(rhs_id)).is_auto;
+
+                        if !lhs_is_auto && !rhs_is_auto {
+                            multiple_regular_traits = true;
+                        }
+                        // Note that the ordering here is important; this ensures the invariant
+                        // mentioned above.
+                        (lhs_is_auto, lhs_id).cmp(&(rhs_is_auto, rhs_id))
+                    }
+                    (WhereClause::Implemented(_), _) => Ordering::Less,
+                    (_, WhereClause::Implemented(_)) => Ordering::Greater,
+                    (WhereClause::AliasEq(lhs), WhereClause::AliasEq(rhs)) => {
+                        match (&lhs.alias, &rhs.alias) {
+                            (AliasTy::Projection(lhs_proj), AliasTy::Projection(rhs_proj)) => {
+                                // We only compare the `associated_ty_id`s. We shouldn't have
+                                // multiple bounds for an associated type in the correct Rust code,
+                                // and if we do, we error out.
+                                if lhs_proj.associated_ty_id == rhs_proj.associated_ty_id {
+                                    multiple_same_projection = true;
+                                }
+                                lhs_proj.associated_ty_id.cmp(&rhs_proj.associated_ty_id)
+                            }
+                            // We don't produce `AliasTy::Opaque`s yet.
+                            _ => unreachable!(),
+                        }
+                    }
+                    // We don't produce `WhereClause::{TypeOutlives, LifetimeOutlives}` yet.
+                    _ => unreachable!(),
+                }
+            });
+
+            if multiple_regular_traits || multiple_same_projection {
+                return None;
+            }
+
+            // As multiple occurrences of the same auto traits *are* permitted, we dedulicate the
+            // bounds. We shouldn't have repeated elements besides auto traits at this point.
+            bounds.dedup();
+
+            Some(QuantifiedWhereClauses::from_iter(Interner, bounds))
+        });
+
+        if let Some(bounds) = bounds {
+            let bounds = crate::make_single_type_binders(bounds);
+            TyKind::Dyn(DynTy { bounds, lifetime: static_lifetime() }).intern(Interner)
+        } else {
+            // FIXME: report error (additional non-auto traits or associated type rebound)
+            TyKind::Error.intern(Interner)
+        }
+    }
+
     fn lower_impl_trait(
         &self,
         bounds: &[Interned<TypeBound>],