Does a constraint violation always result in a diagnostic? - c

I was wondering what would happen if I left out the parameter declarations in a K&R function definition, so I tried the following:
#include <stdio.h>
void f(a)
// int a; <--- omitting `declaration-list` causes warning in gcc, but nothing in clang
{
printf("%d", 9);
}
int main()
{
f(9);
return 0;
}
When I compile this with gcc 11, a diagnostic is issued in the form of a warning (i.e. warning: type of 'a' defaults to 'int'). However, when I compile it with clang 14, no diagnostic message is issued.
This confused me because in the Standard, omitting the declaration-list (which contains the parameter declarations) is a constraint violation (as per C11 6.9.1(6)), which requires a conforming implementation to issue a diagnostic (as per C11 5.1.1.3). So, why didn't clang issue a warning? Have I misinterpreted something?
EDIT: Must a diagnostic be issued if a constraint violation can be "fixed" by the compiler applying some other provision in the Standard?
For example, the empty declaration list in the code above has always been a constraint violation (since C89). However, prior to C99, the Standard also had an implicit int rule (see C90 6.5.2), which would have allowed the compiler to assume any undeclared parameters as being of type int and thereby recover from the no-empty-declaration-list constraint violation.
So, would a pre-1999 ANSI-conformant compiler have still issued a diagnostic in this case? My first impression was that it would have since the constraint would have taken precedence over the implicit int rule, but pg. 287 of Harbison and Steele says (emphasis added):
In the pre-Standard traditional form, the parameter names are listed in the declarator and the types are specified (in any order) in the declaration-list opt following the declarator. All parameters should be declared in the declaration-list, but prior to C99 omitted parameter declarations defaulted to type int.
The bit in bold makes it seem like empty declaration lists were allowed since the implicit int rule would have "fixed" any no-empty-declaration-list constraint violations.

clang will generate a warning if compiled with the -pedantic flag.
<source>:3:8: warning: parameter 'a' was not declared, defaulting to type 'int' [-Wpedantic]
void f(a)
^
1 warning generated.
ASM generation compiler returned: 0
<source>:3:8: warning: parameter 'a' was not declared, defaulting to type 'int' [-Wpedantic]
void f(a)
^
1 warning generated.
Execution build compiler returned: 0
Program returned: 0
9
https://godbolt.org/z/e73M1Eq8E

First of all, please do not assume conforming behavior from the "gcc like" compilers including clang, unless you explicitly compile with -std=c17 -pedantic-errors. clang 14 does give a diagnostic if you use these options. That name "pedantic" was very poorly chosen by gcc back in the days. A better name would have been -conforming or something like that, because that's the behavior that this option actually unlocks.
So, why didn't clang issue a warning? Have I misinterpreted something?
I believe you are reading the standard correctly. We can conclude that clang 14 is non-conforming without a -pedantic flag added.
However, when I compile it with clang 14, no diagnostic message is issued.
There was apparently a known bug which has been corrected in clang 15. See Clang 15.0.0 release notes:
The -Wimplicit-int warning diagnostic now defaults to an error in C99 and later. Prior to C2x, it may be downgraded to a warning with -Wno-error=implicit-int, or disabled entirely with -Wno-implicit-int. As of C2x, support for implicit int has been removed, and the warning options will have no effect. Specifying -Wimplicit-int in C89 mode will now issue warnings instead of being a noop.
Therefore clang 15.0.0 (x86) with default options (with or without -pedantic-errors) gives:
error: parameter 'a' was not declared, defaults to 'int'; ISO C99 and later do not support implicit int [-Wimplicit-int]
void f(a)
clang -Wno-implicit-int disables the error, as per the bug fix above.
Must a diagnostic be issued if a constraint violation can be "fixed" by the compiler applying some other provision in the Standard?
Upon finding a constraint violation, a diagnostic must always be issued by a conforming implementation. This is normative behavior as per C17 5.1.1.3. See What must a C compiler do when it finds an error?
For example, the empty declaration list in the code above has always been a constraint violation (since C89)
I think so too. The C90 6.7.1 constraints say
If the declarator includes an identifier list, each declaration in the declaration list shall have at least one declarator, and those declarators shall declare only identifiers from the identifier list
In your example a is an identifier.
So, would a pre-1999 ANSI-conformant compiler have still issued a diagnostic in this case?
The difference between C90 and C99 is the syntax. C90 6.7.1 says declaration-specifiersopt and C99 6.9.1 says declaration-specifiers, no longer optional. Both standards has the declaration-listopt as optional, but as per the previously quoted constraints, there must be a declaration list in case a declaration with a named identifier is present.
I guess some compilers may have read the optional part in the syntax like "in C90 you could omit both the declaration specifiers and the declaration list", which agrees with the syntax but not the constraints. It's a bit ambiguous, particularly since C90 6.7.1 also explicitly states:
Any parameter that is not declared has type int.
This part was removed in C99.

