[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:
Joel E. Denny 2018-03-02 19:03:22 +00:00
parent 12b3556742
commit aafdf02711
16 changed files with 503 additions and 150 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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()));
} }
} }

View File

@ -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()));
} }
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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