1
Fork 0

Implement const effect predicate in new solver

This commit is contained in:
Michael Goulet 2024-10-20 19:49:11 +00:00
parent a16d491054
commit cde29b9ec9
127 changed files with 1702 additions and 1170 deletions

View file

@ -81,6 +81,17 @@ impl<'tcx> Bounds<'tcx> {
self.clauses.insert(0, (trait_ref.upcast(tcx), span));
}
/// Push a `const` or `~const` bound as a `HostEffect` predicate.
pub(crate) fn push_const_bound(
&mut self,
tcx: TyCtxt<'tcx>,
bound_trait_ref: ty::PolyTraitRef<'tcx>,
host: ty::HostPolarity,
span: Span,
) {
self.clauses.push((bound_trait_ref.to_host_effect_clause(tcx, host), span));
}
pub(crate) fn clauses(
&self,
// FIXME(effects): remove tcx

View file

@ -181,6 +181,7 @@ fn compare_method_predicate_entailment<'tcx>(
});
// Create mapping from trait method to impl method.
let impl_def_id = impl_m.container_id(tcx);
let trait_to_impl_args = GenericArgs::identity_for_item(tcx, impl_m.def_id).rebase_onto(
tcx,
impl_m.container_id(tcx),
@ -204,6 +205,24 @@ fn compare_method_predicate_entailment<'tcx>(
trait_m_predicates.instantiate_own(tcx, trait_to_impl_args).map(|(predicate, _)| predicate),
);
// FIXME(effects): This should be replaced with a more dedicated method.
let check_const_if_const = tcx.constness(impl_def_id) == hir::Constness::Const;
if check_const_if_const {
// Augment the hybrid param-env with the const conditions
// of the impl header and the trait method.
hybrid_preds.extend(
tcx.const_conditions(impl_def_id)
.instantiate_identity(tcx)
.into_iter()
.chain(
tcx.const_conditions(trait_m.def_id).instantiate_own(tcx, trait_to_impl_args),
)
.map(|(trait_ref, _)| {
trait_ref.to_host_effect_clause(tcx, ty::HostPolarity::Maybe)
}),
);
}
let normalize_cause = traits::ObligationCause::misc(impl_m_span, impl_m_def_id);
let param_env = ty::ParamEnv::new(tcx.mk_clauses(&hybrid_preds), Reveal::UserFacing);
let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause);
@ -230,6 +249,34 @@ fn compare_method_predicate_entailment<'tcx>(
ocx.register_obligation(traits::Obligation::new(tcx, cause, param_env, predicate));
}
// If we're within a const implementation, we need to make sure that the method
// does not assume stronger `~const` bounds than the trait definition.
//
// This registers the `~const` bounds of the impl method, which we will prove
// using the hybrid param-env that we earlier augmented with the const conditions
// from the impl header and trait method declaration.
if check_const_if_const {
for (const_condition, span) in
tcx.const_conditions(impl_m.def_id).instantiate_own_identity()
{
let normalize_cause = traits::ObligationCause::misc(span, impl_m_def_id);
let const_condition = ocx.normalize(&normalize_cause, param_env, const_condition);
let cause =
ObligationCause::new(span, impl_m_def_id, ObligationCauseCode::CompareImplItem {
impl_item_def_id: impl_m_def_id,
trait_item_def_id: trait_m.def_id,
kind: impl_m.kind,
});
ocx.register_obligation(traits::Obligation::new(
tcx,
cause,
param_env,
const_condition.to_host_effect_clause(tcx, ty::HostPolarity::Maybe),
));
}
}
// We now need to check that the signature of the impl method is
// compatible with that of the trait method. We do this by
// checking that `impl_fty <: trait_fty`.
@ -1846,9 +1893,10 @@ fn compare_type_predicate_entailment<'tcx>(
trait_ty: ty::AssocItem,
impl_trait_ref: ty::TraitRef<'tcx>,
) -> Result<(), ErrorGuaranteed> {
let impl_def_id = impl_ty.container_id(tcx);
let trait_to_impl_args = GenericArgs::identity_for_item(tcx, impl_ty.def_id).rebase_onto(
tcx,
impl_ty.container_id(tcx),
impl_def_id,
impl_trait_ref.args,
);
@ -1856,7 +1904,9 @@ fn compare_type_predicate_entailment<'tcx>(
let trait_ty_predicates = tcx.predicates_of(trait_ty.def_id);
let impl_ty_own_bounds = impl_ty_predicates.instantiate_own_identity();
if impl_ty_own_bounds.len() == 0 {
let impl_ty_own_const_conditions =
tcx.const_conditions(impl_ty.def_id).instantiate_own_identity();
if impl_ty_own_bounds.len() == 0 && impl_ty_own_const_conditions.len() == 0 {
// Nothing to check.
return Ok(());
}
@ -1881,6 +1931,23 @@ fn compare_type_predicate_entailment<'tcx>(
let impl_ty_span = tcx.def_span(impl_ty_def_id);
let normalize_cause = ObligationCause::misc(impl_ty_span, impl_ty_def_id);
let check_const_if_const = tcx.constness(impl_def_id) == hir::Constness::Const;
if check_const_if_const {
// Augment the hybrid param-env with the const conditions
// of the impl header and the trait assoc type.
hybrid_preds.extend(
tcx.const_conditions(impl_ty_predicates.parent.unwrap())
.instantiate_identity(tcx)
.into_iter()
.chain(
tcx.const_conditions(trait_ty.def_id).instantiate_own(tcx, trait_to_impl_args),
)
.map(|(trait_ref, _)| {
trait_ref.to_host_effect_clause(tcx, ty::HostPolarity::Maybe)
}),
);
}
let param_env = ty::ParamEnv::new(tcx.mk_clauses(&hybrid_preds), Reveal::UserFacing);
let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause);
debug!(caller_bounds=?param_env.caller_bounds());
@ -1901,6 +1968,27 @@ fn compare_type_predicate_entailment<'tcx>(
ocx.register_obligation(traits::Obligation::new(tcx, cause, param_env, predicate));
}
if check_const_if_const {
// Validate the const conditions of the impl associated type.
for (const_condition, span) in impl_ty_own_const_conditions {
let normalize_cause = traits::ObligationCause::misc(span, impl_ty_def_id);
let const_condition = ocx.normalize(&normalize_cause, param_env, const_condition);
let cause =
ObligationCause::new(span, impl_ty_def_id, ObligationCauseCode::CompareImplItem {
impl_item_def_id: impl_ty_def_id,
trait_item_def_id: trait_ty.def_id,
kind: impl_ty.kind,
});
ocx.register_obligation(traits::Obligation::new(
tcx,
cause,
param_env,
const_condition.to_host_effect_clause(tcx, ty::HostPolarity::Maybe),
));
}
}
// Check that all obligations are satisfied by the implementation's
// version.
let errors = ocx.select_all_or_error();
@ -1983,7 +2071,7 @@ pub(super) fn check_type_bounds<'tcx>(
ObligationCause::new(impl_ty_span, impl_ty_def_id, code)
};
let obligations: Vec<_> = tcx
let mut obligations: Vec<_> = tcx
.explicit_item_bounds(trait_ty.def_id)
.iter_instantiated_copied(tcx, rebased_args)
.map(|(concrete_ty_bound, span)| {
@ -1991,6 +2079,22 @@ pub(super) fn check_type_bounds<'tcx>(
traits::Obligation::new(tcx, mk_cause(span), param_env, concrete_ty_bound)
})
.collect();
// Only in a const implementation do we need to check that the `~const` item bounds hold.
if tcx.constness(container_id) == hir::Constness::Const {
obligations.extend(
tcx.implied_const_bounds(trait_ty.def_id)
.iter_instantiated_copied(tcx, rebased_args)
.map(|(c, span)| {
traits::Obligation::new(
tcx,
mk_cause(span),
param_env,
c.to_host_effect_clause(tcx, ty::HostPolarity::Maybe),
)
}),
);
}
debug!(item_bounds=?obligations);
// Normalize predicates with the assumption that the GAT may always normalize

