diff options
author | JF Bastien <jfbastien@apple.com> | 2018-05-25 00:07:09 +0000 |
---|---|---|
committer | JF Bastien <jfbastien@apple.com> | 2018-05-25 00:07:09 +0000 |
commit | 7f0a05ada547b73a33f55d00a3fa984b2cd45a17 (patch) | |
tree | 2334b179bdf397a9fd3bb745d2f1746e19f1ca4a /clang/lib | |
parent | 698b0a674d26abffb8ad0cbc9f4f316d359f5a53 (diff) | |
download | bcm5719-llvm-7f0a05ada547b73a33f55d00a3fa984b2cd45a17.tar.gz bcm5719-llvm-7f0a05ada547b73a33f55d00a3fa984b2cd45a17.zip |
Make atomic non-member functions as nonnull
Summary:
As a companion to libc++ patch https://reviews.llvm.org/D47225, mark builtin atomic non-member functions which accept pointers as nonnull.
The atomic non-member functions accept pointers to std::atomic / std::atomic_flag as well as to the non-atomic value. These are all dereferenced unconditionally when lowered, and therefore will fault if null. It's a tiny gotcha for new users, especially when they pass in NULL as expected value (instead of passing a pointer to a NULL value).
<rdar://problem/18473124>
Reviewers: arphaman
Subscribers: aheejin, cfe-commits
Differential Revision: https://reviews.llvm.org/D47229
llvm-svn: 333246
Diffstat (limited to 'clang/lib')
-rw-r--r-- | clang/lib/Sema/SemaChecking.cpp | 46 |
1 files changed, 31 insertions, 15 deletions
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index bf95c22e99e..6f956b42f02 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -3451,9 +3451,10 @@ ExprResult Sema::SemaAtomicOpsOverloaded(ExprResult TheCallResult, return ExprError(); } - // atomic_fetch_or takes a pointer to a volatile 'A'. We shouldn't let the - // volatile-ness of the pointee-type inject itself into the result or the - // other operands. Similarly atomic_load can take a pointer to a const 'A'. + // All atomic operations have an overload which takes a pointer to a volatile + // 'A'. We shouldn't let the volatile-ness of the pointee-type inject itself + // into the result or the other operands. Similarly atomic_load takes a + // pointer to a const 'A'. ValType.removeLocalVolatile(); ValType.removeLocalConst(); QualType ResultType = ValType; @@ -3466,16 +3467,27 @@ ExprResult Sema::SemaAtomicOpsOverloaded(ExprResult TheCallResult, // The type of a parameter passed 'by value'. In the GNU atomics, such // arguments are actually passed as pointers. QualType ByValType = ValType; // 'CP' - if (!IsC11 && !IsN) + bool IsPassedByAddress = false; + if (!IsC11 && !IsN) { ByValType = Ptr->getType(); + IsPassedByAddress = true; + } - // The first argument --- the pointer --- has a fixed type; we - // deduce the types of the rest of the arguments accordingly. Walk - // the remaining arguments, converting them to the deduced value type. - for (unsigned i = 1; i != TheCall->getNumArgs(); ++i) { + // The first argument's non-CV pointer type is used to deduce the type of + // subsequent arguments, except for: + // - weak flag (always converted to bool) + // - memory order (always converted to int) + // - scope (always converted to int) + for (unsigned i = 0; i != TheCall->getNumArgs(); ++i) { QualType Ty; if (i < NumVals[Form] + 1) { switch (i) { + case 0: + // The first argument is always a pointer. It has a fixed type. + // It is always dereferenced, a nullptr is undefined. + CheckNonNullArgument(*this, TheCall->getArg(i), DRE->getLocStart()); + // Nothing else to do: we already know all we want about this pointer. + continue; case 1: // The second argument is the non-atomic operand. For arithmetic, this // is always passed by value, and for a compare_exchange it is always @@ -3484,14 +3496,16 @@ ExprResult Sema::SemaAtomicOpsOverloaded(ExprResult TheCallResult, assert(Form != Load); if (Form == Init || (Form == Arithmetic && ValType->isIntegerType())) Ty = ValType; - else if (Form == Copy || Form == Xchg) + else if (Form == Copy || Form == Xchg) { + if (IsPassedByAddress) + // The value pointer is always dereferenced, a nullptr is undefined. + CheckNonNullArgument(*this, TheCall->getArg(i), DRE->getLocStart()); Ty = ByValType; - else if (Form == Arithmetic) + } else if (Form == Arithmetic) Ty = Context.getPointerDiffType(); else { Expr *ValArg = TheCall->getArg(i); - // Treat this argument as _Nonnull as we want to show a warning if - // NULL is passed into it. + // The value pointer is always dereferenced, a nullptr is undefined. CheckNonNullArgument(*this, ValArg, DRE->getLocStart()); LangAS AS = LangAS::Default; // Keep address space of non-atomic pointer type. @@ -3504,8 +3518,10 @@ ExprResult Sema::SemaAtomicOpsOverloaded(ExprResult TheCallResult, } break; case 2: - // The third argument to compare_exchange / GNU exchange is a - // (pointer to a) desired value. + // The third argument to compare_exchange / GNU exchange is the desired + // value, either by-value (for the *_n variant) or as a pointer. + if (!IsN) + CheckNonNullArgument(*this, TheCall->getArg(i), DRE->getLocStart()); Ty = ByValType; break; case 3: @@ -3887,7 +3903,7 @@ Sema::SemaBuiltinAtomicOverloaded(ExprResult TheCallResult) { ResultType = Context.BoolTy; break; - case Builtin::BI__sync_lock_test_and_set: + case Builtin::BI__sync_lock_test_and_set: case Builtin::BI__sync_lock_test_and_set_1: case Builtin::BI__sync_lock_test_and_set_2: case Builtin::BI__sync_lock_test_and_set_4: |