Skip to content

Commit 5bb1a9f

Browse files
committed
Add expand_abstract_const
Adds the ability to directly expand a const to an expr without having to deal with intermediate steps.
1 parent f9750c1 commit 5bb1a9f

File tree

11 files changed

+122
-172
lines changed

11 files changed

+122
-172
lines changed

compiler/rustc_hir_analysis/src/check/dropck.rs

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -193,18 +193,6 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
193193
ty::PredicateKind::ConstEvaluatable(a),
194194
ty::PredicateKind::ConstEvaluatable(b),
195195
) => relator.relate(predicate.rebind(a), predicate.rebind(b)).is_ok(),
196-
/*
197-
) => {
198-
if let (Ok(Some(a)), Ok(Some(b))) = (
199-
tcx.expand_bound_abstract_const(tcx.bound_abstract_const(a.def), a.substs),
200-
tcx.expand_bound_abstract_const(tcx.bound_abstract_const(b.def), b.substs),
201-
) && a.ty() == b.ty() {
202-
return relator.relate(a, b).is_ok();
203-
} else {
204-
false
205-
}
206-
}
207-
*/
208196
(
209197
ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_a, lt_a)),
210198
ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_b, lt_b)),

compiler/rustc_infer/src/infer/mod.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1621,15 +1621,11 @@ impl<'tcx> InferCtxt<'tcx> {
16211621
// variables
16221622
let tcx = self.tcx;
16231623
if substs.has_non_region_infer() {
1624-
let substs_erased = tcx.erase_regions(unevaluated.substs);
1625-
let ac = tcx.expand_bound_abstract_const(
1626-
tcx.bound_abstract_const(unevaluated.def),
1627-
substs_erased,
1628-
);
1624+
let ac = tcx.expand_unevaluated_abstract_const(unevaluated.def, unevaluated.substs);
16291625
match ac {
16301626
Ok(None) => {
16311627
substs = InternalSubsts::identity_for_item(tcx, unevaluated.def.did);
1632-
param_env = self.tcx.param_env(unevaluated.def.did);
1628+
param_env = tcx.param_env(unevaluated.def.did);
16331629
}
16341630
Ok(Some(ct)) => {
16351631
if ct.has_non_region_infer() || ct.has_non_region_param() {
Lines changed: 60 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
//! A subset of a mir body used for const evaluatability checking.
2-
use crate::ty::{self, Const, EarlyBinder, FallibleTypeFolder, GenericArg, TyCtxt, TypeFoldable};
2+
use crate::ty::{
3+
self, subst::SubstsRef, Const, EarlyBinder, FallibleTypeFolder, Ty, TyCtxt, TypeFoldable,
4+
TypeSuperFoldable, TypeVisitable,
5+
};
36
use rustc_errors::ErrorGuaranteed;
47
use rustc_hir::def_id::DefId;
58

@@ -33,71 +36,79 @@ pub type BoundAbstractConst<'tcx> = Result<Option<EarlyBinder<ty::Const<'tcx>>>,
3336

3437
impl<'tcx> TyCtxt<'tcx> {
3538
/// Returns a const with substs applied by
36-
pub fn bound_abstract_const(
39+
fn bound_abstract_const(self, uv: ty::WithOptConstParam<DefId>) -> BoundAbstractConst<'tcx> {
40+
let ac = if let Some((did, param_did)) = uv.as_const_arg() {
41+
self.thir_abstract_const_of_const_arg((did, param_did))
42+
} else {
43+
self.thir_abstract_const(uv.did)
44+
};
45+
Ok(ac?.map(|ac| EarlyBinder(ac)))
46+
}
47+
48+
pub fn expand_abstract_consts<T: TypeFoldable<'tcx>>(
3749
self,
38-
uv: ty::WithOptConstParam<DefId>,
39-
) -> BoundAbstractConst<'tcx> {
40-
self.thir_abstract_const_opt_const_arg(uv).map(|ac| ac.map(|ac| EarlyBinder(ac)))
50+
ac: T,
51+
) -> Result<Option<T>, ErrorGuaranteed> {
52+
self._expand_abstract_consts(ac, true)
4153
}
42-
#[inline]
43-
pub fn thir_abstract_const_opt_const_arg(
54+
55+
pub fn expand_unevaluated_abstract_const(
4456
self,
45-
def: ty::WithOptConstParam<DefId>,
57+
did: ty::WithOptConstParam<DefId>,
58+
substs: SubstsRef<'tcx>,
4659
) -> Result<Option<ty::Const<'tcx>>, ErrorGuaranteed> {
47-
if let Some((did, param_did)) = def.as_const_arg() {
48-
self.thir_abstract_const_of_const_arg((did, param_did))
49-
} else {
50-
self.thir_abstract_const(def.did)
51-
}
60+
let Some(ac) = self.bound_abstract_const(did)? else {
61+
return Ok(None);
62+
};
63+
let substs = self.erase_regions(substs);
64+
let ac = ac.subst(self, substs);
65+
self._expand_abstract_consts(ac, false)
5266
}
5367

54-
pub fn expand_bound_abstract_const(
68+
fn _expand_abstract_consts<T: TypeFoldable<'tcx>>(
5569
self,
56-
ct: BoundAbstractConst<'tcx>,
57-
substs: &[GenericArg<'tcx>],
58-
) -> Result<Option<Const<'tcx>>, ErrorGuaranteed> {
70+
ac: T,
71+
first: bool,
72+
) -> Result<Option<T>, ErrorGuaranteed> {
5973
struct Expander<'tcx> {
6074
tcx: TyCtxt<'tcx>,
75+
first: bool,
6176
}
77+
6278
impl<'tcx> FallibleTypeFolder<'tcx> for Expander<'tcx> {
63-
type Error = ErrorGuaranteed;
79+
type Error = Option<ErrorGuaranteed>;
6480
fn tcx(&self) -> TyCtxt<'tcx> {
6581
self.tcx
6682
}
67-
fn try_fold_const(&mut self, c: Const<'tcx>) -> Result<Const<'tcx>, ErrorGuaranteed> {
68-
use ty::ConstKind::*;
69-
let uv = match c.kind() {
70-
Unevaluated(uv) => uv,
71-
Param(..) | Infer(..) | Bound(..) | Placeholder(..) | Value(..) | Error(..) => {
72-
return Ok(c);
73-
}
74-
Expr(e) => {
75-
let new_expr = match e {
76-
ty::Expr::Binop(op, l, r) => {
77-
ty::Expr::Binop(op, l.try_fold_with(self)?, r.try_fold_with(self)?)
78-
}
79-
ty::Expr::UnOp(op, v) => ty::Expr::UnOp(op, v.try_fold_with(self)?),
80-
ty::Expr::Cast(k, c, t) => {
81-
ty::Expr::Cast(k, c.try_fold_with(self)?, t.try_fold_with(self)?)
82-
}
83-
ty::Expr::FunctionCall(func, args) => ty::Expr::FunctionCall(
84-
func.try_fold_with(self)?,
85-
args.try_fold_with(self)?,
86-
),
87-
};
88-
return Ok(self.tcx().mk_const(ty::ConstKind::Expr(new_expr), c.ty()));
83+
fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> {
84+
if ty.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) {
85+
ty.try_super_fold_with(self)
86+
} else {
87+
Ok(ty)
88+
}
89+
}
90+
fn try_fold_const(&mut self, c: Const<'tcx>) -> Result<Const<'tcx>, Self::Error> {
91+
let ct = match c.kind() {
92+
ty::ConstKind::Unevaluated(uv) => {
93+
if let Some(bac) = self.tcx.bound_abstract_const(uv.def)? {
94+
let substs = self.tcx.erase_regions(uv.substs);
95+
bac.subst(self.tcx, substs)
96+
} else if self.first {
97+
return Err(None);
98+
} else {
99+
c
100+
}
89101
}
102+
_ => c,
90103
};
91-
let bac = self.tcx.bound_abstract_const(uv.def);
92-
let ac = self.tcx.expand_bound_abstract_const(bac, uv.substs);
93-
if let Ok(Some(ac)) = ac { ac.try_fold_with(self) } else { Ok(c) }
104+
self.first = false;
105+
ct.try_super_fold_with(self)
94106
}
95107
}
96-
97-
let Some(ac) = ct? else {
98-
return Ok(None);
99-
};
100-
let ac = ac.subst(self, substs);
101-
Ok(Some(ac.try_fold_with(&mut Expander { tcx: self })?))
108+
match ac.try_fold_with(&mut Expander { tcx: self, first }) {
109+
Ok(c) => Ok(Some(c)),
110+
Err(None) => Ok(None),
111+
Err(Some(e)) => Err(e),
112+
}
102113
}
103114
}

compiler/rustc_middle/src/ty/relate.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -626,7 +626,7 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>(
626626
// an unnormalized (i.e. unevaluated) const in the param-env.
627627
// FIXME(generic_const_exprs): Once we always lazily unify unevaluated constants
628628
// these `eval` calls can be removed.
629-
if !relation.tcx().features().generic_const_exprs {
629+
if !tcx.features().generic_const_exprs {
630630
a = a.eval(tcx, relation.param_env());
631631
b = b.eval(tcx, relation.param_env());
632632
}
@@ -647,12 +647,12 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>(
647647
(ty::ConstKind::Placeholder(p1), ty::ConstKind::Placeholder(p2)) => p1 == p2,
648648
(ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => a_val == b_val,
649649

650-
(ty::ConstKind::Unevaluated(au), ty::ConstKind::Unevaluated(bu))
650+
(ty::ConstKind::Unevaluated(_au), ty::ConstKind::Unevaluated(_bu))
651651
if tcx.features().generic_const_exprs =>
652652
{
653653
if let (Ok(Some(a)), Ok(Some(b))) = (
654-
tcx.expand_bound_abstract_const(tcx.bound_abstract_const(au.def), au.substs),
655-
tcx.expand_bound_abstract_const(tcx.bound_abstract_const(bu.def), bu.substs),
654+
tcx.expand_abstract_consts(a),
655+
tcx.expand_abstract_consts(b),
656656
) && a.ty() == b.ty() {
657657
return relation.consts(a, b);
658658
} else {

compiler/rustc_privacy/src/lib.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -287,12 +287,8 @@ where
287287
self.visit_ty(c.ty())?;
288288
let tcx = self.def_id_visitor.tcx();
289289
if let ty::ConstKind::Unevaluated(uv) = c.kind() &&
290-
let Ok(Some(ct)) = tcx.expand_bound_abstract_const(tcx.bound_abstract_const(uv.def),
291-
uv.substs) {
292-
ct.visit_with(self)?;
293-
}
294-
if let ty::ConstKind::Expr(e) = c.kind() {
295-
e.visit_with(self)?;
290+
let Ok(Some(ct)) = tcx.expand_unevaluated_abstract_const(uv.def, uv.substs) {
291+
ct.super_visit_with(self)?;
296292
}
297293
ControlFlow::CONTINUE
298294
}

compiler/rustc_trait_selection/src/traits/const_evaluatable.rs

Lines changed: 38 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,7 @@ pub fn is_const_evaluatable<'tcx>(
2929
let tcx = infcx.tcx;
3030
let uv = match ct.kind() {
3131
ty::ConstKind::Unevaluated(uv) => uv,
32-
// should be recursivee fixes.
33-
ty::ConstKind::Expr(..) => todo!(),
32+
ty::ConstKind::Expr(_) => bug!("unexpected expr in `is_const_evaluatable: {ct:?}"),
3433
ty::ConstKind::Param(_)
3534
| ty::ConstKind::Bound(_, _)
3635
| ty::ConstKind::Placeholder(_)
@@ -40,10 +39,7 @@ pub fn is_const_evaluatable<'tcx>(
4039
};
4140

4241
if tcx.features().generic_const_exprs {
43-
let substs = tcx.erase_regions(uv.substs);
44-
if let Some(ct) =
45-
tcx.expand_bound_abstract_const(tcx.bound_abstract_const(uv.def), substs)?
46-
{
42+
if let Some(ct) = tcx.expand_abstract_consts(ct)? {
4743
if satisfied_from_param_env(tcx, infcx, ct, param_env)? {
4844
return Ok(());
4945
}
@@ -74,17 +70,13 @@ pub fn is_const_evaluatable<'tcx>(
7470
//
7571
// See #74595 for more details about this.
7672
let concrete = infcx.const_eval_resolve(param_env, uv, Some(span));
77-
78-
let substs = tcx.erase_regions(uv.substs);
7973
match concrete {
80-
// If we're evaluating a foreign constant, under a nightly compiler without generic
81-
// const exprs, AND it would've passed if that expression had been evaluated with
82-
// generic const exprs, then suggest using generic const exprs.
74+
// If we're evaluating a generic foreign constant, under a nightly compiler while
75+
// the current crate does not enable `feature(generic_const_exprs)`, abort
76+
// compilation with a useful error.
8377
Err(_) if tcx.sess.is_nightly_build()
84-
&& let Ok(Some(ct)) =
85-
tcx.expand_bound_abstract_const(tcx.bound_abstract_const(uv.def), substs)
86-
&& let ty::ConstKind::Expr(_expr) = ct.kind()
87-
&& satisfied_from_param_env(tcx, infcx, ct, param_env) == Ok(true) => {
78+
&& let Ok(Some(ac)) = tcx.expand_abstract_consts(ct)
79+
&& let ty::ConstKind::Expr(_) = ac.kind() => {
8880
tcx.sess
8981
.struct_span_fatal(
9082
// Slightly better span than just using `span` alone
@@ -126,46 +118,43 @@ fn satisfied_from_param_env<'tcx>(
126118
ct: ty::Const<'tcx>,
127119
param_env: ty::ParamEnv<'tcx>,
128120
) -> Result<bool, NotConstEvaluatable> {
121+
// Try to unify with each subtree in the AbstractConst to allow for
122+
// `N + 1` being const evaluatable even if theres only a `ConstEvaluatable`
123+
// predicate for `(N + 1) * 2`
124+
struct Visitor<'a, 'tcx> {
125+
ct: ty::Const<'tcx>,
126+
param_env: ty::ParamEnv<'tcx>,
127+
128+
infcx: &'a InferCtxt<'tcx>,
129+
}
130+
impl<'a, 'tcx> TypeVisitor<'tcx> for Visitor<'a, 'tcx> {
131+
type BreakTy = ();
132+
fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
133+
if c.ty() == self.ct.ty()
134+
&& let Ok(_nested_obligations) = self
135+
.infcx
136+
.at(&ObligationCause::dummy(), self.param_env)
137+
.eq(c, self.ct)
138+
{
139+
ControlFlow::BREAK
140+
} else if let ty::ConstKind::Expr(e) = c.kind() {
141+
e.visit_with(self)
142+
} else {
143+
ControlFlow::CONTINUE
144+
}
145+
}
146+
}
147+
129148
for pred in param_env.caller_bounds() {
130149
match pred.kind().skip_binder() {
131-
ty::PredicateKind::ConstEvaluatable(uv) => {
132-
let ty::ConstKind::Unevaluated(uv) = uv.kind() else {
150+
ty::PredicateKind::ConstEvaluatable(ce) => {
151+
let ty::ConstKind::Unevaluated(_) = ce.kind() else {
133152
continue
134153
};
135-
let substs = tcx.erase_regions(uv.substs);
136-
let Some(b_ct) =
137-
tcx.expand_bound_abstract_const(tcx.bound_abstract_const(uv.def), substs)? else {
138-
return Ok(false);
154+
let Some(b_ct) = tcx.expand_abstract_consts(ce)? else {
155+
continue
139156
};
140157

141-
// Try to unify with each subtree in the AbstractConst to allow for
142-
// `N + 1` being const evaluatable even if theres only a `ConstEvaluatable`
143-
// predicate for `(N + 1) * 2`
144-
struct Visitor<'a, 'tcx> {
145-
ct: ty::Const<'tcx>,
146-
param_env: ty::ParamEnv<'tcx>,
147-
148-
infcx: &'a InferCtxt<'tcx>,
149-
}
150-
impl<'a, 'tcx> TypeVisitor<'tcx> for Visitor<'a, 'tcx> {
151-
type BreakTy = ();
152-
fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
153-
if c.ty() == self.ct.ty()
154-
&& let Ok(_nested_obligations) = self
155-
.infcx
156-
.at(&ObligationCause::dummy(), self.param_env)
157-
.eq(c, self.ct)
158-
{
159-
//let obligations = nested_obligations.into_obligations();
160-
ControlFlow::BREAK
161-
} else if let ty::ConstKind::Expr(e) = c.kind() {
162-
e.visit_with(self)
163-
} else {
164-
ControlFlow::CONTINUE
165-
}
166-
}
167-
}
168-
169158
let mut v = Visitor { ct, infcx, param_env };
170159
let result = b_ct.visit_with(&mut v);
171160

compiler/rustc_trait_selection/src/traits/fulfill.rs

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -478,14 +478,8 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
478478
);
479479
}
480480
if let (Ok(Some(a)), Ok(Some(b))) = (
481-
tcx.expand_bound_abstract_const(
482-
tcx.bound_abstract_const(a.def),
483-
a.substs,
484-
),
485-
tcx.expand_bound_abstract_const(
486-
tcx.bound_abstract_const(b.def),
487-
b.substs,
488-
),
481+
tcx.expand_abstract_consts(c1),
482+
tcx.expand_abstract_consts(c2),
489483
) && a.ty() == b.ty() &&
490484
let Ok(new_obligations) = infcx
491485
.at(&obligation.cause, obligation.param_env)
@@ -534,7 +528,9 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
534528
.at(&obligation.cause, obligation.param_env)
535529
.eq(c1, c2)
536530
{
537-
Ok(_) => ProcessResult::Changed(vec![]),
531+
Ok(inf_ok) => {
532+
ProcessResult::Changed(mk_pending(inf_ok.into_obligations()))
533+
}
538534
Err(err) => ProcessResult::Error(
539535
FulfillmentErrorCode::CodeConstEquateError(
540536
ExpectedFound::new(true, c1, c2),

compiler/rustc_trait_selection/src/traits/object_safety.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -849,11 +849,8 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<'tcx>>(
849849
//
850850
// This shouldn't really matter though as we can't really use any
851851
// constants which are not considered const evaluatable.
852-
if let ty::ConstKind::Unevaluated(uv) = ct.kind() &&
853-
let Ok(Some(ct)) = self
854-
.tcx
855-
.expand_bound_abstract_const(self.tcx.bound_abstract_const(uv.def), uv.substs)
856-
{
852+
if let ty::ConstKind::Unevaluated(_uv) = ct.kind() &&
853+
let Ok(Some(ct)) = self.tcx.expand_abstract_consts(ct){
857854
self.visit_const(ct)
858855
} else {
859856
ct.super_visit_with(self)

0 commit comments

Comments
 (0)