Related

cl: array type has incomplete element type => why warning and not error?

Sample code (t0.c):
typedef struct S1 T1;
typedef T1 T[];
int main(void)
{
return sizeof(T);
}
Results:
clang (version 11.0.0):
t0.c:2:13: error: array has incomplete element type 'T1' (aka 'struct S1')
gcc (version 10.2.0):
t0.c:2:12: error: array type has incomplete element type 'T1' {aka 'struct S1'}
cl (version 19.25.28611):
t0.c(6): warning C4034: sizeof returns 0
Question: why cl generates warning and not error?
UPD.
If /Za (Disable Language Extensions) is specified, then __STDC__ is defined with definition 1.
According to ANSI Conformance page (https://learn.microsoft.com/en-us/cpp/c-language/ansi-conformance?view=msvc-160):
Microsoft C conforms to the standard for the C language as set forth in the 9899:1990 edition of the ANSI C standard.
ISO/IEC 9899:1990, 6.3.3.4 The sizeof operator (emphasis added):
The sizeof operator shall not be applied to an expression that has function type or an incomplete type, to the parenthesized name of such a type, or to an lvalue that designates a bitfield object.
UPD2. Added compiler versions.
Whether something is a "warning" or an ”error" is left up to the individual implementation:
5.1.1.3 Diagnostics
1 A conforming implementation shall produce at least one diagnostic message (identified in an implementation-defined manner) if a preprocessing translation unit or translation unit contains a violation of any syntax rule or constraint, even if the behavior is also explicitly specified as undefined or implementation-defined. Diagnostic messages need not be produced in other circumstances.9)
9) The intent is that an implementation should identify the nature of, and where possible localize, each violation. Of course, an implementation is free to produce any number of diagnostics as long as a valid program is still correctly translated. It may also successfully translate an invalid program.
C 2011 Online Draft
Among most compilers that I’ve used, anything that halts translation (an unrecoverable syntax error like a missing } or an undeclared identifier) is treated as an error, whereas issues that may cause runtime errors are treated as warnings, but there’s no uniform standard for that. As long as the basic requirements above are met, the implementation is free to do what it wants. It can issue a single diagnostic of "?" in response to all syntax and/or constraint errors and meet the requirements of the standard.

Can a function call discard cons qualifier of its parameter?

The following code causes an error in Green Hills C compiler (error: type int * is incompatible with argument type const int*), while it only produces a warning and compiles with gcc (warning: passing argument 1 of ‘func’ discards ‘const’ qualifier from pointer target type).
void func1(int* a)
{
(*a)++;
}
const int g = 100;
void func2(void)
{
func1(&g);
}
Which behavior is according to C standard?
The call to func1(&g) is invalid. It is a constraint violation in C language, i.e. it is what in everyday terminology is usually referred to as an "error". C language does not support implicit conversion of const int * value to int * type.
The fact that it is "just a warning" in GCC does not mean anything. Use -pedantic-errors switch in GCC to make it report constraint violations as "errors". Green Hills C compiler, as you observed yourself, reports it as an "error" without any help from your side.
Which behavior is according to C standard?
Both compiler behaviors conform with the standard. As #AnT already explained, the call func1(&g) violates a language constraint. The standard's requirement on the compiler in such a case is expressed by paragraph 5.1.1.3/1:
A conforming implementation shall produce at least one diagnostic message (identified in an implementation-defined manner) if a preprocessing translation unit or translation unit contains a violation of any syntax rule or constraint [...]
As you can see, #Olaf is right that the standard does not distinguish different categories of diagnostics. That's an invention and convention of implementations, which generally distinguish between the two based on whether the situation is fatal to the compilation process. The standard has nothing further to say about what a compiler should do so when a constraint violation is detected, however, neither in general nor in this particular case, so it does not even indirectly dictate whether a "warning" or "error" should be emitted.
If the compiler does continue and eventually produces an executable, then the whole resulting program exhibits undefined behavior if at any point in its run it evaluates the problematic function call. This is a reason why a compiler might choose not to emit such a binary (i.e. might consider the constraint violation an "error"), but, again, the standard does not provide any guidance there.

C Compiler not throwing error when no type is specified

Why this below program not throwing an error:
dfljshfksdhfl;
#include <stdio.h>
int main () {
return 0;
}
gcc would just throw a warning:
test.c:1:1: warning: data definition has no type or storage class [enabled by default]
This is because even though implicit int is no longer part of the C standard since C99 some compilers still support it, mainly to prevent breaking a lot of old code. So this line:
dfljshfksdhfl;
ends up being equivalent to:
int dfljshfksdhfl;
clang gives us a much more informative warning by default:
warning: type specifier missing, defaults to 'int' [-Wimplicit-int]
dfljshfksdhfl;
^~~~~~~~~~~~~
We can use the -pedantic-errors flag to turn this into an error, although oddly enough this does not work for clang and so we have to resort to -Werror and turn all warnings into errors, which is actually a good habit to get into. As remyabel points out for clang we can also use -Werror=implicit-int.
I've already answered a similar question (actually I'm pretty sure it's a duplicate, but whatever) and the answer is found in the C99 rationale.
A new feature of C99:
In C89, all type specifiers could be omitted from the declaration
specifiers in a declaration. In such a case int was implied. The
Committee decided that the inherent danger of this feature outweighed
its convenience, and so it was removed. The effect is to guarantee the
production of a diagnostic that will catch an additional category of
programming errors. After issuing the diagnostic, an implementation
may choose to assume an implicit int and continue to translate the
program in order to support existing source code that exploits this
feature.
#Shafik's answers tells you one way to turn the warning into an error (for Clang). If you consider -Werror to be too strict, you can turn that one warning into an error with -Werror=implicit-int. In GCC, it appears that -pedantic-errors is necessary.
First of all, gcc is not a conforming C compiler by default. It implements a dialect of C89/C90 with GNU extensions.
You can use -std=cNN -pedantic (where NN can be 90, 99, or 11) to cause it to (attempt to) conform to a specified version of the ISO C standard. C90 permitted implicit int; it was dropped in C99.
But C compilers are not actually required to generate fatal error messages (except for a #error directive). The standard's requirement (N1570 5.1.1.3p1) is:
A conforming implementation shall produce at least one diagnostic
message (identified in an implementation-defined manner) if a
preprocessing translation unit or translation unit contains a
violation of any syntax rule or constraint, even if the behavior is
also explicitly specified as undefined or implementation-defined.
Diagnostic messages need not be produced in other circumstances.
A non-fatal warning qualifies as a "diagnostic message". A conforming C compiler can print a warning for any error -- even a syntax error -- and then continue to successfully compiler the source file. (This is how some compiler-specific language extensions may be supported.)
Personally, I find gcc to be overly lax about certain errors; in my opinion, a missing int should be treated as a fatal error. But that's just my preference, not a requirement imposed by the standard.
The lesson here is that you should not assume that mere warnings are harmless. Ideally, compiling your code should produce no diagnostics at all. Cases where it's ok to ignore warnings are rare (but they do exist, since compilers are free to warn about perfectly valid code).

implicit int in c language

I am using orwell dev c++ IDE. I know that in the old C89 Standard & pre standard C++ supports default to int rule when return type of function isn't explicitly specified in function definition. But it has banned in C++. But recently i wrote following simple C program and it works fine.
#include <stdio.h>
void fun();
int main(void)
{
int a=9;
printf("%d",a);
printf("%d",a);
fun();
return 0;
}
a=1;
void fun()
{
printf("%d",a);
}
Is it true that default int rule is also applied to variables? My compiler shows me following warnings.
[Warning] data definition has no type or storage class [enabled by default]
[Warning] type defaults to 'int' in declaration of 'a' [enabled by default]
Why C99 standard still allows default to int? It fails in compilation in C++. Correct me if i am wrong? This C program also works on on line compilers like ideone.com
This is explained in the C99 rationale:
A new feature of C99:
In C89, all type specifiers could be omitted from the declaration
specifiers in a declaration. In such a case int was implied. The
Committee decided that the inherent danger of this feature outweighed
its convenience, and so it was removed. The effect is to guarantee the
production of a diagnostic that will catch an additional category of
programming errors. After issuing the diagnostic, an implementation
may choose to assume an implicit int and continue to translate the program in order to support existing source code that exploits this feature.
In other words, it's officially removed from the C99 standard, but compilers may still choose to follow this behavior and issue a diagnostic, as GCC does. For example, view their warning options page for -Wimplicit-int. To make these warnings compile as errors, use -pedantic-errors or -Werror.
As per #Anonymous's answer, c++98 contains a similar rule about type specifiers.
7.1.5/2
At least one type-specifier that is not a cv-qualifier is required
in a declaration unless it declares a constructor, destructor or
conversion function.80)
80) There is no special provision for a
decl-specifier-seq that lacks a type-specifier or that has a type-specifier that only specifies cv-qualifiers. The "implicit int" rule of C is no longer supported.
For example, GCC supports ISO/IEC 14882:1998 and above, so this would be an error no matter what.
The C99 standard does not allow types to be omitted.
Section 6.7.2.2 says:
At least one type specifier shall be given in the declaration
specifiers in each declaration, and in the specifier-qualifier list in
each struct declaration and type name.

