IInspectable
IInspectable

Reputation: 51506

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?

Upvotes: 1

Views: 227

Answers (1)

IInspectable
IInspectable

Reputation: 51506

Update 2025-01-31

Since submitting the initial answer a new topic on Implement NatVis custom intrinsic function for C++ was published (2025-01-10) that fully answers the question:

Intrinsic functions may be called from any expression, including NatVis expressions. Intrinsic functions may also call each other. However, intrinsic functions that use recursion are currently not supported.

There are two noteworthy points:

  • Recursion is not supported.
  • Intrinsic functions can be called from other intrinsic functions before they are defined.

Initial answer

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.

Upvotes: 1

Related Questions