mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-01-26 19:36:28 +00:00
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:
parent
393094859e
commit
d293cbd5fd
@ -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> {
|
||||
|
@ -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.
|
||||
}
|
||||
|
||||
}];
|
||||
}
|
||||
|
@ -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<
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
|
@ -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)
|
||||
|
107
clang/test/SemaCXX/attr-gsl-owner-pointer.cpp
Normal file
107
clang/test/SemaCXX/attr-gsl-owner-pointer.cpp
Normal 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
|
@ -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}}
|
||||
|
||||
|
@ -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") {
|
||||
|
Loading…
x
Reference in New Issue
Block a user