Skip to content

Commit 84f0425

Browse files
committed
separate fn dispatch
1 parent 254ccfd commit 84f0425

5 files changed

Lines changed: 123 additions & 55 deletions

File tree

prebindgen-ext/src/core/prebindgen_ext.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,36 @@ pub trait PrebindgenExt {
157157
None
158158
}
159159

160+
/// Build the wrapper converter for an
161+
/// `impl Fn(args...) + Send + Sync + 'static` parameter, given the
162+
/// already-extracted arg types in declaration order. The resolver
163+
/// calls this only after [`Self::on_input_type_rank_0`] /
164+
/// [`Self::on_input_type_rank_1`] / [`Self::on_input_type_rank_2`] /
165+
/// [`Self::on_input_type_rank_3`] (for the appropriate arity) has
166+
/// returned `None`, so wrappers that need custom callback dispatch
167+
/// can intercept earlier and skip this path.
168+
///
169+
/// `args` are the rust-side argument types as they appear in the
170+
/// source signature. Note that callback args flow inverse to the
171+
/// callback parameter itself: the callback parameter is *input*,
172+
/// but its args are produced by the rust side and consumed by the
173+
/// foreign side, so they are *output* direction for converter
174+
/// resolution. The framework handles this direction-flip at
175+
/// registration time (`register_type_inner` in `core::registry`),
176+
/// so implementations of this method should look up
177+
/// already-registered *output* converters for each arg type.
178+
///
179+
/// Default: `None`. Backends that support `impl Fn` callbacks
180+
/// (e.g. [`crate::jni::JniExt`]) override this.
181+
fn dispatch_fn_input(
182+
&self,
183+
args: &[syn::Type],
184+
registry: &Registry,
185+
) -> Option<ConverterImpl> {
186+
let _ = (args, registry);
187+
None
188+
}
189+
160190
// ── Output direction (rust → wire) ─────────────────────────────
161191

162192
/// Whole-type output converter.

prebindgen-ext/src/core/registry.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -466,9 +466,6 @@ impl Registry {
466466

467467
/// Number of leaves in a type's substitutable-position tree.
468468
pub fn compute_rank(ty: &syn::Type) -> usize {
469-
if let Some(args) = extract_fn_trait_args(ty) {
470-
return args.iter().map(|t| std::cmp::max(1, compute_rank(t))).sum();
471-
}
472469
let positions = immediate_subtype_positions(ty);
473470
if positions.is_empty() {
474471
return 0;

prebindgen-ext/src/core/resolve.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,21 @@ fn try_resolve_entry<E: PrebindgenExt>(
171171
Direction::Input => ext.on_input_type_rank_0(key_ty, registry),
172172
Direction::Output => ext.on_output_type_rank_0(key_ty, registry),
173173
};
174+
// Zero-arg `impl Fn() + Send + Sync + 'static` fallback: after
175+
// the implementer's own rank-0 handler returns None, route the
176+
// empty arg-list to `dispatch_fn_input`. Non-empty Fn arities
177+
// are handled in the rank-N loop below.
178+
let res = res.or_else(|| {
179+
if dir != Direction::Input {
180+
return None;
181+
}
182+
let args = crate::core::registry::extract_fn_trait_args(key_ty)?;
183+
if args.is_empty() {
184+
ext.dispatch_fn_input(&args, registry)
185+
} else {
186+
None
187+
}
188+
});
174189
return res.map(|c| TypeEntry {
175190
destination: c.destination,
176191
function: c.function,
@@ -198,6 +213,25 @@ fn try_resolve_entry<E: PrebindgenExt>(
198213
}
199214
_ => unreachable!("rank N is bounded to 0..=3 by MAX_RANK"),
200215
};
216+
// Fallback for `impl Fn(args...) + Send + Sync + 'static`: after
217+
// the implementer's own rank-N handler returns None, route the
218+
// canonical `impl Fn(_, _, …)` pattern (every arg slot is a
219+
// wildcard) to `dispatch_fn_input`. Non-canonical patterns like
220+
// `impl Fn(Option<_>, _)` are left for user rank handlers — only
221+
// the all-wildcards shape gets the framework default.
222+
let result = result.or_else(|| {
223+
if dir != Direction::Input {
224+
return None;
225+
}
226+
let pat_args = crate::core::registry::extract_fn_trait_args(&pattern)?;
227+
if pat_args.len() != subs.len() {
228+
return None;
229+
}
230+
if !pat_args.iter().all(|t| matches!(t, syn::Type::Infer(_))) {
231+
return None;
232+
}
233+
ext.dispatch_fn_input(subs.as_slice(), registry)
234+
});
201235
// Last-step fallback for `impl Into<_> + Send + 'static`:
202236
// after the implementer's own rank-1 handler returns None,
203237
// assemble the source list (identity arm first when `target`
@@ -685,6 +719,44 @@ mod tests {
685719
assert_eq!(s[0].1, vec!["KeyExpr < 'static >"]);
686720
}
687721

722+
/// Codifies the canonical-shape detection used by the rank-N
723+
/// `dispatch_fn_input` fallback in [`try_resolve_entry`]. For
724+
/// `impl Fn(Option<Sample>)` at rank 1, `enumerate_wildcard_subs`
725+
/// emits both `impl Fn(_)` (canonical — every Fn arg slot is a
726+
/// wildcard, fallback should fire) and `impl Fn(Option<_>)`
727+
/// (non-canonical — fallback must skip so a user rank-1 handler
728+
/// can claim it). The fallback distinguishes them by checking that
729+
/// every element of `extract_fn_trait_args(&pattern)` is a
730+
/// `Type::Infer`; this test pins that invariant.
731+
#[test]
732+
fn impl_fn_canonical_pattern_is_distinguishable_from_nested() {
733+
use crate::core::registry::extract_fn_trait_args;
734+
let t = ty("impl Fn(Option<Sample>) + Send + Sync + 'static");
735+
let v = enumerate_wildcard_subs(&t, 1);
736+
// Two rank-1 variants: deepest-first (Option<_> over Sample),
737+
// then the canonical Fn(_) over Option<Sample>.
738+
assert_eq!(v.len(), 2);
739+
let mut canonical_count = 0;
740+
let mut nested_count = 0;
741+
for (pattern, _subs) in &v {
742+
let args = extract_fn_trait_args(pattern).unwrap();
743+
let all_infer = args.iter().all(|a| matches!(a, syn::Type::Infer(_)));
744+
if all_infer {
745+
canonical_count += 1;
746+
} else {
747+
nested_count += 1;
748+
}
749+
}
750+
assert_eq!(
751+
canonical_count, 1,
752+
"exactly one canonical `impl Fn(_)` pattern is expected"
753+
);
754+
assert_eq!(
755+
nested_count, 1,
756+
"exactly one nested `impl Fn(Option<_>)` pattern is expected"
757+
);
758+
}
759+
688760
#[test]
689761
fn impl_into_recognized_only_with_send_static() {
690762
use crate::core::registry::extract_into_trait_arg;

prebindgen-ext/src/jni/jni_ext.rs

Lines changed: 17 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -441,19 +441,6 @@ impl PrebindgenExt for JniExt {
441441
ty: &syn::Type,
442442
registry: &Registry,
443443
) -> Option<ConverterImpl> {
444-
if let Some(args) = extract_fn_trait_args(ty) {
445-
if args.is_empty() {
446-
let arg_tys: [syn::Type; 0] = [];
447-
let outer_ty = build_fn_type(&arg_tys);
448-
let (wire, body) = callback_input(self, &arg_tys, registry)?;
449-
let niches = default_niches_for_wire(&wire);
450-
return Some(ConverterImpl {
451-
function: self.input_wrapper(&outer_ty, &wire, &body),
452-
destination: wire,
453-
niches,
454-
});
455-
}
456-
}
457444
if let Some((wire, body)) = primitive_input(ty) {
458445
let niches = default_niches_for_wire(&wire);
459446
return Some(ConverterImpl {
@@ -512,19 +499,6 @@ impl PrebindgenExt for JniExt {
512499
niches,
513500
});
514501
}
515-
if let Some(args) = extract_fn_trait_args(pat) {
516-
if args.len() == 1 {
517-
let arg_tys = std::slice::from_ref(t1);
518-
let outer_ty = build_fn_type(arg_tys);
519-
let (wire, body) = callback_input(self, arg_tys, registry)?;
520-
let niches = default_niches_for_wire(&wire);
521-
return Some(ConverterImpl {
522-
function: self.input_wrapper(&outer_ty, &wire, &body),
523-
destination: wire,
524-
niches,
525-
});
526-
}
527-
}
528502
None
529503
}
530504

@@ -537,26 +511,29 @@ impl PrebindgenExt for JniExt {
537511
self.emit_into_dispatcher(target, sources, registry)
538512
}
539513

514+
fn dispatch_fn_input(
515+
&self,
516+
args: &[syn::Type],
517+
registry: &Registry,
518+
) -> Option<ConverterImpl> {
519+
let outer_ty = build_fn_type(args);
520+
let (wire, body) = callback_input(self, args, registry)?;
521+
let niches = default_niches_for_wire(&wire);
522+
Some(ConverterImpl {
523+
function: self.input_wrapper(&outer_ty, &wire, &body),
524+
destination: wire,
525+
niches,
526+
})
527+
}
528+
540529
fn on_input_type_rank_2(
541530
&self,
542531
pat: &syn::Type,
543532
t1: &syn::Type,
544533
t2: &syn::Type,
545534
registry: &Registry,
546535
) -> Option<ConverterImpl> {
547-
if let Some(args) = extract_fn_trait_args(pat) {
548-
if args.len() == 2 {
549-
let arg_tys = [t1.clone(), t2.clone()];
550-
let outer_ty = build_fn_type(&arg_tys);
551-
let (wire, body) = callback_input(self, &arg_tys, registry)?;
552-
let niches = default_niches_for_wire(&wire);
553-
return Some(ConverterImpl {
554-
function: self.input_wrapper(&outer_ty, &wire, &body),
555-
destination: wire,
556-
niches,
557-
});
558-
}
559-
}
536+
let _ = (pat, t1, t2, registry);
560537
None
561538
}
562539

@@ -568,19 +545,7 @@ impl PrebindgenExt for JniExt {
568545
t3: &syn::Type,
569546
registry: &Registry,
570547
) -> Option<ConverterImpl> {
571-
if let Some(args) = extract_fn_trait_args(pat) {
572-
if args.len() == 3 {
573-
let arg_tys = [t1.clone(), t2.clone(), t3.clone()];
574-
let outer_ty = build_fn_type(&arg_tys);
575-
let (wire, body) = callback_input(self, &arg_tys, registry)?;
576-
let niches = default_niches_for_wire(&wire);
577-
return Some(ConverterImpl {
578-
function: self.input_wrapper(&outer_ty, &wire, &body),
579-
destination: wire,
580-
niches,
581-
});
582-
}
583-
}
548+
let _ = (pat, t1, t2, t3, registry);
584549
None
585550
}
586551

zenoh-jni/build.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,10 @@ impl PrebindgenExt for ZenohJniExt {
243243
self.base.dispatch_into_input(target, sources, registry)
244244
}
245245

246+
fn dispatch_fn_input(&self, args: &[syn::Type], registry: &Registry) -> Option<ConverterImpl> {
247+
self.base.dispatch_fn_input(args, registry)
248+
}
249+
246250
// ── Output rank-0 — zenoh-specific arms first ──
247251

248252
fn on_output_type_rank_0(&self, ty: &syn::Type, registry: &Registry) -> Option<ConverterImpl> {

0 commit comments

Comments
 (0)