Bug 24680 - Invalid template code accepted
Summary: Invalid template code accepted
Status: RESOLVED INVALID
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 4.1.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: accepts-invalid
Depends on:
Blocks:
 
Reported: 2005-11-05 00:44 UTC by Paul Pluzhnikov
Modified: 2005-11-07 17:39 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Paul Pluzhnikov 2005-11-05 00:44:37 UTC
$ cat junk.cc
template <typename T>
struct List
{
    struct D { int size; };
    D *d;

    List &fill(const T &t, int size = -1);
};

template <typename T>
List<T> &List<T>::fill(const T &t, int size)
{
    resize(size ? 1 : d->size);
    return *this;
}

$ /usr/local/gcc-4.1-20051001/bin/g++ -c junk.cc
# silently accepted

Replacing "resize(size...);" with "resize(1);" correctly rejects the bogus source (g++ versions 3.4 and above):

junk.cc: In member function 'List<T>& List<T>::fill(const T&, int)':
junk.cc:13: error: there are no arguments to 'resize' that depend on a template parameter, so a declaration of 'resize' must be available
junk.cc:13: error: (if you use '-fpermissive', G++ will accept your code, but allowing the use of an undeclared name is deprecated)
Comment 1 Andrew Pinski 2005-11-05 00:55:49 UTC
Hmm, GCC seems to think "size ? 1 : d->size" is type-dependent in a way it is as D is type dependent.  Someone else with more knowlege of the C++ standard will have to comment.
Another testcase which shows the same issue
template <typename T>
struct List
{
    struct D { int size; };
    D *d;

    List &fill(const T &t, int size = -1);
};

template <typename T>
List<T> &List<T>::fill(const T &t, int size)
{
    resize(d->size);
    return *this;
}


-----------------------
Comeau says this is invalid code but I don't know really.

