Skip to content

Commit 0d17f95

Browse files
committed
short-circuit work when instantiating query responses
Also, perform substitution in smaller parts.
1 parent 03c5428 commit 0d17f95

File tree

1 file changed

+84
-24
lines changed

1 file changed

+84
-24
lines changed

src/librustc/infer/canonical.rs

Lines changed: 84 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
use infer::{InferCtxt, InferOk, InferResult, RegionVariableOrigin, TypeVariableOrigin};
3030
use rustc_data_structures::indexed_vec::Idx;
3131
use std::fmt::Debug;
32+
use std::ops::Index;
3233
use syntax::codemap::Span;
3334
use traits::{Obligation, ObligationCause, PredicateObligation};
3435
use ty::{self, CanonicalVar, Lift, Region, Slice, Ty, TyCtxt, TypeFlags};
@@ -395,28 +396,27 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
395396
.collect(),
396397
};
397398

398-
// Apply the result substitution to the query.
399-
let QueryResult {
400-
var_values: query_values,
401-
region_constraints: query_region_constraints,
402-
certainty: _,
403-
value: user_result,
404-
} = query_result.substitute(self.tcx, result_subst);
405-
406399
// Unify the original values for the canonical variables in
407400
// the input with the value found in the query
408401
// post-substitution. Often, but not always, this is a no-op,
409402
// because we already found the mapping in the first step.
403+
let substituted_values = |index: CanonicalVar| -> Kind<'tcx> {
404+
query_result.substitute_projected(self.tcx, result_subst, |v| &v.var_values[index])
405+
};
410406
let mut obligations =
411-
self.unify_canonical_vars(cause, param_env, original_values, &query_values)?
407+
self.unify_canonical_vars(cause, param_env, original_values, substituted_values)?
412408
.into_obligations();
413409

414410
obligations.extend(self.query_region_constraints_into_obligations(
415411
cause,
416412
param_env,
417-
query_region_constraints,
413+
&query_result.value.region_constraints,
414+
result_subst,
418415
));
419416

