Skip to content

introduce canonical queries, use for normalization and dropck-outlives #48411

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

Merged
merged 27 commits into from
Mar 13, 2018
Merged
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
f873c1e
require `Lifted` types to outlive `'tcx`
nikomatsakis Feb 14, 2018
23837c1
improve TypeFoldable/Lift macros and make a bunch of stuff use them
nikomatsakis Feb 9, 2018
5ddcd09
add `TypeRelation` and `Lift` impls for `Kind`
nikomatsakis Feb 9, 2018
10ae216
fix typo in comment
nikomatsakis Feb 9, 2018
d0aff85
make regions "traceable" so you can do `infcx.at(..).eq(r1, r2)`
nikomatsakis Feb 9, 2018
0037cca
comment the purpose of `TransNormalize`
nikomatsakis Feb 9, 2018
652b3b7
random reformatting
nikomatsakis Feb 21, 2018
1d377d1
add handy helper for Cell<usize>, used for perf stats
nikomatsakis Feb 20, 2018
6d0f931
refactor `ParamEnv::empty(Reveal)` into two distinct methods
nikomatsakis Feb 10, 2018
64d4ed3
move ParamEnv methods from `ty/util` to `ty/mod`
nikomatsakis Feb 14, 2018
80b4c45
change `ParamEnv::and` to sometimes keep the environment [VIC]
nikomatsakis Feb 20, 2018
993c148
add `canonicalize` method to `InferCtxt` [VIC]
nikomatsakis Feb 9, 2018
8c024fd
in `Foo(X)` dep-nodes, allow X to be a `ty` not a `tt`
nikomatsakis Feb 23, 2018
3a50b41
introduce `infcx.at(..).normalize(..)` operation [VIC]
nikomatsakis Feb 25, 2018
ca87d24
introduce `infcx.at(..).dropck_outlives(..)` operaton [VIC]
nikomatsakis Feb 21, 2018
211d9ad
introduce `tcx.normalize_erasing_regions(..)` operaton [VIC]
nikomatsakis Feb 21, 2018
e4728e4
transition various normalization functions to the new methods
nikomatsakis Mar 3, 2018
0a2ac85
move `drain_fulfillment_cx_or_panic` to be private to traits::trans
nikomatsakis Feb 13, 2018
36e5092
add some debug output
nikomatsakis Feb 26, 2018
1e4e632
add regression tests for various MIR bugs that get fixed
nikomatsakis Feb 27, 2018
03c5428
short-circuit `dropck_outlives` for simple cases
nikomatsakis Mar 7, 2018
0d17f95
short-circuit work when instantiating query responses
nikomatsakis Mar 7, 2018
6288faa
`trans_apply_param_substs` => `subst_and_normalize_erasing_regions`
nikomatsakis Mar 9, 2018
fc04c41
add a debug assertion that only outlives-oblig. result from norm.
nikomatsakis Mar 9, 2018
d326738
replace inline docs with references to rustc-guide
nikomatsakis Mar 11, 2018
29dc902
remove dead code
nikomatsakis Mar 11, 2018
17c4103
add "text" sections for things that seem likely to be a problem
nikomatsakis Mar 11, 2018
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
Prev Previous commit
Next Next commit
short-circuit work when instantiating query responses
Also, perform substitution in smaller parts.
  • Loading branch information
nikomatsakis committed Mar 13, 2018
commit 0d17f95465ae3f0c865153fc902eae55ea94e2a9
108 changes: 84 additions & 24 deletions src/librustc/infer/canonical.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
use infer::{InferCtxt, InferOk, InferResult, RegionVariableOrigin, TypeVariableOrigin};
use rustc_data_structures::indexed_vec::Idx;
use std::fmt::Debug;
use std::ops::Index;
use syntax::codemap::Span;
use traits::{Obligation, ObligationCause, PredicateObligation};
use ty::{self, CanonicalVar, Lift, Region, Slice, Ty, TyCtxt, TypeFlags};
Expand Down Expand Up @@ -395,28 +396,27 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
.collect(),
};

// Apply the result substitution to the query.
let QueryResult {
var_values: query_values,
region_constraints: query_region_constraints,
certainty: _,
value: user_result,
} = query_result.substitute(self.tcx, result_subst);

// Unify the original values for the canonical variables in
// the input with the value found in the query
// post-substitution. Often, but not always, this is a no-op,
// because we already found the mapping in the first step.
let substituted_values = |index: CanonicalVar| -> Kind<'tcx> {
query_result.substitute_projected(self.tcx, result_subst, |v| &v.var_values[index])
};
let mut obligations =
self.unify_canonical_vars(cause, param_env, original_values, &query_values)?
self.unify_canonical_vars(cause, param_env, original_values, substituted_values)?
.into_obligations();

obligations.extend(self.query_region_constraints_into_obligations(
cause,
param_env,
query_region_constraints,
&query_result.value.region_constraints,
result_subst,
));

let user_result: R =
query_result.substitute_projected(self.tcx, result_subst, |q_r| &q_r.value);

