pub(crate) mod cast;
pub(crate) mod closure;
mod coerce;
mod expr;
mod mutability;
mod pat;
mod path;
pub(crate) mod unify;
use std::{cell::OnceCell, convert::identity, iter, ops::Index};
use chalk_ir::{
cast::Cast,
fold::TypeFoldable,
interner::HasInterner,
visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor},
DebruijnIndex, Mutability, Safety, Scalar, TyKind, TypeFlags, Variance,
};
use either::Either;
use hir_def::{
body::{Body, HygieneId},
builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
data::{ConstData, StaticData},
hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, PatId},
lang_item::{LangItem, LangItemTarget},
layout::Integer,
path::{ModPath, Path},
resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs},
type_ref::{LifetimeRef, TypeRefId, TypesMap},
AdtId, AssocItemId, DefWithBodyId, FieldId, FunctionId, ImplId, ItemContainerId, Lookup,
TraitId, TupleFieldId, TupleId, TypeAliasId, VariantId,
};
use hir_expand::name::Name;
use indexmap::IndexSet;
use intern::sym;
use la_arena::{ArenaMap, Entry};
use rustc_hash::{FxHashMap, FxHashSet};
use stdx::{always, never};
use triomphe::Arc;
use crate::{
db::HirDatabase,
fold_tys,
generics::Generics,
infer::{coerce::CoerceMany, expr::ExprIsRead, unify::InferenceTable},
lower::ImplTraitLoweringMode,
mir::MirSpan,
to_assoc_type_id,
traits::FnTrait,
utils::{InTypeConstIdMetadata, UnevaluatedConstEvaluatorFolder},
AliasEq, AliasTy, Binders, ClosureId, Const, DomainGoal, GenericArg, Goal, ImplTraitId,
ImplTraitIdx, InEnvironment, Interner, Lifetime, OpaqueTyId, ParamLoweringMode, ProjectionTy,
Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
};
#[allow(unreachable_pub)]
pub use coerce::could_coerce;
#[allow(unreachable_pub)]
pub use unify::{could_unify, could_unify_deeply};
use cast::{CastCheck, CastError};
pub(crate) use closure::{CaptureKind, CapturedItem, CapturedItemWithoutTy};
pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult> {
let _p = tracing::info_span!("infer_query").entered();
let resolver = def.resolver(db.upcast());
let body = db.body(def);
let mut ctx = InferenceContext::new(db, def, &body, resolver);
match def {
DefWithBodyId::FunctionId(f) => {
ctx.collect_fn(f);
}
DefWithBodyId::ConstId(c) => ctx.collect_const(&db.const_data(c)),
DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_data(s)),
DefWithBodyId::VariantId(v) => {
ctx.return_ty = TyBuilder::builtin(
match db.enum_data(v.lookup(db.upcast()).parent).variant_body_type() {
hir_def::layout::IntegerType::Pointer(signed) => match signed {
true => BuiltinType::Int(BuiltinInt::Isize),
false => BuiltinType::Uint(BuiltinUint::Usize),
},
hir_def::layout::IntegerType::Fixed(size, signed) => match signed {
true => BuiltinType::Int(match size {
Integer::I8 => BuiltinInt::I8,
Integer::I16 => BuiltinInt::I16,
Integer::I32 => BuiltinInt::I32,
Integer::I64 => BuiltinInt::I64,
Integer::I128 => BuiltinInt::I128,
}),
false => BuiltinType::Uint(match size {
Integer::I8 => BuiltinUint::U8,
Integer::I16 => BuiltinUint::U16,
Integer::I32 => BuiltinUint::U32,
Integer::I64 => BuiltinUint::U64,
Integer::I128 => BuiltinUint::U128,
}),
},
},
);
}
DefWithBodyId::InTypeConstId(c) => {
ctx.return_ty = c
.lookup(db.upcast())
.expected_ty
.box_any()
.downcast::<InTypeConstIdMetadata>()
.unwrap()
.0;
}
}
ctx.infer_body();
ctx.infer_mut_body();
ctx.infer_closures();
Arc::new(ctx.resolve_all())
}
pub(crate) fn normalize(db: &dyn HirDatabase, trait_env: Arc<TraitEnvironment>, ty: Ty) -> Ty {
if !ty.data(Interner).flags.intersects(TypeFlags::HAS_PROJECTION)
&& !matches!(ty.kind(Interner), TyKind::Array(..))
{
return ty;
}
let mut table = unify::InferenceTable::new(db, trait_env);
let ty_with_vars = table.normalize_associated_types_in(ty);
table.resolve_obligations_as_possible();
table.propagate_diverging_flag();
table.resolve_completely(ty_with_vars)
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
pub enum BindingMode {
#[default]
Move,
Ref(Mutability),
}
impl BindingMode {
fn convert(annotation: BindingAnnotation) -> BindingMode {
match annotation {
BindingAnnotation::Unannotated | BindingAnnotation::Mutable => BindingMode::Move,
BindingAnnotation::Ref => BindingMode::Ref(Mutability::Not),
BindingAnnotation::RefMut => BindingMode::Ref(Mutability::Mut),
}
}
}
#[derive(Debug)]
pub(crate) struct InferOk<T> {
value: T,
goals: Vec<InEnvironment<Goal>>,
}
impl<T> InferOk<T> {
fn map<U>(self, f: impl FnOnce(T) -> U) -> InferOk<U> {
InferOk { value: f(self.value), goals: self.goals }
}
}
#[derive(Debug)]
pub(crate) struct TypeError;
pub(crate) type InferResult<T> = Result<InferOk<T>, TypeError>;
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum InferenceDiagnostic {
NoSuchField {
field: ExprOrPatId,
private: bool,
variant: VariantId,
},
PrivateField {
expr: ExprId,
field: FieldId,
},
PrivateAssocItem {
id: ExprOrPatId,
item: AssocItemId,
},
UnresolvedField {
expr: ExprId,
receiver: Ty,
name: Name,
method_with_same_name_exists: bool,
},
UnresolvedMethodCall {
expr: ExprId,
receiver: Ty,
name: Name,
field_with_same_name: Option<Ty>,
assoc_func_with_same_name: Option<AssocItemId>,
},
UnresolvedAssocItem {
id: ExprOrPatId,
},
UnresolvedIdent {
id: ExprOrPatId,
},
BreakOutsideOfLoop {
expr: ExprId,
is_break: bool,
bad_value_break: bool,
},
MismatchedArgCount {
call_expr: ExprId,
expected: usize,
found: usize,
},
MismatchedTupleStructPatArgCount {
pat: ExprOrPatId,
expected: usize,
found: usize,
},
ExpectedFunction {
call_expr: ExprId,
found: Ty,
},
TypedHole {
expr: ExprId,
expected: Ty,
},
CastToUnsized {
expr: ExprId,
cast_ty: Ty,
},
InvalidCast {
expr: ExprId,
error: CastError,
expr_ty: Ty,
cast_ty: Ty,
},
}
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub struct TypeMismatch {
pub expected: Ty,
pub actual: Ty,
}
#[derive(Clone, PartialEq, Eq, Debug)]
struct InternedStandardTypes {
unknown: Ty,
bool_: Ty,
unit: Ty,
never: Ty,
}
impl Default for InternedStandardTypes {
fn default() -> Self {
InternedStandardTypes {
unknown: TyKind::Error.intern(Interner),
bool_: TyKind::Scalar(Scalar::Bool).intern(Interner),
unit: TyKind::Tuple(0, Substitution::empty(Interner)).intern(Interner),
never: TyKind::Never.intern(Interner),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Adjustment {
pub kind: Adjust,
pub target: Ty,
}
impl Adjustment {
pub fn borrow(m: Mutability, ty: Ty, lt: Lifetime) -> Self {
let ty = TyKind::Ref(m, lt.clone(), ty).intern(Interner);
Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(lt, m)), target: ty }
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Adjust {
NeverToAny,
Deref(Option<OverloadedDeref>),
Borrow(AutoBorrow),
Pointer(PointerCast),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct OverloadedDeref(pub Option<Mutability>);
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum AutoBorrow {
Ref(Lifetime, Mutability),
RawPtr(Mutability),
}
impl AutoBorrow {
fn mutability(&self) -> Mutability {
let (AutoBorrow::Ref(_, m) | AutoBorrow::RawPtr(m)) = self;
*m
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum PointerCast {
ReifyFnPointer,
UnsafeFnPointer,
ClosureFnPointer(Safety),
MutToConstPointer,
#[allow(dead_code)]
ArrayToPointer,
Unsize,
}
#[derive(Clone, PartialEq, Eq, Debug, Default)]
pub struct InferenceResult {
method_resolutions: FxHashMap<ExprId, (FunctionId, Substitution)>,
field_resolutions: FxHashMap<ExprId, Either<FieldId, TupleFieldId>>,
variant_resolutions: FxHashMap<ExprOrPatId, VariantId>,
assoc_resolutions: FxHashMap<ExprOrPatId, (AssocItemId, Substitution)>,
pub tuple_field_access_types: FxHashMap<TupleId, Substitution>,
pub diagnostics: Vec<InferenceDiagnostic>,
pub type_of_expr: ArenaMap<ExprId, Ty>,
pub type_of_pat: ArenaMap<PatId, Ty>,
pub type_of_binding: ArenaMap<BindingId, Ty>,
pub type_of_rpit: ArenaMap<ImplTraitIdx, Ty>,
pub type_of_for_iterator: FxHashMap<ExprId, Ty>,
type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>,
pub(crate) has_errors: bool,
standard_types: InternedStandardTypes,
pub pat_adjustments: FxHashMap<PatId, Vec<Ty>>,
pub binding_modes: ArenaMap<PatId, BindingMode>,
pub expr_adjustments: FxHashMap<ExprId, Vec<Adjustment>>,
pub(crate) closure_info: FxHashMap<ClosureId, (Vec<CapturedItem>, FnTrait)>,
pub mutated_bindings_in_closure: FxHashSet<BindingId>,
pub coercion_casts: FxHashSet<ExprId>,
}
impl InferenceResult {
pub fn method_resolution(&self, expr: ExprId) -> Option<(FunctionId, Substitution)> {
self.method_resolutions.get(&expr).cloned()
}
pub fn field_resolution(&self, expr: ExprId) -> Option<Either<FieldId, TupleFieldId>> {
self.field_resolutions.get(&expr).copied()
}
pub fn variant_resolution_for_expr(&self, id: ExprId) -> Option<VariantId> {
self.variant_resolutions.get(&id.into()).copied()
}
pub fn variant_resolution_for_pat(&self, id: PatId) -> Option<VariantId> {
self.variant_resolutions.get(&id.into()).copied()
}
pub fn variant_resolution_for_expr_or_pat(&self, id: ExprOrPatId) -> Option<VariantId> {
match id {
ExprOrPatId::ExprId(id) => self.variant_resolution_for_expr(id),
ExprOrPatId::PatId(id) => self.variant_resolution_for_pat(id),
}
}
pub fn assoc_resolutions_for_expr(&self, id: ExprId) -> Option<(AssocItemId, Substitution)> {
self.assoc_resolutions.get(&id.into()).cloned()
}
pub fn assoc_resolutions_for_pat(&self, id: PatId) -> Option<(AssocItemId, Substitution)> {
self.assoc_resolutions.get(&id.into()).cloned()
}
pub fn assoc_resolutions_for_expr_or_pat(
&self,
id: ExprOrPatId,
) -> Option<(AssocItemId, Substitution)> {
match id {
ExprOrPatId::ExprId(id) => self.assoc_resolutions_for_expr(id),
ExprOrPatId::PatId(id) => self.assoc_resolutions_for_pat(id),
}
}
pub fn type_mismatch_for_expr(&self, expr: ExprId) -> Option<&TypeMismatch> {
self.type_mismatches.get(&expr.into())
}
pub fn type_mismatch_for_pat(&self, pat: PatId) -> Option<&TypeMismatch> {
self.type_mismatches.get(&pat.into())
}
pub fn type_mismatches(&self) -> impl Iterator<Item = (ExprOrPatId, &TypeMismatch)> {
self.type_mismatches.iter().map(|(expr_or_pat, mismatch)| (*expr_or_pat, mismatch))
}
pub fn expr_type_mismatches(&self) -> impl Iterator<Item = (ExprId, &TypeMismatch)> {
self.type_mismatches.iter().filter_map(|(expr_or_pat, mismatch)| match *expr_or_pat {
ExprOrPatId::ExprId(expr) => Some((expr, mismatch)),
_ => None,
})
}
pub fn closure_info(&self, closure: &ClosureId) -> &(Vec<CapturedItem>, FnTrait) {
self.closure_info.get(closure).unwrap()
}
pub fn type_of_expr_or_pat(&self, id: ExprOrPatId) -> Option<&Ty> {
match id {
ExprOrPatId::ExprId(id) => self.type_of_expr.get(id),
ExprOrPatId::PatId(id) => self.type_of_pat.get(id),
}
}
}
impl Index<ExprId> for InferenceResult {
type Output = Ty;
fn index(&self, expr: ExprId) -> &Ty {
self.type_of_expr.get(expr).unwrap_or(&self.standard_types.unknown)
}
}
impl Index<PatId> for InferenceResult {
type Output = Ty;
fn index(&self, pat: PatId) -> &Ty {
self.type_of_pat.get(pat).unwrap_or(&self.standard_types.unknown)
}
}
impl Index<ExprOrPatId> for InferenceResult {
type Output = Ty;
fn index(&self, id: ExprOrPatId) -> &Ty {
self.type_of_expr_or_pat(id).unwrap_or(&self.standard_types.unknown)
}
}
impl Index<BindingId> for InferenceResult {
type Output = Ty;
fn index(&self, b: BindingId) -> &Ty {
self.type_of_binding.get(b).unwrap_or(&self.standard_types.unknown)
}
}
#[derive(Clone, Debug)]
pub(crate) struct InferenceContext<'a> {
pub(crate) db: &'a dyn HirDatabase,
pub(crate) owner: DefWithBodyId,
pub(crate) body: &'a Body,
pub(crate) resolver: Resolver,
generics: OnceCell<Option<Generics>>,
table: unify::InferenceTable<'a>,
traits_in_scope: FxHashSet<TraitId>,
pub(crate) result: InferenceResult,
tuple_field_accesses_rev:
IndexSet<Substitution, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>,
return_ty: Ty,
return_coercion: Option<CoerceMany>,
resume_yield_tys: Option<(Ty, Ty)>,
diverges: Diverges,
breakables: Vec<BreakableContext>,
inside_assignment: bool,
deferred_cast_checks: Vec<CastCheck>,
current_captures: Vec<CapturedItemWithoutTy>,
current_capture_span_stack: Vec<MirSpan>,
current_closure: Option<ClosureId>,
closure_dependencies: FxHashMap<ClosureId, Vec<ClosureId>>,
deferred_closures: FxHashMap<ClosureId, Vec<(Ty, Ty, Vec<Ty>, ExprId)>>,
}
#[derive(Clone, Debug)]
struct BreakableContext {
may_break: bool,
coerce: Option<CoerceMany>,
label: Option<LabelId>,
kind: BreakableKind,
}
#[derive(Clone, Debug)]
enum BreakableKind {
Block,
Loop,
Border,
}
fn find_breakable(
ctxs: &mut [BreakableContext],
label: Option<LabelId>,
) -> Option<&mut BreakableContext> {
let mut ctxs = ctxs
.iter_mut()
.rev()
.take_while(|it| matches!(it.kind, BreakableKind::Block | BreakableKind::Loop));
match label {
Some(_) => ctxs.find(|ctx| ctx.label == label),
None => ctxs.find(|ctx| matches!(ctx.kind, BreakableKind::Loop)),
}
}
fn find_continuable(
ctxs: &mut [BreakableContext],
label: Option<LabelId>,
) -> Option<&mut BreakableContext> {
match label {
Some(_) => find_breakable(ctxs, label).filter(|it| matches!(it.kind, BreakableKind::Loop)),
None => find_breakable(ctxs, label),
}
}
enum ImplTraitReplacingMode {
ReturnPosition(FxHashSet<Ty>),
TypeAlias,
}
impl<'a> InferenceContext<'a> {
fn new(
db: &'a dyn HirDatabase,
owner: DefWithBodyId,
body: &'a Body,
resolver: Resolver,
) -> Self {
let trait_env = db.trait_environment_for_body(owner);
InferenceContext {
generics: OnceCell::new(),
result: InferenceResult::default(),
table: unify::InferenceTable::new(db, trait_env),
tuple_field_accesses_rev: Default::default(),
return_ty: TyKind::Error.intern(Interner), resume_yield_tys: None,
return_coercion: None,
db,
owner,
body,
traits_in_scope: resolver.traits_in_scope(db.upcast()),
resolver,
diverges: Diverges::Maybe,
breakables: Vec::new(),
deferred_cast_checks: Vec::new(),
current_captures: Vec::new(),
current_capture_span_stack: Vec::new(),
current_closure: None,
deferred_closures: FxHashMap::default(),
closure_dependencies: FxHashMap::default(),
inside_assignment: false,
}
}
pub(crate) fn generics(&self) -> Option<&Generics> {
self.generics
.get_or_init(|| {
self.resolver
.generic_def()
.map(|def| crate::generics::generics(self.db.upcast(), def))
})
.as_ref()
}
pub(crate) fn resolve_all(self) -> InferenceResult {
let InferenceContext {
mut table,
mut result,
mut deferred_cast_checks,
tuple_field_accesses_rev,
..
} = self;
let InferenceResult {
method_resolutions,
field_resolutions: _,
variant_resolutions: _,
assoc_resolutions,
diagnostics,
type_of_expr,
type_of_pat,
type_of_binding,
type_of_rpit,
type_of_for_iterator,
type_mismatches,
has_errors,
standard_types: _,
pat_adjustments,
binding_modes: _,
expr_adjustments,
closure_info: _,
mutated_bindings_in_closure: _,
tuple_field_access_types: _,
coercion_casts,
} = &mut result;
table.fallback_if_possible();
let mut apply_adjustments = |expr, adj| {
expr_adjustments.insert(expr, adj);
};
let mut set_coercion_cast = |expr| {
coercion_casts.insert(expr);
};
for cast in deferred_cast_checks.iter_mut() {
if let Err(diag) =
cast.check(&mut table, &mut apply_adjustments, &mut set_coercion_cast)
{
diagnostics.push(diag);
}
}
table.resolve_obligations_as_possible();
table.propagate_diverging_flag();
for ty in type_of_expr.values_mut() {
*ty = table.resolve_completely(ty.clone());
*has_errors = *has_errors || ty.contains_unknown();
}
for ty in type_of_pat.values_mut() {
*ty = table.resolve_completely(ty.clone());
*has_errors = *has_errors || ty.contains_unknown();
}
for ty in type_of_binding.values_mut() {
*ty = table.resolve_completely(ty.clone());
*has_errors = *has_errors || ty.contains_unknown();
}
for ty in type_of_rpit.values_mut() {
*ty = table.resolve_completely(ty.clone());
*has_errors = *has_errors || ty.contains_unknown();
}
for ty in type_of_for_iterator.values_mut() {
*ty = table.resolve_completely(ty.clone());
*has_errors = *has_errors || ty.contains_unknown();
}
*has_errors |= !type_mismatches.is_empty();
type_mismatches.retain(|_, mismatch| {
mismatch.expected = table.resolve_completely(mismatch.expected.clone());
mismatch.actual = table.resolve_completely(mismatch.actual.clone());
chalk_ir::zip::Zip::zip_with(
&mut UnknownMismatch(self.db),
Variance::Invariant,
&mismatch.expected,
&mismatch.actual,
)
.is_ok()
});
diagnostics.retain_mut(|diagnostic| {
use InferenceDiagnostic::*;
match diagnostic {
ExpectedFunction { found: ty, .. }
| UnresolvedField { receiver: ty, .. }
| UnresolvedMethodCall { receiver: ty, .. } => {
*ty = table.resolve_completely(ty.clone());
if ty.contains_unknown() {
return false;
}
if let UnresolvedMethodCall { field_with_same_name, .. } = diagnostic {
if let Some(ty) = field_with_same_name {
*ty = table.resolve_completely(ty.clone());
if ty.contains_unknown() {
*field_with_same_name = None;
}
}
}
}
TypedHole { expected: ty, .. } => {
*ty = table.resolve_completely(ty.clone());
}
_ => (),
}
true
});
for (_, subst) in method_resolutions.values_mut() {
*subst = table.resolve_completely(subst.clone());
*has_errors =
*has_errors || subst.type_parameters(Interner).any(|ty| ty.contains_unknown());
}
for (_, subst) in assoc_resolutions.values_mut() {
*subst = table.resolve_completely(subst.clone());
*has_errors =
*has_errors || subst.type_parameters(Interner).any(|ty| ty.contains_unknown());
}
for adjustment in expr_adjustments.values_mut().flatten() {
adjustment.target = table.resolve_completely(adjustment.target.clone());
*has_errors = *has_errors || adjustment.target.contains_unknown();
}
for adjustment in pat_adjustments.values_mut().flatten() {
*adjustment = table.resolve_completely(adjustment.clone());
*has_errors = *has_errors || adjustment.contains_unknown();
}
result.tuple_field_access_types = tuple_field_accesses_rev
.into_iter()
.enumerate()
.map(|(idx, subst)| (TupleId(idx as u32), table.resolve_completely(subst)))
.inspect(|(_, subst)| {
*has_errors =
*has_errors || subst.type_parameters(Interner).any(|ty| ty.contains_unknown());
})
.collect();
result
}
fn collect_const(&mut self, data: &ConstData) {
let return_ty = self.make_ty(data.type_ref, &data.types_map);
self.make_tait_coercion_table(iter::once(&return_ty));
self.return_ty = return_ty;
}
fn collect_static(&mut self, data: &StaticData) {
let return_ty = self.make_ty(data.type_ref, &data.types_map);
self.make_tait_coercion_table(iter::once(&return_ty));
self.return_ty = return_ty;
}
fn collect_fn(&mut self, func: FunctionId) {
let data = self.db.function_data(func);
let mut param_tys = self.with_ty_lowering(&data.types_map, |ctx| {
ctx.type_param_mode(ParamLoweringMode::Placeholder)
.impl_trait_mode(ImplTraitLoweringMode::Param);
data.params.iter().map(|&type_ref| ctx.lower_ty(type_ref)).collect::<Vec<_>>()
});
if data.is_varargs() {
let va_list_ty = match self.resolve_va_list() {
Some(va_list) => TyBuilder::adt(self.db, va_list)
.fill_with_defaults(self.db, || self.table.new_type_var())
.build(),
None => self.err_ty(),
};
param_tys.push(va_list_ty);
}
let mut param_tys = param_tys.into_iter().chain(iter::repeat(self.table.new_type_var()));
if let Some(self_param) = self.body.self_param {
if let Some(ty) = param_tys.next() {
let ty = self.insert_type_vars(ty);
let ty = self.normalize_associated_types_in(ty);
self.write_binding_ty(self_param, ty);
}
}
let mut tait_candidates = FxHashSet::default();
for (ty, pat) in param_tys.zip(&*self.body.params) {
let ty = self.insert_type_vars(ty);
let ty = self.normalize_associated_types_in(ty);
self.infer_top_pat(*pat, &ty);
if ty
.data(Interner)
.flags
.intersects(TypeFlags::HAS_TY_OPAQUE.union(TypeFlags::HAS_TY_INFER))
{
tait_candidates.insert(ty);
}
}
let return_ty = data.ret_type;
let return_ty = self.with_ty_lowering(&data.types_map, |ctx| {
ctx.type_param_mode(ParamLoweringMode::Placeholder)
.impl_trait_mode(ImplTraitLoweringMode::Opaque)
.lower_ty(return_ty)
});
let return_ty = self.insert_type_vars(return_ty);
let return_ty = if let Some(rpits) = self.db.return_type_impl_traits(func) {
let fn_placeholders = TyBuilder::placeholder_subst(self.db, func);
let mut mode = ImplTraitReplacingMode::ReturnPosition(FxHashSet::default());
let result =
self.insert_inference_vars_for_impl_trait(return_ty, fn_placeholders, &mut mode);
if let ImplTraitReplacingMode::ReturnPosition(taits) = mode {
tait_candidates.extend(taits);
}
let rpits = rpits.skip_binders();
for (id, _) in rpits.impl_traits.iter() {
if let Entry::Vacant(e) = self.result.type_of_rpit.entry(id) {
never!("Missed RPIT in `insert_inference_vars_for_rpit`");
e.insert(TyKind::Error.intern(Interner));
}
}
result
} else {
return_ty
};
self.return_ty = self.normalize_associated_types_in(return_ty);
self.return_coercion = Some(CoerceMany::new(self.return_ty.clone()));
if self
.return_ty
.data(Interner)
.flags
.intersects(TypeFlags::HAS_TY_OPAQUE.union(TypeFlags::HAS_TY_INFER))
{
tait_candidates.insert(self.return_ty.clone());
}
self.make_tait_coercion_table(tait_candidates.iter());
}
fn insert_inference_vars_for_impl_trait<T>(
&mut self,
t: T,
placeholders: Substitution,
mode: &mut ImplTraitReplacingMode,
) -> T
where
T: crate::HasInterner<Interner = Interner> + crate::TypeFoldable<Interner>,
{
fold_tys(
t,
|ty, _| {
let opaque_ty_id = match ty.kind(Interner) {
TyKind::OpaqueType(opaque_ty_id, _) => *opaque_ty_id,
_ => return ty,
};
let (impl_traits, idx) =
match self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) {
ImplTraitId::ReturnTypeImplTrait(def, idx) => {
if matches!(mode, ImplTraitReplacingMode::TypeAlias) {
if let Some(ty) = self.result.type_of_rpit.get(idx) {
return ty.clone();
}
return ty;
}
(self.db.return_type_impl_traits(def), idx)
}
ImplTraitId::TypeAliasImplTrait(def, idx) => {
if let ImplTraitReplacingMode::ReturnPosition(taits) = mode {
taits.insert(ty.clone());
return ty;
}
(self.db.type_alias_impl_traits(def), idx)
}
_ => unreachable!(),
};
let Some(impl_traits) = impl_traits else {
return ty;
};
let bounds = (*impl_traits)
.map_ref(|its| its.impl_traits[idx].bounds.map_ref(|it| it.iter()));
let var = self.table.new_type_var();
let var_subst = Substitution::from1(Interner, var.clone());
for bound in bounds {
let predicate = bound.map(|it| it.cloned());
let predicate = predicate.substitute(Interner, &placeholders);
let (var_predicate, binders) =
predicate.substitute(Interner, &var_subst).into_value_and_skipped_binders();
always!(binders.is_empty(Interner)); let var_predicate = self.insert_inference_vars_for_impl_trait(
var_predicate,
placeholders.clone(),
mode,
);
self.push_obligation(var_predicate.cast(Interner));
}
self.result.type_of_rpit.insert(idx, var.clone());
var
},
DebruijnIndex::INNERMOST,
)
}
fn make_tait_coercion_table<'b>(&mut self, tait_candidates: impl Iterator<Item = &'b Ty>) {
struct TypeAliasImplTraitCollector<'a, 'b> {
db: &'b dyn HirDatabase,
table: &'b mut InferenceTable<'a>,
assocs: FxHashMap<OpaqueTyId, (ImplId, Ty)>,
non_assocs: FxHashMap<OpaqueTyId, Ty>,
}
impl TypeVisitor<Interner> for TypeAliasImplTraitCollector<'_, '_> {
type BreakTy = ();
fn as_dyn(&mut self) -> &mut dyn TypeVisitor<Interner, BreakTy = Self::BreakTy> {
self
}
fn interner(&self) -> Interner {
Interner
}
fn visit_ty(
&mut self,
ty: &chalk_ir::Ty<Interner>,
outer_binder: DebruijnIndex,
) -> std::ops::ControlFlow<Self::BreakTy> {
let ty = self.table.resolve_ty_shallow(ty);
if let TyKind::OpaqueType(id, _) = ty.kind(Interner) {
if let ImplTraitId::TypeAliasImplTrait(alias_id, _) =
self.db.lookup_intern_impl_trait_id((*id).into())
{
let loc = self.db.lookup_intern_type_alias(alias_id);
match loc.container {
ItemContainerId::ImplId(impl_id) => {
self.assocs.insert(*id, (impl_id, ty.clone()));
}
ItemContainerId::ModuleId(..) | ItemContainerId::ExternBlockId(..) => {
self.non_assocs.insert(*id, ty.clone());
}
_ => {}
}
}
}
ty.super_visit_with(self, outer_binder)
}
}
let mut collector = TypeAliasImplTraitCollector {
db: self.db,
table: &mut self.table,
assocs: FxHashMap::default(),
non_assocs: FxHashMap::default(),
};
for ty in tait_candidates {
ty.visit_with(collector.as_dyn(), DebruijnIndex::INNERMOST);
}
let mut taits = collector.non_assocs;
let impl_id = match self.owner {
DefWithBodyId::FunctionId(it) => {
let loc = self.db.lookup_intern_function(it);
if let ItemContainerId::ImplId(impl_id) = loc.container {
Some(impl_id)
} else {
None
}
}
DefWithBodyId::ConstId(it) => {
let loc = self.db.lookup_intern_const(it);
if let ItemContainerId::ImplId(impl_id) = loc.container {
Some(impl_id)
} else {
None
}
}
_ => None,
};
if let Some(impl_id) = impl_id {
taits.extend(collector.assocs.into_iter().filter_map(|(id, (impl_, ty))| {
if impl_ == impl_id {
Some((id, ty))
} else {
None
}
}));
}
let tait_coercion_table: FxHashMap<_, _> = taits
.into_iter()
.filter_map(|(id, ty)| {
if let ImplTraitId::TypeAliasImplTrait(alias_id, _) =
self.db.lookup_intern_impl_trait_id(id.into())
{
let subst = TyBuilder::placeholder_subst(self.db, alias_id);
let ty = self.insert_inference_vars_for_impl_trait(
ty,
subst,
&mut ImplTraitReplacingMode::TypeAlias,
);
Some((id, ty))
} else {
None
}
})
.collect();
if !tait_coercion_table.is_empty() {
self.table.tait_coercion_table = Some(tait_coercion_table);
}
}
fn infer_body(&mut self) {
match self.return_coercion {
Some(_) => self.infer_return(self.body.body_expr),
None => {
_ = self.infer_expr_coerce(
self.body.body_expr,
&Expectation::has_type(self.return_ty.clone()),
ExprIsRead::Yes,
)
}
}
}
fn write_expr_ty(&mut self, expr: ExprId, ty: Ty) {
self.result.type_of_expr.insert(expr, ty);
}
fn write_expr_adj(&mut self, expr: ExprId, adjustments: Vec<Adjustment>) {
self.result.expr_adjustments.insert(expr, adjustments);
}
fn write_method_resolution(&mut self, expr: ExprId, func: FunctionId, subst: Substitution) {
self.result.method_resolutions.insert(expr, (func, subst));
}
fn write_variant_resolution(&mut self, id: ExprOrPatId, variant: VariantId) {
self.result.variant_resolutions.insert(id, variant);
}
fn write_assoc_resolution(&mut self, id: ExprOrPatId, item: AssocItemId, subs: Substitution) {
self.result.assoc_resolutions.insert(id, (item, subs));
}
fn write_pat_ty(&mut self, pat: PatId, ty: Ty) {
self.result.type_of_pat.insert(pat, ty);
}
fn write_binding_ty(&mut self, id: BindingId, ty: Ty) {
self.result.type_of_binding.insert(id, ty);
}
fn push_diagnostic(&mut self, diagnostic: InferenceDiagnostic) {
self.result.diagnostics.push(diagnostic);
}
fn with_ty_lowering<R>(
&self,
types_map: &TypesMap,
f: impl FnOnce(&mut crate::lower::TyLoweringContext<'_>) -> R,
) -> R {
let mut ctx = crate::lower::TyLoweringContext::new(
self.db,
&self.resolver,
types_map,
self.owner.into(),
);
f(&mut ctx)
}
fn with_body_ty_lowering<R>(
&self,
f: impl FnOnce(&mut crate::lower::TyLoweringContext<'_>) -> R,
) -> R {
self.with_ty_lowering(&self.body.types, f)
}
fn make_ty(&mut self, type_ref: TypeRefId, types_map: &TypesMap) -> Ty {
let ty = self.with_ty_lowering(types_map, |ctx| ctx.lower_ty(type_ref));
let ty = self.insert_type_vars(ty);
self.normalize_associated_types_in(ty)
}
fn make_body_ty(&mut self, type_ref: TypeRefId) -> Ty {
self.make_ty(type_ref, &self.body.types)
}
fn err_ty(&self) -> Ty {
self.result.standard_types.unknown.clone()
}
fn make_lifetime(&mut self, lifetime_ref: &LifetimeRef) -> Lifetime {
let lt = self.with_ty_lowering(TypesMap::EMPTY, |ctx| ctx.lower_lifetime(lifetime_ref));
self.insert_type_vars(lt)
}
fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty {
self.table.insert_type_vars_shallow(ty)
}
fn insert_type_vars<T>(&mut self, ty: T) -> T
where
T: HasInterner<Interner = Interner> + TypeFoldable<Interner>,
{
self.table.insert_type_vars(ty)
}
fn push_obligation(&mut self, o: DomainGoal) {
self.table.register_obligation(o.cast(Interner));
}
fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool {
let ty1 = ty1
.clone()
.try_fold_with(
&mut UnevaluatedConstEvaluatorFolder { db: self.db },
DebruijnIndex::INNERMOST,
)
.unwrap();
let ty2 = ty2
.clone()
.try_fold_with(
&mut UnevaluatedConstEvaluatorFolder { db: self.db },
DebruijnIndex::INNERMOST,
)
.unwrap();
self.table.unify(&ty1, &ty2)
}
fn struct_tail_without_normalization(&mut self, ty: Ty) -> Ty {
self.struct_tail_with_normalize(ty, identity)
}
fn struct_tail_with_normalize(
&mut self,
mut ty: Ty,
mut normalize: impl FnMut(Ty) -> Ty,
) -> Ty {
let recursion_limit = 10;
for iteration in 0.. {
if iteration > recursion_limit {
return self.err_ty();
}
match ty.kind(Interner) {
TyKind::Adt(chalk_ir::AdtId(hir_def::AdtId::StructId(struct_id)), substs) => {
match self.db.field_types((*struct_id).into()).values().next_back().cloned() {
Some(field) => {
ty = field.substitute(Interner, substs);
}
None => break,
}
}
TyKind::Adt(..) => break,
TyKind::Tuple(_, substs) => {
match substs
.as_slice(Interner)
.split_last()
.and_then(|(last_ty, _)| last_ty.ty(Interner))
{
Some(last_ty) => ty = last_ty.clone(),
None => break,
}
}
TyKind::Alias(..) => {
let normalized = normalize(ty.clone());
if ty == normalized {
return ty;
} else {
ty = normalized;
}
}
_ => break,
}
}
ty
}
fn normalize_associated_types_in<T>(&mut self, ty: T) -> T
where
T: HasInterner<Interner = Interner> + TypeFoldable<Interner>,
{
self.table.normalize_associated_types_in(ty)
}
fn resolve_ty_shallow(&mut self, ty: &Ty) -> Ty {
self.table.resolve_ty_shallow(ty)
}
fn resolve_associated_type(&mut self, inner_ty: Ty, assoc_ty: Option<TypeAliasId>) -> Ty {
self.resolve_associated_type_with_params(inner_ty, assoc_ty, &[])
}
fn resolve_associated_type_with_params(
&mut self,
inner_ty: Ty,
assoc_ty: Option<TypeAliasId>,
params: &[GenericArg],
) -> Ty {
match assoc_ty {
Some(res_assoc_ty) => {
let trait_ = match res_assoc_ty.lookup(self.db.upcast()).container {
hir_def::ItemContainerId::TraitId(trait_) => trait_,
_ => panic!("resolve_associated_type called with non-associated type"),
};
let ty = self.table.new_type_var();
let mut param_iter = params.iter().cloned();
let trait_ref = TyBuilder::trait_ref(self.db, trait_)
.push(inner_ty)
.fill(|_| param_iter.next().unwrap())
.build();
let alias_eq = AliasEq {
alias: AliasTy::Projection(ProjectionTy {
associated_ty_id: to_assoc_type_id(res_assoc_ty),
substitution: trait_ref.substitution.clone(),
}),
ty: ty.clone(),
};
self.push_obligation(trait_ref.cast(Interner));
self.push_obligation(alias_eq.cast(Interner));
ty
}
None => self.err_ty(),
}
}
fn resolve_variant(&mut self, path: Option<&Path>, value_ns: bool) -> (Ty, Option<VariantId>) {
let path = match path {
Some(path) => path,
None => return (self.err_ty(), None),
};
let mut ctx = crate::lower::TyLoweringContext::new(
self.db,
&self.resolver,
&self.body.types,
self.owner.into(),
);
let (resolution, unresolved) = if value_ns {
match self.resolver.resolve_path_in_value_ns(self.db.upcast(), path, HygieneId::ROOT) {
Some(ResolveValueResult::ValueNs(value, _)) => match value {
ValueNs::EnumVariantId(var) => {
let substs = ctx.substs_from_path(path, var.into(), true);
let ty = self.db.ty(var.lookup(self.db.upcast()).parent.into());
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
return (ty, Some(var.into()));
}
ValueNs::StructId(strukt) => {
let substs = ctx.substs_from_path(path, strukt.into(), true);
let ty = self.db.ty(strukt.into());
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
return (ty, Some(strukt.into()));
}
ValueNs::ImplSelf(impl_id) => (TypeNs::SelfType(impl_id), None),
_ => return (self.err_ty(), None),
},
Some(ResolveValueResult::Partial(typens, unresolved, _)) => {
(typens, Some(unresolved))
}
None => return (self.err_ty(), None),
}
} else {
match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) {
Some((it, idx, _)) => (it, idx),
None => return (self.err_ty(), None),
}
};
let Some(mod_path) = path.mod_path() else {
never!("resolver should always resolve lang item paths");
return (self.err_ty(), None);
};
return match resolution {
TypeNs::AdtId(AdtId::StructId(strukt)) => {
let substs = ctx.substs_from_path(path, strukt.into(), true);
let ty = self.db.ty(strukt.into());
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
forbid_unresolved_segments((ty, Some(strukt.into())), unresolved)
}
TypeNs::AdtId(AdtId::UnionId(u)) => {
let substs = ctx.substs_from_path(path, u.into(), true);
let ty = self.db.ty(u.into());
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
forbid_unresolved_segments((ty, Some(u.into())), unresolved)
}
TypeNs::EnumVariantId(var) => {
let substs = ctx.substs_from_path(path, var.into(), true);
let ty = self.db.ty(var.lookup(self.db.upcast()).parent.into());
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
forbid_unresolved_segments((ty, Some(var.into())), unresolved)
}
TypeNs::SelfType(impl_id) => {
let generics = crate::generics::generics(self.db.upcast(), impl_id.into());
let substs = generics.placeholder_subst(self.db);
let mut ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs);
let Some(mut remaining_idx) = unresolved else {
return self.resolve_variant_on_alias(ty, None, mod_path);
};
let mut remaining_segments = path.segments().skip(remaining_idx);
while !remaining_segments.is_empty() {
let resolved_segment = path.segments().get(remaining_idx - 1).unwrap();
let current_segment = remaining_segments.take(1);
if let Some((AdtId::EnumId(id), _)) = ty.as_adt() {
let enum_data = self.db.enum_data(id);
let name = current_segment.first().unwrap().name;
if let Some(variant) = enum_data.variant(name) {
return if remaining_segments.len() == 1 {
(ty, Some(variant.into()))
} else {
(self.err_ty(), None)
};
}
}
(ty, _) = ctx.lower_partly_resolved_path(
resolution,
resolved_segment,
current_segment,
false,
);
ty = self.table.insert_type_vars(ty);
ty = self.table.normalize_associated_types_in(ty);
ty = self.table.resolve_ty_shallow(&ty);
if ty.is_unknown() {
return (self.err_ty(), None);
}
remaining_idx += 1;
remaining_segments = remaining_segments.skip(1);
}
let variant = ty.as_adt().and_then(|(id, _)| match id {
AdtId::StructId(s) => Some(VariantId::StructId(s)),
AdtId::UnionId(u) => Some(VariantId::UnionId(u)),
AdtId::EnumId(_) => {
None
}
});
(ty, variant)
}
TypeNs::TypeAliasId(it) => {
let resolved_seg = match unresolved {
None => path.segments().last().unwrap(),
Some(n) => path.segments().get(path.segments().len() - n - 1).unwrap(),
};
let substs =
ctx.substs_from_path_segment(resolved_seg, Some(it.into()), true, None);
let ty = self.db.ty(it.into());
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
self.resolve_variant_on_alias(ty, unresolved, mod_path)
}
TypeNs::AdtSelfType(_) => {
(self.err_ty(), None)
}
TypeNs::GenericParam(_) => {
(self.err_ty(), None)
}
TypeNs::AdtId(AdtId::EnumId(_))
| TypeNs::BuiltinType(_)
| TypeNs::TraitId(_)
| TypeNs::TraitAliasId(_) => {
(self.err_ty(), None)
}
};
fn forbid_unresolved_segments(
result: (Ty, Option<VariantId>),
unresolved: Option<usize>,
) -> (Ty, Option<VariantId>) {
if unresolved.is_none() {
result
} else {
(TyKind::Error.intern(Interner), None)
}
}
}
fn resolve_variant_on_alias(
&mut self,
ty: Ty,
unresolved: Option<usize>,
path: &ModPath,
) -> (Ty, Option<VariantId>) {
let remaining = unresolved.map(|it| path.segments()[it..].len()).filter(|it| it > &0);
let ty = match ty.kind(Interner) {
TyKind::Alias(AliasTy::Projection(proj_ty)) => {
let ty = self.table.normalize_projection_ty(proj_ty.clone());
self.table.resolve_ty_shallow(&ty)
}
_ => ty,
};
match remaining {
None => {
let variant = ty.as_adt().and_then(|(adt_id, _)| match adt_id {
AdtId::StructId(s) => Some(VariantId::StructId(s)),
AdtId::UnionId(u) => Some(VariantId::UnionId(u)),
AdtId::EnumId(_) => {
None
}
});
(ty, variant)
}
Some(1) => {
let segment = path.segments().last().unwrap();
if let Some((AdtId::EnumId(enum_id), _)) = ty.as_adt() {
let enum_data = self.db.enum_data(enum_id);
if let Some(variant) = enum_data.variant(segment) {
return (ty, Some(variant.into()));
}
}
(self.err_ty(), None)
}
Some(_) => {
(self.err_ty(), None)
}
}
}
fn resolve_lang_item(&self, item: LangItem) -> Option<LangItemTarget> {
let krate = self.resolver.krate();
self.db.lang_item(krate, item)
}
fn resolve_output_on(&self, trait_: TraitId) -> Option<TypeAliasId> {
self.db
.trait_data(trait_)
.associated_type_by_name(&Name::new_symbol_root(sym::Output.clone()))
}
fn resolve_lang_trait(&self, lang: LangItem) -> Option<TraitId> {
self.resolve_lang_item(lang)?.as_trait()
}
fn resolve_ops_neg_output(&self) -> Option<TypeAliasId> {
self.resolve_output_on(self.resolve_lang_trait(LangItem::Neg)?)
}
fn resolve_ops_not_output(&self) -> Option<TypeAliasId> {
self.resolve_output_on(self.resolve_lang_trait(LangItem::Not)?)
}
fn resolve_future_future_output(&self) -> Option<TypeAliasId> {
let ItemContainerId::TraitId(trait_) = self
.resolve_lang_item(LangItem::IntoFutureIntoFuture)?
.as_function()?
.lookup(self.db.upcast())
.container
else {
return None;
};
self.resolve_output_on(trait_)
}
fn resolve_boxed_box(&self) -> Option<AdtId> {
let struct_ = self.resolve_lang_item(LangItem::OwnedBox)?.as_struct()?;
Some(struct_.into())
}
fn resolve_range_full(&self) -> Option<AdtId> {
let struct_ = self.resolve_lang_item(LangItem::RangeFull)?.as_struct()?;
Some(struct_.into())
}
fn resolve_range(&self) -> Option<AdtId> {
let struct_ = self.resolve_lang_item(LangItem::Range)?.as_struct()?;
Some(struct_.into())
}
fn resolve_range_inclusive(&self) -> Option<AdtId> {
let struct_ = self.resolve_lang_item(LangItem::RangeInclusiveStruct)?.as_struct()?;
Some(struct_.into())
}
fn resolve_range_from(&self) -> Option<AdtId> {
let struct_ = self.resolve_lang_item(LangItem::RangeFrom)?.as_struct()?;
Some(struct_.into())
}
fn resolve_range_to(&self) -> Option<AdtId> {
let struct_ = self.resolve_lang_item(LangItem::RangeTo)?.as_struct()?;
Some(struct_.into())
}
fn resolve_range_to_inclusive(&self) -> Option<AdtId> {
let struct_ = self.resolve_lang_item(LangItem::RangeToInclusive)?.as_struct()?;
Some(struct_.into())
}
fn resolve_ops_index_output(&self) -> Option<TypeAliasId> {
self.resolve_output_on(self.resolve_lang_trait(LangItem::Index)?)
}
fn resolve_va_list(&self) -> Option<AdtId> {
let struct_ = self.resolve_lang_item(LangItem::VaList)?.as_struct()?;
Some(struct_.into())
}
fn get_traits_in_scope(&self) -> Either<FxHashSet<TraitId>, &FxHashSet<TraitId>> {
let mut b_traits = self.resolver.traits_in_scope_from_block_scopes().peekable();
if b_traits.peek().is_some() {
Either::Left(self.traits_in_scope.iter().copied().chain(b_traits).collect())
} else {
Either::Right(&self.traits_in_scope)
}
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub(crate) enum Expectation {
None,
HasType(Ty),
#[allow(dead_code)]
Castable(Ty),
RValueLikeUnsized(Ty),
}
impl Expectation {
fn has_type(ty: Ty) -> Self {
if ty.is_unknown() {
Expectation::None
} else {
Expectation::HasType(ty)
}
}
fn rvalue_hint(ctx: &mut InferenceContext<'_>, ty: Ty) -> Self {
match ctx.struct_tail_without_normalization(ty.clone()).kind(Interner) {
TyKind::Slice(_) | TyKind::Str | TyKind::Dyn(_) => Expectation::RValueLikeUnsized(ty),
_ => Expectation::has_type(ty),
}
}
fn none() -> Self {
Expectation::None
}
fn resolve(&self, table: &mut unify::InferenceTable<'_>) -> Expectation {
match self {
Expectation::None => Expectation::None,
Expectation::HasType(t) => Expectation::HasType(table.resolve_ty_shallow(t)),
Expectation::Castable(t) => Expectation::Castable(table.resolve_ty_shallow(t)),
Expectation::RValueLikeUnsized(t) => {
Expectation::RValueLikeUnsized(table.resolve_ty_shallow(t))
}
}
}
fn to_option(&self, table: &mut unify::InferenceTable<'_>) -> Option<Ty> {
match self.resolve(table) {
Expectation::None => None,
Expectation::HasType(t)
| Expectation::Castable(t)
| Expectation::RValueLikeUnsized(t) => Some(t),
}
}
fn only_has_type(&self, table: &mut unify::InferenceTable<'_>) -> Option<Ty> {
match self {
Expectation::HasType(t) => Some(table.resolve_ty_shallow(t)),
Expectation::Castable(_) | Expectation::RValueLikeUnsized(_) | Expectation::None => {
None
}
}
}
fn coercion_target_type(&self, table: &mut unify::InferenceTable<'_>) -> Ty {
self.only_has_type(table).unwrap_or_else(|| table.new_type_var())
}
fn adjust_for_branches(&self, table: &mut unify::InferenceTable<'_>) -> Expectation {
match self {
Expectation::HasType(ety) => {
let ety = table.resolve_ty_shallow(ety);
if ety.is_ty_var() {
Expectation::None
} else {
Expectation::HasType(ety)
}
}
Expectation::RValueLikeUnsized(ety) => Expectation::RValueLikeUnsized(ety.clone()),
_ => Expectation::None,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
enum Diverges {
Maybe,
Always,
}
impl Diverges {
fn is_always(self) -> bool {
self == Diverges::Always
}
}
impl std::ops::BitAnd for Diverges {
type Output = Self;
fn bitand(self, other: Self) -> Self {
std::cmp::min(self, other)
}
}
impl std::ops::BitOr for Diverges {
type Output = Self;
fn bitor(self, other: Self) -> Self {
std::cmp::max(self, other)
}
}
impl std::ops::BitAndAssign for Diverges {
fn bitand_assign(&mut self, other: Self) {
*self = *self & other;
}
}
impl std::ops::BitOrAssign for Diverges {
fn bitor_assign(&mut self, other: Self) {
*self = *self | other;
}
}
struct UnknownMismatch<'db>(&'db dyn HirDatabase);
impl chalk_ir::zip::Zipper<Interner> for UnknownMismatch<'_> {
fn zip_tys(&mut self, variance: Variance, a: &Ty, b: &Ty) -> chalk_ir::Fallible<()> {
let zip_substs = |this: &mut Self,
variances,
sub_a: &Substitution,
sub_b: &Substitution| {
this.zip_substs(variance, variances, sub_a.as_slice(Interner), sub_b.as_slice(Interner))
};
match (a.kind(Interner), b.kind(Interner)) {
(TyKind::Adt(id_a, sub_a), TyKind::Adt(id_b, sub_b)) if id_a == id_b => zip_substs(
self,
Some(self.unification_database().adt_variance(*id_a)),
sub_a,
sub_b,
)?,
(
TyKind::AssociatedType(assoc_ty_a, sub_a),
TyKind::AssociatedType(assoc_ty_b, sub_b),
) if assoc_ty_a == assoc_ty_b => zip_substs(self, None, sub_a, sub_b)?,
(TyKind::Tuple(arity_a, sub_a), TyKind::Tuple(arity_b, sub_b))
if arity_a == arity_b =>
{
zip_substs(self, None, sub_a, sub_b)?
}
(TyKind::OpaqueType(opaque_ty_a, sub_a), TyKind::OpaqueType(opaque_ty_b, sub_b))
if opaque_ty_a == opaque_ty_b =>
{
zip_substs(self, None, sub_a, sub_b)?
}
(TyKind::Slice(ty_a), TyKind::Slice(ty_b)) => self.zip_tys(variance, ty_a, ty_b)?,
(TyKind::FnDef(fn_def_a, sub_a), TyKind::FnDef(fn_def_b, sub_b))
if fn_def_a == fn_def_b =>
{
zip_substs(
self,
Some(self.unification_database().fn_def_variance(*fn_def_a)),
sub_a,
sub_b,
)?
}
(TyKind::Ref(mutability_a, _, ty_a), TyKind::Ref(mutability_b, _, ty_b))
if mutability_a == mutability_b =>
{
self.zip_tys(variance, ty_a, ty_b)?
}
(TyKind::Raw(mutability_a, ty_a), TyKind::Raw(mutability_b, ty_b))
if mutability_a == mutability_b =>
{
self.zip_tys(variance, ty_a, ty_b)?
}
(TyKind::Array(ty_a, const_a), TyKind::Array(ty_b, const_b)) if const_a == const_b => {
self.zip_tys(variance, ty_a, ty_b)?
}
(TyKind::Closure(id_a, sub_a), TyKind::Closure(id_b, sub_b)) if id_a == id_b => {
zip_substs(self, None, sub_a, sub_b)?
}
(TyKind::Coroutine(coroutine_a, sub_a), TyKind::Coroutine(coroutine_b, sub_b))
if coroutine_a == coroutine_b =>
{
zip_substs(self, None, sub_a, sub_b)?
}
(
TyKind::CoroutineWitness(coroutine_a, sub_a),
TyKind::CoroutineWitness(coroutine_b, sub_b),
) if coroutine_a == coroutine_b => zip_substs(self, None, sub_a, sub_b)?,
(TyKind::Function(fn_ptr_a), TyKind::Function(fn_ptr_b))
if fn_ptr_a.sig == fn_ptr_b.sig && fn_ptr_a.num_binders == fn_ptr_b.num_binders =>
{
zip_substs(self, None, &fn_ptr_a.substitution.0, &fn_ptr_b.substitution.0)?
}
(TyKind::Error, TyKind::Error) => (),
(TyKind::Error, _)
| (_, TyKind::Error)
| (TyKind::Alias(AliasTy::Projection(_)) | TyKind::AssociatedType(_, _), _)
| (_, TyKind::Alias(AliasTy::Projection(_)) | TyKind::AssociatedType(_, _)) => {
return Err(chalk_ir::NoSolution)
}
_ => (),
}
Ok(())
}
fn zip_lifetimes(&mut self, _: Variance, _: &Lifetime, _: &Lifetime) -> chalk_ir::Fallible<()> {
Ok(())
}
fn zip_consts(&mut self, _: Variance, _: &Const, _: &Const) -> chalk_ir::Fallible<()> {
Ok(())
}
fn zip_binders<T>(
&mut self,
variance: Variance,
a: &Binders<T>,
b: &Binders<T>,
) -> chalk_ir::Fallible<()>
where
T: Clone
+ HasInterner<Interner = Interner>
+ chalk_ir::zip::Zip<Interner>
+ TypeFoldable<Interner>,
{
chalk_ir::zip::Zip::zip_with(self, variance, a.skip_binders(), b.skip_binders())
}
fn interner(&self) -> Interner {
Interner
}
fn unification_database(&self) -> &dyn chalk_ir::UnificationDatabase<Interner> {
&self.0
}
}