rust-analyzer/crates/hir-ty/src/next_solver/util.rs
Chayim Refael Friedman c6ef51e550 Switch to home-made db attaching infrastructure
Instead of using Salsa's, as we can no longer can a `dyn HirDatabase` from the `dyn salsa::Database` Salsa provides.
2025-10-05 09:55:50 +03:00

1064 lines
39 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! Various utilities for the next-trait-solver.
use std::iter;
use std::ops::{self, ControlFlow};
use base_db::Crate;
use hir_def::lang_item::LangItem;
use hir_def::{BlockId, HasModule, ItemContainerId, Lookup};
use intern::sym;
use la_arena::Idx;
use rustc_abi::{Float, HasDataLayout, Integer, IntegerType, Primitive, ReprOptions};
use rustc_type_ir::data_structures::IndexMap;
use rustc_type_ir::inherent::{
AdtDef, Const as _, GenericArg as _, GenericArgs as _, ParamEnv as _, Region as _, SliceLike,
Ty as _,
};
use rustc_type_ir::lang_items::SolverTraitLangItem;
use rustc_type_ir::solve::SizedTraitKind;
use rustc_type_ir::{
BoundVar, Canonical, DebruijnIndex, GenericArgKind, INNERMOST, Interner, PredicatePolarity,
TypeFlags, TypeVisitable, TypeVisitableExt,
};
use rustc_type_ir::{
ConstKind, CoroutineArgs, FloatTy, IntTy, RegionKind, TypeFolder, TypeSuperFoldable,
TypeSuperVisitable, TypeVisitor, UintTy, UniverseIndex, inherent::IntoKind,
};
use rustc_type_ir::{InferCtxtLike, TypeFoldable};
use crate::lower_nextsolver::{LifetimeElisionKind, TyLoweringContext};
use crate::next_solver::infer::InferCtxt;
use crate::next_solver::{
BoundConst, CanonicalVarKind, FxIndexMap, ParamEnv, Placeholder, PlaceholderConst,
PlaceholderRegion, TypingMode,
};
use crate::{
db::HirDatabase,
from_foreign_def_id,
method_resolution::{TraitImpls, TyFingerprint},
};
use super::fold::{BoundVarReplacer, FnMutDelegate};
use super::generics::generics;
use super::{
AliasTerm, AliasTy, Binder, BoundRegion, BoundTy, BoundTyKind, BoundVarKind, BoundVarKinds,
CanonicalVars, Clause, ClauseKind, Clauses, Const, DbInterner, EarlyBinder, GenericArg,
GenericArgs, Predicate, PredicateKind, ProjectionPredicate, Region, SolverContext, SolverDefId,
Term, TraitPredicate, TraitRef, Ty, TyKind,
};
#[derive(Clone, Debug)]
pub struct Discr<'db> {
/// Bit representation of the discriminant (e.g., `-128i8` is `0xFF_u128`).
pub val: u128,
pub ty: Ty<'db>,
}
impl<'db> Discr<'db> {
/// Adds `1` to the value and wraps around if the maximum for the type is reached.
pub fn wrap_incr(self, interner: DbInterner<'db>) -> Self {
self.checked_add(interner, 1).0
}
pub fn checked_add(self, interner: DbInterner<'db>, n: u128) -> (Self, bool) {
let (size, signed) = self.ty.int_size_and_signed(interner);
let (val, oflo) = if signed {
let min = size.signed_int_min();
let max = size.signed_int_max();
let val = size.sign_extend(self.val);
assert!(n < (i128::MAX as u128));
let n = n as i128;
let oflo = val > max - n;
let val = if oflo { min + (n - (max - val) - 1) } else { val + n };
// zero the upper bits
let val = val as u128;
let val = size.truncate(val);
(val, oflo)
} else {
let max = size.unsigned_int_max();
let val = self.val;
let oflo = val > max - n;
let val = if oflo { n - (max - val) - 1 } else { val + n };
(val, oflo)
};
(Self { val, ty: self.ty }, oflo)
}
}
pub trait IntegerTypeExt {
fn to_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db>;
fn initial_discriminant<'db>(&self, interner: DbInterner<'db>) -> Discr<'db>;
fn disr_incr<'db>(
&self,
interner: DbInterner<'db>,
val: Option<Discr<'db>>,
) -> Option<Discr<'db>>;
}
impl IntegerTypeExt for IntegerType {
fn to_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db> {
match self {
IntegerType::Pointer(true) => Ty::new(interner, TyKind::Int(IntTy::Isize)),
IntegerType::Pointer(false) => Ty::new(interner, TyKind::Uint(UintTy::Usize)),
IntegerType::Fixed(i, s) => i.to_ty(interner, *s),
}
}
fn initial_discriminant<'db>(&self, interner: DbInterner<'db>) -> Discr<'db> {
Discr { val: 0, ty: self.to_ty(interner) }
}
fn disr_incr<'db>(
&self,
interner: DbInterner<'db>,
val: Option<Discr<'db>>,
) -> Option<Discr<'db>> {
if let Some(val) = val {
assert_eq!(self.to_ty(interner), val.ty);
let (new, oflo) = val.checked_add(interner, 1);
if oflo { None } else { Some(new) }
} else {
Some(self.initial_discriminant(interner))
}
}
}
pub trait IntegerExt {
fn to_ty<'db>(&self, interner: DbInterner<'db>, signed: bool) -> Ty<'db>;
fn from_int_ty<C: HasDataLayout>(cx: &C, ity: IntTy) -> Integer;
fn from_uint_ty<C: HasDataLayout>(cx: &C, ity: UintTy) -> Integer;
fn repr_discr<'db>(
interner: DbInterner<'db>,
ty: Ty<'db>,
repr: &ReprOptions,
min: i128,
max: i128,
) -> (Integer, bool);
}
impl IntegerExt for Integer {
#[inline]
fn to_ty<'db>(&self, interner: DbInterner<'db>, signed: bool) -> Ty<'db> {
use Integer::*;
match (*self, signed) {
(I8, false) => Ty::new(interner, TyKind::Uint(UintTy::U8)),
(I16, false) => Ty::new(interner, TyKind::Uint(UintTy::U16)),
(I32, false) => Ty::new(interner, TyKind::Uint(UintTy::U32)),
(I64, false) => Ty::new(interner, TyKind::Uint(UintTy::U64)),
(I128, false) => Ty::new(interner, TyKind::Uint(UintTy::U128)),
(I8, true) => Ty::new(interner, TyKind::Int(IntTy::I8)),
(I16, true) => Ty::new(interner, TyKind::Int(IntTy::I16)),
(I32, true) => Ty::new(interner, TyKind::Int(IntTy::I32)),
(I64, true) => Ty::new(interner, TyKind::Int(IntTy::I64)),
(I128, true) => Ty::new(interner, TyKind::Int(IntTy::I128)),
}
}
fn from_int_ty<C: HasDataLayout>(cx: &C, ity: IntTy) -> Integer {
use Integer::*;
match ity {
IntTy::I8 => I8,
IntTy::I16 => I16,
IntTy::I32 => I32,
IntTy::I64 => I64,
IntTy::I128 => I128,
IntTy::Isize => cx.data_layout().ptr_sized_integer(),
}
}
fn from_uint_ty<C: HasDataLayout>(cx: &C, ity: UintTy) -> Integer {
use Integer::*;
match ity {
UintTy::U8 => I8,
UintTy::U16 => I16,
UintTy::U32 => I32,
UintTy::U64 => I64,
UintTy::U128 => I128,
UintTy::Usize => cx.data_layout().ptr_sized_integer(),
}
}
/// Finds the appropriate Integer type and signedness for the given
/// signed discriminant range and `#[repr]` attribute.
/// N.B.: `u128` values above `i128::MAX` will be treated as signed, but
/// that shouldn't affect anything, other than maybe debuginfo.
fn repr_discr<'db>(
interner: DbInterner<'db>,
ty: Ty<'db>,
repr: &ReprOptions,
min: i128,
max: i128,
) -> (Integer, bool) {
// Theoretically, negative values could be larger in unsigned representation
// than the unsigned representation of the signed minimum. However, if there
// are any negative values, the only valid unsigned representation is u128
// which can fit all i128 values, so the result remains unaffected.
let unsigned_fit = Integer::fit_unsigned(std::cmp::max(min as u128, max as u128));
let signed_fit = std::cmp::max(Integer::fit_signed(min), Integer::fit_signed(max));
if let Some(ity) = repr.int {
let discr = Integer::from_attr(&interner, ity);
let fit = if ity.is_signed() { signed_fit } else { unsigned_fit };
if discr < fit {
panic!(
"Integer::repr_discr: `#[repr]` hint too small for \
discriminant range of enum `{ty:?}`"
)
}
return (discr, ity.is_signed());
}
let at_least = if repr.c() {
// This is usually I32, however it can be different on some platforms,
// notably hexagon and arm-none/thumb-none
interner.data_layout().c_enum_min_size
} else {
// repr(Rust) enums try to be as small as possible
Integer::I8
};
// If there are no negative values, we can use the unsigned fit.
if min >= 0 {
(std::cmp::max(unsigned_fit, at_least), false)
} else {
(std::cmp::max(signed_fit, at_least), true)
}
}
}
pub trait FloatExt {
fn to_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db>;
fn from_float_ty(fty: FloatTy) -> Self;
}
impl FloatExt for Float {
#[inline]
fn to_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db> {
use Float::*;
match *self {
F16 => Ty::new(interner, TyKind::Float(FloatTy::F16)),
F32 => Ty::new(interner, TyKind::Float(FloatTy::F32)),
F64 => Ty::new(interner, TyKind::Float(FloatTy::F64)),
F128 => Ty::new(interner, TyKind::Float(FloatTy::F128)),
}
}
fn from_float_ty(fty: FloatTy) -> Self {
use Float::*;
match fty {
FloatTy::F16 => F16,
FloatTy::F32 => F32,
FloatTy::F64 => F64,
FloatTy::F128 => F128,
}
}
}
pub trait PrimitiveExt {
fn to_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db>;
fn to_int_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db>;
}
impl PrimitiveExt for Primitive {
#[inline]
fn to_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db> {
match *self {
Primitive::Int(i, signed) => i.to_ty(interner, signed),
Primitive::Float(f) => f.to_ty(interner),
Primitive::Pointer(_) => Ty::new(
interner,
TyKind::RawPtr(
Ty::new(interner, TyKind::Tuple(Default::default())),
rustc_ast_ir::Mutability::Mut,
),
),
}
}
/// Return an *integer* type matching this primitive.
/// Useful in particular when dealing with enum discriminants.
#[inline]
fn to_int_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db> {
match *self {
Primitive::Int(i, signed) => i.to_ty(interner, signed),
Primitive::Pointer(_) => {
let signed = false;
interner.data_layout().ptr_sized_integer().to_ty(interner, signed)
}
Primitive::Float(_) => panic!("floats do not have an int type"),
}
}
}
impl<'db> HasDataLayout for DbInterner<'db> {
fn data_layout(&self) -> &rustc_abi::TargetDataLayout {
unimplemented!()
}
}
pub trait CoroutineArgsExt<'db> {
fn discr_ty(&self, interner: DbInterner<'db>) -> Ty<'db>;
}
impl<'db> CoroutineArgsExt<'db> for CoroutineArgs<DbInterner<'db>> {
/// The type of the state discriminant used in the coroutine type.
#[inline]
fn discr_ty(&self, interner: DbInterner<'db>) -> Ty<'db> {
Ty::new(interner, TyKind::Uint(UintTy::U32))
}
}
/// Finds the max universe present
pub struct MaxUniverse {
max_universe: UniverseIndex,
}
impl Default for MaxUniverse {
fn default() -> Self {
Self::new()
}
}
impl MaxUniverse {
pub fn new() -> Self {
MaxUniverse { max_universe: UniverseIndex::ROOT }
}
pub fn max_universe(self) -> UniverseIndex {
self.max_universe
}
}
impl<'db> TypeVisitor<DbInterner<'db>> for MaxUniverse {
type Result = ();
fn visit_ty(&mut self, t: Ty<'db>) {
if let TyKind::Placeholder(placeholder) = t.kind() {
self.max_universe = UniverseIndex::from_u32(
self.max_universe.as_u32().max(placeholder.universe.as_u32()),
);
}
t.super_visit_with(self)
}
fn visit_const(&mut self, c: Const<'db>) {
if let ConstKind::Placeholder(placeholder) = c.kind() {
self.max_universe = UniverseIndex::from_u32(
self.max_universe.as_u32().max(placeholder.universe.as_u32()),
);
}
c.super_visit_with(self)
}
fn visit_region(&mut self, r: Region<'db>) {
if let RegionKind::RePlaceholder(placeholder) = r.kind() {
self.max_universe = UniverseIndex::from_u32(
self.max_universe.as_u32().max(placeholder.universe.as_u32()),
);
}
}
}
pub struct BottomUpFolder<'db, F, G, H>
where
F: FnMut(Ty<'db>) -> Ty<'db>,
G: FnMut(Region<'db>) -> Region<'db>,
H: FnMut(Const<'db>) -> Const<'db>,
{
pub interner: DbInterner<'db>,
pub ty_op: F,
pub lt_op: G,
pub ct_op: H,
}
impl<'db, F, G, H> TypeFolder<DbInterner<'db>> for BottomUpFolder<'db, F, G, H>
where
F: FnMut(Ty<'db>) -> Ty<'db>,
G: FnMut(Region<'db>) -> Region<'db>,
H: FnMut(Const<'db>) -> Const<'db>,
{
fn cx(&self) -> DbInterner<'db> {
self.interner
}
fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> {
let t = ty.super_fold_with(self);
(self.ty_op)(t)
}
fn fold_region(&mut self, r: Region<'db>) -> Region<'db> {
// This one is a little different, because `super_fold_with` is not
// implemented on non-recursive `Region`.
(self.lt_op)(r)
}
fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> {
let ct = ct.super_fold_with(self);
(self.ct_op)(ct)
}
}
pub(crate) fn for_trait_impls(
db: &dyn HirDatabase,
krate: Crate,
block: Option<BlockId>,
trait_id: hir_def::TraitId,
self_ty_fp: Option<TyFingerprint>,
mut f: impl FnMut(&TraitImpls) -> ControlFlow<()>,
) -> ControlFlow<()> {
// Note: Since we're using `impls_for_trait` and `impl_provided_for`,
// only impls where the trait can be resolved should ever reach Chalk.
// `impl_datum` relies on that and will panic if the trait can't be resolved.
let in_self_and_deps = db.trait_impls_in_deps(krate);
let trait_module = trait_id.module(db);
let type_module = match self_ty_fp {
Some(TyFingerprint::Adt(adt_id)) => Some(adt_id.module(db)),
Some(TyFingerprint::ForeignType(type_id)) => Some(type_id.module(db)),
Some(TyFingerprint::Dyn(trait_id)) => Some(trait_id.module(db)),
_ => None,
};
let mut def_blocks =
[trait_module.containing_block(), type_module.and_then(|it| it.containing_block())];
let block_impls = iter::successors(block, |&block_id| {
cov_mark::hit!(block_local_impls);
block_id.loc(db).module.containing_block()
})
.inspect(|&block_id| {
// make sure we don't search the same block twice
def_blocks.iter_mut().for_each(|block| {
if *block == Some(block_id) {
*block = None;
}
});
})
.filter_map(|block_id| db.trait_impls_in_block(block_id));
for it in in_self_and_deps.iter().map(ops::Deref::deref) {
f(it)?;
}
for it in block_impls {
f(&it)?;
}
for it in def_blocks.into_iter().flatten().filter_map(|it| db.trait_impls_in_block(it)) {
f(&it)?;
}
ControlFlow::Continue(())
}
// FIXME(next-trait-solver): uplift
pub fn sizedness_constraint_for_ty<'db>(
interner: DbInterner<'db>,
sizedness: SizedTraitKind,
ty: Ty<'db>,
) -> Option<Ty<'db>> {
use rustc_type_ir::TyKind::*;
match ty.kind() {
// these are always sized
Bool | Char | Int(..) | Uint(..) | Float(..) | RawPtr(..) | Ref(..) | FnDef(..)
| FnPtr(..) | Array(..) | Closure(..) | CoroutineClosure(..) | Coroutine(..)
| CoroutineWitness(..) | Never => None,
// these are never sized
Str | Slice(..) | Dynamic(_, _) => match sizedness {
// Never `Sized`
SizedTraitKind::Sized => Some(ty),
// Always `MetaSized`
SizedTraitKind::MetaSized => None,
},
// Maybe `Sized` or `MetaSized`
Param(..) | Alias(..) | Error(_) => Some(ty),
// We cannot instantiate the binder, so just return the *original* type back,
// but only if the inner type has a sized constraint. Thus we skip the binder,
// but don't actually use the result from `sized_constraint_for_ty`.
UnsafeBinder(inner_ty) => {
sizedness_constraint_for_ty(interner, sizedness, inner_ty.skip_binder()).map(|_| ty)
}
// Never `MetaSized` or `Sized`
Foreign(..) => Some(ty),
// Recursive cases
Pat(ty, _) => sizedness_constraint_for_ty(interner, sizedness, ty),
Tuple(tys) => tys
.into_iter()
.next_back()
.and_then(|ty| sizedness_constraint_for_ty(interner, sizedness, ty)),
Adt(adt, args) => {
let tail_ty =
EarlyBinder::bind(adt.all_field_tys(interner).skip_binder().into_iter().last()?)
.instantiate(interner, args);
sizedness_constraint_for_ty(interner, sizedness, tail_ty)
}
Placeholder(..) | Bound(..) | Infer(..) => {
panic!("unexpected type `{ty:?}` in sizedness_constraint_for_ty")
}
}
}
pub fn apply_args_to_binder<'db, T: TypeFoldable<DbInterner<'db>>>(
b: Binder<'db, T>,
args: GenericArgs<'db>,
interner: DbInterner<'db>,
) -> T {
let types = &mut |ty: BoundTy| args.as_slice()[ty.var.index()].expect_ty();
let regions = &mut |region: BoundRegion| args.as_slice()[region.var.index()].expect_region();
let consts = &mut |const_: BoundConst| args.as_slice()[const_.var.index()].expect_const();
let mut instantiate = BoundVarReplacer::new(interner, FnMutDelegate { types, regions, consts });
b.skip_binder().fold_with(&mut instantiate)
}
pub(crate) fn mini_canonicalize<'db, T: TypeFoldable<DbInterner<'db>>>(
mut context: SolverContext<'db>,
val: T,
) -> Canonical<DbInterner<'db>, T> {
let mut canon = MiniCanonicalizer {
context: &mut context,
db: DebruijnIndex::ZERO,
vars: IndexMap::default(),
};
let canon_val = val.fold_with(&mut canon);
let vars = canon.vars;
Canonical {
value: canon_val,
max_universe: UniverseIndex::from_u32(1),
variables: CanonicalVars::new_from_iter(
context.cx(),
vars.iter().enumerate().map(|(idx, (k, v))| match (*k).kind() {
GenericArgKind::Type(ty) => match ty.kind() {
TyKind::Int(..) | TyKind::Uint(..) => rustc_type_ir::CanonicalVarKind::Int,
TyKind::Float(..) => rustc_type_ir::CanonicalVarKind::Float,
_ => rustc_type_ir::CanonicalVarKind::Ty {
ui: UniverseIndex::ZERO,
sub_root: BoundVar::from_usize(idx),
},
},
GenericArgKind::Lifetime(_) => {
rustc_type_ir::CanonicalVarKind::Region(UniverseIndex::ZERO)
}
GenericArgKind::Const(_) => {
rustc_type_ir::CanonicalVarKind::Const(UniverseIndex::ZERO)
}
}),
),
}
}
struct MiniCanonicalizer<'a, 'db> {
context: &'a mut SolverContext<'db>,
db: DebruijnIndex,
vars: IndexMap<GenericArg<'db>, usize>,
}
impl<'db> TypeFolder<DbInterner<'db>> for MiniCanonicalizer<'_, 'db> {
fn cx(&self) -> DbInterner<'db> {
self.context.cx()
}
fn fold_binder<T: TypeFoldable<DbInterner<'db>>>(
&mut self,
t: rustc_type_ir::Binder<DbInterner<'db>, T>,
) -> rustc_type_ir::Binder<DbInterner<'db>, T> {
self.db.shift_in(1);
let res = t.map_bound(|t| t.fold_with(self));
self.db.shift_out(1);
res
}
fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> {
match t.kind() {
rustc_type_ir::TyKind::Bound(db, _) => {
if db >= self.db {
panic!("Unexpected bound var");
}
t
}
rustc_type_ir::TyKind::Infer(infer) => {
let t = match infer {
rustc_type_ir::InferTy::TyVar(vid) => {
self.context.opportunistic_resolve_ty_var(vid)
}
rustc_type_ir::InferTy::IntVar(vid) => {
self.context.opportunistic_resolve_int_var(vid)
}
rustc_type_ir::InferTy::FloatVar(vid) => {
self.context.opportunistic_resolve_float_var(vid)
}
_ => t,
};
let len = self.vars.len();
let var = *self.vars.entry(t.into()).or_insert(len);
Ty::new(
self.cx(),
TyKind::Bound(
self.db,
BoundTy { kind: super::BoundTyKind::Anon, var: BoundVar::from_usize(var) },
),
)
}
_ => t.super_fold_with(self),
}
}
fn fold_region(
&mut self,
r: <DbInterner<'db> as rustc_type_ir::Interner>::Region,
) -> <DbInterner<'db> as rustc_type_ir::Interner>::Region {
match r.kind() {
RegionKind::ReBound(db, _) => {
if db >= self.db {
panic!("Unexpected bound var");
}
r
}
RegionKind::ReVar(vid) => {
let len = self.vars.len();
let var = *self.vars.entry(r.into()).or_insert(len);
Region::new(
self.cx(),
RegionKind::ReBound(
self.db,
BoundRegion {
kind: super::BoundRegionKind::Anon,
var: BoundVar::from_usize(var),
},
),
)
}
_ => r,
}
}
fn fold_const(
&mut self,
c: <DbInterner<'db> as rustc_type_ir::Interner>::Const,
) -> <DbInterner<'db> as rustc_type_ir::Interner>::Const {
match c.kind() {
ConstKind::Bound(db, _) => {
if db >= self.db {
panic!("Unexpected bound var");
}
c
}
ConstKind::Infer(infer) => {
let len = self.vars.len();
let var = *self.vars.entry(c.into()).or_insert(len);
Const::new(
self.cx(),
ConstKind::Bound(self.db, BoundConst { var: BoundVar::from_usize(var) }),
)
}
_ => c.super_fold_with(self),
}
}
}
pub fn explicit_item_bounds<'db>(
interner: DbInterner<'db>,
def_id: SolverDefId,
) -> EarlyBinder<'db, Clauses<'db>> {
let db = interner.db();
match def_id {
SolverDefId::TypeAliasId(type_alias) => {
let trait_ = match type_alias.lookup(db).container {
ItemContainerId::TraitId(t) => t,
_ => panic!("associated type not in trait"),
};
// Lower bounds -- we could/should maybe move this to a separate query in `lower`
let type_alias_data = db.type_alias_signature(type_alias);
let generic_params = generics(db, type_alias.into());
let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db);
let mut ctx = TyLoweringContext::new(
db,
&resolver,
&type_alias_data.store,
type_alias.into(),
LifetimeElisionKind::AnonymousReportError,
);
let item_args = GenericArgs::identity_for_item(interner, def_id);
let interner_ty = Ty::new_projection_from_args(interner, def_id, item_args);
let mut bounds = Vec::new();
for bound in &type_alias_data.bounds {
ctx.lower_type_bound(bound, interner_ty, false).for_each(|pred| {
bounds.push(pred);
});
}
if !ctx.unsized_types.contains(&interner_ty) {
let sized_trait = LangItem::Sized
.resolve_trait(ctx.db, interner.krate.expect("Must have interner.krate"));
let sized_bound = sized_trait.map(|trait_id| {
let trait_ref = TraitRef::new_from_args(
interner,
trait_id.into(),
GenericArgs::new_from_iter(interner, [interner_ty.into()]),
);
Clause(Predicate::new(
interner,
Binder::dummy(rustc_type_ir::PredicateKind::Clause(
rustc_type_ir::ClauseKind::Trait(TraitPredicate {
trait_ref,
polarity: rustc_type_ir::PredicatePolarity::Positive,
}),
)),
))
});
bounds.extend(sized_bound);
bounds.shrink_to_fit();
}
rustc_type_ir::EarlyBinder::bind(Clauses::new_from_iter(interner, bounds))
}
SolverDefId::InternedOpaqueTyId(id) => {
let full_id = db.lookup_intern_impl_trait_id(id);
match full_id {
crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => {
let datas = db
.return_type_impl_traits_ns(func)
.expect("impl trait id without impl traits");
let datas = (*datas).as_ref().skip_binder();
let data = &datas.impl_traits[Idx::from_raw(idx.into_raw())];
EarlyBinder::bind(Clauses::new_from_iter(interner, data.predicates.clone()))
}
crate::ImplTraitId::TypeAliasImplTrait(alias, idx) => {
let datas = db
.type_alias_impl_traits_ns(alias)
.expect("impl trait id without impl traits");
let datas = (*datas).as_ref().skip_binder();
let data = &datas.impl_traits[Idx::from_raw(idx.into_raw())];
EarlyBinder::bind(Clauses::new_from_iter(interner, data.predicates.clone()))
}
crate::ImplTraitId::AsyncBlockTypeImplTrait(..) => {
if let Some((future_trait, future_output)) = LangItem::Future
.resolve_trait(db, interner.krate.expect("Must have interner.krate"))
.and_then(|trait_| {
let alias = trait_.trait_items(db).associated_type_by_name(
&hir_expand::name::Name::new_symbol_root(sym::Output.clone()),
)?;
Some((trait_, alias))
})
{
let args = GenericArgs::identity_for_item(interner, def_id);
let out = args.as_slice()[0];
let mut predicates = vec![];
let item_ty = Ty::new_alias(
interner,
rustc_type_ir::AliasTyKind::Opaque,
AliasTy::new_from_args(interner, def_id, args),
);
let kind = PredicateKind::Clause(ClauseKind::Trait(TraitPredicate {
polarity: rustc_type_ir::PredicatePolarity::Positive,
trait_ref: TraitRef::new_from_args(
interner,
future_trait.into(),
GenericArgs::new_from_iter(interner, [item_ty.into()]),
),
}));
predicates.push(Clause(Predicate::new(
interner,
Binder::bind_with_vars(
kind,
BoundVarKinds::new_from_iter(
interner,
[BoundVarKind::Ty(BoundTyKind::Anon)],
),
),
)));
let sized_trait = LangItem::Sized
.resolve_trait(db, interner.krate.expect("Must have interner.krate"));
if let Some(sized_trait_) = sized_trait {
let kind = PredicateKind::Clause(ClauseKind::Trait(TraitPredicate {
polarity: rustc_type_ir::PredicatePolarity::Positive,
trait_ref: TraitRef::new_from_args(
interner,
sized_trait_.into(),
GenericArgs::new_from_iter(interner, [item_ty.into()]),
),
}));
predicates.push(Clause(Predicate::new(
interner,
Binder::bind_with_vars(
kind,
BoundVarKinds::new_from_iter(
interner,
[BoundVarKind::Ty(BoundTyKind::Anon)],
),
),
)));
}
let kind =
PredicateKind::Clause(ClauseKind::Projection(ProjectionPredicate {
projection_term: AliasTerm::new_from_args(
interner,
future_output.into(),
GenericArgs::new_from_iter(interner, [item_ty.into()]),
),
term: match out.kind() {
GenericArgKind::Lifetime(lt) => panic!(),
GenericArgKind::Type(ty) => Term::Ty(ty),
GenericArgKind::Const(const_) => Term::Const(const_),
},
}));
predicates.push(Clause(Predicate::new(
interner,
Binder::bind_with_vars(
kind,
BoundVarKinds::new_from_iter(
interner,
[BoundVarKind::Ty(BoundTyKind::Anon)],
),
),
)));
EarlyBinder::bind(Clauses::new_from_iter(interner, predicates))
} else {
// If failed to find Symbols value as variable is void: Future::Output, return empty bounds as fallback.
EarlyBinder::bind(Clauses::new_from_iter(interner, []))
}
}
}
}
_ => panic!("Unexpected GeneridDefId"),
}
}
pub struct ContainsTypeErrors;
impl<'db> TypeVisitor<DbInterner<'db>> for ContainsTypeErrors {
type Result = ControlFlow<()>;
fn visit_ty(&mut self, t: Ty<'db>) -> Self::Result {
match t.kind() {
rustc_type_ir::TyKind::Error(_) => ControlFlow::Break(()),
_ => t.super_visit_with(self),
}
}
}
/// The inverse of [`BoundVarReplacer`]: replaces placeholders with the bound vars from which they came.
pub struct PlaceholderReplacer<'a, 'db> {
infcx: &'a InferCtxt<'db>,
mapped_regions: FxIndexMap<PlaceholderRegion, BoundRegion>,
mapped_types: FxIndexMap<Placeholder<BoundTy>, BoundTy>,
mapped_consts: FxIndexMap<PlaceholderConst, BoundConst>,
universe_indices: &'a [Option<UniverseIndex>],
current_index: DebruijnIndex,
}
impl<'a, 'db> PlaceholderReplacer<'a, 'db> {
pub fn replace_placeholders<T: TypeFoldable<DbInterner<'db>>>(
infcx: &'a InferCtxt<'db>,
mapped_regions: FxIndexMap<PlaceholderRegion, BoundRegion>,
mapped_types: FxIndexMap<Placeholder<BoundTy>, BoundTy>,
mapped_consts: FxIndexMap<PlaceholderConst, BoundConst>,
universe_indices: &'a [Option<UniverseIndex>],
value: T,
) -> T {
let mut replacer = PlaceholderReplacer {
infcx,
mapped_regions,
mapped_types,
mapped_consts,
universe_indices,
current_index: INNERMOST,
};
value.fold_with(&mut replacer)
}
}
impl<'db> TypeFolder<DbInterner<'db>> for PlaceholderReplacer<'_, 'db> {
fn cx(&self) -> DbInterner<'db> {
self.infcx.interner
}
fn fold_binder<T: TypeFoldable<DbInterner<'db>>>(
&mut self,
t: Binder<'db, T>,
) -> Binder<'db, T> {
if !t.has_placeholders() && !t.has_infer() {
return t;
}
self.current_index.shift_in(1);
let t = t.super_fold_with(self);
self.current_index.shift_out(1);
t
}
fn fold_region(&mut self, r0: Region<'db>) -> Region<'db> {
let r1 = match r0.kind() {
RegionKind::ReVar(vid) => self
.infcx
.inner
.borrow_mut()
.unwrap_region_constraints()
.opportunistic_resolve_var(self.infcx.interner, vid),
_ => r0,
};
let r2 = match r1.kind() {
RegionKind::RePlaceholder(p) => {
let replace_var = self.mapped_regions.get(&p);
match replace_var {
Some(replace_var) => {
let index = self
.universe_indices
.iter()
.position(|u| matches!(u, Some(pu) if *pu == p.universe))
.unwrap_or_else(|| panic!("Unexpected placeholder universe."));
let db = DebruijnIndex::from_usize(
self.universe_indices.len() - index + self.current_index.as_usize() - 1,
);
Region::new_bound(self.cx(), db, *replace_var)
}
None => r1,
}
}
_ => r1,
};
tracing::debug!(?r0, ?r1, ?r2, "fold_region");
r2
}
fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> {
let ty = self.infcx.shallow_resolve(ty);
match ty.kind() {
TyKind::Placeholder(p) => {
let replace_var = self.mapped_types.get(&p);
match replace_var {
Some(replace_var) => {
let index = self
.universe_indices
.iter()
.position(|u| matches!(u, Some(pu) if *pu == p.universe))
.unwrap_or_else(|| panic!("Unexpected placeholder universe."));
let db = DebruijnIndex::from_usize(
self.universe_indices.len() - index + self.current_index.as_usize() - 1,
);
Ty::new_bound(self.infcx.interner, db, *replace_var)
}
None => {
if ty.has_infer() {
ty.super_fold_with(self)
} else {
ty
}
}
}
}
_ if ty.has_placeholders() || ty.has_infer() => ty.super_fold_with(self),
_ => ty,
}
}
fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> {
let ct = self.infcx.shallow_resolve_const(ct);
if let ConstKind::Placeholder(p) = ct.kind() {
let replace_var = self.mapped_consts.get(&p);
match replace_var {
Some(replace_var) => {
let index = self
.universe_indices
.iter()
.position(|u| matches!(u, Some(pu) if *pu == p.universe))
.unwrap_or_else(|| panic!("Unexpected placeholder universe."));
let db = DebruijnIndex::from_usize(
self.universe_indices.len() - index + self.current_index.as_usize() - 1,
);
Const::new_bound(self.infcx.interner, db, *replace_var)
}
None => {
if ct.has_infer() {
ct.super_fold_with(self)
} else {
ct
}
}
}
} else {
ct.super_fold_with(self)
}
}
}
pub(crate) fn needs_normalization<'db, T: TypeVisitable<DbInterner<'db>>>(
infcx: &InferCtxt<'db>,
value: &T,
) -> bool {
let mut flags = TypeFlags::HAS_ALIAS;
// Opaques are treated as rigid outside of `TypingMode::PostAnalysis`,
// so we can ignore those.
match infcx.typing_mode() {
// FIXME(#132279): We likely want to reveal opaques during post borrowck analysis
TypingMode::Coherence
| TypingMode::Analysis { .. }
| TypingMode::Borrowck { .. }
| TypingMode::PostBorrowckAnalysis { .. } => flags.remove(TypeFlags::HAS_TY_OPAQUE),
TypingMode::PostAnalysis => {}
}
value.has_type_flags(flags)
}
pub fn sizedness_fast_path<'db>(
tcx: DbInterner<'db>,
predicate: Predicate<'db>,
param_env: ParamEnv<'db>,
) -> bool {
// Proving `Sized`/`MetaSized`, very often on "obviously sized" types like
// `&T`, accounts for about 60% percentage of the predicates we have to prove. No need to
// canonicalize and all that for such cases.
if let PredicateKind::Clause(ClauseKind::Trait(trait_pred)) = predicate.kind().skip_binder()
&& trait_pred.polarity == PredicatePolarity::Positive
{
let sizedness = match tcx.as_trait_lang_item(trait_pred.def_id()) {
Some(SolverTraitLangItem::Sized) => SizedTraitKind::Sized,
Some(SolverTraitLangItem::MetaSized) => SizedTraitKind::MetaSized,
_ => return false,
};
// FIXME(sized_hierarchy): this temporarily reverts the `sized_hierarchy` feature
// while a proper fix for `tests/ui/sized-hierarchy/incomplete-inference-issue-143992.rs`
// is pending a proper fix
if matches!(sizedness, SizedTraitKind::MetaSized) {
return true;
}
if trait_pred.self_ty().has_trivial_sizedness(tcx, sizedness) {
tracing::debug!("fast path -- trivial sizedness");
return true;
}
if matches!(trait_pred.self_ty().kind(), TyKind::Param(_) | TyKind::Placeholder(_)) {
for clause in param_env.caller_bounds().iter() {
if let ClauseKind::Trait(clause_pred) = clause.kind().skip_binder()
&& clause_pred.polarity == PredicatePolarity::Positive
&& clause_pred.self_ty() == trait_pred.self_ty()
&& (clause_pred.def_id() == trait_pred.def_id()
|| (sizedness == SizedTraitKind::MetaSized
&& tcx.is_trait_lang_item(
clause_pred.def_id(),
SolverTraitLangItem::Sized,
)))
{
return true;
}
}
}
}
false
}