Skip to content

Commit dcd91d0

Browse files
committed
Auto merge of #794 - HKalbasi:ptr-metadata, r=compiler-errors
Add support for `Pointee` trait cc rust-lang/rust-analyzer#13992
2 parents 0ecdd7f + 16a3b37 commit dcd91d0

File tree

11 files changed

+349
-22
lines changed

11 files changed

+349
-22
lines changed

chalk-integration/src/lowering.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,6 +1140,7 @@ impl Lower for WellKnownTrait {
11401140
WellKnownTrait::Generator => rust_ir::WellKnownTrait::Generator,
11411141
WellKnownTrait::DispatchFromDyn => rust_ir::WellKnownTrait::DispatchFromDyn,
11421142
WellKnownTrait::Tuple => rust_ir::WellKnownTrait::Tuple,
1143+
WellKnownTrait::Pointee => rust_ir::WellKnownTrait::Pointee,
11431144
}
11441145
}
11451146
}

chalk-parse/src/ast.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ pub enum WellKnownTrait {
162162
Generator,
163163
DispatchFromDyn,
164164
Tuple,
165+
Pointee,
165166
}
166167

167168
#[derive(Clone, PartialEq, Eq, Debug)]

chalk-parse/src/parser.lalrpop

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ WellKnownTrait: WellKnownTrait = {
7070
"#" "[" "lang" "(" "generator" ")" "]" => WellKnownTrait::Generator,
7171
"#" "[" "lang" "(" "dispatch_from_dyn" ")" "]" => WellKnownTrait::DispatchFromDyn,
7272
"#" "[" "lang" "(" "tuple_trait" ")" "]" => WellKnownTrait::Tuple,
73+
"#" "[" "lang" "(" "pointee_trait" ")" "]" => WellKnownTrait::Pointee,
7374
};
7475

7576
AdtReprAttr: AdtReprAttr = {

chalk-solve/src/clauses/builtin_traits.rs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
use super::{builder::ClauseBuilder, generalize};
2-
use crate::{CanonicalVarKinds, Interner, RustIrDatabase, TraitRef, WellKnownTrait};
2+
use crate::{
3+
rust_ir::AdtKind, CanonicalVarKinds, Interner, RustIrDatabase, TraitRef, WellKnownTrait,
4+
};
35
use chalk_ir::{Floundered, Substitution, Ty};
46

57
mod clone;
68
mod copy;
79
mod discriminant_kind;
810
mod fn_family;
911
mod generator;
12+
mod pointee;
1013
mod sized;
1114
mod tuple;
1215
mod unsize;
@@ -54,6 +57,9 @@ pub fn add_builtin_program_clauses<I: Interner>(
5457
WellKnownTrait::Tuple => {
5558
tuple::add_tuple_program_clauses(db, builder, self_ty)?;
5659
}
60+
WellKnownTrait::Pointee => {
61+
pointee::add_pointee_program_clauses(db, builder, self_ty)?;
62+
}
5763
// There are no builtin impls provided for the following traits:
5864
WellKnownTrait::Unpin
5965
| WellKnownTrait::Drop
@@ -83,6 +89,16 @@ pub fn add_builtin_assoc_program_clauses<I: Interner>(
8389
Ok(())
8490
})
8591
}
92+
WellKnownTrait::Pointee => {
93+
// If `self_ty` contains bound vars, we want to universally quantify them.
94+
// `Generalize` collects them for us.
95+
let generalized = generalize::Generalize::apply(db.interner(), self_ty);
96+
97+
builder.push_binders(generalized, |builder, self_ty| {
98+
pointee::add_pointee_program_clauses(db, builder, self_ty)?;
99+
Ok(())
100+
})
101+
}
86102
WellKnownTrait::DiscriminantKind => {
87103
discriminant_kind::add_discriminant_clauses(db, builder, self_ty)
88104
}
@@ -97,6 +113,26 @@ pub fn add_builtin_assoc_program_clauses<I: Interner>(
97113
}
98114
}
99115

