Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Re: Functions and the linker

56 views
Skip to first unread message

Alain Ketterlin

unread,
Mar 16, 2016, 4:38:59 AM3/16/16
to
r...@zedat.fu-berlin.de (Stefan Ram) writes:

> #include <iostream> /* ::std::cout */
> #include <ostream> /* << */
> #include <string> /* ::std::string */
>
> int main() { ::std::string const s(); ::std::cout << '>' << s << "<\n"; }
>
> The above program prints »1« here.
>
> Does the above program have undefined behavior because the
> function »s« is just being declared but not defined anywhere?

Your compiler can help you. Mine says:

warning: the address of ‘const string s()’ will always evaluate
as ‘true’

-- Alain.

Juha Nieminen

unread,
Mar 16, 2016, 5:13:54 AM3/16/16
to
Stefan Ram <r...@zedat.fu-berlin.de> wrote:
> #include <iostream> /* ::std::cout */
> #include <ostream> /* << */

While you are at it, why don't you also include <ios> and <streambuf>?
You know, just in case. You never know.

--- news://freenews.netfront.net/ - complaints: ne...@netfront.net ---

Richard

unread,
Mar 16, 2016, 12:19:50 PM3/16/16
to
[Please do not mail me a copy of your followup]

r...@zedat.fu-berlin.de (Stefan Ram) spake the secret code
<functions-20...@ram.dialup.fu-berlin.de> thusly:

>#include <iostream> /* ::std::cout */
>#include <ostream> /* << */
>#include <string> /* ::std::string */
>
>int main() { ::std::string const s(); ::std::cout << '>' << s << "<\n"; }
>
> The above program prints »1« here.

How does it even link? You have referenced something that is
undefined, you should get undefined symbol for 's'.

This is exactly what I expect to get and do indeed get from MSVC.

Who knows WTF gcc is doing, but it successfully links and prints some
junk.

IMO, this is a bug in gcc. You can't print the address of something
that isn't defined.
--
"The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline>
The Computer Graphics Museum <http://computergraphicsmuseum.org>
The Terminals Wiki <http://terminals.classiccmp.org>
Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com>

red floyd

unread,
Mar 16, 2016, 2:48:34 PM3/16/16
to
On 3/16/2016 2:13 AM, Juha Nieminen wrote:
> Stefan Ram <r...@zedat.fu-berlin.de> wrote:
>> #include <iostream> /* ::std::cout */
>> #include <ostream> /* << */
>
> While you are at it, why don't you also include <ios> and <streambuf>?
> You know, just in case. You never know.

I believe that was *technically* required in C++03. Practically,
iostream always included ostream.

I think C++11 fixed this.


Paavo Helde

unread,
Mar 16, 2016, 3:20:38 PM3/16/16
to
On 16.03.2016 4:41, Stefan Ram wrote:
> #include <iostream> /* ::std::cout */
> #include <ostream> /* << */
> #include <string> /* ::std::string */
>
> int main() { ::std::string const s(); ::std::cout << '>' << s << "<\n"; }
>
> The above program prints »1« here.
>
> Does the above program have undefined behavior because the
> function »s« is just being declared but not defined anywhere?
>
From the standard: "Every program shall contain exactly one definition
of every non-inline function or variable that is odr-used in that
program; no diagnostic required."

I suspect taking the address of a function qualifies as odr-use, so the
above program indeed contains UB.


Richard

unread,
Mar 16, 2016, 6:19:37 PM3/16/16
to
[Please do not mail me a copy of your followup]

Paavo Helde <myfir...@osa.pri.ee> spake the secret code
<l9adnRT-QtNoLHTL...@giganews.com> thusly:

> From the standard: "Every program shall contain exactly one definition
>of every non-inline function or variable that is odr-used in that
>program; no diagnostic required."
>
>I suspect taking the address of a function qualifies as odr-use, so the
>above program indeed contains UB.

[3.2 basic.def.odr]

3. "[...] A function whose name appears as a potentially-evaluated
expression is odr-used if it is the unique lookup result or the selected
member of a set of overloaded functions (3.4, 13.3, 13.4), unless it is
a pure virtual function and its name is not explicitly qualified."

Seems pretty straight-forward to me that this means that using the
name of a function as a void* argument to operator<< is an odr-use of
the name.

Alain Ketterlin

unread,
Mar 17, 2016, 5:49:53 AM3/17/16
to
legaliz...@mail.xmission.com (Richard) writes:

