This is the mail archive of the
gcc-bugs@gcc.gnu.org
mailing list for the GCC project.
[Bug c/16622] [C99] extern inline is handled wrong in C99 mode
- From: "hozelda at yahoo dot com" <gcc-bugzilla at gcc dot gnu dot org>
- To: gcc-bugs at gcc dot gnu dot org
- Date: 7 Aug 2004 00:05:29 -0000
- Subject: [Bug c/16622] [C99] extern inline is handled wrong in C99 mode
- References: <20040718224725.16622.pinskia@gcc.gnu.org>
- Reply-to: gcc-bugzilla at gcc dot gnu dot org
------- Additional Comments From hozelda at yahoo dot com 2004-08-07 00:05 -------
Outline:
-1 -- Pre-Summary [this and the Summary constitute the Executive Summary]
0 -- Summary
1 -- What is an inline definition?
2 -- What does constraint #3 of section 6.7.4 mean?
3 -- Some other notes about inline.
4 -- Disclaimer
-1 -- Pre-Summary
If bug report #11377 comment #2 is (completely) correct, then that would
appear to explain away this problem completely. But just in case gcc "extern
inline" != c99 "inline" __exactly, I will continue... with what I think C99 says.
I can't test the state of gcc (modern versions) as I should. The information
below would be useful to someone that wanted a second opinion on some
item or other (even if gcc was perfect). It can also be useful to glance over the
examples quickly and maybe read the Summary completely (although there
are some items not found in the Summary).
Maybe later I or someone else can so some testing of some of the trickier
points.
0 -- Summary
Only 2 types of function definitions exist: inline definitions and external
definitions (6.7.4#6 and 6.9#5). This is a partition; any definition of a function
falls into exactly one of these 2 categories.
By 6.9 and 6.9.1, every translation unit has exactly one or zero definition for a
function. This means that the same translation unit cannot have both an inline
and an external def.
File 1:
inline void f() {} //inline definition, external linkage
File 2:
extern inline void f() {} //external definition, external linkage
File 3:
static inline void g() {} //inline definition, internal linkage
File 4:
static h(void);
static inline h() {} //external definition, internal linkage
static H() {} //ditto.
//all of these four files can coexist in a program. [..even if 'g' 'h' and 'H' were 'f']
File 5
//the following 3 function definitions would all violate 6.7.4#3
// inline void f1_bad() {static a;} //violates part a
static x1;
// inline void f2_bad() {extern x1;} //violates part b
// inline void f3_bad() {x1 = 0;} //violates part b
//but the following are OK
extern inline void f() {static a; extern x1; x1=0;} //not inline def
static inline void g() {static a; extern x1; x1=0;} //not external linkage
static h(void);
static inline h() {static a; extern x1; x1=0;} //neither i.d. nor e.l.
There are other detailed examples (of different inline concepts) in the rest of
the post that may be massaged into proper gcc tests if necessary.
1 -- What is an inline definition?
>From sections 6.9 and 6.7.4 of the standard, we can conclude that all function
definitions are either "inline definitions" or otherwise they are "external
definitions." They can be one or the other but not both. [The constraint from
6.7.4#3 applies only to inline definitions (and with external linkage).]
Assuming that an identifier has at least one function definition within the
current translation unit, then there can be only one such definition and...
it is an inline definition if and only if __all of the function declarations at file
scope in that translation unit for that function identifier obey two restrictions.
First, each declaration of that function must contain the 'inline' keyword.
Second, none of the declarations can include the 'extern' keyword. [Notice that
while extern is implicit (according to 6.2.2#5) when no storage class specifier
is present, that implicitness(?) does not violate the second condition.] If either
of the two restrictions is violated, the definition is an external definition.
It is possible for an inline definition to correspond to an identifier with internal
linkage just as it can correspond to an identifier with external linkage (all
function identifiers will have linkage).
Examples:
file 1:
static inline void f() {}
extern int g() {extern f(void); return 0;}
inline void f(void);
file 2:
inline void f() {}
extern int g() {extern f(void); return 0;}
inline void f(void);
file 3:
static inline void f() {}
extern int g() {extern f(void); return 0;}
void f(void);
file 4:
inline void f() {}
extern int g() {extern f(void); return 0;}
extern inline void f(void);
f has an inline definition in files 1 and 2; it has an external definition in files 3
and 4. In all of the files, the definition for f is found in the first line (an empty
body.. for simplicity).
In file 1, we note that the first line makes f have internal linkage. There are
exactly 2 appearances of f at file scope (as is true for files 2,3, and 4) and in
each case 'inline' is used and 'extern' isn't.
In file 2, line 1 causes f to have external linkage. As with file 1, the two
declarations of f at file scope contain 'inline' but not 'extern'.
File 3 has one declaration without inline, so we have an external declaration.
The linkage of f is internal.
File 4 has one declaration with extern. This makes f's definition be external
also, and the linkage is external in this case.
2 -- What does constraint #3 of section 6.7.4 mean?
[#3] An inline definition of a function with external
linkage shall not contain a definition of a modifiable
object with static storage duration, and shall not contain a
reference to an identifier with internal linkage.
OK, from the examples above, there is only one line (out of 9) that would be
relevant to constraint #3. This would be the first line of file 2. It is a definition; it
is an inline definition; and it is one of a function with external linkage. [In this
case, there is no violation (the function is empty).]
Consider a second example [(a nontrivial) inline definition of an identifier with
external linkage.. g]:
//BEGIN File:
int x1=0, x1a;
static int x2=0;
extern int x3;
static int x4;
static int x5;
void f1(void);
static void f2(void);
inline void g() { //inline definition; external linkage.
// some declarations (potential definitions; also watch out for linkage)
extern int x1; //linkage; external linkage; not a definition.
extern int x1a; //linkage; external linkage; not a definition.
extern int x3; //linkage; external linkage; not a definition.
int x10; //no linkage. definition, but auto storage.
int x5; //no linkage. definition, but auto storage.
const static int x12 = 12; //no linkage. definition of constant object w/ static
storage duration.
// static int x11; //illegal. no linkage, but definition of modifiable object with
static storage duration.
// extern int x4; //illegal. internal linkage (though not a definition).
// other uses (no definitions, but linkage is still an issue)
x1=5; //x1: external linkage.
f1(); //f1: external linkage.
x5=5; //x5: external linkage.
// x2=5; //illegal. x2: internal linkage.
// f2(); //illegal. f2: internal linkage.
}
//END File.
In this example, g has an inline definition. Within its body, we look at each
identifier and ask two questions which can be categorized as (a) is this an
object that is being defined? if so... and (b) does the identifier have internal
linkage?
I had some trouble with the wording on the linkage test portion (part b): "and
shall not contain a reference to an identifier with internal linkage."
[First, I'd note that the "shall" in each of the two parts of this constraint means
that if there is a failure of either part then gcc must produce a diagnostic.]
What troubled me was the word "reference." I searched through most of the
standard and there were a number of cases where this word appeared and
was used with its customary English language meaning as in "there was a
reference to the other bug report" or "forward references." In the other cases,
the meaning was the technical meaning as in the definition in 6.2.5#20 of
"referenced type," which relates to (C language) pointers. In yet other cases
(such as this one), the meaning was ambiguous. In the end, I felt that the
intent here was the English meaning. In fact, 6.7.4#8 uses the English
common meaning.
It makes more sense to see it as that. The idea is that an inline definition must
not have any semantic differences than the external definition found in some
other translation unit because for every call within a t-u (translation unit) that
has an inline definition, either the inline or external version can be used
(neither need be "inlined" yet either could be).
By disallowing the programmer to "use" or "access" or "reference" an identifier
with internal linkage, there is one less class of potential problems since the
external definition would never be able to access those values (except as
mentioned in the next paragraph).. values which are retained through function
calls (state).
What if a pointer reference (or ref to ref to ...) to such an identifier is passed to
the function... well, that would be ok because either function would get that
reference. In the other cases where there could be an effect, the identifier
would have to literally appear within the body and that is what I think #3
prohibits (its appearance).
*** [Using this understanding of "reference" ...] Within a function such as 'g'
(inline def/ exte link), gcc would need to verify the linkage of every identifier it
came across no matter how deeply nested (within a declaration or within a
statement). This is right, no?
3 -- Some other notes about inline.
**
With very few exceptions, the following statements are true:
-- there is exactly one external definition for each function (or object, but the
focus on functions is because inline definition only applies to functions) with
external linkage, within an entire program.
-- there can be as little as zero and as many as N-1 inline definitions for a
function identifier of external linkage for a program composed of N translation
units.
**
There is nothing in the std (that I could find) about inline being part of the type
of a function; thus, compatibility and compositeness do not factor in, I don't
think. This also means that gcc should accept an inline function declaration
even after calls to it were already made (earlier in source) based on
non-inlined declarations. Inline is a hint, in any case, and gcc can inline or fail
to inline with or without that hint (external definitions would have to be
created/exported but this would have been done since the earlier
declarations, by assumption, were not inline). In the other case, where inline
appears and then disappears, that should be accepted just as readily except
that a prior potential inline definition would then be known not to be one, so
gcc had better be able to build the function body for export use (ie, gcc cannot
know until the end of the translation unit, whether or not any given function
definition was an inline definition).
**
Related to the above, an "inline function" is defined in 6.7.4#5 but in terms of
"function," which is not defined. The Std defined function type, function call,
etc, but not function. My guess is that this is further evidence that type is not
affected by "inline" and this definition is a general usage, working definition,
whose precise meaning probably doesn't impact any part of the Standard
except trivially. [Am I right? Did I make sense?] ..so as stated before, it should
not matter when inline appears in a declaration, even if earlier declarations
differed. Inline is a property of the function which, unlike type, is independent
of the precise appearance of the declaration in order to determine applicable
rules at that point (i.e., you can't have incompleteness or undefinedness
because of inline only).
[Tests for potential warnings/errors not provide]
**
6.9#5 footnote 136 (which is not normative -- Foreword#6 --> Part 3 ISO/IEC
Directives (?)) may be in conflict with 6.7.4#6 as follows... 6.9#5 allows for
function identifiers with external linkage to appear 0 or 1 time w/in the
program. 6.7.4#6 says that external linkage function identifiers declared with
inline must have definition (inline def or external def, regardless) found within
the translation unit. These are consistent; what is not consistent is that
footnote 136 draws the conclusion that all external linkage identifiers (eg,
external linkage inline function, such as either of the 'f' functions in File 1 or
File 2 of the Summary) that do not appear within any expressions need not
have any definitions (..despite having been declared). This seems to be
contrary to 6.7.4#6... if but in a very, very technical way, that doesn't affect
99.99999% of programs.
**
If the following aren't part of gcc tests (?), they can be added
>> inline int main () {} //should fail (and does): because of 6.7.4#4
>> inline int main (void); //should fail: see previous
>> inline int main (void); //need not fail: for any particular freestanding
environment. i.e., the previous 2 assumed a hosted environment.
>> inline inline void foo() {} //should pass: despite inline repetition.
4 -- Disclaimer
I did not really test the above examples with gcc 3.2.2 (which is what I have). I
would prefer to test once I have a newer gcc, especially since I expect any
number of the tests to fail (I am also not up to speed on interpreting
object/assembly output).
--
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=16622