Issue 130396
Summary non-exported variable in module interface unit has multiple addresses
Labels
Assignees
Reporter Diltsman
    I posted this on Stack Overflow and it was suggested that I post this to Clang somewhere.  I don't know if this is the correct place to do that.

---
I have a variable `exception_transformers` in coroutine.cpp.  If this variable is in an anonymous namespace, then the output shows that `register_exception_handler()` accesses it at an address 0x18 bytes beyond where every other function accesses it.  If `exception_transformers` is removed from the anonymous namespace, then it is accessed at the same address by all functions.

Is there some interaction between the C++ module and the anonymous namespace that is causing this?  Is there some other cause of this problem?

---
In n4928 (C++23-ish), 9.8.2.2 [namespace.unnamed]

> ...all occurrences of *unique* in a translation unit are replaced by the
> same identifier, and this identifier differs from all other
> identifiers in the translation unit.

To me, this indicates that both anonymous namespaces behave as if they have the same name, so having the declaration and definition of `transform_exception()` in two different anonymous namespaces in the same file shouldn't be causing an issue.

---

https://godbolt.org/z/59EWe5G3b

For clang 19:

    module;
    
    #include <coroutine>
    #include <expected>
    #include <functional>
    #include <iostream>
    #include <ranges>
    #include <stdexcept>
    #include <system_error>
    #include <type_traits>
    #include <vector>
    
    export module mycoroutine;
    export import :stdexception;
    
    export {
      namespace exco {
      template <typename T> using result_t = std::expected<T, std::error_code>;
      }
    }
    
    namespace {
    auto transform_exception() -> exco::result_t<void>;
    std::vector<std::function<exco::result_t<void>()>> exception_transformers{transform_exception};
    }
    
    export {
      namespace exco {
      auto register_exception_handler(std::function<exco::result_t<void>()> func) {
        std::cout << "register_exception_handler()" << &exception_transformers
                  << ' ' << exception_transformers.size() << '\n';
        exception_transformers.emplace_back(std::move(func));
        std::cout << "register_exception_handler()" << &exception_transformers
                  << ' ' << exception_transformers.size() << ' '
                  << sizeof(exception_transformers) << '\n';
      }
      inline auto unerr(auto const t) {
        return std::unexpected{make_error_code(t)};
      }
    
      template <typename T> struct expected_wrapper {
        // Initialize as errno 0 so there are no restrictions on T
        // caused by initializing this
        exco::result_t<T> m_result{exco::unerr(static_cast<std::errc>(0))};
        expected_wrapper<T> *&m_ptr_to_this;
        expected_wrapper(expected_wrapper<T> *&ptr_to_this)
            : m_ptr_to_this{ptr_to_this} {
          m_ptr_to_this = this;
        }
        operator result_t<T>() { return std::move(m_result); };
      };
      } // namespace exco
    
      namespace std {
      template <typename T, typename... Args>
      struct coroutine_traits<exco::result_t<T>, Args...> {
        class promise_type;
        template <typename T1> struct awaiter_type {
          exco::result_t<T1> &m_result;
          explicit awaiter_type(exco::result_t<T1> &result) noexcept
              : m_result{result} {}
          auto await_ready() { return m_result.has_value(); }
          auto await_suspend(std::coroutine_handle<promise_type> h) {
            // This should only happen when await_ready() returns false,
            // which means that has_value() returned false.
            h.destroy();
          }
          auto await_resume() {
            // This should only happen when await_ready() returns true,
            // which means that has_value() returned true.
            return m_result.value();
          }
        };
    
        class promise_type {
          exco::expected_wrapper<T> *m_ptr_to_wrapper;
    
        public:
          auto initial_suspend() noexcept -> std::suspend_never { return {}; }
          auto final_suspend() noexcept -> std::suspend_never { return {}; }
          auto return_value(std::error_code ec) {
            m_ptr_to_wrapper->m_result = std::unexpected{ec};
          }
          auto return_value(auto &&t) { m_ptr_to_wrapper->m_result = std::move(t); }
          auto get_return_object() {
            return exco::expected_wrapper<T>{m_ptr_to_wrapper};
          }
          auto unhandled_exception() {
            for (auto &f : exception_transformers | std::views::reverse) {
              try {
                auto result = f();
                if (!result.has_value()) {
                  m_ptr_to_wrapper->m_result = std::unexpected{result.error()};
                  return;
                }
              } catch (...) {
              }
            }
            m_ptr_to_wrapper->m_result = exco::unerr(exco::stdexception::unknown);
          }
          template <typename T1> auto await_transform(exco::result_t<T1> value) {
            m_ptr_to_wrapper->m_result = std::move(value);
            return awaiter_type<T1>{m_ptr_to_wrapper->m_result};
          }
        };
      };
      } // namespace std
    }
    
    namespace {
    auto transform_exception() -> exco::result_t<void> {
      std::cout << "transform_exception()" << &exception_transformers << ' '
                << exception_transformers.size() << '\n';
      try {
        throw;
      } catch (std::exception const &) {
        return exco::unerr(exco::stdexception::exception);
      }
    }
    }
_______________________________________________
llvm-bugs mailing list
llvm-bugs@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs

Reply via email to