> [Please do not mail me a copy of your followup]
>
> Paavo Helde <myfir...@osa.pri.ee> spake the secret code
> <l9adnRT-QtNoLHTL...@giganews.com> thusly:
>
>> From the standard: "Every program shall contain exactly one definition
>>of every non-inline function or variable that is odr-used in that
>>program; no diagnostic required."
>>
>>I suspect taking the address of a function qualifies as odr-use, so the
>>above program indeed contains UB.
>
> [3.2 basic.def.odr]
>
> 3. "[...] A function whose name appears as a potentially-evaluated
> expression is odr-used if it is the unique lookup result or the selected
> member of a set of overloaded functions (3.4, 13.3, 13.4), unless it is
> a pure virtual function and its name is not explicitly qualified."

The question now is: is the name "potentially evaluated" in the example?

> Seems pretty straight-forward to me that this means that using the
> name of a function as a void* argument to operator<< is an odr-use of
> the name.

void * is irrelevant here, there is no implicit conversion between a
function-pointer to void *. The only applicable conversion is to bool,
which is what happens here.

Since the conversion to bool is "implicit" (4.12), it is not clear to me
whether the name is "potentially-evaluated". (5.20) says that "an
lvalue-to-rvalue conversion" is a constant expression, therefore, not
potentially-evaluated (3.2). If this applies here, then the function
name is not odr-used, and gcc is correct.

But beyond that, I have to say that I'm lost in the standard's
vocabulary. So I disagree with you that the conclusion is
"straight-forward".

-- Alain.

Paavo Helde

unread,
Mar 17, 2016, 8:26:55 AM3/17/16
to
gcc is correct anyway because "no diagnostic required" and UB includes a
program working as expected.

The question is if the program has UB and if not, then MSVC is not
correct when rejecting the program.

>
> But beyond that, I have to say that I'm lost in the standard's
> vocabulary. So I disagree with you that the conclusion is
> "straight-forward".

Agreed.


Richard

unread,
Mar 17, 2016, 12:44:24 PM3/17/16
to
[Please do not mail me a copy of your followup]

Alain Ketterlin <al...@universite-de-strasbourg.fr.invalid> spake the secret code
<87egb9j...@universite-de-strasbourg.fr.invalid> thusly:

>legaliz...@mail.xmission.com (Richard) writes:
>
>> [Please do not mail me a copy of your followup]
>>
>> Paavo Helde <myfir...@osa.pri.ee> spake the secret code
>> <l9adnRT-QtNoLHTL...@giganews.com> thusly:
>>
>>> From the standard: "Every program shall contain exactly one definition
>>>of every non-inline function or variable that is odr-used in that
>>>program; no diagnostic required."
>>>
>>>I suspect taking the address of a function qualifies as odr-use, so the
>>>above program indeed contains UB.
>>
>> [3.2 basic.def.odr]
>>
>> 3. "[...] A function whose name appears as a potentially-evaluated
>> expression is odr-used if it is the unique lookup result or the selected
>> member of a set of overloaded functions (3.4, 13.3, 13.4), unless it is
>> a pure virtual function and its name is not explicitly qualified."
>
>The question now is: is the name "potentially evaluated" in the example?

It is not only potentially evaluated it is actually evaluated.

2. "An expression is /potentially evaluated/ unless it is an unevaluated
operand (Clause 5) or a subexpression thereof."

This is an evaluated operand to operator<<, so it is potentially
evaluated.

>> Seems pretty straight-forward to me that this means that using the
>> name of a function as a void* argument to operator<< is an odr-use of
>> the name.
>
>void * is irrelevant here, there is no implicit conversion between a
>function-pointer to void *. The only applicable conversion is to bool,
>which is what happens here.

OK fine, whatever, I picked the wrong overload of operator<< for a
stream with a pointer to function. It doesn't change anything here.

>Since the conversion to bool is "implicit" (4.12), it is not clear to me
>whether the name is "potentially-evaluated". (5.20) says that "an
>lvalue-to-rvalue conversion" is a constant expression, therefore, not
>potentially-evaluated (3.2). If this applies here, then the function
>name is not odr-used, and gcc is correct.

I'm looking at C++14 draft standard, which has no section 5.20.

Richard

unread,
Mar 17, 2016, 12:45:31 PM3/17/16
to
[Please do not mail me a copy of your followup]

Paavo Helde <myfir...@osa.pri.ee> spake the secret code
<VtKdnXSKJZPiP3fL...@giganews.com> thusly:

>gcc is correct anyway because "no diagnostic required" and UB includes a
>program working as expected.

I don't see any undefined behavior here. What I see is an odr-use of
a name that isn't defined.

Please show me how I got it wrong and convince me :-).

I will learn more that way.

red floyd

unread,
Mar 17, 2016, 12:48:58 PM3/17/16
to
On 3/16/2016 3:44 PM, Stefan Ram wrote:
> Paavo Helde <myfir...@osa.pri.ee> writes:
>> From the standard: "Every program shall contain exactly one definition
>> of every non-inline function or variable that is odr-used in that
>> program; no diagnostic required."
>
>> I suspect taking the address of a function qualifies as odr-use, so the
>> above program indeed contains UB.
>
> Thank you! I now have modified this teaching example of mine so as to
> include the definition:
>
> #include <iostream> /* ::std::cout */
> #include <ostream> /* << */
> #include <string> /* ::std::string */
>
> using namespace ::std::literals;
>
> ::std::string s() {} /* this line was added 2016-03-16 */
>
> int main() { ::std::string const s(); ::std::cout << '>' << s << "<\n"s; }
>

Still shouldn't compile. s() does not return a value.

Alain Ketterlin

unread,
Mar 17, 2016, 1:06:11 PM3/17/16
to
legaliz...@mail.xmission.com (Richard) writes:

>>>> From the standard: "Every program shall contain exactly one definition
>>>>of every non-inline function or variable that is odr-used in that
>>>>program; no diagnostic required."
>>>>
>>>>I suspect taking the address of a function qualifies as odr-use, so the
>>>>above program indeed contains UB.
>>>
>>> [3.2 basic.def.odr]
>>>
>>> 3. "[...] A function whose name appears as a potentially-evaluated
>>> expression is odr-used if it is the unique lookup result or the selected
>>> member of a set of overloaded functions (3.4, 13.3, 13.4), unless it is
>>> a pure virtual function and its name is not explicitly qualified."
>>
>>The question now is: is the name "potentially evaluated" in the example?
>
> It is not only potentially evaluated it is actually evaluated.
>
> 2. "An expression is /potentially evaluated/ unless it is an unevaluated
> operand (Clause 5) or a subexpression thereof."
>
> This is an evaluated operand to operator<<, so it is potentially
> evaluated.

No, it is an operand to the implicit conversion to bool, which doesn't
require evaluation, as I understand it.

>>Since the conversion to bool is "implicit" (4.12), it is not clear to me
>>whether the name is "potentially-evaluated". (5.20) says that "an
>>lvalue-to-rvalue conversion" is a constant expression, therefore, not
>>potentially-evaluated (3.2). If this applies here, then the function
>>name is not odr-used, and gcc is correct.
>
> I'm looking at C++14 draft standard, which has no section 5.20.

I'm looking at "Working Draft, Standard for Programming Language C ++",
Document Number: N4296, Date: 2014-11-19, available from
https://isocpp.org/std/the-standard

Which has section 5.20 ("Constant Expressions"), on page 132.

-- Alain.

Paavo Helde

unread,
Mar 17, 2016, 2:16:49 PM3/17/16
to
On 17.03.2016 11:49, Alain Ketterlin wrote:
If this reasoning is correct, then I feel also the following should compile:

#include <iostream>
#include <string>

std::string const s();

constexpr std::string const (*s2)() = s;

int main() {
std::cout << '>' << s2 << "<\n";
}

Alas, this fails with gcc:

>g++ -std=c++11 main.cpp
/tmp/ccUI16zy.o:main.cpp:(.rdata+0x8): undefined reference to `s()'
collect2: error: ld returned 1 exit status






Paavo Helde

unread,
Mar 17, 2016, 2:30:25 PM3/17/16
to
On 17.03.2016 18:45, Richard wrote:
> [Please do not mail me a copy of your followup]
>
> Paavo Helde <myfir...@osa.pri.ee> spake the secret code
> <VtKdnXSKJZPiP3fL...@giganews.com> thusly:
>
>> gcc is correct anyway because "no diagnostic required" and UB includes a
>> program working as expected.
>
> I don't see any undefined behavior here. What I see is an odr-use of
> a name that isn't defined.

Sorry, I am not sure I am understanding what you wanted to say. An
odr-use of a name that is not defined is exactly what constitutes the
undefined behavior (because a "shall" statement in the standard is not
fulfilled).

Anything which is not defined by the standard is undefined. The standard
only defines programs which contain exactly one definition of a
non-inline odr-used function. The programs which contain 2 or 0
definitions of such a function are not covered by the standard, i.e.
their behavior is undefined.

Cheers
Paavo

Victor Bazarov

unread,
Mar 17, 2016, 2:37:07 PM3/17/16
to
Why? It's not a compilation failure. It can compile fine (and even run
fine) if, e.g., the value-returning function without a 'return'
statement is never actually called.

V
--
I do not respond to top-posted replies, please don't ask

Alain Ketterlin

unread,
Mar 17, 2016, 4:18:44 PM3/17/16
to
Paavo Helde <myfir...@osa.pri.ee> writes:

[...]
>> void * is irrelevant here, there is no implicit conversion between a
>> function-pointer to void *. The only applicable conversion is to bool,
>> which is what happens here.
>>
>> Since the conversion to bool is "implicit" (4.12), it is not clear to me
>> whether the name is "potentially-evaluated". (5.20) says that "an
>> lvalue-to-rvalue conversion" is a constant expression, therefore, not
>> potentially-evaluated (3.2).
>
> If this reasoning is correct, then I feel also the following should compile:
>
> #include <iostream>
> #include <string>
>
> std::string const s();
>
> constexpr std::string const (*s2)() = s;
>
> int main() {
> std::cout << '>' << s2 << "<\n";
> }
>
> Alas, this fails with gcc:
>
>>g++ -std=c++11 main.cpp
> /tmp/ccUI16zy.o:main.cpp:(.rdata+0x8): undefined reference to `s()'
> collect2: error: ld returned 1 exit status

Well, this is expected: the value of s is now used in a conversion to a
function pointer. The same would happen if you cast s to void* to call
operator<<(void*) on cout.

But if you replace your s2 with:

bool s2 = s;

then it compiles & links. The strange behavior is due to the conversion
to bool.

-- Alain.

Richard

unread,
Mar 17, 2016, 4:42:36 PM3/17/16
to
[Please do not mail me a copy of your followup]

Paavo Helde <myfir...@osa.pri.ee> spake the secret code
<2JadnQ_YK8UqanfL...@giganews.com> thusly:

>On 17.03.2016 18:45, Richard wrote:
>> I don't see any undefined behavior here. What I see is an odr-use of
>> a name that isn't defined.
>
>Sorry, I am not sure I am understanding what you wanted to say. An
>odr-use of a name that is not defined is exactly what constitutes the
>undefined behavior [...]

I'm not sure how this constitutes undefined behavior, because the ODR
rule says essentially that everything you use has to be defined.

If you're just concluding "its UB because it doesn't say anything
about it", then we have to go through the entire standard to draw that
conclusion.

>Anything which is not defined by the standard is undefined. The standard
>only defines programs which contain exactly one definition of a
>non-inline odr-used function. The programs which contain 2 or 0
>definitions of such a function are not covered by the standard, i.e.
>their behavior is undefined.

This just seems like an awfully broad definition of UB with respect to
the ODR rule. Or perhaps it is just silly to do anything other than
give a link error for symbols that are multiply defined or undefined
when the ODR is broken. Hell, gcc ran nethack at one point, so while
the compiler is technically free to do whatever it wants during
undefined behavior, letting such undefined symbols slip through
without an error feels highly unuseful.

Richard

unread,
Mar 17, 2016, 4:43:53 PM3/17/16
to
[Please do not mail me a copy of your followup]

Alain Ketterlin <al...@universite-de-strasbourg.fr.invalid> spake the secret code
<87a8lxj...@universite-de-strasbourg.fr.invalid> thusly:

>legaliz...@mail.xmission.com (Richard) writes:
>> I'm looking at C++14 draft standard, which has no section 5.20.
>
>I'm looking at "Working Draft, Standard for Programming Language C ++",
>Document Number: N4296, Date: 2014-11-19, available from
>https://isocpp.org/std/the-standard
>
>Which has section 5.20 ("Constant Expressions"), on page 132.

I was using N3797, so looks like I need to get an updated draft.
Thanks.

Paavo Helde

unread,
Mar 17, 2016, 4:48:10 PM3/17/16
to
So, conversion to a bool is "an lvalue-to-rvalue conversion", but
conversion to a function pointer is not? Or is it that the first is a
"constant expression", but the second is not?

I'm more confused than before. Section 5.20 contains word 'bool' only in
an unrelated example code piece. And section 4.12 (Boolean conversions)
talks only about pointer to bool conversions, so it seems in order to be
converted to bool the function needs to be converted to a pointer first
anyway. How comes a program that contains an extra conversion
(pointer-to-bool) does not contain UB while the program without this
additional conversion has UB?

Cheers
Paavo



Paavo Helde

unread,
Mar 17, 2016, 5:10:17 PM3/17/16
to
On 17.03.2016 22:42, Richard wrote:
> [Please do not mail me a copy of your followup]
>
> Paavo Helde <myfir...@osa.pri.ee> spake the secret code
> <2JadnQ_YK8UqanfL...@giganews.com> thusly:
>
>> On 17.03.2016 18:45, Richard wrote:
>>> I don't see any undefined behavior here. What I see is an odr-use of
>>> a name that isn't defined.
>>
>> Sorry, I am not sure I am understanding what you wanted to say. An
>> odr-use of a name that is not defined is exactly what constitutes the
>> undefined behavior [...]
>
> I'm not sure how this constitutes undefined behavior, because the ODR
> rule says essentially that everything you use has to be defined.
>
> If you're just concluding "its UB because it doesn't say anything
> about it", then we have to go through the entire standard to draw that
> conclusion.
>
>> Anything which is not defined by the standard is undefined. The standard
>> only defines programs which contain exactly one definition of a
>> non-inline odr-used function. The programs which contain 2 or 0
>> definitions of such a function are not covered by the standard, i.e.
>> their behavior is undefined.
>
> This just seems like an awfully broad definition of UB with respect to
> the ODR rule.

This has nothing to do with the ODR rule, it's a general property of the
standard (and many other standards no doubt).

<quote>
1.3.24 [defns.undefined]
undefined behavior
behavior for which this International Standard imposes no requirements
[ Note: Undefined behavior may be expected when this International
Standard omits any explicit definition of`behavior [...].
Many erroneous program constructs do not engender undefined behavior;
they are required to be diagnosed.
</quote>

Coming back to the ODR rule, this is specifically mentioned as not to be
required to be diagnosed ("no diagnostic required"). I guess this is so
because the linker (which would be able to diagnose such errors) might
not be under the control of the compiler developers.

> Or perhaps it is just silly to do anything other than
> give a link error for symbols that are multiply defined or undefined
> when the ODR is broken. Hell, gcc ran nethack at one point, so while
> the compiler is technically free to do whatever it wants during
> undefined behavior, letting such undefined symbols slip through
> without an error feels highly unuseful.

The program works exactly the same as it would have worked when the
function had been defined, so I am not sure there is a good reason to
blame gcc here. On the other hand, there is no good reason to blame MSVC
either for not compiling the program.

Cheers
Paavo





Alain Ketterlin

unread,
Mar 17, 2016, 7:02:58 PM3/17/16
to
Paavo Helde <myfir...@osa.pri.ee> writes:

[...]
> So, conversion to a bool is "an lvalue-to-rvalue conversion", but
> conversion to a function pointer is not? Or is it that the first is a
> "constant expression", but the second is not?

I think because the first is a constant expression.

> I'm more confused than before. Section 5.20 contains word 'bool' only
> in an unrelated example code piece. And section 4.12 (Boolean
> conversions) talks only about pointer to bool conversions, so it seems
> in order to be converted to bool the function needs to be converted to
> a pointer first anyway.

Actually, I was refering to 3.2 ("One definition rule"), §3: "A variable
x whose name appears as a potentially-evaluated expression ex is
odr-used by ex unless applying the lvalue-to-rvalue conversion (4.1) to
x yields a constant expression (5.20) that does not invoke any non-
trivial functions [...]"

So the name is a constant expression after lval-to-rval conversion, and
it is not odr-used. After this, implicit standard conversions (Clause 4
[conv]) apply, especially (4.12) to convert to bool, and since the name
cannot be nullptr_t, the result of the conversion is true.

> How comes a program that contains an extra conversion
> (pointer-to-bool) does not contain UB while the program without this
> additional conversion has UB?

Clause 4 starts with: "Standard conversions are implicit conversions
with built-in meaning." And (5.20) notes: "Constant expressions can be
evaluated during translation."

Therefore, I see no UB, because there is no behavior at all...

-- Alain.
0 new messages