https://github.com/alanzhao1 updated https://github.com/llvm/llvm-project/pull/76976
>From ee4e3c8634bb876166ee753a4ebcbf3c1699a175 Mon Sep 17 00:00:00 2001 From: Alan Zhao <ayz...@google.com> Date: Wed, 3 Jan 2024 12:29:21 -0800 Subject: [PATCH 1/9] [clang] Fix parenthesized list initialization of arrays not working with `new` This bug is caused by parenthesized list initialization not being implemented in `CodeGenFunction::EmitNewArrayInitializer(...)`. Parenthesized list initialization of `struct`s with `operator new` already works in Clang and is not affected by this bug. Additionally, fix the test new-delete.cpp as it incorrectly assumes that using parentheses with operator new to initialize arrays is illegal for C++ versions >= C++17. Fixes #68198 --- clang/docs/ReleaseNotes.rst | 3 + clang/lib/CodeGen/CGExprCXX.cpp | 17 ++--- clang/lib/Sema/SemaExprCXX.cpp | 6 +- clang/test/CodeGen/paren-list-agg-init.cpp | 72 ++++++++++++++++++++++ clang/test/SemaCXX/new-delete.cpp | 20 +++--- 5 files changed, 98 insertions(+), 20 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index c7bf162426a68c..211fd62a1ee85f 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -844,6 +844,9 @@ Bug Fixes to C++ Support - Fix crash when parsing nested requirement. Fixes: (`#73112 <https://github.com/llvm/llvm-project/issues/73112>`_) +- Clang now allows parenthesized initialization of arrays in `operator new[]`. + Fixes: (`#68198 <https://github.com/llvm/llvm-project/issues/68198>`_) + Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ - Fixed an import failure of recursive friend class template. diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index 98ae56e2df8818..72c61bfb5ec344 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -1038,11 +1038,13 @@ void CodeGenFunction::EmitNewArrayInitializer( return true; }; + const InitListExpr *ILE = dyn_cast<InitListExpr>(Init); + const CXXParenListInitExpr *CPLIE = dyn_cast<CXXParenListInitExpr>(Init); // If the initializer is an initializer list, first do the explicit elements. - if (const InitListExpr *ILE = dyn_cast<InitListExpr>(Init)) { + if (ILE || CPLIE) { // Initializing from a (braced) string literal is a special case; the init // list element does not initialize a (single) array element. - if (ILE->isStringLiteralInit()) { + if (ILE && ILE->isStringLiteralInit()) { // Initialize the initial portion of length equal to that of the string // literal. The allocation must be for at least this much; we emitted a // check for that earlier. @@ -1073,7 +1075,7 @@ void CodeGenFunction::EmitNewArrayInitializer( return; } - InitListElements = ILE->getNumInits(); + InitListElements = ILE ? ILE->getNumInits() : CPLIE->getInitExprs().size(); // If this is a multi-dimensional array new, we will initialize multiple // elements with each init list element. @@ -1101,7 +1103,8 @@ void CodeGenFunction::EmitNewArrayInitializer( } CharUnits StartAlign = CurPtr.getAlignment(); - for (unsigned i = 0, e = ILE->getNumInits(); i != e; ++i) { + ArrayRef<Expr *> InitExprs = ILE ? ILE->inits() : CPLIE->getInitExprs(); + for (unsigned i = 0; i < InitExprs.size(); ++i) { // Tell the cleanup that it needs to destroy up to this // element. TODO: some of these stores can be trivially // observed to be unnecessary. @@ -1111,8 +1114,8 @@ void CodeGenFunction::EmitNewArrayInitializer( // FIXME: If the last initializer is an incomplete initializer list for // an array, and we have an array filler, we can fold together the two // initialization loops. - StoreAnyExprIntoOneUnit(*this, ILE->getInit(i), - ILE->getInit(i)->getType(), CurPtr, + Expr *IE = InitExprs[i]; + StoreAnyExprIntoOneUnit(*this, IE, IE->getType(), CurPtr, AggValueSlot::DoesNotOverlap); CurPtr = Address(Builder.CreateInBoundsGEP( CurPtr.getElementType(), CurPtr.getPointer(), @@ -1122,7 +1125,7 @@ void CodeGenFunction::EmitNewArrayInitializer( } // The remaining elements are filled with the array filler expression. - Init = ILE->getArrayFiller(); + Init = ILE ? ILE->getArrayFiller() : CPLIE->getArrayFiller(); // Extract the initializer for the individual array elements by pulling // out the array filler from all the nested initializer lists. This avoids diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 4ae04358d5df7c..71e420648ce7af 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -1947,11 +1947,11 @@ Sema::ActOnCXXNew(SourceLocation StartLoc, bool UseGlobal, } static bool isLegalArrayNewInitializer(CXXNewInitializationStyle Style, - Expr *Init) { + Expr *Init, bool IsCPlusPlus20) { if (!Init) return true; if (ParenListExpr *PLE = dyn_cast<ParenListExpr>(Init)) - return PLE->getNumExprs() == 0; + return IsCPlusPlus20 || PLE->getNumExprs() == 0; if (isa<ImplicitValueInitExpr>(Init)) return true; else if (CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(Init)) @@ -2403,7 +2403,7 @@ ExprResult Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, // Array 'new' can't have any initializers except empty parentheses. // Initializer lists are also allowed, in C++11. Rely on the parser for the // dialect distinction. - if (ArraySize && !isLegalArrayNewInitializer(InitStyle, Initializer)) { + if (ArraySize && !isLegalArrayNewInitializer(InitStyle, Initializer, getLangOpts().CPlusPlus20)) { SourceRange InitRange(Exprs.front()->getBeginLoc(), Exprs.back()->getEndLoc()); Diag(StartLoc, diag::err_new_array_init_args) << InitRange; diff --git a/clang/test/CodeGen/paren-list-agg-init.cpp b/clang/test/CodeGen/paren-list-agg-init.cpp index 0e68beb5c37066..9618b6ddf14558 100644 --- a/clang/test/CodeGen/paren-list-agg-init.cpp +++ b/clang/test/CodeGen/paren-list-agg-init.cpp @@ -513,3 +513,75 @@ namespace gh61567 { I(0); } } + +namespace gh68198 { + // CHECK: define {{.*}} void @{{.*foo25.*}} { + // CHECK-NEXT: entry + // CHECK-NEXT: [[ARR_8:%.*arr8.*]] = alloca ptr, align 8 + // CHECK-NEXT: [[CALL_PTR:%.*]] = call noalias noundef nonnull ptr @_Znam(i64 noundef 8) + // CHECK-NEXT: store i32 1, ptr [[CALL_PTR]], align 4 + // CHECK-NEXT: [[ARRAY_EXP_NEXT:%.*]] = getelementptr inbounds i32, ptr [[CALL_PTR]], i64 1 + // CHECK-NEXT: store i32 2, ptr [[ARRAY_EXP_NEXT]], align 4 + // CHECK-NEXT: [[ARRAY_EXP_NEXT1:%.*]] = getelementptr inbounds i32, ptr [[ARRAY_EXP_NEXT]], i64 1 + // CHECK-NEXT: store ptr [[CALL_PTR]], ptr %arr8, align 8 + // CHECK-NEXT: ret void + void foo25() { + int* arr8 = new int[](1, 2); + } + + // CHECK: define {{.*}} void @{{.*foo26.*}} { + // CHECK-NEXT: entry + // CHECK-NEXT: [[ARR_9:%.*arr9.*]] = alloca ptr, align 8 + // CHECK-NEXT: [[CALL_PTR:%.*]] = call noalias noundef nonnull ptr @_Znam(i64 noundef 16) #7 + // CHECK-NEXT: store i32 1, ptr [[CALL_PTR]], align 4 + // CHECK-NEXT: [[ARRAY_EXP_NEXT:%.*]] = getelementptr inbounds i32, ptr [[CALL_PTR]], i64 1 + // CHECK-NEXT: store i32 2, ptr [[ARRAY_EXP_NEXT]], align 4 + // CHECK-NEXT: [[ARRAY_EXP_NEXT1:%.*]] = getelementptr inbounds i32, ptr [[ARRAY_EXP_NEXT]], i64 1 + // CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[ARRAY_EXP_NEXT1]], i8 0, i64 8, i1 false) + // CHECK-NEXT: store ptr [[CALL_PTR]], ptr [[ARR_9]], align 8 + // CHECK-NEXT: ret void + void foo26() { + int* arr9 = new int[4](1, 2); + } + + // CHECK: define {{.*}} void @{{.*foo27.*}} { + // CHECK-NEXT: entry + // CHECK-NEXT: [[ARR_10:%.*arr10.*]] = alloca ptr, align 8 + // CHECK-NEXT: [[CALL_PTR]] = call noalias noundef nonnull ptr @_Znam(i64 noundef 16) + // CHECK-NEXT: [[ARRAYINIT_BEGIN:%.*]] = getelementptr inbounds [2 x i32], ptr [[CALL]], i64 0, i64 0 + // CHECK-NEXT: store i32 1, ptr [[ARRAYINIT_BEGIN]], align 4 + // CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds i32, ptr [[ARRAYINIT_BEGIN]], i64 1 + // CHECK-NEXT: store i32 2, ptr [[ARRAYINIT_ELEMENT]], align 4 + // CHECK-NEXT: [[ARRAY_EXP_NEXT:%.*]] = getelementptr inbounds [2 x i32], ptr %call, i64 1 + // CHECK-NEXT: [[ARRAYINIT_BEGIN1:%.*]] = getelementptr inbounds [2 x i32], ptr [[ARRAY_EXP_NEXT]], i64 0, i64 0 + // CHECK-NEXT: store i32 3, ptr [[ARRAYINIT_BEGIN1]], align 4 + // CHECK-NEXT: [[ARRAYINIT_ELEMENT2:%.*]] = getelementptr inbounds i32, ptr [[ARRAYINIT_BEGIN1]], i64 1 + // CHECK-NEXT: store i32 4, ptr [[ARRAYINIT_ELEMENT2]], align 4 + // CHECK-NEXT: [[ARRAY_EXP_NEXT3:%.*]] = getelementptr inbounds [2 x i32], ptr [[ARRAY_EXP_NEXT]], i64 1 + // CHECK-NEXT: store ptr [[CALL_PTR]], ptr [[ARR_10]], align 8 + // CHECK-NEXT: ret void + void foo27() { + void* arr10 = new int[][2]({1, 2}, {3, 4}); + } + + // CHECK: define {{.*}} void @{{.*foo28.*}} { + // CHECK-NEXT: entry + // CHECK-NEXT: [[ARR_11:%.*arr11.*]] = alloca ptr, align 8 + // CHECK-NEXT: [[CALL_PTR]] = call noalias noundef nonnull ptr @_Znam(i64 noundef 32) + // CHECK-NEXT: [[ARRAYINIT_BEGIN:%.*]] = getelementptr inbounds [2 x i32], ptr [[CALL]], i64 0, i64 0 + // CHECK-NEXT: store i32 5, ptr [[ARRAYINIT_BEGIN]], align 4 + // CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds i32, ptr [[ARRAYINIT_BEGIN]], i64 1 + // CHECK-NEXT: store i32 6, ptr [[ARRAYINIT_ELEMENT]], align 4 + // CHECK-NEXT: [[ARRAY_EXP_NEXT:%.*]] = getelementptr inbounds [2 x i32], ptr %call, i64 1 + // CHECK-NEXT: [[ARRAYINIT_BEGIN1:%.*]] = getelementptr inbounds [2 x i32], ptr [[ARRAY_EXP_NEXT]], i64 0, i64 0 + // CHECK-NEXT: store i32 7, ptr [[ARRAYINIT_BEGIN1]], align 4 + // CHECK-NEXT: [[ARRAYINIT_ELEMENT2:%.*]] = getelementptr inbounds i32, ptr [[ARRAYINIT_BEGIN1]], i64 1 + // CHECK-NEXT: store i32 8, ptr [[ARRAYINIT_ELEMENT2]], align 4 + // CHECK-NEXT: [[ARRAY_EXP_NEXT3:%.*]] = getelementptr inbounds [2 x i32], ptr [[ARRAY_EXP_NEXT]], i64 1 + // CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[ARRAY_EXP_NEXT3]], i8 0, i64 16, i1 false) + // CHECK-NEXT: store ptr [[CALL_PTR]], ptr [[ARR_11]], align 8 + // CHECK-NEXT: ret void + void foo28() { + void* arr11 = new int[4][2]({5, 6}, {7, 8}); + } +} diff --git a/clang/test/SemaCXX/new-delete.cpp b/clang/test/SemaCXX/new-delete.cpp index 0270e42b7389fe..ec406260b6a391 100644 --- a/clang/test/SemaCXX/new-delete.cpp +++ b/clang/test/SemaCXX/new-delete.cpp @@ -1,7 +1,7 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s -triple=i686-pc-linux-gnu -Wno-new-returns-null -std=c++98 -// RUN: %clang_cc1 -fsyntax-only -verify %s -triple=i686-pc-linux-gnu -Wno-new-returns-null -std=c++11 -// RUN: %clang_cc1 -fsyntax-only -verify %s -triple=i686-pc-linux-gnu -Wno-new-returns-null -std=c++14 -// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx17 %s -triple=i686-pc-linux-gnu -Wno-new-returns-null %std_cxx17- +// RUN: %clang_cc1 -fsyntax-only -verify=expected,precxx20 %s -triple=i686-pc-linux-gnu -Wno-new-returns-null -std=c++11 +// RUN: %clang_cc1 -fsyntax-only -verify=expected,precxx20 %s -triple=i686-pc-linux-gnu -Wno-new-returns-null -std=c++14 +// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx17,precxx20 %s -triple=i686-pc-linux-gnu -Wno-new-returns-null -std=c++17 +// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx17 %s -triple=i686-pc-linux-gnu -Wno-new-returns-null -std=c++20 // FIXME Location is (frontend) // cxx17-note@*:* {{candidate function not viable: requires 2 arguments, but 3 were provided}} @@ -340,23 +340,23 @@ namespace PR5918 { // Look for template operator new overloads. namespace Test1 { void f() { - (void)new int[10](1, 2); // expected-error {{array 'new' cannot have initialization arguments}} + (void)new int[10](1, 2); // precxx20-error {{array 'new' cannot have initialization arguments}} typedef int T[10]; - (void)new T(1, 2); // expected-error {{array 'new' cannot have initialization arguments}} + (void)new T(1, 2); // precxx20-error {{array 'new' cannot have initialization arguments}} } template<typename T> void g(unsigned i) { - (void)new T[1](i); // expected-error {{array 'new' cannot have initialization arguments}} + (void)new T[1](i); // precxx20-error {{array 'new' cannot have initialization arguments}} } template<typename T> void h(unsigned i) { - (void)new T(i); // expected-error {{array 'new' cannot have initialization arguments}} + (void)new T(i); // precxx20-error {{array 'new' cannot have initialization arguments}} } template void h<unsigned>(unsigned); -template void h<unsigned[10]>(unsigned); // expected-note {{in instantiation of function template specialization 'Test1::h<unsigned int[10]>' requested here}} +template void h<unsigned[10]>(unsigned); // precxx20-note {{in instantiation of function template specialization 'Test1::h<unsigned int[10]>' requested here}} } @@ -556,7 +556,7 @@ namespace P12023 { int main() { - CopyCounter* f = new CopyCounter[10](CopyCounter()); // expected-error {{cannot have initialization arguments}} + CopyCounter* f = new CopyCounter[10](CopyCounter()); // precxx20-error {{cannot have initialization arguments}} return 0; } } >From ccdf700474cba4b77e2e0cf7029d673cca65840f Mon Sep 17 00:00:00 2001 From: Alan Zhao <ayz...@google.com> Date: Thu, 4 Jan 2024 09:06:43 -0800 Subject: [PATCH 2/9] fix accidentally deleted line in new-delete.cpp --- clang/test/SemaCXX/new-delete.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/test/SemaCXX/new-delete.cpp b/clang/test/SemaCXX/new-delete.cpp index ec406260b6a391..2db32938f957fb 100644 --- a/clang/test/SemaCXX/new-delete.cpp +++ b/clang/test/SemaCXX/new-delete.cpp @@ -1,3 +1,4 @@ +// RUN: %clang_cc1 -fsyntax-only -verify=expected,precxx20 %s -triple=i686-pc-linux-gnu -Wno-new-returns-null -std=c++98 // RUN: %clang_cc1 -fsyntax-only -verify=expected,precxx20 %s -triple=i686-pc-linux-gnu -Wno-new-returns-null -std=c++11 // RUN: %clang_cc1 -fsyntax-only -verify=expected,precxx20 %s -triple=i686-pc-linux-gnu -Wno-new-returns-null -std=c++14 // RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx17,precxx20 %s -triple=i686-pc-linux-gnu -Wno-new-returns-null -std=c++17 >From 1877e21590aa5c16481b78f1a88ccede5dac4194 Mon Sep 17 00:00:00 2001 From: Alan Zhao <ayz...@google.com> Date: Thu, 4 Jan 2024 09:08:44 -0800 Subject: [PATCH 3/9] fix clang format issue --- clang/lib/Sema/SemaExprCXX.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 71e420648ce7af..90e0dcb35af277 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -2403,7 +2403,8 @@ ExprResult Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, // Array 'new' can't have any initializers except empty parentheses. // Initializer lists are also allowed, in C++11. Rely on the parser for the // dialect distinction. - if (ArraySize && !isLegalArrayNewInitializer(InitStyle, Initializer, getLangOpts().CPlusPlus20)) { + if (ArraySize && !isLegalArrayNewInitializer(InitStyle, Initializer, + getLangOpts().CPlusPlus20)) { SourceRange InitRange(Exprs.front()->getBeginLoc(), Exprs.back()->getEndLoc()); Diag(StartLoc, diag::err_new_array_init_args) << InitRange; >From 5610f3018abbf8076c80c8800b89f429cb8d8fb4 Mon Sep 17 00:00:00 2001 From: Alan Zhao <ayz...@google.com> Date: Thu, 4 Jan 2024 11:17:42 -0800 Subject: [PATCH 4/9] use range-based for loop --- clang/lib/CodeGen/CGExprCXX.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index 72c61bfb5ec344..dd93bcfc57b93c 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -1104,7 +1104,8 @@ void CodeGenFunction::EmitNewArrayInitializer( CharUnits StartAlign = CurPtr.getAlignment(); ArrayRef<Expr *> InitExprs = ILE ? ILE->inits() : CPLIE->getInitExprs(); - for (unsigned i = 0; i < InitExprs.size(); ++i) { + unsigned i = 0; + for (Expr *IE : InitExprs) { // Tell the cleanup that it needs to destroy up to this // element. TODO: some of these stores can be trivially // observed to be unnecessary. @@ -1114,14 +1115,13 @@ void CodeGenFunction::EmitNewArrayInitializer( // FIXME: If the last initializer is an incomplete initializer list for // an array, and we have an array filler, we can fold together the two // initialization loops. - Expr *IE = InitExprs[i]; StoreAnyExprIntoOneUnit(*this, IE, IE->getType(), CurPtr, AggValueSlot::DoesNotOverlap); CurPtr = Address(Builder.CreateInBoundsGEP( CurPtr.getElementType(), CurPtr.getPointer(), Builder.getSize(1), "array.exp.next"), CurPtr.getElementType(), - StartAlign.alignmentAtOffset((i + 1) * ElementSize)); + StartAlign.alignmentAtOffset((++i) * ElementSize)); } // The remaining elements are filled with the array filler expression. >From 471ffa0c098e5c7a66e49567bc31d2de16cbbb67 Mon Sep 17 00:00:00 2001 From: Alan Zhao <ayz...@google.com> Date: Tue, 9 Jan 2024 13:53:24 -0800 Subject: [PATCH 5/9] fix initialization with string literals and dynamically sized arrays --- clang/lib/CodeGen/CGExprCXX.cpp | 32 ++++++---- clang/lib/Sema/SemaInit.cpp | 14 ++--- clang/test/CodeGen/paren-list-agg-init.cpp | 31 +++------- clang/test/CodeGenCXX/new-array-init.cpp | 72 +++++++++++++++++++++- clang/test/SemaCXX/new-delete.cpp | 12 +++- 5 files changed, 115 insertions(+), 46 deletions(-) diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index dd93bcfc57b93c..947553cb2ef2a3 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -1040,11 +1040,12 @@ void CodeGenFunction::EmitNewArrayInitializer( const InitListExpr *ILE = dyn_cast<InitListExpr>(Init); const CXXParenListInitExpr *CPLIE = dyn_cast<CXXParenListInitExpr>(Init); + const StringLiteral *SL = dyn_cast<StringLiteral>(Init); // If the initializer is an initializer list, first do the explicit elements. - if (ILE || CPLIE) { + if (ILE || CPLIE || SL) { // Initializing from a (braced) string literal is a special case; the init // list element does not initialize a (single) array element. - if (ILE && ILE->isStringLiteralInit()) { + if ((ILE && ILE->isStringLiteralInit()) || SL) { // Initialize the initial portion of length equal to that of the string // literal. The allocation must be for at least this much; we emitted a // check for that earlier. @@ -1056,12 +1057,12 @@ void CodeGenFunction::EmitNewArrayInitializer( AggValueSlot::DoesNotOverlap, AggValueSlot::IsNotZeroed, AggValueSlot::IsSanitizerChecked); - EmitAggExpr(ILE->getInit(0), Slot); + EmitAggExpr(SL ? SL : ILE->getInit(0), Slot); // Move past these elements. - InitListElements = - cast<ConstantArrayType>(ILE->getType()->getAsArrayTypeUnsafe()) - ->getSize().getZExtValue(); + const ArrayType *AT = SL ? SL->getType()->getAsArrayTypeUnsafe() + : ILE->getType()->getAsArrayTypeUnsafe(); + InitListElements = cast<ConstantArrayType>(AT)->getSize().getZExtValue(); CurPtr = Builder.CreateConstInBoundsGEP( CurPtr, InitListElements, "string.init.end"); @@ -1564,16 +1565,21 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) { // 1. Build a call to the allocation function. FunctionDecl *allocator = E->getOperatorNew(); - // If there is a brace-initializer, cannot allocate fewer elements than inits. + // If there is a brace-initializer or C++20 parenthesized initializer, cannot + // allocate fewer elements than inits. unsigned minElements = 0; if (E->isArray() && E->hasInitializer()) { - const InitListExpr *ILE = dyn_cast<InitListExpr>(E->getInitializer()); - if (ILE && ILE->isStringLiteralInit()) + const Expr *Init = E->getInitializer(); + const InitListExpr *ILE = dyn_cast<InitListExpr>(Init); + const CXXParenListInitExpr *CPLIE = dyn_cast<CXXParenListInitExpr>(Init); + if ((ILE && ILE->isStringLiteralInit()) || isa<StringLiteral>(Init)) { minElements = - cast<ConstantArrayType>(ILE->getType()->getAsArrayTypeUnsafe()) - ->getSize().getZExtValue(); - else if (ILE) - minElements = ILE->getNumInits(); + cast<ConstantArrayType>(Init->getType()->getAsArrayTypeUnsafe()) + ->getSize() + .getZExtValue(); + } else if (ILE || CPLIE) { + minElements = ILE ? ILE->getNumInits() : CPLIE->getInitExprs().size(); + } } llvm::Value *numElements = nullptr; diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 61d244f3bb9798..1fdebadb14e401 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -5495,14 +5495,12 @@ static void TryOrBuildParenListInitialization( return; } // ...and value-initialized for each k < i <= n; - if (ArrayLength > Args.size()) { - InitializedEntity SubEntity = InitializedEntity::InitializeElement( - S.getASTContext(), Args.size(), Entity); - InitializationKind SubKind = InitializationKind::CreateValue( - Kind.getLocation(), Kind.getLocation(), Kind.getLocation(), true); - if (!HandleInitializedEntity(SubEntity, SubKind, nullptr, &ArrayFiller)) - return; - } + InitializedEntity SubEntity = InitializedEntity::InitializeElement( + S.getASTContext(), Args.size(), Entity); + InitializationKind SubKind = InitializationKind::CreateValue( + Kind.getLocation(), Kind.getLocation(), Kind.getLocation(), true); + if (!HandleInitializedEntity(SubEntity, SubKind, nullptr, &ArrayFiller)) + return; if (ResultType.isNull()) { ResultType = S.Context.getConstantArrayType( diff --git a/clang/test/CodeGen/paren-list-agg-init.cpp b/clang/test/CodeGen/paren-list-agg-init.cpp index 9618b6ddf14558..70b7bbfed0168d 100644 --- a/clang/test/CodeGen/paren-list-agg-init.cpp +++ b/clang/test/CodeGen/paren-list-agg-init.cpp @@ -531,22 +531,7 @@ namespace gh68198 { // CHECK: define {{.*}} void @{{.*foo26.*}} { // CHECK-NEXT: entry - // CHECK-NEXT: [[ARR_9:%.*arr9.*]] = alloca ptr, align 8 - // CHECK-NEXT: [[CALL_PTR:%.*]] = call noalias noundef nonnull ptr @_Znam(i64 noundef 16) #7 - // CHECK-NEXT: store i32 1, ptr [[CALL_PTR]], align 4 - // CHECK-NEXT: [[ARRAY_EXP_NEXT:%.*]] = getelementptr inbounds i32, ptr [[CALL_PTR]], i64 1 - // CHECK-NEXT: store i32 2, ptr [[ARRAY_EXP_NEXT]], align 4 - // CHECK-NEXT: [[ARRAY_EXP_NEXT1:%.*]] = getelementptr inbounds i32, ptr [[ARRAY_EXP_NEXT]], i64 1 - // CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[ARRAY_EXP_NEXT1]], i8 0, i64 8, i1 false) - // CHECK-NEXT: store ptr [[CALL_PTR]], ptr [[ARR_9]], align 8 - // CHECK-NEXT: ret void - void foo26() { - int* arr9 = new int[4](1, 2); - } - - // CHECK: define {{.*}} void @{{.*foo27.*}} { - // CHECK-NEXT: entry - // CHECK-NEXT: [[ARR_10:%.*arr10.*]] = alloca ptr, align 8 + // CHECK-NEXT: [[ARR_10:%.*arr9.*]] = alloca ptr, align 8 // CHECK-NEXT: [[CALL_PTR]] = call noalias noundef nonnull ptr @_Znam(i64 noundef 16) // CHECK-NEXT: [[ARRAYINIT_BEGIN:%.*]] = getelementptr inbounds [2 x i32], ptr [[CALL]], i64 0, i64 0 // CHECK-NEXT: store i32 1, ptr [[ARRAYINIT_BEGIN]], align 4 @@ -560,13 +545,13 @@ namespace gh68198 { // CHECK-NEXT: [[ARRAY_EXP_NEXT3:%.*]] = getelementptr inbounds [2 x i32], ptr [[ARRAY_EXP_NEXT]], i64 1 // CHECK-NEXT: store ptr [[CALL_PTR]], ptr [[ARR_10]], align 8 // CHECK-NEXT: ret void - void foo27() { - void* arr10 = new int[][2]({1, 2}, {3, 4}); + void foo26() { + void* arr9 = new int[][2]({1, 2}, {3, 4}); } - // CHECK: define {{.*}} void @{{.*foo28.*}} { + // CHECK: define {{.*}} void @{{.*foo27.*}} { // CHECK-NEXT: entry - // CHECK-NEXT: [[ARR_11:%.*arr11.*]] = alloca ptr, align 8 + // CHECK-NEXT: [[ARR_10:%.*arr10.*]] = alloca ptr, align 8 // CHECK-NEXT: [[CALL_PTR]] = call noalias noundef nonnull ptr @_Znam(i64 noundef 32) // CHECK-NEXT: [[ARRAYINIT_BEGIN:%.*]] = getelementptr inbounds [2 x i32], ptr [[CALL]], i64 0, i64 0 // CHECK-NEXT: store i32 5, ptr [[ARRAYINIT_BEGIN]], align 4 @@ -579,9 +564,9 @@ namespace gh68198 { // CHECK-NEXT: store i32 8, ptr [[ARRAYINIT_ELEMENT2]], align 4 // CHECK-NEXT: [[ARRAY_EXP_NEXT3:%.*]] = getelementptr inbounds [2 x i32], ptr [[ARRAY_EXP_NEXT]], i64 1 // CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[ARRAY_EXP_NEXT3]], i8 0, i64 16, i1 false) - // CHECK-NEXT: store ptr [[CALL_PTR]], ptr [[ARR_11]], align 8 + // CHECK-NEXT: store ptr [[CALL_PTR]], ptr [[ARR_10]], align 8 // CHECK-NEXT: ret void - void foo28() { - void* arr11 = new int[4][2]({5, 6}, {7, 8}); + void foo27() { + void* arr10 = new int[4][2]({5, 6}, {7, 8}); } } diff --git a/clang/test/CodeGenCXX/new-array-init.cpp b/clang/test/CodeGenCXX/new-array-init.cpp index db740d065e2f7e..6d80bd8035a7e9 100644 --- a/clang/test/CodeGenCXX/new-array-init.cpp +++ b/clang/test/CodeGenCXX/new-array-init.cpp @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -std=c++11 -triple i386-unknown-unknown %s -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 -std=c++20 -triple i386-unknown-unknown %s -emit-llvm -o - | FileCheck %s --check-prefix=CHECK --check-prefix=CHECKCXX20 // RUN: %clang_cc1 -std=c++11 -triple i386-unknown-unknown %s -emit-llvm -fsanitize=signed-integer-overflow -o - | FileCheck --check-prefix=SIO %s // CHECK: @[[ABC4:.*]] = {{.*}} constant [4 x i8] c"abc\00" @@ -15,6 +16,19 @@ void fn(int n) { new int[n] { 1, 2, 3 }; } +#if __cplusplus >= 202002L +// CHECKCXX20-LABEL: define{{.*}} void @_Z8fn_pareni +void fn_paren(int n) { + // CHECKCXX20: icmp ult i{{32|64}} %{{[^ ]+}}, 3 + // CHECKCXX20: store i32 1 + // CHECKCXX20: store i32 2 + // CHECKCXX20: store i32 3 + // CHECKCXX20: sub {{.*}}, 12 + // CHECKCXX20: call void @llvm.memset + new int[n](1, 2, 3); +} +#endif + // CHECK-LABEL: define{{.*}} void @_Z11const_exactv void const_exact() { // CHECK-NOT: icmp ult i{{32|64}} %{{[^ ]+}}, 3 @@ -22,6 +36,15 @@ void const_exact() { new int[3] { 1, 2, 3 }; } +#if __cplusplus >= 202002L +// CHECKCXX20-LABEL: define{{.*}} void @_Z17const_exact_parenv +void const_exact_paren() { + // CHECKCXX20-NOT: icmp ult i{{32|64}} %{{[^ ]+}}, 3 + // CHECKCXX20-NOT: icmp eq ptr + new int[3](1, 2, 3); +} +#endif + // CHECK-LABEL: define{{.*}} void @_Z16const_sufficientv void const_sufficient() { // CHECK-NOT: icmp ult i{{32|64}} %{{[^ ]+}}, 3 @@ -29,6 +52,15 @@ void const_sufficient() { // CHECK: ret void } +#if __cplusplus >= 202002L +// CHECKCXX20-LABEL: define{{.*}} void @_Z22const_sufficient_parenv +void const_sufficient_paren() { + // CHECKCXX20-NOT: icmp ult i{{32|64}} %{{[^ ]+}}, 3 + new int[4](1, 2, 3); + // CHECKCXX20: ret void +} +#endif + // CHECK-LABEL: define{{.*}} void @_Z22check_array_value_initv void check_array_value_init() { struct S; @@ -46,7 +78,7 @@ void check_array_value_init() { // CHECK-LABEL: define{{.*}} void @_Z15string_nonconsti void string_nonconst(int n) { - // CHECK: icmp slt i{{32|64}} %{{[^ ]+}}, 4 + // CHECK: icmp {{s|u}}lt i{{32|64}} %{{[^ ]+}}, 4 // FIXME: Conditionally throw an exception rather than passing -1 to alloc function // CHECK: select // CHECK: %[[PTR:.*]] = call noalias noundef nonnull ptr @_Zna{{.}}(i{{32|64}} @@ -57,6 +89,21 @@ void string_nonconst(int n) { new char[n] { "abc" }; } +#if __cplusplus >= 202002L +// CHECKCXX20-LABEL: define{{.*}} void @_Z21string_nonconst_pareni +void string_nonconst_paren(int n) { + // CHECKCXX20: icmp {{s|u}}lt i{{32|64}} %{{[^ ]+}}, 4 + // FIXME: Conditionally throw an exception rather than passing -1 to alloc function + // CHECKCXX20: select + // CHECKCXX20: %[[PTR:.*]] = call noalias noundef nonnull ptr @_Zna{{.}}(i{{32|64}} + // CHECKCXX20: call void @llvm.memcpy{{.*}}(ptr align {{[0-9]+}} %[[PTR]], ptr align {{[0-9]+}} @[[ABC4]], i32 4, + // CHECKCXX20: %[[REST:.*]] = getelementptr inbounds i8, ptr %[[PTR]], i32 4 + // CHECKCXX20: %[[RESTSIZE:.*]] = sub {{.*}}, 4 + // CHECKCXX20: call void @llvm.memset{{.*}}(ptr align {{[0-9]+}} %[[REST]], i8 0, i{{32|64}} %[[RESTSIZE]], + new char[n]("abc"); +} +#endif + // CHECK-LABEL: define{{.*}} void @_Z12string_exactv void string_exact() { // CHECK-NOT: icmp @@ -66,6 +113,17 @@ void string_exact() { new char[4] { "abc" }; } +#if __cplusplus >= 202002L +// CHECKCXX20-LABEL: define{{.*}} void @_Z18string_exact_parenv +void string_exact_paren() { + // CHECKCXX20-NOT: icmp + // CHECKCXX20: %[[PTR:.*]] = call noalias noundef nonnull ptr @_Zna{{.}}(i{{32|64}} noundef 4) + // CHECKCXX20: call void @llvm.memcpy{{.*}}(ptr align {{[0-9]+}} %[[PTR]], ptr align {{[0-9]+}} @[[ABC4]], i32 4, + // CHECKCXX20-NOT: memset + new char[4]("abc"); +} +#endif + // CHECK-LABEL: define{{.*}} void @_Z17string_sufficientv void string_sufficient() { // CHECK-NOT: icmp @@ -76,6 +134,18 @@ void string_sufficient() { new char[15] { "abc" }; } +#if __cplusplus >= 202002L +// CHECKCXX20-LABEL: define{{.*}} void @_Z23string_sufficient_parenv +void string_sufficient_paren() { + // CHECKCXX20-NOT: icmp + // CHECKCXX20: %[[PTR:.*]] = call noalias noundef nonnull ptr @_Zna{{.}}(i{{32|64}} noundef 15) + // FIXME: For very large arrays, it would be preferable to emit a small copy and a memset. + // CHECKCXX20: call void @llvm.memcpy{{.*}}(ptr align {{[0-9]+}} %[[PTR]], ptr align {{[0-9]+}} @[[ABC15]], i32 15, + // CHECKCXX20-NOT: memset + new char[15] { "abc" }; +} +#endif + // CHECK-LABEL: define{{.*}} void @_Z10aggr_exactv void aggr_exact() { // CHECK-NOT: icmp diff --git a/clang/test/SemaCXX/new-delete.cpp b/clang/test/SemaCXX/new-delete.cpp index 2db32938f957fb..4f78b7c71a91c5 100644 --- a/clang/test/SemaCXX/new-delete.cpp +++ b/clang/test/SemaCXX/new-delete.cpp @@ -2,7 +2,7 @@ // RUN: %clang_cc1 -fsyntax-only -verify=expected,precxx20 %s -triple=i686-pc-linux-gnu -Wno-new-returns-null -std=c++11 // RUN: %clang_cc1 -fsyntax-only -verify=expected,precxx20 %s -triple=i686-pc-linux-gnu -Wno-new-returns-null -std=c++14 // RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx17,precxx20 %s -triple=i686-pc-linux-gnu -Wno-new-returns-null -std=c++17 -// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx17 %s -triple=i686-pc-linux-gnu -Wno-new-returns-null -std=c++20 +// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx17,cxx20 %s -triple=i686-pc-linux-gnu -Wno-new-returns-null -std=c++20 // FIXME Location is (frontend) // cxx17-note@*:* {{candidate function not viable: requires 2 arguments, but 3 were provided}} @@ -27,6 +27,11 @@ struct U struct V : U { }; +struct W // cxx20-note 2{{candidate constructor}} +{ + int a; + int b; +}; inline void operator delete(void *); // expected-warning {{replacement function 'operator delete' cannot be declared 'inline'}} @@ -359,6 +364,11 @@ void h(unsigned i) { template void h<unsigned>(unsigned); template void h<unsigned[10]>(unsigned); // precxx20-note {{in instantiation of function template specialization 'Test1::h<unsigned int[10]>' requested here}} +void i() { + new W[2](1, 2, 3); // precxx20-error {{array 'new' cannot have initialization arguments}} + // cxx20-error@-1 {{no viable conversion from 'int' to 'W'}} +} + } // Don't diagnose access for overload candidates that aren't selected. >From 8e308b22bbc97bea6085dccec3cbc653787b1799 Mon Sep 17 00:00:00 2001 From: Alan Zhao <ayz...@google.com> Date: Tue, 9 Jan 2024 17:26:33 -0800 Subject: [PATCH 6/9] only create array filler if it's a variable length array created with new --- clang/lib/Sema/SemaInit.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 1fdebadb14e401..0cc35d2b2e9911 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -5495,12 +5495,14 @@ static void TryOrBuildParenListInitialization( return; } // ...and value-initialized for each k < i <= n; - InitializedEntity SubEntity = InitializedEntity::InitializeElement( - S.getASTContext(), Args.size(), Entity); - InitializationKind SubKind = InitializationKind::CreateValue( - Kind.getLocation(), Kind.getLocation(), Kind.getLocation(), true); - if (!HandleInitializedEntity(SubEntity, SubKind, nullptr, &ArrayFiller)) - return; + if (ArrayLength > Args.size() || Entity.isVariableLengthArrayNew()) { + InitializedEntity SubEntity = InitializedEntity::InitializeElement( + S.getASTContext(), Args.size(), Entity); + InitializationKind SubKind = InitializationKind::CreateValue( + Kind.getLocation(), Kind.getLocation(), Kind.getLocation(), true); + if (!HandleInitializedEntity(SubEntity, SubKind, nullptr, &ArrayFiller)) + return; + } if (ResultType.isNull()) { ResultType = S.Context.getConstantArrayType( >From f2c98088bcf8baef0728f3b3b4ec1b8fb70f088a Mon Sep 17 00:00:00 2001 From: Alan Zhao <ayz...@google.com> Date: Tue, 16 Jan 2024 16:32:37 -0800 Subject: [PATCH 7/9] fix __extension__ and Objective-C++ @encode(...) crashes This is mostly implemented by adapting InitListExpr::isStringLiteralInit(...). --- clang/lib/CodeGen/CGExprCXX.cpp | 28 +++++++++++++++++------- clang/test/CodeGenCXX/new-array-init.cpp | 9 ++++++++ 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index 947553cb2ef2a3..8a20e895038bea 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -1039,13 +1039,24 @@ void CodeGenFunction::EmitNewArrayInitializer( }; const InitListExpr *ILE = dyn_cast<InitListExpr>(Init); - const CXXParenListInitExpr *CPLIE = dyn_cast<CXXParenListInitExpr>(Init); - const StringLiteral *SL = dyn_cast<StringLiteral>(Init); + const CXXParenListInitExpr *CPLIE = nullptr; + const StringLiteral *SL = nullptr; + const ObjCEncodeExpr *OCEE = nullptr; + const Expr *IgnoreParen = nullptr; + if (!ILE) { + IgnoreParen = Init->IgnoreParenImpCasts(); + CPLIE = dyn_cast<CXXParenListInitExpr>(IgnoreParen); + SL = dyn_cast<StringLiteral>(IgnoreParen); + OCEE = dyn_cast<ObjCEncodeExpr>(IgnoreParen); + } + // If the initializer is an initializer list, first do the explicit elements. - if (ILE || CPLIE || SL) { + if (ILE || CPLIE || SL || OCEE) { // Initializing from a (braced) string literal is a special case; the init // list element does not initialize a (single) array element. - if ((ILE && ILE->isStringLiteralInit()) || SL) { + if ((ILE && ILE->isStringLiteralInit()) || SL || OCEE) { + if (!ILE) + Init = IgnoreParen; // Initialize the initial portion of length equal to that of the string // literal. The allocation must be for at least this much; we emitted a // check for that earlier. @@ -1057,12 +1068,13 @@ void CodeGenFunction::EmitNewArrayInitializer( AggValueSlot::DoesNotOverlap, AggValueSlot::IsNotZeroed, AggValueSlot::IsSanitizerChecked); - EmitAggExpr(SL ? SL : ILE->getInit(0), Slot); + EmitAggExpr(ILE ? ILE->getInit(0) : Init, Slot); // Move past these elements. - const ArrayType *AT = SL ? SL->getType()->getAsArrayTypeUnsafe() - : ILE->getType()->getAsArrayTypeUnsafe(); - InitListElements = cast<ConstantArrayType>(AT)->getSize().getZExtValue(); + InitListElements = + cast<ConstantArrayType>(Init->getType()->getAsArrayTypeUnsafe()) + ->getSize() + .getZExtValue(); CurPtr = Builder.CreateConstInBoundsGEP( CurPtr, InitListElements, "string.init.end"); diff --git a/clang/test/CodeGenCXX/new-array-init.cpp b/clang/test/CodeGenCXX/new-array-init.cpp index 6d80bd8035a7e9..b452e8184e3ebc 100644 --- a/clang/test/CodeGenCXX/new-array-init.cpp +++ b/clang/test/CodeGenCXX/new-array-init.cpp @@ -122,6 +122,15 @@ void string_exact_paren() { // CHECKCXX20-NOT: memset new char[4]("abc"); } + +// CHECKCXX20-LABEL: define{{.*}} void @_Z28string_exact_paren_extensionv +void string_exact_paren_extension() { + // CHECKCXX20-NOT: icmp + // CHECKCXX20: %[[PTR:.*]] = call noalias noundef nonnull ptr @_Zna{{.}}(i{{32|64}} noundef 4) + // CHECKCXX20: call void @llvm.memcpy{{.*}}(ptr align {{[0-9]+}} %[[PTR]], ptr align {{[0-9]+}} @[[ABC4]], i32 4, + // CHECKCXX20-NOT: memset + new char[4](__extension__ "abc"); +} #endif // CHECK-LABEL: define{{.*}} void @_Z17string_sufficientv >From 10f6b1496481f841badcb5b073b9b340855d841b Mon Sep 17 00:00:00 2001 From: Alan Zhao <alanzh...@users.noreply.github.com> Date: Wed, 17 Jan 2024 10:48:32 -0800 Subject: [PATCH 8/9] Update clang/lib/CodeGen/CGExprCXX.cpp Co-authored-by: Mital Ashok <mital.v...@googlemail.com> --- clang/lib/CodeGen/CGExprCXX.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index 8a20e895038bea..82932906ba72d3 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -1088,7 +1088,8 @@ void CodeGenFunction::EmitNewArrayInitializer( return; } - InitListElements = ILE ? ILE->getNumInits() : CPLIE->getInitExprs().size(); + ArrayRef<const Expr *> InitExprs = ILE ? ILE->inits() : CPLIE->getInitExprs(); + InitListElements = InitExprs.size(); // If this is a multi-dimensional array new, we will initialize multiple // elements with each init list element. >From c19eff713ba927c6da5c3e7867c54999e54b4d1b Mon Sep 17 00:00:00 2001 From: Alan Zhao <ayz...@google.com> Date: Wed, 17 Jan 2024 10:56:36 -0800 Subject: [PATCH 9/9] add check for string literal in parens so it doesn't segfault --- clang/lib/CodeGen/CGExprCXX.cpp | 10 ++++++---- clang/test/CodeGenCXX/new-array-init.cpp | 13 +++++++++++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index 82932906ba72d3..d136bfc37278f0 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -1088,7 +1088,8 @@ void CodeGenFunction::EmitNewArrayInitializer( return; } - ArrayRef<const Expr *> InitExprs = ILE ? ILE->inits() : CPLIE->getInitExprs(); + ArrayRef<const Expr *> InitExprs = + ILE ? ILE->inits() : CPLIE->getInitExprs(); InitListElements = InitExprs.size(); // If this is a multi-dimensional array new, we will initialize multiple @@ -1117,9 +1118,8 @@ void CodeGenFunction::EmitNewArrayInitializer( } CharUnits StartAlign = CurPtr.getAlignment(); - ArrayRef<Expr *> InitExprs = ILE ? ILE->inits() : CPLIE->getInitExprs(); unsigned i = 0; - for (Expr *IE : InitExprs) { + for (const Expr *IE : InitExprs) { // Tell the cleanup that it needs to destroy up to this // element. TODO: some of these stores can be trivially // observed to be unnecessary. @@ -1585,7 +1585,9 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) { const Expr *Init = E->getInitializer(); const InitListExpr *ILE = dyn_cast<InitListExpr>(Init); const CXXParenListInitExpr *CPLIE = dyn_cast<CXXParenListInitExpr>(Init); - if ((ILE && ILE->isStringLiteralInit()) || isa<StringLiteral>(Init)) { + const Expr *IgnoreParen = Init->IgnoreParenImpCasts(); + if ((ILE && ILE->isStringLiteralInit()) || + isa<StringLiteral>(IgnoreParen) || isa<ObjCEncodeExpr>(IgnoreParen)) { minElements = cast<ConstantArrayType>(Init->getType()->getAsArrayTypeUnsafe()) ->getSize() diff --git a/clang/test/CodeGenCXX/new-array-init.cpp b/clang/test/CodeGenCXX/new-array-init.cpp index b452e8184e3ebc..fe1bdf425ab142 100644 --- a/clang/test/CodeGenCXX/new-array-init.cpp +++ b/clang/test/CodeGenCXX/new-array-init.cpp @@ -102,6 +102,19 @@ void string_nonconst_paren(int n) { // CHECKCXX20: call void @llvm.memset{{.*}}(ptr align {{[0-9]+}} %[[REST]], i8 0, i{{32|64}} %[[RESTSIZE]], new char[n]("abc"); } + +// CHECKCXX20-LABEL: define{{.*}} void @_Z33string_nonconst_paren_extra_pareni +void string_nonconst_paren_extra_paren(int n) { + // CHECKCXX20: icmp {{s|u}}lt i{{32|64}} %{{[^ ]+}}, 4 + // FIXME: Conditionally throw an exception rather than passing -1 to alloc function + // CHECKCXX20: select + // CHECKCXX20: %[[PTR:.*]] = call noalias noundef nonnull ptr @_Zna{{.}}(i{{32|64}} + // CHECKCXX20: call void @llvm.memcpy{{.*}}(ptr align {{[0-9]+}} %[[PTR]], ptr align {{[0-9]+}} @[[ABC4]], i32 4, + // CHECKCXX20: %[[REST:.*]] = getelementptr inbounds i8, ptr %[[PTR]], i32 4 + // CHECKCXX20: %[[RESTSIZE:.*]] = sub {{.*}}, 4 + // CHECKCXX20: call void @llvm.memset{{.*}}(ptr align {{[0-9]+}} %[[REST]], i8 0, i{{32|64}} %[[RESTSIZE]], + new char[n](("abc")); +} #endif // CHECK-LABEL: define{{.*}} void @_Z12string_exactv _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits