Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid an unreachable code warning due to a compiler bug?

The cut down reproducible test case, for VS2015 update 2, is the following. We were attempting to build with "Warning Level 4" and we treat warnings as fatal errors to improve the chances of noticing said warnings. I am hoping someone in the community has already run into this and has found a reasonable workaround.

This may prove to be too localised if noone else has seen an equivalent issue, so I'd like to note that the underlying question is "how badly should one mangle the codebase to evade poor compiler warnings", or equivalently, "how should one report bugs to a compiler vendor that loses bug reports".

#ifdef _WIN32
#pragma warning( push )
#pragma warning( disable : 4702 ) // As suggested by comments
#endif
template <class Type>
void func (Type t)
{
    t.func ();
    t.func ();
}
#ifdef _WIN32
#pragma warning( pop )
#endif

struct NoThrow
{
    void func () {}
};

struct Throws
{
    void func () {throw 1;}
};

int main ()
{
    NoThrow nt;
    func (nt);

    Throws t;
    func (t);
}

This triggers unreachable code warnings. The templated function looks reasonable by itself, but for one particular instantiation the compiler is able to determine that the second t.func() is dead, so it warns about unreachable code.

This seems a fairly clear cut quality of implementation issue for VS2015, so we opened a bug report here.

We received some guidance from Microsoft,

Unfortunately the backend of the compiler (where this warning originates) has no real concept of template function instantiations as instances of a single template function. They're all just functions with very similar looking names, if it ever bothered to compare the (decorated) names to eachother. Which it never does. So what it sees here is one function with some unreachable code, which it warns about, and another function with no unreachable code, which it does not. This concept of a "cross function warning" you propose, where we gather up and compare data across different template instiantiations and only warn if this or that, is extrodinarily difficult under a pure LTCG binary and impossible otherwise.

Consider a template Foo in a header foo.h. Suppose a.cpp includes it and creates Foo, with unreachable code, and then b.cpp includes it and creates Foo with no unreachable code. You propose that in a.cpp Foo should not warn, dispite the unreachable code, because Foo exists with no unreachable code. But, Foo is compiled in a different file, in a different process, in a different invocation of cl.exe, and most inconveniently in the future. Clearly the compiler has no ability to reach into the future to a yet unborn process and pull in information needed to calcuate if it should warn or not.

The only real viable option we have here is to turn off unreachable code warning for templates all together, which I'll be honest, isn't going to happen until we are sure the harm done is greater than the good being done (it's a net bad). Warnings have false positives, it happens. I'll try to think of other options, and look into the line number/file thing.

The above link may not be available, but as the internet comes with caches you can see a copy here or find your own using 2744730 and incorrect-unreachable-code-warning-in-template-instantiation.

So, if we assume the underlying template instantiation model is to stamp out copies of functions then subject them to the same warnings analysis as any other, how might we go about avoiding dead code warnings? I'm currently torn between adding tag dispatching to the dozen or so templates which are currently affected or switching off the warnings globally.

edit: The direct link seems to be live again

like image 975
Jon Chesterfield Avatar asked Jul 12 '16 14:07

Jon Chesterfield


1 Answers

I would say that this is the correct behavior, annoying as it may be. The implementer of

template <class Type>
void func (Type t)
{
    t.func ();
    t.func ();
}

could be quite happy about this warning. Also, it's not often possible for the compiler to sort out if there would be none, some, all Type that would produce invalid or valid code. Therefore, it waits until Type is specified and continues mostly as if the code was entered with that fixed type.

like image 57
Johan Lundberg Avatar answered Oct 16 '22 20:10

Johan Lundberg