implicit int and implicit declaration of functions with gcc compiler

I read in the c99 Standard:
-remove implicit function declaration,
-remove implicit int.
But when I try to compile this code with gcc compiler in c99 mode using -pedantic
main(void){
f(3);
return 0;
}
int f(int a){
....
}
I expect 2 errors, but I just receive 2 warnings:
-warning: return type defaults to ‘int’
-warning: implicit declaration of function ‘f’.
Shouldn't them be errors in c99?
http://gcc.gnu.org/c99status.html
In both situations there's written "done".
Thanks.
The C standard requires a diagnostic for any translation unit containing a violation of a syntax rule or constraint. It does not require such diagnostics to be fatal; the compiler is free to continue processing the source file. The behavior of the resulting executable, if any, is undefined. The standard makes no distinction between warnings and fatal errors.
(The only thing that requires a compiler to reject a source file is the #error directive.)
Conclusion: when compiling C, take warnings very seriously.
I don't believe the compiler is required to produce a fatal error. Use -Werror if you're concerned...
Two points: first, it may (usually does) take a specific set of flags to get a compiler to conform with the standard.
Second, all that's required by the standard is that the implementation issue a "diagnostic" in the case of an error -- but it's up to the implementation to define what is or isn't a diagnostic. It's free to say a "warning" is a diagnostic if it wants to. When a diagnostic is issued, it may quit compiling, or it can compile the code anyway.
Bottom line: what it's doing is probably enough to conform, for whatever that's worth.

Resources