Skip to content

Commit 9793795

Browse files
committed
Add CandidateValue::All enum variant.
1 parent ceb96ff commit 9793795

4 files changed

Lines changed: 79 additions & 14 deletions

File tree

trustfall_core/src/interpreter/hints/candidates.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ pub enum CandidateValue<T> {
77
Impossible, // statically determined that no values fit
88
Single(T), // there's only one value that fits
99
Multiple(Vec<T>), // there are multiple values that fit
10+
All, // all values are possible for this specific entry
11+
// (e.g. used for tag-from-non-existent-optional cases)
1012
}
1113

1214
impl<T: Debug + Clone + PartialEq + Eq> CandidateValue<T> {
@@ -28,6 +30,7 @@ impl<T: Debug + Clone + PartialEq + Eq> CandidateValue<T> {
2830
*self = CandidateValue::Impossible;
2931
}
3032
}
33+
Self::All => {} // self is unchanged.
3134
}
3235
}
3336
Self::Multiple(multiple) => {
@@ -53,8 +56,13 @@ impl<T: Debug + Clone + PartialEq + Eq> CandidateValue<T> {
5356
*self = Self::Single(first);
5457
} // otherwise it stays Multiple and we already mutated the Vec it holds
5558
}
59+
Self::All => {} // self is unchanged.
5660
}
5761
}
62+
Self::All => {
63+
// Whatever the other candidate was. It can't be any wider than Self::All.
64+
*self = other;
65+
}
5866
}
5967
}
6068
}
@@ -191,6 +199,15 @@ mod tests {
191199
Multiple(vec![&three, &two]),
192200
Multiple(vec![&two, &three]),
193201
),
202+
//
203+
// Merging Candidate::All from either position produces whatever the other value was.
204+
(All, Impossible, Impossible),
205+
(Impossible, All, Impossible),
206+
(All, Single(&one), Single(&one)),
207+
(Single(&one), All, Single(&one)),
208+
(All, Multiple(vec![&one, &two]), Multiple(vec![&one, &two])),
209+
(Multiple(vec![&one, &two]), All, Multiple(vec![&one, &two])),
210+
(All, All, All),
194211
];
195212

196213
for (original, merged, expected) in test_cases {

trustfall_core/src/interpreter/hints/mod.rs

Lines changed: 57 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -99,12 +99,12 @@ pub trait VertexInfo {
9999

100100
// non-optional, non-recursed, non-folded edge
101101
// TODO: What happens if the same edge exists more than once in a given scope?
102-
fn required_edge(&self, edge_name: &str) -> Option<EdgeInfo>;
102+
fn first_required_edge(&self, edge_name: &str) -> Option<EdgeInfo>;
103103

104104
// optional, recursed, or folded edge;
105105
// recursed because recursion always starts at depth 0
106106
// TODO: What happens if the same edge exists more than once in a given scope?
107-
fn optional_edge(&self, edge_name: &str) -> Option<EdgeInfo>;
107+
fn first_edge(&self, edge_name: &str) -> Option<EdgeInfo>;
108108
}
109109

110110
#[non_exhaustive]
@@ -238,7 +238,8 @@ impl VertexInfo for LocalQueryInfo {
238238
return Some(DynamicallyResolvedValue {
239239
query: self.query.clone(),
240240
vid: vertex.vid,
241-
resolve_on_component: self.query.query.indexed_query.vids[&vertex.vid].clone(),
241+
resolve_on_component: self.query.query.indexed_query.vids[&vertex.vid]
242+
.clone(),
242243
context_field: context_field.clone(),
243244
is_multiple: false,
244245
});
@@ -247,7 +248,8 @@ impl VertexInfo for LocalQueryInfo {
247248
return Some(DynamicallyResolvedValue {
248249
query: self.query.clone(),
249250
vid: vertex.vid,
250-
resolve_on_component: self.query.query.indexed_query.vids[&vertex.vid].clone(),
251+
resolve_on_component: self.query.query.indexed_query.vids[&vertex.vid]
252+
.clone(),
251253
context_field: context_field.clone(),
252254
is_multiple: true,
253255
});
@@ -264,7 +266,7 @@ impl VertexInfo for LocalQueryInfo {
264266
// }
265267

266268
// non-optional, non-recursed, non-folded edge
267-
fn required_edge(&self, edge_name: &str) -> Option<EdgeInfo> {
269+
fn first_required_edge(&self, edge_name: &str) -> Option<EdgeInfo> {
268270
// TODO: What happens if the same edge exists more than once in a given scope?
269271
let component = self.current_component();
270272
let current_vertex = self.current_vertex();
@@ -277,7 +279,7 @@ impl VertexInfo for LocalQueryInfo {
277279
first_matching_edge.map(|edge| self.make_non_folded_edge_info(edge.as_ref()))
278280
}
279281

280-
fn optional_edge(&self, edge_name: &str) -> Option<EdgeInfo> {
282+
fn first_edge(&self, edge_name: &str) -> Option<EdgeInfo> {
281283
// TODO: What happens if the same edge exists more than once in a given scope?
282284
let component = self.current_component();
283285
let current_vertex = self.current_vertex();
@@ -410,7 +412,9 @@ impl VertexInfo for NeighboringQueryInfo {
410412
query: self.query.clone(),
411413
vid: vertex.vid,
412414
context_field: context_field.clone(),
413-
resolve_on_component: self.query.query.indexed_query.vids[&self.starting_vertex].clone(),
415+
resolve_on_component: self.query.query.indexed_query.vids
416+
[&self.starting_vertex]
417+
.clone(),
414418
is_multiple: false,
415419
});
416420
}
@@ -421,7 +425,9 @@ impl VertexInfo for NeighboringQueryInfo {
421425
query: self.query.clone(),
422426
vid: vertex.vid,
423427
context_field: context_field.clone(),
424-
resolve_on_component: self.query.query.indexed_query.vids[&self.starting_vertex].clone(),
428+
resolve_on_component: self.query.query.indexed_query.vids
429+
[&self.starting_vertex]
430+
.clone(),
425431
is_multiple: true,
426432
});
427433
}
@@ -437,7 +443,7 @@ impl VertexInfo for NeighboringQueryInfo {
437443
// todo!()
438444
// }
439445

440-
fn required_edge(&self, edge_name: &str) -> Option<EdgeInfo> {
446+
fn first_required_edge(&self, edge_name: &str) -> Option<EdgeInfo> {
441447
// TODO: What happens if the same edge exists more than once in a given scope?
442448
let component = self.current_component();
443449
let current_vertex = self.current_vertex();
@@ -450,7 +456,7 @@ impl VertexInfo for NeighboringQueryInfo {
450456
first_matching_edge.map(|edge| self.make_non_folded_edge_info(edge.as_ref()))
451457
}
452458

453-
fn optional_edge(&self, edge_name: &str) -> Option<EdgeInfo> {
459+
fn first_edge(&self, edge_name: &str) -> Option<EdgeInfo> {
454460
// TODO: What happens if the same edge exists more than once in a given scope?
455461
let component = self.current_component();
456462
let current_vertex = self.current_vertex();
@@ -498,11 +504,30 @@ impl DynamicallyResolvedValue {
498504
&self.context_field,
499505
contexts,
500506
);
507+
let context_field_vid = self.context_field.vertex_id;
508+
let nullable_context_field = self.context_field.field_type.nullable;
501509
if self.is_multiple {
502510
Box::new(iterator.map(move |(ctx, value)| {
503511
match value {
504512
FieldValue::List(v) => (ctx, CandidateValue::Multiple(v)),
505-
FieldValue::Null => (ctx, CandidateValue::Impossible), // nullable field tagged
513+
FieldValue::Null => {
514+
// Either a nullable field was tagged, or
515+
// the @tag is inside an @optional scope that doesn't exist.
516+
let candidate = if ctx.tokens[&context_field_vid].is_none() {
517+
// @optional scope that didn't exist. Our query rules say that
518+
// any filters using this tag *must* pass.
519+
CandidateValue::All
520+
} else {
521+
// The field must have been nullable.
522+
debug_assert!(
523+
nullable_context_field,
524+
"tagged field {:?} was not nullable but received a null value for it",
525+
self.context_field,
526+
);
527+
CandidateValue::Impossible
528+
};
529+
(ctx, candidate)
530+
}
506531
bad_value => {
507532
panic!(
508533
"\
@@ -513,7 +538,27 @@ tagged field named {} of type {:?} produced an invalid value: {bad_value:?}",
513538
}
514539
}))
515540
} else {
516-
Box::new(iterator.map(|(ctx, value)| (ctx, CandidateValue::Single(value))))
541+
Box::new(iterator.map(move |(ctx, value)| match value {
542+
null_value @ FieldValue::Null => {
543+
// Either a nullable field was tagged, or
544+
// the @tag is inside an @optional scope that doesn't exist.
545+
let candidate = if ctx.tokens[&context_field_vid].is_none() {
546+
// @optional scope that didn't exist. Our query rules say that
547+
// any filters using this tag *must* pass.
548+
CandidateValue::All
549+
} else {
550+
// The field must have been nullable.
551+
debug_assert!(
552+
nullable_context_field,
553+
"tagged field {:?} was not nullable but received a null value for it",
554+
self.context_field,
555+
);
556+
CandidateValue::Single(null_value)
557+
};
558+
(ctx, candidate)
559+
}
560+
other_value => (ctx, CandidateValue::Single(other_value)),
561+
}))
517562
}
518563
}
519564
}

trustfall_core/src/interpreter/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ pub mod trace;
2626
#[derive(Debug, Clone)]
2727
pub struct DataContext<DataToken: Clone + Debug> {
2828
pub current_token: Option<DataToken>,
29-
tokens: BTreeMap<Vid, Option<DataToken>>,
29+
pub(crate) tokens: BTreeMap<Vid, Option<DataToken>>,
3030
values: Vec<FieldValue>,
3131
suspended_tokens: Vec<Option<DataToken>>,
3232
folded_contexts: BTreeMap<Eid, Vec<DataContext<DataToken>>>,

trustfall_core/src/ir/value.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,10 @@ impl FieldValue {
138138
}
139139
}
140140

141-
pub fn as_vec_with<'a, T>(&'a self, inner: impl Fn(&'a FieldValue) -> Option<T>) -> Option<Vec<T>> {
141+
pub fn as_vec_with<'a, T>(
142+
&'a self,
143+
inner: impl Fn(&'a FieldValue) -> Option<T>,
144+
) -> Option<Vec<T>> {
142145
match self {
143146
FieldValue::List(l) => {
144147
let maybe_vec: Option<Vec<T>> = l.iter().map(inner).collect();

0 commit comments

Comments
 (0)