Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/infer.rs')
-rw-r--r--crates/hir-ty/src/infer.rs215
1 files changed, 144 insertions, 71 deletions
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 767afdf9eb..7de5b4295f 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -17,11 +17,12 @@ use std::ops::Index;
use std::sync::Arc;
use chalk_ir::{cast::Cast, ConstValue, DebruijnIndex, Mutability, Safety, Scalar, TypeFlags};
+use either::Either;
use hir_def::{
body::Body,
builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
data::{ConstData, StaticData},
- expr::{BindingAnnotation, ExprId, ExprOrPatId, PatId},
+ expr::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, PatId},
lang_item::{LangItem, LangItemTarget},
layout::Integer,
path::Path,
@@ -30,10 +31,9 @@ use hir_def::{
AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, HasModule,
ItemContainerId, Lookup, TraitId, TypeAliasId, VariantId,
};
-use hir_expand::name::name;
-use itertools::Either;
+use hir_expand::name::{name, Name};
use la_arena::ArenaMap;
-use rustc_hash::FxHashMap;
+use rustc_hash::{FxHashMap, FxHashSet};
use stdx::always;
use crate::{
@@ -66,8 +66,10 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
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::FunctionId(f) => ctx.collect_fn(f),
DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_data(s)),
DefWithBodyId::VariantId(v) => {
ctx.return_ty = TyBuilder::builtin(match db.enum_data(v.parent).variant_body_type() {
@@ -144,44 +146,6 @@ impl Default for BindingMode {
}
}
-/// Used to generalize patterns and assignee expressions.
-trait PatLike: Into<ExprOrPatId> + Copy {
- type BindingMode: Copy;
-
- fn infer(
- this: &mut InferenceContext<'_>,
- id: Self,
- expected_ty: &Ty,
- default_bm: Self::BindingMode,
- ) -> Ty;
-}
-
-impl PatLike for ExprId {
- type BindingMode = ();
-
- fn infer(
- this: &mut InferenceContext<'_>,
- id: Self,
- expected_ty: &Ty,
- _: Self::BindingMode,
- ) -> Ty {
- this.infer_assignee_expr(id, expected_ty)
- }
-}
-
-impl PatLike for PatId {
- type BindingMode = BindingMode;
-
- fn infer(
- this: &mut InferenceContext<'_>,
- id: Self,
- expected_ty: &Ty,
- default_bm: Self::BindingMode,
- ) -> Ty {
- this.infer_pat(id, expected_ty, default_bm)
- }
-}
-
#[derive(Debug)]
pub(crate) struct InferOk<T> {
value: T,
@@ -200,11 +164,45 @@ pub(crate) type InferResult<T> = Result<InferOk<T>, TypeError>;
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum InferenceDiagnostic {
- NoSuchField { expr: ExprId },
- PrivateField { expr: ExprId, field: FieldId },
- PrivateAssocItem { id: ExprOrPatId, item: AssocItemId },
- BreakOutsideOfLoop { expr: ExprId, is_break: bool },
- MismatchedArgCount { call_expr: ExprId, expected: usize, found: usize },
+ NoSuchField {
+ expr: ExprId,
+ },
+ 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,
+ /// Contains the type the field resolves to
+ field_with_same_name: Option<Ty>,
+ },
+ // FIXME: Make this proper
+ BreakOutsideOfLoop {
+ expr: ExprId,
+ is_break: bool,
+ bad_value_break: bool,
+ },
+ MismatchedArgCount {
+ call_expr: ExprId,
+ expected: usize,
+ found: usize,
+ },
+ ExpectedFunction {
+ call_expr: ExprId,
+ found: Ty,
+ },
}
/// A mismatch between an expected and an inferred type.
@@ -293,8 +291,10 @@ pub enum Adjust {
/// call, with the signature `&'a T -> &'a U` or `&'a mut T -> &'a mut U`.
/// The target type is `U` in both cases, with the region and mutability
/// being those shared by both the receiver and the returned reference.
+///
+/// Mutability is `None` when we are not sure.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-pub struct OverloadedDeref(pub Mutability);
+pub struct OverloadedDeref(pub Option<Mutability>);
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum AutoBorrow {
@@ -354,7 +354,10 @@ pub struct InferenceResult {
/// **Note**: When a pattern type is resolved it may still contain
/// unresolved or missing subpatterns or subpatterns of mismatched types.
pub type_of_pat: ArenaMap<PatId, Ty>,
+ pub type_of_binding: ArenaMap<BindingId, Ty>,
pub type_of_rpit: ArenaMap<RpitId, Ty>,
+ /// Type of the result of `.into_iter()` on the for. `ExprId` is the one of the whole for loop.
+ pub type_of_for_iterator: FxHashMap<ExprId, Ty>,
type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>,
/// Interned common types to return references to.
standard_types: InternedStandardTypes,
@@ -389,18 +392,15 @@ impl InferenceResult {
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 pat_type_mismatches(&self) -> impl Iterator<Item = (PatId, &TypeMismatch)> {
- self.type_mismatches.iter().filter_map(|(expr_or_pat, mismatch)| match *expr_or_pat {
- ExprOrPatId::PatId(pat) => Some((pat, mismatch)),
- _ => None,
- })
- }
}
impl Index<ExprId> for InferenceResult {
@@ -419,6 +419,14 @@ impl Index<PatId> for InferenceResult {
}
}
+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)
+ }
+}
+
/// The inference context contains all information needed during type inference.
#[derive(Clone, Debug)]
pub(crate) struct InferenceContext<'a> {
@@ -428,14 +436,19 @@ pub(crate) struct InferenceContext<'a> {
pub(crate) resolver: Resolver,
table: unify::InferenceTable<'a>,
trait_env: Arc<TraitEnvironment>,
+ /// The traits in scope, disregarding block modules. This is used for caching purposes.
+ traits_in_scope: FxHashSet<TraitId>,
pub(crate) result: InferenceResult,
/// The return type of the function being inferred, the closure or async block if we're
/// currently within one.
///
/// We might consider using a nested inference context for checking
- /// closures, but currently this is the only field that will change there,
- /// so it doesn't make sense.
+ /// closures so we can swap all shared things out at once.
return_ty: Ty,
+ /// If `Some`, this stores coercion information for returned
+ /// expressions. If `None`, this is in a context where return is
+ /// inappropriate, such as a const expression.
+ return_coercion: Option<CoerceMany>,
/// The resume type and the yield type, respectively, of the generator being inferred.
resume_yield_tys: Option<(Ty, Ty)>,
diverges: Diverges,
@@ -447,7 +460,7 @@ struct BreakableContext {
/// Whether this context contains at least one break expression.
may_break: bool,
/// The coercion target of the context.
- coerce: CoerceMany,
+ coerce: Option<CoerceMany>,
/// The optional label of the context.
label: Option<name::Name>,
kind: BreakableKind,
@@ -503,16 +516,22 @@ impl<'a> InferenceContext<'a> {
trait_env,
return_ty: TyKind::Error.intern(Interner), // set in collect_* calls
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(),
}
}
- fn resolve_all(self) -> InferenceResult {
+ // FIXME: This function should be private in module. It is currently only used in the consteval, since we need
+ // `InferenceResult` in the middle of inference. See the fixme comment in `consteval::eval_to_const`. If you
+ // used this function for another workaround, mention it here. If you really need this function and believe that
+ // there is no problem in it being `pub(crate)`, remove this comment.
+ pub(crate) fn resolve_all(self) -> InferenceResult {
let InferenceContext { mut table, mut result, .. } = self;
table.fallback_if_possible();
@@ -528,13 +547,46 @@ impl<'a> InferenceContext<'a> {
for ty in result.type_of_pat.values_mut() {
*ty = table.resolve_completely(ty.clone());
}
- for ty in result.type_of_rpit.iter_mut().map(|x| x.1) {
+ for ty in result.type_of_binding.values_mut() {
+ *ty = table.resolve_completely(ty.clone());
+ }
+ for ty in result.type_of_rpit.values_mut() {
+ *ty = table.resolve_completely(ty.clone());
+ }
+ for ty in result.type_of_for_iterator.values_mut() {
*ty = table.resolve_completely(ty.clone());
}
for mismatch in result.type_mismatches.values_mut() {
mismatch.expected = table.resolve_completely(mismatch.expected.clone());
mismatch.actual = table.resolve_completely(mismatch.actual.clone());
}
+ result.diagnostics.retain_mut(|diagnostic| {
+ if let InferenceDiagnostic::ExpectedFunction { found: ty, .. }
+ | InferenceDiagnostic::UnresolvedField { receiver: ty, .. }
+ | InferenceDiagnostic::UnresolvedMethodCall { receiver: ty, .. } = diagnostic
+ {
+ *ty = table.resolve_completely(ty.clone());
+ // FIXME: Remove this when we are on par with rustc in terms of inference
+ if ty.contains_unknown() {
+ return false;
+ }
+
+ if let InferenceDiagnostic::UnresolvedMethodCall { field_with_same_name, .. } =
+ diagnostic
+ {
+ let clear = if let Some(ty) = field_with_same_name {
+ *ty = table.resolve_completely(ty.clone());
+ ty.contains_unknown()
+ } else {
+ false
+ };
+ if clear {
+ *field_with_same_name = None;
+ }
+ }
+ }
+ true
+ });
for (_, subst) in result.method_resolutions.values_mut() {
*subst = table.resolve_completely(subst.clone());
}
@@ -580,7 +632,7 @@ impl<'a> InferenceContext<'a> {
let ty = self.insert_type_vars(ty);
let ty = self.normalize_associated_types_in(ty);
- self.infer_pat(*pat, &ty, BindingMode::default());
+ self.infer_top_pat(*pat, &ty);
}
let error_ty = &TypeRef::Error;
let return_ty = if data.has_async_kw() {
@@ -632,10 +684,19 @@ impl<'a> InferenceContext<'a> {
};
self.return_ty = self.normalize_associated_types_in(return_ty);
+ self.return_coercion = Some(CoerceMany::new(self.return_ty.clone()));
}
fn infer_body(&mut self) {
- self.infer_expr_coerce(self.body.body_expr, &Expectation::has_type(self.return_ty.clone()));
+ 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()),
+ )
+ }
+ }
}
fn write_expr_ty(&mut self, expr: ExprId, ty: Ty) {
@@ -662,12 +723,15 @@ impl<'a> InferenceContext<'a> {
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 make_ty(&mut self, type_ref: &TypeRef) -> Ty {
- // FIXME use right resolver for block
let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
let ty = ctx.lower_ty(type_ref);
let ty = self.insert_type_vars(ty);
@@ -681,11 +745,9 @@ impl<'a> InferenceContext<'a> {
/// Replaces ConstScalar::Unknown by a new type var, so we can maybe still infer it.
fn insert_const_vars_shallow(&mut self, c: Const) -> Const {
let data = c.data(Interner);
- match data.value {
+ match &data.value {
ConstValue::Concrete(cc) => match cc.interned {
- hir_def::type_ref::ConstScalar::Unknown => {
- self.table.new_const_var(data.ty.clone())
- }
+ crate::ConstScalar::Unknown => self.table.new_const_var(data.ty.clone()),
_ => c,
},
_ => c,
@@ -785,12 +847,11 @@ impl<'a> InferenceContext<'a> {
Some(path) => path,
None => return (self.err_ty(), None),
};
- let resolver = &self.resolver;
let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
// FIXME: this should resolve assoc items as well, see this example:
// https://play.rust-lang.org/?gist=087992e9e22495446c01c0d4e2d69521
let (resolution, unresolved) = if value_ns {
- match resolver.resolve_path_in_value_ns(self.db.upcast(), path.mod_path()) {
+ match self.resolver.resolve_path_in_value_ns(self.db.upcast(), path.mod_path()) {
Some(ResolveValueResult::ValueNs(value)) => match value {
ValueNs::EnumVariantId(var) => {
let substs = ctx.substs_from_path(path, var.into(), true);
@@ -811,7 +872,7 @@ impl<'a> InferenceContext<'a> {
None => return (self.err_ty(), None),
}
} else {
- match resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) {
+ match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) {
Some(it) => it,
None => return (self.err_ty(), None),
}
@@ -866,7 +927,10 @@ impl<'a> InferenceContext<'a> {
// FIXME potentially resolve assoc type
(self.err_ty(), None)
}
- TypeNs::AdtId(AdtId::EnumId(_)) | TypeNs::BuiltinType(_) | TypeNs::TraitId(_) => {
+ TypeNs::AdtId(AdtId::EnumId(_))
+ | TypeNs::BuiltinType(_)
+ | TypeNs::TraitId(_)
+ | TypeNs::TraitAliasId(_) => {
// FIXME diagnostic
(self.err_ty(), None)
}
@@ -1018,6 +1082,15 @@ impl<'a> InferenceContext<'a> {
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)
+ }
+ }
}
/// When inferring an expression, we propagate downward whatever type hint we