Skip to content

[clang][bytecode] Fix visiting for-range loop variable #147188

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 6, 2025

Conversation

tbaederr
Copy link
Contributor

@tbaederr tbaederr commented Jul 6, 2025

Make sure we're properly registering DecompositionDecls.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:bytecode Issues for the clang bytecode constexpr interpreter labels Jul 6, 2025
@llvmbot
Copy link
Member

llvmbot commented Jul 6, 2025

@llvm/pr-subscribers-clang

Author: Timm Baeder (tbaederr)

Changes

Make sure we're properly registering DecompositionDecls.


Full diff: https://github.com/llvm/llvm-project/pull/147188.diff

2 Files Affected:

  • (modified) clang/lib/AST/ByteCode/Compiler.cpp (+1-2)
  • (added) clang/test/AST/ByteCode/libcxx/tuple-decompose-for-range.cpp (+280)
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 5ed65f21bb2c0..d1c93e4694667 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -5545,7 +5545,6 @@ bool Compiler<Emitter>::visitCXXForRangeStmt(const CXXForRangeStmt *S) {
   const Stmt *BeginStmt = S->getBeginStmt();
   const Stmt *RangeStmt = S->getRangeStmt();
   const Stmt *EndStmt = S->getEndStmt();
-  const VarDecl *LoopVar = S->getLoopVariable();
 
   LabelTy EndLabel = this->getLabel();
   LabelTy CondLabel = this->getLabel();
@@ -5570,7 +5569,7 @@ bool Compiler<Emitter>::visitCXXForRangeStmt(const CXXForRangeStmt *S) {
   if (!this->jumpFalse(EndLabel))
     return false;
 
-  if (!this->visitVarDecl(LoopVar))
+  if (!this->visitDeclStmt(S->getLoopVarStmt(), /*EvaluateConditionDecl=*/true))
     return false;
 
   // Body.
diff --git a/clang/test/AST/ByteCode/libcxx/tuple-decompose-for-range.cpp b/clang/test/AST/ByteCode/libcxx/tuple-decompose-for-range.cpp
new file mode 100644
index 0000000000000..9f7b78f82a5d2
--- /dev/null
+++ b/clang/test/AST/ByteCode/libcxx/tuple-decompose-for-range.cpp
@@ -0,0 +1,280 @@
+// RUN: %clang_cc1 -std=c++2c -verify=expected,both %s -fexperimental-new-constant-interpreter
+// RUN: %clang_cc1 -std=c++2c -verify=ref,both      %s
+
+// both-no-diagnostics
+
+namespace std {
+typedef long unsigned int size_t;
+}
+extern "C++" {
+namespace std {
+template <typename> struct iterator_traits;
+}
+}
+namespace std {
+template <typename _Tp, _Tp __v> struct integral_constant {
+  static constexpr _Tp value = __v;
+};
+template <bool __v> using __bool_constant = integral_constant<bool, __v>;
+using false_type = __bool_constant<false>;
+template <bool, typename _Tp = void> struct enable_if {
+  using type = _Tp;
+};
+template <bool> struct __conditional {
+  template <typename _Tp, typename> using type = _Tp;
+};
+template <bool _Cond, typename _If, typename _Else>
+using __conditional_t =
+    typename __conditional<_Cond>::template type<_If, _Else>;
+template <typename _Tp>
+struct is_empty : public __bool_constant<__is_empty(_Tp)> {};
+template <typename _Tp, typename _Up>
+struct is_same : public __bool_constant<__is_same(_Tp, _Up)> {};
+template <typename _Tp> struct remove_cv {
+  using type = __remove_cv(_Tp);
+};
+template <typename _Tp> struct tuple_size;
+template <typename _Tp, typename _Up = typename remove_cv<_Tp>::type,
+          typename = typename enable_if<is_same<_Tp, _Up>::value>::type,
+          size_t = tuple_size<_Tp>::value>
+using __enable_if_has_tuple_size = _Tp;
+template <typename _Tp>
+struct tuple_size<const __enable_if_has_tuple_size<_Tp>>
+    : public tuple_size<_Tp> {};
+template <size_t __i, typename _Tp> struct tuple_element;
+template <size_t __i, typename _Tp>
+using __tuple_element_t = typename tuple_element<__i, _Tp>::type;
+template <size_t __i, typename _Tp> struct tuple_element<__i, const _Tp> {
+  using type = const __tuple_element_t<__i, _Tp>;
+};
+template <size_t _Np, typename... _Types> struct _Nth_type {
+  using type = __type_pack_element<_Np, _Types...>;
+};
+template <typename _Tp> struct iterator_traits<_Tp *> {
+  using reference = _Tp &;
+};
+} // namespace std
+extern "C++" {
+void *operator new(std::size_t, void *__p);
+}
+namespace std {
+template <typename _Tp, typename... _Args>
+constexpr inline void _Construct(_Tp *__p, _Args &&...__args) {
+  ::new (__p) _Tp(__args...);
+};
+} // namespace std
+namespace __gnu_cxx {
+template <typename _Iterator, typename _Container> class __normal_iterator {
+protected:
+  _Iterator _M_current;
+  typedef std::iterator_traits<_Iterator> __traits_type;
+
+public:
+  typedef _Iterator iterator_type;
+  typedef typename __traits_type::reference reference;
+  explicit constexpr __normal_iterator(const _Iterator &__i)
+      : _M_current(__i) {};
+  constexpr reference operator*() const { return *_M_current; }
+  constexpr __normal_iterator &operator++() {
+    ++_M_current;
+    return *this;
+  }
+  constexpr const _Iterator &base() const { return _M_current; }
+};
+template <typename _Iterator, typename _Container>
+constexpr bool
+operator==(const __normal_iterator<_Iterator, _Container> &__lhs,
+           const __normal_iterator<_Iterator, _Container> &__rhs) {
+  return __lhs.base() == __rhs.base();
+}
+} // namespace __gnu_cxx
+namespace std {
+template <typename _Tp> class __new_allocator {};
+template <typename _Tp> using __allocator_base = __new_allocator<_Tp>;
+template <typename> struct allocator_traits;
+template <typename _Tp> class allocator : public __allocator_base<_Tp> {
+public:
+  typedef _Tp value_type;
+  constexpr _Tp *allocate(size_t __n) {
+    __n *= sizeof(_Tp);
+    return static_cast<_Tp *>(::operator new(__n));
+  }
+  constexpr void deallocate(_Tp *__p, size_t __n) { ::operator delete(__p); }
+};
+template <typename _Tp> struct allocator_traits<allocator<_Tp>> {
+  using allocator_type = allocator<_Tp>;
+  using pointer = _Tp *;
+  using size_type = std::size_t;
+  template <typename _Up> using rebind_alloc = allocator<_Up>;
+  static constexpr pointer allocate(allocator_type &__a, size_type __n) {
+    return __a.allocate(__n);
+  }
+  static constexpr void deallocate(allocator_type &__a, pointer __p,
+                                   size_type __n) {
+    __a.deallocate(__p, __n);
+  }
+};
+} // namespace std
+namespace __gnu_cxx {
+template <typename _Alloc, typename = typename _Alloc::value_type>
+struct __alloc_traits : std::allocator_traits<_Alloc> {
+  typedef std::allocator_traits<_Alloc> _Base_type;
+  template <typename _Tp> struct rebind {
+    typedef typename _Base_type::template rebind_alloc<_Tp> other;
+  };
+};
+} // namespace __gnu_cxx
+namespace std {
+template <typename _InputIterator, typename _ForwardIterator>
+constexpr _ForwardIterator __do_uninit_copy(_InputIterator __first,
+                                            _InputIterator __last,
+                                            _ForwardIterator __result) {
+  _ForwardIterator __cur = __result;
+  for (; __first != __last; ++__first, ++__cur)
+    std::_Construct(&*__cur, *__first);
+  return __cur;
+};
+template <typename _InputIterator, typename _ForwardIterator, typename _Tp>
+constexpr inline _ForwardIterator
+__uninitialized_copy_a(_InputIterator __first, _InputIterator __last,
+                       _ForwardIterator __result, allocator<_Tp> &) {
+  return std::__do_uninit_copy(__first, __last, __result);
+}
+template <class _E> class initializer_list {
+  typedef size_t size_type;
+  typedef const _E *iterator;
+  typedef const _E *const_iterator;
+
+private:
+  iterator _M_array;
+  size_type _M_len;
+
+public:
+  constexpr size_type size() const { return _M_len; }
+  constexpr const_iterator begin() const { return _M_array; }
+  constexpr const_iterator end() const { return begin() + size(); }
+};
+template <typename _Tp, typename _Alloc> struct _Vector_base {
+  typedef
+      typename __gnu_cxx::__alloc_traits<_Alloc>::template rebind<_Tp>::other
+          _Tp_alloc_type;
+  typedef typename __gnu_cxx::__alloc_traits<_Tp_alloc_type>::pointer pointer;
+  struct _Vector_impl_data {
+    pointer _M_start;
+    pointer _M_finish;
+    pointer _M_end_of_storage;
+  };
+  struct _Vector_impl : public _Tp_alloc_type, public _Vector_impl_data {};
+
+public:
+  typedef _Alloc allocator_type;
+  constexpr _Tp_alloc_type &_M_get_Tp_allocator() { return this->_M_impl; }
+  constexpr _Vector_base(const allocator_type &__a) : _M_impl(__a) {}
+  constexpr ~_Vector_base() {
+    _M_deallocate(_M_impl._M_start,
+                  _M_impl._M_end_of_storage - _M_impl._M_start);
+  }
+
+public:
+  _Vector_impl _M_impl;
+  constexpr pointer _M_allocate(size_t __n) {
+    typedef __gnu_cxx::__alloc_traits<_Tp_alloc_type> _Tr;
+    return __n != 0 ? _Tr::allocate(_M_impl, __n) : pointer();
+  }
+  constexpr void _M_deallocate(pointer __p, size_t __n) {
+    typedef __gnu_cxx::__alloc_traits<_Tp_alloc_type> _Tr;
+    if (__p)
+      _Tr::deallocate(_M_impl, __p, __n);
+  }
+
+protected:
+};
+template <typename _Tp, typename _Alloc = std::allocator<_Tp>>
+class vector : protected _Vector_base<_Tp, _Alloc> {
+  typedef _Vector_base<_Tp, _Alloc> _Base;
+
+public:
+  typedef _Tp value_type;
+  typedef typename _Base::pointer pointer;
+  typedef __gnu_cxx::__normal_iterator<pointer, vector> iterator;
+  typedef size_t size_type;
+  typedef _Alloc allocator_type;
+  using _Base::_M_get_Tp_allocator;
+
+public:
+private:
+public:
+  constexpr vector(initializer_list<value_type> __l,
+                   const allocator_type &__a = allocator_type())
+      : _Base(__a) {
+    _M_range_initialize_n(__l.begin(), __l.end(), __l.size());
+  }
+  constexpr iterator begin() { return iterator(this->_M_impl._M_start); }
+  constexpr iterator end() { return iterator(this->_M_impl._M_finish); }
+
+protected:
+  template <typename _Iterator>
+  constexpr void _M_range_initialize_n(_Iterator __first, _Iterator __last,
+                                       size_type __n) {
+    pointer __start = this->_M_impl._M_start = this->_M_allocate(((__n)));
+    this->_M_impl._M_end_of_storage = __start + __n;
+    this->_M_impl._M_finish = std::__uninitialized_copy_a(
+        __first, __last, __start, _M_get_Tp_allocator());
+  }
+};
+template <typename _Tp> struct __is_empty_non_tuple : is_empty<_Tp> {};
+template <typename _Tp>
+using __empty_not_final =
+    __conditional_t<__is_final(_Tp), false_type, __is_empty_non_tuple<_Tp>>;
+template <size_t _Idx, typename _Head, bool = __empty_not_final<_Head>::value>
+struct _Head_base;
+template <size_t _Idx, typename _Head> struct _Head_base<_Idx, _Head, false> {
+  static constexpr const _Head &_M_head(const _Head_base &__b) {
+    return __b._M_head_impl;
+  }
+  _Head _M_head_impl;
+};
+template <size_t _Idx, typename... _Elements> struct _Tuple_impl;
+template <size_t _Idx, typename _Head>
+struct _Tuple_impl<_Idx, _Head> : private _Head_base<_Idx, _Head> {
+  typedef _Head_base<_Idx, _Head> _Base;
+  static constexpr const _Head &_M_head(const _Tuple_impl &__t) {
+    return _Base::_M_head(__t);
+  }
+  explicit constexpr _Tuple_impl(const _Head &__head) : _Base(__head) {}
+
+protected:
+};
+template <typename... _Elements>
+class tuple : public _Tuple_impl<0, _Elements...> {
+  using _Inherited = _Tuple_impl<0, _Elements...>;
+
+public:
+  template <typename = void>
+  constexpr tuple(const _Elements &...__elements) : _Inherited(__elements...) {}
+};
+template <typename... _Elements>
+struct tuple_size<tuple<_Elements...>>
+    : public integral_constant<size_t, sizeof...(_Elements)> {};
+template <size_t __i, typename... _Types>
+struct tuple_element<__i, tuple<_Types...>> {
+  using type = typename _Nth_type<__i, _Types...>::type;
+};
+template <size_t __i, typename _Head, typename... _Tail>
+constexpr const _Head &
+__get_helper(const _Tuple_impl<__i, _Head, _Tail...> &__t) {
+  return _Tuple_impl<__i, _Head, _Tail...>::_M_head(__t);
+};
+template <size_t __i, typename... _Elements>
+constexpr const int get(const tuple<_Elements...> &&__t) {
+  return std::__get_helper<__i>(__t);
+};
+} // namespace std
+constexpr int foo() {
+  std::vector<std::tuple<int>> data_tuples = {{1}};
+  for (const auto [id] : data_tuples) {
+    int a = id + 3;
+  }
+  return 1;
+}
+static_assert(foo() == 1);

Make sure we're properly registering DecompositionDecls.
@tbaederr tbaederr force-pushed the decompose-for-range branch from 30dc7c7 to 62b8ba3 Compare July 6, 2025 14:49
@tbaederr tbaederr merged commit 9c7320e into llvm:main Jul 6, 2025
9 checks passed
@llvm-ci
Copy link
Collaborator

llvm-ci commented Jul 6, 2025

LLVM Buildbot has detected a new failure on builder flang-arm64-windows-msvc running on linaro-armv8-windows-msvc-01 while building clang at step 7 "test-build-unified-tree-check-flang".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/207/builds/3469

Here is the relevant piece of the build log for the reference
Step 7 (test-build-unified-tree-check-flang) failure: test (failure)
******************** TEST 'Flang :: Parser/OpenMP/declare-reduction-operator.f90' FAILED ********************
Exit Code: 2

Command Output (stdout):
--
# RUN: at line 1
c:\users\tcwg\llvm-worker\flang-arm64-windows-msvc\build\bin\flang.exe -fc1 -fdebug-unparse -fopenmp C:\Users\tcwg\llvm-worker\flang-arm64-windows-msvc\llvm-project\flang\test\Parser\OpenMP\declare-reduction-operator.f90 | c:\users\tcwg\llvm-worker\flang-arm64-windows-msvc\build\bin\filecheck.exe --ignore-case C:\Users\tcwg\llvm-worker\flang-arm64-windows-msvc\llvm-project\flang\test\Parser\OpenMP\declare-reduction-operator.f90
# executed command: 'c:\users\tcwg\llvm-worker\flang-arm64-windows-msvc\build\bin\flang.exe' -fc1 -fdebug-unparse -fopenmp 'C:\Users\tcwg\llvm-worker\flang-arm64-windows-msvc\llvm-project\flang\test\Parser\OpenMP\declare-reduction-operator.f90'
# .---command stderr------------
# | PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
# | Stack dump:
# | 0.	Program arguments: c:\\users\\tcwg\\llvm-worker\\flang-arm64-windows-msvc\\build\\bin\\flang.exe -fc1 -fdebug-unparse -fopenmp C:\\Users\\tcwg\\llvm-worker\\flang-arm64-windows-msvc\\llvm-project\\flang\\test\\Parser\\OpenMP\\declare-reduction-operator.f90
# | Exception Code: 0xC0000005
# |  #0 0x00007ff65c322c34 (c:\users\tcwg\llvm-worker\flang-arm64-windows-msvc\build\bin\flang.exe+0x1a92c34)
# |  #1 0x00007ff65c32091c (c:\users\tcwg\llvm-worker\flang-arm64-windows-msvc\build\bin\flang.exe+0x1a9091c)
# |  #2 0x00007ff65c31e7b0 (c:\users\tcwg\llvm-worker\flang-arm64-windows-msvc\build\bin\flang.exe+0x1a8e7b0)
# |  #3 0x00007ff65c3244c4 (c:\users\tcwg\llvm-worker\flang-arm64-windows-msvc\build\bin\flang.exe+0x1a944c4)
# |  #4 0x00007ff65c31e318 (c:\users\tcwg\llvm-worker\flang-arm64-windows-msvc\build\bin\flang.exe+0x1a8e318)
# |  #5 0x00007ff65b13c840 (c:\users\tcwg\llvm-worker\flang-arm64-windows-msvc\build\bin\flang.exe+0x8ac840)
# |  #6 0x00007ff65b185ca0 (c:\users\tcwg\llvm-worker\flang-arm64-windows-msvc\build\bin\flang.exe+0x8f5ca0)
# |  #7 0x00007ff65b22eb5c (c:\users\tcwg\llvm-worker\flang-arm64-windows-msvc\build\bin\flang.exe+0x99eb5c)
# |  #8 0x00007ff65b1850dc (c:\users\tcwg\llvm-worker\flang-arm64-windows-msvc\build\bin\flang.exe+0x8f50dc)
# |  #9 0x00007ff65a928ac8 (c:\users\tcwg\llvm-worker\flang-arm64-windows-msvc\build\bin\flang.exe+0x98ac8)
# | #10 0x00007ff65a93ea90 (c:\users\tcwg\llvm-worker\flang-arm64-windows-msvc\build\bin\flang.exe+0xaea90)
# | #11 0x00007ff65a8934e8 (c:\users\tcwg\llvm-worker\flang-arm64-windows-msvc\build\bin\flang.exe+0x34e8)
# | #12 0x00007ff65a8920a4 (c:\users\tcwg\llvm-worker\flang-arm64-windows-msvc\build\bin\flang.exe+0x20a4)
# | #13 0x00007ff65f551648 mlir::detail::FallbackTypeIDResolver::registerImplicitTypeID(class llvm::StringRef) (c:\users\tcwg\llvm-worker\flang-arm64-windows-msvc\build\bin\flang.exe+0x4cc1648)
# | #14 0xa5357ff65f5516e4
# `-----------------------------
# error: command failed with exit status: 0xc0000005
# executed command: 'c:\users\tcwg\llvm-worker\flang-arm64-windows-msvc\build\bin\filecheck.exe' --ignore-case 'C:\Users\tcwg\llvm-worker\flang-arm64-windows-msvc\llvm-project\flang\test\Parser\OpenMP\declare-reduction-operator.f90'
# .---command stderr------------
# | FileCheck error: '<stdin>' is empty.
# | FileCheck command line:  c:\users\tcwg\llvm-worker\flang-arm64-windows-msvc\build\bin\filecheck.exe --ignore-case C:\Users\tcwg\llvm-worker\flang-arm64-windows-msvc\llvm-project\flang\test\Parser\OpenMP\declare-reduction-operator.f90
# `-----------------------------
# error: command failed with exit status: 2

--

********************


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:bytecode Issues for the clang bytecode constexpr interpreter clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants