summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_middle/src/ty/abstract_const.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_middle/src/ty/abstract_const.rs')
-rw-r--r--compiler/rustc_middle/src/ty/abstract_const.rs200
1 files changed, 45 insertions, 155 deletions
diff --git a/compiler/rustc_middle/src/ty/abstract_const.rs b/compiler/rustc_middle/src/ty/abstract_const.rs
index 1aa4df778..5de758ad9 100644
--- a/compiler/rustc_middle/src/ty/abstract_const.rs
+++ b/compiler/rustc_middle/src/ty/abstract_const.rs
@@ -1,98 +1,13 @@
//! A subset of a mir body used for const evaluatability checking.
-use crate::mir;
-use crate::ty::visit::TypeVisitable;
-use crate::ty::{self, DelaySpanBugEmitted, EarlyBinder, SubstsRef, Ty, TyCtxt};
+use crate::ty::{
+ self, Const, EarlyBinder, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
+ TypeVisitable,
+};
use rustc_errors::ErrorGuaranteed;
use rustc_hir::def_id::DefId;
-use std::cmp;
-use std::ops::ControlFlow;
-rustc_index::newtype_index! {
- /// An index into an `AbstractConst`.
- pub struct NodeId {
- derive [HashStable]
- DEBUG_FORMAT = "n{}",
- }
-}
-
-/// A tree representing an anonymous constant.
-///
-/// This is only able to represent a subset of `MIR`,
-/// and should not leak any information about desugarings.
-#[derive(Debug, Clone, Copy)]
-pub struct AbstractConst<'tcx> {
- // FIXME: Consider adding something like `IndexSlice`
- // and use this here.
- inner: &'tcx [Node<'tcx>],
- substs: SubstsRef<'tcx>,
-}
-
-impl<'tcx> AbstractConst<'tcx> {
- pub fn new(
- tcx: TyCtxt<'tcx>,
- uv: ty::UnevaluatedConst<'tcx>,
- ) -> Result<Option<AbstractConst<'tcx>>, ErrorGuaranteed> {
- let inner = tcx.thir_abstract_const_opt_const_arg(uv.def)?;
- debug!("AbstractConst::new({:?}) = {:?}", uv, inner);
- Ok(inner.map(|inner| AbstractConst { inner, substs: tcx.erase_regions(uv.substs) }))
- }
-
- pub fn from_const(
- tcx: TyCtxt<'tcx>,
- ct: ty::Const<'tcx>,
- ) -> Result<Option<AbstractConst<'tcx>>, ErrorGuaranteed> {
- match ct.kind() {
- ty::ConstKind::Unevaluated(uv) => AbstractConst::new(tcx, uv),
- ty::ConstKind::Error(DelaySpanBugEmitted { reported, .. }) => Err(reported),
- _ => Ok(None),
- }
- }
-
- #[inline]
- pub fn subtree(self, node: NodeId) -> AbstractConst<'tcx> {
- AbstractConst { inner: &self.inner[..=node.index()], substs: self.substs }
- }
-
- #[inline]
- pub fn root(self, tcx: TyCtxt<'tcx>) -> Node<'tcx> {
- let node = self.inner.last().copied().unwrap();
- match node {
- Node::Leaf(leaf) => Node::Leaf(EarlyBinder(leaf).subst(tcx, self.substs)),
- Node::Cast(kind, operand, ty) => {
- Node::Cast(kind, operand, EarlyBinder(ty).subst(tcx, self.substs))
- }
- // Don't perform substitution on the following as they can't directly contain generic params
- Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => node,
- }
- }
-
- pub fn unify_failure_kind(self, tcx: TyCtxt<'tcx>) -> FailureKind {
- let mut failure_kind = FailureKind::Concrete;
- walk_abstract_const::<!, _>(tcx, self, |node| {
- match node.root(tcx) {
- Node::Leaf(leaf) => {
- if leaf.has_non_region_infer() {
- failure_kind = FailureKind::MentionsInfer;
- } else if leaf.has_non_region_param() {
- failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam);
- }
- }
- Node::Cast(_, _, ty) => {
- if ty.has_non_region_infer() {
- failure_kind = FailureKind::MentionsInfer;
- } else if ty.has_non_region_param() {
- failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam);
- }
- }
- Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => {}
- }
- ControlFlow::CONTINUE
- });
- failure_kind
- }
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
+#[derive(Hash, Debug, Clone, Copy, Ord, PartialOrd, PartialEq, Eq)]
+#[derive(TyDecodable, TyEncodable, HashStable, TypeVisitable, TypeFoldable)]
pub enum CastKind {
/// thir::ExprKind::As
As,
@@ -100,16 +15,6 @@ pub enum CastKind {
Use,
}
-/// A node of an `AbstractConst`.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
-pub enum Node<'tcx> {
- Leaf(ty::Const<'tcx>),
- Binop(mir::BinOp, NodeId, NodeId),
- UnaryOp(mir::UnOp, NodeId),
- FunctionCall(NodeId, &'tcx [NodeId]),
- Cast(CastKind, NodeId, Ty<'tcx>),
-}
-
#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
pub enum NotConstEvaluatable {
Error(ErrorGuaranteed),
@@ -127,68 +32,53 @@ TrivialTypeTraversalAndLiftImpls! {
NotConstEvaluatable,
}
+pub type BoundAbstractConst<'tcx> = Result<Option<EarlyBinder<ty::Const<'tcx>>>, ErrorGuaranteed>;
+
impl<'tcx> TyCtxt<'tcx> {
- #[inline]
- pub fn thir_abstract_const_opt_const_arg(
+ /// Returns a const without substs applied
+ pub fn bound_abstract_const(
self,
- def: ty::WithOptConstParam<DefId>,
- ) -> Result<Option<&'tcx [Node<'tcx>]>, ErrorGuaranteed> {
- if let Some((did, param_did)) = def.as_const_arg() {
+ 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)))
}
-}
-#[instrument(skip(tcx, f), level = "debug")]
-pub fn walk_abstract_const<'tcx, R, F>(
- tcx: TyCtxt<'tcx>,
- ct: AbstractConst<'tcx>,
- mut f: F,
-) -> ControlFlow<R>
-where
- F: FnMut(AbstractConst<'tcx>) -> ControlFlow<R>,
-{
- #[instrument(skip(tcx, f), level = "debug")]
- fn recurse<'tcx, R>(
- tcx: TyCtxt<'tcx>,
- ct: AbstractConst<'tcx>,
- f: &mut dyn FnMut(AbstractConst<'tcx>) -> ControlFlow<R>,
- ) -> ControlFlow<R> {
- f(ct)?;
- let root = ct.root(tcx);
- debug!(?root);
- match root {
- Node::Leaf(_) => ControlFlow::CONTINUE,
- Node::Binop(_, l, r) => {
- recurse(tcx, ct.subtree(l), f)?;
- recurse(tcx, ct.subtree(r), f)
+ pub fn expand_abstract_consts<T: TypeFoldable<'tcx>>(self, ac: T) -> T {
+ struct Expander<'tcx> {
+ tcx: TyCtxt<'tcx>,
+ }
+
+ impl<'tcx> TypeFolder<'tcx> for Expander<'tcx> {
+ fn tcx(&self) -> TyCtxt<'tcx> {
+ self.tcx
}
- Node::UnaryOp(_, v) => recurse(tcx, ct.subtree(v), f),
- Node::FunctionCall(func, args) => {
- recurse(tcx, ct.subtree(func), f)?;
- args.iter().try_for_each(|&arg| recurse(tcx, ct.subtree(arg), f))
+ fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
+ if ty.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) {
+ ty.super_fold_with(self)
+ } else {
+ ty
+ }
+ }
+ fn fold_const(&mut self, c: Const<'tcx>) -> Const<'tcx> {
+ let ct = match c.kind() {
+ ty::ConstKind::Unevaluated(uv) => match self.tcx.bound_abstract_const(uv.def) {
+ Err(e) => self.tcx.const_error_with_guaranteed(c.ty(), e),
+ Ok(Some(bac)) => {
+ let substs = self.tcx.erase_regions(uv.substs);
+ bac.subst(self.tcx, substs)
+ }
+ Ok(None) => c,
+ },
+ _ => c,
+ };
+ ct.super_fold_with(self)
}
- Node::Cast(_, operand, _) => recurse(tcx, ct.subtree(operand), f),
}
+ ac.fold_with(&mut Expander { tcx: self })
}
-
- recurse(tcx, ct, &mut f)
-}
-
-// We were unable to unify the abstract constant with
-// a constant found in the caller bounds, there are
-// now three possible cases here.
-#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
-pub enum FailureKind {
- /// The abstract const still references an inference
- /// variable, in this case we return `TooGeneric`.
- MentionsInfer,
- /// The abstract const references a generic parameter,
- /// this means that we emit an error here.
- MentionsParam,
- /// The substs are concrete enough that we can simply
- /// try and evaluate the given constant.
- Concrete,
}