[clang] [C23] Fix compound literals within function prototype (PR #132097)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Mar 19 13:54:18 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Aaron Ballman (AaronBallman)
<details>
<summary>Changes</summary>
WG14 N2819 clarified that a compound literal within a function prototype has a lifetime similar to that of a local variable within the function, not a file scope variable.
---
Full diff: https://github.com/llvm/llvm-project/pull/132097.diff
5 Files Affected:
- (modified) clang/docs/ReleaseNotes.rst (+4)
- (modified) clang/lib/Sema/SemaExpr.cpp (+7-1)
- (modified) clang/test/AST/ByteCode/literals.cpp (+2-4)
- (modified) clang/test/C/C23/n2819.c (+9-8)
- (modified) clang/www/c_status.html (+1-1)
``````````diff
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 666bbf22acc93..768ba480a7ee7 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -148,6 +148,10 @@ C23 Feature Support
better diagnostic behavior for the ``va_start()`` macro in C23 and later.
This also updates the definition of ``va_start()`` in ``<stdarg.h>`` to use
the new builtin. Fixes #GH124031.
+- Implemented ``WG14 N2819 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2819.pdf`_
+ which clarified that a compound literal used within a function prototype is
+ treated as if the compound literal were within the body rather than at file
+ scope.
Non-comprehensive list of changes in this release
-------------------------------------------------
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index a03acb5fbd273..fca3a2e8c19af 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -7141,7 +7141,13 @@ Sema::BuildCompoundLiteralExpr(SourceLocation LParenLoc, TypeSourceInfo *TInfo,
return ExprError();
LiteralExpr = Result.get();
- bool isFileScope = !CurContext->isFunctionOrMethod();
+ // We treat the compound literal as being at file scope if it's not in a
+ // function or method body, or within the function's prototype scope. This
+ // means the following compound literal is not at file scope:
+ // void func(char *para[(int [1]){ 0 }[0]);
+ const Scope *S = getCurScope();
+ bool isFileScope = !CurContext->isFunctionOrMethod() &&
+ (!S || !S->isFunctionPrototypeScope());
// In C, compound literals are l-values for some reason.
// For GCC compatibility, in C++, file-scope array compound literals with
diff --git a/clang/test/AST/ByteCode/literals.cpp b/clang/test/AST/ByteCode/literals.cpp
index f206f020ecb47..a9b787892eff2 100644
--- a/clang/test/AST/ByteCode/literals.cpp
+++ b/clang/test/AST/ByteCode/literals.cpp
@@ -849,13 +849,11 @@ namespace CompoundLiterals {
}
static_assert(get5() == 5, "");
- constexpr int get6(int f = (int[]){1,2,6}[2]) { // ref-note {{subexpression not valid in a constant expression}} \
- // ref-note {{declared here}}
+ constexpr int get6(int f = (int[]){1,2,6}[2]) {
return f;
}
static_assert(get6(6) == 6, "");
- // FIXME: Who's right here?
- static_assert(get6() == 6, ""); // ref-error {{not an integral constant expression}}
+ static_assert(get6() == 6, "");
constexpr int x = (int){3};
static_assert(x == 3, "");
diff --git a/clang/test/C/C23/n2819.c b/clang/test/C/C23/n2819.c
index c428fbba80245..eb0f64a69f375 100644
--- a/clang/test/C/C23/n2819.c
+++ b/clang/test/C/C23/n2819.c
@@ -1,4 +1,4 @@
-// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 3
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5
// RUN: %clang_cc1 -triple=x86_64 -emit-llvm -o - -std=c23 %s | FileCheck %s
/* WG14 N2819: No
@@ -6,9 +6,10 @@
*/
int *escaped;
+
// CHECK-LABEL: define dso_local i32 @f(
// CHECK-SAME: ptr noundef [[PTR:%.*]]) #[[ATTR0:[0-9]+]] {
-// CHECK-NEXT: entry:
+// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8
// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8
// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8
@@ -19,23 +20,23 @@ int f(int *ptr) { escaped = ptr; return 1; }
// CHECK-LABEL: define dso_local i32 @g(
// CHECK-SAME: ptr noundef [[PARA:%.*]]) #[[ATTR0]] {
-// CHECK-NEXT: entry:
+// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[PARA_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [27 x i32], align 4
// CHECK-NEXT: store ptr [[PARA]], ptr [[PARA_ADDR]], align 8
-// CHECK-NEXT: [[CALL:%.*]] = call i32 @f(ptr noundef @.compoundliteral)
+// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[DOTCOMPOUNDLITERAL]], i8 0, i64 108, i1 false)
+// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [27 x i32], ptr [[DOTCOMPOUNDLITERAL]], i64 0, i64 0
+// CHECK-NEXT: [[CALL:%.*]] = call i32 @f(ptr noundef [[ARRAYDECAY]])
// CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[CALL]] to i64
// CHECK-NEXT: ret i32 0
//
-// FIXME: notice the we are using the global .compoundliteral object, not
-// allocating a new object on each call to g(). That's what was clarified by
-// N2819.
int g(char *para [f(( int [27]) { 0 })]) {
return 0;
}
// CHECK-LABEL: define dso_local i32 @main(
// CHECK-SAME: ) #[[ATTR0]] {
-// CHECK-NEXT: entry:
+// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[RETVAL:%.*]] = alloca i32, align 4
// CHECK-NEXT: store i32 0, ptr [[RETVAL]], align 4
// CHECK-NEXT: [[CALL:%.*]] = call i32 @g(ptr noundef null)
diff --git a/clang/www/c_status.html b/clang/www/c_status.html
index 7cf50bfdb6639..c9e2eda4304f3 100644
--- a/clang/www/c_status.html
+++ b/clang/www/c_status.html
@@ -715,7 +715,7 @@ <h2 id="c2x">C23 implementation status</h2>
<tr>
<td>Disambiguate the storage class of some compound literals</td>
<td><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2819.pdf">N2819</a></td>
- <td class="none" align="center">No</td>
+ <td class="unreleased" align="center">Clang 21</td>
</tr>
<tr>
<td>Add annotations for unreachable control flow v2</td>
``````````
</details>
https://github.com/llvm/llvm-project/pull/132097
More information about the cfe-commits
mailing list