diff --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td b/clang/include/clang/Basic/DiagnosticCommonKinds.td --- a/clang/include/clang/Basic/DiagnosticCommonKinds.td +++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td @@ -169,6 +169,11 @@ def err_duplicate_declspec : Error<"%sub{duplicate_declspec}0">; +def warn_redundant_weaker_declspec_constexpr : Warning< + "'%0' declaration specifier is redundant due to the presence " + "of stricter '%sub{select_constexpr_spec_kind}1' declaration specifier">, + InGroup; + def err_friend_decl_spec : Error<"'%0' is invalid in friend declarations">; def err_invalid_member_in_interface : Error< diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -641,6 +641,7 @@ TautologicalObjCBoolCompare]>; def HeaderHygiene : DiagGroup<"header-hygiene">; def DuplicateDeclSpecifier : DiagGroup<"duplicate-decl-specifier">; +def RedundantDeclSpecifier : DiagGroup<"redundant-decl-specifier">; def CompareDistinctPointerType : DiagGroup<"compare-distinct-pointer-types">; def GNUUnionCast : DiagGroup<"gnu-union-cast">; def GNUVariableSizedTypeNotAtEnd : DiagGroup<"gnu-variable-sized-type-not-at-end">; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2707,6 +2707,7 @@ static bool adjustContextForLocalExternDecl(DeclContext *&DC); void DiagnoseFunctionSpecifiers(const DeclSpec &DS); + void DiagnoseRedundantSpecifiers(QualType T, const DeclSpec &DS); NamedDecl *getShadowedDeclaration(const TypedefNameDecl *D, const LookupResult &R); NamedDecl *getShadowedDeclaration(const VarDecl *D, const LookupResult &R); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -6209,6 +6209,20 @@ diag::err_noreturn_non_function); } +/// Diagnose specifiers on a declaration of an identifier that +/// are redundant due to the presence of stricter specifiers +void Sema::DiagnoseRedundantSpecifiers(QualType T, const DeclSpec &DS) { + if (T->isFunctionType()) { + // [dcl.constexpr]p1: A function declared with the constexpr or consteval + // specifier is implicitly an inline function + if (DS.hasConstexprSpecifier() && DS.isInlineSpecified()) { + Diag(DS.getInlineSpecLoc(), + diag::warn_redundant_weaker_declspec_constexpr) + << "inline" << static_cast(DS.getConstexprSpecifier()); + } + } +} + NamedDecl* Sema::ActOnTypedefDeclarator(Scope* S, Declarator& D, DeclContext* DC, TypeSourceInfo *TInfo, LookupResult &Previous) { @@ -9046,6 +9060,8 @@ if (R.getCanonicalType()->castAs()->getCmseNSCallAttr()) Diag(D.getIdentifierLoc(), diag::err_function_decl_cmse_ns_call); + DiagnoseRedundantSpecifiers(R, D.getDeclSpec()); + SmallVector TemplateParamLists; for (TemplateParameterList *TPL : TemplateParamListsRef) TemplateParamLists.push_back(TPL); diff --git a/clang/test/AST/ast-dump-openmp-begin-declare-variant_10.c b/clang/test/AST/ast-dump-openmp-begin-declare-variant_10.c --- a/clang/test/AST/ast-dump-openmp-begin-declare-variant_10.c +++ b/clang/test/AST/ast-dump-openmp-begin-declare-variant_10.c @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fopenmp -verify -ast-dump %s | FileCheck %s --check-prefix=C -// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fopenmp -verify -ast-dump %s -x c++| FileCheck %s --check-prefix=CXX +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -Wno-redundant-decl-specifier -fopenmp -verify -ast-dump %s | FileCheck %s --check-prefix=C +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -Wno-redundant-decl-specifier -fopenmp -verify -ast-dump %s -x c++| FileCheck %s --check-prefix=CXX // expected-no-diagnostics #ifdef __cplusplus diff --git a/clang/test/Analysis/builtin_bitcast.cpp b/clang/test/Analysis/builtin_bitcast.cpp --- a/clang/test/Analysis/builtin_bitcast.cpp +++ b/clang/test/Analysis/builtin_bitcast.cpp @@ -3,7 +3,7 @@ template void clang_analyzer_dump(T); -__attribute__((always_inline)) static inline constexpr unsigned int _castf32_u32(float __A) { +__attribute__((always_inline)) static constexpr unsigned int _castf32_u32(float __A) { return __builtin_bit_cast(unsigned int, __A); // no-warning } diff --git a/clang/test/CXX/temp/temp.deduct.guide/p1.cpp b/clang/test/CXX/temp/temp.deduct.guide/p1.cpp --- a/clang/test/CXX/temp/temp.deduct.guide/p1.cpp +++ b/clang/test/CXX/temp/temp.deduct.guide/p1.cpp @@ -73,7 +73,8 @@ virtual A(int(&)[28]) -> A; // expected-error {{'virtual' can only appear on non-static member functions}} const A(int(&)[31]) -> A; // expected-error {{deduction guide cannot be declared 'const'}} -const volatile static constexpr inline A(int(&)[29]) -> A; // expected-error {{deduction guide cannot be declared 'static inline constexpr const volatile'}} +const volatile static constexpr inline A(int(&)[29]) -> A; // expected-error {{deduction guide cannot be declared 'static inline constexpr const volatile'}} \ + // expected-warning {{'inline' declaration specifier is redundant due to the presence of stricter 'constexpr' declaration specifier}} A(int(&)[30]) const -> A; // expected-error {{deduction guide cannot have 'const' qualifier}} diff --git a/clang/test/Headers/x86-intrinsics-headers-clean.cpp b/clang/test/Headers/x86-intrinsics-headers-clean.cpp --- a/clang/test/Headers/x86-intrinsics-headers-clean.cpp +++ b/clang/test/Headers/x86-intrinsics-headers-clean.cpp @@ -1,7 +1,8 @@ // Make sure the intrinsic headers compile cleanly with no warnings or errors. // RUN: %clang_cc1 -ffreestanding -triple x86_64-unknown-unknown -Wsystem-headers \ -// RUN: -Wcast-qual -fsyntax-only -flax-vector-conversions=none -x c++ -verify %s +// RUN: -Wno-redundant-decl-specifier -Wcast-qual -fsyntax-only \ +// RUN: -flax-vector-conversions=none -x c++ -verify %s // expected-no-diagnostics diff --git a/clang/test/Parser/cxx0x-decl.cpp b/clang/test/Parser/cxx0x-decl.cpp --- a/clang/test/Parser/cxx0x-decl.cpp +++ b/clang/test/Parser/cxx0x-decl.cpp @@ -172,6 +172,15 @@ consteval constinit int huh(); // expected-error {{cannot combine with previous 'consteval'}} } +namespace RedundantSpecifier { + consteval inline int f1(); // expected-warning {{'inline' declaration specifier is redundant due to the presence of stricter 'consteval' declaration specifier}} + inline constexpr int f2(); // expected-warning {{'inline' declaration specifier is redundant due to the presence of stricter 'constexpr' declaration specifier}} + inline constexpr inline int f3(); // expected-warning {{'inline' declaration specifier is redundant due to the presence of stricter 'constexpr' declaration specifier}} \ + // expected-warning {{duplicate 'inline' declaration specifier}} + template inline consteval int f4(T t); // expected-warning {{'inline' declaration specifier is redundant due to the presence of stricter 'consteval' declaration specifier}} + inline constexpr int i1 = 0; +} + namespace ColonColonDecltype { struct S { struct T {}; }; ::decltype(S())::T invalid; // expected-error {{expected unqualified-id}} diff --git a/clang/test/SemaCUDA/function-overload.cu b/clang/test/SemaCUDA/function-overload.cu --- a/clang/test/SemaCUDA/function-overload.cu +++ b/clang/test/SemaCUDA/function-overload.cu @@ -693,9 +693,9 @@ namespace TestImplicitHDWithHAndD { namespace X { inline double foo(double, double) { return 0;} - inline constexpr float foo(float, float) { return 1;} - inline constexpr long double foo(long double, long double) { return 2;} - template inline constexpr double foo(_Tp, _Up) { return 3;} + constexpr float foo(float, float) { return 1;} + constexpr long double foo(long double, long double) { return 2;} + template constexpr double foo(_Tp, _Up) { return 3;} }; using X::foo; inline __device__ double foo(double, double) { return 4;} diff --git a/clang/test/SemaCXX/builtin-is-constant-evaluated.cpp b/clang/test/SemaCXX/builtin-is-constant-evaluated.cpp --- a/clang/test/SemaCXX/builtin-is-constant-evaluated.cpp +++ b/clang/test/SemaCXX/builtin-is-constant-evaluated.cpp @@ -5,7 +5,7 @@ using size_t = decltype(sizeof(int)); namespace std { -inline constexpr bool is_constant_evaluated() noexcept { +constexpr bool is_constant_evaluated() noexcept { return __builtin_is_constant_evaluated(); } } // namespace std diff --git a/clang/test/SemaCXX/cxx2a-destroying-delete.cpp b/clang/test/SemaCXX/cxx2a-destroying-delete.cpp --- a/clang/test/SemaCXX/cxx2a-destroying-delete.cpp +++ b/clang/test/SemaCXX/cxx2a-destroying-delete.cpp @@ -10,7 +10,7 @@ explicit destroying_delete_t(__construct) {} }; - inline constexpr destroying_delete_t destroying_delete(destroying_delete_t::__construct()); + constexpr destroying_delete_t destroying_delete(destroying_delete_t::__construct()); } void operator delete(void*, std::destroying_delete_t); // ok, just a placement delete diff --git a/clang/test/SemaCXX/extended-usual-deallocation-functions.cpp b/clang/test/SemaCXX/extended-usual-deallocation-functions.cpp --- a/clang/test/SemaCXX/extended-usual-deallocation-functions.cpp +++ b/clang/test/SemaCXX/extended-usual-deallocation-functions.cpp @@ -16,7 +16,7 @@ explicit destroying_delete_t(__construct) {} }; -inline constexpr destroying_delete_t destroying_delete(destroying_delete_t::__construct()); +constexpr destroying_delete_t destroying_delete(destroying_delete_t::__construct()); } // FIXME: Should destroying delete really be on in all dialects by default? diff --git a/clang/test/SemaCXX/static-assert-cxx17.cpp b/clang/test/SemaCXX/static-assert-cxx17.cpp --- a/clang/test/SemaCXX/static-assert-cxx17.cpp +++ b/clang/test/SemaCXX/static-assert-cxx17.cpp @@ -15,7 +15,7 @@ }; template -inline constexpr bool constexpr_return_false() { +constexpr bool constexpr_return_false() { return false; }