Patchwork [C++0x] implementing forward declarations for enums

login
register
mail settings
Submitter Rodrigo Rivas
Date Oct. 15, 2010, 9:19 a.m.
Message ID <AANLkTi=RLJwKT-1OTaBwZFyDGukt9EDra9Fc7Z5B0KWW@mail.gmail.com>
Download mbox | patch
Permalink /patch/67912/
State New
Headers show

Comments

Rodrigo Rivas - Oct. 15, 2010, 9:19 a.m.
On Wed, Oct 13, 2010 at 8:12 PM, Jason Merrill <jason@redhat.com> wrote:
>> template<typename T>  struct S
>> {
>>   enum E : int;
>> };
>> /* A */ enum S<int>::E { e }; // error: cannot add an enumerator list
>> /* B */ template<>  enum S<int>::E { e }; // error
>> /* C */ template<typename T>  enum E { a, b, c };
>> /* D */ template<typename T>  enum S<T>::E { e }; // ok.
>
> Agreed; rather, B and D should both be allowed.

Uff, template specializations are hard indeed! But I think that this
patch kind of works. Moreover, since this is unspecified territory I
feel a bit lost.
For now, I allowed for a template specialization to override only the
enumerator list.
Another testcase is added, too. Up to 9.

Regards.
Rodrigo
Jason Merrill - Oct. 24, 2010, 4:22 p.m.
On 10/15/2010 05:19 AM, Rodrigo Rivas wrote:
> Uff, template specializations are hard indeed! But I think that this
> patch kind of works. Moreover, since this is unspecified territory I
> feel a bit lost.
> For now, I allowed for a template specialization to override only the
> enumerator list.

This looks good to me, it just needs a ChangeLog entry.  There are also 
a few lines that go beyond 80 columns, but I'll fix that myself before 
checking it in.

Jason
Rodrigo Rivas - Oct. 25, 2010, 9:48 a.m.
> This looks good to me, it just needs a ChangeLog entry.
Great!

I've just written the changelog. This is by far my largest patch so it
is also my largest changelog, but I hope it is ok.

Best regards.
Rodrigo

--
gcc

2010-10-25  Rodrigo Rivas Costa <rodrigorivascosta@gmail.com>

	Implement opaque-enum-specifiesr for C++0x
	* tree.h (ENUM_IS_OPAQUE): New.
	* dwarf2out.c (gen_enumeration_type_die): Use ENUM_IS_OPAQUE.

gcc/cp

2010-10-25  Rodrigo Rivas Costa <rodrigorivascosta@gmail.com>

	Implement opaque-enum-specifiers for C++0x
	* cp-tree.h (SET_OPAQUE_ENUM_P): New.
	(OPAQUE_ENUM_P): New.
	(ENUM_FIXED_UNDERLYING_TYPE_P): New.
	(start_enum): Update prototype.
	(finish_enum_value_list): New prototype.
	* parser.c (cp_parser_elaborated_type_specifier): Issue a pedwarn if
	"enum class" is used in an elaborated-type-specifier.
	(cp_parser_enum_specifier): Rewrite to parse opaque-enum-specifiers.
	* decl.c (copy_type_enum): New.
	(finish_enum_value_list): New, with code from finish_enum.
	(finish_enum): A lot of code removed. Added a gcc_assert.
	(start_enum): Add parameters enumtype and is_new.
	Rewrite to work with opaque-enum-specifiers.
	* pt.c (maybe_process_partial_specialization): Allow for template
	specialization of enumerations, with a pedwarn.
	(lookup_template_class): Update call to start_enum. Call to
	SET_OPAQUE_ENUM_P.
	(tsubst_enum): Call to begin_scope, finish_scope and
	finish_enum_value_list.
	
gcc/testsuite

2010-10-25  Rodrigo Rivas Costa <rodrigorivascosta@gmail.com>
	
	Implement opaque-enum-specifiers for C++0x
	* g++.dg/cpp0x/forw_enum1.C: New.
	* g++.dg/cpp0x/forw_enum2.C: New.
	* g++.dg/cpp0x/forw_enum3.C: New.
	* g++.dg/cpp0x/forw_enum4.C: New.
	* g++.dg/cpp0x/forw_enum5.C: New.
	* g++.dg/cpp0x/forw_enum6.C: New.
	* g++.dg/cpp0x/forw_enum7.C: New.
	* g++.dg/cpp0x/forw_enum8.C: New.
	* g++.dg/cpp0x/forw_enum9.C: New.
	* g++.dg/parse/enum3.C: Add new errors.
	* g++.dg/cpp0x/scoped_enum.C: Avoid unwanted warning.
	* g++.dg/cpp0x/auto9.C: Add new error.
	* g++.dg/template/crash79.C: Add new errors.
Rodrigo Rivas - Oct. 28, 2010, 1:52 p.m.
Hi!
Jason, I've seen that you committed the patch, but you forgot to
update the information at
 http://gcc.gnu.org/projects/cxx0x.html
and related pages.
Or should this be commented to someone else?

Best regards.
Rodrigo

Patch

