https://github.com/yronglin updated 
https://github.com/llvm/llvm-project/pull/177614

>From acb043dc208cf70c3b0ebf4c35cbf0ef77a2f72b Mon Sep 17 00:00:00 2001
From: "Wang, Yihan" <[email protected]>
Date: Sat, 24 Jan 2026 00:53:21 +0800
Subject: [PATCH] [C++26][clang] Implement P2795R5 'Erroneous behaviour for
 uninitialized reads'

Signed-off-by: Wang, Yihan <[email protected]>
---
 clang/docs/ReleaseNotes.rst                   |  1 +
 clang/include/clang/AST/APValue.h             | 18 +++-
 clang/include/clang/AST/PropertiesBase.td     |  3 +
 clang/include/clang/Basic/Attr.td             | 22 +++++
 clang/include/clang/Basic/AttrDocs.td         | 34 ++++++++
 clang/include/clang/Basic/DiagnosticGroups.td |  1 +
 .../clang/Basic/DiagnosticSemaKinds.td        | 13 +++
 clang/lib/AST/APValue.cpp                     |  3 +
 clang/lib/AST/ASTImporter.cpp                 |  1 +
 clang/lib/AST/Expr.cpp                        |  1 +
 clang/lib/AST/ExprConstant.cpp                | 11 ++-
 clang/lib/AST/ItaniumMangle.cpp               |  2 +
 clang/lib/AST/MicrosoftMangle.cpp             |  1 +
 clang/lib/AST/TextNodeDumper.cpp              |  4 +
 clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp  |  4 +-
 clang/lib/CodeGen/CGDecl.cpp                  |  5 ++
 clang/lib/CodeGen/CGExprConstant.cpp          |  3 +
 clang/lib/Sema/AnalysisBasedWarnings.cpp      | 30 ++++++-
 clang/lib/Sema/SemaDecl.cpp                   | 23 ++++++
 clang/lib/Sema/SemaTemplate.cpp               |  1 +
 .../cxx26-indeterminate-attribute.cpp         | 82 +++++++++++++++++++
 .../cxx26-erroneous-behavior-warning.cpp      | 29 +++++++
 .../SemaCXX/cxx26-erroneous-constexpr.cpp     | 55 +++++++++++++
 .../SemaCXX/cxx26-indeterminate-attribute.cpp | 66 +++++++++++++++
 clang/utils/TableGen/ClangAttrEmitter.cpp     |  2 +-
 clang/www/cxx_status.html                     |  2 +-
 26 files changed, 409 insertions(+), 8 deletions(-)
 create mode 100644 clang/test/CodeGenCXX/cxx26-indeterminate-attribute.cpp
 create mode 100644 clang/test/SemaCXX/cxx26-erroneous-behavior-warning.cpp
 create mode 100644 clang/test/SemaCXX/cxx26-erroneous-constexpr.cpp
 create mode 100644 clang/test/SemaCXX/cxx26-indeterminate-attribute.cpp

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index a734804865c57..92ece34adefff 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -81,6 +81,7 @@ C++ Language Changes
 
 C++2c Feature Support
 ^^^^^^^^^^^^^^^^^^^^^
+- Clang now supports `P2795R5 <https://wg21.link/p2795r5>`_ Erroneous 
behaviour for uninitialized reads.
 
 C++23 Feature Support
 ^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/AST/APValue.h 
b/clang/include/clang/AST/APValue.h
index 8a2d6d434792a..620cbf2fe09e4 100644
--- a/clang/include/clang/AST/APValue.h
+++ b/clang/include/clang/AST/APValue.h
@@ -129,6 +129,15 @@ class APValue {
     None,
     /// This object has an indeterminate value (C++ [basic.indet]).
     Indeterminate,
+
+    /// [defns.erroneous]:
+    /// Erroneous behavior is always the consequence of incorrectprogram code.
+    /// Implementations are allowed, but not required, to diagnose it
+    /// ([intro.compliance.general]). Evaluation of a constant expression
+    /// ([expr.const]) never exhibits behavior specified as erroneous in 
[intro]
+    /// through [cpp].
+    /// Reading it has erroneous behavior but the value is well-defined.
+    Erroneous,
     Int,
     Float,
     FixedPoint,
@@ -435,11 +444,17 @@ class APValue {
     return Result;
   }
 
+  static APValue ErroneousValue() {
+    APValue Result;
+    Result.Kind = Erroneous;
+    return Result;
+  }
+
   APValue &operator=(const APValue &RHS);
   APValue &operator=(APValue &&RHS);
 
   ~APValue() {
-    if (Kind != None && Kind != Indeterminate)
+    if (Kind != None && Kind != Indeterminate && Kind != Erroneous)
       DestroyDataAndMakeUninit();
   }
 
@@ -462,6 +477,7 @@ class APValue {
 
   bool isAbsent() const { return Kind == None; }
   bool isIndeterminate() const { return Kind == Indeterminate; }
+  bool isErroneous() const { return Kind == Erroneous; }
   bool hasValue() const { return Kind != None && Kind != Indeterminate; }
 
   bool isInt() const { return Kind == Int; }
diff --git a/clang/include/clang/AST/PropertiesBase.td 
b/clang/include/clang/AST/PropertiesBase.td
index 5b10127526e4e..3ea1be6bbbd18 100644
--- a/clang/include/clang/AST/PropertiesBase.td
+++ b/clang/include/clang/AST/PropertiesBase.td
@@ -266,6 +266,9 @@ let Class = PropertyTypeCase<APValue, "None"> in {
 let Class = PropertyTypeCase<APValue, "Indeterminate"> in {
   def : Creator<[{ return APValue::IndeterminateValue(); }]>;
 }
+let Class = PropertyTypeCase<APValue, "Erroneous"> in {
+  def : Creator<[{ return APValue::ErroneousValue(); }]>;
+}
 let Class = PropertyTypeCase<APValue, "Int"> in {
   def : Property<"value", APSInt> {
     let Read = [{ node.getInt() }];
diff --git a/clang/include/clang/Basic/Attr.td 
b/clang/include/clang/Basic/Attr.td
index ba44266d22c8c..aaa5e04effebe 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -100,6 +100,9 @@ class SubsetSubject<AttrSubject base, code check, string 
diag> : AttrSubject {
 def LocalVar : SubsetSubject<Var,
                               [{S->hasLocalStorage() && !isa<ParmVarDecl>(S)}],
                               "local variables">;
+def AutomaticStorageVar : SubsetSubject<Var,
+                              [{S->hasLocalStorage()}],
+                              "local variables or function parameters">;
 def NonParmVar : SubsetSubject<Var,
                                [{S->getKind() != Decl::ParmVar}],
                                "variables">;
@@ -350,6 +353,10 @@ class CXX11<string namespace, string name, int version = 1>
     : Spelling<name, "CXX11", version> {
   string Namespace = namespace;
 }
+class CXX26<string namespace, string name, int version = 1>
+    : Spelling<name, "CXX26", version> {
+  string Namespace = namespace;
+}
 class C23<string namespace, string name, int version = 1>
     : Spelling<name, "C23", version> {
   string Namespace = namespace;
@@ -432,6 +439,7 @@ def SYCLHost : LangOpt<"SYCLIsHost">;
 def SYCLDevice : LangOpt<"SYCLIsDevice">;
 def COnly : LangOpt<"", "!LangOpts.CPlusPlus">;
 def CPlusPlus : LangOpt<"CPlusPlus">;
+def CPlusPlus26 : LangOpt<"CPlusPlus26">;
 def OpenCL : LangOpt<"OpenCL">;
 def ObjC : LangOpt<"ObjC">;
 def BlocksSupported : LangOpt<"Blocks">;
@@ -4827,6 +4835,20 @@ def Uninitialized : InheritableAttr {
   let Documentation = [UninitializedDocs];
 }
 
+// [dcl.attr.indet]/p1:
+// The attribute-token indeterminate may be applied to the definition of a 
block variable
+// with automatic storage duration or to a parameter-declaration of a function 
declaration. 
+// No attribute-argument-clause shall be present. The attribute specifies that 
the storage
+// of an object with automatic storage duration is initially indeterminate 
rather than
+// erroneous ([basic.indet]).
+def Indeterminate : InheritableAttr {
+  let Spellings = [CXX11<"", "indeterminate", 202403>];
+  let Subjects = SubjectList<[AutomaticStorageVar]>;
+  let LangOpts = [CPlusPlus26];
+  let Documentation = [IndeterminateDocs];
+  let SimpleHandler = 1;
+}
+
 def LoaderUninitialized : Attr {
   let Spellings = [Clang<"loader_uninitialized">];
   let Subjects = SubjectList<[GlobalVar]>;
diff --git a/clang/include/clang/Basic/AttrDocs.td 
b/clang/include/clang/Basic/AttrDocs.td
index 812b48058d189..98a74f551a89a 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -7205,6 +7205,40 @@ it rather documents the programmer's intent.
   }];
 }
 
+def IndeterminateDocs : Documentation {
+  let Category = DocCatVariable;
+  let Content = [{
+The ``[[indeterminate]]`` attribute is a C++26 standard attribute that can be
+applied to the definition of a variable with automatic storage duration or to
+a function parameter declaration.
+
+In C++26, reading from an uninitialized variable has *erroneous behavior*
+rather than undefined behavior. Variables are implicitly initialized with
+implementation-defined values. The ``[[indeterminate]]`` attribute opts out
+of this erroneous initialization, restoring the previous behavior where the
+variable has an *indeterminate value* and reading it causes undefined behavior.
+
+This attribute is intended for performance-critical code where the overhead
+of automatic initialization is unacceptable. Use with caution, as reading
+from such variables before proper initialization causes undefined behavior.
+
+.. code-block:: c++
+
+  void example() {
+    int x [[indeterminate]];  // x has indeterminate value (not erroneous)
+    int y;                    // y has erroneous value (reading is erroneous 
behavior)
+
+    // Reading x before initialization is undefined behavior
+    // Reading y before initialization is erroneous behavior
+  }
+
+  void f([[indeterminate]] int param);  // param has indeterminate value
+
+If a function parameter is declared with the ``[[indeterminate]]`` attribute,
+it must be so declared in the first declaration of its function.
+  }];
+}
+
 def LoaderUninitializedDocs : Documentation {
   let Category = DocCatVariable;
   let Content = [{
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td 
b/clang/include/clang/Basic/DiagnosticGroups.td
index 34624dd3eed3a..027ddb35a6464 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -982,6 +982,7 @@ def Uninitialized  : DiagGroup<"uninitialized", 
[UninitializedSometimes,
                                                  UninitializedStaticSelfInit,
                                                  UninitializedConstReference,
                                                  UninitializedConstPointer]>;
+def ErroneousBehavior : DiagGroup<"erroneous-behavior">;
 def IgnoredPragmaIntrinsic : DiagGroup<"ignored-pragma-intrinsic">;
 // #pragma optimize is often used to avoid to work around MSVC codegen bugs or
 // to disable inlining. It's not completely clear what alternative to suggest
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index a2be7ab3791b9..9de87275d6c0b 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2567,6 +2567,19 @@ def warn_uninit_const_pointer : Warning<
   "variable %0 is uninitialized when passed as a const pointer argument here">,
   InGroup<UninitializedConstPointer>, DefaultIgnore;
 
+def warn_erroneous_behavior_uninitialized_read : Warning<
+  "reading from variable %0 with erroneous value is erroneous behavior">,
+  InGroup<ErroneousBehavior>, DefaultIgnore;
+def note_variable_erroneous_init : Note<
+  "variable %0 was default-initialized here; "
+  "consider using '[[indeterminate]]' attribute to opt out">;
+def err_indeterminate_attr_not_on_first_decl : Error<
+  "'[[indeterminate]]' attribute on parameter %0 must appear on the "
+  "first declaration of the function">;
+def err_indeterminate_attr_mismatch : Error<
+  "'[[indeterminate]]' attribute on parameter %0 does not match "
+  "previous declaration">;
+
 def warn_unsequenced_mod_mod : Warning<
   "multiple unsequenced modifications to %0">, InGroup<Unsequenced>;
 def warn_unsequenced_mod_use : Warning<
diff --git a/clang/lib/AST/APValue.cpp b/clang/lib/AST/APValue.cpp
index 2e1c8eb3726cf..1aed1ea610b85 100644
--- a/clang/lib/AST/APValue.cpp
+++ b/clang/lib/AST/APValue.cpp
@@ -724,6 +724,8 @@ void APValue::printPretty(raw_ostream &Out, const 
PrintingPolicy &Policy,
   case APValue::Indeterminate:
     Out << "<uninitialized>";
     return;
+  case APValue::Erroneous:
+    Out << "<erroneous>";
   case APValue::Int:
     if (Ty->isBooleanType())
       Out << (getInt().getBoolValue() ? "true" : "false");
@@ -1133,6 +1135,7 @@ LinkageInfo LinkageComputer::getLVForValue(const APValue 
&V,
   switch (V.getKind()) {
   case APValue::None:
   case APValue::Indeterminate:
+  case APValue::Erroneous:
   case APValue::Int:
   case APValue::Float:
   case APValue::FixedPoint:
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 101ab2c40973b..f190950fc707b 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -10601,6 +10601,7 @@ ASTNodeImporter::ImportAPValue(const APValue 
&FromValue) {
   switch (FromValue.getKind()) {
   case APValue::None:
   case APValue::Indeterminate:
+  case APValue::Erroneous:
   case APValue::Int:
   case APValue::Float:
   case APValue::FixedPoint:
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index 4bb979e51b75d..143024ea7b11e 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -302,6 +302,7 @@ ConstantResultStorageKind 
ConstantExpr::getStorageKind(const APValue &Value) {
   switch (Value.getKind()) {
   case APValue::None:
   case APValue::Indeterminate:
+  case APValue::Erroneous:
     return ConstantResultStorageKind::None;
   case APValue::Int:
     if (!Value.getInt().needsCleanup())
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 857688ed8039d..9ebd45eb77bcb 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -2694,6 +2694,7 @@ static bool HandleConversionToBool(const APValue &Val, 
bool &Result) {
   switch (Val.getKind()) {
   case APValue::None:
   case APValue::Indeterminate:
+  case APValue::Erroneous:
     return false;
   case APValue::Int:
     Result = Val.getInt().getBoolValue();
@@ -4251,9 +4252,11 @@ findSubobject(EvalInfo &Info, const Expr *E, const 
CompleteObject &Obj,
 
   // Walk the designator's path to find the subobject.
   for (unsigned I = 0, N = Sub.Entries.size(); /**/; ++I) {
-    // Reading an indeterminate value is undefined, but assigning over one is 
OK.
+    // Reading an indeterminate value is undefined, but assigning over one is
+    // OK. Reading an erroneous value is erroneous behavior also not allowed in
+    // constant expressions.
     if ((O->isAbsent() && !(handler.AccessKind == AK_Construct && I == N)) ||
-        (O->isIndeterminate() &&
+        ((O->isIndeterminate() || O->isErroneous()) &&
          !isValidIndeterminateAccess(handler.AccessKind))) {
       // Object has ended lifetime.
       // If I is non-zero, some subobject (member or array element) of a
@@ -4263,7 +4266,7 @@ findSubobject(EvalInfo &Info, const Expr *E, const 
CompleteObject &Obj,
         return false;
       if (!Info.checkingPotentialConstantExpression())
         Info.FFDiag(E, diag::note_constexpr_access_uninit)
-            << handler.AccessKind << O->isIndeterminate()
+            << handler.AccessKind << (O->isIndeterminate() || O->isErroneous())
             << E->getSourceRange();
       return handler.failed();
     }
@@ -5062,6 +5065,7 @@ struct CompoundAssignSubobjectHandler {
     case APValue::Vector:
       return foundVector(Subobj, SubobjType);
     case APValue::Indeterminate:
+    case APValue::Erroneous:
       Info.FFDiag(E, diag::note_constexpr_access_uninit)
           << /*read of=*/0 << /*uninitialized object=*/1
           << E->getLHS()->getSourceRange();
@@ -7828,6 +7832,7 @@ class APValueToBufferConverter {
     // Dig through Src to find the byte at SrcOffset.
     switch (Val.getKind()) {
     case APValue::Indeterminate:
+    case APValue::Erroneous:
     case APValue::None:
       return true;
 
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index fa28c0d444cc4..65c770a6589ce 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -6411,6 +6411,7 @@ static bool isZeroInitialized(QualType T, const APValue 
&V) {
   switch (V.getKind()) {
   case APValue::None:
   case APValue::Indeterminate:
+  case APValue::Erroneous:
   case APValue::AddrLabelDiff:
     return false;
 
@@ -6564,6 +6565,7 @@ void CXXNameMangler::mangleValueInTemplateArg(QualType T, 
const APValue &V,
   switch (V.getKind()) {
   case APValue::None:
   case APValue::Indeterminate:
+  case APValue::Erroneous:
     Out << 'L';
     mangleType(T);
     Out << 'E';
diff --git a/clang/lib/AST/MicrosoftMangle.cpp 
b/clang/lib/AST/MicrosoftMangle.cpp
index 551aa7bf3321c..73f0c52163d19 100644
--- a/clang/lib/AST/MicrosoftMangle.cpp
+++ b/clang/lib/AST/MicrosoftMangle.cpp
@@ -1940,6 +1940,7 @@ void 
MicrosoftCXXNameMangler::mangleTemplateArgValue(QualType T,
   switch (V.getKind()) {
   case APValue::None:
   case APValue::Indeterminate:
+  case APValue::Erroneous:
     // FIXME: MSVC doesn't allow this, so we can't be sure how it should be
     // mangled.
     if (WithScalarType)
diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index 7bc0404db1bee..898582aa4287e 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -608,6 +608,7 @@ static bool isSimpleAPValue(const APValue &Value) {
   switch (Value.getKind()) {
   case APValue::None:
   case APValue::Indeterminate:
+  case APValue::Erroneous:
   case APValue::Int:
   case APValue::Float:
   case APValue::FixedPoint:
@@ -682,6 +683,9 @@ void TextNodeDumper::Visit(const APValue &Value, QualType 
Ty) {
   case APValue::Indeterminate:
     OS << "Indeterminate";
     return;
+  case APValue::Erroneous:
+    OS << "Erroneous";
+    return;
   case APValue::Int:
     OS << "Int ";
     {
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
index ecb65d901de54..795dd7caafba8 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
@@ -1783,7 +1783,9 @@ mlir::Attribute ConstantEmitter::tryEmitPrivate(const 
APValue &value,
   switch (value.getKind()) {
   case APValue::None:
   case APValue::Indeterminate:
-    cgm.errorNYI("ConstExprEmitter::tryEmitPrivate none or indeterminate");
+  case APValue::Erroneous:
+    cgm.errorNYI(
+        "ConstExprEmitter::tryEmitPrivate none, indeterminate or erroneous");
     return {};
   case APValue::Int: {
     mlir::Type ty = cgm.convertType(destType);
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 8b1cd83af2396..0378b249bbf1d 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -1980,9 +1980,14 @@ void CodeGenFunction::EmitAutoVarInit(const 
AutoVarEmission &emission) {
   auto hasNoTrivialAutoVarInitAttr = [&](const Decl *D) {
     return D && D->hasAttr<NoTrivialAutoVarInitAttr>();
   };
+
+  // C++26 [[indeterminate]] attribute opts out of an erroneous
+  // initialization, restoring indeterminate (undefined) behavior.
+
   // Note: constexpr already initializes everything correctly.
   LangOptions::TrivialAutoVarInitKind trivialAutoVarInit =
       ((D.isConstexpr() || D.getAttr<UninitializedAttr>() ||
+        D.hasAttr<IndeterminateAttr>() ||
         hasNoTrivialAutoVarInitAttr(type->getAsTagDecl()) ||
         hasNoTrivialAutoVarInitAttr(CurFuncDecl))
            ? LangOptions::TrivialAutoVarInitKind::Uninitialized
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp 
b/clang/lib/CodeGen/CGExprConstant.cpp
index 0eec4dba4824a..e19982522f8cb 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -2441,7 +2441,10 @@ ConstantEmitter::tryEmitPrivate(const APValue &Value, 
QualType DestType,
   switch (Value.getKind()) {
   case APValue::None:
   case APValue::Indeterminate:
+  case APValue::Erroneous:
     // Out-of-lifetime and indeterminate values can be modeled as 'undef'.
+    // For C++ erroneous values, runtime code generation uses a defined 
pattern,
+    // but for constant expression failures we use undef.
     return llvm::UndefValue::get(CGM.getTypes().ConvertType(DestType));
   case APValue::LValue:
     return ConstantLValueEmitter(*this, Value, DestType,
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp 
b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index 03d84fc935b8e..ab14bf6a458f8 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -976,8 +976,34 @@ static void DiagUninitUse(Sema &S, const VarDecl *VD, 
const UninitUse &Use,
                           bool IsCapturedByBlock) {
   bool Diagnosed = false;
 
+  // [basic.indet]/p1.1:
+  //  - If the object has dynamic storage duration, or is the object associated
+  //    with a variable or function parameter whose first declaration is marked
+  //    with the [[indeterminate]] attribute ([dcl.attr.indet]), the bytes have
+  //    indeterminate values;
+  //
+  //  - otherwise, the bytes have erroneous values, where each value is
+  //  determined
+  //    by the implementation independently of the state of the program.
+  //
+  // If variable has automatic storage duration and does
+  // not have [[indeterminate]], reading it is erroneous behavior (not
+  // undefined). However, we still warn about it.
+  bool IsErroneousBehavior = S.getLangOpts().CPlusPlus26 &&
+                             VD->hasLocalStorage() &&
+                             !VD->hasAttr<IndeterminateAttr>();
   switch (Use.getKind()) {
   case UninitUse::Always:
+    if (IsErroneousBehavior &&
+        !S.Diags.isIgnored(diag::warn_erroneous_behavior_uninitialized_read,
+                           Use.getUser()->getBeginLoc())) {
+      S.Diag(Use.getUser()->getBeginLoc(),
+             diag::warn_erroneous_behavior_uninitialized_read)
+          << VD->getDeclName() << Use.getUser()->getSourceRange();
+      S.Diag(VD->getLocation(), diag::note_variable_erroneous_init)
+          << VD->getDeclName();
+      return;
+    }
     S.Diag(Use.getUser()->getBeginLoc(), diag::warn_uninit_var)
         << VD->getDeclName() << IsCapturedByBlock
         << Use.getUser()->getSourceRange();
@@ -3164,7 +3190,9 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings(
       !Diags.isIgnored(diag::warn_sometimes_uninit_var, D->getBeginLoc()) ||
       !Diags.isIgnored(diag::warn_maybe_uninit_var, D->getBeginLoc()) ||
       !Diags.isIgnored(diag::warn_uninit_const_reference, D->getBeginLoc()) ||
-      !Diags.isIgnored(diag::warn_uninit_const_pointer, D->getBeginLoc())) {
+      !Diags.isIgnored(diag::warn_uninit_const_pointer, D->getBeginLoc()) ||
+      !Diags.isIgnored(diag::warn_erroneous_behavior_uninitialized_read,
+                       D->getBeginLoc())) {
     if (CFG *cfg = AC.getCFG()) {
       UninitValsDiagReporter reporter(S);
       UninitVariablesAnalysisStats stats;
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index ae779d6830d9b..6692d1d00a129 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -30,6 +30,8 @@
 #include "clang/AST/Type.h"
 #include "clang/Basic/Builtins.h"
 #include "clang/Basic/DiagnosticComment.h"
+#include "clang/Basic/DiagnosticIDs.h"
+#include "clang/Basic/DiagnosticSema.h"
 #include "clang/Basic/HLSLRuntime.h"
 #include "clang/Basic/PartialDiagnostic.h"
 #include "clang/Basic/SourceManager.h"
@@ -3426,6 +3428,27 @@ static void mergeParamDeclAttributes(ParmVarDecl 
*newDecl,
            diag::note_carries_dependency_missing_first_decl) << 1/*Param*/;
   }
 
+  // C++26 [dcl.attr.indet]/p2:
+  // If a function parameter is declared with the indeterminate attribute, it
+  // shall be so declared in the first declaration of its function. If a
+  // function parameter is declared with the indeterminate attribute in the
+  // first declaration of its function in one translation unit and the same
+  // function is declared without the indeterminate attribute on the same
+  // parameter in its first declaration in another translation unit, the 
program
+  // is ill-formed, no diagnostic required.
+  if (S.getLangOpts().CPlusPlus26) {
+    const IndeterminateAttr *IA = newDecl->getAttr<IndeterminateAttr>();
+    if (IA && !oldDecl->hasAttr<IndeterminateAttr>()) {
+      S.Diag(IA->getLocation(), diag::err_indeterminate_attr_not_on_first_decl)
+          << newDecl;
+      const FunctionDecl *FirstFD =
+          cast<FunctionDecl>(oldDecl->getDeclContext())->getFirstDecl();
+      const ParmVarDecl *FirstVD =
+          FirstFD->getParamDecl(oldDecl->getFunctionScopeIndex());
+      S.Diag(FirstVD->getLocation(), diag::note_previous_declaration);
+    }
+  }
+
   propagateAttributes(
       newDecl, oldDecl, [&S](ParmVarDecl *To, const ParmVarDecl *From) {
         unsigned found = 0;
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 3497ff7856eed..fd8d44c703d48 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -8129,6 +8129,7 @@ static Expr 
*BuildExpressionFromNonTypeTemplateArgumentValue(
 
   case APValue::None:
   case APValue::Indeterminate:
+  case APValue::Erroneous:
     llvm_unreachable("Unexpected APValue kind.");
   case APValue::LValue:
   case APValue::MemberPointer:
diff --git a/clang/test/CodeGenCXX/cxx26-indeterminate-attribute.cpp 
b/clang/test/CodeGenCXX/cxx26-indeterminate-attribute.cpp
new file mode 100644
index 0000000000000..bc9bffda65129
--- /dev/null
+++ b/clang/test/CodeGenCXX/cxx26-indeterminate-attribute.cpp
@@ -0,0 +1,82 @@
+// RUN: %clang_cc1 -std=c++26 -triple x86_64-unknown-unknown %s -emit-llvm -o 
- | FileCheck %s -check-prefix=CXX26
+// RUN: %clang_cc1 -std=c++26 -triple x86_64-unknown-unknown 
-ftrivial-auto-var-init=zero %s -emit-llvm -o - | FileCheck %s 
-check-prefix=ZERO
+// RUN: %clang_cc1 -std=c++26 -triple x86_64-unknown-unknown 
-ftrivial-auto-var-init=pattern %s -emit-llvm -o - | FileCheck %s 
-check-prefix=PATTERN
+
+// Test for C++26 [[indeterminate]] attribute (P2795R5)
+// The [[indeterminate]] attribute opts out of erroneous initialization.
+
+template<typename T> void used(T &) noexcept;
+
+extern "C" {
+
+// Test: [[indeterminate]] should suppress zero/pattern initialization
+// CXX26-LABEL: test_indeterminate_local_var(
+// CXX26:       alloca i32
+// CXX26-NOT:   store
+// CXX26:       call void
+// ZERO-LABEL:  test_indeterminate_local_var(
+// ZERO:        alloca i32
+// ZERO-NOT:    store
+// ZERO:        call void
+// PATTERN-LABEL: test_indeterminate_local_var(
+// PATTERN:     alloca i32
+// PATTERN-NOT: store
+// PATTERN:     call void
+void test_indeterminate_local_var() {
+  [[indeterminate]] int x;
+  used(x);
+}
+
+// Test: Without [[indeterminate]], zero/pattern init should apply
+// CXX26-LABEL: test_normal_local_var(
+// CXX26:       alloca i32
+// CXX26-NEXT:  call void
+// ZERO-LABEL:  test_normal_local_var(
+// ZERO:        alloca i32
+// ZERO:        store i32 0
+// PATTERN-LABEL: test_normal_local_var(
+// PATTERN:     alloca i32
+// PATTERN:     store i32 -1431655766
+void test_normal_local_var() {
+  int y;
+  used(y);
+}
+
+// Test: [[indeterminate]] on multiple variables
+// ZERO-LABEL: test_indeterminate_multiple_vars(
+// ZERO:       %a = alloca i32
+// ZERO:       %b = alloca [10 x i32]
+// ZERO:       %c = alloca [10 x [10 x i32]]
+// ZERO-NOT:   call void @llvm.memset
+// ZERO:       call void @_Z4used
+void test_indeterminate_multiple_vars() {
+  [[indeterminate]] int a, b[10], c[10][10];
+  used(a);
+}
+
+// Test: Mixed indeterminate and normal variables
+// ZERO-LABEL: test_mixed_vars(
+void test_mixed_vars() {
+  int normal = {};               // Explicitly zero-initialized
+  [[indeterminate]] int indeterminate_var;
+  int erroneous;                 // Will get erroneous initialization if 
-ftrivial-auto-var-init
+  used(normal);
+  used(indeterminate_var);
+  used(erroneous);
+}
+
+} // extern "C"
+
+// Test: Struct with indeterminate member initialization
+struct SelfStorage {
+  char data[512];
+  void use_data();
+};
+
+// ZERO-LABEL: test_struct_indeterminate
+// ZERO:       alloca %struct.SelfStorage
+// ZERO-NOT:   call void @llvm.memset
+void test_struct_indeterminate() {
+  [[indeterminate]] SelfStorage s;
+  s.use_data();
+}
diff --git a/clang/test/SemaCXX/cxx26-erroneous-behavior-warning.cpp 
b/clang/test/SemaCXX/cxx26-erroneous-behavior-warning.cpp
new file mode 100644
index 0000000000000..d8d7cfa22eb6d
--- /dev/null
+++ b/clang/test/SemaCXX/cxx26-erroneous-behavior-warning.cpp
@@ -0,0 +1,29 @@
+// RUN: %clang_cc1 -std=c++26 -fsyntax-only -Werroneous-behavior -verify %s
+// RUN: %clang_cc1 -std=c++23 -fsyntax-only -Wuninitialized -verify=cxx23 %s
+
+// Test for C++26 erroneous behavior warnings (P2795R5)
+
+void test_erroneous_read() {
+  int x;              // expected-note {{variable 'x' was default-initialized 
here}} \
+                      // expected-note {{initialize the variable 'x' to 
silence this warning}} \
+                      // cxx23-note {{initialize the variable 'x' to silence 
this warning}}
+  int y = x;          // expected-warning {{reading from variable 'x' with 
erroneous value is erroneous behavior}} \
+                      // cxx23-warning {{variable 'x' is uninitialized when 
used here}}
+}
+
+// In C++23, this is regular uninitialized warning
+void test_cxx23_uninit() {
+  int x;              // cxx23-note {{initialize the variable 'x' to silence 
this warning}} \
+                      // expected-note {{variable 'x' was default-initialized 
here}} \
+                      // expected-note {{initialize the variable 'x' to 
silence this warning}}
+  int y = x;          // cxx23-warning {{variable 'x' is uninitialized when 
used here}} \
+                      // expected-warning {{reading from variable 'x' with 
erroneous value is erroneous behavior}}
+}
+
+#if __cplusplus >= 202400L
+// With [[indeterminate]], it's still a regular uninitialized warning (UB, not 
erroneous)
+void test_indeterminate_uninit() {
+  [[indeterminate]] int x;
+  int y = x;          // No erroneous behavior warning - this is UB, not 
erroneous
+}
+#endif
diff --git a/clang/test/SemaCXX/cxx26-erroneous-constexpr.cpp 
b/clang/test/SemaCXX/cxx26-erroneous-constexpr.cpp
new file mode 100644
index 0000000000000..336679e52bed5
--- /dev/null
+++ b/clang/test/SemaCXX/cxx26-erroneous-constexpr.cpp
@@ -0,0 +1,55 @@
+// RUN: %clang_cc1 -std=c++26 -fsyntax-only -verify %s
+
+// Test for C++26 erroneous behavior in constant expressions (P2795R5)
+// Reading an uninitialized/erroneous value in a constant expression is an 
error.
+
+// Direct read of default-initialized variable
+constexpr int test1() {
+  int x;        // default-initialized, has erroneous value
+  return x;     // expected-note {{read of uninitialized object is not allowed 
in a constant expression}}
+}
+constexpr int val1 = test1();  // expected-error {{constexpr variable 'val1' 
must be initialized by a constant expression}} \
+                               // expected-note {{in call to 'test1()'}}
+
+// Reading member with erroneous value
+struct S {
+  int x;
+  constexpr S() {}  // x has erroneous value
+};
+
+constexpr int test2() {
+  S s;
+  return s.x;   // expected-note {{read of uninitialized object is not allowed 
in a constant expression}}
+}
+constexpr int val2 = test2();  // expected-error {{constexpr variable 'val2' 
must be initialized by a constant expression}} \
+                               // expected-note {{in call to 'test2()'}}
+
+// [[indeterminate]] in constexpr - also an error
+constexpr int test3() {
+  [[indeterminate]] int x;  // x has indeterminate value (UB in general, error 
in constexpr)
+  return x;                 // expected-note {{read of uninitialized object is 
not allowed in a constant expression}}
+}
+constexpr int val3 = test3();  // expected-error {{constexpr variable 'val3' 
must be initialized by a constant expression}} \
+                               // expected-note {{in call to 'test3()'}}
+
+// Proper initialization is fine
+constexpr int test4() {
+  int x = 42;
+  return x;
+}
+constexpr int val4 = test4();  // OK
+
+// Array with erroneous elements
+constexpr int test5() {
+  int arr[3];  // elements have erroneous values
+  return arr[0];  // expected-note {{read of uninitialized object is not 
allowed in a constant expression}}
+}
+constexpr int val5 = test5();  // expected-error {{constexpr variable 'val5' 
must be initialized by a constant expression}} \
+                               // expected-note {{in call to 'test5()'}}
+
+// Partial initialization - uninitialized portion is erroneous
+constexpr int test6() {
+  int arr[3] = {1};  // arr[1] and arr[2] are zero-initialized, not erroneous
+  return arr[1];     // OK - zero-initialized
+}
+constexpr int val6 = test6();  // OK, val6 == 0
diff --git a/clang/test/SemaCXX/cxx26-indeterminate-attribute.cpp 
b/clang/test/SemaCXX/cxx26-indeterminate-attribute.cpp
new file mode 100644
index 0000000000000..ac272c7962d00
--- /dev/null
+++ b/clang/test/SemaCXX/cxx26-indeterminate-attribute.cpp
@@ -0,0 +1,66 @@
+// RUN: %clang_cc1 -std=c++26 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify=cxx23 %s
+
+// Test for C++26 [[indeterminate]] attribute (P2795R5)
+
+// In C++23, the attribute is unknown and ignored
+void test_cxx23() {
+  [[indeterminate]] int x;  // cxx23-warning {{'indeterminate' attribute 
ignored}}
+}
+
+#if __cplusplus >= 202400L
+
+// local variable with automatic storage duration
+void test_local_var() {
+  [[indeterminate]] int x;       // OK
+  [[indeterminate]] int arr[10]; // OK
+  [[indeterminate]] int a, b, c; // OK - multiple declarators
+}
+
+// function parameter
+void test_param([[indeterminate]] int x);  // OK
+
+// static storage duration
+// expected-warning@+1 {{'indeterminate' attribute only applies to local 
variables or function parameters}}
+[[indeterminate]] int global_var;
+
+void test_static() {
+  // expected-warning@+1 {{'indeterminate' attribute only applies to local 
variables or function parameters}}
+  [[indeterminate]] static int x;
+  // expected-warning@+1 {{'indeterminate' attribute only applies to local 
variables or function parameters}}
+  [[indeterminate]] thread_local int y;
+}
+
+// attribute on class-type local variable
+struct S {
+  int x;
+  S() {}
+};
+
+void test_class_type() {
+  [[indeterminate]] S s;  // OK - member x has indeterminate value
+}
+
+// constexpr context should error on reading indeterminate value
+constexpr int test_constexpr() {
+  [[indeterminate]] int x;
+  return x;  // expected-note {{read of uninitialized object is not allowed in 
a constant expression}}
+}
+constexpr int val_constexpr = test_constexpr(); // expected-error {{constexpr 
variable 'val_constexpr' must be initialized by a constant expression}} \
+                                                // expected-note {{in call to 
'test_constexpr()'}}
+
+// declaration position
+void test_decl_position() {
+  int x [[indeterminate]];  // OK - attribute on declarator
+  [[indeterminate]] int y;  // OK - attribute at beginning
+}
+
+// [[indeterminate]] must be on first declaration (P2795R5 [dcl.attr.indet]/p2)
+void first_decl_test(int x);                                // first 
declaration without attribute
+void first_decl_test([[indeterminate]] int x);              // expected-error 
{{'[[indeterminate]]' attribute on parameter 'x' must appear on the first 
declaration of the function}}
+                                                            // 
expected-note@-2 {{previous declaration is here}}
+
+void first_decl_ok([[indeterminate]] int x);                // first 
declaration with attribute - OK
+void first_decl_ok([[indeterminate]] int x) {}              // redeclaration 
with attribute - OK
+
+#endif // __cplusplus >= 202400L
diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp 
b/clang/utils/TableGen/ClangAttrEmitter.cpp
index 61fb40e79ea91..b92dfacfbe1dc 100644
--- a/clang/utils/TableGen/ClangAttrEmitter.cpp
+++ b/clang/utils/TableGen/ClangAttrEmitter.cpp
@@ -60,7 +60,7 @@ class FlattenedSpelling {
         N(Spelling.getValueAsString("Name")), OriginalSpelling(Spelling) {
     assert(V != "GCC" && V != "Clang" &&
            "Given a GCC spelling, which means this hasn't been flattened!");
-    if (V == "CXX11" || V == "C23" || V == "Pragma")
+    if (V == "CXX11" || V == "C23" || V == "Pragma" || V == "CXX26")
       NS = Spelling.getValueAsString("Namespace");
   }
 
diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index 30da7a636fda6..331e851393850 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -192,7 +192,7 @@ <h2 id="cxx26">C++2c implementation status</h2>
  <tr>
   <td>Erroneous behaviour for uninitialized reads</td>
   <td><a href="https://wg21.link/P2795R5";>P2795R5</a></td>
-  <td class="none" align="center">No</td>
+  <td class="unreleased" align="center">Clang 23</td>
  </tr>
  <tr>
   <td><tt>= delete("should have a reason");</tt></td>

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

Reply via email to