https://github.com/th0br0 created 
https://github.com/llvm/llvm-project/pull/147285

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.

>From 2adbac2deeaf3279ef973e68ddffc12d23bf0193 Mon Sep 17 00:00:00 2001
From: "Andreas C. Osowski" <an...@google.com>
Date: Mon, 7 Jul 2025 13:38:30 +0200
Subject: [PATCH] [clang][AST] Fix positioning of preserve cconv attributes in
 TypePrinter

---
 clang/lib/AST/TypePrinter.cpp               | 33 ++++++++++++++-------
 clang/test/AST/ast-print-cconv-preserve.cpp | 14 +++++++++
 clang/test/Sema/preserve-call-conv.c        |  8 ++---
 clang/test/Sema/preserve-none-call-conv.c   |  4 +--
 clang/test/SemaCXX/lambda-attributes.cpp    |  6 ++--
 5 files changed, 46 insertions(+), 19 deletions(-)
 create mode 100644 clang/test/AST/ast-print-cconv-preserve.cpp

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)]] { };
 

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to