Skip to content

Commit

Permalink
feat(stageleft): support crates that have no entrypoints (#983)
Browse files Browse the repository at this point in the history
Also includes various bugfixes needed for Hydroflow+.
  • Loading branch information
shadaj authored Dec 19, 2023
1 parent 27dabcf commit 7108323
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 48 deletions.
2 changes: 0 additions & 2 deletions hydroflow_plus_test/examples/networked_basic.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use hydroflow_plus_test::*;

// cannot use hydroflow::main because connect_local_blocking causes a deadlock
#[tokio::main]
async fn main() {
Expand Down
2 changes: 1 addition & 1 deletion stageleft/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Stageleft
<h1 class="crate-title">Stageleft</h1>
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
Expand Down
61 changes: 39 additions & 22 deletions stageleft/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand All @@ -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"));
}
Expand Down Expand Up @@ -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::<Vec<_>>();
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))
}
}

Expand Down
48 changes: 35 additions & 13 deletions stageleft/src/runtime_support.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -54,12 +75,21 @@ pub trait FreeVariable<O> {
}
}

impl FreeVariable<u32> for u32 {
fn to_tokens(self) -> (Option<TokenStream>, Option<TokenStream>) {
(None, Some(quote!(#self)))
}
macro_rules! impl_free_variable_from_literal_numeric {
($($ty:ty),*) => {
$(
impl FreeVariable<$ty> for $ty {
fn to_tokens(self) -> (Option<TokenStream>, Option<TokenStream>) {
(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<T> {
module_path: &'static str,
crate_name: &'static str,
Expand Down Expand Up @@ -93,15 +123,7 @@ pub fn create_import<T>(

impl<T> FreeVariable<T> for Import<T> {
fn to_tokens(self) -> (Option<TokenStream>, Option<TokenStream>) {
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::<syn::Path>(self.module_path).unwrap();
let parsed = syn::parse_str::<syn::Path>(self.path).unwrap();
Expand Down
10 changes: 1 addition & 9 deletions stageleft_macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Vec<_>>();
Expand Down
3 changes: 3 additions & 0 deletions stageleft_macro/src/quote_impl/free_variable/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {}
Expand Down
15 changes: 14 additions & 1 deletion stageleft_tool/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
);
}
}

Expand All @@ -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,
});
Expand Down Expand Up @@ -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"))
};
}

0 comments on commit 7108323

Please sign in to comment.