From 84074cb1852aa702e1307e9533e1fa3448e3e04f Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 11 Apr 2021 11:20:45 +0200 Subject: [PATCH 01/27] Remove our unification code, use Chalk's instead --- crates/hir_ty/src/chalk_db.rs | 15 +- crates/hir_ty/src/db.rs | 4 +- crates/hir_ty/src/infer.rs | 18 +- crates/hir_ty/src/infer/coerce.rs | 10 +- crates/hir_ty/src/infer/expr.rs | 12 +- crates/hir_ty/src/infer/path.rs | 2 +- crates/hir_ty/src/infer/unify.rs | 499 +++++-------------------- crates/hir_ty/src/method_resolution.rs | 3 +- 8 files changed, 125 insertions(+), 438 deletions(-) diff --git a/crates/hir_ty/src/chalk_db.rs b/crates/hir_ty/src/chalk_db.rs index 8f054d06bf5..b108fd559c4 100644 --- a/crates/hir_ty/src/chalk_db.rs +++ b/crates/hir_ty/src/chalk_db.rs @@ -344,20 +344,20 @@ impl<'a> chalk_solve::RustIrDatabase for ChalkContext<'a> { } fn unification_database(&self) -> &dyn chalk_ir::UnificationDatabase { - self + &self.db } } -impl<'a> chalk_ir::UnificationDatabase for ChalkContext<'a> { +impl<'a> chalk_ir::UnificationDatabase for &'a dyn HirDatabase { fn fn_def_variance( &self, fn_def_id: chalk_ir::FnDefId, ) -> chalk_ir::Variances { - self.db.fn_def_variance(self.krate, fn_def_id) + HirDatabase::fn_def_variance(*self, fn_def_id) } fn adt_variance(&self, adt_id: chalk_ir::AdtId) -> chalk_ir::Variances { - self.db.adt_variance(self.krate, adt_id) + HirDatabase::adt_variance(*self, adt_id) } } @@ -651,11 +651,7 @@ pub(crate) fn fn_def_datum_query( Arc::new(datum) } -pub(crate) fn fn_def_variance_query( - db: &dyn HirDatabase, - _krate: CrateId, - fn_def_id: FnDefId, -) -> Variances { +pub(crate) fn fn_def_variance_query(db: &dyn HirDatabase, fn_def_id: FnDefId) -> Variances { let callable_def: CallableDefId = from_chalk(db, fn_def_id); let generic_params = generics(db.upcast(), callable_def.into()); Variances::from_iter( @@ -666,7 +662,6 @@ pub(crate) fn fn_def_variance_query( pub(crate) fn adt_variance_query( db: &dyn HirDatabase, - _krate: CrateId, chalk_ir::AdtId(adt_id): AdtId, ) -> Variances { let generic_params = generics(db.upcast(), adt_id.into()); diff --git a/crates/hir_ty/src/db.rs b/crates/hir_ty/src/db.rs index 9da0a02e334..f773aa6211e 100644 --- a/crates/hir_ty/src/db.rs +++ b/crates/hir_ty/src/db.rs @@ -117,10 +117,10 @@ pub trait HirDatabase: DefDatabase + Upcast { fn fn_def_datum(&self, krate: CrateId, fn_def_id: FnDefId) -> Arc; #[salsa::invoke(chalk_db::fn_def_variance_query)] - fn fn_def_variance(&self, krate: CrateId, fn_def_id: FnDefId) -> chalk_db::Variances; + fn fn_def_variance(&self, fn_def_id: FnDefId) -> chalk_db::Variances; #[salsa::invoke(chalk_db::adt_variance_query)] - fn adt_variance(&self, krate: CrateId, adt_id: chalk_db::AdtId) -> chalk_db::Variances; + fn adt_variance(&self, adt_id: chalk_db::AdtId) -> chalk_db::Variances; #[salsa::invoke(chalk_db::associated_ty_value_query)] fn associated_ty_value( diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs index 0ee851a74c7..2a82f1b47a4 100644 --- a/crates/hir_ty/src/infer.rs +++ b/crates/hir_ty/src/infer.rs @@ -217,7 +217,7 @@ struct InferenceContext<'a> { owner: DefWithBodyId, body: Arc, resolver: Resolver, - table: unify::InferenceTable, + table: unify::InferenceTable<'a>, trait_env: Arc, obligations: Vec, last_obligations_check: Option, @@ -252,15 +252,15 @@ fn find_breakable<'c>( impl<'a> InferenceContext<'a> { fn new(db: &'a dyn HirDatabase, owner: DefWithBodyId, resolver: Resolver) -> Self { + let trait_env = + owner.as_generic_def_id().map_or_else(Default::default, |d| db.trait_environment(d)); InferenceContext { result: InferenceResult::default(), - table: unify::InferenceTable::new(), + table: unify::InferenceTable::new(db, trait_env.clone()), obligations: Vec::default(), last_obligations_check: None, return_ty: TyKind::Error.intern(&Interner), // set in collect_fn_signature - trait_env: owner - .as_generic_def_id() - .map_or_else(Default::default, |d| db.trait_environment(d)), + trait_env, db, owner, body: db.body(owner), @@ -346,17 +346,12 @@ impl<'a> InferenceContext<'a> { } fn resolve_obligations_as_possible(&mut self) { - if self.last_obligations_check == Some(self.table.revision) { - // no change - return; - } let _span = profile::span("resolve_obligations_as_possible"); - self.last_obligations_check = Some(self.table.revision); let obligations = mem::replace(&mut self.obligations, Vec::new()); for obligation in obligations { let in_env = InEnvironment::new(&self.trait_env.env, obligation.clone()); - let canonicalized = self.canonicalizer().canonicalize_obligation(in_env); + let canonicalized = self.canonicalize(in_env); let solution = self.db.trait_solve(self.resolver.krate().unwrap(), canonicalized.value.clone()); @@ -395,6 +390,7 @@ impl<'a> InferenceContext<'a> { self.table.unify(ty1, ty2) } + // FIXME get rid of this, instead resolve shallowly where necessary /// Resolves the type as far as currently possible, replacing type variables /// by their known types. All types returned by the infer_* functions should /// be resolved as far as possible, i.e. contain no type variables with diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs index 1f463a42556..ae858b1b09d 100644 --- a/crates/hir_ty/src/infer/coerce.rs +++ b/crates/hir_ty/src/infer/coerce.rs @@ -7,7 +7,7 @@ use chalk_ir::{cast::Cast, Mutability, TyVariableKind}; use hir_def::lang_item::LangItemTarget; -use crate::{autoderef, Canonical, Interner, Solution, Ty, TyBuilder, TyExt, TyKind}; +use crate::{autoderef, Canonical, DomainGoal, Interner, Solution, Ty, TyBuilder, TyExt, TyKind}; use super::{InEnvironment, InferenceContext}; @@ -141,10 +141,10 @@ impl<'a> InferenceContext<'a> { b.push(from_ty.clone()).push(to_ty.clone()).build() }; - let goal = InEnvironment::new(&self.trait_env.env, trait_ref.cast(&Interner)); + let goal: InEnvironment = + InEnvironment::new(&self.trait_env.env, trait_ref.cast(&Interner)); - let canonicalizer = self.canonicalizer(); - let canonicalized = canonicalizer.canonicalize_obligation(goal); + let canonicalized = self.canonicalize(goal); let solution = self.db.trait_solve(krate, canonicalized.value.clone())?; @@ -169,7 +169,7 @@ impl<'a> InferenceContext<'a> { /// /// Note that the parameters are already stripped the outer reference. fn unify_autoderef_behind_ref(&mut self, from_ty: &Ty, to_ty: &Ty) -> bool { - let canonicalized = self.canonicalizer().canonicalize_ty(from_ty.clone()); + let canonicalized = self.canonicalize(from_ty.clone()); let to_ty = self.resolve_ty_shallow(&to_ty); // FIXME: Auto DerefMut for derefed_ty in autoderef::autoderef( diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index 7278faeec08..aab4d31534c 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs @@ -98,7 +98,7 @@ impl<'a> InferenceContext<'a> { goal: projection.trait_ref(self.db).cast(&Interner), environment: trait_env, }; - let canonical = self.canonicalizer().canonicalize_obligation(obligation.clone()); + let canonical = self.canonicalize(obligation.clone()); if self.db.trait_solve(krate, canonical.value).is_some() { self.push_obligation(obligation.goal); let return_ty = self.normalize_projection_ty(projection); @@ -297,7 +297,7 @@ impl<'a> InferenceContext<'a> { } Expr::Call { callee, args } => { let callee_ty = self.infer_expr(*callee, &Expectation::none()); - let canonicalized = self.canonicalizer().canonicalize_ty(callee_ty.clone()); + let canonicalized = self.canonicalize(callee_ty.clone()); let mut derefs = autoderef( self.db, self.resolver.krate(), @@ -442,7 +442,7 @@ impl<'a> InferenceContext<'a> { } Expr::Field { expr, name } => { let receiver_ty = self.infer_expr_inner(*expr, &Expectation::none()); - let canonicalized = self.canonicalizer().canonicalize_ty(receiver_ty); + let canonicalized = self.canonicalize(receiver_ty); let ty = autoderef::autoderef( self.db, self.resolver.krate(), @@ -559,7 +559,7 @@ impl<'a> InferenceContext<'a> { match op { UnaryOp::Deref => match self.resolver.krate() { Some(krate) => { - let canonicalized = self.canonicalizer().canonicalize_ty(inner_ty); + let canonicalized = self.canonicalize(inner_ty); match autoderef::deref( self.db, krate, @@ -676,7 +676,7 @@ impl<'a> InferenceContext<'a> { if let (Some(index_trait), Some(krate)) = (self.resolve_ops_index(), self.resolver.krate()) { - let canonicalized = self.canonicalizer().canonicalize_ty(base_ty); + let canonicalized = self.canonicalize(base_ty); let self_ty = method_resolution::resolve_indexing_op( self.db, &canonicalized.value, @@ -852,7 +852,7 @@ impl<'a> InferenceContext<'a> { generic_args: Option<&GenericArgs>, ) -> Ty { let receiver_ty = self.infer_expr(receiver, &Expectation::none()); - let canonicalized_receiver = self.canonicalizer().canonicalize_ty(receiver_ty.clone()); + let canonicalized_receiver = self.canonicalize(receiver_ty.clone()); let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast()); diff --git a/crates/hir_ty/src/infer/path.rs b/crates/hir_ty/src/infer/path.rs index 495282eba85..bc64b612b2e 100644 --- a/crates/hir_ty/src/infer/path.rs +++ b/crates/hir_ty/src/infer/path.rs @@ -218,7 +218,7 @@ impl<'a> InferenceContext<'a> { return Some(result); } - let canonical_ty = self.canonicalizer().canonicalize_ty(ty.clone()); + let canonical_ty = self.canonicalize(ty.clone()); let krate = self.resolver.krate()?; let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast()); diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs index d8e0b432064..9b28c76d66f 100644 --- a/crates/hir_ty/src/infer/unify.rs +++ b/crates/hir_ty/src/infer/unify.rs @@ -1,133 +1,52 @@ //! Unification and canonicalization logic. -use std::borrow::Cow; +use std::{borrow::Cow, fmt, sync::Arc}; use chalk_ir::{ cast::Cast, fold::Fold, interner::HasInterner, FloatTy, IntTy, TyVariableKind, UniverseIndex, VariableKind, }; -use ena::unify::{InPlaceUnificationTable, NoError, UnifyKey, UnifyValue}; +use chalk_solve::infer::ParameterEnaVariableExt; +use ena::unify::UnifyKey; -use super::{DomainGoal, InferenceContext}; +use super::InferenceContext; use crate::{ - fold_tys, static_lifetime, AliasEq, AliasTy, BoundVar, Canonical, CanonicalVarKinds, - DebruijnIndex, FnPointer, FnSubst, InEnvironment, InferenceVar, Interner, Scalar, Substitution, - Ty, TyExt, TyKind, WhereClause, + db::HirDatabase, fold_tys, static_lifetime, BoundVar, Canonical, DebruijnIndex, GenericArg, + InferenceVar, Interner, Scalar, Substitution, TraitEnvironment, Ty, TyKind, }; impl<'a> InferenceContext<'a> { - pub(super) fn canonicalizer<'b>(&'b mut self) -> Canonicalizer<'a, 'b> + pub(super) fn canonicalize + HasInterner>( + &mut self, + t: T, + ) -> Canonicalized where - 'a: 'b, + T::Result: HasInterner, { - Canonicalizer { ctx: self, free_vars: Vec::new(), var_stack: Vec::new() } + let result = self.table.var_unification_table.canonicalize(&Interner, t); + let free_vars = result + .free_vars + .into_iter() + .map(|free_var| free_var.to_generic_arg(&Interner)) + .collect(); + Canonicalized { value: result.quantified, free_vars } } } -pub(super) struct Canonicalizer<'a, 'b> -where - 'a: 'b, -{ - ctx: &'b mut InferenceContext<'a>, - free_vars: Vec<(InferenceVar, TyVariableKind)>, - /// A stack of type variables that is used to detect recursive types (which - /// are an error, but we need to protect against them to avoid stack - /// overflows). - var_stack: Vec, -} - #[derive(Debug)] pub(super) struct Canonicalized where T: HasInterner, { pub(super) value: Canonical, - free_vars: Vec<(InferenceVar, TyVariableKind)>, -} - -impl<'a, 'b> Canonicalizer<'a, 'b> { - fn add(&mut self, free_var: InferenceVar, kind: TyVariableKind) -> usize { - self.free_vars.iter().position(|&(v, _)| v == free_var).unwrap_or_else(|| { - let next_index = self.free_vars.len(); - self.free_vars.push((free_var, kind)); - next_index - }) - } - - fn do_canonicalize + HasInterner>( - &mut self, - t: T, - binders: DebruijnIndex, - ) -> T { - fold_tys( - t, - |ty, binders| match ty.kind(&Interner) { - &TyKind::InferenceVar(var, kind) => { - let inner = from_inference_var(var); - if self.var_stack.contains(&inner) { - // recursive type - return self.ctx.table.type_variable_table.fallback_value(var, kind); - } - if let Some(known_ty) = - self.ctx.table.var_unification_table.inlined_probe_value(inner).known() - { - self.var_stack.push(inner); - let result = self.do_canonicalize(known_ty.clone(), binders); - self.var_stack.pop(); - result - } else { - let root = self.ctx.table.var_unification_table.find(inner); - let position = self.add(to_inference_var(root), kind); - TyKind::BoundVar(BoundVar::new(binders, position)).intern(&Interner) - } - } - _ => ty, - }, - binders, - ) - } - - fn into_canonicalized>( - self, - result: T, - ) -> Canonicalized { - let kinds = self - .free_vars - .iter() - .map(|&(_, k)| chalk_ir::WithKind::new(VariableKind::Ty(k), UniverseIndex::ROOT)); - Canonicalized { - value: Canonical { - value: result, - binders: CanonicalVarKinds::from_iter(&Interner, kinds), - }, - free_vars: self.free_vars, - } - } - - pub(crate) fn canonicalize_ty(mut self, ty: Ty) -> Canonicalized { - let result = self.do_canonicalize(ty, DebruijnIndex::INNERMOST); - self.into_canonicalized(result) - } - - pub(crate) fn canonicalize_obligation( - mut self, - obligation: InEnvironment, - ) -> Canonicalized> { - let result = match obligation.goal { - DomainGoal::Holds(wc) => { - DomainGoal::Holds(self.do_canonicalize(wc, DebruijnIndex::INNERMOST)) - } - _ => unimplemented!(), - }; - self.into_canonicalized(InEnvironment { goal: result, environment: obligation.environment }) - } + free_vars: Vec, } impl> Canonicalized { pub(super) fn decanonicalize_ty(&self, ty: Ty) -> Ty { crate::fold_free_vars(ty, |bound, _binders| { - let (v, k) = self.free_vars[bound.index]; - TyKind::InferenceVar(v, k).intern(&Interner) + let var = self.free_vars[bound.index]; + var.assert_ty_ref(&Interner).clone() }) } @@ -155,23 +74,29 @@ impl> Canonicalized { }), ); for (i, ty) in solution.value.iter(&Interner).enumerate() { - let (v, k) = self.free_vars[i]; + // FIXME: deal with non-type vars here -- the only problematic part is the normalization + // and maybe we don't need that with lazy normalization? + let var = self.free_vars[i]; // eagerly replace projections in the type; we may be getting types // e.g. from where clauses where this hasn't happened yet let ty = ctx.normalize_associated_types_in( new_vars.apply(ty.assert_ty_ref(&Interner).clone(), &Interner), ); - ctx.table.unify(&TyKind::InferenceVar(v, k).intern(&Interner), &ty); + ctx.table.unify(var.assert_ty_ref(&Interner), &ty); } } } -pub fn could_unify(t1: &Ty, t2: &Ty) -> bool { - InferenceTable::new().unify(t1, t2) +pub fn could_unify(db: &dyn HirDatabase, env: Arc, t1: &Ty, t2: &Ty) -> bool { + InferenceTable::new(db, env).unify(t1, t2) } -pub(crate) fn unify(tys: &Canonical<(Ty, Ty)>) -> Option { - let mut table = InferenceTable::new(); +pub(crate) fn unify( + db: &dyn HirDatabase, + env: Arc, + tys: &Canonical<(Ty, Ty)>, +) -> Option { + let mut table = InferenceTable::new(db, env); let vars = Substitution::from_iter( &Interner, tys.binders @@ -214,16 +139,16 @@ impl TypeVariableTable { } pub(super) fn set_diverging(&mut self, iv: InferenceVar, diverging: bool) { - self.inner[from_inference_var(iv).0 as usize].diverging = diverging; + self.inner[iv.index() as usize].diverging = diverging; } fn is_diverging(&mut self, iv: InferenceVar) -> bool { - self.inner[from_inference_var(iv).0 as usize].diverging + self.inner[iv.index() as usize].diverging } fn fallback_value(&self, iv: InferenceVar, kind: TyVariableKind) -> Ty { match kind { - _ if self.inner[from_inference_var(iv).0 as usize].diverging => TyKind::Never, + _ if self.inner[iv.index() as usize].diverging => TyKind::Never, TyVariableKind::General => TyKind::Error, TyVariableKind::Integer => TyKind::Scalar(Scalar::Int(IntTy::I32)), TyVariableKind::Float => TyKind::Scalar(Scalar::Float(FloatTy::F64)), @@ -237,27 +162,35 @@ pub(crate) struct TypeVariableData { diverging: bool, } -#[derive(Clone, Debug)] -pub(crate) struct InferenceTable { - pub(super) var_unification_table: InPlaceUnificationTable, +type ChalkInferenceTable = chalk_solve::infer::InferenceTable; + +#[derive(Clone)] +pub(crate) struct InferenceTable<'a> { + db: &'a dyn HirDatabase, + trait_env: Arc, + pub(super) var_unification_table: ChalkInferenceTable, pub(super) type_variable_table: TypeVariableTable, - pub(super) revision: u32, } -impl InferenceTable { - pub(crate) fn new() -> Self { +impl<'a> InferenceTable<'a> { + pub(crate) fn new(db: &'a dyn HirDatabase, trait_env: Arc) -> Self { InferenceTable { - var_unification_table: InPlaceUnificationTable::new(), + db, + trait_env, + var_unification_table: ChalkInferenceTable::new(), type_variable_table: TypeVariableTable { inner: Vec::new() }, - revision: 0, } } fn new_var(&mut self, kind: TyVariableKind, diverging: bool) -> Ty { - self.type_variable_table.push(TypeVariableData { diverging }); - let key = self.var_unification_table.new_key(TypeVarValue::Unknown); - assert_eq!(key.0 as usize, self.type_variable_table.inner.len() - 1); - TyKind::InferenceVar(to_inference_var(key), kind).intern(&Interner) + let var = self.var_unification_table.new_variable(UniverseIndex::ROOT); + self.type_variable_table.inner.extend( + (0..1 + var.index() as usize - self.type_variable_table.inner.len()) + .map(|_| TypeVariableData { diverging: false }), + ); + assert_eq!(var.index() as usize, self.type_variable_table.inner.len() - 1); + self.type_variable_table.inner[var.index() as usize].diverging = diverging; + var.to_ty_with_kind(&Interner, kind) } pub(crate) fn new_type_var(&mut self) -> Ty { @@ -280,240 +213,59 @@ impl InferenceTable { self.resolve_ty_completely_inner(&mut Vec::new(), ty) } + // FIXME get rid of this, instead resolve shallowly where necessary pub(crate) fn resolve_ty_as_possible(&mut self, ty: Ty) -> Ty { self.resolve_ty_as_possible_inner(&mut Vec::new(), ty) } pub(crate) fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool { - self.unify_inner(ty1, ty2, 0) - } - - pub(crate) fn unify_substs( - &mut self, - substs1: &Substitution, - substs2: &Substitution, - depth: usize, - ) -> bool { - substs1.iter(&Interner).zip(substs2.iter(&Interner)).all(|(t1, t2)| { - self.unify_inner(t1.assert_ty_ref(&Interner), t2.assert_ty_ref(&Interner), depth) - }) - } - - fn unify_inner(&mut self, ty1: &Ty, ty2: &Ty, depth: usize) -> bool { - if depth > 1000 { - // prevent stackoverflows - panic!("infinite recursion in unification"); - } - if ty1 == ty2 { - return true; - } - // try to resolve type vars first - let ty1 = self.resolve_ty_shallow(ty1); - let ty2 = self.resolve_ty_shallow(ty2); - if ty1.equals_ctor(&ty2) { - match (ty1.kind(&Interner), ty2.kind(&Interner)) { - (TyKind::Adt(_, substs1), TyKind::Adt(_, substs2)) - | (TyKind::FnDef(_, substs1), TyKind::FnDef(_, substs2)) - | ( - TyKind::Function(FnPointer { substitution: FnSubst(substs1), .. }), - TyKind::Function(FnPointer { substitution: FnSubst(substs2), .. }), - ) - | (TyKind::Tuple(_, substs1), TyKind::Tuple(_, substs2)) - | (TyKind::OpaqueType(_, substs1), TyKind::OpaqueType(_, substs2)) - | (TyKind::AssociatedType(_, substs1), TyKind::AssociatedType(_, substs2)) - | (TyKind::Closure(.., substs1), TyKind::Closure(.., substs2)) => { - self.unify_substs(substs1, substs2, depth + 1) - } - (TyKind::Array(ty1, c1), TyKind::Array(ty2, c2)) if c1 == c2 => { - self.unify_inner(ty1, ty2, depth + 1) - } - (TyKind::Ref(_, _, ty1), TyKind::Ref(_, _, ty2)) - | (TyKind::Raw(_, ty1), TyKind::Raw(_, ty2)) - | (TyKind::Slice(ty1), TyKind::Slice(ty2)) => self.unify_inner(ty1, ty2, depth + 1), - _ => true, /* we checked equals_ctor already */ - } - } else if let (TyKind::Closure(.., substs1), TyKind::Closure(.., substs2)) = - (ty1.kind(&Interner), ty2.kind(&Interner)) - { - self.unify_substs(substs1, substs2, depth + 1) + let result = self.var_unification_table.relate( + &Interner, + &self.db, + &self.trait_env.env, + chalk_ir::Variance::Invariant, + ty1, + ty2, + ); + let result = if let Ok(r) = result { + r } else { - self.unify_inner_trivial(&ty1, &ty2, depth) - } - } - - pub(super) fn unify_inner_trivial(&mut self, ty1: &Ty, ty2: &Ty, depth: usize) -> bool { - match (ty1.kind(&Interner), ty2.kind(&Interner)) { - (TyKind::Error, _) | (_, TyKind::Error) => true, - - (TyKind::Placeholder(p1), TyKind::Placeholder(p2)) if *p1 == *p2 => true, - - (TyKind::Dyn(dyn1), TyKind::Dyn(dyn2)) - if dyn1.bounds.skip_binders().interned().len() - == dyn2.bounds.skip_binders().interned().len() => - { - for (pred1, pred2) in dyn1 - .bounds - .skip_binders() - .interned() - .iter() - .zip(dyn2.bounds.skip_binders().interned().iter()) - { - if !self.unify_preds(pred1.skip_binders(), pred2.skip_binders(), depth + 1) { - return false; - } - } - true - } - - ( - TyKind::InferenceVar(tv1, TyVariableKind::General), - TyKind::InferenceVar(tv2, TyVariableKind::General), - ) - | ( - TyKind::InferenceVar(tv1, TyVariableKind::Integer), - TyKind::InferenceVar(tv2, TyVariableKind::Integer), - ) - | ( - TyKind::InferenceVar(tv1, TyVariableKind::Float), - TyKind::InferenceVar(tv2, TyVariableKind::Float), - ) if self.type_variable_table.is_diverging(*tv1) - == self.type_variable_table.is_diverging(*tv2) => - { - // both type vars are unknown since we tried to resolve them - if !self - .var_unification_table - .unioned(from_inference_var(*tv1), from_inference_var(*tv2)) - { - self.var_unification_table - .union(from_inference_var(*tv1), from_inference_var(*tv2)); - self.revision += 1; - } - true - } - - // The order of MaybeNeverTypeVar matters here. - // Unifying MaybeNeverTypeVar and TypeVar will let the latter become MaybeNeverTypeVar. - // Unifying MaybeNeverTypeVar and other concrete type will let the former become it. - (TyKind::InferenceVar(tv, TyVariableKind::General), other) - | (other, TyKind::InferenceVar(tv, TyVariableKind::General)) - | ( - TyKind::InferenceVar(tv, TyVariableKind::Integer), - other @ TyKind::Scalar(Scalar::Int(_)), - ) - | ( - other @ TyKind::Scalar(Scalar::Int(_)), - TyKind::InferenceVar(tv, TyVariableKind::Integer), - ) - | ( - TyKind::InferenceVar(tv, TyVariableKind::Integer), - other @ TyKind::Scalar(Scalar::Uint(_)), - ) - | ( - other @ TyKind::Scalar(Scalar::Uint(_)), - TyKind::InferenceVar(tv, TyVariableKind::Integer), - ) - | ( - TyKind::InferenceVar(tv, TyVariableKind::Float), - other @ TyKind::Scalar(Scalar::Float(_)), - ) - | ( - other @ TyKind::Scalar(Scalar::Float(_)), - TyKind::InferenceVar(tv, TyVariableKind::Float), - ) => { - // the type var is unknown since we tried to resolve it - self.var_unification_table.union_value( - from_inference_var(*tv), - TypeVarValue::Known(other.clone().intern(&Interner)), - ); - self.revision += 1; - true - } - - _ => false, - } - } - - fn unify_preds(&mut self, pred1: &WhereClause, pred2: &WhereClause, depth: usize) -> bool { - match (pred1, pred2) { - (WhereClause::Implemented(tr1), WhereClause::Implemented(tr2)) - if tr1.trait_id == tr2.trait_id => - { - self.unify_substs(&tr1.substitution, &tr2.substitution, depth + 1) - } - ( - WhereClause::AliasEq(AliasEq { alias: alias1, ty: ty1 }), - WhereClause::AliasEq(AliasEq { alias: alias2, ty: ty2 }), - ) => { - let (substitution1, substitution2) = match (alias1, alias2) { - (AliasTy::Projection(projection_ty1), AliasTy::Projection(projection_ty2)) - if projection_ty1.associated_ty_id == projection_ty2.associated_ty_id => - { - (&projection_ty1.substitution, &projection_ty2.substitution) - } - (AliasTy::Opaque(opaque1), AliasTy::Opaque(opaque2)) - if opaque1.opaque_ty_id == opaque2.opaque_ty_id => - { - (&opaque1.substitution, &opaque2.substitution) - } - _ => return false, - }; - self.unify_substs(&substitution1, &substitution2, depth + 1) - && self.unify_inner(&ty1, &ty2, depth + 1) - } - _ => false, - } + return false; + }; + // TODO deal with new goals + true } /// If `ty` is a type variable with known type, returns that type; /// otherwise, return ty. + // FIXME this could probably just return Ty pub(crate) fn resolve_ty_shallow<'b>(&mut self, ty: &'b Ty) -> Cow<'b, Ty> { - let mut ty = Cow::Borrowed(ty); - // The type variable could resolve to a int/float variable. Hence try - // resolving up to three times; each type of variable shouldn't occur - // more than once - for i in 0..3 { - if i > 0 { - cov_mark::hit!(type_var_resolves_to_int_var); - } - match ty.kind(&Interner) { - TyKind::InferenceVar(tv, _) => { - let inner = from_inference_var(*tv); - match self.var_unification_table.inlined_probe_value(inner).known() { - Some(known_ty) => { - // The known_ty can't be a type var itself - ty = Cow::Owned(known_ty.clone()); - } - _ => return ty, - } - } - _ => return ty, - } - } - log::error!("Inference variable still not resolved: {:?}", ty); - ty + self.var_unification_table + .normalize_ty_shallow(&Interner, ty) + .map_or(Cow::Borrowed(ty), Cow::Owned) } /// Resolves the type as far as currently possible, replacing type variables /// by their known types. All types returned by the infer_* functions should /// be resolved as far as possible, i.e. contain no type variables with /// known type. - fn resolve_ty_as_possible_inner(&mut self, tv_stack: &mut Vec, ty: Ty) -> Ty { + fn resolve_ty_as_possible_inner(&mut self, tv_stack: &mut Vec, ty: Ty) -> Ty { fold_tys( ty, |ty, _| match ty.kind(&Interner) { &TyKind::InferenceVar(tv, kind) => { - let inner = from_inference_var(tv); - if tv_stack.contains(&inner) { + if tv_stack.contains(&tv) { cov_mark::hit!(type_var_cycles_resolve_as_possible); // recursive type return self.type_variable_table.fallback_value(tv, kind); } - if let Some(known_ty) = - self.var_unification_table.inlined_probe_value(inner).known() - { + if let Some(known_ty) = self.var_unification_table.probe_var(tv) { // known_ty may contain other variables that are known by now - tv_stack.push(inner); - let result = self.resolve_ty_as_possible_inner(tv_stack, known_ty.clone()); + tv_stack.push(tv); + let result = self.resolve_ty_as_possible_inner( + tv_stack, + known_ty.assert_ty_ref(&Interner).clone(), + ); tv_stack.pop(); result } else { @@ -528,23 +280,24 @@ impl InferenceTable { /// Resolves the type completely; type variables without known type are /// replaced by TyKind::Unknown. - fn resolve_ty_completely_inner(&mut self, tv_stack: &mut Vec, ty: Ty) -> Ty { + fn resolve_ty_completely_inner(&mut self, tv_stack: &mut Vec, ty: Ty) -> Ty { + // FIXME implement as a proper Folder, handle lifetimes and consts as well fold_tys( ty, |ty, _| match ty.kind(&Interner) { &TyKind::InferenceVar(tv, kind) => { - let inner = from_inference_var(tv); - if tv_stack.contains(&inner) { + if tv_stack.contains(&tv) { cov_mark::hit!(type_var_cycles_resolve_completely); // recursive type return self.type_variable_table.fallback_value(tv, kind); } - if let Some(known_ty) = - self.var_unification_table.inlined_probe_value(inner).known() - { + if let Some(known_ty) = self.var_unification_table.probe_var(tv) { // known_ty may contain other variables that are known by now - tv_stack.push(inner); - let result = self.resolve_ty_completely_inner(tv_stack, known_ty.clone()); + tv_stack.push(tv); + let result = self.resolve_ty_completely_inner( + tv_stack, + known_ty.assert_ty_ref(&Interner).clone(), + ); tv_stack.pop(); result } else { @@ -558,68 +311,10 @@ impl InferenceTable { } } -/// The ID of a type variable. -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub(super) struct TypeVarId(pub(super) u32); - -impl UnifyKey for TypeVarId { - type Value = TypeVarValue; - - fn index(&self) -> u32 { - self.0 - } - - fn from_index(i: u32) -> Self { - TypeVarId(i) - } - - fn tag() -> &'static str { - "TypeVarId" - } -} - -fn from_inference_var(var: InferenceVar) -> TypeVarId { - TypeVarId(var.index()) -} - -fn to_inference_var(TypeVarId(index): TypeVarId) -> InferenceVar { - index.into() -} - -/// The value of a type variable: either we already know the type, or we don't -/// know it yet. -#[derive(Clone, PartialEq, Eq, Debug)] -pub(super) enum TypeVarValue { - Known(Ty), - Unknown, -} - -impl TypeVarValue { - fn known(&self) -> Option<&Ty> { - match self { - TypeVarValue::Known(ty) => Some(ty), - TypeVarValue::Unknown => None, - } - } -} - -impl UnifyValue for TypeVarValue { - type Error = NoError; - - fn unify_values(value1: &Self, value2: &Self) -> Result { - match (value1, value2) { - // We should never equate two type variables, both of which have - // known types. Instead, we recursively equate those types. - (TypeVarValue::Known(t1), TypeVarValue::Known(t2)) => panic!( - "equating two type variables, both of which have known types: {:?} and {:?}", - t1, t2 - ), - - // If one side is known, prefer that one. - (TypeVarValue::Known(..), TypeVarValue::Unknown) => Ok(value1.clone()), - (TypeVarValue::Unknown, TypeVarValue::Known(..)) => Ok(value2.clone()), - - (TypeVarValue::Unknown, TypeVarValue::Unknown) => Ok(TypeVarValue::Unknown), - } +impl<'a> fmt::Debug for InferenceTable<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("InferenceTable") + .field("num_vars", &self.type_variable_table.inner.len()) + .finish() } } diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs index 48bbcfd9ffa..37e1f89d825 100644 --- a/crates/hir_ty/src/method_resolution.rs +++ b/crates/hir_ty/src/method_resolution.rs @@ -798,7 +798,8 @@ pub(crate) fn inherent_impl_substs( binders: CanonicalVarKinds::from_iter(&Interner, kinds), value: (self_ty_with_vars, self_ty.value.clone()), }; - let substs = super::infer::unify(&tys)?; + let trait_env = Arc::new(TraitEnvironment::default()); // FIXME + let substs = super::infer::unify(db, trait_env, &tys)?; // We only want the substs for the vars we added, not the ones from self_ty. // Also, if any of the vars we added are still in there, we replace them by // Unknown. I think this can only really happen if self_ty contained From 693582946fae1813627ad59f60a31c9237e98744 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 1 May 2021 21:53:10 +0200 Subject: [PATCH 02/27] Rewrite coercion using the new unification --- crates/hir_ty/src/builder.rs | 10 +- crates/hir_ty/src/chalk_ext.rs | 5 + crates/hir_ty/src/infer.rs | 12 + crates/hir_ty/src/infer/coerce.rs | 421 +++++++++++++++++++++++------- crates/hir_ty/src/infer/unify.rs | 37 ++- crates/hir_ty/src/lib.rs | 11 + 6 files changed, 381 insertions(+), 115 deletions(-) diff --git a/crates/hir_ty/src/builder.rs b/crates/hir_ty/src/builder.rs index e25ef866d7b..49d06954156 100644 --- a/crates/hir_ty/src/builder.rs +++ b/crates/hir_ty/src/builder.rs @@ -77,15 +77,7 @@ impl TyBuilder<()> { } pub fn fn_ptr(sig: CallableSig) -> Ty { - TyKind::Function(FnPointer { - num_binders: 0, - sig: FnSig { abi: (), safety: Safety::Safe, variadic: sig.is_varargs }, - substitution: FnSubst(Substitution::from_iter( - &Interner, - sig.params_and_return.iter().cloned(), - )), - }) - .intern(&Interner) + TyKind::Function(sig.to_fn_ptr()).intern(&Interner) } pub fn builtin(builtin: BuiltinType) -> Ty { diff --git a/crates/hir_ty/src/chalk_ext.rs b/crates/hir_ty/src/chalk_ext.rs index 5232a7d8038..df340a6ca17 100644 --- a/crates/hir_ty/src/chalk_ext.rs +++ b/crates/hir_ty/src/chalk_ext.rs @@ -18,6 +18,7 @@ pub trait TyExt { fn is_unit(&self) -> bool; fn is_never(&self) -> bool; fn is_unknown(&self) -> bool; + fn is_ty_var(&self) -> bool; fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>; fn as_builtin(&self) -> Option; @@ -55,6 +56,10 @@ impl TyExt for Ty { matches!(self.kind(&Interner), TyKind::Error) } + fn is_ty_var(&self) -> bool { + matches!(self.kind(&Interner), TyKind::InferenceVar(_, _)) + } + fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)> { match self.kind(&Interner) { TyKind::Adt(AdtId(adt), parameters) => Some((*adt, parameters)), diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs index 2a82f1b47a4..603068ab531 100644 --- a/crates/hir_ty/src/infer.rs +++ b/crates/hir_ty/src/infer.rs @@ -106,6 +106,14 @@ impl Default for BindingMode { } } +#[derive(Debug)] +pub(crate) struct InferOk { + // obligations +} +#[derive(Debug)] +pub(crate) struct TypeError; +pub(crate) type InferResult = Result; + /// A mismatch between an expected and an inferred type. #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub struct TypeMismatch { @@ -390,6 +398,10 @@ impl<'a> InferenceContext<'a> { self.table.unify(ty1, ty2) } + fn unify_inner(&mut self, ty1: &Ty, ty2: &Ty) -> InferResult { + self.table.unify_inner(ty1, ty2) + } + // FIXME get rid of this, instead resolve shallowly where necessary /// Resolves the type as far as currently possible, replacing type variables /// by their known types. All types returned by the infer_* functions should diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs index ae858b1b09d..86a7cd4c24b 100644 --- a/crates/hir_ty/src/infer/coerce.rs +++ b/crates/hir_ty/src/infer/coerce.rs @@ -2,14 +2,18 @@ //! happen in certain places, e.g. weakening `&mut` to `&` or deref coercions //! like going from `&Vec` to `&[T]`. //! -//! See: https://doc.rust-lang.org/nomicon/coercions.html +//! See https://doc.rust-lang.org/nomicon/coercions.html and +//! librustc_typeck/check/coercion.rs. use chalk_ir::{cast::Cast, Mutability, TyVariableKind}; use hir_def::lang_item::LangItemTarget; -use crate::{autoderef, Canonical, DomainGoal, Interner, Solution, Ty, TyBuilder, TyExt, TyKind}; +use crate::{ + autoderef, static_lifetime, Canonical, DomainGoal, FnPointer, FnSig, Interner, Solution, + Substitution, Ty, TyBuilder, TyExt, TyKind, +}; -use super::{InEnvironment, InferenceContext}; +use super::{InEnvironment, InferOk, InferResult, InferenceContext, TypeError}; impl<'a> InferenceContext<'a> { /// Unify two types, but may coerce the first one to the second one @@ -17,7 +21,16 @@ impl<'a> InferenceContext<'a> { pub(super) fn coerce(&mut self, from_ty: &Ty, to_ty: &Ty) -> bool { let from_ty = self.resolve_ty_shallow(from_ty).into_owned(); let to_ty = self.resolve_ty_shallow(to_ty); - self.coerce_inner(from_ty, &to_ty) + match self.coerce_inner(from_ty, &to_ty) { + Ok(_result) => { + // TODO deal with goals + true + } + Err(_) => { + // FIXME deal with error + false + } + } } /// Merge two types from different branches, with possible coercion. @@ -52,93 +65,308 @@ impl<'a> InferenceContext<'a> { } } - fn coerce_inner(&mut self, mut from_ty: Ty, to_ty: &Ty) -> bool { - match (from_ty.kind(&Interner), to_ty.kind(&Interner)) { - // Never type will make type variable to fallback to Never Type instead of Unknown. - (TyKind::Never, TyKind::InferenceVar(tv, TyVariableKind::General)) => { - self.table.type_variable_table.set_diverging(*tv, true); - return true; - } - (TyKind::Never, _) => return true, - - // Trivial cases, this should go after `never` check to - // avoid infer result type to be never - _ => { - if self.table.unify_inner_trivial(&from_ty, &to_ty, 0) { - return true; + fn coerce_inner(&mut self, mut from_ty: Ty, to_ty: &Ty) -> InferResult { + if from_ty.is_never() { + // Subtle: If we are coercing from `!` to `?T`, where `?T` is an unbound + // type variable, we want `?T` to fallback to `!` if not + // otherwise constrained. An example where this arises: + // + // let _: Option = Some({ return; }); + // + // here, we would coerce from `!` to `?T`. + match to_ty.kind(&Interner) { + TyKind::InferenceVar(tv, TyVariableKind::General) => { + self.table.type_variable_table.set_diverging(*tv, true); } + _ => {} } + return Ok(InferOk {}); } - // Pointer weakening and function to pointer - match (from_ty.kind(&Interner), to_ty.kind(&Interner)) { - // `*mut T` -> `*const T` - (TyKind::Raw(_, inner), TyKind::Raw(m2 @ Mutability::Not, ..)) => { - from_ty = TyKind::Raw(*m2, inner.clone()).intern(&Interner); - } - // `&mut T` -> `&T` - (TyKind::Ref(_, lt, inner), TyKind::Ref(m2 @ Mutability::Not, ..)) => { - from_ty = TyKind::Ref(*m2, lt.clone(), inner.clone()).intern(&Interner); - } - // `&T` -> `*const T` - // `&mut T` -> `*mut T`/`*const T` - (TyKind::Ref(.., substs), &TyKind::Raw(m2 @ Mutability::Not, ..)) - | (TyKind::Ref(Mutability::Mut, _, substs), &TyKind::Raw(m2, ..)) => { - from_ty = TyKind::Raw(m2, substs.clone()).intern(&Interner); - } + // Consider coercing the subtype to a DST + if let Ok(ret) = self.try_coerce_unsized(&from_ty, &to_ty) { + return Ok(ret); + } - // Illegal mutability conversion - (TyKind::Raw(Mutability::Not, ..), TyKind::Raw(Mutability::Mut, ..)) - | (TyKind::Ref(Mutability::Not, ..), TyKind::Ref(Mutability::Mut, ..)) => return false, - - // `{function_type}` -> `fn()` - (TyKind::FnDef(..), TyKind::Function { .. }) => match from_ty.callable_sig(self.db) { - None => return false, - Some(sig) => { - from_ty = TyBuilder::fn_ptr(sig); - } - }, - - (TyKind::Closure(.., substs), TyKind::Function { .. }) => { - from_ty = substs.at(&Interner, 0).assert_ty_ref(&Interner).clone(); + // Examine the supertype and consider auto-borrowing. + match to_ty.kind(&Interner) { + TyKind::Raw(mt, _) => { + return self.coerce_ptr(from_ty, to_ty, *mt); + } + TyKind::Ref(mt, _, _) => { + return self.coerce_ref(from_ty, to_ty, *mt); } - _ => {} } - if let Some(ret) = self.try_coerce_unsized(&from_ty, &to_ty) { - return ret; + match from_ty.kind(&Interner) { + TyKind::FnDef(..) => { + // Function items are coercible to any closure + // type; function pointers are not (that would + // require double indirection). + // Additionally, we permit coercion of function + // items to drop the unsafe qualifier. + self.coerce_from_fn_item(from_ty, to_ty) + } + TyKind::Function(from_fn_ptr) => { + // We permit coercion of fn pointers to drop the + // unsafe qualifier. + self.coerce_from_fn_pointer(from_ty.clone(), from_fn_ptr, to_ty) + } + TyKind::Closure(_, from_substs) => { + // Non-capturing closures are coercible to + // function pointers or unsafe function pointers. + // It cannot convert closures that require unsafe. + self.coerce_closure_to_fn(from_ty.clone(), from_substs, to_ty) + } + _ => { + // Otherwise, just use unification rules. + self.unify_inner(&from_ty, to_ty) + } } + } - // Auto Deref if cannot coerce - match (from_ty.kind(&Interner), to_ty.kind(&Interner)) { - // FIXME: DerefMut - (TyKind::Ref(.., st1), TyKind::Ref(.., st2)) => { - self.unify_autoderef_behind_ref(st1, st2) + fn coerce_ptr(&mut self, from_ty: Ty, to_ty: &Ty, to_mt: Mutability) -> InferResult { + let (_is_ref, from_mt, from_inner) = match from_ty.kind(&Interner) { + TyKind::Ref(mt, _, ty) => (true, mt, ty), + TyKind::Raw(mt, ty) => (false, mt, ty), + _ => return self.unify_inner(&from_ty, to_ty), + }; + + coerce_mutabilities(*from_mt, to_mt)?; + + // Check that the types which they point at are compatible. + let from_raw = TyKind::Raw(to_mt, from_inner.clone()).intern(&Interner); + // FIXME: behavior differs based on is_ref once we're computing adjustments + self.unify_inner(&from_raw, to_ty) + } + + /// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`. + /// To match `A` with `B`, autoderef will be performed, + /// calling `deref`/`deref_mut` where necessary. + fn coerce_ref(&mut self, from_ty: Ty, to_ty: &Ty, to_mt: Mutability) -> InferResult { + let (from_mt, from_inner) = match from_ty.kind(&Interner) { + TyKind::Ref(mt, _, ty) => { + coerce_mutabilities(*mt, to_mt)?; + (*mt, ty.clone()) + } + _ => return self.unify_inner(&from_ty, to_ty), + }; + + // NOTE: this code is mostly copied and adapted from rustc, and + // currently more complicated than necessary, carrying errors around + // etc.. This complication will become necessary when we actually track + // details of coercion errors though, so I think it's useful to leave + // the structure like it is. + + let canonicalized = self.canonicalize(from_ty.clone()); + let mut autoderef = autoderef::autoderef( + self.db, + self.resolver.krate(), + InEnvironment { + goal: canonicalized.value.clone(), + environment: self.trait_env.env.clone(), + }, + ); + let mut first_error = None; + let mut found = None; + + for (autoderefs, referent_ty) in autoderef.enumerate() { + if autoderefs == 0 { + // Don't let this pass, otherwise it would cause + // &T to autoref to &&T. + continue; } - // Otherwise, normal unify - _ => self.unify(&from_ty, to_ty), + let referent_ty = canonicalized.decanonicalize_ty(referent_ty.value); + + // At this point, we have deref'd `a` to `referent_ty`. So + // imagine we are coercing from `&'a mut Vec` to `&'b mut [T]`. + // In the autoderef loop for `&'a mut Vec`, we would get + // three callbacks: + // + // - `&'a mut Vec` -- 0 derefs, just ignore it + // - `Vec` -- 1 deref + // - `[T]` -- 2 deref + // + // At each point after the first callback, we want to + // check to see whether this would match out target type + // (`&'b mut [T]`) if we autoref'd it. We can't just + // compare the referent types, though, because we still + // have to consider the mutability. E.g., in the case + // we've been considering, we have an `&mut` reference, so + // the `T` in `[T]` needs to be unified with equality. + // + // Therefore, we construct reference types reflecting what + // the types will be after we do the final auto-ref and + // compare those. Note that this means we use the target + // mutability [1], since it may be that we are coercing + // from `&mut T` to `&U`. + let lt = static_lifetime(); // FIXME: handle lifetimes correctly, see rustc + let derefd_from_ty = TyKind::Ref(to_mt, lt, referent_ty).intern(&Interner); + match self.unify_inner(&derefd_from_ty, to_ty) { + Ok(result) => { + found = Some(result); + break; + } + Err(err) => { + if first_error.is_none() { + first_error = Some(err); + } + } + } + } + + // Extract type or return an error. We return the first error + // we got, which should be from relating the "base" type + // (e.g., in example above, the failure from relating `Vec` + // to the target type), since that should be the least + // confusing. + let result = match found { + Some(d) => d, + None => { + let err = first_error.expect("coerce_borrowed_pointer had no error"); + return Err(err); + } + }; + + Ok(result) + } + + /// Attempts to coerce from the type of a Rust function item into a closure + /// or a function pointer. + fn coerce_from_fn_item(&mut self, from_ty: Ty, to_ty: &Ty) -> InferResult { + match to_ty.kind(&Interner) { + TyKind::Function(b_sig) => { + let from_sig = from_ty.callable_sig(self.db).expect("FnDef had no sig"); + + // FIXME check ABI: Intrinsics are not coercible to function pointers + // FIXME Safe `#[target_feature]` functions are not assignable to safe fn pointers (RFC 2396) + + // FIXME rustc normalizes assoc types in the sig here, not sure if necessary + + let from_sig = from_sig.to_fn_ptr(); + let from_fn_pointer = TyKind::Function(from_sig.clone()).intern(&Interner); + let ok = self.coerce_from_safe_fn(from_fn_pointer, &from_sig, to_ty)?; + + Ok(ok) + } + _ => self.unify_inner(&from_ty, to_ty), + } + } + + fn coerce_from_fn_pointer( + &mut self, + from_ty: Ty, + from_f: &FnPointer, + to_ty: &Ty, + ) -> InferResult { + self.coerce_from_safe_fn(from_ty, from_f, to_ty) + } + + fn coerce_from_safe_fn( + &mut self, + from_ty: Ty, + from_fn_ptr: &FnPointer, + to_ty: &Ty, + ) -> InferResult { + if let TyKind::Function(to_fn_ptr) = to_ty.kind(&Interner) { + if let (chalk_ir::Safety::Safe, chalk_ir::Safety::Unsafe) = + (from_fn_ptr.sig.safety, to_fn_ptr.sig.safety) + { + let from_unsafe = + TyKind::Function(safe_to_unsafe_fn_ty(from_fn_ptr.clone())).intern(&Interner); + return self.unify_inner(&from_unsafe, to_ty); + } + } + self.unify_inner(&from_ty, to_ty) + } + + /// Attempts to coerce from the type of a non-capturing closure into a + /// function pointer. + fn coerce_closure_to_fn( + &mut self, + from_ty: Ty, + from_substs: &Substitution, + to_ty: &Ty, + ) -> InferResult { + match to_ty.kind(&Interner) { + TyKind::Function(fn_ty) /* if from_substs is non-capturing (FIXME) */ => { + // We coerce the closure, which has fn type + // `extern "rust-call" fn((arg0,arg1,...)) -> _` + // to + // `fn(arg0,arg1,...) -> _` + // or + // `unsafe fn(arg0,arg1,...) -> _` + let safety = fn_ty.sig.safety; + let pointer_ty = coerce_closure_fn_ty(from_substs, safety); + self.unify_inner(&pointer_ty, to_ty) + } + _ => self.unify_inner(&from_ty, to_ty), } } /// Coerce a type using `from_ty: CoerceUnsized` /// /// See: https://doc.rust-lang.org/nightly/std/marker/trait.CoerceUnsized.html - fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty) -> Option { + fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty) -> InferResult { + // These 'if' statements require some explanation. + // The `CoerceUnsized` trait is special - it is only + // possible to write `impl CoerceUnsized for A` where + // A and B have 'matching' fields. This rules out the following + // two types of blanket impls: + // + // `impl CoerceUnsized for SomeType` + // `impl CoerceUnsized for T` + // + // Both of these trigger a special `CoerceUnsized`-related error (E0376) + // + // We can take advantage of this fact to avoid performing unecessary work. + // If either `source` or `target` is a type variable, then any applicable impl + // would need to be generic over the self-type (`impl CoerceUnsized for T`) + // or generic over the `CoerceUnsized` type parameter (`impl CoerceUnsized for + // SomeType`). + // + // However, these are exactly the kinds of impls which are forbidden by + // the compiler! Therefore, we can be sure that coercion will always fail + // when either the source or target type is a type variable. This allows us + // to skip performing any trait selection, and immediately bail out. + if from_ty.is_ty_var() { + return Err(TypeError); + } + if to_ty.is_ty_var() { + return Err(TypeError); + } + + // Handle reborrows before trying to solve `Source: CoerceUnsized`. + let coerce_from = match (from_ty.kind(&Interner), to_ty.kind(&Interner)) { + (TyKind::Ref(from_mt, _, from_inner), TyKind::Ref(to_mt, _, _)) => { + coerce_mutabilities(*from_mt, *to_mt)?; + + let lt = static_lifetime(); + TyKind::Ref(*to_mt, lt, from_inner.clone()).intern(&Interner) + } + (TyKind::Ref(from_mt, _, from_inner), TyKind::Raw(to_mt, _)) => { + coerce_mutabilities(*from_mt, *to_mt)?; + + TyKind::Raw(*to_mt, from_inner.clone()).intern(&Interner) + } + _ => from_ty.clone(), + }; + let krate = self.resolver.krate().unwrap(); let coerce_unsized_trait = match self.db.lang_item(krate, "coerce_unsized".into()) { Some(LangItemTarget::TraitId(trait_)) => trait_, - _ => return None, + _ => return Err(TypeError), }; let trait_ref = { let b = TyBuilder::trait_ref(self.db, coerce_unsized_trait); if b.remaining() != 2 { // The CoerceUnsized trait should have two generic params: Self and T. - return None; + return Err(TypeError); } - b.push(from_ty.clone()).push(to_ty.clone()).build() + b.push(coerce_from.clone()).push(to_ty.clone()).build() }; let goal: InEnvironment = @@ -146,7 +374,11 @@ impl<'a> InferenceContext<'a> { let canonicalized = self.canonicalize(goal); - let solution = self.db.trait_solve(krate, canonicalized.value.clone())?; + // FIXME: rustc's coerce_unsized is more specialized -- it only tries to + // solve `CoerceUnsized` and `Unsize` goals at this point and leaves the + // rest for later. Also, there's some logic about sized type variables. + // Need to find out in what cases this is necessary + let solution = self.db.trait_solve(krate, canonicalized.value.clone()).ok_or(TypeError)?; match solution { Solution::Unique(v) => { @@ -159,38 +391,39 @@ impl<'a> InferenceContext<'a> { }, ); } - _ => return None, + _ => return Err(TypeError), }; - Some(true) - } - - /// Unify `from_ty` to `to_ty` with optional auto Deref - /// - /// Note that the parameters are already stripped the outer reference. - fn unify_autoderef_behind_ref(&mut self, from_ty: &Ty, to_ty: &Ty) -> bool { - let canonicalized = self.canonicalize(from_ty.clone()); - let to_ty = self.resolve_ty_shallow(&to_ty); - // FIXME: Auto DerefMut - for derefed_ty in autoderef::autoderef( - self.db, - self.resolver.krate(), - InEnvironment { - goal: canonicalized.value.clone(), - environment: self.trait_env.env.clone(), - }, - ) { - let derefed_ty = canonicalized.decanonicalize_ty(derefed_ty.value); - let from_ty = self.resolve_ty_shallow(&derefed_ty); - // Stop when constructor matches. - if from_ty.equals_ctor(&to_ty) { - // It will not recurse to `coerce`. - return self.table.unify(&from_ty, &to_ty); - } else if self.table.unify_inner_trivial(&derefed_ty, &to_ty, 0) { - return true; - } - } - - false + Ok(InferOk {}) + } +} + +fn coerce_closure_fn_ty(closure_substs: &Substitution, safety: chalk_ir::Safety) -> Ty { + let closure_sig = closure_substs.at(&Interner, 0).assert_ty_ref(&Interner).clone(); + match closure_sig.kind(&Interner) { + TyKind::Function(fn_ty) => TyKind::Function(FnPointer { + num_binders: fn_ty.num_binders, + sig: FnSig { safety, ..fn_ty.sig }, + substitution: fn_ty.substitution.clone(), + }) + .intern(&Interner), + _ => TyKind::Error.intern(&Interner), + } +} + +fn safe_to_unsafe_fn_ty(fn_ty: FnPointer) -> FnPointer { + FnPointer { + num_binders: fn_ty.num_binders, + sig: FnSig { safety: chalk_ir::Safety::Unsafe, ..fn_ty.sig }, + substitution: fn_ty.substitution, + } +} + +fn coerce_mutabilities(from: Mutability, to: Mutability) -> InferResult { + match (from, to) { + (Mutability::Mut, Mutability::Mut) + | (Mutability::Mut, Mutability::Not) + | (Mutability::Not, Mutability::Not) => Ok(InferOk {}), + (Mutability::Not, Mutability::Mut) => Err(TypeError), } } diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs index 9b28c76d66f..3a4258e86cd 100644 --- a/crates/hir_ty/src/infer/unify.rs +++ b/crates/hir_ty/src/infer/unify.rs @@ -9,7 +9,7 @@ use chalk_ir::{ use chalk_solve::infer::ParameterEnaVariableExt; use ena::unify::UnifyKey; -use super::InferenceContext; +use super::{InferOk, InferResult, InferenceContext, TypeError}; use crate::{ db::HirDatabase, fold_tys, static_lifetime, BoundVar, Canonical, DebruijnIndex, GenericArg, InferenceVar, Interner, Scalar, Substitution, TraitEnvironment, Ty, TyKind, @@ -45,7 +45,7 @@ where impl> Canonicalized { pub(super) fn decanonicalize_ty(&self, ty: Ty) -> Ty { crate::fold_free_vars(ty, |bound, _binders| { - let var = self.free_vars[bound.index]; + let var = self.free_vars[bound.index].clone(); var.assert_ty_ref(&Interner).clone() }) } @@ -76,7 +76,7 @@ impl> Canonicalized { for (i, ty) in solution.value.iter(&Interner).enumerate() { // FIXME: deal with non-type vars here -- the only problematic part is the normalization // and maybe we don't need that with lazy normalization? - let var = self.free_vars[i]; + let var = self.free_vars[i].clone(); // eagerly replace projections in the type; we may be getting types // e.g. from where clauses where this hasn't happened yet let ty = ctx.normalize_associated_types_in( @@ -218,16 +218,10 @@ impl<'a> InferenceTable<'a> { self.resolve_ty_as_possible_inner(&mut Vec::new(), ty) } + /// Unify two types and register new trait goals that arise from that. + // TODO give these two functions better names pub(crate) fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool { - let result = self.var_unification_table.relate( - &Interner, - &self.db, - &self.trait_env.env, - chalk_ir::Variance::Invariant, - ty1, - ty2, - ); - let result = if let Ok(r) = result { + let result = if let Ok(r) = self.unify_inner(ty1, ty2) { r } else { return false; @@ -236,6 +230,25 @@ impl<'a> InferenceTable<'a> { true } + /// Unify two types and return new trait goals arising from it, so the + /// caller needs to deal with them. + pub(crate) fn unify_inner(&mut self, ty1: &Ty, ty2: &Ty) -> InferResult { + match self.var_unification_table.relate( + &Interner, + &self.db, + &self.trait_env.env, + chalk_ir::Variance::Invariant, + ty1, + ty2, + ) { + Ok(result) => { + // TODO deal with new goals + Ok(InferOk {}) + } + Err(NoSolution) => Err(TypeError), + } + } + /// If `ty` is a type variable with known type, returns that type; /// otherwise, return ty. // FIXME this could probably just return Ty diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs index 15b61bedc18..179a27763bf 100644 --- a/crates/hir_ty/src/lib.rs +++ b/crates/hir_ty/src/lib.rs @@ -203,6 +203,17 @@ impl CallableSig { } } + pub fn to_fn_ptr(&self) -> FnPointer { + FnPointer { + num_binders: 0, + sig: FnSig { abi: (), safety: Safety::Safe, variadic: self.is_varargs }, + substitution: FnSubst(Substitution::from_iter( + &Interner, + self.params_and_return.iter().cloned(), + )), + } + } + pub fn params(&self) -> &[Ty] { &self.params_and_return[0..self.params_and_return.len() - 1] } From 4ca1981c9149fe602b548d1d3629c0cc312d30f7 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 2 May 2021 16:20:37 +0200 Subject: [PATCH 03/27] Fix warnings & format --- crates/hir_ty/src/builder.rs | 6 +++--- crates/hir_ty/src/infer/coerce.rs | 11 +++++------ crates/hir_ty/src/infer/unify.rs | 14 +++----------- 3 files changed, 11 insertions(+), 20 deletions(-) diff --git a/crates/hir_ty/src/builder.rs b/crates/hir_ty/src/builder.rs index 49d06954156..893e727c2e1 100644 --- a/crates/hir_ty/src/builder.rs +++ b/crates/hir_ty/src/builder.rs @@ -6,15 +6,15 @@ use chalk_ir::{ cast::{Cast, CastTo, Caster}, fold::Fold, interner::HasInterner, - AdtId, BoundVar, DebruijnIndex, Safety, Scalar, + AdtId, BoundVar, DebruijnIndex, Scalar, }; use hir_def::{builtin_type::BuiltinType, GenericDefId, TraitId, TypeAliasId}; use smallvec::SmallVec; use crate::{ db::HirDatabase, primitive, to_assoc_type_id, to_chalk_trait_id, utils::generics, Binders, - CallableSig, FnPointer, FnSig, FnSubst, GenericArg, Interner, ProjectionTy, Substitution, - TraitRef, Ty, TyDefId, TyExt, TyKind, ValueTyDefId, + CallableSig, GenericArg, Interner, ProjectionTy, Substitution, TraitRef, Ty, TyDefId, TyExt, + TyKind, ValueTyDefId, }; /// This is a builder for `Ty` or anything that needs a `Substitution`. diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs index 86a7cd4c24b..c82bda70f44 100644 --- a/crates/hir_ty/src/infer/coerce.rs +++ b/crates/hir_ty/src/infer/coerce.rs @@ -65,7 +65,7 @@ impl<'a> InferenceContext<'a> { } } - fn coerce_inner(&mut self, mut from_ty: Ty, to_ty: &Ty) -> InferResult { + fn coerce_inner(&mut self, from_ty: Ty, to_ty: &Ty) -> InferResult { if from_ty.is_never() { // Subtle: If we are coercing from `!` to `?T`, where `?T` is an unbound // type variable, we want `?T` to fallback to `!` if not @@ -145,10 +145,9 @@ impl<'a> InferenceContext<'a> { /// To match `A` with `B`, autoderef will be performed, /// calling `deref`/`deref_mut` where necessary. fn coerce_ref(&mut self, from_ty: Ty, to_ty: &Ty, to_mt: Mutability) -> InferResult { - let (from_mt, from_inner) = match from_ty.kind(&Interner) { - TyKind::Ref(mt, _, ty) => { + match from_ty.kind(&Interner) { + TyKind::Ref(mt, _, _) => { coerce_mutabilities(*mt, to_mt)?; - (*mt, ty.clone()) } _ => return self.unify_inner(&from_ty, to_ty), }; @@ -160,7 +159,7 @@ impl<'a> InferenceContext<'a> { // the structure like it is. let canonicalized = self.canonicalize(from_ty.clone()); - let mut autoderef = autoderef::autoderef( + let autoderef = autoderef::autoderef( self.db, self.resolver.krate(), InEnvironment { @@ -237,7 +236,7 @@ impl<'a> InferenceContext<'a> { /// or a function pointer. fn coerce_from_fn_item(&mut self, from_ty: Ty, to_ty: &Ty) -> InferResult { match to_ty.kind(&Interner) { - TyKind::Function(b_sig) => { + TyKind::Function(_) => { let from_sig = from_ty.callable_sig(self.db).expect("FnDef had no sig"); // FIXME check ABI: Intrinsics are not coercible to function pointers diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs index 3a4258e86cd..a635501b5b7 100644 --- a/crates/hir_ty/src/infer/unify.rs +++ b/crates/hir_ty/src/infer/unify.rs @@ -134,18 +134,10 @@ pub(super) struct TypeVariableTable { } impl TypeVariableTable { - fn push(&mut self, data: TypeVariableData) { - self.inner.push(data); - } - pub(super) fn set_diverging(&mut self, iv: InferenceVar, diverging: bool) { self.inner[iv.index() as usize].diverging = diverging; } - fn is_diverging(&mut self, iv: InferenceVar) -> bool { - self.inner[iv.index() as usize].diverging - } - fn fallback_value(&self, iv: InferenceVar, kind: TyVariableKind) -> Ty { match kind { _ if self.inner[iv.index() as usize].diverging => TyKind::Never, @@ -221,7 +213,7 @@ impl<'a> InferenceTable<'a> { /// Unify two types and register new trait goals that arise from that. // TODO give these two functions better names pub(crate) fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool { - let result = if let Ok(r) = self.unify_inner(ty1, ty2) { + let _result = if let Ok(r) = self.unify_inner(ty1, ty2) { r } else { return false; @@ -241,11 +233,11 @@ impl<'a> InferenceTable<'a> { ty1, ty2, ) { - Ok(result) => { + Ok(_result) => { // TODO deal with new goals Ok(InferOk {}) } - Err(NoSolution) => Err(TypeError), + Err(chalk_ir::NoSolution) => Err(TypeError), } } From 0f7f1f070584fbdc07a10df47b95b147698551fd Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 2 May 2021 16:32:42 +0200 Subject: [PATCH 04/27] Temporary fix for unknown expectations --- crates/hir_ty/src/infer.rs | 4 ++++ crates/hir_ty/src/infer/coerce.rs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs index 603068ab531..f3cccca6817 100644 --- a/crates/hir_ty/src/infer.rs +++ b/crates/hir_ty/src/infer.rs @@ -395,6 +395,10 @@ impl<'a> InferenceContext<'a> { } fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool { + // TODO handle expectations properly + if ty2.is_unknown() { + return true; + } self.table.unify(ty1, ty2) } diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs index c82bda70f44..8467ea056dd 100644 --- a/crates/hir_ty/src/infer/coerce.rs +++ b/crates/hir_ty/src/infer/coerce.rs @@ -19,6 +19,10 @@ impl<'a> InferenceContext<'a> { /// Unify two types, but may coerce the first one to the second one /// using "implicit coercion rules" if needed. pub(super) fn coerce(&mut self, from_ty: &Ty, to_ty: &Ty) -> bool { + // TODO handle expectations properly + if to_ty.is_unknown() { + return true; + } let from_ty = self.resolve_ty_shallow(from_ty).into_owned(); let to_ty = self.resolve_ty_shallow(to_ty); match self.coerce_inner(from_ty, &to_ty) { From aebcf7b5d4a37a08f2a64f7660b7e3d890476dba Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 9 May 2021 20:05:43 +0200 Subject: [PATCH 05/27] Better Debug impl for InternedWrapper --- crates/hir_ty/src/interner.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/hir_ty/src/interner.rs b/crates/hir_ty/src/interner.rs index 7b41197477a..8e77378abe5 100644 --- a/crates/hir_ty/src/interner.rs +++ b/crates/hir_ty/src/interner.rs @@ -15,9 +15,15 @@ use std::{fmt, sync::Arc}; #[derive(Debug, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)] pub struct Interner; -#[derive(PartialEq, Eq, Hash, Debug)] +#[derive(PartialEq, Eq, Hash)] pub struct InternedWrapper(T); +impl fmt::Debug for InternedWrapper { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.0, f) + } +} + impl std::ops::Deref for InternedWrapper { type Target = T; From 278f5b043d3cde9ed5b6dfef708ed29179fd534c Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 9 May 2021 20:06:24 +0200 Subject: [PATCH 06/27] Fix fallback to bound vars in `unify` --- crates/hir_ty/src/infer/unify.rs | 123 ++++++++++++++++++++----------- 1 file changed, 78 insertions(+), 45 deletions(-) diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs index a635501b5b7..00066975fc3 100644 --- a/crates/hir_ty/src/infer/unify.rs +++ b/crates/hir_ty/src/infer/unify.rs @@ -4,7 +4,6 @@ use std::{borrow::Cow, fmt, sync::Arc}; use chalk_ir::{ cast::Cast, fold::Fold, interner::HasInterner, FloatTy, IntTy, TyVariableKind, UniverseIndex, - VariableKind, }; use chalk_solve::infer::ParameterEnaVariableExt; use ena::unify::UnifyKey; @@ -12,7 +11,7 @@ use ena::unify::UnifyKey; use super::{InferOk, InferResult, InferenceContext, TypeError}; use crate::{ db::HirDatabase, fold_tys, static_lifetime, BoundVar, Canonical, DebruijnIndex, GenericArg, - InferenceVar, Interner, Scalar, Substitution, TraitEnvironment, Ty, TyKind, + InferenceVar, Interner, Scalar, Substitution, TraitEnvironment, Ty, TyKind, VariableKind, }; impl<'a> InferenceContext<'a> { @@ -112,19 +111,28 @@ pub(crate) fn unify( } // default any type vars that weren't unified back to their original bound vars // (kind of hacky) - for (i, var) in vars.iter(&Interner).enumerate() { - let var = var.assert_ty_ref(&Interner); - if &*table.resolve_ty_shallow(var) == var { - table.unify( - var, - &TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, i)).intern(&Interner), - ); - } - } + let find_var = |iv| { + vars.iter(&Interner).position(|v| match v.interned() { + chalk_ir::GenericArgData::Ty(ty) => ty.inference_var(&Interner), + chalk_ir::GenericArgData::Lifetime(lt) => lt.inference_var(&Interner), + chalk_ir::GenericArgData::Const(c) => c.inference_var(&Interner), + } == Some(iv)) + }; + let fallback = |iv, kind, default| match kind { + chalk_ir::VariableKind::Ty(_ty_kind) => find_var(iv).map_or(default, |i| { + BoundVar::new(DebruijnIndex::INNERMOST, i).to_ty(&Interner).cast(&Interner) + }), + chalk_ir::VariableKind::Lifetime => find_var(iv).map_or(default, |i| { + BoundVar::new(DebruijnIndex::INNERMOST, i).to_lifetime(&Interner).cast(&Interner) + }), + chalk_ir::VariableKind::Const(ty) => find_var(iv).map_or(default, |i| { + BoundVar::new(DebruijnIndex::INNERMOST, i).to_const(&Interner, ty).cast(&Interner) + }), + }; Some(Substitution::from_iter( &Interner, vars.iter(&Interner) - .map(|v| table.resolve_ty_completely(v.assert_ty_ref(&Interner).clone())), + .map(|v| table.resolve_with_fallback(v.assert_ty_ref(&Interner).clone(), fallback)), )) } @@ -201,8 +209,65 @@ impl<'a> InferenceTable<'a> { self.new_var(TyVariableKind::General, true) } + pub(crate) fn resolve_with_fallback( + &mut self, + t: T, + fallback: impl Fn(InferenceVar, VariableKind, GenericArg) -> GenericArg, + ) -> T::Result + where + T: HasInterner + Fold, + { + self.resolve_with_fallback_inner(&mut Vec::new(), t, &fallback) + } + + fn resolve_with_fallback_inner( + &mut self, + var_stack: &mut Vec, + t: T, + fallback: &impl Fn(InferenceVar, VariableKind, GenericArg) -> GenericArg, + ) -> T::Result + where + T: HasInterner + Fold, + { + fold_tys( + t, + |ty, _| match ty.kind(&Interner) { + &TyKind::InferenceVar(tv, kind) => { + if var_stack.contains(&tv) { + cov_mark::hit!(type_var_cycles_resolve_as_possible); + // recursive type + let default = + self.type_variable_table.fallback_value(tv, kind).cast(&Interner); + return fallback(tv, VariableKind::Ty(kind), default) + .assert_ty_ref(&Interner) + .clone(); + } + if let Some(known_ty) = self.var_unification_table.probe_var(tv) { + // known_ty may contain other variables that are known by now + var_stack.push(tv); + let result = self.resolve_with_fallback_inner( + var_stack, + known_ty.assert_ty_ref(&Interner).clone(), + fallback, + ); + var_stack.pop(); + result + } else { + let default = + self.type_variable_table.fallback_value(tv, kind).cast(&Interner); + fallback(tv, VariableKind::Ty(kind), default) + .assert_ty_ref(&Interner) + .clone() + } + } + _ => ty, + }, + DebruijnIndex::INNERMOST, + ) + } + pub(crate) fn resolve_ty_completely(&mut self, ty: Ty) -> Ty { - self.resolve_ty_completely_inner(&mut Vec::new(), ty) + self.resolve_with_fallback(ty, |_, _, d| d) } // FIXME get rid of this, instead resolve shallowly where necessary @@ -282,38 +347,6 @@ impl<'a> InferenceTable<'a> { DebruijnIndex::INNERMOST, ) } - - /// Resolves the type completely; type variables without known type are - /// replaced by TyKind::Unknown. - fn resolve_ty_completely_inner(&mut self, tv_stack: &mut Vec, ty: Ty) -> Ty { - // FIXME implement as a proper Folder, handle lifetimes and consts as well - fold_tys( - ty, - |ty, _| match ty.kind(&Interner) { - &TyKind::InferenceVar(tv, kind) => { - if tv_stack.contains(&tv) { - cov_mark::hit!(type_var_cycles_resolve_completely); - // recursive type - return self.type_variable_table.fallback_value(tv, kind); - } - if let Some(known_ty) = self.var_unification_table.probe_var(tv) { - // known_ty may contain other variables that are known by now - tv_stack.push(tv); - let result = self.resolve_ty_completely_inner( - tv_stack, - known_ty.assert_ty_ref(&Interner).clone(), - ); - tv_stack.pop(); - result - } else { - self.type_variable_table.fallback_value(tv, kind) - } - } - _ => ty, - }, - DebruijnIndex::INNERMOST, - ) - } } impl<'a> fmt::Debug for InferenceTable<'a> { From 3379a52dffc2d0951892edd93233b8069395de61 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Thu, 13 May 2021 19:44:29 +0200 Subject: [PATCH 07/27] Make type resolver a proper folder, make it resolve consts as well --- crates/hir_ty/src/infer/unify.rs | 165 ++++++++++++++++++++++--------- 1 file changed, 117 insertions(+), 48 deletions(-) diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs index 00066975fc3..896d084f468 100644 --- a/crates/hir_ty/src/infer/unify.rs +++ b/crates/hir_ty/src/infer/unify.rs @@ -118,16 +118,13 @@ pub(crate) fn unify( chalk_ir::GenericArgData::Const(c) => c.inference_var(&Interner), } == Some(iv)) }; - let fallback = |iv, kind, default| match kind { - chalk_ir::VariableKind::Ty(_ty_kind) => find_var(iv).map_or(default, |i| { - BoundVar::new(DebruijnIndex::INNERMOST, i).to_ty(&Interner).cast(&Interner) - }), - chalk_ir::VariableKind::Lifetime => find_var(iv).map_or(default, |i| { - BoundVar::new(DebruijnIndex::INNERMOST, i).to_lifetime(&Interner).cast(&Interner) - }), - chalk_ir::VariableKind::Const(ty) => find_var(iv).map_or(default, |i| { - BoundVar::new(DebruijnIndex::INNERMOST, i).to_const(&Interner, ty).cast(&Interner) - }), + let fallback = |iv, kind, default, binder| match kind { + chalk_ir::VariableKind::Ty(_ty_kind) => find_var(iv) + .map_or(default, |i| BoundVar::new(binder, i).to_ty(&Interner).cast(&Interner)), + chalk_ir::VariableKind::Lifetime => find_var(iv) + .map_or(default, |i| BoundVar::new(binder, i).to_lifetime(&Interner).cast(&Interner)), + chalk_ir::VariableKind::Const(ty) => find_var(iv) + .map_or(default, |i| BoundVar::new(binder, i).to_const(&Interner, ty).cast(&Interner)), }; Some(Substitution::from_iter( &Interner, @@ -212,7 +209,7 @@ impl<'a> InferenceTable<'a> { pub(crate) fn resolve_with_fallback( &mut self, t: T, - fallback: impl Fn(InferenceVar, VariableKind, GenericArg) -> GenericArg, + fallback: impl Fn(InferenceVar, VariableKind, GenericArg, DebruijnIndex) -> GenericArg, ) -> T::Result where T: HasInterner + Fold, @@ -224,50 +221,25 @@ impl<'a> InferenceTable<'a> { &mut self, var_stack: &mut Vec, t: T, - fallback: &impl Fn(InferenceVar, VariableKind, GenericArg) -> GenericArg, + fallback: &impl Fn(InferenceVar, VariableKind, GenericArg, DebruijnIndex) -> GenericArg, ) -> T::Result where T: HasInterner + Fold, { - fold_tys( - t, - |ty, _| match ty.kind(&Interner) { - &TyKind::InferenceVar(tv, kind) => { - if var_stack.contains(&tv) { - cov_mark::hit!(type_var_cycles_resolve_as_possible); - // recursive type - let default = - self.type_variable_table.fallback_value(tv, kind).cast(&Interner); - return fallback(tv, VariableKind::Ty(kind), default) - .assert_ty_ref(&Interner) - .clone(); - } - if let Some(known_ty) = self.var_unification_table.probe_var(tv) { - // known_ty may contain other variables that are known by now - var_stack.push(tv); - let result = self.resolve_with_fallback_inner( - var_stack, - known_ty.assert_ty_ref(&Interner).clone(), - fallback, - ); - var_stack.pop(); - result - } else { - let default = - self.type_variable_table.fallback_value(tv, kind).cast(&Interner); - fallback(tv, VariableKind::Ty(kind), default) - .assert_ty_ref(&Interner) - .clone() - } - } - _ => ty, + t.fold_with( + &mut resolve::Resolver { + type_variable_table: &self.type_variable_table, + var_unification_table: &mut self.var_unification_table, + var_stack, + fallback, }, DebruijnIndex::INNERMOST, ) + .expect("fold failed unexpectedly") } pub(crate) fn resolve_ty_completely(&mut self, ty: Ty) -> Ty { - self.resolve_with_fallback(ty, |_, _, d| d) + self.resolve_with_fallback(ty, |_, _, d, _| d) } // FIXME get rid of this, instead resolve shallowly where necessary @@ -316,9 +288,7 @@ impl<'a> InferenceTable<'a> { } /// Resolves the type as far as currently possible, replacing type variables - /// by their known types. All types returned by the infer_* functions should - /// be resolved as far as possible, i.e. contain no type variables with - /// known type. + /// by their known types. fn resolve_ty_as_possible_inner(&mut self, tv_stack: &mut Vec, ty: Ty) -> Ty { fold_tys( ty, @@ -356,3 +326,102 @@ impl<'a> fmt::Debug for InferenceTable<'a> { .finish() } } + +mod resolve { + use super::{ChalkInferenceTable, TypeVariableTable}; + use crate::{ + ConcreteConst, Const, ConstData, ConstValue, DebruijnIndex, GenericArg, InferenceVar, + Interner, Ty, TyVariableKind, VariableKind, + }; + use chalk_ir::{ + cast::Cast, + fold::{Fold, Folder}, + Fallible, + }; + use hir_def::type_ref::ConstScalar; + + pub(super) struct Resolver<'a, F> { + pub type_variable_table: &'a TypeVariableTable, + pub var_unification_table: &'a mut ChalkInferenceTable, + pub var_stack: &'a mut Vec, + pub fallback: F, + } + impl<'a, 'i, F> Folder<'i, Interner> for Resolver<'a, F> + where + F: Fn(InferenceVar, VariableKind, GenericArg, DebruijnIndex) -> GenericArg + 'i, + { + fn as_dyn(&mut self) -> &mut dyn Folder<'i, Interner> { + self + } + + fn interner(&self) -> &'i Interner { + &Interner + } + + fn fold_inference_ty( + &mut self, + var: InferenceVar, + kind: TyVariableKind, + outer_binder: DebruijnIndex, + ) -> Fallible { + let var = self.var_unification_table.inference_var_root(var); + if self.var_stack.contains(&var) { + cov_mark::hit!(type_var_cycles_resolve_as_possible); + // recursive type + let default = self.type_variable_table.fallback_value(var, kind).cast(&Interner); + return Ok((self.fallback)(var, VariableKind::Ty(kind), default, outer_binder) + .assert_ty_ref(&Interner) + .clone()); + } + let result = if let Some(known_ty) = self.var_unification_table.probe_var(var) { + // known_ty may contain other variables that are known by now + self.var_stack.push(var); + let result = + known_ty.fold_with(self, outer_binder).expect("fold failed unexpectedly"); + self.var_stack.pop(); + result.assert_ty_ref(&Interner).clone() + } else { + let default = self.type_variable_table.fallback_value(var, kind).cast(&Interner); + (self.fallback)(var, VariableKind::Ty(kind), default, outer_binder) + .assert_ty_ref(&Interner) + .clone() + }; + Ok(result) + } + + fn fold_inference_const( + &mut self, + ty: Ty, + var: InferenceVar, + outer_binder: DebruijnIndex, + ) -> Fallible { + let var = self.var_unification_table.inference_var_root(var); + let default = ConstData { + ty: ty.clone(), + value: ConstValue::Concrete(ConcreteConst { interned: ConstScalar::Unknown }), + } + .intern(&Interner) + .cast(&Interner); + if self.var_stack.contains(&var) { + cov_mark::hit!(type_var_cycles_resolve_as_possible); + // recursive + return Ok((self.fallback)(var, VariableKind::Const(ty), default, outer_binder) + .assert_const_ref(&Interner) + .clone()); + } + let result = if let Some(known_ty) = self.var_unification_table.probe_var(var) { + // known_ty may contain other variables that are known by now + self.var_stack.push(var); + let result = + known_ty.fold_with(self, outer_binder).expect("fold failed unexpectedly"); + self.var_stack.pop(); + result.assert_const_ref(&Interner).clone() + } else { + (self.fallback)(var, VariableKind::Const(ty), default, outer_binder) + .assert_const_ref(&Interner) + .clone() + }; + Ok(result) + } + } +} From afa6be243587e523d5a2fc218db78568041ff296 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Thu, 13 May 2021 20:31:06 +0200 Subject: [PATCH 08/27] Update tests with expected changes --- crates/hir_ty/src/infer/unify.rs | 3 -- crates/hir_ty/src/tests/regression.rs | 71 +++++++++++++-------------- 2 files changed, 34 insertions(+), 40 deletions(-) diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs index 896d084f468..e7718aa2289 100644 --- a/crates/hir_ty/src/infer/unify.rs +++ b/crates/hir_ty/src/infer/unify.rs @@ -295,7 +295,6 @@ impl<'a> InferenceTable<'a> { |ty, _| match ty.kind(&Interner) { &TyKind::InferenceVar(tv, kind) => { if tv_stack.contains(&tv) { - cov_mark::hit!(type_var_cycles_resolve_as_possible); // recursive type return self.type_variable_table.fallback_value(tv, kind); } @@ -366,7 +365,6 @@ mod resolve { ) -> Fallible { let var = self.var_unification_table.inference_var_root(var); if self.var_stack.contains(&var) { - cov_mark::hit!(type_var_cycles_resolve_as_possible); // recursive type let default = self.type_variable_table.fallback_value(var, kind).cast(&Interner); return Ok((self.fallback)(var, VariableKind::Ty(kind), default, outer_binder) @@ -403,7 +401,6 @@ mod resolve { .intern(&Interner) .cast(&Interner); if self.var_stack.contains(&var) { - cov_mark::hit!(type_var_cycles_resolve_as_possible); // recursive return Ok((self.fallback)(var, VariableKind::Const(ty), default, outer_binder) .assert_const_ref(&Interner) diff --git a/crates/hir_ty/src/tests/regression.rs b/crates/hir_ty/src/tests/regression.rs index 4318617124f..59a16f3906c 100644 --- a/crates/hir_ty/src/tests/regression.rs +++ b/crates/hir_ty/src/tests/regression.rs @@ -86,8 +86,6 @@ fn bug_651() { #[test] fn recursive_vars() { - cov_mark::check!(type_var_cycles_resolve_completely); - cov_mark::check!(type_var_cycles_resolve_as_possible); check_infer( r#" fn test() { @@ -97,12 +95,12 @@ fn recursive_vars() { "#, expect![[r#" 10..47 '{ ...&y]; }': () - 20..21 'y': &{unknown} - 24..31 'unknown': &{unknown} - 37..44 '[y, &y]': [&&{unknown}; 2] - 38..39 'y': &{unknown} - 41..43 '&y': &&{unknown} - 42..43 'y': &{unknown} + 20..21 'y': {unknown} + 24..31 'unknown': {unknown} + 37..44 '[y, &y]': [{unknown}; 2] + 38..39 'y': {unknown} + 41..43 '&y': &{unknown} + 42..43 'y': {unknown} "#]], ); } @@ -119,19 +117,19 @@ fn recursive_vars_2() { "#, expect![[r#" 10..79 '{ ...x)]; }': () - 20..21 'x': &&{unknown} - 24..31 'unknown': &&{unknown} - 41..42 'y': &&{unknown} - 45..52 'unknown': &&{unknown} - 58..76 '[(x, y..., &x)]': [(&&&{unknown}, &&&{unknown}); 2] - 59..65 '(x, y)': (&&&{unknown}, &&&{unknown}) - 60..61 'x': &&{unknown} - 63..64 'y': &&{unknown} - 67..75 '(&y, &x)': (&&&{unknown}, &&&{unknown}) - 68..70 '&y': &&&{unknown} - 69..70 'y': &&{unknown} - 72..74 '&x': &&&{unknown} - 73..74 'x': &&{unknown} + 20..21 'x': {unknown} + 24..31 'unknown': {unknown} + 41..42 'y': {unknown} + 45..52 'unknown': {unknown} + 58..76 '[(x, y..., &x)]': [({unknown}, {unknown}); 2] + 59..65 '(x, y)': ({unknown}, {unknown}) + 60..61 'x': {unknown} + 63..64 'y': {unknown} + 67..75 '(&y, &x)': (&{unknown}, &{unknown}) + 68..70 '&y': &{unknown} + 69..70 'y': {unknown} + 72..74 '&x': &{unknown} + 73..74 'x': {unknown} "#]], ); } @@ -165,7 +163,6 @@ fn infer_std_crash_1() { #[test] fn infer_std_crash_2() { - cov_mark::check!(type_var_resolves_to_int_var); // caused "equating two type variables, ...", taken from std check_infer( r#" @@ -257,27 +254,27 @@ fn infer_std_crash_5() { expect![[r#" 26..322 '{ ... } }': () 32..320 'for co... }': () - 36..43 'content': &{unknown} + 36..43 'content': {unknown} 47..60 'doesnt_matter': {unknown} 61..320 '{ ... }': () - 75..79 'name': &&{unknown} - 82..166 'if doe... }': &&{unknown} + 75..79 'name': &{unknown} + 82..166 'if doe... }': &{unknown} 85..98 'doesnt_matter': bool - 99..128 '{ ... }': &&{unknown} - 113..118 'first': &&{unknown} - 134..166 '{ ... }': &&{unknown} - 148..156 '&content': &&{unknown} - 149..156 'content': &{unknown} + 99..128 '{ ... }': &{unknown} + 113..118 'first': &{unknown} + 134..166 '{ ... }': &{unknown} + 148..156 '&content': &{unknown} + 149..156 'content': {unknown} 181..188 'content': &{unknown} 191..313 'if ICE... }': &{unknown} 194..231 'ICE_RE..._VALUE': {unknown} 194..247 'ICE_RE...&name)': bool - 241..246 '&name': &&&{unknown} - 242..246 'name': &&{unknown} - 248..276 '{ ... }': &&{unknown} - 262..266 'name': &&{unknown} - 282..313 '{ ... }': &{unknown} - 296..303 'content': &{unknown} + 241..246 '&name': &&{unknown} + 242..246 'name': &{unknown} + 248..276 '{ ... }': &{unknown} + 262..266 'name': &{unknown} + 282..313 '{ ... }': {unknown} + 296..303 'content': {unknown} "#]], ); } @@ -761,7 +758,7 @@ fn issue_4885() { "#, expect![[r#" 136..139 'key': &K - 198..214 '{ ...key) }': impl Future>::Bar> + 198..214 '{ ...key) }': {unknown} 204..207 'bar': fn bar(&K) -> impl Future>::Bar> 204..212 'bar(key)': impl Future>::Bar> 208..211 'key': &K From a09079f27aa631b011f6c0703200862d28af81f4 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 15 May 2021 16:00:24 +0200 Subject: [PATCH 09/27] Fix coercion of two closures to a function pointer Fixes #8604. --- crates/hir_ty/src/infer/coerce.rs | 50 ++++++++++++++++++----------- crates/hir_ty/src/lib.rs | 1 + crates/hir_ty/src/tests/coercion.rs | 17 +++++++++- crates/hir_ty/src/tests/simple.rs | 36 --------------------- 4 files changed, 48 insertions(+), 56 deletions(-) diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs index 8467ea056dd..27f59c8bb8c 100644 --- a/crates/hir_ty/src/infer/coerce.rs +++ b/crates/hir_ty/src/infer/coerce.rs @@ -40,32 +40,45 @@ impl<'a> InferenceContext<'a> { /// Merge two types from different branches, with possible coercion. /// /// Mostly this means trying to coerce one to the other, but - /// - if we have two function types for different functions, we need to + /// - if we have two function types for different functions or closures, we need to /// coerce both to function pointers; /// - if we were concerned with lifetime subtyping, we'd need to look for a /// least upper bound. pub(super) fn coerce_merge_branch(&mut self, ty1: &Ty, ty2: &Ty) -> Ty { + // Special case: two function types. Try to coerce both to + // pointers to have a chance at getting a match. See + // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916 + let sig = match (ty1.kind(&Interner), ty2.kind(&Interner)) { + (TyKind::FnDef(..), TyKind::FnDef(..)) + | (TyKind::Closure(..), TyKind::FnDef(..)) + | (TyKind::FnDef(..), TyKind::Closure(..)) + | (TyKind::Closure(..), TyKind::Closure(..)) => { + // FIXME: we're ignoring safety here. To be more correct, if we have one FnDef and one Closure, + // we should be coercing the closure to a fn pointer of the safety of the FnDef + cov_mark::hit!(coerce_fn_reification); + let sig = ty1.callable_sig(self.db).expect("FnDef without callable sig"); + Some(sig) + } + _ => None, + }; + if let Some(sig) = sig { + let target_ty = TyKind::Function(sig.to_fn_ptr()).intern(&Interner); + let result1 = self.coerce_inner(ty1.clone(), &target_ty); + let result2 = self.coerce_inner(ty2.clone(), &target_ty); + if let (Ok(_result1), Ok(_result2)) = (result1, result2) { + // TODO deal with the goals + return target_ty; + } + } + if self.coerce(ty1, ty2) { ty2.clone() } else if self.coerce(ty2, ty1) { ty1.clone() } else { - if let (TyKind::FnDef(..), TyKind::FnDef(..)) = - (ty1.kind(&Interner), ty2.kind(&Interner)) - { - cov_mark::hit!(coerce_fn_reification); - // Special case: two function types. Try to coerce both to - // pointers to have a chance at getting a match. See - // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916 - let sig1 = ty1.callable_sig(self.db).expect("FnDef without callable sig"); - let sig2 = ty2.callable_sig(self.db).expect("FnDef without callable sig"); - let ptr_ty1 = TyBuilder::fn_ptr(sig1); - let ptr_ty2 = TyBuilder::fn_ptr(sig2); - self.coerce_merge_branch(&ptr_ty1, &ptr_ty2) - } else { - cov_mark::hit!(coerce_merge_fail_fallback); - ty1.clone() - } + // FIXME record a type mismatch + cov_mark::hit!(coerce_merge_fail_fallback); + ty1.clone() } } @@ -236,8 +249,7 @@ impl<'a> InferenceContext<'a> { Ok(result) } - /// Attempts to coerce from the type of a Rust function item into a closure - /// or a function pointer. + /// Attempts to coerce from the type of a Rust function item into a function pointer. fn coerce_from_fn_item(&mut self, from_ty: Ty, to_ty: &Ty) -> InferResult { match to_ty.kind(&Interner) { TyKind::Function(_) => { diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs index 179a27763bf..06d5cd0b6af 100644 --- a/crates/hir_ty/src/lib.rs +++ b/crates/hir_ty/src/lib.rs @@ -167,6 +167,7 @@ pub fn make_canonical>( Canonical { value, binders: chalk_ir::CanonicalVarKinds::from_iter(&Interner, kinds) } } +// FIXME: get rid of this, just replace it by FnPointer /// A function signature as seen by type inference: Several parameter types and /// one return type. #[derive(Clone, PartialEq, Eq, Debug)] diff --git a/crates/hir_ty/src/tests/coercion.rs b/crates/hir_ty/src/tests/coercion.rs index 19047106912..67295b663b3 100644 --- a/crates/hir_ty/src/tests/coercion.rs +++ b/crates/hir_ty/src/tests/coercion.rs @@ -1,6 +1,6 @@ use expect_test::expect; -use super::{check_infer, check_infer_with_mismatches}; +use super::{check_infer, check_infer_with_mismatches, check_types}; #[test] fn infer_block_expr_type_mismatch() { @@ -858,3 +858,18 @@ fn coerce_unsize_generic() { "]], ); } + +#[test] +fn infer_two_closures_lub() { + check_types( + r#" +fn foo(c: i32) { + let add = |a: i32, b: i32| a + b; + let sub = |a, b| a - b; + //^ |i32, i32| -> i32 + if c > 42 { add } else { sub }; + //^ fn(i32, i32) -> i32 +} + "#, + ) +} diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs index a9cd42186ed..5c70a1fc01c 100644 --- a/crates/hir_ty/src/tests/simple.rs +++ b/crates/hir_ty/src/tests/simple.rs @@ -1039,42 +1039,6 @@ fn infer_in_elseif() { ) } -#[test] -fn infer_closure_unify() { - check_infer( - r#" - fn foo(f: bool) { - let a = |x| x; - let b = |x| x; - let id = if f { a } else { b }; - id(123); - } - "#, - expect![[r#" - 7..8 'f': bool - 16..106 '{ ...23); }': () - 26..27 'a': |i32| -> i32 - 30..35 '|x| x': |i32| -> i32 - 31..32 'x': i32 - 34..35 'x': i32 - 45..46 'b': |i32| -> i32 - 49..54 '|x| x': |i32| -> i32 - 50..51 'x': i32 - 53..54 'x': i32 - 64..66 'id': |i32| -> i32 - 69..90 'if f {... { b }': |i32| -> i32 - 72..73 'f': bool - 74..79 '{ a }': |i32| -> i32 - 76..77 'a': |i32| -> i32 - 85..90 '{ b }': |i32| -> i32 - 87..88 'b': |i32| -> i32 - 96..98 'id': |i32| -> i32 - 96..103 'id(123)': i32 - 99..102 '123': i32 - "#]], - ) -} - #[test] fn infer_if_match_with_return() { check_infer( From 32fc944263ae0b30eba130fbcf28f4eb5578fdb3 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 15 May 2021 17:01:27 +0200 Subject: [PATCH 10/27] Fix handling of diverging branches in match coercion Fixes #7626. --- crates/hir_ty/src/infer/coerce.rs | 17 ++++++++++--- crates/hir_ty/src/tests/coercion.rs | 39 +++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs index 27f59c8bb8c..4d80b4a082c 100644 --- a/crates/hir_ty/src/infer/coerce.rs +++ b/crates/hir_ty/src/infer/coerce.rs @@ -45,6 +45,10 @@ impl<'a> InferenceContext<'a> { /// - if we were concerned with lifetime subtyping, we'd need to look for a /// least upper bound. pub(super) fn coerce_merge_branch(&mut self, ty1: &Ty, ty2: &Ty) -> Ty { + let ty1 = self.resolve_ty_shallow(ty1); + let ty1 = ty1.as_ref(); + let ty2 = self.resolve_ty_shallow(ty2); + let ty2 = ty2.as_ref(); // Special case: two function types. Try to coerce both to // pointers to have a chance at getting a match. See // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916 @@ -71,12 +75,17 @@ impl<'a> InferenceContext<'a> { } } - if self.coerce(ty1, ty2) { - ty2.clone() - } else if self.coerce(ty2, ty1) { + // It might not seem like it, but order is important here: ty1 is our + // "previous" type, ty2 is the "new" one being added. If the previous + // type is a type variable and the new one is `!`, trying it the other + // way around first would mean we make the type variable `!`, instead of + // just marking it as possibly diverging. + if self.coerce(ty2, ty1) { ty1.clone() + } else if self.coerce(ty1, ty2) { + ty2.clone() } else { - // FIXME record a type mismatch + // TODO record a type mismatch cov_mark::hit!(coerce_merge_fail_fallback); ty1.clone() } diff --git a/crates/hir_ty/src/tests/coercion.rs b/crates/hir_ty/src/tests/coercion.rs index 67295b663b3..bb568ea372e 100644 --- a/crates/hir_ty/src/tests/coercion.rs +++ b/crates/hir_ty/src/tests/coercion.rs @@ -873,3 +873,42 @@ fn foo(c: i32) { "#, ) } + +#[test] +fn infer_match_diverging_branch_1() { + check_types( + r#" +enum Result { Ok(T), Err } +fn parse() -> T { loop {} } + +fn test() -> i32 { + let a = match parse() { + Ok(val) => val, + Err => return 0, + }; + a + //^ i32 +} + "#, + ) +} + +#[test] +fn infer_match_diverging_branch_2() { + // same as 1 except for order of branches + check_types( + r#" +enum Result { Ok(T), Err } +fn parse() -> T { loop {} } + +fn test() -> i32 { + let a = match parse() { + Err => return 0, + Ok(val) => val, + }; + a + //^ i32 +} + "#, + ) +} From 212f0477f29ec27a3981a916de432fc9ef872ff3 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 15 May 2021 19:28:58 +0200 Subject: [PATCH 11/27] Make diverging type variables work again Chalk doesn't know about the `diverging` flag, so we need to instead propagate it before fully resolving the variables. --- crates/hir_ty/src/infer.rs | 3 +++ crates/hir_ty/src/infer/unify.rs | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs index f3cccca6817..7898740be2a 100644 --- a/crates/hir_ty/src/infer.rs +++ b/crates/hir_ty/src/infer.rs @@ -284,6 +284,9 @@ impl<'a> InferenceContext<'a> { fn resolve_all(mut self) -> InferenceResult { // FIXME resolve obligations as well (use Guidance if necessary) + + // make sure diverging type variables are marked as such + self.table.propagate_diverging_flag(); let mut result = std::mem::take(&mut self.result); for ty in result.type_of_expr.values_mut() { let resolved = self.table.resolve_ty_completely(ty.clone()); diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs index e7718aa2289..56e06b7d0a4 100644 --- a/crates/hir_ty/src/infer/unify.rs +++ b/crates/hir_ty/src/infer/unify.rs @@ -179,8 +179,28 @@ impl<'a> InferenceTable<'a> { } } + /// Chalk doesn't know about the `diverging` flag, so when it unifies two + /// type variables of which one is diverging, the chosen root might not be + /// diverging and we have no way of marking it as such at that time. This + /// function goes through all type variables and make sure their root is + /// marked as diverging if necessary, so that resolving them gives the right + /// result. + pub(super) fn propagate_diverging_flag(&mut self) { + for i in 0..self.type_variable_table.inner.len() { + if !self.type_variable_table.inner[i].diverging { + continue; + } + let v = InferenceVar::from(i as u32); + let root = self.var_unification_table.inference_var_root(v); + if let Some(data) = self.type_variable_table.inner.get_mut(root.index() as usize) { + data.diverging = true; + } + } + } + fn new_var(&mut self, kind: TyVariableKind, diverging: bool) -> Ty { let var = self.var_unification_table.new_variable(UniverseIndex::ROOT); + // Chalk might have created some type variables for its own purposes that we don't know about... self.type_variable_table.inner.extend( (0..1 + var.index() as usize - self.type_variable_table.inner.len()) .map(|_| TypeVariableData { diverging: false }), From b939a6dcacc0460f49781e922c979d235a52eeca Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 15 May 2021 19:35:12 +0200 Subject: [PATCH 12/27] Fix if/else coercion --- crates/hir_ty/src/infer/expr.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index aab4d31534c..f439169ea4a 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs @@ -131,17 +131,20 @@ impl<'a> InferenceContext<'a> { let condition_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); let mut both_arms_diverge = Diverges::Always; + let mut result_ty = self.table.new_type_var(); let then_ty = self.infer_expr_inner(*then_branch, &expected); both_arms_diverge &= mem::replace(&mut self.diverges, Diverges::Maybe); + result_ty = self.coerce_merge_branch(&result_ty, &then_ty); let else_ty = match else_branch { Some(else_branch) => self.infer_expr_inner(*else_branch, &expected), None => TyBuilder::unit(), }; both_arms_diverge &= self.diverges; + result_ty = self.coerce_merge_branch(&result_ty, &else_ty); self.diverges = condition_diverges | both_arms_diverge; - self.coerce_merge_branch(&then_ty, &else_ty) + result_ty } Expr::Block { statements, tail, label, id: _ } => { let old_resolver = mem::replace( From 48c492af7ec8ff6af4fe2b38f83aa007c9f7f0b8 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 15 May 2021 20:18:54 +0200 Subject: [PATCH 13/27] Fix compilation of hir and ide crates --- crates/hir/src/lib.rs | 4 ++-- crates/ide_completion/src/render.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index d443b124c41..21b1a8e4a5e 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -2223,8 +2223,8 @@ impl Type { walk_type(db, self, &mut cb); } - pub fn could_unify_with(&self, other: &Type) -> bool { - could_unify(&self.ty, &other.ty) + pub fn could_unify_with(&self, db: &dyn HirDatabase, other: &Type) -> bool { + could_unify(db, self.env.clone(), &self.ty, &other.ty) } } diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs index 1a762d3dc1f..6b04ee16487 100644 --- a/crates/ide_completion/src/render.rs +++ b/crates/ide_completion/src/render.rs @@ -323,7 +323,7 @@ fn compute_type_match( if completion_ty == expected_type { Some(CompletionRelevanceTypeMatch::Exact) - } else if expected_type.could_unify_with(completion_ty) { + } else if expected_type.could_unify_with(ctx.db, completion_ty) { Some(CompletionRelevanceTypeMatch::CouldUnify) } else { None From 8397734cfe26793d3e9f9ec5f8392655a4b8e106 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 15 May 2021 20:28:07 +0200 Subject: [PATCH 14/27] Fix HIR expecting errors to unify with anything --- crates/hir/src/lib.rs | 9 ++--- crates/hir_ty/src/infer/unify.rs | 8 +++-- crates/hir_ty/src/lib.rs | 56 ++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 8 deletions(-) diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 21b1a8e4a5e..1429384cba4 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -2051,11 +2051,7 @@ impl Type { name: Option<&Name>, mut callback: impl FnMut(&Ty, AssocItem) -> Option, ) -> Option { - // There should be no inference vars in types passed here - // FIXME check that? - // FIXME replace Unknown by bound vars here - let canonical = - Canonical { value: self.ty.clone(), binders: CanonicalVarKinds::empty(&Interner) }; + let canonical = hir_ty::replace_errors_with_variables(self.ty.clone()); let env = self.env.clone(); let krate = krate.id; @@ -2224,7 +2220,8 @@ impl Type { } pub fn could_unify_with(&self, db: &dyn HirDatabase, other: &Type) -> bool { - could_unify(db, self.env.clone(), &self.ty, &other.ty) + let tys = hir_ty::replace_errors_with_variables((self.ty.clone(), other.ty.clone())); + could_unify(db, self.env.clone(), &tys) } } diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs index 56e06b7d0a4..d8edfb4e9c7 100644 --- a/crates/hir_ty/src/infer/unify.rs +++ b/crates/hir_ty/src/infer/unify.rs @@ -86,8 +86,12 @@ impl> Canonicalized { } } -pub fn could_unify(db: &dyn HirDatabase, env: Arc, t1: &Ty, t2: &Ty) -> bool { - InferenceTable::new(db, env).unify(t1, t2) +pub fn could_unify( + db: &dyn HirDatabase, + env: Arc, + tys: &Canonical<(Ty, Ty)>, +) -> bool { + unify(db, env, tys).is_some() } pub(crate) fn unify( diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs index 06d5cd0b6af..56f60c46b24 100644 --- a/crates/hir_ty/src/lib.rs +++ b/crates/hir_ty/src/lib.rs @@ -43,6 +43,7 @@ use hir_def::{ type_ref::{ConstScalar, Rawness}, TypeParamId, }; +use stdx::always; use crate::{db::HirDatabase, display::HirDisplay, utils::generics}; @@ -326,3 +327,58 @@ pub(crate) fn fold_tys + Fold>( } t.fold_with(&mut TyFolder(f), binders).expect("fold failed unexpectedly") } + +pub fn replace_errors_with_variables(t: T) -> Canonical +where + T: HasInterner + Fold, + T::Result: HasInterner, +{ + use chalk_ir::{ + fold::{Folder, SuperFold}, + Fallible, + }; + struct ErrorReplacer { + vars: usize, + } + impl<'i> Folder<'i, Interner> for ErrorReplacer { + fn as_dyn(&mut self) -> &mut dyn Folder<'i, Interner> { + self + } + + fn interner(&self) -> &'i Interner { + &Interner + } + + fn fold_ty(&mut self, ty: Ty, outer_binder: DebruijnIndex) -> Fallible { + if let TyKind::Error = ty.kind(&Interner) { + let index = self.vars; + self.vars += 1; + Ok(TyKind::BoundVar(BoundVar::new(outer_binder, index)).intern(&Interner)) + } else { + let ty = ty.super_fold_with(self.as_dyn(), outer_binder)?; + Ok(ty) + } + } + + fn fold_inference_ty( + &mut self, + var: InferenceVar, + kind: TyVariableKind, + _outer_binder: DebruijnIndex, + ) -> Fallible { + always!(false); + Ok(TyKind::InferenceVar(var, kind).intern(&Interner)) + } + } + let mut error_replacer = ErrorReplacer { vars: 0 }; + let value = t + .fold_with(&mut error_replacer, DebruijnIndex::INNERMOST) + .expect("fold failed unexpectedly"); + let kinds = (0..error_replacer.vars).map(|_| { + chalk_ir::CanonicalVarKind::new( + chalk_ir::VariableKind::Ty(TyVariableKind::General), + chalk_ir::UniverseIndex::ROOT, + ) + }); + Canonical { value, binders: chalk_ir::CanonicalVarKinds::from_iter(&Interner, kinds) } +} From 7c423f5b88f40da4f3682602bf17a9b6848f5411 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 15 May 2021 21:30:40 +0200 Subject: [PATCH 15/27] Fix panic --- crates/hir_ty/src/infer/unify.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs index d8edfb4e9c7..259feecf1d7 100644 --- a/crates/hir_ty/src/infer/unify.rs +++ b/crates/hir_ty/src/infer/unify.rs @@ -149,7 +149,9 @@ impl TypeVariableTable { fn fallback_value(&self, iv: InferenceVar, kind: TyVariableKind) -> Ty { match kind { - _ if self.inner[iv.index() as usize].diverging => TyKind::Never, + _ if self.inner.get(iv.index() as usize).map_or(false, |data| data.diverging) => { + TyKind::Never + } TyVariableKind::General => TyKind::Error, TyVariableKind::Integer => TyKind::Scalar(Scalar::Int(IntTy::I32)), TyVariableKind::Float => TyKind::Scalar(Scalar::Float(FloatTy::F64)), @@ -205,6 +207,7 @@ impl<'a> InferenceTable<'a> { fn new_var(&mut self, kind: TyVariableKind, diverging: bool) -> Ty { let var = self.var_unification_table.new_variable(UniverseIndex::ROOT); // Chalk might have created some type variables for its own purposes that we don't know about... + // TODO refactor this? self.type_variable_table.inner.extend( (0..1 + var.index() as usize - self.type_variable_table.inner.len()) .map(|_| TypeVariableData { diverging: false }), From 29266ada0469440d69fd3f3532121a7e8ff5379d Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 15 May 2021 22:26:55 +0200 Subject: [PATCH 16/27] Improve debug printing without TLS --- crates/hir_ty/src/interner.rs | 41 +++++----- crates/hir_ty/src/tls.rs | 138 +--------------------------------- 2 files changed, 21 insertions(+), 158 deletions(-) diff --git a/crates/hir_ty/src/interner.rs b/crates/hir_ty/src/interner.rs index 8e77378abe5..29ffdd9b7f3 100644 --- a/crates/hir_ty/src/interner.rs +++ b/crates/hir_ty/src/interner.rs @@ -107,66 +107,65 @@ impl chalk_ir::interner::Interner for Interner { opaque_ty: &chalk_ir::OpaqueTy, fmt: &mut fmt::Formatter<'_>, ) -> Option { - tls::with_current_program(|prog| Some(prog?.debug_opaque_ty(opaque_ty, fmt))) + Some(write!(fmt, "{:?}", opaque_ty.opaque_ty_id)) } fn debug_opaque_ty_id( opaque_ty_id: chalk_ir::OpaqueTyId, fmt: &mut fmt::Formatter<'_>, ) -> Option { - tls::with_current_program(|prog| Some(prog?.debug_opaque_ty_id(opaque_ty_id, fmt))) + Some(fmt.debug_struct("OpaqueTyId").field("index", &opaque_ty_id.0).finish()) } fn debug_ty(ty: &chalk_ir::Ty, fmt: &mut fmt::Formatter<'_>) -> Option { - tls::with_current_program(|prog| Some(prog?.debug_ty(ty, fmt))) + Some(write!(fmt, "{:?}", ty.data(&Interner))) } fn debug_lifetime( lifetime: &chalk_ir::Lifetime, fmt: &mut fmt::Formatter<'_>, ) -> Option { - tls::with_current_program(|prog| Some(prog?.debug_lifetime(lifetime, fmt))) + Some(write!(fmt, "{:?}", lifetime.data(&Interner))) } fn debug_generic_arg( parameter: &GenericArg, fmt: &mut fmt::Formatter<'_>, ) -> Option { - tls::with_current_program(|prog| Some(prog?.debug_generic_arg(parameter, fmt))) + Some(write!(fmt, "{:?}", parameter.data(&Interner).inner_debug())) } fn debug_goal(goal: &Goal, fmt: &mut fmt::Formatter<'_>) -> Option { - tls::with_current_program(|prog| Some(prog?.debug_goal(goal, fmt))) + let goal_data = goal.data(&Interner); + Some(write!(fmt, "{:?}", goal_data)) } fn debug_goals( goals: &chalk_ir::Goals, fmt: &mut fmt::Formatter<'_>, ) -> Option { - tls::with_current_program(|prog| Some(prog?.debug_goals(goals, fmt))) + Some(write!(fmt, "{:?}", goals.debug(&Interner))) } fn debug_program_clause_implication( pci: &chalk_ir::ProgramClauseImplication, fmt: &mut fmt::Formatter<'_>, ) -> Option { - tls::with_current_program(|prog| Some(prog?.debug_program_clause_implication(pci, fmt))) + Some(write!(fmt, "{:?}", pci.debug(&Interner))) } fn debug_substitution( substitution: &chalk_ir::Substitution, fmt: &mut fmt::Formatter<'_>, ) -> Option { - tls::with_current_program(|prog| Some(prog?.debug_substitution(substitution, fmt))) + Some(write!(fmt, "{:?}", substitution.debug(&Interner))) } fn debug_separator_trait_ref( separator_trait_ref: &chalk_ir::SeparatorTraitRef, fmt: &mut fmt::Formatter<'_>, ) -> Option { - tls::with_current_program(|prog| { - Some(prog?.debug_separator_trait_ref(separator_trait_ref, fmt)) - }) + Some(write!(fmt, "{:?}", separator_trait_ref.debug(&Interner))) } fn debug_fn_def_id( @@ -179,47 +178,43 @@ impl chalk_ir::interner::Interner for Interner { constant: &chalk_ir::Const, fmt: &mut fmt::Formatter<'_>, ) -> Option { - tls::with_current_program(|prog| Some(prog?.debug_const(constant, fmt))) + Some(write!(fmt, "{:?}", constant.data(&Interner))) } fn debug_variable_kinds( variable_kinds: &chalk_ir::VariableKinds, fmt: &mut fmt::Formatter<'_>, ) -> Option { - tls::with_current_program(|prog| Some(prog?.debug_variable_kinds(variable_kinds, fmt))) + Some(write!(fmt, "{:?}", variable_kinds.as_slice(&Interner))) } fn debug_variable_kinds_with_angles( variable_kinds: &chalk_ir::VariableKinds, fmt: &mut fmt::Formatter<'_>, ) -> Option { - tls::with_current_program(|prog| { - Some(prog?.debug_variable_kinds_with_angles(variable_kinds, fmt)) - }) + Some(write!(fmt, "{:?}", variable_kinds.inner_debug(&Interner))) } fn debug_canonical_var_kinds( canonical_var_kinds: &chalk_ir::CanonicalVarKinds, fmt: &mut fmt::Formatter<'_>, ) -> Option { - tls::with_current_program(|prog| { - Some(prog?.debug_canonical_var_kinds(canonical_var_kinds, fmt)) - }) + Some(write!(fmt, "{:?}", canonical_var_kinds.as_slice(&Interner))) } fn debug_program_clause( clause: &chalk_ir::ProgramClause, fmt: &mut fmt::Formatter<'_>, ) -> Option { - tls::with_current_program(|prog| Some(prog?.debug_program_clause(clause, fmt))) + Some(write!(fmt, "{:?}", clause.data(&Interner))) } fn debug_program_clauses( clauses: &chalk_ir::ProgramClauses, fmt: &mut fmt::Formatter<'_>, ) -> Option { - tls::with_current_program(|prog| Some(prog?.debug_program_clauses(clauses, fmt))) + Some(write!(fmt, "{:?}", clauses.as_slice(&Interner))) } fn debug_quantified_where_clauses( clauses: &chalk_ir::QuantifiedWhereClauses, fmt: &mut fmt::Formatter<'_>, ) -> Option { - tls::with_current_program(|prog| Some(prog?.debug_quantified_where_clauses(clauses, fmt))) + Some(write!(fmt, "{:?}", clauses.as_slice(&Interner))) } fn intern_ty(&self, kind: chalk_ir::TyKind) -> Self::InternedType { diff --git a/crates/hir_ty/src/tls.rs b/crates/hir_ty/src/tls.rs index 87c671a422c..708797c4772 100644 --- a/crates/hir_ty/src/tls.rs +++ b/crates/hir_ty/src/tls.rs @@ -1,7 +1,7 @@ //! Implementation of Chalk debug helper functions using TLS. -use std::fmt; +use std::fmt::{self, Debug}; -use chalk_ir::{AliasTy, GenericArg, Goal, Goals, Lifetime, ProgramClauseImplication}; +use chalk_ir::AliasTy; use itertools::Itertools; use crate::{ @@ -53,14 +53,6 @@ impl DebugContext<'_> { write!(fmt, "{}::{}", trait_data.name, type_alias_data.name) } - pub(crate) fn debug_opaque_ty_id( - &self, - opaque_ty_id: chalk_ir::OpaqueTyId, - fmt: &mut fmt::Formatter<'_>, - ) -> Result<(), fmt::Error> { - fmt.debug_struct("OpaqueTyId").field("index", &opaque_ty_id.0).finish() - } - pub(crate) fn debug_alias( &self, alias_ty: &AliasTy, @@ -68,7 +60,7 @@ impl DebugContext<'_> { ) -> Result<(), fmt::Error> { match alias_ty { AliasTy::Projection(projection_ty) => self.debug_projection_ty(projection_ty, fmt), - AliasTy::Opaque(opaque_ty) => self.debug_opaque_ty(opaque_ty, fmt), + AliasTy::Opaque(opaque_ty) => opaque_ty.fmt(fmt), } } @@ -96,79 +88,6 @@ impl DebugContext<'_> { write!(fmt, ">::{}", type_alias_data.name) } - pub(crate) fn debug_opaque_ty( - &self, - opaque_ty: &chalk_ir::OpaqueTy, - fmt: &mut fmt::Formatter<'_>, - ) -> Result<(), fmt::Error> { - write!(fmt, "{:?}", opaque_ty.opaque_ty_id) - } - - pub(crate) fn debug_ty( - &self, - ty: &chalk_ir::Ty, - fmt: &mut fmt::Formatter<'_>, - ) -> Result<(), fmt::Error> { - write!(fmt, "{:?}", ty.data(&Interner)) - } - - pub(crate) fn debug_lifetime( - &self, - lifetime: &Lifetime, - fmt: &mut fmt::Formatter<'_>, - ) -> Result<(), fmt::Error> { - write!(fmt, "{:?}", lifetime.data(&Interner)) - } - - pub(crate) fn debug_generic_arg( - &self, - parameter: &GenericArg, - fmt: &mut fmt::Formatter<'_>, - ) -> Result<(), fmt::Error> { - write!(fmt, "{:?}", parameter.data(&Interner).inner_debug()) - } - - pub(crate) fn debug_goal( - &self, - goal: &Goal, - fmt: &mut fmt::Formatter<'_>, - ) -> Result<(), fmt::Error> { - let goal_data = goal.data(&Interner); - write!(fmt, "{:?}", goal_data) - } - - pub(crate) fn debug_goals( - &self, - goals: &Goals, - fmt: &mut fmt::Formatter<'_>, - ) -> Result<(), fmt::Error> { - write!(fmt, "{:?}", goals.debug(&Interner)) - } - - pub(crate) fn debug_program_clause_implication( - &self, - pci: &ProgramClauseImplication, - fmt: &mut fmt::Formatter<'_>, - ) -> Result<(), fmt::Error> { - write!(fmt, "{:?}", pci.debug(&Interner)) - } - - pub(crate) fn debug_substitution( - &self, - substitution: &chalk_ir::Substitution, - fmt: &mut fmt::Formatter<'_>, - ) -> Result<(), fmt::Error> { - write!(fmt, "{:?}", substitution.debug(&Interner)) - } - - pub(crate) fn debug_separator_trait_ref( - &self, - separator_trait_ref: &chalk_ir::SeparatorTraitRef, - fmt: &mut fmt::Formatter<'_>, - ) -> Result<(), fmt::Error> { - write!(fmt, "{:?}", separator_trait_ref.debug(&Interner)) - } - pub(crate) fn debug_fn_def_id( &self, fn_def_id: chalk_ir::FnDefId, @@ -190,57 +109,6 @@ impl DebugContext<'_> { } } } - - pub(crate) fn debug_const( - &self, - _constant: &chalk_ir::Const, - fmt: &mut fmt::Formatter<'_>, - ) -> fmt::Result { - write!(fmt, "const") - } - - pub(crate) fn debug_variable_kinds( - &self, - variable_kinds: &chalk_ir::VariableKinds, - fmt: &mut fmt::Formatter<'_>, - ) -> fmt::Result { - write!(fmt, "{:?}", variable_kinds.as_slice(&Interner)) - } - pub(crate) fn debug_variable_kinds_with_angles( - &self, - variable_kinds: &chalk_ir::VariableKinds, - fmt: &mut fmt::Formatter<'_>, - ) -> fmt::Result { - write!(fmt, "{:?}", variable_kinds.inner_debug(&Interner)) - } - pub(crate) fn debug_canonical_var_kinds( - &self, - canonical_var_kinds: &chalk_ir::CanonicalVarKinds, - fmt: &mut fmt::Formatter<'_>, - ) -> fmt::Result { - write!(fmt, "{:?}", canonical_var_kinds.as_slice(&Interner)) - } - pub(crate) fn debug_program_clause( - &self, - clause: &chalk_ir::ProgramClause, - fmt: &mut fmt::Formatter<'_>, - ) -> fmt::Result { - write!(fmt, "{:?}", clause.data(&Interner)) - } - pub(crate) fn debug_program_clauses( - &self, - clauses: &chalk_ir::ProgramClauses, - fmt: &mut fmt::Formatter<'_>, - ) -> fmt::Result { - write!(fmt, "{:?}", clauses.as_slice(&Interner)) - } - pub(crate) fn debug_quantified_where_clauses( - &self, - clauses: &chalk_ir::QuantifiedWhereClauses, - fmt: &mut fmt::Formatter<'_>, - ) -> fmt::Result { - write!(fmt, "{:?}", clauses.as_slice(&Interner)) - } } mod unsafe_tls { From a3d9cac69057db700c4f6e01b84dc59529ea6dfd Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 15 May 2021 23:09:18 +0200 Subject: [PATCH 17/27] Fix another panic --- crates/hir_ty/src/infer/unify.rs | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs index 259feecf1d7..93cd54f0d96 100644 --- a/crates/hir_ty/src/infer/unify.rs +++ b/crates/hir_ty/src/infer/unify.rs @@ -3,7 +3,8 @@ use std::{borrow::Cow, fmt, sync::Arc}; use chalk_ir::{ - cast::Cast, fold::Fold, interner::HasInterner, FloatTy, IntTy, TyVariableKind, UniverseIndex, + cast::Cast, fold::Fold, interner::HasInterner, zip::Zip, FloatTy, IntTy, TyVariableKind, + UniverseIndex, }; use chalk_solve::infer::ParameterEnaVariableExt; use ena::unify::UnifyKey; @@ -43,10 +44,7 @@ where impl> Canonicalized { pub(super) fn decanonicalize_ty(&self, ty: Ty) -> Ty { - crate::fold_free_vars(ty, |bound, _binders| { - let var = self.free_vars[bound.index].clone(); - var.assert_ty_ref(&Interner).clone() - }) + chalk_ir::Substitute::apply(&self.free_vars, ty, &Interner) } pub(super) fn apply_solution( @@ -72,16 +70,16 @@ impl> Canonicalized { _ => panic!("const variable in solution"), }), ); - for (i, ty) in solution.value.iter(&Interner).enumerate() { - // FIXME: deal with non-type vars here -- the only problematic part is the normalization - // and maybe we don't need that with lazy normalization? + for (i, v) in solution.value.iter(&Interner).enumerate() { let var = self.free_vars[i].clone(); - // eagerly replace projections in the type; we may be getting types - // e.g. from where clauses where this hasn't happened yet - let ty = ctx.normalize_associated_types_in( - new_vars.apply(ty.assert_ty_ref(&Interner).clone(), &Interner), - ); - ctx.table.unify(var.assert_ty_ref(&Interner), &ty); + if let Some(ty) = v.ty(&Interner) { + // eagerly replace projections in the type; we may be getting types + // e.g. from where clauses where this hasn't happened yet + let ty = ctx.normalize_associated_types_in(new_vars.apply(ty.clone(), &Interner)); + ctx.table.unify(var.assert_ty_ref(&Interner), &ty); + } else { + let _ = ctx.table.unify_inner(&var, &new_vars.apply(v.clone(), &Interner)); + } } } } @@ -288,14 +286,14 @@ impl<'a> InferenceTable<'a> { /// Unify two types and return new trait goals arising from it, so the /// caller needs to deal with them. - pub(crate) fn unify_inner(&mut self, ty1: &Ty, ty2: &Ty) -> InferResult { + pub(crate) fn unify_inner>(&mut self, t1: &T, t2: &T) -> InferResult { match self.var_unification_table.relate( &Interner, &self.db, &self.trait_env.env, chalk_ir::Variance::Invariant, - ty1, - ty2, + t1, + t2, ) { Ok(_result) => { // TODO deal with new goals From 1250ddc5cf58ff0a6bbf7c07e5bd9f7cc7db5a09 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 16 May 2021 15:50:28 +0200 Subject: [PATCH 18/27] Rework obligation handling We can't do the easy hack that we did before anymore, where we kept track of whether any inference variables changed since the last time we rechecked obligations. Instead, we store the obligations in canonicalized form; that way we can easily check the inference variables to see whether they have changed since the goal was canonicalized. --- crates/hir/src/lib.rs | 10 +- crates/hir_ty/src/db.rs | 6 +- crates/hir_ty/src/infer.rs | 84 +++--------- crates/hir_ty/src/infer/coerce.rs | 7 +- crates/hir_ty/src/infer/expr.rs | 6 +- crates/hir_ty/src/infer/path.rs | 2 +- crates/hir_ty/src/infer/unify.rs | 173 +++++++++++++++++++++---- crates/hir_ty/src/lib.rs | 3 +- crates/hir_ty/src/lower.rs | 4 +- crates/hir_ty/src/method_resolution.rs | 48 +++++-- crates/hir_ty/src/traits.rs | 40 +++--- 11 files changed, 240 insertions(+), 143 deletions(-) diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 1429384cba4..52d72c3c529 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -1712,15 +1712,17 @@ impl Type { resolver: &Resolver, ty: Ty, ) -> Type { - let environment = - resolver.generic_def().map_or_else(Default::default, |d| db.trait_environment(d)); + let environment = resolver + .generic_def() + .map_or_else(|| Arc::new(TraitEnvironment::empty(krate)), |d| db.trait_environment(d)); Type { krate, env: environment, ty } } fn new(db: &dyn HirDatabase, krate: CrateId, lexical_env: impl HasResolver, ty: Ty) -> Type { let resolver = lexical_env.resolver(db.upcast()); - let environment = - resolver.generic_def().map_or_else(Default::default, |d| db.trait_environment(d)); + let environment = resolver + .generic_def() + .map_or_else(|| Arc::new(TraitEnvironment::empty(krate)), |d| db.trait_environment(d)); Type { krate, env: environment, ty } } diff --git a/crates/hir_ty/src/db.rs b/crates/hir_ty/src/db.rs index f773aa6211e..be5b9110ed9 100644 --- a/crates/hir_ty/src/db.rs +++ b/crates/hir_ty/src/db.rs @@ -134,14 +134,14 @@ pub trait HirDatabase: DefDatabase + Upcast { fn trait_solve( &self, krate: CrateId, - goal: crate::Canonical>, + goal: crate::Canonical>, ) -> Option; #[salsa::invoke(crate::traits::trait_solve_query)] fn trait_solve_query( &self, krate: CrateId, - goal: crate::Canonical>, + goal: crate::Canonical>, ) -> Option; #[salsa::invoke(chalk_db::program_clauses_for_chalk_env_query)] @@ -168,7 +168,7 @@ fn infer_wait(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc fn trait_solve_wait( db: &dyn HirDatabase, krate: CrateId, - goal: crate::Canonical>, + goal: crate::Canonical>, ) -> Option { let _p = profile::span("trait_solve::wait"); db.trait_solve_query(krate, goal) diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs index 7898740be2a..97e7c5f8c09 100644 --- a/crates/hir_ty/src/infer.rs +++ b/crates/hir_ty/src/infer.rs @@ -14,7 +14,7 @@ //! the `ena` crate, which is extracted from rustc. use std::borrow::Cow; -use std::mem; + use std::ops::Index; use std::sync::Arc; @@ -27,8 +27,8 @@ use hir_def::{ path::{path, Path}, resolver::{HasResolver, Resolver, TypeNs}, type_ref::TypeRef, - AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, Lookup, TraitId, - TypeAliasId, VariantId, + AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, HasModule, Lookup, + TraitId, TypeAliasId, VariantId, }; use hir_expand::{diagnostics::DiagnosticSink, name::name}; use la_arena::ArenaMap; @@ -36,13 +36,11 @@ use rustc_hash::FxHashMap; use stdx::impl_from; use syntax::SmolStr; -use super::{ - DomainGoal, Guidance, InEnvironment, ProjectionTy, Solution, TraitEnvironment, TraitRef, Ty, -}; +use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty}; use crate::{ db::HirDatabase, fold_tys, infer::diagnostics::InferenceDiagnostic, - lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Canonical, Interner, - TyBuilder, TyExt, TyKind, + lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Interner, TyBuilder, TyExt, + TyKind, }; // This lint has a false positive here. See the link below for details. @@ -227,8 +225,6 @@ struct InferenceContext<'a> { resolver: Resolver, table: unify::InferenceTable<'a>, trait_env: Arc, - obligations: Vec, - last_obligations_check: Option, result: InferenceResult, /// The return type of the function being inferred, or the closure if we're /// currently within one. @@ -260,15 +256,15 @@ fn find_breakable<'c>( impl<'a> InferenceContext<'a> { fn new(db: &'a dyn HirDatabase, owner: DefWithBodyId, resolver: Resolver) -> Self { - let trait_env = - owner.as_generic_def_id().map_or_else(Default::default, |d| db.trait_environment(d)); + let krate = owner.module(db.upcast()).krate(); + let trait_env = owner + .as_generic_def_id() + .map_or_else(|| Arc::new(TraitEnvironment::empty(krate)), |d| db.trait_environment(d)); InferenceContext { result: InferenceResult::default(), table: unify::InferenceTable::new(db, trait_env.clone()), - obligations: Vec::default(), - last_obligations_check: None, - return_ty: TyKind::Error.intern(&Interner), // set in collect_fn_signature trait_env, + return_ty: TyKind::Error.intern(&Interner), // set in collect_fn_signature db, owner, body: db.body(owner), @@ -284,6 +280,7 @@ impl<'a> InferenceContext<'a> { fn resolve_all(mut self) -> InferenceResult { // FIXME resolve obligations as well (use Guidance if necessary) + self.table.resolve_obligations_as_possible(); // make sure diverging type variables are marked as such self.table.propagate_diverging_flag(); @@ -357,44 +354,11 @@ impl<'a> InferenceContext<'a> { } fn resolve_obligations_as_possible(&mut self) { - let _span = profile::span("resolve_obligations_as_possible"); - - let obligations = mem::replace(&mut self.obligations, Vec::new()); - for obligation in obligations { - let in_env = InEnvironment::new(&self.trait_env.env, obligation.clone()); - let canonicalized = self.canonicalize(in_env); - let solution = - self.db.trait_solve(self.resolver.krate().unwrap(), canonicalized.value.clone()); - - match solution { - Some(Solution::Unique(canonical_subst)) => { - canonicalized.apply_solution( - self, - Canonical { - binders: canonical_subst.binders, - // FIXME: handle constraints - value: canonical_subst.value.subst, - }, - ); - } - Some(Solution::Ambig(Guidance::Definite(substs))) => { - canonicalized.apply_solution(self, substs); - self.obligations.push(obligation); - } - Some(_) => { - // FIXME use this when trying to resolve everything at the end - self.obligations.push(obligation); - } - None => { - // FIXME obligation cannot be fulfilled => diagnostic - } - }; - } + self.table.resolve_obligations_as_possible(); } fn push_obligation(&mut self, o: DomainGoal) { - self.obligations.push(o); - self.last_obligations_check = None; + self.table.register_obligation(o.cast(&Interner)); } fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool { @@ -467,25 +431,7 @@ impl<'a> InferenceContext<'a> { /// call). `make_ty` handles this already, but e.g. for field types we need /// to do it as well. fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty { - let ty = self.resolve_ty_as_possible(ty); - fold_tys( - ty, - |ty, _| match ty.kind(&Interner) { - TyKind::Alias(AliasTy::Projection(proj_ty)) => { - self.normalize_projection_ty(proj_ty.clone()) - } - _ => ty, - }, - DebruijnIndex::INNERMOST, - ) - } - - fn normalize_projection_ty(&mut self, proj_ty: ProjectionTy) -> Ty { - let var = self.table.new_type_var(); - let alias_eq = AliasEq { alias: AliasTy::Projection(proj_ty), ty: var.clone() }; - let obligation = alias_eq.cast(&Interner); - self.push_obligation(obligation); - var + self.table.normalize_associated_types_in(ty) } fn resolve_variant(&mut self, path: Option<&Path>) -> (Ty, Option) { diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs index 4d80b4a082c..911343cb954 100644 --- a/crates/hir_ty/src/infer/coerce.rs +++ b/crates/hir_ty/src/infer/coerce.rs @@ -402,12 +402,15 @@ impl<'a> InferenceContext<'a> { // solve `CoerceUnsized` and `Unsize` goals at this point and leaves the // rest for later. Also, there's some logic about sized type variables. // Need to find out in what cases this is necessary - let solution = self.db.trait_solve(krate, canonicalized.value.clone()).ok_or(TypeError)?; + let solution = self + .db + .trait_solve(krate, canonicalized.value.clone().cast(&Interner)) + .ok_or(TypeError)?; match solution { Solution::Unique(v) => { canonicalized.apply_solution( - self, + &mut self.table, Canonical { binders: v.binders, // FIXME handle constraints diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index f439169ea4a..6eaccd9b45e 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs @@ -99,9 +99,9 @@ impl<'a> InferenceContext<'a> { environment: trait_env, }; let canonical = self.canonicalize(obligation.clone()); - if self.db.trait_solve(krate, canonical.value).is_some() { + if self.db.trait_solve(krate, canonical.value.cast(&Interner)).is_some() { self.push_obligation(obligation.goal); - let return_ty = self.normalize_projection_ty(projection); + let return_ty = self.table.normalize_projection_ty(projection); Some((arg_tys, return_ty)) } else { None @@ -306,7 +306,7 @@ impl<'a> InferenceContext<'a> { self.resolver.krate(), InEnvironment { goal: canonicalized.value.clone(), - environment: self.trait_env.env.clone(), + environment: self.table.trait_env.env.clone(), }, ); let (param_tys, ret_ty): (Vec, Ty) = derefs diff --git a/crates/hir_ty/src/infer/path.rs b/crates/hir_ty/src/infer/path.rs index bc64b612b2e..fd366e1211a 100644 --- a/crates/hir_ty/src/infer/path.rs +++ b/crates/hir_ty/src/infer/path.rs @@ -225,7 +225,7 @@ impl<'a> InferenceContext<'a> { method_resolution::iterate_method_candidates( &canonical_ty.value, self.db, - self.trait_env.clone(), + self.table.trait_env.clone(), krate, &traits_in_scope, None, diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs index 93cd54f0d96..75e04e8b50f 100644 --- a/crates/hir_ty/src/infer/unify.rs +++ b/crates/hir_ty/src/infer/unify.rs @@ -1,6 +1,6 @@ //! Unification and canonicalization logic. -use std::{borrow::Cow, fmt, sync::Arc}; +use std::{borrow::Cow, fmt, mem, sync::Arc}; use chalk_ir::{ cast::Cast, fold::Fold, interner::HasInterner, zip::Zip, FloatTy, IntTy, TyVariableKind, @@ -11,8 +11,9 @@ use ena::unify::UnifyKey; use super::{InferOk, InferResult, InferenceContext, TypeError}; use crate::{ - db::HirDatabase, fold_tys, static_lifetime, BoundVar, Canonical, DebruijnIndex, GenericArg, - InferenceVar, Interner, Scalar, Substitution, TraitEnvironment, Ty, TyKind, VariableKind, + db::HirDatabase, fold_tys, static_lifetime, AliasEq, AliasTy, BoundVar, Canonical, + DebruijnIndex, GenericArg, Goal, Guidance, InEnvironment, InferenceVar, Interner, ProjectionTy, + Scalar, Solution, Substitution, TraitEnvironment, Ty, TyKind, VariableKind, }; impl<'a> InferenceContext<'a> { @@ -23,17 +24,11 @@ impl<'a> InferenceContext<'a> { where T::Result: HasInterner, { - let result = self.table.var_unification_table.canonicalize(&Interner, t); - let free_vars = result - .free_vars - .into_iter() - .map(|free_var| free_var.to_generic_arg(&Interner)) - .collect(); - Canonicalized { value: result.quantified, free_vars } + self.table.canonicalize(t) } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub(super) struct Canonicalized where T: HasInterner, @@ -49,22 +44,16 @@ impl> Canonicalized { pub(super) fn apply_solution( &self, - ctx: &mut InferenceContext<'_>, + ctx: &mut InferenceTable, solution: Canonical, ) { // the solution may contain new variables, which we need to convert to new inference vars let new_vars = Substitution::from_iter( &Interner, solution.binders.iter(&Interner).map(|k| match k.kind { - VariableKind::Ty(TyVariableKind::General) => { - ctx.table.new_type_var().cast(&Interner) - } - VariableKind::Ty(TyVariableKind::Integer) => { - ctx.table.new_integer_var().cast(&Interner) - } - VariableKind::Ty(TyVariableKind::Float) => { - ctx.table.new_float_var().cast(&Interner) - } + VariableKind::Ty(TyVariableKind::General) => ctx.new_type_var().cast(&Interner), + VariableKind::Ty(TyVariableKind::Integer) => ctx.new_integer_var().cast(&Interner), + VariableKind::Ty(TyVariableKind::Float) => ctx.new_float_var().cast(&Interner), // Chalk can sometimes return new lifetime variables. We just use the static lifetime everywhere VariableKind::Lifetime => static_lifetime().cast(&Interner), _ => panic!("const variable in solution"), @@ -76,9 +65,9 @@ impl> Canonicalized { // eagerly replace projections in the type; we may be getting types // e.g. from where clauses where this hasn't happened yet let ty = ctx.normalize_associated_types_in(new_vars.apply(ty.clone(), &Interner)); - ctx.table.unify(var.assert_ty_ref(&Interner), &ty); + ctx.unify(var.assert_ty_ref(&Interner), &ty); } else { - let _ = ctx.table.unify_inner(&var, &new_vars.apply(v.clone(), &Interner)); + let _ = ctx.unify_inner(&var, &new_vars.apply(v.clone(), &Interner)); } } } @@ -167,10 +156,11 @@ type ChalkInferenceTable = chalk_solve::infer::InferenceTable; #[derive(Clone)] pub(crate) struct InferenceTable<'a> { - db: &'a dyn HirDatabase, - trait_env: Arc, + pub db: &'a dyn HirDatabase, + pub trait_env: Arc, pub(super) var_unification_table: ChalkInferenceTable, pub(super) type_variable_table: TypeVariableTable, + pending_obligations: Vec>>, } impl<'a> InferenceTable<'a> { @@ -180,6 +170,7 @@ impl<'a> InferenceTable<'a> { trait_env, var_unification_table: ChalkInferenceTable::new(), type_variable_table: TypeVariableTable { inner: Vec::new() }, + pending_obligations: Vec::new(), } } @@ -202,6 +193,50 @@ impl<'a> InferenceTable<'a> { } } + pub(super) fn canonicalize + HasInterner>( + &mut self, + t: T, + ) -> Canonicalized + where + T::Result: HasInterner, + { + let result = self.var_unification_table.canonicalize(&Interner, t); + let free_vars = result + .free_vars + .into_iter() + .map(|free_var| free_var.to_generic_arg(&Interner)) + .collect(); + Canonicalized { value: result.quantified, free_vars } + } + + /// Recurses through the given type, normalizing associated types mentioned + /// in it by replacing them by type variables and registering obligations to + /// resolve later. This should be done once for every type we get from some + /// type annotation (e.g. from a let type annotation, field type or function + /// call). `make_ty` handles this already, but e.g. for field types we need + /// to do it as well. + pub(super) fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty { + let ty = self.resolve_ty_as_possible(ty); + fold_tys( + ty, + |ty, _| match ty.kind(&Interner) { + TyKind::Alias(AliasTy::Projection(proj_ty)) => { + self.normalize_projection_ty(proj_ty.clone()) + } + _ => ty, + }, + DebruijnIndex::INNERMOST, + ) + } + + pub(super) fn normalize_projection_ty(&mut self, proj_ty: ProjectionTy) -> Ty { + let var = self.new_type_var(); + let alias_eq = AliasEq { alias: AliasTy::Projection(proj_ty), ty: var.clone() }; + let obligation = alias_eq.cast(&Interner); + self.register_obligation(obligation); + var + } + fn new_var(&mut self, kind: TyVariableKind, diverging: bool) -> Ty { let var = self.var_unification_table.new_variable(UniverseIndex::ROOT); // Chalk might have created some type variables for its own purposes that we don't know about... @@ -341,6 +376,94 @@ impl<'a> InferenceTable<'a> { DebruijnIndex::INNERMOST, ) } + + pub fn register_obligation(&mut self, goal: Goal) { + let in_env = InEnvironment::new(&self.trait_env.env, goal); + self.register_obligation_in_env(in_env) + } + + fn register_obligation_in_env(&mut self, goal: InEnvironment) { + let canonicalized = self.canonicalize(goal); + if !self.try_resolve_obligation(&canonicalized) { + self.pending_obligations.push(canonicalized); + } + } + + pub fn resolve_obligations_as_possible(&mut self) { + let _span = profile::span("resolve_obligations_as_possible"); + let mut changed = true; + let mut obligations = Vec::new(); + while changed { + changed = false; + mem::swap(&mut self.pending_obligations, &mut obligations); + for canonicalized in obligations.drain(..) { + if !self.check_changed(&canonicalized) { + self.pending_obligations.push(canonicalized); + continue; + } + changed = true; + let uncanonical = chalk_ir::Substitute::apply( + &canonicalized.free_vars, + canonicalized.value.value, + &Interner, + ); + self.register_obligation_in_env(uncanonical); + } + } + } + + /// This checks whether any of the free variables in the `canonicalized` + /// have changed (either been unified with another variable, or with a + /// value). If this is not the case, we don't need to try to solve the goal + /// again -- it'll give the same result as last time. + fn check_changed(&mut self, canonicalized: &Canonicalized>) -> bool { + canonicalized.free_vars.iter().any(|var| { + let iv = match var.data(&Interner) { + chalk_ir::GenericArgData::Ty(ty) => ty.inference_var(&Interner), + chalk_ir::GenericArgData::Lifetime(lt) => lt.inference_var(&Interner), + chalk_ir::GenericArgData::Const(c) => c.inference_var(&Interner), + } + .expect("free var is not inference var"); + if self.var_unification_table.probe_var(iv).is_some() { + return true; + } + let root = self.var_unification_table.inference_var_root(iv); + iv != root + }) + } + + fn try_resolve_obligation( + &mut self, + canonicalized: &Canonicalized>, + ) -> bool { + let solution = self.db.trait_solve(self.trait_env.krate, canonicalized.value.clone()); + + match solution { + Some(Solution::Unique(canonical_subst)) => { + canonicalized.apply_solution( + self, + Canonical { + binders: canonical_subst.binders, + // FIXME: handle constraints + value: canonical_subst.value.subst, + }, + ); + true + } + Some(Solution::Ambig(Guidance::Definite(substs))) => { + canonicalized.apply_solution(self, substs); + false + } + Some(_) => { + // FIXME use this when trying to resolve everything at the end + false + } + None => { + // FIXME obligation cannot be fulfilled => diagnostic + true + } + } + } } impl<'a> fmt::Debug for InferenceTable<'a> { diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs index 56f60c46b24..72093d75a2e 100644 --- a/crates/hir_ty/src/lib.rs +++ b/crates/hir_ty/src/lib.rs @@ -45,7 +45,7 @@ use hir_def::{ }; use stdx::always; -use crate::{db::HirDatabase, display::HirDisplay, utils::generics}; +use crate::{db::HirDatabase, utils::generics}; pub use autoderef::autoderef; pub use builder::TyBuilder; @@ -114,6 +114,7 @@ pub type FnSig = chalk_ir::FnSig; pub type InEnvironment = chalk_ir::InEnvironment; pub type DomainGoal = chalk_ir::DomainGoal; +pub type Goal = chalk_ir::Goal; pub type AliasEq = chalk_ir::AliasEq; pub type Solution = chalk_solve::Solution; pub type ConstrainedSubst = chalk_ir::ConstrainedSubst; diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs index bd8bb602864..8a375b97348 100644 --- a/crates/hir_ty/src/lower.rs +++ b/crates/hir_ty/src/lower.rs @@ -1035,9 +1035,11 @@ pub(crate) fn trait_environment_query( clauses.push(program_clause.into_from_env_clause(&Interner)); } + let krate = def.module(db.upcast()).krate(); + let env = chalk_ir::Environment::new(&Interner).add_clauses(&Interner, clauses); - Arc::new(TraitEnvironment { traits_from_clauses: traits_in_scope, env }) + Arc::new(TraitEnvironment { krate, traits_from_clauses: traits_in_scope, env }) } /// Resolve the where clause(s) of an item with generics. diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs index 37e1f89d825..08e385a42c8 100644 --- a/crates/hir_ty/src/method_resolution.rs +++ b/crates/hir_ty/src/method_resolution.rs @@ -577,6 +577,7 @@ fn iterate_method_candidates_by_receiver( if iterate_inherent_methods( self_ty, db, + env.clone(), name, Some(receiver_ty), krate, @@ -613,8 +614,16 @@ fn iterate_method_candidates_for_self_ty( name: Option<&Name>, mut callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool, ) -> bool { - if iterate_inherent_methods(self_ty, db, name, None, krate, visible_from_module, &mut callback) - { + if iterate_inherent_methods( + self_ty, + db, + env.clone(), + name, + None, + krate, + visible_from_module, + &mut callback, + ) { return true; } iterate_trait_method_candidates(self_ty, db, env, krate, traits_in_scope, name, None, callback) @@ -653,12 +662,12 @@ fn iterate_trait_method_candidates( for (_name, item) in data.items.iter() { // Don't pass a `visible_from_module` down to `is_valid_candidate`, // since only inherent methods should be included into visibility checking. - if !is_valid_candidate(db, name, receiver_ty, *item, self_ty, None) { + if !is_valid_candidate(db, env.clone(), name, receiver_ty, *item, self_ty, None) { continue; } if !known_implemented { let goal = generic_implements_goal(db, env.clone(), t, self_ty.clone()); - if db.trait_solve(krate, goal).is_none() { + if db.trait_solve(krate, goal.cast(&Interner)).is_none() { continue 'traits; } } @@ -675,6 +684,7 @@ fn iterate_trait_method_candidates( fn iterate_inherent_methods( self_ty: &Canonical, db: &dyn HirDatabase, + env: Arc, name: Option<&Name>, receiver_ty: Option<&Canonical>, krate: CrateId, @@ -690,14 +700,24 @@ fn iterate_inherent_methods( for &impl_def in impls.for_self_ty(&self_ty.value) { for &item in db.impl_data(impl_def).items.iter() { - if !is_valid_candidate(db, name, receiver_ty, item, self_ty, visible_from_module) { + if !is_valid_candidate( + db, + env.clone(), + name, + receiver_ty, + item, + self_ty, + visible_from_module, + ) { continue; } // we have to check whether the self type unifies with the type // that the impl is for. If we have a receiver type, this // already happens in `is_valid_candidate` above; if not, we // check it here - if receiver_ty.is_none() && inherent_impl_substs(db, impl_def, self_ty).is_none() { + if receiver_ty.is_none() + && inherent_impl_substs(db, env.clone(), impl_def, self_ty).is_none() + { cov_mark::hit!(impl_self_type_match_without_receiver); continue; } @@ -722,7 +742,7 @@ pub fn resolve_indexing_op( let deref_chain = autoderef_method_receiver(db, krate, ty); for ty in deref_chain { let goal = generic_implements_goal(db, env.clone(), index_trait, ty.clone()); - if db.trait_solve(krate, goal).is_some() { + if db.trait_solve(krate, goal.cast(&Interner)).is_some() { return Some(ty); } } @@ -731,6 +751,7 @@ pub fn resolve_indexing_op( fn is_valid_candidate( db: &dyn HirDatabase, + env: Arc, name: Option<&Name>, receiver_ty: Option<&Canonical>, item: AssocItemId, @@ -749,7 +770,7 @@ fn is_valid_candidate( if !data.has_self_param() { return false; } - let transformed_receiver_ty = match transform_receiver_ty(db, m, self_ty) { + let transformed_receiver_ty = match transform_receiver_ty(db, env, m, self_ty) { Some(ty) => ty, None => return false, }; @@ -776,6 +797,7 @@ fn is_valid_candidate( pub(crate) fn inherent_impl_substs( db: &dyn HirDatabase, + env: Arc, impl_id: ImplId, self_ty: &Canonical, ) -> Option { @@ -798,8 +820,7 @@ pub(crate) fn inherent_impl_substs( binders: CanonicalVarKinds::from_iter(&Interner, kinds), value: (self_ty_with_vars, self_ty.value.clone()), }; - let trait_env = Arc::new(TraitEnvironment::default()); // FIXME - let substs = super::infer::unify(db, trait_env, &tys)?; + let substs = super::infer::unify(db, env, &tys)?; // We only want the substs for the vars we added, not the ones from self_ty. // Also, if any of the vars we added are still in there, we replace them by // Unknown. I think this can only really happen if self_ty contained @@ -824,6 +845,7 @@ fn fallback_bound_vars(s: Substitution, num_vars_to_keep: usize) -> Substitution fn transform_receiver_ty( db: &dyn HirDatabase, + env: Arc, function_id: FunctionId, self_ty: &Canonical, ) -> Option { @@ -833,7 +855,7 @@ fn transform_receiver_ty( .fill_with_unknown() .build(), AssocContainerId::ImplId(impl_id) => { - let impl_substs = inherent_impl_substs(db, impl_id, &self_ty)?; + let impl_substs = inherent_impl_substs(db, env, impl_id, &self_ty)?; TyBuilder::subst_for_def(db, function_id) .use_parent_substs(&impl_substs) .fill_with_unknown() @@ -853,7 +875,7 @@ pub fn implements_trait( trait_: TraitId, ) -> bool { let goal = generic_implements_goal(db, env, trait_, ty.clone()); - let solution = db.trait_solve(krate, goal); + let solution = db.trait_solve(krate, goal.cast(&Interner)); solution.is_some() } @@ -866,7 +888,7 @@ pub fn implements_trait_unique( trait_: TraitId, ) -> bool { let goal = generic_implements_goal(db, env, trait_, ty.clone()); - let solution = db.trait_solve(krate, goal); + let solution = db.trait_solve(krate, goal.cast(&Interner)); matches!(solution, Some(crate::Solution::Unique(_))) } diff --git a/crates/hir_ty/src/traits.rs b/crates/hir_ty/src/traits.rs index 9936d080338..294cb531c8b 100644 --- a/crates/hir_ty/src/traits.rs +++ b/crates/hir_ty/src/traits.rs @@ -2,7 +2,7 @@ use std::env::var; -use chalk_ir::cast::Cast; +use chalk_ir::GoalData; use chalk_solve::{logging_db::LoggingRustIrDatabase, Solver}; use base_db::CrateId; @@ -10,7 +10,7 @@ use hir_def::{lang_item::LangItemTarget, TraitId}; use stdx::panic_context; use crate::{ - db::HirDatabase, AliasEq, AliasTy, Canonical, DomainGoal, Guidance, HirDisplay, InEnvironment, + db::HirDatabase, AliasEq, AliasTy, Canonical, DomainGoal, Goal, Guidance, InEnvironment, Interner, Solution, TraitRefExt, Ty, TyKind, WhereClause, }; @@ -38,6 +38,7 @@ fn create_chalk_solver() -> chalk_recursive::RecursiveSolver { /// we assume that `T: Default`. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct TraitEnvironment { + pub krate: CrateId, // When we're using Chalk's Ty we can make this a BTreeMap since it's Ord, // but for now it's too annoying... pub(crate) traits_from_clauses: Vec<(Ty, TraitId)>, @@ -45,6 +46,14 @@ pub struct TraitEnvironment { } impl TraitEnvironment { + pub fn empty(krate: CrateId) -> Self { + TraitEnvironment { + krate, + traits_from_clauses: Vec::new(), + env: chalk_ir::Environment::new(&Interner), + } + } + pub(crate) fn traits_in_scope_from_clauses<'a>( &'a self, ty: &'a Ty, @@ -59,34 +68,25 @@ impl TraitEnvironment { } } -impl Default for TraitEnvironment { - fn default() -> Self { - TraitEnvironment { - traits_from_clauses: Vec::new(), - env: chalk_ir::Environment::new(&Interner), - } - } -} - /// Solve a trait goal using Chalk. pub(crate) fn trait_solve_query( db: &dyn HirDatabase, krate: CrateId, - goal: Canonical>, + goal: Canonical>, ) -> Option { - let _p = profile::span("trait_solve_query").detail(|| match &goal.value.goal { - DomainGoal::Holds(WhereClause::Implemented(it)) => { + let _p = profile::span("trait_solve_query").detail(|| match &goal.value.goal.data(&Interner) { + GoalData::DomainGoal(DomainGoal::Holds(WhereClause::Implemented(it))) => { db.trait_data(it.hir_trait_id()).name.to_string() } - DomainGoal::Holds(WhereClause::AliasEq(_)) => "alias_eq".to_string(), + GoalData::DomainGoal(DomainGoal::Holds(WhereClause::AliasEq(_))) => "alias_eq".to_string(), _ => "??".to_string(), }); - log::info!("trait_solve_query({})", goal.value.goal.display(db)); + log::info!("trait_solve_query({:?})", goal.value.goal); - if let DomainGoal::Holds(WhereClause::AliasEq(AliasEq { + if let GoalData::DomainGoal(DomainGoal::Holds(WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(projection_ty), .. - })) = &goal.value.goal + }))) = &goal.value.goal.data(&Interner) { if let TyKind::BoundVar(_) = projection_ty.self_type_parameter(&Interner).kind(&Interner) { // Hack: don't ask Chalk to normalize with an unknown self type, it'll say that's impossible @@ -94,11 +94,9 @@ pub(crate) fn trait_solve_query( } } - let canonical = goal.cast(&Interner); - // We currently don't deal with universes (I think / hope they're not yet // relevant for our use cases?) - let u_canonical = chalk_ir::UCanonical { canonical, universes: 1 }; + let u_canonical = chalk_ir::UCanonical { canonical: goal, universes: 1 }; solve(db, krate, &u_canonical) } From a78f0076abbbf61f7b68ce5c323639037c8a72de Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 16 May 2021 15:56:27 +0200 Subject: [PATCH 19/27] Make resolve_ty_shallow return Ty --- crates/hir_ty/src/infer.rs | 4 +--- crates/hir_ty/src/infer/coerce.rs | 8 +++----- crates/hir_ty/src/infer/unify.rs | 9 +++------ 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs index 97e7c5f8c09..8cfc84b8634 100644 --- a/crates/hir_ty/src/infer.rs +++ b/crates/hir_ty/src/infer.rs @@ -13,8 +13,6 @@ //! to certain types. To record this, we use the union-find implementation from //! the `ena` crate, which is extracted from rustc. -use std::borrow::Cow; - use std::ops::Index; use std::sync::Arc; @@ -384,7 +382,7 @@ impl<'a> InferenceContext<'a> { self.table.resolve_ty_as_possible(ty) } - fn resolve_ty_shallow<'b>(&mut self, ty: &'b Ty) -> Cow<'b, Ty> { + fn resolve_ty_shallow(&mut self, ty: &Ty) -> Ty { self.table.resolve_ty_shallow(ty) } diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs index 911343cb954..c85c088f7a4 100644 --- a/crates/hir_ty/src/infer/coerce.rs +++ b/crates/hir_ty/src/infer/coerce.rs @@ -23,7 +23,7 @@ impl<'a> InferenceContext<'a> { if to_ty.is_unknown() { return true; } - let from_ty = self.resolve_ty_shallow(from_ty).into_owned(); + let from_ty = self.resolve_ty_shallow(from_ty); let to_ty = self.resolve_ty_shallow(to_ty); match self.coerce_inner(from_ty, &to_ty) { Ok(_result) => { @@ -46,9 +46,7 @@ impl<'a> InferenceContext<'a> { /// least upper bound. pub(super) fn coerce_merge_branch(&mut self, ty1: &Ty, ty2: &Ty) -> Ty { let ty1 = self.resolve_ty_shallow(ty1); - let ty1 = ty1.as_ref(); let ty2 = self.resolve_ty_shallow(ty2); - let ty2 = ty2.as_ref(); // Special case: two function types. Try to coerce both to // pointers to have a chance at getting a match. See // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916 @@ -80,9 +78,9 @@ impl<'a> InferenceContext<'a> { // type is a type variable and the new one is `!`, trying it the other // way around first would mean we make the type variable `!`, instead of // just marking it as possibly diverging. - if self.coerce(ty2, ty1) { + if self.coerce(&ty2, &ty1) { ty1.clone() - } else if self.coerce(ty1, ty2) { + } else if self.coerce(&ty1, &ty2) { ty2.clone() } else { // TODO record a type mismatch diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs index 75e04e8b50f..278127c69b5 100644 --- a/crates/hir_ty/src/infer/unify.rs +++ b/crates/hir_ty/src/infer/unify.rs @@ -1,6 +1,6 @@ //! Unification and canonicalization logic. -use std::{borrow::Cow, fmt, mem, sync::Arc}; +use std::{fmt, mem, sync::Arc}; use chalk_ir::{ cast::Cast, fold::Fold, interner::HasInterner, zip::Zip, FloatTy, IntTy, TyVariableKind, @@ -340,11 +340,8 @@ impl<'a> InferenceTable<'a> { /// If `ty` is a type variable with known type, returns that type; /// otherwise, return ty. - // FIXME this could probably just return Ty - pub(crate) fn resolve_ty_shallow<'b>(&mut self, ty: &'b Ty) -> Cow<'b, Ty> { - self.var_unification_table - .normalize_ty_shallow(&Interner, ty) - .map_or(Cow::Borrowed(ty), Cow::Owned) + pub(crate) fn resolve_ty_shallow(&mut self, ty: &Ty) -> Ty { + self.var_unification_table.normalize_ty_shallow(&Interner, ty).unwrap_or_else(|| ty.clone()) } /// Resolves the type as far as currently possible, replacing type variables From 4bd446f5b3f8035d5db1fde1c6c50073e3f4fb2b Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 16 May 2021 17:56:38 +0200 Subject: [PATCH 20/27] Get rid of resolve_ty_as_possible Instead use shallow resolving where necessary. --- crates/hir_ty/src/infer.rs | 34 +++++++++++------------ crates/hir_ty/src/infer/coerce.rs | 4 +-- crates/hir_ty/src/infer/expr.rs | 28 ++++++++++--------- crates/hir_ty/src/infer/pat.rs | 19 +++++++------ crates/hir_ty/src/infer/path.rs | 2 +- crates/hir_ty/src/infer/unify.rs | 39 +++------------------------ crates/hir_ty/src/tests/regression.rs | 16 +++++------ crates/hir_ty/src/tests/traits.rs | 2 +- 8 files changed, 56 insertions(+), 88 deletions(-) diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs index 8cfc84b8634..ab742e2032e 100644 --- a/crates/hir_ty/src/infer.rs +++ b/crates/hir_ty/src/infer.rs @@ -273,7 +273,7 @@ impl<'a> InferenceContext<'a> { } fn err_ty(&self) -> Ty { - TyKind::Error.intern(&Interner) + self.result.standard_types.unknown.clone() } fn resolve_all(mut self) -> InferenceResult { @@ -284,12 +284,14 @@ impl<'a> InferenceContext<'a> { self.table.propagate_diverging_flag(); let mut result = std::mem::take(&mut self.result); for ty in result.type_of_expr.values_mut() { - let resolved = self.table.resolve_ty_completely(ty.clone()); - *ty = resolved; + *ty = self.table.resolve_ty_completely(ty.clone()); } for ty in result.type_of_pat.values_mut() { - let resolved = self.table.resolve_ty_completely(ty.clone()); - *ty = resolved; + *ty = self.table.resolve_ty_completely(ty.clone()); + } + for mismatch in result.type_mismatches.values_mut() { + mismatch.expected = self.table.resolve_ty_completely(mismatch.expected.clone()); + mismatch.actual = self.table.resolve_ty_completely(mismatch.actual.clone()); } result } @@ -343,6 +345,14 @@ impl<'a> InferenceContext<'a> { fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty { match ty.kind(&Interner) { TyKind::Error => self.table.new_type_var(), + TyKind::InferenceVar(..) => { + let ty_resolved = self.resolve_ty_shallow(&ty); + if ty_resolved.is_unknown() { + self.table.new_type_var() + } else { + ty + } + } _ => ty, } } @@ -371,18 +381,8 @@ impl<'a> InferenceContext<'a> { self.table.unify_inner(ty1, ty2) } - // FIXME get rid of this, instead resolve shallowly where necessary - /// Resolves the type as far as currently possible, replacing type variables - /// by their known types. All types returned by the infer_* functions should - /// be resolved as far as possible, i.e. contain no type variables with - /// known type. - fn resolve_ty_as_possible(&mut self, ty: Ty) -> Ty { - self.resolve_obligations_as_possible(); - - self.table.resolve_ty_as_possible(ty) - } - fn resolve_ty_shallow(&mut self, ty: &Ty) -> Ty { + self.resolve_obligations_as_possible(); self.table.resolve_ty_shallow(ty) } @@ -416,7 +416,7 @@ impl<'a> InferenceContext<'a> { }; self.push_obligation(trait_ref.cast(&Interner)); self.push_obligation(alias_eq.cast(&Interner)); - self.resolve_ty_as_possible(ty) + ty } None => self.err_ty(), } diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs index c85c088f7a4..00b2b585f44 100644 --- a/crates/hir_ty/src/infer/coerce.rs +++ b/crates/hir_ty/src/infer/coerce.rs @@ -19,12 +19,12 @@ impl<'a> InferenceContext<'a> { /// Unify two types, but may coerce the first one to the second one /// using "implicit coercion rules" if needed. pub(super) fn coerce(&mut self, from_ty: &Ty, to_ty: &Ty) -> bool { + let from_ty = self.resolve_ty_shallow(from_ty); + let to_ty = self.resolve_ty_shallow(to_ty); // TODO handle expectations properly if to_ty.is_unknown() { return true; } - let from_ty = self.resolve_ty_shallow(from_ty); - let to_ty = self.resolve_ty_shallow(to_ty); match self.coerce_inner(from_ty, &to_ty) { Ok(_result) => { // TODO deal with goals diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index 6eaccd9b45e..f5782ab2461 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs @@ -35,7 +35,7 @@ use super::{ impl<'a> InferenceContext<'a> { pub(super) fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty { let ty = self.infer_expr_inner(tgt_expr, expected); - if ty.is_never() { + if self.resolve_ty_shallow(&ty).is_never() { // Any expression that produces a value of type `!` must have diverged self.diverges = Diverges::Always; } @@ -46,7 +46,7 @@ impl<'a> InferenceContext<'a> { TypeMismatch { expected: expected.ty.clone(), actual: ty.clone() }, ); } - self.resolve_ty_as_possible(ty) + ty } /// Infer type of expression with possibly implicit coerce to the expected type. @@ -67,7 +67,7 @@ impl<'a> InferenceContext<'a> { expected.ty.clone() }; - self.resolve_ty_as_possible(ty) + ty } fn callable_sig_from_fn_trait(&mut self, ty: &Ty, num_args: usize) -> Option<(Vec, Ty)> { @@ -284,8 +284,7 @@ impl<'a> InferenceContext<'a> { // Now go through the argument patterns for (arg_pat, arg_ty) in args.iter().zip(sig_tys) { - let resolved = self.resolve_ty_as_possible(arg_ty); - self.infer_pat(*arg_pat, &resolved, BindingMode::default()); + self.infer_pat(*arg_pat, &arg_ty, BindingMode::default()); } let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); @@ -525,14 +524,14 @@ impl<'a> InferenceContext<'a> { Expr::Ref { expr, rawness, mutability } => { let mutability = lower_to_chalk_mutability(*mutability); let expectation = if let Some((exp_inner, exp_rawness, exp_mutability)) = - &expected.ty.as_reference_or_ptr() + &self.resolve_ty_shallow(&expected.ty).as_reference_or_ptr() { if *exp_mutability == Mutability::Mut && mutability == Mutability::Not { - // FIXME: throw type error - expected mut reference but found shared ref, + // FIXME: record type error - expected mut reference but found shared ref, // which cannot be coerced } if *exp_rawness == Rawness::Ref && *rawness == Rawness::RawPtr { - // FIXME: throw type error - expected reference but found ptr, + // FIXME: record type error - expected reference but found ptr, // which cannot be coerced } Expectation::rvalue_hint(Ty::clone(exp_inner)) @@ -559,6 +558,7 @@ impl<'a> InferenceContext<'a> { } Expr::UnaryOp { expr, op } => { let inner_ty = self.infer_expr_inner(*expr, &Expectation::none()); + let inner_ty = self.resolve_ty_shallow(&inner_ty); match op { UnaryOp::Deref => match self.resolver.krate() { Some(krate) => { @@ -615,8 +615,10 @@ impl<'a> InferenceContext<'a> { _ => Expectation::none(), }; let lhs_ty = self.infer_expr(*lhs, &lhs_expectation); + let lhs_ty = self.resolve_ty_shallow(&lhs_ty); let rhs_expectation = op::binary_op_rhs_expectation(*op, lhs_ty.clone()); let rhs_ty = self.infer_expr(*rhs, &Expectation::has_type(rhs_expectation)); + let rhs_ty = self.resolve_ty_shallow(&rhs_ty); let ret = op::binary_op_return_ty(*op, lhs_ty.clone(), rhs_ty.clone()); @@ -699,7 +701,7 @@ impl<'a> InferenceContext<'a> { } } Expr::Tuple { exprs } => { - let mut tys = match expected.ty.kind(&Interner) { + let mut tys = match self.resolve_ty_shallow(&expected.ty).kind(&Interner) { TyKind::Tuple(_, substs) => substs .iter(&Interner) .map(|a| a.assert_ty_ref(&Interner).clone()) @@ -716,7 +718,7 @@ impl<'a> InferenceContext<'a> { TyKind::Tuple(tys.len(), Substitution::from_iter(&Interner, tys)).intern(&Interner) } Expr::Array(array) => { - let elem_ty = match expected.ty.kind(&Interner) { + let elem_ty = match self.resolve_ty_shallow(&expected.ty).kind(&Interner) { TyKind::Array(st, _) | TyKind::Slice(st) => st.clone(), _ => self.table.new_type_var(), }; @@ -788,7 +790,6 @@ impl<'a> InferenceContext<'a> { }; // use a new type variable if we got unknown here let ty = self.insert_type_vars_shallow(ty); - let ty = self.resolve_ty_as_possible(ty); self.write_expr_ty(tgt_expr, ty.clone()); ty } @@ -816,7 +817,6 @@ impl<'a> InferenceContext<'a> { } } - let ty = self.resolve_ty_as_possible(ty); self.infer_pat(*pat, &ty, BindingMode::default()); } Statement::Expr { expr, .. } => { @@ -894,7 +894,8 @@ impl<'a> InferenceContext<'a> { }; // Apply autoref so the below unification works correctly // FIXME: return correct autorefs from lookup_method - let actual_receiver_ty = match expected_receiver_ty.as_reference() { + let actual_receiver_ty = match self.resolve_ty_shallow(&expected_receiver_ty).as_reference() + { Some((_, lifetime, mutability)) => { TyKind::Ref(mutability, lifetime, derefed_receiver_ty).intern(&Interner) } @@ -974,6 +975,7 @@ impl<'a> InferenceContext<'a> { } fn register_obligations_for_call(&mut self, callable_ty: &Ty) { + let callable_ty = self.resolve_ty_shallow(&callable_ty); if let TyKind::FnDef(fn_def, parameters) = callable_ty.kind(&Interner) { let def: CallableDefId = from_chalk(self.db, *fn_def); let generic_predicates = self.db.generic_predicates(def.into()); diff --git a/crates/hir_ty/src/infer/pat.rs b/crates/hir_ty/src/infer/pat.rs index b15f4977dd9..9c8e3b6aea9 100644 --- a/crates/hir_ty/src/infer/pat.rs +++ b/crates/hir_ty/src/infer/pat.rs @@ -94,14 +94,15 @@ impl<'a> InferenceContext<'a> { pub(super) fn infer_pat( &mut self, pat: PatId, - mut expected: &Ty, + expected: &Ty, mut default_bm: BindingMode, ) -> Ty { let body = Arc::clone(&self.body); // avoid borrow checker problem + let mut expected = self.resolve_ty_shallow(expected); if is_non_ref_pat(&body, pat) { while let Some((inner, _lifetime, mutability)) = expected.as_reference() { - expected = inner; + expected = self.resolve_ty_shallow(inner); default_bm = match default_bm { BindingMode::Move => BindingMode::Ref(mutability), BindingMode::Ref(Mutability::Not) => BindingMode::Ref(Mutability::Not), @@ -147,9 +148,9 @@ impl<'a> InferenceContext<'a> { } Pat::Or(ref pats) => { if let Some((first_pat, rest)) = pats.split_first() { - let ty = self.infer_pat(*first_pat, expected, default_bm); + let ty = self.infer_pat(*first_pat, &expected, default_bm); for pat in rest { - self.infer_pat(*pat, expected, default_bm); + self.infer_pat(*pat, &expected, default_bm); } ty } else { @@ -173,13 +174,13 @@ impl<'a> InferenceContext<'a> { Pat::TupleStruct { path: p, args: subpats, ellipsis } => self.infer_tuple_struct_pat( p.as_deref(), subpats, - expected, + &expected, default_bm, pat, *ellipsis, ), Pat::Record { path: p, args: fields, ellipsis: _ } => { - self.infer_record_pat(p.as_deref(), fields, expected, default_bm, pat) + self.infer_record_pat(p.as_deref(), fields, &expected, default_bm, pat) } Pat::Path(path) => { // FIXME use correct resolver for the surrounding expression @@ -193,7 +194,7 @@ impl<'a> InferenceContext<'a> { BindingMode::convert(*mode) }; let inner_ty = if let Some(subpat) = subpat { - self.infer_pat(*subpat, expected, default_bm) + self.infer_pat(*subpat, &expected, default_bm) } else { expected.clone() }; @@ -206,7 +207,6 @@ impl<'a> InferenceContext<'a> { } BindingMode::Move => inner_ty.clone(), }; - let bound_ty = self.resolve_ty_as_possible(bound_ty); self.write_pat_ty(pat, bound_ty); return inner_ty; } @@ -265,13 +265,12 @@ impl<'a> InferenceContext<'a> { }; // use a new type variable if we got error type here let ty = self.insert_type_vars_shallow(ty); - if !self.unify(&ty, expected) { + if !self.unify(&ty, &expected) { self.result.type_mismatches.insert( pat.into(), TypeMismatch { expected: expected.clone(), actual: ty.clone() }, ); } - let ty = self.resolve_ty_as_possible(ty); self.write_pat_ty(pat, ty.clone()); ty } diff --git a/crates/hir_ty/src/infer/path.rs b/crates/hir_ty/src/infer/path.rs index fd366e1211a..14c99eafd31 100644 --- a/crates/hir_ty/src/infer/path.rs +++ b/crates/hir_ty/src/infer/path.rs @@ -65,7 +65,6 @@ impl<'a> InferenceContext<'a> { let typable: ValueTyDefId = match value { ValueNs::LocalBinding(pat) => { let ty = self.result.type_of_pat.get(pat)?.clone(); - let ty = self.resolve_ty_as_possible(ty); return Some(ty); } ValueNs::FunctionId(it) => it.into(), @@ -275,6 +274,7 @@ impl<'a> InferenceContext<'a> { name: &Name, id: ExprOrPatId, ) -> Option<(ValueNs, Option)> { + let ty = self.resolve_ty_shallow(ty); let (enum_id, subst) = match ty.as_adt() { Some((AdtId::EnumId(e), subst)) => (e, subst), _ => return None, diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs index 278127c69b5..539e124204d 100644 --- a/crates/hir_ty/src/infer/unify.rs +++ b/crates/hir_ty/src/infer/unify.rs @@ -24,6 +24,9 @@ impl<'a> InferenceContext<'a> { where T::Result: HasInterner, { + // try to resolve obligations before canonicalizing, since this might + // result in new knowledge about variables + self.resolve_obligations_as_possible(); self.table.canonicalize(t) } } @@ -216,7 +219,6 @@ impl<'a> InferenceTable<'a> { /// call). `make_ty` handles this already, but e.g. for field types we need /// to do it as well. pub(super) fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty { - let ty = self.resolve_ty_as_possible(ty); fold_tys( ty, |ty, _| match ty.kind(&Interner) { @@ -302,11 +304,6 @@ impl<'a> InferenceTable<'a> { self.resolve_with_fallback(ty, |_, _, d, _| d) } - // FIXME get rid of this, instead resolve shallowly where necessary - pub(crate) fn resolve_ty_as_possible(&mut self, ty: Ty) -> Ty { - self.resolve_ty_as_possible_inner(&mut Vec::new(), ty) - } - /// Unify two types and register new trait goals that arise from that. // TODO give these two functions better names pub(crate) fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool { @@ -344,36 +341,6 @@ impl<'a> InferenceTable<'a> { self.var_unification_table.normalize_ty_shallow(&Interner, ty).unwrap_or_else(|| ty.clone()) } - /// Resolves the type as far as currently possible, replacing type variables - /// by their known types. - fn resolve_ty_as_possible_inner(&mut self, tv_stack: &mut Vec, ty: Ty) -> Ty { - fold_tys( - ty, - |ty, _| match ty.kind(&Interner) { - &TyKind::InferenceVar(tv, kind) => { - if tv_stack.contains(&tv) { - // recursive type - return self.type_variable_table.fallback_value(tv, kind); - } - if let Some(known_ty) = self.var_unification_table.probe_var(tv) { - // known_ty may contain other variables that are known by now - tv_stack.push(tv); - let result = self.resolve_ty_as_possible_inner( - tv_stack, - known_ty.assert_ty_ref(&Interner).clone(), - ); - tv_stack.pop(); - result - } else { - ty - } - } - _ => ty, - }, - DebruijnIndex::INNERMOST, - ) - } - pub fn register_obligation(&mut self, goal: Goal) { let in_env = InEnvironment::new(&self.trait_env.env, goal); self.register_obligation_in_env(in_env) diff --git a/crates/hir_ty/src/tests/regression.rs b/crates/hir_ty/src/tests/regression.rs index 59a16f3906c..baef8159010 100644 --- a/crates/hir_ty/src/tests/regression.rs +++ b/crates/hir_ty/src/tests/regression.rs @@ -117,19 +117,19 @@ fn recursive_vars_2() { "#, expect![[r#" 10..79 '{ ...x)]; }': () - 20..21 'x': {unknown} - 24..31 'unknown': {unknown} + 20..21 'x': &{unknown} + 24..31 'unknown': &{unknown} 41..42 'y': {unknown} 45..52 'unknown': {unknown} - 58..76 '[(x, y..., &x)]': [({unknown}, {unknown}); 2] - 59..65 '(x, y)': ({unknown}, {unknown}) - 60..61 'x': {unknown} + 58..76 '[(x, y..., &x)]': [(&{unknown}, {unknown}); 2] + 59..65 '(x, y)': (&{unknown}, {unknown}) + 60..61 'x': &{unknown} 63..64 'y': {unknown} - 67..75 '(&y, &x)': (&{unknown}, &{unknown}) + 67..75 '(&y, &x)': (&{unknown}, {unknown}) 68..70 '&y': &{unknown} 69..70 'y': {unknown} - 72..74 '&x': &{unknown} - 73..74 'x': {unknown} + 72..74 '&x': &&{unknown} + 73..74 'x': &{unknown} "#]], ); } diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs index f80cf987985..a5a2df54cb6 100644 --- a/crates/hir_ty/src/tests/traits.rs +++ b/crates/hir_ty/src/tests/traits.rs @@ -3104,7 +3104,7 @@ fn foo() { 568..573 'f(&s)': FnOnce::Output), (&Option,)> 570..572 '&s': &Option 571..572 's': Option - 549..562: expected Box)>, got Box<|_| -> ()> + 549..562: expected Box)>, got Box<|{unknown}| -> ()> "#]], ); } From 9716c0b949b1a1a95b3f36928faed3abc21c0bda Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 16 May 2021 18:27:17 +0200 Subject: [PATCH 21/27] Deal with goals arising from unification --- crates/hir_ty/src/infer.rs | 6 +++--- crates/hir_ty/src/infer/coerce.rs | 18 ++++++++++-------- crates/hir_ty/src/infer/unify.rs | 13 +++++++------ crates/hir_ty/src/tests/regression.rs | 2 +- 4 files changed, 21 insertions(+), 18 deletions(-) diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs index ab742e2032e..db223401824 100644 --- a/crates/hir_ty/src/infer.rs +++ b/crates/hir_ty/src/infer.rs @@ -37,8 +37,8 @@ use syntax::SmolStr; use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty}; use crate::{ db::HirDatabase, fold_tys, infer::diagnostics::InferenceDiagnostic, - lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Interner, TyBuilder, TyExt, - TyKind, + lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Goal, Interner, TyBuilder, + TyExt, TyKind, }; // This lint has a false positive here. See the link below for details. @@ -104,7 +104,7 @@ impl Default for BindingMode { #[derive(Debug)] pub(crate) struct InferOk { - // obligations + goals: Vec>, } #[derive(Debug)] pub(crate) struct TypeError; diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs index 00b2b585f44..60186bc5f94 100644 --- a/crates/hir_ty/src/infer/coerce.rs +++ b/crates/hir_ty/src/infer/coerce.rs @@ -26,8 +26,8 @@ impl<'a> InferenceContext<'a> { return true; } match self.coerce_inner(from_ty, &to_ty) { - Ok(_result) => { - // TODO deal with goals + Ok(result) => { + self.table.register_infer_ok(result); true } Err(_) => { @@ -67,8 +67,9 @@ impl<'a> InferenceContext<'a> { let target_ty = TyKind::Function(sig.to_fn_ptr()).intern(&Interner); let result1 = self.coerce_inner(ty1.clone(), &target_ty); let result2 = self.coerce_inner(ty2.clone(), &target_ty); - if let (Ok(_result1), Ok(_result2)) = (result1, result2) { - // TODO deal with the goals + if let (Ok(result1), Ok(result2)) = (result1, result2) { + self.table.register_infer_ok(result1); + self.table.register_infer_ok(result2); return target_ty; } } @@ -104,7 +105,7 @@ impl<'a> InferenceContext<'a> { } _ => {} } - return Ok(InferOk {}); + return Ok(InferOk { goals: Vec::new() }); } // Consider coercing the subtype to a DST @@ -416,10 +417,11 @@ impl<'a> InferenceContext<'a> { }, ); } + // FIXME: should we accept ambiguous results here? _ => return Err(TypeError), }; - Ok(InferOk {}) + Ok(InferOk { goals: Vec::new() }) } } @@ -444,11 +446,11 @@ fn safe_to_unsafe_fn_ty(fn_ty: FnPointer) -> FnPointer { } } -fn coerce_mutabilities(from: Mutability, to: Mutability) -> InferResult { +fn coerce_mutabilities(from: Mutability, to: Mutability) -> Result<(), TypeError> { match (from, to) { (Mutability::Mut, Mutability::Mut) | (Mutability::Mut, Mutability::Not) - | (Mutability::Not, Mutability::Not) => Ok(InferOk {}), + | (Mutability::Not, Mutability::Not) => Ok(()), (Mutability::Not, Mutability::Mut) => Err(TypeError), } } diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs index 539e124204d..d872cdd58aa 100644 --- a/crates/hir_ty/src/infer/unify.rs +++ b/crates/hir_ty/src/infer/unify.rs @@ -307,12 +307,12 @@ impl<'a> InferenceTable<'a> { /// Unify two types and register new trait goals that arise from that. // TODO give these two functions better names pub(crate) fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool { - let _result = if let Ok(r) = self.unify_inner(ty1, ty2) { + let result = if let Ok(r) = self.unify_inner(ty1, ty2) { r } else { return false; }; - // TODO deal with new goals + self.register_infer_ok(result); true } @@ -327,10 +327,7 @@ impl<'a> InferenceTable<'a> { t1, t2, ) { - Ok(_result) => { - // TODO deal with new goals - Ok(InferOk {}) - } + Ok(result) => Ok(InferOk { goals: result.goals }), Err(chalk_ir::NoSolution) => Err(TypeError), } } @@ -353,6 +350,10 @@ impl<'a> InferenceTable<'a> { } } + pub fn register_infer_ok(&mut self, infer_ok: InferOk) { + infer_ok.goals.into_iter().for_each(|goal| self.register_obligation_in_env(goal)); + } + pub fn resolve_obligations_as_possible(&mut self) { let _span = profile::span("resolve_obligations_as_possible"); let mut changed = true; diff --git a/crates/hir_ty/src/tests/regression.rs b/crates/hir_ty/src/tests/regression.rs index baef8159010..ad9edf11cd7 100644 --- a/crates/hir_ty/src/tests/regression.rs +++ b/crates/hir_ty/src/tests/regression.rs @@ -758,7 +758,7 @@ fn issue_4885() { "#, expect![[r#" 136..139 'key': &K - 198..214 '{ ...key) }': {unknown} + 198..214 '{ ...key) }': impl Future>::Bar> 204..207 'bar': fn bar(&K) -> impl Future>::Bar> 204..212 'bar(key)': impl Future>::Bar> 208..211 'key': &K From 99c73537faba59c881805573442562418e0b650a Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 16 May 2021 20:04:00 +0200 Subject: [PATCH 22/27] Remove TypeVariableTable --- crates/hir_ty/src/infer/coerce.rs | 2 +- crates/hir_ty/src/infer/unify.rs | 102 ++++++++++++++---------------- 2 files changed, 48 insertions(+), 56 deletions(-) diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs index 60186bc5f94..20c512517b6 100644 --- a/crates/hir_ty/src/infer/coerce.rs +++ b/crates/hir_ty/src/infer/coerce.rs @@ -101,7 +101,7 @@ impl<'a> InferenceContext<'a> { // here, we would coerce from `!` to `?T`. match to_ty.kind(&Interner) { TyKind::InferenceVar(tv, TyVariableKind::General) => { - self.table.type_variable_table.set_diverging(*tv, true); + self.table.set_diverging(*tv, true); } _ => {} } diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs index d872cdd58aa..8674c1b0cea 100644 --- a/crates/hir_ty/src/infer/unify.rs +++ b/crates/hir_ty/src/infer/unify.rs @@ -127,29 +127,6 @@ pub(crate) fn unify( )) } -#[derive(Clone, Debug)] -pub(super) struct TypeVariableTable { - inner: Vec, -} - -impl TypeVariableTable { - pub(super) fn set_diverging(&mut self, iv: InferenceVar, diverging: bool) { - self.inner[iv.index() as usize].diverging = diverging; - } - - fn fallback_value(&self, iv: InferenceVar, kind: TyVariableKind) -> Ty { - match kind { - _ if self.inner.get(iv.index() as usize).map_or(false, |data| data.diverging) => { - TyKind::Never - } - TyVariableKind::General => TyKind::Error, - TyVariableKind::Integer => TyKind::Scalar(Scalar::Int(IntTy::I32)), - TyVariableKind::Float => TyKind::Scalar(Scalar::Float(FloatTy::F64)), - } - .intern(&Interner) - } -} - #[derive(Copy, Clone, Debug)] pub(crate) struct TypeVariableData { diverging: bool, @@ -162,7 +139,7 @@ pub(crate) struct InferenceTable<'a> { pub db: &'a dyn HirDatabase, pub trait_env: Arc, pub(super) var_unification_table: ChalkInferenceTable, - pub(super) type_variable_table: TypeVariableTable, + pub(super) type_variable_table: Vec, pending_obligations: Vec>>, } @@ -172,7 +149,7 @@ impl<'a> InferenceTable<'a> { db, trait_env, var_unification_table: ChalkInferenceTable::new(), - type_variable_table: TypeVariableTable { inner: Vec::new() }, + type_variable_table: Vec::new(), pending_obligations: Vec::new(), } } @@ -184,18 +161,38 @@ impl<'a> InferenceTable<'a> { /// marked as diverging if necessary, so that resolving them gives the right /// result. pub(super) fn propagate_diverging_flag(&mut self) { - for i in 0..self.type_variable_table.inner.len() { - if !self.type_variable_table.inner[i].diverging { + for i in 0..self.type_variable_table.len() { + if !self.type_variable_table[i].diverging { continue; } let v = InferenceVar::from(i as u32); let root = self.var_unification_table.inference_var_root(v); - if let Some(data) = self.type_variable_table.inner.get_mut(root.index() as usize) { + if let Some(data) = self.type_variable_table.get_mut(root.index() as usize) { data.diverging = true; } } } + pub(super) fn set_diverging(&mut self, iv: InferenceVar, diverging: bool) { + self.type_variable_table[iv.index() as usize].diverging = diverging; + } + + fn fallback_value(&self, iv: InferenceVar, kind: TyVariableKind) -> Ty { + match kind { + _ if self + .type_variable_table + .get(iv.index() as usize) + .map_or(false, |data| data.diverging) => + { + TyKind::Never + } + TyVariableKind::General => TyKind::Error, + TyVariableKind::Integer => TyKind::Scalar(Scalar::Int(IntTy::I32)), + TyVariableKind::Float => TyKind::Scalar(Scalar::Float(FloatTy::F64)), + } + .intern(&Interner) + } + pub(super) fn canonicalize + HasInterner>( &mut self, t: T, @@ -239,16 +236,19 @@ impl<'a> InferenceTable<'a> { var } + fn extend_type_variable_table(&mut self, to_index: usize) { + self.type_variable_table.extend( + (0..1 + to_index - self.type_variable_table.len()) + .map(|_| TypeVariableData { diverging: false }), + ); + } + fn new_var(&mut self, kind: TyVariableKind, diverging: bool) -> Ty { let var = self.var_unification_table.new_variable(UniverseIndex::ROOT); // Chalk might have created some type variables for its own purposes that we don't know about... - // TODO refactor this? - self.type_variable_table.inner.extend( - (0..1 + var.index() as usize - self.type_variable_table.inner.len()) - .map(|_| TypeVariableData { diverging: false }), - ); - assert_eq!(var.index() as usize, self.type_variable_table.inner.len() - 1); - self.type_variable_table.inner[var.index() as usize].diverging = diverging; + self.extend_type_variable_table(var.index() as usize); + assert_eq!(var.index() as usize, self.type_variable_table.len() - 1); + self.type_variable_table[var.index() as usize].diverging = diverging; var.to_ty_with_kind(&Interner, kind) } @@ -289,12 +289,7 @@ impl<'a> InferenceTable<'a> { T: HasInterner + Fold, { t.fold_with( - &mut resolve::Resolver { - type_variable_table: &self.type_variable_table, - var_unification_table: &mut self.var_unification_table, - var_stack, - fallback, - }, + &mut resolve::Resolver { table: self, var_stack, fallback }, DebruijnIndex::INNERMOST, ) .expect("fold failed unexpectedly") @@ -433,14 +428,12 @@ impl<'a> InferenceTable<'a> { impl<'a> fmt::Debug for InferenceTable<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("InferenceTable") - .field("num_vars", &self.type_variable_table.inner.len()) - .finish() + f.debug_struct("InferenceTable").field("num_vars", &self.type_variable_table.len()).finish() } } mod resolve { - use super::{ChalkInferenceTable, TypeVariableTable}; + use super::InferenceTable; use crate::{ ConcreteConst, Const, ConstData, ConstValue, DebruijnIndex, GenericArg, InferenceVar, Interner, Ty, TyVariableKind, VariableKind, @@ -452,13 +445,12 @@ mod resolve { }; use hir_def::type_ref::ConstScalar; - pub(super) struct Resolver<'a, F> { - pub type_variable_table: &'a TypeVariableTable, - pub var_unification_table: &'a mut ChalkInferenceTable, + pub(super) struct Resolver<'a, 'b, F> { + pub table: &'a mut InferenceTable<'b>, pub var_stack: &'a mut Vec, pub fallback: F, } - impl<'a, 'i, F> Folder<'i, Interner> for Resolver<'a, F> + impl<'a, 'b, 'i, F> Folder<'i, Interner> for Resolver<'a, 'b, F> where F: Fn(InferenceVar, VariableKind, GenericArg, DebruijnIndex) -> GenericArg + 'i, { @@ -476,15 +468,15 @@ mod resolve { kind: TyVariableKind, outer_binder: DebruijnIndex, ) -> Fallible { - let var = self.var_unification_table.inference_var_root(var); + let var = self.table.var_unification_table.inference_var_root(var); if self.var_stack.contains(&var) { // recursive type - let default = self.type_variable_table.fallback_value(var, kind).cast(&Interner); + let default = self.table.fallback_value(var, kind).cast(&Interner); return Ok((self.fallback)(var, VariableKind::Ty(kind), default, outer_binder) .assert_ty_ref(&Interner) .clone()); } - let result = if let Some(known_ty) = self.var_unification_table.probe_var(var) { + let result = if let Some(known_ty) = self.table.var_unification_table.probe_var(var) { // known_ty may contain other variables that are known by now self.var_stack.push(var); let result = @@ -492,7 +484,7 @@ mod resolve { self.var_stack.pop(); result.assert_ty_ref(&Interner).clone() } else { - let default = self.type_variable_table.fallback_value(var, kind).cast(&Interner); + let default = self.table.fallback_value(var, kind).cast(&Interner); (self.fallback)(var, VariableKind::Ty(kind), default, outer_binder) .assert_ty_ref(&Interner) .clone() @@ -506,7 +498,7 @@ mod resolve { var: InferenceVar, outer_binder: DebruijnIndex, ) -> Fallible { - let var = self.var_unification_table.inference_var_root(var); + let var = self.table.var_unification_table.inference_var_root(var); let default = ConstData { ty: ty.clone(), value: ConstValue::Concrete(ConcreteConst { interned: ConstScalar::Unknown }), @@ -519,7 +511,7 @@ mod resolve { .assert_const_ref(&Interner) .clone()); } - let result = if let Some(known_ty) = self.var_unification_table.probe_var(var) { + let result = if let Some(known_ty) = self.table.var_unification_table.probe_var(var) { // known_ty may contain other variables that are known by now self.var_stack.push(var); let result = From 556c9cebdb91278702263df4ac8c99ec24ab331a Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 21 May 2021 17:41:20 +0200 Subject: [PATCH 23/27] Refactor expectation handling So as to not use `TyKind::Error` as "no expectation". --- crates/hir_ty/src/infer.rs | 59 +++++++++++++++-------- crates/hir_ty/src/infer/coerce.rs | 4 -- crates/hir_ty/src/infer/expr.rs | 79 +++++++++++++++++++------------ 3 files changed, 88 insertions(+), 54 deletions(-) diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs index db223401824..a137c0f9282 100644 --- a/crates/hir_ty/src/infer.rs +++ b/crates/hir_ty/src/infer.rs @@ -370,10 +370,6 @@ impl<'a> InferenceContext<'a> { } fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool { - // TODO handle expectations properly - if ty2.is_unknown() { - return true; - } self.table.unify(ty1, ty2) } @@ -679,17 +675,23 @@ impl<'a> InferenceContext<'a> { /// When inferring an expression, we propagate downward whatever type hint we /// are able in the form of an `Expectation`. #[derive(Clone, PartialEq, Eq, Debug)] -struct Expectation { - ty: Ty, - /// See the `rvalue_hint` method. - rvalue_hint: bool, +enum Expectation { + None, + HasType(Ty), + // Castable(Ty), // rustc has this, we currently just don't propagate an expectation for casts + RValueLikeUnsized(Ty), } impl Expectation { /// The expectation that the type of the expression needs to equal the given /// type. fn has_type(ty: Ty) -> Self { - Expectation { ty, rvalue_hint: false } + if ty.is_unknown() { + // FIXME: get rid of this? + Expectation::None + } else { + Expectation::HasType(ty) + } } /// The following explanation is copied straight from rustc: @@ -713,24 +715,41 @@ impl Expectation { /// See the test case `test/ui/coerce-expect-unsized.rs` and #20169 /// for examples of where this comes up,. fn rvalue_hint(ty: Ty) -> Self { - Expectation { ty, rvalue_hint: true } + match ty.strip_references().kind(&Interner) { + TyKind::Slice(_) | TyKind::Str | TyKind::Dyn(_) => Expectation::RValueLikeUnsized(ty), + _ => Expectation::has_type(ty), + } } /// This expresses no expectation on the type. fn none() -> Self { - Expectation { - // FIXME - ty: TyKind::Error.intern(&Interner), - rvalue_hint: false, + 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::RValueLikeUnsized(t) => { + Expectation::RValueLikeUnsized(table.resolve_ty_shallow(t)) + } } } - fn coercion_target(&self) -> Ty { - if self.rvalue_hint { - // FIXME - TyKind::Error.intern(&Interner) - } else { - self.ty.clone() + fn to_option(&self, table: &mut unify::InferenceTable) -> Option { + 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 { + match self { + Expectation::HasType(t) => Some(table.resolve_ty_shallow(t)), + // Expectation::Castable(_) | + Expectation::RValueLikeUnsized(_) | Expectation::None => None, } } } diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs index 20c512517b6..03dd6ae763b 100644 --- a/crates/hir_ty/src/infer/coerce.rs +++ b/crates/hir_ty/src/infer/coerce.rs @@ -21,10 +21,6 @@ impl<'a> InferenceContext<'a> { pub(super) fn coerce(&mut self, from_ty: &Ty, to_ty: &Ty) -> bool { let from_ty = self.resolve_ty_shallow(from_ty); let to_ty = self.resolve_ty_shallow(to_ty); - // TODO handle expectations properly - if to_ty.is_unknown() { - return true; - } match self.coerce_inner(from_ty, &to_ty) { Ok(result) => { self.table.register_infer_ok(result); diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index f5782ab2461..4ef847d3a53 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs @@ -39,12 +39,14 @@ impl<'a> InferenceContext<'a> { // Any expression that produces a value of type `!` must have diverged self.diverges = Diverges::Always; } - let could_unify = self.unify(&ty, &expected.ty); - if !could_unify { - self.result.type_mismatches.insert( - tgt_expr.into(), - TypeMismatch { expected: expected.ty.clone(), actual: ty.clone() }, - ); + if let Some(expected_ty) = expected.only_has_type(&mut self.table) { + let could_unify = self.unify(&ty, &expected_ty); + if !could_unify { + self.result.type_mismatches.insert( + tgt_expr.into(), + TypeMismatch { expected: expected_ty.clone(), actual: ty.clone() }, + ); + } } ty } @@ -53,18 +55,20 @@ impl<'a> InferenceContext<'a> { /// Return the type after possible coercion. pub(super) fn infer_expr_coerce(&mut self, expr: ExprId, expected: &Expectation) -> Ty { let ty = self.infer_expr_inner(expr, &expected); - let ty = if !self.coerce(&ty, &expected.coercion_target()) { - self.result.type_mismatches.insert( - expr.into(), - TypeMismatch { expected: expected.ty.clone(), actual: ty.clone() }, - ); - // Return actual type when type mismatch. - // This is needed for diagnostic when return type mismatch. - ty - } else if expected.coercion_target().is_unknown() { - ty + let ty = if let Some(target) = expected.only_has_type(&mut self.table) { + if !self.coerce(&ty, &target) { + self.result.type_mismatches.insert( + expr.into(), + TypeMismatch { expected: target.clone(), actual: ty.clone() }, + ); + // Return actual type when type mismatch. + // This is needed for diagnostic when return type mismatch. + ty + } else { + target.clone() + } } else { - expected.ty.clone() + ty }; ty @@ -280,7 +284,9 @@ impl<'a> InferenceContext<'a> { // Eagerly try to relate the closure type with the expected // type, otherwise we often won't have enough information to // infer the body. - self.coerce(&closure_ty, &expected.ty); + if let Some(t) = expected.only_has_type(&mut self.table) { + self.coerce(&closure_ty, &t); + } // Now go through the argument patterns for (arg_pat, arg_ty) in args.iter().zip(sig_tys) { @@ -413,7 +419,9 @@ impl<'a> InferenceContext<'a> { self.write_variant_resolution(tgt_expr.into(), variant); } - self.unify(&ty, &expected.ty); + if let Some(t) = expected.only_has_type(&mut self.table) { + self.unify(&ty, &t); + } let substs = ty .as_adt() @@ -516,6 +524,7 @@ impl<'a> InferenceContext<'a> { self.resolve_associated_type(inner_ty, self.resolve_ops_try_ok()) } Expr::Cast { expr, type_ref } => { + // FIXME: propagate the "castable to" expectation (and find a test case that shows this is necessary) let _inner_ty = self.infer_expr_inner(*expr, &Expectation::none()); let cast_ty = self.make_ty(type_ref); // FIXME check the cast... @@ -523,14 +532,16 @@ impl<'a> InferenceContext<'a> { } Expr::Ref { expr, rawness, mutability } => { let mutability = lower_to_chalk_mutability(*mutability); - let expectation = if let Some((exp_inner, exp_rawness, exp_mutability)) = - &self.resolve_ty_shallow(&expected.ty).as_reference_or_ptr() + let expectation = if let Some((exp_inner, exp_rawness, exp_mutability)) = expected + .only_has_type(&mut self.table) + .as_ref() + .and_then(|t| t.as_reference_or_ptr()) { - if *exp_mutability == Mutability::Mut && mutability == Mutability::Not { + if exp_mutability == Mutability::Mut && mutability == Mutability::Not { // FIXME: record type error - expected mut reference but found shared ref, // which cannot be coerced } - if *exp_rawness == Rawness::Ref && *rawness == Rawness::RawPtr { + if exp_rawness == Rawness::Ref && *rawness == Rawness::RawPtr { // FIXME: record type error - expected reference but found ptr, // which cannot be coerced } @@ -701,8 +712,12 @@ impl<'a> InferenceContext<'a> { } } Expr::Tuple { exprs } => { - let mut tys = match self.resolve_ty_shallow(&expected.ty).kind(&Interner) { - TyKind::Tuple(_, substs) => substs + let mut tys = match expected + .only_has_type(&mut self.table) + .as_ref() + .map(|t| t.kind(&Interner)) + { + Some(TyKind::Tuple(_, substs)) => substs .iter(&Interner) .map(|a| a.assert_ty_ref(&Interner).clone()) .chain(repeat_with(|| self.table.new_type_var())) @@ -718,14 +733,16 @@ impl<'a> InferenceContext<'a> { TyKind::Tuple(tys.len(), Substitution::from_iter(&Interner, tys)).intern(&Interner) } Expr::Array(array) => { - let elem_ty = match self.resolve_ty_shallow(&expected.ty).kind(&Interner) { - TyKind::Array(st, _) | TyKind::Slice(st) => st.clone(), - _ => self.table.new_type_var(), - }; + let elem_ty = + match expected.to_option(&mut self.table).as_ref().map(|t| t.kind(&Interner)) { + Some(TyKind::Array(st, _)) | Some(TyKind::Slice(st)) => st.clone(), + _ => self.table.new_type_var(), + }; let len = match array { Array::ElementList(items) => { for expr in items.iter() { + // FIXME: use CoerceMany (coerce_merge_branch) self.infer_expr_coerce(*expr, &Expectation::has_type(elem_ty.clone())); } Some(items.len() as u64) @@ -839,7 +856,9 @@ impl<'a> InferenceContext<'a> { // we don't even make an attempt at coercion self.table.new_maybe_never_var() } else { - self.coerce(&TyBuilder::unit(), &expected.coercion_target()); + if let Some(t) = expected.only_has_type(&mut self.table) { + self.coerce(&TyBuilder::unit(), &t); + } TyBuilder::unit() } }; From 6e5637983c155b2914647bb5e437337a44a63a7d Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 21 May 2021 17:48:15 +0200 Subject: [PATCH 24/27] Record type mismatches for failed coercions in match etc. --- crates/hir_ty/src/infer/coerce.rs | 14 +++++++++----- crates/hir_ty/src/infer/expr.rs | 22 ++++++++++++---------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs index 03dd6ae763b..40b4db92639 100644 --- a/crates/hir_ty/src/infer/coerce.rs +++ b/crates/hir_ty/src/infer/coerce.rs @@ -6,11 +6,11 @@ //! librustc_typeck/check/coercion.rs. use chalk_ir::{cast::Cast, Mutability, TyVariableKind}; -use hir_def::lang_item::LangItemTarget; +use hir_def::{expr::ExprId, lang_item::LangItemTarget}; use crate::{ - autoderef, static_lifetime, Canonical, DomainGoal, FnPointer, FnSig, Interner, Solution, - Substitution, Ty, TyBuilder, TyExt, TyKind, + autoderef, infer::TypeMismatch, static_lifetime, Canonical, DomainGoal, FnPointer, FnSig, + Interner, Solution, Substitution, Ty, TyBuilder, TyExt, TyKind, }; use super::{InEnvironment, InferOk, InferResult, InferenceContext, TypeError}; @@ -40,7 +40,7 @@ impl<'a> InferenceContext<'a> { /// coerce both to function pointers; /// - if we were concerned with lifetime subtyping, we'd need to look for a /// least upper bound. - pub(super) fn coerce_merge_branch(&mut self, ty1: &Ty, ty2: &Ty) -> Ty { + pub(super) fn coerce_merge_branch(&mut self, id: Option, ty1: &Ty, ty2: &Ty) -> Ty { let ty1 = self.resolve_ty_shallow(ty1); let ty2 = self.resolve_ty_shallow(ty2); // Special case: two function types. Try to coerce both to @@ -80,7 +80,11 @@ impl<'a> InferenceContext<'a> { } else if self.coerce(&ty1, &ty2) { ty2.clone() } else { - // TODO record a type mismatch + if let Some(id) = id { + self.result + .type_mismatches + .insert(id.into(), TypeMismatch { expected: ty1.clone(), actual: ty2.clone() }); + } cov_mark::hit!(coerce_merge_fail_fallback); ty1.clone() } diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index 4ef847d3a53..08c05c67cc9 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs @@ -138,13 +138,14 @@ impl<'a> InferenceContext<'a> { let mut result_ty = self.table.new_type_var(); let then_ty = self.infer_expr_inner(*then_branch, &expected); both_arms_diverge &= mem::replace(&mut self.diverges, Diverges::Maybe); - result_ty = self.coerce_merge_branch(&result_ty, &then_ty); + result_ty = self.coerce_merge_branch(Some(*then_branch), &result_ty, &then_ty); let else_ty = match else_branch { Some(else_branch) => self.infer_expr_inner(*else_branch, &expected), None => TyBuilder::unit(), }; both_arms_diverge &= self.diverges; - result_ty = self.coerce_merge_branch(&result_ty, &else_ty); + // FIXME: create a synthetic `else {}` so we have something to refer to here instead of None? + result_ty = self.coerce_merge_branch(*else_branch, &result_ty, &else_ty); self.diverges = condition_diverges | both_arms_diverge; @@ -358,7 +359,7 @@ impl<'a> InferenceContext<'a> { let arm_ty = self.infer_expr_inner(arm.expr, &expected); all_arms_diverge &= self.diverges; - result_ty = self.coerce_merge_branch(&result_ty, &arm_ty); + result_ty = self.coerce_merge_branch(Some(arm.expr), &result_ty, &arm_ty); } self.diverges = matchee_diverges | all_arms_diverge; @@ -372,12 +373,6 @@ impl<'a> InferenceContext<'a> { } Expr::Continue { .. } => TyKind::Never.intern(&Interner), Expr::Break { expr, label } => { - let val_ty = if let Some(expr) = expr { - self.infer_expr(*expr, &Expectation::none()) - } else { - TyBuilder::unit() - }; - let last_ty = if let Some(ctxt) = find_breakable(&mut self.breakables, label.as_ref()) { ctxt.break_ty.clone() @@ -385,7 +380,14 @@ impl<'a> InferenceContext<'a> { self.err_ty() }; - let merged_type = self.coerce_merge_branch(&last_ty, &val_ty); + let val_ty = if let Some(expr) = expr { + self.infer_expr(*expr, &Expectation::none()) + } else { + TyBuilder::unit() + }; + + // FIXME: create a synthetic `()` during lowering so we have something to refer to here? + let merged_type = self.coerce_merge_branch(*expr, &last_ty, &val_ty); if let Some(ctxt) = find_breakable(&mut self.breakables, label.as_ref()) { ctxt.break_ty = merged_type; From e9d1550001f79390284cf7f0e958981f387a0c58 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 21 May 2021 18:07:30 +0200 Subject: [PATCH 25/27] Fix test after rebase --- crates/hir_ty/src/tests/patterns.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/hir_ty/src/tests/patterns.rs b/crates/hir_ty/src/tests/patterns.rs index ddbadbe4007..cd08b5c7a57 100644 --- a/crates/hir_ty/src/tests/patterns.rs +++ b/crates/hir_ty/src/tests/patterns.rs @@ -747,7 +747,7 @@ fn foo(tuple: (u8, i16, f32)) { 209..210 '_': (u8, i16, f32) 214..216 '{}': () 136..142: expected (u8, i16, f32), got (u8, i16) - 170..182: expected (u8, i16, f32), got (u8, i16, f32, _) + 170..182: expected (u8, i16, f32), got (u8, i16, f32, {unknown}) "#]], ); } From 67f1a08fd8eff669a997950cac6c538dd96718b3 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 21 May 2021 18:20:56 +0200 Subject: [PATCH 26/27] Some remaining cleanups --- crates/hir_ty/src/infer.rs | 4 ---- crates/hir_ty/src/infer/coerce.rs | 20 ++++++++++---------- crates/hir_ty/src/infer/unify.rs | 7 +++---- 3 files changed, 13 insertions(+), 18 deletions(-) diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs index a137c0f9282..f1cebbdb983 100644 --- a/crates/hir_ty/src/infer.rs +++ b/crates/hir_ty/src/infer.rs @@ -373,10 +373,6 @@ impl<'a> InferenceContext<'a> { self.table.unify(ty1, ty2) } - fn unify_inner(&mut self, ty1: &Ty, ty2: &Ty) -> InferResult { - self.table.unify_inner(ty1, ty2) - } - fn resolve_ty_shallow(&mut self, ty: &Ty) -> Ty { self.resolve_obligations_as_possible(); self.table.resolve_ty_shallow(ty) diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs index 40b4db92639..765a02b1ced 100644 --- a/crates/hir_ty/src/infer/coerce.rs +++ b/crates/hir_ty/src/infer/coerce.rs @@ -146,7 +146,7 @@ impl<'a> InferenceContext<'a> { } _ => { // Otherwise, just use unification rules. - self.unify_inner(&from_ty, to_ty) + self.table.try_unify(&from_ty, to_ty) } } } @@ -155,7 +155,7 @@ impl<'a> InferenceContext<'a> { let (_is_ref, from_mt, from_inner) = match from_ty.kind(&Interner) { TyKind::Ref(mt, _, ty) => (true, mt, ty), TyKind::Raw(mt, ty) => (false, mt, ty), - _ => return self.unify_inner(&from_ty, to_ty), + _ => return self.table.try_unify(&from_ty, to_ty), }; coerce_mutabilities(*from_mt, to_mt)?; @@ -163,7 +163,7 @@ impl<'a> InferenceContext<'a> { // Check that the types which they point at are compatible. let from_raw = TyKind::Raw(to_mt, from_inner.clone()).intern(&Interner); // FIXME: behavior differs based on is_ref once we're computing adjustments - self.unify_inner(&from_raw, to_ty) + self.table.try_unify(&from_raw, to_ty) } /// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`. @@ -174,7 +174,7 @@ impl<'a> InferenceContext<'a> { TyKind::Ref(mt, _, _) => { coerce_mutabilities(*mt, to_mt)?; } - _ => return self.unify_inner(&from_ty, to_ty), + _ => return self.table.try_unify(&from_ty, to_ty), }; // NOTE: this code is mostly copied and adapted from rustc, and @@ -228,7 +228,7 @@ impl<'a> InferenceContext<'a> { // from `&mut T` to `&U`. let lt = static_lifetime(); // FIXME: handle lifetimes correctly, see rustc let derefd_from_ty = TyKind::Ref(to_mt, lt, referent_ty).intern(&Interner); - match self.unify_inner(&derefd_from_ty, to_ty) { + match self.table.try_unify(&derefd_from_ty, to_ty) { Ok(result) => { found = Some(result); break; @@ -274,7 +274,7 @@ impl<'a> InferenceContext<'a> { Ok(ok) } - _ => self.unify_inner(&from_ty, to_ty), + _ => self.table.try_unify(&from_ty, to_ty), } } @@ -299,10 +299,10 @@ impl<'a> InferenceContext<'a> { { let from_unsafe = TyKind::Function(safe_to_unsafe_fn_ty(from_fn_ptr.clone())).intern(&Interner); - return self.unify_inner(&from_unsafe, to_ty); + return self.table.try_unify(&from_unsafe, to_ty); } } - self.unify_inner(&from_ty, to_ty) + self.table.try_unify(&from_ty, to_ty) } /// Attempts to coerce from the type of a non-capturing closure into a @@ -323,9 +323,9 @@ impl<'a> InferenceContext<'a> { // `unsafe fn(arg0,arg1,...) -> _` let safety = fn_ty.sig.safety; let pointer_ty = coerce_closure_fn_ty(from_substs, safety); - self.unify_inner(&pointer_ty, to_ty) + self.table.try_unify(&pointer_ty, to_ty) } - _ => self.unify_inner(&from_ty, to_ty), + _ => self.table.try_unify(&from_ty, to_ty), } } diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs index 8674c1b0cea..4987795e031 100644 --- a/crates/hir_ty/src/infer/unify.rs +++ b/crates/hir_ty/src/infer/unify.rs @@ -70,7 +70,7 @@ impl> Canonicalized { let ty = ctx.normalize_associated_types_in(new_vars.apply(ty.clone(), &Interner)); ctx.unify(var.assert_ty_ref(&Interner), &ty); } else { - let _ = ctx.unify_inner(&var, &new_vars.apply(v.clone(), &Interner)); + let _ = ctx.try_unify(&var, &new_vars.apply(v.clone(), &Interner)); } } } @@ -300,9 +300,8 @@ impl<'a> InferenceTable<'a> { } /// Unify two types and register new trait goals that arise from that. - // TODO give these two functions better names pub(crate) fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool { - let result = if let Ok(r) = self.unify_inner(ty1, ty2) { + let result = if let Ok(r) = self.try_unify(ty1, ty2) { r } else { return false; @@ -313,7 +312,7 @@ impl<'a> InferenceTable<'a> { /// Unify two types and return new trait goals arising from it, so the /// caller needs to deal with them. - pub(crate) fn unify_inner>(&mut self, t1: &T, t2: &T) -> InferResult { + pub(crate) fn try_unify>(&mut self, t1: &T, t2: &T) -> InferResult { match self.var_unification_table.relate( &Interner, &self.db, From ef558c97d09b0be8639c92f490e5ad380aa04288 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 21 May 2021 19:50:18 +0200 Subject: [PATCH 27/27] Clean up visibilities --- crates/hir_ty/src/infer/unify.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs index 4987795e031..21d3fb54e9d 100644 --- a/crates/hir_ty/src/infer/unify.rs +++ b/crates/hir_ty/src/infer/unify.rs @@ -136,10 +136,10 @@ type ChalkInferenceTable = chalk_solve::infer::InferenceTable; #[derive(Clone)] pub(crate) struct InferenceTable<'a> { - pub db: &'a dyn HirDatabase, - pub trait_env: Arc, - pub(super) var_unification_table: ChalkInferenceTable, - pub(super) type_variable_table: Vec, + pub(crate) db: &'a dyn HirDatabase, + pub(crate) trait_env: Arc, + var_unification_table: ChalkInferenceTable, + type_variable_table: Vec, pending_obligations: Vec>>, } @@ -332,7 +332,7 @@ impl<'a> InferenceTable<'a> { self.var_unification_table.normalize_ty_shallow(&Interner, ty).unwrap_or_else(|| ty.clone()) } - pub fn register_obligation(&mut self, goal: Goal) { + pub(crate) fn register_obligation(&mut self, goal: Goal) { let in_env = InEnvironment::new(&self.trait_env.env, goal); self.register_obligation_in_env(in_env) } @@ -344,11 +344,11 @@ impl<'a> InferenceTable<'a> { } } - pub fn register_infer_ok(&mut self, infer_ok: InferOk) { + pub(crate) fn register_infer_ok(&mut self, infer_ok: InferOk) { infer_ok.goals.into_iter().for_each(|goal| self.register_obligation_in_env(goal)); } - pub fn resolve_obligations_as_possible(&mut self) { + pub(crate) fn resolve_obligations_as_possible(&mut self) { let _span = profile::span("resolve_obligations_as_possible"); let mut changed = true; let mut obligations = Vec::new(); @@ -445,9 +445,9 @@ mod resolve { use hir_def::type_ref::ConstScalar; pub(super) struct Resolver<'a, 'b, F> { - pub table: &'a mut InferenceTable<'b>, - pub var_stack: &'a mut Vec, - pub fallback: F, + pub(super) table: &'a mut InferenceTable<'b>, + pub(super) var_stack: &'a mut Vec, + pub(super) fallback: F, } impl<'a, 'b, 'i, F> Folder<'i, Interner> for Resolver<'a, 'b, F> where