Index: gcc/tree.h
===================================================================
--- gcc/tree.h	(revision 165496)
+++ gcc/tree.h	(working copy)
@@ -1166,6 +1166,9 @@  extern void omp_clause_range_check_failed (const_t
 /* Used to mark scoped enums.  */
 #define ENUM_IS_SCOPED(NODE) (ENUMERAL_TYPE_CHECK (NODE)->base.static_flag)
 
+/* Determines whether an ENUMERAL_TYPE has defined the list of constants. */
+#define ENUM_IS_OPAQUE(NODE) (ENUMERAL_TYPE_CHECK (NODE)->base.private_flag)
+
 /* In an expr node (usually a conversion) this means the node was made
    implicitly and should not lead to any sort of warning.  In a decl node,
    warnings concerning the decl should be suppressed.  This is used at
Index: gcc/testsuite/g++.dg/parse/enum3.C
===================================================================
--- gcc/testsuite/g++.dg/parse/enum3.C	(revision 165496)
+++ gcc/testsuite/g++.dg/parse/enum3.C	(working copy)
@@ -1,5 +1,5 @@ 
 // PR c++/28261
 
-struct A {};
+struct A {}; // { dg-error "A::A" }
 
-A::A (enum { e }) {} // { dg-error "defined|token" }
+A::A (enum { e }) {} // { dg-error "defined|prototype" }
Index: gcc/testsuite/g++.dg/cpp0x/forw_enum3.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/forw_enum3.C	(revision 0)
+++ gcc/testsuite/g++.dg/cpp0x/forw_enum3.C	(revision 0)
@@ -0,0 +1,44 @@ 
+// { dg-do compile }
+// { dg-options "-std=c++0x" }
+
+namespace S1
+{
+    namespace S2
+    {
+        // opaque enum declarations
+        enum class E1;
+        enum class E2 : int;
+        enum class E3 : short;
+        enum E4 : int;
+        enum E5 : short;
+
+        // can be repeated
+        enum class E1;
+        enum class E2 : int;
+        enum class E3 : short;
+        enum E4 : int;
+        enum E5 : short;
+    }
+}
+
+// are complete so we can declare variables
+S1::S2::E1 b1;
+S1::S2::E2 b2;
+S1::S2::E3 b3;
+S1::S2::E4 b4;
+S1::S2::E5 b5;
+
+//even with elaborated-type-specifiers
+enum S1::S2::E1 a1;
+enum S1::S2::E2 a2;
+enum S1::S2::E3 a3;
+enum S1::S2::E4 a4;
+enum S1::S2::E5 a5;
+
+// and the list can be added later
+enum class S1::S2::E1 { e11, e12 };
+enum class S1::S2::E2 : int { e21, e22 };
+enum class S1::S2::E3 : short {e31, e32 };
+enum S1::S2::E4 : int { e41, e42 };
+enum S1::S2::E5 : short { e51, e52 };
+
Index: gcc/testsuite/g++.dg/cpp0x/forw_enum4.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/forw_enum4.C	(revision 0)
+++ gcc/testsuite/g++.dg/cpp0x/forw_enum4.C	(revision 0)
@@ -0,0 +1,45 @@ 
+// { dg-do compile }
+// { dg-options "-std=c++0x" }
+
+template<typename T> struct S1
+{
+    struct S2
+    {
+        // opaque enum declarations
+        enum class E1;
+        enum class E2 : T;
+        enum class E3 : short;
+        enum E4 : T;
+        enum E5 : short;
+
+        // can be repeated
+        enum class E1;
+        enum class E2 : T;
+        enum class E3 : short;
+        enum E4 : T;
+        enum E5 : short;
+    };
+
+    // are complete so we can declare variables
+    typename S2::E1 b1;
+    typename S2::E2 b2;
+    typename S2::E3 b3;
+    typename S2::E4 b4;
+    typename S2::E5 b5;
+
+    //even with elaborated-type-specifiers
+    enum S1::S2::E1 a1;
+    enum S1::S2::E2 a2;
+    enum S1::S2::E3 a3;
+    enum S1::S2::E4 a4;
+    enum S1::S2::E5 a5;
+
+    // and the list can be added later
+    enum class S1::S2::E1 { e11, e12 };
+    enum class S1::S2::E2 : T { e21, e22 };
+    enum class S1::S2::E3 : short {e31, e32 };
+    enum S1::S2::E4 : T { e41, e42 };
+    enum S1::S2::E5 : short { e51, e52 };
+};
+
+template struct S1<int>;
Index: gcc/testsuite/g++.dg/cpp0x/forw_enum5.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/forw_enum5.C	(revision 0)
+++ gcc/testsuite/g++.dg/cpp0x/forw_enum5.C	(revision 0)
@@ -0,0 +1,63 @@ 
+// { dg-do compile }
+// { dg-options "-std=c++0x" }
+
+namespace one
+{
+    struct S
+    {
+        enum { A = 1, B = 2 };
+        struct T
+        {
+            enum { B = 102 };
+
+            enum class E1;
+            enum E2 : int;
+        };
+    };
+
+    enum class S::T::E1 { A1 = A, B1 = B, C1 };
+    enum S::T::E2 : int { A1 = A, B1 = B, C1 };
+
+    static_assert(int(S::T::E1::A1) == 1, "error");
+    static_assert(int(S::T::E1::B1) == 102, "error");
+    static_assert(int(S::T::E1::C1) == 103, "error");
+
+    static_assert(int(S::T::E2::A1) == 1, "error");
+    static_assert(int(S::T::E2::B1) == 102, "error");
+    static_assert(int(S::T::E2::C1) == 103, "error");
+    static_assert(int(S::T::A1) == 1, "error");
+    static_assert(int(S::T::B1) == 102, "error");
+    static_assert(int(S::T::C1) == 103, "error");
+}
+
+
+namespace two
+{
+    namespace S
+    {
+        enum { A = 1, B = 2 };
+        namespace T
+        {
+            enum { B = 102 };
+
+            enum class E1;
+            enum E2 : int;
+        }
+    }
+
+    enum class S::T::E1 { A1 = A, B1 = B, C1 };
+    enum S::T::E2 : int { A1 = A, B1 = B, C1 };
+
+    static_assert(int(S::T::E1::A1) == 1, "error");
+    static_assert(int(S::T::E1::B1) == 102, "error");
+    static_assert(int(S::T::E1::C1) == 103, "error");
+
+    static_assert(int(S::T::E2::A1) == 1, "error");
+    static_assert(int(S::T::E2::B1) == 102, "error");
+    static_assert(int(S::T::E2::C1) == 103, "error");
+    static_assert(int(S::T::A1) == 1, "error");
+    static_assert(int(S::T::B1) == 102, "error");
+    static_assert(int(S::T::C1) == 103, "error");
+}
+
+
Index: gcc/testsuite/g++.dg/cpp0x/forw_enum6.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/forw_enum6.C	(revision 0)
+++ gcc/testsuite/g++.dg/cpp0x/forw_enum6.C	(revision 0)
@@ -0,0 +1,80 @@ 
+// { dg-do compile }
+// { dg-options "-std=c++0x" }
+
+enum class E1 : int; // { dg-error "previous definition" }
+enum E1 : int;  // { dg-error "scoped/unscoped mismatch" }
+
+enum E2 : int; // { dg-error "previous definition" }
+enum class E2 : int;  // { dg-error "scoped/unscoped mismatch" }
+
+enum struct E3 : int;
+enum class E3 : int; //ok
+
+enum class E4 : int; // { dg-error "previous definition" }
+enum class E4 : long;  // { dg-error "different underlying type" }
+
+enum E5 : int; // { dg-error "previous definition" }
+enum E5 : long;  // { dg-error "different underlying type" }
+
+enum E6 : int;
+enum E6 : int; //ok
+
+enum class E7;
+enum class E7 : int; //ok
+
+enum class E3 e3; // { dg-warning "scoped enum must not use" }
+enum struct E3 e4; // { dg-warning "scoped enum must not use" }
+enum E5 : int e5; // { dg-error "expected|invalid type" }
+
+enum E6 : int { a, b, c }; // { dg-error "previous definition" }
+enum E6 : int { a, b, c }; // { dg-error "multiple definition" }
+
+enum class E7 { }; // { dg-error "previous definition" }
+enum class E7 { a, b, c }; // { dg-error "multiple definition" }
+
+enum E8 { a8, b8 }; // { dg-error "previous definition" }
+enum E8 { a8, b8 }; // { dg-error "multiple definition" }
+
+enum E9 : int; // { dg-error "previous definition" }
+enum E9 { a9, b9 }; // { dg-error "underlying type mismatch" }
+
+namespace N1
+{
+    struct D;
+    enum class E6;
+    enum E7 : int;
+}
+
+enum class N1::E6; // { dg-error "must use a simple identifier" }
+enum N1::E6 e6_1; //ok
+enum ::N1::E6 e6_2; //ok
+
+namespace N2
+{
+    enum class N1::E6 { e1, e2, e3 }; // { dg-error "does not enclose" }
+    enum N1::E7 : int { e1, e2, e3 }; // { dg-error "does not enclose" }
+};
+
+enum class N1::E6 { e1, e2, e3 };
+enum N1::E7 : int { e1, e2, e3 };
+
+struct S1
+{
+    struct D;
+    enum class E6;
+    enum E7 : int;
+};
+
+enum class S1::E6; // { dg-error "must use a simple identifier" }
+enum S1::E6 e6_3; //ok
+enum ::S1::E6 e6_4; //ok
+
+struct S2
+{
+    enum class S1::E6 { e1, e2, e3 }; // { dg-error "does not enclose" }
+    enum S1::E7 : int { e1, e2, e3 }; // { dg-error "does not enclose" }
+};
+
+enum class S1::E6 { e1, e2, e3 };
+enum S1::E7 : int { e1, e2, e3 };
+
Index: gcc/testsuite/g++.dg/cpp0x/scoped_enum.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/scoped_enum.C	(revision 165496)
+++ gcc/testsuite/g++.dg/cpp0x/scoped_enum.C	(working copy)
@@ -24,7 +24,7 @@  enum class Color3 {
   Red
 };
 
-enum class Color color;
+enum Color color;
 enum Color3 color3;
 
 void f(int);
Index: gcc/testsuite/g++.dg/cpp0x/forw_enum7.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/forw_enum7.C	(revision 0)
+++ gcc/testsuite/g++.dg/cpp0x/forw_enum7.C	(revision 0)
@@ -0,0 +1,24 @@ 
+// { dg-do compile }
+// { dg-options "-std=c++0x" }
+
+template<typename T> struct S1
+{
+    enum E1 : int;
+    enum E1 : T;
+    enum class E2 : int;
+    enum class E2 : T;
+};
+
+template<typename T> enum S1<T>::E1 : int { e1 };
+template<typename T> enum class S1<T>::E2 : T { e2 };
+
+S1<int>::E1 x1 = S1<int>::e1;
+S1<int>::E1 x11 = S1<int>::E1::e1;
+S1<int>::E2 x2 = S1<int>::E2::e2;
+
+enum S1<int>::E1 ex1 = S1<int>::e1;
+enum S1<int>::E1 ex11 = S1<int>::E1::e1;
+enum S1<int>::E2 ex2 = S1<int>::E2::e2;
+
+
+enum S1<int>::E1 : int { h }; // { dg-error "add an enumerator list to a template instantiation" }
Index: gcc/testsuite/g++.dg/cpp0x/forw_enum8.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/forw_enum8.C	(revision 0)
+++ gcc/testsuite/g++.dg/cpp0x/forw_enum8.C	(revision 0)
@@ -0,0 +1,26 @@ 
+// { dg-do compile }
+// { dg-options "-std=c++0x" }
+
+//This instatiation is ok
+template<typename T> struct S1
+{
+    enum E : int;
+    enum E : T;
+};
+template struct S1<int>; //ok
+
+//This error is diagnosed at instantiation time
+template<typename T> struct S2
+{
+    enum E : int;   // { dg-error "previous definition" }
+    enum E : T;     // { dg-error "different underlying type" }
+};
+template struct S2<short>;
+
+//This error is diagnosed at compilation time
+template<typename T> struct S3
+{
+    enum E : int;   // { dg-error "previous definition" }
+    enum E : short; // { dg-error "different underlying type" }
+};
+
Index: gcc/testsuite/g++.dg/cpp0x/forw_enum1.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/forw_enum1.C	(revision 0)
+++ gcc/testsuite/g++.dg/cpp0x/forw_enum1.C	(revision 0)
@@ -0,0 +1,45 @@ 
+// { dg-do compile }
+// { dg-options "-std=c++0x" }
+
+// opaque enum declarations
+enum class E1;
+enum class E2 : int;
+enum class E3 : short;
+enum E4 : int;
+enum E5 : short;
+
+// can be repeated
+enum class E1;
+enum class E2 : int;
+enum class E3 : short;
+enum E4 : int;
+enum E5 : short;
+
+// are complete so we can declare variables
+E1 b1;
+E2 b2;
+E3 b3;
+E4 b4;
+E5 b5;
+
+//even with elaborated-type-specifiers
+enum E1 a1;
+enum E2 a2;
+enum E3 a3;
+enum E4 a4;
+enum E5 a5;
+
+// and the list can be added later
+enum class E1 { e11, e12 };
+enum class E2 : int { e21, e22 };
+enum class E3 : short {e31, e32 };
+enum E4 : int { e41, e42 };
+enum E5 : short { e51, e52 };
+
+// more repetitions allowed
+enum class E1;
+enum class E2 : int;
+enum class E3 : short;
+enum E4 : int;
+enum E5 : short;
+
Index: gcc/testsuite/g++.dg/cpp0x/forw_enum9.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/forw_enum9.C	(revision 0)
+++ gcc/testsuite/g++.dg/cpp0x/forw_enum9.C	(revision 0)
@@ -0,0 +1,21 @@ 
+// { dg-do compile }
+// { dg-options "-std=c++0x" }
+
+template<typename T> struct S1
+{
+    enum E1 : int;
+    enum class E2 : int;
+};
+
+template<typename T> enum S1<T>::E1 : int { e1 };
+template<typename T> enum class S1<T>::E2 : T { e2 };
+
+template<> enum S1<int>::E1 : int { i1 };
+template<> enum class S1<int>::E2 : int { i2 };
+
+S1<char>::E1 xci = S1<char>::e1;
+S1<int>::E1 xi1 = S1<int>::i1;
+
+S1<char>::E2 xc2 = S1<char>::E2::e2;
+S1<int>::E2 xi2 = S1<int>::E2::i2;
+
Index: gcc/testsuite/g++.dg/cpp0x/auto9.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/auto9.C	(revision 165496)
+++ gcc/testsuite/g++.dg/cpp0x/auto9.C	(working copy)
@@ -74,7 +74,7 @@  C<auto> c;					// { dg-error "auto|invalid" }
 C<auto *> c2;					// { dg-error "auto|invalid" }
 
 enum : auto { EE = 0 };				// { dg-error "must be an integral type" }
-enum struct D : auto * { FF = 0 };		// { dg-error "declar|expected" }
+enum struct D : auto * { FF = 0 };		// { dg-error "must be an integral type|declar|expected" }
 
 void
 bar ()
Index: gcc/testsuite/g++.dg/cpp0x/forw_enum2.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/forw_enum2.C	(revision 0)
+++ gcc/testsuite/g++.dg/cpp0x/forw_enum2.C	(revision 0)
@@ -0,0 +1,44 @@ 
+// { dg-do compile }
+// { dg-options "-std=c++0x" }
+
+struct S1
+{
+    struct S2
+    {
+        // opaque enum declarations
+        enum class E1;
+        enum class E2 : int;
+        enum class E3 : short;
+        enum E4 : int;
+        enum E5 : short;
+
+        // can be repeated
+        enum class E1;
+        enum class E2 : int;
+        enum class E3 : short;
+        enum E4 : int;
+        enum E5 : short;
+    };
+};
+
+// are complete so we can declare variables
+S1::S2::E1 b1;
+S1::S2::E2 b2;
+S1::S2::E3 b3;
+S1::S2::E4 b4;
+S1::S2::E5 b5;
+
+//even with elaborated-type-specifiers
+enum S1::S2::E1 a1;
+enum S1::S2::E2 a2;
+enum S1::S2::E3 a3;
+enum S1::S2::E4 a4;
+enum S1::S2::E5 a5;
+
+// and the list can be added later
+enum class S1::S2::E1 { e11, e12 };
+enum class S1::S2::E2 : int { e21, e22 };
+enum class S1::S2::E3 : short {e31, e32 };
+enum S1::S2::E4 : int { e41, e42 };
+enum S1::S2::E5 : short { e51, e52 };
+
Index: gcc/testsuite/g++.dg/template/crash79.C
===================================================================
--- gcc/testsuite/g++.dg/template/crash79.C	(revision 165496)
+++ gcc/testsuite/g++.dg/template/crash79.C	(working copy)
@@ -4,6 +4,6 @@  struct A
 {
   A(int);
   template<int> enum { e }; // { dg-error "template|expected" }
-}; // { dg-error "expected" }
+};
 
-A a(A::e);
+A a(A::e); // { dg-error "not a member" }
Index: gcc/cp/decl.c
===================================================================
--- gcc/cp/decl.c	(revision 165496)
+++ gcc/cp/decl.c	(working copy)
@@ -92,6 +92,7 @@  static void layout_var_decl (tree);
 static tree check_initializer (tree, tree, int, tree *);
 static void make_rtl_for_nonlocal_decl (tree, tree, const char *);
 static void save_function_data (tree);
+static void copy_type_enum (tree , tree);
 static void check_function_type (tree, tree);
 static void finish_constructor_body (void);
 static void begin_destructor_body (void);
@@ -11309,106 +11310,172 @@  xref_basetypes (tree ref, tree base_list)
 }
 
 
+/* Copies the enum-related properties from type SRC to type DST.
+   Used with the underlying type of an enum and the enum itself.  */
+static void
+copy_type_enum (tree dst, tree src)
+{
+  TYPE_MIN_VALUE (dst) = TYPE_MIN_VALUE (src);
+  TYPE_MAX_VALUE (dst) = TYPE_MAX_VALUE (src);
+  TYPE_SIZE (dst) = TYPE_SIZE (src);
+  TYPE_SIZE_UNIT (dst) = TYPE_SIZE_UNIT (src);
+  SET_TYPE_MODE (dst, TYPE_MODE (src));
+  TYPE_PRECISION (dst) = TYPE_PRECISION (src);
+  TYPE_ALIGN (dst) = TYPE_ALIGN (src);
+  TYPE_USER_ALIGN (dst) = TYPE_USER_ALIGN (src);
+  TYPE_UNSIGNED (dst) = TYPE_UNSIGNED (src);
+}
+
 /* Begin compiling the definition of an enumeration type.
    NAME is its name, 
 
+   if ENUMTYPE is not NULL_TREE then the type has alredy been found.
+
    UNDERLYING_TYPE is the type that will be used as the storage for
    the enumeration type. This should be NULL_TREE if no storage type
    was specified.
 
    SCOPED_ENUM_P is true if this is a scoped enumeration type.
 
+   if IS_NEW is not NULL, gets TRUE iff a new type is created.
+
    Returns the type object, as yet incomplete.
    Also records info about it so that build_enumerator
    may be used to declare the individual values as they are read.  */
 
 tree
-start_enum (tree name, tree underlying_type, bool scoped_enum_p)
+start_enum (tree name, tree enumtype, tree underlying_type,
+	    bool scoped_enum_p, bool *is_new)
 {
-  tree enumtype;
-
+  tree prevtype = NULL_TREE;
   gcc_assert (TREE_CODE (name) == IDENTIFIER_NODE);
 
+  if (is_new)
+    *is_new = false;
+  /* [C++0x dcl.enum]p5: 
+
+    If not explicitly specified, the underlying type of a scoped
+    enumeration type is int.  */
+  if (!underlying_type && scoped_enum_p)
+    underlying_type = integer_type_node;
+
+  if (underlying_type)
+    underlying_type = cv_unqualified (underlying_type);
+
   /* If this is the real definition for a previous forward reference,
      fill in the contents in the same object that used to be the
      forward reference.  */
+  if (!enumtype)
+    enumtype = lookup_and_check_tag (enum_type, name,
+				     /*tag_scope=*/ts_current,
+				     /*template_header_p=*/false);
 
-  enumtype = lookup_and_check_tag (enum_type, name,
-				   /*tag_scope=*/ts_current,
-				   /*template_header_p=*/false);
-
-  if (enumtype != NULL_TREE && TREE_CODE (enumtype) == ENUMERAL_TYPE)
+  /* In case of a template_decl, the only check that should be deferred
+     to instantiation time is the comparison of underlying types.  */
+  if (enumtype && TREE_CODE (enumtype) == ENUMERAL_TYPE)
     {
-      error_at (input_location, "multiple definition of %q#T", enumtype);
-      error_at (DECL_SOURCE_LOCATION (TYPE_MAIN_DECL (enumtype)),
-		"previous definition here");
-      /* Clear out TYPE_VALUES, and start again.  */
-      TYPE_VALUES (enumtype) = NULL_TREE;
+      if (scoped_enum_p != SCOPED_ENUM_P (enumtype))
+	{
+	  error_at (input_location, "scoped/unscoped mismatch "
+		    "in enum %q#T", enumtype);
+	  error_at (DECL_SOURCE_LOCATION (TYPE_MAIN_DECL (enumtype)),
+		    "previous definition here");
+	  enumtype = error_mark_node;
+	}
+      else if (ENUM_FIXED_UNDERLYING_TYPE_P (enumtype) != !! underlying_type)
+	{
+	  error_at (input_location, "underlying type mismatch "
+		    "in enum %q#T", enumtype);
+	  error_at (DECL_SOURCE_LOCATION (TYPE_MAIN_DECL (enumtype)),
+		    "previous definition here");
+	  enumtype = error_mark_node;
+	}
+      else if (underlying_type && ENUM_UNDERLYING_TYPE (enumtype) 
+	  && !dependent_type_p (underlying_type)
+	  && !dependent_type_p (ENUM_UNDERLYING_TYPE (enumtype))
+	  && !same_type_p (underlying_type, 
+			   ENUM_UNDERLYING_TYPE (enumtype)))
+	{
+	  error_at (input_location, "different underlying type "
+		    "in enum %q#T", enumtype);
+	  error_at (DECL_SOURCE_LOCATION (TYPE_MAIN_DECL (enumtype)),
+		    "previous definition here");
+	  underlying_type = NULL_TREE;
+	}
     }
-  else
+
+  if (!enumtype || TREE_CODE (enumtype) != ENUMERAL_TYPE 
+      || processing_template_decl)
     {
       /* In case of error, make a dummy enum to allow parsing to
 	 continue.  */
       if (enumtype == error_mark_node)
-	name = make_anon_name ();
+	{
+	  name = make_anon_name ();
+	  enumtype = NULL_TREE;
+	}
 
+      /* enumtype may be an ENUMERAL_TYPE if this is a redefinition
+         of an opaque enum, or an opaque enum of an already defined 
+	 enumeration (C++0x only).
+	 In any other case, it'll be NULL_TREE. */
+      if (!enumtype)
+	{
+	  if (is_new)
+	    *is_new = true;
+	}
+      prevtype = enumtype;
       enumtype = cxx_make_type (ENUMERAL_TYPE);
       enumtype = pushtag (name, enumtype, /*tag_scope=*/ts_current);
+      if (enumtype == error_mark_node)
+	return error_mark_node;
+
+      /* The enum is considered opaque until the opening '{' of the 
+	 enumerator list.  */
+      SET_OPAQUE_ENUM_P (enumtype, true);
+      ENUM_FIXED_UNDERLYING_TYPE_P (enumtype) = !! underlying_type;
     }
 
-  if (enumtype == error_mark_node)
-    return enumtype;
+  SET_SCOPED_ENUM_P (enumtype, scoped_enum_p);
 
-  if (scoped_enum_p)
-    {
-      SET_SCOPED_ENUM_P (enumtype, 1);
-      begin_scope (sk_scoped_enum, enumtype);
-
-      /* [C++0x dcl.enum]p5: 
-
-          If not explicitly specified, the underlying type of a scoped
-          enumeration type is int.  */
-      if (!underlying_type)
-        underlying_type = integer_type_node;
-    }
-
   if (underlying_type)
     {
       if (CP_INTEGRAL_TYPE_P (underlying_type))
         {
-          TYPE_MIN_VALUE (enumtype) = TYPE_MIN_VALUE (underlying_type);
-          TYPE_MAX_VALUE (enumtype) = TYPE_MAX_VALUE (underlying_type);
-          TYPE_SIZE (enumtype) = TYPE_SIZE (underlying_type);
-          TYPE_SIZE_UNIT (enumtype) = TYPE_SIZE_UNIT (underlying_type);
-          SET_TYPE_MODE (enumtype, TYPE_MODE (underlying_type));
-          TYPE_PRECISION (enumtype) = TYPE_PRECISION (underlying_type);
-          TYPE_ALIGN (enumtype) = TYPE_ALIGN (underlying_type);
-          TYPE_USER_ALIGN (enumtype) = TYPE_USER_ALIGN (underlying_type);
-          TYPE_UNSIGNED (enumtype) = TYPE_UNSIGNED (underlying_type);
+	  copy_type_enum (enumtype, underlying_type);
           ENUM_UNDERLYING_TYPE (enumtype) = underlying_type;
         }
-      else if (!dependent_type_p (underlying_type))
+      else if (dependent_type_p (underlying_type))
+	ENUM_UNDERLYING_TYPE (enumtype) = underlying_type;
+      else
         error ("underlying type %<%T%> of %<%T%> must be an integral type", 
                underlying_type, enumtype);
     }
 
-  return enumtype;
+  /* If into a template class, the returned enum is always the first 
+     declaration (opaque or not) seen. This way all the references to
+     this type will be to the same declaration. The following ones are used
+     only to check for definition errors.  */
+  if (prevtype && processing_template_decl)
+    return prevtype;
+  else
+    return enumtype;
 }
 
 /* After processing and defining all the values of an enumeration type,
-   install their decls in the enumeration type and finish it off.
-   ENUMTYPE is the type object and VALUES a list of name-value pairs.  */
+   install their decls in the enumeration type.
+   ENUMTYPE is the type object.  */
 
-void
-finish_enum (tree enumtype)
+void 
+finish_enum_value_list (tree enumtype)
 {
   tree values;
+  tree underlying_type;
   tree decl;
-  tree minnode;
-  tree maxnode;
   tree value;
+  tree minnode, maxnode;
   tree t;
-  tree underlying_type = NULL_TREE;
+
   bool fixed_underlying_type_p 
     = ENUM_UNDERLYING_TYPE (enumtype) != NULL_TREE;
 
@@ -11425,10 +11492,6 @@  tree
 	   values;
 	   values = TREE_CHAIN (values))
 	TREE_TYPE (TREE_VALUE (values)) = enumtype;
-      if (at_function_scope_p ())
-	add_stmt (build_min (TAG_DEFN, enumtype));
-      if (SCOPED_ENUM_P (enumtype))
-	finish_scope ();
       return;
     }
 
