Bug 17232 - [DR 1640] classes and class template specializations treated differently w.r.t. core issue #337
Summary: [DR 1640] classes and class template specializations treated differently w.r....
Status: SUSPENDED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 3.4.1
: P2 normal
Target Milestone: ---
Assignee: Jason Merrill
URL:
Keywords: accepts-invalid, rejects-valid, wrong-code
Depends on:
Blocks: 56838
  Show dependency treegraph
 
Reported: 2004-08-29 17:55 UTC by cludwig
Modified: 2015-10-10 06:16 UTC (History)
6 users (show)

See Also:
Host: i686-pc-linux-gnu
Target:
Build:
Known to work:
Known to fail: 2.95.3, 3.0.4, 3.2.3, 3.3.3, 3.4.0, 4.0.0
Last reconfirmed: 2009-04-16 16:39:36


Attachments
testcase (142 bytes, text/x-c++src)
2004-08-29 17:58 UTC, cludwig
Details
Patch to fix it (536 bytes, patch)
2004-08-30 12:39 UTC, Giovanni Bajo
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description cludwig 2004-08-29 17:55:51 UTC
cludwig@lap200:~/C++/gcc3.4/tmp> LC_ALL=C g++ -v 
Reading specs from /opt/gcc/gcc-3.4.1/lib/gcc/i686-pc-linux-gnu/3.4.1/specs 
Configured with: ../gcc-3.4.1/configure --prefix=/opt/gcc/gcc-3.4.1 
--enable-threads=posix --enable-version-specific-runtime-libs 
--enable-languages=c,c++ --enable-__cxa_atexit --enable-c-mbchar 
--enable-concept-checks --enable-libstdcxx-debug --enable-c99 
--enable-libstdcxx-pch 
Thread model: posix 
gcc version 3.4.1 
 
cludwig@lap200:~/C++/gcc3.4/tmp> cat ./is_abstract_test.cc 
template<typename T> 
class A { 
  virtual void f() = 0; 
}; 
 
class B { 
  virtual void f() = 0; 
}; 
 
template<typename T> 
int g(T (*)[1]) { 
  return 0; 
} 
 
template<typename T> 
int g(...) { 
  return 1; 
} 
 
int main() { 
  return  (g< A<int> >(0) == g< B >(0)) ; 
} 
 
cludwig@lap200:~/C++/gcc3.4/tmp> ./is_abstract_test ; echo $? 
0 
 
According to http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/cwg_defects.html#337, 
type deduction fails if it implies constructing an array of an abstract class. 
(This enables traits like boost::is_abstract<T>::value.) Therefore, above 
program is supposed to choose for both g< A<int> >() and g<B>() the second 
overload (the one that returns 1), whence the program's return value should be 
non-zero. 
 
However, g++ 3.4.1 chooses the first overload for g< A<int> >(). 
 
Regards 
 
Christoph
Comment 1 cludwig 2004-08-29 17:58:03 UTC
Created attachment 7001 [details]
testcase
Comment 2 Wolfgang Bangerth 2004-08-29 18:42:53 UTC
I don't presently have the time to figure things out, but here are 
a few data points: 
  gcc2.95, 3.2.3, and 3.3.4 return 1 
  gcc3.4.1, gcc mainline, and icc8 return 0 
 
Note also that the declaration 
  int g(T (*)[1]) 
is taking an argument of type pointer to array of T. However, as the 
types you use are abstract, there can't be any arrays of these types 
and this declaration should probably never match. 
 
That doesn't invalidate your point, though, that the compiler should 
probably treat template instances and non-template classes equivalently. 
 
W. 
Comment 3 cludwig 2004-08-29 18:57:22 UTC
Subject: Re:  classes and class template specializations treated differently w.r.t. core issue #337

On Sun, Aug 29, 2004 at 06:42:54PM -0000, bangerth at dealii dot org wrote:
> 
> I don't presently have the time to figure things out, but here are 
> a few data points: 
>   gcc2.95, 3.2.3, and 3.3.4 return 1 
>   gcc3.4.1, gcc mainline, and icc8 return 0 
>  
> Note also that the declaration 
>   int g(T (*)[1]) 
> is taking an argument of type pointer to array of T. However, as the 
> types you use are abstract, there can't be any arrays of these types 
> and this declaration should probably never match. 

I agree. 

> That doesn't invalidate your point, though, that the compiler should 
> probably treat template instances and non-template classes equivalently. 

To avoid misunderstandings: gcc 3.3.1 also returns 1. But if you
change the return statement into 

  return  (g< A<int> >(0) == 1) && (g< B >(0) == 1);

then the executable generated by gcc 3.3.1 returns 0 because
gcc 3.3.1 does not implement the resolution of core issue 337 at all.

Regards

Christoph

Comment 4 Giovanni Bajo 2004-08-30 12:30:11 UTC
Confirmed.

The problem is that A<int> was not instantiated at the point of call. So the 
compiler does not know yet if A<int> is an abstract type or not -- it will have 
to instantiate that to do so. Now, I am not sure we should force an 
instantiation just because we are trying to form an array type and we need to 
check for abstractness, I fear this requires a clarification from C++ gurus.

The simple workaround is to force an instantiation of the class to happen 
before the call. I don't know if it is possible in your application, though. 
For the testcase, you need to add a "template struct A<int>;" somewhere before 
the call.

What EDG does is to simply ignore the abstract constraint in this situation, 
and allow the call. It will then error out when the array is first used in the 
body of the function as the underlying type is abstract -- but it seems to me 
as a violation of [dcl.array]/1 which says that it is invalid to (just) declare 
an array to abstract type.

I think you are better off raising this issue with comp.std.c++, and get back 
to us. If there is agreement that EDG is wrong and the template deduction 
should be rejected through an instantiation of the argument, I can implement 
this solution into the compiler (I implemented DR 337 in the first place).

I also would like to know if this affects your application in a way that causes 
a regression (that is, 3.3 works but 3.4 does not), because your testcase is 
wisely constructed as a regression, but the actual bug is not (since DR 337 was 
first implemented in 3.4, and has this "bug" since then).
Comment 5 Giovanni Bajo 2004-08-30 12:39:11 UTC
Created attachment 7003 [details]
Patch to fix it

This is the patch I was thinking of, it forces an instantiation of the element
type of the array to satisfy the array constraints in declaration (check for
abstract type).

Not fully tested, but works for the testcase.
Comment 6 cludwig 2004-08-30 13:22:21 UTC
Subject: Re:  classes and class template specializations treated differently w.r.t. core issue #337

On Mon, Aug 30, 2004 at 12:30:28PM -0000, giovannibajo at libero dot it wrote:
> The problem is that A<int> was not instantiated at the point of call. So the 
> compiler does not know yet if A<int> is an abstract type or not -- it will have 
> to instantiate that to do so. Now, I am not sure we should force an 
> instantiation just because we are trying to form an array type and we need to 
> check for abstractness, I fear this requires a clarification from
> C++ gurus.

IMHO the class template needs to be instantiated:

  14.7.1/p4 (Implicit Instantiation)

  A class template specialization is implicitly instantiated if the
  class type is used in a context that requires a completely defined
  object type or if the completeness of the class type affects the
  semantics of the program; in particular, if an expression whose type
  is a class template specialization is involved in overload
  resolution, pointer conversion, pointer to member conversion, the
  class template specialization is implicitly instantiated (3.2);
  [...]

In my testcase, the semantics of the program depends on the
completeness of A<int> and the class template specialization is
obviously involved in overload resolution. I therefore think an
implicit specialization is warranted.

> The simple workaround is to force an instantiation of the class to happen 
> before the call. I don't know if it is possible in your application, though. 
> For the testcase, you need to add a "template struct A<int>;" somewhere before 
> the call.

Unfortunately, explicit instantiation is not a viable alternative in
my project. But I have another workaround, see below.

> What EDG does is to simply ignore the abstract constraint in this situation, 
> and allow the call. It will then error out when the array is first used in the 
> body of the function as the underlying type is abstract -- but it seems to me 
> as a violation of [dcl.array]/1 which says that it is invalid to (just) declare 
> an array to abstract type.

Do I understand you correctly, EDG based compilers will call the first
overload for A<int>? Since my testcase never actually uses the array,
there won't be any error and the function call g< A<int> >(0) will
return 0? (I don't have access to an EDG based compiler whence I
cannot easily test it myself.)
 
> I think you are better off raising this issue with comp.std.c++, and get back 
> to us. If there is agreement that EDG is wrong and the template deduction 
> should be rejected through an instantiation of the argument, I can implement 
> this solution into the compiler (I implemented DR 337 in the first place).

Sure, I will post this problem to comp.std.c++.

> I also would like to know if this affects your application in a way that causes 
> a regression (that is, 3.3 works but 3.4 does not), because your testcase is 
> wisely constructed as a regression, but the actual bug is not (since DR 337 was 
> first implemented in 3.4, and has this "bug" since then).

I don't think you can call it a regression.

I am using the new Boost serialization library that needs to handle
abstract classes differently. For gcc >= 3.4 it uses the
boost::is_abstract type trait to recognize abstract base classes and
it is therefore affected by this problem if the abstract base class is
in fact a template specialization.

For gcc <= 3.3.x it is known that DR 337 is not implemented whence
boost::is_abstract fails. The user must explicitly mark abstract
classes (essentially by an explicit specialization of
boost::is_abstract) if the serialization library is used with 
gcc <= 3.3.x.

Regards

Christoph
Comment 7 Giovanni Bajo 2004-08-30 14:45:58 UTC
(In reply to comment #6)

> IMHO the class template needs to be instantiated:
>   14.7.1/p4 (Implicit Instantiation)
>   [...]

I'm with you here, but we need language lawyers to confirm.

> Do I understand you correctly, EDG based compilers will call the first
> overload for A<int>? 

Yes.

> (I don't have access to an EDG based compiler whence I
> cannot easily test it myself.)

http://www.comeaucomputing.com/tryitout. You have to change your test into a 
compile-time only test though. For instance, remove the catch-all ellipsis 
overload and see if the compilation succeeds or fails because there is no 
viable functions (see also the testcase in my patch, which I have attacched to 
this bug).

> Sure, I will post this problem to comp.std.c++.

Thanks, get back to us when you get an answer.


> For gcc <= 3.3.x it is known that DR 337 is not implemented whence
> boost::is_abstract fails. The user must explicitly mark abstract
> classes (essentially by an explicit specialization of
> boost::is_abstract) if the serialization library is used with 
> gcc <= 3.3.x.

I see you raised this on the Boost list. Maybe it is worth disabling
is_abstract for GCC 3.4, as the problem will be fixed in 3.5.0 only.

I apologize for not catching this simple bug back when I added support for the 
DR, I usually test both template and non-template cases...
Comment 8 Wolfgang Bangerth 2004-08-30 16:05:24 UTC
So do we agree that the problem in this PR is really this: 
- for the non-template, the compiler realizes that B is abstract 
  and that therefore no array type can exist; it therefore rejects 
  the template version of g and goes with the general one 
- for the template class A, the compiler doesn't realize the  
  abstractness and therefore goes with the template version of g 
Is this indeed the reason the testcase fails? I double-checked that 
if I remove the '=0' declaration in the two classes, that the testcase 
really succeeds. 
 
W. 
Comment 9 cludwig 2004-08-30 16:56:07 UTC
Subject: Re:  classes and class template specializations treated differently w.r.t. core issue #337

On Mon, Aug 30, 2004 at 04:05:26PM -0000, bangerth at dealii dot org wrote:
> 
> So do we agree that the problem in this PR is really this: 
> - for the non-template, the compiler realizes that B is abstract 
>   and that therefore no array type can exist; it therefore rejects 
>   the template version of g and goes with the general one 
> - for the template class A, the compiler doesn't realize the  
>   abstractness and therefore goes with the template version of g 
> Is this indeed the reason the testcase fails?

Yes, that's correct.

Perhaps the testcase becomes clearer if you change main() into

  int main() { 
    return  g< A<int> >(0) + 2 * g< B >(0) ; 
  } 

I'd expect the return value 3 since both classes are abstract and (IMO)
the second overload should be chosen in both cases according to the
resolution of DR#337 and the SFINAE principle.
But gcc 3.4.1 returns 2, exhibiting that the compiler chose the
overloads g< A<int> >(A<int>(*)[1]) and g< B >(...),

> I double-checked that 
> if I remove the '=0' declaration in the two classes, that the testcase 
> really succeeds. 

That's correct behaviour. Once the classes are no longer abstract the
substitution T = B and T = A<int> into T(*)[1] yields a valid
expression and g(T(*)[1]) can take part in overload resolution. DR#337
does not apply. 

Since any match is considered better than the ellipsis, g(T(*)[1]) is
chosen both for T = B and T = A<int>.

Regards

Christoph
Comment 10 Giovanni Bajo 2004-08-30 20:10:50 UTC
Yes, Cristoph is right. Since the original testcase is a little confusing, this 
is the minimized version posted with my patch:

------------------------------------
template<typename T>
class A { 
  virtual void f() = 0;
}; 
 
template<typename T>
void g(T (*a)[1]) {}

int main() {
  g<A<int> >(0);  // { dg-error "no matching function" }
}
------------------------------------

This should be rejected by a conforming compiler, to the best of my 
understaning, because of [dcl.array]/1 and [temp.inst]/4. EDG fails to reject 
this. I am waiting for language lawyers though before running the patch through 
testing.

I'm CC'ing Jason and GDR, maybe they have a little time to provide some legal 
comments on this.
Comment 11 Wolfgang Bangerth 2004-08-30 20:56:37 UTC
In my view, 14.7.1/4 clearly indicates that the type should be instantiated. 
In particular, the sub-sentence on overload resolution seems imminent to me. 
 
The question whether the resulting (abstract) type will then cause us to 
reject the template function is a different matter and covered by DR 337. 
It is only in WP status, but it seems clear to me that it will eventually 
be adopted, and gcc seems to have it implemented already anyway, so it 
seems safe to take it as a basis. In that case, if my view of 14.7.1 is 
correct, then we erroneously miscompile the program, as is EDG. 
 
W. 
Comment 12 Jason Merrill 2004-08-30 22:12:00 UTC
Subject: Re:  classes and class template specializations
 treated differently w.r.t. core issue #337

I think that while the overloading makes the question more interesting,
there is a simpler form:

  template<typename T>
  class A { 
    virtual void f() = 0;
  }; 

  A<int>(*p)[1];

Is the compiler required to diagnose this?  If so, simply creating an array
type requires that the element type be instantiated.  I think that this is
the right answer, but I also think it's worth discussing with the
committee.

Jason
Comment 13 Giovanni Bajo 2004-08-30 23:15:31 UTC
Subject: Re:  classes and class template specializations treated differently w.r.t. core issue #337

jason at redhat dot com wrote:

>   template<typename T>
>   class A {
>     virtual void f() = 0;
>   };
>
>   A<int>(*p)[1];
>
> Is the compiler required to diagnose this?  If so, simply creating an
> array type requires that the element type be instantiated.

Yes, [dcl.array]/1 tells us that it's invalid to create an array type if the
element type is abstract, and [temp.inst]/1 tells us that there has to be an
implicit instantiation whenever the completeness of the type affects the
semantic of the program (and we know that we need an instantiation to check if
the class is really abstract or not).

>I think that this is the right answer, but I also think it's worth discussing
with the
> committee.

I think the submitter was going to raise the issue on comp.std.c++, but maybe
you can raise this directly with the committee? When we get a consensus, I can
test & submit the patch I attacched to this bug.

Giovanni Bajo


Comment 14 cludwig 2004-09-06 07:03:37 UTC
Subject: Re:  classes and class template specializations treated differently w.r.t. core issue #337

On Mon, Aug 30, 2004 at 11:15:33PM -0000, giovannibajo at libero dot it wrote:
> I think the submitter was going to raise the issue on comp.std.c++, but maybe
> you can raise this directly with the committee? When we get a consensus, I can
> test & submit the patch I attacched to this bug.

My post to comp.std.c++ drew only one reply; but in this reply Gabriel
Dos Reis confirmed that my understanding of 14.7.1 is correct and this
is indeed a bug in gcc. See
<URL:http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&selm=m3656v637d.fsf%40merlin.cs.tamu.edu>.

Regards

Christoph
Comment 15 Giovanni Bajo 2004-09-06 13:39:53 UTC
Thanks. Patch submitted for review:
http://gcc.gnu.org/ml/gcc-patches/2004-09/msg00549.html
Comment 16 Giovanni Bajo 2004-09-06 13:43:26 UTC
(In reply to comment #12)
Jason:

>   template<typename T>
>   class A { 
>     virtual void f() = 0;
>   }; 
>   A<int>(*p)[1];
>
> Is the compiler required to diagnose this?  If so, simply creating an array
> type requires that the element type be instantiated.  I think that this is
> the right answer, but I also think it's worth discussing with the
> committee.

I agree too. GCC does not diagnose this case yet, we prolly need to complete 
the type in create_array_type_for_decl. If you want, I can prepare such a 
patch, and this issue can be tracked in another PR.
Comment 17 Giovanni Bajo 2004-09-27 12:02:30 UTC
Patch needs updateing. My last fix proposed here:
http://gcc.gnu.org/ml/gcc-patches/2004-09/msg00783.html
is not complete. I need to rethink what we want GCC to do in both the tentative 
and non-tentative substitution mode.
Comment 18 Jason Merrill 2013-03-17 02:36:54 UTC
Author: jason
Date: Sun Mar 17 02:36:40 2013
New Revision: 196734

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=196734
Log:
	DR 337
	PR c++/17232
	* pt.c (tsubst) [ARRAY_TYPE]: Use abstract_virtuals_error_sfinae.
	* typeck2.c (abstract_virtuals_error_sfinae): Call complete_type.

Added:
    trunk/gcc/testsuite/g++.dg/template/abstract-dr337.C
Modified:
    trunk/gcc/cp/ChangeLog
    trunk/gcc/cp/pt.c
    trunk/gcc/cp/typeck2.c
Comment 19 Jason Merrill 2013-03-17 20:32:24 UTC
Author: jason
Date: Sun Mar 17 20:32:17 2013
New Revision: 196758

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=196758
Log:
	PR c++/17232
	PR c++/56642
	* typeck2.c (abstract_virtuals_error_sfinae): Revert complete_type
	change for now.

Modified:
    trunk/gcc/cp/ChangeLog
    trunk/gcc/cp/typeck2.c
Comment 20 Jason Merrill 2013-03-23 17:07:58 UTC
Fixed for 4.9.
Comment 21 Jason Merrill 2013-04-09 01:30:28 UTC
Instantiating a type to make sure it isn't abstract breaks boost::bind, so the committee is going to discuss this further.  I don't have an issue number yet.
Comment 22 Jakub Jelinek 2014-04-22 11:36:49 UTC
GCC 4.9.0 has been released
Comment 23 Jakub Jelinek 2014-07-16 13:30:00 UTC
GCC 4.9.1 has been released.
Comment 24 Jakub Jelinek 2014-10-30 10:40:11 UTC
GCC 4.9.2 has been released.
Comment 25 Jakub Jelinek 2015-06-26 19:57:24 UTC
GCC 4.9.3 has been released.