GCC, clang and VS2017 compile this code. Are they correct?

155 views
Skip to first unread message

Belloc

unread,
May 14, 2018, 3:27:30 PM5/14/18
to ISO C++ Standard - Discussion
According to [class.static]1, it seems to me this code should not compile.

#include <iostream>
struct A {
   
int i = 1;
   
static const int k = 1;
};
extern A a;
           
int main(){
//    std::cout << a.i << '\n';           // This doesn't compile because the object a is not defined
    std
::cout << a.k << '\n';           // But this compiles. Is this correct?
}

GCC, clang and VS2017 compile the code.

Richard Smith

unread,
May 14, 2018, 6:07:48 PM5/14/18
to std-dis...@isocpp.org
The rule you're referring to is the One Definition Rule: 'a' is required to have a definition due to the commented-out line because it results in an odr-use of 'a'. However, most violations of the ODR (including this one) are "no diagnostic required". So a compiler is permitted to accept such code -- even the commented-out line may be accepted by a conforming implementation. (It's easy to think of cases when it would be: if you put that line of code in an "if (false)", then compilers are likely to accept it, at least in their optimizing modes.)

If you make the expression complex enough, eventually the compilers will reject it (for example, this is enough to get clang to emit a reference to 'a' at -O0: https://wandbox.org/permlink/gdBwnpskd22VqkJc).

Ville Voutilainen

unread,
May 14, 2018, 6:30:30 PM5/14/18
to std-dis...@isocpp.org
Well, the definition can also be in a different translation unit, no?

Ville Voutilainen

unread,
May 14, 2018, 6:31:20 PM5/14/18
to std-dis...@isocpp.org
Ah, never mind, it can't, since the definition of A is in this one.
Belloc has such fun examples. :)

Thiago Macieira

unread,
May 14, 2018, 8:16:36 PM5/14/18
to std-dis...@isocpp.org
On Monday, 14 May 2018 15:31:18 PDT Ville Voutilainen wrote:
> Ah, never mind, it can't, since the definition of A is in this one.
> Belloc has such fun examples.

The compiler doesn't know if that definition came from an #include or not.

--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center



Vicente J. Botet Escriba

unread,
May 15, 2018, 1:17:36 AM5/15/18
to std-dis...@isocpp.org, Belloc
IMO, the commented line is also correct. There is just a link error.

A static member can be accessed either using the the qualified-id expression `A::k'  or using the class member access syntax `a.k`.
Why do you think the last line shouldn't compile?

Vicente

Andrew Giese

unread,
May 15, 2018, 6:58:44 AM5/15/18
to std-dis...@isocpp.org
@Richard Smith,
The code sample you provided compiles but doesn't link. Perhaps we should make that distinction. I don't think we could write any (well formed) expression using a that is guaranteed to fail to compile because another translation unit may provide the definition at link time.



--

---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussio...@isocpp.org.
To post to this group, send email to std-dis...@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.

Belloc

unread,
May 15, 2018, 9:47:48 AM5/15/18
to ISO C++ Standard - Discussion


On Tuesday, May 15, 2018 at 7:58:44 AM UTC-3, Andrew Giese wrote:
@Richard Smith,
The code sample you provided compiles but doesn't link. Perhaps we should make that distinction. I don't think we could write any (well formed) expression using a that is guaranteed to fail to compile because another translation unit may provide the definition at link time.

You are correct. I should have used the terms "link(ed)" instead of "compile(ed)" in my first post. My question should then be rephrased: "CGC, clang and VS2017 link this code. Are they correct?".

According to [basic.def.odr]/4 I think the variable a is odr-used in the expression std::cout << a.k << '\n'; as the name a doesn't satisfy the condition

if x is an object, ex is an element of the set of potential results of an expression e, where
either the lvalue-to-rvalue conversion (7.1) is applied to e, or e is a discarded-value expression (8.2).

in the alluded paragraph. Therefore, I believe the code in my first post should not link, because the compiler should not be allowed to replace a.k by 1 in the expression std::cout << a.k << '\n'; at compile time.

Andrew Giese

unread,
May 15, 2018, 11:13:48 AM5/15/18
to std-dis...@isocpp.org
I agree with you assessment, Belloc; a is odr-used in that expression and therefore linking should fail.

-----------------
Andy Giese

--

---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+unsubscribe@isocpp.org.

Andrey Semashev

unread,
May 15, 2018, 12:33:49 PM5/15/18
to std-dis...@isocpp.org
On 05/15/18 18:13, Andrew Giese wrote:
> I agree with you assessment, Belloc; a *is *odr-used in that expression
> and therefore linking should fail.

Thing is the compiler has no reason to reference `a` in this code. I
think, if we have to change something in regard to this case, it should
be the standard, which should make it implementation-defined.

> On Tue, May 15, 2018 at 8:47 AM, Belloc <jabe...@gmail.com
> <mailto:jabe...@gmail.com>> wrote:
>
>
>
> On Tuesday, May 15, 2018 at 7:58:44 AM UTC-3, Andrew Giese wrote:
>
> @Richard Smith,
> The code sample you provided compiles but doesn't link. Perhaps
> we should make that distinction. I don't think we could write
> any (well formed) expression using a that is guaranteed to fail
> to compile because another translation unit may provide the
> definition at link time.
>
>
> You are correct. I should have used the terms "link(ed)" instead of
> "compile(ed)" in my first post. My question should then be
> rephrased: "CGC, clang and VS2017 link this code. Are they correct?".
>
> According to [basic.def.odr]/4
> <http://eel.is/c++draft/basic.def.odr#4> I think the variable a *is*
> odr-used in the expression std::cout << a.k << '\n'; as the name a
> doesn't satisfy the condition
>
> if x is an object, ex is an element of the set of potential
> results of an expression e, where
> either the lvalue-to-rvalue conversion (7.1) is applied to e, or
> e is a discarded-value expression (8.2).
>
>
> in the alluded paragraph. Therefore, I believe the code in my first
> post should *not* link, because the compiler should not be allowed
> to replace a.k by 1 in the expression std::cout << a.k << '\n'; at
> compile time.
>
> --
>
> ---
> You received this message because you are subscribed to the Google
> Groups "ISO C++ Standard - Discussion" group.
> To unsubscribe from this group and stop receiving emails from it,
> send an email to std-discussio...@isocpp.org
> <mailto:std-discussio...@isocpp.org>.
> To post to this group, send email to std-dis...@isocpp.org
> <mailto:std-dis...@isocpp.org>.
> <https://groups.google.com/a/isocpp.org/group/std-discussion/>.
>
>
> --
>
> ---
> You received this message because you are subscribed to the Google
> Groups "ISO C++ Standard - Discussion" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to std-discussio...@isocpp.org
> <mailto:std-discussio...@isocpp.org>.
> To post to this group, send email to std-dis...@isocpp.org
> <mailto:std-dis...@isocpp.org>.

Thiago Macieira

unread,
May 15, 2018, 1:26:21 PM5/15/18
to std-dis...@isocpp.org
On Tuesday, 15 May 2018 09:33:44 PDT Andrey Semashev wrote:
> On 05/15/18 18:13, Andrew Giese wrote:
> > I agree with you assessment, Belloc; a *is *odr-used in that expression
> > and therefore linking should fail.
>
> Thing is the compiler has no reason to reference `a` in this code. I
> think, if we have to change something in regard to this case, it should
> be the standard, which should make it implementation-defined.

The odr-reference just got optimised out of existence. The compiler is allowed
to do that, even for functions.

extern void f();
static void g(bool b)
{
if (b) f();
}
void h()
{
g(false);
}

The function f() is referenced and if it's not defined in another TU, the
linking will fail. Except if the compiler inlined g() into h() and performed a
constant propagation, eliminating the dead code.

Belloc

unread,
May 15, 2018, 2:42:49 PM5/15/18
to ISO C++ Standard - Discussion
On Tuesday, May 15, 2018 at 2:26:21 PM UTC-3, Thiago Macieira wrote:
The odr-reference just got optimised out of existence. The compiler is allowed
to do that, even for functions.

Could you show me one quote from the Standard confirming this, i.e., that the compiler can eliminate the reference to the variablein the code? I have found out more than 200 occurrences of  the string "as if" in the Standard, explaining the application of  [intro.abstract]/1 in each individual case, where those optimizations are allowed. But I couldn't find one single reference applied to the case in discussion.

Richard Smith

unread,
May 15, 2018, 3:49:17 PM5/15/18
to std-dis...@isocpp.org

--

---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+unsubscribe@isocpp.org.
To post to this group, send email to std-dis...@isocpp.org.

Richard Smith

unread,
May 15, 2018, 3:50:20 PM5/15/18
to std-dis...@isocpp.org
On 15 May 2018 at 11:42, Belloc <jabe...@gmail.com> wrote:
As I already mentioned, this rule is "no diagnostic required". So no diagnostic is required. Here's the rule: http://eel.is/c++draft/basic.def.odr#10.sentence-1 

Belloc

unread,
May 15, 2018, 5:02:37 PM5/15/18
to ISO C++ Standard - Discussion

On Tuesday, May 15, 2018 at 4:50:20 PM UTC-3, Richard Smith wrote:

As I already mentioned, this rule is "no diagnostic required". So no diagnostic is required. Here's the rule: http://eel.is/c++draft/basic.def.odr#10.sentence-1 

Then, from [basic.def.odr]/10 and [intro.compliance]/2 (2.3), I conclude that the code above has undefined behavior. 

Thiago Macieira

unread,
May 16, 2018, 2:55:43 AM5/16/18
to std-dis...@isocpp.org
On Tuesday, 15 May 2018 14:02:37 PDT Belloc wrote:
> On Tuesday, May 15, 2018 at 4:50:20 PM UTC-3, Richard Smith wrote:
> > As I already mentioned, this rule is "no diagnostic required". So no
> > diagnostic is required. Here's the rule:
> > http://eel.is/c++draft/basic.def.odr#10.sentence-1
> > <http://www.google.com/url?q=http%3A%2F%2Feel.is%2Fc%2B%2Bdraft%2Fbasic.de
> > f.odr%2310.sentence-1&sa=D&sntz=1&usg=AFQjCNHzAmV9OPIHmDPSa-DiR-PXlJYyKQ>
> Then, from [basic.def.odr]/10 <http://eel.is/c++draft/basic.def.odr#10> and
> [intro.compliance]/2 (2.3) <http://eel.is/c++draft/intro.compliance#2.3>, I
> conclude that the code above has undefined behavior.

Either that or just plain ill-formed. Failing to provide the function that
would have been called or the variable that would have been used is a problem.

But no diagnostic required.

This case here is UB:

struct S
{
static const int i = 1;
};
int f()
{
S *s = nullptr;
return s->i;
Message has been deleted

Belloc

unread,
May 16, 2018, 8:57:13 AM5/16/18
to ISO C++ Standard - Discussion
On Wednesday, May 16, 2018 at 3:55:43 AM UTC-3, Thiago Macieira wrote:

Either that or just plain ill-formed. Failing to provide the function that
would have been called or the variable that would have been used is a problem.

But no diagnostic required.

This case here is UB:

struct S
{
    static const int i = 1;
};
int f()
{
    S *s = nullptr;
    return s->i;
}

Your example is even worse than mine, as there are no linking issues here. Notwithstanding the fact that [basic.def.odr]/10 allows NDR, why do compilers do that? It would be very simple for them to emit an error in this case, as the variable s is also odr-used. 

Andrey Semashev

unread,
May 16, 2018, 10:40:48 AM5/16/18
to std-dis...@isocpp.org
On 05/16/18 15:51, Belloc wrote:
> On Wednesday, May 16, 2018 at 3:55:43 AM UTC-3, Thiago Macieira wrote:
>
>
> Either that or just plain ill-formed. Failing to provide the
> function that
> would have been called or the variable that would have been used is
> a problem.
>
> But no diagnostic required.
>
> This case here is UB:
>
> struct S
> {
>     static const int i = 1;
> };
> int f()
> {
>     S *s = nullptr;
>     return s->i;
> }
>
>
> Notwithstanding the fact that [basic.def.odr]/10 allows NDR, why do
> compilers do that?

I'm not a compiler writer, but I would guess that it is additional work
for no benefit. The object is not used by the code, so emitting a
reference to it and then wasting time on resolving the reference on the
linking stage does no good. Additionally, if the object is imported from
a shared library, emitting a reference to it would result in linking to
the library while it is actually not required.

Thiago Macieira

unread,
May 16, 2018, 5:32:32 PM5/16/18
to std-dis...@isocpp.org
On Wednesday, 16 May 2018 05:51:34 PDT Belloc wrote:
> Your example is even worse than mine, as there are no linking issues here.

Modern GCC and Clang report a warning that this is UB. Despite not needing to
dereference s in order to get to S::i, the standard says that you cannot write
"s->" if s is null. This particular example is easy to detect because the
constant can be easily propagated. But it wouldn't be able to do that if the
variable were external to the function.

> Notwithstanding the fact that [basic.def.odr]/10 allows NDR, why do
> compilers do that? It would be very simple for them to emit an error in the
> two cases (your example and mine), as the variables s and a, respectively,
> are both odr-used.

Because it's not very simple. Considering the code paths that referenced the
variable were eliminated, where would you put the unnecessary reference?

Richard Smith

unread,
May 16, 2018, 6:05:05 PM5/16/18
to std-dis...@isocpp.org
The object file format may not even support emitting references that are (a) required to be diagnosed if absent and (b) guaranteed to not change the resulting executable. So doing this could bloat your binaries too. That said, it might be worthwhile in non-optimized compilation modes. (I expect I would accept a high-quality patch for my implementation that does this.)
Reply all
Reply to author
Forward
0 new messages