in reply to [Solved] XS debugging "failed to extend arg stack"

For the record, along the way I notice a) that metacpan does not know your module depends on ExtUtils::Depends and Test2::Suite, and b) in the module you appear to invoke Scalar::Util::weaken without loading Scalar::Util.

After confusing myself a few times, I've come to the conclusion that you've hit an unfortunate case - the EXTEND macro evaluates its arguments several times. Along the way it invokes EXTEND_HWM_SET, which looks like this for a debugging build:

# define EXTEND_HWM_SET(p, n) \ STMT_START { \ SSize_t ix = (p) - PL_stack_base + (n); \ if (ix > PL_curstackinfo->si_stack_hwm) \ PL_curstackinfo->si_stack_hwm = ix; \ } STMT_END

So given that n here is the string ix==3 ? 2*n : n, it is quite reasonable things are going to go wrong. If you use some variable other than ix, or (for example) duplicate the EXTEND call inside the if/else blocks to avoid the conditional, then the error goes away.

Update: #19818

HTH, Hugo

Replies are listed 'Best First'.
Re^2: XS debugging "failed to extend arg stack"
by ikegami (Patriarch) on Jun 03, 2022 at 16:03 UTC

    I've come to the conclusion that you've hit an unfortunate case - the EXTEND macro evaluates its arguments several times

    It sounds like you're saying that's the problem, but it took a a few reads to realize this isn't actually a problem here. The actual problem is the fact that the macro and the caller both use a var named ix.

    NERDVANA said,

    A good reminder to not use expressions inside macros.

    The relevant lesson is to not use variable names in macros that could possibly be used outside the macro.

      Yes, I probably didn't express myself as clearly as I could have done.

      Of course _any_ variable name "could possibly" be used outside a macro: macros don't come with much by way of safety features. I've been suggesting for a while that we should replace as many as we can with inline functions - which also gives type safety (as much as C has, at least), and protects against repeated evaluation of arguments - but for reasons which escape me there seems little enthusiasm for such a change.

      (I briefly considered suggesting I should write an essay "macros considered harmful", but then I read Considered harmful and thought better of it.)

        I'd love to see the C preprocessor replaced by Perl. :-). maybe perl itself could pioneer that pattern? Compile microperl, then use microperl as the preprocessor for the c code of main perl.
      And especially since I didn't decide to name the variable 'ix'. 'ix' is defined by the xs tooling in any Perl xsub which uses aliasing, and the EXTEND macro is only used in xsubs anyway. So it's a natural name collision.
Re^2: XS debugging "failed to extend arg stack"
by NERDVANA (Priest) on Jun 03, 2022 at 05:18 UTC
    Wow, thanks! A good reminder to not use expressions inside macros. And I'll get the metadata fixed too.
      I don't understand why one shouldn't use expressions inside macros. If you comply with using symbols that don't clash with those of callers, expressions should be no different than a simple variable, as far as I understand
        In general, the more complex expression you put in a macro the more likely it is to trigger some syntax mishap in the expansion of the macro. The main danger is if you use expressions with side effects, and then the macro evaluates it twice, which wasn't a factor here. But in general, macros are fragile, unlike inline functions which evaluate the expression once and then apply the rest of the inlined logic to those values. If you only ever pass bare variable names as arguments to a macro, it is least likely to give problems.
Re^2: XS debugging "failed to extend arg stack"
by NERDVANA (Priest) on Jun 03, 2022 at 16:49 UTC
    On your point about the dependencies, I have them listed in the META.json and .yml:

    "configure" : { "requires" : { "ExtUtils::Depends" : "0.405", "ExtUtils::MakeMaker" : "0" } }, "develop" : { "requires" : { "Devel::PPPort" : "3.23", "Pod::Coverage::TrustPod" : "0", "Test::More" : "0.88", "Test::Pod" : "1.41", "Test::Pod::Coverage" : "1.08" } }, "runtime" : { "requires" : { "Carp" : "0", "Exporter" : "0", "XSLoader" : "0", "strict" : "0", "warnings" : "0" } }, "test" : { "requires" : { "Scalar::Util" : "0", "Test2::Suite" : "0.000139", "Test2::V0" : "0", "Time::HiRes" : "0" } }

    Is this more Metacpan's fault for not showing dependencies from phases other than Runtime? or is this sort of fine-grain dependency listing not ready for prime-time?