@@ -11438,34 +11501,34 @@  tree
       minnode = maxnode = NULL_TREE;
 
       for (values = TYPE_VALUES (enumtype);
-           values;
-           values = TREE_CHAIN (values))
-        {
-          decl = TREE_VALUE (values);
+	   values;
+	   values = TREE_CHAIN (values))
+	{
+	  decl = TREE_VALUE (values);
 
-          /* [dcl.enum]: Following the closing brace of an enum-specifier,
-             each enumerator has the type of its enumeration.  Prior to the
-             closing brace, the type of each enumerator is the type of its
-             initializing value.  */
-          TREE_TYPE (decl) = enumtype;
+	  /* [dcl.enum]: Following the closing brace of an enum-specifier,
+	     each enumerator has the type of its enumeration.  Prior to the
+	     closing brace, the type of each enumerator is the type of its
+	     initializing value.  */
+	  TREE_TYPE (decl) = enumtype;
 
-          /* Update the minimum and maximum values, if appropriate.  */
-          value = DECL_INITIAL (decl);
-          if (value == error_mark_node)
-            value = integer_zero_node;
-          /* Figure out what the minimum and maximum values of the
-             enumerators are.  */
-          if (!minnode)
-            minnode = maxnode = value;
-          else if (tree_int_cst_lt (maxnode, value))
-            maxnode = value;
-          else if (tree_int_cst_lt (value, minnode))
-            minnode = value;
-        }
+	  /* Update the minimum and maximum values, if appropriate.  */
+	  value = DECL_INITIAL (decl);
+	  if (value == error_mark_node)
+	    value = integer_zero_node;
+	  /* Figure out what the minimum and maximum values of the
+	     enumerators are.  */
+	  if (!minnode)
+	    minnode = maxnode = value;
+	  else if (tree_int_cst_lt (maxnode, value))
+	    maxnode = value;
+	  else if (tree_int_cst_lt (value, minnode))
+	    minnode = value;
+	}
     }
   else
     /* [dcl.enum]
-       
+
        If the enumerator-list is empty, the underlying type is as if
        the enumeration had a single enumerator with value 0.  */
     minnode = maxnode = integer_zero_node;
