Lnkqwq wrote:

This PR fixes #88603, an inconsistency in Clang's diagnostic output when 
handling `const volatile` integer static data member initializers in C++98 mode.

## Problem

Consider the following code (C++98):

```cpp
const int a = 1;
const volatile int b = 1;
struct S {
  static const int ay = a;      // OK
  static const int by = b;      // error, but no note
  int az : a;                   // OK
  int bz : b;                   // error + note
};
```

Current Clang output:

```
<source>:5:25: error: in-class initializer for static data member is not a 
constant expression
  static const int by = b;
                        ^
<source>:7:12: error: expression is not an integral constant expression
  int bz : b;
           ^
<source>:7:12: note: read of volatile-qualified type 'const volatile int' is 
not allowed in a constant expression
```

The inconsistency: Both contexts (static member initializer and bit-field 
width) require an integral constant expression in C++98, but only the bit-field 
case emits the helpful note explaining why it's not constant (volatile read). 
The static member case just says "not a constant expression" without 
explanation.

Root Cause Analysis

In SemaDecl.cpp, the code that handles static data member initializers has a 
branch for integral/enumeration types:

```cpp
} else if (DclT->isIntegralOrEnumerationType()) {
  if (getLangOpts().CPlusPlus11 && DclT.isVolatileQualified())
    Diag(VDecl->getLocation(), diag::err_in_class_initializer_volatile);
  // No constant expression check in C++98 mode!
}
```

For C++98, this branch does no constant expression checking at all—it simply 
accepts the initializer without verification. This is why no diagnostic (error 
or note) is produced for the volatile case in C++98.

In contrast, the bit-field handling code (elsewhere) calls 
EvaluateAsConstantExpr(), which performs full constant expression evaluation 
and captures detailed diagnostics (like the volatile read note).

Solution

This PR adds constant expression checking to the integral/enumeration branch 
for all language modes (not just C++11+):

```cpp
} else if (DclT->isIntegralOrEnumerationType()) {
  if (getLangOpts().CPlusPlus11 && DclT.isVolatileQualified())
    Diag(VDecl->getLocation(), diag::err_in_class_initializer_volatile);

  // NEW: Check if it's really a constant expression (for all language modes)
  if (!Init->isValueDependent()) {
    Expr::EvalResult EvalResult;
    if (!Init->EvaluateAsConstantExpr(EvalResult, Context)) {
      Diag(Init->getExprLoc(), diag::err_in_class_initializer_non_constant)
        << Init->getSourceRange();
      VDecl->setInvalidDecl();
      
      // Propagate any detailed notes from evaluation (e.g., volatile read)
      if (EvalResult.Diag) {
        Diag(EvalResult.Diag->first, EvalResult.Diag->second);
      }
    }
  }
}
```

This makes the static member initializer path:

1. Actually verify that the initializer is a valid constant expression
2. Capture and report the same detailed diagnostics that other contexts (like 
bit-fields) already emit

https://github.com/llvm/llvm-project/pull/185021
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to