Skip to content

Commit fc45470

Browse files
dougsonosDoug Wyatt
authored andcommitted
[Clang] SemaFunctionEffects: When verifying a function, ignore any trailing requires clause. (llvm#114266)
--------- Co-authored-by: Doug Wyatt <[email protected]>
1 parent af167cb commit fc45470

File tree

2 files changed

+61
-0
lines changed

2 files changed

+61
-0
lines changed

clang/lib/Sema/SemaFunctionEffects.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -971,6 +971,7 @@ class Analyzer {
971971
PendingFunctionAnalysis &CurrentFunction;
972972
CallableInfo &CurrentCaller;
973973
ViolationSite VSite;
974+
const Expr *TrailingRequiresClause = nullptr;
974975

975976
FunctionBodyASTVisitor(Analyzer &Outer,
976977
PendingFunctionAnalysis &CurrentFunction,
@@ -985,6 +986,9 @@ class Analyzer {
985986
if (auto *Dtor = dyn_cast<CXXDestructorDecl>(CurrentCaller.CDecl))
986987
followDestructor(dyn_cast<CXXRecordDecl>(Dtor->getParent()), Dtor);
987988

989+
if (auto *FD = dyn_cast<FunctionDecl>(CurrentCaller.CDecl))
990+
TrailingRequiresClause = FD->getTrailingRequiresClause();
991+
988992
// Do an AST traversal of the function/block body
989993
TraverseDecl(const_cast<Decl *>(CurrentCaller.CDecl));
990994
}
@@ -1259,6 +1263,17 @@ class Analyzer {
12591263
return true;
12601264
}
12611265

1266+
bool TraverseStmt(Stmt *Statement) {
1267+
// If this statement is a `requires` clause from the top-level function
1268+
// being traversed, ignore it, since it's not generating runtime code.
1269+
// We skip the traversal of lambdas (beyond their captures, see
1270+
// TraverseLambdaExpr below), so just caching this from our constructor
1271+
// should suffice.
1272+
if (Statement != TrailingRequiresClause)
1273+
return Base::TraverseStmt(Statement);
1274+
return true;
1275+
}
1276+
12621277
bool TraverseConstructorInitializer(CXXCtorInitializer *Init) {
12631278
ViolationSite PrevVS = VSite;
12641279
if (Init->isAnyMemberInitializer())
@@ -1297,6 +1312,7 @@ class Analyzer {
12971312
}
12981313

12991314
bool TraverseBlockExpr(BlockExpr * /*unused*/) {
1315+
// As with lambdas, don't traverse the block's body.
13001316
// TODO: are the capture expressions (ctor call?) safe?
13011317
return true;
13021318
}

clang/test/Sema/attr-nonblocking-constraints.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,51 @@ void nb26() [[clang::nonblocking]] {
388388
abort_wrapper(); // no diagnostic
389389
}
390390

391+
// --- Make sure we don't traverse a requires clause. ---
392+
393+
// Apparently some requires clauses are able to be collapsed into a constant before the nonblocking
394+
// analysis sees any function calls. This example (extracted from a real-world case where
395+
// `operator&&` in <valarray>, preceding the inclusion of <expected>) is sufficiently complex
396+
// to look like it contains function calls. There may be simpler examples.
397+
398+
namespace ExpectedTest {
399+
400+
template <class _Tp>
401+
inline constexpr bool is_copy_constructible_v = __is_constructible(_Tp, _Tp&);
402+
403+
template <bool, class _Tp = void>
404+
struct enable_if {};
405+
template <class _Tp>
406+
struct enable_if<true, _Tp> {
407+
typedef _Tp type;
408+
};
409+
410+
template <bool _Bp, class _Tp = void>
411+
using enable_if_t = typename enable_if<_Bp, _Tp>::type;
412+
413+
// Doesn't seem to matter whether the enable_if is true or false.
414+
template <class E1, class E2, enable_if_t<is_copy_constructible_v<E1>> = 0>
415+
inline bool operator&&(const E1& x, const E2& y);
416+
417+
template <class _Tp, class _Err>
418+
class expected {
419+
public:
420+
constexpr expected()
421+
{}
422+
423+
constexpr expected(const expected&)
424+
requires(is_copy_constructible_v<_Tp> && is_copy_constructible_v<_Err>)
425+
= default;
426+
};
427+
428+
void test() [[clang::nonblocking]]
429+
{
430+
expected<int, int> a;
431+
auto b = a;
432+
}
433+
434+
} // namespace ExpectedTest
435+
391436
// --- nonblocking implies noexcept ---
392437
#pragma clang diagnostic warning "-Wperf-constraint-implies-noexcept"
393438

0 commit comments

Comments
 (0)