views:

67

answers:

1

I'be seen a few questions regarding missing tail call optimization in C# supposedly making the language ill suited for recursive algorithm implementations. this, however,begs the question, how can we do tail call optimizations and still provide sensible stack traces when exceptions are raised or when reflection may be used to inspect the call stack and act upon it.

+3  A: 

Well, it only matters if you expect to get an accurate stack trace :)

Tail-call optimizations aren't the only things that can destroy a stack trace - the simplest example is inlining, which can certainly affect things. Basically, anything which relies on the stack trace being accurate is taking a bit of a risk.

Here's a very simple example of exactly that problem:

using System;
using System.Runtime.CompilerServices;

class Program
{
    static void Main(string[] args)
    {
        try
        {
            Call1();
        }
        catch (Exception e)
        {
            Console.WriteLine(e.StackTrace);
        }
    }

    static void Call1()
    {
        Call2();
    }

    static void Call2()
    {
        Call3();
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    static void Call3()
    {
        Call4();
    }

    static void Call4()
    {
        Call5();
    }

    static void Call5()
    {
        throw new Exception();
    }
}

Build and run without the debugger, and you may get this:

at Program.Call3()
at Program.Main(String[] args)

Basically, be careful what you do with stack traces.

Jon Skeet
certainly error messages I deal with on a daily basis from users has an accurate stack trace despite any compiler optimizations.otherwise I'm not sure is get much done :-)
Carlo V. Dango
@Carlo: That's probably because stack traces from users tend to be in paths which don't include inlining or tail-call optimization. Also, I strongly suspect that if you look really carefully, you'll occasionally see stack traces which *aren't* accurate in every frame - but are just accurate in the frames you care about.
Jon Skeet