116+
/// Returns type of the last field of the input struct, which is useful for `Sized` and related
117+
/// traits. Returns `None` if the input is not a struct or it has no fields.
118+
fn last_field_of_struct<I: Interner>(
119+
db: &dyn RustIrDatabase<I>,
120+
id: chalk_ir::AdtId<I>,
121+
subst: &Substitution<I>,
122+
) -> Option<Ty<I>> {
123+
let adt_datum = db.adt_datum(id);
124+
let interner = db.interner();
125+
if adt_datum.kind != AdtKind::Struct {
126+
return None;
127+
}
128+
let last_field_ty = adt_datum
129+
.binders
130+
.map_ref(|b| b.variants.last()?.fields.last().cloned())
131+
.filter_map(|x| x)?
132+
.substitute(interner, subst);
133+
Some(last_field_ty)
134+
}
135+
100136
/// Given a trait ref `T0: Trait` and a list of types `U0..Un`, pushes a clause of the form
101137
/// `Implemented(T0: Trait) :- Implemented(U0: Trait) .. Implemented(Un: Trait)`
102138
pub fn needs_impl_for_tys<I: Interner>(
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
use crate::clauses::ClauseBuilder;
2+
use crate::rust_ir::WellKnownTrait;
3+
use crate::{Interner, RustIrDatabase, TraitRef};
4+
use chalk_ir::{
5+
AliasTy, Floundered, Normalize, ProjectionTy, Substitution, Ty, TyKind, TyVariableKind,
6+
};
7+
8+
use super::last_field_of_struct;
9+
10+
fn push_clauses<I: Interner>(
11+
db: &dyn RustIrDatabase<I>,
12+
builder: &mut ClauseBuilder<'_, I>,
13+
self_ty: Ty<I>,
14+
metadata: Ty<I>,
15+
) {
16+
let interner = db.interner();
17+
let trait_id = db.well_known_trait_id(WellKnownTrait::Pointee).unwrap();
18+
let substitution = Substitution::from1(interner, self_ty);
19+
let trait_datum = db.trait_datum(trait_id);
20+
assert_eq!(
21+
trait_datum.associated_ty_ids.len(),
22+
1,
23+
"Pointee trait should have exactly one associated type, found {:?}",
24+
trait_datum.associated_ty_ids
25+
);
26+
let metadata_id = trait_datum.associated_ty_ids[0];
27+
let alias = AliasTy::Projection(ProjectionTy {
28+
associated_ty_id: metadata_id,
29+
substitution,
30+
});
31+
builder.push_fact(Normalize {
32+
alias,
33+
ty: metadata,
34+
});
35+
}
36+
37+
/// Add implicit impl for the `Pointee` trait for all types
38+
pub fn add_pointee_program_clauses<I: Interner>(
39+
db: &dyn RustIrDatabase<I>,
40+
builder: &mut ClauseBuilder<'_, I>,
41+
self_ty: Ty<I>,
42+
) -> Result<(), Floundered> {
43+
let interner = db.interner();
44+
let trait_id = db.well_known_trait_id(WellKnownTrait::Pointee).unwrap();
45+
let substitution = Substitution::from1(interner, self_ty.clone());
46+
builder.push_fact(TraitRef {
47+
trait_id,
48+
substitution: substitution.clone(),
49+
});
50+
match self_ty.kind(interner) {
51+
TyKind::Str | TyKind::Slice(_) => push_clauses(
52+
db,
53+
builder,
54+
self_ty.clone(),
55+
TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize)).intern(interner),
56+
),
57+
TyKind::Array(_, _)
58+
| TyKind::Never
59+
| TyKind::Closure(_, _)
60+
| TyKind::FnDef(_, _)
61+
| TyKind::Scalar(_)
62+
| TyKind::Raw(_, _)
63+
| TyKind::Function(_)
64+
| TyKind::InferenceVar(_, TyVariableKind::Float)
65+
| TyKind::InferenceVar(_, TyVariableKind::Integer)
66+
| TyKind::Generator(_, _)
67+
| TyKind::GeneratorWitness(_, _)
68+
| TyKind::Ref(_, _, _) => push_clauses(
69+
db,
70+
builder,
71+
self_ty,
72+
TyKind::Tuple(0, Substitution::empty(interner)).intern(interner),
73+
),
74+
TyKind::Adt(id, subst) => {
75+
if let Some(last_field_ty) = last_field_of_struct(db, *id, subst) {
76+
push_for_last_field(last_field_ty, db, builder, self_ty);
77+
} else {
78+
push_clauses(
79+
db,
80+
builder,
81+
self_ty,
82+
TyKind::Tuple(0, Substitution::empty(interner)).intern(interner),
83+
);
84+
}
85+
}
86+
TyKind::Tuple(_, subst) => {
87+
let last_field_ty = subst
88+
.iter(interner)
89+
.rev()
90+
.next()
91+
.and_then(|x| x.ty(interner))
92+
.cloned();
93+
if let Some(last_field_ty) = last_field_ty {
94+
push_for_last_field(last_field_ty, db, builder, self_ty);
95+
} else {
96+
push_clauses(
97+
db,
98+
builder,
99+
self_ty,
100+
TyKind::Tuple(0, Substitution::empty(interner)).intern(interner),
101+
);
102+
}
103+
}
104+
TyKind::BoundVar(_)
105+
| TyKind::AssociatedType(_, _)
106+
| TyKind::OpaqueType(_, _)
107+
| TyKind::Foreign(_)
108+
| TyKind::Error
109+
| TyKind::Placeholder(_)
110+
| TyKind::Alias(_) => (),
111+
TyKind::Dyn(_) => {
112+
// FIXME: We should add a `Normalize(<dyn Trait as Pointee>::Metadata -> DynMetadata<dyn Trait>)` here, but
113+
// since chalk doesn't have the concept of lang item structs yet, we can't.
114+
}
115+
TyKind::InferenceVar(_, TyVariableKind::General) => return Err(Floundered),
116+
}
117+
Ok(())
118+
}
119+
120+
fn push_for_last_field<I: Interner>(
121+
last_field_ty: Ty<I>,
122+
db: &dyn RustIrDatabase<I>,
123+
builder: &mut ClauseBuilder<'_, I>,
124+
self_ty: Ty<I>,
125+
) {
126+
let interner = db.interner();
127+
let _ = add_pointee_program_clauses(db, builder, last_field_ty.clone());
128+
let trait_id = db.well_known_trait_id(WellKnownTrait::Pointee).unwrap();
129+
let trait_datum = db.trait_datum(trait_id);
130+
assert_eq!(
131+
trait_datum.associated_ty_ids.len(),
132+
1,
133+
"Pointee trait should have exactly one associated type, found {:?}",
134+
trait_datum.associated_ty_ids
135+
);
136+
let metadata_id = trait_datum.associated_ty_ids[0];
137+
let alias_last_field = AliasTy::Projection(ProjectionTy {
138+
associated_ty_id: metadata_id,
139+
substitution: Substitution::from1(interner, last_field_ty),
140+
});
141+
let alias_self = AliasTy::Projection(ProjectionTy {
142+
associated_ty_id: metadata_id,
143+
substitution: Substitution::from1(interner, self_ty),
144+
});
145+
builder.push_fact(Normalize {
146+
alias: alias_self,
147+
ty: TyKind::Alias(alias_last_field).intern(interner),
148+
});
149+
}

