diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -321,6 +321,11 @@ removed in the future once clang supports all such operations. - Added the ``-print-diagnostic-options`` option, which prints a list of warnings the compiler supports. +- Added the ``-Warray-parameter`` warning. It diagnoses differences between + array parameters between function redeclarations (arrays of different extents, + etc). This flag is related to the same flag in GCC, but is different in that + it does not accept an explicitly- specified warning level and use of this flag + has no effect on ``-Warray-bounds``. Deprecated Compiler Flags ------------------------- 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 @@ -31,6 +31,7 @@ def GNUAutoType : DiagGroup<"gnu-auto-type">; def ArrayBounds : DiagGroup<"array-bounds">; def ArrayBoundsPointerArithmetic : DiagGroup<"array-bounds-pointer-arithmetic">; +def ArrayParameter : DiagGroup<"array-parameter">; def AutoDisableVptrSanitizer : DiagGroup<"auto-disable-vptr-sanitizer">; def Availability : DiagGroup<"availability">; def Section : DiagGroup<"section">; @@ -978,6 +979,7 @@ ]>; def Most : DiagGroup<"most", [ + ArrayParameter, BoolOperation, CharSubscript, Comment, diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -9400,6 +9400,12 @@ def note_array_declared_here : Note< "array %0 declared here">; +def warn_inconsistent_array_form : Warning< + "argument %0 of type %1 with mismatched bound">, + InGroup, DefaultIgnore; +def note_previous_declaration_as : Note< + "previously declared as %0 here">; + def warn_printf_insufficient_data_args : Warning< "more '%%' conversions than data arguments">, InGroup; def warn_printf_data_arg_not_used : Warning< 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 @@ -3207,6 +3207,39 @@ if (!foundAny) newDecl->dropAttrs(); } +static bool EquivalentArrayTypes(QualType Old, QualType New, + const ASTContext &Ctx) { + + auto NoSizeInfo = [&Ctx](QualType Ty) { + if (Ty->isIncompleteArrayType() || Ty->isPointerType()) + return true; + if (const auto *VAT = Ctx.getAsVariableArrayType(Ty)) + return VAT->getSizeModifier() == ArrayType::ArraySizeModifier::Star; + return false; + }; + + // `type[]` is equivalent to `type *` and `type[*]`. + if (NoSizeInfo(Old) && NoSizeInfo(New)) + return true; + + // Don't try to compare VLA sizes, unless one of them has the star modifier. + if (Old->isVariableArrayType() && New->isVariableArrayType()) { + const auto *OldVAT = Ctx.getAsVariableArrayType(Old); + const auto *NewVAT = Ctx.getAsVariableArrayType(New); + if ((OldVAT->getSizeModifier() == ArrayType::ArraySizeModifier::Star) ^ + (NewVAT->getSizeModifier() == ArrayType::ArraySizeModifier::Star)) + return false; + return true; + } + + // Only compare size, ignore Size modifiers and CVR. + if (Old->isConstantArrayType() && New->isConstantArrayType()) + return Ctx.getAsConstantArrayType(Old)->getSize() == + Ctx.getAsConstantArrayType(New)->getSize(); + + return Old == New; +} + static void mergeParamDeclTypes(ParmVarDecl *NewParam, const ParmVarDecl *OldParam, Sema &S) { @@ -3232,6 +3265,19 @@ NewParam->setType(NewT); } } + const auto *OldParamDT = dyn_cast(OldParam->getType()); + const auto *NewParamDT = dyn_cast(NewParam->getType()); + if (OldParamDT && NewParamDT && + OldParamDT->getPointeeType() == NewParamDT->getPointeeType()) { + QualType OldParamOT = OldParamDT->getOriginalType(); + QualType NewParamOT = NewParamDT->getOriginalType(); + if (!EquivalentArrayTypes(OldParamOT, NewParamOT, S.getASTContext())) { + S.Diag(NewParam->getLocation(), diag::warn_inconsistent_array_form) + << NewParam << NewParamOT; + S.Diag(OldParam->getLocation(), diag::note_previous_declaration_as) + << OldParamOT; + } + } } namespace { diff --git a/clang/test/Misc/warning-wall.c b/clang/test/Misc/warning-wall.c --- a/clang/test/Misc/warning-wall.c +++ b/clang/test/Misc/warning-wall.c @@ -3,6 +3,7 @@ CHECK:-Wall CHECK-NEXT: -Wmost +CHECK-NEXT: -Warray-parameter CHECK-NEXT: -Wbool-operation CHECK-NEXT: -Wbitwise-instead-of-logical CHECK-NEXT: -Wchar-subscripts diff --git a/clang/test/Sema/array-parameter.c b/clang/test/Sema/array-parameter.c new file mode 100644 --- /dev/null +++ b/clang/test/Sema/array-parameter.c @@ -0,0 +1,35 @@ +// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fsyntax-only -Warray-parameter -verify %s +void f0(int a[]); +void f0(int *a); // no warning + +void f1(int a[]); // expected-note {{previously declared as 'int[]' here}} +void f1(int a[2]); // expected-warning {{argument 'a' of type 'int[2]' with mismatched bound}} + +void f2(int a[3]); // expected-note {{previously declared as 'int[3]' here}} +void f2(int a[2]); // expected-warning {{argument 'a' of type 'int[2]' with mismatched bound}} + +void f3(int a[const 2]); +void f3(int a[2]); // no warning + +void f4(int a[static 2]); +void f4(int a[2]); // no warning + +void f5(int a[restrict 2]); +void f5(int a[2]); // no warning + +void f6(int a[volatile 2]); +void f6(int a[2]); // no warning + +void f7(int a[*]); +void f7(int a[]); // no warning + +void f8(int n, int a[*]); // expected-note {{previously declared as 'int[*]' here}} +void f8(int n, int a[n]); // expected-warning {{argument 'a' of type 'int[n]' with mismatched bound}} + +void f9(int *a); +void f9(int a[2]); +void f9(int a[]); // expected-warning {{argument 'a' of type 'int[]' with mismatched bound}} + // expected-note@-2 {{previously declared as 'int[2]' here}} +void f9(int a[2]) // expected-warning {{argument 'a' of type 'int[2]' with mismatched bound}} + // expected-note@-3 {{previously declared as 'int[]' here}} +{} diff --git a/clang/test/Sema/array-parameter.cpp b/clang/test/Sema/array-parameter.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Sema/array-parameter.cpp @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fsyntax-only -Warray-parameter -verify %s + +template +void func(int i[10]); // expected-note {{previously declared as 'int[10]' here}} + +template +void func(int i[N]); // expected-warning {{argument 'i' of type 'int[N]' with mismatched bound}} + +template +void func(int (&Val)[N]); + +template <> +void func<10>(int (&Val)[10]) { +} + +static constexpr int Extent = 10; +void funk(int i[10]); +void funk(int i[Extent]); // no-warning