1
Fork 0

Consolidate type system const evaluation under traits::evaluate_const

mew
This commit is contained in:
Boxy 2024-11-12 02:54:03 +00:00
parent 81eef2d362
commit bea0148ac6
31 changed files with 434 additions and 569 deletions

View file

@ -1491,7 +1491,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} }
} }
} else if self.tcx.features().generic_const_exprs() { } 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 { } else {
ct ct
} }

View file

@ -25,10 +25,10 @@ use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_macros::extension; use rustc_macros::extension;
pub use rustc_macros::{TypeFoldable, TypeVisitable}; pub use rustc_macros::{TypeFoldable, TypeVisitable};
use rustc_middle::bug;
use rustc_middle::infer::canonical::{CanonicalQueryInput, CanonicalVarValues}; use rustc_middle::infer::canonical::{CanonicalQueryInput, CanonicalVarValues};
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableValue, ConstVidKey}; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableValue, ConstVidKey};
use rustc_middle::mir::ConstraintCategory; use rustc_middle::mir::ConstraintCategory;
use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult};
use rustc_middle::traits::select; use rustc_middle::traits::select;
pub use rustc_middle::ty::IntVarValue; pub use rustc_middle::ty::IntVarValue;
use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::error::{ExpectedFound, TypeError};
@ -40,7 +40,6 @@ use rustc_middle::ty::{
self, ConstVid, FloatVid, GenericArg, GenericArgKind, GenericArgs, GenericArgsRef, self, ConstVid, FloatVid, GenericArg, GenericArgKind, GenericArgs, GenericArgsRef,
GenericParamDefKind, InferConst, IntVid, Ty, TyCtxt, TyVid, TypingMode, GenericParamDefKind, InferConst, IntVid, Ty, TyCtxt, TyVid, TypingMode,
}; };
use rustc_middle::{bug, span_bug};
use rustc_span::Span; use rustc_span::Span;
use rustc_span::symbol::Symbol; use rustc_span::symbol::Symbol;
use rustc_type_ir::solve::Reveal; use rustc_type_ir::solve::Reveal;
@ -1279,84 +1278,6 @@ impl<'tcx> InferCtxt<'tcx> {
u u
} }
pub fn try_const_eval_resolve(
&self,
param_env: ty::ParamEnv<'tcx>,
unevaluated: ty::UnevaluatedConst<'tcx>,
span: Span,
) -> Result<ty::Const<'tcx>, 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 `<A as B>::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<T>() { let _ = [0; bar::<T>()]; }` the repeat count
/// constant `bar::<T>()` 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 /// The returned function is used in a fast path. If it returns `true` the variable is
/// unchanged, `false` indicates that the status is unknown. /// unchanged, `false` indicates that the status is unknown.
#[inline] #[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<TyCtxt<'tcx>> 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> { impl<'tcx> InferCtxt<'tcx> {
/// Given a [`hir::Block`], get the span of its last expression or /// Given a [`hir::Block`], get the span of its last expression or
/// statement, peeling off any inner blocks. /// statement, peeling off any inner blocks.

View file