417+
let user_result: R =
418+
query_result.substitute_projected(self.tcx, result_subst, |q_r| &q_r.value);
419+
420420
Ok(InferOk {
421421
value: user_result,
422422
obligations,
@@ -426,25 +426,30 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
426426
/// Converts the region constraints resulting from a query into an
427427
/// iterator of obligations.
428428
fn query_region_constraints_into_obligations<'a>(
429-
&self,
429+
&'a self,
430430
cause: &'a ObligationCause<'tcx>,
431431
param_env: ty::ParamEnv<'tcx>,
432-
query_region_constraints: QueryRegionConstraints<'tcx>,
432+
unsubstituted_region_constraints: &'a QueryRegionConstraints<'tcx>,
433+
result_subst: &'a CanonicalVarValues<'tcx>,
433434
) -> impl Iterator<Item = PredicateObligation<'tcx>> + 'a {
434435
let QueryRegionConstraints {
435436
region_outlives,
436437
ty_outlives,
437-
} = query_region_constraints;
438+
} = unsubstituted_region_constraints;
438439

439-
let region_obligations = region_outlives.into_iter().map(move |(r1, r2)| {
440+
let region_obligations = region_outlives.iter().map(move |(r1, r2)| {
441+
let r1 = substitute_value(self.tcx, result_subst, r1);
442+
let r2 = substitute_value(self.tcx, result_subst, r2);
440443
Obligation::new(
441444
cause.clone(),
442445
param_env,
443446
ty::Predicate::RegionOutlives(ty::Binder(ty::OutlivesPredicate(r1, r2))),
444447
)
445448
});
446449

447-
let ty_obligations = ty_outlives.into_iter().map(move |(t1, r2)| {
450+
let ty_obligations = ty_outlives.iter().map(move |(t1, r2)| {
451+
let t1 = substitute_value(self.tcx, result_subst, t1);
452+
let r2 = substitute_value(self.tcx, result_subst, r2);
448453
Obligation::new(
449454
cause.clone(),
450455
param_env,
@@ -456,17 +461,19 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
456461
}
457462

458463
/// Given two sets of values for the same set of canonical variables, unify them.
459-
pub fn unify_canonical_vars(
464+
/// The second set is produced lazilly by supplying indices from the first set.
465+
fn unify_canonical_vars(
460466
&self,
461467
cause: &ObligationCause<'tcx>,
462468
param_env: ty::ParamEnv<'tcx>,
463469
variables1: &CanonicalVarValues<'tcx>,
464-
variables2: &CanonicalVarValues<'tcx>,
470+
variables2: impl Fn(CanonicalVar) -> Kind<'tcx>,
465471
) -> InferResult<'tcx, ()> {
466-
assert_eq!(variables1.var_values.len(), variables2.var_values.len());
467472
self.commit_if_ok(|_| {
468473
let mut obligations = vec![];
469-
for (value1, value2) in variables1.var_values.iter().zip(&variables2.var_values) {
474+
for (index, value1) in variables1.var_values.iter_enumerated() {
475+
let value2 = variables2(index);
476+
470477
match (value1.unpack(), value2.unpack()) {
471478
(UnpackedKind::Type(v1), UnpackedKind::Type(v2)) => {
472479
obligations
@@ -724,7 +731,9 @@ impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> {
724731
value: out_value,
725732
},
726733
);
727-
let values = CanonicalVarValues { var_values: IndexVec::default() };
734+
let values = CanonicalVarValues {
735+
var_values: IndexVec::default(),
736+
};
728737
return (canon_value, values);
729738
}
730739

@@ -810,13 +819,50 @@ impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> {
810819
impl<'tcx, V> Canonical<'tcx, V> {
811820
/// Instantiate the wrapped value, replacing each canonical value
812821
/// with the value given in `var_values`.
813-
pub fn substitute(&self, tcx: TyCtxt<'_, '_, 'tcx>, var_values: &CanonicalVarValues<'tcx>) -> V
822+
fn substitute(&self, tcx: TyCtxt<'_, '_, 'tcx>, var_values: &CanonicalVarValues<'tcx>) -> V
814823
where
815824
V: TypeFoldable<'tcx>,
825+
{
826+
self.substitute_projected(tcx, var_values, |value| value)
827+
}
828+
829+
/// Invoke `projection_fn` with `self.value` to get a value V that
830+
/// is expressed in terms of the same canonical variables bound in
831+
/// `self`. Apply the substitution `var_values` to this value V,
832+
/// replacing each of the canonical variables.
833+
fn substitute_projected<T>(
834+
&self,
835+
tcx: TyCtxt<'_, '_, 'tcx>,
836+
var_values: &CanonicalVarValues<'tcx>,
837+
projection_fn: impl FnOnce(&V) -> &T,
838+
) -> T
839+
where
840+
T: TypeFoldable<'tcx>,
816841
{
817842
assert_eq!(self.variables.len(), var_values.var_values.len());
818-
self.value
819-
.fold_with(&mut CanonicalVarValuesSubst { tcx, var_values })
843+
let value = projection_fn(&self.value);
844+
substitute_value(tcx, var_values, value)
845+
}
846+
}
847+
848+
/// Substitute the values from `var_values` into `value`. `var_values`
849+
/// must be values for the set of cnaonical variables that appear in
850+
/// `value`.
851+
fn substitute_value<'a, 'tcx, T>(
852+
tcx: TyCtxt<'_, '_, 'tcx>,
853+
var_values: &CanonicalVarValues<'tcx>,
854+
value: &'a T,
855+
) -> T
856+
where
857+
T: TypeFoldable<'tcx>,
858+
{
859+
if var_values.var_values.is_empty() {
860+
debug_assert!(!value.has_type_flags(TypeFlags::HAS_CANONICAL_VARS));
861+
value.clone()
862+
} else if !value.has_type_flags(TypeFlags::HAS_CANONICAL_VARS) {
863+
value.clone()
864+
} else {
865+
value.fold_with(&mut CanonicalVarValuesSubst { tcx, var_values })
820866
}
821867
}
822868

@@ -838,7 +884,13 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for CanonicalVarValuesSubst<'cx, 'g
838884
r => bug!("{:?} is a type but value is {:?}", c, r),
839885
}
840886
}
841-
_ => t.super_fold_with(self),
887+
_ => {
888+
if !t.has_type_flags(TypeFlags::HAS_CANONICAL_VARS) {
889+
t
890+
} else {
891+
t.super_fold_with(self)
892+
}
893+
}
842894
}
843895
}
844896

@@ -926,3 +978,11 @@ BraceStructLiftImpl! {
926978
var_values, region_constraints, certainty, value
927979
} where R: Lift<'tcx>
928980
}
981+
982+
impl<'tcx> Index<CanonicalVar> for CanonicalVarValues<'tcx> {
983+
type Output = Kind<'tcx>;
984+
985+
fn index(&self, value: CanonicalVar) -> &Kind<'tcx> {
986+
&self.var_values[value]
987+
}
988+
}

0 commit comments

Comments
 (0)