This is the code: #define TOLOWER(x) (x&~0x20) #define Word(s) \ s[1] ? s[2] ? s[3] ? \ (TOLOWER(s[0]) << 24) + (TOLOWER(s[1]) << 16) + (TOLOWER(s[2]) << 8) + TOLOWER(s[3]) : \ (TOLOWER(s[0]) << 16) + (TOLOWER(s[1]) << 8) + TOLOWER(s[2]) : \ (TOLOWER(s[0]) << 8) + TOLOWER(s[1]) : \ TOLOWER(s[0]) const unsigned int _the = Word("the"); When compiling, this happens: test.c:9:32: error: initializer element is not constant const unsigned int _the = Word("the"); ^ test.c:3:3: note: in definition of macro ‘Word’ s[1] ? s[2] ? s[3] ? \ ^ How is this not constant? clang thinks it is constant.
(In reply to felix-gcc from comment #0) > How is this not constant? clang thinks it is constant. Not in C: $ clang q.c -c q.c:9:27: error: initializer element is not a compile-time constant
uh, yes in C. $ cat test.c #define TOLOWER(x) (x&~0x20) #define Word(s) \ s[1] ? s[2] ? s[3] ? \ (TOLOWER(s[0]) << 24) + (TOLOWER(s[1]) << 16) + (TOLOWER(s[2]) << 8) + TOLOWER(s[3]) : \ (TOLOWER(s[0]) << 16) + (TOLOWER(s[1]) << 8) + TOLOWER(s[2]) : \ (TOLOWER(s[0]) << 8) + TOLOWER(s[1]) : \ TOLOWER(s[0]) const unsigned int _the = Word("the"); $ clang -c test.c $ clang --version clang version 3.9.0 (trunk 261746)
The C standard says it's not a constant, but clang accepts it as an extension. That doesn't make it valid C though.
So which part of it is not constant, you would say? It all looks constant to me. It only operates on constants. If 3+4 is constant, why should this not be constant?
This is basically about char t = "f"[0]; at the file scope. clang probably started to accept this in 3.8 or in 3.9.
I'm also curious why this wouldn't be considered constant. I only have an old draft C standard, but I see no language forbidding this, and it can be evaluated at compile-time. We even have a fold_read_from_constant_string, but for whatever reason it's not called when folding an ARRAY_REF.
Cc'ing Jonathan in the hope he can answer the question.
I don't think it is forbidden. The C standard allows some latitude for constant expressions in initializers, so I think we could accept code as in Comment 5, i.e. evaluate it to an arithmetic constant expression.
I also agree that accepting it would be a useful extension (perhaps when diagnosed in pedantic mode to aid portability since other compilers reject it). As to where C11 rules it out, I believe it's in 6.6 which says that "constant expressions in initializers ... shall be, or evaluate to, one of the following: -- an /arithmetic constant expression/, -- a null pointer constant, -- an address constant, or -- an address constant for a complete object type plus or minus an integer constant expression." An /arithmetic constant expression/ shall have arithmetic type and shall only have operands that are integer constants, floating constants, enumeration constants, character constants, sizeof expressions whose results are integer constants, and _Alignof expressions. "f"[0] is none of the above expressions. C also says that "An implementation may accept other forms of constant expressions" so accepting it wouldn't be out of line with the requirements.
Martin said almost exactly what I was going to say :-) Compilers are allowed to accept this, as Clang does, but they are not required to.
Seems like there is agreement that this would be a useful enhancement so I'll mark this enhancement request as accepted by changing its Status to New (and its Severity to Enhancement).
*** Bug 54823 has been marked as a duplicate of this bug. ***
I actually tried this a few weeks ago, it's slightly less trivial than it seems since you don't want to fold away the inside of &("fish"[0]).
C++ FE's constexpr.c has bool lval argument to many recursive functions, so it can differentiate between cases where you don't want to fold "str"[0] to 's' and cases where you can do that. It is among other cases set when processing ADDR_EXPR's operand. But c_fully_fold_internal doesn't have anything like that, so that would need to be introduced there.
(In reply to Jakub Jelinek from comment #14) > C++ FE's constexpr.c has bool lval argument to many recursive functions, so > it can differentiate between cases where you don't want to fold "str"[0] to > 's' and cases where you can do that. It is among other cases set when > processing ADDR_EXPR's operand. But c_fully_fold_internal doesn't have > anything like that, so that would need to be introduced there. From the related PR66618, it seems that if we had such an argument properly propagated, we could as well use decl_constant_value_for_optimization during c_fully_fold_internal (if not pedantic?) centrally for VAR_DECLs, rather then just using it on selected operands where we basically know for sure we don't need an lvalue. Without such changes, a hack could be to add the ARRAY_REF of STRING_CST with constant index folding into decl_constant_value_for_optimization.
*** Bug 82695 has been marked as a duplicate of this bug. ***
Author: jakub Date: Sun Nov 19 17:17:01 2017 New Revision: 254930 URL: https://gcc.gnu.org/viewcvs?rev=254930&root=gcc&view=rev Log: PR c/66618 PR c/69960 c-family/ * c-common.h (c_fully_fold): Add LVAL argument defaulted to false. c/ * c-parser.c (c_parser_omp_atomic): Pass true as LVAL to c_fully_fold where needed. * c-typeck.c (build_unary_op, build_modify_expr, build_asm_expr, handle_omp_array_sections): Likewise. (digest_init): Don't call decl_constant_value_for_optimization. * c-tree.h (decl_constant_value_for_optimization): Removed. * c-fold.c (c_fold_array_ref): New function. (c_fully_fold_internal): Add LVAL argument, propagate it through recursive calls. For VAR_P call decl_constant_value and unshare if not LVAL and either optimizing or IN_INIT. Remove decl_constant_value_for_optimization calls. If IN_INIT and not LVAL, fold ARRAY_REF with STRING_CST and INTEGER_CST operands. (c_fully_fold): Add LVAL argument, pass it through to c_fully_fold_internal. (decl_constant_value_for_optimization): Removed. cp/ * cp-gimplify.c (c_fully_fold): Add LVAL argument, call cp_fold_maybe_rvalue instead of cp_fold_rvalue and pass it !LVAL. testsuite/ * gcc.dg/pr69960.c: New test. * gcc.dg/pr66618.c: New test. * gcc.dg/pr66618-2.c: New test. Added: trunk/gcc/testsuite/gcc.dg/pr66618-2.c trunk/gcc/testsuite/gcc.dg/pr66618.c trunk/gcc/testsuite/gcc.dg/pr69960.c Modified: trunk/gcc/c-family/ChangeLog trunk/gcc/c-family/c-common.h trunk/gcc/c/ChangeLog trunk/gcc/c/c-fold.c trunk/gcc/c/c-parser.c trunk/gcc/c/c-tree.h trunk/gcc/c/c-typeck.c trunk/gcc/cp/ChangeLog trunk/gcc/cp/cp-gimplify.c trunk/gcc/testsuite/ChangeLog
Fixed for 8.1+.