Take this program:
#include <stdlib.h>
inline int F(){return rand();}
int main(void) {
return F();
}
This compiles fine with gcc using -O1 -O2 -O3.
But compile without optimising, and it fails to link as it can't find
a function 'F'.
Is it supposed to behave like that? I assume no discrete function F is
being generated because of the 'inline' attribute, but you'd think it
would ignore that if inlining wasn't enabled.
It compiles fine with or without 'inline' using tcc and bcc (my product).
Clang fails too, but clearly only because it has to duplicate gcc's
behaviour for drop-in compatability, even if it is crass.
(This is the actual program: https://web.archive.org/web/20010127204000/http://www.bagley.org/~doug/shootout/bench/lists/lists.gcc)
Take this program:
#include <stdlib.h>
inline int F(){return rand();}
int main(void) {
return F();
}
This compiles fine with gcc using -O1 -O2 -O3.
But compile without optimising, and it fails to link as it can't
find a function 'F'.
Is it supposed to behave like that?
Take this program:
#include <stdlib.h>
inline int F(){return rand();}
int main(void) {
return F();
}
This compiles fine with gcc using -O1 -O2 -O3.
But compile without optimising, and it fails to link as it can't find
a function 'F'.
Is it supposed to behave like that? I assume no discrete function F
is being generated because of the 'inline' attribute, but you'd think
it would ignore that if inlining wasn't enabled.
It compiles fine with or without 'inline' using tcc and bcc (my
product).
Clang fails too, but clearly only because it has to duplicate gcc's behaviour for drop-in compatability, even if it is crass.
(This is the actual program: https://web.archive.org/web/20010127204000/http://www.bagley.org/~doug/shootout/bench/lists/lists.gcc)
Take this program:
#include <stdlib.h>
inline int F(){return rand();}
int main(void) {
return F();
}
This compiles fine with gcc using -O1 -O2 -O3.
But compile without optimising, and it fails to link as it can't find
a function 'F'.
Is it supposed to behave like that?
On Thu, 26 Mar 2026 22:36:59 +0000
Bart <bc@freeuk.com> wrote:
Take this program:
#include <stdlib.h>
inline int F(){return rand();}
int main(void) {
return F();
}
This compiles fine with gcc using -O1 -O2 -O3.
But compile without optimising, and it fails to link as it can't find
a function 'F'.
Is it supposed to behave like that? I assume no discrete function F
is being generated because of the 'inline' attribute, but you'd think
it would ignore that if inlining wasn't enabled.
[...]
Sounds like a bug introduced during transition to c99/gnu99
front end.
With gcc4.x, which defaults to gnu89 it still works.
Also works with the latest gcc -std=gnu89
But there is no hope that gcc maintaines will ever admit that it is a
bug.
Bart <bc@freeuk.com> writes:
Take this program:
#include <stdlib.h>
inline int F(){return rand();}
int main(void) {
return F();
}
This compiles fine with gcc using -O1 -O2 -O3.
But compile without optimising, and it fails to link as it can't find
a function 'F'.
Is it supposed to behave like that? I assume no discrete function F is
being generated because of the 'inline' attribute, but you'd think it
would ignore that if inlining wasn't enabled.
It compiles fine with or without 'inline' using tcc and bcc (my product).
Clang fails too, but clearly only because it has to duplicate gcc's
behaviour for drop-in compatability, even if it is crass.
(This is the actual program:
https://web.archive.org/web/20010127204000/http://www.bagley.org/~doug/shootout/bench/lists/lists.gcc)
I don't have an answer to your question, but defining F as
"static inline" makes the program work. You can also do this:
inline int F();
int F(){return rand();}
(Unless you're using a C23 compiler, I suggest "int F(void)" rather
than "int F()".)
On 26/03/2026 23:12, Keith Thompson wrote:
Bart <bc@freeuk.com> writes:
Take this program:
˙˙ #include <stdlib.h>
˙˙ inline int F(){return rand();}
˙˙ int main(void) {
˙˙˙˙˙˙ return F();
˙˙ }
This compiles fine with gcc using -O1 -O2 -O3.
But compile without optimising, and it fails to link as it can't find
a function 'F'.
Is it supposed to behave like that? I assume no discrete function F is
being generated because of the 'inline' attribute, but you'd think it
would ignore that if inlining wasn't enabled.
It compiles fine with or without 'inline' using tcc and bcc (my
product).
Clang fails too, but clearly only because it has to duplicate gcc's
behaviour for drop-in compatability, even if it is crass.
(This is the actual program:
https://web.archive.org/web/20010127204000/http://www.bagley.org/~doug/shootout/bench/lists/lists.gcc)
I don't have an answer to your question, but defining F as
"static inline" makes the program work.˙ You can also do this:
˙˙˙˙ inline int F();
˙˙˙˙ int F(){return rand();}
Using 'static' to force an actual function to be generated sounds unintuitive. It is when F is exported to be called from another module
that a tangible F function needs to exist.
But my example came from some pre-existing legacy code, where I was surprised that -O0 made it fail.
(Unless you're using a C23 compiler, I suggest "int F(void)" rather
than "int F()".)
Nobody bothers with that any more. Most seem to assume that () already
means zero parameters anyway, judging by the incorrect usage I
constantly saw in open source code.
Michael S <already5chosen@yahoo.com> writes:
On Thu, 26 Mar 2026 22:36:59 +0000
Bart <bc@freeuk.com> wrote:
Take this program:
#include <stdlib.h>
inline int F(){return rand();}
int main(void) {
return F();
}
This compiles fine with gcc using -O1 -O2 -O3.
But compile without optimising, and it fails to link as it can't
find a function 'F'.
Is it supposed to behave like that? I assume no discrete function
F is being generated because of the 'inline' attribute, but you'd
think it would ignore that if inlining wasn't enabled.
[...]
Sounds like a bug introduced during transition to c99/gnu99
front end.
With gcc4.x, which defaults to gnu89 it still works.
Also works with the latest gcc -std=gnu89
But there is no hope that gcc maintaines will ever admit that it is
a bug.
It isn't a bug. The C standard allows this behavior.
On Thu, 26 Mar 2026 19:08:16 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
Michael S <already5chosen@yahoo.com> writes:
On Thu, 26 Mar 2026 22:36:59 +0000
Bart <bc@freeuk.com> wrote:
Take this program:
#include <stdlib.h>
inline int F(){return rand();}
int main(void) {
return F();
}
This compiles fine with gcc using -O1 -O2 -O3.
But compile without optimising, and it fails to link as it can't
find a function 'F'.
Is it supposed to behave like that? I assume no discrete function
F is being generated because of the 'inline' attribute, but you'd
think it would ignore that if inlining wasn't enabled.
[...]
Sounds like a bug introduced during transition to c99/gnu99
front end.
With gcc4.x, which defaults to gnu89 it still works.
Also works with the latest gcc -std=gnu89
But there is no hope that gcc maintaines will ever admit that it is
a bug.
It isn't a bug. The C standard allows this behavior.
So, by C standard, there is no situation in which 'inline' with no
addition qualifuers like 'static' or 'extern' can be used with
predictabe outcome?
On Thu, 26 Mar 2026 19:08:16 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
Michael S <already5chosen@yahoo.com> writes:
On Thu, 26 Mar 2026 22:36:59 +0000
Bart <bc@freeuk.com> wrote:
Take this program:
#include <stdlib.h>
inline int F(){return rand();}
int main(void) {
return F();
}
This compiles fine with gcc using -O1 -O2 -O3.
But compile without optimising, and it fails to link as it can't
find a function 'F'.
Is it supposed to behave like that? I assume no discrete function
F is being generated because of the 'inline' attribute, but you'd
think it would ignore that if inlining wasn't enabled.
[...]
Sounds like a bug introduced during transition to c99/gnu99
front end.
With gcc4.x, which defaults to gnu89 it still works.
Also works with the latest gcc -std=gnu89
But there is no hope that gcc maintaines will ever admit that it is
a bug.
It isn't a bug. The C standard allows this behavior.
So, by C standard, there is no situation in which 'inline' with no
addition qualifuers like 'static' or 'extern' can be used with
predictabe outcome?
Take this program:
˙ #include <stdlib.h>
˙ inline int F(){return rand();}
On 26/03/2026 22:36, Bart wrote:
Take this program:
˙˙ #include <stdlib.h>
˙˙ inline int F(){return rand();}
I just noticed that my subject line used 'include' instead of 'inline'.
Either no one else did, or they were too polite to mention it.
On 26/03/2026 23:12, Keith Thompson wrote:[...]
(Unless you're using a C23 compiler, I suggest "int F(void)" rather
than "int F()".)
Nobody bothers with that any more.
Most seem to assume that () already
means zero parameters anyway, judging by the incorrect usage I
constantly saw in open source code.
Using 'static' to force an actual function to be generated sounds unintuitive.
On Fri, 27 Mar 2026 10:55:55 +0000, Bart wrote:
Using 'static' to force an actual function to be generated sounds
unintuitive.
Lots of things about C are ?unintuitive?. Coming from Pascal, I found
its type-definition syntax completely backwards.
I dare not ask what the rules are; I'm sure they exist, but they still[...]
don't make sense. I can only find out which of these are valid or not
by trial and error. A HLL shouldn't be like that.
On 27/03/2026 21:27, Lawrence D?Oliveiro wrote:
On Fri, 27 Mar 2026 10:55:55 +0000, Bart wrote:
Using 'static' to force an actual function to be generated sounds
unintuitive.
Lots of things about C are ?unintuitive?. Coming from Pascal, I found
its type-definition syntax completely backwards.
If only it was simply backwards! Instead it's inside-out and spirular.
Take this program:
˙ #include <stdlib.h>
˙ inline int F(){return rand();}
˙ int main(void) {
˙˙˙˙˙ return F();
˙ }
This compiles fine with gcc using -O1 -O2 -O3.
But compile without optimising, and it fails to link as it can't find a function 'F'.
Is it supposed to behave like that?
On Thu 3/26/2026 3:36 PM, Bart wrote:
Take this program:
#include <stdlib.h>
inline int F(){return rand();}
int main(void) {
return F();
}
This compiles fine with gcc using -O1 -O2 -O3.
But compile without optimising, and it fails to link as it can't
find a function 'F'.
Is it supposed to behave like that?
Yes.
C is very different from C++ when it comes to inline functions with
external linkage. The language stoically sticks to "manual ODR"
philosophy and refuses to employ the "common section" approach used by
C++. C will not automatically emit an out-of-line definition for an external-linkage inline function. It is your responsibility to provide
one (and choose the site for one) by making an `extern inline`
declaration.
If the compiler decides not to inline the call, it will seek an
out-of-line "body" for your function. If there's no out-of-line
"body", you will get a linker error. Which is exactly what happened in
your case.
So, you have to choose a site for the out-of-line definition - one and
only one translation unit, in which you have to declare your function
as `extern inline`. This translation unit will host the out-of-line
"body" for the function. Since in your case you have only one
translation unit, you can do it as
inline int F(){return rand();}
extern inline int F(); /* <- emits an out-of-line definition */
So, by C standard, there is no situation in which 'inline' with no
addition qualifuers like 'static' or 'extern' can be used with
predictabe outcome?
On 27/03/2026 21:27, Lawrence D?Oliveiro wrote:ascal, I
On Fri, 27 Mar 2026 10:55:55 +0000, Bart wrote:
Using 'static' to force an actual function to be generated sounds
unintuitive.
Lots of things about C are ?unintuitive?. Coming from P
found its type-definition syntax completely backwards.
If only it was simply backwards! Instead it's inside-out and spirular.
But, yeah, some C aspect being unintuitive is nothing new:
extern int abc;
int abc = 123;
Or:
static int abc;
extern int abc;
Both are valid C, but this isn't:
extern int abc;
static int abc;
I dare not ask what the rules are; I'm sure they exist, but they
still don't make sense. I can only find out which of these are valid
or not by trial and error. A HLL shouldn't be like that.
On Fri, 27 Mar 2026 22:05:24 +0000
Bart <bc@freeuk.com> wrote:
On 27/03/2026 21:27, Lawrence D?Oliveiro wrote:
On Fri, 27 Mar 2026 10:55:55 +0000, Bart wrote:
Using 'static' to force an actual function to be generated sounds
unintuitive.
Lots of things about C are ?unintuitive?. Coming from Pascal, I
found its type-definition syntax completely backwards.
If only it was simply backwards! Instead it's inside-out and spirular.
But, yeah, some C aspect being unintuitive is nothing new:
extern int abc;
int abc = 123;
Or:
static int abc;
extern int abc;
Both are valid C, but this isn't:
extern int abc;
static int abc;
I dare not ask what the rules are; I'm sure they exist, but they
still don't make sense. I can only find out which of these are valid
or not by trial and error. A HLL shouldn't be like that.
Why do you care?
As C programmer,
all you have to know is to never use either 2nd or
3rd, because even if one of them can be legal according to Standard,
both of them certainly make no sense.
For your first example, it sometimes make sense, typically as result of #inclyde and/or marcro substitution. What exactly do you find
unintuitive about it?
On 26/03/2026 22:36, Bart wrote:The subject line is supposed to summarize the main message, so people
Take this program:
˙˙ #include <stdlib.h>
˙˙ inline int F(){return rand();}
I just noticed that my subject line used 'include' instead of 'inline'.
Either no one else did, or they were too polite to mention it.
On Fri 3/27/2026 6:47 AM, Michael S wrote:
So, by C standard, there is no situation in which 'inline' with no
addition qualifuers like 'static' or 'extern' can be used with
predictabe outcome?
The outcome is perfectly predictable, as long as you folow the ODR
rules of C language.
Michael S <already5chosen@yahoo.com> writes:
On Thu, 26 Mar 2026 19:08:16 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
Michael S <already5chosen@yahoo.com> writes:
On Thu, 26 Mar 2026 22:36:59 +0000
Bart <bc@freeuk.com> wrote:
Take this program:
#include <stdlib.h>
inline int F(){return rand();}
int main(void) {
return F();
}
This compiles fine with gcc using -O1 -O2 -O3.
But compile without optimising, and it fails to link as it can't
find a function 'F'.
Is it supposed to behave like that? I assume no discrete
function F is being generated because of the 'inline' attribute,
but you'd think it would ignore that if inlining wasn't enabled.
[...]
Sounds like a bug introduced during transition to c99/gnu99
front end.
With gcc4.x, which defaults to gnu89 it still works.
Also works with the latest gcc -std=gnu89
But there is no hope that gcc maintaines will ever admit that it
is a bug.
It isn't a bug. The C standard allows this behavior.
So, by C standard, there is no situation in which 'inline' with no
addition qualifuers like 'static' or 'extern' can be used with
predictabe outcome?
That depends on just how you mean the question. If we have this
source file
inline int
F(){
return 0;
}
int
main(void){
return F();
}
then we might get an undefined reference for F(). However if we
expand that source file just slightly, to
inline int
F(){
return 0;
}
int
main(void){
return F();
}
int F();
then the program has well-defined behavior. This result obtains
because the function F() now has a declaration that is (implicitly)
external, and without specifying 'inline', in addition to the
original inline definition.
On 28/03/2026 17:37, Michael S wrote:
On Fri, 27 Mar 2026 22:05:24 +0000
Bart <bc@freeuk.com> wrote:
On 27/03/2026 21:27, Lawrence D?Oliveiro wrote:
On Fri, 27 Mar 2026 10:55:55 +0000, Bart wrote:
Using 'static' to force an actual function to be generated sounds
unintuitive.
Lots of things about C are ?unintuitive?. Coming from Pascal, I
found its type-definition syntax completely backwards.
If only it was simply backwards! Instead it's inside-out and spirular.
But, yeah, some C aspect being unintuitive is nothing new:
˙˙˙ extern int abc;
˙˙˙ int abc = 123;
Or:
˙˙˙ static int abc;
˙˙˙ extern int abc;
Both are valid C, but this isn't:
˙˙˙ extern int abc;
˙˙˙ static int abc;
I dare not ask what the rules are; I'm sure they exist, but they
still don't make sense. I can only find out which of these are valid
or not by trial and error. A HLL shouldn't be like that.
Why do you care?
As C programmer,
I'm also an implementer.
all you have to know is to never use either 2nd or
3rd, because even if one of them can be legal according to Standard,
both of them certainly make no sense.
For your first example, it sometimes make sense, typically as result of
#inclyde and/or marcro substitution. What exactly do you find
unintuitive about it?
That you declare 'abc' as something to be imported, yet in the next line
it is initialised as though it was exported.
On Sat, 28 Mar 2026 10:25:33 -0700
Andrey Tarasevich <noone@noone.net> wrote:
On Fri 3/27/2026 6:47 AM, Michael S wrote:
So, by C standard, there is no situation in which 'inline' with no
addition qualifuers like 'static' or 'extern' can be used with
predictabe outcome?
The outcome is perfectly predictable, as long as you folow the ODR
rules of C language.
Sometimes it works. Sometimes it does not. The Standard permits both behaviors.
If it is not called 'unpredictable' then I don't know what is.
On 28/03/2026 19:33, Bart wrote:
On 28/03/2026 17:37, Michael S wrote:
On Fri, 27 Mar 2026 22:05:24 +0000
Bart <bc@freeuk.com> wrote:
On 27/03/2026 21:27, Lawrence D?Oliveiro wrote:
On Fri, 27 Mar 2026 10:55:55 +0000, Bart wrote:
Using 'static' to force an actual function to be generated sounds
unintuitive.
Lots of things about C are ?unintuitive?. Coming from Pascal, I
found its type-definition syntax completely backwards.
If only it was simply backwards! Instead it's inside-out and spirular. >>>>
But, yeah, some C aspect being unintuitive is nothing new:
˙˙˙ extern int abc;
˙˙˙ int abc = 123;
Or:
˙˙˙ static int abc;
˙˙˙ extern int abc;
Both are valid C, but this isn't:
˙˙˙ extern int abc;
˙˙˙ static int abc;
I dare not ask what the rules are; I'm sure they exist, but they
still don't make sense. I can only find out which of these are valid
or not by trial and error. A HLL shouldn't be like that.
Why do you care?
As C programmer,
I'm also an implementer.
When you write code, there are two sane choices :
˙˙˙˙extern int abc;˙˙˙˙˙˙˙ // In a header
˙˙˙˙int abc = 123;˙˙˙˙˙˙˙ // In one implementation file
or
˙˙˙˙static int abc = 123;˙˙˙ // In any implementation file
So as a C programmer, as Michael says, the oddities of mixing different declaration types does not matter.
As an implementer, you have two sane choices :
1.
Read the standards and implement them.˙ (The rules here are not remotely difficult to understand or follow.˙ You might find them unintuitive or
plain daft, and you won't want to remember them, but that is no hinder
to getting them right.)
2.
Throw a fatal error message from your compiler if you encounter such
mixups.˙ The compiler would be non-compliant, but almost no one is going
to write code mixing extern and static like this, at least not intentionally.
In your case, I'd recommend option 2.˙ Your compiler is niche, primarily
for compiling code from your own C code generators, or for your own
personal testing and interest.
˙ And presumably they never generate code
that mixes static and extern.
On Sat, 28 Mar 2026 10:25:33 -0700
Andrey Tarasevich <noone@noone.net> wrote:
On Fri 3/27/2026 6:47 AM, Michael S wrote:
So, by C standard, there is no situation in which 'inline' with no
addition qualifuers like 'static' or 'extern' can be used with
predictabe outcome?
The outcome is perfectly predictable, as long as you folow the ODR
rules of C language.
Sometimes it works. Sometimes it does not. The Standard permits both behaviors.
If it is not called 'unpredictable' then I don't know what is.
On Sat, 28 Mar 2026 10:25:33 -0700...
Andrey Tarasevich <noone@noone.net> wrote:
The outcome is perfectly predictable, as long as you folow the ODR
rules of C language.
Sometimes it works. Sometimes it does not. The Standard permits both behaviors.
If it is not called 'unpredictable' then I don't know what is.
On 2026-03-29 03:37, Michael S wrote:
On Sat, 28 Mar 2026 10:25:33 -0700...
Andrey Tarasevich <noone@noone.net> wrote:
The outcome is perfectly predictable, as long as you folow the ODR
rules of C language.
Sometimes it works. Sometimes it does not. The Standard permits both behaviors.
If it is not called 'unpredictable' then I don't know what is.
Using external inline gives the implementation a choice. in general,
the implementation will choose whichever is better in a given
situation, which is usually a good thing.
Sometimes it will call the inline definition, sometimes it will call
the external one. In most cases, you handle that by making sure the
inline definition is the same as the external one - there's some not
very difficult techniques using the preprocessor so that you only
have to write the code once, and the same definition is used for both.
If, for any reason, it's important to you that the behavior be
different when calling the external version than the inline one,
that's also easy to arrange.
What's the problem?
Bart <bc@freeuk.com> wrote:
That you declare 'abc' as something to be imported, yet in the next line
it is initialised as though it was exported.
Well, C is not able to directy express notion of export and import.
Also, C does not have modules/interfaces etc. Instead C object
may be global (which is specified by 'extern' or by lack of storage
class at file scope) or local. C rules allow you to emulate
modules using header files. Namely, in header file you put
'extern' declaration for exports of given module. Any user of
of the module must include the corresponding header file.
To ensure consistency also implementation of the module should
include the header file. So everywhere elso you have just
'extern' declaration, but in implementation there is 'extern'
declaration first followed by the definition. If such
sequence was disallowed, then it would be harder to check
consistency of implementation and corresponding header.
So in reasonable code 'extern' really means "part of interface"
without specifying if it is import or export.
Note that implementers of C decided to use system linker.
When C was born standard linkers did not have support for
modules and due to limits on name length emulating such
support was problematic.
So what C did was best thing
possible. We can now do better, but requirement of
compatibility means that old approach is still in use.
To put it differently: any language which decides to
break compatibility (and a lot did so) can do better than
C. But due to compatibility C is more popular than
competitors.
So, you have to choose a site for the out-of-line definition - one and
only one translation unit, in which you have to declare your function
as `extern inline`. This translation unit will host the out-of-line
"body" for the function. Since in your case you have only one
translation unit, you can do it as
inline int F(){return rand();}
extern inline int F(); /* <- emits an out-of-line definition */
Note that both 'extern' and 'inline' here can be dispensed with
on the declaration. Rather than 'extern inline int F();' it
suffices to say
int F();
to force the compiler to produce a real function body with
a linker-visible symbol. And doing that doesn't interfere
with the function being an inline function.
On Sun, 29 Mar 2026 13:56:49 -0400...
James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
Using external inline gives the implementation a choice. in general,
the implementation will choose whichever is better in a given
situation, which is usually a good thing.
Sometimes it will call the inline definition, sometimes it will call
the external one. In most cases, you handle that by making sure the
inline definition is the same as the external one - there's some not
very difficult techniques using the preprocessor so that you only
have to write the code once, and the same definition is used for both.
If, for any reason, it's important to you that the behavior be
different when calling the external version than the inline one,
that's also easy to arrange.
What's the problem?
If you don't see situation presented by Bart as a problem then we don't
have enough of common ground to continue the conversation.
On 2026-03-29 14:39, Michael S wrote:
On Sun, 29 Mar 2026 13:56:49 -0400...
James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
Using external inline gives the implementation a choice. in general,
the implementation will choose whichever is better in a given
situation, which is usually a good thing.
Sometimes it will call the inline definition, sometimes it will call
the external one. In most cases, you handle that by making sure the
inline definition is the same as the external one - there's some not
very difficult techniques using the preprocessor so that you only
have to write the code once, and the same definition is used for both.
If, for any reason, it's important to you that the behavior be
different when calling the external version than the inline one,
that's also easy to arrange.
What's the problem?
If you don't see situation presented by Bart as a problem then we don't
have enough of common ground to continue the conversation.
Bart's problem is that he pays too much attention to his preferences for
how a language should work, and not enough attention to what the
standard says about how the C language actually works. I can understand preferring different behavior, and switching to a different langauge
that provides that behavior, even if that means creating the language yourself. What I don't understand is why he continues to insist on using
a language whose design bothers him so much.
If he wants the behavior that C defines for static inline, use it. If he wants the behavior that C defines for inline functions with external
linkage, use that. But he should know the difference if he insists on
using inline functions in C.
Since you didn?t declare it ?static? (internal linkage), it has
external linkage. Looking at N3220, section 6.7.5, ?Function
specifiers?:
An inline definition does not provide an external definition for
the function and does not forbid an external definition in
another translation unit. Inline definitions provide an
alternative to external definitions, which a translator may use
to implement any call to the function in the same translation
unit. It is unspecified whether a call to the function uses the
inline definition or the external definition.
On Sat 3/28/2026 12:30 AM, Tim Rentsch wrote:
So, you have to choose a site for the out-of-line definition - one and
only one translation unit, in which you have to declare your function
as `extern inline`. This translation unit will host the out-of-line
"body" for the function. Since in your case you have only one
translation unit, you can do it as
inline int F(){return rand();}
extern inline int F(); /* <- emits an out-of-line definition */
Note that both 'extern' and 'inline' here can be dispensed with
on the declaration. Rather than 'extern inline int F();' it
suffices to say
int F();
to force the compiler to produce a real function body with
a linker-visible symbol. And doing that doesn't interfere
with the function being an inline function.
Yes, I see that any kind of `extern` declaration for this function
(including the implicit `extern`) causes the compiler to emit the
non-inline definition for the corresponding TU, i.e. any of
int F();
extern int F();
extern inline int F();
lead to the same effect.
Personally, if following this approach (i.e. add a re-declaration to
one .c file), I'd probably stick to `extern inline` version just to
express the intent better: to make it clear that this declaration was
placed there specifically for that purpose - to emit a non-inline body
for an inline function.
Otherwise, it would like like an ordinary
function declaration sitting in the middle of the file for no apparent reason. (Of course, one can also write a comment stating the purpose.)
P.S. As it has already been mentioned somewhere in the thread, there
an alternative, header-only approach: just use the preprocessor to
alter the header file definition in one chosen TU
#ifdef MAKE_BODY_
extern
#endif /* MAKE_BODY_ */
inline int F() { /* whatever */ }
On 27/03/2026 21:27, Lawrence D?Oliveiro wrote:
On Fri, 27 Mar 2026 10:55:55 +0000, Bart wrote:
Using 'static' to force an actual function to be generated sounds
unintuitive.
Lots of things about C are ?unintuitive?. Coming from Pascal, I found
its type-definition syntax completely backwards.
If only it was simply backwards! Instead it's inside-out and spirular.
But, yeah, some C aspect being unintuitive is nothing new:
extern int abc;
int abc = 123;
Or:
static int abc;
extern int abc;
Both are valid C, but this isn't:
extern int abc;
static int abc;
I dare not ask what the rules are; I'm sure they exist, but they
still don't make sense.
I can only find out which of these are valid or not
by trial and error.
On 29/03/2026 00:53, Waldek Hebisch wrote:
Bart <bc@freeuk.com> wrote:
That you declare 'abc' as something to be imported, yet in the next line >>> it is initialised as though it was exported.
Well, C is not able to directy express notion of export and import.
Also, C does not have modules/interfaces etc. Instead C object
may be global (which is specified by 'extern' or by lack of storage
class at file scope) or local. C rules allow you to emulate
modules using header files. Namely, in header file you put
'extern' declaration for exports of given module. Any user of
of the module must include the corresponding header file.
To ensure consistency also implementation of the module should
include the header file. So everywhere elso you have just
'extern' declaration, but in implementation there is 'extern'
declaration first followed by the definition. If such
sequence was disallowed, then it would be harder to check
consistency of implementation and corresponding header.
So in reasonable code 'extern' really means "part of interface"
without specifying if it is import or export.
But, the rules are lax, and implementations exploit that by all
behaving a little differently.
TBF, the diverse implementations probably came first, and the
standard had to accommodate existing practice.
next lineOn 29/03/2026 00:53, Waldek Hebisch wrote:
Bart <bc@freeuk.com> wrote:
That you declare 'abc' as something to be imported, yet in the
it is initialised as though it was exported.
Well, C is not able to directy express notion of export and import.
Also, C does not have modules/interfaces etc. Instead C object
may be global (which is specified by 'extern' or by lack of storage
class at file scope) or local. C rules allow you to emulate
modules using header files. Namely, in header file you put
'extern' declaration for exports of given module. Any user of
of the module must include the corresponding header file.
To ensure consistency also implementation of the module should
include the header file. So everywhere elso you have just
'extern' declaration, but in implementation there is 'extern'
declaration first followed by the definition. If such
sequence was disallowed, then it would be harder to check
consistency of implementation and corresponding header.
So in reasonable code 'extern' really means "part of interface"
without specifying if it is import or export.
But, the rules are lax, and implementations exploit that by all
behaving a little differently.
The rules are not lax; they are carefully and precisely defined.
To see that one needs to know what the rules are, which in turn
depends on reading and understanding the C standard.
(In my language, F must be defined in exactly one place. No other declarations are allowed or needed within the program itself. So such multiple instances don't arise.
On Fri, 27 Mar 2026 22:05:24 +0000
Bart <bc@freeuk.com> wrote:
But, yeah, some C aspect being unintuitive is nothing new:
extern int abc;
int abc = 123;
Or:
static int abc;
extern int abc;
Both are valid C, but this isn't:
extern int abc;
static int abc;
I dare not ask what the rules are; I'm sure they exist, but they
still don't make sense. I can only find out which of these are
valid or not by trial and error. A HLL shouldn't be like that.
Why do you care?
As C programmer, all you have to know is to never use either 2nd or
3rd, because even if one of them can be legal according to Standard,
both of them certainly make no sense.
For your first example, it sometimes make sense, typically as result
of #inclyde and/or marcro substitution. What exactly do you find
unintuitive about it?
The bottom 3 paragraphs define the pre-C-spec way of implementing
?inline?.
On Sun, 29 Mar 2026 13:56:49 -0400
James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
On 2026-03-29 03:37, Michael S wrote:
On Sat, 28 Mar 2026 10:25:33 -0700
Andrey Tarasevich <noone@noone.net> wrote:
...
The outcome is perfectly predictable, as long as you folow the ODR
rules of C language.
Sometimes it works. Sometimes it does not. The Standard permits both
behaviors.
If it is not called 'unpredictable' then I don't know what is.
Using external inline gives the implementation a choice. in general,
the implementation will choose whichever is better in a given
situation, which is usually a good thing.
Sometimes it will call the inline definition, sometimes it will call
the external one. In most cases, you handle that by making sure the
inline definition is the same as the external one - there's some not
very difficult techniques using the preprocessor so that you only
have to write the code once, and the same definition is used for both.
If, for any reason, it's important to you that the behavior be
different when calling the external version than the inline one,
that's also easy to arrange.
What's the problem?
If you don't see situation presented by Bart as a problem then we don't
have enough of common ground to continue the conversation.
My own conclusion is simple - my decades-long custom of never using
inline without static is even wiser than I imagined.
On Fri, 27 Mar 2026 09:03:23 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
Michael S <already5chosen@yahoo.com> writes:
On Thu, 26 Mar 2026 19:08:16 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
Michael S <already5chosen@yahoo.com> writes:
On Thu, 26 Mar 2026 22:36:59 +0000
Bart <bc@freeuk.com> wrote:
Take this program:
#include <stdlib.h>
inline int F(){return rand();}
int main(void) {
return F();
}
This compiles fine with gcc using -O1 -O2 -O3.
But compile without optimising, and it fails to link as it
can't find a function 'F'.
Is it supposed to behave like that? I assume no discrete
function F is being generated because of the 'inline'
attribute, but you'd think it would ignore that if
inlining wasn't enabled.
[...]
Sounds like a bug introduced during transition to c99/gnu99
front end.
With gcc4.x, which defaults to gnu89 it still works.
Also works with the latest gcc -std=gnu89
But there is no hope that gcc maintaines will ever admit that it
is a bug.
It isn't a bug. The C standard allows this behavior.
So, by C standard, there is no situation in which 'inline'
with no addition qualifuers like 'static' or 'extern' can be
used with predictabe outcome?
That depends on just how you mean the question. If we have this
source file
inline int
F(){
return 0;
}
int
main(void){
return F();
}
then we might get an undefined reference for F(). However if we
expand that source file just slightly, to
inline int
F(){
return 0;
}
int
main(void){
return F();
}
int F();
then the program has well-defined behavior. This result obtains
because the function F() now has a declaration that is (implicitly)
external, and without specifying 'inline', in addition to the
original inline definition.
It is indeed well-defined. But well-defined behavior does not
match the most probable intention of programmer, which is to
create a body of F() in case that it was not actually inlined and
to *not* create it otherwise.
For something like MSVC it does not matter, because by default it
does not link unused functions into executive. But something like
gcc by default links everything in.
On 30/03/2026 13:33, Tim Rentsch wrote:> Bart <bc@freeuk.com> writes:
On 29/03/2026 00:53, Waldek Hebisch wrote:
Bart <bc@freeuk.com> wrote:
That you declare 'abc' as something to be imported, yet in the next line >>>>> it is initialised as though it was exported.
Well, C is not able to directy express notion of export and import.
Also, C does not have modules/interfaces etc. Instead C object
may be global (which is specified by 'extern' or by lack of storage
class at file scope) or local. C rules allow you to emulate
modules using header files. Namely, in header file you put
'extern' declaration for exports of given module. Any user of
of the module must include the corresponding header file.
To ensure consistency also implementation of the module should
include the header file. So everywhere elso you have just
'extern' declaration, but in implementation there is 'extern'
declaration first followed by the definition. If such
sequence was disallowed, then it would be harder to check
consistency of implementation and corresponding header.
So in reasonable code 'extern' really means "part of interface"
without specifying if it is import or export.
But, the rules are lax, and implementations exploit that by all
behaving a little differently.
The rules are not lax; they are carefully and precisely defined.
To see that one needs to know what the rules are, which in turn
depends on reading and understanding the C standard.
The rules may well be precise, but they can also allow for laxity in
how code is written, and how strictly they are enforced by a compiler.
That is why exactly the same program can pass with 0 warnings or
errors, pass with some warnings, or fail with errors, depending on the options provided. So:
c:\cx>gcc mcc.c
c:\cx>gcc -Wall -Wextra -Wpedantic -O2 mcc.c 2>errors
The first invocation compiled fine. The second generated thousands of warnings, for the same language standard. With -Werror, it would also
fail.
Does the standard also say anything about linking? These two modules:
a.c: int abc; int main(){}
b.c: int abc;
both individually compile with no problems. They also link
successfully using TCC and DMC /C/ compilers.
But gcc (invoking its linker) fails saying there are multiple
definitions of 'abc'.
The point is that a.c + b.c can either form a valid program or not due
to laxness in the language.
Here's another example which I believe you brought up:
static void F();
void F() {}
That first 'static' is what determines F's linkage. However someone
perusing the source code will see 'void F(){...}` and assume it is
exported.
But you can also have:
static void F(int a);
static void F(int b);
static void F(int c);
extern void F(int d) {}
static void F(int e);
void F(int f);
static void F(int g);
This is what I call being lax, while still within the rules, whatever
they are.
(In my language, F must be defined in exactly one place. No other declarations are allowed or needed within the program itself. So such multiple instances don't arise.
It is effectively stricter than C but enforced through better
design. C's design could also be tightened up, but people don't seem
to like losing the 'flexibility' shown above.)
Michael S <already5chosen@yahoo.com> writes:
On Fri, 27 Mar 2026 09:03:23 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
Michael S <already5chosen@yahoo.com> writes:
On Thu, 26 Mar 2026 19:08:16 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
Michael S <already5chosen@yahoo.com> writes:
On Thu, 26 Mar 2026 22:36:59 +0000
Bart <bc@freeuk.com> wrote:
Take this program:
#include <stdlib.h>
inline int F(){return rand();}
int main(void) {
return F();
}
This compiles fine with gcc using -O1 -O2 -O3.
But compile without optimising, and it fails to link as it
can't find a function 'F'.
Is it supposed to behave like that? I assume no discrete
function F is being generated because of the 'inline'
attribute, but you'd think it would ignore that if
inlining wasn't enabled.
[...]
Sounds like a bug introduced during transition to c99/gnu99
front end.
With gcc4.x, which defaults to gnu89 it still works.
Also works with the latest gcc -std=gnu89
But there is no hope that gcc maintaines will ever admit that it
is a bug.
It isn't a bug. The C standard allows this behavior.
So, by C standard, there is no situation in which 'inline'
with no addition qualifuers like 'static' or 'extern' can be
used with predictabe outcome?
That depends on just how you mean the question. If we have this
source file
inline int
F(){
return 0;
}
int
main(void){
return F();
}
then we might get an undefined reference for F(). However if we
expand that source file just slightly, to
inline int
F(){
return 0;
}
int
main(void){
return F();
}
int F();
then the program has well-defined behavior. This result obtains
because the function F() now has a declaration that is (implicitly)
external, and without specifying 'inline', in addition to the
original inline definition.
It is indeed well-defined. But well-defined behavior does not
match the most probable intention of programmer, which is to
create a body of F() in case that it was not actually inlined and
to *not* create it otherwise.
I simply disagree. The behavior you describe is what people expect
for static inline functions. It doesn't make sense that they would
expect the same thing for non-static inline functions, if they had
taken some time to think about it.
For something like MSVC it does not matter, because by default it
does not link unused functions into executive. But something like
gcc by default links everything in.
That depends on what you mean by "by default". gcc does not
produce an actual function body for any optimization level
above -O0. Surely the most common practice these days is to
enable some optimization level above -O0.
On Mon, 30 Mar 2026 14:42:18 +0100
Bart <bc@freeuk.com> wrote:
(In my language, F must be defined in exactly one place. No other
declarations are allowed or needed within the program itself. So such
multiple instances don't arise.
Do you make distinction between declarations and definitions?
The rules may well be precise, but they can also allow for laxity in
how code is written, and how strictly they are enforced by a compiler.
That is why exactly the same program can pass with 0 warnings or
errors, pass with some warnings, or fail with errors, depending on the options provided. So:
c:\cx>gcc mcc.c
c:\cx>gcc -Wall -Wextra -Wpedantic -O2 mcc.c 2>errors
The first invocation compiled fine. The second generated thousands of warnings, for the same language standard. With -Werror, it would also
fail.
Bart <bc@freeuk.com> writes:
[...]
The rules may well be precise, but they can also allow for laxity in
how code is written, and how strictly they are enforced by a compiler.
That is why exactly the same program can pass with 0 warnings or
errors, pass with some warnings, or fail with errors, depending on the
options provided. So:
c:\cx>gcc mcc.c
c:\cx>gcc -Wall -Wextra -Wpedantic -O2 mcc.c 2>errors
The first invocation compiled fine. The second generated thousands of
warnings, for the same language standard. With -Werror, it would also
fail.
Not the same language. gcc with no "-std=..." option compiles the
GNU C dialect. The default is "-std=gnuNN", where NN is 17 or 23
for recent versions of gcc. That's reasonable if your code depends
on GNU extensions, but not if you want to make a point about the
C language as defined by the ISO standard(s).
If you care about the C language as defined by the ISO standard,
you should be using something like "-std=cNN -pedantic" (or "-pedantic-errors"), where NN is the relevant edition of the
standard.
With "-Wall -Wextra", if you get "thousands of warnings", it's
because you asked for them.
You're complaining about gcc being lax, not about the C language
being lax.
I think there is some laxity in ISO C's treatment of "inline",
some cases where one conforming implementation might successfully
handle a program and another might reject it. I agree that's not
ideal. But as a programmer, you can avoid it by understanding the
standard's rules for "inline" and writing portable code. (And as
an implementer, you can either make a choice within the range of
options allowed by the standard, or you can make a non-conforming implementation that does whatever you prefer.)
The rules may well be precise, but they can also allow for laxity in
how code is written, and how strictly they are enforced by a compiler.
That is why exactly the same program can pass with 0 warnings or
errors, pass with some warnings, or fail with errors, depending on the options provided. So:
c:\cx>gcc mcc.c
c:\cx>gcc -Wall -Wextra -Wpedantic -O2 mcc.c 2>errors
The first invocation compiled fine. The second generated thousands of warnings, for the same language standard. With -Werror, it would also
fail.
On 30/03/2026 19:54, Keith Thompson wrote:[...]
You're complaining about gcc being lax, not about the C language
being lax.
It's both. Example:
signed char* p;
unsigned char* q;
p = q;
This compiles OK with no options. But should it; what does the
language say about it?
Another example:
int F(){}
Here not even -Wextra touches it. I need -Wpedantic, which tells me a
lot about C!
On Mon 3/30/2026 12:13 AM, Lawrence D?Oliveiro wrote:
<https://gcc.gnu.org/onlinedocs/gcc-15.2.0/gcc/Inline.html>
The bottom 3 paragraphs define the pre-C-spec way of implementing
?inline?.
It is interesting to note that in many respects it is the opposite
of the modern standard approach. Plain `inline` cases the compiler
to emit an external definition, while `extern inline` definition is essentially a macro - no external definition is generated.
On Mon, 30 Mar 2026 08:19:52 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
Michael S <already5chosen@yahoo.com> writes:
On Fri, 27 Mar 2026 09:03:23 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
Michael S <already5chosen@yahoo.com> writes:
On Thu, 26 Mar 2026 19:08:16 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
Michael S <already5chosen@yahoo.com> writes:
On Thu, 26 Mar 2026 22:36:59 +0000
Bart <bc@freeuk.com> wrote:
Take this program:
#include <stdlib.h>
inline int F(){return rand();}
int main(void) {
return F();
}
This compiles fine with gcc using -O1 -O2 -O3.
But compile without optimising, and it fails to link as it
can't find a function 'F'.
Is it supposed to behave like that? I assume no discrete
function F is being generated because of the 'inline'
attribute, but you'd think it would ignore that if
inlining wasn't enabled.
[...]
Sounds like a bug introduced during transition to c99/gnu99
front end.
With gcc4.x, which defaults to gnu89 it still works.
Also works with the latest gcc -std=gnu89
But there is no hope that gcc maintaines will ever admit that it >>>>>>> is a bug.
It isn't a bug. The C standard allows this behavior.
So, by C standard, there is no situation in which 'inline'
with no addition qualifuers like 'static' or 'extern' can be
used with predictabe outcome?
That depends on just how you mean the question. If we have this
source file
inline int
F(){
return 0;
}
int
main(void){
return F();
}
then we might get an undefined reference for F(). However if we
expand that source file just slightly, to
inline int
F(){
return 0;
}
int
main(void){
return F();
}
int F();
then the program has well-defined behavior. This result obtains
because the function F() now has a declaration that is (implicitly)
external, and without specifying 'inline', in addition to the
original inline definition.
It is indeed well-defined. But well-defined behavior does not
match the most probable intention of programmer, which is to
create a body of F() in case that it was not actually inlined and
to *not* create it otherwise.
I simply disagree. The behavior you describe is what people expect
for static inline functions. It doesn't make sense that they would
expect the same thing for non-static inline functions, if they had
taken some time to think about it.
For something like MSVC it does not matter, because by default it
does not link unused functions into executive. But something like
gcc by default links everything in.
That depends on what you mean by "by default". gcc does not
produce an actual function body for any optimization level
above -O0. Surely the most common practice these days is to
enable some optimization level above -O0.
In the comment above I was talking specifically about that: https://godbolt.org/z/4TazP4sq1
Michael S <already5chosen@yahoo.com> writes:
On Mon, 30 Mar 2026 08:19:52 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
Michael S <already5chosen@yahoo.com> writes:
On Fri, 27 Mar 2026 09:03:23 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
Michael S <already5chosen@yahoo.com> writes:
On Thu, 26 Mar 2026 19:08:16 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
Michael S <already5chosen@yahoo.com> writes:
On Thu, 26 Mar 2026 22:36:59 +0000
Bart <bc@freeuk.com> wrote:
Take this program:
#include <stdlib.h>
inline int F(){return rand();}
int main(void) {
return F();
}
This compiles fine with gcc using -O1 -O2 -O3.
But compile without optimising, and it fails to link as it
can't find a function 'F'.
Is it supposed to behave like that? I assume no discrete
function F is being generated because of the 'inline'
attribute, but you'd think it would ignore that if
inlining wasn't enabled.
[...]
Sounds like a bug introduced during transition to c99/gnu99
front end.
With gcc4.x, which defaults to gnu89 it still works.
Also works with the latest gcc -std=gnu89
But there is no hope that gcc maintaines will ever admit that
it is a bug.
It isn't a bug. The C standard allows this behavior.
So, by C standard, there is no situation in which 'inline'
with no addition qualifuers like 'static' or 'extern' can be
used with predictabe outcome?
That depends on just how you mean the question. If we have this
source file
inline int
F(){
return 0;
}
int
main(void){
return F();
}
then we might get an undefined reference for F(). However if we
expand that source file just slightly, to
inline int
F(){
return 0;
}
int
main(void){
return F();
}
int F();
then the program has well-defined behavior. This result obtains
because the function F() now has a declaration that is
(implicitly) external, and without specifying 'inline', in
addition to the original inline definition.
It is indeed well-defined. But well-defined behavior does not
match the most probable intention of programmer, which is to
create a body of F() in case that it was not actually inlined and
to *not* create it otherwise.
I simply disagree. The behavior you describe is what people expect
for static inline functions. It doesn't make sense that they would
expect the same thing for non-static inline functions, if they had
taken some time to think about it.
For something like MSVC it does not matter, because by default it
does not link unused functions into executive. But something like
gcc by default links everything in.
That depends on what you mean by "by default". gcc does not
produce an actual function body for any optimization level
above -O0. Surely the most common practice these days is to
enable some optimization level above -O0.
In the comment above I was talking specifically about that: https://godbolt.org/z/4TazP4sq1
I don't know what you were expecting me to learn by looking at
that page. As far as I can tell it adds no information beyond
what has already been presented.
Also I'm surprised that you didn't say anything in reaction to the
first paragraph of my comments. No conclusion, just surprised.
Bart <bc@freeuk.com> writes:
On 30/03/2026 19:54, Keith Thompson wrote:[...]
You're complaining about gcc being lax, not about the C language
being lax.
It's both. Example:
signed char* p;
unsigned char* q;
p = q;
This compiles OK with no options. But should it; what does the
language say about it?
As I think you already know, that's a constraint violation,
requiring a diagnostic.
Another example:
int F(){}
Here not even -Wextra touches it. I need -Wpedantic, which tells me a
lot about C!
"gcc -Wpedantic" doesn't complain about that. There is no syntax
error and no constraint violation, so the language does not require
a diagnostic.
If you have a point, feel free to make it more clearly.
On 29/03/2026 10:24, David Brown wrote:
On 28/03/2026 19:33, Bart wrote:
On 28/03/2026 17:37, Michael S wrote:
On Fri, 27 Mar 2026 22:05:24 +0000
Bart <bc@freeuk.com> wrote:
On 27/03/2026 21:27, Lawrence D?Oliveiro wrote:
On Fri, 27 Mar 2026 10:55:55 +0000, Bart wrote:
Using 'static' to force an actual function to be generated sounds >>>>>>> unintuitive.
Lots of things about C are ?unintuitive?. Coming from Pascal, I
found its type-definition syntax completely backwards.
If only it was simply backwards! Instead it's inside-out and spirular. >>>>>
But, yeah, some C aspect being unintuitive is nothing new:
˙˙˙ extern int abc;
˙˙˙ int abc = 123;
Or:
˙˙˙ static int abc;
˙˙˙ extern int abc;
Both are valid C, but this isn't:
˙˙˙ extern int abc;
˙˙˙ static int abc;
I dare not ask what the rules are; I'm sure they exist, but they
still don't make sense. I can only find out which of these are valid >>>>> or not by trial and error. A HLL shouldn't be like that.
Why do you care?
As C programmer,
I'm also an implementer.
When you write code, there are two sane choices :
˙˙˙˙˙extern int abc;˙˙˙˙˙˙˙ // In a header
˙˙˙˙˙int abc = 123;˙˙˙˙˙˙˙ // In one implementation file
or
˙˙˙˙˙static int abc = 123;˙˙˙ // In any implementation file
So as a C programmer, as Michael says, the oddities of mixing
different declaration types does not matter.
As an implementer, you have two sane choices :
1.
Read the standards and implement them.˙ (The rules here are not
remotely difficult to understand or follow.˙ You might find them
unintuitive or plain daft, and you won't want to remember them, but
that is no hinder to getting them right.)
With my compiler, I need to set up a test then display the symbol table
to see whether any declared symbol is local, imported or exported, if
there are multiple, conflicting declarations.
The choices it makes seem to be adequate for most code that is
encountered (at least those have rarely been a problem). But I can't
tell you if it exactly follows the standard.
2.
Throw a fatal error message from your compiler if you encounter such
mixups.˙ The compiler would be non-compliant, but almost no one is
going to write code mixing extern and static like this, at least not
intentionally.
In your case, I'd recommend option 2.˙ Your compiler is niche,
primarily for compiling code from your own C code generators, or for
your own personal testing and interest.
That is actually is something it always does perfectly! Unfortunately
that is only for testing. The point of generating C is for when either someone else builds my code with their compiler, or I run gcc to make
use of its optimisations.
˙ And presumably they never generate code that mixes static and extern.
Yes, the code generated is very conservative and validated; I don't
expect compiler errors.
If I take this program test.m in my language (The backticks preserve case):
˙ export proc `F = end˙˙˙˙ # export from program
˙ global proc `G = end˙˙˙˙ # export from module but not program
˙˙˙˙˙˙˙˙ proc `H = end˙˙˙˙ # local
˙ importdll $dummy =
˙˙˙˙˙˙˙˙ proc `I˙˙˙˙˙˙˙˙˙˙ # import from external library, not module
˙ end
Then the generated C includes these lines:
˙ void test_F();
˙ static void test_G();
˙ static void test_H();
˙ extern void I();
˙ void test_F() {
˙˙˙˙˙ return;
˙ }
˙ static void test_G() {
˙˙˙˙˙ return;
˙ }
˙ static void test_H() {
˙˙˙˙˙ return;
˙ }
(This is for whole-program compilation, and there is a single C file generated. There are no include files, not even for the standard library.)
All locally defined functions have exactly one forward declaration in
the C. Most will be static.
On 31/03/2026 02:07, Keith Thompson wrote:
Bart <bc@freeuk.com> writes:
On 30/03/2026 19:54, Keith Thompson wrote:[...]
As I think you already know, that's a constraint violation,You're complaining about gcc being lax, not about the C language
being lax.
It's both. Example:
signed char* p;
unsigned char* q;
p = q;
This compiles OK with no options. But should it; what does the
language say about it?
requiring a diagnostic.
But apparently it doesn't require it to fail the compilation.
Another example:"gcc -Wpedantic" doesn't complain about that. There is no syntax
int F(){}
Here not even -Wextra touches it. I need -Wpedantic, which tells me a
lot about C!
error and no constraint violation, so the language does not require
a diagnostic.
If you have a point, feel free to make it more clearly.
OK, you suggested that it was a compiler being lax, not the language.
Here, allowing such code to be written IS being lax. And in the above example, the language allowing the compiler to ignore the error or to
only warn, is also being lax.
But I expect that you will now redefine 'lax', or 'laxity' in such a
way that the language cannot ever be accused of being it. It's all
under control!
So a language may require a diagnostic but leaves it up to the
compiler as to how severely it is reported, if at all.
Or the language specifies that running into the final '}' of a
non-void function is UB, thereby washing its hands of it.
However the end result is that people CAN write sloppy code, and that
in my book is the language being 'lax'.
(Both of these examples will fail compilation in my language:[snip]
Bart <bc@freeuk.com> writes:
On 31/03/2026 02:07, Keith Thompson wrote:
Bart <bc@freeuk.com> writes:
On 30/03/2026 19:54, Keith Thompson wrote:
[...]
You're complaining about gcc being lax, not about the C
language being lax.
It's both. Example:
signed char* p;
unsigned char* q;
p = q;
This compiles OK with no options. But should it; what does
the language say about it?
As I think you already know, that's a constraint violation,
requiring a diagnostic.
But apparently it doesn't require it to fail the compilation.
Apparently?
ISO C requires a diagnostic for any translation unit that contains
a violation of any syntax rule or constraint. It never requires
compilation to fail, with the sole exception of the #error
directive.
Did you really not know that?
Another example:
int F(){}
Here not even -Wextra touches it. I need -Wpedantic, which
tells me a lot about C!
"gcc -Wpedantic" doesn't complain about that. There is no
syntax error and no constraint violation, so the language does
not require a diagnostic.
If you have a point, feel free to make it more clearly.
OK, you suggested that it was a compiler being lax, not the
language.
My point was that your example did not demonstrate that the
language is lax. gcc by default does not generate all the
diagnostics that are required by the language. Observing that
fact says nothing about the language.
Here, allowing such code to be written IS being lax. And in the
above example, the language allowing the compiler to ignore the
error or to only warn, is also being lax.
I don't disagree. I would have preferred a requirement that any
translation unit that violates a syntax rule or constraing must be
rejected. [...]
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:[...]
I don't disagree. I would have preferred a requirement that any
translation unit that violates a syntax rule or constraing must be
rejected. [...]
Ridiculous. Such a rule would effectively preclude many useful
extensions, which would have the effect of rendering the provision
allowing extensions pointless.
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
Michael S <already5chosen@yahoo.com> writes:
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
Michael S <already5chosen@yahoo.com> writes:
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
inline int
F(){
return 0;
}
int
main(void){
return F();
}
int F();
In the comment above I was talking specifically about that:
https://godbolt.org/z/4TazP4sq1
I don't know what you were expecting me to learn by looking at
that page. As far as I can tell it adds no information beyond
what has already been presented.
The page shows that when, according to your suggestion, source
code is modified to make it well-defined then body of F() is
generated regardless of optimization level. As you likely
expected.
It was a point of suggested modification, was not it?
| Sysop: | Tetrazocine |
|---|---|
| Location: | Melbourne, VIC, Australia |
| Users: | 14 |
| Nodes: | 8 (0 / 8) |
| Uptime: | 94:04:07 |
| Calls: | 211 |
| Files: | 21,502 |
| Messages: | 82,381 |