[clang] Warning for incorrect useof 'pure' attribute (PR #78200)

via cfe-commits cfe-commits at lists.llvm.org
Fri Jan 19 06:44:22 PST 2024


https://github.com/kelbon updated https://github.com/llvm/llvm-project/pull/78200

>From fb05243d0c0c3702b1615239a9337df337ad0c7c Mon Sep 17 00:00:00 2001
From: Kelbon Nik <kelbonage at gmail.com>
Date: Mon, 15 Jan 2024 22:24:34 +0400
Subject: [PATCH 01/12] add warning and test

---
 clang/include/clang/Basic/DiagnosticGroups.td    | 1 +
 clang/include/clang/Basic/DiagnosticSemaKinds.td | 7 +++++++
 clang/lib/Sema/SemaDecl.cpp                      | 7 +++++++
 clang/test/Sema/incorrect_pure.cpp               | 7 +++++++
 4 files changed, 22 insertions(+)
 create mode 100644 clang/test/Sema/incorrect_pure.cpp

diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 6765721ae7002c..9fcf2be2e45458 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -414,6 +414,7 @@ def : DiagGroup<"c++2a-compat", [CXX20Compat]>;
 def : DiagGroup<"c++2a-compat-pedantic", [CXX20CompatPedantic]>;
 
 def ExitTimeDestructors : DiagGroup<"exit-time-destructors">;
+def IncorrectAttributeUsage : DiagGroup<"incorrect-attribute-usage">;
 def FlexibleArrayExtensions : DiagGroup<"flexible-array-extensions">;
 def FourByteMultiChar : DiagGroup<"four-char-constants">;
 def GlobalConstructors : DiagGroup<"global-constructors"> {
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 03b0122d1c08f7..1df075119a482f 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -692,6 +692,13 @@ def warn_maybe_falloff_nonvoid_function : Warning<
 def warn_falloff_nonvoid_function : Warning<
   "non-void function does not return a value">,
   InGroup<ReturnType>;
+def warn_pure_attr_on_cxx_constructor : Warning<
+  "constructor cannot be 'pure' (undefined behavior)">,
+  InGroup<IncorrectAttributeUsage>;
+def warn_pure_function_returns_void : Warning<
+  "'pure' attribute on function returning 'void'">,
+  InGroup<IncorrectAttributeUsage>;
+
 def err_maybe_falloff_nonvoid_block : Error<
   "non-void block does not return a value in all control paths">;
 def err_falloff_nonvoid_block : Error<
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 5472b43aafd4f3..69ce7c50764fac 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -11898,6 +11898,13 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
     NewFD->setInvalidDecl();
   }
 
+  if (NewFD->hasAttr<PureAttr>() || NewFD->hasAttr<ConstAttr>()) {
+    if (isa<CXXConstructorDecl>(NewFD))
+      Diag(NewFD->getLocation(), diag::warn_pure_attr_on_cxx_constructor);
+    else if (NewFD->getReturnType()->isVoidType())
+      Diag(NewFD->getLocation(), diag::warn_pure_function_returns_void);
+  }
+
   // C++11 [dcl.constexpr]p8:
   //   A constexpr specifier for a non-static member function that is not
   //   a constructor declares that member function to be const.
diff --git a/clang/test/Sema/incorrect_pure.cpp b/clang/test/Sema/incorrect_pure.cpp
new file mode 100644
index 00000000000000..ce02309f086386
--- /dev/null
+++ b/clang/test/Sema/incorrect_pure.cpp
@@ -0,0 +1,7 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+[[gnu::pure]] void foo(); // expected-warning{{'pure' attribute on function returning 'void'}}
+
+struct A {
+    [[gnu::pure]] A(); // expected-warning{{constructor cannot be 'pure' (undefined behavior)}}
+};

>From e89f96c76d730a3cd9e1647837366a38f88118a3 Mon Sep 17 00:00:00 2001
From: Kelbon Nik <kelbonage at gmail.com>
Date: Tue, 16 Jan 2024 00:03:47 +0400
Subject: [PATCH 02/12] fix old incorrect test

---
 clang/test/Analysis/call-invalidation.cpp           | 8 ++++----
 clang/test/CodeGen/pragma-weak.c                    | 6 +++---
 clang/test/Interpreter/disambiguate-decl-stmt.cpp   | 4 ++--
 clang/test/SemaCXX/cxx0x-cursory-default-delete.cpp | 2 +-
 clang/test/SemaCXX/warn-unused-value-cxx11.cpp      | 2 +-
 5 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/clang/test/Analysis/call-invalidation.cpp b/clang/test/Analysis/call-invalidation.cpp
index ef6505e19cf803..727217f228b054 100644
--- a/clang/test/Analysis/call-invalidation.cpp
+++ b/clang/test/Analysis/call-invalidation.cpp
@@ -90,8 +90,8 @@ void testConstReferenceStruct() {
 }
 
 
-void usePointerPure(int * const *) __attribute__((pure));
-void usePointerConst(int * const *) __attribute__((const));
+int usePointerPure(int * const *) __attribute__((pure));
+int usePointerConst(int * const *) __attribute__((const));
 
 void testPureConst() {
   extern int global;
@@ -104,11 +104,11 @@ void testPureConst() {
   clang_analyzer_eval(x == 42); // expected-warning{{TRUE}}
   clang_analyzer_eval(global == -5); // expected-warning{{TRUE}}
 
-  usePointerPure(&p);
+  (void)usePointerPure(&p);
   clang_analyzer_eval(x == 42); // expected-warning{{TRUE}}
   clang_analyzer_eval(global == -5); // expected-warning{{TRUE}}
 
-  usePointerConst(&p);
+  (void)usePointerConst(&p);
   clang_analyzer_eval(x == 42); // expected-warning{{TRUE}}
   clang_analyzer_eval(global == -5); // expected-warning{{TRUE}}
 
diff --git a/clang/test/CodeGen/pragma-weak.c b/clang/test/CodeGen/pragma-weak.c
index 52328bf9ff1be7..d4011628bda7f1 100644
--- a/clang/test/CodeGen/pragma-weak.c
+++ b/clang/test/CodeGen/pragma-weak.c
@@ -16,7 +16,7 @@
 // CHECK-DAG: @declfirstattr = weak{{.*}} alias void (), ptr @__declfirstattr
 // CHECK-DAG: @mix2 = weak{{.*}} alias void (), ptr @__mix2
 // CHECK-DAG: @a1 = weak{{.*}} alias void (), ptr @__a1
-// CHECK-DAG: @xxx = weak{{.*}} alias void (), ptr @__xxx
+// CHECK-DAG: @xxx = weak{{.*}} alias int (), ptr @__xxx
 // CHECK-DAG: @undecfunc_alias1 = weak{{.*}} alias void (), ptr @undecfunc
 // CHECK-DAG: @undecfunc_alias2 = weak{{.*}} alias void (), ptr @undecfunc
 // CHECK-DAG: @undecfunc_alias3 = weak{{.*}} alias void (), ptr @undecfunc
@@ -137,8 +137,8 @@ void __a1(void) {}
 // CHECK: define{{.*}} void @__a1() [[NI:#[0-9]+]]
 
 #pragma weak xxx = __xxx
-__attribute((pure,noinline,const)) void __xxx(void) { }
-// CHECK: void @__xxx() [[RN:#[0-9]+]]
+__attribute((pure,noinline,const)) int __xxx(void) { return 0; }
+// CHECK: int @__xxx() [[RN:#[0-9]+]]
 
 ///////////// PR28611: Try multiple aliases of same undeclared symbol or alias
 #pragma weak undecfunc_alias1 = undecfunc
diff --git a/clang/test/Interpreter/disambiguate-decl-stmt.cpp b/clang/test/Interpreter/disambiguate-decl-stmt.cpp
index a49d7013c540ac..1f4d5e267288bc 100644
--- a/clang/test/Interpreter/disambiguate-decl-stmt.cpp
+++ b/clang/test/Interpreter/disambiguate-decl-stmt.cpp
@@ -95,10 +95,10 @@ Ns::Ns::Fs();
 Ns::Ns::Ns();
 
 struct Attrs1 { Attrs1(); };
-Attrs1::Attrs1() __attribute((pure)) = default;
+Attrs1::Attrs1() __attribute((noreturn)) = default;
 
 struct Attrs2 { Attrs2(); };
-__attribute((pure)) Attrs2::Attrs2() = default;
+__attribute((noreturn)) Attrs2::Attrs2() = default;
 
 // Extra semicolon
 namespace N {};
diff --git a/clang/test/SemaCXX/cxx0x-cursory-default-delete.cpp b/clang/test/SemaCXX/cxx0x-cursory-default-delete.cpp
index 9d68a0e5d358f6..6ae146f0d08c7d 100644
--- a/clang/test/SemaCXX/cxx0x-cursory-default-delete.cpp
+++ b/clang/test/SemaCXX/cxx0x-cursory-default-delete.cpp
@@ -194,7 +194,7 @@ struct except_spec_d_match : except_spec_a, except_spec_b {
 // gcc-compatibility: allow attributes on default definitions
 // (but not normal definitions)
 struct S { S(); };
-S::S() __attribute((pure)) = default;
+S::S() __attribute((noreturn)) = default;
 
 using size_t = decltype(sizeof(0));
 void *operator new(size_t) = delete; // expected-error {{deleted definition must be first declaration}} expected-note {{implicit}}
diff --git a/clang/test/SemaCXX/warn-unused-value-cxx11.cpp b/clang/test/SemaCXX/warn-unused-value-cxx11.cpp
index a6f41c3fd6b3b2..687278a98f4e1a 100644
--- a/clang/test/SemaCXX/warn-unused-value-cxx11.cpp
+++ b/clang/test/SemaCXX/warn-unused-value-cxx11.cpp
@@ -1,6 +1,6 @@
 // RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify -Wunused-value %s
 
-void f() __attribute__((const));
+int f() __attribute__((const));
 
 namespace PR18571 {
 // Unevaluated contexts should not trigger unused result warnings.

>From 20f2f06a575e066276a853f1274fb0d202964318 Mon Sep 17 00:00:00 2001
From: Kelbon Nik <kelbonage at gmail.com>
Date: Wed, 17 Jan 2024 15:00:39 +0400
Subject: [PATCH 03/12] release notes, update warning message, handle pure &&
 const declarations

---
 clang/docs/ReleaseNotes.rst                   |  2 +-
 clang/include/clang/Basic/DiagnosticGroups.td |  1 -
 .../clang/Basic/DiagnosticSemaKinds.td        | 10 +++----
 clang/lib/Sema/SemaDecl.cpp                   | 27 ++++++++++++++-----
 clang/test/CodeGen/pragma-weak.c              |  6 ++---
 clang/test/Sema/attr-print.c                  |  8 +++---
 clang/test/Sema/incorrect_pure.cpp            | 11 ++++++--
 7 files changed, 43 insertions(+), 22 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 0ea6f93a1f5df9..c6153ab97283f2 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -571,7 +571,7 @@ Improvements to Clang's diagnostics
   and template friend declarations with a constraint that depends on a template parameter from an
   enclosing template must be a definition.
 - Clang now diagnoses function/variable templates that shadow their own template parameters, e.g. ``template<class T> void T();``.
-
+- Clang now diagnoses incorrect usage of ``const`` and ``pure`` attributes, so ``-Wignored-attributes`` diagnoses more cases.
 - Clang now emits more descriptive diagnostics for 'unusual' expressions (e.g. incomplete index
   expressions on matrix types or builtin functions without an argument list) as placement-args
   to new-expressions.
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 9fcf2be2e45458..6765721ae7002c 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -414,7 +414,6 @@ def : DiagGroup<"c++2a-compat", [CXX20Compat]>;
 def : DiagGroup<"c++2a-compat-pedantic", [CXX20CompatPedantic]>;
 
 def ExitTimeDestructors : DiagGroup<"exit-time-destructors">;
-def IncorrectAttributeUsage : DiagGroup<"incorrect-attribute-usage">;
 def FlexibleArrayExtensions : DiagGroup<"flexible-array-extensions">;
 def FourByteMultiChar : DiagGroup<"four-char-constants">;
 def GlobalConstructors : DiagGroup<"global-constructors"> {
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 1df075119a482f..ee74a8211c34af 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -692,12 +692,12 @@ def warn_maybe_falloff_nonvoid_function : Warning<
 def warn_falloff_nonvoid_function : Warning<
   "non-void function does not return a value">,
   InGroup<ReturnType>;
-def warn_pure_attr_on_cxx_constructor : Warning<
-  "constructor cannot be 'pure' (undefined behavior)">,
-  InGroup<IncorrectAttributeUsage>;
+def warn_const_attr_with_pure_attr : Warning<
+  "'const' attribute imposes more restrictions, 'pure' attribute ignored">,
+  InGroup<IgnoredAttributes>;
 def warn_pure_function_returns_void : Warning<
-  "'pure' attribute on function returning 'void'">,
-  InGroup<IncorrectAttributeUsage>;
+  "'%select{pure|const}0' attribute on function returning 'void', attribute ignored">,
+  InGroup<IgnoredAttributes>;
 
 def err_maybe_falloff_nonvoid_block : Error<
   "non-void block does not return a value in all control paths">;
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 69ce7c50764fac..b25a437f3a4a68 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -11801,6 +11801,26 @@ static bool CheckMultiVersionFunction(Sema &S, FunctionDecl *NewFD,
                                          OldDecl, Previous);
 }
 
+static void CheckFunctionDeclarationAttributesUsage(Sema &S,
+                                                    FunctionDecl *NewFD) {
+  bool IsPure = NewFD->hasAttr<PureAttr>();
+  bool IsConst = NewFD->hasAttr<ConstAttr>();
+
+  if (IsPure && IsConst) {
+    S.Diag(NewFD->getLocation(), diag::warn_const_attr_with_pure_attr);
+    NewFD->dropAttr<PureAttr>();
+  }
+  if (IsPure || IsConst) {
+    // Constructors and destructors are functions which return void, so are handled here as well.
+    if (NewFD->getReturnType()->isVoidType()) {
+      S.Diag(NewFD->getLocation(), diag::warn_pure_function_returns_void)
+          << IsConst;
+      NewFD->dropAttr<PureAttr>();
+      NewFD->dropAttr<ConstAttr>();
+    }
+  }
+}
+
 /// Perform semantic checking of a new function declaration.
 ///
 /// Performs semantic analysis of the new function declaration
@@ -11898,12 +11918,7 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
     NewFD->setInvalidDecl();
   }
 
-  if (NewFD->hasAttr<PureAttr>() || NewFD->hasAttr<ConstAttr>()) {
-    if (isa<CXXConstructorDecl>(NewFD))
-      Diag(NewFD->getLocation(), diag::warn_pure_attr_on_cxx_constructor);
-    else if (NewFD->getReturnType()->isVoidType())
-      Diag(NewFD->getLocation(), diag::warn_pure_function_returns_void);
-  }
+  CheckFunctionDeclarationAttributesUsage(*this, NewFD);
 
   // C++11 [dcl.constexpr]p8:
   //   A constexpr specifier for a non-static member function that is not
diff --git a/clang/test/CodeGen/pragma-weak.c b/clang/test/CodeGen/pragma-weak.c
index d4011628bda7f1..ea508a485ef612 100644
--- a/clang/test/CodeGen/pragma-weak.c
+++ b/clang/test/CodeGen/pragma-weak.c
@@ -16,7 +16,7 @@
 // CHECK-DAG: @declfirstattr = weak{{.*}} alias void (), ptr @__declfirstattr
 // CHECK-DAG: @mix2 = weak{{.*}} alias void (), ptr @__mix2
 // CHECK-DAG: @a1 = weak{{.*}} alias void (), ptr @__a1
-// CHECK-DAG: @xxx = weak{{.*}} alias int (), ptr @__xxx
+// CHECK-DAG: @xxx = weak{{.*}} alias i32 (), ptr @__xxx
 // CHECK-DAG: @undecfunc_alias1 = weak{{.*}} alias void (), ptr @undecfunc
 // CHECK-DAG: @undecfunc_alias2 = weak{{.*}} alias void (), ptr @undecfunc
 // CHECK-DAG: @undecfunc_alias3 = weak{{.*}} alias void (), ptr @undecfunc
@@ -137,8 +137,8 @@ void __a1(void) {}
 // CHECK: define{{.*}} void @__a1() [[NI:#[0-9]+]]
 
 #pragma weak xxx = __xxx
-__attribute((pure,noinline,const)) int __xxx(void) { return 0; }
-// CHECK: int @__xxx() [[RN:#[0-9]+]]
+__attribute((noinline,const)) int __xxx(void) { return 0; }
+// CHECK: i32 @__xxx() [[RN:#[0-9]+]]
 
 ///////////// PR28611: Try multiple aliases of same undeclared symbol or alias
 #pragma weak undecfunc_alias1 = undecfunc
diff --git a/clang/test/Sema/attr-print.c b/clang/test/Sema/attr-print.c
index ffa27de4a5d306..8492356e5d2e57 100644
--- a/clang/test/Sema/attr-print.c
+++ b/clang/test/Sema/attr-print.c
@@ -9,11 +9,11 @@ __declspec(align(4)) int y;
 // CHECK: short arr[3] __attribute__((aligned));
 short arr[3] __attribute__((aligned));
 
-// CHECK: void foo(void) __attribute__((const));
-void foo(void) __attribute__((const));
+// CHECK: int foo(void) __attribute__((const));
+int foo(void) __attribute__((const));
 
-// CHECK: void bar(void) __attribute__((__const));
-void bar(void) __attribute__((__const));
+// CHECK: int bar(void) __attribute__((__const));
+int bar(void) __attribute__((__const));
 
 // CHECK: int * __ptr32 p32;
 int * __ptr32 p32;
diff --git a/clang/test/Sema/incorrect_pure.cpp b/clang/test/Sema/incorrect_pure.cpp
index ce02309f086386..1fa17785ec3880 100644
--- a/clang/test/Sema/incorrect_pure.cpp
+++ b/clang/test/Sema/incorrect_pure.cpp
@@ -1,7 +1,14 @@
 // RUN: %clang_cc1 -fsyntax-only -verify %s
 
-[[gnu::pure]] void foo(); // expected-warning{{'pure' attribute on function returning 'void'}}
+[[gnu::pure]] void foo(); // expected-warning{{'pure' attribute on function returning 'void', attribute ignored}}
+
+[[gnu::const]] void bar(); // expected-warning{{'const' attribute on function returning 'void', attribute ignored}}
 
 struct A {
-    [[gnu::pure]] A(); // expected-warning{{constructor cannot be 'pure' (undefined behavior)}}
+    [[gnu::pure]] A(); // expected-warning{{'pure' attribute on function returning 'void', attribute ignored}}
+
+    [[gnu::const]] A(int); // expected-warning{{'const' attribute on function returning 'void', attribute ignored}}
+    [[gnu::pure]] ~A(); // expected-warning{{'pure' attribute on function returning 'void', attribute ignored}}
+
+    [[gnu::const]] [[gnu::pure]] int m(); // expected-warning{{'const' attribute imposes more restrictions, 'pure' attribute ignored}}
 };

>From 5305288eabce92b58ef4c5587015116e1c1dcbbe Mon Sep 17 00:00:00 2001
From: Kelbon Nik <kelbonage at gmail.com>
Date: Wed, 17 Jan 2024 22:12:22 +0400
Subject: [PATCH 04/12] format

---
 clang/lib/Sema/SemaDecl.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index b25a437f3a4a68..b18b0eb3d41db8 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -11811,7 +11811,8 @@ static void CheckFunctionDeclarationAttributesUsage(Sema &S,
     NewFD->dropAttr<PureAttr>();
   }
   if (IsPure || IsConst) {
-    // Constructors and destructors are functions which return void, so are handled here as well.
+    // Constructors and destructors are functions which return void, so are
+    // handled here as well.
     if (NewFD->getReturnType()->isVoidType()) {
       S.Diag(NewFD->getLocation(), diag::warn_pure_function_returns_void)
           << IsConst;

>From 74b54dfc5b0cc8e233852614e44e500599d1b4e7 Mon Sep 17 00:00:00 2001
From: Kelbon Nik <kelbonage at gmail.com>
Date: Wed, 17 Jan 2024 22:56:51 +0400
Subject: [PATCH 05/12] fix old tests

---
 clang/test/SemaCXX/attr-print.cpp       | 12 ++++++------
 clang/test/SemaCXX/cxx11-attr-print.cpp |  8 ++++----
 2 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/clang/test/SemaCXX/attr-print.cpp b/clang/test/SemaCXX/attr-print.cpp
index dff290be696be2..c7c58a25504eed 100644
--- a/clang/test/SemaCXX/attr-print.cpp
+++ b/clang/test/SemaCXX/attr-print.cpp
@@ -6,15 +6,15 @@ int x __attribute__((aligned(4)));
 // CHECK: __declspec(align(4)) int y;
 __declspec(align(4)) int y;
 
-// CHECK: void foo() __attribute__((const));
-void foo() __attribute__((const));
+// CHECK: int foo() __attribute__((const));
+int foo() __attribute__((const));
 
-// CHECK: void bar() __attribute__((__const));
-void bar() __attribute__((__const));
+// CHECK: int bar() __attribute__((__const));
+int bar() __attribute__((__const));
 
 // FIXME: Print this with correct format.
-// CHECK: void foo1() __attribute__((noinline)) __attribute__((pure));
-void foo1() __attribute__((noinline, pure));
+// CHECK: int foo1() __attribute__((noinline)) __attribute__((pure));
+int foo1() __attribute__((noinline, pure));
 
 // CHECK: typedef int Small1 __attribute__((mode(byte)));
 typedef int Small1 __attribute__((mode(byte)));
diff --git a/clang/test/SemaCXX/cxx11-attr-print.cpp b/clang/test/SemaCXX/cxx11-attr-print.cpp
index 7095c462031d93..c988972aeb1a5a 100644
--- a/clang/test/SemaCXX/cxx11-attr-print.cpp
+++ b/clang/test/SemaCXX/cxx11-attr-print.cpp
@@ -30,11 +30,11 @@ alignas(4) int cxx11_alignas;
 // CHECK: int c11_alignas _Alignas(int);
 _Alignas(int) int c11_alignas;
 
-// CHECK: void foo() __attribute__((const));
-void foo() __attribute__((const));
+// CHECK: int foo() __attribute__((const));
+int foo() __attribute__((const));
 
-// CHECK: void bar() __attribute__((__const));
-void bar() __attribute__((__const));
+// CHECK: int bar() __attribute__((__const));
+int bar() __attribute__((__const));
 
 // FIXME: It's unfortunate that the string literal prints with the below three
 // cases given that the string is only exposed via the [[nodiscard]] spelling.

>From 8db0b23f6d29451f7ebb795f1a657b3d06e324ee Mon Sep 17 00:00:00 2001
From: Kelbon Nik <kelbonage at gmail.com>
Date: Wed, 17 Jan 2024 23:28:46 +0400
Subject: [PATCH 06/12] old tests

---
 clang/test/CodeGen/function-attributes.c | 6 +++---
 clang/test/Import/attr/test.cpp          | 4 ++--
 clang/test/Index/attributes.c            | 4 ++--
 3 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/clang/test/CodeGen/function-attributes.c b/clang/test/CodeGen/function-attributes.c
index 845f3baf7a4eec..700fdebbcb9225 100644
--- a/clang/test/CodeGen/function-attributes.c
+++ b/clang/test/CodeGen/function-attributes.c
@@ -57,9 +57,9 @@ int f12(int arg) {
   return arg ? 0 : f10_t();
 }
 
-// CHECK: define{{.*}} void @f13() [[NUW_OS_RN:#[0-9]+]]
-void f13(void) __attribute__((pure)) __attribute__((const));
-void f13(void){}
+// CHECK: define{{.*}} int @f13() [[NUW_OS_RN:#[0-9]+]]
+int f13(void) __attribute__((const));
+int f13(void){}
 
 
 // [irgen] clang isn't setting the optsize bit on functions
diff --git a/clang/test/Import/attr/test.cpp b/clang/test/Import/attr/test.cpp
index c9b2d6ed3433ae..bd2f159041cef9 100644
--- a/clang/test/Import/attr/test.cpp
+++ b/clang/test/Import/attr/test.cpp
@@ -14,13 +14,13 @@
 // CHECK-NEXT: LoopHintAttr
 // CHECK-SAME: line:10:9
 
-extern void f() __attribute__((const));
+extern int f() __attribute__((const));
 
 struct S;
 
 void stmt();
 
 void expr() {
-  f();
+  (void)f();
   struct S s;
 }
diff --git a/clang/test/Index/attributes.c b/clang/test/Index/attributes.c
index a5d10a18359143..a3a5fef9cc4f14 100644
--- a/clang/test/Index/attributes.c
+++ b/clang/test/Index/attributes.c
@@ -4,8 +4,8 @@ struct __attribute__((packed)) Test2 {
   char a;
 };
 
-void pure_fn() __attribute__((pure));
-void const_fn() __attribute__((const));
+int pure_fn() __attribute__((pure));
+int const_fn() __attribute__((const));
 void noduplicate_fn() __attribute__((noduplicate));
 
 enum __attribute((flag_enum)) FlagEnum {

>From d3e838072708b3474e1818a553973263107b4c8c Mon Sep 17 00:00:00 2001
From: Kelbon Nik <kelbonage at gmail.com>
Date: Thu, 18 Jan 2024 13:23:35 +0400
Subject: [PATCH 07/12] old tests (its hard)

---
 clang/test/CodeGen/function-attributes.c | 2 +-
 clang/test/Import/attr/Inputs/S.cpp      | 2 +-
 clang/test/Import/attr/test.cpp          | 2 +-
 clang/test/Index/attributes.c            | 4 ++--
 4 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/clang/test/CodeGen/function-attributes.c b/clang/test/CodeGen/function-attributes.c
index 700fdebbcb9225..a841a0e5f43daa 100644
--- a/clang/test/CodeGen/function-attributes.c
+++ b/clang/test/CodeGen/function-attributes.c
@@ -59,7 +59,7 @@ int f12(int arg) {
 
 // CHECK: define{{.*}} int @f13() [[NUW_OS_RN:#[0-9]+]]
 int f13(void) __attribute__((const));
-int f13(void){}
+int f13(void){ return 0; }
 
 
 // [irgen] clang isn't setting the optsize bit on functions
diff --git a/clang/test/Import/attr/Inputs/S.cpp b/clang/test/Import/attr/Inputs/S.cpp
index 28d70c544a7ca7..cf9af91838e8e4 100644
--- a/clang/test/Import/attr/Inputs/S.cpp
+++ b/clang/test/Import/attr/Inputs/S.cpp
@@ -1,4 +1,4 @@
-extern void f() __attribute__((const));
+extern char f() __attribute__((const));
 
 struct S {
   struct {
diff --git a/clang/test/Import/attr/test.cpp b/clang/test/Import/attr/test.cpp
index bd2f159041cef9..9e2d6ee1cbac90 100644
--- a/clang/test/Import/attr/test.cpp
+++ b/clang/test/Import/attr/test.cpp
@@ -14,7 +14,7 @@
 // CHECK-NEXT: LoopHintAttr
 // CHECK-SAME: line:10:9
 
-extern int f() __attribute__((const));
+extern char f() __attribute__((const));
 
 struct S;
 
diff --git a/clang/test/Index/attributes.c b/clang/test/Index/attributes.c
index a3a5fef9cc4f14..0a9b0bf50aabed 100644
--- a/clang/test/Index/attributes.c
+++ b/clang/test/Index/attributes.c
@@ -4,8 +4,8 @@ struct __attribute__((packed)) Test2 {
   char a;
 };
 
-int pure_fn() __attribute__((pure));
-int const_fn() __attribute__((const));
+char pure_fn() __attribute__((pure));
+char const_fn() __attribute__((const));
 void noduplicate_fn() __attribute__((noduplicate));
 
 enum __attribute((flag_enum)) FlagEnum {

>From b7a960ee8ca4326b6991d739ae57ad3832ac9bb8 Mon Sep 17 00:00:00 2001
From: Kelbon Nik <kelbonage at gmail.com>
Date: Thu, 18 Jan 2024 13:32:42 +0400
Subject: [PATCH 08/12] drop attrs

---
 clang/lib/Sema/SemaDecl.cpp | 19 +++++++++----------
 1 file changed, 9 insertions(+), 10 deletions(-)

diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index b18b0eb3d41db8..f2fe596363f8b5 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -11808,17 +11808,16 @@ static void CheckFunctionDeclarationAttributesUsage(Sema &S,
 
   if (IsPure && IsConst) {
     S.Diag(NewFD->getLocation(), diag::warn_const_attr_with_pure_attr);
-    NewFD->dropAttr<PureAttr>();
+    NewFD->dropAttrs<PureAttr>();
   }
-  if (IsPure || IsConst) {
-    // Constructors and destructors are functions which return void, so are
-    // handled here as well.
-    if (NewFD->getReturnType()->isVoidType()) {
-      S.Diag(NewFD->getLocation(), diag::warn_pure_function_returns_void)
-          << IsConst;
-      NewFD->dropAttr<PureAttr>();
-      NewFD->dropAttr<ConstAttr>();
-    }
+  if (!IsPure && !IsConst)
+    return;
+  // Constructors and destructors are functions which return void, so are
+  // handled here as well.
+  if (NewFD->getReturnType()->isVoidType()) {
+    S.Diag(NewFD->getLocation(), diag::warn_pure_function_returns_void)
+        << IsConst;
+    NewFD->dropAttrs<PureAttr, ConstAttr>();
   }
 }
 

>From 004920550d0c341728f6123b41f3ebe6beb19edb Mon Sep 17 00:00:00 2001
From: Kelbon Nik <kelbonage at gmail.com>
Date: Thu, 18 Jan 2024 17:34:58 +0400
Subject: [PATCH 09/12] old test

---
 clang/test/CodeGen/function-attributes.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/test/CodeGen/function-attributes.c b/clang/test/CodeGen/function-attributes.c
index a841a0e5f43daa..bb8670a8530e09 100644
--- a/clang/test/CodeGen/function-attributes.c
+++ b/clang/test/CodeGen/function-attributes.c
@@ -57,7 +57,7 @@ int f12(int arg) {
   return arg ? 0 : f10_t();
 }
 
-// CHECK: define{{.*}} int @f13() [[NUW_OS_RN:#[0-9]+]]
+// CHECK: define{{.*}} i32 @f13() [[NUW_OS_RN:#[0-9]+]]
 int f13(void) __attribute__((const));
 int f13(void){ return 0; }
 

>From 76c8401fcef09cf2255117fd06e71be2765c2404 Mon Sep 17 00:00:00 2001
From: Kelbon Nik <kelbonage at gmail.com>
Date: Fri, 19 Jan 2024 18:34:11 +0400
Subject: [PATCH 10/12] rename helper function

---
 clang/include/clang/Basic/DiagnosticSemaKinds.td |  4 ++--
 clang/lib/Sema/SemaDecl.cpp                      | 16 +++++++++++-----
 2 files changed, 13 insertions(+), 7 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index ee74a8211c34af..e89657e154e9ab 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -693,10 +693,10 @@ def warn_falloff_nonvoid_function : Warning<
   "non-void function does not return a value">,
   InGroup<ReturnType>;
 def warn_const_attr_with_pure_attr : Warning<
-  "'const' attribute imposes more restrictions, 'pure' attribute ignored">,
+  "'const' attribute imposes more restrictions; 'pure' attribute ignored">,
   InGroup<IgnoredAttributes>;
 def warn_pure_function_returns_void : Warning<
-  "'%select{pure|const}0' attribute on function returning 'void', attribute ignored">,
+  "'%select{pure|const}0' attribute on function returning 'void'; attribute ignored">,
   InGroup<IgnoredAttributes>;
 
 def err_maybe_falloff_nonvoid_block : Error<
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index f2fe596363f8b5..e77ccc2e6e57ad 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -11801,17 +11801,23 @@ static bool CheckMultiVersionFunction(Sema &S, FunctionDecl *NewFD,
                                          OldDecl, Previous);
 }
 
-static void CheckFunctionDeclarationAttributesUsage(Sema &S,
-                                                    FunctionDecl *NewFD) {
+static void CheckConstPureAttributesUsage(Sema &S,
+                                          FunctionDecl *NewFD) {
   bool IsPure = NewFD->hasAttr<PureAttr>();
   bool IsConst = NewFD->hasAttr<ConstAttr>();
 
+  // If there are no pure or const attributes, there's nothing to check.
+  if (!IsPure && !IsConst)
+    return;
+
+  // If the function is marked both pure and const, we retain the const attribute
+  // because it makes stronger guarantees than the pure attribute, and we drop
+  // the pure attribute explicitly to prevent later confusion about semantics.
   if (IsPure && IsConst) {
     S.Diag(NewFD->getLocation(), diag::warn_const_attr_with_pure_attr);
     NewFD->dropAttrs<PureAttr>();
   }
-  if (!IsPure && !IsConst)
-    return;
+
   // Constructors and destructors are functions which return void, so are
   // handled here as well.
   if (NewFD->getReturnType()->isVoidType()) {
@@ -11918,7 +11924,7 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
     NewFD->setInvalidDecl();
   }
 
-  CheckFunctionDeclarationAttributesUsage(*this, NewFD);
+  CheckConstPureAttributesUsage(*this, NewFD);
 
   // C++11 [dcl.constexpr]p8:
   //   A constexpr specifier for a non-static member function that is not

>From a39e0913415c400a2e173c028ddbd0a7f9fdf98a Mon Sep 17 00:00:00 2001
From: Kelbon Nik <kelbonage at gmail.com>
Date: Fri, 19 Jan 2024 18:39:52 +0400
Subject: [PATCH 11/12] format

---
 clang/lib/Sema/SemaDecl.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index e77ccc2e6e57ad..539d3d45482fc0 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -11801,8 +11801,7 @@ static bool CheckMultiVersionFunction(Sema &S, FunctionDecl *NewFD,
                                          OldDecl, Previous);
 }
 
-static void CheckConstPureAttributesUsage(Sema &S,
-                                          FunctionDecl *NewFD) {
+static void CheckConstPureAttributesUsage(Sema &S, FunctionDecl *NewFD) {
   bool IsPure = NewFD->hasAttr<PureAttr>();
   bool IsConst = NewFD->hasAttr<ConstAttr>();
 

>From 40293d6df59fa6d0a9180416a6660740c1f0b28f Mon Sep 17 00:00:00 2001
From: Kelbon Nik <kelbonage at gmail.com>
Date: Fri, 19 Jan 2024 18:44:04 +0400
Subject: [PATCH 12/12] format

---
 clang/lib/Sema/SemaDecl.cpp | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 539d3d45482fc0..971b0e2aa4aaa3 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -11809,9 +11809,10 @@ static void CheckConstPureAttributesUsage(Sema &S, FunctionDecl *NewFD) {
   if (!IsPure && !IsConst)
     return;
 
-  // If the function is marked both pure and const, we retain the const attribute
-  // because it makes stronger guarantees than the pure attribute, and we drop
-  // the pure attribute explicitly to prevent later confusion about semantics.
+  // If the function is marked both pure and const, we retain the const
+  // attribute because it makes stronger guarantees than the pure attribute, and
+  // we drop the pure attribute explicitly to prevent later confusion about
+  // semantics.
   if (IsPure && IsConst) {
     S.Diag(NewFD->getLocation(), diag::warn_const_attr_with_pure_attr);
     NewFD->dropAttrs<PureAttr>();



More information about the cfe-commits mailing list