Discussion:
What a strictly conforming program can do with constant expressions for initializers?
(too old to reply)
Woong Jun
2015-01-01 12:42:56 UTC
Permalink
What a strictly conforming program can do with constant expressions for initializers?

n1548 6.6p7 reads as:

More latitude is permitted for constant expressions in
initializers. Such a constant expression 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.

Note that the first two items are syntactic structures according to
their definitions so the "shall be" applies, while the "shall
evaluate to" applies to the 3rd one. The final one is a hybrid of
two.

I doubt that the standard clearly specifies which expressions can
or cannot appear in strictly conforming programs.

static int i, j = &i - &i; /* not s.c. */

The result of (&i - &i) does not change so that a compiler can
evaluate it, but the initializer for j is not allowed because it does
not fit to any of the allowed items; The arithmetic constant
expression can contain no pointer operands.

static int i, *p = &i + (&i-&i); /* not s.c. */

The expression added to an address constant &i is required to be an
integer constant expression and an ICE is not able to have pointers.

static int a[10], *p = &a[&a[0]-&a[0]]; /* ? */

Is this allowed because the standard allows [] to be used to
construct an address constant, or not because after operator
cancellation it evaluates to a+(&a[0]-&a[0])? If the former is the
case, what is the reason to disallow adding (&i-&i) to &i above even
if most compilers would use the same logic to accept those two
initializers?

The situation gets worse if a conditional expression involved:

static int i, *p = (&i-&i)? 0: &i;

Is a conforming implementation obliged to accept this code because it
is evaluated to an address expression? Exactly what restrictions
apply to the first operand of a conditional expression?

static int i, *p = (&i == 0)? 0: &i;
static int i, *p = ((_Bool)&i)? 0: &i;
static int a[10], *p = (&a[1]-&a[0])? 0: &i;
static int a[10], *p = (&a[1]-&a[0] > 1)? 0: &i;

Is these all not permitted? If all of these are permitted forms of
initializers, which requires a compiler to evaluate them in compile
time anyway, what is the benefit from disallowing the following ones?

static int i, *p = &i + (&i == 0);
static int i, *p = &i + (_Bool)&i;
static int a[10], *p = a + (&a[1]-&a[0]);
static int a[10], *p = a + (&a[1]-&a[0] > 1);
static int i, j = (&i == 0);
static int i, j = (_Bool)&i;
static int a[10], j = (&a[1]-&a[0]);
static int a[10], j = (&a[1]-&a[0] > 1);
--
Jun, Woong (woong.jun at gmail.com)
Web: http://code.woong.org
Derek M. Jones
2015-01-01 14:56:50 UTC
Permalink
Woong Jun,
Post by Woong Jun
I doubt that the standard clearly specifies which expressions can
or cannot appear in strictly conforming programs.
static int i, j = &i - &i; /* not s.c. */
Agree.
Post by Woong Jun
The result of (&i - &i) does not change so that a compiler can
This argument could be used to claim that:
sin(x)-sin(x)
is constant because its value does not change.

The rules for static initializers are driven by what existing
implementation technology will support. Addresses need not be
known to the compiler, which can rely on a linker to patch the
actual value or code that has that effect. Similarly linkers also
support address constants of the form: addr+/-offset
Post by Woong Jun
static int a[10], *p = &a[&a[0]-&a[0]]; /* ? */
Is this allowed because the standard allows [] to be used to
construct an address constant, or not because after operator
but &a[0]-&a[0] is not an integer constant expression.
Rewriting the code as: (&a[0]-&a[0])[a]
shows that [] is really just a common or garden binary operator.
Post by Woong Jun
static int i, *p = (&i-&i)? 0: &i;
No nothing changes. Not conforming.

static int i, j = 1 || (&i-&i);

is more interesting because (&i-&i) is not evaluated :-)
Woong Jun
2015-01-12 10:49:38 UTC
Permalink
Derek M. Jones wrote:
[...]

Thanks for your answer and sorry for the late reply. It took a while
to figure out the intent of the standard based on your answer and to
implement it on my compiler for testing.
Post by Derek M. Jones
Post by Woong Jun
The result of (&i - &i) does not change so that a compiler can
sin(x)-sin(x)
is constant because its value does not change.
Agreed that (&i - &i) should not be considered a constant.
Post by Derek M. Jones
The rules for static initializers are driven by what existing
implementation technology will support. Addresses need not be
known to the compiler, which can rely on a linker to patch the
actual value or code that has that effect. Similarly linkers also
support address constants of the form: addr+/-offset
My understanding is that once a pointer has been generated it cannot
be used to make an integer whether the pointer comes from a constant
or a symbol. There are two operations that makes an integer result
from pointers, pointer comparison and pointer subtraction; logical
operations with pointer operands and the only portable to-integer
cast are essentially a kind of pointer comparisons. Thus, those
operations are not allowed in an address constant, not to mention in
an arithmetic constant expression (ACE hereafter).
Post by Derek M. Jones
Post by Woong Jun
static int a[10], *p = &a[&a[0]-&a[0]]; /* ? */
Is this allowed because the standard allows [] to be used to
construct an address constant, or not because after operator
but &a[0]-&a[0] is not an integer constant expression.
Rewriting the code as: (&a[0]-&a[0])[a]
shows that [] is really just a common or garden binary operator.
Sounds reasonable.
Post by Derek M. Jones
Post by Woong Jun
static int i, *p = (&i-&i)? 0: &i;
No nothing changes. Not conforming.
Agreed, but the problem here is that the standard says nothing about
what is allowed in the first operand for static initializers of
pointer type. Permitting only ACEs looks reasonable but no idea how I
can draw that conclusion from the text.
Post by Derek M. Jones
static int i, j = 1 || (&i-&i);
is more interesting because (&i-&i) is not evaluated :-)
My interpretation is, and thus my compiler warns, that this is
non-conforming since an ACE is defined as a syntactic construct with
no operand of pointer type even when appearing in an unevaluated
part.
--
Jun, Woong (woong.jun at gmail.com)
Web: http://code.woong.org
Derek M. Jones
2015-01-13 13:03:48 UTC
Permalink
Post by Woong Jun
Post by Derek M. Jones
Post by Woong Jun
static int i, *p = (&i-&i)? 0: &i;
No nothing changes. Not conforming.
Agreed, but the problem here is that the standard says nothing about
what is allowed in the first operand for static initializers of
pointer type. Permitting only ACEs looks reasonable but no idea how I
can draw that conclusion from the text.
http://c0x.coding-guidelines.com/6.6.html, sentence 1345
"The semantic rules for the evaluation of a constant expression are the
same as for nonconstant expressions."

So the implementation just walks down the expression tree checking
the rules.
Post by Woong Jun
Post by Derek M. Jones
static int i, j = 1 || (&i-&i);
is more interesting because (&i-&i) is not evaluated :-)
My interpretation is, and thus my compiler warns, that this is
non-conforming since an ACE is defined as a syntactic construct with
no operand of pointer type even when appearing in an unevaluated
part.
The code is syntactically correct. ACE is defined as a semantic
construct.

sentence 1331:
"Such a constant expression shall be, or evaluate to, one of the
following: "

"(&i-&i)" is not a constant expression, and does not evaluate to one.
But in this context it is not evaluated.

Does sentence 1345 hold, so we don't care what "(&i-&i)" evaluates to,
or does 1331 hold and irrespective of whether it is evaluated the
effect of evaluation shall be whatever the requirement is?
Woong Jun
2015-01-16 14:46:41 UTC
Permalink
Derek M. Jones wrote:
[...]
Post by Derek M. Jones
Post by Woong Jun
Agreed, but the problem here is that the standard says nothing about
what is allowed in the first operand for static initializers of
pointer type. Permitting only ACEs looks reasonable but no idea how I
can draw that conclusion from the text.
http://c0x.coding-guidelines.com/6.6.html, sentence 1345
"The semantic rules for the evaluation of a constant expression are the
same as for nonconstant expressions."
So the implementation just walks down the expression tree checking
the rules.
I don't think it is the rules for evaluating expressions that matters
here; it is the rules, if any, for folding constants; to what extent
is a conforming implementation required to fold constants when
recognizing static initializers? It looks to me unclear from the text.
Post by Derek M. Jones
Post by Woong Jun
Post by Derek M. Jones
static int i, j = 1 || (&i-&i);
is more interesting because (&i-&i) is not evaluated :-)
My interpretation is, and thus my compiler warns, that this is
non-conforming since an ACE is defined as a syntactic construct with
no operand of pointer type even when appearing in an unevaluated
part.
The code is syntactically correct. ACE is defined as a semantic
construct.
Semantic probably because of the phrase "evaluate to". The ACE itself
is defined as a syntactic construct just as the ICE and null pointer
constant (NPC) are. Furthermore, as I recall, the phrase "(shall) be
or" was added before "evaluate to" by a DR that pointed out some of
the items allowed for static initializers were in fact syntactic
constructs. I guess that the wording for static initializers was
written with having in mind actual implementations that check the
constant-ness of an expression by looking at its operation codes after
parsing it and folding constants therein. What the standard failed is
to clearly specify the requirements for the "folding constants" part.
In addition to it, I dont't understand how an expression can be
_evaluated_ to something defined as a syntact entity if the "(shall)
evaluate to" is really intended to apply to ACEs. For example,

