Quuxplusone / LLVMBugzillaTest

0 stars 0 forks source link

[C++11] lack of backtrace for SFINAE'ed out functions a usability nightmare #13375

Open Quuxplusone opened 12 years ago

Quuxplusone commented 12 years ago
Bugzilla Link PR13309
Status NEW
Importance P enhancement
Reported by Eric Niebler (eniebler@boost.org)
Reported on 2012-07-09 14:23:11 -0700
Last modified on 2017-02-25 10:40:06 -0800
Version trunk
Hardware PC Windows NT
CC dgregor@apple.com, gonzalo.gadeschi@gmail.com, jvapen@gmail.com, llvm-bugs@lists.llvm.org, pfultz2@yahoo.com, richard-llvm@metafoo.co.uk, zeratul976@hotmail.com
Fixed by commit(s)
Attachments pr13309.diff (3414 bytes, text/plain)
sfinae-1.patch (1638 bytes, text/plain)
sfinae-2.patch (1639 bytes, text/plain)
sfinae-3.patch (2318 bytes, text/plain)
Blocks
Blocked by
See also
An extended discussion of this problem, including the suggestion from Doug
Gregor to file this bug, can be found here:
<https://groups.google.com/d/topic/boost-developers-archive/Ipn1bF24STc/discussion>

The gist of it is this:

    template<class T> auto h(T x)->decltype(x.smurf()){return x.smurf();}
    template<class T> auto g(T x)->decltype(h(x)){return h(x);}
    template<class T> auto f(T x)->decltype(g(x)){return g(x);}
    int main(){
      f(3);
    }

Clang's (complete) error is:

1>  C:\cygwin\usr\local\bin\clang++ -c -O2 -DNDEBUG -std=gnu++11 -I../../.. -
I/home/Eric/boost/org/trunk main.cpp -o main.o
1>  main.cpp:5:3: error: no matching function for call to 'f'
1>    f(3);
1>    ^
1>  main.cpp:3:24: note: candidate template ignored: substitution failure [with
T = int]: no matching function for call to 'g'
1>  template<class T> auto f(T x)->decltype(g(x)){return g(x);}
1>                         ^                ~
1>  1 error generated.

You will note that no mention is made of *why* g can't be called, or that h
can't be called because 'int' doesn't have a member function named 'smurf'.

In contrast, g++'s error is far more informative:

1> main.cpp: In function ‘int main()’:
1> main.cpp:5:6: error: no matching function for call to ‘f(int)’
1> main.cpp:5:6: note: candidate is:
1> main.cpp:3:24: note: template<class T> decltype (g(x)) f(T)
1> main.cpp:3:24: note:   template argument deduction/substitution failed:
1> main.cpp: In substitution of ‘template<class T> decltype (g(x)) f(T) [with T
= int]’:
1> main.cpp:5:6:   required from here
1> main.cpp:3:24: error: no matching function for call to ‘g(int&)’
1> main.cpp:3:24: note: candidate is:
1> main.cpp:2:24: note: template<class T> decltype (h(x)) g(T)
1> main.cpp:2:24: note:   template argument deduction/substitution failed:
1> main.cpp: In substitution of ‘template<class T> decltype (h(x)) g(T) [with T
= int]’:
1> main.cpp:3:24:   required by substitution of ‘template<class T> decltype
(g(x)) f(T) [with T = int]’
1> main.cpp:5:6:   required from here
1> main.cpp:2:24: error: no matching function for call to ‘h(int&)’
1> main.cpp:2:24: note: candidate is:
1> main.cpp:1:24: note: template<class T> decltype (x.smurf()) h(T)
1> main.cpp:1:24: note:   template argument deduction/substitution failed:
1> main.cpp: In substitution of ‘template<class T> decltype (x.smurf()) h(T)
[with T = int]’:
1> main.cpp:2:24:   required by substitution of ‘template<class T> decltype
(h(x)) g(T) [with T = int]’
1> main.cpp:3:24:   required by substitution of ‘template<class T> decltype
(g(x)) f(T) [with T = int]’
1> main.cpp:5:6:   required from here
1> main.cpp:1:24: error: request for member ‘smurf’ in ‘x’, which is of non-
class type ‘int’

Insert appropriate discussion here about the pro's and con's of providing
detailed template instantiation backtraces. Doug seems to agree that clang is
providing too little here and that there is some room for improvement. I'll
leave it to the judgement of the clang team to decide how to best provide for
their users.

