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

@ -193,18 +193,6 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
ty::PredicateKind::ConstEvaluatable(a),
ty::PredicateKind::ConstEvaluatable(b),
) => relator.relate(predicate.rebind(a), predicate.rebind(b)).is_ok(),
/*
) => {
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() {
return relator.relate(a, b).is_ok();
} else {
false
}
}
*/
(
ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_a, lt_a)),
ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_b, lt_b)),

View File

@ -1621,15 +1621,11 @@ impl<'tcx> InferCtxt<'tcx> {
// variables
let tcx = self.tcx;
if substs.has_non_region_infer() {
let substs_erased = tcx.erase_regions(unevaluated.substs);
let ac = tcx.expand_bound_abstract_const(
tcx.bound_abstract_const(unevaluated.def),
substs_erased,
);
let ac = tcx.expand_unevaluated_abstract_const(unevaluated.def, unevaluated.substs);
match ac {
Ok(None) => {
substs = InternalSubsts::identity_for_item(tcx, unevaluated.def.did);
param_env = self.tcx.param_env(unevaluated.def.did);
param_env = tcx.param_env(unevaluated.def.did);
}
Ok(Some(ct)) => {
if ct.has_non_region_infer() || ct.has_non_region_param() {

View File

@ -1,5 +1,8 @@
//! A subset of a mir body used for const evaluatability checking.
use crate::ty::{self, Const, EarlyBinder, FallibleTypeFolder, GenericArg, TyCtxt, TypeFoldable};
use crate::ty::{
self, subst::SubstsRef, Const, EarlyBinder, FallibleTypeFolder, Ty, TyCtxt, TypeFoldable,
TypeSuperFoldable, TypeVisitable,
};
use rustc_errors::ErrorGuaranteed;
use rustc_hir::def_id::DefId;
@ -33,71 +36,79 @@ pub type BoundAbstractConst<'tcx> = Result<Option<EarlyBinder<ty::Const<'tcx>>>,
impl<'tcx> TyCtxt<'tcx> {
/// Returns a const with substs applied by
pub fn bound_abstract_const(
self,
uv: ty::WithOptConstParam<DefId>,
) -> BoundAbstractConst<'tcx> {
self.thir_abstract_const_opt_const_arg(uv).map(|ac| ac.map(|ac| EarlyBinder(ac)))
}
#[inline]
pub fn thir_abstract_const_opt_const_arg(
self,
def: ty::WithOptConstParam<DefId>,
) -> Result<Option<ty::Const<'tcx>>, ErrorGuaranteed> {
if let Some((did, param_did)) = def.as_const_arg() {
fn bound_abstract_const(self, uv: ty::WithOptConstParam<DefId>) -> BoundAbstractConst<'tcx> {
let ac = if let Some((did, param_did)) = uv.as_const_arg() {
self.thir_abstract_const_of_const_arg((did, param_did))
} else {
self.thir_abstract_const(def.did)
}
self.thir_abstract_const(uv.did)
};
Ok(ac?.map(|ac| EarlyBinder(ac)))
}
pub fn expand_bound_abstract_const(
pub fn expand_abstract_consts<T: TypeFoldable<'tcx>>(
self,
ct: BoundAbstractConst<'tcx>,
substs: &[GenericArg<'tcx>],
) -> Result<Option<Const<'tcx>>, ErrorGuaranteed> {
ac: T,
) -> Result<Option<T>, ErrorGuaranteed> {
self._expand_abstract_consts(ac, true)
}
pub fn expand_unevaluated_abstract_const(
self,
did: ty::WithOptConstParam<DefId>,
substs: SubstsRef<'tcx>,
) -> Result<Option<ty::Const<'tcx>>, ErrorGuaranteed> {
let Some(ac) = self.bound_abstract_const(did)? else {
return Ok(None);
};
let substs = self.erase_regions(substs);
let ac = ac.subst(self, substs);
self._expand_abstract_consts(ac, false)
}
fn _expand_abstract_consts<T: TypeFoldable<'tcx>>(
self,
ac: T,
first: bool,
) -> Result<Option<T>, ErrorGuaranteed> {
struct Expander<'tcx> {
tcx: TyCtxt<'tcx>,
first: bool,
}
impl<'tcx> FallibleTypeFolder<'tcx> for Expander<'tcx> {
type Error = ErrorGuaranteed;
type Error = Option<ErrorGuaranteed>;
fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx
}
fn try_fold_const(&mut self, c: Const<'tcx>) -> Result<Const<'tcx>, ErrorGuaranteed> {
use ty::ConstKind::*;
let uv = match c.kind() {
Unevaluated(uv) => uv,
Param(..) | Infer(..) | Bound(..) | Placeholder(..) | Value(..) | Error(..) => {
return Ok(c);
}
Expr(e) => {
let new_expr = match e {
ty::Expr::Binop(op, l, r) => {
ty::Expr::Binop(op, l.try_fold_with(self)?, r.try_fold_with(self)?)
}
ty::Expr::UnOp(op, v) => ty::Expr::UnOp(op, v.try_fold_with(self)?),
ty::Expr::Cast(k, c, t) => {
ty::Expr::Cast(k, c.try_fold_with(self)?, t.try_fold_with(self)?)
}
ty::Expr::FunctionCall(func, args) => ty::Expr::FunctionCall(
func.try_fold_with(self)?,
args.try_fold_with(self)?,
),
};
return Ok(self.tcx().mk_const(ty::ConstKind::Expr(new_expr), c.ty()));
fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> {
if ty.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) {
ty.try_super_fold_with(self)
} else {
Ok(ty)
}
}
fn try_fold_const(&mut self, c: Const<'tcx>) -> Result<Const<'tcx>, Self::Error> {
let ct = match c.kind() {
ty::ConstKind::Unevaluated(uv) => {
if let Some(bac) = self.tcx.bound_abstract_const(uv.def)? {
let substs = self.tcx.erase_regions(uv.substs);
bac.subst(self.tcx, substs)
} else if self.first {
return Err(None);
} else {
c
}
}
_ => c,
};
let bac = self.tcx.bound_abstract_const(uv.def);
let ac = self.tcx.expand_bound_abstract_const(bac, uv.substs);
if let Ok(Some(ac)) = ac { ac.try_fold_with(self) } else { Ok(c) }
self.first = false;
ct.try_super_fold_with(self)
}
}
let Some(ac) = ct? else {
return Ok(None);
};
let ac = ac.subst(self, substs);
Ok(Some(ac.try_fold_with(&mut Expander { tcx: self })?))
match ac.try_fold_with(&mut Expander { tcx: self, first }) {
Ok(c) => Ok(Some(c)),
Err(None) => Ok(None),
Err(Some(e)) => Err(e),
}
}
}

View File

@ -626,7 +626,7 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>(
// an unnormalized (i.e. unevaluated) const in the param-env.
// FIXME(generic_const_exprs): Once we always lazily unify unevaluated constants
// these `eval` calls can be removed.
if !relation.tcx().features().generic_const_exprs {
if !tcx.features().generic_const_exprs {
a = a.eval(tcx, relation.param_env());
b = b.eval(tcx, relation.param_env());
}
@ -647,12 +647,12 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>(
(ty::ConstKind::Placeholder(p1), ty::ConstKind::Placeholder(p2)) => p1 == p2,
(ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => a_val == b_val,
(ty::ConstKind::Unevaluated(au), ty::ConstKind::Unevaluated(bu))
(ty::ConstKind::Unevaluated(_au), ty::ConstKind::Unevaluated(_bu))
if tcx.features().generic_const_exprs =>
{
if let (Ok(Some(a)), Ok(Some(b))) = (
tcx.expand_bound_abstract_const(tcx.bound_abstract_const(au.def), au.substs),
tcx.expand_bound_abstract_const(tcx.bound_abstract_const(bu.def), bu.substs),
tcx.expand_abstract_consts(a),
tcx.expand_abstract_consts(b),
) && a.ty() == b.ty() {
return relation.consts(a, b);
} else {

View File

@ -287,12 +287,8 @@ where
self.visit_ty(c.ty())?;
let tcx = self.def_id_visitor.tcx();
if let ty::ConstKind::Unevaluated(uv) = c.kind() &&
let Ok(Some(ct)) = tcx.expand_bound_abstract_const(tcx.bound_abstract_const(uv.def),
uv.substs) {
ct.visit_with(self)?;
}
if let ty::ConstKind::Expr(e) = c.kind() {
e.visit_with(self)?;
let Ok(Some(ct)) = tcx.expand_unevaluated_abstract_const(uv.def, uv.substs) {
ct.super_visit_with(self)?;
}
ControlFlow::CONTINUE
}

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

View File

@ -120,8 +120,6 @@ fn main() {
let v = vec![1, 2, 3];
let bv = v.lazy_updim([3, 4]);
let bbv = bv.bmap(|x| x * x);
//~^ ERROR mismatched types
println!("The size of v is {:?}", bbv.bget([0, 2]).expect("Out of bounds."));
//~^ ERROR mismatched types
}

View File

@ -98,25 +98,7 @@ LL | self.reference.bget(index).map(&self.closure)
= note: expected constant `Self::DIM`
found constant `DIM`
error[E0308]: mismatched types
--> $DIR/issue-83765.rs:122:15
|
LL | let bbv = bv.bmap(|x| x * x);
| ^^^^^^^^^^^^^^^^^^ expected `<LazyUpdim<'_, Vec<{integer}>, { Self::DIM }, 2> as TensorDimension>::DIM`, found `<LazyUpdim<'_, Vec<{integer}>, { Self::DIM }, 2> as TensorDimension>::DIM`
|
= note: expected constant `<LazyUpdim<'_, Vec<{integer}>, { Self::DIM }, 2> as TensorDimension>::DIM`
found constant `<LazyUpdim<'_, Vec<{integer}>, { Self::DIM }, 2> as TensorDimension>::DIM`
error[E0308]: mismatched types
--> $DIR/issue-83765.rs:125:43
|
LL | println!("The size of v is {:?}", bbv.bget([0, 2]).expect("Out of bounds."));
| ^^^^ expected `<LazyUpdim<'_, Vec<{integer}>, { Self::DIM }, 2> as TensorDimension>::DIM`, found `<LazyUpdim<'_, Vec<{integer}>, { Self::DIM }, 2> as TensorDimension>::DIM`
|
= note: expected constant `<LazyUpdim<'_, Vec<{integer}>, { Self::DIM }, 2> as TensorDimension>::DIM`
found constant `<LazyUpdim<'_, Vec<{integer}>, { Self::DIM }, 2> as TensorDimension>::DIM`
error: aborting due to 12 previous errors
error: aborting due to 10 previous errors
Some errors have detailed explanations: E0277, E0308.
For more information about an error, try `rustc --explain E0277`.