Skip to content

Commit

Permalink
Rollup merge of rust-lang#71973 - lcnr:lazy-norm, r=nikomatsakis
Browse files Browse the repository at this point in the history
Lazy normalization of constants (Reprise)

Continuation of rust-lang#67890 by @Skinny121.

Initial implementation of rust-lang#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 rust-lang#71922, fixes rust-lang#71986
  • Loading branch information
Dylan-DPC authored May 18, 2020
2 parents 9a519d4 + 9da8a5b commit 9f59db5
Show file tree
Hide file tree
Showing 56 changed files with 669 additions and 145 deletions.
9 changes: 8 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 @@ -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
}
Expand Down
43 changes: 40 additions & 3 deletions src/librustc_infer/infer/combine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down 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,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);
}
_ => {}
}

Expand Down Expand Up @@ -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> {
Expand Down Expand Up @@ -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
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 @@ -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,
Expand Down Expand Up @@ -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(),
Expand Down
13 changes: 13 additions & 0 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 rustc_data_structures::fx::FxHashMap;
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -976,6 +988,7 @@ where
}
}
}
ty::ConstKind::Unevaluated(..) if self.tcx().lazy_normalization() => 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 @@ -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,
}

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

Expand Down Expand Up @@ -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`.
}
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 @@ -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| {
Expand Down
9 changes: 8 additions & 1 deletion src/librustc_middle/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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());
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 @@ -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
Expand Down Expand Up @@ -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))
}
}
}
}
Expand Down Expand Up @@ -1349,7 +1355,8 @@ impl<'tcx> Predicate<'tcx> {
| Predicate::ObjectSafe(..)
| Predicate::ClosureKind(..)
| Predicate::TypeOutlives(..)
| Predicate::ConstEvaluatable(..) => None,
| Predicate::ConstEvaluatable(..)
| Predicate::ConstEquate(..) => None,
}
}

Expand All @@ -1363,7 +1370,8 @@ impl<'tcx> Predicate<'tcx> {
| Predicate::WellFormed(..)
| Predicate::ObjectSafe(..)
| Predicate::ClosureKind(..)
| Predicate::ConstEvaluatable(..) => None,
| Predicate::ConstEvaluatable(..)
| Predicate::ConstEquate(..) => None,
}
}
}
Expand Down
Loading

0 comments on commit 9f59db5

Please sign in to comment.