Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lazy normalization of constants #67890

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/librustc_feature/active.rs
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,9 @@ declare_features! (

/// Allow negative trait implementations.
(active, negative_impls, "1.44.0", Some(68318), None),

/// Lazily evaluate constants. Which allows constants to depend on type parameters.
(active, lazy_normalization_consts, "1.44.0", Some(60471), None),

// -------------------------------------------------------------------------
// feature-group-end: actual feature gates
Expand All @@ -575,4 +578,5 @@ pub const INCOMPLETE_FEATURES: &[Symbol] = &[
sym::raw_dylib,
sym::const_trait_impl,
sym::const_trait_bound_opt_out,
sym::lazy_normalization_consts,
];
6 changes: 5 additions & 1 deletion src/librustc_infer/infer/canonical/query_response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down Expand Up @@ -675,6 +675,10 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for QueryTypeRelatingDelegate<'_, 'tcx> {
bug!("should never be invoked with eager normalization")
}

fn const_equate(&mut self, _a: &'tcx Const<'tcx>, _b: &'tcx Const<'tcx>) {
unimplemented!()
}

fn normalization() -> NormalizationStrategy {
NormalizationStrategy::Eager
}
Expand Down
39 changes: 37 additions & 2 deletions src/librustc_infer/infer/combine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -164,7 +164,18 @@ 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.features().lazy_normalization_consts =>
{
relation.const_equate_obligation(a, b);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Until we get the full universe transition happening here, I think we want to check that there are no escaping late-bound regions in these types...oh, hmm, I guess they would be substituted for placeholders already...urgh.

return Ok(b);
}
(_, ty::ConstKind::Unevaluated(..))
if self.tcx.features().lazy_normalization_consts =>
{
relation.const_equate_obligation(a, b);
return Ok(a);
}
_ => {}
}

Expand Down Expand Up @@ -375,6 +386,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> {
Expand Down Expand Up @@ -635,11 +660,21 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
}
}
}
ty::ConstKind::Unevaluated(..) if self.tcx().features().lazy_normalization_consts => {
Ok(c)
}
_ => relate::super_relate_consts(self, c, c),
}
}
}

pub trait ConstEquateRelation<'tcx>: TypeRelation<'tcx> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not obvious to me at this moment why we have a distinct trait for this

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are someTypeRelations which don't need fn const_equate_obligation atm, so I ended up adding a bunch of unimplementeds while moving fn const_equate_obligation to TypeRelation.

I slightly prefer keeping the ConstEquateRelation trait for now. Depending on your preference I would drop the last commit of #71973, which merges these two traits. See deaa50e

/// 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
Expand Down
8 changes: 7 additions & 1 deletion src/librustc_infer/infer/equate.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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);
}
}
7 changes: 7 additions & 0 deletions src/librustc_infer/infer/glb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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);
}
}
7 changes: 7 additions & 0 deletions src/librustc_infer/infer/lub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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
Expand Down
20 changes: 20 additions & 0 deletions src/librustc_infer/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1452,6 +1452,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,
Expand Down Expand Up @@ -1738,6 +1749,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(),
Expand Down
19 changes: 17 additions & 2 deletions src/librustc_infer/infer/nll_relate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 crate::traits::DomainGoal;
Expand Down Expand Up @@ -82,6 +83,8 @@ pub trait TypeRelatingDelegate<'tcx> {
/// be related. Used for lazy normalization.
fn push_domain_goal(&mut self, domain_goal: DomainGoal<'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;

Expand Down Expand Up @@ -603,8 +606,8 @@ 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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: seems like a useful diff?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not quite sure what you mean here 🤔 This does not change the actual behavior afaict which is why I removed this change in #71973.

(_, ty::ConstKind::Infer(InferConst::Var(_))) if D::forbid_inference_vars() => {
// Forbid inference variables in the RHS.
bug!("unexpected inference var {:?}", b)
}
Expand Down Expand Up @@ -726,6 +729,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
Expand Down Expand Up @@ -985,6 +997,9 @@ where
}
}
}
ty::ConstKind::Unevaluated(..) if self.tcx().features().lazy_normalization_consts => {
Ok(a)
}
_ => relate::super_relate_consts(self, a, a),
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/librustc_infer/infer/outlives/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)),
Expand Down
7 changes: 7 additions & 0 deletions src/librustc_infer/infer/sub.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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);
}
}
3 changes: 2 additions & 1 deletion src/librustc_infer/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand Down Expand Up @@ -80,6 +80,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,
}

Expand Down
3 changes: 3 additions & 0 deletions src/librustc_infer/traits/structural_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
}
}
Expand Down
6 changes: 6 additions & 0 deletions src/librustc_infer/traits/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,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),
}
}

Expand Down Expand Up @@ -164,6 +166,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`.
}
Expand Down
3 changes: 2 additions & 1 deletion src/librustc_lint/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1220,7 +1220,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| {
Expand Down
12 changes: 10 additions & 2 deletions src/librustc_middle/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1203,6 +1203,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
Expand Down Expand Up @@ -1321,6 +1324,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))
}
}
}
}
Expand Down Expand Up @@ -1498,7 +1504,8 @@ impl<'tcx> Predicate<'tcx> {
| Predicate::ObjectSafe(..)
| Predicate::ClosureKind(..)
| Predicate::TypeOutlives(..)
| Predicate::ConstEvaluatable(..) => None,
| Predicate::ConstEvaluatable(..)
| Predicate::ConstEquate(..) => None,
}
}

Expand All @@ -1512,7 +1519,8 @@ impl<'tcx> Predicate<'tcx> {
| Predicate::WellFormed(..)
| Predicate::ObjectSafe(..)
| Predicate::ClosureKind(..)
| Predicate::ConstEvaluatable(..) => None,
| Predicate::ConstEvaluatable(..)
| Predicate::ConstEquate(..) => None,
}
}
}
Expand Down
6 changes: 5 additions & 1 deletion src/librustc_middle/ty/outlives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ fn compute_components(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, out: &mut SmallVec<[Compo
// in the `subtys` iterator (e.g., when encountering a
// projection).
match ty.kind {
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);
Expand Down Expand Up @@ -139,7 +144,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
Expand Down
Loading