
This is extended to all `std::` functions that take a reference to a value and return a reference (or pointer) to that same value: `move`, `forward`, `move_if_noexcept`, `as_const`, `addressof`, and the libstdc++-specific function `__addressof`. We still require these functions to be declared before they can be used, but don't instantiate their definitions unless their addresses are taken. Instead, code generation, constant evaluation, and static analysis are given direct knowledge of their effect. This change aims to reduce various costs associated with these functions -- per-instantiation memory costs, compile time and memory costs due to creating out-of-line copies and inlining them, code size at -O0, and so on -- so that they are not substantially more expensive than a cast. Most of these improvements are very small, but I measured a 3% decrease in -O0 object file size for a simple C++ source file using the standard library after this change. We now automatically infer the `const` and `nothrow` attributes on these now-builtin functions, in particular meaning that we get a warning for an unused call to one of these functions. In C++20 onwards, we disallow taking the addresses of these functions, per the C++20 "addressable function" rule. In earlier language modes, a compatibility warning is produced but the address can still be taken. The same infrastructure is extended to the existing MSVC builtin `__GetExceptionInfo`, which is now only recognized in namespace `std` like it always should have been. This is a re-commit of fc3090109643af8d2da9822d0f99c84742b9c877, a571f82a50416b767fd3cce0fb5027bb5dfec58c, 64c045e25b8471bbb572bd29159c294a82a86a2, and de6ddaeef3aaa8a9ae3663c12cdb57d9afc0f906, and reverts aa643f455a5362de7189eac630050d2c8aefe8f2. This change also includes a workaround for users using libc++ 3.1 and earlier (!!), as apparently happens on AIX, where std::move sometimes returns by value. Reviewed By: aaron.ballman Differential Revision: https://reviews.llvm.org/D123345 Revert "Fixup D123950 to address revert of D123345" This reverts commit aa643f455a5362de7189eac630050d2c8aefe8f2.
980 lines
30 KiB
C++
980 lines
30 KiB
C++
// RUN: %clang_cc1 -fsyntax-only -verify -Wconsumed -fcxx-exceptions -std=c++11 %s
|
|
|
|
// TODO: Switch to using macros for the expected warnings.
|
|
|
|
#define CALLABLE_WHEN(...) __attribute__ ((callable_when(__VA_ARGS__)))
|
|
#define CONSUMABLE(state) __attribute__ ((consumable(state)))
|
|
#define PARAM_TYPESTATE(state) __attribute__ ((param_typestate(state)))
|
|
#define RETURN_TYPESTATE(state) __attribute__ ((return_typestate(state)))
|
|
#define SET_TYPESTATE(state) __attribute__ ((set_typestate(state)))
|
|
#define TEST_TYPESTATE(state) __attribute__ ((test_typestate(state)))
|
|
|
|
typedef decltype(nullptr) nullptr_t;
|
|
|
|
template <typename T>
|
|
class CONSUMABLE(unconsumed) ConsumableClass {
|
|
T var;
|
|
|
|
public:
|
|
ConsumableClass();
|
|
ConsumableClass(nullptr_t p) RETURN_TYPESTATE(consumed);
|
|
ConsumableClass(T val) RETURN_TYPESTATE(unconsumed);
|
|
ConsumableClass(ConsumableClass<T> &other);
|
|
ConsumableClass(ConsumableClass<T> &&other);
|
|
|
|
ConsumableClass<T>& operator=(ConsumableClass<T> &other);
|
|
ConsumableClass<T>& operator=(ConsumableClass<T> &&other);
|
|
ConsumableClass<T>& operator=(nullptr_t) SET_TYPESTATE(consumed);
|
|
|
|
template <typename U>
|
|
ConsumableClass<T>& operator=(ConsumableClass<U> &other);
|
|
|
|
template <typename U>
|
|
ConsumableClass<T>& operator=(ConsumableClass<U> &&other);
|
|
|
|
void operator()(int a) SET_TYPESTATE(consumed);
|
|
void operator*() const CALLABLE_WHEN("unconsumed");
|
|
void unconsumedCall() const CALLABLE_WHEN("unconsumed");
|
|
void callableWhenUnknown() const CALLABLE_WHEN("unconsumed", "unknown");
|
|
|
|
bool isValid() const TEST_TYPESTATE(unconsumed);
|
|
operator bool() const TEST_TYPESTATE(unconsumed);
|
|
bool operator!=(nullptr_t) const TEST_TYPESTATE(unconsumed);
|
|
bool operator==(nullptr_t) const TEST_TYPESTATE(consumed);
|
|
|
|
void constCall() const;
|
|
void nonconstCall();
|
|
|
|
void consume() SET_TYPESTATE(consumed);
|
|
void unconsume() SET_TYPESTATE(unconsumed);
|
|
};
|
|
|
|
class CONSUMABLE(unconsumed) DestructorTester {
|
|
public:
|
|
DestructorTester();
|
|
DestructorTester(int);
|
|
DestructorTester(nullptr_t) RETURN_TYPESTATE(unconsumed);
|
|
DestructorTester(DestructorTester &&);
|
|
|
|
void operator*() CALLABLE_WHEN("unconsumed");
|
|
|
|
~DestructorTester() CALLABLE_WHEN("consumed");
|
|
|
|
};
|
|
|
|
void dtByVal(DestructorTester);
|
|
void dtByValMarkUnconsumed(DestructorTester RETURN_TYPESTATE(unconsumed));
|
|
|
|
void baf0(const ConsumableClass<int> var);
|
|
void baf1(const ConsumableClass<int> &var);
|
|
void baf2(const ConsumableClass<int> *var);
|
|
|
|
void baf3(ConsumableClass<int> var);
|
|
void baf4(ConsumableClass<int> &var);
|
|
void baf5(ConsumableClass<int> *var);
|
|
void baf6(ConsumableClass<int> &&var);
|
|
|
|
ConsumableClass<int> returnsUnconsumed() {
|
|
return ConsumableClass<int>(); // expected-warning {{return value not in expected state; expected 'unconsumed', observed 'consumed'}}
|
|
}
|
|
|
|
ConsumableClass<int> returnsConsumed() RETURN_TYPESTATE(consumed);
|
|
ConsumableClass<int> returnsConsumed() {
|
|
return ConsumableClass<int>();
|
|
}
|
|
|
|
ConsumableClass<int> returnsUnknown() RETURN_TYPESTATE(unknown);
|
|
|
|
void testInitialization() {
|
|
ConsumableClass<int> var0;
|
|
ConsumableClass<int> var1 = ConsumableClass<int>();
|
|
ConsumableClass<int> var2(42);
|
|
ConsumableClass<int> var3(var2); // copy constructor
|
|
ConsumableClass<int> var4(var0); // copy consumed value
|
|
|
|
*var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
|
|
*var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
|
|
*var2;
|
|
*var3;
|
|
*var4; // expected-warning {{invalid invocation of method 'operator*' on object 'var4' while it is in the 'consumed' state}}
|
|
|
|
var0 = ConsumableClass<int>(42);
|
|
*var0;
|
|
|
|
var0 = var1;
|
|
*var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
|
|
|
|
if (var0.isValid()) {
|
|
*var0;
|
|
*var1;
|
|
|
|
} else {
|
|
*var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
|
|
}
|
|
}
|
|
|
|
void testDestruction() {
|
|
DestructorTester D0(42), D1(42), D2;
|
|
|
|
*D0;
|
|
*D1;
|
|
*D2; // expected-warning {{invalid invocation of method 'operator*' on object 'D2' while it is in the 'consumed' state}}
|
|
|
|
D0.~DestructorTester(); // expected-warning {{invalid invocation of method '~DestructorTester' on object 'D0' while it is in the 'unconsumed' state}}
|
|
|
|
return; // expected-warning {{invalid invocation of method '~DestructorTester' on object 'D0' while it is in the 'unconsumed' state}} \
|
|
expected-warning {{invalid invocation of method '~DestructorTester' on object 'D1' while it is in the 'unconsumed' state}}
|
|
}
|
|
|
|
void testDestructionByVal() {
|
|
{
|
|
// both the var and the temporary are consumed:
|
|
DestructorTester D0(nullptr);
|
|
dtByVal((DestructorTester &&)D0);
|
|
}
|
|
{
|
|
// the var is consumed but the temporary isn't:
|
|
DestructorTester D1(nullptr);
|
|
dtByValMarkUnconsumed((DestructorTester &&)D1); // expected-warning {{invalid invocation of method '~DestructorTester' on a temporary object while it is in the 'unconsumed' state}}
|
|
}
|
|
}
|
|
|
|
void testTempValue() {
|
|
*ConsumableClass<int>(); // expected-warning {{invalid invocation of method 'operator*' on a temporary object while it is in the 'consumed' state}}
|
|
}
|
|
|
|
void testSimpleRValueRefs() {
|
|
ConsumableClass<int> var0;
|
|
ConsumableClass<int> var1(42);
|
|
|
|
*var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
|
|
*var1;
|
|
|
|
var0 = static_cast<ConsumableClass<int>&&>(var1);
|
|
|
|
*var0;
|
|
*var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
|
|
}
|
|
|
|
void testIfStmt() {
|
|
ConsumableClass<int> var;
|
|
|
|
if (var.isValid()) {
|
|
*var;
|
|
} else {
|
|
*var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
|
|
}
|
|
|
|
if (!var.isValid()) {
|
|
*var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
|
|
} else {
|
|
*var;
|
|
}
|
|
|
|
if (var) {
|
|
// Empty
|
|
} else {
|
|
*var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
|
|
}
|
|
|
|
if (var != nullptr) {
|
|
// Empty
|
|
} else {
|
|
*var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
|
|
}
|
|
|
|
if (var == nullptr) {
|
|
*var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
|
|
} else {
|
|
// Empty
|
|
}
|
|
}
|
|
|
|
void testComplexConditionals0() {
|
|
ConsumableClass<int> var0, var1, var2;
|
|
|
|
if (var0 && var1) {
|
|
*var0;
|
|
*var1;
|
|
|
|
} else {
|
|
*var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
|
|
*var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
|
|
}
|
|
|
|
if (var0 || var1) {
|
|
*var0;
|
|
*var1;
|
|
|
|
} else {
|
|
*var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
|
|
*var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
|
|
}
|
|
|
|
if (var0 && !var1) {
|
|
*var0;
|
|
*var1;
|
|
|
|
} else {
|
|
*var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
|
|
*var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
|
|
}
|
|
|
|
if (var0 || !var1) {
|
|
*var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
|
|
*var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
|
|
|
|
} else {
|
|
*var0;
|
|
*var1;
|
|
}
|
|
|
|
if (!var0 && !var1) {
|
|
*var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
|
|
*var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
|
|
|
|
} else {
|
|
*var0;
|
|
*var1;
|
|
}
|
|
|
|
if (!var0 || !var1) {
|
|
*var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
|
|
*var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
|
|
|
|
} else {
|
|
*var0;
|
|
*var1;
|
|
}
|
|
|
|
if (!(var0 && var1)) {
|
|
*var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
|
|
*var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
|
|
|
|
} else {
|
|
*var0;
|
|
*var1;
|
|
}
|
|
|
|
if (!(var0 || var1)) {
|
|
*var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
|
|
*var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
|
|
|
|
} else {
|
|
*var0;
|
|
*var1;
|
|
}
|
|
|
|
if (var0 && var1 && var2) {
|
|
*var0;
|
|
*var1;
|
|
*var2;
|
|
|
|
} else {
|
|
*var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
|
|
*var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
|
|
*var2; // expected-warning {{invalid invocation of method 'operator*' on object 'var2' while it is in the 'consumed' state}}
|
|
}
|
|
|
|
#if 0
|
|
// FIXME: Get this test to pass.
|
|
if (var0 || var1 || var2) {
|
|
*var0;
|
|
*var1;
|
|
*var2;
|
|
|
|
} else {
|
|
*var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
|
|
*var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
|
|
*var2; // expected-warning {{invalid invocation of method 'operator*' on object 'var2' while it is in the 'consumed' state}}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void testComplexConditionals1() {
|
|
ConsumableClass<int> var0, var1, var2;
|
|
|
|
// Coerce all variables into the unknown state.
|
|
baf4(var0);
|
|
baf4(var1);
|
|
baf4(var2);
|
|
|
|
if (var0 && var1) {
|
|
*var0;
|
|
*var1;
|
|
|
|
} else {
|
|
*var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}}
|
|
*var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}}
|
|
}
|
|
|
|
if (var0 || var1) {
|
|
*var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}}
|
|
*var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}}
|
|
|
|
} else {
|
|
*var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
|
|
*var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
|
|
}
|
|
|
|
if (var0 && !var1) {
|
|
*var0;
|
|
*var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
|
|
|
|
} else {
|
|
*var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}}
|
|
*var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}}
|
|
}
|
|
|
|
if (var0 || !var1) {
|
|
*var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}}
|
|
*var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}}
|
|
|
|
} else {
|
|
*var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
|
|
*var1;
|
|
}
|
|
|
|
if (!var0 && !var1) {
|
|
*var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
|
|
*var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
|
|
|
|
} else {
|
|
*var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}}
|
|
*var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}}
|
|
}
|
|
|
|
if (!(var0 || var1)) {
|
|
*var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
|
|
*var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
|
|
|
|
} else {
|
|
*var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}}
|
|
*var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}}
|
|
}
|
|
|
|
if (!var0 || !var1) {
|
|
*var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}}
|
|
*var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}}
|
|
|
|
} else {
|
|
*var0;
|
|
*var1;
|
|
}
|
|
|
|
if (!(var0 && var1)) {
|
|
*var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}}
|
|
*var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}}
|
|
|
|
} else {
|
|
*var0;
|
|
*var1;
|
|
}
|
|
|
|
if (var0 && var1 && var2) {
|
|
*var0;
|
|
*var1;
|
|
*var2;
|
|
|
|
} else {
|
|
*var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}}
|
|
*var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}}
|
|
*var2; // expected-warning {{invalid invocation of method 'operator*' on object 'var2' while it is in the 'unknown' state}}
|
|
}
|
|
|
|
#if 0
|
|
// FIXME: Get this test to pass.
|
|
if (var0 || var1 || var2) {
|
|
*var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}}
|
|
*var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}}
|
|
*var2; // expected-warning {{invalid invocation of method 'operator*' on object 'var2' while it is in the 'unknown' state}}
|
|
|
|
} else {
|
|
*var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
|
|
*var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
|
|
*var2; // expected-warning {{invalid invocation of method 'operator*' on object 'var2' while it is in the 'consumed' state}}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void testStateChangeInBranch() {
|
|
ConsumableClass<int> var;
|
|
|
|
// Make var enter the 'unknown' state.
|
|
baf4(var);
|
|
|
|
if (!var) {
|
|
var = ConsumableClass<int>(42);
|
|
}
|
|
|
|
*var;
|
|
}
|
|
|
|
void testFunctionParam(ConsumableClass<int> param) {
|
|
|
|
if (param.isValid()) {
|
|
*param;
|
|
} else {
|
|
*param;
|
|
}
|
|
|
|
param = nullptr;
|
|
*param; // expected-warning {{invocation of method 'operator*' on object 'param' while it is in the 'consumed' state}}
|
|
}
|
|
|
|
void testParamReturnTypestateCallee(bool cond, ConsumableClass<int> &Param RETURN_TYPESTATE(unconsumed)) { // expected-warning {{parameter 'Param' not in expected state when the function returns: expected 'unconsumed', observed 'consumed'}}
|
|
|
|
if (cond) {
|
|
Param.consume();
|
|
return; // expected-warning {{parameter 'Param' not in expected state when the function returns: expected 'unconsumed', observed 'consumed'}}
|
|
}
|
|
|
|
Param.consume();
|
|
}
|
|
|
|
void testRvalueRefParamReturnTypestateCallee(ConsumableClass<int> &&Param RETURN_TYPESTATE(unconsumed)) {
|
|
Param.unconsume();
|
|
}
|
|
|
|
void testParamReturnTypestateCaller() {
|
|
ConsumableClass<int> var;
|
|
|
|
testParamReturnTypestateCallee(true, var);
|
|
testRvalueRefParamReturnTypestateCallee((ConsumableClass<int> &&)var);
|
|
|
|
*var;
|
|
}
|
|
|
|
void testParamTypestateCallee(ConsumableClass<int> Param0 PARAM_TYPESTATE(consumed),
|
|
ConsumableClass<int> &Param1 PARAM_TYPESTATE(consumed)) {
|
|
|
|
*Param0; // expected-warning {{invalid invocation of method 'operator*' on object 'Param0' while it is in the 'consumed' state}}
|
|
*Param1; // expected-warning {{invalid invocation of method 'operator*' on object 'Param1' while it is in the 'consumed' state}}
|
|
}
|
|
|
|
void testParamTypestateCaller() {
|
|
ConsumableClass<int> Var0, Var1(42);
|
|
|
|
testParamTypestateCallee(Var0, Var1); // expected-warning {{argument not in expected state; expected 'consumed', observed 'unconsumed'}}
|
|
}
|
|
|
|
|
|
void consumeFunc(ConsumableClass<int> P PARAM_TYPESTATE(unconsumed));
|
|
struct ParamTest {
|
|
static void consumeFuncStatic(ConsumableClass<int> P PARAM_TYPESTATE(unconsumed));
|
|
void consumeFuncMeth(ConsumableClass<int> P PARAM_TYPESTATE(unconsumed));
|
|
void operator<<(ConsumableClass<int> P PARAM_TYPESTATE(unconsumed));
|
|
};
|
|
|
|
void operator>>(ParamTest& pt, ConsumableClass<int> P PARAM_TYPESTATE(unconsumed));
|
|
|
|
|
|
void testFunctionParams() {
|
|
// Make sure we handle the different kinds of functions.
|
|
ConsumableClass<int> P;
|
|
|
|
consumeFunc(P); // expected-warning {{argument not in expected state; expected 'unconsumed', observed 'consumed'}}
|
|
ParamTest::consumeFuncStatic(P); // expected-warning {{argument not in expected state; expected 'unconsumed', observed 'consumed'}}
|
|
ParamTest pt;
|
|
pt.consumeFuncMeth(P); // expected-warning {{argument not in expected state; expected 'unconsumed', observed 'consumed'}}
|
|
pt << P; // expected-warning {{argument not in expected state; expected 'unconsumed', observed 'consumed'}}
|
|
pt >> P; // expected-warning {{argument not in expected state; expected 'unconsumed', observed 'consumed'}}
|
|
}
|
|
|
|
void baf3(ConsumableClass<int> var) {
|
|
*var;
|
|
}
|
|
|
|
void baf4(ConsumableClass<int> &var) {
|
|
*var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'unknown' state}}
|
|
}
|
|
|
|
void baf6(ConsumableClass<int> &&var) {
|
|
*var;
|
|
}
|
|
|
|
void testCallingConventions() {
|
|
ConsumableClass<int> var(42);
|
|
|
|
baf0(var);
|
|
*var;
|
|
|
|
baf1(var);
|
|
*var;
|
|
|
|
baf2(&var);
|
|
*var;
|
|
|
|
baf3(var);
|
|
*var;
|
|
|
|
baf4(var);
|
|
*var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'unknown' state}}
|
|
|
|
var = ConsumableClass<int>(42);
|
|
baf5(&var);
|
|
*var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'unknown' state}}
|
|
|
|
var = ConsumableClass<int>(42);
|
|
baf6(static_cast<ConsumableClass<int>&&>(var));
|
|
*var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
|
|
}
|
|
|
|
void testConstAndNonConstMemberFunctions() {
|
|
ConsumableClass<int> var(42);
|
|
|
|
var.constCall();
|
|
*var;
|
|
|
|
var.nonconstCall();
|
|
*var;
|
|
}
|
|
|
|
void testFunctionParam0(ConsumableClass<int> param) {
|
|
*param;
|
|
}
|
|
|
|
void testFunctionParam1(ConsumableClass<int> ¶m) {
|
|
*param; // expected-warning {{invalid invocation of method 'operator*' on object 'param' while it is in the 'unknown' state}}
|
|
}
|
|
|
|
void testReturnStates() {
|
|
ConsumableClass<int> var;
|
|
|
|
var = returnsUnconsumed();
|
|
*var;
|
|
|
|
var = returnsConsumed();
|
|
*var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
|
|
}
|
|
|
|
void testCallableWhen() {
|
|
ConsumableClass<int> var(42);
|
|
|
|
*var;
|
|
|
|
baf4(var);
|
|
|
|
var.callableWhenUnknown();
|
|
}
|
|
|
|
void testMoveAsignmentish() {
|
|
ConsumableClass<int> var0;
|
|
ConsumableClass<long> var1(42);
|
|
|
|
*var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
|
|
*var1;
|
|
|
|
var0 = static_cast<ConsumableClass<long>&&>(var1);
|
|
|
|
*var0;
|
|
*var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
|
|
|
|
var1 = ConsumableClass<long>(42);
|
|
var1 = nullptr;
|
|
*var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
|
|
}
|
|
|
|
void testConditionalMerge() {
|
|
ConsumableClass<int> var;
|
|
|
|
if (var.isValid()) {
|
|
// Empty
|
|
}
|
|
|
|
*var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
|
|
|
|
if (var.isValid()) {
|
|
// Empty
|
|
} else {
|
|
// Empty
|
|
}
|
|
|
|
*var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
|
|
}
|
|
|
|
void testSetTypestate() {
|
|
ConsumableClass<int> var(42);
|
|
|
|
*var;
|
|
|
|
var.consume();
|
|
|
|
*var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
|
|
|
|
var.unconsume();
|
|
|
|
*var;
|
|
}
|
|
|
|
void testConsumes0() {
|
|
ConsumableClass<int> var(nullptr);
|
|
|
|
*var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
|
|
}
|
|
|
|
void testConsumes1() {
|
|
ConsumableClass<int> var(42);
|
|
|
|
var.unconsumedCall();
|
|
var(6);
|
|
|
|
var.unconsumedCall(); // expected-warning {{invalid invocation of method 'unconsumedCall' on object 'var' while it is in the 'consumed' state}}
|
|
}
|
|
|
|
void testUnreachableBlock() {
|
|
ConsumableClass<int> var(42);
|
|
|
|
if (var) {
|
|
*var;
|
|
} else {
|
|
*var;
|
|
}
|
|
|
|
*var;
|
|
}
|
|
|
|
|
|
void testForLoop1() {
|
|
ConsumableClass<int> var0, var1(42);
|
|
|
|
for (int i = 0; i < 10; ++i) { // expected-warning {{state of variable 'var1' must match at the entry and exit of loop}}
|
|
*var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
|
|
|
|
*var1;
|
|
var1.consume();
|
|
*var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
|
|
}
|
|
|
|
*var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
|
|
}
|
|
|
|
void testWhileLoop1() {
|
|
int i = 10;
|
|
|
|
ConsumableClass<int> var0, var1(42);
|
|
|
|
while (i-- > 0) { // expected-warning {{state of variable 'var1' must match at the entry and exit of loop}}
|
|
*var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
|
|
|
|
*var1;
|
|
var1.consume();
|
|
*var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
|
|
}
|
|
|
|
*var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
|
|
}
|
|
|
|
// Tests if state information is correctly discarded for certain shapes of CFGs.
|
|
void testSwitchGOTO(void) {
|
|
int a;
|
|
|
|
LABEL0:
|
|
switch (a)
|
|
case 0:
|
|
goto LABEL0;
|
|
|
|
goto LABEL0;
|
|
}
|
|
|
|
typedef const int*& IntegerPointerReference;
|
|
void testIsRValueRefishAndCanonicalType(IntegerPointerReference a) {}
|
|
|
|
namespace ContinueICETest {
|
|
|
|
bool cond1();
|
|
bool cond2();
|
|
|
|
static void foo1() {
|
|
while (cond1()) {
|
|
if (cond2())
|
|
continue;
|
|
}
|
|
}
|
|
|
|
static void foo2() {
|
|
while (true) {
|
|
if (false)
|
|
continue;
|
|
}
|
|
}
|
|
|
|
class runtime_error
|
|
{
|
|
public:
|
|
virtual ~runtime_error();
|
|
};
|
|
|
|
void read(bool sf) {
|
|
while (sf) {
|
|
if(sf) throw runtime_error();
|
|
}
|
|
}
|
|
|
|
} // end namespace ContinueICETest
|
|
|
|
|
|
namespace StatusUseCaseTests {
|
|
|
|
class CONSUMABLE(unconsumed)
|
|
__attribute__((consumable_auto_cast_state))
|
|
__attribute__((consumable_set_state_on_read))
|
|
Status {
|
|
int code;
|
|
|
|
public:
|
|
static Status OK;
|
|
|
|
Status() RETURN_TYPESTATE(consumed);
|
|
Status(int c) RETURN_TYPESTATE(unconsumed);
|
|
|
|
Status(const Status &other);
|
|
Status(Status &&other);
|
|
|
|
Status& operator=(const Status &other) CALLABLE_WHEN("unknown", "consumed");
|
|
Status& operator=(Status &&other) CALLABLE_WHEN("unknown", "consumed");
|
|
|
|
bool operator==(const Status &other) const SET_TYPESTATE(consumed);
|
|
|
|
bool check() const SET_TYPESTATE(consumed);
|
|
void ignore() const SET_TYPESTATE(consumed);
|
|
// Status& markAsChecked() { return *this; }
|
|
|
|
void clear() CALLABLE_WHEN("unknown", "consumed") SET_TYPESTATE(consumed);
|
|
|
|
~Status() CALLABLE_WHEN("unknown", "consumed");
|
|
|
|
operator bool() const; // Will not consume the object.
|
|
};
|
|
|
|
|
|
bool cond();
|
|
Status doSomething();
|
|
void handleStatus(const Status& s RETURN_TYPESTATE(consumed));
|
|
void handleStatusRef(Status& s);
|
|
void handleStatusPtr(Status* s);
|
|
void handleStatusUnmarked(const Status& s);
|
|
|
|
void log(const char* msg);
|
|
void fail() __attribute__((noreturn));
|
|
void checkStat(const Status& s);
|
|
|
|
|
|
void testSimpleTemporaries0() {
|
|
doSomething(); // expected-warning {{invalid invocation of method '~Status' on a temporary object while it is in the 'unconsumed' state}}
|
|
}
|
|
|
|
void testSimpleTemporaries1() {
|
|
doSomething().ignore();
|
|
}
|
|
|
|
void testSimpleTemporaries2() {
|
|
handleStatus(doSomething());
|
|
}
|
|
|
|
void testSimpleTemporaries3() {
|
|
Status s = doSomething();
|
|
} // expected-warning {{invalid invocation of method '~Status' on object 's' while it is in the 'unconsumed' state}}
|
|
|
|
void testTemporariesWithControlFlow(bool a) {
|
|
bool b = false || doSomething(); // expected-warning {{invalid invocation of method '~Status' on a temporary object while it is in the 'unconsumed' state}}
|
|
}
|
|
|
|
Status testSimpleTemporariesReturn0() {
|
|
return doSomething();
|
|
}
|
|
|
|
Status testSimpleTemporariesReturn1() {
|
|
Status s = doSomething();
|
|
return s;
|
|
}
|
|
|
|
void testSimpleTemporaries4() {
|
|
Status s = doSomething();
|
|
s.check();
|
|
}
|
|
|
|
void testSimpleTemporaries5() {
|
|
Status s = doSomething();
|
|
s.clear(); // expected-warning {{invalid invocation of method 'clear' on object 's' while it is in the 'unconsumed' state}}
|
|
}
|
|
|
|
void testSimpleTemporaries6() {
|
|
Status s1 = doSomething();
|
|
handleStatus(s1);
|
|
|
|
Status s2 = doSomething();
|
|
handleStatusRef(s2);
|
|
|
|
Status s3 = doSomething();
|
|
handleStatusPtr(&s3);
|
|
|
|
Status s4 = doSomething();
|
|
handleStatusUnmarked(s4);
|
|
}
|
|
|
|
void testSimpleTemporaries7() {
|
|
Status s;
|
|
s = doSomething();
|
|
} // expected-warning {{invalid invocation of method '~Status' on object 's' while it is in the 'unconsumed' state}}
|
|
|
|
void testTemporariesWithConditionals0() {
|
|
int a;
|
|
|
|
Status s = doSomething();
|
|
if (cond()) a = 0;
|
|
else a = 1;
|
|
} // expected-warning {{invalid invocation of method '~Status' on object 's' while it is in the 'unconsumed' state}}
|
|
|
|
void testTemporariesWithConditionals1() {
|
|
int a;
|
|
|
|
Status s = doSomething();
|
|
if (cond()) a = 0;
|
|
else a = 1;
|
|
s.ignore();
|
|
}
|
|
|
|
void testTemporariesWithConditionals2() {
|
|
int a;
|
|
|
|
Status s = doSomething();
|
|
s.ignore();
|
|
if (cond()) a = 0;
|
|
else a = 1;
|
|
}
|
|
|
|
void testTemporariesWithConditionals3() {
|
|
Status s = doSomething();
|
|
if (cond()) {
|
|
s.check();
|
|
}
|
|
}
|
|
|
|
void testTemporariesAndConstructors0() {
|
|
Status s(doSomething()); // Test the copy constructor.
|
|
s.check();
|
|
}
|
|
|
|
void testTemporariesAndConstructors1F() {
|
|
Status s1 = doSomething(); // Test the copy constructor.
|
|
Status s2 = s1;
|
|
} // expected-warning {{invalid invocation of method '~Status' on object 's2' while it is in the 'unconsumed' state}}
|
|
|
|
void testTemporariesAndConstructors1S() {
|
|
Status s1 = doSomething(); // Test the copy constructor.
|
|
Status s2(s1);
|
|
s2.check();
|
|
}
|
|
|
|
void testTemporariesAndConstructors2F() {
|
|
// Test the move constructor.
|
|
Status s1 = doSomething();
|
|
Status s2 = static_cast<Status&&>(s1);
|
|
} // expected-warning {{invalid invocation of method '~Status' on object 's2' while it is in the 'unconsumed' state}}
|
|
|
|
void testTemporariesAndConstructors2S() {
|
|
// Test the move constructor.
|
|
Status s1 = doSomething();
|
|
Status s2 = static_cast<Status&&>(s1);
|
|
s2.check();
|
|
}
|
|
|
|
void testTemporariesAndOperators0F() {
|
|
// Test the assignment operator.
|
|
Status s1 = doSomething();
|
|
Status s2;
|
|
s2 = s1;
|
|
} // expected-warning {{invalid invocation of method '~Status' on object 's2' while it is in the 'unconsumed' state}}
|
|
|
|
void testTemporariesAndOperators0S() {
|
|
// Test the assignment operator.
|
|
Status s1 = doSomething();
|
|
Status s2;
|
|
s2 = s1;
|
|
s2.check();
|
|
}
|
|
|
|
void testTemporariesAndOperators1F() {
|
|
// Test the move assignment operator.
|
|
Status s1 = doSomething();
|
|
Status s2;
|
|
s2 = static_cast<Status&&>(s1);
|
|
} // expected-warning {{invalid invocation of method '~Status' on object 's2' while it is in the 'unconsumed' state}}
|
|
|
|
void testTemporariesAndOperators1S() {
|
|
// Test the move assignment operator.
|
|
Status s1 = doSomething();
|
|
Status s2;
|
|
s2 = static_cast<Status&&>(s1);
|
|
s2.check();
|
|
}
|
|
|
|
void testTemporariesAndOperators2() {
|
|
Status s1 = doSomething();
|
|
Status s2 = doSomething();
|
|
s1 = s2; // expected-warning {{invalid invocation of method 'operator=' on object 's1' while it is in the 'unconsumed' state}}
|
|
s1.check();
|
|
s2.check();
|
|
}
|
|
|
|
Status testReturnAutocast() {
|
|
Status s = doSomething();
|
|
s.check(); // consume s
|
|
return s; // should autocast back to unconsumed
|
|
}
|
|
|
|
|
|
namespace TestParens {
|
|
|
|
void test3() {
|
|
checkStat((doSomething()));
|
|
}
|
|
|
|
void test4() {
|
|
Status s = (doSomething());
|
|
s.check();
|
|
}
|
|
|
|
void test5() {
|
|
(doSomething()).check();
|
|
}
|
|
|
|
void test6() {
|
|
if ((doSomething()) == Status::OK)
|
|
return;
|
|
}
|
|
|
|
} // end namespace TestParens
|
|
|
|
} // end namespace InitializerAssertionFailTest
|
|
|
|
|
|
namespace std {
|
|
void move();
|
|
template<class T>
|
|
T &&move(T&);
|
|
|
|
namespace __1 {
|
|
void move();
|
|
template<class T>
|
|
T &&move(T&);
|
|
}
|
|
}
|
|
|
|
namespace PR18260 {
|
|
class X {
|
|
public:
|
|
void move();
|
|
} x;
|
|
|
|
void test() {
|
|
x.move();
|
|
std::move();
|
|
std::move(x); // expected-warning {{ignoring return value}}
|
|
std::__1::move();
|
|
std::__1::move(x);
|
|
}
|
|
} // end namespace PR18260
|
|
|