From 7ec3b66f7a3ac0a33cf435bc3596fdac542fc52a Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 20 Mar 2021 11:23:59 +0100 Subject: Turn Obligation into something similar to chalk_ir::DomainGoal This includes starting to make use of Chalk's `Cast` trait. --- crates/hir/src/lib.rs | 11 ++++--- crates/hir_ty/src/autoderef.rs | 9 +++--- crates/hir_ty/src/chalk_cast.rs | 53 +++++++++++++++++++++++++++++++ crates/hir_ty/src/db.rs | 2 +- crates/hir_ty/src/display.rs | 20 +++--------- crates/hir_ty/src/infer.rs | 12 +++---- crates/hir_ty/src/infer/coerce.rs | 7 ++-- crates/hir_ty/src/infer/expr.rs | 25 +++++++-------- crates/hir_ty/src/infer/path.rs | 12 ++++--- crates/hir_ty/src/infer/unify.rs | 13 +++----- crates/hir_ty/src/lib.rs | 7 ++-- crates/hir_ty/src/method_resolution.rs | 6 ++-- crates/hir_ty/src/traits.rs | 34 ++++++++------------ crates/hir_ty/src/traits/chalk/mapping.rs | 10 +++--- 14 files changed, 130 insertions(+), 91 deletions(-) create mode 100644 crates/hir_ty/src/chalk_cast.rs diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 268a2b901..30e577671 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -56,8 +56,8 @@ use hir_ty::{ primitive::UintTy, to_assoc_type_id, traits::{FnTrait, Solution, SolutionVariables}, - AliasEq, AliasTy, BoundVar, CallableDefId, CallableSig, Canonical, DebruijnIndex, - InEnvironment, Interner, Obligation, ProjectionTy, Scalar, Substitution, Ty, TyDefId, TyKind, + AliasEq, AliasTy, BoundVar, CallableDefId, CallableSig, Canonical, Cast, DebruijnIndex, + InEnvironment, Interner, ProjectionTy, Scalar, Substitution, Ty, TyDefId, TyKind, TyVariableKind, WhereClause, }; use itertools::Itertools; @@ -1767,7 +1767,7 @@ impl Type { let goal = Canonical { value: hir_ty::InEnvironment::new( self.ty.environment.clone(), - hir_ty::Obligation::Trait(trait_ref), + trait_ref.cast(&Interner), ), kinds: Arc::new([]), }; @@ -1789,14 +1789,15 @@ impl Type { let goal = Canonical { value: InEnvironment::new( self.ty.environment.clone(), - Obligation::AliasEq(AliasEq { + AliasEq { alias: AliasTy::Projection(ProjectionTy { associated_ty_id: to_assoc_type_id(alias.id), substitution: subst, }), ty: TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)) .intern(&Interner), - }), + } + .cast(&Interner), ), kinds: Arc::new([TyVariableKind::General]), }; diff --git a/crates/hir_ty/src/autoderef.rs b/crates/hir_ty/src/autoderef.rs index 33b966026..23ab042c1 100644 --- a/crates/hir_ty/src/autoderef.rs +++ b/crates/hir_ty/src/autoderef.rs @@ -6,6 +6,7 @@ use std::iter::successors; use base_db::CrateId; +use chalk_ir::cast::Cast; use hir_def::lang_item::LangItemTarget; use hir_expand::name::name; use log::{info, warn}; @@ -15,8 +16,8 @@ use crate::{ to_assoc_type_id, to_chalk_trait_id, traits::{InEnvironment, Solution}, utils::generics, - AliasEq, AliasTy, BoundVar, Canonical, DebruijnIndex, Interner, Obligation, ProjectionTy, - Substitution, TraitRef, Ty, TyKind, + AliasEq, AliasTy, BoundVar, Canonical, DebruijnIndex, Interner, ProjectionTy, Substitution, + TraitRef, Ty, TyKind, }; const AUTODEREF_RECURSION_LIMIT: usize = 10; @@ -74,7 +75,7 @@ fn deref_by_trait( let implements_goal = Canonical { kinds: ty.value.kinds.clone(), value: InEnvironment { - value: Obligation::Trait(trait_ref), + value: trait_ref.cast(&Interner), environment: ty.environment.clone(), }, }; @@ -92,7 +93,7 @@ fn deref_by_trait( .intern(&Interner), }; - let obligation = super::Obligation::AliasEq(projection); + let obligation = projection.cast(&Interner); let in_env = InEnvironment { value: obligation, environment: ty.environment }; diff --git a/crates/hir_ty/src/chalk_cast.rs b/crates/hir_ty/src/chalk_cast.rs new file mode 100644 index 000000000..bf884ae15 --- /dev/null +++ b/crates/hir_ty/src/chalk_cast.rs @@ -0,0 +1,53 @@ +//! Implementations of the Chalk `Cast` trait for our types. + +use chalk_ir::{ + cast::{Cast, CastTo}, + interner::HasInterner, +}; + +use crate::{AliasEq, DomainGoal, Interner, TraitRef, WhereClause}; + +macro_rules! has_interner { + ($t:ty) => { + impl HasInterner for $t { + type Interner = crate::Interner; + } + }; +} + +has_interner!(WhereClause); +has_interner!(DomainGoal); + +impl CastTo for TraitRef { + fn cast_to(self, _interner: &Interner) -> WhereClause { + WhereClause::Implemented(self) + } +} + +impl CastTo for AliasEq { + fn cast_to(self, _interner: &Interner) -> WhereClause { + WhereClause::AliasEq(self) + } +} + +impl CastTo for WhereClause { + fn cast_to(self, _interner: &Interner) -> DomainGoal { + DomainGoal::Holds(self) + } +} + +macro_rules! transitive_impl { + ($a:ty, $b:ty, $c:ty) => { + impl CastTo<$c> for $a { + fn cast_to(self, interner: &Interner) -> $c { + self.cast::<$b>(interner).cast(interner) + } + } + }; +} + +// In Chalk, these can be done as blanket impls, but that doesn't work here +// because of coherence + +transitive_impl!(TraitRef, WhereClause, DomainGoal); +transitive_impl!(AliasEq, WhereClause, DomainGoal); diff --git a/crates/hir_ty/src/db.rs b/crates/hir_ty/src/db.rs index 300da0f38..91a2e0b5b 100644 --- a/crates/hir_ty/src/db.rs +++ b/crates/hir_ty/src/db.rs @@ -119,7 +119,7 @@ pub trait HirDatabase: DefDatabase + Upcast { fn trait_solve( &self, krate: CrateId, - goal: crate::Canonical>, + goal: crate::Canonical>, ) -> Option; #[salsa::invoke(crate::traits::chalk::program_clauses_for_chalk_env_query)] diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs index 4765c99a4..3845009ae 100644 --- a/crates/hir_ty/src/display.rs +++ b/crates/hir_ty/src/display.rs @@ -19,7 +19,7 @@ use hir_expand::name::Name; use crate::{ db::HirDatabase, from_assoc_type_id, from_foreign_def_id, from_placeholder_idx, primitive, to_assoc_type_id, traits::chalk::from_chalk, utils::generics, AdtId, AliasEq, AliasTy, - CallableDefId, CallableSig, ImplTraitId, Interner, Lifetime, Obligation, OpaqueTy, + CallableDefId, CallableSig, DomainGoal, ImplTraitId, Interner, Lifetime, OpaqueTy, ProjectionTy, Scalar, Substitution, TraitRef, Ty, TyKind, WhereClause, }; @@ -805,22 +805,12 @@ impl HirDisplay for Lifetime { } } -impl HirDisplay for Obligation { +impl HirDisplay for DomainGoal { fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { match self { - Obligation::Trait(tr) => { - write!(f, "Implements(")?; - tr.hir_fmt(f)?; - write!(f, ")") - } - Obligation::AliasEq(AliasEq { alias, ty }) => { - write!(f, "Normalize(")?; - match alias { - AliasTy::Projection(projection_ty) => projection_ty.hir_fmt(f)?, - AliasTy::Opaque(opaque) => opaque.hir_fmt(f)?, - } - write!(f, " => ")?; - ty.hir_fmt(f)?; + DomainGoal::Holds(wc) => { + write!(f, "Holds(")?; + wc.hir_fmt(f)?; write!(f, ")") } } diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs index 82186979a..b9e434c78 100644 --- a/crates/hir_ty/src/infer.rs +++ b/crates/hir_ty/src/infer.rs @@ -18,7 +18,7 @@ use std::mem; use std::ops::Index; use std::sync::Arc; -use chalk_ir::Mutability; +use chalk_ir::{cast::Cast, Mutability}; use hir_def::{ body::Body, data::{ConstData, FunctionData, StaticData}, @@ -37,7 +37,7 @@ use stdx::impl_from; use syntax::SmolStr; use super::{ - traits::{Guidance, Obligation, Solution}, + traits::{DomainGoal, Guidance, Solution}, InEnvironment, ProjectionTy, Substitution, TraitEnvironment, TraitRef, Ty, TypeWalk, }; use crate::{ @@ -204,7 +204,7 @@ struct InferenceContext<'a> { resolver: Resolver, table: unify::InferenceTable, trait_env: Arc, - obligations: Vec, + obligations: Vec, result: InferenceResult, /// The return type of the function being inferred, or the closure if we're /// currently within one. @@ -403,8 +403,8 @@ impl<'a> InferenceContext<'a> { }), ty: ty.clone(), }; - self.obligations.push(Obligation::Trait(trait_ref)); - self.obligations.push(Obligation::AliasEq(alias_eq)); + self.obligations.push(trait_ref.cast(&Interner)); + self.obligations.push(alias_eq.cast(&Interner)); self.resolve_ty_as_possible(ty) } None => self.err_ty(), @@ -430,7 +430,7 @@ impl<'a> InferenceContext<'a> { 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 = Obligation::AliasEq(alias_eq); + let obligation = alias_eq.cast(&Interner); self.obligations.push(obligation); var } diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs index b86474ed4..07eb96573 100644 --- a/crates/hir_ty/src/infer/coerce.rs +++ b/crates/hir_ty/src/infer/coerce.rs @@ -4,12 +4,11 @@ //! //! See: https://doc.rust-lang.org/nomicon/coercions.html -use chalk_ir::{Mutability, TyVariableKind}; +use chalk_ir::{cast::Cast, Mutability, TyVariableKind}; use hir_def::lang_item::LangItemTarget; use crate::{ - autoderef, to_chalk_trait_id, traits::Solution, Interner, Obligation, Substitution, TraitRef, - Ty, TyKind, + autoderef, to_chalk_trait_id, traits::Solution, Interner, Substitution, TraitRef, Ty, TyKind, }; use super::{InEnvironment, InferenceContext}; @@ -143,7 +142,7 @@ impl<'a> InferenceContext<'a> { .build(); let trait_ref = TraitRef { trait_id: to_chalk_trait_id(coerce_unsized_trait), substitution: substs }; - let goal = InEnvironment::new(self.trait_env.clone(), Obligation::Trait(trait_ref)); + let goal = InEnvironment::new(self.trait_env.clone(), trait_ref.cast(&Interner)); let canonicalizer = self.canonicalizer(); let canonicalized = canonicalizer.canonicalize_obligation(goal); diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index 93548b6c0..79bbc5dab 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs @@ -3,7 +3,7 @@ use std::iter::{repeat, repeat_with}; use std::{mem, sync::Arc}; -use chalk_ir::{Mutability, TyVariableKind}; +use chalk_ir::{cast::Cast, Mutability, TyVariableKind}; use hir_def::{ expr::{Array, BinaryOp, Expr, ExprId, Literal, Statement, UnaryOp}, path::{GenericArg, GenericArgs}, @@ -21,7 +21,7 @@ use crate::{ to_assoc_type_id, to_chalk_trait_id, traits::{chalk::from_chalk, FnTrait, InEnvironment}, utils::{generics, variant_data, Generics}, - AdtId, Binders, CallableDefId, FnPointer, FnSig, Interner, Obligation, Rawness, Scalar, + AdtId, Binders, CallableDefId, DomainGoal, FnPointer, FnSig, Interner, Rawness, Scalar, Substitution, TraitRef, Ty, TyKind, }; @@ -90,10 +90,9 @@ impl<'a> InferenceContext<'a> { Substitution::build_for_generics(&generic_params).push(ty.clone()).push(arg_ty).build(); let trait_env = Arc::clone(&self.trait_env); - let implements_fn_trait = Obligation::Trait(TraitRef { - trait_id: to_chalk_trait_id(fn_once_trait), - substitution: substs.clone(), - }); + let implements_fn_trait: DomainGoal = + TraitRef { trait_id: to_chalk_trait_id(fn_once_trait), substitution: substs.clone() } + .cast(&Interner); let goal = self.canonicalizer().canonicalize_obligation(InEnvironment { value: implements_fn_trait.clone(), environment: trait_env, @@ -938,22 +937,20 @@ impl<'a> InferenceContext<'a> { let generic_predicates = self.db.generic_predicates(def.into()); for predicate in generic_predicates.iter() { let predicate = predicate.clone().subst(parameters); - if let Some(obligation) = Obligation::from_predicate(predicate) { - self.obligations.push(obligation); - } + self.obligations.push(predicate.cast(&Interner)); } // add obligation for trait implementation, if this is a trait method match def { CallableDefId::FunctionId(f) => { if let AssocContainerId::TraitId(trait_) = f.lookup(self.db.upcast()).container { - // construct a TraitDef + // construct a TraitRef let substs = parameters.prefix(generics(self.db.upcast(), trait_.into()).len()); - self.obligations.push(Obligation::Trait(TraitRef { - trait_id: to_chalk_trait_id(trait_), - substitution: substs, - })); + self.obligations.push( + TraitRef { trait_id: to_chalk_trait_id(trait_), substitution: substs } + .cast(&Interner), + ); } } CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_) => {} diff --git a/crates/hir_ty/src/infer/path.rs b/crates/hir_ty/src/infer/path.rs index e15135fc1..58cce56ab 100644 --- a/crates/hir_ty/src/infer/path.rs +++ b/crates/hir_ty/src/infer/path.rs @@ -2,6 +2,7 @@ use std::iter; +use chalk_ir::cast::Cast; use hir_def::{ path::{Path, PathSegment}, resolver::{ResolveValueResult, Resolver, TypeNs, ValueNs}, @@ -256,10 +257,13 @@ impl<'a> InferenceContext<'a> { .push(ty.clone()) .fill(std::iter::repeat_with(|| self.table.new_type_var())) .build(); - self.obligations.push(super::Obligation::Trait(TraitRef { - trait_id: to_chalk_trait_id(trait_), - substitution: trait_substs.clone(), - })); + self.obligations.push( + TraitRef { + trait_id: to_chalk_trait_id(trait_), + substitution: trait_substs.clone(), + } + .cast(&Interner), + ); Some(trait_substs) } AssocContainerId::ModuleId(_) => None, diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs index 5b7b423fa..1fc03c8f4 100644 --- a/crates/hir_ty/src/infer/unify.rs +++ b/crates/hir_ty/src/infer/unify.rs @@ -5,7 +5,7 @@ use std::borrow::Cow; use chalk_ir::{FloatTy, IntTy, TyVariableKind}; use ena::unify::{InPlaceUnificationTable, NoError, UnifyKey, UnifyValue}; -use super::{InferenceContext, Obligation}; +use super::{DomainGoal, InferenceContext}; use crate::{ AliasEq, AliasTy, BoundVar, Canonical, DebruijnIndex, FnPointer, InEnvironment, InferenceVar, Interner, Scalar, Substitution, Ty, TyKind, TypeWalk, WhereClause, @@ -87,14 +87,11 @@ impl<'a, 'b> Canonicalizer<'a, 'b> { pub(crate) fn canonicalize_obligation( mut self, - obligation: InEnvironment, - ) -> Canonicalized> { + obligation: InEnvironment, + ) -> Canonicalized> { let result = match obligation.value { - Obligation::Trait(tr) => { - Obligation::Trait(self.do_canonicalize(tr, DebruijnIndex::INNERMOST)) - } - Obligation::AliasEq(alias_eq) => { - Obligation::AliasEq(self.do_canonicalize(alias_eq, DebruijnIndex::INNERMOST)) + DomainGoal::Holds(wc) => { + DomainGoal::Holds(self.do_canonicalize(wc, DebruijnIndex::INNERMOST)) } }; self.into_canonicalized(InEnvironment { diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs index 8de2d422f..c46529879 100644 --- a/crates/hir_ty/src/lib.rs +++ b/crates/hir_ty/src/lib.rs @@ -13,6 +13,7 @@ mod op; mod lower; pub(crate) mod infer; pub(crate) mod utils; +mod chalk_cast; pub mod display; pub mod db; @@ -45,9 +46,11 @@ pub use lower::{ associated_type_shorthand_candidates, callable_item_sig, CallableDefId, ImplTraitLoweringMode, TyDefId, TyLoweringContext, ValueTyDefId, }; -pub use traits::{AliasEq, InEnvironment, Obligation, TraitEnvironment}; +pub use traits::{AliasEq, DomainGoal, InEnvironment, TraitEnvironment}; -pub use chalk_ir::{AdtId, BoundVar, DebruijnIndex, Mutability, Safety, Scalar, TyVariableKind}; +pub use chalk_ir::{ + cast::Cast, AdtId, BoundVar, DebruijnIndex, Mutability, Safety, Scalar, TyVariableKind, +}; pub use crate::traits::chalk::Interner; diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs index 01b78fb44..da6bc2a4a 100644 --- a/crates/hir_ty/src/method_resolution.rs +++ b/crates/hir_ty/src/method_resolution.rs @@ -6,7 +6,7 @@ use std::{iter, sync::Arc}; use arrayvec::ArrayVec; use base_db::CrateId; -use chalk_ir::Mutability; +use chalk_ir::{cast::Cast, Mutability}; use hir_def::{ lang_item::LangItemTarget, AssocContainerId, AssocItemId, FunctionId, GenericDefId, HasModule, ImplId, Lookup, ModuleId, TraitId, @@ -767,7 +767,7 @@ fn generic_implements_goal( env: Arc, trait_: TraitId, self_ty: Canonical, -) -> Canonical> { +) -> Canonical> { let mut kinds = self_ty.kinds.to_vec(); let substs = super::Substitution::build_for_def(db, trait_) .push(self_ty.value) @@ -775,7 +775,7 @@ fn generic_implements_goal( .build(); kinds.extend(iter::repeat(chalk_ir::TyVariableKind::General).take(substs.len() - 1)); let trait_ref = TraitRef { trait_id: to_chalk_trait_id(trait_), substitution: substs }; - let obligation = super::Obligation::Trait(trait_ref); + let obligation = trait_ref.cast(&Interner); Canonical { kinds: kinds.into(), value: InEnvironment::new(env, obligation) } } diff --git a/crates/hir_ty/src/traits.rs b/crates/hir_ty/src/traits.rs index 87c92bd12..7dadd1ffb 100644 --- a/crates/hir_ty/src/traits.rs +++ b/crates/hir_ty/src/traits.rs @@ -9,8 +9,8 @@ use hir_def::{lang_item::LangItemTarget, TraitId}; use stdx::panic_context; use crate::{ - db::HirDatabase, AliasTy, Canonical, DebruijnIndex, HirDisplay, Substitution, TraitRef, Ty, - TyKind, TypeWalk, WhereClause, + db::HirDatabase, AliasTy, Canonical, DebruijnIndex, HirDisplay, Substitution, Ty, TyKind, + TypeWalk, WhereClause, }; use self::chalk::{from_chalk, Interner, ToChalk}; @@ -88,20 +88,8 @@ impl InEnvironment { /// a certain type implements a certain trait. Proving the Obligation might /// result in additional information about inference variables. #[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub enum Obligation { - /// Prove that a certain type implements a trait (the type is the `Self` type - /// parameter to the `TraitRef`). - Trait(TraitRef), - AliasEq(AliasEq), -} - -impl Obligation { - pub fn from_predicate(predicate: WhereClause) -> Option { - match predicate { - WhereClause::Implemented(trait_ref) => Some(Obligation::Trait(trait_ref)), - WhereClause::AliasEq(alias_eq) => Some(Obligation::AliasEq(alias_eq)), - } - } +pub enum DomainGoal { + Holds(WhereClause), } #[derive(Clone, Debug, PartialEq, Eq, Hash)] @@ -136,16 +124,20 @@ impl TypeWalk for AliasEq { 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.value { - Obligation::Trait(it) => db.trait_data(it.hir_trait_id()).name.to_string(), - Obligation::AliasEq(_) => "alias_eq".to_string(), + DomainGoal::Holds(WhereClause::Implemented(it)) => { + db.trait_data(it.hir_trait_id()).name.to_string() + } + DomainGoal::Holds(WhereClause::AliasEq(_)) => "alias_eq".to_string(), }); log::info!("trait_solve_query({})", goal.value.value.display(db)); - if let Obligation::AliasEq(AliasEq { alias: AliasTy::Projection(projection_ty), .. }) = - &goal.value.value + if let DomainGoal::Holds(WhereClause::AliasEq(AliasEq { + alias: AliasTy::Projection(projection_ty), + .. + })) = &goal.value.value { if let TyKind::BoundVar(_) = &projection_ty.substitution[0].interned(&Interner) { // Hack: don't ask Chalk to normalize with an unknown self type, it'll say that's impossible diff --git a/crates/hir_ty/src/traits/chalk/mapping.rs b/crates/hir_ty/src/traits/chalk/mapping.rs index 7841c216c..65feb82e5 100644 --- a/crates/hir_ty/src/traits/chalk/mapping.rs +++ b/crates/hir_ty/src/traits/chalk/mapping.rs @@ -13,7 +13,7 @@ use crate::{ db::HirDatabase, from_assoc_type_id, primitive::UintTy, - traits::{Canonical, Obligation}, + traits::{Canonical, DomainGoal}, AliasTy, CallableDefId, FnPointer, InEnvironment, OpaqueTy, ProjectionTy, Scalar, Substitution, TraitRef, Ty, WhereClause, }; @@ -422,13 +422,15 @@ impl ToChalk for AliasEq { } } -impl ToChalk for Obligation { +impl ToChalk for DomainGoal { type Chalk = chalk_ir::DomainGoal; fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::DomainGoal { match self { - Obligation::Trait(tr) => tr.to_chalk(db).cast(&Interner), - Obligation::AliasEq(alias_eq) => alias_eq.to_chalk(db).cast(&Interner), + DomainGoal::Holds(WhereClause::Implemented(tr)) => tr.to_chalk(db).cast(&Interner), + DomainGoal::Holds(WhereClause::AliasEq(alias_eq)) => { + alias_eq.to_chalk(db).cast(&Interner) + } } } -- cgit v1.2.3