June 12, 2020 Re: What does 'inline' mean? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On Friday, 12 June 2020 at 13:22:35 UTC, Andrei Alexandrescu wrote: > On 6/8/20 2:14 AM, Manu wrote: >> In C/C++, inline says that a function will be emit to the binary only when it is called, and the function is marked with internal linkage (it is not visible to the linker from the symbol table) > > By my recollection this is not the case for C++, at all. > > * "inline" does NOT change a function's linkage in C++. You may have inline functions with internal linkage (static inline) or (default) external linkage. This is important because e.g. defining a static variable in an extern inline function will have the same address in all calls to the function. I believe Manu tried to explain that `inline` in C++ really only affects how the linker must treat the symbol, and it is best to remember that it does nothing at all concerning "inlining" (putting function body inside another). `inline` is really a misnomer. The "linkage" effect of `inline` is required for C++ to work with #include files. There is some room for confusion because, depending on who is talking, the word "linkage" can have more nuances than just internal/external. I personally take the broad stance of "linkage" including anything the linker can do with the symbol. Without `inline`, you would get multiple definition linker errors for functions defined in a header file when linking together two translation units that both included that header file. Instead of normal function linkage that forbids multiple definition, `inline` changes linkage to merge multiple definitions into one. In addition, emission of the function must happen in any translation unit that references it (calling or address taken), and thus that translation unit must also define it (in contrast to just declaring it). I do not think `inline` forbids emission if the function is not referenced. Note that C++17 added `inline` for variables too, because without it you run into the same problem of multiple definitions in e.g. header-only libraries. Obviously that has nothing to do with "inlining" the callee's body; it is purely a linker directive to merge the symbols which is exactly the functionality it provides for functions. In D, this is actually not a problem: multiple definitions of symbols are merged already. You'll never see a multiple definition error with D (also not with extern(C) functions). The wikipedia article seems fairly complete on all the intricacies of `inline`: https://en.wikipedia.org/wiki/Inline_function And the cppreference text is also pretty clear https://en.cppreference.com/w/cpp/language/inline . -Johan |
June 12, 2020 Re: What does 'inline' mean? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On Friday, 12 June 2020 at 15:37:16 UTC, Andrei Alexandrescu wrote: > On 6/11/20 11:01 PM, Avrina wrote: >> On Friday, 12 June 2020 at 01:55:26 UTC, Manu wrote: >>> Can you please explain why you feel opposed to this, and why our existing >>> solution, which is 'weird' by all accounts, and satisfies ZERO of the goals >>> assigned to inline is preferable? >> >> You won't get a direct answer to this. You can't have a debate if he never presents an argument. Or rather in business, you don't have to deal with a problem if you don't acknowledge there is a problem, management 101. > > It seems to me the problem lies with the way the problem is formulated. You managed to figure it out :), at least better than Walter. When talking about inline, it seems even you and Walter seem to be focused on the "inline" part. Yes an empty template does what Manu wants, the problem is that templates have their own nasty side effects. int foo(int) { return 1; } bool foo(bool) { return true; } int bar()(int) { return 1; } bool bar()(bool) { return true; } pragma(msg, __traits(getOverloads, __traits(parent, foo), "foo")); // tuple of (foo, foo) pragma(msg, __traits(getOverloads, __traits(parent, bar), "bar")); // empty tuple () It's relevant enough to be a feature that has already existed for quite some time. All Manu wants is to have that same functionality without the template. Maybe a bit different of an implementation but it does almost the same thing. There were also a few other examples of problems provided that were meant with unhelpful "workarounds" such as this: https://forum.dlang.org/post/rbua3s$1s5m$1@digitalmars.com Yes, just don't ever use enums and/or waste your time trying to figure out what is causing the link error and try to figure out a way to just remove it. So helpful. |
June 12, 2020 Re: What does 'inline' mean? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Johan | On 6/12/20 2:08 PM, Johan wrote: > On Friday, 12 June 2020 at 13:22:35 UTC, Andrei Alexandrescu wrote: >> On 6/8/20 2:14 AM, Manu wrote: >>> In C/C++, inline says that a function will be emit to the binary only when it is called, and the function is marked with internal linkage (it is not visible to the linker from the symbol table) >> >> By my recollection this is not the case for C++, at all. >> >> * "inline" does NOT change a function's linkage in C++. You may have inline functions with internal linkage (static inline) or (default) external linkage. This is important because e.g. defining a static variable in an extern inline function will have the same address in all calls to the function. > > I believe Manu tried to explain that `inline` in C++ really only affects how the linker must treat the symbol, and it is best to remember that it does nothing at all concerning "inlining" (putting function body inside another). Thank you. That's not at all what he wrote. AT ALL. It's what I wrote. I'm emphasizing this because it has been a recurring problem: a legitimate problem with an explanation lost in translation. > `inline` is really a misnomer. > The "linkage" effect of `inline` is required for C++ to work with #include files. There is some room for confusion because, depending on who is talking, the word "linkage" can have more nuances than just internal/external. It helps to use terminology that is well defined and standardized. That's where simply using the meaning defined and consistently used in the C++ standard document can be of great help. > I personally take the broad stance of "linkage" including anything the linker can do with the symbol. Makes sense. It's really nice to just use http://eel.is/c++draft/basic.link, which ensures we all understand one another. > Without `inline`, you would get multiple definition linker errors for functions defined in a header file when linking together two translation units that both included that header file. Instead of normal function linkage that forbids multiple definition, `inline` changes linkage to merge multiple definitions into one. Not sure about that part - if linkage was static by means of using the "static" keyword, multiple definitions may not be merged. (I may be wrong, please correct me.) Consider: static inline int fun() { static int x; return ++x; } In C++, each translation unit containing a definition of fun() will have a distinct address for x. I don't see how the bodies of those functions can be merged. > In addition, emission of the function must happen in any translation unit that references it (calling or address taken), and thus that translation unit must also define it (in contrast to just declaring it). I do not think `inline` forbids emission if the function is not referenced. I also think that's the case. (Also not what was claimed.) |
June 12, 2020 Re: What does 'inline' mean? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Avrina | On 6/12/20 4:13 PM, Avrina wrote: > On Friday, 12 June 2020 at 15:37:16 UTC, Andrei Alexandrescu wrote: >> On 6/11/20 11:01 PM, Avrina wrote: >>> On Friday, 12 June 2020 at 01:55:26 UTC, Manu wrote: >>>> Can you please explain why you feel opposed to this, and why our existing >>>> solution, which is 'weird' by all accounts, and satisfies ZERO of the goals >>>> assigned to inline is preferable? >>> >>> You won't get a direct answer to this. You can't have a debate if he never presents an argument. Or rather in business, you don't have to deal with a problem if you don't acknowledge there is a problem, management 101. >> >> It seems to me the problem lies with the way the problem is formulated. > > You managed to figure it out :), at least better than Walter. When talking about inline, it seems even you and Walter seem to be focused on the "inline" part. > > Yes an empty template does what Manu wants, the problem is that templates have their own nasty side effects. > > int foo(int) { return 1; } > bool foo(bool) { return true; } > > int bar()(int) { return 1; } > bool bar()(bool) { return true; } > > pragma(msg, __traits(getOverloads, __traits(parent, foo), "foo")); // tuple of (foo, foo) > pragma(msg, __traits(getOverloads, __traits(parent, bar), "bar")); // empty tuple () > > > It's relevant enough to be a feature that has already existed for quite some time. All Manu wants is to have that same functionality without the template. Thanks, that's edifying. That makes sense - have pragma(inline, true) do what a template would, without actually using template instantiation. > Maybe a bit different of an implementation but it does almost the same thing. This is important. Again it's been a repeating pattern that a valid request comes with a barrage of red herring details and suggestions for implementation that are often incomplete, unnecessarily complex, difficult to implement, or just plain wrong. > There were also a few other examples of problems provided that were meant with unhelpful "workarounds" such as this: https://forum.dlang.org/post/rbua3s$1s5m$1@digitalmars.com Yes, just don't ever use enums and/or waste your time trying to figure out what is causing the link error and try to figure out a way to just remove it. So helpful. I confess I don't understand why the example doesn't work. It should. And the "doctor, it hurts when I do that - then don't do that" back-and-forth is not helping. |
June 13, 2020 Re: What does 'inline' mean? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Avrina Attachments:
| On Sat, Jun 13, 2020 at 6:15 AM Avrina via Digitalmars-d < digitalmars-d@puremagic.com> wrote:
> On Friday, 12 June 2020 at 15:37:16 UTC, Andrei Alexandrescu wrote:
> > On 6/11/20 11:01 PM, Avrina wrote:
> >> On Friday, 12 June 2020 at 01:55:26 UTC, Manu wrote:
> >>> Can you please explain why you feel opposed to this, and why
> >>> our existing
> >>> solution, which is 'weird' by all accounts, and satisfies
> >>> ZERO of the goals
> >>> assigned to inline is preferable?
> >>
> >> You won't get a direct answer to this. You can't have a debate if he never presents an argument. Or rather in business, you don't have to deal with a problem if you don't acknowledge there is a problem, management 101.
> >
> > It seems to me the problem lies with the way the problem is formulated.
>
> You managed to figure it out :), at least better than Walter. When talking about inline, it seems even you and Walter seem to be focused on the "inline" part.
>
> Yes an empty template does what Manu wants, the problem is that templates have their own nasty side effects.
>
> int foo(int) { return 1; }
> bool foo(bool) { return true; }
>
> int bar()(int) { return 1; }
> bool bar()(bool) { return true; }
>
> pragma(msg, __traits(getOverloads, __traits(parent, foo),
> "foo")); // tuple of (foo, foo)
> pragma(msg, __traits(getOverloads, __traits(parent, bar),
> "bar")); // empty tuple ()
>
Right, this is but one of an endless sea of edge cases extending from the
fact that templates are not functions, they are templates... they may
become functions when they are instantiated, but that requires
instantiation.
Basically the only way that a nullary template function is like a normal
function is a direct call: `foo()`, almost anything else you do has
sufficient semantic differences that break interactions with generic code.
Eg:
void foo() {}
void bar()() {}
// this works (this is the only thing that works):
foo();
bar();
// this doesn't:
auto x = &foo;
auto y = &bar;
// this doesn't:
static assert(is(typeof(foo) == function));
static assert(is(typeof(bar) == function));
// this doesn't:
pragma(msg, __traits(getOverloads, __traits(parent, foo), "foo")); //
(foo)
pragma(msg, __traits(getOverloads, __traits(parent, bar), "bar")); // ()
// etc...
Dummy templates are not a cool workaround. Sometimes functions are just
meant to be functions.
What the template hack does show us though, is that DMD is already
perfectly capable of implementing `inline`, it's probably a 1-line fix.
|
June 13, 2020 Re: What does 'inline' mean? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu Attachments:
| On Fri, Jun 12, 2020 at 11:25 PM Andrei Alexandrescu via Digitalmars-d < digitalmars-d@puremagic.com> wrote: > On 6/8/20 2:14 AM, Manu wrote: > > In C/C++, inline says that a function will be emit to the binary only when it is called, and the function is marked with internal linkage (it is not visible to the linker from the symbol table) > > By my recollection this is not the case for C++, at all. > > * "inline" does NOT change a function's linkage in C++. You may have inline functions with internal linkage (static inline) or (default) external linkage. This is important because e.g. defining a static variable in an extern inline function will have the same address in all calls to the function. It absolutely changes the linkage. I believe it uses what LLVM calls 'ChooseOne' in its code generator, I don't know about 'standard' linker terminology, if such a thing exists. It's clearly in the spec too: """ 1. There may be more than one definition <https://en.cppreference.com/w/cpp/language/definition#One_Definition_Rule> of an inline function or variable in the program as long as each definition *appears in a different translation unit* and (for non-static inline functions and variables) all definitions are identical. For example, an inline function or an inline variable may be defined in a header file that is #include'd in multiple source files. """ If not for that link flag, you receive a "multiply declared symbol" error. If the above is correct, a D language feature dedicated to inlining > should do the following: > > * Always emit the function body in the .di file if ever asked to generate it. > Your sentence is unclear, but I think we agree. I like how I describe this better: "The function must be emit to the _calling_ CU" * Never complain about duplicate symbols if an inline function has > duplicate definitions. And as I have been saying: "It must have the appropriate linker flag" (again, I think LLVM calls it "ChooseOne") Then compilers can decide on specific inlining strategies using the > language feature as a hint. > Correct. Except in the case I described as case #3, in which it would be useful to have SOME WAY, to 'force inline' and receive an error if it failed. I described this in case #3 like some sort of an AST inline; the inline is performed in the front-end as some sort of AST macro, then we can have complete confidence it happened, and error if not. This is a DIFFERENT THING than 'inline' generally, and I would describe it as "force" inline, and suggested `pragma(inline, force)`. D does ALL of the above already for templates, so this is not a > difficult feature to implement. In fact we use this technique (e.g. in > druntime) by spelling inline as "()". Consider: > > int func(int) { ... body ... } > > Let's make this inline: > > int func()(int) { ... body ... } > > Done. > Except for the sea of edge cases when you supply `func` to generic code that was expecting a function. This is a sloppy and unsatisfying work-around. In D there's one additional implication of body availability - the > function is eligible for CTFE. I think any adjustment to the extant inline pragma needs to make this a top-level consideration. > I think this will work naturally with no changes today. If a function definition is present in a di file, you can CTFE call it, but (unless we fix inline) you can not call it at runtime (because no code will be emit anywhere). |
June 12, 2020 Re: What does 'inline' mean? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On 6/12/2020 5:17 PM, Andrei Alexandrescu wrote:
> Not sure about that part - if linkage was static by means of using the "static" keyword, multiple definitions may not be merged. (I may be wrong, please correct me.) Consider:
>
> static inline int fun() {
> static int x;
> return ++x;
> }
>
> In C++, each translation unit containing a definition of fun() will have a distinct address for x. I don't see how the bodies of those functions can be merged.
They are not merged in D, for the simple reason that ModuleA.fun() and ModuleB.fun() will have different (mangled) names presented to the linker.
(Of course, if you mark them extern(C) or extern(C++) the mangling will be the same and so they will be merged.)
As I've mentioned previously, C++ has all kinds of contortions trying to make inline work in a universe with:
1. .h file inclusion
2. no modules
3. no COMDATs
D just defines these problems out of existence.
|
June 12, 2020 Re: What does 'inline' mean? | ||||
---|---|---|---|---|
| ||||
Posted in reply to kinke | On 6/11/2020 10:39 AM, kinke wrote:
> Internal linkage (C(++) static), AFAIK, means that you may end up with multiple identical functions in the final linked binary. Some linkers may be able to still fold them, such as the MS linker and lld with /OPT:ICF (identical COMDAT folding).
>
> Linkonce_odr linkage (C++ templates and `inline` functions) on the other hand means that you end up with either 0 or 1 function in the final binary, even when multiple object files define it; AFAIK, without having to rely on /OPT:REF or --gc-sections etc. At least for LLVM, linkonce_odr additionally means that the definition is discarded early if unused and might not make it to the object file at all.
>
> Weak_odr linkage (current D templates) is similar to linkonce_odr but not 'officially' discardable (if unreferenced) for the final binary (and thus always making it to the object file). This can be overridden by /OPT:REF and --gc-sections etc., the success of which also depends on symbol visibility and/or --export-dynamic etc.
gc-sections isn't about merging multiple definitions, it's about discarding unreferenced sections.
|
June 12, 2020 Re: What does 'inline' mean? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Manu | On 6/11/2020 6:51 PM, Manu wrote:
> I'm fairly sure C++ uses the link flag that LLVM calls "choose one".
> It's in the C++ spec that all inlines collapse to the same one. We should be using the same flag.
D totally relies on that behavior.
|
June 13, 2020 Re: What does 'inline' mean? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu Attachments:
| On Sat, Jun 13, 2020 at 10:20 AM Andrei Alexandrescu via Digitalmars-d < digitalmars-d@puremagic.com> wrote: > On 6/12/20 2:08 PM, Johan wrote: > > On Friday, 12 June 2020 at 13:22:35 UTC, Andrei Alexandrescu wrote: > >> On 6/8/20 2:14 AM, Manu wrote: > >>> In C/C++, inline says that a function will be emit to the binary only when it is called, and the function is marked with internal linkage (it is not visible to the linker from the symbol table) > >> > >> By my recollection this is not the case for C++, at all. > >> > >> * "inline" does NOT change a function's linkage in C++. You may have inline functions with internal linkage (static inline) or (default) external linkage. This is important because e.g. defining a static variable in an extern inline function will have the same address in all calls to the function. > > > > I believe Manu tried to explain that `inline` in C++ really only affects how the linker must treat the symbol, and it is best to remember that it does nothing at all concerning "inlining" (putting function body inside another). > > Thank you. That's not at all what he wrote. AT ALL. It's what I wrote. > > I'm emphasizing this because it has been a recurring problem: a legitimate problem with an explanation lost in translation. You're joking right? """ 1. *I only want the function to be present in the CALLING binary*. I do not want an inline function present in the local binary where it was defined (unless it was called internally). I do not want a linker to see the inline function symbols and be able to link to them externally. [*This is about linkage and controlling the binary or distribution environment*] """ There is absolutely nowhere in that text where I describe that a function should be emit inline to the calling function. I couldn't possibly be clearer about its placement into the calling CU. I genuinely don't have any idea how I could have been misunderstood in this thread. It seems most readers did not misunderstand me. > `inline` is really a misnomer. > > The "linkage" effect of `inline` is required for C++ to work with #include files. There is some room for confusion because, depending on who is talking, the word "linkage" can have more nuances than just internal/external. > > It helps to use terminology that is well defined and standardized. That's where simply using the meaning defined and consistently used in the C++ standard document can be of great help. > We *are* using standard terminology. `inline` means something very specific in C; it means deduplication of symbols, and that's what we're talking about, for years. > I personally take the broad stance of "linkage" > > including anything the linker can do with the symbol. > > Makes sense. It's really nice to just use http://eel.is/c++draft/basic.link, which ensures we all understand one another. > There's no text in there that describes the link flags with standard terminology. I have never heard standard terminology for them. Can you show a reference for standard linker terminology which we can refer to in the future? LLVM is the only reference I know: https://llvm.org/docs/LangRef.html#linkage-types `internal` or `linkonce` are possible choices here, `linkonce` would match C++. > Without `inline`, you would get multiple definition linker errors for > > functions defined in a header file when linking together two translation units that both included that header file. Instead of normal function linkage that forbids multiple definition, `inline` changes linkage to merge multiple definitions into one. > > Not sure about that part - if linkage was static by means of using the "static" keyword, multiple definitions may not be merged. (I may be wrong, please correct me.) Consider: > > static inline int fun() { > static int x; > return ++x; > } > > In C++, each translation unit containing a definition of fun() will have a distinct address for x. I don't see how the bodies of those functions can be merged. > `static inline` is strictly internal. They will not be merged. Each CU will have its own copy of the function, and it will not deduplicate. We should be able use `private` the same way in D. > In addition, emission of the function must happen in any translation > > unit that references it (calling or address taken), and thus that > > translation unit must also define it (in contrast to just declaring it). > > I do not think `inline` forbids emission if the function is not > referenced. > > I also think that's the case. (Also not what was claimed.) > This is literally point #1 in my OP... I honestly don't know how I could have possibly been clearer about this. I've been saying it over and over and over again for years. The only difference you could perceive is that I suggest it should not emit it if it's never referenced... to be clear; I was not commenting on what C++ does, I was commenting on what I'd like. It would be nice to not generate and emit the code and bloat the binary if there are no calls. It's not necessary, because any external references will have their own copy anyway. |
Copyright © 1999-2021 by the D Language Foundation