mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-26 05:06:06 +00:00

- In Sema, when encountering Decls with function effects needing verification, add them to a vector, DeclsWithEffectsToVerify. - Update AST serialization to include DeclsWithEffectsToVerify. - In AnalysisBasedWarnings, use DeclsWithEffectsToVerify as a work queue, verifying functions with declared effects, and inferring (when permitted and necessary) whether their callees have effects. --------- Co-authored-by: Doug Wyatt <dwyatt@apple.com> Co-authored-by: Sirraide <aeternalmail@gmail.com> Co-authored-by: Erich Keane <ekeane@nvidia.com>
218 lines
8.3 KiB
C++
218 lines
8.3 KiB
C++
// RUN: %clang_cc1 %s -ast-dump -fblocks | FileCheck %s
|
|
|
|
// Make sure that the attribute gets parsed and attached to the correct AST elements.
|
|
|
|
#pragma clang diagnostic ignored "-Wunused-variable"
|
|
#pragma clang diagnostic ignored "-Wperf-constraint-implies-noexcept"
|
|
|
|
// =========================================================================================
|
|
// Square brackets, true
|
|
|
|
namespace square_brackets {
|
|
|
|
// 1. On the type of the FunctionDecl
|
|
void nl_function() [[clang::nonblocking]];
|
|
// CHECK: FunctionDecl {{.*}} nl_function 'void () __attribute__((nonblocking))'
|
|
|
|
// 2. On the type of the VarDecl holding a function pointer
|
|
void (*nl_func_a)() [[clang::nonblocking]];
|
|
// CHECK: VarDecl {{.*}} nl_func_a 'void (*)() __attribute__((nonblocking))'
|
|
|
|
// 3. On the type of the ParmVarDecl of a function parameter
|
|
static void nlReceiver(void (*nl_func)() [[clang::nonblocking]]);
|
|
// CHECK: ParmVarDecl {{.*}} nl_func 'void (*)() __attribute__((nonblocking))'
|
|
|
|
// 4. As an AttributedType within the nested types of a typedef
|
|
typedef void (*nl_fp_type)() [[clang::nonblocking]];
|
|
// CHECK: TypedefDecl {{.*}} nl_fp_type 'void (*)() __attribute__((nonblocking))'
|
|
using nl_fp_talias = void (*)() [[clang::nonblocking]];
|
|
// CHECK: TypeAliasDecl {{.*}} nl_fp_talias 'void (*)() __attribute__((nonblocking))'
|
|
|
|
// 5. From a typedef or typealias, on a VarDecl
|
|
nl_fp_type nl_fp_var1;
|
|
// CHECK: VarDecl {{.*}} nl_fp_var1 'nl_fp_type':'void (*)() __attribute__((nonblocking))'
|
|
nl_fp_talias nl_fp_var2;
|
|
// CHECK: VarDecl {{.*}} nl_fp_var2 'nl_fp_talias':'void (*)() __attribute__((nonblocking))'
|
|
|
|
// 6. On type of a FieldDecl
|
|
struct Struct {
|
|
void (*nl_func_field)() [[clang::nonblocking]];
|
|
// CHECK: FieldDecl {{.*}} nl_func_field 'void (*)() __attribute__((nonblocking))'
|
|
};
|
|
|
|
// nonallocating should NOT be subsumed into nonblocking
|
|
void nl1() [[clang::nonblocking]] [[clang::nonallocating]];
|
|
// CHECK: FunctionDecl {{.*}} nl1 'void () __attribute__((nonblocking)) __attribute__((nonallocating))'
|
|
|
|
void nl2() [[clang::nonallocating]] [[clang::nonblocking]];
|
|
// CHECK: FunctionDecl {{.*}} nl2 'void () __attribute__((nonblocking)) __attribute__((nonallocating))'
|
|
|
|
decltype(nl1) nl3;
|
|
// CHECK: FunctionDecl {{.*}} nl3 'decltype(nl1)':'void () __attribute__((nonblocking)) __attribute__((nonallocating))'
|
|
|
|
// Attribute propagates from base class virtual method to overrides.
|
|
struct Base {
|
|
virtual void nb_method() [[clang::nonblocking]];
|
|
};
|
|
struct Derived : public Base {
|
|
void nb_method() override;
|
|
// CHECK: CXXMethodDecl {{.*}} nb_method 'void () __attribute__((nonblocking))'
|
|
};
|
|
|
|
// Dependent expression
|
|
template <bool V>
|
|
struct Dependent {
|
|
void nb_method2() [[clang::nonblocking(V)]];
|
|
// CHECK: CXXMethodDecl {{.*}} nb_method2 'void () __attribute__((nonblocking(V)))'
|
|
};
|
|
|
|
// --- Blocks ---
|
|
|
|
// On the type of the VarDecl holding a BlockDecl
|
|
void (^nl_block1)() [[clang::nonblocking]] = ^() [[clang::nonblocking]] {};
|
|
// CHECK: VarDecl {{.*}} nl_block1 'void (^)() __attribute__((nonblocking))'
|
|
|
|
int (^nl_block2)() [[clang::nonblocking]] = ^() [[clang::nonblocking]] { return 0; };
|
|
// CHECK: VarDecl {{.*}} nl_block2 'int (^)() __attribute__((nonblocking))'
|
|
|
|
// The operand of the CallExpr is an ImplicitCastExpr of a DeclRefExpr -> nl_block which hold the attribute
|
|
static void blockCaller() { nl_block1(); }
|
|
// CHECK: DeclRefExpr {{.*}} 'nl_block1' 'void (^)() __attribute__((nonblocking))'
|
|
|
|
// --- Lambdas ---
|
|
|
|
// On the operator() of a lambda's CXXMethodDecl
|
|
auto nl_lambda = []() [[clang::nonblocking]] {};
|
|
// CHECK: CXXMethodDecl {{.*}} operator() 'void () const __attribute__((nonblocking))' inline
|
|
|
|
// =========================================================================================
|
|
// Square brackets, false
|
|
|
|
void nl_func_false() [[clang::blocking]];
|
|
// CHECK: FunctionDecl {{.*}} nl_func_false 'void () __attribute__((blocking))'
|
|
|
|
auto nl_lambda_false = []() [[clang::blocking]] {};
|
|
// CHECK: CXXMethodDecl {{.*}} operator() 'void () const __attribute__((blocking))'
|
|
|
|
} // namespace square_brackets
|
|
|
|
// =========================================================================================
|
|
// GNU-style attribute, true
|
|
|
|
namespace gnu_style {
|
|
|
|
// 1. On the type of the FunctionDecl
|
|
void nl_function() __attribute__((nonblocking));
|
|
// CHECK: FunctionDecl {{.*}} nl_function 'void () __attribute__((nonblocking))'
|
|
|
|
// 1a. Alternate placement on the FunctionDecl
|
|
__attribute__((nonblocking)) void nl_function();
|
|
// CHECK: FunctionDecl {{.*}} nl_function 'void () __attribute__((nonblocking))'
|
|
|
|
// 2. On the type of the VarDecl holding a function pointer
|
|
void (*nl_func_a)() __attribute__((nonblocking));
|
|
// CHECK: VarDecl {{.*}} nl_func_a 'void (*)() __attribute__((nonblocking))'
|
|
|
|
// 2a. Alternate attribute placement on VarDecl
|
|
__attribute__((nonblocking)) void (*nl_func_b)();
|
|
// CHECK: VarDecl {{.*}} nl_func_b 'void (*)() __attribute__((nonblocking))'
|
|
|
|
// 3. On the type of the ParmVarDecl of a function parameter
|
|
static void nlReceiver(void (*nl_func)() __attribute__((nonblocking)));
|
|
// CHECK: ParmVarDecl {{.*}} nl_func 'void (*)() __attribute__((nonblocking))'
|
|
|
|
// 4. As an AttributedType within the nested types of a typedef
|
|
// Note different placement from square brackets for the typealias.
|
|
typedef void (*nl_fp_type)() __attribute__((nonblocking));
|
|
// CHECK: TypedefDecl {{.*}} nl_fp_type 'void (*)() __attribute__((nonblocking))'
|
|
using nl_fp_talias = __attribute__((nonblocking)) void (*)();
|
|
// CHECK: TypeAliasDecl {{.*}} nl_fp_talias 'void (*)() __attribute__((nonblocking))'
|
|
|
|
// 5. From a typedef or typealias, on a VarDecl
|
|
nl_fp_type nl_fp_var1;
|
|
// CHECK: VarDecl {{.*}} nl_fp_var1 'nl_fp_type':'void (*)() __attribute__((nonblocking))'
|
|
nl_fp_talias nl_fp_var2;
|
|
// CHECK: VarDecl {{.*}} nl_fp_var2 'nl_fp_talias':'void (*)() __attribute__((nonblocking))'
|
|
|
|
// 6. On type of a FieldDecl
|
|
struct Struct {
|
|
void (*nl_func_field)() __attribute__((nonblocking));
|
|
// CHECK: FieldDecl {{.*}} nl_func_field 'void (*)() __attribute__((nonblocking))'
|
|
};
|
|
|
|
} // namespace gnu_style
|
|
|
|
// =========================================================================================
|
|
// nonallocating and allocating - quick checks because the code paths are generally
|
|
// identical after parsing.
|
|
|
|
void na_function() [[clang::nonallocating]];
|
|
// CHECK: FunctionDecl {{.*}} na_function 'void () __attribute__((nonallocating))'
|
|
|
|
void na_true_function() [[clang::nonallocating(true)]];
|
|
// CHECK: FunctionDecl {{.*}} na_true_function 'void () __attribute__((nonallocating))'
|
|
|
|
void na_false_function() [[clang::nonallocating(false)]];
|
|
// CHECK: FunctionDecl {{.*}} na_false_function 'void () __attribute__((allocating))'
|
|
|
|
void alloc_function() [[clang::allocating]];
|
|
// CHECK: FunctionDecl {{.*}} alloc_function 'void () __attribute__((allocating))'
|
|
|
|
|
|
// =========================================================================================
|
|
// Non-blocking with an expression parameter
|
|
|
|
void t0() [[clang::nonblocking(1 - 1)]];
|
|
// CHECK: FunctionDecl {{.*}} t0 'void () __attribute__((blocking))'
|
|
void t1() [[clang::nonblocking(1 + 1)]];
|
|
// CHECK: FunctionDecl {{.*}} t1 'void () __attribute__((nonblocking))'
|
|
|
|
template <bool V>
|
|
struct ValueDependent {
|
|
void nb_method() [[clang::nonblocking(V)]];
|
|
};
|
|
|
|
void t3() [[clang::nonblocking]]
|
|
{
|
|
ValueDependent<false> x1;
|
|
x1.nb_method();
|
|
// CHECK: ClassTemplateSpecializationDecl {{.*}} ValueDependent
|
|
// CHECK: TemplateArgument integral 'false'
|
|
// CHECK: CXXMethodDecl {{.*}} nb_method 'void () __attribute__((blocking))'
|
|
|
|
ValueDependent<true> x2;
|
|
x2.nb_method();
|
|
// CHECK: ClassTemplateSpecializationDecl {{.*}} ValueDependent
|
|
// CHECK: TemplateArgument integral 'true'
|
|
// CHECK: CXXMethodDecl {{.*}} nb_method 'void () __attribute__((nonblocking))'
|
|
}
|
|
|
|
template <typename X>
|
|
struct TypeDependent {
|
|
void td_method() [[clang::nonblocking(X::is_nb)]];
|
|
};
|
|
|
|
struct NBPolicyTrue {
|
|
static constexpr bool is_nb = true;
|
|
};
|
|
|
|
struct NBPolicyFalse {
|
|
static constexpr bool is_nb = false;
|
|
};
|
|
|
|
void t4()
|
|
{
|
|
TypeDependent<NBPolicyFalse> x1;
|
|
x1.td_method();
|
|
// CHECK: ClassTemplateSpecializationDecl {{.*}} TypeDependent
|
|
// CHECK: TemplateArgument type 'NBPolicyFalse'
|
|
// CHECK: CXXMethodDecl {{.*}} td_method 'void () __attribute__((blocking))'
|
|
|
|
TypeDependent<NBPolicyTrue> x2;
|
|
x2.td_method();
|
|
// CHECK: ClassTemplateSpecializationDecl {{.*}} TypeDependent
|
|
// CHECK: TemplateArgument type 'NBPolicyTrue'
|
|
// CHECK: CXXMethodDecl {{.*}} td_method 'void () __attribute__((nonblocking))'
|
|
}
|
|
|