@ -1,17 +1,17 @@
use std::fmt::{self, Debug, Display, Formatter}; use std::fmt::{self, Debug, Display, Formatter};
use either::Either;
use rustc_abi::{HasDataLayout, Size}; use rustc_abi::{HasDataLayout, Size};
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_macros::{HashStable, Lift, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use rustc_macros::{HashStable, Lift, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
use rustc_session::RemapFileNameExt; use rustc_session::RemapFileNameExt;
use rustc_session::config::RemapPathScopeComponents; use rustc_session::config::RemapPathScopeComponents;
use rustc_span::{DUMMY_SP, Span}; 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::interpret::{AllocId, ConstAllocation, ErrorHandled, Scalar, alloc_range};
use crate::mir::{Promoted, pretty_print_const_value}; use crate::mir::{Promoted, pretty_print_const_value};
use crate::ty::print::{pretty_print_const, with_no_trimmed_paths}; 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 /// Evaluated Constants
@ -319,15 +319,13 @@ impl<'tcx> Const<'tcx> {
) -> Result<ConstValue<'tcx>, ErrorHandled> { ) -> Result<ConstValue<'tcx>, ErrorHandled> {
match self { match self {
Const::Ty(_, c) => { Const::Ty(_, c) => {
// We want to consistently have a "clean" value for type system constants (i.e., no if c.has_non_region_param() {
// data hidden in the padding), so we always go through a valtree here. return Err(ErrorHandled::TooGeneric(span));
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 match c.kind() {
.dcx() ConstKind::Value(ty, val) => Ok(tcx.valtree_to_const_val((ty, val))),
.delayed_bug("`mir::Const::eval` called on a non-valtree-compatible type") _ => Err(tcx.dcx().delayed_bug("Unevaluated `ty::Const` in MIR body").into()),
.into()),
Err(Either::Right(e)) => Err(e),
} }
} }
Const::Unevaluated(uneval, _) => { Const::Unevaluated(uneval, _) => {

View file

@ -1,4 +1,3 @@
use either::Either;
use rustc_data_structures::intern::Interned; use rustc_data_structures::intern::Interned;
use rustc_error_messages::MultiSpan; use rustc_error_messages::MultiSpan;
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
@ -9,7 +8,7 @@ use rustc_type_ir::{self as ir, TypeFlags, WithCachedTypeInfo};
use tracing::{debug, instrument}; use tracing::{debug, instrument};
use crate::middle::resolve_bound_vars as rbv; 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}; use crate::ty::{self, GenericArgs, ParamEnv, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt};
mod int; mod int;
@ -18,7 +17,7 @@ mod valtree;
pub use int::*; pub use int::*;
pub use kind::*; pub use kind::*;
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; use rustc_span::{DUMMY_SP, ErrorGuaranteed};
pub use valtree::*; pub use valtree::*;
pub type ConstKind<'tcx> = ir::ConstKind<TyCtxt<'tcx>>; pub type ConstKind<'tcx> = ir::ConstKind<TyCtxt<'tcx>>;
@ -363,60 +362,6 @@ impl<'tcx> Const<'tcx> {
Self::from_bits(tcx, n as u128, ParamEnv::empty().and(tcx.types.usize)) 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<Ty<'tcx>, 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 /// Panics if self.kind != ty::ConstKind::Value
pub fn to_valtree(self) -> (ty::ValTree<'tcx>, Ty<'tcx>) { pub fn to_valtree(self) -> (ty::ValTree<'tcx>, Ty<'tcx>) {
match self.kind() { match self.kind() {

View file

@ -1,46 +1,12 @@
use std::assert_matches::assert_matches; 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 super::Const;
use crate::mir; use crate::mir;
use crate::ty::abstract_const::CastKind; use crate::ty::abstract_const::CastKind;
use crate::ty::visit::TypeVisitableExt as _;
use crate::ty::{self, Ty, TyCtxt}; 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(Copy, Clone, Eq, PartialEq, Hash, Debug)]
#[derive(HashStable, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable)] #[derive(HashStable, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable)]
pub enum ExprKind { pub enum ExprKind {

View file

@ -1,14 +1,13 @@
use either::Either;
use rustc_abi::{FieldIdx, VariantIdx}; use rustc_abi::{FieldIdx, VariantIdx};
use rustc_apfloat::Float; use rustc_apfloat::Float;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_index::Idx; use rustc_index::Idx;
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
use rustc_infer::traits::Obligation; use rustc_infer::traits::Obligation;
use rustc_middle::mir;
use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::mir::interpret::ErrorHandled;
use rustc_middle::thir::{FieldPat, Pat, PatKind}; use rustc_middle::thir::{FieldPat, Pat, PatKind};
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypingMode, ValTree}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypingMode, ValTree};
use rustc_middle::{mir, span_bug};
use rustc_span::Span; use rustc_span::Span;
use rustc_trait_selection::traits::ObligationCause; use rustc_trait_selection::traits::ObligationCause;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; 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. // of opaques defined in this function here.
let infcx = self.tcx.infer_ctxt().build(TypingMode::non_body_analysis()); let infcx = self.tcx.infer_ctxt().build(TypingMode::non_body_analysis());
let mut convert = ConstToPat::new(self, id, span, infcx); 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) ty.is_structural_eq_shallow(self.infcx.tcx)
} }
fn to_pat(&mut self, c: ty::Const<'tcx>, ty: Ty<'tcx>) -> Box<Pat<'tcx>> { fn unevaluated_to_pat(
&mut self,
uv: ty::UnevaluatedConst<'tcx>,
ty: Ty<'tcx>,
) -> Box<Pat<'tcx>> {
trace!(self.treat_byte_string_as_slice); trace!(self.treat_byte_string_as_slice);
let pat_from_kind = |kind| Box::new(Pat { span: self.span, ty, kind }); 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. // It's not *technically* correct to be revealing opaque types here as borrowcheck has
let valtree = match c.eval_valtree(self.tcx(), self.param_env, self.span) { // not run yet. However, CTFE itself uses `Reveal::All` unconditionally even during
Ok((_, valtree)) => valtree, // typeck and not doing so has a lot of (undesirable) fallout (#101478, #119821). As a
Err(Either::Right(e)) => { // result we always use a revealed env when resolving the instance to evaluate.
let err = match e { //
ErrorHandled::Reported(..) => { // 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's tell the use where this failing const occurs.
self.tcx().dcx().emit_err(CouldNotEvalConstPattern { span: self.span }) let e = self.tcx().dcx().emit_err(CouldNotEvalConstPattern { span: self.span });
return pat_from_kind(PatKind::Error(e));
} }
ErrorHandled::TooGeneric(_) => self Err(ErrorHandled::TooGeneric(_)) => {
let e = self
.tcx() .tcx()
.dcx() .dcx()
.emit_err(ConstPatternDependsOnGenericParameter { span: self.span }), .emit_err(ConstPatternDependsOnGenericParameter { span: self.span });
}; return pat_from_kind(PatKind::Error(e));
return pat_from_kind(PatKind::Error(err));
} }
Err(Either::Left(bad_ty)) => { Ok(Err(bad_ty)) => {
// The pattern cannot be turned into a valtree. // The pattern cannot be turned into a valtree.
let e = match bad_ty.kind() { let e = match bad_ty.kind() {
ty::Adt(def, ..) => { ty::Adt(def, ..) => {
@ -128,8 +147,7 @@ impl<'tcx> ConstToPat<'tcx> {
if !self.type_has_partial_eq_impl(ty) { if !self.type_has_partial_eq_impl(ty) {
let err = TypeNotPartialEq { span: self.span, non_peq_ty: ty }; let err = TypeNotPartialEq { span: self.span, non_peq_ty: ty };
let e = self.tcx().dcx().emit_err(err); let e = self.tcx().dcx().emit_err(err);
let kind = PatKind::Error(e); return pat_from_kind(PatKind::Error(e));
return Box::new(Pat { span: self.span, ty, kind });
} }
} }

View file

@ -29,10 +29,10 @@ pub trait SolverDelegate: Deref<Target = <Self as SolverDelegate>::Infcx> + Size
// FIXME: Uplift the leak check into this crate. // FIXME: Uplift the leak check into this crate.
fn leak_check(&self, max_input_universe: ty::UniverseIndex) -> Result<(), NoSolution>; fn leak_check(&self, max_input_universe: ty::UniverseIndex) -> Result<(), NoSolution>;
fn try_const_eval_resolve( fn evaluate_const(
&self, &self,
param_env: <Self::Interner as Interner>::ParamEnv, param_env: <Self::Interner as Interner>::ParamEnv,
unevaluated: ty::UnevaluatedConst<Self::Interner>, uv: ty::UnevaluatedConst<Self::Interner>,
) -> Option<<Self::Interner as Interner>::Const>; ) -> Option<<Self::Interner as Interner>::Const>;
// FIXME: This only is here because `wf::obligations` is in `rustc_trait_selection`! // FIXME: This only is here because `wf::obligations` is in `rustc_trait_selection`!

View file

@ -1001,12 +1001,12 @@ where
// Try to evaluate a const, or return `None` if the const is too generic. // 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 // This doesn't mean the const isn't evaluatable, though, and should be treated
// as an ambiguity rather than no-solution. // as an ambiguity rather than no-solution.
pub(super) fn try_const_eval_resolve( pub(super) fn evaluate_const(
&self, &self,
param_env: I::ParamEnv, param_env: I::ParamEnv,
unevaluated: ty::UnevaluatedConst<I>, uv: ty::UnevaluatedConst<I>,
) -> Option<I::Const> { ) -> Option<I::Const> {
self.delegate.try_const_eval_resolve(param_env, unevaluated) self.delegate.evaluate_const(param_env, uv)
} }
pub(super) fn is_transmutable( pub(super) fn is_transmutable(

View file

@ -143,7 +143,7 @@ where
) -> QueryResult<I> { ) -> QueryResult<I> {
match ct.kind() { match ct.kind() {
ty::ConstKind::Unevaluated(uv) => { 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 itself when failing to evaluate, so emitting an additional fulfillment
// error in that case is unnecessary noise. This may change in the future once // 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 // evaluation failures are allowed to impact selection, e.g. generic const
@ -151,7 +151,7 @@ where
// FIXME(generic_const_exprs): Implement handling for generic // FIXME(generic_const_exprs): Implement handling for generic
// const expressions here. // 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) self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
} else { } else {
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)

View file

@ -14,7 +14,7 @@ where
&mut self, &mut self,
goal: Goal<I, ty::NormalizesTo<I>>, goal: Goal<I, ty::NormalizesTo<I>>,
) -> QueryResult<I> { ) -> QueryResult<I> {
if let Some(normalized_const) = self.try_const_eval_resolve( if let Some(normalized_const) = self.evaluate_const(
goal.param_env, goal.param_env,
ty::UnevaluatedConst::new(goal.predicate.alias.def_id, goal.predicate.alias.args), ty::UnevaluatedConst::new(goal.predicate.alias.def_id, goal.predicate.alias.args),
) { ) {

View file

@ -15,7 +15,7 @@ use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span};
use rustc_type_ir::TypingMode; use rustc_type_ir::TypingMode;
use rustc_type_ir::solve::{Certainty, NoSolution}; use rustc_type_ir::solve::{Certainty, NoSolution};
use crate::traits::specialization_graph; use crate::traits::{EvaluateConstErr, specialization_graph};
#[repr(transparent)] #[repr(transparent)]
pub struct SolverDelegate<'tcx>(InferCtxt<'tcx>); 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) self.0.leak_check(max_input_universe, None).map_err(|_| NoSolution)
} }
fn try_const_eval_resolve( fn evaluate_const(
&self, &self,
param_env: ty::ParamEnv<'tcx>, param_env: ty::ParamEnv<'tcx>,
unevaluated: ty::UnevaluatedConst<'tcx>, uv: ty::UnevaluatedConst<'tcx>,
) -> Option<ty::Const<'tcx>> { ) -> Option<ty::Const<'tcx>> {
use rustc_middle::mir::interpret::ErrorHandled; let ct = ty::Const::new_unevaluated(self.tcx, uv);
match self.const_eval_resolve(param_env, unevaluated, DUMMY_SP) {
Ok(Ok(val)) => Some(ty::Const::new_value( match crate::traits::try_evaluate_const(&self.0, ct, param_env) {
self.tcx, Ok(ct) => Some(ct),
val, Err(EvaluateConstErr::EvaluationFailure(e)) => Some(ty::Const::new_error(self.tcx, e)),
self.tcx.type_of(unevaluated.def).instantiate(self.tcx, unevaluated.args), Err(
)), EvaluateConstErr::InvalidConstParamTy(_) | EvaluateConstErr::HasGenericsOrInfers,
Ok(Err(_)) | Err(ErrorHandled::TooGeneric(_)) => None, ) => None,
Err(ErrorHandled::Reported(e, _)) => Some(ty::Const::new_error(self.tcx, e.into())),
} }
} }

View file

@ -7,7 +7,6 @@ use std::iter;
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet, IndexEntry}; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet, IndexEntry};
use rustc_data_structures::unord::UnordSet; use rustc_data_structures::unord::UnordSet;
use rustc_infer::infer::DefineOpaqueTypes; use rustc_infer::infer::DefineOpaqueTypes;
use rustc_middle::mir::interpret::ErrorHandled;
use rustc_middle::ty::{Region, RegionVid}; use rustc_middle::ty::{Region, RegionVid};
use tracing::debug; use tracing::debug;
use ty::TypingMode; use ty::TypingMode;
@ -761,23 +760,20 @@ impl<'tcx> AutoTraitFinder<'tcx> {
ty::PredicateKind::ConstEquate(c1, c2) => { ty::PredicateKind::ConstEquate(c1, c2) => {
let evaluate = |c: ty::Const<'tcx>| { let evaluate = |c: ty::Const<'tcx>| {
if let ty::ConstKind::Unevaluated(unevaluated) = c.kind() { 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, obligation.param_env,
unevaluated, );
obligation.cause.span,
) { if let Err(EvaluateConstErr::InvalidConstParamTy(_)) = ct {
Ok(Ok(valtree)) => Ok(ty::Const::new_value(selcx.tcx(),valtree, self.tcx.type_of(unevaluated.def).instantiate(self.tcx, unevaluated.args))), self.tcx.dcx().emit_err(UnableToConstructConstantValue {
Ok(Err(_)) => { span: self.tcx.def_span(unevaluated.def),
let tcx = self.tcx;
let reported =
tcx.dcx().emit_err(UnableToConstructConstantValue {
span: tcx.def_span(unevaluated.def),
unevaluated, unevaluated,
}); });
Err(ErrorHandled::Reported(reported.into(), tcx.def_span(unevaluated.def)))
}
Err(err) => Err(err),
} }
ct
} else { } else {
Ok(c) Ok(c)
} }

View file

@ -12,13 +12,13 @@
use rustc_hir::def::DefKind; use rustc_hir::def::DefKind;
use rustc_infer::infer::InferCtxt; use rustc_infer::infer::InferCtxt;
use rustc_middle::bug; use rustc_middle::bug;
use rustc_middle::mir::interpret::ErrorHandled;
use rustc_middle::traits::ObligationCause; use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::abstract_const::NotConstEvaluatable;
use rustc_middle::ty::{self, TyCtxt, TypeVisitable, TypeVisitableExt, TypeVisitor}; use rustc_middle::ty::{self, TyCtxt, TypeVisitable, TypeVisitableExt, TypeVisitor};
use rustc_span::{DUMMY_SP, Span}; use rustc_span::{DUMMY_SP, Span};
use tracing::{debug, instrument}; use tracing::{debug, instrument};
use super::EvaluateConstErr;
use crate::traits::ObligationCtxt; use crate::traits::ObligationCtxt;
/// Check if a given constant can be evaluated. /// Check if a given constant can be evaluated.
@ -68,16 +68,18 @@ pub fn is_const_evaluatable<'tcx>(
// here. // here.
tcx.dcx().span_bug(span, "evaluating `ConstKind::Expr` is not currently supported"); tcx.dcx().span_bug(span, "evaluating `ConstKind::Expr` is not currently supported");
} }
ty::ConstKind::Unevaluated(uv) => { ty::ConstKind::Unevaluated(_) => {
let concrete = infcx.const_eval_resolve(param_env, uv, span); match crate::traits::try_evaluate_const(infcx, unexpanded_ct, param_env) {
match concrete { Err(EvaluateConstErr::HasGenericsOrInfers) => {
Err(ErrorHandled::TooGeneric(_)) => {
Err(NotConstEvaluatable::Error(infcx.dcx().span_delayed_bug( Err(NotConstEvaluatable::Error(infcx.dcx().span_delayed_bug(
span, span,
"Missing value for constant, but no error reported?", "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(()), Ok(_) => Ok(()),
} }
} }
@ -92,16 +94,7 @@ pub fn is_const_evaluatable<'tcx>(
_ => bug!("unexpected constkind in `is_const_evalautable: {unexpanded_ct:?}`"), _ => 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 match crate::traits::try_evaluate_const(infcx, unexpanded_ct, param_env) {
// 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 {
// If we're evaluating a generic foreign constant, under a nightly compiler while // If we're evaluating a generic foreign constant, under a nightly compiler while
// the current crate does not enable `feature(generic_const_exprs)`, abort // the current crate does not enable `feature(generic_const_exprs)`, abort
// compilation with a useful error. // compilation with a useful error.
@ -130,7 +123,7 @@ pub fn is_const_evaluatable<'tcx>(
.emit() .emit()
} }
Err(ErrorHandled::TooGeneric(_)) => { Err(EvaluateConstErr::HasGenericsOrInfers) => {
let err = if uv.has_non_region_infer() { let err = if uv.has_non_region_infer() {
NotConstEvaluatable::MentionsInfer NotConstEvaluatable::MentionsInfer
} else if uv.has_non_region_param() { } else if uv.has_non_region_param() {
@ -145,7 +138,9 @@ pub fn is_const_evaluatable<'tcx>(
Err(err) 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(()), Ok(_) => Ok(()),
} }
} }

View file

@ -10,7 +10,6 @@ use rustc_infer::traits::{
TraitEngine, TraitEngine,
}; };
use rustc_middle::bug; use rustc_middle::bug;
use rustc_middle::mir::interpret::ErrorHandled;
use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::abstract_const::NotConstEvaluatable;
use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::{self, Binder, Const, GenericArgsRef, TypeVisitableExt, TypingMode}; use rustc_middle::ty::{self, Binder, Const, GenericArgsRef, TypeVisitableExt, TypingMode};
@ -26,6 +25,7 @@ use super::{
}; };
use crate::error_reporting::InferCtxtErrorExt; use crate::error_reporting::InferCtxtErrorExt;
use crate::infer::{InferCtxt, TyOrConstInferVar}; use crate::infer::{InferCtxt, TyOrConstInferVar};
use crate::traits::EvaluateConstErr;
use crate::traits::normalize::normalize_with_depth_to; use crate::traits::normalize::normalize_with_depth_to;
use crate::traits::project::{PolyProjectionObligation, ProjectionCacheKeyExt as _}; use crate::traits::project::{PolyProjectionObligation, ProjectionCacheKeyExt as _};
use crate::traits::query::evaluate_obligation::InferCtxtExt; 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>| { let mut evaluate = |c: Const<'tcx>| {
if let ty::ConstKind::Unevaluated(unevaluated) = c.kind() { 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, obligation.param_env,
unevaluated,
obligation.cause.span,
) { ) {
Ok(val) => Ok(val), Ok(val) => Ok(val),
Err(e) => { e @ Err(EvaluateConstErr::HasGenericsOrInfers) => {
match e { stalled_on.extend(
ErrorHandled::TooGeneric(..) => { unevaluated
stalled_on.extend(unevaluated.args.iter().filter_map( .args
TyOrConstInferVar::maybe_from_generic_arg, .iter()
)); .filter_map(TyOrConstInferVar::maybe_from_generic_arg),
} );
_ => {} e
}
Err(e)
} }
e @ Err(
EvaluateConstErr::EvaluationFailure(_)
| EvaluateConstErr::InvalidConstParamTy(_),
) => e,
} }
} else { } else {
Ok(c) Ok(c)
@ -707,14 +709,20 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
} }
} }
} }
(Err(ErrorHandled::Reported(reported, _)), _) (Err(EvaluateConstErr::InvalidConstParamTy(e)), _)
| (_, Err(ErrorHandled::Reported(reported, _))) => ProcessResult::Error( | (_, Err(EvaluateConstErr::InvalidConstParamTy(e))) => {
FulfillmentErrorCode::Select(SelectionError::NotConstEvaluatable( ProcessResult::Error(FulfillmentErrorCode::Select(
NotConstEvaluatable::Error(reported.into()), SelectionError::NotConstEvaluatable(NotConstEvaluatable::Error(e)),
)), ))
), }
(Err(ErrorHandled::TooGeneric(_)), _) (Err(EvaluateConstErr::EvaluationFailure(e)), _)
| (_, Err(ErrorHandled::TooGeneric(_))) => { | (_, 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() { if c1.has_non_region_infer() || c2.has_non_region_infer() {
ProcessResult::Unchanged ProcessResult::Unchanged
} else { } else {

View file

@ -27,6 +27,7 @@ use std::fmt::Debug;
use std::ops::ControlFlow; use std::ops::ControlFlow;
use rustc_errors::ErrorGuaranteed; use rustc_errors::ErrorGuaranteed;
use rustc_hir::def::DefKind;
pub use rustc_infer::traits::*; pub use rustc_infer::traits::*;
use rustc_middle::query::Providers; use rustc_middle::query::Providers;
use rustc_middle::span_bug; 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::fold::TypeFoldable;
use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt}; use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt};
use rustc_middle::ty::{ use rustc_middle::ty::{
self, GenericArgs, GenericArgsRef, Ty, TyCtxt, TypeFolder, TypeSuperVisitable, TypingMode, self, GenericArgs, GenericArgsRef, Ty, TyCtxt, TypeFolder, TypeSuperFoldable,
Upcast, TypeSuperVisitable, TypingMode, Upcast,
}; };
use rustc_span::Span;
use rustc_span::def_id::DefId; use rustc_span::def_id::DefId;
use rustc_span::{DUMMY_SP, Span};
use tracing::{debug, instrument}; use tracing::{debug, instrument};
pub use self::coherence::{ pub use self::coherence::{
@ -366,11 +367,23 @@ pub fn normalize_param_env_or_error<'tcx>(
if c.has_escaping_bound_vars() { if c.has_escaping_bound_vars() {
return ty::Const::new_misc_error(self.0); return ty::Const::new_misc_error(self.0);
} }
// While it is pretty sus to be evaluating things with an empty param env, it // 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 // 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 // const arguments that have a non-empty param env are array repeat counts. These
// do not appear in the type system though. // 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()) 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<ty::Const<'tcx>, 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<TyCtxt<'tcx>> 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 /// 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 /// 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 /// hold. Used when creating vtables to check for unsatisfiable methods. This should not be

View file

@ -418,8 +418,9 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx
self.selcx.infcx, self.selcx.infcx,
&mut self.universes, &mut self.universes,
constant, constant,
|constant| constant.normalize_internal(tcx, self.param_env), |constant| super::evaluate_const(self.selcx.infcx, constant, self.param_env),
) )
.super_fold_with(self)
} }
} }

View file

@ -342,7 +342,7 @@ impl<'a, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'a, 'tcx> {
self.infcx, self.infcx,
&mut self.universes, &mut self.universes,
constant, 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); debug!(?constant, ?self.param_env);
constant.try_super_fold_with(self) constant.try_super_fold_with(self)

View file

@ -403,7 +403,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let mut assume = predicate.trait_ref.args.const_at(2); let mut assume = predicate.trait_ref.args.const_at(2);
// FIXME(min_generic_const_exprs): We should shallowly normalize this. // FIXME(min_generic_const_exprs): We should shallowly normalize this.
if self.tcx().features().generic_const_exprs() { 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) = let Some(assume) =
rustc_transmute::Assume::from_const(self.infcx.tcx, obligation.param_env, assume) rustc_transmute::Assume::from_const(self.infcx.tcx, obligation.param_env, assume)

View file

@ -22,7 +22,6 @@ use rustc_infer::infer::relate::TypeRelation;
use rustc_infer::traits::{PredicateObligations, TraitObligation}; use rustc_infer::traits::{PredicateObligations, TraitObligation};
use rustc_middle::bug; use rustc_middle::bug;
use rustc_middle::dep_graph::{DepNodeIndex, dep_kinds}; use rustc_middle::dep_graph::{DepNodeIndex, dep_kinds};
use rustc_middle::mir::interpret::ErrorHandled;
pub use rustc_middle::traits::select::*; pub use rustc_middle::traits::select::*;
use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::abstract_const::NotConstEvaluatable;
use rustc_middle::ty::error::TypeErrorToStringExt; use rustc_middle::ty::error::TypeErrorToStringExt;
@ -50,7 +49,7 @@ use crate::infer::{InferCtxt, InferOk, TypeFreshener};
use crate::solve::InferCtxtSelectExt as _; use crate::solve::InferCtxtSelectExt as _;
use crate::traits::normalize::{normalize_with_depth, normalize_with_depth_to}; use crate::traits::normalize::{normalize_with_depth, normalize_with_depth_to};
use crate::traits::project::{ProjectAndUnifyResult, ProjectionCacheKeyExt}; use crate::traits::project::{ProjectAndUnifyResult, ProjectionCacheKeyExt};
use crate::traits::{ProjectionCacheKey, Unimplemented, effects}; use crate::traits::{EvaluateConstErr, ProjectionCacheKey, Unimplemented, effects};
mod _match; mod _match;
mod candidate_assembly; mod candidate_assembly;
@ -931,11 +930,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
} }
let evaluate = |c: ty::Const<'tcx>| { let evaluate = |c: ty::Const<'tcx>| {
if let ty::ConstKind::Unevaluated(unevaluated) = c.kind() { if let ty::ConstKind::Unevaluated(_) = c.kind() {
match self.infcx.try_const_eval_resolve( match crate::traits::try_evaluate_const(
self.infcx,
c,
obligation.param_env, obligation.param_env,
unevaluated,
obligation.cause.span,
) { ) {
Ok(val) => Ok(val), Ok(val) => Ok(val),
Err(e) => Err(e), Err(e) => Err(e),
@ -961,10 +960,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
Err(_) => Ok(EvaluatedToErr), Err(_) => Ok(EvaluatedToErr),
} }
} }
(Err(ErrorHandled::Reported(..)), _) (Err(EvaluateConstErr::InvalidConstParamTy(..)), _)
| (_, Err(ErrorHandled::Reported(..))) => Ok(EvaluatedToErr), | (_, Err(EvaluateConstErr::InvalidConstParamTy(..))) => Ok(EvaluatedToErr),
(Err(ErrorHandled::TooGeneric(..)), _) (Err(EvaluateConstErr::EvaluationFailure(..)), _)
| (_, Err(ErrorHandled::TooGeneric(..))) => { | (_, Err(EvaluateConstErr::EvaluationFailure(..))) => Ok(EvaluatedToErr),
(Err(EvaluateConstErr::HasGenericsOrInfers), _)
| (_, Err(EvaluateConstErr::HasGenericsOrInfers)) => {
if c1.has_non_region_infer() || c2.has_non_region_infer() { if c1.has_non_region_infer() || c2.has_non_region_infer() {
Ok(EvaluatedToAmbig) Ok(EvaluatedToAmbig)
} else { } else {

View file

@ -83,7 +83,7 @@ impl<'tcx> At<'_, 'tcx> {
Ok(self.infcx.resolve_vars_if_possible(new_infer_ct)) Ok(self.infcx.resolve_vars_if_possible(new_infer_ct))
} else if self.infcx.tcx.features().generic_const_exprs() { } 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 { } else {
Ok(self.normalize(ct).into_value_registering_obligations(self.infcx, fulfill_cx)) Ok(self.normalize(ct).into_value_registering_obligations(self.infcx, fulfill_cx))
} }

View file

@ -56,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeConstArrays {
&& !item.span.from_expansion() && !item.span.from_expansion()
&& let ty = cx.tcx.type_of(item.owner_id).instantiate_identity() && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity()
&& let ty::Array(element_type, cst) = ty.kind() && 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 element_count = element_count.to_target_usize(cx.tcx)
&& let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes()) && 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) && u128::from(self.maximum_allowed_size) < u128::from(element_count) * u128::from(element_size)

View file

@ -1,8 +0,0 @@
//@ known-bug: #118545
#![feature(generic_const_exprs)]
struct Checked<const F: fn()>;
fn foo() {}
const _: Checked<foo> = Checked::<foo>;
pub fn main() {}

View file

@ -1,15 +0,0 @@
//@ known-bug: #128232
#![feature(generic_const_exprs, unsized_const_params)]
fn function() {}
struct Wrapper<const F: fn()>;
impl Wrapper<{ bar() }> {
fn call() {}
}
fn main() {
Wrapper::<function>::call;
}

View file

@ -0,0 +1,12 @@
#![feature(generic_const_exprs)]
#![allow(incomplete_features)]
// Regression test for #118545
struct Checked<const F: fn()>;
//~^ ERROR: using function pointers as const generic parameters is forbidden
fn foo() {}
const _: Checked<foo> = Checked::<foo>;
pub fn main() {}

View file

@ -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<const F: fn()>;
| ^^^^
|
= note: the only supported types are integers, `bool`, and `char`
error: aborting due to 1 previous error

View file

@ -0,0 +1,19 @@
#![feature(generic_const_exprs, unsized_const_params)]
#![allow(incomplete_features)]
// Regression test for 128232
fn function() {}
struct Wrapper<const F: fn()>;
//~^ 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::<function>::call;
//~^ ERROR: the function or associated item `call` exists for struct `Wrapper<function>`,
}

View file

@ -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<const bar: /* Type */> 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<const F: fn()>;
| ^^^^
error[E0599]: the function or associated item `call` exists for struct `Wrapper<function>`, but its trait bounds were not satisfied
--> $DIR/non_valtreeable_const_arg-2.rs:17:26
|
LL | struct Wrapper<const F: fn()>;
| ----------------------------- function or associated item `call` not found for this struct because it doesn't satisfy `Wrapper<function>: Fn<_>`
...
LL | Wrapper::<function>::call;
| ^^^^ function or associated item cannot be called on `Wrapper<function>` due to unsatisfied trait bounds
|
= note: the following trait bounds were not satisfied:
`Wrapper<function>: Fn<_>`
which is required by `&Wrapper<function>: 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`.

View file

@ -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<const N: Foo>;
fn foo<const N: Foo>(a: Evaluatable<{ N + N }>) {
bar::<{ std::ops::Add::add(N, N) }>();
}
fn bar<const N: Foo>() {}
// test that `N + N` unifies with explicit function calls for builin-types
struct Evaluatable2<const N: usize>;
fn foo2<const N: usize>(a: Evaluatable2<{ N + N }>) {
bar2::<{ std::ops::Add::add(N, N) }>();
// FIXME(generic_const_exprs) make this not an error
}
fn bar2<const N: usize>() {}
fn main() {}

View file

@ -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<const N: Foo>;
| ^^^
|
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<const N: 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<const N: 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<const N: Foo>() {}
| ^^^
|
help: add `#[derive(ConstParamTy)]` to the struct
|
LL + #[derive(ConstParamTy)]
LL | struct Foo(u8);
|
error[E0284]: type annotations needed: cannot normalize `foo2<N>::{constant#0}`
--> $DIR/unify-op-with-fn-call.rs:29:28
|
LL | fn foo2<const N: usize>(a: Evaluatable2<{ N + N }>) {
| ^^^^^^^^^^^^^^^^^^^^^^^ cannot normalize `foo2<N>::{constant#0}`
error[E0015]: cannot call non-const fn `<Foo as Add>::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 `<usize as Add>::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<N>::{constant#0}`
--> $DIR/unify-op-with-fn-call.rs:30:12
|
LL | bar2::<{ std::ops::Add::add(N, N) }>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot normalize `foo2<N>::{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`.

View file

@ -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<T: const Trait>(input: [(); T::make(2)]) -> [(); T::make(2)] {
input
}
struct Struct<T: const Trait, const P: usize>
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
}
}

View file

@ -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<T>::{constant#0}`
--> $DIR/const-trait-bounds.rs:12:35
|
LL | fn process<T: const Trait>(input: [(); T::make(2)]) -> [(); T::make(2)] {
| ^^^^^^^^^^^^^^^^ cannot normalize `process<T>::{constant#0}`
error[E0284]: type annotations needed: cannot normalize `Struct<T, P>::field::{constant#0}`
--> $DIR/const-trait-bounds.rs:20:12
|
LL | field: [u32; T::make(P)],
| ^^^^^^^^^^^^^^^^^ cannot normalize `Struct<T, P>::field::{constant#0}`
error[E0284]: type annotations needed: cannot normalize `process<T>::{constant#1}`
--> $DIR/const-trait-bounds.rs:13:5
|
LL | input
| ^^^^^ cannot normalize `process<T>::{constant#1}`
error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0284`.