Ok(InferOk {
value: user_result,
obligations,
Expand All @@ -426,25 +426,30 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
/// Converts the region constraints resulting from a query into an
/// iterator of obligations.
fn query_region_constraints_into_obligations<'a>(
&self,
&'a self,
cause: &'a ObligationCause<'tcx>,
param_env: ty::ParamEnv<'tcx>,
query_region_constraints: QueryRegionConstraints<'tcx>,
unsubstituted_region_constraints: &'a QueryRegionConstraints<'tcx>,
result_subst: &'a CanonicalVarValues<'tcx>,
) -> impl Iterator<Item = PredicateObligation<'tcx>> + 'a {
let QueryRegionConstraints {
region_outlives,
ty_outlives,
} = query_region_constraints;
} = unsubstituted_region_constraints;

let region_obligations = region_outlives.into_iter().map(move |(r1, r2)| {
let region_obligations = region_outlives.iter().map(move |(r1, r2)| {
let r1 = substitute_value(self.tcx, result_subst, r1);
let r2 = substitute_value(self.tcx, result_subst, r2);
Obligation::new(
cause.clone(),
param_env,
ty::Predicate::RegionOutlives(ty::Binder(ty::OutlivesPredicate(r1, r2))),
)
});

let ty_obligations = ty_outlives.into_iter().map(move |(t1, r2)| {
let ty_obligations = ty_outlives.iter().map(move |(t1, r2)| {
let t1 = substitute_value(self.tcx, result_subst, t1);
let r2 = substitute_value(self.tcx, result_subst, r2);
Obligation::new(
cause.clone(),
param_env,
Expand All @@ -456,17 +461,19 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
}

/// Given two sets of values for the same set of canonical variables, unify them.
pub fn unify_canonical_vars(
/// The second set is produced lazilly by supplying indices from the first set.
fn unify_canonical_vars(
&self,
cause: &ObligationCause<'tcx>,
param_env: ty::ParamEnv<'tcx>,
variables1: &CanonicalVarValues<'tcx>,
variables2: &CanonicalVarValues<'tcx>,
variables2: impl Fn(CanonicalVar) -> Kind<'tcx>,
) -> InferResult<'tcx, ()> {
assert_eq!(variables1.var_values.len(), variables2.var_values.len());
self.commit_if_ok(|_| {
let mut obligations = vec![];
for (value1, value2) in variables1.var_values.iter().zip(&variables2.var_values) {
for (index, value1) in variables1.var_values.iter_enumerated() {
let value2 = variables2(index);

match (value1.unpack(), value2.unpack()) {
(UnpackedKind::Type(v1), UnpackedKind::Type(v2)) => {
obligations
Expand Down Expand Up @@ -724,7 +731,9 @@ impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> {
value: out_value,
},
);
let values = CanonicalVarValues { var_values: IndexVec::default() };
let values = CanonicalVarValues {
var_values: IndexVec::default(),
};
return (canon_value, values);
}

Expand Down Expand Up @@ -810,13 +819,50 @@ impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> {
impl<'tcx, V> Canonical<'tcx, V> {
/// Instantiate the wrapped value, replacing each canonical value
/// with the value given in `var_values`.
pub fn substitute(&self, tcx: TyCtxt<'_, '_, 'tcx>, var_values: &CanonicalVarValues<'tcx>) -> V
fn substitute(&self, tcx: TyCtxt<'_, '_, 'tcx>, var_values: &CanonicalVarValues<'tcx>) -> V
where
V: TypeFoldable<'tcx>,
{
self.substitute_projected(tcx, var_values, |value| value)
}

/// Invoke `projection_fn` with `self.value` to get a value V that
/// is expressed in terms of the same canonical variables bound in
/// `self`. Apply the substitution `var_values` to this value V,
/// replacing each of the canonical variables.
fn substitute_projected<T>(
&self,
tcx: TyCtxt<'_, '_, 'tcx>,
var_values: &CanonicalVarValues<'tcx>,
projection_fn: impl FnOnce(&V) -> &T,
) -> T
where
T: TypeFoldable<'tcx>,
{
assert_eq!(self.variables.len(), var_values.var_values.len());
self.value
.fold_with(&mut CanonicalVarValuesSubst { tcx, var_values })
let value = projection_fn(&self.value);
substitute_value(tcx, var_values, value)
}
}

/// Substitute the values from `var_values` into `value`. `var_values`
/// must be values for the set of cnaonical variables that appear in
/// `value`.
fn substitute_value<'a, 'tcx, T>(
tcx: TyCtxt<'_, '_, 'tcx>,
var_values: &CanonicalVarValues<'tcx>,
value: &'a T,
) -> T
where
T: TypeFoldable<'tcx>,
{
if var_values.var_values.is_empty() {
debug_assert!(!value.has_type_flags(TypeFlags::HAS_CANONICAL_VARS));
value.clone()
} else if !value.has_type_flags(TypeFlags::HAS_CANONICAL_VARS) {
value.clone()
} else {
value.fold_with(&mut CanonicalVarValuesSubst { tcx, var_values })
}
}

Expand All @@ -838,7 +884,13 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for CanonicalVarValuesSubst<'cx, 'g
r => bug!("{:?} is a type but value is {:?}", c, r),
}
}
_ => t.super_fold_with(self),
_ => {
if !t.has_type_flags(TypeFlags::HAS_CANONICAL_VARS) {
t
} else {
t.super_fold_with(self)
}
}
}
}

Expand Down Expand Up @@ -926,3 +978,11 @@ BraceStructLiftImpl! {
var_values, region_constraints, certainty, value
} where R: Lift<'tcx>
}

impl<'tcx> Index<CanonicalVar> for CanonicalVarValues<'tcx> {
type Output = Kind<'tcx>;

fn index(&self, value: CanonicalVar) -> &Kind<'tcx> {
&self.var_values[value]
}
}