@@ -11530,15 +11593,7 @@  tree
          The value of sizeof() applied to an enumeration type, an object
          of an enumeration type, or an enumerator, is the value of sizeof()
          applied to the underlying type.  */
-      TYPE_MIN_VALUE (enumtype) = TYPE_MIN_VALUE (underlying_type);
-      TYPE_MAX_VALUE (enumtype) = TYPE_MAX_VALUE (underlying_type);
-      TYPE_SIZE (enumtype) = TYPE_SIZE (underlying_type);
-      TYPE_SIZE_UNIT (enumtype) = TYPE_SIZE_UNIT (underlying_type);
-      SET_TYPE_MODE (enumtype, TYPE_MODE (underlying_type));
-      TYPE_PRECISION (enumtype) = TYPE_PRECISION (underlying_type);
-      TYPE_ALIGN (enumtype) = TYPE_ALIGN (underlying_type);
-      TYPE_USER_ALIGN (enumtype) = TYPE_USER_ALIGN (underlying_type);
-      TYPE_UNSIGNED (enumtype) = TYPE_UNSIGNED (underlying_type);
+      copy_type_enum (enumtype, underlying_type);
 
       /* Compute the minimum and maximum values for the type.
 
@@ -11603,28 +11658,31 @@  tree
 
   /* Fix up all variant types of this enum type.  */
   for (t = TYPE_MAIN_VARIANT (enumtype); t; t = TYPE_NEXT_VARIANT (t))