I will suggest, however, that this case is considered together with the more
general case of template instantiation failure. One approach that makes sense
to me -- and would meet my need and the needs of others on the boost list --
would be to show at least the start and end of the instantiation backtrace,
snipping the middle, and giving the user a command-line option for displaying
the full backtrace. The error message might even mention the command-line
option.
Quuxplusone commented 12 years ago

Attached pr13309.diff (3414 bytes, text/plain): Crude hack to diagnose certain substitution failures in more detail

Quuxplusone commented 12 years ago
We could produce the full errors in the case where there's only one candidate
and it's not viable due to a SFINAE condition. One quick hack later (attached),
that looks like this:

<stdin>:1:46: error: member reference base type 'int' is not a structure or
union
    template<class T> auto h(T x)->decltype(x.smurf()){return x.smurf();}
                                            ~^~~~~~
<stdin>:1:28: note: while substituting deduced template arguments into function
template 'h' [with T = int]
    template<class T> auto h(T x)->decltype(x.smurf()){return x.smurf();}
                           ^
<stdin>:2:28: note: while substituting deduced template arguments into function
template 'g' [with T = int]
    template<class T> auto g(T x)->decltype(h(x)){return h(x);}
                           ^
<stdin>:3:28: note: while substituting deduced template arguments into function
template 'f' [with T = int]
    template<class T> auto f(T x)->decltype(g(x)){return g(x);}
                           ^

(OK, OK, we're missing a note saying where the call is! I did say quick hack!
It might possibly also be exponential-time in the depth of the chain of calls!)

This also makes the diagnostic marginally better when there are multiple
overloads. If I add:

    template<class T> auto f(const T x)->decltype(g(x)){return g(x);}

... then I get:

<stdin>:5:12: error: no matching function for call to 'f'
void X() { f(0); }
           ^
<stdin>:3:28: note: candidate template ignored: substitution failure [with T =
int]: member reference base type 'int' is not a structure or union
    template<class T> auto f(T x)->decltype(g(x)){return g(x);}
                           ^
<stdin>:4:28: note: candidate template ignored: substitution failure [with T =
int]: member reference base type 'int' is not a structure or union
    template<class T> auto f(const T x)->decltype(g(x)){return g(x);}
                           ^

This seems to be a step in the right direction to me, but I think we're going
to need to be very careful in determining how much detail to provide in cases
where there are multiple candidates, in order to avoid drowning the user in
irrelevant details like g++'s diagnostic here does.
Quuxplusone commented 12 years ago

Which details in gcc's error output do you consider irrelevant? It looks ok to me.

Quuxplusone commented 12 years ago

It is verbose and redundant. The proximal cause of the error is not described until line 23. I certainly don't think we want Clang's diagnostics to head in that direction.

Quuxplusone commented 11 years ago

Im now debugging a case in which I get a substitution failure. Clang only mentions the arguments that were deduced, and the name of the function. There is no mention about what exactly could not be substituted. This looks very similar to the issue being discussed here.

Error messages that reveal the root of the error are useful, even if they are too long. A shorter error message is useless if it doesn't reveal the source of the error.

The problem with the current situation is that if the template arguments look good, the error is useless.

I can understand that if you have 10 functions that fail, the error message can get pretty long if you display everything that couldn't be substituted in every function (maybe restrict ourselves to the first thing that couldn't be substituted?). Still it would be nice to have a compiler option or a debugging pragma that allows detailed error messages for this cases.

Quuxplusone commented 9 years ago

