mgehre created this revision.
mgehre added a reviewer: gribozavr.
Herald added a project: clang.
This is the first part of work announced in
"[RFC] Adding lifetime analysis to clang" [0],
i.e. the addition of the [[gsl::Owner(T)]] and
[[gsl::Pointer(T)]] attributes, which
will enable user-defined types to participate in
the lifetime analysis (which will be part of the
next PR).
The type `T` here is called "DerefType" in the paper,
and denotes the type that an Owner owns and a Pointer
points to. E.g. `std::vector<int>` should be annotated
with `[[gsl::Owner(int)]]` and
a `std::vector<int>::iterator` with `[[gsl::Pointer(int)]]`.
We explicitly allow to add an annotation after
the definition of the class to allow adding annotations
to external source from by the user, e.g.
#include <vector>
namespace std {
template<typename T, typename Alloc>
class [[gsl::Owner(T)]] vector;
}
until we can ship a standard library with annotations.
[0] http://lists.llvm.org/pipermail/cfe-dev/2018-November/060355.html
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D63954
Files:
clang/include/clang/Basic/Attr.td
clang/include/clang/Basic/AttrDocs.td
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/lib/Parse/ParseDecl.cpp
clang/lib/Sema/SemaDecl.cpp
clang/lib/Sema/SemaDeclAttr.cpp
clang/test/AST/ast-dump-attr.cpp
clang/test/Misc/pragma-attribute-supported-attributes-list.test
clang/test/SemaCXX/attr-gsl-owner-pointer.cpp
Index: clang/test/SemaCXX/attr-gsl-owner-pointer.cpp
===================================================================
--- /dev/null
+++ 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
Index: clang/test/Misc/pragma-attribute-supported-attributes-list.test
===================================================================
--- clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ 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)
Index: clang/test/AST/ast-dump-attr.cpp
===================================================================
--- clang/test/AST/ast-dump-attr.cpp
+++ 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));
Index: clang/lib/Sema/SemaDeclAttr.cpp
===================================================================
--- clang/lib/Sema/SemaDeclAttr.cpp
+++ 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<OwnerAttr>(S, D, AL) ||
+ checkAttrMutualExclusion<PointerAttr>(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<PointerAttr>(S, D, AL))
+ return;
+ if (const auto *Attr = D->getAttr<OwnerAttr>()) {
+ 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<OwnerAttr>(S, D, AL))
+ return;
+ if (const auto *Attr = D->getAttr<PointerAttr>()) {
+ 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<OpenCLKernelAttr>(S, D, AL);
break;
Index: clang/lib/Sema/SemaDecl.cpp
===================================================================
--- clang/lib/Sema/SemaDecl.cpp
+++ 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<OwnerAttr>(NewAttribute) || isa<PointerAttr>(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<AlignedAttr>(NewAttribute)) {
if (AA->isAlignas()) {
// C++11 [dcl.align]p6:
Index: clang/lib/Parse/ParseDecl.cpp
===================================================================
--- clang/lib/Parse/ParseDecl.cpp
+++ 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();
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ 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">;
Index: clang/include/clang/Basic/AttrDocs.td
===================================================================
--- clang/include/clang/Basic/AttrDocs.td
+++ 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<int>::iterator`` might be annotated
+``[[gsl::Pointer(int)]]``.
+}];
+}
\ No newline at end of file
Index: clang/include/clang/Basic/Attr.td
===================================================================
--- clang/include/clang/Basic/Attr.td
+++ 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<TargetMicrosoftCXXABI> {
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits