-
Notifications
You must be signed in to change notification settings - Fork 36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Recursive macro expansion in TDL containers #647
Conversation
Codecov ReportAttention:
... and 3 files with indirect coverage changes 📢 Thoughts on this report? Let us know!. |
453df06
to
fb37931
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗺️ PR tour
@@ -12,7 +12,7 @@ use crate::lazy::binary::raw::value::LazyRawBinaryValue; | |||
use crate::lazy::decoder::private::{LazyContainerPrivate, LazyRawValuePrivate}; | |||
use crate::lazy::decoder::{ | |||
LazyDecoder, LazyRawFieldExpr, LazyRawReader, LazyRawSequence, LazyRawStruct, LazyRawValue, | |||
LazyRawValueExpr, | |||
LazyRawValueExpr, RawFieldExpr, RawValueExpr, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗺️ The enum LazyRawValueExpr<'data, D>
can be either a value literal (D::Value
) or a macro invocation (D::MacroInvocation
). Starting with this PR, we needed the ability to talk about value expressions that might have come from templates, which are not a kind of stream encoding and so do not have a corresponding implementation D: LazyDecoder<'data>
.
The new enum RawValueExpr<V, M>
allows any types to be used as the value (V
) and the macro invocation (M
). LazyRawValueExpr<'data, D>
is now a type alias for the most common kind of value expression:
pub type LazyRawValueExpr<'data, D> =
RawValueExpr<
<D as LazyDecoder<'data>>::Value,
<D as LazyDecoder<'data>>::MacroInvocation
>;
An analogous change has been made for the LazyRawFieldExpr
/RawFieldExpr
pair.
Some(Ok(LazyRawValueExpr::ValueLiteral(value))) => Some(Ok( | ||
LazyRawValueExpr::ValueLiteral(LazyRawAnyValue::from(value)), | ||
)), | ||
Some(Ok(LazyRawValueExpr::MacroInvocation(invocation))) => Some(Ok( | ||
LazyRawValueExpr::MacroInvocation(LazyRawAnyMacroInvocation { | ||
Some(Ok(RawValueExpr::ValueLiteral(value))) => { | ||
Some(Ok(RawValueExpr::ValueLiteral(LazyRawAnyValue::from(value)))) | ||
} | ||
Some(Ok(RawValueExpr::MacroInvocation(invocation))) => Some(Ok( | ||
RawValueExpr::MacroInvocation(LazyRawAnyMacroInvocation { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗺️ Now that LazyRawValueExpr<'data, D>
is a type alias, existing concrete uses of it would need to supply type annotations:
// Before:
LazyRawValueExpr::ValueLiteral(v)
// After:
LazyRawValueExpr::<'data, D>::ValueLiteral(v)
However, the compiler is able to infer the correct types for RawValuExpr
, to which the LazyRawValueExpr
alias points. I've switched instantiations of LazyRawValueExpr
to RawValueExpr
for concision.
Unfortunately, that change makes up the lion's share of the line count in this diff. x_x
An analogous change has been made for the LazyRawFieldExpr
/RawFieldExpr
pair.
pub enum ArgumentKind<'top, 'data: 'top, D: LazyDecoder<'data>, M: MacroInvocation<'data, D>> { | ||
pub enum ArgumentKind<'top, 'data, D: LazyDecoder<'data>, M: MacroInvocation<'data, D>> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗺️ In a few places, I've removed an "outlives" constraint: 'data: 'top
. While it is always true throughout our API, only some methods and trait implementations "care." Anywhere else it's just line noise and--since lifetimes are one of the tougher bits of Rust to grok--probably a barrier to understanding.
M: 'data + 'top, | ||
'data: 'top, | ||
M: 'top, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗺️ This is an equivalent formulation, but I liked that it had the familiar 'data: 'top
constraint and emphasizes the bit that our macro invocation type will live as long as 'top
.
spooky: PhantomData<(&'data D, &'data M)>, | ||
spooky: PhantomData<(&'data D, M)>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗺️ It took way too long to realize that this throwaway &'data M
added to a PhantomData
was causing a lifetime constraint in the struct expander to be unsatisfiable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is'data
strictly necessary for D
? (Also, since it's Phantom
, does the &
matter?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I needed 'data
to appear somewhere in MacroEvaluator
because it's being used in the type's trait bounds. I can leave it with D
because D
is guaranteed to outlive 'data
, but I couldn't have it with M
because the types that represent a macro invocation include 'top
lifetime template values. (&'top Element
)
} | ||
|
||
impl< | ||
'iter, | ||
'top: 'iter, | ||
'top, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗺️ Similar to an earlier change: this lifetime constraint is true, but the implementation doesn't care so it's just line noise.
@@ -391,7 +398,6 @@ impl< | |||
evaluator, | |||
context, | |||
initial_stack_depth, | |||
spooky: PhantomData, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗺️ An earlier version of this type needed PhantomData
, but it no longer does.
MacroEvaluator::<'data, D, M, BumpVec<'top, MacroExpansion<D, M>>>::new_transient( | ||
context, | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗺️ There's a subtle bug here. Even though the type annotations specify BumpVec<'top, _>
, the new()
method always constructs an evaluator backed by a Vec
.
I've opened #648 to track fixing this.
@@ -177,101 +229,212 @@ impl<'top, 'data, D: LazyDecoder<'data>> Iterator for ExpandedStructIterator<'to | |||
ref mut state, | |||
} = *self; | |||
match source { | |||
ExpandedStructIteratorSource::ValueLiteral(evaluator, iter) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗺️ This change happens to address feedback in #645.
@@ -50,7 +50,6 @@ use crate::{IonError, IonResult, SymbolTable}; | |||
/// ``` | |||
pub struct LazyList<'top, 'data, D: LazyDecoder<'data>> { | |||
pub(crate) expanded_list: LazyExpandedList<'top, 'data, D>, | |||
pub(crate) symbol_table: &'top SymbolTable, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗺️ The Lazy[Value|List|SExp|Struct
types don't need their own SymbolTable
reference; the expanded value they wrap has an EncodingContext<'top>
they can use.
// `RawValueExpr` above has no ties to a particular encoding. The `LazyRawValueExpr` type alias | ||
// below uses the `Value` and `MacroInvocation` associated types from the decoder `D`. In most | ||
// places, this is a helpful constraint; we can talk about the value expression in terms of the | ||
// LazyDecoder it's associated with. However, in some places (primarily when expanding template | ||
// values that don't have a LazyDecoder) we need to be able to use it without constraints. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be moved to the doc comments of one or both of RawValueExpr
and LazyRawValueExpr
since it's the best explanation of why you would use one vs the other.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call, I'll add this to #652.
spooky: PhantomData<(&'data D, &'data M)>, | ||
spooky: PhantomData<(&'data D, M)>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is'data
strictly necessary for D
? (Also, since it's Phantom
, does the &
matter?)
Builds on #645.
This PR adds the logic needed to recursively evaluate macro invocations found inside containers in TDL (the Template Definition Language).
Prior to this change, e-expressions (macro invocations in the data stream) inside containers would be recursively expanded:
but macro invocations in a TDL expression would not:
with the first level of invocation (the outer
(values ...)
being evaluated, but invocations nested within a container (here: a list) would not.Following this patch, TDL macro invocations found in lists and structs are evaluated correctly:
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.