C99 7.18.4 says:
 The macro INTN_C(value) shall expand to a signed integer constant
 with the specified value and type int_leastN_t.
It goes on to give an example:
 For example, if uint_least64_t is a name for the type unsigned
 long long int, then UINT64_C(0x123) might expand to the integer
 constant 0x123ULL.
This implies that the macro INT8_C() should result in a signed
integer constant of type int_least8_t. On most platforms this
will be a (signed) char.
6.4.4.1 describes integer constants. There is syntax for signed
and unsigned int, long int and long long int. Nowhere is there a
syntax for integer constants of any of the char types. Even the
integer character constants (6.4.4.4) have type int.
So how can INT8_C() result in an integer constant of type
int_least8_t if CHAR_BIT is 8? Take this code:
 #include <stdio.h>
 #include <stdint.h>
 int main(void)
 {
   printf("%lu\n", (unsigned long)sizeof(INT8_C(1)));
 #if INT8_C(1)
   printf("Hello, world!\n");
 #endif
   return 0;
 }
I believe that this must output
 1
 Hello, world!
Does the definition of INT8_C() require compiler magic?
In case you are wondering, Compaq C is now mostly C99 compliant,
but does not include stdint.h. I am trying to write my own
version.
Phil T
Does a cast expression work for this? E.g.
INT8_C(10) => (char)10
This would work in your sizeof() test case.
--
Barry Margolin, bar...@genuity.net
Genuity, Woburn, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
> This is a multi-part message in MIME format.
>
> ------=_NextPart_000_01C11F41.0F4B1A40
> Content-Type: text/plain; charset=ISO-8859-1
> Content-Transfer-Encoding: 7bit
How about this?:
#deifne INT8_C(i) ((char)(i))
#deifne UINT8_C(i) ((unsigned char)(i))
--
Clark S. Cox III
clar...@yahoo.com
http://www.whereismyhead.com/clark/
It would work, but (char)10 isn't an integer constant, which is what
7.18.4.1 requires (or rather, required).
That section has undergone some surgery in Technical Corrigendum 1
(WG14 N932), available at
<http://wwwold.dkuug.dk/JTC1/SC22/WG14/www/docs/n932.pdf>
The expression resulting from the expansion of each of the macros
"shall have the same type as would an expression of the corresponding
type converted according to the integer promotions."
--
Keith Thompson (The_Other_Keith) k...@cts.com <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://www.sdsc.edu/~kst>
Cxiuj via bazo apartenas ni.
Clark S. Cox III <clar...@yahoo.com> wrote in article
<1exs169.e6wp8rl63yrjN%clar...@yahoo.com>...
> Phil Tregoning <ptre...@esoc.esa.de> wrote:
> >
> > #include <stdio.h>
> > #include <stdint.h>
> >
> > int main(void)
> > {
> > printf("%lu\n", (unsigned long)sizeof(INT8_C(1)));
> > #if INT8_C(1)
> > printf("Hello, world!\n");
> > #endif
> > return 0;
> > }
> >
> > I believe that this must output
> >
> > 1
> > Hello, world!
> >
> > Does the definition of INT8_C() require compiler magic?
>
> How about this?:
>
> #deifne INT8_C(i) ((char)(i))
> #deifne UINT8_C(i) ((unsigned char)(i))
>
This doesn't result in an integer constant, but an integer
constant expression. This wouldn't work for the #if
directive. C99 6.10.1 says in the constraints for #if:
The expression that controls conditional inclusion
shall be an integer constant expression except that:
it shall not contain a cast; ....
So your version requires a diagnostic for "#if INT8_C(1)".
It seems to me the intent of the standard is to use token
pasting for the longer versions, e.g.
#define INT64_C(i) i ## LL
In the description of these macros it says (7.18.4):
The argument in any instance of these macros shall be a
decimal, octal, or hexadecimal constant (as defined in
6.4.4.1) with a value that does not exceed the limits
for the corresponding type.
So all the arguments you can invoke these macros with are
suitable for having U, L, UL, LL or ULL pasted onto them.
Invoking them with anything else gives to undefined
behaviour.
In addition, the example given says (7.18.4.1):
For example, if uint_least64_t is a name for the type
unsigned long long int, then UINT64_C(0x123) might
expand to the integer constant 0x123ULL.
So the larger sizes can be defined (with 16 bit shorts
and 32 bit ints) as:
#define INT32_C(i) i
#define UINT32_C(i) i ## U
#define INT64_C(i) i ## LL
#define UINT64_C(i) i ## LLU
But I am stuck on the 8 and 16 bit versions.
Phil T
Keith Thompson <k...@cts.com> wrote in article
<yechevj...@king.cts.com>...
> Barry Margolin <bar...@genuity.net> writes:
> > In article <01c11f30$4bc24a40$4b53...@ptregoni.dev.esoc.esa.de>,
> > Phil Tregoning <ptre...@esoc.esa.de> wrote:
> > >So how can INT8_C() result in an integer constant of type
> > >int_least8_t if CHAR_BIT is 8?
> >
> > Does a cast expression work for this? E.g.
> >
> > INT8_C(10) => (char)10
> >
> > This would work in your sizeof() test case.
>
> It would work, but (char)10 isn't an integer constant, which is what
> 7.18.4.1 requires (or rather, required).
>
> That section has undergone some surgery in Technical Corrigendum 1
> (WG14 N932), available at
> <http://wwwold.dkuug.dk/JTC1/SC22/WG14/www/docs/n932.pdf>
> The expression resulting from the expansion of each of the macros
> "shall have the same type as would an expression of the corresponding
> type converted according to the integer promotions."
>
So on my implementation (8 bit char, 16 bit short, 32 bit int,
and 64 bit long long), these would be OK:
#define INT8_C(i) i
#define UINT8_C(i) i
#define INT16_C(i) i
#define UINT16_C(i) i
#define INT32_C(i) i
#define UINT32_C(i) i ## U
#define INT64_C(i) i ## LL
#define UINT64_C(i) i ## ULL
And Dinkumware's ( http://www.dinkumware.com/ ) are wrong.
Phil T
> So on my implementation (8 bit char, 16 bit short, 32 bit int,
> and 64 bit long long), these would be OK:
>
> #define INT8_C(i) i
> #define UINT8_C(i) i
> #define INT16_C(i) i
> #define UINT16_C(i) i
> #define INT32_C(i) i
> #define UINT32_C(i) i ## U
> #define INT64_C(i) i ## LL
> #define UINT64_C(i) i ## ULL
>
> And Dinkumware's ( http://www.dinkumware.com/ ) are wrong.
Sigh. Yes, I think so, though we've now adopted a more portable form.
I missed the bit in TC1 about these macros being usable in #if expressions.
P.J. Plauger
Dinkumware, Ltd.
http://www.dinkumware.com
But a simple change would make it work:
typedef char __char;
typedef unsigned char __unsigned_char;
#define INT8_C(i) ((__char)+(i))
#define UINT8_C(i) ((__unsigned_char)+(i))
Mark Williams
Not any more. As others have said, it's changed. Here's the relevant
changes:
7.18.4, add #3:
[#3] Each invocation of one of these macros shall expand to
an integer constant expression suitable for use in #if
preprocessing directives. The type of the expression shall
have the same type as would an expression of the corresponding
type converted according to the integer promotions. The value
of the expression shall be that of the argument.
7.18.4.1, replace #1 and #2 by:
[#1] The macro INTN_C(value) shall expand to an integer
constant expression corresponding to the type int_leastN_t.
The macro UINTN_C(value) shall expand to an integer constant
expression corresponding to the type uint_leastN_t. For
example, if uint_least64_t is a name for the type
unsigned long long int, then UINT64_C(0x123) might expand to
the integer constant 0x123ULL.
7.18.4.2: change "constant" to "constant expression" in two places.
--
Clive D.W. Feather, writing for himself | Home: <cl...@davros.org>
Tel: +44 20 8371 1138 (work) | Web: <http://www.davros.org>
Fax: +44 20 8371 4037 (D-fax) | Work: <cl...@demon.net>
Written on my laptop; please observe the Reply-To address
It can't. This was the subject of my DR#209.
Unfortunately, there was some sort of editorial
communication snafu so the published TC#1 that
should have resolved this still has the words
wrong. Subclause 7.18.4.2 paragraph 1 should say:
"The following macro shall expand to an integer
constant expression corresponding to the type
intmax_t" and similarly for the unsigned case.
The "correspondence" is established by a new
paragraph 3 in subclause 7.18.4; it means the
type is that of an expression that is an object
of the type after conversion by the integer
promotions.
Apart from one pathological case discussed here recently, [u]intmax_t
never undergo the integer promotions. So the different wording makes no
difference.
Sure it does, but not for the red-herring reason you raised.
It matters because TC1, as submitted for voting, still required
the constant-forming macros to expand to *the type*, which was
the specification my DR objected to in the first place.
What are the ISO rules on correcting TCs after they are published?
My copy of ISO/IEC 9899:1990/Cor.1:1994(E) says "Published 1994-09-15"
and "Corrected and reprinted 1995-09-15". What was wrong with the
first printing that was corrected in the reprint?
--
Joseph S. Myers
js...@cam.ac.uk
Someone at ISO essentially rewrote the document that the committee
submitted, introducing numerous serious errors in the process.
-Larry Jones
This game lends itself to certain abuses. -- Calvin
Apart from the red herring, I'm not sure what the issue it.
Try reading my original DR.
Or try implementing <stdint.h> sometime.
Tell me how, on a relatively standard platform,
you can define the 8-bit constant-forming macro so that it
expands to an expression of type {unsigned} char (for example)
*suitable for use in preprocessing #if-expressions*
(which means a cast cannot be used).
It cannot be done without special compiler hooks,
and that was never intended.
In fact, WG14 approved corrected wording (which I drafted),
but somehow it didn't make it into the published TC1.
DR 209, right ?
>Or try implementing <stdint.h> sometime.
>Tell me how, on a relatively standard platform,
>you can define the 8-bit constant-forming macro so that it
>expands to an expression of type {unsigned} char (for example)
>*suitable for use in preprocessing #if-expressions*
>(which means a cast cannot be used).
>It cannot be done without special compiler hooks,
>and that was never intended.
Huh ?
The copy of TC1 I have here amends 7.18.4 and 7.18.4.1. The wording in
7.18.4 says:
[#3] Each invocation of one of these macros shall expand to
an integer constant expression suitable for use in #if
preprocessing directives. The type of the expression shall
have the same type as would an expression of the corresponding
type converted according to the integer promotions. The value
of the expression shall be that of the argument.
and that in 7.18.4.1 says "an integer constant expression *corresponding
to* the type int_leastN_t" (my emphasis). It does not require the same
type.
So:
#define INT8_C(value) (value)
#define UINT8_C(value) ((value) + 0U)
or #define UINT8_C(value) (value ## U)
Similarly:
#define INTMAX_C(value) ((value) + 0 * INTMAX_MAX)
#define UINTMAX_C(value) ((value) + 0 * UINTMAX_MAX)
>In fact, WG14 approved corrected wording (which I drafted),
I think I was there (Kona ?).
>but somehow it didn't make it into the published TC1.
I'm confused - the wording in DR 209 matches the wording in the PDF
document that claims to be TC1 (but which I don't have on my laptop).
Yeah, sorry, I misstated my gripe. *That* change got made.
But we agreed in committee that I would fix up the wording
in 7.18.4.2 to be exactly parallel to that in 7.18.4.1,
along with making the other agreed-upon edits to the DR 209
log, which I did and sent to the Convenor, yet that one part
was not reflected in the update on the Web site nor in TC1.
So far as I can determine it was simply a case of incomplete
manual editing of the pre-existing Web page instead of
totally replacing it with the new version that I had sent.
Thus my "No" vote with comment in J11 on TC1:
Comment: TC1 item 36 is not what was agreed upon, and is apparently
based on an incorrectly edited Web page for Defect Report
#209. As it stands, the item does not address the primary
concern of the Defect Report, and therefore should not be
applied to the standard. The agreed-upon item, as sent by
e-mail to the Convenor on 16-Nov-2000, was the following:
36. Page 260, 7.18.4.2
In paragraph 1, change both occurrences of "expands to
an integer constant having the value specified by its
argument and" to "shall expand to an integer constant
expression corresponding to".
There had been verbal committee agreement on the changes,
which in subclause 7.18.4.2 are twofold:
(1) eliminate redundant specification of what the value is;
(2) duplicate the wording about type adopted for 7.18.4.1.
And yes, in the special case of the greatest-width types it
is practically certain that the corresponding expression
will have exactly the same type, but to have a guarantee
of that some fragile assumptions must be made, and there is
no need to make this particular text dependent on them.
The following appears to do the trick nicely... what is your objection to it?
typedef unsigned char __unsigned_char;
#define UINT8_C(i) ((__unsigned_char)+(i))
Mark Williams
Why do you need the typedef? Why do you need the unary plus?
I believe the following is equivalent (and doesn't work either):
#define UINT8_C(i) ((unsigned char)(i))
The problem: you can't have a cast in a preprocessing #if-expression
(C99 6.10.1 paragraph 1).
With the (somewhat imperfect) wording in TC1, the following is a legal
definition:
#define UINT8_C(i) (i)
>mar...@my-deja.com (Mark Williams) writes:
[...]
>> The following appears to do the trick nicely... what is your objection to it?
>>
>> typedef unsigned char __unsigned_char;
>> #define UINT8_C(i) ((__unsigned_char)+(i))
>
>Why do you need the typedef?
Because "(unsigned char)" is a syntax error in a preprocessing
#if-expression, while (__unsigned_char) evaluates to (0) (assuming
defined(__unsigned_char) is false).
> Why do you need the unary plus?
Because in the preprocessor it becomes the addition operator.
e.g.
#if UINT8_C(1) == 1
becomes
#if ((0)+(1)) == 1
>
>I believe the following is equivalent (and doesn't work either):
>
>#define UINT8_C(i) ((unsigned char)(i))
>
>The problem: you can't have a cast in a preprocessing #if-expression
>(C99 6.10.1 paragraph 1).
But Mark's definition doesn't have a cast in a preprocessing
#if-expression.
>
>With the (somewhat imperfect) wording in TC1, the following is a legal
>definition:
>
>#define UINT8_C(i) (i)
Which makes the whole thing moot...
Regards,
-=Dave
--
Change is inevitable, progress is not.
Why? What rule does it violate other than 6.10.1p1? How do you
distinguish between applying 6.10.1p1 to (unsigned char) and
(__unsigned_char)?
...
> >I believe the following is equivalent (and doesn't work either):
> >
> >#define UINT8_C(i) ((unsigned char)(i))
> >
> >The problem: you can't have a cast in a preprocessing #if-expression
> >(C99 6.10.1 paragraph 1).
>
> But Mark's definition doesn't have a cast in a preprocessing
> #if-expression.
The only interpretation that I can come up with that makes that
statement true renders 6.10.1p1 meaningless. In principle, you need to
reach translation phase 7 to recognise that it is a cast. Since the
contents of a #if-expression are removed upon evaluation in phase 4,
they don't survive to phase 7. However, that's true of anything that
could be called a cast.
You are correct in saying that (__unsigned_char), if it were legal in a
#if-expression, would be be replaced with (0), per 6.10.1p3, and would
therefore no longer be a cast. However, that same rule, applied to any
cast, renders it no longer a cast.
Therefore, if 6.10.1p1 has any meaning (which could be debated), it must
be referring to things that would be recognised as casts, if they did
survive to phase 7 without performing the replacement called for by
6.10.1p3, even though they won't actually survive that long.
Ooh, that's sneaky!
It is not meaningless, it is trivally true. It is like the
statement "we're here now" or the question "is that you?".
It is both nonconforming and impossible to have a cast in a
preprocessing expression in an #if. There are no type names
during the phase 4 of translation, so there are no casts.
Therefore, it is impossible for an expression in a
preprocessing expression in an #if to contain a cast.
Sincerely,
Bob Corbett
6.10.1p1 says "it shall not contain a cast". The word "shall" makes it a
requirement, not a statement of fact. A requirement that is guaranteed
to always be satisfied is meaningless. I don't believe that 6.10.1p1 was
meant to be interpreted in such a fashion as to be meaningless.
A better analogy would be "You shall not square the circle!" If you
believe that circles can't be squared, then this injunction would appear
to be meaningless. If someone tells you "You shall not square the
circle!", you should take that injunction as evidence that this person
disagrees with you. That in turn should be taken as a reason to
investigate the possibility that you might be wrong about circles. That
person could also be insane, or fooling with your head, but it's at the
very least impolite to assume, without further investigation, that those
two are the only possible conclusions. I think we can safely rule out
those two explanations in the case of 6.10.1p1. :-)
Can't use casts in preprocessor #if conditional expressions.
I prefer the following, which uses a trick due to Clive Feather:
#define UINT8_C(c) (UINT_LEAST8_MAX - UINT_LEAST8_MAX + (c))
The general pattern works for all the widths, including the
greatest-width version.
>> #define UINT8_C(i) (i)
>
> I prefer the following, which uses a trick due to Clive Feather:
>
> #define UINT8_C(c) (UINT_LEAST8_MAX - UINT_LEAST8_MAX + (c))
But adding and subtracting unsigned chars produces an int (usually),
not an unsigned char.
--
__("< Marcin Kowalczyk * qrc...@knm.org.pl http://qrczak.ids.net.pl/
\__/
^^ SYGNATURA ZASTĘPCZA
QRCZAK
Which was the object of the exercise.
He hasn't. That's the clever (and evil) thing about it.
There isn't a cast when used in an #if conditional expressions. In
an #if conditional expressions, that expands to:
((0)+(i))
--
Clark S. Cox III
clar...@yahoo.com
If that doesn't count as a cast for purposes of 6.10.1p1, what would
count as a cast?
Right, but what advantage does the above have over the simpler
#define UINT8_C(c) (c)
? The result is promoted to int (or conceivably unsigned int) either
way, yes?
I agree that it probably wasn't *meant* to...
On the other hand, replacing all the identifiers with zeros is one of
the steps of transforming the list of preprocessing tokens into the
controlling constant expression; 6.10.1p3 uses words like "the list of
preprocessing tokens that will *become* the controlling constant
expression" and "the resulting tokens compose the controlling constant
expression". The only interpretation I can think of is that the
expression doesn't even exist until after all the identifiers are
gone; can you come up with a different one?
Yes; but only in terms of what "would have been". Specifically, I'm
suggesting that what is prohibited is any expression which would been
interpreted during phase 7 as containing a cast, if it had occurred on
it's own rather than inside a #if expression.
I find such an interpretation clumsy, but it's the only one I can think
of that would make the clause meaningful. Personally, I don't see the
need to for such a prohibition. In the absence of the prohibition,
paragraph 3 of that same section would apply to virtually any cast
expression, radically changing its meaning. That alone would be
sufficient to effectively prohibit cast expressions for any use other
than special cases like the one being discussed here. A warning to that
effect would seem to be all that is needed, and need not be in normative
text.
I don't see much point in the standard going out of it's way to prohibit
this special use; on the other hand, I don't think it's important enough
a use to fight for a change, either. This code left more than one expert
C programmer uncertain about how it was supposed to work, until it was
explained, at which point one of them exclaimed "Ooh, that's sneaky!"
and another referred to it as "clever (and evil)". That's generally a
sign of bad code, even if it is technically correct.
>Yes; but only in terms of what "would have been". Specifically, I'm
>suggesting that what is prohibited is any expression which would been
>interpreted during phase 7 as containing a cast, if it had occurred on
>it's own rather than inside a #if expression.
>
>I find such an interpretation clumsy, but it's the only one I can think
>of that would make the clause meaningful. Personally, I don't see the
>need to for such a prohibition. In the absence of the prohibition,
>paragraph 3 of that same section would apply to virtually any cast
>expression, radically changing its meaning. That alone would be
>sufficient to effectively prohibit cast expressions for any use other
>than special cases like the one being discussed here. A warning to that
>effect would seem to be all that is needed, and need not be in normative
>text.
>
>I don't see much point in the standard going out of it's way to prohibit
>this special use; on the other hand, I don't think it's important enough
>a use to fight for a change, either. This code left more than one expert
>C programmer uncertain about how it was supposed to work, until it was
>explained, at which point one of them exclaimed "Ooh, that's sneaky!"
>and another referred to it as "clever (and evil)". That's generally a
>sign of bad code, even if it is technically correct.
One reason I can think of is that some (pre-C89) compilers did allow
casts in such contexts. I found it quite annoying at first when I
learned they were disallowed. At first I had quite a difficult time
finding replacements for some uses I had made of them, and couldn't
see a good reason for the ban. (Eventually, I did.)
So, guessing, I would think that the line was intended to make
explicit an intentional bad that might otherwise have seemed to some
as an accidental ban that should be changed.
Yes, that's what I want.
No, (c) has the same type as c, whatever that is.
In fact, it will often be (signed) int since that's what the
type of the constant is for typical values, as in UINT8_C(5).
If you follow the thread back, I was merely responding to this message
by douglas gwyn.
I believe my response answers his question, and disproves both of his
assertions :-)
In this case, I assume that the C committee did not realize that it was
impossible to write a cast in a preprocessor expression in an #if.
While you might think it is impolite to assume that the committee did not
know that, I do not.
Sincerely,
Bob Corbett
Sincerely,
Bob Corbett
I may be wrong, but would this be a "cast in an #if expression"?
#define int int
#if (int) 1 == 1
#endif
I know that identifiers that are not #define'd will be replaced with 0,
but int is #define'd, so macro substitution should happen once, but then
it shouldn't be substituted again.
Firstly, there are occasional redundancies in the Standard, though we
tried to eliminate them. Therefore this is a weaker argument.
More to the point, however, while it is currently the case that every
possible type-name involves at least one keyword or identifier, that
might not always be the case (and the implementation might have extended
type names that don't involve them either). Therefore casts are
forbidden even though, at present, they either convert to a
parenthesised constant or a syntax error.
What about a list of preprocessing tokens (I prefer not to call it an
"expression" at this stage, just like 6.10.1p3 doesn't) that wouldn't
become a syntactically correct expression at all if you applied phases
4 to 7 to it?
If you think that's prohibited, too, then what about this:
#if defined NOT_A_MACRO
It doesn't matter whether it's #define'd or not. "After all
replacements due to macro expansion and the defined unary operator
have been performed, all remaining identifiers are replaced with the
pp-number 0, and then each preprocessing token is converted into a
token."
The syntax given in 6.10.1 describes it as a constant expression, so I
don't think you need to be worried about that distinction.
> become a syntactically correct expression at all if you applied phases
> 4 to 7 to it?
Keep in mind that the full syntax does not apply to #if expressions;
only a modified version of a subset of the syntax required by section
6.6 for constant expressions. If, after applying those modifications, it
contains tokens that don't match the syntax referred to by 6.6, it is
indeed a syntax error.
Section 6.6 requires parsing that ordinarily doesn't happen until phase
7; therefore, if you argue that (char) isn't a cast operator yet for the
purposes of #if, then by the same argument, '+' isn't an addition
operator yet - that's also a phase 7 construct. Do you really want to
argue that "#if SIZE<256" doesn't work, because '<' isn't really the
"less than" operator until phase 7?
> If you think that's prohibited, too, then what about this:
>
> #if defined NOT_A_MACRO
I'm not sure I get the point of your question. We're talking about the
explicit prohibition of casts in #if expressions, that is stated in
6.10.1.1. I don't see any connection between that prohibition and this
example.
#if constant expressions are handled a little differently from the way
they are in normal phase 7 processing. Those differences are described
in 6.10.1. In particular, evaluation of the 'defined' operator occurs
before any other handling of identifiers in #if expressions; otherwise,
the 'defined' operator would be meaningless. If NOT_A_MACRO lives up to
it's name, than "defined NOT_A_MACRO" evaluates to 0.
Doug correctly asserted casts aren't allowed in #if-expressions. The
standard explicitly says so, and I haven't seen that disproven. If your
example doesn't qualify as a cast, I don't know what could qualify as
one. Therefore, your example doesn't answer his question, and therefore
doesn't disprove his second assertion, that it can't be done without
special compiler hooks.
No, it's not a macro substitution - all remaining identifiers are
replaced by 0, no matter where they came from.
Note, that in #if-expressions, undefined symbols are treated as (0).
Since, in this context, (__unsigned_char) is undefined, it will expand
to (0), so the actual expression seen in an #if-expression is ((0)+(i)),
which has no cast.
typedef unsigned char __unsigned_char;
#if ((__unsigned_char)+(i)) //There is no cast in this line
#if ((unsigned char)+(i)) //There is a cast in this line
Yes, my previous messages on this thread should have made it quite clear
that I'm aware of that.
> which has no cast.
Which is precisely the point of contention. It had a cast before that
substitution, and that's the only sense in which it could have had a
cast in it. After the substitution, everything that could possibly be
called a cast, is no longer a cast. Every cast operator necessarily
includes at least one keyword or typedef name. That substitution will
convert the keyword or typedef into a 0, making it no longer a valid
cast. Inside a #if expression, the only keyword that gets any special
handling is 'defined'.
> typedef unsigned char __unsigned_char;
> #if ((__unsigned_char)+(i)) //There is no cast in this line
> #if ((unsigned char)+(i)) //There is a cast in this line
Why do you believe that the second #if is different from the first? The
same exact argument applies equally well (or equally poorly) to the
second one. Keep in mind that up until phase 7, "unsigned" and "char"
don't have any more of a special meaning than "__unsigned_char", all
three are just identifiers as far as preprocessing is concerned. If they
don't identify a macro, then they get replaced with '0' in this context.
Either the prohibition on casts in 6.10.1p1 is meaningless, or it
applies to both those examples.
Proof by lack of imagination? How about:
#define int int
#if (int)5
Even without this example, your claim is totally unjustifiable. My
example does not contain a cast when interpreted as a pre-processing
expression, unless you suppress certain phases of pre-processing that
are required by the standard.
Mark W
The point is, that by that logic, there's no such thing as a #if
constant-expression containing a cast, which would render 6.10.1p1's
prohibition on casts in #if constant-expressions vacuous. That can't be
how 6.10.1p1 was intended to be interpreted. On the other hand, there
may be some legitimate uncertainty about whether the wording actually
matches the intent.
But I don't think 6.10.1p3 makes a lot of sense without that
distinction. Since turning the original list of preprocessing tokens
into the final list of tokens involves macro expansion, these two
things can be very different.
> > become a syntactically correct expression at all if you applied phases
> > 4 to 7 to it?
>
> Keep in mind that the full syntax does not apply to #if expressions;
> only a modified version of a subset of the syntax required by section
> 6.6 for constant expressions. If, after applying those modifications, it
> contains tokens that don't match the syntax referred to by 6.6, it is
> indeed a syntax error.
What I am trying to find out is which list of tokens is parsed to
match this modified syntax. Is it the original list of preprocessing
tokens ("the original list" from now on), or is it the final list of
tokens that macro replacement and the other transformations described
in 6.10.1p3 turn it into ("the final list")? Or perhaps both? And is
there also a requirement that you suggested, that applies to what the
original #if expression would become in translation phase 7 if it had
occured outside of an #if directive ("the phase 7 equivalent")? Since
macro replacement is involved, these three lists of tokens can differ
significantly.
The interpretation that sounds natural to me is that only the "final
list" is checked for syntax, and the ban on casts is meaningless
rather than obscure. I think a few compilers agree with this
interpretation.
The interpretation that you suggested is that the ban on casts was not
meant to apply to the "final list", but to the "phase 7 equivalent". I
agree with you that this interpretation is clumsy; but I also argue
that it has other problems. If, as I assumed previously, the
expression syntax that you wanted to use to detect casts is the normal
syntax of constant expressions as defined in 6.6, then "defined
NOT_A_MACRO" causes a syntax error. If, on the other hand, you want
to treat "defined" as an operator, then you get an error from
something as simple as "defined NULL", because its phase 7 equivalent
might be "defined 0", which violates the requirement that the operand
of "defined" must be an identifier. Either way, I don't see anything
in the Standard to support the requirement that an #if expression is
not allowed to generate a syntactically incorrect "phase 7
equivalent", no matter which syntax you wanted to apply to it. And if
it doesn't have to be a syntactically correct expression, I'm not sure
what it means that it's not allowed to contain a cast.
But I have just come up with a third interpretation that does not
render the ban on casts meaningless and does not refer to the "phase 7
equivalent". I must admit that I also find it clumsy, but, sadly, it
seems much closer to what the Standard actually says than my "natural"
interpretation. Even though I don't think any compiler that I know
implements it. Here it goes:
Even though 6.10.1p3 does not refer to the "original list" as an
"expression", 6.10.1p1 clearly does: since the "final list" doesn't
contain identifiers, it wouldn't make sense to talk about the
"defined" operator, casts, or how identifiers are interpreted, if
6.10.1p1 were referring to the "final list". Therefore, the "original
list" must form a syntactically correct expression (with the modified
syntax containing the "defined" operator). Since this expression may
contain identifiers, saying that it's not allowed to contain a cast is
not meaningless.
After the "original list" is turned into the "final list" as described
in 6.10.1p3, it becomes the "controlling constant expression" that is
"evaluated" according to modified rules of 6.6. Even though the
Standard doesn't actually say that, I presume that before this new
expression can be evaluated, it must be checked for syntax again.
One serious problem about this interpretation is that since the
expression before macro expansion is required to be a correct constant
expression, it's not allowed to contain function calls. Wouldn't that
also rule out function-like macros?
> Section 6.6 requires parsing that ordinarily doesn't happen until phase
> 7; therefore, if you argue that (char) isn't a cast operator yet for the
> purposes of #if, then by the same argument, '+' isn't an addition
> operator yet - that's also a phase 7 construct. Do you really want to
> argue that "#if SIZE<256" doesn't work, because '<' isn't really the
> "less than" operator until phase 7?
6.10.1p3 describes how the original list of preprocessing tokens are
transformed into the list of tokens that becomes the controlling
expression that is then evaluated according to 6.6. These
transformations are similar to what happens to other expressions
before they are parsed for syntax in phase 7, but there are two
additionl steps of applying the "defined" operator and replacing
identifiers with zeros. What I was trying to argue is that a '+' is
neither an addition operator nor an unary plus until syntactical
analysis determines which one it can be, and syntactical analysis does
not happen until after the tokens have either reached phase 7 or been
processed according to 6.10.1p3. Similarly, code that was (FOO)
before macro expansion may become a cast operator, an expression in
parentheses, or perhaps something that cannot possibly belong in a
valid expression.
Anyway, the point I was actually trying to make was that all the #if
expressions are gone by the end of phase 4, and that I din't see a
reason to believe that their corectness might depend on what they
would look like if they had occured outside an #if directive and
survived until phase 7.
> > If you think that's prohibited, too, then what about this:
> >
> > #if defined NOT_A_MACRO
>
> I'm not sure I get the point of your question. We're talking about the
> explicit prohibition of casts in #if expressions, that is stated in
> 6.10.1.1. I don't see any connection between that prohibition and this
> example.
You had suggested that the ban on casts applies to what the expression
would become during phase 7 if it had occured on its own rather than
inside an #if expression; my point was that it might not become a
syntactically correct expression at all, even if it obviously was a
correct #if expression in phase 4. I admit I picked a bad example
based on an apparently incorrect interpretation of what you said; but
I still stick to my general point that I don't see a reason why the
correctness of an #if expression should depend on whether it would
become a correct expression in phase 7 if it had not been an #if
expression.
> #if constant expressions are handled a little differently from the way
> they are in normal phase 7 processing. Those differences are described
> in 6.10.1. In particular, evaluation of the 'defined' operator occurs
> before any other handling of identifiers in #if expressions; otherwise,
> the 'defined' operator would be meaningless. If NOT_A_MACRO lives up to
> it's name, than "defined NOT_A_MACRO" evaluates to 0.
I know; but it was your suggestion that the ban on casts applies to
what the expression would normally become in phase 7 if it had not
been an #if expression in the first place. And the normal phase 7
processing would not consider 'defined' to be an operator.
Are you now saying that for the purpose of searching for prohibited
casts, the preprocessor should *both* handle 'defined' *and* run the
normal translation phases 4 to 7 before the syntax is checked for
casts? But the other rule from 6.10.1p3 that turns identifiers into
zeros should not be applied? And are you also suggesting that the fact
that the ban on casts in 6.10.1p1 would be meaningless otherwise is
enough to imply this complicated interpretation?
Paragraph three explicitly states that macro expansion occurs prior to
evaluation. If that weren't the case , "#if CHAR_BITS>8" would never be
meaningful.
...
> You had suggested that the ban on casts applies to what the expression
> would become during phase 7 if it had occured on its own rather than
> inside an #if expression; my point was that it might not become a
> syntactically correct expression at all, even if it obviously was a
> correct #if expression in phase 4. I admit I picked a bad example
> based on an apparently incorrect interpretation of what you said; but
> I still stick to my general point that I don't see a reason why the
> correctness of an #if expression should depend on whether it would
> become a correct expression in phase 7 if it had not been an #if
> expression.
There's no good reason why it should have such a dependence, and I've
never suggested that it should. What I'm suggesting is the following
sequence:
1. Evaluate the defined operator wherever it occurs.
2. Expand macros.
3. If at this point, the expression contains anything that would, if it
were to survive to phase 7, be parsed as a cast, then the code violates
an explicit "shall". In that case, the implementation has no further
responsibilities for handling it properly. The implementation is not
required to actually do anything to detect this fact; it merely must
work correctly as long as the "shall" is satisfied.
5. Replace surviving identifiers with 0.
6. Parse the modified expression as in phase 7, and evaluate it
according to the rules applicable to constant-expressions, as described
in section 6.6.
I don't claim that this is what the standard explicitly says; there's
definitely room for improvement in the wording of the standard. However,
this is an order that renders all of the of 6.10.1 meaningful. An order
that supports your interpretation makes an idiot of anyone who
consciously intended that interpretation to apply. I don't think the
people who wrote this and approved it were idiots; just fallible.
> > #if constant expressions are handled a little differently from the way
> > they are in normal phase 7 processing. Those differences are described
> > in 6.10.1. In particular, evaluation of the 'defined' operator occurs
> > before any other handling of identifiers in #if expressions; otherwise,
> > the 'defined' operator would be meaningless. If NOT_A_MACRO lives up to
> > it's name, than "defined NOT_A_MACRO" evaluates to 0.
>
> I know; but it was your suggestion that the ban on casts applies to
> what the expression would normally become in phase 7 if it had not
> been an #if expression in the first place. And the normal phase 7
> processing would not consider 'defined' to be an operator.
Yes. However, that is only a ban on casts. It doesn't impose the
corresponding syntax requirements. One good way for a set of tokens to
NOT have a cast in it, would be by violating the syntax required of a
cast. In any event, as you incredulously suggest below, the evaluation
of the defined operator must occur prior to macro expansion, or it's
main usefullness would disappear. Macro expansion, in turn, must occur
prior to parsing the result in accordance with phase 7, or most of the
usefulness of #if's would disappear.
> Are you now saying that for the purpose of searching for prohibited
> casts, the preprocessor should *both* handle 'defined' *and* run the
> normal translation phases 4 to 7 before the syntax is checked for
> casts?
Yes and no.
No, because this "shall" doesn't contrain the implementor, it constrains
the user-written code. The implementor has no requirement to check for
casts, because there's nothing it needs to do with them. What the
"shall" means for the implementor is simply that the implementation is
allowed (but not required) to fail if the expression happens to contain
a cast. Because of that requirement, an implementation is allowed to
choke on "((unsigned char)+(i))", even though there's no reason from any
of the other requirements why it would.
However, I could also say "yes", or more accurately, "sort of", in the
following sense: the prohibition on casts can only make sense in terms
of code after the evaluation of the 'defined' operator, after macro
expansions and after the parsing required of phase 7. Despite this, it
must also apply before the substitution of 0 for identifiers.
> ... But the other rule from 6.10.1p3 that turns identifiers into
> zeros should not be applied?
That replacement must be applied, on legal code. However, code that,
before that replacement, contains something that would be interpreted as
a cast in phase 7, violates the "shall". Again, this is absolutely
required in order for the prohibition on casts to be meaningful.
> ... And are you also suggesting that the fact
> that the ban on casts in 6.10.1p1 would be meaningless otherwise is
> enough to imply this complicated interpretation?
No. There's no such implication in the standard. I'm trying to figure
out what the writer could possibly have meant by the phrase; this is the
only interpretation I've found that gives it meaning. I don't in any
sense claim that the standard actually requires this interpretation.
Rather, I'm trying to point out the ways in which the wording needs to
be made more explicit, to clear up this confusion. Either that clause
should be removed, or some interpretation similar to the one that I'm
suggesting should be made clear.
I agree.
> and the ban on casts is meaningless
>rather than obscure.
No - see my post on the topic (which people don't seem to have noticed).
Before evaluation, yes. I never claimed otherwise.
The interpretation that I was talking about is that the expression is
checked for syntax twice: first before 'defined' is evaluated, and
then again just before the evaluation of the entire expression, after
identifiers have been replaced with zeros. I never said that I thought
this was the intended interpretation, neither have I said that that it
makes more sense to me than the interpretation that most people seem
to agree on; I just said that I didn't see anything obvious in the
*words* of the Standard to prove that this interpretation is
incorrect.
This interpretation is based on a simple assumption that paragraph one
talks about what the expression looks like before macro expansion
(otherwise, it wouldn't make sense for it to mention the 'defined'
operator, would it). The only hint I could find that the "expression"
is not required to be a syntactically correct expression before the
expansion of 'defined' and macros is that paragraph three does not
call the initial list of preprocessing tokens an "expression". But
paragraph one does; and since it talks about how the "expression" must
be a constant expression and may contain unary operator *expressions*
with the 'defined' operator (which sounds like it talks about the
syntax, doesn't it), it seems to imply that the #if expression before
evaluation of macros and 'defined' operators is expected to be an
expression already (and that's the expression that is not allowed to
contain casts). Of course, it *also* needs to be an expression *after*
the processing described in 6.10.1p3, because that's the expression
that is actually evaluated.
I claim that the assumptions that lead to the "natural" interpretation
are less obvious:
1 Even though the syntax of preprocessor directives generally applies
to the preprocessing tokens produced by phase 3, the
"constant-expression" on the #if line does not need to be a
syntactically correct expression until after it's been transformed
according to 6.10.1p3.
2 The "expression" is not required to be a syntactically correct
constant expression until after 'defined' and macros have been
expanded, even though 6.10.1p1 says that the expression shall be a
constant expression and may may contain 'defined' expressions, in the
same sentence.
3 The order in which 6.10.1p1 mentions things is pretty much the
opposite of the order in which things actually happen: first, the
original set of preprocessing tokens (not necessarily a syntactically
correct expression at this point) is allowed to contain expressions
with the special 'defined' operator; second, identifiers (and those
'defined' expressions) are transformed according to 6.10.1p3; finally,
the resulting set of tokens is interpreted as a constant expression
and is not allowed to contain a cast (which is impossible anyway,
unless casts are looked for before identifiers get replaced with
zeros).
...
> > I still stick to my general point that I don't see a reason why the
> > correctness of an #if expression should depend on whether it would
> > become a correct expression in phase 7 if it had not been an #if
> > expression.
>
> There's no good reason why it should have such a dependence, and I've
> never suggested that it should.
No, not exactly; but you did suggest that the correctness of an #if
expression should depend on whether it would *contain a cast* in phase
7 if it had not been an #if expression:
>> Yes; but only in terms of what "would have been". Specifically, I'm
>> suggesting that what is prohibited is any expression which would
been
>> interpreted during phase 7 as containing a cast, if it had occurred
on
>> it's own rather than inside a #if expression.
My argument was that if it ended up not being a syntactically correct
expression after being interpreted that way, determining whether it
contained a cast or not could be difficult or impossible; and the
preprocessor needs a reliable way of detecting a cast because a cast
is a constraint violation and requires a diagnostic.
My even more general point, which I didn't think it would be polite to
state explicitly at that point, was that it makes no sense for the
correctness of the #if expression to depend on *any* characteristics
of a set of tokens that "would have been" produced without evaluating
the 'defined' operators and could therefore have been completely
different from anything that the real #if expression becomes at any
point between being produced by translation phase 3 and being
evaluated as defined in 6.6. Your answer, confusingly, sometimes
seems to defend the point of view that 'defined' doesn't need to be
evaluated for the purpose of detecting casts, and sometimes makes the
impression that you don't believe that your words could have even
implied that the expression should be processed without evaluating
'defined', even for the sole purpose of detecting casts. For
instance, here:
> What I'm suggesting is the following sequence:
>
> 1. Evaluate the defined operator wherever it occurs.
> 2. Expand macros.
> 3. If at this point, the expression contains anything that would, if it
> were to survive to phase 7, be parsed as a cast, then the code violates
> an explicit "shall".
> In that case, the implementation has no further
> responsibilities for handling it properly. The implementation is not
> required to actually do anything to detect this fact; it merely must
> work correctly as long as the "shall" is satisfied.
Umm... The "shall not contain a cast" in 6.10.1p1 is a constraint, and
a constraint violation requires a diagnostic. Therefore, a cast in an
#if expression must be detected and reported. Or am I missing
something?
> 5. Replace surviving identifiers with 0.
> 6. Parse the modified expression as in phase 7, and evaluate it
> according to the rules applicable to constant-expressions, as described
> in section 6.6.
In short, the original list of preprocessing tokens (not necessarily a
syntactically valid expression at this point) is transformed according
to 6.10.1p3, with the additional step of detecting casts after macro
expansion; and then the transformed tokens are parsed according to
6.6.
The only problem I see with this version is that it requires the
preprocessor to know which identifiers are type names in order to
decide whether things like "(a)+b" contain a cast.
But this sequence is different from what would happen to the #if
expression would be interpreted if it had occured outside of an #if
directive, which was your original suggestion. Outside of an #if,
steps 1 and 5 from this sequence would be omitted. And you would end
up with a potentially different set of tokens, possibly containing
syntax errors or bogus casts produced by the expansion of macros that
would have been eliminated in step 1 if step 1 hadn't been omitted.
As I said before, wanting to omit step 1 doesn't seem to make sense to
me.
> I don't claim that this is what the standard explicitly says; there's
> definitely room for improvement in the wording of the standard. However,
> this is an order that renders all of the of 6.10.1 meaningful. An order
> that supports your interpretation makes an idiot of anyone who
> consciously intended that interpretation to apply. I don't think the
> people who wrote this and approved it were idiots; just fallible.
It's good to know that we agree on at least one point. :-)
> > > #if constant expressions are handled a little differently from the way
> > > they are in normal phase 7 processing. Those differences are described
> > > in 6.10.1. In particular, evaluation of the 'defined' operator occurs
> > > before any other handling of identifiers in #if expressions; otherwise,
> > > the 'defined' operator would be meaningless. If NOT_A_MACRO lives up to
> > > it's name, than "defined NOT_A_MACRO" evaluates to 0.
> >
> > I know; but it was your suggestion that the ban on casts applies to
> > what the expression would normally become in phase 7 if it had not
> > been an #if expression in the first place. And the normal phase 7
> > processing would not consider 'defined' to be an operator.
>
> Yes. However, that is only a ban on casts. It doesn't impose the
> corresponding syntax requirements. One good way for a set of tokens to
> NOT have a cast in it, would be by violating the syntax required of a
> cast. In any event, as you incredulously suggest below, the evaluation
> of the defined operator must occur prior to macro expansion, or it's
> main usefullness would disappear. Macro expansion, in turn, must occur
> prior to parsing the result in accordance with phase 7, or most of the
> usefulness of #if's would disappear.
I never doubted that the evaluation of the defined operator must occur
prior to macro expansion; I was just unsure whether you really meant
that for the purpose of detecting casts, it's not necessary for the
defined operator to be evaluated at all. And call me an idiot if you
want, but I still am.
> > Are you now saying that for the purpose of searching for prohibited
> > casts, the preprocessor should *both* handle 'defined' *and* run the
> > normal translation phases 4 to 7 before the syntax is checked for
> > casts?
>
> Yes and no.
>
> No, because this "shall" doesn't contrain the implementor, it constrains
> the user-written code. The implementor has no requirement to check for
> casts, because there's nothing it needs to do with them. What the
> "shall" means for the implementor is simply that the implementation is
> allowed (but not required) to fail if the expression happens to contain
> a cast. Because of that requirement, an implementation is allowed to
> choke on "((unsigned char)+(i))", even though there's no reason from any
> of the other requirements why it would.
Actually, there is. The above becomes "((0 0)+(0))" after the
processing described in 6.1.10p3, and that's not a valid expression.
Doesn't a syntax error require a diagnostic?
> However, I could also say "yes", or more accurately, "sort of", in the
> following sense: the prohibition on casts can only make sense in terms
> of code after the evaluation of the 'defined' operator, after macro
> expansions and after the parsing required of phase 7. Despite this, it
> must also apply before the substitution of 0 for identifiers.
Ah. So you do agree that it does not make sense to go straight to
macro expansion, without evaluating 'defined', the way the expression
would be interpreted if it had not occured in an #if directive (note
the similarity to the words of your original suggestion)?...
> > ... But the other rule from 6.10.1p3 that turns identifiers into
> > zeros should not be applied?
>
> That replacement must be applied, on legal code. However, code that,
> before that replacement, contains something that would be interpreted as
> a cast in phase 7, violates the "shall". Again, this is absolutely
> required in order for the prohibition on casts to be meaningful.
Yes. At least, it's one of possible ways to make it meaningful. OK,
probably the best one. :-)
> > ... And are you also suggesting that the fact
> > that the ban on casts in 6.10.1p1 would be meaningless otherwise is
> > enough to imply this complicated interpretation?
>
> No. There's no such implication in the standard. I'm trying to figure
> out what the writer could possibly have meant by the phrase; this is the
> only interpretation I've found that gives it meaning. I don't in any
> sense claim that the standard actually requires this interpretation.
> Rather, I'm trying to point out the ways in which the wording needs to
> be made more explicit, to clear up this confusion. Either that clause
> should be removed, or some interpretation similar to the one that I'm
> suggesting should be made clear.
Agreed.
Um... Well I know I haven't. Where can I find it? :-)
Which is quite a different assertion.
> >> Yes; but only in terms of what "would have been". Specifically, I'm
> >> suggesting that what is prohibited is any expression which would
> been
> >> interpreted during phase 7 as containing a cast, if it had occurred
> on
> >> it's own rather than inside a #if expression.
>
> My argument was that if it ended up not being a syntactically correct
> expression after being interpreted that way, determining whether it
> contained a cast or not could be difficult or impossible; and the
> preprocessor needs a reliable way of detecting a cast because a cast
> is a constraint violation and requires a diagnostic.
I'm sorry; I missed that. You're right - an implementation must detect
the cast. However, over-detection is permitted; it could issue the
diagnostic even for things that look like casts, but aren't, as long as
it doesn't halt translation of the program because of that false
detection.
> My even more general point, which I didn't think it would be polite to
> state explicitly at that point, was that it makes no sense for the
> correctness of the #if expression to depend on *any* characteristics
> of a set of tokens that "would have been" produced without evaluating
> the 'defined' operators and could therefore have been completely
Agreed; I'm suggesting that checking of the expression for casts must
occur after 'defined' is evaluated. I initially didn't say so explicitly
because I considered it to be obvious. However, I though that my most
recent posting finally made that clear.
> different from anything that the real #if expression becomes at any
> point between being produced by translation phase 3 and being
> evaluated as defined in 6.6. Your answer, confusingly, sometimes
> seems to defend the point of view that 'defined' doesn't need to be
> evaluated for the purpose of detecting casts, and sometimes makes the
Incorrect.
> impression that you don't believe that your words could have even
> implied that the expression should be processed without evaluating
> 'defined', even for the sole purpose of detecting casts. For
> instance, here:
Correct.
> > What I'm suggesting is the following sequence:
> >
> > 1. Evaluate the defined operator wherever it occurs.
> > 2. Expand macros.
> > 3. If at this point, the expression contains anything that would, if it
> > were to survive to phase 7, be parsed as a cast, then the code violates
> > an explicit "shall".
> > In that case, the implementation has no further
> > responsibilities for handling it properly. The implementation is not
> > required to actually do anything to detect this fact; it merely must
> > work correctly as long as the "shall" is satisfied.
>
> Umm... The "shall not contain a cast" in 6.10.1p1 is a constraint, and
> a constraint violation requires a diagnostic. Therefore, a cast in an
> #if expression must be detected and reported. Or am I missing
> something?
No - I did.
> > 5. Replace surviving identifiers with 0.
> > 6. Parse the modified expression as in phase 7, and evaluate it
> > according to the rules applicable to constant-expressions, as described
> > in section 6.6.
>
> In short, the original list of preprocessing tokens (not necessarily a
> syntactically valid expression at this point) is transformed according
> to 6.10.1p3, with the additional step of detecting casts after macro
> expansion; and then the transformed tokens are parsed according to
> 6.6.
>
> The only problem I see with this version is that it requires the
> preprocessor to know which identifiers are type names in order to
> decide whether things like "(a)+b" contain a cast.
An implementation could issue a diagnostic about any identifier
appearing in a #if constant-expression in a context where it could be
interpreted as a cast, whether or not that identifier turned out to
actually be a type name. Then it could go ahead and replace the
identifier with a 0, and continue processing normally, whether or not
the diagnostic was issued. That behavior would meet all requirements.
Remember, spurious diagnostics are always allowed.
> But this sequence is different from what would happen to the #if
> expression would be interpreted if it had occured outside of an #if
> directive, which was your original suggestion. Outside of an #if,
> steps 1 and 5 from this sequence would be omitted. And you would end
Step 5 is needed only for evaluating the expression, not in checking it
for casts. I considered step 1 to be implicit, so I said nothing about
it. Perhaps I should have made that explicit.
> up with a potentially different set of tokens, possibly containing
> syntax errors or bogus casts produced by the expansion of macros that
> would have been eliminated in step 1 if step 1 hadn't been omitted.
> As I said before, wanting to omit step 1 doesn't seem to make sense to
> me.
I'm in perfect agreement on that.
...
> > a cast. Because of that requirement, an implementation is allowed to
> > choke on "((unsigned char)+(i))", even though there's no reason from any
> > of the other requirements why it would.
>
> Actually, there is. The above becomes "((0 0)+(0))" after the
> processing described in 6.1.10p3, and that's not a valid expression.
> Doesn't a syntax error require a diagnostic?
Sorry; I should have chosen a single-token type name.
...
> Ah. So you do agree that it does not make sense to go straight to
> macro expansion, without evaluating 'defined', the way the expression
> would be interpreted if it had not occured in an #if directive (note
> the similarity to the words of your original suggestion)?...
Yes, as explained above.
Yes, but that assertion is what caused our misunderstanding. I had
taken your original wording literally and tried to argue that it
doesn't make sense to omit the expansion of 'defined' and then analyze
the wrong expression, while you assumed that it was obvious that you
didn't mean the expansion of 'defined' to be omitted, which made it
difficult for you to see what I was talking about.
...
> > My argument was that if it ended up not being a syntactically correct
> > expression after being interpreted that way, determining whether it
> > contained a cast or not could be difficult or impossible; and the
> > preprocessor needs a reliable way of detecting a cast because a cast
> > is a constraint violation and requires a diagnostic.
>
> I'm sorry; I missed that. You're right - an implementation must detect
> the cast. However, over-detection is permitted; it could issue the
> diagnostic even for things that look like casts, but aren't, as long as
> it doesn't halt translation of the program because of that false
> detection.
This sounds dangerous... Taken to the extreme, it allows a poor
quality implementation to satisfy all the possible requirements for
diagnostics by saying "warning: this code may be incorrect in some
way" unconditionally for every translation unit... ;-)
> > My even more general point, which I didn't think it would be polite to
> > state explicitly at that point, was that it makes no sense for the
> > correctness of the #if expression to depend on *any* characteristics
> > of a set of tokens that "would have been" produced without evaluating
> > the 'defined' operators and could therefore have been completely
>
> Agreed; I'm suggesting that checking of the expression for casts must
> occur after 'defined' is evaluated. I initially didn't say so explicitly
> because I considered it to be obvious. However, I though that my most
> recent posting finally made that clear.
Yes.
> > different from anything that the real #if expression becomes at any
> > point between being produced by translation phase 3 and being
> > evaluated as defined in 6.6. Your answer, confusingly, sometimes
> > seems to defend the point of view that 'defined' doesn't need to be
> > evaluated for the purpose of detecting casts, and sometimes makes the
>
> Incorrect.
Well, it really did seem that way to me; anyway, it doesn't matter
now.
...
> > But this sequence is different from what would happen to the #if
> > expression would be interpreted if it had occured outside of an #if
> > directive, which was your original suggestion. Outside of an #if,
> > steps 1 and 5 from this sequence would be omitted. And you would end
>
> Step 5 is needed only for evaluating the expression, not in checking it
> for casts. I considered step 1 to be implicit, so I said nothing about
> it. Perhaps I should have made that explicit.
It certainly would have made things clearer for me. As you have seen,
I tend to take things literally even if it seems obvious to me that
they don't make a lot of sense that way...
You think you're joking, but that's a perfectly legal implementation.
Useful diagnostics are purely a quality of implementation (QoI) issue;
the standard only requires that they be produced, and be identifiable.
They're never prohibited, and it never requires that more than one
diagnostic be produced, no matter how many different defects your code
may contain.
Message-ID: <8dOtkQz5...@romana.davros.org>
The key point:
More to the point, however, while it is currently the case that every
possible type-name involves at least one keyword or identifier, that
might not always be the case (and the implementation might have extended
type names that don't involve them either). Therefore casts are
forbidden even though, at present, they either convert to a
parenthesised constant or a syntax error.
--
I know; luckily, it's not practical for a real-world implementation to
go that far...
> Useful diagnostics are purely a quality of implementation (QoI) issue;
> the standard only requires that they be produced, and be identifiable.
> They're never prohibited, and it never requires that more than one
> diagnostic be produced, no matter how many different defects your code
> may contain.
I understand why the Standard doesn't want to talk about what a
diagnostic should or shouldn't look like; on the other hand, it would
be nice if diagnostics were required to tell the truth. Or at least
to flag possible lies with a qualifier like "possible", "suspected" or
something like that. Even a poor quality implementation is required
to be capable of translating some code; wouldn't it make sense to also
require it to never say that correct code is broken?
Ah. Thank you.
Still, this sounds like the ban on casts is usually meaningless, and
any possible exceptions are rather obscure... ;-)
Consider the code:
#define ALPHA + 0
#define BRAVO ALPHA
#define CHARLIE BRAVO
#define DELTA CHARLIE
#if ECHO DELTA CHARLIE BRAVO ALPHA
Is this valid ? Your interpretation would rule it out. The "usual" one
doesn't.
>My argument was that if it ended up not being a syntactically correct
>expression after being interpreted that way, determining whether it
>contained a cast or not could be difficult or impossible; and the
>preprocessor needs a reliable way of detecting a cast because a cast
>is a constraint violation and requires a diagnostic.
I'm not at *all* sure that that's a valid line of argument. If no code
could violate a constraint, there is no need to be able to detect it. As
a trivial example:
The type specifiers _Complex and _Imaginary shall not
be used if the implementation does not provide those
types.
need not be tested for on an implementation that provides complex and
imaginary types.
>The only problem I see with this version is that it requires the
>preprocessor to know which identifiers are type names in order to
>decide whether things like "(a)+b" contain a cast.
This can't be done. Whether or not something is a type name cannot be
determined until translation phase 7.
>Actually, there is. The above becomes "((0 0)+(0))" after the
>processing described in 6.1.10p3, and that's not a valid expression.
>Doesn't a syntax error require a diagnostic?
Yes.
I know. As I said in the paragraph you quoted, I don't think this
interpretation was intended or is very useful; I was only arguing that
I didn't see anything obvious in the *words* of the Standard to prove
that this interpretation is wrong.
> >My argument was that if it ended up not being a syntactically correct
> >expression after being interpreted that way, determining whether it
> >contained a cast or not could be difficult or impossible; and the
> >preprocessor needs a reliable way of detecting a cast because a cast
> >is a constraint violation and requires a diagnostic.
>
> I'm not at *all* sure that that's a valid line of argument. If no code
> could violate a constraint, there is no need to be able to detect it. As
> a trivial example:
The above is just a small part of a longer argument about James
Kuyper's suggested interpretation of 6.10 that made the constraint
possible to violate. Or, more accurately, about my literal
interpretation of his words.
> >The only problem I see with this version is that it requires the
> >preprocessor to know which identifiers are type names in order to
> >decide whether things like "(a)+b" contain a cast.
>
> This can't be done. Whether or not something is a type name cannot be
> determined until translation phase 7.
That's why I see that as a problem. :-)