Introduce our own Chalk TypeFamily, instead of using ChalkIr

It's not very different, except we can directly use Salsa IDs instead of casting
them. This means we need to refactor the handling of errors to get rid of
UNKNOWN_TRAIT though.
This commit is contained in:
Florian Diebold 2019-12-21 15:00:44 +01:00
parent 6b5efe5bda
commit 4053fcfca0
4 changed files with 126 additions and 87 deletions

View File

@ -865,7 +865,7 @@ mod foo {
#[test]
fn method_resolution_where_clause_for_unknown_trait() {
// The blanket impl shouldn't apply because we can't even resolve UnknownTrait
// The blanket impl currently applies because we ignore the unresolved where clause
let t = type_at(
r#"
//- /main.rs
@ -875,7 +875,7 @@ impl<T> Trait for T where T: UnknownTrait {}
fn test() { (&S).foo()<|>; }
"#,
);
assert_eq!(t, "{unknown}");
assert_eq!(t, "u128");
}
#[test]

View File

@ -291,7 +291,7 @@ impl FnTrait {
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ClosureFnTraitImplData {
def: DefWithBodyId,
expr: ExprId,
@ -300,7 +300,7 @@ pub struct ClosureFnTraitImplData {
/// An impl. Usually this comes from an impl block, but some built-in types get
/// synthetic impls.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Impl {
/// A normal impl from an impl block.
ImplBlock(ImplId),

View File

@ -28,24 +28,24 @@ pub(super) fn get_builtin_impls(
trait_: TraitId,
mut callback: impl FnMut(Impl),
) {
// Note: since impl_datum needs to be infallible, we need to make sure here
// that we have all prerequisites to build the respective impls.
if let Ty::Apply(ApplicationTy { ctor: TypeCtor::Closure { def, expr }, .. }) = ty {
for &fn_trait in [super::FnTrait::FnOnce, super::FnTrait::FnMut, super::FnTrait::Fn].iter()
{
if let Some(actual_trait) = get_fn_trait(db, krate, fn_trait) {
if trait_ == actual_trait {
let impl_ = super::ClosureFnTraitImplData { def: *def, expr: *expr, fn_trait };
callback(Impl::ClosureFnTraitImpl(impl_));
if check_closure_fn_trait_impl_prerequisites(db, krate, impl_) {
callback(Impl::ClosureFnTraitImpl(impl_));
}
}
}
}
}
}
pub(super) fn impl_datum(
db: &impl HirDatabase,
krate: CrateId,
impl_: Impl,
) -> Option<BuiltinImplData> {
pub(super) fn impl_datum(db: &impl HirDatabase, krate: CrateId, impl_: Impl) -> BuiltinImplData {
match impl_ {
Impl::ImplBlock(_) => unreachable!(),
Impl::ClosureFnTraitImpl(data) => closure_fn_trait_impl_datum(db, krate, data),
@ -65,21 +65,38 @@ pub(super) fn associated_ty_value(
}
}
fn check_closure_fn_trait_impl_prerequisites(
db: &impl HirDatabase,
krate: CrateId,
data: super::ClosureFnTraitImplData,
) -> bool {
// the respective Fn/FnOnce/FnMut trait needs to exist
if get_fn_trait(db, krate, data.fn_trait).is_none() {
return false;
}
// FIXME: there are more assumptions that we should probably check here:
// the traits having no type params, FnOnce being a supertrait
// the FnOnce trait needs to exist and have an assoc type named Output
let fn_once_trait = match get_fn_trait(db, krate, super::FnTrait::FnOnce) {
Some(t) => t,
None => return false,
};
db.trait_data(fn_once_trait).associated_type_by_name(&name![Output]).is_some()
}
fn closure_fn_trait_impl_datum(
db: &impl HirDatabase,
krate: CrateId,
data: super::ClosureFnTraitImplData,
) -> Option<BuiltinImplData> {
) -> BuiltinImplData {
// for some closure |X, Y| -> Z:
// impl<T, U, V> Fn<(T, U)> for closure<fn(T, U) -> V> { Output = V }
let trait_ = get_fn_trait(db, krate, data.fn_trait)?; // get corresponding fn trait
// validate FnOnce trait, since we need it in the assoc ty value definition
// and don't want to return a valid value only to find out later that FnOnce
// is broken
let fn_once_trait = get_fn_trait(db, krate, super::FnTrait::FnOnce)?;
let _output = db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?;
let trait_ = get_fn_trait(db, krate, data.fn_trait) // get corresponding fn trait
// the existence of the Fn trait has been checked before
.expect("fn trait for closure impl missing");
let num_args: u16 = match &db.body(data.def.into())[data.expr] {
Expr::Lambda { args, .. } => args.len() as u16,
@ -107,12 +124,12 @@ fn closure_fn_trait_impl_datum(
let output_ty_id = AssocTyValue::ClosureFnTraitImplOutput(data.clone());
Some(BuiltinImplData {
BuiltinImplData {
num_vars: num_args as usize + 1,
trait_ref,
where_clauses: Vec::new(),
assoc_ty_values: vec![output_ty_id],
})
}
}
fn closure_fn_trait_output_assoc_ty_value(

View File

@ -1,5 +1,5 @@
//! Conversion code from/to Chalk.
use std::sync::Arc;
use std::{fmt, sync::Arc};
use log::debug;
@ -17,7 +17,73 @@ use crate::{
ProjectionTy, Substs, TraitRef, Ty, TypeCtor, TypeWalk,
};
pub type TypeFamily = chalk_ir::family::ChalkIr;
#[derive(Debug, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)]
pub struct TypeFamily {}
impl chalk_ir::family::TypeFamily for TypeFamily {
type InternedType = Box<chalk_ir::TyData<Self>>;
type InternedLifetime = chalk_ir::LifetimeData<Self>;
type InternedParameter = chalk_ir::ParameterData<Self>;
type DefId = InternId;
// FIXME: implement these
fn debug_struct_id(
_type_kind_id: chalk_ir::StructId<Self>,
_fmt: &mut fmt::Formatter<'_>,
) -> Option<fmt::Result> {
None
}
fn debug_trait_id(
_type_kind_id: chalk_ir::TraitId<Self>,
_fmt: &mut fmt::Formatter<'_>,
) -> Option<fmt::Result> {
None
}
fn debug_assoc_type_id(
_id: chalk_ir::AssocTypeId<Self>,
_fmt: &mut fmt::Formatter<'_>,
) -> Option<fmt::Result> {
None
}
fn debug_projection(
_projection: &chalk_ir::ProjectionTy<Self>,
_fmt: &mut fmt::Formatter<'_>,
) -> Option<fmt::Result> {
None
}
fn intern_ty(ty: chalk_ir::TyData<Self>) -> Box<chalk_ir::TyData<Self>> {
Box::new(ty)
}
fn ty_data(ty: &Box<chalk_ir::TyData<Self>>) -> &chalk_ir::TyData<Self> {
ty
}
fn intern_lifetime(lifetime: chalk_ir::LifetimeData<Self>) -> chalk_ir::LifetimeData<Self> {
lifetime
}
fn lifetime_data(lifetime: &chalk_ir::LifetimeData<Self>) -> &chalk_ir::LifetimeData<Self> {
lifetime
}
fn intern_parameter(parameter: chalk_ir::ParameterData<Self>) -> chalk_ir::ParameterData<Self> {
parameter
}
fn parameter_data(parameter: &chalk_ir::ParameterData<Self>) -> &chalk_ir::ParameterData<Self> {
parameter
}
}
impl chalk_ir::family::HasTypeFamily for TypeFamily {
type TypeFamily = Self;
}
pub type AssocTypeId = chalk_ir::AssocTypeId<TypeFamily>;
pub type AssociatedTyDatum = chalk_rust_ir::AssociatedTyDatum<TypeFamily>;
pub type TraitId = chalk_ir::TraitId<TypeFamily>;
@ -29,9 +95,6 @@ pub type ImplDatum = chalk_rust_ir::ImplDatum<TypeFamily>;
pub type AssociatedTyValueId = chalk_rust_ir::AssociatedTyValueId;
pub type AssociatedTyValue = chalk_rust_ir::AssociatedTyValue<TypeFamily>;
/// This represents a trait whose name we could not resolve.
const UNKNOWN_TRAIT: TraitId = chalk_ir::TraitId(chalk_ir::RawId { index: u32::max_value() });
pub(super) trait ToChalk {
type Chalk;
fn to_chalk(self, db: &impl HirDatabase) -> Self::Chalk;
@ -162,11 +225,11 @@ impl ToChalk for hir_def::TraitId {
type Chalk = TraitId;
fn to_chalk(self, _db: &impl HirDatabase) -> TraitId {
chalk_ir::TraitId(id_to_chalk(self))
chalk_ir::TraitId(self.as_intern_id())
}
fn from_chalk(_db: &impl HirDatabase, trait_id: TraitId) -> hir_def::TraitId {
id_from_chalk(trait_id.0)
InternKey::from_intern_id(trait_id.0)
}
}
@ -215,11 +278,11 @@ impl ToChalk for TypeAliasId {
type Chalk = AssocTypeId;
fn to_chalk(self, _db: &impl HirDatabase) -> AssocTypeId {
chalk_ir::AssocTypeId(id_to_chalk(self))
chalk_ir::AssocTypeId(self.as_intern_id())
}
fn from_chalk(_db: &impl HirDatabase, type_alias_id: AssocTypeId) -> TypeAliasId {
id_from_chalk(type_alias_id.0)
InternKey::from_intern_id(type_alias_id.0)
}
}
@ -250,13 +313,7 @@ impl ToChalk for GenericPredicate {
}),
0,
),
GenericPredicate::Error => {
let impossible_trait_ref = chalk_ir::TraitRef {
trait_id: UNKNOWN_TRAIT,
parameters: vec![Ty::Unknown.to_chalk(db).cast()],
};
make_binders(chalk_ir::WhereClause::Implemented(impossible_trait_ref), 0)
}
GenericPredicate::Error => panic!("tried passing GenericPredicate::Error to Chalk"),
}
}
@ -266,10 +323,6 @@ impl ToChalk for GenericPredicate {
) -> GenericPredicate {
match where_clause.value {
chalk_ir::WhereClause::Implemented(tr) => {
if tr.trait_id == UNKNOWN_TRAIT {
// FIXME we need an Error enum on the Chalk side to avoid this
return GenericPredicate::Error;
}
GenericPredicate::Implemented(from_chalk(db, tr))
}
chalk_ir::WhereClause::ProjectionEq(projection_eq) => {
@ -460,9 +513,8 @@ fn convert_where_clauses(
let mut result = Vec::with_capacity(generic_predicates.len());
for pred in generic_predicates.iter() {
if pred.is_error() {
// HACK: Return just the single predicate (which is always false
// anyway), otherwise Chalk can easily get into slow situations
return vec![pred.clone().subst(substs).to_chalk(db)];
// skip errored predicates completely
continue;
}
result.push(pred.clone().subst(substs).to_chalk(db));
}
@ -491,10 +543,11 @@ where
parameters: &[Parameter<TypeFamily>],
) -> Vec<ImplId> {
debug!("impls_for_trait {:?}", trait_id);
if trait_id == UNKNOWN_TRAIT {
return Vec::new();
}
let trait_: hir_def::TraitId = from_chalk(self.db, trait_id);
// Note: Since we're using impls_for_trait, only impls where the trait
// can be resolved should ever reach Chalk. `impl_datum` relies on that
// and will panic if the trait can't be resolved.
let mut result: Vec<_> = self
.db
.impls_for_trait(self.krate, trait_.into())
@ -566,24 +619,6 @@ pub(crate) fn trait_datum_query(
trait_id: TraitId,
) -> Arc<TraitDatum> {
debug!("trait_datum {:?}", trait_id);
if trait_id == UNKNOWN_TRAIT {
let trait_datum_bound = chalk_rust_ir::TraitDatumBound { where_clauses: Vec::new() };
let flags = chalk_rust_ir::TraitFlags {
auto: false,
marker: false,
upstream: true,
fundamental: false,
non_enumerable: true,
coinductive: false,
};
return Arc::new(TraitDatum {
id: trait_id,
binders: make_binders(trait_datum_bound, 1),
flags,
associated_ty_ids: vec![],
});
}
let trait_: hir_def::TraitId = from_chalk(db, trait_id);
let trait_data = db.trait_data(trait_);
debug!("trait {:?} = {:?}", trait_id, trait_data.name);
@ -653,9 +688,8 @@ pub(crate) fn impl_datum_query(
let impl_: Impl = from_chalk(db, impl_id);
match impl_ {
Impl::ImplBlock(impl_block) => impl_block_datum(db, krate, impl_id, impl_block),
_ => builtin::impl_datum(db, krate, impl_).map(|d| Arc::new(d.to_chalk(db))),
_ => Arc::new(builtin::impl_datum(db, krate, impl_).to_chalk(db)),
}
.unwrap_or_else(invalid_impl_datum)
}
fn impl_block_datum(
@ -663,8 +697,11 @@ fn impl_block_datum(
krate: CrateId,
chalk_id: ImplId,
impl_id: hir_def::ImplId,
) -> Option<Arc<ImplDatum>> {
let trait_ref = db.impl_trait(impl_id)?;
) -> Arc<ImplDatum> {
let trait_ref = db
.impl_trait(impl_id)
// ImplIds for impls where the trait ref can't be resolved should never reach Chalk
.expect("invalid impl passed to Chalk");
let impl_data = db.impl_data(impl_id);
let generic_params = generics(db, impl_id.into());
@ -716,21 +753,6 @@ fn impl_block_datum(
polarity,
associated_ty_value_ids,
};
Some(Arc::new(impl_datum))
}
fn invalid_impl_datum() -> Arc<ImplDatum> {
let trait_ref = chalk_ir::TraitRef {
trait_id: UNKNOWN_TRAIT,
parameters: vec![chalk_ir::TyData::BoundVar(0).cast().intern().cast()],
};
let impl_datum_bound = chalk_rust_ir::ImplDatumBound { trait_ref, where_clauses: Vec::new() };
let impl_datum = ImplDatum {
binders: make_binders(impl_datum_bound, 1),
impl_type: chalk_rust_ir::ImplType::External,
polarity: chalk_rust_ir::Polarity::Positive,
associated_ty_value_ids: Vec::new(),
};
Arc::new(impl_datum)
}
@ -786,25 +808,25 @@ fn id_to_chalk<T: InternKey>(salsa_id: T) -> chalk_ir::RawId {
impl From<StructId> for crate::TypeCtorId {
fn from(struct_id: StructId) -> Self {
id_from_chalk(struct_id.0)
InternKey::from_intern_id(struct_id.0)
}
}
impl From<crate::TypeCtorId> for StructId {
fn from(type_ctor_id: crate::TypeCtorId) -> Self {
chalk_ir::StructId(id_to_chalk(type_ctor_id))
chalk_ir::StructId(type_ctor_id.as_intern_id())
}
}
impl From<ImplId> for crate::traits::GlobalImplId {
fn from(impl_id: ImplId) -> Self {
id_from_chalk(impl_id.0)
InternKey::from_intern_id(impl_id.0)
}
}
impl From<crate::traits::GlobalImplId> for ImplId {
fn from(impl_id: crate::traits::GlobalImplId) -> Self {
chalk_ir::ImplId(id_to_chalk(impl_id))
chalk_ir::ImplId(impl_id.as_intern_id())
}
}