diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 0ea6f93a1f5df..c6153ab97283f 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 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/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 03b0122d1c08f..e89657e154e9a 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; +def warn_const_attr_with_pure_attr : Warning< + "'const' attribute imposes more restrictions; 'pure' attribute ignored">, + InGroup; +def warn_pure_function_returns_void : Warning< + "'%select{pure|const}0' attribute on function returning 'void'; attribute ignored">, + InGroup; + 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 5472b43aafd4f..971b0e2aa4aaa 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -11801,6 +11801,32 @@ static bool CheckMultiVersionFunction(Sema &S, FunctionDecl *NewFD, OldDecl, Previous); } +static void CheckConstPureAttributesUsage(Sema &S, FunctionDecl *NewFD) { + bool IsPure = NewFD->hasAttr(); + bool IsConst = NewFD->hasAttr(); + + // 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(); + } + + // 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(); + } +} + /// Perform semantic checking of a new function declaration. /// /// Performs semantic analysis of the new function declaration @@ -11898,6 +11924,8 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD, NewFD->setInvalidDecl(); } + CheckConstPureAttributesUsage(*this, NewFD); + // 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/Analysis/call-invalidation.cpp b/clang/test/Analysis/call-invalidation.cpp index ef6505e19cf80..727217f228b05 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/function-attributes.c b/clang/test/CodeGen/function-attributes.c index 845f3baf7a4ee..bb8670a8530e0 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{{.*}} i32 @f13() [[NUW_OS_RN:#[0-9]+]] +int f13(void) __attribute__((const)); +int f13(void){ return 0; } // [irgen] clang isn't setting the optsize bit on functions diff --git a/clang/test/CodeGen/pragma-weak.c b/clang/test/CodeGen/pragma-weak.c index 52328bf9ff1be..ea508a485ef61 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 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)) void __xxx(void) { } -// CHECK: void @__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/Import/attr/Inputs/S.cpp b/clang/test/Import/attr/Inputs/S.cpp index 28d70c544a7ca..cf9af91838e8e 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 c9b2d6ed3433a..9e2d6ee1cbac9 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 char 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 a5d10a1835914..0a9b0bf50aabe 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)); +char pure_fn() __attribute__((pure)); +char const_fn() __attribute__((const)); void noduplicate_fn() __attribute__((noduplicate)); enum __attribute((flag_enum)) FlagEnum { diff --git a/clang/test/Interpreter/disambiguate-decl-stmt.cpp b/clang/test/Interpreter/disambiguate-decl-stmt.cpp index a49d7013c540a..1f4d5e267288b 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/Sema/attr-print.c b/clang/test/Sema/attr-print.c index ffa27de4a5d30..8492356e5d2e5 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 new file mode 100644 index 0000000000000..69ae41c421300 --- /dev/null +++ b/clang/test/Sema/incorrect_pure.cpp @@ -0,0 +1,14 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +[[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{{'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}} +}; diff --git a/clang/test/SemaCXX/attr-print.cpp b/clang/test/SemaCXX/attr-print.cpp index dff290be696be..c7c58a25504ee 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/cxx0x-cursory-default-delete.cpp b/clang/test/SemaCXX/cxx0x-cursory-default-delete.cpp index 9d68a0e5d358f..6ae146f0d08c7 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/cxx11-attr-print.cpp b/clang/test/SemaCXX/cxx11-attr-print.cpp index 7095c462031d9..c988972aeb1a5 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. diff --git a/clang/test/SemaCXX/warn-unused-value-cxx11.cpp b/clang/test/SemaCXX/warn-unused-value-cxx11.cpp index a6f41c3fd6b3b..687278a98f4e1 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.