diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -330,11 +330,11 @@ ``__BASE_FILE__`` Defined to a string that contains the name of the main input file passed to Clang. - + ``__FILE_NAME__`` Clang-specific extension that functions similar to ``__FILE__`` but only renders the last path component (the filename) instead of an invocation - dependent full path to that file. + dependent full path to that file. ``__COUNTER__`` Defined to an integer value that starts at zero and is incremented each time @@ -2542,6 +2542,13 @@ in the analyzer's `list of source-level annotations `_. +Use ``__is_gsl_pointer(T)`` to check if the type ``T`` is a pointer, reference +or a class with an implicit or explicit ``[[gsl::Pointer]]`` attribute. Clang +implicitly adds that attribute to suitable classes from namespace ``std``. + +Use ``__is_gsl_owner(T)`` to check if the type ``T`` a class with an implicit +or explicit ``[[gsl::Owner]]`` attribute. Clang implicitly adds that attribute +to suitable classes from namespace ``std``. Extensions for Dynamic Analysis =============================== diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -482,6 +482,8 @@ TYPE_TRAIT_1(__is_trivially_copyable, IsTriviallyCopyable, KEYCXX) TYPE_TRAIT_2(__is_trivially_assignable, IsTriviallyAssignable, KEYCXX) TYPE_TRAIT_2(__reference_binds_to_temporary, ReferenceBindsToTemporary, KEYCXX) +TYPE_TRAIT_1(__is_gsl_owner, IsGslOwner, KEYCXX) +TYPE_TRAIT_1(__is_gsl_pointer, IsGslPointer, KEYCXX) KEYWORD(__underlying_type , KEYCXX) // Embarcadero Expression Traits diff --git a/clang/include/clang/Basic/TypeTraits.h b/clang/include/clang/Basic/TypeTraits.h --- a/clang/include/clang/Basic/TypeTraits.h +++ b/clang/include/clang/Basic/TypeTraits.h @@ -44,6 +44,8 @@ UTT_IsFloatingPoint, UTT_IsFunction, UTT_IsFundamental, + UTT_IsGslOwner, + UTT_IsGslPointer, UTT_IsIntegral, UTT_IsInterfaceClass, UTT_IsLiteral, 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 @@ -6085,6 +6085,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::Owner]] and [[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,114 @@ 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) { + addDefaultGslPointerAttribute( + TD, TD->getUnderlyingType().getCanonicalType()->getAsCXXRecordDecl()); +} + +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 @@ -15070,6 +15070,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/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -4381,6 +4381,9 @@ // This type trait always returns false, checking the type is moot. case UTT_IsInterfaceClass: + + case UTT_IsGslOwner: + case UTT_IsGslPointer: return true; // C++14 [meta.unary.prop]: @@ -4875,6 +4878,16 @@ return !T->isIncompleteType(); case UTT_HasUniqueObjectRepresentations: return C.hasUniqueObjectRepresentations(T); + case UTT_IsGslOwner: + if (const CXXRecordDecl *RD = T->getAsCXXRecordDecl()) + return RD->getCanonicalDecl()->hasAttr(); + return false; + case UTT_IsGslPointer: + if (T->hasPointerRepresentation()) + return true; + if (const CXXRecordDecl *RD = T->getAsCXXRecordDecl()) + return RD->getCanonicalDecl()->hasAttr(); + return false; } } 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 @@ -1686,6 +1686,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/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -699,6 +699,7 @@ } SemaRef.InstantiateAttrs(TemplateArgs, D, Typedef); + SemaRef.addDefaultGslPointerAttribute(Typedef); Typedef->setAccess(D->getAccess()); 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,126 @@ +// 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: 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: 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: 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