mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-23 07:14:28 +00:00
Rollup merge of #71973 - lcnr:lazy-norm, r=nikomatsakis
Lazy normalization of constants (Reprise) Continuation of #67890 by @skinny121. Initial implementation of #60471 for constants. Perform normalization/evaluation of constants lazily, which is known as lazy normalization. Lazy normalization is only enabled when using `#![feature(lazy_normalization_consts)]`, by default constants are still evaluated eagerly as there are currently. Lazy normalization of constants is achieved with a new ConstEquate predicate which type inferences uses to delay checking whether constants are equal to each other until later, avoiding cycle errors. Note this doesn't allow the use of generics within repeat count expressions as that is still evaluated during conversion to mir. There are also quite a few other known problems with lazy normalization which will be fixed in future PRs. r? @nikomatsakis fixes #71922, fixes #71986
This commit is contained in:
commit
c6030c957a
@ -25,7 +25,7 @@ use rustc_middle::arena::ArenaAllocatable;
|
||||
use rustc_middle::ty::fold::TypeFoldable;
|
||||
use rustc_middle::ty::relate::TypeRelation;
|
||||
use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
|
||||
use rustc_middle::ty::{self, BoundVar, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{self, BoundVar, Const, Ty, TyCtxt};
|
||||
use std::fmt::Debug;
|
||||
|
||||
impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
|
||||
@ -671,6 +671,13 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for QueryTypeRelatingDelegate<'_, 'tcx> {
|
||||
});
|
||||
}
|
||||
|
||||
fn const_equate(&mut self, _a: &'tcx Const<'tcx>, _b: &'tcx Const<'tcx>) {
|
||||
span_bug!(
|
||||
self.cause.span(self.infcx.tcx),
|
||||
"lazy_normalization_consts: unreachable `const_equate`"
|
||||
);
|
||||
}
|
||||
|
||||
fn normalization() -> NormalizationStrategy {
|
||||
NormalizationStrategy::Eager
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::ty::error::TypeError;
|
||||
use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
|
||||
use rustc_middle::ty::subst::SubstsRef;
|
||||
use rustc_middle::ty::{self, InferConst, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{self, InferConst, Ty, TyCtxt, TypeFoldable};
|
||||
use rustc_middle::ty::{IntType, UintType};
|
||||
use rustc_span::{Span, DUMMY_SP};
|
||||
|
||||
@ -126,7 +126,7 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> {
|
||||
b: &'tcx ty::Const<'tcx>,
|
||||
) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>>
|
||||
where
|
||||
R: TypeRelation<'tcx>,
|
||||
R: ConstEquateRelation<'tcx>,
|
||||
{
|
||||
debug!("{}.consts({:?}, {:?})", relation.tag(), a, b);
|
||||
if a == b {
|
||||
@ -164,7 +164,22 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> {
|
||||
(_, ty::ConstKind::Infer(InferConst::Var(vid))) => {
|
||||
return self.unify_const_variable(!a_is_expected, vid, a);
|
||||
}
|
||||
|
||||
(ty::ConstKind::Unevaluated(..), _) if self.tcx.lazy_normalization() => {
|
||||
// FIXME(#59490): Need to remove the leak check to accomodate
|
||||
// escaping bound variables here.
|
||||
if !a.has_escaping_bound_vars() && !b.has_escaping_bound_vars() {
|
||||
relation.const_equate_obligation(a, b);
|
||||
}
|
||||
return Ok(b);
|
||||
}
|
||||
(_, ty::ConstKind::Unevaluated(..)) if self.tcx.lazy_normalization() => {
|
||||
// FIXME(#59490): Need to remove the leak check to accomodate
|
||||
// escaping bound variables here.
|
||||
if !a.has_escaping_bound_vars() && !b.has_escaping_bound_vars() {
|
||||
relation.const_equate_obligation(a, b);
|
||||
}
|
||||
return Ok(a);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@ -375,6 +390,20 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
|
||||
debug!("generalize: success {{ {:?}, {:?} }}", ty, needs_wf);
|
||||
Ok(Generalization { ty, needs_wf })
|
||||
}
|
||||
|
||||
pub fn add_const_equate_obligation(
|
||||
&mut self,
|
||||
a_is_expected: bool,
|
||||
a: &'tcx ty::Const<'tcx>,
|
||||
b: &'tcx ty::Const<'tcx>,
|
||||
) {
|
||||
let predicate = if a_is_expected {
|
||||
ty::Predicate::ConstEquate(a, b)
|
||||
} else {
|
||||
ty::Predicate::ConstEquate(b, a)
|
||||
};
|
||||
self.obligations.push(Obligation::new(self.trace.cause.clone(), self.param_env, predicate));
|
||||
}
|
||||
}
|
||||
|
||||
struct Generalizer<'cx, 'tcx> {
|
||||
@ -637,11 +666,19 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
|
||||
}
|
||||
}
|
||||
}
|
||||
ty::ConstKind::Unevaluated(..) if self.tcx().lazy_normalization() => Ok(c),
|
||||
_ => relate::super_relate_consts(self, c, c),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ConstEquateRelation<'tcx>: TypeRelation<'tcx> {
|
||||
/// Register an obligation that both constants must be equal to each other.
|
||||
///
|
||||
/// If they aren't equal then the relation doesn't hold.
|
||||
fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>);
|
||||
}
|
||||
|
||||
pub trait RelateResultCompare<'tcx, T> {
|
||||
fn compare<F>(&self, t: T, f: F) -> RelateResult<'tcx, T>
|
||||
where
|
||||
|
@ -1,4 +1,4 @@
|
||||
use super::combine::{CombineFields, RelationDir};
|
||||
use super::combine::{CombineFields, ConstEquateRelation, RelationDir};
|
||||
use super::Subtype;
|
||||
|
||||
use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
|
||||
@ -140,3 +140,9 @@ impl TypeRelation<'tcx> for Equate<'combine, 'infcx, 'tcx> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> ConstEquateRelation<'tcx> for Equate<'_, '_, 'tcx> {
|
||||
fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>) {
|
||||
self.fields.add_const_equate_obligation(self.a_is_expected, a, b);
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ use super::lattice::{self, LatticeDir};
|
||||
use super::InferCtxt;
|
||||
use super::Subtype;
|
||||
|
||||
use crate::infer::combine::ConstEquateRelation;
|
||||
use crate::traits::ObligationCause;
|
||||
use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
@ -116,3 +117,9 @@ impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Glb<'combine, 'infcx,
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> ConstEquateRelation<'tcx> for Glb<'_, '_, 'tcx> {
|
||||
fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>) {
|
||||
self.fields.add_const_equate_obligation(self.a_is_expected, a, b);
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ use super::lattice::{self, LatticeDir};
|
||||
use super::InferCtxt;
|
||||
use super::Subtype;
|
||||
|
||||
use crate::infer::combine::ConstEquateRelation;
|
||||
use crate::traits::ObligationCause;
|
||||
use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
@ -100,6 +101,12 @@ impl TypeRelation<'tcx> for Lub<'combine, 'infcx, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> ConstEquateRelation<'tcx> for Lub<'_, '_, 'tcx> {
|
||||
fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>) {
|
||||
self.fields.add_const_equate_obligation(self.a_is_expected, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Lub<'combine, 'infcx, 'tcx> {
|
||||
fn infcx(&self) -> &'infcx InferCtxt<'infcx, 'tcx> {
|
||||
self.fields.infcx
|
||||
|
@ -1490,6 +1490,17 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||
self.report_and_explain_type_error(trace, &err)
|
||||
}
|
||||
|
||||
pub fn report_mismatched_consts(
|
||||
&self,
|
||||
cause: &ObligationCause<'tcx>,
|
||||
expected: &'tcx ty::Const<'tcx>,
|
||||
actual: &'tcx ty::Const<'tcx>,
|
||||
err: TypeError<'tcx>,
|
||||
) -> DiagnosticBuilder<'tcx> {
|
||||
let trace = TypeTrace::consts(cause, true, expected, actual);
|
||||
self.report_and_explain_type_error(trace, &err)
|
||||
}
|
||||
|
||||
pub fn replace_bound_vars_with_fresh_vars<T>(
|
||||
&self,
|
||||
span: Span,
|
||||
@ -1777,6 +1788,15 @@ impl<'tcx> TypeTrace<'tcx> {
|
||||
TypeTrace { cause: cause.clone(), values: Types(ExpectedFound::new(a_is_expected, a, b)) }
|
||||
}
|
||||
|
||||
pub fn consts(
|
||||
cause: &ObligationCause<'tcx>,
|
||||
a_is_expected: bool,
|
||||
a: &'tcx ty::Const<'tcx>,
|
||||
b: &'tcx ty::Const<'tcx>,
|
||||
) -> TypeTrace<'tcx> {
|
||||
TypeTrace { cause: cause.clone(), values: Consts(ExpectedFound::new(a_is_expected, a, b)) }
|
||||
}
|
||||
|
||||
pub fn dummy(tcx: TyCtxt<'tcx>) -> TypeTrace<'tcx> {
|
||||
TypeTrace {
|
||||
cause: ObligationCause::dummy(),
|
||||
|
@ -21,6 +21,7 @@
|
||||
//! thing we relate in chalk are basically domain goals and their
|
||||
//! constituents)
|
||||
|
||||
use crate::infer::combine::ConstEquateRelation;
|
||||
use crate::infer::InferCtxt;
|
||||
use crate::infer::{ConstVarValue, ConstVariableValue};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
@ -77,6 +78,8 @@ pub trait TypeRelatingDelegate<'tcx> {
|
||||
/// delegate.
|
||||
fn push_outlives(&mut self, sup: ty::Region<'tcx>, sub: ty::Region<'tcx>);
|
||||
|
||||
fn const_equate(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>);
|
||||
|
||||
/// Creates a new universe index. Used when instantiating placeholders.
|
||||
fn create_next_universe(&mut self) -> ty::UniverseIndex;
|
||||
|
||||
@ -715,6 +718,15 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, D> ConstEquateRelation<'tcx> for TypeRelating<'_, 'tcx, D>
|
||||
where
|
||||
D: TypeRelatingDelegate<'tcx>,
|
||||
{
|
||||
fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>) {
|
||||
self.delegate.const_equate(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/// When we encounter a binder like `for<..> fn(..)`, we actually have
|
||||
/// to walk the `fn` value to find all the values bound by the `for`
|
||||
/// (these are not explicitly present in the ty representation right
|
||||
@ -976,6 +988,7 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
ty::ConstKind::Unevaluated(..) if self.tcx().lazy_normalization() => Ok(a),
|
||||
_ => relate::super_relate_consts(self, a, a),
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,8 @@ pub fn explicit_outlives_bounds<'tcx>(
|
||||
| ty::Predicate::ObjectSafe(..)
|
||||
| ty::Predicate::ClosureKind(..)
|
||||
| ty::Predicate::TypeOutlives(..)
|
||||
| ty::Predicate::ConstEvaluatable(..) => None,
|
||||
| ty::Predicate::ConstEvaluatable(..)
|
||||
| ty::Predicate::ConstEquate(..) => None,
|
||||
ty::Predicate::RegionOutlives(ref data) => data
|
||||
.no_bound_vars()
|
||||
.map(|ty::OutlivesPredicate(r_a, r_b)| OutlivesBound::RegionSubRegion(r_b, r_a)),
|
||||
|
@ -1,6 +1,7 @@
|
||||
use super::combine::{CombineFields, RelationDir};
|
||||
use super::SubregionOrigin;
|
||||
|
||||
use crate::infer::combine::ConstEquateRelation;
|
||||
use crate::traits::Obligation;
|
||||
use rustc_middle::ty::fold::TypeFoldable;
|
||||
use rustc_middle::ty::relate::{Cause, Relate, RelateResult, TypeRelation};
|
||||
@ -169,3 +170,9 @@ impl TypeRelation<'tcx> for Sub<'combine, 'infcx, 'tcx> {
|
||||
self.fields.higher_ranked_sub(a, b, self.a_is_expected)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> ConstEquateRelation<'tcx> for Sub<'_, '_, 'tcx> {
|
||||
fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>) {
|
||||
self.fields.add_const_equate_obligation(self.a_is_expected, a, b);
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ pub mod util;
|
||||
|
||||
use rustc_hir as hir;
|
||||
use rustc_middle::ty::error::{ExpectedFound, TypeError};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_middle::ty::{self, Const, Ty};
|
||||
use rustc_span::Span;
|
||||
|
||||
pub use self::FulfillmentErrorCode::*;
|
||||
@ -81,6 +81,7 @@ pub enum FulfillmentErrorCode<'tcx> {
|
||||
CodeSelectionError(SelectionError<'tcx>),
|
||||
CodeProjectionError(MismatchedProjectionTypes<'tcx>),
|
||||
CodeSubtypeError(ExpectedFound<Ty<'tcx>>, TypeError<'tcx>), // always comes from a SubtypePredicate
|
||||
CodeConstEquateError(ExpectedFound<&'tcx Const<'tcx>>, TypeError<'tcx>),
|
||||
CodeAmbiguity,
|
||||
}
|
||||
|
||||
|
@ -41,6 +41,9 @@ impl<'tcx> fmt::Debug for traits::FulfillmentErrorCode<'tcx> {
|
||||
super::CodeSubtypeError(ref a, ref b) => {
|
||||
write!(f, "CodeSubtypeError({:?}, {:?})", a, b)
|
||||
}
|
||||
super::CodeConstEquateError(ref a, ref b) => {
|
||||
write!(f, "CodeConstEquateError({:?}, {:?})", a, b)
|
||||
}
|
||||
super::CodeAmbiguity => write!(f, "Ambiguity"),
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,8 @@ pub fn anonymize_predicate<'tcx>(
|
||||
ty::Predicate::ConstEvaluatable(def_id, substs) => {
|
||||
ty::Predicate::ConstEvaluatable(def_id, substs)
|
||||
}
|
||||
|
||||
ty::Predicate::ConstEquate(c1, c2) => ty::Predicate::ConstEquate(c1, c2),
|
||||
}
|
||||
}
|
||||
|
||||
@ -187,6 +189,10 @@ impl Elaborator<'tcx> {
|
||||
// Currently, we do not elaborate const-evaluatable
|
||||
// predicates.
|
||||
}
|
||||
ty::Predicate::ConstEquate(..) => {
|
||||
// Currently, we do not elaborate const-equate
|
||||
// predicates.
|
||||
}
|
||||
ty::Predicate::RegionOutlives(..) => {
|
||||
// Nothing to elaborate from `'a: 'b`.
|
||||
}
|
||||
|
@ -1221,7 +1221,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TrivialConstraints {
|
||||
ObjectSafe(..) |
|
||||
ClosureKind(..) |
|
||||
Subtype(..) |
|
||||
ConstEvaluatable(..) => continue,
|
||||
ConstEvaluatable(..) |
|
||||
ConstEquate(..) => continue,
|
||||
};
|
||||
if predicate.is_global() {
|
||||
cx.struct_span_lint(TRIVIAL_BOUNDS, span, |lint| {
|
||||
|
@ -1339,7 +1339,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
|
||||
/// What mode(s) of borrowck should we run? AST? MIR? both?
|
||||
/// (Also considers the `#![feature(nll)]` setting.)
|
||||
pub fn borrowck_mode(&self) -> BorrowckMode {
|
||||
pub fn borrowck_mode(self) -> BorrowckMode {
|
||||
// Here are the main constraints we need to deal with:
|
||||
//
|
||||
// 1. An opts.borrowck_mode of `BorrowckMode::Migrate` is
|
||||
@ -1369,6 +1369,13 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
self.sess.opts.borrowck_mode
|
||||
}
|
||||
|
||||
/// If `true`, we should use lazy normalization for constants, otherwise
|
||||
/// we still evaluate them eagerly.
|
||||
#[inline]
|
||||
pub fn lazy_normalization(self) -> bool {
|
||||
self.features().const_generics
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn local_crate_exports_generics(self) -> bool {
|
||||
debug_assert!(self.sess.opts.share_generics());
|
||||
|
@ -1054,6 +1054,9 @@ pub enum Predicate<'tcx> {
|
||||
|
||||
/// Constant initializer must evaluate successfully.
|
||||
ConstEvaluatable(DefId, SubstsRef<'tcx>),
|
||||
|
||||
/// Constants must be equal. The first component is the const that is expected.
|
||||
ConstEquate(&'tcx Const<'tcx>, &'tcx Const<'tcx>),
|
||||
}
|
||||
|
||||
/// The crate outlives map is computed during typeck and contains the
|
||||
@ -1172,6 +1175,9 @@ impl<'tcx> Predicate<'tcx> {
|
||||
Predicate::ConstEvaluatable(def_id, const_substs) => {
|
||||
Predicate::ConstEvaluatable(def_id, const_substs.subst(tcx, substs))
|
||||
}
|
||||
Predicate::ConstEquate(c1, c2) => {
|
||||
Predicate::ConstEquate(c1.subst(tcx, substs), c2.subst(tcx, substs))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1349,7 +1355,8 @@ impl<'tcx> Predicate<'tcx> {
|
||||
| Predicate::ObjectSafe(..)
|
||||
| Predicate::ClosureKind(..)
|
||||
| Predicate::TypeOutlives(..)
|
||||
| Predicate::ConstEvaluatable(..) => None,
|
||||
| Predicate::ConstEvaluatable(..)
|
||||
| Predicate::ConstEquate(..) => None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1363,7 +1370,8 @@ impl<'tcx> Predicate<'tcx> {
|
||||
| Predicate::WellFormed(..)
|
||||
| Predicate::ObjectSafe(..)
|
||||
| Predicate::ClosureKind(..)
|
||||
| Predicate::ConstEvaluatable(..) => None,
|
||||
| Predicate::ConstEvaluatable(..)
|
||||
| Predicate::ConstEquate(..) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -83,6 +83,11 @@ fn compute_components(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, out: &mut SmallVec<[Compo
|
||||
}
|
||||
}
|
||||
|
||||
ty::Array(element, _) => {
|
||||
// Don't look into the len const as it doesn't affect regions
|
||||
compute_components(tcx, element, out);
|
||||
}
|
||||
|
||||
ty::Closure(_, ref substs) => {
|
||||
for upvar_ty in substs.as_closure().upvar_tys() {
|
||||
compute_components(tcx, upvar_ty, out);
|
||||
@ -158,7 +163,6 @@ fn compute_components(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, out: &mut SmallVec<[Compo
|
||||
ty::Opaque(..) | // OutlivesNominalType (ish)
|
||||
ty::Foreign(..) | // OutlivesNominalType
|
||||
ty::Str | // OutlivesScalar (ish)
|
||||
ty::Array(..) | // ...
|
||||
ty::Slice(..) | // ...
|
||||
ty::RawPtr(..) | // ...
|
||||
ty::Ref(..) | // OutlivesReference
|
||||
|
@ -2058,6 +2058,13 @@ define_print_and_forward_display! {
|
||||
print_value_path(def_id, substs),
|
||||
write("` can be evaluated"))
|
||||
}
|
||||
ty::Predicate::ConstEquate(c1, c2) => {
|
||||
p!(write("the constant `"),
|
||||
print(c1),
|
||||
write("` equals `"),
|
||||
print(c2),
|
||||
write("`"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -431,6 +431,9 @@ pub fn super_relate_tys<R: TypeRelation<'tcx>>(
|
||||
let t = relation.relate(&a_t, &b_t)?;
|
||||
match relation.relate(&sz_a, &sz_b) {
|
||||
Ok(sz) => Ok(tcx.mk_ty(ty::Array(t, sz))),
|
||||
// FIXME(#72219) Implement improved diagnostics for mismatched array
|
||||
// length?
|
||||
Err(err) if relation.tcx().lazy_normalization() => Err(err),
|
||||
Err(err) => {
|
||||
// Check whether the lengths are both concrete/known values,
|
||||
// but are unequal, for better diagnostics.
|
||||
|
@ -240,6 +240,7 @@ impl fmt::Debug for ty::Predicate<'tcx> {
|
||||
ty::Predicate::ConstEvaluatable(def_id, substs) => {
|
||||
write!(f, "ConstEvaluatable({:?}, {:?})", def_id, substs)
|
||||
}
|
||||
ty::Predicate::ConstEquate(c1, c2) => write!(f, "ConstEquate({:?}, {:?})", c1, c2),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -492,6 +493,9 @@ impl<'a, 'tcx> Lift<'tcx> for ty::Predicate<'a> {
|
||||
ty::Predicate::ConstEvaluatable(def_id, substs) => {
|
||||
tcx.lift(&substs).map(|substs| ty::Predicate::ConstEvaluatable(def_id, substs))
|
||||
}
|
||||
ty::Predicate::ConstEquate(c1, c2) => {
|
||||
tcx.lift(&(c1, c2)).map(|(c1, c2)| ty::Predicate::ConstEquate(c1, c2))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use rustc_infer::infer::nll_relate::{NormalizationStrategy, TypeRelating, TypeRe
|
||||
use rustc_infer::infer::{InferCtxt, NLLRegionVariableOrigin};
|
||||
use rustc_middle::mir::ConstraintCategory;
|
||||
use rustc_middle::ty::relate::TypeRelation;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_middle::ty::{self, Const, Ty};
|
||||
use rustc_trait_selection::traits::query::Fallible;
|
||||
|
||||
use crate::borrow_check::constraints::OutlivesConstraint;
|
||||
@ -99,6 +99,10 @@ impl TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
// We don't have to worry about the equality of consts during borrow checking
|
||||
// as consts always have a static lifetime.
|
||||
fn const_equate(&mut self, _a: &'tcx Const<'tcx>, _b: &'tcx Const<'tcx>) {}
|
||||
|
||||
fn normalization() -> NormalizationStrategy {
|
||||
NormalizationStrategy::Eager
|
||||
}
|
||||
|
@ -28,7 +28,8 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>) -
|
||||
| Predicate::TypeOutlives(_)
|
||||
| Predicate::WellFormed(_)
|
||||
| Predicate::Projection(_)
|
||||
| Predicate::ConstEvaluatable(..) => continue,
|
||||
| Predicate::ConstEvaluatable(..)
|
||||
| Predicate::ConstEquate(..) => continue,
|
||||
Predicate::ObjectSafe(_) => {
|
||||
bug!("object safe predicate on function: {:#?}", predicate)
|
||||
}
|
||||
|
@ -1277,7 +1277,8 @@ crate fn required_region_bounds(
|
||||
| ty::Predicate::ObjectSafe(..)
|
||||
| ty::Predicate::ClosureKind(..)
|
||||
| ty::Predicate::RegionOutlives(..)
|
||||
| ty::Predicate::ConstEvaluatable(..) => None,
|
||||
| ty::Predicate::ConstEvaluatable(..)
|
||||
| ty::Predicate::ConstEquate(..) => None,
|
||||
ty::Predicate::TypeOutlives(predicate) => {
|
||||
// Search for a bound of the form `erased_self_ty
|
||||
// : 'a`, but be wary of something like `for<'a>
|
||||
|
@ -615,6 +615,17 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
||||
obligation
|
||||
)
|
||||
}
|
||||
|
||||
ty::Predicate::ConstEquate(..) => {
|
||||
// Errors for `ConstEquate` predicates show up as
|
||||
// `SelectionError::ConstEvalFailure`,
|
||||
// not `Unimplemented`.
|
||||
span_bug!(
|
||||
span,
|
||||
"const-equate requirement gave wrong error: `{:?}`",
|
||||
obligation
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1092,6 +1103,15 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> {
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
FulfillmentErrorCode::CodeConstEquateError(ref expected_found, ref err) => {
|
||||
self.report_mismatched_consts(
|
||||
&error.obligation.cause,
|
||||
expected_found.expected,
|
||||
expected_found.found,
|
||||
err.clone(),
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,9 +2,11 @@ use crate::infer::{InferCtxt, TyOrConstInferVar};
|
||||
use rustc_data_structures::obligation_forest::ProcessResult;
|
||||
use rustc_data_structures::obligation_forest::{DoCompleted, Error, ForestObligation};
|
||||
use rustc_data_structures::obligation_forest::{ObligationForest, ObligationProcessor};
|
||||
use rustc_errors::ErrorReported;
|
||||
use rustc_infer::traits::{TraitEngine, TraitEngineExt as _};
|
||||
use rustc_middle::mir::interpret::ErrorHandled;
|
||||
use rustc_middle::ty::error::ExpectedFound;
|
||||
use rustc_middle::ty::{self, ToPolyTraitRef, Ty, TypeFoldable};
|
||||
use rustc_middle::ty::{self, Const, ToPolyTraitRef, Ty, TypeFoldable};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use super::project;
|
||||
@ -520,6 +522,68 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> {
|
||||
Err(err) => ProcessResult::Error(CodeSelectionError(ConstEvalFailure(err))),
|
||||
}
|
||||
}
|
||||
|
||||
ty::Predicate::ConstEquate(c1, c2) => {
|
||||
debug!("equating consts: c1={:?} c2={:?}", c1, c2);
|
||||
|
||||
let stalled_on = &mut pending_obligation.stalled_on;
|
||||
|
||||
let mut evaluate = |c: &'tcx Const<'tcx>| {
|
||||
if let ty::ConstKind::Unevaluated(def_id, substs, promoted) = c.val {
|
||||
match self.selcx.infcx().const_eval_resolve(
|
||||
obligation.param_env,
|
||||
def_id,
|
||||
substs,
|
||||
promoted,
|
||||
Some(obligation.cause.span),
|
||||
) {
|
||||
Ok(val) => Ok(Const::from_value(self.selcx.tcx(), val, c.ty)),
|
||||
Err(ErrorHandled::TooGeneric) => {
|
||||
stalled_on.append(
|
||||
&mut substs
|
||||
.types()
|
||||
.filter_map(|ty| TyOrConstInferVar::maybe_from_ty(ty))
|
||||
.collect(),
|
||||
);
|
||||
Err(ErrorHandled::TooGeneric)
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
} else {
|
||||
Ok(c)
|
||||
}
|
||||
};
|
||||
|
||||
match (evaluate(c1), evaluate(c2)) {
|
||||
(Ok(c1), Ok(c2)) => {
|
||||
match self
|
||||
.selcx
|
||||
.infcx()
|
||||
.at(&obligation.cause, obligation.param_env)
|
||||
.eq(c1, c2)
|
||||
{
|
||||
Ok(_) => ProcessResult::Changed(vec![]),
|
||||
Err(err) => {
|
||||
ProcessResult::Error(FulfillmentErrorCode::CodeConstEquateError(
|
||||
ExpectedFound::new(true, c1, c2),
|
||||
err,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
(Err(ErrorHandled::Reported(ErrorReported)), _)
|
||||
| (_, Err(ErrorHandled::Reported(ErrorReported))) => ProcessResult::Error(
|
||||
CodeSelectionError(ConstEvalFailure(ErrorHandled::Reported(ErrorReported))),
|
||||
),
|
||||
(Err(ErrorHandled::Linted), _) | (_, Err(ErrorHandled::Linted)) => span_bug!(
|
||||
obligation.cause.span(self.selcx.tcx()),
|
||||
"ConstEquate: const_eval_resolve returned an unexpected error"
|
||||
),
|
||||
(Err(ErrorHandled::TooGeneric), _) | (_, Err(ErrorHandled::TooGeneric)) => {
|
||||
ProcessResult::Unchanged
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,8 +16,9 @@ use crate::traits::{self, Obligation, ObligationCause};
|
||||
use rustc_errors::{Applicability, FatalError};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, Subst};
|
||||
use rustc_middle::ty::{self, Predicate, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness};
|
||||
use rustc_middle::ty::subst::{GenericArg, InternalSubsts, Subst};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitor, WithConstness};
|
||||
use rustc_middle::ty::{Predicate, ToPredicate};
|
||||
use rustc_session::lint::builtin::WHERE_CLAUSES_OBJECT_SAFETY;
|
||||
use rustc_span::symbol::Symbol;
|
||||
use rustc_span::Span;
|
||||
@ -281,7 +282,8 @@ fn predicates_reference_self(
|
||||
| ty::Predicate::RegionOutlives(..)
|
||||
| ty::Predicate::ClosureKind(..)
|
||||
| ty::Predicate::Subtype(..)
|
||||
| ty::Predicate::ConstEvaluatable(..) => None,
|
||||
| ty::Predicate::ConstEvaluatable(..)
|
||||
| ty::Predicate::ConstEquate(..) => None,
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
@ -313,7 +315,8 @@ fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
|
||||
| ty::Predicate::ObjectSafe(..)
|
||||
| ty::Predicate::ClosureKind(..)
|
||||
| ty::Predicate::TypeOutlives(..)
|
||||
| ty::Predicate::ConstEvaluatable(..) => false,
|
||||
| ty::Predicate::ConstEvaluatable(..)
|
||||
| ty::Predicate::ConstEquate(..) => false,
|
||||
})
|
||||
}
|
||||
|
||||
@ -724,51 +727,65 @@ fn contains_illegal_self_type_reference<'tcx>(
|
||||
// object type, and we cannot resolve `Self as SomeOtherTrait`
|
||||
// without knowing what `Self` is.
|
||||
|
||||
let mut supertraits: Option<Vec<ty::PolyTraitRef<'tcx>>> = None;
|
||||
let self_ty = tcx.types.self_param;
|
||||
struct IllegalSelfTypeVisitor<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
self_ty: Ty<'tcx>,
|
||||
trait_def_id: DefId,
|
||||
supertraits: Option<Vec<ty::PolyTraitRef<'tcx>>>,
|
||||
}
|
||||
|
||||
let mut walker = ty.walk();
|
||||
while let Some(arg) = walker.next() {
|
||||
if arg == self_ty.into() {
|
||||
return true;
|
||||
}
|
||||
impl<'tcx> TypeVisitor<'tcx> for IllegalSelfTypeVisitor<'tcx> {
|
||||
fn visit_ty(&mut self, t: Ty<'tcx>) -> bool {
|
||||
match t.kind {
|
||||
ty::Param(_) => t == self.self_ty,
|
||||
ty::Projection(ref data) => {
|
||||
// This is a projected type `<Foo as SomeTrait>::X`.
|
||||
|
||||
// Special-case projections (everything else is walked normally).
|
||||
if let GenericArgKind::Type(ty) = arg.unpack() {
|
||||
if let ty::Projection(ref data) = ty.kind {
|
||||
// This is a projected type `<Foo as SomeTrait>::X`.
|
||||
// Compute supertraits of current trait lazily.
|
||||
if self.supertraits.is_none() {
|
||||
let trait_ref =
|
||||
ty::Binder::bind(ty::TraitRef::identity(self.tcx, self.trait_def_id));
|
||||
self.supertraits = Some(traits::supertraits(self.tcx, trait_ref).collect());
|
||||
}
|
||||
|
||||
// Compute supertraits of current trait lazily.
|
||||
if supertraits.is_none() {
|
||||
let trait_ref = ty::Binder::bind(ty::TraitRef::identity(tcx, trait_def_id));
|
||||
supertraits = Some(traits::supertraits(tcx, trait_ref).collect());
|
||||
// Determine whether the trait reference `Foo as
|
||||
// SomeTrait` is in fact a supertrait of the
|
||||
// current trait. In that case, this type is
|
||||
// legal, because the type `X` will be specified
|
||||
// in the object type. Note that we can just use
|
||||
// direct equality here because all of these types
|
||||
// are part of the formal parameter listing, and
|
||||
// hence there should be no inference variables.
|
||||
let projection_trait_ref = ty::Binder::bind(data.trait_ref(self.tcx));
|
||||
let is_supertrait_of_current_trait =
|
||||
self.supertraits.as_ref().unwrap().contains(&projection_trait_ref);
|
||||
|
||||
if is_supertrait_of_current_trait {
|
||||
false // do not walk contained types, do not report error, do collect $200
|
||||
} else {
|
||||
t.super_visit_with(self) // DO walk contained types, POSSIBLY reporting an error
|
||||
}
|
||||
}
|
||||
|
||||
// Determine whether the trait reference `Foo as
|
||||
// SomeTrait` is in fact a supertrait of the
|
||||
// current trait. In that case, this type is
|
||||
// legal, because the type `X` will be specified
|
||||
// in the object type. Note that we can just use
|
||||
// direct equality here because all of these types
|
||||
// are part of the formal parameter listing, and
|
||||
// hence there should be no inference variables.
|
||||
let projection_trait_ref = ty::Binder::bind(data.trait_ref(tcx));
|
||||
let is_supertrait_of_current_trait =
|
||||
supertraits.as_ref().unwrap().contains(&projection_trait_ref);
|
||||
|
||||
if is_supertrait_of_current_trait {
|
||||
// Do not walk contained types, do not report error, do collect $200.
|
||||
walker.skip_current_subtree();
|
||||
}
|
||||
|
||||
// DO walk contained types, POSSIBLY reporting an error.
|
||||
_ => t.super_visit_with(self), // walk contained types, if any
|
||||
}
|
||||
}
|
||||
|
||||
// Walk contained types, if any.
|
||||
fn visit_const(&mut self, _c: &ty::Const<'tcx>) -> bool {
|
||||
// FIXME(#72219) Look into the unevaluated constants for object safety violations.
|
||||
// Do not walk substitutions of unevaluated consts, as they contain `Self`, even
|
||||
// though the const expression doesn't necessary use it. Currently type variables
|
||||
// inside array length expressions are forbidden, so they can't break the above
|
||||
// rules.
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
ty.visit_with(&mut IllegalSelfTypeVisitor {
|
||||
tcx,
|
||||
self_ty: tcx.types.self_param,
|
||||
trait_def_id,
|
||||
supertraits: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn provide(providers: &mut ty::query::Providers<'_>) {
|
||||
|
@ -388,8 +388,12 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> {
|
||||
}
|
||||
|
||||
fn fold_const(&mut self, constant: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
|
||||
let constant = constant.super_fold_with(self);
|
||||
constant.eval(self.selcx.tcx(), self.param_env)
|
||||
if self.selcx.tcx().lazy_normalization() {
|
||||
constant
|
||||
} else {
|
||||
let constant = constant.super_fold_with(self);
|
||||
constant.eval(self.selcx.tcx(), self.param_env)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,11 +38,13 @@ use crate::traits::project::ProjectionCacheKeyExt;
|
||||
use rustc_ast::attr;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
use rustc_errors::ErrorReported;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::lang_items;
|
||||
use rustc_index::bit_set::GrowableBitSet;
|
||||
use rustc_middle::dep_graph::{DepKind, DepNodeIndex};
|
||||
use rustc_middle::mir::interpret::ErrorHandled;
|
||||
use rustc_middle::ty::fast_reject;
|
||||
use rustc_middle::ty::relate::TypeRelation;
|
||||
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst, SubstsRef};
|
||||
@ -503,9 +505,48 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
None,
|
||||
) {
|
||||
Ok(_) => Ok(EvaluatedToOk),
|
||||
Err(ErrorHandled::TooGeneric) => Ok(EvaluatedToAmbig),
|
||||
Err(_) => Ok(EvaluatedToErr),
|
||||
}
|
||||
}
|
||||
|
||||
ty::Predicate::ConstEquate(c1, c2) => {
|
||||
debug!("evaluate_predicate_recursively: equating consts c1={:?} c2={:?}", c1, c2);
|
||||
|
||||
let evaluate = |c: &'tcx ty::Const<'tcx>| {
|
||||
if let ty::ConstKind::Unevaluated(def_id, substs, promoted) = c.val {
|
||||
self.infcx
|
||||
.const_eval_resolve(
|
||||
obligation.param_env,
|
||||
def_id,
|
||||
substs,
|
||||
promoted,
|
||||
Some(obligation.cause.span),
|
||||
)
|
||||
.map(|val| ty::Const::from_value(self.tcx(), val, c.ty))
|
||||
} else {
|
||||
Ok(c)
|
||||
}
|
||||
};
|
||||
|
||||
match (evaluate(c1), evaluate(c2)) {
|
||||
(Ok(c1), Ok(c2)) => {
|
||||
match self.infcx().at(&obligation.cause, obligation.param_env).eq(c1, c2) {
|
||||
Ok(_) => Ok(EvaluatedToOk),
|
||||
Err(_) => Ok(EvaluatedToErr),
|
||||
}
|
||||
}
|
||||
(Err(ErrorHandled::Reported(ErrorReported)), _)
|
||||
| (_, Err(ErrorHandled::Reported(ErrorReported))) => Ok(EvaluatedToErr),
|
||||
(Err(ErrorHandled::Linted), _) | (_, Err(ErrorHandled::Linted)) => span_bug!(
|
||||
obligation.cause.span(self.tcx()),
|
||||
"ConstEquate: const_eval_resolve returned an unexpected error"
|
||||
),
|
||||
(Err(ErrorHandled::TooGeneric), _) | (_, Err(ErrorHandled::TooGeneric)) => {
|
||||
Ok(EvaluatedToAmbig)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,6 +102,10 @@ pub fn predicate_obligations<'a, 'tcx>(
|
||||
wf.compute(ty);
|
||||
}
|
||||
}
|
||||
ty::Predicate::ConstEquate(c1, c2) => {
|
||||
wf.compute(c1.ty);
|
||||
wf.compute(c2.ty);
|
||||
}
|
||||
}
|
||||
|
||||
wf.normalize()
|
||||
|
@ -126,9 +126,8 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::InEnvironment<chalk_ir::Goal<RustInterner<'
|
||||
| ty::Predicate::ObjectSafe(..)
|
||||
| ty::Predicate::ClosureKind(..)
|
||||
| ty::Predicate::Subtype(..)
|
||||
| ty::Predicate::ConstEvaluatable(..) => {
|
||||
bug!("unexpected predicate {}", predicate)
|
||||
}
|
||||
| ty::Predicate::ConstEvaluatable(..)
|
||||
| ty::Predicate::ConstEquate(..) => bug!("unexpected predicate {}", predicate),
|
||||
}
|
||||
}
|
||||
ChalkEnvironmentClause::TypeFromEnv(ty) => Some(
|
||||
@ -192,9 +191,8 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::GoalData<RustInterner<'tcx>>> for ty::Predi
|
||||
Predicate::ObjectSafe(..)
|
||||
| Predicate::ClosureKind(..)
|
||||
| Predicate::Subtype(..)
|
||||
| Predicate::ConstEvaluatable(..) => {
|
||||
chalk_ir::GoalData::All(chalk_ir::Goals::new(interner))
|
||||
}
|
||||
| Predicate::ConstEvaluatable(..)
|
||||
| Predicate::ConstEquate(..) => chalk_ir::GoalData::All(chalk_ir::Goals::new(interner)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -459,7 +457,8 @@ impl<'tcx> LowerInto<'tcx, Option<chalk_ir::QuantifiedWhereClause<RustInterner<'
|
||||
Predicate::ObjectSafe(..)
|
||||
| Predicate::ClosureKind(..)
|
||||
| Predicate::Subtype(..)
|
||||
| Predicate::ConstEvaluatable(..) => bug!("unexpected predicate {}", &self),
|
||||
| Predicate::ConstEvaluatable(..)
|
||||
| Predicate::ConstEquate(..) => bug!("unexpected predicate {}", &self),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -100,7 +100,8 @@ fn compute_implied_outlives_bounds<'tcx>(
|
||||
| ty::Predicate::Projection(..)
|
||||
| ty::Predicate::ClosureKind(..)
|
||||
| ty::Predicate::ObjectSafe(..)
|
||||
| ty::Predicate::ConstEvaluatable(..) => vec![],
|
||||
| ty::Predicate::ConstEvaluatable(..)
|
||||
| ty::Predicate::ConstEquate(..) => vec![],
|
||||
|
||||
ty::Predicate::WellFormed(subty) => {
|
||||
wf_types.push(subty);
|
||||
|
@ -48,6 +48,7 @@ fn not_outlives_predicate(p: &ty::Predicate<'_>) -> bool {
|
||||
| ty::Predicate::ObjectSafe(..)
|
||||
| ty::Predicate::ClosureKind(..)
|
||||
| ty::Predicate::Subtype(..)
|
||||
| ty::Predicate::ConstEvaluatable(..) => true,
|
||||
| ty::Predicate::ConstEvaluatable(..)
|
||||
| ty::Predicate::ConstEquate(..) => true,
|
||||
}
|
||||
}
|
||||
|
@ -810,7 +810,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
|
||||
| ty::Predicate::ObjectSafe(..)
|
||||
| ty::Predicate::ClosureKind(..)
|
||||
| ty::Predicate::TypeOutlives(..)
|
||||
| ty::Predicate::ConstEvaluatable(..) => None,
|
||||
| ty::Predicate::ConstEvaluatable(..)
|
||||
| ty::Predicate::ConstEquate(..) => None,
|
||||
});
|
||||
|
||||
self.elaborate_bounds(bounds, |this, poly_trait_ref, item| {
|
||||
|
@ -1648,6 +1648,16 @@ fn check_opaque_for_inheriting_lifetimes(tcx: TyCtxt<'tcx>, def_id: LocalDefId,
|
||||
|
||||
r.super_visit_with(self)
|
||||
}
|
||||
|
||||
fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> bool {
|
||||
if let ty::ConstKind::Unevaluated(..) = c.val {
|
||||
// FIXME(#72219) We currenctly don't detect lifetimes within substs
|
||||
// which would violate this check. Even though the particular substitution is not used
|
||||
// within the const, this should still be fixed.
|
||||
return false;
|
||||
}
|
||||
c.super_visit_with(self)
|
||||
}
|
||||
}
|
||||
|
||||
let prohibit_opaque = match item.kind {
|
||||
@ -3858,6 +3868,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
ty::Predicate::WellFormed(..) => None,
|
||||
ty::Predicate::ObjectSafe(..) => None,
|
||||
ty::Predicate::ConstEvaluatable(..) => None,
|
||||
ty::Predicate::ConstEquate(..) => None,
|
||||
// N.B., this predicate is created by breaking down a
|
||||
// `ClosureType: FnFoo()` predicate, where
|
||||
// `ClosureType` represents some `Closure`. It can't
|
||||
|
@ -1164,7 +1164,8 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
|
||||
let parent_id = tcx.hir().get_parent_item(hir_id);
|
||||
Some(tcx.hir().local_def_id(parent_id).to_def_id())
|
||||
}
|
||||
// FIXME(#43408) enable this always when we get lazy normalization.
|
||||
// FIXME(#43408) always enable this once `lazy_normalization` is
|
||||
// stable enough and does not need a feature gate anymore.
|
||||
Node::AnonConst(_) => {
|
||||
let parent_id = tcx.hir().get_parent_item(hir_id);
|
||||
let parent_def_id = tcx.hir().local_def_id(parent_id);
|
||||
@ -1172,7 +1173,7 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
|
||||
// HACK(eddyb) this provides the correct generics when
|
||||
// `feature(const_generics)` is enabled, so that const expressions
|
||||
// used with const generics, e.g. `Foo<{N+1}>`, can work at all.
|
||||
if tcx.features().const_generics {
|
||||
if tcx.lazy_normalization() {
|
||||
Some(parent_def_id.to_def_id())
|
||||
} else {
|
||||
let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id));
|
||||
|
@ -413,6 +413,7 @@ fn trait_predicate_kind<'tcx>(
|
||||
| ty::Predicate::Subtype(_)
|
||||
| ty::Predicate::ObjectSafe(_)
|
||||
| ty::Predicate::ClosureKind(..)
|
||||
| ty::Predicate::ConstEvaluatable(..) => None,
|
||||
| ty::Predicate::ConstEvaluatable(..)
|
||||
| ty::Predicate::ConstEquate(..) => None,
|
||||
}
|
||||
}
|
||||
|
@ -58,7 +58,8 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> {
|
||||
| ty::Predicate::ObjectSafe(..)
|
||||
| ty::Predicate::ClosureKind(..)
|
||||
| ty::Predicate::Subtype(..)
|
||||
| ty::Predicate::ConstEvaluatable(..) => (),
|
||||
| ty::Predicate::ConstEvaluatable(..)
|
||||
| ty::Predicate::ConstEquate(..) => (),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -493,7 +493,8 @@ impl<'a> Clean<Option<WherePredicate>> for ty::Predicate<'a> {
|
||||
Predicate::WellFormed(..)
|
||||
| Predicate::ObjectSafe(..)
|
||||
| Predicate::ClosureKind(..)
|
||||
| Predicate::ConstEvaluatable(..) => panic!("not user writable"),
|
||||
| Predicate::ConstEvaluatable(..)
|
||||
| Predicate::ConstEquate(..) => panic!("not user writable"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,8 +13,8 @@ error[E0308]: mismatched types
|
||||
LL | x = Const::<{ [4] }> {};
|
||||
| ^^^^^^^^^^^^^^^^^^^ expected `3usize`, found `4usize`
|
||||
|
|
||||
= note: expected struct `Const<[3usize]>`
|
||||
found struct `Const<[4usize]>`
|
||||
= note: expected type `[3usize]`
|
||||
found type `[4usize]`
|
||||
|
||||
error: aborting due to previous error; 1 warning emitted
|
||||
|
||||
|
@ -11,12 +11,10 @@ error[E0308]: mismatched types
|
||||
--> $DIR/fn-const-param-infer.rs:16:31
|
||||
|
|
||||
LL | let _: Checked<not_one> = Checked::<not_two>;
|
||||
| ---------------- ^^^^^^^^^^^^^^^^^^ expected `{not_one as fn(usize) -> bool}`, found `{not_two as fn(usize) -> bool}`
|
||||
| |
|
||||
| expected due to this
|
||||
| ^^^^^^^^^^^^^^^^^^ expected `{not_one as fn(usize) -> bool}`, found `{not_two as fn(usize) -> bool}`
|
||||
|
|
||||
= note: expected struct `Checked<{not_one as fn(usize) -> bool}>`
|
||||
found struct `Checked<{not_two as fn(usize) -> bool}>`
|
||||
= note: expected type `{not_one as fn(usize) -> bool}`
|
||||
found type `{not_two as fn(usize) -> bool}`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/fn-const-param-infer.rs:20:24
|
||||
@ -37,12 +35,10 @@ error[E0308]: mismatched types
|
||||
--> $DIR/fn-const-param-infer.rs:25:40
|
||||
|
|
||||
LL | let _: Checked<{generic::<u32>}> = Checked::<{generic::<u16>}>;
|
||||
| ------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{generic::<u32> as fn(usize) -> bool}`, found `{generic::<u16> as fn(usize) -> bool}`
|
||||
| |
|
||||
| expected due to this
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{generic::<u32> as fn(usize) -> bool}`, found `{generic::<u16> as fn(usize) -> bool}`
|
||||
|
|
||||
= note: expected struct `Checked<{generic::<u32> as fn(usize) -> bool}>`
|
||||
found struct `Checked<{generic::<u16> as fn(usize) -> bool}>`
|
||||
= note: expected type `{generic::<u32> as fn(usize) -> bool}`
|
||||
found type `{generic::<u16> as fn(usize) -> bool}`
|
||||
|
||||
error: aborting due to 4 previous errors; 1 warning emitted
|
||||
|
||||
|
24
src/test/ui/const-generics/issues/issue-61935.rs
Normal file
24
src/test/ui/const-generics/issues/issue-61935.rs
Normal file
@ -0,0 +1,24 @@
|
||||
// check-pass
|
||||
|
||||
#![feature(const_generics)]
|
||||
//~^ WARN the feature `const_generics` is incomplete
|
||||
|
||||
trait Foo {}
|
||||
|
||||
impl<const N: usize> Foo for [(); N]
|
||||
where
|
||||
Self:FooImpl<{N==0}>
|
||||
{}
|
||||
|
||||
trait FooImpl<const IS_ZERO: bool>{}
|
||||
|
||||
impl FooImpl<true> for [(); 0] {}
|
||||
|
||||
impl<const N:usize> FooImpl<false> for [();N] {}
|
||||
|
||||
fn foo(_: impl Foo) {}
|
||||
|
||||
fn main() {
|
||||
foo([]);
|
||||
foo([()]);
|
||||
}
|
11
src/test/ui/const-generics/issues/issue-61935.stderr
Normal file
11
src/test/ui/const-generics/issues/issue-61935.stderr
Normal file
@ -0,0 +1,11 @@
|
||||
warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||
--> $DIR/issue-61935.rs:3:12
|
||||
|
|
||||
LL | #![feature(const_generics)]
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `#[warn(incomplete_features)]` on by default
|
||||
= note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
|
||||
|
||||
warning: 1 warning emitted
|
||||
|
@ -16,8 +16,7 @@ struct ArrayHolder<const X: usize>([u32; X]);
|
||||
impl<const X: usize> ArrayHolder<X> {
|
||||
pub const fn new() -> Self {
|
||||
ArrayHolder([0; Self::SIZE])
|
||||
//~^ ERROR: mismatched types
|
||||
//~| ERROR constant expression depends on a generic parameter
|
||||
//~^ ERROR constant expression depends on a generic parameter
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,3 @@
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/issue-62504.rs:18:21
|
||||
|
|
||||
LL | ArrayHolder([0; Self::SIZE])
|
||||
| ^^^^^^^^^^^^^^^ expected `X`, found `Self::SIZE`
|
||||
|
|
||||
= note: expected array `[u32; X]`
|
||||
found array `[u32; _]`
|
||||
|
||||
error: constant expression depends on a generic parameter
|
||||
--> $DIR/issue-62504.rs:18:25
|
||||
|
|
||||
@ -15,6 +6,5 @@ LL | ArrayHolder([0; Self::SIZE])
|
||||
|
|
||||
= note: this may fail depending on what value the parameter takes
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
||||
|
@ -1,5 +1,6 @@
|
||||
#![allow(incomplete_features, dead_code, unconditional_recursion)]
|
||||
#![feature(const_generics)]
|
||||
#![feature(lazy_normalization_consts)]
|
||||
|
||||
fn fact<const N: usize>() {
|
||||
fact::<{ N - 1 }>();
|
||||
|
@ -1,5 +1,5 @@
|
||||
error: constant expression depends on a generic parameter
|
||||
--> $DIR/issue-66205.rs:5:12
|
||||
--> $DIR/issue-66205.rs:6:12
|
||||
|
|
||||
LL | fact::<{ N - 1 }>();
|
||||
| ^^^^^^^^^
|
||||
|
32
src/test/ui/const-generics/issues/issue-67185-1.rs
Normal file
32
src/test/ui/const-generics/issues/issue-67185-1.rs
Normal file
@ -0,0 +1,32 @@
|
||||
// check-pass
|
||||
|
||||
#![feature(const_generics)]
|
||||
//~^ WARN the feature `const_generics` is incomplete
|
||||
|
||||
trait Baz {
|
||||
type Quaks;
|
||||
}
|
||||
impl Baz for u8 {
|
||||
type Quaks = [u16; 3];
|
||||
}
|
||||
|
||||
trait Bar {}
|
||||
impl Bar for [u16; 3] {}
|
||||
impl Bar for [[u16; 3]; 2] {}
|
||||
|
||||
trait Foo
|
||||
where
|
||||
[<u8 as Baz>::Quaks; 2]: Bar,
|
||||
<u8 as Baz>::Quaks: Bar,
|
||||
{
|
||||
}
|
||||
|
||||
struct FooImpl;
|
||||
|
||||
impl Foo for FooImpl {}
|
||||
|
||||
fn f(_: impl Foo) {}
|
||||
|
||||
fn main() {
|
||||
f(FooImpl)
|
||||
}
|
11
src/test/ui/const-generics/issues/issue-67185-1.stderr
Normal file
11
src/test/ui/const-generics/issues/issue-67185-1.stderr
Normal file
@ -0,0 +1,11 @@
|
||||
warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||
--> $DIR/issue-67185-1.rs:3:12
|
||||
|
|
||||
LL | #![feature(const_generics)]
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `#[warn(incomplete_features)]` on by default
|
||||
= note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
|
||||
|
||||
warning: 1 warning emitted
|
||||
|
35
src/test/ui/const-generics/issues/issue-67185-2.rs
Normal file
35
src/test/ui/const-generics/issues/issue-67185-2.rs
Normal file
@ -0,0 +1,35 @@
|
||||
#![feature(const_generics)]
|
||||
//~^ WARN the feature `const_generics` is incomplete
|
||||
|
||||
trait Baz {
|
||||
type Quaks;
|
||||
}
|
||||
impl Baz for u8 {
|
||||
type Quaks = [u16; 3];
|
||||
}
|
||||
|
||||
trait Bar {}
|
||||
impl Bar for [u16; 4] {}
|
||||
impl Bar for [[u16; 3]; 3] {}
|
||||
|
||||
trait Foo //~ ERROR the trait bound `[u16; 3]: Bar` is not satisfied [E0277]
|
||||
//~^ ERROR the trait bound `[[u16; 3]; 2]: Bar` is not satisfied [E0277]
|
||||
where
|
||||
[<u8 as Baz>::Quaks; 2]: Bar,
|
||||
<u8 as Baz>::Quaks: Bar,
|
||||
{
|
||||
}
|
||||
|
||||
struct FooImpl;
|
||||
|
||||
impl Foo for FooImpl {}
|
||||
//~^ ERROR the trait bound `[u16; 3]: Bar` is not satisfied [E0277]
|
||||
//~^^ ERROR the trait bound `[[u16; 3]; 2]: Bar` is not satisfied [E0277]
|
||||
|
||||
fn f(_: impl Foo) {}
|
||||
//~^ ERROR the trait bound `[u16; 3]: Bar` is not satisfied [E0277]
|
||||
//~^^ ERROR the trait bound `[[u16; 3]; 2]: Bar` is not satisfied [E0277]
|
||||
|
||||
fn main() {
|
||||
f(FooImpl)
|
||||
}
|
112
src/test/ui/const-generics/issues/issue-67185-2.stderr
Normal file
112
src/test/ui/const-generics/issues/issue-67185-2.stderr
Normal file
@ -0,0 +1,112 @@
|
||||
warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||
--> $DIR/issue-67185-2.rs:1:12
|
||||
|
|
||||
LL | #![feature(const_generics)]
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `#[warn(incomplete_features)]` on by default
|
||||
= note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
|
||||
|
||||
error[E0277]: the trait bound `[u16; 3]: Bar` is not satisfied
|
||||
--> $DIR/issue-67185-2.rs:15:1
|
||||
|
|
||||
LL | / trait Foo
|
||||
LL | |
|
||||
LL | | where
|
||||
LL | | [<u8 as Baz>::Quaks; 2]: Bar,
|
||||
LL | | <u8 as Baz>::Quaks: Bar,
|
||||
LL | | {
|
||||
LL | | }
|
||||
| |_^ the trait `Bar` is not implemented for `[u16; 3]`
|
||||
|
|
||||
= help: the following implementations were found:
|
||||
<[[u16; 3]; 3] as Bar>
|
||||
<[u16; 4] as Bar>
|
||||
= help: see issue #48214
|
||||
= help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
|
||||
|
||||
error[E0277]: the trait bound `[[u16; 3]; 2]: Bar` is not satisfied
|
||||
--> $DIR/issue-67185-2.rs:15:1
|
||||
|
|
||||
LL | / trait Foo
|
||||
LL | |
|
||||
LL | | where
|
||||
LL | | [<u8 as Baz>::Quaks; 2]: Bar,
|
||||
LL | | <u8 as Baz>::Quaks: Bar,
|
||||
LL | | {
|
||||
LL | | }
|
||||
| |_^ the trait `Bar` is not implemented for `[[u16; 3]; 2]`
|
||||
|
|
||||
= help: the following implementations were found:
|
||||
<[[u16; 3]; 3] as Bar>
|
||||
<[u16; 4] as Bar>
|
||||
= help: see issue #48214
|
||||
= help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
|
||||
|
||||
error[E0277]: the trait bound `[u16; 3]: Bar` is not satisfied
|
||||
--> $DIR/issue-67185-2.rs:25:6
|
||||
|
|
||||
LL | trait Foo
|
||||
| --- required by a bound in this
|
||||
...
|
||||
LL | <u8 as Baz>::Quaks: Bar,
|
||||
| --- required by this bound in `Foo`
|
||||
...
|
||||
LL | impl Foo for FooImpl {}
|
||||
| ^^^ the trait `Bar` is not implemented for `[u16; 3]`
|
||||
|
|
||||
= help: the following implementations were found:
|
||||
<[[u16; 3]; 3] as Bar>
|
||||
<[u16; 4] as Bar>
|
||||
|
||||
error[E0277]: the trait bound `[[u16; 3]; 2]: Bar` is not satisfied
|
||||
--> $DIR/issue-67185-2.rs:25:6
|
||||
|
|
||||
LL | trait Foo
|
||||
| --- required by a bound in this
|
||||
...
|
||||
LL | [<u8 as Baz>::Quaks; 2]: Bar,
|
||||
| --- required by this bound in `Foo`
|
||||
...
|
||||
LL | impl Foo for FooImpl {}
|
||||
| ^^^ the trait `Bar` is not implemented for `[[u16; 3]; 2]`
|
||||
|
|
||||
= help: the following implementations were found:
|
||||
<[[u16; 3]; 3] as Bar>
|
||||
<[u16; 4] as Bar>
|
||||
|
||||
error[E0277]: the trait bound `[[u16; 3]; 2]: Bar` is not satisfied
|
||||
--> $DIR/issue-67185-2.rs:29:14
|
||||
|
|
||||
LL | trait Foo
|
||||
| --- required by a bound in this
|
||||
...
|
||||
LL | [<u8 as Baz>::Quaks; 2]: Bar,
|
||||
| --- required by this bound in `Foo`
|
||||
...
|
||||
LL | fn f(_: impl Foo) {}
|
||||
| ^^^ the trait `Bar` is not implemented for `[[u16; 3]; 2]`
|
||||
|
|
||||
= help: the following implementations were found:
|
||||
<[[u16; 3]; 3] as Bar>
|
||||
<[u16; 4] as Bar>
|
||||
|
||||
error[E0277]: the trait bound `[u16; 3]: Bar` is not satisfied
|
||||
--> $DIR/issue-67185-2.rs:29:14
|
||||
|
|
||||
LL | trait Foo
|
||||
| --- required by a bound in this
|
||||
...
|
||||
LL | <u8 as Baz>::Quaks: Bar,
|
||||
| --- required by this bound in `Foo`
|
||||
...
|
||||
LL | fn f(_: impl Foo) {}
|
||||
| ^^^ the trait `Bar` is not implemented for `[u16; 3]`
|
||||
|
|
||||
= help: the following implementations were found:
|
||||
<[[u16; 3]; 3] as Bar>
|
||||
<[u16; 4] as Bar>
|
||||
|
||||
error: aborting due to 6 previous errors; 1 warning emitted
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
@ -1,18 +0,0 @@
|
||||
#![feature(const_generics)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
trait Bar<O> {}
|
||||
impl<O> Bar<O> for [u8; O] {}
|
||||
//~^ ERROR expected value, found type parameter `O`
|
||||
|
||||
struct Foo<const O: usize> {}
|
||||
impl<const O: usize> Foo<O>
|
||||
where
|
||||
[u8; O]: Bar<[(); O]>,
|
||||
{
|
||||
fn foo() {}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
Foo::foo();
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
error[E0423]: expected value, found type parameter `O`
|
||||
--> $DIR/issue-69654.rs:5:25
|
||||
|
|
||||
LL | impl<O> Bar<O> for [u8; O] {}
|
||||
| ^ help: a tuple variant with a similar name exists: `Ok`
|
||||
|
|
||||
::: $SRC_DIR/libcore/result.rs:LL:COL
|
||||
|
|
||||
LL | Ok(#[stable(feature = "rust1", since = "1.0.0")] T),
|
||||
| --------------------------------------------------- similarly named tuple variant `Ok` defined here
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0423`.
|
19
src/test/ui/const-generics/lazy-normalization/issue-71922.rs
Normal file
19
src/test/ui/const-generics/lazy-normalization/issue-71922.rs
Normal file
@ -0,0 +1,19 @@
|
||||
// run-pass
|
||||
#![feature(const_generics)]
|
||||
#![allow(incomplete_features)]
|
||||
trait Foo {}
|
||||
|
||||
impl<const N: usize> Foo for [(); N] where Self: FooImpl<{ N == 0 }> {}
|
||||
|
||||
trait FooImpl<const IS_ZERO: bool> {}
|
||||
|
||||
impl FooImpl<{ 0u8 == 0u8 }> for [(); 0] {}
|
||||
|
||||
impl<const N: usize> FooImpl<{ 0u8 != 0u8 }> for [(); N] {}
|
||||
|
||||
fn foo<T: Foo>(_: T) {}
|
||||
|
||||
fn main() {
|
||||
foo([]);
|
||||
foo([()]);
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
// check-pass
|
||||
#![allow(incomplete_features)]
|
||||
#![feature(const_generics)]
|
||||
|
||||
pub trait Foo<const B: bool> {}
|
||||
pub fn bar<T: Foo<{ true }>>() {}
|
||||
|
||||
fn main() {}
|
@ -11,12 +11,10 @@ error[E0308]: mismatched types
|
||||
--> $DIR/raw-ptr-const-param.rs:7:40
|
||||
|
|
||||
LL | let _: Const<{ 15 as *const _ }> = Const::<{ 10 as *const _ }>;
|
||||
| ------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{0xf as *const u32}`, found `{0xa as *const u32}`
|
||||
| |
|
||||
| expected due to this
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{0xf as *const u32}`, found `{0xa as *const u32}`
|
||||
|
|
||||
= note: expected struct `Const<{0xf as *const u32}>`
|
||||
found struct `Const<{0xa as *const u32}>`
|
||||
= note: expected type `{0xf as *const u32}`
|
||||
found type `{0xa as *const u32}`
|
||||
|
||||
error: aborting due to previous error; 1 warning emitted
|
||||
|
||||
|
@ -11,12 +11,10 @@ error[E0308]: mismatched types
|
||||
--> $DIR/types-mismatch-const-args.rs:13:41
|
||||
|
|
||||
LL | let _: A<'a, u32, {2u32}, {3u32}> = A::<'a, u32, {4u32}, {3u32}> { data: PhantomData };
|
||||
| -------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `2u32`, found `4u32`
|
||||
| |
|
||||
| expected due to this
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `2u32`, found `4u32`
|
||||
|
|
||||
= note: expected struct `A<'_, _, 2u32, _>`
|
||||
found struct `A<'_, _, 4u32, _>`
|
||||
= note: expected type `2u32`
|
||||
found type `4u32`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/types-mismatch-const-args.rs:15:41
|
||||
@ -26,8 +24,8 @@ LL | let _: A<'a, u16, {2u32}, {3u32}> = A::<'b, u32, {2u32}, {3u32}> { data
|
||||
| |
|
||||
| expected due to this
|
||||
|
|
||||
= note: expected struct `A<'a, u16, _, _>`
|
||||
found struct `A<'b, u32, _, _>`
|
||||
= note: expected struct `A<'a, u16, {2u32}, {3u32}>`
|
||||
found struct `A<'b, u32, {2u32}, {3u32}>`
|
||||
|
||||
error: aborting due to 2 previous errors; 1 warning emitted
|
||||
|
||||
|
@ -25,7 +25,7 @@ error: non-defining opaque type use in defining scope
|
||||
LL | fn concrete_const() -> OneConst<{123}> {
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: used non-generic constant `123usize` for generic parameter
|
||||
note: used non-generic constant `{123}` for generic parameter
|
||||
--> $DIR/generic_nondefining_use.rs:10:21
|
||||
|
|
||||
LL | type OneConst<const X: usize> = impl Debug;
|
||||
|
Loading…
Reference in New Issue
Block a user