Underspecified object declarations

Alex Gilding (Perforce UK)

Jens Gustedt (INRIA France)

2022-06-10

org: ISO/IEC JCT1/SC22/WG14 document: N3006
target: IS 9899:2023 version: 1
date: 2022-06-10 license: CC BY

Revision history

Paper number Title Changes
N3006 Underspecified object declarations wording improvements
N2952 Underspecified object declarations common introduction replacing previous
N2890 type-generic programming
N2917 The constexpr specifier

Introduction

N2890 (type-generic programming) and N2917 (the constexpr specifier) proposed new features to C that are already much experienced in other programming languages. Whereas WG14 found current integration of the proposed features for functional extension into C insufficient, we voted in favor of an integration of features when they reduced to object declarations:

These features are syntactically and semantically closely related and so we decided to join efforts such that we may provide a seamless integration of both into C23.

The later three become thus much simpler and hopefully less contested.

Examples

#include <tgmath.h>
constexpr double π = 3.1415926536; // floating point constants
constexpr char const empty[] = ""; // named string constants
auto n = 4294967296u;              // obtain implementation-defined type
auto r = cos(x);                   // infer consistent type

Common syntax requirements

The features have several properties in common

auto n = 4294967296u;
int main() {
  auto k;                  // invalid

  auto alignof(n) n = 44;  // valid, refers to outer
  auto n = n;              // invalid, refers to inner, cyclic

  auto n = 4294967296u, m = 4294967297u; // same types ?

  auto x = (struct s {int a; }){ }; // confusion of scope for struct s
  struct s {int a; } x;             // clearly a local type definition
}

Interaction between storage-class specifiers

Storage-class specifier as a term becomes even more a misnomer with the features that are introduced in this series of papers. In fact they then specify

Their possible combination is as indicated in the following table:

co at au re tl st ex il td
constexpr
__auto_type
auto
register
thread_local
static
extern
inline
typedef

It might be worth considering to rename the term “storage-class specifier” editorially to something that fits better, such as “declaration feature” or “feature specifier”, and to fuse it with the term “function specifier” (inline and _Noreturn).

Design

WG14 would not have to vote on the proposed changes individually, but only in combination with N3007 and N3008.

Terminology

The term underspecified declaration is introduced in a new paragraph at the end of 6.7. Depending on the features that WG14 accepts for C23 one of the following three alternatives should be used.

Alternatives:
A declaration such that the declaration specifiers contain no type specifier or that is declared with constexpr is said to be underspecified.
A declaration such that the declaration specifiers contain no type specifier is said to be underspecified.
A declaration with constexpr is said to be underspecified.

Scope of identifiers

The property that the identifier under declaration should not be visible within the initializer is best modeled by adjusting the scope in which it is visible. Therefore we amend 6.2.1 p7 with special rules that ensure that

7 Structure, union, and enumeration tags have scope that begins just after the appearance of the tag in a type specifier that declares the tag. Each enumeration constant has scope that begins just after the appearance of its defining enumerator in an enumerator list. An ordinary identifier that has an underspecified definition has scope that starts when the definition is completed; if the same ordinary identifier declares another entity with a scope that encloses the current block, that declaration is hidden as soon as the inner declarator is completed.FNT) Any other identifier has scope that begins just after the completion of its declarator.

FNT) That means, that the outer declaration is not visible for the initializer.

Other identifiers

An underspecified declaration should declare exactly one ordinary identifier, no structure ore union tags or members or any other collaterals such as enumeration constants. This is achieved by two additions.

4’ In an underspecified declaration all declared identifiers that do not have a prior declaration shall be ordinary identifiers.
If such a declaration is not a definition, if it declares no or more than one ordinary identifier, if the declared identifier already has a declaration in the same scope, or if the declared entity is not an object, the behavior is undefined.

For the latter the choice is to go with UB, because there are already extensions that go beyond the definitions as given here, such as auto declarations of several variables or constexpr functions.

Storage-class specifiers (6.7.1)

Adapt p6 such that it better reflects on the effective role of storage-class specifiers

6 The typedef specifier is called a “storage-class specifier” for syntactic convenience only; it is discussed in 6.7.8.Storage-class specifiers specify various properties of identifiers and declared features; storage duration (static in block scope, thread_local, auto, register), linkage (extern, static in file scope, typedef) and type (typedef). The meanings of the various linkages and storage durations were discussed in 6.2.2 and 6.2.4, typedef is discussed in 6.7.8.