-    {
-      TYPE_VALUES (t) = TYPE_VALUES (enumtype);
-      TYPE_MIN_VALUE (t) = TYPE_MIN_VALUE (enumtype);
-      TYPE_MAX_VALUE (t) = TYPE_MAX_VALUE (enumtype);
-      TYPE_SIZE (t) = TYPE_SIZE (enumtype);
-      TYPE_SIZE_UNIT (t) = TYPE_SIZE_UNIT (enumtype);
-      SET_TYPE_MODE (t, TYPE_MODE (enumtype));
-      TYPE_PRECISION (t) = TYPE_PRECISION (enumtype);
-      TYPE_ALIGN (t) = TYPE_ALIGN (enumtype);
-      TYPE_USER_ALIGN (t) = TYPE_USER_ALIGN (enumtype);
-      TYPE_UNSIGNED (t) = TYPE_UNSIGNED (enumtype);
-      ENUM_UNDERLYING_TYPE (t) = ENUM_UNDERLYING_TYPE (enumtype);
-    }
+    TYPE_VALUES (t) = TYPE_VALUES (enumtype);
 
-  /* Finish up the scope of a scoped enumeration.  */
-  if (SCOPED_ENUM_P (enumtype))
-    finish_scope ();
-
   /* Finish debugging output for this type.  */
   rest_of_type_compilation (enumtype, namespace_bindings_p ());
 }
 
+/* Finishes the enum type. This is called only the first time an
+   enumeration is seen, be it opaque or odinary.
+   ENUMTYPE is the type object.  */
+
+void
+finish_enum (tree enumtype)
+{
+  if (processing_template_decl)
+    {
+      if (at_function_scope_p ())
+	add_stmt (build_min (TAG_DEFN, enumtype));
+      return;
+    }
+
+  /* Here there should not be any variants of this type.  */
+  gcc_assert (enumtype == TYPE_MAIN_VARIANT (enumtype) 
+	      && !TYPE_NEXT_VARIANT (enumtype));
+}
+
 /* Build and install a CONST_DECL for an enumeration constant of the
    enumeration type ENUMTYPE whose NAME and VALUE (if any) are provided.
    LOC is the location of NAME.
Index: gcc/cp/pt.c
===================================================================
--- gcc/cp/pt.c	(revision 165496)
+++ gcc/cp/pt.c	(working copy)
@@ -907,8 +907,16 @@  maybe_process_partial_specialization (tree type)
     }
   else if (processing_specialization)
     {
-      error ("explicit specialization of non-template %qT", type);
-      return error_mark_node;
+       /* Someday C++0x may allow for enum template specialization.  */
+      if (cxx_dialect > cxx98 && TREE_CODE (type) == ENUMERAL_TYPE
+	  && CLASS_TYPE_P (context) && CLASSTYPE_USE_TEMPLATE (context))
+	pedwarn (input_location, OPT_pedantic, "template specialization "
+		 "of %qD not allowed by ISO C++", type);
+      else
+	{
+	  error ("explicit specialization of non-template %qT", type);
+	  return error_mark_node;
+	}
     }
 
   return type;
