Rollup merge of #132927 - BoxyUwU:consolidate_type_system_const_eval, r=compiler-errors

Consolidate type system const evaluation under `traits::evaluate_const`

Part of #130704

Fixes #128232
Fixes #118545

Removes `ty::Const::{normalize_internal, eval_valtree}` and `InferCtxt::(try_)const_eval_resolve`, consolidating the associated logic into `evaluate_const` in `rustc_trait_selection`. This results in an API for `ty::Const` that is free of any normalization/evaluation functions that would be incorrect to use under `min_generic_const_args`/`associated_const_equality`/`generic_const_exprs` or, more generally, that would be incorrect to use in the presence of generic type system constants.

Moving this logic to `rustc_trait_selection` and out of `rustc_middle` is also a pre-requisite for ensuring that we do not evaluate constants whose where clauses do not hold.

From this point it should be relatively simple (hah) to implement more complex normalization of type system constants such as: checking wf'ness before invoking CTFE machinery, or being able to normalize const aliases that still refer to generic parameters.

r? `@compiler-errors`
This commit is contained in:
Matthias Krüger 2024-11-12 08:07:18 +01:00 committed by GitHub
commit ea61714d52
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 434 additions and 569 deletions

View File

@ -1477,7 +1477,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
}

View File

@ -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<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
/// 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<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> {
/// Given a [`hir::Block`], get the span of its last expression or
/// statement, peeling off any inner blocks.

View File

@ -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<ConstValue<'tcx>, 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, _) => {

View File

@ -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<TyCtxt<'tcx>>;
@ -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<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
pub fn to_valtree(self) -> (ty::ValTree<'tcx>, Ty<'tcx>) {
match self.kind() {

View File

@ -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 {

View File

@ -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<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);
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));
}
}

View File

@ -29,10 +29,10 @@ pub trait SolverDelegate: Deref<Target = <Self as SolverDelegate>::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: <Self::Interner as Interner>::ParamEnv,
unevaluated: ty::UnevaluatedConst<Self::Interner>,
uv: ty::UnevaluatedConst<Self::Interner>,
) -> Option<<Self::Interner as Interner>::Const>;
// 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.
// 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<I>,
uv: ty::UnevaluatedConst<I>,
) -> Option<I::Const> {
self.delegate.try_const_eval_resolve(param_env, unevaluated)
self.delegate.evaluate_const(param_env, uv)
}
pub(super) fn is_transmutable(

View File

@ -143,7 +143,7 @@ where
) -> QueryResult<I> {
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)

View File

@ -14,7 +14,7 @@ where
&mut self,
goal: Goal<I, ty::NormalizesTo<I>>,
) -> QueryResult<I> {
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),
) {

View File

@ -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<ty::Const<'tcx>> {
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,
}
}

View File

@ -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)
}

View File

@ -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(()),
}
}

View File

@ -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 {

View File

@ -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<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
/// 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

View File

@ -418,8 +418,9 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> 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)
}
}

View File

@ -342,7 +342,7 @@ impl<'a, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> 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)

View File

@ -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)

View File

@ -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 {

View File

@ -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))
}

View File

@ -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)

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`.