diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 3eb2b3a0b1b..6619a40b085 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -561,6 +561,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ty::ConstKind::Param(_) | ty::ConstKind::Placeholder(..) => { throw_inval!(TooGeneric) } + ty::ConstKind::Expr(_) => throw_inval!(TooGeneric), ty::ConstKind::Error(reported) => { throw_inval!(AlreadyReported(reported)) } diff --git a/compiler/rustc_hir_analysis/src/check/dropck.rs b/compiler/rustc_hir_analysis/src/check/dropck.rs index e0b465bab16..2754a9a05bc 100644 --- a/compiler/rustc_hir_analysis/src/check/dropck.rs +++ b/compiler/rustc_hir_analysis/src/check/dropck.rs @@ -193,6 +193,18 @@ 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/freshen.rs b/compiler/rustc_infer/src/infer/freshen.rs index ff5d1a05a70..27a94ec5e30 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 2798477d181..06df1dc3a8e 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,31 @@ 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); + let substs_erased = tcx.erase_regions(unevaluated.substs); + let ac = tcx.expand_bound_abstract_const( + tcx.bound_abstract_const(unevaluated.def), + substs_erased, + ); match ac { Ok(None) => { - substs = InternalSubsts::identity_for_item(self.tcx, unevaluated.def.did); + substs = InternalSubsts::identity_for_item(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 { + 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)), } } - 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 +1651,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 ac4b5126190..3fae6694add 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 3e0b5fa6dd9..c51b8f96c71 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 2b3f2c02411..1cac656674d 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 fed943169df..f2030b91b9b 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 4c0974f86fb..0705b4cff53 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 d86bdbd63d8..36cdb50958c 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 e5bcd5fb27a..8a8c46d6f21 100644 --- a/compiler/rustc_middle/src/ty/abstract_const.rs +++ b/compiler/rustc_middle/src/ty/abstract_const.rs @@ -1,98 +1,10 @@ //! 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, FallibleTypeFolder, GenericArg, TyCtxt, TypeFoldable}; 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 +12,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 +29,75 @@ TrivialTypeTraversalAndLiftImpls! { NotConstEvaluatable, } +pub type BoundAbstractConst<'tcx> = Result>>, ErrorGuaranteed>; + 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> { + ) -> Result>, ErrorGuaranteed> { if let Some((did, param_did)) = def.as_const_arg() { self.thir_abstract_const_of_const_arg((did, param_did)) } else { self.thir_abstract_const(def.did) } } -} -#[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) - } - 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)) - } - Node::Cast(_, operand, _) => recurse(tcx, ct.subtree(operand), f), + pub fn expand_bound_abstract_const( + self, + ct: BoundAbstractConst<'tcx>, + substs: &[GenericArg<'tcx>], + ) -> Result>, ErrorGuaranteed> { + struct Expander<'tcx> { + tcx: TyCtxt<'tcx>, } + impl<'tcx> FallibleTypeFolder<'tcx> for Expander<'tcx> { + type Error = ErrorGuaranteed; + 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())); + } + }; + 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) } + } + } + + let Some(ac) = ct? else { + return Ok(None); + }; + let ac = ac.subst(self, substs); + Ok(Some(ac.try_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 b469eebfad9..b22b3961f34 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -345,26 +345,6 @@ 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] -{ - 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::List { @@ -376,6 +356,15 @@ impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> } } +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_const_list((0..len).map::, _>(|_| Decodable::decode(decoder))) + } +} + impl_decodable_via_ref! { &'tcx ty::TypeckResults<'tcx>, &'tcx ty::List>, diff --git a/compiler/rustc_middle/src/ty/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs index 321cba693d9..de63dae8a3d 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 b5327ad0cec..f627dc7ceb1 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -2714,6 +2714,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 { List::from_arena(self.arena, 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 1ee4985cf8d..c9c09c93a3e 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 ee4b8f91c54..bbd050d7cae 100644 --- a/compiler/rustc_middle/src/ty/flags.rs +++ b/compiler/rustc_middle/src/ty/flags.rs @@ -307,6 +307,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 9d778ff2fb6..9928340d921 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 d6025248081..b2bcf0e29cd 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 c54edf10c2d..249004cbc5c 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 3d47b71b7ce..4249decc88f 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 + ), ); } @@ -647,13 +650,21 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>( (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))) + 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), + ) && a.ty() == b.ty() { + return relation.consts(a, b); + } else { + false + } } // 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 +676,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(julianknodt): 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 64b4fd11762..7726ab8e5ab 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -593,6 +593,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 91db9698c41..4fab5abe909 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 1d9ae539b60..580a9eb091c 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}; @@ -286,17 +286,15 @@ 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 + 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)?; + } + ControlFlow::CONTINUE } } diff --git a/compiler/rustc_query_impl/src/on_disk_cache.rs b/compiler/rustc_query_impl/src/on_disk_cache.rs index eaed9aeb850..c61d2a9c2d0 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 d1a2aee207d..b6378af7ba0 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 db3ddc9208a..284ec6b5e3a 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -8,153 +8,16 @@ //! 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_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_middle::traits::ObligationCause; +use rustc_middle::ty::abstract_const::NotConstEvaluatable; +use rustc_middle::ty::{self, TyCtxt, TypeVisitable, TypeVisitor}; + 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, - } - } -} - -#[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)); - } - } - - 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`. -} - /// Check if a given constant can be evaluated. #[instrument(skip(infcx), level = "debug")] pub fn is_const_evaluatable<'tcx>( @@ -166,6 +29,8 @@ 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::Param(_) | ty::ConstKind::Bound(_, _) | ty::ConstKind::Placeholder(_) @@ -175,19 +40,17 @@ 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 substs = tcx.erase_regions(uv.substs); + if let Some(ct) = + tcx.expand_bound_abstract_const(tcx.bound_abstract_const(uv.def), substs)? + { + if satisfied_from_param_env(tcx, infcx, ct, param_env)? { 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)); @@ -212,13 +75,16 @@ 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. 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) => { + && 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) => { tcx.sess .struct_span_fatal( // Slightly better span than just using `span` alone @@ -253,33 +119,60 @@ pub fn is_const_evaluatable<'tcx>( } } -#[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 { 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 }; + let ty::ConstKind::Unevaluated(uv) = uv.kind() else { + continue + }; + let substs = tcx.erase_regions(uv.substs); + let Some(b_ct) = + tcx.expand_bound_abstract_const(tcx.bound_abstract_const(uv.def), substs)? else { + return Ok(false); + }; - // Try to unify with each subtree in the AbstractConst to allow for - // `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, + // 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 } - }); - - if let ControlFlow::Break(()) = result { - debug!("is_const_evaluatable: abstract_const ~~> ok"); - return Ok(true); } } + + 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 Ok(true); + } } _ => {} // don't care } diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index e7513255dc4..652bbeeeffb 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -3,6 +3,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::obligation_forest::ProcessResult; use rustc_data_structures::obligation_forest::{Error, ForestObligation, Outcome}; use rustc_data_structures::obligation_forest::{ObligationForest, ObligationProcessor}; +use rustc_hir::def::DefKind; use rustc_infer::traits::ProjectionCacheKey; use rustc_infer::traits::{SelectionError, TraitEngine, TraitObligation}; use rustc_middle::mir::interpret::ErrorHandled; @@ -452,8 +453,9 @@ 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"); @@ -461,12 +463,39 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, '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)) = - (c1.kind(), c2.kind()) - { - if infcx.try_unify_abstract_consts(a, b, obligation.param_env) { - return ProcessResult::Changed(vec![]); + match (c1.kind(), c2.kind()) { + (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) => { + if tcx.def_kind(a.def.did) == DefKind::AssocConst + || tcx.def_kind(b.def.did) == DefKind::AssocConst + { + // Two different constants using generic parameters ~> error. + let expected_found = ExpectedFound::new(true, c1, c2); + return ProcessResult::Error( + FulfillmentErrorCode::CodeConstEquateError( + expected_found, + TypeError::ConstMismatch(expected_found), + ), + ); + } + 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) = infcx + .at(&obligation.cause, obligation.param_env) + .eq(a, b) { + return ProcessResult::Changed(mk_pending( + new_obligations.into_obligations(), + )); + } } + _ => {} } let stalled_on = &mut pending_obligation.stalled_on; diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 548ca1c1d7f..4df53089890 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -929,10 +929,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 7c4c58ba361..bc8abc0eb90 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; @@ -843,15 +842,19 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<'tcx>>( // // 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 - } - }) + // + // We currently recurse into abstract consts here but do not recurse in + // `is_const_evaluatable`. This means that the object safety check is more + // liberal than the const eval check. + // + // 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) + { + 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 9fe13fe296a..da0a36731b9 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -657,8 +657,9 @@ 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"); @@ -670,9 +671,28 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { 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); - } + 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) = + self.infcx.at(&obligation.cause, obligation.param_env).eq(a, b) + { + 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(), + ); + } } let evaluate = |c: ty::Const<'tcx>| { diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 5e506a23f38..74f6850c2b8 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -476,6 +476,10 @@ impl<'tcx> WfPredicates<'tcx> { ty::Binder::dummy(ty::PredicateKind::WellFormed(ct.into())), )); } + // FIXME(julianknodt): need to infer any nested consts here + // so walk and search recursively? + ty::ConstKind::Expr(_) => unimplemented!(), + 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 cb41c4f94e2..d0705da971c 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,334 +77,310 @@ 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>, +/// 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, + } } -impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { - fn root_span(&self) -> Span { - self.body.exprs[self.body_id].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, } +} - 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) - } - - 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) - } - - #[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]; + 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, - } - } - - fn pat_is_poly(&mut self, pat: &thir::Pat<'tcx>) -> bool { - if pat.ty.has_non_region_param() { - return true; - } - - 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, + 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), - impl<'a, 'tcx> visit::Visitor<'a, 'tcx> for IsThirPolymorphic<'a, 'tcx> { - fn thir(&self) -> &'a thir::Thir<'tcx> { - &self.thir + ExprKind::Call { fun, args, .. } => { + let fun = recurse_build(tcx, body, *fun, root_span)?; + + let mut new_args = Vec::>::with_capacity(args.len()); + for &id in args.iter() { + new_args.push(recurse_build(tcx, body, id, root_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) - } - } - - #[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); - } + let new_args = tcx.mk_const_list(new_args.iter()); + tcx.mk_const(ConstKind::Expr(Expr::FunctionCall(fun, new_args)), node.ty) + } + &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( + tcx, + GenericConstantTooComplexSub::BlockNotSupported(node.span), + root_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 = 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]; - 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); + // 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( + tcx, + GenericConstantTooComplexSub::BorrowNotSupported(node.span), + root_span, + )? + } + } + // FIXME(generic_const_exprs): We may want to support these. + ExprKind::AddressOf { .. } | ExprKind::Deref { .. } => maybe_supported_error( + tcx, + GenericConstantTooComplexSub::AddressAndDerefNotSupported(node.span), + root_span, + )?, + ExprKind::Repeat { .. } | ExprKind::Array { .. } => maybe_supported_error( + tcx, + GenericConstantTooComplexSub::ArrayNotSupported(node.span), + root_span, + )?, + ExprKind::NeverToAny { .. } => maybe_supported_error( + tcx, + GenericConstantTooComplexSub::NeverToAnyNotSupported(node.span), + root_span, + )?, + ExprKind::Tuple { .. } => maybe_supported_error( + tcx, + GenericConstantTooComplexSub::TupleNotSupported(node.span), + root_span, + )?, + ExprKind::Index { .. } => maybe_supported_error( + tcx, + GenericConstantTooComplexSub::IndexNotSupported(node.span), + root_span, + )?, + ExprKind::Field { .. } => maybe_supported_error( + tcx, + GenericConstantTooComplexSub::FieldNotSupported(node.span), + root_span, + )?, + ExprKind::ConstBlock { .. } => maybe_supported_error( + tcx, + GenericConstantTooComplexSub::ConstBlockNotSupported(node.span), + root_span, + )?, + ExprKind::Adt(_) => maybe_supported_error( + tcx, + GenericConstantTooComplexSub::AdtNotSupported(node.span), + root_span, + )?, + // dont know if this is correct + ExprKind::Pointer { .. } => { + error(tcx, GenericConstantTooComplexSub::PointerNotSupported(node.span), root_span)? + } + ExprKind::Yield { .. } => { + error(tcx, GenericConstantTooComplexSub::YieldNotSupported(node.span), root_span)? + } + ExprKind::Continue { .. } | ExprKind::Break { .. } | ExprKind::Loop { .. } => { + error(tcx, GenericConstantTooComplexSub::LoopNotSupported(node.span), root_span)? + } + ExprKind::Box { .. } => { + error(tcx, GenericConstantTooComplexSub::BoxNotSupported(node.span), root_span)? } - Ok(Some(builder)) + ExprKind::Unary { .. } => unreachable!(), + // we handle valid unary/binary ops above + ExprKind::Binary { .. } => { + error(tcx, GenericConstantTooComplexSub::BinaryNotSupported(node.span), root_span)? + } + ExprKind::LogicalOp { .. } => { + error(tcx, GenericConstantTooComplexSub::LogicalOpNotSupported(node.span), root_span)? + } + ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => { + error(tcx, GenericConstantTooComplexSub::AssignNotSupported(node.span), root_span)? + } + ExprKind::Closure { .. } | ExprKind::Return { .. } => error( + tcx, + GenericConstantTooComplexSub::ClosureAndReturnNotSupported(node.span), + root_span, + )?, + // let expressions imply control flow + ExprKind::Match { .. } | ExprKind::If { .. } | ExprKind::Let { .. } => { + error(tcx, GenericConstantTooComplexSub::ControlFlowNotSupported(node.span), root_span)? + } + ExprKind::InlineAsm { .. } => { + error(tcx, GenericConstantTooComplexSub::InlineAsmNotSupported(node.span), root_span)? + } + + // we dont permit let stmts so `VarRef` and `UpvarRef` cant happen + ExprKind::VarRef { .. } + | ExprKind::UpvarRef { .. } + | ExprKind::StaticRef { .. } + | ExprKind::ThreadLocalRef(_) => { + error(tcx, GenericConstantTooComplexSub::OperationNotSupported(node.span), root_span)? + } + }) +} + +struct IsThirPolymorphic<'a, 'tcx> { + is_poly: bool, + thir: &'a thir::Thir<'tcx>, +} + +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 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) +} + +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; + } + + 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; + } + + 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, + } + } +} + +impl<'a, 'tcx> visit::Visitor<'a, 'tcx> for IsThirPolymorphic<'a, 'tcx> { + fn thir(&self) -> &'a thir::Thir<'tcx> { + &self.thir } - /// 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, + #[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) } } - /// 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(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); } } - - /// 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)?; - - Ok(self.tcx.arena.alloc_from_iter(self.nodes.into_iter())) - } - - 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); - - let constant = self.tcx.mk_const(ty::ConstKind::Unevaluated(uneval), node.ty); - - self.nodes.push(Node::Leaf(constant)) - } - - ExprKind::ConstParam { param, .. } => { - let const_param = self.tcx.mk_const(ty::ConstKind::Param(*param), node.ty); - self.nodes.push(Node::Leaf(const_param)) - } - - 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))? - } - - 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))? - } - - // 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))? - } - }) - } } /// Builds an abstract const, do not use this directly, but use `AbstractConst::new` instead. 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 +393,17 @@ pub fn thir_abstract_const<'tcx>( } let body = tcx.thir_body(def)?; + let (body, body_id) = (&*body.0.borrow(), body.1); - AbstractConstBuilder::new(tcx, (&*body.0.borrow(), body.1))? - .map(AbstractConstBuilder::build) - .transpose() + 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; + + Some(recurse_build(tcx, body, body_id, root_span)).transpose() } else { Ok(None) } diff --git a/src/test/ui/const-generics/invariant.rs b/src/test/ui/const-generics/invariant.rs index ee191b65c2c..f7ab8b83cb6 100644 --- a/src/test/ui/const-generics/invariant.rs +++ b/src/test/ui/const-generics/invariant.rs @@ -24,10 +24,10 @@ where fn covariant( v: &'static Foo fn(&'a ())> ) -> &'static Foo { - v //~ ERROR mismatched types + v } fn main() { - let y = covariant(&Foo([], PhantomData)); + let y = covariant(&Foo([], PhantomData)); //~ ERROR mismatched types println!("{:?}", y.0); } diff --git a/src/test/ui/const-generics/invariant.stderr b/src/test/ui/const-generics/invariant.stderr index aabe4c93b36..9f43d77c805 100644 --- a/src/test/ui/const-generics/invariant.stderr +++ b/src/test/ui/const-generics/invariant.stderr @@ -13,13 +13,13 @@ LL | impl SadBee for fn(&'static ()) { = note: `#[warn(coherence_leak_check)]` on by default error[E0308]: mismatched types - --> $DIR/invariant.rs:27:5 + --> $DIR/invariant.rs:31:28 | -LL | v - | ^ one type is more general than the other +LL | let y = covariant(&Foo([], PhantomData)); + | ^^ expected `<_ as SadBee>::ASSOC`, found ` fn(&'a ()) as SadBee>::ASSOC` | - = note: expected reference `&Foo` - found reference `&Foo fn(&'a ())>` + = note: expected constant `<_ as SadBee>::ASSOC` + found constant ` fn(&'a ()) as SadBee>::ASSOC` error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/const-generics/issues/issue-83249.rs b/src/test/ui/const-generics/issues/issue-83249.rs index 65148c55ee5..43dc94b9ced 100644 --- a/src/test/ui/const-generics/issues/issue-83249.rs +++ b/src/test/ui/const-generics/issues/issue-83249.rs @@ -15,9 +15,9 @@ fn foo(_: [u8; T::N]) -> T { pub fn bar() { let _: u8 = foo([0; 1]); + //~^ ERROR mismatched types let _ = foo([0; 1]); - //~^ ERROR type annotations needed } fn main() {} diff --git a/src/test/ui/const-generics/issues/issue-83249.stderr b/src/test/ui/const-generics/issues/issue-83249.stderr index 362b8554b2f..d55e1ea5900 100644 --- a/src/test/ui/const-generics/issues/issue-83249.stderr +++ b/src/test/ui/const-generics/issues/issue-83249.stderr @@ -1,14 +1,12 @@ -error[E0282]: type annotations needed - --> $DIR/issue-83249.rs:19:9 +error[E0308]: mismatched types + --> $DIR/issue-83249.rs:17:21 | -LL | let _ = foo([0; 1]); - | ^ +LL | let _: u8 = foo([0; 1]); + | ^^^^^^ expected `<_ as Foo>::N`, found `::N` | -help: consider giving this pattern a type - | -LL | let _: _ = foo([0; 1]); - | +++ + = note: expected constant `<_ as Foo>::N` + found constant `::N` error: aborting due to previous error -For more information about this error, try `rustc --explain E0282`. +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/const-generics/issues/issue-83765.rs b/src/test/ui/const-generics/issues/issue-83765.rs index 71c164ab0a5..79ca330526f 100644 --- a/src/test/ui/const-generics/issues/issue-83765.rs +++ b/src/test/ui/const-generics/issues/issue-83765.rs @@ -3,7 +3,6 @@ trait TensorDimension { const DIM: usize; - //~^ ERROR cycle detected when resolving instance // FIXME Given the current state of the compiler its expected that we cycle here, // but the cycle is still wrong. const ISSCALAR: bool = Self::DIM == 0; @@ -48,6 +47,7 @@ impl<'a, T: Broadcastable, const DIM: usize> TensorDimension for LazyUpdim<'a, T impl<'a, T: Broadcastable, const DIM: usize> TensorSize for LazyUpdim<'a, T, { T::DIM }, DIM> { fn size(&self) -> [usize; DIM] { + //~^ ERROR method not compatible self.size } } @@ -55,12 +55,15 @@ impl<'a, T: Broadcastable, const DIM: usize> TensorSize for LazyUpdim<'a, T, { T impl<'a, T: Broadcastable, const DIM: usize> Broadcastable for LazyUpdim<'a, T, { T::DIM }, DIM> { type Element = T::Element; fn bget(&self, index: [usize; DIM]) -> Option { + //~^ ERROR method not compatible assert!(DIM >= T::DIM); if !self.inbounds(index) { + //~^ ERROR mismatched types return None; } let size = self.size(); let newindex: [usize; T::DIM] = Default::default(); + //~^ ERROR the trait bound self.reference.bget(newindex) } } @@ -79,7 +82,10 @@ impl<'a, R, T: Broadcastable, F: Fn(T::Element) -> R, const DIM: usize> TensorSi for BMap<'a, R, T, F, DIM> { fn size(&self) -> [usize; DIM] { + //~^ ERROR method not compatible self.reference.size() + //~^ ERROR unconstrained + //~| ERROR mismatched types } } @@ -88,7 +94,10 @@ impl<'a, R, T: Broadcastable, F: Fn(T::Element) -> R, const DIM: usize> Broadcas { type Element = R; fn bget(&self, index: [usize; DIM]) -> Option { + //~^ ERROR method not compatible self.reference.bget(index).map(&self.closure) + //~^ ERROR unconstrained generic constant + //~| ERROR mismatched types } } @@ -111,6 +120,8 @@ 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 4becf3a364c..c0e4ae66a89 100644 --- a/src/test/ui/const-generics/issues/issue-83765.stderr +++ b/src/test/ui/const-generics/issues/issue-83765.stderr @@ -1,21 +1,122 @@ -error[E0391]: cycle detected when resolving instance ` as TensorDimension>::DIM` - --> $DIR/issue-83765.rs:5:5 +error[E0308]: method not compatible with trait + --> $DIR/issue-83765.rs:49:5 | -LL | const DIM: usize; - | ^^^^^^^^^^^^^^^^ +LL | fn size(&self) -> [usize; DIM] { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM` | -note: ...which requires computing candidate for ` as TensorDimension>`... - --> $DIR/issue-83765.rs:4:1 - | -LL | trait TensorDimension { - | ^^^^^^^^^^^^^^^^^^^^^ - = note: ...which again requires resolving instance ` as TensorDimension>::DIM`, completing the cycle -note: cycle used when computing candidate for ` as TensorDimension>` - --> $DIR/issue-83765.rs:4:1 - | -LL | trait TensorDimension { - | ^^^^^^^^^^^^^^^^^^^^^ + = note: expected constant `Self::DIM` + found constant `DIM` -error: aborting due to previous error +error[E0308]: method not compatible with trait + --> $DIR/issue-83765.rs:57:5 + | +LL | fn bget(&self, index: [usize; DIM]) -> Option { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM` + | + = note: expected constant `Self::DIM` + found constant `DIM` -For more information about this error, try `rustc --explain E0391`. +error[E0308]: method not compatible with trait + --> $DIR/issue-83765.rs:84:5 + | +LL | fn size(&self) -> [usize; DIM] { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM` + | + = note: expected constant `Self::DIM` + found constant `DIM` + +error[E0308]: method not compatible with trait + --> $DIR/issue-83765.rs:96:5 + | +LL | fn bget(&self, index: [usize; DIM]) -> Option { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM` + | + = note: expected constant `Self::DIM` + found constant `DIM` + +error[E0308]: mismatched types + --> $DIR/issue-83765.rs:60:27 + | +LL | if !self.inbounds(index) { + | ^^^^^ expected `Self::DIM`, found `DIM` + | + = note: expected constant `Self::DIM` + found constant `DIM` + +error[E0277]: the trait bound `[usize; _]: Default` is not satisfied + --> $DIR/issue-83765.rs:65:41 + | +LL | let newindex: [usize; T::DIM] = Default::default(); + | ^^^^^^^^^^^^^^^^ the trait `Default` is not implemented for `[usize; _]` + | +help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement + | +LL | impl<'a, T: Broadcastable, const DIM: usize> Broadcastable for LazyUpdim<'a, T, { T::DIM }, DIM> where [usize; _]: Default { + | +++++++++++++++++++++++++ + +error: unconstrained generic constant + --> $DIR/issue-83765.rs:86:24 + | +LL | self.reference.size() + | ^^^^ + | + = help: try adding a `where` bound using this expression: `where [(); Self::DIM]:` +note: required by a bound in `TensorSize::size` + --> $DIR/issue-83765.rs:15:31 + | +LL | fn size(&self) -> [usize; Self::DIM]; + | ^^^^^^^^^ required by this bound in `TensorSize::size` + +error[E0308]: mismatched types + --> $DIR/issue-83765.rs:86:9 + | +LL | self.reference.size() + | ^^^^^^^^^^^^^^^^^^^^^ expected `DIM`, found `Self::DIM` + | + = note: expected constant `DIM` + found constant `Self::DIM` + +error: unconstrained generic constant + --> $DIR/issue-83765.rs:98:24 + | +LL | self.reference.bget(index).map(&self.closure) + | ^^^^ + | + = help: try adding a `where` bound using this expression: `where [(); Self::DIM]:` +note: required by a bound in `Broadcastable::bget` + --> $DIR/issue-83765.rs:23:35 + | +LL | fn bget(&self, index: [usize; Self::DIM]) -> Option; + | ^^^^^^^^^ required by this bound in `Broadcastable::bget` + +error[E0308]: mismatched types + --> $DIR/issue-83765.rs:98:29 + | +LL | self.reference.bget(index).map(&self.closure) + | ^^^^^ expected `Self::DIM`, found `DIM` + | + = 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 + +Some errors have detailed explanations: E0277, E0308. +For more information about an error, try `rustc --explain E0277`. 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 db1e2fc2af4..4908fb29692 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 00000000000..fc690576875 --- /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 +