Assume unevaluated consts are equal to the other consts and add ConstEquate obligation. This delays

the need to evaluate consts eagerly and therefore gets around const eval query cycles.
This commit is contained in:
Ben Lewis 2020-02-29 10:03:04 +13:00 committed by Bastian Kauschke
parent 09739c22db
commit 8b14b84933
36 changed files with 370 additions and 121 deletions

View File

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

View File

@ -164,7 +164,6 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> {
(_, ty::ConstKind::Infer(InferConst::Var(vid))) => {
return self.unify_const_variable(!a_is_expected, vid, a);
}
_ => {}
}
@ -375,6 +374,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,6 +650,7 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
}
}
}
ty::ConstKind::Unevaluated(..) => Ok(c),
_ => relate::super_relate_consts(self, c, c),
}
}

View File

@ -4,7 +4,7 @@ use super::Subtype;
use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::TyVar;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::ty::{self, ConstKind, Ty, TyCtxt};
use rustc_hir::def_id::DefId;
@ -119,7 +119,17 @@ impl TypeRelation<'tcx> for Equate<'combine, 'infcx, 'tcx> {
a: &'tcx ty::Const<'tcx>,
b: &'tcx ty::Const<'tcx>,
) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
self.fields.infcx.super_combine_consts(self, a, b)
match (a.val, b.val) {
(ConstKind::Unevaluated(..), _) => {
self.fields.add_const_equate_obligation(self.a_is_expected, a, b);
Ok(b)
}
(_, ConstKind::Unevaluated(..)) => {
self.fields.add_const_equate_obligation(self.a_is_expected, a, b);
Ok(a)
}
_ => self.fields.infcx.super_combine_consts(self, a, b),
}
}
fn binders<T>(

View File

@ -79,7 +79,17 @@ impl TypeRelation<'tcx> for Glb<'combine, 'infcx, 'tcx> {
a: &'tcx ty::Const<'tcx>,
b: &'tcx ty::Const<'tcx>,
) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
self.fields.infcx.super_combine_consts(self, a, b)
match (a.val, b.val) {
(ty::ConstKind::Unevaluated(..), _) => {
self.fields.add_const_equate_obligation(self.a_is_expected, a, b);
Ok(b)
}
(_, ty::ConstKind::Unevaluated(..)) => {
self.fields.add_const_equate_obligation(self.a_is_expected, a, b);
Ok(a)
}
_ => self.fields.infcx.super_combine_consts(self, a, b),
}
}
fn binders<T>(

View File

@ -79,7 +79,17 @@ impl TypeRelation<'tcx> for Lub<'combine, 'infcx, 'tcx> {
a: &'tcx ty::Const<'tcx>,
b: &'tcx ty::Const<'tcx>,
) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
self.fields.infcx.super_combine_consts(self, a, b)
match (a.val, b.val) {
(ty::ConstKind::Unevaluated(..), _) => {
self.fields.add_const_equate_obligation(self.a_is_expected, a, b);
Ok(b)
}
(_, ty::ConstKind::Unevaluated(..)) => {
self.fields.add_const_equate_obligation(self.a_is_expected, a, b);
Ok(a)
}
_ => self.fields.infcx.super_combine_consts(self, a, b),
}
}
fn binders<T>(

View File

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

View File

@ -77,6 +77,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;
@ -592,8 +594,16 @@ where
b = self.infcx.shallow_resolve(b);
}
match b.val {
ty::ConstKind::Infer(InferConst::Var(_)) if D::forbid_inference_vars() => {
match (a.val, b.val) {
(ty::ConstKind::Unevaluated(..), _) => {
self.delegate.const_equate(a, b);
Ok(b)
}
(_, ty::ConstKind::Unevaluated(..)) => {
self.delegate.const_equate(a, b);
Ok(a)
}
(_, ty::ConstKind::Infer(InferConst::Var(_))) if D::forbid_inference_vars() => {
// Forbid inference variables in the RHS.
bug!("unexpected inference var {:?}", b)
}
@ -976,6 +986,7 @@ where
}
}
}
ty::ConstKind::Unevaluated(..) => Ok(a),
_ => relate::super_relate_consts(self, a, a),
}
}

View File

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

View File

@ -155,7 +155,17 @@ impl TypeRelation<'tcx> for Sub<'combine, 'infcx, 'tcx> {
a: &'tcx ty::Const<'tcx>,
b: &'tcx ty::Const<'tcx>,
) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
self.fields.infcx.super_combine_consts(self, a, b)
match (a.val, b.val) {
(ty::ConstKind::Unevaluated(..), _) => {
self.fields.add_const_equate_obligation(self.a_is_expected, a, b);
Ok(b)
}
(_, ty::ConstKind::Unevaluated(..)) => {
self.fields.add_const_equate_obligation(self.a_is_expected, a, b);
Ok(a)
}
_ => self.fields.infcx.super_combine_consts(self, a, b),
}
}
fn binders<T>(

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -432,16 +432,17 @@ pub fn super_relate_tys<R: TypeRelation<'tcx>>(
match relation.relate(&sz_a, &sz_b) {
Ok(sz) => Ok(tcx.mk_ty(ty::Array(t, sz))),
Err(err) => {
// Check whether the lengths are both concrete/known values,
// but are unequal, for better diagnostics.
let sz_a = sz_a.try_eval_usize(tcx, relation.param_env());
let sz_b = sz_b.try_eval_usize(tcx, relation.param_env());
match (sz_a, sz_b) {
(Some(sz_a_val), Some(sz_b_val)) => Err(TypeError::FixedArraySize(
expected_found(relation, &sz_a_val, &sz_b_val),
)),
_ => Err(err),
}
// // Check whether the lengths are both concrete/known values,
// // but are unequal, for better diagnostics.
// let sz_a = sz_a.try_eval_usize(tcx, relation.param_env());
// let sz_b = sz_b.try_eval_usize(tcx, relation.param_env());
// match (sz_a, sz_b) {
// (Some(sz_a_val), Some(sz_b_val)) => Err(TypeError::FixedArraySize(
// expected_found(relation, &sz_a_val, &sz_b_val),
// )),
// _ => Err(err),
// }
Err(err)
}
}
}
@ -604,14 +605,14 @@ pub fn super_relate_consts<R: TypeRelation<'tcx>>(
}
// FIXME(const_generics): this is wrong, as it is a projection
(
ty::ConstKind::Unevaluated(a_def_id, a_substs, a_promoted),
ty::ConstKind::Unevaluated(b_def_id, b_substs, b_promoted),
) if a_def_id == b_def_id && a_promoted == b_promoted => {
let substs =
relation.relate_with_variance(ty::Variance::Invariant, &a_substs, &b_substs)?;
Ok(ty::ConstKind::Unevaluated(a_def_id, &substs, a_promoted))
}
// (
// ty::ConstKind::Unevaluated(a_def_id, a_substs, a_promoted),
// ty::ConstKind::Unevaluated(b_def_id, b_substs, b_promoted),
// ) if a_def_id == b_def_id && a_promoted == b_promoted => {
// let substs =
// relation.relate_with_variance(ty::Variance::Invariant, &a_substs, &b_substs)?;
// Ok(ty::ConstKind::Unevaluated(a_def_id, &substs, a_promoted))
// }
_ => Err(TypeError::ConstMismatch(expected_found(relation, &a, &b))),
};
new_const_val.map(|val| tcx.mk_const(ty::Const { val, ty: a.ty }))

View File

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

View File

@ -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,8 @@ impl TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, 'tcx> {
}
}
fn const_equate(&mut self, _a: &'tcx Const<'tcx>, _b: &'tcx Const<'tcx>) {}
fn normalization() -> NormalizationStrategy {
NormalizationStrategy::Eager
}

View File

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

View File

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

View File

@ -615,6 +615,18 @@ 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 +1104,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();
}
}
}

View File

@ -3,8 +3,9 @@ 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_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 +521,65 @@ 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(ProcessResult::Unchanged)
}
Err(err) => {
Err(ProcessResult::Error(CodeSelectionError(ConstEvalFailure(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,
))
}
}
}
// FIXME(skinny121) How to report both errors if both produces errors?
(Err(result @ ProcessResult::Error(_)), _)
| (_, Err(result @ ProcessResult::Error(_))) => result,
(Err(ProcessResult::Unchanged), _) | (_, Err(ProcessResult::Unchanged)) => {
ProcessResult::Unchanged
}
_ => {
unreachable!("evaluate shouldn't itself return ProcessResult::Changed(..)")
}
}
}
}
}

View File

@ -16,8 +16,8 @@ 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, Predicate, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitor, WithConstness};
use rustc_session::lint::builtin::WHERE_CLAUSES_OBJECT_SAFETY;
use rustc_span::symbol::Symbol;
use rustc_span::Span;
@ -281,7 +281,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 +314,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 +726,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 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<'_>) {

View File

@ -386,11 +386,6 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> {
_ => ty,
}
}
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)
}
}
/// The guts of `normalize`: normalize a specific projection like `<T

View File

@ -201,9 +201,4 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> {
_ => ty,
}
}
fn fold_const(&mut self, constant: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
let constant = constant.super_fold_with(self);
constant.eval(self.infcx.tcx, self.param_env)
}
}

View File

@ -43,6 +43,7 @@ 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 +504,43 @@ 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 {
match self.infcx.const_eval_resolve(
obligation.param_env,
def_id,
substs,
promoted,
Some(obligation.cause.span),
) {
Ok(val) => Ok(ty::Const::from_value(self.tcx(), val, c.ty)),
Err(ErrorHandled::TooGeneric) => Err(EvaluatedToAmbig),
Err(_) => Err(EvaluatedToErr),
}
} 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(EvaluatedToErr), _) | (_, Err(EvaluatedToErr)) => Ok(EvaluatedToErr),
_ => Ok(EvaluatedToAmbig),
}
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1156,7 +1156,8 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
let node = tcx.hir().get(hir_id);
let parent_def_id = match node {
Node::ImplItem(_)
Node::AnonConst(_)
| Node::ImplItem(_)
| Node::TraitItem(_)
| Node::Variant(_)
| Node::Ctor(..)
@ -1164,34 +1165,7 @@ 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.
Node::AnonConst(_) => {
let parent_id = tcx.hir().get_parent_item(hir_id);
let parent_def_id = tcx.hir().local_def_id(parent_id);
// 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 {
Some(parent_def_id.to_def_id())
} else {
let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id));
match parent_node {
// HACK(eddyb) this provides the correct generics for repeat
// expressions' count (i.e. `N` in `[x; N]`), and explicit
// `enum` discriminants (i.e. `D` in `enum Foo { Bar = D }`),
// as they shouldn't be able to cause query cycle errors.
Node::Expr(&Expr { kind: ExprKind::Repeat(_, ref constant), .. })
| Node::Variant(Variant { disr_expr: Some(ref constant), .. })
if constant.hir_id == hir_id =>
{
Some(parent_def_id.to_def_id())
}
_ => None,
}
}
}
Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure(..), .. }) => {
Some(tcx.closure_base_def_id(def_id))
}

View File

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

View File

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

View File

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