Set groundwork for proper const normalization

This commit is contained in:
Boxy 2025-05-01 13:26:23 +01:00
parent 53e3907bcb
commit 238d113b07
18 changed files with 366 additions and 225 deletions

View file

@ -1,7 +1,8 @@
use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::{self, Ty};
use rustc_middle::ty;
use super::InferCtxt;
use crate::infer::Term;
use crate::traits::{Obligation, PredicateObligations};
impl<'tcx> InferCtxt<'tcx> {
@ -11,24 +12,32 @@ impl<'tcx> InferCtxt<'tcx> {
/// of the given projection. This allows us to proceed with projections
/// while they cannot be resolved yet due to missing information or
/// simply due to the lack of access to the trait resolution machinery.
pub fn projection_ty_to_infer(
pub fn projection_term_to_infer(
&self,
param_env: ty::ParamEnv<'tcx>,
projection_ty: ty::AliasTy<'tcx>,
alias_term: ty::AliasTerm<'tcx>,
cause: ObligationCause<'tcx>,
recursion_depth: usize,
obligations: &mut PredicateObligations<'tcx>,
) -> Ty<'tcx> {
) -> Term<'tcx> {
debug_assert!(!self.next_trait_solver());
let ty_var = self.next_ty_var(self.tcx.def_span(projection_ty.def_id));
let span = self.tcx.def_span(alias_term.def_id);
let infer_var = if alias_term.kind(self.tcx).is_type() {
self.next_ty_var(span).into()
} else {
self.next_const_var(span).into()
};
let projection =
ty::PredicateKind::Clause(ty::ClauseKind::Projection(ty::ProjectionPredicate {
projection_term: projection_ty.into(),
term: ty_var.into(),
projection_term: alias_term,
term: infer_var,
}));
let obligation =
Obligation::with_depth(self.tcx, cause, recursion_depth, param_env, projection);
obligations.push(obligation);
ty_var
infer_var
}
}

View file

