Bug 21008 - [4.3/4.4/4.5 Regression] [DR515] Access failure in accessing data member of base class from derived template class
Summary: [4.3/4.4/4.5 Regression] [DR515] Access failure in accessing data member of b...
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 3.4.3
: P5 normal
Target Milestone: 4.3.5
Assignee: Jason Merrill
Keywords: rejects-valid
Depends on:
Reported: 2005-04-13 21:50 UTC by Liviu Nicoara
Modified: 2021-11-05 23:18 UTC (History)
6 users (show)

See Also:
Known to work:
Known to fail: 4.0.4
Last reconfirmed: 2009-11-13 05:55:18


Note You need to log in before you can comment on or make changes to this bug.
Description Liviu Nicoara 2005-04-13 21:50:22 UTC
$ uname -srm
Linux i686

$ g++ -v
Reading specs from /opt/compilers/gcc-3.4.3/lib/gcc/i686-pc-linux-gnu/3.4.3/specs
Configured with: ../gcc-3.4.3/configure --prefix=/opt/compilers/gcc-3.4.3
--enable-shared --enable-threads --enable-languages=c,c++
Thread model: posix
gcc version 3.4.3

Copy and paste at prompt:

$ cat > t.cpp << EOF

struct A
    int foo_;

template <class T>
struct B : public A

