Skip to content

Commit b38f9a5

Browse files
Merge #4570
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 <[email protected]> Co-authored-by: Florian Diebold <[email protected]>
2 parents c5a2271 + e0f9780 commit b38f9a5

File tree

3 files changed

+277
-18
lines changed

3 files changed

+277
-18
lines changed

crates/ra_hir_def/src/lang_item.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ pub struct LangItems {
7373
}
7474

7575
impl LangItems {
76-
pub fn target<'a>(&'a self, item: &str) -> Option<&'a LangItemTarget> {
77-
self.items.get(item)
76+
pub fn target(&self, item: &str) -> Option<LangItemTarget> {
77+
self.items.get(item).copied()
7878
}
7979

8080
/// Salsa query. This will look for lang items in a specific crate.
@@ -163,9 +163,13 @@ impl LangItems {
163163
) where
164164
T: Into<AttrDefId> + Copy,
165165
{
166-
let attrs = db.attrs(item.into());
167-
if let Some(lang_item_name) = attrs.by_key("lang").string_value() {
166+
if let Some(lang_item_name) = lang_attr(db, item) {
168167
self.items.entry(lang_item_name.clone()).or_insert_with(|| constructor(item));
169168
}
170169
}
171170
}
171+
172+
pub fn lang_attr(db: &dyn DefDatabase, item: impl Into<AttrDefId> + Copy) -> Option<SmolStr> {
173+
let attrs = db.attrs(item.into());
174+
attrs.by_key("lang").string_value().cloned()
175+
}

