use crate::infer::canonical::{
Canonical, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind, Canonicalized,
OriginalQueryValues,
};
use crate::infer::InferCtxt;
use rustc_middle::ty::flags::FlagComputation;
use rustc_middle::ty::fold::{TypeFoldable, TypeFolder};
use rustc_middle::ty::subst::GenericArg;
use rustc_middle::ty::{self, BoundVar, InferConst, List, Ty, TyCtxt, TypeFlags};
use std::sync::atomic::Ordering;
use rustc_data_structures::fx::FxHashMap;
use rustc_index::vec::Idx;
use smallvec::SmallVec;
impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
pub fn canonicalize_query<V>(
&self,
value: V,
query_state: &mut OriginalQueryValues<'tcx>,
) -> Canonicalized<'tcx, V>
where
V: TypeFoldable<'tcx>,
{
self.tcx.sess.perf_stats.queries_canonicalized.fetch_add(1, Ordering::Relaxed);
Canonicalizer::canonicalize(
value,
Some(self),
self.tcx,
&CanonicalizeAllFreeRegions,
query_state,
)
}
pub fn canonicalize_response<V>(&self, value: V) -> Canonicalized<'tcx, V>
where
V: TypeFoldable<'tcx>,
{
let mut query_state = OriginalQueryValues::default();
Canonicalizer::canonicalize(
value,
Some(self),
self.tcx,
&CanonicalizeQueryResponse,
&mut query_state,
)
}
pub fn canonicalize_user_type_annotation<V>(&self, value: V) -> Canonicalized<'tcx, V>
where
V: TypeFoldable<'tcx>,
{
let mut query_state = OriginalQueryValues::default();
Canonicalizer::canonicalize(
value,
Some(self),
self.tcx,
&CanonicalizeUserTypeAnnotation,
&mut query_state,
)
}
pub fn canonicalize_hr_query_hack<V>(
&self,
value: V,
query_state: &mut OriginalQueryValues<'tcx>,
) -> Canonicalized<'tcx, V>
where
V: TypeFoldable<'tcx>,
{
self.tcx.sess.perf_stats.queries_canonicalized.fetch_add(1, Ordering::Relaxed);
Canonicalizer::canonicalize(
value,
Some(self),
self.tcx,
&CanonicalizeFreeRegionsOtherThanStatic,
query_state,
)
}
}
trait CanonicalizeRegionMode {
fn canonicalize_free_region(
&self,
canonicalizer: &mut Canonicalizer<'_, 'tcx>,
r: ty::Region<'tcx>,
) -> ty::Region<'tcx>;
fn any(&self) -> bool;
}
struct CanonicalizeQueryResponse;
impl CanonicalizeRegionMode for CanonicalizeQueryResponse {
fn canonicalize_free_region(
&self,
canonicalizer: &mut Canonicalizer<'_, 'tcx>,
r: ty::Region<'tcx>,
) -> ty::Region<'tcx> {
match r {
ty::ReFree(_)
| ty::ReErased
| ty::ReStatic
| ty::ReEmpty(ty::UniverseIndex::ROOT)
| ty::ReEarlyBound(..) => r,
ty::RePlaceholder(placeholder) => canonicalizer.canonical_var_for_region(
CanonicalVarInfo { kind: CanonicalVarKind::PlaceholderRegion(*placeholder) },
r,
),
ty::ReVar(vid) => {
let universe = canonicalizer.region_var_universe(*vid);
canonicalizer.canonical_var_for_region(
CanonicalVarInfo { kind: CanonicalVarKind::Region(universe) },
r,
)
}
ty::ReEmpty(ui) => {
bug!("canonicalizing 'empty in universe {:?}", ui)
}
_ => {
ty::tls::with(|tcx| {
tcx.sess.delay_span_bug(
rustc_span::DUMMY_SP,
&format!("unexpected region in query response: `{:?}`", r),
);
});
r
}
}
}
fn any(&self) -> bool {
false
}
}
struct CanonicalizeUserTypeAnnotation;
impl CanonicalizeRegionMode for CanonicalizeUserTypeAnnotation {
fn canonicalize_free_region(
&self,
canonicalizer: &mut Canonicalizer<'_, 'tcx>,
r: ty::Region<'tcx>,
) -> ty::Region<'tcx> {
match r {
ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReErased | ty::ReStatic => r,
ty::ReVar(_) => canonicalizer.canonical_var_for_region_in_root_universe(r),
_ => {
bug!("unexpected region in query response: `{:?}`", r)
}
}
}
fn any(&self) -> bool {
false
}
}
struct CanonicalizeAllFreeRegions;
impl CanonicalizeRegionMode for CanonicalizeAllFreeRegions {
fn canonicalize_free_region(
&self,
canonicalizer: &mut Canonicalizer<'_, 'tcx>,
r: ty::Region<'tcx>,
) -> ty::Region<'tcx> {
canonicalizer.canonical_var_for_region_in_root_universe(r)
}
fn any(&self) -> bool {
true
}
}
struct CanonicalizeFreeRegionsOtherThanStatic;
impl CanonicalizeRegionMode for CanonicalizeFreeRegionsOtherThanStatic {
fn canonicalize_free_region(
&self,
canonicalizer: &mut Canonicalizer<'_, 'tcx>,
r: ty::Region<'tcx>,
) -> ty::Region<'tcx> {
if let ty::ReStatic = r {
r
} else {
canonicalizer.canonical_var_for_region_in_root_universe(r)
}
}
fn any(&self) -> bool {
true
}
}
struct Canonicalizer<'cx, 'tcx> {
infcx: Option<&'cx InferCtxt<'cx, 'tcx>>,
tcx: TyCtxt<'tcx>,
variables: SmallVec<[CanonicalVarInfo<'tcx>; 8]>,
query_state: &'cx mut OriginalQueryValues<'tcx>,
indices: FxHashMap<GenericArg<'tcx>, BoundVar>,
canonicalize_region_mode: &'cx dyn CanonicalizeRegionMode,
needs_canonical_flags: TypeFlags,
binder_index: ty::DebruijnIndex,
}
impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> {
fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
self.tcx
}
fn fold_binder<T>(&mut self, t: ty::Binder<T>) -> ty::Binder<T>
where
T: TypeFoldable<'tcx>,
{
self.binder_index.shift_in(1);
let t = t.super_fold_with(self);
self.binder_index.shift_out(1);
t
}
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
match *r {
ty::ReLateBound(index, ..) => {
if index >= self.binder_index {
bug!("escaping late-bound region during canonicalization");
} else {
r
}
}
ty::ReVar(vid) => {
let resolved_vid = self
.infcx
.unwrap()
.inner
.borrow_mut()
.unwrap_region_constraints()
.opportunistic_resolve_var(vid);
debug!(
"canonical: region var found with vid {:?}, \
opportunistically resolved to {:?}",
vid, r
);
let r = self.tcx.reuse_or_mk_region(r, ty::ReVar(resolved_vid));
self.canonicalize_region_mode.canonicalize_free_region(self, r)
}
ty::ReStatic
| ty::ReEarlyBound(..)
| ty::ReFree(_)
| ty::ReEmpty(_)
| ty::RePlaceholder(..)
| ty::ReErased => self.canonicalize_region_mode.canonicalize_free_region(self, r),
}
}
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
match *t.kind() {
ty::Infer(ty::TyVar(vid)) => {
debug!("canonical: type var found with vid {:?}", vid);
match self.infcx.unwrap().probe_ty_var(vid) {
Ok(t) => {
debug!("(resolved to {:?})", t);
self.fold_ty(t)
}
Err(mut ui) => {
if !self.infcx.unwrap().tcx.sess.opts.debugging_opts.chalk {
ui = ty::UniverseIndex::ROOT;
}
self.canonicalize_ty_var(
CanonicalVarInfo {
kind: CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)),
},
t,
)
}
}
}
ty::Infer(ty::IntVar(_)) => self.canonicalize_ty_var(
CanonicalVarInfo { kind: CanonicalVarKind::Ty(CanonicalTyVarKind::Int) },
t,
),
ty::Infer(ty::FloatVar(_)) => self.canonicalize_ty_var(
CanonicalVarInfo { kind: CanonicalVarKind::Ty(CanonicalTyVarKind::Float) },
t,
),
ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
bug!("encountered a fresh type during canonicalization")
}
ty::Placeholder(placeholder) => self.canonicalize_ty_var(
CanonicalVarInfo { kind: CanonicalVarKind::PlaceholderTy(placeholder) },
t,
),
ty::Bound(debruijn, _) => {
if debruijn >= self.binder_index {
bug!("escaping bound type during canonicalization")
} else {
t
}
}
ty::Closure(..)
| ty::Generator(..)
| ty::GeneratorWitness(..)
| ty::Bool
| ty::Char
| ty::Int(..)
| ty::Uint(..)
| ty::Float(..)
| ty::Adt(..)
| ty::Str
| ty::Error(_)
| ty::Array(..)
| ty::Slice(..)
| ty::RawPtr(..)
| ty::Ref(..)
| ty::FnDef(..)
| ty::FnPtr(_)
| ty::Dynamic(..)
| ty::Never
| ty::Tuple(..)
| ty::Projection(..)
| ty::Foreign(..)
| ty::Param(..)
| ty::Opaque(..) => {
if t.flags().intersects(self.needs_canonical_flags) {
t.super_fold_with(self)
} else {
t
}
}
}
}
fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
match ct.val {
ty::ConstKind::Infer(InferConst::Var(vid)) => {
debug!("canonical: const var found with vid {:?}", vid);
match self.infcx.unwrap().probe_const_var(vid) {
Ok(c) => {
debug!("(resolved to {:?})", c);
return self.fold_const(c);
}
Err(mut ui) => {
if !self.infcx.unwrap().tcx.sess.opts.debugging_opts.chalk {
ui = ty::UniverseIndex::ROOT;
}
return self.canonicalize_const_var(
CanonicalVarInfo { kind: CanonicalVarKind::Const(ui) },
ct,
);
}
}
}
ty::ConstKind::Infer(InferConst::Fresh(_)) => {
bug!("encountered a fresh const during canonicalization")
}
ty::ConstKind::Bound(debruijn, _) => {
if debruijn >= self.binder_index {
bug!("escaping bound type during canonicalization")
} else {
return ct;
}
}
ty::ConstKind::Placeholder(placeholder) => {
return self.canonicalize_const_var(
CanonicalVarInfo { kind: CanonicalVarKind::PlaceholderConst(placeholder) },
ct,
);
}
_ => {}
}
let flags = FlagComputation::for_const(ct);
if flags.intersects(self.needs_canonical_flags) { ct.super_fold_with(self) } else { ct }
}
}
impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
fn canonicalize<V>(
value: V,
infcx: Option<&InferCtxt<'_, 'tcx>>,
tcx: TyCtxt<'tcx>,
canonicalize_region_mode: &dyn CanonicalizeRegionMode,
query_state: &mut OriginalQueryValues<'tcx>,
) -> Canonicalized<'tcx, V>
where
V: TypeFoldable<'tcx>,
{
let needs_canonical_flags = if canonicalize_region_mode.any() {
TypeFlags::NEEDS_INFER |
TypeFlags::HAS_FREE_REGIONS |
TypeFlags::HAS_TY_PLACEHOLDER |
TypeFlags::HAS_CT_PLACEHOLDER
} else {
TypeFlags::NEEDS_INFER
| TypeFlags::HAS_RE_PLACEHOLDER
| TypeFlags::HAS_TY_PLACEHOLDER
| TypeFlags::HAS_CT_PLACEHOLDER
};
if !value.has_type_flags(needs_canonical_flags) {
let canon_value = Canonical {
max_universe: ty::UniverseIndex::ROOT,
variables: List::empty(),
value: value.clone(),
};
return canon_value;
}
let mut canonicalizer = Canonicalizer {
infcx,
tcx,
canonicalize_region_mode,
needs_canonical_flags,
variables: SmallVec::new(),
query_state,
indices: FxHashMap::default(),
binder_index: ty::INNERMOST,
};
let out_value = value.fold_with(&mut canonicalizer);
debug_assert!(!out_value.needs_infer());
let canonical_variables = tcx.intern_canonical_var_infos(&canonicalizer.variables);
let max_universe = canonical_variables
.iter()
.map(|cvar| cvar.universe())
.max()
.unwrap_or(ty::UniverseIndex::ROOT);
Canonical { max_universe, variables: canonical_variables, value: out_value }
}
fn canonical_var(&mut self, info: CanonicalVarInfo<'tcx>, kind: GenericArg<'tcx>) -> BoundVar {
let Canonicalizer { variables, query_state, indices, .. } = self;
let var_values = &mut query_state.var_values;
if !var_values.spilled() {
if let Some(idx) = var_values.iter().position(|&k| k == kind) {
BoundVar::new(idx)
} else {
variables.push(info);
var_values.push(kind);
assert_eq!(variables.len(), var_values.len());
if var_values.spilled() {
assert!(indices.is_empty());
*indices = var_values
.iter()
.enumerate()
.map(|(i, &kind)| (kind, BoundVar::new(i)))
.collect();
}
BoundVar::new(var_values.len() - 1)
}
} else {
*indices.entry(kind).or_insert_with(|| {
variables.push(info);
var_values.push(kind);
assert_eq!(variables.len(), var_values.len());
BoundVar::new(variables.len() - 1)
})
}
}
fn canonical_var_for_region_in_root_universe(
&mut self,
r: ty::Region<'tcx>,
) -> ty::Region<'tcx> {
self.canonical_var_for_region(
CanonicalVarInfo { kind: CanonicalVarKind::Region(ty::UniverseIndex::ROOT) },
r,
)
}
fn region_var_universe(&self, vid: ty::RegionVid) -> ty::UniverseIndex {
self.infcx.unwrap().inner.borrow_mut().unwrap_region_constraints().var_universe(vid)
}
fn canonical_var_for_region(
&mut self,
info: CanonicalVarInfo<'tcx>,
r: ty::Region<'tcx>,
) -> ty::Region<'tcx> {
let var = self.canonical_var(info, r.into());
let region = ty::ReLateBound(self.binder_index, ty::BoundRegion::BrAnon(var.as_u32()));
self.tcx().mk_region(region)
}
fn canonicalize_ty_var(&mut self, info: CanonicalVarInfo<'tcx>, ty_var: Ty<'tcx>) -> Ty<'tcx> {
let infcx = self.infcx.expect("encountered ty-var without infcx");
let bound_to = infcx.shallow_resolve(ty_var);
if bound_to != ty_var {
self.fold_ty(bound_to)
} else {
let var = self.canonical_var(info, ty_var.into());
self.tcx().mk_ty(ty::Bound(self.binder_index, var.into()))
}
}
fn canonicalize_const_var(
&mut self,
info: CanonicalVarInfo<'tcx>,
const_var: &'tcx ty::Const<'tcx>,
) -> &'tcx ty::Const<'tcx> {
let infcx = self.infcx.expect("encountered const-var without infcx");
let bound_to = infcx.shallow_resolve(const_var);
if bound_to != const_var {
self.fold_const(bound_to)
} else {
let var = self.canonical_var(info, const_var.into());
self.tcx().mk_const(ty::Const {
val: ty::ConstKind::Bound(self.binder_index, var),
ty: self.fold_ty(const_var.ty),
})
}
}
}