Bug 58022 - [4.8 Regression] Compiler rejects abstract class in template class with friend operator<<
Summary: [4.8 Regression] Compiler rejects abstract class in template class with frien...
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 4.8.1
: P3 normal
Target Milestone: 4.8.2
Assignee: Jason Merrill
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2013-07-29 20:03 UTC by Ryan Johnson
Modified: 2013-07-30 16:59 UTC (History)
1 user (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2013-07-29 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Ryan Johnson 2013-07-29 20:03:47 UTC
First, apologies for the vague subject line, I really don't know what to call this bug...

Consider the following test case:

// <<<--- begin bug.cpp --->>>
#include <iostream>
using namespace std;
template <class T> class foo;
template <class T> ostream & operator<<(ostream& o, const foo<T>& l);
template <class T> class foo  {
    friend ostream& operator<< <T> (ostream& o, const foo<T>& l);
};
class bar;
foo<bar> fb;
class bar { virtual void baz()=0; };
// <<<--- end bug.cpp --->>>

The test case was isolated using multidelta on a large code base that compiles cleanly with gcc-4.7 and earlier.

Compiling it with gcc-4.8.1 gives the error: "cannot allocate an object of abstract type ‘bar’", and identifying this function in <ostream>:

  template<typename _CharT, typename _Traits>
    inline basic_ostream<_CharT, _Traits>&
    operator<<(basic_ostream<_CharT, _Traits>& __out, _CharT __c)
    { return __ostream_insert(__out, &__c, 1); }

Replacing "using namespace std" with "std::ostream" everywhere allows it to compile, as does moving the definition of bar above the friend declaration.

I'm not 100% certain the code is valid C++, seeing as how it instantiates a template using an incomplete type, but there are still several issues:

1. The compiler gives no hint whatsoever where the real problem is, leaving the user to infer the context in some other way; it took 2h with multidelta to isolate the above test case and finally "see" what had happened.

2. The declaration of operator<< (which accepts a const ref) should not interfere with the one in <ostream> (which accepts a value); without the const ref declaration the compiler (rightfully!) complains that "template-id ‘operator<< <bar>’ for ‘std::ostream& operator<<(std::ostream&, const foo<bar>&)’ does not match any template declaration"

3. At no point is bar actually instantiated, passed by value, or its members accessed; even if operator<< did do one of those things, operator<< is never actually called with foo<bar> as an argument, so the template shouldn't be instantiated.

For now, the workaround seems to be ensuring that bar is fully defined before any template class mentions it, but that's not going to be easy given how hard it is to find the problem (and the fact that the foo template is in a utility library and really should be included first under normal circumstances).
Comment 1 Paolo Carlini 2013-07-29 20:15:20 UTC
Please try to reduce the testcase further, no includes. You have a number of options here: http://gcc.gnu.org/wiki/A_guide_to_testcase_reduction
Comment 2 Paolo Carlini 2013-07-29 20:18:32 UTC
This compiles fine in mainline, doesn't with current 4_8-branch.
Comment 3 Ryan Johnson 2013-07-29 20:30:12 UTC
(In reply to Paolo Carlini from comment #1)
> Please try to reduce the testcase further, no includes. You have a number of
> options here: http://gcc.gnu.org/wiki/A_guide_to_testcase_reduction

Sorry, I thought <ostream> was an important part of the bug and did some work to put it back in... Here's the fully reduced case:

// <<<--- begin bug.cpp --->>>
template<typename _CharT>
class basic_ostream;

typedef basic_ostream<char> ostream;

template<typename T>
basic_ostream<T>& operator<<(basic_ostream<T>&, T);

template <class T> class foo;

template <class T> ostream & operator<<(ostream&, const foo<T>&);

template <class T> class foo  {
    friend ostream& operator<< <T> (ostream&, const foo<T>&);
};

class bar;

foo<bar> fb;

class bar { virtual void baz()=0; };
// <<<--- end bug.cpp --->>>
Comment 4 Paolo Carlini 2013-07-29 20:34:19 UTC
Thanks. Confirmed as a regression present only in 4_8-branch.
Comment 5 Jason Merrill 2013-07-30 16:59:10 UTC
Fixed for 4.8.2