Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
-- >8 --
While working on Reflection I noticed that we reject:
union U { int i; };
constexpr auto r = ^^typename ::U;
which is due to PR83469. Andrew P. posted a patch in 2021:
https://gcc.gnu.org/pipermail/gcc-patches/2021-December/586344.html
for which I had some comments but an updated patch never came.
~~
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.
~~
This patch is an updated and cleaned up version; I've also addressed
a missing bit in pt.cc.
PR c++/83469
PR c++/93809
gcc/cp/ChangeLog:
* cp-tree.h (UNION_TYPE_P): Define.
(TYPENAME_IS_UNION_P): Define.
* decl.cc (struct typename_info): Add union_p field.
(struct typename_hasher::equal): Compare union_p field.
(build_typename_type): Use ti.union_p for union_type. Set
TYPENAME_IS_UNION_P.
* error.cc (dump_type) <case TYPENAME_TYPE>: Handle
TYPENAME_IS_UNION_P.
* module.cc (trees_out::type_node): Likewise.
* parser.cc (cp_parser_check_class_key): Allow typename key for union
types and allow union keyword for typename types.
* pt.cc (tsubst) <case TYPENAME_TYPE>: Don't conflate unions with
class_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/template/error45.C: Adjust dg-error.
* g++.dg/warn/Wredundant-tags-3.C: Remove xfail.
* g++.dg/parse/union1.C: New test.
* g++.dg/parse/union2.C: New test.
* g++.dg/parse/union3.C: New test.
* g++.dg/parse/union4.C: New test.
* g++.dg/parse/union5.C: New test.
* g++.dg/parse/union6.C: New test.
Co-authored-by: Andrew Pinski <quic_apin...@quicinc.com>
---
gcc/cp/cp-tree.h | 12 ++++++++--
gcc/cp/decl.cc | 10 +++++----
gcc/cp/error.cc | 1 +
gcc/cp/module.cc | 2 ++
gcc/cp/parser.cc | 4 +++-
gcc/cp/pt.cc | 22 ++++++++++++-------
gcc/testsuite/g++.dg/parse/union1.C | 19 ++++++++++++++++
gcc/testsuite/g++.dg/parse/union2.C | 19 ++++++++++++++++
gcc/testsuite/g++.dg/parse/union3.C | 19 ++++++++++++++++
gcc/testsuite/g++.dg/parse/union4.C | 12 ++++++++++
gcc/testsuite/g++.dg/parse/union5.C | 5 +++++
gcc/testsuite/g++.dg/parse/union6.C | 5 +++++
gcc/testsuite/g++.dg/template/error45.C | 2 +-
gcc/testsuite/g++.dg/warn/Wredundant-tags-3.C | 2 +-
14 files changed, 117 insertions(+), 17 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/parse/union1.C
create mode 100644 gcc/testsuite/g++.dg/parse/union2.C
create mode 100644 gcc/testsuite/g++.dg/parse/union3.C
create mode 100644 gcc/testsuite/g++.dg/parse/union4.C
create mode 100644 gcc/testsuite/g++.dg/parse/union5.C
create mode 100644 gcc/testsuite/g++.dg/parse/union6.C
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 1b893e23543..3b92d9af6e1 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -506,6 +506,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
LAMBDA_EXPR_STATIC_P (in LAMBDA_EXPR)
TARGET_EXPR_ELIDING_P (in TARGET_EXPR)
contract_semantic (in ASSERTION_, PRECONDITION_, POSTCONDITION_STMT)
+ TYPENAME_IS_UNION_P (in TYPENAME_TYPE)
4: IDENTIFIER_MARKED (IDENTIFIER_NODEs)
TREE_HAS_CONSTRUCTOR (in INDIRECT_REF, SAVE_EXPR, CONSTRUCTOR,
CALL_EXPR, or FIELD_DECL).
@@ -2354,6 +2355,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_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)
@@ -4485,11 +4490,14 @@ get_vec_init_expr (tree t)
#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" or "struct". */
#define TYPENAME_IS_CLASS_P(NODE) \
(TREE_LANG_FLAG_1 (TYPENAME_TYPE_CHECK (NODE)))
+/* True if a TYPENAME_TYPE was declared as a "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)))
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index be26bd39b22..9b8994b922d 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -4370,6 +4370,7 @@ struct typename_info {
tree template_id;
bool enum_p;
bool class_p;
+ bool union_p;
};
struct typename_hasher : ggc_ptr_hash<tree_node>
@@ -4408,7 +4409,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);
}
};
@@ -4432,9 +4434,8 @@ build_typename_type (tree context, tree name, tree fullname,
ti.name = name;
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);
+ ti.class_p = (tag_type == class_type || tag_type == record_type);
+ ti.union_p = tag_type == union_type;
hashval_t hash = typename_hasher::hash (&ti);
/* See if we already have this type. */
@@ -4450,6 +4451,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.cc b/gcc/cp/error.cc
index abeb0285eec..eb2ff33ac30 100644
--- a/gcc/cp/error.cc
+++ b/gcc/cp/error.cc
@@ -810,6 +810,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 c8e79f33af4..6b5a60a3f3e 100644
--- a/gcc/cp/module.cc
+++ b/gcc/cp/module.cc
@@ -9619,6 +9619,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.cc b/gcc/cp/parser.cc
index 239e6f9a556..28907576d9d 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -35924,7 +35924,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))
{
auto_diagnostic_group d;
if (permerror (input_location, "%qs tag used in naming %q#T",
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 3362a6f8f9c..f57f89d06bb 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -17250,13 +17250,11 @@ tsubst (tree t, tree args, tsubst_flags_t complain,
tree in_decl)
return error_mark_node;
}
- /* FIXME: TYPENAME_IS_CLASS_P conflates 'class' vs 'struct' vs 'union'
- tags. TYPENAME_TYPE should probably remember the exact tag that
- was written. */