Generated c++ code for recursive lpdf, lpmf functions can't compile - c++ template guru help needed

I’ve been investigating https://github.com/stan-dev/stan/issues/2687, which arises when you define a recursive lpmf function the c++ compiler complains.

in branch https://github.com/stan-dev/stan/tree/bugfix/2687-compiler-err-recursive-function I’ve added a bunch of test models to demonstrate what’s happening -

ordinary recursive functions are OK, ditto ordinary _lmpf functions:


recursive _lpdf, _lmpf incur problems:



any suggestions for what needs to be done to generate c++ code that will compile with proper type templates allowing functions to be called in transformed data, transformed parameters, model, and generated quantities blocks?

Here’s a more minimal functional and non-functional example:

functions {
  real foo(real x);
  real foo(real x) {
    return x < 2 ? 1 : x * foo(x / 2);
  }
}
model {
  1 ~ normal(foo(3.2), 1);
}
functions {
  real foo_lpdf(real x);
  real foo_lpdf(real x) {
    return x < 2 ? 1 : x * foo_lpdf(x / 2);
  }
}
model {
  3.2 ~ foo();
}

I think the problem is that the nested calls to foo_lpdf aren’t passing in the propto template parameter and there’s no way to infer it. It’s not a general problem with an extra bool template parameter, because this is OK C++:

#include <iostream>

template <bool a, typename T>
T foo(const T& x);

template <bool a, typename T>
T foo(const T& x) {
  return x < 2 ? x : x * foo<a>(x / 2);
}

int main() {
  std::cout << "foo<true>(5) = " << foo<true>(5) << std::endl;
}

But it won’t work if the template parameters aren’t instantiated.

template <bool propto, typename T0__>
typename boost::math::tools::promote_args<T0__>::type
foo_lpdf(const T0__& x, std::ostream* pstream__);

template <bool propto, typename T0__>
typename boost::math::tools::promote_args<T0__>::type
foo_lpdf(const T0__& x, std::ostream* pstream__) {
    typedef typename boost::math::tools::promote_args<T0__>::type local_scalar_t__;
    typedef local_scalar_t__ fun_return_scalar_t__;
    const static bool propto__ = true;
    (void) propto__;
        local_scalar_t__ DUMMY_VAR__(std::numeric_limits<double>::quiet_NaN());
        (void) DUMMY_VAR__;  // suppress unused var warning

    int current_statement_begin__ = -1;
    try {

        current_statement_begin__ = 4;
        return stan::math::promote_scalar<fun_return_scalar_t__>((logical_lt(x, 2) ? stan::math::promote_scalar<local_scalar_t__>(1) : stan::math::promote_scalar<local_scalar_t__>((x * foo_lpdf((x / 2), pstream__))) ));
**************************PREVIOUS LINE********************************

    } catch (const std::exception& e) {
        stan::lang::rethrow_located(e, current_statement_begin__, prog_reader__());
        // Next line prevents compiler griping about no return
        throw std::runtime_error("*** IF YOU SEE THIS, PLEASE REPORT A BUG ***");
    }
}