bruno created this revision.
bruno added reviewers: ChuanqiXu, nridge, sammccall.
Herald added subscribers: hoy, modimo, wenlei.
Herald added a project: All.
bruno requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

D115187 <https://reviews.llvm.org/D115187> exposed CoroutineSuspendExpr's 
operand, which makes some nodes to show up twice during the traversal, 
confusing the check for unsequenced operations. Skip the operand since it's 
already handled as part of the common expression and get rid of the misleading 
warnings.

https://github.com/llvm/llvm-project/issues/56768


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D142077

Files:
  clang/lib/Sema/SemaChecking.cpp
  clang/test/SemaCXX/warn-unsequenced-coro.cpp

Index: clang/test/SemaCXX/warn-unsequenced-coro.cpp
===================================================================
--- /dev/null
+++ clang/test/SemaCXX/warn-unsequenced-coro.cpp
@@ -0,0 +1,144 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsyntax-only -verify -std=c++20 -Wno-unused -Wno-uninitialized -Wunsequenced %s
+
+// expected-no-diagnostics
+
+typedef __PTRDIFF_TYPE__ ptrdiff_t;
+
+namespace std {
+
+template<typename T> struct remove_reference       { typedef T type; };
+template<typename T> struct remove_reference<T &>  { typedef T type; };
+template<typename T> struct remove_reference<T &&> { typedef T type; };
+
+template<typename T>
+typename remove_reference<T>::type &&move(T &&t) noexcept;
+
+template <class Ret, typename... T>
+struct coroutine_traits { using promise_type = typename Ret::promise_type; };
+
+template <class Promise = void>
+struct coroutine_handle {
+  static coroutine_handle from_address(void *) noexcept;
+  static coroutine_handle from_promise(Promise &promise);
+};
+template <>
+struct coroutine_handle<void> {
+  template <class PromiseType>
+  coroutine_handle(coroutine_handle<PromiseType>) noexcept;
+  static coroutine_handle from_address(void *);
+};
+
+struct suspend_always {
+  bool await_ready() noexcept { return false; }
+  void await_suspend(coroutine_handle<>) noexcept {}
+  void await_resume() noexcept {}
+};
+
+struct suspend_never {
+  bool await_ready() noexcept { return true; }
+  void await_suspend(coroutine_handle<>) noexcept {}
+  void await_resume() noexcept {}
+};
+
+struct input_iterator_tag {};
+struct forward_iterator_tag : public input_iterator_tag {};
+
+} // namespace std
+
+using namespace std;
+
+template<class T>
+struct Task {
+    struct promise_type {
+        Task<T> get_return_object() noexcept;
+        suspend_always initial_suspend() noexcept;
+        suspend_always final_suspend() noexcept;
+        void return_value(T);
+        void unhandled_exception();
+        auto yield_value(Task<T>) noexcept { return final_suspend(); }
+    };
+    bool await_ready() noexcept { return false; }
+    void await_suspend(coroutine_handle<>) noexcept {}
+    T await_resume();
+};
+
+template<>
+struct Task<void> {
+    struct promise_type {
+        Task<void> get_return_object() noexcept;
+        suspend_always initial_suspend() noexcept;
+        suspend_always final_suspend() noexcept;
+        void return_void() noexcept;
+        void unhandled_exception() noexcept;
+        auto yield_value(Task<void>) noexcept { return final_suspend(); }
+    };
+    bool await_ready() noexcept { return false; }
+    void await_suspend(coroutine_handle<>) noexcept {}
+    void await_resume() noexcept {}
+};
+
+template <typename T>
+class generator
+{
+  struct Promise
+  {
+    auto get_return_object() { return generator{*this}; }
+    auto initial_suspend() { return suspend_never{}; }
+    auto final_suspend() noexcept { return suspend_always{}; }
+    void unhandled_exception() {}
+    void return_void() {}
+
+    auto yield_value(T value)
+    {
+      value_ = std::move(value);
+      return suspend_always{};
+    }
+
+    T value_;
+  };
+
+  using Handle = coroutine_handle<Promise>;
+
+  struct sentinel{};
+  struct iterator
+  {
+    using iterator_category = input_iterator_tag;
+    using value_type = T;
+    using difference_type = ptrdiff_t;
+    using reference = T &;
+    using const_reference = const T &;
+    using pointer = T *;
+
+    iterator &operator++()
+    {
+      h_.resume();
+      return *this;
+    }
+    const_reference &operator*() const { return h_.promise().value_; }
+    bool operator!=(sentinel) { return !h_.done(); }
+
+    Handle h_;
+  };
+
+  explicit generator(Promise &p) : h_(Handle::from_promise(p)) {}
+  Handle h_;
+public:
+  using promise_type = Promise;
+  auto begin() { return iterator{h_}; }
+  auto end() { return sentinel{}; }
+};
+
+Task<void> c(int i) {
+  co_await (i = 0, std::suspend_always{});
+}
+
+generator<int> range(int start, int end)
+{
+  while (start < end)
+    co_yield start++;
+}
+
+Task<int> go(int const& val);
+Task<int> go1(int x) {
+  co_return co_await go(++x);
+}
\ No newline at end of file
Index: clang/lib/Sema/SemaChecking.cpp
===================================================================
--- clang/lib/Sema/SemaChecking.cpp
+++ clang/lib/Sema/SemaChecking.cpp
@@ -15176,6 +15176,23 @@
     Base::VisitStmt(E);
   }
 
+  void VisitCoroutineSuspendExpr(const CoroutineSuspendExpr *CSE) {
+    for (auto *Sub : CSE->children()) {
+      const Expr *ChildExpr = dyn_cast_or_null<Expr>(Sub);
+      if (!ChildExpr)
+        continue;
+
+      if (ChildExpr == CSE->getOperand())
+        // Do not recurse over a CoroutineSuspendExpr's operand.
+        // The operand is also a subexpression of getCommonExpr(), and
+        // recursing into it directly could confuse object management
+        // for the sake of sequence tracking.
+        continue;
+
+      Visit(Sub);
+    }
+  }
+
   void VisitCastExpr(const CastExpr *E) {
     Object O = Object();
     if (E->getCastKind() == CK_LValueToRValue)
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to