From 5bb1a9febce13dbe7ece9cdb3248b52c1ac44cc5 Mon Sep 17 00:00:00 2001 From: kadmin Date: Tue, 25 Oct 2022 08:16:43 +0000 Subject: [PATCH] Add expand_abstract_const Adds the ability to directly expand a const to an expr without having to deal with intermediate steps. --- .../rustc_hir_analysis/src/check/dropck.rs | 12 -- compiler/rustc_infer/src/infer/mod.rs | 8 +- .../rustc_middle/src/ty/abstract_const.rs | 113 ++++++++++-------- compiler/rustc_middle/src/ty/relate.rs | 8 +- compiler/rustc_privacy/src/lib.rs | 8 +- .../src/traits/const_evaluatable.rs | 87 ++++++-------- .../src/traits/fulfill.rs | 14 +-- .../src/traits/object_safety.rs | 7 +- .../src/traits/select/mod.rs | 19 ++- .../ui/const-generics/issues/issue-83765.rs | 2 - .../const-generics/issues/issue-83765.stderr | 20 +--- 11 files changed, 124 insertions(+), 174 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/dropck.rs b/compiler/rustc_hir_analysis/src/check/dropck.rs index 2754a9a05bc..e0b465bab16 100644 --- a/compiler/rustc_hir_analysis/src/check/dropck.rs +++ b/compiler/rustc_hir_analysis/src/check/dropck.rs @@ -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)), diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 06df1dc3a8e..8d01afa322e 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -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() { diff --git a/compiler/rustc_middle/src/ty/abstract_const.rs b/compiler/rustc_middle/src/ty/abstract_const.rs index 8a8c46d6f21..7c1029e6604 100644 --- a/compiler/rustc_middle/src/ty/abstract_const.rs +++ b/compiler/rustc_middle/src/ty/abstract_const.rs @@ -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>>, impl<'tcx> TyCtxt<'tcx> { /// Returns a const with substs applied by - pub fn bound_abstract_const( - self, - uv: ty::WithOptConstParam, - ) -> 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, - ) -> Result>, ErrorGuaranteed> { - if let Some((did, param_did)) = def.as_const_arg() { + fn bound_abstract_const(self, uv: ty::WithOptConstParam) -> 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>( self, - ct: BoundAbstractConst<'tcx>, - substs: &[GenericArg<'tcx>], - ) -> Result>, ErrorGuaranteed> { + ac: T, + ) -> Result, ErrorGuaranteed> { + self._expand_abstract_consts(ac, true) + } + + pub fn expand_unevaluated_abstract_const( + self, + did: ty::WithOptConstParam, + substs: SubstsRef<'tcx>, + ) -> Result>, 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>( + self, + ac: T, + first: bool, + ) -> Result, ErrorGuaranteed> { struct Expander<'tcx> { tcx: TyCtxt<'tcx>, + first: bool, } + impl<'tcx> FallibleTypeFolder<'tcx> for Expander<'tcx> { - type Error = ErrorGuaranteed; + type Error = Option; fn tcx(&self) -> TyCtxt<'tcx> { self.tcx } - fn try_fold_const(&mut self, c: Const<'tcx>) -> Result, 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, 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, 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), + } } } diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index 4249decc88f..84eeb81f1db 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -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 { diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 580a9eb091c..f8e99006923 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -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 } diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 284ec6b5e3a..bf7396d6113 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -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 { + // 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 { + 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 { - 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); diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 652bbeeeffb..9f4423605ab 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -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), diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index bc8abc0eb90..0ea126fa9c9 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -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) diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index da0a36731b9..98b227940ff 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -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), } } diff --git a/src/test/ui/const-generics/issues/issue-83765.rs b/src/test/ui/const-generics/issues/issue-83765.rs index 79ca330526f..674efa723cf 100644 --- a/src/test/ui/const-generics/issues/issue-83765.rs +++ b/src/test/ui/const-generics/issues/issue-83765.rs @@ -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 } diff --git a/src/test/ui/const-generics/issues/issue-83765.stderr b/src/test/ui/const-generics/issues/issue-83765.stderr index c0e4ae66a89..3cf1aab3a8b 100644 --- a/src/test/ui/const-generics/issues/issue-83765.stderr +++ b/src/test/ui/const-generics/issues/issue-83765.stderr @@ -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 `, { Self::DIM }, 2> as TensorDimension>::DIM`, found `, { Self::DIM }, 2> as TensorDimension>::DIM` - | - = note: expected constant `, { Self::DIM }, 2> as TensorDimension>::DIM` - found constant `, { 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 `, { Self::DIM }, 2> as TensorDimension>::DIM`, found `, { Self::DIM }, 2> as TensorDimension>::DIM` - | - = note: expected constant `, { Self::DIM }, 2> as TensorDimension>::DIM` - found constant `, { 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`.