https://github.com/MitalAshok updated https://github.com/llvm/llvm-project/pull/95580
>From ef42900b5e3533839c126cb9e6b19d7dcc7806c6 Mon Sep 17 00:00:00 2001 From: Mital Ashok <mi...@mitalashok.co.uk> Date: Fri, 14 Jun 2024 19:14:21 +0100 Subject: [PATCH 1/5] [Clang] [SemaCXX] Diagnose unknown std::initializer_list layout in SemaInit --- .../clangd/unittests/HoverTests.cpp | 2 +- .../clangd/unittests/InlayHintTests.cpp | 2 +- .../min-max-use-initializer-list.cpp | 1 + ...e-emplace-ignore-implicit-constructors.cpp | 3 +- .../checkers/modernize/use-emplace.cpp | 3 +- .../inefficient-vector-operation.cpp | 4 +- .../readability/isolate-declaration-cxx17.cpp | 2 +- .../clang/Basic/DiagnosticSemaKinds.td | 3 ++ clang/lib/AST/ExprConstant.cpp | 51 ++++++++----------- clang/lib/CodeGen/CGExprAgg.cpp | 44 +++++++--------- clang/lib/Sema/SemaInit.cpp | 51 +++++++++++++++++++ .../dcl.spec/dcl.type/dcl.spec.auto/p4.cpp | 2 +- .../dcl.type/dcl.spec.auto/p7-cxx14.cpp | 4 +- clang/test/CodeCompletion/ctor-signature.cpp | 2 +- clang/test/Coverage/unresolved-ctor-expr.cpp | 2 +- .../Modules/Inputs/initializer_list/direct.h | 2 +- clang/test/Modules/pr60775.cppm | 5 +- ...declare_reduction_codegen_in_templates.cpp | 2 +- .../macro_with_initializer_list.cpp | 2 +- ...d_initializer_list_diagnosis_assertion.cpp | 17 +++---- .../test/SemaCXX/auto-invalid-init-crash.cpp | 4 +- .../cxx0x-initializer-stdinitializerlist.cpp | 39 ++++++++++++-- .../cxx11-call-to-deleted-constructor.cpp | 2 +- clang/test/SemaCXX/cxx98-compat.cpp | 8 +-- clang/test/SemaCXX/invalid-member-expr.cpp | 2 +- clang/test/SemaTemplate/instantiate-init.cpp | 4 +- .../ASTMatchers/ASTMatchersNodeTest.cpp | 4 +- .../Analysis/FlowSensitive/TransferTest.cpp | 2 +- .../UncheckedOptionalAccessModelTest.cpp | 1 + 29 files changed, 167 insertions(+), 103 deletions(-) diff --git a/clang-tools-extra/clangd/unittests/HoverTests.cpp b/clang-tools-extra/clangd/unittests/HoverTests.cpp index d9e97e5215a26..8d6d4223d7260 100644 --- a/clang-tools-extra/clangd/unittests/HoverTests.cpp +++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp @@ -2284,7 +2284,7 @@ TEST(Hover, All) { namespace std { template<class _E> - class initializer_list {}; + class initializer_list { const _E *a, *b; }; } void foo() { ^[[auto]] i = {1,2}; diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp index 5b1531eb2fa60..a5a349e93037a 100644 --- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp +++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp @@ -945,7 +945,7 @@ TEST(ParameterHints, ConstructorStdInitList) { // Do not show hints for std::initializer_list constructors. assertParameterHints(R"cpp( namespace std { - template <typename> class initializer_list {}; + template <typename E> class initializer_list { const E *a, *b; }; } struct S { S(std::initializer_list<int> param); diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/min-max-use-initializer-list.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/min-max-use-initializer-list.cpp index 1f2dad2b933ca..c7632fe007a4f 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/min-max-use-initializer-list.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/min-max-use-initializer-list.cpp @@ -11,6 +11,7 @@ T max(T a, T b) { namespace std { template< class T > struct initializer_list { + const T *a, *b; initializer_list()=default; initializer_list(T*,int){} const T* begin() const {return nullptr;} diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-emplace-ignore-implicit-constructors.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-emplace-ignore-implicit-constructors.cpp index 2004993ebde51..150e3ac6494e3 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-emplace-ignore-implicit-constructors.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-emplace-ignore-implicit-constructors.cpp @@ -4,10 +4,11 @@ // RUN: true}}" namespace std { -template <typename> +template <typename E> class initializer_list { public: + const E *a, *b; initializer_list() noexcept {} }; diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-emplace.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-emplace.cpp index f7b1ad55f5df5..3f4a14cd9bb64 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-emplace.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-emplace.cpp @@ -8,9 +8,10 @@ // RUN: '::std::make_pair; ::std::make_tuple; ::test::MakeSingle'}}" namespace std { -template <typename> +template <typename E> class initializer_list { public: + const E *a, *b; initializer_list() noexcept {} }; diff --git a/clang-tools-extra/test/clang-tidy/checkers/performance/inefficient-vector-operation.cpp b/clang-tools-extra/test/clang-tidy/checkers/performance/inefficient-vector-operation.cpp index c28592f4d6368..c295f48f89aed 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/performance/inefficient-vector-operation.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/performance/inefficient-vector-operation.cpp @@ -5,7 +5,7 @@ namespace std { -typedef int size_t; +typedef decltype(sizeof 0) size_t; template<class E> class initializer_list { public: @@ -15,6 +15,8 @@ template<class E> class initializer_list { using size_type = size_t; using iterator = const E*; using const_iterator = const E*; + iterator ptr; + size_type sz; initializer_list(); size_t size() const; // number of elements const E* begin() const; // first element diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/isolate-declaration-cxx17.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/isolate-declaration-cxx17.cpp index f42f2f37155af..b50ad4ce25839 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/isolate-declaration-cxx17.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/isolate-declaration-cxx17.cpp @@ -31,7 +31,7 @@ struct SomeClass { namespace std { template <typename T> -class initializer_list {}; +class initializer_list { const T *a, *b; }; template <typename T> class vector { diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index ab223f2b806d5..d27c61f511afa 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -12216,6 +12216,9 @@ def err_std_source_location_impl_not_found : Error< def err_std_source_location_impl_malformed : Error< "'std::source_location::__impl' must be standard-layout and have only two 'const char *' fields '_M_file_name' and '_M_function_name', and two integral fields '_M_line' and '_M_column'">; +def err_std_initializer_list_malformed : Error< + "%0 layout not recognized. Must be a struct with two fields, a 'const E *' and either another 'const E *' or a 'std::size_t'">; + // HLSL Diagnostics def err_hlsl_attr_unsupported_in_stage : Error<"attribute %0 is unsupported in '%1' shaders, requires %select{|one of the following: }2%3">; def err_hlsl_attr_invalid_type : Error< diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 7178f081d9cf3..4aa19793fa099 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -10528,48 +10528,37 @@ bool RecordExprEvaluator::VisitCXXStdInitializerListExpr( // Get a pointer to the first element of the array. Array.addArray(Info, E, ArrayType); - auto InvalidType = [&] { - Info.FFDiag(E, diag::note_constexpr_unsupported_layout) - << E->getType(); - return false; - }; - - // FIXME: Perform the checks on the field types in SemaInit. - RecordDecl *Record = E->getType()->castAs<RecordType>()->getDecl(); - RecordDecl::field_iterator Field = Record->field_begin(); - if (Field == Record->field_end()) - return InvalidType(); - - // Start pointer. - if (!Field->getType()->isPointerType() || - !Info.Ctx.hasSameType(Field->getType()->getPointeeType(), - ArrayType->getElementType())) - return InvalidType(); - // FIXME: What if the initializer_list type has base classes, etc? Result = APValue(APValue::UninitStruct(), 0, 2); Array.moveInto(Result.getStructField(0)); - if (++Field == Record->field_end()) - return InvalidType(); - - if (Field->getType()->isPointerType() && - Info.Ctx.hasSameType(Field->getType()->getPointeeType(), - ArrayType->getElementType())) { + RecordDecl *Record = E->getType()->castAs<RecordType>()->getDecl(); + RecordDecl::field_iterator Field = Record->field_begin(); + assert(Field != Record->field_end() && + Info.Ctx.hasSameType(Field->getType()->getPointeeType(), + ArrayType->getElementType()) && + "Expected std::initializer_list first field to be const E *"); + ++Field; + assert(Field != Record->field_end() && + "Expected std::initializer_list to have two fields"); + + if (Info.Ctx.hasSameType(Field->getType(), Info.Ctx.getSizeType())) { + // Length. + Result.getStructField(1) = APValue(APSInt(ArrayType->getSize())); + } else { // End pointer. + assert(Info.Ctx.hasSameType(Field->getType()->getPointeeType(), + ArrayType->getElementType()) && + "Expected std::initializer_list second field to be const E *"); if (!HandleLValueArrayAdjustment(Info, E, Array, ArrayType->getElementType(), ArrayType->getZExtSize())) return false; Array.moveInto(Result.getStructField(1)); - } else if (Info.Ctx.hasSameType(Field->getType(), Info.Ctx.getSizeType())) - // Length. - Result.getStructField(1) = APValue(APSInt(ArrayType->getSize())); - else - return InvalidType(); + } - if (++Field != Record->field_end()) - return InvalidType(); + assert(++Field == Record->field_end() && + "Expected std::initializer_list to only have two fields"); return true; } diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index b2a5ceeeae08b..aeb72385515c4 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -429,53 +429,45 @@ AggExprEmitter::VisitCXXStdInitializerListExpr(CXXStdInitializerListExpr *E) { Ctx.getAsConstantArrayType(E->getSubExpr()->getType()); assert(ArrayType && "std::initializer_list constructed from non-array"); - // FIXME: Perform the checks on the field types in SemaInit. RecordDecl *Record = E->getType()->castAs<RecordType>()->getDecl(); RecordDecl::field_iterator Field = Record->field_begin(); - if (Field == Record->field_end()) { - CGF.ErrorUnsupported(E, "weird std::initializer_list"); - return; - } + assert(Field != Record->field_end() && + Ctx.hasSameType(Field->getType()->getPointeeType(), + ArrayType->getElementType()) && + "Expected std::initializer_list first field to be const E *"); // Start pointer. - if (!Field->getType()->isPointerType() || - !Ctx.hasSameType(Field->getType()->getPointeeType(), - ArrayType->getElementType())) { - CGF.ErrorUnsupported(E, "weird std::initializer_list"); - return; - } - AggValueSlot Dest = EnsureSlot(E->getType()); LValue DestLV = CGF.MakeAddrLValue(Dest.getAddress(), E->getType()); LValue Start = CGF.EmitLValueForFieldInitialization(DestLV, *Field); llvm::Value *ArrayStart = ArrayPtr.emitRawPointer(CGF); CGF.EmitStoreThroughLValue(RValue::get(ArrayStart), Start); ++Field; - - if (Field == Record->field_end()) { - CGF.ErrorUnsupported(E, "weird std::initializer_list"); - return; - } + assert(Field != Record->field_end() && + "Expected std::initializer_list to have two fields"); llvm::Value *Size = Builder.getInt(ArrayType->getSize()); LValue EndOrLength = CGF.EmitLValueForFieldInitialization(DestLV, *Field); - if (Field->getType()->isPointerType() && - Ctx.hasSameType(Field->getType()->getPointeeType(), - ArrayType->getElementType())) { + if (Ctx.hasSameType(Field->getType(), Ctx.getSizeType())) { + // Length. + CGF.EmitStoreThroughLValue(RValue::get(Size), EndOrLength); + + } else { // End pointer. + assert(Field->getType()->isPointerType() && + Ctx.hasSameType(Field->getType()->getPointeeType(), + ArrayType->getElementType()) && + "Expected std::initializer_list second field to be const E *"); llvm::Value *Zero = llvm::ConstantInt::get(CGF.PtrDiffTy, 0); llvm::Value *IdxEnd[] = { Zero, Size }; llvm::Value *ArrayEnd = Builder.CreateInBoundsGEP( ArrayPtr.getElementType(), ArrayPtr.emitRawPointer(CGF), IdxEnd, "arrayend"); CGF.EmitStoreThroughLValue(RValue::get(ArrayEnd), EndOrLength); - } else if (Ctx.hasSameType(Field->getType(), Ctx.getSizeType())) { - // Length. - CGF.EmitStoreThroughLValue(RValue::get(Size), EndOrLength); - } else { - CGF.ErrorUnsupported(E, "weird std::initializer_list"); - return; } + + assert(++Field == Record->field_end() && + "Expected std::initializer_list to only have two fields"); } /// Determine if E is a trivial array filler, that is, one that is diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index e805834c0fd38..404495f4cdfd3 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -9392,6 +9392,57 @@ ExprResult InitializationSequence::Perform(Sema &S, // Wrap it in a construction of a std::initializer_list<T>. CurInit = new (S.Context) CXXStdInitializerListExpr(Step->Type, MTE); + if (!Step->Type->isDependentType()) { + assert(S.isCompleteType(CurInit.get()->getExprLoc(), Step->Type, + Sema::CompleteTypeKind::Normal) && + "std::initializer_list<E> incomplete when used during " + "initialization"); + QualType ElementType; + [[maybe_unused]] bool IsStdInitializerList = + S.isStdInitializerList(Step->Type, &ElementType); + assert(IsStdInitializerList && + "StdInitializerList step to non-std::initializer_list"); + RecordDecl *Record = Step->Type->castAs<RecordType>()->getDecl(); + + auto InvalidType = [&] { + S.Diag(Record->getLocation(), + diag::err_std_initializer_list_malformed) + << Step->Type.getUnqualifiedType(); + return ExprError(); + }; + + // FIXME: What if the initializer_list type has base classes, etc? + if (Record->isUnion()) + return InvalidType(); + + RecordDecl::field_iterator Field = Record->field_begin(); + if (Field == Record->field_end()) + return InvalidType(); + + // Start pointer + if (!Field->getType()->isPointerType() || + !S.Context.hasSameType(Field->getType()->getPointeeType(), + ElementType.withConst())) + return InvalidType(); + + if (++Field == Record->field_end()) + return InvalidType(); + + // Size or end pointer + if (Field->getType()->isPointerType()) { + if (!S.Context.hasSameType(Field->getType()->getPointeeType(), + ElementType.withConst())) + return InvalidType(); + } else { + if (Field->isUnnamedBitField() || + !S.Context.hasSameType(Field->getType(), S.Context.getSizeType())) + return InvalidType(); + } + + if (++Field != Record->field_end()) + return InvalidType(); + } + // Bind the result, in case the library has given initializer_list a // non-trivial destructor. if (shouldBindAsTemporary(Entity)) diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p4.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p4.cpp index bf1b3092e08e8..cad42014802e7 100644 --- a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p4.cpp +++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p4.cpp @@ -43,7 +43,7 @@ struct S { const int S::b; const auto S::c = 0; -namespace std { template<typename T> struct initializer_list { initializer_list(); }; } +namespace std { template<typename T> struct initializer_list { const T *a, *b; initializer_list(); }; } // In an initializer of the form ( expression-list ), the expression-list // shall be a single assigment-expression. diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p7-cxx14.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p7-cxx14.cpp index 97e860f91dcd3..89fa6ec670a65 100644 --- a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p7-cxx14.cpp +++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p7-cxx14.cpp @@ -4,9 +4,7 @@ namespace std { template<typename T> struct initializer_list { - const T *p; - unsigned long n; - initializer_list(const T *p, unsigned long n); + const T *a, *b; }; } diff --git a/clang/test/CodeCompletion/ctor-signature.cpp b/clang/test/CodeCompletion/ctor-signature.cpp index d9bb2e566c51b..556fc4db0136f 100644 --- a/clang/test/CodeCompletion/ctor-signature.cpp +++ b/clang/test/CodeCompletion/ctor-signature.cpp @@ -17,7 +17,7 @@ void foo() { } namespace std { -template <typename> struct initializer_list {}; +template <typename E> struct initializer_list { const E *a, *b; }; } // namespace std struct Bar { diff --git a/clang/test/Coverage/unresolved-ctor-expr.cpp b/clang/test/Coverage/unresolved-ctor-expr.cpp index 10286c79f569d..2c57320949f7b 100644 --- a/clang/test/Coverage/unresolved-ctor-expr.cpp +++ b/clang/test/Coverage/unresolved-ctor-expr.cpp @@ -4,7 +4,7 @@ // GH62105 demonstrated a crash with this example code when calculating // coverage mapping because some source location information was being dropped. // Demonstrate that we do not crash on this code. -namespace std { template <typename> class initializer_list {}; } +namespace std { template <typename E> class initializer_list { const E *a, *b; }; } template <typename> struct T { T(std::initializer_list<int>, int = int()); diff --git a/clang/test/Modules/Inputs/initializer_list/direct.h b/clang/test/Modules/Inputs/initializer_list/direct.h index 6058f803a3dde..6f3978ef13284 100644 --- a/clang/test/Modules/Inputs/initializer_list/direct.h +++ b/clang/test/Modules/Inputs/initializer_list/direct.h @@ -2,7 +2,7 @@ namespace std { using size_t = decltype(sizeof(0)); template<typename T> struct initializer_list { - initializer_list(T*, size_t); + const T* ptr; size_t sz; }; template<typename T> int min(initializer_list<T>); diff --git a/clang/test/Modules/pr60775.cppm b/clang/test/Modules/pr60775.cppm index 35eb92512f427..76aec48808867 100644 --- a/clang/test/Modules/pr60775.cppm +++ b/clang/test/Modules/pr60775.cppm @@ -29,9 +29,10 @@ namespace std { typedef decltype(sizeof(int)) size_t; template<typename T> struct initializer_list { + const T* ptr; size_t sz; initializer_list(const T *, size_t); - T* begin(); - T* end(); + const T* begin(); + const T* end(); }; } diff --git a/clang/test/OpenMP/declare_reduction_codegen_in_templates.cpp b/clang/test/OpenMP/declare_reduction_codegen_in_templates.cpp index 7689cfc11f627..76c5675c83856 100644 --- a/clang/test/OpenMP/declare_reduction_codegen_in_templates.cpp +++ b/clang/test/OpenMP/declare_reduction_codegen_in_templates.cpp @@ -16,7 +16,7 @@ #ifndef HEADER #define HEADER -typedef long unsigned a; +typedef decltype(sizeof 0) a; namespace std { template <class> class initializer_list { const int *b; diff --git a/clang/test/Preprocessor/macro_with_initializer_list.cpp b/clang/test/Preprocessor/macro_with_initializer_list.cpp index 287eeb4a843cb..40f53164b263d 100644 --- a/clang/test/Preprocessor/macro_with_initializer_list.cpp +++ b/clang/test/Preprocessor/macro_with_initializer_list.cpp @@ -3,7 +3,7 @@ namespace std { template <class X> class initializer_list { - public: + public: const X *a, *b; initializer_list(); }; } diff --git a/clang/test/SemaCXX/PR20334-std_initializer_list_diagnosis_assertion.cpp b/clang/test/SemaCXX/PR20334-std_initializer_list_diagnosis_assertion.cpp index fb1feee01b29f..0df5d2e7f0aec 100644 --- a/clang/test/SemaCXX/PR20334-std_initializer_list_diagnosis_assertion.cpp +++ b/clang/test/SemaCXX/PR20334-std_initializer_list_diagnosis_assertion.cpp @@ -1,23 +1,18 @@ -// RUN: %clang_cc1 -std=c++11 -verify -emit-llvm-only %s -// RUN: %clang_cc1 -std=c++98 -fsyntax-only -verify %s -DCPP98 -// RUN: %clang_cc1 -std=c++11 -verify -emit-llvm-only %s -fexperimental-new-constant-interpreter -// RUN: %clang_cc1 -std=c++98 -fsyntax-only -verify %s -DCPP98 -fexperimental-new-constant-interpreter +// RUN: %clang_cc1 -std=c++11 -verify=cxx11 -emit-llvm-only %s +// RUN: %clang_cc1 -std=c++98 -fsyntax-only -verify=cxx98 %s -DCPP98 +// RUN: %clang_cc1 -std=c++11 -verify=cxx11 -emit-llvm-only %s -fexperimental-new-constant-interpreter +// RUN: %clang_cc1 -std=c++98 -fsyntax-only -verify=cxx98 %s -DCPP98 -fexperimental-new-constant-interpreter namespace std { template <class _E> class initializer_list {}; + // cxx11-error@-2 {{'std::initializer_list<int>' layout not recognized}} } template<class E> int f(std::initializer_list<E> il); int F = f({1, 2, 3}); -#ifdef CPP98 -//expected-error@-2{{expected expression}} -#else -//expected-error@-4{{cannot compile}} -#endif - - +// cxx98-error@-1 {{expected expression}} diff --git a/clang/test/SemaCXX/auto-invalid-init-crash.cpp b/clang/test/SemaCXX/auto-invalid-init-crash.cpp index f727473dd6085..ec921a4286aa2 100644 --- a/clang/test/SemaCXX/auto-invalid-init-crash.cpp +++ b/clang/test/SemaCXX/auto-invalid-init-crash.cpp @@ -1,8 +1,8 @@ // RUN: %clang_cc1 -std=c++11 -fsyntax-only -fno-recovery-ast -verify %s namespace std { -template <typename> -class initializer_list{}; +template <typename E> +class initializer_list { const E *a, *b; }; int a; auto c = a, &d = {a}; // expected-error {{'auto' deduced as 'int'}} \ expected-error {{non-const lvalue reference to type}} diff --git a/clang/test/SemaCXX/cxx0x-initializer-stdinitializerlist.cpp b/clang/test/SemaCXX/cxx0x-initializer-stdinitializerlist.cpp index 143d93cefc7c4..5a469e1ba6725 100644 --- a/clang/test/SemaCXX/cxx0x-initializer-stdinitializerlist.cpp +++ b/clang/test/SemaCXX/cxx0x-initializer-stdinitializerlist.cpp @@ -1,5 +1,18 @@ // RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -DUNION_TEST -verify %s +#ifdef UNION_TEST +namespace std { + template<class E> + union initializer_list { + // expected-error@-1 {{'std::initializer_list<int>' layout not recognized.}} + const E* begin; + decltype(sizeof 0) size; + }; + + auto x = { 1, 2, 3 }; +}; +#else // This must obviously come before the definition of std::initializer_list. void missing_initializerlist() { auto l = {1, 2, 3, 4}; // expected-error {{std::initializer_list was not found}} @@ -367,14 +380,29 @@ namespace designated_init { } namespace weird_initlist { + template<int> struct weird {}; } -template<> struct std::initializer_list<weird_initlist::weird> { int a, b, c; }; +template<> struct std::initializer_list<weird_initlist::weird<0>> { int a, b, c; }; +// expected-error@-1 2 {{'std::initializer_list<weird<0>>' layout not recognized}} +template<> struct std::initializer_list<weird_initlist::weird<1>> { std::size_t sz; const weird_initlist::weird<1>* p; }; +// expected-error@-1 {{'std::initializer_list<weird<1>>' layout not recognized}} +template<> struct std::initializer_list<weird_initlist::weird<2>> { const weird_initlist::weird<2>* first; const weird_initlist::weird<2>* last; }; +template<> struct std::initializer_list<weird_initlist::weird<3>> { weird_initlist::weird<3>* p; std::size_t sz; }; +// expected-error@-1 {{'std::initializer_list<weird<3>>' layout not recognized}} +template<> struct std::initializer_list<weird_initlist::weird<4>> { const weird_initlist::weird<3>& p; std::size_t sz; }; +// expected-error@-1 {{'std::initializer_list<weird<4>>' layout not recognized}} +template<> struct std::initializer_list<weird_initlist::weird<5>> { const weird_initlist::weird<3>* p; std::size_t : sizeof(std::size_t) * __CHAR_BIT__; }; +// expected-error@-1 {{'std::initializer_list<weird<5>>' layout not recognized}} namespace weird_initlist { - // We don't check the struct layout in Sema. - auto x = {weird{}, weird{}, weird{}, weird{}, weird{}}; - // ... but we do in constant evaluation. - constexpr auto y = {weird{}, weird{}, weird{}, weird{}, weird{}}; // expected-error {{constant}} expected-note {{type 'const std::initializer_list<weird>' has unexpected layout}} + auto _0 = {weird<0>{}, weird<0>{}, weird<0>{}, weird<0>{}, weird<0>{}}; + constexpr auto _00 = {weird<0>{}, weird<0>{}, weird<0>{}, weird<0>{}, weird<0>{}}; + auto _1 = {weird<1>{}, weird<1>{}}; + constexpr auto _2 = {weird<2>{}, weird<2>{}, weird<2>{}}; // (Two pointer representation is supported) + static_assert(_2.first + 3 == _2.last, ""); + auto _3 = {weird<3>{}, weird<3>{}}; + auto _4 = {weird<4>{}, weird<4>{}}; + auto _5 = {weird<5>{}, weird<5>{}}; } auto v = std::initializer_list<int>{1,2,3}; // expected-warning {{array backing local initializer list 'v' will be destroyed at the end of the full-expression}} @@ -386,3 +414,4 @@ std::initializer_list<int> get(int cond) { return {1, 2, 3}; // expected-warning {{returning address of local temporary object}} return std::initializer_list<int>{1, 2, 3}; // expected-warning {{returning address of local temporary object}} } +#endif // UNION_TEST diff --git a/clang/test/SemaCXX/cxx11-call-to-deleted-constructor.cpp b/clang/test/SemaCXX/cxx11-call-to-deleted-constructor.cpp index d77cebdeba500..86738ab10b4cc 100644 --- a/clang/test/SemaCXX/cxx11-call-to-deleted-constructor.cpp +++ b/clang/test/SemaCXX/cxx11-call-to-deleted-constructor.cpp @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s namespace std { -template<class _Ep> class initializer_list { }; +template<class _Ep> class initializer_list { const _Ep *a, *b; }; } namespace cva { diff --git a/clang/test/SemaCXX/cxx98-compat.cpp b/clang/test/SemaCXX/cxx98-compat.cpp index b31bee672bbe3..3ce69d6908c5b 100644 --- a/clang/test/SemaCXX/cxx98-compat.cpp +++ b/clang/test/SemaCXX/cxx98-compat.cpp @@ -6,11 +6,11 @@ namespace std { struct type_info; using size_t = decltype(sizeof(0)); // expected-warning {{decltype}} expected-warning {{alias}} template<typename T> struct initializer_list { - initializer_list(T*, size_t); - T *p; + initializer_list(const T*, size_t); + const T *p; size_t n; - T *begin(); - T *end(); + const T *begin(); + const T *end(); }; } diff --git a/clang/test/SemaCXX/invalid-member-expr.cpp b/clang/test/SemaCXX/invalid-member-expr.cpp index 6ef33ea575a12..235db185c9330 100644 --- a/clang/test/SemaCXX/invalid-member-expr.cpp +++ b/clang/test/SemaCXX/invalid-member-expr.cpp @@ -26,7 +26,7 @@ void test2() { // PR6327 namespace test3 { template <class A, class B> struct pair {}; - template <class _E> class initializer_list {}; + template <class _E> class initializer_list { const _E *a, *b; }; template <typename _Tp> pair<_Tp, _Tp> minmax(initializer_list<_Tp> __l) {}; void test0() { diff --git a/clang/test/SemaTemplate/instantiate-init.cpp b/clang/test/SemaTemplate/instantiate-init.cpp index 6db33a972f86d..5fc3e83114e28 100644 --- a/clang/test/SemaTemplate/instantiate-init.cpp +++ b/clang/test/SemaTemplate/instantiate-init.cpp @@ -2,9 +2,9 @@ namespace std { template<typename T> struct initializer_list { - T *p; + const T *p; __SIZE_TYPE__ n; - initializer_list(T*, __SIZE_TYPE__); + initializer_list(const T*, __SIZE_TYPE__); }; } diff --git a/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp index 2e42b85808953..f2eaf19d61402 100644 --- a/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp @@ -1427,8 +1427,8 @@ TEST_P(ASTMatchersTest, return; } StringRef code = "namespace std {" - "template <typename> class initializer_list {" - " public: initializer_list() noexcept {}" + "template <typename E> class initializer_list {" + " public: const E *a, *b;" "};" "}" "struct A {" diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp index 2a74d7fa63fd7..cfbc64c77b0cc 100644 --- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp @@ -3265,7 +3265,7 @@ TEST(TransferTest, ResultObjectLocationForStdInitializerListExpr) { std::string Code = R"( namespace std { template <typename T> - struct initializer_list {}; + struct initializer_list { const T *a, *b; }; } // namespace std void target() { diff --git a/clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp b/clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp index f16472ef17141..53dd223f06b5e 100644 --- a/clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp @@ -552,6 +552,7 @@ namespace std { template <typename T> class initializer_list { public: + const T *a, *b; initializer_list() noexcept; }; >From 326ceb67dd0faa49407943dcba5fd74743f17f93 Mon Sep 17 00:00:00 2001 From: Mital Ashok <mi...@mitalashok.co.uk> Date: Fri, 14 Jun 2024 20:57:14 +0100 Subject: [PATCH 2/5] Fix some test failures --- clang-tools-extra/clangd/unittests/ASTTests.cpp | 2 +- clang-tools-extra/clangd/unittests/XRefsTests.cpp | 2 +- clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp | 2 +- .../checkers/performance/inefficient-vector-operation.cpp | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/clang-tools-extra/clangd/unittests/ASTTests.cpp b/clang-tools-extra/clangd/unittests/ASTTests.cpp index 3101bf34acd71..32c8e8a63a215 100644 --- a/clang-tools-extra/clangd/unittests/ASTTests.cpp +++ b/clang-tools-extra/clangd/unittests/ASTTests.cpp @@ -80,7 +80,7 @@ TEST(GetDeducedType, KwAutoKwDecltypeExpansion) { namespace std { template<class _E> - class [[initializer_list]] {}; + class [[initializer_list]] { const _E *a, *b; }; } ^auto i = {1,2}; diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp b/clang-tools-extra/clangd/unittests/XRefsTests.cpp index cbceb9a343f87..d393c72974d44 100644 --- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp +++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp @@ -771,7 +771,7 @@ TEST(LocateSymbol, All) { namespace std { template<class _E> - class [[initializer_list]] {}; + class [[initializer_list]] { const _E *a, *b; }; } ^auto i = {1,2}; diff --git a/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp b/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp index dac3f39e1a658..282abce3246ca 100644 --- a/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp +++ b/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp @@ -534,7 +534,7 @@ TEST(WalkAST, Enums) { TEST(WalkAST, InitializerList) { testWalk(R"cpp( namespace std { - template <typename T> struct $implicit^initializer_list {}; + template <typename T> struct $implicit^initializer_list { const T *a, *b; }; })cpp", R"cpp( const char* s = ""; diff --git a/clang-tools-extra/test/clang-tidy/checkers/performance/inefficient-vector-operation.cpp b/clang-tools-extra/test/clang-tidy/checkers/performance/inefficient-vector-operation.cpp index c295f48f89aed..0810d73a0f2d9 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/performance/inefficient-vector-operation.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/performance/inefficient-vector-operation.cpp @@ -5,7 +5,8 @@ namespace std { -typedef decltype(sizeof 0) size_t; +// FIXME: This doesn't work if size_t is not a signed type +typedef int size_t; template<class E> class initializer_list { public: @@ -15,8 +16,7 @@ template<class E> class initializer_list { using size_type = size_t; using iterator = const E*; using const_iterator = const E*; - iterator ptr; - size_type sz; + iterator first, last; initializer_list(); size_t size() const; // number of elements const E* begin() const; // first element >From b66421724d1c3e22f363cba7e499a155adbd96bd Mon Sep 17 00:00:00 2001 From: Mital Ashok <mi...@mitalashok.co.uk> Date: Wed, 19 Jun 2024 16:05:23 +0100 Subject: [PATCH 3/5] Remove fixme Fixed by #95596 --- .../checkers/performance/inefficient-vector-operation.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clang-tools-extra/test/clang-tidy/checkers/performance/inefficient-vector-operation.cpp b/clang-tools-extra/test/clang-tidy/checkers/performance/inefficient-vector-operation.cpp index 5685bd58ee9e8..e1e25d76d4909 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/performance/inefficient-vector-operation.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/performance/inefficient-vector-operation.cpp @@ -5,8 +5,7 @@ namespace std { -// FIXME: This doesn't work if size_t is not a signed type -typedef int size_t; +typedef decltype(sizeof 0) size_t; template<class E> class initializer_list { public: @@ -16,7 +15,8 @@ template<class E> class initializer_list { using size_type = size_t; using iterator = const E*; using const_iterator = const E*; - iterator first, last; + iterator p; + size_t sz; initializer_list(); size_t size() const; // number of elements const E* begin() const; // first element >From ed950592d1b878e44838c7aaccafb53a54e185c8 Mon Sep 17 00:00:00 2001 From: Mital Ashok <mi...@mitalashok.co.uk> Date: Wed, 19 Jun 2024 16:35:00 +0100 Subject: [PATCH 4/5] Check for polymorphic types and bases --- .../clang/Basic/DiagnosticSemaKinds.td | 2 +- clang/lib/Sema/SemaInit.cpp | 20 ++++++------- ...d_initializer_list_diagnosis_assertion.cpp | 9 +++--- .../cxx0x-initializer-stdinitializerlist.cpp | 30 +++++++++++++++---- 4 files changed, 40 insertions(+), 21 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index bebfff78bd9bf..4b6bba5f2cced 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -12210,7 +12210,7 @@ def err_std_source_location_impl_malformed : Error< "'std::source_location::__impl' must be standard-layout and have only two 'const char *' fields '_M_file_name' and '_M_function_name', and two integral fields '_M_line' and '_M_column'">; def err_std_initializer_list_malformed : Error< - "%0 layout not recognized. Must be a struct with two fields, a 'const E *' and either another 'const E *' or a 'std::size_t'">; + "%0 layout not recognized. Must be a non-polymorphic class type with no bases and two fields: a 'const E *' and either another 'const E *' or a 'std::size_t'">; // HLSL Diagnostics def err_hlsl_attr_unsupported_in_stage : Error<"attribute %0 is unsupported in '%1' shaders, requires %select{|one of the following: }2%3">; diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 9c47d523b2231..85d8346992549 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -9393,16 +9393,16 @@ ExprResult InitializationSequence::Perform(Sema &S, CurInit = new (S.Context) CXXStdInitializerListExpr(Step->Type, MTE); if (!Step->Type->isDependentType()) { - assert(S.isCompleteType(CurInit.get()->getExprLoc(), Step->Type, - Sema::CompleteTypeKind::Normal) && - "std::initializer_list<E> incomplete when used during " - "initialization"); QualType ElementType; [[maybe_unused]] bool IsStdInitializerList = S.isStdInitializerList(Step->Type, &ElementType); assert(IsStdInitializerList && "StdInitializerList step to non-std::initializer_list"); - RecordDecl *Record = Step->Type->castAs<RecordType>()->getDecl(); + const CXXRecordDecl *Record = + Step->Type->getAsCXXRecordDecl()->getDefinition(); + assert(Record && Record->isCompleteDefinition() && + "std::initializer_list should have already be " + "complete/instantiated by this point"); auto InvalidType = [&] { S.Diag(Record->getLocation(), @@ -9411,8 +9411,8 @@ ExprResult InitializationSequence::Perform(Sema &S, return ExprError(); }; - // FIXME: What if the initializer_list type has base classes, etc? - if (Record->isUnion()) + if (Record->isUnion() || Record->getNumBases() != 0 || + Record->isPolymorphic()) return InvalidType(); RecordDecl::field_iterator Field = Record->field_begin(); @@ -9429,12 +9429,12 @@ ExprResult InitializationSequence::Perform(Sema &S, return InvalidType(); // Size or end pointer - if (Field->getType()->isPointerType()) { - if (!S.Context.hasSameType(Field->getType()->getPointeeType(), + if (const auto *PT = Field->getType()->getAs<PointerType>()) { + if (!S.Context.hasSameType(PT->getPointeeType(), ElementType.withConst())) return InvalidType(); } else { - if (Field->isUnnamedBitField() || + if (Field->isBitField() || !S.Context.hasSameType(Field->getType(), S.Context.getSizeType())) return InvalidType(); } diff --git a/clang/test/SemaCXX/PR20334-std_initializer_list_diagnosis_assertion.cpp b/clang/test/SemaCXX/PR20334-std_initializer_list_diagnosis_assertion.cpp index 0df5d2e7f0aec..fa8cbe2795b6b 100644 --- a/clang/test/SemaCXX/PR20334-std_initializer_list_diagnosis_assertion.cpp +++ b/clang/test/SemaCXX/PR20334-std_initializer_list_diagnosis_assertion.cpp @@ -1,14 +1,13 @@ // RUN: %clang_cc1 -std=c++11 -verify=cxx11 -emit-llvm-only %s -// RUN: %clang_cc1 -std=c++98 -fsyntax-only -verify=cxx98 %s -DCPP98 +// RUN: %clang_cc1 -std=c++98 -fsyntax-only -verify=cxx98 %s // RUN: %clang_cc1 -std=c++11 -verify=cxx11 -emit-llvm-only %s -fexperimental-new-constant-interpreter -// RUN: %clang_cc1 -std=c++98 -fsyntax-only -verify=cxx98 %s -DCPP98 -fexperimental-new-constant-interpreter +// RUN: %clang_cc1 -std=c++98 -fsyntax-only -verify=cxx98 %s -fexperimental-new-constant-interpreter namespace std { template <class _E> - class initializer_list - {}; - // cxx11-error@-2 {{'std::initializer_list<int>' layout not recognized}} + class initializer_list {}; + // cxx11-error@-1 {{'std::initializer_list<int>' layout not recognized. Must be a non-polymorphic class type with no bases and two fields: a 'const E *' and either another 'const E *' or a 'std::size_t'}} } template<class E> int f(std::initializer_list<E> il); diff --git a/clang/test/SemaCXX/cxx0x-initializer-stdinitializerlist.cpp b/clang/test/SemaCXX/cxx0x-initializer-stdinitializerlist.cpp index 5a469e1ba6725..74aa7cb2abe83 100644 --- a/clang/test/SemaCXX/cxx0x-initializer-stdinitializerlist.cpp +++ b/clang/test/SemaCXX/cxx0x-initializer-stdinitializerlist.cpp @@ -390,19 +390,39 @@ template<> struct std::initializer_list<weird_initlist::weird<1>> { std::size_t template<> struct std::initializer_list<weird_initlist::weird<2>> { const weird_initlist::weird<2>* first; const weird_initlist::weird<2>* last; }; template<> struct std::initializer_list<weird_initlist::weird<3>> { weird_initlist::weird<3>* p; std::size_t sz; }; // expected-error@-1 {{'std::initializer_list<weird<3>>' layout not recognized}} -template<> struct std::initializer_list<weird_initlist::weird<4>> { const weird_initlist::weird<3>& p; std::size_t sz; }; +template<> struct std::initializer_list<weird_initlist::weird<4>> { const weird_initlist::weird<4>& p; std::size_t sz; }; // expected-error@-1 {{'std::initializer_list<weird<4>>' layout not recognized}} -template<> struct std::initializer_list<weird_initlist::weird<5>> { const weird_initlist::weird<3>* p; std::size_t : sizeof(std::size_t) * __CHAR_BIT__; }; +template<> struct std::initializer_list<weird_initlist::weird<5>> { const weird_initlist::weird<5>* p; std::size_t : sizeof(std::size_t) * __CHAR_BIT__; }; // expected-error@-1 {{'std::initializer_list<weird<5>>' layout not recognized}} +template<> struct std::initializer_list<weird_initlist::weird<6>> { const weird_initlist::weird<6>* p; std::size_t sz : sizeof(std::size_t) * __CHAR_BIT__; }; +// expected-error@-1 {{'std::initializer_list<weird<6>>' layout not recognized}} +struct empty_base {}; +template<> struct std::initializer_list<weird_initlist::weird<7>> : empty_base { const weird_initlist::weird<7>* p; std::size_t sz; }; +// expected-error@-1 {{'std::initializer_list<weird<7>>' layout not recognized}} +template<> struct std::initializer_list<weird_initlist::weird<8>> { const weird_initlist::weird<8>* p; std::size_t sz; ~initializer_list(); }; +template<> struct std::initializer_list<weird_initlist::weird<9>> { const weird_initlist::weird<9>* p; std::size_t sz; virtual void f(); }; +// expected-error@-1 {{'std::initializer_list<weird<9>>' layout not recognized}} +template<> struct std::initializer_list<weird_initlist::weird<10>> { const weird_initlist::weird<10>* p; alignas(64) std::size_t sz; }; +template<> struct std::initializer_list<weird_initlist::weird<11>>; +// expected-note@-1 {{forward declaration of 'std::initializer_list<weird_initlist::weird<11>>'}} namespace weird_initlist { auto _0 = {weird<0>{}, weird<0>{}, weird<0>{}, weird<0>{}, weird<0>{}}; - constexpr auto _00 = {weird<0>{}, weird<0>{}, weird<0>{}, weird<0>{}, weird<0>{}}; + constexpr auto _0c = {weird<0>{}, weird<0>{}, weird<0>{}, weird<0>{}, weird<0>{}}; auto _1 = {weird<1>{}, weird<1>{}}; - constexpr auto _2 = {weird<2>{}, weird<2>{}, weird<2>{}}; // (Two pointer representation is supported) - static_assert(_2.first + 3 == _2.last, ""); + auto _2 = {weird<2>{}, weird<2>{}, weird<2>{}}; + constexpr auto _2c = {weird<2>{}, weird<2>{}, weird<2>{}}; // (Two pointer representation is supported) + static_assert(_2c.first + 3 == _2c.last, ""); auto _3 = {weird<3>{}, weird<3>{}}; auto _4 = {weird<4>{}, weird<4>{}}; auto _5 = {weird<5>{}, weird<5>{}}; + auto _6 = {weird<6>{}, weird<6>{}}; + auto _7 = {weird<7>{}, weird<7>{}}; + auto _8 = {weird<8>{}, weird<8>{}}; + auto _9 = {weird<9>{}, weird<9>{}}; + auto _10 = {weird<10>{}, weird<10>{}}; + constexpr auto _10c = {weird<10>{}, weird<10>{}, weird<10>{}}; + static_assert(_10c.sz == 3, ""); + const auto& _11 = {weird<11>{}, weird<11>{}}; // expected-error {{initialization of incomplete type 'const std::initializer_list<weird<11>>'}} } auto v = std::initializer_list<int>{1,2,3}; // expected-warning {{array backing local initializer list 'v' will be destroyed at the end of the full-expression}} >From 2ffa618d6a5202ce3bdf139bbe209ce9d765b291 Mon Sep 17 00:00:00 2001 From: Mital Ashok <mi...@mitalashok.co.uk> Date: Thu, 20 Jun 2024 16:45:13 +0100 Subject: [PATCH 5/5] Add releasenote --- clang/docs/ReleaseNotes.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 7112d1f889fef..783aefeba44f8 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -589,6 +589,10 @@ Improvements to Clang's diagnostics - Clang no longer emits a "declared here" note for a builtin function that has no declaration in source. Fixes #GH93369. +- Clang now diagnoses unsupported class declarations for ``std::initializer_list<E>`` when they are + used rather than when they are needed for constant evaluation or when code is generated for them. + The check is now stricter to prevent crashes for some unsupported declarations (Fixes #GH95495). + Improvements to Clang's time-trace ---------------------------------- _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits