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

@ -193,18 +193,6 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
ty::PredicateKind::ConstEvaluatable(a), ty::PredicateKind::ConstEvaluatable(a),
ty::PredicateKind::ConstEvaluatable(b), ty::PredicateKind::ConstEvaluatable(b),
) => relator.relate(predicate.rebind(a), predicate.rebind(b)).is_ok(), ) => 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_a, lt_a)),
ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_b, lt_b)), ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_b, lt_b)),

View file

@ -1621,15 +1621,11 @@ impl<'tcx> InferCtxt<'tcx> {
// variables // variables
let tcx = self.tcx; let tcx = self.tcx;
if substs.has_non_region_infer() { if substs.has_non_region_infer() {
let substs_erased = tcx.erase_regions(unevaluated.substs); let ac = tcx.expand_unevaluated_abstract_const(unevaluated.def, unevaluated.substs);
let ac = tcx.expand_bound_abstract_const(
tcx.bound_abstract_const(unevaluated.def),
substs_erased,
);
match ac { match ac {
Ok(None) => { Ok(None) => {
substs = InternalSubsts::identity_for_item(tcx, unevaluated.def.did); 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)) => { Ok(Some(ct)) => {
if ct.has_non_region_infer() || ct.has_non_region_param() { 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. //! 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_errors::ErrorGuaranteed;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
@ -33,71 +36,79 @@ pub type BoundAbstractConst<'tcx> = Result<Option<EarlyBinder<ty::Const<'tcx>>>,
impl<'tcx> TyCtxt<'tcx> { impl<'tcx> TyCtxt<'tcx> {
/// Returns a const with substs applied by /// Returns a const with substs applied by
pub fn bound_abstract_const( fn bound_abstract_const(self, uv: ty::WithOptConstParam<DefId>) -> BoundAbstractConst<'tcx> {
self, let ac = if let Some((did, param_did)) = uv.as_const_arg() {
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() {
self.thir_abstract_const_of_const_arg((did, param_did)) self.thir_abstract_const_of_const_arg((did, param_did))
} else { } 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, self,
ct: BoundAbstractConst<'tcx>, ac: T,
substs: &[GenericArg<'tcx>], ) -> Result<Option<T>, ErrorGuaranteed> {
) -> Result<Option<Const<'tcx>>, 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> { struct Expander<'tcx> {
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
first: bool,
} }
impl<'tcx> FallibleTypeFolder<'tcx> for Expander<'tcx> { impl<'tcx> FallibleTypeFolder<'tcx> for Expander<'tcx> {
type Error = ErrorGuaranteed; type Error = Option<ErrorGuaranteed>;
fn tcx(&self) -> TyCtxt<'tcx> { fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx self.tcx
} }
fn try_fold_const(&mut self, c: Const<'tcx>) -> Result<Const<'tcx>, ErrorGuaranteed> { fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> {
use ty::ConstKind::*; if ty.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) {
let uv = match c.kind() { ty.try_super_fold_with(self)
Unevaluated(uv) => uv, } else {
Param(..) | Infer(..) | Bound(..) | Placeholder(..) | Value(..) | Error(..) => { Ok(ty)
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)?), fn try_fold_const(&mut self, c: Const<'tcx>) -> Result<Const<'tcx>, Self::Error> {
ty::Expr::Cast(k, c, t) => { let ct = match c.kind() {
ty::Expr::Cast(k, c.try_fold_with(self)?, t.try_fold_with(self)?) 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
} }
ty::Expr::FunctionCall(func, args) => ty::Expr::FunctionCall( }
func.try_fold_with(self)?, _ => c,
args.try_fold_with(self)?,
),
}; };
return Ok(self.tcx().mk_const(ty::ConstKind::Expr(new_expr), c.ty())); self.first = false;
} ct.try_super_fold_with(self)
};
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) }
} }
} }
match ac.try_fold_with(&mut Expander { tcx: self, first }) {
let Some(ac) = ct? else { Ok(c) => Ok(Some(c)),
return Ok(None); Err(None) => Ok(None),
}; Err(Some(e)) => Err(e),
let ac = ac.subst(self, substs); }
Ok(Some(ac.try_fold_with(&mut Expander { tcx: self })?))
} }
} }

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. // an unnormalized (i.e. unevaluated) const in the param-env.
// FIXME(generic_const_exprs): Once we always lazily unify unevaluated constants // FIXME(generic_const_exprs): Once we always lazily unify unevaluated constants
// these `eval` calls can be removed. // 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()); a = a.eval(tcx, relation.param_env());
b = b.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::Placeholder(p1), ty::ConstKind::Placeholder(p2)) => p1 == p2,
(ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => a_val == b_val, (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 tcx.features().generic_const_exprs =>
{ {
if let (Ok(Some(a)), Ok(Some(b))) = ( if let (Ok(Some(a)), Ok(Some(b))) = (
tcx.expand_bound_abstract_const(tcx.bound_abstract_const(au.def), au.substs), tcx.expand_abstract_consts(a),
tcx.expand_bound_abstract_const(tcx.bound_abstract_const(bu.def), bu.substs), tcx.expand_abstract_consts(b),
) && a.ty() == b.ty() { ) && a.ty() == b.ty() {
return relation.consts(a, b); return relation.consts(a, b);
} else { } else {

View file

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

View file

@ -29,8 +29,7 @@ pub fn is_const_evaluatable<'tcx>(
let tcx = infcx.tcx; let tcx = infcx.tcx;
let uv = match ct.kind() { let uv = match ct.kind() {
ty::ConstKind::Unevaluated(uv) => uv, ty::ConstKind::Unevaluated(uv) => uv,
// should be recursivee fixes. ty::ConstKind::Expr(_) => bug!("unexpected expr in `is_const_evaluatable: {ct:?}"),
ty::ConstKind::Expr(..) => todo!(),
ty::ConstKind::Param(_) ty::ConstKind::Param(_)
| ty::ConstKind::Bound(_, _) | ty::ConstKind::Bound(_, _)
| ty::ConstKind::Placeholder(_) | ty::ConstKind::Placeholder(_)
@ -40,10 +39,7 @@ pub fn is_const_evaluatable<'tcx>(
}; };
if tcx.features().generic_const_exprs { if tcx.features().generic_const_exprs {
let substs = tcx.erase_regions(uv.substs); if let Some(ct) = tcx.expand_abstract_consts(ct)? {
if let Some(ct) =
tcx.expand_bound_abstract_const(tcx.bound_abstract_const(uv.def), substs)?
{
if satisfied_from_param_env(tcx, infcx, ct, param_env)? { if satisfied_from_param_env(tcx, infcx, ct, param_env)? {
return Ok(()); return Ok(());
} }
@ -74,17 +70,13 @@ pub fn is_const_evaluatable<'tcx>(
// //
// See #74595 for more details about this. // See #74595 for more details about this.
let concrete = infcx.const_eval_resolve(param_env, uv, Some(span)); let concrete = infcx.const_eval_resolve(param_env, uv, Some(span));
let substs = tcx.erase_regions(uv.substs);
match concrete { match concrete {
// If we're evaluating a foreign constant, under a nightly compiler without generic // If we're evaluating a generic foreign constant, under a nightly compiler while
// const exprs, AND it would've passed if that expression had been evaluated with // the current crate does not enable `feature(generic_const_exprs)`, abort
// generic const exprs, then suggest using generic const exprs. // compilation with a useful error.
Err(_) if tcx.sess.is_nightly_build() Err(_) if tcx.sess.is_nightly_build()
&& let Ok(Some(ct)) = && let Ok(Some(ac)) = tcx.expand_abstract_consts(ct)
tcx.expand_bound_abstract_const(tcx.bound_abstract_const(uv.def), substs) && let ty::ConstKind::Expr(_) = ac.kind() => {
&& let ty::ConstKind::Expr(_expr) = ct.kind()
&& satisfied_from_param_env(tcx, infcx, ct, param_env) == Ok(true) => {
tcx.sess tcx.sess
.struct_span_fatal( .struct_span_fatal(
// Slightly better span than just using `span` alone // Slightly better span than just using `span` alone
@ -126,18 +118,6 @@ fn satisfied_from_param_env<'tcx>(
ct: ty::Const<'tcx>, ct: ty::Const<'tcx>,
param_env: ty::ParamEnv<'tcx>, param_env: ty::ParamEnv<'tcx>,
) -> Result<bool, NotConstEvaluatable> { ) -> Result<bool, NotConstEvaluatable> {
for pred in param_env.caller_bounds() {
match pred.kind().skip_binder() {
ty::PredicateKind::ConstEvaluatable(uv) => {
let ty::ConstKind::Unevaluated(uv) = uv.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);
};
// Try to unify with each subtree in the AbstractConst to allow for // Try to unify with each subtree in the AbstractConst to allow for
// `N + 1` being const evaluatable even if theres only a `ConstEvaluatable` // `N + 1` being const evaluatable even if theres only a `ConstEvaluatable`
// predicate for `(N + 1) * 2` // predicate for `(N + 1) * 2`
@ -156,7 +136,6 @@ fn satisfied_from_param_env<'tcx>(
.at(&ObligationCause::dummy(), self.param_env) .at(&ObligationCause::dummy(), self.param_env)
.eq(c, self.ct) .eq(c, self.ct)
{ {
//let obligations = nested_obligations.into_obligations();
ControlFlow::BREAK ControlFlow::BREAK
} else if let ty::ConstKind::Expr(e) = c.kind() { } else if let ty::ConstKind::Expr(e) = c.kind() {
e.visit_with(self) e.visit_with(self)
@ -166,6 +145,16 @@ fn satisfied_from_param_env<'tcx>(
} }
} }
for pred in param_env.caller_bounds() {
match pred.kind().skip_binder() {
ty::PredicateKind::ConstEvaluatable(ce) => {
let ty::ConstKind::Unevaluated(_) = ce.kind() else {
continue
};
let Some(b_ct) = tcx.expand_abstract_consts(ce)? else {
continue
};
let mut v = Visitor { ct, infcx, param_env }; let mut v = Visitor { ct, infcx, param_env };
let result = b_ct.visit_with(&mut v); 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))) = ( if let (Ok(Some(a)), Ok(Some(b))) = (
tcx.expand_bound_abstract_const( tcx.expand_abstract_consts(c1),
tcx.bound_abstract_const(a.def), tcx.expand_abstract_consts(c2),
a.substs,
),
tcx.expand_bound_abstract_const(
tcx.bound_abstract_const(b.def),
b.substs,
),
) && a.ty() == b.ty() && ) && a.ty() == b.ty() &&
let Ok(new_obligations) = infcx let Ok(new_obligations) = infcx
.at(&obligation.cause, obligation.param_env) .at(&obligation.cause, obligation.param_env)
@ -534,7 +528,9 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
.at(&obligation.cause, obligation.param_env) .at(&obligation.cause, obligation.param_env)
.eq(c1, c2) .eq(c1, c2)
{ {
Ok(_) => ProcessResult::Changed(vec![]), Ok(inf_ok) => {
ProcessResult::Changed(mk_pending(inf_ok.into_obligations()))
}
Err(err) => ProcessResult::Error( Err(err) => ProcessResult::Error(
FulfillmentErrorCode::CodeConstEquateError( FulfillmentErrorCode::CodeConstEquateError(
ExpectedFound::new(true, c1, c2), 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 // This shouldn't really matter though as we can't really use any
// constants which are not considered const evaluatable. // constants which are not considered const evaluatable.
if let ty::ConstKind::Unevaluated(uv) = ct.kind() && if let ty::ConstKind::Unevaluated(_uv) = ct.kind() &&
let Ok(Some(ct)) = self let Ok(Some(ct)) = self.tcx.expand_abstract_consts(ct){
.tcx
.expand_bound_abstract_const(self.tcx.bound_abstract_const(uv.def), uv.substs)
{
self.visit_const(ct) self.visit_const(ct)
} else { } else {
ct.super_visit_with(self) ct.super_visit_with(self)

View file

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

View file

@ -120,8 +120,6 @@ fn main() {
let v = vec![1, 2, 3]; let v = vec![1, 2, 3];
let bv = v.lazy_updim([3, 4]); let bv = v.lazy_updim([3, 4]);
let bbv = bv.bmap(|x| x * x); let bbv = bv.bmap(|x| x * x);
//~^ ERROR mismatched types
println!("The size of v is {:?}", bbv.bget([0, 2]).expect("Out of bounds.")); 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` = note: expected constant `Self::DIM`
found constant `DIM` found constant `DIM`
error[E0308]: mismatched types error: aborting due to 10 previous errors
--> $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
Some errors have detailed explanations: E0277, E0308. Some errors have detailed explanations: E0277, E0308.
For more information about an error, try `rustc --explain E0277`. For more information about an error, try `rustc --explain E0277`.