forked from OSchip/llvm-project
Model type attributes as regular Attrs.
Specifically, AttributedType now tracks a regular attr::Kind rather than having its own parallel Kind enumeration, and AttributedTypeLoc now holds an Attr* instead of holding an ad-hoc collection of Attr fields. Differential Revision: https://reviews.llvm.org/D50526 This reinstates r339623, reverted in r339638, with a fix to not fail template instantiation if we instantiate a QualType with no associated type source information and we encounter an AttributedType. llvm-svn: 340215
This commit is contained in:
parent
210ccfe3db
commit
e43e2b3667
|
@ -31,6 +31,7 @@
|
|||
#include "clang/AST/TemplateName.h"
|
||||
#include "clang/AST/Type.h"
|
||||
#include "clang/Basic/AddressSpaces.h"
|
||||
#include "clang/Basic/AttrKinds.h"
|
||||
#include "clang/Basic/IdentifierTable.h"
|
||||
#include "clang/Basic/LLVM.h"
|
||||
#include "clang/Basic/LangOptions.h"
|
||||
|
@ -1422,7 +1423,7 @@ public:
|
|||
|
||||
QualType getInjectedClassNameType(CXXRecordDecl *Decl, QualType TST) const;
|
||||
|
||||
QualType getAttributedType(AttributedType::Kind attrKind,
|
||||
QualType getAttributedType(attr::Kind attrKind,
|
||||
QualType modifiedType,
|
||||
QualType equivalentType);
|
||||
|
||||
|
|
|
@ -113,6 +113,19 @@ public:
|
|||
void printPretty(raw_ostream &OS, const PrintingPolicy &Policy) const;
|
||||
};
|
||||
|
||||
class TypeAttr : public Attr {
|
||||
protected:
|
||||
TypeAttr(attr::Kind AK, SourceRange R, unsigned SpellingListIndex,
|
||||
bool IsLateParsed)
|
||||
: Attr(AK, R, SpellingListIndex, IsLateParsed) {}
|
||||
|
||||
public:
|
||||
static bool classof(const Attr *A) {
|
||||
return A->getKind() >= attr::FirstTypeAttr &&
|
||||
A->getKind() <= attr::LastTypeAttr;
|
||||
}
|
||||
};
|
||||
|
||||
class StmtAttr : public Attr {
|
||||
protected:
|
||||
StmtAttr(attr::Kind AK, SourceRange R, unsigned SpellingListIndex,
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "clang/AST/NestedNameSpecifier.h"
|
||||
#include "clang/AST/TemplateName.h"
|
||||
#include "clang/Basic/AddressSpaces.h"
|
||||
#include "clang/Basic/AttrKinds.h"
|
||||
#include "clang/Basic/Diagnostic.h"
|
||||
#include "clang/Basic/ExceptionSpecificationType.h"
|
||||
#include "clang/Basic/LLVM.h"
|
||||
|
@ -1970,7 +1971,16 @@ public:
|
|||
bool isObjCQualifiedClassType() const; // Class<foo>
|
||||
bool isObjCObjectOrInterfaceType() const;
|
||||
bool isObjCIdType() const; // id
|
||||
bool isObjCInertUnsafeUnretainedType() const;
|
||||
|
||||
/// Was this type written with the special inert-in-ARC __unsafe_unretained
|
||||
/// qualifier?
|
||||
///
|
||||
/// This approximates the answer to the following question: if this
|
||||
/// translation unit were compiled in ARC, would this type be qualified
|
||||
/// with __unsafe_unretained?
|
||||
bool isObjCInertUnsafeUnretainedType() const {
|
||||
return hasAttr(attr::ObjCInertUnsafeUnretained);
|
||||
}
|
||||
|
||||
/// Whether the type is Objective-C 'id' or a __kindof type of an
|
||||
/// object type, e.g., __kindof NSView * or __kindof id
|
||||
|
@ -2184,6 +2194,10 @@ public:
|
|||
/// qualifiers from the outermost type.
|
||||
const ArrayType *castAsArrayTypeUnsafe() const;
|
||||
|
||||
/// Determine whether this type had the specified attribute applied to it
|
||||
/// (looking through top-level type sugar).
|
||||
bool hasAttr(attr::Kind AK) const;
|
||||
|
||||
/// Get the base element type of this type, potentially discarding type
|
||||
/// qualifiers. This should never be used when type qualifiers
|
||||
/// are meaningful.
|
||||
|
@ -4293,56 +4307,7 @@ public:
|
|||
/// - the canonical type is VectorType(16, int)
|
||||
class AttributedType : public Type, public llvm::FoldingSetNode {
|
||||
public:
|
||||
// It is really silly to have yet another attribute-kind enum, but
|
||||
// clang::attr::Kind doesn't currently cover the pure type attrs.
|
||||
enum Kind {
|
||||
// Expression operand.
|
||||
attr_address_space,
|
||||
attr_regparm,
|
||||
attr_vector_size,
|
||||
attr_neon_vector_type,
|
||||
attr_neon_polyvector_type,
|
||||
|
||||
FirstExprOperandKind = attr_address_space,
|
||||
LastExprOperandKind = attr_neon_polyvector_type,
|
||||
|
||||
// Enumerated operand (string or keyword).
|
||||
attr_objc_gc,
|
||||
attr_objc_ownership,
|
||||
attr_pcs,
|
||||
attr_pcs_vfp,
|
||||
|
||||
FirstEnumOperandKind = attr_objc_gc,
|
||||
LastEnumOperandKind = attr_pcs_vfp,
|
||||
|
||||
// No operand.
|
||||
attr_noreturn,
|
||||
attr_nocf_check,
|
||||
attr_cdecl,
|
||||
attr_fastcall,
|
||||
attr_stdcall,
|
||||
attr_thiscall,
|
||||
attr_regcall,
|
||||
attr_pascal,
|
||||
attr_swiftcall,
|
||||
attr_vectorcall,
|
||||
attr_inteloclbicc,
|
||||
attr_ms_abi,
|
||||
attr_sysv_abi,
|
||||
attr_preserve_most,
|
||||
attr_preserve_all,
|
||||
attr_ptr32,
|
||||
attr_ptr64,
|
||||
attr_sptr,
|
||||
attr_uptr,
|
||||
attr_nonnull,
|
||||
attr_ns_returns_retained,
|
||||
attr_nullable,
|
||||
attr_null_unspecified,
|
||||
attr_objc_kindof,
|
||||
attr_objc_inert_unsafe_unretained,
|
||||
attr_lifetimebound,
|
||||
};
|
||||
using Kind = attr::Kind;
|
||||
|
||||
private:
|
||||
friend class ASTContext; // ASTContext creates these
|
||||
|
@ -4350,7 +4315,7 @@ private:
|
|||
QualType ModifiedType;
|
||||
QualType EquivalentType;
|
||||
|
||||
AttributedType(QualType canon, Kind attrKind, QualType modified,
|
||||
AttributedType(QualType canon, attr::Kind attrKind, QualType modified,
|
||||
QualType equivalent)
|
||||
: Type(Attributed, canon, equivalent->isDependentType(),
|
||||
equivalent->isInstantiationDependentType(),
|
||||
|
@ -4399,13 +4364,13 @@ public:
|
|||
static Kind getNullabilityAttrKind(NullabilityKind kind) {
|
||||
switch (kind) {
|
||||
case NullabilityKind::NonNull:
|
||||
return attr_nonnull;
|
||||
return attr::TypeNonNull;
|
||||
|
||||
case NullabilityKind::Nullable:
|
||||
return attr_nullable;
|
||||
return attr::TypeNullable;
|
||||
|
||||
case NullabilityKind::Unspecified:
|
||||
return attr_null_unspecified;
|
||||
return attr::TypeNullUnspecified;
|
||||
}
|
||||
llvm_unreachable("Unknown nullability kind.");
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#ifndef LLVM_CLANG_AST_TYPELOC_H
|
||||
#define LLVM_CLANG_AST_TYPELOC_H
|
||||
|
||||
#include "clang/AST/Attr.h"
|
||||
#include "clang/AST/Decl.h"
|
||||
#include "clang/AST/NestedNameSpecifier.h"
|
||||
#include "clang/AST/TemplateBase.h"
|
||||
|
@ -849,16 +850,7 @@ class SubstTemplateTypeParmPackTypeLoc :
|
|||
};
|
||||
|
||||
struct AttributedLocInfo {
|
||||
union {
|
||||
Expr *ExprOperand;
|
||||
|
||||
/// A raw SourceLocation.
|
||||
unsigned EnumOperandLoc;
|
||||
};
|
||||
|
||||
SourceRange OperandParens;
|
||||
|
||||
SourceLocation AttrLoc;
|
||||
const Attr *TypeAttr;
|
||||
};
|
||||
|
||||
/// Type source information for an attributed type.
|
||||
|
@ -867,24 +859,10 @@ class AttributedTypeLoc : public ConcreteTypeLoc<UnqualTypeLoc,
|
|||
AttributedType,
|
||||
AttributedLocInfo> {
|
||||
public:
|
||||
AttributedType::Kind getAttrKind() const {
|
||||
attr::Kind getAttrKind() const {
|
||||
return getTypePtr()->getAttrKind();
|
||||
}
|
||||
|
||||
bool hasAttrExprOperand() const {
|
||||
return (getAttrKind() >= AttributedType::FirstExprOperandKind &&
|
||||
getAttrKind() <= AttributedType::LastExprOperandKind);
|
||||
}
|
||||
|
||||
bool hasAttrEnumOperand() const {
|
||||
return (getAttrKind() >= AttributedType::FirstEnumOperandKind &&
|
||||
getAttrKind() <= AttributedType::LastEnumOperandKind);
|
||||
}
|
||||
|
||||
bool hasAttrOperand() const {
|
||||
return hasAttrExprOperand() || hasAttrEnumOperand();
|
||||
}
|
||||
|
||||
bool isQualifier() const {
|
||||
return getTypePtr()->isQualifier();
|
||||
}
|
||||
|
@ -897,51 +875,16 @@ public:
|
|||
return getInnerTypeLoc();
|
||||
}
|
||||
|
||||
/// The location of the attribute name, i.e.
|
||||
/// __attribute__((regparm(1000)))
|
||||
/// ^~~~~~~
|
||||
SourceLocation getAttrNameLoc() const {
|
||||
return getLocalData()->AttrLoc;
|
||||
/// The type attribute.
|
||||
const Attr *getAttr() const {
|
||||
return getLocalData()->TypeAttr;
|
||||
}
|
||||
void setAttrNameLoc(SourceLocation loc) {
|
||||
getLocalData()->AttrLoc = loc;
|
||||
void setAttr(const Attr *A) {
|
||||
getLocalData()->TypeAttr = A;
|
||||
}
|
||||
|
||||
/// The attribute's expression operand, if it has one.
|
||||
/// void *cur_thread __attribute__((address_space(21)))
|
||||
/// ^~
|
||||
Expr *getAttrExprOperand() const {
|
||||
assert(hasAttrExprOperand());
|
||||
return getLocalData()->ExprOperand;
|
||||
}
|
||||
void setAttrExprOperand(Expr *e) {
|
||||
assert(hasAttrExprOperand());
|
||||
getLocalData()->ExprOperand = e;
|
||||
}
|
||||
|
||||
/// The location of the attribute's enumerated operand, if it has one.
|
||||
/// void * __attribute__((objc_gc(weak)))
|
||||
/// ^~~~
|
||||
SourceLocation getAttrEnumOperandLoc() const {
|
||||
assert(hasAttrEnumOperand());
|
||||
return SourceLocation::getFromRawEncoding(getLocalData()->EnumOperandLoc);
|
||||
}
|
||||
void setAttrEnumOperandLoc(SourceLocation loc) {
|
||||
assert(hasAttrEnumOperand());
|
||||
getLocalData()->EnumOperandLoc = loc.getRawEncoding();
|
||||
}
|
||||
|
||||
/// The location of the parentheses around the operand, if there is
|
||||
/// an operand.
|
||||
/// void * __attribute__((objc_gc(weak)))
|
||||
/// ^ ^
|
||||
SourceRange getAttrOperandParensRange() const {
|
||||
assert(hasAttrOperand());
|
||||
return getLocalData()->OperandParens;
|
||||
}
|
||||
void setAttrOperandParensRange(SourceRange range) {
|
||||
assert(hasAttrOperand());
|
||||
getLocalData()->OperandParens = range;
|
||||
template<typename T> const T *getAttrAs() {
|
||||
return dyn_cast_or_null<T>(getAttr());
|
||||
}
|
||||
|
||||
SourceRange getLocalSourceRange() const {
|
||||
|
@ -954,21 +897,11 @@ public:
|
|||
// ^~ ~~
|
||||
// That enclosure doesn't necessarily belong to a single attribute
|
||||
// anyway.
|
||||
SourceRange range(getAttrNameLoc());
|
||||
if (hasAttrOperand())
|
||||
range.setEnd(getAttrOperandParensRange().getEnd());
|
||||
return range;
|
||||
return getAttr() ? getAttr()->getRange() : SourceRange();
|
||||
}
|
||||
|
||||
void initializeLocal(ASTContext &Context, SourceLocation loc) {
|
||||
setAttrNameLoc(loc);
|
||||
if (hasAttrExprOperand()) {
|
||||
setAttrOperandParensRange(SourceRange(loc));
|
||||
setAttrExprOperand(nullptr);
|
||||
} else if (hasAttrEnumOperand()) {
|
||||
setAttrOperandParensRange(SourceRange(loc));
|
||||
setAttrEnumOperandLoc(loc);
|
||||
}
|
||||
setAttr(nullptr);
|
||||
}
|
||||
|
||||
QualType getInnerType() const {
|
||||
|
|
|
@ -494,10 +494,7 @@ class Attr {
|
|||
}
|
||||
|
||||
/// A type attribute is not processed on a declaration or a statement.
|
||||
class TypeAttr : Attr {
|
||||
// By default, type attributes do not get an AST node.
|
||||
let ASTNode = 0;
|
||||
}
|
||||
class TypeAttr : Attr;
|
||||
|
||||
/// A stmt attribute is not processed on a declaration or a type.
|
||||
class StmtAttr : Attr;
|
||||
|
@ -567,6 +564,8 @@ def AddressSpace : TypeAttr {
|
|||
let Spellings = [Clang<"address_space">];
|
||||
let Args = [IntArgument<"AddressSpace">];
|
||||
let Documentation = [Undocumented];
|
||||
// Represented as a qualifier or DependentAddressSpaceType instead.
|
||||
let ASTNode = 0;
|
||||
}
|
||||
|
||||
def Alias : Attr {
|
||||
|
@ -1224,7 +1223,7 @@ def LayoutVersion : InheritableAttr, TargetSpecificAttr<TargetMicrosoftCXXABI> {
|
|||
let Documentation = [LayoutVersionDocs];
|
||||
}
|
||||
|
||||
def LifetimeBound : InheritableAttr {
|
||||
def LifetimeBound : DeclOrTypeAttr {
|
||||
let Spellings = [Clang<"lifetimebound", 0>];
|
||||
let Subjects = SubjectList<[ParmVar, ImplicitObjectParameter], ErrorDiag>;
|
||||
let Documentation = [LifetimeBoundDocs];
|
||||
|
@ -1327,12 +1326,16 @@ def NeonPolyVectorType : TypeAttr {
|
|||
let Spellings = [Clang<"neon_polyvector_type">];
|
||||
let Args = [IntArgument<"NumElements">];
|
||||
let Documentation = [Undocumented];
|
||||
// Represented as VectorType instead.
|
||||
let ASTNode = 0;
|
||||
}
|
||||
|
||||
def NeonVectorType : TypeAttr {
|
||||
let Spellings = [Clang<"neon_vector_type">];
|
||||
let Args = [IntArgument<"NumElements">];
|
||||
let Documentation = [Undocumented];
|
||||
// Represented as VectorType instead.
|
||||
let ASTNode = 0;
|
||||
}
|
||||
|
||||
def ReturnsTwice : InheritableAttr {
|
||||
|
@ -1507,6 +1510,14 @@ def TypeNullUnspecified : TypeAttr {
|
|||
let Documentation = [TypeNullUnspecifiedDocs];
|
||||
}
|
||||
|
||||
// This is a marker used to indicate that an __unsafe_unretained qualifier was
|
||||
// ignored because ARC is not enabled. The usual representation for this
|
||||
// qualifier is as an ObjCOwnership attribute with Kind == "none".
|
||||
def ObjCInertUnsafeUnretained : TypeAttr {
|
||||
let Spellings = [Keyword<"__unsafe_unretained">];
|
||||
let Documentation = [Undocumented];
|
||||
}
|
||||
|
||||
def ObjCKindOf : TypeAttr {
|
||||
let Spellings = [Keyword<"__kindof">];
|
||||
let Documentation = [Undocumented];
|
||||
|
@ -1594,7 +1605,7 @@ def ObjCBridgeRelated : InheritableAttr {
|
|||
let Documentation = [Undocumented];
|
||||
}
|
||||
|
||||
def NSReturnsRetained : InheritableAttr {
|
||||
def NSReturnsRetained : DeclOrTypeAttr {
|
||||
let Spellings = [Clang<"ns_returns_retained">];
|
||||
// let Subjects = SubjectList<[ObjCMethod, ObjCProperty, Function]>;
|
||||
let Documentation = [Undocumented];
|
||||
|
@ -1779,6 +1790,8 @@ def Regparm : TypeAttr {
|
|||
let Spellings = [GCC<"regparm">];
|
||||
let Args = [UnsignedArgument<"NumParams">];
|
||||
let Documentation = [RegparmDocs];
|
||||
// Represented as part of the enclosing function type.
|
||||
let ASTNode = 0;
|
||||
}
|
||||
|
||||
def ReqdWorkGroupSize : InheritableAttr {
|
||||
|
@ -2067,10 +2080,9 @@ def ObjCGC : TypeAttr {
|
|||
let Documentation = [Undocumented];
|
||||
}
|
||||
|
||||
def ObjCOwnership : InheritableAttr {
|
||||
def ObjCOwnership : DeclOrTypeAttr {
|
||||
let Spellings = [Clang<"objc_ownership">];
|
||||
let Args = [IdentifierArgument<"Kind">];
|
||||
let ASTNode = 0;
|
||||
let Documentation = [Undocumented];
|
||||
}
|
||||
|
||||
|
@ -2108,6 +2120,8 @@ def VectorSize : TypeAttr {
|
|||
let Spellings = [GCC<"vector_size">];
|
||||
let Args = [ExprArgument<"NumBytes">];
|
||||
let Documentation = [Undocumented];
|
||||
// Represented as VectorType instead.
|
||||
let ASTNode = 0;
|
||||
}
|
||||
|
||||
def VecTypeHint : InheritableAttr {
|
||||
|
@ -2202,7 +2216,7 @@ def AnyX86NoCallerSavedRegisters : InheritableAttr,
|
|||
let Documentation = [AnyX86NoCallerSavedRegistersDocs];
|
||||
}
|
||||
|
||||
def AnyX86NoCfCheck : InheritableAttr, TargetSpecificAttr<TargetAnyX86>{
|
||||
def AnyX86NoCfCheck : DeclOrTypeAttr, TargetSpecificAttr<TargetAnyX86>{
|
||||
let Spellings = [GCC<"nocf_check">];
|
||||
let Subjects = SubjectList<[FunctionLike]>;
|
||||
let Documentation = [AnyX86NoCfCheckDocs];
|
||||
|
|
|
@ -1435,8 +1435,6 @@ public:
|
|||
|
||||
TypeSourceInfo *GetTypeForDeclarator(Declarator &D, Scope *S);
|
||||
TypeSourceInfo *GetTypeForDeclaratorCast(Declarator &D, QualType FromTy);
|
||||
TypeSourceInfo *GetTypeSourceInfoForDeclarator(Declarator &D, QualType T,
|
||||
TypeSourceInfo *ReturnTypeInfo);
|
||||
|
||||
/// Package the given type and TSI into a ParsedType.
|
||||
ParsedType CreateParsedType(QualType T, TypeSourceInfo *TInfo);
|
||||
|
@ -3378,30 +3376,6 @@ public:
|
|||
/// Valid types should not have multiple attributes with different CCs.
|
||||
const AttributedType *getCallingConvAttributedType(QualType T) const;
|
||||
|
||||
/// Check whether a nullability type specifier can be added to the given
|
||||
/// type.
|
||||
///
|
||||
/// \param type The type to which the nullability specifier will be
|
||||
/// added. On success, this type will be updated appropriately.
|
||||
///
|
||||
/// \param nullability The nullability specifier to add.
|
||||
///
|
||||
/// \param nullabilityLoc The location of the nullability specifier.
|
||||
///
|
||||
/// \param isContextSensitive Whether this nullability specifier was
|
||||
/// written as a context-sensitive keyword (in an Objective-C
|
||||
/// method) or an Objective-C property attribute, rather than as an
|
||||
/// underscored type specifier.
|
||||
///
|
||||
/// \param allowArrayTypes Whether to accept nullability specifiers on an
|
||||
/// array type (e.g., because it will decay to a pointer).
|
||||
///
|
||||
/// \returns true if nullability cannot be applied, false otherwise.
|
||||
bool checkNullabilityTypeSpecifier(QualType &type, NullabilityKind nullability,
|
||||
SourceLocation nullabilityLoc,
|
||||
bool isContextSensitive,
|
||||
bool allowArrayTypes);
|
||||
|
||||
/// Stmt attributes - this routine is the top level dispatcher.
|
||||
StmtResult ProcessStmtAttributes(Stmt *Stmt,
|
||||
const ParsedAttributesView &Attrs,
|
||||
|
@ -8071,10 +8045,6 @@ public:
|
|||
SourceLocation ProtocolRAngleLoc,
|
||||
bool FailOnError = false);
|
||||
|
||||
/// Check the application of the Objective-C '__kindof' qualifier to
|
||||
/// the given type.
|
||||
bool checkObjCKindOfType(QualType &type, SourceLocation loc);
|
||||
|
||||
/// Ensure attributes are consistent with type.
|
||||
/// \param [in, out] Attributes The attributes to check; they will
|
||||
/// be modified to be consistent with \p PropertyTy.
|
||||
|
|
|
@ -2245,6 +2245,9 @@ public:
|
|||
CXXTemporary *ReadCXXTemporary(ModuleFile &F, const RecordData &Record,
|
||||
unsigned &Idx);
|
||||
|
||||
/// Reads one attribute from the current stream position.
|
||||
Attr *ReadAttr(ModuleFile &M, const RecordData &Record, unsigned &Idx);
|
||||
|
||||
/// Reads attributes from the current stream position.
|
||||
void ReadAttributes(ASTRecordReader &Record, AttrVec &Attrs);
|
||||
|
||||
|
@ -2630,6 +2633,11 @@ public:
|
|||
return ASTReader::ReadVersionTuple(Record, Idx);
|
||||
}
|
||||
|
||||
/// Reads one attribute from the current stream position, advancing Idx.
|
||||
Attr *readAttr() {
|
||||
return Reader->ReadAttr(*F, Record, Idx);
|
||||
}
|
||||
|
||||
/// Reads attributes from the current stream position, advancing Idx.
|
||||
void readAttributes(AttrVec &Attrs) {
|
||||
return Reader->ReadAttributes(*this, Attrs);
|
||||
|
|
|
@ -955,6 +955,9 @@ public:
|
|||
return Writer->AddVersionTuple(Version, *Record);
|
||||
}
|
||||
|
||||
// Emit an attribute.
|
||||
void AddAttr(const Attr *A);
|
||||
|
||||
/// Emit a list of attributes.
|
||||
void AddAttributes(ArrayRef<const Attr*> Attrs);
|
||||
};
|
||||
|
|
|
@ -81,10 +81,11 @@ public:
|
|||
}
|
||||
|
||||
bool handleAttr(AttributedTypeLoc TL, Decl *D = nullptr) {
|
||||
if (TL.getAttrKind() != AttributedType::attr_objc_ownership)
|
||||
auto *OwnershipAttr = TL.getAttrAs<ObjCOwnershipAttr>();
|
||||
if (!OwnershipAttr)
|
||||
return false;
|
||||
|
||||
SourceLocation Loc = TL.getAttrNameLoc();
|
||||
SourceLocation Loc = OwnershipAttr->getLocation();
|
||||
unsigned RawLoc = Loc.getRawEncoding();
|
||||
if (MigrateCtx.AttrSet.count(RawLoc))
|
||||
return true;
|
||||
|
@ -93,13 +94,7 @@ public:
|
|||
SourceManager &SM = Ctx.getSourceManager();
|
||||
if (Loc.isMacroID())
|
||||
Loc = SM.getImmediateExpansionRange(Loc).getBegin();
|
||||
SmallString<32> Buf;
|
||||
bool Invalid = false;
|
||||
StringRef Spell = Lexer::getSpelling(
|
||||
SM.getSpellingLoc(TL.getAttrEnumOperandLoc()),
|
||||
Buf, SM, Ctx.getLangOpts(), &Invalid);
|
||||
if (Invalid)
|
||||
return false;
|
||||
StringRef Spell = OwnershipAttr->getKind()->getName();
|
||||
MigrationContext::GCAttrOccurrence::AttrKind Kind;
|
||||
if (Spell == "strong")
|
||||
Kind = MigrationContext::GCAttrOccurrence::Strong;
|
||||
|
@ -284,7 +279,7 @@ static void checkAllAtProps(MigrationContext &MigrateCtx,
|
|||
}
|
||||
|
||||
for (unsigned i = 0, e = ATLs.size(); i != e; ++i) {
|
||||
SourceLocation Loc = ATLs[i].first.getAttrNameLoc();
|
||||
SourceLocation Loc = ATLs[i].first.getAttr()->getLocation();
|
||||
if (Loc.isMacroID())
|
||||
Loc = MigrateCtx.Pass.Ctx.getSourceManager()
|
||||
.getImmediateExpansionRange(Loc)
|
||||
|
|
|
@ -359,7 +359,7 @@ MigrationContext::~MigrationContext() {
|
|||
bool MigrationContext::isGCOwnedNonObjC(QualType T) {
|
||||
while (!T.isNull()) {
|
||||
if (const AttributedType *AttrT = T->getAs<AttributedType>()) {
|
||||
if (AttrT->getAttrKind() == AttributedType::attr_objc_ownership)
|
||||
if (AttrT->getAttrKind() == attr::ObjCOwnership)
|
||||
return !AttrT->getModifiedType()->isObjCRetainableType();
|
||||
}
|
||||
|
||||
|
|
|
@ -3876,7 +3876,7 @@ QualType ASTContext::getEnumType(const EnumDecl *Decl) const {
|
|||
return QualType(newType, 0);
|
||||
}
|
||||
|
||||
QualType ASTContext::getAttributedType(AttributedType::Kind attrKind,
|
||||
QualType ASTContext::getAttributedType(attr::Kind attrKind,
|
||||
QualType modifiedType,
|
||||
QualType equivalentType) {
|
||||
llvm::FoldingSetNodeID id;
|
||||
|
|
|
@ -592,28 +592,6 @@ bool Type::isObjCClassOrClassKindOfType() const {
|
|||
return OPT->isObjCClassType() || OPT->isObjCQualifiedClassType();
|
||||
}
|
||||
|
||||
/// Was this type written with the special inert-in-MRC __unsafe_unretained
|
||||
/// qualifier?
|
||||
///
|
||||
/// This approximates the answer to the following question: if this
|
||||
/// translation unit were compiled in ARC, would this type be qualified
|
||||
/// with __unsafe_unretained?
|
||||
bool Type::isObjCInertUnsafeUnretainedType() const {
|
||||
const Type *cur = this;
|
||||
while (true) {
|
||||
if (const auto attributed = dyn_cast<AttributedType>(cur)) {
|
||||
if (attributed->getAttrKind() ==
|
||||
AttributedType::attr_objc_inert_unsafe_unretained)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Single-step desugar until we run out of sugar.
|
||||
QualType next = cur->getLocallyUnqualifiedSingleStepDesugaredType();
|
||||
if (next.getTypePtr() == cur) return false;
|
||||
cur = next.getTypePtr();
|
||||
}
|
||||
}
|
||||
|
||||
ObjCTypeParamType::ObjCTypeParamType(const ObjCTypeParamDecl *D,
|
||||
QualType can,
|
||||
ArrayRef<ObjCProtocolDecl *> protocols)
|
||||
|
@ -1641,6 +1619,16 @@ TagDecl *Type::getAsTagDecl() const {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
bool Type::hasAttr(attr::Kind AK) const {
|
||||
const Type *Cur = this;
|
||||
while (const auto *AT = Cur->getAs<AttributedType>()) {
|
||||
if (AT->getAttrKind() == AK)
|
||||
return true;
|
||||
Cur = AT->getEquivalentType().getTypePtr();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class GetContainedDeducedTypeVisitor :
|
||||
|
@ -3168,105 +3156,58 @@ bool RecordType::hasConstFields() const {
|
|||
}
|
||||
|
||||
bool AttributedType::isQualifier() const {
|
||||
// FIXME: Generate this with TableGen.
|
||||
switch (getAttrKind()) {
|
||||
// These are type qualifiers in the traditional C sense: they annotate
|
||||
// something about a specific value/variable of a type. (They aren't
|
||||
// always part of the canonical type, though.)
|
||||
case AttributedType::attr_address_space:
|
||||
case AttributedType::attr_objc_gc:
|
||||
case AttributedType::attr_objc_ownership:
|
||||
case AttributedType::attr_objc_inert_unsafe_unretained:
|
||||
case AttributedType::attr_nonnull:
|
||||
case AttributedType::attr_nullable:
|
||||
case AttributedType::attr_null_unspecified:
|
||||
case AttributedType::attr_lifetimebound:
|
||||
case attr::ObjCGC:
|
||||
case attr::ObjCOwnership:
|
||||
case attr::ObjCInertUnsafeUnretained:
|
||||
case attr::TypeNonNull:
|
||||
case attr::TypeNullable:
|
||||
case attr::TypeNullUnspecified:
|
||||
case attr::LifetimeBound:
|
||||
return true;
|
||||
|
||||
// These aren't qualifiers; they rewrite the modified type to be a
|
||||
// semantically different type.
|
||||
case AttributedType::attr_regparm:
|
||||
case AttributedType::attr_vector_size:
|
||||
case AttributedType::attr_neon_vector_type:
|
||||
case AttributedType::attr_neon_polyvector_type:
|
||||
case AttributedType::attr_pcs:
|
||||
case AttributedType::attr_pcs_vfp:
|
||||
case AttributedType::attr_noreturn:
|
||||
case AttributedType::attr_cdecl:
|
||||
case AttributedType::attr_fastcall:
|
||||
case AttributedType::attr_stdcall:
|
||||
case AttributedType::attr_thiscall:
|
||||
case AttributedType::attr_regcall:
|
||||
case AttributedType::attr_pascal:
|
||||
case AttributedType::attr_swiftcall:
|
||||
case AttributedType::attr_vectorcall:
|
||||
case AttributedType::attr_inteloclbicc:
|
||||
case AttributedType::attr_preserve_most:
|
||||
case AttributedType::attr_preserve_all:
|
||||
case AttributedType::attr_ms_abi:
|
||||
case AttributedType::attr_sysv_abi:
|
||||
case AttributedType::attr_ptr32:
|
||||
case AttributedType::attr_ptr64:
|
||||
case AttributedType::attr_sptr:
|
||||
case AttributedType::attr_uptr:
|
||||
case AttributedType::attr_objc_kindof:
|
||||
case AttributedType::attr_ns_returns_retained:
|
||||
case AttributedType::attr_nocf_check:
|
||||
// All other type attributes aren't qualifiers; they rewrite the modified
|
||||
// type to be a semantically different type.
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
llvm_unreachable("bad attributed type kind");
|
||||
}
|
||||
|
||||
bool AttributedType::isMSTypeSpec() const {
|
||||
// FIXME: Generate this with TableGen?
|
||||
switch (getAttrKind()) {
|
||||
default: return false;
|
||||
case attr_ptr32:
|
||||
case attr_ptr64:
|
||||
case attr_sptr:
|
||||
case attr_uptr:
|
||||
default: return false;
|
||||
case attr::Ptr32:
|
||||
case attr::Ptr64:
|
||||
case attr::SPtr:
|
||||
case attr::UPtr:
|
||||
return true;
|
||||
}
|
||||
llvm_unreachable("invalid attr kind");
|
||||
}
|
||||
|
||||
bool AttributedType::isCallingConv() const {
|
||||
// FIXME: Generate this with TableGen.
|
||||
switch (getAttrKind()) {
|
||||
case attr_ptr32:
|
||||
case attr_ptr64:
|
||||
case attr_sptr:
|
||||
case attr_uptr:
|
||||
case attr_address_space:
|
||||
case attr_regparm:
|
||||
case attr_vector_size:
|
||||
case attr_neon_vector_type:
|
||||
case attr_neon_polyvector_type:
|
||||
case attr_objc_gc:
|
||||
case attr_objc_ownership:
|
||||
case attr_objc_inert_unsafe_unretained:
|
||||
case attr_noreturn:
|
||||
case attr_nonnull:
|
||||
case attr_ns_returns_retained:
|
||||
case attr_nullable:
|
||||
case attr_null_unspecified:
|
||||
case attr_objc_kindof:
|
||||
case attr_nocf_check:
|
||||
case attr_lifetimebound:
|
||||
return false;
|
||||
|
||||
case attr_pcs:
|
||||
case attr_pcs_vfp:
|
||||
case attr_cdecl:
|
||||
case attr_fastcall:
|
||||
case attr_stdcall:
|
||||
case attr_thiscall:
|
||||
case attr_regcall:
|
||||
case attr_swiftcall:
|
||||
case attr_vectorcall:
|
||||
case attr_pascal:
|
||||
case attr_ms_abi:
|
||||
case attr_sysv_abi:
|
||||
case attr_inteloclbicc:
|
||||
case attr_preserve_most:
|
||||
case attr_preserve_all:
|
||||
default: return false;
|
||||
case attr::Pcs:
|
||||
case attr::CDecl:
|
||||
case attr::FastCall:
|
||||
case attr::StdCall:
|
||||
case attr::ThisCall:
|
||||
case attr::RegCall:
|
||||
case attr::SwiftCall:
|
||||
case attr::VectorCall:
|
||||
case attr::Pascal:
|
||||
case attr::MSABI:
|
||||
case attr::SysVABI:
|
||||
case attr::IntelOclBicc:
|
||||
case attr::PreserveMost:
|
||||
case attr::PreserveAll:
|
||||
return true;
|
||||
}
|
||||
llvm_unreachable("invalid attr kind");
|
||||
|
@ -3712,23 +3653,18 @@ LinkageInfo Type::getLinkageAndVisibility() const {
|
|||
return LinkageComputer{}.getTypeLinkageAndVisibility(this);
|
||||
}
|
||||
|
||||
Optional<NullabilityKind> Type::getNullability(const ASTContext &context) const {
|
||||
QualType type(this, 0);
|
||||
do {
|
||||
Optional<NullabilityKind>
|
||||
Type::getNullability(const ASTContext &Context) const {
|
||||
QualType Type(this, 0);
|
||||
while (const auto *AT = Type->getAs<AttributedType>()) {
|
||||
// Check whether this is an attributed type with nullability
|
||||
// information.
|
||||
if (auto attributed = dyn_cast<AttributedType>(type.getTypePtr())) {
|
||||
if (auto nullability = attributed->getImmediateNullability())
|
||||
return nullability;
|
||||
}
|
||||
if (auto Nullability = AT->getImmediateNullability())
|
||||
return Nullability;
|
||||
|
||||
// Desugar the type. If desugaring does nothing, we're done.
|
||||
QualType desugared = type.getSingleStepDesugaredType(context);
|
||||
if (desugared.getTypePtr() == type.getTypePtr())
|
||||
return None;
|
||||
|
||||
type = desugared;
|
||||
} while (true);
|
||||
Type = AT->getEquivalentType();
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
bool Type::canHaveNullability(bool ResultIfUnknown) const {
|
||||
|
@ -3841,12 +3777,13 @@ bool Type::canHaveNullability(bool ResultIfUnknown) const {
|
|||
llvm_unreachable("bad type kind!");
|
||||
}
|
||||
|
||||
llvm::Optional<NullabilityKind> AttributedType::getImmediateNullability() const {
|
||||
if (getAttrKind() == AttributedType::attr_nonnull)
|
||||
llvm::Optional<NullabilityKind>
|
||||
AttributedType::getImmediateNullability() const {
|
||||
if (getAttrKind() == attr::TypeNonNull)
|
||||
return NullabilityKind::NonNull;
|
||||
if (getAttrKind() == AttributedType::attr_nullable)
|
||||
if (getAttrKind() == attr::TypeNullable)
|
||||
return NullabilityKind::Nullable;
|
||||
if (getAttrKind() == AttributedType::attr_null_unspecified)
|
||||
if (getAttrKind() == attr::TypeNullUnspecified)
|
||||
return NullabilityKind::Unspecified;
|
||||
return None;
|
||||
}
|
||||
|
|
|
@ -404,11 +404,11 @@ TypeLoc TypeLoc::IgnoreParensImpl(TypeLoc TL) {
|
|||
}
|
||||
|
||||
SourceLocation TypeLoc::findNullabilityLoc() const {
|
||||
if (auto attributedLoc = getAs<AttributedTypeLoc>()) {
|
||||
if (attributedLoc.getAttrKind() == AttributedType::attr_nullable ||
|
||||
attributedLoc.getAttrKind() == AttributedType::attr_nonnull ||
|
||||
attributedLoc.getAttrKind() == AttributedType::attr_null_unspecified)
|
||||
return attributedLoc.getAttrNameLoc();
|
||||
if (auto ATL = getAs<AttributedTypeLoc>()) {
|
||||
const Attr *A = ATL.getAttr();
|
||||
if (A && (isa<TypeNullableAttr>(A) || isa<TypeNonNullAttr>(A) ||
|
||||
isa<TypeNullUnspecifiedAttr>(A)))
|
||||
return A->getLocation();
|
||||
}
|
||||
|
||||
return {};
|
||||
|
|
|
@ -1354,12 +1354,14 @@ void TypePrinter::printPackExpansionAfter(const PackExpansionType *T,
|
|||
|
||||
void TypePrinter::printAttributedBefore(const AttributedType *T,
|
||||
raw_ostream &OS) {
|
||||
// FIXME: Generate this with TableGen.
|
||||
|
||||
// Prefer the macro forms of the GC and ownership qualifiers.
|
||||
if (T->getAttrKind() == AttributedType::attr_objc_gc ||
|
||||
T->getAttrKind() == AttributedType::attr_objc_ownership)
|
||||
if (T->getAttrKind() == attr::ObjCGC ||
|
||||
T->getAttrKind() == attr::ObjCOwnership)
|
||||
return printBefore(T->getEquivalentType(), OS);
|
||||
|
||||
if (T->getAttrKind() == AttributedType::attr_objc_kindof)
|
||||
if (T->getAttrKind() == attr::ObjCKindOf)
|
||||
OS << "__kindof ";
|
||||
|
||||
printBefore(T->getModifiedType(), OS);
|
||||
|
@ -1367,23 +1369,21 @@ void TypePrinter::printAttributedBefore(const AttributedType *T,
|
|||
if (T->isMSTypeSpec()) {
|
||||
switch (T->getAttrKind()) {
|
||||
default: return;
|
||||
case AttributedType::attr_ptr32: OS << " __ptr32"; break;
|
||||
case AttributedType::attr_ptr64: OS << " __ptr64"; break;
|
||||
case AttributedType::attr_sptr: OS << " __sptr"; break;
|
||||
case AttributedType::attr_uptr: OS << " __uptr"; break;
|
||||
case attr::Ptr32: OS << " __ptr32"; break;
|
||||
case attr::Ptr64: OS << " __ptr64"; break;
|
||||
case attr::SPtr: OS << " __sptr"; break;
|
||||
case attr::UPtr: OS << " __uptr"; break;
|
||||
}
|
||||
spaceBeforePlaceHolder(OS);
|
||||
}
|
||||
|
||||
// Print nullability type specifiers.
|
||||
if (T->getAttrKind() == AttributedType::attr_nonnull ||
|
||||
T->getAttrKind() == AttributedType::attr_nullable ||
|
||||
T->getAttrKind() == AttributedType::attr_null_unspecified) {
|
||||
if (T->getAttrKind() == AttributedType::attr_nonnull)
|
||||
if (T->getImmediateNullability()) {
|
||||
if (T->getAttrKind() == attr::TypeNonNull)
|
||||
OS << " _Nonnull";
|
||||
else if (T->getAttrKind() == AttributedType::attr_nullable)
|
||||
else if (T->getAttrKind() == attr::TypeNullable)
|
||||
OS << " _Nullable";
|
||||
else if (T->getAttrKind() == AttributedType::attr_null_unspecified)
|
||||
else if (T->getAttrKind() == attr::TypeNullUnspecified)
|
||||
OS << " _Null_unspecified";
|
||||
else
|
||||
llvm_unreachable("unhandled nullability");
|
||||
|
@ -1393,9 +1393,11 @@ void TypePrinter::printAttributedBefore(const AttributedType *T,
|
|||
|
||||
void TypePrinter::printAttributedAfter(const AttributedType *T,
|
||||
raw_ostream &OS) {
|
||||
// FIXME: Generate this with TableGen.
|
||||
|
||||
// Prefer the macro forms of the GC and ownership qualifiers.
|
||||
if (T->getAttrKind() == AttributedType::attr_objc_gc ||
|
||||
T->getAttrKind() == AttributedType::attr_objc_ownership)
|
||||
if (T->getAttrKind() == attr::ObjCGC ||
|
||||
T->getAttrKind() == attr::ObjCOwnership)
|
||||
return printAfter(T->getEquivalentType(), OS);
|
||||
|
||||
// If this is a calling convention attribute, don't print the implicit CC from
|
||||
|
@ -1406,107 +1408,74 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
|
|||
|
||||
// Some attributes are printed as qualifiers before the type, so we have
|
||||
// nothing left to do.
|
||||
if (T->getAttrKind() == AttributedType::attr_objc_kindof ||
|
||||
T->isMSTypeSpec() ||
|
||||
T->getAttrKind() == AttributedType::attr_nonnull ||
|
||||
T->getAttrKind() == AttributedType::attr_nullable ||
|
||||
T->getAttrKind() == AttributedType::attr_null_unspecified)
|
||||
if (T->getAttrKind() == attr::ObjCKindOf ||
|
||||
T->isMSTypeSpec() || T->getImmediateNullability())
|
||||
return;
|
||||
|
||||
// Don't print the inert __unsafe_unretained attribute at all.
|
||||
if (T->getAttrKind() == AttributedType::attr_objc_inert_unsafe_unretained)
|
||||
if (T->getAttrKind() == attr::ObjCInertUnsafeUnretained)
|
||||
return;
|
||||
|
||||
// Don't print ns_returns_retained unless it had an effect.
|
||||
if (T->getAttrKind() == AttributedType::attr_ns_returns_retained &&
|
||||
if (T->getAttrKind() == attr::NSReturnsRetained &&
|
||||
!T->getEquivalentType()->castAs<FunctionType>()
|
||||
->getExtInfo().getProducesResult())
|
||||
return;
|
||||
|
||||
if (T->getAttrKind() == AttributedType::attr_lifetimebound) {
|
||||
if (T->getAttrKind() == attr::LifetimeBound) {
|
||||
OS << " [[clang::lifetimebound]]";
|
||||
return;
|
||||
}
|
||||
|
||||
OS << " __attribute__((";
|
||||
switch (T->getAttrKind()) {
|
||||
case AttributedType::attr_lifetimebound:
|
||||
case AttributedType::attr_nonnull:
|
||||
case AttributedType::attr_nullable:
|
||||
case AttributedType::attr_null_unspecified:
|
||||
case AttributedType::attr_objc_gc:
|
||||
case AttributedType::attr_objc_inert_unsafe_unretained:
|
||||
case AttributedType::attr_objc_kindof:
|
||||
case AttributedType::attr_objc_ownership:
|
||||
case AttributedType::attr_ptr32:
|
||||
case AttributedType::attr_ptr64:
|
||||
case AttributedType::attr_sptr:
|
||||
case AttributedType::attr_uptr:
|
||||
#define TYPE_ATTR(NAME)
|
||||
#define DECL_OR_TYPE_ATTR(NAME)
|
||||
#define ATTR(NAME) case attr::NAME:
|
||||
#include "clang/Basic/AttrList.inc"
|
||||
llvm_unreachable("non-type attribute attached to type");
|
||||
|
||||
case attr::OpenCLPrivateAddressSpace:
|
||||
case attr::OpenCLGlobalAddressSpace:
|
||||
case attr::OpenCLLocalAddressSpace:
|
||||
case attr::OpenCLConstantAddressSpace:
|
||||
case attr::OpenCLGenericAddressSpace:
|
||||
// FIXME: Update printAttributedBefore to print these once we generate
|
||||
// AttributedType nodes for them.
|
||||
break;
|
||||
|
||||
case attr::LifetimeBound:
|
||||
case attr::TypeNonNull:
|
||||
case attr::TypeNullable:
|
||||
case attr::TypeNullUnspecified:
|
||||
case attr::ObjCGC:
|
||||
case attr::ObjCInertUnsafeUnretained:
|
||||
case attr::ObjCKindOf:
|
||||
case attr::ObjCOwnership:
|
||||
case attr::Ptr32:
|
||||
case attr::Ptr64:
|
||||
case attr::SPtr:
|
||||
case attr::UPtr:
|
||||
llvm_unreachable("This attribute should have been handled already");
|
||||
|
||||
case AttributedType::attr_address_space:
|
||||
OS << "address_space(";
|
||||
// FIXME: printing the raw LangAS value is wrong. This should probably
|
||||
// use the same code as Qualifiers::print()
|
||||
OS << (unsigned)T->getEquivalentType().getAddressSpace();
|
||||
OS << ')';
|
||||
break;
|
||||
|
||||
case AttributedType::attr_vector_size:
|
||||
OS << "__vector_size__(";
|
||||
if (const auto *vector = T->getEquivalentType()->getAs<VectorType>()) {
|
||||
OS << vector->getNumElements();
|
||||
OS << " * sizeof(";
|
||||
print(vector->getElementType(), OS, StringRef());
|
||||
OS << ')';
|
||||
}
|
||||
OS << ')';
|
||||
break;
|
||||
|
||||
case AttributedType::attr_neon_vector_type:
|
||||
case AttributedType::attr_neon_polyvector_type: {
|
||||
if (T->getAttrKind() == AttributedType::attr_neon_vector_type)
|
||||
OS << "neon_vector_type(";
|
||||
else
|
||||
OS << "neon_polyvector_type(";
|
||||
const auto *vector = T->getEquivalentType()->getAs<VectorType>();
|
||||
OS << vector->getNumElements();
|
||||
OS << ')';
|
||||
break;
|
||||
}
|
||||
|
||||
case AttributedType::attr_regparm: {
|
||||
// FIXME: When Sema learns to form this AttributedType, avoid printing the
|
||||
// attribute again in printFunctionProtoAfter.
|
||||
OS << "regparm(";
|
||||
QualType t = T->getEquivalentType();
|
||||
while (!t->isFunctionType())
|
||||
t = t->getPointeeType();
|
||||
OS << t->getAs<FunctionType>()->getRegParmType();
|
||||
OS << ')';
|
||||
break;
|
||||
}
|
||||
|
||||
case AttributedType::attr_ns_returns_retained:
|
||||
case attr::NSReturnsRetained:
|
||||
OS << "ns_returns_retained";
|
||||
break;
|
||||
|
||||
// FIXME: When Sema learns to form this AttributedType, avoid printing the
|
||||
// attribute again in printFunctionProtoAfter.
|
||||
case AttributedType::attr_noreturn: OS << "noreturn"; break;
|
||||
case AttributedType::attr_nocf_check: OS << "nocf_check"; break;
|
||||
case AttributedType::attr_cdecl: OS << "cdecl"; break;
|
||||
case AttributedType::attr_fastcall: OS << "fastcall"; break;
|
||||
case AttributedType::attr_stdcall: OS << "stdcall"; break;
|
||||
case AttributedType::attr_thiscall: OS << "thiscall"; break;
|
||||
case AttributedType::attr_swiftcall: OS << "swiftcall"; break;
|
||||
case AttributedType::attr_vectorcall: OS << "vectorcall"; break;
|
||||
case AttributedType::attr_pascal: OS << "pascal"; break;
|
||||
case AttributedType::attr_ms_abi: OS << "ms_abi"; break;
|
||||
case AttributedType::attr_sysv_abi: OS << "sysv_abi"; break;
|
||||
case AttributedType::attr_regcall: OS << "regcall"; break;
|
||||
case AttributedType::attr_pcs:
|
||||
case AttributedType::attr_pcs_vfp: {
|
||||
case attr::AnyX86NoCfCheck: OS << "nocf_check"; break;
|
||||
case attr::CDecl: OS << "cdecl"; break;
|
||||
case attr::FastCall: OS << "fastcall"; break;
|
||||
case attr::StdCall: OS << "stdcall"; break;
|
||||
case attr::ThisCall: OS << "thiscall"; break;
|
||||
case attr::SwiftCall: OS << "swiftcall"; break;
|
||||
case attr::VectorCall: OS << "vectorcall"; break;
|
||||
case attr::Pascal: OS << "pascal"; break;
|
||||
case attr::MSABI: OS << "ms_abi"; break;
|
||||
case attr::SysVABI: OS << "sysv_abi"; break;
|
||||
case attr::RegCall: OS << "regcall"; break;
|
||||
case attr::Pcs: {
|
||||
OS << "pcs(";
|
||||
QualType t = T->getEquivalentType();
|
||||
while (!t->isFunctionType())
|
||||
|
@ -1517,12 +1486,12 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
|
|||
break;
|
||||
}
|
||||
|
||||
case AttributedType::attr_inteloclbicc: OS << "inteloclbicc"; break;
|
||||
case AttributedType::attr_preserve_most:
|
||||
case attr::IntelOclBicc: OS << "inteloclbicc"; break;
|
||||
case attr::PreserveMost:
|
||||
OS << "preserve_most";
|
||||
break;
|
||||
|
||||
case AttributedType::attr_preserve_all:
|
||||
case attr::PreserveAll:
|
||||
OS << "preserve_all";
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -5999,14 +5999,14 @@ static void checkAttributesAfterMerging(Sema &S, NamedDecl &ND) {
|
|||
// The [[lifetimebound]] attribute can be applied to the implicit object
|
||||
// parameter of a non-static member function (other than a ctor or dtor)
|
||||
// by applying it to the function type.
|
||||
if (ATL.getAttrKind() == AttributedType::attr_lifetimebound) {
|
||||
if (const auto *A = ATL.getAttrAs<LifetimeBoundAttr>()) {
|
||||
const auto *MD = dyn_cast<CXXMethodDecl>(FD);
|
||||
if (!MD || MD->isStatic()) {
|
||||
S.Diag(ATL.getAttrNameLoc(), diag::err_lifetimebound_no_object_param)
|
||||
<< !MD << ATL.getLocalSourceRange();
|
||||
S.Diag(A->getLocation(), diag::err_lifetimebound_no_object_param)
|
||||
<< !MD << A->getRange();
|
||||
} else if (isa<CXXConstructorDecl>(MD) || isa<CXXDestructorDecl>(MD)) {
|
||||
S.Diag(ATL.getAttrNameLoc(), diag::err_lifetimebound_ctor_dtor)
|
||||
<< isa<CXXDestructorDecl>(MD) << ATL.getLocalSourceRange();
|
||||
S.Diag(A->getLocation(), diag::err_lifetimebound_ctor_dtor)
|
||||
<< isa<CXXDestructorDecl>(MD) << A->getRange();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14648,15 +14648,15 @@ static bool captureInBlock(BlockScopeInfo *BSI, VarDecl *Var,
|
|||
// Warn about implicitly autoreleasing indirect parameters captured by blocks.
|
||||
if (const auto *PT = CaptureType->getAs<PointerType>()) {
|
||||
// This function finds out whether there is an AttributedType of kind
|
||||
// attr_objc_ownership in Ty. The existence of AttributedType of kind
|
||||
// attr_objc_ownership implies __autoreleasing was explicitly specified
|
||||
// attr::ObjCOwnership in Ty. The existence of AttributedType of kind
|
||||
// attr::ObjCOwnership implies __autoreleasing was explicitly specified
|
||||
// rather than being added implicitly by the compiler.
|
||||
auto IsObjCOwnershipAttributedType = [](QualType Ty) {
|
||||
while (const auto *AttrTy = Ty->getAs<AttributedType>()) {
|
||||
if (AttrTy->getAttrKind() == AttributedType::attr_objc_ownership)
|
||||
if (AttrTy->getAttrKind() == attr::ObjCOwnership)
|
||||
return true;
|
||||
|
||||
// Peel off AttributedTypes that are not of kind objc_ownership.
|
||||
// Peel off AttributedTypes that are not of kind ObjCOwnership.
|
||||
Ty = AttrTy->getModifiedType();
|
||||
}
|
||||
|
||||
|
|
|
@ -6360,7 +6360,7 @@ static bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD) {
|
|||
for (TypeLoc TL = TSI->getTypeLoc();
|
||||
(ATL = TL.getAsAdjusted<AttributedTypeLoc>());
|
||||
TL = ATL.getModifiedLoc()) {
|
||||
if (ATL.getAttrKind() == AttributedType::attr_lifetimebound)
|
||||
if (ATL.getAttrAs<LifetimeBoundAttr>())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -2384,7 +2384,7 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property) {
|
|||
QualType modifiedTy = resultTy;
|
||||
if (auto nullability = AttributedType::stripOuterNullability(modifiedTy)) {
|
||||
if (*nullability == NullabilityKind::Unspecified)
|
||||
resultTy = Context.getAttributedType(AttributedType::attr_nonnull,
|
||||
resultTy = Context.getAttributedType(attr::TypeNonNull,
|
||||
modifiedTy, modifiedTy);
|
||||
}
|
||||
}
|
||||
|
@ -2458,7 +2458,7 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property) {
|
|||
QualType modifiedTy = paramTy;
|
||||
if (auto nullability = AttributedType::stripOuterNullability(modifiedTy)){
|
||||
if (*nullability == NullabilityKind::Unspecified)
|
||||
paramTy = Context.getAttributedType(AttributedType::attr_nullable,
|
||||
paramTy = Context.getAttributedType(attr::TypeNullable,
|
||||
modifiedTy, modifiedTy);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -172,6 +172,16 @@ namespace {
|
|||
/// processing is complete.
|
||||
SmallVector<ParsedAttr *, 2> ignoredTypeAttrs;
|
||||
|
||||
/// Attributes corresponding to AttributedTypeLocs that we have not yet
|
||||
/// populated.
|
||||
// FIXME: The two-phase mechanism by which we construct Types and fill
|
||||
// their TypeLocs makes it hard to correctly assign these. We keep the
|
||||
// attributes in creation order as an attempt to make them line up
|
||||
// properly.
|
||||
using TypeAttrPair = std::pair<const AttributedType*, const Attr*>;
|
||||
SmallVector<TypeAttrPair, 8> AttrsForTypes;
|
||||
bool AttrsForTypesSorted = true;
|
||||
|
||||
public:
|
||||
TypeProcessingState(Sema &sema, Declarator &declarator)
|
||||
: sema(sema), declarator(declarator),
|
||||
|
@ -230,6 +240,43 @@ namespace {
|
|||
diagnoseBadTypeAttribute(getSema(), *Attr, type);
|
||||
}
|
||||
|
||||
/// Get an attributed type for the given attribute, and remember the Attr
|
||||
/// object so that we can attach it to the AttributedTypeLoc.
|
||||
QualType getAttributedType(Attr *A, QualType ModifiedType,
|
||||
QualType EquivType) {
|
||||
QualType T =
|
||||
sema.Context.getAttributedType(A->getKind(), ModifiedType, EquivType);
|
||||
AttrsForTypes.push_back({cast<AttributedType>(T.getTypePtr()), A});
|
||||
AttrsForTypesSorted = false;
|
||||
return T;
|
||||
}
|
||||
|
||||
/// Extract and remove the Attr* for a given attributed type.
|
||||
const Attr *takeAttrForAttributedType(const AttributedType *AT) {
|
||||
if (!AttrsForTypesSorted) {
|
||||
std::stable_sort(AttrsForTypes.begin(), AttrsForTypes.end(),
|
||||
[](const TypeAttrPair &A, const TypeAttrPair &B) {
|
||||
return A.first < B.first;
|
||||
});
|
||||
AttrsForTypesSorted = true;
|
||||
}
|
||||
|
||||
// FIXME: This is quadratic if we have lots of reuses of the same
|
||||
// attributed type.
|
||||
for (auto It = std::partition_point(
|
||||
AttrsForTypes.begin(), AttrsForTypes.end(),
|
||||
[=](const TypeAttrPair &A) { return A.first < AT; });
|
||||
It != AttrsForTypes.end() && It->first == AT; ++It) {
|
||||
if (It->second) {
|
||||
const Attr *Result = It->second;
|
||||
It->second = nullptr;
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
|
||||
llvm_unreachable("no Attr* for AttributedType*");
|
||||
}
|
||||
|
||||
~TypeProcessingState() {
|
||||
if (trivial) return;
|
||||
|
||||
|
@ -3832,6 +3879,32 @@ static bool hasOuterPointerLikeChunk(const Declarator &D, unsigned endIndex) {
|
|||
return false;
|
||||
}
|
||||
|
||||
template<typename AttrT>
|
||||
static AttrT *createSimpleAttr(ASTContext &Ctx, ParsedAttr &Attr) {
|
||||
Attr.setUsedAsTypeAttr();
|
||||
return ::new (Ctx)
|
||||
AttrT(Attr.getRange(), Ctx, Attr.getAttributeSpellingListIndex());
|
||||
}
|
||||
|
||||
static Attr *createNullabilityAttr(ASTContext &Ctx, ParsedAttr &Attr,
|
||||
NullabilityKind NK) {
|
||||
switch (NK) {
|
||||
case NullabilityKind::NonNull:
|
||||
return createSimpleAttr<TypeNonNullAttr>(Ctx, Attr);
|
||||
|
||||
case NullabilityKind::Nullable:
|
||||
return createSimpleAttr<TypeNullableAttr>(Ctx, Attr);
|
||||
|
||||
case NullabilityKind::Unspecified:
|
||||
return createSimpleAttr<TypeNullUnspecifiedAttr>(Ctx, Attr);
|
||||
}
|
||||
llvm_unreachable("unknown NullabilityKind");
|
||||
}
|
||||
|
||||
static TypeSourceInfo *
|
||||
GetTypeSourceInfoForDeclarator(TypeProcessingState &State,
|
||||
QualType T, TypeSourceInfo *ReturnTypeInfo);
|
||||
|
||||
static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
|
||||
QualType declSpecType,
|
||||
TypeSourceInfo *TInfo) {
|
||||
|
@ -4184,9 +4257,8 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
|
|||
pointerKind, D.getDeclSpec().getTypeSpecTypeLoc(),
|
||||
D.getDeclSpec().getEndLoc(),
|
||||
D.getMutableDeclSpec().getAttributes())) {
|
||||
T = Context.getAttributedType(
|
||||
AttributedType::getNullabilityAttrKind(*inferNullability),T,T);
|
||||
attr->setUsedAsTypeAttr();
|
||||
T = state.getAttributedType(
|
||||
createNullabilityAttr(Context, *attr, *inferNullability), T, T);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5025,7 +5097,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
|
|||
if (D.isInvalidType())
|
||||
return Context.getTrivialTypeSourceInfo(T);
|
||||
|
||||
return S.GetTypeSourceInfoForDeclarator(D, T, TInfo);
|
||||
return GetTypeSourceInfoForDeclarator(state, T, TInfo);
|
||||
}
|
||||
|
||||
/// GetTypeForDeclarator - Convert the type for the specified
|
||||
|
@ -5161,131 +5233,25 @@ TypeSourceInfo *Sema::GetTypeForDeclaratorCast(Declarator &D, QualType FromTy) {
|
|||
return GetFullTypeForDeclarator(state, declSpecTy, ReturnTypeInfo);
|
||||
}
|
||||
|
||||
/// Map an AttributedType::Kind to an ParsedAttr::Kind.
|
||||
static ParsedAttr::Kind getAttrListKind(AttributedType::Kind kind) {
|
||||
switch (kind) {
|
||||
case AttributedType::attr_address_space:
|
||||
return ParsedAttr::AT_AddressSpace;
|
||||
case AttributedType::attr_regparm:
|
||||
return ParsedAttr::AT_Regparm;
|
||||
case AttributedType::attr_vector_size:
|
||||
return ParsedAttr::AT_VectorSize;
|
||||
case AttributedType::attr_neon_vector_type:
|
||||
return ParsedAttr::AT_NeonVectorType;
|
||||
case AttributedType::attr_neon_polyvector_type:
|
||||
return ParsedAttr::AT_NeonPolyVectorType;
|
||||
case AttributedType::attr_objc_gc:
|
||||
return ParsedAttr::AT_ObjCGC;
|
||||
case AttributedType::attr_objc_ownership:
|
||||
case AttributedType::attr_objc_inert_unsafe_unretained:
|
||||
return ParsedAttr::AT_ObjCOwnership;
|
||||
case AttributedType::attr_noreturn:
|
||||
return ParsedAttr::AT_NoReturn;
|
||||
case AttributedType::attr_nocf_check:
|
||||
return ParsedAttr::AT_AnyX86NoCfCheck;
|
||||
case AttributedType::attr_cdecl:
|
||||
return ParsedAttr::AT_CDecl;
|
||||
case AttributedType::attr_fastcall:
|
||||
return ParsedAttr::AT_FastCall;
|
||||
case AttributedType::attr_stdcall:
|
||||
return ParsedAttr::AT_StdCall;
|
||||
case AttributedType::attr_thiscall:
|
||||
return ParsedAttr::AT_ThisCall;
|
||||
case AttributedType::attr_regcall:
|
||||
return ParsedAttr::AT_RegCall;
|
||||
case AttributedType::attr_pascal:
|
||||
return ParsedAttr::AT_Pascal;
|
||||
case AttributedType::attr_swiftcall:
|
||||
return ParsedAttr::AT_SwiftCall;
|
||||
case AttributedType::attr_vectorcall:
|
||||
return ParsedAttr::AT_VectorCall;
|
||||
case AttributedType::attr_pcs:
|
||||
case AttributedType::attr_pcs_vfp:
|
||||
return ParsedAttr::AT_Pcs;
|
||||
case AttributedType::attr_inteloclbicc:
|
||||
return ParsedAttr::AT_IntelOclBicc;
|
||||
case AttributedType::attr_ms_abi:
|
||||
return ParsedAttr::AT_MSABI;
|
||||
case AttributedType::attr_sysv_abi:
|
||||
return ParsedAttr::AT_SysVABI;
|
||||
case AttributedType::attr_preserve_most:
|
||||
return ParsedAttr::AT_PreserveMost;
|
||||
case AttributedType::attr_preserve_all:
|
||||
return ParsedAttr::AT_PreserveAll;
|
||||
case AttributedType::attr_ptr32:
|
||||
return ParsedAttr::AT_Ptr32;
|
||||
case AttributedType::attr_ptr64:
|
||||
return ParsedAttr::AT_Ptr64;
|
||||
case AttributedType::attr_sptr:
|
||||
return ParsedAttr::AT_SPtr;
|
||||
case AttributedType::attr_uptr:
|
||||
return ParsedAttr::AT_UPtr;
|
||||
case AttributedType::attr_nonnull:
|
||||
return ParsedAttr::AT_TypeNonNull;
|
||||
case AttributedType::attr_nullable:
|
||||
return ParsedAttr::AT_TypeNullable;
|
||||
case AttributedType::attr_null_unspecified:
|
||||
return ParsedAttr::AT_TypeNullUnspecified;
|
||||
case AttributedType::attr_objc_kindof:
|
||||
return ParsedAttr::AT_ObjCKindOf;
|
||||
case AttributedType::attr_ns_returns_retained:
|
||||
return ParsedAttr::AT_NSReturnsRetained;
|
||||
case AttributedType::attr_lifetimebound:
|
||||
return ParsedAttr::AT_LifetimeBound;
|
||||
}
|
||||
llvm_unreachable("unexpected attribute kind!");
|
||||
}
|
||||
|
||||
static void setAttributedTypeLoc(AttributedTypeLoc TL, const ParsedAttr &attr) {
|
||||
TL.setAttrNameLoc(attr.getLoc());
|
||||
if (TL.hasAttrExprOperand()) {
|
||||
assert(attr.isArgExpr(0) && "mismatched attribute operand kind");
|
||||
TL.setAttrExprOperand(attr.getArgAsExpr(0));
|
||||
} else if (TL.hasAttrEnumOperand()) {
|
||||
assert((attr.isArgIdent(0) || attr.isArgExpr(0)) &&
|
||||
"unexpected attribute operand kind");
|
||||
if (attr.isArgIdent(0))
|
||||
TL.setAttrEnumOperandLoc(attr.getArgAsIdent(0)->Loc);
|
||||
else
|
||||
TL.setAttrEnumOperandLoc(attr.getArgAsExpr(0)->getExprLoc());
|
||||
}
|
||||
|
||||
// FIXME: preserve this information to here.
|
||||
if (TL.hasAttrOperand())
|
||||
TL.setAttrOperandParensRange(SourceRange());
|
||||
}
|
||||
|
||||
static void fillAttributedTypeLoc(AttributedTypeLoc TL,
|
||||
const ParsedAttributesView &Attrs,
|
||||
const ParsedAttributesView &DeclAttrs) {
|
||||
// DeclAttrs and Attrs cannot be both empty.
|
||||
assert((!Attrs.empty() || !DeclAttrs.empty()) &&
|
||||
"no type attributes in the expected location!");
|
||||
|
||||
ParsedAttr::Kind parsedKind = getAttrListKind(TL.getAttrKind());
|
||||
// Try to search for an attribute of matching kind in Attrs list.
|
||||
for (const ParsedAttr &AL : Attrs)
|
||||
if (AL.getKind() == parsedKind)
|
||||
return setAttributedTypeLoc(TL, AL);
|
||||
|
||||
for (const ParsedAttr &AL : DeclAttrs)
|
||||
if (AL.isCXX11Attribute() || AL.getKind() == parsedKind)
|
||||
return setAttributedTypeLoc(TL, AL);
|
||||
llvm_unreachable("no matching type attribute in expected location!");
|
||||
TypeProcessingState &State) {
|
||||
TL.setAttr(State.takeAttrForAttributedType(TL.getTypePtr()));
|
||||
}
|
||||
|
||||
namespace {
|
||||
class TypeSpecLocFiller : public TypeLocVisitor<TypeSpecLocFiller> {
|
||||
ASTContext &Context;
|
||||
TypeProcessingState &State;
|
||||
const DeclSpec &DS;
|
||||
|
||||
public:
|
||||
TypeSpecLocFiller(ASTContext &Context, const DeclSpec &DS)
|
||||
: Context(Context), DS(DS) {}
|
||||
TypeSpecLocFiller(ASTContext &Context, TypeProcessingState &State,
|
||||
const DeclSpec &DS)
|
||||
: Context(Context), State(State), DS(DS) {}
|
||||
|
||||
void VisitAttributedTypeLoc(AttributedTypeLoc TL) {
|
||||
fillAttributedTypeLoc(TL, DS.getAttributes(), ParsedAttributesView{});
|
||||
Visit(TL.getModifiedLoc());
|
||||
fillAttributedTypeLoc(TL, State);
|
||||
}
|
||||
void VisitQualifiedTypeLoc(QualifiedTypeLoc TL) {
|
||||
Visit(TL.getUnqualifiedLoc());
|
||||
|
@ -5442,11 +5408,13 @@ namespace {
|
|||
|
||||
class DeclaratorLocFiller : public TypeLocVisitor<DeclaratorLocFiller> {
|
||||
ASTContext &Context;
|
||||
TypeProcessingState &State;
|
||||
const DeclaratorChunk &Chunk;
|
||||
|
||||
public:
|
||||
DeclaratorLocFiller(ASTContext &Context, const DeclaratorChunk &Chunk)
|
||||
: Context(Context), Chunk(Chunk) {}
|
||||
DeclaratorLocFiller(ASTContext &Context, TypeProcessingState &State,
|
||||
const DeclaratorChunk &Chunk)
|
||||
: Context(Context), State(State), Chunk(Chunk) {}
|
||||
|
||||
void VisitQualifiedTypeLoc(QualifiedTypeLoc TL) {
|
||||
llvm_unreachable("qualified type locs not expected here!");
|
||||
|
@ -5456,7 +5424,7 @@ namespace {
|
|||
}
|
||||
|
||||
void VisitAttributedTypeLoc(AttributedTypeLoc TL) {
|
||||
fillAttributedTypeLoc(TL, Chunk.getAttrs(), ParsedAttributesView{});
|
||||
fillAttributedTypeLoc(TL, State);
|
||||
}
|
||||
void VisitAdjustedTypeLoc(AdjustedTypeLoc TL) {
|
||||
// nothing
|
||||
|
@ -5613,10 +5581,13 @@ fillDependentAddressSpaceTypeLoc(DependentAddressSpaceTypeLoc DASTL,
|
|||
/// up in the normal place in the declaration specifiers (such as a C++
|
||||
/// conversion function), this pointer will refer to a type source information
|
||||
/// for that return type.
|
||||
TypeSourceInfo *
|
||||
Sema::GetTypeSourceInfoForDeclarator(Declarator &D, QualType T,
|
||||
TypeSourceInfo *ReturnTypeInfo) {
|
||||
TypeSourceInfo *TInfo = Context.CreateTypeSourceInfo(T);
|
||||
static TypeSourceInfo *
|
||||
GetTypeSourceInfoForDeclarator(TypeProcessingState &State,
|
||||
QualType T, TypeSourceInfo *ReturnTypeInfo) {
|
||||
Sema &S = State.getSema();
|
||||
Declarator &D = State.getDeclarator();
|
||||
|
||||
TypeSourceInfo *TInfo = S.Context.CreateTypeSourceInfo(T);
|
||||
UnqualTypeLoc CurrTL = TInfo->getTypeLoc().getUnqualifiedLoc();
|
||||
|
||||
// Handle parameter packs whose type is a pack expansion.
|
||||
|
@ -5626,7 +5597,6 @@ Sema::GetTypeSourceInfoForDeclarator(Declarator &D, QualType T,
|
|||
}
|
||||
|
||||
for (unsigned i = 0, e = D.getNumTypeObjects(); i != e; ++i) {
|
||||
|
||||
if (DependentAddressSpaceTypeLoc DASTL =
|
||||
CurrTL.getAs<DependentAddressSpaceTypeLoc>()) {
|
||||
fillDependentAddressSpaceTypeLoc(DASTL, D.getTypeObject(i).getAttrs());
|
||||
|
@ -5641,8 +5611,7 @@ Sema::GetTypeSourceInfoForDeclarator(Declarator &D, QualType T,
|
|||
}
|
||||
|
||||
while (AttributedTypeLoc TL = CurrTL.getAs<AttributedTypeLoc>()) {
|
||||
fillAttributedTypeLoc(TL, D.getTypeObject(i).getAttrs(),
|
||||
D.getAttributes());
|
||||
fillAttributedTypeLoc(TL, State);
|
||||
CurrTL = TL.getNextTypeLoc().getUnqualifiedLoc();
|
||||
}
|
||||
|
||||
|
@ -5650,7 +5619,7 @@ Sema::GetTypeSourceInfoForDeclarator(Declarator &D, QualType T,
|
|||
while (AdjustedTypeLoc TL = CurrTL.getAs<AdjustedTypeLoc>())
|
||||
CurrTL = TL.getNextTypeLoc().getUnqualifiedLoc();
|
||||
|
||||
DeclaratorLocFiller(Context, D.getTypeObject(i)).Visit(CurrTL);
|
||||
DeclaratorLocFiller(S.Context, State, D.getTypeObject(i)).Visit(CurrTL);
|
||||
CurrTL = CurrTL.getNextTypeLoc().getUnqualifiedLoc();
|
||||
}
|
||||
|
||||
|
@ -5661,7 +5630,7 @@ Sema::GetTypeSourceInfoForDeclarator(Declarator &D, QualType T,
|
|||
assert(TL.getFullDataSize() == CurrTL.getFullDataSize());
|
||||
memcpy(CurrTL.getOpaqueData(), TL.getOpaqueData(), TL.getFullDataSize());
|
||||
} else {
|
||||
TypeSpecLocFiller(Context, D.getDeclSpec()).Visit(CurrTL);
|
||||
TypeSpecLocFiller(S.Context, State, D.getDeclSpec()).Visit(CurrTL);
|
||||
}
|
||||
|
||||
return TInfo;
|
||||
|
@ -5890,7 +5859,7 @@ static bool hasDirectOwnershipQualifier(QualType type) {
|
|||
while (true) {
|
||||
// __strong id
|
||||
if (const AttributedType *attr = dyn_cast<AttributedType>(type)) {
|
||||
if (attr->getAttrKind() == AttributedType::attr_objc_ownership)
|
||||
if (attr->getAttrKind() == attr::ObjCOwnership)
|
||||
return true;
|
||||
|
||||
type = attr->getModifiedType();
|
||||
|
@ -6034,9 +6003,9 @@ static bool handleObjCOwnershipTypeAttr(TypeProcessingState &state,
|
|||
// the coexistence problems with __unsafe_unretained.
|
||||
if (!S.getLangOpts().ObjCAutoRefCount &&
|
||||
lifetime == Qualifiers::OCL_ExplicitNone) {
|
||||
type = S.Context.getAttributedType(
|
||||
AttributedType::attr_objc_inert_unsafe_unretained,
|
||||
type, type);
|
||||
type = state.getAttributedType(
|
||||
createSimpleAttr<ObjCInertUnsafeUnretainedAttr>(S.Context, attr),
|
||||
type, type);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -6046,9 +6015,12 @@ static bool handleObjCOwnershipTypeAttr(TypeProcessingState &state,
|
|||
|
||||
// If we have a valid source location for the attribute, use an
|
||||
// AttributedType instead.
|
||||
if (AttrLoc.isValid())
|
||||
type = S.Context.getAttributedType(AttributedType::attr_objc_ownership,
|
||||
origType, type);
|
||||
if (AttrLoc.isValid()) {
|
||||
type = state.getAttributedType(::new (S.Context) ObjCOwnershipAttr(
|
||||
attr.getRange(), S.Context, II,
|
||||
attr.getAttributeSpellingListIndex()),
|
||||
origType, type);
|
||||
}
|
||||
|
||||
auto diagnoseOrDelay = [](Sema &S, SourceLocation loc,
|
||||
unsigned diagnostic, QualType type) {
|
||||
|
@ -6148,8 +6120,10 @@ static bool handleObjCGCTypeAttr(TypeProcessingState &state, ParsedAttr &attr,
|
|||
|
||||
// Make an attributed type to preserve the source information.
|
||||
if (attr.getLoc().isValid())
|
||||
type = S.Context.getAttributedType(AttributedType::attr_objc_gc,
|
||||
origType, type);
|
||||
type = state.getAttributedType(
|
||||
::new (S.Context) ObjCGCAttr(attr.getRange(), S.Context, II,
|
||||
attr.getAttributeSpellingListIndex()),
|
||||
origType, type);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -6292,37 +6266,50 @@ namespace {
|
|||
} // end anonymous namespace
|
||||
|
||||
static bool handleMSPointerTypeQualifierAttr(TypeProcessingState &State,
|
||||
ParsedAttr &Attr, QualType &Type) {
|
||||
ParsedAttr &PAttr, QualType &Type) {
|
||||
Sema &S = State.getSema();
|
||||
|
||||
ParsedAttr::Kind Kind = Attr.getKind();
|
||||
Attr *A;
|
||||
switch (PAttr.getKind()) {
|
||||
default: llvm_unreachable("Unknown attribute kind");
|
||||
case ParsedAttr::AT_Ptr32:
|
||||
A = createSimpleAttr<Ptr32Attr>(S.Context, PAttr);
|
||||
break;
|
||||
case ParsedAttr::AT_Ptr64:
|
||||
A = createSimpleAttr<Ptr64Attr>(S.Context, PAttr);
|
||||
break;
|
||||
case ParsedAttr::AT_SPtr:
|
||||
A = createSimpleAttr<SPtrAttr>(S.Context, PAttr);
|
||||
break;
|
||||
case ParsedAttr::AT_UPtr:
|
||||
A = createSimpleAttr<UPtrAttr>(S.Context, PAttr);
|
||||
break;
|
||||
}
|
||||
|
||||
attr::Kind NewAttrKind = A->getKind();
|
||||
QualType Desugared = Type;
|
||||
const AttributedType *AT = dyn_cast<AttributedType>(Type);
|
||||
while (AT) {
|
||||
AttributedType::Kind CurAttrKind = AT->getAttrKind();
|
||||
attr::Kind CurAttrKind = AT->getAttrKind();
|
||||
|
||||
// You cannot specify duplicate type attributes, so if the attribute has
|
||||
// already been applied, flag it.
|
||||
if (getAttrListKind(CurAttrKind) == Kind) {
|
||||
S.Diag(Attr.getLoc(), diag::warn_duplicate_attribute_exact)
|
||||
<< Attr.getName();
|
||||
if (NewAttrKind == CurAttrKind) {
|
||||
S.Diag(PAttr.getLoc(), diag::warn_duplicate_attribute_exact)
|
||||
<< PAttr.getName();
|
||||
return true;
|
||||
}
|
||||
|
||||
// You cannot have both __sptr and __uptr on the same type, nor can you
|
||||
// have __ptr32 and __ptr64.
|
||||
if ((CurAttrKind == AttributedType::attr_ptr32 &&
|
||||
Kind == ParsedAttr::AT_Ptr64) ||
|
||||
(CurAttrKind == AttributedType::attr_ptr64 &&
|
||||
Kind == ParsedAttr::AT_Ptr32)) {
|
||||
S.Diag(Attr.getLoc(), diag::err_attributes_are_not_compatible)
|
||||
if ((CurAttrKind == attr::Ptr32 && NewAttrKind == attr::Ptr64) ||
|
||||
(CurAttrKind == attr::Ptr64 && NewAttrKind == attr::Ptr32)) {
|
||||
S.Diag(PAttr.getLoc(), diag::err_attributes_are_not_compatible)
|
||||
<< "'__ptr32'" << "'__ptr64'";
|
||||
return true;
|
||||
} else if ((CurAttrKind == AttributedType::attr_sptr &&
|
||||
Kind == ParsedAttr::AT_UPtr) ||
|
||||
(CurAttrKind == AttributedType::attr_uptr &&
|
||||
Kind == ParsedAttr::AT_SPtr)) {
|
||||
S.Diag(Attr.getLoc(), diag::err_attributes_are_not_compatible)
|
||||
} else if ((CurAttrKind == attr::SPtr && NewAttrKind == attr::UPtr) ||
|
||||
(CurAttrKind == attr::UPtr && NewAttrKind == attr::SPtr)) {
|
||||
S.Diag(PAttr.getLoc(), diag::err_attributes_are_not_compatible)
|
||||
<< "'__sptr'" << "'__uptr'";
|
||||
return true;
|
||||
}
|
||||
|
@ -6333,175 +6320,20 @@ static bool handleMSPointerTypeQualifierAttr(TypeProcessingState &State,
|
|||
|
||||
// Pointer type qualifiers can only operate on pointer types, but not
|
||||
// pointer-to-member types.
|
||||
//
|
||||
// FIXME: Should we really be disallowing this attribute if there is any
|
||||
// type sugar between it and the pointer (other than attributes)? Eg, this
|
||||
// disallows the attribute on a parenthesized pointer.
|
||||
// And if so, should we really allow *any* type attribute?
|
||||
if (!isa<PointerType>(Desugared)) {
|
||||
if (Type->isMemberPointerType())
|
||||
S.Diag(Attr.getLoc(), diag::err_attribute_no_member_pointers) << Attr;
|
||||
S.Diag(PAttr.getLoc(), diag::err_attribute_no_member_pointers) << PAttr;
|
||||
else
|
||||
S.Diag(Attr.getLoc(), diag::err_attribute_pointers_only) << Attr << 0;
|
||||
S.Diag(PAttr.getLoc(), diag::err_attribute_pointers_only) << PAttr << 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
AttributedType::Kind TAK;
|
||||
switch (Kind) {
|
||||
default: llvm_unreachable("Unknown attribute kind");
|
||||
case ParsedAttr::AT_Ptr32:
|
||||
TAK = AttributedType::attr_ptr32;
|
||||
break;
|
||||
case ParsedAttr::AT_Ptr64:
|
||||
TAK = AttributedType::attr_ptr64;
|
||||
break;
|
||||
case ParsedAttr::AT_SPtr:
|
||||
TAK = AttributedType::attr_sptr;
|
||||
break;
|
||||
case ParsedAttr::AT_UPtr:
|
||||
TAK = AttributedType::attr_uptr;
|
||||
break;
|
||||
}
|
||||
|
||||
Type = S.Context.getAttributedType(TAK, Type, Type);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Sema::checkNullabilityTypeSpecifier(QualType &type,
|
||||
NullabilityKind nullability,
|
||||
SourceLocation nullabilityLoc,
|
||||
bool isContextSensitive,
|
||||
bool allowOnArrayType) {
|
||||
recordNullabilitySeen(*this, nullabilityLoc);
|
||||
|
||||
// Check for existing nullability attributes on the type.
|
||||
QualType desugared = type;
|
||||
while (auto attributed = dyn_cast<AttributedType>(desugared.getTypePtr())) {
|
||||
// Check whether there is already a null
|
||||
if (auto existingNullability = attributed->getImmediateNullability()) {
|
||||
// Duplicated nullability.
|
||||
if (nullability == *existingNullability) {
|
||||
Diag(nullabilityLoc, diag::warn_nullability_duplicate)
|
||||
<< DiagNullabilityKind(nullability, isContextSensitive)
|
||||
<< FixItHint::CreateRemoval(nullabilityLoc);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Conflicting nullability.
|
||||
Diag(nullabilityLoc, diag::err_nullability_conflicting)
|
||||
<< DiagNullabilityKind(nullability, isContextSensitive)
|
||||
<< DiagNullabilityKind(*existingNullability, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
desugared = attributed->getModifiedType();
|
||||
}
|
||||
|
||||
// If there is already a different nullability specifier, complain.
|
||||
// This (unlike the code above) looks through typedefs that might
|
||||
// have nullability specifiers on them, which means we cannot
|
||||
// provide a useful Fix-It.
|
||||
if (auto existingNullability = desugared->getNullability(Context)) {
|
||||
if (nullability != *existingNullability) {
|
||||
Diag(nullabilityLoc, diag::err_nullability_conflicting)
|
||||
<< DiagNullabilityKind(nullability, isContextSensitive)
|
||||
<< DiagNullabilityKind(*existingNullability, false);
|
||||
|
||||
// Try to find the typedef with the existing nullability specifier.
|
||||
if (auto typedefType = desugared->getAs<TypedefType>()) {
|
||||
TypedefNameDecl *typedefDecl = typedefType->getDecl();
|
||||
QualType underlyingType = typedefDecl->getUnderlyingType();
|
||||
if (auto typedefNullability
|
||||
= AttributedType::stripOuterNullability(underlyingType)) {
|
||||
if (*typedefNullability == *existingNullability) {
|
||||
Diag(typedefDecl->getLocation(), diag::note_nullability_here)
|
||||
<< DiagNullabilityKind(*existingNullability, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// If this definitely isn't a pointer type, reject the specifier.
|
||||
if (!desugared->canHaveNullability() &&
|
||||
!(allowOnArrayType && desugared->isArrayType())) {
|
||||
Diag(nullabilityLoc, diag::err_nullability_nonpointer)
|
||||
<< DiagNullabilityKind(nullability, isContextSensitive) << type;
|
||||
return true;
|
||||
}
|
||||
|
||||
// For the context-sensitive keywords/Objective-C property
|
||||
// attributes, require that the type be a single-level pointer.
|
||||
if (isContextSensitive) {
|
||||
// Make sure that the pointee isn't itself a pointer type.
|
||||
const Type *pointeeType;
|
||||
if (desugared->isArrayType())
|
||||
pointeeType = desugared->getArrayElementTypeNoTypeQual();
|
||||
else
|
||||
pointeeType = desugared->getPointeeType().getTypePtr();
|
||||
|
||||
if (pointeeType->isAnyPointerType() ||
|
||||
pointeeType->isObjCObjectPointerType() ||
|
||||
pointeeType->isMemberPointerType()) {
|
||||
Diag(nullabilityLoc, diag::err_nullability_cs_multilevel)
|
||||
<< DiagNullabilityKind(nullability, true)
|
||||
<< type;
|
||||
Diag(nullabilityLoc, diag::note_nullability_type_specifier)
|
||||
<< DiagNullabilityKind(nullability, false)
|
||||
<< type
|
||||
<< FixItHint::CreateReplacement(nullabilityLoc,
|
||||
getNullabilitySpelling(nullability));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Form the attributed type.
|
||||
type = Context.getAttributedType(
|
||||
AttributedType::getNullabilityAttrKind(nullability), type, type);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Sema::checkObjCKindOfType(QualType &type, SourceLocation loc) {
|
||||
if (isa<ObjCTypeParamType>(type)) {
|
||||
// Build the attributed type to record where __kindof occurred.
|
||||
type = Context.getAttributedType(AttributedType::attr_objc_kindof,
|
||||
type, type);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find out if it's an Objective-C object or object pointer type;
|
||||
const ObjCObjectPointerType *ptrType = type->getAs<ObjCObjectPointerType>();
|
||||
const ObjCObjectType *objType = ptrType ? ptrType->getObjectType()
|
||||
: type->getAs<ObjCObjectType>();
|
||||
|
||||
// If not, we can't apply __kindof.
|
||||
if (!objType) {
|
||||
// FIXME: Handle dependent types that aren't yet object types.
|
||||
Diag(loc, diag::err_objc_kindof_nonobject)
|
||||
<< type;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Rebuild the "equivalent" type, which pushes __kindof down into
|
||||
// the object type.
|
||||
// There is no need to apply kindof on an unqualified id type.
|
||||
QualType equivType = Context.getObjCObjectType(
|
||||
objType->getBaseType(), objType->getTypeArgsAsWritten(),
|
||||
objType->getProtocols(),
|
||||
/*isKindOf=*/objType->isObjCUnqualifiedId() ? false : true);
|
||||
|
||||
// If we started with an object pointer type, rebuild it.
|
||||
if (ptrType) {
|
||||
equivType = Context.getObjCObjectPointerType(equivType);
|
||||
if (auto nullability = type->getNullability(Context)) {
|
||||
auto attrKind = AttributedType::getNullabilityAttrKind(*nullability);
|
||||
equivType = Context.getAttributedType(attrKind, equivType, equivType);
|
||||
}
|
||||
}
|
||||
|
||||
// Build the attributed type to record where __kindof occurred.
|
||||
type = Context.getAttributedType(AttributedType::attr_objc_kindof,
|
||||
type,
|
||||
equivType);
|
||||
|
||||
Type = State.getAttributedType(A, Type, Type);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -6522,6 +6354,175 @@ static NullabilityKind mapNullabilityAttrKind(ParsedAttr::Kind kind) {
|
|||
}
|
||||
}
|
||||
|
||||
/// Applies a nullability type specifier to the given type, if possible.
|
||||
///
|
||||
/// \param state The type processing state.
|
||||
///
|
||||
/// \param type The type to which the nullability specifier will be
|
||||
/// added. On success, this type will be updated appropriately.
|
||||
///
|
||||
/// \param attr The attribute as written on the type.
|
||||
///
|
||||
/// \param allowArrayTypes Whether to accept nullability specifiers on an
|
||||
/// array type (e.g., because it will decay to a pointer).
|
||||
///
|
||||
/// \returns true if a problem has been diagnosed, false on success.
|
||||
static bool checkNullabilityTypeSpecifier(TypeProcessingState &state,
|
||||
QualType &type,
|
||||
ParsedAttr &attr,
|
||||
bool allowOnArrayType) {
|
||||
Sema &S = state.getSema();
|
||||
|
||||
NullabilityKind nullability = mapNullabilityAttrKind(attr.getKind());
|
||||
SourceLocation nullabilityLoc = attr.getLoc();
|
||||
bool isContextSensitive = attr.isContextSensitiveKeywordAttribute();
|
||||
|
||||
recordNullabilitySeen(S, nullabilityLoc);
|
||||
|
||||
// Check for existing nullability attributes on the type.
|
||||
QualType desugared = type;
|
||||
while (auto attributed = dyn_cast<AttributedType>(desugared.getTypePtr())) {
|
||||
// Check whether there is already a null
|
||||
if (auto existingNullability = attributed->getImmediateNullability()) {
|
||||
// Duplicated nullability.
|
||||
if (nullability == *existingNullability) {
|
||||
S.Diag(nullabilityLoc, diag::warn_nullability_duplicate)
|
||||
<< DiagNullabilityKind(nullability, isContextSensitive)
|
||||
<< FixItHint::CreateRemoval(nullabilityLoc);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Conflicting nullability.
|
||||
S.Diag(nullabilityLoc, diag::err_nullability_conflicting)
|
||||
<< DiagNullabilityKind(nullability, isContextSensitive)
|
||||
<< DiagNullabilityKind(*existingNullability, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
desugared = attributed->getModifiedType();
|
||||
}
|
||||
|
||||
// If there is already a different nullability specifier, complain.
|
||||
// This (unlike the code above) looks through typedefs that might
|
||||
// have nullability specifiers on them, which means we cannot
|
||||
// provide a useful Fix-It.
|
||||
if (auto existingNullability = desugared->getNullability(S.Context)) {
|
||||
if (nullability != *existingNullability) {
|
||||
S.Diag(nullabilityLoc, diag::err_nullability_conflicting)
|
||||
<< DiagNullabilityKind(nullability, isContextSensitive)
|
||||
<< DiagNullabilityKind(*existingNullability, false);
|
||||
|
||||
// Try to find the typedef with the existing nullability specifier.
|
||||
if (auto typedefType = desugared->getAs<TypedefType>()) {
|
||||
TypedefNameDecl *typedefDecl = typedefType->getDecl();
|
||||
QualType underlyingType = typedefDecl->getUnderlyingType();
|
||||
if (auto typedefNullability
|
||||
= AttributedType::stripOuterNullability(underlyingType)) {
|
||||
if (*typedefNullability == *existingNullability) {
|
||||
S.Diag(typedefDecl->getLocation(), diag::note_nullability_here)
|
||||
<< DiagNullabilityKind(*existingNullability, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// If this definitely isn't a pointer type, reject the specifier.
|
||||
if (!desugared->canHaveNullability() &&
|
||||
!(allowOnArrayType && desugared->isArrayType())) {
|
||||
S.Diag(nullabilityLoc, diag::err_nullability_nonpointer)
|
||||
<< DiagNullabilityKind(nullability, isContextSensitive) << type;
|
||||
return true;
|
||||
}
|
||||
|
||||
// For the context-sensitive keywords/Objective-C property
|
||||
// attributes, require that the type be a single-level pointer.
|
||||
if (isContextSensitive) {
|
||||
// Make sure that the pointee isn't itself a pointer type.
|
||||
const Type *pointeeType;
|
||||
if (desugared->isArrayType())
|
||||
pointeeType = desugared->getArrayElementTypeNoTypeQual();
|
||||
else
|
||||
pointeeType = desugared->getPointeeType().getTypePtr();
|
||||
|
||||
if (pointeeType->isAnyPointerType() ||
|
||||
pointeeType->isObjCObjectPointerType() ||
|
||||
pointeeType->isMemberPointerType()) {
|
||||
S.Diag(nullabilityLoc, diag::err_nullability_cs_multilevel)
|
||||
<< DiagNullabilityKind(nullability, true)
|
||||
<< type;
|
||||
S.Diag(nullabilityLoc, diag::note_nullability_type_specifier)
|
||||
<< DiagNullabilityKind(nullability, false)
|
||||
<< type
|
||||
<< FixItHint::CreateReplacement(nullabilityLoc,
|
||||
getNullabilitySpelling(nullability));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Form the attributed type.
|
||||
type = state.getAttributedType(
|
||||
createNullabilityAttr(S.Context, attr, nullability), type, type);
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Check the application of the Objective-C '__kindof' qualifier to
|
||||
/// the given type.
|
||||
static bool checkObjCKindOfType(TypeProcessingState &state, QualType &type,
|
||||
ParsedAttr &attr) {
|
||||
Sema &S = state.getSema();
|
||||
|
||||
if (isa<ObjCTypeParamType>(type)) {
|
||||
// Build the attributed type to record where __kindof occurred.
|
||||
type = state.getAttributedType(
|
||||
createSimpleAttr<ObjCKindOfAttr>(S.Context, attr), type, type);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find out if it's an Objective-C object or object pointer type;
|
||||
const ObjCObjectPointerType *ptrType = type->getAs<ObjCObjectPointerType>();
|
||||
const ObjCObjectType *objType = ptrType ? ptrType->getObjectType()
|
||||
: type->getAs<ObjCObjectType>();
|
||||
|
||||
// If not, we can't apply __kindof.
|
||||
if (!objType) {
|
||||
// FIXME: Handle dependent types that aren't yet object types.
|
||||
S.Diag(attr.getLoc(), diag::err_objc_kindof_nonobject)
|
||||
<< type;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Rebuild the "equivalent" type, which pushes __kindof down into
|
||||
// the object type.
|
||||
// There is no need to apply kindof on an unqualified id type.
|
||||
QualType equivType = S.Context.getObjCObjectType(
|
||||
objType->getBaseType(), objType->getTypeArgsAsWritten(),
|
||||
objType->getProtocols(),
|
||||
/*isKindOf=*/objType->isObjCUnqualifiedId() ? false : true);
|
||||
|
||||
// If we started with an object pointer type, rebuild it.
|
||||
if (ptrType) {
|
||||
equivType = S.Context.getObjCObjectPointerType(equivType);
|
||||
if (auto nullability = type->getNullability(S.Context)) {
|
||||
// We create a nullability attribute from the __kindof attribute.
|
||||
// Make sure that will make sense.
|
||||
assert(attr.getAttributeSpellingListIndex() == 0 &&
|
||||
"multiple spellings for __kindof?");
|
||||
Attr *A = createNullabilityAttr(S.Context, attr, *nullability);
|
||||
A->setImplicit(true);
|
||||
equivType = state.getAttributedType(A, equivType, equivType);
|
||||
}
|
||||
}
|
||||
|
||||
// Build the attributed type to record where __kindof occurred.
|
||||
type = state.getAttributedType(
|
||||
createSimpleAttr<ObjCKindOfAttr>(S.Context, attr), type, equivType);
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Distribute a nullability type attribute that cannot be applied to
|
||||
/// the type specifier to a pointer, block pointer, or member pointer
|
||||
/// declarator, complaining if necessary.
|
||||
|
@ -6609,27 +6610,27 @@ static bool distributeNullabilityTypeAttr(TypeProcessingState &state,
|
|||
return false;
|
||||
}
|
||||
|
||||
static AttributedType::Kind getCCTypeAttrKind(ParsedAttr &Attr) {
|
||||
static Attr *getCCTypeAttr(ASTContext &Ctx, ParsedAttr &Attr) {
|
||||
assert(!Attr.isInvalid());
|
||||
switch (Attr.getKind()) {
|
||||
default:
|
||||
llvm_unreachable("not a calling convention attribute");
|
||||
case ParsedAttr::AT_CDecl:
|
||||
return AttributedType::attr_cdecl;
|
||||
return createSimpleAttr<CDeclAttr>(Ctx, Attr);
|
||||
case ParsedAttr::AT_FastCall:
|
||||
return AttributedType::attr_fastcall;
|
||||
return createSimpleAttr<FastCallAttr>(Ctx, Attr);
|
||||
case ParsedAttr::AT_StdCall:
|
||||
return AttributedType::attr_stdcall;
|
||||
return createSimpleAttr<StdCallAttr>(Ctx, Attr);
|
||||
case ParsedAttr::AT_ThisCall:
|
||||
return AttributedType::attr_thiscall;
|
||||
return createSimpleAttr<ThisCallAttr>(Ctx, Attr);
|
||||
case ParsedAttr::AT_RegCall:
|
||||
return AttributedType::attr_regcall;
|
||||
return createSimpleAttr<RegCallAttr>(Ctx, Attr);
|
||||
case ParsedAttr::AT_Pascal:
|
||||
return AttributedType::attr_pascal;
|
||||
return createSimpleAttr<PascalAttr>(Ctx, Attr);
|
||||
case ParsedAttr::AT_SwiftCall:
|
||||
return AttributedType::attr_swiftcall;
|
||||
return createSimpleAttr<SwiftCallAttr>(Ctx, Attr);
|
||||
case ParsedAttr::AT_VectorCall:
|
||||
return AttributedType::attr_vectorcall;
|
||||
return createSimpleAttr<VectorCallAttr>(Ctx, Attr);
|
||||
case ParsedAttr::AT_Pcs: {
|
||||
// The attribute may have had a fixit applied where we treated an
|
||||
// identifier as a string literal. The contents of the string are valid,
|
||||
|
@ -6639,20 +6640,22 @@ static AttributedType::Kind getCCTypeAttrKind(ParsedAttr &Attr) {
|
|||
Str = cast<StringLiteral>(Attr.getArgAsExpr(0))->getString();
|
||||
else
|
||||
Str = Attr.getArgAsIdent(0)->Ident->getName();
|
||||
return llvm::StringSwitch<AttributedType::Kind>(Str)
|
||||
.Case("aapcs", AttributedType::attr_pcs)
|
||||
.Case("aapcs-vfp", AttributedType::attr_pcs_vfp);
|
||||
PcsAttr::PCSType Type;
|
||||
if (!PcsAttr::ConvertStrToPCSType(Str, Type))
|
||||
llvm_unreachable("already validated the attribute");
|
||||
return ::new (Ctx) PcsAttr(Attr.getRange(), Ctx, Type,
|
||||
Attr.getAttributeSpellingListIndex());
|
||||
}
|
||||
case ParsedAttr::AT_IntelOclBicc:
|
||||
return AttributedType::attr_inteloclbicc;
|
||||
return createSimpleAttr<IntelOclBiccAttr>(Ctx, Attr);
|
||||
case ParsedAttr::AT_MSABI:
|
||||
return AttributedType::attr_ms_abi;
|
||||
return createSimpleAttr<MSABIAttr>(Ctx, Attr);
|
||||
case ParsedAttr::AT_SysVABI:
|
||||
return AttributedType::attr_sysv_abi;
|
||||
return createSimpleAttr<SysVABIAttr>(Ctx, Attr);
|
||||
case ParsedAttr::AT_PreserveMost:
|
||||
return AttributedType::attr_preserve_most;
|
||||
return createSimpleAttr<PreserveMostAttr>(Ctx, Attr);
|
||||
case ParsedAttr::AT_PreserveAll:
|
||||
return AttributedType::attr_preserve_all;
|
||||
return createSimpleAttr<PreserveAllAttr>(Ctx, Attr);
|
||||
}
|
||||
llvm_unreachable("unexpected attribute kind!");
|
||||
}
|
||||
|
@ -6700,8 +6703,9 @@ static bool handleFunctionTypeAttr(TypeProcessingState &state, ParsedAttr &attr,
|
|||
= unwrapped.get()->getExtInfo().withProducesResult(true);
|
||||
type = unwrapped.wrap(S, S.Context.adjustFunctionType(unwrapped.get(), EI));
|
||||
}
|
||||
type = S.Context.getAttributedType(AttributedType::attr_ns_returns_retained,
|
||||
origType, type);
|
||||
type = state.getAttributedType(
|
||||
createSimpleAttr<NSReturnsRetainedAttr>(S.Context, attr),
|
||||
origType, type);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -6776,13 +6780,12 @@ static bool handleFunctionTypeAttr(TypeProcessingState &state, ParsedAttr &attr,
|
|||
|
||||
const FunctionType *fn = unwrapped.get();
|
||||
CallingConv CCOld = fn->getCallConv();
|
||||
AttributedType::Kind CCAttrKind = getCCTypeAttrKind(attr);
|
||||
Attr *CCAttr = getCCTypeAttr(S.Context, attr);
|
||||
|
||||
if (CCOld != CC) {
|
||||
// Error out on when there's already an attribute on the type
|
||||
// and the CCs don't match.
|
||||
const AttributedType *AT = S.getCallingConvAttributedType(type);
|
||||
if (AT && AT->getAttrKind() != CCAttrKind) {
|
||||
if (const AttributedType *AT = S.getCallingConvAttributedType(type)) {
|
||||
S.Diag(attr.getLoc(), diag::err_attributes_are_not_compatible)
|
||||
<< FunctionType::getNameForCallConv(CC)
|
||||
<< FunctionType::getNameForCallConv(CCOld);
|
||||
|
@ -6836,7 +6839,7 @@ static bool handleFunctionTypeAttr(TypeProcessingState &state, ParsedAttr &attr,
|
|||
Equivalent =
|
||||
unwrapped.wrap(S, S.Context.adjustFunctionType(unwrapped.get(), EI));
|
||||
}
|
||||
type = S.Context.getAttributedType(CCAttrKind, type, Equivalent);
|
||||
type = state.getAttributedType(CCAttr, type, Equivalent);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -7192,14 +7195,15 @@ static void deduceOpenCLImplicitAddrSpace(TypeProcessingState &State,
|
|||
T = State.getSema().Context.getAddrSpaceQualType(T, ImpAddr);
|
||||
}
|
||||
|
||||
static void HandleLifetimeBoundAttr(QualType &CurType,
|
||||
const ParsedAttr &Attr,
|
||||
Sema &S, Declarator &D) {
|
||||
if (D.isDeclarationOfFunction()) {
|
||||
CurType = S.Context.getAttributedType(AttributedType::attr_lifetimebound,
|
||||
CurType, CurType);
|
||||
static void HandleLifetimeBoundAttr(TypeProcessingState &State,
|
||||
QualType &CurType,
|
||||
ParsedAttr &Attr) {
|
||||
if (State.getDeclarator().isDeclarationOfFunction()) {
|
||||
CurType = State.getAttributedType(
|
||||
createSimpleAttr<LifetimeBoundAttr>(State.getSema().Context, Attr),
|
||||
CurType, CurType);
|
||||
} else {
|
||||
Attr.diagnoseAppertainsTo(S, nullptr);
|
||||
Attr.diagnoseAppertainsTo(State.getSema(), nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7309,11 +7313,8 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type,
|
|||
attr.setUsedAsTypeAttr();
|
||||
break;
|
||||
case ParsedAttr::AT_LifetimeBound:
|
||||
if (TAL == TAL_DeclChunk) {
|
||||
HandleLifetimeBoundAttr(type, attr, state.getSema(),
|
||||
state.getDeclarator());
|
||||
attr.setUsedAsTypeAttr();
|
||||
}
|
||||
if (TAL == TAL_DeclChunk)
|
||||
HandleLifetimeBoundAttr(state, type, attr);
|
||||
break;
|
||||
|
||||
MS_TYPE_ATTRS_CASELIST:
|
||||
|
@ -7337,11 +7338,10 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type,
|
|||
bool allowOnArrayType =
|
||||
state.getDeclarator().isPrototypeContext() &&
|
||||
!hasOuterPointerLikeChunk(state.getDeclarator(), endIndex);
|
||||
if (state.getSema().checkNullabilityTypeSpecifier(
|
||||
if (checkNullabilityTypeSpecifier(
|
||||
state,
|
||||
type,
|
||||
mapNullabilityAttrKind(attr.getKind()),
|
||||
attr.getLoc(),
|
||||
attr.isContextSensitiveKeywordAttribute(),
|
||||
attr,
|
||||
allowOnArrayType)) {
|
||||
attr.setInvalid();
|
||||
}
|
||||
|
@ -7368,9 +7368,8 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type,
|
|||
}
|
||||
|
||||
// Apply it regardless.
|
||||
if (state.getSema().checkObjCKindOfType(type, attr.getLoc()))
|
||||
if (checkObjCKindOfType(state, type, attr))
|
||||
attr.setInvalid();
|
||||
attr.setUsedAsTypeAttr();
|
||||
break;
|
||||
|
||||
FUNCTION_TYPE_ATTRS_CASELIST:
|
||||
|
|
|
@ -6058,6 +6058,12 @@ QualType TreeTransform<Derived>::TransformAttributedType(
|
|||
if (modifiedType.isNull())
|
||||
return QualType();
|
||||
|
||||
// oldAttr can be null if we started with a QualType rather than a TypeLoc.
|
||||
const Attr *oldAttr = TL.getAttr();
|
||||
const Attr *newAttr = oldAttr ? getDerived().TransformAttr(oldAttr) : nullptr;
|
||||
if (oldAttr && !newAttr)
|
||||
return QualType();
|
||||
|
||||
QualType result = TL.getType();
|
||||
|
||||
// FIXME: dependent operand expressions?
|
||||
|
@ -6074,26 +6080,20 @@ QualType TreeTransform<Derived>::TransformAttributedType(
|
|||
// type sugar, and therefore cannot be diagnosed in any other way.
|
||||
if (auto nullability = oldType->getImmediateNullability()) {
|
||||
if (!modifiedType->canHaveNullability()) {
|
||||
SemaRef.Diag(TL.getAttrNameLoc(), diag::err_nullability_nonpointer)
|
||||
<< DiagNullabilityKind(*nullability, false) << modifiedType;
|
||||
SemaRef.Diag(TL.getAttr()->getLocation(),
|
||||
diag::err_nullability_nonpointer)
|
||||
<< DiagNullabilityKind(*nullability, false) << modifiedType;
|
||||
return QualType();
|
||||
}
|
||||
}
|
||||
|
||||
result = SemaRef.Context.getAttributedType(oldType->getAttrKind(),
|
||||
result = SemaRef.Context.getAttributedType(TL.getAttrKind(),
|
||||
modifiedType,
|
||||
equivalentType);
|
||||
}
|
||||
|
||||
AttributedTypeLoc newTL = TLB.push<AttributedTypeLoc>(result);
|
||||
newTL.setAttrNameLoc(TL.getAttrNameLoc());
|
||||
if (TL.hasAttrOperand())
|
||||
newTL.setAttrOperandParensRange(TL.getAttrOperandParensRange());
|
||||
if (TL.hasAttrExprOperand())
|
||||
newTL.setAttrExprOperand(TL.getAttrExprOperand());
|
||||
else if (TL.hasAttrEnumOperand())
|
||||
newTL.setAttrEnumOperandLoc(TL.getAttrEnumOperandLoc());
|
||||
|
||||
newTL.setAttr(newAttr);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -6455,6 +6455,10 @@ class TypeLocReader : public TypeLocVisitor<TypeLocReader> {
|
|||
return Reader->ReadNestedNameSpecifierLoc(*F, Record, Idx);
|
||||
}
|
||||
|
||||
Attr *ReadAttr() {
|
||||
return Reader->ReadAttr(*F, Record, Idx);
|
||||
}
|
||||
|
||||
public:
|
||||
TypeLocReader(ModuleFile &F, ASTReader &Reader,
|
||||
const ASTReader::RecordData &Record, unsigned &Idx)
|
||||
|
@ -6646,20 +6650,7 @@ void TypeLocReader::VisitEnumTypeLoc(EnumTypeLoc TL) {
|
|||
}
|
||||
|
||||
void TypeLocReader::VisitAttributedTypeLoc(AttributedTypeLoc TL) {
|
||||
TL.setAttrNameLoc(ReadSourceLocation());
|
||||
if (TL.hasAttrOperand()) {
|
||||
SourceRange range;
|
||||
range.setBegin(ReadSourceLocation());
|
||||
range.setEnd(ReadSourceLocation());
|
||||
TL.setAttrOperandParensRange(range);
|
||||
}
|
||||
if (TL.hasAttrExprOperand()) {
|
||||
if (Record[Idx++])
|
||||
TL.setAttrExprOperand(Reader->ReadExpr(*F));
|
||||
else
|
||||
TL.setAttrExprOperand(nullptr);
|
||||
} else if (TL.hasAttrEnumOperand())
|
||||
TL.setAttrEnumOperandLoc(ReadSourceLocation());
|
||||
TL.setAttr(ReadAttr());
|
||||
}
|
||||
|
||||
void TypeLocReader::VisitTemplateTypeParmTypeLoc(TemplateTypeParmTypeLoc TL) {
|
||||
|
|
|
@ -2648,19 +2648,72 @@ void ASTDeclReader::VisitOMPCapturedExprDecl(OMPCapturedExprDecl *D) {
|
|||
// Attribute Reading
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// Reads attributes from the current stream position.
|
||||
void ASTReader::ReadAttributes(ASTRecordReader &Record, AttrVec &Attrs) {
|
||||
for (unsigned i = 0, e = Record.readInt(); i != e; ++i) {
|
||||
Attr *New = nullptr;
|
||||
auto Kind = (attr::Kind)Record.readInt();
|
||||
SourceRange Range = Record.readSourceRange();
|
||||
ASTContext &Context = getContext();
|
||||
namespace {
|
||||
class AttrReader {
|
||||
ModuleFile *F;
|
||||
ASTReader *Reader;
|
||||
const ASTReader::RecordData &Record;
|
||||
unsigned &Idx;
|
||||
|
||||
public:
|
||||
AttrReader(ModuleFile &F, ASTReader &Reader,
|
||||
const ASTReader::RecordData &Record, unsigned &Idx)
|
||||
: F(&F), Reader(&Reader), Record(Record), Idx(Idx) {}
|
||||
|
||||
const uint64_t &readInt() { return Record[Idx++]; }
|
||||
|
||||
SourceRange readSourceRange() {
|
||||
return Reader->ReadSourceRange(*F, Record, Idx);
|
||||
}
|
||||
|
||||
Expr *readExpr() { return Reader->ReadExpr(*F); }
|
||||
|
||||
std::string readString() {
|
||||
return Reader->ReadString(Record, Idx);
|
||||
}
|
||||
|
||||
TypeSourceInfo *getTypeSourceInfo() {
|
||||
return Reader->GetTypeSourceInfo(*F, Record, Idx);
|
||||
}
|
||||
|
||||
IdentifierInfo *getIdentifierInfo() {
|
||||
return Reader->GetIdentifierInfo(*F, Record, Idx);
|
||||
}
|
||||
|
||||
VersionTuple readVersionTuple() {
|
||||
return ASTReader::ReadVersionTuple(Record, Idx);
|
||||
}
|
||||
|
||||
template <typename T> T *GetLocalDeclAs(uint32_t LocalID) {
|
||||
return cast_or_null<T>(Reader->GetLocalDecl(*F, LocalID));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Attr *ASTReader::ReadAttr(ModuleFile &M, const RecordData &Rec,
|
||||
unsigned &Idx) {
|
||||
AttrReader Record(M, *this, Rec, Idx);
|
||||
auto V = Record.readInt();
|
||||
if (!V)
|
||||
return nullptr;
|
||||
|
||||
Attr *New = nullptr;
|
||||
// Kind is stored as a 1-based integer because 0 is used to indicate a null
|
||||
// Attr pointer.
|
||||
auto Kind = static_cast<attr::Kind>(V - 1);
|
||||
SourceRange Range = Record.readSourceRange();
|
||||
ASTContext &Context = getContext();
|
||||
|
||||
#include "clang/Serialization/AttrPCHRead.inc"
|
||||
|
||||
assert(New && "Unable to decode attribute?");
|
||||
Attrs.push_back(New);
|
||||
}
|
||||
assert(New && "Unable to decode attribute?");
|
||||
return New;
|
||||
}
|
||||
|
||||
/// Reads attributes from the current stream position.
|
||||
void ASTReader::ReadAttributes(ASTRecordReader &Record, AttrVec &Attrs) {
|
||||
for (unsigned I = 0, E = Record.readInt(); I != E; ++I)
|
||||
Attrs.push_back(Record.readAttr());
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -770,19 +770,7 @@ void TypeLocWriter::VisitEnumTypeLoc(EnumTypeLoc TL) {
|
|||
}
|
||||
|
||||
void TypeLocWriter::VisitAttributedTypeLoc(AttributedTypeLoc TL) {
|
||||
Record.AddSourceLocation(TL.getAttrNameLoc());
|
||||
if (TL.hasAttrOperand()) {
|
||||
SourceRange range = TL.getAttrOperandParensRange();
|
||||
Record.AddSourceLocation(range.getBegin());
|
||||
Record.AddSourceLocation(range.getEnd());
|
||||
}
|
||||
if (TL.hasAttrExprOperand()) {
|
||||
Expr *operand = TL.getAttrExprOperand();
|
||||
Record.push_back(operand ? 1 : 0);
|
||||
if (operand) Record.AddStmt(operand);
|
||||
} else if (TL.hasAttrEnumOperand()) {
|
||||
Record.AddSourceLocation(TL.getAttrEnumOperandLoc());
|
||||
}
|
||||
Record.AddAttr(TL.getAttr());
|
||||
}
|
||||
|
||||
void TypeLocWriter::VisitTemplateTypeParmTypeLoc(TemplateTypeParmTypeLoc TL) {
|
||||
|
@ -4481,16 +4469,21 @@ void ASTWriter::WriteModuleFileExtension(Sema &SemaRef,
|
|||
// General Serialization Routines
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// Emit the list of attributes to the specified record.
|
||||
void ASTRecordWriter::AddAttributes(ArrayRef<const Attr *> Attrs) {
|
||||
void ASTRecordWriter::AddAttr(const Attr *A) {
|
||||
auto &Record = *this;
|
||||
Record.push_back(Attrs.size());
|
||||
for (const auto *A : Attrs) {
|
||||
Record.push_back(A->getKind()); // FIXME: stable encoding, target attrs
|
||||
Record.AddSourceRange(A->getRange());
|
||||
if (!A)
|
||||
return Record.push_back(0);
|
||||
Record.push_back(A->getKind() + 1); // FIXME: stable encoding, target attrs
|
||||
Record.AddSourceRange(A->getRange());
|
||||
|
||||
#include "clang/Serialization/AttrPCHWrite.inc"
|
||||
}
|
||||
}
|
||||
|
||||
/// Emit the list of attributes to the specified record.
|
||||
void ASTRecordWriter::AddAttributes(ArrayRef<const Attr *> Attrs) {
|
||||
push_back(Attrs.size());
|
||||
for (const auto *A : Attrs)
|
||||
AddAttr(A);
|
||||
}
|
||||
|
||||
void ASTWriter::AddToken(const Token &Tok, RecordDataImpl &Record) {
|
||||
|
|
|
@ -103,9 +103,9 @@ Nullability getNullabilityAnnotation(QualType Type) {
|
|||
const auto *AttrType = Type->getAs<AttributedType>();
|
||||
if (!AttrType)
|
||||
return Nullability::Unspecified;
|
||||
if (AttrType->getAttrKind() == AttributedType::attr_nullable)
|
||||
if (AttrType->getAttrKind() == attr::TypeNullable)
|
||||
return Nullability::Nullable;
|
||||
else if (AttrType->getAttrKind() == AttributedType::attr_nonnull)
|
||||
else if (AttrType->getAttrKind() == attr::TypeNonNull)
|
||||
return Nullability::Nonnull;
|
||||
return Nullability::Unspecified;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
// RUN: %clang_cc1 -fsyntax-only -std=c++11 -fms-extensions -verify -triple i686-pc-win32 %s
|
||||
// RUN: %clang_cc1 -fsyntax-only -std=c++11 -fms-extensions -verify -triple x86_64-pc-win32 %s
|
||||
|
||||
// FIXME: Extend this portion of the test to cover the 64-bit case.
|
||||
#if !_M_X64
|
||||
|
||||
// Pointers to free functions
|
||||
void free_func_default();
|
||||
|
@ -244,11 +248,16 @@ void call_member_inheritance() {
|
|||
}
|
||||
} // end namespace NonVariadic
|
||||
|
||||
#endif // !_M_X64
|
||||
|
||||
namespace Variadic {
|
||||
struct A {
|
||||
void member_default(int, ...);
|
||||
void __cdecl member_cdecl(int, ...);
|
||||
void __thiscall member_thiscall(int, ...); // expected-error {{variadic function cannot use thiscall calling convention}}
|
||||
void __thiscall member_thiscall(int, ...);
|
||||
#if !_M_X64
|
||||
// expected-error@-2 {{variadic function cannot use thiscall calling convention}}
|
||||
#endif
|
||||
};
|
||||
|
||||
struct B : public A {
|
||||
|
@ -319,7 +328,10 @@ mptr_t __stdcall return_mptr_std(short) {
|
|||
}
|
||||
|
||||
void (A::*(*return_fptr_std_mptr(char))(short))(int) {
|
||||
return return_mptr_std; // expected-error {{cannot initialize return object of type 'void (MultiChunkDecls::A::*(*)(short))(int) __attribute__((thiscall))' with an lvalue of type 'MultiChunkDecls::mptr_t (short) __attribute__((stdcall))'}}
|
||||
return return_mptr_std;
|
||||
#if !_M_X64
|
||||
// expected-error@-2 {{cannot initialize return object of type 'void (MultiChunkDecls::A::*(*)(short))(int) __attribute__((thiscall))' with an lvalue of type 'MultiChunkDecls::mptr_t (short) __attribute__((stdcall))'}}
|
||||
#endif
|
||||
}
|
||||
|
||||
void call_return() {
|
||||
|
@ -387,15 +399,32 @@ Y<decltype(&A::method_thiscall)>::p tmpl9 = &f_cdecl;
|
|||
|
||||
// Test that lambdas that capture nothing convert to cdecl function pointers.
|
||||
namespace Lambdas {
|
||||
|
||||
void pass_fptr_cdecl (void (__cdecl *fp)());
|
||||
void pass_fptr_stdcall (void (__stdcall *fp)()); // expected-note {{candidate function not viable}}
|
||||
void pass_fptr_fastcall(void (__fastcall *fp)()); // expected-note {{candidate function not viable}}
|
||||
void pass_fptr_stdcall (void (__stdcall *fp)());
|
||||
void pass_fptr_fastcall(void (__fastcall *fp)());
|
||||
|
||||
void conversion_to_fptr() {
|
||||
pass_fptr_cdecl ([]() { } );
|
||||
pass_fptr_stdcall ([]() { } ); // expected-error {{no matching function for call}}
|
||||
pass_fptr_fastcall([]() { } ); // expected-error {{no matching function for call}}
|
||||
|
||||
pass_fptr_stdcall ([]() { } );
|
||||
#if !_M_X64
|
||||
// expected-error@-2 {{no matching function for call}}
|
||||
// expected-note@-9 {{candidate function not viable}}
|
||||
#endif
|
||||
|
||||
pass_fptr_fastcall([]() { } );
|
||||
#if !_M_X64
|
||||
// expected-error@-2 {{no matching function for call}}
|
||||
// expected-note@-14 {{candidate function not viable}}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
namespace D50526 {
|
||||
template<typename T, T (__stdcall f)()> void g();
|
||||
void h() { g<void, h>(); }
|
||||
#if !_M_X64
|
||||
// expected-error@-2 {{no matching function for call to}}
|
||||
// expected-note@-4 {{invalid explicitly-specified argument}}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -2470,8 +2470,10 @@ namespace {
|
|||
|
||||
static const AttrClassDescriptor AttrClassDescriptors[] = {
|
||||
{ "ATTR", "Attr" },
|
||||
{ "TYPE_ATTR", "TypeAttr" },
|
||||
{ "STMT_ATTR", "StmtAttr" },
|
||||
{ "INHERITABLE_ATTR", "InheritableAttr" },
|
||||
{ "DECL_OR_TYPE_ATTR", "DeclOrTypeAttr" },
|
||||
{ "INHERITABLE_PARAM_ATTR", "InheritableParamAttr" },
|
||||
{ "PARAMETER_ABI_ATTR", "ParameterABIAttr" }
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue