(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#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)
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?
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.
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.
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.* c-common.h (c_fully_fold): Add LVAL argument defaulted to false. * 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-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
This ought to be discussed again. "clang allows it" is not an argument. First of all, it is questionable if gcc is still conforming after the change discussed here and implemented as per gcc 8.0. Yes "an implementation may accept other forms of constant expressions" but that doesn't mean that a compiler is allowed to ignore the constraints in C17 6.7.9/4 nor the definition of an integer constant expression. So this ought to explicitly be a compiler extension and we ought to have a way to reliably compile strictly conforming programs with gcc without constraint violations silently getting ignored. So if this feature is desired as an extension (I'm sure it is), then the old diagnostic message should still be there when compiling as -std=c17 -pedantic. See detailed discussion and relevant ISO 9899 quotes here: https://stackoverflow.com/questions/68252570/why-are-const-qualified-variables-accepted-as-initializers-on-gcc On top of that mess, I just found out that gcc behaves inconsistently in regards of constant expressions between compiler ports. All gcc ports reject an initializer such as (uint32_t)&function_pointer as they ought, except gcc for ARM32 which silently allows this even under strict mode. This as per https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108875 which I think is NOT a bug, since the definition of an arithmetic constant expression (6.6) always had "Cast operators in an arithmetic constant expression shall only convert arithmetic types to arithmetic types". A function/object pointer is not an arithmetic type. A normative "shall" was violated. A conforming compiler must issue a diagnostic. If the C standard by design blocks meaningful use of some constant expressions inside initializer lists (I would agree that it does, the linked bug report above is a very valid use-case in embedded systems), then gcc has the option to make an extension and only warn in -pedantic mode. Bug again, this route was not taken there either. Standard compliance was just silently abandoned in the ARM32 port. Therefore the current state of affairs is: gcc <8.0 (IMO compliant) behaves differently from gcc >=8.0 which in turn behaves differently from gcc ARM32 any version. Three different gcc behaviors for a language feature which has NOT changed at all between C90 to C17. All of this has to be revisited for the C23 constexpr/"named constants" implementation, so it would be great if we at the same time can separate non-standard extensions from -pedantic mode. Notably C23 does not allow casts from non-arithemtic types inside arithmetic constant expressions either. Also note that C23 changed the wording slightly from C17: "An implementation may accept other forms of constant expressions; however, they are not an integer constant expression." I don't know why but likely because of some implemented DR.
Further info about the "ARM32 port bug". In case you write code like `(uint32_t)&function_pointer` and the port happens to use 32 bit pointers, the non-conforming cast is let through. In case you cast to an integer type of different size in relation to the pointer size (non-portable cast), you first get a warning about that: "warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]", followed by the diagnostic "error: initializer element is not computable at load time". https://godbolt.org/z/xjYvd41qe Correct compiler behavior here is to always give a diagnostic for (uint32_t)&reset_handler not being an acceptable arithmetic constant expression. If that's the same "implementation may accept other forms of constant expressions" bug as originally discussed here or a different bug, I don't know.
On Wed, 22 Feb 2023, daniel.lundin.mail at gmail dot com via Gcc-bugs wrote:
> First of all, it is questionable if gcc is still conforming after the change
> discussed here and implemented as per gcc 8.0. Yes "an implementation may
> accept other forms of constant expressions" but that doesn't mean that a
> compiler is allowed to ignore the constraints in C17 6.7.9/4 nor the definition
> of an integer constant expression. So this ought to explicitly be a compiler
> extension and we ought to have a way to reliably compile strictly conforming
> programs with gcc without constraint violations silently getting ignored.
"integer constant expression" does not mean the same thing as "constant
expression of integer type". If you use this expression in a context
requiring an integer constant expression (case label, bit-field width,
array designator in initializer, enum value, array size at file scope,
constexpr initializer for object of integer type, etc.), it's properly
rejected as required; in contexts where both integer constant expressions
and other expressions are valid but with different semantics (e.g.
determining whether something is a null pointer constant, determining
whether an array is a VLA in a context where both VLA and non-VLA arrays
are valid), again it's treated as non-constant.
I do however expect there may be cases in GCC 13 where constexpr initializers of floating type are accepted that do not meet the definition of arithmetic constant expressions, since GCC is generally a lot more careful about ensuring things are integer constant expressions when required than it is about doing the same for arithmetic constant expressions (before C2x there weren't any cases that allowed arithmetic constant expressions without also allowing other kinds of constant expressions permitted in initializers).
(In reply to joseph@codesourcery.com from comment #21) > On Wed, 22 Feb 2023, daniel.lundin.mail at gmail dot com via Gcc-bugs wrote: > > First of all, it is questionable if gcc is still conforming after the change > > discussed here and implemented as per gcc 8.0. Yes "an implementation may > > accept other forms of constant expressions" but that doesn't mean that a > > compiler is allowed to ignore the constraints in C17 6.7.9/4 nor the definition > > of an integer constant expression. So this ought to explicitly be a compiler > > extension and we ought to have a way to reliably compile strictly conforming > > programs with gcc without constraint violations silently getting ignored. > "integer constant expression" does not mean the same thing as "constant > expression of integer type". Yes, who said otherwise? Rather, this is the problem. Please check out the link I gave for the full reasoning including quotes. https://stackoverflow.com/questions/68252570/why-are-const-qualified-variables-accepted-as-initializers-on-gcc Specifically (C17 6.6): "An integer constant expression shall have integer type and shall only have operands that are integer constants, enumeration constants, character constants, sizeof expressions whose results are integer constants, _Alignof expressions, and floating constants that are the immediate operands of casts." In this code static const int y = 1; static int x = y; y is not an integer constant expression, nor is it an integer constant in the meaning that ISO 9899 defines it. Therefore an initializer was given which is not a constant expression. Therefore this is a constraint violation of C17 6.7.9/4 and a diagnostic must be issued. Therefore gcc is not conforming because of the "bug fix" carried out above. "an implementation may accept other forms of constant expressions" does not mean that an implementation can throw out any constraints it pleases out the window. Also the text "however, they are not an integer constant expression" added in C23 must have been added for a reason, such as misbehaving compilers.> static int x = y; > y is not an integer constant expression, nor is it an integer constant in the > meaning that ISO 9899 defines it. Correct, but irrelevant, since nothing in that code example is required by the standard to be an integer constant expression. > Therefore an initializer was given which is > not a constant expression. No, it's an "other form of constant expression" accepted by GCC. > "an implementation may accept other forms of constant expressions" does not > mean that an implementation can throw out any constraints it pleases out the > window. Correct. The Constraints on constant expressions say "Constant expressions shall not contain assignment, increment, decrement, function-call, or comma operators, except when they are contained within a subexpression that is not evaluated." and "Each constant expression shall evaluate to a constant that is in the range of representable values for its type.". The initializer is entirely consistent with those Constraints, so it is within the bounds of what an implementation may accept as an "other form of constant expression". Whereas it wouldn't be valid for an implementation to accept f() as a constant expression (contains a function call), for example. Note also that only violations of Syntax and Constraints require diagnostics (and thus -pedantic doesn't claim to ensure diagnostics for code that's not strictly conforming for some other reason than violating Syntax or Constraints).
(In reply to joseph@codesourcery.com from comment #24) > On Thu, 23 Feb 2023, daniel.lundin.mail at gmail dot com via Gcc-bugs wrote: Regardless of how one chose to read that part of the standard, fact remains that this part of the standard has not changed since C89 but still gcc behaves wildly different depending on version. This makes it impossible to port strictly conforming programs between different gcc versions, because then you'll either get a diagnostic that you (arguably) shouldn't be getting, or you will not get a diagnostic where you (arguably) should be expecting one. This makes gcc a hazard in my case. My choices are to forbid newer versions or to port to a different compiler. In case -pedantic would still result in a diagnostic past version 8 then that would make gcc behave consistently across versions and that would solve the problem. As was already mentioned previously in this thread, before this change was implemented with that remark ignored. Additionally there is bug #2 where (uint32_t)&function_pointer does not give a diagnostic in case the pointer is 32 bit, which is non-conforming behavior for constant expressions. I should perhaps open a separate bug report about that since I'm not sure if it's related to this one.