crates/ra_hir_ty/src/tests/traits.rs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2602,3 +2602,81 @@ fn test(x: &dyn Foo) {
26022602
"###
26032603
);
26042604
}
2605+
2606+
#[test]
2607+
fn builtin_copy() {
2608+
assert_snapshot!(
2609+
infer_with_mismatches(r#"
2610+
#[lang = "copy"]
2611+
trait Copy {}
2612+
2613+
struct IsCopy;
2614+
impl Copy for IsCopy {}
2615+
struct NotCopy;
2616+
2617+
trait Test { fn test(&self) -> bool; }
2618+
impl<T: Copy> Test for T {}
2619+
2620+
fn test() {
2621+
IsCopy.test();
2622+
NotCopy.test();
2623+
(IsCopy, IsCopy).test();
2624+
(IsCopy, NotCopy).test();
2625+
}
2626+
"#, true),
2627+
@r###"
2628+
111..115 'self': &Self
2629+
167..268 '{ ...t(); }': ()
2630+
173..179 'IsCopy': IsCopy
2631+
173..186 'IsCopy.test()': bool
2632+
192..199 'NotCopy': NotCopy
2633+
192..206 'NotCopy.test()': {unknown}
2634+
212..228 '(IsCop...sCopy)': (IsCopy, IsCopy)
2635+
212..235 '(IsCop...test()': bool
2636+
213..219 'IsCopy': IsCopy
2637+
221..227 'IsCopy': IsCopy
2638+
241..258 '(IsCop...tCopy)': (IsCopy, NotCopy)
2639+
241..265 '(IsCop...test()': {unknown}
2640+
242..248 'IsCopy': IsCopy
2641+
250..257 'NotCopy': NotCopy
2642+
"###
2643+
);
2644+
}
2645+
2646+
#[test]
2647+
fn builtin_sized() {
2648+
assert_snapshot!(
2649+
infer_with_mismatches(r#"
2650+
#[lang = "sized"]
2651+
trait Sized {}
2652+
2653+
trait Test { fn test(&self) -> bool; }
2654+
impl<T: Sized> Test for T {}
2655+
2656+
fn test() {
2657+
1u8.test();
2658+
(*"foo").test(); // not Sized
2659+
(1u8, 1u8).test();
2660+
(1u8, *"foo").test(); // not Sized
2661+
}
2662+
"#, true),
2663+
@r###"
2664+
57..61 'self': &Self
2665+
114..229 '{ ...ized }': ()
2666+
120..123 '1u8': u8
2667+
120..130 '1u8.test()': bool
2668+
136..151 '(*"foo").test()': {unknown}
2669+
137..143 '*"foo"': str
2670+
138..143 '"foo"': &str
2671+
170..180 '(1u8, 1u8)': (u8, u8)
2672+
170..187 '(1u8, ...test()': bool
2673+
171..174 '1u8': u8
2674+
176..179 '1u8': u8
2675+
193..206 '(1u8, *"foo")': (u8, str)
2676+
193..213 '(1u8, ...test()': {unknown}
2677+
194..197 '1u8': u8
2678+
199..205 '*"foo"': str
2679+
200..205 '"foo"': &str
2680+
"###
2681+
);
2682+
}

crates/ra_hir_ty/src/traits/chalk.rs

Lines changed: 191 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,29 @@ use log::debug;
55

66
use chalk_ir::{
77
cast::Cast, fold::shift::Shift, interner::HasInterner, GenericArg, Goal, GoalData,
8-
PlaceholderIndex, TypeName, UniverseIndex,
8+
PlaceholderIndex, Scalar, TypeName, UniverseIndex,
99
};
1010

11-
use hir_def::{AssocContainerId, AssocItemId, GenericDefId, HasModule, Lookup, TypeAliasId};
11+
use hir_def::{
12+
lang_item::{lang_attr, LangItemTarget},
13+
type_ref::Mutability,
14+
AssocContainerId, AssocItemId, GenericDefId, HasModule, Lookup, TypeAliasId,
15+
};
1216
use ra_db::{
1317
salsa::{InternId, InternKey},
1418
CrateId,
1519
};
1620

1721
use super::{builtin, AssocTyValue, Canonical, ChalkContext, Impl, Obligation};
1822
use crate::{
19-
db::HirDatabase, display::HirDisplay, method_resolution::TyFingerprint, utils::generics,
23+
db::HirDatabase,
24+
display::HirDisplay,
25+
method_resolution::TyFingerprint,
26+
primitive::{FloatBitness, FloatTy, IntBitness, IntTy, Signedness, Uncertain},
27+
utils::generics,
2028
ApplicationTy, DebruijnIndex, GenericPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor,
2129
};
30+
use chalk_rust_ir::WellKnownTrait;
2231

2332
pub(super) mod tls;
2433

@@ -330,6 +339,9 @@ impl ToChalk for Ty {
330339
fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Ty<Interner> {
331340
match self {
332341
Ty::Apply(apply_ty) => {
342+
if let TypeCtor::Ref(m) = apply_ty.ctor {
343+
return ref_to_chalk(db, m, apply_ty.parameters);
344+
}
333345
let name = apply_ty.ctor.to_chalk(db);
334346
let substitution = apply_ty.parameters.to_chalk(db);
335347
chalk_ir::ApplicationTy { name, substitution }.cast(&Interner).intern(&Interner)
@@ -373,6 +385,7 @@ impl ToChalk for Ty {
373385
match chalk.data(&Interner).clone() {
374386
chalk_ir::TyData::Apply(apply_ty) => match apply_ty.name {
375387
TypeName::Error => Ty::Unknown,
388+
TypeName::Ref(m) => ref_from_chalk(db, m, apply_ty.substitution),
376389
_ => {
377390
let ctor = from_chalk(db, apply_ty.name);
378391
let parameters = from_chalk(db, apply_ty.substitution);
@@ -409,6 +422,41 @@ impl ToChalk for Ty {
409422
}
410423
}
411424

425+
const LIFETIME_PLACEHOLDER: PlaceholderIndex =
426+
PlaceholderIndex { ui: UniverseIndex::ROOT, idx: usize::MAX };
427+
428+
/// We currently don't model lifetimes, but Chalk does. So, we have to insert a
429+
/// fake lifetime here, because Chalks built-in logic may expect it to be there.
430+
fn ref_to_chalk(
431+
db: &dyn HirDatabase,
432+
mutability: Mutability,
433+
subst: Substs,
434+
) -> chalk_ir::Ty<Interner> {
435+
let arg = subst[0].clone().to_chalk(db);
436+
let lifetime = LIFETIME_PLACEHOLDER.to_lifetime(&Interner);
437+
chalk_ir::ApplicationTy {
438+
name: TypeName::Ref(mutability.to_chalk(db)),
439+
substitution: chalk_ir::Substitution::from(
440+
&Interner,
441+
vec![lifetime.cast(&Interner), arg.cast(&Interner)],
442+
),
443+
}
444+
.intern(&Interner)
445+
}
446+
447+
/// Here we remove the lifetime from the type we got from Chalk.
448+
fn ref_from_chalk(
449+
db: &dyn HirDatabase,
450+
mutability: chalk_ir::Mutability,
451+
subst: chalk_ir::Substitution<Interner>,
452+
) -> Ty {
453+
let tys = subst
454+
.iter(&Interner)
455+
.filter_map(|p| Some(from_chalk(db, p.ty(&Interner)?.clone())))
456+
.collect();
457+
Ty::apply(TypeCtor::Ref(from_chalk(db, mutability)), Substs(tys))
458+
}
459+
412460
impl ToChalk for Substs {
413461
type Chalk = chalk_ir::Substitution<Interner>;
414462

@@ -465,7 +513,31 @@ impl ToChalk for TypeCtor {
465513
let type_id = type_alias.to_chalk(db);
466514
TypeName::AssociatedType(type_id)
467515
}
468-
_ => {
516+
517+
TypeCtor::Bool => TypeName::Scalar(Scalar::Bool),
518+
TypeCtor::Char => TypeName::Scalar(Scalar::Char),
519+
TypeCtor::Int(Uncertain::Known(int_ty)) => TypeName::Scalar(int_ty_to_chalk(int_ty)),
520+
TypeCtor::Float(Uncertain::Known(FloatTy { bitness: FloatBitness::X32 })) => {
521+
TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F32))
522+
}
523+
TypeCtor::Float(Uncertain::Known(FloatTy { bitness: FloatBitness::X64 })) => {
524+
TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F64))
525+
}
526+
527+
TypeCtor::Tuple { cardinality } => TypeName::Tuple(cardinality.into()),
528+
TypeCtor::RawPtr(mutability) => TypeName::Raw(mutability.to_chalk(db)),
529+
TypeCtor::Slice => TypeName::Slice,
530+
TypeCtor::Ref(mutability) => TypeName::Ref(mutability.to_chalk(db)),
531+
TypeCtor::Str => TypeName::Str,
532+
533+
TypeCtor::Int(Uncertain::Unknown)
534+
| TypeCtor::Float(Uncertain::Unknown)
535+
| TypeCtor::Adt(_)
536+
| TypeCtor::Array
537+
| TypeCtor::FnDef(_)
538+
| TypeCtor::FnPtr { .. }
539+
| TypeCtor::Never
540+
| TypeCtor::Closure { .. } => {
469541
// other TypeCtors get interned and turned into a chalk StructId
470542
let struct_id = db.intern_type_ctor(self).into();
471543
TypeName::Adt(struct_id)
@@ -479,12 +551,27 @@ impl ToChalk for TypeCtor {
479551
TypeName::AssociatedType(type_id) => TypeCtor::AssociatedType(from_chalk(db, type_id)),
480552
TypeName::OpaqueType(_) => unreachable!(),
481553

482-
TypeName::Scalar(_) => unreachable!(),
483-
TypeName::Tuple(_) => unreachable!(),
484-
TypeName::Raw(_) => unreachable!(),
485-
TypeName::Slice => unreachable!(),
486-
TypeName::Ref(_) => unreachable!(),
487-
TypeName::Str => unreachable!(),
554+
TypeName::Scalar(Scalar::Bool) => TypeCtor::Bool,
555+
TypeName::Scalar(Scalar::Char) => TypeCtor::Char,
556+
TypeName::Scalar(Scalar::Int(int_ty)) => TypeCtor::Int(Uncertain::Known(IntTy {
557+
signedness: Signedness::Signed,
558+
bitness: bitness_from_chalk_int(int_ty),
559+
})),
560+
TypeName::Scalar(Scalar::Uint(uint_ty)) => TypeCtor::Int(Uncertain::Known(IntTy {
561+
signedness: Signedness::Unsigned,
562+
bitness: bitness_from_chalk_uint(uint_ty),
563+
})),
564+
TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F32)) => {
565+
TypeCtor::Float(Uncertain::Known(FloatTy { bitness: FloatBitness::X32 }))
566+
}
567+
TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F64)) => {
568+
TypeCtor::Float(Uncertain::Known(FloatTy { bitness: FloatBitness::X64 }))
569+
}
570+
TypeName::Tuple(cardinality) => TypeCtor::Tuple { cardinality: cardinality as u16 },
571+
TypeName::Raw(mutability) => TypeCtor::RawPtr(from_chalk(db, mutability)),
572+
TypeName::Slice => TypeCtor::Slice,
573+
TypeName::Ref(mutability) => TypeCtor::Ref(from_chalk(db, mutability)),
574+
TypeName::Str => TypeCtor::Str,
488575

489576
TypeName::FnDef(_) => unreachable!(),
490577

@@ -496,6 +583,71 @@ impl ToChalk for TypeCtor {
496583
}
497584
}
498585

586+
fn bitness_from_chalk_uint(uint_ty: chalk_ir::UintTy) -> IntBitness {
587+
use chalk_ir::UintTy;
588+
589+
match uint_ty {
590+
UintTy::Usize => IntBitness::Xsize,
591+
UintTy::U8 => IntBitness::X8,
592+
UintTy::U16 => IntBitness::X16,
593+
UintTy::U32 => IntBitness::X32,
594+
UintTy::U64 => IntBitness::X64,
595+
UintTy::U128 => IntBitness::X128,
596+
}
597+
}
598+
599+
fn bitness_from_chalk_int(int_ty: chalk_ir::IntTy) -> IntBitness {
600+
use chalk_ir::IntTy;
601+
602+
match int_ty {
603+
IntTy::Isize => IntBitness::Xsize,
604+
IntTy::I8 => IntBitness::X8,
605+
IntTy::I16 => IntBitness::X16,
606+
IntTy::I32 => IntBitness::X32,
607+
IntTy::I64 => IntBitness::X64,
608+
IntTy::I128 => IntBitness::X128,
609+
}
610+
}
611+
612+
fn int_ty_to_chalk(int_ty: IntTy) -> Scalar {
613+
use chalk_ir::{IntTy, UintTy};
614+
615+
match int_ty.signedness {
616+
Signedness::Signed => Scalar::Int(match int_ty.bitness {
617+
IntBitness::Xsize => IntTy::Isize,
618+
IntBitness::X8 => IntTy::I8,
619+
IntBitness::X16 => IntTy::I16,
620+
IntBitness::X32 => IntTy::I32,
621+
IntBitness::X64 => IntTy::I64,
622+
IntBitness::X128 => IntTy::I128,
623+
}),
624+
Signedness::Unsigned => Scalar::Uint(match int_ty.bitness {
625+
IntBitness::Xsize => UintTy::Usize,
626+
IntBitness::X8 => UintTy::U8,
627+
IntBitness::X16 => UintTy::U16,
628+
IntBitness::X32 => UintTy::U32,
629+
IntBitness::X64 => UintTy::U64,
630+
IntBitness::X128 => UintTy::U128,
631+
}),
632+
}
633+
}
634+
635+
impl ToChalk for Mutability {
636+
type Chalk = chalk_ir::Mutability;
637+
fn to_chalk(self, _db: &dyn HirDatabase) -> Self::Chalk {
638+
match self {
639+
Mutability::Shared => chalk_ir::Mutability::Not,
640+
Mutability::Mut => chalk_ir::Mutability::Mut,
641+
}
642+
}
643+
fn from_chalk(_db: &dyn HirDatabase, chalk: Self::Chalk) -> Self {
644+
match chalk {
645+
chalk_ir::Mutability::Mut => Mutability::Mut,
646+
chalk_ir::Mutability::Not => Mutability::Shared,
647+
}
648+
}
649+
}
650+
499651
impl ToChalk for Impl {
500652
type Chalk = ImplId;
501653

@@ -907,10 +1059,15 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
9071059
}
9081060
fn well_known_trait_id(
9091061
&self,
910-
_well_known_trait: chalk_rust_ir::WellKnownTrait,
1062+
well_known_trait: chalk_rust_ir::WellKnownTrait,
9111063
) -> Option<chalk_ir::TraitId<Interner>> {
912-
// FIXME tell Chalk about well-known traits (here and in trait_datum)
913-
None
1064+
let lang_attr = lang_attr_from_well_known_trait(well_known_trait);
1065+
let lang_items = self.db.crate_lang_items(self.krate);
1066+
let trait_ = match lang_items.target(lang_attr) {
1067+
Some(LangItemTarget::TraitId(trait_)) => trait_,
1068+
_ => return None,
1069+
};
1070+
Some(trait_.to_chalk(self.db))
9141071
}
9151072

9161073
fn program_clauses_for_env(
@@ -1012,7 +1169,8 @@ pub(crate) fn trait_datum_query(
10121169
let associated_ty_ids =
10131170
trait_data.associated_types().map(|type_alias| type_alias.to_chalk(db)).collect();
10141171
let trait_datum_bound = chalk_rust_ir::TraitDatumBound { where_clauses };
1015-
let well_known = None; // FIXME set this (depending on lang items)
1172+
let well_known =
1173+
lang_attr(db.upcast(), trait_).and_then(|name| well_known_trait_from_lang_attr(&name));
10161174
let trait_datum = TraitDatum {
10171175
id: trait_id,
10181176
binders: make_binders(trait_datum_bound, bound_vars.len()),
@@ -1023,6 +1181,25 @@ pub(crate) fn trait_datum_query(
10231181
Arc::new(trait_datum)
10241182
}
10251183

1184+
fn well_known_trait_from_lang_attr(name: &str) -> Option<WellKnownTrait> {
1185+
Some(match name {
1186+
"sized" => WellKnownTrait::SizedTrait,
1187+
"copy" => WellKnownTrait::CopyTrait,
1188+
"clone" => WellKnownTrait::CloneTrait,
1189+
"drop" => WellKnownTrait::DropTrait,
1190+
_ => return None,
1191+
})
1192+
}
1193+
1194+
fn lang_attr_from_well_known_trait(attr: WellKnownTrait) -> &'static str {
1195+
match attr {
1196+
WellKnownTrait::SizedTrait => "sized",
1197+
WellKnownTrait::CopyTrait => "copy",
1198+
WellKnownTrait::CloneTrait => "clone",
1199+
WellKnownTrait::DropTrait => "drop",
1200+
}
1201+
}
1202+
10261203
pub(crate) fn struct_datum_query(
10271204
db: &dyn HirDatabase,
10281205
krate: CrateId,

0 commit comments

Comments
 (0)