Implement const effect predicate in new solver
This commit is contained in:
@@ -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,
|
||||
)
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user