From 5d67faa5bb9cf33c559402a9279488eaa0444dd0 Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Mon, 18 Dec 2023 17:01:23 -0800 Subject: [PATCH] feat(stageleft): support crates that have no entrypoints Also includes various bugfixes needed for Hydroflow+. --- .../examples/networked_basic.rs | 2 - stageleft/README.md | 2 +- stageleft/src/lib.rs | 61 ++++++++++++------- stageleft/src/runtime_support.rs | 48 +++++++++++---- stageleft_macro/src/lib.rs | 10 +-- .../src/quote_impl/free_variable/mod.rs | 3 + stageleft_tool/src/lib.rs | 15 ++++- 7 files changed, 93 insertions(+), 48 deletions(-) diff --git a/hydroflow_plus_test/examples/networked_basic.rs b/hydroflow_plus_test/examples/networked_basic.rs index 1f18b163b070..52301cbc5f76 100644 --- a/hydroflow_plus_test/examples/networked_basic.rs +++ b/hydroflow_plus_test/examples/networked_basic.rs @@ -1,5 +1,3 @@ -use hydroflow_plus_test::*; - // cannot use hydroflow::main because connect_local_blocking causes a deadlock #[tokio::main] async fn main() { diff --git a/stageleft/README.md b/stageleft/README.md index e0f99bcf038e..067b015f1d5b 100644 --- a/stageleft/README.md +++ b/stageleft/README.md @@ -1,4 +1,4 @@ -# Stageleft +

Stageleft

