Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Commit

Permalink
Improve handling of the generic baggage fields (#5656)
Browse files Browse the repository at this point in the history
* Parse generic baggage types more carefully to preserve inner structure

* Add example

* Way too many clones

* Allow multiple generic arguments for baggage fields

* Try to detect errors earlier

* Support more types for the baggage fields, get rid of the path constraint
  • Loading branch information
vstakhov authored and al3mart committed Jul 14, 2022
1 parent 4648d38 commit 9f557bc
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 35 deletions.
14 changes: 8 additions & 6 deletions node/orchestra/examples/duo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
//! A dummy to be used with cargo expand

use orchestra::{self as orchestra, Spawner, *};
use std::collections::HashMap;
use std::{collections::HashMap, sync::Arc};
mod misc;

pub use self::misc::*;
Expand Down Expand Up @@ -61,27 +61,29 @@ impl<Context> Fortified {
}

#[orchestra(signal=SigSigSig, event=EvX, error=Yikes, gen=AllMessages)]
struct Duo<T> {
struct Duo<T, U, V, W> {
#[subsystem(consumes: MsgStrukt, sends: [Plinko])]
sub0: Awesome,

#[subsystem(blocking, consumes: Plinko, sends: [MsgStrukt])]
plinkos: GoblinTower,

i_like_pi: f64,
i_like_generic: T,
i_like_hash: HashMap<f64, f64>,
i_like_tuple: (f64, f64),
i_like_generic: Arc<T>,
i_like_hash: HashMap<(U, V), Arc<W>>,
}

fn main() {
use futures::{executor, pin_mut};

executor::block_on(async move {
let (orchestra, _handle): (Duo<_, f64>, _) = Duo::builder()
let (orchestra, _handle): (Duo<_, f64, u32, f32, f64>, _) = Duo::builder()
.sub0(AwesomeSubSys::default())
.plinkos(Fortified::default())
.i_like_pi(::std::f64::consts::PI)
.i_like_generic(42.0)
.i_like_tuple((::std::f64::consts::PI, ::std::f64::consts::PI))
.i_like_generic(Arc::new(42.0))
.i_like_hash(HashMap::new())
.spawner(DummySpawner)
.build()
Expand Down
4 changes: 2 additions & 2 deletions node/orchestra/proc-macro/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ proc-macro2 = "1.0.37"
proc-macro-crate = "1.1.3"
expander = { version = "0.0.6", default-features = false }
petgraph = "0.6.0"
itertools = { version = "0.10.3", optional = true }
itertools = { version = "0.10.3" }

[dev-dependencies]
assert_matches = "1.5"
Expand All @@ -35,4 +35,4 @@ default = [] # enable "graph" by default, blocked by <https://github.com/parityt
expand = []
# Create directional message consuming / outgoing graph.
# Generates: `${OUT_DIR}/${orchestra|lowercase}-subsystem-messaging.dot`
graph = ["itertools"]
graph = []
20 changes: 10 additions & 10 deletions node/orchestra/proc-macro/src/impl_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
// limitations under the License.

use quote::{format_ident, quote};
use syn::{parse_quote, Path, PathSegment};
use syn::{parse_quote, Path, PathSegment, TypePath};

use super::*;

Expand Down Expand Up @@ -89,7 +89,12 @@ pub(crate) fn impl_builder(info: &OrchestraInfo) -> proc_macro2::TokenStream {
let field_name = subsystem_name.iter().chain(baggage_name.iter()).collect::<Vec<_>>();
let field_type = subsystem_generics
.iter()
.map(|ident| Path::from(PathSegment::from(ident.clone())))
.map(|ident| {
syn::Type::Path(TypePath {
qself: None,
path: Path::from(PathSegment::from(ident.clone())),
})
})
.chain(info.baggage().iter().map(|bag| bag.field_ty.clone()))
.collect::<Vec<_>>();

Expand Down Expand Up @@ -249,14 +254,9 @@ pub(crate) fn impl_builder(info: &OrchestraInfo) -> proc_macro2::TokenStream {
post_setter_generics[idx] = parse_quote! { Init<#field_type> };

// Baggage can also be generic, so we need to include that to a signature
let preserved_baggage_generic = if bag_field.generic {
quote! {#field_type,}
} else {
TokenStream::new()
};

let preserved_baggage_generics = &bag_field.generic_types;
quote! {
impl <InitStateSpawner, #preserved_baggage_generic #( #subsystem_passthrough_state_generics, )* #( #impl_baggage_state_generics, )* >
impl <InitStateSpawner, #( #preserved_baggage_generics, )* #( #subsystem_passthrough_state_generics, )* #( #impl_baggage_state_generics, )* >
#builder <InitStateSpawner, #( #subsystem_passthrough_state_generics, )* #( #pre_setter_generics, )* >
{
/// Specify the baggage in the builder when it was not initialized before
Expand All @@ -278,7 +278,7 @@ pub(crate) fn impl_builder(info: &OrchestraInfo) -> proc_macro2::TokenStream {
}
}
}
impl <InitStateSpawner, #preserved_baggage_generic #( #subsystem_passthrough_state_generics, )* #( #impl_baggage_state_generics, )* >
impl <InitStateSpawner, #( #preserved_baggage_generics, )* #( #subsystem_passthrough_state_generics, )* #( #impl_baggage_state_generics, )* >
#builder <InitStateSpawner, #( #subsystem_passthrough_state_generics, )* #( #post_setter_generics, )* > {
/// Specify the baggage in the builder when it has been previously initialized
pub fn #fname (self, var: #field_type ) ->
Expand Down
95 changes: 78 additions & 17 deletions node/orchestra/proc-macro/src/parse/parse_orchestra_struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use itertools::Itertools;
use proc_macro2::{Span, TokenStream};
use std::collections::{hash_map::RandomState, HashMap, HashSet};
use syn::{
Expand All @@ -21,8 +22,8 @@ use syn::{
punctuated::Punctuated,
spanned::Spanned,
token::Bracket,
AttrStyle, Error, Field, FieldsNamed, GenericParam, Ident, ItemStruct, Path, Result, Token,
Type, Visibility,
AttrStyle, Error, Field, FieldsNamed, GenericParam, Ident, ItemStruct, Path, PathSegment,
Result, Token, Type, Visibility,
};

use quote::{quote, ToTokens};
Expand Down Expand Up @@ -106,13 +107,73 @@ pub(crate) struct SubSysField {
pub(crate) wip: bool,
}

fn try_type_to_path(ty: Type, span: Span) -> Result<Path> {
// Converts a type enum to a path if this type is a TypePath
fn try_type_to_path(ty: &Type, span: Span) -> Result<Path> {
match ty {
Type::Path(path) => Ok(path.path),
Type::Path(path) => Ok(path.path.clone()),
_ => Err(Error::new(span, "Type must be a path expression.")),
}
}

// Converts a Rust type to a list of idents recursively checking the possible values
fn flatten_type(ty: &Type, span: Span) -> Result<Vec<Ident>> {
match ty {
syn::Type::Array(ar) => flatten_type(&ar.elem, span),
syn::Type::Paren(par) => flatten_type(&par.elem, span),
syn::Type::Path(type_path) => type_path
.path
.segments
.iter()
.map(|seg| flatten_path_segments(seg, span.clone()))
.flatten_ok()
.collect::<Result<Vec<_>>>(),
syn::Type::Tuple(tup) => tup
.elems
.iter()
.map(|element| flatten_type(element, span.clone()))
.flatten_ok()
.collect::<Result<Vec<_>>>(),
_ => Err(Error::new(span, format!("Unsupported type: {:?}", ty))),
}
}

// Flatten segments of some path to a list of idents used in these segments
fn flatten_path_segments(path_segment: &PathSegment, span: Span) -> Result<Vec<Ident>> {
let mut result = vec![path_segment.ident.clone()];

match &path_segment.arguments {
syn::PathArguments::AngleBracketed(args) => {
let mut recursive_idents = args
.args
.iter()
.map(|generic_argument| match generic_argument {
syn::GenericArgument::Type(ty) => flatten_type(ty, span.clone()),
_ => Err(Error::new(
span,
format!(
"Field has a generic with an unsupported parameter {:?}",
generic_argument
),
)),
})
.flatten_ok()
.collect::<Result<Vec<_>>>()?;
result.append(&mut recursive_idents);
},
syn::PathArguments::None => {},
_ =>
return Err(Error::new(
span,
format!(
"Field has a generic with an unsupported path {:?}",
path_segment.arguments
),
)),
}

Ok(result)
}

macro_rules! extract_variant {
($unique:expr, $variant:ident ; default = $fallback:expr) => {
extract_variant!($unique, $variant).unwrap_or_else(|| $fallback)
Expand Down Expand Up @@ -254,8 +315,8 @@ impl Parse for SubSystemAttrItems {
#[derive(Debug, Clone)]
pub(crate) struct BaggageField {
pub(crate) field_name: Ident,
pub(crate) field_ty: Path,
pub(crate) generic: bool,
pub(crate) field_ty: Type,
pub(crate) generic_types: Vec<Ident>,
pub(crate) vis: Visibility,
}

Expand Down Expand Up @@ -359,8 +420,7 @@ impl OrchestraInfo {
pub(crate) fn baggage_generic_types(&self) -> Vec<Ident> {
self.baggage
.iter()
.filter(|bag| bag.generic)
.filter_map(|bag| bag.field_ty.get_ident().cloned())
.flat_map(|bag| bag.generic_types.clone())
.collect::<Vec<_>>()
}

Expand Down Expand Up @@ -423,7 +483,7 @@ impl OrchestraGuts {
let ident = ident.ok_or_else(|| {
Error::new(
ty.span(),
"Missing identifier for field, only named fields are expceted.",
"Missing identifier for field, only named fields are expected.",
)
})?;

Expand All @@ -442,7 +502,7 @@ impl OrchestraGuts {
let attr_tokens = attr_tokens.clone();
let subsystem_attrs: SubSystemAttrItems = syn::parse2(attr_tokens.clone())?;

let field_ty = try_type_to_path(ty, span)?;
let field_ty = try_type_to_path(&ty, span)?;
let generic = field_ty
.get_ident()
.ok_or_else(|| {
Expand Down Expand Up @@ -484,12 +544,13 @@ impl OrchestraGuts {
blocking,
});
} else {
let field_ty = try_type_to_path(ty, ident.span())?;
let generic = field_ty
.get_ident()
.map(|ident| baggage_generics.contains(ident))
.unwrap_or(false);
baggage.push(BaggageField { field_name: ident, generic, field_ty, vis });
let flattened = flatten_type(&ty, ident.span())?;
let generic_types = flattened
.iter()
.filter(|flat_ident| baggage_generics.contains(flat_ident))
.cloned()
.collect::<Vec<_>>();
baggage.push(BaggageField { field_name: ident, generic_types, field_ty: ty, vis });
}
}
Ok(Self { name, subsystems, baggage })
Expand All @@ -503,7 +564,7 @@ impl Parse for OrchestraGuts {
syn::Fields::Named(named) => {
let name = ds.ident.clone();

// collect the indepedentent subsystem generics
// collect the independent subsystem generics
// which need to be carried along, there are the non-generated ones
let mut orig_generics = ds.generics;

Expand Down

0 comments on commit 9f557bc

Please sign in to comment.