diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -4249,7 +4249,7 @@ int *getInt() { return &value; } }; -The argument ``T`` is optional and currently ignored. +The argument ``T`` is optional and is ignored. This attribute may be used by analysis tools and has no effect on code generation. @@ -4275,7 +4275,7 @@ int *getInt() { return &valuePointer; } }; -The argument ``T`` is optional and currently ignored. +The argument ``T`` is optional and is ignored. This attribute may be used by analysis tools and has no effect on code generation. 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 @@ -6116,6 +6116,18 @@ ClassTemplateSpecializationDecl *BaseTemplateSpec, SourceLocation BaseLoc); + /// Add gsl::Pointer attribute to std::container::iterator + /// \param ND The declaration that introduces the name + /// std::container::iterator. \param UnderlyingRecord The record named by ND. + void addDefaultGslPointerAttribute(NamedDecl *ND, + CXXRecordDecl *UnderlyingRecord); + + /// Add [[gsl::Owner]] and [[gsl::Pointer]] attributes for std:: types. + void addDefaultGslOwnerPointerAttribute(CXXRecordDecl *Record); + + /// Add [[gsl::Pointer]] attributes for std:: types. + void addDefaultGslPointerAttribute(TypedefNameDecl *TD); + void CheckCompletedCXXClass(CXXRecordDecl *Record); /// Check that the C++ class annoated with "trivial_abi" satisfies all the diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp --- a/clang/lib/Sema/SemaAttr.cpp +++ b/clang/lib/Sema/SemaAttr.cpp @@ -85,6 +85,126 @@ MSVtorDispAttr::CreateImplicit(Context, VtorDispStack.CurrentValue)); } +template +static void addGslOwnerPointerAttributeIfNotExisting(ASTContext &Context, + CXXRecordDecl *Record) { + CXXRecordDecl *Canonical = Record->getCanonicalDecl(); + if (Canonical->hasAttr() || Canonical->hasAttr()) + return; + + Canonical->addAttr(::new (Context) Attribute(SourceRange{}, Context, + /*DerefType*/ nullptr, + /*Spelling=*/0)); +} + +void Sema::addDefaultGslPointerAttribute(NamedDecl *ND, + CXXRecordDecl *UnderlyingRecord) { + if (!UnderlyingRecord) + return; + + const auto *Parent = dyn_cast(ND->getDeclContext()); + if (!Parent) + return; + + static llvm::StringSet<> Containers{ + "array", + "basic_string", + "deque", + "forward_list", + "vector", + "list", + "map", + "multiset", + "multimap", + "priority_queue", + "queue", + "set", + "stack", + "unordered_set", + "unordered_map", + "unordered_multiset", + "unordered_multimap", + }; + + static llvm::StringSet<> Iterators{"iterator", "const_iterator", + "reverse_iterator", + "const_reverse_iterator"}; + + if (Parent->isInStdNamespace() && Iterators.count(ND->getName()) && + Containers.count(Parent->getName())) + addGslOwnerPointerAttributeIfNotExisting(Context, + UnderlyingRecord); +} + +void Sema::addDefaultGslPointerAttribute(TypedefNameDecl *TD) { + + QualType Canonical = TD->getUnderlyingType().getCanonicalType(); + + CXXRecordDecl *RD = Canonical->getAsCXXRecordDecl(); + if (!RD) { + if (auto *TST = + dyn_cast(Canonical.getTypePtr())) { + + RD = dyn_cast_or_null( + TST->getTemplateName().getAsTemplateDecl()->getTemplatedDecl()); + } + } + + addDefaultGslPointerAttribute(TD, RD); +} + +void Sema::addDefaultGslOwnerPointerAttribute(CXXRecordDecl *Record) { + static llvm::StringSet<> StdOwners{ + "any", + "array", + "basic_regex", + "basic_string", + "deque", + "forward_list", + "vector", + "list", + "map", + "multiset", + "multimap", + "optional", + "priority_queue", + "queue", + "set", + "stack", + "unique_ptr", + "unordered_set", + "unordered_map", + "unordered_multiset", + "unordered_multimap", + "variant", + }; + static llvm::StringSet<> StdPointers{ + "basic_string_view", + "reference_wrapper", + "regex_iterator", + }; + + if (!Record->getIdentifier()) + return; + + // Handle classes that directly appear in std namespace. + if (Record->isInStdNamespace()) { + CXXRecordDecl *Canonical = Record->getCanonicalDecl(); + if (Canonical->hasAttr() || Canonical->hasAttr()) + return; + + if (StdOwners.count(Record->getName())) + addGslOwnerPointerAttributeIfNotExisting(Context, Record); + else if (StdPointers.count(Record->getName())) + addGslOwnerPointerAttributeIfNotExisting(Context, Record); + + return; + } + + // Handle nested classes that could be a gsl::Pointer. + addDefaultGslPointerAttribute(Record, Record); +} + void Sema::ActOnPragmaOptionsAlign(PragmaOptionsAlignKind Kind, SourceLocation PragmaLoc) { PragmaMsStackAction Action = Sema::PSK_Reset; 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 @@ -5841,6 +5841,8 @@ if (!Previous.empty()) { Redeclaration = true; MergeTypedefNameDecl(S, NewTD, Previous); + } else { + addDefaultGslPointerAttribute(NewTD); } if (ShadowedDecl && !Redeclaration) @@ -15114,6 +15116,9 @@ if (PrevDecl) mergeDeclAttributes(New, PrevDecl); + if (auto *CXXRD = dyn_cast(New)) + addDefaultGslOwnerPointerAttribute(CXXRD); + // If there's a #pragma GCC visibility in scope, set the visibility of this // record. AddPushedVisibilityAttribute(New); diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -1690,6 +1690,7 @@ mergeDeclAttributes(NewClass, PrevClassTemplate->getTemplatedDecl()); AddPushedVisibilityAttribute(NewClass); + addDefaultGslOwnerPointerAttribute(NewClass); if (TUK != TUK_Friend) { // Per C++ [basic.scope.temp]p2, skip the template parameter scopes. diff --git a/clang/test/SemaCXX/attr-gsl-owner-pointer-std.cpp b/clang/test/SemaCXX/attr-gsl-owner-pointer-std.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/attr-gsl-owner-pointer-std.cpp @@ -0,0 +1,129 @@ +// RUN: %clang_cc1 -ast-dump %s | \ +// RUN: FileCheck --implicit-check-not OwnerAttr --implicit-check-not PointerAttr %s + +// Test attribute inference for types in the standard library. +namespace std { +// Attributes are inferred for a (complete) class. +class any { + // CHECK: CXXRecordDecl {{.*}} any + // CHECK: OwnerAttr {{.*}} +}; + +// Attributes are inferred for instantiations of a complete template. +template +class vector { +public: + class iterator {}; + // CHECK: ClassTemplateDecl {{.*}} vector + // CHECK: OwnerAttr {{.*}} + // CHECK: CXXRecordDecl {{.*}} iterator + // CHECK: PointerAttr {{.*}} + // CHECK: ClassTemplateSpecializationDecl {{.*}} vector + // CHECK: TemplateArgument type 'int' + // CHECK: OwnerAttr + // CHECK: CXXRecordDecl {{.*}} iterator + // CHECK: PointerAttr {{.*}} +}; +static_assert(sizeof(vector), ""); // Force instantiation. +static_assert(sizeof(vector::iterator), ""); // Force instantiation. + +// If std::container::iterator is a using declaration, attributes are inferred +// for the underlying class. +template +class __set_iterator {}; +// CHECK: ClassTemplateDecl {{.*}} __set_iterator +// CHECK: PointerAttr +// CHECK: ClassTemplateSpecializationDecl {{.*}} __set_iterator +// CHECK: TemplateArgument type 'int' +// CHECK: PointerAttr + +template +class set { + // CHECK: ClassTemplateDecl {{.*}} set + // CHECK: OwnerAttr {{.*}} + // CHECK: ClassTemplateSpecializationDecl {{.*}} set + // CHECK: OwnerAttr {{.*}} +public: + using iterator = __set_iterator; +}; +static_assert(sizeof(set::iterator), ""); // Force instantiation. + +// If std::container::iterator is a typedef, attributes are inferred for the +// underlying class. +template +class __map_iterator {}; +// CHECK: ClassTemplateDecl {{.*}} __map_iterator +// CHECK: PointerAttr +// CHECK: ClassTemplateSpecializationDecl {{.*}} __map_iterator +// CHECK: TemplateArgument type 'int' +// CHECK: PointerAttr + +template +class map { + // CHECK: ClassTemplateDecl {{.*}} map + // CHECK: OwnerAttr {{.*}} + // CHECK: ClassTemplateSpecializationDecl {{.*}} map + // CHECK: OwnerAttr {{.*}} +public: + typedef __map_iterator iterator; +}; +static_assert(sizeof(map::iterator), ""); // Force instantiation. + +// Inline namespaces are ignored when checking if +// the class lives in the std namespace. +inline namespace inlinens { +template +class __unordered_map_iterator {}; +// CHECK: ClassTemplateDecl {{.*}} __unordered_map_iterator +// CHECK: PointerAttr +// CHECK: ClassTemplateSpecializationDecl {{.*}} __unordered_map_iterator +// CHECK: TemplateArgument type 'int' +// CHECK: PointerAttr + +template +class unordered_map { + // CHECK: ClassTemplateDecl {{.*}} unordered_map + // CHECK: OwnerAttr {{.*}} + // CHECK: ClassTemplateSpecializationDecl {{.*}} unordered_map + // CHECK: OwnerAttr {{.*}} +public: + typedef __unordered_map_iterator iterator; +}; +static_assert(sizeof(unordered_map::iterator), ""); // Force instantiation. +} // namespace inlinens + +// std::list has an implicit gsl::Owner attribute, +// but explicit attributes take precedence. +template +class [[gsl::Pointer]] list{}; +// CHECK: ClassTemplateDecl {{.*}} list +// CHECK: PointerAttr {{.*}} +// CHECK: ClassTemplateSpecializationDecl {{.*}} list +// CHECK: PointerAttr {{.*}} + +static_assert(sizeof(list), ""); // Force instantiation. + +// Forward declared template (Owner). +template < + class CharT, + class Traits> +class basic_regex; +// CHECK: ClassTemplateDecl {{.*}} basic_regex +// CHECK: OwnerAttr {{.*}} + +// Forward declared template (Pointer). +template +class reference_wrapper; +// CHECK: ClassTemplateDecl {{.*}} reference_wrapper +// CHECK: PointerAttr {{.*}} + +class some_unknown_type; +// CHECK: CXXRecordDecl {{.*}} some_unknown_type + +} // namespace std + +namespace user { +// If a class is not in the std namespace, we don't infer the attributes. +class any { +}; +} // namespace user