mirror of https://github.com/microsoft/clang.git
[Attr] Fix parameter indexing for several attributes
The patch fixes a number of bugs related to parameter indexing in attributes: * Parameter indices in some attributes (argument_with_type_tag, pointer_with_type_tag, nonnull, ownership_takes, ownership_holds, and ownership_returns) are specified in source as one-origin including any C++ implicit this parameter, were stored as zero-origin excluding any this parameter, and were erroneously printing (-ast-print) and confusingly dumping (-ast-dump) as the stored values. * For alloc_size, the C++ implicit this parameter was not subtracted correctly in Sema, leading to assert failures or to silent failures of __builtin_object_size to compute a value. * For argument_with_type_tag, pointer_with_type_tag, and ownership_returns, the C++ implicit this parameter was not added back to parameter indices in some diagnostics. This patch fixes the above bugs and aims to prevent similar bugs in the future by introducing careful mechanisms for handling parameter indices in attributes. ParamIdx stores a parameter index and is designed to hide the stored encoding while providing accessors that require each use (such as printing) to make explicit the encoding that is needed. Attribute declarations declare parameter index arguments as [Variadic]ParamIdxArgument, which are exposed as ParamIdx[*]. This patch rewrites all attribute arguments that are processed by checkFunctionOrMethodParameterIndex in SemaDeclAttr.cpp to be declared as [Variadic]ParamIdxArgument. The only exception is xray_log_args's argument, which is encoded as a count not an index. Differential Revision: https://reviews.llvm.org/D43248 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@326602 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
12b3556742
commit
aafdf02711
|
@ -195,6 +195,120 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// A single parameter index whose accessors require each use to make explicit
|
||||||
|
/// the parameter index encoding needed.
|
||||||
|
class ParamIdx {
|
||||||
|
// Idx is exposed only via accessors that specify specific encodings.
|
||||||
|
unsigned Idx : 30;
|
||||||
|
unsigned HasThis : 1;
|
||||||
|
unsigned IsValid : 1;
|
||||||
|
|
||||||
|
void assertComparable(const ParamIdx &I) const {
|
||||||
|
assert(isValid() && I.isValid() &&
|
||||||
|
"ParamIdx must be valid to be compared");
|
||||||
|
// It's possible to compare indices from separate functions, but so far
|
||||||
|
// it's not proven useful. Moreover, it might be confusing because a
|
||||||
|
// comparison on the results of getASTIndex might be inconsistent with a
|
||||||
|
// comparison on the ParamIdx objects themselves.
|
||||||
|
assert(HasThis == I.HasThis &&
|
||||||
|
"ParamIdx must be for the same function to be compared");
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// Construct an invalid parameter index (\c isValid returns false and
|
||||||
|
/// accessors fail an assert).
|
||||||
|
ParamIdx() : Idx(0), HasThis(false), IsValid(false) {}
|
||||||
|
|
||||||
|
/// \param Idx is the parameter index as it is normally specified in
|
||||||
|
/// attributes in the source: one-origin including any C++ implicit this
|
||||||
|
/// parameter.
|
||||||
|
///
|
||||||
|
/// \param D is the declaration containing the parameters. It is used to
|
||||||
|
/// determine if there is a C++ implicit this parameter.
|
||||||
|
ParamIdx(unsigned Idx, const Decl *D)
|
||||||
|
: Idx(Idx), HasThis(false), IsValid(true) {
|
||||||
|
if (const auto *FD = dyn_cast<FunctionDecl>(D))
|
||||||
|
HasThis = FD->isCXXInstanceMember();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \param Idx is the parameter index as it is normally specified in
|
||||||
|
/// attributes in the source: one-origin including any C++ implicit this
|
||||||
|
/// parameter.
|
||||||
|
///
|
||||||
|
/// \param HasThis specifies whether the function has a C++ implicit this
|
||||||
|
/// parameter.
|
||||||
|
ParamIdx(unsigned Idx, bool HasThis)
|
||||||
|
: Idx(Idx), HasThis(HasThis), IsValid(true) {}
|
||||||
|
|
||||||
|
/// Is this parameter index valid?
|
||||||
|
bool isValid() const { return IsValid; }
|
||||||
|
|
||||||
|
/// Is there a C++ implicit this parameter?
|
||||||
|
bool hasThis() const {
|
||||||
|
assert(isValid() && "ParamIdx must be valid");
|
||||||
|
return HasThis;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the parameter index as it would normally be encoded for attributes at
|
||||||
|
/// the source level of representation: one-origin including any C++ implicit
|
||||||
|
/// this parameter.
|
||||||
|
///
|
||||||
|
/// This encoding thus makes sense for diagnostics, pretty printing, and
|
||||||
|
/// constructing new attributes from a source-like specification.
|
||||||
|
unsigned getSourceIndex() const {
|
||||||
|
assert(isValid() && "ParamIdx must be valid");
|
||||||
|
return Idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the parameter index as it would normally be encoded at the AST level
|
||||||
|
/// of representation: zero-origin not including any C++ implicit this
|
||||||
|
/// parameter.
|
||||||
|
///
|
||||||
|
/// This is the encoding primarily used in Sema. However, in diagnostics,
|
||||||
|
/// Sema uses \c getSourceIndex instead.
|
||||||
|
unsigned getASTIndex() const {
|
||||||
|
assert(isValid() && "ParamIdx must be valid");
|
||||||
|
assert(Idx >= 1 + HasThis &&
|
||||||
|
"stored index must be base-1 and not specify C++ implicit this");
|
||||||
|
return Idx - 1 - HasThis;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the parameter index as it would normally be encoded at the LLVM level
|
||||||
|
/// of representation: zero-origin including any C++ implicit this parameter.
|
||||||
|
///
|
||||||
|
/// This is the encoding primarily used in CodeGen.
|
||||||
|
unsigned getLLVMIndex() const {
|
||||||
|
assert(isValid() && "ParamIdx must be valid");
|
||||||
|
assert(Idx >= 1 && "stored index must be base-1");
|
||||||
|
return Idx - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const ParamIdx &I) const {
|
||||||
|
assertComparable(I);
|
||||||
|
return Idx == I.Idx;
|
||||||
|
}
|
||||||
|
bool operator!=(const ParamIdx &I) const {
|
||||||
|
assertComparable(I);
|
||||||
|
return Idx != I.Idx;
|
||||||
|
}
|
||||||
|
bool operator<(const ParamIdx &I) const {
|
||||||
|
assertComparable(I);
|
||||||
|
return Idx < I.Idx;
|
||||||
|
}
|
||||||
|
bool operator>(const ParamIdx &I) const {
|
||||||
|
assertComparable(I);
|
||||||
|
return Idx > I.Idx;
|
||||||
|
}
|
||||||
|
bool operator<=(const ParamIdx &I) const {
|
||||||
|
assertComparable(I);
|
||||||
|
return Idx <= I.Idx;
|
||||||
|
}
|
||||||
|
bool operator>=(const ParamIdx &I) const {
|
||||||
|
assertComparable(I);
|
||||||
|
return Idx >= I.Idx;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
#include "clang/AST/Attrs.inc"
|
#include "clang/AST/Attrs.inc"
|
||||||
|
|
||||||
inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
|
inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
|
||||||
|
|
|
@ -166,6 +166,12 @@ class VariadicUnsignedArgument<string name> : Argument<name, 1>;
|
||||||
class VariadicExprArgument<string name> : Argument<name, 1>;
|
class VariadicExprArgument<string name> : Argument<name, 1>;
|
||||||
class VariadicStringArgument<string name> : Argument<name, 1>;
|
class VariadicStringArgument<string name> : Argument<name, 1>;
|
||||||
|
|
||||||
|
// Like VariadicUnsignedArgument except values are ParamIdx.
|
||||||
|
class VariadicParamIdxArgument<string name> : Argument<name, 1>;
|
||||||
|
|
||||||
|
// Like VariadicParamIdxArgument but for a single function parameter index.
|
||||||
|
class ParamIdxArgument<string name, bit opt = 0> : Argument<name, opt>;
|
||||||
|
|
||||||
// A version of the form major.minor[.subminor].
|
// A version of the form major.minor[.subminor].
|
||||||
class VersionArgument<string name, bit opt = 0> : Argument<name, opt>;
|
class VersionArgument<string name, bit opt = 0> : Argument<name, opt>;
|
||||||
|
|
||||||
|
@ -611,6 +617,12 @@ def XRayInstrument : InheritableAttr {
|
||||||
def XRayLogArgs : InheritableAttr {
|
def XRayLogArgs : InheritableAttr {
|
||||||
let Spellings = [Clang<"xray_log_args">];
|
let Spellings = [Clang<"xray_log_args">];
|
||||||
let Subjects = SubjectList<[Function, ObjCMethod]>;
|
let Subjects = SubjectList<[Function, ObjCMethod]>;
|
||||||
|
// This argument is a count not an index, so it has the same encoding (base
|
||||||
|
// 1 including C++ implicit this parameter) at the source and LLVM levels of
|
||||||
|
// representation, so ParamIdxArgument is inappropriate. It is never used
|
||||||
|
// at the AST level of representation, so it never needs to be adjusted not
|
||||||
|
// to include any C++ implicit this parameter. Thus, we just store it and
|
||||||
|
// use it as an unsigned that never needs adjustment.
|
||||||
let Args = [UnsignedArgument<"ArgumentCount">];
|
let Args = [UnsignedArgument<"ArgumentCount">];
|
||||||
let Documentation = [XRayDocs];
|
let Documentation = [XRayDocs];
|
||||||
}
|
}
|
||||||
|
@ -1018,7 +1030,8 @@ def EmptyBases : InheritableAttr, TargetSpecificAttr<TargetMicrosoftCXXABI> {
|
||||||
def AllocSize : InheritableAttr {
|
def AllocSize : InheritableAttr {
|
||||||
let Spellings = [GCC<"alloc_size">];
|
let Spellings = [GCC<"alloc_size">];
|
||||||
let Subjects = SubjectList<[Function]>;
|
let Subjects = SubjectList<[Function]>;
|
||||||
let Args = [IntArgument<"ElemSizeParam">, IntArgument<"NumElemsParam", 1>];
|
let Args = [ParamIdxArgument<"ElemSizeParam">,
|
||||||
|
ParamIdxArgument<"NumElemsParam", /*opt*/ 1>];
|
||||||
let TemplateDependent = 1;
|
let TemplateDependent = 1;
|
||||||
let Documentation = [AllocSizeDocs];
|
let Documentation = [AllocSizeDocs];
|
||||||
}
|
}
|
||||||
|
@ -1105,7 +1118,7 @@ def Format : InheritableAttr {
|
||||||
|
|
||||||
def FormatArg : InheritableAttr {
|
def FormatArg : InheritableAttr {
|
||||||
let Spellings = [GCC<"format_arg">];
|
let Spellings = [GCC<"format_arg">];
|
||||||
let Args = [IntArgument<"FormatIdx">];
|
let Args = [ParamIdxArgument<"FormatIdx">];
|
||||||
let Subjects = SubjectList<[ObjCMethod, HasFunctionProto]>;
|
let Subjects = SubjectList<[ObjCMethod, HasFunctionProto]>;
|
||||||
let Documentation = [Undocumented];
|
let Documentation = [Undocumented];
|
||||||
}
|
}
|
||||||
|
@ -1385,16 +1398,16 @@ def NonNull : InheritableParamAttr {
|
||||||
let Spellings = [GCC<"nonnull">];
|
let Spellings = [GCC<"nonnull">];
|
||||||
let Subjects = SubjectList<[ObjCMethod, HasFunctionProto, ParmVar], WarnDiag,
|
let Subjects = SubjectList<[ObjCMethod, HasFunctionProto, ParmVar], WarnDiag,
|
||||||
"functions, methods, and parameters">;
|
"functions, methods, and parameters">;
|
||||||
let Args = [VariadicUnsignedArgument<"Args">];
|
let Args = [VariadicParamIdxArgument<"Args">];
|
||||||
let AdditionalMembers =
|
let AdditionalMembers = [{
|
||||||
[{bool isNonNull(unsigned idx) const {
|
bool isNonNull(unsigned IdxAST) const {
|
||||||
if (!args_size())
|
if (!args_size())
|
||||||
return true;
|
|
||||||
for (const auto &V : args())
|
|
||||||
if (V == idx)
|
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return args_end() != std::find_if(
|
||||||
} }];
|
args_begin(), args_end(),
|
||||||
|
[=](const ParamIdx &Idx) { return Idx.getASTIndex() == IdxAST; });
|
||||||
|
}
|
||||||
|
}];
|
||||||
// FIXME: We should merge duplicates into a single nonnull attribute.
|
// FIXME: We should merge duplicates into a single nonnull attribute.
|
||||||
let InheritEvenIfAlreadyPresent = 1;
|
let InheritEvenIfAlreadyPresent = 1;
|
||||||
let Documentation = [NonNullDocs];
|
let Documentation = [NonNullDocs];
|
||||||
|
@ -1452,7 +1465,7 @@ def AssumeAligned : InheritableAttr {
|
||||||
def AllocAlign : InheritableAttr {
|
def AllocAlign : InheritableAttr {
|
||||||
let Spellings = [GCC<"alloc_align">];
|
let Spellings = [GCC<"alloc_align">];
|
||||||
let Subjects = SubjectList<[HasFunctionProto]>;
|
let Subjects = SubjectList<[HasFunctionProto]>;
|
||||||
let Args = [IntArgument<"ParamIndex">];
|
let Args = [ParamIdxArgument<"ParamIndex">];
|
||||||
let Documentation = [AllocAlignDocs];
|
let Documentation = [AllocAlignDocs];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1661,7 +1674,8 @@ def Ownership : InheritableAttr {
|
||||||
Returns;
|
Returns;
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
let Args = [IdentifierArgument<"Module">, VariadicUnsignedArgument<"Args">];
|
let Args = [IdentifierArgument<"Module">,
|
||||||
|
VariadicParamIdxArgument<"Args">];
|
||||||
let Subjects = SubjectList<[HasFunctionProto]>;
|
let Subjects = SubjectList<[HasFunctionProto]>;
|
||||||
let Documentation = [Undocumented];
|
let Documentation = [Undocumented];
|
||||||
}
|
}
|
||||||
|
@ -2486,8 +2500,8 @@ def ArgumentWithTypeTag : InheritableAttr {
|
||||||
Clang<"pointer_with_type_tag">];
|
Clang<"pointer_with_type_tag">];
|
||||||
let Subjects = SubjectList<[HasFunctionProto], ErrorDiag>;
|
let Subjects = SubjectList<[HasFunctionProto], ErrorDiag>;
|
||||||
let Args = [IdentifierArgument<"ArgumentKind">,
|
let Args = [IdentifierArgument<"ArgumentKind">,
|
||||||
UnsignedArgument<"ArgumentIdx">,
|
ParamIdxArgument<"ArgumentIdx">,
|
||||||
UnsignedArgument<"TypeTagIdx">,
|
ParamIdxArgument<"TypeTagIdx">,
|
||||||
BoolArgument<"IsPointer", /*opt*/0, /*fake*/1>];
|
BoolArgument<"IsPointer", /*opt*/0, /*fake*/1>];
|
||||||
let Documentation = [ArgumentWithTypeTagDocs, PointerWithTypeTagDocs];
|
let Documentation = [ArgumentWithTypeTagDocs, PointerWithTypeTagDocs];
|
||||||
}
|
}
|
||||||
|
|
|
@ -5463,9 +5463,8 @@ static bool getBytesReturnedByAllocSizeCall(const ASTContext &Ctx,
|
||||||
llvm::APInt &Result) {
|
llvm::APInt &Result) {
|
||||||
const AllocSizeAttr *AllocSize = getAllocSizeAttr(Call);
|
const AllocSizeAttr *AllocSize = getAllocSizeAttr(Call);
|
||||||
|
|
||||||
// alloc_size args are 1-indexed, 0 means not present.
|
assert(AllocSize && AllocSize->elemSizeParam().isValid());
|
||||||
assert(AllocSize && AllocSize->getElemSizeParam() != 0);
|
unsigned SizeArgNo = AllocSize->elemSizeParam().getASTIndex();
|
||||||
unsigned SizeArgNo = AllocSize->getElemSizeParam() - 1;
|
|
||||||
unsigned BitsInSizeT = Ctx.getTypeSize(Ctx.getSizeType());
|
unsigned BitsInSizeT = Ctx.getTypeSize(Ctx.getSizeType());
|
||||||
if (Call->getNumArgs() <= SizeArgNo)
|
if (Call->getNumArgs() <= SizeArgNo)
|
||||||
return false;
|
return false;
|
||||||
|
@ -5483,14 +5482,13 @@ static bool getBytesReturnedByAllocSizeCall(const ASTContext &Ctx,
|
||||||
if (!EvaluateAsSizeT(Call->getArg(SizeArgNo), SizeOfElem))
|
if (!EvaluateAsSizeT(Call->getArg(SizeArgNo), SizeOfElem))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!AllocSize->getNumElemsParam()) {
|
if (!AllocSize->numElemsParam().isValid()) {
|
||||||
Result = std::move(SizeOfElem);
|
Result = std::move(SizeOfElem);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
APSInt NumberOfElems;
|
APSInt NumberOfElems;
|
||||||
// Argument numbers start at 1
|
unsigned NumArgNo = AllocSize->numElemsParam().getASTIndex();
|
||||||
unsigned NumArgNo = AllocSize->getNumElemsParam() - 1;
|
|
||||||
if (!EvaluateAsSizeT(Call->getArg(NumArgNo), NumberOfElems))
|
if (!EvaluateAsSizeT(Call->getArg(NumArgNo), NumberOfElems))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
|
@ -1847,10 +1847,9 @@ void CodeGenModule::ConstructAttributeList(
|
||||||
HasOptnone = TargetDecl->hasAttr<OptimizeNoneAttr>();
|
HasOptnone = TargetDecl->hasAttr<OptimizeNoneAttr>();
|
||||||
if (auto *AllocSize = TargetDecl->getAttr<AllocSizeAttr>()) {
|
if (auto *AllocSize = TargetDecl->getAttr<AllocSizeAttr>()) {
|
||||||
Optional<unsigned> NumElemsParam;
|
Optional<unsigned> NumElemsParam;
|
||||||
// alloc_size args are base-1, 0 means not present.
|
if (AllocSize->numElemsParam().isValid())
|
||||||
if (unsigned N = AllocSize->getNumElemsParam())
|
NumElemsParam = AllocSize->numElemsParam().getLLVMIndex();
|
||||||
NumElemsParam = N - 1;
|
FuncAttrs.addAllocSizeAttr(AllocSize->elemSizeParam().getLLVMIndex(),
|
||||||
FuncAttrs.addAllocSizeAttr(AllocSize->getElemSizeParam() - 1,
|
|
||||||
NumElemsParam);
|
NumElemsParam);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4395,7 +4394,7 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
|
||||||
OffsetValue);
|
OffsetValue);
|
||||||
} else if (const auto *AA = TargetDecl->getAttr<AllocAlignAttr>()) {
|
} else if (const auto *AA = TargetDecl->getAttr<AllocAlignAttr>()) {
|
||||||
llvm::Value *ParamVal =
|
llvm::Value *ParamVal =
|
||||||
CallArgs[AA->getParamIndex() - 1].RV.getScalarVal();
|
CallArgs[AA->paramIndex().getLLVMIndex()].RV.getScalarVal();
|
||||||
EmitAlignmentAssumption(Ret.getScalarVal(), ParamVal);
|
EmitAlignmentAssumption(Ret.getScalarVal(), ParamVal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2619,12 +2619,13 @@ static void CheckNonNullArguments(Sema &S,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (unsigned Val : NonNull->args()) {
|
for (const ParamIdx &Idx : NonNull->args()) {
|
||||||
if (Val >= Args.size())
|
unsigned IdxAST = Idx.getASTIndex();
|
||||||
|
if (IdxAST >= Args.size())
|
||||||
continue;
|
continue;
|
||||||
if (NonNullArgs.empty())
|
if (NonNullArgs.empty())
|
||||||
NonNullArgs.resize(Args.size());
|
NonNullArgs.resize(Args.size());
|
||||||
NonNullArgs.set(Val);
|
NonNullArgs.set(IdxAST);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5002,12 +5003,7 @@ checkFormatStringExpr(Sema &S, const Expr *E, ArrayRef<const Expr *> Args,
|
||||||
const CallExpr *CE = cast<CallExpr>(E);
|
const CallExpr *CE = cast<CallExpr>(E);
|
||||||
if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(CE->getCalleeDecl())) {
|
if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(CE->getCalleeDecl())) {
|
||||||
if (const FormatArgAttr *FA = ND->getAttr<FormatArgAttr>()) {
|
if (const FormatArgAttr *FA = ND->getAttr<FormatArgAttr>()) {
|
||||||
unsigned ArgIndex = FA->getFormatIdx();
|
const Expr *Arg = CE->getArg(FA->formatIdx().getASTIndex());
|
||||||
if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(ND))
|
|
||||||
if (MD->isInstance())
|
|
||||||
--ArgIndex;
|
|
||||||
const Expr *Arg = CE->getArg(ArgIndex - 1);
|
|
||||||
|
|
||||||
return checkFormatStringExpr(S, Arg, Args,
|
return checkFormatStringExpr(S, Arg, Args,
|
||||||
HasVAListArg, format_idx, firstDataArg,
|
HasVAListArg, format_idx, firstDataArg,
|
||||||
Type, CallType, InFunctionCall,
|
Type, CallType, InFunctionCall,
|
||||||
|
@ -5032,8 +5028,7 @@ checkFormatStringExpr(Sema &S, const Expr *E, ArrayRef<const Expr *> Args,
|
||||||
const auto *ME = cast<ObjCMessageExpr>(E);
|
const auto *ME = cast<ObjCMessageExpr>(E);
|
||||||
if (const auto *ND = ME->getMethodDecl()) {
|
if (const auto *ND = ME->getMethodDecl()) {
|
||||||
if (const auto *FA = ND->getAttr<FormatArgAttr>()) {
|
if (const auto *FA = ND->getAttr<FormatArgAttr>()) {
|
||||||
unsigned ArgIndex = FA->getFormatIdx();
|
const Expr *Arg = ME->getArg(FA->formatIdx().getASTIndex());
|
||||||
const Expr *Arg = ME->getArg(ArgIndex - 1);
|
|
||||||
return checkFormatStringExpr(
|
return checkFormatStringExpr(
|
||||||
S, Arg, Args, HasVAListArg, format_idx, firstDataArg, Type,
|
S, Arg, Args, HasVAListArg, format_idx, firstDataArg, Type,
|
||||||
CallType, InFunctionCall, CheckedVarArgs, UncoveredArg, Offset);
|
CallType, InFunctionCall, CheckedVarArgs, UncoveredArg, Offset);
|
||||||
|
@ -10086,8 +10081,8 @@ void Sema::DiagnoseAlwaysNonNullPointer(Expr *E,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (unsigned ArgNo : NonNull->args()) {
|
for (const ParamIdx &ArgNo : NonNull->args()) {
|
||||||
if (ArgNo == ParamNo) {
|
if (ArgNo.getASTIndex() == ParamNo) {
|
||||||
ComplainAboutNonnullParamOrCall(NonNull);
|
ComplainAboutNonnullParamOrCall(NonNull);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -12242,13 +12237,13 @@ void Sema::CheckArgumentWithTypeTag(const ArgumentWithTypeTagAttr *Attr,
|
||||||
bool IsPointerAttr = Attr->getIsPointer();
|
bool IsPointerAttr = Attr->getIsPointer();
|
||||||
|
|
||||||
// Retrieve the argument representing the 'type_tag'.
|
// Retrieve the argument representing the 'type_tag'.
|
||||||
if (Attr->getTypeTagIdx() >= ExprArgs.size()) {
|
unsigned TypeTagIdxAST = Attr->typeTagIdx().getASTIndex();
|
||||||
// Add 1 to display the user's specified value.
|
if (TypeTagIdxAST >= ExprArgs.size()) {
|
||||||
Diag(CallSiteLoc, diag::err_tag_index_out_of_range)
|
Diag(CallSiteLoc, diag::err_tag_index_out_of_range)
|
||||||
<< 0 << Attr->getTypeTagIdx() + 1;
|
<< 0 << Attr->typeTagIdx().getSourceIndex();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const Expr *TypeTagExpr = ExprArgs[Attr->getTypeTagIdx()];
|
const Expr *TypeTagExpr = ExprArgs[TypeTagIdxAST];
|
||||||
bool FoundWrongKind;
|
bool FoundWrongKind;
|
||||||
TypeTagData TypeInfo;
|
TypeTagData TypeInfo;
|
||||||
if (!GetMatchingCType(ArgumentKind, TypeTagExpr, Context,
|
if (!GetMatchingCType(ArgumentKind, TypeTagExpr, Context,
|
||||||
|
@ -12262,13 +12257,13 @@ void Sema::CheckArgumentWithTypeTag(const ArgumentWithTypeTagAttr *Attr,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve the argument representing the 'arg_idx'.
|
// Retrieve the argument representing the 'arg_idx'.
|
||||||
if (Attr->getArgumentIdx() >= ExprArgs.size()) {
|
unsigned ArgumentIdxAST = Attr->argumentIdx().getASTIndex();
|
||||||
// Add 1 to display the user's specified value.
|
if (ArgumentIdxAST >= ExprArgs.size()) {
|
||||||
Diag(CallSiteLoc, diag::err_tag_index_out_of_range)
|
Diag(CallSiteLoc, diag::err_tag_index_out_of_range)
|
||||||
<< 1 << Attr->getArgumentIdx() + 1;
|
<< 1 << Attr->argumentIdx().getSourceIndex();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const Expr *ArgumentExpr = ExprArgs[Attr->getArgumentIdx()];
|
const Expr *ArgumentExpr = ExprArgs[ArgumentIdxAST];
|
||||||
if (IsPointerAttr) {
|
if (IsPointerAttr) {
|
||||||
// Skip implicit cast of pointer to `void *' (as a function argument).
|
// Skip implicit cast of pointer to `void *' (as a function argument).
|
||||||
if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(ArgumentExpr))
|
if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(ArgumentExpr))
|
||||||
|
|
|
@ -13176,7 +13176,7 @@ void Sema::AddKnownFunctionAttributes(FunctionDecl *FD) {
|
||||||
// We already have a __builtin___CFStringMakeConstantString,
|
// We already have a __builtin___CFStringMakeConstantString,
|
||||||
// but builds that use -fno-constant-cfstrings don't go through that.
|
// but builds that use -fno-constant-cfstrings don't go through that.
|
||||||
if (!FD->hasAttr<FormatArgAttr>())
|
if (!FD->hasAttr<FormatArgAttr>())
|
||||||
FD->addAttr(FormatArgAttr::CreateImplicit(Context, 1,
|
FD->addAttr(FormatArgAttr::CreateImplicit(Context, ParamIdx(1, FD),
|
||||||
FD->getLocation()));
|
FD->getLocation()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -311,7 +311,7 @@ static bool checkAttrMutualExclusion(Sema &S, Decl *D, SourceRange Range,
|
||||||
template <typename AttrInfo>
|
template <typename AttrInfo>
|
||||||
static bool checkFunctionOrMethodParameterIndex(
|
static bool checkFunctionOrMethodParameterIndex(
|
||||||
Sema &S, const Decl *D, const AttrInfo &AI, unsigned AttrArgNum,
|
Sema &S, const Decl *D, const AttrInfo &AI, unsigned AttrArgNum,
|
||||||
const Expr *IdxExpr, uint64_t &Idx, bool AllowImplicitThis = false) {
|
const Expr *IdxExpr, ParamIdx &Idx, bool CanIndexImplicitThis = false) {
|
||||||
assert(isFunctionOrMethodOrBlock(D));
|
assert(isFunctionOrMethodOrBlock(D));
|
||||||
|
|
||||||
// In C++ the implicit 'this' function parameter also counts.
|
// In C++ the implicit 'this' function parameter also counts.
|
||||||
|
@ -331,21 +331,20 @@ static bool checkFunctionOrMethodParameterIndex(
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Idx = IdxInt.getLimitedValue();
|
Idx = ParamIdx(IdxInt.getLimitedValue(UINT_MAX), D);
|
||||||
if (Idx < 1 || (!IV && Idx > NumParams)) {
|
unsigned IdxSource = Idx.getSourceIndex();
|
||||||
|
if (IdxSource < 1 || (!IV && IdxSource > NumParams)) {
|
||||||
S.Diag(getAttrLoc(AI), diag::err_attribute_argument_out_of_bounds)
|
S.Diag(getAttrLoc(AI), diag::err_attribute_argument_out_of_bounds)
|
||||||
<< getAttrName(AI) << AttrArgNum << IdxExpr->getSourceRange();
|
<< getAttrName(AI) << AttrArgNum << IdxExpr->getSourceRange();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Idx--; // Convert to zero-based.
|
if (HasImplicitThisParam && !CanIndexImplicitThis) {
|
||||||
if (HasImplicitThisParam && !AllowImplicitThis) {
|
if (IdxSource == 1) {
|
||||||
if (Idx == 0) {
|
|
||||||
S.Diag(getAttrLoc(AI),
|
S.Diag(getAttrLoc(AI),
|
||||||
diag::err_attribute_invalid_implicit_this_argument)
|
diag::err_attribute_invalid_implicit_this_argument)
|
||||||
<< getAttrName(AI) << IdxExpr->getSourceRange();
|
<< getAttrName(AI) << IdxExpr->getSourceRange();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
--Idx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -772,18 +771,15 @@ static void handleAssertExclusiveLockAttr(Sema &S, Decl *D,
|
||||||
/// AttrArgNo is used to actually retrieve the argument, so it's base-0.
|
/// AttrArgNo is used to actually retrieve the argument, so it's base-0.
|
||||||
template <typename AttrInfo>
|
template <typename AttrInfo>
|
||||||
static bool checkParamIsIntegerType(Sema &S, const FunctionDecl *FD,
|
static bool checkParamIsIntegerType(Sema &S, const FunctionDecl *FD,
|
||||||
const AttrInfo &AI, unsigned AttrArgNo,
|
const AttrInfo &AI, unsigned AttrArgNo) {
|
||||||
bool AllowDependentType = false) {
|
|
||||||
assert(AI.isArgExpr(AttrArgNo) && "Expected expression argument");
|
assert(AI.isArgExpr(AttrArgNo) && "Expected expression argument");
|
||||||
Expr *AttrArg = AI.getArgAsExpr(AttrArgNo);
|
Expr *AttrArg = AI.getArgAsExpr(AttrArgNo);
|
||||||
uint64_t Idx;
|
ParamIdx Idx;
|
||||||
if (!checkFunctionOrMethodParameterIndex(S, FD, AI, AttrArgNo + 1, AttrArg,
|
if (!checkFunctionOrMethodParameterIndex(S, FD, AI, AttrArgNo + 1, AttrArg,
|
||||||
Idx))
|
Idx))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const ParmVarDecl *Param = FD->getParamDecl(Idx);
|
const ParmVarDecl *Param = FD->getParamDecl(Idx.getASTIndex());
|
||||||
if (AllowDependentType && Param->getType()->isDependentType())
|
|
||||||
return true;
|
|
||||||
if (!Param->getType()->isIntegerType() && !Param->getType()->isCharType()) {
|
if (!Param->getType()->isIntegerType() && !Param->getType()->isCharType()) {
|
||||||
SourceLocation SrcLoc = AttrArg->getLocStart();
|
SourceLocation SrcLoc = AttrArg->getLocStart();
|
||||||
S.Diag(SrcLoc, diag::err_attribute_integers_only)
|
S.Diag(SrcLoc, diag::err_attribute_integers_only)
|
||||||
|
@ -806,22 +802,23 @@ static void handleAllocSizeAttr(Sema &S, Decl *D, const AttributeList &AL) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const Expr *SizeExpr = AL.getArgAsExpr(0);
|
const Expr *SizeExpr = AL.getArgAsExpr(0);
|
||||||
int SizeArgNo;
|
int SizeArgNoVal;
|
||||||
// Parameter indices are 1-indexed, hence Index=1
|
// Parameter indices are 1-indexed, hence Index=1
|
||||||
if (!checkPositiveIntArgument(S, AL, SizeExpr, SizeArgNo, /*Index=*/1))
|
if (!checkPositiveIntArgument(S, AL, SizeExpr, SizeArgNoVal, /*Index=*/1))
|
||||||
return;
|
return;
|
||||||
|
ParamIdx SizeArgNo(SizeArgNoVal, D);
|
||||||
|
|
||||||
if (!checkParamIsIntegerType(S, FD, AL, /*AttrArgNo=*/0))
|
if (!checkParamIsIntegerType(S, FD, AL, /*AttrArgNo=*/0))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Args are 1-indexed, so 0 implies that the arg was not present
|
ParamIdx NumberArgNo;
|
||||||
int NumberArgNo = 0;
|
|
||||||
if (AL.getNumArgs() == 2) {
|
if (AL.getNumArgs() == 2) {
|
||||||
const Expr *NumberExpr = AL.getArgAsExpr(1);
|
const Expr *NumberExpr = AL.getArgAsExpr(1);
|
||||||
|
int Val;
|
||||||
// Parameter indices are 1-based, hence Index=2
|
// Parameter indices are 1-based, hence Index=2
|
||||||
if (!checkPositiveIntArgument(S, AL, NumberExpr, NumberArgNo,
|
if (!checkPositiveIntArgument(S, AL, NumberExpr, Val, /*Index=*/2))
|
||||||
/*Index=*/2))
|
|
||||||
return;
|
return;
|
||||||
|
NumberArgNo = ParamIdx(Val, D);
|
||||||
|
|
||||||
if (!checkParamIsIntegerType(S, FD, AL, /*AttrArgNo=*/1))
|
if (!checkParamIsIntegerType(S, FD, AL, /*AttrArgNo=*/1))
|
||||||
return;
|
return;
|
||||||
|
@ -1424,18 +1421,19 @@ static bool attrNonNullArgCheck(Sema &S, QualType T, const AttributeList &AL,
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handleNonNullAttr(Sema &S, Decl *D, const AttributeList &AL) {
|
static void handleNonNullAttr(Sema &S, Decl *D, const AttributeList &AL) {
|
||||||
SmallVector<unsigned, 8> NonNullArgs;
|
SmallVector<ParamIdx, 8> NonNullArgs;
|
||||||
for (unsigned I = 0; I < AL.getNumArgs(); ++I) {
|
for (unsigned I = 0; I < AL.getNumArgs(); ++I) {
|
||||||
Expr *Ex = AL.getArgAsExpr(I);
|
Expr *Ex = AL.getArgAsExpr(I);
|
||||||
uint64_t Idx;
|
ParamIdx Idx;
|
||||||
if (!checkFunctionOrMethodParameterIndex(S, D, AL, I + 1, Ex, Idx))
|
if (!checkFunctionOrMethodParameterIndex(S, D, AL, I + 1, Ex, Idx))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Is the function argument a pointer type?
|
// Is the function argument a pointer type?
|
||||||
if (Idx < getFunctionOrMethodNumParams(D) &&
|
if (Idx.getASTIndex() < getFunctionOrMethodNumParams(D) &&
|
||||||
!attrNonNullArgCheck(S, getFunctionOrMethodParamType(D, Idx), AL,
|
!attrNonNullArgCheck(
|
||||||
Ex->getSourceRange(),
|
S, getFunctionOrMethodParamType(D, Idx.getASTIndex()), AL,
|
||||||
getFunctionOrMethodParamRange(D, Idx)))
|
Ex->getSourceRange(),
|
||||||
|
getFunctionOrMethodParamRange(D, Idx.getASTIndex())))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
NonNullArgs.push_back(Idx);
|
NonNullArgs.push_back(Idx);
|
||||||
|
@ -1459,12 +1457,12 @@ static void handleNonNullAttr(Sema &S, Decl *D, const AttributeList &AL) {
|
||||||
S.Diag(AL.getLoc(), diag::warn_attribute_nonnull_no_pointers);
|
S.Diag(AL.getLoc(), diag::warn_attribute_nonnull_no_pointers);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned *Start = NonNullArgs.data();
|
ParamIdx *Start = NonNullArgs.data();
|
||||||
unsigned Size = NonNullArgs.size();
|
unsigned Size = NonNullArgs.size();
|
||||||
llvm::array_pod_sort(Start, Start + Size);
|
llvm::array_pod_sort(Start, Start + Size);
|
||||||
D->addAttr(::new (S.Context)
|
D->addAttr(::new (S.Context)
|
||||||
NonNullAttr(AL.getRange(), S.Context, Start, Size,
|
NonNullAttr(AL.getRange(), S.Context, Start, Size,
|
||||||
AL.getAttributeSpellingListIndex()));
|
AL.getAttributeSpellingListIndex()));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handleNonNullAttrParameter(Sema &S, ParmVarDecl *D,
|
static void handleNonNullAttrParameter(Sema &S, ParmVarDecl *D,
|
||||||
|
@ -1485,8 +1483,8 @@ static void handleNonNullAttrParameter(Sema &S, ParmVarDecl *D,
|
||||||
return;
|
return;
|
||||||
|
|
||||||
D->addAttr(::new (S.Context)
|
D->addAttr(::new (S.Context)
|
||||||
NonNullAttr(AL.getRange(), S.Context, nullptr, 0,
|
NonNullAttr(AL.getRange(), S.Context, nullptr, 0,
|
||||||
AL.getAttributeSpellingListIndex()));
|
AL.getAttributeSpellingListIndex()));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handleReturnsNonNullAttr(Sema &S, Decl *D,
|
static void handleReturnsNonNullAttr(Sema &S, Decl *D,
|
||||||
|
@ -1587,7 +1585,7 @@ void Sema::AddAllocAlignAttr(SourceRange AttrRange, Decl *D, Expr *ParamExpr,
|
||||||
unsigned SpellingListIndex) {
|
unsigned SpellingListIndex) {
|
||||||
QualType ResultType = getFunctionOrMethodResultType(D);
|
QualType ResultType = getFunctionOrMethodResultType(D);
|
||||||
|
|
||||||
AllocAlignAttr TmpAttr(AttrRange, Context, 0, SpellingListIndex);
|
AllocAlignAttr TmpAttr(AttrRange, Context, ParamIdx(), SpellingListIndex);
|
||||||
SourceLocation AttrLoc = AttrRange.getBegin();
|
SourceLocation AttrLoc = AttrRange.getBegin();
|
||||||
|
|
||||||
if (!ResultType->isDependentType() &&
|
if (!ResultType->isDependentType() &&
|
||||||
|
@ -1597,28 +1595,22 @@ void Sema::AddAllocAlignAttr(SourceRange AttrRange, Decl *D, Expr *ParamExpr,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t IndexVal;
|
ParamIdx Idx;
|
||||||
const auto *FuncDecl = cast<FunctionDecl>(D);
|
const auto *FuncDecl = cast<FunctionDecl>(D);
|
||||||
if (!checkFunctionOrMethodParameterIndex(*this, FuncDecl, TmpAttr,
|
if (!checkFunctionOrMethodParameterIndex(*this, FuncDecl, TmpAttr,
|
||||||
/*AttrArgNo=*/1, ParamExpr,
|
/*AttrArgNo=*/1, ParamExpr, Idx))
|
||||||
IndexVal))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
QualType Ty = getFunctionOrMethodParamType(D, IndexVal);
|
QualType Ty = getFunctionOrMethodParamType(D, Idx.getASTIndex());
|
||||||
if (!Ty->isDependentType() && !Ty->isIntegralType(Context)) {
|
if (!Ty->isDependentType() && !Ty->isIntegralType(Context)) {
|
||||||
Diag(ParamExpr->getLocStart(), diag::err_attribute_integers_only)
|
Diag(ParamExpr->getLocStart(), diag::err_attribute_integers_only)
|
||||||
<< &TmpAttr << FuncDecl->getParamDecl(IndexVal)->getSourceRange();
|
<< &TmpAttr
|
||||||
|
<< FuncDecl->getParamDecl(Idx.getASTIndex())->getSourceRange();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We cannot use the Idx returned from checkFunctionOrMethodParameterIndex
|
D->addAttr(::new (Context)
|
||||||
// because that has corrected for the implicit this parameter, and is zero-
|
AllocAlignAttr(AttrRange, Context, Idx, SpellingListIndex));
|
||||||
// based. The attribute expects what the user wrote explicitly.
|
|
||||||
llvm::APSInt Val;
|
|
||||||
ParamExpr->EvaluateAsInt(Val, Context);
|
|
||||||
|
|
||||||
D->addAttr(::new (Context) AllocAlignAttr(
|
|
||||||
AttrRange, Context, Val.getZExtValue(), SpellingListIndex));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Normalize the attribute, __foo__ becomes foo.
|
/// Normalize the attribute, __foo__ becomes foo.
|
||||||
|
@ -1678,15 +1670,15 @@ static void handleOwnershipAttr(Sema &S, Decl *D, const AttributeList &AL) {
|
||||||
Module = &S.PP.getIdentifierTable().get(ModuleName);
|
Module = &S.PP.getIdentifierTable().get(ModuleName);
|
||||||
}
|
}
|
||||||
|
|
||||||
SmallVector<unsigned, 8> OwnershipArgs;
|
SmallVector<ParamIdx, 8> OwnershipArgs;
|
||||||
for (unsigned i = 1; i < AL.getNumArgs(); ++i) {
|
for (unsigned i = 1; i < AL.getNumArgs(); ++i) {
|
||||||
Expr *Ex = AL.getArgAsExpr(i);
|
Expr *Ex = AL.getArgAsExpr(i);
|
||||||
uint64_t Idx;
|
ParamIdx Idx;
|
||||||
if (!checkFunctionOrMethodParameterIndex(S, D, AL, i, Ex, Idx))
|
if (!checkFunctionOrMethodParameterIndex(S, D, AL, i, Ex, Idx))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Is the function argument a pointer type?
|
// Is the function argument a pointer type?
|
||||||
QualType T = getFunctionOrMethodParamType(D, Idx);
|
QualType T = getFunctionOrMethodParamType(D, Idx.getASTIndex());
|
||||||
int Err = -1; // No error
|
int Err = -1; // No error
|
||||||
switch (K) {
|
switch (K) {
|
||||||
case OwnershipAttr::Takes:
|
case OwnershipAttr::Takes:
|
||||||
|
@ -1717,14 +1709,13 @@ static void handleOwnershipAttr(Sema &S, Decl *D, const AttributeList &AL) {
|
||||||
} else if (K == OwnershipAttr::Returns &&
|
} else if (K == OwnershipAttr::Returns &&
|
||||||
I->getOwnKind() == OwnershipAttr::Returns) {
|
I->getOwnKind() == OwnershipAttr::Returns) {
|
||||||
// A returns attribute conflicts with any other returns attribute using
|
// A returns attribute conflicts with any other returns attribute using
|
||||||
// a different index. Note, diagnostic reporting is 1-based, but stored
|
// a different index.
|
||||||
// argument indexes are 0-based.
|
|
||||||
if (std::find(I->args_begin(), I->args_end(), Idx) == I->args_end()) {
|
if (std::find(I->args_begin(), I->args_end(), Idx) == I->args_end()) {
|
||||||
S.Diag(I->getLocation(), diag::err_ownership_returns_index_mismatch)
|
S.Diag(I->getLocation(), diag::err_ownership_returns_index_mismatch)
|
||||||
<< *(I->args_begin()) + 1;
|
<< I->args_begin()->getSourceIndex();
|
||||||
if (I->args_size())
|
if (I->args_size())
|
||||||
S.Diag(AL.getLoc(), diag::note_ownership_returns_index_mismatch)
|
S.Diag(AL.getLoc(), diag::note_ownership_returns_index_mismatch)
|
||||||
<< (unsigned)Idx + 1 << Ex->getSourceRange();
|
<< Idx.getSourceIndex() << Ex->getSourceRange();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1732,13 +1723,12 @@ static void handleOwnershipAttr(Sema &S, Decl *D, const AttributeList &AL) {
|
||||||
OwnershipArgs.push_back(Idx);
|
OwnershipArgs.push_back(Idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned* start = OwnershipArgs.data();
|
ParamIdx *Start = OwnershipArgs.data();
|
||||||
unsigned size = OwnershipArgs.size();
|
unsigned Size = OwnershipArgs.size();
|
||||||
llvm::array_pod_sort(start, start + size);
|
llvm::array_pod_sort(Start, Start + Size);
|
||||||
|
|
||||||
D->addAttr(::new (S.Context)
|
D->addAttr(::new (S.Context)
|
||||||
OwnershipAttr(AL.getLoc(), S.Context, Module, start, size,
|
OwnershipAttr(AL.getLoc(), S.Context, Module, Start, Size,
|
||||||
AL.getAttributeSpellingListIndex()));
|
AL.getAttributeSpellingListIndex()));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handleWeakRefAttr(Sema &S, Decl *D, const AttributeList &AL) {
|
static void handleWeakRefAttr(Sema &S, Decl *D, const AttributeList &AL) {
|
||||||
|
@ -3109,12 +3099,12 @@ static void handleEnumExtensibilityAttr(Sema &S, Decl *D,
|
||||||
/// http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html
|
/// http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html
|
||||||
static void handleFormatArgAttr(Sema &S, Decl *D, const AttributeList &AL) {
|
static void handleFormatArgAttr(Sema &S, Decl *D, const AttributeList &AL) {
|
||||||
Expr *IdxExpr = AL.getArgAsExpr(0);
|
Expr *IdxExpr = AL.getArgAsExpr(0);
|
||||||
uint64_t Idx;
|
ParamIdx Idx;
|
||||||
if (!checkFunctionOrMethodParameterIndex(S, D, AL, 1, IdxExpr, Idx))
|
if (!checkFunctionOrMethodParameterIndex(S, D, AL, 1, IdxExpr, Idx))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Make sure the format string is really a string.
|
// Make sure the format string is really a string.
|
||||||
QualType Ty = getFunctionOrMethodParamType(D, Idx);
|
QualType Ty = getFunctionOrMethodParamType(D, Idx.getASTIndex());
|
||||||
|
|
||||||
bool NotNSStringTy = !isNSStringType(Ty, S.Context);
|
bool NotNSStringTy = !isNSStringType(Ty, S.Context);
|
||||||
if (NotNSStringTy &&
|
if (NotNSStringTy &&
|
||||||
|
@ -3137,15 +3127,8 @@ static void handleFormatArgAttr(Sema &S, Decl *D, const AttributeList &AL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We cannot use the Idx returned from checkFunctionOrMethodParameterIndex
|
D->addAttr(::new (S.Context) FormatArgAttr(
|
||||||
// because that has corrected for the implicit this parameter, and is zero-
|
AL.getRange(), S.Context, Idx, AL.getAttributeSpellingListIndex()));
|
||||||
// based. The attribute expects what the user wrote explicitly.
|
|
||||||
llvm::APSInt Val;
|
|
||||||
IdxExpr->EvaluateAsInt(Val, S.Context);
|
|
||||||
|
|
||||||
D->addAttr(::new (S.Context)
|
|
||||||
FormatArgAttr(AL.getRange(), S.Context, Val.getZExtValue(),
|
|
||||||
AL.getAttributeSpellingListIndex()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum FormatAttrKind {
|
enum FormatAttrKind {
|
||||||
|
@ -4539,13 +4522,13 @@ static void handleArgumentWithTypeTagAttr(Sema &S, Decl *D,
|
||||||
<< AL.getName() << /* arg num = */ 1 << AANT_ArgumentIdentifier;
|
<< AL.getName() << /* arg num = */ 1 << AANT_ArgumentIdentifier;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t ArgumentIdx;
|
ParamIdx ArgumentIdx;
|
||||||
if (!checkFunctionOrMethodParameterIndex(S, D, AL, 2, AL.getArgAsExpr(1),
|
if (!checkFunctionOrMethodParameterIndex(S, D, AL, 2, AL.getArgAsExpr(1),
|
||||||
ArgumentIdx))
|
ArgumentIdx))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
uint64_t TypeTagIdx;
|
ParamIdx TypeTagIdx;
|
||||||
if (!checkFunctionOrMethodParameterIndex(S, D, AL, 3, AL.getArgAsExpr(2),
|
if (!checkFunctionOrMethodParameterIndex(S, D, AL, 3, AL.getArgAsExpr(2),
|
||||||
TypeTagIdx))
|
TypeTagIdx))
|
||||||
return;
|
return;
|
||||||
|
@ -4553,8 +4536,9 @@ static void handleArgumentWithTypeTagAttr(Sema &S, Decl *D,
|
||||||
bool IsPointer = AL.getName()->getName() == "pointer_with_type_tag";
|
bool IsPointer = AL.getName()->getName() == "pointer_with_type_tag";
|
||||||
if (IsPointer) {
|
if (IsPointer) {
|
||||||
// Ensure that buffer has a pointer type.
|
// Ensure that buffer has a pointer type.
|
||||||
if (ArgumentIdx >= getFunctionOrMethodNumParams(D) ||
|
unsigned ArgumentIdxAST = ArgumentIdx.getASTIndex();
|
||||||
!getFunctionOrMethodParamType(D, ArgumentIdx)->isPointerType())
|
if (ArgumentIdxAST >= getFunctionOrMethodNumParams(D) ||
|
||||||
|
!getFunctionOrMethodParamType(D, ArgumentIdxAST)->isPointerType())
|
||||||
S.Diag(AL.getLoc(), diag::err_attribute_pointers_only)
|
S.Diag(AL.getLoc(), diag::err_attribute_pointers_only)
|
||||||
<< AL.getName() << 0;
|
<< AL.getName() << 0;
|
||||||
}
|
}
|
||||||
|
@ -4594,19 +4578,18 @@ static void handleTypeTagForDatatypeAttr(Sema &S, Decl *D,
|
||||||
AL.getAttributeSpellingListIndex()));
|
AL.getAttributeSpellingListIndex()));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handleXRayLogArgsAttr(Sema &S, Decl *D,
|
static void handleXRayLogArgsAttr(Sema &S, Decl *D, const AttributeList &AL) {
|
||||||
const AttributeList &AL) {
|
ParamIdx ArgCount;
|
||||||
uint64_t ArgCount;
|
|
||||||
|
|
||||||
if (!checkFunctionOrMethodParameterIndex(S, D, AL, 1, AL.getArgAsExpr(0),
|
if (!checkFunctionOrMethodParameterIndex(S, D, AL, 1, AL.getArgAsExpr(0),
|
||||||
ArgCount,
|
ArgCount,
|
||||||
true /* AllowImplicitThis*/))
|
true /* CanIndexImplicitThis */))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// ArgCount isn't a parameter index [0;n), it's a count [1;n] - hence + 1.
|
// ArgCount isn't a parameter index [0;n), it's a count [1;n]
|
||||||
D->addAttr(::new (S.Context)
|
D->addAttr(::new (S.Context) XRayLogArgsAttr(
|
||||||
XRayLogArgsAttr(AL.getRange(), S.Context, ++ArgCount,
|
AL.getRange(), S.Context, ArgCount.getSourceIndex(),
|
||||||
AL.getAttributeSpellingListIndex()));
|
AL.getAttributeSpellingListIndex()));
|
||||||
}
|
}
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
|
@ -176,7 +176,7 @@ static void instantiateDependentAllocAlignAttr(
|
||||||
Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs,
|
Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs,
|
||||||
const AllocAlignAttr *Align, Decl *New) {
|
const AllocAlignAttr *Align, Decl *New) {
|
||||||
Expr *Param = IntegerLiteral::Create(
|
Expr *Param = IntegerLiteral::Create(
|
||||||
S.getASTContext(), llvm::APInt(64, Align->getParamIndex()),
|
S.getASTContext(), llvm::APInt(64, Align->paramIndex().getSourceIndex()),
|
||||||
S.getASTContext().UnsignedLongLongTy, Align->getLocation());
|
S.getASTContext().UnsignedLongLongTy, Align->getLocation());
|
||||||
S.AddAllocAlignAttr(Align->getLocation(), New, Param,
|
S.AddAllocAlignAttr(Align->getLocation(), New, Param,
|
||||||
Align->getSpellingListIndex());
|
Align->getSpellingListIndex());
|
||||||
|
|
|
@ -1231,9 +1231,10 @@ MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE,
|
||||||
if (Att->getModule() != II_malloc)
|
if (Att->getModule() != II_malloc)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
OwnershipAttr::args_iterator I = Att->args_begin(), E = Att->args_end();
|
ParamIdx *I = Att->args_begin(), *E = Att->args_end();
|
||||||
if (I != E) {
|
if (I != E) {
|
||||||
return MallocMemAux(C, CE, CE->getArg(*I), UndefinedVal(), State);
|
return MallocMemAux(C, CE, CE->getArg(I->getASTIndex()), UndefinedVal(),
|
||||||
|
State);
|
||||||
}
|
}
|
||||||
return MallocMemAux(C, CE, UnknownVal(), UndefinedVal(), State);
|
return MallocMemAux(C, CE, UnknownVal(), UndefinedVal(), State);
|
||||||
}
|
}
|
||||||
|
@ -1331,9 +1332,9 @@ ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C,
|
||||||
bool ReleasedAllocated = false;
|
bool ReleasedAllocated = false;
|
||||||
|
|
||||||
for (const auto &Arg : Att->args()) {
|
for (const auto &Arg : Att->args()) {
|
||||||
ProgramStateRef StateI = FreeMemAux(C, CE, State, Arg,
|
ProgramStateRef StateI = FreeMemAux(
|
||||||
Att->getOwnKind() == OwnershipAttr::Holds,
|
C, CE, State, Arg.getASTIndex(),
|
||||||
ReleasedAllocated);
|
Att->getOwnKind() == OwnershipAttr::Holds, ReleasedAllocated);
|
||||||
if (StateI)
|
if (StateI)
|
||||||
State = StateI;
|
State = StateI;
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,10 +58,11 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call,
|
||||||
AttrNonNull.set(0, NumArgs);
|
AttrNonNull.set(0, NumArgs);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
for (unsigned Val : NonNull->args()) {
|
for (const ParamIdx &Idx : NonNull->args()) {
|
||||||
if (Val >= NumArgs)
|
unsigned IdxAST = Idx.getASTIndex();
|
||||||
|
if (IdxAST >= NumArgs)
|
||||||
continue;
|
continue;
|
||||||
AttrNonNull.set(Val);
|
AttrNonNull.set(IdxAST);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -69,4 +69,22 @@ int testIt() {
|
||||||
__builtin_object_size(dependent_calloc<7, 8>(), 0) +
|
__builtin_object_size(dependent_calloc<7, 8>(), 0) +
|
||||||
__builtin_object_size(dependent_calloc2<int, 9>(), 0);
|
__builtin_object_size(dependent_calloc2<int, 9>(), 0);
|
||||||
}
|
}
|
||||||
|
} // namespace templated_alloc_size
|
||||||
|
|
||||||
|
class C {
|
||||||
|
public:
|
||||||
|
void *my_malloc(int N) __attribute__((alloc_size(2)));
|
||||||
|
void *my_calloc(int N, int M) __attribute__((alloc_size(2, 3)));
|
||||||
|
};
|
||||||
|
|
||||||
|
// CHECK-LABEL: define i32 @_Z16callMemberMallocv
|
||||||
|
int callMemberMalloc() {
|
||||||
|
// CHECK: ret i32 16
|
||||||
|
return __builtin_object_size(C().my_malloc(16), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: define i32 @_Z16callMemberCallocv
|
||||||
|
int callMemberCalloc() {
|
||||||
|
// CHECK: ret i32 32
|
||||||
|
return __builtin_object_size(C().my_calloc(16, 2), 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,12 +68,12 @@ __attribute__((pointer_with_type_tag(ident1,1,2)));
|
||||||
void TestBool(void *, int)
|
void TestBool(void *, int)
|
||||||
__attribute__((pointer_with_type_tag(bool1,1,2)));
|
__attribute__((pointer_with_type_tag(bool1,1,2)));
|
||||||
// CHECK: FunctionDecl{{.*}}TestBool
|
// CHECK: FunctionDecl{{.*}}TestBool
|
||||||
// CHECK: ArgumentWithTypeTagAttr{{.*}}pointer_with_type_tag bool1 0 1 IsPointer
|
// CHECK: ArgumentWithTypeTagAttr{{.*}}pointer_with_type_tag bool1 1 2 IsPointer
|
||||||
|
|
||||||
void TestUnsigned(void *, int)
|
void TestUnsigned(void *, int)
|
||||||
__attribute__((pointer_with_type_tag(unsigned1,1,2)));
|
__attribute__((pointer_with_type_tag(unsigned1,1,2)));
|
||||||
// CHECK: FunctionDecl{{.*}}TestUnsigned
|
// CHECK: FunctionDecl{{.*}}TestUnsigned
|
||||||
// CHECK: ArgumentWithTypeTagAttr{{.*}} pointer_with_type_tag unsigned1 0 1
|
// CHECK: ArgumentWithTypeTagAttr{{.*}} pointer_with_type_tag unsigned1 1 2
|
||||||
|
|
||||||
void TestInt(void) __attribute__((constructor(123)));
|
void TestInt(void) __attribute__((constructor(123)));
|
||||||
// CHECK: FunctionDecl{{.*}}TestInt
|
// CHECK: FunctionDecl{{.*}}TestInt
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
// RUN: %clang_cc1 %s -verify -fsyntax-only
|
||||||
|
|
||||||
|
class C {
|
||||||
|
void f(int, int)
|
||||||
|
__attribute__((ownership_returns(foo, 2))) // expected-note {{declared with index 2 here}}
|
||||||
|
__attribute__((ownership_returns(foo, 3))); // expected-error {{'ownership_returns' attribute index does not match; here it is 3}}
|
||||||
|
};
|
|
@ -1,6 +1,67 @@
|
||||||
// RUN: %clang_cc1 %s -ast-print | FileCheck %s
|
// RUN: %clang_cc1 %s -ast-print | FileCheck %s
|
||||||
|
|
||||||
|
// CHECK: void xla(int a) __attribute__((xray_log_args(1)));
|
||||||
|
void xla(int a) __attribute__((xray_log_args(1)));
|
||||||
|
|
||||||
// CHECK: void *as2(int, int) __attribute__((alloc_size(1, 2)));
|
// CHECK: void *as2(int, int) __attribute__((alloc_size(1, 2)));
|
||||||
void *as2(int, int) __attribute__((alloc_size(1, 2)));
|
void *as2(int, int) __attribute__((alloc_size(1, 2)));
|
||||||
// CHECK: void *as1(void *, int) __attribute__((alloc_size(2)));
|
// CHECK: void *as1(void *, int) __attribute__((alloc_size(2)));
|
||||||
void *as1(void *, int) __attribute__((alloc_size(2)));
|
void *as1(void *, int) __attribute__((alloc_size(2)));
|
||||||
|
|
||||||
|
// CHECK: void fmt(int, const char *, ...) __attribute__((format(printf, 2, 3)));
|
||||||
|
void fmt(int, const char *, ...) __attribute__((format(printf, 2, 3)));
|
||||||
|
|
||||||
|
// CHECK: char *fmta(int, const char *) __attribute__((format_arg(2)));
|
||||||
|
char *fmta(int, const char *) __attribute__((format_arg(2)));
|
||||||
|
|
||||||
|
// CHECK: void nn(int *, int *) __attribute__((nonnull(1, 2)));
|
||||||
|
void nn(int *, int *) __attribute__((nonnull(1, 2)));
|
||||||
|
|
||||||
|
// CHECK: int *aa(int i) __attribute__((alloc_align(1)));
|
||||||
|
int *aa(int i) __attribute__((alloc_align(1)));
|
||||||
|
|
||||||
|
// CHECK: void ownt(int *, int *) __attribute__((ownership_takes(foo, 1, 2)));
|
||||||
|
void ownt(int *, int *) __attribute__((ownership_takes(foo, 1, 2)));
|
||||||
|
// CHECK: void ownh(int *, int *) __attribute__((ownership_holds(foo, 1, 2)));
|
||||||
|
void ownh(int *, int *) __attribute__((ownership_holds(foo, 1, 2)));
|
||||||
|
// CHECK: void ownr(int) __attribute__((ownership_returns(foo, 1)));
|
||||||
|
void ownr(int) __attribute__((ownership_returns(foo, 1)));
|
||||||
|
|
||||||
|
// CHECK: void awtt(int, int, ...) __attribute__((argument_with_type_tag(foo, 3, 2)));
|
||||||
|
void awtt(int, int, ...) __attribute__((argument_with_type_tag(foo, 3, 2)));
|
||||||
|
// CHECK: void pwtt(void *, int) __attribute__((pointer_with_type_tag(foo, 1, 2)));
|
||||||
|
void pwtt(void *, int) __attribute__((pointer_with_type_tag(foo, 1, 2)));
|
||||||
|
|
||||||
|
class C {
|
||||||
|
// CHECK: void xla(int a) __attribute__((xray_log_args(2)));
|
||||||
|
void xla(int a) __attribute__((xray_log_args(2)));
|
||||||
|
|
||||||
|
// CHECK: void *as2(int, int) __attribute__((alloc_size(2, 3)));
|
||||||
|
void *as2(int, int) __attribute__((alloc_size(2, 3)));
|
||||||
|
// CHECK: void *as1(void *, int) __attribute__((alloc_size(3)));
|
||||||
|
void *as1(void *, int) __attribute__((alloc_size(3)));
|
||||||
|
|
||||||
|
// CHECK: void fmt(int, const char *, ...) __attribute__((format(printf, 3, 4)));
|
||||||
|
void fmt(int, const char *, ...) __attribute__((format(printf, 3, 4)));
|
||||||
|
|
||||||
|
// CHECK: char *fmta(int, const char *) __attribute__((format_arg(3)));
|
||||||
|
char *fmta(int, const char *) __attribute__((format_arg(3)));
|
||||||
|
|
||||||
|
// CHECK: void nn(int *, int *) __attribute__((nonnull(2, 3)));
|
||||||
|
void nn(int *, int *) __attribute__((nonnull(2, 3)));
|
||||||
|
|
||||||
|
// CHECK: int *aa(int i) __attribute__((alloc_align(2)));
|
||||||
|
int *aa(int i) __attribute__((alloc_align(2)));
|
||||||
|
|
||||||
|
// CHECK: void ownt(int *, int *) __attribute__((ownership_takes(foo, 2, 3)));
|
||||||
|
void ownt(int *, int *) __attribute__((ownership_takes(foo, 2, 3)));
|
||||||
|
// CHECK: void ownh(int *, int *) __attribute__((ownership_holds(foo, 2, 3)));
|
||||||
|
void ownh(int *, int *) __attribute__((ownership_holds(foo, 2, 3)));
|
||||||
|
// CHECK: void ownr(int) __attribute__((ownership_returns(foo, 2)));
|
||||||
|
void ownr(int) __attribute__((ownership_returns(foo, 2)));
|
||||||
|
|
||||||
|
// CHECK: void awtt(int, int, ...) __attribute__((argument_with_type_tag(foo, 4, 3)));
|
||||||
|
void awtt(int, int, ...) __attribute__((argument_with_type_tag(foo, 4, 3)));
|
||||||
|
// CHECK: void pwtt(void *, int) __attribute__((pointer_with_type_tag(foo, 2, 3)));
|
||||||
|
void pwtt(void *, int) __attribute__((pointer_with_type_tag(foo, 2, 3)));
|
||||||
|
};
|
||||||
|
|
|
@ -3,21 +3,50 @@
|
||||||
#define INT_TAG 42
|
#define INT_TAG 42
|
||||||
|
|
||||||
static const int test_in
|
static const int test_in
|
||||||
__attribute__((type_tag_for_datatype(test, int))) = INT_TAG;
|
__attribute__((type_tag_for_datatype(test, int))) = INT_TAG;
|
||||||
|
|
||||||
// Argument index: 1, Type tag index: 2
|
// Argument index: 1, Type tag index: 2
|
||||||
void test_bounds_index(...)
|
void test_bounds_index(...)
|
||||||
__attribute__((argument_with_type_tag(test, 1, 2)));
|
__attribute__((argument_with_type_tag(test, 1, 2)));
|
||||||
|
|
||||||
|
// Argument index: 1, Type tag index: 2
|
||||||
|
void test_bounds_index_ptr(void *, ...)
|
||||||
|
__attribute__((pointer_with_type_tag(test, 1, 2)));
|
||||||
|
|
||||||
// Argument index: 3, Type tag index: 1
|
// Argument index: 3, Type tag index: 1
|
||||||
void test_bounds_arg_index(...)
|
void test_bounds_arg_index(...)
|
||||||
__attribute__((argument_with_type_tag(test, 3, 1)));
|
__attribute__((argument_with_type_tag(test, 3, 1)));
|
||||||
|
|
||||||
|
class C {
|
||||||
|
public:
|
||||||
|
// Argument index: 2, Type tag index: 3
|
||||||
|
void test_bounds_index(...)
|
||||||
|
__attribute__((argument_with_type_tag(test, 2, 3)));
|
||||||
|
|
||||||
|
// Argument index: 2, Type tag index: 3
|
||||||
|
void test_bounds_index_ptr(void *, ...)
|
||||||
|
__attribute__((pointer_with_type_tag(test, 2, 3)));
|
||||||
|
|
||||||
|
// Argument index: 4, Type tag index: 2
|
||||||
|
void test_bounds_arg_index(...)
|
||||||
|
__attribute__((argument_with_type_tag(test, 4, 2)));
|
||||||
|
};
|
||||||
|
|
||||||
void test_bounds()
|
void test_bounds()
|
||||||
{
|
{
|
||||||
|
C c;
|
||||||
|
|
||||||
// Test the boundary edges (ensure no off-by-one) with argument indexing.
|
// Test the boundary edges (ensure no off-by-one) with argument indexing.
|
||||||
test_bounds_index(1, INT_TAG);
|
test_bounds_index(1, INT_TAG);
|
||||||
|
c.test_bounds_index(1, INT_TAG);
|
||||||
|
test_bounds_index_ptr(0, INT_TAG);
|
||||||
|
c.test_bounds_index_ptr(0, INT_TAG);
|
||||||
|
|
||||||
test_bounds_index(1); // expected-error {{type tag index 2 is greater than the number of arguments specified}}
|
test_bounds_index(1); // expected-error {{type tag index 2 is greater than the number of arguments specified}}
|
||||||
test_bounds_arg_index(INT_TAG, 1); // expected-error {{argument index 3 is greater than the number of arguments specified}}
|
c.test_bounds_index(1); // expected-error {{type tag index 3 is greater than the number of arguments specified}}
|
||||||
|
test_bounds_index_ptr(0); // expected-error {{type tag index 2 is greater than the number of arguments specified}}
|
||||||
|
c.test_bounds_index_ptr(0); // expected-error {{type tag index 3 is greater than the number of arguments specified}}
|
||||||
|
|
||||||
|
test_bounds_arg_index(INT_TAG, 1); // expected-error {{argument index 3 is greater than the number of arguments specified}}
|
||||||
|
c.test_bounds_arg_index(INT_TAG, 1); // expected-error {{argument index 4 is greater than the number of arguments specified}}
|
||||||
}
|
}
|
||||||
|
|
|
@ -302,9 +302,6 @@ namespace {
|
||||||
std::string getIsOmitted() const override {
|
std::string getIsOmitted() const override {
|
||||||
if (type == "IdentifierInfo *")
|
if (type == "IdentifierInfo *")
|
||||||
return "!get" + getUpperName().str() + "()";
|
return "!get" + getUpperName().str() + "()";
|
||||||
// FIXME: Do this declaratively in Attr.td.
|
|
||||||
if (getAttrName() == "AllocSize")
|
|
||||||
return "0 == get" + getUpperName().str() + "()";
|
|
||||||
return "false";
|
return "false";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -748,6 +745,138 @@ namespace {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class VariadicParamIdxArgument : public VariadicArgument {
|
||||||
|
public:
|
||||||
|
VariadicParamIdxArgument(const Record &Arg, StringRef Attr)
|
||||||
|
: VariadicArgument(Arg, Attr, "ParamIdx") {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
void writeCtorBody(raw_ostream &OS) const override {
|
||||||
|
VariadicArgument::writeCtorBody(OS);
|
||||||
|
OS << " #ifndef NDEBUG\n"
|
||||||
|
<< " if (" << getLowerName() << "_size()) {\n"
|
||||||
|
<< " bool HasThis = " << getLowerName()
|
||||||
|
<< "_begin()->hasThis();\n"
|
||||||
|
<< " for (const auto Idx : " << getLowerName() << "()) {\n"
|
||||||
|
<< " assert(Idx.isValid() && \"ParamIdx must be valid\");\n"
|
||||||
|
<< " assert(HasThis == Idx.hasThis() && "
|
||||||
|
<< "\"HasThis must be consistent\");\n"
|
||||||
|
<< " }\n"
|
||||||
|
<< " }\n"
|
||||||
|
<< " #endif\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void writePCHReadDecls(raw_ostream &OS) const override {
|
||||||
|
OS << " unsigned " << getUpperName() << "Size = Record.readInt();\n";
|
||||||
|
OS << " bool " << getUpperName() << "HasThis = " << getUpperName()
|
||||||
|
<< "Size ? Record.readInt() : false;\n";
|
||||||
|
OS << " SmallVector<ParamIdx, 4> " << getUpperName() << ";\n"
|
||||||
|
<< " " << getUpperName() << ".reserve(" << getUpperName()
|
||||||
|
<< "Size);\n"
|
||||||
|
<< " for (unsigned i = 0; i != " << getUpperName()
|
||||||
|
<< "Size; ++i) {\n"
|
||||||
|
<< " " << getUpperName()
|
||||||
|
<< ".push_back(ParamIdx(Record.readInt(), " << getUpperName()
|
||||||
|
<< "HasThis));\n"
|
||||||
|
<< " }\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void writePCHReadArgs(raw_ostream &OS) const override {
|
||||||
|
OS << getUpperName() << ".data(), " << getUpperName() << "Size";
|
||||||
|
}
|
||||||
|
|
||||||
|
void writePCHWrite(raw_ostream &OS) const override {
|
||||||
|
OS << " Record.push_back(SA->" << getLowerName() << "_size());\n";
|
||||||
|
OS << " if (SA->" << getLowerName() << "_size())\n"
|
||||||
|
<< " Record.push_back(SA->" << getLowerName()
|
||||||
|
<< "_begin()->hasThis());\n";
|
||||||
|
OS << " for (auto Idx : SA->" << getLowerName() << "())\n"
|
||||||
|
<< " Record.push_back(Idx.getSourceIndex());\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeValueImpl(raw_ostream &OS) const override {
|
||||||
|
OS << " OS << Val.getSourceIndex();\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeDump(raw_ostream &OS) const override {
|
||||||
|
OS << " for (auto Idx : SA->" << getLowerName() << "())\n";
|
||||||
|
OS << " OS << \" \" << Idx.getSourceIndex();\n";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ParamIdxArgument : public Argument {
|
||||||
|
std::string IdxName;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ParamIdxArgument(const Record &Arg, StringRef Attr)
|
||||||
|
: Argument(Arg, Attr), IdxName(getUpperName()) {}
|
||||||
|
|
||||||
|
void writeDeclarations(raw_ostream &OS) const override {
|
||||||
|
OS << "ParamIdx " << IdxName << ";\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeAccessors(raw_ostream &OS) const override {
|
||||||
|
OS << "\n"
|
||||||
|
<< " ParamIdx " << getLowerName() << "() const {"
|
||||||
|
<< " return " << IdxName << "; }\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeCtorParameters(raw_ostream &OS) const override {
|
||||||
|
OS << "ParamIdx " << IdxName;
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeCloneArgs(raw_ostream &OS) const override { OS << IdxName; }
|
||||||
|
|
||||||
|
void writeTemplateInstantiationArgs(raw_ostream &OS) const override {
|
||||||
|
OS << "A->" << getLowerName() << "()";
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeImplicitCtorArgs(raw_ostream &OS) const override {
|
||||||
|
OS << IdxName;
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeCtorInitializers(raw_ostream &OS) const override {
|
||||||
|
OS << IdxName << "(" << IdxName << ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeCtorDefaultInitializers(raw_ostream &OS) const override {
|
||||||
|
OS << IdxName << "()";
|
||||||
|
}
|
||||||
|
|
||||||
|
void writePCHReadDecls(raw_ostream &OS) const override {
|
||||||
|
OS << " unsigned " << IdxName << "Src = Record.readInt();\n";
|
||||||
|
OS << " bool " << IdxName << "HasThis = Record.readInt();\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void writePCHReadArgs(raw_ostream &OS) const override {
|
||||||
|
OS << "ParamIdx(" << IdxName << "Src, " << IdxName << "HasThis)";
|
||||||
|
}
|
||||||
|
|
||||||
|
void writePCHWrite(raw_ostream &OS) const override {
|
||||||
|
OS << " Record.push_back(SA->" << getLowerName()
|
||||||
|
<< "().isValid() ? SA->" << getLowerName()
|
||||||
|
<< "().getSourceIndex() : 0);\n";
|
||||||
|
OS << " Record.push_back(SA->" << getLowerName()
|
||||||
|
<< "().isValid() ? SA->" << getLowerName()
|
||||||
|
<< "().hasThis() : false);\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string getIsOmitted() const override {
|
||||||
|
return "!" + IdxName + ".isValid()";
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeValue(raw_ostream &OS) const override {
|
||||||
|
OS << "\" << " << IdxName << ".getSourceIndex() << \"";
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeDump(raw_ostream &OS) const override {
|
||||||
|
if (isOptional())
|
||||||
|
OS << " if (SA->" << getLowerName() << "().isValid())\n ";
|
||||||
|
OS << " OS << \" \" << SA->" << getLowerName()
|
||||||
|
<< "().getSourceIndex();\n";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Unique the enums, but maintain the original declaration ordering.
|
// Unique the enums, but maintain the original declaration ordering.
|
||||||
std::vector<StringRef>
|
std::vector<StringRef>
|
||||||
uniqueEnumsInOrder(const std::vector<StringRef> &enums) {
|
uniqueEnumsInOrder(const std::vector<StringRef> &enums) {
|
||||||
|
@ -1247,6 +1376,10 @@ createArgument(const Record &Arg, StringRef Attr,
|
||||||
Ptr = llvm::make_unique<VariadicEnumArgument>(Arg, Attr);
|
Ptr = llvm::make_unique<VariadicEnumArgument>(Arg, Attr);
|
||||||
else if (ArgName == "VariadicExprArgument")
|
else if (ArgName == "VariadicExprArgument")
|
||||||
Ptr = llvm::make_unique<VariadicExprArgument>(Arg, Attr);
|
Ptr = llvm::make_unique<VariadicExprArgument>(Arg, Attr);
|
||||||
|
else if (ArgName == "VariadicParamIdxArgument")
|
||||||
|
Ptr = llvm::make_unique<VariadicParamIdxArgument>(Arg, Attr);
|
||||||
|
else if (ArgName == "ParamIdxArgument")
|
||||||
|
Ptr = llvm::make_unique<ParamIdxArgument>(Arg, Attr);
|
||||||
else if (ArgName == "VersionArgument")
|
else if (ArgName == "VersionArgument")
|
||||||
Ptr = llvm::make_unique<VersionArgument>(Arg, Attr);
|
Ptr = llvm::make_unique<VersionArgument>(Arg, Attr);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue