llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang-codegen Author: None (yronglin) <details> <summary>Changes</summary> This patch implement [P2795R5 'Erroneous behaviour for uninitialized reads'](https://wg21.link/p2795r5). In C++26, reading from an uninitialized automatic storage duration variable is no longer undefined behavior, since P2795R5, it's a defined erroneous behavior. - Add `[[indeterminate]]` standard attribute to opt-out of erroneous initialization (restore UB) - Add `APValue::Erroneous` to track erroneous values in constant expression evaluation - Reject erroneous values in constant expressions (same as indeterminate) - Add `-Werroneous-behavior` diagnostic group for runtime warnings - Support `[[indeterminate]]` on local variables and function parameters --- Patch is 33.70 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/177614.diff 26 Files Affected: - (modified) clang/docs/ReleaseNotes.rst (+1) - (modified) clang/include/clang/AST/APValue.h (+17-1) - (modified) clang/include/clang/AST/PropertiesBase.td (+3) - (modified) clang/include/clang/Basic/Attr.td (+22) - (modified) clang/include/clang/Basic/AttrDocs.td (+34) - (modified) clang/include/clang/Basic/DiagnosticGroups.td (+1) - (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+13) - (modified) clang/lib/AST/APValue.cpp (+3) - (modified) clang/lib/AST/ASTImporter.cpp (+1) - (modified) clang/lib/AST/Expr.cpp (+1) - (modified) clang/lib/AST/ExprConstant.cpp (+8-3) - (modified) clang/lib/AST/ItaniumMangle.cpp (+2) - (modified) clang/lib/AST/MicrosoftMangle.cpp (+1) - (modified) clang/lib/AST/TextNodeDumper.cpp (+4) - (modified) clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp (+3-1) - (modified) clang/lib/CodeGen/CGDecl.cpp (+5) - (modified) clang/lib/CodeGen/CGExprConstant.cpp (+3) - (modified) clang/lib/Sema/AnalysisBasedWarnings.cpp (+29-1) - (modified) clang/lib/Sema/SemaDecl.cpp (+23) - (modified) clang/lib/Sema/SemaTemplate.cpp (+1) - (added) clang/test/CodeGenCXX/cxx26-indeterminate-attribute.cpp (+82) - (added) clang/test/SemaCXX/cxx26-erroneous-behavior-warning.cpp (+29) - (added) clang/test/SemaCXX/cxx26-erroneous-constexpr.cpp (+55) - (added) clang/test/SemaCXX/cxx26-indeterminate-attribute.cpp (+66) - (modified) clang/utils/TableGen/ClangAttrEmitter.cpp (+1-1) - (modified) clang/www/cxx_status.html (+1-1) ``````````diff 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..edf1452e25717 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 (P2795R5) +that can be applied to the definition of a block 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 ... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/177614 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
