Add lifetime categories attributes

Summary:
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)]]`.

[0] http://lists.llvm.org/pipermail/cfe-dev/2018-November/060355.html

Reviewers: gribozavr

Subscribers: xazax.hun, cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D63954

llvm-svn: 367040
This commit is contained in:
Matthias Gehre 2019-07-25 17:50:51 +00:00
parent 393094859e
commit d293cbd5fd
10 changed files with 306 additions and 12 deletions

View File

@ -2796,6 +2796,20 @@ def TypeTagForDatatype : InheritableAttr {
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<TargetMicrosoftCXXABI> {

View File

@ -4230,3 +4230,71 @@ not initialized on device side. It has internal linkage and is initialized by
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.
}
}];
}

View File

@ -2519,6 +2519,9 @@ def err_nsobject_attribute : Error<
"'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_attribute_argument_out_of_range : Error<
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<

View File

@ -335,6 +335,7 @@ unsigned Parser::ParseAttributeArgsCommon(
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 @@ unsigned Parser::ParseAttributeArgsCommon(
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 @@ unsigned Parser::ParseAttributeArgsCommon(
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 @@ unsigned Parser::ParseAttributeArgsCommon(
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<unsigned>(ArgExprs.size());
return static_cast<unsigned>(ArgExprs.size() + !TheParsedType.get().isNull());
}
/// Parse the arguments to a parameterized GNU attribute or

View File

@ -2954,8 +2954,7 @@ static void handleVecTypeHint(Sema &S, Decl *D, const ParsedAttr &AL) {
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 @@ static void handleSuppressAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
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<PointerAttr>(S, D, AL))
return;
if (const auto *OAttr = D->getAttr<OwnerAttr>()) {
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<OwnerAttr>(S, D, AL))
return;
if (const auto *PAttr = D->getAttr<PointerAttr>()) {
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 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
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;

View File

@ -211,6 +211,23 @@ namespace TestSuppress {
}
}
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));

View File

@ -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)

View File

@ -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

View File

@ -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}}

View File

@ -303,6 +303,8 @@ namespace {
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 @@ namespace {
<< " 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") {