mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-16 14:58:05 +08:00
Flip the Node trait from eval onto the frame claim's serve
This commit is contained in:
@@ -304,7 +304,7 @@ pub(crate) fn record_shape(parsed: &ParsedNodeFn) -> Option<RecordShape> {
|
||||
// Lazy secondaries are consumed as plain elements; raw record edges and
|
||||
// ranked outputs have no element binding here.
|
||||
let unsupported_lazy_secondary = |field: &ParsedField| match &field.ty {
|
||||
ParsedFieldType::Node(NodeParsedField { output_type, .. }) => is_record_value(output_type) || crate::codegen::ir::strip_ilist(output_type).1 > 0 || !field.attribute_reads.is_empty(),
|
||||
ParsedFieldType::Node(NodeParsedField { output_type, .. }) => is_served(output_type) || crate::codegen::ir::strip_ilist(output_type).1 > 0 || !field.attribute_reads.is_empty(),
|
||||
ParsedFieldType::Regular(_) => false,
|
||||
};
|
||||
if parsed.fields.iter().skip(lazy_carrier as usize).any(|field| unsupported_lazy_secondary(field)) {
|
||||
@@ -503,15 +503,17 @@ pub(crate) fn generic_assignment(field_ty: &Type, row_ty: &Type, generic: &Ident
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn is_record_value(ty: &Type) -> bool {
|
||||
matches!(ty, Type::Path(path) if path.path.segments.last().is_some_and(|segment| segment.ident == "RecordValue"))
|
||||
/// A whole-record position: the served proof a record-opaque kernel hands
|
||||
/// back, or the subject it receives without naming an element.
|
||||
pub(crate) fn is_served(ty: &Type) -> bool {
|
||||
matches!(ty, Type::Path(path) if path.path.segments.last().is_some_and(|segment| segment.ident == "Served"))
|
||||
}
|
||||
|
||||
/// Whether a kernel operates on whole records: it names `RecordValue` in its
|
||||
/// output, receives raw record edges paired with the node's layout, and
|
||||
/// Whether a kernel operates on whole records: it serves through the claim it
|
||||
/// was handed, receives raw record edges paired with the node's layout, and
|
||||
/// takes on the record APIs' unsafe contracts itself.
|
||||
pub(crate) fn record_opaque(parsed: &ParsedNodeFn) -> bool {
|
||||
is_record_value(&slot_value_type(&parsed.output_type))
|
||||
is_served(&slot_value_type(&parsed.output_type))
|
||||
}
|
||||
|
||||
pub(crate) fn routing_io(parsed: &ParsedNodeFn) -> Option<RoutingIo> {
|
||||
@@ -739,45 +741,9 @@ pub(crate) fn named_serving_lifetime(ty: &Type) -> Option<Lifetime> {
|
||||
visitor.found
|
||||
}
|
||||
|
||||
/// Rewrites a kernel-declared `ExtractArena<'e>` bound into the equality the
|
||||
/// trait names.
|
||||
pub(crate) fn desugar_extract_lifetime(bound: &TypeParamBound, core_types: &TokenStream2) -> TokenStream2 {
|
||||
desugar_extract_lifetime_at(bound, core_types, None)
|
||||
}
|
||||
|
||||
/// Renames every occurrence of the named lifetimes to `'__record` in a token
|
||||
/// stream: a flipped kernel's serving lifetime is the record lifetime at the
|
||||
/// impl, under whichever name the author picked.
|
||||
pub(crate) fn rename_lifetimes_to_record(stream: TokenStream2, names: &[String]) -> TokenStream2 {
|
||||
use proc_macro2::{Group, TokenTree};
|
||||
let mut out = Vec::new();
|
||||
let mut tokens = stream.into_iter().peekable();
|
||||
while let Some(token) = tokens.next() {
|
||||
match token {
|
||||
TokenTree::Group(group) => {
|
||||
let renamed = rename_lifetimes_to_record(group.stream(), names);
|
||||
let mut fresh = Group::new(group.delimiter(), renamed);
|
||||
fresh.set_span(group.span());
|
||||
out.push(TokenTree::Group(fresh));
|
||||
}
|
||||
TokenTree::Punct(punct) if punct.as_char() == '\'' => {
|
||||
match tokens.peek() {
|
||||
Some(TokenTree::Ident(ident)) if names.iter().any(|name| ident == name) => {
|
||||
let span = ident.span();
|
||||
tokens.next();
|
||||
out.push(TokenTree::Punct(punct));
|
||||
out.push(TokenTree::Ident(proc_macro2::Ident::new("__record", span)));
|
||||
}
|
||||
_ => out.push(TokenTree::Punct(punct)),
|
||||
}
|
||||
}
|
||||
token => out.push(token),
|
||||
}
|
||||
}
|
||||
out.into_iter().collect()
|
||||
}
|
||||
|
||||
/// As [`desugar_extract_lifetime`], with the arena lifetime overridden: a
|
||||
/// flipped kernel's serving lifetime is the record lifetime at the impl.
|
||||
pub(crate) fn desugar_extract_lifetime_at(bound: &TypeParamBound, core_types: &TokenStream2, at: Option<TokenStream2>) -> TokenStream2 {
|
||||
let TypeParamBound::Trait(trait_bound) = bound else {
|
||||
return quote!(#bound);
|
||||
};
|
||||
@@ -796,7 +762,6 @@ pub(crate) fn desugar_extract_lifetime_at(bound: &TypeParamBound, core_types: &T
|
||||
let Some(GenericArgument::Lifetime(lifetime)) = args.args.first() else {
|
||||
return quote!(#bound);
|
||||
};
|
||||
let lifetime = at.unwrap_or_else(|| quote!(#lifetime));
|
||||
quote!(#core_types::context::ExtractArena<ArenaRef = &#lifetime #core_types::arena::Arena>)
|
||||
}
|
||||
|
||||
|
||||
@@ -359,12 +359,9 @@ fn single_row_entries(parsed: &ParsedNodeFn, struct_name: &Ident, regular_fields
|
||||
let #layout = #handle.layout().clone();
|
||||
let #name = #handle.downcast_record::<#value_ty>()?;
|
||||
},
|
||||
SlotKind::Extracted(value_ty) => quote! {
|
||||
let #handle = inputs.next().unwrap();
|
||||
let #layout = #handle.layout().clone();
|
||||
let #name = gcore::record::RecordExtract::<#value_ty, _>::new(#handle.downcast_record::<#value_ty>()?, &#layout);
|
||||
},
|
||||
SlotKind::Ranked(value_ty) => quote! {
|
||||
// The node reads the element off the edge's own layout, so
|
||||
// neither slot rides a layout to the constructor.
|
||||
SlotKind::Extracted(value_ty) | SlotKind::Ranked(value_ty) => quote! {
|
||||
let #name = inputs.next().unwrap().downcast_record::<#value_ty>()?;
|
||||
},
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use crate::codegen::classify::{
|
||||
Dialect, RoutingIo, bare_ident, context_param, dialect, flip_carrier, generic_assignment, generic_extractable, is_record_value, record_shape, routing_io, slot_value_type,
|
||||
Dialect, RoutingIo, bare_ident, context_param, dialect, flip_carrier, generic_assignment, generic_extractable, is_served, record_shape, routing_io, slot_value_type,
|
||||
};
|
||||
use crate::codegen::entries::implementation_rows;
|
||||
use crate::parsing::{AttributeRead, NodeParsedField, ParsedField, ParsedFieldType, ParsedNodeFn, RecordWrites, RegularParsedField, record_writes};
|
||||
@@ -77,7 +77,7 @@ fn inputs(parsed: &ParsedNodeFn, fields: &[&ParsedField], generics: &[Ident]) ->
|
||||
fn subject(index: usize, field: &ParsedField, carrier_subject: bool, routing: Option<&RoutingIo>) -> bool {
|
||||
match &field.ty {
|
||||
ParsedFieldType::Node(NodeParsedField { output_type, .. }) => {
|
||||
is_record_value(output_type) || routing.is_some_and(|routing| crate::codegen::classify::routing_source_output(output_type, &routing.generic)) || (index == 0 && carrier_subject)
|
||||
is_served(output_type) || routing.is_some_and(|routing| crate::codegen::classify::routing_source_output(output_type, &routing.generic)) || (index == 0 && carrier_subject)
|
||||
}
|
||||
ParsedFieldType::Regular(RegularParsedField { ty, .. }) => routing.is_some_and(|routing| bare_ident(ty) == Some(&routing.generic)) || (index == 0 && carrier_subject),
|
||||
}
|
||||
@@ -166,7 +166,7 @@ fn item_shape(element: &Type, depth: u8, reads: &[AttributeRead], generics: &[Id
|
||||
}
|
||||
|
||||
fn element_of(ty: &Type, generics: &[Ident]) -> Element {
|
||||
if is_record_value(ty) {
|
||||
if is_served(ty) {
|
||||
return Element::Opaque;
|
||||
}
|
||||
match bare_ident(ty) {
|
||||
@@ -371,10 +371,11 @@ pub(crate) enum ValueBinding {
|
||||
}
|
||||
|
||||
/// How a lazy (`impl Node`) input binds in eval. The `Poll` effect further
|
||||
/// selects the borrowed vs `__cell`-driven form within `Element`/`Plain`.
|
||||
/// selects the borrowed vs `__cell`-driven form within `Element`/`Generic`.
|
||||
pub(crate) enum LazyBinding {
|
||||
Element,
|
||||
Plain,
|
||||
/// The kernel holds the whole record behind a bare generic element.
|
||||
Generic,
|
||||
DeriveRouting,
|
||||
DeriveCarrier,
|
||||
OpaqueRecord,
|
||||
@@ -383,7 +384,7 @@ pub(crate) enum LazyBinding {
|
||||
impl ValueBinding {
|
||||
/// Copies an element out of a record edge, so the frame is reclaimed after.
|
||||
pub(crate) fn reads_out(&self) -> bool {
|
||||
matches!(self, ValueBinding::ReadingSecondary | ValueBinding::RecordElement)
|
||||
matches!(self, ValueBinding::ReadingSecondary | ValueBinding::RecordElement | ValueBinding::Plain)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -484,7 +485,7 @@ pub(crate) fn lazy_binding(node: &Node, index: usize) -> LazyBinding {
|
||||
} else if matches!(input.shape.element, Element::Opaque) {
|
||||
LazyBinding::OpaqueRecord
|
||||
} else {
|
||||
LazyBinding::Plain
|
||||
LazyBinding::Generic
|
||||
}
|
||||
}
|
||||
|
||||
@@ -654,7 +655,7 @@ mod tests {
|
||||
} else if kinds.opaque {
|
||||
let record = fields
|
||||
.iter()
|
||||
.position(|field| matches!(&field.ty, ParsedFieldType::Node(NodeParsedField { output_type, .. }) if is_record_value(output_type)));
|
||||
.position(|field| matches!(&field.ty, ParsedFieldType::Node(NodeParsedField { output_type, .. }) if is_served(output_type)));
|
||||
Facts {
|
||||
sources: record.into_iter().collect(),
|
||||
carried: true,
|
||||
@@ -770,7 +771,7 @@ mod tests {
|
||||
assert_bridge(
|
||||
quote!(category("")),
|
||||
quote! {
|
||||
fn memo<'e>(_: impl Ctx, #[data] cache: Store, content: impl Node<Context<'_>, Output = RecordValue<'e>>) -> GPoll<RecordValue<'e>> { content.eval(()) }
|
||||
fn memo<'e, 'l>(_: impl Ctx, #[data] cache: Store, content: impl Node<Context<'_>>, slot: FrameClaim<'l>) -> GPoll<Served<'e>> { content.serve(&(), slot) }
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -819,7 +820,7 @@ mod tests {
|
||||
"flip-raw"
|
||||
} else if flip {
|
||||
"flip-lazy"
|
||||
} else if opaque && raw && is_record_value(output_type) {
|
||||
} else if opaque && raw && is_served(output_type) {
|
||||
"opaque-record"
|
||||
} else if raw {
|
||||
"raw-lazy"
|
||||
@@ -846,8 +847,8 @@ mod tests {
|
||||
(LazyBinding::OpaqueRecord, _) => "opaque-record",
|
||||
(LazyBinding::Element, true) => "flip-raw",
|
||||
(LazyBinding::Element, false) => "flip-lazy",
|
||||
(LazyBinding::Plain, true) => "raw-lazy",
|
||||
(LazyBinding::Plain, false) => "lazy",
|
||||
(LazyBinding::Generic, true) => "raw-lazy",
|
||||
(LazyBinding::Generic, false) => "lazy",
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1012,8 +1013,8 @@ mod tests {
|
||||
assert_bindings(
|
||||
quote!(category("")),
|
||||
quote!(
|
||||
fn memo<'e>(_: impl Ctx, #[data] cache: Store, content: impl Node<Context<'_>, Output = RecordValue<'e>>) -> GPoll<RecordValue<'e>> {
|
||||
content.eval(())
|
||||
fn memo<'e, 'l>(_: impl Ctx, #[data] cache: Store, content: impl Node<Context<'_>>, slot: FrameClaim<'l>) -> GPoll<Served<'e>> {
|
||||
content.serve(&(), slot)
|
||||
}
|
||||
),
|
||||
);
|
||||
|
||||
@@ -17,7 +17,7 @@ pub(crate) fn generate_node_input_references(
|
||||
|
||||
for (input_index, (parsed_input, input_ident)) in parsed.fields.iter().zip(field_idents).enumerate() {
|
||||
// `IList` nesting is rank metadata, not part of the value type.
|
||||
let mut ty = match &parsed_input.ty {
|
||||
let ty = match &parsed_input.ty {
|
||||
ParsedFieldType::Regular(RegularParsedField { ty, .. }) => ty.clone(),
|
||||
ParsedFieldType::Node(NodeParsedField { output_type, .. }) => crate::codegen::ir::strip_ilist(output_type).0,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user