View file

@ -32,7 +32,8 @@ use rustc_trait_selection::traits::misc::{
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
use rustc_trait_selection::traits::{
self, FulfillmentError, ObligationCause, ObligationCauseCode, ObligationCtxt, WellFormedLoc,
self, FulfillmentError, Obligation, ObligationCause, ObligationCauseCode, ObligationCtxt,
WellFormedLoc,
};
use rustc_type_ir::TypeFlags;
use rustc_type_ir::solve::NoSolution;
@ -86,7 +87,7 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
self.body_def_id,
ObligationCauseCode::WellFormed(loc),
);
self.ocx.register_obligation(traits::Obligation::new(
self.ocx.register_obligation(Obligation::new(
self.tcx(),
cause,
self.param_env,
@ -1173,7 +1174,7 @@ fn check_type_defn<'tcx>(
wfcx.body_def_id,
ObligationCauseCode::Misc,
);
wfcx.register_obligation(traits::Obligation::new(
wfcx.register_obligation(Obligation::new(
tcx,
cause,
wfcx.param_env,
@ -1369,6 +1370,30 @@ fn check_impl<'tcx>(
obligation.cause.span = hir_self_ty.span;
}
}
// Ensure that the `~const` where clauses of the trait hold for the impl.
if tcx.constness(item.owner_id.def_id) == hir::Constness::Const {
for (bound, _) in
tcx.const_conditions(trait_ref.def_id).instantiate(tcx, trait_ref.args)
{
let bound = wfcx.normalize(
item.span,
Some(WellFormedLoc::Ty(item.hir_id().expect_owner().def_id)),
bound,
);
wfcx.register_obligation(Obligation::new(
tcx,
ObligationCause::new(
hir_self_ty.span,
wfcx.body_def_id,
ObligationCauseCode::WellFormed(None),
),
wfcx.param_env,
bound.to_host_effect_clause(tcx, ty::HostPolarity::Maybe),
))
}
}
debug!(?obligations);
wfcx.register_obligations(obligations);
}
@ -1561,7 +1586,7 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id
wfcx.body_def_id,
ObligationCauseCode::WhereClause(def_id.to_def_id(), DUMMY_SP),
);
traits::Obligation::new(tcx, cause, wfcx.param_env, pred)
Obligation::new(tcx, cause, wfcx.param_env, pred)
});
let predicates = predicates.instantiate_identity(tcx);
@ -1852,7 +1877,7 @@ fn receiver_is_implemented<'tcx>(
let tcx = wfcx.tcx();
let trait_ref = ty::TraitRef::new(tcx, receiver_trait_def_id, [receiver_ty]);
let obligation = traits::Obligation::new(tcx, cause, wfcx.param_env, trait_ref);
let obligation = Obligation::new(tcx, cause, wfcx.param_env, trait_ref);
if wfcx.infcx.predicate_must_hold_modulo_regions(&obligation) {
true
@ -2188,7 +2213,7 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
.unwrap_or(obligation_span);
}
let obligation = traits::Obligation::new(
let obligation = Obligation::new(
tcx,
traits::ObligationCause::new(
span,

View file

@ -77,6 +77,8 @@ pub fn provide(providers: &mut Providers) {
explicit_supertraits_containing_assoc_item:
predicates_of::explicit_supertraits_containing_assoc_item,
trait_explicit_predicates_and_bounds: predicates_of::trait_explicit_predicates_and_bounds,
const_conditions: predicates_of::const_conditions,
implied_const_bounds: predicates_of::implied_const_bounds,
type_param_predicates: predicates_of::type_param_predicates,
trait_def,
adt_def,

View file

@ -40,7 +40,16 @@ fn associated_type_bounds<'tcx>(
let mut bounds = Bounds::default();
icx.lowerer().lower_bounds(item_ty, hir_bounds, &mut bounds, ty::List::empty(), filter);
// Associated types are implicitly sized unless a `?Sized` bound is found
icx.lowerer().add_sized_bound(&mut bounds, item_ty, hir_bounds, None, span);
match filter {
PredicateFilter::All
| PredicateFilter::SelfOnly
| PredicateFilter::SelfThatDefines(_)
| PredicateFilter::SelfAndAssociatedTypeBounds => {
icx.lowerer().add_sized_bound(&mut bounds, item_ty, hir_bounds, None, span);
}
// `ConstIfConst` is only interested in `~const` bounds.
PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst => {}
}
let trait_def_id = tcx.local_parent(assoc_item_def_id);
let trait_predicates = tcx.trait_explicit_predicates_and_bounds(trait_def_id);
@ -109,10 +118,19 @@ fn remap_gat_vars_and_recurse_into_nested_projections<'tcx>(
} else {
// Only collect *self* type bounds if the filter is for self.
match filter {
PredicateFilter::SelfOnly | PredicateFilter::SelfThatDefines(_) => {
PredicateFilter::All => {}
PredicateFilter::SelfOnly => {
return None;
}
PredicateFilter::All | PredicateFilter::SelfAndAssociatedTypeBounds => {}
PredicateFilter::SelfThatDefines(_)
| PredicateFilter::SelfConstIfConst
| PredicateFilter::SelfAndAssociatedTypeBounds
| PredicateFilter::ConstIfConst => {
unreachable!(
"invalid predicate filter for \
`remap_gat_vars_and_recurse_into_nested_projections`"
)
}
}
clause_ty = alias_ty.self_ty();
@ -308,7 +326,17 @@ fn opaque_type_bounds<'tcx>(
let mut bounds = Bounds::default();
icx.lowerer().lower_bounds(item_ty, hir_bounds, &mut bounds, ty::List::empty(), filter);
// Opaque types are implicitly sized unless a `?Sized` bound is found
icx.lowerer().add_sized_bound(&mut bounds, item_ty, hir_bounds, None, span);
match filter {
PredicateFilter::All
| PredicateFilter::SelfOnly
| PredicateFilter::SelfThatDefines(_)
| PredicateFilter::SelfAndAssociatedTypeBounds => {
// Associated types are implicitly sized unless a `?Sized` bound is found
icx.lowerer().add_sized_bound(&mut bounds, item_ty, hir_bounds, None, span);
}
//`ConstIfConst` is only interested in `~const` bounds.
PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst => {}
}
debug!(?bounds);
tcx.arena.alloc_from_iter(bounds.clauses(tcx))

View file

@ -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,
)
}))
})
}

View file

@ -154,7 +154,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
for hir_bound in hir_bounds {
// In order to avoid cycles, when we're lowering `SelfThatDefines`,
// we skip over any traits that don't define the given associated type.
if let PredicateFilter::SelfThatDefines(assoc_name) = predicate_filter {
if let Some(trait_ref) = hir_bound.trait_ref()
&& let Some(trait_did) = trait_ref.trait_def_id()
@ -193,6 +192,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
);
}
hir::GenericBound::Outlives(lifetime) => {
// `ConstIfConst` is only interested in `~const` bounds.
if matches!(
predicate_filter,
PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst
) {
continue;
}
let region = self.lower_lifetime(lifetime, RegionInferReason::OutlivesBound);
bounds.push_region_bound(
self.tcx(),
@ -392,21 +399,31 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
},
);
bounds.push_projection_bound(
tcx,
projection_term.map_bound(|projection_term| ty::ProjectionPredicate {
projection_term,
term,
}),
constraint.span,
);
match predicate_filter {
PredicateFilter::All
| PredicateFilter::SelfOnly
| PredicateFilter::SelfThatDefines(_)
| PredicateFilter::SelfAndAssociatedTypeBounds => {
bounds.push_projection_bound(
tcx,
projection_term.map_bound(|projection_term| ty::ProjectionPredicate {
projection_term,
term,
}),
constraint.span,
);
}
// `ConstIfConst` is only interested in `~const` bounds.
PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst => {}
}
}
// Lower a constraint like `Item: Debug` as found in HIR bound `T: Iterator<Item: Debug>`
// to a bound involving a projection: `<T as Iterator>::Item: Debug`.
hir::AssocItemConstraintKind::Bound { bounds: hir_bounds } => {
match predicate_filter {
PredicateFilter::SelfOnly | PredicateFilter::SelfThatDefines(_) => {}
PredicateFilter::All | PredicateFilter::SelfAndAssociatedTypeBounds => {
PredicateFilter::All
| PredicateFilter::SelfAndAssociatedTypeBounds
| PredicateFilter::ConstIfConst => {
let projection_ty = projection_term
.map_bound(|projection_term| projection_term.expect_ty(self.tcx()));
// Calling `skip_binder` is okay, because `lower_bounds` expects the `param_ty`
@ -421,6 +438,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
predicate_filter,
);
}
PredicateFilter::SelfOnly
| PredicateFilter::SelfThatDefines(_)
| PredicateFilter::SelfConstIfConst => {}
}
}
}

