Bug 66879

Summary: Error message when defining a member function inside a class definition
Product: gcc Reporter: Anders Granlund <anders.granlund.0>
Component: c++Assignee: Not yet assigned to anyone <unassigned>
Status: NEW ---    
Severity: normal CC: webrown.cpp
Priority: P3 Keywords: accepts-invalid
Version: 6.0   
Target Milestone: ---   
Host: Target:
Build: Known to work:
Known to fail: Last reconfirmed: 2015-07-15 00:00:00
Attachments: prog.cc used in the command line

Description Anders Granlund 2015-07-15 14:03:35 UTC
Created attachment 35988 [details]
prog.cc used in the command line

Compile the attached program with the following command line:

gcc prog.cc

The output is the following:

prog.cc:6:22: error: definition of ‘void A::f()’ is not in namespace enclosing ‘A’ [-fpermissive]
     class A { void f() {} };
                      ^

The expected behaviour is not to get this error message. The error message seem to suggest that the compiler thinks that the program violates [class.mfct]/2 where one sentence says:

"A member function definition that appears outside of the class definition shall appear in a namespace scope enclosing the class definition."

Observe however that the member function definition in the given program is made inside the class definition.

Note that if we remove the member function definition in the given program it compiles without errors.

Output of gcc -v:

Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.8/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 4.8.2-19ubuntu1' --with-bugurl=file:///usr/share/doc/gcc-4.8/README.Bugs --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.8 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.8 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --disable-libmudflap --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.8-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.8.2 (Ubuntu 4.8.2-19ubuntu1)
Comment 1 Jonathan Wakely 2015-07-15 15:09:07 UTC
Clang compiles it OK, but EDG says:

"a.cc", line 5: error: class "A" cannot be defined in the current scope
      class A { void f() {} };

This seems similar to the G++ error, which is due to the name A referring to ::A, which should be defined in the global scope.

I'm not sure whether the definition of Y::A should be an error, or (as Clang thinks) should declare a new type, which cannot be referred to due to the ambiguity caused by the using-declaration.
Comment 2 Jonathan Wakely 2015-07-15 15:31:18 UTC
(In reply to Anders Granlund from comment #0)
> Note that if we remove the member function definition in the given program
> it compiles without errors.

I think that's the real bug. G++ should not accept this program:

class A;

namespace Y {
    using ::A;
    class A { };
}

int main()
{
  Y::A a;
  ::A* p = &a;
}

Clang rejects it because Y::A in main() is ambiguous, but EDG rejects it with the same error as for the original testcase:

"a.cc", line 5: error: class "A" cannot be defined in the current scope
      class A { };
            ^

I believe EDG is right to reject that class-specifier because the class name A has already been introduced into that scope, and it cannot be a definition of ::A because it doesn't use a nested-name-specifier and it isn't in the namespace (or an enclosing namespace) of ::A.
Comment 3 Anders Granlund 2015-07-15 15:53:21 UTC
Thanks for the comments! Now I remember the following bug report that I sent to clang:

https://llvm.org/bugs/show_bug.cgi?id=24030

That bug has now been confirmed and fixed in clang, so yes this is the actual bug and it should be fixed in gcc also.
Comment 4 Jonathan Wakely 2015-07-15 16:06:01 UTC
So confirming as accepts-invalid with this testcase:

class A;

namespace Y {
    using ::A;
    class A { };
}
Comment 5 Anders Granlund 2015-07-27 00:38:45 UTC
*** Bug 66888 has been marked as a duplicate of this bug. ***
Comment 6 Anders Granlund 2015-07-27 00:41:56 UTC
*** Bug 66889 has been marked as a duplicate of this bug. ***
Comment 7 Anders Granlund 2015-07-27 00:50:12 UTC
(In reply to Jonathan Wakely from comment #4)
> So confirming as accepts-invalid with this testcase:
> 
> class A;
> 
> namespace Y {
>     using ::A;
>     class A { };
> }

Here are two similar test cases pulled in from my related bug reports (i have marked them as resolved duplicates).

The following ill-formed programs are also accepted by gcc:

  namespace R { struct f; }
  namespace S { using R::f; }
  struct S::f {};
  int main() {} 

  The above is ill-formed by [dcl.meaning]p1 ( http://eel.is/c++draft/dcl.meaning#1 )

  namespace P { struct S; }
  namespace R { using namespace P; }
  struct R::S {};
  int main() {}

  The above is also ill-formed by [dcl.meaning]p1

In general redeclarations must be done directly and not indirectly.
Comment 8 Anders Granlund 2015-07-27 00:57:55 UTC
*** Bug 66901 has been marked as a duplicate of this bug. ***
Comment 9 Anders Granlund 2015-07-27 01:08:27 UTC
*** Bug 66878 has been marked as a duplicate of this bug. ***
Comment 10 Anders Granlund 2015-07-27 01:09:26 UTC
Here comes two more test cases moved in from my related ICE on compile bug reports:

namespace X {
    struct S;
    namespace Y {
        namespace Z = X;
        struct Z::S {};
    }
}
int main() {}

struct S;
namespace H {
    namespace P { using ::S; }
    struct P::S {};
}
int main() {}

The above programs bot gives ICE on compile (infinite recursion in push_inner_scope_r -> stack overflow). They also involve trying to perform redeclarations indirectly.