static int i = (&i == 0)? 1: 0;

is this valid because it eventually evaluates to something like an ACE
or not because (&i == 0) is not a constant expression? If the latter
holds, where does the standard gives implementations the right not to
fold it?
Post by Derek M. Jones
"Such a constant expression shall be, or evaluate to, one of the
following: "
"(&i-&i)" is not a constant expression, and does not evaluate to one.
But in this context it is not evaluated.
Before saying "unevaluated context", we need to find a way to explain
how an expression can be evaluated to a syntactic construct.
Post by Derek M. Jones
Does sentence 1345 hold, so we don't care what "(&i-&i)" evaluates to,
or does 1331 hold and irrespective of whether it is evaluated the
effect of evaluation shall be whatever the requirement is?
Or because only the "shall be" part applies, 1339 holds and it is not
simply qualified as an ACE? Seems not clear to me.

Another interesting example is:

static int b = (&b == 0);

Is this allowed or not? If not, how about this?

static _Bool b = &b;

If the standard endorses one and does not the other, where does the
difference come from?


--
Jun, Woong (woong.jun at gmail.com)
Web: http://code.woong.org
Derek M. Jones
2015-01-18 16:14:04 UTC
Permalink
Post by Woong Jun
Post by Derek M. Jones
The code is syntactically correct. ACE is defined as a semantic
construct.
Semantic probably because of the phrase "evaluate to". The ACE itself
is defined as a syntactic construct just as the ICE and null pointer
These constructs are described in a section that has the heading
"Semantics".
Post by Woong Jun
In addition to it, I dont't understand how an expression can be
_evaluated_ to something defined as a syntact entity if the "(shall)
evaluate to" is really intended to apply to ACEs. For example,
Good job it is defined in a Semantics section and not a Syntax section.
Post by Woong Jun
Post by Derek M. Jones
Does sentence 1345 hold, so we don't care what "(&i-&i)" evaluates to,
or does 1331 hold and irrespective of whether it is evaluated the
effect of evaluation shall be whatever the requirement is?
Or because only the "shall be" part applies, 1339 holds and it is not
simply qualified as an ACE? Seems not clear to me.
A DR me thinks.
Post by Woong Jun
static int b = (&b == 0);
Is this allowed or not? If not, how about this?
I don't see any chain of reasoning that might permit this.
Woong Jun
2015-01-19 02:28:03 UTC
Permalink
[...]
Post by Derek M. Jones
Post by Woong Jun
Semantic probably because of the phrase "evaluate to". The ACE itself
is defined as a syntactic construct just as the ICE and null pointer
These constructs are described in a section that has the heading
"Semantics".
Post by Woong Jun
In addition to it, I dont't understand how an expression can be
_evaluated_ to something defined as a syntact entity if the "(shall)
evaluate to" is really intended to apply to ACEs. For example,
Good job it is defined in a Semantics section and not a Syntax section.
I didn't meat that level of syntactic/semantic distinction. The
standard specifies, with no mentioning of unevaluated parts, which
lexical elements can appear within an ACE and which cannot. For
example, consider an ICE whose definition parallels to that of ACE:

static int i [1 || ((int)(3.14+0))];

Is this valid or not? All compilers I know of complain that the
given array size is not a constant just because it has a floating
addition in its unevaluated part. The definition of ICE also appears
in a "Semantics" section.

[...]
Post by Derek M. Jones
Post by Woong Jun
static int b = (&b == 0);
Is this allowed or not? If not, how about this?
I don't see any chain of reasoning that might permit this.
Both require an implementation to compare an address to a null
pointer and you said:

Addresses need not be known to the compiler, which can rely on a
linker to patch the actual value or code that has that effect.

A pointer-to-_Bool cast is nominally a conversion:

N1548 6.3.1.2 says:
When any scalar value is converted to _Bool, the result is 0 if
the value compares equal to 0; otherwise, the result is 1.


--
Jun, Woong (woong.jun at gmail.com)
Web: http://code.woong.org
Derek M. Jones
2015-01-20 14:55:52 UTC
Permalink
Post by Woong Jun
Post by Derek M. Jones
Good job it is defined in a Semantics section and not a Syntax section.
I didn't meat that level of syntactic/semantic distinction. The
standard specifies, with no mentioning of unevaluated parts, which
lexical elements can appear within an ACE and which cannot. For
static int i [1 || ((int)(3.14+0))];
http://c0x.coding-guidelines.com/6.6.html, sentence 1345:
"The semantic rules for the evaluation of a constant expression are the
same as for nonconstant expressions"

