Bug 69960 - "initializer element is not constant"
Summary: "initializer element is not constant"
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c (show other bugs)
Version: 5.3.0
: P3 enhancement
Target Milestone: 8.0
Assignee: Not yet assigned to anyone
URL:
Keywords:
: 54823 82695 (view as bug list)
Depends on:
Blocks:
 
Reported: 2016-02-25 16:08 UTC by felix-gcc
Modified: 2021-09-27 20:42 UTC (History)
7 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2016-04-15 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description felix-gcc 2016-02-25 16:08:12 UTC
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.
Comment 1 Marek Polacek 2016-02-25 16:13:22 UTC
(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
Comment 2 felix-gcc 2016-02-25 17:39:21 UTC
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)
Comment 3 Jonathan Wakely 2016-02-25 18:33:37 UTC
The C standard says it's not a constant, but clang accepts it as an extension. That doesn't make it valid C though.
Comment 4 felix-gcc 2016-02-25 18:46:29 UTC
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?
Comment 5 Marek Polacek 2016-03-01 09:16:26 UTC
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.
Comment 6 Bernd Schmidt 2016-03-02 15:00:45 UTC
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.
Comment 7 Bernd Schmidt 2016-03-02 15:01:30 UTC
Cc'ing Jonathan in the hope he can answer the question.
Comment 8 Marek Polacek 2016-03-02 15:12:54 UTC
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.
Comment 9 Martin Sebor 2016-03-02 15:41:00 UTC
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.
Comment 10 Jonathan Wakely 2016-03-02 15:57:06 UTC
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.
Comment 11 Martin Sebor 2016-04-15 19:14:49 UTC
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).
Comment 12 Martin Sebor 2016-04-15 19:20:06 UTC
*** Bug 54823 has been marked as a duplicate of this bug. ***
Comment 13 Bernd Schmidt 2016-04-15 20:46:24 UTC
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]).
Comment 14 Jakub Jelinek 2017-05-11 10:56:33 UTC
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.
Comment 15 Jakub Jelinek 2017-05-11 11:02:11 UTC
(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.
Comment 16 Marek Polacek 2017-10-24 09:39:10 UTC
*** Bug 82695 has been marked as a duplicate of this bug. ***
Comment 17 Jakub Jelinek 2017-11-19 17:17:34 UTC
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
Comment 18 Jakub Jelinek 2017-12-01 08:41:50 UTC
Fixed for 8.1+.