Comment 2 Rich Newman 2005-11-05 01:13:59 UTC
(In reply to comment #1)
> Hmm, GCC seems to think "size ? 1 : d->size" is type-dependent in a way it is
> as D is type dependent. 


I disagree. D is not type depenendent at all -- it is a struct containing exactly one int field; it doesn't change in any way no matter what instantiation args are used.  Therefore, d->size is not type dependent either.
Comment 3 Paul Pluzhnikov 2005-11-05 01:17:26 UTC
Another variation of the same theme:

template <typename T>
List<T> &List<T>::fill(const T &t, int size)
{
    this->resize(1);
    this->resize(d->size);
    return *this;
}

$ /usr/local/gcc-4.1/bin/g++ -c junk.cc
# silently accepted

$ edgcpfe --gnu_version=30400 junk.cc
"junk.cc", line 13: error: class template "List<T>" has no member "resize"
      this->resize(1);
            ^

"junk.cc", line 14: error: class template "List<T>" has no member "resize"
      this->resize(d->size);
            ^

2 errors detected in the compilation of "junk.cc".

# same errors with 'edgcpfe --strict'

BTW, the test case is reduced from Qt4 sources, where 'resize()' is a typo:
reserve() was intended. IOW, g++ is hiding the bug from Qt developers.
Comment 4 Rich Newman 2005-11-05 01:21:34 UTC
See 14.6.2.1 "Dependent names" and 14.6.3 "Non Dependent name"
Comment 5 Andrew Pinski 2005-11-05 01:24:38 UTC
this better be type dependent, otherwise it is just useless really.
Comment 6 Rich Newman 2005-11-05 01:29:35 UTC
Just to be clear:

List<T> *is* type dependent.
List<T>::D is *not*.
Comment 7 Andrew Pinski 2005-11-05 01:35:28 UTC
List<T>::D is by 
14.6.2.1 pargagraph 1 point 2:
a qufified-id with a nested-name-specifier which contains a class-name that names a dependent type of whos unqualified-if names a dependent type.


So by those rules it is type dependent.  (maybe I am missing something).
Comment 8 Rich Newman 2005-11-05 01:55:22 UTC
But 14.6.2.1 says: "Inside a template, some constructs have semantics which may differ from one instantiation to another.  Such a construct depends on the template parameters."

14.6.2.2 [Example
    template <class T> struct X : B<T> {
        typename T::A* pa;
        void f(B<T>* pb) {
            static int i = B<T>::i;
            pb->j++;
        }
    };
the base class name B<T>, the type name T::A, the names B<T>::i and pb->j explicitly depend on the _template-parameter_. -- end example]

There is no explicit dependence for List<T>::D on the template parameter.  The semantics of D do not change depending upon the instantiation of T.

I believe point two refers to this kind of construct:

    List::D<T>

   "-- a _qualified-id_ with a _nested-name-specifier_ which contains a class-name that names a dependent type...."  IOW: if the nested name depends on the template arg, then the nesting entity does too.  But not the other way around.
Comment 9 Andrew Pinski 2005-11-05 01:58:12 UTC
(In reply to comment #8)
Hmm, I assume you are looking at C++03 and not C++98.  I was looking at C++98, maybe there was some defect report about this.
Comment 10 Andrew Pinski 2005-11-05 02:18:02 UTC
(In reply to comment #9)
Never mind that, the numbers have changed a little.


Take the following example:
template <typename T>
struct List
{
    struct D { int size; };
    D *d;

    List &fill(const T &t, int size = -1);
};

template<>
struct List<int>::D
{
  int d;
};

template <typename T>
List<T> &List<T>::fill(const T &t, int size)
{
    resize(d->size);
    return *this;
}



int main(void)
{
  List<int> a;
  a.fill(1, 1);
}


This is really invalid code as List<int>::D::size does not exist but it can only be diagnost at instaination time.
Comment 11 Andrew Pinski 2005-11-05 02:19:33 UTC
So it looks like we have found an EDG bug and not a GCC one.

As for
>There is no explicit dependence for List<T>::D on the template parameter.
>The semantics of D do not change depending upon the instantiation of T.

That is false see my example.
Comment 12 Andrew Pinski 2005-11-05 02:23:14 UTC
(In reply to comment #10)
> This is really invalid code as List<int>::D::size does not exist but it can
> only be diagnost at instaination time.

Oh and resize is not declared either but since d->size is dependent as shown by my example it cannot be looked up at defintion time.
Comment 13 Andrew Pinski 2005-11-05 02:33:51 UTC
Ok, here is a real testcase which shows that EDG gets it really wrong:
#include <stdio.h>


void resize(double&)
{
  printf("resize::double\n");
}
void resize(int&)
{
  printf("resize::int\n");
}



template <typename T>
struct List
{
    struct D { int size; };
    D *d;

    List &fill(const T &t, int size = -1);
};

template<>
struct List<int>::D
{
   double size;
};

template <typename T>
List<T> &List<T>::fill(const T &t, int size)
{
    resize(d->size);
    return *this;
}

int main(void)
{
  List<int> a;
  a.fill(1, 1);
  return 0;
}
-------

So you found a bug in EDG and not GCC.
The error which EDG front-end gives:

"ComeauTest.c", line 33: error: a reference of type "int &" (not const-qualified)
          cannot be initialized with a value of type "double"
      resize(d->size);
             ^
          detected during instantiation of
                    "List<T> &List<T>::fill(const T &, int) [with T=int]" 

1 error detected in the compilation of "ComeauTest.c".

Which does not make sense at all since List<T>::D is type dependent and so is d->size as shown above.
Comment 14 Andrew Pinski 2005-11-05 02:49:43 UTC
(In reply to comment #13)
> The error which EDG front-end gives:
> "ComeauTest.c", line 33: error: a reference of type "int &" (not
> const-qualified)
>           cannot be initialized with a value of type "double"
>       resize(d->size);
>              ^
>           detected during instantiation of
>                     "List<T> &List<T>::fill(const T &, int) [with T=int]" 
> 1 error detected in the compilation of "ComeauTest.c".

The other reason why this does not make sense is that EDG/Comeau is saying that the type of d->size is always int but when instantiate List<T>::fill, the front-end sees that it is not an int and then errors out.
Comment 15 Andrew Pinski 2005-11-05 03:10:50 UTC
hold one for a second there is a defect report about this:
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#224

See also:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2000/n1251.html

So EDG just does not follow the defect report.
Comment 16 Andrew Pinski 2005-11-05 03:18:03 UTC
Just for later reference the defintion of what a dependent name is slightly different now after the DR:
A name is a member of an unknown specialization if the name is a qualified-id in which the nested-name-specifier names a dependent type that is not the current instantiation.

A type is dependent if it is

a template parameter, 
a member of an unknown specialization, 
a nested class that is a member of the current instantiation, 
a cv-qualified type where the cv-unqualified type is dependent, 
a compound type constructed from any dependent type, 
an array type constructed from any dependent type or whose size is specified by a constant expression that is value-dependent, or 
a template-id in which either the template name is a template parameter or any of the template arguments is a dependent type or an expression that is type-dependent or value-dependent. 

-----
This is obvious the "a nested class that is a member of the current instantiation" case.
Comment 17 Rich Newman 2005-11-07 17:39:20 UTC
I concede the argument.