4570: Use Chalk's built-in impls r=matklad a=flodiebold

This contains two changes:
 - Chalk has begun adding built-in representations of primitive types; use these in our type conversion logic. There's one somewhat 'iffy' part here, namely references; we don't keep track of lifetimes, but Chalk does, so it will expect a lifetime parameter on references. If we didn't provide that, it could cause crashes in Chalk code that expects the lifetime, so I rather hackily add an (always the same) lifetime placeholder during conversion. I expect that we'll fully switch to using Chalk's types everywhere before we add lifetime support, so I think this is the best solution for now.
 - let Chalk know about well-known traits (from lang items), so it can apply its built-in impls.

Before:
```
Total expressions: 181485
Expressions of unknown type: 2940 (1%)
Expressions of partially unknown type: 2884 (1%)
Type mismatches: 901
Inference: 37.821210245s, 0b allocated 0b resident
Total: 53.399467609s, 0b allocated 0b resident
```

After:
```
Total expressions: 181485
Expressions of unknown type: 2923 (1%)
Expressions of partially unknown type: 2879 (1%)
Type mismatches: 734
Inference: 39.157752509s, 0b allocated 0b resident
Total: 54.110767621s, 0b allocated 0b resident
```

(I will start splitting up `chalk.rs` in a separate PR, since it's getting pretty big...)

Co-authored-by: Florian Diebold <flodiebold@gmail.com>
Co-authored-by: Florian Diebold <florian.diebold@freiheit.com>
This commit is contained in:
bors[bot] 2020-05-22 17:31:39 +00:00 committed by GitHub
commit b38f9a5810
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 277 additions and 18 deletions

View File

@ -73,8 +73,8 @@ pub struct LangItems {
}
impl LangItems {
pub fn target<'a>(&'a self, item: &str) -> Option<&'a LangItemTarget> {
self.items.get(item)
pub fn target(&self, item: &str) -> Option<LangItemTarget> {
self.items.get(item).copied()
}
/// Salsa query. This will look for lang items in a specific crate.
@ -163,9 +163,13 @@ impl LangItems {
) where
T: Into<AttrDefId> + Copy,
{
let attrs = db.attrs(item.into());
if let Some(lang_item_name) = attrs.by_key("lang").string_value() {
if let Some(lang_item_name) = lang_attr(db, item) {
self.items.entry(lang_item_name.clone()).or_insert_with(|| constructor(item));
}
}
}
pub fn lang_attr(db: &dyn DefDatabase, item: impl Into<AttrDefId> + Copy) -> Option<SmolStr> {
let attrs = db.attrs(item.into());
attrs.by_key("lang").string_value().cloned()
}

View File

@ -2602,3 +2602,81 @@ fn test(x: &dyn Foo) {
"###
);
}
#[test]
fn builtin_copy() {
assert_snapshot!(
infer_with_mismatches(r#"
#[lang = "copy"]
trait Copy {}
struct IsCopy;
impl Copy for IsCopy {}
struct NotCopy;
trait Test { fn test(&self) -> bool; }
impl<T: Copy> Test for T {}
fn test() {
IsCopy.test();
NotCopy.test();
(IsCopy, IsCopy).test();
(IsCopy, NotCopy).test();
}
"#, true),
@r###"
111..115 'self': &Self
167..268 '{ ...t(); }': ()
173..179 'IsCopy': IsCopy
173..186 'IsCopy.test()': bool
192..199 'NotCopy': NotCopy
192..206 'NotCopy.test()': {unknown}
212..228 '(IsCop...sCopy)': (IsCopy, IsCopy)
212..235 '(IsCop...test()': bool
213..219 'IsCopy': IsCopy
221..227 'IsCopy': IsCopy
241..258 '(IsCop...tCopy)': (IsCopy, NotCopy)
241..265 '(IsCop...test()': {unknown}
242..248 'IsCopy': IsCopy
250..257 'NotCopy': NotCopy
"###
);
}
#[test]
fn builtin_sized() {
assert_snapshot!(
infer_with_mismatches(r#"
#[lang = "sized"]
trait Sized {}
trait Test { fn test(&self) -> bool; }
impl<T: Sized> Test for T {}
fn test() {
1u8.test();
(*"foo").test(); // not Sized
(1u8, 1u8).test();
(1u8, *"foo").test(); // not Sized
}
"#, true),
@r###"
57..61 'self': &Self
114..229 '{ ...ized }': ()
120..123 '1u8': u8
120..130 '1u8.test()': bool
136..151 '(*"foo").test()': {unknown}
137..143 '*"foo"': str
138..143 '"foo"': &str
170..180 '(1u8, 1u8)': (u8, u8)
170..187 '(1u8, ...test()': bool
171..174 '1u8': u8
176..179 '1u8': u8
193..206 '(1u8, *"foo")': (u8, str)
193..213 '(1u8, ...test()': {unknown}
194..197 '1u8': u8
199..205 '*"foo"': str
200..205 '"foo"': &str
"###
);
}

View File

@ -5,10 +5,14 @@ use log::debug;
use chalk_ir::{
cast::Cast, fold::shift::Shift, interner::HasInterner, GenericArg, Goal, GoalData,
PlaceholderIndex, TypeName, UniverseIndex,
PlaceholderIndex, Scalar, TypeName, UniverseIndex,
};
use hir_def::{AssocContainerId, AssocItemId, GenericDefId, HasModule, Lookup, TypeAliasId};
use hir_def::{
lang_item::{lang_attr, LangItemTarget},
type_ref::Mutability,
AssocContainerId, AssocItemId, GenericDefId, HasModule, Lookup, TypeAliasId,
};
use ra_db::{
salsa::{InternId, InternKey},
CrateId,
@ -16,9 +20,14 @@ use ra_db::{
use super::{builtin, AssocTyValue, Canonical, ChalkContext, Impl, Obligation};
use crate::{
db::HirDatabase, display::HirDisplay, method_resolution::TyFingerprint, utils::generics,
db::HirDatabase,
display::HirDisplay,
method_resolution::TyFingerprint,
primitive::{FloatBitness, FloatTy, IntBitness, IntTy, Signedness, Uncertain},
utils::generics,
ApplicationTy, DebruijnIndex, GenericPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor,
};
use chalk_rust_ir::WellKnownTrait;
pub(super) mod tls;
@ -330,6 +339,9 @@ impl ToChalk for Ty {
fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Ty<Interner> {
match self {
Ty::Apply(apply_ty) => {
if let TypeCtor::Ref(m) = apply_ty.ctor {
return ref_to_chalk(db, m, apply_ty.parameters);
}
let name = apply_ty.ctor.to_chalk(db);
let substitution = apply_ty.parameters.to_chalk(db);
chalk_ir::ApplicationTy { name, substitution }.cast(&Interner).intern(&Interner)
@ -373,6 +385,7 @@ impl ToChalk for Ty {
match chalk.data(&Interner).clone() {
chalk_ir::TyData::Apply(apply_ty) => match apply_ty.name {
TypeName::Error => Ty::Unknown,
TypeName::Ref(m) => ref_from_chalk(db, m, apply_ty.substitution),
_ => {
let ctor = from_chalk(db, apply_ty.name);
let parameters = from_chalk(db, apply_ty.substitution);
@ -409,6 +422,41 @@ impl ToChalk for Ty {
}
}
const LIFETIME_PLACEHOLDER: PlaceholderIndex =
PlaceholderIndex { ui: UniverseIndex::ROOT, idx: usize::MAX };
/// We currently don't model lifetimes, but Chalk does. So, we have to insert a
/// fake lifetime here, because Chalks built-in logic may expect it to be there.
fn ref_to_chalk(
db: &dyn HirDatabase,
mutability: Mutability,
subst: Substs,
) -> chalk_ir::Ty<Interner> {
let arg = subst[0].clone().to_chalk(db);
let lifetime = LIFETIME_PLACEHOLDER.to_lifetime(&Interner);
chalk_ir::ApplicationTy {
name: TypeName::Ref(mutability.to_chalk(db)),
substitution: chalk_ir::Substitution::from(
&Interner,
vec![lifetime.cast(&Interner), arg.cast(&Interner)],
),
}
.intern(&Interner)
}
/// Here we remove the lifetime from the type we got from Chalk.
fn ref_from_chalk(
db: &dyn HirDatabase,
mutability: chalk_ir::Mutability,
subst: chalk_ir::Substitution<Interner>,
) -> Ty {
let tys = subst
.iter(&Interner)
.filter_map(|p| Some(from_chalk(db, p.ty(&Interner)?.clone())))
.collect();
Ty::apply(TypeCtor::Ref(from_chalk(db, mutability)), Substs(tys))
}
impl ToChalk for Substs {
type Chalk = chalk_ir::Substitution<Interner>;
@ -465,7 +513,31 @@ impl ToChalk for TypeCtor {
let type_id = type_alias.to_chalk(db);
TypeName::AssociatedType(type_id)
}
_ => {
TypeCtor::Bool => TypeName::Scalar(Scalar::Bool),
TypeCtor::Char => TypeName::Scalar(Scalar::Char),
TypeCtor::Int(Uncertain::Known(int_ty)) => TypeName::Scalar(int_ty_to_chalk(int_ty)),
TypeCtor::Float(Uncertain::Known(FloatTy { bitness: FloatBitness::X32 })) => {
TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F32))
}
TypeCtor::Float(Uncertain::Known(FloatTy { bitness: FloatBitness::X64 })) => {
TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F64))
}
TypeCtor::Tuple { cardinality } => TypeName::Tuple(cardinality.into()),
TypeCtor::RawPtr(mutability) => TypeName::Raw(mutability.to_chalk(db)),
TypeCtor::Slice => TypeName::Slice,
TypeCtor::Ref(mutability) => TypeName::Ref(mutability.to_chalk(db)),
TypeCtor::Str => TypeName::Str,
TypeCtor::Int(Uncertain::Unknown)
| TypeCtor::Float(Uncertain::Unknown)
| TypeCtor::Adt(_)
| TypeCtor::Array
| TypeCtor::FnDef(_)
| TypeCtor::FnPtr { .. }
| TypeCtor::Never
| TypeCtor::Closure { .. } => {
// other TypeCtors get interned and turned into a chalk StructId
let struct_id = db.intern_type_ctor(self).into();
TypeName::Adt(struct_id)
@ -479,12 +551,27 @@ impl ToChalk for TypeCtor {
TypeName::AssociatedType(type_id) => TypeCtor::AssociatedType(from_chalk(db, type_id)),
TypeName::OpaqueType(_) => unreachable!(),
TypeName::Scalar(_) => unreachable!(),
TypeName::Tuple(_) => unreachable!(),
TypeName::Raw(_) => unreachable!(),
TypeName::Slice => unreachable!(),
TypeName::Ref(_) => unreachable!(),
TypeName::Str => unreachable!(),
TypeName::Scalar(Scalar::Bool) => TypeCtor::Bool,
TypeName::Scalar(Scalar::Char) => TypeCtor::Char,
TypeName::Scalar(Scalar::Int(int_ty)) => TypeCtor::Int(Uncertain::Known(IntTy {
signedness: Signedness::Signed,
bitness: bitness_from_chalk_int(int_ty),
})),
TypeName::Scalar(Scalar::Uint(uint_ty)) => TypeCtor::Int(Uncertain::Known(IntTy {
signedness: Signedness::Unsigned,
bitness: bitness_from_chalk_uint(uint_ty),
})),
TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F32)) => {
TypeCtor::Float(Uncertain::Known(FloatTy { bitness: FloatBitness::X32 }))
}
TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F64)) => {
TypeCtor::Float(Uncertain::Known(FloatTy { bitness: FloatBitness::X64 }))
}
TypeName::Tuple(cardinality) => TypeCtor::Tuple { cardinality: cardinality as u16 },
TypeName::Raw(mutability) => TypeCtor::RawPtr(from_chalk(db, mutability)),
TypeName::Slice => TypeCtor::Slice,
TypeName::Ref(mutability) => TypeCtor::Ref(from_chalk(db, mutability)),
TypeName::Str => TypeCtor::Str,
TypeName::FnDef(_) => unreachable!(),
@ -496,6 +583,71 @@ impl ToChalk for TypeCtor {
}
}
fn bitness_from_chalk_uint(uint_ty: chalk_ir::UintTy) -> IntBitness {
use chalk_ir::UintTy;
match uint_ty {
UintTy::Usize => IntBitness::Xsize,
UintTy::U8 => IntBitness::X8,
UintTy::U16 => IntBitness::X16,
UintTy::U32 => IntBitness::X32,
UintTy::U64 => IntBitness::X64,
UintTy::U128 => IntBitness::X128,
}
}
fn bitness_from_chalk_int(int_ty: chalk_ir::IntTy) -> IntBitness {
use chalk_ir::IntTy;
match int_ty {
IntTy::Isize => IntBitness::Xsize,
IntTy::I8 => IntBitness::X8,
IntTy::I16 => IntBitness::X16,
IntTy::I32 => IntBitness::X32,
IntTy::I64 => IntBitness::X64,
IntTy::I128 => IntBitness::X128,
}
}
fn int_ty_to_chalk(int_ty: IntTy) -> Scalar {
use chalk_ir::{IntTy, UintTy};
match int_ty.signedness {
Signedness::Signed => Scalar::Int(match int_ty.bitness {
IntBitness::Xsize => IntTy::Isize,
IntBitness::X8 => IntTy::I8,
IntBitness::X16 => IntTy::I16,
IntBitness::X32 => IntTy::I32,
IntBitness::X64 => IntTy::I64,
IntBitness::X128 => IntTy::I128,
}),
Signedness::Unsigned => Scalar::Uint(match int_ty.bitness {
IntBitness::Xsize => UintTy::Usize,
IntBitness::X8 => UintTy::U8,
IntBitness::X16 => UintTy::U16,
IntBitness::X32 => UintTy::U32,
IntBitness::X64 => UintTy::U64,
IntBitness::X128 => UintTy::U128,
}),
}
}
impl ToChalk for Mutability {
type Chalk = chalk_ir::Mutability;
fn to_chalk(self, _db: &dyn HirDatabase) -> Self::Chalk {
match self {
Mutability::Shared => chalk_ir::Mutability::Not,
Mutability::Mut => chalk_ir::Mutability::Mut,
}
}
fn from_chalk(_db: &dyn HirDatabase, chalk: Self::Chalk) -> Self {
match chalk {
chalk_ir::Mutability::Mut => Mutability::Mut,
chalk_ir::Mutability::Not => Mutability::Shared,
}
}
}
impl ToChalk for Impl {
type Chalk = ImplId;
@ -907,10 +1059,15 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
}
fn well_known_trait_id(
&self,
_well_known_trait: chalk_rust_ir::WellKnownTrait,
well_known_trait: chalk_rust_ir::WellKnownTrait,
) -> Option<chalk_ir::TraitId<Interner>> {
// FIXME tell Chalk about well-known traits (here and in trait_datum)
None
let lang_attr = lang_attr_from_well_known_trait(well_known_trait);
let lang_items = self.db.crate_lang_items(self.krate);
let trait_ = match lang_items.target(lang_attr) {
Some(LangItemTarget::TraitId(trait_)) => trait_,
_ => return None,
};
Some(trait_.to_chalk(self.db))
}
fn program_clauses_for_env(
@ -1012,7 +1169,8 @@ pub(crate) fn trait_datum_query(
let associated_ty_ids =
trait_data.associated_types().map(|type_alias| type_alias.to_chalk(db)).collect();
let trait_datum_bound = chalk_rust_ir::TraitDatumBound { where_clauses };
let well_known = None; // FIXME set this (depending on lang items)
let well_known =
lang_attr(db.upcast(), trait_).and_then(|name| well_known_trait_from_lang_attr(&name));
let trait_datum = TraitDatum {
id: trait_id,
binders: make_binders(trait_datum_bound, bound_vars.len()),
@ -1023,6 +1181,25 @@ pub(crate) fn trait_datum_query(
Arc::new(trait_datum)
}
fn well_known_trait_from_lang_attr(name: &str) -> Option<WellKnownTrait> {
Some(match name {
"sized" => WellKnownTrait::SizedTrait,
"copy" => WellKnownTrait::CopyTrait,
"clone" => WellKnownTrait::CloneTrait,
"drop" => WellKnownTrait::DropTrait,
_ => return None,
})
}
fn lang_attr_from_well_known_trait(attr: WellKnownTrait) -> &'static str {
match attr {
WellKnownTrait::SizedTrait => "sized",
WellKnownTrait::CopyTrait => "copy",
WellKnownTrait::CloneTrait => "clone",
WellKnownTrait::DropTrait => "drop",
}
}
pub(crate) fn struct_datum_query(
db: &dyn HirDatabase,
krate: CrateId,