chalk-solve/src/clauses/builtin_traits/sized.rs

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,39 +2,22 @@ use std::iter;
22

33
use crate::clauses::builtin_traits::needs_impl_for_tys;
44
use crate::clauses::ClauseBuilder;
5-
use crate::rust_ir::AdtKind;
65
use crate::{Interner, RustIrDatabase, TraitRef};
76
use chalk_ir::{
87
AdtId, CanonicalVarKinds, Floundered, Substitution, TyKind, TyVariableKind, VariableKind,
98
};
109

10+
use super::last_field_of_struct;
11+
1112
fn push_adt_sized_conditions<I: Interner>(
1213
db: &dyn RustIrDatabase<I>,
1314
builder: &mut ClauseBuilder<'_, I>,
1415
trait_ref: TraitRef<I>,
1516
adt_id: AdtId<I>,
1617
substitution: &Substitution<I>,
1718
) {
18-
let adt_datum = db.adt_datum(adt_id);
19-
20-
// WF ensures that all enums are Sized, so we only have to consider structs.
21-
if adt_datum.kind != AdtKind::Struct {
22-
builder.push_fact(trait_ref);
23-
return;
24-
}
25-
26-
let interner = db.interner();
27-
28-
// To check if a struct S<..> is Sized, we only have to look at its last field.
29-
// This is because the WF checks for ADTs require that all the other fields must be Sized.
30-
let last_field_ty = adt_datum
31-
.binders
32-
.map_ref(|b| b.variants.clone())
33-
.substitute(interner, substitution)
34-
.into_iter()
35-
.take(1) // We have a struct so we're guaranteed one variant
36-
.flat_map(|mut v| v.fields.pop());
37-
19+
// We only need to check last field of the struct here. Rest of the fields and cases are handled in WF.
20+
let last_field_ty = last_field_of_struct(db, adt_id, substitution).into_iter();
3821
needs_impl_for_tys(db, builder, trait_ref, last_field_ty);
3922
}
4023

chalk-solve/src/display/items.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ impl<I: Interner> RenderAsRust<I> for TraitDatum<I> {
206206
WellKnownTrait::Generator => "generator",
207207
WellKnownTrait::DispatchFromDyn => "dispatch_from_dyn",
208208
WellKnownTrait::Tuple => "tuple_trait",
209+
WellKnownTrait::Pointee => "pointee",
209210
};
210211
writeln!(f, "#[lang({})]", name)?;
211212
}

chalk-solve/src/rust_ir.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ pub enum WellKnownTrait {
277277
Generator,
278278
DispatchFromDyn,
279279
Tuple,
280+
Pointee,
280281
}
281282

282283
chalk_ir::const_visit!(WellKnownTrait);

chalk-solve/src/wf.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,7 @@ where
436436
| WellKnownTrait::Sized
437437
| WellKnownTrait::DiscriminantKind
438438
| WellKnownTrait::Generator
439+
| WellKnownTrait::Pointee
439440
| WellKnownTrait::Tuple => false,
440441
};
441442

tests/test/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,7 @@ mod never;
427427
mod numerics;
428428
mod object_safe;
429429
mod opaque_types;
430+
mod pointee;
430431
mod projection;
431432
mod refs;
432433
mod scalars;

0 commit comments

Comments
 (0)