@@ -6654,10 +6662,10 @@  lookup_template_class (tree d1,
 	  if (!is_dependent_type)
 	    {
 	      set_current_access_from_decl (TYPE_NAME (template_type));
-	      t = start_enum (TYPE_IDENTIFIER (template_type),
-                              tsubst (ENUM_UNDERLYING_TYPE (template_type),
-                                      arglist, complain, in_decl),
-                              SCOPED_ENUM_P (template_type));
+	      t = start_enum (TYPE_IDENTIFIER (template_type), NULL_TREE,
+			      tsubst (ENUM_UNDERLYING_TYPE (template_type),
+				      arglist, complain, in_decl),
+			      SCOPED_ENUM_P (template_type), NULL);
 	    }
 	  else
             {
@@ -6668,6 +6676,7 @@  lookup_template_class (tree d1,
               t = cxx_make_type (ENUMERAL_TYPE);
               SET_SCOPED_ENUM_P (t, SCOPED_ENUM_P (template_type));
             }
+          SET_OPAQUE_ENUM_P (t, OPAQUE_ENUM_P (template_type) );
 	}
       else
 	{
@@ -17310,6 +17319,9 @@  static void
 tsubst_enum (tree tag, tree newtag, tree args)
 {
   tree e;
+  
+  if (SCOPED_ENUM_P (newtag))
+    begin_scope (sk_scoped_enum, newtag);
 
   for (e = TYPE_VALUES (tag); e; e = TREE_CHAIN (e))
     {
@@ -17331,7 +17343,12 @@  tsubst_enum (tree tag, tree newtag, tree args)
 	(DECL_NAME (decl), value, newtag, DECL_SOURCE_LOCATION (decl));
     }
 
+  if (SCOPED_ENUM_P (newtag))
+    finish_scope ();
+
+  finish_enum_value_list (newtag);
   finish_enum (newtag);
+
   DECL_SOURCE_LOCATION (TYPE_NAME (newtag))
     = DECL_SOURCE_LOCATION (TYPE_NAME (tag));
 }
Index: gcc/cp/parser.c
===================================================================
--- gcc/cp/parser.c	(revision 165496)
+++ gcc/cp/parser.c	(working copy)
@@ -12912,17 +12912,17 @@  cp_parser_elaborated_type_specifier (cp_parser* pa
       cp_lexer_consume_token (parser->lexer);
       /* Remember that it's an enumeration type.  */
       tag_type = enum_type;
-      /* Parse the optional `struct' or `class' key (for C++0x scoped
-         enums).  */
+      /* Issue a warning if the `struct' or `class' key (for C++0x scoped
+	 enums) is used here.  */
       if (cp_lexer_next_token_is_keyword (parser->lexer, RID_CLASS)
-          || cp_lexer_next_token_is_keyword (parser->lexer, RID_STRUCT))
-        {
-          if (cxx_dialect == cxx98)
-            maybe_warn_cpp0x (CPP0X_SCOPED_ENUMS);
-
-          /* Consume the `struct' or `class'.  */
-          cp_lexer_consume_token (parser->lexer);
-        }
+	  || cp_lexer_next_token_is_keyword (parser->lexer, RID_STRUCT))
+	{
+	    pedwarn (input_location, 0, "elaborated-type-specifier "
+		      "for a scoped enum must not use the %<%D%> keyword",
+		      cp_lexer_peek_token (parser->lexer)->u.value);
+	  /* Consume the `struct' or `class' and parse it anyway.  */
+	  cp_lexer_consume_token (parser->lexer);
+	}
       /* Parse the attributes.  */
       attributes = cp_parser_attributes_opt (parser);
     }
@@ -13212,8 +13212,12 @@  cp_parser_elaborated_type_specifier (cp_parser* pa
 /* Parse an enum-specifier.
 
    enum-specifier:
-     enum-key identifier [opt] enum-base [opt] { enumerator-list [opt] }
+     enum-head { enumerator-list [opt] }
 
+   enum-head:
+     enum-key identifier [opt] enum-base [opt]
+     enum-key nested-name-specifier identifier enum-base [opt]
+
    enum-key:
      enum
      enum class   [C++0x]
@@ -13222,6 +13226,9 @@  cp_parser_elaborated_type_specifier (cp_parser* pa
    enum-base:   [C++0x]
      : type-specifier-seq
 
+   opaque-enum-specifier:
+     enum-key identifier enum-base [opt] ;
+ 
    GNU Extensions:
      enum-key attributes[opt] identifier [opt] enum-base [opt] 
        { enumerator-list [opt] }attributes[opt]
@@ -13233,11 +13240,18 @@  static tree
 cp_parser_enum_specifier (cp_parser* parser)
 {
   tree identifier;
-  tree type;
+  tree type = NULL_TREE;
+  tree prev_scope;
+  tree nested_name_specifier = NULL_TREE;
   tree attributes;
   bool scoped_enum_p = false;
   bool has_underlying_type = false;
+  bool nested_being_defined = false;
+  bool new_value_list = false;
+  bool is_new_type = false;
+  bool is_anonymous = false;
   tree underlying_type = NULL_TREE;
+  cp_token *type_start_token = NULL;
 
   /* Parse tentatively so that we can back up if we don't find a
      enum-specifier.  */
@@ -13254,7 +13268,7 @@  cp_parser_enum_specifier (cp_parser* parser)
   if (cp_lexer_next_token_is_keyword (parser->lexer, RID_CLASS)
       || cp_lexer_next_token_is_keyword (parser->lexer, RID_STRUCT))
     {
-      if (cxx_dialect == cxx98)
+      if (cxx_dialect < cxx0x)
         maybe_warn_cpp0x (CPP0X_SCOPED_ENUMS);
 
       /* Consume the `struct' or `class' token.  */
@@ -13265,10 +13279,64 @@  cp_parser_enum_specifier (cp_parser* parser)
 
   attributes = cp_parser_attributes_opt (parser);
 
-  if (cp_lexer_next_token_is (parser->lexer, CPP_NAME))
-    identifier = cp_parser_identifier (parser);
+  /* Clear the qualification.  */
+  parser->scope = NULL_TREE;
+  parser->qualifying_scope = NULL_TREE;
+  parser->object_scope = NULL_TREE;
+
+  /* Figure out in what scope the declaration is being placed.  */
+  prev_scope = current_scope ();
+
+  type_start_token = cp_lexer_peek_token (parser->lexer);
+
+  push_deferring_access_checks (dk_no_check);
+  nested_name_specifier
+      = cp_parser_nested_name_specifier_opt (parser,
+					     /*typename_keyword_p=*/true,
+					     /*check_dependency_p=*/false,
+					     /*type_p=*/false,
+					     /*is_declaration=*/false);
+
+  if (nested_name_specifier)
+    {
+      tree name;
+
+      identifier = cp_parser_identifier (parser);
+      name =  cp_parser_lookup_name (parser, identifier,
+				     enum_type,
+				     /*is_template=*/false,
+				     /*is_namespace=*/false,
+				     /*check_dependency=*/true,
+				     /*ambiguous_decls=*/NULL,
+				     input_location);
+      if (name)
+	{
+	  type = TREE_TYPE (name);
+	  if (TREE_CODE (type) == TYPENAME_TYPE)
+	    {
+	      /* Are template enums allowed in ISO? */
+	      if (template_parm_scope_p ())
+		pedwarn (type_start_token->location, OPT_pedantic, 
+			 "%qD is an enumeration template", name);
+	      /* ignore a typename reference, for it will be solved by name
+	         in start_enum.  */
+	      type = NULL_TREE;
+	    }
+	}
+      else
+	error_at (type_start_token->location, "%qD is not an enumerator-name", identifier);
+    }
   else
-    identifier = make_anon_name ();
+    {
+      if (cp_lexer_next_token_is (parser->lexer, CPP_NAME))
+	identifier = cp_parser_identifier (parser);
+      else
+	{
+	  identifier = make_anon_name ();
+	  is_anonymous = true;
+	}
+    }
+  pop_deferring_access_checks ();
 
   /* Check for the `:' that denotes a specified underlying type in C++0x.
      Note that a ':' could also indicate a bitfield width, however.  */
@@ -13288,7 +13356,7 @@  cp_parser_enum_specifier (cp_parser* parser)
       if (!cp_parser_parse_definitely (parser))
 	return NULL_TREE;
 
-      if (cxx_dialect == cxx98)
+      if (cxx_dialect < cxx0x)
         maybe_warn_cpp0x (CPP0X_SCOPED_ENUMS);
 
       has_underlying_type = true;
@@ -13306,14 +13374,39 @@  cp_parser_enum_specifier (cp_parser* parser)
   /* Look for the `{' but don't consume it yet.  */
   if (!cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
     {
-      cp_parser_error (parser, "expected %<{%>");
-      if (has_underlying_type)
-	return NULL_TREE;
+      if (cxx_dialect < cxx0x || (!scoped_enum_p && !underlying_type))
+	{
+	  cp_parser_error (parser, "expected %<{%>");
+	  if (has_underlying_type)
+	    return NULL_TREE;
+	}
+      /* An opaque-enum-specifier must have a ';' here.  */
+      if ((scoped_enum_p || underlying_type) 
+	  && cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON))
+	{
+	  cp_parser_error (parser, "expected %<;%> or %<{%>");
+	  if (has_underlying_type)
+	    return NULL_TREE;
+	}
     }
 
   if (!has_underlying_type && !cp_parser_parse_definitely (parser))
     return NULL_TREE;
 
+  if (nested_name_specifier)
+    {
+      if (CLASS_TYPE_P (nested_name_specifier))
+	{
+	  nested_being_defined = TYPE_BEING_DEFINED (nested_name_specifier);
+	  TYPE_BEING_DEFINED (nested_name_specifier) = 1;
+	  push_scope (nested_name_specifier);
+	}
+      else if (TREE_CODE (nested_name_specifier) == NAMESPACE_DECL)
+	{
+	  push_nested_namespace (nested_name_specifier);
+	}
+    }
+
   /* Issue an error message if type-definitions are forbidden here.  */
   if (!cp_parser_check_type_definition (parser))
     type = error_mark_node;
@@ -13321,24 +13414,89 @@  cp_parser_enum_specifier (cp_parser* parser)
     /* Create the new type.  We do this before consuming the opening
        brace so the enum will be recorded as being on the line of its
        tag (or the 'enum' keyword, if there is no tag).  */
-    type = start_enum (identifier, underlying_type, scoped_enum_p);
-  
-  /* Consume the opening brace.  */
-  cp_lexer_consume_token (parser->lexer);
+    type = start_enum (identifier, type, underlying_type,
+		       scoped_enum_p, &is_new_type);
 
-  if (type == error_mark_node)
+  /* If the next token is not '{' it is an opaque-enum-specifier or an 
+     elaborated-type-specifier.  */
+  if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
     {
-      cp_parser_skip_to_end_of_block_or_statement (parser);
-      return error_mark_node;
-    }
+      if (nested_name_specifier)
+	{
+	  /* The following catches invalid code such as:
+	     enum class S<int>::E { A, B, C }; */
+	  if (!processing_specialization
+	      && CLASS_TYPE_P (nested_name_specifier)
+	      && CLASSTYPE_USE_TEMPLATE (nested_name_specifier))
+	    error_at (type_start_token->location, "cannot add an enumerator "
+		      "list to a template instantiation");
 
-  /* If the next token is not '}', then there are some enumerators.  */
-  if (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_BRACE))
-    cp_parser_enumerator_list (parser, type);
+	  /* If that scope does not contain the scope in which the
+	     class was originally declared, the program is invalid.  */
+	  if (prev_scope && !is_ancestor (prev_scope, nested_name_specifier))
+	    {
+	      if (at_namespace_scope_p ())
+		error_at (type_start_token->location,
+			  "declaration of %qD in namespace %qD which does not "
+			  "enclose %qD",
+			  type, prev_scope, nested_name_specifier);
+	      else
+		error_at (type_start_token->location,
+			  "declaration of %qD in %qD which does not enclose %qD",
+			  type, prev_scope, nested_name_specifier);
+	      type = error_mark_node;
+	    }
+	}
 
-  /* Consume the final '}'.  */
-  cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE);
+      if (scoped_enum_p)
+	begin_scope (sk_scoped_enum, type);
 
+      /* Consume the opening brace.  */
+      cp_lexer_consume_token (parser->lexer);
+
+      if (type == error_mark_node)
+	; /* Nothing to add */
+      else if (OPAQUE_ENUM_P (type) 
+	       || (cxx_dialect > cxx98 && processing_specialization))
+	{
+	  new_value_list = true;
+	  SET_OPAQUE_ENUM_P (type, false);
+	  DECL_SOURCE_LOCATION (TYPE_NAME (type)) = type_start_token->location;
+	}
+      else
+	{
+	  error_at (type_start_token->location, "multiple definition of %q#T", type);
+	  error_at (DECL_SOURCE_LOCATION (TYPE_MAIN_DECL (type)),
+		    "previous definition here");
+	  type = error_mark_node;
+	}
+
+      if (type == error_mark_node)
+	cp_parser_skip_to_end_of_block_or_statement (parser);
+      /* If the next token is not '}', then there are some enumerators.  */
+      else if (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_BRACE))
+	cp_parser_enumerator_list (parser, type);
+
+      /* Consume the final '}'.  */
+      cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE);
+
+      if (scoped_enum_p)
+	finish_scope ();
+    }
+  else
+    {
+      /* If a ';' follows, then it is an opaque-enum-specifier
+	and additional restrictions apply.  */
+      if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
+	{
+	  if (is_anonymous)
+	    error_at (type_start_token->location, "opaque-enum-specifier without name");
+	  else if (nested_name_specifier)
+	    error_at (type_start_token->location, "opaque-enum-specifier must use "
+		      "a simple identifier");
+	}
+    }
+
   /* Look for trailing attributes to apply to this enumeration, and
      apply them if appropriate.  */
   if (cp_parser_allow_gnu_extensions_p (parser))
