risingwavelabs/risingwave · error
expect fn
Error message
expect fn
What it means
`AggregateFnOrImpl::as_fn` in src/expr/macro/src/lib.rs unwraps the enum to the `Fn` variant; any other variant (an `impl`-based aggregate) hits `panic!("expect fn")`. The macro internally assumed it was dealing with a plain function attribute but was handed an aggregate `impl` block instead.
Source
Thrown at src/expr/macro/src/lib.rs:599
encode_state: Option<UserFunctionAttr>,
#[allow(dead_code)] // TODO(wrj): support decode
decode_state: Option<UserFunctionAttr>,
}
#[derive(Debug, Clone)]
#[allow(clippy::large_enum_variant)]
enum AggregateFnOrImpl {
/// A simple accumulate/retract function.
Fn(UserFunctionAttr),
/// A full impl block.
Impl(AggregateImpl),
}
impl AggregateFnOrImpl {
fn as_fn(&self) -> &UserFunctionAttr {
match self {
AggregateFnOrImpl::Fn(attr) => attr,
_ => panic!("expect fn"),
}
}
fn accumulate(&self) -> &UserFunctionAttr {
match self {
AggregateFnOrImpl::Fn(attr) => attr,
AggregateFnOrImpl::Impl(impl_) => &impl_.accumulate,
}
}
fn has_retract(&self) -> bool {
match self {
AggregateFnOrImpl::Fn(fn_) => fn_.retract,
AggregateFnOrImpl::Impl(impl_) => impl_.retract.is_some(),
}
}
}
View on GitHub (pinned to 6469eb736d)
Solutions
- Use the impl-based code path (e.g. `accumulate()` or matching on the enum) instead of `as_fn()` when the value may be an `Impl` variant.
- Declare the aggregate as a single function rather than an impl block if the calling macro only supports fn-form aggregates.
- In macro code, replace the panic with a proper `abort!`/compile error explaining that impl-based aggregates are unsupported here.
Example fix
// before - panics for Impl variants
let attr = agg.as_fn();
// after - handle both variants
let attr = match agg {
AggregateFnOrImpl::Fn(attr) => attr,
AggregateFnOrImpl::Impl(imp) => &imp.accumulate,
}; Defensive patterns
Strategy: type-guard
Validate before calling
// In macro code, never unwrap blindly:
fn ensure_fn(v: &AggregateFnOrImpl) -> Option<&UserFunctionAttr> {
match v { AggregateFnOrImpl::Fn(a) => Some(a), _ => None }
} Type guard
fn as_fn_safe(v: &AggregateFnOrImpl) -> Option<&UserFunctionAttr> {
if let AggregateFnOrImpl::Fn(attr) = v { Some(attr) } else { None }
} Try / catch
// This panics at compile time of the downstream crate; catch it by testing macro expansion:
// trybuild-style test
// #[test] fn reject_impl_in_fn_path() { let t = trybuild::TestCases::new(); t.compile_fail("tests/ui/impl_in_fn_path.rs"); } Prevention
- Match on AggregateFnOrImpl explicitly instead of calling as_fn() in new code paths.
- Add a trybuild compile-fail test covering the mismatch case.
- Convert internal panics to proc_macro_error::abort! for better diagnostics.
When it happens
Trigger: Calling `as_fn()` on an `AggregateFnOrImpl::Impl` value — i.e. code paths in the macro that expect a function-style `#[aggregate(...)]` declaration receive an `impl ... for ...` aggregate definition, such as when a non-aggregate macro path processes an impl block.
Common situations: Macro-internal misuse when adding new aggregate features; a developer routes an `impl`-based aggregate (with `accumulate`/`retract` methods) through an API that only supports single-function aggregates.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- You can't use the macro on this type
- Expected #[serde_prefix_all(skip)]
- type inference function cannot be automatically derived. You
- multiple arguments are not supported for non-option function
- expect `impl Iterator` in return type
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/4e2ef351aa749ebb.
Report an issue: GitHub.