I need to implement (integer) exponentiation (val
exp
) 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?
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.