Skip to content

Commit

Permalink
Implement const effect predicate in new solver
Browse files Browse the repository at this point in the history
  • Loading branch information
compiler-errors committed Oct 20, 2024
1 parent 5bbcb5d commit efc40e1
Show file tree
Hide file tree
Showing 126 changed files with 1,679 additions and 917 deletions.
11 changes: 11 additions & 0 deletions compiler/rustc_hir_analysis/src/bounds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(tcx, host), span));
}

pub(crate) fn clauses(
&self,
// FIXME(effects): remove tcx
Expand Down
139 changes: 116 additions & 23 deletions compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,48 +187,61 @@ fn compare_method_predicate_entailment<'tcx>(
let impl_to_placeholder_args = GenericArgs::identity_for_item(tcx, impl_m.def_id);

// Create mapping from trait to placeholder.
let impl_def_id = impl_m.container_id(tcx);
let trait_to_placeholder_args =
impl_to_placeholder_args.rebase_onto(tcx, impl_m.container_id(tcx), trait_to_impl_args);
impl_to_placeholder_args.rebase_onto(tcx, impl_def_id, trait_to_impl_args);
debug!("compare_impl_method: trait_to_placeholder_args={:?}", trait_to_placeholder_args);

let impl_m_predicates = tcx.predicates_of(impl_m.def_id);
let trait_m_predicates = tcx.predicates_of(trait_m.def_id);

// Create obligations for each predicate declared by the impl
// definition in the context of the trait's parameter
// environment. We can't just use `impl_env.caller_bounds`,
// however, because we want to replace all late-bound regions with
// region variables.
let impl_predicates = tcx.predicates_of(impl_m_predicates.parent.unwrap());
let mut hybrid_preds = impl_predicates.instantiate_identity(tcx);

debug!("compare_impl_method: impl_bounds={:?}", hybrid_preds);

// This is the only tricky bit of the new way we check implementation methods
// We need to build a set of predicates where only the method-level bounds
// are from the trait and we assume all other bounds from the implementation
// to be previously satisfied.
//
// We then register the obligations from the impl_m and check to see
// if all constraints hold.
hybrid_preds.predicates.extend(
let impl_predicates = tcx.predicates_of(impl_m_predicates.parent.unwrap());
let mut hybrid_preds = impl_predicates.instantiate_identity(tcx).predicates;
hybrid_preds.extend(
trait_m_predicates
.instantiate_own(tcx, trait_to_placeholder_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.
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_placeholder_args),
)
.map(|(trait_ref, _)| trait_ref.to_host_effect(tcx, ty::HostPolarity::Maybe)),
);
}

// Construct trait parameter environment and then shift it into the placeholder viewpoint.
// The key step here is to update the caller_bounds's predicates to be
// the new hybrid bounds we computed.
let normalize_cause = traits::ObligationCause::misc(impl_m_span, impl_m_def_id);
let param_env = ty::ParamEnv::new(tcx.mk_clauses(&hybrid_preds.predicates), Reveal::UserFacing);
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);

let infcx = &tcx.infer_ctxt().build();
let ocx = ObligationCtxt::new_with_diagnostics(infcx);

debug!("compare_impl_method: caller_bounds={:?}", param_env.caller_bounds());

