[clang] [clang][AST] Fix positioning of preserve cconv attributes in TypePrinter (PR #147285)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Jul 7 04:55:00 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Andreas C. Osowski (th0br0)
<details>
<summary>Changes</summary>
TypePrinter currently generates function pointer declarations that do not compile when using the `preserve_.*` calling conventions as per https://clang.llvm.org/docs/AttributeReference.html#preserve-all ff.
Running clang with `-Xclang -ast-print` on the following:
```cc
using IN1 = void (__attribute__((preserve_most)) *)();
using IN2 = __attribute__((preserve_most)) void (*) ();
```
outputs:
```cc
using IN1 = void (*)() __attribute__((preserve_most));
using IN2 = void ((*))() __attribute__((preserve_most));
```
However, this does not compile:
```cc
<source>:3:23: error: expected ';' after alias declaration
3 | using IN1 = void (*)() __attribute__((preserve_most));
```
This PR updates TypePrinter such that output is correct and compiles:
```cc
using IN1 = __attribute__((preserve_most)) void (*)();
using IN2 = __attribute__((preserve_most)) void ((*))();
```
I've verified via `-ast-dump` that the AST looks equivalent.
---
Full diff: https://github.com/llvm/llvm-project/pull/147285.diff
5 Files Affected:
- (modified) clang/lib/AST/TypePrinter.cpp (+23-10)
- (added) clang/test/AST/ast-print-cconv-preserve.cpp (+14)
- (modified) clang/test/Sema/preserve-call-conv.c (+4-4)
- (modified) clang/test/Sema/preserve-none-call-conv.c (+2-2)
- (modified) clang/test/SemaCXX/lambda-attributes.cpp (+3-3)
``````````diff
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index d18723d807c6a..a845bca42e374 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -1861,6 +1861,17 @@ void TypePrinter::printAttributedBefore(const AttributedType *T,
if (T->getAttrKind() == attr::ObjCKindOf)
OS << "__kindof ";
+ if (T->getAttrKind() == attr::PreserveNone) {
+ OS << "__attribute__((preserve_none)) ";
+ spaceBeforePlaceHolder(OS);
+ } else if (T->getAttrKind() == attr::PreserveMost) {
+ OS << "__attribute__((preserve_most)) ";
+ spaceBeforePlaceHolder(OS);
+ } else if (T->getAttrKind() == attr::PreserveAll) {
+ OS << "__attribute__((preserve_all)) ";
+ spaceBeforePlaceHolder(OS);
+ }
+
if (T->getAttrKind() == attr::AddressSpace)
printBefore(T->getEquivalentType(), OS);
else
@@ -1972,6 +1983,13 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
return;
}
+ if (T->getAttrKind() == attr::PreserveAll ||
+ T->getAttrKind() == attr::PreserveMost ||
+ T->getAttrKind() == attr::PreserveNone) {
+ // This has to be printed before the declaration.
+ return;
+ }
+
OS << " __attribute__((";
switch (T->getAttrKind()) {
#define TYPE_ATTR(NAME)
@@ -2036,6 +2054,9 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
case attr::Blocking:
case attr::Allocating:
case attr::SwiftAttr:
+ case attr::PreserveAll:
+ case attr::PreserveMost:
+ case attr::PreserveNone:
llvm_unreachable("This attribute should have been handled already");
case attr::NSReturnsRetained:
@@ -2071,20 +2092,12 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
case attr::DeviceKernel:
OS << T->getAttr()->getSpelling();
break;
- case attr::IntelOclBicc: OS << "inteloclbicc"; break;
- case attr::PreserveMost:
- OS << "preserve_most";
- break;
-
- case attr::PreserveAll:
- OS << "preserve_all";
+ case attr::IntelOclBicc:
+ OS << "inteloclbicc";
break;
case attr::M68kRTD:
OS << "m68k_rtd";
break;
- case attr::PreserveNone:
- OS << "preserve_none";
- break;
case attr::RISCVVectorCC:
OS << "riscv_vector_cc";
break;
diff --git a/clang/test/AST/ast-print-cconv-preserve.cpp b/clang/test/AST/ast-print-cconv-preserve.cpp
new file mode 100644
index 0000000000000..af12fe64b2278
--- /dev/null
+++ b/clang/test/AST/ast-print-cconv-preserve.cpp
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 -ast-print %s -o - | FileCheck %s
+
+void (__attribute__((preserve_none)) *none)();
+
+// CHECK: __attribute__((preserve_none)) void (*none)();
+
+__attribute__((preserve_all)) void (*all)();
+
+// CHECK: __attribute__((preserve_all)) void ((*all))();
+
+__attribute__((preserve_most)) void (*most)();
+
+// CHECK: __attribute__((preserve_most)) void ((*most))();
+
diff --git a/clang/test/Sema/preserve-call-conv.c b/clang/test/Sema/preserve-call-conv.c
index adb851960b2e3..01d0872bd6c55 100644
--- a/clang/test/Sema/preserve-call-conv.c
+++ b/clang/test/Sema/preserve-call-conv.c
@@ -14,8 +14,8 @@ void __attribute__((preserve_most(1))) foo1(void *ptr) { // expected-error {{'pr
void (__attribute__((preserve_most)) *pfoo1)(void *) = foo;
-void (__attribute__((cdecl)) *pfoo2)(void *) = foo; // expected-error {{incompatible function pointer types initializing 'void (*)(void *) __attribute__((cdecl))' with an expression of type 'void (void *) __attribute__((preserve_most))'}}
-void (*pfoo3)(void *) = foo; // expected-error {{incompatible function pointer types initializing 'void (*)(void *)' with an expression of type 'void (void *) __attribute__((preserve_most))'}}
+void (__attribute__((cdecl)) *pfoo2)(void *) = foo; // expected-error {{incompatible function pointer types initializing 'void (*)(void *) __attribute__((cdecl))' with an expression of type '__attribute__((preserve_most)) void (void *)'}}
+void (*pfoo3)(void *) = foo; // expected-error {{incompatible function pointer types initializing 'void (*)(void *)' with an expression of type '__attribute__((preserve_most)) void (void *)'}}
typedef_fun_t typedef_fun_foo; // expected-note {{previous declaration is here}}
void __attribute__((preserve_most)) typedef_fun_foo(int x) { } // expected-error {{function declared 'preserve_most' here was previously declared without calling convention}}
@@ -30,8 +30,8 @@ void __attribute__((preserve_all(1))) boo1(void *ptr) { // expected-error {{'pre
void (__attribute__((preserve_all)) *pboo1)(void *) = boo;
-void (__attribute__((cdecl)) *pboo2)(void *) = boo; // expected-error {{incompatible function pointer types initializing 'void (*)(void *) __attribute__((cdecl))' with an expression of type 'void (void *) __attribute__((preserve_all))'}}
-void (*pboo3)(void *) = boo; // expected-error {{incompatible function pointer types initializing 'void (*)(void *)' with an expression of type 'void (void *) __attribute__((preserve_all))'}}
+void (__attribute__((cdecl)) *pboo2)(void *) = boo; // expected-error {{incompatible function pointer types initializing 'void (*)(void *) __attribute__((cdecl))' with an expression of type '__attribute__((preserve_all)) void (void *)'}}
+void (*pboo3)(void *) = boo; // expected-error {{incompatible function pointer types initializing 'void (*)(void *)' with an expression of type '__attribute__((preserve_all)) void (void *)'}}
typedef_fun_t typedef_fun_boo; // expected-note {{previous declaration is here}}
void __attribute__((preserve_all)) typedef_fun_boo(int x) { } // expected-error {{function declared 'preserve_all' here was previously declared without calling convention}}
diff --git a/clang/test/Sema/preserve-none-call-conv.c b/clang/test/Sema/preserve-none-call-conv.c
index 678fa7d5317e5..fc9463726e3f5 100644
--- a/clang/test/Sema/preserve-none-call-conv.c
+++ b/clang/test/Sema/preserve-none-call-conv.c
@@ -11,8 +11,8 @@ void __attribute__((preserve_none(1))) boo1(void *ptr) { // expected-error {{'pr
void (__attribute__((preserve_none)) *pboo1)(void *) = boo;
-void (__attribute__((cdecl)) *pboo2)(void *) = boo; // expected-error {{incompatible function pointer types initializing 'void (*)(void *) __attribute__((cdecl))' with an expression of type 'void (void *) __attribute__((preserve_none))'}}
-void (*pboo3)(void *) = boo; // expected-error {{incompatible function pointer types initializing 'void (*)(void *)' with an expression of type 'void (void *) __attribute__((preserve_none))'}}
+void (__attribute__((cdecl)) *pboo2)(void *) = boo; // expected-error {{incompatible function pointer types initializing 'void (*)(void *) __attribute__((cdecl))' with an expression of type '__attribute__((preserve_none)) void (void *)'}}
+void (*pboo3)(void *) = boo; // expected-error {{incompatible function pointer types initializing 'void (*)(void *)' with an expression of type '__attribute__((preserve_none)) void (void *)'}}
typedef_fun_t typedef_fun_boo; // expected-note {{previous declaration is here}}
void __attribute__((preserve_none)) typedef_fun_boo(int x) { } // expected-error {{function declared 'preserve_none' here was previously declared without calling convention}}
diff --git a/clang/test/SemaCXX/lambda-attributes.cpp b/clang/test/SemaCXX/lambda-attributes.cpp
index 97d23053b0f46..d9764cfe4b204 100644
--- a/clang/test/SemaCXX/lambda-attributes.cpp
+++ b/clang/test/SemaCXX/lambda-attributes.cpp
@@ -14,7 +14,7 @@
// CHECK: FunctionDecl {{.*}} f 'void ()' implicit_instantiation
template <typename T>
void f() {
- // CHECK: CXXMethodDecl {{.*}} operator() 'void (int) const __attribute__((preserve_most))':'void (int) __attribute__((preserve_most)) const' implicit_instantiation
+ // CHECK: CXXMethodDecl {{.*}} operator() '__attribute__((preserve_most)) void (int) const':'void (int) __attribute__((preserve_most)) const' implicit_instantiation
(void) [] (T) __attribute__((preserve_most)) { };
// CHECK: CXXMethodDecl {{.*}} operator() 'void (int) const {{\[}}[clang::annotate_type(...)]]':'void (int) const' implicit_instantiation
@@ -25,7 +25,7 @@ void f() {
[[clang::annotate_type("foo")]]
[[clang::annotate_type("foo")]] { };
- // CHECK: CXXMethodDecl {{.*}} operator() 'void (int) const __attribute__((preserve_most)) {{\[}}[clang::annotate_type(...)]]':'void (int) __attribute__((preserve_most)) const' implicit_instantiation
+ // CHECK: CXXMethodDecl {{.*}} operator() '__attribute__((preserve_most)) void (int) const {{\[}}[clang::annotate_type(...)]]':'void (int) __attribute__((preserve_most)) const' implicit_instantiation
(void) [] (T) __attribute__((preserve_most))
[[clang::annotate_type("foo")]] { };
@@ -36,7 +36,7 @@ void f() {
// CHECK: CXXMethodDecl {{.*}} operator() 'void (int) const {{\[}}[clang::annotate_type(...)]]':'void (int) const' implicit_instantiation
(void) [] (T t) [[clang::annotate_type("foo", t)]] { };
- // CHECK: CXXMethodDecl {{.*}} operator() 'void (int) const __attribute__((preserve_most)) {{\[}}[clang::annotate_type(...)]]':'void (int) __attribute__((preserve_most)) const' implicit_instantiation
+ // CHECK: CXXMethodDecl {{.*}} operator() '__attribute__((preserve_most)) void (int) const {{\[}}[clang::annotate_type(...)]]':'void (int) __attribute__((preserve_most)) const' implicit_instantiation
(void) [] (T t) __attribute__((preserve_most))
[[clang::annotate_type("foo", t, t, t, t)]] { };
``````````
</details>
https://github.com/llvm/llvm-project/pull/147285
More information about the cfe-commits
mailing list