diff options
| author | kadmin <julianknodt@gmail.com> | 2022-07-27 07:27:52 +0000 |
|---|---|---|
| committer | kadmin <julianknodt@gmail.com> | 2022-11-25 09:28:43 +0000 |
| commit | f9750c1554a355a3755a412581c57b230248f06d (patch) | |
| tree | f4c25eaeccc34e79cc0e711108592ca7d0fa1ce0 | |
| parent | 41e0363055ade59584cff667c79f64937e6ef3f9 (diff) | |
| download | rust-f9750c1554a355a3755a412581c57b230248f06d.tar.gz rust-f9750c1554a355a3755a412581c57b230248f06d.zip | |
Add empty ConstKind::Abstract
Initial pass at expr/abstract const/s Address comments Switch to using a list instead of &[ty::Const], rm `AbstractConst` Remove try_unify_abstract_consts Update comments Add edits Recurse more More edits Prevent equating associated consts Move failing test to ui Changes this test from incremental to ui, and mark it as failing and a known bug. Does not cause the compiler to ICE, so should be ok.
40 files changed, 837 insertions, 778 deletions
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<DecodeContext<'a, 'tcx>> for Symbol { } } -impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for &'tcx [ty::abstract_const::Node<'tcx>] { - fn decode(d: &mut DecodeContext<'a, 'tcx>) -> Self { - ty::codec::RefDecodable::decode(d) - } -} - impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> 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<DefIndex, LazyValue<mir::Body<'static>>>, promoted_mir: Table<DefIndex, LazyValue<IndexVec<mir::Promoted, mir::Body<'static>>>>, // FIXME(compiler-errors): Why isn't this a LazyArray? - thir_abstract_const: Table<DefIndex, LazyValue<&'static [ty::abstract_const::Node<'static>]>>, + thir_abstract_const: Table<DefIndex, LazyValue<ty::Const<'static>>>, impl_parent: Table<DefIndex, RawDefId>, impl_polarity: Table<DefIndex, ty::ImplPolarity>, constness: Table<DefIndex, hir::Constness>, 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<Option<&'tcx [ty::abstract_const::Node<'tcx>]>, ErrorGuaranteed> { + ) -> Result<Option<ty::Const<'tcx>>, 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<Option<&'tcx [ty::abstract_const::Node<'tcx>]>, ErrorGuaranteed> { + ) -> Result<Option<ty::Const<'tcx>>, 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<LocalDefId> ) -> &'tcx Steal<mir::Body<'tcx>> { 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<Option<AbstractConst<'tcx>>, ErrorGuaranteed> { - let inner = tcx.thir_abstract_const_opt_const_arg(uv.def)?; - debug!("AbstractConst::new({:?}) = {:?}", uv, inner); - Ok(inner.map(|inner| AbstractConst { inner, substs: tcx.erase_regions(uv.substs) })) - } - - pub fn from_const( - tcx: TyCtxt<'tcx>, - ct: ty::Const<'tcx>, - ) -> Result<Option<AbstractConst<'tcx>>, ErrorGuaranteed> { - match ct.kind() { - ty::ConstKind::Unevaluated(uv) => AbstractConst::new(tcx, uv), - ty::ConstKind::Error(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<Option<EarlyBinder<ty::Const<'tcx>>>, ErrorGuaranteed>; + impl<'tcx> TyCtxt<'tcx> { + /// Returns a const with substs applied by + pub fn bound_abstract_const( + self, + uv: ty::WithOptConstParam<DefId>, + ) -> 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<DefId>, - ) -> Result<Option<&'tcx [Node<'tcx>]>, ErrorGuaranteed> { + ) -> Result<Option<ty::Const<'tcx>>, 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<R> -where - F: FnMut(AbstractConst<'tcx>) -> ControlFlow<R>, -{ - #[instrument(skip(tcx, f), level = "debug")] - fn recurse<'tcx, R>( - tcx: TyCtxt<'tcx>, - ct: AbstractConst<'tcx>, - f: &mut dyn FnMut(AbstractConst<'tcx>) -> ControlFlow<R>, - ) -> ControlFlow<R> { - f(ct)?; - let root = ct.root(tcx); - debug!(?root); - match root { - Node::Leaf(_) => ControlFlow::CONTINUE, - Node::Binop(_, l, r) => { - recurse(tcx, ct.subtree(l), f)?; - recurse(tcx, ct.subtree(r), f) + pub fn expand_bound_abstract_const( + self, + ct: BoundAbstractConst<'tcx>, + substs: &[GenericArg<'tcx>], + ) -> Result<Option<Const<'tcx>>, ErrorGuaranteed> { + struct Expander<'tcx> { + tcx: TyCtxt<'tcx>, + } + impl<'tcx> FallibleTypeFolder<'tcx> for Expander<'tcx> { + type Error = ErrorGuaranteed; + 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 try_fold_const(&mut self, c: Const<'tcx>) -> Result<Const<'tcx>, 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) } } - Node::Cast(_, operand, _) => recurse(tcx, ct.subtree(operand), f), } - } - 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, + let Some(ac) = ct? else { + return Ok(None); + }; + let ac = ac.subst(self, substs); + Ok(Some(ac.try_fold_with(&mut Expander { tcx: self })?)) + } } 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 @@ -346,33 +346,22 @@ impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D> } impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> 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::<Vec<_>>(), - ) - } -} - -impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D> - for [ty::abstract_const::NodeId] + for ty::List<ty::BoundVariableKind> { fn decode(decoder: &mut D) -> &'tcx Self { - decoder.interner().arena.alloc_from_iter( - (0..decoder.read_usize()).map(|_| Decodable::decode(decoder)).collect::<Vec<_>>(), + let len = decoder.read_usize(); + decoder.interner().mk_bound_variable_kinds( + (0..len).map::<ty::BoundVariableKind, _>(|_| Decodable::decode(decoder)), ) } } -impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D> - for ty::List<ty::BoundVariableKind> -{ +impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D> for ty::List<ty::Const<'tcx>> { fn decode(decoder: &mut D) -> &'tcx Self { let len = decoder.read_usize(); - decoder.interner().mk_bound_variable_kinds( - (0..len).map::<ty::BoundVariableKind, _>(|_| Decodable::decode(decoder)), - ) + decoder + .interner() + .mk_const_list((0..len).map::<ty::Const<'tcx>, _>(|_| Decodable::decode(decoder))) } } 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,9 +72,24 @@ 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<Const<'tcx>>), + 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); impl<'tcx> ConstKind<'tcx> { 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<I: InternAs<[ty::Const<'tcx>], &'tcx List<ty::Const<'tcx>>>>( + 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<ty::Const<'tcx>> { + if cs.is_empty() { List::empty() } else { List::from_arena(self.arena, cs) } + } + pub fn intern_type_list(self, ts: &[Ty<'tcx>]) -> &'tcx List<Ty<'tcx>> { 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<ty::PolyExistentialPredicate<'t } } +impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List<ty::Const<'tcx>> { + fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, folder: &mut F) -> Result<Self, F::Error> { + ty::util::fold_list(self, folder, |tcx, v| tcx.mk_const_list(v.iter())) + } +} + impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List<ProjectionKind> { fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, folder: &mut F) -> Result<Self, F::Error> { 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::BreakTy> { 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<CacheDecoder<'a, 'tcx>> } } -impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for &'tcx [ty::abstract_const::Node<'tcx>] { - fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { - RefDecodable::decode(d) - } -} - impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> 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,152 +8,15 @@ //! 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_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<AbstractConst<'tcx>> { - 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); +use rustc_middle::traits::ObligationCause; +use rustc_middle::ty::abstract_const::NotConstEvaluatable; +use rustc_middle::ty::{self, TyCtxt, TypeVisitable, TypeVisitor}; - 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<const N: usize>() -> [u8; N + 1]` and `fn b<const M: usize>() -> [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`. -} +use rustc_span::Span; +use std::ops::ControlFlow; /// Check if a given constant can be evaluated. #[instrument(skip(infcx), level = "debug")] @@ -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<bool, NotConstEvaluatable> { 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>, - if let ControlFlow::Break(()) = result { - debug!("is_const_evaluatable: abstract_const ~~> ok"); - return Ok(true); + 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<Self::BreakTy> { + if c.ty() == self.ct.ty() + && let Ok(_nested_obligations) = self + .infcx + .at(&ObligationCause::dummy(), self.param_env) + .eq(c, self.ct) + { + //let obligations = nested_obligations.into_obligations(); + ControlFlow::BREAK + } else if let ty::ConstKind::Expr(e) = c.kind() { + e.visit_with(self) + } else { + ControlFlow::CONTINUE + } } } + + let mut v = Visitor { ct, infcx, param_env }; + let result = b_ct.visit_with(&mut v); + + 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,326 +77,302 @@ 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<NodeId, Node<'tcx>>, -} - -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<!, ErrorGuaranteed> { - 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<!, ErrorGuaranteed> { - 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<Option<AbstractConstBuilder<'a, 'tcx>>, 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<ty::Const<'tcx>, 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, + 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::<ty::Const<'tcx>>::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<const N: usize>(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, + )? } - - #[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( + tcx, + GenericConstantTooComplexSub::BorrowNotSupported(node.span), + root_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( + 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)) - } - - /// 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(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)? } - } - /// 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(tcx, GenericConstantTooComplexSub::OperationNotSupported(node.span), root_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<!, ErrorGuaranteed> { + 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<NodeId, ErrorGuaranteed> { - 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<!, ErrorGuaranteed> { + 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::<NodeId>::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<const N: usize>(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 +380,7 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { pub fn thir_abstract_const<'tcx>( tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam<LocalDefId>, -) -> Result<Option<&'tcx [Node<'tcx>]>, ErrorGuaranteed> { +) -> Result<Option<ty::Const<'tcx>>, 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); + + 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/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<for<'a> fn(&'a ())> ) -> &'static Foo<fn(&'static ())> { - 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 `<for<'a> fn(&'a ()) as SadBee>::ASSOC` | - = note: expected reference `&Foo<fn(&())>` - found reference `&Foo<for<'a> fn(&'a ())>` + = note: expected constant `<_ as SadBee>::ASSOC` + found constant `<for<'a> 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<T: 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 `<u8 as Foo>::N` | -help: consider giving this pattern a type - | -LL | let _: _ = foo([0; 1]); - | +++ + = note: expected constant `<_ as Foo>::N` + found constant `<u8 as Foo>::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<Self::Element> { + //~^ 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<Self::Element> { + //~^ 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 `<LazyUpdim<'_, T, { T::DIM }, DIM> 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 `<LazyUpdim<'_, T, { T::DIM }, DIM> as TensorDimension>`... - --> $DIR/issue-83765.rs:4:1 + = note: expected constant `Self::DIM` + found constant `DIM` + +error[E0308]: method not compatible with trait + --> $DIR/issue-83765.rs:57:5 + | +LL | fn bget(&self, index: [usize; DIM]) -> Option<Self::Element> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM` + | + = note: expected constant `Self::DIM` + found constant `DIM` + +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<Self::Element> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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<Self::Element>; + | ^^^^^^^^^ 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 `<LazyUpdim<'_, Vec<{integer}>, { Self::DIM }, 2> as TensorDimension>::DIM`, found `<LazyUpdim<'_, Vec<{integer}>, { Self::DIM }, 2> as TensorDimension>::DIM` + | + = note: expected constant `<LazyUpdim<'_, Vec<{integer}>, { Self::DIM }, 2> as TensorDimension>::DIM` + found constant `<LazyUpdim<'_, Vec<{integer}>, { Self::DIM }, 2> as TensorDimension>::DIM` + +error[E0308]: mismatched types + --> $DIR/issue-83765.rs:125:43 | -LL | trait TensorDimension { - | ^^^^^^^^^^^^^^^^^^^^^ - = note: ...which again requires resolving instance `<LazyUpdim<'_, T, { T::DIM }, DIM> as TensorDimension>::DIM`, completing the cycle -note: cycle used when computing candidate for `<LazyUpdim<'_, T, { T::DIM }, DIM> as TensorDimension>` - --> $DIR/issue-83765.rs:4:1 +LL | println!("The size of v is {:?}", bbv.bget([0, 2]).expect("Out of bounds.")); + | ^^^^ expected `<LazyUpdim<'_, Vec<{integer}>, { Self::DIM }, 2> as TensorDimension>::DIM`, found `<LazyUpdim<'_, Vec<{integer}>, { Self::DIM }, 2> as TensorDimension>::DIM` | -LL | trait TensorDimension { - | ^^^^^^^^^^^^^^^^^^^^^ + = note: expected constant `<LazyUpdim<'_, Vec<{integer}>, { Self::DIM }, 2> as TensorDimension>::DIM` + found constant `<LazyUpdim<'_, Vec<{integer}>, { Self::DIM }, 2> as TensorDimension>::DIM` -error: aborting due to previous error +error: aborting due to 12 previous errors -For more information about this error, try `rustc --explain E0391`. +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 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<const A: usize>() -> [(); 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<const A: usize>() -> [(); 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 + |