template <class T>
struct C : protected B<T>
    int foo () {
        return A::foo_;

Compilation of the above with:

$ g++ -c t.cpp

results in:

t.cpp: In member function `int C<T>::foo()':
t.cpp:4: error: object missing in reference to `A::foo_'
t.cpp:17: error: from this location

Comment 1 Andrew Pinski 2005-04-13 22:04:14 UTC
Ok, I don't know if this is valid code (I think it is invalid as foo_ is qualified and not dependent, even 
though ICC and Comeau does not reject this):
struct A { int foo_; };
template <class T> struct B : public A {};
template<> struct B<int> {};
template <class T>
struct C : B<T>
    int foo () {
        return A::foo_;

Take the above code and add:
C<int> a;
void f(void) {a.foo();}

ICC only rejects the code at  instantiation time which seems wrong.

Someone else will have to comment to make sure that I got my analysis right.

The way to "fix" the if the code is invalid is to do B<T>::A::_foo or this->_foo.
Comment 2 Wolfgang Bangerth 2005-04-15 05:29:10 UTC
A::foo_ is not template-dependent, so it is looked up and bound at the time 
of template definition, not at instantiation time. Because template-dependent 
base classes are not visible at the time, the access is assumed to be from 
the outside, not within the class hierarchy through this->, and the error, 
though surprising, is correct. 
Comment 3 Martin Sebor 2005-04-19 15:39:04 UTC
I discussed this with Mike Miller of EDG. His response to my query on the issue
(copied with his permission) is below.

Mike Miller wrote:
> There were a couple of different examples in that thread, 
> so just to avoid confusion, here's the one I'll refer to:
>     struct A {
>       int foo_;
>     };
>     template <typename T> struct B: public A { };
>     template <typename T> struct C: B<T> {
>       int foo() {
>         return A::foo_;  // #1
>       }
>     };
> The question is how the reference on line #1 is treated. Wolfgang's 
> analysis isn't quite right.  While it's true that "A" is non-dependent 
> and thus is bound to ::A at template definition time, that is 
> irrelevant.  When C<int>::foo() (for instance) is instantiated, it turns 
> out that the reference to ::A::foo_ is, in fact, a non-static member of 
> a base class (9.3.1p3), so the reference is transformed into 
> (*this).::A::foo_ and there is no error.  This is not a violation of 
> 14.6.2p3 -- there's no lookup in a dependent base class involved, as 
> Wolfgang's comments assume, and the description "the access is assumed 
> to be from the outside, not within the class hierarchy through this->" 
> doesn't accurately describe how 9.3.1p3 works.
> In fact, though, this just sort of happens to work because A is both 
> visible in the definition context and a base class of the instantiated 
> template.  If you add an explicit specialization
>     template<> struct B<int> { };
> as suggested in Andrew's comment, so A is not a base class, or if you 
> change the program so that A is not visible in the definition context 
> (by making it a member of a namespace, for instance), we do report an 
> error in the instantiated C<int>::foo().  (There's no requirement to 
> report errors in uninstantiated templates, of course, contrary to 
> Andrew's observation.)
> This is sort of contrary to the "spirit" of two-stage lookup, though -- 
> Wolfgang's expectation is not unreasonable, I think, even though the 
> details of his reasoning are incorrect.  I'm probably going to open a 
> core issue on this, especially in light of the differences between 
> implementations.
Comment 4 Martin Sebor 2005-04-19 15:41:43 UTC
Here's a followup email from Mike with some calarifying comments:

Mike Miller wrote:
> Martin Sebor wrote:
>> Thanks a lot for the detailed analysis! I wonder if your reasoning
>> would be the same given a slightly different test case (one that's
>> closer to the original in comment #1):
>>>     struct A {
>>       protected:     // <<< ADDED <<<
>>>       int foo_;
>>>     };
>>>     template <typename T> struct B: public A { };
>>>     template <typename T> struct C: B<T> {
>>>       int foo() {
>>>         return A::foo_;  // #1
>>>       }
>>>     };
> No, I don't think that changes things.  Again, the situation is the 
> same: whether A is the global class or the injected-class-name doesn't 
> affect whether C<int> (or whatever) has access to A::foo_.  (There are 
> cases where it does matter, but this isn't one of them.  Those typically 
> involve private or protected inheritance, where the access from 
> "outside" is greater than the inherited access.)
> The general principle is that non-dependent names are looked up and 
> bound in the definition context (14.6.3), but that's really the only 
> semantic effect.  In cases like this one, it's as if "A" were replaced 
> by "::A".  If the result of using "::A" is well-formed, then the version 
> with just "A" is, too.
>> But if an implementation is permitted to diagnose access violations
>> at definition time wouldn't the gcc compilation error be justified?
> I think the general rule is that you should only issue a diagnostic if 
> you can tell that no well-formed instantiation is possible (14.6p7).  In 
> these cases, at least some specializations will have well-formed 
> instantiations, so no diagnostic is permitted.
Comment 5 Wolfgang Bangerth 2005-04-19 18:25:04 UTC
Martin & Mike, 
I'm happy to reopen this PR. I understand your analysis, and in fact thought 
about it when I wrote my comment. Independently of whether it may be strictly 
mandated by the standard, I do have to admit that I find it confusing to see 
the semantics of something change at the time of instantiation, even though 
it was already bound at template definition time. I do think that this is 
a further complication of the already not quite so intuitive two-stage 
name lookup rules. 
But I guess that's immaterial. We're not into intuitive things, but into 
the letter of the law. Some people in this country already claim that  
lawyers stray too far from the letter of the law anyway, so we won't give 
them more reason to complain. 
Incidentally, two question: 
a) your reference to 9.3.1p3 must have been to something else. In TC1,  
   9.3.1 is on const and volatile member functions. 
b) how does your interpretation affect the validity of the following program: 
struct A { 
    int foo_; 
typedef int A::* pAi; 
template <typename T> struct B: public A { }; 
template <typename T> struct C: B<T> { 
     pAi foo() { 
      return &A::foo_; 
If A::foo_ refers to the member variable *of the present object*, then  
taking its address returns an int*, not an "int A::*" object, right? However, 
I can't seem to find a compiler that would reject the code. 
Comment 6 William M. (Mike) Milller 2005-04-20 20:14:50 UTC
Responses to Wolfgang's two questions:

a) You're right; I was looking at the current WP rather than the 2003 
Standard.  Sorry.  It's 9.3.1p2 in the 2003 Standard (the change from troff to 
LaTeX resulted in some paragraphs being numbered differently, and I didn't 
notice that this was one of those cases).

b) No, unary & applied to a qualified-id that names a non-static member is 
always a pointer to member (5.3.1p2-3), regardless of whether the qualifier is 
an injected-class-name or an ordinary class-name.
Comment 7 Mark Mitchell 2005-07-06 17:03:09 UTC
Postponed until 4.0.2.
Comment 8 Andrew Pinski 2005-10-17 14:50:25 UTC
Confirmed, and .....
Comment 9 Andrew Pinski 2005-10-17 14:51:40 UTC
And removing target milestone and suspending since the DR report is still active:
Comment 10 Mark Mitchell 2006-05-25 02:35:55 UTC
Will not be fixed in 4.1.1; adjust target milestone to 4.1.2.
Comment 11 Andrew Pinski 2007-12-29 00:38:13 UTC
This was voted in WP in 2005:
Comment 12 Joseph S. Myers 2008-07-04 16:51:36 UTC
Closing 4.1 branch.
Comment 13 Joseph S. Myers 2009-03-31 18:44:07 UTC
Closing 4.2 branch.
Comment 14 Richard Biener 2009-08-04 12:26:20 UTC
GCC 4.3.4 is being released, adjusting target milestone.
Comment 15 Jason Merrill 2009-11-13 14:41:03 UTC
Subject: Bug 21008

Author: jason
Date: Fri Nov 13 14:40:22 2009
New Revision: 154150

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=154150
	PR c++/21008, DR 515
	* semantics.c (finish_non_static_data_member): Don't check
	derivation in a template.


Comment 16 Jason Merrill 2009-11-13 15:37:42 UTC
Subject: Bug 21008

Author: jason
Date: Fri Nov 13 15:37:29 2009
New Revision: 154153

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=154153
	PR c++/21008, DR 515
	* semantics.c (finish_non_static_data_member): Don't check
	derivation in a template.


Comment 17 Jason Merrill 2009-11-13 18:03:46 UTC
Subject: Bug 21008

Author: jason
Date: Fri Nov 13 18:03:31 2009
New Revision: 154158

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=154158
	PR c++/21008, DR 515
	* semantics.c (finish_non_static_data_member): Don't check
	derivation in a template.


Comment 18 Jason Merrill 2009-11-13 18:03:59 UTC
Comment 19 Tim Turner 2021-11-05 23:18:18 UTC Comment hidden (spam)