// Create obligations for each predicate declared by the impl
// definition in the context of the hybrid param-env. This makes
// sure that the impl's method's where clauses are not more
// restrictive than the trait's method (and the impl itself).
let impl_m_own_bounds = impl_m_predicates.instantiate_own(tcx, impl_to_placeholder_args);
for (predicate, span) in impl_m_own_bounds {
let normalize_cause = traits::ObligationCause::misc(span, impl_m_def_id);
Expand All @@ -243,6 +256,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(tcx, impl_to_placeholder_args)
{
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(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`.
Expand Down Expand Up @@ -1759,14 +1800,14 @@ fn compare_const_predicate_entailment<'tcx>(
// The predicates declared by the impl definition, the trait and the
// associated const in the trait are assumed.
let impl_predicates = tcx.predicates_of(impl_ct_predicates.parent.unwrap());
let mut hybrid_preds = impl_predicates.instantiate_identity(tcx);
hybrid_preds.predicates.extend(
let mut hybrid_preds = impl_predicates.instantiate_identity(tcx).predicates;
hybrid_preds.extend(
trait_ct_predicates
.instantiate_own(tcx, trait_to_impl_args)
.map(|(predicate, _)| predicate),
);

let param_env = ty::ParamEnv::new(tcx.mk_clauses(&hybrid_preds.predicates), Reveal::UserFacing);
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,
Expand Down Expand Up @@ -1871,14 +1912,16 @@ fn compare_type_predicate_entailment<'tcx>(
impl_trait_ref: ty::TraitRef<'tcx>,
) -> Result<(), ErrorGuaranteed> {
let impl_args = GenericArgs::identity_for_item(tcx, impl_ty.def_id);
let trait_to_impl_args =
impl_args.rebase_onto(tcx, impl_ty.container_id(tcx), impl_trait_ref.args);
let impl_def_id = impl_ty.container_id(tcx);
let trait_to_impl_args = impl_args.rebase_onto(tcx, impl_def_id, impl_trait_ref.args);

let impl_ty_predicates = tcx.predicates_of(impl_ty.def_id);
let trait_ty_predicates = tcx.predicates_of(trait_ty.def_id);

let impl_ty_own_bounds = impl_ty_predicates.instantiate_own(tcx, impl_args);
if impl_ty_own_bounds.len() == 0 {
let impl_ty_own_const_conditions =
tcx.const_conditions(impl_ty.def_id).instantiate_own(tcx, impl_args);
if impl_ty_own_bounds.len() == 0 && impl_ty_own_const_conditions.len() == 0 {
// Nothing to check.
return Ok(());
}
Expand All @@ -1892,18 +1935,31 @@ fn compare_type_predicate_entailment<'tcx>(
// The predicates declared by the impl definition, the trait and the
// associated type in the trait are assumed.
let impl_predicates = tcx.predicates_of(impl_ty_predicates.parent.unwrap());
let mut hybrid_preds = impl_predicates.instantiate_identity(tcx);
hybrid_preds.predicates.extend(
let mut hybrid_preds = impl_predicates.instantiate_identity(tcx).predicates;
hybrid_preds.extend(
trait_ty_predicates
.instantiate_own(tcx, trait_to_impl_args)
.map(|(predicate, _)| predicate),
);

let check_const_if_const = tcx.constness(impl_def_id) == hir::Constness::Const;
if check_const_if_const {
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(tcx, ty::HostPolarity::Maybe)),
);
}

debug!("compare_type_predicate_entailment: bounds={:?}", hybrid_preds);

let impl_ty_span = tcx.def_span(impl_ty_def_id);
let normalize_cause = ObligationCause::misc(impl_ty_span, impl_ty_def_id);
let param_env = ty::ParamEnv::new(tcx.mk_clauses(&hybrid_preds.predicates), Reveal::UserFacing);
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);
let infcx = tcx.infer_ctxt().build();
let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
Expand All @@ -1923,6 +1979,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 method.
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(tcx, ty::HostPolarity::Maybe),
));
}
}

// Check that all obligations are satisfied by the implementation's
// version.
let errors = ocx.select_all_or_error();
Expand Down Expand Up @@ -2005,14 +2082,30 @@ 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)| {
debug!("check_type_bounds: concrete_ty_bound = {:?}", concrete_ty_bound);
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(tcx, ty::HostPolarity::Maybe),
)
}),
);
}
debug!("check_type_bounds: item_bounds={:?}", obligations);

// Normalize predicates with the assumption that the GAT may always normalize
Expand Down
37 changes: 31 additions & 6 deletions compiler/rustc_hir_analysis/src/check/wfcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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(tcx, ty::HostPolarity::Maybe),
))
}
}

debug!(?obligations);
wfcx.register_obligations(obligations);
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_hir_analysis/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading

0 comments on commit efc40e1

Please sign in to comment.