View file

@ -78,7 +78,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
ty::ClauseKind::RegionOutlives(_)
| ty::ClauseKind::ConstArgHasType(..)
| ty::ClauseKind::WellFormed(_)
| ty::ClauseKind::ConstEvaluatable(_) => {
| ty::ClauseKind::ConstEvaluatable(_)
| ty::ClauseKind::HostEffect(..) => {
span_bug!(span, "did not expect {pred} clause in object bounds");
}
}

View file

@ -81,6 +81,12 @@ pub enum PredicateFilter {
/// For example, given `Self: Tr<A: B>`, this would expand to `Self: Tr`
/// and `<Self as Tr>::A: B`.
SelfAndAssociatedTypeBounds,
/// Filter only the `~const` bounds, which are lowered into `HostEffect` clauses.
ConstIfConst,
/// Filter only the `~const` bounds which are *also* in the supertrait position.
SelfConstIfConst,
}
#[derive(Debug)]
@ -693,8 +699,49 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
bound_vars,
);
debug!(?poly_trait_ref);
bounds.push_trait_bound(tcx, poly_trait_ref, span, polarity);
match predicate_filter {
PredicateFilter::All
| PredicateFilter::SelfOnly
| PredicateFilter::SelfThatDefines(..)
| PredicateFilter::SelfAndAssociatedTypeBounds => {
debug!(?poly_trait_ref);
bounds.push_trait_bound(tcx, poly_trait_ref, span, polarity);
match constness {
Some(ty::BoundConstness::Const) => {
if polarity == ty::PredicatePolarity::Positive {
bounds.push_const_bound(
tcx,
poly_trait_ref,
ty::HostPolarity::Const,
span,
);
}
}
Some(ty::BoundConstness::ConstIfConst) => {
// We don't emit a const bound here, since that would mean that we
// unconditionally need to prove a `HostEffect` predicate, even when
// the predicates are being instantiated in a non-const context. This
// is instead handled in the `const_conditions` query.
}
None => {}
}
}
// On the flip side, when filtering `ConstIfConst` bounds, we only need to convert
// `~const` bounds. All other predicates are handled in their respective queries.
//
// Note that like `PredicateFilter::SelfOnly`, we don't need to do any filtering
// here because we only call this on self bounds, and deal with the recursive case
// in `lower_assoc_item_constraint`.
PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst => match constness {
Some(ty::BoundConstness::ConstIfConst) => {
if polarity == ty::PredicatePolarity::Positive {
bounds.push_const_bound(tcx, poly_trait_ref, ty::HostPolarity::Maybe, span);
}
}
None | Some(ty::BoundConstness::Const) => {}
},
}
let mut dup_constraints = FxIndexMap::default();
for constraint in trait_segment.args().constraints {

View file

@ -530,6 +530,7 @@ fn trait_specialization_kind<'tcx>(
| ty::ClauseKind::Projection(_)
| ty::ClauseKind::ConstArgHasType(..)
| ty::ClauseKind::WellFormed(_)
| ty::ClauseKind::ConstEvaluatable(..) => None,
| ty::ClauseKind::ConstEvaluatable(..)
| ty::ClauseKind::HostEffect(..) => None,
}
}

View file

@ -53,7 +53,8 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> {
| ty::ClauseKind::Projection(_)
| ty::ClauseKind::ConstArgHasType(_, _)
| ty::ClauseKind::WellFormed(_)
| ty::ClauseKind::ConstEvaluatable(_) => {}
| ty::ClauseKind::ConstEvaluatable(_)
| ty::ClauseKind::HostEffect(..) => {}
}
}

View file

@ -836,14 +836,51 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn_sig.output()
}
#[tracing::instrument(level = "debug", skip(self, _span))]
#[tracing::instrument(level = "debug", skip(self, span))]
pub(super) fn enforce_context_effects(
&self,
_span: Span,
_callee_did: DefId,
_callee_args: GenericArgsRef<'tcx>,
span: Span,
callee_did: DefId,
callee_args: GenericArgsRef<'tcx>,
) {
todo!()
// FIXME(effects): We should be enforcing these effects unconditionally.
// This can be done as soon as we convert the standard library back to
// using const traits, since if we were to enforce these conditions now,
// we'd fail on basically every builtin trait call (i.e. `1 + 2`).
if !self.tcx.features().effects() {
return;
}
let host = match self.tcx.hir().body_const_context(self.body_id) {
Some(hir::ConstContext::Const { .. } | hir::ConstContext::Static(_)) => {
ty::HostPolarity::Const
}
Some(hir::ConstContext::ConstFn) => ty::HostPolarity::Maybe,
None => return,
};
// FIXME(effects): Should this be `is_const_fn_raw`? It depends on if we move
// const stability checking here too, I guess.
if self.tcx.is_const_fn(callee_did)
|| self
.tcx
.trait_of_item(callee_did)
.is_some_and(|def_id| self.tcx.is_const_trait(def_id))
{
let q = self.tcx.const_conditions(callee_did);
// FIXME(effects): Use this span with a better cause code.
for (cond, _) in q.instantiate(self.tcx, callee_args) {
self.register_predicate(Obligation::new(
self.tcx,
self.misc(span),
self.param_env,
cond.to_host_effect_clause(self.tcx, host),
));
}
} else {
// FIXME(effects): This should eventually be caught here.
// For now, though, we defer some const checking to MIR.
}
}
fn confirm_overloaded_call(

View file

@ -52,6 +52,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
| ty::PredicateKind::AliasRelate(..)
| ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
| ty::PredicateKind::ConstEquate(..)
| ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..))
| ty::PredicateKind::Ambiguous => false,
}
}

View file

@ -813,7 +813,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
| ty::ClauseKind::Projection(_)
| ty::ClauseKind::ConstArgHasType(_, _)
| ty::ClauseKind::WellFormed(_)
| ty::ClauseKind::ConstEvaluatable(_) => None,
| ty::ClauseKind::ConstEvaluatable(_)
| ty::ClauseKind::HostEffect(..) => None,
}
});

View file

@ -24,19 +24,9 @@ pub fn explicit_outlives_bounds<'tcx>(
param_env
.caller_bounds()
.into_iter()
.map(ty::Clause::kind)
.filter_map(ty::Clause::as_region_outlives_clause)
.filter_map(ty::Binder::no_bound_vars)
.filter_map(move |kind| match kind {
ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(r_a, r_b)) => {
Some(OutlivesBound::RegionSubRegion(r_b, r_a))
}
ty::ClauseKind::Trait(_)
| ty::ClauseKind::TypeOutlives(_)
| ty::ClauseKind::Projection(_)
| ty::ClauseKind::ConstArgHasType(_, _)
| ty::ClauseKind::WellFormed(_)
| ty::ClauseKind::ConstEvaluatable(_) => None,
})
.map(|ty::OutlivesPredicate(r_a, r_b)| OutlivesBound::RegionSubRegion(r_b, r_a))
}
impl<'tcx> InferCtxt<'tcx> {

View file

@ -1554,7 +1554,9 @@ impl<'tcx> LateLintPass<'tcx> for TrivialConstraints {
// Ignore bounds that a user can't type
| ClauseKind::WellFormed(..)
// FIXME(generic_const_exprs): `ConstEvaluatable` can be written
| ClauseKind::ConstEvaluatable(..) => continue,
| ClauseKind::ConstEvaluatable(..)
// Users don't write this directly, only via another trait ref.
| ty::ClauseKind::HostEffect(..) => continue,
};
if predicate.is_global() {
cx.emit_span_lint(TRIVIAL_BOUNDS, span, BuiltinTrivialBounds {

View file

@ -275,6 +275,8 @@ provide! { tcx, def_id, other, cdata,
impl_parent => { table }
defaultness => { table_direct }
constness => { table_direct }
const_conditions => { table }
implied_const_bounds => { table_defaulted_array }
coerce_unsized_info => {
Ok(cdata
.root

View file

@ -1423,6 +1423,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
let g = tcx.generics_of(def_id);
record!(self.tables.generics_of[def_id] <- g);
record!(self.tables.explicit_predicates_of[def_id] <- self.tcx.explicit_predicates_of(def_id));
record!(self.tables.const_conditions[def_id] <- self.tcx.const_conditions(def_id));
let inferred_outlives = self.tcx.inferred_outlives_of(def_id);
record_defaulted_array!(self.tables.inferred_outlives_of[def_id] <- inferred_outlives);
@ -1456,7 +1457,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
self.tcx.explicit_super_predicates_of(def_id).skip_binder());
record_defaulted_array!(self.tables.explicit_implied_predicates_of[def_id] <-
self.tcx.explicit_implied_predicates_of(def_id).skip_binder());
record_defaulted_array!(self.tables.implied_const_bounds[def_id]
<- self.tcx.implied_const_bounds(def_id).skip_binder());
let module_children = self.tcx.module_children_local(local_id);
record_array!(self.tables.module_children_non_reexports[def_id] <-
module_children.iter().map(|child| child.res.def_id().index));
@ -1467,6 +1469,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
self.tcx.explicit_super_predicates_of(def_id).skip_binder());
record_defaulted_array!(self.tables.explicit_implied_predicates_of[def_id] <-
self.tcx.explicit_implied_predicates_of(def_id).skip_binder());
record_defaulted_array!(self.tables.implied_const_bounds[def_id]
<- self.tcx.implied_const_bounds(def_id).skip_binder());
}
if let DefKind::Trait | DefKind::Impl { .. } = def_kind {
let associated_item_def_ids = self.tcx.associated_item_def_ids(def_id);
@ -1649,6 +1653,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
if let ty::AssocKind::Type = item.kind {
self.encode_explicit_item_bounds(def_id);
self.encode_explicit_item_super_predicates(def_id);
record_defaulted_array!(self.tables.implied_const_bounds[def_id]
<- self.tcx.implied_const_bounds(def_id).skip_binder());
}
}
AssocItemContainer::ImplContainer => {

View file

@ -392,6 +392,7 @@ define_tables! {
inferred_outlives_of: Table<DefIndex, LazyArray<(ty::Clause<'static>, Span)>>,
explicit_super_predicates_of: Table<DefIndex, LazyArray<(ty::Clause<'static>, Span)>>,
explicit_implied_predicates_of: Table<DefIndex, LazyArray<(ty::Clause<'static>, Span)>>,
implied_const_bounds: Table<DefIndex, LazyArray<(ty::PolyTraitRef<'static>, Span)>>,
inherent_impls: Table<DefIndex, LazyArray<DefIndex>>,
associated_types_for_impl_traits_in_associated_fn: Table<DefIndex, LazyArray<DefId>>,
opt_rpitit_info: Table<DefIndex, Option<LazyValue<ty::ImplTraitInTraitData>>>,
@ -435,6 +436,7 @@ define_tables! {
thir_abstract_const: Table<DefIndex, LazyValue<ty::EarlyBinder<'static, ty::Const<'static>>>>,
impl_parent: Table<DefIndex, RawDefId>,
constness: Table<DefIndex, hir::Constness>,
const_conditions: Table<DefIndex, LazyValue<ty::ConstConditions<'static>>>,
defaultness: Table<DefIndex, hir::Defaultness>,
// FIXME(eddyb) perhaps compute this on the fly if cheap enough?
coerce_unsized_info: Table<DefIndex, LazyValue<ty::adjustment::CoerceUnsizedInfo>>,

View file

@ -370,6 +370,7 @@ tcx_lifetime! {
rustc_middle::ty::FnSig,
rustc_middle::ty::GenericArg,
rustc_middle::ty::GenericPredicates,
rustc_middle::ty::ConstConditions,
rustc_middle::ty::inhabitedness::InhabitedPredicate,
rustc_middle::ty::Instance,
rustc_middle::ty::InstanceKind,

View file

@ -683,6 +683,24 @@ rustc_queries! {
}
}
query const_conditions(
key: DefId
) -> ty::ConstConditions<'tcx> {
desc { |tcx| "computing the conditions for `{}` to be considered const",
tcx.def_path_str(key)
}
separate_provide_extern
}
query implied_const_bounds(
key: DefId
) -> ty::EarlyBinder<'tcx, &'tcx [(ty::PolyTraitRef<'tcx>, Span)]> {
desc { |tcx| "computing the implied `~const` bounds for `{}`",
tcx.def_path_str(key)
}
separate_provide_extern
}
/// To avoid cycles within the predicates of a single item we compute
/// per-type-parameter predicates for resolving `T::AssocTy`.
query type_param_predicates(

View file

@ -386,6 +386,17 @@ impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D> for [(ty::Claus
}
}
impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D>
for [(ty::PolyTraitRef<'tcx>, Span)]
{
fn decode(decoder: &mut D) -> &'tcx Self {
decoder
.interner()
.arena
.alloc_from_iter((0..decoder.read_usize()).map(|_| Decodable::decode(decoder)))
}
}
impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D>
for ty::List<ty::BoundVariableKind>
{

View file

@ -78,10 +78,10 @@ use crate::traits::solve::{
use crate::ty::predicate::ExistentialPredicateStableCmpExt as _;
use crate::ty::{
self, AdtDef, AdtDefData, AdtKind, Binder, Clause, Clauses, Const, GenericArg, GenericArgs,
GenericArgsRef, GenericParamDefKind, ImplPolarity, List, ListWithCachedTypeInfo, ParamConst,
ParamTy, Pattern, PatternKind, PolyExistentialPredicate, PolyFnSig, Predicate, PredicateKind,
PredicatePolarity, Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyVid,
Visibility,
GenericArgsRef, GenericParamDefKind, HostPolarity, ImplPolarity, List, ListWithCachedTypeInfo,
ParamConst, ParamTy, Pattern, PatternKind, PolyExistentialPredicate, PolyFnSig, Predicate,
PredicateKind, PredicatePolarity, Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty,
TyKind, TyVid, Visibility,
};
#[allow(rustc::usage_of_ty_tykind)]
@ -383,6 +383,28 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
self.explicit_implied_predicates_of(def_id).map_bound(|preds| preds.into_iter().copied())
}
fn is_const_impl(self, def_id: DefId) -> bool {
self.constness(def_id) == hir::Constness::Const
}
fn const_conditions(
self,
def_id: DefId,
) -> ty::EarlyBinder<'tcx, impl IntoIterator<Item = ty::Binder<'tcx, ty::TraitRef<'tcx>>>> {
ty::EarlyBinder::bind(
self.const_conditions(def_id).instantiate_identity(self).into_iter().map(|(c, _)| c),
)
}
fn implied_const_bounds(
self,
def_id: DefId,
) -> ty::EarlyBinder<'tcx, impl IntoIterator<Item = ty::Binder<'tcx, ty::TraitRef<'tcx>>>> {
ty::EarlyBinder::bind(
self.implied_const_bounds(def_id).iter_identity_copied().map(|(c, _)| c),
)
}
fn has_target_features(self, def_id: DefId) -> bool {
!self.codegen_fn_attrs(def_id).target_features.is_empty()
}
@ -2189,7 +2211,7 @@ macro_rules! nop_slice_lift {
nop_slice_lift! {ty::ValTree<'a> => ty::ValTree<'tcx>}
TrivialLiftImpls! {
ImplPolarity, PredicatePolarity, Promoted
ImplPolarity, PredicatePolarity, Promoted, HostPolarity,
}
macro_rules! sty_debug_print {

View file

@ -265,6 +265,12 @@ impl FlagComputation {
ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) => {
self.add_args(trait_pred.trait_ref.args);
}
ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(ty::HostEffectPredicate {
trait_ref,
host: _,
})) => {
self.add_args(trait_ref.args);
}
ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(
a,
b,

View file

@ -422,3 +422,73 @@ impl<'tcx> GenericPredicates<'tcx> {
instantiated.spans.extend(self.predicates.iter().map(|(_, s)| s));
}
}
/// `~const` bounds for a given item. This is represented using a struct much like
/// `GenericPredicates`, where you can either choose to only instantiate the "own"
/// bounds or all of the bounds including those from the parent. This distinction
/// is necessary for code like `compare_method_predicate_entailment`.
#[derive(Copy, Clone, Default, Debug, TyEncodable, TyDecodable, HashStable)]
pub struct ConstConditions<'tcx> {
pub parent: Option<DefId>,
pub predicates: &'tcx [(ty::PolyTraitRef<'tcx>, Span)],
}
impl<'tcx> ConstConditions<'tcx> {
pub fn instantiate(
self,
tcx: TyCtxt<'tcx>,
args: GenericArgsRef<'tcx>,
) -> Vec<(ty::PolyTraitRef<'tcx>, Span)> {
let mut instantiated = vec![];
self.instantiate_into(tcx, &mut instantiated, args);
instantiated
}
pub fn instantiate_own(
self,
tcx: TyCtxt<'tcx>,
args: GenericArgsRef<'tcx>,
) -> impl Iterator<Item = (ty::PolyTraitRef<'tcx>, Span)> + DoubleEndedIterator + ExactSizeIterator
{
EarlyBinder::bind(self.predicates).iter_instantiated_copied(tcx, args)
}
pub fn instantiate_own_identity(
self,
) -> impl Iterator<Item = (ty::PolyTraitRef<'tcx>, Span)> + DoubleEndedIterator + ExactSizeIterator
{
EarlyBinder::bind(self.predicates).iter_identity_copied()
}
#[instrument(level = "debug", skip(self, tcx))]
fn instantiate_into(
self,
tcx: TyCtxt<'tcx>,
instantiated: &mut Vec<(ty::PolyTraitRef<'tcx>, Span)>,
args: GenericArgsRef<'tcx>,
) {
if let Some(def_id) = self.parent {
tcx.const_conditions(def_id).instantiate_into(tcx, instantiated, args);
}
instantiated.extend(
self.predicates.iter().map(|&(p, s)| (EarlyBinder::bind(p).instantiate(tcx, args), s)),
);
}
pub fn instantiate_identity(self, tcx: TyCtxt<'tcx>) -> Vec<(ty::PolyTraitRef<'tcx>, Span)> {
let mut instantiated = vec![];
self.instantiate_identity_into(tcx, &mut instantiated);
instantiated
}
fn instantiate_identity_into(
self,
tcx: TyCtxt<'tcx>,
instantiated: &mut Vec<(ty::PolyTraitRef<'tcx>, Span)>,
) {
if let Some(def_id) = self.parent {
tcx.const_conditions(def_id).instantiate_identity_into(tcx, instantiated);
}
instantiated.extend(self.predicates.iter().copied());
}
}

View file

@ -84,12 +84,13 @@ pub use self::parameterized::ParameterizedOverTcx;
pub use self::pattern::{Pattern, PatternKind};
pub use self::predicate::{
AliasTerm, Clause, ClauseKind, CoercePredicate, ExistentialPredicate,
ExistentialPredicateStableCmpExt, ExistentialProjection, ExistentialTraitRef, NormalizesTo,
OutlivesPredicate, PolyCoercePredicate, PolyExistentialPredicate, PolyExistentialProjection,
PolyExistentialTraitRef, PolyProjectionPredicate, PolyRegionOutlivesPredicate,
PolySubtypePredicate, PolyTraitPredicate, PolyTraitRef, PolyTypeOutlivesPredicate, Predicate,
PredicateKind, ProjectionPredicate, RegionOutlivesPredicate, SubtypePredicate, ToPolyTraitRef,
TraitPredicate, TraitRef, TypeOutlivesPredicate,
ExistentialPredicateStableCmpExt, ExistentialProjection, ExistentialTraitRef,
HostEffectPredicate, NormalizesTo, OutlivesPredicate, PolyCoercePredicate,
PolyExistentialPredicate, PolyExistentialProjection, PolyExistentialTraitRef,
PolyProjectionPredicate, PolyRegionOutlivesPredicate, PolySubtypePredicate, PolyTraitPredicate,
PolyTraitRef, PolyTypeOutlivesPredicate, Predicate, PredicateKind, ProjectionPredicate,
RegionOutlivesPredicate, SubtypePredicate, ToPolyTraitRef, TraitPredicate, TraitRef,
TypeOutlivesPredicate,
};
pub use self::region::BoundRegionKind::*;
pub use self::region::{

View file

@ -132,6 +132,7 @@ parameterized_over_tcx! {
ty::Ty,
ty::FnSig,
ty::GenericPredicates,
ty::ConstConditions,
ty::TraitRef,
ty::Const,
ty::Predicate,

View file

@ -19,6 +19,7 @@ pub type ExistentialPredicate<'tcx> = ir::ExistentialPredicate<TyCtxt<'tcx>>;
pub type ExistentialTraitRef<'tcx> = ir::ExistentialTraitRef<TyCtxt<'tcx>>;
pub type ExistentialProjection<'tcx> = ir::ExistentialProjection<TyCtxt<'tcx>>;
pub type TraitPredicate<'tcx> = ir::TraitPredicate<TyCtxt<'tcx>>;
pub type HostEffectPredicate<'tcx> = ir::HostEffectPredicate<TyCtxt<'tcx>>;
pub type ClauseKind<'tcx> = ir::ClauseKind<TyCtxt<'tcx>>;
pub type PredicateKind<'tcx> = ir::PredicateKind<TyCtxt<'tcx>>;
pub type NormalizesTo<'tcx> = ir::NormalizesTo<TyCtxt<'tcx>>;
@ -143,6 +144,7 @@ impl<'tcx> Predicate<'tcx> {
| PredicateKind::AliasRelate(..)
| PredicateKind::NormalizesTo(..) => false,
PredicateKind::Clause(ClauseKind::Trait(_))
| PredicateKind::Clause(ClauseKind::HostEffect(..))
| PredicateKind::Clause(ClauseKind::RegionOutlives(_))
| PredicateKind::Clause(ClauseKind::TypeOutlives(_))
| PredicateKind::Clause(ClauseKind::Projection(_))
@ -644,6 +646,7 @@ impl<'tcx> Predicate<'tcx> {
match predicate.skip_binder() {
PredicateKind::Clause(ClauseKind::Trait(t)) => Some(predicate.rebind(t)),
PredicateKind::Clause(ClauseKind::Projection(..))
| PredicateKind::Clause(ClauseKind::HostEffect(..))
| PredicateKind::Clause(ClauseKind::ConstArgHasType(..))
| PredicateKind::NormalizesTo(..)
| PredicateKind::AliasRelate(..)
@ -664,6 +667,7 @@ impl<'tcx> Predicate<'tcx> {
match predicate.skip_binder() {
PredicateKind::Clause(ClauseKind::Projection(t)) => Some(predicate.rebind(t)),
PredicateKind::Clause(ClauseKind::Trait(..))
| PredicateKind::Clause(ClauseKind::HostEffect(..))
| PredicateKind::Clause(ClauseKind::ConstArgHasType(..))
| PredicateKind::NormalizesTo(..)
| PredicateKind::AliasRelate(..)

View file

@ -3075,6 +3075,15 @@ define_print! {
p!(print(self.trait_ref.print_trait_sugared()))
}
ty::HostEffectPredicate<'tcx> {
let constness = match self.host {
ty::HostPolarity::Const => { "const" }
ty::HostPolarity::Maybe => { "~const" }
};
p!(print(self.trait_ref.self_ty()), ": {constness} ");
p!(print(self.trait_ref.print_trait_sugared()))
}
ty::TypeAndMut<'tcx> {
p!(write("{}", self.mutbl.prefix_str()), print(self.ty))
}
@ -3087,6 +3096,7 @@ define_print! {
ty::ClauseKind::RegionOutlives(predicate) => p!(print(predicate)),
ty::ClauseKind::TypeOutlives(predicate) => p!(print(predicate)),
ty::ClauseKind::Projection(predicate) => p!(print(predicate)),
ty::ClauseKind::HostEffect(predicate) => p!(print(predicate)),
ty::ClauseKind::ConstArgHasType(ct, ty) => {
p!("the constant `", print(ct), "` has type `", print(ty), "`")
},

View file

@ -0,0 +1,296 @@
//! Dealing with host effect goals, i.e. enforcing the constness in
//! `T: const Trait` or `T: ~const Trait`.
use rustc_type_ir::fast_reject::DeepRejectCtxt;
use rustc_type_ir::inherent::*;
use rustc_type_ir::{self as ty, Interner};
use tracing::instrument;
use super::assembly::Candidate;
use crate::delegate::SolverDelegate;
use crate::solve::assembly::{self};
use crate::solve::{
BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, NoSolution,
QueryResult,
};
impl<D, I> assembly::GoalKind<D> for ty::HostEffectPredicate<I>
where
D: SolverDelegate<Interner = I>,
I: Interner,
{
fn self_ty(self) -> I::Ty {
self.self_ty()
}
fn trait_ref(self, _: I) -> ty::TraitRef<I> {
self.trait_ref
}
fn with_self_ty(self, cx: I, self_ty: I::Ty) -> Self {
self.with_self_ty(cx, self_ty)
}
fn trait_def_id(self, _: I) -> I::DefId {
self.def_id()
}
fn probe_and_match_goal_against_assumption(
ecx: &mut EvalCtxt<'_, D>,
source: rustc_type_ir::solve::CandidateSource<I>,
goal: Goal<I, Self>,
assumption: <I as Interner>::Clause,
then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
) -> Result<Candidate<I>, NoSolution> {
if let Some(host_clause) = assumption.as_host_effect_clause() {
if host_clause.def_id() == goal.predicate.def_id()
&& host_clause.host().satisfies(goal.predicate.host)
{
if !DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify(
goal.predicate.trait_ref.args,
host_clause.skip_binder().trait_ref.args,
) {
return Err(NoSolution);
}
ecx.probe_trait_candidate(source).enter(|ecx| {
let assumption_trait_pred = ecx.instantiate_binder_with_infer(host_clause);
ecx.eq(
goal.param_env,
goal.predicate.trait_ref,
assumption_trait_pred.trait_ref,
)?;
then(ecx)
})
} else {
Err(NoSolution)
}
} else {
Err(NoSolution)
}
}
fn consider_impl_candidate(
ecx: &mut EvalCtxt<'_, D>,
goal: Goal<I, Self>,
impl_def_id: <I as Interner>::DefId,
) -> Result<Candidate<I>, NoSolution> {
let cx = ecx.cx();
let impl_trait_ref = cx.impl_trait_ref(impl_def_id);
if !DeepRejectCtxt::relate_rigid_infer(ecx.cx())
.args_may_unify(goal.predicate.trait_ref.args, impl_trait_ref.skip_binder().args)
{
return Err(NoSolution);
}
let impl_polarity = cx.impl_polarity(impl_def_id);
match impl_polarity {
ty::ImplPolarity::Negative => return Err(NoSolution),
ty::ImplPolarity::Reservation => {
unimplemented!("reservation impl for const trait: {:?}", goal)
}
ty::ImplPolarity::Positive => {}
};
if !cx.is_const_impl(impl_def_id) {
return Err(NoSolution);
}
ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| {
let impl_args = ecx.fresh_args_for_item(impl_def_id);
ecx.record_impl_args(impl_args);
let impl_trait_ref = impl_trait_ref.instantiate(cx, impl_args);
ecx.eq(goal.param_env, goal.predicate.trait_ref, impl_trait_ref)?;
let where_clause_bounds = cx
.predicates_of(impl_def_id)
.iter_instantiated(cx, impl_args)
.map(|pred| goal.with(cx, pred));
ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds);
// For this impl to be `const`, we need to check its `~const` bounds too.
let const_conditions = cx
.const_conditions(impl_def_id)
.iter_instantiated(cx, impl_args)
.map(|bound_trait_ref| {
goal.with(cx, bound_trait_ref.to_host_effect_clause(cx, goal.predicate.host))
});
ecx.add_goals(GoalSource::ImplWhereBound, const_conditions);
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
})
}
fn consider_error_guaranteed_candidate(
ecx: &mut EvalCtxt<'_, D>,
_guar: <I as Interner>::ErrorGuaranteed,
) -> Result<Candidate<I>, NoSolution> {
ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
.enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
}
fn consider_auto_trait_candidate(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
unreachable!("auto traits are never const")
}
fn consider_trait_alias_candidate(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
unreachable!("trait aliases are never const")
}
fn consider_builtin_sized_candidate(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
unreachable!("Sized is never const")
}
fn consider_builtin_copy_clone_candidate(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
todo!("Copy/Clone is not yet const")
}
fn consider_builtin_pointer_like_candidate(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
unreachable!("PointerLike is not const")
}
fn consider_builtin_fn_ptr_trait_candidate(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
todo!("Fn* are not yet const")
}
fn consider_builtin_fn_trait_candidates(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
_kind: rustc_type_ir::ClosureKind,
) -> Result<Candidate<I>, NoSolution> {
todo!("Fn* are not yet const")
}
fn consider_builtin_async_fn_trait_candidates(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
_kind: rustc_type_ir::ClosureKind,
) -> Result<Candidate<I>, NoSolution> {
todo!("AsyncFn* are not yet const")
}
fn consider_builtin_async_fn_kind_helper_candidate(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
unreachable!("AsyncFnKindHelper is not const")
}
fn consider_builtin_tuple_candidate(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
unreachable!("Tuple trait is not const")
}
fn consider_builtin_pointee_candidate(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
unreachable!("Pointee is not const")
}
fn consider_builtin_future_candidate(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
unreachable!("Future is not const")
}
fn consider_builtin_iterator_candidate(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
todo!("Iterator is not yet const")
}
fn consider_builtin_fused_iterator_candidate(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
unreachable!("FusedIterator is not const")
}
fn consider_builtin_async_iterator_candidate(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
unreachable!("AsyncIterator is not const")
}
fn consider_builtin_coroutine_candidate(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
unreachable!("Coroutine is not const")
}
fn consider_builtin_discriminant_kind_candidate(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
unreachable!("DiscriminantKind is not const")
}
fn consider_builtin_async_destruct_candidate(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
unreachable!("AsyncDestruct is not const")
}
fn consider_builtin_destruct_candidate(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
unreachable!("Destruct is not const")
}
fn consider_builtin_transmute_candidate(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
unreachable!("TransmuteFrom is not const")
}
fn consider_structural_builtin_unsize_candidates(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Vec<Candidate<I>> {
unreachable!("Unsize is not const")
}
}
impl<D, I> EvalCtxt<'_, D>
where
D: SolverDelegate<Interner = I>,
I: Interner,
{
#[instrument(level = "trace", skip(self))]
pub(super) fn compute_host_effect_goal(
&mut self,
goal: Goal<I, ty::HostEffectPredicate<I>>,
) -> QueryResult<I> {
let candidates = self.assemble_and_evaluate_candidates(goal);
self.merge_candidates(candidates)
}
}

View file

@ -443,6 +443,9 @@ where
ty::PredicateKind::Clause(ty::ClauseKind::Trait(predicate)) => {
self.compute_trait_goal(Goal { param_env, predicate })
}
ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(predicate)) => {
self.compute_host_effect_goal(Goal { param_env, predicate })
}
ty::PredicateKind::Clause(ty::ClauseKind::Projection(predicate)) => {
self.compute_projection_goal(Goal { param_env, predicate })
}

View file

@ -13,6 +13,7 @@
mod alias_relate;
mod assembly;
mod effect_goals;
mod eval_ctxt;
pub mod inspect;
mod normalizes_to;

View file

@ -137,6 +137,10 @@ where
ty::ClauseKind::Trait(ty::TraitPredicate { trait_ref, polarity: _ }) => {
self.visit_trait(trait_ref)
}
ty::ClauseKind::HostEffect(pred) => {
try_visit!(self.visit_trait(pred.trait_ref));
pred.host.visit_with(self)
}
ty::ClauseKind::Projection(ty::ProjectionPredicate {
projection_term: projection_ty,
term,

View file

@ -689,6 +689,9 @@ impl<'tcx> Stable<'tcx> for ty::ClauseKind<'tcx> {
ClauseKind::ConstEvaluatable(const_) => {
stable_mir::ty::ClauseKind::ConstEvaluatable(const_.stable(tables))
}
ClauseKind::HostEffect(..) => {
todo!()
}
}
}
}

View file

@ -156,8 +156,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
(leaf_trait_predicate, &obligation)
};
let (main_trait_predicate, leaf_trait_predicate, predicate_constness) = self.get_effects_trait_pred_override(main_trait_predicate, leaf_trait_predicate, span);
let main_trait_ref = main_trait_predicate.to_poly_trait_ref();
let leaf_trait_ref = leaf_trait_predicate.to_poly_trait_ref();
@ -228,7 +226,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
let err_msg = self.get_standard_error_message(
main_trait_predicate,
message,
predicate_constness,
None,
append_const_msg,
post_message,
);
@ -289,13 +287,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
);
}
if tcx.is_lang_item(leaf_trait_ref.def_id(), LangItem::Drop)
&& matches!(predicate_constness, Some(ty::BoundConstness::ConstIfConst | ty::BoundConstness::Const))
{
err.note("`~const Drop` was renamed to `~const Destruct`");
err.note("See <https://github.com/rust-lang/rust/pull/94901> for more details");
}
let explanation = get_explanation_based_on_obligation(
self.tcx,
&obligation,
@ -541,6 +532,29 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
err
}
ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(predicate)) => {
// FIXME(effects): We should recompute the predicate with `~const`
// if it's `const`, and if it holds, explain that this bound only
// *conditionally* holds. If that fails, we should also do selection
// to drill this down to an impl or built-in source, so we can
// point at it and explain that while the trait *is* implemented,
// that implementation is not const.
let err_msg = self.get_standard_error_message(
bound_predicate.rebind(ty::TraitPredicate {
trait_ref: predicate.trait_ref,
polarity: ty::PredicatePolarity::Positive,
}),
None,
Some(match predicate.host {
ty::HostPolarity::Maybe => ty::BoundConstness::ConstIfConst,
ty::HostPolarity::Const => ty::BoundConstness::Const,
}),
None,
String::new(),
);
struct_span_code_err!(self.dcx(), span, E0277, "{}", err_msg)
}
ty::PredicateKind::Subtype(predicate) => {
// Errors for Subtype predicates show up as
// `FulfillmentErrorCode::SubtypeError`,
@ -2374,16 +2388,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
})
}
// FIXME(effects): Remove this.
fn get_effects_trait_pred_override(
&self,
p: ty::PolyTraitPredicate<'tcx>,
leaf: ty::PolyTraitPredicate<'tcx>,
_span: Span,
) -> (ty::PolyTraitPredicate<'tcx>, ty::PolyTraitPredicate<'tcx>, ty::BoundConstness) {
(p, leaf, ty::BoundConstness::NotConst)
}
fn add_tuple_trait_message(
&self,
obligation_cause_code: &ObligationCauseCode<'tcx>,

View file

@ -806,7 +806,8 @@ impl<'tcx> AutoTraitFinder<'tcx> {
| ty::PredicateKind::Subtype(..)
// FIXME(generic_const_exprs): you can absolutely add this as a where clauses
| ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
| ty::PredicateKind::Coerce(..) => {}
| ty::PredicateKind::Coerce(..)
| ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..)) => {}
ty::PredicateKind::Ambiguous => return false,
};
}

View file

@ -245,6 +245,7 @@ fn predicate_references_self<'tcx>(
| ty::ClauseKind::RegionOutlives(..)
// FIXME(generic_const_exprs): this can mention `Self`
| ty::ClauseKind::ConstEvaluatable(..)
| ty::ClauseKind::HostEffect(..)
=> None,
}
}
@ -284,7 +285,8 @@ fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
| ty::ClauseKind::Projection(_)
| ty::ClauseKind::ConstArgHasType(_, _)
| ty::ClauseKind::WellFormed(_)
| ty::ClauseKind::ConstEvaluatable(_) => false,
| ty::ClauseKind::ConstEvaluatable(_)
| ty::ClauseKind::HostEffect(..) => false,
})
}

View file

@ -372,7 +372,11 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
| ty::PredicateKind::Subtype(_)
| ty::PredicateKind::Coerce(_)
| ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
| ty::PredicateKind::ConstEquate(..) => {
| ty::PredicateKind::ConstEquate(..)
// FIXME(effects): We may need to do this using the higher-ranked
// pred instead of just instantiating it with placeholders b/c of
// higher-ranked implied bound issues in the old solver.
| ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..)) => {
let pred = ty::Binder::dummy(infcx.enter_forall_and_leak_universe(binder));
let mut obligations = PredicateObligations::with_capacity(1);
obligations.push(obligation.with(infcx.tcx, pred));
@ -398,6 +402,10 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
)
}
ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..)) => {
ProcessResult::Changed(Default::default())
}
ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(data)) => {
if infcx.considering_regions {
infcx.region_outlives_predicate(&obligation.cause, Binder::dummy(data));

View file

@ -96,6 +96,7 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>(
// FIXME(const_generics): Make sure that `<'a, 'b, const N: &'a &'b u32>` is sound
// if we ever support that
ty::PredicateKind::Clause(ty::ClauseKind::Trait(..))
| ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..))
| ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..))
| ty::PredicateKind::Subtype(..)
| ty::PredicateKind::Coerce(..)
@ -200,6 +201,7 @@ pub fn compute_implied_outlives_bounds_compat_inner<'tcx>(
// FIXME(const_generics): Make sure that `<'a, 'b, const N: &'a &'b u32>` is sound
// if we ever support that
ty::PredicateKind::Clause(ty::ClauseKind::Trait(..))
| ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..))
| ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..))
| ty::PredicateKind::Subtype(..)
| ty::PredicateKind::Coerce(..)

