diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 3eb2b3a0b1b63..221e359d24ab8 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -561,6 +561,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ty::ConstKind::Param(_) | ty::ConstKind::Placeholder(..) => { throw_inval!(TooGeneric) } + // FIXME(generic_const_exprs): `ConstKind::Expr` should be able to be evaluated + ty::ConstKind::Expr(_) => throw_inval!(TooGeneric), ty::ConstKind::Error(reported) => { throw_inval!(AlreadyReported(reported)) } diff --git a/compiler/rustc_infer/src/infer/freshen.rs b/compiler/rustc_infer/src/infer/freshen.rs index ff5d1a05a7062..27a94ec5e30e1 100644 --- a/compiler/rustc_infer/src/infer/freshen.rs +++ b/compiler/rustc_infer/src/infer/freshen.rs @@ -248,6 +248,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> { ty::ConstKind::Param(_) | ty::ConstKind::Value(_) | ty::ConstKind::Unevaluated(..) + | ty::ConstKind::Expr(..) | ty::ConstKind::Error(_) => ct.super_fold_with(self), } } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 2798477d1815d..67feb83faace6 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -23,7 +23,6 @@ use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKin use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult}; use rustc_middle::mir::ConstraintCategory; use rustc_middle::traits::select; -use rustc_middle::ty::abstract_const::{AbstractConst, FailureKind}; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::fold::BoundVarReplacerDelegate; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; @@ -713,32 +712,6 @@ impl<'tcx> InferCtxt<'tcx> { TypeErrCtxt { infcx: self, typeck_results: None, fallback_has_occurred: false } } - /// calls `tcx.try_unify_abstract_consts` after - /// canonicalizing the consts. - #[instrument(skip(self), level = "debug")] - pub fn try_unify_abstract_consts( - &self, - a: ty::UnevaluatedConst<'tcx>, - b: ty::UnevaluatedConst<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ) -> bool { - // Reject any attempt to unify two unevaluated constants that contain inference - // variables, since inference variables in queries lead to ICEs. - if a.substs.has_non_region_infer() - || b.substs.has_non_region_infer() - || param_env.has_non_region_infer() - { - debug!("a or b or param_env contain infer vars in its substs -> cannot unify"); - return false; - } - - let param_env_and = param_env.and((a, b)); - let erased = self.tcx.erase_regions(param_env_and); - debug!("after erase_regions: {:?}", erased); - - self.tcx.try_unify_abstract_consts(erased) - } - pub fn is_in_snapshot(&self) -> bool { self.in_snapshot.get() } @@ -1646,26 +1619,25 @@ impl<'tcx> InferCtxt<'tcx> { // Postpone the evaluation of constants whose substs depend on inference // variables + let tcx = self.tcx; if substs.has_non_region_infer() { - let ac = AbstractConst::new(self.tcx, unevaluated); - match ac { - Ok(None) => { - substs = InternalSubsts::identity_for_item(self.tcx, unevaluated.def.did); - param_env = self.tcx.param_env(unevaluated.def.did); - } - Ok(Some(ct)) => { - if ct.unify_failure_kind(self.tcx) == FailureKind::Concrete { - substs = replace_param_and_infer_substs_with_placeholder(self.tcx, substs); - } else { - return Err(ErrorHandled::TooGeneric); - } + if let Some(ct) = tcx.bound_abstract_const(unevaluated.def)? { + let ct = tcx.expand_abstract_consts(ct.subst(tcx, substs)); + if let Err(e) = ct.error_reported() { + return Err(ErrorHandled::Reported(e)); + } else if ct.has_non_region_infer() || ct.has_non_region_param() { + return Err(ErrorHandled::TooGeneric); + } else { + substs = replace_param_and_infer_substs_with_placeholder(tcx, substs); } - Err(guar) => return Err(ErrorHandled::Reported(guar)), + } else { + substs = InternalSubsts::identity_for_item(tcx, unevaluated.def.did); + param_env = tcx.param_env(unevaluated.def.did); } } - let param_env_erased = self.tcx.erase_regions(param_env); - let substs_erased = self.tcx.erase_regions(substs); + let param_env_erased = tcx.erase_regions(param_env); + let substs_erased = tcx.erase_regions(substs); debug!(?param_env_erased); debug!(?substs_erased); @@ -1673,7 +1645,7 @@ impl<'tcx> InferCtxt<'tcx> { // The return value is the evaluated value which doesn't contain any reference to inference // variables, thus we don't need to substitute back the original values. - self.tcx.const_eval_resolve_for_typeck(param_env_erased, unevaluated, span) + tcx.const_eval_resolve_for_typeck(param_env_erased, unevaluated, span) } /// `ty_or_const_infer_var_changed` is equivalent to one of these two: diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index ac4b512619025..3fae6694add0a 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -644,12 +644,6 @@ impl<'a, 'tcx> Decodable> for Symbol { } } -impl<'a, 'tcx> Decodable> for &'tcx [ty::abstract_const::Node<'tcx>] { - fn decode(d: &mut DecodeContext<'a, 'tcx>) -> Self { - ty::codec::RefDecodable::decode(d) - } -} - impl<'a, 'tcx> Decodable> for &'tcx [(ty::Predicate<'tcx>, Span)] { fn decode(d: &mut DecodeContext<'a, 'tcx>) -> Self { ty::codec::RefDecodable::decode(d) diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 3e0b5fa6dd9ff..c51b8f96c7151 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -366,7 +366,7 @@ define_tables! { mir_for_ctfe: Table>>, promoted_mir: Table>>>, // FIXME(compiler-errors): Why isn't this a LazyArray? - thir_abstract_const: Table]>>, + thir_abstract_const: Table>>, impl_parent: Table, impl_polarity: Table, constness: Table, diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 2b3f2c0241189..1cac656674d69 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -476,6 +476,7 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> { // These variants shouldn't exist in the MIR. ty::ConstKind::Placeholder(_) | ty::ConstKind::Infer(_) + | ty::ConstKind::Expr(_) | ty::ConstKind::Bound(..) => bug!("unexpected MIR constant: {:?}", literal), }, ConstantKind::Unevaluated(uv, _) => { diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index fed943169dfb5..f2030b91b9b65 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1185,7 +1185,8 @@ pub enum NullOp { AlignOf, } -#[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(HashStable, TyEncodable, TyDecodable, TypeFoldable, TypeVisitable)] pub enum UnOp { /// The `!` operator for logical inversion Not, @@ -1193,7 +1194,8 @@ pub enum UnOp { Neg, } -#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, TyEncodable, TyDecodable, Hash, HashStable)] +#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash)] +#[derive(TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] pub enum BinOp { /// The `+` operator (addition) Add, diff --git a/compiler/rustc_middle/src/mir/type_foldable.rs b/compiler/rustc_middle/src/mir/type_foldable.rs index 4c0974f86fb35..0705b4cff53ad 100644 --- a/compiler/rustc_middle/src/mir/type_foldable.rs +++ b/compiler/rustc_middle/src/mir/type_foldable.rs @@ -16,9 +16,7 @@ TrivialTypeTraversalAndLiftImpls! { UserTypeAnnotationIndex, BorrowKind, CastKind, - BinOp, NullOp, - UnOp, hir::Movability, BasicBlock, SwitchTargets, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index d86bdbd63d88d..36cdb50958caa 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -400,7 +400,7 @@ rustc_queries! { /// Try to build an abstract representation of the given constant. query thir_abstract_const( key: DefId - ) -> Result]>, ErrorGuaranteed> { + ) -> Result>, ErrorGuaranteed> { desc { |tcx| "building an abstract representation for `{}`", tcx.def_path_str(key), } @@ -409,7 +409,7 @@ rustc_queries! { /// Try to build an abstract representation of the given constant. query thir_abstract_const_of_const_arg( key: (LocalDefId, DefId) - ) -> Result]>, ErrorGuaranteed> { + ) -> Result>, ErrorGuaranteed> { desc { |tcx| "building an abstract representation for the const argument `{}`", @@ -417,15 +417,6 @@ rustc_queries! { } } - query try_unify_abstract_consts(key: - ty::ParamEnvAnd<'tcx, (ty::UnevaluatedConst<'tcx>, ty::UnevaluatedConst<'tcx> - )>) -> bool { - desc { - |tcx| "trying to unify the generic constants `{}` and `{}`", - tcx.def_path_str(key.value.0.def.did), tcx.def_path_str(key.value.1.def.did) - } - } - query mir_drops_elaborated_and_const_checked( key: ty::WithOptConstParam ) -> &'tcx Steal> { diff --git a/compiler/rustc_middle/src/ty/abstract_const.rs b/compiler/rustc_middle/src/ty/abstract_const.rs index e5bcd5fb27aa7..5de758ad9babd 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, 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>, 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>, ErrorGuaranteed> { - match ct.kind() { - ty::ConstKind::Unevaluated(uv) => AbstractConst::new(tcx, uv), - ty::ConstKind::Error(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>>, 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, - ) -> Result]>, ErrorGuaranteed> { - if let Some((did, param_did)) = def.as_const_arg() { + 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))) } -} -#[instrument(skip(tcx, f), level = "debug")] -pub fn walk_abstract_const<'tcx, R, F>( - tcx: TyCtxt<'tcx>, - ct: AbstractConst<'tcx>, - mut f: F, -) -> ControlFlow -where - F: FnMut(AbstractConst<'tcx>) -> ControlFlow, -{ - #[instrument(skip(tcx, f), level = "debug")] - fn recurse<'tcx, R>( - tcx: TyCtxt<'tcx>, - ct: AbstractConst<'tcx>, - f: &mut dyn FnMut(AbstractConst<'tcx>) -> ControlFlow, - ) -> ControlFlow { - 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>(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, } diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index b469eebfad993..b22b3961f34ea 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -346,33 +346,22 @@ impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> } impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> - for [ty::abstract_const::Node<'tcx>] -{ - fn decode(decoder: &mut D) -> &'tcx Self { - decoder.interner().arena.alloc_from_iter( - (0..decoder.read_usize()).map(|_| Decodable::decode(decoder)).collect::>(), - ) - } -} - -impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> - for [ty::abstract_const::NodeId] + for ty::List { fn decode(decoder: &mut D) -> &'tcx Self { - decoder.interner().arena.alloc_from_iter( - (0..decoder.read_usize()).map(|_| Decodable::decode(decoder)).collect::>(), + let len = decoder.read_usize(); + decoder.interner().mk_bound_variable_kinds( + (0..len).map::(|_| Decodable::decode(decoder)), ) } } -impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> - for ty::List -{ +impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> for ty::List> { fn decode(decoder: &mut D) -> &'tcx Self { let len = decoder.read_usize(); - decoder.interner().mk_bound_variable_kinds( - (0..len).map::(|_| Decodable::decode(decoder)), - ) + decoder + .interner() + .mk_const_list((0..len).map::, _>(|_| Decodable::decode(decoder))) } } diff --git a/compiler/rustc_middle/src/ty/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs index 321cba693d9ee..de63dae8a3df6 100644 --- a/compiler/rustc_middle/src/ty/consts/kind.rs +++ b/compiler/rustc_middle/src/ty/consts/kind.rs @@ -1,10 +1,12 @@ use std::convert::TryInto; +use super::Const; use crate::mir; use crate::mir::interpret::{AllocId, ConstValue, Scalar}; +use crate::ty::abstract_const::CastKind; use crate::ty::subst::{InternalSubsts, SubstsRef}; use crate::ty::ParamEnv; -use crate::ty::{self, TyCtxt, TypeVisitable}; +use crate::ty::{self, List, Ty, TyCtxt, TypeVisitable}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::DefId; @@ -70,8 +72,23 @@ pub enum ConstKind<'tcx> { /// A placeholder for a const which could not be computed; this is /// propagated to avoid useless error messages. Error(ErrorGuaranteed), + + /// Expr which contains an expression which has partially evaluated items. + Expr(Expr<'tcx>), +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)] +#[derive(HashStable, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable)] +pub enum Expr<'tcx> { + Binop(mir::BinOp, Const<'tcx>, Const<'tcx>), + UnOp(mir::UnOp, Const<'tcx>), + FunctionCall(Const<'tcx>, &'tcx List>), + Cast(CastKind, Const<'tcx>, Ty<'tcx>), } +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +static_assert_size!(Expr<'_>, 24); + #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] static_assert_size!(ConstKind<'_>, 32); diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index bede0ec142e81..fbbf3f312e791 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -137,6 +137,7 @@ pub struct CtxtInterners<'tcx> { // Specifically use a speedy hash algorithm for these hash sets, since // they're accessed quite often. type_: InternedSet<'tcx, WithStableHash>>, + const_lists: InternedSet<'tcx, List>>, substs: InternedSet<'tcx, InternalSubsts<'tcx>>, canonical_var_infos: InternedSet<'tcx, List>>, region: InternedSet<'tcx, RegionKind<'tcx>>, @@ -157,6 +158,7 @@ impl<'tcx> CtxtInterners<'tcx> { CtxtInterners { arena, type_: Default::default(), + const_lists: Default::default(), substs: Default::default(), region: Default::default(), poly_existential_predicates: Default::default(), @@ -2261,6 +2263,7 @@ macro_rules! slice_interners { } slice_interners!( + const_lists: _intern_const_list(Const<'tcx>), substs: _intern_substs(GenericArg<'tcx>), canonical_var_infos: _intern_canonical_var_infos(CanonicalVarInfo<'tcx>), poly_existential_predicates: @@ -2716,6 +2719,17 @@ impl<'tcx> TyCtxt<'tcx> { } } + pub fn mk_const_list, &'tcx List>>>( + self, + iter: I, + ) -> I::Output { + iter.intern_with(|xs| self.intern_const_list(xs)) + } + + pub fn intern_const_list(self, cs: &[ty::Const<'tcx>]) -> &'tcx List> { + if cs.is_empty() { List::empty() } else { self._intern_const_list(cs) } + } + pub fn intern_type_list(self, ts: &[Ty<'tcx>]) -> &'tcx List> { if ts.is_empty() { List::empty() diff --git a/compiler/rustc_middle/src/ty/fast_reject.rs b/compiler/rustc_middle/src/ty/fast_reject.rs index 1ee4985cf8de0..c9c09c93a3e1c 100644 --- a/compiler/rustc_middle/src/ty/fast_reject.rs +++ b/compiler/rustc_middle/src/ty/fast_reject.rs @@ -356,7 +356,10 @@ impl DeepRejectCtxt { pub fn consts_may_unify(self, obligation_ct: ty::Const<'_>, impl_ct: ty::Const<'_>) -> bool { match impl_ct.kind() { - ty::ConstKind::Param(_) | ty::ConstKind::Unevaluated(_) | ty::ConstKind::Error(_) => { + ty::ConstKind::Expr(_) + | ty::ConstKind::Param(_) + | ty::ConstKind::Unevaluated(_) + | ty::ConstKind::Error(_) => { return true; } ty::ConstKind::Value(_) => {} @@ -374,7 +377,9 @@ impl DeepRejectCtxt { // As we don't necessarily eagerly evaluate constants, // they might unify with any value. - ty::ConstKind::Unevaluated(_) | ty::ConstKind::Error(_) => true, + ty::ConstKind::Expr(_) | ty::ConstKind::Unevaluated(_) | ty::ConstKind::Error(_) => { + true + } ty::ConstKind::Value(obl) => match k { ty::ConstKind::Value(imp) => obl == imp, _ => true, diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs index 61f3a0685e72c..1e9fe779b7646 100644 --- a/compiler/rustc_middle/src/ty/flags.rs +++ b/compiler/rustc_middle/src/ty/flags.rs @@ -313,6 +313,26 @@ impl FlagComputation { self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE); } ty::ConstKind::Value(_) => {} + ty::ConstKind::Expr(e) => { + use ty::Expr; + match e { + Expr::Binop(_, l, r) => { + self.add_const(l); + self.add_const(r); + } + Expr::UnOp(_, v) => self.add_const(v), + Expr::FunctionCall(f, args) => { + self.add_const(f); + for arg in args { + self.add_const(arg); + } + } + Expr::Cast(_, c, t) => { + self.add_ty(t); + self.add_const(c); + } + } + } ty::ConstKind::Error(_) => self.add_flags(TypeFlags::HAS_ERROR), } } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index f20fa3c32adc9..e7dc2b36fafe9 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -77,7 +77,7 @@ pub use self::closure::{ CAPTURE_STRUCT_LOCAL, }; pub use self::consts::{ - Const, ConstInt, ConstKind, ConstS, InferConst, ScalarInt, UnevaluatedConst, ValTree, + Const, ConstInt, ConstKind, ConstS, Expr, InferConst, ScalarInt, UnevaluatedConst, ValTree, }; pub use self::context::{ tls, CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs index d6025248081db..b2bcf0e29cd9d 100644 --- a/compiler/rustc_middle/src/ty/parameterized.rs +++ b/compiler/rustc_middle/src/ty/parameterized.rs @@ -4,7 +4,6 @@ use rustc_index::vec::{Idx, IndexVec}; use crate::middle::exported_symbols::ExportedSymbol; use crate::mir::Body; -use crate::ty::abstract_const::Node; use crate::ty::{ self, Const, FnSig, GeneratorDiagnosticData, GenericPredicates, Predicate, TraitRef, Ty, }; @@ -124,6 +123,5 @@ parameterized_over_tcx! { Predicate, GeneratorDiagnosticData, Body, - Node, ExportedSymbol, } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index a90b63cc05b84..d5e196b2e9faa 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1253,6 +1253,9 @@ pub trait PrettyPrinter<'tcx>: self.pretty_print_bound_var(debruijn, bound_var)? } ty::ConstKind::Placeholder(placeholder) => p!(write("Placeholder({:?})", placeholder)), + // FIXME(generic_const_exprs): + // write out some legible representation of an abstract const? + ty::ConstKind::Expr(_) => p!("[Const Expr]"), ty::ConstKind::Error(_) => p!("[const error]"), }; Ok(self) diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index 3d47b71b7ce62..e6340040e9c19 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -5,7 +5,7 @@ //! subtyping, type equality, etc. use crate::ty::error::{ExpectedFound, TypeError}; -use crate::ty::{self, ImplSubject, Term, TermKind, Ty, TyCtxt, TypeFoldable}; +use crate::ty::{self, Expr, ImplSubject, Term, TermKind, Ty, TyCtxt, TypeFoldable}; use crate::ty::{GenericArg, GenericArgKind, SubstsRef}; use rustc_hir as ast; use rustc_hir::def_id::DefId; @@ -613,7 +613,10 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>( if a_ty != b_ty { relation.tcx().sess.delay_span_bug( DUMMY_SP, - &format!("cannot relate constants of different types: {} != {}", a_ty, b_ty), + &format!( + "cannot relate constants ({:?}, {:?}) of different types: {} != {}", + a, b, a_ty, b_ty + ), ); } @@ -623,11 +626,16 @@ 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()); } + if tcx.features().generic_const_exprs { + a = tcx.expand_abstract_consts(a); + b = tcx.expand_abstract_consts(b); + } + // Currently, the values that can be unified are primitive types, // and those that derive both `PartialEq` and `Eq`, corresponding // to structural-match types. @@ -644,16 +652,11 @@ 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)) - if tcx.features().generic_const_exprs => - { - tcx.try_unify_abstract_consts(relation.param_env().and((au, bu))) - } - // While this is slightly incorrect, it shouldn't matter for `min_const_generics` // and is the better alternative to waiting until `generic_const_exprs` can // be stabilized. (ty::ConstKind::Unevaluated(au), ty::ConstKind::Unevaluated(bu)) if au.def == bu.def => { + assert_eq!(a.ty(), b.ty()); let substs = relation.relate_with_variance( ty::Variance::Invariant, ty::VarianceDiagInfo::default(), @@ -665,6 +668,50 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>( a.ty(), )); } + // Before calling relate on exprs, it is necessary to ensure that the nested consts + // have identical types. + (ty::ConstKind::Expr(ae), ty::ConstKind::Expr(be)) => { + let r = relation; + + // FIXME(generic_const_exprs): is it possible to relate two consts which are not identical + // exprs? Should we care about that? + let expr = match (ae, be) { + (Expr::Binop(a_op, al, ar), Expr::Binop(b_op, bl, br)) + if a_op == b_op && al.ty() == bl.ty() && ar.ty() == br.ty() => + { + Expr::Binop(a_op, r.consts(al, bl)?, r.consts(ar, br)?) + } + (Expr::UnOp(a_op, av), Expr::UnOp(b_op, bv)) + if a_op == b_op && av.ty() == bv.ty() => + { + Expr::UnOp(a_op, r.consts(av, bv)?) + } + (Expr::Cast(ak, av, at), Expr::Cast(bk, bv, bt)) + if ak == bk && av.ty() == bv.ty() => + { + Expr::Cast(ak, r.consts(av, bv)?, r.tys(at, bt)?) + } + (Expr::FunctionCall(af, aa), Expr::FunctionCall(bf, ba)) + if aa.len() == ba.len() + && af.ty() == bf.ty() + && aa + .iter() + .zip(ba.iter()) + .all(|(a_arg, b_arg)| a_arg.ty() == b_arg.ty()) => + { + let func = r.consts(af, bf)?; + let mut related_args = Vec::with_capacity(aa.len()); + for (a_arg, b_arg) in aa.iter().zip(ba.iter()) { + related_args.push(r.consts(a_arg, b_arg)?); + } + let related_args = tcx.mk_const_list(related_args.iter()); + Expr::FunctionCall(func, related_args) + } + _ => return Err(TypeError::ConstMismatch(expected_found(r, a, b))), + }; + let kind = ty::ConstKind::Expr(expr); + return Ok(tcx.mk_const(kind, a.ty())); + } _ => false, }; if is_match { Ok(a) } else { Err(TypeError::ConstMismatch(expected_found(relation, a, b))) } diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index c8baac1ec20a7..9f70b4f1ffd25 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -601,6 +601,12 @@ impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List TypeFoldable<'tcx> for &'tcx ty::List> { + fn try_fold_with>(self, folder: &mut F) -> Result { + ty::util::fold_list(self, folder, |tcx, v| tcx.mk_const_list(v.iter())) + } +} + impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List { fn try_fold_with>(self, folder: &mut F) -> Result { ty::util::fold_list(self, folder, |tcx, v| tcx.intern_projs(v)) diff --git a/compiler/rustc_middle/src/ty/walk.rs b/compiler/rustc_middle/src/ty/walk.rs index 91db9698c41b2..4fab5abe909d4 100644 --- a/compiler/rustc_middle/src/ty/walk.rs +++ b/compiler/rustc_middle/src/ty/walk.rs @@ -214,6 +214,24 @@ fn push_inner<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent: GenericArg<'tcx>) | ty::ConstKind::Value(_) | ty::ConstKind::Error(_) => {} + ty::ConstKind::Expr(expr) => match expr { + ty::Expr::UnOp(_, v) => push_inner(stack, v.into()), + ty::Expr::Binop(_, l, r) => { + push_inner(stack, r.into()); + push_inner(stack, l.into()) + } + ty::Expr::FunctionCall(func, args) => { + for a in args.iter().rev() { + push_inner(stack, a.into()); + } + push_inner(stack, func.into()); + } + ty::Expr::Cast(_, c, t) => { + push_inner(stack, t.into()); + push_inner(stack, c.into()); + } + }, + ty::ConstKind::Unevaluated(ct) => { stack.extend(ct.substs.iter().rev()); } diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 550ed9b637844..f2177a7c28332 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -3,6 +3,7 @@ #![feature(control_flow_enum)] #![feature(rustc_private)] #![feature(try_blocks)] +#![feature(let_chains)] #![recursion_limit = "256"] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] @@ -25,7 +26,6 @@ use rustc_middle::bug; use rustc_middle::hir::nested_filter; use rustc_middle::middle::privacy::{EffectiveVisibilities, Level}; use rustc_middle::span_bug; -use rustc_middle::ty::abstract_const::{walk_abstract_const, AbstractConst, Node as ACNode}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::subst::InternalSubsts; use rustc_middle::ty::{self, Const, DefIdTree, GenericParamDefKind}; @@ -288,19 +288,8 @@ where } fn visit_const(&mut self, c: Const<'tcx>) -> ControlFlow { - self.visit_ty(c.ty())?; let tcx = self.def_id_visitor.tcx(); - if let Ok(Some(ct)) = AbstractConst::from_const(tcx, c) { - walk_abstract_const(tcx, ct, |node| match node.root(tcx) { - ACNode::Leaf(leaf) => self.visit_const(leaf), - ACNode::Cast(_, _, ty) => self.visit_ty(ty), - ACNode::Binop(..) | ACNode::UnaryOp(..) | ACNode::FunctionCall(_, _) => { - ControlFlow::CONTINUE - } - }) - } else { - ControlFlow::CONTINUE - } + tcx.expand_abstract_consts(c).super_visit_with(self) } } diff --git a/compiler/rustc_query_impl/src/on_disk_cache.rs b/compiler/rustc_query_impl/src/on_disk_cache.rs index eaed9aeb85020..c61d2a9c2d0c6 100644 --- a/compiler/rustc_query_impl/src/on_disk_cache.rs +++ b/compiler/rustc_query_impl/src/on_disk_cache.rs @@ -812,12 +812,6 @@ impl<'a, 'tcx> Decodable> } } -impl<'a, 'tcx> Decodable> for &'tcx [ty::abstract_const::Node<'tcx>] { - fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { - RefDecodable::decode(d) - } -} - impl<'a, 'tcx> Decodable> for &'tcx [(ty::Predicate<'tcx>, Span)] { fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { RefDecodable::decode(d) diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index d1a2aee207dcd..b6378af7ba09c 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -575,6 +575,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { // a path), even for it we still need to encode a placeholder, as // the path could refer back to e.g. an `impl` using the constant. ty::ConstKind::Unevaluated(_) + | ty::ConstKind::Expr(_) | ty::ConstKind::Param(_) | ty::ConstKind::Infer(_) | ty::ConstKind::Bound(..) diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index db3ddc9208ae2..e9e65336299e4 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -8,152 +8,18 @@ //! In this case we try to build an abstract representation of this constant using //! `thir_abstract_const` which can then be checked for structural equality with other //! generic constants mentioned in the `caller_bounds` of the current environment. -use rustc_errors::ErrorGuaranteed; +use rustc_hir::def::DefKind; use rustc_infer::infer::InferCtxt; use rustc_middle::mir::interpret::ErrorHandled; -use rustc_middle::ty::abstract_const::{ - walk_abstract_const, AbstractConst, FailureKind, Node, NotConstEvaluatable, -}; -use rustc_middle::ty::{self, TyCtxt, TypeVisitable}; -use rustc_span::Span; - -use std::iter; -use std::ops::ControlFlow; - -pub struct ConstUnifyCtxt<'tcx> { - pub tcx: TyCtxt<'tcx>, - pub param_env: ty::ParamEnv<'tcx>, -} - -impl<'tcx> ConstUnifyCtxt<'tcx> { - // Substitutes generics repeatedly to allow AbstractConsts to unify where a - // ConstKind::Unevaluated could be turned into an AbstractConst that would unify e.g. - // Param(N) should unify with Param(T), substs: [Unevaluated("T2", [Unevaluated("T3", [Param(N)])])] - #[inline] - #[instrument(skip(self), level = "debug")] - fn try_replace_substs_in_root( - &self, - mut abstr_const: AbstractConst<'tcx>, - ) -> Option> { - while let Node::Leaf(ct) = abstr_const.root(self.tcx) { - match AbstractConst::from_const(self.tcx, ct) { - Ok(Some(act)) => abstr_const = act, - Ok(None) => break, - Err(_) => return None, - } - } - - Some(abstr_const) - } - - /// Tries to unify two abstract constants using structural equality. - #[instrument(skip(self), level = "debug")] - pub fn try_unify(&self, a: AbstractConst<'tcx>, b: AbstractConst<'tcx>) -> bool { - let a = if let Some(a) = self.try_replace_substs_in_root(a) { - a - } else { - return true; - }; - - let b = if let Some(b) = self.try_replace_substs_in_root(b) { - b - } else { - return true; - }; - let a_root = a.root(self.tcx); - let b_root = b.root(self.tcx); - debug!(?a_root, ?b_root); - - match (a_root, b_root) { - (Node::Leaf(a_ct), Node::Leaf(b_ct)) => { - let a_ct = a_ct.eval(self.tcx, self.param_env); - debug!("a_ct evaluated: {:?}", a_ct); - let b_ct = b_ct.eval(self.tcx, self.param_env); - debug!("b_ct evaluated: {:?}", b_ct); - - if a_ct.ty() != b_ct.ty() { - return false; - } - - match (a_ct.kind(), b_ct.kind()) { - // We can just unify errors with everything to reduce the amount of - // emitted errors here. - (ty::ConstKind::Error(_), _) | (_, ty::ConstKind::Error(_)) => true, - (ty::ConstKind::Param(a_param), ty::ConstKind::Param(b_param)) => { - a_param == b_param - } - (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => a_val == b_val, - // If we have `fn a() -> [u8; N + 1]` and `fn b() -> [u8; 1 + M]` - // we do not want to use `assert_eq!(a(), b())` to infer that `N` and `M` have to be `1`. This - // means that we only allow inference variables if they are equal. - (ty::ConstKind::Infer(a_val), ty::ConstKind::Infer(b_val)) => a_val == b_val, - // We expand generic anonymous constants at the start of this function, so this - // branch should only be taking when dealing with associated constants, at - // which point directly comparing them seems like the desired behavior. - // - // FIXME(generic_const_exprs): This isn't actually the case. - // We also take this branch for concrete anonymous constants and - // expand generic anonymous constants with concrete substs. - (ty::ConstKind::Unevaluated(a_uv), ty::ConstKind::Unevaluated(b_uv)) => { - a_uv == b_uv - } - // FIXME(generic_const_exprs): We may want to either actually try - // to evaluate `a_ct` and `b_ct` if they are fully concrete or something like - // this, for now we just return false here. - _ => false, - } - } - (Node::Binop(a_op, al, ar), Node::Binop(b_op, bl, br)) if a_op == b_op => { - self.try_unify(a.subtree(al), b.subtree(bl)) - && self.try_unify(a.subtree(ar), b.subtree(br)) - } - (Node::UnaryOp(a_op, av), Node::UnaryOp(b_op, bv)) if a_op == b_op => { - self.try_unify(a.subtree(av), b.subtree(bv)) - } - (Node::FunctionCall(a_f, a_args), Node::FunctionCall(b_f, b_args)) - if a_args.len() == b_args.len() => - { - self.try_unify(a.subtree(a_f), b.subtree(b_f)) - && iter::zip(a_args, b_args) - .all(|(&an, &bn)| self.try_unify(a.subtree(an), b.subtree(bn))) - } - (Node::Cast(a_kind, a_operand, a_ty), Node::Cast(b_kind, b_operand, b_ty)) - if (a_ty == b_ty) && (a_kind == b_kind) => - { - self.try_unify(a.subtree(a_operand), b.subtree(b_operand)) - } - // use this over `_ => false` to make adding variants to `Node` less error prone - (Node::Cast(..), _) - | (Node::FunctionCall(..), _) - | (Node::UnaryOp(..), _) - | (Node::Binop(..), _) - | (Node::Leaf(..), _) => false, - } - } -} +use rustc_middle::traits::ObligationCause; +use rustc_middle::ty::abstract_const::NotConstEvaluatable; +use rustc_middle::ty::{self, TyCtxt, TypeVisitable, TypeVisitor}; -#[instrument(skip(tcx), level = "debug")] -pub fn try_unify_abstract_consts<'tcx>( - tcx: TyCtxt<'tcx>, - (a, b): (ty::UnevaluatedConst<'tcx>, ty::UnevaluatedConst<'tcx>), - param_env: ty::ParamEnv<'tcx>, -) -> bool { - (|| { - if let Some(a) = AbstractConst::new(tcx, a)? { - if let Some(b) = AbstractConst::new(tcx, b)? { - let const_unify_ctxt = ConstUnifyCtxt { tcx, param_env }; - return Ok(const_unify_ctxt.try_unify(a, b)); - } - } +use rustc_span::Span; +use std::ops::ControlFlow; - Ok(false) - })() - .unwrap_or_else(|_: ErrorGuaranteed| true) - // FIXME(generic_const_exprs): We should instead have this - // method return the resulting `ty::Const` and return `ConstKind::Error` - // on `ErrorGuaranteed`. -} +use crate::traits::ObligationCtxt; /// Check if a given constant can be evaluated. #[instrument(skip(infcx), level = "debug")] @@ -166,6 +32,8 @@ pub fn is_const_evaluatable<'tcx>( let tcx = infcx.tcx; let uv = match ct.kind() { ty::ConstKind::Unevaluated(uv) => uv, + // FIXME(generic_const_exprs): this seems wrong but I couldn't find a way to get this to trigger + ty::ConstKind::Expr(_) => bug!("unexpected expr in `is_const_evaluatable: {ct:?}"), ty::ConstKind::Param(_) | ty::ConstKind::Bound(_, _) | ty::ConstKind::Placeholder(_) @@ -175,21 +43,25 @@ pub fn is_const_evaluatable<'tcx>( }; if tcx.features().generic_const_exprs { - if let Some(ct) = AbstractConst::new(tcx, uv)? { - if satisfied_from_param_env(tcx, ct, param_env)? { + let ct = tcx.expand_abstract_consts(ct); + + let is_anon_ct = if let ty::ConstKind::Unevaluated(uv) = ct.kind() { + tcx.def_kind(uv.def.did) == DefKind::AnonConst + } else { + false + }; + + if !is_anon_ct { + if satisfied_from_param_env(tcx, infcx, ct, param_env) { return Ok(()); } - match ct.unify_failure_kind(tcx) { - FailureKind::MentionsInfer => { - return Err(NotConstEvaluatable::MentionsInfer); - } - FailureKind::MentionsParam => { - return Err(NotConstEvaluatable::MentionsParam); - } - // returned below - FailureKind::Concrete => {} + if ct.has_non_region_infer() { + return Err(NotConstEvaluatable::MentionsInfer); + } else if ct.has_non_region_param() { + return Err(NotConstEvaluatable::MentionsParam); } } + let concrete = infcx.const_eval_resolve(param_env, uv, Some(span)); match concrete { Err(ErrorHandled::TooGeneric) => Err(NotConstEvaluatable::Error( @@ -211,28 +83,33 @@ pub fn is_const_evaluatable<'tcx>( // // See #74595 for more details about this. let concrete = infcx.const_eval_resolve(param_env, uv, Some(span)); - 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. - Err(_) if tcx.sess.is_nightly_build() - && let Ok(Some(ct)) = AbstractConst::new(tcx, uv) - && satisfied_from_param_env(tcx, ct, param_env) == Ok(true) => { - tcx.sess - .struct_span_fatal( - // Slightly better span than just using `span` alone - if span == rustc_span::DUMMY_SP { tcx.def_span(uv.def.did) } else { span }, - "failed to evaluate generic const expression", - ) - .note("the crate this constant originates from uses `#![feature(generic_const_exprs)]`") - .span_suggestion_verbose( - rustc_span::DUMMY_SP, - "consider enabling this feature", - "#![feature(generic_const_exprs)]\n", - rustc_errors::Applicability::MaybeIncorrect, - ) - .emit() + // 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() + && satisfied_from_param_env( + tcx, + infcx, + tcx.expand_abstract_consts(ct), + param_env, + ) => + { + tcx.sess + .struct_span_fatal( + // Slightly better span than just using `span` alone + if span == rustc_span::DUMMY_SP { tcx.def_span(uv.def.did) } else { span }, + "failed to evaluate generic const expression", + ) + .note("the crate this constant originates from uses `#![feature(generic_const_exprs)]`") + .span_suggestion_verbose( + rustc_span::DUMMY_SP, + "consider enabling this feature", + "#![feature(generic_const_exprs)]\n", + rustc_errors::Applicability::MaybeIncorrect, + ) + .emit() } Err(ErrorHandled::TooGeneric) => { @@ -241,49 +118,82 @@ pub fn is_const_evaluatable<'tcx>( } else if uv.has_non_region_param() { NotConstEvaluatable::MentionsParam } else { - let guar = infcx.tcx.sess.delay_span_bug(span, format!("Missing value for constant, but no error reported?")); + let guar = infcx.tcx.sess.delay_span_bug( + span, + format!("Missing value for constant, but no error reported?"), + ); NotConstEvaluatable::Error(guar) }; Err(err) - }, + } Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e)), Ok(_) => Ok(()), } } } -#[instrument(skip(tcx), level = "debug")] +#[instrument(skip(infcx, tcx), level = "debug")] fn satisfied_from_param_env<'tcx>( tcx: TyCtxt<'tcx>, - ct: AbstractConst<'tcx>, + infcx: &InferCtxt<'tcx>, + ct: ty::Const<'tcx>, param_env: ty::ParamEnv<'tcx>, -) -> Result { +) -> bool { + // 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 let Ok(()) = self.infcx.commit_if_ok(|_| { + let ocx = ObligationCtxt::new_in_snapshot(self.infcx); + if let Ok(()) = ocx.eq(&ObligationCause::dummy(), self.param_env, c.ty(), self.ct.ty()) + && let Ok(()) = ocx.eq(&ObligationCause::dummy(), self.param_env, c, self.ct) + && ocx.select_all_or_error().is_empty() + { + Ok(()) + } else { + Err(()) + } + }) { + ControlFlow::BREAK + } else if let ty::ConstKind::Expr(e) = c.kind() { + e.visit_with(self) + } else { + // FIXME(generic_const_exprs): This doesn't recurse into `>::ASSOC`'s substs. + // This is currently unobservable as `>::ASSOC` creates an anon const + // with its own `ConstEvaluatable` bound in the param env which we will visit separately. + // + // If we start allowing directly writing `ConstKind::Expr` without an intermediate anon const + // this will be incorrect. It might be worth investigating making `predicates_of` elaborate + // all of the `ConstEvaluatable` bounds rather than having a visitor here. + ControlFlow::CONTINUE + } + } + } + for pred in param_env.caller_bounds() { match pred.kind().skip_binder() { - ty::PredicateKind::ConstEvaluatable(uv) => { - if let Some(b_ct) = AbstractConst::from_const(tcx, uv)? { - let const_unify_ctxt = ConstUnifyCtxt { tcx, param_env }; - - // 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` - let result = walk_abstract_const(tcx, b_ct, |b_ct| { - match const_unify_ctxt.try_unify(ct, b_ct) { - true => ControlFlow::BREAK, - false => ControlFlow::CONTINUE, - } - }); - - if let ControlFlow::Break(()) = result { - debug!("is_const_evaluatable: abstract_const ~~> ok"); - return Ok(true); - } + ty::PredicateKind::ConstEvaluatable(ce) => { + let b_ct = tcx.expand_abstract_consts(ce); + let mut v = Visitor { ct, infcx, param_env }; + let result = b_ct.visit_with(&mut v); + + if let ControlFlow::Break(()) = result { + debug!("is_const_evaluatable: abstract_const ~~> ok"); + return true; } } _ => {} // don't care } } - Ok(false) + false } diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index b0d0d1a7cba44..80ee363d72f62 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -455,20 +455,47 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { } ty::PredicateKind::ConstEquate(c1, c2) => { + let tcx = self.selcx.tcx(); assert!( - self.selcx.tcx().features().generic_const_exprs, + tcx.features().generic_const_exprs, "`ConstEquate` without a feature gate: {c1:?} {c2:?}", ); - debug!(?c1, ?c2, "equating consts"); // FIXME: we probably should only try to unify abstract constants // 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)) = - (c1.kind(), c2.kind()) { - if infcx.try_unify_abstract_consts(a, b, obligation.param_env) { - return ProcessResult::Changed(vec![]); + let c1 = tcx.expand_abstract_consts(c1); + let c2 = tcx.expand_abstract_consts(c2); + debug!("equating consts:\nc1= {:?}\nc2= {:?}", c1, c2); + + use rustc_hir::def::DefKind; + use ty::ConstKind::Unevaluated; + match (c1.kind(), c2.kind()) { + (Unevaluated(a), Unevaluated(b)) + if a.def.did == b.def.did + && tcx.def_kind(a.def.did) == DefKind::AssocConst => + { + if let Ok(new_obligations) = infcx + .at(&obligation.cause, obligation.param_env) + .trace(c1, c2) + .eq(a.substs, b.substs) + { + return ProcessResult::Changed(mk_pending( + new_obligations.into_obligations(), + )); + } + } + (_, Unevaluated(_)) | (Unevaluated(_), _) => (), + (_, _) => { + if let Ok(new_obligations) = + infcx.at(&obligation.cause, obligation.param_env).eq(c1, c2) + { + return ProcessResult::Changed(mk_pending( + new_obligations.into_obligations(), + )); + } + } } } @@ -508,7 +535,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/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 3112756a0b14d..5285cfa674669 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -932,10 +932,6 @@ pub fn provide(providers: &mut ty::query::Providers) { vtable_trait_upcasting_coercion_new_vptr_slot, subst_and_check_impossible_predicates, is_impossible_method, - try_unify_abstract_consts: |tcx, param_env_and| { - let (param_env, (a, b)) = param_env_and.into_parts(); - const_evaluatable::try_unify_abstract_consts(tcx, (a, b), param_env) - }, ..*providers }; } diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 9f531e03f06ad..a45749fe48cd4 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -17,11 +17,10 @@ use hir::def::DefKind; use rustc_errors::{DelayDm, FatalError, MultiSpan}; use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_middle::ty::abstract_const::{walk_abstract_const, AbstractConst}; +use rustc_middle::ty::subst::{GenericArg, InternalSubsts}; use rustc_middle::ty::{ self, EarlyBinder, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, }; -use rustc_middle::ty::{GenericArg, InternalSubsts}; use rustc_middle::ty::{Predicate, ToPredicate}; use rustc_session::lint::builtin::WHERE_CLAUSES_OBJECT_SAFETY; use rustc_span::symbol::Symbol; @@ -837,23 +836,9 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<'tcx>>( } fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow { - // Constants can only influence object safety if they reference `Self`. + // Constants can only influence object safety if they are generic and reference `Self`. // This is only possible for unevaluated constants, so we walk these here. - // - // If `AbstractConst::from_const` returned an error we already failed compilation - // so we don't have to emit an additional error here. - use rustc_middle::ty::abstract_const::Node; - if let Ok(Some(ct)) = AbstractConst::from_const(self.tcx, ct) { - walk_abstract_const(self.tcx, ct, |node| match node.root(self.tcx) { - Node::Leaf(leaf) => self.visit_const(leaf), - Node::Cast(_, _, ty) => self.visit_ty(ty), - Node::Binop(..) | Node::UnaryOp(..) | Node::FunctionCall(_, _) => { - ControlFlow::CONTINUE - } - }) - } else { - ct.super_visit_with(self) - } + self.tcx.expand_abstract_consts(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 3c8f8b5642c9e..6e8706897bfae 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -657,21 +657,62 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } ty::PredicateKind::ConstEquate(c1, c2) => { + let tcx = self.tcx(); assert!( - self.tcx().features().generic_const_exprs, + tcx.features().generic_const_exprs, "`ConstEquate` without a feature gate: {c1:?} {c2:?}", ); - debug!(?c1, ?c2, "evaluate_predicate_recursively: equating consts"); - // FIXME: we probably should only try to unify abstract constants - // 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)) = - (c1.kind(), c2.kind()) { - if self.infcx.try_unify_abstract_consts(a, b, obligation.param_env) { - return Ok(EvaluatedToOk); + let c1 = tcx.expand_abstract_consts(c1); + let c2 = tcx.expand_abstract_consts(c2); + debug!( + "evalaute_predicate_recursively: equating consts:\nc1= {:?}\nc2= {:?}", + c1, c2 + ); + + use rustc_hir::def::DefKind; + use ty::ConstKind::Unevaluated; + match (c1.kind(), c2.kind()) { + (Unevaluated(a), Unevaluated(b)) + if a.def.did == b.def.did + && tcx.def_kind(a.def.did) == DefKind::AssocConst => + { + if let Ok(new_obligations) = self + .infcx + .at(&obligation.cause, obligation.param_env) + .trace(c1, c2) + .eq(a.substs, b.substs) + { + let mut obligations = new_obligations.obligations; + self.add_depth( + obligations.iter_mut(), + obligation.recursion_depth, + ); + return self.evaluate_predicates_recursively( + previous_stack, + obligations.into_iter(), + ); + } + } + (_, Unevaluated(_)) | (Unevaluated(_), _) => (), + (_, _) => { + if let Ok(new_obligations) = self + .infcx + .at(&obligation.cause, obligation.param_env) + .eq(c1, c2) + { + let mut obligations = new_obligations.obligations; + self.add_depth( + obligations.iter_mut(), + obligation.recursion_depth, + ); + return self.evaluate_predicates_recursively( + previous_stack, + obligations.into_iter(), + ); + } + } } } @@ -698,7 +739,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/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index fe68315ceb690..0855d6d19736f 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -476,6 +476,11 @@ impl<'tcx> WfPredicates<'tcx> { ty::Binder::dummy(ty::PredicateKind::WellFormed(ct.into())), )); } + // FIXME(generic_const_exprs): This seems wrong but I could not find a way to get this to trigger + ty::ConstKind::Expr(_) => { + bug!("checking wfness of `ConstKind::Expr` is unsupported") + } + ty::ConstKind::Error(_) | ty::ConstKind::Param(_) | ty::ConstKind::Bound(..) diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index cb41c4f94e2e6..2b7018bc9c300 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -1,10 +1,11 @@ use rustc_errors::ErrorGuaranteed; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; -use rustc_index::vec::IndexVec; use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput}; -use rustc_middle::ty::abstract_const::{CastKind, Node, NodeId}; -use rustc_middle::ty::{self, TyCtxt, TypeVisitable}; +use rustc_middle::thir::visit; +use rustc_middle::thir::visit::Visitor; +use rustc_middle::ty::abstract_const::CastKind; +use rustc_middle::ty::{self, ConstKind, Expr, TyCtxt, TypeVisitable}; use rustc_middle::{mir, thir}; use rustc_span::Span; use rustc_target::abi::VariantIdx; @@ -76,326 +77,278 @@ pub(crate) fn destructure_const<'tcx>( ty::DestructuredConst { variant, fields } } -pub struct AbstractConstBuilder<'a, 'tcx> { - tcx: TyCtxt<'tcx>, - body_id: thir::ExprId, - body: &'a thir::Thir<'tcx>, - /// The current WIP node tree. - nodes: IndexVec>, -} - -impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { - fn root_span(&self) -> Span { - self.body.exprs[self.body_id].span - } - - fn error(&mut self, sub: GenericConstantTooComplexSub) -> Result { - let reported = self.tcx.sess.emit_err(GenericConstantTooComplex { - span: self.root_span(), - maybe_supported: None, - sub, - }); - - Err(reported) +/// We do not allow all binary operations in abstract consts, so filter disallowed ones. +fn check_binop(op: mir::BinOp) -> bool { + use mir::BinOp::*; + match op { + Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Shl | Shr | Eq | Lt | Le | Ne + | Ge | Gt => true, + Offset => false, } +} - fn maybe_supported_error( - &mut self, - sub: GenericConstantTooComplexSub, - ) -> Result { - let reported = self.tcx.sess.emit_err(GenericConstantTooComplex { - span: self.root_span(), - maybe_supported: Some(()), - sub, - }); - - Err(reported) +/// While we currently allow all unary operations, we still want to explicitly guard against +/// future changes here. +fn check_unop(op: mir::UnOp) -> bool { + use mir::UnOp::*; + match op { + Not | Neg => true, } +} - #[instrument(skip(tcx, body, body_id), level = "debug")] - pub fn new( - tcx: TyCtxt<'tcx>, - (body, body_id): (&'a thir::Thir<'tcx>, thir::ExprId), - ) -> Result>, ErrorGuaranteed> { - let builder = AbstractConstBuilder { tcx, body_id, body, nodes: IndexVec::new() }; - - struct IsThirPolymorphic<'a, 'tcx> { - is_poly: bool, - thir: &'a thir::Thir<'tcx>, +fn recurse_build<'tcx>( + tcx: TyCtxt<'tcx>, + body: &thir::Thir<'tcx>, + node: thir::ExprId, + root_span: Span, +) -> Result, ErrorGuaranteed> { + use thir::ExprKind; + let node = &body.exprs[node]; + + let maybe_supported_error = |a| maybe_supported_error(tcx, a, root_span); + let error = |a| error(tcx, a, root_span); + + Ok(match &node.kind { + // I dont know if handling of these 3 is correct + &ExprKind::Scope { value, .. } => recurse_build(tcx, body, value, root_span)?, + &ExprKind::PlaceTypeAscription { source, .. } + | &ExprKind::ValueTypeAscription { source, .. } => { + recurse_build(tcx, body, source, root_span)? } - - use crate::rustc_middle::thir::visit::Visitor; - use thir::visit; - - impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> { - fn expr_is_poly(&mut self, expr: &thir::Expr<'tcx>) -> bool { - if expr.ty.has_non_region_param() { - return true; + &ExprKind::Literal { lit, neg } => { + let sp = node.span; + match tcx.at(sp).lit_to_const(LitToConstInput { lit: &lit.node, ty: node.ty, neg }) { + Ok(c) => c, + Err(LitToConstError::Reported(guar)) => { + tcx.const_error_with_guaranteed(node.ty, guar) } - - match expr.kind { - thir::ExprKind::NamedConst { substs, .. } => substs.has_non_region_param(), - thir::ExprKind::ConstParam { .. } => true, - thir::ExprKind::Repeat { value, count } => { - self.visit_expr(&self.thir()[value]); - count.has_non_region_param() - } - _ => false, + Err(LitToConstError::TypeError) => { + bug!("encountered type error in lit_to_const") } } + } + &ExprKind::NonHirLiteral { lit, user_ty: _ } => { + let val = ty::ValTree::from_scalar_int(lit); + ty::Const::from_value(tcx, val, node.ty) + } + &ExprKind::ZstLiteral { user_ty: _ } => { + let val = ty::ValTree::zst(); + ty::Const::from_value(tcx, val, node.ty) + } + &ExprKind::NamedConst { def_id, substs, user_ty: _ } => { + let uneval = ty::UnevaluatedConst::new(ty::WithOptConstParam::unknown(def_id), substs); + tcx.mk_const(ty::ConstKind::Unevaluated(uneval), node.ty) + } + ExprKind::ConstParam { param, .. } => tcx.mk_const(ty::ConstKind::Param(*param), node.ty), - fn pat_is_poly(&mut self, pat: &thir::Pat<'tcx>) -> bool { - if pat.ty.has_non_region_param() { - return true; - } + ExprKind::Call { fun, args, .. } => { + let fun = recurse_build(tcx, body, *fun, root_span)?; - match pat.kind { - thir::PatKind::Constant { value } => value.has_non_region_param(), - thir::PatKind::Range(box thir::PatRange { lo, hi, .. }) => { - lo.has_non_region_param() || hi.has_non_region_param() - } - _ => false, - } + let mut new_args = Vec::>::with_capacity(args.len()); + for &id in args.iter() { + new_args.push(recurse_build(tcx, body, id, root_span)?); } + let new_args = tcx.mk_const_list(new_args.iter()); + tcx.mk_const(ConstKind::Expr(Expr::FunctionCall(fun, new_args)), node.ty) } - - impl<'a, 'tcx> visit::Visitor<'a, 'tcx> for IsThirPolymorphic<'a, 'tcx> { - fn thir(&self) -> &'a thir::Thir<'tcx> { - &self.thir - } - - #[instrument(skip(self), level = "debug")] - fn visit_expr(&mut self, expr: &thir::Expr<'tcx>) { - self.is_poly |= self.expr_is_poly(expr); - if !self.is_poly { - visit::walk_expr(self, expr) - } + &ExprKind::Binary { op, lhs, rhs } if check_binop(op) => { + let lhs = recurse_build(tcx, body, lhs, root_span)?; + let rhs = recurse_build(tcx, body, rhs, root_span)?; + tcx.mk_const(ConstKind::Expr(Expr::Binop(op, lhs, rhs)), node.ty) + } + &ExprKind::Unary { op, arg } if check_unop(op) => { + let arg = recurse_build(tcx, body, arg, root_span)?; + tcx.mk_const(ConstKind::Expr(Expr::UnOp(op, arg)), node.ty) + } + // This is necessary so that the following compiles: + // + // ``` + // fn foo(a: [(); N + 1]) { + // bar::<{ N + 1 }>(); + // } + // ``` + ExprKind::Block { block } => { + if let thir::Block { stmts: box [], expr: Some(e), .. } = &body.blocks[*block] { + recurse_build(tcx, body, *e, root_span)? + } else { + maybe_supported_error(GenericConstantTooComplexSub::BlockNotSupported(node.span))? } - - #[instrument(skip(self), level = "debug")] - fn visit_pat(&mut self, pat: &thir::Pat<'tcx>) { - self.is_poly |= self.pat_is_poly(pat); - if !self.is_poly { - visit::walk_pat(self, pat); - } + } + // `ExprKind::Use` happens when a `hir::ExprKind::Cast` is a + // "coercion cast" i.e. using a coercion or is a no-op. + // This is important so that `N as usize as usize` doesnt unify with `N as usize`. (untested) + &ExprKind::Use { source } => { + let arg = recurse_build(tcx, body, source, root_span)?; + tcx.mk_const(ConstKind::Expr(Expr::Cast(CastKind::Use, arg, node.ty)), node.ty) + } + &ExprKind::Cast { source } => { + let arg = recurse_build(tcx, body, source, root_span)?; + tcx.mk_const(ConstKind::Expr(Expr::Cast(CastKind::As, arg, node.ty)), node.ty) + } + ExprKind::Borrow { arg, .. } => { + let arg_node = &body.exprs[*arg]; + + // Skip reborrows for now until we allow Deref/Borrow/AddressOf + // expressions. + // FIXME(generic_const_exprs): Verify/explain why this is sound + if let ExprKind::Deref { arg } = arg_node.kind { + recurse_build(tcx, body, arg, root_span)? + } else { + maybe_supported_error(GenericConstantTooComplexSub::BorrowNotSupported(node.span))? } } - - let mut is_poly_vis = IsThirPolymorphic { is_poly: false, thir: body }; - visit::walk_expr(&mut is_poly_vis, &body[body_id]); - debug!("AbstractConstBuilder: is_poly={}", is_poly_vis.is_poly); - if !is_poly_vis.is_poly { - return Ok(None); + // FIXME(generic_const_exprs): We may want to support these. + ExprKind::AddressOf { .. } | ExprKind::Deref { .. } => maybe_supported_error( + GenericConstantTooComplexSub::AddressAndDerefNotSupported(node.span), + )?, + ExprKind::Repeat { .. } | ExprKind::Array { .. } => { + maybe_supported_error(GenericConstantTooComplexSub::ArrayNotSupported(node.span))? } + ExprKind::NeverToAny { .. } => { + maybe_supported_error(GenericConstantTooComplexSub::NeverToAnyNotSupported(node.span))? + } + ExprKind::Tuple { .. } => { + maybe_supported_error(GenericConstantTooComplexSub::TupleNotSupported(node.span))? + } + ExprKind::Index { .. } => { + maybe_supported_error(GenericConstantTooComplexSub::IndexNotSupported(node.span))? + } + ExprKind::Field { .. } => { + maybe_supported_error(GenericConstantTooComplexSub::FieldNotSupported(node.span))? + } + ExprKind::ConstBlock { .. } => { + maybe_supported_error(GenericConstantTooComplexSub::ConstBlockNotSupported(node.span))? + } + ExprKind::Adt(_) => { + maybe_supported_error(GenericConstantTooComplexSub::AdtNotSupported(node.span))? + } + // dont know if this is correct + ExprKind::Pointer { .. } => { + error(GenericConstantTooComplexSub::PointerNotSupported(node.span))? + } + ExprKind::Yield { .. } => { + error(GenericConstantTooComplexSub::YieldNotSupported(node.span))? + } + ExprKind::Continue { .. } | ExprKind::Break { .. } | ExprKind::Loop { .. } => { + error(GenericConstantTooComplexSub::LoopNotSupported(node.span))? + } + ExprKind::Box { .. } => error(GenericConstantTooComplexSub::BoxNotSupported(node.span))?, - Ok(Some(builder)) - } - - /// We do not allow all binary operations in abstract consts, so filter disallowed ones. - fn check_binop(op: mir::BinOp) -> bool { - use mir::BinOp::*; - match op { - Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Shl | Shr | Eq | Lt | Le - | Ne | Ge | Gt => true, - Offset => false, + ExprKind::Unary { .. } => unreachable!(), + // we handle valid unary/binary ops above + ExprKind::Binary { .. } => { + error(GenericConstantTooComplexSub::BinaryNotSupported(node.span))? + } + ExprKind::LogicalOp { .. } => { + error(GenericConstantTooComplexSub::LogicalOpNotSupported(node.span))? + } + ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => { + error(GenericConstantTooComplexSub::AssignNotSupported(node.span))? + } + ExprKind::Closure { .. } | ExprKind::Return { .. } => { + error(GenericConstantTooComplexSub::ClosureAndReturnNotSupported(node.span))? + } + // let expressions imply control flow + ExprKind::Match { .. } | ExprKind::If { .. } | ExprKind::Let { .. } => { + error(GenericConstantTooComplexSub::ControlFlowNotSupported(node.span))? + } + ExprKind::InlineAsm { .. } => { + error(GenericConstantTooComplexSub::InlineAsmNotSupported(node.span))? } - } - /// While we currently allow all unary operations, we still want to explicitly guard against - /// future changes here. - fn check_unop(op: mir::UnOp) -> bool { - use mir::UnOp::*; - match op { - Not | Neg => true, + // we dont permit let stmts so `VarRef` and `UpvarRef` cant happen + ExprKind::VarRef { .. } + | ExprKind::UpvarRef { .. } + | ExprKind::StaticRef { .. } + | ExprKind::ThreadLocalRef(_) => { + error(GenericConstantTooComplexSub::OperationNotSupported(node.span))? } - } + }) +} - /// Builds the abstract const by walking the thir and bailing out when - /// encountering an unsupported operation. - pub fn build(mut self) -> Result<&'tcx [Node<'tcx>], ErrorGuaranteed> { - debug!("AbstractConstBuilder::build: body={:?}", &*self.body); - self.recurse_build(self.body_id)?; +struct IsThirPolymorphic<'a, 'tcx> { + is_poly: bool, + thir: &'a thir::Thir<'tcx>, +} - Ok(self.tcx.arena.alloc_from_iter(self.nodes.into_iter())) - } +fn error<'tcx>( + tcx: TyCtxt<'tcx>, + sub: GenericConstantTooComplexSub, + root_span: Span, +) -> Result { + let reported = tcx.sess.emit_err(GenericConstantTooComplex { + span: root_span, + maybe_supported: None, + sub, + }); + + Err(reported) +} - fn recurse_build(&mut self, node: thir::ExprId) -> Result { - use thir::ExprKind; - let node = &self.body.exprs[node]; - Ok(match &node.kind { - // I dont know if handling of these 3 is correct - &ExprKind::Scope { value, .. } => self.recurse_build(value)?, - &ExprKind::PlaceTypeAscription { source, .. } - | &ExprKind::ValueTypeAscription { source, .. } => self.recurse_build(source)?, - &ExprKind::Literal { lit, neg } => { - let sp = node.span; - let constant = match self.tcx.at(sp).lit_to_const(LitToConstInput { - lit: &lit.node, - ty: node.ty, - neg, - }) { - Ok(c) => c, - Err(LitToConstError::Reported(guar)) => { - self.tcx.const_error_with_guaranteed(node.ty, guar) - } - Err(LitToConstError::TypeError) => { - bug!("encountered type error in lit_to_const") - } - }; - - self.nodes.push(Node::Leaf(constant)) - } - &ExprKind::NonHirLiteral { lit, user_ty: _ } => { - let val = ty::ValTree::from_scalar_int(lit); - self.nodes.push(Node::Leaf(ty::Const::from_value(self.tcx, val, node.ty))) - } - &ExprKind::ZstLiteral { user_ty: _ } => { - let val = ty::ValTree::zst(); - self.nodes.push(Node::Leaf(ty::Const::from_value(self.tcx, val, node.ty))) - } - &ExprKind::NamedConst { def_id, substs, user_ty: _ } => { - let uneval = - ty::UnevaluatedConst::new(ty::WithOptConstParam::unknown(def_id), substs); +fn maybe_supported_error<'tcx>( + tcx: TyCtxt<'tcx>, + sub: GenericConstantTooComplexSub, + root_span: Span, +) -> Result { + let reported = tcx.sess.emit_err(GenericConstantTooComplex { + span: root_span, + maybe_supported: Some(()), + sub, + }); + + Err(reported) +} - let constant = self.tcx.mk_const(ty::ConstKind::Unevaluated(uneval), node.ty); +impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> { + fn expr_is_poly(&mut self, expr: &thir::Expr<'tcx>) -> bool { + if expr.ty.has_non_region_param() { + return true; + } - self.nodes.push(Node::Leaf(constant)) + match expr.kind { + thir::ExprKind::NamedConst { substs, .. } => substs.has_non_region_param(), + thir::ExprKind::ConstParam { .. } => true, + thir::ExprKind::Repeat { value, count } => { + self.visit_expr(&self.thir()[value]); + count.has_non_region_param() } + _ => false, + } + } + fn pat_is_poly(&mut self, pat: &thir::Pat<'tcx>) -> bool { + if pat.ty.has_non_region_param() { + return true; + } - ExprKind::ConstParam { param, .. } => { - let const_param = self.tcx.mk_const(ty::ConstKind::Param(*param), node.ty); - self.nodes.push(Node::Leaf(const_param)) + match pat.kind { + thir::PatKind::Constant { value } => value.has_non_region_param(), + thir::PatKind::Range(box thir::PatRange { lo, hi, .. }) => { + lo.has_non_region_param() || hi.has_non_region_param() } + _ => false, + } + } +} - ExprKind::Call { fun, args, .. } => { - let fun = self.recurse_build(*fun)?; - - let mut new_args = Vec::::with_capacity(args.len()); - for &id in args.iter() { - new_args.push(self.recurse_build(id)?); - } - let new_args = self.tcx.arena.alloc_slice(&new_args); - self.nodes.push(Node::FunctionCall(fun, new_args)) - } - &ExprKind::Binary { op, lhs, rhs } if Self::check_binop(op) => { - let lhs = self.recurse_build(lhs)?; - let rhs = self.recurse_build(rhs)?; - self.nodes.push(Node::Binop(op, lhs, rhs)) - } - &ExprKind::Unary { op, arg } if Self::check_unop(op) => { - let arg = self.recurse_build(arg)?; - self.nodes.push(Node::UnaryOp(op, arg)) - } - // This is necessary so that the following compiles: - // - // ``` - // fn foo(a: [(); N + 1]) { - // bar::<{ N + 1 }>(); - // } - // ``` - ExprKind::Block { block } => { - if let thir::Block { stmts: box [], expr: Some(e), .. } = &self.body.blocks[*block] - { - self.recurse_build(*e)? - } else { - self.maybe_supported_error(GenericConstantTooComplexSub::BlockNotSupported( - node.span, - ))? - } - } - // `ExprKind::Use` happens when a `hir::ExprKind::Cast` is a - // "coercion cast" i.e. using a coercion or is a no-op. - // This is important so that `N as usize as usize` doesnt unify with `N as usize`. (untested) - &ExprKind::Use { source } => { - let arg = self.recurse_build(source)?; - self.nodes.push(Node::Cast(CastKind::Use, arg, node.ty)) - } - &ExprKind::Cast { source } => { - let arg = self.recurse_build(source)?; - self.nodes.push(Node::Cast(CastKind::As, arg, node.ty)) - } - ExprKind::Borrow { arg, .. } => { - let arg_node = &self.body.exprs[*arg]; - - // Skip reborrows for now until we allow Deref/Borrow/AddressOf - // expressions. - // FIXME(generic_const_exprs): Verify/explain why this is sound - if let ExprKind::Deref { arg } = arg_node.kind { - self.recurse_build(arg)? - } else { - self.maybe_supported_error(GenericConstantTooComplexSub::BorrowNotSupported( - node.span, - ))? - } - } - // FIXME(generic_const_exprs): We may want to support these. - ExprKind::AddressOf { .. } | ExprKind::Deref { .. } => self.maybe_supported_error( - GenericConstantTooComplexSub::AddressAndDerefNotSupported(node.span), - )?, - ExprKind::Repeat { .. } | ExprKind::Array { .. } => self.maybe_supported_error( - GenericConstantTooComplexSub::ArrayNotSupported(node.span), - )?, - ExprKind::NeverToAny { .. } => self.maybe_supported_error( - GenericConstantTooComplexSub::NeverToAnyNotSupported(node.span), - )?, - ExprKind::Tuple { .. } => self.maybe_supported_error( - GenericConstantTooComplexSub::TupleNotSupported(node.span), - )?, - ExprKind::Index { .. } => self.maybe_supported_error( - GenericConstantTooComplexSub::IndexNotSupported(node.span), - )?, - ExprKind::Field { .. } => self.maybe_supported_error( - GenericConstantTooComplexSub::FieldNotSupported(node.span), - )?, - ExprKind::ConstBlock { .. } => self.maybe_supported_error( - GenericConstantTooComplexSub::ConstBlockNotSupported(node.span), - )?, - ExprKind::Adt(_) => self - .maybe_supported_error(GenericConstantTooComplexSub::AdtNotSupported(node.span))?, - // dont know if this is correct - ExprKind::Pointer { .. } => { - self.error(GenericConstantTooComplexSub::PointerNotSupported(node.span))? - } - ExprKind::Yield { .. } => { - self.error(GenericConstantTooComplexSub::YieldNotSupported(node.span))? - } - ExprKind::Continue { .. } | ExprKind::Break { .. } | ExprKind::Loop { .. } => { - self.error(GenericConstantTooComplexSub::LoopNotSupported(node.span))? - } - ExprKind::Box { .. } => { - self.error(GenericConstantTooComplexSub::BoxNotSupported(node.span))? - } +impl<'a, 'tcx> visit::Visitor<'a, 'tcx> for IsThirPolymorphic<'a, 'tcx> { + fn thir(&self) -> &'a thir::Thir<'tcx> { + &self.thir + } - ExprKind::Unary { .. } => unreachable!(), - // we handle valid unary/binary ops above - ExprKind::Binary { .. } => { - self.error(GenericConstantTooComplexSub::BinaryNotSupported(node.span))? - } - ExprKind::LogicalOp { .. } => { - self.error(GenericConstantTooComplexSub::LogicalOpNotSupported(node.span))? - } - ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => { - self.error(GenericConstantTooComplexSub::AssignNotSupported(node.span))? - } - ExprKind::Closure { .. } | ExprKind::Return { .. } => { - self.error(GenericConstantTooComplexSub::ClosureAndReturnNotSupported(node.span))? - } - // let expressions imply control flow - ExprKind::Match { .. } | ExprKind::If { .. } | ExprKind::Let { .. } => { - self.error(GenericConstantTooComplexSub::ControlFlowNotSupported(node.span))? - } - ExprKind::InlineAsm { .. } => { - self.error(GenericConstantTooComplexSub::InlineAsmNotSupported(node.span))? - } + #[instrument(skip(self), level = "debug")] + fn visit_expr(&mut self, expr: &thir::Expr<'tcx>) { + self.is_poly |= self.expr_is_poly(expr); + if !self.is_poly { + visit::walk_expr(self, expr) + } + } - // we dont permit let stmts so `VarRef` and `UpvarRef` cant happen - ExprKind::VarRef { .. } - | ExprKind::UpvarRef { .. } - | ExprKind::StaticRef { .. } - | ExprKind::ThreadLocalRef(_) => { - self.error(GenericConstantTooComplexSub::OperationNotSupported(node.span))? - } - }) + #[instrument(skip(self), level = "debug")] + fn visit_pat(&mut self, pat: &thir::Pat<'tcx>) { + self.is_poly |= self.pat_is_poly(pat); + if !self.is_poly { + visit::walk_pat(self, pat); + } } } @@ -403,7 +356,7 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { pub fn thir_abstract_const<'tcx>( tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam, -) -> Result]>, ErrorGuaranteed> { +) -> Result>, ErrorGuaranteed> { if tcx.features().generic_const_exprs { match tcx.def_kind(def.did) { // FIXME(generic_const_exprs): We currently only do this for anonymous constants, @@ -416,10 +369,17 @@ pub fn thir_abstract_const<'tcx>( } let body = tcx.thir_body(def)?; + let (body, body_id) = (&*body.0.borrow(), body.1); + + let mut is_poly_vis = IsThirPolymorphic { is_poly: false, thir: body }; + visit::walk_expr(&mut is_poly_vis, &body[body_id]); + if !is_poly_vis.is_poly { + return Ok(None); + } + + let root_span = body.exprs[body_id].span; - AbstractConstBuilder::new(tcx, (&*body.0.borrow(), body.1))? - .map(AbstractConstBuilder::build) - .transpose() + Some(recurse_build(tcx, body, body_id, root_span)).transpose() } else { Ok(None) } diff --git a/src/test/ui/const-generics/generic_const_exprs/assoc_const_unification/const_equate_assoc_consts.rs b/src/test/ui/const-generics/generic_const_exprs/assoc_const_unification/const_equate_assoc_consts.rs new file mode 100644 index 0000000000000..e8f89cb1aa2ca --- /dev/null +++ b/src/test/ui/const-generics/generic_const_exprs/assoc_const_unification/const_equate_assoc_consts.rs @@ -0,0 +1,27 @@ +// check-pass +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] + +trait Trait { + const ASSOC: usize; +} +impl Trait for T { + const ASSOC: usize = std::mem::size_of::(); +} + +struct Foo([u8; T::ASSOC]) +where + [(); T::ASSOC]:; + +fn bar() +where + [(); T::ASSOC]:, +{ + let _: Foo = Foo::<_>(make()); +} + +fn make() -> ! { + todo!() +} + +fn main() {} diff --git a/src/test/ui/const-generics/generic_const_exprs/assoc_const_unification/doesnt_unify_evaluatable.rs b/src/test/ui/const-generics/generic_const_exprs/assoc_const_unification/doesnt_unify_evaluatable.rs new file mode 100644 index 0000000000000..c8f7553da7982 --- /dev/null +++ b/src/test/ui/const-generics/generic_const_exprs/assoc_const_unification/doesnt_unify_evaluatable.rs @@ -0,0 +1,15 @@ +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] + +trait Trait { + const ASSOC: usize; +} + +fn foo() where [(); U::ASSOC]:, { + bar::<{ T::ASSOC }>(); + //~^ ERROR: unconstrained generic constant +} + +fn bar() {} + +fn main() {} diff --git a/src/test/ui/const-generics/generic_const_exprs/assoc_const_unification/doesnt_unify_evaluatable.stderr b/src/test/ui/const-generics/generic_const_exprs/assoc_const_unification/doesnt_unify_evaluatable.stderr new file mode 100644 index 0000000000000..e4a0cabe57284 --- /dev/null +++ b/src/test/ui/const-generics/generic_const_exprs/assoc_const_unification/doesnt_unify_evaluatable.stderr @@ -0,0 +1,10 @@ +error: unconstrained generic constant + --> $DIR/doesnt_unify_evaluatable.rs:9:11 + | +LL | bar::<{ T::ASSOC }>(); + | ^^^^^^^^^^^^ + | + = help: try adding a `where` bound using this expression: `where [(); { T::ASSOC }]:` + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/generic_const_exprs/assoc_const_unification/dropck_unifies_assoc_consts.rs b/src/test/ui/const-generics/generic_const_exprs/assoc_const_unification/dropck_unifies_assoc_consts.rs new file mode 100644 index 0000000000000..274caa1e99312 --- /dev/null +++ b/src/test/ui/const-generics/generic_const_exprs/assoc_const_unification/dropck_unifies_assoc_consts.rs @@ -0,0 +1,20 @@ +// check-pass +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] + +trait Trait { + const ASSOC: usize; +} + +struct Foo(T) +where + [(); T::ASSOC]:; + +impl Drop for Foo +where + [(); T::ASSOC]:, +{ + fn drop(&mut self) {} +} + +fn main() {} diff --git a/src/test/ui/const-generics/generic_const_exprs/assoc_const_unification/unifies_evaluatable.rs b/src/test/ui/const-generics/generic_const_exprs/assoc_const_unification/unifies_evaluatable.rs new file mode 100644 index 0000000000000..6597b9f2b3fe3 --- /dev/null +++ b/src/test/ui/const-generics/generic_const_exprs/assoc_const_unification/unifies_evaluatable.rs @@ -0,0 +1,18 @@ +// check-pass + +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] + +trait Trait { + const ASSOC: usize; +} + +fn foo() where [(); T::ASSOC]:, { + bar::<{ T::ASSOC }>(); +} + +fn bar() -> [(); N] { + [(); N] +} + +fn main() {} diff --git a/src/test/ui/const-generics/invariant.rs b/src/test/ui/const-generics/invariant.rs index ee191b65c2c76..39d658be67d40 100644 --- a/src/test/ui/const-generics/invariant.rs +++ b/src/test/ui/const-generics/invariant.rs @@ -24,7 +24,8 @@ where fn covariant( v: &'static Foo fn(&'a ())> ) -> &'static Foo { - v //~ ERROR mismatched types + v + //~^ ERROR mismatched types } fn main() { diff --git a/src/test/ui/const-generics/issues/issue-83765.stderr b/src/test/ui/const-generics/issues/issue-83765.stderr index 4becf3a364cf1..d7b2b006c2aea 100644 --- a/src/test/ui/const-generics/issues/issue-83765.stderr +++ b/src/test/ui/const-generics/issues/issue-83765.stderr @@ -1,15 +1,15 @@ -error[E0391]: cycle detected when resolving instance ` as TensorDimension>::DIM` +error[E0391]: cycle detected when resolving instance `::DIM, DIM> as TensorDimension>::DIM` --> $DIR/issue-83765.rs:5:5 | LL | const DIM: usize; | ^^^^^^^^^^^^^^^^ | -note: ...which requires computing candidate for ` as TensorDimension>`... +note: ...which requires computing candidate for `::DIM, DIM> as TensorDimension>`... --> $DIR/issue-83765.rs:4:1 | LL | trait TensorDimension { | ^^^^^^^^^^^^^^^^^^^^^ - = note: ...which again requires resolving instance ` as TensorDimension>::DIM`, completing the cycle + = note: ...which again requires resolving instance `::DIM, DIM> as TensorDimension>::DIM`, completing the cycle note: cycle used when computing candidate for ` as TensorDimension>` --> $DIR/issue-83765.rs:4:1 | diff --git a/src/test/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-85031-2.rs b/src/test/ui/const-generics/issues/issue-85031-2.rs similarity index 52% rename from src/test/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-85031-2.rs rename to src/test/ui/const-generics/issues/issue-85031-2.rs index db1e2fc2af481..4908fb29692cc 100644 --- a/src/test/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-85031-2.rs +++ b/src/test/ui/const-generics/issues/issue-85031-2.rs @@ -1,4 +1,8 @@ -// revisions: cfail +// check-pass +// known-bug + +// This should not compile, as the compiler should not know +// `A - 0` is satisfied `?x - 0` if `?x` is inferred to `A`. #![allow(incomplete_features)] #![feature(generic_const_exprs)] @@ -6,8 +10,8 @@ pub struct Ref<'a>(&'a i32); impl<'a> Ref<'a> { pub fn foo() -> [(); A - 0] { + //~^ WARN function cannot Self::foo() - //~^ error: type annotations needed } } diff --git a/src/test/ui/const-generics/issues/issue-85031-2.stderr b/src/test/ui/const-generics/issues/issue-85031-2.stderr new file mode 100644 index 0000000000000..fc69057687520 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-85031-2.stderr @@ -0,0 +1,14 @@ +warning: function cannot return without recursing + --> $DIR/issue-85031-2.rs:12:5 + | +LL | pub fn foo() -> [(); A - 0] { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing +LL | +LL | Self::foo() + | ----------- recursive call site + | + = help: a `loop` may express intention better if this is on purpose + = note: `#[warn(unconditional_recursion)]` on by default + +warning: 1 warning emitted +