[clang] [Sema] Support negation/parens with __builtin_available (PR #111439)
George Burgess IV via cfe-commits
cfe-commits at lists.llvm.org
Mon Oct 7 14:07:11 PDT 2024
https://github.com/gburgessiv created https://github.com/llvm/llvm-project/pull/111439
At present, `__builtin_available` is really restrictive with its use. Overall, this seems like a good thing, since the analyses behind it are not very expensive.
That said, it's very straightforward to support these two cases:
```
if ((__builtin_available(foo, *))) {
// ...
}
```
and
```
if (!__builtin_available(foo, *)) {
// ...
} else {
// ...
}
```
Seems nice to do so.
>From d2dda3e6d96e6c77de40abe967e336c37bff1823 Mon Sep 17 00:00:00 2001
From: George Burgess IV <george.burgess.iv at gmail.com>
Date: Mon, 7 Oct 2024 14:34:11 -0600
Subject: [PATCH] [Sema] Support negation/parens with __builtin_available
At present, `__builtin_available` is really restrictive with its use.
Overall, this seems like a good thing, since the analyses behind it are
not very expensive.
That said, it's very straightforward to support these two cases:
```
if ((__builtin_available(foo, *))) {
// ...
}
```
and
```
if (!__builtin_available(foo, *)) {
// ...
} else {
// ...
}
```
Seems nice to do so.
---
clang/lib/Sema/SemaAvailability.cpp | 51 ++++++++++++++++++++++-------
clang/test/Sema/attr-availability.c | 25 +++++++++++++-
2 files changed, 64 insertions(+), 12 deletions(-)
diff --git a/clang/lib/Sema/SemaAvailability.cpp b/clang/lib/Sema/SemaAvailability.cpp
index e04cbeec165552..798cabaa31a476 100644
--- a/clang/lib/Sema/SemaAvailability.cpp
+++ b/clang/lib/Sema/SemaAvailability.cpp
@@ -1005,25 +1005,54 @@ bool DiagnoseUnguardedAvailability::VisitTypeLoc(TypeLoc Ty) {
return true;
}
+struct ExtractedAvailabilityExpr {
+ const ObjCAvailabilityCheckExpr *E = nullptr;
+ bool isNegated = false;
+};
+
+ExtractedAvailabilityExpr extractAvailabilityExpr(const Expr *IfCond) {
+ const auto *E = IfCond;
+ bool IsNegated = false;
+ while (true) {
+ E = E->IgnoreParens();
+ if (const auto *AE = dyn_cast<ObjCAvailabilityCheckExpr>(E)) {
+ return ExtractedAvailabilityExpr{AE, IsNegated};
+ }
+
+ const auto *UO = dyn_cast<UnaryOperator>(E);
+ if (!UO || UO->getOpcode() != UO_LNot) {
+ return ExtractedAvailabilityExpr{};
+ }
+ E = UO->getSubExpr();
+ IsNegated = !IsNegated;
+ }
+}
+
bool DiagnoseUnguardedAvailability::TraverseIfStmt(IfStmt *If) {
- VersionTuple CondVersion;
- if (auto *E = dyn_cast<ObjCAvailabilityCheckExpr>(If->getCond())) {
- CondVersion = E->getVersion();
-
- // If we're using the '*' case here or if this check is redundant, then we
- // use the enclosing version to check both branches.
- if (CondVersion.empty() || CondVersion <= AvailabilityStack.back())
- return TraverseStmt(If->getThen()) && TraverseStmt(If->getElse());
- } else {
+ ExtractedAvailabilityExpr IfCond = extractAvailabilityExpr(If->getCond());
+ if (!IfCond.E) {
// This isn't an availability checking 'if', we can just continue.
return Base::TraverseIfStmt(If);
}
+ VersionTuple CondVersion = IfCond.E->getVersion();
+ // If we're using the '*' case here or if this check is redundant, then we
+ // use the enclosing version to check both branches.
+ if (CondVersion.empty() || CondVersion <= AvailabilityStack.back()) {
+ return TraverseStmt(If->getThen()) && TraverseStmt(If->getElse());
+ }
+
+ auto *Guarded = If->getThen();
+ auto *Unguarded = If->getElse();
+ if (IfCond.isNegated) {
+ std::swap(Guarded, Unguarded);
+ }
+
AvailabilityStack.push_back(CondVersion);
- bool ShouldContinue = TraverseStmt(If->getThen());
+ bool ShouldContinue = TraverseStmt(Guarded);
AvailabilityStack.pop_back();
- return ShouldContinue && TraverseStmt(If->getElse());
+ return ShouldContinue && TraverseStmt(Unguarded);
}
} // end anonymous namespace
diff --git a/clang/test/Sema/attr-availability.c b/clang/test/Sema/attr-availability.c
index a5cc602a8fa9d4..a496c5271f2a3d 100644
--- a/clang/test/Sema/attr-availability.c
+++ b/clang/test/Sema/attr-availability.c
@@ -40,7 +40,7 @@ void test_10095131(void) {
#ifdef WARN_PARTIAL
// FIXME: This note should point to the declaration with the availability
// attribute.
-// expected-note at +2 {{'PartiallyAvailable' has been marked as being introduced in macOS 10.8 here, but the deployment target is macOS 10.5}}
+// expected-note at +2 5 {{'PartiallyAvailable' has been marked as being introduced in macOS 10.8 here, but the deployment target is macOS 10.5}}
#endif
extern void PartiallyAvailable(void) ;
void with_redeclaration(void) {
@@ -53,6 +53,29 @@ void with_redeclaration(void) {
enum PartialEnum p = kPartialEnumConstant;
}
+#ifdef WARN_PARTIAL
+void conditional_warnings() {
+ if (__builtin_available(macos 10.8, *)) {
+ PartiallyAvailable();
+ } else {
+ PartiallyAvailable(); // expected-warning {{only available on macOS 10.8 or newer}} expected-note {{enclose 'PartiallyAvailable'}}
+ }
+ if (!__builtin_available(macos 10.8, *)) {
+ PartiallyAvailable(); // expected-warning {{only available on macOS 10.8 or newer}} expected-note {{enclose 'PartiallyAvailable'}}
+ } else {
+ PartiallyAvailable();
+ }
+ if (!!!(!__builtin_available(macos 10.8, *))) {
+ PartiallyAvailable();
+ } else {
+ PartiallyAvailable(); // expected-warning {{only available on macOS 10.8 or newer}} expected-note {{enclose 'PartiallyAvailable'}}
+ }
+ if (~__builtin_available(macos 10.8, *)) { // expected-warning {{does not guard availability here}}
+ PartiallyAvailable(); // expected-warning {{only available on macOS 10.8 or newer}} expected-note {{enclose 'PartiallyAvailable'}}
+ }
+}
+#endif
+
__attribute__((availability(macos, unavailable))) // expected-warning {{attribute 'availability' is ignored}}
enum {
NSDataWritingFileProtectionWriteOnly = 0x30000000,
More information about the cfe-commits
mailing list