* [C++ PATCH] C++0x scoped enumerations
@ 2008-08-23 12:12 Doug Gregor
2008-08-23 16:54 ` Paolo Bonzini
0 siblings, 1 reply; 5+ messages in thread
From: Doug Gregor @ 2008-08-23 12:12 UTC (permalink / raw)
To: Gcc Patch List
[-- Attachment #1: Type: text/plain, Size: 3452 bytes --]
The attached patch implements support for C++0x scoped enumerations
(or, "strongly-typed" enumerations).
Most of the changes are relatively straightforward: fixed underlying
types, enumeration scope, parsing enum-names in
nested-name-specifiers, disabling implicit conversions to integers and
bool, etc. The only interesting change is that the TYPE_VALUES list
for an ENUMERAL_TYPE generated by the C++ front end now contains
CONST_DECL nodes for the values rather than INTEGER_CST nodes. So,
there are a few places in the common bits where we need to look
through the CONST_DECL to the DECL_INITIAL.
Tested i686-pc-linux-gnu, no regressions. Okay for mainline?
- Doug
2008-08-23 Douglas Gregor <doug.gregor@gmail.com>
* c-common.c (do_switch_warnings): Look through the CONST_DECLs in
the enumerators of an ENUMERAL_TYPE.
* dbxout.c (dbxout_type): Ditto.
2008-08-23 Douglas Gregor <doug.gregor@gmail.com>
* typeck.c (type_after_usual_arithmetic_conversions): Don't do the
usual arithmetic conversions on scoped enumeration types.
(common_type): Ditto.
(default_conversion): Don't perform integral promotions on scoped
enumeration types.
(build_array_ref): Scoped enumeration types can't be used as
subscripts.
* decl.c (start_enum): If building a C++0x scoped enumeration,
enter its scope. If provided with an underlying type, check that
underlying type and set up the enumeration type accordingly.
(finish_enum): Only compute an underlying type if the underlying
type isn't already fixed, and only convert the enumerator values
now if we've just computed the underlying type. Finish the scope
of C++0x scoped enumerations.
(build_enumerator): For enumerations with a fixed underlying type,
check the enumerator values when the enumerator is defined.
(lookup_enumerator): New.
* call.c (standard_conversion): Don't allow assignment from
integers to scoped enumeration types, even with -fpermissive.
Don't convert from scoped enumerations to bool or any arithmetic
types.
(build_conditional_expr): Don't per the usual arithmetic
conversions for scoped enumeration types.
(convert_like_real): Check complain to see if we should
produce warnings.
* error.c (class_key_or_enum_as_string): Print scoped enums.
* cp-tree.h (MAYBE_CLASS_TYPE_P): Check CLASS_TYPE_P, not
TYPE_LANG_FLAG_5.
(INTEGRAL_OR_UNSCOPED_ENUMERATION_TYPE_P): New.
(SCOPED_ENUM_P): New.
(UNSCOPED_ENUM_P): New.
(SET_SCOPED_ENUM_P): New.
(ENUM_UNDERLYING_TYPE): New.
* pt.c (lookup_template_class): Update the instantiation of enum
types to deal with C++0x scoped enumerations and underlying
types.
* name-lookup.c (begin_scope): Deal with scoped enumeration
scopes.
(lookup_qualified_name): Deal with lookup into enumeration types.
* name-lookup.h (enum scope_kind): Add sk_scoped_enum.
* parser.c (cp_parser_class_or_namespace_name): Rename to...
(cp_parser_qualifying_entity): ... this. Also, in C++0x mode,
parse a type-name that can be an enumeration type.
(cp_parser_nested_name_specifier_opt): Update with C++0x grammar.
(cp_parser_elaborated_type_specifier): Parse the
optional `struct' or `class' following enum (in C++0x).
(cp_parser_enum_specifier): Parse C++0x scoped enumerations and
enum-base clauses.
2008-08-23 Douglas Gregor <doug.gregor@gmail.com>
* g++.dg/cpp0x/scoped_enum_examples.C: New.
* g++.dg/cpp0x/scoped_enum.C: New.
* g++.dg/cpp0x/enum_base_warn.C: New.
* g++.dg/cpp0x/enum_base.C: New.
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: scoped-enum-3.patch --]
[-- Type: text/x-patch; name=scoped-enum-3.patch, Size: 45514 bytes --]
Index: c-common.c
===================================================================
--- c-common.c (revision 139506)
+++ c-common.c (working copy)
@@ -4928,6 +4928,8 @@ c_do_switch_warnings (splay_tree cases,
for (chain = TYPE_VALUES (type); chain; chain = TREE_CHAIN (chain))
{
tree value = TREE_VALUE (chain);
+ if (TREE_CODE (value) == CONST_DECL)
+ value = DECL_INITIAL (value);
node = splay_tree_lookup (cases, (splay_tree_key) value);
if (node)
{
Index: dbxout.c
===================================================================
--- dbxout.c (revision 139506)
+++ dbxout.c (working copy)
@@ -2174,16 +2174,21 @@ dbxout_type (tree type, int full)
stabstr_C ('e');
for (tem = TYPE_VALUES (type); tem; tem = TREE_CHAIN (tem))
{
+ tree value = TREE_VALUE (tem);
+
stabstr_I (TREE_PURPOSE (tem));
stabstr_C (':');
- if (TREE_INT_CST_HIGH (TREE_VALUE (tem)) == 0)
- stabstr_D (TREE_INT_CST_LOW (TREE_VALUE (tem)));
- else if (TREE_INT_CST_HIGH (TREE_VALUE (tem)) == -1
- && (HOST_WIDE_INT) TREE_INT_CST_LOW (TREE_VALUE (tem)) < 0)
- stabstr_D (TREE_INT_CST_LOW (TREE_VALUE (tem)));
+ if (TREE_CODE (value) == CONST_DECL)
+ value = DECL_INITIAL (value);
+
+ if (TREE_INT_CST_HIGH (value) == 0)
+ stabstr_D (TREE_INT_CST_LOW (value));
+ else if (TREE_INT_CST_HIGH (value) == -1
+ && (HOST_WIDE_INT) TREE_INT_CST_LOW (value) < 0)
+ stabstr_D (TREE_INT_CST_LOW (value));
else
- stabstr_O (TREE_VALUE (tem));
+ stabstr_O (value);
stabstr_C (',');
if (TREE_CHAIN (tem) != 0)
Index: cp/typeck.c
===================================================================
--- cp/typeck.c (revision 139506)
+++ cp/typeck.c (working copy)
@@ -262,10 +262,10 @@ type_after_usual_arithmetic_conversions
/* FIXME: Attributes. */
gcc_assert (ARITHMETIC_TYPE_P (t1)
|| TREE_CODE (t1) == VECTOR_TYPE
- || TREE_CODE (t1) == ENUMERAL_TYPE);
+ || UNSCOPED_ENUM_P (t1));
gcc_assert (ARITHMETIC_TYPE_P (t2)
|| TREE_CODE (t2) == VECTOR_TYPE
- || TREE_CODE (t2) == ENUMERAL_TYPE);
+ || UNSCOPED_ENUM_P (t2));
/* In what follows, we slightly generalize the rules given in [expr] so
as to deal with `long long' and `complex'. First, merge the
@@ -764,9 +764,9 @@ common_type (tree t1, tree t2)
code1 = TREE_CODE (t1);
code2 = TREE_CODE (t2);
- if ((ARITHMETIC_TYPE_P (t1) || code1 == ENUMERAL_TYPE
+ if ((ARITHMETIC_TYPE_P (t1) || UNSCOPED_ENUM_P (t1)
|| code1 == VECTOR_TYPE)
- && (ARITHMETIC_TYPE_P (t2) || code2 == ENUMERAL_TYPE
+ && (ARITHMETIC_TYPE_P (t2) || UNSCOPED_ENUM_P (t2)
|| code2 == VECTOR_TYPE))
return type_after_usual_arithmetic_conversions (t1, t2);
@@ -1666,7 +1666,7 @@ default_conversion (tree exp)
/* Perform the integral promotions first so that bitfield
expressions (which may promote to "int", even if the bitfield is
declared "unsigned") are promoted correctly. */
- if (INTEGRAL_OR_ENUMERATION_TYPE_P (TREE_TYPE (exp)))
+ if (INTEGRAL_OR_UNSCOPED_ENUMERATION_TYPE_P (TREE_TYPE (exp)))
exp = perform_integral_promotions (exp);
/* Perform the other conversions. */
exp = decay_conversion (exp);
@@ -2548,7 +2548,7 @@ build_array_ref (tree array, tree idx)
warn_array_subscript_with_type_char (idx);
- if (!INTEGRAL_OR_ENUMERATION_TYPE_P (TREE_TYPE (idx)))
+ if (!INTEGRAL_OR_UNSCOPED_ENUMERATION_TYPE_P (TREE_TYPE (idx)))
{
error ("array subscript is not an integer");
return error_mark_node;
Index: cp/decl.c
===================================================================
--- cp/decl.c (revision 139506)
+++ cp/decl.c (working copy)
@@ -10712,13 +10712,20 @@ xref_basetypes (tree ref, tree base_list
\f
/* Begin compiling the definition of an enumeration type.
- NAME is its name.
+ NAME is its name,
+
+ 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.
+
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)
+start_enum (tree name, tree underlying_type, bool scoped_enum_p)
{
tree enumtype;
@@ -10750,6 +10757,39 @@ start_enum (tree name)
enumtype = pushtag (name, enumtype, /*tag_scope=*/ts_current);
}
+ 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);
+ 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);
+ ENUM_UNDERLYING_TYPE (enumtype) = underlying_type;
+ }
+ else
+ error ("underlying type %<%T%> of %<%T%> must be an integral type",
+ underlying_type, enumtype);
+ }
+
return enumtype;
}
@@ -10762,9 +10802,9 @@ finish_enum (tree enumtype)
{
tree values;
tree decl;
- tree value;
tree minnode;
tree maxnode;
+ tree value;
tree t;
bool unsignedp;
bool use_short_enum;
@@ -10773,6 +10813,8 @@ finish_enum (tree enumtype)
int precision;
integer_type_kind itk;
tree underlying_type = NULL_TREE;
+ bool fixed_underlying_type_p
+ = ENUM_UNDERLYING_TYPE (enumtype) != NULL_TREE;
/* We built up the VALUES in reverse order. */
TYPE_VALUES (enumtype) = nreverse (TYPE_VALUES (enumtype));
@@ -10798,34 +10840,34 @@ finish_enum (tree enumtype)
minnode = maxnode = NULL_TREE;
for (values = TYPE_VALUES (enumtype);
- 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;
-
- /* 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;
- }
+ 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;
+
+ /* 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;
@@ -10839,46 +10881,63 @@ finish_enum (tree enumtype)
highprec = min_precision (maxnode, unsignedp);
precision = MAX (lowprec, highprec);
- /* Determine the underlying type of the enumeration.
+ if (!fixed_underlying_type_p)
+ {
+ /* Determine the underlying type of the enumeration.
- [dcl.enum]
+ [dcl.enum]
- The underlying type of an enumeration is an integral type that
- can represent all the enumerator values defined in the
- enumeration. It is implementation-defined which integral type is
- used as the underlying type for an enumeration except that the
- underlying type shall not be larger than int unless the value of
- an enumerator cannot fit in an int or unsigned int.
-
- We use "int" or an "unsigned int" as the underlying type, even if
- a smaller integral type would work, unless the user has
- explicitly requested that we use the smallest possible type. The
- user can request that for all enumerations with a command line
- flag, or for just one enumeration with an attribute. */
-
- use_short_enum = flag_short_enums
- || lookup_attribute ("packed", TYPE_ATTRIBUTES (enumtype));
-
- for (itk = (use_short_enum ? itk_char : itk_int);
- itk != itk_none;
- itk++)
- {
- underlying_type = integer_types[itk];
- if (TYPE_PRECISION (underlying_type) >= precision
- && TYPE_UNSIGNED (underlying_type) == unsignedp)
- break;
- }
- if (itk == itk_none)
- {
- /* DR 377
-
- IF no integral type can represent all the enumerator values, the
- enumeration is ill-formed. */
- error ("no integral type can represent all of the enumerator values "
- "for %qT", enumtype);
- precision = TYPE_PRECISION (long_long_integer_type_node);
- underlying_type = integer_types[itk_unsigned_long_long];
+ The underlying type of an enumeration is an integral type that
+ can represent all the enumerator values defined in the
+ enumeration. It is implementation-defined which integral type is
+ used as the underlying type for an enumeration except that the
+ underlying type shall not be larger than int unless the value of
+ an enumerator cannot fit in an int or unsigned int.
+
+ We use "int" or an "unsigned int" as the underlying type, even if
+ a smaller integral type would work, unless the user has
+ explicitly requested that we use the smallest possible type. The
+ user can request that for all enumerations with a command line
+ flag, or for just one enumeration with an attribute. */
+
+ use_short_enum = flag_short_enums
+ || lookup_attribute ("packed", TYPE_ATTRIBUTES (enumtype));
+
+ for (itk = (use_short_enum ? itk_char : itk_int);
+ itk != itk_none;
+ itk++)
+ {
+ underlying_type = integer_types[itk];
+ if (TYPE_PRECISION (underlying_type) >= precision
+ && TYPE_UNSIGNED (underlying_type) == unsignedp)
+ break;
+ }
+ if (itk == itk_none)
+ {
+ /* DR 377
+
+ IF no integral type can represent all the enumerator values, the
+ enumeration is ill-formed. */
+ error ("no integral type can represent all of the enumerator values "
+ "for %qT", enumtype);
+ precision = TYPE_PRECISION (long_long_integer_type_node);
+ underlying_type = integer_types[itk_unsigned_long_long];
+ }
+
+ /* [dcl.enum]
+
+ 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_SIZE (enumtype) = TYPE_SIZE (underlying_type);
+ TYPE_SIZE_UNIT (enumtype) = TYPE_SIZE_UNIT (underlying_type);
+ TYPE_MODE (enumtype) = TYPE_MODE (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);
}
+ else
+ underlying_type = ENUM_UNDERLYING_TYPE (enumtype);
/* Compute the minimum and maximum values for the type.
@@ -10889,28 +10948,16 @@ finish_enum (tree enumtype)
underlying type in the range bmin to bmax, where bmin and bmax are,
respectively, the smallest and largest values of the smallest bit-
field that can store emin and emax. */
-
+
/* The middle-end currently assumes that types with TYPE_PRECISION
narrower than their underlying type are suitably zero or sign
extended to fill their mode. g++ doesn't make these guarantees.
Until the middle-end can represent such paradoxical types, we
set the TYPE_PRECISION to the width of the underlying type. */
TYPE_PRECISION (enumtype) = TYPE_PRECISION (underlying_type);
-
+
set_min_and_max_values_for_integral_type (enumtype, precision, unsignedp);
-
- /* [dcl.enum]
-
- 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_SIZE (enumtype) = TYPE_SIZE (underlying_type);
- TYPE_SIZE_UNIT (enumtype) = TYPE_SIZE_UNIT (underlying_type);
- TYPE_MODE (enumtype) = TYPE_MODE (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);
-
+
/* Convert each of the enumerators to the type of the underlying
type of the enumeration. */
for (values = TYPE_VALUES (enumtype); values; values = TREE_CHAIN (values))
@@ -10920,9 +10967,14 @@ finish_enum (tree enumtype)
decl = TREE_VALUE (values);
saved_location = input_location;
input_location = DECL_SOURCE_LOCATION (decl);
- value = perform_implicit_conversion (underlying_type,
- DECL_INITIAL (decl),
- tf_warning_or_error);
+ if (fixed_underlying_type_p)
+ /* If the enumeration type has a fixed underlying type, we
+ already checked all of the enumerator values. */
+ value = DECL_INITIAL (decl);
+ else
+ value = perform_implicit_conversion (underlying_type,
+ DECL_INITIAL (decl),
+ tf_warning_or_error);
input_location = saved_location;
/* Do not clobber shared ints. */
@@ -10930,7 +10982,6 @@ finish_enum (tree enumtype)
TREE_TYPE (value) = enumtype;
DECL_INITIAL (decl) = value;
- TREE_VALUE (values) = value;
}
/* Fix up all variant types of this enum type. */
@@ -10946,8 +10997,13 @@ finish_enum (tree 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);
}
+ /* 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 ());
}
@@ -11024,24 +11080,46 @@ build_enumerator (tree name, tree value,
/* Remove no-op casts from the value. */
STRIP_TYPE_NOPS (value);
+
+ /* If the underlying type of the enum is fixed, check whether
+ the enumerator values fits in the underlying type. If it
+ does not fit, the program is ill-formed [C++0x dcl.enum]. */
+ if (ENUM_UNDERLYING_TYPE (enumtype)
+ && value
+ && TREE_CODE (value) == INTEGER_CST
+ && !int_fits_type_p (value, ENUM_UNDERLYING_TYPE (enumtype)))
+ {
+ error ("enumerator value %E is too large for underlying type %<%T%>",
+ value, ENUM_UNDERLYING_TYPE (enumtype));
+
+ /* Silently convert the value so that we can continue. */
+ value = perform_implicit_conversion (ENUM_UNDERLYING_TYPE (enumtype),
+ value, tf_none);
+ if (value == error_mark_node)
+ value = NULL_TREE;
+ }
}
/* C++ associates enums with global, function, or class declarations. */
context = current_scope ();
/* Build the actual enumeration constant. Note that the enumeration
- constants have the type of their initializers until the
- enumeration is complete:
-
- [ dcl.enum ]
-
- Following the closing brace of an enum-specifier, each enumer-
- ator has the type of its enumeration. Prior to the closing
- brace, the type of each enumerator is the type of its
- initializing value.
-
- In finish_enum we will reset the type. Of course, if we're
- processing a template, there may be no value. */
+ constants have the underlying type of the enum (if it is fixed)
+ or the type of their initializer (if the underlying type of the
+ enum is not fixed):
+
+ [ C++0x dcl.enum ]
+
+ If the underlying type is fixed, the type of each enumerator
+ prior to the closing brace is the underlying type; if the
+ initializing value of an enumerator cannot be represented by
+ the underlying type, the program is ill-formed. If the
+ underlying type is not fixed, the type of each enumerator is
+ the type of its initializing value.
+
+ If the underlying type is not fixed, it will be computed by
+ finish_enum and we will reset the type of this enumerator. Of
+ course, if we're processing a template, there may be no value. */
type = value ? TREE_TYPE (value) : NULL_TREE;
if (context && context == current_class_type)
@@ -11070,6 +11148,31 @@ build_enumerator (tree name, tree value,
TYPE_VALUES (enumtype) = tree_cons (name, decl, TYPE_VALUES (enumtype));
}
+/* Look for an enumerator with the given NAME within the enumeration
+ type ENUMTYPE. This routine is used primarily for qualified name
+ lookup into an enumerator in C++0x, e.g.,
+
+ enum class Color { Red, Green, Blue };
+
+ Color color = Color::Red;
+
+ Returns the value corresponding to the enumerator, or
+ NULL_TREE if no such enumerator was found. */
+tree
+lookup_enumerator (tree enumtype, tree name)
+{
+ tree e;
+ gcc_assert (enumtype && TREE_CODE (enumtype) == ENUMERAL_TYPE);
+
+ for (e = TYPE_VALUES (enumtype); e; e = TREE_CHAIN (e))
+ {
+ if (TREE_PURPOSE (e) == name)
+ return TREE_VALUE (e);
+ }
+
+ return NULL_TREE;
+}
+
\f
/* We're defining DECL. Make sure that it's type is OK. */
Index: cp/call.c
===================================================================
--- cp/call.c (revision 139506)
+++ cp/call.c (working copy)
@@ -771,7 +771,7 @@ standard_conversion (tree to, tree from,
conv = build_conv (ck_std, to, conv);
conv->bad_p = true;
}
- else if (tcode == ENUMERAL_TYPE && fcode == INTEGER_TYPE)
+ else if (UNSCOPED_ENUM_P (to) && fcode == INTEGER_TYPE)
{
/* For backwards brain damage compatibility, allow interconversion of
enums and integers with a pedwarn. */
@@ -896,10 +896,11 @@ standard_conversion (tree to, tree from,
{
/* [conv.bool]
- An rvalue of arithmetic, enumeration, pointer, or pointer to
- member type can be converted to an rvalue of type bool. */
+ An rvalue of arithmetic, unscoped enumeration, pointer, or
+ pointer to member type can be converted to an rvalue of type
+ bool. */
if (ARITHMETIC_TYPE_P (from)
- || fcode == ENUMERAL_TYPE
+ || UNSCOPED_ENUM_P (from)
|| fcode == POINTER_TYPE
|| TYPE_PTR_TO_MEMBER_P (from))
{
@@ -919,7 +920,8 @@ standard_conversion (tree to, tree from,
/* As an extension, allow conversion to complex type. */
else if (ARITHMETIC_TYPE_P (to))
{
- if (! (INTEGRAL_CODE_P (fcode) || fcode == REAL_TYPE))
+ if (! (INTEGRAL_CODE_P (fcode) || fcode == REAL_TYPE)
+ || SCOPED_ENUM_P (from))
return NULL;
conv = build_conv (ck_std, to, conv);
@@ -3702,9 +3704,9 @@ build_conditional_expr (tree arg1, tree
type; the usual arithmetic conversions are performed to bring
them to a common type, and the result is of that type. */
else if ((ARITHMETIC_TYPE_P (arg2_type)
- || TREE_CODE (arg2_type) == ENUMERAL_TYPE)
+ || UNSCOPED_ENUM_P (arg2_type))
&& (ARITHMETIC_TYPE_P (arg3_type)
- || TREE_CODE (arg3_type) == ENUMERAL_TYPE))
+ || UNSCOPED_ENUM_P (arg3_type)))
{
/* In this case, there is always a common type. */
result_type = type_after_usual_arithmetic_conversions (arg2_type,
@@ -4791,7 +4793,7 @@ convert_like_real (conversion *convs, tr
if (convs->check_narrowing)
check_narrowing (totype, expr);
- if (issue_conversion_warnings)
+ if (issue_conversion_warnings && (complain & tf_warning))
expr = convert_and_check (totype, expr);
else
expr = convert (totype, expr);
Index: cp/error.c
===================================================================
--- cp/error.c (revision 139506)
+++ cp/error.c (working copy)
@@ -447,8 +447,13 @@ dump_typename (tree t, int flags)
const char *
class_key_or_enum_as_string (tree t)
{
- if (TREE_CODE (t) == ENUMERAL_TYPE)
- return "enum";
+ if (TREE_CODE (t) == ENUMERAL_TYPE)
+ {
+ if (SCOPED_ENUM_P (t))
+ return "enum class";
+ else
+ return "enum";
+ }
else if (TREE_CODE (t) == UNION_TYPE)
return "union";
else if (TYPE_LANG_SPECIFIC (t) && CLASSTYPE_DECLARED_CLASS (t))
Index: cp/cp-tree.h
===================================================================
--- cp/cp-tree.h (revision 139506)
+++ cp/cp-tree.h (working copy)
@@ -116,7 +116,8 @@ extern void cp_cpp_error (cpp_reader *
2: Unused
3: TYPE_FOR_JAVA.
4: TYPE_HAS_NONTRIVIAL_DESTRUCTOR
- 5: CLASS_TYPE_P.
+ 5: CLASS_TYPE_P (in RECORD_TYPE and UNION_TYPE)
+ SCOPED_ENUM_P (in ENUMERAL_TYPE)
6: TYPE_DEPENDENT_P_VALID
Usage of DECL_LANG_FLAG_?:
@@ -980,7 +981,7 @@ enum languages { lang_c, lang_cplusplus,
|| TREE_CODE (T) == TYPEOF_TYPE \
|| TREE_CODE (T) == BOUND_TEMPLATE_TEMPLATE_PARM \
|| TREE_CODE (T) == DECLTYPE_TYPE \
- || TYPE_LANG_FLAG_5 (T))
+ || CLASS_TYPE_P (T))
/* Set CLASS_TYPE_P for T to VAL. T must be a class, struct, or
union type. */
@@ -2688,6 +2689,10 @@ more_aggr_init_expr_args_p (const aggr_i
#define INTEGRAL_OR_ENUMERATION_TYPE_P(TYPE) \
(TREE_CODE (TYPE) == ENUMERAL_TYPE || CP_INTEGRAL_TYPE_P (TYPE))
+/* Returns true if TYPE is an integral or unscoped enumeration type. */
+#define INTEGRAL_OR_UNSCOPED_ENUMERATION_TYPE_P(TYPE) \
+ (UNSCOPED_ENUM_P (TYPE) || CP_INTEGRAL_TYPE_P (TYPE))
+
/* [basic.fundamental]
Integral and floating types are collectively called arithmetic
@@ -2714,6 +2719,59 @@ more_aggr_init_expr_args_p (const aggr_i
|| TYPE_PTR_P (TYPE) \
|| TYPE_PTRMEMFUNC_P (TYPE))
+/* Determines whether this type is a C++0x scoped enumeration
+ type. Scoped enumerations types are introduced via "enum class" or
+ "enum struct", e.g.,
+
+ enum Color {
+ Red, Green, Blue
+ }
+
+ Scoped enumeration types are different from normal (unscoped)
+ enumeration types in several ways:
+
+ - The enumerators of a scoped enumeration type are only available
+ within the scope of the enumeration type and not the enclosing
+ scope. For example, the Red color can be referred to with
+ "Color::Red" but not "Red".
+
+ - Scoped enumerators and enumerations do not implicitly convert
+ to int.
+
+ - The underlying type of the enum is well-defined. */
+#define SCOPED_ENUM_P(TYPE) \
+ (TREE_CODE (TYPE) == ENUMERAL_TYPE && TYPE_LANG_FLAG_5 (TYPE))
+
+/* Determine whether this is an unscoped enumeration type. */
+#define UNSCOPED_ENUM_P(TYPE) \
+ (TREE_CODE (TYPE) == ENUMERAL_TYPE && !TYPE_LANG_FLAG_5 (TYPE))
+
+/* Set the flag indicating whether an ENUMERAL_TYPE is a C++0x scoped
+ enumeration type (1) or a normal (unscoped) enumeration type
+ (0). */
+#define SET_SCOPED_ENUM_P(TYPE, VAL) \
+ (TYPE_LANG_FLAG_5 (ENUMERAL_TYPE_CHECK (TYPE)) = (VAL))
+
+/* Returns the underlying type of the given enum type, which will be
+ an integral type. The underlying type is determined in different
+ ways, depending on the properties of the enum:
+
+ - In C++0x, the underlying type can be explicitly specified, e.g.,
+
+ enum E1 : char { ... } // underlying type is char
+
+ - In a C++0x scoped enumeration, the underlying type is int
+ unless otherwises specified:
+
+ enum class E2 { ... } // underlying type is int
+
+ - Otherwise, the underlying type is determined based on the
+ values of the enumerators. In this case, the
+ ENUM_UNDERLYING_TYPE will not be set until after the definition
+ of the enumeration is completed by finish_enum. */
+#define ENUM_UNDERLYING_TYPE(TYPE) \
+ TREE_TYPE (ENUMERAL_TYPE_CHECK (TYPE))
+
/* [dcl.init.aggr]
An aggregate is an array or a class with no user-declared
@@ -4272,9 +4330,10 @@ extern bool grok_op_properties (tree,
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);
+extern tree start_enum (tree, tree, bool);
extern void finish_enum (tree);
extern void build_enumerator (tree, tree, tree);
+extern tree lookup_enumerator (tree, tree);
extern void start_preparsed_function (tree, tree, int);
extern int start_function (cp_decl_specifier_seq *, const cp_declarator *, tree);
extern tree begin_function_body (void);
Index: cp/pt.c
===================================================================
--- cp/pt.c (revision 139506)
+++ cp/pt.c (working copy)
@@ -5836,14 +5836,20 @@ lookup_template_class (tree d1,
if (!is_partial_instantiation)
{
set_current_access_from_decl (TYPE_NAME (template_type));
- t = start_enum (TYPE_IDENTIFIER (template_type));
+ t = start_enum (TYPE_IDENTIFIER (template_type),
+ tsubst (ENUM_UNDERLYING_TYPE (template_type),
+ arglist, complain, in_decl),
+ SCOPED_ENUM_P (template_type));
}
else
- /* We don't want to call start_enum for this type, since
- the values for the enumeration constants may involve
- template parameters. And, no one should be interested
- in the enumeration constants for such a type. */
- t = make_node (ENUMERAL_TYPE);
+ {
+ /* We don't want to call start_enum for this type, since
+ the values for the enumeration constants may involve
+ template parameters. And, no one should be interested
+ in the enumeration constants for such a type. */
+ t = make_node (ENUMERAL_TYPE);
+ SET_SCOPED_ENUM_P (t, SCOPED_ENUM_P (template_type));
+ }
}
else
{
Index: cp/name-lookup.c
===================================================================
--- cp/name-lookup.c (revision 139506)
+++ cp/name-lookup.c (working copy)
@@ -1329,7 +1329,7 @@ push_binding_level (struct cp_binding_le
/* Create a new KIND scope and make it the top of the active scopes stack.
ENTITY is the scope of the associated C++ entity (namespace, class,
- function); it is NULL otherwise. */
+ function, C++0x enumeration); it is NULL otherwise. */
cxx_scope *
begin_scope (scope_kind kind, tree entity)
@@ -1364,6 +1364,7 @@ begin_scope (scope_kind kind, tree entit
case sk_catch:
case sk_for:
case sk_class:
+ case sk_scoped_enum:
case sk_function_parms:
case sk_omp:
scope->keep = keep_next_level_flag;
@@ -3853,6 +3854,8 @@ lookup_qualified_name (tree scope, tree
if (qualified_lookup_using_namespace (name, scope, &binding, flags))
t = binding.value;
}
+ else if (cxx_dialect != cxx98 && TREE_CODE (scope) == ENUMERAL_TYPE)
+ t = lookup_enumerator (scope, name);
else if (is_class_type (scope, complain))
t = lookup_member (scope, name, 2, is_type_p);
Index: cp/name-lookup.h
===================================================================
--- cp/name-lookup.h (revision 139506)
+++ cp/name-lookup.h (working copy)
@@ -113,6 +113,8 @@ typedef enum scope_kind {
for-init-statement. */
sk_function_parms, /* The scope containing function parameters. */
sk_class, /* The scope containing the members of a class. */
+ sk_scoped_enum, /* The scope containing the enumertors of a C++0x
+ scoped enumeration. */
sk_namespace, /* The scope containing the members of a
namespace, including the global scope. */
sk_template_parms, /* A scope for template parameters. */
Index: cp/parser.c
===================================================================
--- cp/parser.c (revision 139506)
+++ cp/parser.c (working copy)
@@ -1581,7 +1581,7 @@ static tree cp_parser_nested_name_specif
(cp_parser *, bool, bool, bool, bool);
static tree cp_parser_nested_name_specifier
(cp_parser *, bool, bool, bool, bool);
-static tree cp_parser_class_or_namespace_name
+static tree cp_parser_qualifying_entity
(cp_parser *, bool, bool, bool, bool, bool);
static tree cp_parser_postfix_expression
(cp_parser *, bool, bool, bool);
@@ -3932,10 +3932,16 @@ cp_parser_unqualified_id (cp_parser* par
/* Parse an (optional) nested-name-specifier.
- nested-name-specifier:
+ nested-name-specifier: [C++98]
class-or-namespace-name :: nested-name-specifier [opt]
class-or-namespace-name :: template nested-name-specifier [opt]
+ nested-name-specifier: [C++0x]
+ type-name ::
+ namespace-name ::
+ nested-name-specifier identifier ::
+ nested-name-specifier template [opt] simple-template-id ::
+
PARSER->SCOPE should be set appropriately before this function is
called. TYPENAME_KEYWORD_P is TRUE if the `typename' keyword is in
effect. TYPE_P is TRUE if we non-type bindings should be ignored
@@ -4010,7 +4016,7 @@ cp_parser_nested_name_specifier_opt (cp_
else
{
/* If the next token is not an identifier, then it is
- definitely not a class-or-namespace-name. */
+ definitely not a type-name or namespace-name. */
if (token->type != CPP_NAME)
break;
/* If the following token is neither a `<' (to begin a
@@ -4050,12 +4056,12 @@ cp_parser_nested_name_specifier_opt (cp_
/*only_current_p=*/false);
/* Parse the qualifying entity. */
new_scope
- = cp_parser_class_or_namespace_name (parser,
- typename_keyword_p,
- template_keyword_p,
- check_dependency_p,
- type_p,
- is_declaration);
+ = cp_parser_qualifying_entity (parser,
+ typename_keyword_p,
+ template_keyword_p,
+ check_dependency_p,
+ type_p,
+ is_declaration);
/* Look for the `::' token. */
cp_parser_require (parser, CPP_SCOPE, "%<::%>");
@@ -4107,10 +4113,14 @@ cp_parser_nested_name_specifier_opt (cp_
decl = error_mark_node;
}
else
- cp_parser_name_lookup_error
- (parser, token->u.value, decl,
- "is not a class or namespace",
- token->location);
+ {
+ const char* msg = "is not a class or namespace";
+ if (cxx_dialect != cxx98)
+ msg = "is not a class, namespace, or enumeration";
+ cp_parser_name_lookup_error
+ (parser, token->u.value, decl, msg,
+ token->location);
+ }
}
parser->scope = error_mark_node;
error_p = true;
@@ -4229,11 +4239,11 @@ cp_parser_nested_name_specifier (cp_pars
return scope;
}
-/* Parse a class-or-namespace-name.
-
- class-or-namespace-name:
- class-name
- namespace-name
+/* Parse the qualifying entity in a nested-namesspecifier. For C++98,
+ this is either a class-name or a namespace-name (which corresponds
+ to the class-or-namespace-name production in the grammar). For
+ C++0x, it can also be a type-name that refers to an enumeration
+ type.
TYPENAME_KEYWORD_P is TRUE iff the `typename' keyword is in effect.
TEMPLATE_KEYWORD_P is TRUE iff the `template' keyword is in effect.
@@ -4247,18 +4257,19 @@ cp_parser_nested_name_specifier (cp_pars
ERROR_MARK_NODE is returned. */
static tree
-cp_parser_class_or_namespace_name (cp_parser *parser,
- bool typename_keyword_p,
- bool template_keyword_p,
- bool check_dependency_p,
- bool type_p,
- bool is_declaration)
+cp_parser_qualifying_entity (cp_parser *parser,
+ bool typename_keyword_p,
+ bool template_keyword_p,
+ bool check_dependency_p,
+ bool type_p,
+ bool is_declaration)
{
tree saved_scope;
tree saved_qualifying_scope;
tree saved_object_scope;
tree scope;
bool only_class_p;
+ bool successful_parse_p;
/* Before we try to parse the class-name, we must save away the
current PARSER->SCOPE since cp_parser_class_name will destroy
@@ -4268,7 +4279,8 @@ cp_parser_class_or_namespace_name (cp_pa
saved_object_scope = parser->object_scope;
/* Try for a class-name first. If the SAVED_SCOPE is a type, then
there is no need to look for a namespace-name. */
- only_class_p = template_keyword_p || (saved_scope && TYPE_P (saved_scope));
+ only_class_p = template_keyword_p
+ || (saved_scope && TYPE_P (saved_scope) && cxx_dialect == cxx98);
if (!only_class_p)
cp_parser_parse_tentatively (parser);
scope = cp_parser_class_name (parser,
@@ -4278,8 +4290,26 @@ cp_parser_class_or_namespace_name (cp_pa
check_dependency_p,
/*class_head_p=*/false,
is_declaration);
+ successful_parse_p = only_class_p || cp_parser_parse_definitely (parser);
+ /* If that didn't work and we're in C++0x mode, try for a type-name. */
+ if (!only_class_p
+ && cxx_dialect != cxx98
+ && !successful_parse_p)
+ {
+ /* Restore the saved scope. */
+ parser->scope = saved_scope;
+ parser->qualifying_scope = saved_qualifying_scope;
+ parser->object_scope = saved_object_scope;
+
+ /* Parse tentatively. */
+ cp_parser_parse_tentatively (parser);
+
+ /* Parse a typedef-name or enum-name. */
+ scope = cp_parser_nonclass_name (parser);
+ successful_parse_p = cp_parser_parse_definitely (parser);
+ }
/* If that didn't work, try for a namespace-name. */
- if (!only_class_p && !cp_parser_parse_definitely (parser))
+ if (!only_class_p && !successful_parse_p)
{
/* Restore the saved scope. */
parser->scope = saved_scope;
@@ -11307,7 +11337,7 @@ cp_parser_nonclass_name (cp_parser* pars
elaborated-type-specifier:
class-key :: [opt] nested-name-specifier [opt] identifier
class-key :: [opt] nested-name-specifier [opt] template [opt] template-id
- enum :: [opt] nested-name-specifier [opt] identifier
+ enum-key :: [opt] nested-name-specifier [opt] identifier
typename :: [opt] nested-name-specifier identifier
typename :: [opt] nested-name-specifier template [opt]
template-id
@@ -11345,6 +11375,11 @@ cp_parser_elaborated_type_specifier (cp_
cp_lexer_consume_token (parser->lexer);
/* Remember that it's an enumeration type. */
tag_type = enum_type;
+ /* In C++0x, parse the optional `struct' or `class' key. */
+ if (cxx_dialect != cxx98
+ && (cp_lexer_next_token_is_keyword (parser->lexer, RID_CLASS)
+ || cp_lexer_next_token_is_keyword (parser->lexer, RID_STRUCT)))
+ cp_lexer_consume_token (parser->lexer);
/* Parse the attributes. */
attributes = cp_parser_attributes_opt (parser);
}
@@ -11632,11 +11667,19 @@ cp_parser_elaborated_type_specifier (cp_
/* Parse an enum-specifier.
enum-specifier:
- enum identifier [opt] { enumerator-list [opt] }
+ enum-key identifier [opt] enum-base [opt] { enumerator-list [opt] }
+
+ enum-key:
+ enum
+ enum class [C++0x]
+ enum struct [C++0x]
+
+ enum-base: [C++0x]
+ : type-specifier-seq
GNU Extensions:
- enum attributes[opt] identifier [opt] { enumerator-list [opt] }
- attributes[opt]
+ enum-key attributes[opt] identifier [opt] enum-base [opt]
+ { enumerator-list [opt] }attributes[opt]
Returns an ENUM_TYPE representing the enumeration, or NULL_TREE
if the token stream isn't an enum-specifier after all. */
@@ -11647,6 +11690,8 @@ cp_parser_enum_specifier (cp_parser* par
tree identifier;
tree type;
tree attributes;
+ bool scoped_enum_p = false;
+ tree underlying_type = NULL_TREE;
/* Parse tentatively so that we can back up if we don't find a
enum-specifier. */
@@ -11658,6 +11703,18 @@ cp_parser_enum_specifier (cp_parser* par
the enumeration being defined. */
cp_lexer_consume_token (parser->lexer);
+ /* In C++0x, check for "class" or "struct", which indicates a scoped
+ enumeration type. */
+ if ((cxx_dialect != cxx98)
+ && (cp_lexer_next_token_is_keyword (parser->lexer, RID_CLASS)
+ || cp_lexer_next_token_is_keyword (parser->lexer, RID_STRUCT)))
+ {
+ /* Consume the `struct' or `class' token. */
+ cp_lexer_consume_token (parser->lexer);
+
+ scoped_enum_p = true;
+ }
+
attributes = cp_parser_attributes_opt (parser);
if (cp_lexer_next_token_is (parser->lexer, CPP_NAME))
@@ -11665,6 +11722,34 @@ cp_parser_enum_specifier (cp_parser* par
else
identifier = make_anon_name ();
+ /* In C++0x, check for the `:' that denotes a specified underlying
+ type. */
+ if ((cxx_dialect != cxx98)
+ && cp_lexer_next_token_is (parser->lexer, CPP_COLON))
+ {
+ cp_decl_specifier_seq type_specifiers;
+
+ /* Consume the `:'. */
+ cp_lexer_consume_token (parser->lexer);
+
+ /* Parse the type-specifier-seq. */
+ cp_parser_type_specifier_seq (parser, /*is_condition=*/false,
+ &type_specifiers);
+ if (type_specifiers.type == error_mark_node)
+ return error_mark_node;
+
+ /* If that didn't work, stop. */
+ if (type_specifiers.type != error_mark_node)
+ {
+ underlying_type = grokdeclarator (NULL, &type_specifiers, TYPENAME,
+ /*initialized=*/0, NULL);
+ if (underlying_type == error_mark_node)
+ underlying_type = NULL_TREE;
+ }
+ else
+ cp_parser_error (parser, "expected underlying type of enumeration");
+ }
+
/* Look for the `{' but don't consume it yet. */
if (!cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
cp_parser_simulate_error (parser);
@@ -11679,7 +11764,7 @@ cp_parser_enum_specifier (cp_parser* par
/* 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);
+ type = start_enum (identifier, underlying_type, scoped_enum_p);
/* Consume the opening brace. */
cp_lexer_consume_token (parser->lexer);
@@ -11818,7 +11903,7 @@ cp_parser_namespace_name (cp_parser* par
During the lookup of a name preceding the :: scope resolution
operator, object, function, and enumerator names are ignored.
- (Note that cp_parser_class_or_namespace_name only calls this
+ (Note that cp_parser_qualifying_entity only calls this
function if the token after the name is the scope resolution
operator.) */
namespace_decl = cp_parser_lookup_name (parser, identifier,
Index: testsuite/g++.dg/cpp0x/scoped_enum_examples.C
===================================================================
--- testsuite/g++.dg/cpp0x/scoped_enum_examples.C (revision 0)
+++ testsuite/g++.dg/cpp0x/scoped_enum_examples.C (revision 0)
@@ -0,0 +1,27 @@
+// { dg-do compile }
+// { dg-options "-std=c++0x" }
+enum class Col { red, yellow, green };
+
+int x = Col::red; // { dg-error "cannot convert" }
+Col y = Col::red;
+
+void f()
+{
+ if (y) { } // { dg-error "could not convert" }
+}
+
+enum direction { left='l', right='r' };
+void g() {
+ // OK
+ direction d;
+ // OK
+ d = left;
+ // OK
+ d = direction::right;
+}
+enum class altitude { high='h', low='l' };
+void h() {
+ altitude a;
+ a = high; // { dg-error "not declared in this scope" }
+ a = altitude::low;
+}
Index: testsuite/g++.dg/cpp0x/scoped_enum.C
===================================================================
--- testsuite/g++.dg/cpp0x/scoped_enum.C (revision 0)
+++ testsuite/g++.dg/cpp0x/scoped_enum.C (revision 0)
@@ -0,0 +1,76 @@
+// { dg-options "-std=c++0x" }
+enum class Color1 {
+ Red,
+ Green,
+ Blue
+};
+
+enum struct Color2 {
+ Red, // { dg-error "previously declared here" }
+ Orange,
+ Yellow,
+ Green,
+ Blue,
+ Indigo = Green + 2,
+ Violet,
+ Red // { dg-error "redefinition" }
+};
+
+enum Color {
+ Red, Green, Blue
+};
+
+enum class Color3 {
+ Red
+};
+
+enum class Color color;
+enum Color3 color3;
+
+void f(int);
+void f2(Color3);
+
+void g()
+{
+ int i = 0;
+ f(color); // okay: unscoped enum
+ f(color3); // { dg-error "cannot convert" }
+ f2(color); // { dg-error "cannot convert" }
+ f2(color3);
+ f2(i); // { dg-error "cannot convert" }
+ i = color3; // { dg-error "cannot convert" }
+ color3 = i; // { dg-error "cannot convert" }
+ f(static_cast<int>(color3)); // okay
+
+ int a[5];
+ a[color3]; // { dg-error "array subscript is not an integer" }
+
+ bool b = color3; // { dg-error "cannot convert" }
+}
+
+void h()
+{
+ Color1 c1 = Color1::Red;
+ Color2 c2 = Color1::Red; // { dg-error "cannot convert" }
+ c2 = Color1::Red; // { dg-error "cannot convert" }
+
+ c2 = Color2::Red;
+ int c3 = Color::Red;
+}
+
+template<typename T, T value>
+struct constant { };
+
+template<typename T>
+int& sfinae(constant<T, T::Green>*);
+
+float& sfinae(void*);
+
+void sfinae_test()
+{
+ int& test1 = sfinae((constant<Color1, Color1::Green>*)0);
+ int& test2 = sfinae((constant<Color2, Color2::Green>*)0);
+ float& test3 = sfinae((constant<Color1, Color1::Red>*)0);
+ int& test4 = sfinae((constant<Color, Green>*)0);
+ float& test5 = sfinae((constant<Color, Red>*)0);
+}
Index: testsuite/g++.dg/cpp0x/enum_base_warn.C
===================================================================
--- testsuite/g++.dg/cpp0x/enum_base_warn.C (revision 0)
+++ testsuite/g++.dg/cpp0x/enum_base_warn.C (revision 0)
@@ -0,0 +1,25 @@
+// { dg-do run }
+// { dg-options "-O2 -Wtype-limits -std=c++0x" }
+extern void link_error (void);
+
+enum Alpha : unsigned char {
+ ZERO = 0, ONE, TWO, THREE
+};
+
+Alpha a2;
+
+int m1 = -1;
+int GetM1() {
+ return m1;
+}
+
+int main() {
+ a2 = static_cast<Alpha>(GetM1());
+ if (a2 == -1) { // { dg-warning "always false due" }
+ link_error ();
+ }
+ if (-1 == a2) { // { dg-warning "always false due" }
+ link_error ();
+ }
+ return 0;
+}
Index: testsuite/g++.dg/cpp0x/enum_base.C
===================================================================
--- testsuite/g++.dg/cpp0x/enum_base.C (revision 0)
+++ testsuite/g++.dg/cpp0x/enum_base.C (revision 0)
@@ -0,0 +1,25 @@
+// { dg-options "-std=c++0x" }
+
+typedef unsigned volatile long long uvlonglong;
+
+enum E1 : char { };
+enum E2 : signed const short { };
+enum E3 : uvlonglong { };
+enum E4 : char {
+ val = 500 // { dg-error "too large" }
+};
+
+enum class E5 {
+ val = (unsigned long long)-1 // { dg-error "too large" }
+};
+
+typedef float Float;
+
+enum class E6 : Float { }; // { dg-error "must be an integral type" }
+
+static_assert (sizeof(E1) == sizeof(char), "char-sized enum");
+static_assert (sizeof(E2) == sizeof(signed short), "short-sized enum");
+static_assert (sizeof(E3) == sizeof(unsigned long long),
+ "long long-sized enum");
+static_assert (sizeof(E4) == sizeof(char), "char-sized enum");
+static_assert (sizeof(E5) == sizeof(int), "scoped enum with int size");
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [C++ PATCH] C++0x scoped enumerations
2008-08-23 12:12 [C++ PATCH] C++0x scoped enumerations Doug Gregor
@ 2008-08-23 16:54 ` Paolo Bonzini
2008-08-23 17:15 ` Doug Gregor
0 siblings, 1 reply; 5+ messages in thread
From: Paolo Bonzini @ 2008-08-23 16:54 UTC (permalink / raw)
To: Doug Gregor; +Cc: Gcc Patch List
> +/* Determines whether this type is a C++0x scoped enumeration
> + type. Scoped enumerations types are introduced via "enum class" or
> + "enum struct", e.g.,
> +
> + enum Color {
> + Red, Green, Blue
> + }
I guess there is some inconsistency here. :-)
Paolo
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [C++ PATCH] C++0x scoped enumerations
2008-08-23 16:54 ` Paolo Bonzini
@ 2008-08-23 17:15 ` Doug Gregor
2008-08-27 15:55 ` Jason Merrill
0 siblings, 1 reply; 5+ messages in thread
From: Doug Gregor @ 2008-08-23 17:15 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: Gcc Patch List
[-- Attachment #1: Type: text/plain, Size: 542 bytes --]
On Sat, Aug 23, 2008 at 8:11 AM, Paolo Bonzini <bonzini@gnu.org> wrote:
>> +/* Determines whether this type is a C++0x scoped enumeration
>> + type. Scoped enumerations types are introduced via "enum class" or
>> + "enum struct", e.g.,
>> +
>> + enum Color {
>> + Red, Green, Blue
>> + }
>
> I guess there is some inconsistency here. :-)
Hah! Thanks.
Actually, I'll use this opportunity to clean up the value of
ENUM_UNDERLYING_TYPE for enumeration types without a fixed underlying
type. Updated patch attached.
- Doug
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: scoped-enum-4.patch --]
[-- Type: text/x-patch; name=scoped-enum-4.patch, Size: 45852 bytes --]
Index: c-common.c
===================================================================
--- c-common.c (revision 139516)
+++ c-common.c (working copy)
@@ -4928,6 +4928,8 @@ c_do_switch_warnings (splay_tree cases,
for (chain = TYPE_VALUES (type); chain; chain = TREE_CHAIN (chain))
{
tree value = TREE_VALUE (chain);
+ if (TREE_CODE (value) == CONST_DECL)
+ value = DECL_INITIAL (value);
node = splay_tree_lookup (cases, (splay_tree_key) value);
if (node)
{
Index: dbxout.c
===================================================================
--- dbxout.c (revision 139516)
+++ dbxout.c (working copy)
@@ -2174,16 +2174,21 @@ dbxout_type (tree type, int full)
stabstr_C ('e');
for (tem = TYPE_VALUES (type); tem; tem = TREE_CHAIN (tem))
{
+ tree value = TREE_VALUE (tem);
+
stabstr_I (TREE_PURPOSE (tem));
stabstr_C (':');
- if (TREE_INT_CST_HIGH (TREE_VALUE (tem)) == 0)
- stabstr_D (TREE_INT_CST_LOW (TREE_VALUE (tem)));
- else if (TREE_INT_CST_HIGH (TREE_VALUE (tem)) == -1
- && (HOST_WIDE_INT) TREE_INT_CST_LOW (TREE_VALUE (tem)) < 0)
- stabstr_D (TREE_INT_CST_LOW (TREE_VALUE (tem)));
+ if (TREE_CODE (value) == CONST_DECL)
+ value = DECL_INITIAL (value);
+
+ if (TREE_INT_CST_HIGH (value) == 0)
+ stabstr_D (TREE_INT_CST_LOW (value));
+ else if (TREE_INT_CST_HIGH (value) == -1
+ && (HOST_WIDE_INT) TREE_INT_CST_LOW (value) < 0)
+ stabstr_D (TREE_INT_CST_LOW (value));
else
- stabstr_O (TREE_VALUE (tem));
+ stabstr_O (value);
stabstr_C (',');
if (TREE_CHAIN (tem) != 0)
Index: cp/typeck.c
===================================================================
--- cp/typeck.c (revision 139516)
+++ cp/typeck.c (working copy)
@@ -262,10 +262,10 @@ type_after_usual_arithmetic_conversions
/* FIXME: Attributes. */
gcc_assert (ARITHMETIC_TYPE_P (t1)
|| TREE_CODE (t1) == VECTOR_TYPE
- || TREE_CODE (t1) == ENUMERAL_TYPE);
+ || UNSCOPED_ENUM_P (t1));
gcc_assert (ARITHMETIC_TYPE_P (t2)
|| TREE_CODE (t2) == VECTOR_TYPE
- || TREE_CODE (t2) == ENUMERAL_TYPE);
+ || UNSCOPED_ENUM_P (t2));
/* In what follows, we slightly generalize the rules given in [expr] so
as to deal with `long long' and `complex'. First, merge the
@@ -764,9 +764,9 @@ common_type (tree t1, tree t2)
code1 = TREE_CODE (t1);
code2 = TREE_CODE (t2);
- if ((ARITHMETIC_TYPE_P (t1) || code1 == ENUMERAL_TYPE
+ if ((ARITHMETIC_TYPE_P (t1) || UNSCOPED_ENUM_P (t1)
|| code1 == VECTOR_TYPE)
- && (ARITHMETIC_TYPE_P (t2) || code2 == ENUMERAL_TYPE
+ && (ARITHMETIC_TYPE_P (t2) || UNSCOPED_ENUM_P (t2)
|| code2 == VECTOR_TYPE))
return type_after_usual_arithmetic_conversions (t1, t2);
@@ -1666,7 +1666,7 @@ default_conversion (tree exp)
/* Perform the integral promotions first so that bitfield
expressions (which may promote to "int", even if the bitfield is
declared "unsigned") are promoted correctly. */
- if (INTEGRAL_OR_ENUMERATION_TYPE_P (TREE_TYPE (exp)))
+ if (INTEGRAL_OR_UNSCOPED_ENUMERATION_TYPE_P (TREE_TYPE (exp)))
exp = perform_integral_promotions (exp);
/* Perform the other conversions. */
exp = decay_conversion (exp);
@@ -2548,7 +2548,7 @@ build_array_ref (tree array, tree idx)
warn_array_subscript_with_type_char (idx);
- if (!INTEGRAL_OR_ENUMERATION_TYPE_P (TREE_TYPE (idx)))
+ if (!INTEGRAL_OR_UNSCOPED_ENUMERATION_TYPE_P (TREE_TYPE (idx)))
{
error ("array subscript is not an integer");
return error_mark_node;
Index: cp/decl.c
===================================================================
--- cp/decl.c (revision 139516)
+++ cp/decl.c (working copy)
@@ -10712,13 +10712,20 @@ xref_basetypes (tree ref, tree base_list
\f
/* Begin compiling the definition of an enumeration type.
- NAME is its name.
+ NAME is its name,
+
+ 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.
+
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)
+start_enum (tree name, tree underlying_type, bool scoped_enum_p)
{
tree enumtype;
@@ -10750,6 +10757,39 @@ start_enum (tree name)
enumtype = pushtag (name, enumtype, /*tag_scope=*/ts_current);
}
+ 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);
+ 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);
+ ENUM_UNDERLYING_TYPE (enumtype) = underlying_type;
+ }
+ else
+ error ("underlying type %<%T%> of %<%T%> must be an integral type",
+ underlying_type, enumtype);
+ }
+
return enumtype;
}
@@ -10762,9 +10802,9 @@ finish_enum (tree enumtype)
{
tree values;
tree decl;
- tree value;
tree minnode;
tree maxnode;
+ tree value;
tree t;
bool unsignedp;
bool use_short_enum;
@@ -10773,6 +10813,8 @@ finish_enum (tree enumtype)
int precision;
integer_type_kind itk;
tree underlying_type = NULL_TREE;
+ bool fixed_underlying_type_p
+ = ENUM_UNDERLYING_TYPE (enumtype) != NULL_TREE;
/* We built up the VALUES in reverse order. */
TYPE_VALUES (enumtype) = nreverse (TYPE_VALUES (enumtype));
@@ -10798,34 +10840,34 @@ finish_enum (tree enumtype)
minnode = maxnode = NULL_TREE;
for (values = TYPE_VALUES (enumtype);
- 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;
-
- /* 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;
- }
+ 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;
+
+ /* 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;
@@ -10839,46 +10881,70 @@ finish_enum (tree enumtype)
highprec = min_precision (maxnode, unsignedp);
precision = MAX (lowprec, highprec);
- /* Determine the underlying type of the enumeration.
+ if (!fixed_underlying_type_p)
+ {
+ /* Determine the underlying type of the enumeration.
- [dcl.enum]
+ [dcl.enum]
- The underlying type of an enumeration is an integral type that
- can represent all the enumerator values defined in the
- enumeration. It is implementation-defined which integral type is
- used as the underlying type for an enumeration except that the
- underlying type shall not be larger than int unless the value of
- an enumerator cannot fit in an int or unsigned int.
-
- We use "int" or an "unsigned int" as the underlying type, even if
- a smaller integral type would work, unless the user has
- explicitly requested that we use the smallest possible type. The
- user can request that for all enumerations with a command line
- flag, or for just one enumeration with an attribute. */
-
- use_short_enum = flag_short_enums
- || lookup_attribute ("packed", TYPE_ATTRIBUTES (enumtype));
-
- for (itk = (use_short_enum ? itk_char : itk_int);
- itk != itk_none;
- itk++)
- {
- underlying_type = integer_types[itk];
- if (TYPE_PRECISION (underlying_type) >= precision
- && TYPE_UNSIGNED (underlying_type) == unsignedp)
- break;
- }
- if (itk == itk_none)
- {
- /* DR 377
-
- IF no integral type can represent all the enumerator values, the
- enumeration is ill-formed. */
- error ("no integral type can represent all of the enumerator values "
- "for %qT", enumtype);
- precision = TYPE_PRECISION (long_long_integer_type_node);
- underlying_type = integer_types[itk_unsigned_long_long];
+ The underlying type of an enumeration is an integral type that
+ can represent all the enumerator values defined in the
+ enumeration. It is implementation-defined which integral type is
+ used as the underlying type for an enumeration except that the
+ underlying type shall not be larger than int unless the value of
+ an enumerator cannot fit in an int or unsigned int.
+
+ We use "int" or an "unsigned int" as the underlying type, even if
+ a smaller integral type would work, unless the user has
+ explicitly requested that we use the smallest possible type. The
+ user can request that for all enumerations with a command line
+ flag, or for just one enumeration with an attribute. */
+
+ use_short_enum = flag_short_enums
+ || lookup_attribute ("packed", TYPE_ATTRIBUTES (enumtype));
+
+ for (itk = (use_short_enum ? itk_char : itk_int);
+ itk != itk_none;
+ itk++)
+ {
+ underlying_type = integer_types[itk];
+ if (TYPE_PRECISION (underlying_type) >= precision
+ && TYPE_UNSIGNED (underlying_type) == unsignedp)
+ break;
+ }
+ if (itk == itk_none)
+ {
+ /* DR 377
+
+ IF no integral type can represent all the enumerator values, the
+ enumeration is ill-formed. */
+ error ("no integral type can represent all of the enumerator values "
+ "for %qT", enumtype);
+ precision = TYPE_PRECISION (long_long_integer_type_node);
+ underlying_type = integer_types[itk_unsigned_long_long];
+ }
+
+ /* [dcl.enum]
+
+ 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_SIZE (enumtype) = TYPE_SIZE (underlying_type);
+ TYPE_SIZE_UNIT (enumtype) = TYPE_SIZE_UNIT (underlying_type);
+ TYPE_MODE (enumtype) = TYPE_MODE (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);
+
+ /* Set the underlying type of the enumeration type to the
+ computed enumeration type, restricted to the enumerator
+ values. */
+ ENUM_UNDERLYING_TYPE (enumtype) = copy_node (underlying_type);
+ set_min_and_max_values_for_integral_type
+ (ENUM_UNDERLYING_TYPE (enumtype), precision, unsignedp);
}
+ else
+ underlying_type = ENUM_UNDERLYING_TYPE (enumtype);
/* Compute the minimum and maximum values for the type.
@@ -10889,28 +10955,16 @@ finish_enum (tree enumtype)
underlying type in the range bmin to bmax, where bmin and bmax are,
respectively, the smallest and largest values of the smallest bit-
field that can store emin and emax. */
-
+
/* The middle-end currently assumes that types with TYPE_PRECISION
narrower than their underlying type are suitably zero or sign
extended to fill their mode. g++ doesn't make these guarantees.
Until the middle-end can represent such paradoxical types, we
set the TYPE_PRECISION to the width of the underlying type. */
TYPE_PRECISION (enumtype) = TYPE_PRECISION (underlying_type);
-
+
set_min_and_max_values_for_integral_type (enumtype, precision, unsignedp);
-
- /* [dcl.enum]
-
- 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_SIZE (enumtype) = TYPE_SIZE (underlying_type);
- TYPE_SIZE_UNIT (enumtype) = TYPE_SIZE_UNIT (underlying_type);
- TYPE_MODE (enumtype) = TYPE_MODE (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);
-
+
/* Convert each of the enumerators to the type of the underlying
type of the enumeration. */
for (values = TYPE_VALUES (enumtype); values; values = TREE_CHAIN (values))
@@ -10920,9 +10974,14 @@ finish_enum (tree enumtype)
decl = TREE_VALUE (values);
saved_location = input_location;
input_location = DECL_SOURCE_LOCATION (decl);
- value = perform_implicit_conversion (underlying_type,
- DECL_INITIAL (decl),
- tf_warning_or_error);
+ if (fixed_underlying_type_p)
+ /* If the enumeration type has a fixed underlying type, we
+ already checked all of the enumerator values. */
+ value = DECL_INITIAL (decl);
+ else
+ value = perform_implicit_conversion (underlying_type,
+ DECL_INITIAL (decl),
+ tf_warning_or_error);
input_location = saved_location;
/* Do not clobber shared ints. */
@@ -10930,7 +10989,6 @@ finish_enum (tree enumtype)
TREE_TYPE (value) = enumtype;
DECL_INITIAL (decl) = value;
- TREE_VALUE (values) = value;
}
/* Fix up all variant types of this enum type. */
@@ -10946,8 +11004,13 @@ finish_enum (tree 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);
}
+ /* 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 ());
}
@@ -11024,24 +11087,46 @@ build_enumerator (tree name, tree value,
/* Remove no-op casts from the value. */
STRIP_TYPE_NOPS (value);
+
+ /* If the underlying type of the enum is fixed, check whether
+ the enumerator values fits in the underlying type. If it
+ does not fit, the program is ill-formed [C++0x dcl.enum]. */
+ if (ENUM_UNDERLYING_TYPE (enumtype)
+ && value
+ && TREE_CODE (value) == INTEGER_CST
+ && !int_fits_type_p (value, ENUM_UNDERLYING_TYPE (enumtype)))
+ {
+ error ("enumerator value %E is too large for underlying type %<%T%>",
+ value, ENUM_UNDERLYING_TYPE (enumtype));
+
+ /* Silently convert the value so that we can continue. */
+ value = perform_implicit_conversion (ENUM_UNDERLYING_TYPE (enumtype),
+ value, tf_none);
+ if (value == error_mark_node)
+ value = NULL_TREE;
+ }
}
/* C++ associates enums with global, function, or class declarations. */
context = current_scope ();
/* Build the actual enumeration constant. Note that the enumeration
- constants have the type of their initializers until the
- enumeration is complete:
-
- [ dcl.enum ]
-
- Following the closing brace of an enum-specifier, each enumer-
- ator has the type of its enumeration. Prior to the closing
- brace, the type of each enumerator is the type of its
- initializing value.
-
- In finish_enum we will reset the type. Of course, if we're
- processing a template, there may be no value. */
+ constants have the underlying type of the enum (if it is fixed)
+ or the type of their initializer (if the underlying type of the
+ enum is not fixed):
+
+ [ C++0x dcl.enum ]
+
+ If the underlying type is fixed, the type of each enumerator
+ prior to the closing brace is the underlying type; if the
+ initializing value of an enumerator cannot be represented by
+ the underlying type, the program is ill-formed. If the
+ underlying type is not fixed, the type of each enumerator is
+ the type of its initializing value.
+
+ If the underlying type is not fixed, it will be computed by
+ finish_enum and we will reset the type of this enumerator. Of
+ course, if we're processing a template, there may be no value. */
type = value ? TREE_TYPE (value) : NULL_TREE;
if (context && context == current_class_type)
@@ -11070,6 +11155,31 @@ build_enumerator (tree name, tree value,
TYPE_VALUES (enumtype) = tree_cons (name, decl, TYPE_VALUES (enumtype));
}
+/* Look for an enumerator with the given NAME within the enumeration
+ type ENUMTYPE. This routine is used primarily for qualified name
+ lookup into an enumerator in C++0x, e.g.,
+
+ enum class Color { Red, Green, Blue };
+
+ Color color = Color::Red;
+
+ Returns the value corresponding to the enumerator, or
+ NULL_TREE if no such enumerator was found. */
+tree
+lookup_enumerator (tree enumtype, tree name)
+{
+ tree e;
+ gcc_assert (enumtype && TREE_CODE (enumtype) == ENUMERAL_TYPE);
+
+ for (e = TYPE_VALUES (enumtype); e; e = TREE_CHAIN (e))
+ {
+ if (TREE_PURPOSE (e) == name)
+ return TREE_VALUE (e);
+ }
+
+ return NULL_TREE;
+}
+
\f
/* We're defining DECL. Make sure that it's type is OK. */
Index: cp/call.c
===================================================================
--- cp/call.c (revision 139516)
+++ cp/call.c (working copy)
@@ -771,7 +771,7 @@ standard_conversion (tree to, tree from,
conv = build_conv (ck_std, to, conv);
conv->bad_p = true;
}
- else if (tcode == ENUMERAL_TYPE && fcode == INTEGER_TYPE)
+ else if (UNSCOPED_ENUM_P (to) && fcode == INTEGER_TYPE)
{
/* For backwards brain damage compatibility, allow interconversion of
enums and integers with a pedwarn. */
@@ -896,10 +896,11 @@ standard_conversion (tree to, tree from,
{
/* [conv.bool]
- An rvalue of arithmetic, enumeration, pointer, or pointer to
- member type can be converted to an rvalue of type bool. */
+ An rvalue of arithmetic, unscoped enumeration, pointer, or
+ pointer to member type can be converted to an rvalue of type
+ bool. */
if (ARITHMETIC_TYPE_P (from)
- || fcode == ENUMERAL_TYPE
+ || UNSCOPED_ENUM_P (from)
|| fcode == POINTER_TYPE
|| TYPE_PTR_TO_MEMBER_P (from))
{
@@ -919,7 +920,8 @@ standard_conversion (tree to, tree from,
/* As an extension, allow conversion to complex type. */
else if (ARITHMETIC_TYPE_P (to))
{
- if (! (INTEGRAL_CODE_P (fcode) || fcode == REAL_TYPE))
+ if (! (INTEGRAL_CODE_P (fcode) || fcode == REAL_TYPE)
+ || SCOPED_ENUM_P (from))
return NULL;
conv = build_conv (ck_std, to, conv);
@@ -3702,9 +3704,9 @@ build_conditional_expr (tree arg1, tree
type; the usual arithmetic conversions are performed to bring
them to a common type, and the result is of that type. */
else if ((ARITHMETIC_TYPE_P (arg2_type)
- || TREE_CODE (arg2_type) == ENUMERAL_TYPE)
+ || UNSCOPED_ENUM_P (arg2_type))
&& (ARITHMETIC_TYPE_P (arg3_type)
- || TREE_CODE (arg3_type) == ENUMERAL_TYPE))
+ || UNSCOPED_ENUM_P (arg3_type)))
{
/* In this case, there is always a common type. */
result_type = type_after_usual_arithmetic_conversions (arg2_type,
@@ -4791,7 +4793,7 @@ convert_like_real (conversion *convs, tr
if (convs->check_narrowing)
check_narrowing (totype, expr);
- if (issue_conversion_warnings)
+ if (issue_conversion_warnings && (complain & tf_warning))
expr = convert_and_check (totype, expr);
else
expr = convert (totype, expr);
Index: cp/error.c
===================================================================
--- cp/error.c (revision 139516)
+++ cp/error.c (working copy)
@@ -447,8 +447,13 @@ dump_typename (tree t, int flags)
const char *
class_key_or_enum_as_string (tree t)
{
- if (TREE_CODE (t) == ENUMERAL_TYPE)
- return "enum";
+ if (TREE_CODE (t) == ENUMERAL_TYPE)
+ {
+ if (SCOPED_ENUM_P (t))
+ return "enum class";
+ else
+ return "enum";
+ }
else if (TREE_CODE (t) == UNION_TYPE)
return "union";
else if (TYPE_LANG_SPECIFIC (t) && CLASSTYPE_DECLARED_CLASS (t))
Index: cp/cp-tree.h
===================================================================
--- cp/cp-tree.h (revision 139516)
+++ cp/cp-tree.h (working copy)
@@ -116,7 +116,8 @@ extern void cp_cpp_error (cpp_reader *
2: Unused
3: TYPE_FOR_JAVA.
4: TYPE_HAS_NONTRIVIAL_DESTRUCTOR
- 5: CLASS_TYPE_P.
+ 5: CLASS_TYPE_P (in RECORD_TYPE and UNION_TYPE)
+ SCOPED_ENUM_P (in ENUMERAL_TYPE)
6: TYPE_DEPENDENT_P_VALID
Usage of DECL_LANG_FLAG_?:
@@ -980,7 +981,7 @@ enum languages { lang_c, lang_cplusplus,
|| TREE_CODE (T) == TYPEOF_TYPE \
|| TREE_CODE (T) == BOUND_TEMPLATE_TEMPLATE_PARM \
|| TREE_CODE (T) == DECLTYPE_TYPE \
- || TYPE_LANG_FLAG_5 (T))
+ || CLASS_TYPE_P (T))
/* Set CLASS_TYPE_P for T to VAL. T must be a class, struct, or
union type. */
@@ -2688,6 +2689,10 @@ more_aggr_init_expr_args_p (const aggr_i
#define INTEGRAL_OR_ENUMERATION_TYPE_P(TYPE) \
(TREE_CODE (TYPE) == ENUMERAL_TYPE || CP_INTEGRAL_TYPE_P (TYPE))
+/* Returns true if TYPE is an integral or unscoped enumeration type. */
+#define INTEGRAL_OR_UNSCOPED_ENUMERATION_TYPE_P(TYPE) \
+ (UNSCOPED_ENUM_P (TYPE) || CP_INTEGRAL_TYPE_P (TYPE))
+
/* [basic.fundamental]
Integral and floating types are collectively called arithmetic
@@ -2714,6 +2719,59 @@ more_aggr_init_expr_args_p (const aggr_i
|| TYPE_PTR_P (TYPE) \
|| TYPE_PTRMEMFUNC_P (TYPE))
+/* Determines whether this type is a C++0x scoped enumeration
+ type. Scoped enumerations types are introduced via "enum class" or
+ "enum struct", e.g.,
+
+ enum class Color {
+ Red, Green, Blue
+ };
+
+ Scoped enumeration types are different from normal (unscoped)
+ enumeration types in several ways:
+
+ - The enumerators of a scoped enumeration type are only available
+ within the scope of the enumeration type and not in the
+ enclosing scope. For example, the Red color can be referred to
+ with "Color::Red" but not "Red".
+
+ - Scoped enumerators and enumerations do not implicitly convert
+ to integers or 'bool'.
+
+ - The underlying type of the enum is well-defined. */
+#define SCOPED_ENUM_P(TYPE) \
+ (TREE_CODE (TYPE) == ENUMERAL_TYPE && TYPE_LANG_FLAG_5 (TYPE))
+
+/* Determine whether this is an unscoped enumeration type. */
+#define UNSCOPED_ENUM_P(TYPE) \
+ (TREE_CODE (TYPE) == ENUMERAL_TYPE && !TYPE_LANG_FLAG_5 (TYPE))
+
+/* Set the flag indicating whether an ENUMERAL_TYPE is a C++0x scoped
+ enumeration type (1) or a normal (unscoped) enumeration type
+ (0). */
+#define SET_SCOPED_ENUM_P(TYPE, VAL) \
+ (TYPE_LANG_FLAG_5 (ENUMERAL_TYPE_CHECK (TYPE)) = (VAL))
+
+/* Returns the underlying type of the given enumeration type. The
+ underlying type is determined in different ways, depending on the
+ properties of the enum:
+
+ - In C++0x, the underlying type can be explicitly specified, e.g.,
+
+ enum E1 : char { ... } // underlying type is char
+
+ - In a C++0x scoped enumeration, the underlying type is int
+ unless otherwises specified:
+
+ enum class E2 { ... } // underlying type is int
+
+ - Otherwise, the underlying type is determined based on the
+ values of the enumerators. In this case, the
+ ENUM_UNDERLYING_TYPE will not be set until after the definition
+ of the enumeration is completed by finish_enum. */
+#define ENUM_UNDERLYING_TYPE(TYPE) \
+ TREE_TYPE (ENUMERAL_TYPE_CHECK (TYPE))
+
/* [dcl.init.aggr]
An aggregate is an array or a class with no user-declared
@@ -4272,9 +4330,10 @@ extern bool grok_op_properties (tree,
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);
+extern tree start_enum (tree, tree, bool);
extern void finish_enum (tree);
extern void build_enumerator (tree, tree, tree);
+extern tree lookup_enumerator (tree, tree);
extern void start_preparsed_function (tree, tree, int);
extern int start_function (cp_decl_specifier_seq *, const cp_declarator *, tree);
extern tree begin_function_body (void);
Index: cp/pt.c
===================================================================
--- cp/pt.c (revision 139516)
+++ cp/pt.c (working copy)
@@ -5836,14 +5836,20 @@ lookup_template_class (tree d1,
if (!is_partial_instantiation)
{
set_current_access_from_decl (TYPE_NAME (template_type));
- t = start_enum (TYPE_IDENTIFIER (template_type));
+ t = start_enum (TYPE_IDENTIFIER (template_type),
+ tsubst (ENUM_UNDERLYING_TYPE (template_type),
+ arglist, complain, in_decl),
+ SCOPED_ENUM_P (template_type));
}
else
- /* We don't want to call start_enum for this type, since
- the values for the enumeration constants may involve
- template parameters. And, no one should be interested
- in the enumeration constants for such a type. */
- t = make_node (ENUMERAL_TYPE);
+ {
+ /* We don't want to call start_enum for this type, since
+ the values for the enumeration constants may involve
+ template parameters. And, no one should be interested
+ in the enumeration constants for such a type. */
+ t = make_node (ENUMERAL_TYPE);
+ SET_SCOPED_ENUM_P (t, SCOPED_ENUM_P (template_type));
+ }
}
else
{
Index: cp/name-lookup.c
===================================================================
--- cp/name-lookup.c (revision 139516)
+++ cp/name-lookup.c (working copy)
@@ -1329,7 +1329,7 @@ push_binding_level (struct cp_binding_le
/* Create a new KIND scope and make it the top of the active scopes stack.
ENTITY is the scope of the associated C++ entity (namespace, class,
- function); it is NULL otherwise. */
+ function, C++0x enumeration); it is NULL otherwise. */
cxx_scope *
begin_scope (scope_kind kind, tree entity)
@@ -1364,6 +1364,7 @@ begin_scope (scope_kind kind, tree entit
case sk_catch:
case sk_for:
case sk_class:
+ case sk_scoped_enum:
case sk_function_parms:
case sk_omp:
scope->keep = keep_next_level_flag;
@@ -3853,6 +3854,8 @@ lookup_qualified_name (tree scope, tree
if (qualified_lookup_using_namespace (name, scope, &binding, flags))
t = binding.value;
}
+ else if (cxx_dialect != cxx98 && TREE_CODE (scope) == ENUMERAL_TYPE)
+ t = lookup_enumerator (scope, name);
else if (is_class_type (scope, complain))
t = lookup_member (scope, name, 2, is_type_p);
Index: cp/name-lookup.h
===================================================================
--- cp/name-lookup.h (revision 139516)
+++ cp/name-lookup.h (working copy)
@@ -113,6 +113,8 @@ typedef enum scope_kind {
for-init-statement. */
sk_function_parms, /* The scope containing function parameters. */
sk_class, /* The scope containing the members of a class. */
+ sk_scoped_enum, /* The scope containing the enumertors of a C++0x
+ scoped enumeration. */
sk_namespace, /* The scope containing the members of a
namespace, including the global scope. */
sk_template_parms, /* A scope for template parameters. */
Index: cp/parser.c
===================================================================
--- cp/parser.c (revision 139516)
+++ cp/parser.c (working copy)
@@ -1581,7 +1581,7 @@ static tree cp_parser_nested_name_specif
(cp_parser *, bool, bool, bool, bool);
static tree cp_parser_nested_name_specifier
(cp_parser *, bool, bool, bool, bool);
-static tree cp_parser_class_or_namespace_name
+static tree cp_parser_qualifying_entity
(cp_parser *, bool, bool, bool, bool, bool);
static tree cp_parser_postfix_expression
(cp_parser *, bool, bool, bool);
@@ -3932,10 +3932,16 @@ cp_parser_unqualified_id (cp_parser* par
/* Parse an (optional) nested-name-specifier.
- nested-name-specifier:
+ nested-name-specifier: [C++98]
class-or-namespace-name :: nested-name-specifier [opt]
class-or-namespace-name :: template nested-name-specifier [opt]
+ nested-name-specifier: [C++0x]
+ type-name ::
+ namespace-name ::
+ nested-name-specifier identifier ::
+ nested-name-specifier template [opt] simple-template-id ::
+
PARSER->SCOPE should be set appropriately before this function is
called. TYPENAME_KEYWORD_P is TRUE if the `typename' keyword is in
effect. TYPE_P is TRUE if we non-type bindings should be ignored
@@ -4010,7 +4016,7 @@ cp_parser_nested_name_specifier_opt (cp_
else
{
/* If the next token is not an identifier, then it is
- definitely not a class-or-namespace-name. */
+ definitely not a type-name or namespace-name. */
if (token->type != CPP_NAME)
break;
/* If the following token is neither a `<' (to begin a
@@ -4050,12 +4056,12 @@ cp_parser_nested_name_specifier_opt (cp_
/*only_current_p=*/false);
/* Parse the qualifying entity. */
new_scope
- = cp_parser_class_or_namespace_name (parser,
- typename_keyword_p,
- template_keyword_p,
- check_dependency_p,
- type_p,
- is_declaration);
+ = cp_parser_qualifying_entity (parser,
+ typename_keyword_p,
+ template_keyword_p,
+ check_dependency_p,
+ type_p,
+ is_declaration);
/* Look for the `::' token. */
cp_parser_require (parser, CPP_SCOPE, "%<::%>");
@@ -4107,10 +4113,14 @@ cp_parser_nested_name_specifier_opt (cp_
decl = error_mark_node;
}
else
- cp_parser_name_lookup_error
- (parser, token->u.value, decl,
- "is not a class or namespace",
- token->location);
+ {
+ const char* msg = "is not a class or namespace";
+ if (cxx_dialect != cxx98)
+ msg = "is not a class, namespace, or enumeration";
+ cp_parser_name_lookup_error
+ (parser, token->u.value, decl, msg,
+ token->location);
+ }
}
parser->scope = error_mark_node;
error_p = true;
@@ -4229,11 +4239,11 @@ cp_parser_nested_name_specifier (cp_pars
return scope;
}
-/* Parse a class-or-namespace-name.
-
- class-or-namespace-name:
- class-name
- namespace-name
+/* Parse the qualifying entity in a nested-namesspecifier. For C++98,
+ this is either a class-name or a namespace-name (which corresponds
+ to the class-or-namespace-name production in the grammar). For
+ C++0x, it can also be a type-name that refers to an enumeration
+ type.
TYPENAME_KEYWORD_P is TRUE iff the `typename' keyword is in effect.
TEMPLATE_KEYWORD_P is TRUE iff the `template' keyword is in effect.
@@ -4247,18 +4257,19 @@ cp_parser_nested_name_specifier (cp_pars
ERROR_MARK_NODE is returned. */
static tree
-cp_parser_class_or_namespace_name (cp_parser *parser,
- bool typename_keyword_p,
- bool template_keyword_p,
- bool check_dependency_p,
- bool type_p,
- bool is_declaration)
+cp_parser_qualifying_entity (cp_parser *parser,
+ bool typename_keyword_p,
+ bool template_keyword_p,
+ bool check_dependency_p,
+ bool type_p,
+ bool is_declaration)
{
tree saved_scope;
tree saved_qualifying_scope;
tree saved_object_scope;
tree scope;
bool only_class_p;
+ bool successful_parse_p;
/* Before we try to parse the class-name, we must save away the
current PARSER->SCOPE since cp_parser_class_name will destroy
@@ -4268,7 +4279,8 @@ cp_parser_class_or_namespace_name (cp_pa
saved_object_scope = parser->object_scope;
/* Try for a class-name first. If the SAVED_SCOPE is a type, then
there is no need to look for a namespace-name. */
- only_class_p = template_keyword_p || (saved_scope && TYPE_P (saved_scope));
+ only_class_p = template_keyword_p
+ || (saved_scope && TYPE_P (saved_scope) && cxx_dialect == cxx98);
if (!only_class_p)
cp_parser_parse_tentatively (parser);
scope = cp_parser_class_name (parser,
@@ -4278,8 +4290,26 @@ cp_parser_class_or_namespace_name (cp_pa
check_dependency_p,
/*class_head_p=*/false,
is_declaration);
+ successful_parse_p = only_class_p || cp_parser_parse_definitely (parser);
+ /* If that didn't work and we're in C++0x mode, try for a type-name. */
+ if (!only_class_p
+ && cxx_dialect != cxx98
+ && !successful_parse_p)
+ {
+ /* Restore the saved scope. */
+ parser->scope = saved_scope;
+ parser->qualifying_scope = saved_qualifying_scope;
+ parser->object_scope = saved_object_scope;
+
+ /* Parse tentatively. */
+ cp_parser_parse_tentatively (parser);
+
+ /* Parse a typedef-name or enum-name. */
+ scope = cp_parser_nonclass_name (parser);
+ successful_parse_p = cp_parser_parse_definitely (parser);
+ }
/* If that didn't work, try for a namespace-name. */
- if (!only_class_p && !cp_parser_parse_definitely (parser))
+ if (!only_class_p && !successful_parse_p)
{
/* Restore the saved scope. */
parser->scope = saved_scope;
@@ -11307,7 +11337,7 @@ cp_parser_nonclass_name (cp_parser* pars
elaborated-type-specifier:
class-key :: [opt] nested-name-specifier [opt] identifier
class-key :: [opt] nested-name-specifier [opt] template [opt] template-id
- enum :: [opt] nested-name-specifier [opt] identifier
+ enum-key :: [opt] nested-name-specifier [opt] identifier
typename :: [opt] nested-name-specifier identifier
typename :: [opt] nested-name-specifier template [opt]
template-id
@@ -11345,6 +11375,11 @@ cp_parser_elaborated_type_specifier (cp_
cp_lexer_consume_token (parser->lexer);
/* Remember that it's an enumeration type. */
tag_type = enum_type;
+ /* In C++0x, parse the optional `struct' or `class' key. */
+ if (cxx_dialect != cxx98
+ && (cp_lexer_next_token_is_keyword (parser->lexer, RID_CLASS)
+ || cp_lexer_next_token_is_keyword (parser->lexer, RID_STRUCT)))
+ cp_lexer_consume_token (parser->lexer);
/* Parse the attributes. */
attributes = cp_parser_attributes_opt (parser);
}
@@ -11632,11 +11667,19 @@ cp_parser_elaborated_type_specifier (cp_
/* Parse an enum-specifier.
enum-specifier:
- enum identifier [opt] { enumerator-list [opt] }
+ enum-key identifier [opt] enum-base [opt] { enumerator-list [opt] }
+
+ enum-key:
+ enum
+ enum class [C++0x]
+ enum struct [C++0x]
+
+ enum-base: [C++0x]
+ : type-specifier-seq
GNU Extensions:
- enum attributes[opt] identifier [opt] { enumerator-list [opt] }
- attributes[opt]
+ enum-key attributes[opt] identifier [opt] enum-base [opt]
+ { enumerator-list [opt] }attributes[opt]
Returns an ENUM_TYPE representing the enumeration, or NULL_TREE
if the token stream isn't an enum-specifier after all. */
@@ -11647,6 +11690,8 @@ cp_parser_enum_specifier (cp_parser* par
tree identifier;
tree type;
tree attributes;
+ bool scoped_enum_p = false;
+ tree underlying_type = NULL_TREE;
/* Parse tentatively so that we can back up if we don't find a
enum-specifier. */
@@ -11658,6 +11703,18 @@ cp_parser_enum_specifier (cp_parser* par
the enumeration being defined. */
cp_lexer_consume_token (parser->lexer);
+ /* In C++0x, check for "class" or "struct", which indicates a scoped
+ enumeration type. */
+ if ((cxx_dialect != cxx98)
+ && (cp_lexer_next_token_is_keyword (parser->lexer, RID_CLASS)
+ || cp_lexer_next_token_is_keyword (parser->lexer, RID_STRUCT)))
+ {
+ /* Consume the `struct' or `class' token. */
+ cp_lexer_consume_token (parser->lexer);
+
+ scoped_enum_p = true;
+ }
+
attributes = cp_parser_attributes_opt (parser);
if (cp_lexer_next_token_is (parser->lexer, CPP_NAME))
@@ -11665,6 +11722,34 @@ cp_parser_enum_specifier (cp_parser* par
else
identifier = make_anon_name ();
+ /* In C++0x, check for the `:' that denotes a specified underlying
+ type. */
+ if ((cxx_dialect != cxx98)
+ && cp_lexer_next_token_is (parser->lexer, CPP_COLON))
+ {
+ cp_decl_specifier_seq type_specifiers;
+
+ /* Consume the `:'. */
+ cp_lexer_consume_token (parser->lexer);
+
+ /* Parse the type-specifier-seq. */
+ cp_parser_type_specifier_seq (parser, /*is_condition=*/false,
+ &type_specifiers);
+ if (type_specifiers.type == error_mark_node)
+ return error_mark_node;
+
+ /* If that didn't work, stop. */
+ if (type_specifiers.type != error_mark_node)
+ {
+ underlying_type = grokdeclarator (NULL, &type_specifiers, TYPENAME,
+ /*initialized=*/0, NULL);
+ if (underlying_type == error_mark_node)
+ underlying_type = NULL_TREE;
+ }
+ else
+ cp_parser_error (parser, "expected underlying type of enumeration");
+ }
+
/* Look for the `{' but don't consume it yet. */
if (!cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
cp_parser_simulate_error (parser);
@@ -11679,7 +11764,7 @@ cp_parser_enum_specifier (cp_parser* par
/* 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);
+ type = start_enum (identifier, underlying_type, scoped_enum_p);
/* Consume the opening brace. */
cp_lexer_consume_token (parser->lexer);
@@ -11818,7 +11903,7 @@ cp_parser_namespace_name (cp_parser* par
During the lookup of a name preceding the :: scope resolution
operator, object, function, and enumerator names are ignored.
- (Note that cp_parser_class_or_namespace_name only calls this
+ (Note that cp_parser_qualifying_entity only calls this
function if the token after the name is the scope resolution
operator.) */
namespace_decl = cp_parser_lookup_name (parser, identifier,
Index: testsuite/g++.dg/cpp0x/scoped_enum_examples.C
===================================================================
--- testsuite/g++.dg/cpp0x/scoped_enum_examples.C (revision 0)
+++ testsuite/g++.dg/cpp0x/scoped_enum_examples.C (revision 0)
@@ -0,0 +1,27 @@
+// { dg-do compile }
+// { dg-options "-std=c++0x" }
+enum class Col { red, yellow, green };
+
+int x = Col::red; // { dg-error "cannot convert" }
+Col y = Col::red;
+
+void f()
+{
+ if (y) { } // { dg-error "could not convert" }
+}
+
+enum direction { left='l', right='r' };
+void g() {
+ // OK
+ direction d;
+ // OK
+ d = left;
+ // OK
+ d = direction::right;
+}
+enum class altitude { high='h', low='l' };
+void h() {
+ altitude a;
+ a = high; // { dg-error "not declared in this scope" }
+ a = altitude::low;
+}
Index: testsuite/g++.dg/cpp0x/scoped_enum.C
===================================================================
--- testsuite/g++.dg/cpp0x/scoped_enum.C (revision 0)
+++ testsuite/g++.dg/cpp0x/scoped_enum.C (revision 0)
@@ -0,0 +1,76 @@
+// { dg-options "-std=c++0x" }
+enum class Color1 {
+ Red,
+ Green,
+ Blue
+};
+
+enum struct Color2 {
+ Red, // { dg-error "previously declared here" }
+ Orange,
+ Yellow,
+ Green,
+ Blue,
+ Indigo = Green + 2,
+ Violet,
+ Red // { dg-error "redefinition" }
+};
+
+enum Color {
+ Red, Green, Blue
+};
+
+enum class Color3 {
+ Red
+};
+
+enum class Color color;
+enum Color3 color3;
+
+void f(int);
+void f2(Color3);
+
+void g()
+{
+ int i = 0;
+ f(color); // okay: unscoped enum
+ f(color3); // { dg-error "cannot convert" }
+ f2(color); // { dg-error "cannot convert" }
+ f2(color3);
+ f2(i); // { dg-error "cannot convert" }
+ i = color3; // { dg-error "cannot convert" }
+ color3 = i; // { dg-error "cannot convert" }
+ f(static_cast<int>(color3)); // okay
+
+ int a[5];
+ a[color3]; // { dg-error "array subscript is not an integer" }
+
+ bool b = color3; // { dg-error "cannot convert" }
+}
+
+void h()
+{
+ Color1 c1 = Color1::Red;
+ Color2 c2 = Color1::Red; // { dg-error "cannot convert" }
+ c2 = Color1::Red; // { dg-error "cannot convert" }
+
+ c2 = Color2::Red;
+ int c3 = Color::Red;
+}
+
+template<typename T, T value>
+struct constant { };
+
+template<typename T>
+int& sfinae(constant<T, T::Green>*);
+
+float& sfinae(void*);
+
+void sfinae_test()
+{
+ int& test1 = sfinae((constant<Color1, Color1::Green>*)0);
+ int& test2 = sfinae((constant<Color2, Color2::Green>*)0);
+ float& test3 = sfinae((constant<Color1, Color1::Red>*)0);
+ int& test4 = sfinae((constant<Color, Green>*)0);
+ float& test5 = sfinae((constant<Color, Red>*)0);
+}
Index: testsuite/g++.dg/cpp0x/enum_base_warn.C
===================================================================
--- testsuite/g++.dg/cpp0x/enum_base_warn.C (revision 0)
+++ testsuite/g++.dg/cpp0x/enum_base_warn.C (revision 0)
@@ -0,0 +1,25 @@
+// { dg-do run }
+// { dg-options "-O2 -Wtype-limits -std=c++0x" }
+extern void link_error (void);
+
+enum Alpha : unsigned char {
+ ZERO = 0, ONE, TWO, THREE
+};
+
+Alpha a2;
+
+int m1 = -1;
+int GetM1() {
+ return m1;
+}
+
+int main() {
+ a2 = static_cast<Alpha>(GetM1());
+ if (a2 == -1) { // { dg-warning "always false due" }
+ link_error ();
+ }
+ if (-1 == a2) { // { dg-warning "always false due" }
+ link_error ();
+ }
+ return 0;
+}
Index: testsuite/g++.dg/cpp0x/enum_base.C
===================================================================
--- testsuite/g++.dg/cpp0x/enum_base.C (revision 0)
+++ testsuite/g++.dg/cpp0x/enum_base.C (revision 0)
@@ -0,0 +1,25 @@
+// { dg-options "-std=c++0x" }
+
+typedef unsigned volatile long long uvlonglong;
+
+enum E1 : char { };
+enum E2 : signed const short { };
+enum E3 : uvlonglong { };
+enum E4 : char {
+ val = 500 // { dg-error "too large" }
+};
+
+enum class E5 {
+ val = (unsigned long long)-1 // { dg-error "too large" }
+};
+
+typedef float Float;
+
+enum class E6 : Float { }; // { dg-error "must be an integral type" }
+
+static_assert (sizeof(E1) == sizeof(char), "char-sized enum");
+static_assert (sizeof(E2) == sizeof(signed short), "short-sized enum");
+static_assert (sizeof(E3) == sizeof(unsigned long long),
+ "long long-sized enum");
+static_assert (sizeof(E4) == sizeof(char), "char-sized enum");
+static_assert (sizeof(E5) == sizeof(int), "scoped enum with int size");
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [C++ PATCH] C++0x scoped enumerations
2008-08-23 17:15 ` Doug Gregor
@ 2008-08-27 15:55 ` Jason Merrill
2008-08-28 2:52 ` Doug Gregor
0 siblings, 1 reply; 5+ messages in thread
From: Jason Merrill @ 2008-08-27 15:55 UTC (permalink / raw)
To: Doug Gregor; +Cc: Paolo Bonzini, Gcc Patch List
> +lookup_enumerator (tree enumtype, tree name)
> +{
> + tree e;
> + gcc_assert (enumtype && TREE_CODE (enumtype) == ENUMERAL_TYPE);
> +
> + for (e = TYPE_VALUES (enumtype); e; e = TREE_CHAIN (e))
> + {
> + if (TREE_PURPOSE (e) == name)
> + return TREE_VALUE (e);
> + }
Please use purpose_member here.
> +/* Parse the qualifying entity in a nested-namesspecifier. For C++98,
Missing a -.
> + /* In C++0x, parse the optional `struct' or `class' key. */
> + if (cxx_dialect != cxx98
> + && (cp_lexer_next_token_is_keyword (parser->lexer, RID_CLASS)
> + || cp_lexer_next_token_is_keyword (parser->lexer, RID_STRUCT)))
> + cp_lexer_consume_token (parser->lexer);
Please parse it in C++98 mode as well and then complain so people know
that it's implemented, they just need to give the right flag. Ditto
with the underlying type.
Jason
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [C++ PATCH] C++0x scoped enumerations
2008-08-27 15:55 ` Jason Merrill
@ 2008-08-28 2:52 ` Doug Gregor
0 siblings, 0 replies; 5+ messages in thread
From: Doug Gregor @ 2008-08-28 2:52 UTC (permalink / raw)
To: Jason Merrill; +Cc: Paolo Bonzini, Gcc Patch List
[-- Attachment #1: Type: text/plain, Size: 1108 bytes --]
Thanks, Jason!
I've addressed your comments and committed the attached to mainline.
- Doug
On Tue, Aug 26, 2008 at 1:41 PM, Jason Merrill <jason@redhat.com> wrote:
>> +lookup_enumerator (tree enumtype, tree name)
>> +{
>> + tree e;
>> + gcc_assert (enumtype && TREE_CODE (enumtype) == ENUMERAL_TYPE);
>> +
>> + for (e = TYPE_VALUES (enumtype); e; e = TREE_CHAIN (e))
>> + {
>> + if (TREE_PURPOSE (e) == name)
>> + return TREE_VALUE (e);
>> + }
>
> Please use purpose_member here.
>
>> +/* Parse the qualifying entity in a nested-namesspecifier. For C++98,
>
> Missing a -.
>
>> + /* In C++0x, parse the optional `struct' or `class' key. */
>> + if (cxx_dialect != cxx98
>> + && (cp_lexer_next_token_is_keyword (parser->lexer, RID_CLASS)
>> + || cp_lexer_next_token_is_keyword (parser->lexer,
>> RID_STRUCT)))
>> + cp_lexer_consume_token (parser->lexer);
>
> Please parse it in C++98 mode as well and then complain so people know that
> it's implemented, they just need to give the right flag. Ditto with the
> underlying type.
>
> Jason
>
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: scoped-enum-5.patch --]
[-- Type: text/x-patch; name=scoped-enum-5.patch, Size: 49117 bytes --]
Index: c-common.c
===================================================================
--- c-common.c (revision 139610)
+++ c-common.c (working copy)
@@ -4928,6 +4928,8 @@ c_do_switch_warnings (splay_tree cases,
for (chain = TYPE_VALUES (type); chain; chain = TREE_CHAIN (chain))
{
tree value = TREE_VALUE (chain);
+ if (TREE_CODE (value) == CONST_DECL)
+ value = DECL_INITIAL (value);
node = splay_tree_lookup (cases, (splay_tree_key) value);
if (node)
{
Index: dbxout.c
===================================================================
--- dbxout.c (revision 139610)
+++ dbxout.c (working copy)
@@ -2174,16 +2174,21 @@ dbxout_type (tree type, int full)
stabstr_C ('e');
for (tem = TYPE_VALUES (type); tem; tem = TREE_CHAIN (tem))
{
+ tree value = TREE_VALUE (tem);
+
stabstr_I (TREE_PURPOSE (tem));
stabstr_C (':');
- if (TREE_INT_CST_HIGH (TREE_VALUE (tem)) == 0)
- stabstr_D (TREE_INT_CST_LOW (TREE_VALUE (tem)));
- else if (TREE_INT_CST_HIGH (TREE_VALUE (tem)) == -1
- && (HOST_WIDE_INT) TREE_INT_CST_LOW (TREE_VALUE (tem)) < 0)
- stabstr_D (TREE_INT_CST_LOW (TREE_VALUE (tem)));
+ if (TREE_CODE (value) == CONST_DECL)
+ value = DECL_INITIAL (value);
+
+ if (TREE_INT_CST_HIGH (value) == 0)
+ stabstr_D (TREE_INT_CST_LOW (value));
+ else if (TREE_INT_CST_HIGH (value) == -1
+ && (HOST_WIDE_INT) TREE_INT_CST_LOW (value) < 0)
+ stabstr_D (TREE_INT_CST_LOW (value));
else
- stabstr_O (TREE_VALUE (tem));
+ stabstr_O (value);
stabstr_C (',');
if (TREE_CHAIN (tem) != 0)
Index: cp/typeck.c
===================================================================
--- cp/typeck.c (revision 139610)
+++ cp/typeck.c (working copy)
@@ -262,10 +262,10 @@ type_after_usual_arithmetic_conversions
/* FIXME: Attributes. */
gcc_assert (ARITHMETIC_TYPE_P (t1)
|| TREE_CODE (t1) == VECTOR_TYPE
- || TREE_CODE (t1) == ENUMERAL_TYPE);
+ || UNSCOPED_ENUM_P (t1));
gcc_assert (ARITHMETIC_TYPE_P (t2)
|| TREE_CODE (t2) == VECTOR_TYPE
- || TREE_CODE (t2) == ENUMERAL_TYPE);
+ || UNSCOPED_ENUM_P (t2));
/* In what follows, we slightly generalize the rules given in [expr] so
as to deal with `long long' and `complex'. First, merge the
@@ -764,9 +764,9 @@ common_type (tree t1, tree t2)
code1 = TREE_CODE (t1);
code2 = TREE_CODE (t2);
- if ((ARITHMETIC_TYPE_P (t1) || code1 == ENUMERAL_TYPE
+ if ((ARITHMETIC_TYPE_P (t1) || UNSCOPED_ENUM_P (t1)
|| code1 == VECTOR_TYPE)
- && (ARITHMETIC_TYPE_P (t2) || code2 == ENUMERAL_TYPE
+ && (ARITHMETIC_TYPE_P (t2) || UNSCOPED_ENUM_P (t2)
|| code2 == VECTOR_TYPE))
return type_after_usual_arithmetic_conversions (t1, t2);
@@ -1666,7 +1666,7 @@ default_conversion (tree exp)
/* Perform the integral promotions first so that bitfield
expressions (which may promote to "int", even if the bitfield is
declared "unsigned") are promoted correctly. */
- if (INTEGRAL_OR_ENUMERATION_TYPE_P (TREE_TYPE (exp)))
+ if (INTEGRAL_OR_UNSCOPED_ENUMERATION_TYPE_P (TREE_TYPE (exp)))
exp = perform_integral_promotions (exp);
/* Perform the other conversions. */
exp = decay_conversion (exp);
@@ -2548,7 +2548,7 @@ build_array_ref (tree array, tree idx)
warn_array_subscript_with_type_char (idx);
- if (!INTEGRAL_OR_ENUMERATION_TYPE_P (TREE_TYPE (idx)))
+ if (!INTEGRAL_OR_UNSCOPED_ENUMERATION_TYPE_P (TREE_TYPE (idx)))
{
error ("array subscript is not an integer");
return error_mark_node;
Index: cp/decl.c
===================================================================
--- cp/decl.c (revision 139610)
+++ cp/decl.c (working copy)
@@ -10712,13 +10712,20 @@ xref_basetypes (tree ref, tree base_list
\f
/* Begin compiling the definition of an enumeration type.
- NAME is its name.
+ NAME is its name,
+
+ 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.
+
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)
+start_enum (tree name, tree underlying_type, bool scoped_enum_p)
{
tree enumtype;
@@ -10750,6 +10757,39 @@ start_enum (tree name)
enumtype = pushtag (name, enumtype, /*tag_scope=*/ts_current);
}
+ 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);
+ 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);
+ ENUM_UNDERLYING_TYPE (enumtype) = underlying_type;
+ }
+ else
+ error ("underlying type %<%T%> of %<%T%> must be an integral type",
+ underlying_type, enumtype);
+ }
+
return enumtype;
}
@@ -10762,9 +10802,9 @@ finish_enum (tree enumtype)
{
tree values;
tree decl;
- tree value;
tree minnode;
tree maxnode;
+ tree value;
tree t;
bool unsignedp;
bool use_short_enum;
@@ -10773,6 +10813,8 @@ finish_enum (tree enumtype)
int precision;
integer_type_kind itk;
tree underlying_type = NULL_TREE;
+ bool fixed_underlying_type_p
+ = ENUM_UNDERLYING_TYPE (enumtype) != NULL_TREE;
/* We built up the VALUES in reverse order. */
TYPE_VALUES (enumtype) = nreverse (TYPE_VALUES (enumtype));
@@ -10798,34 +10840,34 @@ finish_enum (tree enumtype)
minnode = maxnode = NULL_TREE;
for (values = TYPE_VALUES (enumtype);
- 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;
-
- /* 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;
- }
+ 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;
+
+ /* 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;
@@ -10839,46 +10881,70 @@ finish_enum (tree enumtype)
highprec = min_precision (maxnode, unsignedp);
precision = MAX (lowprec, highprec);
- /* Determine the underlying type of the enumeration.
+ if (!fixed_underlying_type_p)
+ {
+ /* Determine the underlying type of the enumeration.
- [dcl.enum]
+ [dcl.enum]
- The underlying type of an enumeration is an integral type that
- can represent all the enumerator values defined in the
- enumeration. It is implementation-defined which integral type is
- used as the underlying type for an enumeration except that the
- underlying type shall not be larger than int unless the value of
- an enumerator cannot fit in an int or unsigned int.
-
- We use "int" or an "unsigned int" as the underlying type, even if
- a smaller integral type would work, unless the user has
- explicitly requested that we use the smallest possible type. The
- user can request that for all enumerations with a command line
- flag, or for just one enumeration with an attribute. */
-
- use_short_enum = flag_short_enums
- || lookup_attribute ("packed", TYPE_ATTRIBUTES (enumtype));
-
- for (itk = (use_short_enum ? itk_char : itk_int);
- itk != itk_none;
- itk++)
- {
- underlying_type = integer_types[itk];
- if (TYPE_PRECISION (underlying_type) >= precision
- && TYPE_UNSIGNED (underlying_type) == unsignedp)
- break;
- }
- if (itk == itk_none)
- {
- /* DR 377
-
- IF no integral type can represent all the enumerator values, the
- enumeration is ill-formed. */
- error ("no integral type can represent all of the enumerator values "
- "for %qT", enumtype);
- precision = TYPE_PRECISION (long_long_integer_type_node);
- underlying_type = integer_types[itk_unsigned_long_long];
+ The underlying type of an enumeration is an integral type that
+ can represent all the enumerator values defined in the
+ enumeration. It is implementation-defined which integral type is
+ used as the underlying type for an enumeration except that the
+ underlying type shall not be larger than int unless the value of
+ an enumerator cannot fit in an int or unsigned int.
+
+ We use "int" or an "unsigned int" as the underlying type, even if
+ a smaller integral type would work, unless the user has
+ explicitly requested that we use the smallest possible type. The
+ user can request that for all enumerations with a command line
+ flag, or for just one enumeration with an attribute. */
+
+ use_short_enum = flag_short_enums
+ || lookup_attribute ("packed", TYPE_ATTRIBUTES (enumtype));
+
+ for (itk = (use_short_enum ? itk_char : itk_int);
+ itk != itk_none;
+ itk++)
+ {
+ underlying_type = integer_types[itk];
+ if (TYPE_PRECISION (underlying_type) >= precision
+ && TYPE_UNSIGNED (underlying_type) == unsignedp)
+ break;
+ }
+ if (itk == itk_none)
+ {
+ /* DR 377
+
+ IF no integral type can represent all the enumerator values, the
+ enumeration is ill-formed. */
+ error ("no integral type can represent all of the enumerator values "
+ "for %qT", enumtype);
+ precision = TYPE_PRECISION (long_long_integer_type_node);
+ underlying_type = integer_types[itk_unsigned_long_long];
+ }
+
+ /* [dcl.enum]
+
+ 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_SIZE (enumtype) = TYPE_SIZE (underlying_type);
+ TYPE_SIZE_UNIT (enumtype) = TYPE_SIZE_UNIT (underlying_type);
+ TYPE_MODE (enumtype) = TYPE_MODE (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);
+
+ /* Set the underlying type of the enumeration type to the
+ computed enumeration type, restricted to the enumerator
+ values. */
+ ENUM_UNDERLYING_TYPE (enumtype) = copy_node (underlying_type);
+ set_min_and_max_values_for_integral_type
+ (ENUM_UNDERLYING_TYPE (enumtype), precision, unsignedp);
}
+ else
+ underlying_type = ENUM_UNDERLYING_TYPE (enumtype);
/* Compute the minimum and maximum values for the type.
@@ -10889,28 +10955,16 @@ finish_enum (tree enumtype)
underlying type in the range bmin to bmax, where bmin and bmax are,
respectively, the smallest and largest values of the smallest bit-
field that can store emin and emax. */
-
+
/* The middle-end currently assumes that types with TYPE_PRECISION
narrower than their underlying type are suitably zero or sign
extended to fill their mode. g++ doesn't make these guarantees.
Until the middle-end can represent such paradoxical types, we
set the TYPE_PRECISION to the width of the underlying type. */
TYPE_PRECISION (enumtype) = TYPE_PRECISION (underlying_type);
-
+
set_min_and_max_values_for_integral_type (enumtype, precision, unsignedp);
-
- /* [dcl.enum]
-
- 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_SIZE (enumtype) = TYPE_SIZE (underlying_type);
- TYPE_SIZE_UNIT (enumtype) = TYPE_SIZE_UNIT (underlying_type);
- TYPE_MODE (enumtype) = TYPE_MODE (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);
-
+
/* Convert each of the enumerators to the type of the underlying
type of the enumeration. */
for (values = TYPE_VALUES (enumtype); values; values = TREE_CHAIN (values))
@@ -10920,9 +10974,14 @@ finish_enum (tree enumtype)
decl = TREE_VALUE (values);
saved_location = input_location;
input_location = DECL_SOURCE_LOCATION (decl);
- value = perform_implicit_conversion (underlying_type,
- DECL_INITIAL (decl),
- tf_warning_or_error);
+ if (fixed_underlying_type_p)
+ /* If the enumeration type has a fixed underlying type, we
+ already checked all of the enumerator values. */
+ value = DECL_INITIAL (decl);
+ else
+ value = perform_implicit_conversion (underlying_type,
+ DECL_INITIAL (decl),
+ tf_warning_or_error);
input_location = saved_location;
/* Do not clobber shared ints. */
@@ -10930,7 +10989,6 @@ finish_enum (tree enumtype)
TREE_TYPE (value) = enumtype;
DECL_INITIAL (decl) = value;
- TREE_VALUE (values) = value;
}
/* Fix up all variant types of this enum type. */
@@ -10946,8 +11004,13 @@ finish_enum (tree 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);
}
+ /* 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 ());
}
@@ -11024,24 +11087,46 @@ build_enumerator (tree name, tree value,
/* Remove no-op casts from the value. */
STRIP_TYPE_NOPS (value);
+
+ /* If the underlying type of the enum is fixed, check whether
+ the enumerator values fits in the underlying type. If it
+ does not fit, the program is ill-formed [C++0x dcl.enum]. */
+ if (ENUM_UNDERLYING_TYPE (enumtype)
+ && value
+ && TREE_CODE (value) == INTEGER_CST
+ && !int_fits_type_p (value, ENUM_UNDERLYING_TYPE (enumtype)))
+ {
+ error ("enumerator value %E is too large for underlying type %<%T%>",
+ value, ENUM_UNDERLYING_TYPE (enumtype));
+
+ /* Silently convert the value so that we can continue. */
+ value = perform_implicit_conversion (ENUM_UNDERLYING_TYPE (enumtype),
+ value, tf_none);
+ if (value == error_mark_node)
+ value = NULL_TREE;
+ }
}
/* C++ associates enums with global, function, or class declarations. */
context = current_scope ();
/* Build the actual enumeration constant. Note that the enumeration
- constants have the type of their initializers until the
- enumeration is complete:
-
- [ dcl.enum ]
-
- Following the closing brace of an enum-specifier, each enumer-
- ator has the type of its enumeration. Prior to the closing
- brace, the type of each enumerator is the type of its
- initializing value.
-
- In finish_enum we will reset the type. Of course, if we're
- processing a template, there may be no value. */
+ constants have the underlying type of the enum (if it is fixed)
+ or the type of their initializer (if the underlying type of the
+ enum is not fixed):
+
+ [ C++0x dcl.enum ]
+
+ If the underlying type is fixed, the type of each enumerator
+ prior to the closing brace is the underlying type; if the
+ initializing value of an enumerator cannot be represented by
+ the underlying type, the program is ill-formed. If the
+ underlying type is not fixed, the type of each enumerator is
+ the type of its initializing value.
+
+ If the underlying type is not fixed, it will be computed by
+ finish_enum and we will reset the type of this enumerator. Of
+ course, if we're processing a template, there may be no value. */
type = value ? TREE_TYPE (value) : NULL_TREE;
if (context && context == current_class_type)
@@ -11070,6 +11155,26 @@ build_enumerator (tree name, tree value,
TYPE_VALUES (enumtype) = tree_cons (name, decl, TYPE_VALUES (enumtype));
}
+/* Look for an enumerator with the given NAME within the enumeration
+ type ENUMTYPE. This routine is used primarily for qualified name
+ lookup into an enumerator in C++0x, e.g.,
+
+ enum class Color { Red, Green, Blue };
+
+ Color color = Color::Red;
+
+ Returns the value corresponding to the enumerator, or
+ NULL_TREE if no such enumerator was found. */
+tree
+lookup_enumerator (tree enumtype, tree name)
+{
+ tree e;
+ gcc_assert (enumtype && TREE_CODE (enumtype) == ENUMERAL_TYPE);
+
+ e = purpose_member (name, TYPE_VALUES (enumtype));
+ return e? TREE_VALUE (e) : NULL_TREE;
+}
+
\f
/* We're defining DECL. Make sure that it's type is OK. */
Index: cp/call.c
===================================================================
--- cp/call.c (revision 139610)
+++ cp/call.c (working copy)
@@ -771,7 +771,7 @@ standard_conversion (tree to, tree from,
conv = build_conv (ck_std, to, conv);
conv->bad_p = true;
}
- else if (tcode == ENUMERAL_TYPE && fcode == INTEGER_TYPE)
+ else if (UNSCOPED_ENUM_P (to) && fcode == INTEGER_TYPE)
{
/* For backwards brain damage compatibility, allow interconversion of
enums and integers with a pedwarn. */
@@ -896,10 +896,11 @@ standard_conversion (tree to, tree from,
{
/* [conv.bool]
- An rvalue of arithmetic, enumeration, pointer, or pointer to
- member type can be converted to an rvalue of type bool. */
+ An rvalue of arithmetic, unscoped enumeration, pointer, or
+ pointer to member type can be converted to an rvalue of type
+ bool. */
if (ARITHMETIC_TYPE_P (from)
- || fcode == ENUMERAL_TYPE
+ || UNSCOPED_ENUM_P (from)
|| fcode == POINTER_TYPE
|| TYPE_PTR_TO_MEMBER_P (from))
{
@@ -919,7 +920,8 @@ standard_conversion (tree to, tree from,
/* As an extension, allow conversion to complex type. */
else if (ARITHMETIC_TYPE_P (to))
{
- if (! (INTEGRAL_CODE_P (fcode) || fcode == REAL_TYPE))
+ if (! (INTEGRAL_CODE_P (fcode) || fcode == REAL_TYPE)
+ || SCOPED_ENUM_P (from))
return NULL;
conv = build_conv (ck_std, to, conv);
@@ -3702,9 +3704,9 @@ build_conditional_expr (tree arg1, tree
type; the usual arithmetic conversions are performed to bring
them to a common type, and the result is of that type. */
else if ((ARITHMETIC_TYPE_P (arg2_type)
- || TREE_CODE (arg2_type) == ENUMERAL_TYPE)
+ || UNSCOPED_ENUM_P (arg2_type))
&& (ARITHMETIC_TYPE_P (arg3_type)
- || TREE_CODE (arg3_type) == ENUMERAL_TYPE))
+ || UNSCOPED_ENUM_P (arg3_type)))
{
/* In this case, there is always a common type. */
result_type = type_after_usual_arithmetic_conversions (arg2_type,
@@ -4791,7 +4793,7 @@ convert_like_real (conversion *convs, tr
if (convs->check_narrowing)
check_narrowing (totype, expr);
- if (issue_conversion_warnings)
+ if (issue_conversion_warnings && (complain & tf_warning))
expr = convert_and_check (totype, expr);
else
expr = convert (totype, expr);
Index: cp/error.c
===================================================================
--- cp/error.c (revision 139610)
+++ cp/error.c (working copy)
@@ -447,8 +447,13 @@ dump_typename (tree t, int flags)
const char *
class_key_or_enum_as_string (tree t)
{
- if (TREE_CODE (t) == ENUMERAL_TYPE)
- return "enum";
+ if (TREE_CODE (t) == ENUMERAL_TYPE)
+ {
+ if (SCOPED_ENUM_P (t))
+ return "enum class";
+ else
+ return "enum";
+ }
else if (TREE_CODE (t) == UNION_TYPE)
return "union";
else if (TYPE_LANG_SPECIFIC (t) && CLASSTYPE_DECLARED_CLASS (t))
Index: cp/cp-tree.h
===================================================================
--- cp/cp-tree.h (revision 139610)
+++ cp/cp-tree.h (working copy)
@@ -116,7 +116,8 @@ extern void cp_cpp_error (cpp_reader *
2: Unused
3: TYPE_FOR_JAVA.
4: TYPE_HAS_NONTRIVIAL_DESTRUCTOR
- 5: CLASS_TYPE_P.
+ 5: CLASS_TYPE_P (in RECORD_TYPE and UNION_TYPE)
+ SCOPED_ENUM_P (in ENUMERAL_TYPE)
6: TYPE_DEPENDENT_P_VALID
Usage of DECL_LANG_FLAG_?:
@@ -980,7 +981,7 @@ enum languages { lang_c, lang_cplusplus,
|| TREE_CODE (T) == TYPEOF_TYPE \
|| TREE_CODE (T) == BOUND_TEMPLATE_TEMPLATE_PARM \
|| TREE_CODE (T) == DECLTYPE_TYPE \
- || TYPE_LANG_FLAG_5 (T))
+ || CLASS_TYPE_P (T))
/* Set CLASS_TYPE_P for T to VAL. T must be a class, struct, or
union type. */
@@ -2688,6 +2689,10 @@ more_aggr_init_expr_args_p (const aggr_i
#define INTEGRAL_OR_ENUMERATION_TYPE_P(TYPE) \
(TREE_CODE (TYPE) == ENUMERAL_TYPE || CP_INTEGRAL_TYPE_P (TYPE))
+/* Returns true if TYPE is an integral or unscoped enumeration type. */
+#define INTEGRAL_OR_UNSCOPED_ENUMERATION_TYPE_P(TYPE) \
+ (UNSCOPED_ENUM_P (TYPE) || CP_INTEGRAL_TYPE_P (TYPE))
+
/* [basic.fundamental]
Integral and floating types are collectively called arithmetic
@@ -2714,6 +2719,59 @@ more_aggr_init_expr_args_p (const aggr_i
|| TYPE_PTR_P (TYPE) \
|| TYPE_PTRMEMFUNC_P (TYPE))
+/* Determines whether this type is a C++0x scoped enumeration
+ type. Scoped enumerations types are introduced via "enum class" or
+ "enum struct", e.g.,
+
+ enum class Color {
+ Red, Green, Blue
+ };
+
+ Scoped enumeration types are different from normal (unscoped)
+ enumeration types in several ways:
+
+ - The enumerators of a scoped enumeration type are only available
+ within the scope of the enumeration type and not in the
+ enclosing scope. For example, the Red color can be referred to
+ with "Color::Red" but not "Red".
+
+ - Scoped enumerators and enumerations do not implicitly convert
+ to integers or 'bool'.
+
+ - The underlying type of the enum is well-defined. */
+#define SCOPED_ENUM_P(TYPE) \
+ (TREE_CODE (TYPE) == ENUMERAL_TYPE && TYPE_LANG_FLAG_5 (TYPE))
+
+/* Determine whether this is an unscoped enumeration type. */
+#define UNSCOPED_ENUM_P(TYPE) \
+ (TREE_CODE (TYPE) == ENUMERAL_TYPE && !TYPE_LANG_FLAG_5 (TYPE))
+
+/* Set the flag indicating whether an ENUMERAL_TYPE is a C++0x scoped
+ enumeration type (1) or a normal (unscoped) enumeration type
+ (0). */
+#define SET_SCOPED_ENUM_P(TYPE, VAL) \
+ (TYPE_LANG_FLAG_5 (ENUMERAL_TYPE_CHECK (TYPE)) = (VAL))
+
+/* Returns the underlying type of the given enumeration type. The
+ underlying type is determined in different ways, depending on the
+ properties of the enum:
+
+ - In C++0x, the underlying type can be explicitly specified, e.g.,
+
+ enum E1 : char { ... } // underlying type is char
+
+ - In a C++0x scoped enumeration, the underlying type is int
+ unless otherwises specified:
+
+ enum class E2 { ... } // underlying type is int
+
+ - Otherwise, the underlying type is determined based on the
+ values of the enumerators. In this case, the
+ ENUM_UNDERLYING_TYPE will not be set until after the definition
+ of the enumeration is completed by finish_enum. */
+#define ENUM_UNDERLYING_TYPE(TYPE) \
+ TREE_TYPE (ENUMERAL_TYPE_CHECK (TYPE))
+
/* [dcl.init.aggr]
An aggregate is an array or a class with no user-declared
@@ -4272,9 +4330,10 @@ extern bool grok_op_properties (tree,
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);
+extern tree start_enum (tree, tree, bool);
extern void finish_enum (tree);
extern void build_enumerator (tree, tree, tree);
+extern tree lookup_enumerator (tree, tree);
extern void start_preparsed_function (tree, tree, int);
extern int start_function (cp_decl_specifier_seq *, const cp_declarator *, tree);
extern tree begin_function_body (void);
Index: cp/ChangeLog
===================================================================
--- cp/ChangeLog (revision 139610)
+++ cp/ChangeLog (working copy)
@@ -1,3 +1,54 @@
+2008-08-26 Douglas Gregor <doug.gregor@gmail.com>
+
+ * typeck.c (type_after_usual_arithmetic_conversions): Don't do the
+ usual arithmetic conversions on scoped enumeration types.
+ (common_type): Ditto.
+ (default_conversion): Don't perform integral promotions on scoped
+ enumeration types.
+ (build_array_ref): Scoped enumeration types can't be used as
+ subscripts.
+ * decl.c (start_enum): If building a C++0x scoped enumeration,
+ enter its scope. If provided with an underlying type, check that
+ underlying type and set up the enumeration type accordingly.
+ (finish_enum): Only compute an underlying type if the underlying
+ type isn't already fixed, and only convert the enumerator values
+ now if we've just computed the underlying type. Finish the scope
+ of C++0x scoped enumerations.
+ (build_enumerator): For enumerations with a fixed underlying type,
+ check the enumerator values when the enumerator is defined.
+ (lookup_enumerator): New.
+ * call.c (standard_conversion): Don't allow assignment from
+ integers to scoped enumeration types, even with -fpermissive.
+ Don't convert from scoped enumerations to bool or any arithmetic
+ types.
+ (build_conditional_expr): Don't per the usual arithmetic
+ conversions for scoped enumeration types.
+ (convert_like_real): Check complain to see if we should
+ produce warnings.
+ * error.c (class_key_or_enum_as_string): Print scoped enums.
+ * cp-tree.h (MAYBE_CLASS_TYPE_P): Check CLASS_TYPE_P, not
+ TYPE_LANG_FLAG_5.
+ (INTEGRAL_OR_UNSCOPED_ENUMERATION_TYPE_P): New.
+ (SCOPED_ENUM_P): New.
+ (UNSCOPED_ENUM_P): New.
+ (SET_SCOPED_ENUM_P): New.
+ (ENUM_UNDERLYING_TYPE): New.
+ * pt.c (lookup_template_class): Update the instantiation of enum
+ types to deal with C++0x scoped enumerations and underlying
+ types.
+ * name-lookup.c (begin_scope): Deal with scoped enumeration
+ scopes.
+ (lookup_qualified_name): Deal with lookup into enumeration types.
+ * name-lookup.h (enum scope_kind): Add sk_scoped_enum.
+ * parser.c (cp_parser_class_or_namespace_name): Rename to...
+ (cp_parser_qualifying_entity): ... this. Also, in C++0x mode,
+ parse a type-name that can be an enumeration type.
+ (cp_parser_nested_name_specifier_opt): Update with C++0x grammar.
+ (cp_parser_elaborated_type_specifier): Parse the
+ optional `struct' or `class' following enum (in C++0x).
+ (cp_parser_enum_specifier): Parse C++0x scoped enumerations and
+ enum-base clauses.
+
2008-08-21 Manuel Lopez-Ibanez <manu@gcc.gnu.org>
* typeck.c: Update all calls to pedwarn.
Index: cp/pt.c
===================================================================
--- cp/pt.c (revision 139610)
+++ cp/pt.c (working copy)
@@ -5836,14 +5836,20 @@ lookup_template_class (tree d1,
if (!is_partial_instantiation)
{
set_current_access_from_decl (TYPE_NAME (template_type));
- t = start_enum (TYPE_IDENTIFIER (template_type));
+ t = start_enum (TYPE_IDENTIFIER (template_type),
+ tsubst (ENUM_UNDERLYING_TYPE (template_type),
+ arglist, complain, in_decl),
+ SCOPED_ENUM_P (template_type));
}
else
- /* We don't want to call start_enum for this type, since
- the values for the enumeration constants may involve
- template parameters. And, no one should be interested
- in the enumeration constants for such a type. */
- t = make_node (ENUMERAL_TYPE);
+ {
+ /* We don't want to call start_enum for this type, since
+ the values for the enumeration constants may involve
+ template parameters. And, no one should be interested
+ in the enumeration constants for such a type. */
+ t = make_node (ENUMERAL_TYPE);
+ SET_SCOPED_ENUM_P (t, SCOPED_ENUM_P (template_type));
+ }
}
else
{
Index: cp/name-lookup.c
===================================================================
--- cp/name-lookup.c (revision 139610)
+++ cp/name-lookup.c (working copy)
@@ -1329,7 +1329,7 @@ push_binding_level (struct cp_binding_le
/* Create a new KIND scope and make it the top of the active scopes stack.
ENTITY is the scope of the associated C++ entity (namespace, class,
- function); it is NULL otherwise. */
+ function, C++0x enumeration); it is NULL otherwise. */
cxx_scope *
begin_scope (scope_kind kind, tree entity)
@@ -1364,6 +1364,7 @@ begin_scope (scope_kind kind, tree entit
case sk_catch:
case sk_for:
case sk_class:
+ case sk_scoped_enum:
case sk_function_parms:
case sk_omp:
scope->keep = keep_next_level_flag;
@@ -3853,6 +3854,8 @@ lookup_qualified_name (tree scope, tree
if (qualified_lookup_using_namespace (name, scope, &binding, flags))
t = binding.value;
}
+ else if (cxx_dialect != cxx98 && TREE_CODE (scope) == ENUMERAL_TYPE)
+ t = lookup_enumerator (scope, name);
else if (is_class_type (scope, complain))
t = lookup_member (scope, name, 2, is_type_p);
Index: cp/name-lookup.h
===================================================================
--- cp/name-lookup.h (revision 139610)
+++ cp/name-lookup.h (working copy)
@@ -113,6 +113,8 @@ typedef enum scope_kind {
for-init-statement. */
sk_function_parms, /* The scope containing function parameters. */
sk_class, /* The scope containing the members of a class. */
+ sk_scoped_enum, /* The scope containing the enumertors of a C++0x
+ scoped enumeration. */
sk_namespace, /* The scope containing the members of a
namespace, including the global scope. */
sk_template_parms, /* A scope for template parameters. */
Index: cp/parser.c
===================================================================
--- cp/parser.c (revision 139610)
+++ cp/parser.c (working copy)
@@ -1581,7 +1581,7 @@ static tree cp_parser_nested_name_specif
(cp_parser *, bool, bool, bool, bool);
static tree cp_parser_nested_name_specifier
(cp_parser *, bool, bool, bool, bool);
-static tree cp_parser_class_or_namespace_name
+static tree cp_parser_qualifying_entity
(cp_parser *, bool, bool, bool, bool, bool);
static tree cp_parser_postfix_expression
(cp_parser *, bool, bool, bool);
@@ -3932,10 +3932,16 @@ cp_parser_unqualified_id (cp_parser* par
/* Parse an (optional) nested-name-specifier.
- nested-name-specifier:
+ nested-name-specifier: [C++98]
class-or-namespace-name :: nested-name-specifier [opt]
class-or-namespace-name :: template nested-name-specifier [opt]
+ nested-name-specifier: [C++0x]
+ type-name ::
+ namespace-name ::
+ nested-name-specifier identifier ::
+ nested-name-specifier template [opt] simple-template-id ::
+
PARSER->SCOPE should be set appropriately before this function is
called. TYPENAME_KEYWORD_P is TRUE if the `typename' keyword is in
effect. TYPE_P is TRUE if we non-type bindings should be ignored
@@ -4010,7 +4016,7 @@ cp_parser_nested_name_specifier_opt (cp_
else
{
/* If the next token is not an identifier, then it is
- definitely not a class-or-namespace-name. */
+ definitely not a type-name or namespace-name. */
if (token->type != CPP_NAME)
break;
/* If the following token is neither a `<' (to begin a
@@ -4050,12 +4056,12 @@ cp_parser_nested_name_specifier_opt (cp_
/*only_current_p=*/false);
/* Parse the qualifying entity. */
new_scope
- = cp_parser_class_or_namespace_name (parser,
- typename_keyword_p,
- template_keyword_p,
- check_dependency_p,
- type_p,
- is_declaration);
+ = cp_parser_qualifying_entity (parser,
+ typename_keyword_p,
+ template_keyword_p,
+ check_dependency_p,
+ type_p,
+ is_declaration);
/* Look for the `::' token. */
cp_parser_require (parser, CPP_SCOPE, "%<::%>");
@@ -4107,10 +4113,14 @@ cp_parser_nested_name_specifier_opt (cp_
decl = error_mark_node;
}
else
- cp_parser_name_lookup_error
- (parser, token->u.value, decl,
- "is not a class or namespace",
- token->location);
+ {
+ const char* msg = "is not a class or namespace";
+ if (cxx_dialect != cxx98)
+ msg = "is not a class, namespace, or enumeration";
+ cp_parser_name_lookup_error
+ (parser, token->u.value, decl, msg,
+ token->location);
+ }
}
parser->scope = error_mark_node;
error_p = true;
@@ -4229,11 +4239,11 @@ cp_parser_nested_name_specifier (cp_pars
return scope;
}
-/* Parse a class-or-namespace-name.
-
- class-or-namespace-name:
- class-name
- namespace-name
+/* Parse the qualifying entity in a nested-name-specifier. For C++98,
+ this is either a class-name or a namespace-name (which corresponds
+ to the class-or-namespace-name production in the grammar). For
+ C++0x, it can also be a type-name that refers to an enumeration
+ type.
TYPENAME_KEYWORD_P is TRUE iff the `typename' keyword is in effect.
TEMPLATE_KEYWORD_P is TRUE iff the `template' keyword is in effect.
@@ -4247,18 +4257,19 @@ cp_parser_nested_name_specifier (cp_pars
ERROR_MARK_NODE is returned. */
static tree
-cp_parser_class_or_namespace_name (cp_parser *parser,
- bool typename_keyword_p,
- bool template_keyword_p,
- bool check_dependency_p,
- bool type_p,
- bool is_declaration)
+cp_parser_qualifying_entity (cp_parser *parser,
+ bool typename_keyword_p,
+ bool template_keyword_p,
+ bool check_dependency_p,
+ bool type_p,
+ bool is_declaration)
{
tree saved_scope;
tree saved_qualifying_scope;
tree saved_object_scope;
tree scope;
bool only_class_p;
+ bool successful_parse_p;
/* Before we try to parse the class-name, we must save away the
current PARSER->SCOPE since cp_parser_class_name will destroy
@@ -4268,7 +4279,8 @@ cp_parser_class_or_namespace_name (cp_pa
saved_object_scope = parser->object_scope;
/* Try for a class-name first. If the SAVED_SCOPE is a type, then
there is no need to look for a namespace-name. */
- only_class_p = template_keyword_p || (saved_scope && TYPE_P (saved_scope));
+ only_class_p = template_keyword_p
+ || (saved_scope && TYPE_P (saved_scope) && cxx_dialect == cxx98);
if (!only_class_p)
cp_parser_parse_tentatively (parser);
scope = cp_parser_class_name (parser,
@@ -4278,8 +4290,26 @@ cp_parser_class_or_namespace_name (cp_pa
check_dependency_p,
/*class_head_p=*/false,
is_declaration);
+ successful_parse_p = only_class_p || cp_parser_parse_definitely (parser);
+ /* If that didn't work and we're in C++0x mode, try for a type-name. */
+ if (!only_class_p
+ && cxx_dialect != cxx98
+ && !successful_parse_p)
+ {
+ /* Restore the saved scope. */
+ parser->scope = saved_scope;
+ parser->qualifying_scope = saved_qualifying_scope;
+ parser->object_scope = saved_object_scope;
+
+ /* Parse tentatively. */
+ cp_parser_parse_tentatively (parser);
+
+ /* Parse a typedef-name or enum-name. */
+ scope = cp_parser_nonclass_name (parser);
+ successful_parse_p = cp_parser_parse_definitely (parser);
+ }
/* If that didn't work, try for a namespace-name. */
- if (!only_class_p && !cp_parser_parse_definitely (parser))
+ if (!only_class_p && !successful_parse_p)
{
/* Restore the saved scope. */
parser->scope = saved_scope;
@@ -11307,7 +11337,7 @@ cp_parser_nonclass_name (cp_parser* pars
elaborated-type-specifier:
class-key :: [opt] nested-name-specifier [opt] identifier
class-key :: [opt] nested-name-specifier [opt] template [opt] template-id
- enum :: [opt] nested-name-specifier [opt] identifier
+ enum-key :: [opt] nested-name-specifier [opt] identifier
typename :: [opt] nested-name-specifier identifier
typename :: [opt] nested-name-specifier template [opt]
template-id
@@ -11345,6 +11375,17 @@ cp_parser_elaborated_type_specifier (cp_
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). */
+ 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 ("scoped enums");
+
+ /* Consume the `struct' or `class'. */
+ cp_lexer_consume_token (parser->lexer);
+ }
/* Parse the attributes. */
attributes = cp_parser_attributes_opt (parser);
}
@@ -11632,11 +11673,19 @@ cp_parser_elaborated_type_specifier (cp_
/* Parse an enum-specifier.
enum-specifier:
- enum identifier [opt] { enumerator-list [opt] }
+ enum-key identifier [opt] enum-base [opt] { enumerator-list [opt] }
+
+ enum-key:
+ enum
+ enum class [C++0x]
+ enum struct [C++0x]
+
+ enum-base: [C++0x]
+ : type-specifier-seq
GNU Extensions:
- enum attributes[opt] identifier [opt] { enumerator-list [opt] }
- attributes[opt]
+ enum-key attributes[opt] identifier [opt] enum-base [opt]
+ { enumerator-list [opt] }attributes[opt]
Returns an ENUM_TYPE representing the enumeration, or NULL_TREE
if the token stream isn't an enum-specifier after all. */
@@ -11647,6 +11696,8 @@ cp_parser_enum_specifier (cp_parser* par
tree identifier;
tree type;
tree attributes;
+ bool scoped_enum_p = false;
+ tree underlying_type = NULL_TREE;
/* Parse tentatively so that we can back up if we don't find a
enum-specifier. */
@@ -11658,6 +11709,20 @@ cp_parser_enum_specifier (cp_parser* par
the enumeration being defined. */
cp_lexer_consume_token (parser->lexer);
+ /* Parse the "class" or "struct", which indicates a scoped
+ enumeration type in C++0x. */
+ 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 ("scoped enums");
+
+ /* Consume the `struct' or `class' token. */
+ cp_lexer_consume_token (parser->lexer);
+
+ scoped_enum_p = true;
+ }
+
attributes = cp_parser_attributes_opt (parser);
if (cp_lexer_next_token_is (parser->lexer, CPP_NAME))
@@ -11665,6 +11730,35 @@ cp_parser_enum_specifier (cp_parser* par
else
identifier = make_anon_name ();
+ /* Check for the `:' that denotes a specified underlying type in C++0x. */
+ if (cp_lexer_next_token_is (parser->lexer, CPP_COLON))
+ {
+ cp_decl_specifier_seq type_specifiers;
+
+ if (cxx_dialect == cxx98)
+ maybe_warn_cpp0x ("scoped enums");
+
+ /* Consume the `:'. */
+ cp_lexer_consume_token (parser->lexer);
+
+ /* Parse the type-specifier-seq. */
+ cp_parser_type_specifier_seq (parser, /*is_condition=*/false,
+ &type_specifiers);
+ if (type_specifiers.type == error_mark_node)
+ return error_mark_node;
+
+ /* If that didn't work, stop. */
+ if (type_specifiers.type != error_mark_node)
+ {
+ underlying_type = grokdeclarator (NULL, &type_specifiers, TYPENAME,
+ /*initialized=*/0, NULL);
+ if (underlying_type == error_mark_node)
+ underlying_type = NULL_TREE;
+ }
+ else
+ cp_parser_error (parser, "expected underlying type of enumeration");
+ }
+
/* Look for the `{' but don't consume it yet. */
if (!cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
cp_parser_simulate_error (parser);
@@ -11679,7 +11773,7 @@ cp_parser_enum_specifier (cp_parser* par
/* 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);
+ type = start_enum (identifier, underlying_type, scoped_enum_p);
/* Consume the opening brace. */
cp_lexer_consume_token (parser->lexer);
@@ -11818,7 +11912,7 @@ cp_parser_namespace_name (cp_parser* par
During the lookup of a name preceding the :: scope resolution
operator, object, function, and enumerator names are ignored.
- (Note that cp_parser_class_or_namespace_name only calls this
+ (Note that cp_parser_qualifying_entity only calls this
function if the token after the name is the scope resolution
operator.) */
namespace_decl = cp_parser_lookup_name (parser, identifier,
Index: testsuite/g++.dg/cpp0x/scoped_enum_examples.C
===================================================================
--- testsuite/g++.dg/cpp0x/scoped_enum_examples.C (revision 0)
+++ testsuite/g++.dg/cpp0x/scoped_enum_examples.C (revision 0)
@@ -0,0 +1,27 @@
+// { dg-do compile }
+// { dg-options "-std=c++0x" }
+enum class Col { red, yellow, green };
+
+int x = Col::red; // { dg-error "cannot convert" }
+Col y = Col::red;
+
+void f()
+{
+ if (y) { } // { dg-error "could not convert" }
+}
+
+enum direction { left='l', right='r' };
+void g() {
+ // OK
+ direction d;
+ // OK
+ d = left;
+ // OK
+ d = direction::right;
+}
+enum class altitude { high='h', low='l' };
+void h() {
+ altitude a;
+ a = high; // { dg-error "not declared in this scope" }
+ a = altitude::low;
+}
Index: testsuite/g++.dg/cpp0x/scoped_enum.C
===================================================================
--- testsuite/g++.dg/cpp0x/scoped_enum.C (revision 0)
+++ testsuite/g++.dg/cpp0x/scoped_enum.C (revision 0)
@@ -0,0 +1,76 @@
+// { dg-options "-std=c++0x" }
+enum class Color1 {
+ Red,
+ Green,
+ Blue
+};
+
+enum struct Color2 {
+ Red, // { dg-error "previously declared here" }
+ Orange,
+ Yellow,
+ Green,
+ Blue,
+ Indigo = Green + 2,
+ Violet,
+ Red // { dg-error "redefinition" }
+};
+
+enum Color {
+ Red, Green, Blue
+};
+
+enum class Color3 {
+ Red
+};
+
+enum class Color color;
+enum Color3 color3;
+
+void f(int);
+void f2(Color3);
+
+void g()
+{
+ int i = 0;
+ f(color); // okay: unscoped enum
+ f(color3); // { dg-error "cannot convert" }
+ f2(color); // { dg-error "cannot convert" }
+ f2(color3);
+ f2(i); // { dg-error "cannot convert" }
+ i = color3; // { dg-error "cannot convert" }
+ color3 = i; // { dg-error "cannot convert" }
+ f(static_cast<int>(color3)); // okay
+
+ int a[5];
+ a[color3]; // { dg-error "array subscript is not an integer" }
+
+ bool b = color3; // { dg-error "cannot convert" }
+}
+
+void h()
+{
+ Color1 c1 = Color1::Red;
+ Color2 c2 = Color1::Red; // { dg-error "cannot convert" }
+ c2 = Color1::Red; // { dg-error "cannot convert" }
+
+ c2 = Color2::Red;
+ int c3 = Color::Red;
+}
+
+template<typename T, T value>
+struct constant { };
+
+template<typename T>
+int& sfinae(constant<T, T::Green>*);
+
+float& sfinae(void*);
+
+void sfinae_test()
+{
+ int& test1 = sfinae((constant<Color1, Color1::Green>*)0);
+ int& test2 = sfinae((constant<Color2, Color2::Green>*)0);
+ float& test3 = sfinae((constant<Color1, Color1::Red>*)0);
+ int& test4 = sfinae((constant<Color, Green>*)0);
+ float& test5 = sfinae((constant<Color, Red>*)0);
+}
Index: testsuite/g++.dg/cpp0x/enum_base_warn.C
===================================================================
--- testsuite/g++.dg/cpp0x/enum_base_warn.C (revision 0)
+++ testsuite/g++.dg/cpp0x/enum_base_warn.C (revision 0)
@@ -0,0 +1,25 @@
+// { dg-do run }
+// { dg-options "-O2 -Wtype-limits -std=c++0x" }
+extern void link_error (void);
+
+enum Alpha : unsigned char {
+ ZERO = 0, ONE, TWO, THREE
+};
+
+Alpha a2;
+
+int m1 = -1;
+int GetM1() {
+ return m1;
+}
+
+int main() {
+ a2 = static_cast<Alpha>(GetM1());
+ if (a2 == -1) { // { dg-warning "always false due" }
+ link_error ();
+ }
+ if (-1 == a2) { // { dg-warning "always false due" }
+ link_error ();
+ }
+ return 0;
+}
Index: testsuite/g++.dg/cpp0x/scoped_enum_98.C
===================================================================
--- testsuite/g++.dg/cpp0x/scoped_enum_98.C (revision 0)
+++ testsuite/g++.dg/cpp0x/scoped_enum_98.C (revision 0)
@@ -0,0 +1,4 @@
+// { dg-do compile }
+// { dg-options "-std=c++98" }
+enum class E1 { e1 }; // { dg-warning "scoped enums" }
+enum E2 : char { e2 }; // { dg-warning "scoped enums" }
Index: testsuite/g++.dg/cpp0x/enum_base.C
===================================================================
--- testsuite/g++.dg/cpp0x/enum_base.C (revision 0)
+++ testsuite/g++.dg/cpp0x/enum_base.C (revision 0)
@@ -0,0 +1,25 @@
+// { dg-options "-std=c++0x" }
+
+typedef unsigned volatile long long uvlonglong;
+
+enum E1 : char { };
+enum E2 : signed const short { };
+enum E3 : uvlonglong { };
+enum E4 : char {
+ val = 500 // { dg-error "too large" }
+};
+
+enum class E5 {
+ val = (unsigned long long)-1 // { dg-error "too large" }
+};
+
+typedef float Float;
+
+enum class E6 : Float { }; // { dg-error "must be an integral type" }
+
+static_assert (sizeof(E1) == sizeof(char), "char-sized enum");
+static_assert (sizeof(E2) == sizeof(signed short), "short-sized enum");
+static_assert (sizeof(E3) == sizeof(unsigned long long),
+ "long long-sized enum");
+static_assert (sizeof(E4) == sizeof(char), "char-sized enum");
+static_assert (sizeof(E5) == sizeof(int), "scoped enum with int size");
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2008-08-26 22:35 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-08-23 12:12 [C++ PATCH] C++0x scoped enumerations Doug Gregor
2008-08-23 16:54 ` Paolo Bonzini
2008-08-23 17:15 ` Doug Gregor
2008-08-27 15:55 ` Jason Merrill
2008-08-28 2:52 ` Doug Gregor
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).