View file

@ -645,6 +645,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
self.evaluate_trait_predicate_recursively(previous_stack, obligation)
}
ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..)) => {
// FIXME(effects): It should be relatively straightforward to implement
// old trait solver support for `HostEffect` bounds; or at least basic
// support for them.
todo!()
}
ty::PredicateKind::Subtype(p) => {
let p = bound_predicate.rebind(p);
// Does this code ever run?

View file

@ -170,6 +170,10 @@ pub fn clause_obligations<'tcx>(
ty::ClauseKind::Trait(t) => {
wf.compute_trait_pred(t, Elaborate::None);
}
ty::ClauseKind::HostEffect(..) => {
// Technically the well-formedness of this predicate is implied by
// the corresponding trait predicate it should've been generated beside.
}
ty::ClauseKind::RegionOutlives(..) => {}
ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, _reg)) => {
wf.compute(ty.into());
@ -1021,6 +1025,7 @@ pub(crate) fn required_region_bounds<'tcx>(
}
}
ty::ClauseKind::Trait(_)
| ty::ClauseKind::HostEffect(..)
| ty::ClauseKind::RegionOutlives(_)
| ty::ClauseKind::Projection(_)
| ty::ClauseKind::ConstArgHasType(_, _)

View file

@ -55,6 +55,7 @@ fn not_outlives_predicate(p: ty::Predicate<'_>) -> bool {
| ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(..)) => false,
ty::PredicateKind::Clause(ty::ClauseKind::Trait(..))
| ty::PredicateKind::Clause(ty::ClauseKind::Projection(..))
| ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..))
| ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..))
| ty::PredicateKind::NormalizesTo(..)
| ty::PredicateKind::AliasRelate(..)

View file

@ -150,6 +150,15 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> {
});
}
// We extend the param-env of our item with the const conditions of the item,
// since we're allowed to assume `~const` bounds hold within the item itself.
predicates.extend(
tcx.const_conditions(def_id)
.instantiate_identity(tcx)
.into_iter()
.map(|(trait_ref, _)| trait_ref.to_host_effect_clause(tcx, ty::HostPolarity::Maybe)),
);
let local_did = def_id.as_local();
let unnormalized_env =

View file

@ -155,6 +155,16 @@ impl<I: Interner, O: Elaboratable<I>> Elaborator<I, O> {
),
};
}
// `T: ~const Trait` implies `T: ~const Supertrait`.
ty::ClauseKind::HostEffect(data) => self.extend_deduped(
cx.implied_const_bounds(data.def_id()).iter_identity().map(|trait_ref| {
elaboratable.child(
trait_ref
.to_host_effect_clause(cx, data.host)
.instantiate_supertrait(cx, bound_clause.rebind(data.trait_ref)),
)
}),
),
ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty_max, r_min)) => {
// We know that `T: 'a` for some type `T`. We can
// often elaborate this. For example, if we know that

View file

@ -468,6 +468,14 @@ pub trait Clause<I: Interner<Clause = Self>>:
.transpose()
}
fn as_host_effect_clause(self) -> Option<ty::Binder<I, ty::HostEffectPredicate<I>>> {
self.kind()
.map_bound(
|clause| if let ty::ClauseKind::HostEffect(t) = clause { Some(t) } else { None },
)
.transpose()
}
fn as_projection_clause(self) -> Option<ty::Binder<I, ty::ProjectionPredicate<I>>> {
self.kind()
.map_bound(

View file

@ -24,6 +24,7 @@ pub trait Interner:
+ IrPrint<ty::AliasTerm<Self>>
+ IrPrint<ty::TraitRef<Self>>
+ IrPrint<ty::TraitPredicate<Self>>
+ IrPrint<ty::HostEffectPredicate<Self>>
+ IrPrint<ty::ExistentialTraitRef<Self>>
+ IrPrint<ty::ExistentialProjection<Self>>
+ IrPrint<ty::ProjectionPredicate<Self>>
@ -228,6 +229,16 @@ pub trait Interner:
def_id: Self::DefId,
) -> ty::EarlyBinder<Self, impl IntoIterator<Item = (Self::Clause, Self::Span)>>;
fn is_const_impl(self, def_id: Self::DefId) -> bool;
fn const_conditions(
self,
def_id: Self::DefId,
) -> ty::EarlyBinder<Self, impl IntoIterator<Item = ty::Binder<Self, ty::TraitRef<Self>>>>;
fn implied_const_bounds(
self,
def_id: Self::DefId,
) -> ty::EarlyBinder<Self, impl IntoIterator<Item = ty::Binder<Self, ty::TraitRef<Self>>>>;
fn has_target_features(self, def_id: Self::DefId) -> bool;
fn require_lang_item(self, lang_item: TraitSolverLangItem) -> Self::DefId;

View file

@ -2,8 +2,8 @@ use std::fmt;
use crate::{
AliasTerm, AliasTy, Binder, CoercePredicate, ExistentialProjection, ExistentialTraitRef, FnSig,
Interner, NormalizesTo, OutlivesPredicate, ProjectionPredicate, SubtypePredicate,
TraitPredicate, TraitRef,
HostEffectPredicate, Interner, NormalizesTo, OutlivesPredicate, ProjectionPredicate,
SubtypePredicate, TraitPredicate, TraitRef,
};
pub trait IrPrint<T> {
@ -53,6 +53,7 @@ define_display_via_print!(
NormalizesTo,
SubtypePredicate,
CoercePredicate,
HostEffectPredicate,
AliasTy,
AliasTerm,
FnSig,

View file

@ -111,6 +111,13 @@ impl<I: Interner> ty::Binder<I, TraitRef<I>> {
pub fn def_id(&self) -> I::DefId {
self.skip_binder().def_id
}
pub fn to_host_effect_clause(self, cx: I, host: HostPolarity) -> I::Clause {
self.map_bound(|trait_ref| {
ty::ClauseKind::HostEffect(HostEffectPredicate { trait_ref, host })
})
.upcast(cx)
}
}
#[derive_where(Clone, Copy, Hash, PartialEq, Eq; I: Interner)]
@ -745,6 +752,64 @@ impl<I: Interner> fmt::Debug for NormalizesTo<I> {
}
}
#[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)]
#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)]
#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))]
pub struct HostEffectPredicate<I: Interner> {
pub trait_ref: ty::TraitRef<I>,
pub host: HostPolarity,
}
impl<I: Interner> HostEffectPredicate<I> {
pub fn self_ty(self) -> I::Ty {
self.trait_ref.self_ty()
}
pub fn with_self_ty(self, interner: I, self_ty: I::Ty) -> Self {
Self { trait_ref: self.trait_ref.with_self_ty(interner, self_ty), ..self }
}
pub fn def_id(self) -> I::DefId {
self.trait_ref.def_id
}
}
impl<I: Interner> ty::Binder<I, HostEffectPredicate<I>> {
pub fn def_id(self) -> I::DefId {
// Ok to skip binder since trait `DefId` does not care about regions.
self.skip_binder().def_id()
}
pub fn self_ty(self) -> ty::Binder<I, I::Ty> {
self.map_bound(|trait_ref| trait_ref.self_ty())
}
#[inline]
pub fn host(self) -> HostPolarity {
self.skip_binder().host
}
}
#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
#[derive(TypeVisitable_Generic, TypeFoldable_Generic)]
#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))]
pub enum HostPolarity {
/// May be called in const environments if the callee is const.
Maybe,
/// Always allowed to be called in const environments.
Const,
}
impl HostPolarity {
pub fn satisfies(self, goal: HostPolarity) -> bool {
match (self, goal) {
(HostPolarity::Const, HostPolarity::Const | HostPolarity::Maybe) => true,
(HostPolarity::Maybe, HostPolarity::Maybe) => true,
(HostPolarity::Maybe, HostPolarity::Const) => false,
}
}
}
/// Encodes that `a` must be a subtype of `b`. The `a_is_expected` flag indicates
/// whether the `a` type is the type that we should label as "expected" when
/// presenting user diagnostics.

View file

@ -37,6 +37,12 @@ pub enum ClauseKind<I: Interner> {
/// Constant initializer must evaluate successfully.
ConstEvaluatable(I::Const),
/// Enforces the constness of the predicate we're calling. Like a projection
/// goal from a where clause, it's always going to be paired with a
/// corresponding trait clause; this just enforces the *constness* of that
/// implementation.
HostEffect(ty::HostEffectPredicate<I>),
}
#[derive_where(Clone, Copy, Hash, PartialEq, Eq; I: Interner)]
@ -110,6 +116,7 @@ impl<I: Interner> fmt::Debug for ClauseKind<I> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ClauseKind::ConstArgHasType(ct, ty) => write!(f, "ConstArgHasType({ct:?}, {ty:?})"),
ClauseKind::HostEffect(data) => data.fmt(f),
ClauseKind::Trait(a) => a.fmt(f),
ClauseKind::RegionOutlives(pair) => pair.fmt(f),
ClauseKind::TypeOutlives(pair) => pair.fmt(f),