Fix C++/93809 and C++/83469: typenames and unions
Commit Message
From: Andrew Pinski <apinski@marvell.com>
There are a few issues here with typenames and unions (and even struct
keywords with unions). First in cp_parser_check_class_key,
we need to allow typenames to name union types and union key
to be able to use with typenames.
The next issue is we need to record if we had a union key,
right now we just record it was a struct/class/typename one
which is wrong.
OK? Boostrapped and tested on x86_64-linux-gnu with no regressions.
PR c++/83469
PR c++/93809
gcc/cp/ChangeLog:
* cp-tree.h (UNION_CLASS_TYPE_P): New define.
(TYPENAME_IS_UNION_P): New Define.
* decl.c (struct typename_info): Add union_p field.
(struct typename_hasher::equal): Compare union_p field.
(build_typename_type): Move union_type to
union_p/TYPENAME_IS_UNION_P.
* error.c (dump_type) <case TYPENAME_TYPE>: Handle TYPENAME_IS_UNION_P
as "union"
* module.cc (trees_out::type_node): Handle TYPENAME_IS_UNION_P.
* parser.c (cp_parser_check_class_key): Allow
typename key for union types and allow union keyword for
typename types.
* pt.c (tsubst) <case TYPENAME_TYPE>: For TYPENAME_IS_CLASS_P,
check NON_UNION_CLASS_TYPE_P rather than CLASS_TYPE_P.
Add TYPENAME_IS_UNION_P handling.
gcc/testsuite/ChangeLog:
* g++.dg/warn/Wredundant-tags-3.C: Remove xfail.
* g++.dg/pr83469-1.C: New test.
* g++.dg/pr83469-2.C: New test.
* g++.dg/pr83469-3.C: New test.
* g++.dg/pr93809-1.C: New test.
* g++.dg/pr93809-2.C: New test.
* g++.dg/pr93809-3.C: New test.
---
gcc/cp/cp-tree.h | 11 +++++++++--
gcc/cp/decl.c | 9 ++++++---
gcc/cp/error.c | 1 +
gcc/cp/module.cc | 2 ++
gcc/cp/parser.c | 4 +++-
gcc/cp/pt.c | 10 +++++++++-
gcc/testsuite/g++.dg/pr83469-1.C | 15 +++++++++++++++
gcc/testsuite/g++.dg/pr83469-2.C | 13 +++++++++++++
gcc/testsuite/g++.dg/pr83469-3.C | 13 +++++++++++++
gcc/testsuite/g++.dg/pr93809-1.C | 11 +++++++++++
gcc/testsuite/g++.dg/pr93809-2.C | 5 +++++
gcc/testsuite/g++.dg/pr93809-3.C | 4 ++++
gcc/testsuite/g++.dg/warn/Wredundant-tags-3.C | 2 +-
13 files changed, 92 insertions(+), 8 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/pr83469-1.C
create mode 100644 gcc/testsuite/g++.dg/pr83469-2.C
create mode 100644 gcc/testsuite/g++.dg/pr83469-3.C
create mode 100644 gcc/testsuite/g++.dg/pr93809-1.C
create mode 100644 gcc/testsuite/g++.dg/pr93809-2.C
create mode 100644 gcc/testsuite/g++.dg/pr93809-3.C
Comments
On Tue, Dec 07, 2021 at 09:59:42AM -0800, apinski--- via Gcc-patches wrote:
> From: Andrew Pinski <apinski@marvell.com>
>
> There are a few issues here with typenames and unions (and even struct
> keywords with unions). First in cp_parser_check_class_key,
> we need to allow typenames to name union types and union key
> to be able to use with typenames.
>
> The next issue is we need to record if we had a union key,
> right now we just record it was a struct/class/typename one
> which is wrong.
>
> OK? Boostrapped and tested on x86_64-linux-gnu with no regressions.
>
> PR c++/83469
> PR c++/93809
>
> gcc/cp/ChangeLog:
>
> * cp-tree.h (UNION_CLASS_TYPE_P): New define.
> (TYPENAME_IS_UNION_P): New Define.
> * decl.c (struct typename_info): Add union_p field.
> (struct typename_hasher::equal): Compare union_p field.
> (build_typename_type): Move union_type to
> union_p/TYPENAME_IS_UNION_P.
> * error.c (dump_type) <case TYPENAME_TYPE>: Handle TYPENAME_IS_UNION_P
> as "union"
Missing "." here.
> * module.cc (trees_out::type_node): Handle TYPENAME_IS_UNION_P.
> * parser.c (cp_parser_check_class_key): Allow
> typename key for union types and allow union keyword for
> typename types.
> * pt.c (tsubst) <case TYPENAME_TYPE>: For TYPENAME_IS_CLASS_P,
> check NON_UNION_CLASS_TYPE_P rather than CLASS_TYPE_P.
> Add TYPENAME_IS_UNION_P handling.
>
> gcc/testsuite/ChangeLog:
>
> * g++.dg/warn/Wredundant-tags-3.C: Remove xfail.
> * g++.dg/pr83469-1.C: New test.
> * g++.dg/pr83469-2.C: New test.
> * g++.dg/pr83469-3.C: New test.
> * g++.dg/pr93809-1.C: New test.
> * g++.dg/pr93809-2.C: New test.
> * g++.dg/pr93809-3.C: New test.
> ---
> gcc/cp/cp-tree.h | 11 +++++++++--
> gcc/cp/decl.c | 9 ++++++---
> gcc/cp/error.c | 1 +
> gcc/cp/module.cc | 2 ++
> gcc/cp/parser.c | 4 +++-
> gcc/cp/pt.c | 10 +++++++++-
> gcc/testsuite/g++.dg/pr83469-1.C | 15 +++++++++++++++
> gcc/testsuite/g++.dg/pr83469-2.C | 13 +++++++++++++
> gcc/testsuite/g++.dg/pr83469-3.C | 13 +++++++++++++
> gcc/testsuite/g++.dg/pr93809-1.C | 11 +++++++++++
> gcc/testsuite/g++.dg/pr93809-2.C | 5 +++++
> gcc/testsuite/g++.dg/pr93809-3.C | 4 ++++
> gcc/testsuite/g++.dg/warn/Wredundant-tags-3.C | 2 +-
> 13 files changed, 92 insertions(+), 8 deletions(-)
> create mode 100644 gcc/testsuite/g++.dg/pr83469-1.C
> create mode 100644 gcc/testsuite/g++.dg/pr83469-2.C
> create mode 100644 gcc/testsuite/g++.dg/pr83469-3.C
> create mode 100644 gcc/testsuite/g++.dg/pr93809-1.C
> create mode 100644 gcc/testsuite/g++.dg/pr93809-2.C
> create mode 100644 gcc/testsuite/g++.dg/pr93809-3.C
>
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 3510512d751..ea9cbb775e6 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -2206,6 +2206,10 @@ enum languages { lang_c, lang_cplusplus };
> #define NON_UNION_CLASS_TYPE_P(T) \
> (TREE_CODE (T) == RECORD_TYPE && TYPE_LANG_FLAG_5 (T))
>
> +/* Nonzero if T is a class type and is a union. */
> +#define UNION_CLASS_TYPE_P(T) \
> + (TREE_CODE (T) == UNION_TYPE && TYPE_LANG_FLAG_5 (T))
> +
> /* Keep these checks in ascending code order. */
> #define RECORD_OR_UNION_CODE_P(T) \
> ((T) == RECORD_TYPE || (T) == UNION_TYPE)
> @@ -4184,11 +4188,14 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
> #define TYPENAME_IS_ENUM_P(NODE) \
> (TREE_LANG_FLAG_0 (TYPENAME_TYPE_CHECK (NODE)))
>
> -/* True if a TYPENAME_TYPE was declared as a "class", "struct", or
> - "union". */
> +/* True if a TYPENAME_TYPE was declared as a "class", "struct". */
> #define TYPENAME_IS_CLASS_P(NODE) \
> (TREE_LANG_FLAG_1 (TYPENAME_TYPE_CHECK (NODE)))
>
> +/* True if a TYPENAME_TYPE was declared as an "union". */
"a union"
> +#define TYPENAME_IS_UNION_P(NODE) \
> + (TREE_LANG_FLAG_3 (TYPENAME_TYPE_CHECK (NODE)))
> +
Please document this new flag in "Usage of TREE_LANG_FLAG_?:" above.
> /* True if a TYPENAME_TYPE is in the process of being resolved. */
> #define TYPENAME_IS_RESOLVING_P(NODE) \
> (TREE_LANG_FLAG_2 (TYPENAME_TYPE_CHECK (NODE)))
> diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
> index 56f80775ca0..8fa07e30d69 100644
> --- a/gcc/cp/decl.c
> +++ b/gcc/cp/decl.c
> @@ -3930,6 +3930,7 @@ struct typename_info {
> tree template_id;
> bool enum_p;
> bool class_p;
> + bool union_p;
> };
>
> struct typename_hasher : ggc_ptr_hash<tree_node>
> @@ -3958,7 +3959,8 @@ struct typename_hasher : ggc_ptr_hash<tree_node>
> && TYPE_CONTEXT (t1) == t2->scope
> && TYPENAME_TYPE_FULLNAME (t1) == t2->template_id
> && TYPENAME_IS_ENUM_P (t1) == t2->enum_p
> - && TYPENAME_IS_CLASS_P (t1) == t2->class_p);
> + && TYPENAME_IS_CLASS_P (t1) == t2->class_p
> + && TYPENAME_IS_UNION_P (t1) == t2->union_p);
> }
> };
>
> @@ -3983,8 +3985,8 @@ build_typename_type (tree context, tree name, tree fullname,
> ti.template_id = fullname;
> ti.enum_p = tag_type == enum_type;
> ti.class_p = (tag_type == class_type
> - || tag_type == record_type
> - || tag_type == union_type);
> + || tag_type == record_type);
Looks like this can fit on a single line now.
> + ti.union_p = tag_type == union_type;
> hashval_t hash = (htab_hash_pointer (ti.scope)
> ^ htab_hash_pointer (ti.name));
>
> @@ -4001,6 +4003,7 @@ build_typename_type (tree context, tree name, tree fullname,
> TYPENAME_TYPE_FULLNAME (t) = ti.template_id;
> TYPENAME_IS_ENUM_P (t) = ti.enum_p;
> TYPENAME_IS_CLASS_P (t) = ti.class_p;
> + TYPENAME_IS_UNION_P (t) = ti.union_p;
>
> /* Build the corresponding TYPE_DECL. */
> tree d = build_decl (input_location, TYPE_DECL, name, t);
> diff --git a/gcc/cp/error.c b/gcc/cp/error.c
> index daea3b39a15..49b509eef14 100644
> --- a/gcc/cp/error.c
> +++ b/gcc/cp/error.c
> @@ -671,6 +671,7 @@ dump_type (cxx_pretty_printer *pp, tree t, int flags)
> pp_cxx_ws_string (pp,
> TYPENAME_IS_ENUM_P (t) ? "enum"
> : TYPENAME_IS_CLASS_P (t) ? "class"
> + : TYPENAME_IS_UNION_P (t) ? "union"
> : "typename");
> dump_typename (pp, t, flags);
> break;
> diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
> index 71d0fab411f..f956d1d90cb 100644
> --- a/gcc/cp/module.cc
> +++ b/gcc/cp/module.cc
> @@ -8819,6 +8819,8 @@ trees_out::type_node (tree type)
> tag_type = enum_type;
> else if (TYPENAME_IS_CLASS_P (type))
> tag_type = class_type;
> + else if (TYPENAME_IS_UNION_P (type))
> + tag_type = union_type;
> u (int (tag_type));
> }
> }
> diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
> index 55e6a1a8b3a..bbb1a52e381 100644
> --- a/gcc/cp/parser.c
> +++ b/gcc/cp/parser.c
> @@ -33304,7 +33304,9 @@ cp_parser_check_class_key (cp_parser *parser, location_t key_loc,
> return;
>
> bool seen_as_union = TREE_CODE (type) == UNION_TYPE;
> - if (seen_as_union != (class_key == union_type))
> + if (class_key != typename_type
> + && TREE_CODE (type) != TYPENAME_TYPE
> + && seen_as_union != (class_key == union_type))
> {
> if (permerror (input_location, "%qs tag used in naming %q#T",
> class_key == union_type ? "union"
> diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
> index d3efc6ea238..3d7539f62c1 100644
> --- a/gcc/cp/pt.c
> +++ b/gcc/cp/pt.c
> @@ -16113,7 +16113,7 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
> else
> return error_mark_node;
> }
> - else if (TYPENAME_IS_CLASS_P (t) && !CLASS_TYPE_P (f))
> + else if (TYPENAME_IS_CLASS_P (t) && !NON_UNION_CLASS_TYPE_P (f))
> {
> if (complain & tf_error)
> error ("%qT resolves to %qT, which is not a class type",
> @@ -16121,6 +16121,14 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
> else
> return error_mark_node;
> }
> + else if (TYPENAME_IS_UNION_P (t) && !UNION_CLASS_TYPE_P (f))
> + {
> + if (complain & tf_error)
> + error ("%qT resolves to %qT, which is not an union type",
"a union"
> + t, f);
> + else
> + return error_mark_node;
> + }
> }
>
> return cp_build_qualified_type_real
> diff --git a/gcc/testsuite/g++.dg/pr83469-1.C b/gcc/testsuite/g++.dg/pr83469-1.C
> new file mode 100644
> index 00000000000..94a21fbbb98
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/pr83469-1.C
> @@ -0,0 +1,15 @@
> +// { dg-do compile }
> +// PR C++/83469
> + struct S {
> + union U { int m; };
> + };
> +
> + template <typename T>
> + void f()
> + { union T::U u; }
> +
> + int
> + main()
> + {
> + f<S>();
> + }
> diff --git a/gcc/testsuite/g++.dg/pr83469-2.C b/gcc/testsuite/g++.dg/pr83469-2.C
> new file mode 100644
> index 00000000000..c202e36b03b
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/pr83469-2.C
> @@ -0,0 +1,13 @@
> +// { dg-do compile }
> +// PR C++/83469
> + struct S {
> + union U { int m; };
> + };
> + template <typename T>
> + void f()
> + { struct T::U u; } // { dg-error "" }
Can we expect a particular error here rather than any?
> + int
> + main()
> + {
> + f<S>();
> + }
> diff --git a/gcc/testsuite/g++.dg/pr83469-3.C b/gcc/testsuite/g++.dg/pr83469-3.C
> new file mode 100644
> index 00000000000..1ffff0af3c9
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/pr83469-3.C
> @@ -0,0 +1,13 @@
> +// { dg-do compile }
> +// PR C++/83469
> + struct S {
> + struct C { int m; };
> + };
> + template <typename T>
> + void f()
> + { union T::C u; } // { dg-error "" }
Likewise.
Additionally, can you move the new tests into an existing subdirectory?
I guess parse/ would be appropriate for this.
Thanks for the patch.
Marek
@@ -2206,6 +2206,10 @@ enum languages { lang_c, lang_cplusplus };
#define NON_UNION_CLASS_TYPE_P(T) \
(TREE_CODE (T) == RECORD_TYPE && TYPE_LANG_FLAG_5 (T))
+/* Nonzero if T is a class type and is a union. */
+#define UNION_CLASS_TYPE_P(T) \
+ (TREE_CODE (T) == UNION_TYPE && TYPE_LANG_FLAG_5 (T))
+
/* Keep these checks in ascending code order. */
#define RECORD_OR_UNION_CODE_P(T) \
((T) == RECORD_TYPE || (T) == UNION_TYPE)
@@ -4184,11 +4188,14 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
#define TYPENAME_IS_ENUM_P(NODE) \
(TREE_LANG_FLAG_0 (TYPENAME_TYPE_CHECK (NODE)))
-/* True if a TYPENAME_TYPE was declared as a "class", "struct", or
- "union". */
+/* True if a TYPENAME_TYPE was declared as a "class", "struct". */
#define TYPENAME_IS_CLASS_P(NODE) \
(TREE_LANG_FLAG_1 (TYPENAME_TYPE_CHECK (NODE)))
+/* True if a TYPENAME_TYPE was declared as an "union". */
+#define TYPENAME_IS_UNION_P(NODE) \
+ (TREE_LANG_FLAG_3 (TYPENAME_TYPE_CHECK (NODE)))
+
/* True if a TYPENAME_TYPE is in the process of being resolved. */
#define TYPENAME_IS_RESOLVING_P(NODE) \
(TREE_LANG_FLAG_2 (TYPENAME_TYPE_CHECK (NODE)))
@@ -3930,6 +3930,7 @@ struct typename_info {
tree template_id;
bool enum_p;
bool class_p;
+ bool union_p;
};
struct typename_hasher : ggc_ptr_hash<tree_node>
@@ -3958,7 +3959,8 @@ struct typename_hasher : ggc_ptr_hash<tree_node>
&& TYPE_CONTEXT (t1) == t2->scope
&& TYPENAME_TYPE_FULLNAME (t1) == t2->template_id
&& TYPENAME_IS_ENUM_P (t1) == t2->enum_p
- && TYPENAME_IS_CLASS_P (t1) == t2->class_p);
+ && TYPENAME_IS_CLASS_P (t1) == t2->class_p
+ && TYPENAME_IS_UNION_P (t1) == t2->union_p);
}
};
@@ -3983,8 +3985,8 @@ build_typename_type (tree context, tree name, tree fullname,
ti.template_id = fullname;
ti.enum_p = tag_type == enum_type;
ti.class_p = (tag_type == class_type
- || tag_type == record_type
- || tag_type == union_type);
+ || tag_type == record_type);
+ ti.union_p = tag_type == union_type;
hashval_t hash = (htab_hash_pointer (ti.scope)
^ htab_hash_pointer (ti.name));
@@ -4001,6 +4003,7 @@ build_typename_type (tree context, tree name, tree fullname,
TYPENAME_TYPE_FULLNAME (t) = ti.template_id;
TYPENAME_IS_ENUM_P (t) = ti.enum_p;
TYPENAME_IS_CLASS_P (t) = ti.class_p;
+ TYPENAME_IS_UNION_P (t) = ti.union_p;
/* Build the corresponding TYPE_DECL. */
tree d = build_decl (input_location, TYPE_DECL, name, t);
@@ -671,6 +671,7 @@ dump_type (cxx_pretty_printer *pp, tree t, int flags)
pp_cxx_ws_string (pp,
TYPENAME_IS_ENUM_P (t) ? "enum"
: TYPENAME_IS_CLASS_P (t) ? "class"
+ : TYPENAME_IS_UNION_P (t) ? "union"
: "typename");
dump_typename (pp, t, flags);
break;
@@ -8819,6 +8819,8 @@ trees_out::type_node (tree type)
tag_type = enum_type;
else if (TYPENAME_IS_CLASS_P (type))
tag_type = class_type;
+ else if (TYPENAME_IS_UNION_P (type))
+ tag_type = union_type;
u (int (tag_type));
}
}
@@ -33304,7 +33304,9 @@ cp_parser_check_class_key (cp_parser *parser, location_t key_loc,
return;
bool seen_as_union = TREE_CODE (type) == UNION_TYPE;
- if (seen_as_union != (class_key == union_type))
+ if (class_key != typename_type
+ && TREE_CODE (type) != TYPENAME_TYPE
+ && seen_as_union != (class_key == union_type))
{
if (permerror (input_location, "%qs tag used in naming %q#T",
class_key == union_type ? "union"
@@ -16113,7 +16113,7 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
else
return error_mark_node;
}
- else if (TYPENAME_IS_CLASS_P (t) && !CLASS_TYPE_P (f))
+ else if (TYPENAME_IS_CLASS_P (t) && !NON_UNION_CLASS_TYPE_P (f))
{
if (complain & tf_error)
error ("%qT resolves to %qT, which is not a class type",
@@ -16121,6 +16121,14 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
else
return error_mark_node;
}
+ else if (TYPENAME_IS_UNION_P (t) && !UNION_CLASS_TYPE_P (f))
+ {
+ if (complain & tf_error)
+ error ("%qT resolves to %qT, which is not an union type",
+ t, f);
+ else
+ return error_mark_node;
+ }
}
return cp_build_qualified_type_real
new file mode 100644
@@ -0,0 +1,15 @@
+// { dg-do compile }
+// PR C++/83469
+ struct S {
+ union U { int m; };
+ };
+
+ template <typename T>
+ void f()
+ { union T::U u; }
+
+ int
+ main()
+ {
+ f<S>();
+ }
new file mode 100644
@@ -0,0 +1,13 @@
+// { dg-do compile }
+// PR C++/83469
+ struct S {
+ union U { int m; };
+ };
+ template <typename T>
+ void f()
+ { struct T::U u; } // { dg-error "" }
+ int
+ main()
+ {
+ f<S>();
+ }
new file mode 100644
@@ -0,0 +1,13 @@
+// { dg-do compile }
+// PR C++/83469
+ struct S {
+ struct C { int m; };
+ };
+ template <typename T>
+ void f()
+ { union T::C u; } // { dg-error "" }
+ int
+ main()
+ {
+ f<S>();
+ }
new file mode 100644
@@ -0,0 +1,11 @@
+// { dg-do compile }
+// PR C++/93809
+class C { };
+enum E { };
+struct S { };
+union U { };
+
+typedef typename ::C C2;
+typedef typename ::E E2;
+typedef typename ::S S2;
+typedef typename ::U U2; // error
new file mode 100644
@@ -0,0 +1,5 @@
+// { dg-do compile }
+// { dg-require-effective-target c++11 }
+// PR C++/93809
+union a{};
+auto var = new (typename :: a );
new file mode 100644
@@ -0,0 +1,4 @@
+// { dg-do compile }
+// PR C++/93809
+typedef union{} typedef_union;
+typename :: typedef_union foo1 () { return typedef_union(); }
@@ -28,7 +28,7 @@ struct N::S s3; // { dg-warning "-Wredundant-tags" }
N::U u1;
typename N::U u2; // { dg-bogus "-Wredundant-tags" }
- // { dg-bogus "'class' tag used in naming 'union N::U" "pr93809" { xfail *-*-*} .-1 }
+ // { dg-bogus "'class' tag used in naming 'union N::U" "pr93809" { target *-*-*} .-1 }
union N::U u3; // { dg-warning "-Wredundant-tags" }