Keith Thompson said:
Tim Rentsch said:
Keith Thompson said:
[...]
I understand your thesis here, but you still haven't presented
any convincing evidence that it's correct. Any device for
which a conforming C compiler cannot be fairly easily provided
is so limited (or perhaps unorthodox) that a suitable language
for it would not be C-like except perhaps very superficially.
I think this may be the fundamental point on which we disagree.
I haven't worked on very small embedded systems, but I can
imagine a system (again, an 8051 or 4004 might qualify) where it
makes perfectly good sense to implement all C syntax and
semantics *except* that, say, floating-point types and integers
wider than 16 bits are not supported.
This can be done in a conforming implementation, as I have
already explained.
[...]
An implementation that has no support for floating-point, for a
target system on which such support would not be useful, can be
"conforming" only in some sense that is practically
indistinguishable from non-conformance.
I'm not sure why you put quotes around the word conforming, since
I am using the word in the same sense that the Standard defines
it. More importantly the phrasing used disguises the distinction
I am trying to highlight: the key question is not whether an
implmentation is conforming -- which might be true purely by
accident -- but whether its implementtors are committed to
delivering a conforming implementation. Continued below...
The only references to implementation-specific capacity limits
that I can find in N1570 are:
Section 1, paragraph 2:
This International Standard does not specify
[...]
- the size or complexity of a program and its data that will
exceed the capacity of any specific data-processing system
or the capacity of a particular processor
and section 5.2.4.1, which discusses translation limits and has
the long list of things like "127 nesting levels of blocks".
What you're suggesting, if I understand correctly, is that an
implementation could accept a declaration of a structure containing
function pointers, bit fields, and 42 levels of nested unions, but
reject a declaration "float x;" because it exceeds its capacity,
and claim conformance to the standard. Is my understanding of your
position correct?
No, I wasn't considering section 1 paragraph 2. More below.
I suppose that's true in the most literal sense, but I don't believe
the standard as written is *intended* to make floating-point an
optional feature. By your reasoning, a compiler could refuse to
accept *any* arbitrary language feature as long as it issues a
diagnostic and justifies that rejection as a capacity limitation
(regardless of how the diagnostic is phrased).
I don't believe the permission to impose capacity limits is
intended to permit omitting entire non-optional features.
My remarks are about the letter of the Standard, not its intent.
Furthermore I don't the Standard signals any clear intent in this
regard, so going by the letter is pretty much the only option
available.
To take things just a little further, I could construct a single
C program that precisely hits every one of the translation limits
in 5.2n.4.1 and produces no output or other visible behavior. I
could then write a "compiler" that recognizes that single program
and creates an executable that does nothing, while rejecting
every other C source file. I would then have a conforming C
implementation according to the letter of the standard -- but it
would be utterly useless.
I'm not sure I completely agree with your conclusion, but even if
I did it wouldn't change what I'm saying. The Standard admits
any number of behaviors in conforming implementations that are
utterly useless.
(I believe the intent of 5.2.4.1 is that any *reasonable*
compiler that accepts that "one program" will also accept most
real-world code; the easiest way to satisfy 5.2.4.1 while
implementing a useful compiler is to avoid imposing any fixed
limits at all, which is what most vendors have actually done.)
This comment glosses over an important distinction, namely,
the difference between "accept" and "translate and execute".
Conforming implementations are required to "translate and
execute" only one program, but they are required to "accept"
any strictly conforming program. Any implementation may
choose not to accept a program having a declaration
char foo[ 65536 ];
because the program is not strictly conforming. However, a
conforming hosted implementation must accept the following
program
#include <stdio.h>
char small_foo[ 32767 ];
int main( void ){ return 0; }
(even if it doesn't translate and execute it) because the program
is strictly conforming when considered in a hosted environment.
This program may be accepted by a conforming freestanding
implementation, but it need not be, because of the inclusion of a
system header <stdio.h>. Now how about the same program but
without the #include line:
char small_foo[ 32767 ];
int main( void ){ return 0; }
I would argue that a conforming freestanding implementation is
not obliged to accept this program, because it is not strictly
conforming when considered in a freestanding environment.
Certainly the Standard anticipates results of this kind, because
it specifically designates the limit on minimum object size given
in 5.2.4.1 p1 as applying to "hosted implementations only".
Furthermore this result coincides with an intuitive sense of what
"freestanding" means, namely, for minimal environments. How
minimal is minimal? Pointedly, the Standard doesn't say. So
even though carrying that reasoning down to very small sizes
might be unexpected, I don't think it is contrary to the spirit
of the notion that freestanding implementations may be "tiny", so
be prepared to accept them as such.
Incidentally, considering the cited portion in 1 p2, note that a
conforming implementation could /accept/ a program that uses
floating point but not /translate and execute/ said program,
citing a capacity limit. Such an implementation clearly conforms
to the letter of what constitutes a conforming implementation,
and considering 1 p2 I believe it conforms to the spirit as well.
Yes, I *think* the Standard allows such behavior, but I hardly
think that's the only *relevant* question.
What I meant was relevant to the point under discussion, which is
concerned only with whether certain implementations are
conforming.
And I'm not
completely convinced that the Standard actually allows it. The
counterargument would be that failing to implement floating-point
does not constitute a *capacity* limitation as permitted by 1p2.
Not a compelling argument. Clearly there are existing hardware
targets having memory limitations severe enough so that
implementing even a single floating-point operation exceeds the
capacity of program memory. But the same model processor with
more memory might have enough room for floating-point operation
subroutines.
How is it impossible to answer? I believe a single compiler
could simultaneously meet either description, with the only
difference being how it's described.
Two problems: one, what is meant by the terms isn't clear; two,
there are two implementations, but what is different between
them isn't specified. Apparently your intention was to ask
about the phrases applied to the same (unknown) implementation,
but the way the question was asked that wasn't clear.
Suppose we have two groups of implementors, A and B. Group A has
decided to do a conforming implementation. Group B has decided
to implement "C-", which is most of C but leaves out some things
that they feel would impact the implementation too much, such as
floating point and 'long long'. The implementation of group A is
conforming working under the assumption of the thesis posited
above. The implementation of group B might or might not be
conforming under the same conditions.
Given this state of affairs, what do we know? About the
implementation done by group A, we know two things:
1. The implementation will issue a diagnostic for any
program that contains a constraint violation, and
2. Any program that is accepted without a diagnostic will
behave as specified by the relevant ISO standard.
About the implementation done by group B, we know essentially
nothing. Probably it will behave like ISO C in most cases, but
there's no way to be sure without a careful reading of the
documentation done for implementation B, and even then we might
not know. Furthermore, when the next release arrives things
might be different, because group B has decided on their own what
language to implement. Granted, group B's language(s) will be a
lot like standard C, but just how much is up to them - having
abandoned the principle of conformance in one area, we may
reasonably expect they may be lax about conformance in another
area.
Suppose Group B makes the following guarantees:
1. Our implementation fully conforms to the 2011 C standard as a
freestanding implementation *except* that floating-point and
types "long long" and "unsigned long long" are not supported,
and the widest integer types are 32 bits. (A few additional
paragraphs would be needed to cover the ramifications, such as
the types used for
intmax_t and the behavior of
preprocessor expressions.)
2. Our implementation will issue a diagnostic for any program
that contains a constraint violation (and for any program that
violates the restrictions stated in (1)).
3. Any program that is accepted without a diagnostic will behave
as specified by the 2011 ISO C standard.
Obviously that would be a different hypothetical (let me call
it B'). A key difference is I think B' is much less likely to
occur than either A or B - once a group has decided to be "not
conforming", they won't care so much about paying attention to
conformance in other areas.
I think you're stretching the meaning of a "conforming
implementation", not necessarily beyond the Standard's definition
of that term, but beyond what is reasonable. I think that a
typical user would think of a compiler that rejects any use of
floating-point as non-conforming. The fact that it can be
justified by citing the standard would be at best an interesting
but obscure curiosity. (That's pretty much how I think of it,
and I'm far more pedantic than a typical user.)
With all due respect, I think you're reading something into the
Standard that it doesn't say and doesn't mean to say. Being a
conforming implementation clearly is not the same as being a
high-quality implementation. Apparently you think of being
conforming as guaranteeing some minimal level of quality (in
terms of what programs it will execute correctly). It doesn't,
nor was it meant to.