diff mbox

Fix PR c++/70241 (inconsistent access with in-class enumeration)

Message ID 1460916090-9444-1-git-send-email-patrick@parcs.ath.cx
State New
Headers show

Commit Message

Patrick Palka April 17, 2016, 6:01 p.m. UTC
When an in-class unscoped enumeration is defined out-of-line its
enumerators currently don't inherit the access of the enumeration.  This
patch makes the access of the enumerations defined out-of-line match the
access of the enumerator.

Also, we currently don't check that redeclarations of in-class
enumerations have the same access, which this patch fixes as well.

Bootstrapped + regtested on x86_64-pc-linux-gnu, does this look OK to
commit?

gcc/cp/ChangeLog:

	PR c++/70241
	* decl.c (build_enumerator): Set current_access_specifier when
	declaring an enumerator belonging to an in-class enumeration.
	* parser.c (cp_parser_check_access_in_redecleration): Also
	consider in-class enumerations.

gcc/testsite/ChangeLog:

	PR c++/70241
	* g++.dg/cpp0x/enum32.C: New test.
	* g++.dg/cpp0x/enum33.C: New test.
---
 gcc/cp/decl.c                       | 28 ++++++++++++++++++++++++----
 gcc/cp/parser.c                     |  8 +++++---
 gcc/testsuite/g++.dg/cpp0x/enum32.C | 25 +++++++++++++++++++++++++
 gcc/testsuite/g++.dg/cpp0x/enum33.C | 11 +++++++++++
 4 files changed, 65 insertions(+), 7 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/enum32.C
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/enum33.C

Comments

Patrick Palka April 25, 2016, 5:40 p.m. UTC | #1
On Sun, Apr 17, 2016 at 2:01 PM, Patrick Palka <patrick@parcs.ath.cx> wrote:
> When an in-class unscoped enumeration is defined out-of-line its
> enumerators currently don't inherit the access of the enumeration.  This
> patch makes the access of the enumerations defined out-of-line match the
> access of the enumerator.
>
> Also, we currently don't check that redeclarations of in-class
> enumerations have the same access, which this patch fixes as well.
>
> Bootstrapped + regtested on x86_64-pc-linux-gnu, does this look OK to
> commit?
>
> gcc/cp/ChangeLog:
>
>         PR c++/70241
>         * decl.c (build_enumerator): Set current_access_specifier when
>         declaring an enumerator belonging to an in-class enumeration.
>         * parser.c (cp_parser_check_access_in_redecleration): Also
>         consider in-class enumerations.
>
> gcc/testsite/ChangeLog:
>
>         PR c++/70241
>         * g++.dg/cpp0x/enum32.C: New test.
>         * g++.dg/cpp0x/enum33.C: New test.
> ---
>  gcc/cp/decl.c                       | 28 ++++++++++++++++++++++++----
>  gcc/cp/parser.c                     |  8 +++++---
>  gcc/testsuite/g++.dg/cpp0x/enum32.C | 25 +++++++++++++++++++++++++
>  gcc/testsuite/g++.dg/cpp0x/enum33.C | 11 +++++++++++
>  4 files changed, 65 insertions(+), 7 deletions(-)
>  create mode 100644 gcc/testsuite/g++.dg/cpp0x/enum32.C
>  create mode 100644 gcc/testsuite/g++.dg/cpp0x/enum33.C
>
> diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
> index f9f12a7..0f217a5 100644
> --- a/gcc/cp/decl.c
> +++ b/gcc/cp/decl.c
> @@ -13694,10 +13694,30 @@ incremented enumerator value is too large for %<long%>");
>      cplus_decl_attributes (&decl, attributes, 0);
>
>    if (context && context == current_class_type && !SCOPED_ENUM_P (enumtype))
> -    /* In something like `struct S { enum E { i = 7 }; };' we put `i'
> -       on the TYPE_FIELDS list for `S'.  (That's so that you can say
> -       things like `S::i' later.)  */
> -    finish_member_declaration (decl);
> +    {
> +      /* In something like `struct S { enum E { i = 7 }; };' we put `i'
> +        on the TYPE_FIELDS list for `S'.  (That's so that you can say
> +        things like `S::i' later.)  */
> +
> +      /* The enumerator may be getting declared outside of its enclosing
> +        class, like so:
> +
> +          class S { public: enum E : int; }; enum S::E : int { i = 7; };
> +
> +        For which case we need to make sure that the access of `S::i'
> +        matches the access of `S::E'.  */
> +      tree saved_cas = current_access_specifier;
> +      if (TREE_PRIVATE (TYPE_NAME (enumtype)))
> +       current_access_specifier = access_private_node;
> +      else if (TREE_PROTECTED (TYPE_NAME (enumtype)))
> +       current_access_specifier = access_protected_node;
> +      else
> +       current_access_specifier = access_public_node;
> +
> +      finish_member_declaration (decl);
> +
> +      current_access_specifier = saved_cas;
> +    }
>    else
>      pushdecl (decl);
>
> diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
> index 5486129..f782d70 100644
> --- a/gcc/cp/parser.c
> +++ b/gcc/cp/parser.c
> @@ -27228,13 +27228,15 @@ cp_parser_check_class_key (enum tag_types class_key, tree type)
>
>  /* Issue an error message if DECL is redeclared with different
>     access than its original declaration [class.access.spec/3].
> -   This applies to nested classes and nested class templates.
> -   [class.mem/1].  */
> +   This applies to nested classes, nested class templates and
> +   enumerations [class.mem/1].  */
>
>  static void
>  cp_parser_check_access_in_redeclaration (tree decl, location_t location)
>  {
> -  if (!decl || !CLASS_TYPE_P (TREE_TYPE (decl)))
> +  if (!decl
> +      || (!CLASS_TYPE_P (TREE_TYPE (decl))
> +         && TREE_CODE (TREE_TYPE (decl)) != ENUMERAL_TYPE))
>      return;
>
>    if ((TREE_PRIVATE (decl)
> diff --git a/gcc/testsuite/g++.dg/cpp0x/enum32.C b/gcc/testsuite/g++.dg/cpp0x/enum32.C
> new file mode 100644
> index 0000000..9d7a7b5
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp0x/enum32.C
> @@ -0,0 +1,25 @@
> +// PR c++/70241
> +// { dg-do compile { target c++11 } }
> +
> +class A {
> +public:
> +   enum B : int;
> +};
> +
> +enum A::B : int {
> +   x
> +};
> +
> +struct C {
> +private:
> +    enum D : int;
> +};
> +
> +enum C::D : int {
> +   y
> +};
> +
> +int main() {
> +   A::x;
> +   C::y; // { dg-error "private" }
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp0x/enum33.C b/gcc/testsuite/g++.dg/cpp0x/enum33.C
> new file mode 100644
> index 0000000..ac39741
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp0x/enum33.C
> @@ -0,0 +1,11 @@
> +// PR c++/70241
> +// { dg-do compile { target c++11 } }
> +
> +class A {
> +public:
> +  enum B : int;
> +  enum class C : int;
> +private:
> +  enum B : int { }; // { dg-error "different access" }
> +  enum class C : int { }; // { dg-error "different access" }
> +};
> --
> 2.8.1.231.g95ac767
>

Ping.
Jason Merrill April 25, 2016, 7:24 p.m. UTC | #2
OK.

Jason
diff mbox

Patch

diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index f9f12a7..0f217a5 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -13694,10 +13694,30 @@  incremented enumerator value is too large for %<long%>");
     cplus_decl_attributes (&decl, attributes, 0);
 
   if (context && context == current_class_type && !SCOPED_ENUM_P (enumtype))
-    /* In something like `struct S { enum E { i = 7 }; };' we put `i'
-       on the TYPE_FIELDS list for `S'.  (That's so that you can say
-       things like `S::i' later.)  */
-    finish_member_declaration (decl);
+    {
+      /* In something like `struct S { enum E { i = 7 }; };' we put `i'
+	 on the TYPE_FIELDS list for `S'.  (That's so that you can say
+	 things like `S::i' later.)  */
+
+      /* The enumerator may be getting declared outside of its enclosing
+	 class, like so:
+
+	   class S { public: enum E : int; }; enum S::E : int { i = 7; };
+
+	 For which case we need to make sure that the access of `S::i'
+	 matches the access of `S::E'.  */
+      tree saved_cas = current_access_specifier;
+      if (TREE_PRIVATE (TYPE_NAME (enumtype)))
+	current_access_specifier = access_private_node;
+      else if (TREE_PROTECTED (TYPE_NAME (enumtype)))
+	current_access_specifier = access_protected_node;
+      else
+	current_access_specifier = access_public_node;
+
+      finish_member_declaration (decl);
+
+      current_access_specifier = saved_cas;
+    }
   else
     pushdecl (decl);
 
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 5486129..f782d70 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -27228,13 +27228,15 @@  cp_parser_check_class_key (enum tag_types class_key, tree type)
 
 /* Issue an error message if DECL is redeclared with different
    access than its original declaration [class.access.spec/3].
-   This applies to nested classes and nested class templates.
-   [class.mem/1].  */
+   This applies to nested classes, nested class templates and
+   enumerations [class.mem/1].  */
 
 static void
 cp_parser_check_access_in_redeclaration (tree decl, location_t location)
 {
-  if (!decl || !CLASS_TYPE_P (TREE_TYPE (decl)))
+  if (!decl
+      || (!CLASS_TYPE_P (TREE_TYPE (decl))
+	  && TREE_CODE (TREE_TYPE (decl)) != ENUMERAL_TYPE))
     return;
 
   if ((TREE_PRIVATE (decl)
diff --git a/gcc/testsuite/g++.dg/cpp0x/enum32.C b/gcc/testsuite/g++.dg/cpp0x/enum32.C
new file mode 100644
index 0000000..9d7a7b5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/enum32.C
@@ -0,0 +1,25 @@ 
+// PR c++/70241
+// { dg-do compile { target c++11 } }
+
+class A {
+public:
+   enum B : int;
+};
+
+enum A::B : int {
+   x
+};
+
+struct C {
+private:
+    enum D : int;
+};
+
+enum C::D : int {
+   y
+};
+
+int main() {
+   A::x;
+   C::y; // { dg-error "private" }
+}
diff --git a/gcc/testsuite/g++.dg/cpp0x/enum33.C b/gcc/testsuite/g++.dg/cpp0x/enum33.C
new file mode 100644
index 0000000..ac39741
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/enum33.C
@@ -0,0 +1,11 @@ 
+// PR c++/70241
+// { dg-do compile { target c++11 } }
+
+class A {
+public:
+  enum B : int;
+  enum class C : int;
+private:
+  enum B : int { }; // { dg-error "different access" }
+  enum class C : int { }; // { dg-error "different access" }
+};