@@ -13351,8 +13509,26 @@  cp_parser_enum_specifier (cp_parser* parser)
     }
 
   /* Finish up the enumeration.  */
-  finish_enum (type);
+  if (type != error_mark_node)
+    {
+      if (new_value_list)
+	finish_enum_value_list (type);
+      if (is_new_type)
+	finish_enum (type);
+    }
 
+  if (nested_name_specifier)
+    {
+      if (CLASS_TYPE_P (nested_name_specifier))
+	{
+	  TYPE_BEING_DEFINED (nested_name_specifier) = nested_being_defined;
+	  pop_scope (nested_name_specifier);
+	}
+      else if (TREE_CODE (nested_name_specifier) == NAMESPACE_DECL)
+	{
+	  pop_nested_namespace (nested_name_specifier);
+	}
+    }
   return type;
 }
 
Index: gcc/cp/cp-tree.h
===================================================================
--- gcc/cp/cp-tree.h	(revision 165496)
+++ gcc/cp/cp-tree.h	(working copy)
@@ -3101,6 +3101,16 @@  more_aggr_init_expr_args_p (const aggr_init_expr_a
 #define SET_SCOPED_ENUM_P(TYPE, VAL)                    \
   (ENUM_IS_SCOPED (TYPE) = (VAL))
 
+#define SET_OPAQUE_ENUM_P(TYPE, VAL)                    \
+  (ENUM_IS_OPAQUE (TYPE) = (VAL))
+
+#define OPAQUE_ENUM_P(TYPE)				\
+  (TREE_CODE (TYPE) == ENUMERAL_TYPE && ENUM_IS_OPAQUE (TYPE))
+
+/* Determines whether an ENUMERAL_TYPE has an explicit
+   underlying type.  */
+#define ENUM_FIXED_UNDERLYING_TYPE_P(NODE) (TYPE_LANG_FLAG_3 (NODE))
+
 /* Returns the underlying type of the given enumeration type. The
    underlying type is determined in different ways, depending on the
    properties of the enum:
@@ -4785,7 +4795,8 @@  extern bool grok_op_properties			(tree, bool);
 extern tree xref_tag				(enum tag_types, tree, tag_scope, bool);
 extern tree xref_tag_from_type			(tree, tree, tag_scope);
 extern bool xref_basetypes			(tree, tree);
-extern tree start_enum				(tree, tree, bool);
+extern tree start_enum				(tree, tree, tree, bool, bool *);
+extern void finish_enum_value_list		(tree);
 extern void finish_enum				(tree);
 extern void build_enumerator			(tree, tree, tree, location_t);
 extern tree lookup_enumerator			(tree, tree);
Index: gcc/dwarf2out.c
===================================================================
--- gcc/dwarf2out.c	(revision 165496)
+++ gcc/dwarf2out.c	(working copy)
@@ -18306,9 +18306,13 @@  gen_enumeration_type_die (tree type, dw_die_ref co
 			  scope_die_for (type, context_die), type);
       equate_type_number_to_die (type, type_die);
       add_name_attribute (type_die, type_tag (type));
-      if ((dwarf_version >= 4 || !dwarf_strict)
-	  && ENUM_IS_SCOPED (type))
-	add_AT_flag (type_die, DW_AT_enum_class, 1);
+      if (dwarf_version >= 4 || !dwarf_strict)
+	{
+	  if (ENUM_IS_SCOPED (type))
+	    add_AT_flag (type_die, DW_AT_enum_class, 1);
+	  if (ENUM_IS_OPAQUE (type))
+	    add_AT_flag (type_die, DW_AT_declaration, 1);
+	}
     }
   else if (! TYPE_SIZE (type))
     return type_die;