aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorTed Kremenek <kremenek@apple.com>2010-07-31 01:52:11 +0000
committerTed Kremenek <kremenek@apple.com>2010-07-31 01:52:11 +0000
commitdd0e490c24aeade2c59ca4cae171199f6af9f02e (patch)
tree6fbe261cccc17fa85e01252bb7422f932f1d9b0b /lib
parentcc5888d833caf90ebda37f24da40d2cd06b4d820 (diff)
After a lengthy design discussion, add support for "ownership attributes" for malloc/free checking. Patch by Andrew McGregor!
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@109939 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib')
-rw-r--r--lib/AST/AttrImpl.cpp53
-rw-r--r--lib/Checker/MallocChecker.cpp173
-rw-r--r--lib/Lex/PPMacroExpansion.cpp3
-rw-r--r--lib/Parse/AttributeList.cpp3
-rw-r--r--lib/Sema/SemaDeclAttr.cpp175
5 files changed, 387 insertions, 20 deletions
diff --git a/lib/AST/AttrImpl.cpp b/lib/AST/AttrImpl.cpp
index 7277bbce24..4146248fb0 100644
--- a/lib/AST/AttrImpl.cpp
+++ b/lib/AST/AttrImpl.cpp
@@ -48,6 +48,43 @@ NonNullAttr::NonNullAttr(ASTContext &C, unsigned* arg_nums, unsigned size)
memcpy(ArgNums, arg_nums, sizeof(*ArgNums)*size);
}
+OwnershipAttr::OwnershipAttr(attr::Kind AK, ASTContext &C, unsigned* arg_nums,
+ unsigned size,
+ llvm::StringRef module,
+ OwnershipKind kind) :
+ AttrWithString(AK, C, module), ArgNums(0), Size(0), OKind(kind) {
+ if (size == 0)
+ return;
+ assert(arg_nums);
+ ArgNums = new (C) unsigned[size];
+ Size = size;
+ AKind = AK;
+ OKind = kind;
+ memcpy(ArgNums, arg_nums, sizeof(*ArgNums) * size);
+}
+
+
+void OwnershipAttr::Destroy(ASTContext &C) {
+ if (ArgNums)
+ C.Deallocate(ArgNums);
+}
+
+OwnershipTakesAttr::OwnershipTakesAttr(ASTContext &C, unsigned* arg_nums,
+ unsigned size, llvm::StringRef module) :
+ OwnershipAttr(attr::OwnershipTakes, C, arg_nums, size, module, Takes) {
+}
+
+OwnershipHoldsAttr::OwnershipHoldsAttr(ASTContext &C, unsigned* arg_nums,
+ unsigned size, llvm::StringRef module) :
+ OwnershipAttr(attr::OwnershipHolds, C, arg_nums, size, module, Holds) {
+}
+
+OwnershipReturnsAttr::OwnershipReturnsAttr(ASTContext &C, unsigned* arg_nums,
+ unsigned size,
+ llvm::StringRef module) :
+ OwnershipAttr(attr::OwnershipReturns, C, arg_nums, size, module, Returns) {
+}
+
#define DEF_SIMPLE_ATTR_CLONE(ATTR) \
Attr *ATTR##Attr::clone(ASTContext &C) const { \
return ::new (C) ATTR##Attr; \
@@ -147,6 +184,22 @@ Attr *NonNullAttr::clone(ASTContext &C) const {
return ::new (C) NonNullAttr(C, ArgNums, Size);
}
+Attr *OwnershipAttr::clone(ASTContext &C) const {
+ return ::new (C) OwnershipAttr(AKind, C, ArgNums, Size, getModule(), OKind);
+}
+
+Attr *OwnershipReturnsAttr::clone(ASTContext &C) const {
+ return ::new (C) OwnershipReturnsAttr(C, ArgNums, Size, getModule());
+}
+
+Attr *OwnershipTakesAttr::clone(ASTContext &C) const {
+ return ::new (C) OwnershipTakesAttr(C, ArgNums, Size, getModule());
+}
+
+Attr *OwnershipHoldsAttr::clone(ASTContext &C) const {
+ return ::new (C) OwnershipHoldsAttr(C, ArgNums, Size, getModule());
+}
+
Attr *FormatAttr::clone(ASTContext &C) const {
return ::new (C) FormatAttr(C, getType(), formatIdx, firstArg);
}
diff --git a/lib/Checker/MallocChecker.cpp b/lib/Checker/MallocChecker.cpp
index dcc21ca386..4ed309513e 100644
--- a/lib/Checker/MallocChecker.cpp
+++ b/lib/Checker/MallocChecker.cpp
@@ -24,15 +24,17 @@ using namespace clang;
namespace {
class RefState {
- enum Kind { AllocateUnchecked, AllocateFailed, Released, Escaped } K;
+ enum Kind { AllocateUnchecked, AllocateFailed, Released, Escaped, Relinquished } K;
const Stmt *S;
public:
RefState(Kind k, const Stmt *s) : K(k), S(s) {}
bool isAllocated() const { return K == AllocateUnchecked; }
+ bool isFailed() const { return K == AllocateFailed; }
bool isReleased() const { return K == Released; }
bool isEscaped() const { return K == Escaped; }
+ bool isRelinquished() const { return K == Relinquished; }
bool operator==(const RefState &X) const {
return K == X.K && S == X.S;
@@ -46,6 +48,7 @@ public:
}
static RefState getReleased(const Stmt *s) { return RefState(Released, s); }
static RefState getEscaped(const Stmt *s) { return RefState(Escaped, s); }
+ static RefState getRelinquished(const Stmt *s) { return RefState(Relinquished, s); }
void Profile(llvm::FoldingSetNodeID &ID) const {
ID.AddInteger(K);
@@ -59,12 +62,13 @@ class MallocChecker : public CheckerVisitor<MallocChecker> {
BuiltinBug *BT_DoubleFree;
BuiltinBug *BT_Leak;
BuiltinBug *BT_UseFree;
+ BuiltinBug *BT_UseRelinquished;
BuiltinBug *BT_BadFree;
IdentifierInfo *II_malloc, *II_free, *II_realloc, *II_calloc;
public:
MallocChecker()
- : BT_DoubleFree(0), BT_Leak(0), BT_UseFree(0), BT_BadFree(0),
+ : BT_DoubleFree(0), BT_Leak(0), BT_UseFree(0), BT_UseRelinquished(0), BT_BadFree(0),
II_malloc(0), II_free(0), II_realloc(0), II_calloc(0) {}
static void *getTag();
bool EvalCallExpr(CheckerContext &C, const CallExpr *CE);
@@ -73,9 +77,13 @@ public:
void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S);
const GRState *EvalAssume(const GRState *state, SVal Cond, bool Assumption);
void VisitLocation(CheckerContext &C, const Stmt *S, SVal l);
+ virtual void PreVisitBind(CheckerContext &C, const Stmt *AssignE,
+ const Stmt *StoreE, SVal location,
+ SVal val);
private:
void MallocMem(CheckerContext &C, const CallExpr *CE);
+ void MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE, const OwnershipAttr* Att);
const GRState *MallocMemAux(CheckerContext &C, const CallExpr *CE,
const Expr *SizeEx, SVal Init,
const GRState *state) {
@@ -86,8 +94,9 @@ private:
const GRState *state);
void FreeMem(CheckerContext &C, const CallExpr *CE);
+ void FreeMemAttr(CheckerContext &C, const CallExpr *CE, const OwnershipAttr* Att);
const GRState *FreeMemAux(CheckerContext &C, const CallExpr *CE,
- const GRState *state);
+ const GRState *state, unsigned Num, bool Hold);
void ReallocMem(CheckerContext &C, const CallExpr *CE);
void CallocMem(CheckerContext &C, const CallExpr *CE);
@@ -156,7 +165,31 @@ bool MallocChecker::EvalCallExpr(CheckerContext &C, const CallExpr *CE) {
return true;
}
- return false;
+ // Check all the attributes, if there are any.
+ // There can be multiple of these attributes.
+ bool rv = false;
+ if (FD->hasAttrs()) {
+ for (const Attr *attr = FD->getAttrs(); attr; attr = attr->getNext()) {
+ if(const OwnershipAttr* Att = dyn_cast<OwnershipAttr>(attr)) {
+ switch (Att->getKind()) {
+ case OwnershipAttr::Returns: {
+ MallocMemReturnsAttr(C, CE, Att);
+ rv = true;
+ break;
+ }
+ case OwnershipAttr::Takes:
+ case OwnershipAttr::Holds: {
+ FreeMemAttr(C, CE, Att);
+ rv = true;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+ }
+ return rv;
}
void MallocChecker::MallocMem(CheckerContext &C, const CallExpr *CE) {
@@ -165,6 +198,23 @@ void MallocChecker::MallocMem(CheckerContext &C, const CallExpr *CE) {
C.addTransition(state);
}
+void MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE,
+ const OwnershipAttr* Att) {
+ if (!Att->isModule("malloc"))
+ return;
+
+ const unsigned *I = Att->begin(), *E = Att->end();
+ if (I != E) {
+ const GRState *state =
+ MallocMemAux(C, CE, CE->getArg(*I), UndefinedVal(), C.getState());
+ C.addTransition(state);
+ return;
+ }
+ const GRState *state = MallocMemAux(C, CE, UnknownVal(), UndefinedVal(),
+ C.getState());
+ C.addTransition(state);
+}
+
const GRState *MallocChecker::MallocMemAux(CheckerContext &C,
const CallExpr *CE,
SVal Size, SVal Init,
@@ -196,25 +246,52 @@ const GRState *MallocChecker::MallocMemAux(CheckerContext &C,
}
void MallocChecker::FreeMem(CheckerContext &C, const CallExpr *CE) {
- const GRState *state = FreeMemAux(C, CE, C.getState());
+ const GRState *state = FreeMemAux(C, CE, C.getState(), 0, false);
if (state)
C.addTransition(state);
}
+void MallocChecker::FreeMemAttr(CheckerContext &C, const CallExpr *CE,
+ const OwnershipAttr* Att) {
+ if (!Att->isModule("malloc"))
+ return;
+
+ for (const unsigned *I = Att->begin(), *E = Att->end(); I != E; ++I) {
+ const GRState *state =
+ FreeMemAux(C, CE, C.getState(), *I, Att->isKind(OwnershipAttr::Holds));
+ if (state)
+ C.addTransition(state);
+ }
+}
+
const GRState *MallocChecker::FreeMemAux(CheckerContext &C, const CallExpr *CE,
- const GRState *state) {
- const Expr *ArgExpr = CE->getArg(0);
+ const GRState *state, unsigned Num,
+ bool Hold) {
+ const Expr *ArgExpr = CE->getArg(Num);
SVal ArgVal = state->getSVal(ArgExpr);
- // If ptr is NULL, no operation is preformed.
- if (ArgVal.isZeroConstant())
+ DefinedOrUnknownSVal location = cast<DefinedOrUnknownSVal>(ArgVal);
+
+ // Check for null dereferences.
+ if (!isa<Loc>(location))
return state;
-
+
+ // FIXME: Technically using 'Assume' here can result in a path
+ // bifurcation. In such cases we need to return two states, not just one.
+ const GRState *notNullState, *nullState;
+ llvm::tie(notNullState, nullState) = state->Assume(location);
+
+ // The explicit NULL case, no operation is performed.
+ if (nullState && !notNullState)
+ return nullState;
+
+ assert(notNullState);
+
// Unknown values could easily be okay
// Undefined values are handled elsewhere
if (ArgVal.isUnknownOrUndef())
- return state;
+ return notNullState;
const MemRegion *R = ArgVal.getAsRegion();
@@ -253,7 +330,7 @@ const GRState *MallocChecker::FreeMemAux(CheckerContext &C, const CallExpr *CE,
// Various cases could lead to non-symbol values here.
// For now, ignore them.
if (!SR)
- return state;
+ return notNullState;
SymbolRef Sym = SR->getSymbol();
@@ -263,14 +340,15 @@ const GRState *MallocChecker::FreeMemAux(CheckerContext &C, const CallExpr *CE,
// called on a pointer that does not get its pointee directly from malloc().
// Full support of this requires inter-procedural analysis.
if (!RS)
- return state;
+ return notNullState;
// Check double free.
if (RS->isReleased()) {
ExplodedNode *N = C.GenerateSink();
if (N) {
if (!BT_DoubleFree)
- BT_DoubleFree = new BuiltinBug("Double free",
+ BT_DoubleFree
+ = new BuiltinBug("Double free",
"Try to free a memory block that has been released");
// FIXME: should find where it's freed last time.
BugReport *R = new BugReport(*BT_DoubleFree,
@@ -281,7 +359,9 @@ const GRState *MallocChecker::FreeMemAux(CheckerContext &C, const CallExpr *CE,
}
// Normal free.
- return state->set<RegionState>(Sym, RefState::getReleased(CE));
+ if (Hold)
+ return notNullState->set<RegionState>(Sym, RefState::getRelinquished(CE));
+ return notNullState->set<RegionState>(Sym, RefState::getReleased(CE));
}
bool MallocChecker::SummarizeValue(llvm::raw_ostream& os, SVal V) {
@@ -446,13 +526,13 @@ void MallocChecker::ReallocMem(CheckerContext &C, const CallExpr *CE) {
ValMgr.makeIntValWithPtrWidth(0, false));
if (const GRState *stateSizeZero = stateNotEqual->Assume(SizeZero, true)) {
- const GRState *stateFree = FreeMemAux(C, CE, stateSizeZero);
+ const GRState *stateFree = FreeMemAux(C, CE, stateSizeZero, 0, false);
if (stateFree)
C.addTransition(stateFree->BindExpr(CE, UndefinedVal(), true));
}
if (const GRState *stateSizeNotZero=stateNotEqual->Assume(SizeZero,false)) {
- const GRState *stateFree = FreeMemAux(C, CE, stateSizeNotZero);
+ const GRState *stateFree = FreeMemAux(C, CE, stateSizeNotZero, 0, false);
if (stateFree) {
// FIXME: We should copy the content of the original buffer.
const GRState *stateRealloc = MallocMemAux(C, CE, CE->getArg(1),
@@ -581,3 +661,62 @@ void MallocChecker::VisitLocation(CheckerContext &C, const Stmt *S, SVal l) {
}
}
}
+
+void MallocChecker::PreVisitBind(CheckerContext &C,
+ const Stmt *AssignE,
+ const Stmt *StoreE,
+ SVal location,
+ SVal val) {
+ // The PreVisitBind implements the same algorithm as already used by the
+ // Objective C ownership checker: if the pointer escaped from this scope by
+ // assignment, let it go. However, assigning to fields of a stack-storage
+ // structure does not transfer ownership.
+
+ const GRState *state = C.getState();
+ DefinedOrUnknownSVal l = cast<DefinedOrUnknownSVal>(location);
+
+ // Check for null dereferences.
+ if (!isa<Loc>(l))
+ return;
+
+ // Before checking if the state is null, check if 'val' has a RefState.
+ // Only then should we check for null and bifurcate the state.
+ SymbolRef Sym = val.getLocSymbolInBase();
+ if (Sym) {
+ if (const RefState *RS = state->get<RegionState>(Sym)) {
+ // If ptr is NULL, no operation is performed.
+ const GRState *notNullState, *nullState;
+ llvm::tie(notNullState, nullState) = state->Assume(l);
+
+ // Generate a transition for 'nullState' to record the assumption
+ // that the state was null.
+ if (nullState)
+ C.addTransition(nullState);
+
+ if (!notNullState)
+ return;
+
+ if (RS->isAllocated()) {
+ // Something we presently own is being assigned somewhere.
+ const MemRegion *AR = location.getAsRegion();
+ if (!AR)
+ return;
+ AR = AR->StripCasts()->getBaseRegion();
+ do {
+ // If it is on the stack, we still own it.
+ if (AR->hasStackNonParametersStorage())
+ break;
+
+ // If the state can't represent this binding, we still own it.
+ if (notNullState == (notNullState->bindLoc(cast<Loc>(location), UnknownVal())))
+ break;
+
+ // We no longer own this pointer.
+ notNullState = notNullState->set<RegionState>(Sym, RefState::getRelinquished(StoreE));
+ }
+ while (false);
+ }
+ C.addTransition(notNullState);
+ }
+ }
+}
diff --git a/lib/Lex/PPMacroExpansion.cpp b/lib/Lex/PPMacroExpansion.cpp
index ebf606e940..ad05b40e42 100644
--- a/lib/Lex/PPMacroExpansion.cpp
+++ b/lib/Lex/PPMacroExpansion.cpp
@@ -506,6 +506,9 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) {
.Case("cxx_static_assert", LangOpts.CPlusPlus0x)
.Case("objc_nonfragile_abi", LangOpts.ObjCNonFragileABI)
.Case("objc_weak_class", LangOpts.ObjCNonFragileABI)
+ .Case("ownership_holds", true)
+ .Case("ownership_returns", true)
+ .Case("ownership_takes", true)
//.Case("cxx_concepts", false)
//.Case("cxx_lambdas", false)
//.Case("cxx_nullptr", false)
diff --git a/lib/Parse/AttributeList.cpp b/lib/Parse/AttributeList.cpp
index 98d5d07e15..dc94420bef 100644
--- a/lib/Parse/AttributeList.cpp
+++ b/lib/Parse/AttributeList.cpp
@@ -118,6 +118,9 @@ AttributeList::Kind AttributeList::getKind(const IdentifierInfo *Name) {
.Case("ns_returns_retained", AT_ns_returns_retained)
.Case("cf_returns_not_retained", AT_cf_returns_not_retained)
.Case("cf_returns_retained", AT_cf_returns_retained)
+ .Case("ownership_returns", AT_ownership_returns)
+ .Case("ownership_holds", AT_ownership_holds)
+ .Case("ownership_takes", AT_ownership_takes)
.Case("reqd_work_group_size", AT_reqd_wg_size)
.Case("init_priority", AT_init_priority)
.Case("no_instrument_function", AT_no_instrument_function)
diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp
index 3b82f58be4..cebe6e7850 100644
--- a/lib/Sema/SemaDeclAttr.cpp
+++ b/lib/Sema/SemaDeclAttr.cpp
@@ -346,10 +346,175 @@ static void HandleNonNullAttr(Decl *d, const AttributeList &Attr, Sema &S) {
unsigned* start = &NonNullArgs[0];
unsigned size = NonNullArgs.size();
- std::sort(start, start + size);
+ llvm::array_pod_sort(start, start + size);
d->addAttr(::new (S.Context) NonNullAttr(S.Context, start, size));
}
+static void HandleOwnershipAttr(Decl *d, const AttributeList &AL, Sema &S) {
+ // This attribute must be applied to a function declaration.
+ // The first argument to the attribute must be a string,
+ // the name of the resource, for example "malloc".
+ // The following arguments must be argument indexes, the arguments must be
+ // of integer type for Returns, otherwise of pointer type.
+ // The difference between Holds and Takes is that a pointer may still be used
+ // after being held. free() should be __attribute((ownership_takes)), whereas a list
+ // append function may well be __attribute((ownership_holds)).
+
+ if (!AL.getParameterName()) {
+ S.Diag(AL.getLoc(), diag::err_attribute_argument_n_not_string)
+ << AL.getName()->getName() << 1;
+ return;
+ }
+ // Figure out our Kind, and check arguments while we're at it.
+ OwnershipAttr::OwnershipKind K = OwnershipAttr::None;
+ if (AL.getName()->getName().equals("ownership_takes")) {
+ K = OwnershipAttr::Takes;
+ if (AL.getNumArgs() < 1) {
+ S.Diag(AL.getLoc(), diag::err_attribute_wrong_number_arguments) << 2;
+ return;
+ }
+ } else if (AL.getName()->getName().equals("ownership_holds")) {
+ K = OwnershipAttr::Holds;
+ if (AL.getNumArgs() < 1) {
+ S.Diag(AL.getLoc(), diag::err_attribute_wrong_number_arguments) << 2;
+ return;
+ }
+ } else if (AL.getName()->getName().equals("ownership_returns")) {
+ K = OwnershipAttr::Returns;
+ if (AL.getNumArgs() > 1) {
+ S.Diag(AL.getLoc(), diag::err_attribute_wrong_number_arguments)
+ << AL.getNumArgs() + 1;
+ return;
+ }
+ }
+ // This should never happen given how we are called.
+ if (K == OwnershipAttr::None) {
+ return;
+ }
+
+ if (!isFunction(d) || !hasFunctionProto(d)) {
+ S.Diag(AL.getLoc(), diag::warn_attribute_wrong_decl_type) << AL.getName()
+ << 0 /*function*/;
+ return;
+ }
+
+ unsigned NumArgs = getFunctionOrMethodNumArgs(d);
+
+ llvm::StringRef Module = AL.getParameterName()->getName();
+
+ // Normalize the argument, __foo__ becomes foo.
+ if (Module.startswith("__") && Module.endswith("__"))
+ Module = Module.substr(2, Module.size() - 4);
+
+ llvm::SmallVector<unsigned, 10> OwnershipArgs;
+
+ for (AttributeList::arg_iterator I = AL.arg_begin(), E = AL.arg_end(); I != E; ++I) {
+
+ Expr *IdxExpr = static_cast<Expr *>(*I);
+ llvm::APSInt ArgNum(32);
+ if (IdxExpr->isTypeDependent() || IdxExpr->isValueDependent()
+ || !IdxExpr->isIntegerConstantExpr(ArgNum, S.Context)) {
+ S.Diag(AL.getLoc(), diag::err_attribute_argument_not_int)
+ << AL.getName()->getName() << IdxExpr->getSourceRange();
+ continue;
+ }
+
+ unsigned x = (unsigned) ArgNum.getZExtValue();
+
+ if (x > NumArgs || x < 1) {
+ S.Diag(AL.getLoc(), diag::err_attribute_argument_out_of_bounds)
+ << AL.getName()->getName() << x << IdxExpr->getSourceRange();
+ continue;
+ }
+ --x;
+ switch (K) {
+ case OwnershipAttr::Takes:
+ case OwnershipAttr::Holds: {
+ // Is the function argument a pointer type?
+ QualType T = getFunctionOrMethodArgType(d, x);
+ if (!T->isAnyPointerType() && !T->isBlockPointerType()) {
+ // FIXME: Should also highlight argument in decl.
+ S.Diag(AL.getLoc(), diag::err_ownership_type)
+ << ((K==OwnershipAttr::Takes)?"ownership_takes":"ownership_holds")
+ << "pointer"
+ << IdxExpr->getSourceRange();
+ continue;
+ }
+ break;
+ }
+ case OwnershipAttr::Returns: {
+ if (AL.getNumArgs() > 1) {
+ // Is the function argument an integer type?
+ Expr *IdxExpr = static_cast<Expr *>(AL.getArg(0));
+ llvm::APSInt ArgNum(32);
+ if (IdxExpr->isTypeDependent() || IdxExpr->isValueDependent()
+ || !IdxExpr->isIntegerConstantExpr(ArgNum, S.Context)) {
+ S.Diag(AL.getLoc(), diag::err_ownership_type)
+ << "ownership_returns" << "integer"
+ << IdxExpr->getSourceRange();
+ return;
+ }
+ }
+ break;
+ }
+ // Should never happen, here to silence a warning.
+ default: {
+ return;
+ }
+ } // switch
+
+ // Check we don't have a conflict with another ownership attribute.
+ if (d->hasAttrs()) {
+ for (const Attr *attr = d->getAttrs(); attr; attr = attr->getNext()) {
+ if (const OwnershipAttr* Att = dyn_cast<OwnershipAttr>(attr)) {
+ // Two ownership attributes of the same kind can't conflict,
+ // except returns attributes.
+ if (K == OwnershipAttr::Returns || Att->getKind() != K) {
+ for (const unsigned *I = Att->begin(), *E = Att->end(); I != E; ++I) {
+ if (x == *I) {
+ S.Diag(AL.getLoc(), diag::err_attributes_are_not_compatible)
+ << AL.getName()->getName() << "ownership_*";
+ }
+ }
+ }
+ }
+ }
+ }
+ OwnershipArgs.push_back(x);
+ }
+
+ unsigned* start = OwnershipArgs.data();
+ unsigned size = OwnershipArgs.size();
+ llvm::array_pod_sort(start, start + size);
+ switch (K) {
+ case OwnershipAttr::Takes: {
+ if (OwnershipArgs.empty()) {
+ S.Diag(AL.getLoc(), diag::err_attribute_wrong_number_arguments) << 2;
+ return;
+ }
+ d->addAttr(::new (S.Context) OwnershipTakesAttr(S.Context, start, size,
+ Module));
+ break;
+ }
+ case OwnershipAttr::Holds: {
+ if (OwnershipArgs.empty()) {
+ S.Diag(AL.getLoc(), diag::err_attribute_wrong_number_arguments) << 2;
+ return;
+ }
+ d->addAttr(::new (S.Context) OwnershipHoldsAttr(S.Context, start, size,
+ Module));
+ break;
+ }
+ case OwnershipAttr::Returns: {
+ d->addAttr(::new (S.Context) OwnershipReturnsAttr(S.Context, start, size,
+ Module));
+ break;
+ }
+ default:
+ break;
+ }
+}
+
static bool isStaticVarOrStaticFunciton(Decl *D) {
if (VarDecl *VD = dyn_cast<VarDecl>(D))
return VD->getStorageClass() == VarDecl::Static;
@@ -379,7 +544,7 @@ static void HandleWeakRefAttr(Decl *d, const AttributeList &Attr, Sema &S) {
Ctx = Ctx->getLookupContext();
if (!isa<TranslationUnitDecl>(Ctx) && !isa<NamespaceDecl>(Ctx) ) {
S.Diag(Attr.getLoc(), diag::err_attribute_weakref_not_global_context) <<
- dyn_cast<NamedDecl>(d)->getNameAsString();
+ dyn_cast<NamedDecl>(d)->getNameAsString();
return;
}
}
@@ -2009,6 +2174,10 @@ static void ProcessDeclAttribute(Scope *scope, Decl *D,
case AttributeList::AT_mode: HandleModeAttr (D, Attr, S); break;
case AttributeList::AT_malloc: HandleMallocAttr (D, Attr, S); break;
case AttributeList::AT_nonnull: HandleNonNullAttr (D, Attr, S); break;
+ case AttributeList::AT_ownership_returns:
+ case AttributeList::AT_ownership_takes:
+ case AttributeList::AT_ownership_holds:
+ HandleOwnershipAttr (D, Attr, S); break;
case AttributeList::AT_noreturn: HandleNoReturnAttr (D, Attr, S); break;
case AttributeList::AT_nothrow: HandleNothrowAttr (D, Attr, S); break;
case AttributeList::AT_override: HandleOverrideAttr (D, Attr, S); break;
@@ -2087,7 +2256,7 @@ void Sema::ProcessDeclAttributeList(Scope *S, Decl *D, const AttributeList *Attr
// but that looks really pointless. We reject it.
if (D->hasAttr<WeakRefAttr>() && !D->hasAttr<AliasAttr>()) {
Diag(AttrList->getLoc(), diag::err_attribute_weakref_without_alias) <<
- dyn_cast<NamedDecl>(D)->getNameAsString();
+ dyn_cast<NamedDecl>(D)->getNameAsString();
return;
}
}