1
Fork 0

Add expand_abstract_const

Adds the ability to directly expand a const to an expr without having to deal with intermediate
steps.
This commit is contained in:
kadmin 2022-10-25 08:16:43 +00:00
parent f9750c1554
commit 5bb1a9febc
11 changed files with 124 additions and 174 deletions

View file

@ -29,8 +29,7 @@ pub fn is_const_evaluatable<'tcx>(
let tcx = infcx.tcx;
let uv = match ct.kind() {
ty::ConstKind::Unevaluated(uv) => uv,
// should be recursivee fixes.
ty::ConstKind::Expr(..) => todo!(),
ty::ConstKind::Expr(_) => bug!("unexpected expr in `is_const_evaluatable: {ct:?}"),
ty::ConstKind::Param(_)
| ty::ConstKind::Bound(_, _)
| ty::ConstKind::Placeholder(_)
@ -40,10 +39,7 @@ pub fn is_const_evaluatable<'tcx>(
};
if tcx.features().generic_const_exprs {
let substs = tcx.erase_regions(uv.substs);
if let Some(ct) =
tcx.expand_bound_abstract_const(tcx.bound_abstract_const(uv.def), substs)?
{
if let Some(ct) = tcx.expand_abstract_consts(ct)? {
if satisfied_from_param_env(tcx, infcx, ct, param_env)? {
return Ok(());
}
@ -74,17 +70,13 @@ pub fn is_const_evaluatable<'tcx>(
//
// See #74595 for more details about this.
let concrete = infcx.const_eval_resolve(param_env, uv, Some(span));
let substs = tcx.erase_regions(uv.substs);
match concrete {
// If we're evaluating a foreign constant, under a nightly compiler without generic
// const exprs, AND it would've passed if that expression had been evaluated with
// generic const exprs, then suggest using generic const exprs.
// If we're evaluating a generic foreign constant, under a nightly compiler while
// the current crate does not enable `feature(generic_const_exprs)`, abort
// compilation with a useful error.
Err(_) if tcx.sess.is_nightly_build()
&& let Ok(Some(ct)) =
tcx.expand_bound_abstract_const(tcx.bound_abstract_const(uv.def), substs)
&& let ty::ConstKind::Expr(_expr) = ct.kind()
&& satisfied_from_param_env(tcx, infcx, ct, param_env) == Ok(true) => {
&& let Ok(Some(ac)) = tcx.expand_abstract_consts(ct)
&& let ty::ConstKind::Expr(_) = ac.kind() => {
tcx.sess
.struct_span_fatal(
// Slightly better span than just using `span` alone
@ -126,46 +118,43 @@ fn satisfied_from_param_env<'tcx>(
ct: ty::Const<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> Result<bool, NotConstEvaluatable> {
// Try to unify with each subtree in the AbstractConst to allow for
// `N + 1` being const evaluatable even if theres only a `ConstEvaluatable`
// predicate for `(N + 1) * 2`
struct Visitor<'a, 'tcx> {
ct: ty::Const<'tcx>,
param_env: ty::ParamEnv<'tcx>,
infcx: &'a InferCtxt<'tcx>,
}
impl<'a, 'tcx> TypeVisitor<'tcx> for Visitor<'a, 'tcx> {
type BreakTy = ();
fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
if c.ty() == self.ct.ty()
&& let Ok(_nested_obligations) = self
.infcx
.at(&ObligationCause::dummy(), self.param_env)
.eq(c, self.ct)
{
ControlFlow::BREAK
} else if let ty::ConstKind::Expr(e) = c.kind() {
e.visit_with(self)
} else {
ControlFlow::CONTINUE
}
}
}
for pred in param_env.caller_bounds() {
match pred.kind().skip_binder() {
ty::PredicateKind::ConstEvaluatable(uv) => {
let ty::ConstKind::Unevaluated(uv) = uv.kind() else {
ty::PredicateKind::ConstEvaluatable(ce) => {
let ty::ConstKind::Unevaluated(_) = ce.kind() else {
continue
};
let substs = tcx.erase_regions(uv.substs);
let Some(b_ct) =
tcx.expand_bound_abstract_const(tcx.bound_abstract_const(uv.def), substs)? else {
return Ok(false);
let Some(b_ct) = tcx.expand_abstract_consts(ce)? else {
continue
};
// Try to unify with each subtree in the AbstractConst to allow for
// `N + 1` being const evaluatable even if theres only a `ConstEvaluatable`
// predicate for `(N + 1) * 2`
struct Visitor<'a, 'tcx> {
ct: ty::Const<'tcx>,
param_env: ty::ParamEnv<'tcx>,
infcx: &'a InferCtxt<'tcx>,
}
impl<'a, 'tcx> TypeVisitor<'tcx> for Visitor<'a, 'tcx> {
type BreakTy = ();
fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
if c.ty() == self.ct.ty()
&& let Ok(_nested_obligations) = self
.infcx
.at(&ObligationCause::dummy(), self.param_env)
.eq(c, self.ct)
{
//let obligations = nested_obligations.into_obligations();
ControlFlow::BREAK
} else if let ty::ConstKind::Expr(e) = c.kind() {
e.visit_with(self)
} else {
ControlFlow::CONTINUE
}
}
}
let mut v = Visitor { ct, infcx, param_env };
let result = b_ct.visit_with(&mut v);

View file

@ -478,14 +478,8 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
);
}
if let (Ok(Some(a)), Ok(Some(b))) = (
tcx.expand_bound_abstract_const(
tcx.bound_abstract_const(a.def),
a.substs,
),
tcx.expand_bound_abstract_const(
tcx.bound_abstract_const(b.def),
b.substs,
),
tcx.expand_abstract_consts(c1),
tcx.expand_abstract_consts(c2),
) && a.ty() == b.ty() &&
let Ok(new_obligations) = infcx
.at(&obligation.cause, obligation.param_env)
@ -534,7 +528,9 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
.at(&obligation.cause, obligation.param_env)
.eq(c1, c2)
{
Ok(_) => ProcessResult::Changed(vec![]),
Ok(inf_ok) => {
ProcessResult::Changed(mk_pending(inf_ok.into_obligations()))
}
Err(err) => ProcessResult::Error(
FulfillmentErrorCode::CodeConstEquateError(
ExpectedFound::new(true, c1, c2),

View file

@ -849,11 +849,8 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<'tcx>>(
//
// This shouldn't really matter though as we can't really use any
// constants which are not considered const evaluatable.
if let ty::ConstKind::Unevaluated(uv) = ct.kind() &&
let Ok(Some(ct)) = self
.tcx
.expand_bound_abstract_const(self.tcx.bound_abstract_const(uv.def), uv.substs)
{
if let ty::ConstKind::Unevaluated(_uv) = ct.kind() &&
let Ok(Some(ct)) = self.tcx.expand_abstract_consts(ct){
self.visit_const(ct)
} else {
ct.super_visit_with(self)

View file

@ -668,19 +668,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// if the constants depend on generic parameters.
//
// Let's just see where this breaks :shrug:
if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) =
if let (ty::ConstKind::Unevaluated(_), ty::ConstKind::Unevaluated(_)) =
(c1.kind(), c2.kind())
{
if let (Ok(Some(a)), Ok(Some(b))) = (
tcx.expand_bound_abstract_const(
tcx.bound_abstract_const(a.def),
a.substs,
),
tcx.expand_bound_abstract_const(
tcx.bound_abstract_const(b.def),
b.substs,
),
) && a.ty() == b.ty() && let Ok(new_obligations) =
tcx.expand_abstract_consts(c1),
tcx.expand_abstract_consts(c2),
) && a.ty() == b.ty() && let Ok(new_obligations) =
self.infcx.at(&obligation.cause, obligation.param_env).eq(a, b)
{
let mut obligations = new_obligations.obligations;
@ -718,7 +712,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
.at(&obligation.cause, obligation.param_env)
.eq(c1, c2)
{
Ok(_) => Ok(EvaluatedToOk),
Ok(inf_ok) => self.evaluate_predicates_recursively(
previous_stack,
inf_ok.into_obligations(),
),
Err(_) => Ok(EvaluatedToErr),
}
}