Index: cfe/trunk/include/clang/Basic/Attr.td =================================================================== --- cfe/trunk/include/clang/Basic/Attr.td +++ cfe/trunk/include/clang/Basic/Attr.td @@ -2796,6 +2796,20 @@ let Documentation = [TypeTagForDatatypeDocs]; } +def Owner : InheritableAttr { + let Spellings = [CXX11<"gsl", "Owner">]; + let Subjects = SubjectList<[Struct]>; + let Args = [TypeArgument<"DerefType", /*opt=*/1>]; + let Documentation = [LifetimeOwnerDocs]; +} + +def Pointer : InheritableAttr { + let Spellings = [CXX11<"gsl", "Pointer">]; + let Subjects = SubjectList<[Struct]>; + let Args = [TypeArgument<"DerefType", /*opt=*/1>]; + let Documentation = [LifetimePointerDocs]; +} + // Microsoft-related attributes def MSNoVTable : InheritableAttr, TargetSpecificAttr { Index: cfe/trunk/include/clang/Basic/AttrDocs.td =================================================================== --- cfe/trunk/include/clang/Basic/AttrDocs.td +++ cfe/trunk/include/clang/Basic/AttrDocs.td @@ -4230,3 +4230,71 @@ the initializer on host side. }]; } + +def LifetimeOwnerDocs : Documentation { + let Category = DocCatDecl; + let Content = [{ +.. Note:: This attribute is experimental and its effect on analysis is subject to change in + a future version of clang. + +The attribute ``[[gsl::Owner(T)]]`` applies to structs and classes that own an +object of type ``T``: + +.. code-block:: c++ + + class [[gsl::Owner(int)]] IntOwner { + private: + int value; + public: + int *getInt() { return &value; } + }; + +The argument ``T`` is optional and currently ignored. +This attribute may be used by analysis tools and has no effect on code +generation. + +See Pointer_ for an example. +}]; +} + +def LifetimePointerDocs : Documentation { + let Category = DocCatDecl; + let Content = [{ +.. Note:: This attribute is experimental and its effect on analysis is subject to change in + a future version of clang. + +The attribute ``[[gsl::Pointer(T)]]`` applies to structs and classes that behave +like pointers to an object of type ``T``: + +.. code-block:: c++ + + class [[gsl::Pointer(int)]] IntPointer { + private: + int *valuePointer; + public: + int *getInt() { return &valuePointer; } + }; + +The argument ``T`` is optional and currently ignored. +This attribute may be used by analysis tools and has no effect on code +generation. + +Example: +When constructing an instance of a class annotated like this (a Pointer) from +an instance of a class annotated with ``[[gsl::Owner]]`` (an Owner), +then the analysis will consider the Pointer to point inside the Owner. +When the Owner's lifetime ends, it will consider the Pointer to be dangling. + +.. code-block:: c++ + + int f() { + IntPointer P; + if (true) { + IntOwner O(7); + P = IntPointer(O); // P "points into" O + } // P is dangling + return P.get(); // error: Using a dangling Pointer. + } + +}]; +} Index: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td @@ -2519,6 +2519,9 @@ "'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< + "%select{'void'|a reference type|an array type|a non-vector or " + "non-vectorizable scalar type}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">; @@ -2567,8 +2570,6 @@ def err_init_priority_object_attr : Error< "can only use 'init_priority' attribute on file-scope definitions " "of objects of class type">; -def err_attribute_argument_vec_type_hint : Error< - "invalid attribute argument %0 - expecting a vector or vectorizable scalar type">; def err_attribute_argument_out_of_bounds : Error< "%0 attribute parameter %1 is out of bounds">; def err_attribute_only_once_per_parameter : Error< Index: cfe/trunk/lib/Parse/ParseDecl.cpp =================================================================== --- cfe/trunk/lib/Parse/ParseDecl.cpp +++ cfe/trunk/lib/Parse/ParseDecl.cpp @@ -335,6 +335,7 @@ ConsumeParen(); bool ChangeKWThisToIdent = attributeTreatsKeywordThisAsIdentifier(*AttrName); + bool AttributeIsTypeArgAttr = attributeIsTypeArgAttr(*AttrName); // Interpret "kw_this" as an identifier if the attributed requests it. if (ChangeKWThisToIdent && Tok.is(tok::kw_this)) @@ -360,6 +361,7 @@ ArgExprs.push_back(ParseIdentifierLoc()); } + ParsedType TheParsedType; if (!ArgExprs.empty() ? Tok.is(tok::comma) : Tok.isNot(tok::r_paren)) { // Eat the comma. if (!ArgExprs.empty()) @@ -372,8 +374,17 @@ Tok.setKind(tok::identifier); ExprResult ArgExpr; - if (Tok.is(tok::identifier) && - attributeHasVariadicIdentifierArg(*AttrName)) { + if (AttributeIsTypeArgAttr) { + TypeResult T = ParseTypeName(); + if (T.isInvalid()) { + SkipUntil(tok::r_paren, StopAtSemi); + return 0; + } + if (T.isUsable()) + TheParsedType = T.get(); + break; // FIXME: Multiple type arguments are not implemented. + } else if (Tok.is(tok::identifier) && + attributeHasVariadicIdentifierArg(*AttrName)) { ArgExprs.push_back(ParseIdentifierLoc()); } else { bool Uneval = attributeParsedArgsUnevaluated(*AttrName); @@ -397,14 +408,20 @@ SourceLocation RParen = Tok.getLocation(); if (!ExpectAndConsume(tok::r_paren)) { SourceLocation AttrLoc = ScopeLoc.isValid() ? ScopeLoc : AttrNameLoc; - Attrs.addNew(AttrName, SourceRange(AttrLoc, RParen), ScopeName, ScopeLoc, - ArgExprs.data(), ArgExprs.size(), Syntax); + + if (AttributeIsTypeArgAttr && !TheParsedType.get().isNull()) { + Attrs.addNewTypeAttr(AttrName, SourceRange(AttrNameLoc, RParen), + ScopeName, ScopeLoc, TheParsedType, Syntax); + } else { + Attrs.addNew(AttrName, SourceRange(AttrLoc, RParen), ScopeName, ScopeLoc, + ArgExprs.data(), ArgExprs.size(), Syntax); + } } if (EndLoc) *EndLoc = RParen; - return static_cast(ArgExprs.size()); + return static_cast(ArgExprs.size() + !TheParsedType.get().isNull()); } /// Parse the arguments to a parameterized GNU attribute or Index: cfe/trunk/lib/Sema/SemaDeclAttr.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaDeclAttr.cpp +++ cfe/trunk/lib/Sema/SemaDeclAttr.cpp @@ -2954,8 +2954,7 @@ if (!ParmType->isExtVectorType() && !ParmType->isFloatingType() && (ParmType->isBooleanType() || !ParmType->isIntegralType(S.getASTContext()))) { - S.Diag(AL.getLoc(), diag::err_attribute_argument_vec_type_hint) - << ParmType; + S.Diag(AL.getLoc(), diag::err_attribute_invalid_argument) << 3 << AL; return; } @@ -4555,6 +4554,67 @@ DiagnosticIdentifiers.size(), AL.getAttributeSpellingListIndex())); } +static void handleLifetimeCategoryAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + TypeSourceInfo *DerefTypeLoc = nullptr; + QualType ParmType; + if (AL.hasParsedType()) { + ParmType = S.GetTypeFromParser(AL.getTypeArg(), &DerefTypeLoc); + + unsigned SelectIdx = ~0U; + if (ParmType->isVoidType()) + SelectIdx = 0; + else if (ParmType->isReferenceType()) + SelectIdx = 1; + else if (ParmType->isArrayType()) + SelectIdx = 2; + + if (SelectIdx != ~0U) { + S.Diag(AL.getLoc(), diag::err_attribute_invalid_argument) + << SelectIdx << 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 *OAttr = D->getAttr()) { + const Type *ExistingDerefType = OAttr->getDerefTypeLoc() + ? OAttr->getDerefType().getTypePtr() + : nullptr; + if (ExistingDerefType != ParmType.getTypePtrOrNull()) { + S.Diag(AL.getLoc(), diag::err_attributes_are_not_compatible) + << AL << OAttr; + S.Diag(OAttr->getLocation(), diag::note_conflicting_attribute); + } + return; + } + D->addAttr(::new (S.Context) + OwnerAttr(AL.getRange(), S.Context, DerefTypeLoc, + AL.getAttributeSpellingListIndex())); + } else { + if (checkAttrMutualExclusion(S, D, AL)) + return; + if (const auto *PAttr = D->getAttr()) { + const Type *ExistingDerefType = PAttr->getDerefTypeLoc() + ? PAttr->getDerefType().getTypePtr() + : nullptr; + if (ExistingDerefType != ParmType.getTypePtrOrNull()) { + S.Diag(AL.getLoc(), diag::err_attributes_are_not_compatible) + << AL << PAttr; + S.Diag(PAttr->getLocation(), diag::note_conflicting_attribute); + } + return; + } + 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()) @@ -7158,6 +7218,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; Index: cfe/trunk/test/AST/ast-dump-attr.cpp =================================================================== --- cfe/trunk/test/AST/ast-dump-attr.cpp +++ cfe/trunk/test/AST/ast-dump-attr.cpp @@ -211,6 +211,23 @@ } } +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 + +class [[gsl::Pointer]] PointerWithoutArgument{}; +// CHECK: CXXRecordDecl{{.*}} class PointerWithoutArgument +// CHECK: PointerAttr + +class [[gsl::Owner]] OwnerWithoutArgument{}; +// CHECK: CXXRecordDecl{{.*}} class OwnerWithoutArgument +// CHECK: OwnerAttr +} // namespace TestLifetimeCategories + // 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)); Index: cfe/trunk/test/Misc/pragma-attribute-supported-attributes-list.test =================================================================== --- cfe/trunk/test/Misc/pragma-attribute-supported-attributes-list.test +++ cfe/trunk/test/Misc/pragma-attribute-supported-attributes-list.test @@ -117,8 +117,10 @@ // CHECK-NEXT: OpenCLNoSVM (SubjectMatchRule_variable) // CHECK-NEXT: OptimizeNone (SubjectMatchRule_function, SubjectMatchRule_objc_method) // CHECK-NEXT: Overloadable (SubjectMatchRule_function) +// CHECK-NEXT: Owner (SubjectMatchRule_record_not_is_union) // CHECK-NEXT: ParamTypestate (SubjectMatchRule_variable_is_parameter) // CHECK-NEXT: PassObjectSize (SubjectMatchRule_variable_is_parameter) +// CHECK-NEXT: Pointer (SubjectMatchRule_record_not_is_union) // CHECK-NEXT: RenderScriptKernel (SubjectMatchRule_function) // CHECK-NEXT: ReqdWorkGroupSize (SubjectMatchRule_function) // CHECK-NEXT: RequireConstantInit (SubjectMatchRule_variable_is_global) Index: cfe/trunk/test/SemaCXX/attr-gsl-owner-pointer.cpp =================================================================== --- cfe/trunk/test/SemaCXX/attr-gsl-owner-pointer.cpp +++ cfe/trunk/test/SemaCXX/attr-gsl-owner-pointer.cpp @@ -0,0 +1,107 @@ +// RUN: %clang_cc1 -verify -ast-dump %s | \ +// RUN: FileCheck --implicit-check-not OwnerAttr --implicit-check-not PointerAttr %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 structs}} + +union [[gsl::Owner(int)]] Union{}; +// expected-warning@-1 {{'Owner' attribute only applies to structs}} + +struct S { +}; + +S [[gsl::Owner]] Instance; +// expected-error@-1 {{'Owner' attribute cannot be applied to types}} + +class [[gsl::Owner(7)]] OwnerDerefNoType{}; +// expected-error@-1 {{expected a type}} + +class [[gsl::Pointer("int")]] PointerDerefNoType{}; +// expected-error@-1 {{expected a type}} + +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}} +// CHECK: CXXRecordDecl {{.*}} BothOwnerPointer +// CHECK: OwnerAttr {{.*}} int + +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::Pointer(int)]] AddConflictLater{}; +// CHECK: CXXRecordDecl {{.*}} AddConflictLater +// CHECK: PointerAttr {{.*}} int +class [[gsl::Owner(int)]] AddConflictLater; +// expected-error@-1 {{'Owner' and 'Pointer' attributes are not compatible}} +// expected-note@-5 {{conflicting attribute is here}} +// CHECK: CXXRecordDecl {{.*}} AddConflictLater +// CHECK: PointerAttr {{.*}} Inherited int + +class [[gsl::Owner(int)]] AddConflictLater2{}; +// CHECK: CXXRecordDecl {{.*}} AddConflictLater2 +// CHECK: OwnerAttr {{.*}} int +class [[gsl::Owner(float)]] AddConflictLater2; +// expected-error@-1 {{'Owner' and 'Owner' attributes are not compatible}} +// expected-note@-5 {{conflicting attribute is here}} +// CHECK: CXXRecordDecl {{.*}} AddConflictLater +// CHECK: OwnerAttr {{.*}} Inherited int + +class [[gsl::Owner()]] [[gsl::Owner(int)]] WithAndWithoutParameter{}; +// expected-error@-1 {{'Owner' and 'Owner' attributes are not compatible}} +// expected-note@-2 {{conflicting attribute is here}} +// CHECK: CXXRecordDecl {{.*}} WithAndWithoutParameter +// CHECK: OwnerAttr + +class [[gsl::Owner(int &)]] ReferenceType{}; +// expected-error@-1 {{a reference type is an invalid argument to attribute 'Owner'}} + +class [[gsl::Pointer(int[])]] ArrayType{}; +// expected-error@-1 {{an array type is an invalid argument to attribute 'Pointer'}} + +class [[gsl::Owner]] OwnerMissingParameter{}; +// CHECK: CXXRecordDecl {{.*}} OwnerMissingParameter +// CHECK: OwnerAttr + +class [[gsl::Pointer]] PointerMissingParameter{}; +// CHECK: CXXRecordDecl {{.*}} PointerMissingParameter +// CHECK: PointerAttr + +class [[gsl::Owner()]] OwnerWithEmptyParameterList{}; +// CHECK: CXXRecordDecl {{.*}} OwnerWithEmptyParameterList +// CHECK: OwnerAttr {{.*}} + +class [[gsl::Pointer()]] PointerWithEmptyParameterList{}; +// CHECK: CXXRecordDecl {{.*}} PointerWithEmptyParameterList +// CHECK: PointerAttr {{.*}} + +struct [[gsl::Owner(int)]] AnOwner{}; +// CHECK: CXXRecordDecl {{.*}} AnOwner +// CHECK: OwnerAttr {{.*}} int + +struct S; +class [[gsl::Pointer(S)]] APointer{}; +// CHECK: CXXRecordDecl {{.*}} APointer +// CHECK: PointerAttr {{.*}} S + +class [[gsl::Owner(int)]] [[gsl::Owner(int)]] DuplicateOwner{}; +// CHECK: CXXRecordDecl {{.*}} DuplicateOwner +// CHECK: OwnerAttr {{.*}} int + +class [[gsl::Pointer(int)]] [[gsl::Pointer(int)]] DuplicatePointer{}; +// CHECK: CXXRecordDecl {{.*}} DuplicatePointer +// CHECK: PointerAttr {{.*}} int + +class [[gsl::Owner(int)]] AddTheSameLater{}; +// CHECK: CXXRecordDecl {{.*}} AddTheSameLater +// CHECK: OwnerAttr {{.*}} int + +class [[gsl::Owner(int)]] AddTheSameLater; +// CHECK: CXXRecordDecl {{.*}} prev {{.*}} AddTheSameLater +// CHECK: OwnerAttr {{.*}} int Index: cfe/trunk/test/SemaOpenCL/invalid-kernel-attrs.cl =================================================================== --- cfe/trunk/test/SemaOpenCL/invalid-kernel-attrs.cl +++ cfe/trunk/test/SemaOpenCL/invalid-kernel-attrs.cl @@ -1,12 +1,12 @@ -// RUN: %clang_cc1 -verify %s +// RUN: %clang_cc1 -verify %s kernel __attribute__((vec_type_hint)) void kernel1() {} //expected-error{{'vec_type_hint' attribute takes one argument}} kernel __attribute__((vec_type_hint(not_type))) void kernel2() {} //expected-error{{unknown type name 'not_type'}} -kernel __attribute__((vec_type_hint(void))) void kernel3() {} //expected-error{{invalid attribute argument 'void' - expecting a vector or vectorizable scalar type}} +kernel __attribute__((vec_type_hint(void))) void kernel3() {} //expected-error{{a non-vector or non-vectorizable scalar type is an invalid argument to attribute 'vec_type_hint'}} -kernel __attribute__((vec_type_hint(bool))) void kernel4() {} //expected-error{{invalid attribute argument 'bool' - expecting a vector or vectorizable scalar type}} +kernel __attribute__((vec_type_hint(bool))) void kernel4() {} //expected-error{{a non-vector or non-vectorizable scalar type is an invalid argument to attribute 'vec_type_hint'}} kernel __attribute__((vec_type_hint(int))) __attribute__((vec_type_hint(float))) void kernel5() {} //expected-warning{{attribute 'vec_type_hint' is already applied with different parameters}} Index: cfe/trunk/utils/TableGen/ClangAttrEmitter.cpp =================================================================== --- cfe/trunk/utils/TableGen/ClangAttrEmitter.cpp +++ cfe/trunk/utils/TableGen/ClangAttrEmitter.cpp @@ -303,6 +303,8 @@ std::string getIsOmitted() const override { if (type == "IdentifierInfo *") return "!get" + getUpperName().str() + "()"; + if (type == "TypeSourceInfo *") + return "!get" + getUpperName().str() + "Loc()"; if (type == "ParamIdx") return "!get" + getUpperName().str() + "().isValid()"; return "false"; @@ -336,6 +338,8 @@ << " OS << \" \" << SA->get" << getUpperName() << "()->getName();\n"; } else if (type == "TypeSourceInfo *") { + if (isOptional()) + OS << " if (SA->get" << getUpperName() << "Loc())"; OS << " OS << \" \" << SA->get" << getUpperName() << "().getAsString();\n"; } else if (type == "bool") {