The rule "...floating constants that are the immediate operands of casts."
gets checked even though the rhs of || is not evaluated, just like
semantic checking of code that is executed.
Post by Woong Jun
Post by Derek M. Jones
Post by Woong Jun
static int b = (&b == 0);
Is this allowed or not? If not, how about this?
I don't see any chain of reasoning that might permit this.
Both require an implementation to compare an address to a null
Addresses need not be known to the compiler, which can rely on a
linker to patch the actual value or code that has that effect.
I did, but the committee failed to add my words of wisdom into the
standard ;-(
Woong Jun
2015-01-21 09:01:14 UTC
Permalink
Post by Derek M. Jones
Post by Woong Jun
Post by Derek M. Jones
Good job it is defined in a Semantics section and not a Syntax section.
I didn't meat that level of syntactic/semantic distinction. The
standard specifies, with no mentioning of unevaluated parts, which
lexical elements can appear within an ACE and which cannot. For
static int i [1 || ((int)(3.14+0))];
"The semantic rules for the evaluation of a constant expression are the
same as for nonconstant expressions"
The rule "...floating constants that are the immediate operands of casts."
gets checked even though the rhs of || is not evaluated, just like
semantic checking of code that is executed.
The same logic holds for the definition of ACE and thusly

static int i = 1 || (&i == 0);

cannot be an ACE if ignoring the "(shall) evaluate to" phrase. One
may argue that the example should be an ACE because it "evaluates to"
1 which itself is an ACE. This is one of things to be made clear
probably by a DR.
Post by Derek M. Jones
Post by Woong Jun
Post by Derek M. Jones
Post by Woong Jun
static int b = (&b == 0);
Is this allowed or not? If not, how about this?
I don't see any chain of reasoning that might permit this.
Both require an implementation to compare an address to a null
Addresses need not be known to the compiler, which can rely on a
linker to patch the actual value or code that has that effect.
I did, but the committee failed to add my words of wisdom into the
standard ;-(
I hope a DR makes a way to the standard for it.


--
Jun, Woong (woong.jun at gmail.com)
Web: http://code.woong.org
Tim Rentsch
2015-01-13 01:37:33 UTC
Permalink
Post by Woong Jun
What a strictly conforming program can do with constant
expressions for initializers?
More latitude is permitted for constant expressions in
initializers. Such a constant expression shall be, or evaluate to,
- 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.
Note that the first two items are syntactic structures according
to their definitions so the "shall be" applies, while the "shall
evaluate to" applies to the 3rd one. The final one is a hybrid of
two.
I doubt that the standard clearly specifies which expressions can
or cannot appear in strictly conforming programs.
static int i, j = &i - &i; /* not s.c. */
The result of (&i - &i) does not change so that a compiler can
evaluate it, but the initializer for j is not allowed because it
does not fit to any of the allowed items; The arithmetic constant
expression can contain no pointer operands.
static int i, *p = &i + (&i-&i); /* not s.c. */
The expression added to an address constant &i is required to be
an integer constant expression and an ICE is not able to have
pointers.
static int a[10], *p = &a[&a[0]-&a[0]]; /* ? */
Is this allowed because the standard allows [] to be used to
construct an address constant, or not because after operator
cancellation it evaluates to a+(&a[0]-&a[0])? If the former is
the case, what is the reason to disallow adding (&i-&i) to &i
above even if most compilers would use the same logic to accept
those two initializers?
static int i, *p = (&i-&i)? 0: &i;
Is a conforming implementation obliged to accept this code because
it is evaluated to an address expression? Exactly what
restrictions apply to the first operand of a conditional
expression?
static int i, *p = (&i == 0)? 0: &i;
static int i, *p = ((_Bool)&i)? 0: &i;
static int a[10], *p = (&a[1]-&a[0])? 0: &i;
static int a[10], *p = (&a[1]-&a[0] > 1)? 0: &i;
Is these all not permitted? If all of these are permitted forms
of initializers, which requires a compiler to evaluate them in
compile time anyway, what is the benefit from disallowing the
following ones?
static int i, *p = &i + (&i == 0);
static int i, *p = &i + (_Bool)&i;
static int a[10], *p = a + (&a[1]-&a[0]);
static int a[10], *p = a + (&a[1]-&a[0] > 1);
static int i, j = (&i == 0);
static int i, j = (_Bool)&i;
static int a[10], j = (&a[1]-&a[0]);
static int a[10], j = (&a[1]-&a[0] > 1);
High order bit: you make some good observations, and I agree the
Standard is not clear enough about what is allowed and what
isn't. To me there is enough ambiguity to merit a Defect
Report (and so I hope you will write one).

There are some detailed points in your comments that I think
might be argued. For example, what may be an arithmetic constant
expression? The phrase "or evaluate to" is troubling. Let's
suppose the predicate '&i == 0' can be evaluated at compile time.
If that is so, what should we conclude about an initializer such
as

&i == 0 ? 49 : sizeof i

This expression is not an ACE, but does it _evaluate_ to an ACE?
I think the point might be argued either way.

Also, what are the limits on what can be evaluated at compile
time? There are some difficult possibilities lurking here.
An example:

int a, b;
int *p = (&a + 1 == &b) == (&a + 1 != &b) ? &a : &b;

The predicate condition is always false (ie, zero). A compiler
that is smart enough could notice that, and decide that the
initializing expression could be evaluated at compile time
(evaluating to an address constant). Surely no one seriously
expects the Standard requires all conforming implementations
to exhibit this behavior, but what parts of the Standard support
that conclusion unambiguously?

I didn't go through all your examples in great detail, and I
expect there would be some other potentially arguable points
if I did. However these are minor issues. On the main point,
that the Standard does not adequately answer the question of
what the minimum bar is, I agree with you. More specifically,
I think the two main subquestions are, one, what are the limits
on expressions that "can be evaluated during translation rather
than runtime", and two, what does the phrase "or evalute to"
mean precisely?
Woong Jun
2015-01-16 14:47:41 UTC
Permalink
[...]
Post by Tim Rentsch
High order bit: you make some good observations, and I agree the
Standard is not clear enough about what is allowed and what
isn't. To me there is enough ambiguity to merit a Defect
Report (and so I hope you will write one).
Unfortunately, my national body to ISO/IEC JTC1 is not quite active
in regard to WG14 activities, so I doubt that they will file my
proposed defect report to deliver to ISO. Nevertheless, I think I
can prepare one even if it will be written in poor standardese when I
have spare time.
Post by Tim Rentsch
There are some detailed points in your comments that I think
might be argued. For example, what may be an arithmetic constant
expression? The phrase "or evaluate to" is troubling. Let's
suppose the predicate '&i == 0' can be evaluated at compile time.
If that is so, what should we conclude about an initializer such
as
&i == 0 ? 49 : sizeof i
This expression is not an ACE, but does it _evaluate_ to an ACE?
I think the point might be argued either way.
Agreed. As discussed in the other branch of this thread, the "(shall)
evaluate to" part is really troublesome.
Post by Tim Rentsch
Also, what are the limits on what can be evaluated at compile
time? There are some difficult possibilities lurking here.
int a, b;
int *p = (&a + 1 == &b) == (&a + 1 != &b) ? &a : &b;
The predicate condition is always false (ie, zero). A compiler
that is smart enough could notice that, and decide that the
initializing expression could be evaluated at compile time
(evaluating to an address constant). Surely no one seriously
expects the Standard requires all conforming implementations
to exhibit this behavior, but what parts of the Standard support
that conclusion unambiguously?
I didn't go through all your examples in great detail, and I
expect there would be some other potentially arguable points
if I did. However these are minor issues. On the main point,
that the Standard does not adequately answer the question of
what the minimum bar is, I agree with you. More specifically,
I think the two main subquestions are, one, what are the limits
on expressions that "can be evaluated during translation rather
than runtime", and two, what does the phrase "or evalute to"
mean precisely?
I couldn't agree more.

I believe this is what the standard intends to specify for static
initializers:

1. For static initializers of arithmetic types,
- only constant operands of arithmetic types are allowed; and
- only casts between arithmetic types are allowed.
2. For static initializers of pointer types,
- pointers can be generated starting from
- applying & to a static object or a function;
- implicit to-pointer conversion from arrays and functions;
- null pointer constant; or
- an integer cast to a pointer type
- and only the following operations can be applied if the stored
value is not accessed:
- indirection(*, []), address(&), member access(->, .),
- pointer +/- integer; for which, an ICE must be given as the
integer part; and
- pointer-to-pointer casts.
3. The item 1 should apply to the first operand of a conditional
expression.
4. Any unevaluated part of an expression is not subject to these
restrictions.

Even if I implemented according to the literal reading of the
standard (which differs from item 4, I believe), letting an
implementation ignore an unevaluated part of an expression helps to
catch non-portable initializers; inherited or synthesized flags
within an expression tree are necessary anyway to support C90 mode,
however.

Note that

static _Bool b = &b;

is precluded by item 1, and all pointer comparisons are not allowed.


--
Jun, Woong (woong.jun at gmail.com)
Web: http://code.woong.org
Wojtek Lerch
2015-01-16 21:20:40 UTC
Permalink
On 16-Jan-15 9:47 AM, Woong Jun wrote:
...
Post by Woong Jun
I believe this is what the standard intends to specify for static
1. For static initializers of arithmetic types,
- only constant operands of arithmetic types are allowed; and
- only casts between arithmetic types are allowed.
Where does the standard forbid the initializer for a _Bool to be an
address constant?
Post by Woong Jun
2. For static initializers of pointer types,
- pointers can be generated starting from
- applying & to a static object or a function;
- implicit to-pointer conversion from arrays and functions;
- null pointer constant; or
- an integer cast to a pointer type
- and only the following operations can be applied if the stored
- indirection(*, []), address(&), member access(->, .),
There should be a restriction on the arithmetic operand to [], but I
don't see where it's stated in the standard. Consider:

int arr[2], *ptr = &arr[ sin(0)==0 ];

The pointer is created explicitly using the unary & operator, points to
an object of static storage duration, and the value of an object is not
accessed by use of the [] operator. I doubt this was meant to be
allowed, but what restriction does it violate?
Post by Woong Jun
- pointer +/- integer; for which, an ICE must be given as the
integer part; and
- pointer-to-pointer casts.
3. The item 1 should apply to the first operand of a conditional
expression.
4. Any unevaluated part of an expression is not subject to these
restrictions.
Any unevaluated part, or just any operand to sizeof?

static int i = 0 && printf("Hello") ? printf("world!\n") : 0;
Post by Woong Jun
Even if I implemented according to the literal reading of the
standard (which differs from item 4, I believe), letting an
implementation ignore an unevaluated part of an expression helps to
catch non-portable initializers; inherited or synthesized flags
within an expression tree are necessary anyway to support C90 mode,
however.
Note that
static _Bool b = &b;
is precluded by item 1, and all pointer comparisons are not allowed.
But this is not a comparison, it's a conversion.
Woong Jun
2015-01-19 06:24:31 UTC
Permalink
Post by Wojtek Lerch
...
Post by Woong Jun
I believe this is what the standard intends to specify for static
1. For static initializers of arithmetic types,
- only constant operands of arithmetic types are allowed; and
- only casts between arithmetic types are allowed.
Where does the standard forbid the initializer for a _Bool to be an
address constant?
It does not forbid. The same effect can be achieved by removing from
my description the type restrictions for initializers. I dropped the
pointer-to-_Bool conversion because it looks inconsistent to me to
allow an address constant for a _Bool initializer disallowing
pointer comparisons.
Post by Wojtek Lerch
Post by Woong Jun
2. For static initializers of pointer types,
- pointers can be generated starting from
- applying & to a static object or a function;
- implicit to-pointer conversion from arrays and functions;
- null pointer constant; or
- an integer cast to a pointer type
- and only the following operations can be applied if the stored
- indirection(*, []), address(&), member access(->, .),
There should be a restriction on the arithmetic operand to [], but I
int arr[2], *ptr = &arr[ sin(0)==0 ];
The pointer is created explicitly using the unary & operator, points to
an object of static storage duration, and the value of an object is not
accessed by use of the [] operator. I doubt this was meant to be
allowed, but what restriction does it violate?
This is not clear from the text of the standard, which is one of the
reasons I started this thread. I think, however, it is convincible
that [] is a mere syntactic sugar for pointer addition and
indirection so the restriction for pointer addition is still imposed.
Post by Wojtek Lerch
Post by Woong Jun
- pointer +/- integer; for which, an ICE must be given as the
integer part; and
- pointer-to-pointer casts.
3. The item 1 should apply to the first operand of a conditional
expression.
4. Any unevaluated part of an expression is not subject to these
restrictions.
Any unevaluated part, or just any operand to sizeof?
static int i = 0 && printf("Hello") ? printf("world!\n") : 0;
Any unevaluated part including an operand of sizeof for consistency.
Differently from C90, C99 even allows assignments or function calls
to appear if not evaluated:

static int *p = 0 && ((void (*)())0)(); // valid in C99

I cannot tell, because of the unclear intent of the committee, if
this relaxation for unevaluated parts should also apply to ICEs.
Post by Wojtek Lerch
Post by Woong Jun
Even if I implemented according to the literal reading of the
standard (which differs from item 4, I believe), letting an
implementation ignore an unevaluated part of an expression helps to
catch non-portable initializers; inherited or synthesized flags
within an expression tree are necessary anyway to support C90 mode,
however.
Note that
static _Bool b = &b;
is precluded by item 1, and all pointer comparisons are not allowed.
But this is not a comparison, it's a conversion.
Yes, it's a conversion but nominally. The standard specifies
comparsion to 0 is performed for the conversion.


--
Jun, Woong (woong.jun at gmail.com)
Web: http://code.woong.org
Wojtek Lerch
2015-01-19 21:44:11 UTC
Permalink
Post by Woong Jun
Post by Wojtek Lerch
...
Post by Woong Jun
I believe this is what the standard intends to specify for static
1. For static initializers of arithmetic types,
- only constant operands of arithmetic types are allowed; and
- only casts between arithmetic types are allowed.
Where does the standard forbid the initializer for a _Bool to be an
address constant?
It does not forbid. The same effect can be achieved by removing from
my description the type restrictions for initializers. I dropped the
pointer-to-_Bool conversion because it looks inconsistent to me to
allow an address constant for a _Bool initializer disallowing
pointer comparisons.
Are you saying that the standard allows them unintentionally?

I guess it's not impossible that the Committee simply forgot to add
words to specifically forbid using an address constant to initialize a
_Bool. But personally I see it as consistent with the general rules of
initializers, which allow pointers to initialize pointers or _Bools but
not any other integers. For sure, the inconsistency between _Bool and
other integers is there on purpose, and I don't think adding words to
make static _Bools more consistent with other static integers, but less
consistent with automatic _Bools, would add much value to the standard.

Comparison operators involving pointers are disallowed in all static
initializers, so I don't see much of an inconsistency there either.
Post by Woong Jun
Post by Wojtek Lerch
Post by Woong Jun
2. For static initializers of pointer types,
- pointers can be generated starting from
- applying & to a static object or a function;
- implicit to-pointer conversion from arrays and functions;
- null pointer constant; or
- an integer cast to a pointer type
- and only the following operations can be applied if the stored
- indirection(*, []), address(&), member access(->, .),
There should be a restriction on the arithmetic operand to [], but I
int arr[2], *ptr = &arr[ sin(0)==0 ];
The pointer is created explicitly using the unary & operator, points to
an object of static storage duration, and the value of an object is not
accessed by use of the [] operator. I doubt this was meant to be
allowed, but what restriction does it violate?
This is not clear from the text of the standard, which is one of the
reasons I started this thread. I think, however, it is convincible
that [] is a mere syntactic sugar for pointer addition and
indirection so the restriction for pointer addition is still imposed.
But my concern was not specifically about the [] operator but generally
about integer subexpressions in address constants. The expression
"arr+(sin(0)==0)", just like the expression "&arr[sin(0)==0]", is a
pointer to an object of static storage duration; but it is not an
address constant, is it? But why not?
Post by Woong Jun
Post by Wojtek Lerch
Post by Woong Jun
- pointer +/- integer; for which, an ICE must be given as the
integer part; and
- pointer-to-pointer casts.
3. The item 1 should apply to the first operand of a conditional
expression.
4. Any unevaluated part of an expression is not subject to these
restrictions.
Any unevaluated part, or just any operand to sizeof?
static int i = 0 && printf("Hello") ? printf("world!\n") : 0;
Any unevaluated part including an operand of sizeof for consistency.
But the standard does not specify an exemption for any unevaluated parts
other than operands of sizeof; are you saying that's unintentional?
Post by Woong Jun
Differently from C90, C99 even allows assignments or function calls
static int *p = 0 && ((void (*)())0)(); // valid in C99
Does it? As far as I can tell, the above is not a null pointer, a
pointer to an lvalue designating an object of static storage duration,
or a pointer to a function designator. What makes it an address constant?

...
Post by Woong Jun
Post by Wojtek Lerch
Post by Woong Jun
Even if I implemented according to the literal reading of the
standard (which differs from item 4, I believe), letting an
Ah, I just noticed this part; so you do agree that the literal meaning
disagrees with your item 4, but you believe that that's unintentional.
Correct?
Post by Woong Jun
Post by Wojtek Lerch
Post by Woong Jun
implementation ignore an unevaluated part of an expression helps to
catch non-portable initializers; inherited or synthesized flags
within an expression tree are necessary anyway to support C90 mode,
however.
Note that
static _Bool b = &b;
is precluded by item 1, and all pointer comparisons are not allowed.
But this is not a comparison, it's a conversion.
Yes, it's a conversion but nominally. The standard specifies
comparsion to 0 is performed for the conversion.
My point was that a comparison operator is not involved. When you wrote
"all pointer comparisons are not allowed", did you mean comparison
operators, or did you mean all operations whose descriptions use the
phrase "compares equal"?

Also, even if that conversion did count as a "comparison", I don't see
why that would affect the definition of "address constant". The address
constant evaluates to an address (or a null pointer); the conversion to
_Bool happens afterwards, and is not part of the address constant.
Woong Jun
2015-01-20 04:36:53 UTC
Permalink
Post by Wojtek Lerch
Post by Woong Jun
Post by Wojtek Lerch
...
Post by Woong Jun
I believe this is what the standard intends to specify for static
1. For static initializers of arithmetic types,
- only constant operands of arithmetic types are allowed; and
- only casts between arithmetic types are allowed.
Where does the standard forbid the initializer for a _Bool to be an
address constant?
It does not forbid. The same effect can be achieved by removing from
my description the type restrictions for initializers. I dropped the
pointer-to-_Bool conversion because it looks inconsistent to me to
allow an address constant for a _Bool initializer disallowing
pointer comparisons.
Are you saying that the standard allows them unintentionally?
No. I'm saying I dropped them because I just thought them
inconsistent with the policy that I believed to exist behind the spec
for static initializers, that is, compilers need not know about
addresses. This never means that the committee mistakenly allowed an
address constant for _Bool initializers.
Post by Wojtek Lerch
I guess it's not impossible that the Committee simply forgot to add
words to specifically forbid using an address constant to initialize a
_Bool.
Agreed.
Post by Wojtek Lerch
But personally I see it as consistent with the general rules of
initializers, which allow pointers to initialize pointers or _Bools but
not any other integers. For sure, the inconsistency between _Bool and
other integers is there on purpose, and I don't think adding words to
make static _Bools more consistent with other static integers, but less
consistent with automatic _Bools, would add much value to the standard.
You made a good point here. I agree with you that the inconsistency
from allowing an address for static _Bool can be seen as coming from
the inconsistency between _Bool and other integers, so no significant
conflict with the general rules of initializers.
Post by Wojtek Lerch
Comparison operators involving pointers are disallowed in all static
initializers, so I don't see much of an inconsistency there either.
Post by Woong Jun
Post by Wojtek Lerch
Post by Woong Jun
2. For static initializers of pointer types,
- pointers can be generated starting from
- applying & to a static object or a function;
- implicit to-pointer conversion from arrays and functions;
- null pointer constant; or
- an integer cast to a pointer type
- and only the following operations can be applied if the stored
- indirection(*, []), address(&), member access(->, .),
There should be a restriction on the arithmetic operand to [], but I
int arr[2], *ptr = &arr[ sin(0)==0 ];
The pointer is created explicitly using the unary & operator, points to
an object of static storage duration, and the value of an object is not
accessed by use of the [] operator. I doubt this was meant to be
allowed, but what restriction does it violate?
This is not clear from the text of the standard, which is one of the
reasons I started this thread. I think, however, it is convincible
that [] is a mere syntactic sugar for pointer addition and
indirection so the restriction for pointer addition is still imposed.
But my concern was not specifically about the [] operator but generally
about integer subexpressions in address constants. The expression
"arr+(sin(0)==0)", just like the expression "&arr[sin(0)==0]", is a
pointer to an object of static storage duration; but it is not an
address constant, is it? But why not?
I don't think you are asking a practical reason why only ICEs are
permitted as the integer part for pointer addition on most compilers.
Are you saying that, with the current wording of the standard,
(arr+(sin(0)==0)) is a valid address constant because it is evaluated
to a pointer to a static object? Or that the wording in problem
should change to endorse that expression in the next revision?

If the former is the case, in order to preclude such an expression,
the wording should be revised to separately specify how addresses
(pointers) can be generated and which operations are allowed to
apply to them.

If the latter, the current restriction on ICE looks reasonable
considering practical implementations for static initializers.

[...]
Post by Wojtek Lerch
Post by Woong Jun
Post by Wojtek Lerch
Any unevaluated part, or just any operand to sizeof?
static int i = 0 && printf("Hello") ? printf("world!\n") : 0;
Any unevaluated part including an operand of sizeof for consistency.
But the standard does not specify an exemption for any unevaluated parts
other than operands of sizeof; are you saying that's unintentional?
Yes, I think it is because allowing unevaluated parts to be
ignored eases implementations; and I believe this is why we got

6.6p3 Constraints:
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.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

instead of similar one to C90's. I know that the wording change from
"an operand of sizeof" to "an unevaluated subexpression" originally
came from covering VLAs, but IIRC the committee intentionally left
the wording untouched even if it was indicated that the wording
should be revised to "within an operand of sizeof that is not
evaluated" or something like that.
Post by Wojtek Lerch
Post by Woong Jun
Differently from C90, C99 even allows assignments or function calls
static int *p = 0 && ((void (*)())0)(); // valid in C99
Does it? As far as I can tell, the above is not a null pointer, a
pointer to an lvalue designating an object of static storage duration,
or a pointer to a function designator. What makes it an address constant?
Ooops, you're correct. The example should read as:

static int i, *p = (1)? &i: ((int *(*)())0)(); // valid in C99

An address constant is a semantic entity (as opposed to ICE or ACE)
and thus "(shall) evaluate to" applies.
Post by Wojtek Lerch
...
Post by Woong Jun
Post by Wojtek Lerch
Post by Woong Jun
Even if I implemented according to the literal reading of the
standard (which differs from item 4, I believe), letting an
Ah, I just noticed this part; so you do agree that the literal meaning
disagrees with your item 4, but you believe that that's unintentional.
Correct?
Yes. My literal reading of the standard is implemented in my
compiler and it gives this result in regard to unevaluated parts of
an expression:

static int *p = 0 && (3.14+0); /* invalid */
static int i, *q = (1)? &i: ((int *(*)())0)(); /* valid */
static int j = 1 || (&i == 0); /* invalid */

Other major compilers silently accept the last one; I don't know if
it is intentional.

[...]
Post by Wojtek Lerch
Post by Woong Jun
Post by Wojtek Lerch
Post by Woong Jun
Note that
static _Bool b = &b;
is precluded by item 1, and all pointer comparisons are not allowed.
But this is not a comparison, it's a conversion.
Yes, it's a conversion but nominally. The standard specifies
comparsion to 0 is performed for the conversion.
"all pointer comparisons are not allowed", did you mean comparison
operators, or did you mean all operations whose descriptions use the
phrase "compares equal"?
The latter based on the policy I said above.


Thanks.
--
Jun, Woong (woong.jun at gmail.com)
Web: http://code.woong.org
Wojtek Lerch
2015-01-21 04:49:01 UTC
Permalink
Post by Woong Jun
Post by Wojtek Lerch
Post by Woong Jun
Post by Wojtek Lerch
...
Post by Woong Jun
I believe this is what the standard intends to specify for static
1. For static initializers of arithmetic types,
- only constant operands of arithmetic types are allowed; and
- only casts between arithmetic types are allowed.
Where does the standard forbid the initializer for a _Bool to be an
address constant?
It does not forbid. The same effect can be achieved by removing from
my description the type restrictions for initializers. I dropped the
pointer-to-_Bool conversion because it looks inconsistent to me to
allow an address constant for a _Bool initializer disallowing
pointer comparisons.
Are you saying that the standard allows them unintentionally?
No. I'm saying I dropped them because I just thought them
inconsistent with the policy that I believed to exist behind the spec
for static initializers, that is, compilers need not know about
addresses. This never means that the committee mistakenly allowed an
address constant for _Bool initializers.
My understanding is that the policy behind address constants was to
match the lowest common denominator of what linkers were capable of
doing -- which was compute the address of a symbol plus a constant. But
in an initializer for _Bool, the compiler doesn't need the linker's
help, because all it needs to know is whether the value is a null
pointer or not.
Post by Woong Jun
Post by Wojtek Lerch
I guess it's not impossible that the Committee simply forgot to add
words to specifically forbid using an address constant to initialize a
_Bool.
Agreed.
... but if it did occur to them, I can't think of a good reason to
consider it important enough to add more words to the text.

[...]
Post by Woong Jun
Post by Wojtek Lerch
Post by Woong Jun
Post by Wojtek Lerch
There should be a restriction on the arithmetic operand to [], but I
int arr[2], *ptr = &arr[ sin(0)==0 ];
The pointer is created explicitly using the unary & operator, points to
an object of static storage duration, and the value of an object is not
accessed by use of the [] operator. I doubt this was meant to be
allowed, but what restriction does it violate?
This is not clear from the text of the standard, which is one of the
reasons I started this thread. I think, however, it is convincible
that [] is a mere syntactic sugar for pointer addition and
indirection so the restriction for pointer addition is still imposed.
But my concern was not specifically about the [] operator but generally
about integer subexpressions in address constants. The expression
"arr+(sin(0)==0)", just like the expression "&arr[sin(0)==0]", is a
pointer to an object of static storage duration; but it is not an
address constant, is it? But why not?
I don't think you are asking a practical reason why only ICEs are
permitted as the integer part for pointer addition on most compilers.
Correct: I think I know what the practical reason is. My concern is
that I don't see where that restriction is stated in the standard.
Post by Woong Jun
Are you saying that, with the current wording of the standard,
(arr+(sin(0)==0)) is a valid address constant because it is evaluated
to a pointer to a static object? Or that the wording in problem
should change to endorse that expression in the next revision?
No, I was concerned that (arr+(sin(0)==0)) was a valid address constant
according to the text, even though I don't think it should be; but I
think I was mistaken, because it violates 6.6#3.

But I can think of other examples that don't violate 6.6#3 or any other
words of the standard (as far as I can tell) but I strongly suspect that
they were not meant to be considered valid address constants. Here's an
one that GCC rejects:

extern int arr1[5], arr2[5];
static int *ptr = &arr1[ arr1+5==arr2 ];

If there some requirement that I haven't noticed this violates?
Post by Woong Jun
If the former is the case, in order to preclude such an expression,
the wording should be revised to separately specify how addresses
(pointers) can be generated and which operations are allowed to
apply to them.
Yes, I think that would be nice. Personally I don't see a good reason
to allow anything other than an ICE in the integer part of an address
constant. That would be consistent with the fact that an initializer
can be an adress constant plus minus an ICE -- allowing more freedom to
the integer part of the address constant than to the integer added to it
doesn't make sense to me.
Post by Woong Jun
If the latter, the current restriction on ICE looks reasonable
considering practical implementations for static initializers.
It looks invisible to me... Can you help me find it?

Or is your interpretation that "&arr[exp]" used as an initializer must
be interpreted as address constant "arr" plus ICE "exp", as opposed to
the whole "&arr[exp]" being an address constant with no separate ICE
added to it? I have to say that I see no support in the standard for
such an interpretation (even though I think it would be a very
reasonable requirement).
Post by Woong Jun
[...]
Post by Wojtek Lerch
Post by Woong Jun
Post by Wojtek Lerch
Any unevaluated part, or just any operand to sizeof?
static int i = 0 && printf("Hello") ? printf("world!\n") : 0;
Any unevaluated part including an operand of sizeof for consistency.
But the standard does not specify an exemption for any unevaluated parts
other than operands of sizeof; are you saying that's unintentional?
Yes, I think it is because allowing unevaluated parts to be
ignored eases implementations; and I believe this is why we got
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.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
I don't know; the compiler has to completely parse the syntax anyway.
Having to know that the constraint only applies to certain parts of the
expression sounds like a complication to me, not a simplification. But
I guess that may depend on how you write your compiler.
Post by Woong Jun
instead of similar one to C90's. I know that the wording change from
"an operand of sizeof" to "an unevaluated subexpression" originally
came from covering VLAs, but IIRC the committee intentionally left
the wording untouched even if it was indicated that the wording
should be revised to "within an operand of sizeof that is not
evaluated" or something like that.
"A sizeof operator whose result is an integer constant" is the wording
used in 6.6#8; I find it messy because it's a reference to a distant and
non-trivial part of the spec, but it does the job.
Post by Woong Jun
Post by Wojtek Lerch
Post by Woong Jun
Differently from C90, C99 even allows assignments or function calls
static int *p = 0 && ((void (*)())0)(); // valid in C99
Does it? As far as I can tell, the above is not a null pointer, a
pointer to an lvalue designating an object of static storage duration,
or a pointer to a function designator. What makes it an address constant?
static int i, *p = (1)? &i: ((int *(*)())0)(); // valid in C99
Yes, since the function call is not evaluated, the constraint of 6.6#3
does not apply here, and therefore I have to admit, reluctantly, that
this example is valid. Personally I would prefer sizeof to be the only
exception allowing function calls.
Post by Woong Jun
An address constant is a semantic entity (as opposed to ICE or ACE)
and thus "(shall) evaluate to" applies.
I'm not sure if I understand what a "semantic entity" is. Is an address
constant not an expression in the source code?

My impression has been that the standard is generally not very pedantic
about the distinction between expressions and their values, and I have
always assumed that the definition of "address constant" just talks
about expressions in a somewhat sloppy way. Obviously, there can't be
such a thing as "a pointer to an lvalue" or "a pointer to a function
designator" -- lvalues and function designators are expressions, and a
pointer can't point to a sequence of tokens in the source code. I have
always assumed that "a pointer to X" in this definition really means the
expression &X -- is that an oversimplification?

(I have a similar issue with the descriptions of the various operators
saying that the *result* of the operator, as opposed to the expression
containing it, is an lvalue. I have always assumed that that was
because the text wasn't completely translated from C90, where "lvalue"
was in fact defined as the result of an expression (the identity of the
object that the expression designates) rather than the expression
itself. The same excuse would apply to what I see as a slopiness in the
wording here as well. Am I completely misinterpreting this?)

[...]
Post by Woong Jun
My literal reading of the standard is implemented in my
compiler and it gives this result in regard to unevaluated parts of
static int *p = 0 && (3.14+0); /* invalid */
static int i, *q = (1)? &i: ((int *(*)())0)(); /* valid */
static int j = 1 || (&i == 0); /* invalid */
Other major compilers silently accept the last one; I don't know if
it is intentional.
Intentional or not, the compiler has an excuse: 6.6#10 "An
implementation may accept other forms of constant expressions." Also,
because the requirement that an initializer shall be an ACE is not a
constraint, violating it triggers undefined behaviour without a
mandatory diagnostic, doesn't it?

Incidentally, I just recently ran into a case where GCC surprised me by
happily compiling code similar to this:

static const int a = offsetof(x,y);
static const int b = a + offsetof(p,q);

except when I turned off optimization. I was thinking about it as a
compiler bug until I realized that 6.6#10 makes it a feature...
Post by Woong Jun
[...]
Post by Wojtek Lerch
Post by Woong Jun
Post by Wojtek Lerch
Post by Woong Jun
Note that
static _Bool b = &b;
is precluded by item 1, and all pointer comparisons are not allowed.
But this is not a comparison, it's a conversion.
Yes, it's a conversion but nominally. The standard specifies
comparsion to 0 is performed for the conversion.
"all pointer comparisons are not allowed", did you mean comparison
operators, or did you mean all operations whose descriptions use the
phrase "compares equal"?
The latter based on the policy I said above.
I'm sorry but I'm not sure which policy you're referring to... I
understand that comparison operators are not on your list of allowed
operators (even though they're not on C99's list of forbidden
operators), and I have to admit that I'm too lazy to actually search the
standard for all occurrences of "compare equal", but here we are talking
about a conversion that is applied to the value of the address constant,
and its validity is independent of the operations that are allowed in an
address constant.
Woong Jun
2015-01-21 08:36:01 UTC
Permalink
[...]
Post by Wojtek Lerch
Post by Woong Jun
Post by Wojtek Lerch
Are you saying that the standard allows them unintentionally?
No. I'm saying I dropped them because I just thought them
inconsistent with the policy that I believed to exist behind the spec
for static initializers, that is, compilers need not know about
addresses. This never means that the committee mistakenly allowed an
address constant for _Bool initializers.
My understanding is that the policy behind address constants was to
match the lowest common denominator of what linkers were capable of
doing -- which was compute the address of a symbol plus a constant. But
in an initializer for _Bool, the compiler doesn't need the linker's
help, because all it needs to know is whether the value is a null
pointer or not.
The same goes for (&i == 0), too. From the specifiction of the
standard, a compiler can fold, with no help from a linker,
(&a == &b), (&a[0] == &a[1]) and other complicated pointer
comparisons. Where can a conforming implementation stop such a
constant folding? That should be precisely defined by the standard.

[...]
Post by Wojtek Lerch
No, I was concerned that (arr+(sin(0)==0)) was a valid address constant
according to the text, even though I don't think it should be; but I
think I was mistaken, because it violates 6.6#3.
But I can think of other examples that don't violate 6.6#3 or any other
words of the standard (as far as I can tell) but I strongly suspect that
they were not meant to be considered valid address constants. Here's an
extern int arr1[5], arr2[5];
static int *ptr = &arr1[ arr1+5==arr2 ];
If there some requirement that I haven't noticed this violates?
There aren't. This simply means that the literal reading of the
standard fails to carry its intention clearly.
Post by Wojtek Lerch
Post by Woong Jun
If the former is the case, in order to preclude such an expression,
the wording should be revised to separately specify how addresses
(pointers) can be generated and which operations are allowed to
apply to them.
Yes, I think that would be nice. Personally I don't see a good reason
to allow anything other than an ICE in the integer part of an address
constant. That would be consistent with the fact that an initializer
can be an adress constant plus minus an ICE -- allowing more freedom to
the integer part of the address constant than to the integer added to it
doesn't make sense to me.
Couldn't agree more.
Post by Wojtek Lerch
Post by Woong Jun
If the latter, the current restriction on ICE looks reasonable
considering practical implementations for static initializers.
It looks invisible to me... Can you help me find it?
Or is your interpretation that "&arr[exp]" used as an initializer must
be interpreted as address constant "arr" plus ICE "exp", as opposed to
the whole "&arr[exp]" being an address constant with no separate ICE
added to it?
Yes. Here I'm talking about the intent not the literal reading of the
text. I should have been clearer.
Post by Wojtek Lerch
I have to say that I see no support in the standard for
such an interpretation (even though I think it would be a very
reasonable requirement).
Agreed. The text of the standard reads as allowing everything as long
as it evaluates to a pointer to an object or a function for an
address constant, even if that should not be the intent, I believe.

[...]
Post by Wojtek Lerch
Post by Woong Jun
An address constant is a semantic entity (as opposed to ICE or ACE)
and thus "(shall) evaluate to" applies.
I'm not sure if I understand what a "semantic entity" is. Is an address
constant not an expression in the source code?
Sorry for insufficient explanation for my interpretation.

My understanding for static initializers is:

For ACE that is defined as specifying what operands an ACE can have,
the "shall be" applies, so an expression cannot be an ACE if, say,
a pointer appears in its unevaluated part.

On the other hand, the standard defines an address constant by
enumerating what it should be after evaluation, so "(shall) evaluated
to" applies, which gives more latitute for unevaluated parts.

This is just my interpretation and others may argue differently,
which proves that a DR is in order.
Post by Wojtek Lerch
My impression has been that the standard is generally not very pedantic
about the distinction between expressions and their values, and I have
always assumed that the definition of "address constant" just talks
about expressions in a somewhat sloppy way. Obviously, there can't be
such a thing as "a pointer to an lvalue" or "a pointer to a function
designator" -- lvalues and function designators are expressions, and a
pointer can't point to a sequence of tokens in the source code. I have
always assumed that "a pointer to X" in this definition really means the
expression &X -- is that an oversimplification?
I don't think so.
Post by Wojtek Lerch
(I have a similar issue with the descriptions of the various operators
saying that the *result* of the operator, as opposed to the expression
containing it, is an lvalue. I have always assumed that that was
because the text wasn't completely translated from C90, where "lvalue"
was in fact defined as the result of an expression (the identity of the
object that the expression designates) rather than the expression
itself. The same excuse would apply to what I see as a slopiness in the
wording here as well. Am I completely misinterpreting this?)
I think your interpretation is correct.
Post by Wojtek Lerch
[...]
Post by Woong Jun
My literal reading of the standard is implemented in my
compiler and it gives this result in regard to unevaluated parts of
static int *p = 0 && (3.14+0); /* invalid */
static int i, *q = (1)? &i: ((int *(*)())0)(); /* valid */
static int j = 1 || (&i == 0); /* invalid */
Other major compilers silently accept the last one; I don't know if
it is intentional.
Intentional or not, the compiler has an excuse: 6.6#10 "An
implementation may accept other forms of constant expressions."
As I recall, the intended purpose of this wording is to allow for an
expression from, say, expansion of offsetof, which cannot be a valid
constant expression otherwise. I think there is a C90 DR for this
issue to clearly specify the wording is not intended to give an
implementation latitute to silently accept arbitrary expressions as
constant expressions if it wants.
Post by Wojtek Lerch
Also,
because the requirement that an initializer shall be an ACE is not a
constraint, violating it triggers undefined behaviour without a
mandatory diagnostic, doesn't it?
Agreed.
Post by Wojtek Lerch
Incidentally, I just recently ran into a case where GCC surprised me by
static const int a = offsetof(x,y);
static const int b = a + offsetof(p,q);
except when I turned off optimization. I was thinking about it as a
compiler bug until I realized that 6.6#10 makes it a feature...
Post by Woong Jun
[...]
Post by Wojtek Lerch
"all pointer comparisons are not allowed", did you mean comparison
operators, or did you mean all operations whose descriptions use the
phrase "compares equal"?
The latter based on the policy I said above.
I'm sorry but I'm not sure which policy you're referring to...
Sorry for the unclarity. I meant that a compiler need not know about
addresses.
Post by Wojtek Lerch
I
understand that comparison operators are not on your list of allowed
operators (even though they're not on C99's list of forbidden
operators), and I have to admit that I'm too lazy to actually search the
standard for all occurrences of "compare equal", but here we are talking
about a conversion that is applied to the value of the address constant,
and its validity is independent of the operations that are allowed in an
address constant.
Allowing an address for a _Bool initializer itself is not the
problem. Even if the wording for conversion to _Bool contains pointer
comparison, I see no practical problem in allowing it for static
initializers since it can be argued that it is a conversion anyway
and even no cast is necessary. The real problem is, I think, that the
standard fails to specify what expressions a compiler is required to
fold and what expressions it is allowed to reject to fold.


--
Jun, Woong (woong.jun at gmail.com)
Web: http://code.woong.org
Wojtek Lerch
2015-01-22 04:49:42 UTC
Permalink
[...]
Post by Woong Jun
Post by Wojtek Lerch
My understanding is that the policy behind address constants was to
match the lowest common denominator of what linkers were capable of
doing -- which was compute the address of a symbol plus a constant. But
in an initializer for _Bool, the compiler doesn't need the linker's
help, because all it needs to know is whether the value is a null
pointer or not.
The same goes for (&i == 0), too. From the specifiction of the
standard, a compiler can fold, with no help from a linker,
(&a == &b), (&a[0] == &a[1]) and other complicated pointer
comparisons. Where can a conforming implementation stop such a
constant folding? That should be precisely defined by the standard.
For integer initializers, it is -- an arithmetic constant expression
can't contain an identifier or anything else that could be used to
produce a pointer.

For address constants, it seems that you and I agree that it should be
restricted to produce (informally speaking) the address of a static
object plus a constant, and that that restriction is missing from the
text of the standard.

If I had to come up with a more formal definition, I think I'd define it
recursively using a helper term that we could call an "lvalue constant
expression" -- something like this:

An lvalue constant expression is one of the following:

* an identifier for an object with a static storage duration,
* a unary * operator with an operand that is an address constant
* a [] operator with an address constant and an arithmetic constant
expression as its operands,
* a . operator with an lvalue constant expression as its left operand
* a -> operator with an address constant as its left operand

An address constant is one of the following:

* A null pointer constant
* An arithmetic constant expression cast to a pointer type
* An address constant cast to a pointer type
* A unary & with an operand that is an lvalue constant expression
* An lvalue constant expression with an array type that is not the
operand of a unary &
* The name of a function, with or without with a unary & applied to it
* A binary + or - with an address constant and an arithmetic constant
expression as its operands

Have I missed anything?

[...]
Post by Woong Jun
For ACE that is defined as specifying what operands an ACE can have,
the "shall be" applies, so an expression cannot be an ACE if, say,
a pointer appears in its unevaluated part.
On the other hand, the standard defines an address constant by
enumerating what it should be after evaluation, so "(shall) evaluated
to" applies, which gives more latitute for unevaluated parts.
And, as we have agreed, it also gives an unreasonable amount of latitude
to the integer operand of [], by forgetting to impose any restrictions
on it (other than the constraints of 6.6p3).

On the other hand, 6.6p8 doesn't mention that adding an integer to a
pointer is allowed. 6.6p7 does, but that's not quite the same -- it
seems that the addition is only allowed after the address constant, not
inside it:

struct x { int m[2]; };
char buf[N];
int *p = ( (struct x*)( &buf[4] ) ) -> m + 1; // ok
int *q = ( (struct x*)( buf + 4 ) ) -> m + 1; // bad
dbrower
2015-01-31 20:06:18 UTC
Permalink
I recently ran into what seemed like a surprise with a compiler on what I had previously taken to be a legal constant expression.

#define STRCONST "abcd"

static const unsigned int magic = ( (unsigned)STRCONST[0] << 24 |
(unsigned)STRCONST[1] << 16 |
(unsigned)STRCONST[2] << 8 |
(unsigned)STRCONST[4] );

A particular compiler complained about this being non-constant.

What is in error, the code or the compiler?

thx,
-dB
Wojtek Lerch
2015-01-31 21:41:45 UTC
Permalink
Post by dbrower
I recently ran into what seemed like a surprise with a compiler on what I had previously taken to be a legal constant expression.
#define STRCONST "abcd"
static const unsigned int magic = ( (unsigned)STRCONST[0] << 24 |
(unsigned)STRCONST[1] << 16 |
(unsigned)STRCONST[2] << 8 |
(unsigned)STRCONST[4] );
A particular compiler complained about this being non-constant.
What is in error, the code or the compiler?
The compiler is right. An initializer for a static integer is required
to be an "arithmetic constant expression". Here's how it's defined in C99:

6.6#8 An arithmetic constant expression shall have arithmetic type and
shall only have operands that are integer constants, floating constants,
enumeration constants, character constants, and sizeof expressions. Cast
operators in an arithmetic constant expression
shall only convert arithmetic types to arithmetic types, except as part
of an operand to a sizeof operator whose result is an integer constant.

Note that string literals are not allowed.
Dave Brower
2015-02-01 19:36:33 UTC
Permalink
Thanks. Still learning stuff bout this small language after using it for 30 years.

-dB
Post by Wojtek Lerch
Post by dbrower
I recently ran into what seemed like a surprise with a compiler on what I had previously taken to be a legal constant expression.
#define STRCONST "abcd"
static const unsigned int magic = ( (unsigned)STRCONST[0] << 24 |
(unsigned)STRCONST[1] << 16 |
(unsigned)STRCONST[2] << 8 |
(unsigned)STRCONST[4] );
A particular compiler complained about this being non-constant.
What is in error, the code or the compiler?
The compiler is right. An initializer for a static integer is required
6.6#8 An arithmetic constant expression shall have arithmetic type and
shall only have operands that are integer constants, floating constants,
enumeration constants, character constants, and sizeof expressions. Cast
operators in an arithmetic constant expression
shall only convert arithmetic types to arithmetic types, except as part
of an operand to a sizeof operator whose result is an integer constant.
Note that string literals are not allowed.
Woong Jun
2015-02-03 06:46:44 UTC
Permalink
Wojtek Lerch wrote:
[...]

My apologies for the late reply.
Post by Wojtek Lerch
If I had to come up with a more formal definition, I think I'd define it
recursively using a helper term that we could call an "lvalue constant
* an identifier for an object with a static storage duration,
* a unary * operator with an operand that is an address constant
* a [] operator with an address constant and an arithmetic constant
expression as its operands,
You intentionally used ACE here, which relaxed the original
restriction. I wonder if you meant to completely replace ICE with
ACE, for example, in defining enum constants or arrays.
Post by Wojtek Lerch
* a . operator with an lvalue constant expression as its left operand
* a -> operator with an address constant as its left operand
* A null pointer constant
* An arithmetic constant expression cast to a pointer type
* An address constant cast to a pointer type
* A unary & with an operand that is an lvalue constant expression
* An lvalue constant expression with an array type that is not the
operand of a unary &
* The name of a function, with or without with a unary & applied to it
* A binary + or - with an address constant and an arithmetic constant
expression as its operands
Have I missed anything?
No you haven't, AFAICT.

[...]
Post by Wojtek Lerch
And, as we have agreed, it also gives an unreasonable amount of latitude
to the integer operand of [], by forgetting to impose any restrictions
on it (other than the constraints of 6.6p3).
On the other hand, 6.6p8 doesn't mention that adding an integer to a
You meant 6.6p9 that defines an address constant?
Post by Wojtek Lerch
pointer is allowed. 6.6p7 does, but that's not quite the same -- it
seems that the addition is only allowed after the address constant, not
struct x { int m[2]; };
char buf[N];
int *p = ( (struct x*)( &buf[4] ) ) -> m + 1; // ok
int *q = ( (struct x*)( buf + 4 ) ) -> m + 1; // bad
I think one can argue that even if others may argue differently. The
description for static pointer initiailizers is surely broken.


--
Jun, Woong (woong.jun at gmail.com)
Web: http://code.woong.org
Wojtek Lerch
2015-02-03 21:07:39 UTC
Permalink
Post by Woong Jun
[...]
My apologies for the late reply.
Post by Wojtek Lerch
If I had to come up with a more formal definition, I think I'd define it
recursively using a helper term that we could call an "lvalue constant
* an identifier for an object with a static storage duration,
* a unary * operator with an operand that is an address constant
* a [] operator with an address constant and an arithmetic constant
expression as its operands,
You intentionally used ACE here, which relaxed the original
restriction. I wonder if you meant to completely replace ICE with
ACE, for example, in defining enum constants or arrays.
Frankly I don't remember if it was an intentional change; but no, I did
not want to add the topic of enum constants and arrays to this
discussion. I used ACE because that's what the standard uses for
integer static initializers, and it felt natural to use it for pointer
initializers too, and keep the rules for initializers separate from ICEs
and enums and bitfields and array sizes. I wonder if there are some
technical reasons to make the integer part of pointer initializers more
restricted than integer initializers? If so, then I can accept that.
Post by Woong Jun
Post by Wojtek Lerch
* a . operator with an lvalue constant expression as its left operand
* a -> operator with an address constant as its left operand
* A null pointer constant
* An arithmetic constant expression cast to a pointer type
* An address constant cast to a pointer type
* A unary & with an operand that is an lvalue constant expression
* An lvalue constant expression with an array type that is not the
operand of a unary &
* The name of a function, with or without with a unary & applied to it
* A binary + or - with an address constant and an arithmetic constant
expression as its operands
Have I missed anything?
No you haven't, AFAICT.
[...]
Post by Wojtek Lerch
And, as we have agreed, it also gives an unreasonable amount of latitude
to the integer operand of [], by forgetting to impose any restrictions
on it (other than the constraints of 6.6p3).
On the other hand, 6.6p8 doesn't mention that adding an integer to a
You meant 6.6p9 that defines an address constant?
Um yes, sorry.
Post by Woong Jun
Post by Wojtek Lerch
pointer is allowed. 6.6p7 does, but that's not quite the same -- it
seems that the addition is only allowed after the address constant, not
struct x { int m[2]; };
char buf[N];
int *p = ( (struct x*)( &buf[4] ) ) -> m + 1; // ok
int *q = ( (struct x*)( buf + 4 ) ) -> m + 1; // bad
I think one can argue that even if others may argue differently. The
description for static pointer initiailizers is surely broken.
Woong Jun
2015-02-04 05:48:45 UTC
Permalink
Post by Wojtek Lerch
Post by Woong Jun
[...]
My apologies for the late reply.
Post by Wojtek Lerch
If I had to come up with a more formal definition, I think I'd define it
recursively using a helper term that we could call an "lvalue constant
* an identifier for an object with a static storage duration,
* a unary * operator with an operand that is an address constant
* a [] operator with an address constant and an arithmetic constant
expression as its operands,
You intentionally used ACE here, which relaxed the original
restriction. I wonder if you meant to completely replace ICE with
ACE, for example, in defining enum constants or arrays.
Frankly I don't remember if it was an intentional change; but no, I did
not want to add the topic of enum constants and arrays to this
discussion. I used ACE because that's what the standard uses for
integer static initializers, and it felt natural to use it for pointer
initializers too, and keep the rules for initializers separate from ICEs
and enums and bitfields and array sizes.
Because of this, I thought you meant an intentional change; I also
agree with you that it is very natural to separate rules for
initializers from those for other contexts where ICE is required.
Post by Wojtek Lerch
I wonder if there are some
technical reasons to make the integer part of pointer initializers more
restricted than integer initializers?
As far as I know, no there aren't. I think most compilers have
separate routines for accepting ICE and for other constant
expressions, because after C99 it is required to strictly recognize
ICEs to distinguish VLAs. Allowing ACEs for pointer initializers
seems more natural to me.


--
Jun, Woong (woong.jun at gmail.com)
Web: http://code.woong.org
Tim Rentsch
2015-02-18 17:50:00 UTC
Permalink
Post by Woong Jun
[...]
Post by Tim Rentsch
High order bit: you make some good observations, and I agree the
Standard is not clear enough about what is allowed and what
isn't. To me there is enough ambiguity to merit a Defect
Report (and so I hope you will write one).
Unfortunately, my national body to ISO/IEC JTC1 is not quite active
in regard to WG14 activities, so I doubt that they will file my
proposed defect report to deliver to ISO. Nevertheless, I think I
can prepare one even if it will be written in poor standardese when I
have spare time.
Sorry for the late response, it's a busy time.

My one suggestion here is you might want to write directly to WG14
and ask how you can submit a DR directly. Here is a link to the
WG14 contacts page -

http://www.open-std.org/jtc1/sc22/wg14/www/contacts

If it were me I'd probably send an email addressed to both the
Convener and the Secretariat, plead ignorance, and ask if there
is a way you can send in a DR and if so what the process is.
I believe your question arises because you are an implementor
and wondering exactly what your implementation must do. If
so I think that raises the credibility of the DR, so that might
be worth mentioning in the contact email.

If you do get to the point of writing a DR you might want to
post a draft here before submitting it. I don't have time
for the whole thing but I'm happy to contribute with comments
and suggestions, etc (and don't worry if the draft is rough,
get it out there early and get as much help as you can).

Good luck!
Woong Jun
2015-03-11 23:47:14 UTC
Permalink
Post by Tim Rentsch
Post by Woong Jun
[...]
Post by Tim Rentsch
High order bit: you make some good observations, and I agree the
Standard is not clear enough about what is allowed and what
isn't. To me there is enough ambiguity to merit a Defect
Report (and so I hope you will write one).
Unfortunately, my national body to ISO/IEC JTC1 is not quite active
in regard to WG14 activities, so I doubt that they will file my
proposed defect report to deliver to ISO. Nevertheless, I think I
can prepare one even if it will be written in poor standardese when I
have spare time.
Sorry for the late response, it's a busy time.
My one suggestion here is you might want to write directly to WG14
and ask how you can submit a DR directly. Here is a link to the
WG14 contacts page -
http://www.open-std.org/jtc1/sc22/wg14/www/contacts
If it were me I'd probably send an email addressed to both the
Convener and the Secretariat, plead ignorance, and ask if there
is a way you can send in a DR and if so what the process is.
I believe your question arises because you are an implementor
and wondering exactly what your implementation must do. If
so I think that raises the credibility of the DR, so that might
be worth mentioning in the contact email.
If you do get to the point of writing a DR you might want to
post a draft here before submitting it. I don't have time
for the whole thing but I'm happy to contribute with comments
and suggestions, etc (and don't worry if the draft is rough,
get it out there early and get as much help as you can).
Good luck!
My apologies on the late reply; I didn't notice that my post got a
response.

As you suggested, I'll write to WG14 using the contacts page you gave
me to ask if there is a way to directly send a PDR without help from
my national body. Before submitting, I'll post here a rough draft
with a proposed solution to the issue to hear opinions and to get
help to polish my poor standardese.

Thank you for the valuable advice.


--
Jun, Woong (woong.jun at gmail.com)
Web: http://code.woong.org

Loading...