Skip to content

Commit 1a8c9c9

Browse files
authored
feat(query): add basic structures to query items out of the HIR (#7)
2 parents 8c6aa9d + 531fa19 commit 1a8c9c9

4 files changed

Lines changed: 319 additions & 2 deletions

File tree

src/ir/hir.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ pub struct TraitImplementation {
301301
}
302302

303303
/// A type that may be `Self`, or some other resolved type.
304-
#[derive(Clone, Debug)]
304+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
305305
pub enum MaybeSelfType {
306306
SelfType,
307307
Type(TypeId),

src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ mod ir;
77
mod lex;
88
mod passes;
99
mod prelude;
10+
mod query;
1011
mod ty;
1112
mod util;
1213

src/prelude.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ pub use crate::{
2727

2828
// Common items from standard library.
2929
pub use ::std::{
30-
collections::HashMap,
30+
collections::{HashMap, HashSet},
3131
fmt::{Debug, Display},
3232
hash::Hash,
3333
marker::PhantomData,

src/query/mod.rs

Lines changed: 316 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,316 @@
1+
#![cfg_attr(
2+
not(test),
3+
expect(dead_code, reason = "queries are not used yet"),
4+
expect(unused_imports, reason = "queries are not used yet")
5+
)]
6+
7+
use crate::prelude::*;
8+
use hir::*;
9+
10+
pub use self::traits::*;
11+
12+
/// Query builder for searching through the [`Hir`] for some [`Queryable`].
13+
pub struct Query<O: Queryable> {
14+
/// Current filter.
15+
filter: O::Filter,
16+
}
17+
18+
impl<O: Queryable> Query<O> {
19+
/// Consume the query, and produce an iterator of items which satisfy the filter.
20+
pub fn query<'hir>(self, hir: &'hir hir::Hir) -> impl Iterator<Item = &'hir O>
21+
where
22+
O: 'hir,
23+
{
24+
let candidates = O::get_all(hir);
25+
26+
candidates
27+
.iter()
28+
.filter(move |candidate| candidate.validate(&self.filter))
29+
}
30+
}
31+
32+
/// A queryable entity.
33+
pub trait Queryable: Sized {
34+
/// Representation of the filter for this entity.
35+
type Filter: Default;
36+
37+
/// ID used to represent this entity in the [`Hir`].
38+
type Id;
39+
40+
/// Fetch all entities.
41+
fn get_all(hir: &Hir) -> &IndexedVec<Self::Id, Self>;
42+
43+
/// Validate this entity against the provided filter.
44+
fn validate(&self, filter: &Self::Filter) -> bool;
45+
}
46+
47+
mod traits {
48+
use super::*;
49+
50+
/// Helper methods for querying [`Trait`]s.
51+
impl Query<Trait> {
52+
/// Create a new query for traits.
53+
pub fn traits() -> Query<Trait> {
54+
Query {
55+
filter: <Trait as Queryable>::Filter::default(),
56+
}
57+
}
58+
59+
/// Select traits satisfying the provided signature filter.
60+
pub fn methods(mut self, signatures: impl IntoIterator<Item = SignatureFilter>) -> Self {
61+
self.filter.signatures = Some(Vec::from_iter(signatures));
62+
self
63+
}
64+
}
65+
66+
/// Implementation of [`Queryable`] for [`Trait`].
67+
impl Queryable for Trait {
68+
type Filter = TraitFilter;
69+
type Id = TraitId;
70+
71+
fn get_all(hir: &Hir) -> &IndexedVec<Self::Id, Self> {
72+
&hir.traits
73+
}
74+
75+
fn validate(&self, filter: &Self::Filter) -> bool {
76+
if let Some(filter_functions) = &filter.signatures {
77+
if self.methods.len() != filter_functions.len() {
78+
// Number of functions differ.
79+
return false;
80+
}
81+
82+
let mut unvalidated = self.methods.iter_keys().collect::<HashSet<_>>();
83+
let valid = filter_functions.iter().all(|filter| {
84+
// For each unvalidated method, try match a filter.
85+
let Some(&validated) = unvalidated
86+
.iter()
87+
.find(|&&id| filter.validate(&self.methods[id]))
88+
else {
89+
return false;
90+
};
91+
92+
// Mark the method as validated.
93+
assert!(unvalidated.remove(&validated));
94+
95+
// Continue the search.
96+
true
97+
});
98+
99+
if !valid {
100+
return false;
101+
}
102+
103+
assert!(unvalidated.is_empty());
104+
};
105+
106+
true
107+
}
108+
}
109+
110+
/// Information required to filter traits.
111+
#[derive(Clone, Default, Debug)]
112+
pub struct TraitFilter {
113+
/// Signatures that the trait must contain.
114+
signatures: Option<Vec<SignatureFilter>>,
115+
}
116+
117+
/// Information required to filter a function signature.
118+
#[derive(Clone, Default, Debug)]
119+
pub struct SignatureFilter {
120+
/// Parameters which the signature must match.
121+
parameters: Option<Vec<MaybeSelfType>>,
122+
/// Return type of the signature.
123+
return_ty: Option<MaybeSelfType>,
124+
}
125+
126+
impl SignatureFilter {
127+
/// Create a new filter.
128+
pub fn new() -> Self {
129+
Self::default()
130+
}
131+
132+
/// Only accept signatures which have the provided parameters.
133+
pub fn parameters(mut self, parameters: impl IntoIterator<Item = MaybeSelfType>) -> Self {
134+
self.parameters = Some(Vec::from_iter(parameters));
135+
self
136+
}
137+
138+
/// Only accept signatures which have the provided return type.
139+
pub fn return_ty(mut self, return_ty: impl Into<MaybeSelfType>) -> Self {
140+
self.return_ty = Some(return_ty.into());
141+
self
142+
}
143+
144+
/// Validate the provided [`FunctionSignature`] matches the filter.
145+
fn validate(&self, signature: &FunctionSignature<MaybeSelfType>) -> bool {
146+
if let Some(parameters) = &self.parameters {
147+
if parameters.len() != signature.parameters.len() {
148+
// Parameter count must match.
149+
return false;
150+
}
151+
152+
// Ensure parameter types match.
153+
let parameters_match = parameters
154+
.iter()
155+
.zip(signature.parameters.iter().map(|(_, ty)| ty))
156+
.all(|(parameter, signature)| parameter == signature);
157+
if !parameters_match {
158+
return false;
159+
}
160+
}
161+
162+
if let Some(return_ty) = &self.return_ty
163+
&& return_ty != &signature.return_ty
164+
{
165+
// Return type must match.
166+
return false;
167+
}
168+
169+
true
170+
}
171+
}
172+
}
173+
174+
#[cfg(test)]
175+
mod test {
176+
use super::*;
177+
178+
#[fixture]
179+
fn ctx() -> Ctx {
180+
Ctx::new()
181+
}
182+
183+
#[fixture]
184+
fn hir() -> Hir {
185+
let mut hir = Hir::default();
186+
187+
// Empty trait.
188+
hir.traits.insert(Trait {
189+
name: TraitBindingId::from_id(0),
190+
method_scope: ScopeId::from_id(0),
191+
method_bindings: HashMap::new(),
192+
methods: IndexedVec::new(),
193+
});
194+
195+
// Single method trait.
196+
hir.traits.insert(Trait {
197+
name: TraitBindingId::from_id(1),
198+
method_scope: ScopeId::from_id(1),
199+
method_bindings: HashMap::from_iter([(
200+
IdentifierBindingId::from_id(0),
201+
TraitMethodId::from_id(0),
202+
)]),
203+
methods: indexed_vec![FunctionSignature {
204+
parameters: vec![],
205+
return_ty: MaybeSelfType::Type(TypeId::from_id(0)),
206+
}],
207+
});
208+
209+
// Single method trait with parameters.
210+
hir.traits.insert(Trait {
211+
name: TraitBindingId::from_id(2),
212+
method_scope: ScopeId::from_id(2),
213+
method_bindings: HashMap::from_iter([(
214+
IdentifierBindingId::from_id(0),
215+
TraitMethodId::from_id(0),
216+
)]),
217+
methods: indexed_vec![FunctionSignature {
218+
parameters: vec![
219+
(IdentifierBindingId::from_id(1), MaybeSelfType::SelfType),
220+
(
221+
IdentifierBindingId::from_id(2),
222+
MaybeSelfType::Type(TypeId::from_id(0))
223+
),
224+
],
225+
return_ty: MaybeSelfType::Type(TypeId::from_id(0)),
226+
}],
227+
});
228+
229+
// Multi method trait.
230+
hir.traits.insert(Trait {
231+
name: TraitBindingId::from_id(1),
232+
method_scope: ScopeId::from_id(1),
233+
method_bindings: HashMap::from_iter([
234+
(IdentifierBindingId::from_id(0), TraitMethodId::from_id(0)),
235+
(IdentifierBindingId::from_id(1), TraitMethodId::from_id(1)),
236+
]),
237+
methods: indexed_vec![
238+
FunctionSignature {
239+
parameters: vec![],
240+
return_ty: MaybeSelfType::Type(TypeId::from_id(0)),
241+
},
242+
FunctionSignature {
243+
parameters: vec![],
244+
return_ty: MaybeSelfType::Type(TypeId::from_id(0)),
245+
}
246+
],
247+
});
248+
249+
hir
250+
}
251+
252+
#[rstest]
253+
fn all_traits(hir: Hir) {
254+
assert_eq!(Query::traits().query(&hir).count(), 4);
255+
}
256+
257+
#[rstest]
258+
fn single_method_traits(hir: Hir) {
259+
assert_eq!(
260+
Query::traits()
261+
.methods([SignatureFilter::new()])
262+
.query(&hir)
263+
.count(),
264+
2
265+
);
266+
}
267+
268+
#[rstest]
269+
fn single_method_traits_returning_ty(hir: Hir) {
270+
assert_eq!(
271+
Query::traits()
272+
.methods([SignatureFilter::new().return_ty(TypeId::from_id(0))])
273+
.query(&hir)
274+
.count(),
275+
2
276+
);
277+
}
278+
279+
#[rstest]
280+
fn single_method_trait_no_parameters_returning_ty(hir: Hir) {
281+
assert_eq!(
282+
Query::traits()
283+
.methods([SignatureFilter::new()
284+
.parameters([])
285+
.return_ty(TypeId::from_id(0))])
286+
.query(&hir)
287+
.count(),
288+
1
289+
);
290+
}
291+
292+
#[rstest]
293+
fn single_method_trait_with_parameters(hir: Hir) {
294+
assert_eq!(
295+
Query::traits()
296+
.methods([SignatureFilter::new().parameters([
297+
MaybeSelfType::SelfType,
298+
MaybeSelfType::Type(TypeId::from_id(0))
299+
])])
300+
.query(&hir)
301+
.count(),
302+
1
303+
);
304+
}
305+
306+
#[rstest]
307+
fn multiple_method_trait(hir: Hir) {
308+
assert_eq!(
309+
Query::traits()
310+
.methods([SignatureFilter::new(), SignatureFilter::new()])
311+
.query(&hir)
312+
.count(),
313+
1
314+
);
315+
}
316+
}

0 commit comments

Comments
 (0)