Skip to content

Commit 2c9f04c

Browse files
authored
[clang] Fix parenthesized list initialization of arrays not working with new (llvm#76976)
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 llvm#68198
1 parent 5de1d00 commit 2c9f04c

File tree

7 files changed

+221
-33
lines changed

7 files changed

+221
-33
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -976,6 +976,9 @@ Bug Fixes to C++ Support
976976
``std::source_location::current()`` was used in a function template.
977977
Fixes (`#78128 <https://github.com/llvm/llvm-project/issues/78128>`_)
978978

979+
- Clang now allows parenthesized initialization of arrays in `operator new[]`.
980+
Fixes: (`#68198 <https://github.com/llvm/llvm-project/issues/68198>`_)
981+
979982
Bug Fixes to AST Handling
980983
^^^^^^^^^^^^^^^^^^^^^^^^^
981984
- Fixed an import failure of recursive friend class template.

clang/lib/CodeGen/CGExprCXX.cpp

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,11 +1038,25 @@ void CodeGenFunction::EmitNewArrayInitializer(
10381038
return true;
10391039
};
10401040

1041+
const InitListExpr *ILE = dyn_cast<InitListExpr>(Init);
1042+
const CXXParenListInitExpr *CPLIE = nullptr;
1043+
const StringLiteral *SL = nullptr;
1044+
const ObjCEncodeExpr *OCEE = nullptr;
1045+
const Expr *IgnoreParen = nullptr;
1046+
if (!ILE) {
1047+
IgnoreParen = Init->IgnoreParenImpCasts();
1048+
CPLIE = dyn_cast<CXXParenListInitExpr>(IgnoreParen);
1049+
SL = dyn_cast<StringLiteral>(IgnoreParen);
1050+
OCEE = dyn_cast<ObjCEncodeExpr>(IgnoreParen);
1051+
}
1052+
10411053
// If the initializer is an initializer list, first do the explicit elements.
1042-
if (const InitListExpr *ILE = dyn_cast<InitListExpr>(Init)) {
1054+
if (ILE || CPLIE || SL || OCEE) {
10431055
// Initializing from a (braced) string literal is a special case; the init
10441056
// list element does not initialize a (single) array element.
1045-
if (ILE->isStringLiteralInit()) {
1057+
if ((ILE && ILE->isStringLiteralInit()) || SL || OCEE) {
1058+
if (!ILE)
1059+
Init = IgnoreParen;
10461060
// Initialize the initial portion of length equal to that of the string
10471061
// literal. The allocation must be for at least this much; we emitted a
10481062
// check for that earlier.
@@ -1054,12 +1068,13 @@ void CodeGenFunction::EmitNewArrayInitializer(
10541068
AggValueSlot::DoesNotOverlap,
10551069
AggValueSlot::IsNotZeroed,
10561070
AggValueSlot::IsSanitizerChecked);
1057-
EmitAggExpr(ILE->getInit(0), Slot);
1071+
EmitAggExpr(ILE ? ILE->getInit(0) : Init, Slot);
10581072

10591073
// Move past these elements.
10601074
InitListElements =
1061-
cast<ConstantArrayType>(ILE->getType()->getAsArrayTypeUnsafe())
1062-
->getSize().getZExtValue();
1075+
cast<ConstantArrayType>(Init->getType()->getAsArrayTypeUnsafe())
1076+
->getSize()
1077+
.getZExtValue();
10631078
CurPtr = Builder.CreateConstInBoundsGEP(
10641079
CurPtr, InitListElements, "string.init.end");
10651080

@@ -1073,7 +1088,9 @@ void CodeGenFunction::EmitNewArrayInitializer(
10731088
return;
10741089
}
10751090

1076-
InitListElements = ILE->getNumInits();
1091+
ArrayRef<const Expr *> InitExprs =
1092+
ILE ? ILE->inits() : CPLIE->getInitExprs();
1093+
InitListElements = InitExprs.size();
10771094

10781095
// If this is a multi-dimensional array new, we will initialize multiple
10791096
// elements with each init list element.
@@ -1101,7 +1118,8 @@ void CodeGenFunction::EmitNewArrayInitializer(
11011118
}
11021119

11031120
CharUnits StartAlign = CurPtr.getAlignment();
1104-
for (unsigned i = 0, e = ILE->getNumInits(); i != e; ++i) {
1121+
unsigned i = 0;
1122+
for (const Expr *IE : InitExprs) {
11051123
// Tell the cleanup that it needs to destroy up to this
11061124
// element. TODO: some of these stores can be trivially
11071125
// observed to be unnecessary.
@@ -1111,18 +1129,17 @@ void CodeGenFunction::EmitNewArrayInitializer(
11111129
// FIXME: If the last initializer is an incomplete initializer list for
11121130
// an array, and we have an array filler, we can fold together the two
11131131
// initialization loops.
1114-
StoreAnyExprIntoOneUnit(*this, ILE->getInit(i),
1115-
ILE->getInit(i)->getType(), CurPtr,
1132+
StoreAnyExprIntoOneUnit(*this, IE, IE->getType(), CurPtr,
11161133
AggValueSlot::DoesNotOverlap);
11171134
CurPtr = Address(Builder.CreateInBoundsGEP(
11181135
CurPtr.getElementType(), CurPtr.getPointer(),
11191136
Builder.getSize(1), "array.exp.next"),
11201137
CurPtr.getElementType(),
1121-
StartAlign.alignmentAtOffset((i + 1) * ElementSize));
1138+
StartAlign.alignmentAtOffset((++i) * ElementSize));
11221139
}
11231140

11241141
// The remaining elements are filled with the array filler expression.
1125-
Init = ILE->getArrayFiller();
1142+
Init = ILE ? ILE->getArrayFiller() : CPLIE->getArrayFiller();
11261143

11271144
// Extract the initializer for the individual array elements by pulling
11281145
// out the array filler from all the nested initializer lists. This avoids
@@ -1561,16 +1578,23 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) {
15611578
// 1. Build a call to the allocation function.
15621579
FunctionDecl *allocator = E->getOperatorNew();
15631580

1564-
// If there is a brace-initializer, cannot allocate fewer elements than inits.
1581+
// If there is a brace-initializer or C++20 parenthesized initializer, cannot
1582+
// allocate fewer elements than inits.
15651583
unsigned minElements = 0;
15661584
if (E->isArray() && E->hasInitializer()) {
1567-
const InitListExpr *ILE = dyn_cast<InitListExpr>(E->getInitializer());
1568-
if (ILE && ILE->isStringLiteralInit())
1585+
const Expr *Init = E->getInitializer();
1586+
const InitListExpr *ILE = dyn_cast<InitListExpr>(Init);
1587+
const CXXParenListInitExpr *CPLIE = dyn_cast<CXXParenListInitExpr>(Init);
1588+
const Expr *IgnoreParen = Init->IgnoreParenImpCasts();
1589+
if ((ILE && ILE->isStringLiteralInit()) ||
1590+
isa<StringLiteral>(IgnoreParen) || isa<ObjCEncodeExpr>(IgnoreParen)) {
15691591
minElements =
1570-
cast<ConstantArrayType>(ILE->getType()->getAsArrayTypeUnsafe())
1571-
->getSize().getZExtValue();
1572-
else if (ILE)
1573-
minElements = ILE->getNumInits();
1592+
cast<ConstantArrayType>(Init->getType()->getAsArrayTypeUnsafe())
1593+
->getSize()
1594+
.getZExtValue();
1595+
} else if (ILE || CPLIE) {
1596+
minElements = ILE ? ILE->getNumInits() : CPLIE->getInitExprs().size();
1597+
}
15741598
}
15751599

15761600
llvm::Value *numElements = nullptr;

clang/lib/Sema/SemaExprCXX.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1947,11 +1947,11 @@ Sema::ActOnCXXNew(SourceLocation StartLoc, bool UseGlobal,
19471947
}
19481948

19491949
static bool isLegalArrayNewInitializer(CXXNewInitializationStyle Style,
1950-
Expr *Init) {
1950+
Expr *Init, bool IsCPlusPlus20) {
19511951
if (!Init)
19521952
return true;
19531953
if (ParenListExpr *PLE = dyn_cast<ParenListExpr>(Init))
1954-
return PLE->getNumExprs() == 0;
1954+
return IsCPlusPlus20 || PLE->getNumExprs() == 0;
19551955
if (isa<ImplicitValueInitExpr>(Init))
19561956
return true;
19571957
else if (CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(Init))
@@ -2406,7 +2406,8 @@ ExprResult Sema::BuildCXXNew(SourceRange Range, bool UseGlobal,
24062406
// Array 'new' can't have any initializers except empty parentheses.
24072407
// Initializer lists are also allowed, in C++11. Rely on the parser for the
24082408
// dialect distinction.
2409-
if (ArraySize && !isLegalArrayNewInitializer(InitStyle, Initializer)) {
2409+
if (ArraySize && !isLegalArrayNewInitializer(InitStyle, Initializer,
2410+
getLangOpts().CPlusPlus20)) {
24102411
SourceRange InitRange(Exprs.front()->getBeginLoc(),
24112412
Exprs.back()->getEndLoc());
24122413
Diag(StartLoc, diag::err_new_array_init_args) << InitRange;

clang/lib/Sema/SemaInit.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5496,7 +5496,7 @@ static void TryOrBuildParenListInitialization(
54965496
return;
54975497
}
54985498
// ...and value-initialized for each k < i <= n;
5499-
if (ArrayLength > Args.size()) {
5499+
if (ArrayLength > Args.size() || Entity.isVariableLengthArrayNew()) {
55005500
InitializedEntity SubEntity = InitializedEntity::InitializeElement(
55015501
S.getASTContext(), Args.size(), Entity);
55025502
InitializationKind SubKind = InitializationKind::CreateValue(

clang/test/CodeGen/paren-list-agg-init.cpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,3 +513,60 @@ namespace gh61567 {
513513
I(0);
514514
}
515515
}
516+
517+
namespace gh68198 {
518+
// CHECK: define {{.*}} void @{{.*foo25.*}} {
519+
// CHECK-NEXT: entry
520+
// CHECK-NEXT: [[ARR_8:%.*arr8.*]] = alloca ptr, align 8
521+
// CHECK-NEXT: [[CALL_PTR:%.*]] = call noalias noundef nonnull ptr @_Znam(i64 noundef 8)
522+
// CHECK-NEXT: store i32 1, ptr [[CALL_PTR]], align 4
523+
// CHECK-NEXT: [[ARRAY_EXP_NEXT:%.*]] = getelementptr inbounds i32, ptr [[CALL_PTR]], i64 1
524+
// CHECK-NEXT: store i32 2, ptr [[ARRAY_EXP_NEXT]], align 4
525+
// CHECK-NEXT: [[ARRAY_EXP_NEXT1:%.*]] = getelementptr inbounds i32, ptr [[ARRAY_EXP_NEXT]], i64 1
526+
// CHECK-NEXT: store ptr [[CALL_PTR]], ptr %arr8, align 8
527+
// CHECK-NEXT: ret void
528+
void foo25() {
529+
int* arr8 = new int[](1, 2);
530+
}
531+
532+
// CHECK: define {{.*}} void @{{.*foo26.*}} {
533+
// CHECK-NEXT: entry
534+
// CHECK-NEXT: [[ARR_10:%.*arr9.*]] = alloca ptr, align 8
535+
// CHECK-NEXT: [[CALL_PTR]] = call noalias noundef nonnull ptr @_Znam(i64 noundef 16)
536+
// CHECK-NEXT: [[ARRAYINIT_BEGIN:%.*]] = getelementptr inbounds [2 x i32], ptr [[CALL]], i64 0, i64 0
537+
// CHECK-NEXT: store i32 1, ptr [[ARRAYINIT_BEGIN]], align 4
538+
// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds i32, ptr [[ARRAYINIT_BEGIN]], i64 1
539+
// CHECK-NEXT: store i32 2, ptr [[ARRAYINIT_ELEMENT]], align 4
540+
// CHECK-NEXT: [[ARRAY_EXP_NEXT:%.*]] = getelementptr inbounds [2 x i32], ptr %call, i64 1
541+
// CHECK-NEXT: [[ARRAYINIT_BEGIN1:%.*]] = getelementptr inbounds [2 x i32], ptr [[ARRAY_EXP_NEXT]], i64 0, i64 0
542+
// CHECK-NEXT: store i32 3, ptr [[ARRAYINIT_BEGIN1]], align 4
543+
// CHECK-NEXT: [[ARRAYINIT_ELEMENT2:%.*]] = getelementptr inbounds i32, ptr [[ARRAYINIT_BEGIN1]], i64 1
544+
// CHECK-NEXT: store i32 4, ptr [[ARRAYINIT_ELEMENT2]], align 4
545+
// CHECK-NEXT: [[ARRAY_EXP_NEXT3:%.*]] = getelementptr inbounds [2 x i32], ptr [[ARRAY_EXP_NEXT]], i64 1
546+
// CHECK-NEXT: store ptr [[CALL_PTR]], ptr [[ARR_10]], align 8
547+
// CHECK-NEXT: ret void
548+
void foo26() {
549+
void* arr9 = new int[][2]({1, 2}, {3, 4});
550+
}
551+
552+
// CHECK: define {{.*}} void @{{.*foo27.*}} {
553+
// CHECK-NEXT: entry
554+
// CHECK-NEXT: [[ARR_10:%.*arr10.*]] = alloca ptr, align 8
555+
// CHECK-NEXT: [[CALL_PTR]] = call noalias noundef nonnull ptr @_Znam(i64 noundef 32)
556+
// CHECK-NEXT: [[ARRAYINIT_BEGIN:%.*]] = getelementptr inbounds [2 x i32], ptr [[CALL]], i64 0, i64 0
557+
// CHECK-NEXT: store i32 5, ptr [[ARRAYINIT_BEGIN]], align 4
558+
// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds i32, ptr [[ARRAYINIT_BEGIN]], i64 1
559+
// CHECK-NEXT: store i32 6, ptr [[ARRAYINIT_ELEMENT]], align 4
560+
// CHECK-NEXT: [[ARRAY_EXP_NEXT:%.*]] = getelementptr inbounds [2 x i32], ptr %call, i64 1
561+
// CHECK-NEXT: [[ARRAYINIT_BEGIN1:%.*]] = getelementptr inbounds [2 x i32], ptr [[ARRAY_EXP_NEXT]], i64 0, i64 0
562+
// CHECK-NEXT: store i32 7, ptr [[ARRAYINIT_BEGIN1]], align 4
563+
// CHECK-NEXT: [[ARRAYINIT_ELEMENT2:%.*]] = getelementptr inbounds i32, ptr [[ARRAYINIT_BEGIN1]], i64 1
564+
// CHECK-NEXT: store i32 8, ptr [[ARRAYINIT_ELEMENT2]], align 4
565+
// CHECK-NEXT: [[ARRAY_EXP_NEXT3:%.*]] = getelementptr inbounds [2 x i32], ptr [[ARRAY_EXP_NEXT]], i64 1
566+
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[ARRAY_EXP_NEXT3]], i8 0, i64 16, i1 false)
567+
// CHECK-NEXT: store ptr [[CALL_PTR]], ptr [[ARR_10]], align 8
568+
// CHECK-NEXT: ret void
569+
void foo27() {
570+
void* arr10 = new int[4][2]({5, 6}, {7, 8});
571+
}
572+
}

clang/test/CodeGenCXX/new-array-init.cpp

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// RUN: %clang_cc1 -std=c++11 -triple i386-unknown-unknown %s -emit-llvm -o - | FileCheck %s
2+
// RUN: %clang_cc1 -std=c++20 -triple i386-unknown-unknown %s -emit-llvm -o - | FileCheck %s --check-prefix=CHECK --check-prefix=CHECKCXX20
23
// RUN: %clang_cc1 -std=c++11 -triple i386-unknown-unknown %s -emit-llvm -fsanitize=signed-integer-overflow -o - | FileCheck --check-prefix=SIO %s
34

45
// CHECK: @[[ABC4:.*]] = {{.*}} constant [4 x i8] c"abc\00"
@@ -15,20 +16,51 @@ void fn(int n) {
1516
new int[n] { 1, 2, 3 };
1617
}
1718

19+
#if __cplusplus >= 202002L
20+
// CHECKCXX20-LABEL: define{{.*}} void @_Z8fn_pareni
21+
void fn_paren(int n) {
22+
// CHECKCXX20: icmp ult i{{32|64}} %{{[^ ]+}}, 3
23+
// CHECKCXX20: store i32 1
24+
// CHECKCXX20: store i32 2
25+
// CHECKCXX20: store i32 3
26+
// CHECKCXX20: sub {{.*}}, 12
27+
// CHECKCXX20: call void @llvm.memset
28+
new int[n](1, 2, 3);
29+
}
30+
#endif
31+
1832
// CHECK-LABEL: define{{.*}} void @_Z11const_exactv
1933
void const_exact() {
2034
// CHECK-NOT: icmp ult i{{32|64}} %{{[^ ]+}}, 3
2135
// CHECK-NOT: icmp eq ptr
2236
new int[3] { 1, 2, 3 };
2337
}
2438

39+
#if __cplusplus >= 202002L
40+
// CHECKCXX20-LABEL: define{{.*}} void @_Z17const_exact_parenv
41+
void const_exact_paren() {
42+
// CHECKCXX20-NOT: icmp ult i{{32|64}} %{{[^ ]+}}, 3
43+
// CHECKCXX20-NOT: icmp eq ptr
44+
new int[3](1, 2, 3);
45+
}
46+
#endif
47+
2548
// CHECK-LABEL: define{{.*}} void @_Z16const_sufficientv
2649
void const_sufficient() {
2750
// CHECK-NOT: icmp ult i{{32|64}} %{{[^ ]+}}, 3
2851
new int[4] { 1, 2, 3 };
2952
// CHECK: ret void
3053
}
3154

55+
#if __cplusplus >= 202002L
56+
// CHECKCXX20-LABEL: define{{.*}} void @_Z22const_sufficient_parenv
57+
void const_sufficient_paren() {
58+
// CHECKCXX20-NOT: icmp ult i{{32|64}} %{{[^ ]+}}, 3
59+
new int[4](1, 2, 3);
60+
// CHECKCXX20: ret void
61+
}
62+
#endif
63+
3264
// CHECK-LABEL: define{{.*}} void @_Z22check_array_value_initv
3365
void check_array_value_init() {
3466
struct S;
@@ -46,7 +78,7 @@ void check_array_value_init() {
4678

4779
// CHECK-LABEL: define{{.*}} void @_Z15string_nonconsti
4880
void string_nonconst(int n) {
49-
// CHECK: icmp slt i{{32|64}} %{{[^ ]+}}, 4
81+
// CHECK: icmp {{s|u}}lt i{{32|64}} %{{[^ ]+}}, 4
5082
// FIXME: Conditionally throw an exception rather than passing -1 to alloc function
5183
// CHECK: select
5284
// CHECK: %[[PTR:.*]] = call noalias noundef nonnull ptr @_Zna{{.}}(i{{32|64}}
@@ -57,6 +89,34 @@ void string_nonconst(int n) {
5789
new char[n] { "abc" };
5890
}
5991

92+
#if __cplusplus >= 202002L
93+
// CHECKCXX20-LABEL: define{{.*}} void @_Z21string_nonconst_pareni
94+
void string_nonconst_paren(int n) {
95+
// CHECKCXX20: icmp {{s|u}}lt i{{32|64}} %{{[^ ]+}}, 4
96+
// FIXME: Conditionally throw an exception rather than passing -1 to alloc function
97+
// CHECKCXX20: select
98+
// CHECKCXX20: %[[PTR:.*]] = call noalias noundef nonnull ptr @_Zna{{.}}(i{{32|64}}
99+
// CHECKCXX20: call void @llvm.memcpy{{.*}}(ptr align {{[0-9]+}} %[[PTR]], ptr align {{[0-9]+}} @[[ABC4]], i32 4,
100+
// CHECKCXX20: %[[REST:.*]] = getelementptr inbounds i8, ptr %[[PTR]], i32 4
101+
// CHECKCXX20: %[[RESTSIZE:.*]] = sub {{.*}}, 4
102+
// CHECKCXX20: call void @llvm.memset{{.*}}(ptr align {{[0-9]+}} %[[REST]], i8 0, i{{32|64}} %[[RESTSIZE]],
103+
new char[n]("abc");
104+
}
105+
106+
// CHECKCXX20-LABEL: define{{.*}} void @_Z33string_nonconst_paren_extra_pareni
107+
void string_nonconst_paren_extra_paren(int n) {
108+
// CHECKCXX20: icmp {{s|u}}lt i{{32|64}} %{{[^ ]+}}, 4
109+
// FIXME: Conditionally throw an exception rather than passing -1 to alloc function
110+
// CHECKCXX20: select
111+
// CHECKCXX20: %[[PTR:.*]] = call noalias noundef nonnull ptr @_Zna{{.}}(i{{32|64}}
112+
// CHECKCXX20: call void @llvm.memcpy{{.*}}(ptr align {{[0-9]+}} %[[PTR]], ptr align {{[0-9]+}} @[[ABC4]], i32 4,
113+
// CHECKCXX20: %[[REST:.*]] = getelementptr inbounds i8, ptr %[[PTR]], i32 4
114+
// CHECKCXX20: %[[RESTSIZE:.*]] = sub {{.*}}, 4
115+
// CHECKCXX20: call void @llvm.memset{{.*}}(ptr align {{[0-9]+}} %[[REST]], i8 0, i{{32|64}} %[[RESTSIZE]],
116+
new char[n](("abc"));
117+
}
118+
#endif
119+
60120
// CHECK-LABEL: define{{.*}} void @_Z12string_exactv
61121
void string_exact() {
62122
// CHECK-NOT: icmp
@@ -66,6 +126,26 @@ void string_exact() {
66126
new char[4] { "abc" };
67127
}
68128

129+
#if __cplusplus >= 202002L
130+
// CHECKCXX20-LABEL: define{{.*}} void @_Z18string_exact_parenv
131+
void string_exact_paren() {
132+
// CHECKCXX20-NOT: icmp
133+
// CHECKCXX20: %[[PTR:.*]] = call noalias noundef nonnull ptr @_Zna{{.}}(i{{32|64}} noundef 4)
134+
// CHECKCXX20: call void @llvm.memcpy{{.*}}(ptr align {{[0-9]+}} %[[PTR]], ptr align {{[0-9]+}} @[[ABC4]], i32 4,
135+
// CHECKCXX20-NOT: memset
136+
new char[4]("abc");
137+
}
138+
139+
// CHECKCXX20-LABEL: define{{.*}} void @_Z28string_exact_paren_extensionv
140+
void string_exact_paren_extension() {
141+
// CHECKCXX20-NOT: icmp
142+
// CHECKCXX20: %[[PTR:.*]] = call noalias noundef nonnull ptr @_Zna{{.}}(i{{32|64}} noundef 4)
143+
// CHECKCXX20: call void @llvm.memcpy{{.*}}(ptr align {{[0-9]+}} %[[PTR]], ptr align {{[0-9]+}} @[[ABC4]], i32 4,
144+
// CHECKCXX20-NOT: memset
145+
new char[4](__extension__ "abc");
146+
}
147+
#endif
148+
69149
// CHECK-LABEL: define{{.*}} void @_Z17string_sufficientv
70150
void string_sufficient() {
71151
// CHECK-NOT: icmp
@@ -76,6 +156,18 @@ void string_sufficient() {
76156
new char[15] { "abc" };
77157
}
78158

159+
#if __cplusplus >= 202002L
160+
// CHECKCXX20-LABEL: define{{.*}} void @_Z23string_sufficient_parenv
161+
void string_sufficient_paren() {
162+
// CHECKCXX20-NOT: icmp
163+
// CHECKCXX20: %[[PTR:.*]] = call noalias noundef nonnull ptr @_Zna{{.}}(i{{32|64}} noundef 15)
164+
// FIXME: For very large arrays, it would be preferable to emit a small copy and a memset.
165+
// CHECKCXX20: call void @llvm.memcpy{{.*}}(ptr align {{[0-9]+}} %[[PTR]], ptr align {{[0-9]+}} @[[ABC15]], i32 15,
166+
// CHECKCXX20-NOT: memset
167+
new char[15] { "abc" };
168+
}
169+
#endif
170+
79171
// CHECK-LABEL: define{{.*}} void @_Z10aggr_exactv
80172
void aggr_exact() {
81173
// CHECK-NOT: icmp

0 commit comments

Comments
 (0)