diff mbox

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

Message ID AANLkTin7cyGaTH5EUkgyu5cVY+vRkr8KF4xgxVqc7khj@mail.gmail.com
State New
Headers show

Commit Message

Rodrigo Rivas Oct. 4, 2010, 7:17 p.m. UTC
On Mon, Oct 4, 2010 at 4:20 PM, Jason Merrill <jason@redhat.com> wrote:
>> Oh, I just copied the nearest condition I could find. Should I change
>> the others too?
>
> As a separate patch, sure.
Ok.

> What bits of finish_enum do you actually need for an opaque enum?  Very
> little, it seems to me.
Well, actually just three parts. I divided the function in two parts,
and ensured that "finish_enum()" is called just once, when the first
time the enumeration is seen; "finish_enum_value_list()" is called
when/if the enumeration list is parsed.
This way the debugging information for an opaque enumerator will lack
the enumeration list, but that's hard to avoid.

>> BTW, there is some code that is getting in my way. It is the call to
>> lookup_and_check_tag() in start_enum(). The comment says that this is
>> related to "forward references". AFAIK, forwards enums are a GCC
>> extension for C, but they are not enabled in C++ (and anyway are
>> superseded by opaque enums). Could you tell me if this is still in use
>> or if it is an evolutionary remnant?
>
> Sounds like the latter; what happens if you remove it?
Well, as it turns out, this code is not in my way; it is just what I
need. So maybe before it was not needed (I didn't check), but now it
is  ;-).

Please, find the attached patch. I think it works pretty well now.
Some random thoughts:
 * The following code compiles and I cannot see why it shouldn't, but
there is something strange about it so I didn't write it in a
testcase:

template<typename T>
struct S
{
    enum class E;
};
enum class S<int>::E { a, b, c};
enum class S<double>::E { d, e, f};

* The latest draft says that the elaborated-type-specifier for an
enumeration cannot use "enum class/struct". I added a pedwarn for that
instead of an error, since it is "mostly harmless". Anyway, I'm not
sure if the diagnostic can interfere with the tentative parsing (I
think it is safe).

* I've rewritten a lot of the parsing code for enumerations, so I
advise extra care in the review.

* The new error messages are written in my home-made English, so feel
free to rewrite them as you see fit.

Regards.
Rodrigo

Changelog:

gcc/testsuite:
2010-10-04  Rodrigo Rivas Costa <rodrigorivascosta@gmail.com>
	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/scoped_enum.C: Fix compiler error.
	g++.dg/parse/enum3.C: Add new dg-error.
	g++.dg/cpp0x/auto9.C: Likewise.
	g++.dg/template/crash79.C: Likewise.

gcc/cp:
2010-10-04  Rodrigo Rivas Costa <rodrigorivascosta@gmail.com>
	decl.c (copy_type_enum): New.
	(start_enum): New parameters enumtype and is_new.
	Check additional errors. Call copy_type_enum.
	Do not call begin_scope.
	(finish_enum_value_list): New from pieces of finish_enum.
	(finish_enum): Split into two. Call copy_type_enum.
	Do not call end_scope.
	pt.c (lookup_template_class): Update call to start_enum.
	(tsubst_enum): Call begin_scope, finish_scope and
	finish_enum_value_list.
	parser.c (cp_parser_elaborated_type_specifier): Pedwarn if
	an enum-key is used in an elaborated-type-specifier.
	(cp_parser_enum_specifier): Parse opaque-enum-specifiers.
	cp-tree.h (ENUM_HAS_VALUE_LIST_P): New.
	Declare finish_enum_value_list. Update declaration of start_enum.

Comments

Jason Merrill Oct. 6, 2010, 4:35 a.m. UTC | #1
On 10/04/2010 03:17 PM, Rodrigo Rivas wrote:
> On Mon, Oct 4, 2010 at 4:20 PM, Jason Merrill<jason@redhat.com>  wrote:
>> What bits of finish_enum do you actually need for an opaque enum?  Very
>> little, it seems to me.
> Well, actually just three parts. I divided the function in two parts,
> and ensured that "finish_enum()" is called just once, when the first
> time the enumeration is seen; "finish_enum_value_list()" is called
> when/if the enumeration list is parsed.

Do you really need to do the variant fixup for opaque enums?  It's not 
clear to me how you could get a variant with the wrong values unless you 
had a forward declaration with a different underlying type, which would 
only happen with GCC forward declarations, not with C++0x opaque enums.

> This way the debugging information for an opaque enumerator will lack
> the enumeration list, but that's hard to avoid.

It doesn't seem that hard; we just need to indicate incompleteness to 
the back end in some way other than not setting TYPE_SIZE.  There are 
lots of free flags still.

I'd suggest reversing the sense of ENUM_HAS_VALUE_LIST_P so that it's 
only set on opaque enums, and then moving it to use base.private_flag 
instead of lang_flag_2.

I don't think we want to do rest_of_type_compilation for opaque decls, 
either; only for the actual definition.

>   * The following code compiles and I cannot see why it shouldn't, but
> there is something strange about it so I didn't write it in a
> testcase:
>
> template<typename T>
> struct S
> {
>      enum class E;
> };
> enum class S<int>::E { a, b, c };
> enum class S<double>::E { d, e, f };

Interesting.  Yes, it makes sense for that to work, though perhaps they 
should have "template<>" at the beginning.  I'll bring this up with the 
committee.

> * The latest draft says that the elaborated-type-specifier for an
> enumeration cannot use "enum class/struct". I added a pedwarn for that
> instead of an error, since it is "mostly harmless". Anyway, I'm not
> sure if the diagnostic can interfere with the tentative parsing (I
> think it is safe).

I agree; "enum class E" will always be either an 
elaborated-type-specifier or an enum-specifier, we can't tentatively 
parse it as one thing and then another.

BTW, let's improve the diagnostic for "enum E;" in C++0x mode to let the 
user know that either "enum E: int;" or "enum class E;" would be OK.

> +      if (!underlying_type
> +         || (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");
> +       }

This test complains about mismatching underlying types for

enum A { e };
enum A { e };

which is incorrect.

> +      if (cxx_dialect == cxx98)
> +       {
> +         error_at (input_location, "multiple definition of %q#T", enumtype);

Do we need to check this here?  I would expect the code in 
cp_parser_enum_specifier to be sufficient.

> +  /* Clear the qualification. Note that '::' is not allowed here
> +     so try to make a useful message.  */

I don't see the useful message, or a testcase for it.

> +  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 nested_name_specifier is a dependent type, as in

template <class T>
struct A
{
   enum E: int;
};

template <class T>
enum A<T>::E { e1 };

this lookup returns a TYPENAME_TYPE, which start_enum then ignores and 
creates a new ENUMERAL_TYPE which doesn't actually get added to the 
class.  We shouldn't just ignore an invalid enumtype passed into the 
function, and we should resolve a TYPENAME_TYPE like 
cp_parser_class_head does.

Jason
Rodrigo Rivas Oct. 6, 2010, 1:42 p.m. UTC | #2
On Wed, Oct 6, 2010 at 6:35 AM, Jason Merrill <jason@redhat.com> wrote:
> Do you really need to do the variant fixup for opaque enums?  It's not clear
> to me how you could get a variant with the wrong values unless you had a
> forward declaration with a different underlying type, which would only
> happen with GCC forward declarations, not with C++0x opaque enums.

I'm afraid I don't really understand this variant stuff or when they
are created, so I just did it safely.

But there are two parts in this fixup: (a) it fixes the underlying
type; (b) it fixes the enumerator list.
So I just did (a) in finish_enum and (b) in finish_enum_value_list.
Given that this two fixups are needed, I don't see how this is
different with opaque enums, except that they are done at different
times.

>> This way the debugging information for an opaque enumerator will lack
>> the enumeration list, but that's hard to avoid.
>
> It doesn't seem that hard; we just need to indicate incompleteness to the
> back end in some way other than not setting TYPE_SIZE.  There are lots of
> free flags still.

But then, should I modify the debuggers back end? I think that's overkilling!

> I'd suggest reversing the sense of ENUM_HAS_VALUE_LIST_P so that it's only
> set on opaque enums, and then moving it to use base.private_flag instead of
> lang_flag_2.
>
> I don't think we want to do rest_of_type_compilation for opaque decls,
> either; only for the actual definition.

rest_of_type_compilation just emits the debugging information for the
type, right?
But the opaque-enum *are* definitions: a opaque enum is a complete
type. You may not have an enumerator list in this compilation unit
while you may have variables of this type:

enum class E;
E e;
/* EOF */

If I don't emit debugging information for the opaque-enum, then the
variable e will lack debugging information.

Curiously, I tried to remove the call to rest_of_type_compilation in
my linux box: with "-g" the debugger is happy, but with "-gstabs+" it
complains that the type is incomplete.

>> template<typename T>
>> struct S
>> {
>>     enum class E;
>> };
>> enum class S<int>::E { a, b, c };
>> enum class S<double>::E { d, e, f };
>
> Interesting.  Yes, it makes sense for that to work, though perhaps they
> should have "template<>" at the beginning.  I'll bring this up with the
> committee.

template<> makes sense if you see this as a template specialization
(but see my reply below to the other template issue).
I see it as two opaque enum listings that happen to be into two class
template instantiations.
I think that the reason for that weirdness is that opaque enum listing
is the only case in C++ (that I know of) where a type is modified
after it is completed.

> BTW, let's improve the diagnostic for "enum E;" in C++0x mode to let the
> user know that either "enum E: int;" or "enum class E;" would be OK.
Hmmm, as it happens "enum E;" is parsed without diagnostics in C++98
if the enumeration is already defined.
It emits the error "use of E without a previous definition" because it
parses it as an elaborated_type_specifier.
So I think that this error may be emitted in
cp_parser_elaborated_type_specifier if it is an enum and the
next_token is a ';'.

BTW this should also be tagged as an error in C++98, I think, because
"If an elaborated-type-specifier is the sole constituent of a
declaration, the declaration is ill-formed..."

> This test complains about mismatching underlying types for
>
> enum A { e };
> enum A { e };
>
> which is incorrect.

Is is because the parser sees something like that.

enum A : unique_type1 { e };
enum A : unique_type2 { e };

Where unique_typeN is created after the list of enumerators.
So the first error it sees it that the underlying types are different.
Admittedly, the message is misleading, but correct, from a certain
point of view ;-)
The only solution I can think of is to add yet another flag to the
enumeration tree to signal whether an enum has an explicit underlying
type. Checking this flag we can ignore this particular error and it
will stump upon the multiple definition error.

>> +      if (cxx_dialect == cxx98)
>> +       {
>> +         error_at (input_location, "multiple definition of %q#T",
>> enumtype);
>
> Do we need to check this here?  I would expect the code in
> cp_parser_enum_specifier to be sufficient.

I think you are right, this is not needed here any more.

>> +  /* Clear the qualification. Note that '::' is not allowed here
>> +     so try to make a useful message.  */
>
> I don't see the useful message, or a testcase for it.
Ops! I had these  two lines of code here:
+ if (cp_lexer_next_token_is (parser->lexer, CPP_SCOPE))
+    cp_parser_error(parser, "unexpected %<::%>");

But since this code is always parsed tentatively, the error message is
never seen. And cp_parser_nested_name_specifier_opt
fails with '::' anyway.
I'll simply delete the offending comment.

> If nested_name_specifier is a dependent type, as in
>
> template <class T>
> struct A
> {
>  enum E: int;
> };
>
> template <class T>
> enum A<T>::E { e1 };
>
> this lookup returns a TYPENAME_TYPE, which start_enum then ignores and
> creates a new ENUMERAL_TYPE which doesn't actually get added to the class.
>  We shouldn't just ignore an invalid enumtype passed into the function, and
> we should resolve a TYPENAME_TYPE like cp_parser_class_head does.

This certainly does the wrong thing, but I don't think it should even compile!
According to 14.1 (templ):
"
The declaration in a template-declaration shall
- declare or define a function or a class, or
- define a member function, a member class or a static data member of
a class template or of a class
nested within a class template, or
- define a member template of a class or class template, or
- be an alias-declaration.
"
So an "enum template" does not exist. It would be a nice gnu++0x
extension, though (too late to try to get it in the draft...).

While I await your further comments, I'll prepare a new patch with
these corrections.

Regards.
Rodrigo
Jason Merrill Oct. 11, 2010, 8:35 p.m. UTC | #3
On 10/06/2010 09:42 AM, Rodrigo Rivas wrote:
> On Wed, Oct 6, 2010 at 6:35 AM, Jason Merrill<jason@redhat.com>  wrote:
>> Do you really need to do the variant fixup for opaque enums?  It's not clear
>> to me how you could get a variant with the wrong values unless you had a
>> forward declaration with a different underlying type, which would only
>> happen with GCC forward declarations, not with C++0x opaque enums.
>
> I'm afraid I don't really understand this variant stuff or when they
> are created, so I just did it safely.

Variants are e.g. const-qualified, typedef'd, or attributed versions of 
types.  We can't have any variants of a type before it is first 
declared, so we should never need to copy_type_enum to variants; since 
we don't have C-like forward declarations with unspecified underlying 
type, there's no way to create a variant before the underlying type is 
set.  I suppose the code that was doing that before was a relic of when 
the C++ front end forked from the C front end.

Let's try removing that call and instead asserting that when we're 
setting the underlying type, there are no variants.

>>> This way the debugging information for an opaque enumerator will lack
>>> the enumeration list, but that's hard to avoid.
>>
>> It doesn't seem that hard; we just need to indicate incompleteness to the
>> back end in some way other than not setting TYPE_SIZE.  There are lots of
>> free flags still.
>
> But then, should I modify the debuggers back end? I think that's overkilling!

I don't think it's overkill at all.  If we don't have the enumeration 
list, then we don't have enough information for complete debug info.

>> I'd suggest reversing the sense of ENUM_HAS_VALUE_LIST_P so that it's only
>> set on opaque enums, and then moving it to use base.private_flag instead of
>> lang_flag_2.
>>
>> I don't think we want to do rest_of_type_compilation for opaque decls,
>> either; only for the actual definition.
>
> rest_of_type_compilation just emits the debugging information for the
> type, right?

Yes.

> But the opaque-enum *are* definitions: a opaque enum is a complete
> type. You may not have an enumerator list in this compilation unit
> while you may have variables of this type:

> enum class E;
> E e;
> /* EOF */
>
> If I don't emit debugging information for the opaque-enum, then the
> variable e will lack debugging information.

It should work the same way as a use of an incomplete class:

class A *ap;

When the debugging back end sees a use of a type, it emits it if it 
hasn't already been emitted.

>>> template<typename T>
>>> struct S
>>> {
>>>      enum class E;
>>> };
>>> enum class S<int>::E { a, b, c };
>>> enum class S<double>::E { d, e, f };
>>
>> Interesting.  Yes, it makes sense for that to work, though perhaps they
>> should have "template<>" at the beginning.  I'll bring this up with the
>> committee.
>
> template<>  makes sense if you see this as a template specialization
> I see it as two opaque enum listings that happen to be into two class
> template instantiations.

Yes, but the convention is that we use template<> for definitions of 
members of template instantiations as well:

template <typename T>
struct S
{
   static T t;
};

template<> int S<int>::t = 42;
template<> double S<double>::t = 3.1415;

>> BTW, let's improve the diagnostic for "enum E;" in C++0x mode to let the
>> user know that either "enum E: int;" or "enum class E;" would be OK.
> Hmmm, as it happens "enum E;" is parsed without diagnostics in C++98
> if the enumeration is already defined.
> It emits the error "use of E without a previous definition" because it
> parses it as an elaborated_type_specifier.

Right, we can just give a different error at the same place in C++0x mode.

>> This test complains about mismatching underlying types for
>>
>> enum A { e };
>> enum A { e };
>>
>> which is incorrect.
>
> Is is because the parser sees something like that.
>
> enum A : unique_type1 { e };
> enum A : unique_type2 { e };
>
> Where unique_typeN is created after the list of enumerators.
> So the first error it sees it that the underlying types are different.
> Admittedly, the message is misleading, but correct, from a certain
> point of view ;-)
> The only solution I can think of is to add yet another flag to the
> enumeration tree to signal whether an enum has an explicit underlying
> type. Checking this flag we can ignore this particular error and it
> will stump upon the multiple definition error.

Couldn't we just wait to give this error until finish_enum?

>> template<class T>
>> struct A
>> {
>>   enum E: int;
>> };
>>
>> template<class T>
>> enum A<T>::E { e1 };
>>
> This certainly does the wrong thing, but I don't think it should even compile!
> According to 14.1 (templ):
> "
> The declaration in a template-declaration shall
> - declare or define a function or a class, or
> - define a member function, a member class or a static data member of
> a class template or of a class
> nested within a class template, or
> - define a member template of a class or class template, or
> - be an alias-declaration.
> "
> So an "enum template" does not exist. It would be a nice gnu++0x
> extension, though (too late to try to get it in the draft...).

I'm not so sure it's too late to get it in; it seems like a simple 
oversight to me.  I am trying to get it into the draft, for which a 
working implementation would help...:)

