From bea0148ac62758a99dcc2ac2cc2efedacb8881bf Mon Sep 17 00:00:00 2001 From: Boxy Date: Tue, 12 Nov 2024 02:54:03 +0000 Subject: [PATCH] Consolidate type system const evaluation under `traits::evaluate_const` mew --- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 2 +- compiler/rustc_infer/src/infer/mod.rs | 136 +---------- compiler/rustc_middle/src/mir/consts.rs | 20 +- compiler/rustc_middle/src/ty/consts.rs | 59 +---- compiler/rustc_middle/src/ty/consts/kind.rs | 36 +-- .../src/thir/pattern/const_to_pat.rs | 62 +++-- .../rustc_next_trait_solver/src/delegate.rs | 4 +- .../src/solve/eval_ctxt/mod.rs | 6 +- .../rustc_next_trait_solver/src/solve/mod.rs | 4 +- .../src/solve/normalizes_to/anon_const.rs | 2 +- .../src/solve/delegate.rs | 23 +- .../src/traits/auto_trait.rs | 28 +-- .../src/traits/const_evaluatable.rs | 31 ++- .../src/traits/fulfill.rs | 52 +++-- .../rustc_trait_selection/src/traits/mod.rs | 213 +++++++++++++++++- .../src/traits/normalize.rs | 3 +- .../src/traits/query/normalize.rs | 2 +- .../src/traits/select/confirmation.rs | 2 +- .../src/traits/select/mod.rs | 21 +- .../src/traits/structural_normalize.rs | 2 +- .../clippy_lints/src/large_const_arrays.rs | 2 +- tests/crashes/118545.rs | 8 - tests/crashes/128232.rs | 15 -- .../non_valtreeable_const_arg-1.rs | 12 + .../non_valtreeable_const_arg-1.stderr | 10 + .../non_valtreeable_const_arg-2.rs | 19 ++ .../non_valtreeable_const_arg-2.stderr | 39 ++++ .../unify-op-with-fn-call.rs | 36 --- .../unify-op-with-fn-call.stderr | 93 -------- .../traits/const-traits/const-trait-bounds.rs | 32 --- .../const-traits/const-trait-bounds.stderr | 29 --- 31 files changed, 434 insertions(+), 569 deletions(-) delete mode 100644 tests/crashes/118545.rs delete mode 100644 tests/crashes/128232.rs create mode 100644 tests/ui/const-generics/adt_const_params/non_valtreeable_const_arg-1.rs create mode 100644 tests/ui/const-generics/adt_const_params/non_valtreeable_const_arg-1.stderr create mode 100644 tests/ui/const-generics/adt_const_params/non_valtreeable_const_arg-2.rs create mode 100644 tests/ui/const-generics/adt_const_params/non_valtreeable_const_arg-2.stderr delete mode 100644 tests/ui/const-generics/generic_const_exprs/unify-op-with-fn-call.rs delete mode 100644 tests/ui/const-generics/generic_const_exprs/unify-op-with-fn-call.stderr delete mode 100644 tests/ui/traits/const-traits/const-trait-bounds.rs delete mode 100644 tests/ui/traits/const-traits/const-trait-bounds.stderr diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index ce0ab8a913b0a..c290f96dfdb70 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -1491,7 +1491,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } else if self.tcx.features().generic_const_exprs() { - ct.normalize_internal(self.tcx, self.param_env) + rustc_trait_selection::traits::evaluate_const(&self.infcx, ct, self.param_env) } else { ct } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index fc54d69449f04..12df4a10e6364 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -25,10 +25,10 @@ use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_macros::extension; pub use rustc_macros::{TypeFoldable, TypeVisitable}; +use rustc_middle::bug; use rustc_middle::infer::canonical::{CanonicalQueryInput, CanonicalVarValues}; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableValue, ConstVidKey}; use rustc_middle::mir::ConstraintCategory; -use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult}; use rustc_middle::traits::select; pub use rustc_middle::ty::IntVarValue; use rustc_middle::ty::error::{ExpectedFound, TypeError}; @@ -40,7 +40,6 @@ use rustc_middle::ty::{ self, ConstVid, FloatVid, GenericArg, GenericArgKind, GenericArgs, GenericArgsRef, GenericParamDefKind, InferConst, IntVid, Ty, TyCtxt, TyVid, TypingMode, }; -use rustc_middle::{bug, span_bug}; use rustc_span::Span; use rustc_span::symbol::Symbol; use rustc_type_ir::solve::Reveal; @@ -1279,84 +1278,6 @@ impl<'tcx> InferCtxt<'tcx> { u } - pub fn try_const_eval_resolve( - &self, - param_env: ty::ParamEnv<'tcx>, - unevaluated: ty::UnevaluatedConst<'tcx>, - span: Span, - ) -> Result, ErrorHandled> { - match self.const_eval_resolve(param_env, unevaluated, span) { - Ok(Ok(val)) => Ok(ty::Const::new_value( - self.tcx, - val, - self.tcx.type_of(unevaluated.def).instantiate(self.tcx, unevaluated.args), - )), - Ok(Err(bad_ty)) => { - let tcx = self.tcx; - let def_id = unevaluated.def; - span_bug!( - tcx.def_span(def_id), - "unable to construct a valtree for the unevaluated constant {:?}: type {bad_ty} is not valtree-compatible", - unevaluated - ); - } - Err(err) => Err(err), - } - } - - /// Resolves and evaluates a constant. - /// - /// The constant can be located on a trait like `::C`, in which case the given - /// generic parameters and environment are used to resolve the constant. Alternatively if the - /// constant has generic parameters in scope the instantiations are used to evaluate the value - /// of the constant. For example in `fn foo() { let _ = [0; bar::()]; }` the repeat count - /// constant `bar::()` requires a instantiation for `T`, if the instantiation for `T` is - /// still too generic for the constant to be evaluated then `Err(ErrorHandled::TooGeneric)` is - /// returned. - /// - /// This handles inferences variables within both `param_env` and `args` by - /// performing the operation on their respective canonical forms. - #[instrument(skip(self), level = "debug")] - pub fn const_eval_resolve( - &self, - mut param_env: ty::ParamEnv<'tcx>, - unevaluated: ty::UnevaluatedConst<'tcx>, - span: Span, - ) -> EvalToValTreeResult<'tcx> { - let mut args = self.resolve_vars_if_possible(unevaluated.args); - debug!(?args); - - // Postpone the evaluation of constants whose args depend on inference - // variables - let tcx = self.tcx; - if args.has_non_region_infer() { - if let Some(ct) = tcx.thir_abstract_const(unevaluated.def)? { - let ct = tcx.expand_abstract_consts(ct.instantiate(tcx, args)); - if let Err(e) = ct.error_reported() { - return Err(ErrorHandled::Reported(e.into(), span)); - } else if ct.has_non_region_infer() || ct.has_non_region_param() { - return Err(ErrorHandled::TooGeneric(span)); - } else { - args = replace_param_and_infer_args_with_placeholder(tcx, args); - } - } else { - args = GenericArgs::identity_for_item(tcx, unevaluated.def); - param_env = tcx.param_env(unevaluated.def); - } - } - - let param_env_erased = tcx.erase_regions(param_env); - let args_erased = tcx.erase_regions(args); - debug!(?param_env_erased); - debug!(?args_erased); - - let unevaluated = ty::UnevaluatedConst { def: unevaluated.def, args: args_erased }; - - // The return value is the evaluated value which doesn't contain any reference to inference - // variables, thus we don't need to instantiate back the original values. - tcx.const_eval_resolve_for_typeck(param_env_erased, unevaluated, span) - } - /// The returned function is used in a fast path. If it returns `true` the variable is /// unchanged, `false` indicates that the status is unknown. #[inline] @@ -1622,61 +1543,6 @@ impl RegionVariableOrigin { } } -/// Replaces args that reference param or infer variables with suitable -/// placeholders. This function is meant to remove these param and infer -/// args when they're not actually needed to evaluate a constant. -fn replace_param_and_infer_args_with_placeholder<'tcx>( - tcx: TyCtxt<'tcx>, - args: GenericArgsRef<'tcx>, -) -> GenericArgsRef<'tcx> { - struct ReplaceParamAndInferWithPlaceholder<'tcx> { - tcx: TyCtxt<'tcx>, - idx: u32, - } - - impl<'tcx> TypeFolder> for ReplaceParamAndInferWithPlaceholder<'tcx> { - fn cx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - if let ty::Infer(_) = t.kind() { - let idx = { - let idx = self.idx; - self.idx += 1; - idx - }; - Ty::new_placeholder(self.tcx, ty::PlaceholderType { - universe: ty::UniverseIndex::ROOT, - bound: ty::BoundTy { - var: ty::BoundVar::from_u32(idx), - kind: ty::BoundTyKind::Anon, - }, - }) - } else { - t.super_fold_with(self) - } - } - - fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> { - if let ty::ConstKind::Infer(_) = c.kind() { - ty::Const::new_placeholder(self.tcx, ty::PlaceholderConst { - universe: ty::UniverseIndex::ROOT, - bound: ty::BoundVar::from_u32({ - let idx = self.idx; - self.idx += 1; - idx - }), - }) - } else { - c.super_fold_with(self) - } - } - } - - args.fold_with(&mut ReplaceParamAndInferWithPlaceholder { tcx, idx: 0 }) -} - impl<'tcx> InferCtxt<'tcx> { /// Given a [`hir::Block`], get the span of its last expression or /// statement, peeling off any inner blocks. diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index 1ae0bd6740dc9..757034fe30a5e 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -1,17 +1,17 @@ use std::fmt::{self, Debug, Display, Formatter}; -use either::Either; use rustc_abi::{HasDataLayout, Size}; use rustc_hir::def_id::DefId; use rustc_macros::{HashStable, Lift, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use rustc_session::RemapFileNameExt; use rustc_session::config::RemapPathScopeComponents; use rustc_span::{DUMMY_SP, Span}; +use rustc_type_ir::visit::TypeVisitableExt; use crate::mir::interpret::{AllocId, ConstAllocation, ErrorHandled, Scalar, alloc_range}; use crate::mir::{Promoted, pretty_print_const_value}; use crate::ty::print::{pretty_print_const, with_no_trimmed_paths}; -use crate::ty::{self, GenericArgsRef, ScalarInt, Ty, TyCtxt}; +use crate::ty::{self, ConstKind, GenericArgsRef, ScalarInt, Ty, TyCtxt}; /////////////////////////////////////////////////////////////////////////// /// Evaluated Constants @@ -319,15 +319,13 @@ impl<'tcx> Const<'tcx> { ) -> Result, ErrorHandled> { match self { Const::Ty(_, c) => { - // We want to consistently have a "clean" value for type system constants (i.e., no - // data hidden in the padding), so we always go through a valtree here. - match c.eval_valtree(tcx, param_env, span) { - Ok((ty, val)) => Ok(tcx.valtree_to_const_val((ty, val))), - Err(Either::Left(_bad_ty)) => Err(tcx - .dcx() - .delayed_bug("`mir::Const::eval` called on a non-valtree-compatible type") - .into()), - Err(Either::Right(e)) => Err(e), + if c.has_non_region_param() { + return Err(ErrorHandled::TooGeneric(span)); + } + + match c.kind() { + ConstKind::Value(ty, val) => Ok(tcx.valtree_to_const_val((ty, val))), + _ => Err(tcx.dcx().delayed_bug("Unevaluated `ty::Const` in MIR body").into()), } } Const::Unevaluated(uneval, _) => { diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 5ab85a69ce608..5689f3d426541 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -1,4 +1,3 @@ -use either::Either; use rustc_data_structures::intern::Interned; use rustc_error_messages::MultiSpan; use rustc_hir::def::{DefKind, Res}; @@ -9,7 +8,7 @@ use rustc_type_ir::{self as ir, TypeFlags, WithCachedTypeInfo}; use tracing::{debug, instrument}; use crate::middle::resolve_bound_vars as rbv; -use crate::mir::interpret::{ErrorHandled, LitToConstInput, Scalar}; +use crate::mir::interpret::{LitToConstInput, Scalar}; use crate::ty::{self, GenericArgs, ParamEnv, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt}; mod int; @@ -18,7 +17,7 @@ mod valtree; pub use int::*; pub use kind::*; -use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; +use rustc_span::{DUMMY_SP, ErrorGuaranteed}; pub use valtree::*; pub type ConstKind<'tcx> = ir::ConstKind>; @@ -363,60 +362,6 @@ impl<'tcx> Const<'tcx> { Self::from_bits(tcx, n as u128, ParamEnv::empty().and(tcx.types.usize)) } - /// Returns the evaluated constant as a valtree; - /// if that fails due to a valtree-incompatible type, indicate which type that is - /// by returning `Err(Left(bad_type))`. - #[inline] - pub fn eval_valtree( - self, - tcx: TyCtxt<'tcx>, - param_env: ParamEnv<'tcx>, - span: Span, - ) -> Result<(Ty<'tcx>, ValTree<'tcx>), Either, ErrorHandled>> { - assert!(!self.has_escaping_bound_vars(), "escaping vars in {self:?}"); - match self.kind() { - ConstKind::Unevaluated(unevaluated) => { - // FIXME(eddyb) maybe the `const_eval_*` methods should take - // `ty::ParamEnvAnd` instead of having them separate. - let (param_env, unevaluated) = unevaluated.prepare_for_eval(tcx, param_env); - // try to resolve e.g. associated constants to their definition on an impl, and then - // evaluate the const. - match tcx.const_eval_resolve_for_typeck(param_env, unevaluated, span) { - Ok(Ok(c)) => { - Ok((tcx.type_of(unevaluated.def).instantiate(tcx, unevaluated.args), c)) - } - Ok(Err(bad_ty)) => Err(Either::Left(bad_ty)), - Err(err) => Err(Either::Right(err)), - } - } - ConstKind::Value(ty, val) => Ok((ty, val)), - ConstKind::Error(g) => Err(Either::Right(g.into())), - ConstKind::Param(_) - | ConstKind::Infer(_) - | ConstKind::Bound(_, _) - | ConstKind::Placeholder(_) - | ConstKind::Expr(_) => Err(Either::Right(ErrorHandled::TooGeneric(span))), - } - } - - /// Normalizes the constant to a value or an error if possible. - #[inline] - pub fn normalize_internal(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Self { - match self.eval_valtree(tcx, param_env, DUMMY_SP) { - Ok((ty, val)) => Self::new_value(tcx, val, ty), - Err(Either::Left(_bad_ty)) => { - // This can happen when we run on ill-typed code. - Self::new_error( - tcx, - tcx.dcx() - .delayed_bug("`ty::Const::eval` called on a non-valtree-compatible type"), - ) - } - Err(Either::Right(ErrorHandled::Reported(r, _span))) => Self::new_error(tcx, r.into()), - Err(Either::Right(ErrorHandled::TooGeneric(_span))) => self, - } - } - /// Panics if self.kind != ty::ConstKind::Value pub fn to_valtree(self) -> (ty::ValTree<'tcx>, Ty<'tcx>) { match self.kind() { diff --git a/compiler/rustc_middle/src/ty/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs index 91b764ae1d451..b3436550e8e07 100644 --- a/compiler/rustc_middle/src/ty/consts/kind.rs +++ b/compiler/rustc_middle/src/ty/consts/kind.rs @@ -1,46 +1,12 @@ use std::assert_matches::assert_matches; -use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable, extension}; +use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use super::Const; use crate::mir; use crate::ty::abstract_const::CastKind; -use crate::ty::visit::TypeVisitableExt as _; use crate::ty::{self, Ty, TyCtxt}; -#[extension(pub(crate) trait UnevaluatedConstEvalExt<'tcx>)] -impl<'tcx> ty::UnevaluatedConst<'tcx> { - /// FIXME(RalfJung): I cannot explain what this does or why it makes sense, but not doing this - /// hurts performance. - #[inline] - fn prepare_for_eval( - self, - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ) -> (ty::ParamEnv<'tcx>, Self) { - // HACK(eddyb) this erases lifetimes even though `const_eval_resolve` - // also does later, but we want to do it before checking for - // inference variables. - // Note that we erase regions *before* calling `with_reveal_all_normalized`, - // so that we don't try to invoke this query with - // any region variables. - - // HACK(eddyb) when the query key would contain inference variables, - // attempt using identity args and `ParamEnv` instead, that will succeed - // when the expression doesn't depend on any parameters. - // FIXME(eddyb, skinny121) pass `InferCtxt` into here when it's available, so that - // we can call `infcx.const_eval_resolve` which handles inference variables. - if (param_env, self).has_non_region_infer() { - (tcx.param_env(self.def), ty::UnevaluatedConst { - def: self.def, - args: ty::GenericArgs::identity_for_item(tcx, self.def), - }) - } else { - (tcx.erase_regions(param_env).with_reveal_all_normalized(tcx), tcx.erase_regions(self)) - } - } -} - #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] #[derive(HashStable, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable)] pub enum ExprKind { diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 983853d2de186..82632350af59c 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -1,14 +1,13 @@ -use either::Either; use rustc_abi::{FieldIdx, VariantIdx}; use rustc_apfloat::Float; use rustc_hir as hir; use rustc_index::Idx; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::Obligation; -use rustc_middle::mir; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::thir::{FieldPat, Pat, PatKind}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypingMode, ValTree}; +use rustc_middle::{mir, span_bug}; use rustc_span::Span; use rustc_trait_selection::traits::ObligationCause; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; @@ -40,7 +39,12 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { // of opaques defined in this function here. let infcx = self.tcx.infer_ctxt().build(TypingMode::non_body_analysis()); let mut convert = ConstToPat::new(self, id, span, infcx); - convert.to_pat(c, ty) + + match c.kind() { + ty::ConstKind::Unevaluated(uv) => convert.unevaluated_to_pat(uv, ty), + ty::ConstKind::Value(_, val) => convert.valtree_to_pat(val, ty), + _ => span_bug!(span, "Invalid `ConstKind` for `const_to_pat`: {:?}", c), + } } } @@ -81,27 +85,42 @@ impl<'tcx> ConstToPat<'tcx> { ty.is_structural_eq_shallow(self.infcx.tcx) } - fn to_pat(&mut self, c: ty::Const<'tcx>, ty: Ty<'tcx>) -> Box> { + fn unevaluated_to_pat( + &mut self, + uv: ty::UnevaluatedConst<'tcx>, + ty: Ty<'tcx>, + ) -> Box> { trace!(self.treat_byte_string_as_slice); let pat_from_kind = |kind| Box::new(Pat { span: self.span, ty, kind }); - // Get a valtree. If that fails, this const is definitely not valid for use as a pattern. - let valtree = match c.eval_valtree(self.tcx(), self.param_env, self.span) { - Ok((_, valtree)) => valtree, - Err(Either::Right(e)) => { - let err = match e { - ErrorHandled::Reported(..) => { - // Let's tell the use where this failing const occurs. - self.tcx().dcx().emit_err(CouldNotEvalConstPattern { span: self.span }) - } - ErrorHandled::TooGeneric(_) => self - .tcx() - .dcx() - .emit_err(ConstPatternDependsOnGenericParameter { span: self.span }), - }; - return pat_from_kind(PatKind::Error(err)); + // It's not *technically* correct to be revealing opaque types here as borrowcheck has + // not run yet. However, CTFE itself uses `Reveal::All` unconditionally even during + // typeck and not doing so has a lot of (undesirable) fallout (#101478, #119821). As a + // result we always use a revealed env when resolving the instance to evaluate. + // + // FIXME: `const_eval_resolve_for_typeck` should probably just set the env to `Reveal::All` + // instead of having this logic here + let param_env = + self.tcx().erase_regions(self.param_env).with_reveal_all_normalized(self.tcx()); + let uv = self.tcx().erase_regions(uv); + + // try to resolve e.g. associated constants to their definition on an impl, and then + // evaluate the const. + let valtree = match self.infcx.tcx.const_eval_resolve_for_typeck(param_env, uv, self.span) { + Ok(Ok(c)) => c, + Err(ErrorHandled::Reported(_, _)) => { + // Let's tell the use where this failing const occurs. + let e = self.tcx().dcx().emit_err(CouldNotEvalConstPattern { span: self.span }); + return pat_from_kind(PatKind::Error(e)); } - Err(Either::Left(bad_ty)) => { + Err(ErrorHandled::TooGeneric(_)) => { + let e = self + .tcx() + .dcx() + .emit_err(ConstPatternDependsOnGenericParameter { span: self.span }); + return pat_from_kind(PatKind::Error(e)); + } + Ok(Err(bad_ty)) => { // The pattern cannot be turned into a valtree. let e = match bad_ty.kind() { ty::Adt(def, ..) => { @@ -128,8 +147,7 @@ impl<'tcx> ConstToPat<'tcx> { if !self.type_has_partial_eq_impl(ty) { let err = TypeNotPartialEq { span: self.span, non_peq_ty: ty }; let e = self.tcx().dcx().emit_err(err); - let kind = PatKind::Error(e); - return Box::new(Pat { span: self.span, ty, kind }); + return pat_from_kind(PatKind::Error(e)); } } diff --git a/compiler/rustc_next_trait_solver/src/delegate.rs b/compiler/rustc_next_trait_solver/src/delegate.rs index f7fbfed5b8e38..4ba54a2e0bf23 100644 --- a/compiler/rustc_next_trait_solver/src/delegate.rs +++ b/compiler/rustc_next_trait_solver/src/delegate.rs @@ -29,10 +29,10 @@ pub trait SolverDelegate: Deref::Infcx> + Size // FIXME: Uplift the leak check into this crate. fn leak_check(&self, max_input_universe: ty::UniverseIndex) -> Result<(), NoSolution>; - fn try_const_eval_resolve( + fn evaluate_const( &self, param_env: ::ParamEnv, - unevaluated: ty::UnevaluatedConst, + uv: ty::UnevaluatedConst, ) -> Option<::Const>; // FIXME: This only is here because `wf::obligations` is in `rustc_trait_selection`! diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 8685896715e1e..979a379474822 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -1001,12 +1001,12 @@ where // Try to evaluate a const, or return `None` if the const is too generic. // This doesn't mean the const isn't evaluatable, though, and should be treated // as an ambiguity rather than no-solution. - pub(super) fn try_const_eval_resolve( + pub(super) fn evaluate_const( &self, param_env: I::ParamEnv, - unevaluated: ty::UnevaluatedConst, + uv: ty::UnevaluatedConst, ) -> Option { - self.delegate.try_const_eval_resolve(param_env, unevaluated) + self.delegate.evaluate_const(param_env, uv) } pub(super) fn is_transmutable( diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 6793779b205d1..5c54656cc59f5 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -143,7 +143,7 @@ where ) -> QueryResult { match ct.kind() { ty::ConstKind::Unevaluated(uv) => { - // We never return `NoSolution` here as `try_const_eval_resolve` emits an + // We never return `NoSolution` here as `evaluate_const` emits an // error itself when failing to evaluate, so emitting an additional fulfillment // error in that case is unnecessary noise. This may change in the future once // evaluation failures are allowed to impact selection, e.g. generic const @@ -151,7 +151,7 @@ where // FIXME(generic_const_exprs): Implement handling for generic // const expressions here. - if let Some(_normalized) = self.try_const_eval_resolve(param_env, uv) { + if let Some(_normalized) = self.evaluate_const(param_env, uv) { self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } else { self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs index 5d5597429da45..8ad0bf5cdf9a4 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs @@ -14,7 +14,7 @@ where &mut self, goal: Goal>, ) -> QueryResult { - if let Some(normalized_const) = self.try_const_eval_resolve( + if let Some(normalized_const) = self.evaluate_const( goal.param_env, ty::UnevaluatedConst::new(goal.predicate.alias.def_id, goal.predicate.alias.args), ) { diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index c53689b211d2a..2cc787c8bc577 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -15,7 +15,7 @@ use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; use rustc_type_ir::TypingMode; use rustc_type_ir::solve::{Certainty, NoSolution}; -use crate::traits::specialization_graph; +use crate::traits::{EvaluateConstErr, specialization_graph}; #[repr(transparent)] pub struct SolverDelegate<'tcx>(InferCtxt<'tcx>); @@ -77,20 +77,19 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< self.0.leak_check(max_input_universe, None).map_err(|_| NoSolution) } - fn try_const_eval_resolve( + fn evaluate_const( &self, param_env: ty::ParamEnv<'tcx>, - unevaluated: ty::UnevaluatedConst<'tcx>, + uv: ty::UnevaluatedConst<'tcx>, ) -> Option> { - use rustc_middle::mir::interpret::ErrorHandled; - match self.const_eval_resolve(param_env, unevaluated, DUMMY_SP) { - Ok(Ok(val)) => Some(ty::Const::new_value( - self.tcx, - val, - self.tcx.type_of(unevaluated.def).instantiate(self.tcx, unevaluated.args), - )), - Ok(Err(_)) | Err(ErrorHandled::TooGeneric(_)) => None, - Err(ErrorHandled::Reported(e, _)) => Some(ty::Const::new_error(self.tcx, e.into())), + let ct = ty::Const::new_unevaluated(self.tcx, uv); + + match crate::traits::try_evaluate_const(&self.0, ct, param_env) { + Ok(ct) => Some(ct), + Err(EvaluateConstErr::EvaluationFailure(e)) => Some(ty::Const::new_error(self.tcx, e)), + Err( + EvaluateConstErr::InvalidConstParamTy(_) | EvaluateConstErr::HasGenericsOrInfers, + ) => None, } } diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 52ba5621d3191..103f7c76a86d7 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -7,7 +7,6 @@ use std::iter; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet, IndexEntry}; use rustc_data_structures::unord::UnordSet; use rustc_infer::infer::DefineOpaqueTypes; -use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::ty::{Region, RegionVid}; use tracing::debug; use ty::TypingMode; @@ -761,23 +760,20 @@ impl<'tcx> AutoTraitFinder<'tcx> { ty::PredicateKind::ConstEquate(c1, c2) => { let evaluate = |c: ty::Const<'tcx>| { if let ty::ConstKind::Unevaluated(unevaluated) = c.kind() { - match selcx.infcx.const_eval_resolve( + let ct = super::try_evaluate_const( + selcx.infcx, + c, obligation.param_env, - unevaluated, - obligation.cause.span, - ) { - Ok(Ok(valtree)) => Ok(ty::Const::new_value(selcx.tcx(),valtree, self.tcx.type_of(unevaluated.def).instantiate(self.tcx, unevaluated.args))), - Ok(Err(_)) => { - let tcx = self.tcx; - let reported = - tcx.dcx().emit_err(UnableToConstructConstantValue { - span: tcx.def_span(unevaluated.def), - unevaluated, - }); - Err(ErrorHandled::Reported(reported.into(), tcx.def_span(unevaluated.def))) - } - Err(err) => Err(err), + ); + + if let Err(EvaluateConstErr::InvalidConstParamTy(_)) = ct { + self.tcx.dcx().emit_err(UnableToConstructConstantValue { + span: self.tcx.def_span(unevaluated.def), + unevaluated, + }); } + + ct } else { Ok(c) } diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 1be3c96445465..15eb5d74cbfa5 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -12,13 +12,13 @@ use rustc_hir::def::DefKind; use rustc_infer::infer::InferCtxt; use rustc_middle::bug; -use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::{self, TyCtxt, TypeVisitable, TypeVisitableExt, TypeVisitor}; use rustc_span::{DUMMY_SP, Span}; use tracing::{debug, instrument}; +use super::EvaluateConstErr; use crate::traits::ObligationCtxt; /// Check if a given constant can be evaluated. @@ -68,16 +68,18 @@ pub fn is_const_evaluatable<'tcx>( // here. tcx.dcx().span_bug(span, "evaluating `ConstKind::Expr` is not currently supported"); } - ty::ConstKind::Unevaluated(uv) => { - let concrete = infcx.const_eval_resolve(param_env, uv, span); - match concrete { - Err(ErrorHandled::TooGeneric(_)) => { + ty::ConstKind::Unevaluated(_) => { + match crate::traits::try_evaluate_const(infcx, unexpanded_ct, param_env) { + Err(EvaluateConstErr::HasGenericsOrInfers) => { Err(NotConstEvaluatable::Error(infcx.dcx().span_delayed_bug( span, "Missing value for constant, but no error reported?", ))) } - Err(ErrorHandled::Reported(e, _)) => Err(NotConstEvaluatable::Error(e.into())), + Err( + EvaluateConstErr::EvaluationFailure(e) + | EvaluateConstErr::InvalidConstParamTy(e), + ) => Err(NotConstEvaluatable::Error(e.into())), Ok(_) => Ok(()), } } @@ -92,16 +94,7 @@ pub fn is_const_evaluatable<'tcx>( _ => bug!("unexpected constkind in `is_const_evalautable: {unexpanded_ct:?}`"), }; - // FIXME: We should only try to evaluate a given constant here if it is fully concrete - // as we don't want to allow things like `[u8; std::mem::size_of::<*mut T>()]`. - // - // We previously did not check this, so we only emit a future compat warning if - // const evaluation succeeds and the given constant is still polymorphic for now - // and hopefully soon change this to an error. - // - // See #74595 for more details about this. - let concrete = infcx.const_eval_resolve(param_env, uv, span); - match concrete { + match crate::traits::try_evaluate_const(infcx, unexpanded_ct, param_env) { // 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. @@ -130,7 +123,7 @@ pub fn is_const_evaluatable<'tcx>( .emit() } - Err(ErrorHandled::TooGeneric(_)) => { + Err(EvaluateConstErr::HasGenericsOrInfers) => { let err = if uv.has_non_region_infer() { NotConstEvaluatable::MentionsInfer } else if uv.has_non_region_param() { @@ -145,7 +138,9 @@ pub fn is_const_evaluatable<'tcx>( Err(err) } - Err(ErrorHandled::Reported(e, _)) => Err(NotConstEvaluatable::Error(e.into())), + Err( + EvaluateConstErr::EvaluationFailure(e) | EvaluateConstErr::InvalidConstParamTy(e), + ) => Err(NotConstEvaluatable::Error(e.into())), Ok(_) => Ok(()), } } diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 21141f6e18f39..2ec5f0d22496d 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -10,7 +10,6 @@ use rustc_infer::traits::{ TraitEngine, }; use rustc_middle::bug; -use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::{self, Binder, Const, GenericArgsRef, TypeVisitableExt, TypingMode}; @@ -26,6 +25,7 @@ use super::{ }; use crate::error_reporting::InferCtxtErrorExt; use crate::infer::{InferCtxt, TyOrConstInferVar}; +use crate::traits::EvaluateConstErr; use crate::traits::normalize::normalize_with_depth_to; use crate::traits::project::{PolyProjectionObligation, ProjectionCacheKeyExt as _}; use crate::traits::query::evaluate_obligation::InferCtxtExt; @@ -664,23 +664,25 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { let mut evaluate = |c: Const<'tcx>| { if let ty::ConstKind::Unevaluated(unevaluated) = c.kind() { - match self.selcx.infcx.try_const_eval_resolve( + match super::try_evaluate_const( + self.selcx.infcx, + c, obligation.param_env, - unevaluated, - obligation.cause.span, ) { Ok(val) => Ok(val), - Err(e) => { - match e { - ErrorHandled::TooGeneric(..) => { - stalled_on.extend(unevaluated.args.iter().filter_map( - TyOrConstInferVar::maybe_from_generic_arg, - )); - } - _ => {} - } - Err(e) + e @ Err(EvaluateConstErr::HasGenericsOrInfers) => { + stalled_on.extend( + unevaluated + .args + .iter() + .filter_map(TyOrConstInferVar::maybe_from_generic_arg), + ); + e } + e @ Err( + EvaluateConstErr::EvaluationFailure(_) + | EvaluateConstErr::InvalidConstParamTy(_), + ) => e, } } else { Ok(c) @@ -707,14 +709,20 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { } } } - (Err(ErrorHandled::Reported(reported, _)), _) - | (_, Err(ErrorHandled::Reported(reported, _))) => ProcessResult::Error( - FulfillmentErrorCode::Select(SelectionError::NotConstEvaluatable( - NotConstEvaluatable::Error(reported.into()), - )), - ), - (Err(ErrorHandled::TooGeneric(_)), _) - | (_, Err(ErrorHandled::TooGeneric(_))) => { + (Err(EvaluateConstErr::InvalidConstParamTy(e)), _) + | (_, Err(EvaluateConstErr::InvalidConstParamTy(e))) => { + ProcessResult::Error(FulfillmentErrorCode::Select( + SelectionError::NotConstEvaluatable(NotConstEvaluatable::Error(e)), + )) + } + (Err(EvaluateConstErr::EvaluationFailure(e)), _) + | (_, Err(EvaluateConstErr::EvaluationFailure(e))) => { + ProcessResult::Error(FulfillmentErrorCode::Select( + SelectionError::NotConstEvaluatable(NotConstEvaluatable::Error(e)), + )) + } + (Err(EvaluateConstErr::HasGenericsOrInfers), _) + | (_, Err(EvaluateConstErr::HasGenericsOrInfers)) => { if c1.has_non_region_infer() || c2.has_non_region_infer() { ProcessResult::Unchanged } else { diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 436c0fabd2966..17636d432aeed 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -27,6 +27,7 @@ use std::fmt::Debug; use std::ops::ControlFlow; use rustc_errors::ErrorGuaranteed; +use rustc_hir::def::DefKind; pub use rustc_infer::traits::*; use rustc_middle::query::Providers; use rustc_middle::span_bug; @@ -34,11 +35,11 @@ use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt}; use rustc_middle::ty::{ - self, GenericArgs, GenericArgsRef, Ty, TyCtxt, TypeFolder, TypeSuperVisitable, TypingMode, - Upcast, + self, GenericArgs, GenericArgsRef, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, + TypeSuperVisitable, TypingMode, Upcast, }; -use rustc_span::Span; use rustc_span::def_id::DefId; +use rustc_span::{DUMMY_SP, Span}; use tracing::{debug, instrument}; pub use self::coherence::{ @@ -366,11 +367,23 @@ pub fn normalize_param_env_or_error<'tcx>( if c.has_escaping_bound_vars() { return ty::Const::new_misc_error(self.0); } + // While it is pretty sus to be evaluating things with an empty param env, it // should actually be okay since without `feature(generic_const_exprs)` the only // const arguments that have a non-empty param env are array repeat counts. These // do not appear in the type system though. - c.normalize_internal(self.0, ty::ParamEnv::empty()) + if let ty::ConstKind::Unevaluated(uv) = c.kind() + && self.0.def_kind(uv.def) == DefKind::AnonConst + { + let infcx = self.0.infer_ctxt().build(TypingMode::non_body_analysis()); + let c = evaluate_const(&infcx, c, ty::ParamEnv::empty()); + // We should never wind up with any `infcx` local state when normalizing anon consts + // under min const generics. + assert!(!c.has_infer() && !c.has_placeholders()); + return c; + } + + c } } @@ -474,6 +487,198 @@ pub fn normalize_param_env_or_error<'tcx>( ty::ParamEnv::new(tcx.mk_clauses(&predicates), unnormalized_env.reveal()) } +#[derive(Debug)] +pub enum EvaluateConstErr { + /// The constant being evaluated was either a generic parameter or inference variable, *or*, + /// some unevaluated constant with either generic parameters or inference variables in its + /// generic arguments. + HasGenericsOrInfers, + /// The type this constant evalauted to is not valid for use in const generics. This should + /// always result in an error when checking the constant is correctly typed for the parameter + /// it is an argument to, so a bug is delayed when encountering this. + InvalidConstParamTy(ErrorGuaranteed), + /// CTFE failed to evaluate the constant in some unrecoverable way (e.g. encountered a `panic!`). + /// This is also used when the constant was already tainted by error. + EvaluationFailure(ErrorGuaranteed), +} + +// FIXME(BoxyUwU): Private this once we `generic_const_exprs` isn't doing its own normalization routine +// FIXME(generic_const_exprs): Consider accepting a `ty::UnevaluatedConst` when we are not rolling our own +// normalization scheme +/// Evaluates a type system constant returning a `ConstKind::Error` in cases where CTFE failed and +/// returning the passed in constant if it was not fully concrete (i.e. depended on generic parameters +/// or inference variables) +/// +/// You should not call this function unless you are implementing normalization itself. Prefer to use +/// `normalize_erasing_regions` or the `normalize` functions on `ObligationCtxt`/`FnCtxt`/`InferCtxt`. +pub fn evaluate_const<'tcx>( + infcx: &InferCtxt<'tcx>, + ct: ty::Const<'tcx>, + param_env: ty::ParamEnv<'tcx>, +) -> ty::Const<'tcx> { + match try_evaluate_const(infcx, ct, param_env) { + Ok(ct) => ct, + Err(EvaluateConstErr::EvaluationFailure(e) | EvaluateConstErr::InvalidConstParamTy(e)) => { + ty::Const::new_error(infcx.tcx, e) + } + Err(EvaluateConstErr::HasGenericsOrInfers) => ct, + } +} + +// FIXME(BoxyUwU): Private this once we `generic_const_exprs` isn't doing its own normalization routine +// FIXME(generic_const_exprs): Consider accepting a `ty::UnevaluatedConst` when we are not rolling our own +// normalization scheme +/// Evaluates a type system constant making sure to not allow constants that depend on generic parameters +/// or inference variables to succeed in evaluating. +/// +/// You should not call this function unless you are implementing normalization itself. Prefer to use +/// `normalize_erasing_regions` or the `normalize` functions on `ObligationCtxt`/`FnCtxt`/`InferCtxt`. +#[instrument(level = "debug", skip(infcx), ret)] +pub fn try_evaluate_const<'tcx>( + infcx: &InferCtxt<'tcx>, + ct: ty::Const<'tcx>, + param_env: ty::ParamEnv<'tcx>, +) -> Result, EvaluateConstErr> { + let tcx = infcx.tcx; + let ct = infcx.resolve_vars_if_possible(ct); + debug!(?ct); + + match ct.kind() { + ty::ConstKind::Value(..) => Ok(ct), + ty::ConstKind::Error(e) => Err(EvaluateConstErr::EvaluationFailure(e)), + ty::ConstKind::Param(_) + | ty::ConstKind::Infer(_) + | ty::ConstKind::Bound(_, _) + | ty::ConstKind::Placeholder(_) + | ty::ConstKind::Expr(_) => Err(EvaluateConstErr::HasGenericsOrInfers), + ty::ConstKind::Unevaluated(uv) => { + // Postpone evaluation of constants that depend on generic parameters or inference variables. + let (args, param_env) = if tcx.features().generic_const_exprs() + && uv.has_non_region_infer() + { + // `feature(generic_const_exprs)` causes anon consts to inherit all parent generics. This can cause + // inference variables and generic parameters to show up in `ty::Const` even though the anon const + // does not actually make use of them. We handle this case specially and attempt to evaluate anyway. + match tcx.thir_abstract_const(uv.def) { + Ok(Some(ct)) => { + let ct = tcx.expand_abstract_consts(ct.instantiate(tcx, uv.args)); + if let Err(e) = ct.error_reported() { + return Err(EvaluateConstErr::EvaluationFailure(e)); + } else if ct.has_non_region_infer() || ct.has_non_region_param() { + // If the anon const *does* actually use generic parameters or inference variables from + // the generic arguments provided for it, then we should *not* attempt to evaluate it. + return Err(EvaluateConstErr::HasGenericsOrInfers); + } else { + (replace_param_and_infer_args_with_placeholder(tcx, uv.args), param_env) + } + } + Err(_) | Ok(None) => { + let args = GenericArgs::identity_for_item(tcx, uv.def); + let param_env = tcx.param_env(uv.def); + (args, param_env) + } + } + } else { + // FIXME: We don't check anything on stable as the only way we can wind up with + // an unevaluated constant containing generic parameters is through array repeat + // expression counts which have a future compat lint for usage of generic parameters + // instead of a hard error. + // + // This codepath is however also reachable by `generic_const_exprs` and some other + // feature gates which allow constants in the type system to use generic parameters. + // In theory we should be checking for generic parameters here and returning an error + // in such cases. + (uv.args, param_env) + }; + let uv = ty::UnevaluatedConst::new(uv.def, args); + + // It's not *technically* correct to be revealing opaque types here as we could still be + // before borrowchecking. However, CTFE itself uses `Reveal::All` unconditionally even during + // typeck and not doing so has a lot of (undesirable) fallout (#101478, #119821). As a result we + // always use a revealed env when resolving the instance to evaluate. + // + // FIXME: `const_eval_resolve_for_typeck` should probably just set the env to `Reveal::All` + // instead of having this logic here + let env = tcx.erase_regions(param_env).with_reveal_all_normalized(tcx); + let erased_uv = tcx.erase_regions(uv); + + use rustc_middle::mir::interpret::ErrorHandled; + match tcx.const_eval_resolve_for_typeck(env, erased_uv, DUMMY_SP) { + Ok(Ok(val)) => Ok(ty::Const::new_value( + tcx, + val, + tcx.type_of(uv.def).instantiate(tcx, uv.args), + )), + Ok(Err(_)) => { + let e = tcx.dcx().delayed_bug( + "Type system constant with non valtree'able type evaluated but no error emitted", + ); + Err(EvaluateConstErr::InvalidConstParamTy(e)) + } + Err(ErrorHandled::Reported(info, _)) => { + Err(EvaluateConstErr::EvaluationFailure(info.into())) + } + Err(ErrorHandled::TooGeneric(_)) => Err(EvaluateConstErr::HasGenericsOrInfers), + } + } + } +} + +/// Replaces args that reference param or infer variables with suitable +/// placeholders. This function is meant to remove these param and infer +/// args when they're not actually needed to evaluate a constant. +fn replace_param_and_infer_args_with_placeholder<'tcx>( + tcx: TyCtxt<'tcx>, + args: GenericArgsRef<'tcx>, +) -> GenericArgsRef<'tcx> { + struct ReplaceParamAndInferWithPlaceholder<'tcx> { + tcx: TyCtxt<'tcx>, + idx: u32, + } + + impl<'tcx> TypeFolder> for ReplaceParamAndInferWithPlaceholder<'tcx> { + fn cx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + if let ty::Infer(_) = t.kind() { + let idx = { + let idx = self.idx; + self.idx += 1; + idx + }; + Ty::new_placeholder(self.tcx, ty::PlaceholderType { + universe: ty::UniverseIndex::ROOT, + bound: ty::BoundTy { + var: ty::BoundVar::from_u32(idx), + kind: ty::BoundTyKind::Anon, + }, + }) + } else { + t.super_fold_with(self) + } + } + + fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> { + if let ty::ConstKind::Infer(_) = c.kind() { + ty::Const::new_placeholder(self.tcx, ty::PlaceholderConst { + universe: ty::UniverseIndex::ROOT, + bound: ty::BoundVar::from_u32({ + let idx = self.idx; + self.idx += 1; + idx + }), + }) + } else { + c.super_fold_with(self) + } + } + } + + args.fold_with(&mut ReplaceParamAndInferWithPlaceholder { tcx, idx: 0 }) +} + /// Normalizes the predicates and checks whether they hold in an empty environment. If this /// returns true, then either normalize encountered an error or one of the predicates did not /// hold. Used when creating vtables to check for unsatisfiable methods. This should not be diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index 954dfe93387de..5c38d162712e6 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -418,8 +418,9 @@ impl<'a, 'b, 'tcx> TypeFolder> for AssocTypeNormalizer<'a, 'b, 'tcx self.selcx.infcx, &mut self.universes, constant, - |constant| constant.normalize_internal(tcx, self.param_env), + |constant| super::evaluate_const(self.selcx.infcx, constant, self.param_env), ) + .super_fold_with(self) } } diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index a8d701a750dd0..5f89894fb82ed 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -342,7 +342,7 @@ impl<'a, 'tcx> FallibleTypeFolder> for QueryNormalizer<'a, 'tcx> { self.infcx, &mut self.universes, constant, - |constant| constant.normalize_internal(self.infcx.tcx, self.param_env), + |constant| crate::traits::evaluate_const(&self.infcx, constant, self.param_env), ); debug!(?constant, ?self.param_env); constant.try_super_fold_with(self) diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 041cfc212670e..712856e6a8f2d 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -403,7 +403,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let mut assume = predicate.trait_ref.args.const_at(2); // FIXME(min_generic_const_exprs): We should shallowly normalize this. if self.tcx().features().generic_const_exprs() { - assume = assume.normalize_internal(self.tcx(), obligation.param_env); + assume = crate::traits::evaluate_const(self.infcx, assume, obligation.param_env) } let Some(assume) = rustc_transmute::Assume::from_const(self.infcx.tcx, obligation.param_env, assume) diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index cffb62ab559ba..5b4e895189b0e 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -22,7 +22,6 @@ use rustc_infer::infer::relate::TypeRelation; use rustc_infer::traits::{PredicateObligations, TraitObligation}; use rustc_middle::bug; use rustc_middle::dep_graph::{DepNodeIndex, dep_kinds}; -use rustc_middle::mir::interpret::ErrorHandled; pub use rustc_middle::traits::select::*; use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::TypeErrorToStringExt; @@ -50,7 +49,7 @@ use crate::infer::{InferCtxt, InferOk, TypeFreshener}; use crate::solve::InferCtxtSelectExt as _; use crate::traits::normalize::{normalize_with_depth, normalize_with_depth_to}; use crate::traits::project::{ProjectAndUnifyResult, ProjectionCacheKeyExt}; -use crate::traits::{ProjectionCacheKey, Unimplemented, effects}; +use crate::traits::{EvaluateConstErr, ProjectionCacheKey, Unimplemented, effects}; mod _match; mod candidate_assembly; @@ -931,11 +930,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } let evaluate = |c: ty::Const<'tcx>| { - if let ty::ConstKind::Unevaluated(unevaluated) = c.kind() { - match self.infcx.try_const_eval_resolve( + if let ty::ConstKind::Unevaluated(_) = c.kind() { + match crate::traits::try_evaluate_const( + self.infcx, + c, obligation.param_env, - unevaluated, - obligation.cause.span, ) { Ok(val) => Ok(val), Err(e) => Err(e), @@ -961,10 +960,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Err(_) => Ok(EvaluatedToErr), } } - (Err(ErrorHandled::Reported(..)), _) - | (_, Err(ErrorHandled::Reported(..))) => Ok(EvaluatedToErr), - (Err(ErrorHandled::TooGeneric(..)), _) - | (_, Err(ErrorHandled::TooGeneric(..))) => { + (Err(EvaluateConstErr::InvalidConstParamTy(..)), _) + | (_, Err(EvaluateConstErr::InvalidConstParamTy(..))) => Ok(EvaluatedToErr), + (Err(EvaluateConstErr::EvaluationFailure(..)), _) + | (_, Err(EvaluateConstErr::EvaluationFailure(..))) => Ok(EvaluatedToErr), + (Err(EvaluateConstErr::HasGenericsOrInfers), _) + | (_, Err(EvaluateConstErr::HasGenericsOrInfers)) => { if c1.has_non_region_infer() || c2.has_non_region_infer() { Ok(EvaluatedToAmbig) } else { diff --git a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs index 23b5f62b5ca49..f8e8f2176c182 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs @@ -83,7 +83,7 @@ impl<'tcx> At<'_, 'tcx> { Ok(self.infcx.resolve_vars_if_possible(new_infer_ct)) } else if self.infcx.tcx.features().generic_const_exprs() { - Ok(ct.normalize_internal(self.infcx.tcx, self.param_env)) + Ok(super::evaluate_const(&self.infcx, ct, self.param_env)) } else { Ok(self.normalize(ct).into_value_registering_obligations(self.infcx, fulfill_cx)) } diff --git a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs index aa8b4d88d2f27..44b9d6adaaab1 100644 --- a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs +++ b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs @@ -56,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeConstArrays { && !item.span.from_expansion() && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity() && let ty::Array(element_type, cst) = ty.kind() - && let Ok((_, ty::ValTree::Leaf(element_count))) = cst.eval_valtree(cx.tcx, ParamEnv::empty(), item.span) + && let Some((ty::ValTree::Leaf(element_count), _)) = cx.tcx.try_normalize_erasing_regions(ParamEnv::empty(), *cst).unwrap_or(*cst).try_to_valtree() && let element_count = element_count.to_target_usize(cx.tcx) && let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes()) && u128::from(self.maximum_allowed_size) < u128::from(element_count) * u128::from(element_size) diff --git a/tests/crashes/118545.rs b/tests/crashes/118545.rs deleted file mode 100644 index 1d4bb848bf04e..0000000000000 --- a/tests/crashes/118545.rs +++ /dev/null @@ -1,8 +0,0 @@ -//@ known-bug: #118545 -#![feature(generic_const_exprs)] - -struct Checked; - -fn foo() {} -const _: Checked = Checked::; -pub fn main() {} diff --git a/tests/crashes/128232.rs b/tests/crashes/128232.rs deleted file mode 100644 index 67f61e1b2402c..0000000000000 --- a/tests/crashes/128232.rs +++ /dev/null @@ -1,15 +0,0 @@ -//@ known-bug: #128232 - -#![feature(generic_const_exprs, unsized_const_params)] - -fn function() {} - -struct Wrapper; - -impl Wrapper<{ bar() }> { - fn call() {} -} - -fn main() { - Wrapper::::call; -} diff --git a/tests/ui/const-generics/adt_const_params/non_valtreeable_const_arg-1.rs b/tests/ui/const-generics/adt_const_params/non_valtreeable_const_arg-1.rs new file mode 100644 index 0000000000000..cea82722a0e60 --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/non_valtreeable_const_arg-1.rs @@ -0,0 +1,12 @@ +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] + +// Regression test for #118545 + +struct Checked; +//~^ ERROR: using function pointers as const generic parameters is forbidden + +fn foo() {} +const _: Checked = Checked::; + +pub fn main() {} diff --git a/tests/ui/const-generics/adt_const_params/non_valtreeable_const_arg-1.stderr b/tests/ui/const-generics/adt_const_params/non_valtreeable_const_arg-1.stderr new file mode 100644 index 0000000000000..71339411c847e --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/non_valtreeable_const_arg-1.stderr @@ -0,0 +1,10 @@ +error: using function pointers as const generic parameters is forbidden + --> $DIR/non_valtreeable_const_arg-1.rs:6:25 + | +LL | struct Checked; + | ^^^^ + | + = note: the only supported types are integers, `bool`, and `char` + +error: aborting due to 1 previous error + diff --git a/tests/ui/const-generics/adt_const_params/non_valtreeable_const_arg-2.rs b/tests/ui/const-generics/adt_const_params/non_valtreeable_const_arg-2.rs new file mode 100644 index 0000000000000..6b87ad86d4bc2 --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/non_valtreeable_const_arg-2.rs @@ -0,0 +1,19 @@ +#![feature(generic_const_exprs, unsized_const_params)] +#![allow(incomplete_features)] + +// Regression test for 128232 + +fn function() {} + +struct Wrapper; +//~^ ERROR: using function pointers as const generic parameters is forbidden + +impl Wrapper<{ bar() }> { + //~^ ERROR: cannot find function `bar` in this scope + fn call() {} +} + +fn main() { + Wrapper::::call; + //~^ ERROR: the function or associated item `call` exists for struct `Wrapper`, +} diff --git a/tests/ui/const-generics/adt_const_params/non_valtreeable_const_arg-2.stderr b/tests/ui/const-generics/adt_const_params/non_valtreeable_const_arg-2.stderr new file mode 100644 index 0000000000000..b13f76eabadbc --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/non_valtreeable_const_arg-2.stderr @@ -0,0 +1,39 @@ +error[E0425]: cannot find function `bar` in this scope + --> $DIR/non_valtreeable_const_arg-2.rs:11:16 + | +LL | impl Wrapper<{ bar() }> { + | ^^^ not found in this scope + | +help: you might be missing a const parameter + | +LL | impl Wrapper<{ bar() }> { + | +++++++++++++++++++++++ + +error[E0741]: using function pointers as const generic parameters is forbidden + --> $DIR/non_valtreeable_const_arg-2.rs:8:25 + | +LL | struct Wrapper; + | ^^^^ + +error[E0599]: the function or associated item `call` exists for struct `Wrapper`, but its trait bounds were not satisfied + --> $DIR/non_valtreeable_const_arg-2.rs:17:26 + | +LL | struct Wrapper; + | ----------------------------- function or associated item `call` not found for this struct because it doesn't satisfy `Wrapper: Fn<_>` +... +LL | Wrapper::::call; + | ^^^^ function or associated item cannot be called on `Wrapper` due to unsatisfied trait bounds + | + = note: the following trait bounds were not satisfied: + `Wrapper: Fn<_>` + which is required by `&Wrapper: Fn<_>` +note: the trait `Fn` must be implemented + --> $SRC_DIR/core/src/ops/function.rs:LL:COL + = help: items from traits can only be used if the trait is implemented and in scope + = note: the following trait defines an item `call`, perhaps you need to implement it: + candidate #1: `Fn` + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0425, E0599, E0741. +For more information about an error, try `rustc --explain E0425`. diff --git a/tests/ui/const-generics/generic_const_exprs/unify-op-with-fn-call.rs b/tests/ui/const-generics/generic_const_exprs/unify-op-with-fn-call.rs deleted file mode 100644 index 6c0ac639612cd..0000000000000 --- a/tests/ui/const-generics/generic_const_exprs/unify-op-with-fn-call.rs +++ /dev/null @@ -1,36 +0,0 @@ -//@ known-bug: #110395 -//@ compile-flags: -Znext-solver -#![feature(generic_const_exprs, adt_const_params, const_trait_impl)] -#![allow(incomplete_features)] - -// test `N + N` unifies with explicit function calls for non-builtin-types -#[derive(PartialEq, Eq)] -struct Foo(u8); - -impl const std::ops::Add for Foo { - type Output = Self; - - fn add(self, rhs: Self) -> Self::Output { - self - } -} - -struct Evaluatable; - -fn foo(a: Evaluatable<{ N + N }>) { - bar::<{ std::ops::Add::add(N, N) }>(); -} - -fn bar() {} - -// test that `N + N` unifies with explicit function calls for builin-types -struct Evaluatable2; - -fn foo2(a: Evaluatable2<{ N + N }>) { - bar2::<{ std::ops::Add::add(N, N) }>(); - // FIXME(generic_const_exprs) make this not an error -} - -fn bar2() {} - -fn main() {} diff --git a/tests/ui/const-generics/generic_const_exprs/unify-op-with-fn-call.stderr b/tests/ui/const-generics/generic_const_exprs/unify-op-with-fn-call.stderr deleted file mode 100644 index 882fdf0b228e8..0000000000000 --- a/tests/ui/const-generics/generic_const_exprs/unify-op-with-fn-call.stderr +++ /dev/null @@ -1,93 +0,0 @@ -error: `-Znext-solver=globally` and `generic_const_exprs` are incompatible, using them at the same time is not allowed - --> $DIR/unify-op-with-fn-call.rs:3:12 - | -LL | #![feature(generic_const_exprs, adt_const_params, const_trait_impl)] - | ^^^^^^^^^^^^^^^^^^^ - | - = help: remove one of these features - -error: const `impl` for trait `Add` which is not marked with `#[const_trait]` - --> $DIR/unify-op-with-fn-call.rs:10:12 - | -LL | impl const std::ops::Add for Foo { - | ^^^^^^^^^^^^^ - | - = note: marking a trait with `#[const_trait]` ensures all default method bodies are `const` - = note: adding a non-const method body in the future would be a breaking change - -error[E0741]: `Foo` must implement `ConstParamTy` to be used as the type of a const generic parameter - --> $DIR/unify-op-with-fn-call.rs:18:29 - | -LL | struct Evaluatable; - | ^^^ - | -help: add `#[derive(ConstParamTy)]` to the struct - | -LL + #[derive(ConstParamTy)] -LL | struct Foo(u8); - | - -error[E0015]: cannot call non-const operator in constants - --> $DIR/unify-op-with-fn-call.rs:20:39 - | -LL | fn foo(a: Evaluatable<{ N + N }>) { - | ^^^^^ - | - = note: calls in constants are limited to constant functions, tuple structs and tuple variants - -error[E0741]: `Foo` must implement `ConstParamTy` to be used as the type of a const generic parameter - --> $DIR/unify-op-with-fn-call.rs:20:17 - | -LL | fn foo(a: Evaluatable<{ N + N }>) { - | ^^^ - | -help: add `#[derive(ConstParamTy)]` to the struct - | -LL + #[derive(ConstParamTy)] -LL | struct Foo(u8); - | - -error[E0741]: `Foo` must implement `ConstParamTy` to be used as the type of a const generic parameter - --> $DIR/unify-op-with-fn-call.rs:24:17 - | -LL | fn bar() {} - | ^^^ - | -help: add `#[derive(ConstParamTy)]` to the struct - | -LL + #[derive(ConstParamTy)] -LL | struct Foo(u8); - | - -error[E0284]: type annotations needed: cannot normalize `foo2::{constant#0}` - --> $DIR/unify-op-with-fn-call.rs:29:28 - | -LL | fn foo2(a: Evaluatable2<{ N + N }>) { - | ^^^^^^^^^^^^^^^^^^^^^^^ cannot normalize `foo2::{constant#0}` - -error[E0015]: cannot call non-const fn `::add` in constants - --> $DIR/unify-op-with-fn-call.rs:21:13 - | -LL | bar::<{ std::ops::Add::add(N, N) }>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: calls in constants are limited to constant functions, tuple structs and tuple variants - -error[E0015]: cannot call non-const fn `::add` in constants - --> $DIR/unify-op-with-fn-call.rs:30:14 - | -LL | bar2::<{ std::ops::Add::add(N, N) }>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: calls in constants are limited to constant functions, tuple structs and tuple variants - -error[E0284]: type annotations needed: cannot normalize `foo2::{constant#0}` - --> $DIR/unify-op-with-fn-call.rs:30:12 - | -LL | bar2::<{ std::ops::Add::add(N, N) }>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot normalize `foo2::{constant#0}` - -error: aborting due to 10 previous errors - -Some errors have detailed explanations: E0015, E0284, E0741. -For more information about an error, try `rustc --explain E0015`. diff --git a/tests/ui/traits/const-traits/const-trait-bounds.rs b/tests/ui/traits/const-traits/const-trait-bounds.rs deleted file mode 100644 index 8722d9909edda..0000000000000 --- a/tests/ui/traits/const-traits/const-trait-bounds.rs +++ /dev/null @@ -1,32 +0,0 @@ -//@ known-bug: #110395 -//@ compile-flags: -Znext-solver -// FIXME(const_trait_impl): check-pass -#![feature(const_trait_impl, generic_const_exprs)] -#![allow(incomplete_features)] - -fn main() { - let _ = process::<()>([()]); - let _ = Struct::<(), 4> { field: [1, 0] }; -} - -fn process(input: [(); T::make(2)]) -> [(); T::make(2)] { - input -} - -struct Struct -where - [u32; T::make(P)]:, -{ - field: [u32; T::make(P)], -} - -#[const_trait] -trait Trait { - fn make(input: usize) -> usize; -} - -impl const Trait for () { - fn make(input: usize) -> usize { - input / 2 - } -} diff --git a/tests/ui/traits/const-traits/const-trait-bounds.stderr b/tests/ui/traits/const-traits/const-trait-bounds.stderr deleted file mode 100644 index 29a01b9d7dcef..0000000000000 --- a/tests/ui/traits/const-traits/const-trait-bounds.stderr +++ /dev/null @@ -1,29 +0,0 @@ -error: `-Znext-solver=globally` and `generic_const_exprs` are incompatible, using them at the same time is not allowed - --> $DIR/const-trait-bounds.rs:4:30 - | -LL | #![feature(const_trait_impl, generic_const_exprs)] - | ^^^^^^^^^^^^^^^^^^^ - | - = help: remove one of these features - -error[E0284]: type annotations needed: cannot normalize `process::{constant#0}` - --> $DIR/const-trait-bounds.rs:12:35 - | -LL | fn process(input: [(); T::make(2)]) -> [(); T::make(2)] { - | ^^^^^^^^^^^^^^^^ cannot normalize `process::{constant#0}` - -error[E0284]: type annotations needed: cannot normalize `Struct::field::{constant#0}` - --> $DIR/const-trait-bounds.rs:20:12 - | -LL | field: [u32; T::make(P)], - | ^^^^^^^^^^^^^^^^^ cannot normalize `Struct::field::{constant#0}` - -error[E0284]: type annotations needed: cannot normalize `process::{constant#1}` - --> $DIR/const-trait-bounds.rs:13:5 - | -LL | input - | ^^^^^ cannot normalize `process::{constant#1}` - -error: aborting due to 4 previous errors - -For more information about this error, try `rustc --explain E0284`.