This patch adds an __array_size keyword for the C, C++, Objective C and Objective C++ languages which is similar to the sizeof keyword, but yields the size of the specified array in elements, not bytes, and will not accept expressions of pointer type.
At the moment, I am only looking for feedback, as I appreciate that this would have to wait for GCC 7 at the earliest, if it is deemed desirable. I have searched and been unable to find any previous submissions similar to this, or previous discussion of the idea. I have discussed this with a number of C developers and generally received positive encouragement for submitting this patch, so it seems odd to me that this is the case, so perhaps I have simply missed prior submissions, somehow? The intent is that one can (perhaps conditionally) use a new definition for the ARRAY_SIZE macro that is commonly used: #ifdef FOO #define ARRAY_SIZE(arr) __array_size(arr) #else #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(*arr)) #endif The advantage to using this macro is that it will catch situations where we attempt to take the array size of a pointer. The error message that I have added is as follows: arraysize-test.c: In function ‘foo’: arraysize-test.c:4:55: error: invalid application of ‘__array_size’ to non-array type printf("%i\n", __array_size(arr)); ^ There may well be a better wording for this message and I would welcome suggestions. It should be noted that GCC's __builtin_types_compatible_p builtin allows similar type checking to be performed with an expression that results in evaluation of the size of a bitfield of negative width, in the case where a pointer type is used. However, the macro or series of macros to do this is not especially simple, nor is the error message that is generated especially readable, even when using appropriately named macros for the purpose[0]: arraysize-old.c: In function ‘main’: arraysize-old.c:12:20: error: negative width in bit-field ‘<anonymous>’ (sizeof(struct { int:-!!(e)*1234; })) ^ arraysize-old.c:17:6: note: in expansion of macro ‘BUILD_BUG_ON_ZERO’ BUILD_BUG_ON_ZERO(SAME_TYPE((a), &(*a))) ^~~~~~~~~~~~~~~~~ arraysize-old.c:30:8: note: in expansion of macro ‘MUST_BE_ARRAY’ + MUST_BE_ARRAY(a)) ^~~~~~~~~~~~~ arraysize-old.c:36:17: note: in expansion of macro ‘ARRAY_SIZE’ printf("%i\n", ARRAY_SIZE(b)); ^~~~~~~~~~ An additional consideration would be whether a feature test macro would be beneficial, and whether there are any matters of consideration that might hypothetically be relevant to ISO/IEC JTC 1/SC 22 / WG14 (although perhaps I may be getting ahead of myself). I present this change largely as a diagnostics improvement, rather than as new functionality. That said, for C++, this does introduce __array_size as a keyword that must be handled within the name mangling scheme. For this, I have used the string "as", but I would appreciate advice on how appropriate this encoding is. Documentation and test code are currently absent from the patch, but I will add these, should this RFC receive sufficient positive feedback to be worth following up. I would like advice as to what test cases I should add, i.e. should I simply duplicate all of the tests for sizeof, or only target likely trouble spots where __array_size behaves differently? I appreciate that I will also need a ChangeLog entry, and to do proper testing and include test results. If anyone could help guide me through this process I would be very grateful. My patch, if deemed desirable, may be large enough to require copyright assignment, in which case I would very much appreciate some help in dealing with this. -- Stuart Brady [0] My own blog post with the macro that I use: http://zubplot.blogspot.co.uk/2015/01/gcc-is-wonderful-better-arraysize-macro.html c-family/c-common.c | 54 +++++++++++++++++++++++++++++++ c-family/c-common.def | 4 ++ c-family/c-common.h | 3 + c/c-parser.c | 62 ++++++++++++++++++++++++++++++++++++ c/c-tree.h | 3 + c/c-typeck.c | 69 ++++++++++++++++++++++++++++++++++++++++ cp/constexpr.c | 1 cp/cp-objcp-common.c | 1 cp/cp-tree.h | 9 +++++ cp/cxx-pretty-print.c | 11 ++++-- cp/error.c | 6 ++- cp/mangle.c | 12 ++++++ cp/operators.def | 1 cp/parser.c | 16 ++++++++- cp/pt.c | 35 +++++++++++++++----- cp/tree.c | 8 ++++ cp/typeck.c | 86 +++++++++++++++++++++++++++++++++++++++++++++----- 17 files changed, 360 insertions(+), 21 deletions(-) diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c index 3d84316..8033d14 100644 --- a/gcc/c-family/c-common.c +++ b/gcc/c-family/c-common.c @@ -445,6 +445,8 @@ const struct c_common_resword c_common_reswords[] = { "__PRETTY_FUNCTION__", RID_PRETTY_FUNCTION_NAME, 0 }, { "__alignof", RID_ALIGNOF, 0 }, { "__alignof__", RID_ALIGNOF, 0 }, + { "__array_size", RID_ARRAY_SIZE, 0 }, + { "__array_size__", RID_ARRAY_SIZE, 0 }, { "__asm", RID_ASM, 0 }, { "__asm__", RID_ASM, 0 }, { "__attribute", RID_ATTRIBUTE, 0 }, @@ -4929,6 +4931,58 @@ c_sizeof_or_alignof_type (location_t loc, return value; } +/* Compute '__array_size (TYPE)'. The COMPLAIN flag controls whether we + should diagnose possibly ill-formed constructs or not. LOC is the + location of the __array_size operator. */ + +tree +c_array_size_type (location_t loc, tree type, int complain) +{ + tree value = NULL; + enum tree_code type_code = TREE_CODE (type); + const char *op_name = "__array_size"; + + if (type_code != ARRAY_TYPE) + { + if (complain) + error_at (loc, "invalid application of %qs to non-array type", + op_name); + return error_mark_node; + } + else if (c_dialect_cxx () && !COMPLETE_TYPE_P (TREE_TYPE (type))) + { + if (complain) + error_at (loc, "invalid application of %qs to array type %qT of " + "incomplete element type", op_name, type); + return error_mark_node; + } + else if (!TYPE_DOMAIN (type)) + { + if (complain) + error_at (loc, "invalid use of array with unspecified bounds"); + return error_mark_node; + } + else + { + value = TYPE_MAX_VALUE (TYPE_DOMAIN (type)); + if (value == NULL) + { + if (complain) + error_at (loc, "invalid use of flexible array member"); + return error_mark_node; + } + + value = size_binop (PLUS_EXPR, value, size_one_node); + } + + /* VALUE will have the middle-end integer type sizetype. + However, we should really return a value of type `size_t', + which is just a typedef for an ordinary integer type. */ + value = fold_convert_loc (loc, size_type_node, value); + + return value; +} + /* Implement the __alignof keyword: Return the minimum required alignment of EXPR, measured in bytes. For VAR_DECLs, FUNCTION_DECLs and FIELD_DECLs return DECL_ALIGN (which can be set diff --git a/gcc/c-family/c-common.def b/gcc/c-family/c-common.def index 1d3ee53..7bbf40e 100644 --- a/gcc/c-family/c-common.def +++ b/gcc/c-family/c-common.def @@ -55,6 +55,9 @@ DEFTREECODE (USERDEF_LITERAL, "userdef_literal", tcc_exceptional, 3) or for the purpose of -Wsizeof-pointer-memaccess warning. */ DEFTREECODE (SIZEOF_EXPR, "sizeof_expr", tcc_expression, 1) +/* Represents a '__array_size' expression during C++ template expansion */ +DEFTREECODE (ARRAY_SIZE_EXPR, "array_size_expr", tcc_expression, 1) + /* Array Notation expression. Operand 0 is the array. Operand 1 is the starting array index. diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index fa3746c..bd6512d 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -103,6 +103,7 @@ enum rid RID_TYPES_COMPATIBLE_P, RID_BUILTIN_COMPLEX, RID_BUILTIN_SHUFFLE, RID_DFLOAT32, RID_DFLOAT64, RID_DFLOAT128, RID_FRACT, RID_ACCUM, RID_AUTO_TYPE, RID_BUILTIN_CALL_WITH_STATIC_CHAIN, + RID_ARRAY_SIZE, /* C11 */ RID_ALIGNAS, RID_GENERIC, @@ -814,6 +815,7 @@ extern tree c_save_expr (tree); extern tree c_common_truthvalue_conversion (location_t, tree); extern void c_apply_type_quals_to_decl (int, tree); extern tree c_sizeof_or_alignof_type (location_t, tree, bool, bool, int); +extern tree c_array_size_type (location_t, tree, int); extern tree c_alignof_expr (location_t, tree); /* Print an error message for invalid operands to arith operation CODE. NOP_EXPR is used as a special case (see truthvalue_conversion). */ @@ -851,6 +853,7 @@ extern bool pointer_to_zero_sized_aggr_p (tree); #define c_sizeof(LOC, T) c_sizeof_or_alignof_type (LOC, T, true, false, 1) #define c_alignof(LOC, T) c_sizeof_or_alignof_type (LOC, T, false, false, 1) +#define c_array_size(LOC, T) c_array_size_type (LOC, T, 1) /* Subroutine of build_binary_op, used for certain operations. */ extern tree shorten_binary_op (tree result_type, tree op0, tree op1, bool bitwise); diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c index eede3a7..c38fc79 100644 --- a/gcc/c/c-parser.c +++ b/gcc/c/c-parser.c @@ -1321,6 +1321,7 @@ static struct c_expr c_parser_cast_expression (c_parser *, struct c_expr *); static struct c_expr c_parser_unary_expression (c_parser *); static struct c_expr c_parser_sizeof_expression (c_parser *); static struct c_expr c_parser_alignof_expression (c_parser *); +static struct c_expr c_parser_array_size_expression (c_parser *); static struct c_expr c_parser_postfix_expression (c_parser *); static struct c_expr c_parser_postfix_expression_after_paren_type (c_parser *, struct c_type_name *, @@ -6717,6 +6718,8 @@ c_parser_cast_expression (c_parser *parser, struct c_expr *after) unary-expression: __alignof__ unary-expression __alignof__ ( type-name ) + __array_size unary-expression + __array_size ( type-name ) && identifier (C11 permits _Alignof with type names only.) @@ -6841,6 +6844,8 @@ c_parser_unary_expression (c_parser *parser) return c_parser_sizeof_expression (parser); case RID_ALIGNOF: return c_parser_alignof_expression (parser); + case RID_ARRAY_SIZE: + return c_parser_array_size_expression (parser); case RID_EXTENSION: c_parser_consume_token (parser); ext = disable_extension_diagnostics (); @@ -7030,6 +7035,62 @@ c_parser_alignof_expression (c_parser *parser) } } +static struct c_expr +c_parser_array_size_expression (c_parser *parser) +{ + struct c_expr expr; + location_t expr_loc; + gcc_assert (c_parser_next_token_is_keyword (parser, RID_ARRAY_SIZE)); + c_parser_consume_token (parser); + c_inhibit_evaluation_warnings++; + in_array_size++; + if (c_parser_next_token_is (parser, CPP_OPEN_PAREN) + && c_token_starts_typename (c_parser_peek_2nd_token (parser))) + { + /* Either array_size ( type-name ) or array_size unary-expression + starting with a compound literal. */ + struct c_type_name *type_name; + c_parser_consume_token (parser); + expr_loc = c_parser_peek_token (parser)->location; + type_name = c_parser_type_name (parser); + c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>"); + if (type_name == NULL) + { + struct c_expr ret; + c_inhibit_evaluation_warnings--; + in_array_size--; + ret.value = error_mark_node; + ret.original_code = ERROR_MARK; + ret.original_type = NULL; + return ret; + } + if (c_parser_next_token_is (parser, CPP_OPEN_BRACE)) + { + expr = c_parser_postfix_expression_after_paren_type (parser, + type_name, + expr_loc); + goto array_size_expr; + } + /* array_size ( type-name ). */ + c_inhibit_evaluation_warnings--; + in_array_size--; + return c_expr_array_size_type (expr_loc, type_name); + } + else + { + expr_loc = c_parser_peek_token (parser)->location; + expr = c_parser_unary_expression (parser); + array_size_expr: + c_inhibit_evaluation_warnings--; + in_array_size--; + mark_exp_read (expr.value); + if (TREE_CODE (expr.value) == COMPONENT_REF + && DECL_C_BIT_FIELD (TREE_OPERAND (expr.value, 1))) + error_at (expr_loc, "%<__array_size%> applied to a bit-field"); + return c_expr_array_size_expr (expr_loc, expr); + } +} + /* Helper function to read arguments of builtins which are interfaces for the middle-end nodes like COMPLEX_EXPR, VEC_PERM_EXPR and others. The name of the builtin is passed using BNAME parameter. @@ -9479,6 +9540,7 @@ c_parser_objc_selector (c_parser *parser) case RID_SIZEOF: case RID_TYPEOF: case RID_ALIGNOF: + case RID_ARRAY_SIZE: case RID_UNSIGNED: case RID_LONG: case RID_CONST: diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h index cf79ba7..dc083c1 100644 --- a/gcc/c/c-tree.h +++ b/gcc/c/c-tree.h @@ -580,6 +580,7 @@ extern bool c_vla_unspec_p (tree x, tree fn); /* in c-typeck.c */ extern int in_alignof; +extern int in_array_size; extern int in_sizeof; extern int in_typeof; @@ -608,6 +609,8 @@ extern tree build_component_ref (location_t, tree, tree); extern tree build_array_ref (location_t, tree, tree); extern tree build_external_ref (location_t, tree, int, tree *); extern void pop_maybe_used (bool); +extern struct c_expr c_expr_array_size_expr (location_t, struct c_expr); +extern struct c_expr c_expr_array_size_type (location_t, struct c_type_name *); extern struct c_expr c_expr_sizeof_expr (location_t, struct c_expr); extern struct c_expr c_expr_sizeof_type (location_t, struct c_type_name *); extern struct c_expr parser_build_unary_op (location_t, enum tree_code, diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c index 65925cb..faae55fc 100644 --- a/gcc/c/c-typeck.c +++ b/gcc/c/c-typeck.c @@ -62,6 +62,9 @@ enum impl_conv { /* The level of nesting inside "__alignof__". */ int in_alignof; +/* The level of nesting inside "__array_size". */ +int in_array_size; + /* The level of nesting inside "sizeof". */ int in_sizeof; @@ -2900,6 +2903,72 @@ c_expr_sizeof_type (location_t loc, struct c_type_name *t) return ret; } +/* Return the result of sizeof applied to EXPR. */ + +struct c_expr +c_expr_array_size_expr (location_t loc, struct c_expr expr) +{ + struct c_expr ret; + if (expr.value == error_mark_node) + { + ret.value = error_mark_node; + ret.original_code = ERROR_MARK; + ret.original_type = NULL; + pop_maybe_used (false); + } + else + { + bool expr_const_operands = true; + tree folded_expr = c_fully_fold (expr.value, require_constant_value, + &expr_const_operands); + ret.value = c_array_size (loc, TREE_TYPE (folded_expr)); + ret.original_code = ARRAY_SIZE_EXPR; + ret.original_type = NULL; + if (c_vla_type_p (TREE_TYPE (folded_expr))) + { + /* sizeof is evaluated when given a vla (C99 6.5.3.4p2). */ + ret.value = build2 (C_MAYBE_CONST_EXPR, TREE_TYPE (ret.value), + folded_expr, ret.value); + C_MAYBE_CONST_EXPR_NON_CONST (ret.value) = !expr_const_operands; + SET_EXPR_LOCATION (ret.value, loc); + } + pop_maybe_used (C_TYPE_VARIABLE_SIZE (TREE_TYPE (folded_expr))); + } + return ret; +} + +struct c_expr +c_expr_array_size_type (location_t loc, struct c_type_name *t) +{ + tree type; + struct c_expr ret; + tree type_expr = NULL_TREE; + bool type_expr_const = true; + type = groktypename (t, &type_expr, &type_expr_const); + ret.value = c_array_size (loc, type); + ret.original_code = ARRAY_SIZE_EXPR; + ret.original_type = NULL; + if ((type_expr || TREE_CODE (ret.value) == INTEGER_CST) + && c_vla_type_p (type)) + { + /* If the type is a [*] array, it is a VLA but is represented as + having a size of zero. In such a case we must ensure that + the result of sizeof does not get folded to a constant by + c_fully_fold, because if the size is evaluated the result is + not constant and so constraints on zero or negative size + arrays must not be applied when this sizeof call is inside + another array declarator. */ + if (!type_expr) + type_expr = integer_zero_node; + ret.value = build2 (C_MAYBE_CONST_EXPR, TREE_TYPE (ret.value), + type_expr, ret.value); + C_MAYBE_CONST_EXPR_NON_CONST (ret.value) = !type_expr_const; + } + pop_maybe_used (type != error_mark_node + ? C_TYPE_VARIABLE_SIZE (type) : false); + return ret; +} + /* Build a function call to function FUNCTION with parameters PARAMS. The function call is at LOC. PARAMS is a list--a chain of TREE_LIST nodes--in which the diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 85fc64e..0ec254e 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -4275,6 +4275,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, case CONST_DECL: case SIZEOF_EXPR: case ALIGNOF_EXPR: + case ARRAY_SIZE_EXPR: case OFFSETOF_EXPR: case NOEXCEPT_EXPR: case TEMPLATE_PARM_INDEX: diff --git a/gcc/cp/cp-objcp-common.c b/gcc/cp/cp-objcp-common.c index f7ddb00..4d87bff 100644 --- a/gcc/cp/cp-objcp-common.c +++ b/gcc/cp/cp-objcp-common.c @@ -271,6 +271,7 @@ cp_common_init_ts (void) MARK_TS_TYPED (ARROW_EXPR); MARK_TS_TYPED (SIZEOF_EXPR); MARK_TS_TYPED (ALIGNOF_EXPR); + MARK_TS_TYPED (ARRAY_SIZE_EXPR); MARK_TS_TYPED (AT_ENCODE_EXPR); MARK_TS_TYPED (UNARY_PLUS_EXPR); MARK_TS_TYPED (TRAIT_EXPR); diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 3b91089..b902748 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -139,6 +139,7 @@ operator == (const cp_expr &lhs, tree rhs) OVL_ARG_DEPENDENT (in OVERLOAD) PACK_EXPANSION_LOCAL_P (in *_PACK_EXPANSION) TINFO_HAS_ACCESS_ERRORS (in TEMPLATE_INFO) + ARRAY_SIZE_EXPR_TYPE_P (in ARRAY_SIZE_EXPR) SIZEOF_EXPR_TYPE_P (in SIZEOF_EXPR) COMPOUND_REQ_NOEXCEPT_P (in COMPOUND_REQ) WILDCARD_PACK_P (in WILDCARD_DECL) @@ -172,6 +173,7 @@ operator == (const cp_expr &lhs, tree rhs) REF_PARENTHESIZED_P (in COMPONENT_REF, INDIRECT_REF) AGGR_INIT_ZERO_FIRST (in AGGR_INIT_EXPR) CONSTRUCTOR_MUTABLE_POISON (in CONSTRUCTOR) + PACK_EXPANSION_ARRAY_SIZE_P (in *_PACK_EXPANSION) 3: (TREE_REFERENCE_EXPR) (in NON_LVALUE_EXPR) (commented-out). ICS_BAD_FLAG (in _CONV) FN_TRY_BLOCK_P (in TRY_BLOCK) @@ -3266,6 +3268,9 @@ extern void decl_shadowed_for_var_insert (tree, tree); /* True iff this pack expansion is for sizeof.... */ #define PACK_EXPANSION_SIZEOF_P(NODE) TREE_LANG_FLAG_1 (NODE) +/* True iff this pack expansion is for array_size.... */ +#define PACK_EXPANSION_ARRAY_SIZE_P(NODE) TREE_LANG_FLAG_2 (NODE) + /* True iff the wildcard can match a template parameter pack. */ #define WILDCARD_PACK_P(NODE) TREE_LANG_FLAG_0 (NODE) @@ -4583,6 +4588,10 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter) #define SIZEOF_EXPR_TYPE_P(NODE) \ TREE_LANG_FLAG_0 (SIZEOF_EXPR_CHECK (NODE)) +/* True if ARRAY_SIZE_EXPR argument is type. */ +#define ARRAY_SIZE_EXPR_TYPE_P(NODE) \ + TREE_LANG_FLAG_0 (ARRAY_SIZE_EXPR_CHECK (NODE)) + /* An enumeration of the kind of tags that C++ accepts. */ enum tag_types { none_type = 0, /* Not a tag type. */ diff --git a/gcc/cp/cxx-pretty-print.c b/gcc/cp/cxx-pretty-print.c index cc28045..3ccc48a 100644 --- a/gcc/cp/cxx-pretty-print.c +++ b/gcc/cp/cxx-pretty-print.c @@ -771,9 +771,10 @@ cxx_pretty_printer::unary_expression (tree t) break; case SIZEOF_EXPR: + case ARRAY_SIZE_EXPR: if (PACK_EXPANSION_P (TREE_OPERAND (t, 0))) { - pp_cxx_ws_string (this, "sizeof"); + pp_cxx_ws_string (this, code == SIZEOF_EXPR ? "sizeof" : "__array_size__"); pp_cxx_ws_string (this, "..."); pp_cxx_whitespace (this); pp_cxx_left_paren (this); @@ -787,9 +788,12 @@ cxx_pretty_printer::unary_expression (tree t) /* Fall through */ case ALIGNOF_EXPR: - pp_cxx_ws_string (this, code == SIZEOF_EXPR ? "sizeof" : "__alignof__"); + pp_cxx_ws_string (this, code == SIZEOF_EXPR ? "sizeof" : + code == ARRAY_SIZE_EXPR ? "__array_size__" : + "__alignof__"); pp_cxx_whitespace (this); - if (TREE_CODE (t) == SIZEOF_EXPR && SIZEOF_EXPR_TYPE_P (t)) + if ((TREE_CODE (t) == SIZEOF_EXPR && SIZEOF_EXPR_TYPE_P (t)) + || (TREE_CODE (t) == ARRAY_SIZE_EXPR && ARRAY_SIZE_EXPR_TYPE_P (t))) { pp_cxx_left_paren (this); type_id (TREE_TYPE (TREE_OPERAND (t, 0))); @@ -1098,6 +1102,7 @@ cxx_pretty_printer::expression (tree t) case SIZEOF_EXPR: case ALIGNOF_EXPR: + case ARRAY_SIZE_EXPR: case NOEXCEPT_EXPR: unary_expression (t); break; diff --git a/gcc/cp/error.c b/gcc/cp/error.c index 3f9cf4a..1cf8929 100644 --- a/gcc/cp/error.c +++ b/gcc/cp/error.c @@ -2510,8 +2510,11 @@ dump_expr (cxx_pretty_printer *pp, tree t, int flags) case SIZEOF_EXPR: case ALIGNOF_EXPR: + case ARRAY_SIZE_EXPR: if (TREE_CODE (t) == SIZEOF_EXPR) pp_cxx_ws_string (pp, "sizeof"); + else if (TREE_CODE (t) == ARRAY_SIZE_EXPR) + pp_cxx_ws_string (pp, "__array_size__"); else { gcc_assert (TREE_CODE (t) == ALIGNOF_EXPR); @@ -2525,7 +2528,8 @@ dump_expr (cxx_pretty_printer *pp, tree t, int flags) } pp_cxx_whitespace (pp); pp_cxx_left_paren (pp); - if (TREE_CODE (t) == SIZEOF_EXPR && SIZEOF_EXPR_TYPE_P (t)) + if ((TREE_CODE (t) == SIZEOF_EXPR && SIZEOF_EXPR_TYPE_P (t)) + || (TREE_CODE (t) == ARRAY_SIZE_EXPR && ARRAY_SIZE_EXPR_TYPE_P (t))) dump_type (pp, TREE_TYPE (op), flags); else if (TYPE_P (TREE_OPERAND (t, 0))) dump_type (pp, op, flags); diff --git a/gcc/cp/mangle.c b/gcc/cp/mangle.c index 410c7f4..896a063 100644 --- a/gcc/cp/mangle.c +++ b/gcc/cp/mangle.c @@ -2771,6 +2771,12 @@ write_expression (tree expr) write_string ("st"); write_type (TREE_TYPE (TREE_OPERAND (expr, 0))); } + else if (TREE_CODE (expr) == ARRAY_SIZE_EXPR + && ARRAY_SIZE_EXPR_TYPE_P (expr)) + { + write_string ("as"); + write_type (TREE_TYPE (TREE_OPERAND (expr, 0))); + } else if (TREE_CODE (expr) == SIZEOF_EXPR && TYPE_P (TREE_OPERAND (expr, 0))) { @@ -2783,6 +2789,12 @@ write_expression (tree expr) write_string ("at"); write_type (TREE_OPERAND (expr, 0)); } + else if (TREE_CODE (expr) == ARRAY_SIZE_EXPR + && TYPE_P (TREE_OPERAND (expr, 0))) + { + write_string ("as"); + write_type (TREE_OPERAND (expr, 0)); + } else if (code == SCOPE_REF || code == BASELINK) { diff --git a/gcc/cp/operators.def b/gcc/cp/operators.def index aa657fa..8eb6a9a 100644 --- a/gcc/cp/operators.def +++ b/gcc/cp/operators.def @@ -94,6 +94,7 @@ DEF_SIMPLE_OPERATOR ("--", PREDECREMENT_EXPR, "mm", 1) DEF_SIMPLE_OPERATOR ("sizeof", SIZEOF_EXPR, "sz", 1) /* These are extensions. */ DEF_SIMPLE_OPERATOR ("alignof", ALIGNOF_EXPR, "az", 1) +DEF_SIMPLE_OPERATOR ("array_size", ARRAY_SIZE_EXPR, "as", 1) DEF_SIMPLE_OPERATOR ("__imag__", IMAGPART_EXPR, "v18__imag__", 1) DEF_SIMPLE_OPERATOR ("__real__", REALPART_EXPR, "v18__real__", 1) diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index 07d1821..b188ab5 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -7686,6 +7686,7 @@ cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk, switch (keyword) { + case RID_ARRAY_SIZE: case RID_ALIGNOF: case RID_SIZEOF: { @@ -7693,7 +7694,20 @@ cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk, enum tree_code op; location_t first_loc; - op = keyword == RID_ALIGNOF ? ALIGNOF_EXPR : SIZEOF_EXPR; + switch (keyword) + { + case RID_ARRAY_SIZE: + op = ARRAY_SIZE_EXPR; + break; + case RID_ALIGNOF: + op = ALIGNOF_EXPR; + break; + case RID_SIZEOF: + op = SIZEOF_EXPR; + break; + default: + gcc_unreachable (); + } /* Consume the token. */ cp_lexer_consume_token (parser->lexer); first_loc = cp_lexer_peek_token (parser->lexer)->location; diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 6780172..364131b 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -10966,6 +10966,7 @@ tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain, some pack expansion args we won't do anything yet. */ if (TREE_CODE (t) == TYPE_PACK_EXPANSION || PACK_EXPANSION_SIZEOF_P (t) + || PACK_EXPANSION_ARRAY_SIZE_P (t) || pack_expansion_args_count (args)) return args; /* Also optimize expression pack expansions if we can tell that the @@ -14050,12 +14051,14 @@ tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl) } case SIZEOF_EXPR: + case ARRAY_SIZE_EXPR: if (PACK_EXPANSION_P (TREE_OPERAND (t, 0))) { tree expanded, op = TREE_OPERAND (t, 0); int len = 0; - if (SIZEOF_EXPR_TYPE_P (t)) + if ((code == SIZEOF_EXPR && SIZEOF_EXPR_TYPE_P (t)) || + (code == ARRAY_SIZE_EXPR && ARRAY_SIZE_EXPR_TYPE_P (t))) op = TREE_TYPE (op); ++cp_unevaluated_operand; @@ -14082,27 +14085,33 @@ tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl) { if (TREE_CODE (expanded) == TREE_VEC) expanded = TREE_VEC_ELT (expanded, len - 1); - else + else if (code == SIZEOF_EXPR) PACK_EXPANSION_SIZEOF_P (expanded) = true; + else + PACK_EXPANSION_ARRAY_SIZE_P (expanded) = true; if (TYPE_P (expanded)) - return cxx_sizeof_or_alignof_type (expanded, SIZEOF_EXPR, + return cxx_sizeof_or_alignof_type (expanded, code, complain & tf_error); else - return cxx_sizeof_or_alignof_expr (expanded, SIZEOF_EXPR, + return cxx_sizeof_or_alignof_expr (expanded, code, complain & tf_error); } else return build_int_cst (size_type_node, len); } - if (SIZEOF_EXPR_TYPE_P (t)) + if ((code == SIZEOF_EXPR && SIZEOF_EXPR_TYPE_P (t)) || + (code == ARRAY_SIZE_EXPR && ARRAY_SIZE_EXPR_TYPE_P (t))) { r = tsubst (TREE_TYPE (TREE_OPERAND (t, 0)), args, complain, in_decl); r = build1 (NOP_EXPR, r, error_mark_node); - r = build1 (SIZEOF_EXPR, + r = build1 (code, tsubst (TREE_TYPE (t), args, complain, in_decl), r); - SIZEOF_EXPR_TYPE_P (r) = 1; + if (code == SIZEOF_EXPR) + SIZEOF_EXPR_TYPE_P (r) = 1; + else + ARRAY_SIZE_EXPR_TYPE_P (r) = 1; return r; } /* Fall through */ @@ -16150,6 +16159,7 @@ tsubst_copy_and_build (tree t, length, stride, TREE_TYPE (op1))); } case SIZEOF_EXPR: + case ARRAY_SIZE_EXPR: if (PACK_EXPANSION_P (TREE_OPERAND (t, 0))) RETURN (tsubst_copy (t, args, complain, in_decl)); /* Fall through */ @@ -22635,7 +22645,12 @@ value_dependent_expression_p (tree expression) } case SIZEOF_EXPR: - if (SIZEOF_EXPR_TYPE_P (expression)) + case ARRAY_SIZE_EXPR: + if (((TREE_CODE (expression) == SIZEOF_EXPR) && + SIZEOF_EXPR_TYPE_P (expression)) || + ((TREE_CODE (expression) == ARRAY_SIZE_EXPR) && + ARRAY_SIZE_EXPR_TYPE_P (expression))) + return dependent_type_p (TREE_TYPE (TREE_OPERAND (expression, 0))); /* FALLTHRU */ case ALIGNOF_EXPR: @@ -22827,6 +22842,7 @@ type_dependent_expression_p (tree expression) /* Some expression forms are never type-dependent. */ if (TREE_CODE (expression) == PSEUDO_DTOR_EXPR || TREE_CODE (expression) == SIZEOF_EXPR + || TREE_CODE (expression) == ARRAY_SIZE_EXPR || TREE_CODE (expression) == ALIGNOF_EXPR || TREE_CODE (expression) == AT_ENCODE_EXPR || TREE_CODE (expression) == NOEXCEPT_EXPR @@ -23040,6 +23056,7 @@ instantiation_dependent_r (tree *tp, int *walk_subtrees, /* Handle expressions with type operands. */ case SIZEOF_EXPR: + case ARRAY_SIZE_EXPR: case ALIGNOF_EXPR: case TYPEID_EXPR: case AT_ENCODE_EXPR: @@ -23047,6 +23064,8 @@ instantiation_dependent_r (tree *tp, int *walk_subtrees, tree op = TREE_OPERAND (*tp, 0); if (code == SIZEOF_EXPR && SIZEOF_EXPR_TYPE_P (*tp)) op = TREE_TYPE (op); + else if (code == ARRAY_SIZE_EXPR && ARRAY_SIZE_EXPR_TYPE_P (*tp)) + op = TREE_TYPE (op); if (TYPE_P (op)) { if (dependent_type_p (op)) diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c index 3203aca..9c4919f 100644 --- a/gcc/cp/tree.c +++ b/gcc/cp/tree.c @@ -3136,6 +3136,7 @@ cp_tree_equal (tree t1, tree t2) case SIZEOF_EXPR: case ALIGNOF_EXPR: + case ARRAY_SIZE_EXPR: { tree o1 = TREE_OPERAND (t1, 0); tree o2 = TREE_OPERAND (t2, 0); @@ -3147,6 +3148,13 @@ cp_tree_equal (tree t1, tree t2) if (SIZEOF_EXPR_TYPE_P (t2)) o2 = TREE_TYPE (o2); } + else if (code1 == ARRAY_SIZE_EXPR) + { + if (ARRAY_SIZE_EXPR_TYPE_P (t1)) + o1 = TREE_TYPE (o1); + if (ARRAY_SIZE_EXPR_TYPE_P (t2)) + o2 = TREE_TYPE (o2); + } if (TREE_CODE (o1) != TREE_CODE (o2)) return false; if (TYPE_P (o1)) diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index c9fa112..a07eadb 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -1550,7 +1550,7 @@ cxx_sizeof_or_alignof_type (tree type, enum tree_code op, bool complain) tree value; bool dependent_p; - gcc_assert (op == SIZEOF_EXPR || op == ALIGNOF_EXPR); + gcc_assert (op == SIZEOF_EXPR || op == ALIGNOF_EXPR || op == ARRAY_SIZE_EXPR); if (type == error_mark_node) return error_mark_node; @@ -1585,9 +1585,13 @@ cxx_sizeof_or_alignof_type (tree type, enum tree_code op, bool complain) return value; } - return c_sizeof_or_alignof_type (input_location, complete_type (type), - op == SIZEOF_EXPR, false, - complain); + if (op == ARRAY_SIZE_EXPR) + return c_array_size_type (input_location, complete_type (type), + complain); + else + return c_sizeof_or_alignof_type (input_location, complete_type (type), + op == SIZEOF_EXPR, false, + complain); } /* Return the size of the type, without producing any warnings for @@ -1741,16 +1745,82 @@ cxx_alignof_expr (tree e, tsubst_flags_t complain) return fold_convert (size_type_node, t); } +/* Process an array_size expression where the operand is an expression. */ + +static tree +cxx_array_size_expr (tree e, tsubst_flags_t complain) +{ + if (e == error_mark_node) + return error_mark_node; + + if (processing_template_decl) + { + e = build_min (ARRAY_SIZE_EXPR, size_type_node, e); + TREE_SIDE_EFFECTS (e) = 0; + TREE_READONLY (e) = 1; + + return e; + } + + /* To get the size of a static data member declared as an array of + unknown bound, we need to instantiate it. */ + if (VAR_P (e) + && VAR_HAD_UNKNOWN_BOUND (e) + && DECL_TEMPLATE_INSTANTIATION (e)) + instantiate_decl (e, /*defer_ok*/true, /*expl_inst_mem*/false); + + e = mark_type_use (e); + + if (TREE_CODE (e) == COMPONENT_REF + && TREE_CODE (TREE_OPERAND (e, 1)) == FIELD_DECL + && DECL_C_BIT_FIELD (TREE_OPERAND (e, 1))) + { + if (complain & tf_error) + error ("invalid application of %<__array_size%> to a bit-field"); + else + return error_mark_node; + e = char_type_node; + } + else if (is_overloaded_fn (e)) + { + if (complain & tf_error) + permerror (input_location, "invalid application of %<__array_size%> to an expression of " + "function type"); + else + return error_mark_node; + e = char_type_node; + } + else if (type_unknown_p (e)) + { + if (complain & tf_error) + cxx_incomplete_type_error (e, TREE_TYPE (e)); + else + return error_mark_node; + e = char_type_node; + } + else + e = TREE_TYPE (e); + + return cxx_sizeof_or_alignof_type (e, ARRAY_SIZE_EXPR, complain & tf_error); +} + /* Process a sizeof or alignof expression E with code OP where the operand is an expression. */ tree cxx_sizeof_or_alignof_expr (tree e, enum tree_code op, bool complain) { - if (op == SIZEOF_EXPR) - return cxx_sizeof_expr (e, complain? tf_warning_or_error : tf_none); - else - return cxx_alignof_expr (e, complain? tf_warning_or_error : tf_none); + switch (op) + { + case SIZEOF_EXPR: + return cxx_sizeof_expr (e, complain? tf_warning_or_error : tf_none); + case ALIGNOF_EXPR: + return cxx_alignof_expr (e, complain? tf_warning_or_error : tf_none); + case ARRAY_SIZE_EXPR: + return cxx_array_size_expr (e, complain? tf_warning_or_error : tf_none); + default: + gcc_unreachable (); + } } /* Build a representation of an expression 'alignas(E).' Return the