diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -2766,6 +2766,20 @@ let Documentation = [TypeTagForDatatypeDocs]; } +def Owner : InheritableAttr { + let Spellings = [CXX11<"gsl", "Owner">]; + let Subjects = SubjectList<[CXXRecord]>; + let Args = [TypeArgument<"DerefType">]; + let Documentation = [LifetimeOwnerDocs]; +} + +def Pointer : InheritableAttr { + let Subjects = SubjectList<[CXXRecord]>; + let Spellings = [CXX11<"gsl", "Pointer">]; + let Args = [TypeArgument<"DerefType">]; + let Documentation = [LifetimePointerDocs]; +} + // Microsoft-related attributes def MSNoVTable : InheritableAttr, TargetSpecificAttr { 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 @@ -4157,3 +4157,29 @@ ``__attribute__((malloc))``. }]; } + +def LifetimeOwnerDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +When annotating a class ``O`` with ``[[gsl::Owner(T)]]``, then each function +that returns cv-qualified ``T&`` or ``T*`` is assumed to return a +pointer/reference to the data owned by ``O``. The owned data is assumed to end +its lifetime once the owning object's lifetime end. + +This attribute may be used by analysis tools but will not have effect on code +generation. +}]; +} + +def LifetimePointerDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +When annotating a class ``P`` with ``[[gsl::Pointer(T)]]``, it assumed to be a +non-owning type whose objects can point to ``T`` type objects or dangle. +This attribute may be used by analysis tools but will not have effect on code +generation. + +For example ``std::vector::iterator`` might be annotated +``[[gsl::Pointer(int)]]``. +}]; +} \ No newline at end of file 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 @@ -2513,6 +2513,8 @@ "'NSObject' attribute is for pointer types only">; def err_attributes_are_not_compatible : Error< "%0 and %1 attributes are not compatible">; +def err_attribute_invalid_argument : Error< + "%0 is an invalid argument to attribute %1">; def err_attribute_wrong_number_arguments : Error< "%0 attribute %plural{0:takes no arguments|1:takes one argument|" ":requires exactly %1 arguments}1">; diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -331,6 +331,15 @@ IdentifierInfo *AttrName, SourceLocation AttrNameLoc, ParsedAttributes &Attrs, SourceLocation *EndLoc, IdentifierInfo *ScopeName, SourceLocation ScopeLoc, ParsedAttr::Syntax Syntax) { + + if (attributeIsTypeArgAttr(*AttrName)) { + ParseAttributeWithTypeArg(*AttrName, AttrNameLoc, Attrs, EndLoc, ScopeName, + ScopeLoc, Syntax); + // FIXME: when attributeIsTypeArgAttr() is true, assumes that the attribute + // takes a single parameter. + return 1; + } + // Ignore the left paren location for now. ConsumeParen(); 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 @@ -2614,6 +2614,11 @@ // C's _Noreturn is allowed to be added to a function after it is defined. ++I; continue; + } else if (isa(NewAttribute) || isa(NewAttribute)) { + // gsl::Owner and gsl::Pointer are allowed to be added tp a class after it + // is defined. + ++I; + continue; } else if (const AlignedAttr *AA = dyn_cast(NewAttribute)) { if (AA->isAlignas()) { // C++11 [dcl.align]p6: diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -4533,6 +4533,54 @@ DiagnosticIdentifiers.size(), AL.getAttributeSpellingListIndex())); } +static void handleLifetimeCategoryAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + // Only one lifetime attribute is allowed for a specific Decl node. + if (checkAttrMutualExclusion(S, D, AL) || + checkAttrMutualExclusion(S, D, AL)) + return; + + if (!AL.hasParsedType()) { + S.Diag(AL.getLoc(), diag::err_attribute_wrong_number_arguments) << AL << 1; + return; + } + + TypeSourceInfo *DerefTypeLoc = nullptr; + QualType ParmType = S.GetTypeFromParser(AL.getTypeArg(), &DerefTypeLoc); + assert(DerefTypeLoc && "no type source info for attribute argument"); + + if (ParmType->isVoidType()) { + S.Diag(AL.getLoc(), diag::err_attribute_invalid_argument) << "'void'" << AL; + return; + } + + // To check if earlier decl attributes do not conflict the newly parsed ones + // we always add (and check) the attribute to the cannonical decl. + D = D->getCanonicalDecl(); + if(AL.getKind() == ParsedAttr::AT_Owner) { + if (checkAttrMutualExclusion(S, D, AL)) + return; + if (const auto *Attr = D->getAttr()) { + if (Attr->getDerefType().getTypePtr() != ParmType.getTypePtr()) { + S.Diag(AL.getLoc(), diag::err_attributes_are_not_compatible) << AL << Attr; + S.Diag(Attr->getLocation(), diag::note_conflicting_attribute); + } + } + D->addAttr(::new (S.Context) OwnerAttr( + AL.getRange(), S.Context, DerefTypeLoc, AL.getAttributeSpellingListIndex())); + } else { + if (checkAttrMutualExclusion(S, D, AL)) + return; + if (const auto *Attr = D->getAttr()) { + if (Attr->getDerefType().getTypePtr() != ParmType.getTypePtr()) { + S.Diag(AL.getLoc(), diag::err_attributes_are_not_compatible) << AL << Attr; + S.Diag(Attr->getLocation(), diag::note_conflicting_attribute); + } + } + D->addAttr(::new (S.Context) PointerAttr( + AL.getRange(), S.Context, DerefTypeLoc, AL.getAttributeSpellingListIndex())); + } +} + bool Sema::CheckCallingConvAttr(const ParsedAttr &Attrs, CallingConv &CC, const FunctionDecl *FD) { if (Attrs.isInvalid()) @@ -7110,6 +7158,10 @@ case ParsedAttr::AT_Suppress: handleSuppressAttr(S, D, AL); break; + case ParsedAttr::AT_Owner: + case ParsedAttr::AT_Pointer: + handleLifetimeCategoryAttr(S, D, AL); + break; case ParsedAttr::AT_OpenCLKernel: handleSimpleAttribute(S, D, AL); break; diff --git a/clang/test/AST/ast-dump-attr.cpp b/clang/test/AST/ast-dump-attr.cpp --- a/clang/test/AST/ast-dump-attr.cpp +++ b/clang/test/AST/ast-dump-attr.cpp @@ -211,6 +211,15 @@ } } +namespace TestLifetimeCategories { + class [[gsl::Owner(int)]] AOwner {}; + // CHECK: CXXRecordDecl{{.*}} class AOwner + // CHECK: OwnerAttr {{.*}} int + class [[gsl::Pointer(int)]] APointer {}; + // CHECK: CXXRecordDecl{{.*}} class APointer + // CHECK: PointerAttr {{.*}} int +} + // Verify the order of attributes in the Ast. It must reflect the order // in the parsed source. int mergeAttrTest() __attribute__((deprecated)) __attribute__((warn_unused_result)); diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -116,8 +116,10 @@ // CHECK-NEXT: OpenCLNoSVM (SubjectMatchRule_variable) // CHECK-NEXT: OptimizeNone (SubjectMatchRule_function, SubjectMatchRule_objc_method) // CHECK-NEXT: Overloadable (SubjectMatchRule_function) +// CHECK-NEXT: Owner (SubjectMatchRule_record) // CHECK-NEXT: ParamTypestate (SubjectMatchRule_variable_is_parameter) // CHECK-NEXT: PassObjectSize (SubjectMatchRule_variable_is_parameter) +// CHECK-NEXT: Pointer (SubjectMatchRule_record) // CHECK-NEXT: RenderScriptKernel (SubjectMatchRule_function) // CHECK-NEXT: ReqdWorkGroupSize (SubjectMatchRule_function) // CHECK-NEXT: RequireConstantInit (SubjectMatchRule_variable_is_global) diff --git a/clang/test/SemaCXX/attr-gsl-owner-pointer.cpp b/clang/test/SemaCXX/attr-gsl-owner-pointer.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/attr-gsl-owner-pointer.cpp @@ -0,0 +1,64 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +int [[gsl::Owner]] i; +// expected-error@-1 {{'Owner' attribute cannot be applied to types}} +void [[gsl::Owner]] f(); +// expected-error@-1 {{'Owner' attribute cannot be applied to types}} + +[[gsl::Owner]] void f(); + // expected-warning@-1 {{'Owner' attribute only applies to classes}} + +struct S { +}; + +S [[gsl::Owner]] Instance; +// expected-error@-1 {{'Owner' attribute cannot be applied to types}} + +class [[gsl::Owner]] OwnerMissingParameter {}; +// expected-error@-1 {{'Owner' attribute takes one argument}} +class [[gsl::Pointer]] PointerMissingParameter {}; +// expected-error@-1 {{'Pointer' attribute takes one argument}} + +class [[gsl::Owner(7)]] OwnerDerefNoType {}; +// expected-error@-1 {{expected a type}} expected-error@-1 {{expected ')'}} +// expected-note@-2 {{to match this '('}} + +class [[gsl::Pointer("int")]] PointerDerefNoType {}; +// expected-error@-1 {{expected a type}} expected-error@-1 {{expected ')'}} +// expected-note@-2 {{to match this '('}} + +class [[gsl::Owner(int)]] [[gsl::Pointer(int)]] BothOwnerPointer {}; +// expected-error@-1 {{'Pointer' and 'Owner' attributes are not compatible}} +// expected-note@-2 {{conflicting attribute is here}} + +class [[gsl::Owner(int)]] [[gsl::Owner(int)]] DuplicateOwner {}; +// expected-error@-1 {{'Owner' and 'Owner' attributes are not compatible}} +// expected-note@-2 {{conflicting attribute is here}} + +class [[gsl::Pointer(int)]] [[gsl::Pointer(int)]] DuplicatePointer {}; +// expected-error@-1 {{'Pointer' and 'Pointer' attributes are not compatible}} +// expected-note@-2 {{conflicting attribute is here}} + +class [[gsl::Owner(void)]] OwnerVoidDerefType {}; +// expected-error@-1 {{'void' is an invalid argument to attribute 'Owner'}} +class [[gsl::Pointer(void)]] PointerVoidDerefType {}; +// expected-error@-1 {{'void' is an invalid argument to attribute 'Pointer'}} + +class [[gsl::Owner(int)]] AnOwner {}; +class [[gsl::Pointer(S)]] APointer {}; + +class AddOwnerLater {}; +class [[gsl::Owner(int)]] AddOwnerLater; + +class [[gsl::Pointer(int)]] AddConflictLater {}; +class [[gsl::Owner(int)]] AddConflictLater; +// expected-error@-1 {{'Owner' and 'Pointer' attributes are not compatible}} +// expected-note@-3 {{conflicting attribute is here}} + +class [[gsl::Owner(int)]] AddConflictLater2 {}; +class [[gsl::Owner(float)]] AddConflictLater2; +// expected-error@-1 {{'Owner' and 'Owner' attributes are not compatible}} +// expected-note@-3 {{conflicting attribute is here}} + +class [[gsl::Owner(int)]] AddTheSameLater {}; +class [[gsl::Owner(int)]] AddTheSameLater; \ No newline at end of file