about summary refs log tree commit diff
path: root/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_hir_analysis/src/collect/predicates_of.rs')
-rw-r--r--compiler/rustc_hir_analysis/src/collect/predicates_of.rs239
1 files changed, 234 insertions, 5 deletions
diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
index f9460ed5b47..ec5773d174f 100644
--- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
@@ -12,6 +12,7 @@ use rustc_span::symbol::Ident;
 use rustc_span::{DUMMY_SP, Span};
 use tracing::{debug, instrument, trace};
 
+use super::item_bounds::explicit_item_bounds_with_filter;
 use crate::bounds::Bounds;
 use crate::collect::ItemCtxt;
 use crate::constrained_generic_params as cgp;
@@ -685,29 +686,76 @@ pub(super) fn assert_only_contains_predicates_from<'tcx>(
                         assert_eq!(
                             trait_predicate.self_ty(),
                             ty,
-                            "expected `Self` predicate when computing `{filter:?}` implied bounds: {clause:?}"
+                            "expected `Self` predicate when computing \
+                            `{filter:?}` implied bounds: {clause:?}"
                         );
                     }
                     ty::ClauseKind::Projection(projection_predicate) => {
                         assert_eq!(
                             projection_predicate.self_ty(),
                             ty,
-                            "expected `Self` predicate when computing `{filter:?}` implied bounds: {clause:?}"
+                            "expected `Self` predicate when computing \
+                            `{filter:?}` implied bounds: {clause:?}"
                         );
                     }
                     ty::ClauseKind::TypeOutlives(outlives_predicate) => {
                         assert_eq!(
                             outlives_predicate.0, ty,
-                            "expected `Self` predicate when computing `{filter:?}` implied bounds: {clause:?}"
+                            "expected `Self` predicate when computing \
+                            `{filter:?}` implied bounds: {clause:?}"
                         );
                     }
 
                     ty::ClauseKind::RegionOutlives(_)
                     | ty::ClauseKind::ConstArgHasType(_, _)
                     | ty::ClauseKind::WellFormed(_)
