Search code examples
c++language-lawyerone-definition-rule

Is it correct to say that the compiler can replace the expression `a->i` below by its value 1 because...?


The code below compiles in GCC, clang and VS2017 and the expression a->i in the return statement is replaced by its constant value 1. Is it correct to say that this is valid because a is not odr-used in the expression a->i?.

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

PS: I believe a is not odr-used in the expression a->i because it satisfies the "unless" condition in [basic.def.odr]/4, as follows:

A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying the lvalue-to-rvalue conversion (7.1) to x yields a constant expression (8.6) that does not invoke any non-trivial functions and, 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 particular, the expression ex == a is an element of the set of potential results of the expression e == a->i, according to [basic.def.odr]/2 (2.3), containing the expression ex, where the lvalue-to-rvalue conversion is applied to e.


Solution

  • a is odr-used because you fail the first part of the "unless":

    applying the lvalue-to-rvalue conversion (7.1) to x yields a constant expression (8.6) that does not invoke any non-trivial functions

    Applying the lvalue-to-rvalue conversion to a does not yield a constant expression.

    The rest is core issues 315 and 232.


    Your analysis is broken in two additional ways:

    • The "object expression" is defined using the . form of class member access, so you need to rewrite a->i to dot form, i.e., (*a).i, before applying [basic.def.odr]/2.3. a is not a member of the set of potential results of that expression.
    • That bullet itself is defective because it was written with non-static data members in mind. For static data members, the set of potential results should be in fact the named static data member - see core issue 2353, so a is doubly not a member of the set of potential results of that expression.

    [expr.const]/2.7:

    An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine, would evaluate one of the following expressions:

    • [...]
    • an lvalue-to-rvalue conversion unless it is applied to
      • a non-volatile glvalue of integral or enumeration type that refers to a complete non-volatile const object with a preceding initialization, initialized with a constant expression, or
      • a non-volatile glvalue that refers to a subobject of a string literal, or
      • a non-volatile glvalue that refers to a non-volatile object defined with constexpr, or that refers to a non-mutable subobject of such an object, or
      • a non-volatile glvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation of e;
    • [...]