Home > OS >  Why is using a reserved identifier name in constexpr context not diagnosed?
Why is using a reserved identifier name in constexpr context not diagnosed?

Time:12-07

Based on the following two rules:

  • Using an identifier starting with "_" capital letter, or containing double underscore, is undefined behavior.
  • Undefined behavior is not allowed in constexpr expressions -> compiler should not compile.

Then why aren't compilers complaining about this?

constexpr int _UB() {return 1;}
int main() {
    constexpr int a = _UB();
    return a;
}

Demo

Also, I see a lot of professional, MISRA-compliant code that seems to violate this naming rule, see e.g. here:

#ifndef __STM32F732xx_H
#define __STM32F732xx_H

Are all these libs invoking UB too?

CodePudding user response:

Undefined behavior is not allowed in constexpr expressions -> compiler should not compile

It's a bit more narrow than this; as per [expr.const]/5, /5.7 in particular:

An expression E is a core constant expression unless the evaluation of E, following the rules of the abstract machine ([intro.execution]), would evaluate one of the following:

  • [...]
  • /5.7 an operation that would have undefined behavior as specified in [intro] through [cpp];

Now, [intro] through [cpp] includes:

1 Scope[intro.scope]
2 Normative references[intro.refs]
3 Terms and definitions[intro.defs]
4 General principles[intro]
5 Lexical conventions[lex]
6 Basics[basic]
7 Expressions[expr]
8 Statements[stmt.stmt]
9 Declarations[dcl.dcl]
10 Modules[module]
11 Classes[class]
12 Overloading[over]
13 Templates[temp]
14 Exception handling[except]
15 Preprocessing directives[cpp]

The rule about underscore for global names, however, comes from [library], particularly [reserved.names]/2 and [global.names]/1 in [library], which is not covered by "[intro] through [cpp]".

[reserved.names]/2 If a program declares or defines a name in a context where it is reserved, other than as explicitly allowed by this Clause, its behavior is undefined.

[global.names]/1 Certain sets of names and function signatures are always reserved to the implementation:

  • (1.1) Each name that contains a double underscore __ or begins with an underscore followed by an uppercase letter ([lex.key]) is reserved to the implementation for any use.
  • (1.2) Each name that begins with an underscore is reserved to the implementation for use as a name in the global namespace.

Now, [lex.name]/3 also includes the same rule for reservation of identifiers

In addition, some identifiers are reserved for use by C implementations and shall not be used otherwise; no diagnostic is required.

  • (3.1) Each identifier that contains a double underscore __ or begins with an underscore followed by an uppercase letter is reserved to the implementation for any use.
  • (3.2) Each identifier that begins with an underscore is reserved to the implementation for use as a name in the global namespace.

A violation of [lex.name]/3 is ill-formed no diagnostic required (IFNDR), which is not the same as undefined behaviour; as per [expr.const]/5.7 above some UB should actually be diagnosed (constexpr contexts).

The limitations of [expr.const]/5.7 to UB as per [intro] through [cpp] is arguably intentionally limiting the rule to avoid constructs that is UB for the typical C implementor but not the STL library implementors, e.g. rules in [library]. This could also be a wording defect, particularly as the rules of [lex.name]/3 goes from IFNDR to UB only "after the fact" of [reserved.names]/2 in [library].

Thus, this "kind" of undefined behaviour (UB) does arguably not fall under the UB that disqualifies an expression from being a core constant expression.

CodePudding user response:

Having macros, variables or other members following these conventions isn't "undefined behaviour". It's perfectly legal, unlike for example identifiers starting with digits, control chars or other such things.

Identifiers starting with two (2) underscores, such as

typedef uint32_t __my4ByteInt;

is not recommended, because the standard library uses this convention for internal identifiers and it could cause issues if your member has the same name.

Other than that, these identifiers are compliant. I'm not sure about MISRA, but even there I'm fairly certain naming conventions are only recommendations and isn't checked by the compiler, but by third-party analysis tools like CodeSonar.

  • Related