(In reply to comment #4)

It is verbose and redundant. The proximal cause of the error is not described until line 23. I certainly don't think we want Clang's diagnostics to head in that direction.

Actually, I think a better approach is to not report substitution failure inside of decltype(unless there is no other substition failure to report). Instead it would be better to report what caused that failure. If that failure is inside of another decltype then it will continue until it finds a failure that are not inside of decltype. Finally, it will only report the final failures that occurred. There really isn't need to see a full backtrace of substitution failures, but seeing the final failures can be really useful.

So right now in Clang, we have an error message like this:

:5:12: error: no matching function for call to 'f' void X() { f(0); } ^ :3:28: note: candidate template ignored: substitution failure [with T = int]: no matching function for call to 'g' template auto f(T x)->decltype(g(x)){return g(x);} ^ We have an error with a note inside of the `decltype`. If clang instead just skipped reporting substitution failures in `decltype` and instead just reported the last failure, I would hope the error would look like this: :5:12: error: no matching function for call to 'f' void X() { f(0); } ^ :3:28: note: candidate template ignored: substitution failure [with T = int]: member reference base type 'int' is not a structure or union template auto h(T x)->decltype(x.smurf()){return x.smurf();} ~^~~~~~ Now in this case, it still reports the final error inside of a `decltype`, but there can be other cases where the final substitution failure happens inside of say an `enable_if`, so say it was written like this: template ::value>::type> auto h(T x) -> decltype(*x) { return *x; } template auto g(T x) -> decltype(h(x)) { return h(x); } template auto f(T x) -> decltype(g(x)) { return g(x); }; int main() { f(0); } Right now when we compile with clang we have this: sfinae.cpp:15:5: error: no matching function for call to 'f' f(0); ^ sfinae.cpp:11:6: note: candidate template ignored: substitution failure [with T = int]: no matching function for call to 'g' auto f(T x) -> decltype(g(x)) { return g(x); }; ^ ~ But if we skip substitution failures inside of `decltype` we could have something more like this: sfinae.cpp:16:5: error: no matching function for call to 'h' h(0); ^ sfinae.cpp:4:50: note: candidate template ignored: disabled by 'enable_if' [with T = int] template ::value>::type> ^ Which is nice and short and very informative. Plus, this allows using `decltype` as a way to transport SFINAE errors(right now there isn't a mechanism for doing this), which is useful for libraries that build abstractions on top of functions. Of course, I am not a compiler writer, so I am not sure how much difficult it would be to implement such a mechanism, but it would be immensely useful.
Quuxplusone commented 9 years ago
(In reply to comment #6)
> sfinae.cpp:16:5: error: no matching function for call to 'h'
>     h(0);
>     ^
> sfinae.cpp:4:50: note: candidate template ignored: disabled by 'enable_if'
> [with T = int]
> template <class T, class=typename
> std::enable_if<std::is_pointer<T>::value>::type>
>                                                  ^

This error message alone is not particularly useful, because it doesn't say why
we were trying to call 'h'. This also doesn't work if there are multiple
candidates at any level, because we'd produce multiple errors, often in code
that is unrelated to the intended callee. If we add a backtrace explaining how
we got to 'h', and we only do this when there's only a single candidate, then
this is a special case of my suggestion in comment#2.
Quuxplusone commented 9 years ago

sfinae.cpp:16:5: error: no matching function for call to 'h' h(0); ^ sfinae.cpp:4:50: note: candidate template ignored: disabled by 'enable_if' [with T = int] template <class T, class=typename std::enable_if<std::is_pointer::value>::type>

Sorry, this was a copy and paste error. I was suggesting the error look like this:

sfinae.cpp:15:5: error: no matching function for call to 'f' f(0); ^ sfinae.cpp:4:50: note: candidate template ignored: disabled by 'enable_if' [with T = int] template <class T, class=typename std::enable_if<std::is_pointer::value>::type> ^

This error message alone is not particularly useful

This message is very useful for two reasons:

1) It shows that to the user of the function that they are using it incorrectly, by showing an error that points to the call of the function.

2) It shows why the user cannot call the function by pointing to where substitution ultimately failed(ie because an int is not a pointer).

This also doesn't work if there are multiple candidates at any level, because we'd produce multiple errors

I dont think we should produce multiple errors. Rather clang should just produce multiple notes. So if h had another overload like this:

template <class T, class=typename std::enable_if<std::is_pointer::value>::type> auto h(T x) -> decltype(x) { return x; }

template <class T, class=typename std::enable_if<std::is_flaoting_point::value>::type> auto h(T x) -> decltype(x) { return x; }

It would produce an error with multiple notes:

sfinae.cpp:15:5: error: no matching function for call to 'f' f(0); ^ sfinae.cpp:4:50: note: candidate template ignored: disabled by 'enable_if' [with T = int] template <class T, class=typename std::enable_if<std::is_pointer::value>::type> ^

sfinae.cpp:6:50: note: candidate template ignored: disabled by 'enable_if' [with T = int] template <class T, class=typename std::enable_if<std::is_floating_point::value>::type> ^

If we add a backtrace explaining how we got to 'h'

I don't think a backtrace is as useful(perhaps in some use cases), its really an implementation detail how f ultimately called h. Whats more important to the user, is showing where(the first error to the call of the function) and why(the notes to the final substitution failures) they can't call the function.

Now, there maybe some use cases where the user may want a backtrace, and perhaps, that could be added in the future(perhaps with a compiler flag -fshow-sfinae-backtrace or something), but showing the message as I have shown above, is still more useful than what is shown now.

we only do this when there's only a single candidate

I see no reason to restrict it to a single candidate. I have many use cases which involve multiple overloads(I can show some simple use cases if interested).

Quuxplusone commented 9 years ago

(In reply to comment #8)

I don't think a backtrace is as useful(perhaps in some use cases), its really an implementation detail how f ultimately called h. Whats more important to the user, is showing where(the first error to the call of the function) and why(the notes to the final substitution failures) they can't call the function.

Usually, the user didn't actually call that function; some template provided by some other library did. In such cases (which are overwhelmingly common in codebases where users usually don't define templates themselves), it's useless to provide a diagnostic without such a backtrace.

we only do this when there's only a single candidate

I see no reason to restrict it to a single candidate. I have many use cases which involve multiple overloads(I can show some simple use cases if interested).

Including a full tree of possible overload resolution candidates (if there are multiple choices at each of several levels) can result in an extremely verbose diagnostic. This is fine, and perhaps even ideal, for users who are prepared to pick through that diagnostic and figure out exactly where it diverged from what they expected (which is, I think, the case that Eric was in, in comment#0), but our feedback from lots of users is that drowning them in information like this is useless -- long diagnostics get ignored (or at best, people pattern-match on the "shape" of the diagnostic) rather than being read.

The tricky part is to provide a good tradeoff between these extremes.

Quuxplusone commented 9 years ago
>The tricky part is to provide a good tradeoff between these extremes.

I think this is a noble goal for the default behavior but I don't think it will
ever make everyone happy. Independently of which default behavior is chosen,
opting into a full back-trace with full overload-resolution/SFINAE information
at every level should be as easy as possible. This could be achieved by
providing a way to produce the verbose output at the end of the "condensed"
error message:

 To see a verbose diagnostic of this error, execute:
   clang++ ....... // this could be very large

This way users can just copy a command into their terminals to recompile a
given TU and produce the verbose diagnostic without having to play tricks with
their build system to add an extra compiler option (and without having to
recompile everything).
Quuxplusone commented 9 years ago

Usually, the user didn't actually call that function; some template provided by some other library did.

Yes and in that case clang would provide a backtrace. That of course is not what I am referring to. I am talking about a backtrace for SFINAE(which clang currently does not provide at all). Instead clang produces an error that says the function can't be called and provides a note one level down of why it can't be called. If the SFINAE is only one level deep then that is informative, but many times when it involves decltype it is not. Everytime clang encounters a decltype substitution failure, it should go one more level deeper until it reaches an endpoint and then it should report the note(s). So instead of clang reporting a note of the "first" level down of decltype, instead it will report the "last" level of subtitution failure.

it's useless to provide a diagnostic without such a backtrace.

Yet this is what clang does currently with substitution failure in a decltype. It provides no backtrace, because as you say, it can be quite verbose(like with GCC), and the true cause of the error will be way down the backtrace. Now I am not proposing to add a full backtrace right now(a full backtrace could be added in the future) because with substitution failure, the true cause of the error will be seen at the endpoints. So, instead of clang reporting the "next level" as it currently does, it should just report the endpoints.

Futhermore, for SFINAE, many times a backtrace won't ever be needed. This is because a substitution failure is a failure to meet the type requirements of a function. So first clang produces an error for the function call(and any backtrace that led to that function call if its inside a template[I am not proposing to change this behaviour]), and then clang should produce a note of why the types could not be subtituted(or why the type failed to meet the type requriements). And the answer to the "why" will be at the endpoints, and not in the backtrace.

Of course there could be use cases for seeing the SFINAE backtrace, but first it should be optional, and second, it would be better to wait for those use cases. Eric's case is not a use case for a backtrace, his ultimate goal was just to report back just the endpoints.

Including a full tree of possible overload resolution candidates (if there are multiple choices at each of several levels) can result in an extremely verbose diagnostic.

Yes, it can. Thats why I am suggesting to only report back the leafs from the tree.

This is fine, and perhaps even ideal, for users who are prepared to pick through that diagnostic and figure out exactly where it diverged from what they expected

That is why is should be an optional flag to show the full backtrace, and it should be driven by use cases.

(which is, I think, the case that Eric was in, in comment#0)

That is not the case of Eric, he was wanting to just show the endpoints. See here for a futher discussion of what he was trying to achieve:

http://boost.2283326.n4.nabble.com/C-11-decltype-SFINAE-puzzler-td4632622.html

but our feedback from lots of users is that drowning them in information like this is useless -- long diagnostics get ignored (or at best, people pattern-match on the "shape" of the diagnostic) rather than being read.

Yes, exactly, that is why I am sug gesting to show just the endpoints(or leafs) since they are the most useful to the user rather than show the next leve down. As in the example above, clang currently shows this error:

sfinae.cpp:15:5: error: no matching function for call to 'f' f(0); ^ sfinae.cpp:11:6: note: candidate template ignored: substitution failure [with T = int]: no matching function for call to 'g' auto f(T x) -> decltype(g(x)) { return g(x); }; ^ ~

Which is just a step down in the trace. An even though it is short, the note generated provides no useful information to the user. Instead an error like this is more useful:

sfinae.cpp:15:5: error: no matching function for call to 'f' f(0); ^ sfinae.cpp:4:50: note: candidate template ignored: disabled by 'enable_if' [with T = int] template <class T, class=typename std::enable_if<std::is_pointer::value>::type> ^

sfinae.cpp:6:50: note: candidate template ignored: disabled by 'enable_if' [with T = int] template <class T, class=typename std::enable_if<std::is_floating_point::value>::type> ^

So it shows where the error occured(at the call to f), and then both of the notes provide useful information as to why the function f can't be called(eg int is not a pointer and int is not a floating point).

Quuxplusone commented 9 years ago
(In reply to comment #11)
> As in the example above, clang currently shows this error:
>
> sfinae.cpp:15:5: error: no matching function for call to 'f'
>     f(0);
>     ^
> sfinae.cpp:11:6: note: candidate template ignored: substitution failure
> [with T = int]: no matching function for call to 'g'
> auto f(T x) -> decltype(g(x)) { return g(x); };
>      ^                  ~
>
> Which is just a step down in the trace.

Yes, but it includes enough information for the user to figure out what went
wrong: they can try writing the call to 'g' themselves and see why it doesn't
work.

> Instead an error like this is more useful:
>
> sfinae.cpp:15:5: error: no matching function for call to 'f'
>     f(0);
>     ^
> sfinae.cpp:4:50: note: candidate template ignored: disabled by 'enable_if'
> [with T = int]
> template <class T, class=typename
> std::enable_if<std::is_pointer<T>::value>::type>
>                                                  ^
>
> sfinae.cpp:6:50: note: candidate template ignored: disabled by 'enable_if'
> [with T = int]
> template <class T, class=typename
> std::enable_if<std::is_floating_point<T>::value>::type>
>                                                  ^

This error seems quite useless to me. It's impossible to tell why we're even
*considering* the candidates on line 4 and 6 (they're declarations of 'h', not
of 'f'), or why we deduced T as int for each of them -- we've lost the crucial
information of how we got from here to there.

You also seem to be assuming that the point at which things went wrong is the
point at which a SFINAE check fails. That's often not the case; in many cases
we should not have reached the SFINAE check in the first place. We have been
considering a mechanism by which "obviously irrelevant" stages in the backtrace
could be elided by default (that is, in cases where template 'foo' *always*
triggers an instantiation of template 'bar', maybe indirectly, we don't need to
include the complete path between them), but that is not the case here, since
ADL is involved in each of your lookups, and it's possible that we were
supposed to (say) call a different version of 'g' in the associated namespace
of the argument.

(In reply to comment #10)
> >The tricky part is to provide a good tradeoff between these extremes.
>
> I think this is a noble goal for the default behavior but I don't think it
> will ever make everyone happy. Independently of which default behavior is
> chosen, opting into a full back-trace with full overload-resolution/SFINAE
> information at every level should be as easy as possible.

Yes, it would seem reasonable to add a flag for this behavior.
Quuxplusone commented 9 years ago

_Bug 22192 has been marked as a duplicate of this bug._

Quuxplusone commented 9 years ago

You also seem to be assuming that the point at which things went wrong is the point at which a SFINAE check fails. That's often not the case;

In most of the cases I have seen, that is the case. I have yet to see an actual case for a full SFINAE backtrace(it doesn't meant it doesn't exist, its just quite rare).

Quuxplusone commented 9 years ago

So I found a way to get close to emulating the semantics I would like. I explain it in detail here:

http://pfultz2.com/blog/2015/01/24/improving-error-messages/

However, its still not perfect.

First, it would be nice to push the substitution failure one level deeper, especially, when using decltype, like here:

template<class F, class=void> struct get_failure { template struct of { template using apply = decltype(Id()(std::declval())(std::declval()...)); }; };

This would avoid the problem of writing the type requirements twice.

Also, when using template aliases, the types don't match up. For example, clang can report back notes like this:

reveal2.cpp:215:13: note: candidate template ignored: disabled by 'enable_if' [with Ts = <foo &, int>, Id = identity] REQUIRE_OF(models<Advanceable(Iterator, int)>()); ^

It shows the types Ts and Id, but it doesn't tell me what the Iterator type is.

Quuxplusone commented 9 years ago

Ok so it seems using the above, library writers can reduce the sfinae backtrace down to two, but clang reports only one in the backtrace. I don't think there is a way to reduce the above to one. It would be nice if clang provided -fsfinae-backtrace-limit= option. The default could be -fsfinae- backtrace-limit=1, which is the current behavior, however, in the future the default could be changed to -fsfinae-backtrace-limit=2 if it proves to be a better option. Right now, there doesn't seem to be a better option at all. Plus, gcc now only reports a backtrace of one as well.

I think this issue is important, even in other areas as well such as concept checking. See this report here about the troubles of using concept checking in Eric's range library:

https://github.com/ericniebler/range-v3/issues/94

Having a sfinae backtrace would help a lot in this area. Even more so, some users would like to avoid SFINAE altogether, even where it could be applicable, because of the usability problem of SFINAE reporting, see here:

https://github.com/ericniebler/range-v3/issues/107

Also Scott Prager discusses in his blog about this "unfortunate dilemma" as well:

http://yapb-soc.blogspot.com/2015/02/sfinae-stdresultof-yeah-right.html

There should be a sfinae backtrace and libraries can work towards making it short and concise. However, a backtrace of one is not doable. A backtrace of two is doable, and a backtrace that can be configured would be the most reasonable.

Quuxplusone commented 9 years ago

Attached sfinae-1.patch (1638 bytes, text/plain): A patch to produce better error message from SFINAE

Quuxplusone commented 9 years ago

Ok, so I added a patch that would enable the behavior close to what I was describing. It can still be improved. Right now, the takeSFINAEDiagnostic will pick the second diagnostic if its available, but that may not always be the better choice. Perhaps there are some better heuristics that could be used to prioritize which SFINAE message should be used.

Quuxplusone commented 9 years ago

Attached sfinae-2.patch (1639 bytes, text/plain): A newer patch to improve SFINAE errors

Quuxplusone commented 9 years ago

Attached sfinae-3.patch (2318 bytes, text/plain): This will reduce down nested messages

Quuxplusone commented 9 years ago

_Bug 24252 has been marked as a duplicate of this bug._

Quuxplusone commented 8 years ago

Any progress on reviewing / merging this patch?

Quuxplusone commented 8 years ago

The link to the submitted patch is here:

http://reviews.llvm.org/D8309

Richard wanted a compiler flag to enable this as he was concerned users might find it difficult to diagnose the problem to know how f got to g. Although, its just as easy to trace g back to f as it is to chase f to g in the current system. You can see more of the discussion of this in the link.

I, personally, use this patch all the time, it is extremely helpful and does not have the problems that the current reporting does. In fact, I never have to chase f to g(nor g to f) because the compiler already tells me f and g. Of course, as a compiler flag more users could try it out and give feedback than just me. I would like to add the compiler flag for it, but I don't have the time currently to focus on it. Hopefully, soon, or perhaps someone else could add the flag on top of this patch.

Eventually it would be nice to support a complete backtrace, however, this requires some major refactoring, and I, of course, do not work on clang full time to do this.

Quuxplusone commented 7 years ago

I have wasted countless hours during the last 5 years debugging substitution failure errors with clang that would have been a 1 minute thing if it actually would have spitted the back trace.

Since there seem to be no consensus about what the best default should be, could we at least get a flag to opt-in to full back traces?

I would like to not have to spend the next five years in frustration sensesly crafting broken code with the sole purpose of making clang spit out the information i need to fix my bugs.

I am pretty sure others feel the same.