8117: Turn Obligation into something similar to chalk_ir::DomainGoal r=flodiebold a=flodiebold

This includes starting to make use of Chalk's `Cast` trait.

Co-authored-by: Florian Diebold <flodiebold@gmail.com>
This commit is contained in:
bors[bot] 2021-03-20 11:47:50 +00:00 committed by GitHub
commit 4d67032bbb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 130 additions and 91 deletions

View File

@ -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]),
};

View File

@ -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 };

View File

@ -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<WhereClause> for TraitRef {
fn cast_to(self, _interner: &Interner) -> WhereClause {
WhereClause::Implemented(self)
}
}
impl CastTo<WhereClause> for AliasEq {
fn cast_to(self, _interner: &Interner) -> WhereClause {
WhereClause::AliasEq(self)
}
}
impl CastTo<DomainGoal> 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);

View File

@ -119,7 +119,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
fn trait_solve(
&self,
krate: CrateId,
goal: crate::Canonical<crate::InEnvironment<crate::Obligation>>,
goal: crate::Canonical<crate::InEnvironment<crate::DomainGoal>>,
) -> Option<crate::traits::Solution>;
#[salsa::invoke(crate::traits::chalk::program_clauses_for_chalk_env_query)]

View File

@ -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, ")")
}
}

View File

@ -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<TraitEnvironment>,
obligations: Vec<Obligation>,
obligations: Vec<DomainGoal>,
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
}

View File

@ -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);

View File

@ -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(_) => {}

View File

@ -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,

View File

@ -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<Obligation>,
) -> Canonicalized<InEnvironment<Obligation>> {
obligation: InEnvironment<DomainGoal>,
) -> Canonicalized<InEnvironment<DomainGoal>> {
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 {

View File

@ -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;

View File

@ -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<TraitEnvironment>,
trait_: TraitId,
self_ty: Canonical<Ty>,
) -> Canonical<InEnvironment<super::Obligation>> {
) -> Canonical<InEnvironment<super::DomainGoal>> {
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) }
}

View File

@ -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<T> InEnvironment<T> {
/// 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<Obligation> {
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<InEnvironment<Obligation>>,
goal: Canonical<InEnvironment<DomainGoal>>,
) -> Option<Solution> {
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

View File

@ -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<Interner>;
fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::DomainGoal<Interner> {
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)
}
}
}