[-Wcompletion-handler] Fix a non-termination issue (#78380)

The Called-Once dataflow analysis could never terminate as a
consequence of non-monotonic update on states.  States of kind Escape
can override states leading to non-monotonic update.

This fix disallows the `Escape` state to override the `Reported`
state.

rdar://119671856
This commit is contained in:
Ziqing Luo 2024-01-26 11:23:15 -08:00 committed by GitHub
parent 6c74d8083b
commit 2a06850701
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 13 additions and 2 deletions

View File

@ -163,7 +163,7 @@ public:
NotVisited = 0x8, /* 1000 */ NotVisited = 0x8, /* 1000 */
// We already reported a violation and stopped tracking calls for this // We already reported a violation and stopped tracking calls for this
// parameter. // parameter.
Reported = 0x15, /* 1111 */ Reported = 0xF, /* 1111 */
LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ Reported) LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ Reported)
}; };
@ -932,7 +932,8 @@ private:
ParameterStatus &CurrentParamStatus = CurrentState.getStatusFor(Index); ParameterStatus &CurrentParamStatus = CurrentState.getStatusFor(Index);
// Escape overrides whatever error we think happened. // Escape overrides whatever error we think happened.
if (CurrentParamStatus.isErrorStatus()) { if (CurrentParamStatus.isErrorStatus() &&
CurrentParamStatus.getKind() != ParameterStatus::Kind::Reported) {
CurrentParamStatus = ParameterStatus::Escaped; CurrentParamStatus = ParameterStatus::Escaped;
} }
} }

View File

@ -1194,6 +1194,16 @@ void suppression_3(int cond, void (^callback)(void) CALLED_ONCE) {
escape(handler); escape(handler);
} }
- (void)test_termination:(int)cond
withCompletion:(void (^)(void))handler {
// The code below was able to cause non-termination but should be
// fixed now:
do {
escape(handler);
handler(); // expected-warning{{completion handler is called twice}} expected-note{{previous call is here; set to nil to indicate it cannot be called afterwards}}
} while (cond);
}
typedef void (^DeferredBlock)(void); typedef void (^DeferredBlock)(void);
static inline void DefferedCallback(DeferredBlock *inBlock) { (*inBlock)(); } static inline void DefferedCallback(DeferredBlock *inBlock) { (*inBlock)(); }
#define _DEFERCONCAT(a, b) a##b #define _DEFERCONCAT(a, b) a##b