Stageleft brings the magic of staged programming to Rust, making it easy to write macros with type-safe logic and high-level APIs that can generate efficient code under the hood. ## Example diff --git a/stageleft/src/lib.rs b/stageleft/src/lib.rs index 28fbcca36115..b6e668f87fe7 100644 --- a/stageleft/src/lib.rs +++ b/stageleft/src/lib.rs @@ -18,6 +18,8 @@ pub use stageleft_macro::{entry, q, quse_fn, runtime}; pub mod runtime_support; use runtime_support::FreeVariable; +use crate::runtime_support::get_final_crate_name; + #[macro_export] macro_rules! stageleft_crate { ($macro_crate:ident) => { @@ -27,7 +29,18 @@ macro_rules! stageleft_crate { #[cfg(not(feature = "macro"))] #[doc(hidden)] - #[allow(unused)] + #[allow(unused, ambiguous_glob_reexports)] + pub mod __staged { + include!(concat!(env!("OUT_DIR"), "/lib_pub.rs")); + } + }; +} + +#[macro_export] +macro_rules! stageleft_no_entry_crate { + () => { + #[doc(hidden)] + #[allow(unused, ambiguous_glob_reexports)] pub mod __staged { include!(concat!(env!("OUT_DIR"), "/lib_pub.rs")); } @@ -122,37 +135,41 @@ impl< ) }); - let final_crate = proc_macro_crate::crate_name(crate_name) - .unwrap_or_else(|_| panic!("{crate_name} should be present in `Cargo.toml`")); - let final_crate_root = match final_crate { - proc_macro_crate::FoundCrate::Itself => quote!(crate), - proc_macro_crate::FoundCrate::Name(name) => { - let ident = syn::Ident::new(&name, Span::call_site()); - quote! { #ident } - } - }; + let final_crate_root = get_final_crate_name(crate_name); let module_path: syn::Path = syn::parse_str(&module_path).unwrap(); - let module_path = module_path + let module_path_segments = module_path .segments .iter() - .skip(1) // skip crate + .skip(1) + .skip_while(|p| p.ident == "__staged") // skip crate .cloned() .collect::>(); - let module_path = syn::Path { - leading_colon: None, - segments: syn::punctuated::Punctuated::from_iter(module_path), + let module_path = if module_path_segments.is_empty() { + None + } else { + Some(syn::Path { + leading_colon: None, + segments: syn::punctuated::Punctuated::from_iter(module_path_segments), + }) }; - let expr: syn::Expr = syn::parse(expr_tokens.into()).unwrap(); - ( - None, - Some(quote!({ - use #final_crate_root::#module_path::*; + let expr: syn::Expr = syn::parse2(expr_tokens).unwrap(); + let with_env = if let Some(module_path) = module_path { + quote!({ + use #final_crate_root::__staged::#module_path::*; #(#instantiated_free_variables)* #expr - })), - ) + }) + } else { + quote!({ + use #final_crate_root::__staged::*; + #(#instantiated_free_variables)* + #expr + }) + }; + + (None, Some(with_env)) } } diff --git a/stageleft/src/runtime_support.rs b/stageleft/src/runtime_support.rs index a32dcae36e8e..c7a79026b213 100644 --- a/stageleft/src/runtime_support.rs +++ b/stageleft/src/runtime_support.rs @@ -4,6 +4,27 @@ use std::mem::MaybeUninit; use proc_macro2::{Span, TokenStream}; use quote::quote; +pub fn get_final_crate_name(crate_name: &str) -> TokenStream { + let final_crate = proc_macro_crate::crate_name(crate_name) + .unwrap_or_else(|_| panic!("{crate_name} should be present in `Cargo.toml`")); + + match final_crate { + proc_macro_crate::FoundCrate::Itself => { + if std::env::var("CARGO_BIN_NAME").is_ok() { + let underscored = crate_name.replace('-', "_"); + let underscored_ident = syn::Ident::new(&underscored, Span::call_site()); + quote! { #underscored_ident } + } else { + quote! { crate } + } + } + proc_macro_crate::FoundCrate::Name(name) => { + let ident = syn::Ident::new(&name, Span::call_site()); + quote! { #ident } + } + } +} + pub trait ParseFromLiteral { fn parse_from_literal(literal: &syn::Expr) -> Self; } @@ -54,12 +75,21 @@ pub trait FreeVariable { } } -impl FreeVariable for u32 { - fn to_tokens(self) -> (Option, Option) { - (None, Some(quote!(#self))) - } +macro_rules! impl_free_variable_from_literal_numeric { + ($($ty:ty),*) => { + $( + impl FreeVariable<$ty> for $ty { + fn to_tokens(self) -> (Option, Option) { + (None, Some(quote!(#self))) + } + } + )* + }; } +impl_free_variable_from_literal_numeric!(i8, i16, i32, i64, i128, isize); +impl_free_variable_from_literal_numeric!(u8, u16, u32, u64, u128, usize); + pub struct Import { module_path: &'static str, crate_name: &'static str, @@ -93,15 +123,7 @@ pub fn create_import( impl FreeVariable for Import { fn to_tokens(self) -> (Option, Option) { - let final_crate = proc_macro_crate::crate_name(self.crate_name) - .unwrap_or_else(|_| panic!("{} should be present in `Cargo.toml`", self.crate_name)); - let final_crate_root = match final_crate { - proc_macro_crate::FoundCrate::Itself => quote!(crate), - proc_macro_crate::FoundCrate::Name(name) => { - let ident = syn::Ident::new(&name, Span::call_site()); - quote! { #ident } - } - }; + let final_crate_root = get_final_crate_name(self.crate_name); let module_path = syn::parse_str::(self.module_path).unwrap(); let parsed = syn::parse_str::(self.path).unwrap(); diff --git a/stageleft_macro/src/lib.rs b/stageleft_macro/src/lib.rs index 975e9b09aaab..2fd610eaada1 100644 --- a/stageleft_macro/src/lib.rs +++ b/stageleft_macro/src/lib.rs @@ -331,15 +331,7 @@ pub fn entry( }; let final_crate_name = env!("STAGELEFT_FINAL_CRATE_NAME"); - let final_crate = #root::internal::proc_macro_crate::crate_name(final_crate_name) - .unwrap_or_else(|_| panic!("{final_crate_name} should be present in `Cargo.toml`")); - let final_crate_root = match final_crate { - #root::internal::proc_macro_crate::FoundCrate::Itself => ::#root::internal::quote! { crate }, - #root::internal::proc_macro_crate::FoundCrate::Name(name) => { - let ident = #root::internal::syn::Ident::new(&name, #root::internal::Span::call_site()); - ::#root::internal::quote! { #pound ident } - } - }; + let final_crate_root = #root::runtime_support::get_final_crate_name(final_crate_name); let module_path: #root::internal::syn::Path = #root::internal::syn::parse_str(module_path!()).unwrap(); let module_path = module_path.segments.iter().skip(1).cloned().collect::>(); diff --git a/stageleft_macro/src/quote_impl/free_variable/mod.rs b/stageleft_macro/src/quote_impl/free_variable/mod.rs index 56fd8765adc3..e154f4276af2 100644 --- a/stageleft_macro/src/quote_impl/free_variable/mod.rs +++ b/stageleft_macro/src/quote_impl/free_variable/mod.rs @@ -163,6 +163,9 @@ impl<'ast> Visit<'ast> for FreeVariableVisitor { fn visit_expr_method_call(&mut self, i: &'ast syn::ExprMethodCall) { syn::visit::visit_expr(self, &i.receiver); + for arg in &i.args { + self.visit_expr(arg); + } } fn visit_type(&mut self, _: &'ast syn::Type) {} diff --git a/stageleft_tool/src/lib.rs b/stageleft_tool/src/lib.rs index 35776fc64a59..baf800ae2589 100644 --- a/stageleft_tool/src/lib.rs +++ b/stageleft_tool/src/lib.rs @@ -179,6 +179,17 @@ impl VisitMut for GenFinalPubVistor { *i = parse_quote!(pub use #cur_path::#e_name;); return; } + } else if let syn::Item::Trait(e) = i { + if matches!(e.vis, syn::Visibility::Public(_)) { + let e_name = &e.ident; + *i = parse_quote!(pub use #cur_path::#e_name;); + return; + } + } else if let syn::Item::Impl(e) = i { + *i = parse_quote!( + #[cfg(feature = "macro")] + #e + ); } } @@ -189,6 +200,8 @@ impl VisitMut for GenFinalPubVistor { i.items.retain(|i| match i { syn::Item::Macro(m) => { m.mac.path.to_token_stream().to_string() != "stageleft :: stageleft_crate" + && m.mac.path.to_token_stream().to_string() + != "stageleft :: stageleft_no_entry_crate" } _ => true, }); @@ -221,6 +234,6 @@ pub fn gen_final_helper(final_crate: &str) { #[macro_export] macro_rules! gen_final { () => { - $crate::gen_final_helper(env!("CARGO_CRATE_NAME")) + $crate::gen_final_helper(env!("CARGO_PKG_NAME")) }; }