Skip to content

Commit

Permalink
Require default in macro call
Browse files Browse the repository at this point in the history
Previously, existence of any generic parameters implied `default`.
However, this doesn't play well with specializations that may involve
lifetimes (I'm not sure yet if `min_specialization` is fine with those,
but regardless...). Explicit is better than implicit any way.

This is a breaking change since it modifies macro's call convention.
  • Loading branch information
ozars committed Apr 8, 2024
1 parent edc978e commit 0cfa2a6
Show file tree
Hide file tree
Showing 5 changed files with 22 additions and 22 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ fn example<Arg>(arg: Arg) -> String {
Arg -> String,
// Defaut implementation. At least one default value is required.
// Referring to values other than the argument is not supported.
fn <T>(_: T) => format!("default value"),
default fn <T>(_: T) => format!("default value"),
// Specialization for concrete type u8.
fn (v: u8) => format!("u8: {}", v),
// Specialization for concrete type u16.
Expand Down Expand Up @@ -107,7 +107,7 @@ fn example<Arg: Display>(arg: Arg) -> String {
arg,
Arg -> String,
// Notice the trait bound.
fn <T: Display>(v: T) => format!("default value: {}", v),
default fn <T: Display>(v: T) => format!("default value: {}", v),
// Note that specializations also need to satisfy the same bound.
fn (v: u8) => format!("u8: {}", v),
fn (v: u16) => format!("u16: {}", v),
Expand Down
2 changes: 1 addition & 1 deletion examples/simple_example.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ fn example<Arg>(arg: Arg) -> String {
Arg -> String,
// Defaut implementation. At least one default value is required.
// Referring to values other than the argument is not supported.
fn <T>(_: T) => format!("default value"),
default fn <T>(_: T) => format!("default value"),
// Specialization for concrete type u8.
fn (v: u8) => format!("u8: {}", v),
// Specialization for concrete type u16.
Expand Down
2 changes: 1 addition & 1 deletion examples/trait_bound.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ fn example<Arg: Display>(arg: Arg) -> String {
arg,
Arg -> String,
// Notice the trait bound.
fn <T: Display>(v: T) => format!("default value: {}", v),
default fn <T: Display>(v: T) => format!("default value: {}", v),
// Note that specializations also need to satisfy the same bound.
fn (v: u8) => format!("u8: {}", v),
fn (v: u16) => format!("u16: {}", v),
Expand Down
24 changes: 12 additions & 12 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use syn::{
parse::{Parse, ParseStream},
parse_macro_input,
punctuated::Punctuated,
token::Default,
Expr, GenericParam, Ident, Result, Token, Type,
};

Expand Down Expand Up @@ -59,6 +58,7 @@ impl ToTokens for ArgNameExpr {
/// ```
#[derive(Debug, Eq, PartialEq)]
struct DispatchArmExpr {
default: Option<Token![default]>,
generic_params: Option<Punctuated<GenericParam, Token![,]>>,
arg_name: ArgNameExpr,
arg_type: Type,
Expand All @@ -67,6 +67,7 @@ struct DispatchArmExpr {

impl Parse for DispatchArmExpr {
fn parse(input: ParseStream) -> Result<Self> {
let default = input.parse::<Option<Token![default]>>()?;
let _ = input.parse::<Token![fn]>()?;
let generic_params = if input.peek(Token![<]) {
let _ = input.parse::<Token![<]>()?;
Expand All @@ -88,6 +89,7 @@ impl Parse for DispatchArmExpr {
let _ = input.parse::<Token![=>]>()?;
let body = input.parse()?;
Ok(Self {
default,
generic_params,
arg_name,
arg_type,
Expand Down Expand Up @@ -150,7 +152,7 @@ fn generate_trait_declaration(trait_name: &Ident, return_type: &Type) -> TokenSt
/// Generates implementation of the helper trait for specialized dispatch arms. This covers both
/// generic case(s) and concrete case(s).
fn generate_trait_implementation(
default: Option<Token![default]>,
default: Option<&Token![default]>,
trait_name: &Ident,
generic_params: Option<&Punctuated<GenericParam, Token![,]>>,
arg_type: &Type,
Expand Down Expand Up @@ -184,14 +186,7 @@ impl ToTokens for SpecializedDispatchExpr {

for arm in &self.arms {
trait_impls.extend(generate_trait_implementation(
// TODO(ozars): Make `default` come from the macro declaration instead of inferring
// it here. This will be a breaking change, but that should be fine since the crate
// is a toddler anyway. Aim for 0.2.0.
if arm.generic_params.is_some() {
Some(Default::default())
} else {
None
},
arm.default.as_ref(),
&trait_name,
arm.generic_params.as_ref(),
&arm.arg_type,
Expand Down Expand Up @@ -233,6 +228,7 @@ mod tests {
assert_eq!(
arm,
DispatchArmExpr {
default: None,
generic_params: None,
arg_name: parse_quote!(v),
arg_type: parse_quote!(u8),
Expand All @@ -243,10 +239,11 @@ mod tests {

#[test]
fn parse_arm_with_generic_type() {
let arm: DispatchArmExpr = parse_quote!(fn <T>(_: T) => format!("default value"));
let arm: DispatchArmExpr = parse_quote!(default fn <T>(_: T) => format!("default value"));
assert_eq!(
arm,
DispatchArmExpr {
default: Some(Default::default()),
generic_params: Some(parse_quote!(T)),
arg_name: parse_quote!(_),
arg_type: parse_quote!(T),
Expand All @@ -260,7 +257,7 @@ mod tests {
let expr: SpecializedDispatchExpr = parse_quote! {
arg,
Arg -> String,
fn <T>(_: T) => format!("default value"),
default fn <T>(_: T) => format!("default value"),
fn (v: u8) => format!("u8: {}", v),
fn (v: u16) => format!("u16: {}", v),
};
Expand All @@ -272,18 +269,21 @@ mod tests {
to_type: parse_quote!(String),
arms: vec![
DispatchArmExpr {
default: Some(Default::default()),
generic_params: Some(parse_quote!(T)),
arg_name: parse_quote!(_),
arg_type: parse_quote!(T),
body: parse_quote!(format!("default value")),
},
DispatchArmExpr {
default: None,
generic_params: None,
arg_name: parse_quote!(v),
arg_type: parse_quote!(u8),
body: parse_quote!(format!("u8: {}", v)),
},
DispatchArmExpr {
default: None,
generic_params: None,
arg_name: parse_quote!(v),
arg_type: parse_quote!(u16),
Expand Down
12 changes: 6 additions & 6 deletions tests/integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ fn test_example() {
specialized_dispatch!(
arg,
Arg -> String,
fn <T>(_: T) => format!("default value"),
default fn <T>(_: T) => format!("default value"),
fn (v: u8) => format!("u8: {}", v),
fn (v: u16) => format!("u16: {}", v),
)
Expand All @@ -27,7 +27,7 @@ fn test_example_different_order() {
Arg -> String,
fn (v: u8) => format!("u8: {}", v),
fn (v: u16) => format!("u16: {}", v),
fn <T>(_: T) => format!("default value"),
default fn <T>(_: T) => format!("default value"),
)
}

Expand All @@ -43,14 +43,14 @@ fn test_multiple_calls_in_same_scope() {
u8 -> &'static str,
fn (_: u8) => "u8",
fn (_: u16) => "u16",
fn <T>(_: T) => "other",
default fn <T>(_: T) => "other",
);
let s2 = specialized_dispatch!(
0u16,
u16 -> &'static str,
fn (_: u8) => "u8",
fn (_: u16) => "u16",
fn <T>(_: T) => "other",
default fn <T>(_: T) => "other",
);
assert_eq!(format!("{}-{}", s1, s2), "u8-u16");
}
Expand All @@ -63,7 +63,7 @@ fn test_bound_traits() {
specialized_dispatch!(
arg,
Arg -> String,
fn <T: Display + Debug>(v: T) => format!("default value: {}", v),
default fn <T: Display + Debug>(v: T) => format!("default value: {}", v),
fn (v: u8) => format!("u8: {}", v),
fn (v: u16) => format!("u16: {}", v),
)
Expand All @@ -86,7 +86,7 @@ fn test_bound_traits_with_generic() {
specialized_dispatch!(
arg,
Arg -> String,
fn <T: Display + GenericTrait<()>>(v: T) => format!("default value: {}", v),
default fn <T: Display + GenericTrait<()>>(v: T) => format!("default value: {}", v),
fn (v: u8) => format!("u8: {}", v),
fn (v: u16) => format!("u16: {}", v),
)
Expand Down

0 comments on commit 0cfa2a6

Please sign in to comment.