Jason
diff mbox

Patch

Index: gcc/testsuite/g++.dg/parse/enum3.C
===================================================================
--- gcc/testsuite/g++.dg/parse/enum3.C	(revision 164926)
+++ 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,74 @@ 
+// { 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" }
+
+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 164926)
+++ 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_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/auto9.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/auto9.C	(revision 164926)
+++ 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 164926)
+++ 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 164926)
+++ 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);
@@ -11310,41 +11311,90 @@  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;
-
   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 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 && TREE_CODE (enumtype) == ENUMERAL_TYPE)
+    {
+      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");
+	}
+      if (!underlying_type
+	  || (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");
+	}
 
-  if (enumtype != NULL_TREE && 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 (cxx_dialect == cxx98)
+	{
+	  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;
+	}
     }
   else
     {
@@ -11353,6 +11403,8 @@  tree
       if (enumtype == error_mark_node)
 	name = make_anon_name ();
 
+      if (is_new)
+	*is_new = true;
       enumtype = cxx_make_type (ENUMERAL_TYPE);
       enumtype = pushtag (name, enumtype, /*tag_scope=*/ts_current);
     }
@@ -11360,35 +11412,18 @@  tree
   if (enumtype == error_mark_node)
     return enumtype;
 
-  if (scoped_enum_p)
-    {
-      SET_SCOPED_ENUM_P (enumtype, 1);
-      begin_scope (sk_scoped_enum, enumtype);
+  SET_SCOPED_ENUM_P (enumtype, scoped_enum_p);
 
-      /* [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);
     }
@@ -11397,19 +11432,19 @@  tree
 }
 
 /* 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;
 
@@ -11426,10 +11461,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;
     }
 
@@ -11439,34 +11470,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;
@@ -11531,15 +11562,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.
 
@@ -11604,24 +11627,32 @@  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);
+}
+
+/* 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)
+{
+  tree t;
+
+  if (processing_template_decl)
     {
-      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);
+      if (at_function_scope_p ())
+	add_stmt (build_min (TAG_DEFN, enumtype));
+      return;
+    }
+
+  /* Fix up all variant types of this enum type.  */
+  for (t = TYPE_MAIN_VARIANT (enumtype); t; t = TYPE_NEXT_VARIANT (t))
+    {
+      copy_type_enum (t, enumtype);
       ENUM_UNDERLYING_TYPE (t) = ENUM_UNDERLYING_TYPE (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 ());
 }
Index: gcc/cp/pt.c
===================================================================
--- gcc/cp/pt.c	(revision 164926)
+++ gcc/cp/pt.c	(working copy)
@@ -6654,10 +6654,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
             {
@@ -17292,6 +17292,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))
     {
@@ -17313,7 +17316,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 164926)
+++ gcc/cp/parser.c	(working copy)
@@ -12905,17 +12897,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);
     }
@@ -13205,8 +13197,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]
@@ -13215,6 +13211,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]
@@ -13226,11 +13225,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;
   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.  */
@@ -13247,7 +13253,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.  */
@@ -13258,10 +13264,51 @@  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. Note that '::' is not allowed here
+     so try to make a useful message.  */
+  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);
+      else
+	error_at (input_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.  */
@@ -13281,7 +13328,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;
@@ -13299,14 +13346,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 %<;%>");
+	  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;
@@ -13314,24 +13386,80 @@  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)
+	{
+	  /* 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 (input_location,
+			  "declaration of %qD in namespace %qD which does not "
+			  "enclose %qD",
+			  type, prev_scope, nested_name_specifier);
+	      else
+		error_at (input_location,
+			  "declaration of %qD in %qD which does not enclose %qD",
+			  type, prev_scope, nested_name_specifier);
+	      type = error_mark_node;
+	    }
+	}
 
-  /* 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 (scoped_enum_p)
+	begin_scope (sk_scoped_enum, type);
 
-  /* Consume the final '}'.  */
-  cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE);
+      /* Consume the opening brace.  */
+      cp_lexer_consume_token (parser->lexer);
 
+      if (type == error_mark_node)
+	; /* Nothing to add */
+      else if (ENUM_HAS_VALUE_LIST_P (type))
+	{
+	  error_at (input_location, "multiple definition of %q#T", type);
+	  error_at (DECL_SOURCE_LOCATION (TYPE_MAIN_DECL (type)),
+		    "previous definition here");
+	  type = error_mark_node;
+	}
+      else
+	{
+	  new_value_list = true;
+	  ENUM_HAS_VALUE_LIST_P (type) = true;
+	  DECL_SOURCE_LOCATION (TYPE_NAME (type)) = type_start_token->location;
+	}
+
+      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 (input_location, "opaque-enum-specifier without name");
+	  else if (nested_name_specifier)
+	    error_at (input_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))
@@ -13344,8 +13472,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 164926)
+++ gcc/cp/cp-tree.h	(working copy)
@@ -3102,6 +3102,8 @@  more_aggr_init_expr_args_p (const aggr_init_expr_a
 #define SET_SCOPED_ENUM_P(TYPE, VAL)                    \
   (ENUM_IS_SCOPED (TYPE) = (VAL))
 
+#define ENUM_HAS_VALUE_LIST_P(NODE) (TYPE_LANG_FLAG_2 (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:
@@ -4778,7 +4780,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);