-                    | ty::ClauseKind::ConstEvaluatable(_) => {
+                    | ty::ClauseKind::ConstEvaluatable(_)
+                    | ty::ClauseKind::HostEffect(..) => {
                         bug!(
-                            "unexpected non-`Self` predicate when computing `{filter:?}` implied bounds: {clause:?}"
+                            "unexpected non-`Self` predicate when computing \
+                            `{filter:?}` implied bounds: {clause:?}"
+                        );
+                    }
+                }
+            }
+        }
+        PredicateFilter::ConstIfConst => {
+            for (clause, _) in bounds {
+                match clause.kind().skip_binder() {
+                    ty::ClauseKind::HostEffect(ty::HostEffectPredicate {
+                        trait_ref: _,
+                        host: ty::HostPolarity::Maybe,
+                    }) => {}
+                    _ => {
+                        bug!(
+                            "unexpected non-`HostEffect` predicate when computing \
+                            `{filter:?}` implied bounds: {clause:?}"
+                        );
+                    }
+                }
+            }
+        }
+        PredicateFilter::SelfConstIfConst => {
+            for (clause, _) in bounds {
+                match clause.kind().skip_binder() {
+                    ty::ClauseKind::HostEffect(pred) => {
+                        assert_eq!(
+                            pred.host,
+                            ty::HostPolarity::Maybe,
+                            "expected `~const` predicate when computing `{filter:?}` \
+                            implied bounds: {clause:?}",
+                        );
+                        assert_eq!(
+                            pred.trait_ref.self_ty(),
+                            ty,
+                            "expected `Self` predicate when computing `{filter:?}` \
+                            implied bounds: {clause:?}"
+                        );
+                    }
+                    _ => {
+                        bug!(
+                            "unexpected non-`HostEffect` predicate when computing \
+                            `{filter:?}` implied bounds: {clause:?}"
                         );
                     }
                 }
@@ -829,3 +877,184 @@ impl<'tcx> ItemCtxt<'tcx> {
         bounds.clauses(self.tcx).collect()
     }
 }
+
+/// Compute the conditions that need to hold for a conditionally-const item to be const.
+/// That is, compute the set of `~const` where clauses for a given item.
+///
+/// This query also computes the `~const` where clauses for associated types, which are
+/// not "const", but which have item bounds which may be `~const`. These must hold for
+/// the `~const` item bound to hold.
+pub(super) fn const_conditions<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    def_id: LocalDefId,
+) -> ty::ConstConditions<'tcx> {
+    // This logic is spaghetti, and should be cleaned up. The current methods that are
+    // defined to deal with constness are very unintuitive.
+    if tcx.is_const_fn_raw(def_id.to_def_id()) {
+        // Ok, const fn or method in const trait.
+    } else {
+        match tcx.def_kind(def_id) {
+            DefKind::Trait => {
+                if !tcx.is_const_trait(def_id.to_def_id()) {
+                    return Default::default();
+                }
+            }
+            DefKind::Impl { .. } => {
+                // FIXME(effects): Should be using a dedicated function to
+                // test if this is a const trait impl.
+                if tcx.constness(def_id) != hir::Constness::Const {
+                    return Default::default();
+                }
+            }
+            DefKind::AssocTy | DefKind::AssocFn => {
+                let parent_def_id = tcx.local_parent(def_id).to_def_id();
+                match tcx.associated_item(def_id).container {
+                    ty::AssocItemContainer::TraitContainer => {
+                        if !tcx.is_const_trait(parent_def_id) {
+                            return Default::default();
+                        }
+                    }
+                    ty::AssocItemContainer::ImplContainer => {
+                        // FIXME(effects): Should be using a dedicated function to
+                        // test if this is a const trait impl.
+                        if tcx.constness(parent_def_id) != hir::Constness::Const {
+                            return Default::default();
+                        }
+                    }
+                }
+            }
+            DefKind::Closure | DefKind::OpaqueTy => {
+                // Closures and RPITs will eventually have const conditions
+                // for `~const` bounds.
+                return Default::default();
+            }
+            _ => return Default::default(),
+        }
+    }
+
+    let (generics, trait_def_id_and_supertraits, has_parent) = match tcx.hir_node_by_def_id(def_id)
+    {
+        Node::Item(item) => match item.kind {
+            hir::ItemKind::Impl(impl_) => (impl_.generics, None, false),
+            hir::ItemKind::Fn(_, generics, _) => (generics, None, false),
+            hir::ItemKind::Trait(_, _, generics, supertraits, _) => {
+                (generics, Some((item.owner_id.def_id, supertraits)), false)
+            }
+            _ => return Default::default(),
+        },
+        // While associated types are not really const, we do allow them to have `~const`
+        // bounds and where clauses. `const_conditions` is responsible for gathering
+        // these up so we can check them in `compare_type_predicate_entailment`, and
+        // in `HostEffect` goal computation.
+        Node::TraitItem(item) => match item.kind {
+            hir::TraitItemKind::Fn(_, _) | hir::TraitItemKind::Type(_, _) => {
+                (item.generics, None, true)
+            }
+            _ => return Default::default(),
+        },
+        Node::ImplItem(item) => match item.kind {
+            hir::ImplItemKind::Fn(_, _) | hir::ImplItemKind::Type(_) => (item.generics, None, true),
+            _ => return Default::default(),
+        },
+        _ => return Default::default(),
+    };
+
+    let icx = ItemCtxt::new(tcx, def_id);
+    let mut bounds = Bounds::default();
+
+    for pred in generics.predicates {
+        match pred {
+            hir::WherePredicate::BoundPredicate(bound_pred) => {
+                let ty = icx.lowerer().lower_ty_maybe_return_type_notation(bound_pred.bounded_ty);
+                let bound_vars = tcx.late_bound_vars(bound_pred.hir_id);
+                icx.lowerer().lower_bounds(
+                    ty,
+                    bound_pred.bounds.iter(),
+                    &mut bounds,
+                    bound_vars,
+                    PredicateFilter::ConstIfConst,
+                );
+            }
+            _ => {}
+        }
+    }
+
+    if let Some((def_id, supertraits)) = trait_def_id_and_supertraits {
+        bounds.push_const_bound(
+            tcx,
+            ty::Binder::dummy(ty::TraitRef::identity(tcx, def_id.to_def_id())),
+            ty::HostPolarity::Maybe,
+            DUMMY_SP,
+        );
+
+        icx.lowerer().lower_bounds(
+            tcx.types.self_param,
+            supertraits.into_iter(),
+            &mut bounds,
+            ty::List::empty(),
+            PredicateFilter::ConstIfConst,
+        );
+    }
+
+    ty::ConstConditions {
+        parent: has_parent.then(|| tcx.local_parent(def_id).to_def_id()),
+        predicates: tcx.arena.alloc_from_iter(bounds.clauses(tcx).map(|(clause, span)| {
+            (
+                clause.kind().map_bound(|clause| match clause {
+                    ty::ClauseKind::HostEffect(ty::HostEffectPredicate {
+                        trait_ref,
+                        host: ty::HostPolarity::Maybe,
+                    }) => trait_ref,
+                    _ => bug!("converted {clause:?}"),
+                }),
+                span,
+            )
+        })),
+    }
+}
+
+pub(super) fn implied_const_bounds<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    def_id: LocalDefId,
+) -> ty::EarlyBinder<'tcx, &'tcx [(ty::PolyTraitRef<'tcx>, Span)]> {
+    let bounds = match tcx.hir_node_by_def_id(def_id) {
+        Node::Item(hir::Item { kind: hir::ItemKind::Trait(..), .. }) => {
+            if !tcx.is_const_trait(def_id.to_def_id()) {
+                return ty::EarlyBinder::bind(&[]);
+            }
+
+            implied_predicates_with_filter(
+                tcx,
+                def_id.to_def_id(),
+                PredicateFilter::SelfConstIfConst,
+            )
+        }
+        Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Type(..), .. }) => {
+            if !tcx.is_const_trait(tcx.local_parent(def_id).to_def_id()) {
+                return ty::EarlyBinder::bind(&[]);
+            }
+
+            explicit_item_bounds_with_filter(tcx, def_id, PredicateFilter::ConstIfConst)
+        }
+        Node::OpaqueTy(..) => {
+            // We should eventually collect the `~const` bounds on opaques.
+            return ty::EarlyBinder::bind(&[]);
+        }
+        _ => return ty::EarlyBinder::bind(&[]),
+    };
+
+    bounds.map_bound(|bounds| {
+        &*tcx.arena.alloc_from_iter(bounds.iter().copied().map(|(clause, span)| {
+            (
+                clause.kind().map_bound(|clause| match clause {
+                    ty::ClauseKind::HostEffect(ty::HostEffectPredicate {
+                        trait_ref,
+                        host: ty::HostPolarity::Maybe,
+                    }) => trait_ref,
+                    _ => bug!("converted {clause:?}"),
+                }),
+                span,
+            )
+        }))
+    })
+}