@ -243,10 +243,18 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
ty::AliasTermKind::ProjectionTy
}
}
DefKind::AssocConst => {
if let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(alias.def_id))
{
ty::AliasTermKind::InherentConst
} else {
ty::AliasTermKind::ProjectionConst
}
}
DefKind::OpaqueTy => ty::AliasTermKind::OpaqueTy,
DefKind::TyAlias => ty::AliasTermKind::FreeTy,
DefKind::AssocConst => ty::AliasTermKind::ProjectionConst,
DefKind::AnonConst | DefKind::Const | DefKind::Ctor(_, CtorKind::Const) => {
DefKind::Const => ty::AliasTermKind::FreeConst,
DefKind::AnonConst | DefKind::Ctor(_, CtorKind::Const) => {
ty::AliasTermKind::UnevaluatedConst
}
kind => bug!("unexpected DefKind in AliasTy: {kind:?}"),

View file

@ -3195,7 +3195,7 @@ define_print! {
ty::AliasTerm<'tcx> {
match self.kind(cx.tcx()) {
ty::AliasTermKind::InherentTy => p!(pretty_print_inherent_projection(*self)),
ty::AliasTermKind::InherentTy | ty::AliasTermKind::InherentConst => p!(pretty_print_inherent_projection(*self)),
ty::AliasTermKind::ProjectionTy => {
if !(cx.should_print_verbose() || with_reduced_queries())
&& cx.tcx().is_impl_trait_in_trait(self.def_id)
@ -3205,7 +3205,8 @@ define_print! {
p!(print_def_path(self.def_id, self.args));
}
}
| ty::AliasTermKind::FreeTy
ty::AliasTermKind::FreeTy
| ty::AliasTermKind::FreeConst
| ty::AliasTermKind::OpaqueTy
| ty::AliasTermKind::UnevaluatedConst
| ty::AliasTermKind::ProjectionConst => {

View file

@ -966,7 +966,9 @@ impl<'tcx> TyCtxt<'tcx> {
}
ty::AliasTermKind::OpaqueTy => Some(self.variances_of(def_id)),
ty::AliasTermKind::InherentTy
| ty::AliasTermKind::InherentConst
| ty::AliasTermKind::FreeTy
| ty::AliasTermKind::FreeConst
| ty::AliasTermKind::UnevaluatedConst
| ty::AliasTermKind::ProjectionConst => None,
}

View file

@ -568,6 +568,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
// Lower the named constant to a THIR pattern.
let args = self.typeck_results.node_args(id);
// FIXME(mgca): we will need to special case IACs here to have type system compatible
// generic args, instead of how we represent them in body expressions.
let c = ty::Const::new_unevaluated(self.tcx, ty::UnevaluatedConst { def: def_id, args });
let mut pattern = self.const_to_pat(c, ty, id, span);

View file

@ -19,19 +19,25 @@ where
goal: Goal<I, ty::NormalizesTo<I>>,
) -> QueryResult<I> {
let cx = self.cx();
let free_ty = goal.predicate.alias;
let free_alias = goal.predicate.alias;
// Check where clauses
self.add_goals(
GoalSource::Misc,
cx.predicates_of(free_ty.def_id)
.iter_instantiated(cx, free_ty.args)
cx.predicates_of(free_alias.def_id)
.iter_instantiated(cx, free_alias.args)
.map(|pred| goal.with(cx, pred)),
);
let actual = cx.type_of(free_ty.def_id).instantiate(cx, free_ty.args);
self.instantiate_normalizes_to_term(goal, actual.into());
let actual = if free_alias.kind(cx).is_type() {
cx.type_of(free_alias.def_id).instantiate(cx, free_alias.args)
} else {
// FIXME(mgca): once const items are actual aliases defined as equal to type system consts
// this should instead return that.
panic!("normalizing free const aliases in the type system is unsupported");
};
self.instantiate_normalizes_to_term(goal, actual.into());
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
}

View file

@ -15,12 +15,12 @@ where
D: SolverDelegate<Interner = I>,
I: Interner,
{
pub(super) fn normalize_inherent_associated_type(
pub(super) fn normalize_inherent_associated_term(
&mut self,
goal: Goal<I, ty::NormalizesTo<I>>,
) -> QueryResult<I> {
let cx = self.cx();
let inherent = goal.predicate.alias.expect_ty(cx);
let inherent = goal.predicate.alias;
let impl_def_id = cx.parent(inherent.def_id);
let impl_args = self.fresh_args_for_item(impl_def_id);
@ -48,8 +48,13 @@ where
.map(|pred| goal.with(cx, pred)),
);
let normalized = cx.type_of(inherent.def_id).instantiate(cx, inherent_args);
self.instantiate_normalizes_to_term(goal, normalized.into());
let normalized = if inherent.kind(cx).is_type() {
cx.type_of(inherent.def_id).instantiate(cx, inherent_args).into()
} else {
// FIXME(mgca): Properly handle IACs in the type system
panic!("normalizing inherent associated consts in the type system is unsupported");
};
self.instantiate_normalizes_to_term(goal, normalized);
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
}

View file

@ -48,9 +48,13 @@ where
})
})
}
ty::AliasTermKind::InherentTy => self.normalize_inherent_associated_type(goal),
ty::AliasTermKind::InherentTy | ty::AliasTermKind::InherentConst => {
self.normalize_inherent_associated_term(goal)
}
ty::AliasTermKind::OpaqueTy => self.normalize_opaque_type(goal),
ty::AliasTermKind::FreeTy => self.normalize_free_alias(goal),
ty::AliasTermKind::FreeTy | ty::AliasTermKind::FreeConst => {
self.normalize_free_alias(goal)
}
ty::AliasTermKind::UnevaluatedConst => self.normalize_anon_const(goal),
}
}
@ -333,6 +337,8 @@ where
cx.type_of(target_item_def_id).map_bound(|ty| ty.into())
}
ty::AliasTermKind::ProjectionConst => {
// FIXME(mgca): once const items are actual aliases defined as equal to type system consts
// this should instead return that.
if cx.features().associated_const_equality() {
panic!("associated const projection is not supported yet")
} else {

View file

@ -51,7 +51,7 @@ pub use self::dyn_compatibility::{
pub use self::engine::{ObligationCtxt, TraitEngineExt};
pub use self::fulfill::{FulfillmentContext, OldSolverError, PendingPredicateObligation};
pub use self::normalize::NormalizeExt;
pub use self::project::{normalize_inherent_projection, normalize_projection_ty};
pub use self::project::{normalize_inherent_projection, normalize_projection_term};
pub use self::select::{
EvaluationCache, EvaluationResult, IntercrateAmbiguityCause, OverflowError, SelectionCache,
SelectionContext,

View file

@ -1,6 +1,7 @@
//! Deeply normalize types using the old trait solver.
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_hir::def::DefKind;
use rustc_infer::infer::at::At;
use rustc_infer::infer::{InferCtxt, InferOk};
use rustc_infer::traits::{
@ -10,15 +11,12 @@ use rustc_macros::extension;
use rustc_middle::span_bug;
use rustc_middle::traits::{ObligationCause, ObligationCauseCode};
use rustc_middle::ty::{
self, AliasTy, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable,
self, AliasTerm, Term, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable,
TypeVisitableExt, TypingMode,
};
use tracing::{debug, instrument};
use super::{
BoundVarReplacer, PlaceholderReplacer, SelectionContext, project,
with_replaced_escaping_bound_vars,
};
use super::{BoundVarReplacer, PlaceholderReplacer, SelectionContext, project};
use crate::error_reporting::InferCtxtErrorExt;
use crate::error_reporting::traits::OverflowCause;
use crate::solve::NextSolverError;
@ -179,8 +177,10 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> {
if !needs_normalization(self.selcx.infcx, &value) { value } else { value.fold_with(self) }
}
fn normalize_trait_projection(&mut self, data: AliasTy<'tcx>) -> Ty<'tcx> {
if !data.has_escaping_bound_vars() {
// FIXME(mgca): While this supports constants, it is only used for types by default right now
#[instrument(level = "debug", skip(self), ret)]
fn normalize_trait_projection(&mut self, proj: AliasTerm<'tcx>) -> Term<'tcx> {
if !proj.has_escaping_bound_vars() {
// When we don't have escaping bound vars we can normalize ambig aliases
// to inference variables (done in `normalize_projection_ty`). This would
// be wrong if there were escaping bound vars as even if we instantiated
@ -189,23 +189,15 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> {
//
// Also, as an optimization: when we don't have escaping bound vars, we don't
// need to replace them with placeholders (see branch below).
let data = data.fold_with(self);
let normalized_ty = project::normalize_projection_ty(
let proj = proj.fold_with(self);
project::normalize_projection_term(
self.selcx,
self.param_env,
data,
proj,
self.cause.clone(),
self.depth,
self.obligations,
);
debug!(
?self.depth,
?ty,
?normalized_ty,
obligations.len = ?self.obligations.len(),
"AssocTypeNormalizer: normalized type"
);
normalized_ty.expect_type()
)
} else {
// If there are escaping bound vars, we temporarily replace the
// bound vars with placeholders. Note though, that in the case
@ -218,72 +210,64 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> {
// Note: this isn't necessarily the final approach here; we may
// want to figure out how to register obligations with escaping vars
// or handle this some other way.
let infcx = self.selcx.infcx;
let (data, mapped_regions, mapped_types, mapped_consts) =
BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data);
let data = data.fold_with(self);
let normalized_ty = project::opt_normalize_projection_term(
let (proj, mapped_regions, mapped_types, mapped_consts) =
BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, proj);
let proj = proj.fold_with(self);
let normalized_term = project::opt_normalize_projection_term(
self.selcx,
self.param_env,
data.into(),
proj,
self.cause.clone(),
self.depth,
self.obligations,
)
.ok()
.flatten()
.map(|term| term.expect_type())
.map(|normalized_ty| {
PlaceholderReplacer::replace_placeholders(
infcx,
mapped_regions,
mapped_types,
mapped_consts,
&self.universes,
normalized_ty,
)
})
.unwrap_or_else(|| ty.super_fold_with(self));
.unwrap_or(proj.to_term(infcx.tcx));
debug!(
?self.depth,
?ty,
?normalized_ty,
obligations.len = ?self.obligations.len(),
"AssocTypeNormalizer: normalized type"
);
normalized_ty
PlaceholderReplacer::replace_placeholders(
infcx,
mapped_regions,
mapped_types,
mapped_consts,
&self.universes,
normalized_term,
)
}
}
fn normalize_inherent_projection(&mut self, data: AliasTy<'tcx>) -> Ty<'tcx> {
if !data.has_escaping_bound_vars() {
// This branch is *mostly* just an optimization: when we don't
// have escaping bound vars, we don't need to replace them with
// placeholders (see branch below). *Also*, we know that we can
// register an obligation to *later* project, since we know
// there won't be bound vars there.
let data = data.fold_with(self);
// FIXME(mgca): While this supports constants, it is only used for types by default right now
#[instrument(level = "debug", skip(self), ret)]
fn normalize_inherent_projection(&mut self, inherent: AliasTerm<'tcx>) -> Term<'tcx> {
if !inherent.has_escaping_bound_vars() {
// When we don't have escaping bound vars we can normalize ambig aliases
// to inference variables (done in `normalize_projection_ty`). This would
// be wrong if there were escaping bound vars as even if we instantiated
// the bound vars with placeholders, we wouldn't be able to map them back
// after normalization succeeded.
//
// Also, as an optimization: when we don't have escaping bound vars, we don't
// need to replace them with placeholders (see branch below).
let inherent = inherent.fold_with(self);
project::normalize_inherent_projection(
self.selcx,
self.param_env,
data,
inherent,
self.cause.clone(),
self.depth,
self.obligations,
)
} else {
let infcx = self.selcx.infcx;
let (data, mapped_regions, mapped_types, mapped_consts) =
BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data);
let data = data.fold_with(self);
let ty = project::normalize_inherent_projection(
let (inherent, mapped_regions, mapped_types, mapped_consts) =
BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, inherent);
let inherent = inherent.fold_with(self);
let inherent = project::normalize_inherent_projection(
self.selcx,
self.param_env,
data,
inherent,
self.cause.clone(),
self.depth,
self.obligations,
@ -295,16 +279,18 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> {
mapped_types,
mapped_consts,
&self.universes,
ty,
inherent,
)
}
}
fn normalize_free_alias(&mut self, data: AliasTy<'tcx>) -> Ty<'tcx> {
// FIXME(mgca): While this supports constants, it is only used for types by default right now
#[instrument(level = "debug", skip(self), ret)]
fn normalize_free_alias(&mut self, free: AliasTerm<'tcx>) -> Term<'tcx> {
let recursion_limit = self.cx().recursion_limit();
if !recursion_limit.value_within_limit(self.depth) {
self.selcx.infcx.err_ctxt().report_overflow_error(
OverflowCause::DeeplyNormalize(data.into()),
OverflowCause::DeeplyNormalize(free.into()),
self.cause.span,
false,
|diag| {
@ -315,9 +301,13 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> {
let infcx = self.selcx.infcx;
self.obligations.extend(
infcx.tcx.predicates_of(data.def_id).instantiate_own(infcx.tcx, data.args).map(
// FIXME(BoxyUwU):
// FIXME(lazy_type_alias):
// It seems suspicious to instantiate the predicates with arguments that might be bound vars,
// we might wind up instantiating one of these bound vars underneath a hrtb.
infcx.tcx.predicates_of(free.def_id).instantiate_own(infcx.tcx, free.args).map(
|(mut predicate, span)| {
if data.has_escaping_bound_vars() {
if free.has_escaping_bound_vars() {
(predicate, ..) = BoundVarReplacer::replace_bound_vars(
infcx,
&mut self.universes,
@ -325,13 +315,21 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> {
);
}
let mut cause = self.cause.clone();
cause.map_code(|code| ObligationCauseCode::TypeAlias(code, span, data.def_id));
cause.map_code(|code| ObligationCauseCode::TypeAlias(code, span, free.def_id));
Obligation::new(infcx.tcx, cause, self.param_env, predicate)
},
),
);
self.depth += 1;
let res = infcx.tcx.type_of(data.def_id).instantiate(infcx.tcx, data.args).fold_with(self);
let res = if free.kind(infcx.tcx).is_type() {
infcx.tcx.type_of(free.def_id).instantiate(infcx.tcx, free.args).fold_with(self).into()
} else {
// FIXME(mgca): once const items are actual aliases defined as equal to type system consts
// this should instead use that rather than evaluating.
super::evaluate_const(infcx, free.to_term(infcx.tcx).expect_const(), self.param_env)
.super_fold_with(self)
.into()
};
self.depth -= 1;
res
}
@ -415,28 +413,64 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx
}
}
}
ty::Projection => self.normalize_trait_projection(data),
ty::Free => self.normalize_free_alias(data),
ty::Inherent => self.normalize_inherent_projection(data),
ty::Projection => self.normalize_trait_projection(data.into()).expect_type(),
ty::Inherent => self.normalize_inherent_projection(data.into()).expect_type(),
ty::Free => self.normalize_free_alias(data.into()).expect_type(),
}
}
#[instrument(skip(self), level = "debug")]
fn fold_const(&mut self, constant: ty::Const<'tcx>) -> ty::Const<'tcx> {
fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
let tcx = self.selcx.tcx();
if tcx.features().generic_const_exprs() || !needs_normalization(self.selcx.infcx, &constant)
{
constant
if tcx.features().generic_const_exprs() || !needs_normalization(self.selcx.infcx, &ct) {
return ct;
}
// Doing "proper" normalization of const aliases is inherently cyclic until const items
// are real aliases instead of having bodies. We gate proper const alias handling behind
// mgca to avoid breaking stable code, though this should become the "main" codepath long
// before mgca is stabilized.
//
// FIXME(BoxyUwU): Enabling this by default is blocked on a refactoring to how const items
// are represented.
if tcx.features().min_generic_const_args() {
let uv = match ct.kind() {
ty::ConstKind::Unevaluated(uv) => uv,
_ => return ct.super_fold_with(self),
};
let ct = match tcx.def_kind(uv.def) {
DefKind::AssocConst => match tcx.def_kind(tcx.parent(uv.def)) {
DefKind::Trait => self.normalize_trait_projection(uv.into()),
DefKind::Impl { of_trait: false } => {
self.normalize_inherent_projection(uv.into())
}
kind => unreachable!(
"unexpected `DefKind` for const alias' resolution's parent def: {:?}",
kind
),
},
DefKind::Const | DefKind::AnonConst => self.normalize_free_alias(uv.into()),
kind => {
unreachable!("unexpected `DefKind` for const alias to resolve to: {:?}", kind)
}
};
// We re-fold the normalized const as the `ty` field on `ConstKind::Value` may be
// unnormalized after const evaluation returns.
ct.expect_const().super_fold_with(self)
} else {
let constant = constant.super_fold_with(self);
debug!(?constant, ?self.param_env);
with_replaced_escaping_bound_vars(
let ct = ct.super_fold_with(self);
return super::with_replaced_escaping_bound_vars(
self.selcx.infcx,
&mut self.universes,
constant,
|constant| super::evaluate_const(self.selcx.infcx, constant, self.param_env),
ct,
|ct| super::evaluate_const(self.selcx.infcx, ct, self.param_env),
)
.super_fold_with(self)
.super_fold_with(self);
// We re-fold the normalized const as the `ty` field on `ConstKind::Value` may be
// unnormalized after const evaluation returns.
}
}

View file

@ -5,7 +5,6 @@ use std::ops::ControlFlow;
use rustc_data_structures::sso::SsoHashSet;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::ErrorGuaranteed;
use rustc_hir::def::DefKind;
use rustc_hir::lang_items::LangItem;
use rustc_infer::infer::DefineOpaqueTypes;
use rustc_infer::infer::resolve::OpportunisticRegionResolver;
@ -172,6 +171,7 @@ pub(super) enum ProjectAndUnifyResult<'tcx> {
/// ```
/// If successful, this may result in additional obligations. Also returns
/// the projection cache key used to track these additional obligations.
// FIXME(mgca): While this supports constants, it is only used for types by default right now
#[instrument(level = "debug", skip(selcx))]
pub(super) fn poly_project_and_unify_term<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
@ -201,6 +201,7 @@ pub(super) fn poly_project_and_unify_term<'cx, 'tcx>(
/// If successful, this may result in additional obligations.
///
/// See [poly_project_and_unify_term] for an explanation of the return value.
// FIXME(mgca): While this supports constants, it is only used for types by default right now
#[instrument(level = "debug", skip(selcx))]
fn project_and_unify_term<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
@ -258,34 +259,28 @@ fn project_and_unify_term<'cx, 'tcx>(
/// there are unresolved type variables in the projection, we will
/// instantiate it with a fresh type variable `$X` and generate a new
/// obligation `<T as Trait>::Item == $X` for later.
pub fn normalize_projection_ty<'a, 'b, 'tcx>(
// FIXME(mgca): While this supports constants, it is only used for types by default right now
pub fn normalize_projection_term<'a, 'b, 'tcx>(
selcx: &'a mut SelectionContext<'b, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
projection_ty: ty::AliasTy<'tcx>,
alias_term: ty::AliasTerm<'tcx>,
cause: ObligationCause<'tcx>,
depth: usize,
obligations: &mut PredicateObligations<'tcx>,
) -> Term<'tcx> {
opt_normalize_projection_term(
selcx,
param_env,
projection_ty.into(),
cause.clone(),
depth,
obligations,
)
.ok()
.flatten()
.unwrap_or_else(move || {
// if we bottom out in ambiguity, create a type variable
// and a deferred predicate to resolve this when more type
// information is available.
opt_normalize_projection_term(selcx, param_env, alias_term, cause.clone(), depth, obligations)
.ok()
.flatten()
.unwrap_or_else(move || {
// if we bottom out in ambiguity, create a type variable
// and a deferred predicate to resolve this when more type
// information is available.
selcx
.infcx
.projection_ty_to_infer(param_env, projection_ty, cause, depth + 1, obligations)
.into()
})
selcx
.infcx
.projection_term_to_infer(param_env, alias_term, cause, depth + 1, obligations)
.into()
})
}
/// The guts of `normalize`: normalize a specific projection like `<T
@ -298,6 +293,7 @@ pub fn normalize_projection_ty<'a, 'b, 'tcx>(
/// often immediately appended to another obligations vector. So now this
/// function takes an obligations vector and appends to it directly, which is
/// slightly uglier but avoids the need for an extra short-lived allocation.
// FIXME(mgca): While this supports constants, it is only used for types by default right now
#[instrument(level = "debug", skip(selcx, param_env, cause, obligations))]
pub(super) fn opt_normalize_projection_term<'a, 'b, 'tcx>(
selcx: &'a mut SelectionContext<'b, 'tcx>,
@ -456,6 +452,7 @@ pub(super) fn opt_normalize_projection_term<'a, 'b, 'tcx>(
/// an error for this obligation, but we legitimately should not,
/// because it contains `[type error]`. Yuck! (See issue #29857 for
/// one case where this arose.)
// FIXME(mgca): While this supports constants, it is only used for types by default right now
fn normalize_to_error<'a, 'tcx>(
selcx: &SelectionContext<'a, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
@ -469,9 +466,10 @@ fn normalize_to_error<'a, 'tcx>(
| ty::AliasTermKind::InherentTy
| ty::AliasTermKind::OpaqueTy
| ty::AliasTermKind::FreeTy => selcx.infcx.next_ty_var(cause.span).into(),
ty::AliasTermKind::UnevaluatedConst | ty::AliasTermKind::ProjectionConst => {
selcx.infcx.next_const_var(cause.span).into()
}
ty::AliasTermKind::FreeConst
| ty::AliasTermKind::InherentConst
| ty::AliasTermKind::UnevaluatedConst
| ty::AliasTermKind::ProjectionConst => selcx.infcx.next_const_var(cause.span).into(),
};
let mut obligations = PredicateObligations::new();
obligations.push(Obligation {
@ -484,36 +482,37 @@ fn normalize_to_error<'a, 'tcx>(
}
/// Confirm and normalize the given inherent projection.
// FIXME(mgca): While this supports constants, it is only used for types by default right now
#[instrument(level = "debug", skip(selcx, param_env, cause, obligations))]
pub fn normalize_inherent_projection<'a, 'b, 'tcx>(
selcx: &'a mut SelectionContext<'b, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
alias_ty: ty::AliasTy<'tcx>,
alias_term: ty::AliasTerm<'tcx>,
cause: ObligationCause<'tcx>,
depth: usize,
obligations: &mut PredicateObligations<'tcx>,
) -> Ty<'tcx> {
) -> ty::Term<'tcx> {
let tcx = selcx.tcx();
if !tcx.recursion_limit().value_within_limit(depth) {
// Halt compilation because it is important that overflows never be masked.
tcx.dcx().emit_fatal(InherentProjectionNormalizationOverflow {
span: cause.span,
ty: alias_ty.to_string(),
ty: alias_term.to_string(),
});
}
let args = compute_inherent_assoc_ty_args(
let args = compute_inherent_assoc_term_args(
selcx,
param_env,
alias_ty,
alias_term,
cause.clone(),
depth,
obligations,
);
// Register the obligations arising from the impl and from the associated type itself.
let predicates = tcx.predicates_of(alias_ty.def_id).instantiate(tcx, args);
let predicates = tcx.predicates_of(alias_term.def_id).instantiate(tcx, args);
for (predicate, span) in predicates {
let predicate = normalize_with_depth_to(
selcx,
@ -531,7 +530,7 @@ pub fn normalize_inherent_projection<'a, 'b, 'tcx>(
// cause code, inherent projections will be printed with identity instantiation in
// diagnostics which is not ideal.
// Consider creating separate cause codes for this specific situation.
ObligationCauseCode::WhereClause(alias_ty.def_id, span),
ObligationCauseCode::WhereClause(alias_term.def_id, span),
);
obligations.push(Obligation::with_depth(
@ -543,27 +542,33 @@ pub fn normalize_inherent_projection<'a, 'b, 'tcx>(
));
}
let ty = tcx.type_of(alias_ty.def_id).instantiate(tcx, args);
let term: Term<'tcx> = if alias_term.kind(tcx).is_type() {
tcx.type_of(alias_term.def_id).instantiate(tcx, args).into()
} else {
get_associated_const_value(selcx, alias_term.to_term(tcx).expect_const(), param_env).into()
};
let mut ty = selcx.infcx.resolve_vars_if_possible(ty);
if ty.has_aliases() {
ty = normalize_with_depth_to(selcx, param_env, cause.clone(), depth + 1, ty, obligations);
let mut term = selcx.infcx.resolve_vars_if_possible(term);
if term.has_aliases() {
term =
normalize_with_depth_to(selcx, param_env, cause.clone(), depth + 1, term, obligations);
}
ty
term
}
pub fn compute_inherent_assoc_ty_args<'a, 'b, 'tcx>(
// FIXME(mgca): While this supports constants, it is only used for types by default right now
pub fn compute_inherent_assoc_term_args<'a, 'b, 'tcx>(
selcx: &'a mut SelectionContext<'b, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
alias_ty: ty::AliasTy<'tcx>,
alias_term: ty::AliasTerm<'tcx>,
cause: ObligationCause<'tcx>,
depth: usize,
obligations: &mut PredicateObligations<'tcx>,
) -> ty::GenericArgsRef<'tcx> {
let tcx = selcx.tcx();
let impl_def_id = tcx.parent(alias_ty.def_id);
let impl_def_id = tcx.parent(alias_term.def_id);
let impl_args = selcx.infcx.fresh_args_for_item(cause.span, impl_def_id);
let mut impl_ty = tcx.type_of(impl_def_id).instantiate(tcx, impl_args);
@ -580,7 +585,7 @@ pub fn compute_inherent_assoc_ty_args<'a, 'b, 'tcx>(
// Infer the generic parameters of the impl by unifying the
// impl type with the self type of the projection.
let mut self_ty = alias_ty.self_ty();
let mut self_ty = alias_term.self_ty();
if !selcx.infcx.next_trait_solver() {
self_ty = normalize_with_depth_to(
selcx,
@ -602,7 +607,7 @@ pub fn compute_inherent_assoc_ty_args<'a, 'b, 'tcx>(
}
}
alias_ty.rebase_inherent_args_onto_impl(impl_args, tcx)
alias_term.rebase_inherent_args_onto_impl(impl_args, tcx)
}
enum Projected<'tcx> {
@ -630,6 +635,7 @@ impl<'tcx> Progress<'tcx> {
///
/// IMPORTANT:
/// - `obligation` must be fully normalized
// FIXME(mgca): While this supports constants, it is only used for types by default right now
#[instrument(level = "info", skip(selcx))]
fn project<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
@ -896,7 +902,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
ImplSource::UserDefined(impl_data) => {
// We have to be careful when projecting out of an
// impl because of specialization. If we are not in
// codegen (i.e., projection mode is not "any"), and the
// codegen (i.e., `TypingMode` is not `PostAnalysis`), and the
// impl's type is declared as default, then we disable
// projection (even if the trait ref is fully
// monomorphic). In the case where trait ref is not
@ -1189,6 +1195,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
});
}
// FIXME(mgca): While this supports constants, it is only used for types by default right now
fn confirm_candidate<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionTermObligation<'tcx>,
@ -1222,6 +1229,7 @@ fn confirm_candidate<'cx, 'tcx>(
result
}
// FIXME(mgca): While this supports constants, it is only used for types by default right now
fn confirm_select_candidate<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionTermObligation<'tcx>,
@ -1873,6 +1881,7 @@ fn confirm_async_fn_kind_helper_candidate<'cx, 'tcx>(
.with_addl_obligations(nested)
}
// FIXME(mgca): While this supports constants, it is only used for types by default right now
fn confirm_param_env_candidate<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionTermObligation<'tcx>,
@ -1926,9 +1935,7 @@ fn confirm_param_env_candidate<'cx, 'tcx>(
) {
Ok(InferOk { value: _, obligations }) => {
nested_obligations.extend(obligations);
assoc_ty_own_obligations(selcx, obligation, &mut nested_obligations);
// FIXME(associated_const_equality): Handle consts here as well? Maybe this progress type should just take
// a term instead.
assoc_term_own_obligations(selcx, obligation, &mut nested_obligations);
Progress { term: cache_entry.term, obligations: nested_obligations }
}
Err(e) => {
@ -1942,6 +1949,7 @@ fn confirm_param_env_candidate<'cx, 'tcx>(
}
}
// FIXME(mgca): While this supports constants, it is only used for types by default right now
fn confirm_impl_candidate<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionTermObligation<'tcx>,
@ -1955,8 +1963,8 @@ fn confirm_impl_candidate<'cx, 'tcx>(
let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap();
let param_env = obligation.param_env;
let assoc_ty = match specialization_graph::assoc_def(tcx, impl_def_id, assoc_item_id) {
Ok(assoc_ty) => assoc_ty,
let assoc_term = match specialization_graph::assoc_def(tcx, impl_def_id, assoc_item_id) {
Ok(assoc_term) => assoc_term,
Err(guar) => return Ok(Projected::Progress(Progress::error(tcx, guar))),
};
@ -1965,10 +1973,10 @@ fn confirm_impl_candidate<'cx, 'tcx>(
// has impossible-to-satisfy predicates (since those were
// allowed in <https://github.com/rust-lang/rust/pull/135480>),
// or because the impl is literally missing the definition.
if !assoc_ty.item.defaultness(tcx).has_value() {
if !assoc_term.item.defaultness(tcx).has_value() {
debug!(
"confirm_impl_candidate: no associated type {:?} for {:?}",
assoc_ty.item.name(),
assoc_term.item.name(),
obligation.predicate
);
if tcx.impl_self_is_guaranteed_unsized(impl_def_id) {
@ -1979,7 +1987,11 @@ fn confirm_impl_candidate<'cx, 'tcx>(
return Ok(Projected::NoProgress(obligation.predicate.to_term(tcx)));
} else {
return Ok(Projected::Progress(Progress {
term: Ty::new_misc_error(tcx).into(),
term: if obligation.predicate.kind(tcx).is_type() {
Ty::new_misc_error(tcx).into()
} else {
ty::Const::new_misc_error(tcx).into()
},
obligations: nested,
}));
}
@ -1992,27 +2004,32 @@ fn confirm_impl_candidate<'cx, 'tcx>(
// * `args` is `[u32]`
// * `args` ends up as `[u32, S]`
let args = obligation.predicate.args.rebase_onto(tcx, trait_def_id, args);
let args = translate_args(selcx.infcx, param_env, impl_def_id, args, assoc_ty.defining_node);
let is_const = matches!(tcx.def_kind(assoc_ty.item.def_id), DefKind::AssocConst);
let args = translate_args(selcx.infcx, param_env, impl_def_id, args, assoc_term.defining_node);
let term: ty::EarlyBinder<'tcx, ty::Term<'tcx>> = if is_const {
let did = assoc_ty.item.def_id;
let identity_args = crate::traits::GenericArgs::identity_for_item(tcx, did);
let uv = ty::UnevaluatedConst::new(did, identity_args);
ty::EarlyBinder::bind(ty::Const::new_unevaluated(tcx, uv).into())
let term = if obligation.predicate.kind(tcx).is_type() {
tcx.type_of(assoc_term.item.def_id).map_bound(|ty| ty.into())
} else {
tcx.type_of(assoc_ty.item.def_id).map_bound(|ty| ty.into())
ty::EarlyBinder::bind(
get_associated_const_value(
selcx,
obligation.predicate.to_term(tcx).expect_const(),
param_env,
)
.into(),
)
};
let progress = if !tcx.check_args_compatible(assoc_ty.item.def_id, args) {
let err = Ty::new_error_with_message(
tcx,
obligation.cause.span,
"impl item and trait item have different parameters",
);
Progress { term: err.into(), obligations: nested }
let progress = if !tcx.check_args_compatible(assoc_term.item.def_id, args) {
let msg = "impl item and trait item have different parameters";
let span = obligation.cause.span;
let err = if obligation.predicate.kind(tcx).is_type() {
Ty::new_error_with_message(tcx, span, msg).into()
} else {
ty::Const::new_error_with_message(tcx, span, msg).into()
};
Progress { term: err, obligations: nested }
} else {
assoc_ty_own_obligations(selcx, obligation, &mut nested);
assoc_term_own_obligations(selcx, obligation, &mut nested);
Progress { term: term.instantiate(tcx, args), obligations: nested }
};
Ok(Projected::Progress(progress))
@ -2020,7 +2037,8 @@ fn confirm_impl_candidate<'cx, 'tcx>(
// Get obligations corresponding to the predicates from the where-clause of the
// associated type itself.
fn assoc_ty_own_obligations<'cx, 'tcx>(
// FIXME(mgca): While this supports constants, it is only used for types by default right now
fn assoc_term_own_obligations<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionTermObligation<'tcx>,
nested: &mut PredicateObligations<'tcx>,
@ -2090,3 +2108,15 @@ impl<'cx, 'tcx> ProjectionCacheKeyExt<'cx, 'tcx> for ProjectionCacheKey<'tcx> {
})
}
}
fn get_associated_const_value<'tcx>(
selcx: &mut SelectionContext<'_, 'tcx>,
alias_ct: ty::Const<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> ty::Const<'tcx> {
// FIXME(mgca): We shouldn't be invoking ctfe here, instead const items should be aliases to type
// system consts that we can retrieve with some `query const_arg_of_alias` query. Evaluating the
// constant is "close enough" to getting the actual rhs of the const item for now even if it might
// lead to some cycles
super::evaluate_const(selcx.infcx, alias_ct, param_env)
}

View file

@ -406,7 +406,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
let mut assume = predicate.trait_ref.args.const_at(2);
// FIXME(mgca): We should shallowly normalize this.
if self.tcx().features().generic_const_exprs() {
assume = crate::traits::evaluate_const(self.infcx, assume, obligation.param_env)
}

View file

@ -6,6 +6,7 @@
use std::iter;
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir::lang_items::LangItem;
use rustc_infer::traits::{ObligationCauseCode, PredicateObligations};
use rustc_middle::bug;
@ -486,7 +487,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
/// Pushes the obligations required for an inherent alias to be WF
/// into `self.out`.
// FIXME(inherent_associated_types): Merge this function with `fn compute_alias`.
fn add_wf_preds_for_inherent_projection(&mut self, data: ty::AliasTy<'tcx>) {
fn add_wf_preds_for_inherent_projection(&mut self, data: ty::AliasTerm<'tcx>) {
// An inherent projection is well-formed if
//
// (a) its predicates hold (*)
@ -498,7 +499,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
if !data.self_ty().has_escaping_bound_vars() {
// FIXME(inherent_associated_types): Should this happen inside of a snapshot?
// FIXME(inherent_associated_types): This is incompatible with the new solver and lazy norm!
let args = traits::project::compute_inherent_assoc_ty_args(
let args = traits::project::compute_inherent_assoc_term_args(
&mut traits::SelectionContext::new(self.infcx),
self.param_env,
data,
@ -776,7 +777,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
self.out.extend(obligations);
}
ty::Alias(ty::Inherent, data) => {
self.add_wf_preds_for_inherent_projection(data);
self.add_wf_preds_for_inherent_projection(data.into());
return; // Subtree handled by compute_inherent_projection.
}
@ -961,9 +962,6 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
match c.kind() {
ty::ConstKind::Unevaluated(uv) => {
if !c.has_escaping_bound_vars() {
let obligations = self.nominal_obligations(uv.def, uv.args);
self.out.extend(obligations);
let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(
ty::ClauseKind::ConstEvaluatable(c),
));
@ -975,6 +973,16 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
self.param_env,
predicate,
));
if tcx.def_kind(uv.def) == DefKind::AssocConst
&& tcx.def_kind(tcx.parent(uv.def)) == (DefKind::Impl { of_trait: false })
{
self.add_wf_preds_for_inherent_projection(uv.into());
return; // Subtree is handled by above function
} else {
let obligations = self.nominal_obligations(uv.def, uv.args);
self.out.extend(obligations);
}
}
}
ty::ConstKind::Infer(_) => {

View file

@ -32,8 +32,14 @@ fn normalize_canonicalized_projection_ty<'tcx>(
let selcx = &mut SelectionContext::new(ocx.infcx);
let cause = ObligationCause::dummy();
let mut obligations = PredicateObligations::new();
let answer =
traits::normalize_projection_ty(selcx, param_env, goal, cause, 0, &mut obligations);
let answer = traits::normalize_projection_term(
selcx,
param_env,
goal.into(),
cause,
0,
&mut obligations,
);
ocx.register_obligations(obligations);
// #112047: With projections and opaques, we are able to create opaques that
// are recursive (given some generic parameters of the opaque's type variables).
@ -104,14 +110,14 @@ fn normalize_canonicalized_inherent_projection_ty<'tcx>(
let answer = traits::normalize_inherent_projection(
selcx,
param_env,
goal,
goal.into(),
cause,
0,
&mut obligations,
);
ocx.register_obligations(obligations);
Ok(NormalizationResult { normalized_ty: answer })
Ok(NormalizationResult { normalized_ty: answer.expect_type() })
},
)
}

View file

@ -474,10 +474,15 @@ pub enum AliasTermKind {
/// Currently only used if the type alias references opaque types.
/// Can always be normalized away.
FreeTy,
/// An unevaluated const coming from a generic const expression.
/// An unevaluated anonymous constants.
UnevaluatedConst,
/// An unevaluated const coming from an associated const.
ProjectionConst,
/// A top level const item not part of a trait or impl.
FreeConst,
/// An associated const in an inherent `impl`
InherentConst,
}
impl AliasTermKind {
@ -486,11 +491,27 @@ impl AliasTermKind {
AliasTermKind::ProjectionTy => "associated type",
AliasTermKind::ProjectionConst => "associated const",
AliasTermKind::InherentTy => "inherent associated type",
AliasTermKind::InherentConst => "inherent associated const",
AliasTermKind::OpaqueTy => "opaque type",
AliasTermKind::FreeTy => "type alias",
AliasTermKind::FreeConst => "unevaluated constant",
AliasTermKind::UnevaluatedConst => "unevaluated constant",
}
}
pub fn is_type(self) -> bool {
match self {
AliasTermKind::ProjectionTy
| AliasTermKind::InherentTy
| AliasTermKind::OpaqueTy
| AliasTermKind::FreeTy => true,
AliasTermKind::UnevaluatedConst
| AliasTermKind::ProjectionConst
| AliasTermKind::InherentConst
| AliasTermKind::FreeConst => false,
}
}
}
impl From<ty::AliasTyKind> for AliasTermKind {
@ -566,7 +587,10 @@ impl<I: Interner> AliasTerm<I> {
| AliasTermKind::InherentTy
| AliasTermKind::OpaqueTy
| AliasTermKind::FreeTy => {}
AliasTermKind::UnevaluatedConst | AliasTermKind::ProjectionConst => {
AliasTermKind::InherentConst
| AliasTermKind::FreeConst
| AliasTermKind::UnevaluatedConst
| AliasTermKind::ProjectionConst => {
panic!("Cannot turn `UnevaluatedConst` into `AliasTy`")
}
}
@ -603,18 +627,19 @@ impl<I: Interner> AliasTerm<I> {
ty::AliasTy { def_id: self.def_id, args: self.args, _use_alias_ty_new_instead: () },
)
.into(),
AliasTermKind::UnevaluatedConst | AliasTermKind::ProjectionConst => {
I::Const::new_unevaluated(
interner,
ty::UnevaluatedConst::new(self.def_id, self.args),
)
.into()
}
AliasTermKind::FreeConst
| AliasTermKind::InherentConst
| AliasTermKind::UnevaluatedConst
| AliasTermKind::ProjectionConst => I::Const::new_unevaluated(
interner,
ty::UnevaluatedConst::new(self.def_id, self.args),
)
.into(),
}
}
}
/// The following methods work only with (trait) associated type projections.
/// The following methods work only with (trait) associated term projections.
impl<I: Interner> AliasTerm<I> {
pub fn self_ty(self) -> I::Ty {
self.args.type_at(0)
@ -659,6 +684,31 @@ impl<I: Interner> AliasTerm<I> {
}
}
/// The following methods work only with inherent associated term projections.
impl<I: Interner> AliasTerm<I> {
/// Transform the generic parameters to have the given `impl` args as the base and the GAT args on top of that.
///
/// Does the following transformation:
///
/// ```text
/// [Self, P_0...P_m] -> [I_0...I_n, P_0...P_m]
///
/// I_i impl args
/// P_j GAT args
/// ```
pub fn rebase_inherent_args_onto_impl(
self,
impl_args: I::GenericArgs,
interner: I,
) -> I::GenericArgs {
debug_assert!(matches!(
self.kind(interner),
AliasTermKind::InherentTy | AliasTermKind::InherentConst
));
interner.mk_args_from_iter(impl_args.iter().chain(self.args.iter().skip(1)))
}
}
impl<I: Interner> From<ty::AliasTy<I>> for AliasTerm<I> {
fn from(ty: ty::AliasTy<I>) -> Self {
AliasTerm { args: ty.args, def_id: ty.def_id, _use_alias_term_new_instead: () }

View file

@ -273,8 +273,10 @@ impl<I: Interner> Relate<I> for ty::AliasTerm<I> {
false, // do not fetch `type_of(a_def_id)`, as it will cause a cycle
)?,
ty::AliasTermKind::ProjectionTy
| ty::AliasTermKind::FreeConst
| ty::AliasTermKind::FreeTy
| ty::AliasTermKind::InherentTy
| ty::AliasTermKind::InherentConst
| ty::AliasTermKind::UnevaluatedConst
| ty::AliasTermKind::ProjectionConst => {
relate_args_invariantly(relation, a.args, b.args)?

View file

@ -514,28 +514,6 @@ impl<I: Interner> AliasTy<I> {
}
}
/// The following methods work only with inherent associated type projections.
impl<I: Interner> AliasTy<I> {
/// Transform the generic parameters to have the given `impl` args as the base and the GAT args on top of that.
///
/// Does the following transformation:
///
/// ```text
/// [Self, P_0...P_m] -> [I_0...I_n, P_0...P_m]
///
/// I_i impl args
/// P_j GAT args
/// ```
pub fn rebase_inherent_args_onto_impl(
self,
impl_args: I::GenericArgs,
interner: I,
) -> I::GenericArgs {
debug_assert_eq!(self.kind(interner), AliasTyKind::Inherent);
interner.mk_args_from_iter(impl_args.iter().chain(self.args.iter().skip(1)))
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(
feature = "nightly",

View file

@ -39,14 +39,6 @@ error: unconstrained generic constant
LL | let _ = const_evaluatable_lib::test1::<T>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: required by a bound in `test1`
--> $DIR/auxiliary/const_evaluatable_lib.rs:5:10
|
LL | pub fn test1<T>() -> [u8; std::mem::size_of::<T>() - 1]
| ----- required by a bound in this function
LL | where
LL | [u8; std::mem::size_of::<T>() - 1]: Sized,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `test1`
help: try adding a `where` bound
|
LL | fn user<T>() where [(); std::mem::size_of::<T>() - 1]: {
@ -59,10 +51,13 @@ LL | let _ = const_evaluatable_lib::test1::<T>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: required by a bound in `test1`
--> $DIR/auxiliary/const_evaluatable_lib.rs:3:27
--> $DIR/auxiliary/const_evaluatable_lib.rs:5:10
|
LL | pub fn test1<T>() -> [u8; std::mem::size_of::<T>() - 1]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `test1`
| ----- required by a bound in this function
LL | where
LL | [u8; std::mem::size_of::<T>() - 1]: Sized,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `test1`
help: try adding a `where` bound
|
LL | fn user<T>() where [(); std::mem::size_of::<T>() - 1]: {