From f9750c1554a355a3755a412581c57b230248f06d Mon Sep 17 00:00:00 2001 From: kadmin Date: Wed, 27 Jul 2022 07:27:52 +0000 Subject: [PATCH 01/16] 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. --- .../rustc_const_eval/src/interpret/operand.rs | 1 + .../rustc_hir_analysis/src/check/dropck.rs | 12 + compiler/rustc_infer/src/infer/freshen.rs | 1 + compiler/rustc_infer/src/infer/mod.rs | 48 +- compiler/rustc_metadata/src/rmeta/decoder.rs | 6 - compiler/rustc_metadata/src/rmeta/mod.rs | 2 +- compiler/rustc_middle/src/mir/pretty.rs | 1 + compiler/rustc_middle/src/mir/syntax.rs | 6 +- .../rustc_middle/src/mir/type_foldable.rs | 2 - compiler/rustc_middle/src/query/mod.rs | 13 +- .../rustc_middle/src/ty/abstract_const.rs | 207 ++----- compiler/rustc_middle/src/ty/codec.rs | 27 +- compiler/rustc_middle/src/ty/consts/kind.rs | 19 +- compiler/rustc_middle/src/ty/context.rs | 11 + compiler/rustc_middle/src/ty/fast_reject.rs | 9 +- compiler/rustc_middle/src/ty/flags.rs | 20 + compiler/rustc_middle/src/ty/mod.rs | 2 +- compiler/rustc_middle/src/ty/parameterized.rs | 2 - compiler/rustc_middle/src/ty/print/pretty.rs | 3 + compiler/rustc_middle/src/ty/relate.rs | 61 +- .../rustc_middle/src/ty/structural_impls.rs | 6 + compiler/rustc_middle/src/ty/walk.rs | 18 + compiler/rustc_privacy/src/lib.rs | 20 +- .../rustc_query_impl/src/on_disk_cache.rs | 6 - compiler/rustc_symbol_mangling/src/v0.rs | 1 + .../src/traits/const_evaluatable.rs | 235 ++----- .../src/traits/fulfill.rs | 41 +- .../rustc_trait_selection/src/traits/mod.rs | 4 - .../src/traits/object_safety.rs | 25 +- .../src/traits/select/mod.rs | 28 +- .../rustc_trait_selection/src/traits/wf.rs | 4 + compiler/rustc_ty_utils/src/consts.rs | 576 +++++++++--------- src/test/ui/const-generics/invariant.rs | 4 +- src/test/ui/const-generics/invariant.stderr | 10 +- .../ui/const-generics/issues/issue-83249.rs | 2 +- .../const-generics/issues/issue-83249.stderr | 16 +- .../ui/const-generics/issues/issue-83765.rs | 13 +- .../const-generics/issues/issue-83765.stderr | 131 +++- .../const-generics/issues}/issue-85031-2.rs | 8 +- .../issues/issue-85031-2.stderr | 14 + 40 files changed, 837 insertions(+), 778 deletions(-) rename src/test/{incremental/const-generics/try_unify_abstract_const_regression_tests => ui/const-generics/issues}/issue-85031-2.rs (52%) create mode 100644 src/test/ui/const-generics/issues/issue-85031-2.stderr diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 3eb2b3a0b1b63..6619a40b08567 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 e0b465bab16d9..2754a9a05bce0 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 ff5d1a05a7062..27a94ec5e30e1 100644 --- a/compiler/rustc_infer/src/infer/freshen.rs +++ b/compiler/rustc_infer/src/infer/freshen.rs @@ -248,6 +248,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> { ty::ConstKind::Param(_) | ty::ConstKind::Value(_) | ty::ConstKind::Unevaluated(..) + | ty::ConstKind::Expr(..) | ty::ConstKind::Error(_) => ct.super_fold_with(self), } } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 2798477d1815d..06df1dc3a8e1c 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 ac4b512619025..3fae6694add0a 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -644,12 +644,6 @@ impl<'a, 'tcx> Decodable> for Symbol { } } -impl<'a, 'tcx> Decodable> for &'tcx [ty::abstract_const::Node<'tcx>] { - fn decode(d: &mut DecodeContext<'a, 'tcx>) -> Self { - ty::codec::RefDecodable::decode(d) - } -} - impl<'a, 'tcx> Decodable> for &'tcx [(ty::Predicate<'tcx>, Span)] { fn decode(d: &mut DecodeContext<'a, 'tcx>) -> Self { ty::codec::RefDecodable::decode(d) diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 3e0b5fa6dd9ff..c51b8f96c7151 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -366,7 +366,7 @@ define_tables! { mir_for_ctfe: Table>>, promoted_mir: Table>>>, // FIXME(compiler-errors): Why isn't this a LazyArray? - thir_abstract_const: Table]>>, + thir_abstract_const: Table>>, impl_parent: Table, impl_polarity: Table, constness: Table, diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 2b3f2c0241189..1cac656674d69 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -476,6 +476,7 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> { // These variants shouldn't exist in the MIR. ty::ConstKind::Placeholder(_) | ty::ConstKind::Infer(_) + | ty::ConstKind::Expr(_) | ty::ConstKind::Bound(..) => bug!("unexpected MIR constant: {:?}", literal), }, ConstantKind::Unevaluated(uv, _) => { diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index fed943169dfb5..f2030b91b9b65 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1185,7 +1185,8 @@ pub enum NullOp { AlignOf, } -#[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(HashStable, TyEncodable, TyDecodable, TypeFoldable, TypeVisitable)] pub enum UnOp { /// The `!` operator for logical inversion Not, @@ -1193,7 +1194,8 @@ pub enum UnOp { Neg, } -#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, TyEncodable, TyDecodable, Hash, HashStable)] +#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash)] +#[derive(TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] pub enum BinOp { /// The `+` operator (addition) Add, diff --git a/compiler/rustc_middle/src/mir/type_foldable.rs b/compiler/rustc_middle/src/mir/type_foldable.rs index 4c0974f86fb35..0705b4cff53ad 100644 --- a/compiler/rustc_middle/src/mir/type_foldable.rs +++ b/compiler/rustc_middle/src/mir/type_foldable.rs @@ -16,9 +16,7 @@ TrivialTypeTraversalAndLiftImpls! { UserTypeAnnotationIndex, BorrowKind, CastKind, - BinOp, NullOp, - UnOp, hir::Movability, BasicBlock, SwitchTargets, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index d86bdbd63d88d..36cdb50958caa 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -400,7 +400,7 @@ rustc_queries! { /// Try to build an abstract representation of the given constant. query thir_abstract_const( key: DefId - ) -> Result]>, ErrorGuaranteed> { + ) -> Result>, ErrorGuaranteed> { desc { |tcx| "building an abstract representation for `{}`", tcx.def_path_str(key), } @@ -409,7 +409,7 @@ rustc_queries! { /// Try to build an abstract representation of the given constant. query thir_abstract_const_of_const_arg( key: (LocalDefId, DefId) - ) -> Result]>, ErrorGuaranteed> { + ) -> Result>, ErrorGuaranteed> { desc { |tcx| "building an abstract representation for the const argument `{}`", @@ -417,15 +417,6 @@ rustc_queries! { } } - query try_unify_abstract_consts(key: - ty::ParamEnvAnd<'tcx, (ty::UnevaluatedConst<'tcx>, ty::UnevaluatedConst<'tcx> - )>) -> bool { - desc { - |tcx| "trying to unify the generic constants `{}` and `{}`", - tcx.def_path_str(key.value.0.def.did), tcx.def_path_str(key.value.1.def.did) - } - } - query mir_drops_elaborated_and_const_checked( key: ty::WithOptConstParam ) -> &'tcx Steal> { diff --git a/compiler/rustc_middle/src/ty/abstract_const.rs b/compiler/rustc_middle/src/ty/abstract_const.rs index e5bcd5fb27aa7..8a8c46d6f216e 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) + 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 } - 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, 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 b469eebfad993..b22b3961f34ea 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -346,33 +346,22 @@ impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> } impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> - for [ty::abstract_const::Node<'tcx>] -{ - fn decode(decoder: &mut D) -> &'tcx Self { - decoder.interner().arena.alloc_from_iter( - (0..decoder.read_usize()).map(|_| Decodable::decode(decoder)).collect::>(), - ) - } -} - -impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> - for [ty::abstract_const::NodeId] + for ty::List { fn decode(decoder: &mut D) -> &'tcx Self { - decoder.interner().arena.alloc_from_iter( - (0..decoder.read_usize()).map(|_| Decodable::decode(decoder)).collect::>(), + let len = decoder.read_usize(); + decoder.interner().mk_bound_variable_kinds( + (0..len).map::(|_| Decodable::decode(decoder)), ) } } -impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> - for ty::List -{ +impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> for ty::List> { fn decode(decoder: &mut D) -> &'tcx Self { let len = decoder.read_usize(); - decoder.interner().mk_bound_variable_kinds( - (0..len).map::(|_| Decodable::decode(decoder)), - ) + decoder + .interner() + .mk_const_list((0..len).map::, _>(|_| Decodable::decode(decoder))) } } diff --git a/compiler/rustc_middle/src/ty/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs index 321cba693d9ee..de63dae8a3df6 100644 --- a/compiler/rustc_middle/src/ty/consts/kind.rs +++ b/compiler/rustc_middle/src/ty/consts/kind.rs @@ -1,10 +1,12 @@ use std::convert::TryInto; +use super::Const; use crate::mir; use crate::mir::interpret::{AllocId, ConstValue, Scalar}; +use crate::ty::abstract_const::CastKind; use crate::ty::subst::{InternalSubsts, SubstsRef}; use crate::ty::ParamEnv; -use crate::ty::{self, TyCtxt, TypeVisitable}; +use crate::ty::{self, List, Ty, TyCtxt, TypeVisitable}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::DefId; @@ -70,8 +72,23 @@ pub enum ConstKind<'tcx> { /// A placeholder for a const which could not be computed; this is /// propagated to avoid useless error messages. Error(ErrorGuaranteed), + + /// Expr which contains an expression which has partially evaluated items. + Expr(Expr<'tcx>), +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)] +#[derive(HashStable, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable)] +pub enum Expr<'tcx> { + Binop(mir::BinOp, Const<'tcx>, Const<'tcx>), + UnOp(mir::UnOp, Const<'tcx>), + FunctionCall(Const<'tcx>, &'tcx List>), + Cast(CastKind, Const<'tcx>, Ty<'tcx>), } +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +static_assert_size!(Expr<'_>, 24); + #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] static_assert_size!(ConstKind<'_>, 32); diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index b5327ad0cecc6..f627dc7ceb192 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 1ee4985cf8de0..c9c09c93a3e1c 100644 --- a/compiler/rustc_middle/src/ty/fast_reject.rs +++ b/compiler/rustc_middle/src/ty/fast_reject.rs @@ -356,7 +356,10 @@ impl DeepRejectCtxt { pub fn consts_may_unify(self, obligation_ct: ty::Const<'_>, impl_ct: ty::Const<'_>) -> bool { match impl_ct.kind() { - ty::ConstKind::Param(_) | ty::ConstKind::Unevaluated(_) | ty::ConstKind::Error(_) => { + ty::ConstKind::Expr(_) + | ty::ConstKind::Param(_) + | ty::ConstKind::Unevaluated(_) + | ty::ConstKind::Error(_) => { return true; } ty::ConstKind::Value(_) => {} @@ -374,7 +377,9 @@ impl DeepRejectCtxt { // As we don't necessarily eagerly evaluate constants, // they might unify with any value. - ty::ConstKind::Unevaluated(_) | ty::ConstKind::Error(_) => true, + ty::ConstKind::Expr(_) | ty::ConstKind::Unevaluated(_) | ty::ConstKind::Error(_) => { + true + } ty::ConstKind::Value(obl) => match k { ty::ConstKind::Value(imp) => obl == imp, _ => true, diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs index ee4b8f91c5487..bbd050d7cae28 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 9d778ff2fb6e3..9928340d92171 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -77,7 +77,7 @@ pub use self::closure::{ CAPTURE_STRUCT_LOCAL, }; pub use self::consts::{ - Const, ConstInt, ConstKind, ConstS, InferConst, ScalarInt, UnevaluatedConst, ValTree, + Const, ConstInt, ConstKind, ConstS, Expr, InferConst, ScalarInt, UnevaluatedConst, ValTree, }; pub use self::context::{ tls, CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs index d6025248081db..b2bcf0e29cd9d 100644 --- a/compiler/rustc_middle/src/ty/parameterized.rs +++ b/compiler/rustc_middle/src/ty/parameterized.rs @@ -4,7 +4,6 @@ use rustc_index::vec::{Idx, IndexVec}; use crate::middle::exported_symbols::ExportedSymbol; use crate::mir::Body; -use crate::ty::abstract_const::Node; use crate::ty::{ self, Const, FnSig, GeneratorDiagnosticData, GenericPredicates, Predicate, TraitRef, Ty, }; @@ -124,6 +123,5 @@ parameterized_over_tcx! { Predicate, GeneratorDiagnosticData, Body, - Node, ExportedSymbol, } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index c54edf10c2d8f..249004cbc5ca8 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1253,6 +1253,9 @@ pub trait PrettyPrinter<'tcx>: self.pretty_print_bound_var(debruijn, bound_var)? } ty::ConstKind::Placeholder(placeholder) => p!(write("Placeholder({:?})", placeholder)), + // FIXME(generic_const_exprs): + // write out some legible representation of an abstract const? + ty::ConstKind::Expr(_) => p!("[Const Expr]"), ty::ConstKind::Error(_) => p!("[const error]"), }; Ok(self) diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index 3d47b71b7ce62..4249decc88f46 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 64b4fd1176252..7726ab8e5abb2 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 91db9698c41b2..4fab5abe909d4 100644 --- a/compiler/rustc_middle/src/ty/walk.rs +++ b/compiler/rustc_middle/src/ty/walk.rs @@ -214,6 +214,24 @@ fn push_inner<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent: GenericArg<'tcx>) | ty::ConstKind::Value(_) | ty::ConstKind::Error(_) => {} + ty::ConstKind::Expr(expr) => match expr { + ty::Expr::UnOp(_, v) => push_inner(stack, v.into()), + ty::Expr::Binop(_, l, r) => { + push_inner(stack, r.into()); + push_inner(stack, l.into()) + } + ty::Expr::FunctionCall(func, args) => { + for a in args.iter().rev() { + push_inner(stack, a.into()); + } + push_inner(stack, func.into()); + } + ty::Expr::Cast(_, c, t) => { + push_inner(stack, t.into()); + push_inner(stack, c.into()); + } + }, + ty::ConstKind::Unevaluated(ct) => { stack.extend(ct.substs.iter().rev()); } diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 1d9ae539b60dd..580a9eb091c6c 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 eaed9aeb85020..c61d2a9c2d0c6 100644 --- a/compiler/rustc_query_impl/src/on_disk_cache.rs +++ b/compiler/rustc_query_impl/src/on_disk_cache.rs @@ -812,12 +812,6 @@ impl<'a, 'tcx> Decodable> } } -impl<'a, 'tcx> Decodable> for &'tcx [ty::abstract_const::Node<'tcx>] { - fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { - RefDecodable::decode(d) - } -} - impl<'a, 'tcx> Decodable> for &'tcx [(ty::Predicate<'tcx>, Span)] { fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { RefDecodable::decode(d) diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index d1a2aee207dcd..b6378af7ba09c 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -575,6 +575,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { // a path), even for it we still need to encode a placeholder, as // the path could refer back to e.g. an `impl` using the constant. ty::ConstKind::Unevaluated(_) + | ty::ConstKind::Expr(_) | ty::ConstKind::Param(_) | ty::ConstKind::Infer(_) | ty::ConstKind::Bound(..) diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index db3ddc9208ae2..284ec6b5e3a25 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> { - 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() -> [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`. -} +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 { 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 { + 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 e7513255dc401..652bbeeeffbed 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 548ca1c1d7faa..4df5308989057 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 7c4c58ba36167..bc8abc0eb9036 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 9fe13fe296a16..da0a36731b9d0 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 5e506a23f383f..74f6850c2b815 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 cb41c4f94e2e6..d0705da971c11 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>, -} - -impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { - fn root_span(&self) -> Span { - self.body.exprs[self.body_id].span - } - - fn error(&mut self, sub: GenericConstantTooComplexSub) -> Result { - let reported = self.tcx.sess.emit_err(GenericConstantTooComplex { - span: self.root_span(), - maybe_supported: None, - sub, - }); - - Err(reported) +/// We do not allow all binary operations in abstract consts, so filter disallowed ones. +fn check_binop(op: mir::BinOp) -> bool { + use mir::BinOp::*; + match op { + Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Shl | Shr | Eq | Lt | Le | Ne + | Ge | Gt => true, + Offset => false, } +} - fn maybe_supported_error( - &mut self, - sub: GenericConstantTooComplexSub, - ) -> Result { - let reported = self.tcx.sess.emit_err(GenericConstantTooComplex { - span: self.root_span(), - maybe_supported: Some(()), - sub, - }); - - Err(reported) +/// While we currently allow all unary operations, we still want to explicitly guard against +/// future changes here. +fn check_unop(op: mir::UnOp) -> bool { + use mir::UnOp::*; + match op { + Not | Neg => true, } +} - #[instrument(skip(tcx, body, body_id), level = "debug")] - pub fn new( - tcx: TyCtxt<'tcx>, - (body, body_id): (&'a thir::Thir<'tcx>, thir::ExprId), - ) -> Result>, ErrorGuaranteed> { - let builder = AbstractConstBuilder { tcx, body_id, body, nodes: IndexVec::new() }; - - struct IsThirPolymorphic<'a, 'tcx> { - is_poly: bool, - thir: &'a thir::Thir<'tcx>, +fn recurse_build<'tcx>( + tcx: TyCtxt<'tcx>, + body: &thir::Thir<'tcx>, + node: thir::ExprId, + root_span: Span, +) -> Result, ErrorGuaranteed> { + use thir::ExprKind; + let node = &body.exprs[node]; + Ok(match &node.kind { + // I dont know if handling of these 3 is correct + &ExprKind::Scope { value, .. } => recurse_build(tcx, body, value, root_span)?, + &ExprKind::PlaceTypeAscription { source, .. } + | &ExprKind::ValueTypeAscription { source, .. } => { + recurse_build(tcx, body, source, root_span)? } - - use crate::rustc_middle::thir::visit::Visitor; - use thir::visit; - - impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> { - fn expr_is_poly(&mut self, expr: &thir::Expr<'tcx>) -> bool { - if expr.ty.has_non_region_param() { - return true; + &ExprKind::Literal { lit, neg } => { + let sp = node.span; + match tcx.at(sp).lit_to_const(LitToConstInput { lit: &lit.node, ty: node.ty, neg }) { + Ok(c) => c, + Err(LitToConstError::Reported(guar)) => { + tcx.const_error_with_guaranteed(node.ty, guar) } - - match expr.kind { - thir::ExprKind::NamedConst { substs, .. } => substs.has_non_region_param(), - thir::ExprKind::ConstParam { .. } => true, - thir::ExprKind::Repeat { value, count } => { - self.visit_expr(&self.thir()[value]); - count.has_non_region_param() - } - _ => false, + Err(LitToConstError::TypeError) => { + bug!("encountered type error in lit_to_const") } } + } + &ExprKind::NonHirLiteral { lit, user_ty: _ } => { + let val = ty::ValTree::from_scalar_int(lit); + ty::Const::from_value(tcx, val, node.ty) + } + &ExprKind::ZstLiteral { user_ty: _ } => { + let val = ty::ValTree::zst(); + ty::Const::from_value(tcx, val, node.ty) + } + &ExprKind::NamedConst { def_id, substs, user_ty: _ } => { + let uneval = ty::UnevaluatedConst::new(ty::WithOptConstParam::unknown(def_id), substs); + tcx.mk_const(ty::ConstKind::Unevaluated(uneval), node.ty) + } + ExprKind::ConstParam { param, .. } => tcx.mk_const(ty::ConstKind::Param(*param), node.ty), - fn pat_is_poly(&mut self, pat: &thir::Pat<'tcx>) -> bool { - if pat.ty.has_non_region_param() { - return true; - } + ExprKind::Call { fun, args, .. } => { + let fun = recurse_build(tcx, body, *fun, root_span)?; - match pat.kind { - thir::PatKind::Constant { value } => value.has_non_region_param(), - thir::PatKind::Range(box thir::PatRange { lo, hi, .. }) => { - lo.has_non_region_param() || hi.has_non_region_param() - } - _ => false, - } + let mut new_args = Vec::>::with_capacity(args.len()); + for &id in args.iter() { + new_args.push(recurse_build(tcx, body, id, root_span)?); } + let new_args = tcx.mk_const_list(new_args.iter()); + tcx.mk_const(ConstKind::Expr(Expr::FunctionCall(fun, new_args)), node.ty) } - - impl<'a, 'tcx> visit::Visitor<'a, 'tcx> for IsThirPolymorphic<'a, 'tcx> { - fn thir(&self) -> &'a thir::Thir<'tcx> { - &self.thir - } - - #[instrument(skip(self), level = "debug")] - fn visit_expr(&mut self, expr: &thir::Expr<'tcx>) { - self.is_poly |= self.expr_is_poly(expr); - if !self.is_poly { - visit::walk_expr(self, expr) - } + &ExprKind::Binary { op, lhs, rhs } if check_binop(op) => { + let lhs = recurse_build(tcx, body, lhs, root_span)?; + let rhs = recurse_build(tcx, body, rhs, root_span)?; + tcx.mk_const(ConstKind::Expr(Expr::Binop(op, lhs, rhs)), node.ty) + } + &ExprKind::Unary { op, arg } if check_unop(op) => { + let arg = recurse_build(tcx, body, arg, root_span)?; + tcx.mk_const(ConstKind::Expr(Expr::UnOp(op, arg)), node.ty) + } + // This is necessary so that the following compiles: + // + // ``` + // fn foo(a: [(); N + 1]) { + // bar::<{ N + 1 }>(); + // } + // ``` + ExprKind::Block { block } => { + if let thir::Block { stmts: box [], expr: Some(e), .. } = &body.blocks[*block] { + recurse_build(tcx, body, *e, root_span)? + } else { + maybe_supported_error( + 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 { + let reported = tcx.sess.emit_err(GenericConstantTooComplex { + span: root_span, + maybe_supported: None, + sub, + }); + + Err(reported) +} - fn recurse_build(&mut self, node: thir::ExprId) -> Result { - use thir::ExprKind; - let node = &self.body.exprs[node]; - Ok(match &node.kind { - // I dont know if handling of these 3 is correct - &ExprKind::Scope { value, .. } => self.recurse_build(value)?, - &ExprKind::PlaceTypeAscription { source, .. } - | &ExprKind::ValueTypeAscription { source, .. } => self.recurse_build(source)?, - &ExprKind::Literal { lit, neg } => { - let sp = node.span; - let constant = match self.tcx.at(sp).lit_to_const(LitToConstInput { - lit: &lit.node, - ty: node.ty, - neg, - }) { - Ok(c) => c, - Err(LitToConstError::Reported(guar)) => { - self.tcx.const_error_with_guaranteed(node.ty, guar) - } - Err(LitToConstError::TypeError) => { - bug!("encountered type error in lit_to_const") - } - }; - - self.nodes.push(Node::Leaf(constant)) - } - &ExprKind::NonHirLiteral { lit, user_ty: _ } => { - let val = ty::ValTree::from_scalar_int(lit); - self.nodes.push(Node::Leaf(ty::Const::from_value(self.tcx, val, node.ty))) - } - &ExprKind::ZstLiteral { user_ty: _ } => { - let val = ty::ValTree::zst(); - self.nodes.push(Node::Leaf(ty::Const::from_value(self.tcx, val, node.ty))) - } - &ExprKind::NamedConst { def_id, substs, user_ty: _ } => { - let uneval = - ty::UnevaluatedConst::new(ty::WithOptConstParam::unknown(def_id), substs); +fn maybe_supported_error<'tcx>( + tcx: TyCtxt<'tcx>, + sub: GenericConstantTooComplexSub, + root_span: Span, +) -> Result { + let reported = tcx.sess.emit_err(GenericConstantTooComplex { + span: root_span, + maybe_supported: Some(()), + sub, + }); + + Err(reported) +} - let constant = self.tcx.mk_const(ty::ConstKind::Unevaluated(uneval), node.ty); +impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> { + fn expr_is_poly(&mut self, expr: &thir::Expr<'tcx>) -> bool { + if expr.ty.has_non_region_param() { + return true; + } - self.nodes.push(Node::Leaf(constant)) + match expr.kind { + thir::ExprKind::NamedConst { substs, .. } => substs.has_non_region_param(), + thir::ExprKind::ConstParam { .. } => true, + thir::ExprKind::Repeat { value, count } => { + self.visit_expr(&self.thir()[value]); + count.has_non_region_param() } + _ => false, + } + } + fn pat_is_poly(&mut self, pat: &thir::Pat<'tcx>) -> bool { + if pat.ty.has_non_region_param() { + return true; + } - ExprKind::ConstParam { param, .. } => { - let const_param = self.tcx.mk_const(ty::ConstKind::Param(*param), node.ty); - self.nodes.push(Node::Leaf(const_param)) + match pat.kind { + thir::PatKind::Constant { value } => value.has_non_region_param(), + thir::PatKind::Range(box thir::PatRange { lo, hi, .. }) => { + lo.has_non_region_param() || hi.has_non_region_param() } + _ => false, + } + } +} - ExprKind::Call { fun, args, .. } => { - let fun = self.recurse_build(*fun)?; - - let mut new_args = Vec::::with_capacity(args.len()); - for &id in args.iter() { - new_args.push(self.recurse_build(id)?); - } - let new_args = self.tcx.arena.alloc_slice(&new_args); - self.nodes.push(Node::FunctionCall(fun, new_args)) - } - &ExprKind::Binary { op, lhs, rhs } if Self::check_binop(op) => { - let lhs = self.recurse_build(lhs)?; - let rhs = self.recurse_build(rhs)?; - self.nodes.push(Node::Binop(op, lhs, rhs)) - } - &ExprKind::Unary { op, arg } if Self::check_unop(op) => { - let arg = self.recurse_build(arg)?; - self.nodes.push(Node::UnaryOp(op, arg)) - } - // This is necessary so that the following compiles: - // - // ``` - // fn foo(a: [(); N + 1]) { - // bar::<{ N + 1 }>(); - // } - // ``` - ExprKind::Block { block } => { - if let thir::Block { stmts: box [], expr: Some(e), .. } = &self.body.blocks[*block] - { - self.recurse_build(*e)? - } else { - self.maybe_supported_error(GenericConstantTooComplexSub::BlockNotSupported( - node.span, - ))? - } - } - // `ExprKind::Use` happens when a `hir::ExprKind::Cast` is a - // "coercion cast" i.e. using a coercion or is a no-op. - // This is important so that `N as usize as usize` doesnt unify with `N as usize`. (untested) - &ExprKind::Use { source } => { - let arg = self.recurse_build(source)?; - self.nodes.push(Node::Cast(CastKind::Use, arg, node.ty)) - } - &ExprKind::Cast { source } => { - let arg = self.recurse_build(source)?; - self.nodes.push(Node::Cast(CastKind::As, arg, node.ty)) - } - ExprKind::Borrow { arg, .. } => { - let arg_node = &self.body.exprs[*arg]; - - // Skip reborrows for now until we allow Deref/Borrow/AddressOf - // expressions. - // FIXME(generic_const_exprs): Verify/explain why this is sound - if let ExprKind::Deref { arg } = arg_node.kind { - self.recurse_build(arg)? - } else { - self.maybe_supported_error(GenericConstantTooComplexSub::BorrowNotSupported( - node.span, - ))? - } - } - // FIXME(generic_const_exprs): We may want to support these. - ExprKind::AddressOf { .. } | ExprKind::Deref { .. } => self.maybe_supported_error( - GenericConstantTooComplexSub::AddressAndDerefNotSupported(node.span), - )?, - ExprKind::Repeat { .. } | ExprKind::Array { .. } => self.maybe_supported_error( - GenericConstantTooComplexSub::ArrayNotSupported(node.span), - )?, - ExprKind::NeverToAny { .. } => self.maybe_supported_error( - GenericConstantTooComplexSub::NeverToAnyNotSupported(node.span), - )?, - ExprKind::Tuple { .. } => self.maybe_supported_error( - GenericConstantTooComplexSub::TupleNotSupported(node.span), - )?, - ExprKind::Index { .. } => self.maybe_supported_error( - GenericConstantTooComplexSub::IndexNotSupported(node.span), - )?, - ExprKind::Field { .. } => self.maybe_supported_error( - GenericConstantTooComplexSub::FieldNotSupported(node.span), - )?, - ExprKind::ConstBlock { .. } => self.maybe_supported_error( - GenericConstantTooComplexSub::ConstBlockNotSupported(node.span), - )?, - ExprKind::Adt(_) => self - .maybe_supported_error(GenericConstantTooComplexSub::AdtNotSupported(node.span))?, - // dont know if this is correct - ExprKind::Pointer { .. } => { - self.error(GenericConstantTooComplexSub::PointerNotSupported(node.span))? - } - ExprKind::Yield { .. } => { - self.error(GenericConstantTooComplexSub::YieldNotSupported(node.span))? - } - ExprKind::Continue { .. } | ExprKind::Break { .. } | ExprKind::Loop { .. } => { - self.error(GenericConstantTooComplexSub::LoopNotSupported(node.span))? - } - ExprKind::Box { .. } => { - self.error(GenericConstantTooComplexSub::BoxNotSupported(node.span))? - } +impl<'a, 'tcx> visit::Visitor<'a, 'tcx> for IsThirPolymorphic<'a, 'tcx> { + fn thir(&self) -> &'a thir::Thir<'tcx> { + &self.thir + } - ExprKind::Unary { .. } => unreachable!(), - // we handle valid unary/binary ops above - ExprKind::Binary { .. } => { - self.error(GenericConstantTooComplexSub::BinaryNotSupported(node.span))? - } - ExprKind::LogicalOp { .. } => { - self.error(GenericConstantTooComplexSub::LogicalOpNotSupported(node.span))? - } - ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => { - self.error(GenericConstantTooComplexSub::AssignNotSupported(node.span))? - } - ExprKind::Closure { .. } | ExprKind::Return { .. } => { - self.error(GenericConstantTooComplexSub::ClosureAndReturnNotSupported(node.span))? - } - // let expressions imply control flow - ExprKind::Match { .. } | ExprKind::If { .. } | ExprKind::Let { .. } => { - self.error(GenericConstantTooComplexSub::ControlFlowNotSupported(node.span))? - } - ExprKind::InlineAsm { .. } => { - self.error(GenericConstantTooComplexSub::InlineAsmNotSupported(node.span))? - } + #[instrument(skip(self), level = "debug")] + fn visit_expr(&mut self, expr: &thir::Expr<'tcx>) { + self.is_poly |= self.expr_is_poly(expr); + if !self.is_poly { + visit::walk_expr(self, expr) + } + } - // we dont permit let stmts so `VarRef` and `UpvarRef` cant happen - ExprKind::VarRef { .. } - | ExprKind::UpvarRef { .. } - | ExprKind::StaticRef { .. } - | ExprKind::ThreadLocalRef(_) => { - self.error(GenericConstantTooComplexSub::OperationNotSupported(node.span))? - } - }) + #[instrument(skip(self), level = "debug")] + fn visit_pat(&mut self, pat: &thir::Pat<'tcx>) { + self.is_poly |= self.pat_is_poly(pat); + if !self.is_poly { + visit::walk_pat(self, pat); + } } } @@ -403,7 +380,7 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { pub fn thir_abstract_const<'tcx>( tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam, -) -> Result]>, ErrorGuaranteed> { +) -> Result>, ErrorGuaranteed> { if tcx.features().generic_const_exprs { match tcx.def_kind(def.did) { // FIXME(generic_const_exprs): We currently only do this for anonymous constants, @@ -416,10 +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 ee191b65c2c76..f7ab8b83cb6b6 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 aabe4c93b3624..9f43d77c8059f 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 65148c55ee541..43dc94b9ced01 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 362b8554b2fcb..d55e1ea5900b3 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 71c164ab0a5bf..79ca330526fd5 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 4becf3a364cf1..c0e4ae66a899d 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 + = 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 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 | 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 | 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` | -LL | trait TensorDimension { - | ^^^^^^^^^^^^^^^^^^^^^ + = note: expected constant `, { Self::DIM }, 2> as TensorDimension>::DIM` + found constant `, { 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 similarity index 52% rename from src/test/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-85031-2.rs rename to src/test/ui/const-generics/issues/issue-85031-2.rs index db1e2fc2af481..4908fb29692cc 100644 --- a/src/test/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-85031-2.rs +++ b/src/test/ui/const-generics/issues/issue-85031-2.rs @@ -1,4 +1,8 @@ -// revisions: cfail +// check-pass +// known-bug + +// This should not compile, as the compiler should not know +// `A - 0` is satisfied `?x - 0` if `?x` is inferred to `A`. #![allow(incomplete_features)] #![feature(generic_const_exprs)] @@ -6,8 +10,8 @@ pub struct Ref<'a>(&'a i32); impl<'a> Ref<'a> { pub fn foo() -> [(); A - 0] { + //~^ WARN function cannot Self::foo() - //~^ error: type annotations needed } } diff --git a/src/test/ui/const-generics/issues/issue-85031-2.stderr b/src/test/ui/const-generics/issues/issue-85031-2.stderr new file mode 100644 index 0000000000000..fc69057687520 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-85031-2.stderr @@ -0,0 +1,14 @@ +warning: function cannot return without recursing + --> $DIR/issue-85031-2.rs:12:5 + | +LL | pub fn foo() -> [(); A - 0] { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing +LL | +LL | Self::foo() + | ----------- recursive call site + | + = help: a `loop` may express intention better if this is on purpose + = note: `#[warn(unconditional_recursion)]` on by default + +warning: 1 warning emitted + From 5bb1a9febce13dbe7ece9cdb3248b52c1ac44cc5 Mon Sep 17 00:00:00 2001 From: kadmin Date: Tue, 25 Oct 2022 08:16:43 +0000 Subject: [PATCH 02/16] Add expand_abstract_const Adds the ability to directly expand a const to an expr without having to deal with intermediate steps. --- .../rustc_hir_analysis/src/check/dropck.rs | 12 -- compiler/rustc_infer/src/infer/mod.rs | 8 +- .../rustc_middle/src/ty/abstract_const.rs | 109 ++++++++++-------- compiler/rustc_middle/src/ty/relate.rs | 8 +- compiler/rustc_privacy/src/lib.rs | 8 +- .../src/traits/const_evaluatable.rs | 87 ++++++-------- .../src/traits/fulfill.rs | 14 +-- .../src/traits/object_safety.rs | 7 +- .../src/traits/select/mod.rs | 19 ++- .../ui/const-generics/issues/issue-83765.rs | 2 - .../const-generics/issues/issue-83765.stderr | 20 +--- 11 files changed, 122 insertions(+), 172 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/dropck.rs b/compiler/rustc_hir_analysis/src/check/dropck.rs index 2754a9a05bce0..e0b465bab16d9 100644 --- a/compiler/rustc_hir_analysis/src/check/dropck.rs +++ b/compiler/rustc_hir_analysis/src/check/dropck.rs @@ -193,18 +193,6 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( ty::PredicateKind::ConstEvaluatable(a), ty::PredicateKind::ConstEvaluatable(b), ) => relator.relate(predicate.rebind(a), predicate.rebind(b)).is_ok(), - /* - ) => { - if let (Ok(Some(a)), Ok(Some(b))) = ( - tcx.expand_bound_abstract_const(tcx.bound_abstract_const(a.def), a.substs), - tcx.expand_bound_abstract_const(tcx.bound_abstract_const(b.def), b.substs), - ) && a.ty() == b.ty() { - return relator.relate(a, b).is_ok(); - } else { - false - } - } - */ ( ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_a, lt_a)), ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_b, lt_b)), diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 06df1dc3a8e1c..8d01afa322e2e 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1621,15 +1621,11 @@ impl<'tcx> InferCtxt<'tcx> { // variables let tcx = self.tcx; if substs.has_non_region_infer() { - let substs_erased = tcx.erase_regions(unevaluated.substs); - let ac = tcx.expand_bound_abstract_const( - tcx.bound_abstract_const(unevaluated.def), - substs_erased, - ); + let ac = tcx.expand_unevaluated_abstract_const(unevaluated.def, unevaluated.substs); match ac { Ok(None) => { substs = InternalSubsts::identity_for_item(tcx, unevaluated.def.did); - param_env = self.tcx.param_env(unevaluated.def.did); + param_env = tcx.param_env(unevaluated.def.did); } Ok(Some(ct)) => { if ct.has_non_region_infer() || ct.has_non_region_param() { diff --git a/compiler/rustc_middle/src/ty/abstract_const.rs b/compiler/rustc_middle/src/ty/abstract_const.rs index 8a8c46d6f216e..7c1029e6604c7 100644 --- a/compiler/rustc_middle/src/ty/abstract_const.rs +++ b/compiler/rustc_middle/src/ty/abstract_const.rs @@ -1,5 +1,8 @@ //! A subset of a mir body used for const evaluatability checking. -use crate::ty::{self, Const, EarlyBinder, FallibleTypeFolder, GenericArg, TyCtxt, TypeFoldable}; +use crate::ty::{ + self, subst::SubstsRef, Const, EarlyBinder, FallibleTypeFolder, Ty, TyCtxt, TypeFoldable, + TypeSuperFoldable, TypeVisitable, +}; use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::DefId; @@ -33,71 +36,79 @@ pub type BoundAbstractConst<'tcx> = Result>>, impl<'tcx> TyCtxt<'tcx> { /// Returns a const with substs applied by - pub fn bound_abstract_const( + fn bound_abstract_const(self, uv: ty::WithOptConstParam) -> BoundAbstractConst<'tcx> { + let ac = if let Some((did, param_did)) = uv.as_const_arg() { + self.thir_abstract_const_of_const_arg((did, param_did)) + } else { + self.thir_abstract_const(uv.did) + }; + Ok(ac?.map(|ac| EarlyBinder(ac))) + } + + pub fn expand_abstract_consts>( self, - uv: ty::WithOptConstParam, - ) -> BoundAbstractConst<'tcx> { - self.thir_abstract_const_opt_const_arg(uv).map(|ac| ac.map(|ac| EarlyBinder(ac))) + ac: T, + ) -> Result, ErrorGuaranteed> { + self._expand_abstract_consts(ac, true) } - #[inline] - pub fn thir_abstract_const_opt_const_arg( + + pub fn expand_unevaluated_abstract_const( self, - def: ty::WithOptConstParam, + did: ty::WithOptConstParam, + substs: SubstsRef<'tcx>, ) -> 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) - } + let Some(ac) = self.bound_abstract_const(did)? else { + return Ok(None); + }; + let substs = self.erase_regions(substs); + let ac = ac.subst(self, substs); + self._expand_abstract_consts(ac, false) } - pub fn expand_bound_abstract_const( + fn _expand_abstract_consts>( self, - ct: BoundAbstractConst<'tcx>, - substs: &[GenericArg<'tcx>], - ) -> Result>, ErrorGuaranteed> { + ac: T, + first: bool, + ) -> Result, ErrorGuaranteed> { struct Expander<'tcx> { tcx: TyCtxt<'tcx>, + first: bool, } + impl<'tcx> FallibleTypeFolder<'tcx> for Expander<'tcx> { - type Error = ErrorGuaranteed; + type Error = Option; fn tcx(&self) -> TyCtxt<'tcx> { self.tcx } - fn try_fold_const(&mut self, c: Const<'tcx>) -> Result, ErrorGuaranteed> { - use ty::ConstKind::*; - let uv = match c.kind() { - Unevaluated(uv) => uv, - Param(..) | Infer(..) | Bound(..) | Placeholder(..) | Value(..) | Error(..) => { - return Ok(c); - } - Expr(e) => { - let new_expr = match e { - ty::Expr::Binop(op, l, r) => { - ty::Expr::Binop(op, l.try_fold_with(self)?, r.try_fold_with(self)?) - } - ty::Expr::UnOp(op, v) => ty::Expr::UnOp(op, v.try_fold_with(self)?), - ty::Expr::Cast(k, c, t) => { - ty::Expr::Cast(k, c.try_fold_with(self)?, t.try_fold_with(self)?) - } - ty::Expr::FunctionCall(func, args) => ty::Expr::FunctionCall( - func.try_fold_with(self)?, - args.try_fold_with(self)?, - ), - }; - return Ok(self.tcx().mk_const(ty::ConstKind::Expr(new_expr), c.ty())); + fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result, Self::Error> { + if ty.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) { + ty.try_super_fold_with(self) + } else { + Ok(ty) + } + } + fn try_fold_const(&mut self, c: Const<'tcx>) -> Result, Self::Error> { + let ct = match c.kind() { + ty::ConstKind::Unevaluated(uv) => { + if let Some(bac) = self.tcx.bound_abstract_const(uv.def)? { + let substs = self.tcx.erase_regions(uv.substs); + bac.subst(self.tcx, substs) + } else if self.first { + return Err(None); + } else { + c + } } + _ => c, }; - let bac = self.tcx.bound_abstract_const(uv.def); - let ac = self.tcx.expand_bound_abstract_const(bac, uv.substs); - if let Ok(Some(ac)) = ac { ac.try_fold_with(self) } else { Ok(c) } + self.first = false; + ct.try_super_fold_with(self) } } - - let Some(ac) = ct? else { - return Ok(None); - }; - let ac = ac.subst(self, substs); - Ok(Some(ac.try_fold_with(&mut Expander { tcx: self })?)) + match ac.try_fold_with(&mut Expander { tcx: self, first }) { + Ok(c) => Ok(Some(c)), + Err(None) => Ok(None), + Err(Some(e)) => Err(e), + } } } diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index 4249decc88f46..84eeb81f1dbe9 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -626,7 +626,7 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>( // an unnormalized (i.e. unevaluated) const in the param-env. // FIXME(generic_const_exprs): Once we always lazily unify unevaluated constants // these `eval` calls can be removed. - if !relation.tcx().features().generic_const_exprs { + if !tcx.features().generic_const_exprs { a = a.eval(tcx, relation.param_env()); b = b.eval(tcx, relation.param_env()); } @@ -647,12 +647,12 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>( (ty::ConstKind::Placeholder(p1), ty::ConstKind::Placeholder(p2)) => p1 == p2, (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => a_val == b_val, - (ty::ConstKind::Unevaluated(au), ty::ConstKind::Unevaluated(bu)) + (ty::ConstKind::Unevaluated(_au), ty::ConstKind::Unevaluated(_bu)) if tcx.features().generic_const_exprs => { if let (Ok(Some(a)), Ok(Some(b))) = ( - tcx.expand_bound_abstract_const(tcx.bound_abstract_const(au.def), au.substs), - tcx.expand_bound_abstract_const(tcx.bound_abstract_const(bu.def), bu.substs), + tcx.expand_abstract_consts(a), + tcx.expand_abstract_consts(b), ) && a.ty() == b.ty() { return relation.consts(a, b); } else { diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 580a9eb091c6c..f8e99006923e6 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -287,12 +287,8 @@ where self.visit_ty(c.ty())?; let tcx = self.def_id_visitor.tcx(); if let ty::ConstKind::Unevaluated(uv) = c.kind() && - let Ok(Some(ct)) = tcx.expand_bound_abstract_const(tcx.bound_abstract_const(uv.def), - uv.substs) { - ct.visit_with(self)?; - } - if let ty::ConstKind::Expr(e) = c.kind() { - e.visit_with(self)?; + let Ok(Some(ct)) = tcx.expand_unevaluated_abstract_const(uv.def, uv.substs) { + ct.super_visit_with(self)?; } ControlFlow::CONTINUE } diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 284ec6b5e3a25..bf7396d6113f1 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -29,8 +29,7 @@ pub fn is_const_evaluatable<'tcx>( let tcx = infcx.tcx; let uv = match ct.kind() { ty::ConstKind::Unevaluated(uv) => uv, - // should be recursivee fixes. - ty::ConstKind::Expr(..) => todo!(), + ty::ConstKind::Expr(_) => bug!("unexpected expr in `is_const_evaluatable: {ct:?}"), ty::ConstKind::Param(_) | ty::ConstKind::Bound(_, _) | ty::ConstKind::Placeholder(_) @@ -40,10 +39,7 @@ pub fn is_const_evaluatable<'tcx>( }; if tcx.features().generic_const_exprs { - let substs = tcx.erase_regions(uv.substs); - if let Some(ct) = - tcx.expand_bound_abstract_const(tcx.bound_abstract_const(uv.def), substs)? - { + if let Some(ct) = tcx.expand_abstract_consts(ct)? { if satisfied_from_param_env(tcx, infcx, ct, param_env)? { return Ok(()); } @@ -74,17 +70,13 @@ pub fn is_const_evaluatable<'tcx>( // // See #74595 for more details about this. let concrete = infcx.const_eval_resolve(param_env, uv, Some(span)); - - let substs = tcx.erase_regions(uv.substs); match concrete { - // If we're evaluating a foreign constant, under a nightly compiler without generic - // const exprs, AND it would've passed if that expression had been evaluated with - // generic const exprs, then suggest using generic const exprs. + // If we're evaluating a generic foreign constant, under a nightly compiler while + // the current crate does not enable `feature(generic_const_exprs)`, abort + // compilation with a useful error. Err(_) if tcx.sess.is_nightly_build() - && let Ok(Some(ct)) = - tcx.expand_bound_abstract_const(tcx.bound_abstract_const(uv.def), substs) - && let ty::ConstKind::Expr(_expr) = ct.kind() - && satisfied_from_param_env(tcx, infcx, ct, param_env) == Ok(true) => { + && let Ok(Some(ac)) = tcx.expand_abstract_consts(ct) + && let ty::ConstKind::Expr(_) = ac.kind() => { tcx.sess .struct_span_fatal( // Slightly better span than just using `span` alone @@ -126,46 +118,43 @@ fn satisfied_from_param_env<'tcx>( ct: ty::Const<'tcx>, param_env: ty::ParamEnv<'tcx>, ) -> Result { + // Try to unify with each subtree in the AbstractConst to allow for + // `N + 1` being const evaluatable even if theres only a `ConstEvaluatable` + // predicate for `(N + 1) * 2` + struct Visitor<'a, 'tcx> { + ct: ty::Const<'tcx>, + param_env: ty::ParamEnv<'tcx>, + + infcx: &'a InferCtxt<'tcx>, + } + impl<'a, 'tcx> TypeVisitor<'tcx> for Visitor<'a, 'tcx> { + type BreakTy = (); + fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow { + if c.ty() == self.ct.ty() + && let Ok(_nested_obligations) = self + .infcx + .at(&ObligationCause::dummy(), self.param_env) + .eq(c, self.ct) + { + ControlFlow::BREAK + } else if let ty::ConstKind::Expr(e) = c.kind() { + e.visit_with(self) + } else { + ControlFlow::CONTINUE + } + } + } + for pred in param_env.caller_bounds() { match pred.kind().skip_binder() { - ty::PredicateKind::ConstEvaluatable(uv) => { - let ty::ConstKind::Unevaluated(uv) = uv.kind() else { + ty::PredicateKind::ConstEvaluatable(ce) => { + let ty::ConstKind::Unevaluated(_) = ce.kind() else { continue }; - let substs = tcx.erase_regions(uv.substs); - let Some(b_ct) = - tcx.expand_bound_abstract_const(tcx.bound_abstract_const(uv.def), substs)? else { - return Ok(false); + let Some(b_ct) = tcx.expand_abstract_consts(ce)? else { + continue }; - // Try to unify with each subtree in the AbstractConst to allow for - // `N + 1` being const evaluatable even if theres only a `ConstEvaluatable` - // predicate for `(N + 1) * 2` - struct Visitor<'a, 'tcx> { - ct: ty::Const<'tcx>, - param_env: ty::ParamEnv<'tcx>, - - infcx: &'a InferCtxt<'tcx>, - } - impl<'a, 'tcx> TypeVisitor<'tcx> for Visitor<'a, 'tcx> { - type BreakTy = (); - fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow { - if c.ty() == self.ct.ty() - && let Ok(_nested_obligations) = self - .infcx - .at(&ObligationCause::dummy(), self.param_env) - .eq(c, self.ct) - { - //let obligations = nested_obligations.into_obligations(); - ControlFlow::BREAK - } else if let ty::ConstKind::Expr(e) = c.kind() { - e.visit_with(self) - } else { - ControlFlow::CONTINUE - } - } - } - let mut v = Visitor { ct, infcx, param_env }; let result = b_ct.visit_with(&mut v); diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 652bbeeeffbed..9f4423605ab89 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -478,14 +478,8 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { ); } if let (Ok(Some(a)), Ok(Some(b))) = ( - tcx.expand_bound_abstract_const( - tcx.bound_abstract_const(a.def), - a.substs, - ), - tcx.expand_bound_abstract_const( - tcx.bound_abstract_const(b.def), - b.substs, - ), + tcx.expand_abstract_consts(c1), + tcx.expand_abstract_consts(c2), ) && a.ty() == b.ty() && let Ok(new_obligations) = infcx .at(&obligation.cause, obligation.param_env) @@ -534,7 +528,9 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { .at(&obligation.cause, obligation.param_env) .eq(c1, c2) { - Ok(_) => ProcessResult::Changed(vec![]), + Ok(inf_ok) => { + ProcessResult::Changed(mk_pending(inf_ok.into_obligations())) + } Err(err) => ProcessResult::Error( FulfillmentErrorCode::CodeConstEquateError( ExpectedFound::new(true, c1, c2), diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index bc8abc0eb9036..0ea126fa9c99e 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -849,11 +849,8 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<'tcx>>( // // This shouldn't really matter though as we can't really use any // constants which are not considered const evaluatable. - if let ty::ConstKind::Unevaluated(uv) = ct.kind() && - let Ok(Some(ct)) = self - .tcx - .expand_bound_abstract_const(self.tcx.bound_abstract_const(uv.def), uv.substs) - { + if let ty::ConstKind::Unevaluated(_uv) = ct.kind() && + let Ok(Some(ct)) = self.tcx.expand_abstract_consts(ct){ self.visit_const(ct) } else { ct.super_visit_with(self) diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index da0a36731b9d0..98b227940ffa0 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -668,19 +668,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // if the constants depend on generic parameters. // // Let's just see where this breaks :shrug: - if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) = + if let (ty::ConstKind::Unevaluated(_), ty::ConstKind::Unevaluated(_)) = (c1.kind(), c2.kind()) { if let (Ok(Some(a)), Ok(Some(b))) = ( - tcx.expand_bound_abstract_const( - tcx.bound_abstract_const(a.def), - a.substs, - ), - tcx.expand_bound_abstract_const( - tcx.bound_abstract_const(b.def), - b.substs, - ), - ) && a.ty() == b.ty() && let Ok(new_obligations) = + tcx.expand_abstract_consts(c1), + tcx.expand_abstract_consts(c2), + ) && a.ty() == b.ty() && let Ok(new_obligations) = self.infcx.at(&obligation.cause, obligation.param_env).eq(a, b) { let mut obligations = new_obligations.obligations; @@ -718,7 +712,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .at(&obligation.cause, obligation.param_env) .eq(c1, c2) { - Ok(_) => Ok(EvaluatedToOk), + Ok(inf_ok) => self.evaluate_predicates_recursively( + previous_stack, + inf_ok.into_obligations(), + ), Err(_) => Ok(EvaluatedToErr), } } diff --git a/src/test/ui/const-generics/issues/issue-83765.rs b/src/test/ui/const-generics/issues/issue-83765.rs index 79ca330526fd5..674efa723cfac 100644 --- a/src/test/ui/const-generics/issues/issue-83765.rs +++ b/src/test/ui/const-generics/issues/issue-83765.rs @@ -120,8 +120,6 @@ fn main() { let v = vec![1, 2, 3]; let bv = v.lazy_updim([3, 4]); let bbv = bv.bmap(|x| x * x); - //~^ ERROR mismatched types println!("The size of v is {:?}", bbv.bget([0, 2]).expect("Out of bounds.")); - //~^ ERROR mismatched types } diff --git a/src/test/ui/const-generics/issues/issue-83765.stderr b/src/test/ui/const-generics/issues/issue-83765.stderr index c0e4ae66a899d..3cf1aab3a8bf3 100644 --- a/src/test/ui/const-generics/issues/issue-83765.stderr +++ b/src/test/ui/const-generics/issues/issue-83765.stderr @@ -98,25 +98,7 @@ LL | self.reference.bget(index).map(&self.closure) = note: expected constant `Self::DIM` found constant `DIM` -error[E0308]: mismatched types - --> $DIR/issue-83765.rs:122:15 - | -LL | let bbv = bv.bmap(|x| x * x); - | ^^^^^^^^^^^^^^^^^^ expected `, { Self::DIM }, 2> as TensorDimension>::DIM`, found `, { Self::DIM }, 2> as TensorDimension>::DIM` - | - = note: expected constant `, { Self::DIM }, 2> as TensorDimension>::DIM` - found constant `, { Self::DIM }, 2> as TensorDimension>::DIM` - -error[E0308]: mismatched types - --> $DIR/issue-83765.rs:125:43 - | -LL | println!("The size of v is {:?}", bbv.bget([0, 2]).expect("Out of bounds.")); - | ^^^^ expected `, { Self::DIM }, 2> as TensorDimension>::DIM`, found `, { Self::DIM }, 2> as TensorDimension>::DIM` - | - = note: expected constant `, { Self::DIM }, 2> as TensorDimension>::DIM` - found constant `, { Self::DIM }, 2> as TensorDimension>::DIM` - -error: aborting due to 12 previous errors +error: aborting due to 10 previous errors Some errors have detailed explanations: E0277, E0308. For more information about an error, try `rustc --explain E0277`. From 8c729bd0f3bc8726e50e05c0337d960ab4791a18 Mon Sep 17 00:00:00 2001 From: Boxy Date: Mon, 14 Nov 2022 18:20:53 +0000 Subject: [PATCH 03/16] handle nested obligations in `satisfied_from_param_env` --- .../src/traits/const_evaluatable.rs | 60 +++++++++++-------- .../src/traits/fulfill.rs | 24 ++++---- .../doesnt_unify_evaluatable.rs | 15 +++++ .../doesnt_unify_evaluatable.stderr | 10 ++++ .../unifies_evaluatable.rs | 18 ++++++ .../ui/const-generics/issues/issue-83765.rs | 2 + .../const-generics/issues/issue-83765.stderr | 42 ++++++++++--- 7 files changed, 126 insertions(+), 45 deletions(-) create mode 100644 src/test/ui/const-generics/generic_const_exprs/assoc_const_unification/doesnt_unify_evaluatable.rs create mode 100644 src/test/ui/const-generics/generic_const_exprs/assoc_const_unification/doesnt_unify_evaluatable.stderr create mode 100644 src/test/ui/const-generics/generic_const_exprs/assoc_const_unification/unifies_evaluatable.rs diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index bf7396d6113f1..5df1f85ec416e 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -18,6 +18,8 @@ use rustc_middle::ty::{self, TyCtxt, TypeVisitable, TypeVisitor}; use rustc_span::Span; use std::ops::ControlFlow; +use crate::traits::ObligationCtxt; + /// Check if a given constant can be evaluated. #[instrument(skip(infcx), level = "debug")] pub fn is_const_evaluatable<'tcx>( @@ -71,26 +73,27 @@ pub fn is_const_evaluatable<'tcx>( // See #74595 for more details about this. let concrete = infcx.const_eval_resolve(param_env, uv, Some(span)); match concrete { - // If we're evaluating a generic foreign constant, under a nightly compiler while - // the current crate does not enable `feature(generic_const_exprs)`, abort - // compilation with a useful error. - Err(_) if tcx.sess.is_nightly_build() - && let Ok(Some(ac)) = tcx.expand_abstract_consts(ct) - && let ty::ConstKind::Expr(_) = ac.kind() => { - tcx.sess - .struct_span_fatal( - // Slightly better span than just using `span` alone - if span == rustc_span::DUMMY_SP { tcx.def_span(uv.def.did) } else { span }, - "failed to evaluate generic const expression", - ) - .note("the crate this constant originates from uses `#![feature(generic_const_exprs)]`") - .span_suggestion_verbose( - rustc_span::DUMMY_SP, - "consider enabling this feature", - "#![feature(generic_const_exprs)]\n", - rustc_errors::Applicability::MaybeIncorrect, - ) - .emit() + // If we're evaluating a generic foreign constant, under a nightly compiler while + // the current crate does not enable `feature(generic_const_exprs)`, abort + // compilation with a useful error. + Err(_) if tcx.sess.is_nightly_build() + && let Ok(Some(ac)) = tcx.expand_abstract_consts(ct) + && let ty::ConstKind::Expr(_) = ac.kind() => + { + tcx.sess + .struct_span_fatal( + // Slightly better span than just using `span` alone + if span == rustc_span::DUMMY_SP { tcx.def_span(uv.def.did) } else { span }, + "failed to evaluate generic const expression", + ) + .note("the crate this constant originates from uses `#![feature(generic_const_exprs)]`") + .span_suggestion_verbose( + rustc_span::DUMMY_SP, + "consider enabling this feature", + "#![feature(generic_const_exprs)]\n", + rustc_errors::Applicability::MaybeIncorrect, + ) + .emit() } Err(ErrorHandled::TooGeneric) => { @@ -130,12 +133,17 @@ fn satisfied_from_param_env<'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) - { + if let Ok(()) = self.infcx.commit_if_ok(|_| { + let ocx = ObligationCtxt::new_in_snapshot(self.infcx); + if let Ok(()) = ocx.eq(&ObligationCause::dummy(), self.param_env, c.ty(), self.ct.ty()) + && let Ok(()) = ocx.eq(&ObligationCause::dummy(), self.param_env, c, self.ct) + && ocx.select_all_or_error().is_empty() + { + Ok(()) + } else { + Err(()) + } + }) { ControlFlow::BREAK } else if let ty::ConstKind::Expr(e) = c.kind() { e.visit_with(self) diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 9f4423605ab89..806e031a4bb65 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -3,7 +3,6 @@ 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; @@ -465,6 +464,8 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { // Let's just see where this breaks :shrug: match (c1.kind(), c2.kind()) { (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) => { + // FIXME: remove + use rustc_hir::def::DefKind; if tcx.def_kind(a.def.did) == DefKind::AssocConst || tcx.def_kind(b.def.did) == DefKind::AssocConst { @@ -477,16 +478,17 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { ), ); } - if let (Ok(Some(a)), Ok(Some(b))) = ( - tcx.expand_abstract_consts(c1), - tcx.expand_abstract_consts(c2), - ) && a.ty() == b.ty() && - let Ok(new_obligations) = infcx - .at(&obligation.cause, obligation.param_env) - .eq(a, b) { - return ProcessResult::Changed(mk_pending( - new_obligations.into_obligations(), - )); + + if let Ok(Some(a)) = tcx.expand_abstract_consts(c1) + && let Ok(Some(b)) = tcx.expand_abstract_consts(c2) + && 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(), + )); } } _ => {} diff --git a/src/test/ui/const-generics/generic_const_exprs/assoc_const_unification/doesnt_unify_evaluatable.rs b/src/test/ui/const-generics/generic_const_exprs/assoc_const_unification/doesnt_unify_evaluatable.rs new file mode 100644 index 0000000000000..c8f7553da7982 --- /dev/null +++ b/src/test/ui/const-generics/generic_const_exprs/assoc_const_unification/doesnt_unify_evaluatable.rs @@ -0,0 +1,15 @@ +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] + +trait Trait { + const ASSOC: usize; +} + +fn foo() where [(); U::ASSOC]:, { + bar::<{ T::ASSOC }>(); + //~^ ERROR: unconstrained generic constant +} + +fn bar() {} + +fn main() {} diff --git a/src/test/ui/const-generics/generic_const_exprs/assoc_const_unification/doesnt_unify_evaluatable.stderr b/src/test/ui/const-generics/generic_const_exprs/assoc_const_unification/doesnt_unify_evaluatable.stderr new file mode 100644 index 0000000000000..e4a0cabe57284 --- /dev/null +++ b/src/test/ui/const-generics/generic_const_exprs/assoc_const_unification/doesnt_unify_evaluatable.stderr @@ -0,0 +1,10 @@ +error: unconstrained generic constant + --> $DIR/doesnt_unify_evaluatable.rs:9:11 + | +LL | bar::<{ T::ASSOC }>(); + | ^^^^^^^^^^^^ + | + = help: try adding a `where` bound using this expression: `where [(); { T::ASSOC }]:` + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/generic_const_exprs/assoc_const_unification/unifies_evaluatable.rs b/src/test/ui/const-generics/generic_const_exprs/assoc_const_unification/unifies_evaluatable.rs new file mode 100644 index 0000000000000..6597b9f2b3fe3 --- /dev/null +++ b/src/test/ui/const-generics/generic_const_exprs/assoc_const_unification/unifies_evaluatable.rs @@ -0,0 +1,18 @@ +// check-pass + +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] + +trait Trait { + const ASSOC: usize; +} + +fn foo() where [(); T::ASSOC]:, { + bar::<{ T::ASSOC }>(); +} + +fn bar() -> [(); N] { + [(); N] +} + +fn main() {} diff --git a/src/test/ui/const-generics/issues/issue-83765.rs b/src/test/ui/const-generics/issues/issue-83765.rs index 674efa723cfac..cf59763675bda 100644 --- a/src/test/ui/const-generics/issues/issue-83765.rs +++ b/src/test/ui/const-generics/issues/issue-83765.rs @@ -59,9 +59,11 @@ impl<'a, T: Broadcastable, const DIM: usize> Broadcastable for LazyUpdim<'a, T, assert!(DIM >= T::DIM); if !self.inbounds(index) { //~^ ERROR mismatched types + //~^^ ERROR unconstrained generic constant return None; } let size = self.size(); + //~^ ERROR unconstrained generic constant let newindex: [usize; T::DIM] = Default::default(); //~^ ERROR the trait bound self.reference.bget(newindex) diff --git a/src/test/ui/const-generics/issues/issue-83765.stderr b/src/test/ui/const-generics/issues/issue-83765.stderr index 3cf1aab3a8bf3..f84fd0146357d 100644 --- a/src/test/ui/const-generics/issues/issue-83765.stderr +++ b/src/test/ui/const-generics/issues/issue-83765.stderr @@ -17,7 +17,7 @@ LL | fn bget(&self, index: [usize; DIM]) -> Option { found constant `DIM` error[E0308]: method not compatible with trait - --> $DIR/issue-83765.rs:84:5 + --> $DIR/issue-83765.rs:86:5 | LL | fn size(&self) -> [usize; DIM] { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM` @@ -26,7 +26,7 @@ LL | fn size(&self) -> [usize; DIM] { found constant `DIM` error[E0308]: method not compatible with trait - --> $DIR/issue-83765.rs:96:5 + --> $DIR/issue-83765.rs:98:5 | LL | fn bget(&self, index: [usize; DIM]) -> Option { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM` @@ -34,6 +34,19 @@ LL | fn bget(&self, index: [usize; DIM]) -> Option { = note: expected constant `Self::DIM` found constant `DIM` +error: unconstrained generic constant + --> $DIR/issue-83765.rs:60:18 + | +LL | if !self.inbounds(index) { + | ^^^^^^^^ + | + = help: try adding a `where` bound using this expression: `where [(); Self::DIM]:` +note: required by a bound in `TensorSize::inbounds` + --> $DIR/issue-83765.rs:16:39 + | +LL | fn inbounds(&self, index: [usize; Self::DIM]) -> bool { + | ^^^^^^^^^ required by this bound in `TensorSize::inbounds` + error[E0308]: mismatched types --> $DIR/issue-83765.rs:60:27 | @@ -43,8 +56,21 @@ LL | if !self.inbounds(index) { = note: expected constant `Self::DIM` found constant `DIM` +error: unconstrained generic constant + --> $DIR/issue-83765.rs:65:25 + | +LL | let size = self.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[E0277]: the trait bound `[usize; _]: Default` is not satisfied - --> $DIR/issue-83765.rs:65:41 + --> $DIR/issue-83765.rs:67:41 | LL | let newindex: [usize; T::DIM] = Default::default(); | ^^^^^^^^^^^^^^^^ the trait `Default` is not implemented for `[usize; _]` @@ -55,7 +81,7 @@ LL | impl<'a, T: Broadcastable, const DIM: usize> Broadcastable for LazyUpdim<'a | +++++++++++++++++++++++++ error: unconstrained generic constant - --> $DIR/issue-83765.rs:86:24 + --> $DIR/issue-83765.rs:88:24 | LL | self.reference.size() | ^^^^ @@ -68,7 +94,7 @@ LL | fn size(&self) -> [usize; Self::DIM]; | ^^^^^^^^^ required by this bound in `TensorSize::size` error[E0308]: mismatched types - --> $DIR/issue-83765.rs:86:9 + --> $DIR/issue-83765.rs:88:9 | LL | self.reference.size() | ^^^^^^^^^^^^^^^^^^^^^ expected `DIM`, found `Self::DIM` @@ -77,7 +103,7 @@ LL | self.reference.size() found constant `Self::DIM` error: unconstrained generic constant - --> $DIR/issue-83765.rs:98:24 + --> $DIR/issue-83765.rs:100:24 | LL | self.reference.bget(index).map(&self.closure) | ^^^^ @@ -90,7 +116,7 @@ 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 + --> $DIR/issue-83765.rs:100:29 | LL | self.reference.bget(index).map(&self.closure) | ^^^^^ expected `Self::DIM`, found `DIM` @@ -98,7 +124,7 @@ LL | self.reference.bget(index).map(&self.closure) = note: expected constant `Self::DIM` found constant `DIM` -error: aborting due to 10 previous errors +error: aborting due to 12 previous errors Some errors have detailed explanations: E0277, E0308. For more information about an error, try `rustc --explain E0277`. From d75cd5c05111256a3c0d82d0df727cb2b52680b7 Mon Sep 17 00:00:00 2001 From: Boxy Date: Tue, 15 Nov 2022 20:45:57 +0000 Subject: [PATCH 04/16] fmt --- .../src/traits/select/mod.rs | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 98b227940ffa0..6bf46bb9a1c8a 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -671,22 +671,24 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if let (ty::ConstKind::Unevaluated(_), ty::ConstKind::Unevaluated(_)) = (c1.kind(), c2.kind()) { - if let (Ok(Some(a)), Ok(Some(b))) = ( - tcx.expand_abstract_consts(c1), - tcx.expand_abstract_consts(c2), - ) && a.ty() == b.ty() && let Ok(new_obligations) = - self.infcx.at(&obligation.cause, obligation.param_env).eq(a, b) - { - let mut obligations = new_obligations.obligations; - self.add_depth( - obligations.iter_mut(), - obligation.recursion_depth, - ); - return self.evaluate_predicates_recursively( - previous_stack, - obligations.into_iter(), - ); - } + if let Ok(Some(a)) = tcx.expand_abstract_consts(c1) + && let Ok(Some(b)) = tcx.expand_abstract_consts(c2) + && a.ty() == b.ty() + && let Ok(new_obligations) = self + .infcx + .at(&obligation.cause, obligation.param_env) + .eq(a, b) + { + let mut obligations = new_obligations.obligations; + 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>| { From 0ae3c5c60964bdfb95a524e86d7804e97ca6ceb8 Mon Sep 17 00:00:00 2001 From: Boxy Date: Tue, 15 Nov 2022 22:53:30 +0000 Subject: [PATCH 05/16] handle assoc consts in fulfill `ConstEquate` --- .../src/traits/fulfill.rs | 50 ++++--- .../const_equate_assoc_consts.rs | 27 ++++ src/test/ui/const-generics/invariant.rs | 3 +- src/test/ui/const-generics/invariant.stderr | 10 +- .../ui/const-generics/issues/issue-83249.rs | 4 +- .../const-generics/issues/issue-83249.stderr | 16 +- .../ui/const-generics/issues/issue-83765.rs | 13 +- .../const-generics/issues/issue-83765.stderr | 139 ++---------------- 8 files changed, 88 insertions(+), 174 deletions(-) create mode 100644 src/test/ui/const-generics/generic_const_exprs/assoc_const_unification/const_equate_assoc_consts.rs diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 806e031a4bb65..1fd0926fec98e 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -457,41 +457,45 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { tcx.features().generic_const_exprs, "`ConstEquate` without a feature gate: {c1:?} {c2:?}", ); - debug!(?c1, ?c2, "equating consts"); // FIXME: we probably should only try to unify abstract constants // if the constants depend on generic parameters. // // Let's just see where this breaks :shrug: - match (c1.kind(), c2.kind()) { - (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) => { - // FIXME: remove - use rustc_hir::def::DefKind; - if tcx.def_kind(a.def.did) == DefKind::AssocConst - || tcx.def_kind(b.def.did) == DefKind::AssocConst + { + let c1 = + if let Ok(Some(a)) = tcx.expand_abstract_consts(c1) { a } else { c1 }; + let c2 = + if let Ok(Some(b)) = tcx.expand_abstract_consts(c2) { b } else { c2 }; + debug!("equating consts:\nc1= {:?}\nc2= {:?}", c1, c2); + + use rustc_hir::def::DefKind; + use ty::ConstKind::Unevaluated; + match (c1.kind(), c2.kind()) { + (Unevaluated(a), Unevaluated(b)) + if a.def.did == b.def.did + && tcx.def_kind(a.def.did) == DefKind::AssocConst => { - // 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)) = tcx.expand_abstract_consts(c1) - && let Ok(Some(b)) = tcx.expand_abstract_consts(c2) - && a.ty() == b.ty() - && let Ok(new_obligations) = infcx + if let Ok(new_obligations) = infcx .at(&obligation.cause, obligation.param_env) - .eq(a, b) + .trace(c1, c2) + .eq(a.substs, b.substs) { return ProcessResult::Changed(mk_pending( new_obligations.into_obligations(), )); } + } + (_, Unevaluated(_)) | (Unevaluated(_), _) => (), + (_, _) => { + if let Ok(new_obligations) = + infcx.at(&obligation.cause, obligation.param_env).eq(c1, c2) + { + return ProcessResult::Changed(mk_pending( + new_obligations.into_obligations(), + )); + } + } } - _ => {} } let stalled_on = &mut pending_obligation.stalled_on; diff --git a/src/test/ui/const-generics/generic_const_exprs/assoc_const_unification/const_equate_assoc_consts.rs b/src/test/ui/const-generics/generic_const_exprs/assoc_const_unification/const_equate_assoc_consts.rs new file mode 100644 index 0000000000000..e8f89cb1aa2ca --- /dev/null +++ b/src/test/ui/const-generics/generic_const_exprs/assoc_const_unification/const_equate_assoc_consts.rs @@ -0,0 +1,27 @@ +// check-pass +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] + +trait Trait { + const ASSOC: usize; +} +impl Trait for T { + const ASSOC: usize = std::mem::size_of::(); +} + +struct Foo([u8; T::ASSOC]) +where + [(); T::ASSOC]:; + +fn bar() +where + [(); T::ASSOC]:, +{ + let _: Foo = Foo::<_>(make()); +} + +fn make() -> ! { + todo!() +} + +fn main() {} diff --git a/src/test/ui/const-generics/invariant.rs b/src/test/ui/const-generics/invariant.rs index f7ab8b83cb6b6..39d658be67d40 100644 --- a/src/test/ui/const-generics/invariant.rs +++ b/src/test/ui/const-generics/invariant.rs @@ -25,9 +25,10 @@ fn covariant( v: &'static Foo fn(&'a ())> ) -> &'static Foo { v + //~^ ERROR mismatched types } fn main() { - let y = covariant(&Foo([], PhantomData)); //~ ERROR mismatched types + let y = covariant(&Foo([], PhantomData)); println!("{:?}", y.0); } diff --git a/src/test/ui/const-generics/invariant.stderr b/src/test/ui/const-generics/invariant.stderr index 9f43d77c8059f..aabe4c93b3624 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:31:28 + --> $DIR/invariant.rs:27:5 | -LL | let y = covariant(&Foo([], PhantomData)); - | ^^ expected `<_ as SadBee>::ASSOC`, found ` fn(&'a ()) as SadBee>::ASSOC` +LL | v + | ^ one type is more general than the other | - = note: expected constant `<_ as SadBee>::ASSOC` - found constant ` fn(&'a ()) as SadBee>::ASSOC` + = note: expected reference `&Foo` + found reference `&Foo fn(&'a ())>` 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 43dc94b9ced01..a16b28253b55b 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 d55e1ea5900b3..362b8554b2fcb 100644 --- a/src/test/ui/const-generics/issues/issue-83249.stderr +++ b/src/test/ui/const-generics/issues/issue-83249.stderr @@ -1,12 +1,14 @@ -error[E0308]: mismatched types - --> $DIR/issue-83249.rs:17:21 +error[E0282]: type annotations needed + --> $DIR/issue-83249.rs:19:9 | -LL | let _: u8 = foo([0; 1]); - | ^^^^^^ expected `<_ as Foo>::N`, found `::N` +LL | let _ = foo([0; 1]); + | ^ | - = note: expected constant `<_ as Foo>::N` - found constant `::N` +help: consider giving this pattern a type + | +LL | let _: _ = foo([0; 1]); + | +++ error: aborting due to previous error -For more information about this error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0282`. diff --git a/src/test/ui/const-generics/issues/issue-83765.rs b/src/test/ui/const-generics/issues/issue-83765.rs index cf59763675bda..71c164ab0a5bf 100644 --- a/src/test/ui/const-generics/issues/issue-83765.rs +++ b/src/test/ui/const-generics/issues/issue-83765.rs @@ -3,6 +3,7 @@ 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; @@ -47,7 +48,6 @@ 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,17 +55,12 @@ 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 - //~^^ ERROR unconstrained generic constant return None; } let size = self.size(); - //~^ ERROR unconstrained generic constant let newindex: [usize; T::DIM] = Default::default(); - //~^ ERROR the trait bound self.reference.bget(newindex) } } @@ -84,10 +79,7 @@ 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 } } @@ -96,10 +88,7 @@ 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 } } diff --git a/src/test/ui/const-generics/issues/issue-83765.stderr b/src/test/ui/const-generics/issues/issue-83765.stderr index f84fd0146357d..d7b2b006c2aea 100644 --- a/src/test/ui/const-generics/issues/issue-83765.stderr +++ b/src/test/ui/const-generics/issues/issue-83765.stderr @@ -1,130 +1,21 @@ -error[E0308]: method not compatible with trait - --> $DIR/issue-83765.rs:49:5 +error[E0391]: cycle detected when resolving instance `::DIM, DIM> as TensorDimension>::DIM` + --> $DIR/issue-83765.rs:5:5 | -LL | fn size(&self) -> [usize; DIM] { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM` +LL | const DIM: usize; + | ^^^^^^^^^^^^^^^^ | - = 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 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM` - | - = note: expected constant `Self::DIM` - found constant `DIM` - -error[E0308]: method not compatible with trait - --> $DIR/issue-83765.rs:86: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:98:5 - | -LL | fn bget(&self, index: [usize; DIM]) -> Option { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM` - | - = note: expected constant `Self::DIM` - found constant `DIM` - -error: unconstrained generic constant - --> $DIR/issue-83765.rs:60:18 - | -LL | if !self.inbounds(index) { - | ^^^^^^^^ - | - = help: try adding a `where` bound using this expression: `where [(); Self::DIM]:` -note: required by a bound in `TensorSize::inbounds` - --> $DIR/issue-83765.rs:16:39 - | -LL | fn inbounds(&self, index: [usize; Self::DIM]) -> bool { - | ^^^^^^^^^ required by this bound in `TensorSize::inbounds` - -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: unconstrained generic constant - --> $DIR/issue-83765.rs:65:25 - | -LL | let size = self.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[E0277]: the trait bound `[usize; _]: Default` is not satisfied - --> $DIR/issue-83765.rs:67: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:88: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:88: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:100: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:100:29 +note: ...which requires computing candidate for `::DIM, DIM> as TensorDimension>`... + --> $DIR/issue-83765.rs:4:1 | -LL | self.reference.bget(index).map(&self.closure) - | ^^^^^ expected `Self::DIM`, found `DIM` +LL | trait TensorDimension { + | ^^^^^^^^^^^^^^^^^^^^^ + = note: ...which again requires resolving instance `::DIM, DIM> as TensorDimension>::DIM`, completing the cycle +note: cycle used when computing candidate for ` as TensorDimension>` + --> $DIR/issue-83765.rs:4:1 | - = note: expected constant `Self::DIM` - found constant `DIM` +LL | trait TensorDimension { + | ^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 12 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0277, E0308. -For more information about an error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0391`. From fd271ffe28055c7a8a764ada1b508aa5a7bd1453 Mon Sep 17 00:00:00 2001 From: Boxy Date: Tue, 15 Nov 2022 23:11:30 +0000 Subject: [PATCH 06/16] also handle it in evaluate --- .../src/traits/select/mod.rs | 75 +++++++++++++------ 1 file changed, 51 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 6bf46bb9a1c8a..daa3f2775e069 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -662,32 +662,59 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { tcx.features().generic_const_exprs, "`ConstEquate` without a feature gate: {c1:?} {c2:?}", ); - debug!(?c1, ?c2, "evaluate_predicate_recursively: equating consts"); - // FIXME: we probably should only try to unify abstract constants - // if the constants depend on generic parameters. - // - // Let's just see where this breaks :shrug: - if let (ty::ConstKind::Unevaluated(_), ty::ConstKind::Unevaluated(_)) = - (c1.kind(), c2.kind()) { - if let Ok(Some(a)) = tcx.expand_abstract_consts(c1) - && let Ok(Some(b)) = tcx.expand_abstract_consts(c2) - && a.ty() == b.ty() - && let Ok(new_obligations) = self - .infcx - .at(&obligation.cause, obligation.param_env) - .eq(a, b) - { - let mut obligations = new_obligations.obligations; - self.add_depth( - obligations.iter_mut(), - obligation.recursion_depth, - ); - return self.evaluate_predicates_recursively( - previous_stack, - obligations.into_iter(), - ); + let c1 = + if let Ok(Some(a)) = tcx.expand_abstract_consts(c1) { a } else { c1 }; + let c2 = + if let Ok(Some(b)) = tcx.expand_abstract_consts(c2) { b } else { c2 }; + debug!( + "evalaute_predicate_recursively: equating consts:\nc1= {:?}\nc2= {:?}", + c1, c2 + ); + + use rustc_hir::def::DefKind; + use ty::ConstKind::Unevaluated; + match (c1.kind(), c2.kind()) { + (Unevaluated(a), Unevaluated(b)) + if a.def.did == b.def.did + && tcx.def_kind(a.def.did) == DefKind::AssocConst => + { + if let Ok(new_obligations) = self + .infcx + .at(&obligation.cause, obligation.param_env) + .trace(c1, c2) + .eq(a.substs, b.substs) + { + let mut obligations = new_obligations.obligations; + self.add_depth( + obligations.iter_mut(), + obligation.recursion_depth, + ); + return self.evaluate_predicates_recursively( + previous_stack, + obligations.into_iter(), + ); + } + } + (_, Unevaluated(_)) | (Unevaluated(_), _) => (), + (_, _) => { + if let Ok(new_obligations) = self + .infcx + .at(&obligation.cause, obligation.param_env) + .eq(c1, c2) + { + let mut obligations = new_obligations.obligations; + self.add_depth( + obligations.iter_mut(), + obligation.recursion_depth, + ); + return self.evaluate_predicates_recursively( + previous_stack, + obligations.into_iter(), + ); + } + } } } From f59b91e8a0108db4126b227e8bd92671e1251e17 Mon Sep 17 00:00:00 2001 From: Boxy Date: Tue, 15 Nov 2022 23:23:31 +0000 Subject: [PATCH 07/16] reduce duplicated argument logic --- compiler/rustc_ty_utils/src/consts.rs | 104 ++++++++++---------------- 1 file changed, 40 insertions(+), 64 deletions(-) diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index d0705da971c11..2b7018bc9c300 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -104,6 +104,10 @@ fn recurse_build<'tcx>( ) -> Result, ErrorGuaranteed> { use thir::ExprKind; let node = &body.exprs[node]; + + let maybe_supported_error = |a| maybe_supported_error(tcx, a, root_span); + let error = |a| error(tcx, a, root_span); + Ok(match &node.kind { // I dont know if handling of these 3 is correct &ExprKind::Scope { value, .. } => recurse_build(tcx, body, value, root_span)?, @@ -167,11 +171,7 @@ fn recurse_build<'tcx>( 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, - )? + maybe_supported_error(GenericConstantTooComplexSub::BlockNotSupported(node.span))? } } // `ExprKind::Use` happens when a `hir::ExprKind::Cast` is a @@ -194,90 +194,66 @@ fn recurse_build<'tcx>( 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, - )? + maybe_supported_error(GenericConstantTooComplexSub::BorrowNotSupported(node.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, )?, + ExprKind::Repeat { .. } | ExprKind::Array { .. } => { + maybe_supported_error(GenericConstantTooComplexSub::ArrayNotSupported(node.span))? + } + ExprKind::NeverToAny { .. } => { + maybe_supported_error(GenericConstantTooComplexSub::NeverToAnyNotSupported(node.span))? + } + ExprKind::Tuple { .. } => { + maybe_supported_error(GenericConstantTooComplexSub::TupleNotSupported(node.span))? + } + ExprKind::Index { .. } => { + maybe_supported_error(GenericConstantTooComplexSub::IndexNotSupported(node.span))? + } + ExprKind::Field { .. } => { + maybe_supported_error(GenericConstantTooComplexSub::FieldNotSupported(node.span))? + } + ExprKind::ConstBlock { .. } => { + maybe_supported_error(GenericConstantTooComplexSub::ConstBlockNotSupported(node.span))? + } + ExprKind::Adt(_) => { + maybe_supported_error(GenericConstantTooComplexSub::AdtNotSupported(node.span))? + } // dont know if this is correct ExprKind::Pointer { .. } => { - error(tcx, GenericConstantTooComplexSub::PointerNotSupported(node.span), root_span)? + error(GenericConstantTooComplexSub::PointerNotSupported(node.span))? } ExprKind::Yield { .. } => { - error(tcx, GenericConstantTooComplexSub::YieldNotSupported(node.span), root_span)? + error(GenericConstantTooComplexSub::YieldNotSupported(node.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)? + error(GenericConstantTooComplexSub::LoopNotSupported(node.span))? } + ExprKind::Box { .. } => error(GenericConstantTooComplexSub::BoxNotSupported(node.span))?, ExprKind::Unary { .. } => unreachable!(), // we handle valid unary/binary ops above ExprKind::Binary { .. } => { - error(tcx, GenericConstantTooComplexSub::BinaryNotSupported(node.span), root_span)? + error(GenericConstantTooComplexSub::BinaryNotSupported(node.span))? } ExprKind::LogicalOp { .. } => { - error(tcx, GenericConstantTooComplexSub::LogicalOpNotSupported(node.span), root_span)? + error(GenericConstantTooComplexSub::LogicalOpNotSupported(node.span))? } ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => { - error(tcx, GenericConstantTooComplexSub::AssignNotSupported(node.span), root_span)? + error(GenericConstantTooComplexSub::AssignNotSupported(node.span))? + } + ExprKind::Closure { .. } | ExprKind::Return { .. } => { + error(GenericConstantTooComplexSub::ClosureAndReturnNotSupported(node.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)? + error(GenericConstantTooComplexSub::ControlFlowNotSupported(node.span))? } ExprKind::InlineAsm { .. } => { - error(tcx, GenericConstantTooComplexSub::InlineAsmNotSupported(node.span), root_span)? + error(GenericConstantTooComplexSub::InlineAsmNotSupported(node.span))? } // we dont permit let stmts so `VarRef` and `UpvarRef` cant happen @@ -285,7 +261,7 @@ fn recurse_build<'tcx>( | ExprKind::UpvarRef { .. } | ExprKind::StaticRef { .. } | ExprKind::ThreadLocalRef(_) => { - error(tcx, GenericConstantTooComplexSub::OperationNotSupported(node.span), root_span)? + error(GenericConstantTooComplexSub::OperationNotSupported(node.span))? } }) } From 4833ce8673957a0a3a057bfa637298cee54ca7dd Mon Sep 17 00:00:00 2001 From: Boxy Date: Mon, 21 Nov 2022 04:32:07 +0000 Subject: [PATCH 08/16] fmt --- compiler/rustc_middle/src/ty/abstract_const.rs | 2 +- compiler/rustc_middle/src/ty/relate.rs | 4 ++-- compiler/rustc_privacy/src/lib.rs | 5 +++-- .../rustc_trait_selection/src/traits/const_evaluatable.rs | 2 +- src/test/ui/const-generics/issues/issue-83249.rs | 2 +- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_middle/src/ty/abstract_const.rs b/compiler/rustc_middle/src/ty/abstract_const.rs index 7c1029e6604c7..e297a01e90e7a 100644 --- a/compiler/rustc_middle/src/ty/abstract_const.rs +++ b/compiler/rustc_middle/src/ty/abstract_const.rs @@ -35,7 +35,7 @@ TrivialTypeTraversalAndLiftImpls! { pub type BoundAbstractConst<'tcx> = Result>>, ErrorGuaranteed>; impl<'tcx> TyCtxt<'tcx> { - /// Returns a const with substs applied by + /// Returns a const without substs applied fn bound_abstract_const(self, uv: ty::WithOptConstParam) -> BoundAbstractConst<'tcx> { let ac = if let Some((did, param_did)) = uv.as_const_arg() { self.thir_abstract_const_of_const_arg((did, param_did)) diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index 84eeb81f1dbe9..decd99de3c48e 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -647,7 +647,7 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>( (ty::ConstKind::Placeholder(p1), ty::ConstKind::Placeholder(p2)) => p1 == p2, (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => a_val == b_val, - (ty::ConstKind::Unevaluated(_au), ty::ConstKind::Unevaluated(_bu)) + (ty::ConstKind::Unevaluated(_), ty::ConstKind::Unevaluated(_)) if tcx.features().generic_const_exprs => { if let (Ok(Some(a)), Ok(Some(b))) = ( @@ -681,7 +681,7 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>( (ty::ConstKind::Expr(ae), ty::ConstKind::Expr(be)) => { let r = relation; - // FIXME(julianknodt): is it possible to relate two consts which are not identical + // FIXME(generic_const_exprs): is it possible to relate two consts which are not identical // exprs? Should we care about that? let expr = match (ae, be) { (Expr::Binop(a_op, al, ar), Expr::Binop(b_op, bl, br)) diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index f8e99006923e6..eebff421b813b 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -286,8 +286,9 @@ where fn visit_const(&mut self, c: Const<'tcx>) -> ControlFlow { self.visit_ty(c.ty())?; let tcx = self.def_id_visitor.tcx(); - if let ty::ConstKind::Unevaluated(uv) = c.kind() && - let Ok(Some(ct)) = tcx.expand_unevaluated_abstract_const(uv.def, uv.substs) { + if let ty::ConstKind::Unevaluated(uv) = c.kind() + && let Ok(Some(ct)) = tcx.expand_unevaluated_abstract_const(uv.def, uv.substs) + { ct.super_visit_with(self)?; } ControlFlow::CONTINUE diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 5df1f85ec416e..d72e5c7a11c4b 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -78,7 +78,7 @@ pub fn is_const_evaluatable<'tcx>( // compilation with a useful error. Err(_) if tcx.sess.is_nightly_build() && let Ok(Some(ac)) = tcx.expand_abstract_consts(ct) - && let ty::ConstKind::Expr(_) = ac.kind() => + && let ty::ConstKind::Expr(_) = ac.kind() => { tcx.sess .struct_span_fatal( diff --git a/src/test/ui/const-generics/issues/issue-83249.rs b/src/test/ui/const-generics/issues/issue-83249.rs index a16b28253b55b..65148c55ee541 100644 --- a/src/test/ui/const-generics/issues/issue-83249.rs +++ b/src/test/ui/const-generics/issues/issue-83249.rs @@ -15,7 +15,7 @@ fn foo(_: [u8; T::N]) -> T { pub fn bar() { let _: u8 = foo([0; 1]); - + let _ = foo([0; 1]); //~^ ERROR type annotations needed } From e58b932dafe9a3135bde39c229134d9ed2cd00d8 Mon Sep 17 00:00:00 2001 From: Boxy Date: Tue, 22 Nov 2022 12:20:05 +0000 Subject: [PATCH 09/16] add FIXME for things that I couldn't find ways to trigger --- .../rustc_trait_selection/src/traits/const_evaluatable.rs | 1 + compiler/rustc_trait_selection/src/traits/wf.rs | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index d72e5c7a11c4b..a2f82841e40f2 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -31,6 +31,7 @@ pub fn is_const_evaluatable<'tcx>( let tcx = infcx.tcx; let uv = match ct.kind() { ty::ConstKind::Unevaluated(uv) => uv, + // FIXME(generic_const_exprs): this seems wrong but I couldn't find a way to get this to trigger ty::ConstKind::Expr(_) => bug!("unexpected expr in `is_const_evaluatable: {ct:?}"), ty::ConstKind::Param(_) | ty::ConstKind::Bound(_, _) diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 74f6850c2b815..8becc476acf59 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -476,9 +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!(), + // FIXME(generic_const_exprs): This seems wrong but I could not find a way to get this to trigger + ty::ConstKind::Expr(_) => { + bug!("checking wfness of `ConstKind::Expr` is unsupported") + } ty::ConstKind::Error(_) | ty::ConstKind::Param(_) From 5a496aab03d2e3ba9ce1bb1bd0b791c83513d2cb Mon Sep 17 00:00:00 2001 From: Boxy Date: Tue, 22 Nov 2022 12:22:36 +0000 Subject: [PATCH 10/16] dont skip const evalautable of non unevaluateds --- .../rustc_trait_selection/src/traits/const_evaluatable.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index a2f82841e40f2..42d50b68c6dc7 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -157,12 +157,7 @@ fn satisfied_from_param_env<'tcx>( for pred in param_env.caller_bounds() { match pred.kind().skip_binder() { ty::PredicateKind::ConstEvaluatable(ce) => { - let ty::ConstKind::Unevaluated(_) = ce.kind() else { - continue - }; - let Some(b_ct) = tcx.expand_abstract_consts(ce)? else { - continue - }; + let b_ct = tcx.expand_abstract_consts(ce)?.unwrap_or(ce); let mut v = Visitor { ct, infcx, param_env }; let result = b_ct.visit_with(&mut v); From 95e1a39c048b922754b3bc0fc5d305259a56289f Mon Sep 17 00:00:00 2001 From: Boxy Date: Wed, 23 Nov 2022 09:25:01 +0000 Subject: [PATCH 11/16] correctly intern `List>` --- compiler/rustc_middle/src/ty/context.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index f627dc7ceb192..8cf33a5ca0bb9 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -137,6 +137,7 @@ pub struct CtxtInterners<'tcx> { // Specifically use a speedy hash algorithm for these hash sets, since // they're accessed quite often. type_: InternedSet<'tcx, WithStableHash>>, + const_lists: InternedSet<'tcx, List>>, substs: InternedSet<'tcx, InternalSubsts<'tcx>>, canonical_var_infos: InternedSet<'tcx, List>>, region: InternedSet<'tcx, RegionKind<'tcx>>, @@ -157,6 +158,7 @@ impl<'tcx> CtxtInterners<'tcx> { CtxtInterners { arena, type_: Default::default(), + const_lists: Default::default(), substs: Default::default(), region: Default::default(), poly_existential_predicates: Default::default(), @@ -2261,6 +2263,7 @@ macro_rules! slice_interners { } slice_interners!( + const_lists: _intern_const_list(Const<'tcx>), substs: _intern_substs(GenericArg<'tcx>), canonical_var_infos: _intern_canonical_var_infos(CanonicalVarInfo<'tcx>), poly_existential_predicates: @@ -2722,7 +2725,7 @@ impl<'tcx> TyCtxt<'tcx> { } 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) } + if cs.is_empty() { List::empty() } else { self._intern_const_list(cs) } } pub fn intern_type_list(self, ts: &[Ty<'tcx>]) -> &'tcx List> { From 430f7d16e669d0dc21e7c7545c72720a4f0acfaf Mon Sep 17 00:00:00 2001 From: Boxy Date: Thu, 24 Nov 2022 08:20:51 +0000 Subject: [PATCH 12/16] add FIXME's --- compiler/rustc_const_eval/src/interpret/operand.rs | 1 + compiler/rustc_middle/src/ty/relate.rs | 3 +++ .../rustc_trait_selection/src/traits/const_evaluatable.rs | 7 +++++++ 3 files changed, 11 insertions(+) diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 6619a40b08567..221e359d24ab8 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) } + // FIXME(generic_const_exprs): `ConstKind::Expr` should be able to be evaluated ty::ConstKind::Expr(_) => throw_inval!(TooGeneric), ty::ConstKind::Error(reported) => { throw_inval!(AlreadyReported(reported)) diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index decd99de3c48e..8b460bfe044f2 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -650,6 +650,9 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>( (ty::ConstKind::Unevaluated(_), ty::ConstKind::Unevaluated(_)) if tcx.features().generic_const_exprs => { + // FIXME(generic_const_exprs): this spurriously fails when relating two assoc consts + // i.e. `::ASSOC eq ::ASSOC` would return `false`. Wheras if + // both were behind an anon const that gets normalized away here it would succeed. if let (Ok(Some(a)), Ok(Some(b))) = ( tcx.expand_abstract_consts(a), tcx.expand_abstract_consts(b), diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 42d50b68c6dc7..b72bc42fa65e0 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -149,6 +149,13 @@ fn satisfied_from_param_env<'tcx>( } else if let ty::ConstKind::Expr(e) = c.kind() { e.visit_with(self) } else { + // FIXME(generic_const_exprs): This doesn't recurse into `>::ASSOC`'s substs. + // This is currently unobservable as `>::ASSOC` creates an anon const + // with its own `ConstEvaluatable` bound in the param env which we will visit separately. + // + // If we start allowing directly writing `ConstKind::Expr` without an intermediate anon const + // this will be incorrect. It might be worth investigating making `predicates_of` elaborate + // all of the `ConstEvaluatable` bounds rather than having a visitor here. ControlFlow::CONTINUE } } From 4085e94ece1ca462a89aa28e3d38a5c8742fd2e9 Mon Sep 17 00:00:00 2001 From: Boxy Date: Thu, 24 Nov 2022 08:36:28 +0000 Subject: [PATCH 13/16] `super_relate_consts` do not spurriously fail on assoc consts --- compiler/rustc_middle/src/ty/relate.rs | 25 +++++++------------ .../dropck_unifies_assoc_consts.rs | 20 +++++++++++++++ 2 files changed, 29 insertions(+), 16 deletions(-) create mode 100644 src/test/ui/const-generics/generic_const_exprs/assoc_const_unification/dropck_unifies_assoc_consts.rs diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index 8b460bfe044f2..a5580fa62ac4d 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -631,6 +631,15 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>( b = b.eval(tcx, relation.param_env()); } + if tcx.features().generic_const_exprs { + if let Ok(Some(a2)) = tcx.expand_abstract_consts(a) { + a = a2; + } + if let Ok(Some(b2)) = tcx.expand_abstract_consts(b) { + b = b2 + } + } + // Currently, the values that can be unified are primitive types, // and those that derive both `PartialEq` and `Eq`, corresponding // to structural-match types. @@ -647,22 +656,6 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>( (ty::ConstKind::Placeholder(p1), ty::ConstKind::Placeholder(p2)) => p1 == p2, (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => a_val == b_val, - (ty::ConstKind::Unevaluated(_), ty::ConstKind::Unevaluated(_)) - if tcx.features().generic_const_exprs => - { - // FIXME(generic_const_exprs): this spurriously fails when relating two assoc consts - // i.e. `::ASSOC eq ::ASSOC` would return `false`. Wheras if - // both were behind an anon const that gets normalized away here it would succeed. - if let (Ok(Some(a)), Ok(Some(b))) = ( - tcx.expand_abstract_consts(a), - tcx.expand_abstract_consts(b), - ) && 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. diff --git a/src/test/ui/const-generics/generic_const_exprs/assoc_const_unification/dropck_unifies_assoc_consts.rs b/src/test/ui/const-generics/generic_const_exprs/assoc_const_unification/dropck_unifies_assoc_consts.rs new file mode 100644 index 0000000000000..274caa1e99312 --- /dev/null +++ b/src/test/ui/const-generics/generic_const_exprs/assoc_const_unification/dropck_unifies_assoc_consts.rs @@ -0,0 +1,20 @@ +// check-pass +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] + +trait Trait { + const ASSOC: usize; +} + +struct Foo(T) +where + [(); T::ASSOC]:; + +impl Drop for Foo +where + [(); T::ASSOC]:, +{ + fn drop(&mut self) {} +} + +fn main() {} From 2ac5d91d63e0cd2da3f0535163638bd9fe3020b2 Mon Sep 17 00:00:00 2001 From: Boxy Date: Thu, 24 Nov 2022 11:09:15 +0000 Subject: [PATCH 14/16] Make `expand_abstract_consts` infallible --- compiler/rustc_infer/src/infer/mod.rs | 24 +++---- .../rustc_middle/src/ty/abstract_const.rs | 68 ++++++------------- compiler/rustc_middle/src/ty/relate.rs | 8 +-- compiler/rustc_privacy/src/lib.rs | 8 +-- .../src/traits/const_evaluatable.rs | 18 +++-- .../src/traits/fulfill.rs | 6 +- .../src/traits/object_safety.rs | 19 +----- .../src/traits/select/mod.rs | 6 +- 8 files changed, 52 insertions(+), 105 deletions(-) diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 8d01afa322e2e..67feb83faace6 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1621,20 +1621,18 @@ impl<'tcx> InferCtxt<'tcx> { // variables let tcx = self.tcx; if substs.has_non_region_infer() { - let ac = tcx.expand_unevaluated_abstract_const(unevaluated.def, unevaluated.substs); - match ac { - Ok(None) => { - substs = InternalSubsts::identity_for_item(tcx, unevaluated.def.did); - param_env = tcx.param_env(unevaluated.def.did); + if let Some(ct) = tcx.bound_abstract_const(unevaluated.def)? { + let ct = tcx.expand_abstract_consts(ct.subst(tcx, substs)); + if let Err(e) = ct.error_reported() { + return Err(ErrorHandled::Reported(e)); + } else if ct.has_non_region_infer() || ct.has_non_region_param() { + return Err(ErrorHandled::TooGeneric); + } else { + substs = replace_param_and_infer_substs_with_placeholder(tcx, substs); } - Ok(Some(ct)) => { - if ct.has_non_region_infer() || ct.has_non_region_param() { - return Err(ErrorHandled::TooGeneric); - } else { - substs = replace_param_and_infer_substs_with_placeholder(tcx, substs); - } - } - Err(guar) => return Err(ErrorHandled::Reported(guar)), + } else { + substs = InternalSubsts::identity_for_item(tcx, unevaluated.def.did); + param_env = tcx.param_env(unevaluated.def.did); } } diff --git a/compiler/rustc_middle/src/ty/abstract_const.rs b/compiler/rustc_middle/src/ty/abstract_const.rs index e297a01e90e7a..5de758ad9babd 100644 --- a/compiler/rustc_middle/src/ty/abstract_const.rs +++ b/compiler/rustc_middle/src/ty/abstract_const.rs @@ -1,7 +1,7 @@ //! A subset of a mir body used for const evaluatability checking. use crate::ty::{ - self, subst::SubstsRef, Const, EarlyBinder, FallibleTypeFolder, Ty, TyCtxt, TypeFoldable, - TypeSuperFoldable, TypeVisitable, + self, Const, EarlyBinder, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeVisitable, }; use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::DefId; @@ -36,7 +36,10 @@ pub type BoundAbstractConst<'tcx> = Result>>, impl<'tcx> TyCtxt<'tcx> { /// Returns a const without substs applied - fn bound_abstract_const(self, uv: ty::WithOptConstParam) -> BoundAbstractConst<'tcx> { + pub fn bound_abstract_const( + self, + uv: ty::WithOptConstParam, + ) -> BoundAbstractConst<'tcx> { let ac = if let Some((did, param_did)) = uv.as_const_arg() { self.thir_abstract_const_of_const_arg((did, param_did)) } else { @@ -45,70 +48,37 @@ impl<'tcx> TyCtxt<'tcx> { Ok(ac?.map(|ac| EarlyBinder(ac))) } - pub fn expand_abstract_consts>( - self, - ac: T, - ) -> Result, ErrorGuaranteed> { - self._expand_abstract_consts(ac, true) - } - - pub fn expand_unevaluated_abstract_const( - self, - did: ty::WithOptConstParam, - substs: SubstsRef<'tcx>, - ) -> Result>, ErrorGuaranteed> { - let Some(ac) = self.bound_abstract_const(did)? else { - return Ok(None); - }; - let substs = self.erase_regions(substs); - let ac = ac.subst(self, substs); - self._expand_abstract_consts(ac, false) - } - - fn _expand_abstract_consts>( - self, - ac: T, - first: bool, - ) -> Result, ErrorGuaranteed> { + pub fn expand_abstract_consts>(self, ac: T) -> T { struct Expander<'tcx> { tcx: TyCtxt<'tcx>, - first: bool, } - impl<'tcx> FallibleTypeFolder<'tcx> for Expander<'tcx> { - type Error = Option; + impl<'tcx> TypeFolder<'tcx> for Expander<'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.tcx } - fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result, Self::Error> { + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { if ty.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) { - ty.try_super_fold_with(self) + ty.super_fold_with(self) } else { - Ok(ty) + ty } } - fn try_fold_const(&mut self, c: Const<'tcx>) -> Result, Self::Error> { + fn fold_const(&mut self, c: Const<'tcx>) -> Const<'tcx> { let ct = match c.kind() { - ty::ConstKind::Unevaluated(uv) => { - if let Some(bac) = self.tcx.bound_abstract_const(uv.def)? { + ty::ConstKind::Unevaluated(uv) => match self.tcx.bound_abstract_const(uv.def) { + Err(e) => self.tcx.const_error_with_guaranteed(c.ty(), e), + Ok(Some(bac)) => { let substs = self.tcx.erase_regions(uv.substs); bac.subst(self.tcx, substs) - } else if self.first { - return Err(None); - } else { - c } - } + Ok(None) => c, + }, _ => c, }; - self.first = false; - ct.try_super_fold_with(self) + ct.super_fold_with(self) } } - match ac.try_fold_with(&mut Expander { tcx: self, first }) { - Ok(c) => Ok(Some(c)), - Err(None) => Ok(None), - Err(Some(e)) => Err(e), - } + ac.fold_with(&mut Expander { tcx: self }) } } diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index a5580fa62ac4d..e6340040e9c19 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -632,12 +632,8 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>( } if tcx.features().generic_const_exprs { - if let Ok(Some(a2)) = tcx.expand_abstract_consts(a) { - a = a2; - } - if let Ok(Some(b2)) = tcx.expand_abstract_consts(b) { - b = b2 - } + a = tcx.expand_abstract_consts(a); + b = tcx.expand_abstract_consts(b); } // Currently, the values that can be unified are primitive types, diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index eebff421b813b..3b41b9b830ed6 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -284,14 +284,8 @@ where } fn visit_const(&mut self, c: Const<'tcx>) -> ControlFlow { - self.visit_ty(c.ty())?; let tcx = self.def_id_visitor.tcx(); - if let ty::ConstKind::Unevaluated(uv) = c.kind() - && let Ok(Some(ct)) = tcx.expand_unevaluated_abstract_const(uv.def, uv.substs) - { - ct.super_visit_with(self)?; - } - ControlFlow::CONTINUE + tcx.expand_abstract_consts(c).super_visit_with(self) } } diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index b72bc42fa65e0..a26653b3184b1 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -8,6 +8,7 @@ //! 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_hir::def::DefKind; use rustc_infer::infer::InferCtxt; use rustc_middle::mir::interpret::ErrorHandled; @@ -42,7 +43,15 @@ pub fn is_const_evaluatable<'tcx>( }; if tcx.features().generic_const_exprs { - if let Some(ct) = tcx.expand_abstract_consts(ct)? { + let ct = tcx.expand_abstract_consts(ct); + + let is_anon_ct = if let ty::ConstKind::Unevaluated(uv) = ct.kind() { + tcx.def_kind(uv.def.did) == DefKind::AnonConst + } else { + false + }; + + if !is_anon_ct { if satisfied_from_param_env(tcx, infcx, ct, param_env)? { return Ok(()); } @@ -52,6 +61,7 @@ pub fn is_const_evaluatable<'tcx>( return Err(NotConstEvaluatable::MentionsParam); } } + let concrete = infcx.const_eval_resolve(param_env, uv, Some(span)); match concrete { Err(ErrorHandled::TooGeneric) => Err(NotConstEvaluatable::Error( @@ -78,8 +88,7 @@ pub fn is_const_evaluatable<'tcx>( // the current crate does not enable `feature(generic_const_exprs)`, abort // compilation with a useful error. Err(_) if tcx.sess.is_nightly_build() - && let Ok(Some(ac)) = tcx.expand_abstract_consts(ct) - && let ty::ConstKind::Expr(_) = ac.kind() => + && let ty::ConstKind::Expr(_) = tcx.expand_abstract_consts(ct).kind() => { tcx.sess .struct_span_fatal( @@ -164,8 +173,7 @@ fn satisfied_from_param_env<'tcx>( for pred in param_env.caller_bounds() { match pred.kind().skip_binder() { ty::PredicateKind::ConstEvaluatable(ce) => { - let b_ct = tcx.expand_abstract_consts(ce)?.unwrap_or(ce); - + let b_ct = tcx.expand_abstract_consts(ce); let mut v = Visitor { ct, infcx, param_env }; let result = b_ct.visit_with(&mut v); diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 1fd0926fec98e..e58881710a577 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -462,10 +462,8 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { // // Let's just see where this breaks :shrug: { - let c1 = - if let Ok(Some(a)) = tcx.expand_abstract_consts(c1) { a } else { c1 }; - let c2 = - if let Ok(Some(b)) = tcx.expand_abstract_consts(c2) { b } else { c2 }; + let c1 = tcx.expand_abstract_consts(c1); + let c2 = tcx.expand_abstract_consts(c2); debug!("equating consts:\nc1= {:?}\nc2= {:?}", c1, c2); use rustc_hir::def::DefKind; diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 0ea126fa9c99e..70e4ed29266da 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -837,24 +837,9 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<'tcx>>( } fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow { - // Constants can only influence object safety if they reference `Self`. + // Constants can only influence object safety if they are generic and reference `Self`. // This is only possible for unevaluated constants, so we walk these here. - // - // If `AbstractConst::from_const` returned an error we already failed compilation - // so we don't have to emit an additional error here. - // - // 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_abstract_consts(ct){ - self.visit_const(ct) - } else { - ct.super_visit_with(self) - } + self.tcx.expand_abstract_consts(ct).super_visit_with(self) } } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index daa3f2775e069..c93b3fbf61b70 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -664,10 +664,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ); { - let c1 = - if let Ok(Some(a)) = tcx.expand_abstract_consts(c1) { a } else { c1 }; - let c2 = - if let Ok(Some(b)) = tcx.expand_abstract_consts(c2) { b } else { c2 }; + let c1 = tcx.expand_abstract_consts(c1); + let c2 = tcx.expand_abstract_consts(c2); debug!( "evalaute_predicate_recursively: equating consts:\nc1= {:?}\nc2= {:?}", c1, c2 From 677bdcb8a907d4e348e34c40479976e0f0446de8 Mon Sep 17 00:00:00 2001 From: Boxy Date: Thu, 24 Nov 2022 11:25:19 +0000 Subject: [PATCH 15/16] only emit "enable gce" error if it would fix compile error --- .../src/traits/const_evaluatable.rs | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index a26653b3184b1..e9e65336299e4 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -52,7 +52,7 @@ pub fn is_const_evaluatable<'tcx>( }; if !is_anon_ct { - if satisfied_from_param_env(tcx, infcx, ct, param_env)? { + if satisfied_from_param_env(tcx, infcx, ct, param_env) { return Ok(()); } if ct.has_non_region_infer() { @@ -87,8 +87,14 @@ pub fn is_const_evaluatable<'tcx>( // If we're evaluating a generic foreign constant, under a nightly compiler while // the current crate does not enable `feature(generic_const_exprs)`, abort // compilation with a useful error. - Err(_) if tcx.sess.is_nightly_build() - && let ty::ConstKind::Expr(_) = tcx.expand_abstract_consts(ct).kind() => + Err(_) + if tcx.sess.is_nightly_build() + && satisfied_from_param_env( + tcx, + infcx, + tcx.expand_abstract_consts(ct), + param_env, + ) => { tcx.sess .struct_span_fatal( @@ -112,12 +118,15 @@ pub fn is_const_evaluatable<'tcx>( } else if uv.has_non_region_param() { NotConstEvaluatable::MentionsParam } else { - let guar = infcx.tcx.sess.delay_span_bug(span, format!("Missing value for constant, but no error reported?")); + let guar = infcx.tcx.sess.delay_span_bug( + span, + format!("Missing value for constant, but no error reported?"), + ); NotConstEvaluatable::Error(guar) }; Err(err) - }, + } Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e)), Ok(_) => Ok(()), } @@ -130,7 +139,7 @@ fn satisfied_from_param_env<'tcx>( infcx: &InferCtxt<'tcx>, ct: ty::Const<'tcx>, param_env: ty::ParamEnv<'tcx>, -) -> Result { +) -> bool { // Try to unify with each subtree in the AbstractConst to allow for // `N + 1` being const evaluatable even if theres only a `ConstEvaluatable` // predicate for `(N + 1) * 2` @@ -179,12 +188,12 @@ fn satisfied_from_param_env<'tcx>( if let ControlFlow::Break(()) = result { debug!("is_const_evaluatable: abstract_const ~~> ok"); - return Ok(true); + return true; } } _ => {} // don't care } } - Ok(false) + false } From d0209db15776a47ee0893385f218521b9a105cd8 Mon Sep 17 00:00:00 2001 From: kadmin Date: Fri, 25 Nov 2022 09:35:37 +0000 Subject: [PATCH 16/16] Fix mk_const_list --- compiler/rustc_middle/src/ty/context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 8cf33a5ca0bb9..4bffe94d8d557 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -2717,7 +2717,7 @@ impl<'tcx> TyCtxt<'tcx> { } } - pub fn mk_const_list], &'tcx List>>>( + pub fn mk_const_list, &'tcx List>>>( self, iter: I, ) -> I::Output {