diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 1a3247bc3eb60..f404adc087626 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -5079,6 +5079,23 @@ bool Sema::MergeCompatibleFunctionDecls(FunctionDecl *New, FunctionDecl *Old, if (getLangOpts().CPlusPlus) return MergeCXXFunctionDecl(New, Old, S); + // In BoundsSafety, we allow merging bounds attributes between function decls + // if the old decl is implicit. This enables us to add bounds attributes to a + // function decl if the old one is implicit without causing 'incompatible + // function decl' error. For example, we can redeclare the implicit decl of + // `memcmp`, and add `__sized_by()` attributes to its parameters. + // In attribute-only mode, those attributes are sugars though, and will be + // dropped by `Context.mergeTypes()` below. If both canonical types are the + // same, we can keep the new type to retain sugars. + if (getLangOpts().BoundsSafetyAttributes) { + if (Old->isImplicit() && + Old->getType().getCanonicalType() == + New->getType().getCanonicalType() && + !Context.mergeTypes(Old->getType(), New->getType()).isNull()) { + return false; + } + } + // Merge the function types so the we get the composite types for the return // and argument types. Per C11 6.2.7/4, only update the type if the old decl // was visible. diff --git a/clang/test/BoundsSafety/AST/merge-implicit.c b/clang/test/BoundsSafety/AST/merge-implicit.c new file mode 100644 index 0000000000000..a549d268d7fbf --- /dev/null +++ b/clang/test/BoundsSafety/AST/merge-implicit.c @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fsyntax-only -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fsyntax-only -fbounds-safety -x c++ -fexperimental-bounds-safety-cxx -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fsyntax-only -fbounds-safety -x objective-c -fexperimental-bounds-safety-objc -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fsyntax-only -fexperimental-bounds-safety-attributes -x c -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fsyntax-only -fexperimental-bounds-safety-attributes -x c++ -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c++ -ast-dump %s 2>&1 | FileCheck %s + +#include +#include + +// CHECK: FunctionDecl {{.+}} memcmp 'int (const void *[[single:(__single)?]] __sized_by(n), const void *[[single]] __sized_by(n), size_t)' +// CHECK-NEXT: ParmVarDecl [[s1:0x[^ ]+]] {{.+}} s1 'const void *[[single]] __sized_by(n)':'const void *[[single]]' +// CHECK-NEXT: ParmVarDecl [[s2:0x[^ ]+]] {{.+}} s2 'const void *[[single]] __sized_by(n)':'const void *[[single]]' +// CHECK-NEXT: ParmVarDecl {{.+}} used n 'size_t':'unsigned long' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit [[s1]] [[s2]] 0 0 +int memcmp(const void *__sized_by(n) s1, const void *__sized_by(n) s2, size_t n); + +// CHECK: FunctionDecl {{.+}} memcpy 'void *[[single]] __sized_by(n)(void *[[single]] __sized_by(n), const void *[[single]] __sized_by(n), size_t)' +// CHECK-NEXT: ParmVarDecl [[dst:0x[^ ]+]] {{.+}} dst 'void *[[single]] __sized_by(n)':'void *[[single]]' +// CHECK-NEXT: ParmVarDecl [[src:0x[^ ]+]] {{.+}} src 'const void *[[single]] __sized_by(n)':'const void *[[single]]' +// CHECK-NEXT: ParmVarDecl {{.+}} used n 'size_t':'unsigned long' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} <> Implicit [[dst]] [[src]] 0 0 +void *__sized_by(n) memcpy(void *__sized_by(n) dst, const void *__sized_by(n) src, size_t n);