debuggingdebuggervisualizernatvis

Do .natvis intrinsic functions support recursive evaluation?


I need to implement (integer) exponentiation (valexp) in a Natvis visualizer. My initial instinct to employ recursion

<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">

  <Type Name="foobar">
    <Intrinsic Name="pow" Expression="exp == 0 ? 1 : val * pow(val, exp - 1)">
      <Parameter Name="val" Type="int"/>
      <Parameter Name="exp" Type="int"/>
    </Intrinsic>
    <DisplayString>pow(2, 4) = {pow(2, 4)}</DisplayString>
  </Type>

</AutoVisualizer>

was greeted with a Natvis error:

Natvis: a.natvis (from <path>\a.pdb): Error: identifier "pow" is undefined
    Error while evaluating 'exp == 0 ? 1 : val * pow(val, exp - 1)' in the context of type 'a.exe!foobar'.

The error diagnostic is only partially helpful. It appears that you cannot reference an <Intrinsic> before it has been fully defined. If that is true then recursion is obviously not supported.

Is the diagnostic correct and recursion is not supported for that reason?


Solution

  • Is the diagnostic correct [...]?

    Sort of, but with a twist.

    The <Intrinsic> is indeed not fully defined. Specifically, it lacks a ReturnType. When missing, the ReturnType is inferred from the Expression. The Expression here, however, delegates back to the same <Intrinsic>, meaning that it (potentially) requires infinite space/time to evaluate.

    I'm guessing that the Expression Evaluator acknowledges defeat and gives up, producing a somewhat accurate, even if unhelpful error diagnostic.

    [Is] recursion [...] not supported for that reason?

    The inability to evaluate a type in finite space/time is certainly a good reason to reject recursion. However, I believe1 that things are more fundamental than that. As an experiment, we can help the debugger by explicitly specifying a ReturnType so it won't have to figure it out.

    Replacing

        <Intrinsic Name="pow" Expression="exp == 0 ? 1 : val * pow(val, exp - 1)">
    

    with

        <Intrinsic Name="pow" ReturnType="int" Expression="exp == 0 ? 1 : val * pow(val, exp - 1)">
    

    produces a more succinct error diagnostic:

    Natvis: a.natvis (from <path>\a.pdb): Error: Recursion in natvis-defined intrinsic functions is not supported.
    

    In place of a formal specification1 this diagnostic must suffice: Recursion in <Intrinsic> functions is not supported.


    1 None of this is sufficiently documented. The <Intrinsic> documentation omits anything but Name and Expression, making the Natvis XSD schema definition the de-facto documentation.