This revision was automatically updated to reflect the committed changes. Closed by commit rL368147: gsl::Owner/gsl::Pointer: Add implicit annotations for some std types (authored by mgehre, committed by ). Herald added a project: LLVM. Herald added a subscriber: llvm-commits.
Changed prior to commit: https://reviews.llvm.org/D64448?vs=212446&id=213836#toc Repository: rL LLVM CHANGES SINCE LAST ACTION https://reviews.llvm.org/D64448/new/ https://reviews.llvm.org/D64448 Files: cfe/trunk/include/clang/Basic/AttrDocs.td cfe/trunk/include/clang/Sema/Sema.h cfe/trunk/lib/Sema/SemaAttr.cpp cfe/trunk/lib/Sema/SemaDecl.cpp cfe/trunk/lib/Sema/SemaTemplate.cpp cfe/trunk/test/SemaCXX/attr-gsl-owner-pointer-std.cpp
Index: cfe/trunk/include/clang/Sema/Sema.h =================================================================== --- cfe/trunk/include/clang/Sema/Sema.h +++ cfe/trunk/include/clang/Sema/Sema.h @@ -6116,6 +6116,17 @@ 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 inferGslPointerAttribute(NamedDecl *ND, CXXRecordDecl *UnderlyingRecord); + + /// Add [[gsl::Owner]] and [[gsl::Pointer]] attributes for std:: types. + void inferGslOwnerPointerAttribute(CXXRecordDecl *Record); + + /// Add [[gsl::Pointer]] attributes for std:: types. + void inferGslPointerAttribute(TypedefNameDecl *TD); + void CheckCompletedCXXClass(CXXRecordDecl *Record); /// Check that the C++ class annoated with "trivial_abi" satisfies all the Index: cfe/trunk/include/clang/Basic/AttrDocs.td =================================================================== --- cfe/trunk/include/clang/Basic/AttrDocs.td +++ cfe/trunk/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. Index: cfe/trunk/test/SemaCXX/attr-gsl-owner-pointer-std.cpp =================================================================== --- cfe/trunk/test/SemaCXX/attr-gsl-owner-pointer-std.cpp +++ cfe/trunk/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 <typename T> +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<int>), ""); // Force instantiation. +static_assert(sizeof(vector<int>::iterator), ""); // Force instantiation. + +// If std::container::iterator is a using declaration, attributes are inferred +// for the underlying class. +template <typename T> +class __set_iterator {}; +// CHECK: ClassTemplateDecl {{.*}} __set_iterator +// CHECK: PointerAttr +// CHECK: ClassTemplateSpecializationDecl {{.*}} __set_iterator +// CHECK: TemplateArgument type 'int' +// CHECK: PointerAttr + +template <typename T> +class set { + // CHECK: ClassTemplateDecl {{.*}} set + // CHECK: OwnerAttr {{.*}} + // CHECK: ClassTemplateSpecializationDecl {{.*}} set + // CHECK: OwnerAttr {{.*}} +public: + using iterator = __set_iterator<T>; +}; +static_assert(sizeof(set<int>::iterator), ""); // Force instantiation. + +// If std::container::iterator is a typedef, attributes are inferred for the +// underlying class. +template <typename T> +class __map_iterator {}; +// CHECK: ClassTemplateDecl {{.*}} __map_iterator +// CHECK: PointerAttr +// CHECK: ClassTemplateSpecializationDecl {{.*}} __map_iterator +// CHECK: TemplateArgument type 'int' +// CHECK: PointerAttr + +template <typename T> +class map { + // CHECK: ClassTemplateDecl {{.*}} map + // CHECK: OwnerAttr {{.*}} + // CHECK: ClassTemplateSpecializationDecl {{.*}} map + // CHECK: OwnerAttr {{.*}} +public: + typedef __map_iterator<T> iterator; +}; +static_assert(sizeof(map<int>::iterator), ""); // Force instantiation. + +// Inline namespaces are ignored when checking if +// the class lives in the std namespace. +inline namespace inlinens { +template <typename T> +class __unordered_map_iterator {}; +// CHECK: ClassTemplateDecl {{.*}} __unordered_map_iterator +// CHECK: PointerAttr +// CHECK: ClassTemplateSpecializationDecl {{.*}} __unordered_map_iterator +// CHECK: TemplateArgument type 'int' +// CHECK: PointerAttr + +template <typename T> +class unordered_map { + // CHECK: ClassTemplateDecl {{.*}} unordered_map + // CHECK: OwnerAttr {{.*}} + // CHECK: ClassTemplateSpecializationDecl {{.*}} unordered_map + // CHECK: OwnerAttr {{.*}} +public: + typedef __unordered_map_iterator<T> iterator; +}; +static_assert(sizeof(unordered_map<int>::iterator), ""); // Force instantiation. +} // namespace inlinens + +// std::list has an implicit gsl::Owner attribute, +// but explicit attributes take precedence. +template <typename T> +class [[gsl::Pointer]] list{}; +// CHECK: ClassTemplateDecl {{.*}} list +// CHECK: PointerAttr {{.*}} +// CHECK: ClassTemplateSpecializationDecl {{.*}} list +// CHECK: PointerAttr {{.*}} + +static_assert(sizeof(list<int>), ""); // 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 T> +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 Index: cfe/trunk/lib/Sema/SemaAttr.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaAttr.cpp +++ cfe/trunk/lib/Sema/SemaAttr.cpp @@ -85,6 +85,126 @@ MSVtorDispAttr::CreateImplicit(Context, VtorDispStack.CurrentValue)); } +template <typename Attribute> +static void addGslOwnerPointerAttributeIfNotExisting(ASTContext &Context, + CXXRecordDecl *Record) { + CXXRecordDecl *Canonical = Record->getCanonicalDecl(); + if (Canonical->hasAttr<OwnerAttr>() || Canonical->hasAttr<PointerAttr>()) + return; + + Canonical->addAttr(::new (Context) Attribute(SourceRange{}, Context, + /*DerefType*/ nullptr, + /*Spelling=*/0)); +} + +void Sema::inferGslPointerAttribute(NamedDecl *ND, + CXXRecordDecl *UnderlyingRecord) { + if (!UnderlyingRecord) + return; + + const auto *Parent = dyn_cast<CXXRecordDecl>(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<PointerAttr>(Context, + UnderlyingRecord); +} + +void Sema::inferGslPointerAttribute(TypedefNameDecl *TD) { + + QualType Canonical = TD->getUnderlyingType().getCanonicalType(); + + CXXRecordDecl *RD = Canonical->getAsCXXRecordDecl(); + if (!RD) { + if (auto *TST = + dyn_cast<TemplateSpecializationType>(Canonical.getTypePtr())) { + + RD = dyn_cast_or_null<CXXRecordDecl>( + TST->getTemplateName().getAsTemplateDecl()->getTemplatedDecl()); + } + } + + inferGslPointerAttribute(TD, RD); +} + +void Sema::inferGslOwnerPointerAttribute(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<OwnerAttr>() || Canonical->hasAttr<PointerAttr>()) + return; + + if (StdOwners.count(Record->getName())) + addGslOwnerPointerAttributeIfNotExisting<OwnerAttr>(Context, Record); + else if (StdPointers.count(Record->getName())) + addGslOwnerPointerAttributeIfNotExisting<PointerAttr>(Context, Record); + + return; + } + + // Handle nested classes that could be a gsl::Pointer. + inferGslPointerAttribute(Record, Record); +} + void Sema::ActOnPragmaOptionsAlign(PragmaOptionsAlignKind Kind, SourceLocation PragmaLoc) { PragmaMsStackAction Action = Sema::PSK_Reset; Index: cfe/trunk/lib/Sema/SemaTemplate.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaTemplate.cpp +++ cfe/trunk/lib/Sema/SemaTemplate.cpp @@ -1690,6 +1690,7 @@ mergeDeclAttributes(NewClass, PrevClassTemplate->getTemplatedDecl()); AddPushedVisibilityAttribute(NewClass); + inferGslOwnerPointerAttribute(NewClass); if (TUK != TUK_Friend) { // Per C++ [basic.scope.temp]p2, skip the template parameter scopes. Index: cfe/trunk/lib/Sema/SemaDecl.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaDecl.cpp +++ cfe/trunk/lib/Sema/SemaDecl.cpp @@ -5858,6 +5858,8 @@ if (!Previous.empty()) { Redeclaration = true; MergeTypedefNameDecl(S, NewTD, Previous); + } else { + inferGslPointerAttribute(NewTD); } if (ShadowedDecl && !Redeclaration) @@ -15131,6 +15133,9 @@ if (PrevDecl) mergeDeclAttributes(New, PrevDecl); + if (auto *CXXRD = dyn_cast<CXXRecordDecl>(New)) + inferGslOwnerPointerAttribute(CXXRD); + // If there's a #pragma GCC visibility in scope, set the visibility of this // record. AddPushedVisibilityAttribute(New);
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits